Handle R_GENERIC_TLS_TPREL relocations
This relocation is used for static TLS's initial-exec (IE) accesses.
A TLS symbol's value is its offset from the start of the ELF module's
TLS segment. It doesn't make sense to add the load_bias to this value,
so skip the call to soinfo::resolve_symbol_address.
Allow TLS relocations to refer to an unresolved weak symbol. In that case,
sym will be non-zero, but lsi will be nullptr. The dynamic linker resolves
the TPREL relocation to 0, making &missing_weak_symbol equal the thread
pointer.
Recognize Gold-style relocations to STB_LOCAL TLS symbols/sections and
issue an error.
Remove the "case R_AARCH64_TLS_TPREL64", because the R_GENERIC_TLS_TPREL
case handles it.
Remove the no-op R_AARCH64_TLSDESC handler. It's better to issue an error.
dlopen_library_with_ELF_TLS now fails with a consistent error about an
unimplemented dynamic TLS relocation.
Bug: http://b/78026329
Test: bionic unit tests (elftls tests are added in a later CL)
Change-Id: Ia08e1b5c8098117e12143d3b4ebb4dfaa5ca46ec
diff --git a/libc/bionic/bionic_elf_tls.cpp b/libc/bionic/bionic_elf_tls.cpp
index 691293d..4253b97 100644
--- a/libc/bionic/bionic_elf_tls.cpp
+++ b/libc/bionic/bionic_elf_tls.cpp
@@ -71,6 +71,10 @@
return true;
}
+size_t StaticTlsLayout::offset_thread_pointer() const {
+ return offset_bionic_tcb_ + (-MIN_TLS_SLOT * sizeof(void*));
+}
+
// 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,
diff --git a/libc/private/bionic_elf_tls.h b/libc/private/bionic_elf_tls.h
index 94c01e0..09e1958 100644
--- a/libc/private/bionic_elf_tls.h
+++ b/libc/private/bionic_elf_tls.h
@@ -61,6 +61,7 @@
public:
size_t offset_bionic_tcb() const { return offset_bionic_tcb_; }
size_t offset_bionic_tls() const { return offset_bionic_tls_; }
+ size_t offset_thread_pointer() const;
size_t size() const { return offset_; }
size_t alignment() const { return alignment_; }
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 4aac39b..00bd08c 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -2670,16 +2670,32 @@
#else
static ElfW(Addr) get_addend(ElfW(Rel)* rel, ElfW(Addr) reloc_addr) {
if (ELFW(R_TYPE)(rel->r_info) == R_GENERIC_RELATIVE ||
- ELFW(R_TYPE)(rel->r_info) == R_GENERIC_IRELATIVE) {
+ ELFW(R_TYPE)(rel->r_info) == R_GENERIC_IRELATIVE ||
+ ELFW(R_TYPE)(rel->r_info) == R_GENERIC_TLS_DTPREL ||
+ ELFW(R_TYPE)(rel->r_info) == R_GENERIC_TLS_TPREL) {
return *reinterpret_cast<ElfW(Addr)*>(reloc_addr);
}
return 0;
}
#endif
+static bool is_tls_reloc(ElfW(Word) type) {
+ switch (type) {
+ case R_GENERIC_TLS_DTPMOD:
+ case R_GENERIC_TLS_DTPREL:
+ case R_GENERIC_TLS_TPREL:
+ case R_GENERIC_TLSDESC:
+ return true;
+ default:
+ return false;
+ }
+}
+
template<typename ElfRelIteratorT>
bool soinfo::relocate(const VersionTracker& version_tracker, ElfRelIteratorT&& rel_iterator,
const soinfo_list_t& global_group, const soinfo_list_t& local_group) {
+ const size_t tls_tp_base = __libc_shared_globals()->static_tls_layout.offset_thread_pointer();
+
for (size_t idx = 0; rel_iterator.has_next(); ++idx) {
const auto rel = rel_iterator.next();
if (rel == nullptr) {
@@ -2702,7 +2718,22 @@
const ElfW(Sym)* s = nullptr;
soinfo* lsi = nullptr;
- if (sym != 0) {
+ if (sym == 0) {
+ // Do nothing.
+ } else if (ELF_ST_BIND(symtab_[sym].st_info) == STB_LOCAL && is_tls_reloc(type)) {
+ // In certain situations, the Gold linker accesses a TLS symbol using a
+ // relocation to an STB_LOCAL symbol in .dynsym of either STT_SECTION or
+ // STT_TLS type. Bionic doesn't support these relocations, so issue an
+ // error. References:
+ // - https://groups.google.com/d/topic/generic-abi/dJ4_Y78aQ2M/discussion
+ // - https://sourceware.org/bugzilla/show_bug.cgi?id=17699
+ s = &symtab_[sym];
+ sym_name = get_string(s->st_name);
+ DL_ERR("unexpected TLS reference to local symbol \"%s\": "
+ "sym type %d, rel type %u (idx %zu of \"%s\")",
+ sym_name, ELF_ST_TYPE(s->st_info), type, idx, get_realpath());
+ return false;
+ } else {
sym_name = get_string(symtab_[sym].st_name);
const version_info* vi = nullptr;
@@ -2739,6 +2770,10 @@
case R_GENERIC_GLOB_DAT:
case R_GENERIC_RELATIVE:
case R_GENERIC_IRELATIVE:
+ case R_GENERIC_TLS_DTPMOD:
+ case R_GENERIC_TLS_DTPREL:
+ case R_GENERIC_TLS_TPREL:
+ case R_GENERIC_TLSDESC:
#if defined(__aarch64__)
case R_AARCH64_ABS64:
case R_AARCH64_ABS32:
@@ -2786,12 +2821,21 @@
}
}
#endif
- if (ELF_ST_TYPE(s->st_info) == STT_TLS) {
- DL_ERR("unsupported ELF TLS symbol \"%s\" referenced by \"%s\"",
- sym_name, get_realpath());
- return false;
+ if (is_tls_reloc(type)) {
+ if (ELF_ST_TYPE(s->st_info) != STT_TLS) {
+ DL_ERR("reference to non-TLS symbol \"%s\" from TLS relocation in \"%s\"",
+ sym_name, get_realpath());
+ return false;
+ }
+ sym_addr = s->st_value;
+ } else {
+ if (ELF_ST_TYPE(s->st_info) == STT_TLS) {
+ DL_ERR("reference to TLS symbol \"%s\" from non-TLS relocation in \"%s\"",
+ sym_name, get_realpath());
+ return false;
+ }
+ sym_addr = lsi->resolve_symbol_address(s);
}
- sym_addr = lsi->resolve_symbol_address(s);
#if !defined(__LP64__)
if (protect_segments) {
if (phdr_table_unprotect_segments(phdr, phnum, load_bias) < 0) {
@@ -2864,6 +2908,39 @@
*reinterpret_cast<ElfW(Addr)*>(reloc) = ifunc_addr;
}
break;
+ case R_GENERIC_TLS_TPREL:
+ count_relocation(kRelocRelative);
+ MARK(rel->r_offset);
+ {
+ ElfW(Addr) tpoff = 0;
+ if (sym == 0) {
+ // By convention in ld.bfd and lld, an omitted symbol
+ // (ELFW(R_SYM) == 0) refers to the local module.
+ lsi = this;
+ }
+ if (lsi == nullptr) {
+ // Unresolved weak relocation. Leave tpoff at 0 to resolve
+ // &weak_tls_symbol to __get_tls().
+ } else if (soinfo_tls* lsi_tls = lsi->get_tls()) {
+ if (lsi_tls->module->static_offset != SIZE_MAX) {
+ tpoff += lsi_tls->module->static_offset - tls_tp_base;
+ } else {
+ DL_ERR("TLS symbol \"%s\" in dlopened \"%s\" referenced from \"%s\" using IE access model",
+ sym_name, lsi->get_realpath(), get_realpath());
+ return false;
+ }
+ } else {
+ DL_ERR("TLS relocation refers to symbol \"%s\" in solib \"%s\" with no TLS segment",
+ sym_name, lsi->get_realpath());
+ return false;
+ }
+ tpoff += sym_addr + addend;
+ TRACE_TYPE(RELO, "RELO TLS_TPREL %16p <- %16p %s\n",
+ reinterpret_cast<void*>(reloc),
+ reinterpret_cast<void*>(tpoff), sym_name);
+ *reinterpret_cast<ElfW(Addr)*>(reloc) = tpoff;
+ }
+ break;
#if defined(__aarch64__)
case R_AARCH64_ABS64:
@@ -2965,14 +3042,6 @@
*/
DL_ERR("%s R_AARCH64_COPY relocations are not supported", get_realpath());
return false;
- case R_AARCH64_TLS_TPREL64:
- TRACE_TYPE(RELO, "RELO TLS_TPREL64 *** %16llx <- %16llx - %16llx\n",
- reloc, (sym_addr + addend), rel->r_offset);
- break;
- case R_AARCH64_TLSDESC:
- TRACE_TYPE(RELO, "RELO TLSDESC *** %16llx <- %16llx - %16llx\n",
- reloc, (sym_addr + addend), rel->r_offset);
- break;
#elif defined(__x86_64__)
case R_X86_64_32:
count_relocation(kRelocRelative);
diff --git a/tests/dlfcn_test.cpp b/tests/dlfcn_test.cpp
index 5f48e67..176a6f8 100644
--- a/tests/dlfcn_test.cpp
+++ b/tests/dlfcn_test.cpp
@@ -1086,7 +1086,7 @@
dlerror(); // Clear any pending errors.
void* handle = dlopen("libelf-tls-library.so", RTLD_NOW);
ASSERT_TRUE(handle == nullptr);
- ASSERT_SUBSTR("unsupported ELF TLS", dlerror());
+ ASSERT_SUBSTR("unknown reloc type ", dlerror());
}
TEST(dlfcn, dlopen_bad_flags) {