[lld] [lld][MachO] Fix --reproduce paths for non-global symbol lists and SDK libraries/frameworks (PR #223687)

Vy Nguyen via llvm-commits llvm-commits at lists.llvm.org
Thu Sep 17 06:53:59 PDT 2026


https://github.com/oontvoo updated https://github.com/llvm/llvm-project/pull/223687

>From 6f51c1f6ab47bce632e860a832c15d5a89d9523c Mon Sep 17 00:00:00 2001
From: xiaoyanggong <xiaoyanggong at bytedance.com>
Date: Tue, 15 Sep 2026 16:55:10 +0800
Subject: [PATCH 1/4] [lld][MachO] Rewrite symbol-strip list paths in
 reproducers

Rewrite non_global_symbols_strip_list and non_global_symbols_no_strip_list paths to their archived locations. Extend the existing reproduce.s cases to check both rewritten paths and replay the generated response files.
---
 lld/MachO/DriverUtils.cpp  |  2 ++
 lld/test/MachO/reproduce.s | 10 ++++++++--
 2 files changed, 10 insertions(+), 2 deletions(-)

diff --git a/lld/MachO/DriverUtils.cpp b/lld/MachO/DriverUtils.cpp
index 3ff9d96ed53ce8..f7d69f28732ee8 100644
--- a/lld/MachO/DriverUtils.cpp
+++ b/lld/MachO/DriverUtils.cpp
@@ -178,6 +178,8 @@ std::string macho::createResponseFile(const InputArgList &args) {
       break;
     case OPT_F:
     case OPT_L:
+    case OPT_non_global_symbols_strip_list:
+    case OPT_non_global_symbols_no_strip_list:
     case OPT_bundle_loader:
     case OPT_exported_symbols_list:
     case OPT_order_file:
diff --git a/lld/test/MachO/reproduce.s b/lld/test/MachO/reproduce.s
index 68d94cab2c7ee7..f6949fbab068b8 100644
--- a/lld/test/MachO/reproduce.s
+++ b/lld/test/MachO/reproduce.s
@@ -7,6 +7,7 @@
 # RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos %s -o %t.dir/build1/foo.o
 # RUN: echo '_main' > %t.dir/main.exports
 # RUN: echo '_main' > %t.dir/main.order
+# RUN: echo '_local' > %t.dir/main.locals
 # RUN: echo 'not a virus' > %t.dir/sectdata.txt
 # RUN: cd %t.dir
 # RUN: %lld -platform_version macos 10.10.0 11.0 \
@@ -14,6 +15,7 @@
 # RUN:     -order_file main.order \
 # RUN:     -sectcreate __COMPLETELY __legit sectdata.txt \
 # RUN:     -rpath /usr/lib/swift \
+# RUN:     -non_global_symbols_strip_list main.locals \
 # RUN:     build1/foo.o -o bar --reproduce repro1.tar
 
 # RUN: tar tf repro1.tar | FileCheck -DPATH='%:t.dir' --check-prefix=LIST %s
@@ -34,6 +36,7 @@
 # RSP1-NEXT: -order_file [[BASEDIR]]/main.order
 # RSP1-NEXT: -sectcreate __COMPLETELY __legit [[BASEDIR]]/sectdata.txt
 # RSP1-NEXT: -rpath /usr/lib/swift
+# RSP1-NEXT: -non_global_symbols_strip_list [[BASEDIR]]/main.locals
 # RSP1-NOT:  {{^}}repro1{{[/\\]}}
 # RSP1-NEXT: [[BASEDIR]]/build1/foo.o
 # RSP1-NEXT: -o bar
@@ -48,11 +51,14 @@
 # RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos %s -o %t.dir/build2/foo.o
 # RUN: cd %t.dir/build2/a/b/c
 # RUN: echo ./../../../foo.o > %t.dir/build2/filelist
-# RUN: env LLD_REPRODUCE=repro2.tar %lld -filelist %t.dir/build2/filelist -o /dev/null
+# RUN: env LLD_REPRODUCE=repro2.tar %lld \
+# RUN:     -non_global_symbols_no_strip_list %t.dir/main.locals \
+# RUN:     -filelist %t.dir/build2/filelist -o /dev/null
 # RUN: tar xf repro2.tar
 # RUN: cmp %t.dir/build2/foo.o repro2/%:t.dir/build2/foo.o
-# RUN: FileCheck %s --check-prefix=RSP2 < repro2/response.txt
+# RUN: FileCheck %s --check-prefix=RSP2 -DPATH=%:t.dir < repro2/response.txt
 # RSP2-NOT:  {{^}}repro2{{[/\\]}}
+# RSP2:      -non_global_symbols_no_strip_list [[PATH]]/main.locals
 # RSP2:      {{[/\\]}}foo.o
 
 # RUN: cd repro2; %no-arg-lld @response.txt

>From 3a72b69f29a78512b0510b617318c01b38cb88b2 Mon Sep 17 00:00:00 2001
From: xiaoyanggong <xiaoyanggong at bytedance.com>
Date: Tue, 15 Sep 2026 16:58:08 +0800
Subject: [PATCH 2/4] [lld][MachO] Resolve SDK search paths in reproducers

Share SDK rerooting between search-path setup and response-file generation. Resolve absolute -L and -F paths before making them relative so replay retains SDK order, host fallback, and relative-path behavior.
---
 lld/MachO/Driver.cpp                    | 21 +++--------
 lld/MachO/Driver.h                      |  3 ++
 lld/MachO/DriverUtils.cpp               | 26 ++++++++++++++
 lld/test/MachO/reproduce-search-paths.s | 48 +++++++++++++++++++++++++
 4 files changed, 81 insertions(+), 17 deletions(-)
 create mode 100644 lld/test/MachO/reproduce-search-paths.s

diff --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp
index 8edaff6e386575..f14cef7737f0eb 100644
--- a/lld/MachO/Driver.cpp
+++ b/lld/MachO/Driver.cpp
@@ -170,23 +170,10 @@ getSearchPaths(unsigned optionCode, InputArgList &args,
                const SmallVector<StringRef, 2> &systemPaths) {
   std::vector<StringRef> paths;
   StringRef optionLetter{optionCode == OPT_F ? "F" : "L"};
-  for (StringRef path : args::getStrings(args, optionCode)) {
-    // NOTE: only absolute paths are re-rooted to syslibroot(s)
-    bool found = false;
-    if (path::is_absolute(path, path::Style::posix)) {
-      for (StringRef root : roots) {
-        SmallString<261> buffer(root);
-        path::append(buffer, path);
-        // Do not warn about paths that are computed via the syslib roots
-        if (fs::is_directory(buffer)) {
-          paths.push_back(saver().save(buffer.str()));
-          found = true;
-        }
-      }
-    }
-    if (!found && warnIfNotDirectory(optionLetter, path))
-      paths.push_back(path);
-  }
+  for (StringRef path : args::getStrings(args, optionCode))
+    for (StringRef searchPath : getRerootedSearchPaths(path, roots))
+      if (warnIfNotDirectory(optionLetter, searchPath))
+        paths.push_back(searchPath);
 
   // `-Z` suppresses the standard "system" search paths.
   if (args.hasArg(OPT_Z))
diff --git a/lld/MachO/Driver.h b/lld/MachO/Driver.h
index bd1cd960768bf5..0d3d40d7cfa0dd 100644
--- a/lld/MachO/Driver.h
+++ b/lld/MachO/Driver.h
@@ -49,6 +49,9 @@ void parseLCLinkerOption(llvm::SmallVectorImpl<StringRef> &LCLinkerOptions,
                          InputFile *f, unsigned argc, StringRef data);
 void resolveLCLinkerOptions();
 
+std::vector<StringRef> getRerootedSearchPaths(StringRef path,
+                                              ArrayRef<StringRef> roots);
+
 std::string createResponseFile(const llvm::opt::InputArgList &args);
 
 // Check for both libfoo.dylib and libfoo.tbd (in that order).
diff --git a/lld/MachO/DriverUtils.cpp b/lld/MachO/DriverUtils.cpp
index f7d69f28732ee8..81e00ce80e6cf9 100644
--- a/lld/MachO/DriverUtils.cpp
+++ b/lld/MachO/DriverUtils.cpp
@@ -134,6 +134,26 @@ void MachOOptTable::printHelp(CommonLinkerContext &ctx, const char *argv0,
   outs << '\n';
 }
 
+// If any SDK contains the directory, use those directories in SDK order
+// instead of falling back to the host.
+std::vector<StringRef>
+macho::getRerootedSearchPaths(StringRef searchPath, ArrayRef<StringRef> roots) {
+  std::vector<StringRef> paths;
+  // NOTE: only absolute paths are re-rooted to syslibroot(s)
+  if (path::is_absolute(searchPath, path::Style::posix)) {
+    for (StringRef root : roots) {
+      SmallString<261> buffer(root);
+      path::append(buffer, searchPath);
+      // Do not warn about paths that are computed via the syslib roots
+      if (fs::is_directory(buffer))
+        paths.push_back(saver().save(buffer.str()));
+    }
+  }
+  if (paths.empty())
+    paths.push_back(searchPath);
+  return paths;
+}
+
 static std::string rewritePath(StringRef s) {
   if (fs::exists(s))
     return relativeToRoot(s);
@@ -178,6 +198,12 @@ std::string macho::createResponseFile(const InputArgList &args) {
       break;
     case OPT_F:
     case OPT_L:
+      // Resolve SDK prefixes before making search paths relative: relative
+      // search paths are not rerooted when the reproducer is replayed.
+      for (StringRef path :
+           getRerootedSearchPaths(arg->getValue(), config->systemLibraryRoots))
+        os << arg->getSpelling() << " " << quote(rewritePath(path)) << "\n";
+      break;
     case OPT_non_global_symbols_strip_list:
     case OPT_non_global_symbols_no_strip_list:
     case OPT_bundle_loader:
diff --git a/lld/test/MachO/reproduce-search-paths.s b/lld/test/MachO/reproduce-search-paths.s
new file mode 100644
index 00000000000000..51e6e0c91f8473
--- /dev/null
+++ b/lld/test/MachO/reproduce-search-paths.s
@@ -0,0 +1,48 @@
+# REQUIRES: x86
+# UNSUPPORTED: system-windows
+
+# RUN: rm -rf %t; mkdir -p %t/host %t/sdk1/%:t/host/Foo.framework %t/sdk2/%:t/host/Bar.framework
+# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos %s -o %t/foo.o
+# RUN: %no-arg-lld -arch x86_64 -platform_version macos 11.0 11.0 -dylib -Z \
+# RUN:     %t/foo.o -o %t/sdk1/%:t/host/libfoo.dylib
+# RUN: cp %t/sdk1/%:t/host/libfoo.dylib %t/sdk1/%:t/host/Foo.framework/Foo
+# RUN: cp %t/sdk1/%:t/host/libfoo.dylib %t/sdk2/%:t/host/libbar.dylib
+# RUN: cp %t/sdk1/%:t/host/libfoo.dylib %t/sdk2/%:t/host/Bar.framework/Bar
+
+## Libraries and frameworks exist in SDKs only.
+# RUN: %no-arg-lld -arch x86_64 -platform_version macos 11.0 11.0 -dylib -Z \
+# RUN:     -syslibroot %t/sdk1 -syslibroot %t/sdk2 -L %t/host -F %t/host \
+# RUN:     -lfoo -lbar -framework Foo -framework Bar -o %t/out --reproduce %t/repro.tar
+# RUN: cd %t; tar xf repro.tar
+# RUN: FileCheck %s --check-prefix=SDK -DPATH=%:t < repro/response.txt
+# SDK:      -L [[PATH]]/sdk1/[[PATH]]/host
+# SDK-NEXT: -L [[PATH]]/sdk2/[[PATH]]/host
+# SDK-NEXT: -F [[PATH]]/sdk1/[[PATH]]/host
+# SDK-NEXT: -F [[PATH]]/sdk2/[[PATH]]/host
+# RUN: rm -rf sdk1 sdk2
+# RUN: cd repro; %no-arg-lld @response.txt
+
+## Libraries and frameworks exist outside the SDK only.
+## Absolute paths fall back to host directories; relative paths ignore SDK roots.
+# RUN: mkdir -p %t/sdk/relative %t/fallback/Foo.framework %t/relative/Bar.framework
+# RUN: %no-arg-lld -arch x86_64 -platform_version macos 11.0 11.0 -dylib -Z \
+# RUN:     %t/foo.o -o %t/fallback/libfoo.dylib
+# RUN: cp %t/fallback/libfoo.dylib %t/fallback/Foo.framework/Foo
+# RUN: cp %t/fallback/libfoo.dylib %t/relative/libbar.dylib
+# RUN: cp %t/fallback/libfoo.dylib %t/relative/Bar.framework/Bar
+# RUN: cd %t
+# RUN: %no-arg-lld -arch x86_64 -platform_version macos 11.0 11.0 -dylib -Z \
+# RUN:     -syslibroot %t/sdk -L %t/fallback -F %t/fallback -L relative -F relative \
+# RUN:     -lfoo -lbar -framework Foo -framework Bar -o out --reproduce fallback.tar
+# RUN: tar xf fallback.tar
+# RUN: FileCheck %s --check-prefix=FALLBACK -DPATH=%:t < fallback/response.txt
+# FALLBACK:      -L [[PATH]]/fallback
+# FALLBACK-NEXT: -F [[PATH]]/fallback
+# FALLBACK-NEXT: -L [[PATH]]/relative
+# FALLBACK-NEXT: -F [[PATH]]/relative
+# RUN: rm -rf sdk relative fallback/libfoo.dylib fallback/Foo.framework
+# RUN: cd fallback; %no-arg-lld @response.txt
+
+.globl _foo
+_foo:
+  ret

>From cbbd0c52799de762d5dcdb20f70c4a695116a544 Mon Sep 17 00:00:00 2001
From: Xiaoyang Gong <xyg_aster at outlook.com>
Date: Wed, 16 Sep 2026 11:17:27 +0800
Subject: [PATCH 3/4] Skip directory checking when SDK resolved successfully

Co-authored-by: Vy Nguyen <1762963+oontvoo at users.noreply.github.com>
---
 lld/MachO/Driver.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp
index f14cef7737f0eb..5603dcba5a38a5 100644
--- a/lld/MachO/Driver.cpp
+++ b/lld/MachO/Driver.cpp
@@ -172,7 +172,7 @@ getSearchPaths(unsigned optionCode, InputArgList &args,
   StringRef optionLetter{optionCode == OPT_F ? "F" : "L"};
   for (StringRef path : args::getStrings(args, optionCode))
     for (StringRef searchPath : getRerootedSearchPaths(path, roots))
-      if (warnIfNotDirectory(optionLetter, searchPath))
+      if (searchPath != path || warnIfNotDirectory(optionLetter, searchPath))
         paths.push_back(searchPath);
 
   // `-Z` suppresses the standard "system" search paths.

>From 8567843f8e1ca5e9407e4dc957264f54b6ca41c1 Mon Sep 17 00:00:00 2001
From: xiaoyanggong <xiaoyanggong at bytedance.com>
Date: Wed, 16 Sep 2026 11:32:20 +0800
Subject: [PATCH 4/4] Use SmallVector to reduce heap allocations in
 getRerootedSearchPaths

---
 lld/MachO/Driver.h        | 3 ++-
 lld/MachO/DriverUtils.cpp | 4 ++--
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/lld/MachO/Driver.h b/lld/MachO/Driver.h
index 0d3d40d7cfa0dd..9374dffffd8bdd 100644
--- a/lld/MachO/Driver.h
+++ b/lld/MachO/Driver.h
@@ -11,6 +11,7 @@
 
 #include "lld/Common/LLVM.h"
 #include "llvm/ADT/SetVector.h"
+#include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/BinaryFormat/MachO.h"
 #include "llvm/Option/OptTable.h"
@@ -49,7 +50,7 @@ void parseLCLinkerOption(llvm::SmallVectorImpl<StringRef> &LCLinkerOptions,
                          InputFile *f, unsigned argc, StringRef data);
 void resolveLCLinkerOptions();
 
-std::vector<StringRef> getRerootedSearchPaths(StringRef path,
+SmallVector<StringRef> getRerootedSearchPaths(StringRef path,
                                               ArrayRef<StringRef> roots);
 
 std::string createResponseFile(const llvm::opt::InputArgList &args);
diff --git a/lld/MachO/DriverUtils.cpp b/lld/MachO/DriverUtils.cpp
index 81e00ce80e6cf9..bbfc4477bb978b 100644
--- a/lld/MachO/DriverUtils.cpp
+++ b/lld/MachO/DriverUtils.cpp
@@ -136,9 +136,9 @@ void MachOOptTable::printHelp(CommonLinkerContext &ctx, const char *argv0,
 
 // If any SDK contains the directory, use those directories in SDK order
 // instead of falling back to the host.
-std::vector<StringRef>
+SmallVector<StringRef>
 macho::getRerootedSearchPaths(StringRef searchPath, ArrayRef<StringRef> roots) {
-  std::vector<StringRef> paths;
+  SmallVector<StringRef> paths;
   // NOTE: only absolute paths are re-rooted to syslibroot(s)
   if (path::is_absolute(searchPath, path::Style::posix)) {
     for (StringRef root : roots) {



More information about the llvm-commits mailing list