I want to write a generic function that performs a system call. Sort of
long my_syscall2(long number, long arg1, long arg2);
I want it to be as portable as possible. The implementation is obviously different for all architectures. Do I need a different signature for the function? Can I use long
or use another?
Here are the possible solutions that I found:
- The kernel uses dark magic : (__SYSCALL_DEFINEx calls __SC_LONG to get the type, __SC_LONG contains magic). I heard somewhere that types in user space are not always the same as in kernel space, so I don't know if I can use it.
- musl-libc uses long for all architectures it supports except x32 : (defined in [arch] /syscall_arch.h).
- I could find documentation for all the processor architectures and compilers that I want to support, look at the sizes of the registers and the sizes of the integer types, and select any integer type with the same size as the registers.
So, I suppose the question is: "Is there any rule that says:" The type of the system call arguments is always long
with some exceptions, for example x32 ", or do I need to look for documentation for each architecture and compiler?"
Edit: I know that some system calls accept pointers and other types as parameters. I want to write general functions that can call any system call, with typical parameter types. These generic parameter types must be large enough to hold any of the actual parameter types. I know this is possible because syscall () exists.
Edit2: Here is another partial solution to this problem.
Implementations of these functions currently look like this:
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; }
The interesting part is "=a"(ret)
, which means that the return value in syscall, which is stored in register a
, must be stored in the variable ret
. Instead of writing a function that creates a local variable, does syscall, stores the return value in the variable and returns the variable, I can write a macro that does syscall, and stores the result in the variable provided by the caller. It will look like this:
#define my_syscall2(RET, NUMBER, ARG1, ARG2) \ __asm__ __volatile__ ("syscall" : "=a"(RET) : "a"(NUMBER), "D"(ARG1), "S"(ARG2) \ : "rcx", "r11", "memory");
And it will be used as follows:
long result; void * arg1; int arg2; my_syscall2(result, <syscall number>, arg1, arg2);
Thus, I do not need to know the size of the register and the integer type that is large enough to hold the value of the register.