Aarch64 程序集中的系统调用调用

Syscall invoke in Aarch64 assembly

我正在为 Aarch64 arm bit cpu 开发一个系统调用处理函数,我正在研究它是如何在 x86 汇编中完成的,但我无法弄清楚它是如何在 x86 汇编中完成的Aarch64 程序集。

我在 github 上查看了这个示例:https://github.com/rockytriton/LLD/blob/main/linux_os/part1/src/start.S 它是用 x86 汇编语言编写的。

.globl _syscall
_syscall:
    movq %rdi, %rax
    movq %rsi, %rdi
    movq %rdx, %rsi
    movq %rcx, %rdx
    movq %r8, %r10
    movq %r9, %r8
    movq 8(%rsp), %r9
    syscall
    ret

而一些在线等价物如本 answer 所示不满足相同的函数调用设计。这是用 Aarch64 程序集编写的。

/* Generated by gensyscalls.py. Do not edit. */

#include <private/bionic_asm.h>

    .hidden __set_errno

ENTRY(write)
    mov     x8, __NR_write
    svc     #0

    cmn     x0, #(MAX_ERRNO + 1)
    cneg    x0, x0, hi
    b.hi    __set_errno

    ret
END(write)

到目前为止我有这段代码(从 x86 移植到 Aarch64 的效果很差):

.globl _syscall
_syscall:
    mov     x8, r7
    svc     #0
    cmn     x0, #(4095 + 1)
    cneg    x0, x0, hi
    ret

它确实不起作用,但我还是试过了,讽刺的是当我assemble它时,它似乎不喜欢[=13]的寄存器名称=],我不太明白为什么,因为它应该是函数调用的参数(见下文)。

我在我的 C 程序的头文件中有一个函数布局:unsigned long _syscall(int num, void *a0, void *a1, void *a2, void *a3, void *a4, void *a5),有人对如何在 Aarch64 程序集中重新创建相同的系统调用处理程序功能有任何想法吗 - 我的尝试移植失败了。我对此很迷茫,因为汇编不是我的强项 - 具有讽刺意味的是,这是我项目中唯一需要的汇编。

非常感谢!

如另一个答案所示,当你用svc发出系统调用时,你应该在x8中有系统调用号,在x0-x5中有参数。

然而,根据 ARM ABI,参数在寄存器 x0-x7 中从左到右传递给您的 C 函数。 (文档使用 r0-r7 令人困惑;根据它们是 64 位还是 32 位值,这意味着 x0-x7w0-w7。AArch64 没有名为 r7 或类似名称的寄存器,尽管 ARM32做了。)

所以系统调用号在w0中,而它应该在x8中(或者等价于w8,因为它总是一个正的32位数字);第一个参数 a0 在需要在 x0 中时在 x1 中;等等。所以你需要一些代码来随机播放它们。

    mov w8, w0
    mov x0, x1
    mov x1, x2
    mov x2, x3
    mov x3, x4
    mov x4, x5
    mov x5, x6
    svc #0

(如果将 num 参数放在参数列表的末尾而不是开头,可能会省去一些麻烦,如果您不想更改现有代码,也许可以借助宏.那么你只需要mov w8, w6 ; svc #0.)

在另一端,svc #0x0 中留下了 return 值。但是,如果系统调用失败,则 x0 不包含 -1 而是应该 returned 的 errno 代码的负值(在 -1 和 - 之间) 4095 含)。此测试与检查作为无符号值的 return 值是否高于 -4096 相同。当发生这种情况时,通常您希望将 errno 设置为 -x0,然后将 x0 设置为 -1(以便 -1 被 returned来自系统调用函数)。

不清楚您希望 syscall 函数在出现错误时如何运行。按照您的代码,它将否定负错误代码,然后将其留在 x0 中 return,这可能不是您想要的。例如,如果您尝试使用 syscall 打开一个文件,但失败并返回 ENOENT 因为该文件不存在,那么 x0 将包含 -2 return 来自系统调用。您的测试将否定它并且您的 syscall 函数将 return 2,这与 open 成功并在 fd 2 上打开文件没有区别。您可能想想出一些其他的机制,但我不知道你是否想要一个像 errno 或其他东西的全局变量。