动态库与静态库

111 阅读8分钟

动态库与静态库

1. 符号种类与Common Symbol 2.  3.(.a)与.framework静态库详解

  1. shell初探
  2. dead_strip与静态库
  3. .o⽂件的合并与.o⽂件链接静态库的区别
  4. 静态库的合并  

动态库

  1. dead strip 2. 动态库.dylib.framework编译链接详解

  2. install_name与@rpath

  3. tdb格式动态库 5. 符号与库

  4. -noall_load

  5. -all_load

  6. -ObjC

  7. -force_load

-dead_strip
常用库文件格式
  1. .a
  2. .dylib
  3. .framework
  4. .xcframework
XCFramework
  • XCFramework:是苹果官⽅推荐的、⽀持的,可以更⽅便的表示⼀个多个平台和架构的分发⼆进制库的格式。

  • 需要Xcode11以上⽀持。

  • 是为更好的⽀持Mac Catalyst和ARM芯⽚的macOS。

  • 专⻔在2019年提出的framework的另⼀种先进格式。

  • iOS/iPad:arm64

  • iOS/iPad Simulator:x86_64 arm64

  • Mac Catalyst: x86_64 arm64

  • Mac: x86_64 arm64

和传统的framework相⽐:

1. 可以⽤单个.xcframework⽂件提供多个平台的分发⼆进制⽂件;

2. 与Fat Header相⽐,可以按照平台划分,可以包含相同架构的不同平台的⽂件;

3. 在使⽤时,不需要再通过脚本去剥离不需要的架构体系。

什么是库(Library)?
  • 库(Library)说⽩了就是⼀段编译好的⼆进制代码,加上头⽂件就可以供别⼈使⽤。
什么时候会⽤到库(Library)?

1. 某些代码需要给别⼈使⽤,但是我们不希望别⼈看到源码,就需要以 库的形式进⾏封装,只暴露出头⽂件。

2. 对于某些不会进⾏⼤的改动的代码,我们想减少编译的时间,就可以把它打包成库,因为库是已经编译好的⼆进制了,编译的时候只需要 Link ⼀下,不会浪费编译时间。

什么是链接(Link)?
库在使⽤的时候需要链接(Link),链接 的⽅式有两种:

1. 静态

2. 动态

什么是静态库?
  • 静态库即静态链接库:可以简单的看成⼀组⽬标⽂件的集合。即很多⽬
  • 标⽂件经过压缩打包后形成的⽂件。Windows 下的 .lib,Linux 和 Mac下的 .a。Mac独有的.framework。
缺点:
  • 浪费内存和磁盘空间,模块更新困难
什么是动态库?
  • 与静态库相反,动态库在编译时并不会被拷⻉到⽬标程序中,⽬标程序中只会存储指向动态库的引⽤。等到程序运⾏时,动态库才会被真正加载进来。格式有:.framework、.dylib、.tdb。
缺点:
  • 会导致⼀些性能损失。但是可以优化,⽐如延迟绑定(Lazy Binding)技术
什么是tdb格式?
  • tbd全称是text-based stub libraries,本质上就是⼀个YAML描述的⽂本⽂件。
  • 他的作⽤是⽤于记录动态库的⼀些信息,包括导出的符号、动态库的架构信息、动 态库的依赖信息
  • ⽤于避免在真机开发过程中直接使⽤传统的dylib。
  • 对于真机来说,由于动态库都是在设备上,在Xcode上使⽤基于tbd格式的伪 framework可以⼤⼤减少Xcode的⼤⼩。
Framework
  • Mac OS/iOS 平台还可以使⽤ Framework。Framework 实际上是⼀种打包⽅式,将库的⼆进制⽂件,头⽂件和有关的资源⽂件打包到⼀起,⽅便管理和分发。

  • Framework 和系统的UIKit.Framework 还是有很⼤区别。系统的Framework不需要拷⻉到⽬标程序中,我们⾃⼰做出来的 Framework 哪怕是动态的,最后也还是要拷⻉到 App 中(App 和Extension 的 Bundle 是共享的),因此苹果⼜把这种 Framework 称为Embedded Framework。

