19a8ff24cSMiguel Ojeda // SPDX-License-Identifier: GPL-2.0
29a8ff24cSMiguel Ojeda 
39a8ff24cSMiguel Ojeda //! The custom target specification file generator for `rustc`.
49a8ff24cSMiguel Ojeda //!
59a8ff24cSMiguel Ojeda //! To configure a target from scratch, a JSON-encoded file has to be passed
69a8ff24cSMiguel Ojeda //! to `rustc` (introduced in [RFC 131]). These options and the file itself are
79a8ff24cSMiguel Ojeda //! unstable. Eventually, `rustc` should provide a way to do this in a stable
89a8ff24cSMiguel Ojeda //! manner. For instance, via command-line arguments. Therefore, this file
99a8ff24cSMiguel Ojeda //! should avoid using keys which can be set via `-C` or `-Z` options.
109a8ff24cSMiguel Ojeda //!
119a8ff24cSMiguel Ojeda //! [RFC 131]: https://rust-lang.github.io/rfcs/0131-target-specification.html
129a8ff24cSMiguel Ojeda 
139a8ff24cSMiguel Ojeda use std::{
149a8ff24cSMiguel Ojeda     collections::HashMap,
159a8ff24cSMiguel Ojeda     fmt::{Display, Formatter, Result},
169a8ff24cSMiguel Ojeda     io::BufRead,
179a8ff24cSMiguel Ojeda };
189a8ff24cSMiguel Ojeda 
199a8ff24cSMiguel Ojeda enum Value {
209a8ff24cSMiguel Ojeda     Boolean(bool),
219a8ff24cSMiguel Ojeda     Number(i32),
229a8ff24cSMiguel Ojeda     String(String),
23c6945acaSMatthew Maurer     Array(Vec<Value>),
249a8ff24cSMiguel Ojeda     Object(Object),
259a8ff24cSMiguel Ojeda }
269a8ff24cSMiguel Ojeda 
279a8ff24cSMiguel Ojeda type Object = Vec<(String, Value)>;
289a8ff24cSMiguel Ojeda 
comma_sep<T>( seq: &[T], formatter: &mut Formatter<'_>, f: impl Fn(&mut Formatter<'_>, &T) -> Result, ) -> Result29c6945acaSMatthew Maurer fn comma_sep<T>(
30c6945acaSMatthew Maurer     seq: &[T],
31c6945acaSMatthew Maurer     formatter: &mut Formatter<'_>,
32c6945acaSMatthew Maurer     f: impl Fn(&mut Formatter<'_>, &T) -> Result,
33c6945acaSMatthew Maurer ) -> Result {
34c6945acaSMatthew Maurer     if let [ref rest @ .., ref last] = seq[..] {
35c6945acaSMatthew Maurer         for v in rest {
36c6945acaSMatthew Maurer             f(formatter, v)?;
37c6945acaSMatthew Maurer             formatter.write_str(",")?;
38c6945acaSMatthew Maurer         }
39c6945acaSMatthew Maurer         f(formatter, last)?;
40c6945acaSMatthew Maurer     }
41c6945acaSMatthew Maurer     Ok(())
42c6945acaSMatthew Maurer }
43c6945acaSMatthew Maurer 
44c6945acaSMatthew Maurer /// Minimal "almost JSON" generator (e.g. no `null`s, no escaping),
459a8ff24cSMiguel Ojeda /// enough for this purpose.
469a8ff24cSMiguel Ojeda impl Display for Value {
fmt(&self, formatter: &mut Formatter<'_>) -> Result479a8ff24cSMiguel Ojeda     fn fmt(&self, formatter: &mut Formatter<'_>) -> Result {
489a8ff24cSMiguel Ojeda         match self {
499a8ff24cSMiguel Ojeda             Value::Boolean(boolean) => write!(formatter, "{}", boolean),
509a8ff24cSMiguel Ojeda             Value::Number(number) => write!(formatter, "{}", number),
519a8ff24cSMiguel Ojeda             Value::String(string) => write!(formatter, "\"{}\"", string),
52c6945acaSMatthew Maurer             Value::Array(values) => {
53c6945acaSMatthew Maurer                 formatter.write_str("[")?;
54c6945acaSMatthew Maurer                 comma_sep(&values[..], formatter, |formatter, v| v.fmt(formatter))?;
55c6945acaSMatthew Maurer                 formatter.write_str("]")
56c6945acaSMatthew Maurer             }
579a8ff24cSMiguel Ojeda             Value::Object(object) => {
589a8ff24cSMiguel Ojeda                 formatter.write_str("{")?;
59c6945acaSMatthew Maurer                 comma_sep(&object[..], formatter, |formatter, v| {
60c6945acaSMatthew Maurer                     write!(formatter, "\"{}\": {}", v.0, v.1)
61c6945acaSMatthew Maurer                 })?;
629a8ff24cSMiguel Ojeda                 formatter.write_str("}")
639a8ff24cSMiguel Ojeda             }
649a8ff24cSMiguel Ojeda         }
659a8ff24cSMiguel Ojeda     }
669a8ff24cSMiguel Ojeda }
679a8ff24cSMiguel Ojeda 
68c6945acaSMatthew Maurer impl From<bool> for Value {
from(value: bool) -> Self69c6945acaSMatthew Maurer     fn from(value: bool) -> Self {
70c6945acaSMatthew Maurer         Self::Boolean(value)
71c6945acaSMatthew Maurer     }
72c6945acaSMatthew Maurer }
73c6945acaSMatthew Maurer 
74c6945acaSMatthew Maurer impl From<i32> for Value {
from(value: i32) -> Self75c6945acaSMatthew Maurer     fn from(value: i32) -> Self {
76c6945acaSMatthew Maurer         Self::Number(value)
77c6945acaSMatthew Maurer     }
78c6945acaSMatthew Maurer }
79c6945acaSMatthew Maurer 
80c6945acaSMatthew Maurer impl From<String> for Value {
from(value: String) -> Self81c6945acaSMatthew Maurer     fn from(value: String) -> Self {
82c6945acaSMatthew Maurer         Self::String(value)
83c6945acaSMatthew Maurer     }
84c6945acaSMatthew Maurer }
85c6945acaSMatthew Maurer 
86c6945acaSMatthew Maurer impl From<&str> for Value {
from(value: &str) -> Self87c6945acaSMatthew Maurer     fn from(value: &str) -> Self {
88c6945acaSMatthew Maurer         Self::String(value.to_string())
89c6945acaSMatthew Maurer     }
90c6945acaSMatthew Maurer }
91c6945acaSMatthew Maurer 
92c6945acaSMatthew Maurer impl From<Object> for Value {
from(object: Object) -> Self93c6945acaSMatthew Maurer     fn from(object: Object) -> Self {
94c6945acaSMatthew Maurer         Self::Object(object)
95c6945acaSMatthew Maurer     }
96c6945acaSMatthew Maurer }
97c6945acaSMatthew Maurer 
98c6945acaSMatthew Maurer impl<T: Into<Value>, const N: usize> From<[T; N]> for Value {
from(i: [T; N]) -> Self99c6945acaSMatthew Maurer     fn from(i: [T; N]) -> Self {
100c6945acaSMatthew Maurer         Self::Array(i.into_iter().map(|v| v.into()).collect())
101c6945acaSMatthew Maurer     }
102c6945acaSMatthew Maurer }
103c6945acaSMatthew Maurer 
1049a8ff24cSMiguel Ojeda struct TargetSpec(Object);
1059a8ff24cSMiguel Ojeda 
1069a8ff24cSMiguel Ojeda impl TargetSpec {
new() -> TargetSpec1079a8ff24cSMiguel Ojeda     fn new() -> TargetSpec {
1089a8ff24cSMiguel Ojeda         TargetSpec(Vec::new())
1099a8ff24cSMiguel Ojeda     }
1109a8ff24cSMiguel Ojeda 
push(&mut self, key: &str, value: impl Into<Value>)111c6945acaSMatthew Maurer     fn push(&mut self, key: &str, value: impl Into<Value>) {
112c6945acaSMatthew Maurer         self.0.push((key.to_string(), value.into()));
1139a8ff24cSMiguel Ojeda     }
1149a8ff24cSMiguel Ojeda }
1159a8ff24cSMiguel Ojeda 
1169a8ff24cSMiguel Ojeda impl Display for TargetSpec {
fmt(&self, formatter: &mut Formatter<'_>) -> Result1179a8ff24cSMiguel Ojeda     fn fmt(&self, formatter: &mut Formatter<'_>) -> Result {
1189a8ff24cSMiguel Ojeda         // We add some newlines for clarity.
1199a8ff24cSMiguel Ojeda         formatter.write_str("{\n")?;
1209a8ff24cSMiguel Ojeda         if let [ref rest @ .., ref last] = self.0[..] {
1219a8ff24cSMiguel Ojeda             for (key, value) in rest {
1229a8ff24cSMiguel Ojeda                 write!(formatter, "    \"{}\": {},\n", key, value)?;
1239a8ff24cSMiguel Ojeda             }
1249a8ff24cSMiguel Ojeda             write!(formatter, "    \"{}\": {}\n", last.0, last.1)?;
1259a8ff24cSMiguel Ojeda         }
1269a8ff24cSMiguel Ojeda         formatter.write_str("}")
1279a8ff24cSMiguel Ojeda     }
1289a8ff24cSMiguel Ojeda }
1299a8ff24cSMiguel Ojeda 
1309a8ff24cSMiguel Ojeda struct KernelConfig(HashMap<String, String>);
1319a8ff24cSMiguel Ojeda 
1329a8ff24cSMiguel Ojeda impl KernelConfig {
1339a8ff24cSMiguel Ojeda     /// Parses `include/config/auto.conf` from `stdin`.
from_stdin() -> KernelConfig1349a8ff24cSMiguel Ojeda     fn from_stdin() -> KernelConfig {
1359a8ff24cSMiguel Ojeda         let mut result = HashMap::new();
1369a8ff24cSMiguel Ojeda 
1379a8ff24cSMiguel Ojeda         let stdin = std::io::stdin();
1389a8ff24cSMiguel Ojeda         let mut handle = stdin.lock();
1399a8ff24cSMiguel Ojeda         let mut line = String::new();
1409a8ff24cSMiguel Ojeda 
1419a8ff24cSMiguel Ojeda         loop {
1429a8ff24cSMiguel Ojeda             line.clear();
1439a8ff24cSMiguel Ojeda 
1449a8ff24cSMiguel Ojeda             if handle.read_line(&mut line).unwrap() == 0 {
1459a8ff24cSMiguel Ojeda                 break;
1469a8ff24cSMiguel Ojeda             }
1479a8ff24cSMiguel Ojeda 
1489a8ff24cSMiguel Ojeda             if line.starts_with('#') {
1499a8ff24cSMiguel Ojeda                 continue;
1509a8ff24cSMiguel Ojeda             }
1519a8ff24cSMiguel Ojeda 
1529a8ff24cSMiguel Ojeda             let (key, value) = line.split_once('=').expect("Missing `=` in line.");
1539a8ff24cSMiguel Ojeda             result.insert(key.to_string(), value.trim_end_matches('\n').to_string());
1549a8ff24cSMiguel Ojeda         }
1559a8ff24cSMiguel Ojeda 
1569a8ff24cSMiguel Ojeda         KernelConfig(result)
1579a8ff24cSMiguel Ojeda     }
1589a8ff24cSMiguel Ojeda 
1599a8ff24cSMiguel Ojeda     /// Does the option exist in the configuration (any value)?
1609a8ff24cSMiguel Ojeda     ///
1619a8ff24cSMiguel Ojeda     /// The argument must be passed without the `CONFIG_` prefix.
1629a8ff24cSMiguel Ojeda     /// This avoids repetition and it also avoids `fixdep` making us
1639a8ff24cSMiguel Ojeda     /// depend on it.
has(&self, option: &str) -> bool1649a8ff24cSMiguel Ojeda     fn has(&self, option: &str) -> bool {
1659a8ff24cSMiguel Ojeda         let option = "CONFIG_".to_owned() + option;
1669a8ff24cSMiguel Ojeda         self.0.contains_key(&option)
1679a8ff24cSMiguel Ojeda     }
1689a8ff24cSMiguel Ojeda 
1699a8ff24cSMiguel Ojeda     /// Is the rustc version at least `major.minor.patch`?
rustc_version_atleast(&self, major: u32, minor: u32, patch: u32) -> bool1709a8ff24cSMiguel Ojeda     fn rustc_version_atleast(&self, major: u32, minor: u32, patch: u32) -> bool {
1719a8ff24cSMiguel Ojeda         let check_version = 100000 * major + 100 * minor + patch;
1729a8ff24cSMiguel Ojeda         let actual_version = self
1739a8ff24cSMiguel Ojeda             .0
1749a8ff24cSMiguel Ojeda             .get("CONFIG_RUSTC_VERSION")
175*ccb8ce52SChristian Schrrefl             .unwrap()
176*ccb8ce52SChristian Schrrefl             .parse::<u32>()
177*ccb8ce52SChristian Schrrefl             .unwrap();
178724a75acSJamie Cunliffe         check_version <= actual_version
17970a57b24SMiguel Ojeda     }
18070a57b24SMiguel Ojeda }
18170a57b24SMiguel Ojeda 
main()18270a57b24SMiguel Ojeda fn main() {
18370a57b24SMiguel Ojeda     let cfg = KernelConfig::from_stdin();
18470a57b24SMiguel Ojeda     let mut ts = TargetSpec::new();
185724a75acSJamie Cunliffe 
18609498135SMiguel Ojeda     // `llvm-target`s are taken from `scripts/Makefile.clang`.
18709498135SMiguel Ojeda     if cfg.has("ARM") {
18809498135SMiguel Ojeda         panic!("arm uses the builtin rustc target");
18956f64b37SMiguel Ojeda     } else if cfg.has("ARM64") {
19009498135SMiguel Ojeda         panic!("arm64 uses the builtin rustc aarch64-unknown-none target");
1910eba65f0SMiguel Ojeda     } else if cfg.has("RISCV") {
192aefb2f2eSBreno Leitao         if cfg.has("64BIT") {
193284a3ac4SMiguel Ojeda             panic!("64-bit RISC-V uses the builtin rustc riscv64-unknown-none-elf target");
194284a3ac4SMiguel Ojeda         } else {
195284a3ac4SMiguel Ojeda             panic!("32-bit RISC-V is an unsupported architecture");
196284a3ac4SMiguel Ojeda         }
197284a3ac4SMiguel Ojeda     } else if cfg.has("X86_64") {
19809498135SMiguel Ojeda         ts.push("arch", "x86_64");
199284a3ac4SMiguel Ojeda         if cfg.rustc_version_atleast(1, 86, 0) {
200284a3ac4SMiguel Ojeda             ts.push("rustc-abi", "x86-softfloat");
20109498135SMiguel Ojeda         }
202fc582dfcSMiguel Ojeda         ts.push(
203fc582dfcSMiguel Ojeda             "data-layout",
204fc582dfcSMiguel Ojeda             "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
205fc582dfcSMiguel Ojeda         );
206fc582dfcSMiguel Ojeda         let mut features = "-mmx,+soft-float".to_string();
207fc582dfcSMiguel Ojeda         if cfg.has("MITIGATION_RETPOLINE") {
208fc582dfcSMiguel Ojeda             // The kernel uses `-mretpoline-external-thunk` (for Clang), which Clang maps to the
209fc582dfcSMiguel Ojeda             // target feature of the same name plus the other two target features in
21009498135SMiguel Ojeda             // `clang/lib/Driver/ToolChains/Arch/X86.cpp`. These should be eventually enabled via
21109498135SMiguel Ojeda             // `-Ctarget-feature` when `rustc` starts recognizing them (or via a new dedicated
212e3117404SMatthew Maurer             // flag); see https://github.com/rust-lang/rust/issues/116852.
21309498135SMiguel Ojeda             features += ",+retpoline-external-thunk";
214ab0f4cedSDavid Gow             features += ",+retpoline-indirect-branches";
215ab0f4cedSDavid Gow             features += ",+retpoline-indirect-calls";
216ab0f4cedSDavid Gow         }
217ab0f4cedSDavid Gow         if cfg.has("MITIGATION_SLS") {
218ab0f4cedSDavid Gow             // The kernel uses `-mharden-sls=all`, which Clang maps to both these target features in
219ab0f4cedSDavid Gow             // `clang/lib/Driver/ToolChains/Arch/X86.cpp`. These should be eventually enabled via
220ab0f4cedSDavid Gow             // `-Ctarget-feature` when `rustc` starts recognizing them (or via a new dedicated
221ab0f4cedSDavid Gow             // flag); see https://github.com/rust-lang/rust/issues/116851.
222ab0f4cedSDavid Gow             features += ",+harden-sls-ijmp";
223ab0f4cedSDavid Gow             features += ",+harden-sls-ret";
2240eba65f0SMiguel Ojeda         }
225ab0f4cedSDavid Gow         ts.push("features", features);
226ab0f4cedSDavid Gow         ts.push("llvm-target", "x86_64-linux-gnu");
227ab0f4cedSDavid Gow         ts.push("supported-sanitizers", ["kcfi", "kernel-address"]);
228ab0f4cedSDavid Gow         ts.push("target-pointer-width", "64");
229ab0f4cedSDavid Gow     } else if cfg.has("X86_32") {
230ab0f4cedSDavid Gow         // This only works on UML, as i386 otherwise needs regparm support in rustc
23190868ff9SWANG Rui         if !cfg.has("UML") {
2328f8d74eeSWANG Rui             panic!("32-bit x86 only works under UML");
2339a8ff24cSMiguel Ojeda         }
2349a8ff24cSMiguel Ojeda         ts.push("arch", "x86");
2359a8ff24cSMiguel Ojeda         if cfg.rustc_version_atleast(1, 86, 0) {
2369a8ff24cSMiguel Ojeda             ts.push("rustc-abi", "x86-softfloat");
2379a8ff24cSMiguel Ojeda         }
2389a8ff24cSMiguel Ojeda         ts.push(
2399a8ff24cSMiguel Ojeda             "data-layout",
2409a8ff24cSMiguel Ojeda             "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i128:128-f64:32:64-f80:32-n8:16:32-S128",
2419a8ff24cSMiguel Ojeda         );
2429a8ff24cSMiguel Ojeda         let mut features = "-mmx,+soft-float".to_string();
2439a8ff24cSMiguel Ojeda         if cfg.has("MITIGATION_RETPOLINE") {
2449a8ff24cSMiguel Ojeda             features += ",+retpoline-external-thunk";
2459a8ff24cSMiguel Ojeda         }
2469a8ff24cSMiguel Ojeda         ts.push("features", features);
2479a8ff24cSMiguel Ojeda         ts.push("llvm-target", "i386-unknown-linux-gnu");
2489a8ff24cSMiguel Ojeda         ts.push("target-pointer-width", "32");
2499a8ff24cSMiguel Ojeda     } else if cfg.has("LOONGARCH") {
2509a8ff24cSMiguel Ojeda         panic!("loongarch uses the builtin rustc loongarch64-unknown-none-softfloat target");
2519a8ff24cSMiguel Ojeda     } else {
252         panic!("Unsupported architecture");
253     }
254 
255     ts.push("emit-debug-gdb-scripts", false);
256     ts.push("frame-pointer", "may-omit");
257     ts.push(
258         "stack-probes",
259         vec![("kind".to_string(), Value::String("none".to_string()))],
260     );
261 
262     // Everything else is LE, whether `CPU_LITTLE_ENDIAN` is declared or not
263     // (e.g. x86). It is also `rustc`'s default.
264     if cfg.has("CPU_BIG_ENDIAN") {
265         ts.push("target-endian", "big");
266     }
267 
268     println!("{}", ts);
269 }
270