[v4,10/14] elf: Bootstrap allocation for future protected memory allocator

Message ID 79fc25bb098ab1671534a83c201df942d7fba5e1.1738530302.git.fweimer@redhat.com (mailing list archive)
State New
Headers
Series RELRO link maps |

Checks

Context Check Description
redhat-pt-bot/TryBot-apply_patch success Patch applied to master at the time it was sent
linaro-tcwg-bot/tcwg_glibc_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_glibc_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_glibc_check--master-aarch64 fail Test failed
linaro-tcwg-bot/tcwg_glibc_check--master-arm fail Test failed

Commit Message

Florian Weimer Feb. 2, 2025, 9:13 p.m. UTC
  A subsequent change will place link maps into memory which is
read-only most of the time.  This means that the link map for
ld.so itself (GLPM (dl_rtld_map)) needs to be put there as well,
which requires allocating it dynamically.
---
 elf/Makefile                                |   1 +
 elf/dl-protmem_bootstrap.h                  |  29 ++++
 elf/rtld.c                                  |  87 ++++++----
 elf/tst-rtld-nomem.c                        | 177 ++++++++++++++++++++
 sysdeps/generic/dl-early_mmap.h             |  35 ++++
 sysdeps/generic/ldsodefs.h                  |   6 +-
 sysdeps/mips/Makefile                       |   6 +
 sysdeps/unix/sysv/linux/dl-early_allocate.c |  17 +-
 sysdeps/unix/sysv/linux/dl-early_mmap.h     |  41 +++++
 9 files changed, 345 insertions(+), 54 deletions(-)
 create mode 100644 elf/dl-protmem_bootstrap.h
 create mode 100644 elf/tst-rtld-nomem.c
 create mode 100644 sysdeps/generic/dl-early_mmap.h
 create mode 100644 sysdeps/unix/sysv/linux/dl-early_mmap.h
  

Patch

diff --git a/elf/Makefile b/elf/Makefile
index 5c833871d0..1d93993241 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -463,6 +463,7 @@  tests += \
   tst-rtld-no-malloc \
   tst-rtld-no-malloc-audit \
   tst-rtld-no-malloc-preload \
+  tst-rtld-nomem \
   tst-rtld-run-static \
   tst-single_threaded \
   tst-single_threaded-pthread \
diff --git a/elf/dl-protmem_bootstrap.h b/elf/dl-protmem_bootstrap.h
new file mode 100644
index 0000000000..a2fc267a2d
--- /dev/null
+++ b/elf/dl-protmem_bootstrap.h
@@ -0,0 +1,29 @@ 
+/* Bootstrap allocation for the protected memory area.
+   Copyright (C) 2025 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 <dl-early_mmap.h>
+
+/* Return a pointer to the protected memory area, or NULL if
+   allocation fails.  This function is called before self-relocation,
+   and the system call needs to be inlined for (most)
+   HIDDEN_VAR_NEEDS_DYNAMIC_RELOC targets.  */
+static inline __attribute__ ((always_inline)) struct rtld_protmem *
+_dl_protmem_bootstrap (void)
+{
+  return _dl_early_mmap (sizeof (struct rtld_protmem));
+}
diff --git a/elf/rtld.c b/elf/rtld.c
index 0bc7d9dbcd..de9e87cd0b 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -53,6 +53,7 @@ 
 #include <dl-find_object.h>
 #include <dl-audit-check.h>
 #include <dl-call_tls_init_tp.h>
+#include <dl-protmem_bootstrap.h>
 
 #include <assert.h>
 
@@ -345,8 +346,6 @@  struct rtld_global _rtld_global =
 extern struct rtld_global _rtld_local
     __attribute__ ((alias ("_rtld_global"), visibility ("hidden")));
 
-struct rtld_protmem _rtld_protmem;
-
 /* This variable is similar to _rtld_local, but all values are
    read-only after relocation.  */
 struct rtld_global_ro _rtld_global_ro attribute_relro =
