From fd7569c8e366ceb89698a2a3a1b29c6365a2450e Mon Sep 17 00:00:00 2001 From: Bob Haarman Date: Fri, 30 Aug 2019 16:50:10 +0000 Subject: [PATCH] [lld-link] implement -start-lib and -end-lib Summary: This implements -start-lib and -end-lib flags for lld-link, analogous to the similarly named options in ld.lld. Object files after -start-lib are included in the link only when needed to resolve undefined symbols. The -end-lib flag goes back to the normal behavior of always including object files in the link. This mimics the semantics of static libraries, but without needing to actually create the archive file. Reviewers: ruiu, smeenai, MaskRay Reviewed By: ruiu, MaskRay Subscribers: akhuang, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D66848 llvm-svn: 370487 --- lld/COFF/DebugTypes.cpp | 2 +- lld/COFF/Driver.cpp | 60 +++++++++++++---- lld/COFF/Driver.h | 5 +- lld/COFF/InputFiles.cpp | 62 ++++++++++++++--- lld/COFF/InputFiles.h | 37 ++++++++++- lld/COFF/Options.td | 4 ++ lld/COFF/SymbolTable.cpp | 77 +++++++++++++++------- lld/COFF/SymbolTable.h | 5 +- lld/COFF/Symbols.cpp | 6 +- lld/COFF/Symbols.h | 41 +++++++----- lld/COFF/Writer.cpp | 3 +- lld/test/COFF/Inputs/start-lib1.ll | 13 ++++ lld/test/COFF/Inputs/start-lib2.ll | 9 +++ lld/test/COFF/start-lib-cmd-diagnostics.ll | 19 ++++++ lld/test/COFF/start-lib.ll | 43 ++++++++++++ 15 files changed, 316 insertions(+), 70 deletions(-) create mode 100644 lld/test/COFF/Inputs/start-lib1.ll create mode 100644 lld/test/COFF/Inputs/start-lib2.ll create mode 100644 lld/test/COFF/start-lib-cmd-diagnostics.ll create mode 100644 lld/test/COFF/start-lib.ll diff --git a/lld/COFF/DebugTypes.cpp b/lld/COFF/DebugTypes.cpp index 78c1c78b408d..c41c49cc319c 100644 --- a/lld/COFF/DebugTypes.cpp +++ b/lld/COFF/DebugTypes.cpp @@ -231,7 +231,7 @@ void TypeServerSource::enqueue(const ObjFile *dependentFile, if (!it.second) return; // another OBJ already scheduled this PDB for load - driver->enqueuePath(*p, false); + driver->enqueuePath(*p, false, false); } // Create an instance of TypeServerSource or an error string if the PDB couldn't diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp index 312c161f869f..6e27edbecadf 100644 --- a/lld/COFF/Driver.cpp +++ b/lld/COFF/Driver.cpp @@ -170,7 +170,7 @@ MemoryBufferRef LinkerDriver::takeBuffer(std::unique_ptr mb) { } void LinkerDriver::addBuffer(std::unique_ptr mb, - bool wholeArchive) { + bool wholeArchive, bool lazy) { StringRef filename = mb->getBufferIdentifier(); MemoryBufferRef mbref = takeBuffer(std::move(mb)); @@ -195,11 +195,17 @@ void LinkerDriver::addBuffer(std::unique_ptr mb, symtab->addFile(make(mbref)); break; case file_magic::bitcode: - symtab->addFile(make(mbref, "", 0)); + if (lazy) + symtab->addFile(make(mbref)); + else + symtab->addFile(make(mbref, "", 0)); break; case file_magic::coff_object: case file_magic::coff_import_library: - symtab->addFile(make(mbref)); + if (lazy) + symtab->addFile(make(mbref)); + else + symtab->addFile(make(mbref)); break; case file_magic::pdb: loadTypeServerSource(mbref); @@ -220,7 +226,7 @@ void LinkerDriver::addBuffer(std::unique_ptr mb, } } -void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive) { +void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive, bool lazy) { auto future = std::make_shared>(createFutureForFile(path)); std::string pathStr = path; @@ -240,7 +246,7 @@ void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive) { else error(msg + "; did you mean '" + nearest + "'"); } else - driver->addBuffer(std::move(mbOrErr.first), wholeArchive); + driver->addBuffer(std::move(mbOrErr.first), wholeArchive, lazy); }); } @@ -359,7 +365,7 @@ void LinkerDriver::parseDirectives(InputFile *file) { break; case OPT_defaultlib: if (Optional path = findLib(arg->getValue())) - enqueuePath(*path, false); + enqueuePath(*path, false, false); break; case OPT_entry: config->entry = addUndefined(mangle(arg->getValue())); @@ -1553,19 +1559,45 @@ void LinkerDriver::link(ArrayRef argsArr) { return false; }; - // Create a list of input files. Files can be given as arguments - // for /defaultlib option. - for (auto *arg : args.filtered(OPT_INPUT, OPT_wholearchive_file)) - if (Optional path = findFile(arg->getValue())) - enqueuePath(*path, isWholeArchive(*path)); + // Create a list of input files. These can be given as OPT_INPUT options + // and OPT_wholearchive_file options, and we also need to track OPT_start_lib + // and OPT_end_lib. + bool inLib = false; + for (auto *arg : args) { + switch (arg->getOption().getID()) { + case OPT_end_lib: + if (!inLib) + error("stray " + arg->getSpelling()); + inLib = false; + break; + case OPT_start_lib: + if (inLib) + error("nested " + arg->getSpelling()); + inLib = true; + break; + case OPT_wholearchive_file: + if (Optional path = findFile(arg->getValue())) + enqueuePath(*path, true, inLib); + break; + case OPT_INPUT: + if (Optional path = findFile(arg->getValue())) + enqueuePath(*path, isWholeArchive(*path), inLib); + break; + default: + // Ignore other options. + break; + } + } + // Process files specified as /defaultlib. These should be enequeued after + // other files, which is why they are in a separate loop. for (auto *arg : args.filtered(OPT_defaultlib)) if (Optional path = findLib(arg->getValue())) - enqueuePath(*path, false); + enqueuePath(*path, false, false); // Windows specific -- Create a resource file containing a manifest file. if (config->manifest == Configuration::Embed) - addBuffer(createManifestRes(), false); + addBuffer(createManifestRes(), false, false); // Read all input files given via the command line. run(); @@ -1782,7 +1814,7 @@ void LinkerDriver::link(ArrayRef argsArr) { if (args.hasArg(OPT_include_optional)) { // Handle /includeoptional for (auto *arg : args.filtered(OPT_include_optional)) - if (dyn_cast_or_null(symtab->find(arg->getValue()))) + if (dyn_cast_or_null(symtab->find(arg->getValue()))) addUndefined(arg->getValue()); while (run()); } diff --git a/lld/COFF/Driver.h b/lld/COFF/Driver.h index 42aaea33319b..0fd617595402 100644 --- a/lld/COFF/Driver.h +++ b/lld/COFF/Driver.h @@ -77,7 +77,7 @@ public: MemoryBufferRef takeBuffer(std::unique_ptr mb); - void enqueuePath(StringRef path, bool wholeArchive); + void enqueuePath(StringRef path, bool wholeArchive, bool lazy); private: std::unique_ptr tar; // for /linkrepro @@ -124,7 +124,8 @@ private: StringRef findDefaultEntry(); WindowsSubsystem inferSubsystem(); - void addBuffer(std::unique_ptr mb, bool wholeArchive); + void addBuffer(std::unique_ptr mb, bool wholeArchive, + bool lazy); void addArchiveBuffer(MemoryBufferRef mbref, StringRef symName, StringRef parentName, uint64_t offsetInArchive); diff --git a/lld/COFF/InputFiles.cpp b/lld/COFF/InputFiles.cpp index c24b59deb39e..7bdeee9b3619 100644 --- a/lld/COFF/InputFiles.cpp +++ b/lld/COFF/InputFiles.cpp @@ -73,6 +73,10 @@ static void checkAndSetWeakAlias(SymbolTable *symtab, InputFile *f, } } +static bool ignoredSymbolName(StringRef name) { + return name == "@feat.00" || name == "@comp.id"; +} + ArchiveFile::ArchiveFile(MemoryBufferRef m) : InputFile(ArchiveKind, m) {} void ArchiveFile::parse() { @@ -81,7 +85,7 @@ void ArchiveFile::parse() { // Read the symbol table to construct Lazy objects. for (const Archive::Symbol &sym : file->symbols()) - symtab->addLazy(this, sym); + symtab->addLazyArchive(this, sym); } // Returns a buffer pointing to a member file containing a given symbol. @@ -116,6 +120,49 @@ std::vector getArchiveMembers(Archive *file) { return v; } +void LazyObjFile::fetch() { + if (mb.getBuffer().empty()) + return; + + InputFile *file; + if (isBitcode(mb)) + file = make(mb, "", 0, std::move(symbols)); + else + file = make(mb, std::move(symbols)); + mb = {}; + symtab->addFile(file); +} + +void LazyObjFile::parse() { + if (isBitcode(this->mb)) { + // Bitcode file. + std::unique_ptr obj = + CHECK(lto::InputFile::create(this->mb), this); + for (const lto::InputFile::Symbol &sym : obj->symbols()) { + if (!sym.isUndefined()) + symtab->addLazyObject(this, sym.getName()); + } + return; + } + + // Native object file. + COFFObjectFile *coffObj = + dyn_cast(CHECK(createBinary(mb), this).get()); + uint32_t numSymbols = coffObj->getNumberOfSymbols(); + for (uint32_t i = 0; i < numSymbols; ++i) { + COFFSymbolRef coffSym = check(coffObj->getSymbol(i)); + if (coffSym.isUndefined() || !coffSym.isExternal() || + coffSym.isWeakExternal()) + continue; + StringRef name; + coffObj->getSymbolName(coffSym, name); + if (coffSym.isAbsolute() && ignoredSymbolName(name)) + continue; + symtab->addLazyObject(this, name); + i += coffSym.getNumberOfAuxSymbols(); + } +} + void ObjFile::parse() { // Parse a memory buffer as a COFF file. std::unique_ptr bin = CHECK(createBinary(mb), this); @@ -526,13 +573,11 @@ Optional ObjFile::createDefined( if (sym.isAbsolute()) { StringRef name = getName(); - // Skip special symbols. - if (name == "@comp.id") - return nullptr; - if (name == "@feat.00") { + if (name == "@feat.00") feat00Flags = sym.getValue(); + // Skip special symbols. + if (ignoredSymbolName(name)) return nullptr; - } if (sym.isExternal()) return symtab->addAbsolute(name, sym); @@ -782,8 +827,9 @@ void ImportFile::parse() { } BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName, - uint64_t offsetInArchive) - : InputFile(BitcodeKind, mb) { + uint64_t offsetInArchive, + std::vector &&symbols) + : InputFile(BitcodeKind, mb), symbols(std::move(symbols)) { std::string path = mb.getBufferIdentifier().str(); if (config->thinLTOIndexOnly) path = replaceThinLTOSuffix(mb.getBufferIdentifier()); diff --git a/lld/COFF/InputFiles.h b/lld/COFF/InputFiles.h index d822685dc864..108a4caacd69 100644 --- a/lld/COFF/InputFiles.h +++ b/lld/COFF/InputFiles.h @@ -14,6 +14,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" +#include "llvm/BinaryFormat/Magic.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h" #include "llvm/LTO/LTO.h" #include "llvm/Object/Archive.h" @@ -55,7 +56,13 @@ class TpiSource; // The root class of input files. class InputFile { public: - enum Kind { ArchiveKind, ObjectKind, ImportKind, BitcodeKind }; + enum Kind { + ArchiveKind, + ObjectKind, + LazyObjectKind, + ImportKind, + BitcodeKind + }; Kind kind() const { return fileKind; } virtual ~InputFile() {} @@ -102,10 +109,28 @@ private: llvm::DenseSet seen; }; +// .obj or .o file between -start-lib and -end-lib. +class LazyObjFile : public InputFile { +public: + explicit LazyObjFile(MemoryBufferRef m) : InputFile(LazyObjectKind, m) {} + static bool classof(const InputFile *f) { + return f->kind() == LazyObjectKind; + } + // Makes this object file part of the link. + void fetch(); + // Adds the symbols in this file to the symbol table as LazyObject symbols. + void parse() override; + +private: + std::vector symbols; +}; + // .obj or .o file. This may be a member of an archive file. class ObjFile : public InputFile { public: explicit ObjFile(MemoryBufferRef m) : InputFile(ObjectKind, m) {} + explicit ObjFile(MemoryBufferRef m, std::vector &&symbols) + : InputFile(ObjectKind, m), symbols(std::move(symbols)) {} static bool classof(const InputFile *f) { return f->kind() == ObjectKind; } void parse() override; MachineTypes getMachineType() override; @@ -301,7 +326,11 @@ public: class BitcodeFile : public InputFile { public: BitcodeFile(MemoryBufferRef mb, StringRef archiveName, - uint64_t offsetInArchive); + uint64_t offsetInArchive) + : BitcodeFile(mb, archiveName, offsetInArchive, {}) {} + explicit BitcodeFile(MemoryBufferRef m, StringRef archiveName, + uint64_t offsetInArchive, + std::vector &&symbols); static bool classof(const InputFile *f) { return f->kind() == BitcodeKind; } ArrayRef getSymbols() { return symbols; } MachineTypes getMachineType() override; @@ -314,6 +343,10 @@ private: std::vector symbols; }; +inline bool isBitcode(MemoryBufferRef mb) { + return identify_magic(mb.getBuffer()) == llvm::file_magic::bitcode; +} + std::string replaceThinLTOSuffix(StringRef path); } // namespace coff diff --git a/lld/COFF/Options.td b/lld/COFF/Options.td index 6b9b26923fbc..59e69ea54394 100644 --- a/lld/COFF/Options.td +++ b/lld/COFF/Options.td @@ -162,6 +162,8 @@ def help : F<"help">; def help_q : Flag<["/??", "-??", "/?", "-?"], "">, Alias; // LLD extensions +def end_lib : F<"end-lib">, + HelpText<"End a grouping of objects that should be treated as if they were together in an archive">; def exclude_all_symbols : F<"exclude-all-symbols">; def export_all_symbols : F<"export-all-symbols">; defm demangle : B<"demangle", @@ -176,6 +178,8 @@ def pdb_source_path : P<"pdbsourcepath", "Base path used to make relative source file path absolute in PDB">; def rsp_quoting : Joined<["--"], "rsp-quoting=">, HelpText<"Quoting style for response files, 'windows' (default) or 'posix'">; +def start_lib : F<"start-lib">, + HelpText<"Start a grouping of objects that should be treated as if they were together in an archive">; def thinlto_emit_imports_files : F<"thinlto-emit-imports-files">, HelpText<"Emit .imports files with -thinlto-index-only">; diff --git a/lld/COFF/SymbolTable.cpp b/lld/COFF/SymbolTable.cpp index 865791ca8e13..4b75d667bd4c 100644 --- a/lld/COFF/SymbolTable.cpp +++ b/lld/COFF/SymbolTable.cpp @@ -61,6 +61,24 @@ static void errorOrWarn(const Twine &s) { error(s); } +// Causes the file associated with a lazy symbol to be linked in. +static void forceLazy(Symbol *s) { + s->pendingArchiveLoad = true; + switch (s->kind()) { + case Symbol::Kind::LazyArchiveKind: { + auto *l = cast(s); + l->file->addMember(l->sym); + break; + } + case Symbol::Kind::LazyObjectKind: + cast(s)->file->fetch(); + break; + default: + llvm_unreachable( + "symbol passed to forceLazy is not a LazyArchive or LazyObject"); + } +} + // Returns the symbol in SC whose value is <= Addr that is closest to Addr. // This is generally the global variable or function whose definition contains // Addr. @@ -192,16 +210,15 @@ void SymbolTable::loadMinGWAutomaticImports() { if (name.startswith("__imp_")) continue; - // If we have an undefined symbol, but we have a Lazy representing a - // symbol we could load from file, make sure to load that. - Lazy *l = dyn_cast_or_null(find(("__imp_" + name).str())); - if (!l || l->pendingArchiveLoad) + // If we have an undefined symbol, but we have a lazy symbol we could + // load, load it. + Symbol *l = find(("__imp_" + name).str()); + if (!l || l->pendingArchiveLoad || !l->isLazy()) continue; - log("Loading lazy " + l->getName() + " from " + l->file->getName() + + log("Loading lazy " + l->getName() + " from " + l->getFile()->getName() + " for automatic import"); - l->pendingArchiveLoad = true; - l->file->addMember(l->sym); + forceLazy(l); } } @@ -435,26 +452,22 @@ Symbol *SymbolTable::addUndefined(StringRef name, InputFile *f, Symbol *s; bool wasInserted; std::tie(s, wasInserted) = insert(name, f); - if (wasInserted || (isa(s) && isWeakAlias)) { + if (wasInserted || (s->isLazy() && isWeakAlias)) { replaceSymbol(s, name); return s; } - if (auto *l = dyn_cast(s)) { - if (!s->pendingArchiveLoad) { - s->pendingArchiveLoad = true; - l->file->addMember(l->sym); - } - } + if (s->isLazy()) + forceLazy(s); return s; } -void SymbolTable::addLazy(ArchiveFile *f, const Archive::Symbol &sym) { +void SymbolTable::addLazyArchive(ArchiveFile *f, const Archive::Symbol &sym) { StringRef name = sym.getName(); Symbol *s; bool wasInserted; std::tie(s, wasInserted) = insert(name); if (wasInserted) { - replaceSymbol(s, f, sym); + replaceSymbol(s, f, sym); return; } auto *u = dyn_cast(s); @@ -464,6 +477,21 @@ void SymbolTable::addLazy(ArchiveFile *f, const Archive::Symbol &sym) { f->addMember(sym); } +void SymbolTable::addLazyObject(LazyObjFile *f, StringRef n) { + Symbol *s; + bool wasInserted; + std::tie(s, wasInserted) = insert(n, f); + if (wasInserted) { + replaceSymbol(s, f, n); + return; + } + auto *u = dyn_cast(s); + if (!u || u->weakAlias || s->pendingArchiveLoad) + return; + s->pendingArchiveLoad = true; + f->fetch(); +} + void SymbolTable::reportDuplicate(Symbol *existing, InputFile *newFile) { std::string msg = "duplicate symbol: " + toString(*existing) + " in " + toString(existing->getFile()) + " and in " + @@ -480,7 +508,7 @@ Symbol *SymbolTable::addAbsolute(StringRef n, COFFSymbolRef sym) { bool wasInserted; std::tie(s, wasInserted) = insert(n, nullptr); s->isUsedInRegularObj = true; - if (wasInserted || isa(s) || isa(s)) + if (wasInserted || isa(s) || s->isLazy()) replaceSymbol(s, n, sym); else if (!isa(s)) reportDuplicate(s, nullptr); @@ -492,7 +520,7 @@ Symbol *SymbolTable::addAbsolute(StringRef n, uint64_t va) { bool wasInserted; std::tie(s, wasInserted) = insert(n, nullptr); s->isUsedInRegularObj = true; - if (wasInserted || isa(s) || isa(s)) + if (wasInserted || isa(s) || s->isLazy()) replaceSymbol(s, n, va); else if (!isa(s)) reportDuplicate(s, nullptr); @@ -504,7 +532,7 @@ Symbol *SymbolTable::addSynthetic(StringRef n, Chunk *c) { bool wasInserted; std::tie(s, wasInserted) = insert(n, nullptr); s->isUsedInRegularObj = true; - if (wasInserted || isa(s) || isa(s)) + if (wasInserted || isa(s) || s->isLazy()) replaceSymbol(s, n, c); else if (!isa(s)) reportDuplicate(s, nullptr); @@ -560,7 +588,7 @@ Symbol *SymbolTable::addImportData(StringRef n, ImportFile *f) { bool wasInserted; std::tie(s, wasInserted) = insert(n, nullptr); s->isUsedInRegularObj = true; - if (wasInserted || isa(s) || isa(s)) { + if (wasInserted || isa(s) || s->isLazy()) { replaceSymbol(s, n, f); return s; } @@ -575,7 +603,7 @@ Symbol *SymbolTable::addImportThunk(StringRef name, DefinedImportData *id, bool wasInserted; std::tie(s, wasInserted) = insert(name, nullptr); s->isUsedInRegularObj = true; - if (wasInserted || isa(s) || isa(s)) { + if (wasInserted || isa(s) || s->isLazy()) { replaceSymbol(s, name, id, machine); return s; } @@ -589,9 +617,12 @@ void SymbolTable::addLibcall(StringRef name) { if (!sym) return; - if (Lazy *l = dyn_cast(sym)) { + if (auto *l = dyn_cast(sym)) { MemoryBufferRef mb = l->getMemberBuffer(); - if (identify_magic(mb.getBuffer()) == llvm::file_magic::bitcode) + if (isBitcode(mb)) + addUndefined(sym->getName()); + } else if (LazyObject *o = dyn_cast(sym)) { + if (isBitcode(o->file->mb)) addUndefined(sym->getName()); } } diff --git a/lld/COFF/SymbolTable.h b/lld/COFF/SymbolTable.h index 52eb8901e615..4ae818ef292e 100644 --- a/lld/COFF/SymbolTable.h +++ b/lld/COFF/SymbolTable.h @@ -29,7 +29,7 @@ class Defined; class DefinedAbsolute; class DefinedRegular; class DefinedRelative; -class Lazy; +class LazyArchive; class SectionChunk; class Symbol; @@ -86,7 +86,8 @@ public: Symbol *addAbsolute(StringRef n, uint64_t va); Symbol *addUndefined(StringRef name, InputFile *f, bool isWeakAlias); - void addLazy(ArchiveFile *f, const Archive::Symbol &sym); + void addLazyArchive(ArchiveFile *f, const Archive::Symbol &sym); + void addLazyObject(LazyObjFile *f, StringRef n); Symbol *addAbsolute(StringRef n, COFFSymbolRef s); Symbol *addRegular(InputFile *f, StringRef n, const llvm::object::coff_symbol_generic *s = nullptr, diff --git a/lld/COFF/Symbols.cpp b/lld/COFF/Symbols.cpp index 1af11820a7e6..ccdbc2553470 100644 --- a/lld/COFF/Symbols.cpp +++ b/lld/COFF/Symbols.cpp @@ -61,7 +61,9 @@ StringRef Symbol::getName() { InputFile *Symbol::getFile() { if (auto *sym = dyn_cast(this)) return sym->file; - if (auto *sym = dyn_cast(this)) + if (auto *sym = dyn_cast(this)) + return sym->file; + if (auto *sym = dyn_cast(this)) return sym->file; return nullptr; } @@ -119,7 +121,7 @@ Defined *Undefined::getWeakAlias() { return nullptr; } -MemoryBufferRef Lazy::getMemberBuffer() { +MemoryBufferRef LazyArchive::getMemberBuffer() { Archive::Child c = CHECK(sym.getMember(), "could not get the member for symbol " + toCOFFString(sym)); diff --git a/lld/COFF/Symbols.h b/lld/COFF/Symbols.h index 78c357aa2a58..fd79bd5065b1 100644 --- a/lld/COFF/Symbols.h +++ b/lld/COFF/Symbols.h @@ -59,7 +59,8 @@ public: DefinedSyntheticKind, UndefinedKind, - LazyKind, + LazyArchiveKind, + LazyObjectKind, LastDefinedCOFFKind = DefinedCommonKind, LastDefinedKind = DefinedSyntheticKind, @@ -79,6 +80,10 @@ public: // after calling markLive. bool isLive() const; + bool isLazy() const { + return symbolKind == LazyArchiveKind || symbolKind == LazyObjectKind; + } + protected: friend SymbolTable; explicit Symbol(Kind k, StringRef n = "") @@ -256,26 +261,29 @@ private: // 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 Symbol { +// symbol. If the resolver finds both Undefined and LazyArchive for +// the same name, it will ask the LazyArchive to load a file. +class LazyArchive : public Symbol { public: - Lazy(ArchiveFile *f, const Archive::Symbol s) - : Symbol(LazyKind, s.getName()), file(f), sym(s) {} + LazyArchive(ArchiveFile *f, const Archive::Symbol s) + : Symbol(LazyArchiveKind, s.getName()), file(f), sym(s) {} - static bool classof(const Symbol *s) { return s->kind() == LazyKind; } + static bool classof(const Symbol *s) { return s->kind() == LazyArchiveKind; } MemoryBufferRef getMemberBuffer(); ArchiveFile *file; - -private: - friend SymbolTable; - -private: const Archive::Symbol sym; }; +class LazyObject : public Symbol { +public: + LazyObject(LazyObjFile *f, StringRef n) + : Symbol(LazyObjectKind, n), file(f) {} + static bool classof(const Symbol *s) { return s->kind() == LazyObjectKind; } + LazyObjFile *file; +}; + // Undefined symbols. class Undefined : public Symbol { public: @@ -381,7 +389,8 @@ inline uint64_t Defined::getRVA() { return cast(this)->getRVA(); case DefinedRegularKind: return cast(this)->getRVA(); - case LazyKind: + case LazyArchiveKind: + case LazyObjectKind: case UndefinedKind: llvm_unreachable("Cannot get the address for an undefined symbol."); } @@ -404,7 +413,8 @@ inline Chunk *Defined::getChunk() { return cast(this)->getChunk(); case DefinedCommonKind: return cast(this)->getChunk(); - case LazyKind: + case LazyArchiveKind: + case LazyObjectKind: case UndefinedKind: llvm_unreachable("Cannot get the chunk of an undefined symbol."); } @@ -419,11 +429,12 @@ union SymbolUnion { alignas(DefinedCommon) char b[sizeof(DefinedCommon)]; alignas(DefinedAbsolute) char c[sizeof(DefinedAbsolute)]; alignas(DefinedSynthetic) char d[sizeof(DefinedSynthetic)]; - alignas(Lazy) char e[sizeof(Lazy)]; + alignas(LazyArchive) char e[sizeof(LazyArchive)]; alignas(Undefined) char f[sizeof(Undefined)]; alignas(DefinedImportData) char g[sizeof(DefinedImportData)]; alignas(DefinedImportThunk) char h[sizeof(DefinedImportThunk)]; alignas(DefinedLocalImport) char i[sizeof(DefinedLocalImport)]; + alignas(LazyObject) char j[sizeof(LazyObject)]; }; template diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp index 0adf155e6335..a98af439d042 100644 --- a/lld/COFF/Writer.cpp +++ b/lld/COFF/Writer.cpp @@ -1519,7 +1519,8 @@ static void maybeAddAddressTakenFunction(SymbolRVASet &addressTakenSyms, // Absolute is never code, synthetic generally isn't and usually isn't // determinable. break; - case Symbol::LazyKind: + case Symbol::LazyArchiveKind: + case Symbol::LazyObjectKind: case Symbol::UndefinedKind: // Undefined symbols resolve to zero, so they don't have an RVA. Lazy // symbols shouldn't have relocations. diff --git a/lld/test/COFF/Inputs/start-lib1.ll b/lld/test/COFF/Inputs/start-lib1.ll new file mode 100644 index 000000000000..3d4fe19daab9 --- /dev/null +++ b/lld/test/COFF/Inputs/start-lib1.ll @@ -0,0 +1,13 @@ +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +declare i32 @bar() + +define i32 @foo() { + %1 = call i32 () @bar() + %2 = add i32 %1, 1 + ret i32 %2 +} + +!llvm.linker.options = !{!0} +!0 = !{!"/INCLUDE:foo"} diff --git a/lld/test/COFF/Inputs/start-lib2.ll b/lld/test/COFF/Inputs/start-lib2.ll new file mode 100644 index 000000000000..830ec1d6d191 --- /dev/null +++ b/lld/test/COFF/Inputs/start-lib2.ll @@ -0,0 +1,9 @@ +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +define i32 @bar() { + ret i32 1 +} + +!llvm.linker.options = !{!0} +!0 = !{!"/INCLUDE:bar"} diff --git a/lld/test/COFF/start-lib-cmd-diagnostics.ll b/lld/test/COFF/start-lib-cmd-diagnostics.ll new file mode 100644 index 000000000000..b193ff6bb21e --- /dev/null +++ b/lld/test/COFF/start-lib-cmd-diagnostics.ll @@ -0,0 +1,19 @@ +; REQUIRES: x86 +; +; We need an input file to lld, so create one. +; RUN: llc -filetype=obj %s -o %t.obj + +; RUN: not lld-link %t.obj -end-lib 2>&1 \ +; RUN: | FileCheck --check-prefix=STRAY_END %s +; STRAY_END: stray -end-lib + +; RUN: not lld-link -start-lib -start-lib %t.obj 2>&1 \ +; RUN: | FileCheck --check-prefix=NESTED_START %s +; NESTED_START: nested -start-lib + +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +define void @main() { + ret void +} diff --git a/lld/test/COFF/start-lib.ll b/lld/test/COFF/start-lib.ll new file mode 100644 index 000000000000..c48c167ff1b4 --- /dev/null +++ b/lld/test/COFF/start-lib.ll @@ -0,0 +1,43 @@ +; REQUIRES: x86 +; +; RUN: llc -filetype=obj %s -o %t.obj +; RUN: llc -filetype=obj %p/Inputs/start-lib1.ll -o %t1.obj +; RUN: llc -filetype=obj %p/Inputs/start-lib2.ll -o %t2.obj +; RUN: opt -thinlto-bc %s -o %t.bc +; RUN: opt -thinlto-bc %p/Inputs/start-lib1.ll -o %t1.bc +; RUN: opt -thinlto-bc %p/Inputs/start-lib2.ll -o %t2.bc +; +; RUN: lld-link -out:%t1.exe -entry:main -opt:noref -lldmap:%t1.map \ +; RUN: %t.obj %t1.obj %t2.obj +; RUN: FileCheck --check-prefix=TEST1 %s < %t1.map +; RUN: lld-link -out:%t1.exe -entry:main -opt:noref -lldmap:%t1.thinlto.map \ +; RUN: %t.bc %t1.bc %t2.bc +; RUN: FileCheck --check-prefix=TEST1 %s < %t1.thinlto.map +; TEST1: foo +; TEST1: bar +; +; RUN: lld-link -out:%t2.exe -entry:main -opt:noref -lldmap:%t2.map \ +; RUN: %t.obj -start-lib %t1.obj -end-lib %t2.obj +; RUN: FileCheck --check-prefix=TEST2 %s < %t2.map +; RUN: lld-link -out:%t2.exe -entry:main -opt:noref -lldmap:%t2.thinlto.map \ +; RUN: %t.bc -start-lib %t1.bc -end-lib %t2.bc +; RUN: FileCheck --check-prefix=TEST2 %s < %t2.thinlto.map +; TEST2-NOT: Name: foo +; TEST2: bar +; TEST2-NOT: Name: foo +; +; RUN: lld-link -out:%t3.exe -entry:main -opt:noref -lldmap:%t3.map \ +; RUN: %t.obj -start-lib %t1.obj %t2.obj +; RUN: FileCheck --check-prefix=TEST3 %s < %t3.map +; RUN: lld-link -out:%t3.exe -entry:main -opt:noref -lldmap:%t3.thinlto.map \ +; RUN: %t.bc -start-lib %t1.bc %t2.bc +; RUN: FileCheck --check-prefix=TEST3 %s < %t3.thinlto.map +; TEST3-NOT: foo +; TEST3-NOT: bar + +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +define void @main() { + ret void +}