理解objc运行时四-运行时流程分析

概览

  • 系统调用加载objc动态库前的一些流程

环境

macOS Sierra 10.12.3
Xcode8.2.1

应用程序的执行流程

exec函数族

1
2
3
4
5
$ whatis exec
builtin(1), !(1), %(1), .(1),... endif(1), endsw(1), esac(1), eval(1), exec(1), exit(1), export(1),
...
exec(ntcl) - Invoke subprocesses
sandbox-exec(1) - execute within a sandbox

exec函数族可以根据指定的文件名找到可执行文件,并用它来取代调用进程的内容。

函数原型

1
2
3
4
5
6
7
#include <unistd.h>
int execl(const char *pathname, const char *arg0, ... /* (char *)0 */);
int execv(const char *pathname, char *const argv[]);
int execle(const char *pathname, const char *arg0, ... /* (char *)0, char *const envp[] */);
int execve(const char *pathname, char *const argv[], char *const envp[]);
int execlp(const char *filename, const char *arg0, ... /* (char *)0 */);
int execvp(cosnt char *filename, char *const argv[]);

只有execve是真正意义上的系统调用,其它都是在此基础上经过包装的库函数

macOS*操作系统的内核为XNU*

参考苹果开源网站

在如下目录

xnu/bsd/kern/kern_exec.c

execve函数的定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/*
* execve
*
* Parameters: uap->fname File name to exec
* uap->argp Argument list
* uap->envp Environment list
*
* Returns: 0 Success
* __mac_execve:EINVAL Invalid argument
* __mac_execve:ENOTSUP Invalid argument
* __mac_execve:EACCES Permission denied
* __mac_execve:EINTR Interrupted function
* __mac_execve:ENOMEM Not enough space
* __mac_execve:EFAULT Bad address
* __mac_execve:ENAMETOOLONG Filename too long
* __mac_execve:ENOEXEC Executable file format error
* __mac_execve:ETXTBSY Text file busy [misuse of error code]
* __mac_execve:???
*
* TODO: Dynamic linker header address on stack is copied via suword()
*/
/* ARGSUSED */
int
execve(proc_t p, struct execve_args *uap, int32_t *retval)
{
struct __mac_execve_args muap;
int err;

memoryshot(VM_EXECVE, DBG_FUNC_NONE);

muap.fname = uap->fname;
muap.argp = uap->argp;
muap.envp = uap->envp;
muap.mac_p = USER_ADDR_NULL;
err = __mac_execve(p, &muap, retval);

return(err);
}

可看出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动态库的加载流程。在此之前,有两个问题想要说明一下

  1. macOS系统架构
  2. 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中,如下所示:

Snip20170223_2

字段解释

魔数(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:加载器,负责加载其他动态库

Snip20170223_4

LC_LOAD_DYLIB(xxx):加载的动态库

Snip20170223_5

更详细的可参考 iOS安全–了解Mach-o文件结构

参考

Swift 3上的String和Character 浅析iOS启动图动画
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×