[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
This commit is contained in:
Bob Haarman 2019-08-30 16:50:10 +00:00
parent b8a3564975
commit fd7569c8e3
15 changed files with 316 additions and 70 deletions

View File

@ -231,7 +231,7 @@ void TypeServerSource::enqueue(const ObjFile *dependentFile,
if (!it.second) if (!it.second)
return; // another OBJ already scheduled this PDB for load 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 // Create an instance of TypeServerSource or an error string if the PDB couldn't

View File

@ -170,7 +170,7 @@ MemoryBufferRef LinkerDriver::takeBuffer(std::unique_ptr<MemoryBuffer> mb) {
} }
void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb, void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb,
bool wholeArchive) { bool wholeArchive, bool lazy) {
StringRef filename = mb->getBufferIdentifier(); StringRef filename = mb->getBufferIdentifier();
MemoryBufferRef mbref = takeBuffer(std::move(mb)); MemoryBufferRef mbref = takeBuffer(std::move(mb));
@ -195,11 +195,17 @@ void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb,
symtab->addFile(make<ArchiveFile>(mbref)); symtab->addFile(make<ArchiveFile>(mbref));
break; break;
case file_magic::bitcode: case file_magic::bitcode:
symtab->addFile(make<BitcodeFile>(mbref, "", 0)); if (lazy)
symtab->addFile(make<LazyObjFile>(mbref));
else
symtab->addFile(make<BitcodeFile>(mbref, "", 0));
break; break;
case file_magic::coff_object: case file_magic::coff_object:
case file_magic::coff_import_library: case file_magic::coff_import_library:
symtab->addFile(make<ObjFile>(mbref)); if (lazy)
symtab->addFile(make<LazyObjFile>(mbref));
else
symtab->addFile(make<ObjFile>(mbref));
break; break;
case file_magic::pdb: case file_magic::pdb:
loadTypeServerSource(mbref); loadTypeServerSource(mbref);
@ -220,7 +226,7 @@ void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb,
} }
} }
void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive) { void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive, bool lazy) {
auto future = auto future =
std::make_shared<std::future<MBErrPair>>(createFutureForFile(path)); std::make_shared<std::future<MBErrPair>>(createFutureForFile(path));
std::string pathStr = path; std::string pathStr = path;
@ -240,7 +246,7 @@ void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive) {
else else
error(msg + "; did you mean '" + nearest + "'"); error(msg + "; did you mean '" + nearest + "'");
} else } 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; break;
case OPT_defaultlib: case OPT_defaultlib:
if (Optional<StringRef> path = findLib(arg->getValue())) if (Optional<StringRef> path = findLib(arg->getValue()))
enqueuePath(*path, false); enqueuePath(*path, false, false);
break; break;
case OPT_entry: case OPT_entry:
config->entry = addUndefined(mangle(arg->getValue())); config->entry = addUndefined(mangle(arg->getValue()));
@ -1553,19 +1559,45 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
return false; return false;
}; };
// Create a list of input files. Files can be given as arguments // Create a list of input files. These can be given as OPT_INPUT options
// for /defaultlib option. // and OPT_wholearchive_file options, and we also need to track OPT_start_lib
for (auto *arg : args.filtered(OPT_INPUT, OPT_wholearchive_file)) // and OPT_end_lib.
if (Optional<StringRef> path = findFile(arg->getValue())) bool inLib = false;
enqueuePath(*path, isWholeArchive(*path)); 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<StringRef> path = findFile(arg->getValue()))
enqueuePath(*path, true, inLib);
break;
case OPT_INPUT:
if (Optional<StringRef> 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)) for (auto *arg : args.filtered(OPT_defaultlib))
if (Optional<StringRef> path = findLib(arg->getValue())) if (Optional<StringRef> path = findLib(arg->getValue()))
enqueuePath(*path, false); enqueuePath(*path, false, false);
// Windows specific -- Create a resource file containing a manifest file. // Windows specific -- Create a resource file containing a manifest file.
if (config->manifest == Configuration::Embed) if (config->manifest == Configuration::Embed)
addBuffer(createManifestRes(), false); addBuffer(createManifestRes(), false, false);
// Read all input files given via the command line. // Read all input files given via the command line.
run(); run();
@ -1782,7 +1814,7 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
if (args.hasArg(OPT_include_optional)) { if (args.hasArg(OPT_include_optional)) {
// Handle /includeoptional // Handle /includeoptional
for (auto *arg : args.filtered(OPT_include_optional)) for (auto *arg : args.filtered(OPT_include_optional))
if (dyn_cast_or_null<Lazy>(symtab->find(arg->getValue()))) if (dyn_cast_or_null<LazyArchive>(symtab->find(arg->getValue())))
addUndefined(arg->getValue()); addUndefined(arg->getValue());
while (run()); while (run());
} }

