In recent years, the Rust language has gained a lot of developer attention for its memory safety, high reliability, zero abstraction, and other capabilities that happen to be needed in kernel programming, so let’s look at 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 theoretically 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 Debian 11 kernel 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. the 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 both C and Rust. rust also provides an official library like bindgen, which generates .rs files from .h files.
So, it looks like it is possible to translate the kernel headers directly to .rs using bindgen?
But there is still one 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 paths, compilation parameters, etc., for bindgen to generate the code.
Rust and target configurations
The main differences between kernel modules and ordinary programs are:
- the kernel module is freestanding, no libc, and memory allocation is primitive
- kernel modules have 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 that you can customize the declaration to generate the binary specification.
Binary conventions for kernel modules
The kernel has some conventions for kernel modules.
- Declaring module information through sections such as .modinfo
- provide init_module, cleanup_module to provide kernel module installation and uninstallation
In this regard, Rust provides link_section to define sections, and 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.
|
|
Specific builds and runs.
|
|
Tested on kernel 5.10.0-17-amd64.
For the specific code and related configuration, please refer to: https://github.com/robberphex/linux-kernel-module-rust
Some details
-
VSCode support
Since rust-analyzer does not have enough support for custom targets and multiple modules, we need to manually configure settings.json for the time being in order to develop properly.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
{ "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 features
For example, character devices, sysctl and other functions, you can refer to the relevant test code in the project.
More Planning
- Maintain API consistency with Rust-for-Linux. (Rust-for-Linux example: https://github.com/Rust-for-Linux/linux/blob/d9b2e84c0700782f26c9558a3eaacbe1f78c01e8/samples/rust/rust_chrdev.rs)
- Rust provides memory security, zero abstraction, and other capabilities that are much needed in the kernel space. For example, a memory leak or a dangling pointer in the kernel state can have a big impact and be difficult to debug. In this area, we can use Rust’s capabilities to construct safer and larger projects.
The original project is fishinabarrel/linux-kernel-module-rust, but it is currently prompted to use rust-for-linux, which has been archived. however, given that there are still a lot of older kernels out there, I have re-fixed some of the environment of this project to allow people to write in Rust on older kernels kernel module.