In recent years, the Rust language has gained a lot of developer attention for its memory safety, high reliability, and zero abstraction capabilities, which happen to be needed in kernel programming, so let’s try out how to write Linux kernel modules in rust.
Rust and kernel modules
Although Rust support has been merged into the mainline in Linux Kernel version 6.1, so in theory, developers can use Rust to write kernel modules for Linux 6.1. However, in actual development work, the kernel version is not the latest, for example, the kernel of Debian 11 is version 5.10, so in this case, how to write kernel modules with Rust?
Principle
- how Rust registers callbacks with the kernel, how to call kernel code. interoperability of Rust and C
- how to compile Rust to the target platform. target configuration of Rust
- how Rust declares kernel module entries and adds special sections. binary conventions for Rust kernel modules
Interoperability of Rust and C
The first issue is basically the interoperability of C and Rust. Thanks to Rust’s abstraction level, it is easy to call C and Rust from each other. rust also provides an official library like bindgen that generates .rs files from .h files. In this way, it seems that it is possible to use bindgen to translate kernel headers to .rs directly? But there is still a problem, how to get the kernel header path? You can use a dummy kernel module to derive the compilation parameters during the compilation process, which contains the header path, compilation parameters, etc., for bindgen to generate the code.
Rust’s target configuration
The main differences between kernel modules and normal programs are.
- the kernel module is freestanding, no libc, memory allocation is also relatively primitive
- the kernel module has special conventions for exception handling, etc.
rust provides a no_std mechanism that allows rust code to be compiled into freestanding binary; rust also provides a way to customize the target, so you can customize the declaration to generate the binary specification.
Binary conventions for kernel modules
The kernel has a number of conventions for kernel modules.
- Declaring module information through sections such as .modinfo
- provide
init_module
,cleanup_module
to provide kernel module installation and uninstallation functions
In this piece, rust provides link_section to customize the section, and also supports extern “C” to export functions. In addition, these underlying operations can be simplified by the kernel providing some C macros, and Rust also provides macros that can be used to do similar things.
A small example
Having said that, let’s look at an example with comments.
|
|
Specifically, how is it built and run?
|
|
It has been tested on kernel 5.10.0-17-amd64.
For the specific code and related configuration, you can refer to the GitHub repository: https://github.com/robberphex/linux-kernel-module-rust
Some small details
-
VSCode support
Since rust-analyzer does not have enough support for custom targets and multiple modules, we temporarily need to manually configure settings.json to develop properly.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
// ./.vscode/settings.json { "rust-analyzer.cargo.extraEnv": { "RUST_TARGET_PATH": "/root/linux-kernel-module-rust" }, "rust-analyzer.cargo.target": "x86_64-linux-kernel-module", "rust-analyzer.server.extraEnv": { "RA_LOG": "lsp_server=debug", "RUST_TARGET_PATH": "/root/linux-kernel-module-rust" }, "rust-analyzer.trace.server": "verbose", "rust-analyzer.linkedProjects": [ "hello-world/Cargo.toml", "Cargo.toml" ], }
-
Other advanced functions
such as character devices, sysctl, and other functions, can be found in the relevant test code in Project.
More planning
- The interface is consistent with the Rust-for-Linux API. (Example of Rust-for-Linux: https://github.com/Rust-for-Linux/linux/blob/d9b2e84c0700782f26c9558a3eaacbe1f78c01e8/samples/rust/rust_chrdev.rs)
- Rust provides memory security, zero abstraction, and other capabilities that happen to be much needed features and capabilities in the kernel domain. For example, a memory leak or a dangling pointer in the kernel state can have a big impact and be very difficult to debug. In this area, we can use Rust’s capabilities to construct safer and larger projects.
The original project was fishinabarrel/linux-kernel-module-rust
, but it is currently prompted to use rust-for-linux, which has been archived.
However, considering that there are still a lot of older kernels out there, I have re-fixed some of the environments in this project to allow people to write kernel modules in Rust on older kernels.