动态库详解

2021-12-06 13:48:26 浏览数 (1)

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

调用脚本查看打印信息

build

动态库.dylib.framework编译链接详解

test.m 链接 AFNetworking
  1. 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
代码语言:txt复制
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文件的合集, 而动态库(最终链接产物)是静态库链接后的产物 -> 动态库不能合并,跟可执行文件是同一级别的.

dongtaiBuild

解决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
    3. framework实际是苹果对动静态库多了一层包装, 本质是一个动态库或者静态库.
    4. lldb -file test -> r -> 运行起来报错(Library not loaded)
    5. 其实就是程序运行的时候,根据路径找不到动态库.
    6. otool -l test | grep 'DYLIB' -> 查看动态库路径
      1. otool -l test | grep 'DYLIB' -A 5 -> -A 查找时多显示5行
      2. 发现根据系统动态库的名字 -> 很像一个路径 -> 我们自定义的动态库的名字孤零零的 -> 动态库路径不对
  4. 解决Library not loaded错误
    1. 编译链接生成动态库的时候, 去保存动态库的路径 -> 动态库的Macho文件Load_Command去保存自己的路径
    2. 进入动态库目录 -> otool -l TestExample | grep 'ID' -A 5
      1. A 是向下 B是向下显示
    3. LC_ID_DYLIB -> name -> name的命名规则是包含路径信息的 -> 此处的错误就是因为这里引起的
    4. man install_name_tool -> 改变动态库的install names
    5. install_name_tool -id 路径 动态库 -> 修改成功 -> 查看一下是否修改成功(otool -l) -> 修改成功后, 需要重新链接动态库 -> 再查看是否链接成功
    6. 最好是在生产动态库的时候, 路径就修改好 -> 改动态库的脚本 -> 最后链接生成动态库的时候 -> 添加参数-install_name 相对路径
    7. 查看@rpath定义(下方有做说明) -> 修改路径 -> install_name_tool -id @rpath/Framework/TestExample.framework/TestExample
    8. @rpath -> 由可执行文件的MachO提供
      1. 去查看可执行文件中是否有@rpath -> otool -l test | grep 'RPATH' -A 5 -> 发现没有
      2. 注意此处大小写敏感
      3. 在可执行文件中添加@rpath -> install_name_tool -add_rpath <路径> <添加的可执行文件>
      4. otool -l test | grep 'dylib' -A 3 -i
        1. 如果想大小写不敏感 -> 拼上-i的参数
      5. 添加后可直接运行查看 -> lldb -file test -> r -> q
    9. 修改可执行文件的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'所在的目录, 每次加载时, 都可能被设置为不同的路径, 由上层决定
代码语言:txt复制
* @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
    3. 没有最里层的导出符号
  3. 重新导出符号
    1. 去到中间的动态库 -> -Xlinker -reexport_framework -Xlinke TestExampleLog
    2. 意思是重新导出TestExampleLog的符号表
    3. 可通过man ld -> /reexport 去查看命令参数, 上面是有关framework, -l相关的是 -reexport -lx <.a/.dylib>
    4. 查看中间层的导出符号 -> nm -m <动静态库>
    5. 注意: 中间层的 -> LC_REEXPORT_DYLIB -> 通过改参数来链接最外层的可执行文件和最里层的动态库
    6. 只需要引入最里层的头文件就可以了
      1. -I 最里层的头文件 -> -I./Frameworks/TestExample.framework/Frameworks/TestExampleLog.framework/Headers

tdb格式动态库

什么是tdb格式

tdb格式全称(text_based stub libraries),本质上就是一个YAML描述的文本文件,类似于配置文件.

它的作用是用于记录动态库的一些信息, 包括导出的符号, 动态库的架构信息, 动态库的依赖信息.

用于避免在真机开发过程中直接使用传统的dylib.

对于真机来说, 由于动态库都是在设备上, 在Xcode上使用基于tdb格式的伪framework可以大大减少Xcode的大小.

0 人点赞