-
Notifications
You must be signed in to change notification settings - Fork 13.5k
[LLD][COFF] Make unresolved symbol search behavior compliant with MSVC link.exe #85290
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
d5c4adf
b1149e1
6c2a777
ad59e48
b115c60
9d910c7
865dc26
114870b
0ece320
4d6fcd8
87a90c3
c2e7c78
0f5d47e
baeca96
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -43,6 +43,7 @@ | |
#include "llvm/Support/Parallel.h" | ||
#include "llvm/Support/Path.h" | ||
#include "llvm/Support/Process.h" | ||
#include "llvm/Support/SaveAndRestore.h" | ||
#include "llvm/Support/TarWriter.h" | ||
#include "llvm/Support/TargetSelect.h" | ||
#include "llvm/Support/VirtualFileSystem.h" | ||
|
@@ -187,7 +188,8 @@ MemoryBufferRef LinkerDriver::takeBuffer(std::unique_ptr<MemoryBuffer> mb) { | |
} | ||
|
||
void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb, | ||
bool wholeArchive, bool lazy) { | ||
bool wholeArchive, bool lazy, | ||
ArchiveFile *parent) { | ||
StringRef filename = mb->getBufferIdentifier(); | ||
|
||
MemoryBufferRef mbref = takeBuffer(std::move(mb)); | ||
|
@@ -213,11 +215,11 @@ void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb, | |
ctx.symtab.addFile(make<ArchiveFile>(ctx, mbref)); | ||
break; | ||
case file_magic::bitcode: | ||
ctx.symtab.addFile(make<BitcodeFile>(ctx, mbref, "", 0, lazy)); | ||
ctx.symtab.addFile(make<BitcodeFile>(ctx, mbref, "", 0, lazy, parent)); | ||
break; | ||
case file_magic::coff_object: | ||
case file_magic::coff_import_library: | ||
ctx.symtab.addFile(make<ObjFile>(ctx, mbref, lazy)); | ||
ctx.symtab.addFile(make<ObjFile>(ctx, mbref, lazy, parent)); | ||
break; | ||
case file_magic::pdb: | ||
ctx.symtab.addFile(make<PDBInputFile>(ctx, mbref)); | ||
|
@@ -242,7 +244,9 @@ void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb, | |
} | ||
} | ||
|
||
void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive, bool lazy) { | ||
void LinkerDriver::enqueuePath( | ||
StringRef path, bool wholeArchive, bool lazy, | ||
std::optional<std::shared_future<ArchiveFile *>> parent) { | ||
auto future = std::make_shared<std::future<MBErrPair>>( | ||
createFutureForFile(std::string(path))); | ||
std::string pathStr = std::string(path); | ||
|
@@ -281,13 +285,15 @@ void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive, bool lazy) { | |
else | ||
error(msg + "; did you mean '" + nearest + "'"); | ||
} else | ||
ctx.driver.addBuffer(std::move(mb), wholeArchive, lazy); | ||
ctx.driver.addBuffer(std::move(mb), wholeArchive, lazy, | ||
parent ? parent->get() : nullptr); | ||
}); | ||
} | ||
|
||
void LinkerDriver::addArchiveBuffer(MemoryBufferRef mb, StringRef symName, | ||
StringRef parentName, | ||
uint64_t offsetInArchive) { | ||
uint64_t offsetInArchive, | ||
ArchiveFile *parent) { | ||
file_magic magic = identify_magic(mb.getBuffer()); | ||
if (magic == file_magic::coff_import_library) { | ||
InputFile *imp = make<ImportFile>(ctx, mb); | ||
|
@@ -298,10 +304,10 @@ void LinkerDriver::addArchiveBuffer(MemoryBufferRef mb, StringRef symName, | |
|
||
InputFile *obj; | ||
if (magic == file_magic::coff_object) { | ||
obj = make<ObjFile>(ctx, mb); | ||
obj = make<ObjFile>(ctx, mb, /*lazy=*/false, parent); | ||
} else if (magic == file_magic::bitcode) { | ||
obj = | ||
make<BitcodeFile>(ctx, mb, parentName, offsetInArchive, /*lazy=*/false); | ||
obj = make<BitcodeFile>(ctx, mb, parentName, offsetInArchive, | ||
/*lazy=*/false, parent); | ||
} else if (magic == file_magic::coff_cl_gl_object) { | ||
error(mb.getBufferIdentifier() + | ||
": is not a native COFF file. Recompile without /GL?"); | ||
|
@@ -318,7 +324,8 @@ void LinkerDriver::addArchiveBuffer(MemoryBufferRef mb, StringRef symName, | |
|
||
void LinkerDriver::enqueueArchiveMember(const Archive::Child &c, | ||
const Archive::Symbol &sym, | ||
StringRef parentName) { | ||
StringRef parentName, | ||
ArchiveFile *parent) { | ||
|
||
auto reportBufferError = [=](Error &&e, StringRef childName) { | ||
fatal("could not get the buffer for the member defining symbol " + | ||
|
@@ -332,10 +339,10 @@ void LinkerDriver::enqueueArchiveMember(const Archive::Child &c, | |
if (!mbOrErr) | ||
reportBufferError(mbOrErr.takeError(), check(c.getFullName())); | ||
MemoryBufferRef mb = mbOrErr.get(); | ||
enqueueTask([=]() { | ||
enqueueSecondaryTask([=]() { | ||
llvm::TimeTraceScope timeScope("Archive: ", mb.getBufferIdentifier()); | ||
ctx.driver.addArchiveBuffer(mb, toCOFFString(ctx, sym), parentName, | ||
offsetInArchive); | ||
offsetInArchive, parent); | ||
}); | ||
return; | ||
} | ||
|
@@ -346,7 +353,7 @@ void LinkerDriver::enqueueArchiveMember(const Archive::Child &c, | |
toCOFFString(ctx, sym)); | ||
auto future = | ||
std::make_shared<std::future<MBErrPair>>(createFutureForFile(childName)); | ||
enqueueTask([=]() { | ||
enqueueSecondaryTask([=]() { | ||
auto mbOrErr = future->get(); | ||
if (mbOrErr.second) | ||
reportBufferError(errorCodeToError(mbOrErr.second), childName); | ||
|
@@ -356,7 +363,15 @@ void LinkerDriver::enqueueArchiveMember(const Archive::Child &c, | |
// used as the buffer identifier. | ||
ctx.driver.addArchiveBuffer(takeBuffer(std::move(mbOrErr.first)), | ||
toCOFFString(ctx, sym), "", | ||
/*OffsetInArchive=*/0); | ||
/*OffsetInArchive=*/0, parent); | ||
}); | ||
} | ||
|
||
void LinkerDriver::enqueueLazyFile(InputFile *file) { | ||
enqueueSecondaryTask([=]() { | ||
// Once it has been enqueued, it cannot be lazy anymore. | ||
file->lazy = false; | ||
ctx.symtab.addFile(file); | ||
}); | ||
} | ||
|
||
|
@@ -365,6 +380,24 @@ bool LinkerDriver::isDecorated(StringRef sym) { | |
(!ctx.config.mingw && sym.contains('@')); | ||
} | ||
|
||
static LLVM_THREAD_LOCAL bool executingFirstQueue; | ||
|
||
static bool executeQueue(std::list<std::function<void()>> &queue) { | ||
bool didWork = !queue.empty(); | ||
while (!queue.empty()) { | ||
queue.front()(); | ||
queue.pop_front(); | ||
} | ||
return didWork; | ||
} | ||
|
||
static bool executeFirstQueue(std::list<std::function<void()>> &queue) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Comments here about the purpose of the first queue (to load only .objs from the command line) would be helpful. |
||
if (executingFirstQueue) | ||
return false; | ||
SaveAndRestore s(executingFirstQueue, true); | ||
return executeQueue(queue); | ||
} | ||
|
||
// Parses .drectve section contents and returns a list of files | ||
// specified by /defaultlib. | ||
void LinkerDriver::parseDirectives(InputFile *file) { | ||
|
@@ -475,6 +508,11 @@ void LinkerDriver::parseDirectives(InputFile *file) { | |
toString(file) + ")"); | ||
} | ||
} | ||
|
||
// If we are running off the low-priority task list, execute and drain the | ||
// high priority task list before going any further. This is to ensure symbols | ||
// provided by /DEFAULTLIB archives are linked properly in the symbol table. | ||
executeFirstQueue(firstTaskQueue); | ||
Comment on lines
+511
to
+515
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I’m wondering if we could simplify this by having |
||
} | ||
|
||
// Find file from search paths. You can omit ".obj", this function takes | ||
|
@@ -1057,18 +1095,19 @@ void LinkerDriver::parseModuleDefs(StringRef path) { | |
} | ||
|
||
void LinkerDriver::enqueueTask(std::function<void()> task) { | ||
taskQueue.push_back(std::move(task)); | ||
firstTaskQueue.push_back(std::move(task)); | ||
} | ||
|
||
void LinkerDriver::enqueueSecondaryTask(std::function<void()> task) { | ||
secondaryTaskQueue.push_back(std::move(task)); | ||
} | ||
|
||
bool LinkerDriver::run() { | ||
llvm::TimeTraceScope timeScope("Read input files"); | ||
ScopedTimer t(ctx.inputFileTimer); | ||
|
||
bool didWork = !taskQueue.empty(); | ||
while (!taskQueue.empty()) { | ||
taskQueue.front()(); | ||
taskQueue.pop_front(); | ||
} | ||
bool didWork = executeFirstQueue(firstTaskQueue); | ||
didWork |= executeQueue(secondaryTaskQueue); | ||
return didWork; | ||
} | ||
|
||
|
@@ -2111,25 +2150,38 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) { | |
{ | ||
llvm::TimeTraceScope timeScope2("Parse & queue inputs"); | ||
bool inLib = false; | ||
std::optional<std::shared_future<ArchiveFile *>> inLibArchive; | ||
for (auto *arg : args) { | ||
switch (arg->getOption().getID()) { | ||
case OPT_end_lib: | ||
if (!inLib) | ||
error("stray " + arg->getSpelling()); | ||
inLib = false; | ||
inLibArchive = std::nullopt; | ||
break; | ||
case OPT_start_lib: | ||
if (inLib) | ||
error("nested " + arg->getSpelling()); | ||
inLib = true; | ||
// In is important to create a fake archive here so that we remember its | ||
// placement on the command-line. This will be later needed to resolve | ||
// symbols in the archive order required by the MSVC specification. | ||
{ | ||
auto a = std::make_shared<std::promise<ArchiveFile *>>(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Review note: this is made a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is nitty code golf, but can this be implemented with an explicit lambda capture and std::move? This SO pattern is what I was thinking of. Maybe the task gets copied around more, so if not, then the shared_ptr is fine. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I did try several things, including what you're suggesting, as well as the std::bind trick, but nothing else works, besides the |
||
inLibArchive = a->get_future().share(); | ||
enqueueTask([=] { | ||
a->set_value( | ||
make<ArchiveFile>(ctx, MemoryBufferRef({}, "<cmdline-lib>"))); | ||
}); | ||
} | ||
break; | ||
case OPT_wholearchive_file: | ||
if (std::optional<StringRef> path = findFileIfNew(arg->getValue())) | ||
enqueuePath(*path, true, inLib); | ||
break; | ||
case OPT_INPUT: | ||
if (std::optional<StringRef> path = findFileIfNew(arg->getValue())) | ||
enqueuePath(*path, isWholeArchive(*path), inLib); | ||
enqueuePath(*path, isWholeArchive(*path), inLib, inLibArchive); | ||
break; | ||
default: | ||
// Ignore other options. | ||
|
@@ -2138,8 +2190,10 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) { | |
} | ||
} | ||
|
||
// Read all input files given via the command line. | ||
run(); | ||
// Read all input files given via the command line. Do not process the | ||
// dependent OBJs pulled from archives just yet, since we need to push the | ||
// default libs first. | ||
executeFirstQueue(firstTaskQueue); | ||
if (errorCount()) | ||
return; | ||
|
||
|
@@ -2443,9 +2497,11 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) { | |
|
||
if (args.hasArg(OPT_include_optional)) { | ||
// Handle /includeoptional | ||
for (auto *arg : args.filtered(OPT_include_optional)) | ||
if (isa_and_nonnull<LazyArchive>(ctx.symtab.find(arg->getValue()))) | ||
for (auto *arg : args.filtered(OPT_include_optional)) { | ||
Symbol *sym = ctx.symtab.find(arg->getValue()); | ||
if (sym && (isa<LazyArchive>(sym) || isa<LazyObject>(sym))) | ||
addUndefined(arg->getValue()); | ||
} | ||
} | ||
} while (run()); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than TLS for every thread, can this be a LinkerDriver field and these helpers private methods?