这篇文章CFI directives in assembly file (18 Jan 2017), 是出自google 工程师Adam Langley
问题提出:
-
函数调用栈
: : | caller's stack | +----------------+ <----$rsp value before CALL | return address | +----------------+ <----$rsp at function entry | caller's rbp | +----------------+ <----$rbp always points here | callee's stack |
函数调用中 调用call指令时将call指令的下一条指令 return address 入栈, 其中return address存放在RIP寄存器中也就是将 RIP入栈,之后进入子函数后会将父函数的栈基地址入栈,其中父函数的栈基地保存在RBP寄存器中。
-
函数栈调用框架:
push rbp
mov rbp, rsp
…. (子函数处理部分)
mov rsp, rbp
pop bp
(leave)
-
问题在于
1)对于只有少量代码的函数;
2) 如果使用 -O -fomit-frame-pointer, 选项 会将省略rbp部分
解决方案:
新增额外的调试表, Call Frame Information (CFI), 汇编指令以 .cfi_开头。
CFA, Canonical Frame Address, 它的值为RSP value before CALL
: : | whatever | <--- CFA caller's stack +----------------+ | Saved RIP | +----------------+ | caller’s RBP | <--- %rsp == CFA - 16 +----------------+ | | +----------------+ callee's local variables
CFI设计上就是存储一个(寄存器,偏移)的键值对。
-
CFI函数栈调用框架:
.cfi_startproc
.cfi_def_cfa rsp,8
push rbp
.cfi_def_cfa rsp, 16
mov rbp, rsp
.cfi_def_cfa rbp, 16
….
pop rbp
.cfi_def_cfa rsp, 8
ret
.cfi_endproc
-
CFI的CIE和FDE
CIE(Common Information Entry)公共条目信息
FDE(Frame Description Entry)函数中的CFI指令
-
CFI指令优化
更新偏移量,更新寄存器:
.cfi_def_cfa_offset,.cfi_def_cfa_register
使用相对值更新偏移量:
.cfi_adjust_cfa_offset
-
保存寄存器
指示寄存器保存在堆栈中
.cfi_offset
-
CFI表达式
.cfi_escape
允许用户向展开信息添加任意字节。 可以使用它来添加特定于操作系统的CFI操作码,或者GAS尚不支持的通用CFI操作码。
-
CFI注册号
Register number | x86-64 | x86 | ARM |
---|---|---|---|
0 | RAX | EAX | r0 |
1 | RDX | ECX | r1 |
2 | RCX | EDX | r2 |
3 | RBX | EBX | r3 |
4 | RSI | ESP | r4 |
5 | RDI | EBP | r5 |
6 | RBP | ESI | r6 |
7 | RSP | EDI | r7 |
8 | R8 | r8 | |
9 | R9 | r9 | |
10 | R10 | r10 | |
11 | R11 | r11 | |
12 | R12 | r12 | |
13 | R13 | r13 | |
14 | R14 | r14 | |
15 | R15 | r15 | |
16 | RIP |
Be First to Comment