Linux C编程(8) 使用相对路径加载动态库-rpath和$ORIGIN

商业程序如何加载自己的so
使用LD_LIBRARY_PATH的缺点是要实现设置LD_LIBRARY_PATH。不够自动化。那么大型的商业程序是如何加载自己的so呢。
这里以QtCreator为例。
QtCreator安装在/home/xxx/Qt5.3.1目录下。使用ldd查看qtcreator依赖的so。结果如下:

xxx@ubuntu:~/Qt5.3.1/Tools/QtCreator/bin$ ldd qtcreator
    linux-gate.so.1 =>  (0xb7701000)
    libExtensionSystem.so.1 => /home/xxx/Qt5.3.1/Tools/QtCreator/bin/./../lib/qtcreator/libExtensionSystem.so.1 (0xb76c2000)
    libQt5Widgets.so.5 => /home/xxx/Qt5.3.1/Tools/QtCreator/bin/./../lib/qtcreator/libQt5Widgets.so.5 (0xb707e000)
    libQt5Network.so.5 => /home/xxx/Qt5.3.1/Tools/QtCreator/bin/./../lib/qtcreator/libQt5Network.so.5 (0xb6f19000)
    libQt5Gui.so.5 => /home/xxx/Qt5.3.1/Tools/QtCreator/bin/./../lib/qtcreator/libQt5Gui.so.5 (0xb69d8000)
    libQt5Core.so.5 => /home/xxx/Qt5.3.1/Tools/QtCreator/bin/./../lib/qtcreator/libQt5Core.so.5 (0xb649d000)
    libpthread.so.0 => /lib/i386-linux-gnu/libpthread.so.0 (0xb646b000)
    libstdc++.so.6 => /usr/lib/i386-linux-gnu/libstdc++.so.6 (0xb6382000)
    libgcc_s.so.1 => /lib/i386-linux-gnu/libgcc_s.so.1 (0xb6364000)
    libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb61b6000)
    libgobject-2.0.so.0 => /usr/lib/i386-linux-gnu/libgobject-2.0.so.0 (0xb6164000)
    libglib-2.0.so.0 => /lib/i386-linux-gnu/libglib-2.0.so.0 (0xb6058000)
    libX11.so.6 => /usr/lib/i386-linux-gnu/libX11.so.6 (0xb5f24000)
    libm.so.6 => /lib/i386-linux-gnu/libm.so.6 (0xb5edd000)
    libGL.so.1 => /usr/lib/i386-linux-gnu/mesa/libGL.so.1 (0xb5e7d000)
    libicui18n.so.52 => /home/xx/Qt5.3.1/Tools/QtCreator/bin/./../lib/qtcreator/./libicui18n.so.52 (0xb5c53000)
    libicuuc.so.52 => /home/xxx/Qt5.3.1/Tools/QtCreator/bin/./../lib/qtcreator/./libicuuc.so.52 (0xb5ad7000)
    libdl.so.2 => /lib/i386-linux-gnu/libdl.so.2 (0xb5ad2000)
    libgthread-2.0.so.0 => /usr/lib/i386-linux-gnu/libgthread-2.0.so.0 (0xb5ace000)
    librt.so.1 => /lib/i386-linux-gnu/librt.so.1 (0xb5ac5000)
    /lib/ld-linux.so.2 (0xb7702000)
    libffi.so.6 => /usr/lib/i386-linux-gnu/libffi.so.6 (0xb5abe000)
    libpcre.so.3 => /lib/i386-linux-gnu/libpcre.so.3 (0xb5a80000)
    libxcb.so.1 => /usr/lib/i386-linux-gnu/libxcb.so.1 (0xb5a5e000)
    libglapi.so.0 => /usr/lib/i386-linux-gnu/libglapi.so.0 (0xb5a45000)
    libXext.so.6 => /usr/lib/i386-linux-gnu/libXext.so.6 (0xb5a32000)
    libXdamage.so.1 => /usr/lib/i386-linux-gnu/libXdamage.so.1 (0xb5a2e000)
    libXfixes.so.3 => /usr/lib/i386-linux-gnu/libXfixes.so.3 (0xb5a28000)
    libX11-xcb.so.1 => /usr/lib/i386-linux-gnu/libX11-xcb.so.1 (0xb5a25000)
    libxcb-glx.so.0 => /usr/lib/i386-linux-gnu/libxcb-glx.so.0 (0xb5a0c000)
    libxcb-dri2.so.0 => /usr/lib/i386-linux-gnu/libxcb-dri2.so.0 (0xb5a06000)
    libxcb-dri3.so.0 => /usr/lib/i386-linux-gnu/libxcb-dri3.so.0 (0xb5a02000)
    libxcb-present.so.0 => /usr/lib/i386-linux-gnu/libxcb-present.so.0 (0xb59fe000)
    libxcb-sync.so.1 => /usr/lib/i386-linux-gnu/libxcb-sync.so.1 (0xb59f7000)
    libxshmfence.so.1 => /usr/lib/i386-linux-gnu/libxshmfence.so.1 (0xb59f3000)
    libXxf86vm.so.1 => /usr/lib/i386-linux-gnu/libXxf86vm.so.1 (0xb59ed000)
    libdrm.so.2 => /usr/lib/i386-linux-gnu/libdrm.so.2 (0xb59df000)
    libicudata.so.52 => /home/xxx/Qt5.3.1/Tools/QtCreator/bin/./../lib/qtcreator/././libicudata.so.52 (0xb4373000)
    libXau.so.6 => /usr/lib/i386-linux-gnu/libXau.so.6 (0xb436e000)
    libXdmcp.so.6 => /usr/lib/i386-linux-gnu/libXdmcp.so.6 (0xb4367000)

