//
//===----------------------------------------------------------------------===//
+#include "Symbols.h"
+#include "SyntheticSections.h"
#include "Target.h"
+
#include "lld/Common/ErrorHandler.h"
#include "llvm/BinaryFormat/MachO.h"
#include "llvm/Support/Endian.h"
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) {
}
}
-} // 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 <bind offset>
+ 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;
}
}
+ // 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<Defined>(config->entry)) {
error("undefined symbol: " + config->entry->getName());
return false;
}
}
+DylibFile::DylibFile() : InputFile(DylibKind, MemoryBufferRef()) {}
+
+DylibFile *DylibFile::createLibSystemMock() {
+ auto *file = make<DylibFile>();
+ 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 "<internal>" or "baz.o".
std::string lld::toString(const InputFile *file) {
return file ? std::string(file->getName()) : "<internal>";
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
};
#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"
uint64_t va = 0;
if (auto *s = r.target.dyn_cast<Symbol *>()) {
if (auto *dylibSymbol = dyn_cast<DylibSymbol>(s)) {
- va = in.got->addr + dylibSymbol->gotIndex * WordSize;
+ va = target->getDylibSymbolVA(*dylibSymbol, r.type);
} else {
va = s->getVA();
}
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";
DylibFile *file;
uint32_t gotIndex = UINT32_MAX;
+ uint32_t stubsIndex = UINT32_MAX;
+ uint32_t lazyBindOffset = UINT32_MAX;
};
inline uint64_t Symbol::getVA() const {
using namespace llvm;
using namespace llvm::MachO;
using namespace llvm::support;
+using namespace llvm::support::endian;
namespace lld {
namespace macho {
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<uint8_t *>(hdr + 1);
for (LoadCommand *lc : loadCommands) {
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<DylibSymbol>(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<uint8_t>(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<uint8_t>(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM |
+ sym.file->ordinal);
+ else
+ fatal("TODO: Support larger dylib symbol ordinals");
+
+ os << static_cast<uint8_t>(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM)
+ << sym.getName() << '\0' << static_cast<uint8_t>(BIND_OPCODE_DO_BIND)
+ << static_cast<uint8_t>(BIND_OPCODE_DONE);
+ return opstreamOffset;
+}
+
ExportSection::ExportSection()
: SyntheticSection(segment_names::linkEdit, section_names::export_) {}
#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 {
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";
public:
GotSection();
- void addEntry(DylibSymbol &sym);
const llvm::SetVector<const DylibSymbol *> &getEntries() const {
return entries;
}
// runtime by dyld.
}
+ void addEntry(DylibSymbol &sym);
+
private:
llvm::SetVector<const DylibSymbol *> entries;
};
SmallVector<char, 128> 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<DylibSymbol *> &getEntries() const { return entries; }
+
+ void addEntry(DylibSymbol &sym);
+
+private:
+ llvm::SetVector<DylibSymbol *> 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<char, 128> contents;
+ llvm::raw_svector_ostream os{contents};
+};
+
// Stores a trie that describes the set of exported symbols.
class ExportSection : public SyntheticSection {
public:
struct InStruct {
GotSection *got = nullptr;
+ LazyPointerSection *lazyPointers = nullptr;
+ StubsSection *stubs = nullptr;
+ StubHelperSection *stubHelper = nullptr;
+ ImageLoaderCacheSection *imageLoaderCache = nullptr;
};
extern InStruct in;
#ifndef LLD_MACHO_TARGET_H
#define LLD_MACHO_TARGET_H
+#include <cstddef>
#include <cstdint>
namespace lld {
namespace macho {
+class DylibSymbol;
+
enum {
// We are currently only supporting 64-bit targets since macOS and iOS are
// deprecating 32-bit apps.
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();
uint64_t fileOff = 0;
MachHeaderSection *headerSection = nullptr;
BindingSection *bindingSection = nullptr;
+ LazyBindingSection *lazyBindingSection = nullptr;
ExportSection *exportSection = nullptr;
StringTableSection *stringTableSection = nullptr;
SymtabSection *symtabSection = nullptr;
// 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); }
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();
}
BindingSection *bindingSection;
+ LazyBindingSection *lazyBindingSection;
ExportSection *exportSection;
};
for (Reloc &r : sect->relocs)
if (auto *s = r.target.dyn_cast<Symbol *>())
if (auto *dylibSymbol = dyn_cast<DylibSymbol>(s))
- in.got->addEntry(*dylibSymbol);
+ target->prepareDylibSymbolRelocation(*dylibSymbol, r.type);
}
void Writer::createLoadCommands() {
headerSection->addLoadCommand(
- make<LCDyldInfo>(bindingSection, exportSection));
+ make<LCDyldInfo>(bindingSection, lazyBindingSection, exportSection));
headerSection->addLoadCommand(
make<LCSymtab>(symtabSection, stringTableSection));
headerSection->addLoadCommand(make<LCDysymtab>());
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<LCLoadDylib>("/usr/lib/libSystem.B.dylib"));
}
void Writer::createOutputSections() {
// First, create hidden sections
headerSection = make<MachHeaderSection>();
bindingSection = make<BindingSection>();
+ lazyBindingSection = make<LazyBindingSection>();
stringTableSection = make<StringTableSection>();
symtabSection = make<SymtabSection>(*stringTableSection);
exportSection = make<ExportSection>();
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.
// Fill __LINKEDIT contents.
bindingSection->finalizeContents();
+ lazyBindingSection->finalizeContents();
exportSection->finalizeContents();
symtabSection->finalizeContents();
void macho::writeResult() { Writer().run(); }
-void macho::createSyntheticSections() { in.got = make<GotSection>(); }
+void macho::createSyntheticSections() {
+ in.got = make<GotSection>();
+ in.lazyPointers = make<LazyPointerSection>();
+ in.stubs = make<StubsSection>();
+ in.stubHelper = make<StubHelperSection>();
+ in.imageLoaderCache = make<ImageLoaderCacheSection>();
+}
.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
.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
--- /dev/null
+# 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