动态库
执行如下脚本,此处不再说明,前面静态库分析的文章里解释过。
echo "编译test.m--test.o"
clang -target x86_64-apple-macos11.3 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.3.sdk \
-I./dylib \
-c test.m -o test.o
pushd ./dylib
echo "编译TestExample.m--TestExample.o"
clang -x objective-c \
-target x86_64-apple-macos11.3 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.3.sdk \
-c TestExample.m -o TestExample.o
echo "编译TestExample.m--libTestExample.dylib"
# -dynamiclib 动态库 编译为
clang -dynamiclib \
-target x86_64-apple-macos11.3 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.3.sdk \
TestExample.o -o libTestExample.dylib
# 1 官方 Xcode->静态库 2 ar -rc libTestExample.a TestExample.o
libtool -static -arch_only x86_64 TestExample.o -o libTestExample.a
popd
echo "链接libTestExample.dylib---test"
clang -target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.3.sdk \
-L./dylib \
-lTestExample \
test.o -o test
- 运行脚本 如果运行脚报权限相关的错误的话,执行 如下命令
chmod +x ./build.sh 给与权限
- 运行可执行文件
lldb -file test
再运行
r
运行结果如下
分析
静态库是.o的合集直接将.o文件改成静态库可以运行,但是动态库是一个链接编译的最终产物。这也就意味着静态库可以通过链接变成动态库
修改脚本
# Xcode->静态库
libtool -static -arch_only x86_64 TestExample.o -o libTestExample.a
echo "编译TestExample.m --- libTestExample.dylib"
# -dynamiclib: 动态库
# dylib 最终链接产物 -》
ld -dylib -arch x86_64 \
-macosx_version_min 11.3 \
-syslibroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.3.sdk \
-lsystem -framework Foundation \
-all_load \
libTestExample.a -o libTestExample.dylib
- 再次运行可执行文件 同样报错 错误一样
总结
静态库是.o文件的合集动态库是.o文件链接过后的产物静态库可以通过链接生成动态库动态库也就是最终产物,这也是动态库无法合并原因
动态库解析
引入一个概念 tbd文件
引入头文件链接路径
或者直接将SYCSSColor.tbd文件直接拖到项目中
俩种方式代码都编译成功
tbd文件
- tbd全称是
text-based stub libraries,本质上就是一个YAML描述的文本文件。 - 他的作用是用于
记录动态库的一些信息,包括导出的符号、动态库的架构信息、动态库的依赖信息 - 用于
避免在真机开发过程中直接使用传统的dylib。 - 对于真机来说,由于
动态库都是在设备上,在Xcode上使用基于tbd格式的伪framework可以大大减少Xcode的大小。
tbd格式
exports导出的意思symbols是符号的意思,也就意味着11-14行都是导出符号objc-classes是objc类的集合
生成tbd
Xcode的Build Settings搜索text
原理:就是通过拼上一些参数,来扫描Headers里面的头文件,然后把这个符号写到文件里去
tbd总结
通过上面我们可以知道我们用脚本去链接库的是用到-L,-l链接的是符号,也就是我们只需要知道符号所在的位置,不需要知道源码位置
运行肯定会报错
在运行的时候,由dyld动态加载动态库,在加载的时候,它会去找这个符号真实地址的时候,找不到直接闪退报错,而静态库在编译的时候就已将符号放在一起,不会动态加载
动态库加载
- 这里的
Mach-o相当于我们的可执行文件Test,Test的Mach-o有个专门的LC_LOAD_DYLIB,它里面有我们需要用到的动态库路径,当它找不到这个路径的时候就会报错`
终端执行如下命令
// 查找使用的动态库
otool -l test | grep 'DYLIB'
// 查找到使用的动态库 继续显示 5行
otool -l test | grep 'DYLIB' -A 5
//-A是向下展示,向上展示时-B
动态库中name字段就是该动态库路径,但是我们导入的动态库TestExample路径只有一个名字,别的什么都没有,这样肯定找不到TestExample
动态库路径
动态库有一个专门的地方来保存自己的路径,也就是说动态库的路径是保存在自己的Mach-o中的,下面我们就来查看下这个路径
有个命令来更改路径名称:install_name_tool,改变动态库的install name
执行如下命令
再来执行一次build.sh脚本,重新生成test文件,在查看下引入的动态库
再运行可执行文件
动态库路径优化
绝对路径有一个问题,当我们做好SDK后给别人使用,那么这个路径就会改变,很不方便。
@rpath
@rpath:Runpath search Paths! dyld搜索路径。运行时@rpath指示dyld按顺序搜索路径列表,以找到动态库。@rpath保存一个或多个路径的变量! 也就是谁连接我谁给我提供@rpath
test给TestExample提供@rpath 方式有俩种如下
替换rpath,下面的是添加rpath到指定的Mach-o
lldb -file test 执行文件
路径继续优化 @executable_path
@executable_path @loader_path
@executable_path:表示可执行程序所在的目录,解析为可执行文件的绝对路径。@loader_path:表示被加载的Mach-O所在的目录,每次加载时,都可能被设置为不同的路径,由上层指定
@executable_path
@executable_path,不管在谁的电脑上它都指向执行程序所在的路径
@loader_path
举例
test中使用的framework中包含TestExample.framework,而TestExample.framework的framework中包含TestExampleLog.framework
修改TestExample.framework的脚本
手动看一下
运行
再次改脚本
cocoaPod
为了加深@executable_path印象,我们可以看看cocoaPods的xcconfig文件
当我们编译代码,看下可执行文件的包内容,我们发现动态库都会存放在framework文件中,也就是上面设置的文件里
总结
链接动态库,一定要指定好路径,否咋就会报错。
- 我们介绍了通过
install_name_tool来更改路径 - 为了优化路径,我们引入
@rpath -rpath lod new 是替换路径,-add_rpath是将路径添加到指定位置- 为了再优化,我们又引入了
@executable_path和@loader_path
上面讲了可以替换路径,那么就想到了破解,因为我们可以改变路径,将我们自己写的动态库作为目标软件的依赖,达到破解的目的。我们这么做事改变了Mach-o,之前讲过Mach-o只有签名后才会被苹果系统承认,所以再破解软件是都会调用一句命令:code sign --force --deep --sign - 这句命令也就是强制打一个签名,后面再详细研究。