Needs to be prefixed with "-Wl," for most Linux OS (or "-R
# assuming your target lib is libb.so and it depends on liba.so # and their deployment structure is like:| libb.so | | liba.so # then you should supply below argument to your gcc or g++: # the double '$' is necessary if it is written in makefile (as escaping char) # -Wl,-rpath,'$$ORIGIN/ ' # or, without single quote, you can write it like: # -Wl,-rpath,\$$ORIGIN/ # and -shared and -fPIC is usually need to build dynamic linkable library gcc -o libb.so -shared -fPIC -L. -la -Wl,-rpath,\$$ORIGIN/ libb.c
Check RPATH with 'readelf':
# -d means dynamic readelf -d# output looks like: # Tag Type Name/Value # 0x0000000000000001 (NEEDED) Shared library: [libb.so] # 0x0000000000000001 (NEEDED) Shared library: [libstdc++.so.6] # 0x0000000000000001 (NEEDED) Shared library: [libm.so.6] # 0x0000000000000001 (NEEDED) Shared library: [libgcc_s.so.1] # 0x0000000000000001 (NEEDED) Shared library: [libc.so.6] # 0x000000000000000f (RPATH) Library rpath: [$ORIGIN/libb] # 0x000000000000000c (INIT) 0x7d8 # 0x000000000000000d (FINI) 0xa88 # 0x000000006ffffef5 (GNU_HASH) 0x158 # 0x0000000000000005 (STRTAB) 0x3c8 # 0x0000000000000006 (SYMTAB) 0x1a0 # 0x000000000000000a (STRSZ) 479 (bytes) # 0x000000000000000b (SYMENT) 24 (bytes) # 0x0000000000000003 (PLTGOT) 0x200e28 # 0x0000000000000002 (PLTRELSZ) 216 (bytes) # 0x0000000000000014 (PLTREL) RELA # 0x0000000000000017 (JMPREL) 0x700 # 0x0000000000000007 (RELA) 0x628 # 0x0000000000000008 (RELASZ) 216 (bytes) # 0x0000000000000009 (RELAENT) 24 (bytes) # 0x000000006ffffffe (VERNEED) 0x5d8 # 0x000000006fffffff (VERNEEDNUM) 2 # 0x000000006ffffff0 (VERSYM) 0x5a8 # 0x000000006ffffff9 (RELACOUNT) 3 # 0x0000000000000000 (NULL) 0x0
Sample
// liba.h #ifndef LIBA_GPGH7YWD_H #define LIBA_GPGH7YWD_H namespace dlltest { void foo(); class A { public: A(); }; } /* dlltest */ #endif /* end of include guard: LIBA_GPGH7YWD_H */ // liba.cpp #include#include "liba.h" namespace dlltest { void foo() { std::cout << "liba:foo" << std::endl; } A::A() { foo(); } } // libb.h #ifndef LIBB_5NC56UVQ_H #define LIBB_5NC56UVQ_H #include "liba.h" namespace dlltest { void goo(); class B : public A { public: B(); }; } #endif /* end of include guard: LIBB_5NC56UVQ_H */ // libb.cpp #include #include "libb.h" namespace dlltest { void goo() { std::cout << "libb:goo" << std::endl; } B::B() { goo(); } } // libc1.h: // you never want to name it libc.so because it is // the name of standard C library #ifndef LIBC_1Y6MU09S_H #define LIBC_1Y6MU09S_H #include "libb.h" extern "C" void hoo(); extern "C" void hoo2(); namespace dlltest { class C : public B { public: C(); }; } #endif /* end of include guard: LIBC_1Y6MU09S_H */ // libc1.cpp #include #include "libc1.h" void hoo() { std::cout << "libc:hoo" << std::endl; } void hoo2() { dlltest::C c; } namespace dlltest { C::C() { hoo(); } }
// main.cpp // implicit dll dependency #include "libc1.h" using namespace dlltest; int main(int argc, const char *argv[]) { C c; return 0; }
// main2.cpp // load dll with dlopen #include#include #include typedef void(*FP)(); int main(int argc, char **argv) { void *handle; char *error; handle = dlopen ("./libc1.so", RTLD_LAZY); if (!handle) { fprintf (stderr, "%s\n", dlerror()); exit(1); } dlerror(); /* Clear any existing error */ FP fp = (FP)dlsym(handle, "hoo2"); if ((error = dlerror()) != NULL) { fprintf (stderr, "%s\n", error); exit(1); } (*fp)(); dlclose(handle); return 0; }
// makefile # Dynamic loading shared library # dynamic libraries could be independent on each other when built # but, in this case, executable binary depends on all of them dll: dir g++ -fPIC -shared -o bin/liba.so liba.cpp g++ -fPIC -shared -o bin/libb.so libb.cpp g++ -fPIC -shared -o bin/libc1.so libc1.cpp g++ -o bin/dlink main.cpp -Lbin -lc1 -lb -la -Wl,-rpath,\$$ORIGIN # libc1.so is loaded by dlopen in main2.cpp # libc1.so implicitly depends on libb.so, without RPATH libc1 # doesn't know where to find it # libb.so implicitly depends on liba.so, without RPATH libb doesn't # know where to find it # need to link libb.so and liba.so to demo, so demo will load libb # and liba when it needs them dlopen: dir2 g++ -fPIC -shared -o bin/liba.so liba.cpp g++ -fPIC -shared -o bin/libb/libb.so libb.cpp g++ -fPIC -shared -o bin/libc1.so libc1.cpp g++ -o bin/dldemo main2.cpp -Lbin/libb -Lbin -la -lb -ldl -Wl,-rpath,\$$ORIGIN -Wl,-rpath,\$$ORIGIN/libb # alternative to dlopen case: # add libb dependency explicitly when build libc1: # -Lbin/libb and -lb is essential because you want libc1 know libb # has the symbols it needs and -rpath tells libc1 where to find libb # but liba's search path is not built into libb, it's needed to # build the dependency into dldemo # otherwise, update the rpath of libb(for liba) should also work, check dlopen3 dlopen2: dir2 g++ -fPIC -shared -o bin/liba.so liba.cpp g++ -fPIC -shared -o bin/libb/libb.so libb.cpp g++ -fPIC -shared -o bin/libc1.so libc1.cpp -Lbin/libb -lb -Wl,-rpath,\$$ORIGIN/libb g++ -o bin/dldemo main2.cpp -Lbin -la -ldl -Wl,-rpath,\$$ORIGIN # second alternative to dlopen case: # dldemo implicitly depends on none of 3 libs, but you still need to tell it where to # find libc1 with -rpath. otherwise, if you specify correct path to libc1.so when # dlopen (and start the program in correct working diretory if you use indirect # file path in dlopen), then -rpath is not needed # libc1 implicitly depends on libb and libc1 know where to find libb with -rpath setup # libb implicitly depends on liba and libb know where to find liba with -rpath setup dlopen3: dir2 g++ -fPIC -shared -o bin/liba.so liba.cpp g++ -fPIC -shared -o bin/libb/libb.so libb.cpp -Lbin -la -Wl,-rpath,\$$ORIGIN/.. g++ -fPIC -shared -o bin/libc1.so libc1.cpp -Lbin/libb -lb -Wl,-rpath,\$$ORIGIN/libb #g++ -o bin/dldemo main2.cpp -ldl -Wl,-rpath,\$$ORIGIN g++ -o bin/dldemo main2.cpp -ldl # demostrate how to use -rpath to organize libraries into separate folders strc1: dir2 g++ -fPIC -shared -o bin/liba.so liba.cpp g++ -fPIC -shared -o bin/libb/libb.so libb.cpp g++ -fPIC -shared -o bin/libc1.so libc1.cpp g++ -o bin/strc1 main.cpp -Lbin -lc1 -la -Lbin/libb -lb \ -Wl,-rpath,\$$ORIGIN -Wl,-rpath,\$$ORIGIN/libb # mix static lib with dynamic lib mix1: dir g++ -fPIC -shared -o bin/liba.so liba.cpp g++ -fPIC -c libb.cpp g++ -fPIC -shared -o bin/libc1.so libc1.cpp libb.o g++ -o bin/mix1 main.cpp -Lbin -lc1 -la -Wl,-rpath,\$$ORIGIN rm *.o # static link: # if you build 'dll' before 'mix2' without clean, you can find the below -lc1 # will link dynamic lib libc1.so instead of static lib libc1.a # the conclusion is shared lib link is default behavior mix2: dir clean g++ -fPIC -shared -o bin/liba.so liba.cpp g++ -fPIC -shared -o bin/libb.so libb.cpp g++ -c libc1.cpp ar -cvq bin/libc1.a libc1.o g++ -o bin/mix2 main.cpp -Lbin -lc1 -lb -la -Wl,-rpath,\$$ORIGIN rm *.o dir: if [ ! -d bin ]; then mkdir bin; fi dir2: if [ ! -d bin/libb ]; then mkdir -p bin/libb; fi clean: rm -rf bin/*
For more information:
- check 'man ld'
- RPATH on Wiki
- Shared Library Search Path
- use ldd command to check dependency of a binary