diff options
-rw-r--r-- | lld/ELF/Driver.cpp | 11 | ||||
-rw-r--r-- | lld/ELF/Driver.h | 7 | ||||
-rw-r--r-- | lld/ELF/InputFiles.cpp | 60 | ||||
-rw-r--r-- | lld/ELF/InputFiles.h | 43 | ||||
-rw-r--r-- | lld/ELF/Options.td | 6 | ||||
-rw-r--r-- | lld/ELF/OutputSections.cpp | 3 | ||||
-rw-r--r-- | lld/ELF/SymbolTable.cpp | 15 | ||||
-rw-r--r-- | lld/ELF/SymbolTable.h | 1 | ||||
-rw-r--r-- | lld/ELF/Symbols.cpp | 15 | ||||
-rw-r--r-- | lld/ELF/Symbols.h | 48 | ||||
-rw-r--r-- | lld/test/ELF/lto/Inputs/start-lib.ll | 6 | ||||
-rw-r--r-- | lld/test/ELF/lto/start-lib.ll | 19 | ||||
-rw-r--r-- | lld/test/ELF/start-lib.s | 16 |
13 files changed, 231 insertions, 19 deletions
diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp index 93103d9e88b..ab3335bb900 100644 --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -129,7 +129,10 @@ void LinkerDriver::addFile(StringRef Path) { Files.push_back(createSharedFile(MBRef)); return; default: - Files.push_back(createObjectFile(MBRef)); + if (InLib) + Files.push_back(make_unique<LazyObjectFile>(MBRef)); + else + Files.push_back(createObjectFile(MBRef)); } } @@ -359,6 +362,12 @@ void LinkerDriver::createFiles(opt::InputArgList &Args) { case OPT_no_whole_archive: WholeArchive = false; break; + case OPT_start_lib: + InLib = true; + break; + case OPT_end_lib: + InLib = false; + break; } } diff --git a/lld/ELF/Driver.h b/lld/ELF/Driver.h index 0e9aaa9d6f4..f562b0fa496 100644 --- a/lld/ELF/Driver.h +++ b/lld/ELF/Driver.h @@ -33,8 +33,13 @@ private: void createFiles(llvm::opt::InputArgList &Args); template <class ELFT> 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<std::unique_ptr<InputFile>> Files; std::vector<std::unique_ptr<MemoryBuffer>> OwningMBs; }; diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp index 3d23fde0577..6eb2dc65e6c 100644 --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -564,6 +564,66 @@ std::unique_ptr<InputFile> elf::createSharedFile(MemoryBufferRef MB) { return createELFFile<SharedFile>(MB); } +void LazyObjectFile::parse() { + for (StringRef Sym : getSymbols()) + LazySymbols.emplace_back(Sym, this->MB); +} + +template <class ELFT> std::vector<StringRef> LazyObjectFile::getElfSymbols() { + typedef typename ELFT::Shdr Elf_Shdr; + typedef typename ELFT::Sym Elf_Sym; + typedef typename ELFT::SymRange Elf_Sym_Range; + + const ELFFile<ELFT> Obj = createELFObj<ELFT>(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; + StringRef StringTable = check(Obj.getStringTableForSymtab(Sec)); + std::vector<StringRef> V; + for (const Elf_Sym &Sym : Syms.slice(FirstNonLocal)) + V.push_back(check(Sym.getName(StringTable))); + return V; + } + return {}; +} + +std::vector<StringRef> LazyObjectFile::getBitcodeSymbols() { + LLVMContext Context; + std::unique_ptr<IRObjectFile> Obj = + check(IRObjectFile::create(this->MB, Context)); + std::vector<StringRef> V; + for (const BasicSymbolRef &Sym : Obj->symbols()) { + if (BitcodeFile::shouldSkip(Sym)) + 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<StringRef> LazyObjectFile::getSymbols() { + using namespace sys::fs; + + StringRef Buf = this->MB.getBuffer(); + if (identify_magic(Buf) == file_magic::bitcode) + return getBitcodeSymbols(); + + std::pair<unsigned char, unsigned char> Type = getElfArchType(Buf); + if (Type.first == ELF::ELFCLASS32) { + if (Type.second == ELF::ELFDATA2LSB) + return getElfSymbols<ELF32LE>(); + return getElfSymbols<ELF32BE>(); + } + if (Type.second == ELF::ELFDATA2LSB) + return getElfSymbols<ELF64LE>(); + return getElfSymbols<ELF64BE>(); +} + template class elf::ELFFileBase<ELF32LE>; template class elf::ELFFileBase<ELF32BE>; template class elf::ELFFileBase<ELF64LE>; diff --git a/lld/ELF/InputFiles.h b/lld/ELF/InputFiles.h index 1ed47c8f3f5..ca41d16392b 100644 --- a/lld/ELF/InputFiles.h +++ b/lld/ELF/InputFiles.h @@ -36,7 +36,14 @@ class SymbolBody; // 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 @@ private: llvm::SpecificBumpPtrAllocator<EHInputSection<ELFT>> 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 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<LazyObject> getLazySymbols() { return LazySymbols; } + +private: + std::vector<StringRef> getSymbols(); + template <class ELFT> std::vector<StringRef> getElfSymbols(); + std::vector<StringRef> getBitcodeSymbols(); + + llvm::BumpPtrAllocator Alloc; + llvm::StringSaver Saver{Alloc}; + std::vector<LazyObject> LazySymbols; +}; + +// An ArchiveFile object represents a .a file. class ArchiveFile : public InputFile { public: explicit ArchiveFile(MemoryBufferRef M) : InputFile(ArchiveKind, M) {} @@ -165,11 +202,11 @@ public: // (So that we don't instantiate same members more than once.) MemoryBufferRef getMember(const Archive::Symbol *Sym); - llvm::MutableArrayRef<Lazy> getLazySymbols() { return LazySymbols; } + llvm::MutableArrayRef<LazyArchive> getLazySymbols() { return LazySymbols; } private: std::unique_ptr<Archive> File; - std::vector<Lazy> LazySymbols; + std::vector<LazyArchive> LazySymbols; llvm::DenseSet<uint64_t> Seen; }; diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td index 31ccf9ba398..8b72fcacf68 100644 --- a/lld/ELF/Options.td +++ b/lld/ELF/Options.td @@ -48,6 +48,9 @@ def eh_frame_hdr : Flag<["--"], "eh-frame-hdr">, 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<"<entry>">, HelpText<"Name of entry point symbol">; @@ -121,6 +124,9 @@ def shared : Flag<["-"], "shared">, 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">; diff --git a/lld/ELF/OutputSections.cpp b/lld/ELF/OutputSections.cpp index cb82e22eec9..f79a1d3a923 100644 --- a/lld/ELF/OutputSections.cpp +++ b/lld/ELF/OutputSections.cpp @@ -1501,7 +1501,8 @@ SymbolTableSection<ELFT>::getOutputSection(SymbolBody *Sym) { 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"); diff --git a/lld/ELF/SymbolTable.cpp b/lld/ELF/SymbolTable.cpp index ba91b10bcac..2348f9a6fb4 100644 --- a/lld/ELF/SymbolTable.cpp +++ b/lld/ELF/SymbolTable.cpp @@ -75,7 +75,7 @@ void SymbolTable<ELFT>::addFile(std::unique_ptr<InputFile> File) { return; } - // LLVM bitcode file. + // LLVM bitcode file if (auto *F = dyn_cast<BitcodeFile>(FileP)) { BitcodeFiles.emplace_back(cast<BitcodeFile>(File.release())); F->parse(ComdatGroups); @@ -85,7 +85,16 @@ void SymbolTable<ELFT>::addFile(std::unique_ptr<InputFile> File) { return; } - // .o file + // Lazy object file + if (auto *F = dyn_cast<LazyObjectFile>(FileP)) { + LazyObjectFiles.emplace_back(cast<LazyObjectFile>(File.release())); + F->parse(); + for (Lazy &Sym : F->getLazySymbols()) + addLazy(&Sym); + return; + } + + // Regular object file auto *F = cast<ObjectFile<ELFT>>(FileP); ObjectFiles.emplace_back(cast<ObjectFile<ELFT>>(File.release())); F->parse(ComdatGroups); @@ -306,7 +315,7 @@ void SymbolTable<ELFT>::addMemberFile(SymbolBody *Undef, Lazy *L) { // 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<InputFile> File = L->getMember()) + if (std::unique_ptr<InputFile> File = L->getFile()) addFile(std::move(File)); } diff --git a/lld/ELF/SymbolTable.h b/lld/ELF/SymbolTable.h index b80e7df18eb..34ab4655615 100644 --- a/lld/ELF/SymbolTable.h +++ b/lld/ELF/SymbolTable.h @@ -89,6 +89,7 @@ private: // The symbol table owns all file objects. std::vector<std::unique_ptr<ArchiveFile>> ArchiveFiles; std::vector<std::unique_ptr<ObjectFile<ELFT>>> ObjectFiles; + std::vector<std::unique_ptr<LazyObjectFile>> LazyObjectFiles; std::vector<std::unique_ptr<SharedFile<ELFT>>> SharedFiles; std::vector<std::unique_ptr<BitcodeFile>> BitcodeFiles; diff --git a/lld/ELF/Symbols.cpp b/lld/ELF/Symbols.cpp index 5ce57386818..e699640f448 100644 --- a/lld/ELF/Symbols.cpp +++ b/lld/ELF/Symbols.cpp @@ -78,7 +78,8 @@ static typename ELFT::uint getSymVA(const SymbolBody &Body, 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: @@ -301,7 +302,13 @@ DefinedCommon::DefinedCommon(StringRef N, uint64_t Size, uint64_t Alignment, : Defined(SymbolBody::DefinedCommonKind, N, Binding, StOther, Type), Alignment(Alignment), Size(Size) {} -std::unique_ptr<InputFile> Lazy::getMember() { +std::unique_ptr<InputFile> Lazy::getFile() { + if (auto *S = dyn_cast<LazyArchive>(this)) + return S->getFile(); + return cast<LazyObject>(this)->getFile(); +} + +std::unique_ptr<InputFile> LazyArchive::getFile() { MemoryBufferRef MBRef = File->getMember(&Sym); // getMember returns an empty buffer if the member was already @@ -311,6 +318,10 @@ std::unique_ptr<InputFile> Lazy::getMember() { return createObjectFile(MBRef, File->getName()); } +std::unique_ptr<InputFile> LazyObject::getFile() { + return createObjectFile(MBRef); +} + // Returns the demangled C++ symbol name for Name. std::string elf::demangle(StringRef Name) { #if !defined(HAVE_CXXABI_H) diff --git a/lld/ELF/Symbols.h b/lld/ELF/Symbols.h index e4dcb1c738f..c3d5635a3bd 100644 --- a/lld/ELF/Symbols.h +++ b/lld/ELF/Symbols.h @@ -59,7 +59,8 @@ public: DefinedLast = DefinedSyntheticKind, UndefinedElfKind, UndefinedBitcodeKind, - LazyKind + LazyArchiveKind, + LazyObjectKind, }; Kind kind() const { return static_cast<Kind>(SymbolKind); } @@ -70,7 +71,9 @@ public: } 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; } @@ -339,22 +342,51 @@ public: // 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<InputFile> getMember(); + std::unique_ptr<InputFile> 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<InputFile> 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<InputFile> getFile(); + +private: + MemoryBufferRef MBRef; +}; + // Some linker-generated symbols need to be created as // DefinedRegular symbols. template <class ELFT> struct ElfSym { diff --git a/lld/test/ELF/lto/Inputs/start-lib.ll b/lld/test/ELF/lto/Inputs/start-lib.ll new file mode 100644 index 00000000000..c1dbdaa07f1 --- /dev/null +++ b/lld/test/ELF/lto/Inputs/start-lib.ll @@ -0,0 +1,6 @@ +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +define void @_bar() { + ret void +} diff --git a/lld/test/ELF/lto/start-lib.ll b/lld/test/ELF/lto/start-lib.ll new file mode 100644 index 00000000000..1af776a9d54 --- /dev/null +++ b/lld/test/ELF/lto/start-lib.ll @@ -0,0 +1,19 @@ +; REQUIRES: x86 +; +; RUN: llvm-as %s -o %t1.o +; RUN: llvm-as %p/Inputs/start-lib.ll -o %t2.o +; +; RUN: ld.lld -m elf_x86_64 -shared -o %t3 %t1.o %t2.o +; RUN: llvm-readobj --symbols %t3 | FileCheck --check-prefix=ADDED %s +; ADDED: Name: _bar +; +; RUN: ld.lld -m elf_x86_64 -shared -o %t3 %t1.o --start-lib %t2.o +; RUN: llvm-readobj --symbols %t3 | FileCheck --check-prefix=LIB %s +; LIB-NOT: Name: _bar + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +define void @_start() { + ret void +} diff --git a/lld/test/ELF/start-lib.s b/lld/test/ELF/start-lib.s new file mode 100644 index 00000000000..caec1a5009c --- /dev/null +++ b/lld/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: |