View File

@ -77,7 +77,7 @@ public:
MemoryBufferRef takeBuffer(std::unique_ptr<MemoryBuffer> mb); MemoryBufferRef takeBuffer(std::unique_ptr<MemoryBuffer> mb);
void enqueuePath(StringRef path, bool wholeArchive); void enqueuePath(StringRef path, bool wholeArchive, bool lazy);
private: private:
std::unique_ptr<llvm::TarWriter> tar; // for /linkrepro std::unique_ptr<llvm::TarWriter> tar; // for /linkrepro
@ -124,7 +124,8 @@ private:
StringRef findDefaultEntry(); StringRef findDefaultEntry();
WindowsSubsystem inferSubsystem(); WindowsSubsystem inferSubsystem();
void addBuffer(std::unique_ptr<MemoryBuffer> mb, bool wholeArchive); void addBuffer(std::unique_ptr<MemoryBuffer> mb, bool wholeArchive,
bool lazy);
void addArchiveBuffer(MemoryBufferRef mbref, StringRef symName, void addArchiveBuffer(MemoryBufferRef mbref, StringRef symName,
StringRef parentName, uint64_t offsetInArchive); StringRef parentName, uint64_t offsetInArchive);

View File

@ -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) {} ArchiveFile::ArchiveFile(MemoryBufferRef m) : InputFile(ArchiveKind, m) {}
void ArchiveFile::parse() { void ArchiveFile::parse() {
@ -81,7 +85,7 @@ void ArchiveFile::parse() {
// Read the symbol table to construct Lazy objects. // Read the symbol table to construct Lazy objects.
for (const Archive::Symbol &sym : file->symbols()) 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. // Returns a buffer pointing to a member file containing a given symbol.
@ -116,6 +120,49 @@ std::vector<MemoryBufferRef> getArchiveMembers(Archive *file) {
return v; return v;
} }
void LazyObjFile::fetch() {
if (mb.getBuffer().empty())
return;
InputFile *file;
if (isBitcode(mb))
file = make<BitcodeFile>(mb, "", 0, std::move(symbols));
else
file = make<ObjFile>(mb, std::move(symbols));
mb = {};
symtab->addFile(file);
}
void LazyObjFile::parse() {
if (isBitcode(this->mb)) {
// Bitcode file.
std::unique_ptr<lto::InputFile> 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<COFFObjectFile>(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() { void ObjFile::parse() {
// Parse a memory buffer as a COFF file. // Parse a memory buffer as a COFF file.
std::unique_ptr<Binary> bin = CHECK(createBinary(mb), this); std::unique_ptr<Binary> bin = CHECK(createBinary(mb), this);
@ -526,13 +573,11 @@ Optional<Symbol *> ObjFile::createDefined(
if (sym.isAbsolute()) { if (sym.isAbsolute()) {
StringRef name = getName(); StringRef name = getName();
// Skip special symbols. if (name == "@feat.00")
if (name == "@comp.id")
return nullptr;
if (name == "@feat.00") {
feat00Flags = sym.getValue(); feat00Flags = sym.getValue();
// Skip special symbols.
if (ignoredSymbolName(name))
return nullptr; return nullptr;
}
if (sym.isExternal()) if (sym.isExternal())
return symtab->addAbsolute(name, sym); return symtab->addAbsolute(name, sym);
@ -782,8 +827,9 @@ void ImportFile::parse() {
} }
BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName, BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName,
uint64_t offsetInArchive) uint64_t offsetInArchive,
: InputFile(BitcodeKind, mb) { std::vector<Symbol *> &&symbols)
: InputFile(BitcodeKind, mb), symbols(std::move(symbols)) {
std::string path = mb.getBufferIdentifier().str(); std::string path = mb.getBufferIdentifier().str();
if (config->thinLTOIndexOnly) if (config->thinLTOIndexOnly)
path = replaceThinLTOSuffix(mb.getBufferIdentifier()); path = replaceThinLTOSuffix(mb.getBufferIdentifier());

View File

@ -14,6 +14,7 @@
#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/DenseSet.h"
#include "llvm/BinaryFormat/Magic.h"
#include "llvm/DebugInfo/CodeView/TypeRecord.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h"
#include "llvm/LTO/LTO.h" #include "llvm/LTO/LTO.h"
#include "llvm/Object/Archive.h" #include "llvm/Object/Archive.h"
@ -55,7 +56,13 @@ class TpiSource;
// The root class of input files. // The root class of input files.
class InputFile { class InputFile {
public: public:
enum Kind { ArchiveKind, ObjectKind, ImportKind, BitcodeKind }; enum Kind {
ArchiveKind,
ObjectKind,
LazyObjectKind,
ImportKind,
BitcodeKind
};
Kind kind() const { return fileKind; } Kind kind() const { return fileKind; }
virtual ~InputFile() {} virtual ~InputFile() {}
@ -102,10 +109,28 @@ private:
llvm::DenseSet<uint64_t> seen; llvm::DenseSet<uint64_t> 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<Symbol *> symbols;
};
// .obj or .o file. This may be a member of an archive file. // .obj or .o file. This may be a member of an archive file.
class ObjFile : public InputFile { class ObjFile : public InputFile {
public: public:
explicit ObjFile(MemoryBufferRef m) : InputFile(ObjectKind, m) {} explicit ObjFile(MemoryBufferRef m) : InputFile(ObjectKind, m) {}
explicit ObjFile(MemoryBufferRef m, std::vector<Symbol *> &&symbols)
: InputFile(ObjectKind, m), symbols(std::move(symbols)) {}
static bool classof(const InputFile *f) { return f->kind() == ObjectKind; } static bool classof(const InputFile *f) { return f->kind() == ObjectKind; }
void parse() override; void parse() override;
MachineTypes getMachineType() override; MachineTypes getMachineType() override;
@ -301,7 +326,11 @@ public:
class BitcodeFile : public InputFile { class BitcodeFile : public InputFile {
public: public:
BitcodeFile(MemoryBufferRef mb, StringRef archiveName, 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<Symbol *> &&symbols);
static bool classof(const InputFile *f) { return f->kind() == BitcodeKind; } static bool classof(const InputFile *f) { return f->kind() == BitcodeKind; }
ArrayRef<Symbol *> getSymbols() { return symbols; } ArrayRef<Symbol *> getSymbols() { return symbols; }
MachineTypes getMachineType() override; MachineTypes getMachineType() override;
@ -314,6 +343,10 @@ private:
std::vector<Symbol *> symbols; std::vector<Symbol *> symbols;
}; };
inline bool isBitcode(MemoryBufferRef mb) {
return identify_magic(mb.getBuffer()) == llvm::file_magic::bitcode;
}
std::string replaceThinLTOSuffix(StringRef path); std::string replaceThinLTOSuffix(StringRef path);
} // namespace coff } // namespace coff

View File

@ -162,6 +162,8 @@ def help : F<"help">;
def help_q : Flag<["/??", "-??", "/?", "-?"], "">, Alias<help>; def help_q : Flag<["/??", "-??", "/?", "-?"], "">, Alias<help>;
// LLD extensions // 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 exclude_all_symbols : F<"exclude-all-symbols">;
def export_all_symbols : F<"export-all-symbols">; def export_all_symbols : F<"export-all-symbols">;
defm demangle : B<"demangle", 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">; "Base path used to make relative source file path absolute in PDB">;
def rsp_quoting : Joined<["--"], "rsp-quoting=">, def rsp_quoting : Joined<["--"], "rsp-quoting=">,
HelpText<"Quoting style for response files, 'windows' (default) or 'posix'">; 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 : def thinlto_emit_imports_files :
F<"thinlto-emit-imports-files">, F<"thinlto-emit-imports-files">,
HelpText<"Emit .imports files with -thinlto-index-only">; HelpText<"Emit .imports files with -thinlto-index-only">;

View File

@ -61,6 +61,24 @@ static void errorOrWarn(const Twine &s) {
error(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<LazyArchive>(s);
l->file->addMember(l->sym);
break;
}
case Symbol::Kind::LazyObjectKind:
cast<LazyObject>(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. // 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 // This is generally the global variable or function whose definition contains
// Addr. // Addr.
@ -192,16 +210,15 @@ void SymbolTable::loadMinGWAutomaticImports() {
if (name.startswith("__imp_")) if (name.startswith("__imp_"))
continue; continue;
// If we have an undefined symbol, but we have a Lazy representing a // If we have an undefined symbol, but we have a lazy symbol we could
// symbol we could load from file, make sure to load that. // load, load it.
Lazy *l = dyn_cast_or_null<Lazy>(find(("__imp_" + name).str())); Symbol *l = find(("__imp_" + name).str());
if (!l || l->pendingArchiveLoad) if (!l || l->pendingArchiveLoad || !l->isLazy())
continue; continue;
log("Loading lazy " + l->getName() + " from " + l->file->getName() + log("Loading lazy " + l->getName() + " from " + l->getFile()->getName() +
" for automatic import"); " for automatic import");
l->pendingArchiveLoad = true; forceLazy(l);
l->file->addMember(l->sym);
} }
} }
@ -435,26 +452,22 @@ Symbol *SymbolTable::addUndefined(StringRef name, InputFile *f,
Symbol *s; Symbol *s;
bool wasInserted; bool wasInserted;
std::tie(s, wasInserted) = insert(name, f); std::tie(s, wasInserted) = insert(name, f);
if (wasInserted || (isa<Lazy>(s) && isWeakAlias)) { if (wasInserted || (s->isLazy() && isWeakAlias)) {
replaceSymbol<Undefined>(s, name); replaceSymbol<Undefined>(s, name);
return s; return s;
} }
if (auto *l = dyn_cast<Lazy>(s)) { if (s->isLazy())
if (!s->pendingArchiveLoad) { forceLazy(s);
s->pendingArchiveLoad = true;
l->file->addMember(l->sym);
}
}
return 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(); StringRef name = sym.getName();
Symbol *s; Symbol *s;
bool wasInserted; bool wasInserted;
std::tie(s, wasInserted) = insert(name); std::tie(s, wasInserted) = insert(name);
if (wasInserted) { if (wasInserted) {
replaceSymbol<Lazy>(s, f, sym); replaceSymbol<LazyArchive>(s, f, sym);
return; return;
} }
auto *u = dyn_cast<Undefined>(s); auto *u = dyn_cast<Undefined>(s);
@ -464,6 +477,21 @@ void SymbolTable::addLazy(ArchiveFile *f, const Archive::Symbol &sym) {
f->addMember(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<LazyObject>(s, f, n);
return;
}
auto *u = dyn_cast<Undefined>(s);
if (!u || u->weakAlias || s->pendingArchiveLoad)
return;
s->pendingArchiveLoad = true;
f->fetch();
}
void SymbolTable::reportDuplicate(Symbol *existing, InputFile *newFile) { void SymbolTable::reportDuplicate(Symbol *existing, InputFile *newFile) {
std::string msg = "duplicate symbol: " + toString(*existing) + " in " + std::string msg = "duplicate symbol: " + toString(*existing) + " in " +
toString(existing->getFile()) + " and in " + toString(existing->getFile()) + " and in " +
@ -480,7 +508,7 @@ Symbol *SymbolTable::addAbsolute(StringRef n, COFFSymbolRef sym) {
bool wasInserted; bool wasInserted;
std::tie(s, wasInserted) = insert(n, nullptr); std::tie(s, wasInserted) = insert(n, nullptr);
s->isUsedInRegularObj = true; s->isUsedInRegularObj = true;
if (wasInserted || isa<Undefined>(s) || isa<Lazy>(s)) if (wasInserted || isa<Undefined>(s) || s->isLazy())
replaceSymbol<DefinedAbsolute>(s, n, sym); replaceSymbol<DefinedAbsolute>(s, n, sym);
else if (!isa<DefinedCOFF>(s)) else if (!isa<DefinedCOFF>(s))
reportDuplicate(s, nullptr); reportDuplicate(s, nullptr);
@ -492,7 +520,7 @@ Symbol *SymbolTable::addAbsolute(StringRef n, uint64_t va) {
bool wasInserted; bool wasInserted;
std::tie(s, wasInserted) = insert(n, nullptr); std::tie(s, wasInserted) = insert(n, nullptr);
s->isUsedInRegularObj = true; s->isUsedInRegularObj = true;
if (wasInserted || isa<Undefined>(s) || isa<Lazy>(s)) if (wasInserted || isa<Undefined>(s) || s->isLazy())
replaceSymbol<DefinedAbsolute>(s, n, va); replaceSymbol<DefinedAbsolute>(s, n, va);
else if (!isa<DefinedCOFF>(s)) else if (!isa<DefinedCOFF>(s))
reportDuplicate(s, nullptr); reportDuplicate(s, nullptr);
@ -504,7 +532,7 @@ Symbol *SymbolTable::addSynthetic(StringRef n, Chunk *c) {
bool wasInserted; bool wasInserted;
std::tie(s, wasInserted) = insert(n, nullptr); std::tie(s, wasInserted) = insert(n, nullptr);
s->isUsedInRegularObj = true; s->isUsedInRegularObj = true;
if (wasInserted || isa<Undefined>(s) || isa<Lazy>(s)) if (wasInserted || isa<Undefined>(s) || s->isLazy())
replaceSymbol<DefinedSynthetic>(s, n, c); replaceSymbol<DefinedSynthetic>(s, n, c);
else if (!isa<DefinedCOFF>(s)) else if (!isa<DefinedCOFF>(s))
reportDuplicate(s, nullptr); reportDuplicate(s, nullptr);
@ -560,7 +588,7 @@ Symbol *SymbolTable::addImportData(StringRef n, ImportFile *f) {
bool wasInserted; bool wasInserted;
std::tie(s, wasInserted) = insert(n, nullptr); std::tie(s, wasInserted) = insert(n, nullptr);
s->isUsedInRegularObj = true; s->isUsedInRegularObj = true;
if (wasInserted || isa<Undefined>(s) || isa<Lazy>(s)) { if (wasInserted || isa<Undefined>(s) || s->isLazy()) {
replaceSymbol<DefinedImportData>(s, n, f); replaceSymbol<DefinedImportData>(s, n, f);
return s; return s;
} }
@ -575,7 +603,7 @@ Symbol *SymbolTable::addImportThunk(StringRef name, DefinedImportData *id,
bool wasInserted; bool wasInserted;
std::tie(s, wasInserted) = insert(name, nullptr); std::tie(s, wasInserted) = insert(name, nullptr);
s->isUsedInRegularObj = true; s->isUsedInRegularObj = true;
if (wasInserted || isa<Undefined>(s) || isa<Lazy>(s)) { if (wasInserted || isa<Undefined>(s) || s->isLazy()) {
replaceSymbol<DefinedImportThunk>(s, name, id, machine); replaceSymbol<DefinedImportThunk>(s, name, id, machine);
return s; return s;
} }
@ -589,9 +617,12 @@ void SymbolTable::addLibcall(StringRef name) {
if (!sym) if (!sym)
return; return;
if (Lazy *l = dyn_cast<Lazy>(sym)) { if (auto *l = dyn_cast<LazyArchive>(sym)) {
MemoryBufferRef mb = l->getMemberBuffer(); 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<LazyObject>(sym)) {
if (isBitcode(o->file->mb))
addUndefined(sym->getName()); addUndefined(sym->getName());
} }
} }

View File

@ -29,7 +29,7 @@ class Defined;
class DefinedAbsolute; class DefinedAbsolute;
class DefinedRegular; class DefinedRegular;
class DefinedRelative; class DefinedRelative;
class Lazy; class LazyArchive;
class SectionChunk; class SectionChunk;
class Symbol; class Symbol;
@ -86,7 +86,8 @@ public:
Symbol *addAbsolute(StringRef n, uint64_t va); Symbol *addAbsolute(StringRef n, uint64_t va);
Symbol *addUndefined(StringRef name, InputFile *f, bool isWeakAlias); 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 *addAbsolute(StringRef n, COFFSymbolRef s);
Symbol *addRegular(InputFile *f, StringRef n, Symbol *addRegular(InputFile *f, StringRef n,
const llvm::object::coff_symbol_generic *s = nullptr, const llvm::object::coff_symbol_generic *s = nullptr,

View File

@ -61,7 +61,9 @@ StringRef Symbol::getName() {
InputFile *Symbol::getFile() { InputFile *Symbol::getFile() {
if (auto *sym = dyn_cast<DefinedCOFF>(this)) if (auto *sym = dyn_cast<DefinedCOFF>(this))
return sym->file; return sym->file;
if (auto *sym = dyn_cast<Lazy>(this)) if (auto *sym = dyn_cast<LazyArchive>(this))
return sym->file;
if (auto *sym = dyn_cast<LazyObject>(this))
return sym->file; return sym->file;
return nullptr; return nullptr;
} }
@ -119,7 +121,7 @@ Defined *Undefined::getWeakAlias() {
return nullptr; return nullptr;
} }
MemoryBufferRef Lazy::getMemberBuffer() { MemoryBufferRef LazyArchive::getMemberBuffer() {
Archive::Child c = Archive::Child c =
CHECK(sym.getMember(), CHECK(sym.getMember(),
"could not get the member for symbol " + toCOFFString(sym)); "could not get the member for symbol " + toCOFFString(sym));

View File

@ -59,7 +59,8 @@ public:
DefinedSyntheticKind, DefinedSyntheticKind,
UndefinedKind, UndefinedKind,
LazyKind, LazyArchiveKind,
LazyObjectKind,
LastDefinedCOFFKind = DefinedCommonKind, LastDefinedCOFFKind = DefinedCommonKind,
LastDefinedKind = DefinedSyntheticKind, LastDefinedKind = DefinedSyntheticKind,
@ -79,6 +80,10 @@ public:
// after calling markLive. // after calling markLive.
bool isLive() const; bool isLive() const;
bool isLazy() const {
return symbolKind == LazyArchiveKind || symbolKind == LazyObjectKind;
}
protected: protected:
friend SymbolTable; friend SymbolTable;
explicit Symbol(Kind k, StringRef n = "") explicit Symbol(Kind k, StringRef n = "")
@ -256,26 +261,29 @@ private:
// This class represents a symbol defined in an archive file. It is // 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 // created from an archive file header, and it knows how to load an
// object file from an archive to replace itself with a defined // object file from an archive to replace itself with a defined
// symbol. If the resolver finds both Undefined and Lazy for // symbol. If the resolver finds both Undefined and LazyArchive for
// the same name, it will ask the Lazy to load a file. // the same name, it will ask the LazyArchive to load a file.
class Lazy : public Symbol { class LazyArchive : public Symbol {
public: public:
Lazy(ArchiveFile *f, const Archive::Symbol s) LazyArchive(ArchiveFile *f, const Archive::Symbol s)
: Symbol(LazyKind, s.getName()), file(f), sym(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(); MemoryBufferRef getMemberBuffer();
ArchiveFile *file; ArchiveFile *file;
private:
friend SymbolTable;
private:
const Archive::Symbol sym; 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. // Undefined symbols.
class Undefined : public Symbol { class Undefined : public Symbol {
public: public:
@ -381,7 +389,8 @@ inline uint64_t Defined::getRVA() {
return cast<DefinedCommon>(this)->getRVA(); return cast<DefinedCommon>(this)->getRVA();
case DefinedRegularKind: case DefinedRegularKind:
return cast<DefinedRegular>(this)->getRVA(); return cast<DefinedRegular>(this)->getRVA();
case LazyKind: case LazyArchiveKind:
case LazyObjectKind:
case UndefinedKind: case UndefinedKind:
llvm_unreachable("Cannot get the address for an undefined symbol."); llvm_unreachable("Cannot get the address for an undefined symbol.");
} }
@ -404,7 +413,8 @@ inline Chunk *Defined::getChunk() {
return cast<DefinedLocalImport>(this)->getChunk(); return cast<DefinedLocalImport>(this)->getChunk();
case DefinedCommonKind: case DefinedCommonKind:
return cast<DefinedCommon>(this)->getChunk(); return cast<DefinedCommon>(this)->getChunk();
case LazyKind: case LazyArchiveKind:
case LazyObjectKind:
case UndefinedKind: case UndefinedKind:
llvm_unreachable("Cannot get the chunk of an undefined symbol."); llvm_unreachable("Cannot get the chunk of an undefined symbol.");
} }
@ -419,11 +429,12 @@ union SymbolUnion {
alignas(DefinedCommon) char b[sizeof(DefinedCommon)]; alignas(DefinedCommon) char b[sizeof(DefinedCommon)];
alignas(DefinedAbsolute) char c[sizeof(DefinedAbsolute)]; alignas(DefinedAbsolute) char c[sizeof(DefinedAbsolute)];
alignas(DefinedSynthetic) char d[sizeof(DefinedSynthetic)]; 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(Undefined) char f[sizeof(Undefined)];
alignas(DefinedImportData) char g[sizeof(DefinedImportData)]; alignas(DefinedImportData) char g[sizeof(DefinedImportData)];
alignas(DefinedImportThunk) char h[sizeof(DefinedImportThunk)]; alignas(DefinedImportThunk) char h[sizeof(DefinedImportThunk)];
alignas(DefinedLocalImport) char i[sizeof(DefinedLocalImport)]; alignas(DefinedLocalImport) char i[sizeof(DefinedLocalImport)];
alignas(LazyObject) char j[sizeof(LazyObject)];
}; };
template <typename T, typename... ArgT> template <typename T, typename... ArgT>

View File

@ -1519,7 +1519,8 @@ static void maybeAddAddressTakenFunction(SymbolRVASet &addressTakenSyms,
// Absolute is never code, synthetic generally isn't and usually isn't // Absolute is never code, synthetic generally isn't and usually isn't
// determinable. // determinable.
break; break;
case Symbol::LazyKind: case Symbol::LazyArchiveKind:
case Symbol::LazyObjectKind:
case Symbol::UndefinedKind: case Symbol::UndefinedKind:
// Undefined symbols resolve to zero, so they don't have an RVA. Lazy // Undefined symbols resolve to zero, so they don't have an RVA. Lazy
// symbols shouldn't have relocations. // symbols shouldn't have relocations.

View File

@ -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"}

View File

@ -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"}

View File

@ -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
}

View File

@ -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
}