[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