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.release())); + break; + } case file_magic::macho_object: inputFiles.push_back(make(mbref)); break; diff --git a/lld/MachO/InputFiles.h b/lld/MachO/InputFiles.h --- a/lld/MachO/InputFiles.h +++ b/lld/MachO/InputFiles.h @@ -28,6 +28,7 @@ enum Kind { ObjKind, DylibKind, + ArchiveKind, }; virtual ~InputFile() = default; @@ -67,6 +68,17 @@ uint64_t ordinal = 0; // Ordinal numbering starts from 1, so 0 is a sentinel }; +// .a file +class ArchiveFile : public InputFile { +public: + explicit ArchiveFile(llvm::object::Archive *file); + static bool classof(const InputFile *f) { return f->kind() == ArchiveKind; } + InputFile *fetch(const llvm::object::Archive::Symbol &sym); + +private: + llvm::DenseSet seen; +}; + 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 @@ -263,6 +263,31 @@ } } +ArchiveFile::ArchiveFile(llvm::object::Archive *f) + : InputFile(ArchiveKind, f->getMemoryBufferRef()) { + for (const object::Archive::Symbol &sym : f->symbols()) + symtab->addLazy(sym.getName(), this, sym); +} + +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); + sections.insert(sections.end(), file->sections.begin(), file->sections.end()); + 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 @@ -56,6 +56,8 @@ if (wasInserted) replaceSymbol(s, name); + else if (LazySymbol *lazy = dyn_cast(s)) + lazy->fetchFile(); return s; } @@ -69,4 +71,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, file, 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,20 @@ uint32_t gotIndex = UINT32_MAX; }; +class LazySymbol : public Symbol { +public: + LazySymbol(ArchiveFile *file, const llvm::object::Archive::Symbol &sym) + : Symbol(LazyKind, sym.getName()), file(file), sym(sym) {} + + static bool classof(const Symbol *s) { return s->kind() == LazyKind; } + + InputFile *fetchFile(); + +private: + ArchiveFile *file; + 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 +104,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/MachO/Symbols.cpp b/lld/MachO/Symbols.cpp --- a/lld/MachO/Symbols.cpp +++ b/lld/MachO/Symbols.cpp @@ -15,6 +15,8 @@ using namespace lld; using namespace lld::macho; +InputFile *LazySymbol::fetchFile() { return file->fetch(sym); } + // Returns a symbol for an error message. std::string lld::toString(const Symbol &sym) { if (Optional s = demangleItanium(sym.getName())) 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 +_lonely: + mov $5, %rax + ret diff --git a/lld/test/MachO/Inputs/f1.s b/lld/test/MachO/Inputs/f1.s new file mode 100644 --- /dev/null +++ b/lld/test/MachO/Inputs/f1.s @@ -0,0 +1,4 @@ +.global f +f: + mov $1, %rax + ret diff --git a/lld/test/MachO/Inputs/f2.s b/lld/test/MachO/Inputs/f2.s new file mode 100644 --- /dev/null +++ b/lld/test/MachO/Inputs/f2.s @@ -0,0 +1,4 @@ +.global f +f: + mov $2, %rax + ret diff --git a/lld/test/MachO/Inputs/f2g.s b/lld/test/MachO/Inputs/f2g.s new file mode 100644 --- /dev/null +++ b/lld/test/MachO/Inputs/f2g.s @@ -0,0 +1,9 @@ +.global f +f: + mov $2, %rax + ret + +.global g +g: + callq f + ret diff --git a/lld/test/MachO/Inputs/g.s b/lld/test/MachO/Inputs/g.s new file mode 100644 --- /dev/null +++ b/lld/test/MachO/Inputs/g.s @@ -0,0 +1,5 @@ +.global f +.global g +g: + callq f + 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,26 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %s -o %t.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %S/Inputs/archive2.s -o %t2.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %S/Inputs/archive3.s -o %t3.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %S/Inputs/archive4.s -o %t4.o + +# RUN: rm -f %t.a +# RUN: llvm-ar rcs %t.a %t2.o %t3.o %t4.o +# RUN: lld -flavor darwinnew %t.o %t.a -o %t.out + +## TODO: Run llvm-nm -p to validate symbol order +# RUN: llvm-nm %t.out | FileCheck %s +# CHECK: T _bar +# CHECK: T _boo +# CHECK: T _main + +# RUN: llvm-nm %t.out | FileCheck %s --check-prefix VISIBLE +# VISIBLE-NOT: T _alone +# VISIBLE-NOT: T _lonely + +.global _main +_main: + callq _boo + callq _bar + mov $0, %rax + ret diff --git a/lld/test/MachO/invalid/archive-no-index.s b/lld/test/MachO/invalid/archive-no-index.s new file mode 100644 --- /dev/null +++ b/lld/test/MachO/invalid/archive-no-index.s @@ -0,0 +1,15 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %s -o %t.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %S/../Inputs/archive2.s -o %t2.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %S/../Inputs/archive3.s -o %t3.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %S/../Inputs/archive4.s -o %t4.o +# RUN: rm -f %t.a +# RUN: llvm-ar rcS %t.a %t2.o %t3.o %t4.o + +# RUN: not lld -flavor darwinnew %t.o %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/invalid/bad-archive.s b/lld/test/MachO/invalid/bad-archive.s new file mode 100644 --- /dev/null +++ b/lld/test/MachO/invalid/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 diff --git a/lld/test/MachO/symbol-order.s b/lld/test/MachO/symbol-order.s new file mode 100644 --- /dev/null +++ b/lld/test/MachO/symbol-order.s @@ -0,0 +1,32 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %S/Inputs/g.s -o %tg.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %S/Inputs/f1.s -o %tf1.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %S/Inputs/f2.s -o %tf2.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %S/Inputs/f2g.s -o %tf2g.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %s -o %t.o + +# RUN: lld -flavor darwinnew -dylib -o %tlibf1.dylib %tf1.o + +# RUN: rm -f %tlibf2_g.a +# RUN: llvm-ar rcs %tlibf2_g.a %tf2.o %tg.o + +# RUN: rm -f %tlibf2g.a +# RUN: llvm-ar rcs %tlibf2g.a %tf2g.o + +# RUN: lld -flavor darwinnew %tlibf1.dylib %tlibf2_g.a %t.o -lSystem -o %t.out +# RUN: ./%t.out; echo $? | FileCheck %s --check-prefix DYLIB-FIRST +# DYLIB-FIRST: 1 + +# RUN: lld -flavor darwinnew %tlibf2_g.a %tlibf1.dylib %t.o -lSystem -o %t.out +# RUN: ./%t.out; echo $? | FileCheck %s --check-prefix ARCHIVE-FIRST +# ARCHIVE-FIRST: 2 + +# RUN: lld -flavor darwinnew %tlibf1.dylib %tlibf2g.a %t.o -lSystem -o %t.out +# RUN: ./%t.out; echo $? | FileCheck %s --check-prefix ARCHIVE-PRIORITY +# ARCHIVE-PRIORITY: 2 + +.global g +.global _main +_main: + callq g + ret