2012-03-25 79 views
4

爲了更好地掌握調用約定以及如何處理堆棧,我進行了一些嘗試,但是我無法弄清楚爲什麼在設置堆棧時爲什麼主要分配三個額外的雙字(在<main+0>)。它沒有對齊到8字節或16字節,所以這不是我所知道的原因。正如我所看到的,主要需要12個字節的兩個參數func和返回值。堆棧分配,爲什麼多餘的空間?

我錯過了什麼?

該程序是在x86架構上用「gcc -ggdb」編譯的C代碼。

編輯:我從gcc刪除了-O0標誌,​​它對輸出沒有任何影響。

(gdb) disas main 
Dump of assembler code for function main: 
    0x080483d1 <+0>: sub esp,0x18 
    0x080483d4 <+3>: mov DWORD PTR [esp+0x4],0x7 
    0x080483dc <+11>: mov DWORD PTR [esp],0x3 
    0x080483e3 <+18>: call 0x80483b4 <func> 
    0x080483e8 <+23>: mov DWORD PTR [esp+0x14],eax 
    0x080483ec <+27>: add esp,0x18 
    0x080483ef <+30>: ret  
End of assembler dump. 

編輯:當然,我應該已經發布了C代碼:

int func(int a, int b) { 
    int c = 9; 
    return a + b + c; 
} 

void main() { 
    int x; 
    x = func(3, 7); 
} 

該平臺的Arch Linux的i686。

+1

發佈C代碼可能會有所幫助 – 2012-03-25 17:02:11

+0

由於您在詢問有關調用約定的知識,因此該平臺也很有用。例如,Mac OS X需要將堆棧保持對齊在16字節的邊界上。 – 2012-03-25 17:07:21

+0

最好假定當你禁用優化時,你最終會看到未優化的代碼。 – 2012-03-25 17:49:20

回答

2

它是一致的。我假設出於某種原因,esp將從一開始就對齊,這顯然不是。

gcc將堆棧幀對齊默認爲16字節,這就是發生了什麼事。

+0

堆棧在* call調用之前由16 *對齊,所以參數16B對齊。如果堆棧在進入'main'之前沒有已知的對齊方式,gcc會發出使用'和esp,-16'對齊的代碼。 – 2017-12-10 17:56:13

4

當您輸入函數時,函數的參數(包括但不限於main)已經在堆棧中。你在函數內分配的空間是局部變量。對於簡單返回類型的函數,如int,返回值通常位於寄存器中(eax,x86上具有典型的32位編譯器)。

如果,例如,main是這樣的:

int main(int argc, char **argv) { 
    char a[35]; 

    return 0; 
} 

...我們希望看到在棧上分配的,因爲我們進入主要以騰出空間給a至少35個字節。假設32位實現,通常會四捨五入到4的下一個倍數(本例中爲36),以保持堆棧的32位對齊。我們不希望看到爲返回值分配任何空間。 argcargv會在堆棧中,但在輸入main之前它們已經在堆棧中,因此main將不必爲分配空間做任何事情。

在上述情況下,爲a分配空間後,a將typicaly開始在[esp-36]argv將在[esp-44]argc將在[esp-48](或這兩個可能被逆轉 - 這取決於是否爭論被推向左邊右或從右到左)。如果你想知道爲什麼我跳過[esp-40],那將是返回地址。

編輯:

enter image description here

編輯2:這裏是在進入功能堆棧的圖形,並設置堆棧幀後根據您的更新問題,你有什麼是稍微的迂迴,但並不特別難以理解。在輸入main時,它不僅爲main的本地變量分配空間,還爲從main調用的功能傳遞參數。

這佔了至少一些額外的空間被分配(雖然不一定是全部)。

+0

但main()只有三個本地整數,12個字節。那麼它爲什麼分配24個字節呢? – spektre 2012-03-25 17:36:13

+1

@spektre:其餘的是填充,將堆棧對齊到16個字節。嘗試修改'func()'以使用less/more參數。你會注意到'main()'的堆棧區域以16字節的增長變化(隨着更多的參數你最終會看到'sub esp,0x28',然後有更多的參數它會變成'sub esp,0x38' ,...)。 – ninjalj 2012-03-25 20:48:46

+0

@ninjalj 對齊是我想到的第一件事,但它沒有加起來,因爲我已經在進入'main()'時已經對齊了'esp'。儘管現在我想到了它,但如果是這樣的話,那麼對齊永遠不會是必需的。 爲什麼它從一開始就沒有對齊呢?當然在輸入'main()'之前運行的代碼使用堆棧,還是該代碼嚴格地位於另一個上下文中? – spektre 2012-03-25 21:03:14