Blame elf/dl-reloc.c

Packit 6c4009
/* Relocate a shared object and resolve its references to other loaded objects.
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 <libintl.h>
Packit 6c4009
#include <stdlib.h>
Packit 6c4009
#include <unistd.h>
Packit 6c4009
#include <ldsodefs.h>
Packit 6c4009
#include <sys/mman.h>
Packit 6c4009
#include <sys/param.h>
Packit 6c4009
#include <sys/types.h>
Packit 6c4009
#include <_itoa.h>
Packit 6c4009
#include <libc-pointer-arith.h>
Packit 6c4009
#include "dynamic-link.h"
Packit 6c4009
Packit 6c4009
/* Statistics function.  */
Packit 6c4009
#ifdef SHARED
Packit 6c4009
# define bump_num_cache_relocations() ++GL(dl_num_cache_relocations)
Packit 6c4009
#else
Packit 6c4009
# define bump_num_cache_relocations() ((void) 0)
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* We are trying to perform a static TLS relocation in MAP, but it was
Packit 6c4009
   dynamically loaded.  This can only work if there is enough surplus in
Packit 6c4009
   the static TLS area already allocated for each running thread.  If this
Packit Service d640cf
   object's TLS segment is too big to fit, we fail with -1.  If it fits,
Packit Service d640cf
   we set MAP->l_tls_offset and return 0.
Packit Service d640cf
   A portion of the surplus static TLS can be optionally used to optimize
Packit Service d640cf
   dynamic TLS access (with TLSDESC or powerpc TLS optimizations).
Packit Service d640cf
   If OPTIONAL is true then TLS is allocated for such optimization and
Packit Service d640cf
   the caller must have a fallback in case the optional portion of surplus
Packit Service d640cf
   TLS runs out.  If OPTIONAL is false then the entire surplus TLS area is
Packit Service d640cf
   considered and the allocation only fails if that runs out.  */
