diff --git a/.travis.yml b/.travis.yml index ccba938e..36f3d34c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,5 @@ matrix: include: - - dist: xenial - dist: bionic language: rust @@ -13,10 +12,11 @@ branches: install: - sudo apt-get install -y "linux-headers-$(uname -r)" coreutils + - sudo apt-get install clang-9 - rustup component add rust-src rustfmt clippy script: - - ./tests/run_tests.py + - CLANG=clang-9 ./tests/run_tests.py - | for p in . hello-world tests/*; do if [ -d "$p" ]; then diff --git a/README.md b/README.md index 1529caa6..2b5869d7 100644 --- a/README.md +++ b/README.md @@ -19,9 +19,8 @@ various other examples in the tests/ directory. We run [bindgen](https://github.com/rust-lang/rust-bindgen) on the kernel headers to generate automatic Rust FFI bindings. bindgen is powered by [Clang](https://clang.llvm.org), so we use the kernel's -own build system to determine the appropriate CFLAGS (see -`kernel-cflags-finder`) and pass them to bindgen (see `build.rs`). Then we -write safe bindings to these types (see the various files inside `src/`). +own build system to determine the appropriate CFLAGS. Then we write safe +bindings to these types (see the various files inside `src/`). Each kernel module in Rust lives in a `staticlib` crate, which generates a `.a` file. We pass this object to the Linux kernel's own module build @@ -50,14 +49,15 @@ for expected status. They'll need src/c_types.rs ported, too. You'll need to have [Rust](https://www.rust-lang.org) - in particular Rust nightly, as we use [some unstable features](https://github.com/fishinabarrel/linux-kernel-module-rust/issues/41) - -and [Clang](https://clang.llvm.org) installed. You need LLVM/Clang 3.9 -or higher [to bind constants -properly](https://github.com/rust-lang/rust-bindgen/issues/1316). If -you're running kernel 5.0 or newer, [you'll need Clang -9](https://github.com/fishinabarrel/linux-kernel-module-rust/issues/123) -(released September 2019), which adds support for `asm goto`. -You may need to set the `CLANG` environment variable appropriately, -e.g., `CLANG=clang-9`. +and [Clang](https://clang.llvm.org) installed. You need LLVM/Clang 9 +(released September 2019) or higher for multiple reasons, primarily +[support for `asm goto`] +(https://github.com/fishinabarrel/linux-kernel-module-rust/issues/123). +If you're on Debian, Ubuntu, or a derivative, https://apt.llvm.org is +great. + +If the binary named `clang` is too old, make sure to set the `CC` or +`CLANG` environment variable appropriately, e.g., `CC=clang-9`. Very recent kernels may require newer versions of Clang - try Clang 11 if older versions don't work for you. diff --git a/build.rs b/build.rs index 6fdc4671..7d537493 100644 --- a/build.rs +++ b/build.rs @@ -1,6 +1,5 @@ use std::io::{BufRead, BufReader}; use std::path::PathBuf; -use std::process::Command; use std::{env, fs}; const INCLUDED_TYPES: &[&str] = &["file_system_type", "mode_t", "umode_t", "ctl_table"]; @@ -112,38 +111,39 @@ fn handle_kernel_symbols_cfg(symvers_path: &PathBuf) { } } -fn add_env_if_present(cmd: &mut Command, var: &str) { - if let Ok(val) = env::var(var) { - cmd.env(var, val); +// Takes the CFLAGS from the kernel Makefile and changes all the include paths to be absolute +// instead of relative. +fn prepare_cflags(cflags: &str, kernel_dir: &str) -> Vec { + let cflag_parts = shlex::split(&cflags).unwrap(); + let mut cflag_iter = cflag_parts.iter(); + let mut kernel_args = vec![]; + while let Some(arg) = cflag_iter.next() { + if arg.starts_with("-I") && !arg.starts_with("-I/") { + kernel_args.push(format!("-I{}/{}", kernel_dir, &arg[2..])); + } else if arg == "-include" { + kernel_args.push(arg.to_string()); + let include_path = cflag_iter.next().unwrap(); + if include_path.starts_with('/') { + kernel_args.push(include_path.to_string()); + } else { + kernel_args.push(format!("{}/{}", kernel_dir, include_path)); + } + } else { + kernel_args.push(arg.to_string()); + } } + kernel_args } fn main() { - println!("cargo:rerun-if-env-changed=KDIR"); - let kdir = env::var("KDIR").unwrap_or(format!( - "/lib/modules/{}/build", - std::str::from_utf8(&(Command::new("uname").arg("-r").output().unwrap().stdout)) - .unwrap() - .trim() - )); - - println!("cargo:rerun-if-env-changed=CLANG"); - println!("cargo:rerun-if-changed=kernel-cflags-finder/Makefile"); - let mut cmd = Command::new("make"); - cmd.arg("-C") - .arg("kernel-cflags-finder") - .arg("-s") - .env_clear(); - add_env_if_present(&mut cmd, "KDIR"); - add_env_if_present(&mut cmd, "CLANG"); - add_env_if_present(&mut cmd, "PATH"); - let output = cmd.output().unwrap(); - if !output.status.success() { - eprintln!("kernel-cflags-finder did not succeed"); - eprintln!("stdout: {}", std::str::from_utf8(&output.stdout).unwrap()); - eprintln!("stderr: {}", std::str::from_utf8(&output.stderr).unwrap()); - std::process::exit(1); - } + println!("cargo:rerun-if-env-changed=CC"); + println!("cargo:rerun-if-env-changed=abs_srctree"); + println!("cargo:rerun-if-env-changed=c_flags"); + + let kernel_dir = env::var("abs_srctree").expect("Must be invoked from kernel makefile"); + let kernel_cflags = env::var("c_flags").expect("Add 'export c_flags' to Kbuild"); + + let kernel_args = prepare_cflags(&kernel_cflags, &kernel_dir); let target = env::var("TARGET").unwrap(); @@ -155,8 +155,8 @@ fn main() { .rustfmt_bindings(true); builder = builder.clang_arg(format!("--target={}", target)); - for arg in shlex::split(std::str::from_utf8(&output.stdout).unwrap()).unwrap() { - builder = builder.clang_arg(arg.to_string()); + for arg in kernel_args.iter() { + builder = builder.clang_arg(arg.clone()); } println!("cargo:rerun-if-changed=src/bindings_helper.h"); @@ -182,14 +182,14 @@ fn main() { .expect("Couldn't write bindings!"); handle_kernel_version_cfg(&out_path.join("bindings.rs")); - handle_kernel_symbols_cfg(&PathBuf::from(&kdir).join("Module.symvers")); + handle_kernel_symbols_cfg(&PathBuf::from(&kernel_dir).join("Module.symvers")); let mut builder = cc::Build::new(); - builder.compiler(env::var("CLANG").unwrap_or_else(|_| "clang".to_string())); + builder.compiler(env::var("CC").unwrap_or_else(|_| "clang".to_string())); builder.target(&target); builder.warnings(false); builder.file("src/helpers.c"); - for arg in shlex::split(std::str::from_utf8(&output.stdout).unwrap()).unwrap() { + for arg in kernel_args.iter() { builder.flag(&arg); } builder.compile("helpers"); diff --git a/hello-world/Kbuild b/hello-world/Kbuild index de77737e..dfac1853 100644 --- a/hello-world/Kbuild +++ b/hello-world/Kbuild @@ -3,6 +3,9 @@ helloworld-objs := hello_world.rust.o CARGO ?= cargo +export c_flags +export abs_srctree ?= ${CURDIR} + $(src)/target/x86_64-linux-kernel/debug/libhello_world.a: $(src)/Cargo.toml $(wildcard $(src)/src/*.rs) cd $(src); $(CARGO) build -Z build-std=core,alloc --target=x86_64-linux-kernel diff --git a/hello-world/Makefile b/hello-world/Makefile index 85587b7b..4677c5f6 100644 --- a/hello-world/Makefile +++ b/hello-world/Makefile @@ -1,7 +1,12 @@ KDIR ?= /lib/modules/$(shell uname -r)/build +CLANG ?= clang +ifeq ($(origin CC),default) +CC := ${CLANG} +endif + all: - $(MAKE) -C $(KDIR) M=$(CURDIR) + $(MAKE) -C $(KDIR) M=$(CURDIR) CC=$(CC) CONFIG_CC_IS_CLANG=y clean: - $(MAKE) -C $(KDIR) M=$(CURDIR) clean + $(MAKE) -C $(KDIR) M=$(CURDIR) CC=$(CC) clean diff --git a/kernel-cflags-finder/.gitignore b/kernel-cflags-finder/.gitignore deleted file mode 100644 index 1975efc2..00000000 --- a/kernel-cflags-finder/.gitignore +++ /dev/null @@ -1,12 +0,0 @@ -.built-in.o.cmd -.dummy.ko.cmd -.dummy.mod.o.cmd -.dummy.o.cmd -.tmp_versions/ -Module.symvers -built-in.o -dummy.ko -dummy.mod.c -dummy.mod.o -dummy.o -modules.order diff --git a/kernel-cflags-finder/Makefile b/kernel-cflags-finder/Makefile deleted file mode 100644 index 9f65fc9b..00000000 --- a/kernel-cflags-finder/Makefile +++ /dev/null @@ -1,35 +0,0 @@ -ifneq ($(KERNELRELEASE),) -obj-m += dummy.o -clean-files := dummy.c - -# Some systems for installing kernel headers (e.g. Debian's) happen to -# trigger the out-of-tree build code because the kernel headers directly -# actually just recursively invokes another non-arch-specific one. This -# means that they already generate absolute paths for -I by using the -# flags/addtree make functions. Some (e.g. Ubuntu's) do not, and -# generate relative paths. We want absolute paths, but we cannot force -# the out-of-tree build code because it won't work on Debian-style -# kernel headers directory (it will look in the mostly-empty kernel -# headers directory instead of the actual one). So we steal the addtree -# and flags functions from scripts/Kbuild.include, and use them _after_ -# the build system has generated paths - if any remaining paths are -# relative, we make them absolute with respect to CURDIR. (Unlike the -# upstream addtree function, we prefix -I./foo. We also need to fix -# -include ./include/linux/kconfig.h) -our_addtree = $(if $(patsubst -I%,%,$(1)), \ -$(if $(filter-out -I/% -I../%,$(1)),$(patsubst ./%,$(CURDIR)/%,$(patsubst -I%,-I$(CURDIR)/%,$(1))),$(1)),$(1)) -our_flags = $(foreach o,$($(1)),$(call our_addtree,$(o))) - -$(M)/dummy.c: - @echo $(NOSTDINC_FLAGS) $(call our_flags,LINUXINCLUDE) $(or $(__c_flags),$(_c_flags)) $(modkern_cflags) - @touch $@ - -.PHONY: $(M)/dummy.c -else -KDIR ?= /lib/modules/$(shell uname -r)/build -CLANG ?= clang -all: - $(MAKE) -C $(KDIR) M=$(CURDIR) CC=$(CLANG) CONFIG_CC_IS_CLANG=y -clean: - $(MAKE) -C $(KDIR) M=$(CURDIR) clean -endif diff --git a/kernel-cflags-finder/README.md b/kernel-cflags-finder/README.md deleted file mode 100644 index b9674a46..00000000 --- a/kernel-cflags-finder/README.md +++ /dev/null @@ -1,10 +0,0 @@ -Prints the compiler arguments needed to run Clang against the kernel -headers. We use this for running libclang from bindgen. - -Normal usage: run `make -s` and look at stdout (errors go to stderr). If -you need to build for a specific set of kernel headers, add -something like `KDIR=/lib/modules/3.16.0-4-amd64/build`. If your clang -command is not `clang`, add something like `CLANG=clang-3.8`. - -This generates a non-functional kernel module in the process. To clean -up, `make clean`. diff --git a/tests/Kbuild b/tests/Kbuild index 0fd51892..f71fa106 100644 --- a/tests/Kbuild +++ b/tests/Kbuild @@ -3,6 +3,9 @@ testmodule-objs := $(TEST_NAME).rust.o CARGO ?= cargo +export c_flags +export abs_srctree ?= ${CURDIR} + $(src)/target/x86_64-linux-kernel/debug/lib%.a: $(src)/$(TEST_PATH)/Cargo.toml $(wildcard $(src)/$(TEST_PATH)/src/*.rs) cd $(src)/$(TEST_PATH); CARGO_TARGET_DIR=../target $(CARGO) build -Z build-std=core,alloc --target=x86_64-linux-kernel cd $(src)/$(TEST_PATH); CARGO_TARGET_DIR=../target $(CARGO) clippy -Z build-std=core,alloc --target=x86_64-linux-kernel -- -Dwarnings diff --git a/tests/Makefile b/tests/Makefile index 85587b7b..4677c5f6 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,7 +1,12 @@ KDIR ?= /lib/modules/$(shell uname -r)/build +CLANG ?= clang +ifeq ($(origin CC),default) +CC := ${CLANG} +endif + all: - $(MAKE) -C $(KDIR) M=$(CURDIR) + $(MAKE) -C $(KDIR) M=$(CURDIR) CC=$(CC) CONFIG_CC_IS_CLANG=y clean: - $(MAKE) -C $(KDIR) M=$(CURDIR) clean + $(MAKE) -C $(KDIR) M=$(CURDIR) CC=$(CC) clean