Index: ELF/Config.h =================================================================== --- ELF/Config.h +++ ELF/Config.h @@ -121,6 +121,7 @@ CallGraphProfile; bool AllowMultipleDefinition; bool AndroidPackDynRelocs; + llvm::Optional AndroidTlsTcb; bool ARMHasBlx = false; bool ARMHasMovtMovw = false; bool ARMJ1J2BranchEncoding = false; Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -451,6 +451,12 @@ } } +static llvm::Optional getAndroidTlsTcb(opt::InputArgList &Args) { + if (!Args.hasFlag(OPT_android_tls_tcb, OPT_no_android_tls, false)) + return None; + return args::getInteger(Args, OPT_android_tls_tcb, 0); +} + static std::string getRpath(opt::InputArgList &Args) { std::vector V = args::getStrings(Args, OPT_rpath); return llvm::join(V.begin(), V.end(), ":"); @@ -759,6 +765,7 @@ Args.hasFlag(OPT_allow_multiple_definition, OPT_no_allow_multiple_definition, false) || hasZOption(Args, "muldefs"); + Config->AndroidTlsTcb = getAndroidTlsTcb(Args); Config->AuxiliaryList = args::getStrings(Args, OPT_auxiliary); Config->Bsymbolic = Args.hasArg(OPT_Bsymbolic); Config->BsymbolicFunctions = Args.hasArg(OPT_Bsymbolic_functions); Index: ELF/InputSection.h =================================================================== --- ELF/InputSection.h +++ ELF/InputSection.h @@ -363,6 +363,8 @@ extern std::vector InputSections; Relocation *getRISCVPCRelHi20(const Symbol *Sym, const uint64_t Addend); + +int64_t getTlsTpOffset(); } // namespace elf std::string toString(const elf::InputSectionBase *); Index: ELF/InputSection.cpp =================================================================== --- ELF/InputSection.cpp +++ ELF/InputSection.cpp @@ -568,7 +568,12 @@ // A TLS symbol's virtual address is relative to the TLS segment. Add a // target-specific adjustment to produce a thread-pointer-relative offset. -static int64_t getTlsTpOffset() { +int64_t lld::elf::getTlsTpOffset() { + // Android uses a variant 1 TLS layout on all architectures with a + // non-standard TCB size. + if (Config->AndroidTlsTcb) + return alignTo(*Config->AndroidTlsTcb, Out::TlsPhdr->p_align); + switch (Config->EMachine) { case EM_ARM: case EM_AARCH64: Index: ELF/Options.td =================================================================== --- ELF/Options.td +++ ELF/Options.td @@ -63,6 +63,9 @@ "Allow multiple definitions", "Do not allow multiple definitions (default)">; +defm android_tls_tcb: Eq<"android-tls-tcb", + "Set the TLS TCB size and emit PT_ANDROID_TLS_TPOFF">, MetaVarName<"">; + defm apply_dynamic_relocs: B<"apply-dynamic-relocs", "Apply link-time values for dynamic relocations", "Do not apply link-time values for dynamic relocations (default)">; @@ -217,6 +220,9 @@ def nostdlib: F<"nostdlib">, HelpText<"Only search directories specified on the command line">; +def no_android_tls: F<"no-android-tls">, + HelpText<"Use the ordinary TLS layout for the target (default)">; + def no_color_diagnostics: F<"no-color-diagnostics">, HelpText<"Do not use colors in diagnostics">; Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -1915,9 +1915,16 @@ for (OutputSection *Sec : OutputSections) if (Sec->Flags & SHF_TLS) TlsHdr->add(Sec); - if (TlsHdr->FirstSec) + if (TlsHdr->FirstSec) { Ret.push_back(TlsHdr); + // Create a PT_ANDROID_TLS_TPOFF segment to record the TP-to-TLS offset + // used to resolve an executable's TLS LE relocations. The Bionic linker + // uses this segment to verify that a binary was linked correctly. + if (Config->AndroidTlsTcb && !Config->Shared) + AddHdr(PT_ANDROID_TLS_TPOFF, 0); + } + // Add an entry for .dynamic. if (In.DynSymTab) AddHdr(PT_DYNAMIC, In.Dynamic->getParent()->getPhdrFlags()) @@ -2170,6 +2177,14 @@ if (P->p_type == PT_TLS && P->p_memsz) P->p_memsz = alignTo(P->p_memsz, P->p_align); } + + // Initialize PT_ANDROID_TLS_TPOFF in a second pass because, while Android + // uses a variant 1 layout, TLS offsets can't generally be calculated until + // the PT_TLS segment's p_memsz field is finalized. + for (PhdrEntry *P : Phdrs) { + if (P->p_type == PT_ANDROID_TLS_TPOFF) + P->p_vaddr = getTlsTpOffset(); + } } // A helper struct for checkSectionOverlap. Index: test/ELF/aarch64-android-tls-tcb.s =================================================================== --- /dev/null +++ test/ELF/aarch64-android-tls-tcb.s @@ -0,0 +1,30 @@ +# REQUIRES: aarch64 +# RUN: llvm-mc -filetype=obj -triple=aarch64-none-linux-android %s -o %tmain.o +# RUN: ld.lld --android-tls-tcb=17185 %tmain.o -o %tout +# RUN: llvm-objdump -d %tout | FileCheck %s +# RUN: llvm-readelf -l %tout | FileCheck -check-prefix=PHDR %s + +# The TLS segment starts at 17185 aligned by 16 (0x4321 rounded to 0x4330). + +#CHECK: Disassembly of section .text: +#CHECK: _start: +#CHECK: 210000: 40 d0 3b d5 mrs x0, TPIDR_EL0 +#CHECK: 210004: 00 10 40 91 add x0, x0, #4, lsl #12 +#CHECK: 210008: 00 c0 0c 91 add x0, x0, #816 + +#PHDR: TLS {{.*}} 0x000000 0x000010 R 0x10 +#PHDR-NEXT: ANDROID_TLS_TPOFF 0x000000 0x0000000000004330 0x0000000000000000 0x000000 0x000000 0x0 + + .globl _start +_start: + mrs x0, TPIDR_EL0 + add x0, x0, :tprel_hi12:v1 + add x0, x0, :tprel_lo12_nc:v1 + + .section .tbss,"awT",@nobits + .type v1,@object + .globl v1 + .p2align 4 +v1: + .space 16 + .size v1, 16