Blame elf/dl-minimal.c

Packit 6c4009
/* Minimal replacements for basic facilities used in the dynamic linker.
Packit 6c4009
   Copyright (C) 1995-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <limits.h>
Packit 6c4009
#include <stdio.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
#include <tls.h>
Packit 6c4009
#include <unistd.h>
Packit 6c4009
#include <sys/mman.h>
Packit 6c4009
#include <sys/param.h>
Packit 6c4009
#include <sys/types.h>
Packit 6c4009
#include <ldsodefs.h>
Packit Service 0457af
#include <dl-irel.h>
Packit Service 0457af
#include <dl-hash.h>
Packit Service 0457af
#include <dl-sym-post.h>
Packit 6c4009
#include <_itoa.h>
Packit 6c4009
#include <malloc/malloc-internal.h>
Packit 6c4009
Packit 6c4009
#include <assert.h>
Packit 6c4009
Packit Service 0457af
/* The rtld startup code calls __rtld_malloc_init_stubs after the
Packit Service 0457af
  first self-relocation to adjust the pointers to the minimal
Packit Service 0457af
  implementation below.  Before the final relocation,
Packit Service 0457af
  __rtld_malloc_init_real is called to replace the pointers with the
Packit Service 0457af
  real implementation.  */
Packit Service ca3b91
__typeof (calloc) *__rtld_calloc attribute_relro;
Packit Service ca3b91
__typeof (free) *__rtld_free attribute_relro;
Packit Service ca3b91
__typeof (malloc) *__rtld_malloc attribute_relro;
Packit Service ca3b91
__typeof (realloc) *__rtld_realloc attribute_relro;
Packit Service 0457af
Packit Service 0457af
/* Defined below.  */
Packit Service ca3b91
static __typeof (calloc) rtld_calloc;
Packit Service ca3b91
static __typeof (free) rtld_free;
Packit Service ca3b91
static __typeof (malloc) rtld_malloc;
Packit Service ca3b91
static __typeof (realloc) rtld_realloc;
Packit Service 0457af
Packit Service 0457af
void
Packit Service 0457af
__rtld_malloc_init_stubs (void)
Packit Service 0457af
{
Packit Service 0457af
  __rtld_calloc = &rtld_calloc;
Packit Service 0457af
  __rtld_free = &rtld_free;
Packit Service 0457af
  __rtld_malloc = &rtld_malloc;
Packit Service 0457af
  __rtld_realloc = &rtld_realloc;
Packit Service 0457af
}
Packit Service 0457af
Packit Service 49938c
bool
Packit Service 49938c
__rtld_malloc_is_complete (void)
Packit Service 49938c
{
Packit Service 49938c
  /* The caller assumes that there is an active malloc.  */
Packit Service 49938c
  assert (__rtld_malloc != NULL);
Packit Service 49938c
  return __rtld_malloc != &rtld_malloc;
Packit Service 49938c
}
Packit Service 49938c
Packit Service 0457af
/* Lookup NAME at VERSION in the scope of MATCH.  */
Packit Service 0457af
static void *
Packit Service 0457af
lookup_malloc_symbol (struct link_map *main_map, const char *name,
Packit Service 0457af
		      struct r_found_version *version)
