install_name_tool to update a executable to search for dylib in Mac OS X

I have a dynamic libray libtest.dylib that is installed in /PATH/lib, and an execution binary, myapp, that uses the dylib installed in /PATH/bin.

I can run myapp to find the dylib as follows (Is it OK to use DYLD_LIBRARY_PATH on Mac OS X? And, what's the dynamic library search algorithm with it?):

DYLD_LIBRARY_PATH="/PATH/lib" myapp 

I think I can use install_name_tool to update the library and executable so that the library can be found with rpath. I used the hints in this post - How can I specify the rpath in a dylib?.

In lib, I executed this command to add rpath.

install_name_tool -id "@rpath/libtest.dylib" libtest.dylib
install_name_tool -add_rpath "@executable_path/../lib/" libtest.dylib

In bin, I executed install_name_tool -add_rpath "@executable_path/../lib/" myapp.

However, when I executed myapp in bin directory, I have the error messages.

dyld: Library not loaded: libtest.dylib
  Referenced from: /PATH/bin/./myapp
  Reason: image not found
Trace/BPT trap: 5

otool -l myapp shows the rpath is correctly updated in myapp.

Load command 16
          cmd LC_RPATH
      cmdsize 40
         path @executable_path/../lib/ (offset 12)

The same is true with libtest.dylib

Load command 13
          cmd LC_RPATH
      cmdsize 40
         path @executable_path/../lib/ (offset 12)

What might be wrong?

ADDED

Of course, I can use cc -install_name when compile and link time, but I wanted to know how to do the same thing my modifying the generatd dylib and execution binary.

From the lib:

cc -install_name "@loader_path/../lib/libtest.dylib" -dynamiclib -o libtest.dylib test.c

Or, the install_name can use @rpath:

cc -install_name "@rpath/libtest.dylib" -dynamiclib -o libtest.dylib test.c

From the bin:

cc -I../lib -c main.c
cc -o main main.o ../lib/libtest.dylib -Wl,-rpath -Wl,@loader_path/../lib

Or just one line:

cc -I../lib -L../lib -o main main.c -ltest -Wl,-rpath -Wl,@loader_path/../lib

From otool -l, I analyzed what should be added or modified from the original library and binary.

Dylib

The change is in id:

Load command 2 <-- OLD
          cmd LC_ID_DYLIB
      cmdsize 40
         name libtest.dylib (offset 24)
   time stamp 1 Wed Dec 31 18:00:01 1969

Load command 2 <-- NEW
          cmd LC_ID_DYLIB
      cmdsize 64
         name @loader_path/../lib/libtest.dylib (offset 24)

This is the command to accomplish the change:

install_name_tool -id "@loader_path/../lib/libtest.dylib" libtest.dylib 

Or use rpath:

install_name_tool -id "@rpath/libtest.dylib" libtest.dylib

The executable

There are two changes: rpath and load_dylib

Load command 12 <-- OLD
          cmd LC_LOAD_DYLIB
      cmdsize 40
         name libtest.dylib (offset 24)

Load command 12 <-- NEW
          cmd LC_LOAD_DYLIB
      cmdsize 64
         name @loader_path/../lib/libtest.dylib (offset 24)

This is the command to accomplish the change

install_name_tool -change libtest.dylib @loader_path/../lib/libtest.dylib myapp 

Also I needed to add the rpath

Load command 14
          cmd LC_RPATH
      cmdsize 32
         path @loader_path/../lib (offset 12)

This is the command to accomplish the addition:

 install_name_tool -add_rpath "@loader_path/../lib" myapp

The idea

The binary tries to find the library, it knows where it is located from install_name_tool -add_rpath "@loader_path/../lib" myapp. It loads the library, and the library's id is @rpath/libtest.dylib where @rpath is set to @loader_path/../lib in the executable binary to make the match.

Reference

  • Can you please help me understand how Mach-O libraries work in Mac Os X?

Cmake

When using CMake, we can automatize the process with the following addition in CMakeLists.txt file.

Library

The id should be added.

# https://cmake.org/pipermail/cmake/2006-October/011530.html
SET_TARGET_PROPERTIES (test
  PROPERTIES BUILD_WITH_INSTALL_RPATH 1
             INSTALL_NAME_DIR "@rpath"
  )
Executable

The rpath should be specified:

SET(CMAKE_INSTALL_RPATH "@loader_path/../lib/libtest.dylib")