Implement TLS_DTPMOD and TLS_DTPREL relocations
Generalize the omitted symbol and missing-TLS-segment behaviors to all TLS
relocations.
R_GENERIC_TLS_DTPMOD is a module ID, which starts at 1 for the executable.
R_GENERIC_TLS_DTPREL is an offset from the start of a module to a specific
TLS symbol.
binutils currently disagrees with Bionic about the values of
R_AARCH64_TLS_DTPMOD64 and R_AARCH64_TLS_DTPREL64, so disable
DTPMOD/DTPREL for now on arm64.
Bug: http://b/78026329
Test: bionic unit tests (Tests for dynamic TLS will be added later)
Change-Id: I05c28d6a1036bdd6127f605036679b7475689445
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 8a28bad..4dcdf7e 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -2722,7 +2722,11 @@
soinfo* lsi = nullptr;
if (sym == 0) {
- // Do nothing.
+ // By convention in ld.bfd and lld, an omitted symbol on a TLS relocation
+ // is a reference to the current module.
+ if (is_tls_reloc(type)) {
+ lsi = this;
+ }
} 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
@@ -2830,6 +2834,11 @@
sym_name, get_realpath());
return false;
}
+ if (lsi->get_tls() == nullptr) {
+ DL_ERR("TLS relocation refers to symbol \"%s\" in solib \"%s\" with no TLS segment",
+ sym_name, lsi->get_realpath());
+ return false;
+ }
sym_addr = s->st_value;
} else {
if (ELF_ST_TYPE(s->st_info) == STT_TLS) {
@@ -2916,16 +2925,12 @@
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()) {
- const TlsModule& mod = get_tls_module(lsi_tls->module_id);
+ } else {
+ CHECK(lsi->get_tls() != nullptr); // We rejected a missing TLS segment above.
+ const TlsModule& mod = get_tls_module(lsi->get_tls()->module_id);
if (mod.static_offset != SIZE_MAX) {
tpoff += mod.static_offset - tls_tp_base;
} else {
@@ -2933,10 +2938,6 @@
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",
@@ -2946,6 +2947,35 @@
}
break;
+#if !defined(__aarch64__)
+ // Omit support for DTPMOD/DTPREL on arm64, at least until
+ // http://b/123385182 is fixed. arm64 uses TLSDESC instead.
+ case R_GENERIC_TLS_DTPMOD:
+ count_relocation(kRelocRelative);
+ MARK(rel->r_offset);
+ {
+ size_t module_id = 0;
+ if (lsi == nullptr) {
+ // Unresolved weak relocation. Evaluate the module ID to 0.
+ } else {
+ CHECK(lsi->get_tls() != nullptr); // We rejected a missing TLS segment above.
+ module_id = lsi->get_tls()->module_id;
+ }
+ TRACE_TYPE(RELO, "RELO TLS_DTPMOD %16p <- %zu %s\n",
+ reinterpret_cast<void*>(reloc), module_id, sym_name);
+ *reinterpret_cast<ElfW(Addr)*>(reloc) = module_id;
+ }
+ break;
+ case R_GENERIC_TLS_DTPREL:
+ count_relocation(kRelocRelative);
+ MARK(rel->r_offset);
+ TRACE_TYPE(RELO, "RELO TLS_DTPREL %16p <- %16p %s\n",
+ reinterpret_cast<void*>(reloc),
+ reinterpret_cast<void*>(sym_addr + addend), sym_name);
+ *reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr + addend;
+ break;
+#endif // !defined(__aarch64__)
+
#if defined(__aarch64__)
case R_AARCH64_ABS64:
count_relocation(kRelocAbsolute);