Embedded Framework
  • 开发中使⽤的动态库会被放⼊到ipa下的framework⽬录下,基于沙盒运⾏。
  • 不同的App使⽤相同的动态库,并不会只在系统中存在⼀份。⽽是会在多个App中各⾃打包、签名、加载⼀份。
Mach-o File Format
  • ⼀个Mach-o⽂件有两部分组成:header 和data。
  • header:代表了⽂件的映射,描述了⽂件的内容以及⽂件所有内容所在的位置。
  • data:紧跟header之后,由多个⼆进制组成,one by one。
Load Commands
  • 进制⽂件加载进内存要执⾏的⼀些指令。
  • 这⾥的指令主要在负责我们APP 对应进程的创建和基本设置(分配虚拟内存,创建主线程,处理代码签名/加密的⼯作),然后对动态链接库(.dylib系统库和我们⾃⼰创建的动态库)进⾏库加载和符号解析的⼯作。
Mach-o File Format
  • ⼀个Mach-o⽂件有两部分组成:header 和data。
  • header:包含三种类型。Mach header,segment,sections
  • header内的section描述了对应的⼆进制信息。
  • 注意⚠:Mach header属于header的⼀部分,它包含了整个⽂件的信息和segment信息。

image.png

image.png

App framework存放位置

image.png

Embedded Framework
  • 开发中使⽤的Embedded Framework会被放⼊到ipa下的Frameworks⽬录下,基于沙盒运⾏。
  • 不同的App使⽤相同的动态库,并不会只在系统中存在⼀份。⽽是会在多个App中各⾃打包、签名、加载⼀份。

image.png

workspace

image.png

A. 可重⽤性。多个模块可以在多个项⽬中使⽤。节约开发和维护时间。

B. 节省测试时间。单独模块意味着每个模块中都可以添加测试功能。

C. 更好的理解模块化思想。

macOS是Big endian or little endian?? iOS呢??
  • 168496141 = 0x0A0B0C0D

image.png

image.png

magic number

image.png

Mach header
  • magic cputype cpusubtype caps filetype ncmds sizeofcmds flags 
  • 0xfeedfacf 16777223 3 0x00 1 4 1400 0x00002000
Mach-o File Format
  • ⼀个Mach-o⽂件有两部分组成:header 和data。

  • Segments(segment commands): 指定操作系统应该将Segments加载到内存中的什么位置,以及为该Segments分配的字节数。还指定⽂件中的哪些字节属于该Segments,以及⽂件包含多少 sections。Mac上始终是4096字节或4 KB的倍数,其中4096字节是最⼩⼤⼩。iOS上是8 KB。Segments名称的约定是使⽤全⼤写字⺟,后跟双下划线(例如__TEXT)。

  • Section: 所有sections都在每个segment之后⼀个接⼀个地描述。sections⾥⾯定义其名称,在内存中的地址,⼤⼩,⽂件中section数据的偏移量和segment名称。Section的名称约定是使⽤全⼩写字⺟,再加上双下划线(例如__text)。

  • ⼀个Mach-o⽂件由两部分组成:header 和data。

image.png

  • __TEXT段:只读区域

  • 包含可执⾏代码和常量数据。

  • __DATA段:读/写

  • 包含初始化和未初始化数据和⼀些动态链接专属数据。

  • 'int main() { }' compiled for x86_64-apple-macosx with clang

  • 0x55, // offset 0 -- pushq %rbp

  • 0x48, 0x89, 0xe5, // offset 1 -- movq %rsp, %rbp

  • 0x31, 0xc0, // offset 4 -- xorl %eax, %eax

  • 0x5d, // offset 6 -- popq %rbp

  • 0xc3 // offset 7 -- retq

  • 那么要验证链接动态库与静态对ipa包体积的影响, 先要明⽩ipa格式到底是什么?

  • 源码⽂件越少,链接的外部函数越少,⽣成的静态库要⽐动态库体积⼩

  • 其次,静态库中某个⽬标⽂件中的代码没有在项⽬中引⽤,那么就不会链接到项⽬的Mach-o⽂件内。

  • 也就是,静态库默认仅将⽤到的⽂件链接进去。

  • 并且以类⽂件为最⼩链接单位。

  • 还有⼀个问题,⽣成库⽂件的时候链接三⽅库会是什么样的情况?

  • 静态库链接动态库

  • 静态库链接静态库

  • 动态库链接静态库

  • 动态库链接动态库

image.png

@rpath
  • Runpath search Paths
  • dyld搜索路径
  • 运⾏时@rpath指示dyld按顺序搜索路径列表,以找到动态库。
  • @rpath保存⼀个或多个路径的变量
  • @executable_path:表示可执⾏程序所在的⽬录,解析为可执⾏⽂件的绝对路径。
  • @loader_path:表示被加载的Mach-O 所在的⽬录,每次加载时,都可能 被设置为不同的路径,由上层指定。

image.png

image.png

image.png

静态链接

image.png

Swift静态库的合并
  • 在 Xcode 9 之后,Swift 开始⽀持静态库。
  • Swift 没有头⽂件的概念,那么我们外界要使⽤Swift中⽤public修饰的类和函数怎么办?
  • Swift库中引⼊了⼀个全新的⽂件.swiftmodule。
  • .swiftmodule 包含序列化过的 AST(抽象语法树,Abstract Syntax
  • Tree),也包含 SIL(Swift 中间语⾔,Swift Intermediate Language)。
Mach-o File Format
'int main() { }' compiled for x86_64-apple-macosx with clang
  • 0x55, // offset 0 -- pushq %rbp
  • 0x48, 0x89, 0xe5, // offset 1 -- movq %rsp, %rbp
  • 0x31, 0xc0, // offset 4 -- xorl %eax, %eax
  • 0x5d, // offset 6 -- popq %rbp
  • 0xc3 // offset 7 -- retq

image.png

image.png

image.png

image.png

  1. 2013年9⽉之前,蜂窝⽹络下载App⼤⼩的限制为50 MB;
  2. 2013年9⽉,iOS 7正式版后,蜂窝⽹络下载App的限制,从 50 MB 提升⾄100 MB
  3. 2017年9⽉,iOS11正式版本后,限制从 100 MB 提升⾄ 150 MB,并在
  4. 2019年5⽉下旬,将 150 MB放宽到200MB;
  5. 2019年9⽉,iOS13正式版本后,放开了蜂窝⽹络下App下载⼤⼩的限制
  • 根据Apple的审核要求,上传App Store的ipa的可执⾏⽂件有⼤⼩限制,这⾥的⼤⼩不是指⼆进制(Mach-O)⽂件⼤⼩,⽽是指其中的__TEXT部分的⼤⼩。
  1. IOS 7版本之前, ⼆进制⽂件中所有__TEXT部分总和不得超过80MB
  2. iOS 7.X ⾄ iOS 8.X,⼆进制⽂件中,每个架构中的__TEXT部分不得超过60MB
  3. iOS 9.0之后,⼆进制⽂件中所有__TEXT部分的总和不超过500 MB

image.png

image.png

Bitcode

image.png

image.png

image.png

⾸先,就是⼤家开发中⾮常关注的问题
  • 静态库 
  • VS 动态库
  • 分发体积 
  • 链接到App中后到体积

image.png

image.png

image.png

  • 对于iOS9,Load commands segment vmsize默认是⼀个内存⻚,也就是16k。
  • 对于iOS10,Load commands segment vmsize默认是32k。
  • vmsize:此segment占⽤的虚拟内存的字节数。
  • filesize:指示此segment在磁盘上占⽤的字节数。
App链接静态库

image.png

App链接动态库

image.png

  • -noall_load 
  • -all_load 
  • -ObjC 
  • -force_load
  • ld(链接器)-map <map_file_path>: 显示所有的符号信息和链接的信息
  • ld(链接器)参数在通过Clang传递时,可以使⽤-Xlinker or -Wl传递
  • Code size 优化
  • Compiler层

image.png

image.png

Bitcode

image.png

image.png

image.png • 精简代码

• 调⽤过程优化

• 内联优化

image.png

image.png

Link层
  • Link Time Optimization(LTO)

image.png

如何探索一个库/组件?

image.png

实战配置

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png