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 @@ -34,6 +34,7 @@ case X86_64_RELOC_BRANCH: case X86_64_RELOC_SIGNED: case X86_64_RELOC_GOT_LOAD: + case X86_64_RELOC_SIGNED_1: return read32le(loc); default: error("TODO: Unhandled relocation type " + std::to_string(type)); @@ -46,6 +47,7 @@ case X86_64_RELOC_BRANCH: case X86_64_RELOC_SIGNED: case X86_64_RELOC_GOT_LOAD: + case X86_64_RELOC_SIGNED_1: // 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); diff --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp --- a/lld/MachO/Driver.cpp +++ b/lld/MachO/Driver.cpp @@ -26,6 +26,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/BinaryFormat/MachO.h" #include "llvm/BinaryFormat/Magic.h" +#include "llvm/Object/Archive.h" #include "llvm/Option/ArgList.h" #include "llvm/Option/Option.h" #include "llvm/Support/MemoryBuffer.h" @@ -104,6 +105,17 @@ MemoryBufferRef mbref = *buffer; switch (identify_magic(mbref.getBuffer())) { + case file_magic::archive: { + std::unique_ptr file = CHECK( + object::Archive::create(mbref), path + ": failed to parse archive"); + + if (!file->isEmpty() && !file->hasSymbolTable()) { + error(mbref.getBufferIdentifier() + + ": archive has no index; run ranlib to add one"); + } + inputFiles.push_back(make(*file)); + break; + } case file_magic::macho_object: inputFiles.push_back(make(mbref)); break; @@ -167,7 +179,7 @@ // Initialize InputSections. for (InputFile *file : inputFiles) - for (InputSection *sec : file->sections) + for (InputSection *sec : file->getInputSections()) inputSections.push_back(sec); // Write to an output file. diff --git a/lld/MachO/InputFiles.h b/lld/MachO/InputFiles.h --- a/lld/MachO/InputFiles.h +++ b/lld/MachO/InputFiles.h @@ -28,15 +28,16 @@ enum Kind { ObjKind, DylibKind, + ArchiveKind, }; virtual ~InputFile() = default; Kind kind() const { return fileKind; } StringRef getName() const { return mb.getBufferIdentifier(); } + virtual std::vector getInputSections() const = 0; MemoryBufferRef mb; std::vector symbols; - std::vector sections; protected: InputFile(Kind kind, MemoryBufferRef mb) : mb(mb), fileKind(kind) {} @@ -55,6 +56,10 @@ public: explicit ObjFile(MemoryBufferRef mb); static bool classof(const InputFile *f) { return f->kind() == ObjKind; } + std::vector getInputSections() const { return sections; } + +private: + std::vector sections; }; // .dylib file @@ -62,9 +67,26 @@ public: explicit DylibFile(MemoryBufferRef mb); static bool classof(const InputFile *f) { return f->kind() == DylibKind; } + std::vector getInputSections() const { return sections; } StringRef dylibName; uint64_t ordinal = 0; // Ordinal numbering starts from 1, so 0 is a sentinel + +private: + std::vector sections; +}; + +// .a file +class ArchiveFile : public InputFile { +public: + explicit ArchiveFile(llvm::object::Archive &file); + static bool classof(const InputFile *f) { return f->kind() == ArchiveKind; } + std::vector getInputSections() const; + InputFile *fetch(const llvm::object::Archive::Symbol &sym); + +private: + llvm::DenseSet seen; + std::vector resolvedInputs; }; extern std::vector inputFiles; diff --git a/lld/MachO/InputFiles.cpp b/lld/MachO/InputFiles.cpp --- a/lld/MachO/InputFiles.cpp +++ b/lld/MachO/InputFiles.cpp @@ -263,6 +263,40 @@ } } +ArchiveFile::ArchiveFile(llvm::object::Archive &f) + : InputFile(ArchiveKind, f.getMemoryBufferRef()) { + for (const object::Archive::Symbol &sym : f.symbols()) + symtab->addLazy(sym.getName(), this, sym); +} + +std::vector ArchiveFile::getInputSections() const { + std::vector sections; + for (InputFile *input : resolvedInputs) { + auto inputSections = input->getInputSections(); + sections.insert(sections.end(), inputSections.begin(), inputSections.end()); + } + return sections; +} + +InputFile *ArchiveFile::fetch(const object::Archive::Symbol &sym) { + object::Archive::Child c = + CHECK(sym.getMember(), toString(this) + + ": could not get the member for symbol " + + sym.getName()); + + if (!seen.insert(c.getChildOffset()).second) + return nullptr; + + MemoryBufferRef mb = + CHECK(c.getMemoryBufferRef(), + toString(this) + + ": could not get the buffer for the member defining symbol " + + sym.getName()); + auto file = make(mb); + resolvedInputs.push_back(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/SymbolTable.h b/lld/MachO/SymbolTable.h --- a/lld/MachO/SymbolTable.h +++ b/lld/MachO/SymbolTable.h @@ -30,6 +30,9 @@ Symbol *addDylib(StringRef name, DylibFile *file); + Symbol *addLazy(StringRef name, ArchiveFile *file, + const llvm::object::Archive::Symbol &sym); + 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 @@ -69,4 +69,17 @@ return s; } +Symbol *SymbolTable::addLazy(StringRef name, ArchiveFile *file, + const llvm::object::Archive::Symbol &sym) { + Symbol *s; + bool wasInserted; + std::tie(s, wasInserted) = insert(name); + + if (wasInserted) + replaceSymbol(s, sym); + else if (isa(s)) + file->fetch(sym); + 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 @@ -35,6 +35,7 @@ DefinedKind, UndefinedKind, DylibKind, + LazyKind, }; Kind kind() const { return static_cast(symbolKind); } @@ -79,6 +80,17 @@ uint32_t gotIndex = UINT32_MAX; }; +class LazySymbol : public Symbol { +public: + LazySymbol(const llvm::object::Archive::Symbol &sym) + : Symbol(LazyKind, sym.getName()), sym(sym) {} + + static bool classof(const Symbol *s) { return s->kind() == LazyKind; } + +private: + const llvm::object::Archive::Symbol sym; +}; + inline uint64_t Symbol::getVA() const { if (auto *d = dyn_cast(this)) return d->isec->getVA() + d->value; @@ -89,6 +101,7 @@ alignas(Defined) char a[sizeof(Defined)]; alignas(Undefined) char b[sizeof(Undefined)]; alignas(DylibSymbol) char c[sizeof(DylibSymbol)]; + alignas(LazySymbol) char d[sizeof(LazySymbol)]; }; template diff --git a/lld/test/MachO/Inputs/archive2.s b/lld/test/MachO/Inputs/archive2.s new file mode 100644 --- /dev/null +++ b/lld/test/MachO/Inputs/archive2.s @@ -0,0 +1,4 @@ +.global _boo +_boo: + mov $2, %rax + ret diff --git a/lld/test/MachO/Inputs/archive3.s b/lld/test/MachO/Inputs/archive3.s new file mode 100644 --- /dev/null +++ b/lld/test/MachO/Inputs/archive3.s @@ -0,0 +1,4 @@ +.global _bar +_bar: + mov $3, %rax + ret diff --git a/lld/test/MachO/Inputs/archive4.s b/lld/test/MachO/Inputs/archive4.s new file mode 100644 --- /dev/null +++ b/lld/test/MachO/Inputs/archive4.s @@ -0,0 +1,4 @@ +.global _lonely +_alone: + mov $5, %rax + ret diff --git a/lld/test/MachO/archive-no-index.s b/lld/test/MachO/archive-no-index.s new file mode 100644 --- /dev/null +++ b/lld/test/MachO/archive-no-index.s @@ -0,0 +1,15 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %s -o %t +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %S/Inputs/archive2.s -o %t2 +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %S/Inputs/archive3.s -o %t3 +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %S/Inputs/archive4.s -o %t4 +# RUN: rm -f %t.a +# RUN: llvm-ar rcS %t.a %t2 %t3 %t4 + +# RUN: not lld -flavor darwinnew %t %t.a -o /dev/null 2>&1 | FileCheck %s +# CHECK: error: {{.*}}.a: archive has no index; run ranlib to add one + +.global _main +_main: + mov $0, %rax + ret diff --git a/lld/test/MachO/archive.s b/lld/test/MachO/archive.s new file mode 100644 --- /dev/null +++ b/lld/test/MachO/archive.s @@ -0,0 +1,22 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %s -o %t +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %S/Inputs/archive2.s -o %t2 +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %S/Inputs/archive3.s -o %t3 +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %S/Inputs/archive4.s -o %t4 + +# RUN: rm -f %t.a +# RUN: llvm-ar rcs %t.a %t2 %t3 %t4 + +# RUN: lld -flavor darwinnew %t %t.a -o %t.out +# RUN: llvm-nm %t.out | FileCheck %s + +# CHECK: T _bar +# CHECK-NEXT: T _boo +# CHECK-NEXT: T _main + +.global _main +_main: + callq _boo + callq _bar + mov $0, %rax + ret diff --git a/lld/test/MachO/bad-archive.s b/lld/test/MachO/bad-archive.s new file mode 100644 --- /dev/null +++ b/lld/test/MachO/bad-archive.s @@ -0,0 +1,11 @@ +# REQUIRES: x86 +# RUN: echo "!" > %t.a +# RUN: echo "foo" >> %t.a +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %s -o %t.o + +# RUN: not lld -flavor darwinnew %t.o %t.a -o /dev/null 2>&1 | FileCheck -DFILE=%t.a %s +# CHECK: error: [[FILE]]: failed to parse archive: truncated or malformed archive (remaining size of archive too small for next archive member header at offset 8) + +.global _main +_main: + ret