[lld] 28d2772 - [lld][COFF] Add /linkreprofullpathrsp flag (#165449)

via llvm-commits llvm-commits at lists.llvm.org
Wed Jan 7 07:57:04 PST 2026


Author: David Truby
Date: 2026-01-07T10:57:00-05:00
New Revision: 28d2772a8d1a907f2f9dc135224e1fa31ccad243

URL: https://github.com/llvm/llvm-project/commit/28d2772a8d1a907f2f9dc135224e1fa31ccad243
DIFF: https://github.com/llvm/llvm-project/commit/28d2772a8d1a907f2f9dc135224e1fa31ccad243.diff

LOG: [lld][COFF] Add /linkreprofullpathrsp flag (#165449)

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.

Added: 
    lld/test/COFF/linkreprofullpathrsp.test

Modified: 
    lld/COFF/Driver.cpp
    lld/COFF/Driver.h
    lld/COFF/Options.td
    lld/docs/ReleaseNotes.rst

Removed: 
    


################################################################################
diff  --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp
index 121cb32c5f095..a5ce5829eadab 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -340,7 +340,26 @@ void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb,
   }
 }
 
-void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive, bool lazy) {
+void LinkerDriver::handleReproFile(StringRef path, InputOpt inputOpt) {
+  if (!reproFile)
+    return;
+
+  *reproFile << '"';
+  if (inputOpt == InputOpt::DefaultLib)
+    *reproFile << "/defaultlib:";
+  else if (inputOpt == InputOpt::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";
+}
+
+void LinkerDriver::enqueuePath(StringRef path, bool lazy, InputOpt inputOpt) {
   auto future = std::make_shared<std::future<MBErrPair>>(
       createFutureForFile(std::string(path)));
   std::string pathStr = std::string(path);
@@ -378,8 +397,11 @@ void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive, bool lazy) {
         Err(ctx) << msg;
       else
         Err(ctx) << msg << "; did you mean '" << nearest << "'";
-    } else
-      ctx.driver.addBuffer(std::move(mb), wholeArchive, lazy);
+    } else {
+      handleReproFile(pathStr, inputOpt);
+      ctx.driver.addBuffer(std::move(mb), inputOpt == InputOpt::WholeArchive,
+                           lazy);
+    }
   });
 }
 
@@ -428,10 +450,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);
@@ -537,7 +558,7 @@ void LinkerDriver::parseDirectives(InputFile *file) {
       break;
     case OPT_defaultlib:
       if (std::optional<StringRef> path = findLibIfNew(arg->getValue()))
-        enqueuePath(*path, false, false);
+        enqueuePath(*path, false, InputOpt::DefaultLib);
       break;
     case OPT_entry:
       if (!arg->getValue()[0])
@@ -1638,6 +1659,15 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
       }
     }
   }
+  // Handle /linkreprofullpathrsp
+  if (auto *arg = args.getLastArg(OPT_linkreprofullpathrsp)) {
+    std::error_code ec;
+    reproFile = std::make_unique<raw_fd_ostream>(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))
@@ -2280,11 +2310,13 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
         break;
       case OPT_wholearchive_file:
         if (std::optional<StringRef> path = findFileIfNew(arg->getValue()))
-          enqueuePath(*path, true, inLib);
+          enqueuePath(*path, inLib, InputOpt::WholeArchive);
         break;
       case OPT_INPUT:
         if (std::optional<StringRef> path = findFileIfNew(arg->getValue()))
-          enqueuePath(*path, isWholeArchive(*path), inLib);
+          enqueuePath(*path, inLib,
+                      isWholeArchive(*path) ? InputOpt::WholeArchive
+                                            : InputOpt::None);
         break;
       default:
         // Ignore other options.
@@ -2324,7 +2356,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, false, InputOpt::DefaultLib);
   run();
   if (errorCount())
     return;
@@ -2890,6 +2922,9 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> 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();

diff  --git a/lld/COFF/Driver.h b/lld/COFF/Driver.h
index 14710d5853bcf..e0c447cfc7f80 100644
--- a/lld/COFF/Driver.h
+++ b/lld/COFF/Driver.h
@@ -88,11 +88,13 @@ class LinkerDriver {
   void enqueueArchiveMember(const Archive::Child &c, const Archive::Symbol &sym,
                             StringRef parentName);
 
-  void enqueuePDB(StringRef Path) { enqueuePath(Path, false, false); }
+  enum class InputOpt { None, DefaultLib, WholeArchive };
+  void enqueuePDB(StringRef Path) { enqueuePath(Path, false); }
 
   MemoryBufferRef takeBuffer(std::unique_ptr<MemoryBuffer> mb);
 
-  void enqueuePath(StringRef path, bool wholeArchive, bool lazy);
+  void enqueuePath(StringRef path, bool lazy,
+                   InputOpt inputOpt = InputOpt::None);
 
   // Returns a list of chunks of selected symbols.
   std::vector<Chunk *> getChunks() const;
@@ -138,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, InputOpt inputOpt);
+
   void createImportLibrary(bool asLib);
 
   // Used by the resolver to parse .drectve section contents.
@@ -193,6 +199,9 @@ class LinkerDriver {
   int sdkMajor = 0;
   llvm::SmallString<128> windowsSdkLibPath;
 
+  // For linkreprofullpathrsp
+  std::unique_ptr<llvm::raw_fd_ostream> reproFile;
+
   // Functions below this line are defined in DriverUtils.cpp.
 
   void printHelp(const char *argv0);

diff  --git a/lld/COFF/Options.td b/lld/COFF/Options.td
index 2e790baa5a00c..32e55f57fb696 100644
--- a/lld/COFF/Options.td
+++ b/lld/COFF/Options.td
@@ -75,6 +75,9 @@ def link : F<"link">, HelpText<"Ignored for compatibility">;
 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/docs/ReleaseNotes.rst b/lld/docs/ReleaseNotes.rst
index 31958c65c18aa..d26c5dbb2a694 100644
--- a/lld/docs/ReleaseNotes.rst
+++ b/lld/docs/ReleaseNotes.rst
@@ -40,6 +40,8 @@ COFF Improvements
 
 * ``/fat-lto-objects`` addded to support FatLTO. Without ``/fat-lto-objects`` or with ``/fat-lto-objects:no``, LLD will link LLVM FatLTO objects using the relocatable object file.
   (`#165529 <https://github.com/llvm/llvm-project/pull/165529>`_)
+* ``/linkreprofullpathrsp`` prints the full path to each object passed to the link line to a file.
+  (`#165449 <https://github.com/llvm/llvm-project/pull/165449>`_)
 
 MinGW Improvements
 ------------------

diff  --git a/lld/test/COFF/linkreprofullpathrsp.test b/lld/test/COFF/linkreprofullpathrsp.test
new file mode 100644
index 0000000000000..09d7b821ac9bb
--- /dev/null
+++ b/lld/test/COFF/linkreprofullpathrsp.test
@@ -0,0 +1,43 @@
+# REQUIRES: x86
+
+# 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.
+# 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 at 0 /linkreprofullpathrsp:%t.rsp \
+# 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 at 0
+# RUN: 
diff  %t.exe %t2.exe
+
+# 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"
+
+#--- 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"


        


More information about the llvm-commits mailing list