about summary refs log tree commit diff
path: root/elf/dl-debug.c
diff options
context:
space:
mode:
authorH.J. Lu <hjl.tools@gmail.com>2021-08-17 19:35:48 -0700
committerH.J. Lu <hjl.tools@gmail.com>2021-09-19 13:51:35 -0700
commita93d9e03a31ec14405cb3a09aa95413b67067380 (patch)
tree532f865c7241b3cf363433272e5923c468ea2969 /elf/dl-debug.c
parent885762aa31d75de8b9fea4c0e2e372b582d4c548 (diff)
downloadglibc-a93d9e03a31ec14405cb3a09aa95413b67067380.tar.gz
glibc-a93d9e03a31ec14405cb3a09aa95413b67067380.tar.xz
glibc-a93d9e03a31ec14405cb3a09aa95413b67067380.zip
Extend struct r_debug to support multiple namespaces [BZ #15971]
Glibc does not provide an interface for debugger to access libraries
loaded in multiple namespaces via dlmopen.

The current rtld-debugger interface is described in the file:

elf/rtld-debugger-interface.txt

under the "Standard debugger interface" heading.  This interface only
provides access to the first link-map (LM_ID_BASE).

1. Bump r_version to 2 when multiple namespaces are used.  This triggers
the GDB bug:

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

2. Add struct r_debug_extended to extend struct r_debug into a linked-list,
where each element correlates to an unique namespace.
3. Initialize the r_debug_extended structure.  Bump r_version to 2 for
the new namespace and add the new namespace to the namespace linked list.
4. Add _dl_debug_update to return the address of struct r_debug' of a
namespace.
5. Add a hidden symbol, _r_debug_extended, for struct r_debug_extended.
6. Provide the symbol, _r_debug, with size of struct r_debug, as an alias
of _r_debug_extended, for programs which reference _r_debug.

This fixes BZ #15971.

Reviewed-by: Florian Weimer <fweimer@redhat.com>
Diffstat (limited to 'elf/dl-debug.c')
-rw-r--r--elf/dl-debug.c81
1 files changed, 62 insertions, 19 deletions
diff --git a/elf/dl-debug.c b/elf/dl-debug.c
index 2cd5f09753..f637d4bb8d 100644
--- a/elf/dl-debug.c
+++ b/elf/dl-debug.c
@@ -30,37 +30,80 @@ extern const int verify_link_map_members[(VERIFY_MEMBER (l_addr)
 					  && VERIFY_MEMBER (l_prev))
 					 ? 1 : -1];
 
-/* This structure communicates dl state to the debugger.  The debugger
-   normally finds it via the DT_DEBUG entry in the dynamic section, but in
-   a statically-linked program there is no dynamic section for the debugger
-   to examine and it looks for this particular symbol name.  */
-struct r_debug _r_debug;
+/* Update the `r_map' member and return the address of `struct r_debug'
+   of the namespace NS. */
 
+struct r_debug *
+_dl_debug_update (Lmid_t ns)
+{
+  struct r_debug_extended *r;
+  if (ns == LM_ID_BASE)
+    r = &_r_debug_extended;
+  else
+    r = &GL(dl_ns)[ns]._ns_debug;
+  if (r->base.r_map == NULL)
+    atomic_store_release (&r->base.r_map,
+			  (void *) GL(dl_ns)[ns]._ns_loaded);
+  return &r->base;
+}
 
-/* Initialize _r_debug if it has not already been done.  The argument is
-   the run-time load address of the dynamic linker, to be put in
-   _r_debug.r_ldbase.  Returns the address of _r_debug.  */
+/* Initialize _r_debug_extended for the namespace NS.  LDBASE is the
+   run-time load address of the dynamic linker, to be put in
+   _r_debug_extended.r_ldbase.  Return the address of _r_debug.  */
 
 struct r_debug *
 _dl_debug_initialize (ElfW(Addr) ldbase, Lmid_t ns)
 {
-  struct r_debug *r;
+  struct r_debug_extended *r, **pp = NULL;
 
   if (ns == LM_ID_BASE)
-    r = &_r_debug;
-  else
-    r = &GL(dl_ns)[ns]._ns_debug;
+    {
+      r = &_r_debug_extended;
+      /* Initialize r_version to 1.  */
+      if (_r_debug_extended.base.r_version == 0)
+	_r_debug_extended.base.r_version = 1;
+    }
+  else if (DL_NNS > 1)
+    {
+      r = &GL(dl_ns)[ns]._ns_debug;
+      if (r->base.r_brk == 0)
+	{
+	  /* Add the new namespace to the linked list.  After a namespace
+	     is initialized, r_brk becomes non-zero.  A namespace becomes
+	     empty (r_map == NULL) when it is unused.  But it is never
+	     removed from the linked list.  */
+	  struct r_debug_extended *p;
+	  for (pp = &_r_debug_extended.r_next;
+	       (p = *pp) != NULL;
+	       pp = &p->r_next)
+	    ;
+
+	  r->base.r_version = 2;
+	}
+    }
+
+  if (r->base.r_brk == 0)
+    {
+      /* Tell the debugger where to find the map of loaded objects.
+	 This function is called from dlopen.  Initialize the namespace
+	 only once.  */
+      r->base.r_ldbase = ldbase ?: _r_debug_extended.base.r_ldbase;
+      r->base.r_brk = (ElfW(Addr)) &_dl_debug_state;
+      r->r_next = NULL;
+    }
+
+  if (r->base.r_map == NULL)
+    atomic_store_release (&r->base.r_map,
+			  (void *) GL(dl_ns)[ns]._ns_loaded);
 
-  if (r->r_map == NULL || ldbase != 0)
+  if (pp != NULL)
     {
-      /* Tell the debugger where to find the map of loaded objects.  */
-      r->r_version = 1	/* R_DEBUG_VERSION XXX */;
-      r->r_ldbase = ldbase ?: _r_debug.r_ldbase;
-      r->r_map = (void *) GL(dl_ns)[ns]._ns_loaded;
-      r->r_brk = (ElfW(Addr)) &_dl_debug_state;
+      atomic_store_release (pp, r);
+      /* Bump r_version to 2 for the new namespace.  */
+      atomic_store_release (&_r_debug_extended.base.r_version, 2);
     }
 
-  return r;
+  return &r->base;
 }