Packit Service 0457af
{
Packit Service 0457af
Packit Service 0457af
  const ElfW(Sym) *ref = NULL;
Packit Service 0457af
  lookup_t result = _dl_lookup_symbol_x (name, main_map, &ref,
Packit Service 0457af
					 main_map->l_scope,
Packit Service 0457af
					 version, 0, 0, NULL);
Packit Service 0457af
Packit Service 0457af
  assert (ELFW(ST_TYPE) (ref->st_info) != STT_TLS);
Packit Service 0457af
  void *value = DL_SYMBOL_ADDRESS (result, ref);
Packit Service 0457af
Packit Service 0457af
  return _dl_sym_post (result, ref, value, 0, main_map);
Packit Service 0457af
}
Packit Service 0457af
Packit Service 0457af
void
Packit Service 0457af
__rtld_malloc_init_real (struct link_map *main_map)
Packit Service 0457af
{
Packit Service 0457af
  /* We cannot use relocations and initializers for this because the
Packit Service 0457af
     changes made by __rtld_malloc_init_stubs break REL-style
Packit Service 0457af
     (non-RELA) relocations that depend on the previous pointer
Packit Service 0457af
     contents.  Also avoid direct relocation depedencies for the
Packit Service 0457af
     malloc symbols so this function can be called before the final
Packit Service 0457af
     rtld relocation (which enables RELRO, after which the pointer
Packit Service 0457af
     variables cannot be written to).  */
Packit Service 0457af
Packit Service 0457af
  struct r_found_version version;
Packit Service 0457af
  version.name = symbol_version_string (libc, GLIBC_2_0);
Packit Service 0457af
  version.hidden = 0;
Packit Service 0457af
  version.hash = _dl_elf_hash (version.name);
Packit Service 0457af
  version.filename = NULL;
Packit Service 0457af
Packit Service 0457af
  void *new_calloc = lookup_malloc_symbol (main_map, "calloc", &version);
Packit Service 0457af
  void *new_free = lookup_malloc_symbol (main_map, "free", &version);
Packit Service 0457af
  void *new_malloc = lookup_malloc_symbol (main_map, "malloc", &version);
Packit Service 0457af
  void *new_realloc = lookup_malloc_symbol (main_map, "realloc", &version);
Packit Service 0457af
Packit Service 0457af
  /* Update the pointers in one go, so that any internal allocations
Packit Service 0457af
     performed by lookup_malloc_symbol see a consistent
Packit Service 0457af
     implementation.  */
Packit Service 0457af
  __rtld_calloc = new_calloc;
Packit Service 0457af
  __rtld_free = new_free;
Packit Service 0457af
  __rtld_malloc = new_malloc;
Packit Service 0457af
  __rtld_realloc = new_realloc;
Packit Service 0457af
}
Packit Service 0457af
Packit 6c4009
/* Minimal malloc allocator for used during initial link.  After the
Packit 6c4009
   initial link, a full malloc implementation is interposed, either
Packit 6c4009
   the one in libc, or a different one supplied by the user through
Packit 6c4009
   interposition.  */
