/* Minimal replacements for basic facilities used in the dynamic linker.
- Copyright (C) 1995-2016 Free Software Foundation, Inc.
+ Copyright (C) 1995-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
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
- <http://www.gnu.org/licenses/>. */
+ <https://www.gnu.org/licenses/>. */
-#include <errno.h>
-#include <limits.h>
-#include <stdio.h>
-#include <string.h>
-#include <tls.h>
-#include <unistd.h>
-#include <sys/mman.h>
-#include <sys/param.h>
-#include <sys/types.h>
+#include <assert.h>
#include <ldsodefs.h>
+#include <dl-irel.h>
+#include <dl-hash.h>
+#include <dl-sym-post.h>
#include <_itoa.h>
+#include <dl-minimal-malloc.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
-#include <assert.h>
-
-/* Minimal `malloc' allocator for use while loading shared libraries.
- No block is ever freed. */
-
-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);
-extern unsigned long int weak_function __strtoul_internal (const char *nptr,
- char **endptr,
- int base,
- int group);
-extern unsigned long int weak_function strtoul (const char *nptr,
- char **endptr, int base);
-
+/* 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 attribute_relro;
+__typeof (free) *__rtld_free attribute_relro;
+__typeof (malloc) *__rtld_malloc attribute_relro;
+__typeof (realloc) *__rtld_realloc attribute_relro;
-/* Allocate an aligned memory block. */
-void * weak_function
-__libc_memalign (size_t align, size_t n)
+void
+__rtld_malloc_init_stubs (void)
{
- if (alloc_end == 0)
- {
- /* Consume any unused space in the last page of our data segment. */
- extern int _end attribute_hidden;
- alloc_ptr = &_end;
- alloc_end = (void *) 0 + (((alloc_ptr - (void *) 0)
- + GLRO(dl_pagesize) - 1)
- & ~(GLRO(dl_pagesize) - 1));
- }
-
- /* Make sure the allocation pointer is ideally aligned. */
- alloc_ptr = (void *) 0 + (((alloc_ptr - (void *) 0) + align - 1)
- & ~(align - 1));
-
- if (alloc_ptr + n >= alloc_end || n >= -(uintptr_t) alloc_ptr)
- {
- /* Insufficient space left; allocate another page. */
- caddr_t page;
- size_t nup = (n + GLRO(dl_pagesize) - 1) & ~(GLRO(dl_pagesize) - 1);
- if (__glibc_unlikely (nup == 0))
- {
- if (n)
- return NULL;
- nup = GLRO(dl_pagesize);
- }
- page = __mmap (0, nup, PROT_READ|PROT_WRITE,
- MAP_ANON|MAP_PRIVATE, -1, 0);
- if (page == MAP_FAILED)
- return NULL;
- if (page != alloc_end)
- alloc_ptr = page;
- alloc_end = page + nup;
- }
-
- alloc_last_block = (void *) alloc_ptr;
- alloc_ptr += n;
- return alloc_last_block;
+ __rtld_calloc = &__minimal_calloc;
+ __rtld_free = &__minimal_free;
+ __rtld_malloc = &__minimal_malloc;
+ __rtld_realloc = &__minimal_realloc;
}
-void * weak_function
-malloc (size_t n)
+bool
+__rtld_malloc_is_complete (void)
{
- return __libc_memalign (sizeof (double), n);
+ /* The caller assumes that there is an active malloc. */
+ assert (__rtld_malloc != NULL);
+ return __rtld_malloc != &__minimal_malloc;
}
-/* 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)
+/* 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)
{
- /* 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,
- by clearing memory in free, below.) */
- size_t bytes = nmemb * size;
-#define HALF_SIZE_T (((size_t) 1) << (8 * sizeof (size_t) / 2))
- if (__builtin_expect ((nmemb | size) >= HALF_SIZE_T, 0)
- && size != 0 && bytes / size != nmemb)
- return NULL;
+ const ElfW(Sym) *ref = NULL;
+ lookup_t result = _dl_lookup_symbol_x (name, main_map, &ref,
+ main_map->l_scope,
+ version, 0, 0, NULL);
- return malloc (bytes);
-}
+ assert (ELFW(ST_TYPE) (ref->st_info) != STT_TLS);
+ void *value = DL_SYMBOL_ADDRESS (result, ref);
-/* This will rarely be called. */
-void weak_function
-free (void *ptr)
-{
- /* We can free only the last block allocated. */
- if (ptr == alloc_last_block)
- {
- /* Since this is rare, we clear the freed block here
- so that calloc can presume malloc returns cleared memory. */
- memset (alloc_last_block, '\0', alloc_ptr - alloc_last_block);
- alloc_ptr = alloc_last_block;
- }
+ return _dl_sym_post (result, ref, value, 0, main_map);
}
-/* This is only called with the most recent block returned by malloc. */
-void * weak_function
-realloc (void *ptr, size_t n)
+void
+__rtld_malloc_init_real (struct link_map *main_map)
{
- if (ptr == NULL)
- return malloc (n);
- assert (ptr == alloc_last_block);
- size_t old_size = alloc_ptr - alloc_last_block;
- alloc_ptr = alloc_last_block;
- void *new = malloc (n);
- return new != ptr ? memcpy (new, ptr, old_size) : new;
+ /* 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 dependencies 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;
}
+
\f
/* Avoid signal frobnication in setjmp/longjmp. Keeps things smaller. */
assertion);
}
+# ifndef NO_RTLD_HIDDEN
rtld_hidden_weak (__assert_fail)
+# endif
void weak_function
__assert_perror_fail (int errnum,
__strerror_r (errnum, errbuf, sizeof errbuf));
}
+# ifndef NO_RTLD_HIDDEN
rtld_hidden_weak (__assert_perror_fail)
+# endif
#endif
\f
-unsigned long int weak_function
-__strtoul_internal (const char *nptr, char **endptr, int base, int group)
-{
- unsigned long int result = 0;
- long int sign = 1;
- unsigned max_digit;
-
- while (*nptr == ' ' || *nptr == '\t')
- ++nptr;
-
- if (*nptr == '-')
- {
- sign = -1;
- ++nptr;
- }
- else if (*nptr == '+')
- ++nptr;
-
- if (*nptr < '0' || *nptr > '9')
- {
- if (endptr != NULL)
- *endptr = (char *) nptr;
- return 0UL;
- }
-
- assert (base == 0);
- base = 10;
- max_digit = 9;
- if (*nptr == '0')
- {
- if (nptr[1] == 'x' || nptr[1] == 'X')
- {
- base = 16;
- nptr += 2;
- }
- else
- {
- base = 8;
- max_digit = 7;
- }
- }
-
- while (1)
- {
- unsigned long int digval;
- if (*nptr >= '0' && *nptr <= '0' + max_digit)
- digval = *nptr - '0';
- else if (base == 16)
- {
- if (*nptr >= 'a' && *nptr <= 'f')
- digval = *nptr - 'a' + 10;
- else if (*nptr >= 'A' && *nptr <= 'F')
- digval = *nptr - 'A' + 10;
- else
- break;
- }
- else
- break;
-
- if (result > ULONG_MAX / base
- || (result == ULONG_MAX / base && digval > ULONG_MAX % base))
- {
- errno = ERANGE;
- if (endptr != NULL)
- *endptr = (char *) nptr;
- return ULONG_MAX;
- }
- result *= base;
- result += digval;
- ++nptr;
- }
-
- if (endptr != NULL)
- *endptr = (char *) nptr;
- return result * sign;
-}
-
-
#undef _itoa
/* We always use _itoa instead of _itoa_word in ld.so since the former
also has to be present and it is never about speed when these