| 1 | |
| 2 |
|
| 3 | |
| 4 | |
| 5 | |
| 6 | |
| 7 | |
| 8 | |
| 9 | |
| 10 | |
| 11 | |
| 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 | Array(Vec<Value>),
|
| 24 | Object(Object),
|
| 25 | }
|
| 26 |
|
| 27 | type Object = Vec<(String, Value)>;
|
| 28 |
|
| 29 | fn comma_sep<T>(
|
| 30 | seq: &[T],
|
| 31 | formatter: &mut Formatter<'_>,
|
| 32 | f: impl Fn(&mut Formatter<'_>, &T) -> Result,
|
| 33 | ) -> Result {
|
| 34 | if let [ref rest @ .., ref last] = seq[..] {
|
| 35 | for v in rest {
|
| 36 | f(formatter, v)?;
|
| 37 | formatter.write_str(",")?;
|
| 38 | }
|
| 39 | f(formatter, last)?;
|
| 40 | }
|
| 41 | Ok(())
|
| 42 | }
|
| 43 |
|
| 44 | |
| 45 | |
| 46 | impl Display for Value {
|
| 47 | fn fmt(&self, formatter: &mut Formatter<'_>) -> Result {
|
| 48 | match self {
|
| 49 | Value::Boolean(boolean) => write!(formatter, "{}", boolean),
|
| 50 | Value::Number(number) => write!(formatter, "{}", number),
|
| 51 | Value::String(string) => write!(formatter, "\"{}\"", string),
|
| 52 | Value::Array(values) => {
|
| 53 | formatter.write_str("[")?;
|
| 54 | comma_sep(&values[..], formatter, |formatter, v| v.fmt(formatter))?;
|
| 55 | formatter.write_str("]")
|
| 56 | }
|
| 57 | Value::Object(object) => {
|
| 58 | formatter.write_str("{")?;
|
| 59 | comma_sep(&object[..], formatter, |formatter, v| {
|
| 60 | write!(formatter, "\"{}\": {}", v.0, v.1)
|
| 61 | })?;
|
| 62 | formatter.write_str("}")
|
| 63 | }
|
| 64 | }
|
| 65 | }
|
| 66 | }
|
| 67 |
|
| 68 | impl From<bool> for Value {
|
| 69 | fn from(value: bool) -> Self {
|
| 70 | Self::Boolean(value)
|
| 71 | }
|
| 72 | }
|
| 73 |
|
| 74 | impl From<i32> for Value {
|
| 75 | fn from(value: i32) -> Self {
|
| 76 | Self::Number(value)
|
| 77 | }
|
| 78 | }
|
| 79 |
|
| 80 | impl From<String> for Value {
|
| 81 | fn from(value: String) -> Self {
|
| 82 | Self::String(value)
|
| 83 | }
|
| 84 | }
|
| 85 |
|
| 86 | impl From<&str> for Value {
|
| 87 | fn from(value: &str) -> Self {
|
| 88 | Self::String(value.to_string())
|
| 89 | }
|
| 90 | }
|
| 91 |
|
| 92 | impl From<Object> for Value {
|
| 93 | fn from(object: Object) -> Self {
|
| 94 | Self::Object(object)
|
| 95 | }
|
| 96 | }
|
| 97 |
|
| 98 | impl<T: Into<Value>, const N: usize> From<[T; N]> for Value {
|
| 99 | fn from(i: [T; N]) -> Self {
|
| 100 | Self::Array(i.into_iter().map(|v| v.into()).collect())
|
| 101 | }
|
| 102 | }
|
| 103 |
|
| 104 | struct TargetSpec(Object);
|
| 105 |
|
| 106 | impl TargetSpec {
|
| 107 | fn new() -> TargetSpec {
|
| 108 | TargetSpec(Vec::new())
|
| 109 | }
|
| 110 |
|
| 111 | fn push(&mut self, key: &str, value: impl Into<Value>) {
|
| 112 | self.0.push((key.to_string(), value.into()));
|
| 113 | }
|
| 114 | }
|
| 115 |
|
| 116 | impl Display for TargetSpec {
|
| 117 | fn fmt(&self, formatter: &mut Formatter<'_>) -> Result {
|
| 118 | |
| 119 | formatter.write_str("{\n")?;
|
| 120 | if let [ref rest @ .., ref last] = self.0[..] {
|
| 121 | for (key, value) in rest {
|
| 122 | write!(formatter, " \"{}\": {},\n", key, value)?;
|
| 123 | }
|
| 124 | write!(formatter, " \"{}\": {}\n", last.0, last.1)?;
|
| 125 | }
|
| 126 | formatter.write_str("}")
|
| 127 | }
|
| 128 | }
|
| 129 |
|
| 130 | struct KernelConfig(HashMap<String, String>);
|
| 131 |
|
| 132 | impl KernelConfig {
|
| 133 | |
| 134 | fn from_stdin() -> KernelConfig {
|
| 135 | let mut result = HashMap::new();
|
| 136 |
|
| 137 | let stdin = std::io::stdin();
|
| 138 | let mut handle = stdin.lock();
|
| 139 | let mut line = String::new();
|
| 140 |
|
| 141 | loop {
|
| 142 | line.clear();
|
| 143 |
|
| 144 | if handle.read_line(&mut line).unwrap() == 0 {
|
| 145 | break;
|
| 146 | }
|
| 147 |
|
| 148 | if line.starts_with('#') {
|
| 149 | continue;
|
| 150 | }
|
| 151 |
|
| 152 | let (key, value) = line.split_once('=').expect("Missing `=` in line.");
|
| 153 | result.insert(key.to_string(), value.trim_end_matches('\n').to_string());
|
| 154 | }
|
| 155 |
|
| 156 | KernelConfig(result)
|
| 157 | }
|
| 158 |
|
| 159 | |
| 160 | |
| 161 | |
| 162 | |
| 163 | |
| 164 | fn has(&self, option: &str) -> bool {
|
| 165 | let option = "CONFIG_".to_owned() + option;
|
| 166 | self.0.contains_key(&option)
|
| 167 | }
|
| 168 |
|
| 169 | |
| 170 | fn rustc_version_atleast(&self, major: u32, minor: u32, patch: u32) -> bool {
|
| 171 | let check_version = 100000 * major + 100 * minor + patch;
|
| 172 | let actual_version = self
|
| 173 | .0
|
| 174 | .get("CONFIG_RUSTC_VERSION")
|
| 175 | .unwrap()
|
| 176 | .parse::<u32>()
|
| 177 | .unwrap();
|
| 178 | check_version <= actual_version
|
| 179 | }
|
| 180 | }
|
| 181 |
|
| 182 | fn main() {
|
| 183 | let cfg = KernelConfig::from_stdin();
|
| 184 | let mut ts = TargetSpec::new();
|
| 185 |
|
| 186 | |
| 187 | if cfg.has("ARM") {
|
| 188 | panic!("arm uses the builtin rustc target");
|
| 189 | } else if cfg.has("ARM64") {
|
| 190 | panic!("arm64 uses the builtin rustc aarch64-unknown-none target");
|
| 191 | } else if cfg.has("RISCV") {
|
| 192 | if cfg.has("64BIT") {
|
| 193 | panic!("64-bit RISC-V uses the builtin rustc riscv64-unknown-none-elf target");
|
| 194 | } else {
|
| 195 | panic!("32-bit RISC-V is an unsupported architecture");
|
| 196 | }
|
| 197 | } else if cfg.has("X86_64") {
|
| 198 | ts.push("arch", "x86_64");
|
| 199 | if cfg.rustc_version_atleast(1, 98, 0) {
|
| 200 | ts.push("rustc-abi", "softfloat");
|
| 201 | } else if cfg.rustc_version_atleast(1, 86, 0) {
|
| 202 | ts.push("rustc-abi", "x86-softfloat");
|
| 203 | }
|
| 204 | ts.push(
|
| 205 | "data-layout",
|
| 206 | "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
|
| 207 | );
|
| 208 | let mut features = "-mmx,+soft-float".to_string();
|
| 209 | if cfg.has("MITIGATION_RETPOLINE") {
|
| 210 | |
| 211 | |
| 212 | |
| 213 | |
| 214 | |
| 215 | features += ",+retpoline-external-thunk";
|
| 216 | features += ",+retpoline-indirect-branches";
|
| 217 | features += ",+retpoline-indirect-calls";
|
| 218 | }
|
| 219 | if cfg.has("MITIGATION_SLS") {
|
| 220 | |
| 221 | |
| 222 | |
| 223 | |
| 224 | features += ",+harden-sls-ijmp";
|
| 225 | features += ",+harden-sls-ret";
|
| 226 | }
|
| 227 | if cfg.has("X86_NATIVE_CPU") {
|
| 228 | |
| 229 | |
| 230 | features += ",-apxf";
|
| 231 | }
|
| 232 | ts.push("features", features);
|
| 233 | ts.push("llvm-target", "x86_64-linux-gnu");
|
| 234 | ts.push("supported-sanitizers", ["kcfi", "kernel-address"]);
|
| 235 | if cfg.rustc_version_atleast(1, 91, 0) {
|
| 236 | ts.push("target-pointer-width", 64);
|
| 237 | } else {
|
| 238 | ts.push("target-pointer-width", "64");
|
| 239 | }
|
| 240 | } else if cfg.has("X86_32") {
|
| 241 | |
| 242 | if !cfg.has("UML") {
|
| 243 | panic!("32-bit x86 only works under UML");
|
| 244 | }
|
| 245 | ts.push("arch", "x86");
|
| 246 | if cfg.rustc_version_atleast(1, 98, 0) {
|
| 247 | ts.push("rustc-abi", "softfloat");
|
| 248 | } else if cfg.rustc_version_atleast(1, 86, 0) {
|
| 249 | ts.push("rustc-abi", "x86-softfloat");
|
| 250 | }
|
| 251 | ts.push(
|
| 252 | "data-layout",
|
| 253 | "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",
|
| 254 | );
|
| 255 | let mut features = "-mmx,+soft-float".to_string();
|
| 256 | if cfg.has("MITIGATION_RETPOLINE") {
|
| 257 | features += ",+retpoline-external-thunk";
|
| 258 | }
|
| 259 | ts.push("features", features);
|
| 260 | ts.push("llvm-target", "i386-unknown-linux-gnu");
|
| 261 | if cfg.rustc_version_atleast(1, 91, 0) {
|
| 262 | ts.push("target-pointer-width", 32);
|
| 263 | } else {
|
| 264 | ts.push("target-pointer-width", "32");
|
| 265 | }
|
| 266 | } else if cfg.has("LOONGARCH") {
|
| 267 | panic!("loongarch uses the builtin rustc loongarch64-unknown-none-softfloat target");
|
| 268 | } else if cfg.has("S390") {
|
| 269 | panic!("s390 uses the builtin rustc s390x-unknown-none-softfloat target");
|
| 270 | } else {
|
| 271 | panic!("Unsupported architecture");
|
| 272 | }
|
| 273 |
|
| 274 | ts.push("emit-debug-gdb-scripts", false);
|
| 275 | ts.push("frame-pointer", "may-omit");
|
| 276 | ts.push(
|
| 277 | "stack-probes",
|
| 278 | vec![("kind".to_string(), Value::String("none".to_string()))],
|
| 279 | );
|
| 280 |
|
| 281 | |
| 282 | |
| 283 | if cfg.has("CPU_BIG_ENDIAN") {
|
| 284 | ts.push("target-endian", "big");
|
| 285 | }
|
| 286 |
|
| 287 | println!("{}", ts);
|
| 288 | }
|