wiki:LinkingDarwin

Version 3 (modified by smm, 10 years ago) (diff)

--

Below are some tests that illustrate the behaviour of the OSX linker on 10.4.10 and 10.5.2 (Intel) with two level namespaces.

Create a test directory

mkdir /Users/smm/dyldtest
cd /Users/smm/dyldtest

Create the following .cc files

a.cc:

#include <iostream>
void a() { std::cout << "a()" << std::endl; }

b.cc:

#include <iostream>
void a();
void b() { std::cout << "b()" << std::endl; a(); }

c.cc:

#include <iostream>
void b();
void c() { std::cout << "c()" << std::endl; b(); }

d.cc:

void c();
int main(int, char**) { c(); return 0; }

and compile them all:

g++ -c a.cc b.cc c.cc d.cc

Create libraries from a.o and b.o:

g++ -o liba.dylib -dynamiclib a.o
g++ -o libb.dylib -dynamiclib b.o -L. -la

Notice the otool -L output for these libraries:

liba.dylib:
        liba.dylib
        /usr/lib/libstdc++.6.dylib
        /usr/lib/libgcc_s.1.dylib
        /usr/lib/libSystem.B.dylib
 
libb.dylib:
        libb.dylib
        liba.dylib
        /usr/lib/libstdc++.6.dylib
        /usr/lib/libgcc_s.1.dylib
        /usr/lib/libSystem.B.dylib

Create a library from c.o in various ways

g++ -o libc.dylib -dynamiclib c.o -L. -lb
  • 10.4.10: works
  • 10.5.2: works
rm libc.dylib
mkdir a
mv liba.dylib a/
export LD_LIBRARY_PATH=/Users/smm/dyldtest/a
g++ -o libc.dylib -dynamiclib c.o -L. -lb
  • 10.4.10: fails
  • 10.5.2: works
export DYLD_LIBRARY_PATH=/Users/smm/dyldtest/a
g++ -o libc.dylib -dynamiclib c.o -L. -lb
  • 10.4.10: fails
  • 10.5.2: works
export DYLD_FALLBACK_LIBRARY_PATH=/Users/smm/dyldtest/a
g++ -o libc.dylib -dynamiclib c.o -L. -lb
  • 10.4.10: fails
  • 10.5.2: works

OK, what about specifying -L options for all directories that might contain dependent libraries?

g++ -o libc.dylib -dynamiclib c.o -L. -L/Users/smm/dyldtest/a -lb
  • 10.4.10: fails
  • 10.5.2: works

However:

g++ -o libc.dylib -dynamiclib c.o -L. -L/Users/smm/dyldtest/a -lb -la
  • 10.4.10: works
  • 10.5.2: works

Lets try a correct relative path to liba.dylib in libb.dylib:

install_name_tool -change liba.dylib a/liba.dylib libb.dylib
g++ -o libc.dylib -dynamiclib c.o -L. -lb

works. Investigating further:

rm libc.dylib
mkdir arg
mv a/ arg/.
g++ -o libc.dylib -dynamiclib c.o -L. -lb
  • 10.4.10: fails as expected
  • 10.5.2: works
g++ -o libc.dylib -dynamiclib c.o -L. -L/Users/smm/dyldtest/arg -lb

fails

  • 10.4.10: fails
  • 10.5.2: works
export DYLD_LIBRARY_PATH=/Users/smm/dyldtest/arg
g++ -o libc.dylib -dynamiclib c.o -L. -L/Users/smm/dyldtest/arg -lb
  • 10.4.10: fails
  • 10.5.2: works
cd arg
mv a ../
g++ -o libc.dylib -dynamiclib ../c.o -L../ -lb
  • 10.4.10: fails
  • 10.5.2: works

10.4.2

It looks like the relative path name in the install_name for liba.dylib stored inside libb.dylib is evaluated relative to the current working directory of the link command, not the -L options or [DY]LD_LIBRARY_PATH.

10.5.2

It looks like indirect dylibs aren't looked for at all at link time. Indeed, from man ld:

Indirect dynamic libraries

     If the command line specifies to link against dylib A, and when dylib A was built it linked against
     dylib B, then B is considered an indirect dylib.  When linking for two-level namespace, ld does not
     look at indirect dylibs, except when re-exported by a direct dylibs.

10.4.10: Does DYLD_LIBRARY_PATH override an install_name with an absolute path?

cd /Users/smm/dyldtest
rm -rf a/ arg/ *.dylib
g++ -o liba.dylib -dynamiclib a.o
mkdir a/
mv liba.dylib a/
install_name_tool -id /Users/smm/dyldtest/a/liba.dylib a/liba.dylib
g++ -o libb.dylib -dynamiclib b.o -La -la
g++ -o libc.dylib -dynamiclib c.o -L. -lb
g++ -o test d.o -L. -lc
export DYLD_PRINT_LIBRARIES=y
./test

results in this:

dyld: loaded: /Users/smm/dyldtest/./test
dyld: loaded: /usr/lib/libstdc++.6.dylib, cpu-sub-type: 3
dyld: loaded: libc.dylib
dyld: loaded: /usr/lib/libgcc_s.1.dylib, cpu-sub-type: 3
dyld: loaded: /usr/lib/libSystem.B.dylib, cpu-sub-type: 3
dyld: loaded: /usr/lib/system/libmathCommon.A.dylib, cpu-sub-type: 3
dyld: loaded: libb.dylib
dyld: loaded: /Users/smm/dyldtest/a/liba.dylib
c()
b()
a()

Next:

mkdir adagger
cp a/liba.dylib adagger
export DYLD_LIBRARY_PATH=/Users/smm/dyldtest/adagger
./test

which gives

dyld: loaded: /Users/smm/dyldtest/./test
dyld: loaded: /usr/lib/libstdc++.6.dylib, cpu-sub-type: 3
dyld: loaded: libc.dylib
dyld: loaded: /usr/lib/libgcc_s.1.dylib, cpu-sub-type: 3
dyld: loaded: /usr/lib/libSystem.B.dylib, cpu-sub-type: 3
dyld: loaded: /usr/lib/system/libmathCommon.A.dylib, cpu-sub-type: 3
dyld: loaded: libb.dylib
dyld: loaded: /Users/smm/dyldtest/adagger/liba.dylib
c()
b()
a()

So the answer is yes.

10.5.2: Does [DY]LD_LIBRARY_PATH override an @rpath/ prefixed install_name combined with -Wl,-rpath -Wl,directory1 ...?