dead strip 补充
- 跟这些参数没有关系_noall_load,-all_load,-Objc,-force_load<file>
- 这些参数控制你链接的库必须是静态库的时候. 死代码删除
- dead code stripping
- 链接的时候, 链接器提供的代码优化方式
证明步骤
- test.m里面没有用静态库的东西
- 编译,链接生成可执行文件 (链接器默认_noall_load)
- 用build.sh脚本
- objdump --macho -d test -> 查看汇编代码 -> 没有静态库方法
- 如果想链接进去可执行文件 -> -Xlinker -all_load -> 有静态库方法了
dead strip
- dead strip -> man ld -> /dead strip 查看方法说明
- 本地没有被入口点或导出符号用到的, 就会被移出
- build.sh -> -Xlinker -dead strip -> objdump --macho --syms test查看符号表
- 全局符号 global_function()没有使用,就会被干掉
- 使用后,导出符号表就会有
总结: -Xlinker -dead strip -Xlinker -all_load 同时写上去,
静态库的符号并不会被干掉,因为OC是动态运行的,如果静态库里面的符号被干掉,用的时候就会出问题.
查看一个符号为什么活着
-Xlinker -why_live -Xlinker _global_function
调用脚本查看打印信息
build
动态库.dylib.framework编译链接详解
test.m 链接 AFNetworking
- test.m -> test.o 1. clang -target x86_64-apple-macos11.1undefined-fobjc-arcundefined-isysroot $SYSROOTundefined-I./AFNetworkingundefined-c text.m -o test.o 2. 链接动态库 1. clang -target x86_64-apple-macos11.1undefined-fobjc-arcundefined-isysroot $SYSROOTundefined-L./AFNetworkingundefined-lAFNetworkingundefinedtext.o -o test
3. -file test
4. r -> 出错 -> Library not loaded/ image not found
5. q
动态库原理
- 按照静态库链链接的脚本去写, 同样会报上面的错误Library not loaded/ image not found
- 不添加-all_load 会报错: ""referenced from, 因为动态库的导出符号表里没有(默认_noall_load的问题)
- objdump --macho --exports-trie 动态库路径 -> 查看动态库的导出符号表
- 修正-all_load后,还是报错Library not loaded/ image not -> 动态库特性
- 静态库.o文件的合集, 而动态库(最终链接产物)是静态库链接后的产物 -> 动态库不能合并,跟可执行文件是同一级别的.
dongtaiBuild
解决Library not loaded
test.o 链接动态的时候, 到底用到了什么东西
- LoginApp 使用SYCSSColor动态库
- xcconfig -> 告诉程序HEADER_SEARCH_PATHS -> 也就是-I的参数
- FRAMEWORK_SEARCH_PATHS -> 去哪个路径下找frameworks -> 也就是-F
- OTHER_LDFLAGS -> 要连接库的名称 -> 也就是-framework
- 上面参数主要目的是 -> 告诉程序导出符号在哪里
- tdb格式的讲解(请看下方tdb格式说明) -> 动态库在链接的时候, 只需要知道你所需符号所在的一个位置就行,不需要知道源码. -> 错误之所以存在就是链接的时候没有问题, 在运行的时候找不到了
- 动态库与framework
- 根据脚本生成framework,TestExample
- test.o 链接framework,test-framework
- framework实际是苹果对动静态库多了一层包装, 本质是一个动态库或者静态库.
- lldb -file test -> r -> 运行起来报错(Library not loaded)
- 其实就是程序运行的时候,根据路径找不到动态库.
- otool -l test | grep 'DYLIB' -> 查看动态库路径
- otool -l test | grep 'DYLIB' -A 5 -> -A 查找时多显示5行
- 发现根据系统动态库的名字 -> 很像一个路径 -> 我们自定义的动态库的名字孤零零的 -> 动态库路径不对
- 解决Library not loaded错误
- 编译链接生成动态库的时候, 去保存动态库的路径 -> 动态库的Macho文件Load_Command去保存自己的路径
- 进入动态库目录 -> otool -l TestExample | grep 'ID' -A 5
- A 是向下 B是向下显示
- LC_ID_DYLIB -> name -> name的命名规则是包含路径信息的 -> 此处的错误就是因为这里引起的
- man install_name_tool -> 改变动态库的install names
- install_name_tool -id 路径 动态库 -> 修改成功 -> 查看一下是否修改成功(otool -l) -> 修改成功后, 需要重新链接动态库 -> 再查看是否链接成功
- 最好是在生产动态库的时候, 路径就修改好 -> 改动态库的脚本 -> 最后链接生成动态库的时候 -> 添加参数-install_name 相对路径
- 查看@rpath定义(下方有做说明) -> 修改路径 -> install_name_tool -id @rpath/Framework/TestExample.framework/TestExample
- @rpath -> 由可执行文件的MachO提供
- 去查看可执行文件中是否有@rpath -> otool -l test | grep 'RPATH' -A 5 -> 发现没有
- 注意此处大小写敏感
- 在可执行文件中添加@rpath -> install_name_tool -add_rpath <路径> <添加的可执行文件>
- otool -l test | grep 'dylib' -A 3 -i
- 如果想大小写不敏感 -> 拼上-i的参数
- 添加后可直接运行查看 -> lldb -file test -> r -> q
- 修改可执行文件的rpath路径 -> install_name_tool -rpath <旧路径> <新路径> <可执行文件的名字>
- 当然也可以重新添加一个@rpath -> 注意:可执行文件的rpath可以有多个
- 可以查看cocopods里面的xcconfig文件 -> LD_RUNPATH_SEARCH_PATH 键值对来加深印象
install_name 与 @rpath
- @rpath -> Runpath search Paths -> dylb搜索路径 -> 谁链接动态库, 就由谁来提供@rpath
- '@executable_path': 表示可执行程序所在的目录, 解析为可执行文件的绝对路径.
- '@loader_path': 表示被加载的'Mach-O'所在的目录, 每次加载时, 都可能被设置为不同的路径, 由上层决定
* @loader_path -> 一句话就是谁链接我的动态库的那个可执行文件的路径
loader_path说明,动动链接
- 可执行文件 -> 链接了一个动态库, 但是同时我这个动态库里面 -> 链接的有其他的动态库
- 注意: 此时编译应该从后往前编译 -> 即先编译最里面的动态库 (01:45:00)
- 最后的动态库-> -Xlinker -install_name -Xlinker @rpath/TestExampleLog.framework/TestExampleLog
- -Xlinker -> 正常开发中系统提供的链接器
- 前面的可执行文件 -> -Xlinker -rpath -Xlinker @executable_path/Frameworks
- 中间的动态库 -> -Xlinker -install_name -Xlinker @rpath/TestExample.framework/TestExample
- 中间层的动态库 -> 提供loader_path -> -Xlinker -rpath -Xlinker @executable_path/Frameworks/TestExample.framework/Frameworks -> 是可以运行成功的
- 建议-Xlinker -rpath -Xlinker @loader_path/Frameworks
- 对应到Xcode -> build setting -> install_name/rpath (搜索查看)
可执行文件使用动态库中的动态库探究
- 可执行文件为什么能够使用动态库 -> 因为动态库的暴露了自己的导出符号给可执行文件
- 但是最里层的动态库对于最外层的可执行文件,其导出符号是否暴露呢
- 查看最里层的导出符号表 -> objdump --macho --exports-trie TestExampleLog
- 查看中间层的导出符号表 -> objdump --macho --exports-trie TestExample
- 没有最里层的导出符号
- 重新导出符号
- 去到中间的动态库 -> -Xlinker -reexport_framework -Xlinke TestExampleLog
- 意思是重新导出TestExampleLog的符号表
- 可通过man ld -> /reexport 去查看命令参数, 上面是有关framework, -l相关的是 -reexport -lx <.a/.dylib>
- 查看中间层的导出符号 -> nm -m <动静态库>
- 注意: 中间层的 -> LC_REEXPORT_DYLIB -> 通过改参数来链接最外层的可执行文件和最里层的动态库
- 只需要引入最里层的头文件就可以了
- -I 最里层的头文件 -> -I./Frameworks/TestExample.framework/Frameworks/TestExampleLog.framework/Headers
tdb格式动态库
什么是tdb格式
tdb格式全称(text_based stub libraries),本质上就是一个YAML描述的文本文件,类似于配置文件.
它的作用是用于记录动态库的一些信息, 包括导出的符号, 动态库的架构信息, 动态库的依赖信息.
用于避免在真机开发过程中直接使用传统的dylib.
对于真机来说, 由于动态库都是在设备上, 在Xcode上使用基于tdb格式的伪framework可以大大减少Xcode的大小.
注意点:
- 苹果不允许dylib的动态库, 所以我们开发中自己生成的动态库, 基本上都是.framework格式的.原因是单dylib少了签名文件,请参考生成的IPA包中,所有引入的动态库,最后都要进行签名生成签名文件.
- 动态库比静态库分发体积要小, 给别人提供SDK的时候,要考虑SDK的体积,虽然别人使用的时候会让别人的IPA包变大.
- 可以更好的控制动态库里面符号的可见性