Skip to content

[lld-macho]Fix bug in finding "chained" re-exported libs. #135241

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 28, 2025

Conversation

oontvoo
Copy link
Member

@oontvoo oontvoo commented Apr 10, 2025

Details:
When we have the following scenario:

  • lib_a re-exports lib_b
  • lib_b re-exports @rpath/lib_c
    • lib_b contains LC_RPATH

Previously, lld-macho cannot find lib_c because it was attempting to resolve the '@rpath' from lib_b (which had no LC_RPATH defined). The change here is to also consider all the LC_RPATH rom lib_b when trying to find lib_c.

Inspired by real-life example when linking with libXCTestSwiftSupport.dylib (which re-exports XCTest, which re-exports XCTestCore)

Details:
When we have the following scenario:

- lib_a re-exports lib_b
- lib_b re-exports @rpath/lib_c
  + lib_b contains LC_RPATH

Previously, lld-macho cannot find lib_c because it was attempting to resolve the '@rpath' from lib_b (which had no LC_RPATH defined).
The change here is to also consider all the LC_RPATH rom lib_b when trying to find lib_c.

Inspired by real-life example when linking with libXCTestSwiftSupport.dylib (which re-exports XCTest, which re-exports XCTestCore)
@llvmbot
Copy link
Member

llvmbot commented Apr 10, 2025

@llvm/pr-subscribers-lld

Author: Vy Nguyen (oontvoo)

Changes

Details:
When we have the following scenario:

  • lib_a re-exports lib_b
  • lib_b re-exports @rpath/lib_c
    • lib_b contains LC_RPATH

Previously, lld-macho cannot find lib_c because it was attempting to resolve the '@rpath' from lib_b (which had no LC_RPATH defined). The change here is to also consider all the LC_RPATH rom lib_b when trying to find lib_c.

Inspired by real-life example when linking with libXCTestSwiftSupport.dylib (which re-exports XCTest, which re-exports XCTestCore)


Full diff: https://github.com/llvm/llvm-project/pull/135241.diff

2 Files Affected:

  • (modified) lld/MachO/InputFiles.cpp (+21-3)
  • (added) lld/test/MachO/reexport-with-rpath.s (+37)
diff --git a/lld/MachO/InputFiles.cpp b/lld/MachO/InputFiles.cpp
index 9adfbc9d3f6f5..9afa5feaa1693 100644
--- a/lld/MachO/InputFiles.cpp
+++ b/lld/MachO/InputFiles.cpp
@@ -1633,6 +1633,17 @@ static DylibFile *findDylib(StringRef path, DylibFile *umbrella,
       if (std::optional<StringRef> dylibPath = resolveDylibPath(newPath.str()))
         return loadDylib(*dylibPath, umbrella);
     }
+    // If not found in umbrella, try the rpaths specified via -rpath too.
+    for (StringRef rpath : config->runtimePaths) {
+      newPath.clear();
+      if (rpath.consume_front("@loader_path/")) {
+        fs::real_path(umbrella->getName(), newPath);
+        path::remove_filename(newPath);
+      }
+      path::append(newPath, rpath, path.drop_front(strlen("@rpath/")));
+      if (std::optional<StringRef> dylibPath = resolveDylibPath(newPath.str()))
+        return loadDylib(*dylibPath, umbrella);
+    }
   }
 
   // FIXME: Should this be further up?
