Blame sysdeps/x86_64/nptl/tls.h

Packit 6c4009
/* Definition for thread-local data handling.  nptl/x86_64 version.
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
#ifndef _TLS_H
Packit 6c4009
#define _TLS_H	1
Packit 6c4009
Packit 6c4009
#ifndef __ASSEMBLER__
Packit 6c4009
# include <asm/prctl.h>	/* For ARCH_SET_FS.  */
Packit 6c4009
# include <stdbool.h>
Packit 6c4009
# include <stddef.h>
Packit 6c4009
# include <stdint.h>
Packit 6c4009
# include <stdlib.h>
Packit 6c4009
# include <sysdep.h>
Packit 6c4009
# include <libc-pointer-arith.h> /* For cast_to_integer.  */
Packit 6c4009
# include <kernel-features.h>
Packit 6c4009
# include <dl-dtv.h>
Packit 6c4009
Packit 6c4009
/* Replacement type for __m128 since this file is included by ld.so,
Packit 6c4009
   which is compiled with -mno-sse.  It must not change the alignment
Packit 6c4009
   of rtld_savespace_sse.  */
Packit 6c4009
typedef struct
Packit 6c4009
{
Packit 6c4009
  int i[4];
Packit 6c4009
} __128bits;
Packit 6c4009
Packit 6c4009
Packit 6c4009
typedef struct
Packit 6c4009
{
Packit 6c4009
  void *tcb;		/* Pointer to the TCB.  Not necessarily the
Packit 6c4009
			   thread descriptor used by libpthread.  */
Packit 6c4009
  dtv_t *dtv;
Packit 6c4009
  void *self;		/* Pointer to the thread descriptor.  */
Packit 6c4009
  int multiple_threads;
Packit 6c4009
  int gscope_flag;
Packit 6c4009
  uintptr_t sysinfo;
Packit 6c4009
  uintptr_t stack_guard;
Packit 6c4009
  uintptr_t pointer_guard;
Packit 6c4009
  unsigned long int vgetcpu_cache[2];
Packit 6c4009
  /* Bit 0: X86_FEATURE_1_IBT.
Packit 6c4009
     Bit 1: X86_FEATURE_1_SHSTK.
Packit 6c4009
   */
Packit 6c4009
  unsigned int feature_1;
Packit 6c4009
  int __glibc_unused1;
Packit 6c4009
  /* Reservation of some values for the TM ABI.  */
Packit 6c4009
  void *__private_tm[4];
Packit 6c4009
  /* GCC split stack support.  */
Packit 6c4009
  void *__private_ss;
Packit 6c4009
  /* The lowest address of shadow stack,  */
Packit 6c4009
  unsigned long long int ssp_base;
Packit 6c4009
  /* Must be kept even if it is no longer used by glibc since programs,
Packit 6c4009
     like AddressSanitizer, depend on the size of tcbhead_t.  */
Packit 6c4009
  __128bits __glibc_unused2[8][4] __attribute__ ((aligned (32)));
Packit 6c4009
Packit 6c4009
  void *__padding[8];
Packit 6c4009
} tcbhead_t;
Packit 6c4009
Packit 6c4009
# ifdef __ILP32__
Packit 6c4009
/* morestack.S in libgcc uses offset 0x40 to access __private_ss,   */
Packit 6c4009
_Static_assert (offsetof (tcbhead_t, __private_ss) == 0x40,
Packit 6c4009
		"offset of __private_ss != 0x40");
Packit 6c4009
/* NB: ssp_base used to be "long int __glibc_reserved2", which was
Packit 6c4009
   changed from 32 bits to 64 bits.  Make sure that the offset of the
Packit 6c4009
   next field, __glibc_unused2, is unchanged.  */
Packit 6c4009
_Static_assert (offsetof (tcbhead_t, __glibc_unused2) == 0x60,
Packit 6c4009
		"offset of __glibc_unused2 != 0x60");
Packit 6c4009
# else
Packit 6c4009
/* morestack.S in libgcc uses offset 0x70 to access __private_ss,   */
Packit 6c4009
_Static_assert (offsetof (tcbhead_t, __private_ss) == 0x70,
Packit 6c4009
		"offset of __private_ss != 0x70");
Packit 6c4009
_Static_assert (offsetof (tcbhead_t, __glibc_unused2) == 0x80,
Packit 6c4009
		"offset of __glibc_unused2 != 0x80");
Packit 6c4009
# endif
Packit 6c4009
Packit 6c4009
#else /* __ASSEMBLER__ */
Packit 6c4009
# include <tcb-offsets.h>
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Alignment requirement for the stack.  */
Packit 6c4009
#define STACK_ALIGN	16
Packit 6c4009
Packit 6c4009
Packit 6c4009
#ifndef __ASSEMBLER__
Packit 6c4009
/* Get system call information.  */
Packit 6c4009
# include <sysdep.h>
Packit 6c4009
Packit 6c4009
#ifndef LOCK_PREFIX
Packit 6c4009
# ifdef UP
Packit 6c4009
#  define LOCK_PREFIX	/* nothing */
Packit 6c4009
# else
Packit 6c4009
#  define LOCK_PREFIX	"lock;"
Packit 6c4009
# endif
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
/* This is the size of the initial TCB.  Can't be just sizeof (tcbhead_t),
Packit 6c4009
   because NPTL getpid, __libc_alloca_cutoff etc. need (almost) the whole
Packit 6c4009
   struct pthread even when not linked with -lpthread.  */
Packit 6c4009
# define TLS_INIT_TCB_SIZE sizeof (struct pthread)
Packit 6c4009
Packit 6c4009
/* Alignment requirements for the initial TCB.  */
Packit 6c4009
# define TLS_INIT_TCB_ALIGN __alignof__ (struct pthread)
Packit 6c4009
Packit 6c4009
/* This is the size of the TCB.  */
Packit 6c4009
# define TLS_TCB_SIZE sizeof (struct pthread)
Packit 6c4009
Packit 6c4009
/* Alignment requirements for the TCB.  */
Packit 6c4009
# define TLS_TCB_ALIGN __alignof__ (struct pthread)
Packit 6c4009
Packit 6c4009
/* The TCB can have any size and the memory following the address the
Packit 6c4009
   thread pointer points to is unspecified.  Allocate the TCB there.  */
Packit 6c4009
# define TLS_TCB_AT_TP	1
Packit 6c4009
# define TLS_DTV_AT_TP	0
Packit 6c4009
Packit 6c4009
/* Get the thread descriptor definition.  */
Packit 6c4009
# include <nptl/descr.h>
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Install the dtv pointer.  The pointer passed is to the element with
Packit 6c4009
   index -1 which contain the length.  */
Packit 6c4009
# define INSTALL_DTV(descr, dtvp) \
Packit 6c4009
  ((tcbhead_t *) (descr))->dtv = (dtvp) + 1
Packit 6c4009
Packit 6c4009
/* Install new dtv for current thread.  */
Packit 6c4009
# define INSTALL_NEW_DTV(dtvp) \
Packit 6c4009
  ({ struct pthread *__pd;						      \
Packit 6c4009
     THREAD_SETMEM (__pd, header.dtv, (dtvp)); })
Packit 6c4009
Packit 6c4009
/* Return dtv of given thread descriptor.  */
Packit 6c4009
# define GET_DTV(descr) \
Packit 6c4009
  (((tcbhead_t *) (descr))->dtv)
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Code to initially initialize the thread pointer.  This might need
Packit 6c4009
   special attention since 'errno' is not yet available and if the
Packit 6c4009
   operation can cause a failure 'errno' must not be touched.
Packit 6c4009
Packit 6c4009
   We have to make the syscall for both uses of the macro since the
Packit 6c4009
   address might be (and probably is) different.  */
Packit 6c4009
# define TLS_INIT_TP(thrdescr) \
Packit 6c4009
  ({ void *_thrdescr = (thrdescr);					      \
Packit 6c4009
     tcbhead_t *_head = _thrdescr;					      \
Packit 6c4009
     int _result;							      \
Packit 6c4009
									      \
Packit 6c4009
     _head->tcb = _thrdescr;						      \
Packit 6c4009
     /* For now the thread descriptor is at the same address.  */	      \
Packit 6c4009
     _head->self = _thrdescr;						      \
Packit 6c4009
									      \
Packit 6c4009
     /* It is a simple syscall to set the %fs value for the thread.  */	      \
Packit 6c4009
     asm volatile ("syscall"						      \
Packit 6c4009
		   : "=a" (_result)					      \
Packit 6c4009
		   : "0" ((unsigned long int) __NR_arch_prctl),		      \
Packit 6c4009
		     "D" ((unsigned long int) ARCH_SET_FS),		      \
Packit 6c4009
		     "S" (_thrdescr)					      \
Packit 6c4009
		   : "memory", "cc", "r11", "cx");			      \
Packit 6c4009
									      \
Packit 6c4009
    _result ? "cannot set %fs base address for thread-local storage" : 0;     \
Packit 6c4009
  })
