write32le(location, result | read32le(location));
}
+/// \brief R_AARCH64_TLSDESC_ADR_PAGE21 - Page(G(GTLSDESC(S+A))) - Page(P)
+static std::error_code relocR_AARCH64_TLSDESC_ADR_PAGE21(uint8_t *location,
+ uint64_t P, uint64_t S,
+ int64_t A) {
+ int64_t result = page(S + A) - page(P);
+ if (!isInt<32>(result))
+ return make_out_of_range_reloc_error();
+ result = result >> 12;
+ uint32_t immlo = result & 0x3;
+ uint32_t immhi = result & 0x1FFFFC;
+ immlo = immlo << 29;
+ immhi = immhi << 3;
+ DEBUG(llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -";
+ llvm::dbgs() << " S: " << Twine::utohexstr(S);
+ llvm::dbgs() << " A: " << Twine::utohexstr(A);
+ llvm::dbgs() << " P: " << Twine::utohexstr(P);
+ llvm::dbgs() << " immhi: " << Twine::utohexstr(immhi);
+ llvm::dbgs() << " immlo: " << Twine::utohexstr(immlo);
+ llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n");
+ write32le(location, immlo | immhi | read32le(location));
+ return std::error_code();
+}
+
+/// \brief R_AARCH64_TLSDESC_LD64_LO12_NC - G(GTLSDESC(S+A)) -> S + A
+static std::error_code relocR_AARCH64_TLSDESC_LD64_LO12_NC(uint8_t *location,
+ uint64_t P,
+ uint64_t S,
+ int64_t A) {
+ int32_t result = S + A;
+ DEBUG(llvm::dbgs() << " S: " << Twine::utohexstr(S);
+ llvm::dbgs() << " A: " << Twine::utohexstr(A);
+ llvm::dbgs() << " P: " << Twine::utohexstr(P);
+ llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n");
+ if ((result & 0x7) != 0)
+ return make_unaligned_range_reloc_error();
+ result &= 0xFF8;
+ result <<= 7;
+ write32le(location, result | read32le(location));
+ return std::error_code();
+}
+
+/// \brief R_AARCH64_TLSDESC_ADD_LO12_NC - G(GTLSDESC(S+A)) -> S + A
+static void relocR_AARCH64_TLSDESC_ADD_LO12_NC(uint8_t *location, uint64_t P,
+ uint64_t S, int64_t A) {
+ int32_t result = (int32_t)((S + A) & 0xFFF);
+ result <<= 10;
+ DEBUG(llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -";
+ llvm::dbgs() << " S: " << Twine::utohexstr(S);
+ llvm::dbgs() << " A: " << Twine::utohexstr(A);
+ llvm::dbgs() << " P: " << Twine::utohexstr(P);
+ llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n");
+ write32le(location, result | read32le(location));
+}
+
std::error_code AArch64TargetRelocationHandler::applyRelocation(
ELFWriter &writer, llvm::FileOutputBuffer &buf, const AtomLayout &atom,
const Reference &ref) const {
return relocR_AARCH64_PREL32(loc, reloc, target, addend);
case R_AARCH64_PREL16:
return relocR_AARCH64_PREL16(loc, reloc, target, addend);
- // Runtime only relocations. Ignore here.
- case R_AARCH64_RELATIVE:
- case R_AARCH64_IRELATIVE:
- case R_AARCH64_JUMP_SLOT:
- case R_AARCH64_GLOB_DAT:
- case R_AARCH64_TLS_TPREL64:
- break;
case R_AARCH64_ADR_PREL_PG_HI21:
return relocR_AARCH64_ADR_PREL_PG_HI21(loc, reloc, target, addend);
case R_AARCH64_ADR_PREL_LO21:
relocR_AARCH64_TLSLE_ADD_TPREL_LO12_NC(loc, reloc, target + tpoffset,
addend);
} break;
+ case R_AARCH64_TLSDESC_ADR_PAGE21:
+ return relocR_AARCH64_TLSDESC_ADR_PAGE21(loc, reloc, target, addend);
+ case R_AARCH64_TLSDESC_LD64_LO12_NC:
+ return relocR_AARCH64_TLSDESC_LD64_LO12_NC(loc, reloc, target, addend);
+ case R_AARCH64_TLSDESC_ADD_LO12_NC:
+ relocR_AARCH64_TLSDESC_ADD_LO12_NC(loc, reloc, target, addend);
+ break;
+ case R_AARCH64_TLSDESC_CALL:
+ // Relaxation only to optimize TLS access. Ignore for now.
+ break;
+ // Runtime only relocations. Ignore here.
+ case R_AARCH64_RELATIVE:
+ case R_AARCH64_IRELATIVE:
+ case R_AARCH64_JUMP_SLOT:
+ case R_AARCH64_GLOB_DAT:
+ case R_AARCH64_TLS_TPREL64:
+ case R_AARCH64_TLSDESC:
+ break;
default:
return make_unhandled_reloc_error();
}
// .got values
static const uint8_t AArch64GotAtomContent[8] = {0};
+// tls descriptor .got values, the layout is:
+// struct tlsdesc {
+// ptrdiff_t (*entry) (struct tlsdesc *);
+// void *arg;
+// };
+static const uint8_t AArch64TlsdescGotAtomContent[16] = {0};
+
// .plt value (entry 0)
static const uint8_t AArch64Plt0AtomContent[32] = {
0xf0, 0x7b, 0xbf, 0xa9, // stp x16, x30, [sp,#-16]!
0x20, 0x02, 0x1f, 0xd6 // br x17
};
+// .plt tlsdesc values
+static const uint8_t AArch64PltTlsdescAtomContent[32] = {
+ 0xe2, 0x0f, 0xbf, 0xa9, // stp x2, x3, [sp, #-16]
+ 0x02, 0x00, 0x00, 0x90, // adpr x2, 0
+ 0x03, 0x00, 0x00, 0x90, // adpr x3, 0
+ 0x42, 0x00, 0x40, 0xf9, // ldr x2, [x2, #0]
+ 0x63, 0x00, 0x00, 0x91, // add x3, x3, 0
+ 0x40, 0x00, 0x1f, 0xd6, // br x2
+ 0x1f, 0x20, 0x03, 0xd5, // nop
+ 0x1f, 0x20, 0x03, 0xd5 // nop
+};
+
namespace {
/// \brief Atoms that are used by AArch64 dynamic linking
AArch64GOTPLTAtom(const File &f) : AArch64GOTAtom(f, ".got.plt") {}
};
+class AArch64TLSDESCGOTAtom : public AArch64GOTPLTAtom {
+public:
+ AArch64TLSDESCGOTAtom(const File &f) : AArch64GOTPLTAtom(f) {}
+
+ ArrayRef<uint8_t> rawContent() const override {
+ return ArrayRef<uint8_t>(AArch64TlsdescGotAtomContent, 16);
+ }
+};
+
+
class AArch64PLT0Atom : public PLT0Atom {
public:
AArch64PLT0Atom(const File &f) : PLT0Atom(f) {}
}
};
+class AArch64PLTTLSDESCAtom : public PLTAtom {
+public:
+ AArch64PLTTLSDESCAtom(const File &f) : PLTAtom(f, ".plt") {}
+
+ ArrayRef<uint8_t> rawContent() const override {
+ return ArrayRef<uint8_t>(AArch64PltTlsdescAtomContent, 32);
+ }
+};
+
class ELFPassFile : public SimpleFile {
public:
ELFPassFile(const ELFLinkingContext &eti) : SimpleFile("ELFPassFile") {
case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
static_cast<Derived *>(this)->handleGOTTPREL(ref);
break;
+ case R_AARCH64_TLSDESC_ADR_PAGE21:
+ case R_AARCH64_TLSDESC_LD64_LO12_NC:
+ case R_AARCH64_TLSDESC_ADD_LO12_NC:
+ static_cast<Derived *>(this)->handleTLSDESC(ref);
+ break;
}
}
/// \brief Create a GOT TPREL entry and change the relocation to a PC32 to
/// the GOT.
std::error_code handleGOTTPREL(const Reference &ref) {
- if (isa<DefinedAtom>(ref.target())) {
+ if (isa<DefinedAtom>(ref.target()))
const_cast<Reference &>(ref).setTarget(getGOTTPREL(ref.target()));
+ return std::error_code();
+ }
+
+ /// \brief Generates a double GOT entry with R_AARCH64_TLSDESC dynamic
+ /// relocation reference. Since the dynamic relocation is resolved
+ /// lazily so the GOT associated should be in .got.plt.
+ const GOTAtom *getTLSDESCPLTEntry(const Atom *da) {
+ auto got = _gotMap.find(da);
+ if (got != _gotMap.end())
+ return got->second;
+ auto ga = new (_file._alloc) AArch64TLSDESCGOTAtom(_file);
+ ga->addReferenceELF_AArch64(R_AARCH64_TLSDESC, 0, da, 0);
+ auto pa = new (_file._alloc) AArch64PLTTLSDESCAtom(_file);
+ pa->addReferenceELF_AArch64(R_AARCH64_ADR_PREL_PG_HI21, 4, ga, 0);
+ pa->addReferenceELF_AArch64(R_AARCH64_ADR_PREL_PG_HI21, 8, ga, 0);
+ pa->addReferenceELF_AArch64(R_AARCH64_LDST64_ABS_LO12_NC, 12, ga, 0);
+ pa->addReferenceELF_AArch64(R_AARCH64_ADD_ABS_LO12_NC, 16, ga, 0);
+#ifndef NDEBUG
+ ga->_name = "__got_tlsdesc_";
+ ga->_name += da->name();
+ pa->_name = "__plt_tlsdesc_";
+ pa->_name += da->name();
+#endif
+ _gotMap[da] = ga;
+ _pltMap[da] = pa;
+ _tlsdescVector.push_back(ga);
+ _pltVector.push_back(pa);
+ return ga;
+ }
+
+ std::error_code handleTLSDESC(const Reference &ref) {
+ if (isa<DefinedAtom>(ref.target())) {
+ const_cast<Reference &>(ref).setTarget(getTLSDESCPLTEntry(ref.target()));
}
return std::error_code();
}
got->setOrdinal(ordinal++);
mf.addAtom(*got);
}
+ // Add any tlsdesc GOT relocation after default PLT and iFUNC entries.
+ for (auto &tlsdesc : _tlsdescVector) {
+ tlsdesc->setOrdinal(ordinal++);
+ mf.addAtom(*tlsdesc);
+ }
for (auto obj : _objectVector) {
obj->setOrdinal(ordinal++);
mf.addAtom(*obj);
/// \brief the list of GOT/PLT atoms
std::vector<GOTAtom *> _gotVector;
+ std::vector<GOTAtom *> _tlsdescVector;
std::vector<PLTAtom *> _pltVector;
std::vector<ObjectAtom *> _objectVector;
if (r->kindNamespace() != Reference::KindNamespace::ELF)
continue;
assert(r->kindArch() == Reference::KindArch::AArch64);
- if (r->kindValue() == R_AARCH64_TLS_TPREL64)
+ if ((r->kindValue() == R_AARCH64_TLS_TPREL64) ||
+ (r->kindValue() == R_AARCH64_TLSDESC))
_tlsMap[r->target()] = _tlsMap.size();
}
--- /dev/null
+---
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ OSABI: ELFOSABI_GNU
+ Type: ET_REL
+ Machine: EM_AARCH64
+Sections:
+ - Name: .text
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ AddressAlign: 0x0000000000000004
+ Content: FF4300D1E00F00B900040011E103002A48D03BD50801009108010091000100B9E10B00B9FF430091C0035FD6
+ - Name: .rela.text
+ Type: SHT_RELA
+ Link: .symtab
+ AddressAlign: 0x0000000000000008
+ Info: .text
+ Relocations:
+ - Offset: 0x0000000000000014
+ Symbol: var
+ Type: R_AARCH64_TLSLE_ADD_TPREL_HI12
+ - Offset: 0x0000000000000018
+ Symbol: var
+ Type: R_AARCH64_TLSLE_ADD_TPREL_LO12_NC
+ - Name: .data
+ Type: SHT_PROGBITS
+ Flags: [ SHF_WRITE, SHF_ALLOC ]
+ AddressAlign: 0x0000000000000004
+ Content: ''
+ - Name: .bss
+ Type: SHT_NOBITS
+ Flags: [ SHF_WRITE, SHF_ALLOC ]
+ AddressAlign: 0x0000000000000004
+ Content: ''
+ - Name: .tbss
+ Type: SHT_NOBITS
+ Flags: [ SHF_WRITE, SHF_ALLOC, SHF_TLS ]
+ AddressAlign: 0x0000000000000004
+ Content: 00636C61
+ - Name: .note.GNU-stack
+ Type: SHT_PROGBITS
+ AddressAlign: 0x0000000000000001
+ Content: ''
+Symbols:
+ Local:
+# - Name: test_tls.c
+# Type: STT_FILE
+ - Name: '$d.1'
+ Section: .tbss
+ - Name: '$x.0'
+ Section: .text
+ - Name: .tbss
+ Type: STT_TLS
+ Section: .tbss
+ Global:
+ - Name: foo
+ Type: STT_FUNC
+ Section: .text
+ Size: 0x000000000000002C
+ - Name: var
+ Type: STT_TLS
+ Section: .tbss
+ Size: 0x0000000000000004
+...
--- /dev/null
+# Check for correct offsets when handling relocations for general dynamic TLS
+# access in executable binaries.
+#
+# The test case was generated from following code snippet:
+#
+# == test_tls.c ==
+#
+# __thread int var;
+# void foo (int x) {
+# var = x + 1;
+# }
+#
+# == test_main.c ==
+# #include <stdio.h>
+#
+# extern __thread int var;
+# extern void foo (int);
+#
+# int main () {
+# foo (10);
+# return var;
+# }
+#
+# The objects are compiled with -fpic.
+
+#RUN: yaml2obj -format=elf %p/Inputs/general-dyn-tls-0.yaml -o=%t-t1.o
+#RUN: yaml2obj -format=elf %s -o %t-t0.o
+#RUN: lld -flavor gnu -target arm64 --noinhibit-exec -o %t.exe %t-t0.o %t-t1.o
+#RUN: llvm-readobj -relocations %t.exe | FileCheck %s -check-prefix=CHECKRELOCATION
+#RUN: llvm-objdump -s -t %t.exe | FileCheck %s
+
+#CHECKRELOCATION: Relocations [
+#CHECKRELOCATION: .rela.dyn {
+#CHECKRELOCATION: 0x401090 R_AARCH64_TLSDESC - 0x0
+#CHECKRELOCATION: }
+
+#CHECK: Contents of section .text:
+#CHECK: 400230 a8c31fb8 40018052 0b000094 000000b0 ....@..R........
+# \_ adrp x0, 401000 <_DYNAMIC> (R_AARCH64_TLSDESC_ADR_PAGE21)
+#CHECK-NEXT: 400240 014840f9 00400291 20003fd6 49d03bd5 .H@..@.. .?.I.;.
+# \_ | | ldr x1, [x0,#144] (R_AARCH64_TLSDESC_LD64_LO12_NC)
+# \_ | add x0, x0, #0x90 (R_AARCH64_TLSDESC_ADD_LO12_NC)
+# \_ blr x1 (R_AARCH64_TLSDESC_CALL)
+
+---
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ OSABI: ELFOSABI_GNU
+ Type: ET_REL
+ Machine: EM_AARCH64
+Sections:
+ - Name: .text
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ AddressAlign: 0x0000000000000004
+ Content: FD7BBFA9FD030091FF4300D1E8031F2AA8C31FB8400180520000009400000090010040F90000009120003FD649D03BD5286960B8E003082ABF030091FD7BC1A8C0035FD6
+ - Name: .rela.text
+ Type: SHT_RELA
+ Link: .symtab
+ AddressAlign: 0x0000000000000008
+ Info: .text
+ Relocations:
+ - Offset: 0x0000000000000018
+ Symbol: foo
+ Type: R_AARCH64_CALL26
+ - Offset: 0x000000000000001C
+ Symbol: var
+ Type: R_AARCH64_TLSDESC_ADR_PAGE21
+ - Offset: 0x0000000000000020
+ Symbol: var
+ Type: R_AARCH64_TLSDESC_LD64_LO12_NC
+ - Offset: 0x0000000000000024
+ Symbol: var
+ Type: R_AARCH64_TLSDESC_ADD_LO12_NC
+ - Offset: 0x0000000000000028
+ Symbol: var
+ Type: R_AARCH64_TLSDESC_CALL
+ - Name: .data
+ Type: SHT_PROGBITS
+ Flags: [ SHF_WRITE, SHF_ALLOC ]
+ AddressAlign: 0x0000000000000004
+ Content: ''
+ - Name: .bss
+ Type: SHT_NOBITS
+ Flags: [ SHF_WRITE, SHF_ALLOC ]
+ AddressAlign: 0x0000000000000004
+ Content: ''
+ - Name: .note.GNU-stack
+ Type: SHT_PROGBITS
+ AddressAlign: 0x0000000000000001
+ Content: ''
+Symbols:
+ Local:
+ - Name: '$x.0'
+ Section: .text
+ Global:
+ - Name: foo
+ - Name: main
+ Type: STT_FUNC
+ Section: .text
+ Size: 0x0000000000000044
+ - Name: var
+ Type: STT_TLS