diff options
-rw-r--r-- | libc/bionic/bionic_elf_tls.cpp | 57 | ||||
-rw-r--r-- | libc/bionic/libc_init_static.cpp | 2 | ||||
-rw-r--r-- | libc/private/bionic_elf_tls.h | 5 | ||||
-rw-r--r-- | linker/linker_tls.cpp | 2 |
4 files changed, 61 insertions, 5 deletions
diff --git a/libc/bionic/bionic_elf_tls.cpp b/libc/bionic/bionic_elf_tls.cpp index 2d2cbe305..a54f29e76 100644 --- a/libc/bionic/bionic_elf_tls.cpp +++ b/libc/bionic/bionic_elf_tls.cpp @@ -65,8 +65,57 @@ bool __bionic_get_tls_segment(const ElfW(Phdr)* phdr_table, size_t phdr_count, return false; } -void StaticTlsLayout::reserve_tcb() { - offset_bionic_tcb_ = reserve_type<bionic_tcb>(); +// Reserves space for the Bionic TCB and the executable's TLS segment. Returns +// the offset of the executable's TLS segment. +size_t StaticTlsLayout::reserve_exe_segment_and_tcb(const TlsSegment* exe_segment, + const char* progname __attribute__((unused))) { + // Special case: if the executable has no TLS segment, then just allocate a + // TCB and skip the minimum alignment check on ARM. + if (exe_segment == nullptr) { + offset_bionic_tcb_ = reserve_type<bionic_tcb>(); + return 0; + } + +#if defined(__arm__) || defined(__aarch64__) + + // First reserve enough space for the TCB before the executable segment. + reserve(sizeof(bionic_tcb), 1); + + // Then reserve the segment itself. + const size_t result = reserve(exe_segment->size, exe_segment->alignment); + + // The variant 1 ABI that ARM linkers follow specifies a 2-word TCB between + // the thread pointer and the start of the executable's TLS segment, but both + // the thread pointer and the TLS segment are aligned appropriately for the + // TLS segment. Calculate the distance between the thread pointer and the + // EXE's segment. + const size_t exe_tpoff = __BIONIC_ALIGN(sizeof(void*) * 2, exe_segment->alignment); + + const size_t min_bionic_alignment = BIONIC_ROUND_UP_POWER_OF_2(MAX_TLS_SLOT) * sizeof(void*); + if (exe_tpoff < min_bionic_alignment) { + async_safe_fatal("error: \"%s\": executable's TLS segment is underaligned: " + "alignment is %zu, needs to be at least %zu for %s Bionic", + progname, exe_segment->alignment, min_bionic_alignment, + (sizeof(void*) == 4 ? "ARM" : "ARM64")); + } + + offset_bionic_tcb_ = result - exe_tpoff - (-MIN_TLS_SLOT * sizeof(void*)); + return result; + +#elif defined(__i386__) || defined(__x86_64__) + + // x86 uses variant 2 TLS layout. The executable's segment is located just + // before the TCB. + static_assert(MIN_TLS_SLOT == 0, "First slot of bionic_tcb must be slot #0 on x86"); + const size_t exe_size = round_up_with_overflow_check(exe_segment->size, exe_segment->alignment); + reserve(exe_size, 1); + const size_t max_align = MAX(alignof(bionic_tcb), exe_segment->alignment); + offset_bionic_tcb_ = reserve(sizeof(bionic_tcb), max_align); + return offset_bionic_tcb_ - exe_size; + +#else +#error "Unrecognized architecture" +#endif } void StaticTlsLayout::reserve_bionic_tls() { @@ -76,6 +125,10 @@ void StaticTlsLayout::reserve_bionic_tls() { void StaticTlsLayout::finish_layout() { // Round the offset up to the alignment. offset_ = round_up_with_overflow_check(offset_, alignment_); + + if (overflowed_) { + async_safe_fatal("error: TLS segments in static TLS overflowed"); + } } // The size is not required to be a multiple of the alignment. The alignment diff --git a/libc/bionic/libc_init_static.cpp b/libc/bionic/libc_init_static.cpp index 68650ed83..6ba6583c5 100644 --- a/libc/bionic/libc_init_static.cpp +++ b/libc/bionic/libc_init_static.cpp @@ -86,7 +86,7 @@ static void apply_gnu_relro() { static void layout_static_tls() { StaticTlsLayout& layout = __libc_shared_globals()->static_tls_layout; layout.reserve_bionic_tls(); - layout.reserve_tcb(); + layout.reserve_exe_segment_and_tcb(nullptr); layout.finish_layout(); } diff --git a/libc/private/bionic_elf_tls.h b/libc/private/bionic_elf_tls.h index 48a4d2563..c67c22147 100644 --- a/libc/private/bionic_elf_tls.h +++ b/libc/private/bionic_elf_tls.h @@ -63,8 +63,11 @@ public: size_t alignment() const { return alignment_; } bool overflowed() const { return overflowed_; } - void reserve_tcb(); + size_t reserve_exe_segment_and_tcb(const TlsSegment* exe_segment, const char* progname); void reserve_bionic_tls(); + size_t reserve_solib_segment(const TlsSegment& segment) { + return reserve(segment.size, segment.alignment); + } void finish_layout(); private: diff --git a/linker/linker_tls.cpp b/linker/linker_tls.cpp index 33274535c..c0ebf252c 100644 --- a/linker/linker_tls.cpp +++ b/linker/linker_tls.cpp @@ -41,7 +41,7 @@ extern "C" void __linker_reserve_bionic_tls_in_static_tls() { // Stub for linker static TLS layout. void layout_linker_static_tls() { StaticTlsLayout& layout = __libc_shared_globals()->static_tls_layout; - layout.reserve_tcb(); + layout.reserve_exe_segment_and_tcb(nullptr); // The pthread key data is located at the very front of bionic_tls. As a // temporary workaround, allocate bionic_tls just after the thread pointer so |