AmrDeveloper/ClangQL

Crash on macOS: dyld[50112]: Library not loaded: @rpath/libclang.dylib

Opened this issue · 5 comments

Describe the bug
Built clangql from git on macOS Sonoma 14.4.1 with rust 1.71.1 and the resulting binary is crashing due to unset LC_RPATH, and hence the binary fails to load libclang.dylib

$ clangql
dyld[50112]: Library not loaded: @rpath/libclang.dylib
  Referenced from: <2E046823-CCFD-39AC-8724-B8765694F2F4> /usr/local/bin/clangql
  Reason: no LC_RPATH's found
Abort trap: 6

I used DYLD_FALLBACK_LIBRARY_PATH to set my libclang.dylib path and it worked afterwards. Not sure if there is any way to fix this via code. I seen that opencv faces this issue in rust

I used DYLD_FALLBACK_LIBRARY_PATH to set my libclang.dylib path and it worked afterwards. Not sure if there is any way to fix this via code. I seen that opencv faces this issue in rust

Thats good i think we can't solve this using code but when we release executables we don't need dyn linking for all users

I will mark this issue as solution for anyone who faced same issue

Thank you

Please note that macOS sanitizes your environment if you have System Integrity Protection (SIP) enabled. E.g. export DYLD_FALLBACK_LIBRARY_PATH=/usr/local/lib followed by echo $DYLD_FALLBACK_LIBRARY_PATH prints nothing.

The following variables are sanitized:

    DYLD_LIBRARY_PATH
    DYLD_FALLBACK_LIBRARY_PATH
    LD_LIBRARY_PATH
    DYLD_INSERT_LIBRARIES

See more info here: https://briandfoy.github.io/macos-s-system-integrity-protection-sanitizes-your-environment/

Please also note that from macOS Sonoma onwards the dynamic linker no longer sets DYLD_FALLBACK_LIBRARY_PATH (it used to be set to /usr/local/lib:/usr/lib previously, which allowed binaries to find libraries in those paths without having to explicitly set library load paths). This means that maintainers of software now have to make an explicit decision on how to find dependent libraries. The choices are to:

  1. Set the absolute path to the dependent library
  2. Use @rpath and LC_PATH loader command to set absolute path to the library from
  3. Use @rpath and LC_PATH loader command to set library load path relative to the executable (use variables like @executable_path or @loader_path).

Last choice offers most flexibility to relocate the executable binary. However, low level system tools should probably use option 1. I think this tool probably should as well.

For more information see man dyld.

So, the proper solution here is to somehow pass -rpath option to the system linker. I've done a bit of research and it looks like cargo build system is a bit immature in this regard. There was a request to add support for setting arbitrary rpaths through cargo over 6 years ago, but it is still Open:

rust-lang/cargo#5077

Please note that macOS sanitizes your environment if you have System Integrity Protection (SIP) enabled. E.g. export DYLD_FALLBACK_LIBRARY_PATH=/usr/local/lib followed by echo $DYLD_FALLBACK_LIBRARY_PATH prints nothing.

The following variables are sanitized:

    DYLD_LIBRARY_PATH
    DYLD_FALLBACK_LIBRARY_PATH
    LD_LIBRARY_PATH
    DYLD_INSERT_LIBRARIES

See more info here: https://briandfoy.github.io/macos-s-system-integrity-protection-sanitizes-your-environment/

Please also note that from macOS Sonoma onwards the dynamic linker no longer sets DYLD_FALLBACK_LIBRARY_PATH (it used to be set to /usr/local/lib:/usr/lib previously, which allowed binaries to find libraries in those paths without having to explicitly set library load paths). This means that maintainers of software now have to make an explicit decision on how to find dependent libraries. The choices are to:

  1. Set the absolute path to the dependent library
  2. Use @rpath and LC_PATH loader command to set absolute path to the library from
  3. Use @rpath and LC_PATH loader command to set library load path relative to the executable (use variables like @executable_path or @loader_path).

Last choice offers most flexibility to relocate the executable binary. However, low level system tools should probably use option 1. I think this tool probably should as well.

For more information see man dyld.

So, the proper solution here is to somehow pass -rpath option to the system linker. I've done a bit of research and it looks like cargo build system is a bit immature in this regard. There was a request to add support for setting arbitrary rpaths through cargo over 6 years ago, but it is still Open:

rust-lang/cargo#5077

Thank you so much for information, I will use them in CD Too

@AmrDeveloper Try using compiler flags -rpath liek @mario-grgic suggested. When I faced this problem for cpp project I built in that way not sure if there is a way to do that in Rust or not.