What is the type of system call arguments in Linux?

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.

+5
source share
3 answers

There is no general solution. If you want to make your ultra-multiarch code you can just do something like this:

 #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); ... 

But for most common architectures, the size of the register is equal to the length.

+1
source

System call arguments are passed to registers. Thus, the size is limited by the size of the processor register. That is, a 32-bit version in a 32-bit architecture, a 64-bit version in a 64-bit architecture. Floating point numbers cannot be passed to the kernel in this way. Traditionally, the kernel does not use floating point instructions (and may not be possible, since the state of the FPU is usually not saved when entering the kernel), so try to avoid floating point numbers in your own system calls.

System calls using arguments of smaller types, zero or sign, expand them. System calls that use larger types of arguments can split an argument into multiple registers.

System calls (e.g. mmap() ) that have many parameters can be implemented by passing parameters as a pointer to a structure, but this has measurable overhead, so avoid designing system calls with more than five parameters.

At the end of the day, use the types that match the value you want to send. Let libc deal with putting data in the right places.

+4
source

I suggest you use the existing syscall system call, rather than trying to write it yourself. It seems to be doing exactly what you want. See the Architecture Requirements section of the manual page for a discussion of the actual issues that you raised.

+3
source

Source: https://habr.com/ru/post/1243854/


All Articles