2017-03-25 22 views
7

問題Compilers: Understanding assembly code generated from small programs編譯器使用兩個局部變量而不調整堆棧指針。編譯器使用局部變量而無需調整RSP

不調整RSP的使用局部變量似乎不會中斷安全所以編譯器似乎依靠硬件中斷髮生時自動切換到系統堆棧。否則,出現的第一個中斷會將指令指針壓入堆棧並覆蓋局部變量。

從這個問題的代碼是:

#include <stdio.h> 

int main() 
{ 
    for(int i=0;i<10;i++){ 
     int k=0; 
    } 
} 

由編譯器生成的彙編代碼是:

00000000004004d6 <main>: 
    4004d6:  55      push rbp 
    4004d7:  48 89 e5    mov rbp,rsp 
    4004da:  c7 45 f8 00 00 00 00 mov DWORD PTR [rbp-0x8],0x0 
    4004e1:  eb 0b     jmp 4004ee <main+0x18> 
    4004e3:  c7 45 fc 00 00 00 00 mov DWORD PTR [rbp-0x4],0x0 
    4004ea:  83 45 f8 01    add DWORD PTR [rbp-0x8],0x1 
    4004ee:  83 7d f8 09    cmp DWORD PTR [rbp-0x8],0x9 
    4004f2:  7e ef     jle 4004e3 <main+0xd> 
    4004f4:  b8 00 00 00 00   mov eax,0x0 
    4004f9:  5d      pop rbp 
    4004fa:  c3      ret  

局部變量是在i[rbp-0x8]k[rbp-0x4]

任何人都可以照亮這個中斷問題嗎?硬件是否切換到系統堆棧?怎麼樣?我的理解錯了嗎?

+0

也許這可能是有趣的:http://stackoverflow.com/questions/28759227/which-stack-is-used-by-interrupt-handler-linux – 4386427

回答

8

這是x86-64 ABI的所謂「紅色區域」。從wikipedia總結:

在計算中,紅色區域是在一個函數的超出其不受該函數保留了當前堆棧指針堆棧幀中的固定大小的區域。被調用函數可以使用紅色區域來存儲局部變量而沒有修改堆棧指針的額外開銷。這個區域的內存不會被中斷/異常/信號處理程序修改。 System V使用的x86-64 ABI規定了一個128字節的紅色區域,該區域直接在堆棧指針的當前值下開始。

在64位Linux用戶代碼中,只要不超過128個字節,就可以。它是由葉功能,不調用其他函數即函數最突出使用的優化,


如果你有GCC編譯示例程序作爲一個64位的Linux程序(或使用-mno-red-zone選項你會看到這樣的代碼兼容的編譯器)產生:

main: 
     push rbp 
     mov  rbp, rsp 
     sub  rsp, 16;  <<============ Observe RSP is now being adjusted. 
     mov  DWORD PTR [rbp-4], 0 
.L3: 
     cmp  DWORD PTR [rbp-4], 9 
     jg  .L2 
     mov  DWORD PTR [rbp-8], 0 
     add  DWORD PTR [rbp-4], 1 
     jmp  .L3 
.L2: 
     mov  eax, 0 
     leave 
     ret 

此代碼生成可以在這個godbolt.org鏈接進行觀察。


對於32位Linux用戶程序來說,不調整堆棧指針是件壞事。如果你是(使用-m32選項)來編譯了一個問題,32位代碼的代碼main會出現類似下面的代碼:

main: 
     push ebp 
     mov  ebp, esp 
     sub  esp, 16;  <<============ Observe ESP is being adjusted. 
     mov  DWORD PTR [ebp-4], 0 
.L3: 
     cmp  DWORD PTR [ebp-4], 9 
     jg  .L2 
     mov  DWORD PTR [ebp-8], 0 
     add  DWORD PTR [ebp-4], 1 
     jmp  .L3 
.L2: 
     mov  eax, 0 
     leave 
     ret 

此代碼生成可以在這個gotbolt.org鏈接進行觀察。

+0

感謝您的回答。它解釋了一切。因此,在中斷時,硬件將指令指針向下推入堆棧,因此不會覆蓋局部變量。如果函數需要超過128字節的本地存儲,它必須調整堆棧指針。 –

+1

@Paul - [內核使用單獨的堆棧](http://stackoverflow.com/questions/15168822/intel-x86-vs-x64-system-call/15169141#15169141),所以特權中斷將不會觸及用戶代碼的堆棧。 –

+0

@波佩爾鬆,我知道堆棧是根據中斷的權限級別切換的。因此,相同特權級別的中斷將使用當前堆棧_and_不得使用前128個字節。那是對的嗎? –