一段代碼的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到程序計數寄存器中,將控制權返回給調用者.