diff options
author | Simon Marchi <simon.marchi@efficios.com> | 2025-07-17 15:58:05 -0400 |
---|---|---|
committer | Simon Marchi <simon.marchi@efficios.com> | 2025-08-22 10:45:47 -0400 |
commit | fcdc6c5aff198140e0e2c8b94eef48d340e04dbc (patch) | |
tree | ee4519b58f6d624357031113eb0d43de925d4d49 /ld/testsuite/ld-ctf/array-int.c | |
parent | 7f554749455b363af16be91f3c879664d1b8709c (diff) | |
download | binutils-fcdc6c5aff198140e0e2c8b94eef48d340e04dbc.zip binutils-fcdc6c5aff198140e0e2c8b94eef48d340e04dbc.tar.gz binutils-fcdc6c5aff198140e0e2c8b94eef48d340e04dbc.tar.bz2 |
gdb/solib: don't check filename when checking for duplicate solib
On Arch Linux, I get:
FAIL: gdb.base/dlmopen-ns-ids.exp: reopen a namespace
The symptom observed is that after stepping over the last dlmopen of the
test, "info sharedlibrary" does not show the library just opened. After
digging, I found that when stepping over that dlmopen call, the shlib
event breakpoint (that GDB inserts in glibc to get notified of dynamic
linker activity) does not get hit. I then saw that after the previous
dlclose, the shlib event breakpoints were suddenly all marked as
pending:
(gdb) maintenance info breakpoints
Num Type Disp Enb Address What
-1 shlib events keep n <PENDING>
-1.1 y- <PENDING>
The root cause of this problem is the fact that the dynamic linker path
specified in binaries contains a symlink:
$ readelf --program-headers /bin/ls | grep "Requesting program interpreter"
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
$ ls -l /lib64
lrwxrwxrwx 1 root root 7 May 3 15:26 /lib64 -> usr/lib
$ realpath /lib64/ld-linux-x86-64.so.2
/usr/lib/ld-linux-x86-64.so.2
As a result, the instances of the dynamic linker in the non-base
namespace have the real path instead of the original path:
(gdb) info sharedlibrary
From To NS Syms Read Shared Object Library
0x00007ffff7fc6000 0x00007ffff7fff000 [[0]] Yes /lib64/ld-linux-x86-64.so.2
...
0x00007ffff7fc6000 0x00007ffff7fff000 [[1]] Yes /usr/lib/ld-linux-x86-64.so.2
...
0x00007ffff7fc6000 0x00007ffff7fff000 [[1]] Yes /usr/lib/ld-linux-x86-64.so.2
...
0x00007ffff7fc6000 0x00007ffff7fff000 [[1]] Yes /usr/lib/ld-linux-x86-64.so.2
Notice that all instances of the dynamic loader have the same address
range. This is expected: the dynamic loader is really loaded just once
in memory, it's just that it's visible in the various namespaces, so
listed multiple times. Also, notice that the last three specify
namespace 1... seems like a separate bug to me (ignore it for now).
The fact that the paths differ between the first one and the subsequent
ones is not something we control: we receive those paths as-is from the
glibc link map.
Since these multiple solib entries are really the same mapping, we would
expect this code in solib_read_symbols to associate them to the same
objfile:
/* Have we already loaded this shared object? */
so.objfile = nullptr;
for (objfile *objfile : current_program_space->objfiles ())
{
if (filename_cmp (objfile_name (objfile), so.name.c_str ())
== 0
&& objfile->addr_low == so.addr_low)
{
so.objfile = objfile;
break;
}
}
But because the filenames differ, we end up creating two different
objfiles with the same symbols, same address ranges, etc. I would guess
that this is not a state we want.
When the dlclose call closes the last library from the non-base
namespace, the dynamic linker entry for that namespace is also
removed. From GDB's point of view, it just looks like an solib getting
unloaded. In update_solib_list, we have this code to check if the
objfile behind the solib is used by other solibs, and avoid deleting the
objfile if so:
bool still_in_use
= (gdb_iter->objfile != nullptr
&& solib_used (current_program_space, *gdb_iter));
/* Notify any observer that the shared object has been
unloaded before we remove it from GDB's tables. */
notify_solib_unloaded (current_program_space, *gdb_iter,
still_in_use, false);
/* Unless the user loaded it explicitly, free SO's objfile. */
if (gdb_iter->objfile != nullptr
&& !(gdb_iter->objfile->flags & OBJF_USERLOADED)
&& !still_in_use)
gdb_iter->objfile->unlink ();
Because this is the last solib to use that objfile instance, the objfile
is deleted. In the process, disable_breakpoints_in_unloaded_shlib (in
breakpoint.c) is called. The breakpoint locations for the shlib event
breakpoints get marked as "shlib_disabled", which then causes them (I
suppose) to not get inserted and be marked as pending. And then, when
stepping on the subsequent dlmopen call, GDB misses the load of the new
library.
It seems clear to me that, at least, the duplicate objfile detection in
solib_read_symbols needs to be fixed. Right now, to conclude that an
solib matches an existing objfile, it checks that:
- the two have equivalent paths (filename_cmp)
- the two have the same "low" address
In this patch, I remove the filename check. This makes it such that all
the solibs for dynamic linker entries will share the same objfile.
This assumes that no two different solibs / objfiles will have the same
low address. At first glance, it seems like a reasonable assumption to
make, but I don't know if there are some corner cases where this is not
true.
To fix my specific case, I could change the code to resolve the symlinks
and realize that these are all the same file. But I don't think it
would work in a general way. For example, if debugging remotely and
using the target: filesystem, we would need to resolve the symlink on
the target, and I don't think we can do that today (there is no
readlink/realpath operation in the target file I/O).
With this patch, gdb.base/dlmopen-ns-ids.exp passes cleanly:
# of expected passes 44
Change-Id: I3b60051085fb9597b7a72f50122c1104c969908e
Reviewed-By: Guinevere Larsen <guinevere@redhat.com>
Diffstat (limited to 'ld/testsuite/ld-ctf/array-int.c')
0 files changed, 0 insertions, 0 deletions