Idea: expose Linux raw syscall interface in std::os::linux

A lot of feedback has been given in the PR. I think that there is a real problem here affecting users, and that this problem is worth solving. It's very unclear to me that this solution is the best solution to the problem, and think that it would be a better idea to step back and write down precisely what the problem is, the general properties of a good solution (e.g. it should be possible to inline the syscall wrappers) are, what solutions do we currently have available for this problem in stable/unstable Rust and in std/#![no_std] binaries, and what their pros and cons are.

The constraint that it should be possible to inline the thin "system call" wrapper would require justification. For example, C code cannot inline syscall. The most that inlining here does is remove a single function call that jumps into an assembly blob. We can't inline through the assembly, and we can't inline further into the kernel. Also, inlining isn't always desirable, so it should at most be left up to the optimizer.


For example, some of the current solutions with their pros / cons would be:

use linked syscall wrapper from the C library

Binaries that link libstd can, on stable Rust, without any dependencies, reliably write:

extern "C" { fn syscall(...) -> ...; }

and use the libc syscall API. This solution can be more easily used on nightly, without any extern declaration, by enabling the unstable feature(libc) and just writing use libc::syscall.

Pros of this solution are that it is the stable solution of the platform, it is always guaranteed to work with all codegen backends, that it provides a nice variadic API, and that it can be made to work with a libc-free standard (the standard library would just provide a syscall symbol).

Cons of this solution are lack of inlining of the syscall wrapper, that these wrappers unnecessarily use errno, this solution does not work on #![no_std]

compile and link an assembly blob

We can do this with the cc crate on stable, or with global_asm! on nightly.

Pros: always guaranteed to work on stable Rust (the cc version), works with all codegen backends (the cc version), multiple syscalls can be provided, works with libc-free standard, works with#![no_std]`.

Cons of this solution: requires a C compiler (which is always available for Linux targets), can't be inlined.

compile and link a C wrapper

Stable Rust binaries with or without std can implement these wrappers in C with inline assembly, and link them into their own binary using xLTO.

Pros of this solution are that this is always guaranteed to work on stable Rust, the syscalls wrappers can be inlined, works with all codegen backends, multiple syscalls can be provided, a nice variadic syscall wrapper that does not use errno can also be provided and inlined, works with libc-free standard, works with #![no_std].

Cons of this solution: requires writing C code (bad, but it only must be done once), requires a C compiler (which is always available for Linux targets), requires building with xLTO which isn't straight-forward and requires using an appropriate linker.

using inline asm!

On unstable Rust we can always use asm!.

Pros: the syscall wrappers can be inlined, works for #![no_std], works with libc-free standard,

Cons: asm! is unstable and code using it can and does break, can't implement a variadic syscall API that does not use errno, doesn't work for different codegen backends (e.g. it would abort when used with librustc_codegen_cranelift)


Some of the ways in which we could solve this problem are:

Add this to core (somewhere)

Pros: all users might be able to just include them and use them, would inline wrappers, would work with #![no_std], would work with libc-free standard, we could add any API we'd want

Cons: increases the surface area of the standard library (requires convincing the libs team of the value this API adds), unclear how to implement this such that it works for backends that do not support inline assembly like rustc_codegen_cranelift (might require linking C code to core), duplicates an API that libstd already exposes (libstd already exposes syscall on all Linux targets, and core would be re-exposing a different way of doing syscalls).

Making xLTO easier and simpler to use

We could add support for xLTO to the cc crate to make it easy to use on linux for this, which is something probably worth doing anyways, and would immediately work on stable.

Stabilize inline assembly or global assembly

One could always push for an RFC that stabilizes inline assembly. There is no time pressure for this given the existence of the other solutions.


There are probably many other ways to solve this problem currently, things one can do to improve the current situation in the meantime, and probably many other ways to fully solve it with a great solution in the future.

From all the solutions I see, using the cc crate to compile C code that uses inline assembly sounds like the best way to do this today. It's unclear to me whether inlining the wrappers is worth it, but if it is, using -Clinker-plugin-lto when building the crate would solve the issue and would work with thinLTO so all --release builds would benefit from it.

From all other solutions, stabilizing inline assembly is something that we want to do anyways, and would allow solving this problem in pure Rust, making any other solution that we come up here obsolete.

6 Likes