public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH v4] gdb, gdbserver: support dlmopen()
@ 2022-05-24 18:40 Ben Woodard
  0 siblings, 0 replies; 13+ messages in thread
From: Ben Woodard @ 2022-05-24 18:40 UTC (permalink / raw)
  To: gdb-patches; +Cc: Kevin Buettner, Ben Woodard

Sorry for the late feedback. I was not aware of Markus’s patch until yesterday when Kevin pointed it out to me. I’ve just started playing with a test build that Kevin gave me. 

> On 2022-03-09 12:24, Metzger, Markus T wrote: > Hello Pedro, 
> > 
> >> Should the commit have a Co-Authored-By: tag for H.J.? 
> > 
> > Definitely. I didn't know we were using such tags. 
> > 
> > 
> >> I'm wondering whether ideally symbol lookup would be updated to handle 
> >> different namespaces. I'm thinking of for example 
> >> svr4_iterate_over_objfiles_in_search_order. 
> > 
> > Do you mean that we should restrict the search to the namespace of the 
> > current_objfile argument? And use, say, the default namespace if that is nullptr? 
> Something like that. I mean, doesn't the dynamic loader restrict resolving symbols to the same namespace (and then the global namespace, I guess)? Not sure about completely restricting to the namespace is what users would want, but at least I'd think we should search symbols in the current namespace first before searching the global namespace and then other namespaces (*). The idea being that evaluating an expression in GDB should yield the same result the same expression would yield if coded in the program. (*) Similar to how we search symbols in scope, and can still print static globals of other files even if they're not in scope. It's just a question at this point, I haven't thought about actual use cases, but I think it's worth it to ponder about it. 
I actually have thought about this a lot and have customers which have asked me for this feature. 

The real use case is not dlmopen(); I’m not really sure I’ve seen anyone actually use dlmopen(). The real use case is LD_AUDIT and DT_AUDIT / DT_DEPAUDIT. The dynamic linker is antiquated and doesn’t meet our needs but it evidently cannot be changed. LD_AUDIT allows us to get in there and change the dynamic linker’s behavior so that it can be made to meet our needs. 

Tool developer need to debug their code the same way that app developers do. Without support like Markus’s patch this is EXTREMELY difficult. When we wrote Spindle https://computing.llnl.gov/projects/spindle getting it working was PAINFUL. We had to write tools to write tools. The thing is tool developers and app developers are different. Thinking of it as one big namespace is OK but it is unwieldy for tool developers. One big thing that we need is the ability to disambiguate symbols based upon which namespace they are found in.

App developers have quite a few different compilers that they use. The tool developers have had extra work maintaining their tools because they had to make them work with every compiler and libstdc++ combination in use because they were using the same libstdc++ and boost libs that the apps used. This introduced a lot of extra support cost for the tool developers. What they are trying to get to is a place where they can make one version of the tool using their preferred version lof libstdc++ and boost (and whatever other libraries) and have it insulated from the application. Then they give the application authors some magic invocation for their link line which sets up the DT_AUDIT and DT_DEPAUDIT voila’ their tool is “always on”. The upshot of this is that there will likely be two versions of libstdc++ and likely other libraries loaded into the process’s address space. With Markus’s patch we can now see all the libraries:

 (gdb) info shared
   From                To                  Syms Read   Shared Object Library
   0x00007ffff7fc8090  0x00007ffff7feea45  Yes         /lib64/ld-linux-x86-64.so.2
   0x00007ffff7fb12a0  0x00007ffff7fb9022  Yes         ./auditlib.so
   0x00007ffff7df73f0  0x00007ffff7eff532  Yes         /lib64/libstdc++.so.6
   0x00007ffff7c873b0  0x00007ffff7cf8b58  Yes         /lib64/libm.so.6
   0x00007ffff7c5a670  0x00007ffff7c70c05  Yes         /lib64/libgcc_s.so.1
   0x00007ffff7a82740  0x00007ffff7bf371d  Yes         /lib64/libc.so.6
   0x00007ffff7fc8090  0x00007ffff7feea45  Yes         /lib64/ld-linux-x86-64.so.2
   0x00007ffff777d740  0x00007ffff78ee71d  Yes         /lib64/libc.so.6

Great! The problem is we don’t know which ones are in which namespace. It would be great if we had something like:

 (gdb) info shared
   From                To                  Syms Read   NS	Shared Object Library
   0x00007ffff7fc8090  0x00007ffff7feea45  Yes         1	/lib64/ld-linux-x86-64.so.2
   0x00007ffff7fb12a0  0x00007ffff7fb9022  Yes         1	./auditlib.so
   0x00007ffff7df73f0  0x00007ffff7eff532  Yes         1	/lib64/libstdc++.so.6
   0x00007ffff7c873b0  0x00007ffff7cf8b58  Yes         0	/lib64/libm.so.6
   0x00007ffff7c5a670  0x00007ffff7c70c05  Yes         0	/lib64/libgcc_s.so.1
   0x00007ffff7a82740  0x00007ffff7bf371d  Yes         1	/lib64/libc.so.6
   0x00007ffff7fc8090  0x00007ffff7feea45  Yes         0	/lib64/ld-linux-x86-64.so.2
   0x00007ffff777d740  0x00007ffff78ee71d  Yes         0	/lib64/libc.so.6

Then when we want to set a breakpoint on a symbol or something we can qualify it with the namespace something like:

(gdb) b 1::printf

The problem with lazily searching all namespaces and setting breakpoints at all of the functions is most evident with C++ inline functions expanded from header files. There can be thousands of functions in the app which are not interesting to a tool developer. Tool developers tend to want to focus on developing the tool while app developers focus on their app. The tool developer may need to use the app as a reproducer while debugging their tool.

Similar problems seem to abound in other places where the lack of a way to disambiguate namespaces can be a problem:

When I say:

(gdb) list <function>

How do I get it to show me the version of the function which is in the first private namespace vs. the version which is in the application?

or when I do something like:

(gdb) break filename.c:231

How do I specify the filename.c TU which is part of the libstdc++.so which is linked into one of the audit libraries rather than the one that is linked into the app. Path to the filename isn’t sufficient because they could have both been built in the same directory structure from different sources. That is why I believe that we need some way to know which shared objects are in which namespace and some way to specify which linkage namespace to search.

Since the linkage namespaces are on a linked list in glibc, I thought referring to the default namespace as 0 and counting the depth and then reusing the C++ namespace qualifier might be a good idea but it is not something that I am hung up on. If someone has a better idea, great.

Anyway, that is my first pass at feedback. Once I get feedback from other developers that I work with and have more time to play with the build that Kevin gave me, I’ll have more for you guys.

-ben


> Pedro Alves


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v4] gdb, gdbserver: support dlmopen()
  2022-05-31  9:29   ` Metzger, Markus T
@ 2022-05-31 18:44     ` Ben Woodard
  0 siblings, 0 replies; 13+ messages in thread
From: Ben Woodard @ 2022-05-31 18:44 UTC (permalink / raw)
  To: Metzger, Markus T; +Cc: Kevin Buettner, gdb-patches



> On May 31, 2022, at 2:29 AM, Metzger, Markus T <markus.t.metzger@intel.com> wrote:
> 
> Thanks Kevin and Ben, for your positive feedback on the usefulness of this.
> 
> The patch has grown into a small series when I added gdbserver support
> and changed several occurrences of direct objfiles traversal with calls to
> gdbarch_iterate_over_objfiles_in_search_order() to take namespaces into
> account.
> 
> The latest version can be found in users/mmetzger/dlmopen.  I'll also send
> it as v5 with a few opens left.
> 
>> While I'm convinced that other work will be needed to improve GDB's UI
>> to both display linker namespaces (e.g.  in the "info shared" command)
>> and accept namespace qualifiers when specifying a symbol (e.g. with a
>> breakpoint command), I think that this current patch is useful as
>> is.  I.e., I'd like to see it (or a modest update) go in as soon as
>> possible.
> 
> Indeed, there are a few known issues and many direct objfiles traversals
> in GDB that all assume that there can be at most one global symbol
> of the same name.  Fixing all of them will be a fair amount of work.
> 
> Ben Woodard:
>> How do I specify the filename.c TU which is part of the libstdc++.so which is
>> linked into one of the audit libraries rather than the one that is linked into the
>> app. Path to the filename isn't sufficient because they could have both been built
>> in the same directory structure from different sources. That is why I believe that
>> we need some way to know which shared objects are in which namespace and
>> some way to specify which linkage namespace to search.
>> 
>> Since the linkage namespaces are on a linked list in glibc, I thought referring to
>> the default namespace as 0 and counting the depth and then reusing the C++
>> namespace qualifier might be a good idea but it is not something that I am hung
>> up on. If someone has a better idea, great.
> 
> I currently keep namespaces hidden and local to SVr4.  The rest of GDB doesn't
> know about them.
> 
> The official namespace id that dlinfo(RTLD_DI_LMID) provides is available for
> dynamic linker notifications but not during the (initial) load map scan - or I
> haven't found it.  I use the address of the r_debug(_ext) object as identifier.
> 

To me this sounds like a weakness in the r_debug interface that glibc provides, shall we bring this up to with the glibc folk?
Sure they would have to bump it up to version 3 but, I do not think any Unix has been here before and we are probably running into issues that no one has had to consider before.

I think that you could probably explain the issue on glibc-alpha better than I can but I can take that on if it would help advance the topic.

> We could simply number them, as Ben suggests, using the ordinal in the
> r_debug chain.  This may be confusing to users who are aware of the actual
> namespace id, though.
> 

I think using the namespace ID is a better idea than my ordinal numbering system. Mostly, I just felt GDB some sort of handle to disambiguate symbols that exist in different namespaces.

> To make GDB aware of namespaces, we'd probably want to partition
> solibs and objfiles by namespace.  This will be a big patch when we change
> program_space::objfiles() to take a namespace id argument and update all
> users.
> 
> Ideally, I think we'd want objfiles to be shared so we only read the debug
> info once per unique instance and relocate it lazily but GDB is currently not
> doing that and it doesn't sound entirely trivial.

Yeah with DWARF being what it is, you really do not want to blow up the memory utilization unnecessarily. However, I think increased memory utilization by debuggers is preferable to not being able to debug audiors effectively.  I am not intimately aware of GDB’s internals but I can imagine a lot of data structures having to change to go from a symbol pointing to one address in one loaded object to a symbol referring to a set of objects and addresses. That doesn’t sound trivial.

-ben

> 
> regards,
> markus.
> Intel Deutschland GmbH
> Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
> Tel: +49 89 99 8853-0, www.intel.de <http://www.intel.de>
> Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva  
> Chairperson of the Supervisory Board: Nicole Lau
> Registered Office: Munich
> Commercial Register: Amtsgericht Muenchen HRB 186928
> 


^ permalink raw reply	[flat|nested] 13+ messages in thread

