On Sun, 5 Nov 2017, Stuart Henderson wrote:
> On 2017/11/05 21:08, Charles Collicutt wrote:
> > Hello,
> > 
> > I have a program that uses Thread-Local Storage (TLS) with the 'Local Exec'
> > access model [1] on AMD64. This looks like it should work on OpenBSD and,
> > indeed, it mostly does.
> 
> OpenBSD doesn't have real thread-local storage yet.

Well, ld.so and libc _should_ currently support startup-time TLS using the 
initial-exec and local-exec modules.

The problem with static non-PIE executables is that we don't pass an AUX 
vector to such processes, so the startup code can't find the TLS segment 
and doesn't leave any space for it.  On a variant II arch like amd64, the 
negative offset of a TLS variable results in accessing before the 
beginning of the page the TCB allocation is on, thus the fault.

The diff below fixes that, at least on amd64, by checking whether no 
AUX_phdr value was found and, if so, trying to instead find them via the 
ELF header referenced via the linker-provided __executable_start symbol. 
That's the second and third chunks below.

The first and fourth chunks correct the sizing of the allocation with the 
requested alignment was less than the natural alignment of the TIB itself,
resulting in a misaligned TIB.

The last chunk correctly relocates the pointer to the TLS initialization 
data (the .tdata segment), so that initialized TLS data works in static 
executables.


This will obviously require some testing...


Philip


Index: dlfcn/init.c
===================================================================
RCS file: /data/src/openbsd/src/lib/libc/dlfcn/init.c,v
retrieving revision 1.5
diff -u -p -r1.5 init.c
--- dlfcn/init.c        6 Sep 2016 18:49:34 -0000       1.5
+++ dlfcn/init.c        5 Nov 2017 21:02:13 -0000
@@ -34,6 +34,8 @@
 
 #include "init.h"
 
+#define MAX(a,b)       (((a)>(b))?(a):(b))
+
 /* XXX should be in an include file shared with csu */
 char   ***_csu_finish(char **_argv, char **_envp, void (*_cleanup)(void));
 
@@ -53,8 +55,10 @@ struct dl_phdr_info  _static_phdr_info = 
 
 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 +103,15 @@ _csu_finish(char **argv, char **envp, vo
        }
 
 #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);
@@ -169,13 +182,22 @@ setup_static_tib(Elf_Phdr *phdr, int phn
 #elif TLS_VARIANT == 2
                        /*
                         * variant 2 places the data before the TIB
-                        * so we need to round up to the alignment
+                        * so we need to round up the size to the
+                        * larger of the TLS data alignment and the
+                        * 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);
+                           MAX(__alignof__(struct tib), 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 = (void *)phdr[i].p_vaddr +
+                                   _static_phdr_info.dlpi_addr;
                                static_tls_fsize = phdr[i].p_filesz;
                        }
                        break;

Reply via email to