Index: ELF/Driver.h =================================================================== --- ELF/Driver.h +++ ELF/Driver.h @@ -33,8 +33,13 @@ void createFiles(llvm::opt::InputArgList &Args); template void link(llvm::opt::InputArgList &Args); - llvm::BumpPtrAllocator Alloc; + // True if we are in --whole-archive and --no-whole-archive. bool WholeArchive = false; + + // True if we are in --start-lib and --end-lib. + bool InLib = false; + + llvm::BumpPtrAllocator Alloc; std::vector> Files; std::vector> OwningMBs; }; Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -129,7 +129,10 @@ Files.push_back(createSharedFile(MBRef)); return; default: - Files.push_back(createObjectFile(MBRef)); + if (InLib) + Files.push_back(make_unique(MBRef)); + else + Files.push_back(createObjectFile(MBRef)); } } @@ -359,6 +362,12 @@ case OPT_no_whole_archive: WholeArchive = false; break; + case OPT_start_lib: + InLib = true; + break; + case OPT_end_lib: + InLib = false; + break; } } Index: ELF/InputFiles.h =================================================================== --- ELF/InputFiles.h +++ ELF/InputFiles.h @@ -36,7 +36,14 @@ // The root class of input files. class InputFile { public: - enum Kind { ObjectKind, SharedKind, ArchiveKind, BitcodeKind }; + enum Kind { + ObjectKind, + SharedKind, + LazyObjectKind, + ArchiveKind, + BitcodeKind, + }; + Kind kind() const { return FileKind; } StringRef getName() const { return MB.getBufferIdentifier(); } @@ -154,6 +161,36 @@ llvm::SpecificBumpPtrAllocator> EHAlloc; }; +// LazyObjectFile is analogous to ArchiveFile in the sense that +// the file contains lazy symbols. The difference is that +// LazyObjectFile wraps a single file instead of multiple files. +// +// This class is used for --start-lib and --end-lib options which +// instruct the linker to link object files between them with the +// archive-file-like semantics. +class LazyObjectFile : public InputFile { +public: + explicit LazyObjectFile(MemoryBufferRef M) : InputFile(LazyObjectKind, M) {} + + static bool classof(const InputFile *F) { + return F->kind() == LazyObjectKind; + } + + void parse(); + + llvm::MutableArrayRef getLazySymbols() { return LazySymbols; } + +private: + std::vector getSymbols(); + template std::vector getElfSymbols(); + std::vector getBitcodeSymbols(); + + llvm::BumpPtrAllocator Alloc; + llvm::StringSaver Saver{Alloc}; + std::vector LazySymbols; +}; + +// An ArchiveFile object represents a .a file. class ArchiveFile : public InputFile { public: explicit ArchiveFile(MemoryBufferRef M) : InputFile(ArchiveKind, M) {} @@ -165,11 +202,11 @@ // (So that we don't instantiate same members more than once.) MemoryBufferRef getMember(const Archive::Symbol *Sym); - llvm::MutableArrayRef getLazySymbols() { return LazySymbols; } + llvm::MutableArrayRef getLazySymbols() { return LazySymbols; } private: std::unique_ptr File; - std::vector LazySymbols; + std::vector LazySymbols; llvm::DenseSet Seen; }; Index: ELF/InputFiles.cpp =================================================================== --- ELF/InputFiles.cpp +++ ELF/InputFiles.cpp @@ -564,6 +564,67 @@ return createELFFile(MB); } +void LazyObjectFile::parse() { + for (StringRef Sym : getSymbols()) + LazySymbols.emplace_back(Sym, this->MB); +} + +template std::vector LazyObjectFile::getElfSymbols() { + typedef typename ELFT::Shdr Elf_Shdr; + typedef typename ELFT::Sym Elf_Sym; + typedef typename ELFT::SymRange Elf_Sym_Range; + + const ELFFile Obj = createELFObj(this->MB); + for (const Elf_Shdr &Sec : Obj.sections()) { + if (Sec.sh_type != SHT_SYMTAB) + continue; + Elf_Sym_Range Syms = Obj.symbols(&Sec); + uint32_t FirstNonLocal = Sec.sh_info; + std::vector V; + StringRef StringTable = check(Obj.getStringTableForSymtab(Sec)); + for (const Elf_Sym *I = Syms.begin() + FirstNonLocal, *E = Syms.end(); + I != E; ++I) + V.push_back(check(I->getName(StringTable))); + return V; + } + return {}; +} + +std::vector LazyObjectFile::getBitcodeSymbols() { + LLVMContext Context; + std::unique_ptr Obj = + check(IRObjectFile::create(this->MB, Context)); + std::vector V; + for (const BasicSymbolRef &Sym : Obj->symbols()) { + if (!(Sym.getFlags() & BasicSymbolRef::SF_Global)) + continue; + SmallString<64> Name; + raw_svector_ostream OS(Name); + Sym.printName(OS); + V.push_back(Saver.save(StringRef(Name))); + } + return V; +} + +// Returns a vector of globally-visible symbol names. +std::vector LazyObjectFile::getSymbols() { + using namespace sys::fs; + + StringRef Buf = this->MB.getBuffer(); + if (identify_magic(Buf) == file_magic::bitcode) + return getBitcodeSymbols(); + + std::pair Type = getElfArchType(Buf); + if (Type.first == ELF::ELFCLASS32) { + if (Type.second == ELF::ELFDATA2LSB) + return getElfSymbols(); + return getElfSymbols(); + } + if (Type.second == ELF::ELFDATA2LSB) + return getElfSymbols(); + return getElfSymbols(); +} + template class elf::ELFFileBase; template class elf::ELFFileBase; template class elf::ELFFileBase; Index: ELF/Options.td =================================================================== --- ELF/Options.td +++ ELF/Options.td @@ -48,6 +48,9 @@ def enable_new_dtags : Flag<["--"], "enable-new-dtags">, HelpText<"Enable new dynamic tags">; +def end_lib : Flag<["--"], "end-lib">, + HelpText<"End a library">; + def entry : Separate<["--", "-"], "entry">, MetaVarName<"">, HelpText<"Name of entry point symbol">; @@ -121,6 +124,9 @@ def soname : Joined<["-"], "soname=">, HelpText<"Set DT_SONAME">; +def start_lib : Flag<["--"], "start-lib">, + HelpText<"Start a library">; + def strip_all : Flag<["--"], "strip-all">, HelpText<"Strip all symbols">; Index: ELF/OutputSections.cpp =================================================================== --- ELF/OutputSections.cpp +++ ELF/OutputSections.cpp @@ -1502,7 +1502,8 @@ break; case SymbolBody::UndefinedElfKind: case SymbolBody::UndefinedBitcodeKind: - case SymbolBody::LazyKind: + case SymbolBody::LazyArchiveKind: + case SymbolBody::LazyObjectKind: break; case SymbolBody::DefinedBitcodeKind: llvm_unreachable("should have been replaced"); Index: ELF/SymbolTable.h =================================================================== --- ELF/SymbolTable.h +++ ELF/SymbolTable.h @@ -87,8 +87,9 @@ llvm::DenseSet ComdatGroups; // The symbol table owns all file objects. - std::vector> ArchiveFiles; + std::vector> ArchiveFiles; std::vector>> ObjectFiles; + std::vector> LazyObjectFiles; std::vector>> SharedFiles; std::vector> BitcodeFiles; Index: ELF/SymbolTable.cpp =================================================================== --- ELF/SymbolTable.cpp +++ ELF/SymbolTable.cpp @@ -75,7 +75,7 @@ return; } - // LLVM bitcode file. + // LLVM bitcode file if (auto *F = dyn_cast(FileP)) { BitcodeFiles.emplace_back(cast(File.release())); F->parse(ComdatGroups); @@ -85,7 +85,16 @@ return; } - // .o file + // Lazy object file + if (auto *F = dyn_cast(FileP)) { + LazyObjectFiles.emplace_back(cast(File.release())); + F->parse(); + for (Lazy &Sym : F->getLazySymbols()) + addLazy(&Sym); + return; + } + + // Regular object file auto *F = cast>(FileP); ObjectFiles.emplace_back(cast>(File.release())); F->parse(ComdatGroups); @@ -306,7 +315,7 @@ // Fetch a member file that has the definition for L. // getMember returns nullptr if the member was already read from the library. - if (std::unique_ptr File = L->getMember()) + if (std::unique_ptr File = L->getFile()) addFile(std::move(File)); } Index: ELF/Symbols.h =================================================================== --- ELF/Symbols.h +++ ELF/Symbols.h @@ -59,7 +59,8 @@ DefinedLast = DefinedSyntheticKind, UndefinedElfKind, UndefinedBitcodeKind, - LazyKind + LazyArchiveKind, + LazyObjectKind, }; Kind kind() const { return static_cast(SymbolKind); } @@ -70,7 +71,9 @@ } bool isDefined() const { return SymbolKind <= DefinedLast; } bool isCommon() const { return SymbolKind == DefinedCommonKind; } - bool isLazy() const { return SymbolKind == LazyKind; } + bool isLazy() const { + return SymbolKind == LazyArchiveKind || SymbolKind == LazyObjectKind; + } bool isShared() const { return SymbolKind == SharedKind; } bool isLocal() const { return Binding == llvm::ELF::STB_LOCAL; } bool isUsedInRegularObj() const { return IsUsedInRegularObj; } @@ -337,22 +340,51 @@ // 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(), llvm::ELF::STB_GLOBAL, - llvm::ELF::STV_DEFAULT, /* Type */ 0), - File(F), Sym(S) {} + Lazy(SymbolBody::Kind K, StringRef Name) + : SymbolBody(K, Name, llvm::ELF::STB_GLOBAL, llvm::ELF::STV_DEFAULT, + /* Type */ 0) {} - static bool classof(const SymbolBody *S) { return S->kind() == LazyKind; } + static bool classof(const SymbolBody *S) { return S->isLazy(); } // Returns an object file for this symbol, or a nullptr if the file // was already returned. - std::unique_ptr getMember(); + std::unique_ptr getFile(); +}; + +// LazyArchive symbols represents symbols in archive files. +class LazyArchive : public Lazy { +public: + LazyArchive(ArchiveFile *F, const llvm::object::Archive::Symbol S) + : Lazy(LazyArchiveKind, S.getName()), File(F), Sym(S) {} + + static bool classof(const SymbolBody *S) { + return S->kind() == LazyArchiveKind; + } + + std::unique_ptr getFile(); private: ArchiveFile *File; const llvm::object::Archive::Symbol Sym; }; +// LazyObject symbols represents symbols in object files between +// --start-lib and --end-lib options. +class LazyObject : public Lazy { +public: + LazyObject(StringRef Name, MemoryBufferRef M) + : Lazy(LazyObjectKind, Name), MBRef(M) {} + + static bool classof(const SymbolBody *S) { + return S->kind() == LazyObjectKind; + } + + std::unique_ptr getFile(); + +private: + MemoryBufferRef MBRef; +}; + // Some linker-generated symbols need to be created as // DefinedRegular symbols. template struct ElfSym { Index: ELF/Symbols.cpp =================================================================== --- ELF/Symbols.cpp +++ ELF/Symbols.cpp @@ -78,7 +78,8 @@ case SymbolBody::UndefinedElfKind: case SymbolBody::UndefinedBitcodeKind: return 0; - case SymbolBody::LazyKind: + case SymbolBody::LazyArchiveKind: + case SymbolBody::LazyObjectKind: assert(Body.isUsedInRegularObj() && "lazy symbol reached writer"); return 0; case SymbolBody::DefinedBitcodeKind: @@ -294,7 +295,13 @@ : Defined(SymbolBody::DefinedCommonKind, N, Binding, StOther, Type), Alignment(Alignment), Size(Size) {} -std::unique_ptr Lazy::getMember() { +std::unique_ptr Lazy::getFile() { + if (auto *S = dyn_cast(this)) + return S->getFile(); + return cast(this)->getFile(); +} + +std::unique_ptr LazyArchive::getFile() { MemoryBufferRef MBRef = File->getMember(&Sym); // getMember returns an empty buffer if the member was already @@ -304,6 +311,10 @@ return createObjectFile(MBRef, File->getName()); } +std::unique_ptr LazyObject::getFile() { + return createObjectFile(MBRef); +} + // Returns the demangled C++ symbol name for Name. std::string elf::demangle(StringRef Name) { #if !defined(HAVE_CXXABI_H) Index: test/ELF/start-lib.s =================================================================== --- /dev/null +++ test/ELF/start-lib.s @@ -0,0 +1,16 @@ +// REQUIRES: x86 + +// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t1.o +// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux \ +// RUN: %p/Inputs/whole-archive.s -o %t2.o + +// RUN: ld.lld -o %t3 %t1.o %t2.o +// RUN: llvm-readobj --symbols %t3 | FileCheck --check-prefix=ADDED %s +// ADDED: Name: _bar + +// RUN: ld.lld -o %t3 %t1.o --start-lib %t2.o +// RUN: llvm-readobj --symbols %t3 | FileCheck --check-prefix=LIB %s +// LIB-NOT: Name: _bar + +.globl _start +_start: