XNU 加载 MachO 和 dyld 的流程
-
加载流程总结
XNU加载MachO 可执行文件和dyld 动态链接器文件的大体流程其实很简单:
创建进程 -> 创建虚拟内存空间 ->
解析和映射MachO 可执行文件->
解析和映射dyld 动态链接器文件->
进入动态链接器 dyld的执行流程

以下只是简述了XNU加载MachO 可执行文件和dyld 动态链接器文件的大体的流程
下文的分析中,需要大家根据源码和注释进行反复多次理解,这样才能真正了解整个流程和这个流程中的细节 -
XNU 源码版本
本文所使用的 XNU 源码版本为:
xnu-6153.61.1
可以到苹果官网下载相应版本的 XNU -
XNU 源码分析
① 首先,我们来到:
xnu - 6153.61.1\bsd\kern\kern_exec.c找到load_init_program(...)函数load_init_program(...)函数用于加载系统的初始化进程launchd
launchd是一个用于管理进程的后台守护进程(daemon:守护进程的意思)
在非Debug模式下,只会加载"/sbin/launchd"
参数proc_t p表示由操作系统底层创建出来的原始进程

其中,init_programs[]就是用于存储守护进程路径的数组
在内核的 debug 模式下,可以加载用于调试的launchd.debug或launchd.development
在非 debug 模式下,只加载launchd,用于进程管理

②load_init_program_at_path(...)函数用于验证输入参数和前置条件
并构造用于调用execve(...)函数的参数

③execve(...)函数底层调用__mac_execve(...)函数,用于:
在父进程中fork出一个子进程,并在子进程中调用exec 函数启动新的程序(即执行一个新的程序)

④
exec_activate_image(...)函数用于按照文件的格式分发内存映射的函数,目前支持的文件的格式有 3 种:- 单指令集 MachO 可执行文件
- 多指令集 MachO 可执行文件,即 Fat Binary(如果是 Fat Binary,则会先进行指令集级别的 MachO 分解,然后再循环调用
execsw(...)函数进行内存映射) - Shell 脚本
上层也可直接调用
posix_spawn(...)函数生成新进程,posix_spawn(...)函数会自动调用exec_activate_image(...)函数:

数组execsw[]的定义如下:

⑤exec_mach_imgact(...)函数主要完成了以下几个过程:- 为
vfork(...)函数生成新的线程(vfork(...)函数会生成进程,但不会生成线程) - 把 MachO 可执行文件映射进内存(
调用的子函数 load_machfile(...)) - 签名、uid 等权限处理,dyld 相关的处理工作
- 释放资源

exec_fat_imgact(...)函数用于处理胖二进制文件(通用二进制文件)
exec_shell_imgact(...)函数用于处理 Shell 脚本
这两个函数不是重点,我们只看一下它俩的定义:


⑥load_machfile(...)函数位于\xnu - 6153.61.1\bsd\kern\mach_loader.c中,用于 MachO 可执行文件的加载(不包含 MachO 可执行文件的解析):- 为当前
task分配可执行内存,task是一个任务实例,负责进程内的虚拟内存空间,线程管理等工作 - MachO 和 dyld 的
ASLR 偏移量的随机 - 为
exec_mach_imgact(...)函数回传结果

其中,vm_map_create(..)函数用于创建虚拟内存映射,底层调用了vm_map_create_options(..)函数
vm_compute_max_offset(...)函数则用于获取不同 CPU 架构的最大的虚拟内存空间
这两个函数都位于\xnu - 6153.61.1\osfmk\vm\vm_map.c中


⑦parse_machfile(...)函数主要完成了以下几个工作:MachO可执行文件的解析,相关segment虚拟内存分配dyld动态链接器文件的加载(调用的子函数load_dylinker(...))dyld动态链接器文件的解析及虚拟内存分配
注意:内核(XNU)并不关心 MachO 中具体 Section(节)的内容,即不解析 Section 的具体信息,而是以 Segment(段) 为单位进行映射

⑧load_dylinker(...)函数用于加载动态链接器dyld的可执行文件
并调用步骤 ⑦ 中的parse_machfile(...)函数,进行dyld的解析与虚拟内存分配:

-
App 在虚拟内存中的分布
①
虚拟内存: 我们开发者在开发过程中所接触到的内存均为虚拟内存,虚拟内存使 App 认为它拥有连续可用的内存空间(一个连续完整的地址空间),而实际上,App 通常是分布在多个物理内存碎片中,系统的虚拟内存空间映射表(vm_map)负责管理虚拟内存和物理内存的映射关系文中所提的内存均为虚拟内存。共享动态库其实就是共享的物理内存中的那份动态库,App 虚拟内存中的共享动态库并未真实分配物理内存,使用时虚拟内存会访问同一份物理内存达到共享动态库的目的
iPhoneXr 的物理内存(RAM)只有3GB,那么当超过 3GB 的物理内存时,iOS 是如何处理的呢?
系统会使用一部分硬盘空间(ROM)来充当内存使用,在需要时进行数据交换。当然,硬盘的数据交换速度是远远慢于物理内存的,这也是我们内存过载时,App 卡顿的原因之一②
ASLR: Address Space Layout Randomization,是一种针对缓冲区溢出漏洞的安全保护技术,通过对 堆、栈、共享库映射等 线性区布局的随机化,通过增加攻击者预测目的地址的难度,达到保护的目的。注意:ASLR只是随机了线性区布局的起始地址,而不改变线性区里面内容的顺序③ 以
Arm64架构为例,根据上面对XNU源码的分析可知:- App 最大的虚拟内存空间为 64GB
- 空指针陷阱段的默认大小为 4GB
- MachO 执行文件、dyld 动态链接器文件、共享动态库 都有独立的 ASLR 偏移量
我们大体可以得出一个 App 的虚拟内存分布,如下图所示:

本文介绍XNU操作系统如何加载MachO可执行文件和dyld动态链接器,包括创建进程、虚拟内存空间的分配、文件解析和映射等关键步骤。
&spm=1001.2101.3001.5002&articleId=109707447&d=1&t=3&u=593205a9c1de41f39241d2d6ec0b3ac9)
1475

被折叠的 条评论
为什么被折叠?



