概览
- 系统调用加载objc动态库前的一些流程
环境
macOS Sierra 10.12.3
Xcode8.2.1
应用程序的执行流程
exec函数族
1 | $ whatis exec |
exec函数族可以根据指定的文件名找到可执行文件,并用它来取代调用进程的内容。
1 | #include <unistd.h> |
只有execve是真正意义上的系统调用,其它都是在此基础上经过包装的库函数
macOS*操作系统的内核为XNU*
参考苹果开源网站
在如下目录
xnu/bsd/kern/kern_exec.c
有execve函数的定义
1 | /* |
可看出macOS中的execve函数
- 调用__mac_execve函数
- 函数原型的参数和一般的Unix系统有出入
__mac_execve
- exec_activate_image
- exec_mach_imgact
- load_machfile
- parse_machfile
exec_mach_imgact
拷贝可执行文件到内存中,并根据不同的可执行文件类型选择不同的加载函数,所有的镜像的加载要么终止在一个错误上,要么最终完成加载镜像。
OS X有三种可执行文件,mach-o由exec_mach_imgact处理,fat binary由exec_fat_imgact处理,interpreter(解释器)由exec_shell_imgact处理
load_machfile
load_machfile会加载Mach-O中的各种load monmand命令。在其内部会禁止数据段执行,防止溢出漏洞攻击,还会设置地址空间布局随机化(ASLR),还有一些映射的调整。
parse_machfile
parse_machfile会根据load_command的种类选择不同的函数来加载,内部是一个Switch语句来实现的
对于命令的加载会进行多次扫描,当扫描三次之后,并且存在dylinker_command命令时,会执行 load_dylinker(),启动动态链接器 (dyld)
更详细的流程,请参看趣探 Mach-O:加载过程
重点关注的是objc运行时的加载流程,因为运行时也是一个动态库,有dyld加载器后,就可以去关注了objc动态库的加载流程。在此之前,有两个问题想要说明一下
- macOS系统架构
- mach-o文件头
macOS系统架构
Mac OS X is built on a Unix core; the Darwin core is based on the Berkeley Software Distribution (BSD) version of Unix. The heart of the Darwin core is called Mach.
Darwin(达尔文)内核的核心是Mach,上层封装了BSD来支持POSIX
mach-o文件头
概述
Mach-o包含三个基本区域:
头部(header structure)。
加载命令(load command)。
段(segment)。可以拥有多个段(segment),每个段可以拥有零个或多个区域(section)。每一个段(segment)都拥有一段虚拟地址映射到进程的地址空间。
链接信息。一个完整的用户级Mach-o文件的末端是链接信息。其中包含了动态加载器用来链接可执行文件或者依赖库所需使用的符号表,字符串表等等。
新建一个Xcode工程,⌘ + B 后生成Product,将生成的App中的二进制文件拖到MachOView中,如下所示:
字段解释
魔数(Magic Number)
标志了可执行文件的类型
后面找却只有
ARMv7是有的,但是ARM64却在wiki上没找到,搜索后,根据常用魔数
MH_MAGIC_64宏的定义在
xnu-(内核版本xxx)/EXTERNAL_HEADERS/mach-o/loader.h
这样就对上了
架构(Number of Architecture 2)
可支持架构数量(ARMv7 & ARMv64)
CPU类型(CPU Type) & CPU类型(CPU SubType)
标明CPU类型,为了后续方便处理
同样offset,Size,Align都是说明了应用的内存位置相关的信息
Load Commands
加载命令,根据上面会由parse_machfile函数执行
主要关注几个字段
LC_LOAD_DYLINKER:加载器,负责加载其他动态库
LC_LOAD_DYLIB(xxx):加载的动态库
更详细的可参考 iOS安全–了解Mach-o文件结构