-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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 <keescook@chromium.org> Co-developed-by: Alex Gaynor <alex.gaynor@gmail.com> Signed-off-by: Alex Gaynor <alex.gaynor@gmail.com> Co-developed-by: Wedson Almeida Filho <wedsonaf@google.com> Signed-off-by: Wedson Almeida Filho <wedsonaf@google.com> Co-developed-by: David Gow <davidgow@google.com> Signed-off-by: David Gow <davidgow@google.com> Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
- Loading branch information
Showing
2 changed files
with
172 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
// SPDX-License-Identifier: GPL-2.0 | ||
|
||
//! The custom target specification file generator for `rustc`. | ||
//! | ||
//! To configure a target from scratch, a JSON-encoded file has to be passed | ||
//! to `rustc` (introduced in [RFC 131]). These options and the file itself are | ||
//! unstable. Eventually, `rustc` should provide a way to do this in a stable | ||
//! manner. For instance, via command-line arguments. Therefore, this file | ||
//! should avoid using keys which can be set via `-C` or `-Z` options. | ||
//! | ||
//! [RFC 131]: https://rust-lang.github.io/rfcs/0131-target-specification.html | ||
|
||
use std::{ | ||
collections::HashMap, | ||
fmt::{Display, Formatter, Result}, | ||
io::BufRead, | ||
}; | ||
|
||
enum Value { | ||
Boolean(bool), | ||
Number(i32), | ||
String(String), | ||
Object(Object), | ||
} | ||
|
||
type Object = Vec<(String, Value)>; | ||
|
||
/// Minimal "almost JSON" generator (e.g. no `null`s, no arrays, no escaping), | ||
/// enough for this purpose. | ||
impl Display for Value { | ||
fn fmt(&self, formatter: &mut Formatter<'_>) -> Result { | ||
match self { | ||
Value::Boolean(boolean) => write!(formatter, "{}", boolean), | ||
Value::Number(number) => write!(formatter, "{}", number), | ||
Value::String(string) => write!(formatter, "\"{}\"", string), | ||
Value::Object(object) => { | ||
formatter.write_str("{")?; | ||
if let [ref rest @ .., ref last] = object[..] { | ||
for (key, value) in rest { | ||
write!(formatter, "\"{}\": {},", key, value)?; | ||
} | ||
write!(formatter, "\"{}\": {}", last.0, last.1)?; | ||
} | ||
formatter.write_str("}") | ||
} | ||
} | ||
} | ||
} | ||
|
||
struct TargetSpec(Object); | ||
|
||
impl TargetSpec { | ||
fn new() -> TargetSpec { | ||
TargetSpec(Vec::new()) | ||
} | ||
} | ||
|
||
trait Push<T> { | ||
fn push(&mut self, key: &str, value: T); | ||
} | ||
|
||
impl Push<bool> for TargetSpec { | ||
fn push(&mut self, key: &str, value: bool) { | ||
self.0.push((key.to_string(), Value::Boolean(value))); | ||
} | ||
} | ||
|
||
impl Push<i32> for TargetSpec { | ||
fn push(&mut self, key: &str, value: i32) { | ||
self.0.push((key.to_string(), Value::Number(value))); | ||
} | ||
} | ||
|
||
impl Push<String> for TargetSpec { | ||
fn push(&mut self, key: &str, value: String) { | ||
self.0.push((key.to_string(), Value::String(value))); | ||
} | ||
} | ||
|
||
impl Push<&str> for TargetSpec { | ||
fn push(&mut self, key: &str, value: &str) { | ||
self.push(key, value.to_string()); | ||
} | ||
} | ||
|
||
impl Push<Object> for TargetSpec { | ||
fn push(&mut self, key: &str, value: Object) { | ||
self.0.push((key.to_string(), Value::Object(value))); | ||
} | ||
} | ||
|
||
impl Display for TargetSpec { | ||
fn fmt(&self, formatter: &mut Formatter<'_>) -> Result { | ||
// We add some newlines for clarity. | ||
formatter.write_str("{\n")?; | ||
if let [ref rest @ .., ref last] = self.0[..] { | ||
for (key, value) in rest { | ||
write!(formatter, " \"{}\": {},\n", key, value)?; | ||
} | ||
write!(formatter, " \"{}\": {}\n", last.0, last.1)?; | ||
} | ||
formatter.write_str("}") | ||
} | ||
} | ||
|
||
struct KernelConfig(HashMap<String, String>); | ||
|
||
impl KernelConfig { | ||
/// Parses `include/config/auto.conf` from `stdin`. | ||
fn from_stdin() -> KernelConfig { | ||
let mut result = HashMap::new(); | ||
|
||
let stdin = std::io::stdin(); | ||
let mut handle = stdin.lock(); | ||
let mut line = String::new(); | ||
|
||
loop { | ||
line.clear(); | ||
|
||
if handle.read_line(&mut line).unwrap() == 0 { | ||
break; | ||
} | ||
|
||
if line.starts_with('#') { | ||
continue; | ||
} | ||
|
||
let (key, value) = line.split_once('=').expect("Missing `=` in line."); | ||
result.insert(key.to_string(), value.trim_end_matches('\n').to_string()); | ||
} | ||
|
||
KernelConfig(result) | ||
} | ||
|
||
/// Does the option exist in the configuration (any value)? | ||
/// | ||
/// The argument must be passed without the `CONFIG_` prefix. | ||
/// This avoids repetition and it also avoids `fixdep` making us | ||
/// depend on it. | ||
fn has(&self, option: &str) -> bool { | ||
let option = "CONFIG_".to_owned() + option; | ||
self.0.contains_key(&option) | ||
} | ||
} | ||
|
||
fn main() { | ||
let cfg = KernelConfig::from_stdin(); | ||
let mut ts = TargetSpec::new(); | ||
|
||
// `llvm-target`s are taken from `scripts/Makefile.clang`. | ||
if cfg.has("DUMMY_ARCH") { | ||
ts.push("arch", "dummy_arch"); | ||
} else { | ||
panic!("Unsupported architecture"); | ||
} | ||
|
||
ts.push("emit-debug-gdb-scripts", false); | ||
ts.push("frame-pointer", "may-omit"); | ||
ts.push( | ||
"stack-probes", | ||
vec![("kind".to_string(), Value::String("none".to_string()))], | ||
); | ||
|
||
// Everything else is LE, whether `CPU_LITTLE_ENDIAN` is declared or not | ||
// (e.g. x86). It is also `rustc`'s default. | ||
if cfg.has("CPU_BIG_ENDIAN") { | ||
ts.push("target-endian", "big"); | ||
} | ||
|
||
println!("{}", ts); | ||
} |