1 /* $OpenBSD: init.c,v 1.24 2024/07/22 22:06:27 kettenis Exp $ */ 2 /* 3 * Copyright (c) 2014,2015 Philip Guenther <guenther@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 19 #define _DYN_LOADER 20 21 #include <sys/types.h> 22 #include <sys/syscall.h> 23 #include <sys/timetc.h> /* timekeep */ 24 25 #ifndef PIC 26 #include <sys/mman.h> 27 #endif 28 29 #include <tib.h> 30 #include <limits.h> /* NAME_MAX */ 31 #include <link.h> 32 #include <stdlib.h> /* atexit */ 33 #include <string.h> 34 #include <unistd.h> 35 36 #include "init.h" 37 38 #define MAX(a,b) (((a)>(b))?(a):(b)) 39 40 #ifdef TIB_EXTRA_ALIGN 41 # define TIB_ALIGN MAX(__alignof__(struct tib), TIB_EXTRA_ALIGN) 42 #else 43 # define TIB_ALIGN __alignof__(struct tib) 44 #endif 45 46 /* XXX should be in an include file shared with csu */ 47 char ***_csu_finish(char **_argv, char **_envp, void (*_cleanup)(void)); 48 49 /* provide definitions for these */ 50 int _pagesize = 0; 51 struct timekeep *_timekeep; 52 unsigned long _hwcap, _hwcap2; 53 int _hwcap_avail, _hwcap2_avail; 54 55 /* 56 * In dynamically linked binaries environ and __progname are overridden by 57 * the definitions in ld.so. 58 */ 59 char **environ __attribute__((weak)) = NULL; 60 char *__progname __attribute__((weak)) = NULL; 61 62 63 #ifndef PIC 64 struct dl_phdr_info _static_phdr_info __relro = { .dlpi_name = "a.out" }; 65 66 static inline void early_static_init(char **_argv, char **_envp); 67 static inline void setup_static_tib(Elf_Phdr *_phdr, int _phnum); 68 69 /* provided by the linker */ 70 extern Elf_Ehdr __executable_start[] __attribute__((weak)); 71 #endif /* PIC */ 72 73 /* provide definitions for these */ 74 const dl_cb *_dl_cb __relro = NULL; 75 76 int HIDDEN(execve)(const char *, char *const *, char *const *) 77 __attribute__((weak)); 78 79 void _libc_preinit(int, char **, char **, dl_cb_cb *) __dso_hidden; 80 void 81 _libc_preinit(int argc, char **argv, char **envp, dl_cb_cb *cb) 82 { 83 AuxInfo *aux; 84 #ifndef PIC 85 Elf_Phdr *phdr = NULL; 86 int phnum = 0; 87 88 /* static libc in a static link? */ 89 if (cb == NULL) 90 early_static_init(argv, envp); 91 #endif /* !PIC */ 92 93 if (cb != NULL) 94 _dl_cb = cb(DL_CB_CUR); 95 96 /* Extract useful bits from the auxiliary vector */ 97 while (*envp++ != NULL) 98 ; 99 for (aux = (void *)envp; aux->au_id != AUX_null; aux++) { 100 switch (aux->au_id) { 101 case AUX_hwcap: 102 _hwcap = aux->au_v; 103 _hwcap_avail = 1; 104 break; 105 case AUX_hwcap2: 106 _hwcap2 = aux->au_v; 107 _hwcap2_avail = 1; 108 break; 109 case AUX_pagesz: 110 _pagesize = aux->au_v; 111 break; 112 #ifndef PIC 113 case AUX_base: 114 _static_phdr_info.dlpi_addr = aux->au_v; 115 break; 116 case AUX_phdr: 117 phdr = (void *)aux->au_v; 118 break; 119 case AUX_phnum: 120 phnum = aux->au_v; 121 break; 122 #endif /* !PIC */ 123 case AUX_openbsd_timekeep: 124 if (_tc_get_timecount) { 125 _timekeep = (void *)aux->au_v; 126 if (_timekeep && 127 _timekeep->tk_version != TK_VERSION) 128 _timekeep = NULL; 129 } 130 if (issetugid() == 0 && getenv("LIBC_NOUSERTC")) 131 _timekeep = NULL; 132 break; 133 } 134 } 135 136 #ifndef PIC 137 if (cb == NULL && phdr == NULL && __executable_start != NULL) { 138 /* 139 * Static non-PIE processes don't get an AUX vector, 140 * so find the phdrs through the ELF header 141 */ 142 phdr = (void *)((char *)__executable_start + 143 __executable_start->e_phoff); 144 phnum = __executable_start->e_phnum; 145 } 146 _static_phdr_info.dlpi_phdr = phdr; 147 _static_phdr_info.dlpi_phnum = phnum; 148 149 /* static libc in a static link? */ 150 if (cb == NULL) 151 setup_static_tib(phdr, phnum); 152 153 /* 154 * If a static binary has text relocations (DT_TEXT), then un-writeable 155 * segments were not made immutable by the kernel. Textrel and RELRO 156 * changes have now been completed and permissions corrected, so these 157 * regions can become immutable. 158 */ 159 if (phdr) { 160 int i; 161 162 for (i = 0; i < phnum; i++) { 163 if (phdr[i].p_type == PT_LOAD && 164 (phdr[i].p_flags & PF_W) == 0) 165 mimmutable((void *)(_static_phdr_info.dlpi_addr + 166 phdr[i].p_vaddr), phdr[i].p_memsz); 167 } 168 } 169 #endif /* !PIC */ 170 } 171 172 /* ARM just had to be different... */ 173 #ifndef __arm__ 174 # define TYPE "@" 175 #else 176 # define TYPE "%" 177 #endif 178 179 #ifdef __LP64__ 180 # define VALUE_ALIGN ".balign 8" 181 # define VALUE_DIRECTIVE ".quad" 182 #else 183 # define VALUE_ALIGN ".balign 4" 184 # ifdef __hppa__ 185 /* hppa just had to be different: func pointers prefix with 'P%' */ 186 # define VALUE_DIRECTIVE ".int P%" 187 # else 188 # define VALUE_DIRECTIVE ".int" 189 # endif 190 #endif 191 192 #ifdef PIC 193 /* 194 * Set a priority so _libc_preinit gets called before the constructor 195 * on libcompiler_rt that may use elf_aux_info(3). 196 */ 197 __asm(" .section .init_array.50,\"a\","TYPE"init_array\n " \ 198 VALUE_ALIGN"\n "VALUE_DIRECTIVE" _libc_preinit\n .previous"); 199 #else 200 __asm(" .section .preinit_array,\"a\","TYPE"preinit_array\n " \ 201 VALUE_ALIGN"\n "VALUE_DIRECTIVE" _libc_preinit\n .previous"); 202 #endif 203 204 /* 205 * In dynamic links, invoke ld.so's dl_clean_boot() callback, if any, 206 * and register its cleanup. 207 */ 208 char *** 209 _csu_finish(char **argv, char **envp, void (*cleanup)(void)) 210 { 211 if (_dl_cb != NULL && _dl_cb->dl_clean_boot != NULL) 212 _dl_cb->dl_clean_boot(); 213 214 if (cleanup != NULL) 215 atexit(cleanup); 216 217 return &environ; 218 } 219 220 #ifndef PIC 221 /* 222 * static libc in a static link? Then set up __progname and environ 223 */ 224 static inline void 225 early_static_init(char **argv, char **envp) 226 { 227 static char progname_storage[NAME_MAX+1]; 228 229 environ = envp; 230 231 /* set up __progname */ 232 if (*argv != NULL) { /* NULL ptr if argc = 0 */ 233 const char *p = strrchr(*argv, '/'); 234 235 if (p == NULL) 236 p = *argv; 237 else 238 p++; 239 strlcpy(progname_storage, p, sizeof(progname_storage)); 240 } 241 __progname = progname_storage; 242 } 243 244 /* 245 * static TLS handling 246 */ 247 #define ELF_ROUND(x,malign) (((x) + (malign)-1) & ~((malign)-1)) 248 249 /* for static binaries, the location and size of the TLS image */ 250 static void *static_tls __relro; 251 static size_t static_tls_fsize __relro; 252 253 size_t _static_tls_size __relro = 0; 254 int _static_tls_align __relro; 255 int _static_tls_align_offset __relro; 256 257 static inline void 258 setup_static_tib(Elf_Phdr *phdr, int phnum) 259 { 260 struct tib *tib; 261 char *base; 262 int i; 263 264 _static_tls_align = TIB_ALIGN; 265 if (phdr != NULL) { 266 for (i = 0; i < phnum; i++) { 267 if (phdr[i].p_type != PT_TLS) 268 continue; 269 if (phdr[i].p_memsz == 0) 270 break; 271 if (phdr[i].p_memsz < phdr[i].p_filesz) 272 break; /* invalid */ 273 if (phdr[i].p_align > getpagesize()) 274 break; /* nope */ 275 _static_tls_align = MAX(phdr[i].p_align, TIB_ALIGN); 276 #if TLS_VARIANT == 1 277 /* 278 * Variant 1 places the data after the TIB. If the 279 * TLS alignment is larger than the TIB alignment 280 * then we may need to pad in front of the TIB to 281 * place the TLS data on the proper alignment. 282 * Example: p_align=16 sizeof(TIB)=52 align(TIB)=4 283 * - need to offset the TIB 12 bytes from the start 284 * - to place ths TLS data at offset 64 285 */ 286 _static_tls_size = phdr[i].p_memsz; 287 _static_tls_align_offset = 288 ELF_ROUND(sizeof(struct tib), _static_tls_align) - 289 sizeof(struct tib); 290 #elif TLS_VARIANT == 2 291 /* 292 * Variant 2 places the data before the TIB 293 * so we need to round up the size to the 294 * TLS data alignment TIB's alignment. 295 * Example A: p_memsz=24 p_align=16 align(TIB)=8 296 * - need to allocate 32 bytes for TLS as compiler 297 * - will give the first TLS symbol an offset of -32 298 * Example B: p_memsz=4 p_align=4 align(TIB)=8 299 * - need to allocate 8 bytes so that the TIB is 300 * - properly aligned 301 */ 302 _static_tls_size = ELF_ROUND(phdr[i].p_memsz, 303 phdr[i].p_align); 304 _static_tls_align_offset = ELF_ROUND(_static_tls_size, 305 _static_tls_align) - _static_tls_size; 306 #endif 307 if (phdr[i].p_vaddr != 0 && phdr[i].p_filesz != 0) { 308 static_tls = (void *)phdr[i].p_vaddr + 309 _static_phdr_info.dlpi_addr; 310 static_tls_fsize = phdr[i].p_filesz; 311 } 312 break; 313 } 314 } 315 316 base = mmap(NULL, _static_tls_size + _static_tls_align_offset 317 + sizeof *tib, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0); 318 319 tib = _static_tls_init(base, NULL); 320 tib->tib_tid = getthrid(); 321 TCB_SET(TIB_TO_TCB(tib)); 322 #if ! TCB_HAVE_MD_GET 323 _libc_single_tcb = TIB_TO_TCB(tib); 324 #endif 325 } 326 327 struct tib * 328 _static_tls_init(char *base, void *thread) 329 { 330 struct tib *tib; 331 332 base += _static_tls_align_offset; 333 # if TLS_VARIANT == 1 334 tib = (struct tib *)base; 335 base += sizeof(struct tib); 336 # elif TLS_VARIANT == 2 337 tib = (struct tib *)(base + _static_tls_size); 338 # endif 339 340 if (_static_tls_size) { 341 if (static_tls != NULL) 342 memcpy(base, static_tls, static_tls_fsize); 343 memset(base + static_tls_fsize, 0, 344 _static_tls_size - static_tls_fsize); 345 } 346 347 TIB_INIT(tib, NULL, thread); 348 return tib; 349 } 350 #endif /* !PIC */ 351