Blame elf/dl-reloc.c

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