1b00401f0SDavid Xu /*- 2b00401f0SDavid Xu * Copyright (c) 2004 Doug Rabson 3b00401f0SDavid Xu * All rights reserved. 4b00401f0SDavid Xu * 5b00401f0SDavid Xu * Redistribution and use in source and binary forms, with or without 6b00401f0SDavid Xu * modification, are permitted provided that the following conditions 7b00401f0SDavid Xu * are met: 8b00401f0SDavid Xu * 1. Redistributions of source code must retain the above copyright 9b00401f0SDavid Xu * notice, this list of conditions and the following disclaimer. 10b00401f0SDavid Xu * 2. Redistributions in binary form must reproduce the above copyright 11b00401f0SDavid Xu * notice, this list of conditions and the following disclaimer in the 12b00401f0SDavid Xu * documentation and/or other materials provided with the distribution. 13b00401f0SDavid Xu * 14b00401f0SDavid Xu * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15b00401f0SDavid Xu * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16b00401f0SDavid Xu * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17b00401f0SDavid Xu * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18b00401f0SDavid Xu * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19b00401f0SDavid Xu * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20b00401f0SDavid Xu * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21b00401f0SDavid Xu * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22b00401f0SDavid Xu * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23b00401f0SDavid Xu * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24b00401f0SDavid Xu * SUCH DAMAGE. 25b00401f0SDavid Xu * 26b00401f0SDavid Xu * $FreeBSD: src/lib/libc/gen/tls.c,v 1.7 2005/03/01 23:42:00 davidxu Exp $ 27*a1eee96aSMatthew Dillon * $DragonFly: src/lib/libc/gen/tls.c,v 1.9 2005/05/11 19:46:52 dillon Exp $ 28b00401f0SDavid Xu */ 29b00401f0SDavid Xu 30b00401f0SDavid Xu /* 31b00401f0SDavid Xu * Define stubs for TLS internals so that programs and libraries can 32b00401f0SDavid Xu * link. These functions will be replaced by functional versions at 33b00401f0SDavid Xu * runtime from ld-elf.so.1. 34b00401f0SDavid Xu */ 35b00401f0SDavid Xu 3692df6c3eSDavid Xu #include <sys/cdefs.h> 37bc633d63SMatthew Dillon #include <sys/tls.h> 389e2ee207SJoerg Sonnenberger 399e2ee207SJoerg Sonnenberger #include <machine/tls.h> 409e2ee207SJoerg Sonnenberger 41b00401f0SDavid Xu #include <stdlib.h> 42b00401f0SDavid Xu #include <string.h> 43b00401f0SDavid Xu #include <elf.h> 44b00401f0SDavid Xu #include <assert.h> 45*a1eee96aSMatthew Dillon #include <errno.h> 46*a1eee96aSMatthew Dillon #include <unistd.h> 4792df6c3eSDavid Xu 48b00401f0SDavid Xu #include "libc_private.h" 49b00401f0SDavid Xu 5092df6c3eSDavid Xu __weak_reference(__libc_allocate_tls, _rtld_allocate_tls); 5192df6c3eSDavid Xu __weak_reference(__libc_free_tls, _rtld_free_tls); 52*a1eee96aSMatthew Dillon __weak_reference(__libc_call_init, _rtld_call_init); 5392df6c3eSDavid Xu #ifdef __i386__ 5492df6c3eSDavid Xu __weak_reference(___libc_tls_get_addr, ___tls_get_addr); 5592df6c3eSDavid Xu #endif 5692df6c3eSDavid Xu __weak_reference(__libc_tls_get_addr, __tls_get_addr); 57*a1eee96aSMatthew Dillon __weak_reference(__libc_tls_get_addr_tcb, __tls_get_addr_tcb); 58*a1eee96aSMatthew Dillon __weak_reference(_libc_init_tls, _init_tls); 5992df6c3eSDavid Xu 60*a1eee96aSMatthew Dillon struct tls_tcb *__libc_allocate_tls(void); 61f20fd431SJoerg Sonnenberger void __libc_free_tls(struct tls_tcb *tcb); 62b00401f0SDavid Xu 639e2ee207SJoerg Sonnenberger #if !defined(RTLD_STATIC_TLS_VARIANT_II) 649e2ee207SJoerg Sonnenberger #error "Unsupported TLS layout" 65b00401f0SDavid Xu #endif 66b00401f0SDavid Xu 67b00401f0SDavid Xu #ifndef PIC 68b00401f0SDavid Xu 69b00401f0SDavid Xu #define round(size, align) \ 70b00401f0SDavid Xu (((size) + (align) - 1) & ~((align) - 1)) 71b00401f0SDavid Xu 72b00401f0SDavid Xu static size_t tls_static_space; 73b00401f0SDavid Xu static size_t tls_init_size; 74b00401f0SDavid Xu static void *tls_init; 75*a1eee96aSMatthew Dillon static struct tls_tcb *initial_tcb; 76b00401f0SDavid Xu #endif 77b00401f0SDavid Xu 78b00401f0SDavid Xu #ifdef __i386__ 79b00401f0SDavid Xu 8092df6c3eSDavid Xu /* GNU ABI */ 81b00401f0SDavid Xu 8292df6c3eSDavid Xu void *___libc_tls_get_addr(void *ti) __attribute__((__regparm__(1))); 8392df6c3eSDavid Xu 84b00401f0SDavid Xu __attribute__((__regparm__(1))) 85b00401f0SDavid Xu void * 8692df6c3eSDavid Xu ___libc_tls_get_addr(void *ti __unused) 87b00401f0SDavid Xu { 88b00401f0SDavid Xu return (0); 89b00401f0SDavid Xu } 90b00401f0SDavid Xu 91b00401f0SDavid Xu #endif 92b00401f0SDavid Xu 9392df6c3eSDavid Xu void *__libc_tls_get_addr(void *ti); 94*a1eee96aSMatthew Dillon void *__libc_tls_get_addr_tcb(struct tls_tcb *, void *); 95bc633d63SMatthew Dillon 96b00401f0SDavid Xu void * 9792df6c3eSDavid Xu __libc_tls_get_addr(void *ti __unused) 98b00401f0SDavid Xu { 99*a1eee96aSMatthew Dillon return (NULL); 100*a1eee96aSMatthew Dillon } 101*a1eee96aSMatthew Dillon 102*a1eee96aSMatthew Dillon void * 103*a1eee96aSMatthew Dillon __libc_tls_get_addr_tcb(struct tls_tcb *tcb __unused, void *got_ptr __unused) 104*a1eee96aSMatthew Dillon { 105*a1eee96aSMatthew Dillon return (NULL); 106b00401f0SDavid Xu } 107b00401f0SDavid Xu 10892df6c3eSDavid Xu #ifndef PIC 10992df6c3eSDavid Xu 110b00401f0SDavid Xu /* 111*a1eee96aSMatthew Dillon * Free Static TLS, weakly bound to _rtld_free_tls() 112b00401f0SDavid Xu */ 113b00401f0SDavid Xu void 114f20fd431SJoerg Sonnenberger __libc_free_tls(struct tls_tcb *tcb) 115b00401f0SDavid Xu { 116bc633d63SMatthew Dillon size_t data_size; 117b00401f0SDavid Xu 118bc633d63SMatthew Dillon data_size = (tls_static_space + RTLD_STATIC_TLS_ALIGN_MASK) & 119bc633d63SMatthew Dillon ~RTLD_STATIC_TLS_ALIGN_MASK; 120*a1eee96aSMatthew Dillon 121*a1eee96aSMatthew Dillon if (tcb == initial_tcb) { 122*a1eee96aSMatthew Dillon /* initial_tcb was allocated with sbrk(), cannot call free() */ 123*a1eee96aSMatthew Dillon } else { 124bc633d63SMatthew Dillon free((char *)tcb - data_size); 125b00401f0SDavid Xu } 126*a1eee96aSMatthew Dillon } 127b00401f0SDavid Xu 128b00401f0SDavid Xu /* 129*a1eee96aSMatthew Dillon * Allocate Static TLS, weakly bound to _rtld_allocate_tls() 130a378ce7dSJoerg Sonnenberger * 131*a1eee96aSMatthew Dillon * NOTE! There is a chicken-and-egg problem here because no TLS exists 132*a1eee96aSMatthew Dillon * on the first call into this function. 133b00401f0SDavid Xu */ 134bc633d63SMatthew Dillon struct tls_tcb * 135*a1eee96aSMatthew Dillon __libc_allocate_tls(void) 136b00401f0SDavid Xu { 137bc633d63SMatthew Dillon size_t data_size; 138bc633d63SMatthew Dillon struct tls_tcb *tcb; 139b00401f0SDavid Xu Elf_Addr *dtv; 140b00401f0SDavid Xu 141bc633d63SMatthew Dillon data_size = (tls_static_space + RTLD_STATIC_TLS_ALIGN_MASK) & 142bc633d63SMatthew Dillon ~RTLD_STATIC_TLS_ALIGN_MASK; 143a378ce7dSJoerg Sonnenberger 144*a1eee96aSMatthew Dillon /* 145*a1eee96aSMatthew Dillon * Allocate space. malloc() may require a working TLS segment 146*a1eee96aSMatthew Dillon * so we use sbrk() for main's TLS. 147*a1eee96aSMatthew Dillon */ 148*a1eee96aSMatthew Dillon if (initial_tcb == NULL) 149*a1eee96aSMatthew Dillon tcb = sbrk(data_size + sizeof(*tcb) + 3 * sizeof(Elf_Addr)); 150*a1eee96aSMatthew Dillon else 151f20fd431SJoerg Sonnenberger tcb = malloc(data_size + sizeof(*tcb)); 1529e2ee207SJoerg Sonnenberger 153*a1eee96aSMatthew Dillon tcb = (struct tls_tcb *)((char *)tcb + data_size); 154*a1eee96aSMatthew Dillon dtv = (Elf_Addr *)(tcb + 1); 155*a1eee96aSMatthew Dillon 156*a1eee96aSMatthew Dillon memset(tcb, 0, sizeof(*tcb)); 1579e2ee207SJoerg Sonnenberger #ifdef RTLD_TCB_HAS_SELF_POINTER 1589e2ee207SJoerg Sonnenberger tcb->tcb_self = tcb; 1599e2ee207SJoerg Sonnenberger #endif 1609e2ee207SJoerg Sonnenberger tcb->tcb_dtv = dtv; 161b00401f0SDavid Xu 162*a1eee96aSMatthew Dillon /* 163*a1eee96aSMatthew Dillon * Dummy-up the module array. A static binary has only one. This 164*a1eee96aSMatthew Dillon * allows us to support the generic __tls_get_addr compiler ABI 165*a1eee96aSMatthew Dillon * function. However, if there is no RTLD linked in, nothing in 166*a1eee96aSMatthew Dillon * the program should ever call __tls_get_addr (and our version 167*a1eee96aSMatthew Dillon * of it doesn't do anything). 168*a1eee96aSMatthew Dillon */ 169b00401f0SDavid Xu dtv[0] = 1; 170b00401f0SDavid Xu dtv[1] = 1; 171bc633d63SMatthew Dillon dtv[2] = (Elf_Addr)((char *)tcb - tls_static_space); 172bc633d63SMatthew Dillon 173bc633d63SMatthew Dillon memcpy((char *)tcb - tls_static_space, 174b00401f0SDavid Xu tls_init, tls_init_size); 175bc633d63SMatthew Dillon memset((char *)tcb - tls_static_space + tls_init_size, 176b00401f0SDavid Xu 0, tls_static_space - tls_init_size); 177a378ce7dSJoerg Sonnenberger 178*a1eee96aSMatthew Dillon /* 179*a1eee96aSMatthew Dillon * Activate the initial TCB 180*a1eee96aSMatthew Dillon */ 181*a1eee96aSMatthew Dillon if (initial_tcb == NULL) { 182*a1eee96aSMatthew Dillon initial_tcb = tcb; 183a378ce7dSJoerg Sonnenberger tls_set_tcb(tcb); 184*a1eee96aSMatthew Dillon } 185*a1eee96aSMatthew Dillon return (tcb); 186b00401f0SDavid Xu } 187b00401f0SDavid Xu 18892df6c3eSDavid Xu #else 18992df6c3eSDavid Xu 190bc633d63SMatthew Dillon struct tls_tcb * 191*a1eee96aSMatthew Dillon __libc_allocate_tls(void) 19292df6c3eSDavid Xu { 193*a1eee96aSMatthew Dillon return (NULL); 19492df6c3eSDavid Xu } 19592df6c3eSDavid Xu 19692df6c3eSDavid Xu void 197f20fd431SJoerg Sonnenberger __libc_free_tls(struct tls_tcb *tcb __unused) 19892df6c3eSDavid Xu { 19992df6c3eSDavid Xu } 20092df6c3eSDavid Xu 20192df6c3eSDavid Xu #endif /* PIC */ 20292df6c3eSDavid Xu 203*a1eee96aSMatthew Dillon void 204*a1eee96aSMatthew Dillon __libc_call_init(void) 205*a1eee96aSMatthew Dillon { 206*a1eee96aSMatthew Dillon } 207*a1eee96aSMatthew Dillon 208b00401f0SDavid Xu extern char **environ; 209b00401f0SDavid Xu 210*a1eee96aSMatthew Dillon struct tls_tcb * 211*a1eee96aSMatthew Dillon _libc_init_tls(void) 212b00401f0SDavid Xu { 213*a1eee96aSMatthew Dillon struct tls_tcb *tcb; 214*a1eee96aSMatthew Dillon 215b00401f0SDavid Xu #ifndef PIC 216*a1eee96aSMatthew Dillon /* 217*a1eee96aSMatthew Dillon * If this is a static binary there is no RTLD and so we have not 218*a1eee96aSMatthew Dillon * yet calculated the static space requirement. Do so now. 219*a1eee96aSMatthew Dillon */ 220b00401f0SDavid Xu Elf_Addr *sp; 221b00401f0SDavid Xu Elf_Auxinfo *aux, *auxp; 222b00401f0SDavid Xu Elf_Phdr *phdr; 223b00401f0SDavid Xu size_t phent, phnum; 224b00401f0SDavid Xu int i; 225b00401f0SDavid Xu 226b00401f0SDavid Xu sp = (Elf_Addr *) environ; 227b00401f0SDavid Xu while (*sp++ != 0) 228b00401f0SDavid Xu ; 229b00401f0SDavid Xu aux = (Elf_Auxinfo *) sp; 230b00401f0SDavid Xu phdr = 0; 231b00401f0SDavid Xu phent = phnum = 0; 232b00401f0SDavid Xu for (auxp = aux; auxp->a_type != AT_NULL; auxp++) { 233b00401f0SDavid Xu switch (auxp->a_type) { 234b00401f0SDavid Xu case AT_PHDR: 235b00401f0SDavid Xu phdr = auxp->a_un.a_ptr; 236b00401f0SDavid Xu break; 237b00401f0SDavid Xu 238b00401f0SDavid Xu case AT_PHENT: 239b00401f0SDavid Xu phent = auxp->a_un.a_val; 240b00401f0SDavid Xu break; 241b00401f0SDavid Xu 242b00401f0SDavid Xu case AT_PHNUM: 243b00401f0SDavid Xu phnum = auxp->a_un.a_val; 244b00401f0SDavid Xu break; 245b00401f0SDavid Xu } 246b00401f0SDavid Xu } 247b00401f0SDavid Xu if (phdr == 0 || phent != sizeof(Elf_Phdr) || phnum == 0) 248b00401f0SDavid Xu return; 249b00401f0SDavid Xu 250b00401f0SDavid Xu for (i = 0; (unsigned)i < phnum; i++) { 251b00401f0SDavid Xu if (phdr[i].p_type == PT_TLS) { 252b00401f0SDavid Xu tls_static_space = round(phdr[i].p_memsz, 253b00401f0SDavid Xu phdr[i].p_align); 254b00401f0SDavid Xu tls_init_size = phdr[i].p_filesz; 255b00401f0SDavid Xu tls_init = (void*) phdr[i].p_vaddr; 256b00401f0SDavid Xu } 257b00401f0SDavid Xu } 258b00401f0SDavid Xu #endif 259*a1eee96aSMatthew Dillon 260*a1eee96aSMatthew Dillon /* 261*a1eee96aSMatthew Dillon * Allocate the initial TLS segment. The TLS has not been set up 262*a1eee96aSMatthew Dillon * yet for either the static or dynamic linked case (RTLD no longer 263*a1eee96aSMatthew Dillon * sets up an initial TLS segment for us). 264*a1eee96aSMatthew Dillon */ 265*a1eee96aSMatthew Dillon tcb = _libc_allocate_tls(); 266*a1eee96aSMatthew Dillon tls_set_tcb(tcb); 267*a1eee96aSMatthew Dillon return(tcb); 268b00401f0SDavid Xu } 269*a1eee96aSMatthew Dillon 270*a1eee96aSMatthew Dillon /* 271*a1eee96aSMatthew Dillon * Allocate a standard TLS. This function is called by libc and by 272*a1eee96aSMatthew Dillon * thread libraries to create a new TCB with libc-related fields properly 273*a1eee96aSMatthew Dillon * initialized (whereas _rtld_allocate_tls() is unable to completely set 274*a1eee96aSMatthew Dillon * up the TCB). 275*a1eee96aSMatthew Dillon * 276*a1eee96aSMatthew Dillon * Note that this is different from __libc_allocate_tls which is the 277*a1eee96aSMatthew Dillon * weakly bound symbol that handles the case where _rtld_allocate_tls 278*a1eee96aSMatthew Dillon * does not exist. 279*a1eee96aSMatthew Dillon */ 280*a1eee96aSMatthew Dillon struct tls_tcb * 281*a1eee96aSMatthew Dillon _libc_allocate_tls(void) 282*a1eee96aSMatthew Dillon { 283*a1eee96aSMatthew Dillon struct tls_tcb *tcb; 284*a1eee96aSMatthew Dillon 285*a1eee96aSMatthew Dillon tcb = _rtld_allocate_tls(); 286*a1eee96aSMatthew Dillon 287*a1eee96aSMatthew Dillon #if 0 288*a1eee96aSMatthew Dillon #if defined(__thread) 289*a1eee96aSMatthew Dillon /* non-TLS libc */ 290*a1eee96aSMatthew Dillon tcb->tcb_errno_p = &errno; 291*a1eee96aSMatthew Dillon #elif defined(PIC) 292*a1eee96aSMatthew Dillon /* TLS libc dynamically linked */ 293*a1eee96aSMatthew Dillon tcb->tcb_errno_p = __tls_get_addr_tcb(tcb, __get_errno_GOT_ptr()); 294*a1eee96aSMatthew Dillon #else 295*a1eee96aSMatthew Dillon /* TLS libc (threaded or unthreaded) */ 296*a1eee96aSMatthew Dillon tcb->tcb_errno_p = (void *)((char *)tcb + __get_errno_GS_offset()); 297*a1eee96aSMatthew Dillon #endif 298*a1eee96aSMatthew Dillon #endif 299*a1eee96aSMatthew Dillon return(tcb); 300*a1eee96aSMatthew Dillon } 301*a1eee96aSMatthew Dillon 302