静态库和动态库相关知识

静态库和动态库

常用命令

  • nm

nm 是用来查看二进制文件符号表的命令

常用参数
-a 显示所有符号表条目,包括那些插入供调试器使用的条目。
-g 仅显示全局(外部)符号。
-n 按数字而非字母顺序排序。
-o 将文件或存档元素名称添加到每个输出行,而不仅仅是一次。
-p 不排序; 以符号表顺序显示。
-r 倒序排序。
-u 只显示未定义的符号。
-U 不显示未定义的符号。
Symbol Type说明
Uundefined(未定义)
Aabsolute(绝对符号)
Ttext section symbol(__TEXT.__text)
Ddata section symbol(__DATA.__data)
Bbss section symbol(__DATA.__bss)
Ccommon symbol(只能出现在MH_OBJECT 类型的Mach-O文件中)
-debugger symbol table
S除了上面所述的,存放在其他section的内容,例如未初始化的全局变量存放在(__DATA,__common)中
Iindirect symbol(符号信息相同,代表同一符号)
u动态共享库中的小写u表示一个未定义引用对同一库中另一个模块中私有外部符号
  • ar

ar用于创建和修改二进制库文件
常用于静态库拆分合并替换.o文件。查看静态库中的.o文件等

-d  删除备存文件中的成员文件。 
-m  变更成员文件在备存文件中的次序。
-p  显示备存文件中的成员文件内容。
-q  将文件附加在备存文件末端。
-r  将文件插入备存文件中。
-t  显示备存文件中所包含的文件。
-x  自备存文件中取出成员文件

从libcurl.a中取出libcurl_la-wolfssh.o
ar -x ./libcurl.a libcurl_la-wolfssh.o

从libUAS_arm64.a中删除th-lock.cpp.o
ar -d  ./libUAS_arm64.a th-lock.cpp.o

把libcurl_la-wolfssh.o加入到静态库libUAS_arm64.a中
ar -r ./libUAS_arm64.a ./libcurl_la-wolfssh.o
  • objdump
// 查看mach-header
objdump --macho -private-header ${MACH_PATH}
// 查看__TEXT
objdump --macho -d ${MACH_PATH}
// 查看符号表
objdump --macho --syms ${MACH_PATH}
// 查看导出符号
objdump --macho --exports-trie ${MACH_PATH}
// 查看间接符号表
objdump --macho --indirect-symbols ${MACH_PATH}

--macho: 指定mach-o类型
-h:打印各个段的基本信息
-x:打印各个段更详细的信息
-d:将所有包含指定的段反汇编
-s:将所有段的内容以16进制的方式打印出来
--lazy-bind:打印lazy binding info
--syms:打印符号表
  • 其他
// 创建静态库
libtool -static -arch_only x86_64 WSTLoginHelper.o -o libWSTLoginHelper.a

libtool -static -arch_only arm64 -D -syslibroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.6.sdk test.o -o libTest.a

// 链接静态库
ld -static -arch x86_64 -e _main a.a -o a
// 链接动态库
ld -dylib -arch x86_64 -macosx_version_min 10.13 a.dylib -o a

otool
-l:显示解析后的mach header和load command
-h:显示未解析的mach header 
-L:打印所有链接的动态库路径
-D:打印当前动态库的install_name

strip -S a.o
-S 删除调试符号
-X 移除本地符号 ‘L’开头的
-x 移除全部的本地符号,只保留全局符号

man 命令 : 查看命令的参数以及描述

动态库和静态库链接流程:
链接器ld -> 读取mach header -> 读取load commands -> 读取每个Segment的虚拟地址文件地址和属性 -> 映射到进程虚拟空间 -> 控制权限转交可执行文件入口地址(dyld) -> 程序开始执行

静态库

静态库即静态链接库:可以简单的看成一组目标文件的集合。即很多目标文件经过压缩打包后形成的文件。

这里采用两个简单的源文件来编译成静态库,并链接使用

WSTLoginHelper.h WSTLoginHelper.m main.m

源文件内容

//  WSTLoginHelper.m
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN

@interface WSTLoginHelper : NSObject
+ (void)test;
@end

NS_ASSUME_NONNULL_END

// main.m
#import "WSTLoginHelper.h"
int main() {
    [WSTLoginHelper test];
    return 0;
}
  • 编译成.a静态库

编译脚本

#编译main.m
clang -x objective-c \
-target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk \
-I./lib \
-c ./main.m -o ./main.o

