动态库原理 - ShenYj/ShenYj.github.io GitHub Wiki
顺着探索静态库原理的思路,来探索动态库
-
test.m
文件#import <Foundation/Foundation.h> #import "TestExample.h" int main(){ NSLog(@"testApp----"); TestExample *manager = [TestExample new]; [manager lg_test: nil]; return 0; }
-
TestExample.h
#import <Foundation/Foundation.h> @interface TestExample : NSObject - (void)lg_test:(_Nullable id)e; @end
-
TestExample.m
#import "TestExample.h" @implementation TestExample - (void)lg_test:(_Nullable id)e { NSLog(@"TestExample----"); } @end
-
脚本
目录结构:
├── build.sh ├── dylib │ ├── TestExample.h │ └── TestExample.m └── test.m
先尝试按照探索静态库的方式处理
- 编译生成
.dylib
- 编译生成
.o
- 用
.o
链接.dylib
,生成可执行文件 -
lldb
环境运行验证
-
脚本
echo "编译test.m --- test.o" clang -target x86_64-apple-macos12.1 \ -fobjc-arc \ -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.1.sdk \ -I./dylib \ -c test.m -o test.o pushd ./dylib echo "编译TestExample.m --- TestExample.o" clang -target x86_64-apple-macos12.1 \ -fobjc-arc \ -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.1.sdk \ -c TestExample.m -o TestExample.o echo "编译TestExample.o --- libTestExample.dylib" clang -dynamiclib \ # 链接成动态库 -target x86_64-apple-macos12.1 \ -fobjc-arc \ -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.1.sdk \ TestExample.o -o libTestExample.dylib popd echo "链接libTestExample.dylib -- test EXEC" clang -target x86_64-apple-macos12.1 \ -fobjc-arc \ -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.1.sdk \ -L./dylib \ -lTestExample \ test.o -o test
- 在探究静态库时,使用手动修改后缀的方式,libtool, ar 生成静态库, 这里通过
clang
提供的参数-dynamiclib
来将.o
链接成一个动态库
- 在探究静态库时,使用手动修改后缀的方式,libtool, ar 生成静态库, 这里通过
-
脚本处理过程中并未出现任何错误,并且生成了我们预期的
.o
、.dylib
、.out
等文件
但是在lldb
环境下运行环节却出现了错误❯ lldb -file test (lldb) target create "test" Current executable set to '/Users/shenyj/动态库原理/test' (x86_64). (lldb) run Process 33122 launched: '/Users/shenyj/动态库原理/test' (x86_64) dyld[33122]: Library not loaded: libTestExample.dylib Referenced from: /Users/shenyj//动态库原理/test Reason: tried: 'libTestExample.dylib' (no such file), '/usr/local/lib/libTestExample.dylib' (no such file), '/usr/lib/libTestExample.dylib' (no such file), '/Users/shenyj/动态库原理/libTestExample.dylib' (no such file), '/usr/local/lib/libTestExample.dylib' (no such file), '/usr/lib/libTestExample.dylib' (no such file) Process 33122 stopped * thread #1, stop reason = signal SIGABRT frame #0: 0x00000001000560ce dyld`__abort_with_payload + 10 dyld`__abort_with_payload: -> 0x1000560ce <+10>: jae 0x1000560d8 ; <+20> 0x1000560d0 <+12>: movq %rax, %rdi 0x1000560d3 <+15>: jmp 0x100013120 ; cerror_nocancel 0x1000560d8 <+20>: retq Target 0: (test) stopped. (lldb)
换个思路:
- 先将
TestExample
通过libtool
生成静态库 - 再通过
ld
链接器将其链接成动态库 - 再用
.o
去链接这静态库变成的动态库,生成可执行文件 -
lldb
环境执行验证
-
修改脚本
echo "编译test.m --- test.o" clang -target x86_64-apple-macos12.1 \ -fobjc-arc \ -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.1.sdk \ -I./dylib \ -c test.m -o test.o pushd ./dylib echo "编译TestExample.m --- TestExample.o" clang -target x86_64-apple-macos12.1 \ -fobjc-arc \ -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.1.sdk \ -c TestExample.m -o TestExample.o echo "编译TestExample.o --- libTestExample.a" # 新增 >>>>>>>>>>> # Xcode (默认使用的就是 libtool 来编译静态库) -> 静态库 libtool -static -arch_only x86_64 TestExample.o -o libTestExample.a echo "编译TestExample.m --- libTestExample.dylib" ld -dylib -arch x86_64 \ -macosx_version_min 12.1 \ -syslibroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.1.sdk \ -lsystem -framework Foundation \ # -lsystem: 需要系统库支撑: dylib dyld,-framework Foundation: 链接Foundation 这个动态库 libTestExample.a -o libTestExample.dylib # 新增 <<<<<<<<<<< popd echo "链接libTestExample.dylib -- test EXEC" clang -target x86_64-apple-macos12.1 \ -fobjc-arc \ -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.1.sdk \ -L./dylib \ -lTestExample \ test.o -o test
-
ar
更多的是查看相关的功能,默认Xcode
是用libtool
来创建静态库的,所以脚本中使用了libtool
先创建一个静态库 -
ld
, 对比开头的脚本,这里没有使用clang
创建动态库,而是直接用了链接器
-
-
执行脚本后报错:
编译TestExample.m --- TestExample.o 编译TestExample.o --- libTestExample.a 编译TestExample.m --- libTestExample.dylib ~/xxxxxx链接libTestExample.dylib -- test EXEC Undefined symbols for architecture x86_64: "_OBJC_CLASS_$_TestExample", referenced from: objc-class-ref in test.o ld: symbol(s) not found for architecture x86_64
在链接动态库生成可执行文件的时候失败,
_OBJC_CLASS_$_TestExample
这个符号找不到推理出,动态库的导出符号表中不包含此符号
借助
objdump --macho -exports-trie
来验证一下❯ objdump --macho -exports-trie /Users/s/动态库原理/dylib/libTestExample.dylib /Users/s//动态库原理/dylib/libTestExample.dylib: Exports trie:
发现导出符号表的确是空的,这是因为
Xcode
默认链接静态库时使用的是noall_load
,将其剥离了,所以在上述脚本中添加配置,将其改为-all_load
,将符号保留
-
添加
-all_load
后的脚本echo "编译test.m --- test.o" clang -target x86_64-apple-macos12.1 \ -fobjc-arc \ -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.1.sdk \ -I./dylib \ -c test.m -o test.o pushd ./dylib echo "编译TestExample.m --- TestExample.o" clang -target x86_64-apple-macos12.1 \ -fobjc-arc \ -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.1.sdk \ -c TestExample.m -o TestExample.o echo "编译TestExample.o --- libTestExample.a" # Xcode (默认使用的就是 libtool 来编译静态库) -> 静态库 libtool -static -arch_only x86_64 TestExample.o -o libTestExample.a echo "编译TestExample.m --- libTestExample.dylib" ld -dylib -arch x86_64 \ -macosx_version_min 12.1 \ -syslibroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.1.sdk \ -lsystem -framework Foundation \ # 新增 >>>>>>>>>>> -all_load \ # 新增 <<<<<<<<<<< libTestExample.a -o libTestExample.dylib popd echo "链接libTestExample.dylib -- test EXEC" clang -target x86_64-apple-macos12.1 \ -fobjc-arc \ -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.1.sdk \ -L./dylib \ -lTestExample \ test.o -o test
-
lldb
环境下运行可执行文件Process 37789 launched: '/Users/shenyj/动态库原理/test' (x86_64) dyld[37789]: Library not loaded: libTestExample.dylib Referenced from: /Users/shenyj/动态库原理/test Reason: tried: 'libTestExample.dylib' (no such file), '/usr/local/lib/libTestExample.dylib' (no such file), '/usr/lib/libTestExample.dylib' (no such file), '/Users/shenyj/动态库原理/libTestExample.dylib' (no such file), '/usr/local/lib/libTestExample.dylib' (no such file), '/usr/lib/libTestExample.dylib' (no such file) Process 37789 stopped * thread #1, stop reason = signal SIGABRT frame #0: 0x00000001000560ce dyld`__abort_with_payload + 10 dyld`__abort_with_payload: -> 0x1000560ce <+10>: jae 0x1000560d8 ; <+20> 0x1000560d0 <+12>: movq %rax, %rdi 0x1000560d3 <+15>: jmp 0x100013120 ; cerror_nocancel 0x1000560d8 <+20>: retq Target 0: (test) stopped. (lldb)
运行结果仍然是失败的
Library not loaded
虽然仍然没能成功在 lldb
环境下运行起来,但目前已经可以得出结论
当使用 -L
和-l
链接一个库的时候,只需要知道符号所在的位置,并不需要源码,本质上是标记了导出符号所在的位置
- 动态库: 在运行时,由
dyld
动态加载, 在查找符号真实地址的时候找不到了 - 静态库: 链接的时候代码、符号表已经被合并在一起
通过探索静态库得知,其是目标文件的集合,而动态库和可执行文件一样,是链接后的产物,所以才能将一个静态库链接成动态库
动态库是链接最终产物,无法与.o
进行合并的
以上操作最终之所以没能成功在 lldb
环境下运行起来,是因为动态库中缺少路径,而链接生成可执行文件后,同样缺少了路径,导致 dyld
加载时找不到动态库
具体过程的探索与分析: framework动态库 - 链接 - Library not loaded -- 失败分析
并借助 install_name_tool
这个工具为动态库添加真实路径, 重新链接动态库生成可执行文件后,便可成功在 lldb
下运行
链接framework动态库 这种先生成 framework
,再通过 install_name_tool
设置路径的的方式多了那么一个环节,并且设置的是一个绝对路径, 在设置路径时, 可以尝试以下方案
另外我们完全可以在链接的时候就为动态库指定路径 链接动态库时指定路径
因为动态库不像静态库一样被合并到 Mach-O
中
那么动态库就会自己记录其所存在的路径,并在链接的时候,将自己的路径告诉 Mach-O
,记录在 Mach-O
的 LOAD_COMMAND
中,这样 dylb
在加载的时候才能找到动态库