Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -57,6 +57,9 @@ using namespace llvm::sys::fs; file_magic Magic = identify_magic(MB.getBuffer()); + if (Magic == file_magic::archive) + return llvm::make_unique(MB); + std::pair Type = object::getElfArchType(MB.getBuffer()); if (Type.second != ELF::ELFDATA2LSB && Type.second != ELF::ELFDATA2MSB) Index: ELF/InputFiles.h =================================================================== --- ELF/InputFiles.h +++ ELF/InputFiles.h @@ -14,16 +14,22 @@ #include "Symbols.h" #include "lld/Core/LLVM.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/Object/Archive.h" #include "llvm/Object/ELF.h" namespace lld { namespace elf2 { + +using llvm::object::Archive; + +class Lazy; class SymbolBody; // The root class of input files. class InputFile { public: - enum Kind { ObjectKind, SharedKind }; + enum Kind { ObjectKind, SharedKind, ArchiveKind }; Kind kind() const { return FileKind; } virtual ~InputFile() {} @@ -137,6 +143,25 @@ ArrayRef SymtabSHNDX; }; +class ArchiveFile : public InputFile { +public: + explicit ArchiveFile(MemoryBufferRef M) : InputFile(ArchiveKind, M) {} + static bool classof(const InputFile *F) { return F->kind() == ArchiveKind; } + void parse() override; + + // Returns a memory buffer for a given symbol. An empty memory buffer + // is returned if we have already returned the same memory buffer. + // (So that we don't instantiate same members more than once.) + MemoryBufferRef getMember(const Archive::Symbol *Sym); + + llvm::MutableArrayRef getLazySymbols() { return LazySymbols; } + +private: + std::unique_ptr File; + std::vector LazySymbols; + llvm::DenseMap Seen; +}; + // .so file. class SharedFileBase : public ELFFileBase { public: Index: ELF/InputFiles.cpp =================================================================== --- ELF/InputFiles.cpp +++ ELF/InputFiles.cpp @@ -13,6 +13,7 @@ #include "Symbols.h" #include "llvm/ADT/STLExtras.h" +using namespace llvm; using namespace llvm::ELF; using namespace llvm::object; @@ -149,6 +150,39 @@ } } +void ArchiveFile::parse() { + auto ArchiveOrErr = Archive::create(MB); + error(ArchiveOrErr, "Failed to parse archive"); + File = std::move(*ArchiveOrErr); + + // Allocate a buffer for Lazy objects. + size_t NumSyms = File->getNumberOfSymbols(); + LazySymbols.reserve(NumSyms); + + // Read the symbol table to construct Lazy objects. + for (const Archive::Symbol &Sym : File->symbols()) + LazySymbols.emplace_back(this, Sym); +} + +// Returns a buffer pointing to a member file containing a given symbol. +MemoryBufferRef ArchiveFile::getMember(const Archive::Symbol *Sym) { + auto ItOrErr = Sym->getMember(); + error(ItOrErr, + Twine("Could not get the member for symbol ") + Sym->getName()); + Archive::child_iterator It = *ItOrErr; + + // Return an empty buffer if we have already returned the same buffer. + bool &SeenMember = Seen[It->getChildOffset()]; + if (SeenMember) { + return MemoryBufferRef(); + } + SeenMember = true; + ErrorOr Ret = It->getMemoryBufferRef(); + error(Ret, Twine("Could not get the buffer for the member defining symbol ") + + Sym->getName()); + return *Ret; +} + template void SharedFile::parse() { this->openELF(MB); } namespace lld { Index: ELF/SymbolTable.h =================================================================== --- ELF/SymbolTable.h +++ ELF/SymbolTable.h @@ -54,11 +54,16 @@ } private: + Symbol *insert(SymbolBody *New); void addELFFile(ELFFileBase *File); + void addLazy(Lazy *New); + void addMemberFile(Lazy *Body); template void init(); template void resolve(SymbolBody *Body); + std::vector> ArchiveFiles; + llvm::DenseMap Symtab; llvm::BumpPtrAllocator Alloc; Index: ELF/SymbolTable.cpp =================================================================== --- ELF/SymbolTable.cpp +++ ELF/SymbolTable.cpp @@ -23,8 +23,13 @@ void SymbolTable::addFile(std::unique_ptr File) { File->parse(); InputFile *FileP = File.release(); - auto *P = cast(FileP); - addELFFile(P); + if (auto *AF = dyn_cast(FileP)) { + ArchiveFiles.emplace_back(AF); + for (Lazy &Sym : AF->getLazySymbols()) + addLazy(&Sym); + return; + } + addELFFile(cast(FileP)); } template void SymbolTable::init() { @@ -88,22 +93,65 @@ // This function resolves conflicts if there's an existing symbol with // the same name. Decisions are made based on symbol type. template void SymbolTable::resolve(SymbolBody *New) { + Symbol *Sym = insert(New); + if (Sym->Body == New) + return; + + SymbolBody *Existing = Sym->Body; + + if (Lazy *L = dyn_cast(Existing)) { + if (New->isUndefined()) { + addMemberFile(L); + return; + } + + // Found a definition for something also in an archive. Ignore the archive + // definition. + Sym->Body = New; + return; + } + + // compare() returns -1, 0, or 1 if the lhs symbol is less preferable, + // equivalent (conflicting), or more preferable, respectively. + int comp = Existing->compare(New); + if (comp < 0) + Sym->Body = New; + if (comp == 0) + error(Twine("duplicate symbol: ") + Sym->Body->getName()); +} + +Symbol *SymbolTable::insert(SymbolBody *New) { // Find an existing Symbol or create and insert a new one. StringRef Name = New->getName(); Symbol *&Sym = Symtab[Name]; if (!Sym) { Sym = new (Alloc) Symbol(New); New->setBackref(Sym); - return; + return Sym; } New->setBackref(Sym); + return Sym; +} - // compare() returns -1, 0, or 1 if the lhs symbol is less preferable, - // equivalent (conflicting), or more preferable, respectively. +void SymbolTable::addLazy(Lazy *New) { + Symbol *Sym = insert(New); + if (Sym->Body == New) + return; SymbolBody *Existing = Sym->Body; - int comp = Existing->compare(New); - if (comp < 0) - Sym->Body = New; - if (comp == 0) - error(Twine("duplicate symbol: ") + Name); + if (Existing->isDefined() || isa(Existing)) + return; + Sym->Body = New; + if (Existing->isUndefined()) + addMemberFile(New); +} + +void SymbolTable::addMemberFile(Lazy *Body) { + std::unique_ptr File = Body->getMember(); + + // getMember returns an empty buffer if the member was already + // read from the library. + if (!File) + return; + + addFile(std::move(File)); } Index: ELF/Symbols.h =================================================================== --- ELF/Symbols.h +++ ELF/Symbols.h @@ -13,13 +13,13 @@ #include "Chunks.h" #include "lld/Core/LLVM.h" +#include "llvm/Object/Archive.h" #include "llvm/Object/ELF.h" namespace lld { namespace elf2 { -using llvm::object::ELFFile; - +class ArchiveFile; class Chunk; class InputFile; class SymbolBody; @@ -42,7 +42,8 @@ DefinedAbsoluteKind = 1, DefinedCommonKind = 2, DefinedLast = 2, - UndefinedKind = 3 + UndefinedKind = 3, + LazyKind = 4, }; Kind kind() const { return static_cast(SymbolKind); } @@ -52,6 +53,7 @@ bool isDefined() const { return !isUndefined(); } bool isStrongUndefined() const { return !IsWeak && isUndefined(); } bool isCommon() const { return SymbolKind == DefinedCommonKind; } + bool isLazy() const { return SymbolKind == LazyKind; } // Returns the symbol name. StringRef getName() const { return Name; } @@ -200,6 +202,28 @@ template typename Undefined::Elf_Sym Undefined::Synthetic; +// This class represents a symbol defined in an archive file. It is +// created from an archive file header, and it knows how to load an +// object file from an archive to replace itself with a defined +// symbol. If the resolver finds both Undefined and Lazy for +// the same name, it will ask the Lazy to load a file. +class Lazy : public SymbolBody { +public: + Lazy(ArchiveFile *F, const llvm::object::Archive::Symbol S) + : SymbolBody(LazyKind, S.getName(), false, llvm::ELF::STV_DEFAULT), + File(F), Sym(S) {} + + static bool classof(const SymbolBody *S) { return S->kind() == LazyKind; } + + // Returns an object file for this symbol, or a nullptr if the file + // was already returned. + std::unique_ptr getMember(); + +private: + ArchiveFile *File; + const llvm::object::Archive::Symbol Sym; +}; + } // namespace elf2 } // namespace lld Index: ELF/Symbols.cpp =================================================================== --- ELF/Symbols.cpp +++ ELF/Symbols.cpp @@ -12,6 +12,9 @@ #include "Error.h" #include "InputFiles.h" +#include "llvm/ADT/STLExtras.h" + +using namespace llvm; using namespace llvm::object; using namespace llvm::ELF; @@ -29,6 +32,7 @@ // Returns 1, 0 or -1 if this symbol should take precedence // over the Other, tie or lose, respectively. template int SymbolBody::compare(SymbolBody *Other) { + assert(!isLazy() && !Other->isLazy()); std::pair L(isDefined(), !isWeak()); std::pair R(Other->isDefined(), !Other->isWeak()); @@ -67,6 +71,36 @@ return 1; } +static std::unique_ptr createFile(MemoryBufferRef MB) { + // FIXME: duplicated + std::pair Type = getElfArchType(MB.getBuffer()); + if (Type.second != ELFDATA2LSB && Type.second != ELFDATA2MSB) + error("Invalid data encoding"); + + if (Type.first == ELFCLASS32) { + if (Type.second == ELFDATA2LSB) + return make_unique>(MB); + return make_unique>(MB); + } + if (Type.first == ELFCLASS64) { + if (Type.second == ELFDATA2LSB) + return make_unique>(MB); + return make_unique>(MB); + } + error("Invalid file class"); +} + +std::unique_ptr Lazy::getMember() { + MemoryBufferRef MBRef = File->getMember(&Sym); + + // getMember returns an empty buffer if the member was already + // read from the library. + if (MBRef.getBuffer().empty()) + return std::unique_ptr(nullptr); + + return createFile(MBRef); +} + template int SymbolBody::compare(SymbolBody *Other); template int SymbolBody::compare(SymbolBody *Other); template int SymbolBody::compare(SymbolBody *Other); Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -302,6 +302,8 @@ StringRef Name = P.first; Symbol *Sym = P.second; SymbolBody *Body = Sym->Body; + if (Body->isLazy()) + continue; const Elf_Sym &InputSym = cast>(Body)->Sym; uint8_t V = Body->getMostConstrainingVisibility(); @@ -325,6 +327,8 @@ assert(Body->isWeak() && "Should be defined by now"); case SymbolBody::DefinedAbsoluteKind: break; + case SymbolBody::LazyKind: + assert("Lazy symbols shouldn't get here."); } uint8_t Type = InputSym.getType(); @@ -453,6 +457,8 @@ for (auto &P : Symtab.getSymbols()) { StringRef Name = P.first; SymbolBody *Body = P.second->Body; + if (Body->isLazy()) + continue; if (auto *C = dyn_cast>(Body)) CommonSymbols.push_back(C); uint8_t V = Body->getMostConstrainingVisibility(); Index: test/elf2/archive.s =================================================================== --- /dev/null +++ test/elf2/archive.s @@ -0,0 +1,10 @@ +# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t +# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %S/basic.s -o %t2 +# RUN: llvm-ar rcs %tar %t2 +# RUN: lld -flavor gnu2 %t %tar -o %tout +# RUN: llvm-nm %tout | FileCheck %s +# REQUIRES: x86 + +# Nothing here. Just needed for the linker to create a undefined _start symbol. + +# CHECK: T _start