I have been struggling with this concept for a while and I cannot really understand what the difference is between -change
and -id
.The man page states
-id name
Changes the shared library identification name of a dynamic shared library to name. If the Mach-O binary is not a dynamic
shared library and the -id option is specified it is ignored.
-change old new
Changes the dependent shared library install name old to new in the specified Mach-O binary. More than one of these options
can be specified. If the Mach-O binary does not contain the old install name in a specified -change option the option is
ignored.
So far I have experimented with -change
. Suppose I have the following structure
Test.App
|_Contents
|_MacOS
| |_test -----> item A
|_Library
|_test_library.dylib --->item B
|_another_library.dylib --->item C
Now suppose I ran the following on itemB
$ otool -L test_library.dylib
test_library.dylib
/some/path/another_library.dylib -->item D
The above result indicates that test_library.dylib
depends on another_library.dylib
now if I needed to change the location of another_library.dylib
I would do this
install_name_tool -change /some/path/another_library.dylib some/new/path/another_library.dylib test_library.dylib
this would change the location of item D. My question is what does install-name_tool -id
do and when do I use that ?
My understanding is:
-id
: This sets the "install name" that will be used when linking against the dynamic library. It would be run on the target dynamic library file.
-change
: This changes the "install name" after linking and would be run on the executable or dynamic library that links against the target dynamic library.
install-name_tool -id
is used for change the install name
of dylib
, you can use the otool -D
see a dylib install name
in the terminal, it will show the default value for you, the /some/path/another_library.dylib
is the default install name
of another _library.dylib
, of course, you can change it use install-name_tool -id
in the terminal, just use like this in terminal
install-name_tool -id /some/path/another_library_newname.dylib /some/path/another_library.dylib
now,you use the otool -D /some/path/another_library.dylib
, you will find the install name
is /some/path/another_library_newname.dylib
here is my example in picture
id
is used at link time and install name
is used at runtime. They are all information provided for the linker to locate the dylib. I followed this tutorial.
Let me show an example,
$ cat a.cc
#include <iostream>
void a() { std::cout << "a()" << std::endl; }
$ clang++ -c a.cc
$ clang++ -o liba.dylib -dynamiclib a.o
$ otool -L liba.dylib
liba.dylib:
liba.dylib (compatibility version 0.0.0, current version 0.0.0)
/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 400.9.4)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.250.1)
As you can see, the first line is the id
. Let's link with libb.dylib,
$ cat b.cc
#include <iostream>
void a();
void b() { std::cout << "b()" << std::endl; a(); }
$ clang++ -c b.cc
$ clang++ -o libb.dylib -dynamiclib b.o -L. -la
$ otool -L libb.dylib
libb.dylib:
libb.dylib (compatibility version 0.0.0, current version 0.0.0)
liba.dylib (compatibility version 0.0.0, current version 0.0.0)
/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 400.9.4)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.250.1)
Just notice the second line, the id
for liba.dylib is used here. Let's change the id to foo/liba.dylib
and link again,
$ install_name_tool -id foo/liba.dylib liba.dylib
$ otool -D liba.dylib
liba.dylib:
foo/liba.dylib
liba.dylib:
foo/liba.dylib (compatibility version 0.0.0, current version 0.0.0)
/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 400.9.4)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.250.1)
So you see the -D
and -L
all outputs the current id
as foo/liba.dylib
.
Let's link again with liba.dylib again,
$ clang++ -o libb.dylib -dynamiclib b.o -L. -la
$ otool -L libb.dylib
libb.dylib:
libb.dylib (compatibility version 0.0.0, current version 0.0.0)
foo/liba.dylib (compatibility version 0.0.0, current version 0.0.0)
/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 400.9.4)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.250.1)
See the difference? The run time location to find liba.dylib is changed to foo/liba.dylib
at the second line.
Basically, it tells libb.dylib to find liba.dylib from current_dir/foo