专栏首页落影的专栏为何百兆静态库能打进数兆的可执行文件?

为何百兆静态库能打进数兆的可执行文件?

前言

第三方库是工程开发必不可少的部分,而第三方库可以是.a和.framework的静态库,也可以是.framework的动态库,其中静态库是最常用的方式。 静态库往往比较大,可在打包到可执行文件之后,对安装包大小的增加远远小于静态库本身的Size。 那么,就产生两个问题: 1、静态库里面存在什么内容? 2、静态链接到可执行文件后为什么体积变小? 本文就以.framework的静态库来分析具体情况。

正文

1、framework静态库的打包

新建工程,选择Cocoa Touch Framework,再到Build Settings选择Mach-O TypeStatic Library,然后build出来目标文件就是framework静态库。

2、framework静态库的内容

按照上面的步骤打包出来的LYTestKit.framework,具体的内容如下:

  • Headers为头文件;
  • plist文件和Modules文件夹是一些描述文件;
  • LYTestKit为Mach-O binary文件,LYTestKit占了静态库99%的体积。

3、LYTestKit文件内容分析

打开terminal(终端),进入文件夹所在目录,输入指令file LYTestKit

LYTestKit: Mach-O universal binary with 4 architectures: [arm_v7:current ar archive] [arm64]
LYTestKit (for architecture armv7): current ar archive
LYTestKit (for architecture i386):  current ar archive
LYTestKit (for architecture x86_64):    current ar archive
LYTestKit (for architecture arm64): current ar archive

可以知道LYTestKit包括armv7i386x86_64arm64四种CPU架构的指令。 接下来就是我们的大杀器Hopper Disassembler登场。

软件的链接地址,密码:vbrqit

Hopper Disassembler打开LYTestKit,选择架构为arm64,再选择一个原工程对应的.o文件,首先映入眼帘的是下面这段描述:(不知道.o文件是什么的,点这里复习下)

/*
--------------------------------------------------------------------------------

        File: /Users/loyinglin/Documents/LYTestKit/Framework/LYTestKit.framework/LYTestKit
        MachO file
        CPU: aarch64
        64 bits

--------------------------------------------------------------------------------
*/

接下来,是这个文件的描述:

        ; Segment
        ; Range: [0x0; 0x8a1b[ (35355 bytes)
        ; File offset : [3584; 38939[ (35355 bytes)
        ; Permissions: readable / writable / executable

整个文件的大小是35355 bytes,对应的区间为[3584, 38939]。 文件的最后一行为:

0000000000008a1a        db  0x01 ; '.'

整个Segment的地址是0x8a1b结束,十六进制0x8a1b = 35355 十进制。 从这里可以看出,静态库中地址与大小一一对应。 可以这么理解,假如某条指令需要到地址0x0008a1a取值,即是相对应起始位置加上0x0008a1a偏移的地址。 而静态库中的偏移应该等于内存中的偏移,那么0x0008a1a的偏移地址在内存中的大小就是35355 bytes。

紧接着是下面这段描述:

        ; Section __text
        ; Range: [0x0; 0x2300[ (8960 bytes)
        ; File offset : [3584; 12544[ (8960 bytes)
        ; Flags: 0x80000400
        ;   S_REGULAR
        ;   S_ATTR_PURE_INSTRUCTIONS
        ;   S_ATTR_SOME_INSTRUCTIONS

.Section __text表示接下来是代码段的内容,机器指令的大小为8960 bytes。 类似的,代码段之后还有:

  • Section __objc_classrefs 类的引用,大小为 64 bytes
  • Section __objc_methname 自动生成和声明的函数名, 大小为2140 bytes;("viewDidLoad"、"init"这样的函数名常量)
  • Section __objc_selrefs selector的引用),大小为 520 bytes;(@selector(init)、@selector(setProperty:))
  • Section __objc_data 初始化的变量, 大小为80 bytes;(@property、ivar、 struct __objc_class这样的变量)
  • Section __objc_ivar ivar的引用,大小为 84 bytes;(OBJC_IVAR$_SampleClass._property) ivar是objc_ivar的指针,objc_ivar有变量名、变量类型等成员,如下:
typedef objc_ivar * Ivar;
  struct objc_ivar {
     char *ivar_name;
     char *ivar_type;
     int ivar_offset;
  #ifdef __LP64__
     int space;
  #endif
  }
  • Section __cstring 代码中声明的字符常量,大小为 1360 bytes;(@"Hello"、@"你好"这样的字符常量)
  • Section __cfstring 代码中声明的CFString字符常量,大小为 224 bytes
  • Section __objc_classname Objective-C Class的类名,大小为 64 bytes
  • Section __objc_const __objc_metaclass_xxx常量的引用和__objc_class_xxx structs 的声明,大小为 3672 bytes;(OC的runtime用结构体比如struct __objc_method、 struct __objc_ivar 、struct __objc_property)
  • Section __debug_str 调试用的字符信息,包括方法对应的字符串,大小为 5489 bytes
  • Section __debug_line 调试用的代码行号信息,大小为 2891 bytes
  • 以及各类其他的Section。

在大致了解一个.o文件的内部构成之后,我们来看看链接过程.o文件变化。

4、静态链接的过程

静态连接就是把静态链接库中的文件链接到可执行文件中,整个过程由链接器负责。 链接过程分为两步:

  • 1、空间和地址分配,扫描所有的目标文件,获得各个段的长度、属性、位置信息,并把所有的符号定义以及引用收集起来,放到全局的符号表中。通过所有段的长度,计算和合并后的长度和位置,并建立映射关系;
  • 2、符号解析和重定位,使用上一步收集到的信息,读取文件中段的数据和重定位信息,进行符号解析和重定位。

静态链接的更详细内容点这里

那么可执行文件的内部是什么组成? 答案是:File Header.text secton.data secton.bss secton四大部分。 File Header是文件头,描述整个文件的文件属性,包括是否可执行文件、目标操作系统、目标硬件等信息。文件头还包括一个段表(Section Table)描述下面几个段的偏移地址以及属性。 .text section是代码段,存放编译生成的指令; .data section是数据段,存放已初始化的静态常量数据; .bss section存放未初始化的静态常量。

静态库链接过程体积变小的答案

framework静态库在链接之后,体积会急剧减少,原因有几个: 1、用于链接的信息被剔除,比如说类引用、函数名等,字符信息中的函数名字等在链接时会放入链接表,用于查找地址,但不打入二进制文件; 2、调试用的信息比如符号串、代码行号等不会打入二进制包,而是额外生成符号表; 3、Xcode默认在release下会用fastest的优化选项;

总结

经过此次思考,对静态库的内容有更清晰的认识,也对编译与链接更加了解。

附录 exploring Mach-O binaries analysis of facebook app classllvm MCSectionMachO developer.apple Assemble asm_directives

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 程序员进阶之算法练习(十四)

    前言 坚持做算法练习对开发的好处是抽象能力变强,拿到一个需求能很快对其进行抽象,然后再用学过的设计模式相关知识进行整理,最后用代码实现。 最大的好处在于:对...

    落影
  • Swift学习笔记

    这是一篇学习swift的笔记 Objective-C是很好的语言,Runtime机制、消息机制等也是爱不释手。 Swift一直在更新,闲暇时间学一遍。学习的B...

    落影
  • GPUImage详细解析(九)图像的输入输出和滤镜通道

    前言 GPUImage系列解析已经接近尾声,这次介绍的是: 纹理输入输出GPUImageTextureOutput 和 GPUImageTextureOutpu...

    落影
  • Microsoft Avro介绍

    Microsoft发布了他们自己对Apache Avro通信协议的实现。Avro被描述为“紧凑的二进制数据序列化格式,类似于Thrift或者Protocol B...

    张善友
  • 高并发系统中的常见问题

    本文一共分析了三个案例,分别介绍并发系统中的共享资源并发访问、计算型密集型任务缓存访问 、单一热点资源峰值流量问题和解决方案。 Q1:订票系统,某车次只有一张火...

    wangxl
  • CCF-GAIR 2018「企业之夜」,专属定制晚宴虚位以待

    CCF-GAIR 2018于即日起正式推出「企业之夜」——企业定制晚宴服务,首次在大会同期提供一个 11 个 AI 行业覆盖,上百名高校青年荟萃的定向匹配的企学...

    AI科技评论
  • AI芯片评测如何与时俱进?地平线提出全新MAPS评测方法,帮助用户理解AI芯片性能

    一是峰值算力,它反映AI芯片理论上的最大算力,但却不能反映其在实际场景中的处理能力;

    量子位
  • 透明桥接模式【胖AP+上层网络】

    企业已经部署了有线上网方式的网络,现需要将FAT AP透明植入原有网络,不改变网络拓扑和周边设备的配置,为员工提供无线上网服务。

    网络技术联盟站
  • 紧急整理了 20 道 Spring Boot 面试题,我经常拿来面试别人!

    面试了一些人,简历上都说自己熟悉 Spring Boot, 或者说正在学习 Spring Boot,一问他们时,都只停留在简单的使用阶段,很多东西都不清楚,也让...

    Java技术栈
  • Three.js 粒子系统学习小记:礼花效果实现

    在3D建模过程中,当我们需要创建很多细小的物体时,并不会一个个地创建这些物体,而是通过创建粒子,粒子可以模拟很多效果,例如烟花、火焰、雨滴、雪花、云朵等等。Th...

    郭诗雅

扫码关注云+社区

领取腾讯云代金券