diff options
Diffstat (limited to 'linker/linker.cpp')
-rw-r--r-- | linker/linker.cpp | 120 |
1 files changed, 105 insertions, 15 deletions
diff --git a/linker/linker.cpp b/linker/linker.cpp index 1f259e16c..412b8eb8e 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -65,8 +65,10 @@ #include "linker_phdr.h" #include "linker_relocs.h" #include "linker_reloc_iterators.h" +#include "linker_tls.h" #include "linker_utils.h" +#include "private/bionic_globals.h" #include "android-base/macros.h" #include "android-base/strings.h" #include "android-base/stringprintf.h" @@ -601,6 +603,9 @@ class LoadTask { } void set_fd(int fd, bool assume_ownership) { + if (fd_ != -1 && close_fd_) { + close(fd_); + } fd_ = fd; close_fd_ = assume_ownership; } @@ -1652,6 +1657,7 @@ bool find_libraries(android_namespace_t* ns, if (!si->is_linked() && !si->prelink_image()) { return false; } + register_soinfo_tls(si); } // Step 4: Construct the global group. Note: DF_1_GLOBAL bit of a library is @@ -1887,6 +1893,7 @@ static void soinfo_unload_impl(soinfo* root) { si->get_realpath(), si); notify_gdb_of_unload(si); + unregister_soinfo_tls(si); get_cfi_shadow()->BeforeUnload(si); soinfo_free(si); } @@ -2666,16 +2673,32 @@ static ElfW(Addr) get_addend(ElfW(Rela)* rela, ElfW(Addr) reloc_addr __unused) { #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) { @@ -2698,7 +2721,22 @@ bool soinfo::relocate(const VersionTracker& version_tracker, ElfRelIteratorT&& r 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; @@ -2735,6 +2773,10 @@ bool soinfo::relocate(const VersionTracker& version_tracker, ElfRelIteratorT&& r 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: @@ -2782,12 +2824,21 @@ bool soinfo::relocate(const VersionTracker& version_tracker, ElfRelIteratorT&& r } } #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) { @@ -2860,6 +2911,40 @@ bool soinfo::relocate(const VersionTracker& version_tracker, ElfRelIteratorT&& r *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()) { + const TlsModule& mod = get_tls_module(lsi_tls->module_id); + if (mod.static_offset != SIZE_MAX) { + tpoff += mod.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: @@ -2961,14 +3046,6 @@ bool soinfo::relocate(const VersionTracker& version_tracker, ElfRelIteratorT&& r */ 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); @@ -3073,6 +3150,19 @@ bool soinfo::prelink_image() { &ARM_exidx, &ARM_exidx_count); #endif + TlsSegment tls_segment; + if (__bionic_get_tls_segment(phdr, phnum, load_bias, &tls_segment)) { + if (!__bionic_check_tls_alignment(&tls_segment.alignment)) { + if (!relocating_linker) { + DL_ERR("TLS segment alignment in \"%s\" is not a power of 2: %zu", + get_realpath(), tls_segment.alignment); + } + return false; + } + tls_ = std::make_unique<soinfo_tls>(); + tls_->segment = tls_segment; + } + // Extract useful information from dynamic section. // Note that: "Except for the DT_NULL element at the end of the array, // and the relative order of DT_NEEDED elements, entries may appear in any order." |