alk*_*edr 5 c system-calls linux-kernel
我想写一个执行系统调用的泛型函数.就像是
long my_syscall2(long number, long arg1, long arg2);
Run Code Online (Sandbox Code Playgroud)
我希望它尽可能便携.对于所有体系结构,实现明显不同.功能的签名是否也需要不同?我可以使用long或者我应该使用其他东西吗?
以下是我找到的可能的解决方案:
所以我想问题是"是否有一些规则说'系统调用参数的类型总是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;
}
Run Code Online (Sandbox Code Playgroud)
有趣的是"=a"(ret),这意味着存储在寄存器中的系统调用返回值a应该保存到变量中ret.使用syscall而不是编写创建局部变量的函数,而是将其返回值保存到变量中并返回变量,我可以编写一个宏来进行系统调用并将结果存储到调用者提供的变量中.它看起来像这样:
#define my_syscall2(RET, NUMBER, ARG1, ARG2) \
__asm__ __volatile__ ("syscall" : "=a"(RET) : "a"(NUMBER), "D"(ARG1), "S"(ARG2) \
: "rcx", "r11", "memory");
Run Code Online (Sandbox Code Playgroud)
它会像这样使用:
long result;
void * arg1;
int arg2;
my_syscall2(result, <syscall number>, arg1, arg2);
Run Code Online (Sandbox Code Playgroud)
这样我就不需要知道足够大的寄存器大小和整数类型来保存寄存器的值.
系统调用参数在寄存器中传递。因此,大小受限于 CPU 寄存器的大小。也就是说,32 位架构上的 32 位,64 位架构上的 64 位。浮点数不能以这种方式传递给内核。传统上,内核不使用浮点指令(并且可能无法使用,因为 FPU 状态通常不会在进入内核时保存),因此尽量避免在您自己的系统调用中使用浮点数。
使用较小类型的参数零或符号扩展它们的系统调用。使用较大参数类型的系统调用可能会将参数拆分为多个寄存器。
mmap()可以通过将参数作为指向结构的指针传递参数来实现具有多个参数的系统调用(如),但这具有可衡量的性能开销,因此避免设计具有超过五个参数的系统调用。
在一天结束时,使用适合您要发送的值的类型。让 libc 处理将数据放在正确的位置。
小智 3
没有通用的解决方案。如果你想让你的代码超多架构,你可以这样做:
#if ARCH_WITH_32BIT_REGS
typedef uint32_t reg_size_int_t;
#elif ARCH_WITH_64BIT_REGS
typedef uint64_t reg_size_int_t;
#elif ARCH_WITH_16BIT_REGS
typedef uint16_t reg_size_int_t;
....
#endif
reg_size_int_t syscall_1( reg_size_t nr, reg_size_t arg0);
...
Run Code Online (Sandbox Code Playgroud)
但对于大多数常用的体系结构,寄存器的大小等于 long。