| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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<uint8_t> 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("<mach-o TLV Pass>") {} | ||
|
|
||
| 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<Reference*>(ref)->setTarget(tlvpEntry); | ||
| _archHandler.updateReferenceToTLV(ref); | ||
| } | ||
| } | ||
|
|
||
| std::vector<const TLVPEntryAtom*> 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<const Atom*, const TLVPEntryAtom*> _targetToTLVP; | ||
| }; | ||
|
|
||
| void addTLVPass(PassManager &pm, const MachOLinkingContext &ctx) { | ||
| assert(ctx.needsTLVPass()); | ||
| pm.add(llvm::make_unique<TLVPass>(ctx)); | ||
| } | ||
|
|
||
|
|
||
| } // end namesapce mach_o | ||
| } // end namesapce lld |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |