1. ARM的棧幀
先來看看ARM的棧幀布局圖:

上圖描述的是ARM的棧幀布局方式,main stack frame為調用函數的棧幀,func1 stack frame為當前函數(被調用者)的棧幀,棧底在高地址,棧向下增長。圖中FP就是棧基址,它指向函數的棧幀起始地址;SP則是函數的棧指針,它指向棧頂的位置。ARM壓棧的順序很是規矩,依次為當前函數指針PC、返回指針LR、棧指針SP、棧基址FP、傳入參數個數及指針、本地變量和臨時變量。如果函數准備調用另一個函數,跳轉之前臨時變量區先要保存另一個函數的參數。
ARM也可以用棧基址和棧指針明確標示棧幀的位置,棧指針SP一直移動,相比於x86,ARM更為鮮明的特點是,兩個棧空間內的地址(SP+FP)前面,必然有兩個代碼地址(PC+LR)明確標示着調用函數位置內的某個地址。
先來看看ARM的棧幀布局圖:

上圖描述的是ARM的棧幀布局方式,main stack frame為調用函數的棧幀,func1 stack frame為當前函數(被調用者)的棧幀,棧底在高地址,棧向下增長。圖中FP就是棧基址,它指向函數的棧幀起始地址;SP則是函數的棧指針,它指向棧頂的位置。ARM壓棧的順序很是規矩,依次為當前函數指針PC、返回指針LR、棧指針SP、棧基址FP、傳入參數個數及指針、本地變量和臨時變量。如果函數准備調用另一個函數,跳轉之前臨時變量區先要保存另一個函數的參數。
ARM也可以用棧基址和棧指針明確標示棧幀的位置,棧指針SP一直移動,相比於x86,ARM更為鮮明的特點是,兩個棧空間內的地址(SP+FP)前面,必然有兩個代碼地址(PC+LR)明確標示着調用函數位置內的某個地址。
2. ARM的匯編指令和棧操作
ARM微處理器共有37個寄存器,其中31個為通用寄存器,6個為狀態寄存器。但是這些寄存器不能被同時訪問,具體哪些寄存器是可編程訪問的,取決於微處理器的工作狀態及具體的運行模式。但在任何時候,通用寄存器R0~R15、一個或兩個狀態寄存器都是可訪問的。有三個特殊的通用寄存器:
寄存器R13:在ARM指令中常用作堆棧指針SP
寄存器R14:也稱作子程序連接寄存器(Subroutine Link Register)即連接寄存器LR
寄存器R15:也稱作程序計數器PC
ARM進行函數內壓棧和出棧往往使用如下的語句:
stmfd sp!, {r0-r9, lr} ; 滿遞減入棧,給寄存器r0-r9,lr壓棧,sp不斷減4
ldmfd sp!, {r0-r9, pc} ; 滿遞減出棧,給寄存器r0-r9出棧,並使程序跳轉回函數的調用點,sp不斷增4
常用的函數內外跳轉指令有mov和BL,ARM有兩種跳轉方式:
(1)mov pc, <跳轉地址〉
這種向程序計數器PC直接寫跳轉地址,能在4GB連續空間內任意跳轉。
(2)通過 B BL BLX BX 可以完成在當前指令向前或者向后32MB的地址空間的跳轉(為什么是32MB呢?寄存器是32位的,此時的值是24位有符號數,所以32MB?后面再查查看)。B是最簡單的跳轉指令。要注意的是,跳轉指令的實際值不是絕對地址,而是相對地址——是相對當前PC值的一個偏移量,它的值由匯編器計算得出。BL很常用,它在跳轉之前會在寄存器LR(R14)中保存PC的當前內容。BL的經典用法如下:
bl NEXT ; 跳轉到NEXT
……
NEXT
……
mov pc, lr ; 從子程序返回。
ARM微處理器共有37個寄存器,其中31個為通用寄存器,6個為狀態寄存器。但是這些寄存器不能被同時訪問,具體哪些寄存器是可編程訪問的,取決於微處理器的工作狀態及具體的運行模式。但在任何時候,通用寄存器R0~R15、一個或兩個狀態寄存器都是可訪問的。有三個特殊的通用寄存器:
寄存器R13:在ARM指令中常用作堆棧指針SP
寄存器R14:也稱作子程序連接寄存器(Subroutine Link Register)即連接寄存器LR
寄存器R15:也稱作程序計數器PC
ARM進行函數內壓棧和出棧往往使用如下的語句:
stmfd sp!, {r0-r9, lr} ; 滿遞減入棧,給寄存器r0-r9,lr壓棧,sp不斷減4
ldmfd sp!, {r0-r9, pc} ; 滿遞減出棧,給寄存器r0-r9出棧,並使程序跳轉回函數的調用點,sp不斷增4
常用的函數內外跳轉指令有mov和BL,ARM有兩種跳轉方式:
(1)mov pc, <跳轉地址〉
這種向程序計數器PC直接寫跳轉地址,能在4GB連續空間內任意跳轉。
(2)通過 B BL BLX BX 可以完成在當前指令向前或者向后32MB的地址空間的跳轉(為什么是32MB呢?寄存器是32位的,此時的值是24位有符號數,所以32MB?后面再查查看)。B是最簡單的跳轉指令。要注意的是,跳轉指令的實際值不是絕對地址,而是相對地址——是相對當前PC值的一個偏移量,它的值由匯編器計算得出。BL很常用,它在跳轉之前會在寄存器LR(R14)中保存PC的當前內容。BL的經典用法如下:
bl NEXT ; 跳轉到NEXT
……
NEXT
……
mov pc, lr ; 從子程序返回。
看代碼:
int func(int a, int b, int c, int d) { return 1; } int main() { int i = 1, j = 2; func(i, j, 3, 4); return 0; }
使用arm-linux-gcc編譯后,使用ida打開:
.text:000083D0 EXPORT main .text:000083D0 main ; DATA XREF: .text:000082C4o .text:000083D0 ; .text:off_82DCo .text:000083D0 .text:000083D0 b = -0x14 .text:000083D0 a = -0x10 .text:000083D0 .text:000083D0 IP = R12 .text:000083D0 FP = R11 .text:000083D0 MOV IP, SP .text:000083D4 STMFD SP!, {FP,IP,LR,PC} .text:000083D8 SUB FP, IP, #4 .text:000083DC SUB SP, SP, #8 .text:000083E0 MOV R3, #1 .text:000083E4 STR R3, [FP,#a] .text:000083E8 MOV R3, #2 .text:000083EC STR R3, [FP,#b] .text:000083F0 LDR R0, [FP,#a] .text:000083F4 LDR R1, [FP,#b] .text:000083F8 MOV R2, #3 .text:000083FC MOV R3, #4 .text:00008400 BL func .text:00008404 MOV R3, #0 .text:00008408 MOV R0, R3 .text:0000840C SUB SP, FP, #0xC .text:00008410 LDMFD SP, {FP,SP,PC} .text:00008410 ; End of function main
可以發現,在main函數中,使用IP(R12)暫時保存棧指針sp,然后使用堆棧操作指令stmfd將棧幀(FP)、IP、程序返回地址(LR)、程序計數器(PC)壓棧,以保護現場,然后使用sub fp,ip,#4使fp指向當前函數棧幀的棧底,sub sp,sp,#8,為當前函數局部變量分配看空間。接下來通過寄存器傳遞參數r1,r2,r3,r4。使用BL指令調用函數,BL指令同時也會將當前指令的下一條指令地址賦給LR,以跳轉回來。最后使用ldmfd恢復現場。
.text:000083A0 ; =============== S U B R O U T I N E ======================================= .text:000083A0 .text:000083A0 ; Attributes: bp-based frame .text:000083A0 .text:000083A0 EXPORT func .text:000083A0 func ; CODE XREF: main+30p .text:000083A0 .text:000083A0 var_1C = -0x1C .text:000083A0 var_18 = -0x18 .text:000083A0 var_14 = -0x14 .text:000083A0 var_10 = -0x10 .text:000083A0 .text:000083A0 MOV R12, SP .text:000083A4 STMFD SP!, {R11,R12,LR,PC} .text:000083A8 SUB R11, R12, #4 .text:000083AC SUB SP, SP, #0x10 .text:000083B0 STR R0, [R11,#var_10] .text:000083B4 STR R1, [R11,#var_14] .text:000083B8 STR R2, [R11,#var_18] .text:000083BC STR R3, [R11,#var_1C] .text:000083C0 MOV R3, #1 .text:000083C4 MOV R0, R3 .text:000083C8 SUB SP, R11, #0xC .text:000083CC LDMFD SP, {R11,SP,PC} .text:000083CC ; End of function func .text:000083CC .text:000083D0 .text:000083D0 ; =============== S U B R O U T I N E =======================================
參考:
http://blog.chinaunix.net/uid-16459552-id-3364761.html
http://m.blog.csdn.net/blog/u011405813/41899197