#编译WSTLoginHelper.a
clang -x objective-c \
-target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk \
-I./lib \
-c ./lib/WSTLoginHelper.m -o ./lib/WSTLoginHelper.o

#这里输出的静态库名称一定是lib开头的
ar -rc ./lib/libWSTLoginHelper.a ./lib/WSTLoginHelper.o

#main链接WSTLoginHelper.a
clang -target x86_64-apple-macos11.1   \
-fobjc-arc              \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk      \
-L./lib \
#这里链接的静态库名称为不加lib前缀
-lWSTLoginHelper \
main.o -o main

  • 编译成framework静态库

编译脚本

#编译main.m
clang -x objective-c \
-target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk \
-I./lib \
-c ./main.m -o ./main.o

#编译WSTLoginHelper.framework
echo "开始构建framework"
pushd ./lib

clang -x objective-c \
-target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk \
-I./ \
-c ./WSTLoginHelper.m -o ./WSTLoginHelper

mkdir WSTLoginHelper.framework
mkdir WSTLoginHelper.framework/Headers

cp ./WSTLoginHelper ./WSTLoginHelper.framework
cp ./WSTLoginHelper.h ./WSTLoginHelper.framework/Headers

popd
echo "构建framework结束"

echo "main链接WSTLoginHelper.framework"

clang -target x86_64-apple-macos11.1   \
-fobjc-arc              \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk      \
-F./lib \
-framework WSTLoginHelper \
main.o -o main

echo "main链接WSTLoginHelper.framework完成"

综上所述:不论是链接.a还是framework需要的三要素是静态库的头文件路径、静态库的路径、静态库的名称。

  • 静态库补充说明

id链接器在Other Linker Flags配置说明。
-all_load 加载静态库包含的所有文件
-Objc 加载静态库包含的所有的OC代码和Catehory代码
-force_load <path_to_archive> 加载静态库中指定的文件

动态库

与静态库相反,动态库在编译时并不会被拷⻉到目标程序中,目标程序中只会存储指向动态库的引用。等到程序运行时,动态库才会被真正加载进来

编译动态库采用上面两个源文件。

#编译main.m
clang -x objective-c \
-target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk \
-I./lib \
-c ./main.m -o ./main.o

pushd ./lib
echo "开始编译WSTLoginHelper.m"
clang -x objective-c \
-target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk \
-I./ \
-c ./WSTLoginHelper.m -o ./WSTLoginHelper.o

libtool -static -arch_only x86_64 WSTLoginHelper.o -o libWSTLoginHelper.a

echo "开始构建动态库"
ld -dylib -arch x86_64 \
-macosx_version_min 11.1 \
-syslibroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk \
-lsystem -framework Foundation \
-all_load \
# 指定动态库的rpath
-install_name @rpath/libWSTLoginHelper.dylib \
libWSTLoginHelper.a -o libWSTLoginHelper.dylib

echo "构建dylib结束"

popd


echo "main链接WSTLoginHelper.dylib"

clang -target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk \
-L./lib \
-lWSTLoginHelper \
main.o -o main

#给主程序增加rpath,以免运行的时候无法找到动态库
install_name_tool -add_rpath @executable_path/lib main

otool -l test | grep 'RPATH' -A 3
otool -l test | grep 'DYLIB' -A 3

echo "main链接WSTLoginHelper.dylib完成"


App -> 动态库A -> 动态库B
1.三要素:头文件、库文件所在位置、库文件名称
对于App、动态库A正常链接,但是动态库B并不在动态库A(他链接的动态库B)保存的@rpath与动态库B的install_name组合的路径下:
解决:修改动态库A的rpath或者copy动态库B到指定路径中
方式:i、修改动态库A的rpath为动态库B的install_name之前的绝对路径(LD_RUNPATH_SEARCH_PATHS)
     ii、Cocoapods向App中导入动态库B,在导入的过程中,Cocoapods会帮助我们将动态库B拷贝到App的Frameworks/目录下
     iii、通过脚本进行动手Copy,将动态库B拷贝到App的Frameworks/目录下,参考Cocoapods脚本
     
