【问题标题】:Why does C not push a pointer on the stack when calling a assembly function?为什么C在调用汇编函数时不将指针压入堆栈?
【发布时间】:2017-01-08 17:54:42
【问题描述】:

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

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

【问题讨论】:

  • 如果您使用 gcc 或 clang,您可以在编译 C 文件时传递“-S”,它将转储出它生成的程序集。这可能会帮助您调试 C 代码正在执行的操作。

标签: c pointers assembly x86 x86-64


【解决方案1】:

C 编译器将使用的调用约定取决于您的系统、传递给编译器的元数据和标志。听起来您的编译器正在使用此处详述的 System V AMD64 调用约定:https://en.m.wikipedia.org/wiki/X86_calling_conventions(暗示您在 64 位 x86 芯片上使用类 Unix 操作系统)。基本上,在这个约定中,大多数参数都进入寄存器,因为它更快,并且 64 位 x86 系统有足够的寄存器来完成这项工作(通常)。

【讨论】:

    【解决方案2】:

    我假设 C 遵循调用约定并将所有参数压入堆栈。

    没有“the”调用约定。通过堆栈传递参数只是一个可能的调用约定(许多)。这种策略在 32 位系统上很常用,但即便如此,它也不是传递参数的唯一方式。

    大多数 64 位调用约定在寄存器中传递前 4-6 个参数,这通常比在堆栈中传递它们更有效。

    这里使用的调用约定具体取决于系统;您的问题并没有提供太多线索,您使用的是 Windows 还是 *nix,但我猜测您使用的是 *nix,因为参数是在 rdi 中传递的登记。在这种情况下,编译器将遵循System V AMD64 ABI

    在 System V AMD64 调用约定中,前六个整数大小的参数(也可以是指针)在寄存器 RDIRSIRDXRCXR8 中传递和R9,按此顺序。每个寄存器专用于一个参数,因此参数 1 总是进入 RDI,参数 2 总是进入 RSI,以此类推。相反,浮点参数通过向量寄存器XMM0-XMM7 传递。附加参数以相反的顺序在堆栈上传递。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-12-10
      • 1970-01-01
      • 2010-12-19
      • 1970-01-01
      • 2017-08-01
      • 2011-02-13
      • 1970-01-01
      • 2012-06-22
      相关资源
      最近更新 更多