The Argument -rpath,'$ORIGIN'
Needs to be prefixed with "-Wl," for most Linux OS (or "-R<path>" for Solaris, -brtl for AIX and "-Wl,+b<path>" for HPUX). It is to pass linker options to your linker through gcc or g++. It is significant if you need to build a binary that depends on other dynamic library. "rpath" means runtime path (to load dependent dynamic library). And the very useful variable that we frequently used is "$ORIGIN" which means the path where your (link) target binary is loaded.
# assuming your target lib is libb.so and it depends on liba.so
# and their deployment structure is like:
<install_root>
|
libb.so
|
<sub_dir>
|
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/<sub_dir>'
# or, without single quote, you can write it like:
# -Wl,-rpath,\$$ORIGIN/<sub_dir>
# and -shared and -fPIC is usually need to build dynamic linkable library
gcc -o libb.so -shared -fPIC -L. -la -Wl,-rpath,\$$ORIGIN/<sub_dir> libb.c
Check RPATH with 'readelf':
# -d means dynamic
readelf -d <dll_name>
# 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 <iostream>
#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 <iostream>
#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 <iostream>
#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 <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
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