66

我想解释一下 GCC 生成的程序集中与 .cfi_def_cfa_offset 指令一起使用的值。我隐约知道 .cfi 指令涉及调用帧和堆栈展开,但我想更详细地解释一下为什么在编译以下 C 程序时 GCC 输出的程序集中使用值 16 和 8在我的 64 位 Ubuntu 机器上。

C程序:

#include <stdio.h>

int main(int argc, char** argv)
{
        printf("%d", 0);
        return 0;
}

我在源文件 test.c 上调用了 GCC,如下所示gcc -S -O3 test.c:我知道 -O3 启用非标准优化,但为了简洁起见,我想限制生成的程序集的大小。

生成的程序集:

        .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 指令使用值 16 和 8?另外,为什么数字 22 用于本地函数开始和函数结束标签?

4

2 回答 2

98

正如DWARF 规范在第 6.4 节中所说:

[...]调用帧由堆栈上的地址标识。我们将此地址称为规范帧地址或 CFA。通常,CFA 被定义为前一帧中调用站点的堆栈指针的值(可能与进入当前帧时的值不同)。

main()从其他地方调用(在libcC 运行时支持代码中),并且在call执行指令时,%rsp将指向堆栈的顶部(这是最低地址 - 堆栈向下增长),无论可能是什么(究竟是什么并不重要):

:                :                              ^
|    whatever    | <--- %rsp                    | increasing addresses
+----------------+                              |

此时的值%rsp是“调用站点的堆栈指针的值”,即规范定义的CFA。

call指令执行时,它将一个 64 位(8 字节)的返回地址压入堆栈:

:                :
|    whatever    | <--- CFA
+----------------+
| return address | <--- %rsp == CFA - 8
+----------------+

现在我们在 处运行代码main,它执行subq $8, %rsp为自己保留另外 8 个字节的堆栈:

:                :
|    whatever    | <--- CFA
+----------------+
| return address |
+----------------+
| reserved space | <--- %rsp == CFA - 16
+----------------+

堆栈指针的变化使用指令在调试信息中声明.cfi_def_cfa_offset,您可以看到 CFA 现在位于当前堆栈指针的 16 字节偏移处。

在函数结束时,该addq $8, %rsp指令再次更改堆栈指针,因此.cfi_def_cfa_offset插入另一个指令以指示 CFA 现在位于距堆栈指针仅 8 个字节的偏移处。

(标签中的数字“22”只是一个任意值。编译器将根据一些实现细节生成唯一的标签名称,例如基本块的内部编号。)

于 2011-09-23T23:29:22.247 回答
2

我想解释一下.cfi_def_cfa_offsetGCC 生成的程序集中的指令使用的值。

马修提供了一个很好的解释。这是GAS 手册中第 7.10 节 CFI 指令的定义:

.cfi_def_cfa_offset修改计算 CFA 的规则。寄存器保持不变,但偏移量是新的。请注意,将添加到定义的寄存器以计算 CFA 地址的绝对偏移量。

并且.cfi_adjust_cfa_offset

相同,.cfi_def_cfa_offset但偏移量是从前一个偏移量中添加/减去的相对值。

于 2017-04-20T10:02:12.057 回答