* RE: [PATCH v4] gdb, gdbserver: support dlmopen()
  2022-05-25 17:12 ` Kevin Buettner
@ 2022-05-31  9:29   ` Metzger, Markus T
  2022-05-31 18:44     ` Ben Woodard
  0 siblings, 1 reply; 13+ messages in thread
From: Metzger, Markus T @ 2022-05-31  9:29 UTC (permalink / raw)
  To: Kevin Buettner, Ben Woodard; +Cc: gdb-patches

Thanks Kevin and Ben, for your positive feedback on the usefulness of this.

The patch has grown into a small series when I added gdbserver support
and changed several occurrences of direct objfiles traversal with calls to
gdbarch_iterate_over_objfiles_in_search_order() to take namespaces into
account.

The latest version can be found in users/mmetzger/dlmopen.  I'll also send
it as v5 with a few opens left.

>While I'm convinced that other work will be needed to improve GDB's UI
>to both display linker namespaces (e.g.  in the "info shared" command)
>and accept namespace qualifiers when specifying a symbol (e.g. with a
>breakpoint command), I think that this current patch is useful as
>is.  I.e., I'd like to see it (or a modest update) go in as soon as
>possible.

Indeed, there are a few known issues and many direct objfiles traversals
in GDB that all assume that there can be at most one global symbol
of the same name.  Fixing all of them will be a fair amount of work.

Ben Woodard:
>How do I specify the filename.c TU which is part of the libstdc++.so which is
>linked into one of the audit libraries rather than the one that is linked into the
>app. Path to the filename isn't sufficient because they could have both been built
>in the same directory structure from different sources. That is why I believe that
>we need some way to know which shared objects are in which namespace and
>some way to specify which linkage namespace to search.
>
>Since the linkage namespaces are on a linked list in glibc, I thought referring to
>the default namespace as 0 and counting the depth and then reusing the C++
>namespace qualifier might be a good idea but it is not something that I am hung
>up on. If someone has a better idea, great.

I currently keep namespaces hidden and local to SVr4.  The rest of GDB doesn't
know about them.

The official namespace id that dlinfo(RTLD_DI_LMID) provides is available for
dynamic linker notifications but not during the (initial) load map scan - or I
haven't found it.  I use the address of the r_debug(_ext) object as identifier.

We could simply number them, as Ben suggests, using the ordinal in the
r_debug chain.  This may be confusing to users who are aware of the actual
namespace id, though.

To make GDB aware of namespaces, we'd probably want to partition
solibs and objfiles by namespace.  This will be a big patch when we change
program_space::objfiles() to take a namespace id argument and update all
users.

Ideally, I think we'd want objfiles to be shared so we only read the debug
info once per unique instance and relocate it lazily but GDB is currently not
doing that and it doesn't sound entirely trivial.

regards,
markus.
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de <http://www.intel.de>
Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva  
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v4] gdb, gdbserver: support dlmopen()
  2021-11-17 14:28 Markus Metzger
                   ` (2 preceding siblings ...)
  2022-03-03 18:32 ` Pedro Alves
@ 2022-05-25 17:12 ` Kevin Buettner
  2022-05-31  9:29   ` Metzger, Markus T
  3 siblings, 1 reply; 13+ messages in thread
From: Kevin Buettner @ 2022-05-25 17:12 UTC (permalink / raw)
  To: gdb-patches

On Wed, 17 Nov 2021 15:28:12 +0100
Markus Metzger via Gdb-patches <gdb-patches@sourceware.org> wrote:

> This patch was originally developed by H.J.  Last version:
> https://sourceware.org/pipermail/gdb-patches/2021-October/182353.html
> 
> Changes in v4:
> 
> 1. add gdb.base/dlmopen.exp
> 2. extend svr4_same() to compare the load offset in addition to the name
> 3. supply l_addr_inferior for default DSO to make it work with #2
> 4. fix bugs in gdbserver/linux-low.cc
> 
> Changes in v3:
> 
> 1. Fix gdbserver support.
> 
> Changes in v2:
> 
> 1. Don't check shared libraries in other namespaces when updating shared
> libraries in a new namespace.
> 
> Regression-tested on x86-64 linux.
> 
> ---
> 
> In glibc, the r_debug structure contains (amongst others) the following
> fields:
> 
>   int r_version:
>     Version number for this protocol.  It should be greater than 0.
> 
> If r_version is 2, struct r_debug is extended to struct r_debug_extended
> with one additional field:
> 
>   struct r_debug_extended *r_next;
>     Link to the next r_debug_extended structure.  Each r_debug_extended
>     structure represents a different namespace.  The first r_debug_extended
>     structure is for the default namespace.
> 
> 1. Change solib_svr4_r_map argument to take the debug base.
> 2. Add solib_svr4_r_next to find the link map in the next namespace from
> the r_next field.
> 3. Update svr4_current_sos_direct to get the link map in the next namespace
> from the r_next field.
> 4. Don't check shared libraries in other namespaces when updating shared
> libraries in a new namespace.
> 5. Update svr4_same to check the load offset in addition to the name
> 6. Update svr4_default_sos to also set l_addr_inferior
> 
> Add gdb.base/dlmopen.exp to test this.
> 
> This fixes PR 11839.

I've been playing around with this patch on Fedora 36 which uses
glibc-2.35.  I also found an LD_AUDIT library to play with.  See:

https://github.com/buildsi/ldaudit-yaml

Here are the interesting portions of a session when debugging whoami
while using the audit library on F36:

  [kev@f36-1 ldaudit-yaml]$ /mesquite2/sourceware-git/f36-dlmopen/inst/bin/gdb -q whoami
  Reading symbols from whoami...
  [...]
  (gdb) b main
  Breakpoint 1 at 0x25a0: file ../src/whoami.c, line 59.
  (gdb) set env LD_AUDIT=./auditlib.so
  (gdb) run
  Starting program: /usr/bin/whoami 
  auditlib:
    la_version: 2
    audits:
    - event: handshake
	function: la_version
	value: 2
  [lots of output from the audit library snipped]
  Breakpoint 1, main (argc=1, argv=0x7fffffffdbe8) at ../src/whoami.c:59
  59	{
  (gdb) info shared
  From                To                  Syms Read   Shared Object Library
  0x00007ffff7fc8090  0x00007ffff7feea45  Yes         /lib64/ld-linux-x86-64.so.2
  0x00007ffff7fb12a0  0x00007ffff7fb9022  Yes         ./auditlib.so
  0x00007ffff7df73f0  0x00007ffff7eff532  Yes         /lib64/libstdc++.so.6
  0x00007ffff7c873b0  0x00007ffff7cf8b58  Yes         /lib64/libm.so.6
  0x00007ffff7c5a670  0x00007ffff7c70c05  Yes         /lib64/libgcc_s.so.1
  0x00007ffff7a82740  0x00007ffff7bf371d  Yes         /lib64/libc.so.6
  0x00007ffff7fc8090  0x00007ffff7feea45  Yes         /lib64/ld-linux-x86-64.so.2
  0x00007ffff777d740  0x00007ffff78ee71d  Yes         /lib64/libc.so.6

Without this patch (or when running on an OS with a version of glibc
earlier than 2.35), I see:

  (gdb) info shared
  From                To                  Syms Read   Shared Object Library
  0x00007ffff7fc9090  0x00007ffff7fee593  Yes         /lib64/ld-linux-x86-64.so.2
  0x00007ffff778c740  0x00007ffff78fee3d  Yes         /lib64/libc.so.6

I found that I can also place breakpoints in the audit library and run
to those breakpoints.  When stopped at a breakpoint, the backtrace looks
reasonable.  E.g...

  Breakpoint 2, doPrint (output=...) at auditlib.cpp:30
  30	  char * out = getenv("LDAUDIT_OUTFILE");
  (gdb) bt
  #0  doPrint (output=...) at auditlib.cpp:30
  #1  0x00007ffff7fb4cf0 in la_activity (cookie=<optimized out>, 
	flag=<optimized out>) at /usr/include/c++/11/bits/char_traits.h:357
  #2  0x00007ffff7fdf415 in _dl_audit_activity_map (l=l@entry=0x7ffff7ffe2a0, 
	action=action@entry=1) at dl-audit.c:33
  #3  0x00007ffff7fe6c98 in dl_main (phdr=<optimized out>, 
	phnum=<optimized out>, user_entry=<optimized out>, auxv=<optimized out>)
	at rtld.c:1832
  #4  0x00007ffff7fe328f in _dl_sysdep_start (
	start_argptr=start_argptr@entry=0x7fffffffdbe0, 
	dl_main=dl_main@entry=0x7ffff7fe52a0 <dl_main>) at ../elf/dl-sysdep.c:256
  #5  0x00007ffff7fe503e in _dl_start_final (arg=0x7fffffffdbe0) at rtld.c:507
  #6  _dl_start (arg=0x7fffffffdbe0) at rtld.c:596
  #7  0x00007ffff7fe3e38 in _start () from /lib64/ld-linux-x86-64.so.2

While I'm convinced that other work will be needed to improve GDB's UI
to both display linker namespaces (e.g.  in the "info shared" command)
and accept namespace qualifiers when specifying a symbol (e.g. with a
breakpoint command), I think that this current patch is useful as
is.  I.e., I'd like to see it (or a modest update) go in as soon as
possible.

Kevin


^ permalink raw reply	[flat|nested] 13+ messages in thread

* RE: [PATCH v4] gdb, gdbserver: support dlmopen()
  2022-03-29 16:13       ` Metzger, Markus T
@ 2022-04-08 12:40         ` Metzger, Markus T
  0 siblings, 0 replies; 13+ messages in thread
From: Metzger, Markus T @ 2022-04-08 12:40 UTC (permalink / raw)
  To: Pedro Alves, Tom Tromey; +Cc: gdb-patches

Hello,

>I extended my test case by adding a dependent DSO that gets loaded
>automatically when I dlmopen() the test DSO.  When breaking inside the test
>DSO, I change a variable inside the dependent DSO from GDB.  This should
>affect the current namespace but not others.  And, since the variable is not
>located in the current DSO, GDB would need to traverse that namespace
>to find the correct instance.
>
>This appears to be working [1] as long as I stay on the ELF symbol level.
>GDB has separate objfile and separate so_list objects for each instance
>of a DSO inside a separate namespace.
>
>When I compile the test with clang, however, which does not add a declaration
>for the variable inside the test DSO, GDB reads the DWARF of the dependent
>DSO, which contains the variable.  It does that several times for different
>instances.
>When looking up the name, GDB uses different objfile objects and finds different
>symbol objects, yet it ends up accessing the same target virtual address.

I eventually found the root cause of this.  When looking up the symbol, GDB
finds the correct symbol in the correct objfile in the correct linker namespace.

Instead of taking the value directly from the symbol, though, get_symbol_address()
iterates over all objfiles again and looks for a matching minimal symbol.  This time,
however, it does not use iterate_over_objfiles_in_search_order() so it does not
take linker namespaces into account and it may hence find a different instance
of that symbol in a different objfile in a different linker namespace.

This was introduced in

    4b610737f02 Handle copy relocations

The commit message says that this is to handle copies inside the main program.
Wouldn't it suffice to look inside the main objfile in that case, rather than inside
each objfile?  This would also be a lot faster.

Alternatively, it would need to use iterate_over_objfiles_in_search_order().  That
would be still slow, however, assuming we don't run into issues with recursive calls.

Could copy relocations be handled when processing a shared library's symbols so
get_symbol_address() and get_msymbol_address() can just return the address
stored inside the respective symbol object?

I counted 123 other places that iterate over objfiles in a flat list.  I hope they don't
try to lookup symbols...

Regards,
Markus.

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de <http://www.intel.de>
Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva  
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 13+ messages in thread

* RE: [PATCH v4] gdb, gdbserver: support dlmopen()
  2022-03-09 14:15     ` Pedro Alves
@ 2022-03-29 16:13       ` Metzger, Markus T
  2022-04-08 12:40         ` Metzger, Markus T
  0 siblings, 1 reply; 13+ messages in thread
From: Metzger, Markus T @ 2022-03-29 16:13 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches, Tom Tromey

Hello Pedro,

>>> I'm wondering whether ideally symbol lookup would be updated to handle
>>> different namespaces.  I'm thinking of for example
>>> svr4_iterate_over_objfiles_in_search_order.
>>
>> Do you mean that we should restrict the search to the namespace of the
>> current_objfile argument?  And use, say, the default namespace if that is
>nullptr?
>
>Something like that.  I mean, doesn't the dynamic loader restrict resolving
>symbols to
>the same namespace (and then the global namespace, I guess)?  Not sure about
>completely restricting to
>the namespace is what users would want, but at least I'd think we should search
>symbols in the current
>namespace first before searching the global namespace and then other
>namespaces (*).  The idea being that
>evaluating an expression in GDB should yield the same result the same
>expression would yield if coded
>in the program.
>
>(*) Similar to how we search symbols in scope, and can still print static globals of
>other
>files even if they're not in scope.
>
>It's just a question at this point, I haven't thought about actual use cases, but I
>think
>it's worth it to ponder about it.

Indeed.  From what I could find about linker namespaces, symbol lookup is actually
restricted entirely to the namespace.

I extended my test case by adding a dependent DSO that gets loaded
automatically when I dlmopen() the test DSO.  When breaking inside the test
DSO, I change a variable inside the dependent DSO from GDB.  This should
affect the current namespace but not others.  And, since the variable is not
located in the current DSO, GDB would need to traverse that namespace
to find the correct instance.

This appears to be working [1] as long as I stay on the ELF symbol level.
GDB has separate objfile and separate so_list objects for each instance
of a DSO inside a separate namespace.

When I compile the test with clang, however, which does not add a declaration
for the variable inside the test DSO, GDB reads the DWARF of the dependent
DSO, which contains the variable.  It does that several times for different instances.
When looking up the name, GDB uses different objfile objects and finds different
symbol objects, yet it ends up accessing the same target virtual address.

Tom commented on objfile handling and seemed to suggest that GDB would
only have a single object per binary file.  That's not the case but maybe GDB only
keeps a single DWARF instance?  This shouldn't matter since the relocation offsets
are different for the different DSO instances - provided we stored them outside
of the CU objects.  I'm still debugging this.

Regards,
Markus.

[1] I'm currently ignoring things like manually added symbol files.  They are
assumed to be added to the initial namespace.  This won't be easy to extend
since I'm not getting the namespace id when traversing the r_debug chain
(e.g. on a full update) so I'm using the r_debug address to identify namespaces,
but that will not be available to users, which would want to use the namespace
id returned by dlinfo().  A topic for another day.
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de <http://www.intel.de>
Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva  
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v4] gdb, gdbserver: support dlmopen()
  2022-03-09 12:24   ` Metzger, Markus T
@ 2022-03-09 14:15     ` Pedro Alves
  2022-03-29 16:13       ` Metzger, Markus T
  0 siblings, 1 reply; 13+ messages in thread
From: Pedro Alves @ 2022-03-09 14:15 UTC (permalink / raw)
  To: Metzger, Markus T; +Cc: gdb-patches


On 2022-03-09 12:24, Metzger, Markus T wrote:
> Hello Pedro,
> 
>> Should the commit have a Co-Authored-By: tag for H.J.?
> 
> Definitely.  I didn't know we were using such tags.
> 
> 
>> I'm wondering whether ideally symbol lookup would be updated to handle
>> different namespaces.  I'm thinking of for example
>> svr4_iterate_over_objfiles_in_search_order.
> 
> Do you mean that we should restrict the search to the namespace of the
> current_objfile argument?  And use, say, the default namespace if that is nullptr?

Something like that.  I mean, doesn't the dynamic loader restrict resolving symbols to
the same namespace (and then the global namespace, I guess)?  Not sure about completely restricting to
the namespace is what users would want, but at least I'd think we should search symbols in the current
namespace first before searching the global namespace and then other namespaces (*).  The idea being that
evaluating an expression in GDB should yield the same result the same expression would yield if coded
in the program.

(*) Similar to how we search symbols in scope, and can still print static globals of other
files even if they're not in scope.

It's just a question at this point, I haven't thought about actual use cases, but I think
it's worth it to ponder about it.

Pedro Alves

> 
> 
>> Can you describe what xml changes were necessary?  Do we need to update
>> the manual where the xml/packet are described?
> 
> This patch doesn't change the XML so everything ends up in a flat list and GDB
> doesn't really know about namespaces.  This prevents incremental updates
> and would also prevent things like the above.
> 
> I had planned to leave support for that for backwards compatibility and update
> the XML to pass namespaces for future versions.  See also
> https://sourceware.org/pipermail/gdb-patches/2022-February/186135.html.
> 
> If we need GDB to know about namespaces, however, this wouldn't work and
> we'd instead need to conditionalize namespace support based on gdbserver
> features.
> 

^ permalink raw reply	[flat|nested] 13+ messages in thread

* RE: [PATCH v4] gdb, gdbserver: support dlmopen()
  2022-03-03 18:32 ` Pedro Alves
