为什么C在调用汇编函数时不会在堆栈上按下指针?

时间:2017-01-08 17:54:42

标签: c pointers assembly x86 x86-64

我目前正试图从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寄存器中。

为什么会这样?我无法找到有关此行为的任何信息。

2 个答案:

答案 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调用约定中,前六个整数大小的参数(也可以是指针)在寄存器rdiRDIRSI,{{中传递1}},RDXRCX按此顺序排列。每个寄存器专用于一个参数,因此参数1始终进入R8,参数2始终进入R9,依此类推。相反,浮点参数通过向量寄存器RDI - RSI传递。其他参数以相反的顺序在堆栈上传递。

标记wiki中提供了有关此公约和其他常用调用约定的更多信息。