链接framework动态库 - ShenYj/ShenYj.github.io GitHub Wiki
通过 将dylib包装成framework动态库 这篇笔记,模仿真实环境手动包装了一个 framework
动态库
接下来使用并验证,这样的操作是否是可行的
-
test.m
#import <Foundation/Foundation.h> #import "TestExample.h" int main(){ TestExample *te = [TestExample new]; [te lg_test:@"gdas"]; NSLog(@"testApp----"); return 0; }
导入并使用了这个
framework
的方法
-
通过
clang
先将test.m
编译成.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./Frameworks/TestExample.framework/Headers \ -c test.m -o 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 \ -F./Frameworks \ -framework TestExample \ test.o -o test
-
-F
: 在指定目录寻找framework (framework search path) -
-frame
: 指定链接的framework名称 (other link flags -framework AFNetworking)
-
-
目录结构
. ├── Frameworks │ └── TextExample.framework │ ├── Headers │ │ └── TestExample.h │ ├── TestExample │ ├── TestExample.m │ ├── TestExample.o │ └── build.sh ├── build.sh └── test.m
通过以上准备成功生成 test
可执行文件
-
lldb
环境下运行❯ lldb -file test (lldb) target create "test" Current executable set to '/Users/shenyj/framework动态库/test' (x86_64). (lldb) r Process 59818 launched: '/Users/shenyj/framework动态库/test' (x86_64) dyld[59818]: Library not loaded: TestExample Referenced from: /Users/shenyj/framework动态库/test Reason: tried: 'TestExample' (no such file), '/usr/local/lib/TestExample' (no such file), '/usr/lib/TestExample' (no such file), '/Users/shenyj/framework动态库/TestExample' (no such file), '/usr/local/lib/TestExample' (no such file), '/usr/lib/TestExample' (no such file) Process 59818 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
这个动态库
-
动态库加载原理
在运行
Mach-O
执行文件时
在LC_LOAD_DYLIB
这个load command
中,记录着当前Mach-O
使用的动态库的路径
dyld
在加载Mach-o
可执行文件文件时,dyld
通过这个路径来找动态库, 找不到了就会报错- 这里我反复尝试,出现的报错都是
Reason: tried: 'TestExample' (no such file)
,没能复现出image not found
- 这里我反复尝试,出现的报错都是
-
借助
otool
来查看test
可执行文件的Load Command
otool -l /Users/shenyj/framework动态库/test
Load command 13 cmd LC_LOAD_DYLIB cmdsize 40 name TestExample (offset 24) time stamp 2 Thu Jan 1 08:00:02 1970 current version 0.0.0 compatibility version 0.0.0 Load command 14 cmd LC_LOAD_DYLIB cmdsize 96 name /System/Library/Frameworks/Foundation.framework/Versions/C/Foundation (offset 24) time stamp 2 Thu Jan 1 08:00:02 1970 current version 1856.105.0 compatibility version 300.0.0 Load command 15 cmd LC_LOAD_DYLIB cmdsize 56 name /usr/lib/libobjc.A.dylib (offset 24) time stamp 2 Thu Jan 1 08:00:02 1970 current version 228.0.0 compatibility version 1.0.0 Load command 16 cmd LC_LOAD_DYLIB cmdsize 56 name /usr/lib/libSystem.B.dylib (offset 24) time stamp 2 Thu Jan 1 08:00:02 1970 current version 1311.0.0 compatibility version 1.0.0 Load command 17 cmd LC_LOAD_DYLIB cmdsize 104 name /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (offset 24) time stamp 2 Thu Jan 1 08:00:02 1970 current version 1856.105.0 compatibility version 150.0.0
通过输出看到
5个
动态库,后4个
都是系统的,并且能清晰看到name
参数对应的就是动态库的路径,指向了系统根目录
而第1个
正是我们自己的TestExample
动态库,而name
路径不正确,导致出现了上面的报错 -
动态库的路径记录在动态库自身当中, 通过
ID
这个关键字来筛选我们包装好的TestExample
动态库的Load Command
可以得到如下信息❯ otool -l TestExample | grep 'ID' -A 5 -B 5 maxprot 0x00000001 initprot 0x00000001 nsects 0 flags 0x0 Load command 4 cmd LC_ID_DYLIB cmdsize 40 name TestExample (offset 24) time stamp 1 Thu Jan 1 08:00:01 1970 current version 0.0.0 compatibility version 0.0.0 -- extreloff 0 nextrel 0 locreloff 0 nlocrel 0 Load command 9 cmd LC_UUID cmdsize 24 uuid 751402DD-EBFE-36BD-9D78-C918909190BD Load command 10 cmd LC_BUILD_VERSION cmdsize 32
-
借助一个标准的
RxSwift
动态库来进行对比❯ otool -l RxSwift | grep 'ID' -A 5 -B 5 maxprot 0x00000001 initprot 0x00000001 nsects 0 flags 0x0 Load command 3 cmd LC_ID_DYLIB cmdsize 64 name @rpath/RxSwift.framework/RxSwift (offset 24) time stamp 1 Thu Jan 1 08:00:01 1970 current version 1.0.0 compatibility version 1.0.0 -- extreloff 0 nextrel 0 locreloff 0 nlocrel 0 Load command 7 cmd LC_UUID cmdsize 24 uuid D88635B7-BE9F-3B08-8FC4-F417EA7F54C5 Load command 8 cmd LC_VERSION_MIN_IPHONEOS cmdsize 16
对比一下 name
的信息
TestExample (自己的包装的动态库) | RxSwift (真实项目中生成的) | |
---|---|---|
name | TestExample (offset 24) | @rpath/RxSwift.framework/RxSwift (offset 24) |
所以,当我们在手动链接生成动态库的时候,这个路径缺失了
通过 install_name_tool
这个工具,可以更改动态共享库的 name
等信息, 参考 install_name_tool
修改了动态库的name
后,使用之前build
的脚本重新链接生成可执行文件 test
-
再来查看
test
可执行文件的Load Command
❯ otool -l test | grep 'DYLIB' -A 5 cmd LC_LOAD_DYLIB cmdsize 200 name /Users/shenyj/framework动态库/Frameworks/TestExample.framework/TestExample (offset 24) time stamp 2 Thu Jan 1 08:00:02 1970 current version 0.0.0 compatibility version 0.0.0 -- cmd LC_LOAD_DYLIB cmdsize 96 name /System/Library/Frameworks/Foundation.framework/Versions/C/Foundation (offset 24) time stamp 2 Thu Jan 1 08:00:02 1970 current version 1856.105.0 compatibility version 300.0.0 -- cmd LC_LOAD_DYLIB cmdsize 56 name /usr/lib/libobjc.A.dylib (offset 24) time stamp 2 Thu Jan 1 08:00:02 1970 current version 228.0.0 compatibility version 1.0.0 -- cmd LC_LOAD_DYLIB cmdsize 56 name /usr/lib/libSystem.B.dylib (offset 24) time stamp 2 Thu Jan 1 08:00:02 1970 current version 1311.0.0 compatibility version 1.0.0 -- cmd LC_LOAD_DYLIB cmdsize 104 name /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (offset 24) time stamp 2 Thu Jan 1 08:00:02 1970 current version 1856.105.0 compatibility version 150.0.0
目前,动态库自身中的name
成功写入了路径,并且重新链接后的test
可执行文件中也记录了动态库的路径,并且是一一对应的
-
lldb
下再次运行❯ lldb -file test (lldb) target create "test" Current executable set to '/Users/shenyj/framework动态库/test' (x86_64). (lldb) r Process 77167 launched: '/Users/shenyj/framework动态库/test' (x86_64) 2022-02-11 20:22:42.344816+0800 test[77167:3610752] TestExample---- 2022-02-11 20:22:42.345091+0800 test[77167:3610752] testApp---- Process 77167 exited with status = 0 (0x00000000) (lldb)
成功运行
整个过程当中,我们只是用了 install_name_tool
给动态库自身添加路径,原来链接生成可执行文件的脚本并没有修改,因此可以确定,最终Mach-O
可执行文件中LOAD_COMMAND
中的动态库路径就是动态库在链接的时候给到 Mach-O
的
虽然我们链接动态库并成功运行了可执行文件,但是动态库的 path
指定的是一个绝对路径,也就代表着如果项目路径发生变化,就会再次找不到
-
可以通过 @rpath - install_name_tool设置rpath 为其设置一个相对路径
-
除了通过
install_name_tool
这个工具,我们完全可以在链接的时候就为动态库指定路径 链接动态库时指定路径