diff options
author | Philip Guenther <guenther@cvs.openbsd.org> | 2017-12-01 23:30:06 +0000 |
---|---|---|
committer | Philip Guenther <guenther@cvs.openbsd.org> | 2017-12-01 23:30:06 +0000 |
commit | 510705aa85a5914ad9570dc6e7c5145a4b6c399f (patch) | |
tree | dd2fc4c50d9e4c478db4ce70e93a86e81e3c2293 /lib/libc | |
parent | e9b4c199f94473f7595bfea33e4bd18a60939874 (diff) |
Redo the calculation of the alignment and placement of static TLS data to
correctly take into account the segment p_align. Previously, anything
with a size belong the natural alignment or with alignment larger than
the natural one would either not be intialized correctly, be misaligned,
or result in the TIB being misaligned.
Problems reported by Charles Collicutt (charles (at) collicutt.co.uk)
ok kettenis@
Diffstat (limited to 'lib/libc')
-rw-r--r-- | lib/libc/dlfcn/init.c | 94 | ||||
-rw-r--r-- | lib/libc/dlfcn/tib.c | 40 | ||||
-rw-r--r-- | lib/libc/hidden/tib.h | 16 |
3 files changed, 105 insertions, 45 deletions
diff --git a/lib/libc/dlfcn/init.c b/lib/libc/dlfcn/init.c index c791c88610e..3c387a60d6b 100644 --- a/lib/libc/dlfcn/init.c +++ b/lib/libc/dlfcn/init.c @@ -1,4 +1,4 @@ -/* $OpenBSD: init.c,v 1.5 2016/09/06 18:49:34 guenther Exp $ */ +/* $OpenBSD: init.c,v 1.6 2017/12/01 23:30:05 guenther Exp $ */ /* * Copyright (c) 2014,2015 Philip Guenther <guenther@openbsd.org> * @@ -34,6 +34,14 @@ #include "init.h" +#define MAX(a,b) (((a)>(b))?(a):(b)) + +#ifdef TIB_EXTRA_ALIGN +# define TIB_ALIGN MAX(__alignof__(struct tib), TIB_EXTRA_ALIGN) +#else +# define TIB_ALIGN __alignof__(struct tib) +#endif + /* XXX should be in an include file shared with csu */ char ***_csu_finish(char **_argv, char **_envp, void (*_cleanup)(void)); @@ -53,8 +61,10 @@ struct dl_phdr_info _static_phdr_info = { .dlpi_name = "a.out" }; static inline void early_static_init(char **_argv, char **_envp); static inline void setup_static_tib(Elf_Phdr *_phdr, int _phnum); -#endif /* PIC */ +/* provided by the linker */ +extern Elf_Ehdr __executable_start[] __attribute__((weak)); +#endif /* PIC */ /* * extract useful bits from the auxiliary vector and either @@ -99,6 +109,15 @@ _csu_finish(char **argv, char **envp, void (*cleanup)(void)) } #ifndef PIC + if (cleanup == NULL && phdr == NULL && __executable_start != NULL) { + /* + * Static non-PIE processes don't get an AUX vector, + * so find the phdrs through the ELF header + */ + phdr = (void *)((char *)__executable_start + + __executable_start->e_phoff); + phnum = __executable_start->e_phnum; + } /* static libc in a static link? */ if (cleanup == NULL) setup_static_tib(phdr, phnum); @@ -148,6 +167,8 @@ static void *static_tls; static size_t static_tls_fsize; size_t _static_tls_size = 0; +int _static_tls_align; +int _static_tls_align_offset; static inline void setup_static_tib(Elf_Phdr *phdr, int phnum) @@ -156,6 +177,7 @@ setup_static_tib(Elf_Phdr *phdr, int phnum) char *base; int i; + _static_tls_align = TIB_ALIGN; if (phdr != NULL) { for (i = 0; i < phnum; i++) { if (phdr[i].p_type != PT_TLS) @@ -164,39 +186,53 @@ setup_static_tib(Elf_Phdr *phdr, int phnum) break; if (phdr[i].p_memsz < phdr[i].p_filesz) break; /* invalid */ + if (phdr[i].p_align > getpagesize()) + break; /* nope */ + _static_tls_align = MAX(phdr[i].p_align, TIB_ALIGN); #if TLS_VARIANT == 1 + /* + * Variant 1 places the data after the TIB. If the + * TLS alignment is larger than the TIB alignment + * then we may need to pad in front of the TIB to + * place the TLS data on the proper alignment. + * Example: p_align=16 sizeof(TIB)=52 align(TIB)=4 + * - need to offset the TIB 12 bytes from the start + * - to place ths TLS data at offset 64 + */ _static_tls_size = phdr[i].p_memsz; + _static_tls_align_offset = + ELF_ROUND(sizeof(struct tib), _static_tls_align) - + sizeof(struct tib); #elif TLS_VARIANT == 2 /* - * variant 2 places the data before the TIB - * so we need to round up to the alignment + * Variant 2 places the data before the TIB + * so we need to round up the size to the + * TLS data alignment TIB's alignment. + * Example A: p_memsz=24 p_align=16 align(TIB)=8 + * - need to allocate 32 bytes for TLS as compiler + * - will give the first TLS symbol an offset of -32 + * Example B: p_memsz=4 p_align=4 align(TIB)=8 + * - need to allocate 8 bytes so that the TIB is + * - properly aligned */ _static_tls_size = ELF_ROUND(phdr[i].p_memsz, phdr[i].p_align); + _static_tls_align_offset = ELF_ROUND(_static_tls_size, + _static_tls_align) - _static_tls_size; #endif if (phdr[i].p_vaddr != 0 && phdr[i].p_filesz != 0) { - static_tls = (void *)phdr[i].p_vaddr; + static_tls = (void *)phdr[i].p_vaddr + + _static_phdr_info.dlpi_addr; 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 + base = mmap(NULL, _static_tls_size + _static_tls_align_offset + + sizeof *tib, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0); - _static_tls_init(base); - TIB_INIT(tib, NULL, NULL); + tib = _static_tls_init(base, NULL); tib->tib_tid = getthrid(); TCB_SET(TIB_TO_TCB(tib)); #if ! TCB_HAVE_MD_GET @@ -204,17 +240,27 @@ setup_static_tib(Elf_Phdr *phdr, int phnum) #endif } -void -_static_tls_init(char *base) +struct tib * +_static_tls_init(char *base, void *thread) { + struct tib *tib; + + base += _static_tls_align_offset; +# if TLS_VARIANT == 1 + tib = (struct tib *)base; + base += sizeof(struct tib); +# elif TLS_VARIANT == 2 + tib = (struct tib *)(base + _static_tls_size); +# endif + 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); } + + TIB_INIT(tib, NULL, thread); + return tib; } #endif /* !PIC */ diff --git a/lib/libc/dlfcn/tib.c b/lib/libc/dlfcn/tib.c index 4aba706e363..3b437c18251 100644 --- a/lib/libc/dlfcn/tib.c +++ b/lib/libc/dlfcn/tib.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tib.c,v 1.1 2016/05/07 19:05:22 guenther Exp $ */ +/* $OpenBSD: tib.c,v 1.2 2017/12/01 23:30:05 guenther Exp $ */ /* * Copyright (c) 2016 Philip Guenther <guenther@openbsd.org> * @@ -18,7 +18,7 @@ #include <tib.h> #ifndef PIC -# include <stdlib.h> /* malloc and free */ +# include <stdlib.h> /* posix_memalign and free */ #endif #define ELF_ROUND(x,malign) (((x) + (malign)-1) & ~((malign)-1)) @@ -43,29 +43,31 @@ _dl_allocate_tib(size_t extra) #ifdef PIC return NULL; /* overriden by ld.so */ #else - char *base; + void *base; char *thread; - struct tib *tib; # if TLS_VARIANT == 1 - /* round up the extra size to align the tib after it */ - extra = ELF_ROUND(extra, sizeof(void *)); - base = malloc(extra + sizeof(struct tib) + _static_tls_size); - tib = (struct tib *)(base + extra); + /* round up the extra size to align the TIB and TLS data after it */ + extra = (extra <= _static_tls_align_offset) ? 0 : + ELF_ROUND(extra - _static_tls_align_offset, _static_tls_align); + if (posix_memalign(&base, _static_tls_align, extra + + _static_tls_align_offset + sizeof(struct tib) + + _static_tls_size) != 0) + return NULL; thread = base; + base = (char *)base + extra; # elif TLS_VARIANT == 2 - /* round up the tib size to align the extra area after it */ - base = malloc(ELF_ROUND(sizeof(struct tib), TIB_EXTRA_ALIGN) + - extra + _static_tls_size); - tib = (struct tib *)(base + _static_tls_size); - thread = (char *)tib + ELF_ROUND(sizeof(struct tib), TIB_EXTRA_ALIGN); + /* round up the TIB size to align the extra area after it */ + if (posix_memalign(&base, _static_tls_align, + _static_tls_align_offset + _static_tls_size + + ELF_ROUND(sizeof(struct tib), TIB_EXTRA_ALIGN) + extra) != 0) + return NULL; + thread = (char *)base + _static_tls_align_offset + _static_tls_size + + ELF_ROUND(sizeof(struct tib), TIB_EXTRA_ALIGN); # endif - _static_tls_init(base); - TIB_INIT(tib, NULL, thread); - - return (tib); + return _static_tls_init(base, thread); #endif /* !PIC */ } @@ -76,10 +78,12 @@ _dl_free_tib(void *tib, size_t extra) size_t tib_offset; # if TLS_VARIANT == 1 - tib_offset = ELF_ROUND(extra, sizeof(void *)); + tib_offset = (extra <= _static_tls_align_offset) ? 0 : + ELF_ROUND(extra - _static_tls_align_offset, _static_tls_align); # elif TLS_VARIANT == 2 tib_offset = _static_tls_size; # endif + tib_offset += _static_tls_align_offset; free((char *)tib - tib_offset); #endif /* !PIC */ diff --git a/lib/libc/hidden/tib.h b/lib/libc/hidden/tib.h index 49a562dbad9..9c4be95fb96 100644 --- a/lib/libc/hidden/tib.h +++ b/lib/libc/hidden/tib.h @@ -1,4 +1,4 @@ -/* $OpenBSD: tib.h,v 1.1 2016/05/07 19:05:22 guenther Exp $ */ +/* $OpenBSD: tib.h,v 1.2 2017/12/01 23:30:05 guenther Exp $ */ /* * Copyright (c) 2015 Philip Guenther <guenther@openbsd.org> * @@ -23,10 +23,20 @@ __BEGIN_HIDDEN_DECLS #ifndef PIC -void _static_tls_init(char *_base); +/* + * Handling for static TLS allocation in staticly linked programs + */ +/* Given the base of a TIB allocation, initialize the static TLS for a thread */ +struct tib *_static_tls_init(char *_base, void *_thread); -/* size of static TLS allocation in staticly linked programs */ +/* size of static TLS allocation */ extern size_t _static_tls_size; + +/* alignment of static TLS allocation */ +extern int _static_tls_align; + +/* base-offset alignment of static TLS allocation */ +extern int _static_tls_align_offset; #endif #if ! TCB_HAVE_MD_GET |