4

I see a lot of people online referencing

arch/x86/entry/syscalls/syscall_64.tbl

for the syscall table, that works fine. But a lot of others reference

/include/uapi/asm-generic/unistd.h

which is commonly found in the headers package. How come syscall_64.tbl shows,

0 common  read      sys_read

The right answer, and unistd.h shows,

#define __NR_io_setup 0
__SC_COMP(__NR_io_setup, sys_io_setup, compat_sys_io_setup)

And then it shows __NR_read as

#define __NR_read 63
__SYSCALL(__NR_read, sys_read)

Why is that 63, and not 1? How do I make sense of out of /include/uapi/asm-generic/unistd.h? Still in /usr/include/asm/ there is

/usr/include/asm/unistd_x32.h
#define __NR_read (__X32_SYSCALL_BIT + 0)
#define __NR_write (__X32_SYSCALL_BIT + 1)
#define __NR_open (__X32_SYSCALL_BIT + 2)
#define __NR_close (__X32_SYSCALL_BIT + 3)
#define __NR_stat (__X32_SYSCALL_BIT + 4)

/usr/include/asm/unistd_64.h
#define __NR_read 0
#define __NR_write 1
#define __NR_open 2
#define __NR_close 3
#define __NR_stat 4

/usr/include/asm/unistd_32.h
#define __NR_restart_syscall 0
#define __NR_exit 1           
#define __NR_fork 2           
#define __NR_read 3           
#define __NR_write 4          

Could someone tell me the difference between these unistd files. Explain how unistd.h works? And what the best method for finding the syscall table?

3

When I’m investigating this kind of thing, I find it useful to ask the compiler directly (see Printing out standard C/GCC predefined macros in terminal for details):

echo -n "#include <sys/syscall.h>\nSYS_read" | gcc -E -

This shows that the headers involved (on Debian) are /usr/include/x86_64-linux-gnu/sys/syscall.h, /usr/include/x86_64-linux-gnu/asm/unistd.h, /usr/include/x86_64-linux-gnu/asm/unistd_64.h, and /usr/include/x86_64-linux-gnu/bits/syscall.h, and prints the system call number for read, which is 0 on x86-64.

You can find the system call numbers for other architectures if you have the appropriate system headers installed (in a cross-compiler environment). For 32-bit x86 it’s quite easy:

echo -n "#include <sys/syscall.h>\nSYS_read" | gcc -m32 -E -

which involves /usr/include/asm/unistd_32.h among other header files, and prints the number 3.

So from the userspace perspective, 32-bit x86 system calls are defined in asm/unistd_32.h, 64-bit x86 system calls in asm/unistd_64.h. asm/unistd_x32.h is used for the x32 ABI.

uapi/asm-generic/unistd.h lists the default system calls, which are used on architectures which don’t have an architecture-specific system call table.

In the kernel the references are slightly different, and are architecture-specific (again, for architectures which don’t use the generic system call table). This is where files such as arch/x86/entry/syscalls/syscall_64.tbl come in (and they ultimately end up producing the header files which are used in user space, unistd_64.h etc.). You’ll find a lot more detail about system calls in the pair of LWN articles on the topic, Anatomy of a system call part 1 and Anatomy of a system call part 2.

2

I have a page which lists all system calls for each Linux supported architecture:

https://fedora.juszkiewicz.com.pl/syscalls.html

1

This answer will not touch on the asm-generic version of unistd.h, because nothing includes it.1

As noted in syscalls(2):

Roughly speaking, the code belonging to the system call with number __NR_xxx defined in /usr/include/asm/unistd.h can be found in the Linux kernel source in the routine sys_xxx().

That is, the correct syscall numbers will be found in /usr/include/asm/unistd.h. Now, on a typical x86 system, this will simply include one of the asm/unistd_*.h files depending on the target.

The syscall numbers appropriate for a 64-bit program are in asm/unistd_64.h, and those for a 32-bit program in asm/unistd_32.h (or the nearly-equivalent _x32.h variant). The two are different because the 32- and 64-bit architectures are, effectively, completely different operating systems. They share the same set of syscalls, but not in the same order, for various reasons.

Most of these have C-language wrappers as well, so rarely will you need to use syscall(2) directly.


1 And because I don't know what it's for.

1

63 is read in arm64, 0 is read in x86_64

The syscall numbers are different for each architecture.

The arm64 numbers for example are defined at: include/uapi/asm-generic/unistd.h which shows that 63, see also: https://reverseengineering.stackexchange.com/questions/16917/arm64-syscalls-table/18834#18834

As explained in that answer, I think that include/uapi/asm-generic/unistd.h is a newer attempt at unifying syscall numbers across all archs.

But since syscall numbers cannot change to not break the syscall API, older archs before that unification effort have kept the old numbers.

This question asks for an automated way of getting the full syscall list including parameters: https://stackoverflow.com/questions/6604007/how-can-i-get-a-list-of-linux-system-calls-and-number-of-args-they-take-automati

strace source code

I trust that tool, and they keep their data tidy under linux/, e.g.:

Note that the aarch64 one #includes the arch agnostic 64/syscallent.h which I referred to earlier.

Those tables contain the number of arguments, but not actual argument types, I wonder where strace encodes them.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.