Skip to content

Commit 13f2876

Browse files
ojedakloenk
authored andcommitted
Forbid rustc from using -f{function,data}-sections (fixes #20 panic)
Some kernel configs were panicking at the Rust example driver initialization. The `__MOD` static value had a bogus value, which meant that trying to initialize it was dropping the object that was, supposedly, there. The memory corruption happened during rootfs unpacking, which explains why it only happened in some configs (like in CI) and why it also didn't happen if there was an early error during unpacking. That memory corruption, in turn, was caused because the `__MOD` symbol was being placed after the end of the kernel reserve. That happened due to the kernel's linker script not supporting unique sections per symbol for dead code data elimination -- yet. Some arches do, but until we can rely on that, we need to disable their generation in rustc's side for the moment. Since we discussed to have the target spec on our side, and since `-Z function-sections=false` was added just a month ago, I went with the spec route. Other symbols were being placed in unexpected places, which should be fixed now too. Signed-off-by: Miguel Ojeda <[email protected]>
1 parent 67a9249 commit 13f2876

File tree

3 files changed

+171
-3
lines changed

3 files changed

+171
-3
lines changed

Makefile

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -511,12 +511,11 @@ KBUILD_CFLAGS := -Wall -Wundef -Werror=strict-prototypes -Wno-trigraphs \
511511
KBUILD_CPPFLAGS := -D__KERNEL__
512512
KBUILD_RUSTCFLAGS :=
513513
# TODO: a simple way to update `Cargo.lock` when we add a new driver
514-
# TODO: another option is using explicit target specs, e.g.
515-
# `--target=$(srctree)/arch/$(SRCARCH)/rust-target-spec.json`
516514
KBUILD_CARGOFLAGS := $(CARGO_VERBOSE) --locked \
517515
-Z build-std=core,alloc \
518516
-Z unstable-options \
519-
--target=x86_64-linux-kernel
517+
--target=$(PWD)/$(srctree)/arch/$(SRCARCH)/rust/target.json \
518+
--out-dir=out
520519
KBUILD_AFLAGS_KERNEL :=
521520
KBUILD_CFLAGS_KERNEL :=
522521
KBUILD_RUSTCFLAGS_KERNEL :=

arch/x86/rust/target.json

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
{
2+
"arch": "x86_64",
3+
"code-model": "kernel",
4+
"cpu": "x86-64",
5+
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128",
6+
"disable-redzone": true,
7+
"eliminate-frame-pointer": false,
8+
"env": "gnu",
9+
"features": "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-3dnow,-3dnowa,-avx,-avx2,+soft-float",
10+
"function-sections": false,
11+
"is-builtin": true,
12+
"linker-flavor": "gcc",
13+
"linker-is-gnu": true,
14+
"llvm-target": "x86_64-elf",
15+
"max-atomic-width": 64,
16+
"needs-plt": true,
17+
"os": "none",
18+
"panic-strategy": "abort",
19+
"position-independent-executables": true,
20+
"pre-link-args": {
21+
"gcc": [
22+
"-Wl,--as-needed",
23+
"-Wl,-z,noexecstack",
24+
"-m64"
25+
]
26+
},
27+
"relocation-model": "static",
28+
"relro-level": "full",
29+
"stack-probes": true,
30+
"target-c-int-width": "32",
31+
"target-endian": "little",
32+
"target-pointer-width": "64",
33+
"vendor": "unknown"
34+
}

rust/kernel/build.rs

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
use std::env;
4+
use std::path::PathBuf;
5+
6+
const INCLUDED_TYPES: &[&str] = &["file_system_type", "mode_t", "umode_t", "ctl_table"];
7+
const INCLUDED_FUNCTIONS: &[&str] = &[
8+
"cdev_add",
9+
"cdev_init",
10+
"cdev_del",
11+
"register_filesystem",
12+
"unregister_filesystem",
13+
"krealloc",
14+
"kfree",
15+
"mount_nodev",
16+
"kill_litter_super",
17+
"register_sysctl",
18+
"unregister_sysctl_table",
19+
"access_ok",
20+
"_copy_to_user",
21+
"_copy_from_user",
22+
"alloc_chrdev_region",
23+
"unregister_chrdev_region",
24+
"wait_for_random_bytes",
25+
"get_random_bytes",
26+
"rng_is_initialized",
27+
"printk",
28+
"add_device_randomness",
29+
];
30+
const INCLUDED_VARS: &[&str] = &[
31+
"EINVAL",
32+
"ENOMEM",
33+
"ESPIPE",
34+
"EFAULT",
35+
"EAGAIN",
36+
"__this_module",
37+
"FS_REQUIRES_DEV",
38+
"FS_BINARY_MOUNTDATA",
39+
"FS_HAS_SUBTYPE",
40+
"FS_USERNS_MOUNT",
41+
"FS_RENAME_DOES_D_MOVE",
42+
"BINDINGS_GFP_KERNEL",
43+
"KERN_INFO",
44+
"VERIFY_WRITE",
45+
"LINUX_VERSION_CODE",
46+
"SEEK_SET",
47+
"SEEK_CUR",
48+
"SEEK_END",
49+
"O_NONBLOCK",
50+
"param_ops_bool",
51+
"param_ops_int",
52+
];
53+
const OPAQUE_TYPES: &[&str] = &[
54+
// These need to be opaque because they're both packed and aligned, which rustc
55+
// doesn't support yet. See https://github.com/rust-lang/rust/issues/59154
56+
// and https://github.com/rust-lang/rust-bindgen/issues/1538
57+
"desc_struct",
58+
"xregs_state",
59+
];
60+
61+
// Takes the CFLAGS from the kernel Makefile and changes all the include paths to be absolute
62+
// instead of relative.
63+
fn prepare_cflags(cflags: &str, kernel_dir: &str) -> Vec<String> {
64+
let cflag_parts = shlex::split(&cflags).unwrap();
65+
let mut cflag_iter = cflag_parts.iter();
66+
let mut kernel_args = vec![];
67+
while let Some(arg) = cflag_iter.next() {
68+
// TODO: bindgen complains
69+
if arg.starts_with("-Wp,-MMD") {
70+
continue;
71+
}
72+
73+
if arg.starts_with("-I") && !arg.starts_with("-I/") {
74+
kernel_args.push(format!("-I{}/{}", kernel_dir, &arg[2..]));
75+
} else if arg == "-include" {
76+
kernel_args.push(arg.to_string());
77+
let include_path = cflag_iter.next().unwrap();
78+
if include_path.starts_with('/') {
79+
kernel_args.push(include_path.to_string());
80+
} else {
81+
kernel_args.push(format!("{}/{}", kernel_dir, include_path));
82+
}
83+
} else {
84+
kernel_args.push(arg.to_string());
85+
}
86+
}
87+
kernel_args
88+
}
89+
90+
fn main() {
91+
println!("cargo:rerun-if-env-changed=CC");
92+
println!("cargo:rerun-if-env-changed=RUST_BINDGEN_CFLAGS");
93+
94+
let kernel_dir = "../../";
95+
let cflags = env::var("RUST_BINDGEN_CFLAGS").expect("Must be invoked from kernel makefile");
96+
97+
let kernel_args = prepare_cflags(&cflags, &kernel_dir);
98+
99+
// TODO: pass the proper triple to bindgen
100+
let target = "x86_64-linux-kernel";
101+
102+
let mut builder = bindgen::Builder::default()
103+
.use_core()
104+
.ctypes_prefix("c_types")
105+
.derive_default(true)
106+
.size_t_is_usize(true)
107+
.rustfmt_bindings(true);
108+
109+
builder = builder.clang_arg(format!("--target={}", target));
110+
for arg in kernel_args.iter() {
111+
builder = builder.clang_arg(arg.clone());
112+
}
113+
114+
println!("cargo:rerun-if-changed=src/bindings_helper.h");
115+
builder = builder.header("src/bindings_helper.h");
116+
117+
for t in INCLUDED_TYPES {
118+
builder = builder.whitelist_type(t);
119+
}
120+
for f in INCLUDED_FUNCTIONS {
121+
builder = builder.whitelist_function(f);
122+
}
123+
for v in INCLUDED_VARS {
124+
builder = builder.whitelist_var(v);
125+
}
126+
for t in OPAQUE_TYPES {
127+
builder = builder.opaque_type(t);
128+
}
129+
let bindings = builder.generate().expect("Unable to generate bindings");
130+
131+
let out_path = PathBuf::from("src/bindings_gen.rs");
132+
bindings
133+
.write_to_file(out_path)
134+
.expect("Couldn't write bindings!");
135+
}

0 commit comments

Comments
 (0)