Static PIE extends address space layout randomization to static
executables. It provides additional security hardening benefits at
the cost of some memory and performance.
Dynamic linker, ld.so, is a standalone program which can be loaded at
any address. This patch adds a configure option, --enable-static-pie,
to embed the part of ld.so in static executable to create static position
independent executable (static PIE). A static PIE is similar to static
executable, but can be loaded at any address without help from a dynamic
linker. When --enable-static-pie is used to configure glibc, libc.a is
built as PIE and all static executables, including tests, are built as
static PIE. The resulting libc.a can be used together with GCC 8 or
above to build static PIE with the compiler option, -static-pie. But
GCC 8 isn't required to build glibc with --enable-static-pie. Only GCC
with PIE support is needed. When an older GCC is used to build glibc
with --enable-static-pie, proper input files are passed to linker to
create static executables as static PIE, together with "-z text" to
prevent dynamic relocations in read-only segments, which are not allowed
in static PIE.
The following changes are made for static PIE:
1. Add a new function, _dl_relocate_static_pie, to:
a. Get the run-time load address.
b. Read the dynamic section.
c. Perform dynamic relocations.
Dynamic linker also performs these steps. But static PIE doesn't load
any shared objects.
2. Call _dl_relocate_static_pie at entrance of LIBC_START_MAIN in
libc.a. crt1.o, which is used to create dynamic and non-PIE static
executables, is updated to include a dummy _dl_relocate_static_pie.
rcrt1.o is added to create static PIE, which will link in the real
_dl_relocate_static_pie. grcrt1.o is also added to create static PIE
with -pg. GCC 8 has been updated to support rcrt1.o and grcrt1.o for
static PIE.
Static PIE can work on all architectures which support PIE, provided:
1. Target must support accessing of local functions without dynamic
relocations, which is needed in start.S to call __libc_start_main with
function addresses of __libc_csu_init, __libc_csu_fini and main. All
functions in static PIE are local functions. If PIE start.S can't reach
main () defined in a shared object, the code sequence:
pass address of local_main to __libc_start_main
...
local_main:
tail call to main via PLT
can be used.
2. start.S is updated to check PIC instead SHARED for PIC code path and
avoid dynamic relocation, when PIC is defined and SHARED isn't defined,
to support static PIE.
3. All assembly codes are updated check PIC instead SHARED for PIC code
path to avoid dynamic relocations in read-only sections.
4. All assembly codes are updated check SHARED instead PIC for static
symbol name.
5. elf_machine_load_address in dl-machine.h are updated to support static
PIE.
6. __brk works without TLS nor dynamic relocations in read-only section
so that it can be used by __libc_setup_tls to initializes TLS in static
PIE.
NB: When glibc is built with GCC defaulted to PIE, libc.a is compiled
with -fPIE, regardless if --enable-static-pie is used to configure glibc.
When glibc is configured with --enable-static-pie, libc.a is compiled
with -fPIE, regardless whether GCC defaults to PIE or not. The same
libc.a can be used to build both static executable and static PIE.
There is no need for separate PIE copy of libc.a.
On x86-64, the normal static sln:
text data bss dec hex filename
625425 8284 5456 639165 9c0bd elf/sln
the static PIE sln:
text data bss dec hex filename
657626 20636 5392 683654 a6e86 elf/sln
The code size is increased by 5% and the binary size is increased by 7%.
Linker requirements to build glibc with --enable-static-pie:
1. Linker supports --no-dynamic-linker to remove PT_INTERP segment from
static PIE.
2. Linker can create working static PIE. The x86-64 linker needs the
fix for
https://sourceware.org/bugzilla/show_bug.cgi?id=21782
The i386 linker needs to be able to convert "movl main@GOT(%ebx), %eax"
to "leal main@GOTOFF(%ebx), %eax" if main is defined locally.
Binutils 2.29 or above are OK for i686 and x86-64. But linker status for
other targets need to be verified.
3. Linker should resolve undefined weak symbols to 0 in static PIE:
https://sourceware.org/bugzilla/show_bug.cgi?id=22269
4. Many ELF backend linkers incorrectly check bfd_link_pic for TLS
relocations, which should check bfd_link_executable instead:
https://sourceware.org/bugzilla/show_bug.cgi?id=22263
Tested on aarch64, i686 and x86-64.
Using GCC 7 and binutils master branch, build-many-glibcs.py with
--enable-static-pie with all patches for static PIE applied have the
following build successes:
PASS: glibcs-aarch64_be-linux-gnu build
PASS: glibcs-aarch64-linux-gnu build
PASS: glibcs-armeb-linux-gnueabi-be8 build
PASS: glibcs-armeb-linux-gnueabi build
PASS: glibcs-armeb-linux-gnueabihf-be8 build
PASS: glibcs-armeb-linux-gnueabihf build
PASS: glibcs-arm-linux-gnueabi build
PASS: glibcs-arm-linux-gnueabihf build
PASS: glibcs-arm-linux-gnueabihf-v7a build
PASS: glibcs-arm-linux-gnueabihf-v7a-disable-multi-arch build
PASS: glibcs-m68k-linux-gnu build
PASS: glibcs-microblazeel-linux-gnu build
PASS: glibcs-microblaze-linux-gnu build
PASS: glibcs-mips64el-linux-gnu-n32 build
PASS: glibcs-mips64el-linux-gnu-n32-nan2008 build
PASS: glibcs-mips64el-linux-gnu-n32-nan2008-soft build
PASS: glibcs-mips64el-linux-gnu-n32-soft build
PASS: glibcs-mips64el-linux-gnu-n64 build
PASS: glibcs-mips64el-linux-gnu-n64-nan2008 build
PASS: glibcs-mips64el-linux-gnu-n64-nan2008-soft build
PASS: glibcs-mips64el-linux-gnu-n64-soft build
PASS: glibcs-mips64-linux-gnu-n32 build
PASS: glibcs-mips64-linux-gnu-n32-nan2008 build
PASS: glibcs-mips64-linux-gnu-n32-nan2008-soft build
PASS: glibcs-mips64-linux-gnu-n32-soft build
PASS: glibcs-mips64-linux-gnu-n64 build
PASS: glibcs-mips64-linux-gnu-n64-nan2008 build
PASS: glibcs-mips64-linux-gnu-n64-nan2008-soft build
PASS: glibcs-mips64-linux-gnu-n64-soft build
PASS: glibcs-mipsel-linux-gnu build
PASS: glibcs-mipsel-linux-gnu-nan2008 build
PASS: glibcs-mipsel-linux-gnu-nan2008-soft build
PASS: glibcs-mipsel-linux-gnu-soft build
PASS: glibcs-mips-linux-gnu build
PASS: glibcs-mips-linux-gnu-nan2008 build
PASS: glibcs-mips-linux-gnu-nan2008-soft build
PASS: glibcs-mips-linux-gnu-soft build
PASS: glibcs-nios2-linux-gnu build
PASS: glibcs-powerpc64le-linux-gnu build
PASS: glibcs-powerpc64-linux-gnu build
PASS: glibcs-tilegxbe-linux-gnu-32 build
PASS: glibcs-tilegxbe-linux-gnu build
PASS: glibcs-tilegx-linux-gnu-32 build
PASS: glibcs-tilegx-linux-gnu build
PASS: glibcs-tilepro-linux-gnu build
and the following build failures:
FAIL: glibcs-alpha-linux-gnu build
elf/sln is failed to link due to:
assertion fail bfd/elf64-alpha.c:4125
This is caused by linker bug and/or non-PIC code in PIE libc.a.
FAIL: glibcs-hppa-linux-gnu build
elf/sln is failed to link due to:
collect2: fatal error: ld terminated with signal 11 [Segmentation fault]
https://sourceware.org/bugzilla/show_bug.cgi?id=22537
FAIL: glibcs-ia64-linux-gnu build
elf/sln is failed to link due to:
collect2: fatal error: ld terminated with signal 11 [Segmentation fault]
FAIL: glibcs-powerpc-linux-gnu build
FAIL: glibcs-powerpc-linux-gnu-soft build
FAIL: glibcs-powerpc-linux-gnuspe build
FAIL: glibcs-powerpc-linux-gnuspe-e500v1 build
elf/sln is failed to link due to:
ld: read-only segment has dynamic relocations.
This is caused by linker bug and/or non-PIC code in PIE libc.a. See:
https://sourceware.org/bugzilla/show_bug.cgi?id=22264
FAIL: glibcs-powerpc-linux-gnu-power4 build
elf/sln is failed to link due to:
findlocale.c:96:(.text+0x22c): @local call to ifunc memchr
This is caused by linker bug and/or non-PIC code in PIE libc.a.
FAIL: glibcs-s390-linux-gnu build
elf/sln is failed to link due to:
collect2: fatal error: ld terminated with signal 11 [Segmentation fault], core dumped
assertion fail bfd/elflink.c:14299
This is caused by linker bug and/or non-PIC code in PIE libc.a.
FAIL: glibcs-sh3eb-linux-gnu build
FAIL: glibcs-sh3-linux-gnu build
FAIL: glibcs-sh4eb-linux-gnu build
FAIL: glibcs-sh4eb-linux-gnu-soft build
FAIL: glibcs-sh4-linux-gnu build
FAIL: glibcs-sh4-linux-gnu-soft build
elf/sln is failed to link due to:
ld: read-only segment has dynamic relocations.
This is caused by linker bug and/or non-PIC code in PIE libc.a. See:
https://sourceware.org/bugzilla/show_bug.cgi?id=22263
Also TLS code sequence in SH assembly syscalls in glibc doesn't match TLS
code sequence expected by ld:
https://sourceware.org/bugzilla/show_bug.cgi?id=22270
FAIL: glibcs-sparc64-linux-gnu build
FAIL: glibcs-sparcv9-linux-gnu build
FAIL: glibcs-tilegxbe-linux-gnu build
FAIL: glibcs-tilegxbe-linux-gnu-32 build
FAIL: glibcs-tilegx-linux-gnu build
FAIL: glibcs-tilegx-linux-gnu-32 build
FAIL: glibcs-tilepro-linux-gnu build
elf/sln is failed to link due to:
ld: read-only segment has dynamic relocations.
This is caused by linker bug and/or non-PIC code in PIE libc.a. See:
https://sourceware.org/bugzilla/show_bug.cgi?id=22263
[BZ #19574]
* INSTALL: Regenerated.
* Makeconfig (real-static-start-installed-name): New.
(pic-default): Updated for --enable-static-pie.
(pie-default): New for --enable-static-pie.
(default-pie-ldflag): Likewise.
(+link-static-before-libc): Replace $(DEFAULT-LDFLAGS-$(@F))
with $(if $($(@F)-no-pie),$(no-pie-ldflag),$(default-pie-ldflag)).
Replace $(static-start-installed-name) with
$(real-static-start-installed-name).
(+prectorT): Updated for --enable-static-pie.
(+postctorT): Likewise.
(CFLAGS-.o): Add $(pie-default).
(CFLAGS-.op): Likewise.
* NEWS: Mention --enable-static-pie.
* config.h.in (ENABLE_STATIC_PIE): New.
* configure.ac (--enable-static-pie): New configure option.
(have-no-dynamic-linker): New LIBC_CONFIG_VAR.
(have-static-pie): Likewise.
Enable static PIE if linker supports --no-dynamic-linker.
(ENABLE_STATIC_PIE): New AC_DEFINE.
(enable-static-pie): New LIBC_CONFIG_VAR.
* configure: Regenerated.
* csu/Makefile (omit-deps): Add r$(start-installed-name) and
gr$(start-installed-name) for --enable-static-pie.
(extra-objs): Likewise.
(install-lib): Likewise.
(extra-objs): Add static-reloc.o and static-reloc.os
($(objpfx)$(start-installed-name)): Also depend on
$(objpfx)static-reloc.o.
($(objpfx)r$(start-installed-name)): New.
($(objpfx)g$(start-installed-name)): Also depend on
$(objpfx)static-reloc.os.
($(objpfx)gr$(start-installed-name)): New.
* csu/libc-start.c (LIBC_START_MAIN): Call _dl_relocate_static_pie
in libc.a.
* csu/libc-tls.c (__libc_setup_tls): Add main_map->l_addr to
initimage.
* csu/static-reloc.c: New file.
* elf/Makefile (routines): Add dl-reloc-static-pie.
(elide-routines.os): Likewise.
(DEFAULT-LDFLAGS-tst-tls1-static-non-pie): Removed.
(tst-tls1-static-non-pie-no-pie): New.
* elf/dl-reloc-static-pie.c: New file.
* elf/dl-support.c (_dl_get_dl_main_map): New function.
* elf/dynamic-link.h (ELF_DURING_STARTUP): Also check
STATIC_PIE_BOOTSTRAP.
* elf/get-dynamic-info.h (elf_get_dynamic_info): Likewise.
* gmon/Makefile (tests): Add tst-gmon-static-pie.
(tests-static): Likewise.
(DEFAULT-LDFLAGS-tst-gmon-static): Removed.
(tst-gmon-static-no-pie): New.
(CFLAGS-tst-gmon-static-pie.c): Likewise.
(CRT-tst-gmon-static-pie): Likewise.
(tst-gmon-static-pie-ENV): Likewise.
(tests-special): Likewise.
($(objpfx)tst-gmon-static-pie.out): Likewise.
(clean-tst-gmon-static-pie-data): Likewise.
($(objpfx)tst-gmon-static-pie-gprof.out): Likewise.
* gmon/tst-gmon-static-pie.c: New file.
* manual/install.texi: Document --enable-static-pie.
* sysdeps/generic/ldsodefs.h (_dl_relocate_static_pie): New.
(_dl_get_dl_main_map): Likewise.
* sysdeps/i386/configure.ac: Check if linker supports static PIE.
* sysdeps/x86_64/configure.ac: Likewise.
* sysdeps/i386/configure: Regenerated.
* sysdeps/x86_64/configure: Likewise.
* sysdeps/mips/Makefile (ASFLAGS-.o): Add $(pie-default).
(ASFLAGS-.op): Likewise.
On Linux/i386, there are 3 ways to make a system call:
1. call *%gs:SYSINFO_OFFSET. This requires TLS initialization.
2. call *_dl_sysinfo. This requires relocation of _dl_sysinfo.
3. int $0x80. This is slower than #2 and #3, but works everywhere.
When an object file is compiled with PIC, #1 is prefered since it is
faster than #3 and doesn't require relocation of _dl_sysinfo. For
dynamic executables, ld.so initializes TLS. However, for static
executables, before TLS is initialized by __libc_setup_tls, #3 should
be used for system calls.
This patch adds <startup.h> which defines _startup_fatal and defaults
it to __libc_fatal. It replaces __libc_fatal with _startup_fatal in
static executables where it is called before __libc_setup_tls is called.
This header file is included in all files containing functions which are
called before __libc_setup_tls is called. On Linux/i386, when PIE is
enabled by default, _startup_fatal is turned into ABORT_INSTRUCTION and
I386_USE_SYSENTER is defined to 0 so that "int $0x80" is used for system
calls before __libc_setup_tls is called.
Tested on i686 and x86-64. Without this patch, all statically-linked
tests will fail on i686 when the compiler defaults to -fPIE.
[BZ #21913]
* csu/libc-tls.c: Include <startup.h> first.
(__libc_setup_tls): Call _startup_fatal instead of __libc_fatal.
* elf/dl-tunables.c: Include <startup.h> first.
* include/libc-symbols.h (BUILD_PIE_DEFAULT): New.
* sysdeps/generic/startup.h: New file.
* sysdeps/unix/sysv/linux/i386/startup.h: Likewise.
* sysdeps/unix/sysv/linux/i386/brk.c [BUILD_PIE_DEFAULT != 0]
(I386_USE_SYSENTER): New. Defined to 0.
The address of the stack canary is stored in a per-thread variable,
which means that we must ensure that the TLS area is intialized before
calling any -fstack-protector'ed functions. For dynamically linked
applications, we ensure this (in a later patch) by disabling
-fstack-protector for the whole dynamic linker, but for static
applications, the AT_ENTRY address is called directly by the kernel, so
we must deal with the problem differently.
In static appliations, __libc_setup_tls performs the TCB setup and TLS
initialization, so this commit arranges for it to be called early and
unconditionally. The call (and the stack guard initialization) is
before the DL_SYSDEP_OSCHECK hook, which if set will probably call
functions which are stack-protected (it does on Linux and NaCL too). We
also move apply_irel up, so that we can still safely call functions that
require ifuncs while in __libc_setup_tls (though if stack-protection is
enabled we still have to avoid calling functions that are not
stack-protected at this stage).
Commit 7a5e3d9d63 (elf: Assume TLS is
initialized in _dl_map_object_from_fd) removed the last call of
_dl_tls_setup, but did not remove the function itself.
Instead of a flag which indicates the pointer can be freed, dtv_t
now includes the pointer which should be freed. Due to padding,
the size of dtv_t does not increase.
To avoid using memalign, the new allocate_dtv_entry function
allocates a sufficiently large buffer so that a sub-buffer
can be found in it which starts with an aligned pointer. Both
the aligned and original pointers are kept, the latter for calling
free later.
We need to align TCB offset to the maximum alignment for TLS_TCB_AT_TP
targets, as _dl_allocate_tls_storage (in elf/dl-tls.c) does using
__libc_memalign and dl_tls_static_align.
[BZ #18383]
* csu/libc-tls.c (__libc_setup_tls) [TLS_TCB_AT_TP]: Align
TCB_OFFSET to MAX_ALIGN, not just TCBALIGN. Add comment.
* elf/Makefile (test-xfail-tst-tlsalign{,-static}): Remove
comment for i386/x86-64.
(test-xfail-tst-tlsalign-extern-static): Removed.
Fixes to address issues from BZ #15022 resolution, as follows:
* TLS updates to csu/libc-tls.c -- we now have a proper main map, so
there's no longer a need to create a separate fake one to keep TLS
structures,
* random updates to elf/dl-close.c -- LM_ID_BASE is now a valid name
space ID for static executables as well, so assert that we don't
unload the main map. Similarly dl_nns isn't supposed to be 0 for
static executables anymore,
* actual BZ #16046 fix to elf/dl-iteratephdr.c -- the dl_iterate_phdr
special function for static executables isn't needed anymore, provided
that l_phdr and l_phnum members of the main map have been properly
initialized (done in _dl_non_dynamic_init in elf/dl-support.c now),
* ld.so.cache loader update to elf/dl-load.c --
GL(dl_ns)[LM_ID_BASE]._ns_loaded is now always initialized in static
executables so can become the fallback loader map to check for
DF_1_NODEFLIB, provided that the l_flags_1 member of the main map has
been properly initialized (done in elf/dl-support.c now); this also
ensures previous semantics elsewhere in elf/dl-load.c,
* matching updates to elf/dl-support.c -- to complement the two fixes
above.
This reverts the change that allows the POSIX Thread default stack size
to be changed by the environment variable
GLIBC_PTHREAD_DEFAULT_STACKSIZE. It has been requested that more
discussion happen before this change goes into 2.18.
* csu/libc-tls.c (static_dtv): Renamed to ...
(_dl_static_dtv): This. Make it global.
(_dl_initial_dtv): Removed.
(__libc_setup_tls): Updated.
* elf/dl-tls.c (DL_INITIAL_DTV): New macro.
(_dl_deallocate_tls): Replace GL(dl_initial_dtv) with
DL_INITIAL_DTV.