[v3,1/3] elf: Introduce separate _r_debug_array variable

Message ID 5a10aa4ba531581cfa201f8839c8fcd70ff3fccc.1667826585.git.fweimer@redhat.com
State New
Headers
Series Restore support for _r_debug copy relocations & interposition |

Checks

Context Check Description
dj/TryBot-apply_patch success Patch applied to master at the time it was sent

Commit Message

Florian Weimer Nov. 7, 2022, 1:11 p.m. UTC
  It replaces the ns_debug member of the namespaces.  Previously,
the base namespace had an unused ns_debug member.

This change also fixes a concurrency issue: Now _dl_debug_initialize
only updates r_next of the previous namespace's r_debug after the new
r_debug is initialized, so that only the initialized version is
observed.  (Client code accessing _r_debug will benefit from load
dependency tracking in CPUs even without explicit barriers.)
---
 elf/dl-debug.c             | 91 +++++++++++++++++++++-----------------
 sysdeps/generic/ldsodefs.h |  2 -
 2 files changed, 50 insertions(+), 43 deletions(-)
  

Patch

diff --git a/elf/dl-debug.c b/elf/dl-debug.c
index 538468dc0b..66f9ad375d 100644
--- a/elf/dl-debug.c
+++ b/elf/dl-debug.c
@@ -30,17 +30,37 @@  extern const int verify_link_map_members[(VERIFY_MEMBER (l_addr)
 					  && VERIFY_MEMBER (l_prev))
 					 ? 1 : -1];
 
+#ifdef SHARED
+/* r_debug structs for secondary namespaces.  The first namespace is
+   handled separately because its r_debug structure must overlap with
+   the public _r_debug symbol, so the first array element corresponds
+   to LM_ID_BASE + 1.  See elf/dl-debug-symbols.S.  */
+struct r_debug_extended _r_debug_array[DL_NNS - 1];
+
+/* Return the r_debug object for the namespace NS.  */
+static inline struct r_debug_extended *
+get_rdebug (Lmid_t ns)
+{
+  if (ns == LM_ID_BASE)
+    return &_r_debug_extended;
+  else
+    return  &_r_debug_array[ns - 1];
+}
+#else /* !SHARED */
+static inline struct r_debug_extended *
+get_rdebug (Lmid_t ns)
+{
+  return &_r_debug_extended; /* There is just one namespace.  */
+}
+#endif  /* !SHARED */
+
 /* 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;
+  struct r_debug_extended *r = get_rdebug (ns);
   if (r->base.r_map == NULL)
     atomic_store_release (&r->base.r_map,
 			  (void *) GL(dl_ns)[ns]._ns_loaded);
@@ -54,34 +74,7 @@  _dl_debug_update (Lmid_t ns)
 struct r_debug *
 _dl_debug_initialize (ElfW(Addr) ldbase, Lmid_t ns)
 {
-  struct r_debug_extended *r, **pp = NULL;
-
-  if (ns == LM_ID_BASE)
-    {
-      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;
-	}
-    }
-
+  struct r_debug_extended *r = get_rdebug (ns);
   if (r->base.r_brk == 0)
     {
       /* Tell the debugger where to find the map of loaded objects.
@@ -89,20 +82,36 @@  _dl_debug_initialize (ElfW(Addr) ldbase, Lmid_t ns)
 	 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;
+
+#ifdef SHARED
+      /* Add the new namespace to the linked list.  This assumes that
+	 namespaces are allocated in increasing order.  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.  */
+
+      if (ns != LM_ID_BASE)
+	{
+	  r->base.r_version = 2;
+	  if (ns - 1 == LM_ID_BASE)
+	    {
+	      atomic_store_release (&_r_debug_extended.r_next, r);
+	      /* Now there are multiple namespaces.  */
+	      atomic_store_release (&_r_debug_extended.base.r_version, 2);
+	    }
+	  else
+	    /* Update r_debug_extended of the previous namespace.  */
+	    atomic_store_release (&_r_debug_array[ns - 2].r_next, r);
+	}
+      else
+#endif /* SHARED */
+	r->base.r_version = 1;
     }
 
   if (r->base.r_map == NULL)
     atomic_store_release (&r->base.r_map,
 			  (void *) GL(dl_ns)[ns]._ns_loaded);
 
-  if (pp != NULL)
-    {
-      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->base;
 }
 
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index 9dae72b1ed..f668a954d3 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -351,8 +351,6 @@  struct rtld_global
       size_t n_elements;
       void (*free) (void *);
     } _ns_unique_sym_table;
-    /* Keep track of changes to each namespace' list.  */
-    struct r_debug_extended _ns_debug;
   } _dl_ns[DL_NNS];
   /* One higher than index of last used namespace.  */
   EXTERN size_t _dl_nns;