diff options
Diffstat (limited to 'lib/libc/dlfcn/init.c')
-rw-r--r-- | lib/libc/dlfcn/init.c | 108 |
1 files changed, 105 insertions, 3 deletions
diff --git a/lib/libc/dlfcn/init.c b/lib/libc/dlfcn/init.c index 9e6af9d92f2..3147f9ba7f1 100644 --- a/lib/libc/dlfcn/init.c +++ b/lib/libc/dlfcn/init.c @@ -1,4 +1,4 @@ -/* $OpenBSD: init.c,v 1.3 2016/03/21 00:41:13 guenther Exp $ */ +/* $OpenBSD: init.c,v 1.4 2016/05/07 19:05:22 guenther Exp $ */ /* * Copyright (c) 2014,2015 Philip Guenther <guenther@openbsd.org> * @@ -22,10 +22,15 @@ #include <sys/exec_elf.h> #include <sys/syscall.h> +#ifndef PIC +#include <sys/mman.h> +#endif + +#include <tib.h> #include <limits.h> /* NAME_MAX */ #include <stdlib.h> /* atexit */ #include <string.h> -#include <unistd.h> /* _pagesize */ +#include <unistd.h> /* XXX should be in an include file shared with csu */ char ***_csu_finish(char **_argv, char **_envp, void (*_cleanup)(void)); @@ -43,6 +48,7 @@ char *__progname __attribute__((weak)) = NULL; #ifndef PIC static inline void early_static_init(char **_argv, char **_envp); +static inline void setup_static_tib(Elf_Phdr *_phdr, int _phnum); #endif /* PIC */ @@ -55,8 +61,10 @@ char *** _csu_finish(char **argv, char **envp, void (*cleanup)(void)) { AuxInfo *aux; - #ifndef PIC + Elf_Phdr *phdr = NULL; + int phnum = 0; + /* static libc in a static link? */ if (cleanup == NULL) early_static_init(argv, envp); @@ -70,9 +78,23 @@ _csu_finish(char **argv, char **envp, void (*cleanup)(void)) case AUX_pagesz: _pagesize = aux->au_v; break; +#ifndef PIC + case AUX_phdr: + phdr = (void *)aux->au_v; + break; + case AUX_phnum: + phnum = aux->au_v; + break; +#endif /* !PIC */ } } +#ifndef PIC + /* static libc in a static link? */ + if (cleanup == NULL) + setup_static_tib(phdr, phnum); +#endif /* !PIC */ + if (cleanup != NULL) atexit(cleanup); @@ -106,4 +128,84 @@ early_static_init(char **argv, char **envp) } __progname = progname_storage; } + +/* + * static TLS handling + */ +#define ELF_ROUND(x,malign) (((x) + (malign)-1) & ~((malign)-1)) + +/* for static binaries, the location and size of the TLS image */ +static void *static_tls; +static size_t static_tls_fsize; + +size_t _static_tls_size = 0; + +static inline void +setup_static_tib(Elf_Phdr *phdr, int phnum) +{ + struct tib *tib; + char *base; + int i; + + if (phdr != NULL) { + for (i = 0; i < phnum; i++) { + if (phdr[i].p_type != PT_TLS) + continue; + if (phdr[i].p_memsz == 0) + break; + if (phdr[i].p_memsz < phdr[i].p_filesz) + break; /* invalid */ +#if TLS_VARIANT == 1 + _static_tls_size = phdr[i].p_memsz; +#elif TLS_VARIANT == 2 + /* + * variant 2 places the data before the TIB + * so we need to round up to the alignment + */ + _static_tls_size = ELF_ROUND(phdr[i].p_memsz, + phdr[i].p_align); +#endif + if (phdr[i].p_vaddr != 0 && phdr[i].p_filesz != 0) { + static_tls = (void *)phdr[i].p_vaddr; + static_tls_fsize = phdr[i].p_filesz; + } + break; + } + } + + /* + * We call getpagesize() here instead of using _pagesize because + * there's no aux-vector in non-PIE static links, so _pagesize + * might not be set yet. If so getpagesize() will get the value. + */ + base = mmap(NULL, ELF_ROUND(_static_tls_size + sizeof *tib, + getpagesize()), PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0); +# if TLS_VARIANT == 1 + tib = (struct tib *)base; +# elif TLS_VARIANT == 2 + tib = (struct tib *)(base + _static_tls_size); +# endif + + _static_tls_init(base); + TIB_INIT(tib, NULL, NULL); + tib->tib_tid = getthrid(); + TCB_SET(TIB_TO_TCB(tib)); +#if ! TCB_HAVE_MD_GET + _libc_single_tcb = TIB_TO_TCB(tib); +#endif +} + +void +_static_tls_init(char *base) +{ + if (_static_tls_size) { +#if TLS_VARIANT == 1 + base += sizeof(struct tib); +#endif + if (static_tls != NULL) + memcpy(base, static_tls, static_tls_fsize); + memset(base + static_tls_fsize, 0, + _static_tls_size - static_tls_fsize); + } +} #endif /* !PIC */ |