diff mbox series

[v2,2/2] ld.so: Hide _r_debug to support DT_DEBUG [BZ #28130]

Message ID 20210802204239.205959-1-hjl.tools@gmail.com
State Not Applicable
Headers show
Series None | expand

Commit Message

H.J. Lu Aug. 2, 2021, 8:42 p.m. UTC
<link.h> has

extern struct r_debug _r_debug;

which is exported from ld.so.  rtld.c has

  /* Initialize _r_debug.  */
  struct r_debug *r = _dl_debug_initialize (GL(dl_rtld_map).l_addr,
                                            LM_ID_BASE);
...
  if (main_map->l_info[DT_DEBUG] != NULL)
    /* There is a DT_DEBUG entry in the dynamic section.  Fill it in
       with the run-time address of the r_debug structure  */
    main_map->l_info[DT_DEBUG]->d_un.d_ptr = (ElfW(Addr)) r;

With COPY relocation, the address of _r_debug at run-time may be in the
executable if it references _r_debug.  Since debugger uses DT_DEBUG, it
will reference the staled original definition of _r_debug in ld.so.

1. Make _r_debug a compatible symbol in ld.so and don't export it.  The
existing dynamic executables with _r_debug reference will get a copy of
_r_debug which won't be updated by ld.so.  But DT_DEBUG will work with
debuggers.
2. Make a hidden alias of _r_debug, _r_debug_internal, and use it in
ld.so.
3. Add a support function, get_r_debug, to return the value in DT_DEBUG,
which is the address of _r_debug.  FIXME: MIPS needs to check a different
dynamic tag to get the address of _r_debug.
4. Provide

struct r_debug *get_r_debug (void);
 #define _r_debug (*get_r_debug ())

in <support/support.h> to support glibc tests.

This fixes BZ #28130.
---
 elf/Makefile                     |  2 ++
 elf/circleload1.c                |  1 +
 elf/dl-debug.c                   | 28 ++++++++++++++---
 elf/link.h                       |  3 --
 elf/loadtest.c                   |  1 +
 elf/neededtest.c                 |  1 +
 elf/neededtest2.c                |  1 +
 elf/neededtest3.c                |  1 +
 elf/neededtest4.c                |  1 +
 elf/unload.c                     |  1 +
 elf/unload2.c                    |  1 +
 support/Makefile                 |  1 +
 support/get_r_debug.c            | 54 ++++++++++++++++++++++++++++++++
 support/support.h                |  6 ++++
 sysdeps/generic/dl-get_r_debug.h | 28 +++++++++++++++++
 15 files changed, 122 insertions(+), 8 deletions(-)
 create mode 100644 support/get_r_debug.c
 create mode 100644 sysdeps/generic/dl-get_r_debug.h
diff mbox series

Patch

diff --git a/elf/Makefile b/elf/Makefile
index d05f410592..a4d3db922b 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -609,6 +609,8 @@  $(objpfx)ld.so: $(objpfx)librtld.os $(ld-map)
 	$(call after-link,$@.new)
 	$(READELF) -s $@.new \
 	  | $(AWK) '($$7 ~ /^UND(|EF)$$/ && $$1 != "0:" && $$4 != "REGISTER") { print; p=1 } END { exit p != 0 }'
+	$(READELF) -rW $@.new \
+	  | $(AWK) '($$5 ~/_r_debug/) { print; p=1 } END { exit p != 0 }'
 	mv -f $@.new $@
 
 ifeq (yes,$(build-shared))
diff --git a/elf/circleload1.c b/elf/circleload1.c
index 990ff84a84..e1ff8e272b 100644
--- a/elf/circleload1.c
+++ b/elf/circleload1.c
@@ -4,6 +4,7 @@ 
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <support/support.h>
 
 #define MAPS ((struct link_map *) _r_debug.r_map)
 
diff --git a/elf/dl-debug.c b/elf/dl-debug.c
index 2cd5f09753..a70ba7abb7 100644
--- a/elf/dl-debug.c
+++ b/elf/dl-debug.c
@@ -17,7 +17,7 @@ 
    <https://www.gnu.org/licenses/>.  */
 
 #include <ldsodefs.h>
-
+#include <shlib-compat.h>
 
 /* These are the members in the public `struct link_map' type.
    Sanity check that the internal type and the public type match.  */
@@ -36,9 +36,27 @@  extern const int verify_link_map_members[(VERIFY_MEMBER (l_addr)
    to examine and it looks for this particular symbol name.  */
 struct r_debug _r_debug;
 
+extern struct r_debug _r_debug_internal attribute_hidden;
+strong_alias (_r_debug, _r_debug_internal)
+
+/* Define ABI_rtld_GLIBC_2_35 from ABI_rtld_GLIBC_2_34 if there are no
+   symbols in GLIBC_2.35 when this macro was first used.  */
+#ifndef ABI_rtld_GLIBC_2_35
+# define ABI_rtld_GLIBC_2_35 ABI_rtld_GLIBC_2_34
+#endif
+
+/* The latest ABI of _r_debug is GLIBC_2.33 in RV32 of RISC-V.  */
+#if ABI_rtld_GLIBC_2_35 != ABI_rtld_GLIBC_2_34
+# error ABI_rtld_GLIBC_2_35 != ABI_rtld_GLIBC_2_34
+#endif
+
+#if SHLIB_COMPAT (rtld, GLIBC_2_0, GLIBC_2_35)
+strong_alias (_r_debug, _r_debug_compat)
+compat_symbol (rtld, _r_debug_compat, _r_debug, GLIBC_2_0);
+#endif
 
-/* 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
+/* 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 *
@@ -47,7 +65,7 @@  _dl_debug_initialize (ElfW(Addr) ldbase, Lmid_t ns)
   struct r_debug *r;
 
   if (ns == LM_ID_BASE)
-    r = &_r_debug;
+    r = &_r_debug_internal;
   else
     r = &GL(dl_ns)[ns]._ns_debug;
 
@@ -55,7 +73,7 @@  _dl_debug_initialize (ElfW(Addr) ldbase, Lmid_t ns)
     {
       /* 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_debug_internal.r_ldbase;
       r->r_map = (void *) GL(dl_ns)[ns]._ns_loaded;
       r->r_brk = (ElfW(Addr)) &_dl_debug_state;
     }
diff --git a/elf/link.h b/elf/link.h
index ff3a85c847..3431142d10 100644
--- a/elf/link.h
+++ b/elf/link.h
@@ -63,9 +63,6 @@  struct r_debug
     ElfW(Addr) r_ldbase;	/* Base address the linker is loaded at.  */
   };
 
-/* This is the instance of that structure used by the dynamic linker.  */
-extern struct r_debug _r_debug;
-
 /* This symbol refers to the "dynamic structure" in the `.dynamic' section
    of whatever module refers to `_DYNAMIC'.  So, to find its own
    `struct r_debug', a program could do:
diff --git a/elf/loadtest.c b/elf/loadtest.c
index b5eab5e93c..9c507e8802 100644
--- a/elf/loadtest.c
+++ b/elf/loadtest.c
@@ -6,6 +6,7 @@ 
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <support/support.h>
 
 
 /* How many load/unload operations do we do.  */
diff --git a/elf/neededtest.c b/elf/neededtest.c
index 3cea499314..252e193dca 100644
--- a/elf/neededtest.c
+++ b/elf/neededtest.c
@@ -4,6 +4,7 @@ 
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <support/support.h>
 
 #define MAPS ((struct link_map *) _r_debug.r_map)
 
diff --git a/elf/neededtest2.c b/elf/neededtest2.c
index 17c75f2ba3..129c2ac486 100644
--- a/elf/neededtest2.c
+++ b/elf/neededtest2.c
@@ -4,6 +4,7 @@ 
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <support/support.h>
 
 #define MAPS ((struct link_map *) _r_debug.r_map)
 
diff --git a/elf/neededtest3.c b/elf/neededtest3.c
index 41970cf2c7..5f6940fcbe 100644
--- a/elf/neededtest3.c
+++ b/elf/neededtest3.c
@@ -4,6 +4,7 @@ 
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <support/support.h>
 
 #define MAPS ((struct link_map *) _r_debug.r_map)
 
diff --git a/elf/neededtest4.c b/elf/neededtest4.c
index 0ae0b7ff47..50a88e8835 100644
--- a/elf/neededtest4.c
+++ b/elf/neededtest4.c
@@ -4,6 +4,7 @@ 
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <support/support.h>
 
 #define MAPS ((struct link_map *) _r_debug.r_map)
 
diff --git a/elf/unload.c b/elf/unload.c
index 4566f226f8..35c8a03f79 100644
--- a/elf/unload.c
+++ b/elf/unload.c
@@ -8,6 +8,7 @@ 
 #include <mcheck.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <support/support.h>
 
 #define MAPS ((struct link_map *) _r_debug.r_map)
 
diff --git a/elf/unload2.c b/elf/unload2.c
index eef2bfd426..9e8b26ec55 100644
--- a/elf/unload2.c
+++ b/elf/unload2.c
@@ -5,6 +5,7 @@ 
 #include <link.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <support/support.h>
 
 #define MAPS ((struct link_map *) _r_debug.r_map)
 
diff --git a/support/Makefile b/support/Makefile
index a462781718..5b6d5bb23a 100644
--- a/support/Makefile
+++ b/support/Makefile
@@ -32,6 +32,7 @@  libsupport-routines = \
   check_hostent \
   check_netent \
   delayed_exit \
+  get_r_debug \
   ignore_stderr \
   next_to_fault \
   oom_error \
diff --git a/support/get_r_debug.c b/support/get_r_debug.c
new file mode 100644
index 0000000000..2bc30022fe
--- /dev/null
+++ b/support/get_r_debug.c
@@ -0,0 +1,54 @@ 
+/* Return a pointer to _r_debug, the internal debug structure in the
+   dynamic linker.
+   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>
+#include <dl-get_r_debug.h>
+
+/* Get a pointer to _r_debug.  */
+
+static int
+callback (struct dl_phdr_info *info, size_t size, void *data)
+{
+  const ElfW(Phdr) *phdr = info->dlpi_phdr;
+  ElfW(Dyn) *d;
+  int n;
+  struct r_debug **debugp = (struct r_debug **) data;
+
+  for (n = info->dlpi_phnum; --n >= 0; phdr++)
+    if (phdr->p_type == PT_DYNAMIC)
+      {
+	d = (void *) (info->dlpi_addr + phdr->p_vaddr);
+	struct r_debug *debug = dl_get_r_debug (d);
+	if (debug != NULL)
+	  {
+	    *debugp = debug;
+	    return 1;
+	  }
+      }
+
+  return 0;
+}
+
+struct r_debug *
+get_r_debug (void)
+{
+  struct r_debug *debug = NULL;
+  dl_iterate_phdr (callback, &debug);
+  return debug;
+}
diff --git a/support/support.h b/support/support.h
index 834dba9097..6d00e4c46e 100644
--- a/support/support.h
+++ b/support/support.h
@@ -33,6 +33,8 @@ 
 #include <sys/types.h>
 /* For locale_t.  */
 #include <locale.h>
+/* For struct r_debug.  */
+#include <link.h>
 
 __BEGIN_DECLS
 
@@ -193,6 +195,10 @@  struct support_stack support_stack_alloc (size_t size);
 /* Deallocate the STACK.  */
 void support_stack_free (struct support_stack *stack);
 
+/* Get a pointer to _r_debug. */
+struct r_debug *get_r_debug (void);
+#define _r_debug (*get_r_debug ())
+
 __END_DECLS
 
 #endif /* SUPPORT_H */
diff --git a/sysdeps/generic/dl-get_r_debug.h b/sysdeps/generic/dl-get_r_debug.h
new file mode 100644
index 0000000000..2595ec2ba9
--- /dev/null
+++ b/sysdeps/generic/dl-get_r_debug.h
@@ -0,0 +1,28 @@ 
+/* Get a pointer to _r_debug from PT_DYNAMIC.
+   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/>.  */
+
+/* Return the value in DT_DEBUG.  */
+
+struct r_debug *
+dl_get_r_debug (ElfW(Dyn) *d)
+{
+  for (; d->d_tag != DT_NULL; ++d)
+    if (d->d_tag == DT_DEBUG)
+      return (struct r_debug *) d->d_un.d_val;
+  return NULL;
+}