【发布时间】:2016-06-08 07:45:36
【问题描述】:
我想编写一个执行系统调用的通用函数。类似的东西
long my_syscall2(long number, long arg1, long arg2);
我希望它尽可能便携。对于所有架构,实现显然是不同的。函数的签名是否也需要不同?我可以使用long 还是应该使用其他东西?
以下是我找到的可能解决方案:
- 内核使用了一些dark magic:(__SYSCALL_DEFINEx 调用__SC_LONG 来获取类型,__SC_LONG 包含魔法)。我在某处听说用户空间中的类型与内核空间中的类型并不总是相同,所以我不知道我是否可以使用它。
- musl-libc uses long for all architectures that it supports except x32:(在 [arch]/syscall_arch.h 中定义)。
- 我可以找到我想要支持的所有处理器架构和编译器的文档,查找寄存器大小和整数类型大小,并选择与寄存器大小相同的任何整数类型。
所以我想问题是“是否有一些规则说‘系统调用参数的类型总是long,除了 x32 之类的例外’,还是我需要查找每个架构和编译器的文档?”
编辑:我知道有些系统调用将指针和其他类型作为参数。我想用泛型参数类型编写可以调用任何系统调用的泛型函数。这些泛型参数类型应该足够大以容纳任何实际参数类型。我知道这是可能的,因为syscall() 函数存在。
Edit2:这是此问题的另一个部分解决方案。
这些函数的实现目前如下所示:
static __inline long my_syscall2(long number, long arg1, long arg2)
{
unsigned long ret;
__asm__ __volatile__ ("syscall" : "=a"(ret) : "a"(number), "D"(arg1), "S"(arg2)
: "rcx", "r11", "memory");
return ret;
}
有趣的部分是"=a"(ret),这意味着存储在寄存器a中的系统调用返回值应该保存到变量ret中。与其编写创建局部变量的函数,进行系统调用,将其返回值保存到变量中并返回变量,我可以编写一个宏来进行系统调用并将结果存储到调用者提供的变量中。它看起来像这样:
#define my_syscall2(RET, NUMBER, ARG1, ARG2) \
__asm__ __volatile__ ("syscall" : "=a"(RET) : "a"(NUMBER), "D"(ARG1), "S"(ARG2) \
: "rcx", "r11", "memory");
它会这样使用:
long result;
void * arg1;
int arg2;
my_syscall2(result, <syscall number>, arg1, arg2);
这样我就不需要知道寄存器的大小和足够大的整数类型来保存寄存器的值。
【问题讨论】:
-
如何处理不带参数的系统调用?
-
@black 我会定义 6 个函数 my_syscall{0-5}
-
它实际上比这里简洁地回答要复杂得多(除非您指定架构),但请随意使用我的 syscall() 宏和 syscall0()-syscall6() 函数的公共领域实现github.com/technosaurus/BQC ... 所有这些都转换为 long 参数
-
@technosaurus 看来你一直在使用
long,所以从技术上讲它并不能回答这个问题,但是你的库看起来很有趣,我一定会在写我自己的之前仔细看看,谢谢。 -
我还没有想出一个好的编程方法来确定体系结构是否使用 long long 进行系统调用,但是 AFAIK 唯一不使用
long的体系结构是 64 位体系结构上的 x32 变体当定义 ILP32 时,像 x86_64、PPC64 和 aarch64(可能还有 mips)。我最终可能会在 ARCHDATA 中指定它并为系统调用创建一个单独的 typedef,但我没有测试它们的好方法(我通常使用原住民 linux 构建和 qemu)
标签: c linux-kernel system-calls