前言:
我们还使用静态库中的案例
//test.m内容如下
#import
#import "TestExample.h"
int main(){
NSLog(@"testApp----");
// TestExample *manager = [TestExample new];
// [manager lg_test: nil];
return 0;
}
执行build.sh脚本
$ ./build.sh
$ objdump --macho -d test
TestExample并没有被链接到test中,因为dead code strip默认选择-noall_load,此时需配置 -Xlinker -all_load
接下来对test.m内容进行修改如下
#import
#import "TestExample.h"
// 全局符号
void global_function() {
}
int main(){
// global_function();
NSLog(@"testApp----");
// TestExample *manager = [TestExample new];
// [manager lg_test: nil];
return 0;
}
定义的全局符号global_function并没有使用,执行完脚本,查看test的符号表发现全局符号global_function依然会导出,此时需配置 #-Xlinker -dead_strip \ 把未使用的全局符号剥离,然而TestExample虽然未被使用,但是却没有被剥离掉,是因为OC是一门动态语言。
接下来我们再次对test.m内容进行修改如下
#import
#import "TestExample.h"
// 全局符号
void global_function() {
}
int main(){
global_function();
NSLog(@"testApp----");
// TestExample *manager = [TestExample new];
// [manager lg_test: nil];
return 0;
}
// 执行脚本并且查看符号表
$ ./build.sh
$ objdump --macho --syms test
这个时候全局符号global_function会被导出,我们可以配置-Xlinker -why_live -Xlinker _global_function \查看global_function为什么会被导出?
//最终脚本内容如下
SYSROOT=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.1.sdk
FILE_NAME=test
HEADER_SEARCH_PATH=./StaticLibrary
echo "-----开始编译test.m"
clang -x objective-c \
-target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot $SYSROOT \
-I${HEADER_SEARCH_PATH} \
-c ${FILE_NAME}.m -o ${FILE_NAME}.o
echo "-----开始进入StaticLibrary"
pushd ./StaticLibrary
clang -x objective-c \
-target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot $SYSROOT \
-c TestExample.m -o TestExample.o
ar -rc libTestExample.a TestExample.o
echo "-----开始退出StaticLibrary"
popd
echo "-----开始test.o to test EXEC"
clang -target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot $SYSROOT \
-Xlinker -dead_strip \
-Xlinker -all_load \
-Xlinker -why_live -Xlinker _global_function \
-L./StaticLibrary \
-lTestExample \
${FILE_NAME}.o -o ${FILE_NAME}
// 执行脚本
$ ./build.sh
-----开始test.o to test EXEC
_global_function from test.o
_main from test.o
_main from test.o
可以看出来全局符号被test.o中的_main使用
一. 动态库原理
1.1准备文件如下,内容与静态库一样
clang -target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.1.sdk \
-I./dylib \
-c test.m -o test.o
pushd ./dylib
echo "编译TestExample.m --- TestExample.o"
clang -target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.1.sdk \
-c TestExample.m -o TestExample.o
clang -dynamiclib \
-target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.1.sdk \
TestExample.o -o libTestExample.dylib
popd
echo "编译TestExample.m --- libTestExample.dylib"
clang -target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.1.sdk \
-L./dylib \
-lTestExample \
test.o -o test
运行后报错dyld: Library not loaded: libTestExample.dylib
//把o文件变成a文件,x86_64是指定的架构。也可以使用ar -rc a.a a.o
libtool -static -arch_only x86_64 TestExample.o -o libTestExample.a
//把.a文件变成.dylib文件
ld -dylib -arch x86_64 \
-macosx_version_min 11.0 \
-syslibroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
-lsystem -framework Foundation \
-all_load \ //如果没有这个参数,链接器会默认not all load
libTestExample.a -o libTestExample.dylib
运行之后还是报错dyld: Library not loaded: libTestExample.dylib
查看test链接的动态库
$ otool -l test | grep 'DYLIB'
cmd LC_LOAD_DYLIB
cmd LC_LOAD_DYLIB
cmd LC_LOAD_DYLIB
cmd LC_LOAD_DYLIB
cmd LC_LOAD_DYLIB
接下来查看链接的动态库信息,让向下多显示5行动态库信息 注意!!! -A是向下 -B是向上
$ otool -l test | grep 'DYLIB' -A 5
cmd LC_LOAD_DYLIB
cmdsize 48
name libTestExample.dylib (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 1770.255.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 1292.60.1
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 1770.255.0
compatibility version 150.0.0
可以看到我们自己创建的库 name libTestExample.dylib (offset 24),而其他的动态库的name却是库的路径。
我们的动态库能够自己保存自己的路径
$ otool -l ./dylib/libTestExample.dylib | grep 'ID' -A 5
cmd LC_ID_DYLIB
cmdsize 48
name libTestExample.dylib (offset 24)
time stamp 1 Thu Jan 1 08:00:01 1970
current version 0.0.0
compatibility version 0.0.0
--
cmd LC_UUID
cmdsize 24
uuid 296915D5-4E95-3C0B-BC55-740E357860F4
Load command 9
cmd LC_BUILD_VERSION
cmdsize 32
我们看到name 还是 libTestExample.dylib
$ install_name_tool -id /Users/wangning/Documents/资料/1:22/动态库/上课代码/动态库原理/dylib/libTestExample.dylib libTestExample.dylib
重新查看动态库的LC_ID_DYLIB
$ otool -l libTestExample.dylib | grep 'ID' -A 5
cmd LC_ID_DYLIB
cmdsize 128
name /Users/wangning/Documents/资料/1:22/动态库/上课代码/动态库原理/dylib/libTestExample.dylib (offset 24)
time stamp 1613027333 Thu Feb 11 15:08:53 2021
current version 0.0.0
compatibility version 0.0.0
--
cmd LC_UUID
cmdsize 24
uuid 296915D5-4E95-3C0B-BC55-740E357860F4
Load command 9
cmd LC_BUILD_VERSION
cmdsize 32
注意⚠️ 此时重新执行build.sh脚本时,要把pushd~popd之间合并libTestExample.dylib操作屏蔽掉,只保留合并test的操作,不然上面install_name_tool的路径会被覆盖掉
然后重新编译和链接我们的test文件运行成功
1.3更改上面的绝对路径为相对路径
上面指定的路径是绝对路径,如果动态库换了位置,还是会出现Reason: image not found
@rpath解决绝对路径的困扰
@rpath Runpath search Paths dyld搜索路径
运行时@rpath指示dyld按顺序搜索路径列表,以找到动态库。
@rpath保存一个或多个路径的变量
所以我们修改脚本
// TestExample.o -o libTestExample.dylib这一步操作过程中添加
-install_name @rpath/dylib/libTestExample.dylib
修改脚本之后执行,然后给test文件添加rpath路径
$ install_name_tool -add_rpath /Users/wangning/Documents/资料/1:22/动态库/上课代码/动态库与framework test
上面配置完成之后,链接我们的test文件运行成功
小提示
如果上面配置rpath之后,链接运行报错,可以执行下面命令查看路径是否配置正确
$ otool -l test | grep 'RPATH' -A 5
// 还可以查看动态库路径 -i参数,防止大小写敏感
$ otool -l test | grep 'dylib' -A 3 -i
1.4 executable_path和loader_path
@executable_path:表示可执行程序所在的目录,解析为可执行文件的绝对路径。
@loader_path:表示被加载的Mach-O 所在的目录,每次加载时,都可能 被设置为不同的路径,由上层指定。
// 由于上面添加过rpath,接下来修改rpath路径
$ install_name_tool -rpath /Users/wangning/Documents/资料/1:22/动态库/上课代码/动态库原理 @executable_path test
再次查看
二. 动态库链接动态库
test.m文件使用Frameworks中的TestExample.framework
在TestExample.framework库中使用TestExampleLog.framework
2.1 先编译并链接TestExampleLog.framework
// install_name是提供给外部需要链接TestExampleLog的路径。
clang -target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.1.sdk \
-I./Headers \
-c TestExampleLog.m -o TestExampleLog.o
clang -dynamiclib \
-target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.1.sdk \
-Xlinker -install_name -Xlinker @rpath/TestExampleLog.framework/TestExampleLog \
TestExampleLog.o -o TestExampleLog
// install_name是提供给调用者加载的路径
// rpath是提供给调用TestExampleLog的初始路径
clang -target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.1.sdk \
-I./Headers \
-I./Frameworks/TestExampleLog.framework/Headers \
-c TestExample.m -o TestExample.o
clang -dynamiclib \
-target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.1.sdk \
-Xlinker -install_name -Xlinker @rpath/TestExample.framework/TestExample \
-Xlinker -rpath -Xlinker @loader_path/Frameworks \
-F./Frameworks \
-framework TestExampleLog \
TestExample.o -o TestExample
2.3编译并链接test
clang -target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.1.sdk \
-I./Frameworks/TestExample.framework/Headers \
-c test.m -o test.o
clang -target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.1.sdk \
-Xlinker -rpath -Xlinker @executable_path/Frameworks \
-F./Frameworks \
-framework TestExample \
test.o -o test
链接运行test文件成功
如果我们想使用TestExampleLog,我们需要能再TestExample中导出符号中能看到TestExampleLog
$ objdump --macho --exports-trie TestExample
TestExample:
Exports trie:
0x000080C0 _OBJC_METACLASS_$_TestExample
0x000080E8 _OBJC_CLASS_$_TestExample
我们没有看出有导出符号,所以我们无法在test使用TestExampleLog,需要使用reexport_framework
TestExample内的脚本
clang -dynamiclib \
-target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.1.sdk \
-Xlinker -install_name -Xlinker @rpath/TestExample.framework/TestExample \
-Xlinker -rpath -Xlinker @loader_path/Frameworks \
-Xlinker -reexport_framework -Xlinker TestExampleLog \
-F./Frameworks \
-framework TestExampleLog \
TestExample.o -o TestExample
查看DYLIIB信息
$ otool -l TestExample | grep 'DYLIB' -A 5
cmd LC_ID_DYLIB
cmdsize 72
name @rpath/TestExample.framework/TestExample (offset 24)
time stamp 1 Thu Jan 1 08:00:01 1970
current version 0.0.0
compatibility version 0.0.0
--
cmd LC_REEXPORT_DYLIB
cmdsize 72
name @rpath/TestExampleLog.framework/TestExampleLog (offset 24)
time stamp 2 Thu Jan 1 08:00:02 1970
current version 0.0.0
compatibility version 0.0.0
--
TestExample重新加一个cmd,LC_REEXPORT_DYLIB
我们在test中导入头文件
#import
#import "TestExample.h"
#import "TestExampleLog.h"
int main(){
NSLog(@"testApp----");
TestExample *manager = [TestExample new];
[manager lg_test: nil];
TestExampleLog *log = [TestExampleLog new];
NSLog(@"testApp----%@",log);
return 0;
}
成功引用TestExampleLog类