Packit 6c4009
Packit 6c4009
static void *alloc_ptr, *alloc_end, *alloc_last_block;
Packit 6c4009
Packit 6c4009
/* Allocate an aligned memory block.  */
Packit Service 0457af
static void *
Packit Service 0457af
rtld_malloc (size_t n)
Packit 6c4009
{
Packit 6c4009
  if (alloc_end == 0)
Packit 6c4009
    {
Packit 6c4009
      /* Consume any unused space in the last page of our data segment.  */
Packit 6c4009
      extern int _end attribute_hidden;
Packit 6c4009
      alloc_ptr = &_end;
Packit 6c4009
      alloc_end = (void *) 0 + (((alloc_ptr - (void *) 0)
Packit 6c4009
				 + GLRO(dl_pagesize) - 1)
Packit 6c4009
				& ~(GLRO(dl_pagesize) - 1));
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Make sure the allocation pointer is ideally aligned.  */
Packit 6c4009
  alloc_ptr = (void *) 0 + (((alloc_ptr - (void *) 0) + MALLOC_ALIGNMENT - 1)
Packit 6c4009
			    & ~(MALLOC_ALIGNMENT - 1));
Packit 6c4009
Packit 6c4009
  if (alloc_ptr + n >= alloc_end || n >= -(uintptr_t) alloc_ptr)
Packit 6c4009
    {
Packit 6c4009
      /* Insufficient space left; allocate another page plus one extra
Packit 6c4009
	 page to reduce number of mmap calls.  */
Packit 6c4009
      caddr_t page;
Packit 6c4009
      size_t nup = (n + GLRO(dl_pagesize) - 1) & ~(GLRO(dl_pagesize) - 1);
Packit 6c4009
      if (__glibc_unlikely (nup == 0 && n != 0))
Packit 6c4009
	return NULL;
Packit 6c4009
      nup += GLRO(dl_pagesize);
Packit 6c4009
      page = __mmap (0, nup, PROT_READ|PROT_WRITE,
Packit 6c4009
		     MAP_ANON|MAP_PRIVATE, -1, 0);
Packit 6c4009
      if (page == MAP_FAILED)
Packit 6c4009
	return NULL;
Packit 6c4009
      if (page != alloc_end)
Packit 6c4009
	alloc_ptr = page;
Packit 6c4009
      alloc_end = page + nup;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  alloc_last_block = (void *) alloc_ptr;
Packit 6c4009
  alloc_ptr += n;
Packit 6c4009
  return alloc_last_block;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* We use this function occasionally since the real implementation may
Packit 6c4009
   be optimized when it can assume the memory it returns already is
Packit 6c4009
   set to NUL.  */
Packit Service 0457af
static void *
Packit Service 0457af
rtld_calloc (size_t nmemb, size_t size)
Packit 6c4009
{
Packit 6c4009
  /* New memory from the trivial malloc above is always already cleared.
Packit 6c4009
     (We make sure that's true in the rare occasion it might not be,
Packit 6c4009
     by clearing memory in free, below.)  */
Packit 6c4009
  size_t bytes = nmemb * size;
Packit 6c4009
Packit 6c4009
#define HALF_SIZE_T (((size_t) 1) << (8 * sizeof (size_t) / 2))
Packit 6c4009
  if (__builtin_expect ((nmemb | size) >= HALF_SIZE_T, 0)
Packit 6c4009
      && size != 0 && bytes / size != nmemb)
Packit 6c4009
    return NULL;
Packit 6c4009
Packit 6c4009
  return malloc (bytes);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* This will rarely be called.  */
Packit Service 0457af
void
Packit Service 0457af
rtld_free (void *ptr)
Packit 6c4009
{
Packit 6c4009
  /* We can free only the last block allocated.  */
Packit 6c4009
  if (ptr == alloc_last_block)
Packit 6c4009
    {
Packit 6c4009
      /* Since this is rare, we clear the freed block here
Packit 6c4009
	 so that calloc can presume malloc returns cleared memory.  */
Packit 6c4009
      memset (alloc_last_block, '\0', alloc_ptr - alloc_last_block);
Packit 6c4009
      alloc_ptr = alloc_last_block;
Packit 6c4009
    }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* This is only called with the most recent block returned by malloc.  */
Packit Service 0457af
void *
Packit Service 0457af
rtld_realloc (void *ptr, size_t n)
Packit 6c4009
{
Packit 6c4009
  if (ptr == NULL)
Packit 6c4009
    return malloc (n);
Packit 6c4009
  assert (ptr == alloc_last_block);
Packit 6c4009
  size_t old_size = alloc_ptr - alloc_last_block;
Packit 6c4009
  alloc_ptr = alloc_last_block;
Packit 6c4009
  void *new = malloc (n);
Packit 6c4009
  return new != ptr ? memcpy (new, ptr, old_size) : new;
Packit 6c4009
}
Packit 6c4009

Packit 6c4009
/* Avoid signal frobnication in setjmp/longjmp.  Keeps things smaller.  */
Packit 6c4009
Packit 6c4009
#include <setjmp.h>
Packit 6c4009
Packit 6c4009
int weak_function
Packit 6c4009
__sigjmp_save (sigjmp_buf env, int savemask __attribute__ ((unused)))
Packit 6c4009
{
Packit 6c4009
  env[0].__mask_was_saved = 0;
Packit 6c4009
  return 0;
Packit 6c4009
}
Packit 6c4009

Packit 6c4009
/* Define our own version of the internal function used by strerror.  We
Packit 6c4009
   only provide the messages for some common errors.  This avoids pulling
Packit 6c4009
   in the whole error list.  */
Packit 6c4009
Packit 6c4009
char * weak_function
Packit 6c4009
__strerror_r (int errnum, char *buf, size_t buflen)
Packit 6c4009
{
Packit 6c4009
  char *msg;
Packit 6c4009
Packit 6c4009
  switch (errnum)
Packit 6c4009
    {
Packit 6c4009
    case ENOMEM:
Packit 6c4009
      msg = (char *) "Cannot allocate memory";
Packit 6c4009
      break;
Packit 6c4009
    case EINVAL:
Packit 6c4009
      msg = (char *) "Invalid argument";
Packit 6c4009
      break;
Packit 6c4009
    case ENOENT:
Packit 6c4009
      msg = (char *) "No such file or directory";
Packit 6c4009
      break;
Packit 6c4009
    case EPERM:
Packit 6c4009
      msg = (char *) "Operation not permitted";
Packit 6c4009
      break;
Packit 6c4009
    case EIO:
Packit 6c4009
      msg = (char *) "Input/output error";
Packit 6c4009
      break;
Packit 6c4009
    case EACCES:
Packit 6c4009
      msg = (char *) "Permission denied";
Packit 6c4009
      break;
Packit 6c4009
    default:
Packit 6c4009
      /* No need to check buffer size, all calls in the dynamic linker
Packit 6c4009
	 provide enough space.  */
Packit 6c4009
      buf[buflen - 1] = '\0';
Packit 6c4009
      msg = _itoa (errnum, buf + buflen - 1, 10, 0);
Packit 6c4009
      msg = memcpy (msg - (sizeof ("Error ") - 1), "Error ",
Packit 6c4009
		    sizeof ("Error ") - 1);
Packit 6c4009
      break;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return msg;
Packit 6c4009
}
Packit 6c4009

Packit 6c4009
void
Packit 6c4009
__libc_fatal (const char *message)
Packit 6c4009
{
Packit 6c4009
  _dl_fatal_printf ("%s", message);
Packit 6c4009
}
Packit 6c4009
rtld_hidden_def (__libc_fatal)
Packit 6c4009
Packit 6c4009
void
Packit 6c4009
__attribute__ ((noreturn))
Packit 6c4009
__chk_fail (void)
Packit 6c4009
{
Packit 6c4009
  _exit (127);
Packit 6c4009
}
Packit 6c4009
rtld_hidden_def (__chk_fail)
Packit 6c4009
Packit 6c4009
#ifndef NDEBUG
Packit 6c4009
/* Define (weakly) our own assert failure function which doesn't use stdio.
Packit 6c4009
   If we are linked into the user program (-ldl), the normal __assert_fail
Packit 6c4009
   defn can override this one.  */
Packit 6c4009
Packit 6c4009
void weak_function
Packit 6c4009
__assert_fail (const char *assertion,
Packit 6c4009
	       const char *file, unsigned int line, const char *function)
Packit 6c4009
{
Packit 6c4009
  _dl_fatal_printf ("\
Packit 6c4009
Inconsistency detected by ld.so: %s: %u: %s%sAssertion `%s' failed!\n",
Packit 6c4009
		    file, line, function ?: "", function ? ": " : "",
Packit 6c4009
		    assertion);
Packit 6c4009
Packit 6c4009
}
Packit 6c4009
rtld_hidden_weak (__assert_fail)
Packit 6c4009
Packit 6c4009
void weak_function
Packit 6c4009
__assert_perror_fail (int errnum,
Packit 6c4009
		      const char *file, unsigned int line,
Packit 6c4009
		      const char *function)
Packit 6c4009
{
Packit 6c4009
  char errbuf[400];
Packit 6c4009
  _dl_fatal_printf ("\
Packit 6c4009
Inconsistency detected by ld.so: %s: %u: %s%sUnexpected error: %s.\n",
Packit 6c4009
		    file, line, function ?: "", function ? ": " : "",
Packit 6c4009
		    __strerror_r (errnum, errbuf, sizeof errbuf));
Packit 6c4009
Packit 6c4009
}
Packit 6c4009
rtld_hidden_weak (__assert_perror_fail)
Packit 6c4009
#endif
Packit 6c4009

Packit 6c4009
#undef _itoa
Packit 6c4009
/* We always use _itoa instead of _itoa_word in ld.so since the former
Packit 6c4009
   also has to be present and it is never about speed when these
Packit 6c4009
   functions are used.  */
Packit 6c4009
char *
Packit 6c4009
_itoa (unsigned long long int value, char *buflim, unsigned int base,
Packit 6c4009
       int upper_case)
Packit 6c4009
{
Packit 6c4009
  assert (! upper_case);
Packit 6c4009
Packit 6c4009
  do
Packit 6c4009
    *--buflim = _itoa_lower_digits[value % base];
Packit 6c4009
  while ((value /= base) != 0);
Packit 6c4009
Packit 6c4009
  return buflim;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* The '_itoa_lower_digits' variable in libc.so is able to handle bases
Packit 6c4009
   up to 36.  We don't need this here.  */
Packit 6c4009
const char _itoa_lower_digits[16] = "0123456789abcdef";
Packit 6c4009
rtld_hidden_data_def (_itoa_lower_digits)
Packit 6c4009

Packit 6c4009
/* The following is not a complete strsep implementation.  It cannot
Packit 6c4009
   handle empty delimiter strings.  But this isn't necessary for the
Packit 6c4009
   execution of ld.so.  */
Packit 6c4009
#undef strsep
Packit 6c4009
#undef __strsep
Packit 6c4009
char *
Packit 6c4009
__strsep (char **stringp, const char *delim)
Packit 6c4009
{
Packit 6c4009
  char *begin;
Packit 6c4009
Packit 6c4009
  assert (delim[0] != '\0');
Packit 6c4009
Packit 6c4009
  begin = *stringp;
Packit 6c4009
  if (begin != NULL)
Packit 6c4009
    {
Packit 6c4009
      char *end = begin;
Packit 6c4009
Packit 6c4009
      while (*end != '\0' || (end = NULL))
Packit 6c4009
	{
Packit 6c4009
	  const char *dp = delim;
Packit 6c4009
Packit 6c4009
	  do
Packit 6c4009
	    if (*dp == *end)
Packit 6c4009
	      break;
Packit 6c4009
	  while (*++dp != '\0');
Packit 6c4009
Packit 6c4009
	  if (*dp != '\0')
Packit 6c4009
	    {
Packit 6c4009
	      *end++ = '\0';
Packit 6c4009
	      break;
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  ++end;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      *stringp = end;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return begin;
Packit 6c4009
}
Packit 6c4009
weak_alias (__strsep, strsep)
Packit 6c4009
strong_alias (__strsep, __strsep_g)