2011-09-23 73 views
39

的說明我想要解釋一下GCC生成的彙編中使用的.cfi_def_cfa_offset指令的值。我隱約知道.cfi指令涉及到調用幀和堆棧展開,但我想要更詳細地解釋爲什麼,例如,在GCC輸出的程序集中使用值16和8編譯以下C程序在我的64位Ubuntu機器上。GAS:.cfi_def_cfa_offset

C程序:

#include <stdio.h> 

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

我調用GCC上的源文件test.c的如下: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開始和功能結束標籤?

回答

61

作爲DWARF spec說,在第6.4節:

[...]的調用幀是 由堆棧上的地址來標識。我們將此地址稱爲規範框架 地址或CFA。通常,CFA被定義爲前一幀中呼叫站點 處的堆棧指針的值(其可能與當前幀的入口處的值不同)。

main()從其他地方稱爲(在libc C運行時支持代碼),並且,在執行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」只是一個任意值。編譯器將生成基於一些實現細節,唯一的標籤的名字,如基本塊的其內部的編號。)

+3

確實很好的解釋。通常標籤是按順序編號的(關於函數範圍),在這裏我們可能只會看到那些,因爲優化器刪除了其他標籤。 – JohnTortugo

+0

非常感謝你給出了非常明確的解釋! – namanhams

+2

要了解.cfi_ *指令,您還應該查看https://sourceware.org/binutils/docs/as/CFI-directives.html。它很薄,但它是官方的。 –

1

我想用於與組裝.cfi_def_cfa_offset指令由GCC生成中使用的值的解釋。

馬修提供了一個很好的解釋。以下是GAS手冊中Section 7.10 CFI Directives的定義:

.cfi_def_cfa_offset修改計算CFA的規則。寄存器保持不變,但抵消是新的。請注意,這是將被添加到定義的寄存器以計算CFA地址的絕對偏移量。

而且.cfi_adjust_cfa_offset

.cfi_def_cfa_offset但偏移量是從先前的偏移中減去被添加的相對值/。