[lld] [LLD][COFF] Handle --start-lib/--end-lib group in the same way as other archives (PR #136496)
Alexandre Ganea via llvm-commits
llvm-commits at lists.llvm.org
Fri May 2 14:46:36 PDT 2025
https://github.com/aganea updated https://github.com/llvm/llvm-project/pull/136496
>From c541eee802d859bc884ae25c6e717bc6835849c3 Mon Sep 17 00:00:00 2001
From: Alexandre Ganea <alex_toresh at yahoo.fr>
Date: Sun, 20 Apr 2025 12:10:36 -0400
Subject: [PATCH 1/2] [LLD][COFF] Handle --start-lib/--end-lib group in the
same way as other archives
---
lld/COFF/Driver.cpp | 100 +++++++++++++++++++++++----------------
lld/COFF/Driver.h | 18 ++++---
lld/COFF/InputFiles.cpp | 44 +++++++++++------
lld/COFF/InputFiles.h | 58 ++++++++++++++++-------
lld/COFF/SymbolTable.cpp | 12 ++---
5 files changed, 144 insertions(+), 88 deletions(-)
diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp
index e3ff647209e72..9487868f109fa 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -200,28 +200,13 @@ static bool compatibleMachineType(COFFLinkerContext &ctx, MachineTypes mt) {
}
}
-void LinkerDriver::addFile(InputFile *file) {
- Log(ctx) << "Reading " << toString(file);
- if (file->lazy) {
- if (auto *f = dyn_cast<BitcodeFile>(file))
- f->parseLazy();
- else
- cast<ObjFile>(file)->parseLazy();
- } else {
- file->parse();
- if (auto *f = dyn_cast<ObjFile>(file)) {
- ctx.objFileInstances.push_back(f);
- } else if (auto *f = dyn_cast<BitcodeFile>(file)) {
- if (ltoCompilationDone) {
- Err(ctx) << "LTO object file " << toString(file)
- << " linked in after "
- "doing LTO compilation.";
- }
- f->symtab.bitcodeFileInstances.push_back(f);
- } else if (auto *f = dyn_cast<ImportFile>(file)) {
- ctx.importFileInstances.push_back(f);
- }
+void LinkerDriver::addFile(InputFile *file, CmdLineArchive *inCmdLineArchive) {
+ if (inCmdLineArchive) {
+ inCmdLineArchive->addInputFile(file); // schedule for lazy parsing
+ return;
}
+ Log(ctx) << "Reading " << toString(file);
+ file->maybeParse();
MachineTypes mt = file->getMachineType();
// The ARM64EC target must be explicitly specified and cannot be inferred.
@@ -259,7 +244,8 @@ MemoryBufferRef LinkerDriver::takeBuffer(std::unique_ptr<MemoryBuffer> mb) {
}
void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb,
- bool wholeArchive, bool lazy) {
+ bool wholeArchive,
+ CmdLineArchive *inCmdLineArchive) {
StringRef filename = mb->getBufferIdentifier();
MemoryBufferRef mbref = takeBuffer(std::move(mb));
@@ -267,9 +253,15 @@ void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb,
// File type is detected by contents, not by file extension.
switch (identify_magic(mbref.getBuffer())) {
case file_magic::windows_resource:
+ assert(!inCmdLineArchive &&
+ "Cannot specify a RES file inside a --start-lib/--end-lib group.");
resources.push_back(mbref);
break;
case file_magic::archive:
+ // FIXME: We could later support --start-lib/--end-lib groups, to allow for
+ // "extending" an existing archive/LIB.
+ assert(!inCmdLineArchive &&
+ "Cannot specify a LIB file inside a --start-lib/--end-lib group.");
if (wholeArchive) {
std::unique_ptr<Archive> file =
CHECK(Archive::create(mbref), filename + ": failed to parse archive");
@@ -284,13 +276,15 @@ void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb,
addFile(make<ArchiveFile>(ctx, mbref));
break;
case file_magic::bitcode:
- addFile(BitcodeFile::create(ctx, mbref, "", 0, lazy));
+ addFile(BitcodeFile::create(ctx, mbref, "", 0), inCmdLineArchive);
break;
case file_magic::coff_object:
case file_magic::coff_import_library:
- addFile(ObjFile::create(ctx, mbref, lazy));
+ addFile(ObjFile::create(ctx, mbref), inCmdLineArchive);
break;
case file_magic::pdb:
+ assert(!inCmdLineArchive &&
+ "Cannot specify a PDB file inside a --start-lib/--end-lib group.");
addFile(make<PDBInputFile>(ctx, mbref));
break;
case file_magic::coff_cl_gl_object:
@@ -299,6 +293,9 @@ void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb,
break;
case file_magic::pecoff_executable:
if (ctx.config.mingw) {
+ assert(
+ !inCmdLineArchive &&
+ "Cannot specify a PE/EXE file inside a --start-lib/--end-lib group.");
addFile(make<DLLFile>(ctx.symtab, mbref));
break;
}
@@ -315,7 +312,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,
+ std::optional<std::shared_future<CmdLineArchive *>> inCmdLineArchive) {
auto future = std::make_shared<std::future<MBErrPair>>(
createFutureForFile(std::string(path)));
std::string pathStr = std::string(path);
@@ -354,7 +353,9 @@ void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive, bool lazy) {
else
Err(ctx) << msg << "; did you mean '" << nearest << "'";
} else
- ctx.driver.addBuffer(std::move(mb), wholeArchive, lazy);
+ ctx.driver.addBuffer(std::move(mb), wholeArchive,
+ inCmdLineArchive ? inCmdLineArchive->get()
+ : nullptr);
});
}
@@ -373,8 +374,7 @@ void LinkerDriver::addArchiveBuffer(MemoryBufferRef mb, StringRef symName,
if (magic == file_magic::coff_object) {
obj = ObjFile::create(ctx, mb);
} else if (magic == file_magic::bitcode) {
- obj = BitcodeFile::create(ctx, mb, parentName, offsetInArchive,
- /*lazy=*/false);
+ obj = BitcodeFile::create(ctx, mb, parentName, offsetInArchive);
} else if (magic == file_magic::coff_cl_gl_object) {
Err(ctx) << mb.getBufferIdentifier()
<< ": is not a native COFF file. Recompile without /GL?";
@@ -494,7 +494,7 @@ void LinkerDriver::parseDirectives(InputFile *file) {
break;
case OPT_defaultlib:
if (std::optional<StringRef> path = findLibIfNew(arg->getValue()))
- enqueuePath(*path, false, false);
+ enqueuePath(*path);
break;
case OPT_entry:
if (!arg->getValue()[0])
@@ -2177,32 +2177,50 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
// and OPT_end_lib.
{
llvm::TimeTraceScope timeScope2("Parse & queue inputs");
- bool inLib = false;
+ std::optional<std::shared_future<CmdLineArchive *>> inCmdLineArchive;
for (auto *arg : args) {
switch (arg->getOption().getID()) {
case OPT_end_lib:
- if (!inLib)
+ if (!inCmdLineArchive) {
Err(ctx) << "stray " << arg->getSpelling();
- inLib = false;
+ } else {
+ enqueueTask([=]() { inCmdLineArchive->get()->maybeParse(); });
+ inCmdLineArchive = std::nullopt;
+ }
break;
case OPT_start_lib:
- if (inLib)
+ if (inCmdLineArchive) {
Err(ctx) << "nested " << arg->getSpelling();
- inLib = true;
+ } else {
+ auto a = std::make_shared<std::promise<CmdLineArchive *>>();
+ inCmdLineArchive = a->get_future().share();
+ enqueueTask([&, a]() {
+ // 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.
+ a->set_value(make<CmdLineArchive>(
+ ctx.symtab, MemoryBufferRef({}, "<cmdline-lib>")));
+ });
+ }
break;
case OPT_wholearchive_file:
if (std::optional<StringRef> path = findFileIfNew(arg->getValue()))
- enqueuePath(*path, true, inLib);
+ enqueuePath(*path, true, inCmdLineArchive);
break;
case OPT_INPUT:
if (std::optional<StringRef> path = findFileIfNew(arg->getValue()))
- enqueuePath(*path, isWholeArchive(*path), inLib);
+ enqueuePath(*path, isWholeArchive(*path), inCmdLineArchive);
break;
default:
// Ignore other options.
break;
}
}
+ if (inCmdLineArchive) {
+ Warn(ctx) << "--start-lib with no --end-lib";
+ enqueueTask([=]() { inCmdLineArchive->get()->maybeParse(); });
+ }
}
// Read all input files given via the command line.
@@ -2236,7 +2254,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
// addWinSysRootLibSearchPaths(), which is why they are in a separate loop.
for (auto *arg : args.filtered(OPT_defaultlib))
if (std::optional<StringRef> path = findLibIfNew(arg->getValue()))
- enqueuePath(*path, false, false);
+ enqueuePath(*path);
run();
if (errorCount())
return;
@@ -2553,9 +2571,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>(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)))
symtab.addGCRoot(arg->getValue());
+ }
}
});
} while (run());
@@ -2720,7 +2740,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
// /manifestdependency: enables /manifest unless an explicit /manifest:no is
// also passed.
if (config->manifest == Configuration::Embed)
- addBuffer(createManifestRes(), false, false);
+ addBuffer(createManifestRes(), false);
else if (config->manifest == Configuration::SideBySide ||
(config->manifest == Configuration::Default &&
!config->manifestDependencies.empty()))
diff --git a/lld/COFF/Driver.h b/lld/COFF/Driver.h
index 14c97a98875bf..59fbc97e1910e 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>
@@ -80,7 +81,7 @@ class LinkerDriver {
void linkerMain(llvm::ArrayRef<const char *> args);
- void addFile(InputFile *file);
+ void addFile(InputFile *file, CmdLineArchive *inCmdLineArchive = nullptr);
void addClangLibSearchPaths(const std::string &argv0);
@@ -88,18 +89,23 @@ class LinkerDriver {
void enqueueArchiveMember(const Archive::Child &c, const Archive::Symbol &sym,
StringRef parentName);
- void enqueuePDB(StringRef Path) { enqueuePath(Path, false, false); }
+ void enqueuePDB(StringRef Path) { enqueuePath(Path); }
MemoryBufferRef takeBuffer(std::unique_ptr<MemoryBuffer> mb);
- void enqueuePath(StringRef path, bool wholeArchive, bool lazy);
+ // Schedule a input file for reading.
+ void enqueuePath(StringRef path, bool wholeArchive = false,
+ std::optional<std::shared_future<CmdLineArchive *>>
+ inCmdLineArchive = std::nullopt);
+
+ void pullArm64ECIcallHelper();
// Returns a list of chunks of selected symbols.
std::vector<Chunk *> getChunks() const;
std::unique_ptr<llvm::TarWriter> tar; // for /linkrepro
- void pullArm64ECIcallHelper();
+ bool ltoCompilationDone = false;
private:
// Searches a file from search paths.
@@ -170,7 +176,7 @@ class LinkerDriver {
std::set<std::string> visitedLibs;
void addBuffer(std::unique_ptr<MemoryBuffer> mb, bool wholeArchive,
- bool lazy);
+ CmdLineArchive *inCmdLineArchive = nullptr);
void addArchiveBuffer(MemoryBufferRef mbref, StringRef symName,
StringRef parentName, uint64_t offsetInArchive);
@@ -258,8 +264,6 @@ class LinkerDriver {
// Create export thunks for exported and patchable Arm64EC function symbols.
void createECExportThunks();
void maybeCreateECExportThunk(StringRef name, Symbol *&sym);
-
- bool ltoCompilationDone = false;
};
// Create enum with OPT_xxx values for each option in Options.td
diff --git a/lld/COFF/InputFiles.cpp b/lld/COFF/InputFiles.cpp
index 7fb42bb681939..20c4613aa422e 100644
--- a/lld/COFF/InputFiles.cpp
+++ b/lld/COFF/InputFiles.cpp
@@ -226,8 +226,19 @@ lld::coff::getArchiveMembers(COFFLinkerContext &ctx, Archive *file) {
return v;
}
-ObjFile::ObjFile(SymbolTable &symtab, COFFObjectFile *coffObj, bool lazy)
- : InputFile(symtab, ObjectKind, coffObj->getMemoryBufferRef(), lazy),
+void CmdLineArchive::parse() {
+ for (InputFile *f : files) {
+ if (auto *o = dyn_cast<ObjFile>(f))
+ o->parseLazy();
+ else if (auto *b = dyn_cast<BitcodeFile>(f))
+ b->parseLazy();
+ }
+}
+
+void CmdLineArchive::addInputFile(InputFile *f) { files.push_back(f); }
+
+ObjFile::ObjFile(SymbolTable &symtab, COFFObjectFile *coffObj)
+ : InputFile(symtab, ObjectKind, coffObj->getMemoryBufferRef()),
coffObj(coffObj) {}
ObjFile *ObjFile::create(COFFLinkerContext &ctx, MemoryBufferRef m, bool lazy) {
@@ -241,8 +252,7 @@ ObjFile *ObjFile::create(COFFLinkerContext &ctx, MemoryBufferRef m, bool lazy) {
Fatal(ctx) << m.getBufferIdentifier() << " is not a COFF file";
bin->release();
- return make<ObjFile>(ctx.getSymtab(MachineTypes(obj->getMachine())), obj,
- lazy);
+ return make<ObjFile>(ctx.getSymtab(MachineTypes(obj->getMachine())), obj);
}
void ObjFile::parseLazy() {
@@ -257,8 +267,6 @@ void ObjFile::parseLazy() {
if (coffSym.isAbsolute() && ignoredSymbolName(name))
continue;
symtab.addLazyObject(this, name);
- if (!lazy)
- return;
i += coffSym.getNumberOfAuxSymbols();
}
}
@@ -299,6 +307,8 @@ void ObjFile::initializeECThunks() {
}
void ObjFile::parse() {
+ symtab.ctx.objFileInstances.push_back(this);
+
// Read section and symbol tables.
initializeChunks();
initializeSymbols();
@@ -1201,6 +1211,8 @@ ImportThunkChunk *ImportFile::makeImportThunk() {
}
void ImportFile::parse() {
+ symtab.ctx.importFileInstances.push_back(this);
+
const auto *hdr =
reinterpret_cast<const coff_import_header *>(mb.getBufferStart());
@@ -1307,14 +1319,14 @@ void ImportFile::parse() {
}
BitcodeFile::BitcodeFile(SymbolTable &symtab, MemoryBufferRef mb,
- std::unique_ptr<lto::InputFile> &o, bool lazy)
- : InputFile(symtab, BitcodeKind, mb, lazy) {
+ std::unique_ptr<lto::InputFile> &o)
+ : InputFile(symtab, BitcodeKind, mb) {
obj.swap(o);
}
BitcodeFile *BitcodeFile::create(COFFLinkerContext &ctx, MemoryBufferRef mb,
StringRef archiveName,
- uint64_t offsetInArchive, bool lazy) {
+ uint64_t offsetInArchive) {
std::string path = mb.getBufferIdentifier().str();
if (ctx.config.thinLTOIndexOnly)
path = replaceThinLTOSuffix(mb.getBufferIdentifier(),
@@ -1335,13 +1347,18 @@ BitcodeFile *BitcodeFile::create(COFFLinkerContext &ctx, MemoryBufferRef mb,
utostr(offsetInArchive)));
std::unique_ptr<lto::InputFile> obj = check(lto::InputFile::create(mbref));
- return make<BitcodeFile>(ctx.getSymtab(getMachineType(obj.get())), mb, obj,
- lazy);
+ return make<BitcodeFile>(ctx.getSymtab(getMachineType(obj.get())), mb, obj);
}
BitcodeFile::~BitcodeFile() = default;
void BitcodeFile::parse() {
+ if (symtab.ctx.driver.ltoCompilationDone) {
+ Err(symtab.ctx) << "LTO object file " << toString(this)
+ << " linked in after doing LTO compilation.";
+ }
+ symtab.bitcodeFileInstances.push_back(this);
+
llvm::StringSaver &saver = lld::saver();
std::vector<std::pair<Symbol *, bool>> comdat(obj->getComdatTable().size());
@@ -1406,11 +1423,8 @@ void BitcodeFile::parse() {
void BitcodeFile::parseLazy() {
for (const lto::InputFile::Symbol &sym : obj->symbols())
- if (!sym.isUndefined()) {
+ if (!sym.isUndefined())
symtab.addLazyObject(this, sym.getName());
- if (!lazy)
- return;
- }
}
MachineTypes BitcodeFile::getMachineType(const llvm::lto::InputFile *obj) {
diff --git a/lld/COFF/InputFiles.h b/lld/COFF/InputFiles.h
index 21b9aeef21d4f..09553bb9e56d5 100644
--- a/lld/COFF/InputFiles.h
+++ b/lld/COFF/InputFiles.h
@@ -71,6 +71,7 @@ class InputFile {
public:
enum Kind {
ArchiveKind,
+ CmdLineArchiveKind,
ObjectKind,
PDBKind,
ImportKind,
@@ -83,14 +84,19 @@ class InputFile {
// Returns the filename.
StringRef getName() const { return mb.getBufferIdentifier(); }
- // Reads a file (the constructor doesn't do that).
- virtual void parse() = 0;
-
// Returns the CPU type this file was compiled to.
virtual MachineTypes getMachineType() const {
return IMAGE_FILE_MACHINE_UNKNOWN;
}
+ // Parse the file if it wasn't already parsed.
+ void maybeParse() {
+ if (parsed)
+ return;
+ parsed = true;
+ parse();
+ }
+
MemoryBufferRef mb;
// An archive file name if this file is created from an archive.
@@ -102,17 +108,18 @@ class InputFile {
SymbolTable &symtab;
protected:
- InputFile(SymbolTable &s, Kind k, MemoryBufferRef m, bool lazy = false)
- : mb(m), symtab(s), fileKind(k), lazy(lazy) {}
+ InputFile(SymbolTable &s, Kind k, MemoryBufferRef m)
+ : mb(m), symtab(s), fileKind(k) {}
+
+ // Reads a file (the constructor doesn't do that).
+ virtual void parse() = 0;
StringRef directives;
+ bool parsed = false;
+
private:
const Kind fileKind;
-
-public:
- // True if this is a lazy ObjFile or BitcodeFile.
- bool lazy = false;
};
// .lib or .a file.
@@ -132,16 +139,30 @@ class ArchiveFile : public InputFile {
llvm::DenseSet<uint64_t> seen;
};
+// A synthetic --start-lib/--end-lib archive.
+class CmdLineArchive : public InputFile {
+public:
+ explicit CmdLineArchive(SymbolTable &s, MemoryBufferRef m)
+ : InputFile(s, CmdLineArchiveKind, m) {}
+ static bool classof(const InputFile *f) {
+ return f->kind() == CmdLineArchiveKind;
+ }
+ void parse() override;
+ void addInputFile(InputFile *f);
+
+private:
+ std::vector<InputFile *> files;
+};
+
// .obj or .o file. This may be a member of an archive file.
class ObjFile : public InputFile {
public:
static ObjFile *create(COFFLinkerContext &ctx, MemoryBufferRef mb,
bool lazy = false);
- explicit ObjFile(SymbolTable &symtab, COFFObjectFile *coffObj, bool lazy);
+ explicit ObjFile(SymbolTable &symtab, COFFObjectFile *coffObj);
static bool classof(const InputFile *f) { return f->kind() == ObjectKind; }
void parse() override;
- void parseLazy();
MachineTypes getMachineType() const override;
ArrayRef<Chunk *> getChunks() { return chunks; }
ArrayRef<SectionChunk *> getDebugChunks() { return debugChunks; }
@@ -191,6 +212,9 @@ class ObjFile : public InputFile {
// True if this file was compiled with /guard:ehcont.
bool hasGuardEHCont() { return feat00Flags & 0x4000; }
+ // Add lazy references to the symbols in this file.
+ void parseLazy();
+
// 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
@@ -391,24 +415,24 @@ class ImportFile : public InputFile {
class BitcodeFile : public InputFile {
public:
explicit BitcodeFile(SymbolTable &symtab, MemoryBufferRef mb,
- std::unique_ptr<llvm::lto::InputFile> &obj, bool lazy);
+ std::unique_ptr<llvm::lto::InputFile> &obj);
~BitcodeFile();
static BitcodeFile *create(COFFLinkerContext &ctx, MemoryBufferRef mb,
- StringRef archiveName, uint64_t offsetInArchive,
- bool lazy);
+ StringRef archiveName, uint64_t offsetInArchive);
static bool classof(const InputFile *f) { return f->kind() == BitcodeKind; }
ArrayRef<Symbol *> getSymbols() { return symbols; }
MachineTypes getMachineType() const override {
return getMachineType(obj.get());
}
static MachineTypes getMachineType(const llvm::lto::InputFile *obj);
- void parseLazy();
+ void parse() override;
std::unique_ptr<llvm::lto::InputFile> obj;
-private:
- void parse() override;
+ // Add lazy references to the symbols in this file.
+ void parseLazy();
+private:
std::vector<Symbol *> symbols;
};
diff --git a/lld/COFF/SymbolTable.cpp b/lld/COFF/SymbolTable.cpp
index 8fb0ee4e890d6..020b1b37cdb3c 100644
--- a/lld/COFF/SymbolTable.cpp
+++ b/lld/COFF/SymbolTable.cpp
@@ -47,6 +47,8 @@ static COFFSyncStream errorOrWarn(COFFLinkerContext &ctx) {
// Causes the file associated with a lazy symbol to be linked in.
static void forceLazy(Symbol *s) {
+ if (s->pendingArchiveLoad)
+ return;
s->pendingArchiveLoad = true;
switch (s->kind()) {
case Symbol::Kind::LazyArchiveKind: {
@@ -56,11 +58,6 @@ static void forceLazy(Symbol *s) {
}
case Symbol::Kind::LazyObjectKind: {
InputFile *file = cast<LazyObject>(s)->file;
- // FIXME: Remove this once we resolve all defineds before all undefineds in
- // ObjFile::initializeSymbols().
- if (!file->lazy)
- return;
- file->lazy = false;
file->symtab.ctx.driver.addFile(file);
break;
}
@@ -747,7 +744,6 @@ void SymbolTable::addLazyArchive(ArchiveFile *f, const Archive::Symbol &sym) {
}
void SymbolTable::addLazyObject(InputFile *f, StringRef n) {
- assert(f->lazy);
if (isEC() && !checkLazyECPair<LazyObject>(this, n, f))
return;
auto [s, wasInserted] = insert(n, f);
@@ -759,7 +755,6 @@ void SymbolTable::addLazyObject(InputFile *f, StringRef n) {
if (!u || (u->weakAlias && !u->isECAlias(machine)) || s->pendingArchiveLoad)
return;
s->pendingArchiveLoad = true;
- f->lazy = false;
ctx.driver.addFile(f);
}
@@ -1372,8 +1367,7 @@ void SymbolTable::compileBitcodeFiles() {
lto->add(*f);
for (InputFile *newObj : lto->compile()) {
ObjFile *obj = cast<ObjFile>(newObj);
- obj->parse();
- ctx.objFileInstances.push_back(obj);
+ obj->maybeParse();
}
}
>From a782451d48fb695c6964c08aaeaf9fff6ca22e51 Mon Sep 17 00:00:00 2001
From: Alexandre Ganea <alex_toresh at yahoo.fr>
Date: Fri, 2 May 2025 17:45:23 -0400
Subject: [PATCH 2/2] Change the asserts into warning and add more test
coverage
---
lld/COFF/Driver.cpp | 68 ++++++++++++++++++++++-----------
lld/COFF/Driver.h | 4 +-
lld/COFF/InputFiles.h | 11 ++++--
lld/Common/Args.cpp | 10 +++++
lld/include/lld/Common/Args.h | 11 +++++-
lld/test/COFF/start-lib-warn.ll | 55 ++++++++++++++++++++++++++
6 files changed, 131 insertions(+), 28 deletions(-)
create mode 100644 lld/test/COFF/start-lib-warn.ll
diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp
index 9487868f109fa..1855969c31acd 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -250,18 +250,23 @@ void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb,
MemoryBufferRef mbref = takeBuffer(std::move(mb));
+ auto maybePrintWarning = [&](StringRef type, StringRef message) {
+ if (inCmdLineArchive)
+ Warn(ctx) << type << " file provided between "
+ << inCmdLineArchive->startLibArg << "/"
+ << inCmdLineArchive->endLibArg << " " << message;
+ };
+
// File type is detected by contents, not by file extension.
switch (identify_magic(mbref.getBuffer())) {
case file_magic::windows_resource:
- assert(!inCmdLineArchive &&
- "Cannot specify a RES file inside a --start-lib/--end-lib group.");
+ maybePrintWarning(".res", "will not be lazy");
resources.push_back(mbref);
break;
case file_magic::archive:
// FIXME: We could later support --start-lib/--end-lib groups, to allow for
// "extending" an existing archive/LIB.
- assert(!inCmdLineArchive &&
- "Cannot specify a LIB file inside a --start-lib/--end-lib group.");
+ maybePrintWarning(".lib/.a", "has no effect");
if (wholeArchive) {
std::unique_ptr<Archive> file =
CHECK(Archive::create(mbref), filename + ": failed to parse archive");
@@ -283,8 +288,7 @@ void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb,
addFile(ObjFile::create(ctx, mbref), inCmdLineArchive);
break;
case file_magic::pdb:
- assert(!inCmdLineArchive &&
- "Cannot specify a PDB file inside a --start-lib/--end-lib group.");
+ maybePrintWarning(".pdb", "will not be lazy");
addFile(make<PDBInputFile>(ctx, mbref));
break;
case file_magic::coff_cl_gl_object:
@@ -293,9 +297,7 @@ void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb,
break;
case file_magic::pecoff_executable:
if (ctx.config.mingw) {
- assert(
- !inCmdLineArchive &&
- "Cannot specify a PE/EXE file inside a --start-lib/--end-lib group.");
+ maybePrintWarning(".dll", "will not be lazy");
addFile(make<DLLFile>(ctx.symtab, mbref));
break;
}
@@ -314,7 +316,7 @@ void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb,
void LinkerDriver::enqueuePath(
StringRef path, bool wholeArchive,
- std::optional<std::shared_future<CmdLineArchive *>> inCmdLineArchive) {
+ std::optional<std::shared_ptr<CmdLineArchive *>> inCmdLineArchive) {
auto future = std::make_shared<std::future<MBErrPair>>(
createFutureForFile(std::string(path)));
std::string pathStr = std::string(path);
@@ -354,8 +356,7 @@ void LinkerDriver::enqueuePath(
Err(ctx) << msg << "; did you mean '" << nearest << "'";
} else
ctx.driver.addBuffer(std::move(mb), wholeArchive,
- inCmdLineArchive ? inCmdLineArchive->get()
- : nullptr);
+ inCmdLineArchive ? **inCmdLineArchive : nullptr);
});
}
@@ -2172,19 +2173,38 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
return false;
};
+ // Store start_lib/end_lib arguments in order to render dignostics in the
+ // same way the flags are written on the command line.
+ llvm::opt::Arg *startLibArg = nullptr;
+ llvm::opt::Arg *endLibArg = nullptr;
+ auto endLibSpelling = [&]() {
+ return endLibArg ? endLibArg->getSpelling()
+ : lld::args::getOptionSpellingLikeArg(
+ ctx.optTable, OPT_end_lib, startLibArg, ctx.saver);
+ };
+
// 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.
{
llvm::TimeTraceScope timeScope2("Parse & queue inputs");
- std::optional<std::shared_future<CmdLineArchive *>> inCmdLineArchive;
+ std::optional<std::shared_ptr<CmdLineArchive *>> inCmdLineArchive;
+ auto close = [&]() {
+ enqueueTask([=]() {
+ assert(inCmdLineArchive);
+ if (CmdLineArchive *a = **inCmdLineArchive)
+ a->maybeParse();
+ });
+ };
+
for (auto *arg : args) {
switch (arg->getOption().getID()) {
case OPT_end_lib:
if (!inCmdLineArchive) {
Err(ctx) << "stray " << arg->getSpelling();
} else {
- enqueueTask([=]() { inCmdLineArchive->get()->maybeParse(); });
+ endLibArg = arg;
+ close();
inCmdLineArchive = std::nullopt;
}
break;
@@ -2192,15 +2212,16 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
if (inCmdLineArchive) {
Err(ctx) << "nested " << arg->getSpelling();
} else {
- auto a = std::make_shared<std::promise<CmdLineArchive *>>();
- inCmdLineArchive = a->get_future().share();
- enqueueTask([&, a]() {
+ startLibArg = arg;
+ inCmdLineArchive = std::make_shared<CmdLineArchive *>();
+ enqueueTask([&, inCmdLineArchive, startLibArg, endLibArg]() {
// 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.
- a->set_value(make<CmdLineArchive>(
- ctx.symtab, MemoryBufferRef({}, "<cmdline-lib>")));
+ **inCmdLineArchive = make<CmdLineArchive>(
+ ctx.symtab, MemoryBufferRef({}, "<cmdline-lib>"),
+ startLibArg->getSpelling(), endLibSpelling());
});
}
break;
@@ -2218,8 +2239,11 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
}
}
if (inCmdLineArchive) {
- Warn(ctx) << "--start-lib with no --end-lib";
- enqueueTask([=]() { inCmdLineArchive->get()->maybeParse(); });
+ StringRef startLib = startLibArg->getSpelling();
+ Warn(ctx) << startLib << " without " << endLibSpelling()
+ << "\nNOTE: all files provided after " << startLib
+ << " were lazy.";
+ close();
}
}
@@ -2740,7 +2764,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
// /manifestdependency: enables /manifest unless an explicit /manifest:no is
// also passed.
if (config->manifest == Configuration::Embed)
- addBuffer(createManifestRes(), false);
+ addBuffer(createManifestRes());
else if (config->manifest == Configuration::SideBySide ||
(config->manifest == Configuration::Default &&
!config->manifestDependencies.empty()))
diff --git a/lld/COFF/Driver.h b/lld/COFF/Driver.h
index 59fbc97e1910e..203cab930536e 100644
--- a/lld/COFF/Driver.h
+++ b/lld/COFF/Driver.h
@@ -95,7 +95,7 @@ class LinkerDriver {
// Schedule a input file for reading.
void enqueuePath(StringRef path, bool wholeArchive = false,
- std::optional<std::shared_future<CmdLineArchive *>>
+ std::optional<std::shared_ptr<CmdLineArchive *>>
inCmdLineArchive = std::nullopt);
void pullArm64ECIcallHelper();
@@ -175,7 +175,7 @@ class LinkerDriver {
std::set<std::string> visitedLibs;
- void addBuffer(std::unique_ptr<MemoryBuffer> mb, bool wholeArchive,
+ void addBuffer(std::unique_ptr<MemoryBuffer> mb, bool wholeArchive = false,
CmdLineArchive *inCmdLineArchive = nullptr);
void addArchiveBuffer(MemoryBufferRef mbref, StringRef symName,
StringRef parentName, uint64_t offsetInArchive);
diff --git a/lld/COFF/InputFiles.h b/lld/COFF/InputFiles.h
index 09553bb9e56d5..6688a8344eb1a 100644
--- a/lld/COFF/InputFiles.h
+++ b/lld/COFF/InputFiles.h
@@ -139,17 +139,22 @@ class ArchiveFile : public InputFile {
llvm::DenseSet<uint64_t> seen;
};
-// A synthetic --start-lib/--end-lib archive.
+// A synthetic -start-lib/-end-lib archive.
class CmdLineArchive : public InputFile {
public:
- explicit CmdLineArchive(SymbolTable &s, MemoryBufferRef m)
- : InputFile(s, CmdLineArchiveKind, m) {}
+ explicit CmdLineArchive(SymbolTable &s, MemoryBufferRef m, StringRef startLib,
+ StringRef endLib)
+ : InputFile(s, CmdLineArchiveKind, m), startLibArg(startLib),
+ endLibArg(endLib) {}
static bool classof(const InputFile *f) {
return f->kind() == CmdLineArchiveKind;
}
void parse() override;
void addInputFile(InputFile *f);
+ StringRef startLibArg;
+ StringRef endLibArg;
+
private:
std::vector<InputFile *> files;
};
diff --git a/lld/Common/Args.cpp b/lld/Common/Args.cpp
index 5546b2aece641..e64769e09d13f 100644
--- a/lld/Common/Args.cpp
+++ b/lld/Common/Args.cpp
@@ -90,3 +90,13 @@ StringRef lld::args::getFilenameWithoutExe(StringRef path) {
return sys::path::stem(path);
return sys::path::filename(path);
}
+
+StringRef lld::args::getOptionSpellingLikeArg(llvm::opt::OptTable &optTable,
+ llvm::opt::OptSpecifier opt,
+ llvm::opt::Arg *arg,
+ llvm::StringSaver &saver) {
+ StringRef prefix = arg->getSpelling();
+ prefix.consume_back(arg->getOption().getName());
+ auto option = optTable.getOption(opt);
+ return saver.save((prefix + option.getName()).str());
+}
diff --git a/lld/include/lld/Common/Args.h b/lld/include/lld/Common/Args.h
index 60f83fbbbf1a3..6e962807a15da 100644
--- a/lld/include/lld/Common/Args.h
+++ b/lld/include/lld/Common/Args.h
@@ -15,9 +15,13 @@
#include <vector>
namespace llvm {
+class StringSaver;
namespace opt {
+class Arg;
class InputArgList;
-}
+class OptSpecifier;
+class OptTable;
+} // namespace opt
} // namespace llvm
namespace lld {
@@ -40,6 +44,11 @@ std::vector<StringRef> getLines(MemoryBufferRef mb);
StringRef getFilenameWithoutExe(StringRef path);
+StringRef getOptionSpellingLikeArg(llvm::opt::OptTable &optTable,
+ llvm::opt::OptSpecifier opt,
+ llvm::opt::Arg *arg,
+ llvm::StringSaver &saver);
+
} // namespace args
} // namespace lld
diff --git a/lld/test/COFF/start-lib-warn.ll b/lld/test/COFF/start-lib-warn.ll
new file mode 100644
index 0000000000000..05d40e4a1a049
--- /dev/null
+++ b/lld/test/COFF/start-lib-warn.ll
@@ -0,0 +1,55 @@
+; REQUIRES: x86
+;
+; RUN: split-file %s %t.dir
+;
+; We need an input file to lld, so create one.
+; RUN: llc -filetype=obj %t.dir/main.ll -o %t.obj
+
+; RUN: lld-link -start-lib %t.obj /subsystem:console /force 2>&1 \
+; RUN: | FileCheck --check-prefix=MISSING_END %s
+; MISSING_END: -start-lib without -end-lib
+
+; RUN: not lld-link %t.obj -end-lib /subsystem:console /force 2>&1 \
+; RUN: | FileCheck --check-prefix=STRAY_END %s
+; STRAY_END: stray -end-lib
+
+; RUN: not lld-link -start-lib -start-lib %t.obj /subsystem:console /force 2>&1 \
+; RUN: | FileCheck --check-prefix=NESTED_START %s
+; NESTED_START: nested -start-lib
+
+; RUN: lld-link -start-lib %t.obj %S/Inputs/resource.res -end-lib /subsystem:console /force 2>&1 \
+; RUN: | FileCheck --check-prefix=WARN_RES %s
+; WARN_RES: .res file provided between -start-lib/-end-lib will not be lazy
+
+; RUN: lld-link -start-lib %t.obj %S/Inputs/ret42.lib -end-lib /subsystem:console /force 2>&1 \
+; RUN: | FileCheck --check-prefix=WARN_LIB %s
+; WARN_LIB: .lib/.a file provided between -start-lib/-end-lib has no effect
+
+; RUN: lld-link -start-lib %t.obj %S/Inputs/pdb-diff-cl.pdb -end-lib /subsystem:console /force 2>&1 \
+; RUN: | FileCheck --check-prefix=WARN_PDB %s
+; WARN_PDB: .pdb file provided between -start-lib/-end-lib will not be lazy
+
+; RUN: llvm-mc -filetype=obj -triple=x86_64-windows-gnu %t.dir/lib.s -o %t.lib.o
+; RUN: lld-link -noentry -dll -def:%t.dir/lib.def %t.lib.o -out:%t.lib.dll -implib:%t.implib.lib
+; RUN: lld-link -lldmingw -start-lib %t.lib.dll -end-lib /force 2>&1 \
+; RUN: | FileCheck --check-prefix=WARN_EXE %s
+; WARN_EXE: .dll file provided between -start-lib/-end-lib will not be lazy
+
+#--- main.ll
+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
+}
+
+#--- lib.s
+.text
+.global func1
+func1:
+ ret
+
+#--- lib.def
+NAME lib.dll
+EXPORTS
+ func1
\ No newline at end of file
More information about the llvm-commits
mailing list