首页
社区
课程
招聘
[原创]ARM函数调用传参规则
2020-10-14 13:21 4904

[原创]ARM函数调用传参规则

2020-10-14 13:21
4904

传递基本类型

1
2
3
4
5
6
7
8
9
10
int func(int a, int b, int c, int d, int e) {
    int v1 = 1;
    int r = a + b + c + d + e + v1;
    return r;
}
int main() {
    int i = 1, j = 2;
    int r = func(i, j, 3, 4, 5);
    printf("%d \n", r);
}

对应main函数的汇编代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
; int __cdecl main(int argc, const char **argv, const char **envp)
 
PUSH            {R11,LR}                    ;保存现场
ADD             R11, SP, #4                 ;新栈的基址 FP(BP)
SUB             SP, SP, #0x18               ;开辟栈空间
MOV             R3, #1                      ;r3 = 1
STR             R3, [R11,#var_8]            ;[r11+var_8] = 1
MOV             R3, #2                      ;r3 = 2
STR             R3, [R11,#var_C]            ;[r11+var_C] = 2
MOV             R3, #5                      ;r3 = 5
STR             R3, [SP,#0x1C+var_1C] ; int ;[sp+0x1c+var_1c] = 5
LDR             R0, [R11,#var_8]            ;r0 = [r11+var_8] = 1   
LDR             R1, [R11,#var_C]            ;r1 = [r11+var_C] = 2
MOV             R2, #3                      ;r2 = 3
MOV             R3, #4                      ;r3 = 4
BL              _Z4funciiiii ; func(int,int,int,int,int)

此时栈及寄存器如下:
图片描述

 

对应func函数的汇编代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
; _DWORD __fastcall func(int, int, int, int, int)
PUSH            {R11}
ADD             R11, SP, #0
SUB             SP, SP, #0x1C
STR             R0, [R11,#var_10]   ; r0 = 1
STR             R1, [R11,#var_14]   ; r1 = 2
STR             R2, [R11,#var_18]   ; r2 = 3
STR             R3, [R11,#var_1C]   ; r3 = 4
MOV             R3, #1
STR             R3, [R11,#var_8]
LDR             R2, [R11,#var_10]
LDR             R3, [R11,#var_14]
ADD             R2, R2, R3 ; 1 + 2
LDR             R3, [R11,#var_18]
ADD             R2, R2, R3 ; 1+2+3
LDR             R3, [R11,#var_1C]
ADD             R2, R2, R3 ; 1+2+3+4
LDR             R3, [R11,#arg_0]    ; 注意这里 r11+4,就是在main中传入的第5个参数
ADD             R2, R2, R3

图片描述

 

结论: 传递基本数据类型,前4个放入寄存器r0-r3中,多于的放入栈上

传递数组

1
2
3
4
5
6
7
8
9
10
void func6(int a[], int len) {
    for (int i = 0; i < len; i++) {
        printf("index: %d , num: %d\n", i, a[i]);
    }
}
int main() {
    int a[] = {0, 1, 2};
    int n = sizeof(a) / sizeof(int);
    func6(a, n);
}

main函数对应的汇编

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
PUSH            {R11,LR}
ADD             R11, SP, #4
SUB             SP, SP, #0x10
LDR             R2, =(dword_20BC - 0x8C8)
ADD             R2, PC, R2 ; dword_20BC     ; r2存储数组首地址
SUB             R3, R11, #-var_14
LDM             R2, {R0-R2}                 ; 把数组[0,1,2]内容存到r0,r1,r2
STM             R3, {R0-R2}                 ; 再存到r3所指向的栈上
MOV             R3, #3
STR             R3, [R11,#var_8]
SUB             R3, R11, #-var_14           ; 获取数组在栈上首地址。数组首地址放到r0中,作为参数传递
MOV             R0, R3  ; int *
LDR             R1, [R11,#var_8] ; int
BL              _Z5func6Pii                 ; func6(int *,int)
MOV             R3, #0
MOV             R0, R3
SUB             SP, R11, #4
POP             {R11,PC}

结论:传递数组 实际上传递的是数组首地址指针,取数组内容通过首地址偏移

传递结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
struct st_a {
    int a;
    int b;
    int c;
    int d;
    int e;
};
void func1(st_a stA) {
    int a = stA.a;
    int b = stA.b;
    int c = stA.c;
    int d = stA.d;
    int e = stA.e;
    printf("%d, %d \n", a, b, c, d, e);
}
int main() {
    st_a stA;
    stA.a = 123;
    stA.b = 234;
    stA.c = 345;
    stA.d = 456;
    stA.e = 567;
    func1(stA);
}

main函数对应汇编

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
PUSH            {R11,LR}
ADD             R11, SP, #4
SUB             SP, SP, #0x20
MOV             R3, #123
STR             R3, [R11,#var_18]       ; 123 放到栈上[r11-0x18]
MOV             R3, #234
STR             R3, [R11,#var_14]       ; 234 放到栈上[r11-0x14]
LDR             R3, =345
STR             R3, [R11,#var_10]       ; 345 放到栈上[r11-0x10]
MOV             R3, #456
STR             R3, [R11,#var_C]        ; 456 放到栈上[r11-0xc]
LDR             R3, =567
STR             R3, [R11,#var_8]        ; 567 放到栈上[r11-0x8]
LDR             R3, [R11,#var_8]
STR             R3, [SP,#0x24+var_24]   ; 567 放到栈上[sp]
SUB             R3, R11, #-var_18
LDM             R3, {R0-R3}             ; 将 123 234 345 456放到寄存器r0,r1,r2,r3
BL              _Z5func14st_a ; func1(st_a)
MOV             R3, #0
MOV             R0, R3
SUB             SP, R11, #4
POP             {R11,PC}

结论:结构体的传递和直接传递参数一样 --> func1(int a, int b, int c, int d, int e)

传递枚举

1
2
3
4
5
6
7
8
9
10
enum en_a {
    p1 = 103,
    p2 = 201
};
void func2(en_a enA) {
    printf("%d \n", enA);
}
int main() {
    func2(p1);
}

main对应汇编代码

1
2
3
4
5
6
7
PUSH            {R11,LR}
ADD             R11, SP, #4
MOV             R0, #103
BL              _Z5func24en_a ; func2(en_a)
MOV             R3, #0
MOV             R0, R3
POP             {R11,PC}

结论:传递枚举,直接传递的值 类似于 func2(int a)

传递对象地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Person {
public:
    Person() {
        printf("调用\n");
    }
 
    int get() {
        return this->a;
    }
 
    int getb() {
        return this->b;
    }
 
    void set(int pa, int pb) {
        this->a = pa;
        this->b = pb;
    }
 
private:
    int a;
    int b;
};
void func3(Person *person) {
    printf("%d \n", person->get());
}
int main() {
    Person person;
    person.set(102, 222);
    func3(&person);
}

main函数对应汇编

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
PUSH            {R11,LR}
ADD             R11, SP, #4
SUB             SP, SP, #8
SUB             R3, R11, #-var_C
MOV             R0, R3              ; this  分配栈上地址,调用构造函数
BL              _ZN6PersonC2Ev      ; Person::Person(void)
SUB             R3, R11, #-var_C
MOV             R0, R3  ; this
MOV             R1, #102 ; int
MOV             R2, #222 ; int
BL              _ZN6Person3setEii   ; 调用函数的时候,会多传一个参数,为对象分配的地址  Person::set(int,int)
SUB             R3, R11, #-var_C
MOV             R0, R3              ; 传递的是栈上地址 Person *
BL              _Z5func3P6Person    ; func3(Person *)
MOV             R3, #0
MOV             R0, R3
SUB             SP, R11, #4
POP             {R11,PC}

结论:传递对象指针 传递过来的是地址

传递对象引用

1
2
3
4
5
6
void func4(Person &person) {
    printf("%d \n", person.get());
}
int main() {
    func4(person);
}

main函数汇编,和传递对象地址是一样的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
PUSH            {R11,LR}
ADD             R11, SP, #4
SUB             SP, SP, #8
SUB             R3, R11, #-var_C
MOV             R0, R3              ; this
BL              _ZN6PersonC2Ev      ; Person::Person(void)
SUB             R3, R11, #-var_C
MOV             R0, R3  ; this
MOV             R1, #102 ; int
MOV             R2, #222 ; int
BL              _ZN6Person3setEii   ; Person::set(int,int)
SUB             R3, R11, #-var_C
MOV             R0, R3  ; Person *
BL              _Z5func4R6Person    ; func4(Person &)
MOV             R3, #0
MOV             R0, R3
SUB             SP, R11, #4
POP             {R11,PC}

传递对象

1
2
3
4
5
6
7
void func5(Person person) {
    printf("a: %d ,b: %d\n", person.get(), person.getb());
    person.set(222, 333);
}
int main() {
    func5(person);
}

main函数的汇编代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
PUSH            {R11,LR}
ADD             R11, SP, #4
SUB             SP, SP, #8
SUB             R3, R11, #-var_C
MOV             R0, R3              ; this
BL              _ZN6PersonC2Ev      ; Person::Person(void)
SUB             R3, R11, #-var_C
MOV             R0, R3  ; this
MOV             R1, #102 ; int
MOV             R2, #222 ; int
BL              _ZN6Person3setEii   ; Person::set(int,int)
SUB             R3, R11, #-var_C
LDM             R3, {R0,R1}         ; 将对象的属性赋值给r1和r2
BL              _Z5func56Person     ; func5(Person)
MOV             R3, #0
MOV             R0, R3
SUB             SP, R11, #4
POP             {R11,PC}

func5对应的部分汇编代码

1
2
3
4
5
6
7
8
PUSH            {R4,R11,LR}
ADD             R11, SP, #8
SUB             SP, SP, #0xC
SUB             R3, R11, #-var_14
STM             R3, {R0,R1}         ; 重新为对象分配空间并赋值
SUB             R3, R11, #-var_14
MOV             R0, R3  ; this
BL              _ZN6Person3getEv    ; Person::get(void)

结论:传递对象 传递过来的是成员变量值,在被调用方再分配空间。 c++对象内存模型中只有成员变量占对象内存空间


[培训]内核驱动高级班,冲击BAT一流互联网大厂工 作,每周日13:00-18:00直播授课

最后于 2020-10-14 19:09 被lanoche编辑 ,原因: 修改图片错误
收藏
点赞2
打赏
分享
最新回复 (2)
雪    币: 1438
活跃值: (1521)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
奋斗der小鸟 2020-10-14 17:04
2
0
感觉main函数栈 图,的r11 和 lr的顺序不大对
雪    币: 1394
活跃值: (3089)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
lanoche 2020-10-14 19:07
3
0
奋斗der小鸟 感觉main函数栈 图,的r11 和 lr的顺序不大对
是的,写反了
游客
登录 | 注册 方可回帖
返回