Packit 6c4009
int
Packit Service d640cf
_dl_try_allocate_static_tls (struct link_map *map, bool optional)
Packit 6c4009
{
Packit 6c4009
  /* If we've already used the variable with dynamic access, or if the
Packit 6c4009
     alignment requirements are too high, fail.  */
Packit 6c4009
  if (map->l_tls_offset == FORCED_DYNAMIC_TLS_OFFSET
Packit 6c4009
      || map->l_tls_align > GL(dl_tls_static_align))
Packit 6c4009
    {
Packit 6c4009
    fail:
Packit 6c4009
      return -1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
#if TLS_TCB_AT_TP
Packit 6c4009
  size_t freebytes = GL(dl_tls_static_size) - GL(dl_tls_static_used);
Packit 6c4009
  if (freebytes < TLS_TCB_SIZE)
Packit 6c4009
    goto fail;
Packit 6c4009
  freebytes -= TLS_TCB_SIZE;
Packit 6c4009
Packit 6c4009
  size_t blsize = map->l_tls_blocksize + map->l_tls_firstbyte_offset;
Packit 6c4009
  if (freebytes < blsize)
Packit 6c4009
    goto fail;
Packit 6c4009
Packit 6c4009
  size_t n = (freebytes - blsize) / map->l_tls_align;
Packit 6c4009
Packit Service d640cf
  /* Account optional static TLS surplus usage.  */
Packit Service d640cf
  size_t use = freebytes - n * map->l_tls_align - map->l_tls_firstbyte_offset;
Packit Service d640cf
  if (optional && use > GL(dl_tls_static_optional))
Packit Service d640cf
    goto fail;
Packit Service d640cf
  else if (optional)
Packit Service d640cf
    GL(dl_tls_static_optional) -= use;
Packit Service d640cf
Packit Service d640cf
  size_t offset = GL(dl_tls_static_used) + use;
Packit 6c4009
Packit 6c4009
  map->l_tls_offset = GL(dl_tls_static_used) = offset;
Packit 6c4009
#elif TLS_DTV_AT_TP
Packit 6c4009
  /* dl_tls_static_used includes the TCB at the beginning.  */
Packit 6c4009
  size_t offset = (ALIGN_UP(GL(dl_tls_static_used)
Packit 6c4009
			    - map->l_tls_firstbyte_offset,
Packit 6c4009
			    map->l_tls_align)
Packit 6c4009
		   + map->l_tls_firstbyte_offset);
Packit 6c4009
  size_t used = offset + map->l_tls_blocksize;
Packit 6c4009
Packit 6c4009
  if (used > GL(dl_tls_static_size))
Packit 6c4009
    goto fail;
Packit 6c4009
Packit Service d640cf
  /* Account optional static TLS surplus usage.  */
Packit Service d640cf
  size_t use = used - GL(dl_tls_static_used);
Packit Service d640cf
  if (optional && use > GL(dl_tls_static_optional))
Packit Service d640cf
    goto fail;
Packit Service d640cf
  else if (optional)
Packit Service d640cf
    GL(dl_tls_static_optional) -= use;
Packit Service d640cf
Packit 6c4009
  map->l_tls_offset = offset;
Packit 6c4009
  map->l_tls_firstbyte_offset = GL(dl_tls_static_used);
Packit 6c4009
  GL(dl_tls_static_used) = used;
Packit 6c4009
#else
Packit 6c4009
# error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
  /* If the object is not yet relocated we cannot initialize the
Packit 6c4009
     static TLS region.  Delay it.  */
Packit 6c4009
  if (map->l_real->l_relocated)
Packit 6c4009
    {
Packit 6c4009
#ifdef SHARED
Packit 6c4009
      if (__builtin_expect (THREAD_DTV()[0].counter != GL(dl_tls_generation),
Packit 6c4009
			    0))
Packit 6c4009
	/* Update the slot information data for at least the generation of
Packit 6c4009
	   the DSO we are allocating data for.  */
Packit 6c4009
	(void) _dl_update_slotinfo (map->l_tls_modid);
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
      GL(dl_init_static_tls) (map);
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    map->l_need_tls_init = 1;
Packit 6c4009
Packit 6c4009
  return 0;
Packit 6c4009
}
Packit 6c4009
Packit Service d640cf
/* This function intentionally does not return any value but signals error
Packit Service d640cf
   directly, as static TLS should be rare and code handling it should
Packit Service d640cf
   not be inlined as much as possible.  */
Packit 6c4009
void
Packit 6c4009
__attribute_noinline__
Packit 6c4009
_dl_allocate_static_tls (struct link_map *map)
Packit 6c4009
{
Packit 6c4009
  if (map->l_tls_offset == FORCED_DYNAMIC_TLS_OFFSET
Packit Service d640cf
      || _dl_try_allocate_static_tls (map, false))
Packit 6c4009
    {
Packit 6c4009
      _dl_signal_error (0, map->l_name, NULL, N_("\
Packit 6c4009
cannot allocate memory in static TLS block"));
Packit 6c4009
    }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Initialize static TLS area and DTV for current (only) thread.
Packit 6c4009
   libpthread implementations should provide their own hook
Packit 6c4009
   to handle all threads.  */
Packit 6c4009
void
Packit 6c4009
_dl_nothread_init_static_tls (struct link_map *map)
Packit 6c4009
{
Packit 6c4009
#if TLS_TCB_AT_TP
Packit 6c4009
  void *dest = (char *) THREAD_SELF - map->l_tls_offset;
Packit 6c4009
#elif TLS_DTV_AT_TP
Packit 6c4009
  void *dest = (char *) THREAD_SELF + map->l_tls_offset + TLS_PRE_TCB_SIZE;
Packit 6c4009
#else
Packit 6c4009
# error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
  /* Initialize the memory.  */
Packit 6c4009
  memset (__mempcpy (dest, map->l_tls_initimage, map->l_tls_initimage_size),
Packit 6c4009
	  '\0', map->l_tls_blocksize - map->l_tls_initimage_size);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
void
Packit 6c4009
_dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[],
Packit 6c4009
		     int reloc_mode, int consider_profiling)
Packit 6c4009
{
Packit 6c4009
  struct textrels
Packit 6c4009
  {
Packit 6c4009
    caddr_t start;
Packit 6c4009
    size_t len;
Packit 6c4009
    int prot;
Packit 6c4009
    struct textrels *next;
Packit 6c4009
  } *textrels = NULL;
Packit 6c4009
  /* Initialize it to make the compiler happy.  */
Packit 6c4009
  const char *errstring = NULL;
Packit 6c4009
  int lazy = reloc_mode & RTLD_LAZY;
Packit 6c4009
  int skip_ifunc = reloc_mode & __RTLD_NOIFUNC;
Packit 6c4009
Packit 6c4009
#ifdef SHARED
Packit 6c4009
  /* If we are auditing, install the same handlers we need for profiling.  */
Packit 6c4009
  if ((reloc_mode & __RTLD_AUDIT) == 0)
Packit 6c4009
    consider_profiling |= GLRO(dl_audit) != NULL;
Packit 6c4009
#elif defined PROF
Packit 6c4009
  /* Never use dynamic linker profiling for gprof profiling code.  */
Packit 6c4009
# define consider_profiling 0
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
  if (l->l_relocated)
Packit 6c4009
    return;
Packit 6c4009
Packit 6c4009
  /* If DT_BIND_NOW is set relocate all references in this object.  We
Packit 6c4009
     do not do this if we are profiling, of course.  */
Packit 6c4009
  // XXX Correct for auditing?
Packit 6c4009
  if (!consider_profiling
Packit 6c4009
      && __builtin_expect (l->l_info[DT_BIND_NOW] != NULL, 0))
Packit 6c4009
    lazy = 0;
Packit 6c4009
Packit 6c4009
  if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_RELOC))
Packit 6c4009
    _dl_debug_printf ("\nrelocation processing: %s%s\n",
Packit 6c4009
		      DSO_FILENAME (l->l_name), lazy ? " (lazy)" : "");
Packit 6c4009
Packit 6c4009
  /* DT_TEXTREL is now in level 2 and might phase out at some time.
Packit 6c4009
     But we rewrite the DT_FLAGS entry to a DT_TEXTREL entry to make
Packit 6c4009
     testing easier and therefore it will be available at all time.  */
Packit 6c4009
  if (__glibc_unlikely (l->l_info[DT_TEXTREL] != NULL))
Packit 6c4009
    {
Packit 6c4009
      /* Bletch.  We must make read-only segments writable
Packit 6c4009
	 long enough to relocate them.  */
Packit 6c4009
      const ElfW(Phdr) *ph;
Packit 6c4009
      for (ph = l->l_phdr; ph < &l->l_phdr[l->l_phnum]; ++ph)
Packit 6c4009
	if (ph->p_type == PT_LOAD && (ph->p_flags & PF_W) == 0)
Packit 6c4009
	  {
Packit 6c4009
	    struct textrels *newp;
Packit 6c4009
Packit 6c4009
	    newp = (struct textrels *) alloca (sizeof (*newp));
Packit 6c4009
	    newp->len = ALIGN_UP (ph->p_vaddr + ph->p_memsz, GLRO(dl_pagesize))
Packit 6c4009
			- ALIGN_DOWN (ph->p_vaddr, GLRO(dl_pagesize));
Packit 6c4009
	    newp->start = PTR_ALIGN_DOWN (ph->p_vaddr, GLRO(dl_pagesize))
Packit 6c4009
			  + (caddr_t) l->l_addr;
Packit 6c4009
Packit 6c4009
	    if (__mprotect (newp->start, newp->len, PROT_READ|PROT_WRITE) < 0)
Packit 6c4009
	      {
Packit 6c4009
		errstring = N_("cannot make segment writable for relocation");
Packit 6c4009
	      call_error:
Packit 6c4009
		_dl_signal_error (errno, l->l_name, NULL, errstring);
Packit 6c4009
	      }
Packit 6c4009
Packit 6c4009
#if (PF_R | PF_W | PF_X) == 7 && (PROT_READ | PROT_WRITE | PROT_EXEC) == 7
Packit 6c4009
	    newp->prot = (PF_TO_PROT
Packit 6c4009
			  >> ((ph->p_flags & (PF_R | PF_W | PF_X)) * 4)) & 0xf;
Packit 6c4009
#else
Packit 6c4009
	    newp->prot = 0;
Packit 6c4009
	    if (ph->p_flags & PF_R)
Packit 6c4009
	      newp->prot |= PROT_READ;
Packit 6c4009
	    if (ph->p_flags & PF_W)
Packit 6c4009
	      newp->prot |= PROT_WRITE;
Packit 6c4009
	    if (ph->p_flags & PF_X)
Packit 6c4009
	      newp->prot |= PROT_EXEC;
Packit 6c4009
#endif
Packit 6c4009
	    newp->next = textrels;
Packit 6c4009
	    textrels = newp;
Packit 6c4009
	  }
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  {
Packit 6c4009
    /* Do the actual relocation of the object's GOT and other data.  */
Packit 6c4009
Packit 6c4009
    /* String table object symbols.  */
Packit 6c4009
    const char *strtab = (const void *) D_PTR (l, l_info[DT_STRTAB]);
Packit 6c4009
Packit 6c4009
    /* This macro is used as a callback from the ELF_DYNAMIC_RELOCATE code.  */
Packit 6c4009
#define RESOLVE_MAP(ref, version, r_type) \
Packit 6c4009
    ((ELFW(ST_BIND) ((*ref)->st_info) != STB_LOCAL			      \
Packit 6c4009
      && __glibc_likely (!dl_symbol_visibility_binds_local_p (*ref)))	      \
Packit 6c4009
     ? ((__builtin_expect ((*ref) == l->l_lookup_cache.sym, 0)		      \
Packit 6c4009
	 && elf_machine_type_class (r_type) == l->l_lookup_cache.type_class)  \
Packit 6c4009
	? (bump_num_cache_relocations (),				      \
Packit 6c4009
	   (*ref) = l->l_lookup_cache.ret,				      \
Packit 6c4009
	   l->l_lookup_cache.value)					      \
Packit 6c4009
	: ({ lookup_t _lr;						      \
Packit 6c4009
	     int _tc = elf_machine_type_class (r_type);			      \
Packit 6c4009
	     l->l_lookup_cache.type_class = _tc;			      \
Packit 6c4009
	     l->l_lookup_cache.sym = (*ref);				      \
Packit 6c4009
	     const struct r_found_version *v = NULL;			      \
Packit 6c4009
	     if ((version) != NULL && (version)->hash != 0)		      \
Packit 6c4009
	       v = (version);						      \
Packit 6c4009
	     _lr = _dl_lookup_symbol_x (strtab + (*ref)->st_name, l, (ref),   \
Packit 6c4009
					scope, v, _tc,			      \
Packit Service 0b6d34
					DL_LOOKUP_ADD_DEPENDENCY	      \
Packit Service 0b6d34
					| DL_LOOKUP_FOR_RELOCATE, NULL);      \
Packit 6c4009
	     l->l_lookup_cache.ret = (*ref);				      \
Packit 6c4009
	     l->l_lookup_cache.value = _lr; }))				      \
Packit 6c4009
     : l)
Packit 6c4009
Packit 6c4009
#include "dynamic-link.h"
Packit 6c4009
Packit 6c4009
    ELF_DYNAMIC_RELOCATE (l, lazy, consider_profiling, skip_ifunc);
Packit 6c4009
Packit 6c4009
#ifndef PROF
Packit 6c4009
    if (__glibc_unlikely (consider_profiling)
Packit 6c4009
	&& l->l_info[DT_PLTRELSZ] != NULL)
Packit 6c4009
      {
Packit 6c4009
	/* Allocate the array which will contain the already found
Packit 6c4009
	   relocations.  If the shared object lacks a PLT (for example
Packit 6c4009
	   if it only contains lead function) the l_info[DT_PLTRELSZ]
Packit 6c4009
	   will be NULL.  */
Packit 6c4009
	size_t sizeofrel = l->l_info[DT_PLTREL]->d_un.d_val == DT_RELA
Packit 6c4009
			   ? sizeof (ElfW(Rela))
Packit 6c4009
			   : sizeof (ElfW(Rel));
Packit 6c4009
	size_t relcount = l->l_info[DT_PLTRELSZ]->d_un.d_val / sizeofrel;
Packit 6c4009
	l->l_reloc_result = calloc (sizeof (l->l_reloc_result[0]), relcount);
Packit 6c4009
Packit 6c4009
	if (l->l_reloc_result == NULL)
Packit 6c4009
	  {
Packit 6c4009
	    errstring = N_("\
Packit 6c4009
%s: out of memory to store relocation results for %s\n");
Packit 6c4009
	    _dl_fatal_printf (errstring, RTLD_PROGNAME, l->l_name);
Packit 6c4009
	  }
Packit 6c4009
      }
Packit 6c4009
#endif
Packit 6c4009
  }
Packit 6c4009
Packit 6c4009
  /* Mark the object so we know this work has been done.  */
Packit 6c4009
  l->l_relocated = 1;
Packit 6c4009
Packit 6c4009
  /* Undo the segment protection changes.  */
Packit 6c4009
  while (__builtin_expect (textrels != NULL, 0))
Packit 6c4009
    {
Packit 6c4009
      if (__mprotect (textrels->start, textrels->len, textrels->prot) < 0)
Packit 6c4009
	{
Packit 6c4009
	  errstring = N_("cannot restore segment prot after reloc");
Packit 6c4009
	  goto call_error;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
#ifdef CLEAR_CACHE
Packit 6c4009
      CLEAR_CACHE (textrels->start, textrels->start + textrels->len);
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
      textrels = textrels->next;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* In case we can protect the data now that the relocations are
Packit 6c4009
     done, do it.  */
Packit 6c4009
  if (l->l_relro_size != 0)
Packit 6c4009
    _dl_protect_relro (l);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
void
Packit 6c4009
_dl_protect_relro (struct link_map *l)
Packit 6c4009
{
Packit 6c4009
  ElfW(Addr) start = ALIGN_DOWN((l->l_addr
Packit 6c4009
				 + l->l_relro_addr),
Packit 6c4009
				GLRO(dl_pagesize));
Packit 6c4009
  ElfW(Addr) end = ALIGN_DOWN((l->l_addr
Packit 6c4009
			       + l->l_relro_addr
Packit 6c4009
			       + l->l_relro_size),
Packit 6c4009
			      GLRO(dl_pagesize));
Packit 6c4009
  if (start != end
Packit 6c4009
      && __mprotect ((void *) start, end - start, PROT_READ) < 0)
Packit 6c4009
    {
Packit 6c4009
      static const char errstring[] = N_("\
Packit 6c4009
cannot apply additional memory protection after relocation");
Packit 6c4009
      _dl_signal_error (errno, l->l_name, NULL, errstring);
Packit 6c4009
    }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
void
Packit 6c4009
__attribute_noinline__
Packit 6c4009
_dl_reloc_bad_type (struct link_map *map, unsigned int type, int plt)
Packit 6c4009
{
Packit 6c4009
#define DIGIT(b)	_itoa_lower_digits[(b) & 0xf];
Packit 6c4009
Packit 6c4009
  /* XXX We cannot translate these messages.  */
Packit 6c4009
  static const char msg[2][32
Packit 6c4009
#if __ELF_NATIVE_CLASS == 64
Packit 6c4009
			   + 6
Packit 6c4009
#endif
Packit 6c4009
  ] = { "unexpected reloc type 0x",
Packit 6c4009
	"unexpected PLT reloc type 0x" };
Packit 6c4009
  char msgbuf[sizeof (msg[0])];
Packit 6c4009
  char *cp;
Packit 6c4009
Packit 6c4009
  cp = __stpcpy (msgbuf, msg[plt]);
Packit 6c4009
#if __ELF_NATIVE_CLASS == 64
Packit 6c4009
  if (__builtin_expect(type > 0xff, 0))
Packit 6c4009
    {
Packit 6c4009
      *cp++ = DIGIT (type >> 28);
Packit 6c4009
      *cp++ = DIGIT (type >> 24);
Packit 6c4009
      *cp++ = DIGIT (type >> 20);
Packit 6c4009
      *cp++ = DIGIT (type >> 16);
Packit 6c4009
      *cp++ = DIGIT (type >> 12);
Packit 6c4009
      *cp++ = DIGIT (type >> 8);
Packit 6c4009
    }
Packit 6c4009
#endif
Packit 6c4009
  *cp++ = DIGIT (type >> 4);
Packit 6c4009
  *cp++ = DIGIT (type);
Packit 6c4009
  *cp = '\0';
Packit 6c4009
Packit 6c4009
  _dl_signal_error (0, map->l_name, NULL, msgbuf);
Packit 6c4009
}