Mach-O与XCode编译环境配置

2,939 阅读4分钟

什么是Mach-O?

Mach-O(Mach Object)macOS、iOS、iPadOS存储程序(动态库,静态库)的文件格式。对应系统通过应用二进制接口(application binary interface,缩写为ABI)来运行该格式的文件。ABI说明了Mach-O的文件格式 Mach-O格式用来替代BSD系统的a.out格式。Mach-O文件格式保存了在编译过程和链接过程中产生的机器代码和数据,从而为静态链接动态链接的代码提供了单一文件格式。

APP 是怎么跑起来的?

Application run的过程,其实就是加载我们application folder里面的可执行文件,那这个过程是怎样的呢?

  1. 调用fork函数,创建一个process
  2. 调用execve或其衍生函数,在该进程上加载,执行我们的Mach-O文件 当我们调用时execve(程序加载器),内核实际上在执行以下操作:
  3. 将文件加载到内存
  4. 开始分析Mach-O中的mach_header,以确认它是有效的Mach-O文件

分析Mach-O

使用如下命令

objdump -macho -private-headers {可执行文件路径}

image.png 可以看到private headers里有很多load command. 可以理解Mach-O文件就是配置文件() + 二进制代码Mach-O中都是二进制. 我们的程序为什么入口是main? 是因为在Mach-O文件中指定了程序入口是mainimage.png 这个LC_MAIN是告诉动态链接器(dyld)去加载可执行文件的入口,并不一定是main, 可以自己指定.

为了理解Mach-O本质上就是一个二进制文件,Mach-O本质上是可读可写的,在网上找了一些资料去分析Mach-O内容详见 image.png Mach-O中的数据是按照一定的规则排列的,最前面是mach_header_64的结构体,后面依次存放的是load_command image.png image.png

编译与链接

当我们编译生成目标文件会有一个.o文件生成,那么这个.o文件是什么呢?为什么要生成一个.o文件?编译过程到底发生了什么事情?在此推荐一本书程序员的自我修养. 编译其实就是将我们所写的代码放到对应的配置文件里面. 比如对于一个符号,是全局的还是本地的,是外部的还是内部的?比如NSLog的调用,NSLog其实是一个外部符号. 根据这些符号的特性,将这些符号在编译的过程中进行分类. 当然我们编译的时候,肯定不止一个.o文件,简单来说,链接的本质就是多个目标文件组合成一个文件.

查看符号的命令

  1. Symbol Table:就是用来保存符号。
  2. String Table:就是用来保存符号的名称。
  3. Indirect Symbol Table:间接符号表。保存使用的外部符号。更准确一点就是使 用的外部动态库的符号。是Symbol Table的子集。

tty命令:输出当前terminal的标识. image.png image.pngBuild Phase中配置run script如上,当我们编译的时候,就会输出hello mmmterminal中. 怎样在我们编译之后,直接执行一个shell脚本呢? 我们在xconfig中定义的变量,也可以在build phases中的run script使用.

当然我们也可以build 通过之后,直接在terminal中执行nm -pa {可执行文件路径} image.png 我们可以看到,输出了符号表的内容,但是把一些我们没必要关心的Debug符号也包含了,当然后面我们可以通过strip命令剥离Debug符号. 我们可以看下build的过程 image.png 我们发现run script是在编译完整,sign之前去run的.

strip 命令

build setting中可以设置strip image.png strip命令可以剥离符号表,符号占用的控件还是挺大的. 但是strip命令是在sign之前,run script之后.

我们查看下ld命令有什么 option可以剥离Debug符号 image.png 因此我们可以在xconfig中配置other link flag 如下:

OTHER_LDFLAGS = -Xlinker -S

可以剥离掉Debug符号,再次执行nm -pa {可执行文件路径}命令, 可以得到如下结果 image.png 上述输出结果,是我们的目标文件中,除了Debug符号的所有符号. 只有了解了符号,后续才能知道怎么对动态库进行瘦身.

对库瘦身

  1. 编译选项-O1 -Oz之类的 image.png
  2. dead code strip死代码剥离 (链接的过程中) image.png
  3. strip命令进行剥离符号(修改mach-o

image.png