一段代碼的stack調用分析: 下面分析的代碼: [cpp] /* * ===================================================================================== * * Filename: stack.c * * Description: gdb 棧的使用,對棧的分析過程 * * Version: 1.0 * Created: 2013年02月10日 13時48分02秒 * Revision: none * Compiler: gcc * * Author: LeoK, * Organization: * * ===================================================================================== */ #include <stdlib.h> #include <stdio.h> #include <ctype.h> #include <stdlib.h> #define MAX (1UL<<20) typedef unsigned long long u64; typedef unsigned int u32; u32 max_addend = MAX; u64 sum_till_MAX(u32 n) { u64 sum; n++; sum = n; if (n < max_addend) { sum += sum_till_MAX(n); } return sum; } int main(int argc, char** argv) { u64 sum = 0; if (argc == 2 && isdigit(*(argv[1]))) max_addend = strtoul(argv[1], NULL, 0); if (max_addend >= MAX || max_addend == 0) { fprintf(stderr, "Invalid number is specidied\n"); return 1; } www.2cto.com sum = sum_till_MAX(0); printf("sum(0..%lu) = %llu\n", max_addend, sum); return 0; } 圖1 main函數的匯編代碼 其中main調用了sum_till_MAX 步驟1:圖1 movl $0x0, (%esp),這個匯編代碼就是main 調用sum_till_MAX的准備工作. 主要完成的就是stack.c:51行,把0函數參數傳進來(代碼是圖2). 圖2 執行完步驟1此時的棧如下,因為程序執行的時候都是邏輯地址,所以棧地址就假設開始為0x8fff0000開始 Address Content Expain 0x8fff0000 0x0 $esp指向這裡 0x8ffefffc ??? 內容random 0x8ffefff8 ??? 內容random 0x8ffefff4 ??? 內容random 0x8ffefff0 ??? 內容random 0x8ffeffec ??? 內容random 0x8ffeffe8 ??? 內容random 0x8ffeffe4 ??? 內容random 0x8ffeffe0 ??? 內容random 圖3 步驟1完成之後棧的內容 注意:如果函數是多參數的,那麼參數的入棧順序是從右向左入棧,such as int sum(int a, int b, int c) 這個函數的入棧順序是push c =>push b=> push a這個做的原因可能是函數執行可變參數的原因吧 步驟2 main函數調用call sum_till_MAX這個函數首先把eip壓棧,eip就是程序的執行的下一個地址,此時的stack如圖4 Address Content Expain 0x8fff0000 0x0 push 0 0x8ffefffc 0x0804858d 寄存器eip esp指向這裡 0x8ffefff8 ??? 內容random 0x8ffefff4 ??? 內容random 0x8ffefff0 ??? 內容random 0x8ffeffec ??? 內容random 0x8ffeffe8 ??? 內容random 0x8ffeffe4 ??? 內容random 0x8ffeffe0 ??? 內容random 圖4步驟2完畢stack情況 下面進入到sum_till_MAX函數 sum_till_MAX的反匯編代碼如下: 圖5sum_till_MAX函數匯編代碼 步驟1 push %ebp把調用sum_till_MAX函數的ebp進行壓棧,恢復上一層棧幀的時候使用,此時棧如下: Address Content Expain 0x8fff0000 0x0 push 0 0x8ffefffc 0x0804858d 寄存器eip 0x8ffefff8 $ebp 上一個棧幀的棧基址 $esp指向這裡 0x8ffefff4 ??? 內容 random 0x8ffefff0 ??? 內容random 0x8ffeffec ??? 內容random 0x8ffeffe8 ??? 內容random 0x8ffeffe4 ??? 內容random 0x8ffeffe0 ??? 內容random 步驟2 mov %esp , %ebp這段代碼表示把ebp指向esp也是就是地址0x8ffefff4地址。 步驟3 sub $0x28 , %esp表示把esp指針指向0x8ffefff4-0x28=0x8FFEFFCC,也就是為函數內部開辟新的占空間,是2*16+8=40個字節。 此時棧如下: Address Content Expain 0x8fff0000 0x0 push 0 0x8ffefffc 0x0804858d 寄存器eip 0x8ffefff8 $ebp 上一個棧幀的棧基址 0x8ffefff4 ??? $ebp指向這裡 0x8ffefff0 ??? 內容random 0x8ffeffec ??? 內容random … … … 0x8ffeffcc ??? esp指向這裡 步驟4 addl $0x1, 0x8(%ebp)這段代碼是把0拿出來(%ebp+0x8),進行++ 步驟5 mov 0x8(%ebp), %eax 把變量n放到eax寄存器中 步驟6 mov 0x0 , %edx 把寄存器edx清零 步驟7 mov %eax , -0x10(%ebp)這個是sum =n 把n賦值給sum 步驟21 leave指令為刪除棧幀的執行,它執行與1和2相反的處理,以釋放以前的棧。 步驟22 ret 為子程序返回指令,將棧中保存的返回地址POP到程序計數寄存器中,將控制權返回給調用者.