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/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);