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.

glibc source code


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.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .