forked from OSchip/llvm-project
[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
This commit is contained in:
parent
db157d2733
commit
b3e2fc931d
|
@ -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 <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;
|
||||
|
|
|
@ -158,6 +158,14 @@ bool macho::link(llvm::ArrayRef<const char *> 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<Defined>(config->entry)) {
|
||||
error("undefined symbol: " + config->entry->getName());
|
||||
return false;
|
||||
|
|
|
@ -263,6 +263,16 @@ DylibFile::DylibFile(MemoryBufferRef mb) : InputFile(DylibKind, mb) {
|
|||
}
|
||||
}
|
||||
|
||||
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>";
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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<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();
|
||||
}
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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<uint8_t *>(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<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_) {}
|
||||
|
||||
|
|
|
@ -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<const DylibSymbol *> &getEntries() const {
|
||||
return entries;
|
||||
}
|
||||
|
@ -83,6 +86,8 @@ public:
|
|||
// runtime by dyld.
|
||||
}
|
||||
|
||||
void addEntry(DylibSymbol &sym);
|
||||
|
||||
private:
|
||||
llvm::SetVector<const DylibSymbol *> entries;
|
||||
};
|
||||
|
@ -103,6 +108,91 @@ public:
|
|||
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:
|
||||
|
@ -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;
|
||||
|
|
|
@ -9,11 +9,14 @@
|
|||
#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.
|
||||
|
@ -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();
|
||||
|
|
|
@ -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<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>());
|
||||
|
@ -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<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>();
|
||||
|
@ -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<GotSection>(); }
|
||||
void macho::createSyntheticSections() {
|
||||
in.got = make<GotSection>();
|
||||
in.lazyPointers = make<LazyPointerSection>();
|
||||
in.stubs = make<StubsSection>();
|
||||
in.stubHelper = make<StubHelperSection>();
|
||||
in.imageLoaderCache = make<ImageLoaderCacheSection>();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue