0
votes

The man page for syscall says that the first argument is the system call number. On my system (linux x64), arguments are sent in rdi, rsi, rdx, rcx, r8 and r9. So, rdi receives the call number. The syscall in ASM expects the system call number to be in rax, which means by having a generic ASM function syscall(int number, ...arguments), some shifting has to be made, and it's what I've seen when looking at some implementations of the LIBC.

For every system call, take the first argument sent from C code (received in rdi), move it to rax for the system, take the second argument from C code (received in rsi) and move it to rdi (will be first argument for the system).

This makes extra computation, by having an exit(int status) writen straight in ASM, arguments are well placed for the system, and the ASM function just puts 0x3c code in rax and executes syscall.

Are there benefits from having an available generic syscall(int number, ...) in C which has to shift arguments, rather than 312 specific ASM functions ? Other than writing less code.

System calls for my system are here: https://syscalls.w3challs.com/?arch=x86_64

The syscall man page: https://man7.org/linux/man-pages/man2/syscall.2.html

PS: not trying to reinvent the standard, just doing stuff from scratch for fun.

1
The normal libc wrappers don't use that crappy syscall(2) stand-alone function that takes the call number as the first arg.Peter Cordes
Also note that people have written macros for every system call for most arches Linux has been ported to, using some helper macros to combine generic handling of 3-arg system calls named macros for each system call: github.com/linux-on-ibm-z/linux-syscall-support/blob/master/…Peter Cordes

1 Answers

3
votes

syscall is a glue between your portable C code and the platform-specific syscall mechanism of your OS.

You call syscall with the standard C calling convention understood by your compiler. Yes, this calls for some extra tweaking with the registers on x86-64 but it also means that if you want to port your code to, say, Rasberry Pi, under the same kernel version, you only need to recompile your code.
You don't need to use a macro to switch on and off different implementations of each syscall you made.

syscall will also convert from the error convention used by the syscall interface (e.g. using negative numbers on Linux) to the standard errno C convention.

Using syscall will of course already limit the portability of your code but not as much as writing directly in assembly.

Could the syscall parameters have been reordered so that less shifting would be required (e.g. by having the syscall number last)?
Yes, but that would only be true for x86-64 and syscall existed well before that.
Furthermore, syscall is variadic and there is no concept of last in this case. I think it is still possible to make sure the first args are in the right register but it would be a very ugly interface (at that's now how syscall is already defined).

Wouldn't be better if glibc wrote a fast, tailored, handler for each syscall?
That's more a BSD fashioned choice (where kernel and user code are developed together) than a GNU fashioned one.
GNU is not Linux and glibc is not released together with the Linux kernel, having to maintain all the syscalls of Linux (and other OSes) would be too cumbersome. Also, C is more strict than assembly, you can somehow extend the assembly interface of an already existing syscall but it may be problematic in C (if you used a fully prototyped function with no varargs and with strict param types, for example).
I think I understand their need to be generic when dealing with this highly variable aspect of the platform.