这篇文章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 numberx86-64x86ARM
0RAXEAXr0
1RDXECXr1
2RCXEDXr2
3RBXEBXr3
4RSIESPr4
5RDIEBPr5
6RBPESIr6
7RSPEDIr7
8R8r8
9R9r9
10R10r10
11R11r11
12R12r12
13R13r13
14R14r14
15R15r15
16RIP