[PATCH] Extend struct r_debug to support multiple namespaces
H.J. Lu
hjl.tools@gmail.com
Sun Aug 15 00:33:36 GMT 2021
On Mon, Aug 09, 2021 at 10:16:53AM -0700, Daniel Walker wrote:
> On Mon, Aug 09, 2021 at 07:32:26AM -0700, H.J. Lu wrote:
> > We need a new DT_XXX to support dlmopen. We have 2 choices:
> >
> > 1. Similar to DT_DEBUG. Linker will allocate a new DT_XXX and
> > ld.so will fill it with the address of the new debug data structure for
> > dlmopen.
> > 2. Similar to DT_MIPS_RLD_MAP_REL/DT_MIPS_RLD_MAP.
> > Linker will allocate a space for a pointer, a new DT_XXX and fill
> > the DT_XXX entry with the address of the pointer. ld.so will update
> > the pointer with the address of the new debug data structure for
> > dlmopen.
> >
> > #1 is the most straightforward to implement. #2 is compatible with
> > the current MIPS implementation.
> >
> > Does anyone have any preferences?
>
>
> I have #1 fully implemented already.
>
Here is a slightly different approach with DT_DEBUGSZ. The corresponding
linker patch is at
https://sourceware.org/pipermail/binutils/2021-August/117706.html
H.J.
---
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).
Based on the patch from Conan C Huang <conhuang@cisco.com>:
https://sourceware.org/pipermail/libc-alpha/2020-June/115448.html
1. Extend struct r_debug into a linked-list, where each element correlates
to an unique namespace.
2. Add a new dynamic tag, DT_DEBUGSZ,
#define DT_DEBUGSZ 0x6ffffff8
which records the size of the extended struct r_debug.
3. For executables with a DT_DEBUGSZ dynamic tag, debugger (GDB) can
traverse r_debug linked-list in the extended struct r_debug from the
DT_DEBUG dynamic tag to retrieve information for all loaded namespaces.
4. Provide the compatibility symbol, _r_debug, for programs which
reference _r_debug and update the copy of _r_debug in executable if
copy relocation is used.
---
config.h.in | 4 +++
configure | 43 +++++++++++++++++++++++
configure.ac | 12 +++++++
csu/Makefile | 3 ++
csu/libc-start.c | 2 +-
csu/rtld-sizes.sym | 4 +++
elf/Makefile | 24 +++++++++++--
elf/dl-close.c | 7 +++-
elf/dl-debug-symbols-gen.c | 24 +++++++++++++
elf/dl-debug-symbols.S | 34 +++++++++++++++++++
elf/dl-debug.c | 35 +++++++++++++------
elf/dl-load.c | 7 +++-
elf/dl-open.c | 19 +++++++----
elf/dl-reloc-static-pie.c | 8 ++++-
elf/elf.h | 1 +
elf/link.h | 13 ++++++-
elf/rtld-debugger-interface.txt | 12 +++++++
elf/rtld.c | 10 ++++--
elf/tst-_r_gnu_debug.c | 60 +++++++++++++++++++++++++++++++++
include/link.h | 4 +++
sysdeps/generic/ldsodefs.h | 6 ++--
21 files changed, 304 insertions(+), 28 deletions(-)
create mode 100644 csu/rtld-sizes.sym
create mode 100644 elf/dl-debug-symbols-gen.c
create mode 100644 elf/dl-debug-symbols.S
create mode 100644 elf/tst-_r_gnu_debug.c
diff --git a/config.h.in b/config.h.in
index 0d92504f65..f05cf57f38 100644
--- a/config.h.in
+++ b/config.h.in
@@ -59,6 +59,10 @@
/* Define if the linker supports the -z combreloc option. */
#undef HAVE_Z_COMBRELOC
+/* Define to 1 if the linker supports the -z dt-debugsz option to emit
+ the DT_DEBUGSZ dynamic tag in executables. */
+#undef HAVE_Z_DT_DEBUGSZ
+
/* Define if _rtld_local structure should be forced into .sdata section. */
#undef HAVE_SDATA_SECTION
diff --git a/configure b/configure
index 7272fbf6ea..6d06a1c87b 100755
--- a/configure
+++ b/configure
@@ -6003,6 +6003,49 @@ $as_echo "$libc_linker_feature" >&6; }
config_vars="$config_vars
have-no-dynamic-linker = $libc_cv_no_dynamic_linker"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for linker that supports -z dt-debugsz" >&5
+$as_echo_n "checking for linker that supports -z dt-debugsz... " >&6; }
+libc_linker_feature=no
+if test x"$gnu_ld" = x"yes"; then
+ libc_linker_check=`$LD -v --help 2>/dev/null | grep "\-z dt-debugsz"`
+ if test -n "$libc_linker_check"; then
+ cat > conftest.c <<EOF
+int _start (void) { return 42; }
+EOF
+ if { ac_try='${CC-cc} $CFLAGS $CPPFLAGS $LDFLAGS $no_ssp
+ -Wl,-z,dt-debugsz -nostdlib -nostartfiles
+ -fPIC -shared -o conftest.so conftest.c
+ 1>&5'
+ { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; }
+ then
+ libc_linker_feature=yes
+ fi
+ rm -f conftest*
+ fi
+fi
+if test $libc_linker_feature = yes; then
+ libc_cv_z_dt_debugsz=yes
+else
+ libc_cv_z_dt_debugsz=no
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $libc_linker_feature" >&5
+$as_echo "$libc_linker_feature" >&6; }
+config_vars="$config_vars
+have-z-dt-debugsz = $libc_cv_z_dt_debugsz"
+if test $libc_cv_z_dt_debugsz = yes; then
+ have_z_dt_debugsz=1
+else
+ have_z_dt_debugsz=0
+fi
+cat >>confdefs.h <<_ACEOF
+#define HAVE_Z_DT_DEBUGSZ $have_z_dt_debugsz
+_ACEOF
+
+
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for -static-pie" >&5
$as_echo_n "checking for -static-pie... " >&6; }
if ${libc_cv_static_pie+:} false; then :
diff --git a/configure.ac b/configure.ac
index af47cd51e6..c5ffa381eb 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1343,6 +1343,18 @@ LIBC_LINKER_FEATURE([--no-dynamic-linker],
[libc_cv_no_dynamic_linker=no])
LIBC_CONFIG_VAR([have-no-dynamic-linker], [$libc_cv_no_dynamic_linker])
+LIBC_LINKER_FEATURE([-z dt-debugsz],
+ [-Wl,-z,dt-debugsz],
+ [libc_cv_z_dt_debugsz=yes],
+ [libc_cv_z_dt_debugsz=no])
+LIBC_CONFIG_VAR([have-z-dt-debugsz], [$libc_cv_z_dt_debugsz])
+if test $libc_cv_z_dt_debugsz = yes; then
+ have_z_dt_debugsz=1
+else
+ have_z_dt_debugsz=0
+fi
+AC_DEFINE_UNQUOTED([HAVE_Z_DT_DEBUGSZ], [$have_z_dt_debugsz])
+
AC_CACHE_CHECK(for -static-pie, libc_cv_static_pie, [dnl
LIBC_TRY_CC_OPTION([-static-pie],
[libc_cv_static_pie=yes],
diff --git a/csu/Makefile b/csu/Makefile
index 3054329cea..e2390e4a7d 100644
--- a/csu/Makefile
+++ b/csu/Makefile
@@ -88,6 +88,9 @@ endif
before-compile += $(objpfx)abi-tag.h
generated += abi-tag.h
+# Put it here to generate it earlier.
+gen-as-const-headers += rtld-sizes.sym
+
# These are the special initializer/finalizer files. They are always the
# first and last file in the link. crti.o ... crtn.o define the global
# "functions" _init and _fini to run the .init and .fini sections.
diff --git a/csu/libc-start.c b/csu/libc-start.c
index 0350b006fd..8cfa68a057 100644
--- a/csu/libc-start.c
+++ b/csu/libc-start.c
@@ -403,7 +403,7 @@ LIBC_START_MAIN (int (*main) (int, char **, char ** MAIN_AUXVEC_DECL),
#endif
#ifndef SHARED
- _dl_debug_initialize (0, LM_ID_BASE);
+ _dl_debug_initialize (0, LM_ID_BASE, NULL);
#endif
__libc_start_call_main (main, argc, argv MAIN_AUXVEC_PARAM);
diff --git a/csu/rtld-sizes.sym b/csu/rtld-sizes.sym
new file mode 100644
index 0000000000..2583e58b19
--- /dev/null
+++ b/csu/rtld-sizes.sym
@@ -0,0 +1,4 @@
+#include <link.h>
+
+--
+COMPAT_R_DEBUG_SIZE offsetof (struct r_debug, r_ldbase) + sizeof (ElfW(Addr))
diff --git a/elf/Makefile b/elf/Makefile
index d05f410592..c97c9f8468 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -35,7 +35,8 @@ dl-routines = $(addprefix dl-,load lookup object reloc deps \
execstack open close trampoline \
exception sort-maps lookup-direct \
call-libc-early-init write \
- thread_gscope_wait tls_init_tp)
+ thread_gscope_wait tls_init_tp \
+ debug-symbols)
ifeq (yes,$(use-ldconfig))
dl-routines += dl-cache
endif
@@ -224,7 +225,7 @@ tests += restest1 preloadtest loadfail multiload origtest resolvfail \
tst-tls-ie tst-tls-ie-dlmopen argv0test \
tst-glibc-hwcaps tst-glibc-hwcaps-prepend tst-glibc-hwcaps-mask \
tst-tls20 tst-tls21 tst-dlmopen-dlerror tst-dlmopen-gethostbyname \
- tst-dl-is_dso
+ tst-dl-is_dso tst-_r_gnu_debug
# reldep9
tests-internal += loadtest unload unload2 circleload1 \
neededtest neededtest2 neededtest3 neededtest4 \
@@ -672,6 +673,21 @@ LC_ALL=C sed $(ldd-rewrite) < $< \
endef
endif
+ifeq ($(build-shared),yes)
+generated += dl-debug-compat-symbols.os dl-debug-compat-symbols.o
+
+libof-dl-debug-compat-symbols = rtld
+
+$(objpfx)dl-debug-compat-symbols.os: dl-debug-symbols-gen.c
+ $(compile-command.c) -S
+
+$(objpfx)dl-debug-compat-symbols.o: dl-debug-symbols-gen.c
+ $(compile-command.c) -S
+
+$(objpfx)dl-debug-symbols.os: $(objpfx)dl-debug-compat-symbols.os
+$(objpfx)dl-debug-symbols.o: $(objpfx)dl-debug-compat-symbols.o
+endif
+
$(objpfx)ldd: ldd.bash.in $(common-objpfx)soversions.mk \
$(common-objpfx)config.make
$(gen-ldd)
@@ -1906,3 +1922,7 @@ $(objpfx)tst-getauxval-static.out: $(objpfx)tst-auxvalmod.so
tst-getauxval-static-ENV = LD_LIBRARY_PATH=$(objpfx):$(common-objpfx)
$(objpfx)tst-dlmopen-gethostbyname.out: $(objpfx)tst-dlmopen-gethostbyname-mod.so
+
+ifeq ($(have-z-dt-debugsz),yes)
+LDFLAGS-tst-_r_gnu_debug += -Wl,-z,dt-debugsz
+endif
diff --git a/elf/dl-close.c b/elf/dl-close.c
index f39001cab9..647a5fafe1 100644
--- a/elf/dl-close.c
+++ b/elf/dl-close.c
@@ -500,8 +500,11 @@ _dl_close_worker (struct link_map *map, bool force)
#endif
/* Notify the debugger we are about to remove some loaded objects. */
- struct r_debug *r = _dl_debug_initialize (0, nsid);
+ struct r_debug *r_debug_compat;
+ struct r_debug *r = _dl_debug_initialize (0, nsid, &r_debug_compat);
r->r_state = RT_DELETE;
+ if (r_debug_compat != NULL)
+ r_debug_compat->r_state = RT_DELETE;
_dl_debug_state ();
LIBC_PROBE (unmap_start, 2, nsid, r);
@@ -821,6 +824,8 @@ _dl_close_worker (struct link_map *map, bool force)
/* Notify the debugger those objects are finalized and gone. */
r->r_state = RT_CONSISTENT;
+ if (r_debug_compat != NULL)
+ r_debug_compat->r_state = RT_CONSISTENT;
_dl_debug_state ();
LIBC_PROBE (unmap_complete, 2, nsid, r);
diff --git a/elf/dl-debug-symbols-gen.c b/elf/dl-debug-symbols-gen.c
new file mode 100644
index 0000000000..a8d2aea75d
--- /dev/null
+++ b/elf/dl-debug-symbols-gen.c
@@ -0,0 +1,24 @@
+/* Generate the _r_gnu_debug symbol used to communicate dynamic linker
+ state to the debugger at runtime.
+ Copyright (C) 2021 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <link.h>
+
+/* This structure communicates dl state to the debugger. The debugger
+ finds it via the DT_DEBUG entry in the dynamic section. */
+struct r_debug _r_gnu_debug;
diff --git a/elf/dl-debug-symbols.S b/elf/dl-debug-symbols.S
new file mode 100644
index 0000000000..b43a2c310d
--- /dev/null
+++ b/elf/dl-debug-symbols.S
@@ -0,0 +1,34 @@
+/* Define symbols used to communicate dynamic linker state to the
+ debugger at runtime.
+ Copyright (C) 2021 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <rtld-sizes.h>
+
+#ifdef SHARED
+# include "dl-debug-compat-symbols.os"
+#else
+# include "dl-debug-compat-symbols.o"
+#endif
+
+/* Define the compatibility symbol, _r_debug, used to communicate
+ dynamic linker 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.
+ This must be done after _r_gnu_debug has been defined first. */
+declare_object_symbol_alias (_r_debug, _r_gnu_debug, COMPAT_R_DEBUG_SIZE);
diff --git a/elf/dl-debug.c b/elf/dl-debug.c
index 2cd5f09753..e7d634e237 100644
--- a/elf/dl-debug.c
+++ b/elf/dl-debug.c
@@ -30,34 +30,47 @@ 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;
-
+/* The compatibility symbol, _r_debug, which can have a different
+ address from _r_gnu_debug at run-time due to copy relocation. */
+extern struct r_debug _r_debug;
/* 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. */
struct r_debug *
-_dl_debug_initialize (ElfW(Addr) ldbase, Lmid_t ns)
+_dl_debug_initialize (ElfW(Addr) ldbase, Lmid_t ns,
+ struct r_debug ** _r_debug_compat_p)
{
- struct r_debug *r;
+ struct r_debug *r_debug_compat = NULL;
+ struct r_debug *r, *rp;
if (ns == LM_ID_BASE)
- r = &_r_debug;
+ {
+ r = &_r_gnu_debug;
+ if (r != &_r_debug)
+ r_debug_compat = &_r_debug;
+ }
else
- r = &GL(dl_ns)[ns]._ns_debug;
+ {
+ r = &GL(dl_ns)[ns]._ns_debug;
+ rp = &GL(dl_ns)[ns - 1]._ns_debug;
+ rp->next = r;
+ if (ns - 1 == LM_ID_BASE)
+ _r_gnu_debug.next = r;
+ }
+
+ if (_r_debug_compat_p != NULL)
+ *_r_debug_compat_p = r_debug_compat;
if (r->r_map == NULL || ldbase != 0)
{
/* 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_ldbase = ldbase ?: _r_gnu_debug.r_ldbase;
r->r_map = (void *) GL(dl_ns)[ns]._ns_loaded;
r->r_brk = (ElfW(Addr)) &_dl_debug_state;
+ r->next = NULL;
}
return r;
diff --git a/elf/dl-load.c b/elf/dl-load.c
index 650e4edc35..370f56a6d6 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -949,7 +949,8 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
/* Initialize to keep the compiler happy. */
const char *errstring = NULL;
int errval = 0;
- struct r_debug *r = _dl_debug_initialize (0, nsid);
+ struct r_debug *r_debug_compat;
+ struct r_debug *r = _dl_debug_initialize (0, nsid, &r_debug_compat);
bool make_consistent = false;
/* Get file information. To match the kernel behavior, do not fill
@@ -986,6 +987,8 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
if (make_consistent && r != NULL)
{
r->r_state = RT_CONSISTENT;
+ if (r_debug_compat != NULL)
+ r_debug_compat->r_state = RT_CONSISTENT;
_dl_debug_state ();
LIBC_PROBE (map_failed, 2, nsid, r);
}
@@ -1082,6 +1085,8 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
call _dl_debug_initialize in a static program in case dynamic
linking has not been used before. */
r->r_state = RT_ADD;
+ if (r_debug_compat != NULL)
+ r_debug_compat->r_state = RT_ADD;
_dl_debug_state ();
LIBC_PROBE (map_start, 2, nsid, r);
make_consistent = true;
diff --git a/elf/dl-open.c b/elf/dl-open.c
index ec386626f9..cf4f381b3f 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -523,7 +523,7 @@ dl_open_worker (void *a)
/* One might be tempted to assert that we are RT_CONSISTENT at this point, but that
may not be true if this is a recursive call to dlopen. */
- _dl_debug_initialize (0, args->nsid);
+ _dl_debug_initialize (0, args->nsid, NULL);
/* Load the named object. */
struct link_map *new;
@@ -574,7 +574,8 @@ dl_open_worker (void *a)
if ((mode & RTLD_GLOBAL) && new->l_global == 0)
add_to_global_update (new);
- assert (_dl_debug_initialize (0, args->nsid)->r_state == RT_CONSISTENT);
+ assert (_dl_debug_initialize (0, args->nsid, NULL)->r_state
+ == RT_CONSISTENT);
return;
}
@@ -630,8 +631,12 @@ dl_open_worker (void *a)
#endif
/* Notify the debugger all new objects are now ready to go. */
- struct r_debug *r = _dl_debug_initialize (0, args->nsid);
+ struct r_debug *r_debug_compat;
+ struct r_debug *r = _dl_debug_initialize (0, args->nsid,
+ &r_debug_compat);
r->r_state = RT_CONSISTENT;
+ if (r_debug_compat != NULL)
+ r_debug_compat->r_state = RT_CONSISTENT;
_dl_debug_state ();
LIBC_PROBE (map_complete, 3, args->nsid, r, new);
@@ -830,7 +835,7 @@ no more namespaces available for dlmopen()"));
++GL(dl_nns);
}
- _dl_debug_initialize (0, nsid)->r_state = RT_CONSISTENT;
+ _dl_debug_initialize (0, nsid, NULL)->r_state = RT_CONSISTENT;
}
/* Never allow loading a DSO in a namespace which is empty. Such
direct placements is only causing problems. Also don't allow
@@ -899,7 +904,8 @@ no more namespaces available for dlmopen()"));
the flag here. */
}
- assert (_dl_debug_initialize (0, args.nsid)->r_state == RT_CONSISTENT);
+ assert (_dl_debug_initialize (0, args.nsid, NULL)->r_state
+ == RT_CONSISTENT);
/* Release the lock. */
__rtld_lock_unlock_recursive (GL(dl_load_lock));
@@ -908,7 +914,8 @@ no more namespaces available for dlmopen()"));
_dl_signal_exception (errcode, &exception, NULL);
}
- assert (_dl_debug_initialize (0, args.nsid)->r_state == RT_CONSISTENT);
+ assert (_dl_debug_initialize (0, args.nsid, NULL)->r_state
+ == RT_CONSISTENT);
/* Release the lock. */
__rtld_lock_unlock_recursive (GL(dl_load_lock));
diff --git a/elf/dl-reloc-static-pie.c b/elf/dl-reloc-static-pie.c
index d5bd2f31e9..30c8f1a8bc 100644
--- a/elf/dl-reloc-static-pie.c
+++ b/elf/dl-reloc-static-pie.c
@@ -52,7 +52,7 @@ _dl_relocate_static_pie (void)
main_map->l_relocated = 1;
/* Initialize _r_debug. */
- struct r_debug *r = _dl_debug_initialize (0, LM_ID_BASE);
+ struct r_debug *r = _dl_debug_initialize (0, LM_ID_BASE, NULL);
r->r_state = RT_CONSISTENT;
/* Set up debugging before the debugger is notified for the first
@@ -66,5 +66,11 @@ _dl_relocate_static_pie (void)
with the run-time address of the r_debug structure */
main_map->l_info[DT_DEBUG]->d_un.d_ptr = (ElfW(Addr)) r;
# endif
+
+ /* There is a DT_DEBUGSZ entry in the dynamic section. Fill in the
+ size of the r_debug structure */
+ if (main_map->l_info[VERSYMIDX (DT_DEBUGSZ)] != NULL)
+ main_map->l_info[VERSYMIDX (DT_DEBUGSZ)]->d_un.d_val
+ = sizeof (struct r_debug);
}
#endif
diff --git a/elf/elf.h b/elf/elf.h
index 4738dfa28f..f9f4d217c2 100644
--- a/elf/elf.h
+++ b/elf/elf.h
@@ -939,6 +939,7 @@ typedef struct
GNU extension. */
#define DT_VERSYM 0x6ffffff0
+#define DT_DEBUGSZ 0x6ffffff8
#define DT_RELACOUNT 0x6ffffff9
#define DT_RELCOUNT 0x6ffffffa
diff --git a/elf/link.h b/elf/link.h
index ff3a85c847..229b22c57a 100644
--- a/elf/link.h
+++ b/elf/link.h
@@ -61,9 +61,20 @@ struct r_debug
} r_state;
ElfW(Addr) r_ldbase; /* Base address the linker is loaded at. */
+
+ /* The following extended fields should be accessed only after
+ verifying the size of the r_debug structure via the DT_DEBUGSZ
+ dynamic tag. */
+
+ /* Link to next r_debug structure. Each r_debug structure represents
+ a different namespace. The first r_debug structure is for the
+ default namespace. */
+ struct r_debug *next;
};
-/* This is the instance of that structure used by the dynamic linker. */
+/* This is the compatibility symbol of that structure provided by the
+ dynamic linker. Access to its fields beyond r_ldbase may be invalid.
+ */
extern struct r_debug _r_debug;
/* This symbol refers to the "dynamic structure" in the `.dynamic' section
diff --git a/elf/rtld-debugger-interface.txt b/elf/rtld-debugger-interface.txt
index 61bc99e4b0..920da9c21a 100644
--- a/elf/rtld-debugger-interface.txt
+++ b/elf/rtld-debugger-interface.txt
@@ -32,6 +32,18 @@ but there is no way for the debugger to discover whether any of the
objects in the link-map have been relocated or not.
+Extension to the r_debug structure
+==================================
+
+If the executable's dynamic section has a DT_DEBUGSZ element, the
+run-time linker sets that element's value to the size of the extended
+r_debug structure which has the additional field:
+
+ struct r_debug *next;
+ Link to next r_debug structure. Each r_debug structure represents a
+ different namespace. The first r_debug structure is for the default
+ namespace.
+
Probe-based debugger interface
==============================
diff --git a/elf/rtld.c b/elf/rtld.c
index 878e6480f4..a652dd3729 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -1662,7 +1662,7 @@ dl_main (const ElfW(Phdr) *phdr,
/* Initialize _r_debug. */
struct r_debug *r = _dl_debug_initialize (GL(dl_rtld_map).l_addr,
- LM_ID_BASE);
+ LM_ID_BASE, NULL);
r->r_state = RT_CONSISTENT;
/* Put the link_map for ourselves on the chain so it can be found by
@@ -1771,6 +1771,12 @@ dl_main (const ElfW(Phdr) *phdr,
GL(dl_rtld_map).l_info[DT_DEBUG]->d_un.d_ptr = (ElfW(Addr)) r;
#endif
+ /* There is a DT_DEBUGSZ entry in the dynamic section. Fill in the
+ size of the r_debug structure */
+ if (main_map->l_info[VERSYMIDX (DT_DEBUGSZ)] != NULL)
+ main_map->l_info[VERSYMIDX (DT_DEBUGSZ)]->d_un.d_val
+ = sizeof (struct r_debug);
+
/* We start adding objects. */
r->r_state = RT_ADD;
_dl_debug_state ();
@@ -2491,7 +2497,7 @@ dl_main (const ElfW(Phdr) *phdr,
/* Notify the debugger all new objects are now ready to go. We must re-get
the address since by now the variable might be in another object. */
- r = _dl_debug_initialize (0, LM_ID_BASE);
+ r = _dl_debug_initialize (0, LM_ID_BASE, NULL);
r->r_state = RT_CONSISTENT;
_dl_debug_state ();
LIBC_PROBE (init_complete, 2, LM_ID_BASE, r);
diff --git a/elf/tst-_r_gnu_debug.c b/elf/tst-_r_gnu_debug.c
new file mode 100644
index 0000000000..cf0f99c9da
--- /dev/null
+++ b/elf/tst-_r_gnu_debug.c
@@ -0,0 +1,60 @@
+/* Test _r_gnu_debug via DT_DEBUG and DT_DEBUGSZ.
+ Copyright (C) 2021 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <config.h>
+#include <link.h>
+#include <stdlib.h>
+#include <support/check.h>
+#include <support/test-driver.h>
+
+#ifndef ELF_MACHINE_GET_R_DEBUG
+# define ELF_MACHINE_GET_R_DEBUG(d) \
+ (__extension__ ({ \
+ struct r_debug *debug; \
+ if ((d)->d_tag == DT_DEBUG) \
+ debug = (struct r_debug *) (d)->d_un.d_ptr; \
+ else \
+ debug = NULL; \
+ debug; }))
+#endif
+
+static int
+do_test (void)
+{
+ int found = 0;
+ ElfW(Dyn) *d;
+ for (d = _DYNAMIC; d->d_tag != DT_NULL; ++d)
+ if (d->d_tag == DT_DEBUGSZ)
+ {
+ TEST_VERIFY_EXIT (d->d_un.d_val == sizeof (struct r_debug));
+ found++;
+ }
+ else
+ {
+ struct r_debug *debug = ELF_MACHINE_GET_R_DEBUG (d);
+ if (debug != NULL)
+ {
+ TEST_VERIFY_EXIT (debug->r_version == 1);
+ found++;
+ }
+ }
+ return (found == (HAVE_Z_DT_DEBUGSZ ? 2 : 1)
+ ? EXIT_SUCCESS : EXIT_UNSUPPORTED);
+}
+
+#include <support/test-driver.c>
diff --git a/include/link.h b/include/link.h
index 4af16cb596..a762fccdc1 100644
--- a/include/link.h
+++ b/include/link.h
@@ -353,6 +353,10 @@ struct auditstate
};
+/* This is the hidden instance of struct r_debug used by the dynamic
+ linker. */
+extern struct r_debug _r_gnu_debug attribute_hidden;
+
#if __ELF_NATIVE_CLASS == 32
# define symbind symbind32
#elif __ELF_NATIVE_CLASS == 64
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index 9c15259236..c02027b309 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -1095,8 +1095,10 @@ rtld_hidden_proto (_dl_debug_state)
/* Initialize `struct 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 the `r_ldbase' member. Returns the address of the structure. */
-extern struct r_debug *_dl_debug_initialize (ElfW(Addr) ldbase, Lmid_t ns)
+ in the `r_ldbase' member. Returns the address of the structure and
+ the address of the compatibility symbol. */
+extern struct r_debug *_dl_debug_initialize (ElfW(Addr) ldbase, Lmid_t ns,
+ struct r_debug **)
attribute_hidden;
/* Initialize the basic data structure for the search paths. SOURCE
--
2.31.1
More information about the Libc-alpha
mailing list