summary refs log tree commit diff
path: root/elf/dl-minimal.c
diff options
context:
space:
mode:
authorFlorian Weimer <fweimer@redhat.com>2020-02-08 19:58:43 +0100
committerFlorian Weimer <fweimer@redhat.com>2020-02-15 11:01:23 +0100
commit3a0ecccb599a6b1ad4b149dc569c0080e92d057b (patch)
treee1c4c0e5f2e80221054d6bb6260b4038e27567b4 /elf/dl-minimal.c
parent2efa52c880d46ee89523c8ed8102ceeb02043926 (diff)
downloadglibc-3a0ecccb599a6b1ad4b149dc569c0080e92d057b.tar.gz
glibc-3a0ecccb599a6b1ad4b149dc569c0080e92d057b.tar.xz
glibc-3a0ecccb599a6b1ad4b149dc569c0080e92d057b.zip
ld.so: Do not export free/calloc/malloc/realloc functions [BZ #25486]
Exporting functions and relying on symbol interposition from libc.so
makes the choice of implementation dependent on DT_NEEDED order, which
is not what some compiler drivers expect.

This commit replaces one magic mechanism (symbol interposition) with
another one (preprocessor-/compiler-based redirection).  This makes
the hand-over from the minimal malloc to the full malloc more
explicit.

Removing the ABI symbols is backwards-compatible because libc.so is
always in scope, and the dynamic loader will find the malloc-related
symbols there since commit f0b2132b35248c1f4a80f62a2c38cddcc802aa8c
("ld.so: Support moving versioned symbols between sonames
[BZ #24741]").

Reviewed-by: Carlos O'Donell <carlos@redhat.com>
Diffstat (limited to 'elf/dl-minimal.c')
-rw-r--r--elf/dl-minimal.c97
1 files changed, 84 insertions, 13 deletions
diff --git a/elf/dl-minimal.c b/elf/dl-minimal.c
index 42192f8a7b..c79ce23be4 100644
--- a/elf/dl-minimal.c
+++ b/elf/dl-minimal.c
@@ -26,11 +26,87 @@
 #include <sys/param.h>
 #include <sys/types.h>
 #include <ldsodefs.h>
+#include <dl-irel.h>
+#include <dl-hash.h>
+#include <dl-sym-post.h>
 #include <_itoa.h>
 #include <malloc/malloc-internal.h>
 
 #include <assert.h>
 
+/* The rtld startup code calls __rtld_malloc_init_stubs after the
+  first self-relocation to adjust the pointers to the minimal
+  implementation below.  Before the final relocation,
+  __rtld_malloc_init_real is called to replace the pointers with the
+  real implementation.  */
+__typeof (calloc) *__rtld_calloc;
+__typeof (free) *__rtld_free;
+__typeof (malloc) *__rtld_malloc;
+__typeof (realloc) *__rtld_realloc;
+
+/* Defined below.  */
+static __typeof (calloc) rtld_calloc attribute_relro;
+static __typeof (free) rtld_free attribute_relro;
+static __typeof (malloc) rtld_malloc attribute_relro;
+static __typeof (realloc) rtld_realloc attribute_relro;
+
+void
+__rtld_malloc_init_stubs (void)
+{
+  __rtld_calloc = &rtld_calloc;
+  __rtld_free = &rtld_free;
+  __rtld_malloc = &rtld_malloc;
+  __rtld_realloc = &rtld_realloc;
+}
+
+/* Lookup NAME at VERSION in the scope of MATCH.  */
+static void *
+lookup_malloc_symbol (struct link_map *main_map, const char *name,
+		      struct r_found_version *version)
+{
+
+  const ElfW(Sym) *ref = NULL;
+  lookup_t result = _dl_lookup_symbol_x (name, main_map, &ref,
+					 main_map->l_scope,
+					 version, 0, 0, NULL);
+
+  assert (ELFW(ST_TYPE) (ref->st_info) != STT_TLS);
+  void *value = DL_SYMBOL_ADDRESS (result, ref);
+
+  return _dl_sym_post (result, ref, value, 0, main_map);
+}
+
+void
+__rtld_malloc_init_real (struct link_map *main_map)
+{
+  /* We cannot use relocations and initializers for this because the
+     changes made by __rtld_malloc_init_stubs break REL-style
+     (non-RELA) relocations that depend on the previous pointer
+     contents.  Also avoid direct relocation depedencies for the
+     malloc symbols so this function can be called before the final
+     rtld relocation (which enables RELRO, after which the pointer
+     variables cannot be written to).  */
+
+  struct r_found_version version;
+  version.name = symbol_version_string (libc, GLIBC_2_0);
+  version.hidden = 0;
+  version.hash = _dl_elf_hash (version.name);
+  version.filename = NULL;
+
+  void *new_calloc = lookup_malloc_symbol (main_map, "calloc", &version);
+  void *new_free = lookup_malloc_symbol (main_map, "free", &version);
+  void *new_malloc = lookup_malloc_symbol (main_map, "malloc", &version);
+  void *new_realloc = lookup_malloc_symbol (main_map, "realloc", &version);
+
+  /* Update the pointers in one go, so that any internal allocations
+     performed by lookup_malloc_symbol see a consistent
+     implementation.  */
+  __rtld_calloc = new_calloc;
+  __rtld_free = new_free;
+  __rtld_malloc = new_malloc;
+  __rtld_realloc = new_realloc;
+}
+
 /* Minimal malloc allocator for used during initial link.  After the
    initial link, a full malloc implementation is interposed, either
    the one in libc, or a different one supplied by the user through
@@ -38,14 +114,9 @@
 
 static void *alloc_ptr, *alloc_end, *alloc_last_block;
 
-/* Declarations of global functions.  */
-extern void weak_function free (void *ptr);
-extern void * weak_function realloc (void *ptr, size_t n);
-
-
 /* Allocate an aligned memory block.  */
-void * weak_function
-malloc (size_t n)
+static void *
+rtld_malloc (size_t n)
 {
   if (alloc_end == 0)
     {
@@ -87,8 +158,8 @@ malloc (size_t n)
 /* We use this function occasionally since the real implementation may
    be optimized when it can assume the memory it returns already is
    set to NUL.  */
-void * weak_function
-calloc (size_t nmemb, size_t size)
+static void *
+rtld_calloc (size_t nmemb, size_t size)
 {
   /* New memory from the trivial malloc above is always already cleared.
      (We make sure that's true in the rare occasion it might not be,
@@ -104,8 +175,8 @@ calloc (size_t nmemb, size_t size)
 }
 
 /* This will rarely be called.  */
-void weak_function
-free (void *ptr)
+void
+rtld_free (void *ptr)
 {
   /* We can free only the last block allocated.  */
   if (ptr == alloc_last_block)
@@ -118,8 +189,8 @@ free (void *ptr)
 }
 
 /* This is only called with the most recent block returned by malloc.  */
-void * weak_function
-realloc (void *ptr, size_t n)
+void *
+rtld_realloc (void *ptr, size_t n)
 {
   if (ptr == NULL)
     return malloc (n);