macos动态库加载分析
最近折腾的NPAPI插件需要用到第三方动态库, 总是加载不成功, 经过各种google, 终于明白:
##dylib_id 动态库标识 在java里, 加载jar库或者class文件都是已路径进行的, 而dylib也一样
每个dylib有一个id属性,这个值形式上
是一个unix路径, 但其实它仅仅是一个id
这个id作为库的一个名称, 提供给连接者
使用, 例如我有一个libimobiledevice.dylib 库:
//查看
liangmatoMacBook-Pro:MacOS liangwei$ otool -L ./libimobiledevice.dylib
./libimobiledevice.dylib:
/usr/local/lib/libimobiledevice.3.dylib (compatibility version 4.0.0, current version 4.1.0)
/usr/local/lib/libplist.1.1.8.dylib (compatibility version 1.0.0, current version 1.1.8)
第一行的/usr/local/lib/libimobiledevice.3.dylib
就是这个库的id,
不管这个dylib文件命名是什么, 也不管它放置在哪里, 当你的程序编译ld它的时候, 都是以id作为标识进行连接的
(当然, 一般情况下, 库安装路径跟id是相同的)
比如, 你有个程序A, 用到了libimobiledevice.dylib库, 库文件放在/workspace/test/libs下, 当你编译完之后, 查看A
liangmatoMacBook-Pro:MacOS liangwei$ otool -L ./A
./A:
/usr/local/lib/libimobiledevice.3.dylib (compatibility version 4.0.0, current version 4.1.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 169.3.0)
因为A是个可执行程序, 所以, 第一行开始的内容就是使用到的库,
那么它使用的是以/usr/local/lib/libimobiledevice.3.dylib
为id的库
这里的连接值是库的id, 而不是它的路径/workspace/test/libs/libimobiledevice.3.dylib
##DYLD_LIBRARY_PATH 动态库搜索路径
运行程序A, A使用了/usr/local/lib/libimobiledevice.3.dylib
, 那么系统如何知道去哪里读取这个文件?
系统首先直接去/usr/local/lib/libimobiledevice.3.dylib
路径去读取, 如果有则加载,
如果没有, 系统以文件名libimobiledevice.3.dylib
去设定好的目录搜索的,默认的, 这个目录是/usr/lib
.
你也可以进行设置, 添加其他一些搜索路径. 让系统在/usr/lib
找不到之后再搜索这些目录
而这个设置, 正是DYLD_LIBRARY_PATH
环境变量:
export DYLD_LIBRARY_PATH=/另一个libs的存放路径/:/另一个libs的存放路径2/
系统搜索完默认的/usr/lib
之后会依次搜索DYLD_LIBRARY_PATH指定的目录
如果把文件libimobiledevice.3.dylib改名, 会提示搜索不到, A将无法运行
##如何修改A所需库路径为程序所在目录下?
有一个变量叫@loader_path
, 就是只可执行程序当前所在目录. 于是我们修改A对libimobiledevice.3.dylib的依赖
install_name_tool -change /usr/local/lib/libimobiledevice.3.dylib @loader_path/libimobiledevice.3.dylib A
再查看
liangmatoMacBook-Pro:MacOS liangwei$ otool -L ./A
./A:
@loader_path/libimobiledevice.3.dylib (compatibility version 4.0.0, current version 4.1.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 169.3.0)
那么此时如果A在目录/workspace/test/A, 运行时, 会直接去/workspace/test/ 加载libimobiledevice.3.dylib
发现A还是无法运行, 发现libimobiledevice.3.dylib 依赖libplist.1.1.8.dylib ,
同样, 把libimobiledevice.3.dylib 对libplist.1.1.8.dylib的也修改为当前目录
install_name_tool -change /usr/local/lib/libplist.1.1.8.dylib @loader_path/libplist.1.1.8.dylib libimobiledevice.3.dylib
然后把libplist.1.1.8.dylib也拷贝到A所在目录下即可
##如何让A编译出来直接连接的是@loader_path下的库?
修改他们的id为@loader_path/xxx
, 如:
install_name_tool -id @loader_path/libimobiledevice.3.dylib libimobiledevice.3.dylib
使用修改过的dylib文件进行重新编译, A直接就依赖@loader_path/libimobiledevice.3.dylib
而不再是/usr/local/lib/libimobiledevice.3.dylib
##其他
-
loader_path 支持相对路径, 如:
/workspace …/libs ……/libimobiledevice.3.dylib ……/libplist.1.1.8.dylib …/bin ……/A
那么A对libimobiledevice.3.dylib
的依赖可以改为@loader_path/../libs/libimobiledevice.3.dylib
##完整例子
工程目录:
/workspace
A.c
.../libs
....../libimobiledevice.3.dylib
....../libplist.1.1.8.dylib
.../bin
....../A
A 依赖 libimobiledevice.3.dylib, libusbmuxd.2.dylib, libplist.1.dylib
libimobiledevice.3.dylib 依赖 libusbmuxd.2.dylib, libplist.1.dylib
libusbmuxd.2.dylib 依赖 libplist.1.dylib
libplist.1.dylib 无依赖
1) 下载新的库
2) 分别修改 libimobiledevice.3.dylib, libusbmuxd.2.dylib, libplist.1.dylib 的id为 @loader_path/文件名
3) 分别修改 dylib对其他dylib的依赖为 @loader_path/文件名
4) Xcode 中Build Phases
->Link Binary With Libraries
, 添加libs目录下的三个库
5) Xcode 中Build Phases
->Add Build Phase
->Add Copy Files
, 修改Destination
为Executables
, 并添加libs目录下的三个库
6) 编译即可
进入bin目录查看得到:
liangmatoMacBook-Pro:libs liangwei$ otool -L /workspace/bin/*
/workspace/bin/A:
@loader_path/libplist.1.dylib (compatibility version 1.0.0, current version 1.1.8)
@loader_path/libusbmuxd.2.dylib (compatibility version 2.0.0, current version 1.0.8)
@loader_path/libimobiledevice.dylib (compatibility version 4.0.0, current version 4.1.0)
/System/Library/Frameworks/WebKit.framework/Versions/A/WebKit (compatibility version 1.0.0, current version 536.26.9)
/System/Library/Frameworks/Cocoa.framework/Versions/A/Cocoa (compatibility version 1.0.0, current version 19.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 169.3.0)
/workspace/bin/libimobiledevice.dylib:
@loader_path/libimobiledevice.dylib (compatibility version 4.0.0, current version 4.1.0)
@loader_path/libplist.1.dylib (compatibility version 1.0.0, current version 1.1.8)
@loader_path/libusbmuxd.2.dylib (compatibility version 2.0.0, current version 1.0.8)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 88.3.11)
/usr/lib/libssl.0.9.7.dylib (compatibility version 0.9.7, current version 0.9.7)
/usr/lib/libcrypto.0.9.7.dylib (compatibility version 0.9.7, current version 0.9.7)
/usr/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current version 1.0.0)
/workspace/bin/libplist.1.dylib:
@loader_path/libplist.1.dylib (compatibility version 1.0.0, current version 1.1.8)
/usr/lib/libxml2.2.dylib (compatibility version 9.0.0, current version 9.16.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 88.3.11)
/usr/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current version 1.0.0)
/workspace/bin/libusbmuxd.2.dylib:
@loader_path/libusbmuxd.2.dylib (compatibility version 2.0.0, current version 1.0.8)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 88.3.11)
@loader_path/libplist.1.dylib (compatibility version 1.0.0, current version 1.1.8)
/usr/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current version 1.0.0)