| 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 <kevquinn@gentoo.org> |
| Fixed for 2.10 by Magnus Granberg <zorry@ume.nu> |
| Fixed for 2.18 by Magnus Granberg <zorry@gentoo.org> |
| |
| --- csu/libc-start.c |
| +++ csu/libc-start.c |
| @@ -28,6 +28,7 @@ |
| extern int __libc_multiple_libcs; |
| |
| #include <tls.h> |
| +#include <sysdep.h> |
| #ifndef SHARED |
| # include <dl-osinfo.h> |
| 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 <unistd.h> |
| #include <stdio.h> |
| #include <sys/param.h> |
| - |
| +#include <sysdep.h> |
| |
| #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 <errno.h> |
| #include <stdint.h> |
| #include <unistd.h> |
| +#include <sysdep.h> |
| |
| /* 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" |