]> git.ipfire.org Git - thirdparty/glibc.git/commitdiff
elf: Introduce separate _r_debug_array variable
authorFlorian Weimer <fweimer@redhat.com>
Fri, 4 Jul 2025 19:46:05 +0000 (21:46 +0200)
committerFlorian Weimer <fweimer@redhat.com>
Sat, 5 Jul 2025 18:15:12 +0000 (20:15 +0200)
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.)

Reviewed-by: H.J. Lu <hjl.tools@gmail.com>
elf/dl-debug.c
sysdeps/generic/ldsodefs.h

index 5ff1460ab7c4a2cbf10ebe0086fd1d18acd63d1a..09624eae9c492914e56c7ec1e51bf4153b08fab4 100644 (file)
@@ -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;
 }
 
index fc4a3de7678fcbefc1ad74ffa41458dfea8640f7..528c80e2e0a3a0ad4ebd7f34983d13efdbbb557f 100644 (file)
@@ -368,8 +368,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;