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
* [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).