Fix dlsym and dladdr for TLS symbols
* dlsym: call __tls_get_addr for TLS symbols
* dladdr: skip TLS symbols
Bug: b/123772574
Test: bionic unit tests
Change-Id: I59a8bc4a7d455e1018b0d577b027b6417c8487cd
diff --git a/linker/linker.cpp b/linker/linker.cpp
index c60ab6a..044689f 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -2383,9 +2383,26 @@
if (sym != nullptr) {
uint32_t bind = ELF_ST_BIND(sym->st_info);
+ uint32_t type = ELF_ST_TYPE(sym->st_info);
if ((bind == STB_GLOBAL || bind == STB_WEAK) && sym->st_shndx != 0) {
- *symbol = reinterpret_cast<void*>(found->resolve_symbol_address(sym));
+ if (type == STT_TLS) {
+ // For a TLS symbol, dlsym returns the address of the current thread's
+ // copy of the symbol. This function may allocate a DTV and/or storage
+ // for the source TLS module. (Allocating a DTV isn't necessary if the
+ // symbol is part of static TLS, but it's simpler to reuse
+ // __tls_get_addr.)
+ soinfo_tls* tls_module = found->get_tls();
+ if (tls_module == nullptr) {
+ DL_ERR("TLS symbol \"%s\" in solib \"%s\" with no TLS segment",
+ sym_name, found->get_realpath());
+ return false;
+ }
+ const TlsIndex ti { tls_module->module_id, sym->st_value };
+ *symbol = TLS_GET_ADDR(&ti);
+ } else {
+ *symbol = reinterpret_cast<void*>(found->resolve_symbol_address(sym));
+ }
failure_guard.Disable();
LD_LOG(kLogDlsym,
"... dlsym successful: sym_name=\"%s\", sym_ver=\"%s\", found in=\"%s\", address=%p",
diff --git a/linker/linker_soinfo.cpp b/linker/linker_soinfo.cpp
index 89119aa..31ee74c 100644
--- a/linker/linker_soinfo.cpp
+++ b/linker/linker_soinfo.cpp
@@ -297,7 +297,11 @@
}
static bool symbol_matches_soaddr(const ElfW(Sym)* sym, ElfW(Addr) soaddr) {
+ // Skip TLS symbols. A TLS symbol's value is relative to the start of the TLS segment rather than
+ // to the start of the solib. The solib only reserves space for the initialized part of the TLS
+ // segment. (i.e. .tdata is followed by .tbss, and .tbss overlaps other sections.)
return sym->st_shndx != SHN_UNDEF &&
+ ELF_ST_TYPE(sym->st_info) != STT_TLS &&
soaddr >= sym->st_value &&
soaddr < sym->st_value + sym->st_size;
}