x86、arm、mips架构栈空间分布情况分析
一、准备工作
1、相关概念
一个函数如果不再调用其他的函数,那么这个函数是叶子函数,一个函数如果调用其他的函数,那么这个函数是非叶子函数。一般来说,函数都是非叶子函数。
2、示例代码
使用不同平台的gcc进行源码编译以下代码,均不开启Stack-canary保护、栈执行保护(-fno-stack-protector -no-pie)。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int funcB(char *A, char *B,char *C, char *D,char *E, char *F)
{
char a[4]={'1'};
char b[4]={'2'};
A[0] = 'a';
B[0] = 'b';
C[0] = 'c';
D[0] = 'd';
E[0] = 'e';
F[0] = 'f';
return 0;
}
int funcA()
{
char A[4] = {'A', 'A', 'A', 'A'};
char B[4] = {'B', 'B', 'B', 'B'};
char C[4] = {'C', 'C', 'C', 'C'};
char D[4] = {'D', 'D', 'D', 'D'};
char E[4] = {'E', 'E', 'E', 'E'};
char F[4] = {'F', 'F', 'F', 'F'};
funcB(A,B,C,D,E,F);
return 0;
}
int main(int argc, char* argv[])
{
funcA();
return 0;
}
二、x86架构
1、汇编代码
.text:08049176 ; =============== S U B R O U T I N E =======================================
.text:08049176
.text:08049176 ; Attributes: bp-based frame
.text:08049176
.text:08049176 ; int __cdecl funcB(char *A, char *B, char *C, char *D, char *E, char *F)
.text:08049176 public funcB
.text:08049176 funcB proc near ; CODE XREF: funcA+56↓p
.text:08049176
.text:08049176 b = byte ptr -8
.text:08049176 a = byte ptr -4
.text:08049176 A = dword ptr 8
.text:08049176 B = dword ptr 0Ch
.text:08049176 C = dword ptr 10h
.text:08049176 D = dword ptr 14h
.text:08049176 E = dword ptr 18h
.text:08049176 F = dword ptr 1Ch
.text:08049176
.text:08049176 ; __unwind {
.text:08049176 endbr32
.text:0804917A push ebp
.text:0804917B mov ebp, esp
.text:0804917D sub esp, 10h
.text:08049180 call __x86_get_pc_thunk_ax
.text:08049185 add eax, (offset _GLOBAL_OFFSET_TABLE_ - $)
.text:0804918A mov dword ptr [ebp+a], 31h ; '1'
.text:08049191 mov dword ptr [ebp+b], 32h ; '2'
.text:08049198 mov eax, [ebp+A]
.text:0804919B mov byte ptr ds:(_GLOBAL_OFFSET_TABLE_ - 804C000h)[eax], 61h ; 'a'
.text:0804919E mov eax, [ebp+B]
.text:080491A1 mov byte ptr ds:(_GLOBAL_OFFSET_TABLE_ - 804C000h)[eax], 62h ; 'b'
.text:080491A4 mov eax, [ebp+C]
.text:080491A7 mov byte ptr ds:(_GLOBAL_OFFSET_TABLE_ - 804C000h)[eax], 63h ; 'c'
.text:080491AA mov eax, [ebp+D]
.text:080491AD mov byte ptr ds:(_GLOBAL_OFFSET_TABLE_ - 804C000h)[eax], 64h ; 'd'
.text:080491B0 mov eax, [ebp+E]
.text:080491B3 mov byte ptr ds:(_GLOBAL_OFFSET_TABLE_ - 804C000h)[eax], 65h ; 'e'
.text:080491B6 mov eax, [ebp+F]
.text:080491B9 mov byte ptr ds:(_GLOBAL_OFFSET_TABLE_ - 804C000h)[eax], 66h ; 'f'
.text:080491BC mov eax, 0
.text:080491C1 leave
.text:080491C2 retn
.text:080491C2 ; } // starts at 8049176
.text:080491C2 funcB endp
.text:080491C2
.text:080491C3
.text:080491C3 ; =============== S U B R O U T I N E =======================================
.text:080491C3
.text:080491C3 ; Attributes: bp-based frame
.text:080491C3
.text:080491C3 ; int funcA()
.text:080491C3 public funcA
.text:080491C3 funcA proc near ; CODE XREF: main+11↓p
.text:080491C3
.text:080491C3 F = byte ptr -18h
.text:080491C3 E = byte ptr -14h
.text:080491C3 D = byte ptr -10h
.text:080491C3 C = byte ptr -0Ch
.text:080491C3 B = byte ptr -8
.text:080491C3 A = byte ptr -4
.text:080491C3
.text:080491C3 ; __unwind {
.text:080491C3 endbr32
.text:080491C7 push ebp
.text:080491C8 mov ebp, esp
.text:080491CA sub esp, 20h
.text:080491CD call __x86_get_pc_thunk_ax
.text:080491D2 add eax, (offset _GLOBAL_OFFSET_TABLE_ - $)
.text:080491D7 mov dword ptr [ebp+A], 41414141h
.text:080491DE mov dword ptr [ebp+B], 42424242h
.text:080491E5 mov dword ptr [ebp+C], 43434343h
.text:080491EC mov dword ptr [ebp+D], 44444444h
.text:080491F3 mov dword ptr [ebp+E], 45454545h
.text:080491FA mov dword ptr [ebp+F], 46464646h
.text:08049201 lea eax, [ebp+F]
.text:08049204 push eax ; F
.text:08049205 lea eax, [ebp+E]
.text:08049208 push eax ; E
.text:08049209 lea eax, [ebp+D]
.text:0804920C push eax ; D
.text:0804920D lea eax, [ebp+C]
.text:08049210 push eax ; C
.text:08049211 lea eax, [ebp+B]
.text:08049214 push eax ; B
.text:08049215 lea eax, [ebp+A]
.text:08049218 push eax ; A
.text:08049219 call funcB
.text:0804921E add esp, 18h
.text:08049221 mov eax, 0
.text:08049226 leave
.text:08049227 retn
.text:08049227 ; } // starts at 80491C3
.text:08049227 funcA endp
.text:08049227
.text:08049228
.text:08049228 ; =============== S U B R O U T I N E =======================================
.text:08049228
.text:08049228 ; Attributes: bp-based frame
.text:08049228
.text:08049228 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:08049228 public main
.text:08049228 main proc near ; DATA XREF: _start+2A↑o
.text:08049228
.text:08049228 argc = dword ptr 8
.text:08049228 argv = dword ptr 0Ch
.text:08049228 envp = dword ptr 10h
.text:08049228
.text:08049228 ; __unwind {
.text:08049228 endbr32
.text:0804922C push ebp
.text:0804922D mov ebp, esp
.text:0804922F call __x86_get_pc_thunk_ax
.text:08049234 add eax, (offset _GLOBAL_OFFSET_TABLE_ - $)
.text:08049239 call funcA
.text:0804923E mov eax, 0
.text:08049243 pop ebp
.text:08049244 retn
.text:08049244 ; } // starts at 8049228
.text:08049244 main endp
.text:08049244
.text:08049245
.text:08049245 ; =============== S U B R O U T I N E =======================================
2、栈分布
函数名 | 形参 | 局部参数 | 申请栈空间大小 |
main | 3个dword值=12字节 | 0个 | 0字节 |
funcA | 0个 | 6个dword值=20字节 | 32字节 |
funcB | 6个dword值=24字节 | 2个dword值=8字节 | 16字节 |
main->funcA->funcB时,即执行到0x000011F8(retn之前),此时栈分布如下所示:
3、传参方式
从右至左,依次压栈(F->E->D->C->B->A),call函数后,将返回地址压栈。
三、arm架构
1、汇编代码
.text:000083E4 ; =============== S U B R O U T I N E =======================================
.text:000083E4
.text:000083E4 ; Attributes: bp-based frame
.text:000083E4
.text:000083E4 ; int funcA()
.text:000083E4 EXPORT funcA
.text:000083E4 funcA ; CODE XREF: main+18↓p
.text:000083E4
.text:000083E4 var_2C = -0x2C
.text:000083E4 var_28 = -0x28
.text:000083E4 F = -0x24
.text:000083E4 E = -0x20
.text:000083E4 D = -0x1C
.text:000083E4 C = -0x18
.text:000083E4 B = -0x14
.text:000083E4 A = -0x10
.text:000083E4
.text:000083E4 MOV R12, SP
.text:000083E8 PUSH {R11,R12,LR,PC}
.text:000083EC SUB R11, R12, #4
.text:000083F0 SUB SP, SP, #0x20
.text:000083F4 SUB R2, R11, #-A
.text:000083F8 MOV R3, #0x4141
.text:00008400 ORR R3, R3, R3,LSL#16
.text:00008404 STR R3, [R2]
.text:00008408 SUB R2, R11, #-B
.text:0000840C MOV R3, #0x4242
.text:00008414 ORR R3, R3, R3,LSL#16
.text:00008418 STR R3, [R2]
.text:0000841C SUB R2, R11, #-C
.text:00008420 MOV R3, #0x4343
.text:00008428 ORR R3, R3, R3,LSL#16
.text:0000842C STR R3, [R2]
.text:00008430 SUB R2, R11, #-D
.text:00008434 MOV R3, #0x4444
.text:0000843C ORR R3, R3, R3,LSL#16
.text:00008440 STR R3, [R2]
.text:00008444 SUB R2, R11, #-E
.text:00008448 MOV R3, #0x4545
.text:00008450 ORR R3, R3, R3,LSL#16
.text:00008454 STR R3, [R2]
.text:00008458 SUB R2, R11, #-F
.text:0000845C MOV R3, #0x4646
.text:00008464 ORR R3, R3, R3,LSL#16
.text:00008468 STR R3, [R2]
.text:0000846C SUB R2, R11, #-A
.text:00008470 SUB R1, R11, #-B ; B
.text:00008474 SUB R12, R11, #-C
.text:00008478 SUB LR, R11, #-D
.text:0000847C SUB R3, R11, #-E
.text:00008480 STR R3, [SP,#0x2C+var_2C] ; E
.text:00008484 SUB R3, R11, #-F
.text:00008488 STR R3, [SP,#0x2C+var_28] ; F
.text:0000848C MOV R0, R2 ; A
.text:00008490 MOV R2, R12 ; C
.text:00008494 MOV R3, LR ; D
.text:00008498 BL funcB
.text:0000849C MOV R3, #0
.text:000084A0 MOV R0, R3
.text:000084A4 SUB SP, R11, #0xC
.text:000084A8 LDMFD SP, {R11,SP,PC}
.text:000084A8 ; End of function funcA
.text:000084A8
.text:000084AC
.text:000084AC ; =============== S U B R O U T I N E =======================================
.text:000084AC
.text:000084AC ; Attributes: bp-based frame
.text:000084AC
.text:000084AC ; int __cdecl funcB(unsigned __int8 *A, unsigned __int8 *B, unsigned __int8 *C, unsigned __int8 *D, unsigned __int8 *E, unsigned __int8 *F)
.text:000084AC EXPORT funcB
.text:000084AC funcB ; CODE XREF: funcA+B4↑p
.text:000084AC
.text:000084AC D = -0x24
.text:000084AC C = -0x20
.text:000084AC B = -0x1C
.text:000084AC A = -0x18
.text:000084AC b = -0x14
.text:000084AC a = -0x10
.text:000084AC E = 4
.text:000084AC F = 8
.text:000084AC
.text:000084AC MOV R12, SP
.text:000084B0 PUSH {R11,R12,LR,PC}
.text:000084B4 SUB R11, R12, #4
.text:000084B8 SUB SP, SP, #0x18
.text:000084BC STR R0, [R11,#A]
.text:000084C0 STR R1, [R11,#B]
.text:000084C4 STR R2, [R11,#C]
.text:000084C8 STR R3, [R11,#D]
.text:000084CC LDR R2, [R11,#A]
.text:000084D0 MOV R3, #0x61 ; 'a'
.text:000084D4 STRB R3, [R2]
.text:000084D8 LDR R2, [R11,#B]
.text:000084DC MOV R3, #0x62 ; 'b'
.text:000084E0 STRB R3, [R2]
.text:000084E4 LDR R2, [R11,#C]
.text:000084E8 MOV R3, #0x63 ; 'c'
.text:000084EC STRB R3, [R2]
.text:000084F0 LDR R2, [R11,#D]
.text:000084F4 MOV R3, #0x64 ; 'd'
.text:000084F8 STRB R3, [R2]
.text:000084FC LDR R2, [R11,#E]
.text:00008500 MOV R3, #0x65 ; 'e'
.text:00008504 STRB R3, [R2]
.text:00008508 LDR R2, [R11,#F]
.text:0000850C MOV R3, #0x66 ; 'f'
.text:00008510 STRB R3, [R2]
.text:00008514 MOV R3, #0x31 ; '1'
.text:00008518 STRB R3, [R11,#a]
.text:0000851C MOV R3, #0x32 ; '2'
.text:00008520 STRB R3, [R11,#b]
.text:00008524 MOV R3, #0
.text:00008528 MOV R0, R3
.text:0000852C SUB SP, R11, #0xC
.text:00008530 LDMFD SP, {R11,SP,PC}
.text:00008530 ; End of function funcB
.text:00008530
.text:00008534
.text:00008534 ; =============== S U B R O U T I N E =======================================
.text:00008534
.text:00008534 ; Attributes: bp-based frame
.text:00008534
.text:00008534 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:00008534 EXPORT main
.text:00008534 main ; DATA XREF: _start+20↑o
.text:00008534 ; .text:off_83DC↑o
.text:00008534
.text:00008534 argv = -0x14
.text:00008534 argc = -0x10
.text:00008534
.text:00008534 MOV R12, SP
.text:00008538 PUSH {R11,R12,LR,PC}
.text:0000853C SUB R11, R12, #4
.text:00008540 SUB SP, SP, #8
.text:00008544 STR R0, [R11,#argc]
.text:00008548 STR R1, [R11,#argv]
.text:0000854C BL funcA
.text:00008550 MOV R3, #0
.text:00008554 MOV R0, R3
.text:00008558 SUB SP, R11, #0xC
.text:0000855C LDMFD SP, {R11,SP,PC}
.text:0000855C ; End of function main
.text:0000855C
.text:00008560
.text:00008560 ; =============== S U B R O U T I N E =======================================
2、栈分布
函数名 | 形参 | 局部参数 | 申请栈空间大小 |
main | 2个dword值=8字节 | 0个 | 8字节 |
funcA | 0个 | 6个dword值=20字节 | 32字节 |
funcB | 6个dword值=24字节 | 2个dword值=8字节 | 16字节 |
main->funcA->funcB时,即执行到0x000084A0(函数末尾前),此时栈分布如下所示:
3、传参方式
前四个参数,R0->R1->R2->R3寄存器:A->B->C->D,剩余的参数从右至左,依次压栈(F->E),BL函数后,将返回地址存于LR寄存器。
四、mips架构
1、汇编代码
.text:00400710 # =============== S U B R O U T I N E =======================================
.text:00400710
.text:00400710 # Attributes: bp-based frame fpd=0x10
.text:00400710
.text:00400710 # int __cdecl funcB(char *A, char *B, char *C, char *D, char *E, char *F)
.text:00400710 .globl funcB
.text:00400710 funcB: # CODE XREF: funcA+C8↓p
.text:00400710 # DATA XREF: LOAD:004003A4↑o ...
.text:00400710
.text:00400710 a = -8
.text:00400710 b = -4
.text:00400710 var_s0 = 0
.text:00400710 A = 8
.text:00400710 B = 0xC
.text:00400710 C = 0x10
.text:00400710 D = 0x14
.text:00400710 E = 0x18
.text:00400710 F = 0x1C
.text:00400710
.text:00400710 addiu $sp, -0x18
.text:00400714 sw $fp, 0x10+var_s0($sp)
.text:00400718 move $fp, $sp
.text:0040071C sw $a0, 0x10+A($fp)
.text:00400720 sw $a1, 0x10+B($fp)
.text:00400724 sw $a2, 0x10+C($fp)
.text:00400728 sw $a3, 0x10+D($fp)
.text:0040072C sw $zero, 0x10+a($fp)
.text:00400730 li $v0, 0x31 # '1'
.text:00400734 sb $v0, 0x10+a($fp)
.text:00400738 sw $zero, 0x10+b($fp)
.text:0040073C li $v0, 0x32 # '2'
.text:00400740 sb $v0, 0x10+b($fp)
.text:00400744 lw $v1, 0x10+A($fp)
.text:00400748 li $v0, 0x61 # 'a'
.text:0040074C sb $v0, 0($v1)
.text:00400750 lw $v1, 0x10+B($fp)
.text:00400754 li $v0, 0x62 # 'b'
.text:00400758 sb $v0, 0($v1)
.text:0040075C lw $v1, 0x10+C($fp)
.text:00400760 li $v0, 0x63 # 'c'
.text:00400764 sb $v0, 0($v1)
.text:00400768 lw $v1, 0x10+D($fp)
.text:0040076C li $v0, 0x64 # 'd'
.text:00400770 sb $v0, 0($v1)
.text:00400774 lw $v1, 0x10+E($fp)
.text:00400778 li $v0, 0x65 # 'e'
.text:0040077C sb $v0, 0($v1)
.text:00400780 lw $v1, 0x10+F($fp)
.text:00400784 li $v0, 0x66 # 'f'
.text:00400788 sb $v0, 0($v1)
.text:0040078C move $v0, $zero
.text:00400790 move $sp, $fp
.text:00400794 lw $fp, 0x10+var_s0($sp)
.text:00400798 addiu $sp, 0x18
.text:0040079C jr $ra
.text:004007A0 nop
.text:004007A0 # End of function funcB
.text:004007A0
.text:004007A4
.text:004007A4 # =============== S U B R O U T I N E =======================================
.text:004007A4
.text:004007A4 # Attributes: bp-based frame fpd=0x38
.text:004007A4
.text:004007A4 # int funcA()
.text:004007A4 .globl funcA
.text:004007A4 funcA: # CODE XREF: main+30↓p
.text:004007A4 # DATA XREF: LOAD:00400384↑o ...
.text:004007A4
.text:004007A4 var_28 = -0x28
.text:004007A4 var_24 = -0x24
.text:004007A4 var_20 = -0x20
.text:004007A4 A = -0x18
.text:004007A4 B = -0x14
.text:004007A4 C = -0x10
.text:004007A4 D = -0xC
.text:004007A4 E = -8
.text:004007A4 F = -4
.text:004007A4 var_s0 = 0
.text:004007A4 var_s4 = 4
.text:004007A4
.text:004007A4 li $gp, (_GLOBAL_OFFSET_TABLE_+0x7FF0 - .)
.text:004007AC addu $gp, $t9
.text:004007B0 addiu $sp, -0x40
.text:004007B4 sw $ra, 0x38+var_s4($sp)
.text:004007B8 sw $fp, 0x38+var_s0($sp)
.text:004007BC move $fp, $sp
.text:004007C0 sw $gp, 0x38+var_20($sp)
.text:004007C4 li $v0, 0x400000
.text:004007C8 nop
.text:004007CC lw $v0, (C_0_2411 - 0x400000)($v0)
.text:004007D0 nop
.text:004007D4 sw $v0, 0x38+A($fp)
.text:004007D8 li $v0, 0x400000
.text:004007DC nop
.text:004007E0 lw $v0, (C_1_2412 - 0x400000)($v0)
.text:004007E4 nop
.text:004007E8 sw $v0, 0x38+B($fp)
.text:004007EC li $v0, 0x400000
.text:004007F0 nop
.text:004007F4 lw $v0, (C_2_2413 - 0x400000)($v0)
.text:004007F8 nop
.text:004007FC sw $v0, 0x38+C($fp)
.text:00400800 li $v0, 0x400000
.text:00400804 nop
.text:00400808 lw $v0, (C_3_2414 - 0x400000)($v0)
.text:0040080C nop
.text:00400810 sw $v0, 0x38+D($fp)
.text:00400814 li $v0, 0x400000
.text:00400818 nop
.text:0040081C lw $v0, (C_4_2415 - 0x400000)($v0)
.text:00400820 nop
.text:00400824 sw $v0, 0x38+E($fp)
.text:00400828 li $v0, 0x400000
.text:0040082C nop
.text:00400830 lw $v0, (C_5_2416 - 0x400000)($v0)
.text:00400834 nop
.text:00400838 sw $v0, 0x38+F($fp)
.text:0040083C addiu $v1, $fp, 0x38+B
.text:00400840 addiu $a2, $fp, 0x38+C # C
.text:00400844 addiu $a3, $fp, 0x38+D # D
.text:00400848 addiu $v0, $fp, 0x38+E
.text:0040084C sw $v0, 0x38+var_28($sp) # E
.text:00400850 addiu $v0, $fp, 0x38+F
.text:00400854 sw $v0, 0x38+var_24($sp) # F
.text:00400858 addiu $v0, $fp, 0x38+A
.text:0040085C move $a0, $v0 # A
.text:00400860 move $a1, $v1 # B
.text:00400864 la $t9, funcB
.text:00400868 nop
.text:0040086C jalr $t9 ; funcB
.text:00400870 nop
.text:00400874 lw $gp, 0x38+var_20($fp)
.text:00400878 move $v0, $zero
.text:0040087C move $sp, $fp
.text:00400880 lw $ra, 0x38+var_s4($sp)
.text:00400884 lw $fp, 0x38+var_s0($sp)
.text:00400888 addiu $sp, 0x40
.text:0040088C jr $ra
.text:00400890 nop
.text:00400890 # End of function funcA
.text:00400890
.text:00400894
.text:00400894 # =============== S U B R O U T I N E =======================================
.text:00400894
.text:00400894 # Attributes: bp-based frame fpd=0x18
.text:00400894
.text:00400894 # int __cdecl main(int argc, const char **argv, const char **envp)
.text:00400894 .globl main
.text:00400894 main: # DATA XREF: LOAD:004003B4↑o
.text:00400894 # __start+1C↑o ...
.text:00400894
.text:00400894 var_8 = -8
.text:00400894 var_s0 = 0
.text:00400894 var_s4 = 4
.text:00400894 argc = 8
.text:00400894 argv = 0xC
.text:00400894
.text:00400894 li $gp, (_GLOBAL_OFFSET_TABLE_+0x7FF0 - .)
.text:0040089C addu $gp, $t9
.text:004008A0 addiu $sp, -0x20
.text:004008A4 sw $ra, 0x18+var_s4($sp)
.text:004008A8 sw $fp, 0x18+var_s0($sp)
.text:004008AC move $fp, $sp
.text:004008B0 sw $gp, 0x18+var_8($sp)
.text:004008B4 sw $a0, 0x18+argc($fp)
.text:004008B8 sw $a1, 0x18+argv($fp)
.text:004008BC la $t9, funcA
.text:004008C0 nop
.text:004008C4 jalr $t9 ; funcA
.text:004008C8 nop
.text:004008CC lw $gp, 0x18+var_8($fp)
.text:004008D0 move $v0, $zero
.text:004008D4 move $sp, $fp
.text:004008D8 lw $ra, 0x18+var_s4($sp)
.text:004008DC lw $fp, 0x18+var_s0($sp)
.text:004008E0 addiu $sp, 0x20
.text:004008E4 jr $ra
.text:004008E8 nop
.text:004008E8 # End of function main
.text:004008E8
.text:004008E8 # ---------------------------------------------------------------------------
.text:004008EC .align 4
.text:004008F0
.text:004008F0 # =============== S U B R O U T I N E =======================================
2、栈分布
函数名 | 形参 | 局部参数 | 申请栈空间大小 |
main | 2个dword值=8字节 | 0个 | 32字节 |
funcA | 0个 | 6个dword值=20字节 | 64字节 |
funcB | 6个dword值=24字节 | 2个dword值=8字节 | 24字节 |
main->funcA->funcB时,即执行到0x00400798(函数末尾前),此时栈分布如下所示:
3、传参方式
前四个参数,a0->a1->a2->a3寄存器:A->B->C->D,剩余的参数从右至左,依次压栈(F->E),jalr函数后,将返回地址存于ra寄存器。
五、总结
架构 | x86 | arm | mips |
运行到funB末尾前消耗栈空间 | 0x6C 字节 | 0x70 字节 | 0x80 字节 |
传参方式 | 从左至右压栈,其次返回地址压栈 | 前4个参数从左至右存入R0-R3寄存器,剩余参数从右至左压栈,其次返回地址存入LR寄存器 | 前4个参数从左至右存入a0-a3寄存器,剩余参数从右至左压栈,其次返回地址存入ra寄存器 |
栈操作 | 使用push、pop的方式 | 使用push、pop的方式 | 内存地址操作 |
函数返回方式 | 调用call的时候返回地址存于栈 call funA ... retn | 返回地址在LR PUSH {R11,R12,LR,PC} ... LDMFD SP, {R11,SP,PC} | 叶子函数(funA)和非叶子函数(funB)在存放返回地址的时候,存在差异。 叶子函数只把返回地址保存在$ra寄存其中,结束函数调用的时候,通过jr $ra指令返回即可。 非叶子函数把在函数调用初始把$ra寄存器中的返回地址保存在栈中,然后结束函数调用的时候将栈中保存的返回地址加载到$ra寄存器中,再通过jr $ra指令返回。 |