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));
}
}
case OPT_no_whole_archive:
WholeArchive = false;
break;
+ case OPT_start_lib:
+ InLib = true;
+ break;
+ case OPT_end_lib:
+ InLib = false;
+ break;
}
}
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;
};
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>;
// 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(); }
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) {}
// (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;
};
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">;
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">;
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");
return;
}
- // LLVM bitcode file.
+ // LLVM bitcode file
if (auto *F = dyn_cast<BitcodeFile>(FileP)) {
BitcodeFiles.emplace_back(cast<BitcodeFile>(File.release()));
F->parse(ComdatGroups);
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);
// 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));
}
// 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;
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:
: 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
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)
DefinedLast = DefinedSyntheticKind,
UndefinedElfKind,
UndefinedBitcodeKind,
- LazyKind
+ LazyArchiveKind,
+ LazyObjectKind,
};
Kind kind() const { return static_cast<Kind>(SymbolKind); }
}
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; }
// 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 {
--- /dev/null
+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
+}
--- /dev/null
+; 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
+}
--- /dev/null
+// 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: