momo zone

调核人的blog

关于现代linux动态库的搜索

最近才知道LD_LIBRARY_PATH这个传统的环境变量在引入ldconfig 后降级了,也就是说LD_LIBRARY_PATH指示的是额外的库路径,不能通过覆盖它而覆盖整个运行时的库路径。

现代的运行时so加载是这样的,运行时的/lib/ld-x.xx.so 会按照/etc/ld.so.conf中指示的路径去搜索库文件,然后才是LD_LIBRARY_PATH指示的路径。然后是/lib,最后是/usr/lib这些传统路径。

除了/etc/ld.so.conf 和传统的LD_LIBRARY_PATH能够新增额外的库路径还可以在编译时进行:

gcc test.c -g -o test.bin -Wl, -R /tmp/lib -Wl,–dynamic-linker=/lib/ld-2.11.2.so

gcc test.c -g -o test.bin -Wl, –rpath /tmp/lib -Wl,–dynamic-linker=/lib/ld-2.11.2.so

参数 -Wl,–dynamic-linker=/lib/ld-2.11.2.so 当然可以不用指定,编译好的elf文件可以用readelf -a查看:

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  PHDR           0x000034 0x08048034 0x08048034 0x00120 0x00120 R E 0x4
  INTERP         0x000154 0x08048154 0x08048154 0x00012 0x00012 R   0x1
      [Requesting program interpreter: /lib/ld-2.11.2.so]
  LOAD           0x000000 0x08048000 0x08048000 0x008bc 0x008bc R E 0x1000
  LOAD           0x000ef4 0x08049ef4 0x08049ef4 0x00124 0x00164 RW  0x1000
  DYNAMIC        0x000f08 0x08049f08 0x08049f08 0x000e8 0x000e8 RW  0x4
  NOTE           0x000168 0x08048168 0x08048168 0x0005c 0x0005c R   0x4
  GNU_EH_FRAME   0x000818 0x08048818 0x08048818 0x00024 0x00024 R   0x4
  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0x4
  GNU_RELRO      0x000ef4 0x08049ef4 0x08049ef4 0x0010c 0x0010c R   0x1

但这样指定之后运行时对动态库的搜索将变得复杂一些:

问题反应在elf文件里:

0xXXXXXXXX(RPATH) Library rpath: [<path>]
0xXXXXXXXX(RUNPATH) Library runpath: [<path>]

我在opensuse上测试如果指定-R或–rpath则这两个section都会添加。有其他的发行版会只设置RPATH,而只有在指定–enable-new-dtags才会加上另一个。但无论如何不会出现只有RUNPATH而没有RPATH的情况。

总结一下就是这样子:

ELF 中 RPATH ELF 中 RUNPATH LD_LIBRARY_PATH 变量 尝试加载目录的顺序
未设置 未设置 未设置 ldconfig=> /lib => /usr/lib
未设置 未设置 设置 ${LD_LIBRARY_PATH} => ldconfig=>/lib => /usr/lib
设置 未设置 未设置 ${RPATH} => ldconfig=>/lib => /usr/lib
设置 未设置 设置 ${RPATH} => ${LD_LIBRARY_PATH} =>ldconfig=> /lib => /usr/lib
设置 或 未设置 设置 设置 ${LD_LIBRARY_PATH} => ${RUN_PATH} =>  ldconfig=>/lib => /usr/lib
设置 或 未设置 设置 未设置 ${RUN_PATH} => ldconfig=> /lib => /usr/lib

注意,这里有个非常怪异的设计,如果设置了RUN_PATH则完全无视RPATH,而且也不先看自己RUN_PATH,而是去看LD_LIBRARY_PATH。

另外参考了一下Qt项目的文档,这里有更详细和完备的描述

Unless loading object has RUNPATH: #如果不含有RUNPATH
    RPATH of the loading object,
        then the RPATH of its loader (unless it has a RUNPATH), ...,  #按照调用链向上递归查找RPATH直到可执行程序
        until the end of the chain, which is either the executable     #遇到RUNPATH就退出循环
        or an object loaded by dlopen
    Unless executable has RUNPATH:
        RPATH of the executable
LD_LIBRARY_PATH
RUNPATH of the loading object
ld.so.cache
default dirs
Advertisements

发表评论

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 更改 )

Twitter picture

You are commenting using your Twitter account. Log Out / 更改 )

Facebook photo

You are commenting using your Facebook account. Log Out / 更改 )

Google+ photo

You are commenting using your Google+ account. Log Out / 更改 )

Connecting to %s

%d 博主赞过: