不知道为什么, 经常有时候会遇到要改变一个已经编译好的executable或动态链接库里带的rpath(Linux里是DT_RPATH, Mac OS X是@rpath)… 或者Mac OS X上我改的方法是用install_name_tool改某个库的install name, 然后利用Linux上$ORIGIN对应的@loader_path/@executable_path来做。

比如吧, 上个学期做算法作业2, 不知道为啥没有找到可以下载的Qt的静态链接库还是咋地, 我的Mac OS X上只有动态链接库, 然后老师又要求交可执行文件 … 那些标准位置的标准库, libc++.1.dylib什么的就算了, 但是Qt的库目测要自己带了, 要自己打包。就跟py2exe那种功能一样= =整个把动态链接库全部一起打包发布。编译完之后, 交作业的时候我把有引用的Qt的2个库QtGui, QtCore都拷贝到跟可执行文件一个目录下. 然后用Mac的install_name_tool把可执行文件对某个dynamic lib的install_name改成了相对可执行文件路径@loader_path的这个库的路径。但是其实这种做法有一个问题就是, 拷贝过来的动态链接库是传染的… 动态链接库链接的动态链接库也需要拷贝过来, 除了标准库以外, 这里其实只有Qt的两个库有链接到另一个新的库, 我在这里是手做的…其实这种做法很不靠谱… 标准库也有version等各种问题, 不是每台Mac OS X上都是一个版本的标准库… 真的要这样打包所有动态链接库, 还是需要递归的去把各个库的install_name全部改掉…

install_name_tool -change ${lib} @loader_path/$(basename ${lib}) ${exe_file}

用OS X10.5之后提供的rpath功能也可以, 在这里也可以指定exec file的rpath为@loader_path, 然后还是要把install_name改成@rpath/开头的, 所以…总感觉Mac OS X的rpath没啥用, 是不是用处是: 可以提供一个搜索的list, 而不是单个?

Linux上的LD_LIBRARY_PATH和Mac上的DYLD_LIBRARY_PATH环境变量指定的路径list会在Mac可执行文件中每个linked dynamic lib的install_name(当然也在rpath之前, 因为Mac上的@rpath要显式写在install_name里)和Linux的rpath起作用之前先搜索, 这个可以作为改execfile的rpath的替代品…

Linux里面并没有install_name这个概念, 有DT_RPATH和DT_RUNPATH两种标志写在可执行文件的header中, 其中DT_RPATH比LD_LIBRARY_PATHld.so.conf里面的搜索路径的优先级要高, 如果设置RPATH用户patch自己改过的库就不灵活了, 所以现在都说DT_RPATH已经deprecated了。DT_RUNPATH比环境变量的优先级要低, 但是还是高于ld.so.conf, 我觉得之所以要这么做, 因为大多数情况一个系统上常用通用的库都会写在ld.so.conf里, 很可能我想发布的软件跟这些通用的库有version的冲突, 需要链接我自己带的不同version的该库, 这样一个小白用户很冤枉, 明明系统管理员早就装了库啊, 还是遇到了undefined symbol这种错误, 这怎么知道为啥错了。而另一方面, 想保证用户想用加速过的库之类的时候能够patch库的灵活性, 这些高级用户可以在确认patch可行的时候改一改环境变量嘛, 灵活性还是保证了的。

一个Linux小工具chrpath

今天在Linux上先装了cntk的库, 编译出一个_cntk_py.so, 为SWIG生成的python c extension编译得到的库, 其中链接到了一个我没有的libmpi.so.12, 因为Ubuntu14.04的package mirror里面libmpi没有这么高的版本。从源码装了libmpi-1.10之后(没有装到标准路径… 免得各种奇怪的version冲突问题, 不是包管理器下载的之后还不好删除(教训…)), 可以直接把/my/path/to/mpilib加入ld.so.conf里面就好了, 但是这个改变了dynamic loader全局的设置。所以也可以用chrpath这个Linux上改DT_RPATH/DT_RUNPATH的小工具, 这个_cntk_py.so原来的rpath里面有两个目录并不存在… 还有不知道谁发布的/home/philly/的一个什么鬼路径…

$ chrpath -r  '$ORIGIN/cntk/libs:/my/path/to/mpilib' _cntk_py.so
$ objdump -p _cntk_py.so | grep -i runpath
  RUNPATH              $ORIGIN/cntk/libs:/my/path/to/mpilib

工具也就列出/修改DT_RUNPATHDT_RPATH的功能, 所以用起来还是很简单的, 然后再LDD就不会有not found了。

一些参考:

  1. http://jorgen.tjer.no/post/2014/05/20/dt-rpath-ld-and-at-rpath-dyld/
  2. 这个不错: https://wincent.com/wiki/@executable_path,@load_path_and@rpath
  3. http://stackoverflow.com/questions/7967848/use-rpath-but-not-runpath
  4. https://mikeash.com/pyblog/friday-qa-2009-11-06-linking-and-install-names.html
  5. Mach-O format: https://developer.apple.com/library/content/documentation/DeveloperTools/Conceptual/MachOTopics/0-Introduction/introduction.html#//apple_ref/doc/uid/TP40001519

NOTE: 其实并没有看其中几篇… 只是以前在书签里贴上来了…