直接看代码
int get_gata3(int* da1,int* da2,int *da3){
*da1=1;
*da2=2;
*da3=3;
return 0;
}
int get_gata2(int* da1,int* da2){
*da1=1;
*da2=2;
return 0;
}
int main(){
// sum(1,2);
int (*get_data)(int*,int*,int*);
get_data=(int (*)(int *, int *, int *))get_gata2;
int a=3,b=4,c=5;
get_data(&a,&b,&c);
}
首先这里是通过函数进行对a b c三个变量重新赋值,正常的只传递参数也是相同的效果。示例中给函数get_gata2传递了三个参数,但实际上这个函数只有两个参数。接下来看汇编代码
.arch armv8-a
.file "main.c"
.text
.align 2
.global get_gata3
.type get_gata3, %function
get_gata3:
sub sp, sp, #32
str x0, [sp, 24]
str x1, [sp, 16]
str x2, [sp, 8]
ldr x0, [sp, 24]
mov w1, 1
str w1, [x0]
ldr x0, [sp, 16]
mov w1, 2
str w1, [x0]
ldr x0, [sp, 8]
mov w1, 3
str w1, [x0]
mov w0, 0
add sp, sp, 32
ret
.size get_gata3, .-get_gata3
.align 2
.global get_gata2
.type get_gata2, %function
get_gata2:
sub sp, sp, #16
str x0, [sp, 8]
str x1, [sp]
ldr x0, [sp, 8]
mov w1, 1
str w1, [x0]
ldr x0, [sp]
mov w1, 2
str w1, [x0]
mov w0, 0
add sp, sp, 16
ret
.size get_gata2, .-get_gata2
.align 2
.global main
.type main, %function
main:
stp x29, x30, [sp, -48]!
add x29, sp, 0
adrp x0, :got:__stack_chk_guard
ldr x0, [x0, #:got_lo12:__stack_chk_guard]
ldr x1, [x0]
str x1, [x29, 40]
mov x1,0
adrp x0, get_gata2
add x0, x0, :lo12:get_gata2
str x0, [x29, 32]
mov w0, 3
str w0, [x29, 20]
mov w0, 4
str w0, [x29, 24]
mov w0, 5
str w0, [x29, 28]
add x2, x29, 28
add x1, x29, 24
add x0, x29, 20
ldr x3, [x29, 32]
blr x3
mov w0, 0
adrp x1, :got:__stack_chk_guard
ldr x1, [x1, #:got_lo12:__stack_chk_guard]
ldr x2, [x29, 40]
ldr x1, [x1]
eor x1, x2, x1
cmp x1, 0
beq .L7
bl __stack_chk_fail
.L7:
ldp x29, x30, [sp], 48
ret
.size main, .-main
.ident "GCC: (Ubuntu/Linaro 7.5.0-3ubuntu1~18.04) 7.5.0"
.section .note.GNU-stack,"",@progbits
我把其中重要的部分提取出来解释,首先主函数部分
adrp x0, get_gata2
add x0, x0, :lo12:get_gata2
str x0, [x29, 32]
此段将函数get_gata2的地址放如栈中偏移40的地址。
mov w0, 3
str w0, [x29, 20]
mov w0, 4
str w0, [x29, 24]
mov w0, 5
str w0, [x29, 28]
add x2, x29, 28
add x1, x29, 24
add x0, x29, 20
ldr x3, [x29, 32]
blr x3
此段[x29, 20] [x29, 24] [x29, 28]三个分别是三个局部变量a,b,c在栈上的地址,这一步就是给局部变量赋初始值。
接下来
add x2, x29, 28
add x1, x29, 24
add x0, x29, 20
将局部变量的地址给x0 x1 x2,函数传递参数较少的情况下是使用寄存器传值的。这三个寄存器的值会在函数中使用
然后是
ldr x3, [x29, 32]
blr x3
拿到函数的地址,也就是get_gata2的地址然后跳转过去。
接下来看函数内部
get_gata2:
sub sp, sp, #16
str x0, [sp, 8]
str x1, [sp]
ldr x0, [sp, 8]
mov w1, 1
str w1, [x0]
ldr x0, [sp]
mov w1, 2
str w1, [x0]
mov w0, 0
add sp, sp, 16
ret
.size get_gata2, .-get_gata2
.align 2
.global main
.type main, %function
注意前三行,这里就是C语言传递参数为什么要指针才能把值带出来的原因了,
参数在传递过程中首先在栈上开了内存
然后将x0(a的地址) 存储在栈上 位置[sp, 8]
将x1(b的地址) 存储在栈上 位置[sp]
然后因为没有第三个参数,所有寄存器x2的地址被废弃,也不会被保存。
mov w0, 0 是返回值。
所以由此看来,可以多传参数(会被丢弃),但是不能少穿,少穿会导致不可预知的后果,因为多余参数的数值我们并不能知道,
就比如get_gata2只传递一个参数,那么在运行时 x1的值就不能确定,赋值时就会报错。