@ 2022-03-09 12:24   ` Metzger, Markus T
  2022-03-09 14:15     ` Pedro Alves
  0 siblings, 1 reply; 13+ messages in thread
From: Metzger, Markus T @ 2022-03-09 12:24 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches

Hello Pedro,

>Should the commit have a Co-Authored-By: tag for H.J.?

Definitely.  I didn't know we were using such tags.


>I'm wondering whether ideally symbol lookup would be updated to handle
>different namespaces.  I'm thinking of for example
>svr4_iterate_over_objfiles_in_search_order.

Do you mean that we should restrict the search to the namespace of the
current_objfile argument?  And use, say, the default namespace if that is nullptr?


>Can you describe what xml changes were necessary?  Do we need to update
>the manual where the xml/packet are described?

This patch doesn't change the XML so everything ends up in a flat list and GDB
doesn't really know about namespaces.  This prevents incremental updates
and would also prevent things like the above.

I had planned to leave support for that for backwards compatibility and update
the XML to pass namespaces for future versions.  See also
https://sourceware.org/pipermail/gdb-patches/2022-February/186135.html.

If we need GDB to know about namespaces, however, this wouldn't work and
we'd instead need to conditionalize namespace support based on gdbserver
features.

Regards,
Markus.
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de <http://www.intel.de>
Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva  
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 13+ messages in thread

* RE: [PATCH v4] gdb, gdbserver: support dlmopen()
  2022-03-01 19:10 ` Tom Tromey
@ 2022-03-09 12:23   ` Metzger, Markus T
  0 siblings, 0 replies; 13+ messages in thread
From: Metzger, Markus T @ 2022-03-09 12:23 UTC (permalink / raw)
  To: Tom Tromey; +Cc: Markus Metzger via Gdb-patches

Hello Tom,

>Markus> +  /* There may be different instances of the same library, in different
>Markus> +     namespaces.  Each instance, however, must have been loaded at a
>Markus> +     different address so its relocation offset would be different.  */
>Markus> +  const lm_info_svr4 *lmg = (const lm_info_svr4 *) gdb->lm_info;
>Markus> +  const lm_info_svr4 *lmi = (const lm_info_svr4 *) inferior->lm_info;
>Markus> +
>Markus> +  return (lmg->l_addr_inferior == lmi->l_addr_inferior);
>
>Can you explain how objfile handling works?
>
>I'd suspect that just a single objfile will be made for a given .so.
>But, if that comment is correct (I think it is) then that would be
>wrong, because an objfile has to know the correct runtime offset.

GDB creates separate objfile objects per instance.  I placed a breakpoint at
symbol_file_add_with_addrs and compared the objfile * returned from
objfile::make().

See also the output of 'maint print objfiles':
[...]
Object file /users/mmetzger/gdb/build/dlmopen-fc35/gdb/testsuite/outputs/gdb.base/dlmopen/dlmopen-lib.1.so:  Objfile at 0x4bec900, bfd at 0x4ee7f60, 19 minsyms

Psymtabs:
/users/mmetzger/gdb/git/gdb/testsuite/gdb.base/dlmopen-lib.c at 0x4d05200

Object file /users/mmetzger/gdb/build/dlmopen-fc35/gdb/testsuite/outputs/gdb.base/dlmopen/dlmopen-lib.1.so:  Objfile at 0x4beee90, bfd at 0x4ee7f60, 19 minsyms

Psymtabs:
/users/mmetzger/gdb/git/gdb/testsuite/gdb.base/dlmopen-lib.c at 0x4d05200
[...]

Regards,
Markus.
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de <http://www.intel.de>
Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva  
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v4] gdb, gdbserver: support dlmopen()
  2021-11-17 14:28 Markus Metzger
  2022-01-21 11:42 ` Metzger, Markus T
  2022-03-01 19:10 ` Tom Tromey
@ 2022-03-03 18:32 ` Pedro Alves
  2022-03-09 12:24   ` Metzger, Markus T
  2022-05-25 17:12 ` Kevin Buettner
  3 siblings, 1 reply; 13+ messages in thread
From: Pedro Alves @ 2022-03-03 18:32 UTC (permalink / raw)
  To: Markus Metzger, gdb-patches

On 2021-11-17 14:28, Markus Metzger via Gdb-patches wrote:
> This patch was originally developed by H.J.  Last version:
> https://sourceware.org/pipermail/gdb-patches/2021-October/182353.html
> 

Should the commit have a Co-Authored-By: tag for H.J.?

> Changes in v4:
> 
> 1. add gdb.base/dlmopen.exp
> 2. extend svr4_same() to compare the load offset in addition to the name
> 3. supply l_addr_inferior for default DSO to make it work with #2
> 4. fix bugs in gdbserver/linux-low.cc
> 
> Changes in v3:
> 
> 1. Fix gdbserver support.
> 
> Changes in v2:
> 
> 1. Don't check shared libraries in other namespaces when updating shared
> libraries in a new namespace.
> 
> Regression-tested on x86-64 linux.
> 
> ---
> 
> In glibc, the r_debug structure contains (amongst others) the following
> fields:
> 
>   int r_version:
>     Version number for this protocol.  It should be greater than 0.
> 
> If r_version is 2, struct r_debug is extended to struct r_debug_extended
> with one additional field:
> 
>   struct r_debug_extended *r_next;
>     Link to the next r_debug_extended structure.  Each r_debug_extended
>     structure represents a different namespace.  The first r_debug_extended
>     structure is for the default namespace.
> 
> 1. Change solib_svr4_r_map argument to take the debug base.
> 2. Add solib_svr4_r_next to find the link map in the next namespace from
> the r_next field.
> 3. Update svr4_current_sos_direct to get the link map in the next namespace
> from the r_next field.
> 4. Don't check shared libraries in other namespaces when updating shared
> libraries in a new namespace.
> 5. Update svr4_same to check the load offset in addition to the name
> 6. Update svr4_default_sos to also set l_addr_inferior
> 
> Add gdb.base/dlmopen.exp to test this.

I'm wondering whether ideally symbol lookup would be updated to handle
different namespaces.  I'm thinking of for example
svr4_iterate_over_objfiles_in_search_order.


Can you describe what xml changes were necessary?  Do we need to update
the manual where the xml/packet are described?


> +++ b/gdb/testsuite/gdb.base/dlmopen.exp
> @@ -0,0 +1,141 @@
> +# This testcase is part of GDB, the GNU debugger.
> +#
> +# Copyright 2021 Free Software Foundation, Inc.
> +#
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation; either version 3 of the License, or
> +# (at your option) any later version.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program.  If not, see <http://www.gnu.org/licenses/>.
> +

Please a short description of what is being tested, here.

> +clean_restart $binfile
> +
> +# Start the test program.
> +set test_spawn_id [spawn_wait_for_attach $binfile]
> +set testpid [spawn_id_get_pid $test_spawn_id]
> +
> +# Attach.
> +gdb_test "attach $testpid" "Attaching to program.*, process $testpid.*"

Seems like a can_spawn_for_attach check is missing.

> +
> +with_test_prefix "attach" {
> +    # Remove the pause.  We no longer need it.
> +    gdb_test "print pause = 0" "\\\$1 = 0"
> +
> +    # Set the same breakpoints again.  This time, however, we do not allow the
> +    # breakpoint to be pending since the library has already been loaded.
> +    gdb_breakpoint $srcfile_lib:$bp_inc
> +    gdb_breakpoint $srcfile:$bp_main
> +
> +    test_dlmopen
> +}


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v4] gdb, gdbserver: support dlmopen()
  2021-11-17 14:28 Markus Metzger
  2022-01-21 11:42 ` Metzger, Markus T
@ 2022-03-01 19:10 ` Tom Tromey
  2022-03-09 12:23   ` Metzger, Markus T
  2022-03-03 18:32 ` Pedro Alves
  2022-05-25 17:12 ` Kevin Buettner
  3 siblings, 1 reply; 13+ messages in thread
From: Tom Tromey @ 2022-03-01 19:10 UTC (permalink / raw)
  To: Markus Metzger via Gdb-patches

>>>>> "Markus" == Markus Metzger via Gdb-patches <gdb-patches@sourceware.org> writes:

Markus> This patch was originally developed by H.J.  Last version:
Markus> https://sourceware.org/pipermail/gdb-patches/2021-October/182353.html

Hi.  Thanks to you and HJ for doing this -- dlmopen has long been a hole
in gdb.

Markus> +  /* There may be different instances of the same library, in different
Markus> +     namespaces.  Each instance, however, must have been loaded at a
Markus> +     different address so its relocation offset would be different.  */
Markus> +  const lm_info_svr4 *lmg = (const lm_info_svr4 *) gdb->lm_info;
Markus> +  const lm_info_svr4 *lmi = (const lm_info_svr4 *) inferior->lm_info;
Markus> +
Markus> +  return (lmg->l_addr_inferior == lmi->l_addr_inferior);

Can you explain how objfile handling works?

I'd suspect that just a single objfile will be made for a given .so.
But, if that comment is correct (I think it is) then that would be
wrong, because an objfile has to know the correct runtime offset.

Maybe I'm mistaken about how the objfiles are created.
That would be good...

thanks,
Tom

^ permalink raw reply	[flat|nested] 13+ messages in thread

* RE: [PATCH v4] gdb, gdbserver: support dlmopen()
  2021-11-17 14:28 Markus Metzger
@ 2022-01-21 11:42 ` Metzger, Markus T
  2022-03-01 19:10 ` Tom Tromey
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 13+ messages in thread
From: Metzger, Markus T @ 2022-01-21 11:42 UTC (permalink / raw)
  To: gdb-patches

Kindly pinging,
Markus.

