Twinklebear / oidn-rs

Rust bindings to Intel's OpenImageDenoise Library
MIT License
24 stars 12 forks source link

@rpath/oidn lib not found #6

Closed virtualritz closed 3 years ago

virtualritz commented 3 years ago

I get this on macOS when trying to dynamically load my crate (crate-type cdylib) from an executable:

system error: dlopen(/Users/moritz/code/r-display/target/debug/libr_display.dylib, 1):
  Library not loaded: @rpath/libOpenImageDenoise.0.dylib
  Referenced from: /Users/moritz/code/r-display/target/debug/libr_display.dylib
  Reason: image not found)
Twinklebear commented 3 years ago

Maybe an issue with it not being in the runtime path? I'm not that familiar with Mac or these kind of issues there but maybe setting the DYLD_LIBRARY_PATH to have libOpenImageDenoise in the path would work (if you haven't tried that already)? Otherwise I do have one now so can find some time to look at this, maybe the build script needs to set this info. I think on linux it needs to be in your LD_LIBRARY_PATH like other .so lookups

virtualritz commented 3 years ago

TLDR; I think the issue is that there is an RPATH stored in the lib that the crate builds.

Ofc. if I set LD_LIBRARY_PATH this works.

There are two scenarios:

  1. I am using a crate doing R&D and want things to just work on my system.
  2. I am shipping a binary to the outside world.

In the former scenario stuff should just work. This is why I created this ticket: it doesn't.

In the latter scenario it is expected that I need to do something. Usually package the lib with my app and put a relative RPATH into my binary so it can be found.

In my NSI crate I just link against some 3rd party lib using -L passed to clang. This works because there is no explicit RPATH stored in the executable. I guess if the lib is elsewhere on another system it would break. But this is ok (see 1.)

virtualritz commented 3 years ago

My nsi-sys crate does not suffer from this issue. It also requires a pre-built 3rd party dynamic library and if I use this crate my resulting executable has that lib dependency showing up without the @rpath prefix.

I tried various things to get oidn-rs to spit out a lib that depends on libOpenImageDenoise.0.dylib instead of @rpath/libOpenImageDenoise.0.dylib but no success. I'm really puzzled what part of the build process is inserting this @rpath.

virtualritz commented 3 years ago

I found the culprit to be that the pre-compiled OIDN lib has the @rpath embedded. rustc just copies this when linking and there seems to be no way to change this. The solution for me was to manually replace the @rpaths with @loader_paths using:

install_name_tool -id "@loader_path/libOpenImageDenoise.0.dylib" libOpenImageDenoise.0.dylib
install_name_tool -id "@loader_path/libtbb.dylib" libtbb.dylib
install_name_tool -id "@loader_path/libtbbmalloc.dylib" libtbbmalloc.dylib
install_name_tool -change "@rpath/libtbb.dylib" "@loader_path/libtbb.dylib" libOpenImageDenoise.0.dylib
install_name_tool -change "@rpath/libtbbmalloc.dylib" "@loader_path/libtbbmalloc.dylib" libOpenImageDenoise.0.dylib

Note: the above is for macOS. For Linux the equivalent should be replacing @rpath with $ORIGIN.

All this assumes the dependencies live in the same folder as the library/executable linking against OIDN.

Twinklebear commented 3 years ago

Oh interesting, I wonder if there is some way to tweak the rpath setting from the build script in the crate so this issue doesn't come up

virtualritz commented 3 years ago

I guess what you really want is for the user to choose what they want: @rpath, @loader_path/$ORIGIN, some custom hard-coded path or plain nothing (dynamic linker will have to figure everything out from DYLD_LIBRARY_PATH).

Edit: as far as I can tell there is no comfy way to do this in rust. It is also debatable if should be the job of the linker to change these paths on-the-fly in libraries not generated by rust. As would be the case here.