1 棧:棧是一種具有后進先出的數據組織方式,也就是說后存放的先取出,先存放的后取出。棧底是第一個進棧的數據所處位置,棧頂是最后一個數據進棧所處的位置。
數據組織:有鏈表、圖、樹等等(就數據結構那些東東)
2 滿/空棧
根據SP指針指向的位置,棧可以分為滿棧和空棧。
滿棧:當堆棧指針總是指向最后壓入堆棧的數據
空棧:當堆棧指針總是指向下一個將要放入數據的空位置
ARM采用滿棧
3 升/降棧
根據SP指針移動的方向,棧可以分為升棧和降棧
升棧:隨着數據的入棧,SP指針從低地址->高地址移動
降棧:隨着數據的入棧,SP指針從高地址->低地址移動
ARM采用降棧
注:ARM是滿降棧
4 棧幀
上圖描述的是ARM的棧幀布局方式,main stack frame為調用函數的棧幀,func1 stack frame為當前函數(被調用者)的棧幀,棧底在高地址,棧向下增長。圖中FP就是棧基址,它指向函數的棧幀起始地址;SP則是函數的棧指針,它指向棧頂的位置。ARM壓棧的順序依次為當前函數指針PC、返回指針LR、棧指針SP、棧基址FP、傳入參數個數及指針、本地變量和臨時變量。如果函數准備調用另一個函數,跳轉之前臨時變量區先要保存另一個函數的參數。
ARM也可以用棧基址和棧指針明確標示棧幀的位置,棧指針SP一直移動。
棧幀(stack frame):就是一個函數所使用的那部分棧,所有函數的棧幀串起來就組成了一個完整的棧。棧幀的兩個邊界分別由fp(r11)和sp(r13)來限定。
#include <stdio.h> int main() { ... func1(); ... } int func1() { ... }
例子中有兩個函數,程序運行起來會有一個棧。
fp(r11)棧幀指針,棧幀上邊界由fp指針界定,下邊界有sp指針界定。從main函數進入到func1函數,main函數的上邊界和下邊界保存在被它調用的棧幀里面。
5 棧的作用
5.1 保存局部變量
#include <stdio.h> int main() { int a; a++; return a; }
/****************************************************** 反匯編找到main函數 dongry@d-linux:~/test/hardwork/stack$ arm-linux-gcc -g stack1.c -o stack1 dongry@d-linux:~/test/hardwork/stack$ arm-linux-objdump -D -S stack1 >dump dongry@d-linux:~/test/hardwork/stack$ vim dump /main *******************************************************/
/*反匯編代碼*/ 000083a0 <main>: #include <stdio.h> int main() { 83a0: e1a0c00d mov ip, sp 83a4: e92dd800 stmdb sp!, {fp, ip, lr, pc} 83a8: e24cb004 sub fp, ip, #4 ; 0x4 83ac: e24dd004 sub sp, sp, #4 ; 0x4 int a; a++; 83b0: e51b3010 ldr r3, [fp, #-16] 83b4: e2833001 add r3, r3, #1 ; 0x1 83b8: e50b3010 str r3, [fp, #-16] return a; 83bc: e51b3010 ldr r3, [fp, #-16] }
/*分析*/ mov ip,sp //保存sp到ip stmdb sp!,{fp,ip,lr,pc} /*先對sp-4,再對fp,ip,lr,pc壓棧*/ //sp=sp-4;push {pc};sp=pc; /*先壓pc*/ //sp=sp-4;push {lr};sp=lr; /*壓lr*/ //sp=sp-4;push {ip};sp=ip; /*壓ip*/ //sp=sp-4;push {fp};sp=fp; /*壓fp*/ sub fp,ip,#4 //fp指向ip-4 sub sp,sp,#4 //開辟一塊空間 ldr r3,[fp,#-16] //臨時存放在[fp-16] add r3,r3,#1 str r3,[fp,#-16]
5.2 參數傳遞
#include <stdio.h> void func(int a,int b,int c,int d,int e,int f) { int k; int l; k=e+f; l=a+b; } int main() { func(1,2,3,4,5,6); return 0; }
參數大於4個的時候,多出來的參數用棧傳遞;
5.3 保存寄存器的值
#include <stdio.h> void func2(int a,int b) { int k; k=a+b; } void func1(int a,int b) { int c; func2(3,4); } int main() { func1(1,2); return 0; }
如果不用棧,會將原來r0、r1寄存器中的值覆蓋掉