在看一下本身直接加载的动态库:

xxx@ubuntu:~/Qt5.3.1/Tools/QtCreator/bin$ readelf --dynamic qtcreator

Dynamic section at offset 0x11eb0 contains 30 entries:
  标记        类型                         名称/值
 0x00000001 (NEEDED)                     共享库:[libExtensionSystem.so.1]
 0x00000001 (NEEDED)                     共享库:[libQt5Widgets.so.5]
 0x00000001 (NEEDED)                     共享库:[libQt5Network.so.5]
 0x00000001 (NEEDED)                     共享库:[libQt5Gui.so.5]
 0x00000001 (NEEDED)                     共享库:[libQt5Core.so.5]
 0x00000001 (NEEDED)                     共享库:[libpthread.so.0]
 0x00000001 (NEEDED)                     共享库:[libstdc++.so.6]
 0x00000001 (NEEDED)                     共享库:[libgcc_s.so.1]
 0x00000001 (NEEDED)                     共享库:[libc.so.6]
 0x0000000f (RPATH)                      Library rpath: [$ORIGIN/../lib/qtcreator]

秘密就在这句:0x0000000f (RPATH) Library rpath: [$ORIGIN/../lib/qtcreator]

rpath与ORIGIN
rpath是gcc的一个参数。rpath添加一个目录。当程序被加载时,搜寻此目录,寻找动态库。rpath添加的目录信息保存在可执行文件中。即使这句
0x0000000f (RPATH) Library rpath: [xxx]。
现在的问题是,可执行文件如何知道自身所在目录。
ORIGIN ORIGIN更详细的信息,可参考此文档。

例子
例子目录结构如下
src
…main.c
…Makefile
…lib/foo.c
操作系统:ubuntu 14 32位
编译i:gcc 4.8

main.c文件源码

void test_tk();

int main(void)
{
    test_tk();
    return 0;
}

Makefile文件内容

main:main.c lib/libfoo.so
    gcc -L${shell pwd}/lib -g -Wall -o test -Wl,-rpath,'$$ORIGIN/lib' main.c -lfoo
lib/libfoo.so:lib/foo.c
    gcc -g -Wall -fPIC -shared -o lib/libfoo.so lib/foo.c

foo.c文件源码

#include 

void test_tk()
{
    printf("called!\n");
}

编译,生成test可执行文件。
执行readelf –dynamic test 来看加载的动态库。

Dynamic section at offset 0xf04 contains 26 entries:
  标记        类型                         名称/值
 0x00000001 (NEEDED)                     共享库:[libfoo.so]
 0x00000001 (NEEDED)                     共享库:[libc.so.6]
 0x0000000f (RPATH)                      Library rpath: [$ORIGIN/lib]
 ..........................................................

运行test。执行结果位

called!

可以讲src拷贝到其他目录试试。发现程序也可以正常运行。

你可能感兴趣的:(linux,c)