我目前正试图从C调用汇编函数获得一些经验。因此,我创建了一个计算所有数组元素总和的小程序。
C代码如下所示:
#include <stdio.h>
#include <stdint.h>
extern int32_t arrsum(int32_t* arr,int32_t length);
int main()
{
int32_t test[] = {1,2,3};
int32_t length = 3;
int32_t sum = arrsum(test,length);
printf("Sum of arr: %d\n",sum);
return 0;
}
汇编函数如下所示:
.text
.global arrsum
arrsum:
pushq %rbp
movq %rsp, %rbp
pushq %rdi
pushq %rcx
movq 24(%rbp),%rcx
#movq 16(%rbp),%rdi
xorq %rax,%rax
start_loop:
addl (%rdi),%eax
addq $4,%rdi
loop start_loop
popq %rcx
popq %rdi
movq %rbp , %rsp
popq %rbp
ret
我假设C遵守调用约定并将所有参数推送到堆栈上。事实上,在第24位(%rbp),我能够找到数组的长度。我期望在16(%rbp)找到指向数组的指针,但我只是找到了0x0。经过一些调试后,我发现C根本没有按下指针,而是将整个指针移动到%rdi寄存器中。
为什么会这样?我无法找到有关此行为的任何信息。
答案 0 :(得分:2)
C编译器将使用的调用约定取决于您的系统,传递给编译器的元数据和标志。听起来您的编译器正在使用此处详述的System V AMD64调用约定:https://en.m.wikipedia.org/wiki/X86_calling_conventions(暗示您在64位x86芯片上使用类Unix操作系统)。基本上,在这个约定中,大多数参数都会进入寄存器,因为它更快,64位x86系统有足够的寄存器来完成这项工作(通常)。
答案 1 :(得分:2)
我假设C遵守调用约定并在堆栈上推送所有参数。
没有“召唤”惯例。通过堆栈传递参数只是一个可能的调用约定(很多)。这种策略通常用于32位系统,但即便如此,它也不是传递参数的唯一方法。
大多数 64位调用约定会传递寄存器中的前4-6个参数,这通常比在堆栈中传递它们更有效。
究竟哪个调用约定在这里起作用是依赖于系统的;你的问题并没有提供很多线索,无论你是使用Windows还是* nix,但是我猜测你正在使用* nix,因为参数是在{{1}中传递的注册。在这种情况下,编译器将遵循System V AMD64 ABI。
在System V AMD64调用约定中,前六个整数大小的参数(也可以是指针)在寄存器rdi
,RDI
,RSI
,{{中传递1}},RDX
和RCX
按此顺序排列。每个寄存器专用于一个参数,因此参数1始终进入R8
,参数2始终进入R9
,依此类推。相反,浮点参数通过向量寄存器RDI
- RSI
传递。其他参数以相反的顺序在堆栈上传递。
x86标记wiki中提供了有关此公约和其他常用调用约定的更多信息。