ARM汇编语言(4) 参数传递

时间:2022-01-18 01:14:05

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中,指令执行前,寄存器中的数据如下:

ARM汇编语言(4) 参数传递

STMFD    r13!,{r3-r6,r14}指令会将R3,R4,R5,R6,R14这5个寄存器的值入栈,因为堆栈是满递减的,目前R13的值为0x08000000,这5个数据入栈后,R13的值应该变为0x08000000 - 20 = 0x07FFFFEC,目前0x07FFFFEC地址开始到0x07FFFFFF地址的内存内容是:

ARM汇编语言(4) 参数传递

指令执行完毕后:

ARM汇编语言(4) 参数传递

地址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中的值变为:

ARM汇编语言(4) 参数传递


然后:

        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

中对参数传递有其规定:

ARM汇编语言(4) 参数传递

因此,我们在调用函数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处执行,执行之前:

ARM汇编语言(4) 参数传递

ARM汇编语言(4) 参数传递

BL指令执行时(仅仅是发生跳转后,未执行sum2中的指令),会将BL之后的下一条指令的地址放到LR中,然后PC中会变成sum2表示的地址:

ARM汇编语言(4) 参数传递

跟我们预测的一致,待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这些指令向内存中存取数据,算不算入栈和出栈?