@@ -421,6 +420,7 @@  static ElfW(Addr) _dl_start_final (void *arg);
 struct dl_start_final_info
 {
   struct link_map l;
+  struct rtld_protmem *protmem;
   RTLD_TIMING_VAR (start_time);
 };
 static ElfW(Addr) _dl_start_final (void *arg,
@@ -455,6 +455,14 @@  _dl_start_final (void *arg, struct dl_start_final_info *info)
 {
   ElfW(Addr) start_addr;
 
+#ifndef DONT_USE_BOOTSTRAP_MAP
+  GLRO (dl_protmem) = info->protmem;
+#endif
+
+  /* Delayed error reporting after relocation processing.  */
+  if (GLRO (dl_protmem) == NULL)
+    _dl_fatal_printf ("Fatal glibc error: Cannot allocate link map\n");
+
   __rtld_malloc_init_stubs ();
 
   /* Do not use an initializer for these members because it would
@@ -478,21 +486,10 @@  _dl_start_final (void *arg, struct dl_start_final_info *info)
   RTLD_TIMING_SET (start_time, info->start_time);
 #endif
 
-  /* Transfer data about ourselves to the permanent link_map structure.  */
-#ifndef DONT_USE_BOOTSTRAP_MAP
-  GLPM(dl_rtld_map).l_addr = info->l.l_addr;
-  GLPM(dl_rtld_map).l_ld = info->l.l_ld;
-  GLPM(dl_rtld_map).l_ld_readonly = info->l.l_ld_readonly;
-  memcpy (GLPM(dl_rtld_map).l_info, info->l.l_info,
-	  sizeof GLPM(dl_rtld_map).l_info);
-  GLPM(dl_rtld_map).l_mach = info->l.l_mach;
-  GLPM(dl_rtld_map).l_relocated = 1;
-#endif
   _dl_setup_hash (&GLPM(dl_rtld_map));
   GLPM(dl_rtld_map).l_real = &GLPM(dl_rtld_map);
   GLPM(dl_rtld_map).l_map_start = (ElfW(Addr)) &__ehdr_start;
   GLPM(dl_rtld_map).l_map_end = (ElfW(Addr)) _end;
-  /* Copy the TLS related data if necessary.  */
 #ifndef DONT_USE_BOOTSTRAP_MAP
 # if NO_TLS_OFFSET != 0
   GLPM(dl_rtld_map).l_rw->l_tls_offset = NO_TLS_OFFSET;
@@ -537,43 +534,59 @@  _dl_start (void *arg)
   rtld_timer_start (&info.start_time);
 #endif
 
-  /* Partly clean the `bootstrap_map' structure up.  Don't use
-     `memset' since it might not be built in or inlined and we cannot
-     make function calls at this point.  Use '__builtin_memset' if we
-     know it is available.  We do not have to clear the memory if we
-     do not have to use the temporary bootstrap_map.  Global variables
-     are initialized to zero by default.  */
-#ifndef DONT_USE_BOOTSTRAP_MAP
-# ifdef HAVE_BUILTIN_MEMSET
-  __builtin_memset (bootstrap_map.l_info, '\0', sizeof (bootstrap_map.l_info));
-# else
-  for (size_t cnt = 0;
-       cnt < sizeof (bootstrap_map.l_info) / sizeof (bootstrap_map.l_info[0]);
-       ++cnt)
-    bootstrap_map.l_info[cnt] = 0;
-# endif
+  struct rtld_protmem *protmem = _dl_protmem_bootstrap ();
+  bool protmem_failed = protmem == NULL;
+  if (protmem_failed)
+    {
+      /* Allocate some space for a stub protected memory area on the
+	 stack, to get to the point when we can report the error.  */
+      protmem = alloca (sizeof (*protmem));
+
+      /* Partly clean the `bootstrap_map' structure up.  Don't use
+	 `memset' since it might not be built in or inlined and we
+	 cannot make function calls at this point.  Use
+	 '__builtin_memset' if we know it is available.  */
+#ifdef HAVE_BUILTIN_MEMSET
+      __builtin_memset (protmem->_dl_rtld_map.l_info,
+			'\0', sizeof (protmem->_dl_rtld_map.l_info));
+#else
+      for (size_t i = 0; i < array_length (protmem->_dl_rtld_map.l_info); ++i)
+	protmem->_dl_rtld_map.l_info[i] = NULL;
 #endif
+    }
 
   /* Figure out the run-time load address of the dynamic linker itself.  */
-  bootstrap_map.l_addr = elf_machine_load_address ();
+  protmem->_dl_rtld_map.l_addr = elf_machine_load_address ();
 
   /* Read our own dynamic section and fill in the info array.  */
-  bootstrap_map.l_ld = (void *) bootstrap_map.l_addr + elf_machine_dynamic ();
-  bootstrap_map.l_ld_readonly = DL_RO_DYN_SECTION;
-  elf_get_dynamic_info (&bootstrap_map, true, false);
+  protmem->_dl_rtld_map.l_ld = ((void *) protmem->_dl_rtld_map.l_addr
+				+ elf_machine_dynamic ());
+  protmem->_dl_rtld_map.l_ld_readonly = DL_RO_DYN_SECTION;
+  elf_get_dynamic_info (&protmem->_dl_rtld_map, true, false);
 
 #ifdef ELF_MACHINE_BEFORE_RTLD_RELOC
-  ELF_MACHINE_BEFORE_RTLD_RELOC (&bootstrap_map, bootstrap_map.l_info);
+  ELF_MACHINE_BEFORE_RTLD_RELOC (&protmem->_dl_rtld_map,
+				 protmem->_dl_rtld_map.l_info);
 #endif
 
-  if (bootstrap_map.l_addr)
+  if (protmem->_dl_rtld_map.l_addr)
     {
       /* Relocate ourselves so we can do normal function calls and
 	 data access using the global offset table.  */
 
-      ELF_DYNAMIC_RELOCATE (&bootstrap_map, NULL, 0, 0, 0);
+      ELF_DYNAMIC_RELOCATE (&protmem->_dl_rtld_map, NULL, 0, 0, 0);
     }
-  bootstrap_map.l_relocated = 1;
+  protmem->_dl_rtld_map.l_relocated = 1;
+
+  /* Communicate the original mmap failure to _dl_start_final.  */
+  if (protmem_failed)
+    protmem = NULL;
+
+#ifdef DONT_USE_BOOTSTRAP_MAP
+  GLRO (dl_protmem) = protmem;
+#else
+  info.protmem = protmem;
+#endif
 
   /* Please note that we don't allow profiling of this object and
      therefore need not test whether we have to allocate the array
@@ -1024,7 +1037,7 @@  ERROR: audit interface '%s' requires version %d (maximum supported version %d);
   else
     *last_audit = (*last_audit)->next = &newp->ifaces;
 
-  /* The dynamic linker link map is statically allocated, so the
+  /* The dynamic linker link map is allocated separately, so the
      cookie in _dl_new_object has not happened.  */
   link_map_audit_state (&GLPM(dl_rtld_map), GLRO (dl_naudit))->cookie
     = (intptr_t) &GLPM(dl_rtld_map);
diff --git a/elf/tst-rtld-nomem.c b/elf/tst-rtld-nomem.c
new file mode 100644
index 0000000000..b8caf5d8fe
--- /dev/null
+++ b/elf/tst-rtld-nomem.c
@@ -0,0 +1,177 @@ 
+/* Test that out-of-memory during early ld.so startup reports an error.
+   Copyright (C) 2025 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/>.  */
+
+/* This test invokes execve with increasing RLIMIT_AS limits, to
+   trigger the early _dl_protmem_bootstrap memory allocation failure
+   and check that a proper error is reported for it.  */
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <support/check.h>
+#include <support/support.h>
+#include <support/xunistd.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+static int
+do_test (void)
+{
+  long int page_size = sysconf (_SC_PAGE_SIZE);
+  TEST_VERIFY (page_size > 0);
+
+  struct rlimit rlim;
+  TEST_COMPARE (getrlimit (RLIMIT_AS, &rlim), 0);
+
+  /* Reduced once we encounter success.  */
+  int kb_limit = 2048;
+
+  /* Exit status in case of test error.  */
+  enum { unexpected_error = 17 };
+
+  /* Used to verify that at least one execve crash is encountered.
+     This is how execve reports late memory allocation failures due
+     to rlimit.  */
+  bool crash_seen = false;
+
+  /* Set to true if the early out-of-memory error message is
+     encountered.  */
+  bool oom_error_seen = false;
+
+  /* Set to true once success (the usage message) is encountered.
+     This is expected to happen only after oom_error_seen turns true,
+     otherwise the rlimit does not work.  */
+  bool success_seen = false;
+
+  /* Try increasing rlimits.  The kernel rounds down to page sizes, so
+     try only page size increments.  */
+  for (int kb = 128; kb <= kb_limit; kb += page_size / 1024)
+    {
+      printf ("info: trying %d KiB\n", kb);
+
+      int pipe_stdout[2];
+      xpipe (pipe_stdout);
+      int pipe_stderr[2];
+      xpipe (pipe_stderr);
+
+      pid_t pid = xfork ();
+      if (pid == 0)
+        {
+          /* Restrict address space for the ld.so invocation.  */
+          rlim.rlim_cur = kb * 1024;
+          int ret = setrlimit (RLIMIT_AS, &rlim);
+          TEST_COMPARE (ret, 0);
+          if (ret != 0)
+            _exit (unexpected_error);
+
+          /* Redirect output for capture.  */
+          TEST_COMPARE (dup2 (pipe_stdout[1], STDOUT_FILENO),
+                        STDOUT_FILENO);
+          TEST_COMPARE (dup2 (pipe_stderr[1], STDERR_FILENO),
+                        STDERR_FILENO);
+
+          /* Try to invoke ld.so with the resource limit in place.  */
+          char ldso[] = "ld.so";
+          char *const argv[] = { ldso, NULL };
+          execve (support_objdir_elf_ldso, argv, &argv[1]);
+          TEST_COMPARE (errno, ENOMEM);
+          _exit (unexpected_error);
+        }
+
+      int status;
+      xwaitpid (pid, &status, 0);
+
+      xclose (pipe_stdout[1]);
+      xclose (pipe_stderr[1]);
+
+      /* No output on stdout.  */
+      char actual[1024];
+      ssize_t count = read (pipe_stdout[0], actual, sizeof (actual));
+      if (count < 0)
+        FAIL_EXIT1 ("read stdout: %m");
+      TEST_COMPARE_BLOB ("", 0, actual, count);
+
+      /* Read the standard error output.  */
+      count = read (pipe_stderr[0], actual, sizeof (actual));
+      if (count < 0)
+        FAIL_EXIT1 ("read stderr: %m");
+
+      if (WIFEXITED (status) && WEXITSTATUS (status) == 1)
+        {
+          TEST_VERIFY (oom_error_seen);
+          static const char expected[] = "\
+ld.so: missing program name\n\
+Try 'ld.so --help' for more information.\n\
+";
+          TEST_COMPARE_BLOB (expected, strlen (expected), actual, count);
+          if (!success_seen)
+            {
+              puts ("info: first success");
+              /* Four more tries with increasing rlimit, to catch
+                 potential secondary crashes.  */
+              kb_limit = kb + page_size / 1024 * 4;
+            }
+          success_seen = true;
+          continue;
+        }
+      if (WIFEXITED (status) && WEXITSTATUS (status) == 127)
+        {
+          TEST_VERIFY (crash_seen);
+          TEST_VERIFY (!success_seen);
+          static const char expected[] =
+            "Fatal glibc error: Cannot allocate link map\n";
+          TEST_COMPARE_BLOB (expected, strlen (expected), actual, count);
+          if (!oom_error_seen)
+            puts ("info: first memory allocation error");
+          oom_error_seen = true;
+          continue;
+        }
+
+      TEST_VERIFY (!success_seen);
+      TEST_VERIFY (!oom_error_seen);
+
+      if (WIFEXITED (status))
+        {
+          /* Unexpected regular exit status.  */
+          TEST_COMPARE (WIFEXITED (status), 1);
+          TEST_COMPARE_BLOB ("", 0, actual, count);
+        }
+      else if (WIFSIGNALED (status) && WTERMSIG (status) == SIGSEGV)
+        {
+          /* Very early out of memory.  No output expected.  */
+          TEST_COMPARE_BLOB ("", 0, actual, count);
+          if (!crash_seen)
+            puts ("info: first expected crash observed");
+          crash_seen = true;
+        }
+      else
+        {
+          /* Unexpected status.  */
+          printf ("error: unexpected exit status %d\n", status);
+          support_record_failure ();
+          TEST_COMPARE_BLOB ("", 0, actual, count);
+        }
+    }
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/sysdeps/generic/dl-early_mmap.h b/sysdeps/generic/dl-early_mmap.h
new file mode 100644
index 0000000000..75eb8eb30c
--- /dev/null
+++ b/sysdeps/generic/dl-early_mmap.h
@@ -0,0 +1,35 @@ 
+/* Early anonymous mmap for ld.so, before self-relocation.  Generic version.
+   Copyright (C) 2025 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/>.  */
+
+#ifndef DL_EARLY_MMAP_H
+#define DL_EARLY_MMAP_H
+
+/* The generic version assumes that regular mmap works.  It returns
+   NULL on failure.  */
+static inline void *
+_dl_early_mmap (size_t size)
+{
+  void *ret = __mmap (NULL, size, PROT_READ | PROT_WRITE,
+                      MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+  if (ret == MAP_FAILED)
+    return NULL;
+  else
+    return ret;
+}
+
+#endif /* DL_EARLY_MMAP_H */
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index ac71668f29..0ff0650cb1 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -515,12 +515,11 @@  struct rtld_protmem
   /* Structure describing the dynamic linker itself.  */
   struct link_map _dl_rtld_map;
 };
-extern struct rtld_protmem _rtld_protmem attribute_hidden;
 #endif /* SHARED */
 
 /* GLPM(FIELD) denotes the FIELD in the protected memory area.  */
 #ifdef SHARED
-# define GLPM(name) _rtld_protmem._##name
+# define GLPM(name) GLRO (dl_protmem)->_##name
 #else
 # define GLPM(name) _##name
 #endif
@@ -660,6 +659,9 @@  struct rtld_global_ro
   EXTERN enum dso_sort_algorithm _dl_dso_sort_algo;
 
 #ifdef SHARED
+  /* Pointer to the protected memory area.  */
+  EXTERN struct rtld_protmem *_dl_protmem;
+
   /* We add a function table to _rtld_global which is then used to
      call the function instead of going through the PLT.  The result
      is that we can avoid exporting the functions and we do not jump
diff --git a/sysdeps/mips/Makefile b/sysdeps/mips/Makefile
index d189973aa0..2ec9bf2a6c 100644
--- a/sysdeps/mips/Makefile
+++ b/sysdeps/mips/Makefile
@@ -32,6 +32,12 @@  test-xfail-tst-audit24d = yes
 test-xfail-tst-audit25a = yes
 test-xfail-tst-audit25b = yes
 
+# _dl_start performs a system call before self-relocation, to allocate
+# the link map for ld.so itself.  This involves a direct function
+# call.  Build rtld.c in MIPS32 mode, so that this function call does
+# not require a run-time relocation.
+CFLAGS-rtld.c += -mno-mips16
+
 ifneq ($(o32-fpabi),)
 tests += tst-abi-interlink
 
diff --git a/sysdeps/unix/sysv/linux/dl-early_allocate.c b/sysdeps/unix/sysv/linux/dl-early_allocate.c
index 257519b789..ca7121d52e 100644
--- a/sysdeps/unix/sysv/linux/dl-early_allocate.c
+++ b/sysdeps/unix/sysv/linux/dl-early_allocate.c
@@ -29,7 +29,7 @@ 
 #include <unistd.h>
 
 #include <brk_call.h>
-#include <mmap_call.h>
+#include <dl-early_mmap.h>
 
 /* Defined in brk.c.  */
 extern void *__curbrk;
@@ -63,20 +63,7 @@  _dl_early_allocate (size_t size)
      unfortunate ASLR layout decisions and kernel bugs, particularly
      for static PIE.  */
   if (result == NULL)
-    {
-      long int ret;
-      int prot = PROT_READ | PROT_WRITE;
-      int flags = MAP_PRIVATE | MAP_ANONYMOUS;
-#ifdef __NR_mmap2
-      ret = MMAP_CALL_INTERNAL (mmap2, 0, size, prot, flags, -1, 0);
-#else
-      ret = MMAP_CALL_INTERNAL (mmap, 0, size, prot, flags, -1, 0);
-#endif
-      if (INTERNAL_SYSCALL_ERROR_P (ret))
-        result = NULL;
-      else
-        result = (void *) ret;
-    }
+    result = _dl_early_mmap (size);
 
   return result;
 }
diff --git a/sysdeps/unix/sysv/linux/dl-early_mmap.h b/sysdeps/unix/sysv/linux/dl-early_mmap.h
new file mode 100644
index 0000000000..1d83daa6a6
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/dl-early_mmap.h
@@ -0,0 +1,41 @@ 
+/* Early anonymous mmap for ld.so, before self-relocation.  Linux version.
+   Copyright (C) 2022-2023 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/>.  */
+
+#ifndef DL_EARLY_MMAP_H
+#define DL_EARLY_MMAP_H
+
+#include <mmap_call.h>
+
+static inline __attribute__ ((always_inline)) void *
+_dl_early_mmap (size_t size)
+{
+  long int ret;
+  int prot = PROT_READ | PROT_WRITE;
+  int flags = MAP_PRIVATE | MAP_ANONYMOUS;
+#ifdef __NR_mmap2
+  ret = MMAP_CALL_INTERNAL (mmap2, 0, size, prot, flags, -1, 0);
+#else
+  ret = MMAP_CALL_INTERNAL (mmap, 0, size, prot, flags, -1, 0);
+#endif
+  if (INTERNAL_SYSCALL_ERROR_P (ret))
+    return NULL;
+  else
+    return (void *) ret;
+}
+
+#endif /* DL_EARLY_MMAP_H */