diff --git a/lld/MachO/Arch/X86_64.cpp b/lld/MachO/Arch/X86_64.cpp --- a/lld/MachO/Arch/X86_64.cpp +++ b/lld/MachO/Arch/X86_64.cpp @@ -36,7 +36,8 @@ void prepareSymbolRelocation(lld::macho::Symbol &, const InputSection *, const Reloc &) override; - uint64_t getSymbolVA(const lld::macho::Symbol &, uint8_t type) const override; + uint64_t resolveSymbolVA(uint8_t *buf, const lld::macho::Symbol &, + uint8_t type) const override; }; } // namespace @@ -72,6 +73,11 @@ const relocation_info &rel) const { auto *buf = reinterpret_cast(mb.getBufferStart()); const uint8_t *loc = buf + sec.offset + rel.r_address; + + if (isThreadLocalVariables(sec.flags) && rel.r_type != X86_64_RELOC_UNSIGNED) + error("relocations in thread-local variable sections must be " + "X86_64_RELOC_UNSIGNED"); + switch (rel.r_type) { case X86_64_RELOC_BRANCH: // XXX: ld64 also supports r_length = 0 here but I'm not sure when such a @@ -84,6 +90,7 @@ case X86_64_RELOC_SIGNED_4: case X86_64_RELOC_GOT_LOAD: case X86_64_RELOC_GOT: + case X86_64_RELOC_TLV: if (!rel.r_pcrel) fatal(getErrorLocation(mb, sec, rel) + ": relocations of type " + std::to_string(rel.r_type) + " must be pcrel"); @@ -123,6 +130,7 @@ case X86_64_RELOC_SIGNED_4: case X86_64_RELOC_GOT_LOAD: case X86_64_RELOC_GOT: + case X86_64_RELOC_TLV: // These types are only used for pc-relative relocations, so offset by 4 // since the RIP has advanced by 4 at this point. This is only valid when // r_length = 2, which is enforced by validateLength(). @@ -239,8 +247,13 @@ case X86_64_RELOC_SIGNED_2: case X86_64_RELOC_SIGNED_4: break; - case X86_64_RELOC_SUBTRACTOR: case X86_64_RELOC_TLV: + if (auto *dysym = dyn_cast(&sym)) + error("relocations to thread-local dylib symbols not yet implemented"); + else + assert(isa(&sym)); + break; + case X86_64_RELOC_SUBTRACTOR: fatal("TODO: handle relocation type " + std::to_string(r.type)); break; default: @@ -248,8 +261,8 @@ } } -uint64_t X86_64::getSymbolVA(const lld::macho::Symbol &sym, - uint8_t type) const { +uint64_t X86_64::resolveSymbolVA(uint8_t *buf, const lld::macho::Symbol &sym, + uint8_t type) const { switch (type) { case X86_64_RELOC_GOT_LOAD: case X86_64_RELOC_GOT: @@ -264,8 +277,18 @@ case X86_64_RELOC_SIGNED_2: case X86_64_RELOC_SIGNED_4: return sym.getVA(); + case X86_64_RELOC_TLV: { + if (auto *dysym = dyn_cast(&sym)) + error("relocations to thread-local dylib symbols not yet implemented"); + + // Convert the movq to a leaq. + assert(isa(&sym)); + if (buf[-2] != 0x8b) + error("X86_64_RELOC_TLV must be used with movq instructions"); + buf[-2] = 0x8d; + return sym.getVA(); + } case X86_64_RELOC_SUBTRACTOR: - case X86_64_RELOC_TLV: fatal("TODO: handle relocation type " + std::to_string(type)); default: llvm_unreachable("Unexpected relocation type"); diff --git a/lld/MachO/InputSection.h b/lld/MachO/InputSection.h --- a/lld/MachO/InputSection.h +++ b/lld/MachO/InputSection.h @@ -38,6 +38,10 @@ inline bool isZeroFill(uint8_t flags) { return llvm::MachO::isVirtualSection(flags & llvm::MachO::SECTION_TYPE); } + +inline bool isThreadLocalVariables(uint8_t flags) { + return (flags & llvm::MachO::SECTION_TYPE) == + llvm::MachO::S_THREAD_LOCAL_VARIABLES; } class InputSection { diff --git a/lld/MachO/InputSection.cpp b/lld/MachO/InputSection.cpp --- a/lld/MachO/InputSection.cpp +++ b/lld/MachO/InputSection.cpp @@ -35,10 +35,19 @@ for (Reloc &r : relocs) { uint64_t va = 0; - if (auto *s = r.target.dyn_cast()) - va = target->getSymbolVA(*s, r.type); - else if (auto *isec = r.target.dyn_cast()) + if (auto *s = r.target.dyn_cast()) { + va = target->resolveSymbolVA(buf + r.offset, *s, r.type); + + if (isThreadLocalVariables(flags)) { + // References from thread-local variable sections are treated as + // offsets relative to the start of the target section, instead of as + // absolute addresses. + if (auto *defined = dyn_cast(s)) + va -= defined->isec->parent->addr; + } + } else if (auto *isec = r.target.dyn_cast()) { va = isec->getVA(); + } uint64_t val = va + r.addend; if (r.pcrel) diff --git a/lld/MachO/SyntheticSections.cpp b/lld/MachO/SyntheticSections.cpp --- a/lld/MachO/SyntheticSections.cpp +++ b/lld/MachO/SyntheticSections.cpp @@ -59,9 +59,19 @@ hdr->ncmds = loadCommands.size(); hdr->sizeofcmds = sizeOfCmds; hdr->flags = MachO::MH_NOUNDEFS | MachO::MH_DYLDLINK | MachO::MH_TWOLEVEL; + if (config->outputType == MachO::MH_DYLIB && !config->hasReexports) hdr->flags |= MachO::MH_NO_REEXPORTED_DYLIBS; + for (OutputSegment *seg : outputSegments) { + for (OutputSection *osec : seg->getSections()) { + if (isThreadLocalVariables(osec->flags)) { + hdr->flags |= MachO::MH_HAS_TLV_DESCRIPTORS; + break; + } + } + } + uint8_t *p = reinterpret_cast(hdr + 1); for (LoadCommand *lc : loadCommands) { lc->writeTo(p); diff --git a/lld/MachO/Target.h b/lld/MachO/Target.h --- a/lld/MachO/Target.h +++ b/lld/MachO/Target.h @@ -51,11 +51,13 @@ // Symbols may be referenced via either the GOT or the stubs section, // depending on the relocation type. prepareSymbolRelocation() will set up the - // GOT/stubs entries, and getSymbolVA() will return the addresses of those - // entries. + // GOT/stubs entries, and resolveSymbolVA() will return the addresses of those + // entries. resolveSymbolVA() may also relax the target instructions to save + // on a level of address indirection. virtual void prepareSymbolRelocation(Symbol &, const InputSection *, const Reloc &) = 0; - virtual uint64_t getSymbolVA(const Symbol &, uint8_t type) const = 0; + virtual uint64_t resolveSymbolVA(uint8_t *buf, const Symbol &, + uint8_t type) const = 0; uint32_t cpuType; uint32_t cpuSubtype; diff --git a/lld/test/MachO/Inputs/MacOSX.sdk/usr/lib/libSystem.tbd b/lld/test/MachO/Inputs/MacOSX.sdk/usr/lib/libSystem.tbd --- a/lld/test/MachO/Inputs/MacOSX.sdk/usr/lib/libSystem.tbd +++ b/lld/test/MachO/Inputs/MacOSX.sdk/usr/lib/libSystem.tbd @@ -18,7 +18,7 @@ parent-umbrella: System exports: - archs: [ 'x86_64' ] - symbols: [ dyld_stub_binder ] + symbols: [ dyld_stub_binder, __tlv_bootstrap ] --- !tapi-tbd-v3 archs: [ x86_64 ] uuids: [ 'x86_64: 00000000-0000-0000-0000-000000000002' ] diff --git a/lld/test/MachO/invalid/bad-tlv-def.s b/lld/test/MachO/invalid/bad-tlv-def.s new file mode 100644 --- /dev/null +++ b/lld/test/MachO/invalid/bad-tlv-def.s @@ -0,0 +1,15 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %s -o %t.o +# RUN: not lld -flavor darwinnew -o /dev/null %t.o 2>&1 | FileCheck %s + +# CHECK: error: relocations in thread-local variable sections must be X86_64_RELOC_UNSIGNED + +.text +.globl _main +_main: + ret + +.section __DATA,__thread_vars,thread_local_variables +.globl _foo, _bar +_foo: + movq _bar@GOTPCREL(%rip), %rax diff --git a/lld/test/MachO/invalid/bad-tlv-opcode.s b/lld/test/MachO/invalid/bad-tlv-opcode.s new file mode 100644 --- /dev/null +++ b/lld/test/MachO/invalid/bad-tlv-opcode.s @@ -0,0 +1,14 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %s -o %t.o +# RUN: not lld -flavor darwinnew -o /dev/null %t.o 2>&1 | FileCheck %s + +# CHECK: error: X86_64_RELOC_TLV must be used with movq instructions + +.text +.globl _main +_main: + leaq _foo@TLVP(%rip), %rax + ret + +.section __DATA,__thread_vars,thread_local_variables +_foo: diff --git a/lld/test/MachO/tlv.s b/lld/test/MachO/tlv.s new file mode 100644 --- /dev/null +++ b/lld/test/MachO/tlv.s @@ -0,0 +1,57 @@ +# REQUIRES: x86 +# RUN: mkdir -p %t +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %s -o %t/test.o +# RUN: lld -flavor darwinnew -L%S/Inputs/MacOSX.sdk/usr/lib -lSystem -o %t/test %t/test.o +# RUN: llvm-readobj --file-headers %t/test | FileCheck %s --check-prefix=HEADER +# RUN: llvm-objdump -D %t/test | FileCheck %s + +# HEADER: MH_HAS_TLV_DESCRIPTORS + +# CHECK: Disassembly of section __TEXT,__text: +# CHECK-EMPTY: +# CHECK-NEXT: <_main>: +# CHECK-NEXT: leaq {{.*}}(%rip), %rax # {{.*}} <_foo> +# CHECK-NEXT: leaq {{.*}}(%rip), %rax # {{.*}} <_bar> +# CHECK-NEXT: retq +# CHECK-EMPTY: +# CHECK-NEXT: Disassembly of section __DATA,__thread_data: +# CHECK-EMPTY: +# CHECK-NEXT: <__thread_data>: +# CHECK-NEXT: ef +# CHECK-NEXT: be ad de be ba +# CHECK-NEXT: fe ca +# CHECK-EMPTY: +# CHECK-NEXT: Disassembly of section __DATA,__thread_vars: +# CHECK-EMPTY: +# CHECK-NEXT: <_foo>: +# CHECK-NEXT: ... +# CHECK-EMPTY: +# CHECK-NEXT: <_bar>: +# CHECK-NEXT: ... +# CHECK-NEXT: 04 00 +# CHECK-NEXT: 00 00 +# CHECK-NEXT: 00 00 +# CHECK-NEXT: 00 00 + +.globl _main +_main: + mov _foo@TLVP(%rip), %rax + mov _bar@TLVP(%rip), %rax + ret + +.section __DATA,__thread_data,thread_local_regular +_foo$tlv$init: + .long 0xdeadbeef +_bar$tlv$init: + .long 0xcafebabe + +.section __DATA,__thread_vars,thread_local_variables +.globl _foo, _bar +_foo: + .quad __tlv_bootstrap + .quad 0 + .quad _foo$tlv$init +_bar: + .quad __tlv_bootstrap + .quad 0 + .quad _bar$tlv$init