[lld] [llvm] [LLD][COFF] Make unresolved symbol search behavior compliant with MSVC link.exe (PR #85290)
Alexandre Ganea via llvm-commits
llvm-commits at lists.llvm.org
Mon Jun 17 13:06:13 PDT 2024
https://github.com/aganea updated https://github.com/llvm/llvm-project/pull/85290
>From d5c4adfb5e85e80758cb51a91a246630924c7472 Mon Sep 17 00:00:00 2001
From: Alexandre Ganea <aganea at havenstudios.com>
Date: Thu, 14 Mar 2024 13:31:51 -0400
Subject: [PATCH 01/14] [LLD][COFF] Align unresolved symbols search behavior
with MSVC link.exe
---
lld/COFF/Driver.cpp | 53 +++++++++---
lld/COFF/Driver.h | 21 +++--
lld/COFF/InputFiles.cpp | 13 +--
lld/COFF/InputFiles.h | 26 ++++--
lld/COFF/SymbolTable.cpp | 110 +++++++++++++++++++++++--
lld/COFF/Symbols.h | 11 +++
lld/test/COFF/duplicate-imp-func.s | 6 +-
lld/test/COFF/lib-searching-behavior.s | 67 +++++++++++++++
llvm/include/llvm/Support/Allocator.h | 28 ++++++-
9 files changed, 292 insertions(+), 43 deletions(-)
create mode 100644 lld/test/COFF/lib-searching-behavior.s
diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp
index 22ee2f133be98..38e0392a87630 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -187,7 +187,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 +214,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 +243,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 +284,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 +303,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 +323,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 " +
@@ -335,7 +341,7 @@ void LinkerDriver::enqueueArchiveMember(const Archive::Child &c,
enqueueTask([=]() {
llvm::TimeTraceScope timeScope("Archive: ", mb.getBufferIdentifier());
ctx.driver.addArchiveBuffer(mb, toCOFFString(ctx, sym), parentName,
- offsetInArchive);
+ offsetInArchive, parent);
});
return;
}
@@ -356,7 +362,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) {
+ enqueueTask([=]() {
+ // Once it has been enqued, it cannot be lazy anymore.
+ file->lazy = false;
+ ctx.symtab.addFile(file);
});
}
@@ -2111,17 +2125,30 @@ 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 *>>();
+ 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()))
@@ -2129,7 +2156,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
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.
diff --git a/lld/COFF/Driver.h b/lld/COFF/Driver.h
index fa54de05befb5..da3c41e1bca73 100644
--- a/lld/COFF/Driver.h
+++ b/lld/COFF/Driver.h
@@ -22,6 +22,7 @@
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/TarWriter.h"
#include "llvm/WindowsDriver/MSVCPaths.h"
+#include <future>
#include <memory>
#include <optional>
#include <set>
@@ -91,13 +92,20 @@ class LinkerDriver {
// Used by ArchiveFile to enqueue members.
void enqueueArchiveMember(const Archive::Child &c, const Archive::Symbol &sym,
- StringRef parentName);
+ StringRef parentName,
+ ArchiveFile *parent = nullptr);
- void enqueuePDB(StringRef Path) { enqueuePath(Path, false, false); }
+ void enqueuePDB(StringRef Path) {
+ enqueuePath(Path, false, false, /*parent=*/std::nullopt);
+ }
MemoryBufferRef takeBuffer(std::unique_ptr<MemoryBuffer> mb);
- void enqueuePath(StringRef path, bool wholeArchive, bool lazy);
+ void enqueuePath(
+ StringRef path, bool wholeArchive, bool lazy,
+ std::optional<std::shared_future<ArchiveFile *>> parent = std::nullopt);
+
+ void enqueueLazyFile(InputFile *file);
std::unique_ptr<llvm::TarWriter> tar; // for /linkrepro
@@ -182,10 +190,11 @@ class LinkerDriver {
StringRef findDefaultEntry();
WindowsSubsystem inferSubsystem();
- void addBuffer(std::unique_ptr<MemoryBuffer> mb, bool wholeArchive,
- bool lazy);
+ void addBuffer(std::unique_ptr<MemoryBuffer> mb, bool wholeArchive, bool lazy,
+ ArchiveFile *parent = nullptr);
void addArchiveBuffer(MemoryBufferRef mbref, StringRef symName,
- StringRef parentName, uint64_t offsetInArchive);
+ StringRef parentName, uint64_t offsetInArchive,
+ ArchiveFile *parent = nullptr);
void enqueueTask(std::function<void()> task);
bool run();
diff --git a/lld/COFF/InputFiles.cpp b/lld/COFF/InputFiles.cpp
index 037fae45242c6..42cdd1cf3b6c2 100644
--- a/lld/COFF/InputFiles.cpp
+++ b/lld/COFF/InputFiles.cpp
@@ -94,9 +94,12 @@ static bool ignoredSymbolName(StringRef name) {
}
ArchiveFile::ArchiveFile(COFFLinkerContext &ctx, MemoryBufferRef m)
- : InputFile(ctx, ArchiveKind, m) {}
+ : InputFile(ctx, ArchiveKind, m, /*lazy=*/true) {
+ static unsigned Order = 0;
+ CmdLineIndex = Order++;
+}
-void ArchiveFile::parse() {
+void ArchiveFile::parseLazy() {
// Parse a MemoryBufferRef as an archive file.
file = CHECK(Archive::create(mb), this);
@@ -115,7 +118,7 @@ void ArchiveFile::addMember(const Archive::Symbol &sym) {
if (!seen.insert(c.getChildOffset()).second)
return;
- ctx.driver.enqueueArchiveMember(c, sym, getName());
+ ctx.driver.enqueueArchiveMember(c, sym, getName(), this);
}
std::vector<MemoryBufferRef> lld::coff::getArchiveMembers(Archive *file) {
@@ -1000,8 +1003,8 @@ void ImportFile::parse() {
BitcodeFile::BitcodeFile(COFFLinkerContext &ctx, MemoryBufferRef mb,
StringRef archiveName, uint64_t offsetInArchive,
- bool lazy)
- : InputFile(ctx, BitcodeKind, mb, lazy) {
+ bool lazy, ArchiveFile *parent)
+ : InputFile(ctx, BitcodeKind, mb, lazy), parent(parent) {
std::string path = mb.getBufferIdentifier().str();
if (ctx.config.thinLTOIndexOnly)
path = replaceThinLTOSuffix(mb.getBufferIdentifier(),
diff --git a/lld/COFF/InputFiles.h b/lld/COFF/InputFiles.h
index 3b55cd791bfda..7070f51fdf78a 100644
--- a/lld/COFF/InputFiles.h
+++ b/lld/COFF/InputFiles.h
@@ -66,7 +66,6 @@ class InputFile {
enum Kind {
ArchiveKind,
ObjectKind,
- LazyObjectKind,
PDBKind,
ImportKind,
BitcodeKind,
@@ -105,7 +104,7 @@ class InputFile {
public:
// True if this is a lazy ObjFile or BitcodeFile.
- bool lazy = false;
+ bool lazy;
};
// .lib or .a file.
@@ -113,23 +112,30 @@ class ArchiveFile : public InputFile {
public:
explicit ArchiveFile(COFFLinkerContext &ctx, MemoryBufferRef m);
static bool classof(const InputFile *f) { return f->kind() == ArchiveKind; }
- void parse() override;
+ void parse() override{};
+ void parseLazy();
// Enqueues an archive member load for the given symbol. If we've already
// enqueued a load for the same archive member, this function does nothing,
// which ensures that we don't load the same member more than once.
void addMember(const Archive::Symbol &sym);
-private:
std::unique_ptr<Archive> file;
+
+ // The order this archive was seen on the cmd-line. This is later needed for
+ // resolving undefined symbols in archive OBJs.
+ uint32_t CmdLineIndex;
+
+private:
llvm::DenseSet<uint64_t> seen;
};
// .obj or .o file. This may be a member of an archive file.
class ObjFile : public InputFile {
public:
- explicit ObjFile(COFFLinkerContext &ctx, MemoryBufferRef m, bool lazy = false)
- : InputFile(ctx, ObjectKind, m, lazy) {}
+ explicit ObjFile(COFFLinkerContext &ctx, MemoryBufferRef m, bool lazy = false,
+ ArchiveFile *parent = nullptr)
+ : InputFile(ctx, ObjectKind, m, lazy), parent(parent) {}
static bool classof(const InputFile *f) { return f->kind() == ObjectKind; }
void parse() override;
void parseLazy();
@@ -182,6 +188,9 @@ class ObjFile : public InputFile {
// True if this file was compiled with /guard:ehcont.
bool hasGuardEHCont() { return feat00Flags & 0x4000; }
+ // Whether this Obj buffer is part of an archive.
+ ArchiveFile *parent;
+
// Pointer to the PDB module descriptor builder. Various debug info records
// will reference object files by "module index", which is here. Things like
// source files and section contributions are also recorded here. Will be null
@@ -369,7 +378,7 @@ class BitcodeFile : public InputFile {
public:
explicit BitcodeFile(COFFLinkerContext &ctx, MemoryBufferRef mb,
StringRef archiveName, uint64_t offsetInArchive,
- bool lazy);
+ bool lazy = false, ArchiveFile *parent = nullptr);
~BitcodeFile();
static bool classof(const InputFile *f) { return f->kind() == BitcodeKind; }
ArrayRef<Symbol *> getSymbols() { return symbols; }
@@ -377,6 +386,9 @@ class BitcodeFile : public InputFile {
void parseLazy();
std::unique_ptr<llvm::lto::InputFile> obj;
+ // Whether this bitcode buffer is part of an archive.
+ ArchiveFile *parent;
+
private:
void parse() override;
diff --git a/lld/COFF/SymbolTable.cpp b/lld/COFF/SymbolTable.cpp
index 44aa506d2c35d..f570e8c211f43 100644
--- a/lld/COFF/SymbolTable.cpp
+++ b/lld/COFF/SymbolTable.cpp
@@ -54,8 +54,10 @@ void SymbolTable::addFile(InputFile *file) {
if (file->lazy) {
if (auto *f = dyn_cast<BitcodeFile>(file))
f->parseLazy();
- else
- cast<ObjFile>(file)->parseLazy();
+ else if (auto *o = dyn_cast<ObjFile>(file))
+ o->parseLazy();
+ else if (auto *a = dyn_cast<ArchiveFile>(file))
+ a->parseLazy();
} else {
file->parse();
if (auto *f = dyn_cast<ObjFile>(file)) {
@@ -102,7 +104,7 @@ static void forceLazy(Symbol *s) {
}
case Symbol::Kind::LazyObjectKind: {
InputFile *file = cast<LazyObject>(s)->file;
- file->ctx.symtab.addFile(file);
+ file->ctx.driver.enqueueLazyFile(file);
break;
}
case Symbol::Kind::LazyDLLSymbolKind: {
@@ -562,6 +564,57 @@ std::pair<Symbol *, bool> SymbolTable::insert(StringRef name, InputFile *file) {
return result;
}
+static LazyIntrusiveNode *lazyNode(Symbol *s) {
+ if (auto *sym = dyn_cast<LazyArchive>(s))
+ return &sym->node;
+ if (auto *sym = dyn_cast<LazyObject>(s))
+ return &sym->node;
+ return nullptr;
+}
+
+static ArchiveFile *lazyParent(InputFile *f) {
+ if (!f)
+ return nullptr;
+ if (auto *obj = dyn_cast<ObjFile>(f))
+ return obj->parent;
+ if (auto *obj = dyn_cast<BitcodeFile>(f))
+ return obj->parent;
+ return nullptr;
+}
+
+static ArchiveFile *lazyArchive(Symbol *s) {
+ if (auto *sym = dyn_cast<LazyArchive>(s))
+ return sym->file;
+ if (auto *sym = dyn_cast<LazyObject>(s))
+ return lazyParent(sym->file);
+ return nullptr;
+}
+
+// The search behavior for undefined symbols is different when the OBJ
+// was pulled from an archive (LIB). This is documented here:
+// https://learn.microsoft.com/en-us/cpp/build/reference/link-input-files?view=msvc-170
+// "Object files on the command line are processed in the order they
+// appear on the command line. Libraries are searched in command line
+// order as well, with the following caveat: Symbols that are unresolved
+// when bringing in an object file from a library are searched for in
+// that library first, and then the following libraries from the command
+// line and /DEFAULTLIB (Specify default library) directives, and then
+// to any libraries at the beginning of the command line."
+static Symbol *searchArchiveSymbol(Symbol *s, ArchiveFile *pivot) {
+ auto &Alloc = getSpecificAllocSingleton<SymbolUnion>().Allocator;
+ Symbol *curr = s;
+ for (;;) {
+ if (lazyArchive(curr)->CmdLineIndex >= pivot->CmdLineIndex)
+ return curr;
+ uint32_t next = lazyNode(curr)->next;
+ if (!next)
+ break;
+ curr = reinterpret_cast<LazyArchive *>(
+ Alloc.fromAlignedIndex<SymbolUnion>(next));
+ }
+ return s;
+}
+
Symbol *SymbolTable::addUndefined(StringRef name, InputFile *f,
bool isWeakAlias) {
auto [s, wasInserted] = insert(name, f);
@@ -569,11 +622,43 @@ Symbol *SymbolTable::addUndefined(StringRef name, InputFile *f,
replaceSymbol<Undefined>(s, name);
return s;
}
- if (s->isLazy())
+ if (s->isLazy()) {
+ if (ArchiveFile *parent = lazyParent(f)) {
+ Symbol *selected = searchArchiveSymbol(s, parent);
+ forceLazy(selected);
+ // Now that we have selected a symbol, we don't need the linked list of
+ // `LazyArchive`s anymore. Collapse to the selected symbol.
+ memcpy(s, selected, sizeof(SymbolUnion));
+ return s;
+ }
forceLazy(s);
+ }
return s;
}
+// This creates a linked list of archives where a specific symbol was seen.
+// We later walk that list if a undefined symbol needs to be resolved from an
+// archive OBJ.
+template <typename T, typename... ArgT>
+static void chainLazy(LazyIntrusiveNode *front, ArgT &&...arg) {
+ // Chain with symbols defined in other archives
+ Symbol *newSym = reinterpret_cast<Symbol *>(make<SymbolUnion>());
+ newSym->canInline = true;
+ replaceSymbol<T>(newSym, std::forward<ArgT>(arg)...);
+
+ auto &Alloc = getSpecificAllocSingleton<SymbolUnion>().Allocator;
+ uint32_t index = Alloc.identifyKnownAlignedObject<SymbolUnion>(newSym);
+
+ if (!front->next)
+ front->next = index;
+ if (front->last) {
+ Symbol *last = reinterpret_cast<Symbol *>(
+ Alloc.fromAlignedIndex<SymbolUnion>(front->last));
+ lazyNode(last)->next = index;
+ }
+ front->last = index;
+}
+
void SymbolTable::addLazyArchive(ArchiveFile *f, const Archive::Symbol &sym) {
StringRef name = sym.getName();
auto [s, wasInserted] = insert(name);
@@ -581,6 +666,10 @@ void SymbolTable::addLazyArchive(ArchiveFile *f, const Archive::Symbol &sym) {
replaceSymbol<LazyArchive>(s, f, sym);
return;
}
+ if (auto *n = lazyNode(s)) {
+ chainLazy<LazyArchive>(n, f, sym);
+ return;
+ }
auto *u = dyn_cast<Undefined>(s);
if (!u || u->weakAlias || s->pendingArchiveLoad)
return;
@@ -588,19 +677,22 @@ void SymbolTable::addLazyArchive(ArchiveFile *f, const Archive::Symbol &sym) {
f->addMember(sym);
}
-void SymbolTable::addLazyObject(InputFile *f, StringRef n) {
+void SymbolTable::addLazyObject(InputFile *f, StringRef name) {
assert(f->lazy);
- auto [s, wasInserted] = insert(n, f);
+ auto [s, wasInserted] = insert(name, f);
if (wasInserted) {
- replaceSymbol<LazyObject>(s, f, n);
+ replaceSymbol<LazyObject>(s, f, name);
+ return;
+ }
+ if (auto *n = lazyNode(s)) {
+ chainLazy<LazyObject>(n, f, name);
return;
}
auto *u = dyn_cast<Undefined>(s);
if (!u || u->weakAlias || s->pendingArchiveLoad)
return;
s->pendingArchiveLoad = true;
- f->lazy = false;
- addFile(f);
+ f->ctx.driver.enqueueLazyFile(f);
}
void SymbolTable::addLazyDLLSymbol(DLLFile *f, DLLFile::Symbol *sym,
diff --git a/lld/COFF/Symbols.h b/lld/COFF/Symbols.h
index ca69fb2d05270..1577406c8626c 100644
--- a/lld/COFF/Symbols.h
+++ b/lld/COFF/Symbols.h
@@ -286,6 +286,15 @@ class DefinedSynthetic : public Defined {
uint32_t offset;
};
+// Keep track of symbols with the same name exposed by archives. This is
+// required to later resolve unresolved symbols in the same order as required
+// by the MSVC spec. These are indexes in the specific bump allocator for
+// SymbolUnion.
+struct LazyIntrusiveNode {
+ uint32_t next = 0;
+ uint32_t last = 0;
+};
+
// 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
@@ -302,6 +311,7 @@ class LazyArchive : public Symbol {
ArchiveFile *file;
const Archive::Symbol sym;
+ LazyIntrusiveNode node;
};
class LazyObject : public Symbol {
@@ -309,6 +319,7 @@ class LazyObject : public Symbol {
LazyObject(InputFile *f, StringRef n) : Symbol(LazyObjectKind, n), file(f) {}
static bool classof(const Symbol *s) { return s->kind() == LazyObjectKind; }
InputFile *file;
+ LazyIntrusiveNode node;
};
// MinGW only.
diff --git a/lld/test/COFF/duplicate-imp-func.s b/lld/test/COFF/duplicate-imp-func.s
index fc0cf1ef6ae05..631c714c951f7 100644
--- a/lld/test/COFF/duplicate-imp-func.s
+++ b/lld/test/COFF/duplicate-imp-func.s
@@ -28,8 +28,10 @@
# Once the import library member from %t.lib.dll.a gets loaded, libfunc
# and __imp_libfunc already are defined.
-# Just check that this fails cleanly (doesn't crash).
-# RUN: not lld-link -lldmingw -out:%t.main.exe -entry:main %t.main.o %t.lib.dll.a %t.helper.a
+# This test should now succeed since we're following the MSVC symbol searching behvior described in:
+# https://learn.microsoft.com/en-us/cpp/build/reference/link-input-files?view=msvc-170
+# In this case, the linker will select the libfunc symbol in %t.helper.a
+# RUN: lld-link -lldmingw -out:%t.main.exe -entry:main %t.main.o %t.lib.dll.a %t.helper.a
# Test with %t.helper.a on the command line; in this case we won't try to
# include libfunc from %t.lib.dll.a and everything works fine.
diff --git a/lld/test/COFF/lib-searching-behavior.s b/lld/test/COFF/lib-searching-behavior.s
new file mode 100644
index 0000000000000..eb4ba55c39753
--- /dev/null
+++ b/lld/test/COFF/lib-searching-behavior.s
@@ -0,0 +1,67 @@
+# REQUIRES: x86
+
+# This test ensures that we're following the MSVC symbol searching behvior described in:
+# https://learn.microsoft.com/en-us/cpp/build/reference/link-input-files?view=msvc-170
+# "Object files on the command line are processed in the order they appear on the command line.
+# Libraries are searched in command line order as well, with the following caveat: Symbols that
+# are unresolved when bringing in an object file from a library are searched for in that library
+# first, and then the following libraries from the command line and /DEFAULTLIB (Specify default
+# library) directives, and then to any libraries at the beginning of the command line."
+
+# RUN: echo -e ".intel_syntax noprefix\n.globl libfunc\n.text\nlibfunc:\nmov eax, 1\nret\n.section .drectve\n.ascii \"/EXPORT:libfunc\"" > %t.lib.s
+# RUN: llvm-mc -triple=x86_64-pc-windows-msvc %t.lib.s -filetype=obj -o %t.lib.o
+# RUN: lld-link -dll -out:%t.lib.dll -entry:libfunc %t.lib.o -implib:%t.lib.dll.a
+
+# RUN: echo -e ".globl helper\n.text\nhelper:\ncall libfunc\nret" > %t.helper1.s
+# RUN: echo -e ".intel_syntax noprefix\n.globl libfunc\n.text\nlibfunc:\nxor eax, eax\nret" > %t.helper2.s
+# RUN: llvm-mc -triple=x86_64-pc-windows-msvc %t.helper1.s -filetype=obj -o %t.helper1.o
+# RUN: llvm-mc -triple=x86_64-pc-windows-msvc %t.helper2.s -filetype=obj -o %t.helper2.o
+
+# RUN: llvm-ar rcs %t.helper.a %t.helper1.o %t.helper2.o
+
+# RUN: llvm-mc -triple=x86_64-pc-windows-msvc %s -filetype=obj -o %t.main.o
+
+# Simulate a setup, where two libraries provide the same function;
+# %t.lib.dll.a is a pure import library which provides a import symbol "libfunc".
+# %t.helper.a is a static library which contains "helper1" and "helper2".
+#
+# helper1 contains an undefined reference to libfunc. helper2 contains an
+# implementation of libfunc.
+#
+# First %t.main.o is processed and pushes a undefined symbol 'helper'.
+# Then %t.lib.dll.a is processed a pushes the lazy archive symbol 'libfunc' in the symbol table.
+# Then comes %t.helper.a and it pushes 'helper' and 'libfunc' as lazy symbols. Then 'helper' is
+# resolved and that pushes 'libfunc' as a undefined symbol. That pulls on %t.helper.a(%t.helper2.o)
+# which contains the 'libfunc' symbol, resolving it. This is illustrative of the MSVC library searching
+# behavior which starts with the current library object which requested the unresolved symbol.
+# RUN: lld-link -out:%t.main.exe -entry:main %t.main.o %t.lib.dll.a %t.helper.a
+# RUN: llvm-objdump --no-print-imm-hex -d %t.main.exe | FileCheck --check-prefix=LIB %s
+
+# In this case, the symbol in %t.helper.a(%t.helper2.o) is still considered first.
+# RUN: lld-link -out:%t.main.exe -entry:main %t.main.o %t.helper.a %t.lib.dll.a
+# RUN: llvm-objdump --no-print-imm-hex -d %t.main.exe | FileCheck --check-prefix=LIB %s
+
+# In this test we're defining libfunc in a third library that comes after all the others. The symbol should be pulled
+# now from that third library.
+# RUN: llvm-ar rcs %t.helper1.a %t.helper1.o
+# RUN: llvm-ar rcs %t.helper2.a %t.helper2.o
+# RUN: lld-link -out:%t.main.exe -entry:main %t.main.o %t.lib.dll.a %t.helper1.a %t.helper2.a
+# RUN: llvm-objdump --no-print-imm-hex -d %t.main.exe | FileCheck --check-prefix=LIB %s
+
+# LIB: 140001000 <.text>:
+# LIB: 140001000: e8 03 00 00 00 callq 0x140001008 <.text+0x8>
+# LIB: 140001008: e8 03 00 00 00 callq 0x140001010 <.text+0x10>
+# LIB: 140001010: 31 c0 xorl %eax, %eax
+
+# In this last test, we should pick up the import symbol from %t.lib.dll.a since it isn't defined anywhere else.
+# RUN: lld-link -out:%t.main.exe -entry:main %t.main.o %t.lib.dll.a %t.helper1.a
+# RUN: llvm-objdump --no-print-imm-hex -d %t.main.exe | FileCheck --check-prefix=LIB-IMP %s
+
+# LIB-IMP: 140001000 <.text>:
+# LIB-IMP: 140001010: ff 25 22 10 00 00 jmpq *4130(%rip)
+
+ .globl main
+ .text
+main:
+ call helper
+ ret
diff --git a/llvm/include/llvm/Support/Allocator.h b/llvm/include/llvm/Support/Allocator.h
index c1e5c6d2853bd..8c061b72d65f7 100644
--- a/llvm/include/llvm/Support/Allocator.h
+++ b/llvm/include/llvm/Support/Allocator.h
@@ -278,6 +278,32 @@ class BumpPtrAllocatorImpl
return Out / alignof(T);
}
+ /// Gets an already allocated object from an index that was previously
+ /// retrieved with `identifyKnownAlignedObject`.
+ template <typename T> T *fromAlignedIndex(int64_t Index) {
+ Index *= alignof(T);
+
+ int64_t InSlabIdx = 0;
+ for (size_t Idx = 0, E = Slabs.size(); Idx < E; Idx++) {
+ char *S = static_cast<char *>(Slabs[Idx]);
+ if (Index >= InSlabIdx &&
+ Index < InSlabIdx + static_cast<int64_t>(computeSlabSize(Idx)))
+ return reinterpret_cast<T *>(S + (Index - InSlabIdx));
+ InSlabIdx += static_cast<int64_t>(computeSlabSize(Idx));
+ }
+
+ // Use negative index to denote custom sized slabs.
+ int64_t InCustomSizedSlabIdx = -1;
+ for (size_t Idx = 0, E = CustomSizedSlabs.size(); Idx < E; Idx++) {
+ char *S = static_cast<char *>(CustomSizedSlabs[Idx].first);
+ int64_t Size = static_cast<int64_t>(CustomSizedSlabs[Idx].second);
+ if (Index <= InCustomSizedSlabIdx && Index > InCustomSizedSlabIdx - Size)
+ return reinterpret_cast<T *>(S - (Index - InCustomSizedSlabIdx));
+ InCustomSizedSlabIdx -= static_cast<int64_t>(Size);
+ }
+ return nullptr;
+ }
+
size_t getTotalMemory() const {
size_t TotalMemory = 0;
for (auto I = Slabs.begin(), E = Slabs.end(); I != E; ++I)
@@ -380,9 +406,9 @@ typedef BumpPtrAllocatorImpl<> BumpPtrAllocator;
/// This allows calling the destructor in DestroyAll() and when the allocator is
/// destroyed.
template <typename T> class SpecificBumpPtrAllocator {
+public:
BumpPtrAllocator Allocator;
-public:
SpecificBumpPtrAllocator() {
// Because SpecificBumpPtrAllocator walks the memory to call destructors,
// it can't have red zones between allocations.
>From b1149e14bed66742286093dcb68399029980cdaf Mon Sep 17 00:00:00 2001
From: Alexandre Ganea <aganea at havenstudios.com>
Date: Thu, 14 Mar 2024 14:29:25 -0400
Subject: [PATCH 02/14] Revert unneeded changes
---
lld/COFF/SymbolTable.cpp | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/lld/COFF/SymbolTable.cpp b/lld/COFF/SymbolTable.cpp
index f570e8c211f43..1b0e073ac0052 100644
--- a/lld/COFF/SymbolTable.cpp
+++ b/lld/COFF/SymbolTable.cpp
@@ -677,15 +677,15 @@ void SymbolTable::addLazyArchive(ArchiveFile *f, const Archive::Symbol &sym) {
f->addMember(sym);
}
-void SymbolTable::addLazyObject(InputFile *f, StringRef name) {
+void SymbolTable::addLazyObject(InputFile *f, StringRef n) {
assert(f->lazy);
- auto [s, wasInserted] = insert(name, f);
+ auto [s, wasInserted] = insert(n, f);
if (wasInserted) {
- replaceSymbol<LazyObject>(s, f, name);
+ replaceSymbol<LazyObject>(s, f, n);
return;
}
- if (auto *n = lazyNode(s)) {
- chainLazy<LazyObject>(n, f, name);
+ if (auto *node = lazyNode(s)) {
+ chainLazy<LazyObject>(node, f, n);
return;
}
auto *u = dyn_cast<Undefined>(s);
>From 6c2a7770dccde79e0b0b61b004cec4447d24848f Mon Sep 17 00:00:00 2001
From: Alexandre Ganea <aganea at havenstudios.com>
Date: Thu, 14 Mar 2024 14:29:48 -0400
Subject: [PATCH 03/14] Test cmd-line libraries
---
lld/test/COFF/lib-searching-behavior.s | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/lld/test/COFF/lib-searching-behavior.s b/lld/test/COFF/lib-searching-behavior.s
index eb4ba55c39753..4ba786f015f44 100644
--- a/lld/test/COFF/lib-searching-behavior.s
+++ b/lld/test/COFF/lib-searching-behavior.s
@@ -53,13 +53,17 @@
# LIB: 140001008: e8 03 00 00 00 callq 0x140001010 <.text+0x10>
# LIB: 140001010: 31 c0 xorl %eax, %eax
-# In this last test, we should pick up the import symbol from %t.lib.dll.a since it isn't defined anywhere else.
+# Here, we should pick up the import symbol from %t.lib.dll.a since it isn't defined anywhere else.
# RUN: lld-link -out:%t.main.exe -entry:main %t.main.o %t.lib.dll.a %t.helper1.a
# RUN: llvm-objdump --no-print-imm-hex -d %t.main.exe | FileCheck --check-prefix=LIB-IMP %s
# LIB-IMP: 140001000 <.text>:
# LIB-IMP: 140001010: ff 25 22 10 00 00 jmpq *4130(%rip)
+# Test cmd-line archives
+# RUN: lld-link -out:%t.main.exe -entry:main %t.main.o %t.lib.dll.a -start-lib %t.helper1.o %t.helper2.o -end-lib
+# RUN: llvm-objdump --no-print-imm-hex -d %t.main.exe | FileCheck --check-prefix=LIB %s
+
.globl main
.text
main:
>From ad59e483aae69af21cdcecb81a55630361f8e93a Mon Sep 17 00:00:00 2001
From: Alexandre Ganea <aganea at havenstudios.com>
Date: Fri, 15 Mar 2024 09:32:42 -0400
Subject: [PATCH 04/14] LLD release notes
---
lld/docs/ReleaseNotes.rst | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/lld/docs/ReleaseNotes.rst b/lld/docs/ReleaseNotes.rst
index 97ed060489100..4b83d078e21e8 100644
--- a/lld/docs/ReleaseNotes.rst
+++ b/lld/docs/ReleaseNotes.rst
@@ -36,6 +36,10 @@ Breaking changes
COFF Improvements
-----------------
+* Symbols pulled from archives/LIBs are now resolved in the same way (archive
+ order) as MSVC link.exe. For more information see:
+ https://learn.microsoft.com/en-us/cpp/build/reference/link-input-files?view=msvc-170
+
MinGW Improvements
------------------
>From b115c60988183be6a15aaa2a9ad969a0072cb9cb Mon Sep 17 00:00:00 2001
From: Alexandre Ganea <aganea at havenstudios.com>
Date: Fri, 15 Mar 2024 09:48:33 -0400
Subject: [PATCH 05/14] Add another egde case where symbols could be
pulled/resolved from two archives
---
lld/COFF/SymbolTable.cpp | 11 ++++++++++-
lld/test/COFF/lib-searching-behavior.s | 20 ++++++++++++++++++++
2 files changed, 30 insertions(+), 1 deletion(-)
diff --git a/lld/COFF/SymbolTable.cpp b/lld/COFF/SymbolTable.cpp
index 1b0e073ac0052..7c26e91ec42c2 100644
--- a/lld/COFF/SymbolTable.cpp
+++ b/lld/COFF/SymbolTable.cpp
@@ -95,6 +95,7 @@ static void errorOrWarn(const Twine &s, bool forceUnresolved) {
// Causes the file associated with a lazy symbol to be linked in.
static void forceLazy(Symbol *s) {
+ assert(!s->pendingArchiveLoad);
s->pendingArchiveLoad = true;
switch (s->kind()) {
case Symbol::Kind::LazyArchiveKind: {
@@ -623,14 +624,21 @@ Symbol *SymbolTable::addUndefined(StringRef name, InputFile *f,
return s;
}
if (s->isLazy()) {
+ if (s->pendingArchiveLoad)
+ return s;
if (ArchiveFile *parent = lazyParent(f)) {
+ // We're placing a undefined symbol from an archive OBJ. The rules are
+ // different than regular OBJs on the command-line.
Symbol *selected = searchArchiveSymbol(s, parent);
forceLazy(selected);
// Now that we have selected a symbol, we don't need the linked list of
// `LazyArchive`s anymore. Collapse to the selected symbol.
- memcpy(s, selected, sizeof(SymbolUnion));
+ if (s != selected)
+ memcpy(s, selected, sizeof(SymbolUnion));
+ *lazyNode(s) = LazyIntrusiveNode();
return s;
}
+ // We're placing a undefined symbol from a command-line OBJ.
forceLazy(s);
}
return s;
@@ -667,6 +675,7 @@ void SymbolTable::addLazyArchive(ArchiveFile *f, const Archive::Symbol &sym) {
return;
}
if (auto *n = lazyNode(s)) {
+ assert(!s->pendingArchiveLoad);
chainLazy<LazyArchive>(n, f, sym);
return;
}
diff --git a/lld/test/COFF/lib-searching-behavior.s b/lld/test/COFF/lib-searching-behavior.s
index 4ba786f015f44..95bc580b3da4a 100644
--- a/lld/test/COFF/lib-searching-behavior.s
+++ b/lld/test/COFF/lib-searching-behavior.s
@@ -37,10 +37,12 @@
# RUN: lld-link -out:%t.main.exe -entry:main %t.main.o %t.lib.dll.a %t.helper.a
# RUN: llvm-objdump --no-print-imm-hex -d %t.main.exe | FileCheck --check-prefix=LIB %s
+
# In this case, the symbol in %t.helper.a(%t.helper2.o) is still considered first.
# RUN: lld-link -out:%t.main.exe -entry:main %t.main.o %t.helper.a %t.lib.dll.a
# RUN: llvm-objdump --no-print-imm-hex -d %t.main.exe | FileCheck --check-prefix=LIB %s
+
# In this test we're defining libfunc in a third library that comes after all the others. The symbol should be pulled
# now from that third library.
# RUN: llvm-ar rcs %t.helper1.a %t.helper1.o
@@ -53,6 +55,7 @@
# LIB: 140001008: e8 03 00 00 00 callq 0x140001010 <.text+0x10>
# LIB: 140001010: 31 c0 xorl %eax, %eax
+
# Here, we should pick up the import symbol from %t.lib.dll.a since it isn't defined anywhere else.
# RUN: lld-link -out:%t.main.exe -entry:main %t.main.o %t.lib.dll.a %t.helper1.a
# RUN: llvm-objdump --no-print-imm-hex -d %t.main.exe | FileCheck --check-prefix=LIB-IMP %s
@@ -60,10 +63,27 @@
# LIB-IMP: 140001000 <.text>:
# LIB-IMP: 140001010: ff 25 22 10 00 00 jmpq *4130(%rip)
+
# Test cmd-line archives
# RUN: lld-link -out:%t.main.exe -entry:main %t.main.o %t.lib.dll.a -start-lib %t.helper1.o %t.helper2.o -end-lib
# RUN: llvm-objdump --no-print-imm-hex -d %t.main.exe | FileCheck --check-prefix=LIB %s
+
+# Test pulling two different OBJ from two archives, which themselves both define the same symbol 'libfunc'.
+# Ensure that we resolve the symbol only once.
+
+# RUN: echo -e ".globl test\n.text\ntest:\ncall libfunc\nret" > %t.test1.s
+# RUN: echo -e ".intel_syntax noprefix\n.globl libfunc\n.text\nlibfunc:\nmov eax, 2\nret" > %t.test2.s
+# RUN: llvm-mc -triple=x86_64-pc-windows-msvc %t.test1.s -filetype=obj -o %t.test1.o
+# RUN: llvm-mc -triple=x86_64-pc-windows-msvc %t.test2.s -filetype=obj -o %t.test2.o
+# RUN: llvm-ar rcs %t.test.a %t.test1.o %t.test2.o
+
+# RUN: echo -e ".globl main\n.text\nmain:\ncall test\ncall helper\nret" > %t.main2.s
+# RUN: llvm-mc -triple=x86_64-pc-windows-msvc %s -filetype=obj -o %t.main2.o
+
+# RUN: lld-link -out:%t.main.exe -entry:main %t.main2.o %t.helper.a %t.test.a 2>&1 | FileCheck --allow-empty --check-prefix=LIB-TWO %s
+# LIB-TWO-NOT: duplicate symbol:
+
.globl main
.text
main:
>From 9d910c7cb83cbf0cc861b06847e8f38f04aa7e57 Mon Sep 17 00:00:00 2001
From: Alexandre Ganea <aganea at havenstudios.com>
Date: Fri, 15 Mar 2024 09:54:19 -0400
Subject: [PATCH 06/14] Missing assert for cmd-line archives
---
lld/COFF/SymbolTable.cpp | 1 +
1 file changed, 1 insertion(+)
diff --git a/lld/COFF/SymbolTable.cpp b/lld/COFF/SymbolTable.cpp
index 7c26e91ec42c2..414040dd67c0e 100644
--- a/lld/COFF/SymbolTable.cpp
+++ b/lld/COFF/SymbolTable.cpp
@@ -694,6 +694,7 @@ void SymbolTable::addLazyObject(InputFile *f, StringRef n) {
return;
}
if (auto *node = lazyNode(s)) {
+ assert(!s->pendingArchiveLoad);
chainLazy<LazyObject>(node, f, n);
return;
}
>From 865dc264ca00b5c2b1decf632d53ca5a7c74c83d Mon Sep 17 00:00:00 2001
From: Alexandre Ganea <aganea at havenstudios.com>
Date: Mon, 18 Mar 2024 19:02:36 -0400
Subject: [PATCH 07/14] Support the new behavior with /DEFAULTLIB on the
cmd-line or in directive section
---
lld/COFF/Driver.cpp | 55 ++++++++++++++++++++++++++++++----------
lld/COFF/Driver.h | 14 +++++++---
lld/COFF/InputFiles.cpp | 36 +++++++++++++++++++++++++-
lld/COFF/SymbolTable.cpp | 30 ++--------------------
4 files changed, 89 insertions(+), 46 deletions(-)
diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp
index 38e0392a87630..071c9d1bdaf59 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -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"
@@ -338,7 +339,7 @@ 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, parent);
@@ -352,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);
@@ -367,7 +368,7 @@ void LinkerDriver::enqueueArchiveMember(const Archive::Child &c,
}
void LinkerDriver::enqueueLazyFile(InputFile *file) {
- enqueueTask([=]() {
+ enqueueSecondaryTask([=]() {
// Once it has been enqued, it cannot be lazy anymore.
file->lazy = false;
ctx.symtab.addFile(file);
@@ -379,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) {
+ 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) {
@@ -489,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 property in the symbol table.
+ executeFirstQueue(firstTaskQueue);
}
// Find file from search paths. You can omit ".obj", this function takes
@@ -1071,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;
}
@@ -2165,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;
@@ -2470,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());
}
diff --git a/lld/COFF/Driver.h b/lld/COFF/Driver.h
index da3c41e1bca73..c38e1cffe77f8 100644
--- a/lld/COFF/Driver.h
+++ b/lld/COFF/Driver.h
@@ -95,9 +95,7 @@ class LinkerDriver {
StringRef parentName,
ArchiveFile *parent = nullptr);
- void enqueuePDB(StringRef Path) {
- enqueuePath(Path, false, false, /*parent=*/std::nullopt);
- }
+ void enqueuePDB(StringRef Path) { enqueuePath(Path, false, false); }
MemoryBufferRef takeBuffer(std::unique_ptr<MemoryBuffer> mb);
@@ -197,9 +195,17 @@ class LinkerDriver {
ArchiveFile *parent = nullptr);
void enqueueTask(std::function<void()> task);
+ void enqueueSecondaryTask(std::function<void()> task);
bool run();
- std::list<std::function<void()>> taskQueue;
+ // The first queue contains all direct command-line inputs, all /defaultlib
+ // LIBs, provided on the command-line or in a directives section. The second
+ // queue is meant for lower-priority dependent OBJs pulled by a symbol from an
+ // archive. If there are items in both queues, the first one must be fully
+ // executed first before the second queue. This is important to ensure we pull
+ // on archives symbols in the order specified by the MSVC spec.
+ std::list<std::function<void()>> firstTaskQueue;
+ std::list<std::function<void()>> secondaryTaskQueue;
std::vector<StringRef> filePaths;
std::vector<MemoryBufferRef> resources;
diff --git a/lld/COFF/InputFiles.cpp b/lld/COFF/InputFiles.cpp
index 42cdd1cf3b6c2..f7639729d86ca 100644
--- a/lld/COFF/InputFiles.cpp
+++ b/lld/COFF/InputFiles.cpp
@@ -28,6 +28,7 @@
#include "llvm/LTO/LTO.h"
#include "llvm/Object/Binary.h"
#include "llvm/Object/COFF.h"
+#include "llvm/Object/WindowsMachineFlag.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/Error.h"
@@ -93,6 +94,33 @@ static bool ignoredSymbolName(StringRef name) {
return name == "@feat.00" || name == "@comp.id";
}
+static bool compatibleMachineType(COFFLinkerContext &ctx, MachineTypes mt) {
+ if (mt == IMAGE_FILE_MACHINE_UNKNOWN)
+ return true;
+ switch (ctx.config.machine) {
+ case ARM64:
+ return mt == ARM64 || mt == ARM64X;
+ case ARM64EC:
+ return COFF::isArm64EC(mt) || mt == AMD64;
+ case ARM64X:
+ return COFF::isAnyArm64(mt) || mt == AMD64;
+ default:
+ return ctx.config.machine == mt;
+ }
+}
+
+static void setMachine(InputFile *file, COFFLinkerContext &ctx) {
+ MachineTypes mt = file->getMachineType();
+ if (ctx.config.machine == IMAGE_FILE_MACHINE_UNKNOWN) {
+ ctx.config.machine = mt;
+ ctx.driver.addWinSysRootLibSearchPaths();
+ } else if (!compatibleMachineType(ctx, mt)) {
+ error(toString(file) + ": machine type " + machineToStr(mt) +
+ " conflicts with " + machineToStr(ctx.config.machine));
+ return;
+ }
+}
+
ArchiveFile::ArchiveFile(COFFLinkerContext &ctx, MemoryBufferRef m)
: InputFile(ctx, ArchiveKind, m, /*lazy=*/true) {
static unsigned Order = 0;
@@ -165,6 +193,7 @@ void ObjFile::parse() {
} else {
fatal(toString(this) + " is not a COFF file");
}
+ setMachine(this, ctx);
// Read section and symbol tables.
initializeChunks();
@@ -216,6 +245,7 @@ SectionChunk *ObjFile::readSection(uint32_t sectionNumber,
ArrayRef<uint8_t> data;
cantFail(coffObj->getSectionContents(sec, data));
directives = StringRef((const char *)data.data(), data.size());
+ ctx.driver.parseDirectives(this);
return nullptr;
}
@@ -1025,6 +1055,7 @@ BitcodeFile::BitcodeFile(COFFLinkerContext &ctx, MemoryBufferRef mb,
utostr(offsetInArchive)));
obj = check(lto::InputFile::create(mbref));
+ setMachine(this, ctx);
}
BitcodeFile::~BitcodeFile() = default;
@@ -1032,6 +1063,9 @@ BitcodeFile::~BitcodeFile() = default;
void BitcodeFile::parse() {
llvm::StringSaver &saver = lld::saver();
+ directives = saver.save(obj->getCOFFLinkerOpts());
+ ctx.driver.parseDirectives(this);
+
std::vector<std::pair<Symbol *, bool>> comdat(obj->getComdatTable().size());
for (size_t i = 0; i != obj->getComdatTable().size(); ++i)
// FIXME: Check nodeduplicate
@@ -1089,7 +1123,6 @@ void BitcodeFile::parse() {
if (objSym.isUsed())
ctx.config.gcroot.push_back(sym);
}
- directives = saver.save(obj->getCOFFLinkerOpts());
}
void BitcodeFile::parseLazy() {
@@ -1143,6 +1176,7 @@ void DLLFile::parse() {
error(toString(this) + " is not a COFF file");
return;
}
+ setMachine(this, ctx);
if (!coffObj->getPE32Header() && !coffObj->getPE32PlusHeader()) {
error(toString(this) + " is not a PE-COFF executable");
diff --git a/lld/COFF/SymbolTable.cpp b/lld/COFF/SymbolTable.cpp
index 414040dd67c0e..f45f68e22153f 100644
--- a/lld/COFF/SymbolTable.cpp
+++ b/lld/COFF/SymbolTable.cpp
@@ -19,7 +19,6 @@
#include "llvm/DebugInfo/DIContext.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/LTO/LTO.h"
-#include "llvm/Object/WindowsMachineFlag.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include <utility>
@@ -34,21 +33,6 @@ StringRef ltrim1(StringRef s, const char *chars) {
return s;
}
-static bool compatibleMachineType(COFFLinkerContext &ctx, MachineTypes mt) {
- if (mt == IMAGE_FILE_MACHINE_UNKNOWN)
- return true;
- switch (ctx.config.machine) {
- case ARM64:
- return mt == ARM64 || mt == ARM64X;
- case ARM64EC:
- return COFF::isArm64EC(mt) || mt == AMD64;
- case ARM64X:
- return COFF::isAnyArm64(mt) || mt == AMD64;
- default:
- return ctx.config.machine == mt;
- }
-}
-
void SymbolTable::addFile(InputFile *file) {
log("Reading " + toString(file));
if (file->lazy) {
@@ -72,18 +56,6 @@ void SymbolTable::addFile(InputFile *file) {
ctx.importFileInstances.push_back(f);
}
}
-
- MachineTypes mt = file->getMachineType();
- if (ctx.config.machine == IMAGE_FILE_MACHINE_UNKNOWN) {
- ctx.config.machine = mt;
- ctx.driver.addWinSysRootLibSearchPaths();
- } else if (!compatibleMachineType(ctx, mt)) {
- error(toString(file) + ": machine type " + machineToStr(mt) +
- " conflicts with " + machineToStr(ctx.config.machine));
- return;
- }
-
- ctx.driver.parseDirectives(file);
}
static void errorOrWarn(const Twine &s, bool forceUnresolved) {
@@ -636,6 +608,8 @@ Symbol *SymbolTable::addUndefined(StringRef name, InputFile *f,
if (s != selected)
memcpy(s, selected, sizeof(SymbolUnion));
*lazyNode(s) = LazyIntrusiveNode();
+ if (!isa<BitcodeFile>(f))
+ s->isUsedInRegularObj = true;
return s;
}
// We're placing a undefined symbol from a command-line OBJ.
>From 114870b6b8e676319c0abb745202e93cc9c547eb Mon Sep 17 00:00:00 2001
From: Alexandre Ganea <aganea at havenstudios.com>
Date: Tue, 19 Mar 2024 16:13:30 -0400
Subject: [PATCH 08/14] Fix chaining with pending load symbols
---
lld/COFF/SymbolTable.cpp | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/lld/COFF/SymbolTable.cpp b/lld/COFF/SymbolTable.cpp
index f45f68e22153f..673b730235443 100644
--- a/lld/COFF/SymbolTable.cpp
+++ b/lld/COFF/SymbolTable.cpp
@@ -648,9 +648,9 @@ void SymbolTable::addLazyArchive(ArchiveFile *f, const Archive::Symbol &sym) {
replaceSymbol<LazyArchive>(s, f, sym);
return;
}
- if (auto *n = lazyNode(s)) {
- assert(!s->pendingArchiveLoad);
- chainLazy<LazyArchive>(n, f, sym);
+ if (auto *node = lazyNode(s)) {
+ if (!s->pendingArchiveLoad)
+ chainLazy<LazyArchive>(node, f, sym);
return;
}
auto *u = dyn_cast<Undefined>(s);
@@ -668,8 +668,8 @@ void SymbolTable::addLazyObject(InputFile *f, StringRef n) {
return;
}
if (auto *node = lazyNode(s)) {
- assert(!s->pendingArchiveLoad);
- chainLazy<LazyObject>(node, f, n);
+ if (!s->pendingArchiveLoad)
+ chainLazy<LazyObject>(node, f, n);
return;
}
auto *u = dyn_cast<Undefined>(s);
>From 0ece3207b52b2462f6e86b50c8418c24a42a4b5c Mon Sep 17 00:00:00 2001
From: Alexandre Ganea <aganea at havenstudios.com>
Date: Mon, 8 Apr 2024 19:43:04 -0400
Subject: [PATCH 09/14] Add /DEFAULTLIB tests
---
lld/test/COFF/lib-searching-behavior.s | 34 +++++++++++++++++++++++---
1 file changed, 31 insertions(+), 3 deletions(-)
diff --git a/lld/test/COFF/lib-searching-behavior.s b/lld/test/COFF/lib-searching-behavior.s
index 95bc580b3da4a..b9f49739016f6 100644
--- a/lld/test/COFF/lib-searching-behavior.s
+++ b/lld/test/COFF/lib-searching-behavior.s
@@ -1,6 +1,6 @@
# REQUIRES: x86
-# This test ensures that we're following the MSVC symbol searching behvior described in:
+# This test ensures that we're following the MSVC symbol searching behavior described in:
# https://learn.microsoft.com/en-us/cpp/build/reference/link-input-files?view=msvc-170
# "Object files on the command line are processed in the order they appear on the command line.
# Libraries are searched in command line order as well, with the following caveat: Symbols that
@@ -8,7 +8,7 @@
# first, and then the following libraries from the command line and /DEFAULTLIB (Specify default
# library) directives, and then to any libraries at the beginning of the command line."
-# RUN: echo -e ".intel_syntax noprefix\n.globl libfunc\n.text\nlibfunc:\nmov eax, 1\nret\n.section .drectve\n.ascii \"/EXPORT:libfunc\"" > %t.lib.s
+# RUN: echo -e ".intel_syntax noprefix\n.globl libfunc\n.text\nlibfunc:\nmov eax, 1\nret\n.section .drectve\n.ascii \" /EXPORT:libfunc\"" > %t.lib.s
# RUN: llvm-mc -triple=x86_64-pc-windows-msvc %t.lib.s -filetype=obj -o %t.lib.o
# RUN: lld-link -dll -out:%t.lib.dll -entry:libfunc %t.lib.o -implib:%t.lib.dll.a
@@ -79,11 +79,39 @@
# RUN: llvm-ar rcs %t.test.a %t.test1.o %t.test2.o
# RUN: echo -e ".globl main\n.text\nmain:\ncall test\ncall helper\nret" > %t.main2.s
-# RUN: llvm-mc -triple=x86_64-pc-windows-msvc %s -filetype=obj -o %t.main2.o
+# RUN: llvm-mc -triple=x86_64-pc-windows-msvc %t.main2.s -filetype=obj -o %t.main2.o
# RUN: lld-link -out:%t.main.exe -entry:main %t.main2.o %t.helper.a %t.test.a 2>&1 | FileCheck --allow-empty --check-prefix=LIB-TWO %s
# LIB-TWO-NOT: duplicate symbol:
+
+# Test pulling symbols from /DEFAULTLIB archives. These archives should come
+# after all the other archives passed explictly on the command-line.
+
+# RUN: echo -e ".intel_syntax noprefix\n.globl libfunc\n.text\nlibfunc:\nmov eax, 3\nret" > %t.deflib.s
+# RUN: llvm-mc -triple=x86_64-pc-windows-msvc %t.deflib.s -filetype=obj -o %t.deflib.o
+# RUN: llvm-ar rcs %t.deflib.a %t.deflib.o
+
+# RUN: lld-link -out:%t.main.exe -entry:main %t.main2.o %t.helper1.a /DEFAULTLIB:%t.deflib.a %t.test.a 2>&1 | FileCheck --allow-empty --check-prefix=LIB-TWO %s
+
+# RUN: llvm-ar rcs %t.test1.a %t.test1.o
+# RUN: lld-link -out:%t.main.exe -entry:main %t.main2.o %t.test1.a /DEFAULTLIB:%t.deflib.a %t.helper.a 2>&1 | FileCheck --allow-empty --check-prefix=LIB-TWO %s
+
+
+# Test implicit /DEFAULTLIB from .drectve sections. These archives should come
+# after all the other archives passed explictly on the command-line, and are
+# added dynamically while possibly parsing an existing OBJ file.
+
+# RUN: echo -e -n ".intel_syntax noprefix\n.globl test\n.text\ntest:\ncall libfunc\nret\n.section .drectve\n.ascii \" /DEFAULTLIB:" > %t.lib.s
+# RUN: echo -n "%/t.deflib.a" >> %t.lib.s
+# RUN: echo -e "\"" >> %t.lib.s
+# RUN: llvm-mc -triple=x86_64-pc-windows-msvc %t.lib.s -filetype=obj -o %t.lib.o
+# RUN: llvm-ar rcs %t.lib.a %t.lib.o
+
+# RUN: lld-link -out:%t.main.exe -entry:main %t.main2.o %t.helper.a %t.lib.a 2>&1 | FileCheck --allow-empty --check-prefix=LIB-TWO %s
+# RUN: lld-link -out:%t.main.exe -entry:main %t.main2.o %t.lib.a %t.helper1.a 2>&1 | FileCheck --allow-empty --check-prefix=LIB-TWO %s
+
+
.globl main
.text
main:
>From 4d6fcd842a4c6a7886971c4b193ed6d649032b45 Mon Sep 17 00:00:00 2001
From: Alexandre Ganea <aganea at havenstudios.com>
Date: Mon, 8 Apr 2024 19:43:31 -0400
Subject: [PATCH 10/14] Review fixes
---
lld/COFF/SymbolTable.cpp | 67 +++++++++++++++++-------------
lld/test/COFF/duplicate-imp-func.s | 2 +-
2 files changed, 40 insertions(+), 29 deletions(-)
diff --git a/lld/COFF/SymbolTable.cpp b/lld/COFF/SymbolTable.cpp
index 673b730235443..49e5e4a92f4c6 100644
--- a/lld/COFF/SymbolTable.cpp
+++ b/lld/COFF/SymbolTable.cpp
@@ -537,7 +537,7 @@ std::pair<Symbol *, bool> SymbolTable::insert(StringRef name, InputFile *file) {
return result;
}
-static LazyIntrusiveNode *lazyNode(Symbol *s) {
+static LazyIntrusiveNode *getLazyNode(Symbol *s) {
if (auto *sym = dyn_cast<LazyArchive>(s))
return &sym->node;
if (auto *sym = dyn_cast<LazyObject>(s))
@@ -545,9 +545,7 @@ static LazyIntrusiveNode *lazyNode(Symbol *s) {
return nullptr;
}
-static ArchiveFile *lazyParent(InputFile *f) {
- if (!f)
- return nullptr;
+static ArchiveFile *getLazyParent(InputFile *f) {
if (auto *obj = dyn_cast<ObjFile>(f))
return obj->parent;
if (auto *obj = dyn_cast<BitcodeFile>(f))
@@ -555,11 +553,11 @@ static ArchiveFile *lazyParent(InputFile *f) {
return nullptr;
}
-static ArchiveFile *lazyArchive(Symbol *s) {
+static ArchiveFile *getLazyArchive(Symbol *s) {
if (auto *sym = dyn_cast<LazyArchive>(s))
return sym->file;
if (auto *sym = dyn_cast<LazyObject>(s))
- return lazyParent(sym->file);
+ return getLazyParent(sym->file);
return nullptr;
}
@@ -573,19 +571,32 @@ static ArchiveFile *lazyArchive(Symbol *s) {
// that library first, and then the following libraries from the command
// line and /DEFAULTLIB (Specify default library) directives, and then
// to any libraries at the beginning of the command line."
-static Symbol *searchArchiveSymbol(Symbol *s, ArchiveFile *pivot) {
+static Symbol *lookupLazy(Symbol *frontSym, ArchiveFile *requestingArchive) {
auto &Alloc = getSpecificAllocSingleton<SymbolUnion>().Allocator;
- Symbol *curr = s;
+ Symbol *currentSym = frontSym;
for (;;) {
- if (lazyArchive(curr)->CmdLineIndex >= pivot->CmdLineIndex)
- return curr;
- uint32_t next = lazyNode(curr)->next;
- if (!next)
+ ArchiveFile *currentArchive = getLazyArchive(currentSym);
+ assert(currentArchive && "Unhandled lazy archive");
+ if (currentArchive->CmdLineIndex >= requestingArchive->CmdLineIndex)
+ return currentSym;
+ LazyIntrusiveNode *node = getLazyNode(currentSym);
+ assert(node && "Unhandled lazy node");
+ if (!node->next)
break;
- curr = reinterpret_cast<LazyArchive *>(
- Alloc.fromAlignedIndex<SymbolUnion>(next));
+ currentSym = reinterpret_cast<LazyArchive *>(
+ Alloc.fromAlignedIndex<SymbolUnion>(node->next));
}
- return s;
+ return frontSym;
+}
+
+static void collapseLazy(Symbol *frontSym, Symbol *selected, InputFile *f) {
+ if (frontSym != selected)
+ memcpy(frontSym, selected, sizeof(SymbolUnion));
+ LazyIntrusiveNode *node = getLazyNode(frontSym);
+ assert(node && "Unhandled lazy node");
+ *node = LazyIntrusiveNode();
+ if (!isa<BitcodeFile>(f))
+ frontSym->isUsedInRegularObj = true;
}
Symbol *SymbolTable::addUndefined(StringRef name, InputFile *f,
@@ -598,18 +609,16 @@ Symbol *SymbolTable::addUndefined(StringRef name, InputFile *f,
if (s->isLazy()) {
if (s->pendingArchiveLoad)
return s;
- if (ArchiveFile *parent = lazyParent(f)) {
- // We're placing a undefined symbol from an archive OBJ. The rules are
- // different than regular OBJs on the command-line.
- Symbol *selected = searchArchiveSymbol(s, parent);
+ if (ArchiveFile *parent = getLazyParent(f)) {
+ // Lookup the most suitable undefined symbol exposed by an archive OBJ.
+ // The rules are different than regular OBJs on the command-line (see
+ // above).
+ Symbol *selected = lookupLazy(s, parent);
forceLazy(selected);
+
// Now that we have selected a symbol, we don't need the linked list of
// `LazyArchive`s anymore. Collapse to the selected symbol.
- if (s != selected)
- memcpy(s, selected, sizeof(SymbolUnion));
- *lazyNode(s) = LazyIntrusiveNode();
- if (!isa<BitcodeFile>(f))
- s->isUsedInRegularObj = true;
+ collapseLazy(s, selected, f);
return s;
}
// We're placing a undefined symbol from a command-line OBJ.
@@ -619,7 +628,7 @@ Symbol *SymbolTable::addUndefined(StringRef name, InputFile *f,
}
// This creates a linked list of archives where a specific symbol was seen.
-// We later walk that list if a undefined symbol needs to be resolved from an
+// We later walk that list if an undefined symbol needs to be resolved from an
// archive OBJ.
template <typename T, typename... ArgT>
static void chainLazy(LazyIntrusiveNode *front, ArgT &&...arg) {
@@ -636,7 +645,9 @@ static void chainLazy(LazyIntrusiveNode *front, ArgT &&...arg) {
if (front->last) {
Symbol *last = reinterpret_cast<Symbol *>(
Alloc.fromAlignedIndex<SymbolUnion>(front->last));
- lazyNode(last)->next = index;
+ LazyIntrusiveNode *node = getLazyNode(last);
+ assert(node && "Unhandled lazy node");
+ node->next = index;
}
front->last = index;
}
@@ -648,7 +659,7 @@ void SymbolTable::addLazyArchive(ArchiveFile *f, const Archive::Symbol &sym) {
replaceSymbol<LazyArchive>(s, f, sym);
return;
}
- if (auto *node = lazyNode(s)) {
+ if (auto *node = getLazyNode(s)) {
if (!s->pendingArchiveLoad)
chainLazy<LazyArchive>(node, f, sym);
return;
@@ -667,7 +678,7 @@ void SymbolTable::addLazyObject(InputFile *f, StringRef n) {
replaceSymbol<LazyObject>(s, f, n);
return;
}
- if (auto *node = lazyNode(s)) {
+ if (auto *node = getLazyNode(s)) {
if (!s->pendingArchiveLoad)
chainLazy<LazyObject>(node, f, n);
return;
diff --git a/lld/test/COFF/duplicate-imp-func.s b/lld/test/COFF/duplicate-imp-func.s
index 631c714c951f7..bd173483a9cc3 100644
--- a/lld/test/COFF/duplicate-imp-func.s
+++ b/lld/test/COFF/duplicate-imp-func.s
@@ -28,7 +28,7 @@
# Once the import library member from %t.lib.dll.a gets loaded, libfunc
# and __imp_libfunc already are defined.
-# This test should now succeed since we're following the MSVC symbol searching behvior described in:
+# This test should now succeed since we're following the MSVC symbol searching behavior described in:
# https://learn.microsoft.com/en-us/cpp/build/reference/link-input-files?view=msvc-170
# In this case, the linker will select the libfunc symbol in %t.helper.a
# RUN: lld-link -lldmingw -out:%t.main.exe -entry:main %t.main.o %t.lib.dll.a %t.helper.a
>From 87a90c3a1a03c4dad02874310425b5f2e83ad60d Mon Sep 17 00:00:00 2001
From: Alexandre Ganea <aganea at havenstudios.com>
Date: Mon, 8 Apr 2024 19:46:11 -0400
Subject: [PATCH 11/14] More review fixes
---
lld/COFF/Driver.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp
index 071c9d1bdaf59..da6ccbd3c8681 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -369,7 +369,7 @@ void LinkerDriver::enqueueArchiveMember(const Archive::Child &c,
void LinkerDriver::enqueueLazyFile(InputFile *file) {
enqueueSecondaryTask([=]() {
- // Once it has been enqued, it cannot be lazy anymore.
+ // Once it has been enqueued, it cannot be lazy anymore.
file->lazy = false;
ctx.symtab.addFile(file);
});
>From c2e7c7856894d48b4a7900cab936e8cc1ab2e26c Mon Sep 17 00:00:00 2001
From: Alexandre Ganea <aganea at havenstudios.com>
Date: Mon, 8 Apr 2024 19:52:01 -0400
Subject: [PATCH 12/14] clang-format
---
lld/COFF/InputFiles.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lld/COFF/InputFiles.h b/lld/COFF/InputFiles.h
index 7070f51fdf78a..77cba8b0eac05 100644
--- a/lld/COFF/InputFiles.h
+++ b/lld/COFF/InputFiles.h
@@ -112,7 +112,7 @@ class ArchiveFile : public InputFile {
public:
explicit ArchiveFile(COFFLinkerContext &ctx, MemoryBufferRef m);
static bool classof(const InputFile *f) { return f->kind() == ArchiveKind; }
- void parse() override{};
+ void parse() override {};
void parseLazy();
// Enqueues an archive member load for the given symbol. If we've already
>From 0f5d47ef8d05bd38c5e78b4c7f0edfe5d32289cf Mon Sep 17 00:00:00 2001
From: Alexandre Ganea <aganea at havenstudios.com>
Date: Mon, 8 Apr 2024 19:59:56 -0400
Subject: [PATCH 13/14] Typo
---
lld/COFF/Driver.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp
index da6ccbd3c8681..a2c7ccc4fab7d 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -511,7 +511,7 @@ void LinkerDriver::parseDirectives(InputFile *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 property in the symbol table.
+ // provided by /DEFAULTLIB archives are linked properly in the symbol table.
executeFirstQueue(firstTaskQueue);
}
>From baeca96ccbe22ca0b0bc21376353080222a260d4 Mon Sep 17 00:00:00 2001
From: Alexandre Ganea <aganea at havenstudios.com>
Date: Mon, 17 Jun 2024 16:05:21 -0400
Subject: [PATCH 14/14] Fix lazy node casting in the presence of wrapped
symbols (MinGW -wrap)
---
lld/COFF/SymbolTable.cpp | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/lld/COFF/SymbolTable.cpp b/lld/COFF/SymbolTable.cpp
index 49e5e4a92f4c6..8f22bdd222e24 100644
--- a/lld/COFF/SymbolTable.cpp
+++ b/lld/COFF/SymbolTable.cpp
@@ -538,25 +538,25 @@ std::pair<Symbol *, bool> SymbolTable::insert(StringRef name, InputFile *file) {
}
static LazyIntrusiveNode *getLazyNode(Symbol *s) {
- if (auto *sym = dyn_cast<LazyArchive>(s))
+ if (auto *sym = dyn_cast_if_present<LazyArchive>(s))
return &sym->node;
- if (auto *sym = dyn_cast<LazyObject>(s))
+ if (auto *sym = dyn_cast_if_present<LazyObject>(s))
return &sym->node;
return nullptr;
}
static ArchiveFile *getLazyParent(InputFile *f) {
- if (auto *obj = dyn_cast<ObjFile>(f))
+ if (auto *obj = dyn_cast_if_present<ObjFile>(f))
return obj->parent;
- if (auto *obj = dyn_cast<BitcodeFile>(f))
+ if (auto *obj = dyn_cast_if_present<BitcodeFile>(f))
return obj->parent;
return nullptr;
}
static ArchiveFile *getLazyArchive(Symbol *s) {
- if (auto *sym = dyn_cast<LazyArchive>(s))
+ if (auto *sym = dyn_cast_if_present<LazyArchive>(s))
return sym->file;
- if (auto *sym = dyn_cast<LazyObject>(s))
+ if (auto *sym = dyn_cast_if_present<LazyObject>(s))
return getLazyParent(sym->file);
return nullptr;
}
More information about the llvm-commits
mailing list