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-x7
或 w0-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 #0
在 x0
中留下了 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
或其他东西的全局变量。
我正在为 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-x7
或 w0-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 #0
在 x0
中留下了 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
或其他东西的全局变量。