在 f2c.cfi中找到main的 FDE,它涵盖了从0x40160到(但不包括)0x401097的main函数:
00000084 0000000000000014 00000088 FDE cie=00000000 pc=0000000000401060..0000000000401097 DW_CFA_advance_loc: 4 to 0000000000401064 DW_CFA_def_cfa_offset: 32 DW_CFA_advance_loc: 50 to 0000000000401096 DW_CFA_def_cfa_offset: 8 DW_CFA_nop
在执行函数中的第一条指令之前,CIE 描述调用帧状态。然而,当处理器执行函数中的指令时,细节将会改变。 首先,指令 DW_CFA_advance_loc和DW_CFA_def_cfa_offset与main中401060处的第一条指令匹配。 这会将堆栈指针向下调整0x18(24 个字节)。 CFA 没有改变位置,但堆栈指针改变了,因此 CFA 在401064处的正确计算是rsp+32。 这就是这段代码中序言指令的范围。 以下是main中的前几条指令:
0000000000401060
DW_CFA_advance_loc使当前行应用于函数中接下来的 50 个字节的代码,直到401096。CFA 位于rsp+32,直到401092处的堆栈调整指令完成执行。DW_CFA_def_cfa_offset将 CFA 的计算更新为与函数入口相同。这是预期之中的,因为401096处的下一条指令是返回指令ret,并将返回值从堆栈中弹出。
401090: 31 c0 xor %eax,%eax 401092: 48 83 c4 18 add $0x18,%rsp 401096: c3 ret
f2c函数的 FDE 使用与main函数相同的 CIE,并覆盖0x41190到0x4011c3的范围:
00000068 0000000000000018 0000006c FDE cie=00000000 pc=0000000000401190..00000000004011c3 DW_CFA_advance_loc: 1 to 0000000000401191 DW_CFA_def_cfa_offset: 16 DW_CFA_offset: r3 (rbx) at cfa-16 DW_CFA_advance_loc: 29 to 00000000004011ae DW_CFA_def_cfa_offset: 8 DW_CFA_nop DW_CFA_nop DW_CFA_nop
可执行文件中 f2c函数的objdump输出:
0000000000401190
在 f2c的 FDE 中,函数开头有一个带有DW_CFA_advance_loc的单字节指令。在高级操作之后,还有两个附加操作。DW_CFA_def_cfa_offset将 CFA 更改为%rsp+16,DW_CFA_offset表示%rbx中的初始值现在位于CFA-16(堆栈顶部)。
查看这个 fc2反汇编代码,可以看到push用于将%rbx保存到堆栈中。 在代码生成中省略帧指针的优点之一是可以使用push和pop等紧凑指令在堆栈中存储和检索值。 在这种情况下,保存%rbx是因为%rbx用于向printf函数传递参数(实际上转换为puts调用),但需要保存传递到函数中的f初始值以供后面的计算使用。4011ae的DW_CFA_advance_loc29字节显示了pop %rbx之后的下一个状态变化,它恢复了%rbx的原始值。DW_CFA_def_cfa_offset指出 pop 将 CFA 更改为%rsp+8。