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