>-----Original Message-----
>From: Metzger, Markus T <markus.t.metzger@intel.com>
>Sent: Wednesday, November 17, 2021 3:28 PM
>To: gdb-patches@sourceware.org
>Subject: [PATCH v4] gdb, gdbserver: support dlmopen()
>
>This patch was originally developed by H.J.  Last version:
>https://sourceware.org/pipermail/gdb-patches/2021-October/182353.html
>
>Changes in v4:
>
>1. add gdb.base/dlmopen.exp
>2. extend svr4_same() to compare the load offset in addition to the name
>3. supply l_addr_inferior for default DSO to make it work with #2
>4. fix bugs in gdbserver/linux-low.cc
>
>Changes in v3:
>
>1. Fix gdbserver support.
>
>Changes in v2:
>
>1. Don't check shared libraries in other namespaces when updating shared
>libraries in a new namespace.
>
>Regression-tested on x86-64 linux.
>
>---
>
>In glibc, the r_debug structure contains (amongst others) the following
>fields:
>
>  int r_version:
>    Version number for this protocol.  It should be greater than 0.
>
>If r_version is 2, struct r_debug is extended to struct r_debug_extended
>with one additional field:
>
>  struct r_debug_extended *r_next;
>    Link to the next r_debug_extended structure.  Each r_debug_extended
>    structure represents a different namespace.  The first r_debug_extended
>    structure is for the default namespace.
>
>1. Change solib_svr4_r_map argument to take the debug base.
>2. Add solib_svr4_r_next to find the link map in the next namespace from
>the r_next field.
>3. Update svr4_current_sos_direct to get the link map in the next namespace
>from the r_next field.
>4. Don't check shared libraries in other namespaces when updating shared
>libraries in a new namespace.
>5. Update svr4_same to check the load offset in addition to the name
>6. Update svr4_default_sos to also set l_addr_inferior
>
>Add gdb.base/dlmopen.exp to test this.
>
>This fixes PR 11839.
>---
> gdb/linux-tdep.c                     |   2 +
> gdb/mips-fbsd-tdep.c                 |   2 +
> gdb/mips-netbsd-tdep.c               |   2 +
> gdb/solib-svr4.c                     |  89 ++++++++--
> gdb/solib-svr4.h                     |   3 +
> gdb/testsuite/gdb.base/dlmopen-lib.c |  25 +++
> gdb/testsuite/gdb.base/dlmopen.c     |  56 +++++++
> gdb/testsuite/gdb.base/dlmopen.exp   | 141 ++++++++++++++++
> gdbserver/linux-low.cc               | 242 ++++++++++++++++-----------
> 9 files changed, 450 insertions(+), 112 deletions(-)
> create mode 100644 gdb/testsuite/gdb.base/dlmopen-lib.c
> create mode 100644 gdb/testsuite/gdb.base/dlmopen.c
> create mode 100644 gdb/testsuite/gdb.base/dlmopen.exp
>
>diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c
>index e2cff83086a..cb8e52d760a 100644
>--- a/gdb/linux-tdep.c
>+++ b/gdb/linux-tdep.c
>@@ -2739,6 +2739,7 @@ linux_ilp32_fetch_link_map_offsets ()
>       lmo.r_map_offset = 4;
>       lmo.r_brk_offset = 8;
>       lmo.r_ldsomap_offset = -1;
>+      lmo.r_next_offset = 20;
>
>       /* Everything we need is in the first 20 bytes.  */
>       lmo.link_map_size = 20;
>@@ -2767,6 +2768,7 @@ linux_lp64_fetch_link_map_offsets ()
>       lmo.r_map_offset = 8;
>       lmo.r_brk_offset = 16;
>       lmo.r_ldsomap_offset = -1;
>+      lmo.r_next_offset = 40;
>
>       /* Everything we need is in the first 40 bytes.  */
>       lmo.link_map_size = 40;
>diff --git a/gdb/mips-fbsd-tdep.c b/gdb/mips-fbsd-tdep.c
>index 0b7c97c445f..00e38a8f1c4 100644
>--- a/gdb/mips-fbsd-tdep.c
>+++ b/gdb/mips-fbsd-tdep.c
>@@ -495,6 +495,7 @@ mips_fbsd_ilp32_fetch_link_map_offsets (void)
>       lmo.r_map_offset = 4;
>       lmo.r_brk_offset = 8;
>       lmo.r_ldsomap_offset = -1;
>+      lmo.r_next_offset = -1;
>
>       lmo.link_map_size = 24;
>       lmo.l_addr_offset = 0;
>@@ -522,6 +523,7 @@ mips_fbsd_lp64_fetch_link_map_offsets (void)
>       lmo.r_map_offset = 8;
>       lmo.r_brk_offset = 16;
>       lmo.r_ldsomap_offset = -1;
>+      lmo.r_next_offset = -1;
>
>       lmo.link_map_size = 48;
>       lmo.l_addr_offset = 0;
>diff --git a/gdb/mips-netbsd-tdep.c b/gdb/mips-netbsd-tdep.c
>index a32ae5e3a29..12d702ef359 100644
>--- a/gdb/mips-netbsd-tdep.c
>+++ b/gdb/mips-netbsd-tdep.c
>@@ -308,6 +308,7 @@ mipsnbsd_ilp32_fetch_link_map_offsets (void)
>       lmo.r_map_offset = 4;
>       lmo.r_brk_offset = 8;
>       lmo.r_ldsomap_offset = -1;
>+      lmo.r_next_offset = -1;
>
>       /* Everything we need is in the first 24 bytes.  */
>       lmo.link_map_size = 24;
>@@ -336,6 +337,7 @@ mipsnbsd_lp64_fetch_link_map_offsets (void)
>       lmo.r_map_offset = 8;
>       lmo.r_brk_offset = 16;
>       lmo.r_ldsomap_offset = -1;
>+      lmo.r_next_offset = -1;
>
>       /* Everything we need is in the first 40 bytes.  */
>       lmo.link_map_size = 48;
>diff --git a/gdb/solib-svr4.c b/gdb/solib-svr4.c
>index 3de1bb9c7f7..d57a3c1ffdb 100644
>--- a/gdb/solib-svr4.c
>+++ b/gdb/solib-svr4.c
>@@ -174,7 +174,16 @@ svr4_same_1 (const char *gdb_so_name, const char
>*inferior_so_name)
> static int
> svr4_same (struct so_list *gdb, struct so_list *inferior)
> {
>-  return (svr4_same_1 (gdb->so_original_name, inferior->so_original_name));
>+  if (!svr4_same_1 (gdb->so_original_name, inferior->so_original_name))
>+    return false;
>+
>+  /* There may be different instances of the same library, in different
>+     namespaces.  Each instance, however, must have been loaded at a
>+     different address so its relocation offset would be different.  */
>+  const lm_info_svr4 *lmg = (const lm_info_svr4 *) gdb->lm_info;
>+  const lm_info_svr4 *lmi = (const lm_info_svr4 *) inferior->lm_info;
>+
>+  return (lmg->l_addr_inferior == lmi->l_addr_inferior);
> }
>
> static std::unique_ptr<lm_info_svr4>
>@@ -767,7 +776,7 @@ locate_base (struct svr4_info *info)
>    RT_CONSISTENT.  */
>
> static CORE_ADDR
>-solib_svr4_r_map (struct svr4_info *info)
>+solib_svr4_r_map (CORE_ADDR debug_base)
> {
>   struct link_map_offsets *lmo = svr4_fetch_link_map_offsets ();
>   struct type *ptr_type = builtin_type (target_gdbarch ())->builtin_data_ptr;
>@@ -775,7 +784,7 @@ solib_svr4_r_map (struct svr4_info *info)
>
>   try
>     {
>-      addr = read_memory_typed_address (info->debug_base + lmo-
>>r_map_offset,
>+      addr = read_memory_typed_address (debug_base + lmo->r_map_offset,
> 					ptr_type);
>     }
>   catch (const gdb_exception_error &ex)
>@@ -829,6 +838,35 @@ solib_svr4_r_ldsomap (struct svr4_info *info)
> 				    ptr_type);
> }
>
>+/* Find the next namespace from the r_next field.  */
>+
>+static CORE_ADDR
>+solib_svr4_r_next (CORE_ADDR debug_base)
>+{
>+  struct link_map_offsets *lmo = svr4_fetch_link_map_offsets ();
>+  struct type *ptr_type = builtin_type (target_gdbarch ())->builtin_data_ptr;
>+  enum bfd_endian byte_order = type_byte_order (ptr_type);
>+  ULONGEST version = 0;
>+
>+  try
>+    {
>+      version
>+	= read_memory_unsigned_integer (debug_base + lmo->r_version_offset,
>+					lmo->r_version_size, byte_order);
>+    }
>+  catch (const gdb_exception_error &ex)
>+    {
>+      exception_print (gdb_stderr, ex);
>+    }
>+
>+  /* The r_next field is added with r_version == 2.  */
>+  if (version < 2 || lmo->r_next_offset == -1)
>+    return 0;
>+
>+  return read_memory_typed_address (debug_base + lmo->r_next_offset,
>+				    ptr_type);
>+}
>+
> /* On Solaris systems with some versions of the dynamic linker,
>    ld.so's l_name pointer points to the SONAME in the string table
>    rather than into writable memory.  So that GDB can find shared
>@@ -886,7 +924,7 @@ open_symbol_file_object (int from_tty)
>     return 0;	/* failed somehow...  */
>
>   /* First link map member should be the executable.  */
>-  lm = solib_svr4_r_map (info);
>+  lm = solib_svr4_r_map (info->debug_base);
>   if (lm == 0)
>     return 0;	/* failed somehow...  */
>
>@@ -1178,7 +1216,7 @@ svr4_default_sos (svr4_info *info)
>   newobj->lm_info = li;
>
>   /* Nothing will ever check the other fields if we set l_addr_p.  */
>-  li->l_addr = info->debug_loader_offset;
>+  li->l_addr = li->l_addr_inferior = info->debug_loader_offset;
>   li->l_addr_p = 1;
>
>   strncpy (newobj->so_name, info->debug_loader_name,
>SO_NAME_MAX_PATH_SIZE - 1);
>@@ -1323,7 +1361,7 @@ svr4_current_sos_direct (struct svr4_info *info)
>
>   /* Walk the inferior's link map list, and build our list of
>      `struct so_list' nodes.  */
>-  lm = solib_svr4_r_map (info);
>+  lm = solib_svr4_r_map (info->debug_base);
>   if (lm)
>     svr4_read_so_list (info, lm, 0, &link_ptr, ignore_first);
>
>@@ -1335,6 +1373,18 @@ svr4_current_sos_direct (struct svr4_info *info)
>   if (lm)
>     svr4_read_so_list (info, lm, 0, &link_ptr, 0);
>
>+  /* Get the next namespace from the r_next field.  */
>+  lm = solib_svr4_r_next (info->debug_base);
>+  while (lm != 0)
>+    {
>+      /* Get the link map in this namespace.  */
>+      CORE_ADDR link_map = solib_svr4_r_map (lm);
>+      if (link_map != 0)
>+	svr4_read_so_list (info, link_map, 0, &link_ptr, 0);
>+      /* Go to the next namespace.  */
>+      lm = solib_svr4_r_next (lm);
>+    }
>+
>   cleanup.release ();
>
>   if (head == NULL)
>@@ -1706,7 +1756,8 @@ solist_update_full (struct svr4_info *info)
>    failure.  */
>
> static int
>-solist_update_incremental (struct svr4_info *info, CORE_ADDR lm)
>+solist_update_incremental (struct svr4_info *info, CORE_ADDR debug_base,
>+			   CORE_ADDR lm)
> {
>   struct so_list *tail;
>   CORE_ADDR prev_lm;
>@@ -1727,8 +1778,15 @@ solist_update_incremental (struct svr4_info *info,
>CORE_ADDR lm)
>   for (tail = info->solib_list; tail->next != NULL; tail = tail->next)
>     /* Nothing.  */;
>
>-  lm_info_svr4 *li = (lm_info_svr4 *) tail->lm_info;
>-  prev_lm = li->lm_addr;
>+  /* Don't check shared libraries in other namespaces when updating
>+     shared libraries in a new namespace.  */
>+  if (debug_base == info->debug_base)
>+    {
>+      lm_info_svr4 *li = (lm_info_svr4 *) tail->lm_info;
>+      prev_lm = li->lm_addr;
>+    }
>+  else
>+    prev_lm = 0;
>
>   /* Read the new objects.  */
>   if (info->using_xfer)
>@@ -1869,13 +1927,6 @@ svr4_handle_solib_event (void)
> 	  return;
>       }
>
>-    /* GDB does not currently support libraries loaded via dlmopen
>-       into namespaces other than the initial one.  We must ignore
>-       any namespace other than the initial namespace here until
>-       support for this is added to GDB.  */
>-    if (debug_base != info->debug_base)
>-      action = DO_NOTHING;
>-
>     if (action == UPDATE_OR_RELOAD)
>       {
> 	try
>@@ -1901,7 +1952,7 @@ svr4_handle_solib_event (void)
>
>   if (action == UPDATE_OR_RELOAD)
>     {
>-      if (!solist_update_incremental (info, lm))
>+      if (!solist_update_incremental (info, debug_base, lm))
> 	action = FULL_RELOAD;
>     }
>
>@@ -2136,7 +2187,7 @@ enable_break (struct svr4_info *info, int from_tty)
>
>   solib_add (NULL, from_tty, auto_solib_add);
>   sym_addr = 0;
>-  if (info->debug_base && solib_svr4_r_map (info) != 0)
>+  if (info->debug_base && solib_svr4_r_map (info->debug_base) != 0)
>     sym_addr = solib_svr4_r_brk (info);
>
>   if (sym_addr != 0)
>@@ -3087,6 +3138,7 @@ svr4_ilp32_fetch_link_map_offsets (void)
>       lmo.r_map_offset = 4;
>       lmo.r_brk_offset = 8;
>       lmo.r_ldsomap_offset = 20;
>+      lmo.r_next_offset = -1;
>
>       /* Everything we need is in the first 20 bytes.  */
>       lmo.link_map_size = 20;
>@@ -3118,6 +3170,7 @@ svr4_lp64_fetch_link_map_offsets (void)
>       lmo.r_map_offset = 8;
>       lmo.r_brk_offset = 16;
>       lmo.r_ldsomap_offset = 40;
>+      lmo.r_next_offset = -1;
>
>       /* Everything we need is in the first 40 bytes.  */
>       lmo.link_map_size = 40;
>diff --git a/gdb/solib-svr4.h b/gdb/solib-svr4.h
>index 8d94d9cb26e..64854e2edd9 100644
>--- a/gdb/solib-svr4.h
>+++ b/gdb/solib-svr4.h
>@@ -66,6 +66,9 @@ struct link_map_offsets
>     /* Offset of r_debug.r_ldsomap.  */
>     int r_ldsomap_offset;
>
>+    /* Offset of r_debug_extended.r_next.  */
>+    int r_next_offset;
>+
>     /* Size of struct link_map (or equivalent), or at least enough of it
>        to be able to obtain the fields below.  */
>     int link_map_size;
>diff --git a/gdb/testsuite/gdb.base/dlmopen-lib.c
>b/gdb/testsuite/gdb.base/dlmopen-lib.c
>new file mode 100644
>index 00000000000..264659ec9de
>--- /dev/null
>+++ b/gdb/testsuite/gdb.base/dlmopen-lib.c
>@@ -0,0 +1,25 @@
>+/* This testcase is part of GDB, the GNU debugger.
>+
>+   Copyright 2021 Free Software Foundation, Inc.
>+
>+   This program is free software; you can redistribute it and/or modify
>+   it under the terms of the GNU General Public License as published by
>+   the Free Software Foundation; either version 3 of the License, or
>+   (at your option) any later version.
>+
>+   This program is distributed in the hope that it will be useful,
>+   but WITHOUT ANY WARRANTY; without even the implied warranty of
>+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>+   GNU General Public License for more details.
>+
>+   You should have received a copy of the GNU General Public License
>+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
>+
>+*/
>+
>+__attribute__((visibility ("default")))
>+int
>+inc (int n)
>+{
>+  return n + 1;  /* bp.inc.  */
>+}
>diff --git a/gdb/testsuite/gdb.base/dlmopen.c
>b/gdb/testsuite/gdb.base/dlmopen.c
>new file mode 100644
>index 00000000000..49040dc8738
>--- /dev/null
>+++ b/gdb/testsuite/gdb.base/dlmopen.c
>@@ -0,0 +1,56 @@
>+/* This testcase is part of GDB, the GNU debugger.
>+
>+   Copyright 2021 Free Software Foundation, Inc.
>+
>+   This program is free software; you can redistribute it and/or modify
>+   it under the terms of the GNU General Public License as published by
>+   the Free Software Foundation; either version 3 of the License, or
>+   (at your option) any later version.
>+
>+   This program is distributed in the hope that it will be useful,
>+   but WITHOUT ANY WARRANTY; without even the implied warranty of
>+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>+   GNU General Public License for more details.
>+
>+   You should have received a copy of the GNU General Public License
>+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
>+
>+*/
>+
>+#define _GNU_SOURCE
>+#include <dlfcn.h>
>+#include <stddef.h>
>+#include <assert.h>
>+
>+volatile int pause = 1;
>+
>+int
>+main (void)
>+{
>+  void *handle[2];
>+  int (*fun) (int);
>+
>+  handle[0] = dlmopen (LM_ID_NEWLM, DSO_NAME, RTLD_LAZY | RTLD_LOCAL);
>+  assert (handle[0] != NULL);
>+
>+  handle[1] = dlmopen (LM_ID_NEWLM, DSO_NAME, RTLD_LAZY | RTLD_LOCAL);
>+  assert (handle[1] != NULL);
>+
>+  fun = dlsym (handle[0], "inc");
>+  assert (fun != NULL);
>+
>+  while (pause != 0)
>+    ;
>+
>+  fun (42);
>+
>+  dlclose (handle[0]);
>+
>+  fun = dlsym (handle[1], "inc");
>+  assert (fun != NULL);
>+
>+  fun (42);
>+
>+  dlclose (handle[1]);
>+  return 0;  /* bp.main  */
>+}
>diff --git a/gdb/testsuite/gdb.base/dlmopen.exp
>b/gdb/testsuite/gdb.base/dlmopen.exp
>new file mode 100644
>index 00000000000..fbf6fcb6b42
>--- /dev/null
>+++ b/gdb/testsuite/gdb.base/dlmopen.exp
>@@ -0,0 +1,141 @@
>+# This testcase is part of GDB, the GNU debugger.
>+#
>+# Copyright 2021 Free Software Foundation, Inc.
>+#
>+# This program is free software; you can redistribute it and/or modify
>+# it under the terms of the GNU General Public License as published by
>+# the Free Software Foundation; either version 3 of the License, or
>+# (at your option) any later version.
>+#
>+# This program is distributed in the hope that it will be useful,
>+# but WITHOUT ANY WARRANTY; without even the implied warranty of
>+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>+# GNU General Public License for more details.
>+#
>+# You should have received a copy of the GNU General Public License
>+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
>+
>+if { [skip_shlib_tests] } {
>+    unsupported "target does not support dynamic libraries"
>+    return -1
>+}
>+
>+standard_testfile
>+
>+set basename_lib dlmopen-lib
>+set srcfile_lib $srcdir/$subdir/$basename_lib.c
>+set binfile_lib [standard_output_file $basename_lib.so]
>+
>+if { [gdb_compile_shlib $srcfile_lib $binfile_lib {debug}] != "" } {
>+    untested "failed to prepare shlib"
>+    return -1
>+}
>+
>+if { [prepare_for_testing "failed to prepare" $testfile $srcfile \
>+	  [list additional_flags=-DDSO_NAME=\"$binfile_lib\" \
>+	       libs=-ldl debug]] } {
>+    return -1
>+}
>+
>+if { ![runto_main] } {
>+    return -1
>+}
>+
>+# Check that 'info shared' show NUM occurrences of DSO.
>+proc check_dso_count { dso num } {
>+    global gdb_prompt
>+
>+    set count 0
>+    gdb_test_multiple "info shared" "info shared" {
>+	-re "$dso" {
>+	    set count [expr $count + 1]
>+	    exp_continue
>+	}
>+	-re "$gdb_prompt " {
>+	    gdb_assert {$count == $num} $gdb_test_name
>+	}
>+    }
>+}
>+
>+# The actual test.  We run it twice.
>+proc test_dlmopen {} {
>+    global srcfile srcfile_lib binfile_lib bp_main bp_inc
>+
>+    with_test_prefix "dso 1" {
>+	# Try to reach the breakpoint in the dynamically loaded library.
>+	gdb_continue_to_breakpoint "cont to bp.inc" \
>+	    ".*$srcfile_lib:$bp_inc\r\n.*"
>+
>+	# The library should be listed twice.
>+	check_dso_count $binfile_lib 2
>+
>+	# This might help debugging.
>+	gdb_test "info breakpoints" ".*"
>+	gdb_test "print \$pc" ".*"
>+    }
>+
>+    with_test_prefix "dso 2" {
>+	# Try to reach the breakpoint in the dynamically loaded library.
>+	gdb_continue_to_breakpoint "cont to bp.inc" \
>+	    ".*$srcfile_lib:$bp_inc\r\n.*"
>+
>+	# The library should be listed once.
>+	check_dso_count $binfile_lib 1
>+
>+	# This might help debugging.
>+	gdb_test "info breakpoints" ".*"
>+	gdb_test "print \$pc" ".*"
>+    }
>+
>+    with_test_prefix "main" {
>+	# Try to reach the breakpoint in the dynamically loaded library.
>+	gdb_continue_to_breakpoint "cont to bp.main" \
>+	    ".*$srcfile:$bp_main\r\n.*"
>+
>+	# The library should not be listed.
>+	check_dso_count $binfile_lib 0
>+    }
>+}
>+
>+# Remove the pause.  We only need it for the attach test.
>+gdb_test "print pause = 0" "\\\$1 = 0"
>+
>+# The library is not yet loaded.  We need breakpoints to be pending.
>+gdb_test_no_output "set breakpoint pending on"
>+
>+# Break in the to-be-loaded library and at the end of main.
>+set bp_inc [gdb_get_line_number "bp.inc" $srcfile_lib]
>+set bp_main [gdb_get_line_number "bp.main" $srcfile]
>+
>+delete_breakpoints
>+gdb_breakpoint $srcfile_lib:$bp_inc allow-pending
>+gdb_breakpoint $srcfile:$bp_main
>+
>+test_dlmopen
>+
>+# Try the same again when attaching after dlmopen().
>+if { ![can_spawn_for_attach] } {
>+    unsupported "target does not support attach"
>+    return -1
>+}
>+
>+clean_restart $binfile
>+
>+# Start the test program.
>+set test_spawn_id [spawn_wait_for_attach $binfile]
>+set testpid [spawn_id_get_pid $test_spawn_id]
>+
>+# Attach.
>+gdb_test "attach $testpid" "Attaching to program.*, process $testpid.*"
>+
>+with_test_prefix "attach" {
>+    # Remove the pause.  We no longer need it.
>+    gdb_test "print pause = 0" "\\\$1 = 0"
>+
>+    # Set the same breakpoints again.  This time, however, we do not allow the
>+    # breakpoint to be pending since the library has already been loaded.
>+    gdb_breakpoint $srcfile_lib:$bp_inc
>+    gdb_breakpoint $srcfile:$bp_main
>+
>+    test_dlmopen
>+}
>diff --git a/gdbserver/linux-low.cc b/gdbserver/linux-low.cc
>index 34ede238d19..4ad0f94526d 100644
>--- a/gdbserver/linux-low.cc
>+++ b/gdbserver/linux-low.cc
>@@ -6727,6 +6727,9 @@ struct link_map_offsets
>     /* Offset and size of r_debug.r_map.  */
>     int r_map_offset;
>
>+    /* Offset of r_debug_extended.r_next.  */
>+    int r_next_offset;
>+
>     /* Offset to l_addr field in struct link_map.  */
>     int l_addr_offset;
>
>@@ -6743,6 +6746,98 @@ struct link_map_offsets
>     int l_prev_offset;
>   };
>
>+static const struct link_map_offsets lmo_32bit_offsets =
>+  {
>+    0,     /* r_version offset. */
>+    4,     /* r_debug.r_map offset.  */
>+    20,    /* r_debug_extended.r_next.  */
>+    0,     /* l_addr offset in link_map.  */
>+    4,     /* l_name offset in link_map.  */
>+    8,     /* l_ld offset in link_map.  */
>+    12,    /* l_next offset in link_map.  */
>+    16     /* l_prev offset in link_map.  */
>+  };
>+
>+static const struct link_map_offsets lmo_64bit_offsets =
>+  {
>+    0,     /* r_version offset. */
>+    8,     /* r_debug.r_map offset.  */
>+    40,    /* r_debug_extended.r_next.  */
>+    0,     /* l_addr offset in link_map.  */
>+    8,     /* l_name offset in link_map.  */
>+    16,    /* l_ld offset in link_map.  */
>+    24,    /* l_next offset in link_map.  */
>+    32     /* l_prev offset in link_map.  */
>+  };
>+
>+/* Get the loaded shared libraries from one namespace.  */
>+
>+static void
>+read_link_map (std::string &document, CORE_ADDR lm_addr, CORE_ADDR
>lm_prev,
>+	       int ptr_size, const struct link_map_offsets *lmo,
>+	       bool ignore_first, int &header_done)
>+{
>+  CORE_ADDR l_name, l_addr, l_ld, l_next, l_prev;
>+
>+  while (lm_addr
>+	 && read_one_ptr (lm_addr + lmo->l_name_offset,
>+			  &l_name, ptr_size) == 0
>+	 && read_one_ptr (lm_addr + lmo->l_addr_offset,
>+			  &l_addr, ptr_size) == 0
>+	 && read_one_ptr (lm_addr + lmo->l_ld_offset,
>+			  &l_ld, ptr_size) == 0
>+	 && read_one_ptr (lm_addr + lmo->l_prev_offset,
>+			  &l_prev, ptr_size) == 0
>+	 && read_one_ptr (lm_addr + lmo->l_next_offset,
>+			  &l_next, ptr_size) == 0)
>+    {
>+      unsigned char libname[PATH_MAX];
>+
>+      if (lm_prev != l_prev)
>+	{
>+	  warning ("Corrupted shared library list: 0x%lx != 0x%lx",
>+		   (long) lm_prev, (long) l_prev);
>+	  break;
>+	}
>+
>+      /* Ignore the first entry even if it has valid name as the first entry
>+	 corresponds to the main executable.  The first entry should not be
>+	 skipped if the dynamic loader was loaded late by a static executable
>+	 (see solib-svr4.c parameter ignore_first).  But in such case the main
>+	 executable does not have PT_DYNAMIC present and this function already
>+	 exited above due to failed get_r_debug.  */
>+      if (ignore_first && lm_prev == 0)
>+	string_appendf (document, " main-lm=\"0x%lx\"", (unsigned long)
>lm_addr);
>+      else
>+	{
>+	  /* Not checking for error because reading may stop before
>+	     we've got PATH_MAX worth of characters.  */
>+	  libname[0] = '\0';
>+	  linux_read_memory (l_name, libname, sizeof (libname) - 1);
>+	  libname[sizeof (libname) - 1] = '\0';
>+	  if (libname[0] != '\0')
>+	    {
>+	      if (!header_done)
>+		{
>+		  /* Terminate `<library-list-svr4'.  */
>+		  document += '>';
>+		  header_done = 1;
>+		}
>+
>+	      string_appendf (document, "<library name=\"");
>+	      xml_escape_text_append (&document, (char *) libname);
>+	      string_appendf (document, "\" lm=\"0x%lx\" "
>+			      "l_addr=\"0x%lx\" l_ld=\"0x%lx\"/>",
>+			      (unsigned long) lm_addr, (unsigned long) l_addr,
>+			      (unsigned long) l_ld);
>+	    }
>+	}
>+
>+      lm_prev = lm_addr;
>+      lm_addr = l_next;
>+    }
>+}
>+
> /* Construct qXfer:libraries-svr4:read reply.  */
>
> int
>@@ -6754,33 +6849,8 @@ linux_process_target::qxfer_libraries_svr4 (const char
>*annex,
>   struct process_info_private *const priv = current_process ()->priv;
>   char filename[PATH_MAX];
>   int pid, is_elf64;
>-
>-  static const struct link_map_offsets lmo_32bit_offsets =
>-    {
>-      0,     /* r_version offset. */
>-      4,     /* r_debug.r_map offset.  */
>-      0,     /* l_addr offset in link_map.  */
>-      4,     /* l_name offset in link_map.  */
>-      8,     /* l_ld offset in link_map.  */
>-      12,    /* l_next offset in link_map.  */
>-      16     /* l_prev offset in link_map.  */
>-    };
>-
>-  static const struct link_map_offsets lmo_64bit_offsets =
>-    {
>-      0,     /* r_version offset. */
>-      8,     /* r_debug.r_map offset.  */
>-      0,     /* l_addr offset in link_map.  */
>-      8,     /* l_name offset in link_map.  */
>-      16,    /* l_ld offset in link_map.  */
>-      24,    /* l_next offset in link_map.  */
>-      32     /* l_prev offset in link_map.  */
>-    };
>-  const struct link_map_offsets *lmo;
>   unsigned int machine;
>-  int ptr_size;
>   CORE_ADDR lm_addr = 0, lm_prev = 0;
>-  CORE_ADDR l_name, l_addr, l_ld, l_next, l_prev;
>   int header_done = 0;
>
>   if (writebuf != NULL)
>@@ -6791,8 +6861,18 @@ linux_process_target::qxfer_libraries_svr4 (const char
>*annex,
>   pid = lwpid_of (current_thread);
>   xsnprintf (filename, sizeof filename, "/proc/%d/exe", pid);
>   is_elf64 = elf_64_file_p (filename, &machine);
>-  lmo = is_elf64 ? &lmo_64bit_offsets : &lmo_32bit_offsets;
>-  ptr_size = is_elf64 ? 8 : 4;
>+  const struct link_map_offsets *lmo;
>+  int ptr_size;
>+  if (is_elf64)
>+    {
>+      lmo = &lmo_64bit_offsets;
>+      ptr_size = 8;
>+    }
>+  else
>+    {
>+      lmo = &lmo_32bit_offsets;
>+      ptr_size = 4;
>+    }
>
>   while (annex[0] != '\0')
>     {
>@@ -6821,95 +6901,69 @@ linux_process_target::qxfer_libraries_svr4 (const char
>*annex,
>       annex = decode_address_to_semicolon (addrp, sep + 1);
>     }
>
>-  if (lm_addr == 0)
>-    {
>-      int r_version = 0;
>+  std::string document = "<library-list-svr4 version=\"1.0\"";
>
>-      if (priv->r_debug == 0)
>-	priv->r_debug = get_r_debug (pid, is_elf64);
>+  /* When the starting LM_ADDR is passed in the annex, only traverse that
>+     namespace.
>+
>+     Otherwise, start with R_DEBUG and traverse all namespaces we find.  */
>+  if (lm_addr != 0)
>+    read_link_map (document, lm_addr, lm_prev, ptr_size, lmo, false,
>+		   header_done);
>+  else
>+    {
>+      CORE_ADDR lm = priv->r_debug;
>+      if (lm == 0)
>+	lm = priv->r_debug = get_r_debug (pid, is_elf64);
>
>       /* We failed to find DT_DEBUG.  Such situation will not change
> 	 for this inferior - do not retry it.  Report it to GDB as
> 	 E01, see for the reasons at the GDB solib-svr4.c side.  */
>-      if (priv->r_debug == (CORE_ADDR) -1)
>+      if (lm == (CORE_ADDR) -1)
> 	return -1;
>
>-      if (priv->r_debug != 0)
>+      bool ignore_first = true;
>+      while (lm != 0)
> 	{
>-	  if (linux_read_memory (priv->r_debug + lmo->r_version_offset,
>+	  int r_version = 0;
>+	  if (linux_read_memory (lm + lmo->r_version_offset,
> 				 (unsigned char *) &r_version,
>-				 sizeof (r_version)) != 0
>-	      || r_version < 1)
>+				 sizeof (r_version)) != 0)
>+	    {
>+	      warning ("unable to read r_version from 0x%lx",
>+		       (long) lm + lmo->r_version_offset);
>+	      break;
>+	    }
>+
>+	  if (r_version < 1)
> 	    {
> 	      warning ("unexpected r_debug version %d", r_version);
>+	      break;
> 	    }
>-	  else if (read_one_ptr (priv->r_debug + lmo->r_map_offset,
>-				 &lm_addr, ptr_size) != 0)
>+
>+	  if (read_one_ptr (lm + lmo->r_map_offset, &lm_addr, ptr_size) != 0)
> 	    {
> 	      warning ("unable to read r_map from 0x%lx",
>-		       (long) priv->r_debug + lmo->r_map_offset);
>+		       (long) lm + lmo->r_map_offset);
>+	      break;
> 	    }
>-	}
>-    }
>
>-  std::string document = "<library-list-svr4 version=\"1.0\"";
>+	  read_link_map (document, lm_addr, lm_prev, ptr_size, lmo,
>+			 ignore_first, header_done);
>
>-  while (lm_addr
>-	 && read_one_ptr (lm_addr + lmo->l_name_offset,
>-			  &l_name, ptr_size) == 0
>-	 && read_one_ptr (lm_addr + lmo->l_addr_offset,
>-			  &l_addr, ptr_size) == 0
>-	 && read_one_ptr (lm_addr + lmo->l_ld_offset,
>-			  &l_ld, ptr_size) == 0
>-	 && read_one_ptr (lm_addr + lmo->l_prev_offset,
>-			  &l_prev, ptr_size) == 0
>-	 && read_one_ptr (lm_addr + lmo->l_next_offset,
>-			  &l_next, ptr_size) == 0)
>-    {
>-      unsigned char libname[PATH_MAX];
>+	  if (r_version < 2)
>+	    break;
>
>-      if (lm_prev != l_prev)
>-	{
>-	  warning ("Corrupted shared library list: 0x%lx != 0x%lx",
>-		   (long) lm_prev, (long) l_prev);
>-	  break;
>-	}
>+	  /* Only applies to the default namespace.  */
>+	  ignore_first = false;
>
>-      /* Ignore the first entry even if it has valid name as the first entry
>-	 corresponds to the main executable.  The first entry should not be
>-	 skipped if the dynamic loader was loaded late by a static executable
>-	 (see solib-svr4.c parameter ignore_first).  But in such case the main
>-	 executable does not have PT_DYNAMIC present and this function already
>-	 exited above due to failed get_r_debug.  */
>-      if (lm_prev == 0)
>-	string_appendf (document, " main-lm=\"0x%lx\"", (unsigned long)
>lm_addr);
>-      else
>-	{
>-	  /* Not checking for error because reading may stop before
>-	     we've got PATH_MAX worth of characters.  */
>-	  libname[0] = '\0';
>-	  linux_read_memory (l_name, libname, sizeof (libname) - 1);
>-	  libname[sizeof (libname) - 1] = '\0';
>-	  if (libname[0] != '\0')
>+	  if (read_one_ptr (lm + lmo->r_next_offset, &lm, ptr_size) != 0)
> 	    {
>-	      if (!header_done)
>-		{
>-		  /* Terminate `<library-list-svr4'.  */
>-		  document += '>';
>-		  header_done = 1;
>-		}
>-
>-	      string_appendf (document, "<library name=\"");
>-	      xml_escape_text_append (&document, (char *) libname);
>-	      string_appendf (document, "\" lm=\"0x%lx\" "
>-			      "l_addr=\"0x%lx\" l_ld=\"0x%lx\"/>",
>-			      (unsigned long) lm_addr, (unsigned long) l_addr,
>-			      (unsigned long) l_ld);
>+	      warning ("unable to read r_next from 0x%lx",
>+		       (long) lm + lmo->r_next_offset);
>+	      break;
> 	    }
> 	}
>-
>-      lm_prev = lm_addr;
>-      lm_addr = l_next;
>     }
>
>   if (!header_done)
>--
>2.31.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de <http://www.intel.de>
Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva  
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v4] gdb, gdbserver: support dlmopen()
@ 2021-11-17 14:28 Markus Metzger
  2022-01-21 11:42 ` Metzger, Markus T
                   ` (3 more replies)
  0 siblings, 4 replies; 13+ messages in thread
From: Markus Metzger @ 2021-11-17 14:28 UTC (permalink / raw)
  To: gdb-patches

This patch was originally developed by H.J.  Last version:
https://sourceware.org/pipermail/gdb-patches/2021-October/182353.html

Changes in v4:

1. add gdb.base/dlmopen.exp
2. extend svr4_same() to compare the load offset in addition to the name
3. supply l_addr_inferior for default DSO to make it work with #2
4. fix bugs in gdbserver/linux-low.cc

Changes in v3:

1. Fix gdbserver support.

Changes in v2:

1. Don't check shared libraries in other namespaces when updating shared
libraries in a new namespace.

Regression-tested on x86-64 linux.

---

In glibc, the r_debug structure contains (amongst others) the following
fields:

  int r_version:
    Version number for this protocol.  It should be greater than 0.

If r_version is 2, struct r_debug is extended to struct r_debug_extended
with one additional field:

  struct r_debug_extended *r_next;
    Link to the next r_debug_extended structure.  Each r_debug_extended
    structure represents a different namespace.  The first r_debug_extended
    structure is for the default namespace.

1. Change solib_svr4_r_map argument to take the debug base.
2. Add solib_svr4_r_next to find the link map in the next namespace from
the r_next field.
3. Update svr4_current_sos_direct to get the link map in the next namespace
from the r_next field.
4. Don't check shared libraries in other namespaces when updating shared
libraries in a new namespace.
5. Update svr4_same to check the load offset in addition to the name
6. Update svr4_default_sos to also set l_addr_inferior

Add gdb.base/dlmopen.exp to test this.

This fixes PR 11839.
---
 gdb/linux-tdep.c                     |   2 +
 gdb/mips-fbsd-tdep.c                 |   2 +
 gdb/mips-netbsd-tdep.c               |   2 +
 gdb/solib-svr4.c                     |  89 ++++++++--
 gdb/solib-svr4.h                     |   3 +
 gdb/testsuite/gdb.base/dlmopen-lib.c |  25 +++
 gdb/testsuite/gdb.base/dlmopen.c     |  56 +++++++
 gdb/testsuite/gdb.base/dlmopen.exp   | 141 ++++++++++++++++
 gdbserver/linux-low.cc               | 242 ++++++++++++++++-----------
 9 files changed, 450 insertions(+), 112 deletions(-)
 create mode 100644 gdb/testsuite/gdb.base/dlmopen-lib.c
 create mode 100644 gdb/testsuite/gdb.base/dlmopen.c
 create mode 100644 gdb/testsuite/gdb.base/dlmopen.exp

diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c
index e2cff83086a..cb8e52d760a 100644
--- a/gdb/linux-tdep.c
+++ b/gdb/linux-tdep.c
@@ -2739,6 +2739,7 @@ linux_ilp32_fetch_link_map_offsets ()
       lmo.r_map_offset = 4;
       lmo.r_brk_offset = 8;
       lmo.r_ldsomap_offset = -1;
+      lmo.r_next_offset = 20;
 
       /* Everything we need is in the first 20 bytes.  */
       lmo.link_map_size = 20;
@@ -2767,6 +2768,7 @@ linux_lp64_fetch_link_map_offsets ()
       lmo.r_map_offset = 8;
       lmo.r_brk_offset = 16;
       lmo.r_ldsomap_offset = -1;
+      lmo.r_next_offset = 40;
 
       /* Everything we need is in the first 40 bytes.  */
       lmo.link_map_size = 40;
diff --git a/gdb/mips-fbsd-tdep.c b/gdb/mips-fbsd-tdep.c
index 0b7c97c445f..00e38a8f1c4 100644
--- a/gdb/mips-fbsd-tdep.c
+++ b/gdb/mips-fbsd-tdep.c
@@ -495,6 +495,7 @@ mips_fbsd_ilp32_fetch_link_map_offsets (void)
       lmo.r_map_offset = 4;
       lmo.r_brk_offset = 8;
       lmo.r_ldsomap_offset = -1;
+      lmo.r_next_offset = -1;
 
       lmo.link_map_size = 24;
       lmo.l_addr_offset = 0;
@@ -522,6 +523,7 @@ mips_fbsd_lp64_fetch_link_map_offsets (void)
       lmo.r_map_offset = 8;
       lmo.r_brk_offset = 16;
       lmo.r_ldsomap_offset = -1;
+      lmo.r_next_offset = -1;
 
       lmo.link_map_size = 48;
       lmo.l_addr_offset = 0;
diff --git a/gdb/mips-netbsd-tdep.c b/gdb/mips-netbsd-tdep.c
index a32ae5e3a29..12d702ef359 100644
--- a/gdb/mips-netbsd-tdep.c
+++ b/gdb/mips-netbsd-tdep.c
@@ -308,6 +308,7 @@ mipsnbsd_ilp32_fetch_link_map_offsets (void)
       lmo.r_map_offset = 4;
       lmo.r_brk_offset = 8;
       lmo.r_ldsomap_offset = -1;
+      lmo.r_next_offset = -1;
 
       /* Everything we need is in the first 24 bytes.  */
       lmo.link_map_size = 24;
@@ -336,6 +337,7 @@ mipsnbsd_lp64_fetch_link_map_offsets (void)
       lmo.r_map_offset = 8;
       lmo.r_brk_offset = 16;
       lmo.r_ldsomap_offset = -1;
+      lmo.r_next_offset = -1;
 
       /* Everything we need is in the first 40 bytes.  */
       lmo.link_map_size = 48;
diff --git a/gdb/solib-svr4.c b/gdb/solib-svr4.c
index 3de1bb9c7f7..d57a3c1ffdb 100644
--- a/gdb/solib-svr4.c
+++ b/gdb/solib-svr4.c
@@ -174,7 +174,16 @@ svr4_same_1 (const char *gdb_so_name, const char *inferior_so_name)
 static int
 svr4_same (struct so_list *gdb, struct so_list *inferior)
 {
-  return (svr4_same_1 (gdb->so_original_name, inferior->so_original_name));
+  if (!svr4_same_1 (gdb->so_original_name, inferior->so_original_name))
+    return false;
+
+  /* There may be different instances of the same library, in different
+     namespaces.  Each instance, however, must have been loaded at a
+     different address so its relocation offset would be different.  */
+  const lm_info_svr4 *lmg = (const lm_info_svr4 *) gdb->lm_info;
+  const lm_info_svr4 *lmi = (const lm_info_svr4 *) inferior->lm_info;
+
+  return (lmg->l_addr_inferior == lmi->l_addr_inferior);
 }
 
 static std::unique_ptr<lm_info_svr4>
@@ -767,7 +776,7 @@ locate_base (struct svr4_info *info)
    RT_CONSISTENT.  */
 
 static CORE_ADDR
-solib_svr4_r_map (struct svr4_info *info)
+solib_svr4_r_map (CORE_ADDR debug_base)
 {
   struct link_map_offsets *lmo = svr4_fetch_link_map_offsets ();
   struct type *ptr_type = builtin_type (target_gdbarch ())->builtin_data_ptr;
@@ -775,7 +784,7 @@ solib_svr4_r_map (struct svr4_info *info)
 
   try
     {
-      addr = read_memory_typed_address (info->debug_base + lmo->r_map_offset,
+      addr = read_memory_typed_address (debug_base + lmo->r_map_offset,
 					ptr_type);
     }
   catch (const gdb_exception_error &ex)
@@ -829,6 +838,35 @@ solib_svr4_r_ldsomap (struct svr4_info *info)
 				    ptr_type);
 }
 
+/* Find the next namespace from the r_next field.  */
+
+static CORE_ADDR
+solib_svr4_r_next (CORE_ADDR debug_base)
+{
+  struct link_map_offsets *lmo = svr4_fetch_link_map_offsets ();
+  struct type *ptr_type = builtin_type (target_gdbarch ())->builtin_data_ptr;
+  enum bfd_endian byte_order = type_byte_order (ptr_type);
+  ULONGEST version = 0;
+
+  try
+    {
+      version
+	= read_memory_unsigned_integer (debug_base + lmo->r_version_offset,
+					lmo->r_version_size, byte_order);
+    }
+  catch (const gdb_exception_error &ex)
+    {
+      exception_print (gdb_stderr, ex);
+    }
+
+  /* The r_next field is added with r_version == 2.  */
+  if (version < 2 || lmo->r_next_offset == -1)
+    return 0;
+
+  return read_memory_typed_address (debug_base + lmo->r_next_offset,
+				    ptr_type);
+}
+
 /* On Solaris systems with some versions of the dynamic linker,
    ld.so's l_name pointer points to the SONAME in the string table
    rather than into writable memory.  So that GDB can find shared
@@ -886,7 +924,7 @@ open_symbol_file_object (int from_tty)
     return 0;	/* failed somehow...  */
 
   /* First link map member should be the executable.  */
-  lm = solib_svr4_r_map (info);
+  lm = solib_svr4_r_map (info->debug_base);
   if (lm == 0)
     return 0;	/* failed somehow...  */
 
@@ -1178,7 +1216,7 @@ svr4_default_sos (svr4_info *info)
   newobj->lm_info = li;
 
   /* Nothing will ever check the other fields if we set l_addr_p.  */
-  li->l_addr = info->debug_loader_offset;
+  li->l_addr = li->l_addr_inferior = info->debug_loader_offset;
   li->l_addr_p = 1;
 
   strncpy (newobj->so_name, info->debug_loader_name, SO_NAME_MAX_PATH_SIZE - 1);
@@ -1323,7 +1361,7 @@ svr4_current_sos_direct (struct svr4_info *info)
 
   /* Walk the inferior's link map list, and build our list of
      `struct so_list' nodes.  */
-  lm = solib_svr4_r_map (info);
+  lm = solib_svr4_r_map (info->debug_base);
   if (lm)
     svr4_read_so_list (info, lm, 0, &link_ptr, ignore_first);
 
@@ -1335,6 +1373,18 @@ svr4_current_sos_direct (struct svr4_info *info)
   if (lm)
     svr4_read_so_list (info, lm, 0, &link_ptr, 0);
 
+  /* Get the next namespace from the r_next field.  */
+  lm = solib_svr4_r_next (info->debug_base);
+  while (lm != 0)
+    {
+      /* Get the link map in this namespace.  */
+      CORE_ADDR link_map = solib_svr4_r_map (lm);
+      if (link_map != 0)
+	svr4_read_so_list (info, link_map, 0, &link_ptr, 0);
+      /* Go to the next namespace.  */
+      lm = solib_svr4_r_next (lm);
+    }
+
   cleanup.release ();
 
   if (head == NULL)
@@ -1706,7 +1756,8 @@ solist_update_full (struct svr4_info *info)
    failure.  */
 
 static int
-solist_update_incremental (struct svr4_info *info, CORE_ADDR lm)
+solist_update_incremental (struct svr4_info *info, CORE_ADDR debug_base,
+			   CORE_ADDR lm)
 {
   struct so_list *tail;
   CORE_ADDR prev_lm;
@@ -1727,8 +1778,15 @@ solist_update_incremental (struct svr4_info *info, CORE_ADDR lm)
   for (tail = info->solib_list; tail->next != NULL; tail = tail->next)
     /* Nothing.  */;
 
-  lm_info_svr4 *li = (lm_info_svr4 *) tail->lm_info;
-  prev_lm = li->lm_addr;
+  /* Don't check shared libraries in other namespaces when updating
+     shared libraries in a new namespace.  */
+  if (debug_base == info->debug_base)
+    {
+      lm_info_svr4 *li = (lm_info_svr4 *) tail->lm_info;
+      prev_lm = li->lm_addr;
+    }
+  else
+    prev_lm = 0;
 
   /* Read the new objects.  */
   if (info->using_xfer)
@@ -1869,13 +1927,6 @@ svr4_handle_solib_event (void)
 	  return;
       }
 
-    /* GDB does not currently support libraries loaded via dlmopen
-       into namespaces other than the initial one.  We must ignore
-       any namespace other than the initial namespace here until
-       support for this is added to GDB.  */
-    if (debug_base != info->debug_base)
-      action = DO_NOTHING;
-
     if (action == UPDATE_OR_RELOAD)
       {
 	try
@@ -1901,7 +1952,7 @@ svr4_handle_solib_event (void)
 
   if (action == UPDATE_OR_RELOAD)
     {
-      if (!solist_update_incremental (info, lm))
+      if (!solist_update_incremental (info, debug_base, lm))
 	action = FULL_RELOAD;
     }
 
@@ -2136,7 +2187,7 @@ enable_break (struct svr4_info *info, int from_tty)
 
   solib_add (NULL, from_tty, auto_solib_add);
   sym_addr = 0;
-  if (info->debug_base && solib_svr4_r_map (info) != 0)
+  if (info->debug_base && solib_svr4_r_map (info->debug_base) != 0)
     sym_addr = solib_svr4_r_brk (info);
 
   if (sym_addr != 0)
@@ -3087,6 +3138,7 @@ svr4_ilp32_fetch_link_map_offsets (void)
       lmo.r_map_offset = 4;
       lmo.r_brk_offset = 8;
       lmo.r_ldsomap_offset = 20;
+      lmo.r_next_offset = -1;
 
       /* Everything we need is in the first 20 bytes.  */
       lmo.link_map_size = 20;
@@ -3118,6 +3170,7 @@ svr4_lp64_fetch_link_map_offsets (void)
       lmo.r_map_offset = 8;
       lmo.r_brk_offset = 16;
       lmo.r_ldsomap_offset = 40;
+      lmo.r_next_offset = -1;
 
       /* Everything we need is in the first 40 bytes.  */
       lmo.link_map_size = 40;
diff --git a/gdb/solib-svr4.h b/gdb/solib-svr4.h
index 8d94d9cb26e..64854e2edd9 100644
--- a/gdb/solib-svr4.h
+++ b/gdb/solib-svr4.h
@@ -66,6 +66,9 @@ struct link_map_offsets
     /* Offset of r_debug.r_ldsomap.  */
     int r_ldsomap_offset;
 
+    /* Offset of r_debug_extended.r_next.  */
+    int r_next_offset;
+
     /* Size of struct link_map (or equivalent), or at least enough of it
        to be able to obtain the fields below.  */
     int link_map_size;
diff --git a/gdb/testsuite/gdb.base/dlmopen-lib.c b/gdb/testsuite/gdb.base/dlmopen-lib.c
new file mode 100644
index 00000000000..264659ec9de
--- /dev/null
+++ b/gdb/testsuite/gdb.base/dlmopen-lib.c
@@ -0,0 +1,25 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2021 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+__attribute__((visibility ("default")))
+int
+inc (int n)
+{
+  return n + 1;  /* bp.inc.  */
+}
diff --git a/gdb/testsuite/gdb.base/dlmopen.c b/gdb/testsuite/gdb.base/dlmopen.c
new file mode 100644
index 00000000000..49040dc8738
--- /dev/null
+++ b/gdb/testsuite/gdb.base/dlmopen.c
@@ -0,0 +1,56 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2021 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#define _GNU_SOURCE
+#include <dlfcn.h>
+#include <stddef.h>
+#include <assert.h>
+
+volatile int pause = 1;
+
+int
+main (void)
+{
+  void *handle[2];
+  int (*fun) (int);
+
+  handle[0] = dlmopen (LM_ID_NEWLM, DSO_NAME, RTLD_LAZY | RTLD_LOCAL);
+  assert (handle[0] != NULL);
+
+  handle[1] = dlmopen (LM_ID_NEWLM, DSO_NAME, RTLD_LAZY | RTLD_LOCAL);
+  assert (handle[1] != NULL);
+
+  fun = dlsym (handle[0], "inc");
+  assert (fun != NULL);
+
+  while (pause != 0)
+    ;
+
+  fun (42);
+
+  dlclose (handle[0]);
+
+  fun = dlsym (handle[1], "inc");
+  assert (fun != NULL);
+
+  fun (42);
+
+  dlclose (handle[1]);
+  return 0;  /* bp.main  */
+}
diff --git a/gdb/testsuite/gdb.base/dlmopen.exp b/gdb/testsuite/gdb.base/dlmopen.exp
new file mode 100644
index 00000000000..fbf6fcb6b42
--- /dev/null
+++ b/gdb/testsuite/gdb.base/dlmopen.exp
@@ -0,0 +1,141 @@
+# This testcase is part of GDB, the GNU debugger.
+#
+# Copyright 2021 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+if { [skip_shlib_tests] } {
+    unsupported "target does not support dynamic libraries"
+    return -1
+}
+
+standard_testfile
+
+set basename_lib dlmopen-lib
+set srcfile_lib $srcdir/$subdir/$basename_lib.c
+set binfile_lib [standard_output_file $basename_lib.so]
+
+if { [gdb_compile_shlib $srcfile_lib $binfile_lib {debug}] != "" } {
+    untested "failed to prepare shlib"
+    return -1
+}
+
+if { [prepare_for_testing "failed to prepare" $testfile $srcfile \
+	  [list additional_flags=-DDSO_NAME=\"$binfile_lib\" \
+	       libs=-ldl debug]] } {
+    return -1
+}
+
+if { ![runto_main] } {
+    return -1
+}
+
+# Check that 'info shared' show NUM occurrences of DSO.
+proc check_dso_count { dso num } {
+    global gdb_prompt
+
+    set count 0
+    gdb_test_multiple "info shared" "info shared" {
+	-re "$dso" {
+	    set count [expr $count + 1]
+	    exp_continue
+	}
+	-re "$gdb_prompt " {
+	    gdb_assert {$count == $num} $gdb_test_name
+	}
+    }
+}
+
+# The actual test.  We run it twice.
+proc test_dlmopen {} {
+    global srcfile srcfile_lib binfile_lib bp_main bp_inc
+
+    with_test_prefix "dso 1" {
+	# Try to reach the breakpoint in the dynamically loaded library.
+	gdb_continue_to_breakpoint "cont to bp.inc" \
+	    ".*$srcfile_lib:$bp_inc\r\n.*"
+
+	# The library should be listed twice.
+	check_dso_count $binfile_lib 2
+
+	# This might help debugging.
+	gdb_test "info breakpoints" ".*"
+	gdb_test "print \$pc" ".*"
+    }
+
+    with_test_prefix "dso 2" {
+	# Try to reach the breakpoint in the dynamically loaded library.
+	gdb_continue_to_breakpoint "cont to bp.inc" \
+	    ".*$srcfile_lib:$bp_inc\r\n.*"
+
+	# The library should be listed once.
+	check_dso_count $binfile_lib 1
+
+	# This might help debugging.
+	gdb_test "info breakpoints" ".*"
+	gdb_test "print \$pc" ".*"
+    }
+
+    with_test_prefix "main" {
+	# Try to reach the breakpoint in the dynamically loaded library.
+	gdb_continue_to_breakpoint "cont to bp.main" \
+	    ".*$srcfile:$bp_main\r\n.*"
+
+	# The library should not be listed.
+	check_dso_count $binfile_lib 0
+    }
+}
+
+# Remove the pause.  We only need it for the attach test.
+gdb_test "print pause = 0" "\\\$1 = 0"
+
+# The library is not yet loaded.  We need breakpoints to be pending.
+gdb_test_no_output "set breakpoint pending on"
+
+# Break in the to-be-loaded library and at the end of main.
+set bp_inc [gdb_get_line_number "bp.inc" $srcfile_lib]
+set bp_main [gdb_get_line_number "bp.main" $srcfile]
+
+delete_breakpoints
+gdb_breakpoint $srcfile_lib:$bp_inc allow-pending
+gdb_breakpoint $srcfile:$bp_main
+
+test_dlmopen
+
+# Try the same again when attaching after dlmopen().
+if { ![can_spawn_for_attach] } {
+    unsupported "target does not support attach"
+    return -1
+}
+
+clean_restart $binfile
+
+# Start the test program.
+set test_spawn_id [spawn_wait_for_attach $binfile]
+set testpid [spawn_id_get_pid $test_spawn_id]
+
+# Attach.
+gdb_test "attach $testpid" "Attaching to program.*, process $testpid.*"
+
+with_test_prefix "attach" {
+    # Remove the pause.  We no longer need it.
+    gdb_test "print pause = 0" "\\\$1 = 0"
+
+    # Set the same breakpoints again.  This time, however, we do not allow the
+    # breakpoint to be pending since the library has already been loaded.
+    gdb_breakpoint $srcfile_lib:$bp_inc
+    gdb_breakpoint $srcfile:$bp_main
+
+    test_dlmopen
+}
diff --git a/gdbserver/linux-low.cc b/gdbserver/linux-low.cc
index 34ede238d19..4ad0f94526d 100644
--- a/gdbserver/linux-low.cc
+++ b/gdbserver/linux-low.cc
@@ -6727,6 +6727,9 @@ struct link_map_offsets
     /* Offset and size of r_debug.r_map.  */
     int r_map_offset;
 
+    /* Offset of r_debug_extended.r_next.  */
+    int r_next_offset;
+
     /* Offset to l_addr field in struct link_map.  */
     int l_addr_offset;
 
@@ -6743,6 +6746,98 @@ struct link_map_offsets
     int l_prev_offset;
   };
 
+static const struct link_map_offsets lmo_32bit_offsets =
+  {
+    0,     /* r_version offset. */
+    4,     /* r_debug.r_map offset.  */
+    20,    /* r_debug_extended.r_next.  */
+    0,     /* l_addr offset in link_map.  */
+    4,     /* l_name offset in link_map.  */
+    8,     /* l_ld offset in link_map.  */
+    12,    /* l_next offset in link_map.  */
+    16     /* l_prev offset in link_map.  */
+  };
+
+static const struct link_map_offsets lmo_64bit_offsets =
+  {
+    0,     /* r_version offset. */
+    8,     /* r_debug.r_map offset.  */
+    40,    /* r_debug_extended.r_next.  */
+    0,     /* l_addr offset in link_map.  */
+    8,     /* l_name offset in link_map.  */
+    16,    /* l_ld offset in link_map.  */
+    24,    /* l_next offset in link_map.  */
+    32     /* l_prev offset in link_map.  */
+  };
+
+/* Get the loaded shared libraries from one namespace.  */
+
+static void
+read_link_map (std::string &document, CORE_ADDR lm_addr, CORE_ADDR lm_prev,
+	       int ptr_size, const struct link_map_offsets *lmo,
+	       bool ignore_first, int &header_done)
+{
+  CORE_ADDR l_name, l_addr, l_ld, l_next, l_prev;
+
+  while (lm_addr
+	 && read_one_ptr (lm_addr + lmo->l_name_offset,
+			  &l_name, ptr_size) == 0
+	 && read_one_ptr (lm_addr + lmo->l_addr_offset,
+			  &l_addr, ptr_size) == 0
+	 && read_one_ptr (lm_addr + lmo->l_ld_offset,
+			  &l_ld, ptr_size) == 0
+	 && read_one_ptr (lm_addr + lmo->l_prev_offset,
+			  &l_prev, ptr_size) == 0
+	 && read_one_ptr (lm_addr + lmo->l_next_offset,
+			  &l_next, ptr_size) == 0)
+    {
+      unsigned char libname[PATH_MAX];
+
+      if (lm_prev != l_prev)
+	{
+	  warning ("Corrupted shared library list: 0x%lx != 0x%lx",
+		   (long) lm_prev, (long) l_prev);
+	  break;
+	}
+
+      /* Ignore the first entry even if it has valid name as the first entry
+	 corresponds to the main executable.  The first entry should not be
+	 skipped if the dynamic loader was loaded late by a static executable
+	 (see solib-svr4.c parameter ignore_first).  But in such case the main
+	 executable does not have PT_DYNAMIC present and this function already
+	 exited above due to failed get_r_debug.  */
+      if (ignore_first && lm_prev == 0)
+	string_appendf (document, " main-lm=\"0x%lx\"", (unsigned long) lm_addr);
+      else
+	{
+	  /* Not checking for error because reading may stop before
+	     we've got PATH_MAX worth of characters.  */
+	  libname[0] = '\0';
+	  linux_read_memory (l_name, libname, sizeof (libname) - 1);
+	  libname[sizeof (libname) - 1] = '\0';
+	  if (libname[0] != '\0')
+	    {
+	      if (!header_done)
+		{
+		  /* Terminate `<library-list-svr4'.  */
+		  document += '>';
+		  header_done = 1;
+		}
+
+	      string_appendf (document, "<library name=\"");
+	      xml_escape_text_append (&document, (char *) libname);
+	      string_appendf (document, "\" lm=\"0x%lx\" "
+			      "l_addr=\"0x%lx\" l_ld=\"0x%lx\"/>",
+			      (unsigned long) lm_addr, (unsigned long) l_addr,
+			      (unsigned long) l_ld);
+	    }
+	}
+
+      lm_prev = lm_addr;
+      lm_addr = l_next;
+    }
+}
+
 /* Construct qXfer:libraries-svr4:read reply.  */
 
 int
@@ -6754,33 +6849,8 @@ linux_process_target::qxfer_libraries_svr4 (const char *annex,
   struct process_info_private *const priv = current_process ()->priv;
   char filename[PATH_MAX];
   int pid, is_elf64;
-
-  static const struct link_map_offsets lmo_32bit_offsets =
-    {
-      0,     /* r_version offset. */
-      4,     /* r_debug.r_map offset.  */
-      0,     /* l_addr offset in link_map.  */
-      4,     /* l_name offset in link_map.  */
-      8,     /* l_ld offset in link_map.  */
-      12,    /* l_next offset in link_map.  */
-      16     /* l_prev offset in link_map.  */
-    };
-
-  static const struct link_map_offsets lmo_64bit_offsets =
-    {
-      0,     /* r_version offset. */
-      8,     /* r_debug.r_map offset.  */
-      0,     /* l_addr offset in link_map.  */
-      8,     /* l_name offset in link_map.  */
-      16,    /* l_ld offset in link_map.  */
-      24,    /* l_next offset in link_map.  */
-      32     /* l_prev offset in link_map.  */
-    };
-  const struct link_map_offsets *lmo;
   unsigned int machine;
-  int ptr_size;
   CORE_ADDR lm_addr = 0, lm_prev = 0;
-  CORE_ADDR l_name, l_addr, l_ld, l_next, l_prev;
   int header_done = 0;
 
   if (writebuf != NULL)
@@ -6791,8 +6861,18 @@ linux_process_target::qxfer_libraries_svr4 (const char *annex,
   pid = lwpid_of (current_thread);
   xsnprintf (filename, sizeof filename, "/proc/%d/exe", pid);
   is_elf64 = elf_64_file_p (filename, &machine);
-  lmo = is_elf64 ? &lmo_64bit_offsets : &lmo_32bit_offsets;
-  ptr_size = is_elf64 ? 8 : 4;
+  const struct link_map_offsets *lmo;
+  int ptr_size;
+  if (is_elf64)
+    {
+      lmo = &lmo_64bit_offsets;
+      ptr_size = 8;
+    }
+  else
+    {
+      lmo = &lmo_32bit_offsets;
+      ptr_size = 4;
+    }
 
   while (annex[0] != '\0')
     {
@@ -6821,95 +6901,69 @@ linux_process_target::qxfer_libraries_svr4 (const char *annex,
       annex = decode_address_to_semicolon (addrp, sep + 1);
     }
 
-  if (lm_addr == 0)
-    {
-      int r_version = 0;
+  std::string document = "<library-list-svr4 version=\"1.0\"";
 
-      if (priv->r_debug == 0)
-	priv->r_debug = get_r_debug (pid, is_elf64);
+  /* When the starting LM_ADDR is passed in the annex, only traverse that
+     namespace.
+
+     Otherwise, start with R_DEBUG and traverse all namespaces we find.  */
+  if (lm_addr != 0)
+    read_link_map (document, lm_addr, lm_prev, ptr_size, lmo, false,
+		   header_done);
+  else
+    {
+      CORE_ADDR lm = priv->r_debug;
+      if (lm == 0)
+	lm = priv->r_debug = get_r_debug (pid, is_elf64);
 
       /* We failed to find DT_DEBUG.  Such situation will not change
 	 for this inferior - do not retry it.  Report it to GDB as
 	 E01, see for the reasons at the GDB solib-svr4.c side.  */
-      if (priv->r_debug == (CORE_ADDR) -1)
+      if (lm == (CORE_ADDR) -1)
 	return -1;
 
-      if (priv->r_debug != 0)
+      bool ignore_first = true;
+      while (lm != 0)
 	{
-	  if (linux_read_memory (priv->r_debug + lmo->r_version_offset,
+	  int r_version = 0;
+	  if (linux_read_memory (lm + lmo->r_version_offset,
 				 (unsigned char *) &r_version,
-				 sizeof (r_version)) != 0
-	      || r_version < 1)
+				 sizeof (r_version)) != 0)
+	    {
+	      warning ("unable to read r_version from 0x%lx",
+		       (long) lm + lmo->r_version_offset);
+	      break;
+	    }
+
+	  if (r_version < 1)
 	    {
 	      warning ("unexpected r_debug version %d", r_version);
+	      break;
 	    }
-	  else if (read_one_ptr (priv->r_debug + lmo->r_map_offset,
-				 &lm_addr, ptr_size) != 0)
+
+	  if (read_one_ptr (lm + lmo->r_map_offset, &lm_addr, ptr_size) != 0)
 	    {
 	      warning ("unable to read r_map from 0x%lx",
-		       (long) priv->r_debug + lmo->r_map_offset);
+		       (long) lm + lmo->r_map_offset);
+	      break;
 	    }
-	}
-    }
 
-  std::string document = "<library-list-svr4 version=\"1.0\"";
+	  read_link_map (document, lm_addr, lm_prev, ptr_size, lmo,
+			 ignore_first, header_done);
 
-  while (lm_addr
-	 && read_one_ptr (lm_addr + lmo->l_name_offset,
-			  &l_name, ptr_size) == 0
-	 && read_one_ptr (lm_addr + lmo->l_addr_offset,
-			  &l_addr, ptr_size) == 0
-	 && read_one_ptr (lm_addr + lmo->l_ld_offset,
-			  &l_ld, ptr_size) == 0
-	 && read_one_ptr (lm_addr + lmo->l_prev_offset,
-			  &l_prev, ptr_size) == 0
-	 && read_one_ptr (lm_addr + lmo->l_next_offset,
-			  &l_next, ptr_size) == 0)
-    {
-      unsigned char libname[PATH_MAX];
+	  if (r_version < 2)
+	    break;
 
-      if (lm_prev != l_prev)
-	{
-	  warning ("Corrupted shared library list: 0x%lx != 0x%lx",
-		   (long) lm_prev, (long) l_prev);
-	  break;
-	}
+	  /* Only applies to the default namespace.  */
+	  ignore_first = false;
 
-      /* Ignore the first entry even if it has valid name as the first entry
-	 corresponds to the main executable.  The first entry should not be
-	 skipped if the dynamic loader was loaded late by a static executable
-	 (see solib-svr4.c parameter ignore_first).  But in such case the main
-	 executable does not have PT_DYNAMIC present and this function already
-	 exited above due to failed get_r_debug.  */
-      if (lm_prev == 0)
-	string_appendf (document, " main-lm=\"0x%lx\"", (unsigned long) lm_addr);
-      else
-	{
-	  /* Not checking for error because reading may stop before
-	     we've got PATH_MAX worth of characters.  */
-	  libname[0] = '\0';
-	  linux_read_memory (l_name, libname, sizeof (libname) - 1);
-	  libname[sizeof (libname) - 1] = '\0';
-	  if (libname[0] != '\0')
+	  if (read_one_ptr (lm + lmo->r_next_offset, &lm, ptr_size) != 0)
 	    {
-	      if (!header_done)
-		{
-		  /* Terminate `<library-list-svr4'.  */
-		  document += '>';
-		  header_done = 1;
-		}
-
-	      string_appendf (document, "<library name=\"");
-	      xml_escape_text_append (&document, (char *) libname);
-	      string_appendf (document, "\" lm=\"0x%lx\" "
-			      "l_addr=\"0x%lx\" l_ld=\"0x%lx\"/>",
-			      (unsigned long) lm_addr, (unsigned long) l_addr,
-			      (unsigned long) l_ld);
+	      warning ("unable to read r_next from 0x%lx",
+		       (long) lm + lmo->r_next_offset);
+	      break;
 	    }
 	}
-
-      lm_prev = lm_addr;
-      lm_addr = l_next;
     }
 
   if (!header_done)
-- 
2.31.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de <http://www.intel.de>
Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva  
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928


^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2022-05-31 18:44 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-05-24 18:40 [PATCH v4] gdb, gdbserver: support dlmopen() Ben Woodard
  -- strict thread matches above, loose matches on Subject: below --
2021-11-17 14:28 Markus Metzger
2022-01-21 11:42 ` Metzger, Markus T
2022-03-01 19:10 ` Tom Tromey
2022-03-09 12:23   ` Metzger, Markus T
2022-03-03 18:32 ` Pedro Alves
2022-03-09 12:24   ` Metzger, Markus T
2022-03-09 14:15     ` Pedro Alves
2022-03-29 16:13       ` Metzger, Markus T
2022-04-08 12:40         ` Metzger, Markus T
2022-05-25 17:12 ` Kevin Buettner
2022-05-31  9:29   ` Metzger, Markus T
2022-05-31 18:44     ` Ben Woodard

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).