[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)
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

View File

@ -170,7 +170,7 @@ MemoryBufferRef LinkerDriver::takeBuffer(std::unique_ptr<MemoryBuffer> mb) {
}
void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> 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<MemoryBuffer> mb,
symtab->addFile(make<ArchiveFile>(mbref));
break;
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;
case file_magic::coff_object:
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;
case file_magic::pdb:
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 =
std::make_shared<std::future<MBErrPair>>(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<StringRef> 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<const char *> 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<StringRef> 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<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))
if (Optional<StringRef> 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<const char *> argsArr) {
if (args.hasArg(OPT_include_optional)) {
// Handle /includeoptional
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());
while (run());
}

View File

@ -77,7 +77,7 @@ public:
MemoryBufferRef takeBuffer(std::unique_ptr<MemoryBuffer> mb);
void enqueuePath(StringRef path, bool wholeArchive);
void enqueuePath(StringRef path, bool wholeArchive, bool lazy);
private:
std::unique_ptr<llvm::TarWriter> tar; // for /linkrepro
@ -124,7 +124,8 @@ private:
StringRef findDefaultEntry();
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,
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) {}
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<MemoryBufferRef> getArchiveMembers(Archive *file) {
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() {
// Parse a memory buffer as a COFF file.
std::unique_ptr<Binary> bin = CHECK(createBinary(mb), this);
@ -526,13 +573,11 @@ Optional<Symbol *> 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<Symbol *> &&symbols)
: InputFile(BitcodeKind, mb), symbols(std::move(symbols)) {
std::string path = mb.getBufferIdentifier().str();
if (config->thinLTOIndexOnly)
path = replaceThinLTOSuffix(mb.getBufferIdentifier());

View File

@ -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<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.
class ObjFile : public InputFile {
public:
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; }
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<Symbol *> &&symbols);
static bool classof(const InputFile *f) { return f->kind() == BitcodeKind; }
ArrayRef<Symbol *> getSymbols() { return symbols; }
MachineTypes getMachineType() override;
@ -314,6 +343,10 @@ private:
std::vector<Symbol *> symbols;
};
inline bool isBitcode(MemoryBufferRef mb) {
return identify_magic(mb.getBuffer()) == llvm::file_magic::bitcode;
}
std::string replaceThinLTOSuffix(StringRef path);
} // namespace coff

View File

@ -162,6 +162,8 @@ def help : F<"help">;
def help_q : Flag<["/??", "-??", "/?", "-?"], "">, Alias<help>;
// 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">;

View File

@ -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<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.
// 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<Lazy>(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<Lazy>(s) && isWeakAlias)) {
if (wasInserted || (s->isLazy() && isWeakAlias)) {
replaceSymbol<Undefined>(s, name);
return s;
}
if (auto *l = dyn_cast<Lazy>(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<Lazy>(s, f, sym);
replaceSymbol<LazyArchive>(s, f, sym);
return;
}
auto *u = dyn_cast<Undefined>(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<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) {
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<Undefined>(s) || isa<Lazy>(s))
if (wasInserted || isa<Undefined>(s) || s->isLazy())
replaceSymbol<DefinedAbsolute>(s, n, sym);
else if (!isa<DefinedCOFF>(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<Undefined>(s) || isa<Lazy>(s))
if (wasInserted || isa<Undefined>(s) || s->isLazy())
replaceSymbol<DefinedAbsolute>(s, n, va);
else if (!isa<DefinedCOFF>(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<Undefined>(s) || isa<Lazy>(s))
if (wasInserted || isa<Undefined>(s) || s->isLazy())
replaceSymbol<DefinedSynthetic>(s, n, c);
else if (!isa<DefinedCOFF>(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<Undefined>(s) || isa<Lazy>(s)) {
if (wasInserted || isa<Undefined>(s) || s->isLazy()) {
replaceSymbol<DefinedImportData>(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<Undefined>(s) || isa<Lazy>(s)) {
if (wasInserted || isa<Undefined>(s) || s->isLazy()) {
replaceSymbol<DefinedImportThunk>(s, name, id, machine);
return s;
}
@ -589,9 +617,12 @@ void SymbolTable::addLibcall(StringRef name) {
if (!sym)
return;
if (Lazy *l = dyn_cast<Lazy>(sym)) {
if (auto *l = dyn_cast<LazyArchive>(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<LazyObject>(sym)) {
if (isBitcode(o->file->mb))
addUndefined(sym->getName());
}
}

View File

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

View File

@ -61,7 +61,9 @@ StringRef Symbol::getName() {
InputFile *Symbol::getFile() {
if (auto *sym = dyn_cast<DefinedCOFF>(this))
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 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));

View File

@ -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<DefinedCommon>(this)->getRVA();
case DefinedRegularKind:
return cast<DefinedRegular>(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<DefinedLocalImport>(this)->getChunk();
case DefinedCommonKind:
return cast<DefinedCommon>(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 <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
// 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.

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
}