diff --git a/lld/include/lld/ReaderWriter/MachOLinkingContext.h b/lld/include/lld/ReaderWriter/MachOLinkingContext.h index e27395fb2d4a..531f41c29496 100644 --- a/lld/include/lld/ReaderWriter/MachOLinkingContext.h +++ b/lld/include/lld/ReaderWriter/MachOLinkingContext.h @@ -244,6 +244,9 @@ public: // GOT creation Pass should be run. bool needsGOTPass() const; + /// Pass to add TLV sections. + bool needsTLVPass() const; + /// Pass to transform __compact_unwind into __unwind_info should be run. bool needsCompactUnwindPass() const; diff --git a/lld/lib/ReaderWriter/MachO/ArchHandler.h b/lld/lib/ReaderWriter/MachO/ArchHandler.h index 7f0961ebc807..120f5dfd4cd2 100644 --- a/lld/lib/ReaderWriter/MachO/ArchHandler.h +++ b/lld/lib/ReaderWriter/MachO/ArchHandler.h @@ -49,6 +49,12 @@ public: return false; } + /// Used by TLVPass to locate TLV References. + virtual bool isTLVAccess(const Reference &) const { return false; } + + /// Used by the TLVPass to update TLV References. + virtual void updateReferenceToTLV(const Reference *) {} + /// Used by ShimPass to insert shims in branches that switch mode. virtual bool isNonCallBranch(const Reference &) = 0; diff --git a/lld/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp b/lld/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp index 8605c7a5789c..003d34ea41b8 100644 --- a/lld/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp +++ b/lld/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp @@ -59,6 +59,19 @@ public: } } + bool isTLVAccess(const Reference &ref) const override { + assert(ref.kindNamespace() == Reference::KindNamespace::mach_o); + assert(ref.kindArch() == Reference::KindArch::x86_64); + return ref.kindValue() == ripRel32Tlv; + } + + void updateReferenceToTLV(const Reference *ref) override { + assert(ref->kindNamespace() == Reference::KindNamespace::mach_o); + assert(ref->kindArch() == Reference::KindArch::x86_64); + assert(ref->kindValue() == ripRel32Tlv); + const_cast(ref)->setKindValue(ripRel32); + } + /// Used by GOTPass to update GOT References void updateReferenceToGOT(const Reference *ref, bool targetNowGOT) override { assert(ref->kindNamespace() == Reference::KindNamespace::mach_o); @@ -173,6 +186,7 @@ private: ripRel32Minus4Anon, /// ex: movw $0x12345678, L1(%rip) ripRel32GotLoad, /// ex: movq _foo@GOTPCREL(%rip), %rax ripRel32Got, /// ex: pushq _foo@GOTPCREL(%rip) + ripRel32Tlv, /// ex: movq _foo@TLVP(%rip), %rdi pointer64, /// ex: .quad _foo pointer64Anon, /// ex: .quad L1 delta64, /// ex: .quad _foo - . @@ -195,6 +209,8 @@ private: /// relocatable object (yay for implicit contracts!). unwindInfoToEhFrame, /// Fix low 24 bits of compact unwind encoding to /// refer to __eh_frame entry. + tlvInitSectionOffset /// Location contains offset tlv init-value atom + /// within the __thread_data section. }; Reference::KindValue kindFromReloc(const normalized::Relocation &reloc); @@ -227,7 +243,8 @@ const Registry::KindStrings ArchHandler_x86_64::_sKindStrings[] = { LLD_KIND_STRING_ENTRY(ripRel32Minus4Anon), LLD_KIND_STRING_ENTRY(ripRel32GotLoad), LLD_KIND_STRING_ENTRY(ripRel32GotLoadNowLea), - LLD_KIND_STRING_ENTRY(ripRel32Got), LLD_KIND_STRING_ENTRY(lazyPointer), + LLD_KIND_STRING_ENTRY(ripRel32Got), LLD_KIND_STRING_ENTRY(ripRel32Tlv), + LLD_KIND_STRING_ENTRY(lazyPointer), LLD_KIND_STRING_ENTRY(lazyImmediateLocation), LLD_KIND_STRING_ENTRY(pointer64), LLD_KIND_STRING_ENTRY(pointer64Anon), LLD_KIND_STRING_ENTRY(delta32), LLD_KIND_STRING_ENTRY(delta64), @@ -236,6 +253,7 @@ const Registry::KindStrings ArchHandler_x86_64::_sKindStrings[] = { LLD_KIND_STRING_ENTRY(imageOffset), LLD_KIND_STRING_ENTRY(imageOffsetGot), LLD_KIND_STRING_ENTRY(unwindFDEToFunction), LLD_KIND_STRING_ENTRY(unwindInfoToEhFrame), + LLD_KIND_STRING_ENTRY(tlvInitSectionOffset), LLD_KIND_STRING_END }; @@ -322,6 +340,8 @@ ArchHandler_x86_64::kindFromReloc(const Relocation &reloc) { return ripRel32GotLoad; case X86_64_RELOC_GOT | rPcRel | rExtern | rLength4: return ripRel32Got; + case X86_64_RELOC_TLV | rPcRel | rExtern | rLength4: + return ripRel32Tlv; case X86_64_RELOC_UNSIGNED | rExtern | rLength8: return pointer64; case X86_64_RELOC_UNSIGNED | rLength8: @@ -383,14 +403,23 @@ ArchHandler_x86_64::getReferenceInfo(const Relocation &reloc, return atomFromAddress(reloc.symbol, targetAddress, target, addend); case ripRel32GotLoad: case ripRel32Got: + case ripRel32Tlv: if (E ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; *addend = *(const little32_t *)fixupContent; return std::error_code(); + case tlvInitSectionOffset: case pointer64: if (E ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; - *addend = *(const little64_t *)fixupContent; + // If this is the 3rd pointer of a tlv-thunk (i.e. the pointer to the TLV's + // initial value) we need to handle it specially. + if (inAtom->contentType() == DefinedAtom::typeThunkTLV && + offsetInAtom == 16) { + *kind = tlvInitSectionOffset; + assert(*addend == 0 && "TLV-init has non-zero addend?"); + } else + *addend = *(const little64_t *)fixupContent; return std::error_code(); case pointer64Anon: targetAddress = *(const little64_t *)fixupContent; @@ -508,12 +537,16 @@ void ArchHandler_x86_64::applyFixupFinal( case ripRel32Anon: case ripRel32Got: case ripRel32GotLoad: + case ripRel32Tlv: *loc32 = targetAddress - (fixupAddress + 4) + ref.addend(); return; case pointer64: case pointer64Anon: *loc64 = targetAddress + ref.addend(); return; + case tlvInitSectionOffset: + *loc64 = targetAddress - findSectionAddress(*ref.target()) + ref.addend(); + return; case ripRel32Minus1: case ripRel32Minus1Anon: *loc32 = targetAddress - (fixupAddress + 5) + ref.addend(); @@ -583,11 +616,13 @@ void ArchHandler_x86_64::applyFixupRelocatable(const Reference &ref, case ripRel32: case ripRel32Got: case ripRel32GotLoad: + case ripRel32Tlv: *loc32 = ref.addend(); return; case ripRel32Anon: *loc32 = (targetAddress - (fixupAddress + 4)) + ref.addend(); return; + case tlvInitSectionOffset: case pointer64: *loc64 = ref.addend(); return; @@ -682,6 +717,11 @@ void ArchHandler_x86_64::appendSectionRelocations( appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, X86_64_RELOC_GOT_LOAD | rPcRel | rExtern | rLength4 ); return; + case ripRel32Tlv: + appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, + X86_64_RELOC_TLV | rPcRel | rExtern | rLength4 ); + return; + case tlvInitSectionOffset: case pointer64: appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, X86_64_RELOC_UNSIGNED | rExtern | rLength8); diff --git a/lld/lib/ReaderWriter/MachO/CMakeLists.txt b/lld/lib/ReaderWriter/MachO/CMakeLists.txt index e396537c63c8..7ce782af6f99 100644 --- a/lld/lib/ReaderWriter/MachO/CMakeLists.txt +++ b/lld/lib/ReaderWriter/MachO/CMakeLists.txt @@ -15,6 +15,7 @@ add_llvm_library(lldMachO MachONormalizedFileYAML.cpp ShimPass.cpp StubsPass.cpp + TLVPass.cpp WriterMachO.cpp LINK_LIBS lldCore diff --git a/lld/lib/ReaderWriter/MachO/MachOLinkingContext.cpp b/lld/lib/ReaderWriter/MachO/MachOLinkingContext.cpp index dc8b8c6223d6..30ce7739732a 100644 --- a/lld/lib/ReaderWriter/MachO/MachOLinkingContext.cpp +++ b/lld/lib/ReaderWriter/MachO/MachOLinkingContext.cpp @@ -336,6 +336,17 @@ bool MachOLinkingContext::needsShimPass() const { } } +bool MachOLinkingContext::needsTLVPass() const { + switch (_outputMachOType) { + case MH_BUNDLE: + case MH_EXECUTE: + case MH_DYLIB: + return true; + default: + return false; + } +} + StringRef MachOLinkingContext::binderSymbolName() const { return archHandler().stubInfo().binderSymbolName; } @@ -588,6 +599,8 @@ void MachOLinkingContext::addPasses(PassManager &pm) { mach_o::addCompactUnwindPass(pm, *this); if (needsGOTPass()) mach_o::addGOTPass(pm, *this); + if (needsTLVPass()) + mach_o::addTLVPass(pm, *this); if (needsShimPass()) mach_o::addShimPass(pm, *this); // Shim pass must run after stubs pass. } diff --git a/lld/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp b/lld/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp index 7a7464545348..092ffd64c578 100644 --- a/lld/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp +++ b/lld/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp @@ -94,7 +94,8 @@ SegmentInfo::SegmentInfo(StringRef n) class Util { public: Util(const MachOLinkingContext &ctxt) - : _ctx(ctxt), _archHandler(ctxt.archHandler()), _entryAtom(nullptr) {} + : _ctx(ctxt), _archHandler(ctxt.archHandler()), _entryAtom(nullptr), + _hasTLVDescriptors(false) {} ~Util(); void assignAtomsToSections(const lld::File &atomFile); @@ -168,6 +169,7 @@ private: const DefinedAtom *_entryAtom; AtomToIndex _atomToSymbolIndex; std::vector _machHeaderAliasAtoms; + bool _hasTLVDescriptors; }; Util::~Util() { @@ -246,6 +248,12 @@ const MachOFinalSectionFromAtomType sectsToAtomType[] = { typeTerminatorPtr), ENTRY("__DATA", "__got", S_NON_LAZY_SYMBOL_POINTERS, typeGOT), + ENTRY("__DATA", "__thread_vars", S_THREAD_LOCAL_VARIABLES, + typeThunkTLV), + ENTRY("__DATA", "__thread_data", S_THREAD_LOCAL_REGULAR, + typeTLVInitialData), + ENTRY("__DATA", "__thread_ptrs", S_THREAD_LOCAL_VARIABLE_POINTERS, + typeTLVInitializerPtr), ENTRY("__DATA", "__bss", S_ZEROFILL, typeZeroFill), ENTRY("__DATA", "__interposing", S_INTERPOSING, typeInterposingTuples), }; @@ -263,6 +271,9 @@ SectionInfo *Util::getFinalSection(DefinedAtom::ContentType atomType) { case DefinedAtom::typeStubHelper: sectionAttrs = S_ATTR_PURE_INSTRUCTIONS; break; + case DefinedAtom::typeThunkTLV: + _hasTLVDescriptors = true; + break; default: break; } @@ -1169,10 +1180,12 @@ uint32_t Util::fileFlags() { if (_ctx.outputMachOType() == MH_OBJECT) { return MH_SUBSECTIONS_VIA_SYMBOLS; } else { + uint32_t flags = MH_DYLDLINK | MH_NOUNDEFS | MH_TWOLEVEL; if ((_ctx.outputMachOType() == MH_EXECUTE) && _ctx.PIE()) - return MH_DYLDLINK | MH_NOUNDEFS | MH_TWOLEVEL | MH_PIE; - else - return MH_DYLDLINK | MH_NOUNDEFS | MH_TWOLEVEL; + flags |= MH_PIE; + if (_hasTLVDescriptors) + flags |= (MH_PIE | MH_HAS_TLV_DESCRIPTORS); + return flags; } } diff --git a/lld/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp b/lld/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp index 6ef5a0b4a459..971c44eba95e 100644 --- a/lld/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp +++ b/lld/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp @@ -79,6 +79,9 @@ const MachORelocatableSectionToAtomType sectsToAtomType[] = { ENTRY("", "", S_NON_LAZY_SYMBOL_POINTERS, typeGOT), ENTRY("__DATA", "__interposing", S_INTERPOSING, typeInterposingTuples), + ENTRY("__DATA", "__thread_vars", S_THREAD_LOCAL_VARIABLES, + typeThunkTLV), + ENTRY("__DATA", "__thread_data", S_THREAD_LOCAL_REGULAR, typeTLVInitialData), ENTRY("", "", S_INTERPOSING, typeInterposingTuples), ENTRY("__LD", "__compact_unwind", S_REGULAR, typeCompactUnwindInfo), diff --git a/lld/lib/ReaderWriter/MachO/MachOPasses.h b/lld/lib/ReaderWriter/MachO/MachOPasses.h index 86f4bc0f5d54..a73785418d5f 100644 --- a/lld/lib/ReaderWriter/MachO/MachOPasses.h +++ b/lld/lib/ReaderWriter/MachO/MachOPasses.h @@ -19,6 +19,7 @@ namespace mach_o { void addLayoutPass(PassManager &pm, const MachOLinkingContext &ctx); void addStubsPass(PassManager &pm, const MachOLinkingContext &ctx); void addGOTPass(PassManager &pm, const MachOLinkingContext &ctx); +void addTLVPass(PassManager &pm, const MachOLinkingContext &ctx); void addCompactUnwindPass(PassManager &pm, const MachOLinkingContext &ctx); void addShimPass(PassManager &pm, const MachOLinkingContext &ctx); diff --git a/lld/lib/ReaderWriter/MachO/TLVPass.cpp b/lld/lib/ReaderWriter/MachO/TLVPass.cpp new file mode 100644 index 000000000000..fa6ccae75999 --- /dev/null +++ b/lld/lib/ReaderWriter/MachO/TLVPass.cpp @@ -0,0 +1,140 @@ +//===- lib/ReaderWriter/MachO/TLVPass.cpp ---------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This linker pass transforms all TLV references to real references. +/// +//===----------------------------------------------------------------------===// + +#include "ArchHandler.h" +#include "File.h" +#include "MachOPasses.h" +#include "lld/Core/Simple.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/Debug.h" + +namespace lld { +namespace mach_o { + +// +// TLVP Entry Atom created by the TLV pass. +// +class TLVPEntryAtom : public SimpleDefinedAtom { +public: + TLVPEntryAtom(const File &file, bool is64, StringRef name) + : SimpleDefinedAtom(file), _is64(is64), _name(name) {} + + ContentType contentType() const override { + return DefinedAtom::typeTLVInitializerPtr; + } + + Alignment alignment() const override { + return _is64 ? 8 : 4; + } + + uint64_t size() const override { + return _is64 ? 8 : 4; + } + + ContentPermissions permissions() const override { + return DefinedAtom::permRW_; + } + + ArrayRef rawContent() const override { + static const uint8_t zeros[] = + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + return llvm::makeArrayRef(zeros, size()); + } + + StringRef slotName() const { + return _name; + } + +private: + const bool _is64; + StringRef _name; +}; + +class TLVPass : public Pass { +public: + TLVPass(const MachOLinkingContext &context) + : _ctx(context), _archHandler(_ctx.archHandler()), + _file("") {} + +private: + + std::error_code perform(SimpleFile &mergedFile) override { + + bool allowTLV = _ctx.minOS("10.7", "1.0"); + + for (const DefinedAtom *atom : mergedFile.defined()) { + for (const Reference *ref : *atom) { + if (!_archHandler.isTLVAccess(*ref)) + continue; + + if (!allowTLV) + return make_dynamic_error_code( + "targeted OS version does not support use of thread local " + "variables in " + atom->name() + " for architecture " + + _ctx.archName()); + + const Atom *target = ref->target(); + assert(target != nullptr); + + const DefinedAtom *tlvpEntry = makeTLVPEntry(target); + const_cast(ref)->setTarget(tlvpEntry); + _archHandler.updateReferenceToTLV(ref); + } + } + + std::vector entries; + entries.reserve(_targetToTLVP.size()); + for (auto &it : _targetToTLVP) + entries.push_back(it.second); + std::sort(entries.begin(), entries.end(), + [](const TLVPEntryAtom *lhs, const TLVPEntryAtom *rhs) { + return (lhs->slotName().compare(rhs->slotName()) < 0); + }); + + for (const TLVPEntryAtom *slot : entries) + mergedFile.addAtom(*slot); + + return std::error_code(); + } + + const DefinedAtom *makeTLVPEntry(const Atom *target) { + auto pos = _targetToTLVP.find(target); + + if (pos != _targetToTLVP.end()) + return pos->second; + + TLVPEntryAtom *tlvpEntry = new (_file.allocator()) + TLVPEntryAtom(_file, _ctx.is64Bit(), target->name()); + _targetToTLVP[target] = tlvpEntry; + const ArchHandler::ReferenceInfo &nlInfo = + _archHandler.stubInfo().nonLazyPointerReferenceToBinder; + tlvpEntry->addReference(Reference::KindNamespace::mach_o, nlInfo.arch, + nlInfo.kind, 0, target, 0); + return tlvpEntry; + } + + const MachOLinkingContext &_ctx; + mach_o::ArchHandler &_archHandler; + MachOFile _file; + llvm::DenseMap _targetToTLVP; +}; + +void addTLVPass(PassManager &pm, const MachOLinkingContext &ctx) { + assert(ctx.needsTLVPass()); + pm.add(llvm::make_unique(ctx)); +} + + +} // end namesapce mach_o +} // end namesapce lld diff --git a/lld/test/mach-o/parse-tlv-relocs-x86-64.yaml b/lld/test/mach-o/parse-tlv-relocs-x86-64.yaml new file mode 100644 index 000000000000..78b17841d4e7 --- /dev/null +++ b/lld/test/mach-o/parse-tlv-relocs-x86-64.yaml @@ -0,0 +1,100 @@ +# RUN: lld -flavor darwin -arch x86_64 -r -print_atoms %s -o %t | FileCheck %s \ +# RUN: && lld -flavor darwin -arch x86_64 -r -print_atoms %t -o %t2 | FileCheck %s +# +# Test parsing of x86_64 tlv relocations. + +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +compat-version: 0.0 +current-version: 0.0 +has-UUID: false +OS: unknown +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + alignment: 16 + address: 0x0000000000000000 + content: [ 0x55, 0x48, 0x89, 0xE5, 0x48, 0x8B, 0x3D, 0x00, + 0x00, 0x00, 0x00, 0xFF, 0x17, 0x8B, 0x00, 0x5D, + 0xC3 ] + relocations: + - offset: 0x00000007 + type: X86_64_RELOC_TLV + length: 2 + pc-rel: true + extern: true + symbol: 2 + - segment: __DATA + section: __thread_data + type: S_THREAD_LOCAL_REGULAR + attributes: [ ] + alignment: 4 + address: 0x0000000000000014 + content: [ 0x07, 0x00, 0x00, 0x00 ] + - segment: __DATA + section: __thread_vars + type: S_THREAD_LOCAL_VARIABLES + attributes: [ ] + address: 0x0000000000000018 + content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ] + relocations: + - offset: 0x00000010 + type: X86_64_RELOC_UNSIGNED + length: 3 + pc-rel: false + extern: true + symbol: 0 + - offset: 0x00000000 + type: X86_64_RELOC_UNSIGNED + length: 3 + pc-rel: false + extern: true + symbol: 3 +local-symbols: + - name: '_x$tlv$init' + type: N_SECT + sect: 2 + value: 0x0000000000000014 +global-symbols: + - name: _main + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000000 + - name: _x + type: N_SECT + scope: [ N_EXT ] + sect: 3 + value: 0x0000000000000018 +undefined-symbols: + - name: __tlv_bootstrap + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 +page-size: 0x00000000 +... + +# CHECK: - name: _x +# CHECK-NEXT: scope: global +# CHECK-NEXT: type: tlv-thunk +# CHECK-NOT: - name: +# CHECK: references: +# CHECK-NEXT: - kind: pointer64 +# CHECK-NEXT: offset: 0 +# CHECK-NEXT: target: __tlv_bootstrap +# CHECK-NEXT: - kind: tlvInitSectionOffset +# CHECK-NEXT: offset: 16 +# CHECK-NEXT: target: '_x$tlv$init' +# CHECK: - name: _main +# CHECK-NOT: - name: +# CHECK-NEXT: scope: global +# CHECK: references: +# CHECK-NEXT: - kind: ripRel32Tlv +# CHECK-NEXT: offset: 7 +# CHECK-NEXT: target: _x diff --git a/lld/test/mach-o/run-tlv-pass-x86-64.yaml b/lld/test/mach-o/run-tlv-pass-x86-64.yaml new file mode 100644 index 000000000000..74400a143653 --- /dev/null +++ b/lld/test/mach-o/run-tlv-pass-x86-64.yaml @@ -0,0 +1,133 @@ +# RUN: lld -flavor darwin -macosx_version_min 10.7 -arch x86_64 -print_atoms %s -o %t | FileCheck %s +# RUN: not lld -flavor darwin -macosx_version_min 10.6 -arch x86_64 -o %t %s 2> %t2 +# RUN: FileCheck < %t2 %s --check-prefix=CHECK-ERROR +# +# Test parsing of x86_64 tlv relocations. + +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +compat-version: 0.0 +current-version: 0.0 +has-UUID: false +OS: unknown +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + alignment: 16 + address: 0x0000000000000000 + content: [ 0x55, 0x48, 0x89, 0xE5, 0x48, 0x8B, 0x3D, 0x00, + 0x00, 0x00, 0x00, 0xFF, 0x17, 0x8B, 0x00, 0x5D, + 0xC3 ] + relocations: + - offset: 0x00000007 + type: X86_64_RELOC_TLV + length: 2 + pc-rel: true + extern: true + symbol: 2 + - segment: __DATA + section: __thread_data + type: S_THREAD_LOCAL_REGULAR + attributes: [ ] + alignment: 4 + address: 0x0000000000000014 + content: [ 0x07, 0x00, 0x00, 0x00 ] + - segment: __DATA + section: __thread_vars + type: S_THREAD_LOCAL_VARIABLES + attributes: [ ] + address: 0x0000000000000018 + content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ] + relocations: + - offset: 0x00000010 + type: X86_64_RELOC_UNSIGNED + length: 3 + pc-rel: false + extern: true + symbol: 0 + - offset: 0x00000000 + type: X86_64_RELOC_UNSIGNED + length: 3 + pc-rel: false + extern: true + symbol: 3 + - segment: __DATA + section: __dummy + type: S_REGULAR + attributes: [ ] + alignment: 8 + address: 0x00000000000000C0 + content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ] +local-symbols: + - name: '_x$tlv$init' + type: N_SECT + sect: 2 + value: 0x0000000000000014 +global-symbols: + - name: _main + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000000 + - name: _x + type: N_SECT + scope: [ N_EXT ] + sect: 3 + value: 0x0000000000000018 + - name: '__tlv_bootstrap' + type: N_SECT + scope: [ N_EXT ] + sect: 4 + value: 0x00000000000000C0 + - name: 'dyld_stub_binder' + type: N_SECT + scope: [ N_EXT ] + sect: 4 + value: 0x00000000000000C8 + - name: 'start' + type: N_SECT + scope: [ N_EXT ] + sect: 4 + value: 0x00000000000000D0 +page-size: 0x00000000 +... + +# CHECK: - name: _x +# CHECK-NEXT: scope: global +# CHECK-NEXT: type: tlv-thunk +# CHECK-NOT: - name: +# CHECK: references: +# CHECK-NEXT: - kind: pointer64 +# CHECK-NEXT: offset: 0 +# CHECK-NEXT: target: __tlv_bootstrap +# CHECK-NEXT: - kind: tlvInitSectionOffset +# CHECK-NEXT: offset: 16 +# CHECK-NEXT: target: '_x$tlv$init' +# CHECK-NEXT: - name: '_x$tlv$init' +# CHECK-NEXT: type: tlv-data +# CHECK: - name: _main +# CHECK-NOT: - name: +# CHECK: references: +# CHECK-NEXT: - kind: ripRel32 +# CHECK-NEXT: offset: 7 +# CHECK-NEXT: target: L[[ID:[0-9]+]] +# CHECK: - ref-name: L[[ID]] +# CHECK-NEXT: scope: hidden +# CHECK-NEXT: type: tlv-initializer-ptr +# CHECK-NEXT: content: [ 00, 00, 00, 00, 00, 00, 00, 00 ] +# CHECK-NEXT: alignment: 8 +# CHECK-NEXT: permissions: rw- +# CHECK-NEXT: references: +# CHECK-NEXT: - kind: pointer64 +# CHECK-NEXT: offset: 0 +# CHECK-NEXT: target: _x + +# CHECK-ERROR: targeted OS version does not support use of thread local variables in _main for architecture x86_64