1)来源
.cfi_def_cfa_offset directives in assembly generated by GCC
不知道intel有没有。
main()
is called from somewhere else (in the libc
C runtime support code), and, at the time the call
instruction is executed, %rsp
will point to the top of the stack (which is the lowest address - the stack grows downwards)
CFI(calling frame info
)的作用是出现异常时stack
的回滚(unwind
)。而回滚的过程是一级级CFA往上回退,直到异常被catch
。
.cfi指令解读(一)_Alan的修炼的博客-CSDN博客_cfi_def_cfa_offset
上面这个网址其实讲得挺好的。
CFA定义为执行call xxx
时SP(stack pointer
)所指向的地址。
-
pushl %ebp
-
.cfi_def_cfa_offset 8
-
.cfi_offset 5, -8
表示执行完pushl %ebp
后SP与CFA偏了8
字节(4
字节return address
,4
字节ebp
)
-
movl %esp, %ebp
-
.cfi_def_cfa_register 5
表示执行完movl %esp, %ebp
后cfa_register
不再是esp
,而是ebp
-
leave
-
.cfi_restore 5
-
.cfi_def_cfa 4, 4
表示执行完leave
后ebp
的值已经恢复到初始状态,并且CFA的计算方式应该是esp+4
其中寄存器对应的数字是架构相关的,详细参考xxx_map_dwarf_register
:
5d8067c60… Jace*0760 static unsigned x86_64_map_dwarf_register(unsigned regno, const struct module* module, BOOL eh_frame) e2b62c91d… Eric*0761 { 0762 unsigned reg; 0763 0764 if (regno >= 17 && regno <= 24) 0765 reg = CV_AMD64_XMM0 + regno - 17; 0766 else if (regno >= 25 && regno <= 32) 0767 reg = CV_AMD64_XMM8 + regno - 25; 0768 else if (regno >= 33 && regno <= 40) 0769 reg = CV_AMD64_ST0 + regno - 33; 0770 else switch (regno) 0771 { 0772 case 0: reg = CV_AMD64_RAX; break; 0773 case 1: reg = CV_AMD64_RDX; break; 0774 case 2: reg = CV_AMD64_RCX; break; 0775 case 3: reg = CV_AMD64_RBX; break; 0776 case 4: reg = CV_AMD64_RSI; break; 0777 case 5: reg = CV_AMD64_RDI; break; 0778 case 6: reg = CV_AMD64_RBP; break; 0779 case 7: reg = CV_AMD64_RSP; break; 0780 case 8: reg = CV_AMD64_R8; break; 0781 case 9: reg = CV_AMD64_R9; break; 0782 case 10: reg = CV_AMD64_R10; break; 0783 case 11: reg = CV_AMD64_R11; break; 0784 case 12: reg = CV_AMD64_R12; break; 0785 case 13: reg = CV_AMD64_R13; break; 0786 case 14: reg = CV_AMD64_R14; break; 0787 case 15: reg = CV_AMD64_R15; break; 0788 case 16: reg = CV_AMD64_RIP; break; 0789 case 49: reg = CV_AMD64_EFLAGS; break; 0790 case 50: reg = CV_AMD64_ES; break; 0791 case 51: reg = CV_AMD64_CS; break; 0792 case 52: reg = CV_AMD64_SS; break; 0793 case 53: reg = CV_AMD64_DS; break; 0794 case 54: reg = CV_AMD64_FS; break; 0795 case 55: reg = CV_AMD64_GS; break; 0796 case 62: reg = CV_AMD64_TR; break; 0797 case 63: reg = CV_AMD64_LDTR; break; 0798 case 64: reg = CV_AMD64_MXCSR; break; 0799 case 65: reg = CV_AMD64_CTRL; break; 0800 case 66: reg = CV_AMD64_STAT; break; 0801 /* 0802 * 56-57 reserved 0803 * 58 %fs.base 0804 * 59 %gs.base 0805 * 60-61 reserved 0806 */ 0807 default: 0808 FIXME("Don't know how to map register %d\n", regno); 0809 return 0; 0810 } 0811 return reg; 0812 } 0813
/wine-7.5/dlls/dbghelp/cpu_x86_64.c
基本上解释清楚了。
7.12.13 .cfi_offset register, offset
Previous value of register is saved at offset offset from CFA.
这里的register是一个编号,真是莫名其妙的规定。
2)
We refer to this address as the Canonical Frame Address or CFA
CFA是个地址,与RSP之间的距离,有什么用?
As the call
instruction is executed, it will push a 64-bit (8 byte) return address onto the stack:
: :
| whatever | <--- CFA
+----------------+
| return address | <--- %rsp == CFA - 8
+----------------+
Now we are running the code at main
, which executes subq $8, %rsp
to reserve another 8 bytes of stack for itself:
: :
| whatever | <--- CFA
+----------------+
| return address |
+----------------+
| reserved space | <--- %rsp == CFA - 16
+----------------+
#include <stdio.h>
int main(int argc, char** argv)
{
printf("%d", 0);
return 0;
}
The generated assembly:
.file "test.c"
.section .rodata.str1.1,"aMS",@progbits,1
.LC0:
.string "%d"
.text
.p2align 4,,15
.globl main
.type main, @function
main:
.LFB22:
.cfi_startproc
subq $8, %rsp
.cfi_def_cfa_offset 16
xorl %edx, %edx
movl $.LC0, %esi
movl $1, %edi
xorl %eax, %eax
call __printf_chk
xorl %eax, %eax
addq $8, %rsp
.cfi_def_cfa_offset 8
ret
.cfi_endproc
.LFE22:
.size main, .-main
.ident "GCC: (Ubuntu/Linaro 4.5.2-8ubuntu4) 4.5.2"
.section .note.GNU-stack,"",@progbits
这个.cfi_def_cfa_offset 是解释得清楚了。
rsp 栈顶,低地址。rbp 栈底,高地址。向下生长。
.cfi_def_cfa_offset
modifies a rule for computing CFA. Register remains the same, but offset is new. Note that it is the absolute offset that will be added to a defined register to compute CFA address.
不知道有什么用。
https://sourceware.org/binutils/docs/as/CFI-directives.html#CFI-directives
7.12.11 .cfi_def_cfa_offset offset
.cfi_def_cfa_offset
modifies a rule for computing CFA. Register remains the same, but offset is new. Note that it is the absolute offset that will be added to a defined register to compute CFA address.
7.12.13 .cfi_offset register, offset
Previous value of register is saved at offset offset from CFA.
寄存器的前一个值被保存在CFA的偏移地址?什么意思嘛?
3)
8.12. .cfi_def_cfa_offset offset
.cfi_def_cfa_offset modifies a rule for computing CFA. Register remains the same, but offset is new. Note that it is the absolute offset that will be added to a defined register to compute CFA address.
绝对位置,问题是CFA有什么用?
8.14. .cfi_offset register, offset
Previous value of register is saved at offset offset from CFA.
都属于Assembler Directives,编译指导语句。
做啥用的嘛?
8.3. .align abs-expr, abs-expr, abs-expr
Pad the location counter (in the current subsection) to a particular storage boundary. The first expression (which must be absolute) is the alignment required, as described below.
对齐方式,具体是怎么回事嘛?其实要举例子才行。
http://web.mit.edu/rhel-doc/3/rhel-as-en-3/index.html
桢的概念
4)
5)
CFI 即 Call Frame Information,是 DWARF 2.0 定义的函数栈信息,DWARF 即 Debugging With Attributed Record Formats ,是一种调试信息格式。
.cfi_ 开头的汇编指示符,例如 .cfi_startproc 、.cfi_undefined 、.cfi_endproc 等,CFI 即 Call Frame Information,是 DWARF 2.0 定义的函数栈信息,DWARF 即 Debugging With Attributed Record Formats ,是一种调试信息格式。 .cfi_ 开头的汇编指示符用来告诉汇编器生成相应的 DWARF 调试信息,主要是和函数有关。.cfi_startproc 定义函数开始,.cfi_endproc 定义函数结束。
意思可以全部删掉?