I see a lot of people online referencing


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


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

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

#define __NR_read 0
#define __NR_write 1
#define __NR_open 2
#define __NR_close 3
#define __NR_stat 4

#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?

5 Answers 5


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):

printf SYS_read | gcc -include sys/syscall.h -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:

printf SYS_read | gcc -include sys/syscall.h -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.

  • Is the syscall table stable between Linux kernel version and future ones?
    – Biswapriyo
    Sep 3, 2019 at 19:52
  • @Biswapriyo it is, that’s part of the ABI stability which the kernel developers always try to preserve. New syscalls can be added, but old ones don’t change, except in a very small number of extreme cases (such as the tux syscall). Sep 3, 2019 at 20:44

Different archs have different syscall numbers defined in different files

The syscall numbers are different for each architecture, e.g.:

include/uapi/asm-generic/unistd.h vs arch/* definitions

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 (including x86, x86_64 and arm) have kept the old numbers defined under arch/. arm64 is newer, and got the new unistd.h API however.

This related 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.


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



To add on all the great answers, there is a utility ausyscall which can be used to list all the syscalls and their integer mappings for the particular architecture.


$ ausyscall --dump
Using x86_64 syscall table:
0   read
1   write
2   open
3   close
4   stat

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.

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.