使用寄存器而不是堆栈从 x64 程序集调用 C 函数

Posted

技术标签:

【中文标题】使用寄存器而不是堆栈从 x64 程序集调用 C 函数【英文标题】:Calling C function from x64 assembly with registers instead of stack 【发布时间】:2015-04-20 12:06:59 【问题描述】:

This answer 让我很困惑。

根据standard C calling conventions,调用C 函数的标准方法是将push 参数传递给堆栈和call 子例程。这与syscalls 明显不同,在syscalls 中,您使用适当的参数设置不同的寄存器,然后syscall

但是,上面提到的答案给出了这个 GAS 代码:

        .global main
        .section .data
hello:  .asciz "Hello\n"
        .section .text
main:
        movq $hello, %rdi
        movq $0, %rax
        call printf
        movq $0, %rax
        ret

适用于gcc hello.s -o hello。调用printf的部分是:

        movq $hello, %rdi
        movq $0, %rax
        call printf

它使用rdi 寄存器而不是堆栈将参数传递给printf。将上述更改为

        push $hello
        call printf

导致分段错误。

由于printf 是一个C 函数,与sys_write 不同,我认为应该将参数传递给堆栈,而不是寄存器。我在这里有什么误解?其他标准 C 函数呢,比如malloc

(任何参考将不胜感激。)

【问题讨论】:

“约定”不是“标准”。在 64 位世界中情况有所不同。看看this Wikipedia article。 和你一样的问题:***.com/questions/17193786/… 【参考方案1】:

将参数传递给可变参数函数更加复杂。请参阅x86-64 ELF ABI,第 3.5.7 节。否则,x86-64 使用寄存器传递其前 6 个参数:%rdi, %rsi, %rdx, %rcx, %r8, %r9(不包括浮点/向量参数)。

根据规范,%rax = 0 表示变量参数列表没有 (0) 浮点参数传入向量寄存器。你的方法是错误的,因为第一个参数(例如,以 nul 结尾的字符串:"Hello\n")必须在%rdi 中传递,并且在调用函数时%rax 必须为零。

【讨论】:

这正是我一直在寻找的。非常感谢。

以上是关于使用寄存器而不是堆栈从 x64 程序集调用 C 函数的主要内容,如果未能解决你的问题,请参考以下文章

X.86 X64 汇编器中的正确堆栈操作

如何获取基本堆栈指针的地址

.NET 4.0 NGEN x64 程序集上的符号解析不起作用

在过程调用和操作 ecx 寄存器之前保存 ECX 寄存器

对于 ARM Aarch64 的 NEON 编码,如何将寄存器推送到堆栈?似乎 STMFD 不是 Aarch64 指令集的一部分?

GCC 生成的程序集 - C 函数调用时的段错误