Skip to content

Commit 9a8ff24

Browse files
committed
scripts: add generate_rust_target.rs
This script takes care of generating the custom target specification file for `rustc`, based on the kernel configuration. It also serves as an example of a Rust host program. A dummy architecture is kept in this patch so that a later patch adds x86 support on top with as few changes as possible. Reviewed-by: Kees Cook <[email protected]> Co-developed-by: Alex Gaynor <[email protected]> Signed-off-by: Alex Gaynor <[email protected]> Co-developed-by: Wedson Almeida Filho <[email protected]> Signed-off-by: Wedson Almeida Filho <[email protected]> Co-developed-by: David Gow <[email protected]> Signed-off-by: David Gow <[email protected]> Signed-off-by: Miguel Ojeda <[email protected]>
1 parent 8c4555c commit 9a8ff24

File tree

2 files changed

+172
-0
lines changed

2 files changed

+172
-0
lines changed

scripts/.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# SPDX-License-Identifier: GPL-2.0-only
22
/asn1_compiler
33
/bin2c
4+
/generate_rust_target
45
/insert-sys-cert
56
/kallsyms
67
/module.lds

scripts/generate_rust_target.rs

+171
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
//! The custom target specification file generator for `rustc`.
4+
//!
5+
//! To configure a target from scratch, a JSON-encoded file has to be passed
6+
//! to `rustc` (introduced in [RFC 131]). These options and the file itself are
7+
//! unstable. Eventually, `rustc` should provide a way to do this in a stable
8+
//! manner. For instance, via command-line arguments. Therefore, this file
9+
//! should avoid using keys which can be set via `-C` or `-Z` options.
10+
//!
11+
//! [RFC 131]: https://rust-lang.github.io/rfcs/0131-target-specification.html
12+
13+
use std::{
14+
collections::HashMap,
15+
fmt::{Display, Formatter, Result},
16+
io::BufRead,
17+
};
18+
19+
enum Value {
20+
Boolean(bool),
21+
Number(i32),
22+
String(String),
23+
Object(Object),
24+
}
25+
26+
type Object = Vec<(String, Value)>;
27+
28+
/// Minimal "almost JSON" generator (e.g. no `null`s, no arrays, no escaping),
29+
/// enough for this purpose.
30+
impl Display for Value {
31+
fn fmt(&self, formatter: &mut Formatter<'_>) -> Result {
32+
match self {
33+
Value::Boolean(boolean) => write!(formatter, "{}", boolean),
34+
Value::Number(number) => write!(formatter, "{}", number),
35+
Value::String(string) => write!(formatter, "\"{}\"", string),
36+
Value::Object(object) => {
37+
formatter.write_str("{")?;
38+
if let [ref rest @ .., ref last] = object[..] {
39+
for (key, value) in rest {
40+
write!(formatter, "\"{}\": {},", key, value)?;
41+
}
42+
write!(formatter, "\"{}\": {}", last.0, last.1)?;
43+
}
44+
formatter.write_str("}")
45+
}
46+
}
47+
}
48+
}
49+
50+
struct TargetSpec(Object);
51+
52+
impl TargetSpec {
53+
fn new() -> TargetSpec {
54+
TargetSpec(Vec::new())
55+
}
56+
}
57+
58+
trait Push<T> {
59+
fn push(&mut self, key: &str, value: T);
60+
}
61+
62+
impl Push<bool> for TargetSpec {
63+
fn push(&mut self, key: &str, value: bool) {
64+
self.0.push((key.to_string(), Value::Boolean(value)));
65+
}
66+
}
67+
68+
impl Push<i32> for TargetSpec {
69+
fn push(&mut self, key: &str, value: i32) {
70+
self.0.push((key.to_string(), Value::Number(value)));
71+
}
72+
}
73+
74+
impl Push<String> for TargetSpec {
75+
fn push(&mut self, key: &str, value: String) {
76+
self.0.push((key.to_string(), Value::String(value)));
77+
}
78+
}
79+
80+
impl Push<&str> for TargetSpec {
81+
fn push(&mut self, key: &str, value: &str) {
82+
self.push(key, value.to_string());
83+
}
84+
}
85+
86+
impl Push<Object> for TargetSpec {
87+
fn push(&mut self, key: &str, value: Object) {
88+
self.0.push((key.to_string(), Value::Object(value)));
89+
}
90+
}
91+
92+
impl Display for TargetSpec {
93+
fn fmt(&self, formatter: &mut Formatter<'_>) -> Result {
94+
// We add some newlines for clarity.
95+
formatter.write_str("{\n")?;
96+
if let [ref rest @ .., ref last] = self.0[..] {
97+
for (key, value) in rest {
98+
write!(formatter, " \"{}\": {},\n", key, value)?;
99+
}
100+
write!(formatter, " \"{}\": {}\n", last.0, last.1)?;
101+
}
102+
formatter.write_str("}")
103+
}
104+
}
105+
106+
struct KernelConfig(HashMap<String, String>);
107+
108+
impl KernelConfig {
109+
/// Parses `include/config/auto.conf` from `stdin`.
110+
fn from_stdin() -> KernelConfig {
111+
let mut result = HashMap::new();
112+
113+
let stdin = std::io::stdin();
114+
let mut handle = stdin.lock();
115+
let mut line = String::new();
116+
117+
loop {
118+
line.clear();
119+
120+
if handle.read_line(&mut line).unwrap() == 0 {
121+
break;
122+
}
123+
124+
if line.starts_with('#') {
125+
continue;
126+
}
127+
128+
let (key, value) = line.split_once('=').expect("Missing `=` in line.");
129+
result.insert(key.to_string(), value.trim_end_matches('\n').to_string());
130+
}
131+
132+
KernelConfig(result)
133+
}
134+
135+
/// Does the option exist in the configuration (any value)?
136+
///
137+
/// The argument must be passed without the `CONFIG_` prefix.
138+
/// This avoids repetition and it also avoids `fixdep` making us
139+
/// depend on it.
140+
fn has(&self, option: &str) -> bool {
141+
let option = "CONFIG_".to_owned() + option;
142+
self.0.contains_key(&option)
143+
}
144+
}
145+
146+
fn main() {
147+
let cfg = KernelConfig::from_stdin();
148+
let mut ts = TargetSpec::new();
149+
150+
// `llvm-target`s are taken from `scripts/Makefile.clang`.
151+
if cfg.has("DUMMY_ARCH") {
152+
ts.push("arch", "dummy_arch");
153+
} else {
154+
panic!("Unsupported architecture");
155+
}
156+
157+
ts.push("emit-debug-gdb-scripts", false);
158+
ts.push("frame-pointer", "may-omit");
159+
ts.push(
160+
"stack-probes",
161+
vec![("kind".to_string(), Value::String("none".to_string()))],
162+
);
163+
164+
// Everything else is LE, whether `CPU_LITTLE_ENDIAN` is declared or not
165+
// (e.g. x86). It is also `rustc`'s default.
166+
if cfg.has("CPU_BIG_ENDIAN") {
167+
ts.push("target-endian", "big");
168+
}
169+
170+
println!("{}", ts);
171+
}

0 commit comments

Comments
 (0)