cargo: `cargo run` exports bad DYLD_LIBRARY_PATH on macOS
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: https://github.com/rust-lang/rust/issues/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.
About this issue
- Original URL
- State: closed
- Created 8 years ago
- Comments: 26 (20 by maintainers)
Commits related to this issue
- Attempt to solve #3366, regarding spurious shared library search paths. This drops `native_dirs` entries that are not within the target directory when modifying the (DY)LD_LIBRARY_PATH environment va... — committed to pkgw/cargo by pkgw 7 years ago
- Auto merge of #3651 - pkgw:pr-issue-3366, r=alexcrichton Attempt to solve #3366, regarding spurious shared library search paths. This drops `native_dirs` entries that are not within the target outpu... — committed to rust-lang/cargo by bors 7 years ago
Oh for sure
sh.rustup.rswith a HTTP redirect would break everyone so I’m not too worried about that, it looks like the main one is the Cargo documentation which doesn’t pass-L? That’s a bummer 😦I think I’ve figured out a good middle-ground though, the HTTP redirect response body now contains the shell script you recommended, so if
-Lis passed it “just works”, but if it’s not passed you get a messageHm, maybe that wasn’t the best idea. curl without
-Ldoes nothing and prints nothing.If it can’t point directly at the up-to-date file, maybe replace it with something like this:
(or whatever wording sounds good)
I do agree with the assessment that setting
DYLD_FALLBACK_LIBRARY_PATHis the right thing to do. Here’s another case that can trigger the problem on macOS. Many people on Mac install PostgreSQL using Postgres.app. That program placeslibpq.dylibin a subdirectory of the executable, and also vendorslibjpeg.dylib. If we link against libpq when someone has installed it using that method, that directory will be added toDYLD_LIBRARY_PATH, and the same issue will occur withdyld: Symbol not found __cg_jpeg_resync_to_restart