I have just browsed the Linux kernel source tree and read the file tools/include/nolibc/nolibc.h.
I saw the syscall in this file uses %r8, %r9 and %r10 in the clobber list.
Also there is a comment that says:
rcx and r8..r11 may be clobbered, others are preserved.
As far as I know, syscall only clobbers %rax, %rcx and %r11 (and memory).
Is there a real example of syscall that clobbers %r8, %r9 and %r10?
A system call is a function that allows a process to communicate with the Linux kernel. It's just a programmatic way for a computer program to order a facility from the operating system's kernel. System calls expose the operating system's resources to user programs through an API (Application Programming Interface).
syscall is an instruction in x86-64, and is used as part of the ABI for making system calls. (The 32-bit ABI uses int 80h or sysenter , and is also available in 64-bit mode, but using the 32-bit ABI from 64-bit code is a bad idea, especially for calls with pointer arguments.)
syscall doesn't do any loads or stores, it only modifies registers. Instead of using special registers to save a return address, it simply uses regular integer registers. It's not a coincidence that RCX=RIP and R11=RFLAGS after the kernel returns to your user-space code.
A system call is a programmatic way a program requests a service from the kernel, and strace is a powerful tool that allows you to trace the thin layer between user processes and the Linux kernel. To understand how an operating system works, you first need to understand how system calls work.
Only 32-bit system calls (e.g. via int 0x80) in 64-bit mode step on those registers, along with R11. (What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code?).
syscall properly saves/restores all regs including R8, R9, and R10, so user-space using it can assume they keep their values, except the RAX return value. (The kernel's syscall entry point even saves RCX and R11, but at that point they've already been overwritten by the syscall instruction itself with the original RIP and before-masking RFLAGS value.)
Those, with R11, are the non-legacy registers that are call-clobbered in the function-calling convention, so compiler-generated code for C functions inside the kernel naturally preserves R12-R15, even if an asm entry point didn't save them.
Currently the 64-bit int 0x80 entry point just pushes 0 for the call-clobbered R8-R11 registers in the process-state struct that it will restore from before returning to user space, instead of the original register values.
Historically, the int 0x80 entry point from 32-bit user-space didn't save/restore those registers at all. So their values were whatever compiler-generated kernel code left sitting around. This was thought to be innocent because 32-bit mode can't read those registers, until it was realized that user-space can far-jump to 64-bit mode, using the same CS value that the kernel uses for normal 64-bit user-space processes, selecting that system-wide GDT entry. So there was an actual info leak of kernel data, which was fixed by zeroing those registers.
IDK whether there used to be or still is a separate entry point from 64-bit user-space vs. 32-bit, or how they differ in struct pt_regs layout. The historical situation where int 0x80 leaked r8..r11 wouldn't have made sense for 64-bit user-space; that leak would have been obvious. So if they're unified now, they must not have been in the past.
According to x86-64 ABI about syscall section A.2 AMD64 Linux Kernel Conventions, A.2.1 Calling Conventions [1]:
User-level applications use as integer registers for passing the sequence
%rdi,%rsi,%rdx,%rcx,%r8and%r9. The kernel interface uses%rdi,%rsi,%rdx,%r10,%r8and%r9.A system-call is done via the
syscallinstruction. The kernel destroys registers%rcxand%r11.The number of the
syscallhas to be passed in register%rax.System-calls are limited to six arguments, no argument is passed directly on the stack.
Returning from the
syscall, register%raxcontains the result of the system-call. A value in the range between -4095 and -1 indicates an error, it is -errno.Only values of class INTEGER or class MEMORY are passed to the kernel.
From (2), (5) and (6), we can conclude that Linux x86-64 syscall clobbers %rax, %rcx and %r11 (and "memory").
Link: https://gitlab.com/x86-psABIs/x86-64-ABI/-/wikis/x86-64-psABI [1]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With