Dynamic libraries and prefer-dynamic on macOS


Yesterday I ran into an issue that when using a shared library (linked with cdylib) and the main application not using -C prefer-dynamic it would get crashes inside the allocator. The reason I assume is because two allocators had been setup and that causes issues. I had two callstacks like this (shortened) that were using different addresses

    #5 0x10058c747 in _$LT$alloc..raw_vec..RawVec$LT$T$C$$u20$A$GT$$GT$::try_reserve::hf5d4ff372ed65bef raw_vec.rs:541
    #6 0x10058cc84 in _$LT$alloc..raw_vec..RawVec$LT$T$C$$u20$A$GT$$GT$::reserve::ha6e52b3c7a24081f raw_vec.rs:555

    #3 0x1105570e7 in _$LT$alloc..raw_vec..RawVec$LT$T$C$$u20$A$GT$$GT$::try_reserve::hf97724ac05f26392 raw_vec.rs:541
    #4 0x110557624 in _$LT$alloc..raw_vec..RawVec$LT$T$C$$u20$A$GT$$GT$::reserve::h81bf31fab984b813 raw_vec.rs:555

Now this issue I was able to solve by using export RUSTFLAGS='-C prefer-dynamic' and thus both my shared libs and main application now uses the shared run-time library. So far so good.

The problem is that using cargo run it runs just fine but using target/debug/exe I get

dyld: Library not loaded: @rpath/libstd-8a3965f7ed752bd1.dylib
  Referenced from: <path>/hello_world/target/debug/hello_world
  Reason: image not found

I can use otool to change the path to be a fixed one after compilation but this is somewhat annoying. So to my questions:

  1. Is there another way to pass the RUSTFLAGS to cargo without using a global env?
  2. As cargo run can figure out the run-time lib how can I make it work without requiring cargo run?
  3. When I need to deploy my application I need to figure out the exact name (and location) of the run-time library so I can copied to be include next to my application. What is the best way to achive this? I could parse the output from otool but I hope there is a better way.
  4. I assume the problem will be similar on other platforms (such as Windows) so I need to do something similar there as well. I wonder if anyone has any experience with that there?


Replying to myself:

  1. One can use rustc --print sysroot

That way I can figure out where to find the libstd file by doing some traversing and then patch up the exe afterwards. This way I can also find it for deployment.

I believe you can also put RUSTFLAGS in your project’s .cargo/config file (or ~/.cargo/config to have it work globally) and it will be automatically applied by cargo. This is probably more useful than the solution you found because it can be entered into version control and applied based on the target triple.

Configuration keys

All of the following keys are optional, and their defaults are listed as their value unless otherwise noted.

# For the following sections, $triple refers to any valid target triple, not the
# literal string "$triple", and it will apply whenever that target triple is
# being compiled to. 'cfg(...)' refers to the Rust-like `#[cfg]` syntax for
# conditional compilation.
# custom flags to pass to all compiler invocations that target $triple
# this value overrides build.rustflags when both are present
rustflags = ["..", ".."]

Ah, good to know. Thanks!

I tried this out today and it works fine like this

rustflags = ["-C", "link-arg=-Wl,-rpath,/path/to/something/lib"]

My problem I’m currently having I would like to have a environment for the path like

rustflags = ["-C", "link-arg=-Wl,-rpath,$(SOME_VAR)/lib"]

But I can’t seem to find a way to do this :confused: