概述
本篇文章主要是模拟app编译生成MachO过程,在这个过程中我们会了解.o文件、以及重定位符号表、再MachO中系统是如何调用函数的。在这个过程中操作会比较多,但是每一步我都会详细讲解。
探究
Macho 代码段
第一步:新建一个test.m
文件,里面只有一个main
函数,如下图,进入文件所在目录,然后再终端输入 clang test.m -o test , 实际上test就是一个可执行的最终产物
通过
objdump --macho -d test
可以查看test的的代码段:第二步.png
第二步:我们在
test.m
里面添加test1
和test2
两个函数,让后重复上面的操作,如下图:第三步.png
第四步.png
我们发现machO的代码段也多出对应的函数,并且在顺序也是按照文件声明函数的顺序来排列的。其实地址我们可以看出
pagezero + offset
,举例:100003f80 = 10000000 + 3f80
总结:实际
macho
的代码段存储的就是 虚拟地址+代码指令
目标文件
我们知道在编译过程中,我们的.m
会先生成中间文件.o
,最后把.o文件链接到一起,生成我们的可执行文件machO。
clang -c test.m -o test
生成.o文件
- 我们可以看一下
.o
文件的代码段和最终的可执行文件的代码段不一样(.o
文件的前面是序号,而不是虚拟地址),我的理解是这个时候的.o
文件还不是最终的产物,只有合并到一起的时候,才能确定在macho
的最终位置。 - 我们在看
test1
函数的调用,e8 00 00 00 00
其中e8
表示调用的意思后面的00 00 00 00
实际上是占位的偏移量,这个时候还没有分配。只有再等链接完成后才会给值。 - 实际上编译的时候所有的这些等待被分配值都会放在一张表里面,我们叫做重定位符号表,可以用下面的命令查看
objcdump --macho --reloc test.o
第五步.png
如上图,我们可以看到test1
和test2
都在重定位符号表里面
函数调用
我们在代码里面在main
里面调用了test1
,但是再底层是如何查找到test1
的地址的呢?
如上图所:
c5 ff ff ff
由于ios是小端模式实际上这个偏移量的值是0xffffffc5
这是一个负数,并且是一个补码,那么他的原码应该取反加1,得到
0x3b
,然后0x10003fab+(-0x3b)=0x10003f70
,可以得出0x100003f70
正好是test1
虚拟地址。
总结:重定位符号表实际上生成.o
文件的时候会生成,最后生成可执行文件会生成一个大的符号表,也就是会合并所有.o
文件的重定位表,同时会再占位符填写对应的偏移量的值。
网友评论