When building glibc PIE (which is not something upstream support), several modifications are necessary to the glibc build process. First, any syscalls in PIEs must be of the PIC variant, otherwise textrels ensue. Then, any syscalls made before the initialisation of the TLS will fail on i386, as the sysenter variant on i386 uses the TLS, giving rise to a chicken-and-egg situation. This patch defines a PIC syscall variant that doesn't use sysenter, even when the sysenter version is normally used, and uses the non-sysenter version for the brk syscall that is performed by the TLS initialisation. Further, the TLS initialisation is moved in this case prior to the initialisation of dl_osversion, as that requires further syscalls. csu/libc-start.c: Move initial TLS initialization to before the initialisation of dl_osversion, when INTERNAL_SYSCALL_NOSYSENTER is defined csu/libc-tls.c: Use the no-sysenter version of sbrk when INTERNAL_SYSCALL_NOSYSENTER is defined. misc/sbrk.c: Define a no-sysenter version of sbrk, using the no-sysenter version of brk - if INTERNAL_SYSCALL_NOSYSENTER is defined. misc/brk.c: Define a no-sysenter version of brk if INTERNAL_SYSCALL_NOSYSENTER is defined. sysdeps/unix/sysv/linux/i386/sysdep.h: Define INTERNAL_SYSCALL_NOSYSENTER Make INTERNAL_SYSCALL always use the PIC variant, even if not SHARED. Patch by Kevin F. Quinn Fixed for 2.10 by Magnus Granberg Fixed for 2.18 by Magnus Granberg --- csu/libc-start.c +++ csu/libc-start.c @@ -28,6 +28,7 @@ extern int __libc_multiple_libcs; #include +#include #ifndef SHARED # include extern void __pthread_initialize_minimal (void); @@ -170,7 +170,11 @@ LIBC_START_MAIN (int (*main) (int, char GL(dl_phnum) = __ehdr_start.e_phnum; } } - +# ifdef INTERNAL_SYSCALL_NOSYSENTER + /* Do the initial TLS initialization before _dl_osversion, + since the latter uses the uname syscall. */ + __pthread_initialize_minimal (); +# endif # ifdef DL_SYSDEP_OSCHECK if (!__libc_multiple_libcs) { @@ -138,10 +144,12 @@ } # endif +# ifndef INTERNAL_SYSCALL_NOSYSENTER /* Initialize the thread library at least a bit since the libgcc functions are using thread functions if these are available and we need to setup errno. */ __pthread_initialize_minimal (); +# endif /* Set up the stack checker's canary. */ uintptr_t stack_chk_guard = _dl_setup_stack_chk_guard (); --- csu/libc-tls.c +++ csu/libc-tls.c @@ -22,14 +22,17 @@ #include #include #include - +#include #ifdef SHARED #error makefile bug, this file is for static only #endif -dtv_t _dl_static_dtv[2 + TLS_SLOTINFO_SURPLUS]; +#ifdef INTERNAL_SYSCALL_NOSYSENTER +extern void *__sbrk_nosysenter (intptr_t __delta); +#endif +dtv_t _dl_static_dtv[2 + TLS_SLOTINFO_SURPLUS]; static struct { @@ -139,14 +142,26 @@ __libc_setup_tls (size_t tcbsize, size_t The initialized value of _dl_tls_static_size is provided by dl-open.c to request some surplus that permits dynamic loading of modules with - IE-model TLS. */ + IE-model TLS. + + Where the normal sbrk would use a syscall that needs the TLS (i386) + use the special non-sysenter version instead. */ #if TLS_TCB_AT_TP tcb_offset = roundup (memsz + GL(dl_tls_static_size), tcbalign); +# ifdef INTERNAL_SYSCALL_NOSYSENTER + tlsblock = __sbrk_nosysenter (tcb_offset + tcbsize + max_align); +# else tlsblock = __sbrk (tcb_offset + tcbsize + max_align); +#endif #elif TLS_DTV_AT_TP tcb_offset = roundup (tcbsize, align ?: 1); +# ifdef INTERNAL_SYSCALL_NOSYSENTER + tlsblock = __sbrk_nosysenter (tcb_offset + memsz + max_align + + TLS_PRE_TCB_SIZE + GL(dl_tls_static_size)); +# else tlsblock = __sbrk (tcb_offset + memsz + max_align + TLS_PRE_TCB_SIZE + GL(dl_tls_static_size)); +#endif tlsblock += TLS_PRE_TCB_SIZE; #else /* In case a model with a different layout for the TCB and DTV --- misc/sbrk.c +++ misc/sbrk.c @@ -18,6 +18,7 @@ #include #include #include +#include /* Defined in brk.c. */ extern void *__curbrk; @@ -29,6 +30,35 @@ /* Extend the process's data space by INCREMENT. If INCREMENT is negative, shrink data space by - INCREMENT. Return start of new space allocated, or -1 for errors. */ +#ifdef INTERNAL_SYSCALL_NOSYSENTER +/* This version is used by csu/libc-tls.c whem initialising the TLS + if the SYSENTER version requires the TLS (which it does on i386). + Obviously using the TLS before it is initialised is broken. */ +extern int __brk_nosysenter (void *addr); +void * +__sbrk_nosysenter (intptr_t increment) +{ + void *oldbrk; + + /* If this is not part of the dynamic library or the library is used + via dynamic loading in a statically linked program update + __curbrk from the kernel's brk value. That way two separate + instances of __brk and __sbrk can share the heap, returning + interleaved pieces of it. */ + if (__curbrk == NULL || __libc_multiple_libcs) + if (__brk_nosysenter (0) < 0) /* Initialize the break. */ + return (void *) -1; + + if (increment == 0) + return __curbrk; + + oldbrk = __curbrk; + if (__brk_nosysenter (oldbrk + increment) < 0) + return (void *) -1; + + return oldbrk; +} +#endif void * __sbrk (intptr_t increment) { --- sysdeps/unix/sysv/linux/i386/brk.c +++ sysdeps/unix/sysv/linux/i386/brk.c @@ -31,6 +31,29 @@ linker. */ weak_alias (__curbrk, ___brk_addr) +#ifdef INTERNAL_SYSCALL_NOSYSENTER +/* This version is used by csu/libc-tls.c whem initialising the TLS + * if the SYSENTER version requires the TLS (which it does on i386). + * Obviously using the TLS before it is initialised is broken. */ +int +__brk_nosysenter (void *addr) +{ + void * newbrk; + + INTERNAL_SYSCALL_DECL (err); + newbrk = (void *) INTERNAL_SYSCALL_NOSYSENTER (brk, err, 1, addr); + + __curbrk = newbrk; + + if (newbrk < addr) + { + __set_errno (ENOMEM); + return -1; + } + + return 0; +} +#endif int __brk (void *addr) { --- sysdeps/unix/sysv/linux/i386/sysdep.h +++ sysdeps/unix/sysv/linux/i386/sysdep.h @@ -187,7 +187,7 @@ /* The original calling convention for system calls on Linux/i386 is to use int $0x80. */ #ifdef I386_USE_SYSENTER -# ifdef SHARED +# if defined SHARED || defined __PIC__ # define ENTER_KERNEL call *%gs:SYSINFO_OFFSET # else # define ENTER_KERNEL call *_dl_sysinfo @@ -358,7 +358,7 @@ possible to use more than four parameters. */ #undef INTERNAL_SYSCALL #ifdef I386_USE_SYSENTER -# ifdef SHARED +# if defined SHARED || defined __PIC__ # define INTERNAL_SYSCALL(name, err, nr, args...) \ ({ \ register unsigned int resultvar; \ @@ -384,6 +384,18 @@ : "0" (name), "i" (offsetof (tcbhead_t, sysinfo)) \ ASMFMT_##nr(args) : "memory", "cc"); \ (int) resultvar; }) +# define INTERNAL_SYSCALL_NOSYSENTER(name, err, nr, args...) \ + ({ \ + register unsigned int resultvar; \ + EXTRAVAR_##nr \ + asm volatile ( \ + LOADARGS_NOSYSENTER_##nr \ + "movl %1, %%eax\n\t" \ + "int $0x80\n\t" \ + RESTOREARGS_NOSYSENTER_##nr \ + : "=a" (resultvar) \ + : "i" (__NR_##name) ASMFMT_##nr(args) : "memory", "cc"); \ + (int) resultvar; }) # else # define INTERNAL_SYSCALL(name, err, nr, args...) \ ({ \ @@ -447,12 +459,20 @@ #define LOADARGS_0 #ifdef __PIC__ -# if defined I386_USE_SYSENTER && defined SHARED +# if defined I386_USE_SYSENTER && ( defined SHARED || defined __PIC__ ) # define LOADARGS_1 \ "bpushl .L__X'%k3, %k3\n\t" # define LOADARGS_5 \ "movl %%ebx, %4\n\t" \ "movl %3, %%ebx\n\t" +# define LOADARGS_NOSYSENTER_1 \ + "bpushl .L__X'%k2, %k2\n\t" +# define LOADARGS_NOSYSENTER_2 LOADARGS_NOSYSENTER_1 +# define LOADARGS_NOSYSENTER_3 LOADARGS_3 +# define LOADARGS_NOSYSENTER_4 LOADARGS_3 +# define LOADARGS_NOSYSENTER_5 \ + "movl %%ebx, %3\n\t" \ + "movl %2, %%ebx\n\t" # else # define LOADARGS_1 \ "bpushl .L__X'%k2, %k2\n\t" @@ -474,11 +495,18 @@ #define RESTOREARGS_0 #ifdef __PIC__ -# if defined I386_USE_SYSENTER && defined SHARED +# if defined I386_USE_SYSENTER && ( defined SHARED || defined __PIC__ ) # define RESTOREARGS_1 \ "bpopl .L__X'%k3, %k3\n\t" # define RESTOREARGS_5 \ "movl %4, %%ebx" +# define RESTOREARGS_NOSYSENTER_1 \ + "bpopl .L__X'%k2, %k2\n\t" +# define RESTOREARGS_NOSYSENTER_2 RESTOREARGS_NOSYSENTER_1 +# define RESTOREARGS_NOSYSENTER_3 RESTOREARGS_3 +# define RESTOREARGS_NOSYSENTER_4 RESTOREARGS_3 +# define RESTOREARGS_NOSYSENTER_5 \ + "movl %3, %%ebx" # else # define RESTOREARGS_1 \ "bpopl .L__X'%k2, %k2\n\t"