C语言代码:
#include <stdio.h>
#include <stdlib.h>
int sum2(int param1, int param2)
{
return param1 + param2;
}
int sum5(int param1, int param2, int param3, int param4, int param5)
{
return param1 + param2 + param3 + param4 + param5;
}
int main(int argc, char** argv)
{
int param1 = 100;
int param2 = 200;
int param3 = 300;
int param4 = 400;
int param5 = 500;
int total2 = 0;
int total5 = 0;
total2 = sum2(param1, param2);
total5 = sum5(param1, param2, param3, param4, param5);
return 0;
}
使用ADS 1.2编译代码,然后反汇编,.text段如下:
sum2
$a
.text
0x00000000: e0800001 .... ADD r0,r0,r1
0x00000004: e1a0f00e .... MOV pc,r14
sum5
0x00000008: e59dc000 .... LDR r12,[r13,#0]
0x0000000c: e0800001 .... ADD r0,r0,r1
0x00000010: e0800002 .... ADD r0,r0,r2
0x00000014: e0800003 .... ADD r0,r0,r3
0x00000018: e080000c .... ADD r0,r0,r12
0x0000001c: e1a0f00e .... MOV pc,r14
main
0x00000020: e92d4078 x@-. STMFD r13!,{r3-r6,r14}
0x00000024: e3a04064 d@.. MOV r4,#0x64
0x00000028: e3a050c8 .P.. MOV r5,#0xc8
0x0000002c: e3a02f4b K/.. MOV r2,#0x12c
0x00000030: e3a06f64 do.. MOV r6,#0x190
0x00000034: e3a03f7d }?.. MOV r3,#0x1f4
0x00000038: e1a01005 .... MOV r1,r5
0x0000003c: e1a00004 .... MOV r0,r4
0x00000040: ebfffffe .... BL sum2 ; 0x0
0x00000044: e58d3000 .0.. STR r3,[r13,#0]
0x00000048: e1a03006 .0.. MOV r3,r6
0x0000004c: e1a01005 .... MOV r1,r5
0x00000050: e1a00004 .... MOV r0,r4
0x00000054: ebfffffe .... BL sum5 ; 0x8
0x00000058: e3a00000 .... MOV r0,#0
0x0000005c: e8bd8078 x... LDMFD r13!,{r3-r6,pc}
代码分析:
main中第一条指令:STMFD r13!,{r3-r6,r14}
FD表示:full-descending,参考Procedure Call Standard for the ARM Architecture文档,"Processes, Memory and the Stack"章节中说明:
The stack implementation is full-descending, with the current extent of the stack held in the register SP (r13)
STM指令, 将寄存器中的数据存入内存中,此处FD表示是数据入栈,FD是满递减,此处R13是基址寄存器,包含这次传输的起始地址,感叹号表示最后的地址会回写到R13中,指令执行前,寄存器中的数据如下:
STMFD r13!,{r3-r6,r14}指令会将R3,R4,R5,R6,R14这5个寄存器的值入栈,因为堆栈是满递减的,目前R13的值为0x08000000,这5个数据入栈后,R13的值应该变为0x08000000 - 20 = 0x07FFFFEC,目前0x07FFFFEC地址开始到0x07FFFFFF地址的内存内容是:
指令执行完毕后:
地址0x07FFFFFC - 0x07FFFFFF:0x00008334,即原来R14中的值,
地址0x07FFFFF8 - 0x07FFFFFB:0x00000000,即原来R6中的值,
地址0x07FFFFF4 - 0x07FFFFF7:0x0000832C,即原来R5中的值,
地址0x07FFFFF0 - 0x07FFFFF3:0x0000897C,即原来R4中的值,
地址0x07FFFFEC - 0x07FFFFEF:0x04004480,即原来R3中的值。
可见,寄存器列表中,RX中X值越小,其入栈时对应较低的地址值,X值越大,其入栈时对应较高的地址值。
同时,由于R13后有感叹号,因此指令执行完毕后,地址值也会回写到R13中,此时R13中的值变为:
然后:
0x00000024: e3a04064 d@.. MOV r4,#0x64
0x00000028: e3a050c8 .P.. MOV r5,#0xc8
0x0000002c: e3a02f4b K/.. MOV r2,#0x12c
0x00000030: e3a06f64 do.. MOV r6,#0x190
0x00000034: e3a03f7d }?.. MOV r3,#0x1f4
这些指令对应我们C代码中的赋值语句:
int param1 = 100;
int param2 = 200;
int param3 = 300;
int param4 = 400;
int param5 = 500;
然后,指令:
0x00000038: e1a01005 .... MOV r1,r5
0x0000003c: e1a00004 .... MOV r0,r4
0x00000040: ebfffffe .... BL sum2 ; 0x0
就需要特别注意了,前面的指令中,param1的值放在了寄存器R4中,param2的值放在了寄存器R5中,
C程序中调用sum2函数,需要向其传递参数,Procedure Call Standard for the ARM Architecture
中对参数传递有其规定:
因此,我们在调用函数sum2之前,需要先将参数放在R0,R1这些寄存器中:
0x00000038: e1a01005 .... MOV r1,r5
0x0000003c: e1a00004 .... MOV r0,r4
之前R4存放param1的值,现在放到R0中,R5存放param2的值,现在放到R1中,这样就可以调用sum2程序了:
0x00000040: ebfffffe .... BL sum2 ; 0x0
此处sum2是个标号,表示地址,BL指令同时会将下一条指令的地址复制到LR中,到sum2处执行,执行之前:
BL指令执行时(仅仅是发生跳转后,未执行sum2中的指令),会将BL之后的下一条指令的地址放到LR中,然后PC中会变成sum2表示的地址:
跟我们预测的一致,待sum2中的指令执行完毕后,会执行指令
MOV pc,r14
将PC设置为跳转指令的下一条指令的地址,恢复原来的执行顺序。
这里还有一个问题需要注意,sum2函数是带返回值的,那么它的返回值如何传递回来呢?
通过上面的图片,我们可以看到,寄存器R0和R1负担返回结果的任务,正如指令中:ADD r0,r0,r1
sum2函数调用完毕后,源码中还继续调用了sum5函数,AAPCS中仅定义了4个寄存器用于传递参数,那么当参数的个数多余4个时如何处理呢?
答案是使用堆栈传递参数,因此在调用sum5函数时,就需要使用堆栈传递参数:
0x00000044: e58d3000 .0.. STR r3,[r13,#0]
指令将R3中的值入栈,
0x00000048: e1a03006 .0.. MOV r3,r6
0x0000004c: e1a01005 .... MOV r1,r5
0x00000050: e1a00004 .... MOV r0,r4
这些指令分别将其它的值放到前4个寄存器中,因为R2中的值没有修改过,所以保持赋值时的原值,无需改动
然后BL sum5调用sum5程序:
sum5
0x00000008: e59dc000 .... LDR r12,[r13,#0]
0x0000000c: e0800001 .... ADD r0,r0,r1
0x00000010: e0800002 .... ADD r0,r0,r2
0x00000014: e0800003 .... ADD r0,r0,r3
0x00000018: e080000c .... ADD r0,r0,r12
0x0000001c: e1a0f00e .... MOV pc,r14
sum5程序也比较简单,只是将入栈的值加载到R12中,然后依次ADD指令累加值,然后将R14的值移入PC,然后返回
sum5执行返回后:
0x00000058: e3a00000 .... MOV r0,#0
0x0000005c: e8bd8078 x... LDMFD r13!,{r3-r6,pc}
MOV R0, #0将立即数0放到寄存器R0中,没什么意义,或许是因为没有使用到sum5函数的返回值
LDMFD r13!,{r3-r6,pc}指令,将R13为基址的内存中的值,读取到R3,R4,R5,R6,PC这些寄存器中,与程序开始执行时的STMFD操作
是逆操作,但是需要注意,STMFD存入堆栈的时LR,此处却是将之前存入的LD值放到PC中,也是函数调用的返回操作,看来我们C代码中的
main函数也是被其它函数调用的,此处不过是返回到调用main函数的那个函数中继续执行而已。
问题:
1、STM,LDM这些指令向内存中存取数据,算不算入栈和出栈?