Packit 6c4009
Packit 6c4009
# define TLS_DEFINE_INIT_TP(tp, pd) void *tp = (pd)
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Return the address of the dtv for the current thread.  */
Packit 6c4009
# define THREAD_DTV() \
Packit 6c4009
  ({ struct pthread *__pd;						      \
Packit 6c4009
     THREAD_GETMEM (__pd, header.dtv); })
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Return the thread descriptor for the current thread.
Packit 6c4009
Packit 6c4009
   The contained asm must *not* be marked volatile since otherwise
Packit 6c4009
   assignments like
Packit 6c4009
	pthread_descr self = thread_self();
Packit 6c4009
   do not get optimized away.  */
Packit 6c4009
# define THREAD_SELF \
Packit 6c4009
  ({ struct pthread *__self;						      \
Packit 6c4009
     asm ("mov %%fs:%c1,%0" : "=r" (__self)				      \
Packit 6c4009
	  : "i" (offsetof (struct pthread, header.self)));	 	      \
Packit 6c4009
     __self;})
Packit 6c4009
Packit 6c4009
/* Magic for libthread_db to know how to do THREAD_SELF.  */
Packit 6c4009
# define DB_THREAD_SELF_INCLUDE  <sys/reg.h> /* For the FS constant.  */
Packit 6c4009
# define DB_THREAD_SELF CONST_THREAD_AREA (64, FS)
Packit 6c4009
Packit 6c4009
/* Read member of the thread descriptor directly.  */
Packit 6c4009
# define THREAD_GETMEM(descr, member) \
Packit 6c4009
  ({ __typeof (descr->member) __value;					      \
Packit 6c4009
     if (sizeof (__value) == 1)						      \
Packit 6c4009
       asm volatile ("movb %%fs:%P2,%b0"				      \
Packit 6c4009
		     : "=q" (__value)					      \
Packit 6c4009
		     : "0" (0), "i" (offsetof (struct pthread, member)));     \
Packit 6c4009
     else if (sizeof (__value) == 4)					      \
Packit 6c4009
       asm volatile ("movl %%fs:%P1,%0"					      \
Packit 6c4009
		     : "=r" (__value)					      \
Packit 6c4009
		     : "i" (offsetof (struct pthread, member)));	      \
Packit 6c4009
     else								      \
Packit 6c4009
       {								      \
Packit 6c4009
	 if (sizeof (__value) != 8)					      \
Packit 6c4009
	   /* There should not be any value with a size other than 1,	      \
Packit 6c4009
	      4 or 8.  */						      \
Packit 6c4009
	   abort ();							      \
Packit 6c4009
									      \
Packit 6c4009
	 asm volatile ("movq %%fs:%P1,%q0"				      \
Packit 6c4009
		       : "=r" (__value)					      \
Packit 6c4009
		       : "i" (offsetof (struct pthread, member)));	      \
Packit 6c4009
       }								      \
Packit 6c4009
     __value; })
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Same as THREAD_GETMEM, but the member offset can be non-constant.  */
Packit 6c4009
# define THREAD_GETMEM_NC(descr, member, idx) \
Packit 6c4009
  ({ __typeof (descr->member[0]) __value;				      \
Packit 6c4009
     if (sizeof (__value) == 1)						      \
Packit 6c4009
       asm volatile ("movb %%fs:%P2(%q3),%b0"				      \
Packit 6c4009
		     : "=q" (__value)					      \
Packit 6c4009
		     : "0" (0), "i" (offsetof (struct pthread, member[0])),   \
Packit 6c4009
		       "r" (idx));					      \
Packit 6c4009
     else if (sizeof (__value) == 4)					      \
Packit 6c4009
       asm volatile ("movl %%fs:%P1(,%q2,4),%0"				      \
Packit 6c4009
		     : "=r" (__value)					      \
Packit 6c4009
		     : "i" (offsetof (struct pthread, member[0])), "r" (idx));\
Packit 6c4009
     else								      \
Packit 6c4009
       {								      \
Packit 6c4009
	 if (sizeof (__value) != 8)					      \
Packit 6c4009
	   /* There should not be any value with a size other than 1,	      \
Packit 6c4009
	      4 or 8.  */						      \
Packit 6c4009
	   abort ();							      \
Packit 6c4009
									      \
Packit 6c4009
	 asm volatile ("movq %%fs:%P1(,%q2,8),%q0"			      \
Packit 6c4009
		       : "=r" (__value)					      \
Packit 6c4009
		       : "i" (offsetof (struct pthread, member[0])),	      \
Packit 6c4009
			 "r" (idx));					      \
Packit 6c4009
       }								      \
Packit 6c4009
     __value; })
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Loading addresses of objects on x86-64 needs to be treated special
Packit 6c4009
   when generating PIC code.  */
