Description
I've been going down the rabbit hole for a while on this one. Thankfully though, it seems like it's actually hopefully a pretty simple fix. Or, if it isn't, at least maybe I'm reporting the issue in the right repo ^_^
I originally thought it was a git2-rs issue, and reported it in https://github.com/alexcrichton/git2-rs/issues/175. Then I thought it was a rustc issue, and reported it here: rust-lang/rust#36250.
Finally though, I've gotten to the bottom of it, and it appears to be a cargo bug.
A few things need to happen to trigger the issue:
-
We are linking ina dynamic library that is not provided by the system, for example something from homebrew in
/usr/local/lib
, or from macports in/opt/local/lib
. -
We are also linking in a system framework.
-
In the nonstandard location in 1., there is a dynamically linked library which the system framework from 2. needs. However, this library in 1. is not the version that the framework expects.
-
We run the binary with
cargo run
.
To make everything concrete, let's say that we're linking in openssl
provided by macports in /opt/local/lib
, the framework we're using is the ApplicationServices
framework, and we also happen to have libiconv
installed in /opt/local/lib
.
Cargo will export DYLD_LIBRARY_PATH=/opt/local/lib:...
, and we'll get the following error:
: cargo run
Compiling rust-dynamic-linker-error v0.0.0 (file:///Users/rodarmor/src/rust-dynamic-linker-error)
Finished debug [unoptimized + debuginfo] target(s) in 0.34 secs
Running `target/debug/rust-dynamic-linker-error`
dyld: Symbol not found: _iconv
Referenced from: /System/Library/PrivateFrameworks/LanguageModeling.framework/Versions/A/LanguageModeling
Expected in: /opt/local/lib/libiconv.2.dylib
in /System/Library/PrivateFrameworks/LanguageModeling.framework/Versions/A/LanguageModeling
What's happening is that the LanguageModeling
framework, which is a transitive dependency of the ApplicationServices
framework also depends on libiconv
. Normally it finds it in /usr/lib/libiconv.dylib
. However, because of the value of DYLD_LIBRARY_PATH
, it finds it in /usr/local/lib/libiconv.dylib
, and we get the error because it is the wrong version with a missing symbol.
If we run the binary directly, without the environment variable, it works:
: ./target/debug/rust-dynamic-linker-error
Hello, world!
Although the conditions needed to trigger this bug may seem esoteric, they are actually very common on mac development machines. Developers often have a ton of stuff installed via macports or homebrew.
There are a few ways that this might be resolved:
The DYLD_LIBRARY_PATH
value seems to be unnecessary at least in this case, since the binary works when run directly. If so, perhaps we can avoid exporting it so as not to cause this breakage? However, perhaps there are other situations where the variable is necessary to find libraries? What's the purpose of the export?
If the DYLD_LIBRARY_PATH
value is necessary, then we need to communicate that to developers somehow. It would be very frustrating to have a binary which runs with cargo run
, but which cannot be run directly.
And, if we do need DYLD_LIBRARY_PATH
, then we should consider if DYLD_FALLBACK_LIBRARY_PATH
might suffice. DYLD_FALLBACK_LIBRARY_PATH
does not disrupt the normal order of dynamic library lookup, but instead is only consulted if the dynamic linker cannot find a library in the normal places.