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:
- Back in the stack frame
- Get the function call address,
- parsing
MachO
The 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
- Tail-call optimization
- Inline function