Packit 6c4009
#ifdef __pic__
Packit 6c4009
# define IMM_MODE "nr"
Packit 6c4009
#else
Packit 6c4009
# define IMM_MODE "ir"
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Set member of the thread descriptor directly.  */
Packit 6c4009
# define THREAD_SETMEM(descr, member, value) \
Packit 6c4009
  ({ if (sizeof (descr->member) == 1)					      \
Packit 6c4009
       asm volatile ("movb %b0,%%fs:%P1" :				      \
Packit 6c4009
		     : "iq" (value),					      \
Packit 6c4009
		       "i" (offsetof (struct pthread, member)));	      \
Packit 6c4009
     else if (sizeof (descr->member) == 4)				      \
Packit 6c4009
       asm volatile ("movl %0,%%fs:%P1" :				      \
Packit 6c4009
		     : IMM_MODE (value),				      \
Packit 6c4009
		       "i" (offsetof (struct pthread, member)));	      \
Packit 6c4009
     else								      \
Packit 6c4009
       {								      \
Packit 6c4009
	 if (sizeof (descr->member) != 8)				      \
Packit 6c4009
	   /* There should not be any value with a size other than 1,	      \
Packit 6c4009
	      4 or 8.  */						      \
Packit 6c4009
	   abort ();							      \
Packit 6c4009
									      \
Packit 6c4009
	 asm volatile ("movq %q0,%%fs:%P1" :				      \
Packit 6c4009
		       : IMM_MODE ((uint64_t) cast_to_integer (value)),	      \
Packit 6c4009
			 "i" (offsetof (struct pthread, member)));	      \
Packit 6c4009
       }})
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Same as THREAD_SETMEM, but the member offset can be non-constant.  */
Packit 6c4009
# define THREAD_SETMEM_NC(descr, member, idx, value) \
Packit 6c4009
  ({ if (sizeof (descr->member[0]) == 1)				      \
Packit 6c4009
       asm volatile ("movb %b0,%%fs:%P1(%q2)" :				      \
Packit 6c4009
		     : "iq" (value),					      \
Packit 6c4009
		       "i" (offsetof (struct pthread, member[0])),	      \
Packit 6c4009
		       "r" (idx));					      \
Packit 6c4009
     else if (sizeof (descr->member[0]) == 4)				      \
Packit 6c4009
       asm volatile ("movl %0,%%fs:%P1(,%q2,4)" :			      \
Packit 6c4009
		     : IMM_MODE (value),				      \
Packit 6c4009
		       "i" (offsetof (struct pthread, member[0])),	      \
Packit 6c4009
		       "r" (idx));					      \
Packit 6c4009
     else								      \
Packit 6c4009
       {								      \
Packit 6c4009
	 if (sizeof (descr->member[0]) != 8)				      \
Packit 6c4009
	   /* There should not be any value with a size other than 1,	      \
Packit 6c4009
	      4 or 8.  */						      \
Packit 6c4009
	   abort ();							      \
Packit 6c4009
									      \
Packit 6c4009
	 asm volatile ("movq %q0,%%fs:%P1(,%q2,8)" :			      \
Packit 6c4009
		       : IMM_MODE ((uint64_t) cast_to_integer (value)),	      \
Packit 6c4009
			 "i" (offsetof (struct pthread, member[0])),	      \
Packit 6c4009
			 "r" (idx));					      \
Packit 6c4009
       }})
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Atomic compare and exchange on TLS, returning old value.  */
Packit 6c4009
# define THREAD_ATOMIC_CMPXCHG_VAL(descr, member, newval, oldval) \
Packit 6c4009
  ({ __typeof (descr->member) __ret;					      \
Packit 6c4009
     __typeof (oldval) __old = (oldval);				      \
Packit 6c4009
     if (sizeof (descr->member) == 4)					      \
Packit 6c4009
       asm volatile (LOCK_PREFIX "cmpxchgl %2, %%fs:%P3"		      \
Packit 6c4009
		     : "=a" (__ret)					      \
Packit 6c4009
		     : "0" (__old), "r" (newval),			      \
Packit 6c4009
		       "i" (offsetof (struct pthread, member)));	      \
Packit 6c4009
     else								      \
Packit 6c4009
       /* Not necessary for other sizes in the moment.  */		      \
Packit 6c4009
       abort ();							      \
Packit 6c4009
     __ret; })
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Atomic logical and.  */
Packit 6c4009
# define THREAD_ATOMIC_AND(descr, member, val) \
Packit 6c4009
  (void) ({ if (sizeof ((descr)->member) == 4)				      \
Packit 6c4009
	      asm volatile (LOCK_PREFIX "andl %1, %%fs:%P0"		      \
Packit 6c4009
			    :: "i" (offsetof (struct pthread, member)),	      \
Packit 6c4009
			       "ir" (val));				      \
Packit 6c4009
	    else							      \
Packit 6c4009
	      /* Not necessary for other sizes in the moment.  */	      \
Packit 6c4009
	      abort (); })
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Atomic set bit.  */
Packit 6c4009
# define THREAD_ATOMIC_BIT_SET(descr, member, bit) \
Packit 6c4009
  (void) ({ if (sizeof ((descr)->member) == 4)				      \
Packit 6c4009
	      asm volatile (LOCK_PREFIX "orl %1, %%fs:%P0"		      \
Packit 6c4009
			    :: "i" (offsetof (struct pthread, member)),	      \
Packit 6c4009
			       "ir" (1 << (bit)));			      \
Packit 6c4009
	    else							      \
Packit 6c4009
	      /* Not necessary for other sizes in the moment.  */	      \
Packit 6c4009
	      abort (); })
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Set the stack guard field in TCB head.  */
Packit 6c4009
# define THREAD_SET_STACK_GUARD(value) \
Packit 6c4009
    THREAD_SETMEM (THREAD_SELF, header.stack_guard, value)
