In a similar vein, jart's Cosmopolitan libc has a really fun collection of tables that compare various constants across platforms, e.g. syscalls, syscall flags, error numbers, etc. It includes (variants of) Linux, XNU, NT, and the BSDs.
This is a thing of beauty. I used to make spreadsheets like this ages ago when I was working across Linux and Solaris, but they were nowhere near as thorough as this.
I wish this site could have existed earlier when I was writing eBPF filters for my sandbox and had to check how specific syscalls were implemented in different archs here and there. Thanks for your great work.
Do you mean arguments and the internal syscall number used for a syscall on your given platform?
I recently had enough of parsing the various syscall.h files on different architectures and wrote a debugfs syscall info reader instead. That way you can see all tracepoint-instrumented syscalls and arguments available exactly on your currently running kernel on your platform:
Edit: changed "all" to "all tracepoint-instrumented" based on a comment below - some added syscalls don't (immediately) get instrumented with a tracepoint so tracefs wouldn't show them (until someone instruments them in a later kernel version as seems to be the case). The tracefs approach has been good enough for me, but the only 100% guaranteed way to see all currently available syscalls would be to read the syscall table from kernel memory and see which syscall handler kernel functions they call (as the syscall name itself is meaningless inside the kernel).
Yes. My primary use case was to allow only some syscalls and block all others. Until I had to support multiple architectures and executables having different runtime behavior. I ended up attaching an debugger and searching every syscall I met one by one. My understanding to kernels then was not enough to reduce the development friction.
I've been using other tables but they were always incomplete and often x86_64 only. This one contains everything: number, symbol, links to kernel implementation, signature, user space ABI registers. And I can select kernel version, kernel binary interface and processor architecture!
I'm very interested in how you are collecting or generating all this information. Please post details on the process. I need similar information in order to compile system call tables into lone, my own programming language which features direct Linux system call support.
I use scripts that parse the information out of Linux user space API headers: the compiler prints all the preprocessor definitions from linux/unistd.h, the "SYS_" definitions are selected and then turned into a C array initializer for a number/name structure.
# makefile
$(call source_to_object,source/lone/lisp/modules/intrinsic/linux.c): $(targets.NR.c)
$(targets.NR.c): $(targets.NR.list) scripts/NR.generate
scripts/NR.generate < $< > $@
$(targets.NR.list): scripts/NR.filter
$(CC) -E -dM -include linux/unistd.h - < /dev/null | scripts/NR.filter > $@
# scripts/NR.filter
grep __NR_ | sed 's/#define //g' | cut -d ' ' -f 1
# scripts/NR.generate
# generates C array initializers like:
# { "read", __NR_read },
while read -r NR; do
printf '{ "%s", %s },\n' "${NR#__NR_}" "${NR}"
done
// source/lone/lisp/modules/intrinsic/linux.c
static struct linux_system_call {
char *symbol;
lone_lisp_integer number;
} linux_system_calls[] = {
/* huge generated array initializer
* with all the system calls found
* on the host platform
*/
#include <lone/lisp/modules/intrinsic/linux/NR.c>
};
Thank you very much :). I am using static analysis of kernel images (vmlinux ELF) that are built with debug information. Each table you see was extracted from a kernel built by my tool, Systrack, that can configure and build kernels that have all the syscalls available. The code is heavily commented and available on GitHub if you are interested: https://github.com/mebeim/systrack
I realized soon in the process that simply looking at kernel sources was not enough to extract everything accurately, specially definition locations. I also wanted this to be a tool to extract syscalls actually implemented from a given kernel image, so that's what it does.
Your approach should be fine, that is what any other language does basically: rely on uapi headers provided by the kernel (just beware that some may be generated at build time inside e.g. include/asm/generated/xxx). You should rely on the headers that are exported when you do `make headers_install`. Also, make sure to have a generic syscall() function that takes an arbitrary syscall number and an arbitrary amount of args to make raw syscalls for the weird ones you don't easily find in uapi headers and you should be good. After all, even in the C library headers some of the "weird" syscalls aren't present sometimes.
Man syscall and man syscalls. I'm on Mobile and remoted into my wsl to find neither list the actual table at first blush, but I have seen it. It's probably in one of the related pages. This is why I like the site though. It's all just there.
There are also the manual pages for each individual system call.
The syscall numbers unfortunately cannot be found in the manual. They are found in published tables on the internet.
They are defined in numerous locations in the Linux kernel tree. They are included via the linux/unistd.h header which in turn includes the appropriate asm-generic/ and asm/ headers.
There's quite a bit of complexity here. The system call numbers are stable for each architecture but may differ between architectures. Some architectures have multiple historical versions of the same system call which are maintained for backwards compatibility, others have just the latest version of the relevant system call with the version number removed.
I assume this complexity is the reason why this information is not typically included. People expect you to rely on the libc which abstracts all this.
You can't since man pages present the libc implementation, that's more useful if you work with syscalls that use structures since it shows you what to search for in libc to copy it in other language.
I'm pretty sure it is because when Linus ported Linux from x86 to the DEC Alpha in the early 90s, he used the DEC OSF/1 syscall numbers (and error numbers) so that he could bootstrap the Linux kernel from an OSF/1 userland. He probably should have had a flag day & normalized the syscall numbers to be arch independent like they are on *BSD, but he never did.
Coming from BSD, I find this very confusing and tend to grumble when I'm tracing something and have to go groveling from the right errno.h. Eg:
I vaguely remember reading somewhere that the MIPS ones are weird to support compatibility with the existing unix syscall numbering, but I can't find any evidence for that anywhere, so maybe it was aspirational or I'm hallucinating.
System call internal numbers are meaningless, are different across platforms and can change across kernel compiles/upgrades. There are also gaps in the internal numbering, so the max value seen in define _NR_syscall doesn't show the actual number of used syscall numbers in your current kernel...
The homepage loads exactly the most common x86-64 ABI, which is x64 (according to kernel naming). It's the one for 64-bit syscalls made by 64-bit code. On x86-64 you also have IA32 (32-bit syscalls made by 32-bit code) and x32 (64-bit syscalls made by 64-bit code specifically built to only use 32-bit pointers).
I wonder why it displays without javascript in chromium, but fails to do so in firefox. If the author is here, could that be fixed?
Edit:
Restarting chromium and trying again yields the same behavior as firefox. I wonder if the javascript somehow slipped past umatrix & ublock origin on my first try. Given that I launched chromium by dragging the link onto its icon the first time, perhaps the script-blocking extensions weren't fully loaded?
Testing again several more times, that does seem likely. I can reproduce it intermittently by dragging the URL onto my chromium shortcut if chromium isn't already running.
The html sourcecode shows the content of the table is not served within the page, but is added on page load through javascript by formatting a json requested file. I changed the browser's user-agent to chrome and the same page was served (though the link to git shows it's an static page). My guess is may be chromium is not disabling javascript.
PS: I'm a Firefox user too, I always browse with Javascript disabled by uMatrix (in addition to uBlockOrigin), I only enable it when the web deserves it and leaving disabled third domains js loads almost always.
I'm the author and I can confirm. The website will not work with JS disabled simply because it's a static HTML "skeleton" page loading JSON tables with JS and populating a <table> element. If it's working then it means you must have JS enabled. I don't plan to add support for browsers without JS, but the JSON tables have all the information you need anyway, and those are just static files (e.g. https://syscalls.mebeim.net/db/x86/64/x64/latest/table.json).
>slipped past umatrix & ublock origin on my first try. Given that I launched chromium by dragging the link onto its icon the first time, perhaps the script-blocking extensions weren't fully loaded?
Yes. Chrome will actually pause extensions loading to deliver you that first rendered picture fraction(arguable, probably slower in the end considering extensions load from disk) of a second faster. I think it was direct uBO sabotage.
TLDR: First website loaded by starting browser with a link or from last session has almost 100% chance of bypassing uBlockOrigin. Chrome and Chromium based browsers are not User Agents, they are Google Agents.
Okay, this is super cool. Thanks for sharing.
In a similar vein, jart's Cosmopolitan libc has a really fun collection of tables that compare various constants across platforms, e.g. syscalls, syscall flags, error numbers, etc. It includes (variants of) Linux, XNU, NT, and the BSDs.
https://github.com/jart/cosmopolitan/blob/master/libc/sysv/c...
In the off chance you haven't heard of Cosmopolitan yet, I hope you find the discovery as much fun as I have.
GNU/Systemd is pretty hilarious
This is a thing of beauty. I used to make spreadsheets like this ages ago when I was working across Linux and Solaris, but they were nowhere near as thorough as this.
I am curious what the difference is supposed to be between "XNU's Not UNIX!" and "MacOS (Arm64)".
I'm guessing it's just x86-64 vs. AArch64. There are two columns, one marked "(Aarch64)", for Linux too.
I would imagine the "MacOS" bit is there to emphasize that the values haven't been verified on iOS.
1 reply →
<3
I wish this site could have existed earlier when I was writing eBPF filters for my sandbox and had to check how specific syscalls were implemented in different archs here and there. Thanks for your great work.
Do you mean arguments and the internal syscall number used for a syscall on your given platform?
I recently had enough of parsing the various syscall.h files on different architectures and wrote a debugfs syscall info reader instead. That way you can see all tracepoint-instrumented syscalls and arguments available exactly on your currently running kernel on your platform:
https://tanelpoder.com/posts/list-linux-system-call-argument...
Edit: changed "all" to "all tracepoint-instrumented" based on a comment below - some added syscalls don't (immediately) get instrumented with a tracepoint so tracefs wouldn't show them (until someone instruments them in a later kernel version as seems to be the case). The tracefs approach has been good enough for me, but the only 100% guaranteed way to see all currently available syscalls would be to read the syscall table from kernel memory and see which syscall handler kernel functions they call (as the syscall name itself is meaningless inside the kernel).
Yes. My primary use case was to allow only some syscalls and block all others. Until I had to support multiple architectures and executables having different runtime behavior. I ended up attaching an debugger and searching every syscall I met one by one. My understanding to kernels then was not enough to reduce the development friction.
This is SUCH a good tool! Thank you!!
I've been using other tables but they were always incomplete and often x86_64 only. This one contains everything: number, symbol, links to kernel implementation, signature, user space ABI registers. And I can select kernel version, kernel binary interface and processor architecture!
I'm very interested in how you are collecting or generating all this information. Please post details on the process. I need similar information in order to compile system call tables into lone, my own programming language which features direct Linux system call support.
I use scripts that parse the information out of Linux user space API headers: the compiler prints all the preprocessor definitions from linux/unistd.h, the "SYS_" definitions are selected and then turned into a C array initializer for a number/name structure.
Thank you very much :). I am using static analysis of kernel images (vmlinux ELF) that are built with debug information. Each table you see was extracted from a kernel built by my tool, Systrack, that can configure and build kernels that have all the syscalls available. The code is heavily commented and available on GitHub if you are interested: https://github.com/mebeim/systrack
I realized soon in the process that simply looking at kernel sources was not enough to extract everything accurately, specially definition locations. I also wanted this to be a tool to extract syscalls actually implemented from a given kernel image, so that's what it does.
Your approach should be fine, that is what any other language does basically: rely on uapi headers provided by the kernel (just beware that some may be generated at build time inside e.g. include/asm/generated/xxx). You should rely on the headers that are exported when you do `make headers_install`. Also, make sure to have a generic syscall() function that takes an arbitrary syscall number and an arbitrary amount of args to make raw syscalls for the weird ones you don't easily find in uapi headers and you should be good. After all, even in the C library headers some of the "weird" syscalls aren't present sometimes.
This is good. Usually I end up finding one I think Google made for Chromebooks. Or even worse, restoring to the man page.
Where can I find syscall numbers in the man pages?
Man syscall and man syscalls. I'm on Mobile and remoted into my wsl to find neither list the actual table at first blush, but I have seen it. It's probably in one of the related pages. This is why I like the site though. It's all just there.
9 replies →
The manuals have the following pages on system calls:
https://www.man7.org/linux/man-pages/man2/syscall.2.html
https://www.man7.org/linux/man-pages/man2/syscalls.2.html
There are also the manual pages for each individual system call.
The syscall numbers unfortunately cannot be found in the manual. They are found in published tables on the internet.
They are defined in numerous locations in the Linux kernel tree. They are included via the linux/unistd.h header which in turn includes the appropriate asm-generic/ and asm/ headers.
https://github.com/torvalds/linux/blob/master/include/uapi/a...
https://github.com/torvalds/linux/blob/master/tools/arch/x86...
https://github.com/torvalds/linux/blob/master/tools/arch/x86...
https://github.com/torvalds/linux/blob/master/tools/arch/arm...
There's quite a bit of complexity here. The system call numbers are stable for each architecture but may differ between architectures. Some architectures have multiple historical versions of the same system call which are maintained for backwards compatibility, others have just the latest version of the relevant system call with the version number removed.
I assume this complexity is the reason why this information is not typically included. People expect you to rely on the libc which abstracts all this.
You can't since man pages present the libc implementation, that's more useful if you work with syscalls that use structures since it shows you what to search for in libc to copy it in other language.
4 replies →
Is there a reason syscall numbers don’t match up between architectures?
Or is it just a quirk of history?
I'm pretty sure it is because when Linus ported Linux from x86 to the DEC Alpha in the early 90s, he used the DEC OSF/1 syscall numbers (and error numbers) so that he could bootstrap the Linux kernel from an OSF/1 userland. He probably should have had a flag day & normalized the syscall numbers to be arch independent like they are on *BSD, but he never did.
Coming from BSD, I find this very confusing and tend to grumble when I'm tracing something and have to go groveling from the right errno.h. Eg:
% find ~/linux/ -name 'errno*' | wc -l
% find ~/freebsd/sys -name 'errno*' | wc -l
They were renumbered in x86_64 so that the syscalls that are frequently used together have their function pointers live in the same cacheline in the lookup table: https://lkml.iu.edu/hypermail/linux/kernel/0104.0/0547.html
I vaguely remember reading somewhere that the MIPS ones are weird to support compatibility with the existing unix syscall numbering, but I can't find any evidence for that anywhere, so maybe it was aspirational or I'm hallucinating.
I am curious how much this actually helps.
Missing RISC-V
That's the next arch I want to add but it takes a bit of work, sooner or later I will add it though :')
I'm missing s390x.
Yeah, it seems odd that it has PowerPC but not RISC-V.
This is neat! Finally someone who added all the information I need :)
Only 357 syscalls in 6.0. Don't know why, but I thought there would be more.
It's 462
System call internal numbers are meaningless, are different across platforms and can change across kernel compiles/upgrades. There are also gaps in the internal numbering, so the max value seen in define _NR_syscall doesn't show the actual number of used syscall numbers in your current kernel...
Edit: this answer has some relevant details:
https://stackoverflow.com/questions/63713056/why-is-their-a-...
462 is just the highest number. Many are skipped. It tells you at the bottom the total, 357.
There's a few of these floating around that are generally missing something - recent syscalls, types, etc. Thank you for such a complete one!
Thanks! This is great. If you ever need extra housing for this, I would be glad to provide it.
I've wished for a complete version like this for so long. Great work. Thank you!
Can you make one for kernel exports and list whether they are GPL-only or not?
That is just a simple `grep -R EXPORT_SYMBOL` or `grep -R EXPORT_SYMBOL_GPL`, isn't it? A table for that wouldn't have much value.
> A table for that wouldn't have much value
Just because you can search the source, doesn't mean this wouldn't come in handy to someone some day.
Why would you care about that?
Third-party driver development
similarly, does a Windows syscall tracker exist?
https://github.com/j00ru/windows-syscalls
From this I learned about Landlock, thanks!
What API is the most common for x86-64bit?
The x86-64 ABI is the most common x86-64 ABI ;)
(x32 is rare https://en.wikipedia.org/wiki/X32_ABI )
The homepage loads exactly the most common x86-64 ABI, which is x64 (according to kernel naming). It's the one for 64-bit syscalls made by 64-bit code. On x86-64 you also have IA32 (32-bit syscalls made by 32-bit code) and x32 (64-bit syscalls made by 64-bit code specifically built to only use 32-bit pointers).
removed
The linked source seems to be checking (len_in && !len).
len_in being the passed argument and len being the page aligned len.
That's handy.
I wonder why it displays without javascript in chromium, but fails to do so in firefox. If the author is here, could that be fixed?
Edit:
Restarting chromium and trying again yields the same behavior as firefox. I wonder if the javascript somehow slipped past umatrix & ublock origin on my first try. Given that I launched chromium by dragging the link onto its icon the first time, perhaps the script-blocking extensions weren't fully loaded?
Testing again several more times, that does seem likely. I can reproduce it intermittently by dragging the URL onto my chromium shortcut if chromium isn't already running.
Edit 2: Sure enough:
https://github.com/gorhill/uBlock/issues/1913
https://github.com/gorhill/uBlock/issues/1327
I'm not the author.
The html sourcecode shows the content of the table is not served within the page, but is added on page load through javascript by formatting a json requested file. I changed the browser's user-agent to chrome and the same page was served (though the link to git shows it's an static page). My guess is may be chromium is not disabling javascript.
PS: I'm a Firefox user too, I always browse with Javascript disabled by uMatrix (in addition to uBlockOrigin), I only enable it when the web deserves it and leaving disabled third domains js loads almost always.
I'm the author and I can confirm. The website will not work with JS disabled simply because it's a static HTML "skeleton" page loading JSON tables with JS and populating a <table> element. If it's working then it means you must have JS enabled. I don't plan to add support for browsers without JS, but the JSON tables have all the information you need anyway, and those are just static files (e.g. https://syscalls.mebeim.net/db/x86/64/x64/latest/table.json).
>slipped past umatrix & ublock origin on my first try. Given that I launched chromium by dragging the link onto its icon the first time, perhaps the script-blocking extensions weren't fully loaded?
Yes. Chrome will actually pause extensions loading to deliver you that first rendered picture fraction(arguable, probably slower in the end considering extensions load from disk) of a second faster. I think it was direct uBO sabotage.
TLDR: First website loaded by starting browser with a link or from last session has almost 100% chance of bypassing uBlockOrigin. Chrome and Chromium based browsers are not User Agents, they are Google Agents.
It also happen when opening a link in Incognito mode
Chrome breaks security addons by design, in the name of "performance". It's purely a coincidence that Chrome is made by a major ad company.