why can a dylib missing dependency still be loaded?

I have two dylibs built with different parameters on different machines. Both have the same dependency(@rpath/libc++.dylib).

When @rpath/libc++.dylib is missing, one of them can still be laoded via dlopen with RTLD_NOW, and I want to understand why.

Additional infomation:

  1. Both dylibs are the same architecture(arm64)
  2. They had identical LC_RPATH settings. But I've removed them via install_name_tool just to simplify the problem.
  3. Through otool -l to view load commands, I can't find any differnent between them except they had different libSystem.B.dylib version.

And then,I through setting DYLD_PRINT_SEARCHING=1 and load them. I found differenes in their dependency search processes, but' I'm unsure what causes this discrepancy.

these are outputs:

./a.out libchrome_zlib.dylib.good
dyld[37001]: find path "/usr/lib/libc++.1.dylib"
dyld[37001]:   possible path(original path on disk): "/usr/lib/libc++.1.dylib"
dyld[37001]:   possible path(cryptex prefix): "/System/Volumes/Preboot/Cryptexes/OS/usr/lib/libc++.1.dylib"
dyld[37001]:   possible path(original path): "/usr/lib/libc++.1.dylib"
dyld[37001]:   found: dylib-from-cache: (0x000A) "/usr/lib/libc++.1.dylib"
dyld[37001]: find path "/usr/lib/libSystem.B.dylib"
dyld[37001]:   possible path(original path on disk): "/usr/lib/libSystem.B.dylib"
dyld[37001]:   possible path(cryptex prefix): "/System/Volumes/Preboot/Cryptexes/OS/usr/lib/libSystem.B.dylib"
dyld[37001]:   possible path(original path): "/usr/lib/libSystem.B.dylib"
dyld[37001]:   found: dylib-from-cache: (0x00AB) "/usr/lib/libSystem.B.dylib"
dyld[37001]: find path "libchrome_zlib.dylib.good"
dyld[37001]:   possible path(original path on disk): "libchrome_zlib.dylib.good"
dyld[37001]:   found: dylib-from-disk: "libchrome_zlib.dylib.good"
dyld[37001]: find path "@rpath/libc++.dylib"
dyld[37001]:   possible path(default fallback): "/usr/local/lib/libc++.dylib"
dyld[37001]:   possible path(default fallback): "/usr/lib/libc++.dylib"
dyld[37001]:   found: dylib-from-cache: (0x000A) "/usr/lib/libc++.dylib"
./a.out libchrome_zlib.dylib.bad 
dyld[41256]: find path "/usr/lib/libc++.1.dylib"
dyld[41256]:   possible path(original path on disk): "/usr/lib/libc++.1.dylib"
dyld[41256]:   possible path(cryptex prefix): "/System/Volumes/Preboot/Cryptexes/OS/usr/lib/libc++.1.dylib"
dyld[41256]:   possible path(original path): "/usr/lib/libc++.1.dylib"
dyld[41256]:   found: dylib-from-cache: (0x000A) "/usr/lib/libc++.1.dylib"
dyld[41256]: find path "/usr/lib/libSystem.B.dylib"
dyld[41256]:   possible path(original path on disk): "/usr/lib/libSystem.B.dylib"
dyld[41256]:   possible path(cryptex prefix): "/System/Volumes/Preboot/Cryptexes/OS/usr/lib/libSystem.B.dylib"
dyld[41256]:   possible path(original path): "/usr/lib/libSystem.B.dylib"
dyld[41256]:   found: dylib-from-cache: (0x00AB) "/usr/lib/libSystem.B.dylib"
dyld[41256]: find path "libchrome_zlib.dylib.bad"
dyld[41256]:   possible path(original path on disk): "libchrome_zlib.dylib.bad"
dyld[41256]:   found: dylib-from-disk: "libchrome_zlib.dylib.bad"
dyld[41256]: find path "@rpath/libc++.dylib"
dyld[41256]:   not found: "@rpath/libc++.dylib"
dlopen failed: dlopen(libchrome_zlib.dylib.bad, 0x0002): Library not loaded: @rpath/libc++.dylib
  Referenced from: <42E93041-7B58-365B-9967-04AE754AA9F0> /Users/jiangzh/dlopen/libchrome_zlib.dylib.bad
  Reason: no LC_RPATH's found

I’m not sure what’s happen in your exact case but I wanna step back and ask about the big picture. /usr/lib/libc++.1.dylib is macOS’s built-in copy of the C++ standard library. It’s meant to be referenced by an absolute path:

% grep install-name /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/lib/libc++.1.tbd 
install-name:    '/usr/lib/libc++.1.dylib'

Why are you trying to load it via an rpath-relative reference?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

I have already found the answer.

This is because dyld has a default fallback search behavior when the dylib is built on an older macOS version (prior to 14.0).

In such case, dyld will attempt to search missing dependcy libraries in these fallback paths: /usr/local/lib:/usr/lib.

As a result, the dependcy library libc++.dylib can be loaded since it always exists in the library cache.

some reference code:

JustInTimeLoader.cpp#L515

JustInTimeLoader.cpp#L1011

MachOFile.cpp#L2879

why can a dylib missing dependency still be loaded?
 
 
Q