Packit 6c4009
# define THREAD_COPY_STACK_GUARD(descr) \
Packit 6c4009
    ((descr)->header.stack_guard					      \
Packit 6c4009
     = THREAD_GETMEM (THREAD_SELF, header.stack_guard))
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Set the pointer guard field in the TCB head.  */
Packit 6c4009
# define THREAD_SET_POINTER_GUARD(value) \
Packit 6c4009
  THREAD_SETMEM (THREAD_SELF, header.pointer_guard, value)
Packit 6c4009
# define THREAD_COPY_POINTER_GUARD(descr) \
Packit 6c4009
  ((descr)->header.pointer_guard					      \
Packit 6c4009
   = THREAD_GETMEM (THREAD_SELF, header.pointer_guard))
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Get and set the global scope generation counter in the TCB head.  */
Packit 6c4009
# define THREAD_GSCOPE_IN_TCB      1
Packit 6c4009
# define THREAD_GSCOPE_FLAG_UNUSED 0
Packit 6c4009
# define THREAD_GSCOPE_FLAG_USED   1
Packit 6c4009
# define THREAD_GSCOPE_FLAG_WAIT   2
Packit 6c4009
# define THREAD_GSCOPE_RESET_FLAG() \
Packit 6c4009
  do									      \
Packit 6c4009
    { int __res;							      \
Packit 6c4009
      asm volatile ("xchgl %0, %%fs:%P1"				      \
Packit 6c4009
		    : "=r" (__res)					      \
Packit 6c4009
		    : "i" (offsetof (struct pthread, header.gscope_flag)),    \
Packit 6c4009
		      "0" (THREAD_GSCOPE_FLAG_UNUSED));			      \
Packit 6c4009
      if (__res == THREAD_GSCOPE_FLAG_WAIT)				      \
Packit 6c4009
	lll_futex_wake (&THREAD_SELF->header.gscope_flag, 1, LLL_PRIVATE);    \
Packit 6c4009
    }									      \
Packit 6c4009
  while (0)
Packit 6c4009
# define THREAD_GSCOPE_SET_FLAG() \
Packit 6c4009
  THREAD_SETMEM (THREAD_SELF, header.gscope_flag, THREAD_GSCOPE_FLAG_USED)
Packit 6c4009
# define THREAD_GSCOPE_WAIT() \
Packit 6c4009
  GL(dl_wait_lookup_done) ()
Packit 6c4009
Packit 6c4009
#endif /* __ASSEMBLER__ */
Packit 6c4009
Packit 6c4009
#endif	/* tls.h */