From b3e2fc931d2e06560cc8c1640e006aa25ff1ef5a Mon Sep 17 00:00:00 2001 From: Jez Ng Date: Tue, 5 May 2020 17:38:10 -0700 Subject: [PATCH] [lld-macho] Support calls to functions in dylibs Summary: This diff implements lazy symbol binding -- very similar to the PLT mechanism in ELF. ELF's .plt section is broken up into two sections in Mach-O: StubsSection and StubHelperSection. Calls to functions in dylibs will end up calling into StubsSection, which contains indirect jumps to addresses stored in the LazyPointerSection (the counterpart to ELF's .plt.got). Initially, the LazyPointerSection contains addresses that point into one of the entry points in the middle of the StubHelperSection. The code in StubHelperSection will push on the stack an offset into the LazyBindingSection. The push is followed by a jump to the beginning of the StubHelperSection (similar to PLT0), which then calls into dyld_stub_binder. dyld_stub_binder is a non-lazily bound symbol, so this call looks it up in the GOT. The stub binder will look up the bind opcodes in the LazyBindingSection at the given offset. The bind opcodes will tell the binder to update the address in the LazyPointerSection to point to the symbol, so that subsequent calls don't have to redo the symbol resolution. The binder will then jump to the resolved symbol. Depends on D78269. Reviewers: ruiu, pcc, MaskRay, smeenai, alexshap, gkm, Ktwu, christylee Subscribers: llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D78270 --- lld/MachO/Arch/X86_64.cpp | 110 +++++++++++++++++++++++-- lld/MachO/Driver.cpp | 8 ++ lld/MachO/InputFiles.cpp | 10 +++ lld/MachO/InputFiles.h | 5 ++ lld/MachO/InputSection.cpp | 3 +- lld/MachO/OutputSegment.h | 3 +- lld/MachO/Symbols.h | 2 + lld/MachO/SyntheticSections.cpp | 128 +++++++++++++++++++++++++++++ lld/MachO/SyntheticSections.h | 96 +++++++++++++++++++++- lld/MachO/Target.h | 22 +++++ lld/MachO/Writer.cpp | 34 +++++--- lld/test/MachO/Inputs/libgoodbye.s | 11 ++- lld/test/MachO/Inputs/libhello.s | 11 ++- lld/test/MachO/dylink-lazy.s | 59 +++++++++++++ 14 files changed, 481 insertions(+), 21 deletions(-) create mode 100644 lld/test/MachO/dylink-lazy.s diff --git a/lld/MachO/Arch/X86_64.cpp b/lld/MachO/Arch/X86_64.cpp index 6c13ac53c747..08b23a57c3d3 100644 --- a/lld/MachO/Arch/X86_64.cpp +++ b/lld/MachO/Arch/X86_64.cpp @@ -6,7 +6,10 @@ // //===----------------------------------------------------------------------===// +#include "Symbols.h" +#include "SyntheticSections.h" #include "Target.h" + #include "lld/Common/ErrorHandler.h" #include "llvm/BinaryFormat/MachO.h" #include "llvm/Support/Endian.h" @@ -20,14 +23,20 @@ namespace { struct X86_64 : TargetInfo { X86_64(); + uint64_t getImplicitAddend(const uint8_t *loc, uint8_t type) const override; void relocateOne(uint8_t *loc, uint8_t type, uint64_t val) const override; + + void writeStub(uint8_t *buf, const DylibSymbol &) const override; + void writeStubHelperHeader(uint8_t *buf) const override; + void writeStubHelperEntry(uint8_t *buf, const DylibSymbol &, + uint64_t entryAddr) const override; + + void prepareDylibSymbolRelocation(DylibSymbol &, uint8_t type) override; + uint64_t getDylibSymbolVA(const DylibSymbol &, uint8_t type) const override; }; -X86_64::X86_64() { - cpuType = CPU_TYPE_X86_64; - cpuSubtype = CPU_SUBTYPE_X86_64_ALL; -} +} // namespace uint64_t X86_64::getImplicitAddend(const uint8_t *loc, uint8_t type) const { switch (type) { @@ -62,7 +71,98 @@ void X86_64::relocateOne(uint8_t *loc, uint8_t type, uint64_t val) const { } } -} // namespace +// The following methods emit a number of assembly sequences with RIP-relative +// addressing. Note that RIP-relative addressing on X86-64 has the RIP pointing +// to the next instruction, not the current instruction, so we always have to +// account for the current instruction's size when calculating offsets. +// writeRipRelative helps with that. +// +// bufAddr: The virtual address corresponding to buf[0]. +// bufOff: The offset within buf of the next instruction. +// destAddr: The destination address that the current instruction references. +static void writeRipRelative(uint8_t *buf, uint64_t bufAddr, uint64_t bufOff, + uint64_t destAddr) { + uint64_t rip = bufAddr + bufOff; + // For the instructions we care about, the RIP-relative address is always + // stored in the last 4 bytes of the instruction. + write32le(buf + bufOff - 4, destAddr - rip); +} + +static constexpr uint8_t stub[] = { + 0xff, 0x25, 0, 0, 0, 0, // jmpq *__la_symbol_ptr(%rip) +}; + +void X86_64::writeStub(uint8_t *buf, const DylibSymbol &sym) const { + memcpy(buf, stub, 2); // just copy the two nonzero bytes + uint64_t stubAddr = in.stubs->addr + sym.stubsIndex * sizeof(stub); + writeRipRelative(buf, stubAddr, sizeof(stub), + in.lazyPointers->addr + sym.stubsIndex * WordSize); +} + +static constexpr uint8_t stubHelperHeader[] = { + 0x4c, 0x8d, 0x1d, 0, 0, 0, 0, // 0x0: leaq ImageLoaderCache(%rip), %r11 + 0x41, 0x53, // 0x7: pushq %r11 + 0xff, 0x25, 0, 0, 0, 0, // 0x9: jmpq *dyld_stub_binder@GOT(%rip) + 0x90, // 0xf: nop +}; + +static constexpr uint8_t stubHelperEntry[] = { + 0x68, 0, 0, 0, 0, // 0x0: pushq + 0xe9, 0, 0, 0, 0, // 0x5: jmp <__stub_helper> +}; + +void X86_64::writeStubHelperHeader(uint8_t *buf) const { + memcpy(buf, stubHelperHeader, sizeof(stubHelperHeader)); + writeRipRelative(buf, in.stubHelper->addr, 7, in.imageLoaderCache->getVA()); + writeRipRelative(buf, in.stubHelper->addr, 0xf, + in.got->addr + + in.stubHelper->stubBinder->gotIndex * WordSize); +} + +void X86_64::writeStubHelperEntry(uint8_t *buf, const DylibSymbol &sym, + uint64_t entryAddr) const { + memcpy(buf, stubHelperEntry, sizeof(stubHelperEntry)); + write32le(buf + 1, sym.lazyBindOffset); + writeRipRelative(buf, entryAddr, sizeof(stubHelperEntry), + in.stubHelper->addr); +} + +void X86_64::prepareDylibSymbolRelocation(DylibSymbol &sym, uint8_t type) { + switch (type) { + case X86_64_RELOC_GOT_LOAD: + in.got->addEntry(sym); + break; + case X86_64_RELOC_BRANCH: + in.stubs->addEntry(sym); + break; + case X86_64_RELOC_GOT: + fatal("TODO: Unhandled dylib symbol relocation X86_64_RELOC_GOT"); + default: + llvm_unreachable("Unexpected dylib relocation type"); + } +} + +uint64_t X86_64::getDylibSymbolVA(const DylibSymbol &sym, uint8_t type) const { + switch (type) { + case X86_64_RELOC_GOT_LOAD: + return in.got->addr + sym.gotIndex * WordSize; + case X86_64_RELOC_BRANCH: + return in.stubs->addr + sym.stubsIndex * sizeof(stub); + case X86_64_RELOC_GOT: + fatal("TODO: Unhandled dylib symbol relocation X86_64_RELOC_GOT"); + default: + llvm_unreachable("Unexpected dylib relocation type"); + } +} + +X86_64::X86_64() { + cpuType = CPU_TYPE_X86_64; + cpuSubtype = CPU_SUBTYPE_X86_64_ALL; + + stubSize = sizeof(stub); + stubHelperHeaderSize = sizeof(stubHelperHeader); + stubHelperEntrySize = sizeof(stubHelperEntry); +} TargetInfo *macho::createX86_64TargetInfo() { static X86_64 t; diff --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp index aabdc2ed5320..3accfee662f2 100644 --- a/lld/MachO/Driver.cpp +++ b/lld/MachO/Driver.cpp @@ -158,6 +158,14 @@ bool macho::link(llvm::ArrayRef argsArr, bool canExitEarly, } } + // dyld requires us to load libSystem. Since we may run tests on non-OSX + // systems which do not have libSystem, we mock it out here. + // TODO: Replace this with a stub tbd file once we have TAPI support. + if (StringRef(getenv("LLD_IN_TEST")) == "1" && + config->outputType == MH_EXECUTE) { + inputFiles.push_back(DylibFile::createLibSystemMock()); + } + if (config->outputType == MH_EXECUTE && !isa(config->entry)) { error("undefined symbol: " + config->entry->getName()); return false; diff --git a/lld/MachO/InputFiles.cpp b/lld/MachO/InputFiles.cpp index 02e7d1c55221..eb8a30f753f4 100644 --- a/lld/MachO/InputFiles.cpp +++ b/lld/MachO/InputFiles.cpp @@ -263,6 +263,16 @@ DylibFile::DylibFile(MemoryBufferRef mb) : InputFile(DylibKind, mb) { } } +DylibFile::DylibFile() : InputFile(DylibKind, MemoryBufferRef()) {} + +DylibFile *DylibFile::createLibSystemMock() { + auto *file = make(); + file->mb = MemoryBufferRef("", "/usr/lib/libSystem.B.dylib"); + file->dylibName = "/usr/lib/libSystem.B.dylib"; + file->symbols.push_back(symtab->addDylib("dyld_stub_binder", file)); + return file; +} + // Returns "" or "baz.o". std::string lld::toString(const InputFile *file) { return file ? std::string(file->getName()) : ""; diff --git a/lld/MachO/InputFiles.h b/lld/MachO/InputFiles.h index 72a0ae4592a0..43bfd66e8f73 100644 --- a/lld/MachO/InputFiles.h +++ b/lld/MachO/InputFiles.h @@ -63,6 +63,11 @@ public: explicit DylibFile(MemoryBufferRef mb); static bool classof(const InputFile *f) { return f->kind() == DylibKind; } + // Do not use this constructor!! This is meant only for createLibSystemMock(), + // but it cannot be made private as we call it via make(). + DylibFile(); + static DylibFile *createLibSystemMock(); + StringRef dylibName; uint64_t ordinal = 0; // Ordinal numbering starts from 1, so 0 is a sentinel }; diff --git a/lld/MachO/InputSection.cpp b/lld/MachO/InputSection.cpp index 55e17b606549..cfc6050cb90c 100644 --- a/lld/MachO/InputSection.cpp +++ b/lld/MachO/InputSection.cpp @@ -9,7 +9,6 @@ #include "InputSection.h" #include "OutputSegment.h" #include "Symbols.h" -#include "SyntheticSections.h" #include "Target.h" #include "lld/Common/Memory.h" #include "llvm/Support/Endian.h" @@ -35,7 +34,7 @@ void InputSection::writeTo(uint8_t *buf) { uint64_t va = 0; if (auto *s = r.target.dyn_cast()) { if (auto *dylibSymbol = dyn_cast(s)) { - va = in.got->addr + dylibSymbol->gotIndex * WordSize; + va = target->getDylibSymbolVA(*dylibSymbol, r.type); } else { va = s->getVA(); } diff --git a/lld/MachO/OutputSegment.h b/lld/MachO/OutputSegment.h index 332f38d2c259..af658ab36a7c 100644 --- a/lld/MachO/OutputSegment.h +++ b/lld/MachO/OutputSegment.h @@ -18,8 +18,9 @@ namespace macho { namespace segment_names { -constexpr const char *text = "__TEXT"; constexpr const char *pageZero = "__PAGEZERO"; +constexpr const char *text = "__TEXT"; +constexpr const char *data = "__DATA"; constexpr const char *linkEdit = "__LINKEDIT"; constexpr const char *dataConst = "__DATA_CONST"; diff --git a/lld/MachO/Symbols.h b/lld/MachO/Symbols.h index 1f5817117e80..7b35b4530a95 100644 --- a/lld/MachO/Symbols.h +++ b/lld/MachO/Symbols.h @@ -77,6 +77,8 @@ public: DylibFile *file; uint32_t gotIndex = UINT32_MAX; + uint32_t stubsIndex = UINT32_MAX; + uint32_t lazyBindOffset = UINT32_MAX; }; inline uint64_t Symbol::getVA() const { diff --git a/lld/MachO/SyntheticSections.cpp b/lld/MachO/SyntheticSections.cpp index c23f6abdb564..d3a601852e60 100644 --- a/lld/MachO/SyntheticSections.cpp +++ b/lld/MachO/SyntheticSections.cpp @@ -22,6 +22,7 @@ using namespace llvm; using namespace llvm::MachO; using namespace llvm::support; +using namespace llvm::support::endian; namespace lld { namespace macho { @@ -56,6 +57,8 @@ void MachHeaderSection::writeTo(uint8_t *buf) const { hdr->ncmds = loadCommands.size(); hdr->sizeofcmds = sizeOfCmds; hdr->flags = MH_NOUNDEFS | MH_DYLDLINK | MH_TWOLEVEL; + if (config->outputType == MH_DYLIB) + hdr->flags |= MH_NO_REEXPORTED_DYLIBS; uint8_t *p = reinterpret_cast(hdr + 1); for (LoadCommand *lc : loadCommands) { @@ -131,6 +134,131 @@ void BindingSection::writeTo(uint8_t *buf) const { memcpy(buf, contents.data(), contents.size()); } +StubsSection::StubsSection() + : SyntheticSection(segment_names::text, "__stubs") {} + +size_t StubsSection::getSize() const { + return entries.size() * target->stubSize; +} + +void StubsSection::writeTo(uint8_t *buf) const { + size_t off = 0; + for (const DylibSymbol *sym : in.stubs->getEntries()) { + target->writeStub(buf + off, *sym); + off += target->stubSize; + } +} + +void StubsSection::addEntry(DylibSymbol &sym) { + if (entries.insert(&sym)) + sym.stubsIndex = entries.size() - 1; +} + +StubHelperSection::StubHelperSection() + : SyntheticSection(segment_names::text, "__stub_helper") {} + +size_t StubHelperSection::getSize() const { + return target->stubHelperHeaderSize + + in.stubs->getEntries().size() * target->stubHelperEntrySize; +} + +bool StubHelperSection::isNeeded() const { + return !in.stubs->getEntries().empty(); +} + +void StubHelperSection::writeTo(uint8_t *buf) const { + target->writeStubHelperHeader(buf); + size_t off = target->stubHelperHeaderSize; + for (const DylibSymbol *sym : in.stubs->getEntries()) { + target->writeStubHelperEntry(buf + off, *sym, addr + off); + off += target->stubHelperEntrySize; + } +} + +void StubHelperSection::setup() { + stubBinder = dyn_cast_or_null(symtab->find("dyld_stub_binder")); + if (stubBinder == nullptr) { + error("symbol dyld_stub_binder not found (normally in libSystem.dylib). " + "Needed to perform lazy binding."); + return; + } + in.got->addEntry(*stubBinder); + + inputSections.push_back(in.imageLoaderCache); + symtab->addDefined("__dyld_private", in.imageLoaderCache, 0); +} + +ImageLoaderCacheSection::ImageLoaderCacheSection() { + segname = segment_names::data; + name = "__data"; +} + +LazyPointerSection::LazyPointerSection() + : SyntheticSection(segment_names::data, "__la_symbol_ptr") { + align = 8; + flags = S_LAZY_SYMBOL_POINTERS; +} + +size_t LazyPointerSection::getSize() const { + return in.stubs->getEntries().size() * WordSize; +} + +bool LazyPointerSection::isNeeded() const { + return !in.stubs->getEntries().empty(); +} + +void LazyPointerSection::writeTo(uint8_t *buf) const { + size_t off = 0; + for (const DylibSymbol *sym : in.stubs->getEntries()) { + uint64_t stubHelperOffset = target->stubHelperHeaderSize + + sym->stubsIndex * target->stubHelperEntrySize; + write64le(buf + off, in.stubHelper->addr + stubHelperOffset); + off += WordSize; + } +} + +LazyBindingSection::LazyBindingSection() + : SyntheticSection(segment_names::linkEdit, section_names::lazyBinding) {} + +bool LazyBindingSection::isNeeded() const { return in.stubs->isNeeded(); } + +void LazyBindingSection::finalizeContents() { + // TODO: Just precompute output size here instead of writing to a temporary + // buffer + for (DylibSymbol *sym : in.stubs->getEntries()) + sym->lazyBindOffset = encode(*sym); +} + +void LazyBindingSection::writeTo(uint8_t *buf) const { + memcpy(buf, contents.data(), contents.size()); +} + +// Unlike the non-lazy binding section, the bind opcodes in this section aren't +// interpreted all at once. Rather, dyld will start interpreting opcodes at a +// given offset, typically only binding a single symbol before it finds a +// BIND_OPCODE_DONE terminator. As such, unlike in the non-lazy-binding case, +// we cannot encode just the differences between symbols; we have to emit the +// complete bind information for each symbol. +uint32_t LazyBindingSection::encode(const DylibSymbol &sym) { + uint32_t opstreamOffset = contents.size(); + OutputSegment *dataSeg = in.lazyPointers->parent; + os << static_cast(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | + dataSeg->index); + uint64_t offset = in.lazyPointers->addr - dataSeg->firstSection()->addr + + sym.stubsIndex * WordSize; + encodeULEB128(offset, os); + if (sym.file->ordinal <= BIND_IMMEDIATE_MASK) + os << static_cast(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | + sym.file->ordinal); + else + fatal("TODO: Support larger dylib symbol ordinals"); + + os << static_cast(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM) + << sym.getName() << '\0' << static_cast(BIND_OPCODE_DO_BIND) + << static_cast(BIND_OPCODE_DONE); + return opstreamOffset; +} + ExportSection::ExportSection() : SyntheticSection(segment_names::linkEdit, section_names::export_) {} diff --git a/lld/MachO/SyntheticSections.h b/lld/MachO/SyntheticSections.h index c8dadf9bb0de..15e6d5770883 100644 --- a/lld/MachO/SyntheticSections.h +++ b/lld/MachO/SyntheticSections.h @@ -10,9 +10,12 @@ #define LLD_MACHO_SYNTHETIC_SECTIONS_H #include "ExportTrie.h" +#include "InputSection.h" #include "OutputSection.h" #include "Target.h" + #include "llvm/ADT/SetVector.h" +#include "llvm/Support/raw_ostream.h" namespace lld { namespace macho { @@ -22,6 +25,7 @@ namespace section_names { constexpr const char *pageZero = "__pagezero"; constexpr const char *header = "__mach_header"; constexpr const char *binding = "__binding"; +constexpr const char *lazyBinding = "__lazy_binding"; constexpr const char *export_ = "__export"; constexpr const char *symbolTable = "__symbol_table"; constexpr const char *stringTable = "__string_table"; @@ -69,7 +73,6 @@ class GotSection : public SyntheticSection { public: GotSection(); - void addEntry(DylibSymbol &sym); const llvm::SetVector &getEntries() const { return entries; } @@ -83,6 +86,8 @@ public: // runtime by dyld. } + void addEntry(DylibSymbol &sym); + private: llvm::SetVector entries; }; @@ -103,6 +108,91 @@ public: SmallVector contents; }; +// The following sections implement lazy symbol binding -- very similar to the +// PLT mechanism in ELF. +// +// ELF's .plt section is broken up into two sections in Mach-O: StubsSection and +// StubHelperSection. Calls to functions in dylibs will end up calling into +// StubsSection, which contains indirect jumps to addresses stored in the +// LazyPointerSection (the counterpart to ELF's .plt.got). +// +// Initially, the LazyPointerSection contains addresses that point into one of +// the entry points in the middle of the StubHelperSection. The code in +// StubHelperSection will push on the stack an offset into the +// LazyBindingSection. The push is followed by a jump to the beginning of the +// StubHelperSection (similar to PLT0), which then calls into dyld_stub_binder. +// dyld_stub_binder is a non-lazily-bound symbol, so this call looks it up in +// the GOT. +// +// The stub binder will look up the bind opcodes in the LazyBindingSection at +// the given offset. The bind opcodes will tell the binder to update the address +// in the LazyPointerSection to point to the symbol, so that subsequent calls +// don't have to redo the symbol resolution. The binder will then jump to the +// resolved symbol. + +class StubsSection : public SyntheticSection { +public: + StubsSection(); + size_t getSize() const override; + bool isNeeded() const override { return !entries.empty(); } + void writeTo(uint8_t *buf) const override; + + const llvm::SetVector &getEntries() const { return entries; } + + void addEntry(DylibSymbol &sym); + +private: + llvm::SetVector entries; +}; + +class StubHelperSection : public SyntheticSection { +public: + StubHelperSection(); + size_t getSize() const override; + bool isNeeded() const override; + void writeTo(uint8_t *buf) const override; + + void setup(); + + DylibSymbol *stubBinder = nullptr; +}; + +// This section contains space for just a single word, and will be used by dyld +// to cache an address to the image loader it uses. Note that unlike the other +// synthetic sections, which are OutputSections, the ImageLoaderCacheSection is +// an InputSection that gets merged into the __data OutputSection. +class ImageLoaderCacheSection : public InputSection { +public: + ImageLoaderCacheSection(); + size_t getSize() const override { return WordSize; } +}; + +class LazyPointerSection : public SyntheticSection { +public: + LazyPointerSection(); + size_t getSize() const override; + bool isNeeded() const override; + void writeTo(uint8_t *buf) const override; +}; + +class LazyBindingSection : public SyntheticSection { +public: + LazyBindingSection(); + void finalizeContents(); + size_t getSize() const override { return contents.size(); } + uint32_t encode(const DylibSymbol &); + // Like other sections in __LINKEDIT, the lazy binding section is special: its + // offsets are recorded in the LC_DYLD_INFO_ONLY load command, instead of in + // section headers. + bool isHidden() const override { return true; } + bool isNeeded() const override; + void writeTo(uint8_t *buf) const override; + +private: + SmallVector contents; + llvm::raw_svector_ostream os{contents}; +}; + // Stores a trie that describes the set of exported symbols. class ExportSection : public SyntheticSection { public: @@ -165,6 +255,10 @@ private: struct InStruct { GotSection *got = nullptr; + LazyPointerSection *lazyPointers = nullptr; + StubsSection *stubs = nullptr; + StubHelperSection *stubHelper = nullptr; + ImageLoaderCacheSection *imageLoaderCache = nullptr; }; extern InStruct in; diff --git a/lld/MachO/Target.h b/lld/MachO/Target.h index 342a204b7b10..9781b70cb46c 100644 --- a/lld/MachO/Target.h +++ b/lld/MachO/Target.h @@ -9,11 +9,14 @@ #ifndef LLD_MACHO_TARGET_H #define LLD_MACHO_TARGET_H +#include #include namespace lld { namespace macho { +class DylibSymbol; + enum { // We are currently only supporting 64-bit targets since macOS and iOS are // deprecating 32-bit apps. @@ -30,8 +33,27 @@ public: uint8_t type) const = 0; virtual void relocateOne(uint8_t *loc, uint8_t type, uint64_t val) const = 0; + // Write code for lazy binding. See the comments on StubsSection for more + // details. + virtual void writeStub(uint8_t *buf, const DylibSymbol &) const = 0; + virtual void writeStubHelperHeader(uint8_t *buf) const = 0; + virtual void writeStubHelperEntry(uint8_t *buf, const DylibSymbol &, + uint64_t entryAddr) const = 0; + + // Dylib symbols are referenced via either the GOT or the stubs section, + // depending on the relocation type. prepareDylibSymbolRelocation() will set + // up the GOT/stubs entries, and getDylibSymbolVA() will return the addresses + // of those entries. + virtual void prepareDylibSymbolRelocation(DylibSymbol &, uint8_t type) = 0; + virtual uint64_t getDylibSymbolVA(const DylibSymbol &, + uint8_t type) const = 0; + uint32_t cpuType; uint32_t cpuSubtype; + + size_t stubSize; + size_t stubHelperHeaderSize; + size_t stubHelperEntrySize; }; TargetInfo *createX86_64TargetInfo(); diff --git a/lld/MachO/Writer.cpp b/lld/MachO/Writer.cpp index 19a69c3bb734..999e63ae6773 100644 --- a/lld/MachO/Writer.cpp +++ b/lld/MachO/Writer.cpp @@ -52,6 +52,7 @@ public: uint64_t fileOff = 0; MachHeaderSection *headerSection = nullptr; BindingSection *bindingSection = nullptr; + LazyBindingSection *lazyBindingSection = nullptr; ExportSection *exportSection = nullptr; StringTableSection *stringTableSection = nullptr; SymtabSection *symtabSection = nullptr; @@ -60,8 +61,11 @@ public: // LC_DYLD_INFO_ONLY stores the offsets of symbol import/export information. class LCDyldInfo : public LoadCommand { public: - LCDyldInfo(BindingSection *bindingSection, ExportSection *exportSection) - : bindingSection(bindingSection), exportSection(exportSection) {} + LCDyldInfo(BindingSection *bindingSection, + LazyBindingSection *lazyBindingSection, + ExportSection *exportSection) + : bindingSection(bindingSection), lazyBindingSection(lazyBindingSection), + exportSection(exportSection) {} uint32_t getSize() const override { return sizeof(dyld_info_command); } @@ -73,6 +77,10 @@ public: c->bind_off = bindingSection->fileOff; c->bind_size = bindingSection->getFileSize(); } + if (lazyBindingSection->isNeeded()) { + c->lazy_bind_off = lazyBindingSection->fileOff; + c->lazy_bind_size = lazyBindingSection->getFileSize(); + } if (exportSection->isNeeded()) { c->export_off = exportSection->fileOff; c->export_size = exportSection->getFileSize(); @@ -80,6 +88,7 @@ public: } BindingSection *bindingSection; + LazyBindingSection *lazyBindingSection; ExportSection *exportSection; }; @@ -260,12 +269,12 @@ void Writer::scanRelocations() { for (Reloc &r : sect->relocs) if (auto *s = r.target.dyn_cast()) if (auto *dylibSymbol = dyn_cast(s)) - in.got->addEntry(*dylibSymbol); + target->prepareDylibSymbolRelocation(*dylibSymbol, r.type); } void Writer::createLoadCommands() { headerSection->addLoadCommand( - make(bindingSection, exportSection)); + make(bindingSection, lazyBindingSection, exportSection)); headerSection->addLoadCommand( make(symtabSection, stringTableSection)); headerSection->addLoadCommand(make()); @@ -295,17 +304,13 @@ void Writer::createLoadCommands() { dylibFile->ordinal = dylibOrdinal++; } } - - // TODO: dyld requires libSystem to be loaded. libSystem is a universal - // binary and we don't have support for that yet, so mock it out here. - headerSection->addLoadCommand( - make("/usr/lib/libSystem.B.dylib")); } void Writer::createOutputSections() { // First, create hidden sections headerSection = make(); bindingSection = make(); + lazyBindingSection = make(); stringTableSection = make(); symtabSection = make(*stringTableSection); exportSection = make(); @@ -387,6 +392,8 @@ void Writer::run() { getOrCreateOutputSegment(segment_names::linkEdit); scanRelocations(); + if (in.stubHelper->isNeeded()) + in.stubHelper->setup(); // Sort and assign sections to their respective segments. No more sections nor // segments may be created after this method runs. @@ -407,6 +414,7 @@ void Writer::run() { // Fill __LINKEDIT contents. bindingSection->finalizeContents(); + lazyBindingSection->finalizeContents(); exportSection->finalizeContents(); symtabSection->finalizeContents(); @@ -426,4 +434,10 @@ void Writer::run() { void macho::writeResult() { Writer().run(); } -void macho::createSyntheticSections() { in.got = make(); } +void macho::createSyntheticSections() { + in.got = make(); + in.lazyPointers = make(); + in.stubs = make(); + in.stubHelper = make(); + in.imageLoaderCache = make(); +} diff --git a/lld/test/MachO/Inputs/libgoodbye.s b/lld/test/MachO/Inputs/libgoodbye.s index 205f877fbf01..40c829cfc6e2 100644 --- a/lld/test/MachO/Inputs/libgoodbye.s +++ b/lld/test/MachO/Inputs/libgoodbye.s @@ -1,5 +1,14 @@ .section __TEXT,__cstring -.globl _goodbye_world +.globl _goodbye_world, _print_goodbye _goodbye_world: .asciz "Goodbye world!\n" + +.text +_print_goodbye: + movl $0x2000004, %eax # write() syscall + mov $1, %rdi # stdout + leaq _goodbye_world(%rip), %rsi + mov $15, %rdx # length of str + syscall + ret diff --git a/lld/test/MachO/Inputs/libhello.s b/lld/test/MachO/Inputs/libhello.s index 9fc880c85cb5..0b9930bafcb1 100644 --- a/lld/test/MachO/Inputs/libhello.s +++ b/lld/test/MachO/Inputs/libhello.s @@ -1,8 +1,17 @@ .section __TEXT,__cstring -.globl _hello_world, _hello_its_me +.globl _hello_world, _hello_its_me, _print_hello _hello_world: .asciz "Hello world!\n" _hello_its_me: .asciz "Hello, it's me\n" + +.text +_print_hello: + movl $0x2000004, %eax # write() syscall + mov $1, %rdi # stdout + leaq _hello_world(%rip), %rsi + mov $13, %rdx # length of str + syscall + ret diff --git a/lld/test/MachO/dylink-lazy.s b/lld/test/MachO/dylink-lazy.s new file mode 100644 index 000000000000..911ca60777e7 --- /dev/null +++ b/lld/test/MachO/dylink-lazy.s @@ -0,0 +1,59 @@ +# REQUIRES: x86 +# RUN: mkdir -p %t +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %p/Inputs/libhello.s \ +# RUN: -o %t/libhello.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %p/Inputs/libgoodbye.s \ +# RUN: -o %t/libgoodbye.o +# RUN: lld -flavor darwinnew -dylib -install_name \ +# RUN: @executable_path/libhello.dylib %t/libhello.o -o %t/libhello.dylib +# RUN: lld -flavor darwinnew -dylib -install_name \ +# RUN: @executable_path/libgoodbye.dylib %t/libgoodbye.o -o %t/libgoodbye.dylib + +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %s -o %t/dylink-lazy.o +# RUN: lld -flavor darwinnew -o %t/dylink-lazy -L%t -lhello -lgoodbye %t/dylink-lazy.o + +## When looking at the __stubs section alone, we are unable to easily tell which +## symbol each entry points to. So we call objdump twice in order to get the +## disassembly of __text and the bind tables first, which allow us to check for +## matching entries in __stubs. +# RUN: (llvm-objdump -d --no-show-raw-insn --syms --bind --lazy-bind %t/dylink-lazy; \ +# RUN: llvm-objdump -D --no-show-raw-insn %t/dylink-lazy) | FileCheck %s + +# CHECK-LABEL: SYMBOL TABLE: +# CHECK: {{0*}}[[#%x, IMGLOADER:]] {{.*}} __DATA,__data __dyld_private + +# CHECK-LABEL: Disassembly of section __TEXT,__text: +# CHECK: callq 0x[[#%x, HELLO_STUB:]] +# CHECK-NEXT: callq 0x[[#%x, GOODBYE_STUB:]] + +# CHECK-LABEL: Bind table: +# CHECK: __DATA_CONST __got 0x[[#%x, BINDER:]] pointer 0 libSystem dyld_stub_binder + +# CHECK-LABEL: Lazy bind table: +# CHECK-DAG: __DATA __la_symbol_ptr 0x{{0*}}[[#%x, HELLO_LAZY_PTR:]] libhello _print_hello +# CHECK-DAG: __DATA __la_symbol_ptr 0x{{0*}}[[#%x, GOODBYE_LAZY_PTR:]] libgoodbye _print_goodbye + +# CHECK-LABEL: Disassembly of section __TEXT,__stubs: +# CHECK-DAG: [[#%x, HELLO_STUB]]: jmpq *[[#%u, HELLO_LAZY_PTR - HELLO_STUB - 6]](%rip) +# CHECK-DAG: [[#%x, GOODBYE_STUB]]: jmpq *[[#%u, GOODBYE_LAZY_PTR - GOODBYE_STUB - 6]](%rip) + +# CHECK-LABEL: Disassembly of section __TEXT,__stub_helper: +# CHECK: {{0*}}[[#%x, STUB_HELPER_ENTRY:]] <__stub_helper>: +# CHECK-NEXT: leaq [[#%u, IMGLOADER - STUB_HELPER_ENTRY - 7]](%rip), %r11 +# CHECK-NEXT: pushq %r11 +# CHECK-NEXT: jmpq *[[#%u, BINDER_OFF:]](%rip) +# CHECK-NEXT: [[#%x, BINDER - BINDER_OFF]]: nop +# CHECK-NEXT: pushq $0 +# CHECK-NEXT: jmp 0x[[#STUB_HELPER_ENTRY]] +# CHECK-NEXT: pushq $21 +# CHECK-NEXT: jmp 0x[[#STUB_HELPER_ENTRY]] + +.text +.globl _main + +_main: + sub $8, %rsp # 16-byte-align the stack; dyld checks for this + callq _print_hello + callq _print_goodbye + add $8, %rsp + ret