From e49faac40e1d3faf382b5c8d343ee3cebcac97c5 Mon Sep 17 00:00:00 2001 From: David Truby Date: Tue, 28 Oct 2025 14:25:01 +0000 Subject: [PATCH 1/9] [lld][COFF] Add /linkreprofullpathrsp flag This patch adds the /linkreprofullpathrsp flag with the same behaviour as link.exe. This flag emits a file containing the full paths to each object passed to the link line. This is used in particular when linking Arm64X binaries, as you need the full path to all the Arm64 objects that were used in a standard Arm64 build. See: https://learn.microsoft.com/en-us/cpp/build/reference/link-repro-full-path-rsp for the Microsoft documentation of the flag. --- lld/COFF/Driver.cpp | 33 +++++++++++++++++---- lld/COFF/Driver.h | 5 ++-- lld/COFF/Options.td | 3 ++ lld/test/COFF/linkreprofullpathrsp.test | 38 +++++++++++++++++++++++++ 4 files changed, 71 insertions(+), 8 deletions(-) create mode 100644 lld/test/COFF/linkreprofullpathrsp.test diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp index 0e528de9c3652..e2ad2f1c1e993 100644 --- a/lld/COFF/Driver.cpp +++ b/lld/COFF/Driver.cpp @@ -318,7 +318,8 @@ void LinkerDriver::addBuffer(std::unique_ptr mb, } } -void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive, bool lazy) { +void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive, bool lazy, + llvm::raw_ostream *reproFile, bool defaultlib) { auto future = std::make_shared>( createFutureForFile(std::string(path))); std::string pathStr = std::string(path); @@ -356,8 +357,17 @@ void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive, bool lazy) { Err(ctx) << msg; else Err(ctx) << msg << "; did you mean '" << nearest << "'"; - } else + } else { + // Write full path to library to repro file if /linkreprofullpathrsp + // is specified. + if (reproFile) { + *reproFile << '"'; + if (defaultlib) + *reproFile << "/defaultlib:"; + *reproFile << pathStr << "\"\n"; + } ctx.driver.addBuffer(std::move(mb), wholeArchive, lazy); + } }); } @@ -514,7 +524,7 @@ void LinkerDriver::parseDirectives(InputFile *file) { break; case OPT_defaultlib: if (std::optional path = findLibIfNew(arg->getValue())) - enqueuePath(*path, false, false); + enqueuePath(*path, false, false, nullptr); break; case OPT_entry: if (!arg->getValue()[0]) @@ -2204,6 +2214,17 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { config->incremental = false; } + // Handle /linkreprofullpathrsp. + std::unique_ptr reproFile; + if (auto *arg = args.getLastArg(OPT_linkreprofullpathrsp)) { + std::error_code ec; + reproFile = std::make_unique(arg->getValue(), ec); + if (ec) { + Err(ctx) << "cannot open " << arg->getValue() << ": " << ec.message(); + reproFile.reset(); + } + } + if (errCount(ctx)) return; @@ -2245,11 +2266,11 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { break; case OPT_wholearchive_file: if (std::optional path = findFileIfNew(arg->getValue())) - enqueuePath(*path, true, inLib); + enqueuePath(*path, true, inLib, reproFile.get()); break; case OPT_INPUT: if (std::optional path = findFileIfNew(arg->getValue())) - enqueuePath(*path, isWholeArchive(*path), inLib); + enqueuePath(*path, isWholeArchive(*path), inLib, reproFile.get()); break; default: // Ignore other options. @@ -2289,7 +2310,7 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { // addWinSysRootLibSearchPaths(), which is why they are in a separate loop. for (auto *arg : args.filtered(OPT_defaultlib)) if (std::optional path = findLibIfNew(arg->getValue())) - enqueuePath(*path, false, false); + enqueuePath(*path, false, false, reproFile.get(), true); run(); if (errorCount()) return; diff --git a/lld/COFF/Driver.h b/lld/COFF/Driver.h index 14710d5853bcf..f4b72a317d077 100644 --- a/lld/COFF/Driver.h +++ b/lld/COFF/Driver.h @@ -88,11 +88,12 @@ 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, false, false, nullptr); } MemoryBufferRef takeBuffer(std::unique_ptr mb); - void enqueuePath(StringRef path, bool wholeArchive, bool lazy); + void enqueuePath(StringRef path, bool wholeArchive, bool lazy, + raw_ostream *reproFile, bool defaultlib = false); // Returns a list of chunks of selected symbols. std::vector getChunks() const; diff --git a/lld/COFF/Options.td b/lld/COFF/Options.td index d77478fc9c987..dca8a721dc4fd 100644 --- a/lld/COFF/Options.td +++ b/lld/COFF/Options.td @@ -74,6 +74,9 @@ def libpath : P<"libpath", "Additional library search path">; def linkrepro : Joined<["/", "-", "/?", "-?"], "linkrepro:">, MetaVarName<"directory">, HelpText<"Write repro.tar containing inputs and command to reproduce link">; +def linkreprofullpathrsp : Joined<["/", "-", "/?", "-?"], "linkreprofullpathrsp:">, + MetaVarName<"directory">, + HelpText<"Write .rsp file containing inputs used to link with full paths">; def lldignoreenv : F<"lldignoreenv">, HelpText<"Ignore environment variables like %LIB%">; def lldltocache : P<"lldltocache", diff --git a/lld/test/COFF/linkreprofullpathrsp.test b/lld/test/COFF/linkreprofullpathrsp.test new file mode 100644 index 0000000000000..c77f09eae9dcd --- /dev/null +++ b/lld/test/COFF/linkreprofullpathrsp.test @@ -0,0 +1,38 @@ +# REQUIRES: x86 +# Unsupported on Windows due to maximum path length limitations. + +# RUN: rm -rf %t.dir +# RUN: split-file %s %t.dir +# RUN: yaml2obj %p/Inputs/hello32.yaml -o %t.obj +# RUN: llvm-mc -filetype=obj -triple=i386-windows %t.dir/drectve.s -o %t.dir/drectve.obj +# RUN: echo '_main@0' > %t.order +# RUN: touch %t.def +# RUN: touch %t.cg + + + +Test link.exe-style /linkreprofullpathrsp: flag. +# RUN: mkdir -p %t.dir/build1 +# RUN: cd %t.dir/build1 +# RUN: lld-link %t.obj %p/Inputs/std32.lib /subsystem:console /defaultlib:%p/Inputs/library.lib \ +# RUN: /entry:main@0 /linkreprofullpathrsp:%t.rsp /out:%t.exe +# RUN: FileCheck %s --check-prefix=RSP -DT=%t -DP=%p < %t.rsp + +# RSP: [[T]].obj +# RSP-NEXT: "[[P]]/Inputs/std32.lib" +# RSP-NEXT: "/defaultlib:[[P]]/Inputs/library.lib" + +#--- drectve.s + .section .drectve, "yn" + .ascii "/defaultlib:std32" + +#--- archive.s + .text + .intel_syntax noprefix + .globl exportfn3 + .p2align 4 +exportfn3: + ret + + .section .drectve,"yni" + .ascii " /EXPORT:exportfn3" From f34b707062d1129f66da4387bb250a3d0407acf1 Mon Sep 17 00:00:00 2001 From: David Truby Date: Mon, 3 Nov 2025 15:08:11 +0000 Subject: [PATCH 2/9] Actually find the full path --- lld/COFF/Driver.cpp | 23 +++++++++++++++++------ lld/test/COFF/linkreprofullpathrsp.test | 14 +++++--------- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp index e2ad2f1c1e993..c26955fbbfe5b 100644 --- a/lld/COFF/Driver.cpp +++ b/lld/COFF/Driver.cpp @@ -318,6 +318,20 @@ void LinkerDriver::addBuffer(std::unique_ptr mb, } } +static void handleReproFile(COFFLinkerContext &ctx, raw_ostream &reproFile, + StringRef path, bool defaultlib) { + reproFile << '"'; + if (defaultlib) + reproFile << "/defaultlib:"; + SmallString<128> absPath = path; + std::error_code ec = sys::fs::make_absolute(absPath); + if (ec) + Err(ctx) << "cannot find absolute path for reproFile for " << absPath + << ": " << ec.message(); + sys::path::remove_dots(absPath, true); + reproFile << absPath << "\"\n"; +} + void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive, bool lazy, llvm::raw_ostream *reproFile, bool defaultlib) { auto future = std::make_shared>( @@ -360,12 +374,9 @@ void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive, bool lazy, } else { // Write full path to library to repro file if /linkreprofullpathrsp // is specified. - if (reproFile) { - *reproFile << '"'; - if (defaultlib) - *reproFile << "/defaultlib:"; - *reproFile << pathStr << "\"\n"; - } + if (reproFile) + handleReproFile(ctx, *reproFile, pathStr, defaultlib); + ctx.driver.addBuffer(std::move(mb), wholeArchive, lazy); } }); diff --git a/lld/test/COFF/linkreprofullpathrsp.test b/lld/test/COFF/linkreprofullpathrsp.test index c77f09eae9dcd..5e09eff273a2b 100644 --- a/lld/test/COFF/linkreprofullpathrsp.test +++ b/lld/test/COFF/linkreprofullpathrsp.test @@ -1,26 +1,22 @@ # REQUIRES: x86 # Unsupported on Windows due to maximum path length limitations. +# UNSUPPORTED: system-windows # RUN: rm -rf %t.dir -# RUN: split-file %s %t.dir -# RUN: yaml2obj %p/Inputs/hello32.yaml -o %t.obj -# RUN: llvm-mc -filetype=obj -triple=i386-windows %t.dir/drectve.s -o %t.dir/drectve.obj -# RUN: echo '_main@0' > %t.order -# RUN: touch %t.def -# RUN: touch %t.cg - - Test link.exe-style /linkreprofullpathrsp: flag. # RUN: mkdir -p %t.dir/build1 # RUN: cd %t.dir/build1 # RUN: lld-link %t.obj %p/Inputs/std32.lib /subsystem:console /defaultlib:%p/Inputs/library.lib \ -# RUN: /entry:main@0 /linkreprofullpathrsp:%t.rsp /out:%t.exe +# RUN: /libpath:%p/Inputs /defaultlib:std64.lib ret42.lib /entry:main@0 /linkreprofullpathrsp:%t.rsp \ +# RUN: /out:%t.exe # RUN: FileCheck %s --check-prefix=RSP -DT=%t -DP=%p < %t.rsp # RSP: [[T]].obj # RSP-NEXT: "[[P]]/Inputs/std32.lib" +# RSP-NEXT: "[[P]]/Inputs/ret42.lib" # RSP-NEXT: "/defaultlib:[[P]]/Inputs/library.lib" +# RSP-NEXT: "/defaultlib:[[P]]/Inputs/std64.lib" #--- drectve.s .section .drectve, "yn" From 5070301464edb514c482e9cd5c28fa743b8627c5 Mon Sep 17 00:00:00 2001 From: David Truby Date: Mon, 3 Nov 2025 16:01:41 +0000 Subject: [PATCH 3/9] Fix failing test --- lld/test/COFF/linkreprofullpathrsp.test | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lld/test/COFF/linkreprofullpathrsp.test b/lld/test/COFF/linkreprofullpathrsp.test index 5e09eff273a2b..b099d4f217913 100644 --- a/lld/test/COFF/linkreprofullpathrsp.test +++ b/lld/test/COFF/linkreprofullpathrsp.test @@ -2,7 +2,8 @@ # Unsupported on Windows due to maximum path length limitations. # UNSUPPORTED: system-windows -# RUN: rm -rf %t.dir +# RUN: rm -rf %t.dir %t.obj +# RUN: yaml2obj %p/Inputs/hello32.yaml -o %t.obj Test link.exe-style /linkreprofullpathrsp: flag. # RUN: mkdir -p %t.dir/build1 From 822cc21a082d37b0232af9e16c9bfda39d3f9aaa Mon Sep 17 00:00:00 2001 From: David Truby Date: Tue, 11 Nov 2025 15:00:30 +0000 Subject: [PATCH 4/9] Add pdbs and wholearchives to the linkrepro file --- lld/COFF/Driver.cpp | 61 +++++++++++++------------ lld/COFF/Driver.h | 14 ++++-- lld/test/COFF/linkreprofullpathrsp.test | 7 ++- 3 files changed, 48 insertions(+), 34 deletions(-) diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp index c26955fbbfe5b..7d1b9ed54d011 100644 --- a/lld/COFF/Driver.cpp +++ b/lld/COFF/Driver.cpp @@ -318,26 +318,30 @@ void LinkerDriver::addBuffer(std::unique_ptr mb, } } -static void handleReproFile(COFFLinkerContext &ctx, raw_ostream &reproFile, - StringRef path, bool defaultlib) { - reproFile << '"'; - if (defaultlib) - reproFile << "/defaultlib:"; +void LinkerDriver::handleReproFile(StringRef path, InputType inputType) { + if (!reproFile) + return; + + *reproFile << '"'; + if (inputType == InputType::DefaultLib) + *reproFile << "/defaultlib:"; + else if (inputType == InputType::WholeArchive) + *reproFile << "/wholearchive:"; + SmallString<128> absPath = path; std::error_code ec = sys::fs::make_absolute(absPath); if (ec) Err(ctx) << "cannot find absolute path for reproFile for " << absPath << ": " << ec.message(); sys::path::remove_dots(absPath, true); - reproFile << absPath << "\"\n"; + *reproFile << absPath << "\"\n"; } -void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive, bool lazy, - llvm::raw_ostream *reproFile, bool defaultlib) { +void LinkerDriver::enqueuePath(StringRef path, bool lazy, InputType pathType) { auto future = std::make_shared>( createFutureForFile(std::string(path))); std::string pathStr = std::string(path); - enqueueTask([=]() { + enqueueTask([=, reproFile = reproFile.get()]() { llvm::TimeTraceScope timeScope("File: ", path); auto [mb, ec] = future->get(); if (ec) { @@ -372,12 +376,9 @@ void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive, bool lazy, else Err(ctx) << msg << "; did you mean '" << nearest << "'"; } else { - // Write full path to library to repro file if /linkreprofullpathrsp - // is specified. - if (reproFile) - handleReproFile(ctx, *reproFile, pathStr, defaultlib); - - ctx.driver.addBuffer(std::move(mb), wholeArchive, lazy); + handleReproFile(pathStr, pathType); + ctx.driver.addBuffer(std::move(mb), pathType == InputType::WholeArchive, + lazy); } }); } @@ -535,7 +536,7 @@ void LinkerDriver::parseDirectives(InputFile *file) { break; case OPT_defaultlib: if (std::optional path = findLibIfNew(arg->getValue())) - enqueuePath(*path, false, false, nullptr); + enqueuePath(*path, false, InputType::DefaultLib); break; case OPT_entry: if (!arg->getValue()[0]) @@ -1628,6 +1629,15 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { } } } + // Handle /linkreprofullpathrsp + if (auto *arg = args.getLastArg(OPT_linkreprofullpathrsp)) { + std::error_code ec; + reproFile = std::make_unique(arg->getValue(), ec); + if (ec) { + Err(ctx) << "cannot open " << arg->getValue() << ": " << ec.message(); + reproFile.reset(); + } + } if (!args.hasArg(OPT_INPUT, OPT_wholearchive_file)) { if (args.hasArg(OPT_deffile)) @@ -2225,17 +2235,6 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { config->incremental = false; } - // Handle /linkreprofullpathrsp. - std::unique_ptr reproFile; - if (auto *arg = args.getLastArg(OPT_linkreprofullpathrsp)) { - std::error_code ec; - reproFile = std::make_unique(arg->getValue(), ec); - if (ec) { - Err(ctx) << "cannot open " << arg->getValue() << ": " << ec.message(); - reproFile.reset(); - } - } - if (errCount(ctx)) return; @@ -2277,11 +2276,13 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { break; case OPT_wholearchive_file: if (std::optional path = findFileIfNew(arg->getValue())) - enqueuePath(*path, true, inLib, reproFile.get()); + enqueuePath(*path, inLib, InputType::WholeArchive); break; case OPT_INPUT: if (std::optional path = findFileIfNew(arg->getValue())) - enqueuePath(*path, isWholeArchive(*path), inLib, reproFile.get()); + enqueuePath(*path, inLib, + isWholeArchive(*path) ? InputType::WholeArchive + : InputType::Direct); break; default: // Ignore other options. @@ -2321,7 +2322,7 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { // addWinSysRootLibSearchPaths(), which is why they are in a separate loop. for (auto *arg : args.filtered(OPT_defaultlib)) if (std::optional path = findLibIfNew(arg->getValue())) - enqueuePath(*path, false, false, reproFile.get(), true); + enqueuePath(*path, false, InputType::DefaultLib); run(); if (errorCount()) return; diff --git a/lld/COFF/Driver.h b/lld/COFF/Driver.h index f4b72a317d077..bb1d3b15c11b1 100644 --- a/lld/COFF/Driver.h +++ b/lld/COFF/Driver.h @@ -88,12 +88,13 @@ class LinkerDriver { void enqueueArchiveMember(const Archive::Child &c, const Archive::Symbol &sym, StringRef parentName); - void enqueuePDB(StringRef Path) { enqueuePath(Path, false, false, nullptr); } + enum class InputType { Direct, DefaultLib, WholeArchive }; + void enqueuePDB(StringRef Path) { enqueuePath(Path, false); } MemoryBufferRef takeBuffer(std::unique_ptr mb); - void enqueuePath(StringRef path, bool wholeArchive, bool lazy, - raw_ostream *reproFile, bool defaultlib = false); + void enqueuePath(StringRef path, bool lazy, + InputType inputType = InputType::Direct); // Returns a list of chunks of selected symbols. std::vector getChunks() const; @@ -139,6 +140,10 @@ class LinkerDriver { // std::string getImportName(bool asLib); + // Write fullly resolved path to repro file if /linkreprofullpathrsp + // is specified. + void handleReproFile(StringRef path, InputType inputType); + void createImportLibrary(bool asLib); // Used by the resolver to parse .drectve section contents. @@ -194,6 +199,9 @@ class LinkerDriver { int sdkMajor = 0; llvm::SmallString<128> windowsSdkLibPath; + // For linkreprofullpathrsp + std::unique_ptr reproFile = nullptr; + // Functions below this line are defined in DriverUtils.cpp. void printHelp(const char *argv0); diff --git a/lld/test/COFF/linkreprofullpathrsp.test b/lld/test/COFF/linkreprofullpathrsp.test index b099d4f217913..545bcb08f5a0d 100644 --- a/lld/test/COFF/linkreprofullpathrsp.test +++ b/lld/test/COFF/linkreprofullpathrsp.test @@ -4,18 +4,23 @@ # RUN: rm -rf %t.dir %t.obj # RUN: yaml2obj %p/Inputs/hello32.yaml -o %t.obj +# RUN: yaml2obj %p/Inputs/empty.yaml -o %t.archive.obj +# RUN: rm -f %t.archive.lib +# RUN: llvm-ar rcs %t.archive.lib %t.archive.obj + Test link.exe-style /linkreprofullpathrsp: flag. # RUN: mkdir -p %t.dir/build1 # RUN: cd %t.dir/build1 # RUN: lld-link %t.obj %p/Inputs/std32.lib /subsystem:console /defaultlib:%p/Inputs/library.lib \ # RUN: /libpath:%p/Inputs /defaultlib:std64.lib ret42.lib /entry:main@0 /linkreprofullpathrsp:%t.rsp \ -# RUN: /out:%t.exe +# RUN: /wholearchive:%t.archive.lib /out:%t.exe # RUN: FileCheck %s --check-prefix=RSP -DT=%t -DP=%p < %t.rsp # RSP: [[T]].obj # RSP-NEXT: "[[P]]/Inputs/std32.lib" # RSP-NEXT: "[[P]]/Inputs/ret42.lib" +# RSP-NEXT: "/wholearchive:[[T]].archive.lib" # RSP-NEXT: "/defaultlib:[[P]]/Inputs/library.lib" # RSP-NEXT: "/defaultlib:[[P]]/Inputs/std64.lib" From acb00f725b9a69c5708934a582955076b7cb88a3 Mon Sep 17 00:00:00 2001 From: David Truby Date: Tue, 18 Nov 2025 14:38:48 +0000 Subject: [PATCH 5/9] Add pdb to test --- lld/COFF/Driver.cpp | 8 ++++---- lld/test/COFF/linkreprofullpathrsp.test | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp index 7d1b9ed54d011..09f34c0934243 100644 --- a/lld/COFF/Driver.cpp +++ b/lld/COFF/Driver.cpp @@ -335,6 +335,7 @@ void LinkerDriver::handleReproFile(StringRef path, InputType inputType) { << ": " << ec.message(); sys::path::remove_dots(absPath, true); *reproFile << absPath << "\"\n"; + reproFile->flush(); } void LinkerDriver::enqueuePath(StringRef path, bool lazy, InputType pathType) { @@ -427,10 +428,9 @@ void LinkerDriver::enqueueArchiveMember(const Archive::Child &c, StringRef parentName) { auto reportBufferError = [=](Error &&e) { - StringRef childName = - CHECK(c.getName(), - "could not get child name for archive " + parentName + - " while loading symbol " + toCOFFString(ctx, sym)); + StringRef childName = CHECK( + c.getName(), "could not get child name for archive " + parentName + + " while loading symbol " + toCOFFString(ctx, sym)); Fatal(ctx) << "could not get the buffer for the member defining symbol " << &sym << ": " << parentName << "(" << childName << "): " << std::move(e); diff --git a/lld/test/COFF/linkreprofullpathrsp.test b/lld/test/COFF/linkreprofullpathrsp.test index 545bcb08f5a0d..53f789bdc924f 100644 --- a/lld/test/COFF/linkreprofullpathrsp.test +++ b/lld/test/COFF/linkreprofullpathrsp.test @@ -1,12 +1,11 @@ # REQUIRES: x86 -# Unsupported on Windows due to maximum path length limitations. -# UNSUPPORTED: system-windows # RUN: rm -rf %t.dir %t.obj # RUN: yaml2obj %p/Inputs/hello32.yaml -o %t.obj # RUN: yaml2obj %p/Inputs/empty.yaml -o %t.archive.obj # RUN: rm -f %t.archive.lib # RUN: llvm-ar rcs %t.archive.lib %t.archive.obj +# RUN: llvm-pdbutil yaml2pdb %S/Inputs/pdb-type-server-simple-ts.yaml -pdb %t.pdb Test link.exe-style /linkreprofullpathrsp: flag. @@ -14,10 +13,11 @@ Test link.exe-style /linkreprofullpathrsp: flag. # RUN: cd %t.dir/build1 # RUN: lld-link %t.obj %p/Inputs/std32.lib /subsystem:console /defaultlib:%p/Inputs/library.lib \ # RUN: /libpath:%p/Inputs /defaultlib:std64.lib ret42.lib /entry:main@0 /linkreprofullpathrsp:%t.rsp \ -# RUN: /wholearchive:%t.archive.lib /out:%t.exe +# RUN: /wholearchive:%t.archive.lib /out:%t.exe /pdb:%t.pdb # RUN: FileCheck %s --check-prefix=RSP -DT=%t -DP=%p < %t.rsp -# RSP: [[T]].obj +# RSP: "[[T].obj" +# RSP-NEXT: "[[T]].pdb" # RSP-NEXT: "[[P]]/Inputs/std32.lib" # RSP-NEXT: "[[P]]/Inputs/ret42.lib" # RSP-NEXT: "/wholearchive:[[T]].archive.lib" From db873da647c7b4cfe0569755d4733f4e35d51e93 Mon Sep 17 00:00:00 2001 From: David Truby Date: Wed, 19 Nov 2025 14:22:36 +0000 Subject: [PATCH 6/9] Fix tests --- lld/test/COFF/linkreprofullpathrsp.test | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lld/test/COFF/linkreprofullpathrsp.test b/lld/test/COFF/linkreprofullpathrsp.test index 53f789bdc924f..2a00cd5aa14c0 100644 --- a/lld/test/COFF/linkreprofullpathrsp.test +++ b/lld/test/COFF/linkreprofullpathrsp.test @@ -13,14 +13,14 @@ Test link.exe-style /linkreprofullpathrsp: flag. # RUN: cd %t.dir/build1 # RUN: lld-link %t.obj %p/Inputs/std32.lib /subsystem:console /defaultlib:%p/Inputs/library.lib \ # RUN: /libpath:%p/Inputs /defaultlib:std64.lib ret42.lib /entry:main@0 /linkreprofullpathrsp:%t.rsp \ -# RUN: /wholearchive:%t.archive.lib /out:%t.exe /pdb:%t.pdb +# RUN: /wholearchive:%t.archive.lib /out:%t.exe %t.pdb # RUN: FileCheck %s --check-prefix=RSP -DT=%t -DP=%p < %t.rsp -# RSP: "[[T].obj" -# RSP-NEXT: "[[T]].pdb" +# RSP: "[[T]].obj" # RSP-NEXT: "[[P]]/Inputs/std32.lib" # RSP-NEXT: "[[P]]/Inputs/ret42.lib" # RSP-NEXT: "/wholearchive:[[T]].archive.lib" +# RSP-NEXT: "[[T]].pdb" # RSP-NEXT: "/defaultlib:[[P]]/Inputs/library.lib" # RSP-NEXT: "/defaultlib:[[P]]/Inputs/std64.lib" From 173c1fd7bef5e2b64103ff94ce564da3bd2a281b Mon Sep 17 00:00:00 2001 From: David Truby Date: Wed, 19 Nov 2025 14:30:32 +0000 Subject: [PATCH 7/9] Flush and close file at the end of LinkerMain --- lld/COFF/Driver.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp index 09f34c0934243..7a3eb4216cf7f 100644 --- a/lld/COFF/Driver.cpp +++ b/lld/COFF/Driver.cpp @@ -335,14 +335,13 @@ void LinkerDriver::handleReproFile(StringRef path, InputType inputType) { << ": " << ec.message(); sys::path::remove_dots(absPath, true); *reproFile << absPath << "\"\n"; - reproFile->flush(); } void LinkerDriver::enqueuePath(StringRef path, bool lazy, InputType pathType) { auto future = std::make_shared>( createFutureForFile(std::string(path))); std::string pathStr = std::string(path); - enqueueTask([=, reproFile = reproFile.get()]() { + enqueueTask([=]() { llvm::TimeTraceScope timeScope("File: ", path); auto [mb, ec] = future->get(); if (ec) { @@ -2889,6 +2888,9 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { if (config->showTiming) ctx.rootTimer.print(); + // Clean up /linkreprofullpathrsp file + reproFile.reset(); + if (config->timeTraceEnabled) { // Manually stop the topmost "COFF link" scope, since we're shutting down. timeTraceProfilerEnd(); From 9a99826f559b2ce6e396bb6458c052adce72a7bd Mon Sep 17 00:00:00 2001 From: David Truby Date: Thu, 20 Nov 2025 13:43:26 +0000 Subject: [PATCH 8/9] Fix path separator in tests --- lld/test/COFF/linkreprofullpathrsp.test | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lld/test/COFF/linkreprofullpathrsp.test b/lld/test/COFF/linkreprofullpathrsp.test index 2a00cd5aa14c0..65dde4edc5f4f 100644 --- a/lld/test/COFF/linkreprofullpathrsp.test +++ b/lld/test/COFF/linkreprofullpathrsp.test @@ -17,12 +17,12 @@ Test link.exe-style /linkreprofullpathrsp: flag. # RUN: FileCheck %s --check-prefix=RSP -DT=%t -DP=%p < %t.rsp # RSP: "[[T]].obj" -# RSP-NEXT: "[[P]]/Inputs/std32.lib" -# RSP-NEXT: "[[P]]/Inputs/ret42.lib" +# RSP-NEXT: "[[P]]{{[/\\]}}Inputs{{[/\\]}}std32.lib" +# RSP-NEXT: "[[P]]{{[/\\]}}Inputs{{[/\\]}}ret42.lib" # RSP-NEXT: "/wholearchive:[[T]].archive.lib" # RSP-NEXT: "[[T]].pdb" -# RSP-NEXT: "/defaultlib:[[P]]/Inputs/library.lib" -# RSP-NEXT: "/defaultlib:[[P]]/Inputs/std64.lib" +# RSP-NEXT: "/defaultlib:[[P]]{{[/\\]}}Inputs{{[/\\]}}library.lib" +# RSP-NEXT: "/defaultlib:[[P]]{{[/\\]}}Inputs{{[/\\]}}std64.lib" #--- drectve.s .section .drectve, "yn" From 8f4d0f5ed45119c9273de62d2a199d825484032f Mon Sep 17 00:00:00 2001 From: David Truby Date: Thu, 20 Nov 2025 13:56:56 +0000 Subject: [PATCH 9/9] Add test that re-linking produces same binary --- lld/test/COFF/linkreprofullpathrsp.test | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lld/test/COFF/linkreprofullpathrsp.test b/lld/test/COFF/linkreprofullpathrsp.test index 65dde4edc5f4f..09d7b821ac9bb 100644 --- a/lld/test/COFF/linkreprofullpathrsp.test +++ b/lld/test/COFF/linkreprofullpathrsp.test @@ -16,6 +16,9 @@ Test link.exe-style /linkreprofullpathrsp: flag. # RUN: /wholearchive:%t.archive.lib /out:%t.exe %t.pdb # RUN: FileCheck %s --check-prefix=RSP -DT=%t -DP=%p < %t.rsp +# RUN: lld-link @%t.rsp /out:%t2.exe /entry:main@0 +# RUN: diff %t.exe %t2.exe + # RSP: "[[T]].obj" # RSP-NEXT: "[[P]]{{[/\\]}}Inputs{{[/\\]}}std32.lib" # RSP-NEXT: "[[P]]{{[/\\]}}Inputs{{[/\\]}}ret42.lib"