@@ -404,6 +404,8 @@ tests += \
tst-dlmopen1 \
tst-dlmopen3 \
tst-dlmopen4 \
+ tst-dlmopen4-nonpic \
+ tst-dlmopen4-pic \
tst-dlopen-self \
tst-dlopen-tlsmodid \
tst-dlopenfail \
@@ -2001,6 +2003,13 @@ $(objpfx)tst-dlmopen3.out: $(objpfx)tst-dlmopen1mod.so
$(objpfx)tst-dlmopen4.out: $(objpfx)tst-dlmopen1mod.so
+CFLAGS-tst-dlmopen4-pic.c += -fPIC
+$(objpfx)tst-dlmopen4-pic.out: $(objpfx)tst-dlmopen1mod.so
+
+CFLAGS-tst-dlmopen4-nonpic.c += -fno-pie
+tst-dlmopen4-nonpic-no-pie = yes
+$(objpfx)tst-dlmopen4-nonpic.out: $(objpfx)tst-dlmopen1mod.so
+
$(objpfx)tst-audit1.out: $(objpfx)tst-auditmod1.so
tst-audit1-ENV = LD_AUDIT=$(objpfx)tst-auditmod1.so
@@ -16,6 +16,7 @@
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
+#include <assert.h>
#include <ldsodefs.h>
@@ -37,6 +38,37 @@ extern const int verify_link_map_members[(VERIFY_MEMBER (l_addr)
to LM_ID_BASE + 1. See elf/dl-debug-symbols.S. */
struct r_debug_extended _r_debug_array[DL_NNS - 1];
+/* If not null, pointer to the _r_debug in the main executable. */
+static struct r_debug *_r_debug_main;
+
+void
+_dl_debug_post_relocate (struct link_map *main_map)
+{
+ /* Perform a full symbol search in all objects, to maintain
+ compatibility if interposed _r_debug definitions. The lookup
+ cannot faul because there is a definition in ld.so. */
+ const ElfW(Sym) *sym = NULL;
+ lookup_t result =_dl_lookup_symbol_x ("_r_debug", main_map, &sym,
+ main_map->l_scope, NULL, 0, 0, NULL);
+ assert (sym != NULL);
+ if (sym->st_size >= sizeof (struct r_debug))
+ {
+ struct r_debug *main_r_debug = DL_SYMBOL_ADDRESS (result, sym);
+ if (main_r_debug != &_r_debug_extended.base)
+ {
+ /* The extended version of the struct is not available in
+ the main executable because a copy relocation has been
+ used. r_map etc. have already been copied as part of the
+ copy relocation processing. */
+ main_r_debug->r_version = 1;
+
+ /* Record that dual updates of the initial link map are
+ required. */
+ _r_debug_main = main_r_debug;
+ }
+ }
+}
+
/* Return the r_debug object for the namespace NS. */
static inline struct r_debug_extended *
get_rdebug (Lmid_t ns)
@@ -71,6 +103,11 @@ void
_dl_debug_change_state (struct r_debug *r, int state)
{
atomic_store_release (&r->r_state, state);
+#ifdef SHARED
+ if (r == &_r_debug_extended.base && _r_debug_main != NULL)
+ /* Update the copy-relocation of _r_debug. */
+ atomic_store_release (&_r_debug_main->r_state, state);
+#endif
_dl_debug_state ();
}
@@ -103,7 +140,9 @@ _dl_debug_initialize (ElfW(Addr) ldbase, Lmid_t ns)
if (ns - 1 == LM_ID_BASE)
{
atomic_store_release (&_r_debug_extended.r_next, r);
- /* Now there are multiple namespaces. */
+ /* Now there are multiple namespaces. Note that this
+ deliberately does not update the copy in the main
+ executable (if it exists). */
atomic_store_release (&_r_debug_extended.base.r_version, 2);
}
else
@@ -116,8 +155,15 @@ _dl_debug_initialize (ElfW(Addr) ldbase, Lmid_t ns)
}
if (r->base.r_map == NULL)
- atomic_store_release (&r->base.r_map,
- (void *) GL(dl_ns)[ns]._ns_loaded);
+ {
+ struct link_map_public *l = (void *) GL(dl_ns)[ns]._ns_loaded;
+ atomic_store_release (&r->base.r_map, l);
+#ifdef SHARED
+ if (ns == LM_ID_BASE && _r_debug_main != NULL)
+ /* Update the copy-relocation of _r_debug. */
+ atomic_store_release (&_r_debug_main->r_map, l);
+#endif
+ }
return &r->base;
}
@@ -2325,6 +2325,9 @@ dl_main (const ElfW(Phdr) *phdr,
/* We must prepare the profiling. */
_dl_start_profile ();
+ /* Update _r_debug if necessary. */
+ _dl_debug_post_relocate (main_map);
+
if ((!was_tls_init_tp_called && GL(dl_tls_max_dtv_idx) > 0)
|| count_modids != _dl_count_modids ())
++GL(dl_tls_generation);
new file mode 100644
@@ -0,0 +1,2 @@
+#define BUILD_FOR_NONPIC
+#include "tst-dlmopen4.c"
new file mode 100644
@@ -0,0 +1,2 @@
+#define BUILD_FOR_PIC
+#include "tst-dlmopen4.c"
@@ -46,6 +46,15 @@ do_test (void)
TEST_COMPARE (debug->base.r_version, 1);
TEST_VERIFY_EXIT (debug->r_next == NULL);
+#ifdef BUILD_FOR_PIC
+ /* In a PIC build, using _r_debug directly should give us the same
+ object. */
+ TEST_VERIFY (&_r_debug == &debug->base);
+#endif
+#ifdef BUILD_FOR_NONPIC
+ TEST_COMPARE (_r_debug.r_version, 1);
+#endif
+
void *h = xdlmopen (LM_ID_NEWLM, "$ORIGIN/tst-dlmopen1mod.so",
RTLD_LAZY);
@@ -57,6 +66,19 @@ do_test (void)
const char *name = basename (debug->r_next->base.r_map->l_name);
TEST_COMPARE_STRING (name, "tst-dlmopen1mod.so");
+#ifdef BUILD_FOR_NONPIC
+ /* If a copy relocation is used, it must be at version 1. */
+ if (&_r_debug != &debug->base)
+ {
+ TEST_COMPARE (_r_debug.r_version, 1);
+ TEST_COMPARE ((uintptr_t) _r_debug.r_map,
+ (uintptr_t) debug->base.r_map);
+ TEST_COMPARE (_r_debug.r_brk, debug->base.r_brk);
+ TEST_COMPARE (_r_debug.r_state, debug->base.r_state);
+ TEST_COMPARE (_r_debug.r_ldbase, debug->base.r_ldbase);
+ }
+#endif
+
xdlclose (h);
return 0;
@@ -1069,6 +1069,10 @@ rtld_hidden_proto (_dl_debug_state)
extern struct r_debug *_dl_debug_initialize (ElfW(Addr) ldbase, Lmid_t ns)
attribute_hidden;
+/* This is called after relocation processing to handle a potential
+ copy relocation for _r_debug. */
+void _dl_debug_post_relocate (struct link_map *main_map) attribute_hidden;
+
/* Update the `r_map' member and return the address of `struct r_debug'
of the namespace NS. */
extern struct r_debug *_dl_debug_update (Lmid_t ns) attribute_hidden;