@@ -1678,9 +1689,16 @@ static bool isImplicitlyLinked(StringRef path) {
 void DylibFile::loadReexport(StringRef path, DylibFile *umbrella,
                          const InterfaceFile *currentTopLevelTapi) {
   DylibFile *reexport = findDylib(path, umbrella, currentTopLevelTapi);
-  if (!reexport)
-    error(toString(this) + ": unable to locate re-export with install name " +
-          path);
+  if (!reexport) {
+    // If not found in umbrella, retry since some rpaths might have been
+    // defined in "this" dyblib (which contains the LC_REEXPORT_DYLIB cmd) and
+    // not in the umbrella.
+    DylibFile *reexport2 = findDylib(path, this, currentTopLevelTapi);
+    if (!reexport2) {
+      error(toString(this) + ": unable to locate re-export with install name " +
+            path);
+    }
+  }
 }
 
 DylibFile::DylibFile(MemoryBufferRef mb, DylibFile *umbrella,
diff --git a/lld/test/MachO/reexport-with-rpath.s b/lld/test/MachO/reexport-with-rpath.s
new file mode 100644
index 0000000000000..f1b00ddc9f7e5
--- /dev/null
+++ b/lld/test/MachO/reexport-with-rpath.s
@@ -0,0 +1,37 @@
+# REQUIRES: x86
+# RUN: rm -rf %t; split-file %s %t
+# RUN: mkdir -p %t/cc/two/three
+# RUN: mkdir -p %t/bb/two/three
+# RUN: mkdir -p %t/aa/two/three
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/c.s -o %t/c.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/b.s -o %t/b.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/a.s -o %t/a.o
+
+# RUN: %lld -dylib -install_name @rpath/two/three/libCee.dylib %t/c.o -o %t/cc/two/three/libCee.dylib 
+# RUN: %lld -dylib -install_name @rpath/two/three/libBee.dylib -L%t/cc/two/three -sub_library libCee -lCee %t/b.o -o %t/bb/two/three/libBee.dylib -rpath %t/cc
+# RUN: %lld -dylib -install_name @rpath/two/three/libAee.dylib -L%t/bb/two/three -sub_library libBee -lBee %t/a.o -o %t/aa/two/three/libAee.dylib
+##################
+
+
+#--- c.s
+.text
+.global _c_func
+_c_func:
+  mov $0, %rax
+  ret
+
+#--- b.s
+.text
+.global _b_func
+
+_b_func:
+  mov $0, %rax
+  ret
+
+#--- a.s
+.text
+.global _a_func
+_a_func:
+  mov $0, %rax
+  ret

@llvmbot
Copy link
Member

llvmbot commented Apr 10, 2025

@llvm/pr-subscribers-lld-macho

Author: Vy Nguyen (oontvoo)

Changes

Details:
When we have the following scenario:

  • lib_a re-exports lib_b
  • lib_b re-exports @rpath/lib_c
    • lib_b contains LC_RPATH

Previously, lld-macho cannot find lib_c because it was attempting to resolve the '@rpath' from lib_b (which had no LC_RPATH defined). The change here is to also consider all the LC_RPATH rom lib_b when trying to find lib_c.

Inspired by real-life example when linking with libXCTestSwiftSupport.dylib (which re-exports XCTest, which re-exports XCTestCore)


Full diff: https://github.com/llvm/llvm-project/pull/135241.diff

2 Files Affected:

  • (modified) lld/MachO/InputFiles.cpp (+21-3)
  • (added) lld/test/MachO/reexport-with-rpath.s (+37)
diff --git a/lld/MachO/InputFiles.cpp b/lld/MachO/InputFiles.cpp
index 9adfbc9d3f6f5..9afa5feaa1693 100644
--- a/lld/MachO/InputFiles.cpp
+++ b/lld/MachO/InputFiles.cpp
@@ -1633,6 +1633,17 @@ static DylibFile *findDylib(StringRef path, DylibFile *umbrella,
       if (std::optional<StringRef> dylibPath = resolveDylibPath(newPath.str()))
         return loadDylib(*dylibPath, umbrella);
     }
+    // If not found in umbrella, try the rpaths specified via -rpath too.
+    for (StringRef rpath : config->runtimePaths) {
+      newPath.clear();
+      if (rpath.consume_front("@loader_path/")) {
+        fs::real_path(umbrella->getName(), newPath);
+        path::remove_filename(newPath);
+      }
+      path::append(newPath, rpath, path.drop_front(strlen("@rpath/")));
+      if (std::optional<StringRef> dylibPath = resolveDylibPath(newPath.str()))
+        return loadDylib(*dylibPath, umbrella);
+    }
   }
 
   // FIXME: Should this be further up?
@@ -1678,9 +1689,16 @@ static bool isImplicitlyLinked(StringRef path) {
 void DylibFile::loadReexport(StringRef path, DylibFile *umbrella,
                          const InterfaceFile *currentTopLevelTapi) {
   DylibFile *reexport = findDylib(path, umbrella, currentTopLevelTapi);
-  if (!reexport)
-    error(toString(this) + ": unable to locate re-export with install name " +
-          path);
+  if (!reexport) {
+    // If not found in umbrella, retry since some rpaths might have been
+    // defined in "this" dyblib (which contains the LC_REEXPORT_DYLIB cmd) and
+    // not in the umbrella.
+    DylibFile *reexport2 = findDylib(path, this, currentTopLevelTapi);
+    if (!reexport2) {
+      error(toString(this) + ": unable to locate re-export with install name " +
+            path);
+    }
+  }
 }
 
 DylibFile::DylibFile(MemoryBufferRef mb, DylibFile *umbrella,
diff --git a/lld/test/MachO/reexport-with-rpath.s b/lld/test/MachO/reexport-with-rpath.s
new file mode 100644
index 0000000000000..f1b00ddc9f7e5
--- /dev/null
+++ b/lld/test/MachO/reexport-with-rpath.s
@@ -0,0 +1,37 @@
+# REQUIRES: x86
+# RUN: rm -rf %t; split-file %s %t
+# RUN: mkdir -p %t/cc/two/three
+# RUN: mkdir -p %t/bb/two/three
+# RUN: mkdir -p %t/aa/two/three
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/c.s -o %t/c.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/b.s -o %t/b.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/a.s -o %t/a.o
+
+# RUN: %lld -dylib -install_name @rpath/two/three/libCee.dylib %t/c.o -o %t/cc/two/three/libCee.dylib 
+# RUN: %lld -dylib -install_name @rpath/two/three/libBee.dylib -L%t/cc/two/three -sub_library libCee -lCee %t/b.o -o %t/bb/two/three/libBee.dylib -rpath %t/cc
+# RUN: %lld -dylib -install_name @rpath/two/three/libAee.dylib -L%t/bb/two/three -sub_library libBee -lBee %t/a.o -o %t/aa/two/three/libAee.dylib
+##################
+
+
+#--- c.s
+.text
+.global _c_func
+_c_func:
+  mov $0, %rax
+  ret
+
+#--- b.s
+.text
+.global _b_func
+
+_b_func:
+  mov $0, %rax
+  ret
+
+#--- a.s
+.text
+.global _a_func
+_a_func:
+  mov $0, %rax
+  ret

Copy link
Contributor

@nico nico left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#134925 recently was kind of similar. But that was about tbd files and this here is about mach-o files.

I wonder if our behavior here is consistent across .tbd and .dylib inputs.

@oontvoo oontvoo merged commit c5f6190 into llvm:main Apr 28, 2025
9 of 11 checks passed
@oontvoo
Copy link
Member Author

oontvoo commented Apr 28, 2025

Whoops - the last commit didn't get pushed successfully - I've added it in PR/137672

oontvoo pushed a commit that referenced this pull request Apr 28, 2025
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
Details:
When we have the following scenario:

- lib_a re-exports lib_b
- lib_b re-exports @rpath/lib_c
  + lib_b contains LC_RPATH

Previously, lld-macho cannot find lib_c because it was attempting to
resolve the '@rpath' from lib_b (which had no LC_RPATH defined). The
change here is to also consider all the LC_RPATH rom lib_b when trying
to find lib_c.

Inspired by real-life example when linking with
libXCTestSwiftSupport.dylib (which re-exports XCTest, which re-exports
XCTestCore)
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
Details:
When we have the following scenario:

- lib_a re-exports lib_b
- lib_b re-exports @rpath/lib_c
  + lib_b contains LC_RPATH

Previously, lld-macho cannot find lib_c because it was attempting to
resolve the '@rpath' from lib_b (which had no LC_RPATH defined). The
change here is to also consider all the LC_RPATH rom lib_b when trying
to find lib_c.

Inspired by real-life example when linking with
libXCTestSwiftSupport.dylib (which re-exports XCTest, which re-exports
XCTestCore)
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
Details:
When we have the following scenario:

- lib_a re-exports lib_b
- lib_b re-exports @rpath/lib_c
  + lib_b contains LC_RPATH

Previously, lld-macho cannot find lib_c because it was attempting to
resolve the '@rpath' from lib_b (which had no LC_RPATH defined). The
change here is to also consider all the LC_RPATH rom lib_b when trying
to find lib_c.

Inspired by real-life example when linking with
libXCTestSwiftSupport.dylib (which re-exports XCTest, which re-exports
XCTestCore)
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
GeorgeARM pushed a commit to GeorgeARM/llvm-project that referenced this pull request May 7, 2025
Details:
When we have the following scenario:

- lib_a re-exports lib_b
- lib_b re-exports @rpath/lib_c
  + lib_b contains LC_RPATH

Previously, lld-macho cannot find lib_c because it was attempting to
resolve the '@rpath' from lib_b (which had no LC_RPATH defined). The
change here is to also consider all the LC_RPATH rom lib_b when trying
to find lib_c.

Inspired by real-life example when linking with
libXCTestSwiftSupport.dylib (which re-exports XCTest, which re-exports
XCTestCore)
GeorgeARM pushed a commit to GeorgeARM/llvm-project that referenced this pull request May 7, 2025
Ankur-0429 pushed a commit to Ankur-0429/llvm-project that referenced this pull request May 9, 2025
Details:
When we have the following scenario:

- lib_a re-exports lib_b
- lib_b re-exports @rpath/lib_c
  + lib_b contains LC_RPATH

Previously, lld-macho cannot find lib_c because it was attempting to
resolve the '@rpath' from lib_b (which had no LC_RPATH defined). The
change here is to also consider all the LC_RPATH rom lib_b when trying
to find lib_c.

Inspired by real-life example when linking with
libXCTestSwiftSupport.dylib (which re-exports XCTest, which re-exports
XCTestCore)
Ankur-0429 pushed a commit to Ankur-0429/llvm-project that referenced this pull request May 9, 2025
@oontvoo oontvoo deleted the reexport_w_rpath branch June 2, 2025 17:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants