匯編語言---函數調用棧


當發生函數調用的時候,棧空間中存放的數據是這樣的:
1、調用者函數把被調函數所需要的參數按照與被調函數的形參順序相反的順序壓入棧中,即:從右向左依次把被調函數所需要的參數壓入棧;
2、調用者函數使用call指令調用被調函數,並把call指令的下一條指令的地址當成返回地址壓入棧中(這個壓棧操作隱含在call指令中);
3、在被調函數中,被調函數會先保存調用者函數的棧底地址(push ebp)(從高內在地址--》低內存地址),然后再保存調用者函數的棧頂地址,即:當前被調函數的棧底地址(mov ebp,esp);
4、在被調函數中,從ebp的位置處開始存放被調函數中的局部變量和臨時變量,並且這些變量的地址按照定義時的順序依次減小,即:這些變量的地址是按照棧的延伸方向排列的,先定義的變量先入棧,后定義的變量后入棧;
所以,發生函數調用時,入棧的順序為:
參數N
參數N-1
參數N-2
.....
參數3
參數2
參數1
函數返回地址
上一層調用函數的EBP/BP
局部變量1
局部變量2
....
局部變量N
函數調用棧如下圖所示:


解釋:
首先,將調用者函數的EBP入棧(push ebp),
然后將調用者函數的棧頂指針ESP賦值給被調函數的EBP(作為被調函數的棧底,mov ebp,esp),
此時,EBP寄存器處於一個非常重要的位置,該寄存器中存放着一個地址(原EBP入棧后的棧頂),

以該地址為基准,向上(棧底方向)能獲取返回地址、參數值,向下(棧頂方向)能獲取函數的局部變量值,而該地址處又存放着上一層函數調用時的EBP值;

一般而言,SS:[ebp+4]處為被調函數的返回地址,
SS:[EBP+8]處為傳遞給被調函數的第一個參數(最后一個入棧的參數,此處假設其占用4字節內存)的值,
SS:[EBP-4]處為被調函數中的第一個局部變量,
SS:[EBP]處為上一層EBP值;由於EBP中的地址處總是"上一層函數調用時的EBP值",

而在每一層函數調用中,都能通過當時的EBP值"向上(棧底方向)能獲取返回地址、參數值,向下(棧頂方向)能獲取被調函數的局部變量值";

如此遞歸,就形成了函數調用棧;
函數內局部變量布局示例:
#include <stdio.h>
#include <string.h>
struct C
{
  int a;
  int b;
  int c;
};
int test2(int x, int y, int z)
{
  printf("hello,test2\n");
  return 0;
}
int test(int x, int y, int z)
{
  int a = 1;
  int b = 2;
  int c = 3;
  struct C st;
  printf("addr x = %u\n",(unsigned int)(&x));
  printf("addr y = %u\n",(unsigned int)(&y));
  printf("addr z = %u\n",(unsigned int)(&z));
  printf("addr a = %u\n",(unsigned int)(&a));
  printf("addr b = %u\n",(unsigned int)(&b));
  printf("addr c = %u\n",(unsigned int)(&c));
  printf("addr st = %u\n",(unsigned int)(&st));
  printf("addr st.a = %u\n",(unsigned int)(&st.a));
  printf("addr st.b = %u\n",(unsigned int)(&st.b));
  printf("addr st.c = %u\n",(unsigned int)(&st.c));
  return 0;
}

int main(int argc, char** argv)
{
  int x = 1;
  int y = 2;
  int z = 3;
  test(x,y,z);
  printf("x = %d; y = %d; z = %d;\n", x,y,z);
  memset(&y, 0, 8);
  printf("x = %d; y = %d; z = %d;\n", x,y,z);
  return 0;
}
打印輸出如下:
addr x = 4288282272
addr y = 4288282276
addr z = 4288282280
addr a = 4288282260
addr b = 4288282256
addr c = 4288282252
addr st = 4288282240
addr st.a = 4288282240
addr st.b = 4288282244
addr st.c = 4288282248
a = 1; b = 2; c = 3;
a = 0; b = 0; c = 3;
示例效果圖:


該圖中的局部變量都是在該示例中定義的;

 這個圖片中反映的是一個典型的函數調用棧的內存布局;


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM