tls.c revision 203946
1133064Sdfr/*- 2133064Sdfr * Copyright (c) 2004 Doug Rabson 3133064Sdfr * All rights reserved. 4133064Sdfr * 5133064Sdfr * Redistribution and use in source and binary forms, with or without 6133064Sdfr * modification, are permitted provided that the following conditions 7133064Sdfr * are met: 8133064Sdfr * 1. Redistributions of source code must retain the above copyright 9133064Sdfr * notice, this list of conditions and the following disclaimer. 10133064Sdfr * 2. Redistributions in binary form must reproduce the above copyright 11133064Sdfr * notice, this list of conditions and the following disclaimer in the 12133064Sdfr * documentation and/or other materials provided with the distribution. 13133064Sdfr * 14133064Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15133064Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16133064Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17133064Sdfr * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18133064Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19133064Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20133064Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21133064Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22133064Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23133064Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24133064Sdfr * SUCH DAMAGE. 25133064Sdfr * 26133064Sdfr * $FreeBSD: head/lib/libc/gen/tls.c 203946 2010-02-16 02:22:59Z marcel $ 27133064Sdfr */ 28133064Sdfr 29133064Sdfr/* 30133064Sdfr * Define stubs for TLS internals so that programs and libraries can 31133064Sdfr * link. These functions will be replaced by functional versions at 32133064Sdfr * runtime from ld-elf.so.1. 33133064Sdfr */ 34133064Sdfr 35143921Sdavidxu#include <sys/cdefs.h> 36133754Sdfr#include <stdlib.h> 37133754Sdfr#include <string.h> 38133754Sdfr#include <elf.h> 39143921Sdavidxu 40133754Sdfr#include "libc_private.h" 41133064Sdfr 42143921Sdavidxu__weak_reference(__libc_allocate_tls, _rtld_allocate_tls); 43143921Sdavidxu__weak_reference(__libc_free_tls, _rtld_free_tls); 44143921Sdavidxu 45143921Sdavidxu#ifdef __i386__ 46143921Sdavidxu 47143921Sdavidxu__weak_reference(___libc_tls_get_addr, ___tls_get_addr); 48143921Sdavidxu__attribute__((__regparm__(1))) void * ___libc_tls_get_addr(void *); 49143921Sdavidxu 50143921Sdavidxu#endif 51143921Sdavidxu 52143921Sdavidxuvoid * __libc_tls_get_addr(void *); 53143921Sdavidxu__weak_reference(__libc_tls_get_addr, __tls_get_addr); 54143921Sdavidxu 55143921Sdavidxuvoid *_rtld_allocate_tls(void *oldtls, size_t tcbsize, size_t tcbalign); 56143921Sdavidxuvoid _rtld_free_tls(void *tls, size_t tcbsize, size_t tcbalign); 57143921Sdavidxuvoid *__libc_allocate_tls(void *oldtls, size_t tcbsize, size_t tcbalign); 58143921Sdavidxuvoid __libc_free_tls(void *tls, size_t tcbsize, size_t tcbalign); 59143921Sdavidxu 60163118Skmacy#if defined(__ia64__) || defined(__amd64__) 61163118Skmacy#define TLS_TCB_ALIGN 16 62163118Skmacy#elif defined(__powerpc__) || defined(__i386__) || defined(__arm__) || \ 63178684Sgonzo defined(__sparc64__) || defined(__mips__) 64163118Skmacy#define TLS_TCB_ALIGN sizeof(void *) 65163118Skmacy#else 66163118Skmacy#error TLS_TCB_ALIGN undefined for target architecture 67163118Skmacy#endif 68163118Skmacy 69161800Smarcel#if defined(__ia64__) || defined(__powerpc__) 70133754Sdfr#define TLS_VARIANT_I 71133754Sdfr#endif 72135686Scognet#if defined(__i386__) || defined(__amd64__) || defined(__sparc64__) || \ 73178684Sgonzo defined(__arm__) || defined(__mips__) 74133754Sdfr#define TLS_VARIANT_II 75133754Sdfr#endif 76133754Sdfr 77133754Sdfr#ifndef PIC 78133754Sdfr 79133754Sdfr#define round(size, align) \ 80133754Sdfr (((size) + (align) - 1) & ~((align) - 1)) 81133754Sdfr 82133754Sdfrstatic size_t tls_static_space; 83133754Sdfrstatic size_t tls_init_size; 84133754Sdfrstatic void *tls_init; 85133754Sdfr#endif 86133754Sdfr 87133064Sdfr#ifdef __i386__ 88133064Sdfr 89143921Sdavidxu/* GNU ABI */ 90133064Sdfr 91133064Sdfr__attribute__((__regparm__(1))) 92133064Sdfrvoid * 93143921Sdavidxu___libc_tls_get_addr(void *ti __unused) 94133064Sdfr{ 95133064Sdfr return (0); 96133064Sdfr} 97133064Sdfr 98133064Sdfr#endif 99133064Sdfr 100133064Sdfrvoid * 101143921Sdavidxu__libc_tls_get_addr(void *ti __unused) 102133064Sdfr{ 103133064Sdfr return (0); 104133064Sdfr} 105133064Sdfr 106143921Sdavidxu#ifndef PIC 107143921Sdavidxu 108133754Sdfr#ifdef TLS_VARIANT_I 109133754Sdfr 110161800Smarcel#define TLS_TCB_SIZE (2 * sizeof(void *)) 111161800Smarcel 112142560Sdavidxu/* 113142959Sdavidxu * Free Static TLS using the Variant I method. 114142560Sdavidxu */ 115133754Sdfrvoid 116161800Smarcel__libc_free_tls(void *tcb, size_t tcbsize, size_t tcbalign __unused) 117133754Sdfr{ 118161800Smarcel Elf_Addr *dtv; 119161800Smarcel Elf_Addr **tls; 120133754Sdfr 121161800Smarcel tls = (Elf_Addr **)((Elf_Addr)tcb + tcbsize - TLS_TCB_SIZE); 122161800Smarcel dtv = tls[0]; 123133754Sdfr free(dtv); 124161800Smarcel free(tcb); 125133754Sdfr} 126133754Sdfr 127133754Sdfr/* 128133754Sdfr * Allocate Static TLS using the Variant I method. 129133754Sdfr */ 130133064Sdfrvoid * 131161800Smarcel__libc_allocate_tls(void *oldtcb, size_t tcbsize, size_t tcbalign __unused) 132133064Sdfr{ 133133754Sdfr Elf_Addr *dtv; 134161800Smarcel Elf_Addr **tls; 135161800Smarcel char *tcb; 136133754Sdfr 137161800Smarcel if (oldtcb != NULL && tcbsize == TLS_TCB_SIZE) 138161800Smarcel return (oldtcb); 139133754Sdfr 140203946Smarcel tcb = calloc(1, tls_static_space + tcbsize - TLS_TCB_SIZE); 141161800Smarcel tls = (Elf_Addr **)(tcb + tcbsize - TLS_TCB_SIZE); 142133754Sdfr 143161800Smarcel if (oldtcb != NULL) { 144203946Smarcel memcpy(tls, oldtcb, tls_static_space); 145161800Smarcel free(oldtcb); 146133754Sdfr 147161800Smarcel /* Adjust the DTV. */ 148161800Smarcel dtv = tls[0]; 149161800Smarcel dtv[2] = (Elf_Addr)tls + TLS_TCB_SIZE; 150161800Smarcel } else { 151161800Smarcel dtv = malloc(3 * sizeof(Elf_Addr)); 152161800Smarcel tls[0] = dtv; 153161800Smarcel dtv[0] = 1; 154161800Smarcel dtv[1] = 1; 155161800Smarcel dtv[2] = (Elf_Addr)tls + TLS_TCB_SIZE; 156133754Sdfr 157161800Smarcel if (tls_init_size > 0) 158161827Smarcel memcpy((void*)dtv[2], tls_init, tls_init_size); 159161800Smarcel if (tls_static_space > tls_init_size) 160161827Smarcel memset((void*)(dtv[2] + tls_init_size), 0, 161161827Smarcel tls_static_space - tls_init_size); 162133754Sdfr } 163133754Sdfr 164161800Smarcel return(tcb); 165133064Sdfr} 166133064Sdfr 167133754Sdfr#endif 168133754Sdfr 169133754Sdfr#ifdef TLS_VARIANT_II 170133754Sdfr 171161800Smarcel#define TLS_TCB_SIZE (3 * sizeof(Elf_Addr)) 172161800Smarcel 173133754Sdfr/* 174133754Sdfr * Free Static TLS using the Variant II method. 175133754Sdfr */ 176133064Sdfrvoid 177143921Sdavidxu__libc_free_tls(void *tcb, size_t tcbsize __unused, size_t tcbalign) 178133064Sdfr{ 179133754Sdfr size_t size; 180133754Sdfr Elf_Addr* dtv; 181133754Sdfr Elf_Addr tlsstart, tlsend; 182133754Sdfr 183133754Sdfr /* 184133754Sdfr * Figure out the size of the initial TLS block so that we can 185133754Sdfr * find stuff which ___tls_get_addr() allocated dynamically. 186133754Sdfr */ 187133754Sdfr size = round(tls_static_space, tcbalign); 188133754Sdfr 189133754Sdfr dtv = ((Elf_Addr**)tcb)[1]; 190133754Sdfr tlsend = (Elf_Addr) tcb; 191133754Sdfr tlsstart = tlsend - size; 192133754Sdfr free((void*) tlsstart); 193133754Sdfr free(dtv); 194133064Sdfr} 195133754Sdfr 196133754Sdfr/* 197133754Sdfr * Allocate Static TLS using the Variant II method. 198133754Sdfr */ 199133754Sdfrvoid * 200143921Sdavidxu__libc_allocate_tls(void *oldtls, size_t tcbsize, size_t tcbalign) 201133754Sdfr{ 202133754Sdfr size_t size; 203133754Sdfr char *tls; 204133754Sdfr Elf_Addr *dtv; 205133754Sdfr Elf_Addr segbase, oldsegbase; 206133754Sdfr 207133754Sdfr size = round(tls_static_space, tcbalign); 208133754Sdfr 209166995Skientzle if (tcbsize < 2 * sizeof(Elf_Addr)) 210166995Skientzle tcbsize = 2 * sizeof(Elf_Addr); 211157198Sdavidxu tls = calloc(1, size + tcbsize); 212133754Sdfr dtv = malloc(3 * sizeof(Elf_Addr)); 213133754Sdfr 214133754Sdfr segbase = (Elf_Addr)(tls + size); 215133754Sdfr ((Elf_Addr*)segbase)[0] = segbase; 216133754Sdfr ((Elf_Addr*)segbase)[1] = (Elf_Addr) dtv; 217133754Sdfr 218133754Sdfr dtv[0] = 1; 219133754Sdfr dtv[1] = 1; 220133754Sdfr dtv[2] = segbase - tls_static_space; 221133754Sdfr 222133754Sdfr if (oldtls) { 223133754Sdfr /* 224133754Sdfr * Copy the static TLS block over whole. 225133754Sdfr */ 226133754Sdfr oldsegbase = (Elf_Addr) oldtls; 227133754Sdfr memcpy((void *)(segbase - tls_static_space), 228133754Sdfr (const void *)(oldsegbase - tls_static_space), 229133754Sdfr tls_static_space); 230133754Sdfr 231133754Sdfr /* 232133754Sdfr * We assume that this block was the one we created with 233133754Sdfr * allocate_initial_tls(). 234133754Sdfr */ 235133754Sdfr _rtld_free_tls(oldtls, 2*sizeof(Elf_Addr), sizeof(Elf_Addr)); 236133754Sdfr } else { 237133754Sdfr memcpy((void *)(segbase - tls_static_space), 238133754Sdfr tls_init, tls_init_size); 239133754Sdfr memset((void *)(segbase - tls_static_space + tls_init_size), 240133754Sdfr 0, tls_static_space - tls_init_size); 241133754Sdfr } 242133754Sdfr 243133754Sdfr return (void*) segbase; 244143921Sdavidxu} 245143921Sdavidxu 246143921Sdavidxu#endif /* TLS_VARIANT_II */ 247143921Sdavidxu 248133754Sdfr#else 249143921Sdavidxu 250143921Sdavidxuvoid * 251143921Sdavidxu__libc_allocate_tls(void *oldtls __unused, size_t tcbsize __unused, 252143921Sdavidxu size_t tcbalign __unused) 253143921Sdavidxu{ 254133754Sdfr return (0); 255133754Sdfr} 256133754Sdfr 257143921Sdavidxuvoid 258143921Sdavidxu__libc_free_tls(void *tcb __unused, size_t tcbsize __unused, 259143921Sdavidxu size_t tcbalign __unused) 260143921Sdavidxu{ 261143921Sdavidxu} 262133754Sdfr 263143921Sdavidxu#endif /* PIC */ 264143921Sdavidxu 265143921Sdavidxuextern char **environ; 266143921Sdavidxu 267133754Sdfrvoid 268133754Sdfr_init_tls() 269133754Sdfr{ 270133754Sdfr#ifndef PIC 271133754Sdfr Elf_Addr *sp; 272133754Sdfr Elf_Auxinfo *aux, *auxp; 273133754Sdfr Elf_Phdr *phdr; 274133754Sdfr size_t phent, phnum; 275133754Sdfr int i; 276133949Sdfr void *tls; 277133754Sdfr 278133754Sdfr sp = (Elf_Addr *) environ; 279133754Sdfr while (*sp++ != 0) 280133754Sdfr ; 281133754Sdfr aux = (Elf_Auxinfo *) sp; 282133754Sdfr phdr = 0; 283133754Sdfr phent = phnum = 0; 284133754Sdfr for (auxp = aux; auxp->a_type != AT_NULL; auxp++) { 285133754Sdfr switch (auxp->a_type) { 286133754Sdfr case AT_PHDR: 287133754Sdfr phdr = auxp->a_un.a_ptr; 288133754Sdfr break; 289133754Sdfr 290133754Sdfr case AT_PHENT: 291133754Sdfr phent = auxp->a_un.a_val; 292133754Sdfr break; 293133754Sdfr 294133754Sdfr case AT_PHNUM: 295133754Sdfr phnum = auxp->a_un.a_val; 296133754Sdfr break; 297133754Sdfr } 298133754Sdfr } 299133754Sdfr if (phdr == 0 || phent != sizeof(Elf_Phdr) || phnum == 0) 300133754Sdfr return; 301133754Sdfr 302143921Sdavidxu for (i = 0; (unsigned) i < phnum; i++) { 303133754Sdfr if (phdr[i].p_type == PT_TLS) { 304133754Sdfr tls_static_space = round(phdr[i].p_memsz, 305133754Sdfr phdr[i].p_align); 306133754Sdfr tls_init_size = phdr[i].p_filesz; 307133754Sdfr tls_init = (void*) phdr[i].p_vaddr; 308133754Sdfr } 309133754Sdfr } 310133754Sdfr 311163118Skmacy tls = _rtld_allocate_tls(NULL, TLS_TCB_SIZE, TLS_TCB_ALIGN); 312133754Sdfr 313133949Sdfr _set_tp(tls); 314133754Sdfr#endif 315133754Sdfr} 316