Sunday, 20 February 2022

Where’s that library?

This note is about some difficulties using shared libraries (.dylibs) on macOS.

On Unix-like operating systems, when an executable that uses a shared library loads, the loader ( on Linux, dyld on macOS) uses a search path to find the shared library.

If you don’t do anything else, there’s a standard search path (on macOS, /usr/local/lib:/usr/lib). You can prefix this using the environment variable LD_LIBRARY_PATH on Linux, or DYLD_LIBRARY_PATH on macOS (some say that macOS allows either form, but for me (Monterey) LD_LIBRARY_PATH doesn’t).

If (speaking of macOS only now) you specify -rpath path then that path is baked into the resulting executable or shared library (‘dylib’).

In the case of building cvc4, where the target build structure is


the path baked in (found by otool -l cvc4 | tail) is


You find the shared libraries used by cvc4 by

$ otool -L cvc4
	@rpath/libcvc4parser.7.dylib (compatibility version 7.0.0, current version 0.0.0)
	@rpath/libcvc4.7.dylib (compatibility version 7.0.0, current version 0.0.0)
	/opt/gcc-12.0.1/lib/libgmp.10.dylib (compatibility version 15.0.0, current version 15.1.0)
	/opt/gcc-12.0.1/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 7.30.0)
	/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1238.0.0)

which means that whenever you run cvc4 it’ll translate the @rpath into the baked-in path.

Unfortunately, not only “whenever” but also “wherever”. You’d hope to be able to copy everything under /Volumes/Miscellaneous1/spark2014/install into an archive so that someone else would be able to install to some convenient place and have it just work without that pesky development disk. No:

$ bin/cvc4
dyld[6029]: Library not loaded: @rpath/libcvc4parser.7.dylib
  Referenced from: /Users/simon/tmp/libexec/spark/bin/cvc4
  Reason: tried: '/Volumes/Miscellaneous1/spark2014/install/libexec/spark/lib/libcvc4parser.7.dylib' (no such file),
  '/Volumes/Miscellaneous1/spark2014/install/libexec/spark/lib/libcvc4parser.7.dylib' (no such file),
  '/usr/local/lib/libcvc4parser.7.dylib' (no such file),
  '/usr/lib/libcvc4parser.7.dylib' (no such file)
Abort trap: 6

(and who wants to require users to set DYLD_LIBRARY_PATH?)

A manual workround is to edit the executable so it doesn’t use the baked-in path.

As well as @rpath, you can use @load_path, which is where dyld found the executable or dylib that needs the referenced dylib, or @executable_path, which is where (in this case) cvc4 was found.

We need to change @rpath/libcvc4parser.7.dylib to @executable_path/../lib/libcvc4parser.7.dylib:

$ install_name_tool \
  -change \
  @rpath/libcvc4parser.7.dylib \
  @executable_path/../lib/libcvc4parser.7.dylib \

and likewise for libcvc4.7.dylib.

What would have made things easier would have been if cvc4 had been linked with -rpath @executable_path/../lib - this works because we know that the executable is in spark/bin and the dylib is in spark/lib.

No comments:

Post a Comment