探究动态库

动态库

动态库定义

静态库相反,动态库在编译时不会拷贝到目标程序中,目标程序中只会存储指向动态库的引用。等到程序运行时,动态库才会真正的被加载进来。常见的格式有:.framework.dylib.tbd

缺点: 会导致一些性能损失,但是可以优化,比如延迟绑定(Lazy Binding)技术。

编译链接动态库

之前已经尝试过制作链接静态库,那么今天将尝试链接动态库。

同样,现在将TestExample编译链接称为动态库,test.m变为目标文件后去链接这个动态库。 image.png image.png image.png

步骤:

  1. 编译test.m ----> test.o

clang -target arm64-apple-macos12.0 \-fobjc-arc \-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk \-F./Frameworks \-I./dylib \-c test.m -o test.o

  1. 编译TestExample.m ---> TestExample.o

clang -target arm64-apple-macos12.0 \-fobjc-arc \-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk \-c TestExample.m -o TestExample.o

  1. 编译TestExample.m ---> libTestExample.dylib

    这里有两种做法,一种还是使用clang命令,另一种是先编译为静态库后再链接为动态库

    • clang命令:

    clang -dynamiclib  \-target arm64-apple-macos12.0 \-fobjc-arc \-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk \TestExample.o -o libTestExample.dylib

    • 先静态后动态:

    libtool -static -arch_only arm64 TestExample.o -o libTestExample.a

    ld -dylib -arch arm64 \-macosx_version_min 12.0 \-syslibroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk \-lsystem -framework Foundation \libTestExample.a -o libTestExample.dylib

  2. 链接libTestExample.dylib ----> test EXEC

clang -target arm64-apple-macos12.0 \-fobjc-arc \-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk \-L./dylib \-lTestExample \test.o -o test

但是这里会报个错,链接过程符号找不到,这就是因为之前说的dead strip,只需要加上-all_load即可

image.png

但是在lldb环境下运行已经生成的可执行文件时会报出另一个错

image.png

Image Not Found原因分析

graph TD
dyld --> Mach-O --> LC_LOAD_DYLIB ---> /System/Library/Frameworks/Foundation.framework/Versions/C/Foundation
Mach-O --> LC_LOAD_DYLIB2 ---> libTestExample.dylib

大致如图所示,在链接动态库的过程中,其中路径便存在于可执行文件Mach-O中的一个Load-Command中,具体字段为LC_LOAD_DYLIB,然后读到这个地址后会去以这个地址来加载动态库,如果没找到则报出image not found

具体可以使用命令otool -l test | grep "DYLIB" -A 5来查看

image.png

同样这个路径是先存在于动态库本身的,通过命令查看otool -l libTestExample.dylib | grep "ID" -A 5

image.png

修改动态库安装路径

image.png

install_name_tool -id [绝对路径] libTestExample.dylib

这里同样可以使用链接器的命令来执行 image.png ld -install_name [绝对路径]

再对可执行程序进行重新链接动态库后执行

此时再查看可执行程序中的LC_LOAD_DYLIBLoad-Command

otool -l test | grep "DYLIB" -A 5

image.png

image.png

但是刚才使用的是绝对路径,一旦把库更换到别的目录,问题就出来了,非常不灵活

@rpath

@rpath全名是:Runpath Search Paths,按照我们的例子来讲就是Test链接动态库,那么@rpath就由Test来提供。

同样@rpath也是存在于可执行文件的LC_Command

install_name_tool -rpath [可执行文件所在路径]

otool -l test | grep "rpath" -A 5 -i(-i参数可忽略大小写进行搜索)查看,已存在LC_RPATH

image.png

此时再对动态库进行修改install_name_tool -change [之前路径] @rpath/dylib/libTestExample.dylib libTestExample.dylib

查看otool -l libTestExample.dylib | grep "ID" -A 5 image.png

这样即可成功运行

image.png

但是这样对于可执行文件test来说,它提供的@rpath其实还是一个绝对路径。

@executable_path

@executable_path表示可执行程序所在的目录。

那么对于刚才的问题直接对test@rpath进行修改即可

install_name_tool -rpath [原路径] @executable_path test

通过otool -l test | grep "rpath" -A 5 -i查看

image.png

@loader_path

@loader_path表示被加载的Mach-O文件所在的目录。这样看起来好像和@executable_path是一样的,这只针对于单层链接调用动态库是一样的情况,一旦可执行文件调用一个动态库A动态库A又链接动态库B,那么对于动态库B给它提供@rpath的就是动态库A,动态库A提供的RPATH就可以使用到@loader_path

重新导出Framework

如下图所示,Test链接调用动态库A动态库A链接调用动态库B

graph TD
Test --> 动态库A --> 动态库B

那么Test能直接调用动态库B吗?答案是不行的,因为仅从符号的角度来看,通过命令objdump --macho --exports-trie [动态库A]和命令nm -m [动态库A],导出符号表中没有动态库B的符号。如果要调用可以使用重新导出动态库B的方案。

动态库A编译链接动态库B的过程中,告诉链接器要重新导出动态库B -Xlinker -reexport_framework -Xlinker TestExampleLog

image.png

动态库AMach-O中新增了LC_REEXPORT_DYLIB字段,标记了重新导出的动态库

那么在Test中编译成目标文件的过程中告诉其动态库B的头文件存放路径 clang -target x86_64-apple-macos12.0 \-fobjc-arc \-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk \-I./Frameworks/TestExample.framework/Headers \-I./Frameworks/TestExample.framework/Frameworks/TestExampleLog.framework/Headers \-c test.m -o test.o

Test.m中直接调用动态库B

image.png

编译链接后成功运行,可以看到成功调用了动态库B

image.png

猜你喜欢

转载自juejin.im/post/7035198242929262599