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 @@ -32,6 +32,7 @@ uint64_t X86_64::getImplicitAddend(const uint8_t *loc, uint8_t type) const { switch (type) { case X86_64_RELOC_SIGNED: + case X86_64_RELOC_GOT_LOAD: return read32le(loc); default: error("TODO: Unhandled relocation type " + std::to_string(type)); @@ -42,8 +43,9 @@ void X86_64::relocateOne(uint8_t *loc, uint8_t type, uint64_t val) const { switch (type) { case X86_64_RELOC_SIGNED: - // This type is only used for pc-relative relocations, so offset by 4 since - // the RIP has advanced by 4 at this point. + case X86_64_RELOC_GOT_LOAD: + // These types are only used for pc-relative relocations, so offset by 4 + // since the RIP has advanced by 4 at this point. write32le(loc, val - 4); break; default: diff --git a/lld/MachO/CMakeLists.txt b/lld/MachO/CMakeLists.txt --- a/lld/MachO/CMakeLists.txt +++ b/lld/MachO/CMakeLists.txt @@ -10,6 +10,7 @@ OutputSegment.cpp SymbolTable.cpp Symbols.cpp + SyntheticSections.cpp Target.cpp Writer.cpp diff --git a/lld/MachO/Config.h b/lld/MachO/Config.h --- a/lld/MachO/Config.h +++ b/lld/MachO/Config.h @@ -11,6 +11,8 @@ #include "llvm/ADT/StringRef.h" +#include + namespace lld { namespace macho { @@ -19,6 +21,8 @@ struct Configuration { llvm::StringRef outputFile; Symbol *entry; + + std::vector searchPaths; }; extern Configuration *config; diff --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp --- a/lld/MachO/Driver.cpp +++ b/lld/MachO/Driver.cpp @@ -68,6 +68,17 @@ return args; } +// This is for -lfoo. We'll look for libfoo.dylib from search paths. +static Optional findDylib(StringRef name) { + for (StringRef dir : config->searchPaths) { + std::string path = (dir + "/lib" + name + ".dylib").str(); + if (fs::exists(path)) + return path; + } + error("library not found for -l" + name); + return None; +} + static TargetInfo *createTargetInfo(opt::InputArgList &args) { StringRef s = args.getLastArgValue(OPT_arch, "x86_64"); if (s != "x86_64") @@ -75,6 +86,15 @@ return createX86_64TargetInfo(); } +static std::vector getSearchPaths(opt::InputArgList &args) { + std::vector ret; + if (!args.hasArg(OPT_Z)) + ret = {"/usr/lib", "/usr/local/lib"}; + for (StringRef s : args::getStrings(args, OPT_L)) + ret.push_back(s); + return ret; +} + static void addFile(StringRef path) { Optional buffer = readFile(path); if (!buffer) @@ -83,7 +103,10 @@ switch (identify_magic(mbref.getBuffer())) { case file_magic::macho_object: - inputFiles.push_back(make(mbref)); + inputFiles.push_back(make(path, mbref)); + break; + case file_magic::macho_dynamically_linked_shared_lib: + inputFiles.push_back(make(path, mbref)); break; default: error(path + ": unhandled file type"); @@ -110,15 +133,21 @@ config->entry = symtab->addUndefined(args.getLastArgValue(OPT_e, "_main")); config->outputFile = args.getLastArgValue(OPT_o, "a.out"); + config->searchPaths = getSearchPaths(args); getOrCreateOutputSegment("__TEXT", VM_PROT_READ | VM_PROT_EXECUTE); getOrCreateOutputSegment("__DATA", VM_PROT_READ | VM_PROT_WRITE); + getOrCreateOutputSegment("__DATA_CONST", VM_PROT_READ | VM_PROT_WRITE); for (opt::Arg *arg : args) { switch (arg->getOption().getID()) { case OPT_INPUT: addFile(arg->getValue()); break; + case OPT_l: + if (Optional path = findDylib(arg->getValue())) + addFile(*path); + break; } } @@ -127,6 +156,8 @@ return false; } + createSyntheticSections(); + // Initialize InputSections. for (InputFile *file : inputFiles) for (InputSection *sec : file->sections) @@ -136,6 +167,7 @@ for (InputSection *isec : inputSections) { OutputSegment *os = getOrCreateOutputSegment(isec->segname, VM_PROT_READ | VM_PROT_WRITE); + isec->parent = os; os->sections[isec->name].push_back(isec); } diff --git a/lld/MachO/InputFiles.h b/lld/MachO/InputFiles.h --- a/lld/MachO/InputFiles.h +++ b/lld/MachO/InputFiles.h @@ -27,20 +27,21 @@ public: enum Kind { ObjKind, + DylibKind, }; virtual ~InputFile() = default; - Kind kind() const { return fileKind; } StringRef getName() const { return mb.getBufferIdentifier(); } + StringRef path; MemoryBufferRef mb; std::vector symbols; std::vector sections; - StringRef dylibName; protected: - InputFile(Kind kind, MemoryBufferRef mb) : mb(mb), fileKind(kind) {} + InputFile(Kind kind, StringRef path, MemoryBufferRef mb) + : path(path), mb(mb), fileKind(kind) {} std::vector parseSections(ArrayRef); @@ -54,10 +55,20 @@ // .o file class ObjFile : public InputFile { public: - explicit ObjFile(MemoryBufferRef mb); + explicit ObjFile(StringRef path, MemoryBufferRef mb); static bool classof(const InputFile *f) { return f->kind() == ObjKind; } }; +// .dylib file +class DylibFile : public InputFile { +public: + explicit DylibFile(StringRef path, MemoryBufferRef mb); + static bool classof(const InputFile *f) { return f->kind() == DylibKind; } + + StringRef dylibName; + uint64_t ordinal = 0; // Ordinal numbering starts from 1, so 0 is a sentinel +}; + extern std::vector inputFiles; llvm::Optional readFile(StringRef path); diff --git a/lld/MachO/InputFiles.cpp b/lld/MachO/InputFiles.cpp --- a/lld/MachO/InputFiles.cpp +++ b/lld/MachO/InputFiles.cpp @@ -74,7 +74,15 @@ std::unique_ptr &mb = *mbOrErr; MemoryBufferRef mbref = mb->getMemBufferRef(); make>(std::move(mb)); // take mb ownership - return mbref; + + // If this is a regular non-fat file, return it. + const char *buf = mbref.getBufferStart(); + auto *hdr = reinterpret_cast(buf); + if (read32be(&hdr->magic) != MachO::FAT_MAGIC) + return mbref; + + error("TODO: Add support for universal binaries"); + return None; } static const load_command *findCommand(const mach_header_64 *hdr, @@ -143,7 +151,8 @@ } } -ObjFile::ObjFile(MemoryBufferRef mb) : InputFile(ObjKind, mb) { +ObjFile::ObjFile(StringRef path, MemoryBufferRef mb) + : InputFile(ObjKind, path, mb) { auto *buf = reinterpret_cast(mb.getBufferStart()); auto *hdr = reinterpret_cast(mb.getBufferStart()); ArrayRef objSections; @@ -155,6 +164,7 @@ sections = parseSections(objSections); } + // TODO: Error on missing LC_SYMTAB? if (const load_command *cmd = findCommand(hdr, LC_SYMTAB)) { auto *c = reinterpret_cast(cmd); const char *strtab = reinterpret_cast(buf) + c->stroff; @@ -168,7 +178,7 @@ // Undefined symbol if (!sym.n_sect) { - error("TODO: Support undefined symbols"); + symbols.push_back(symtab->addUndefined(name)); continue; } @@ -198,6 +208,36 @@ } } +DylibFile::DylibFile(StringRef path, MemoryBufferRef mb) + : InputFile(DylibKind, path, mb) { + auto *buf = reinterpret_cast(mb.getBufferStart()); + auto *hdr = reinterpret_cast(mb.getBufferStart()); + + // Initialize dylibName. + if (const load_command *cmd = findCommand(hdr, LC_ID_DYLIB)) { + auto *c = reinterpret_cast(cmd); + dylibName = reinterpret_cast(cmd) + read32le(&c->dylib.name); + } else { + error("dylib " + path + " missing LC_ID_DYLIB load command"); + return; + } + + // Initialize symbols. + if (const load_command *cmd = findCommand(hdr, LC_SYMTAB)) { + auto *c = reinterpret_cast(cmd); + const char *strtab = reinterpret_cast(buf + c->stroff); + ArrayRef nList( + reinterpret_cast(buf + c->symoff), c->nsyms); + + symbols.reserve(c->nsyms); + + for (const nlist_64 &sym : nList) { + StringRef name = strtab + sym.n_strx; + symbols.push_back(symtab->addDylib(name, this)); + } + } +} + // Returns "" or "baz.o". std::string lld::toString(const InputFile *file) { return file ? std::string(file->getName()) : ""; diff --git a/lld/MachO/InputSection.h b/lld/MachO/InputSection.h --- a/lld/MachO/InputSection.h +++ b/lld/MachO/InputSection.h @@ -19,6 +19,7 @@ class InputFile; class InputSection; +class OutputSegment; class Symbol; struct Reloc { @@ -30,9 +31,12 @@ class InputSection { public: - void writeTo(uint8_t *buf); + virtual ~InputSection() = default; + virtual void writeTo(uint8_t *buf); + virtual size_t getSize() const { return data.size(); } InputFile *file = nullptr; + OutputSegment *parent = nullptr; StringRef name; StringRef segname; diff --git a/lld/MachO/InputSection.cpp b/lld/MachO/InputSection.cpp --- a/lld/MachO/InputSection.cpp +++ b/lld/MachO/InputSection.cpp @@ -8,6 +8,7 @@ #include "InputSection.h" #include "Symbols.h" +#include "SyntheticSections.h" #include "Target.h" #include "lld/Common/Memory.h" #include "llvm/Support/Endian.h" @@ -24,9 +25,13 @@ for (Reloc &r : relocs) { uint64_t va = 0; - if (auto *s = r.target.dyn_cast()) - va = s->getVA(); - else if (auto *isec = r.target.dyn_cast()) + if (auto *s = r.target.dyn_cast()) { + if (auto *dylibSymbol = dyn_cast(s)) { + va = in.got->addr - ImageBase + dylibSymbol->gotIndex * WordSize; + } else { + va = s->getVA(); + } + } else if (auto *isec = r.target.dyn_cast()) va = isec->addr; else llvm_unreachable("Unknown relocation target"); diff --git a/lld/MachO/Options.td b/lld/MachO/Options.td --- a/lld/MachO/Options.td +++ b/lld/MachO/Options.td @@ -1,10 +1,19 @@ include "llvm/Option/OptParser.td" +def L: JoinedOrSeparate<["-"], "L">, MetaVarName<"">, + HelpText<"Add directory to library search path">; + +def Z: Flag<["-"], "Z">, + HelpText<"Do not add standard directories to library search path">; + def arch: Separate<["-"], "arch">, MetaVarName<"">, HelpText<"Architecture to link">; def e: Separate<["-"], "e">, HelpText<"Name of entry point symbol">; +def l: Joined<["-"], "l">, MetaVarName<"">, + HelpText<"Base name of library searched for in -L directories">; + def o: Separate<["-"], "o">, MetaVarName<"">, HelpText<"Path to file to write output">; diff --git a/lld/MachO/OutputSegment.h b/lld/MachO/OutputSegment.h --- a/lld/MachO/OutputSegment.h +++ b/lld/MachO/OutputSegment.h @@ -19,8 +19,13 @@ class OutputSegment { public: + InputSection *firstSection() const { return sections.front().second.at(0); } + + InputSection *lastSection() const { return sections.back().second.back(); } + StringRef name; uint32_t perms; + uint8_t index; llvm::MapVector> sections; }; diff --git a/lld/MachO/SymbolTable.h b/lld/MachO/SymbolTable.h --- a/lld/MachO/SymbolTable.h +++ b/lld/MachO/SymbolTable.h @@ -16,9 +16,9 @@ namespace lld { namespace macho { -class InputFile; -class InputSection; class ArchiveFile; +class DylibFile; +class InputSection; class Symbol; class SymbolTable { @@ -27,6 +27,8 @@ Symbol *addUndefined(StringRef name); + Symbol *addDylib(StringRef name, DylibFile *file); + ArrayRef getSymbols() const { return symVector; } Symbol *find(StringRef name); diff --git a/lld/MachO/SymbolTable.cpp b/lld/MachO/SymbolTable.cpp --- a/lld/MachO/SymbolTable.cpp +++ b/lld/MachO/SymbolTable.cpp @@ -59,4 +59,14 @@ return s; } +Symbol *SymbolTable::addDylib(StringRef name, DylibFile *file) { + Symbol *s; + bool wasInserted; + std::tie(s, wasInserted) = insert(name); + + if (wasInserted) + replaceSymbol(s, file, name); + return s; +} + SymbolTable *macho::symtab; diff --git a/lld/MachO/Symbols.h b/lld/MachO/Symbols.h --- a/lld/MachO/Symbols.h +++ b/lld/MachO/Symbols.h @@ -18,7 +18,7 @@ namespace macho { class InputSection; -class InputFile; +class DylibFile; class ArchiveFile; struct StringRefZ { @@ -34,6 +34,7 @@ enum Kind { DefinedKind, UndefinedKind, + DylibKind, }; Kind kind() const { return static_cast(symbolKind); } @@ -42,11 +43,8 @@ uint64_t getVA() const; - InputFile *file; - protected: - Symbol(Kind k, InputFile *file, StringRefZ name) - : file(file), symbolKind(k), name(name) {} + Symbol(Kind k, StringRefZ name) : symbolKind(k), name(name) {} Kind symbolKind; StringRefZ name; @@ -55,7 +53,7 @@ class Defined : public Symbol { public: Defined(StringRefZ name, InputSection *isec, uint32_t value) - : Symbol(DefinedKind, nullptr, name), isec(isec), value(value) {} + : Symbol(DefinedKind, name), isec(isec), value(value) {} InputSection *isec; uint32_t value; @@ -65,11 +63,22 @@ class Undefined : public Symbol { public: - Undefined(StringRefZ name) : Symbol(UndefinedKind, nullptr, name) {} + Undefined(StringRefZ name) : Symbol(UndefinedKind, name) {} static bool classof(const Symbol *s) { return s->kind() == UndefinedKind; } }; +class DylibSymbol : public Symbol { +public: + DylibSymbol(DylibFile *file, StringRefZ name) + : Symbol(DylibKind, name), file(file) {} + + static bool classof(const Symbol *s) { return s->kind() == DylibKind; } + + DylibFile *file; + uint32_t gotIndex = UINT32_MAX; +}; + inline uint64_t Symbol::getVA() const { if (auto *d = dyn_cast(this)) return d->isec->addr + d->value - ImageBase; @@ -79,6 +88,7 @@ union SymbolUnion { alignas(Defined) char a[sizeof(Defined)]; alignas(Undefined) char b[sizeof(Undefined)]; + alignas(DylibSymbol) char c[sizeof(DylibSymbol)]; }; template diff --git a/lld/MachO/SyntheticSections.h b/lld/MachO/SyntheticSections.h new file mode 100644 --- /dev/null +++ b/lld/MachO/SyntheticSections.h @@ -0,0 +1,52 @@ +//===- SyntheticSections.h -------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_MACHO_SYNTHETIC_SECTIONS_H +#define LLD_MACHO_SYNTHETIC_SECTIONS_H + +#include "InputSection.h" +#include "Target.h" +#include "llvm/ADT/SetVector.h" + +namespace lld { +namespace macho { + +class DylibSymbol; + +// This section will be populated by dyld with addresses to non-lazily-loaded +// dylib symbols. +class GotSection : public InputSection { +public: + GotSection(); + + void addEntry(DylibSymbol &sym); + const llvm::SetVector &getEntries() const { + return entries; + } + + size_t getSize() const override { return entries.size() * WordSize; } + + void writeTo(uint8_t *buf) override { + // Nothing to write, GOT contains all zeros at link time; it's populated at + // runtime by dyld. + } + +private: + llvm::SetVector entries; +}; + +struct InStruct { + GotSection *got; +}; + +extern InStruct in; + +} // namespace macho +} // namespace lld + +#endif diff --git a/lld/MachO/SyntheticSections.cpp b/lld/MachO/SyntheticSections.cpp new file mode 100644 --- /dev/null +++ b/lld/MachO/SyntheticSections.cpp @@ -0,0 +1,36 @@ +//===- SyntheticSections.cpp ---------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "SyntheticSections.h" +#include "Symbols.h" + +using namespace llvm::MachO; + +namespace lld { +namespace macho { + +GotSection::GotSection() { + segname = "__DATA_CONST"; + name = "__got"; + align = 8; + flags = S_NON_LAZY_SYMBOL_POINTERS; + + // TODO: section_64::reserved1 should be an index into the indirect symbol + // table, which we do not currently emit +} + +void GotSection::addEntry(DylibSymbol &sym) { + if (entries.insert(&sym)) { + sym.gotIndex = entries.size() - 1; + } +} + +InStruct in; + +} // namespace macho +} // namespace lld diff --git a/lld/MachO/Target.h b/lld/MachO/Target.h --- a/lld/MachO/Target.h +++ b/lld/MachO/Target.h @@ -15,6 +15,9 @@ namespace macho { enum { + // We are currently only supporting 64-bit targets since macOS and iOS are + // deprecating 32-bit apps. + WordSize = 8, PageSize = 4096, ImageBase = 4096, MaxAlignmentPowerOf2 = 32, diff --git a/lld/MachO/Writer.h b/lld/MachO/Writer.h --- a/lld/MachO/Writer.h +++ b/lld/MachO/Writer.h @@ -14,6 +14,8 @@ void writeResult(); +void createSyntheticSections(); + } // namespace macho } // namespace lld diff --git a/lld/MachO/Writer.cpp b/lld/MachO/Writer.cpp --- a/lld/MachO/Writer.cpp +++ b/lld/MachO/Writer.cpp @@ -13,11 +13,13 @@ #include "OutputSegment.h" #include "SymbolTable.h" #include "Symbols.h" +#include "SyntheticSections.h" #include "Target.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Memory.h" #include "llvm/BinaryFormat/MachO.h" +#include "llvm/Support/EndianStream.h" #include "llvm/Support/LEB128.h" #include "llvm/Support/MathExtras.h" @@ -44,8 +46,11 @@ Writer() : buffer(errorHandler().outputBuffer) {} void createLoadCommands(); + void scanRelocations(); void assignAddresses(); + void createDyldInfoContents(); + void openFile(); void writeHeader(); void writeSections(); @@ -83,8 +88,9 @@ c->cmd = LC_SEGMENT_64; c->cmdsize = getSize(); strcpy(c->segname, "__LINKEDIT"); + c->vmaddr = addr; c->fileoff = fileOff; - c->filesize = contents.size(); + c->filesize = c->vmsize = contents.size(); c->maxprot = VM_PROT_READ | VM_PROT_WRITE; c->initprot = VM_PROT_READ; } @@ -92,6 +98,7 @@ uint64_t getOffset() const { return fileOff + contents.size(); } uint64_t fileOff = 0; + uint64_t addr = 0; SmallVector contents; }; @@ -103,10 +110,14 @@ auto *c = reinterpret_cast(buf); c->cmd = LC_DYLD_INFO_ONLY; c->cmdsize = getSize(); + c->bind_off = bindOff; + c->bind_size = bindSize; c->export_off = exportOff; c->export_size = exportSize; } + uint64_t bindOff = 0; + uint64_t bindSize = 0; uint64_t exportOff = 0; uint64_t exportSize = 0; }; @@ -139,16 +150,14 @@ c->cmdsize = getSize(); memcpy(c->segname, name.data(), name.size()); - InputSection *firstSec = seg->sections.front().second[0]; - InputSection *lastSec = seg->sections.back().second.back(); - // dyld3's MachOLoaded::getSlide() assumes that the __TEXT segment starts // from the beginning of the file (i.e. the header). // TODO: replace this logic by creating a synthetic __TEXT,__mach_header // section instead. - c->fileoff = name == "__TEXT" ? 0 : firstSec->addr - ImageBase; + c->fileoff = name == "__TEXT" ? 0 : seg->firstSection()->addr - ImageBase; c->vmaddr = c->fileoff + ImageBase; - c->vmsize = c->filesize = lastSec->addr + lastSec->data.size() - c->vmaddr; + c->vmsize = c->filesize = + seg->lastSection()->addr + seg->lastSection()->getSize() - c->vmaddr; c->maxprot = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; c->initprot = seg->perms; c->nsects = seg->sections.size(); @@ -171,7 +180,7 @@ maxAlign = std::max(maxAlign, section->align); sectHdr->align = Log2_32(maxAlign); sectHdr->flags = sections[0]->flags; - sectHdr->size = sections.back()->addr + sections.back()->data.size() - + sectHdr->size = sections.back()->addr + sections.back()->getSize() - sections[0]->addr; } } @@ -265,13 +274,34 @@ loadCommands.push_back(make()); loadCommands.push_back(make()); loadCommands.push_back(make()); + + uint8_t segIndex = 1; // LCPagezero is a segment load command + for (OutputSegment *seg : outputSegments) { + if (!seg->sections.empty()) { + loadCommands.push_back(make(seg->name, seg)); + seg->index = segIndex++; + } + } + + uint64_t dylibOrdinal = 1; + for (InputFile *file : inputFiles) { + if (auto *dylibFile = dyn_cast(file)) { + loadCommands.push_back(make(dylibFile->dylibName)); + 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. loadCommands.push_back(make("/usr/lib/libSystem.B.dylib")); +} - for (OutputSegment *seg : outputSegments) - if (!seg->sections.empty()) - loadCommands.push_back(make(seg->name, seg)); +void Writer::scanRelocations() { + for (InputSection *sect : inputSections) + for (Reloc &r : sect->relocs) + if (auto *s = r.target.dyn_cast()) + if (auto *dylibSymbol = dyn_cast(s)) + in.got->addEntry(*dylibSymbol); } void Writer::assignAddresses() { @@ -291,14 +321,56 @@ for (InputSection *isec : sections) { addr = alignTo(addr, isec->align); isec->addr = addr; - addr += isec->data.size(); + addr += isec->getSize(); } } } + addr = alignTo(addr, PageSize); + linkEditSeg->addr = addr; linkEditSeg->fileOff = addr - ImageBase; } +// LC_DYLD_INFO_ONLY contains symbol import/export information. Imported +// symbols are described by a sequence of bind opcodes, which allow for a +// compact encoding. Exported symbols are described using a trie. +void Writer::createDyldInfoContents() { + uint64_t sectionStart = linkEditSeg->getOffset(); + raw_svector_ostream os{linkEditSeg->contents}; + + if (in.got->getSize() != 0) { + // Emit bind opcodes, which tell dyld which dylib symbols to load. + + // Tell dyld to write to the section containing the GOT. + os << static_cast(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | + in.got->parent->index); + encodeULEB128(in.got->addr - in.got->parent->firstSection()->addr, os); + for (const DylibSymbol *sym : in.got->getEntries()) { + // TODO: Implement compact encoding -- we only need to encode the + // differences between consecutive symbol entries. + if (sym->file->ordinal <= BIND_IMMEDIATE_MASK) { + os << static_cast(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | + sym->file->ordinal); + } else { + error("TODO: Support larger dylib symbol ordinals"); + continue; + } + os << static_cast(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM) + << sym->getName() << '\0' + << static_cast(BIND_OPCODE_SET_TYPE_IMM | BIND_TYPE_POINTER) + << static_cast(BIND_OPCODE_DO_BIND); + } + + os << static_cast(BIND_OPCODE_DONE); + + dyldInfoSeg->bindOff = sectionStart; + dyldInfoSeg->bindSize = linkEditSeg->getOffset() - sectionStart; + } + + // TODO: emit bind opcodes for lazy symbols. + // TODO: Implement symbol export trie. +} + void Writer::openFile() { Expected> bufferOrErr = FileOutputBuffer::create(config->outputFile, fileSize, @@ -342,7 +414,11 @@ void Writer::run() { createLoadCommands(); + scanRelocations(); assignAddresses(); + + // Fill __LINKEDIT contents + createDyldInfoContents(); fileSize = linkEditSeg->fileOff + linkEditSeg->contents.size(); openFile(); @@ -357,3 +433,8 @@ } void macho::writeResult() { Writer().run(); } + +void macho::createSyntheticSections() { + in.got = make(); + inputSections.push_back(in.got); +} diff --git a/lld/test/MachO/Inputs/goodbye-dylib.yaml b/lld/test/MachO/Inputs/goodbye-dylib.yaml new file mode 100644 --- /dev/null +++ b/lld/test/MachO/Inputs/goodbye-dylib.yaml @@ -0,0 +1,175 @@ +## This yaml file was originally generated from linking the following source +## input with ld64: +## +## .section __TEXT,__cstring +## .globl _goodbye_world +## +## _goodbye_world: +## .asciz "Goodbye world!\n" +## +## When lld can produce dylibs, we will use that instead for our test setup. + +--- !mach-o +FileHeader: + magic: 0xFEEDFACF + cputype: 0x01000007 + cpusubtype: 0x00000003 + filetype: 0x00000006 + ncmds: 11 + sizeofcmds: 624 + flags: 0x00100085 + reserved: 0x00000000 +LoadCommands: + - cmd: LC_SEGMENT_64 + cmdsize: 232 + segname: __TEXT + vmaddr: 0 + vmsize: 4096 + fileoff: 0 + filesize: 4096 + maxprot: 5 + initprot: 5 + nsects: 2 + flags: 0 + Sections: + - sectname: __text + segname: __TEXT + addr: 0x0000000000000FF0 + size: 0 + offset: 0x00000FF0 + align: 0 + reloff: 0x00000000 + nreloc: 0 + flags: 0x80000400 + reserved1: 0x00000000 + reserved2: 0x00000000 + reserved3: 0x00000000 + content: '' + - sectname: __cstring + segname: __TEXT + addr: 0x0000000000000FF0 + size: 16 + offset: 0x00000FF0 + align: 0 + reloff: 0x00000000 + nreloc: 0 + flags: 0x00000002 + reserved1: 0x00000000 + reserved2: 0x00000000 + reserved3: 0x00000000 + content: 476F6F6462796520776F726C64210A00 + - cmd: LC_SEGMENT_64 + cmdsize: 72 + segname: __LINKEDIT + vmaddr: 4096 + vmsize: 4096 + fileoff: 4096 + filesize: 72 + maxprot: 1 + initprot: 1 + nsects: 0 + flags: 0 + - cmd: LC_ID_DYLIB + cmdsize: 64 + dylib: + name: 24 + timestamp: 1 + current_version: 0 + compatibility_version: 0 + PayloadString: '@executable_path/libgoodbye.dylib' + ZeroPadBytes: 7 + - cmd: LC_DYLD_INFO_ONLY + cmdsize: 48 + rebase_off: 0 + rebase_size: 0 + bind_off: 0 + bind_size: 0 + weak_bind_off: 0 + weak_bind_size: 0 + lazy_bind_off: 0 + lazy_bind_size: 0 + export_off: 4096 + export_size: 24 + - cmd: LC_SYMTAB + cmdsize: 24 + symoff: 4128 + nsyms: 1 + stroff: 4144 + strsize: 24 + - cmd: LC_DYSYMTAB + cmdsize: 80 + ilocalsym: 0 + nlocalsym: 0 + iextdefsym: 0 + nextdefsym: 1 + iundefsym: 1 + nundefsym: 0 + tocoff: 0 + ntoc: 0 + modtaboff: 0 + nmodtab: 0 + extrefsymoff: 0 + nextrefsyms: 0 + indirectsymoff: 0 + nindirectsyms: 0 + extreloff: 0 + nextrel: 0 + locreloff: 0 + nlocrel: 0 + - cmd: LC_UUID + cmdsize: 24 + uuid: EA09CDDC-A3EA-3EB9-8C4F-334077FE6E5A + - cmd: LC_BUILD_VERSION + cmdsize: 32 + platform: 1 + minos: 659200 + sdk: 659200 + ntools: 1 + Tools: + - tool: 3 + version: 34734080 + - cmd: LC_SOURCE_VERSION + cmdsize: 16 + version: 0 + - cmd: LC_FUNCTION_STARTS + cmdsize: 16 + dataoff: 4120 + datasize: 8 + - cmd: LC_DATA_IN_CODE + cmdsize: 16 + dataoff: 4128 + datasize: 0 +LinkEditData: + ExportTrie: + TerminalSize: 0 + NodeOffset: 0 + Name: '' + Flags: 0x0000000000000000 + Address: 0x0000000000000000 + Other: 0x0000000000000000 + ImportName: '' + Children: + - TerminalSize: 3 + NodeOffset: 18 + Name: _goodbye_world + Flags: 0x0000000000000000 + Address: 0x0000000000000FF0 + Other: 0x0000000000000000 + ImportName: '' + NameList: + - n_strx: 2 + n_type: 0x0F + n_sect: 2 + n_desc: 0 + n_value: 4080 + StringTable: + - ' ' + - _goodbye_world + - '' + - '' + - '' + - '' + - '' + - '' + - '' +... diff --git a/lld/test/MachO/Inputs/hello-dylib.yaml b/lld/test/MachO/Inputs/hello-dylib.yaml new file mode 100644 --- /dev/null +++ b/lld/test/MachO/Inputs/hello-dylib.yaml @@ -0,0 +1,169 @@ +## This yaml file was originally generated from linking the following source +## input with ld64: +## +## .section __TEXT,__cstring +## .globl _hello_world +## +## _hello_world: +## .asciz "Hello world!\n" +## +## When lld can produce dylibs, we will use that instead for our test setup. + +--- !mach-o +FileHeader: + magic: 0xFEEDFACF + cputype: 0x01000007 + cpusubtype: 0x00000003 + filetype: 0x00000006 + ncmds: 11 + sizeofcmds: 616 + flags: 0x00100085 + reserved: 0x00000000 +LoadCommands: + - cmd: LC_SEGMENT_64 + cmdsize: 232 + segname: __TEXT + vmaddr: 0 + vmsize: 4096 + fileoff: 0 + filesize: 4096 + maxprot: 5 + initprot: 5 + nsects: 2 + flags: 0 + Sections: + - sectname: __text + segname: __TEXT + addr: 0x0000000000000FF2 + size: 0 + offset: 0x00000FF2 + align: 0 + reloff: 0x00000000 + nreloc: 0 + flags: 0x80000400 + reserved1: 0x00000000 + reserved2: 0x00000000 + reserved3: 0x00000000 + content: '' + - sectname: __cstring + segname: __TEXT + addr: 0x0000000000000FF2 + size: 14 + offset: 0x00000FF2 + align: 0 + reloff: 0x00000000 + nreloc: 0 + flags: 0x00000002 + reserved1: 0x00000000 + reserved2: 0x00000000 + reserved3: 0x00000000 + content: 48656C6C6F20776F726C64210A00 + - cmd: LC_SEGMENT_64 + cmdsize: 72 + segname: __LINKEDIT + vmaddr: 4096 + vmsize: 4096 + fileoff: 4096 + filesize: 64 + maxprot: 1 + initprot: 1 + nsects: 0 + flags: 0 + - cmd: LC_ID_DYLIB + cmdsize: 56 + dylib: + name: 24 + timestamp: 1 + current_version: 0 + compatibility_version: 0 + PayloadString: '@executable_path/libhello.dylib' + ZeroPadBytes: 1 + - cmd: LC_DYLD_INFO_ONLY + cmdsize: 48 + rebase_off: 0 + rebase_size: 0 + bind_off: 0 + bind_size: 0 + weak_bind_off: 0 + weak_bind_size: 0 + lazy_bind_off: 0 + lazy_bind_size: 0 + export_off: 4096 + export_size: 24 + - cmd: LC_SYMTAB + cmdsize: 24 + symoff: 4128 + nsyms: 1 + stroff: 4144 + strsize: 16 + - cmd: LC_DYSYMTAB + cmdsize: 80 + ilocalsym: 0 + nlocalsym: 0 + iextdefsym: 0 + nextdefsym: 1 + iundefsym: 1 + nundefsym: 0 + tocoff: 0 + ntoc: 0 + modtaboff: 0 + nmodtab: 0 + extrefsymoff: 0 + nextrefsyms: 0 + indirectsymoff: 0 + nindirectsyms: 0 + extreloff: 0 + nextrel: 0 + locreloff: 0 + nlocrel: 0 + - cmd: LC_UUID + cmdsize: 24 + uuid: 4826226E-9210-3984-A388-D5BD6D6DB368 + - cmd: LC_BUILD_VERSION + cmdsize: 32 + platform: 1 + minos: 659200 + sdk: 659200 + ntools: 1 + Tools: + - tool: 3 + version: 34734080 + - cmd: LC_SOURCE_VERSION + cmdsize: 16 + version: 0 + - cmd: LC_FUNCTION_STARTS + cmdsize: 16 + dataoff: 4120 + datasize: 8 + - cmd: LC_DATA_IN_CODE + cmdsize: 16 + dataoff: 4128 + datasize: 0 +LinkEditData: + ExportTrie: + TerminalSize: 0 + NodeOffset: 0 + Name: '' + Flags: 0x0000000000000000 + Address: 0x0000000000000000 + Other: 0x0000000000000000 + ImportName: '' + Children: + - TerminalSize: 3 + NodeOffset: 16 + Name: _hello_world + Flags: 0x0000000000000000 + Address: 0x0000000000000FF2 + Other: 0x0000000000000000 + ImportName: '' + NameList: + - n_strx: 2 + n_type: 0x0F + n_sect: 2 + n_desc: 0 + n_value: 4082 + StringTable: + - ' ' + - _hello_world + - '' +... diff --git a/lld/test/MachO/Inputs/no-id-dylib.yaml b/lld/test/MachO/Inputs/no-id-dylib.yaml new file mode 100644 --- /dev/null +++ b/lld/test/MachO/Inputs/no-id-dylib.yaml @@ -0,0 +1,160 @@ +## This yaml file was originally generated from linking the following source +## input with ld64: +## +## .section __TEXT,__cstring +## .globl _hello_world +## +## _hello_world: +## .asciz "Hello world!\n" +## +## Then we deleted the LC_ID_DYLIB command from the YAML file. + +--- !mach-o +FileHeader: + magic: 0xFEEDFACF + cputype: 0x01000007 + cpusubtype: 0x00000003 + filetype: 0x00000006 + ncmds: 10 + sizeofcmds: 616 + flags: 0x00100085 + reserved: 0x00000000 +LoadCommands: + - cmd: LC_SEGMENT_64 + cmdsize: 232 + segname: __TEXT + vmaddr: 0 + vmsize: 4096 + fileoff: 0 + filesize: 4096 + maxprot: 5 + initprot: 5 + nsects: 2 + flags: 0 + Sections: + - sectname: __text + segname: __TEXT + addr: 0x0000000000000FF2 + size: 0 + offset: 0x00000FF2 + align: 0 + reloff: 0x00000000 + nreloc: 0 + flags: 0x80000400 + reserved1: 0x00000000 + reserved2: 0x00000000 + reserved3: 0x00000000 + content: '' + - sectname: __cstring + segname: __TEXT + addr: 0x0000000000000FF2 + size: 14 + offset: 0x00000FF2 + align: 0 + reloff: 0x00000000 + nreloc: 0 + flags: 0x00000002 + reserved1: 0x00000000 + reserved2: 0x00000000 + reserved3: 0x00000000 + content: 48656C6C6F20776F726C64210A00 + - cmd: LC_SEGMENT_64 + cmdsize: 72 + segname: __LINKEDIT + vmaddr: 4096 + vmsize: 4096 + fileoff: 4096 + filesize: 64 + maxprot: 1 + initprot: 1 + nsects: 0 + flags: 0 + - cmd: LC_DYLD_INFO_ONLY + cmdsize: 48 + rebase_off: 0 + rebase_size: 0 + bind_off: 0 + bind_size: 0 + weak_bind_off: 0 + weak_bind_size: 0 + lazy_bind_off: 0 + lazy_bind_size: 0 + export_off: 4096 + export_size: 24 + - cmd: LC_SYMTAB + cmdsize: 24 + symoff: 4128 + nsyms: 1 + stroff: 4144 + strsize: 16 + - cmd: LC_DYSYMTAB + cmdsize: 80 + ilocalsym: 0 + nlocalsym: 0 + iextdefsym: 0 + nextdefsym: 1 + iundefsym: 1 + nundefsym: 0 + tocoff: 0 + ntoc: 0 + modtaboff: 0 + nmodtab: 0 + extrefsymoff: 0 + nextrefsyms: 0 + indirectsymoff: 0 + nindirectsyms: 0 + extreloff: 0 + nextrel: 0 + locreloff: 0 + nlocrel: 0 + - cmd: LC_UUID + cmdsize: 24 + uuid: 4826226E-9210-3984-A388-D5BD6D6DB368 + - cmd: LC_BUILD_VERSION + cmdsize: 32 + platform: 1 + minos: 659200 + sdk: 659200 + ntools: 1 + Tools: + - tool: 3 + version: 34734080 + - cmd: LC_SOURCE_VERSION + cmdsize: 16 + version: 0 + - cmd: LC_FUNCTION_STARTS + cmdsize: 16 + dataoff: 4120 + datasize: 8 + - cmd: LC_DATA_IN_CODE + cmdsize: 16 + dataoff: 4128 + datasize: 0 +LinkEditData: + ExportTrie: + TerminalSize: 0 + NodeOffset: 0 + Name: '' + Flags: 0x0000000000000000 + Address: 0x0000000000000000 + Other: 0x0000000000000000 + ImportName: '' + Children: + - TerminalSize: 3 + NodeOffset: 16 + Name: _hello_world + Flags: 0x0000000000000000 + Address: 0x0000000000000FF2 + Other: 0x0000000000000000 + ImportName: '' + NameList: + - n_strx: 2 + n_type: 0x0F + n_sect: 2 + n_desc: 0 + n_value: 4082 + StringTable: + - ' ' + - _hello_world + - '' +... diff --git a/lld/test/MachO/dylink.s b/lld/test/MachO/dylink.s new file mode 100644 --- /dev/null +++ b/lld/test/MachO/dylink.s @@ -0,0 +1,35 @@ +# REQUIRES: x86 +# RUN: mkdir -p %t +# RUN: yaml2obj %p/Inputs/hello-dylib.yaml -o %t/libhello.dylib +# RUN: yaml2obj %p/Inputs/goodbye-dylib.yaml -o %t/libgoodbye.dylib +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %s -o %t/dylink.o +# RUN: lld -flavor darwinnew -o %t/dylink -Z -L%t -lhello -lgoodbye %t/dylink.o +# RUN: llvm-objdump --bind -d %t/dylink | FileCheck %s + +# CHECK: movq [[#%u, HELLO_OFF:]](%rip), %rsi +# CHECK-NEXT: [[#%x, HELLO_RIP:]]: + +# CHECK: movq [[#%u, GOODBYE_OFF:]](%rip), %rsi +# CHECK-NEXT: [[#%x, GOODBYE_RIP:]]: + +# CHECK-LABEL: Bind table: +# CHECK-DAG: __DATA_CONST __got 0x{{0*}}[[#%x, HELLO_RIP + HELLO_OFF]] pointer 0 libhello _hello_world +# CHECK-DAG: __DATA_CONST __got 0x{{0*}}[[#%x, GOODBYE_RIP + GOODBYE_OFF]] pointer 0 libgoodbye _goodbye_world + +.section __TEXT,__text +.globl _main + +_main: + movl $0x2000004, %eax # write() syscall + mov $1, %rdi # stdout + movq _hello_world@GOTPCREL(%rip), %rsi + mov $13, %rdx # length of str + syscall + + movl $0x2000004, %eax # write() syscall + mov $1, %rdi # stdout + movq _goodbye_world@GOTPCREL(%rip), %rsi + mov $15, %rdx # length of str + syscall + mov $0, %rax + ret diff --git a/lld/test/MachO/missing-dylib.s b/lld/test/MachO/missing-dylib.s new file mode 100644 --- /dev/null +++ b/lld/test/MachO/missing-dylib.s @@ -0,0 +1,5 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %s -o %t.o +# RUN: not lld -flavor darwinnew -Z -o %t -lmissing %t.o 2>&1 | FileCheck %s + +# CHECK: library not found for -lmissing diff --git a/lld/test/MachO/no-id-dylink.s b/lld/test/MachO/no-id-dylink.s new file mode 100644 --- /dev/null +++ b/lld/test/MachO/no-id-dylink.s @@ -0,0 +1,13 @@ +# REQUIRES: x86 +# RUN: mkdir -p %t +# RUN: yaml2obj %p/Inputs/no-id-dylib.yaml -o %t/libnoid.dylib +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %s -o %t/no-id-dylink.o +# RUN: not lld -flavor darwinnew -o %t/no-id-dylink -Z -L%t -lnoid %t/no-id-dylink.o 2>&1 | FileCheck %s +# CHECK: dylib {{.*}}libnoid.dylib missing LC_ID_DYLIB load command + +.text +.globl _main + +_main: + mov $0, %rax + ret