How do I log stack information? (a)

When the App is stuck or crashes, or when the developer wants to record the stack information, it can help the developer to solve the problem better. Today, we will analyze the working principle of BSBacktraceLogger

The principle of the record stack is basically as follows:

  1. Back in the stack frame
  2. Get the function call address,
  3. parsingMachOThe file gets the function name

To understand the backtrace stack frame, you need to understand how the arm64 function stack works

Function call stack

First, a brief introduction to ARM64 knowledge

register describe
x0-x30 General purpose register, w0-W30 when 32 bits
X29 (FP) The bottom position of the current function stack frame
X (LR) Points to the next instruction to be executed after the current function completes execution
SP Move SP to the top of the current function stack frame. Move SP to change the current function stack frame size
PC The program counter always points to the next hop instruction to be executed
CPSR Status register
Void c (){int c = 3; } void a (){ int a = 4; c(); }Copy the code

Let’s make a break point at b()

Assume that the current call stack looks like the following:


0x104f793a8 <+0>: sub sp, sp, #0x20 ; =0x20

The SP moves 0x20(32) bytes towards the lower address, as shown in the figure below


0x104f793ac <+4>: stp x29, x30, [sp, #0x10]

The values of x29 and X30 registers are stored at SP + 0x10 at the top of the current stack. The bottom of the stack of C function and the next instruction after the return of A function are stored respectively


0x104f793b0 <+8>: add x29, sp, #0x10 ; =0x10

The current top SP + 0x10 is stored in the X29 (FP) register, which is equivalent to FP pointing to SP + 0x10. FP points to the bottom of the call stack of function A, which stores the bottom of the call stack of function C


0x104f793b4 <+12>: mov w8, #0x4

This instruction is a simple assignment operation, w8 is the lower 8-bit storage constant 4 of register X8


0x104f793b8 <+16>: stur w8, [x29, #-0x4]

Store the value of W8 in the X29 (FP) register offset 4 bytes down


-> 0x104f793bc <+20>: bl 0x104f79394 ; c at ViewController.m:67

Bl is a jump instruction with a return, and the return address is stored in an X30 (LR) register

Jump to 0x104F79394 and go to function C


0x104f79394 <+0>: sub sp, sp, #0x10 ; =0x10

Sp continues to go down


0x104f79398 <+4>: mov w8, #0x3

This instruction is a simple assignment operation, w8 is the lower 8-bit storage constant 3 of register X8


-> 0x104f7939c <+8>: str w8, [sp, #0xc]

Store the value of w8 at sp + 0xC,

Compare the location of the assignment operation in the a function

0x104f793b8 <+16>: stur w8, [x29, #-0x4]

Store the value of W8 in the X29 (FP) register offset 4 bytes down

It can also be found that in the assembly code of function B, there is no instruction to save FP(X29) and LR(x30) of function A. It is speculated that because function B is at the end of the whole call chain, it does not call other functions, so it does not need special record, and only needs to return the instruction to LR after completion of execution


0x104f793a0 <+12>: add sp, sp, #0x10 ; =0x10

Sp moves up 0x10, corresponding to the first instruction of c function, one is to push and the other is to push. At this time, C function has been completely called, ret returns to the position of X30 (LR), and the current call stack is as shown in the figure:

Go back to function A and continue

0x104f793c0 <+24>: ldp x29, x30, [sp, #0x10]

Data is read from sp + 0x10 and stored in x29 and X30 registers. As can be seen from the figure above, sp + 0x10 does store x29 and X30 data of the caller of function A, which is actually the restoration site


0x104f793c4 <+28>: add sp, sp, #0x20 ; =0x20

SP is offset 0x20, also corresponding to the first instruction of function B, at which point the call stack of function A ends and ret returns to LR(x30).


Outside a x function calls a function is a process in the stack, the beginning of the call stack, at the same time need to keep the function x FP and LR, in a function of FP and FP + 8 position, namely, the current function a save location of FP is the caller’s FP position, at the end of a function call returns to the position of LR continue with the next instruction, This instruction belongs to the x function, because we can establish the relationship of the whole call chain by FP, and confirm the symbol of the caller function by LR.

There are two special cases where the call stack cannot be retrieved

  1. Tail-call optimization
  2. Inline function