Implement arm64 TLSDESC
Each TLSDESC relocation relocates a 2-word descriptor in the GOT that
contains:
- the address of a TLS resolver function
- an argument to pass (indirectly) to the resolver function
(Specifically, the address of the 2-word descriptor is passed to the
resolver.)
The loader resolves R_GENERIC_TLSDESC relocations using one of three
resolver functions that it defines:
- tlsdesc_resolver_static
- tlsdesc_resolver_dynamic
- tlsdesc_resolver_unresolved_weak
The resolver functions are written in assembly because they have a
restrictive calling convention. They're only allowed to modify x0 and
(apparently) the condition codes.
For a relocation to memory in static TLS (i.e. the executable or an solib
loaded initially), the loader uses a simple resolver function,
tlsdesc_resolver_static, that returns the static offset it receives from
the loader.
For relocations to dynamic TLS memory (i.e. memory in a dlopen'ed solib),
the loader uses tlsdesc_resolver_dynamic, which allocates TLS memory on
demand. It inlines the fast path of __tls_get_addr, then falls back to
__tls_get_addr when it needs to allocate memory. The loader handles these
dynamic TLS relocations in two passes:
- In the first pass, it allocates a table of TlsDynamicResolverArg
objects, one per dynamic TLSDESC relocation.
- In the second pass, once the table is finalized, it writes the
addresses of the TlsDynamicResolverArg objects into the TLSDESC
relocations.
tlsdesc_resolver_unresolved_weak returns a negated thread pointer so that
taking the address of an unresolved weak TLS symbols produces NULL.
The loader handles R_GENERIC_TLSDESC in a target-independent way, but
only for arm64, because Bionic has only implemented the resolver functions
for arm64.
Bug: http://b/78026329
Test: bionic unit tests
Test: check that backtrace works inside a resolver function and inside
__tls_get_addr called from a resolver
(gdbclient.py, b __tls_get_addr, bt)
Change-Id: I752e59ff986292449892c449dad2546e6f0ff7b6
diff --git a/linker/Android.bp b/linker/Android.bp
index 5ae09ba..033860a 100644
--- a/linker/Android.bp
+++ b/linker/Android.bp
@@ -103,6 +103,7 @@
name: "linker_sources_arm64",
srcs: [
"arch/arm64/begin.S",
+ "arch/arm64/tlsdesc_resolver.S",
],
}
diff --git a/linker/arch/arm64/tlsdesc_resolver.S b/linker/arch/arm64/tlsdesc_resolver.S
new file mode 100644
index 0000000..ef46839
--- /dev/null
+++ b/linker/arch/arm64/tlsdesc_resolver.S
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <private/bionic_asm.h>
+#include <private/bionic_asm_tls.h>
+
+.globl __tls_get_addr
+
+// These resolver functions must preserve every register except x0. They set x0
+// to the offset of the TLS symbol relative to the thread pointer.
+
+ENTRY_PRIVATE(tlsdesc_resolver_static)
+ ldr x0, [x0, #8]
+ ret
+END(tlsdesc_resolver_static)
+
+ENTRY_PRIVATE(tlsdesc_resolver_dynamic)
+ stp x19, x20, [sp, #-32]!
+ .cfi_def_cfa_offset 32
+ .cfi_rel_offset x19, 0
+ .cfi_rel_offset x20, 8
+ stp x21, x22, [sp, #16]
+ .cfi_rel_offset x21, 16
+ .cfi_rel_offset x22, 24
+
+ mrs x19, tpidr_el0 // __get_tls()
+ ldr x20, [x19, #(TLS_SLOT_DTV * 8)]
+ ldr x21, [x20] // TlsDtv::generation
+
+ ldr x0, [x0, #8] // TlsDynamicResolverArg*
+ ldr x22, [x0] // TlsDynamicResolverArg::generation
+
+ cmp x21, x22
+ b.lo .fallback
+
+ ldr x21, [x0, #8] // TlsIndex::module
+ ldr x22, [x0, #16] // TlsIndex::offset
+ ldr x21, [x20, x21, lsl #3] // TlsDtv::modules[module]
+ cbz x21, .fallback
+ add x0, x21, x22
+ sub x0, x0, x19
+
+ ldp x21, x22, [sp, #16]
+ .cfi_remember_state
+ .cfi_restore x21
+ .cfi_restore x22
+ ldp x19, x20, [sp], #32
+ .cfi_adjust_cfa_offset -32
+ .cfi_restore x19
+ .cfi_restore x20
+ ret
+
+.fallback:
+ .cfi_restore_state
+ ldp x21, x22, [sp, #16]
+ .cfi_restore x21
+ .cfi_restore x22
+ ldp x19, x20, [sp], #32
+ .cfi_adjust_cfa_offset -32
+ .cfi_restore x19
+ .cfi_restore x20
+ b tlsdesc_resolver_dynamic_slow_path
+END(tlsdesc_resolver_dynamic)
+
+#define SAVE_REG(x, slot) \
+ str x, [sp, #((slot) * 8)]; \
+ .cfi_rel_offset x, (slot) * 8; \
+
+#define SAVE_GPR_PAIR(x, y, slot) \
+ stp x, y, [sp, #((slot) * 8)]; \
+ .cfi_rel_offset x, (slot) * 8; \
+ .cfi_rel_offset y, ((slot) + 1) * 8; \
+
+#define SAVE_VEC_PAIR(x, y, slot) \
+ stp x, y, [sp, #((slot) * 8)]; \
+ .cfi_rel_offset x, (slot) * 8; \
+ .cfi_rel_offset y, ((slot) + 2) * 8; \
+
+#define RESTORE_REG(x, slot) \
+ ldr x, [sp, #((slot) * 8)]; \
+ .cfi_restore x; \
+
+#define RESTORE_REG_PAIR(x, y, slot) \
+ ldp x, y, [sp, #((slot) * 8)]; \
+ .cfi_restore x; \
+ .cfi_restore y; \
+
+// On entry, x0 is the address of a TlsDynamicResolverArg object rather than
+// the TlsDescriptor address passed to the original resolver function.
+ENTRY_PRIVATE(tlsdesc_resolver_dynamic_slow_path)
+ sub sp, sp, #(8 * 84)
+ .cfi_def_cfa_offset (8 * 84)
+ SAVE_GPR_PAIR(x29, x30, 0)
+ mov x29, sp
+
+ // Avoid leaking the contents of the shadow call stack register (x18) into
+ // memory. x19 through x29 are callee-save registers, so we do not need to
+ // save them.
+ SAVE_GPR_PAIR(x1, x2, 2)
+ SAVE_GPR_PAIR(x3, x4, 4)
+ SAVE_GPR_PAIR(x5, x6, 6)
+ SAVE_GPR_PAIR(x7, x8, 8)
+ SAVE_GPR_PAIR(x9, x10, 10)
+ SAVE_GPR_PAIR(x11, x12, 12)
+ SAVE_GPR_PAIR(x13, x14, 14)
+ SAVE_GPR_PAIR(x15, x16, 16)
+ SAVE_REG(x17, 18)
+
+ SAVE_VEC_PAIR(q0, q1, 20)
+ SAVE_VEC_PAIR(q2, q3, 24)
+ SAVE_VEC_PAIR(q4, q5, 28)
+ SAVE_VEC_PAIR(q6, q7, 32)
+ SAVE_VEC_PAIR(q8, q9, 36)
+ SAVE_VEC_PAIR(q10, q11, 40)
+ SAVE_VEC_PAIR(q12, q13, 44)
+ SAVE_VEC_PAIR(q14, q15, 48)
+ SAVE_VEC_PAIR(q16, q17, 52)
+ SAVE_VEC_PAIR(q18, q19, 56)
+ SAVE_VEC_PAIR(q20, q21, 60)
+ SAVE_VEC_PAIR(q22, q23, 64)
+ SAVE_VEC_PAIR(q24, q25, 68)
+ SAVE_VEC_PAIR(q26, q27, 72)
+ SAVE_VEC_PAIR(q28, q29, 76)
+ SAVE_VEC_PAIR(q30, q31, 80)
+
+ add x0, x0, #8
+ bl __tls_get_addr
+ mrs x1, tpidr_el0 // __get_tls()
+ sub x0, x0, x1
+
+ RESTORE_REG_PAIR(q30, q31, 80)
+ RESTORE_REG_PAIR(q28, q29, 76)
+ RESTORE_REG_PAIR(q26, q27, 72)
+ RESTORE_REG_PAIR(q24, q25, 68)
+ RESTORE_REG_PAIR(q22, q23, 64)
+ RESTORE_REG_PAIR(q20, q21, 60)
+ RESTORE_REG_PAIR(q18, q19, 56)
+ RESTORE_REG_PAIR(q16, q17, 52)
+ RESTORE_REG_PAIR(q14, q15, 48)
+ RESTORE_REG_PAIR(q12, q13, 44)
+ RESTORE_REG_PAIR(q10, q11, 40)
+ RESTORE_REG_PAIR(q8, q9, 36)
+ RESTORE_REG_PAIR(q6, q7, 32)
+ RESTORE_REG_PAIR(q4, q5, 28)
+ RESTORE_REG_PAIR(q2, q3, 24)
+ RESTORE_REG_PAIR(q0, q1, 20)
+
+ RESTORE_REG(x17, 18)
+ RESTORE_REG_PAIR(x15, x16, 16)
+ RESTORE_REG_PAIR(x13, x14, 14)
+ RESTORE_REG_PAIR(x11, x12, 12)
+ RESTORE_REG_PAIR(x9, x10, 10)
+ RESTORE_REG_PAIR(x7, x8, 8)
+ RESTORE_REG_PAIR(x5, x6, 6)
+ RESTORE_REG_PAIR(x3, x4, 4)
+ RESTORE_REG_PAIR(x1, x2, 2)
+
+ RESTORE_REG_PAIR(x29, x30, 0)
+ add sp, sp, #(8 * 84)
+ .cfi_def_cfa_offset 0
+ ret
+END(tlsdesc_resolver_dynamic_slow_path)
+
+// The address of an unresolved weak TLS symbol evaluates to NULL with TLSDESC.
+// The value returned by this function is added to the thread pointer, so return
+// a negated thread pointer to cancel it out.
+ENTRY_PRIVATE(tlsdesc_resolver_unresolved_weak)
+ str x19, [sp, #-16]!
+ .cfi_def_cfa_offset 16
+ .cfi_rel_offset x19, 0
+ ldr x19, [x0, #8]
+ mrs x0, tpidr_el0 // __get_tls()
+ sub x0, x19, x0
+ ldr x19, [sp], #16
+ .cfi_def_cfa_offset 0
+ .cfi_restore x19
+ ret
+END(tlsdesc_resolver_unresolved_weak)
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 4dcdf7e..d0c740b 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -2698,6 +2698,7 @@
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();
+ std::vector<std::pair<TlsDescriptor*, size_t>> deferred_tlsdesc_relocs;
for (size_t idx = 0; rel_iterator.has_next(); ++idx) {
const auto rel = rel_iterator.next();
@@ -2977,6 +2978,49 @@
#endif // !defined(__aarch64__)
#if defined(__aarch64__)
+ // Bionic currently only implements TLSDESC for arm64. This implementation should work with
+ // other architectures, as long as the resolver functions are implemented.
+ case R_GENERIC_TLSDESC:
+ count_relocation(kRelocRelative);
+ MARK(rel->r_offset);
+ {
+ TlsDescriptor* desc = reinterpret_cast<TlsDescriptor*>(reloc);
+ if (lsi == nullptr) {
+ // Unresolved weak relocation.
+ desc->func = tlsdesc_resolver_unresolved_weak;
+ desc->arg = addend;
+ TRACE_TYPE(RELO, "RELO TLSDESC %16p <- unresolved weak 0x%zx %s\n",
+ reinterpret_cast<void*>(reloc), static_cast<size_t>(addend), sym_name);
+ } else {
+ CHECK(lsi->get_tls() != nullptr); // We rejected a missing TLS segment above.
+ size_t module_id = lsi->get_tls()->module_id;
+ const TlsModule& mod = get_tls_module(module_id);
+ if (mod.static_offset != SIZE_MAX) {
+ desc->func = tlsdesc_resolver_static;
+ desc->arg = mod.static_offset - tls_tp_base + sym_addr + addend;
+ TRACE_TYPE(RELO, "RELO TLSDESC %16p <- static (0x%zx - 0x%zx + 0x%zx + 0x%zx) %s\n",
+ reinterpret_cast<void*>(reloc), mod.static_offset, tls_tp_base,
+ static_cast<size_t>(sym_addr), static_cast<size_t>(addend), sym_name);
+ } else {
+ tlsdesc_args_.push_back({
+ .generation = mod.first_generation,
+ .index.module_id = module_id,
+ .index.offset = sym_addr + addend,
+ });
+ // Defer the TLSDESC relocation until the address of the TlsDynamicResolverArg object
+ // is finalized.
+ deferred_tlsdesc_relocs.push_back({ desc, tlsdesc_args_.size() - 1 });
+ const TlsDynamicResolverArg& desc_arg = tlsdesc_args_.back();
+ TRACE_TYPE(RELO, "RELO TLSDESC %16p <- dynamic (gen %zu, mod %zu, off %zu) %s",
+ reinterpret_cast<void*>(reloc), desc_arg.generation,
+ desc_arg.index.module_id, desc_arg.index.offset, sym_name);
+ }
+ }
+ }
+ break;
+#endif // defined(R_GENERIC_TLSDESC)
+
+#if defined(__aarch64__)
case R_AARCH64_ABS64:
count_relocation(kRelocAbsolute);
MARK(rel->r_offset);
@@ -3145,6 +3189,13 @@
return false;
}
}
+
+ for (const std::pair<TlsDescriptor*, size_t>& pair : deferred_tlsdesc_relocs) {
+ TlsDescriptor* desc = pair.first;
+ desc->func = tlsdesc_resolver_dynamic;
+ desc->arg = reinterpret_cast<size_t>(&tlsdesc_args_[pair.second]);
+ }
+
return true;
}
#endif // !defined(__mips__)
diff --git a/linker/linker_soinfo.h b/linker/linker_soinfo.h
index 3499cf7..dd9c6aa 100644
--- a/linker/linker_soinfo.h
+++ b/linker/linker_soinfo.h
@@ -32,6 +32,7 @@
#include <memory>
#include <string>
+#include <vector>
#include "private/bionic_elf_tls.h"
#include "linker_namespaces.h"
@@ -379,6 +380,7 @@
// version >= 5
std::unique_ptr<soinfo_tls> tls_;
+ std::vector<TlsDynamicResolverArg> tlsdesc_args_;
};
// This function is used by dlvsym() to calculate hash of sym_ver
diff --git a/linker/linker_tls.h b/linker/linker_tls.h
index fbb1dcf..87e1f0d 100644
--- a/linker/linker_tls.h
+++ b/linker/linker_tls.h
@@ -30,6 +30,8 @@
#include <stdlib.h>
+#include "private/bionic_elf_tls.h"
+
struct TlsModule;
struct soinfo;
@@ -40,3 +42,24 @@
void unregister_soinfo_tls(soinfo* si);
const TlsModule& get_tls_module(size_t module_id);
+
+typedef size_t TlsDescResolverFunc(size_t);
+
+struct TlsDescriptor {
+#if defined(__arm__)
+ size_t arg;
+ TlsDescResolverFunc* func;
+#else
+ TlsDescResolverFunc* func;
+ size_t arg;
+#endif
+};
+
+struct TlsDynamicResolverArg {
+ size_t generation;
+ TlsIndex index;
+};
+
+__LIBC_HIDDEN__ extern "C" size_t tlsdesc_resolver_static(size_t);
+__LIBC_HIDDEN__ extern "C" size_t tlsdesc_resolver_dynamic(size_t);
+__LIBC_HIDDEN__ extern "C" size_t tlsdesc_resolver_unresolved_weak(size_t);