From f8baa6605674e1a1ff3fd60b750e42291a4442c5 Mon Sep 17 00:00:00 2001 From: Rui Ueyama Date: Thu, 7 Apr 2016 19:24:51 +0000 Subject: [PATCH] ELF: Implement --start-lib and --end-lib start-lib and end-lib are options to link object files in the same semantics as archive files. If an object is in start-lib and end-lib, the object is linked only when the file is needed to resolve undefined symbols. That means, if an object is in start-lib and end-lib, it behaves as if it were in an archive file. In this patch, I introduced a new notion, LazyObjectFile. That is analogous to Archive file type, but that works for a single object file instead of for an archive file. http://reviews.llvm.org/D18814 llvm-svn: 265710 --- lld/ELF/Driver.cpp | 11 ++++++- lld/ELF/Driver.h | 7 ++++- lld/ELF/InputFiles.cpp | 60 ++++++++++++++++++++++++++++++++++++ lld/ELF/InputFiles.h | 43 ++++++++++++++++++++++++-- lld/ELF/Options.td | 6 ++++ lld/ELF/OutputSections.cpp | 3 +- lld/ELF/SymbolTable.cpp | 15 +++++++-- lld/ELF/SymbolTable.h | 1 + lld/ELF/Symbols.cpp | 15 +++++++-- lld/ELF/Symbols.h | 48 ++++++++++++++++++++++++----- lld/test/ELF/lto/Inputs/start-lib.ll | 6 ++++ lld/test/ELF/lto/start-lib.ll | 19 ++++++++++++ lld/test/ELF/start-lib.s | 16 ++++++++++ 13 files changed, 231 insertions(+), 19 deletions(-) create mode 100644 lld/test/ELF/lto/Inputs/start-lib.ll create mode 100644 lld/test/ELF/lto/start-lib.ll create mode 100644 lld/test/ELF/start-lib.s diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp index 93103d9..ab3335b 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(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 0e9aaa9..f562b0f 100644 --- a/lld/ELF/Driver.h +++ b/lld/ELF/Driver.h @@ -33,8 +33,13 @@ private: 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; }; diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp index 3d23fde..6eb2dc65 100644 --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -564,6 +564,66 @@ std::unique_ptr elf::createSharedFile(MemoryBufferRef MB) { 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; + StringRef StringTable = check(Obj.getStringTableForSymtab(Sec)); + std::vector V; + for (const Elf_Sym &Sym : Syms.slice(FirstNonLocal)) + V.push_back(check(Sym.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 (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 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; diff --git a/lld/ELF/InputFiles.h b/lld/ELF/InputFiles.h index 1ed47c8..ca41d16 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> 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 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 @@ public: // (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; }; diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td index 31ccf9b..8b72fca 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<"">, 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 cb82e22..f79a1d3a 100644 --- a/lld/ELF/OutputSections.cpp +++ b/lld/ELF/OutputSections.cpp @@ -1501,7 +1501,8 @@ SymbolTableSection::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 ba91b10..2348f9a 100644 --- a/lld/ELF/SymbolTable.cpp +++ b/lld/ELF/SymbolTable.cpp @@ -75,7 +75,7 @@ void SymbolTable::addFile(std::unique_ptr File) { 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 @@ void SymbolTable::addFile(std::unique_ptr File) { 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 @@ void SymbolTable::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 File = L->getMember()) + if (std::unique_ptr File = L->getFile()) addFile(std::move(File)); } diff --git a/lld/ELF/SymbolTable.h b/lld/ELF/SymbolTable.h index b80e7df..34ab465 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> ArchiveFiles; std::vector>> ObjectFiles; + std::vector> LazyObjectFiles; std::vector>> SharedFiles; std::vector> BitcodeFiles; diff --git a/lld/ELF/Symbols.cpp b/lld/ELF/Symbols.cpp index 5ce5738..e699640 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 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 @@ -311,6 +318,10 @@ std::unique_ptr Lazy::getMember() { 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) diff --git a/lld/ELF/Symbols.h b/lld/ELF/Symbols.h index e4dcb1c..c3d5635 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(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 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 { 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 0000000..c1dbdaa --- /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 0000000..1af776a --- /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 0000000..caec1a5 --- /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: -- 2.7.4