Add a __bionic_get_tls_segment function
The function searches for a TLS segment in a ElfXX_Phdr table.
Bug: http://b/78026329
Test: bionic unit tests
Change-Id: I221b13420d1a2da33fc2174b7dd256589f6ecfdb
diff --git a/libc/bionic/bionic_elf_tls.cpp b/libc/bionic/bionic_elf_tls.cpp
index 55c2c31..2d2cbe3 100644
--- a/libc/bionic/bionic_elf_tls.cpp
+++ b/libc/bionic/bionic_elf_tls.cpp
@@ -28,11 +28,43 @@
#include "private/bionic_elf_tls.h"
+#include <async_safe/log.h>
#include <sys/param.h>
+#include <unistd.h>
#include "private/bionic_macros.h"
#include "private/bionic_tls.h"
+// Search for a TLS segment in the given phdr table. Returns true if it has a
+// TLS segment and false otherwise.
+bool __bionic_get_tls_segment(const ElfW(Phdr)* phdr_table, size_t phdr_count,
+ ElfW(Addr) load_bias, const char* mod_name,
+ TlsSegment* out) {
+ for (size_t i = 0; i < phdr_count; ++i) {
+ const ElfW(Phdr)& phdr = phdr_table[i];
+ if (phdr.p_type == PT_TLS) {
+ // N.B. The size does not need to be a multiple of the alignment. With
+ // ld.bfd (or after using binutils' strip), the TLS segment's size isn't
+ // rounded up.
+ size_t alignment = phdr.p_align;
+ if (alignment == 0 || !powerof2(alignment)) {
+ async_safe_fatal("error: \"%s\": TLS segment alignment is not a power of 2: %zu",
+ mod_name, alignment);
+ }
+ // Bionic only respects TLS alignment up to one page.
+ alignment = MIN(alignment, PAGE_SIZE);
+ *out = TlsSegment {
+ phdr.p_memsz,
+ alignment,
+ reinterpret_cast<void*>(load_bias + phdr.p_vaddr),
+ phdr.p_filesz,
+ };
+ return true;
+ }
+ }
+ return false;
+}
+
void StaticTlsLayout::reserve_tcb() {
offset_bionic_tcb_ = reserve_type<bionic_tcb>();
}
diff --git a/libc/private/bionic_elf_tls.h b/libc/private/bionic_elf_tls.h
index e847669..48a4d25 100644
--- a/libc/private/bionic_elf_tls.h
+++ b/libc/private/bionic_elf_tls.h
@@ -28,7 +28,20 @@
#pragma once
+#include <link.h>
#include <stdint.h>
+#include <sys/cdefs.h>
+
+struct TlsSegment {
+ size_t size = 0;
+ size_t alignment = 1;
+ const void* init_ptr = ""; // Field is non-null even when init_size is 0.
+ size_t init_size = 0;
+};
+
+__LIBC_HIDDEN__ bool __bionic_get_tls_segment(const ElfW(Phdr)* phdr_table, size_t phdr_count,
+ ElfW(Addr) load_bias, const char* mod_name,
+ TlsSegment* out);
struct StaticTlsLayout {
constexpr StaticTlsLayout() {}