@loader_path - ShenYj/ShenYj.github.io GitHub Wiki
loader_path
: 表示被加载的 Mach-O
所在的目录,每次加载时,都可能被设置为不同的路径,由上层指定
在此之后继续探索 @loader_path
实际场景为例: 我们的App链接了一个动态库,动态库中链接了另外一个动态库
最终生成的
test
可执行文件链接了TestExample
这个动态库,TestExample
中又链接了TestExampleLog
这个动态库
-
目录结构
. ├── Frameworks │ └── TestExample.framework │ ├── Frameworks │ │ └── TestExampleLog.framework │ │ ├── Headers │ │ │ └── TestExampleLog.h │ │ ├── TestExampleLog │ │ ├── TestExampleLog.m │ │ └── buildTestExampleLog.sh │ ├── Headers │ │ └── TestExample.h │ ├── TestExample.m │ └── buildTestExample.sh ├── build.sh └── test.m
-
代码
test、TestExample、TestExampleLog
-
test.m
#import <Foundation/Foundation.h> #import "TestExample.h" #import "TestExampleLog.h" int main(){ NSLog(@"testApp----"); TestExample *manager = [TestExample new]; [manager test: nil]; TestExampleLog *log = [TestExampleLog new]; NSLog(@"testApp----%@",log); return 0; }
-
TestExample.h
#import <Foundation/Foundation.h> @interface TestExample : NSObject - (void)test:(_Nullable id)e; @end
-
TestExample.m
#import "TestExample.h" #import "TestExampleLog.h" @implementation TestExample - (void)test:(_Nullable id)e { NSLog(@"TestExample----"); TestExampleLog *log = [TestExampleLog new]; [log test_example_log: self]; } @end
-
TestExampleLog.h
#import <Foundation/Foundation.h> @interface TestExampleLog : NSObject - (void)test_example_log:(_Nullable id)e; @end
-
TestExampleLog.m
#import "TestExampleLog.h" @implementation TestExampleLog - (void)test_example_log:(_Nullable id)e { NSLog(@"TestExampleLog---%@", e); } @end
-
脚本
这里采取的链接器
Xlinker -install_name
的时候直接设置路径,免去了生成后在借助install_name_tool
修改的环节
- 脚本中重点关注
-Xlinker -install_name
的不同设置, 尤其是buildTestExample.sh
-
buildTestExampleLog.sh
clang -target x86_64-apple-macos12.1 \ -fobjc-arc \ -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.1.sdk \ -I./Headers \ -c TestExampleLog.m -o TestExampleLog.o clang -dynamiclib \ -target x86_64-apple-macos12.1 \ -fobjc-arc \ -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.1.sdk \ -Xlinker -install_name -Xlinker @rpath/TestExampleLog.framework/TestExampleLog \ TestExampleLog.o -o TestExampleLog otool -l TestExampleLog | grep 'ID' -A 5
buildTestExampleLog.sh 编译结果
❯ ./buildTestExampleLog.sh cmd LC_ID_DYLIB cmdsize 72 name @rpath/TestExampleLog.framework/TestExampleLog (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 948B94AA-6EB1-33A3-9BCE-D4F13B5A107D Load command 10 cmd LC_BUILD_VERSION cmdsize 32
-
buildTestExample.sh
clang -target x86_64-apple-macos12.1 \ -fobjc-arc \ -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.1.sdk \ -I./Headers \ -I./Frameworks/TestExampleLog.framework/Headers \ -c TestExample.m -o TestExample.o clang -dynamiclib \ -target x86_64-apple-macos12.1 \ -fobjc-arc \ -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.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 echo "-------DYLIB---------" otool -l TestExample | grep 'DYLIB' -A 5 echo "-------ID---------" otool -l TestExample | grep 'ID' -A 5
-
-Xlinker -install_name -Xlinker @rpath/TestExample.framework/TestExample
- 给当前
TestExample
动态库配置rpath
- 给当前
-
-Xlinker -rpath -Xlinker @loader_path/Frameworks
- 这一行是为了保证在
buildTestExample
链接TestExampleLog
时能够找到正确的路径, 为TestExampleLog
补全前面的路径部分 - 若此时使用
@executable_path
又要拼接中间这个动态库的路径
可执行程序与TestExampleLog
之间隔着一个TestExample
动态库, 拼接起来不方便
当然使用@executable_path/Frameworks/TestExample.framework/Frameworks
也是没有问题的 - 通过
@loader_path
就能很好的解决这个问题, 谁来链接TestExampleLog
,路径就代表谁- 在这个示例中
TestExample
链接了TestExampleLog
, 那么@loader_path
就能代表TestExample
所在的路径, 即.../Frameworks/TestExample.framework
- 在这个示例中
- 这一行是为了保证在
-
-Xlinker -reexport_framework -Xlinker TestExampleLog
这里保证了可执行程序中能够直接调用TestExampleLog
的方法- 在
TestExample
动态库中将TestExampleLog
重新导出放入LC_REEXPORT_DYLIB
,保证能够在可执行文件中直接调用到TestExampleLog
api- 可执行文件 -->
LC_REEXPORT_DYLIB
-->TestExampleLog
的符号
- 可执行文件 -->
- 在
buildTestExample.sh 编译结果
❯ ./buildTestExample.sh -------DYLIB--------- 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 -- 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 1856.105.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 1311.0.0 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 1856.105.0 compatibility version 150.0.0 -------ID--------- 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_UUID cmdsize 24 uuid 5F06CDBF-5B3C-3B62-ADFA-1C8EE11B6010 Load command 10 cmd LC_BUILD_VERSION cmdsize 32
-
-
build.sh
clang -target x86_64-apple-macos12.1 \ -fobjc-arc \ -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.1.sdk \ -I./Frameworks/TestExample.framework/Headers \ -I./Frameworks/TestExample.framework/Frameworks/TestExampleLog.framework/Headers \ -c test.m -o test.o clang \ -target x86_64-apple-macos12.1 \ -fobjc-arc \ -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.1.sdk \ -Xlinker -rpath -Xlinker @executable_path/Frameworks \ -F./Frameworks \ -framework TestExample \ test.o -o test otool -l test | grep 'RPATH' -A 3 otool -l test | grep 'DYLIB' -A 3
build.sh 编译结果
❯ ./build.sh cmd LC_RPATH cmdsize 40 path @executable_path/Frameworks (offset 12) Load command 19 cmd LC_LOAD_DYLIB cmdsize 72 name @rpath/TestExample.framework/TestExample (offset 24) time stamp 2 Thu Jan 1 08:00:02 1970 -- 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 -- cmd LC_LOAD_DYLIB cmdsize 56 name /usr/lib/libobjc.A.dylib (offset 24) time stamp 2 Thu Jan 1 08:00:02 1970 -- cmd LC_LOAD_DYLIB cmdsize 56 name /usr/lib/libSystem.B.dylib (offset 24) time stamp 2 Thu Jan 1 08:00:02 1970 -- 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
-
最后通过
lldb
环境运行,在可执行文件中成功调用了两个动态库的方法❯ lldb -file test (lldb) target create "test" Current executable set to '/Users/shenyj/Documents/CodeForTest/lib/loader_path/test' (x86_64). (lldb) r Process 5186 launched: '/Users/shenyj/Documents/CodeForTest/lib/loader_path/test' (x86_64) 2022-02-12 01:02:47.507558+0800 test[5186:3840218] testApp---- 2022-02-12 01:02:47.507807+0800 test[5186:3840218] TestExample---- 2022-02-12 01:02:47.507899+0800 test[5186:3840218] TestExampleLog---<TestExample: 0x600000010000> 2022-02-12 01:02:47.507927+0800 test[5186:3840218] testApp----<TestExampleLog: 0x600000010010> Process 5186 exited with status = 0 (0x00000000)