1 /*- 2 * Copyright (c) 2004 Doug Rabson 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD: src/lib/libc/gen/tls.c,v 1.7 2005/03/01 23:42:00 davidxu Exp $ 27 * $DragonFly: src/lib/libc/gen/tls.c,v 1.4 2005/03/28 03:33:12 dillon Exp $ 28 */ 29 30 /* 31 * Define stubs for TLS internals so that programs and libraries can 32 * link. These functions will be replaced by functional versions at 33 * runtime from ld-elf.so.1. 34 */ 35 36 #include <sys/cdefs.h> 37 #include <sys/tls.h> 38 #include <stdlib.h> 39 #include <string.h> 40 #include <elf.h> 41 #include <assert.h> 42 43 #include "libc_private.h" 44 45 __weak_reference(__libc_allocate_tls, _rtld_allocate_tls); 46 __weak_reference(__libc_free_tls, _rtld_free_tls); 47 #ifdef __i386__ 48 __weak_reference(___libc_tls_get_addr, ___tls_get_addr); 49 #endif 50 __weak_reference(__libc_tls_get_addr, __tls_get_addr); 51 52 struct tls_tcb *_rtld_allocate_tls(struct tls_tcb *, size_t tcbsize, int flags); 53 void _rtld_free_tls(struct tls_tcb *tcb, size_t tcb_size); 54 struct tls_tcb *__libc_allocate_tls(struct tls_tcb *old_tcb, size_t tcbsize, 55 int flags); 56 void __libc_free_tls(struct tls_tcb *tcb, size_t tcb_size); 57 58 #if defined(__ia64__) || defined(__powerpc__) 59 #define TLS_VARIANT_I 60 #endif 61 #if defined(__i386__) || defined(__amd64__) || defined(__sparc64__) || \ 62 defined(__arm__) 63 #define TLS_VARIANT_II 64 #endif 65 66 #ifndef PIC 67 68 #define round(size, align) \ 69 (((size) + (align) - 1) & ~((align) - 1)) 70 71 static size_t tls_static_space; 72 static size_t tls_init_size; 73 static void *tls_init; 74 #endif 75 76 #ifdef __i386__ 77 78 /* GNU ABI */ 79 80 void *___libc_tls_get_addr(void *ti) __attribute__((__regparm__(1))); 81 82 __attribute__((__regparm__(1))) 83 void * 84 ___libc_tls_get_addr(void *ti __unused) 85 { 86 return (0); 87 } 88 89 #endif 90 91 void *__libc_tls_get_addr(void *ti); 92 93 void * 94 __libc_tls_get_addr(void *ti __unused) 95 { 96 return (0); 97 } 98 99 #ifndef PIC 100 101 /* 102 * Free Static TLS 103 */ 104 void 105 __libc_free_tls(struct tls_tcb *tcb, size_t tcb_size __unused) 106 { 107 size_t data_size; 108 109 if (tcb->dtv_base) 110 free(tcb->dtv_base); 111 data_size = (tls_static_space + RTLD_STATIC_TLS_ALIGN_MASK) & 112 ~RTLD_STATIC_TLS_ALIGN_MASK; 113 free((char *)tcb - data_size); 114 } 115 116 /* 117 * Allocate Static TLS. 118 */ 119 struct tls_tcb * 120 __libc_allocate_tls(struct tls_tcb *old_tcb, size_t tcb_size, int flags) 121 { 122 size_t data_size; 123 struct tls_tcb *tcb; 124 Elf_Addr *dtv; 125 126 data_size = (tls_static_space + RTLD_STATIC_TLS_ALIGN_MASK) & 127 ~RTLD_STATIC_TLS_ALIGN_MASK; 128 tcb = malloc(data_size + tcb_size); 129 tcb = (struct tls_tcb *)((char *)tcb + data_size); 130 bzero(tcb, tcb_size); 131 dtv = malloc(3 * sizeof(Elf_Addr)); 132 tcb->tcb_base = tcb; 133 tcb->dtv_base = dtv; 134 135 dtv[0] = 1; 136 dtv[1] = 1; 137 dtv[2] = (Elf_Addr)((char *)tcb - tls_static_space); 138 139 if (old_tcb) { 140 /* 141 * Copy the static TLS block over whole. 142 */ 143 memcpy((char *)tcb - tls_static_space, 144 (char *)old_tcb - tls_static_space, 145 tls_static_space); 146 147 /* 148 * We assume that this block was the one we created with 149 * allocate_initial_tls(). 150 */ 151 _rtld_free_tls(old_tcb, sizeof(struct tls_tcb)); 152 } else { 153 memcpy((char *)tcb - tls_static_space, 154 tls_init, tls_init_size); 155 memset((char *)tcb - tls_static_space + tls_init_size, 156 0, tls_static_space - tls_init_size); 157 } 158 return (tcb); 159 } 160 161 #else 162 163 struct tls_tcb * 164 __libc_allocate_tls(struct tls_tcb *old_tls __unused, size_t tcb_size __unused, 165 int flags __unused) 166 { 167 return (0); 168 } 169 170 void 171 __libc_free_tls(struct tls_tcb *tcb __unused, size_t tcb_size __unused) 172 { 173 } 174 175 #endif /* PIC */ 176 177 extern char **environ; 178 179 void 180 _init_tls() 181 { 182 #ifndef PIC 183 Elf_Addr *sp; 184 Elf_Auxinfo *aux, *auxp; 185 Elf_Phdr *phdr; 186 size_t phent, phnum; 187 int i; 188 struct tls_tcb *tcb; 189 190 sp = (Elf_Addr *) environ; 191 while (*sp++ != 0) 192 ; 193 aux = (Elf_Auxinfo *) sp; 194 phdr = 0; 195 phent = phnum = 0; 196 for (auxp = aux; auxp->a_type != AT_NULL; auxp++) { 197 switch (auxp->a_type) { 198 case AT_PHDR: 199 phdr = auxp->a_un.a_ptr; 200 break; 201 202 case AT_PHENT: 203 phent = auxp->a_un.a_val; 204 break; 205 206 case AT_PHNUM: 207 phnum = auxp->a_un.a_val; 208 break; 209 } 210 } 211 if (phdr == 0 || phent != sizeof(Elf_Phdr) || phnum == 0) 212 return; 213 214 for (i = 0; (unsigned)i < phnum; i++) { 215 if (phdr[i].p_type == PT_TLS) { 216 tls_static_space = round(phdr[i].p_memsz, 217 phdr[i].p_align); 218 tls_init_size = phdr[i].p_filesz; 219 tls_init = (void*) phdr[i].p_vaddr; 220 } 221 } 222 223 if (tls_static_space) { 224 tcb = _rtld_allocate_tls(NULL, sizeof(struct tls_tcb), 0); 225 _set_tp(tcb, (size_t)-1); 226 } 227 #endif 228 } 229