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