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("ARM64") { 152 panic!("arm64 uses the builtin rustc aarch64-unknown-none target"); 153 } else if cfg.has("X86_64") { 154 ts.push("arch", "x86_64"); 155 ts.push( 156 "data-layout", 157 "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128", 158 ); 159 let mut features = "-3dnow,-3dnowa,-mmx,+soft-float".to_string(); 160 if cfg.has("MITIGATION_RETPOLINE") { 161 features += ",+retpoline-external-thunk"; 162 } 163 ts.push("features", features); 164 ts.push("llvm-target", "x86_64-linux-gnu"); 165 ts.push("target-pointer-width", "64"); 166 } else if cfg.has("LOONGARCH") { 167 ts.push("arch", "loongarch64"); 168 ts.push("data-layout", "e-m:e-p:64:64-i64:64-i128:128-n64-S128"); 169 ts.push("features", "-f,-d"); 170 ts.push("llvm-target", "loongarch64-linux-gnusf"); 171 ts.push("llvm-abiname", "lp64s"); 172 ts.push("target-pointer-width", "64"); 173 } else { 174 panic!("Unsupported architecture"); 175 } 176 177 ts.push("emit-debug-gdb-scripts", false); 178 ts.push("frame-pointer", "may-omit"); 179 ts.push( 180 "stack-probes", 181 vec![("kind".to_string(), Value::String("none".to_string()))], 182 ); 183 184 // Everything else is LE, whether `CPU_LITTLE_ENDIAN` is declared or not 185 // (e.g. x86). It is also `rustc`'s default. 186 if cfg.has("CPU_BIG_ENDIAN") { 187 ts.push("target-endian", "big"); 188 } 189 190 println!("{}", ts); 191 } 192