Hacker News new | past | comments | ask | show | jobs | submit login
Guide to Linux System Calls (2016) (packagecloud.io)
154 points by crunchbang123 on June 28, 2020 | hide | past | favorite | 35 comments



> Calling system calls by crafting your own assembly is generally a bad idea as the ABI may break underneath you.

syscall should have a stable ABI at the very least, because this would otherwise break all statically linked code.


That is true for Linux but might not be true for other operating systems though.


It is absolutely not true on many (most?) operating systems; Linux is actually an outlier, and we mostly forget that it's the odd one out because it's so popular. Off the top of my head, I believe both NT and Solaris define libc as the stable interface that userspace uses; I don't recall exactly what the BSDs do, but I suspect that they at least strongly encourage using libc and not trying to talk to the kernel yourself (IIRC OpenBSD does this because some of their security measures are managed by libc). Go has hit this a few times because they don't want to depend on libc if they can avoid it, but on a lot of systems they really can't avoid it.

Ah, here we go: https://github.com/golang/go/issues/36435

> Upcoming changes to the OpenBSD kernel will prevent system calls from being made unless they are coming from libc.so (with some exceptions, for example, a static binary). There are also likely to be changes to the APIs used for system calls. As such, the Go runtime (and other packages) need to stop using direct syscalls, rather calling into libc functions instead (as has always been done for Solaris and now also for macOS).

(and the "with some exceptions" is why I say "strongly encouraged")


Only UNIX based OSes use libc as part of the stable interface, which on UNIXes case actually means ISO C + POSIX.

On non-POSIX OSes like NT and plenty of others, libc is part of whatever compiler one decides to use and as such not part of any OS interface as such.

On NT the stable OS APIs are provided via the OS personalities, meaning OS/2 (dead now), the old POSIX one (also dead and replaced by WSL), and Win32 (actually User, Kernel, GDI as the main ones), which as of Windows 8 and MinWin refactoring is split into redirection dlls know as API sets, https://docs.microsoft.com/en-us/windows/win32/apiindex/wind....

Which is why on code that never intends to be portable, you will see calls like ZeroMemory instead of memset.


Yep, ntdll is the bottom of the stack. Raw syscall numbers are not guaranteed across Windows versions and in fact, can be changed by as something as small as a security update. They end up being generated automatically at build time, so there's no guarantee of any kind of stability.


Oh, interesting; I'd assumed that NT was just using libc as its stable ABI, but on further reading it looks more like ntdll.dll (probably just for that personality?). Similar concept, slightly different place. Still, my point was that under the "Windows" personalities, you talk to a library, never directly to the kernel.

EDIT: Found https://web.archive.org/web/20121224002314/http://netcode.cz... which if I'm reading right indicates that ntdll is indeed the bottom-layer library that's allowed to actually talk to the kernel.


Yes, ntdll is the lowest level, but you aren't supposed to use it directly, and if you do, well no one is going to help when a patch Tuesday or something like that breaks the application.

The personality DLLs are the applications entry point with the kernel.


Most of ntdll.dll is officially sanctioned at this point. It's officially documented, and obviously plays into the backwards compat choices they make.


Not really, Windows Internals always refers to the few public ones as "take care when relying on this", very few entries do exist on MSDN or Technet, and those that do exist are mostly tailored for device drivers scenarios.


Sort of. Many NT functions are officially documented. But they're also officially documented as unstable. They probably won't break many of the oldest functions but they reserve the right to do so at some point.


macOS, in some sense a BSD (at least nominally), would like you to not make system calls yourself as well. Actually, not linking against libc has a number of hilarious consequences, one of which is that you bypass the platform sandbox because apparently the engineers thought it couldn't be possible to write a program without it :P


