I think @newpavlov properly explained this above. But the Linux kernel syscalls return a bare usize
, which is zero for success, negative for error, and each negative number corresponds to an error condition.
glibc
could have just made their syscall
API return that, but instead, they take that error condition, potentially translate it into an user-space error code, put it in a thread local, and only make syscall
return 0
or -1
. This gives glibc a consistent error API, even for syscalls
. This used to be ok-ish for single threaded code, but errno
hasn't aged well for multi-threaded applications.
I'm surprised to see that the
syscall
crate's interface returns a bareusize
instead ofResult<usize, Errno>
.
First, it should be Result<(), NonZeroErrno>
, because the success representation is always zero, and the error representation is always non-zero. This allows the Result
to fit into a single usize
, instead of requiring two registers, one for the discriminant, and one for the payload. Second, while we do perform this niche optimization on Result
-like enums, we do not guarantee that this optimization is always performed (we only guarantee it for Option
-like enums), so using this on FFI is probably not a good idea until that changes.
That is, code that uses this type on FFI might or might not map to a single usize
, depending on the compiler version.
I think FreeBSD also reserves the right to change kernel ABI, but has compat symbol versions in its libc.
I can confirm that FreeBSD adds new incompatible versions of kernel APIs on every release using new symbol names, and changes its C library to dispatch to those. Most of the time, it also provides the older versions under the old symbol names, such that binaries compiled against those "just work" on newer kernel versions "as is". Rarely, FreeBSD does make backward incompatible changes to the symbols in place. This happens when the FreeBSD maintainers "believe" that no code is using these symbols. In my experience, the burden of proof for those beliefs is quite low. The consequence of those changes is that, e.g., Rust's libc
crate ends up only being able to provide "guaranteed undefined behavior depending on your FreeBSD version" for those APIs. I can only hope that nobody is really using these.