[RFC] [gdb] Work around zero l_addr/l_ld

Tom de Vries <[email protected]>
Newsgroups gmane.comp.gdb.patches
Message-ID <[email protected]>
On ppc64le-linux (AlmaLinux 9.8), I run into:
...
FAIL: gdb.mi/mi-dlmopen.exp: still-in-use fields were all correct
...

While investigating this, I stumbled on this warning emitted during the
calculation of allow_dlmopen_tests:
...
(gdb) run ^M
Starting program: allow_dlmopen_tests.x ^M
[Thread debugging using libthread_db enabled]^M
Using host libthread_db library "/lib64/libthread_db.so.1".^M
warning: .dynamic section for "/lib64/ld64.so.2" is not at the expected \
  address (wrong library or version mismatch?)^M
dlmopen debug supported.^M
...

The warning is mentioned in this glibc commit 88361b408b:
...
    elf: Copy l_addr/l_ld when adding ld.so to a new namespace

    When add ld.so to a new namespace, we don't actually load ld.so.  We
    create a new link map and refers the real one for almost everything.
    Copy l_addr and l_ld from the real ld.so link map to avoid GDB warning:

    warning: .dynamic section for ".../elf/ld-linux-x86-64.so.2" is not at \
      the expected address (wrong library or version mismatch?)

    when handling shared library loaded by dlmopen.
...

So, AFAICT the setup is:
- the glibc package is based on v2.34
- it contains a backport of commit a93d9e03a3 ("Extend struct r_debug to
  support multiple namespaces [BZ #15971]")
- it doesn't contain a backport of commit 88361b408b ("elf: Copy l_addr/l_ld
  when adding ld.so to a new namespace")
- both commits are part of v2.35

What happens is:
- when probing for l_addr and l_ld in svr4_solib_ops::read_lm_info, both get
  the value 0
- in svr4_solib_ops::lm_addr_check, the 0 value propagates to l_dynaddr, and
  "l_addr = l_dynaddr - dynaddr" then underflows, and things go downhill from
  there, resulting in the warning and eventually the FAIL.

Fix this by replicating the missing glibc commit in
svr4_solib_ops::read_lm_info.

I've enabled the fix only for the configuration I can test, for all others I
disabled it using "lmo.l_real_offset = -1".

This is an RFC.  My question is: is the added complexity worth the trouble for
what looks like a cornercase?

[ FTR, in an earlier attempt I proposed to deal with the FAIL using an
xfail [1]. ]

Tested on ppc64le-linux and x86_64-linux.

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=33662

[1] https://sourceware.org/pipermail/gdb-patches/2026-July/229073.html
---
 gdb/mips-fbsd-tdep.c   |  1 +
 gdb/mips-netbsd-tdep.c |  1 +
 gdb/solib-svr4-linux.c |  6 ++--
 gdb/solib-svr4.c       | 67 +++++++++++++++++++++++++++++++-----------
 gdb/solib-svr4.h       |  4 +++
 5 files changed, 60 insertions(+), 19 deletions(-)

diff --git a/gdb/mips-fbsd-tdep.c b/gdb/mips-fbsd-tdep.c
index ecad943e8fa..65278937415 100644
--- a/gdb/mips-fbsd-tdep.c
+++ b/gdb/mips-fbsd-tdep.c
@@ -571,6 +571,7 @@ mips_fbsd_lp64_solib_ops::fetch_link_map_offsets () const
       lmo.l_ld_offset = 24;
       lmo.l_next_offset = 32;
       lmo.l_prev_offset = 40;
+      lmo.l_real_offset = -1;
     }
 
   return lmp;
diff --git a/gdb/mips-netbsd-tdep.c b/gdb/mips-netbsd-tdep.c
index a5bcde20e46..79b1a3cedee 100644
--- a/gdb/mips-netbsd-tdep.c
+++ b/gdb/mips-netbsd-tdep.c
@@ -333,6 +333,7 @@ mips_nbsd_ilp32_svr4_solib_ops::fetch_link_map_offsets () const
       lmo.l_ld_offset = 12;
       lmo.l_next_offset = 16;
       lmo.l_prev_offset = 20;
+      lmo.l_real_offset = -1;
     }
 
   return lmp;
diff --git a/gdb/solib-svr4-linux.c b/gdb/solib-svr4-linux.c
index 2d36adcca73..e67f3036df7 100644
--- a/gdb/solib-svr4-linux.c
+++ b/gdb/solib-svr4-linux.c
@@ -53,6 +53,7 @@ linux_ilp32_svr4_solib_ops::fetch_link_map_offsets () const
       lmo.l_ld_offset = 8;
       lmo.l_next_offset = 12;
       lmo.l_prev_offset = 16;
+      lmo.l_real_offset = -1;
     }
 
   return lmp;
@@ -85,13 +86,14 @@ linux_lp64_svr4_solib_ops::fetch_link_map_offsets () const
       lmo.r_ldsomap_offset = -1;
       lmo.r_next_offset = 40;
 
-      /* Everything we need is in the first 40 bytes.  */
-      lmo.link_map_size = 40;
+      /* Everything we need is in the first 48 bytes.  */
+      lmo.link_map_size = 48;
       lmo.l_addr_offset = 0;
       lmo.l_name_offset = 8;
       lmo.l_ld_offset = 16;
       lmo.l_next_offset = 24;
       lmo.l_prev_offset = 32;
+      lmo.l_real_offset = 40;
     }
 
   return lmp;
diff --git a/gdb/solib-svr4.c b/gdb/solib-svr4.c
index 8e3de4d3ea1..02a20f000d4 100644
--- a/gdb/solib-svr4.c
+++ b/gdb/solib-svr4.c
@@ -189,27 +189,58 @@ svr4_solib_ops::read_lm_info (CORE_ADDR lm_addr, CORE_ADDR debug_base) const
   gdb::byte_vector lm (lmo->link_map_size);
 
   if (target_read_memory (lm_addr, lm.data (), lmo->link_map_size) != 0)
-    warning (_("Error reading shared library list entry at %s"),
-	     paddress (current_inferior ()->arch (), lm_addr));
-  else
     {
-      type *ptr_type
-	= builtin_type (current_inferior ()->arch ())->builtin_data_ptr;
+      warning (_("Error reading shared library list entry at %s"),
+	       paddress (current_inferior ()->arch (), lm_addr));
+      return lm_info;
+    }
+
+  type *ptr_type
+    = builtin_type (current_inferior ()->arch ())->builtin_data_ptr;
+
+  lm_info = std::make_unique<lm_info_svr4> (debug_base);
+  lm_info->lm_addr = lm_addr;
 
-      lm_info = std::make_unique<lm_info_svr4> (debug_base);
-      lm_info->lm_addr = lm_addr;
-
-      lm_info->l_addr_inferior = extract_typed_address (&lm[lmo->l_addr_offset],
-							ptr_type);
-      lm_info->l_ld = extract_typed_address (&lm[lmo->l_ld_offset], ptr_type);
-      lm_info->l_next = extract_typed_address (&lm[lmo->l_next_offset],
-					       ptr_type);
-      lm_info->l_prev = extract_typed_address (&lm[lmo->l_prev_offset],
-					       ptr_type);
-      lm_info->l_name = extract_typed_address (&lm[lmo->l_name_offset],
-					       ptr_type);
+  lm_info->l_addr_inferior
+    = extract_typed_address (&lm[lmo->l_addr_offset], ptr_type);
+  lm_info->l_ld = extract_typed_address (&lm[lmo->l_ld_offset], ptr_type);
+  lm_info->l_next = extract_typed_address (&lm[lmo->l_next_offset], ptr_type);
+  lm_info->l_prev = extract_typed_address (&lm[lmo->l_prev_offset], ptr_type);
+  lm_info->l_name = extract_typed_address (&lm[lmo->l_name_offset], ptr_type);
+
+  if (lmo->l_real_offset == -1)
+    return lm_info;
+  CORE_ADDR l_real = extract_typed_address (&lm[lmo->l_real_offset], ptr_type);
+
+  if (l_real == lm_addr)
+    return lm_info;
+
+  /* We have l_real != lm_addr, so l_real points to the link map of ld.so.  */
+
+  if (!(lm_info->l_addr_inferior == 0 && lm_info->l_ld == 0))
+    return lm_info;
+
+  /* We have l_addr_inferior == 0 and l_ld == 0.  This can happen with a glibc
+     that:
+     - has commit a93d9e03a3 ("Extend struct r_debug to support multiple
+       namespaces [BZ #15971]"), but
+     - misses commit 88361b408b ("elf: Copy l_addr/l_ld when adding ld.so to a
+       new namespace").
+     This seems to be the case at least for the alma linux 9.8 BaseOS version,
+     which uses glibc v2.34 and backports only the first commit.
+     Fix / work around this here by replicating the copy of ld_addr/l_ld.  */
+
+  if (target_read_memory (l_real, lm.data (), lmo->link_map_size) != 0)
+    {
+      warning (_("Error reading shared library list entry at %s"),
+	       paddress (current_inferior ()->arch (), l_real));
+      return lm_info;
     }
 
+  lm_info->l_addr_inferior = extract_typed_address (&lm[lmo->l_addr_offset],
+						    ptr_type);
+  lm_info->l_ld = extract_typed_address (&lm[lmo->l_ld_offset], ptr_type);
+
   return lm_info;
 }
 
@@ -3537,6 +3568,7 @@ ilp32_svr4_solib_ops::fetch_link_map_offsets () const
       lmo.l_ld_offset = 8;
       lmo.l_next_offset = 12;
       lmo.l_prev_offset = 16;
+      lmo.l_real_offset = -1;
     }
 
   return lmp;
@@ -3586,6 +3618,7 @@ lp64_svr4_solib_ops::fetch_link_map_offsets () const
       lmo.l_ld_offset = 16;
       lmo.l_next_offset = 24;
       lmo.l_prev_offset = 32;
+      lmo.l_real_offset = -1;
     }
 
   return lmp;
diff --git a/gdb/solib-svr4.h b/gdb/solib-svr4.h
index 3078a092778..83d65842faa 100644
--- a/gdb/solib-svr4.h
+++ b/gdb/solib-svr4.h
@@ -207,6 +207,10 @@ struct link_map_offsets
 
     /* Offset to l_name field in struct link_map.  */
     int l_name_offset;
+
+    /* Offset to l_real field in struct link_map.  Available since glibc
+       version v2.3.4.  */
+    int l_real_offset;
   };
 
 /* Set the gdbarch methods for SVR4 systems.  */

base-commit: e189bfd9b492a0361a37858b9a4709a52a92c803
-- 
2.51.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.