Skip to content

cargo run exports bad DYLD_LIBRARY_PATH on macOS #3366

Closed
@casey

Description

@casey

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:

  1. 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.

  2. We are also linking in a system framework.

  3. 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.

  4. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions