iOS中线程Call Stack的捕获和解析

转自:

http://blog.youkuaiyun.com/jasonblog/article/details/49909163

http://blog.youkuaiyun.com/jasonblog/article/details/49909209


http://blog.youkuaiyun.com/jasonblog/article/details/49909209这里对上个月做的一个技术项目做部分技术小结,这篇文章描述的功能和我们在使用Xcode进行调试时点击暂停的效果类似。

一、获取任意一个线程的Call Stack

如果要获取当前线程的调用栈,可以直接使用现有API:[NSThread callStackSymbols]

但是并没有相关API支持获取任意线程的调用栈,所以只能自己编码实现。

1. 基础结构

一个线程的调用栈是什么样的呢?

我的理解是应该包含当前线程的执行地址,并且从这个地址可以一级一级回溯到线程的入口地址,这样就反向构成了一条链:线程入口执行某个方法,然后逐级嵌套调用到当前现场。

Call_stack_layout_svg(图片来源于维基百科)

如图所示,每一级的方法调用,都对应了一张活动记录,也称为活动帧。也就是说,调用栈是由一张张帧结构组成的,可以称之为栈帧。

我们可以看到,一张栈帧结构中包含着Return Address,也就是当前活动记录执行结束后要返回的地址(展开)。

那么,在我们获取到栈帧后,就可以通过返回地址来进行回溯了。

2. 指令指针和基址指针

我们明确了两个目标:(1)当前执行的指令,(2)当前栈帧结构。

以x86为例,寄存器用途如下:

<code class="hljs livecodeserver has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">SP/ESP/RSP: Stack pointer <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">for</span> top address <span class="hljs-operator" style="box-sizing: border-box;">of</span> <span class="hljs-operator" style="box-sizing: border-box;">the</span> stack.
BP/EBP/RBP: Stack base pointer <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">for</span> holding <span class="hljs-operator" style="box-sizing: border-box;">the</span> address <span class="hljs-operator" style="box-sizing: border-box;">of</span> <span class="hljs-operator" style="box-sizing: border-box;">the</span> current stack frame.
IP/EIP/RIP: Instruction pointer. Holds <span class="hljs-operator" style="box-sizing: border-box;">the</span> program counter, <span class="hljs-operator" style="box-sizing: border-box;">the</span> current instruction address.</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li></ul>

可以看到,我们可以通过指令指针来获取当前指令地址,以及通过栈基址指针获取当前栈帧地址。

那么问题来了,我们怎么获取到相关寄存器呢?

3. 线程执行状态

考虑到一个线程被挂起时,后续继续执行需要恢复现场,所以在挂起时相关现场需要被保存起来,比如当前执行到哪条指令了。

那么就要有相关的结构体来为线程保存运行时的状态,经过一番查阅,得到如下信息:

The function thread_get_state returns the execution state (e.g. the machine registers) of target_thread as specified by flavor.

<code class="hljs cs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">Function - Return the execution state <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">for</span> a thread.

SYNOPSIS

kern_return_t   thread_get_state
                (thread_act_t                     target_thread,
                 thread_state_flavor_t                   flavor,
                 thread_state_t                       old_state,
                 mach_msg_type_number_t         old_state_count);
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/*
 * THREAD_STATE_FLAVOR_LIST 0
 *  these are the supported flavors
 */</span>
<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#<span class="hljs-keyword" style="box-sizing: border-box;">define</span> x86_THREAD_STATE32      1</span>
<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#<span class="hljs-keyword" style="box-sizing: border-box;">define</span> x86_FLOAT_STATE32       2</span>
<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#<span class="hljs-keyword" style="box-sizing: border-box;">define</span> x86_EXCEPTION_STATE32       3</span>
<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#<span class="hljs-keyword" style="box-sizing: border-box;">define</span> x86_THREAD_STATE64      4</span>
<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#<span class="hljs-keyword" style="box-sizing: border-box;">define</span> x86_FLOAT_STATE64       5</span>
<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#<span class="hljs-keyword" style="box-sizing: border-box;">define</span> x86_EXCEPTION_STATE64       6</span>
<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#<span class="hljs-keyword" style="box-sizing: border-box;">define</span> x86_THREAD_STATE        7</span>
<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#<span class="hljs-keyword" style="box-sizing: border-box;">define</span> x86_FLOAT_STATE         8</span>
<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#<span class="hljs-keyword" style="box-sizing: border-box;">define</span> x86_EXCEPTION_STATE     9</span>
<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#<span class="hljs-keyword" style="box-sizing: border-box;">define</span> x86_DEBUG_STATE32       10</span>
<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#<span class="hljs-keyword" style="box-sizing: border-box;">define</span> x86_DEBUG_STATE64       11</span>
<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#<span class="hljs-keyword" style="box-sizing: border-box;">define</span> x86_DEBUG_STATE         12</span>
<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#<span class="hljs-keyword" style="box-sizing: border-box;">define</span> THREAD_STATE_NONE       13</span>
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* 14 and 15 are used for the internal x86_SAVED_STATE flavours */</span>
<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#<span class="hljs-keyword" style="box-sizing: border-box;">define</span> x86_AVX_STATE32         16</span>
<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#<span class="hljs-keyword" style="box-sizing: border-box;">define</span> x86_AVX_STATE64         17</span>
<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#<span class="hljs-keyword" style="box-sizing: border-box;">define</span> x86_AVX_STATE           18</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li></ul>

所以我们可以通过这个API搭配相关参数来获得想要的寄存器信息:

<code class="hljs lasso has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">bool jdy_fillThreadStateIntoMachineContext(thread_t <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">thread</span>, _STRUCT_MCONTEXT <span class="hljs-subst" style="color: rgb(0, 0, 0); box-sizing: border-box;">*</span>machineContext) {
    mach_msg_type_number_t state_count <span class="hljs-subst" style="color: rgb(0, 0, 0); box-sizing: border-box;">=</span> x86_THREAD_STATE64_COUNT;
    kern_return_t kr <span class="hljs-subst" style="color: rgb(0, 0, 0); box-sizing: border-box;">=</span> thread_get_state(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">thread</span>, x86_THREAD_STATE64, (thread_state_t)<span class="hljs-subst" style="color: rgb(0, 0, 0); box-sizing: border-box;">&</span>machineContext<span class="hljs-subst" style="color: rgb(0, 0, 0); box-sizing: border-box;">-></span>__ss, <span class="hljs-subst" style="color: rgb(0, 0, 0); box-sizing: border-box;">&</span>state_count);
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> (kr <span class="hljs-subst" style="color: rgb(0, 0, 0); box-sizing: border-box;">==</span> KERN_SUCCESS);
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li></ul>

这里引入了一个结构体叫_STRUCT_MCONTEXT

4. 不同平台的寄存器

_STRUCT_MCONTEXT在不同平台上的结构不同:

x86_64,如iPhone 6模拟器:

<code class="hljs mathematica has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">_STRUCT_MCONTEXT64
<span class="hljs-list" style="box-sizing: border-box;">{
    _STRUCT_X86_EXCEPTION_STATE64   __es;
    _STRUCT_X86_THREAD_STATE64  __ss;
    _STRUCT_X86_FLOAT_STATE64   __fs;
}</span>;

_STRUCT_X86_THREAD_STATE64
<span class="hljs-list" style="box-sizing: border-box;">{
    __uint64_t  __rax;
    __uint64_t  __rbx;
    __uint64_t  __rcx;
    __uint64_t  __rdx;
    __uint64_t  __rdi;
    __uint64_t  __rsi;
    __uint64_t  __rbp;
    __uint64_t  __rsp;
    __uint64_t  __r8;
    __uint64_t  __r9;
    __uint64_t  __r10;
    __uint64_t  __r11;
    __uint64_t  __r12;
    __uint64_t  __r13;
    __uint64_t  __r14;
    __uint64_t  __r15;
    __uint64_t  __rip;
    __uint64_t  __rflags;
    __uint64_t  __cs;
    __uint64_t  __fs;
    __uint64_t  __gs;
}</span>;</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li></ul>

x86_32,如iPhone 4s模拟器:

<code class="hljs objectivec has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">_STRUCT_MCONTEXT32
{
    _STRUCT_X86_EXCEPTION_STATE32   __es;
    _STRUCT_X86_THREAD_STATE32  __ss;
    _STRUCT_X86_FLOAT_STATE32   __fs;
};

_STRUCT_X86_THREAD_STATE32
{
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">unsigned</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span>    __eax;
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">unsigned</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span>    __ebx;
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">unsigned</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span>    __ecx;
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">unsigned</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span>    __edx;
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">unsigned</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span>    __edi;
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">unsigned</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span>    __esi;
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">unsigned</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span>    __ebp;
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">unsigned</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span>    __esp;
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">unsigned</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span>    __ss;
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">unsigned</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span>    __eflags;
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">unsigned</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span>    __eip;
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">unsigned</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span>    __cs;
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">unsigned</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span>    __ds;
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">unsigned</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span>    __es;
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">unsigned</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span>    __fs;
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">unsigned</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span>    __gs;
};</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li></ul>

ARM64,如iPhone 5s:

<code class="hljs scss has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">_STRUCT_MCONTEXT64
{
    _STRUCT_ARM_EXCEPTION_STATE64   __es;
    _STRUCT_ARM_THREAD_STATE64  __ss;
    _STRUCT_ARM_NEON_STATE64    __ns;
};

_STRUCT_ARM_THREAD_STATE64
{
    __uint64_t    __x<span class="hljs-attr_selector" style="color: rgb(0, 136, 0); box-sizing: border-box;">[29]</span>;  <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* General purpose registers x0-x28 */</span>
    __uint64_t    __fp;     <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* Frame pointer x29 */</span>
    __uint64_t    __lr;     <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* Link register x30 */</span>
    __uint64_t    __sp;     <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* Stack pointer x31 */</span>
    __uint64_t    __pc;     <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* Program counter */</span>
    __uint32_t    __cpsr;   <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* Current program status register */</span>
    __uint32_t    __pad;    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* Same size for 32-bit or 64-bit clients */</span>
};</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li></ul>

ARMv7/v6,如iPhone 4s:

<code class="hljs scss has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">_STRUCT_MCONTEXT32
{
    _STRUCT_ARM_EXCEPTION_STATE __es;
    _STRUCT_ARM_THREAD_STATE    __ss;
    _STRUCT_ARM_VFP_STATE       __fs;
};

_STRUCT_ARM_THREAD_STATE
{
    __uint32_t  __r<span class="hljs-attr_selector" style="color: rgb(0, 136, 0); box-sizing: border-box;">[13]</span>;    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* General purpose register r0-r12 */</span>
    __uint32_t  __sp;       <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* Stack pointer r13 */</span>
    __uint32_t  __lr;       <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* Link register r14 */</span>
    __uint32_t  __pc;       <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* Program counter r15 */</span>
    __uint32_t  __cpsr;     <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* Current program status register */</span>
};</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li></ul>

可以对照《iOS ABI Function Call Guide》,其中在ARM64相关章节中描述到:

The frame pointer register (x29) must always address a valid frame record, although some functions–such as leaf functions or tail calls–may elect not to create an entry in this list. As a result, stack traces will always be meaningful, even without debug information

而在ARMv7/v6上描述到:

The function calling conventions used in the ARMv6 environment are the same as those used in the Procedure Call Standard for the ARM Architecture (release 1.07), with the following exceptions:

*The stack is 4-byte aligned at the point of function calls. 
Large data types (larger than 4 bytes) are 4-byte aligned. 
Register R7 is used as a frame pointer 
Register R9 has special usage.*

所以,通过了解以上不同平台的寄存器结构,我们可以编写出比较通用的回溯功能。

5. 算法实现

<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
 * 关于栈帧的布局可以参考:
 * https://en.wikipedia.org/wiki/Call_stack
 * http://www.cs.cornell.edu/courses/cs412/2008sp/lectures/lec20.pdf
 * http://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64/
 */</span>
typedef struct JDYStackFrame {
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">const</span> struct JDYStackFrame* <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">const</span> previous;
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">const</span> uintptr_t returnAddress;
} JDYStackFrame;

<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//</span>

<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> jdy_backtraceThread(thread_t thread, uintptr_t *backtraceBuffer, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> limit) {
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (limit <= <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>) <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>;

    _STRUCT_MCONTEXT mcontext;
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (!jdy_fillThreadStateIntoMachineContext(thread, &mcontext)) {
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>;
    }

    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> i = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>;
    uintptr_t pc = jdy_programCounterOfMachineContext(&mcontext);
    backtraceBuffer[i++] = pc;
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (i == limit) <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> i;

    uintptr_t lr = jdy_linkRegisterOfMachineContext(&mcontext);
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (lr != <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>) {
        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* 由于lr保存的也是返回地址,所以在lr有效时,应该会产生重复的地址项 */</span>
        backtraceBuffer[i++] = lr;
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (i == limit) <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> i;
    }

    JDYStackFrame frame = {<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>};
    uintptr_t fp = jdy_framePointerOfMachineContext(&mcontext);
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (fp == <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span> || jdy_copyMemory((<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> *)fp, &frame, sizeof(frame)) != KERN_SUCCESS) {
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> i;
    }

    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">while</span> (i < limit) {
        backtraceBuffer[i++] = frame.returnAddress;
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (frame.returnAddress == <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>
            || frame.previous == NULL
            || jdy_copyMemory((<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> *)frame.previous, &frame, sizeof(frame)) != KERN_SUCCESS) {
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">break</span>;
        }
    }

    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> i;
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li><li style="box-sizing: border-box; padding: 0px 5px;">40</li><li style="box-sizing: border-box; padding: 0px 5px;">41</li><li style="box-sizing: border-box; padding: 0px 5px;">42</li><li style="box-sizing: border-box; padding: 0px 5px;">43</li><li style="box-sizing: border-box; padding: 0px 5px;">44</li><li style="box-sizing: border-box; padding: 0px 5px;">45</li><li style="box-sizing: border-box; padding: 0px 5px;">46</li><li style="box-sizing: border-box; padding: 0px 5px;">47</li><li style="box-sizing: border-box; padding: 0px 5px;">48</li><li style="box-sizing: border-box; padding: 0px 5px;">49</li><li style="box-sizing: border-box; padding: 0px 5px;">50</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li><li style="box-sizing: border-box; padding: 0px 5px;">40</li><li style="box-sizing: border-box; padding: 0px 5px;">41</li><li style="box-sizing: border-box; padding: 0px 5px;">42</li><li style="box-sizing: border-box; padding: 0px 5px;">43</li><li style="box-sizing: border-box; padding: 0px 5px;">44</li><li style="box-sizing: border-box; padding: 0px 5px;">45</li><li style="box-sizing: border-box; padding: 0px 5px;">46</li><li style="box-sizing: border-box; padding: 0px 5px;">47</li><li style="box-sizing: border-box; padding: 0px 5px;">48</li><li style="box-sizing: border-box; padding: 0px 5px;">49</li><li style="box-sizing: border-box; padding: 0px 5px;">50</li></ul>

如上。

二、编码实现对一个地址进行符号化解析

后续iOS中线程Call Stack的捕获和解析(二)

上接iOS中线程Call Stack的捕获和解析(一)

1. 部分参考资料

做这一块时也是查阅了很多链接和书籍,包括但不限于:

以及很多Google Search。

2. 相关API和数据结构

由于我们在上面回溯线程调用栈拿到的是一组地址,所以这里进行符号化的输入输出应该分别是地址和符号,接口设计类似如下:

- (NSString *)symbolicateAddress:(uintptr_t)addr;

不过在实际操作中,我们需要依赖于dyld相关方法和数据结构

<code class="hljs applescript has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">/*
 * Structure filled <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">in</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">by</span> dladdr().
 */
typedef struct dl_info {
        const char      *dli_fname;     /* Pathname <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">of</span> shared object */
        void            *dli_fbase;     /* Base address <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">of</span> shared object */
        const char      *dli_sname;     /* Name <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">of</span> nearest symbol */
        void            *dli_saddr;     /* Address <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">of</span> nearest symbol */
} Dl_info;

extern int dladdr(const void *, Dl_info *);
DESCRIPTION
     These routines provide additional introspection <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">of</span> dyld beyond <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">that</span> provided <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">by</span> dlopen() <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">and</span> dladdr()

     _dyld_image_count() returns <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">the</span> current <span class="hljs-type" style="box-sizing: border-box;">number</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">of</span> images mapped <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">in</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">by</span> dyld. Note <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">that</span> using this <span class="hljs-command" style="box-sizing: border-box;">count</span>
     <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">to</span> iterate all images <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">is</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">not</span> thread safe, because another thread may be adding <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">or</span> removing images dur-ing during
     ing <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">the</span> iteration.

     _dyld_get_image_header() returns a pointer <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">to</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">the</span> mach header <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">of</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">the</span> image indexed <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">by</span> image_index.  If
     image_index <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">is</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">out of</span> range, NULL <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">is</span> returned.

     _dyld_get_image_vmaddr_slide() returns <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">the</span> virtural memory address slide amount <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">of</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">the</span> image indexed <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">by</span>
     image_index. If image_index <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">is</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">out of</span> range zero <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">is</span> returned.

     _dyld_get_image_name() returns <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">the</span> <span class="hljs-property" style="box-sizing: border-box;">name</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">of</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">the</span> image indexed <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">by</span> image_index. The C-<span class="hljs-type" style="box-sizing: border-box;">string</span> continues <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">to</span>
     be owned <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">by</span> dyld <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">and</span> should <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">not</span> deleted.  If image_index <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">is</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">out of</span> range NULL <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">is</span> returned.</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li></ul>

又为了要判断此次解析是否成功,所以接口设计演变为:

bool jdy_symbolicateAddress(const uintptr_t addr, Dl_info *info)

Dl_info用来填充解析的结果。

3. 算法思路

对一个地址进行符号化解析说起来也是比较直接的,就是找到地址所属的内存镜像,然后定位该镜像中的符号表,最后从符号表中匹配目标地址的符号。

mach_o_segments(图片来源于苹果官方文档)

以下思路是描述一个大致的方向,并没有涵盖具体的细节,比如基于ASLR的偏移量:

<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 基于ASLR的偏移量https://en.wikipedia.org/wiki/Address_space_layout_randomization</span>

        <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**

         * When the dynamic linker loads an image, 

         * the image must be mapped into the virtual address space of the process at an unoccupied address.

         * The dynamic linker accomplishes this by adding a value "the virtual memory slide amount" to the base address of the image.

         */</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li></ul>

3.1 寻找包含地址的目标镜像

起初看到一个API还有点小惊喜,可惜iPhone上用不了:

extern bool _dyld_image_containing_address(const void* address) 
__OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_3,__MAC_10_5,__IPHONE_NA,__IPHONE_NA);

所以得自己来判断。

怎么判断呢?

A segment defines a range of bytes in a Mach-O file and the addresses and memory protection attributes at which those bytes are mapped into virtual memory when the dynamic linker loads the application. As such, segments are always virtual memory page aligned. A segment contains zero or more sections.

通过遍历每个段,判断目标地址是否落在该段包含的范围内:

<code class="hljs applescript has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">/*
 * The segment load command indicates <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">that</span> a part <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">of</span> this <span class="hljs-type" style="box-sizing: border-box;">file</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">is</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">to</span> be
 * mapped <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">into</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">the</span> task's address <span class="hljs-constant" style="box-sizing: border-box;">space</span>.  The size <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">of</span> this segment <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">in</span> memory,
 * vmsize, maybe <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">equal</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">to</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">or</span> larger than <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">the</span> amount <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">to</span> map <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">from</span> this <span class="hljs-type" style="box-sizing: border-box;">file</span>,
 * filesize.  The <span class="hljs-type" style="box-sizing: border-box;">file</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">is</span> mapped starting <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">at</span> fileoff <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">to</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">the</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">beginning</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">of</span>
 * <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">the</span> segment <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">in</span> memory, vmaddr.  The <span class="hljs-property" style="box-sizing: border-box;">rest</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">of</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">the</span> memory <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">of</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">the</span> segment,
 * <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> any, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">is</span> allocated zero fill <span class="hljs-function_start" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">on</span></span> demand.  The segment's maximum virtual
 * memory protection <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">and</span> initial virtual memory protection are specified
 * <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">by</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">the</span> maxprot <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">and</span> initprot fields.  If <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">the</span> segment has sections <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">then</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">the</span>
 * section structures directly follow <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">the</span> segment command <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">and</span> their size <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">is</span>
 * reflected <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">in</span> cmdsize.
 */
struct segment_command { /* <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">for</span> <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">32</span>-bit architectures */
    uint32_t    cmd;        /* LC_SEGMENT */
    uint32_t    cmdsize;    /* includes sizeof section structs */
    char        segname[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">16</span>];    /* segment <span class="hljs-property" style="box-sizing: border-box;">name</span> */
    uint32_t    vmaddr;     /* memory address <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">of</span> this segment */
    uint32_t    vmsize;     /* memory size <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">of</span> this segment */
    uint32_t    fileoff;    /* <span class="hljs-type" style="box-sizing: border-box;">file</span> <span class="hljs-command" style="box-sizing: border-box;">offset</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">of</span> this segment */
    uint32_t    filesize;   /* amount <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">to</span> map <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">from</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">the</span> <span class="hljs-type" style="box-sizing: border-box;">file</span> */
    vm_prot_t   maxprot;    /* maximum VM protection */
    vm_prot_t   initprot;   /* initial VM protection */
    uint32_t    nsects;     /* <span class="hljs-type" style="box-sizing: border-box;">number</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">of</span> sections <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">in</span> segment */
    uint32_t    flags;      /* flags */
};


/**
 * @brief 判断某个segment_command是否包含addr这个地址,基于segment的虚拟地址和段大小来判断
 */
bool jdy_segmentContainsAddress(const struct load_command *cmdPtr, const uintptr_t addr) {
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (cmdPtr->cmd == LC_SEGMENT) {
        struct segment_command *segPtr = (struct segment_command *)cmdPtr;
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (addr >= segPtr->vmaddr && addr < (segPtr->vmaddr + segPtr->vmsize)) {
<span class="hljs-command" style="box-sizing: border-box;">            return</span> <span class="hljs-constant" style="box-sizing: border-box;">true</span>;
        }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li></ul>

这样一来,我们就可以找到包含目标地址的镜像文件了。

3.2 定位目标镜像的符号表

由于符号的收集和符号表的创建贯穿着编译和链接阶段,这里就不展开了,而是只要确定除了代码段_TEXT和数据段DATA外,还有个_LINKEDIT段包含符号表:

The __LINKEDIT segment contains raw data used by the dynamic linker, such as symbol, string, and relocation table entries.

所以现在我们需要先定位到__LINKEDIT段,同样摘自苹果官方文档:

Segments and sections are normally accessed by name. Segments, by convention, are named using all uppercase letters preceded by two underscores (for example, _TEXT); sections should be named using all lowercase letters preceded by two underscores (for example, _text). This naming convention is standard, although not required for the tools to operate correctly.

我们通过遍历每个段,比较段名称是否和__LINKEDIT相同:

<code class="hljs lasso has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">usr/include/mach<span class="hljs-attribute" style="box-sizing: border-box;">-o</span>/loader<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">.</span>h

<span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">#define</span> SEG_LINKEDIT    <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"__LINKEDIT"</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li></ul>

接着来找符号表:

<code class="hljs applescript has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">/**

 * 摘自《The Mac Hacker's Handbook》:

 * The LC_SYMTAB load command describes <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">where</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">to</span> find <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">the</span> <span class="hljs-type" style="box-sizing: border-box;">string</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">and</span> symbol tables within <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">the</span> __LINKEDIT segment. The offsets <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">given</span> are <span class="hljs-type" style="box-sizing: border-box;">file</span> offsets, so you subtract <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">the</span> <span class="hljs-type" style="box-sizing: border-box;">file</span> <span class="hljs-command" style="box-sizing: border-box;">offset</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">of</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">the</span> __LINKEDIT segment <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">to</span> obtain <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">the</span> virtual memory <span class="hljs-command" style="box-sizing: border-box;">offset</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">of</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">the</span> <span class="hljs-type" style="box-sizing: border-box;">string</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">and</span> symbol tables. Adding <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">the</span> virtual memory <span class="hljs-command" style="box-sizing: border-box;">offset</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">to</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">the</span> virtual-memory address <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">where</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">the</span> __LINKEDIT segment <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">is</span> loaded will give you <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">the</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">in</span>-memory location <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">of</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">the</span> <span class="hljs-type" style="box-sizing: border-box;">string</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">and</span> sym- bol tables.

 */</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li></ul>

也就是说,我们需要结合__LINKEDIT segment_command(见上面结构描述)和LC_SYMTAB load_command(见下面结构描述)来定位符号表:

<code class="hljs applescript has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">/*
 * The symtab_command <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">contains</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">the</span> offsets <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">and</span> sizes <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">of</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">the</span> link-edit <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">4.3</span>BSD
 * <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"stab"</span> style symbol table information <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">as</span> described <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">in</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">the</span> header files
 * <nlist.h> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">and</span> <stab.h>.
 */
struct symtab_command {
    uint32_t    cmd;        /* LC_SYMTAB */
    uint32_t    cmdsize;    /* sizeof(struct symtab_command) */
    uint32_t    symoff;     /* symbol table <span class="hljs-command" style="box-sizing: border-box;">offset</span> */
    uint32_t    nsyms;      /* <span class="hljs-type" style="box-sizing: border-box;">number</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">of</span> symbol table entries */
    uint32_t    stroff;     /* <span class="hljs-type" style="box-sizing: border-box;">string</span> table <span class="hljs-command" style="box-sizing: border-box;">offset</span> */
    uint32_t    strsize;    /* <span class="hljs-type" style="box-sizing: border-box;">string</span> table size <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">in</span> bytes */
};</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li></ul>

如上述引用描述,LC_SYMTAB和_LINKEDIT中的偏移量都是文件偏移量,所以要获得内存中符号表和字符串表的地址,我们先将LC_SYMTAB的symoff和stroff分别减去LINKEDIT的fileoff得到虚拟地址偏移量,然后再加上_LINKEDIT的vmoffset得到虚拟地址。当然,要得到最终的实际内存地址,还需要加上基于ASLR的偏移量。

3.3 在符号表中寻找和目标地址最匹配的符号

终于找到符号表了,写到这里有点小累,直接贴下代码:

<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
 *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @brief</span> 在指定的符号表中为地址匹配最合适的符号,这里的地址需要减去vmaddr_slide
 */</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">const</span> JDY_SymbolTableEntry *jdy_findBestMatchSymbolForAddress(uintptr_t addr,
                                                              JDY_SymbolTableEntry *symbolTable,
                                                              uint32_t nsyms) {

    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 1. addr >= symbol.value; 因为addr是某个函数中的一条指令地址,它应该大于等于这个函数的入口地址,也就是对应符号的值;</span>
    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 2. symbol.value is nearest to addr; 离指令地址addr更近的函数入口地址,才是更准确的匹配项;</span>

    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">const</span> JDY_SymbolTableEntry *nearestSymbol = NULL;
    uintptr_t currentDistance = UINT32_MAX;

    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">for</span> (uint32_t symIndex = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>; symIndex < nsyms; symIndex++) {
        uintptr_t symbolValue = symbolTable[symIndex].n_value;
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (symbolValue > <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>) {
            uintptr_t symbolDistance = addr - symbolValue;
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (symbolValue <= addr && symbolDistance <= currentDistance) {
                currentDistance = symbolDistance;
                nearestSymbol = symbolTable + symIndex;
            }
        }
    }

    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> nearestSymbol;
}


<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/*
 * This is the symbol table entry structure for 64-bit architectures.
 */</span>
struct nlist_64 {
    union {
        uint32_t  n_strx; <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* index into the string table */</span>
    } n_un;
    uint8_t n_type;        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* type flag, see below */</span>
    uint8_t n_sect;        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* section number or NO_SECT */</span>
    uint16_t n_desc;       <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* see <mach-o/stab.h> */</span>
    uint64_t n_value;      <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* value of this symbol (or stab offset) */</span>
};</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li><li style="box-sizing: border-box; padding: 0px 5px;">40</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li><li style="box-sizing: border-box; padding: 0px 5px;">40</li></ul>

找到匹配的nlist结构后,我们可以通过.n_un.n_strx来定位字符串表中相应的符号名。



<think>嗯,我现在需要解释iOS崩溃采集的原理。首先,我得回忆一下自己知道的关于iOS崩溃处理的知识。记得iOS应用如果崩溃了,系统会有一些机制来捕捉这些崩溃信息,然后开发者可以收集这些信息来调试修复问题。那具体是怎么工作的呢? 首先,可能涉及到的是信号处理机制。当应用发生崩溃时,比如访问了野指针或者数组越界,系统会发送特定的信号,比如SIGABRT、SIGSEGV等。应用可以注册信号处理函数来捕获这些信号,然后在处理函数中收集堆信息等数据。这部分好像是通过signal或者sigaction函数来设置的。 然后,还有可能用到的就是NSSetUncaughtExceptionHandler方法。这个方法可以用来捕获未处理的Objective-C异常,比如NSException。当有异常没有被catch的时候,这个handler就会被调用,里面可以记录异常的信息,比如reason、name、callStackSymbols等。 接下来,收集到的崩溃信息需要保存下来,可能在本地存储,等下次应用启动时上传到服务器。不过iOS应用在崩溃后可能无法立即上传,所以通常的做法是将崩溃日志写入到文件,然后在下次启动时检测并发送。 另外,系统本身也会记录崩溃日志,存放在设备的特定目录下,比如Settings → Privacy → Analytics & Improvements → Analytics Data里可以看到一些日志。不过这些日志可能需要用户授权才能获取,或者需要经过符号解析才能有用。 还有,崩溃信息的捕获可能涉及到Mach异常Unix信号的关系。iOS底层是基于Mach的,Mach异常会被转换成相应的Unix信号。所以有时候需要同时处理这两者,或者通过Mach的API来捕获更底层的异常信息。 另外,堆信息的获取是关键部分。在捕获到崩溃信号或异常后,需要获取当前的调用堆。对于Objective-C,可以使用NSThreadcallStackSymbols方法。而对于C++或者纯C的代码,可能需要使用backtracebacktrace_symbols函数来获取堆地址,然后通过符号化工具将其转换为可读的函数名行号。 不过符号化可能需要应用的dSYM文件,这是编译时生成的调试符号文件,用来将内存地址映射到源代码的位置。所以在实际处理崩溃日志时,需要将收集到的堆地址与dSYM文件结合使用,才能得到有意义的堆跟踪。 还有需要注意的地方是,多个崩溃处理可能会冲突。比如如果应用中有多个地方注册了异常处理函数或者信号处理函数,那么后面的可能会覆盖前面的。所以需要确保在设置自己的处理函数时,先保存原来的处理函数,然后在自己的处理完成后,再调用原来的处理函数,避免影响其他框架(比如第三方SDK)的崩溃处理逻辑。 另外,Swift的崩溃处理可能有所不同,因为Swift的错误处理机制Objective-C的异常不同。不过Swift代码如果调用了Objective-C的代码或者产生了一些底层错误,可能还是会触发信号或者异常。所以对于纯Swift的崩溃,可能需要更多的测试不同的处理方式。 还有,崩溃日志的收集需要考虑用户隐私问题。需要确保收集的信息不包含用户的敏感数据,并且符合App Store的审核指南。可能需要对日志进行脱敏处理,或者在收集前获得用户的同意。 总结一下,iOS崩溃采集的大致步骤应该是: 1. 注册未捕获的异常处理函数(NSSetUncaughtExceptionHandler)来捕获Objective-C异常。 2. 注册信号处理函数,处理常见的崩溃信号(如SIGSEGV, SIGABRT等)。 3. 在异常/信号处理函数中收集堆信息、线信息、设备信息等。 4. 将收集到的信息写入本地文件,等待下次应用启动时上传。 5. 上传到服务器后,结合dSYM文件进行符号解析,定位问题代码。 可能还需要考虑多线环境下的安全性,比如在信号处理函数中只能使用异步安全的函数,避免死锁等问题。此外,如何高效地记录传输崩溃日志,避免影响应用性能也是需要考虑的方面。 有没有其他可能遗漏的点呢?比如,CrashReporter框架或者第三方库如PLCrashReporter、KSCrash的工作原理?这些第三方库可能更全面,处理了更多的边缘情况,比如处理Mach异常、生成更详细的报告等。可能他们的实现会更复杂一些,但基本原理应该是类似的。 另外,iOS系统本身提供的崩溃日志收集机制,比如通过Xcode的Organizer可以获取用户共享的崩溃日志,但开发者可能无法完全依赖这个,因为需要用户主动上传,所以自己实现崩溃采集还是有必要的。 最后,还需要考虑如何测试崩溃采集的有效性,比如主动触发各种类型的崩溃(如数组越界、野指针访问、Objective-C异常等),然后验证是否能正确捕获并记录日志。</think>iOS崩溃采集的核心原理是通过**信号处理****异常捕获**机制获取崩溃信息,结合堆解析生成可读报告。以下是分步说明: --- ### **1. 异常类型捕获** #### **1.1 Objective-C异常(NSException)** - **原理**:通过`NSSetUncaughtExceptionHandler`注册全局异常处理器。 - **触发条件**:未捕获的Objective-C异常(如`NSInvalidArgumentException`)。 - **数据采集**: ```objc void CustomExceptionHandler(NSException *exception) { // 记录异常信息:名称、原因、堆符号 NSArray *stack = [exception callStackSymbols]; NSString *reason = [exception reason]; // 保存到本地文件 } NSSetUncaughtExceptionHandler(&CustomExceptionHandler); ``` #### **1.2 底层信号(Signal)** - **原理**:Unix信号(如`SIGSEGV`、`SIGABRT`)捕获。 - **触发条件**:内存访问错误、强制终止等。 - **数据采集**: ```objc void SignalHandler(int signal) { // 获取线(需符号化) void* callstack[128]; int frames = backtrace(callstack, 128); char **strs = backtrace_symbols(callstack, frames); // 保存到本地文件 } // 注册常见信号 signal(SIGSEGV, SignalHandler); signal(SIGABRT, SignalHandler); ``` --- ### **2. 堆信息解析** - **原始堆**:获取的堆地址是内存地址(如`0x1045a6b2c`),需符号化。 - **符号化工具**: - **atos**:命令行工具,将地址转换为函数名+行号。 - **dSYM文件**:编译时生成,包含调试符号。 ```bash atos -o YourApp.dSYM/Contents/Resources/DWARF/YourApp -arch arm64 0x1045a6b2c ``` --- ### **3. 数据存储与上传** - **本地缓存**:崩溃日志写入沙盒(如`Library/Caches/CrashReports`)。 - **延迟上报**:下次启动时检测并上传(崩溃后进无法立即通信)。 --- ### **4. 第三方库优化** - **PLCrashReporter/KSCrash**: - 同时捕获**Mach异常**(iOS底层异常)Unix信号。 - 支持生成更详细的报告(线状态、寄存器值、二进制映像列表)。 --- ### **5. 系统级日志** - **路径**:`Settings → Privacy → Analytics → Analytics Data`(需用户授权共享)。 - **限制**:需设备连接Xcode或用户主动上传,覆盖率低。 --- ### **关键问题与注意事项** 1. **信号安全函数**:信号处理函数中必须使用异步安全函数(如`write`代替`NSLog`)。 2. **避免覆盖**:先保存系统原有处理函数,执行完自定义逻辑后调用原函数。 3. **Swift崩溃**:纯Swift错误需通过`@convention(c)`桥接或结合Objective-C层捕获。 4. **符号化**:需匹配正确的dSYM文件(UUID一致)。 --- 通过以上步骤,开发者可高效采集崩溃信息并定位问题。实际开发中推荐使用成熟库(如PLCrashReporter)以覆盖更多边界情况。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值