1 /* $OpenBSD: init.c,v 1.22 2024/01/21 17:18:13 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 53 /* 54 * In dynamically linked binaries environ and __progname are overridden by 55 * the definitions in ld.so. 56 */ 57 char **environ __attribute__((weak)) = NULL; 58 char *__progname __attribute__((weak)) = NULL; 59 60 61 #ifndef PIC 62 struct dl_phdr_info _static_phdr_info __relro = { .dlpi_name = "a.out" }; 63 64 static inline void early_static_init(char **_argv, char **_envp); 65 static inline void setup_static_tib(Elf_Phdr *_phdr, int _phnum); 66 67 /* provided by the linker */ 68 extern Elf_Ehdr __executable_start[] __attribute__((weak)); 69 #endif /* PIC */ 70 71 /* provide definitions for these */ 72 const dl_cb *_dl_cb __relro = NULL; 73 74 int HIDDEN(execve)(const char *, char *const *, char *const *) 75 __attribute__((weak)); 76 77 void _libc_preinit(int, char **, char **, dl_cb_cb *) __dso_hidden; 78 void 79 _libc_preinit(int argc, char **argv, char **envp, dl_cb_cb *cb) 80 { 81 AuxInfo *aux; 82 #ifndef PIC 83 Elf_Phdr *phdr = NULL; 84 int phnum = 0; 85 86 /* static libc in a static link? */ 87 if (cb == NULL) 88 early_static_init(argv, envp); 89 #endif /* !PIC */ 90 91 if (cb != NULL) 92 _dl_cb = cb(DL_CB_CUR); 93 94 /* Extract useful bits from the auxiliary vector */ 95 while (*envp++ != NULL) 96 ; 97 for (aux = (void *)envp; aux->au_id != AUX_null; aux++) { 98 switch (aux->au_id) { 99 case AUX_pagesz: 100 _pagesize = aux->au_v; 101 break; 102 #ifndef PIC 103 case AUX_base: 104 _static_phdr_info.dlpi_addr = aux->au_v; 105 break; 106 case AUX_phdr: 107 phdr = (void *)aux->au_v; 108 break; 109 case AUX_phnum: 110 phnum = aux->au_v; 111 break; 112 #endif /* !PIC */ 113 case AUX_openbsd_timekeep: 114 if (_tc_get_timecount) { 115 _timekeep = (void *)aux->au_v; 116 if (_timekeep && 117 _timekeep->tk_version != TK_VERSION) 118 _timekeep = NULL; 119 } 120 if (issetugid() == 0 && getenv("LIBC_NOUSERTC")) 121 _timekeep = NULL; 122 break; 123 } 124 } 125 126 #ifndef PIC 127 if (cb == NULL && phdr == NULL && __executable_start != NULL) { 128 /* 129 * Static non-PIE processes don't get an AUX vector, 130 * so find the phdrs through the ELF header 131 */ 132 phdr = (void *)((char *)__executable_start + 133 __executable_start->e_phoff); 134 phnum = __executable_start->e_phnum; 135 } 136 _static_phdr_info.dlpi_phdr = phdr; 137 _static_phdr_info.dlpi_phnum = phnum; 138 139 /* static libc in a static link? */ 140 if (cb == NULL) 141 setup_static_tib(phdr, phnum); 142 143 /* 144 * If a static binary has text relocations (DT_TEXT), then un-writeable 145 * segments were not made immutable by the kernel. Textrel and RELRO 146 * changes have now been completed and permissions corrected, so these 147 * regions can become immutable. 148 */ 149 if (phdr) { 150 int i; 151 152 for (i = 0; i < phnum; i++) { 153 if (phdr[i].p_type == PT_LOAD && 154 (phdr[i].p_flags & PF_W) == 0) 155 mimmutable((void *)(_static_phdr_info.dlpi_addr + 156 phdr[i].p_vaddr), phdr[i].p_memsz); 157 } 158 } 159 #endif /* !PIC */ 160 } 161 162 /* ARM just had to be different... */ 163 #ifndef __arm__ 164 # define TYPE "@" 165 #else 166 # define TYPE "%" 167 #endif 168 169 #ifdef __LP64__ 170 # define VALUE_ALIGN ".balign 8" 171 # define VALUE_DIRECTIVE ".quad" 172 #else 173 # define VALUE_ALIGN ".balign 4" 174 # ifdef __hppa__ 175 /* hppa just had to be different: func pointers prefix with 'P%' */ 176 # define VALUE_DIRECTIVE ".int P%" 177 # else 178 # define VALUE_DIRECTIVE ".int" 179 # endif 180 #endif 181 182 #define ADD_TO_ARRAY(func, which) \ 183 __asm( " .section ."#which",\"a\","TYPE#which"\n " \ 184 VALUE_ALIGN"\n "VALUE_DIRECTIVE" "#func"\n .previous") 185 186 #ifdef PIC 187 ADD_TO_ARRAY(_libc_preinit, init_array); 188 #else 189 ADD_TO_ARRAY(_libc_preinit, preinit_array); 190 #endif 191 192 /* 193 * In dynamic links, invoke ld.so's dl_clean_boot() callback, if any, 194 * and register its cleanup. 195 */ 196 char *** 197 _csu_finish(char **argv, char **envp, void (*cleanup)(void)) 198 { 199 if (_dl_cb != NULL && _dl_cb->dl_clean_boot != NULL) 200 _dl_cb->dl_clean_boot(); 201 202 if (cleanup != NULL) 203 atexit(cleanup); 204 205 return &environ; 206 } 207 208 #ifndef PIC 209 /* 210 * static libc in a static link? Then set up __progname and environ 211 */ 212 static inline void 213 early_static_init(char **argv, char **envp) 214 { 215 static char progname_storage[NAME_MAX+1]; 216 217 environ = envp; 218 219 /* set up __progname */ 220 if (*argv != NULL) { /* NULL ptr if argc = 0 */ 221 const char *p = strrchr(*argv, '/'); 222 223 if (p == NULL) 224 p = *argv; 225 else 226 p++; 227 strlcpy(progname_storage, p, sizeof(progname_storage)); 228 } 229 __progname = progname_storage; 230 } 231 232 /* 233 * static TLS handling 234 */ 235 #define ELF_ROUND(x,malign) (((x) + (malign)-1) & ~((malign)-1)) 236 237 /* for static binaries, the location and size of the TLS image */ 238 static void *static_tls __relro; 239 static size_t static_tls_fsize __relro; 240 241 size_t _static_tls_size __relro = 0; 242 int _static_tls_align __relro; 243 int _static_tls_align_offset __relro; 244 245 static inline void 246 setup_static_tib(Elf_Phdr *phdr, int phnum) 247 { 248 struct tib *tib; 249 char *base; 250 int i; 251 252 _static_tls_align = TIB_ALIGN; 253 if (phdr != NULL) { 254 for (i = 0; i < phnum; i++) { 255 if (phdr[i].p_type != PT_TLS) 256 continue; 257 if (phdr[i].p_memsz == 0) 258 break; 259 if (phdr[i].p_memsz < phdr[i].p_filesz) 260 break; /* invalid */ 261 if (phdr[i].p_align > getpagesize()) 262 break; /* nope */ 263 _static_tls_align = MAX(phdr[i].p_align, TIB_ALIGN); 264 #if TLS_VARIANT == 1 265 /* 266 * Variant 1 places the data after the TIB. If the 267 * TLS alignment is larger than the TIB alignment 268 * then we may need to pad in front of the TIB to 269 * place the TLS data on the proper alignment. 270 * Example: p_align=16 sizeof(TIB)=52 align(TIB)=4 271 * - need to offset the TIB 12 bytes from the start 272 * - to place ths TLS data at offset 64 273 */ 274 _static_tls_size = phdr[i].p_memsz; 275 _static_tls_align_offset = 276 ELF_ROUND(sizeof(struct tib), _static_tls_align) - 277 sizeof(struct tib); 278 #elif TLS_VARIANT == 2 279 /* 280 * Variant 2 places the data before the TIB 281 * so we need to round up the size to the 282 * TLS data alignment TIB's alignment. 283 * Example A: p_memsz=24 p_align=16 align(TIB)=8 284 * - need to allocate 32 bytes for TLS as compiler 285 * - will give the first TLS symbol an offset of -32 286 * Example B: p_memsz=4 p_align=4 align(TIB)=8 287 * - need to allocate 8 bytes so that the TIB is 288 * - properly aligned 289 */ 290 _static_tls_size = ELF_ROUND(phdr[i].p_memsz, 291 phdr[i].p_align); 292 _static_tls_align_offset = ELF_ROUND(_static_tls_size, 293 _static_tls_align) - _static_tls_size; 294 #endif 295 if (phdr[i].p_vaddr != 0 && phdr[i].p_filesz != 0) { 296 static_tls = (void *)phdr[i].p_vaddr + 297 _static_phdr_info.dlpi_addr; 298 static_tls_fsize = phdr[i].p_filesz; 299 } 300 break; 301 } 302 } 303 304 base = mmap(NULL, _static_tls_size + _static_tls_align_offset 305 + sizeof *tib, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0); 306 307 tib = _static_tls_init(base, NULL); 308 tib->tib_tid = getthrid(); 309 TCB_SET(TIB_TO_TCB(tib)); 310 #if ! TCB_HAVE_MD_GET 311 _libc_single_tcb = TIB_TO_TCB(tib); 312 #endif 313 } 314 315 struct tib * 316 _static_tls_init(char *base, void *thread) 317 { 318 struct tib *tib; 319 320 base += _static_tls_align_offset; 321 # if TLS_VARIANT == 1 322 tib = (struct tib *)base; 323 base += sizeof(struct tib); 324 # elif TLS_VARIANT == 2 325 tib = (struct tib *)(base + _static_tls_size); 326 # endif 327 328 if (_static_tls_size) { 329 if (static_tls != NULL) 330 memcpy(base, static_tls, static_tls_fsize); 331 memset(base + static_tls_fsize, 0, 332 _static_tls_size - static_tls_fsize); 333 } 334 335 TIB_INIT(tib, NULL, thread); 336 return tib; 337 } 338 #endif /* !PIC */ 339