1. 动态库的反向依赖,因为符号的作用空间问题,那么在运行时,动态库可以动态找到App的符号。所以只要在编译期间不报符号未定义的错误即可。可以通过-U<符号>,来指定一个符号的时动态查找符号。
2. 如果App想使用动态库B的方法,第一种方式是让App直接链接动态库B。第二种方式是通过-reexport_framework或者-reexport_l重新将动态库B通过动态库A导出给App。

 
App -> 动态库A -> 静态库B
因为动态库A生成的过程中在链接静态库B时,会把静态库B所有代码都链接进去。所以编译链接都不会报错。
如果动态库A不想把静态库B的导出符号(全局符号)暴露出去,可以通过 -hidden-l隐藏静态库的全局符号
 
 
App -> 静态库A -> 静态库B
静态库A生成时,只保存了静态库B的头文件信息或者静态库B的称(Auto-Link)。App链接静态库A后,会把静态库A所有代码都链接进去。但是并不知道静态库B的位置和名称。
解决办法:
第一种: 通过Cocoapods将静态库B引入到App内
第二种: 手动配置静态库B的位置和名称。
  
App -> 静态库A -> 动态库B
静态库A生成时,只保存了动态库B的名称(Auto-Link)。App链接静态库A后,会把静态库A所有代码都链接进去。但是并不知道动态库B的位置,也没有提供rpath。
解决办法:
第一种: 通过Cocoapods将动态库B引入到App内
第二种: 手动配置动态库B的位置和提供rpath

弱引用动态库
标记为weak import是,允许在运行时不链接该库。例如正常情况下,动态库链接一个库文件时,如果库文件不在指定的路径中,会报image not found。通过-weak_library <library name>或者-weak_framework <framework name>指定为weak imports,如果在运行时找不到该库,会自动将该库的地址内容设置为0
  • xcframework

xcframework是苹果官方推荐的、支持的,可以更方便的表示一个多个平台和架构的分发二进制库的格式。需要Xcode11以上支持。

和传统的framework相比:

  1. 可以用单个.xcframework文件提供多个平台的分发二进制文件;
  2. 与Fat Header相比,可以按照平台划分,可以包含相同架构的不同平台的文件;
  3. 在使用时,不需要再通过脚本去剥离不需要的架构体系

利用编写好的framework工程生成对应的framework然后合并成xcframework。

编译脚本

#构建模拟器版本的framework
xcodebuild archive -project './FrameworkDemo/FrameworkDemo.xcodeproj' \
-scheme 'WstVPN' \
-configuration Release \
-destination 'generic/platform=iOS Simulator' \
-archivePath './archives/WstVPN.framework-iphonesimulator.xcarchive' \
SKIP_INSTALL=NO

#构建真机版本的framework
xcodebuild archive -project './FrameworkDemo/FrameworkDemo.xcodeproj' \
-scheme 'WstVPN' \
-configuration Release \
-destination 'generic/platform=iOS' \
-archivePath './archives/WstVPN.framework-iphoneos.xcarchive' \
SKIP_INSTALL=NO

#将两个framework合并成xcframework
xcodebuild -create-xcframework \
-framework './archives/WstVPN.framework-iphoneos.xcarchive/Products/Library/Frameworks/WstVPN.framework' \
-framework './archives/WstVPN.framework-iphonesimulator.xcarchive/Products/Library/Frameworks/WstVPN.framework' \
-output './WstVPN.xcframework'

完成之后的目录结构
在这里插入图片描述

  • 总结

动态库与静态对ipa包体积的影响。

源码文件越少,链接的外部函数越少,生成的静态库要比动态库体积小。
静态库中某个目标文件中的代码没有在项目 中引用,那么就不会链接到项目的Mach-o文件内,也就是,静态库默认仅将用到的文件链接进去。并且以类文件为最小链接单位。

@rpath(Runpath search Paths)
dyld搜索路径,运行时@rpath指示dyld按顺序搜索路径列表,来找到动态库。
@executable_path:表示可执行程序所在的目录,解析为可执行文件的绝对路径
@loader_path:表示被加载的Mach-O 所在的目录,每次加载时,都可能被设置为不同的路径,由上层指定。

clang编译命令

clang是C、C++、OC的编译器

-x: 指定编译文件语言类型(objective-c++、objective-c、c)
-g: 生成调试信息
-c: 生成目标文件,只运行preprocess,compile,assemble,不链接
-o: 输出文件
-isysroot: 使用的SDK路径
-I<directory> 在指定目录寻找头文件 header search path
-L<dir> 指定库文件路径(.a\.dylib库文件) library search path
-l<library_name> 指定链接的库文件名称(.a\.dylib库文件)other link flags 
-F<directory> 在指定目录寻找framework framework search path
-framework <framework_name> 指定链接的framework名称 other link flags 
-install_name 指定动态库初次安装的默认路径,向'LC_ID_DYLIB'添加安装路径,该路径作为dylid定位该库
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值