07-27-2023, 11:30 AM
I want to write a generic function that does a system call. Something like
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. Does the signature of the function also need to be different? Can I use `long` or should I use something else?
Here are the possible solutions that I found:
- The kernel uses some [dark magic][1]: (__SYSCALL_DEFINEx calls __SC_LONG to get the type, __SC_LONG contains magic). I heard somewhere that types in user space aren't always the same as in kernel space, so I don't know if I can use it.
- musl-libc [uses long for all architectures that it supports except x32][2]: (defined in [arch]/syscall_arch.h).
- I could find documentation for all processor architectures and compilers that I want to support, look up register sizes and integer types sizes and pick any integer type that has the same size as registers.
So I guess the question is "Is there some rule that says 'type of system call arguments are always `long` with some exceptions like x32' or do I need to look up documentation for every architecture and compiler?"
**Edit:** I know that some system calls take pointers and other types as parameters. I want to write generic functions that can call any system call, with generic parameter types. These generic parameter types should be big enough to hold any of the actual parameter types. I know it's possible because [syscall()][3] function 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)`, this means that syscall return value that is stored in register `a` should be saved into variable `ret`. Instead of writing a function that creates local variable, makes syscall, saves its return value into the variable and returns the variable I could write a macro that makes syscall and stores the result into a variable provided by caller. It would 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 would be used like this:
long result;
void * arg1;
int arg2;
my_syscall2(result, <syscall number>, arg1, arg2);
This way I don't need to know register size and integer type that is big enough to hold a value of the register.
[1]:
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. Does the signature of the function also need to be different? Can I use `long` or should I use something else?
Here are the possible solutions that I found:
- The kernel uses some [dark magic][1]: (__SYSCALL_DEFINEx calls __SC_LONG to get the type, __SC_LONG contains magic). I heard somewhere that types in user space aren't always the same as in kernel space, so I don't know if I can use it.
- musl-libc [uses long for all architectures that it supports except x32][2]: (defined in [arch]/syscall_arch.h).
- I could find documentation for all processor architectures and compilers that I want to support, look up register sizes and integer types sizes and pick any integer type that has the same size as registers.
So I guess the question is "Is there some rule that says 'type of system call arguments are always `long` with some exceptions like x32' or do I need to look up documentation for every architecture and compiler?"
**Edit:** I know that some system calls take pointers and other types as parameters. I want to write generic functions that can call any system call, with generic parameter types. These generic parameter types should be big enough to hold any of the actual parameter types. I know it's possible because [syscall()][3] function 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)`, this means that syscall return value that is stored in register `a` should be saved into variable `ret`. Instead of writing a function that creates local variable, makes syscall, saves its return value into the variable and returns the variable I could write a macro that makes syscall and stores the result into a variable provided by caller. It would 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 would be used like this:
long result;
void * arg1;
int arg2;
my_syscall2(result, <syscall number>, arg1, arg2);
This way I don't need to know register size and integer type that is big enough to hold a value of the register.
[1]:
[To see links please register here]
[2]:[To see links please register here]
[3]:[To see links please register here]