Is there an example somewhere on how to link without libc and make my own syscalls? I tried this a while ago (can't remember which version of macOS it was), fiddling with Csu, nasm etc. but couldn't quite figure it out.


https://john-millikin.com/unix-syscalls#darwin is a small, "hello world" example.


> Note that I have left out the instructions to statically link binaries because they are documented as unsupported

That's a bit annoying, especially since you're already using raw syscall numbers anyways. Here's how to make it static:

  .intel_syntax noprefix
  
  #include <sys/syscall.h>
  
  #define UNIX_SYSCALL 0x2000000
  
  .globl start
  start:
      mov rax, UNIX_SYSCALL | SYS_write
      mov rdi, 1
      lea rsi, text[rip]
      lea rdx, length
      syscall
      mov rax, UNIX_SYSCALL | SYS_exit
      xor rdi, rdi
      syscall
  
  text:
  .asciz "Hello, world!\n"
  .equ length, . - text
Compile that with clang -static -nostdlib.


You don't have to change the source or compile with `clang` -- switching the LD command to:

  ld -arch x86_64 -o hello hello.o -macosx_version_min 10.8 -static -e _main
is sufficient if you're determined to violate the OS vendor's compatibility requirements.


That works too, but I'm lazy :P


-static -nostdlib, and make sure you have an entry point set.


I'm curious why golang architects (looks like top smart people) treated ABI as stable interface not only for Linux/Window but also initially for BSD/macOS.


Windows has a libc? I often see windows binaries statically linked because otherwise you have to drag along all the DLLs.


With Windows "libc" is split in two: ucrt and vcruntime

ucrt is available on all modern version of Windows (since 7) and doesn't need to be statically linked or distributed with the application. It has most functions needed for the c runtime and library.

vcruntime comes with Microsoft's C/C++ compiler. It has functions such as longjmp, memcpy, memset etc and C++ exception handlers. This does not come with Windows. It can be installed separately by the user or distributed with the application (either by placing it the same folder as the exe or by statically linking).


The article is specifically about _Linux_ system calls.


Yep, that's wrong. There's no way Linus would ever let such a change get merged.


Yes, that doesn't make much sense. Linux syscall interface is stable whether or not you use glibc or not.


Did it ever change the last 15 years for linux or windows systems? There are additional commands and perhaps some rules changed about which register need parameters and which need to be saved. But I cannot remember fundamental changes here.


Note that a "syscall" means calling into the kernel directly. Only Linux has stable syscalls.

As mentioned below, on Windows syscalls are highly unstable. They change with every single update to the OS. You have to call functions in ntdll and they in turn will call the kernel. Think of it like a kind of libc but one that must be dynamically linked. You can't statically link it because it's tied to the exact version of Windows you're using.

Of course Window's actual stable interface is the Win32 API, which will call ntdll which in turn makes the syscall.


They don't have them documented, but I didn't think they changed that much. But apparently they did. Just found this neat site:

https://j00ru.vexillium.org/syscalls/nt/64/


Note that they can potentially change with every single update to the OS. That's why that site lists the syscall for every update. They are not stable.


I guess not many actually need to call linux kernel system calls directly bypassing proper measures, but how many fondly remembers int 21h?


I fondly remember INT 21h, and reading the 40Hex magazine along with Ralph Brown's interrupt list.

I was recently working on generating some assembly language output and I added the ability to generate a breakpoint at the start of my executable.

It took me an embarassingly long time to realize that the reason my executables were crashing, not dropping into the debugger, was that "INT3" was assembled differently than "INT 03h" - I knew I needed 0x03, and I knew it was the one-byte form of the instruction (0xCC) rather than (0xCD 0xNN), to ease patching, but .. yeah.


The thing I don't like using language like "proper" is that it creates a frame of "good or bad", when in reality everything in tech is a trade-off, rather than "good or bad".


Well put!


raising hand Plus of course int13 for BIOS...



In grad school for Operating Systems class, one of our assignments was adding a system call to the linux kernel. I found that a useful exercise - both doable in a reasonable amount of time and also a way to learn a lot.


18pt text, gray, thin.

Why?




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: