Blame csu/libc-tls.c

Packit 6c4009
/* Initialization code for TLS in statically linked application.
Packit 6c4009
   Copyright (C) 2002-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 <startup.h>
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <ldsodefs.h>
Packit 6c4009
#include <tls.h>
Packit 6c4009
#include <unistd.h>
Packit 6c4009
#include <stdio.h>
Packit 6c4009
#include <sys/param.h>
Packit 6c4009
Packit 6c4009
Packit 6c4009
#ifdef SHARED
Packit 6c4009
 #error makefile bug, this file is for static only
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
dtv_t _dl_static_dtv[2 + TLS_SLOTINFO_SURPLUS];
Packit 6c4009
Packit 6c4009
Packit 6c4009
static struct
Packit 6c4009
{
Packit 6c4009
  struct dtv_slotinfo_list si;
Packit 6c4009
  /* The dtv_slotinfo_list data structure does not include the actual
Packit 6c4009
     information since it is defined as an array of size zero.  We define
Packit 6c4009
     here the necessary entries.  Note that it is not important whether
Packit 6c4009
     there is padding or not since we will always access the information
Packit 6c4009
     through the 'si' element.  */
Packit 6c4009
  struct dtv_slotinfo info[2 + TLS_SLOTINFO_SURPLUS];
Packit 6c4009
} static_slotinfo;
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Highest dtv index currently needed.  */
Packit 6c4009
size_t _dl_tls_max_dtv_idx;
Packit 6c4009
/* Flag signalling whether there are gaps in the module ID allocation.  */
Packit 6c4009
bool _dl_tls_dtv_gaps;
Packit 6c4009
/* Information about the dtv slots.  */
Packit 6c4009
struct dtv_slotinfo_list *_dl_tls_dtv_slotinfo_list;
Packit 6c4009
/* Number of modules in the static TLS block.  */
Packit 6c4009
size_t _dl_tls_static_nelem;
Packit Service 3b0880
/* Size of the static TLS block.  Giving this initialized value
Packit Service 3b0880
   preallocates some surplus bytes in the static TLS area.  */
Packit Service 3b0880
size_t _dl_tls_static_size = 2048;
Packit 6c4009
/* Size actually allocated in the static TLS block.  */
Packit 6c4009
size_t _dl_tls_static_used;
Packit 6c4009
/* Alignment requirement of the static TLS block.  */
Packit 6c4009
size_t _dl_tls_static_align;
Packit 6c4009
Packit 6c4009
/* Generation counter for the dtv.  */
Packit 6c4009
size_t _dl_tls_generation;
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Additional definitions needed by TLS initialization.  */
Packit 6c4009
#ifdef TLS_INIT_HELPER
Packit 6c4009
TLS_INIT_HELPER
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
init_slotinfo (void)
Packit 6c4009
{
Packit 6c4009
  /* Create the slotinfo list.  */
Packit 6c4009
  static_slotinfo.si.len = (((char *) (&static_slotinfo + 1)
Packit 6c4009
			     - (char *) &static_slotinfo.si.slotinfo[0])
Packit 6c4009
			    / sizeof static_slotinfo.si.slotinfo[0]);
Packit 6c4009
  // static_slotinfo.si.next = NULL;	already zero
Packit 6c4009
Packit 6c4009
  /* The slotinfo list.  Will be extended by the code doing dynamic
Packit 6c4009
     linking.  */
Packit 6c4009
  GL(dl_tls_max_dtv_idx) = 1;
Packit 6c4009
  GL(dl_tls_dtv_slotinfo_list) = &static_slotinfo.si;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
init_static_tls (size_t memsz, size_t align)
Packit 6c4009
{
Packit Service 3b0880
  /* That is the size of the TLS memory for this object.  The initialized
Packit Service 3b0880
     value of _dl_tls_static_size is provided by dl-open.c to request some
Packit Service 3b0880
     surplus that permits dynamic loading of modules with IE-model TLS.  */
Packit Service 3b0880
  GL(dl_tls_static_size) = roundup (memsz + GL(dl_tls_static_size),
Packit 6c4009
				    TLS_TCB_ALIGN);
Packit 6c4009
#if TLS_TCB_AT_TP
Packit 6c4009
  GL(dl_tls_static_size) += TLS_TCB_SIZE;
Packit 6c4009
#endif
Packit 6c4009
  GL(dl_tls_static_used) = memsz;
Packit 6c4009
  /* The alignment requirement for the static TLS block.  */
Packit 6c4009
  GL(dl_tls_static_align) = align;
Packit 6c4009
  /* Number of elements in the static TLS block.  */
Packit 6c4009
  GL(dl_tls_static_nelem) = GL(dl_tls_max_dtv_idx);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
void
Packit 6c4009
__libc_setup_tls (void)
Packit 6c4009
{
Packit 6c4009
  void *tlsblock;
Packit 6c4009
  size_t memsz = 0;
Packit 6c4009
  size_t filesz = 0;
Packit 6c4009
  void *initimage = NULL;
Packit 6c4009
  size_t align = 0;
Packit 6c4009
  size_t max_align = TCB_ALIGNMENT;
Packit 6c4009
  size_t tcb_offset;
Packit 6c4009
  const ElfW(Phdr) *phdr;
Packit 6c4009
Packit 6c4009
  struct link_map *main_map = GL(dl_ns)[LM_ID_BASE]._ns_loaded;
Packit 6c4009
Packit 6c4009
  /* Look through the TLS segment if there is any.  */
Packit 6c4009
  if (_dl_phdr != NULL)
Packit 6c4009
    for (phdr = _dl_phdr; phdr < &_dl_phdr[_dl_phnum]; ++phdr)
Packit 6c4009
      if (phdr->p_type == PT_TLS)
Packit 6c4009
	{
Packit 6c4009
	  /* Remember the values we need.  */
Packit 6c4009
	  memsz = phdr->p_memsz;
Packit 6c4009
	  filesz = phdr->p_filesz;
Packit 6c4009
	  initimage = (void *) phdr->p_vaddr + main_map->l_addr;
Packit 6c4009
	  align = phdr->p_align;
Packit 6c4009
	  if (phdr->p_align > max_align)
Packit 6c4009
	    max_align = phdr->p_align;
Packit 6c4009
	  break;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
  /* We have to set up the TCB block which also (possibly) contains
Packit 6c4009
     'errno'.  Therefore we avoid 'malloc' which might touch 'errno'.
Packit 6c4009
     Instead we use 'sbrk' which would only uses 'errno' if it fails.
Packit 6c4009
     In this case we are right away out of memory and the user gets
Packit Service 3b0880
     what she/he deserves.
Packit Service 3b0880
Packit Service 3b0880
     The initialized value of _dl_tls_static_size is provided by dl-open.c
Packit Service 3b0880
     to request some surplus that permits dynamic loading of modules with
Packit Service 3b0880
     IE-model TLS.  */
Packit 6c4009
#if TLS_TCB_AT_TP
Packit 6c4009
  /* Align the TCB offset to the maximum alignment, as
Packit 6c4009
     _dl_allocate_tls_storage (in elf/dl-tls.c) does using __libc_memalign
Packit 6c4009
     and dl_tls_static_align.  */
Packit Service 3b0880
  tcb_offset = roundup (memsz + GL(dl_tls_static_size), max_align);
Packit 6c4009
  tlsblock = __sbrk (tcb_offset + TLS_INIT_TCB_SIZE + max_align);
Packit 6c4009
#elif TLS_DTV_AT_TP
Packit 6c4009
  tcb_offset = roundup (TLS_INIT_TCB_SIZE, align ?: 1);
Packit 6c4009
  tlsblock = __sbrk (tcb_offset + memsz + max_align
Packit Service 3b0880
		     + TLS_PRE_TCB_SIZE + GL(dl_tls_static_size));
Packit 6c4009
  tlsblock += TLS_PRE_TCB_SIZE;
Packit 6c4009
#else
Packit 6c4009
  /* In case a model with a different layout for the TCB and DTV
Packit 6c4009
     is defined add another #elif here and in the following #ifs.  */
Packit 6c4009
# error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
  /* Align the TLS block.  */
Packit 6c4009
  tlsblock = (void *) (((uintptr_t) tlsblock + max_align - 1)
Packit 6c4009
		       & ~(max_align - 1));
Packit 6c4009
Packit 6c4009
  /* Initialize the dtv.  [0] is the length, [1] the generation counter.  */
Packit 6c4009
  _dl_static_dtv[0].counter = (sizeof (_dl_static_dtv) / sizeof (_dl_static_dtv[0])) - 2;
Packit 6c4009
  // _dl_static_dtv[1].counter = 0;		would be needed if not already done
Packit 6c4009
Packit 6c4009
  /* Initialize the TLS block.  */
Packit 6c4009
#if TLS_TCB_AT_TP
Packit 6c4009
  _dl_static_dtv[2].pointer.val = ((char *) tlsblock + tcb_offset
Packit 6c4009
			       - roundup (memsz, align ?: 1));
Packit 6c4009
  main_map->l_tls_offset = roundup (memsz, align ?: 1);
Packit 6c4009
#elif TLS_DTV_AT_TP
Packit 6c4009
  _dl_static_dtv[2].pointer.val = (char *) tlsblock + tcb_offset;
Packit 6c4009
  main_map->l_tls_offset = tcb_offset;
Packit 6c4009
#else
Packit 6c4009
# error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
Packit 6c4009
#endif
Packit 6c4009
  _dl_static_dtv[2].pointer.to_free = NULL;
Packit 6c4009
  /* sbrk gives us zero'd memory, so we don't need to clear the remainder.  */
Packit 6c4009
  memcpy (_dl_static_dtv[2].pointer.val, initimage, filesz);
Packit 6c4009
Packit 6c4009
  /* Install the pointer to the dtv.  */
Packit 6c4009
Packit 6c4009
  /* Initialize the thread pointer.  */
Packit 6c4009
#if TLS_TCB_AT_TP
Packit 6c4009
  INSTALL_DTV ((char *) tlsblock + tcb_offset, _dl_static_dtv);
Packit 6c4009
Packit 6c4009
  const char *lossage = TLS_INIT_TP ((char *) tlsblock + tcb_offset);
Packit 6c4009
#elif TLS_DTV_AT_TP
Packit 6c4009
  INSTALL_DTV (tlsblock, _dl_static_dtv);
Packit 6c4009
  const char *lossage = TLS_INIT_TP (tlsblock);
Packit 6c4009
#else
Packit 6c4009
# error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
Packit 6c4009
#endif
Packit 6c4009
  if (__builtin_expect (lossage != NULL, 0))
Packit 6c4009
    _startup_fatal (lossage);
Packit 6c4009
Packit 6c4009
  /* Update the executable's link map with enough information to make
Packit 6c4009
     the TLS routines happy.  */
Packit 6c4009
  main_map->l_tls_align = align;
Packit 6c4009
  main_map->l_tls_blocksize = memsz;
Packit 6c4009
  main_map->l_tls_initimage = initimage;
Packit 6c4009
  main_map->l_tls_initimage_size = filesz;
Packit 6c4009
  main_map->l_tls_modid = 1;
Packit 6c4009
Packit 6c4009
  init_slotinfo ();
Packit 6c4009
  // static_slotinfo.si.slotinfo[1].gen = 0; already zero
Packit 6c4009
  static_slotinfo.si.slotinfo[1].map = main_map;
Packit 6c4009
Packit 6c4009
  memsz = roundup (memsz, align ?: 1);
Packit 6c4009
Packit 6c4009
#if TLS_DTV_AT_TP
Packit 6c4009
  memsz += tcb_offset;
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
  init_static_tls (memsz, MAX (TLS_TCB_ALIGN, max_align));
Packit 6c4009
}