generate_rust_target.rs 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. // SPDX-License-Identifier: GPL-2.0
  2. //! The custom target specification file generator for `rustc`.
  3. //!
  4. //! To configure a target from scratch, a JSON-encoded file has to be passed
  5. //! to `rustc` (introduced in [RFC 131]). These options and the file itself are
  6. //! unstable. Eventually, `rustc` should provide a way to do this in a stable
  7. //! manner. For instance, via command-line arguments. Therefore, this file
  8. //! should avoid using keys which can be set via `-C` or `-Z` options.
  9. //!
  10. //! [RFC 131]: https://rust-lang.github.io/rfcs/0131-target-specification.html
  11. use std::{
  12. collections::HashMap,
  13. fmt::{Display, Formatter, Result},
  14. io::BufRead,
  15. };
  16. enum Value {
  17. Boolean(bool),
  18. Number(i32),
  19. String(String),
  20. Array(Vec<Value>),
  21. Object(Object),
  22. }
  23. type Object = Vec<(String, Value)>;
  24. fn comma_sep<T>(
  25. seq: &[T],
  26. formatter: &mut Formatter<'_>,
  27. f: impl Fn(&mut Formatter<'_>, &T) -> Result,
  28. ) -> Result {
  29. if let [ref rest @ .., ref last] = seq[..] {
  30. for v in rest {
  31. f(formatter, v)?;
  32. formatter.write_str(",")?;
  33. }
  34. f(formatter, last)?;
  35. }
  36. Ok(())
  37. }
  38. /// Minimal "almost JSON" generator (e.g. no `null`s, no escaping),
  39. /// enough for this purpose.
  40. impl Display for Value {
  41. fn fmt(&self, formatter: &mut Formatter<'_>) -> Result {
  42. match self {
  43. Value::Boolean(boolean) => write!(formatter, "{}", boolean),
  44. Value::Number(number) => write!(formatter, "{}", number),
  45. Value::String(string) => write!(formatter, "\"{}\"", string),
  46. Value::Array(values) => {
  47. formatter.write_str("[")?;
  48. comma_sep(&values[..], formatter, |formatter, v| v.fmt(formatter))?;
  49. formatter.write_str("]")
  50. }
  51. Value::Object(object) => {
  52. formatter.write_str("{")?;
  53. comma_sep(&object[..], formatter, |formatter, v| {
  54. write!(formatter, "\"{}\": {}", v.0, v.1)
  55. })?;
  56. formatter.write_str("}")
  57. }
  58. }
  59. }
  60. }
  61. impl From<bool> for Value {
  62. fn from(value: bool) -> Self {
  63. Self::Boolean(value)
  64. }
  65. }
  66. impl From<i32> for Value {
  67. fn from(value: i32) -> Self {
  68. Self::Number(value)
  69. }
  70. }
  71. impl From<String> for Value {
  72. fn from(value: String) -> Self {
  73. Self::String(value)
  74. }
  75. }
  76. impl From<&str> for Value {
  77. fn from(value: &str) -> Self {
  78. Self::String(value.to_string())
  79. }
  80. }
  81. impl From<Object> for Value {
  82. fn from(object: Object) -> Self {
  83. Self::Object(object)
  84. }
  85. }
  86. impl<T: Into<Value>, const N: usize> From<[T; N]> for Value {
  87. fn from(i: [T; N]) -> Self {
  88. Self::Array(i.into_iter().map(|v| v.into()).collect())
  89. }
  90. }
  91. struct TargetSpec(Object);
  92. impl TargetSpec {
  93. fn new() -> TargetSpec {
  94. TargetSpec(Vec::new())
  95. }
  96. fn push(&mut self, key: &str, value: impl Into<Value>) {
  97. self.0.push((key.to_string(), value.into()));
  98. }
  99. }
  100. impl Display for TargetSpec {
  101. fn fmt(&self, formatter: &mut Formatter<'_>) -> Result {
  102. // We add some newlines for clarity.
  103. formatter.write_str("{\n")?;
  104. if let [ref rest @ .., ref last] = self.0[..] {
  105. for (key, value) in rest {
  106. write!(formatter, " \"{}\": {},\n", key, value)?;
  107. }
  108. write!(formatter, " \"{}\": {}\n", last.0, last.1)?;
  109. }
  110. formatter.write_str("}")
  111. }
  112. }
  113. struct KernelConfig(HashMap<String, String>);
  114. impl KernelConfig {
  115. /// Parses `include/config/auto.conf` from `stdin`.
  116. fn from_stdin() -> KernelConfig {
  117. let mut result = HashMap::new();
  118. let stdin = std::io::stdin();
  119. let mut handle = stdin.lock();
  120. let mut line = String::new();
  121. loop {
  122. line.clear();
  123. if handle.read_line(&mut line).unwrap() == 0 {
  124. break;
  125. }
  126. if line.starts_with('#') {
  127. continue;
  128. }
  129. let (key, value) = line.split_once('=').expect("Missing `=` in line.");
  130. result.insert(key.to_string(), value.trim_end_matches('\n').to_string());
  131. }
  132. KernelConfig(result)
  133. }
  134. /// Does the option exist in the configuration (any value)?
  135. ///
  136. /// The argument must be passed without the `CONFIG_` prefix.
  137. /// This avoids repetition and it also avoids `fixdep` making us
  138. /// depend on it.
  139. fn has(&self, option: &str) -> bool {
  140. let option = "CONFIG_".to_owned() + option;
  141. self.0.contains_key(&option)
  142. }
  143. /// Is the rustc version at least `major.minor.patch`?
  144. fn rustc_version_atleast(&self, major: u32, minor: u32, patch: u32) -> bool {
  145. let check_version = 100000 * major + 100 * minor + patch;
  146. let actual_version = self
  147. .0
  148. .get("CONFIG_RUSTC_VERSION")
  149. .unwrap()
  150. .parse::<u32>()
  151. .unwrap();
  152. check_version <= actual_version
  153. }
  154. }
  155. fn main() {
  156. let cfg = KernelConfig::from_stdin();
  157. let mut ts = TargetSpec::new();
  158. // `llvm-target`s are taken from `scripts/Makefile.clang`.
  159. if cfg.has("ARM") {
  160. panic!("arm uses the builtin rustc target");
  161. } else if cfg.has("ARM64") {
  162. panic!("arm64 uses the builtin rustc aarch64-unknown-none target");
  163. } else if cfg.has("RISCV") {
  164. if cfg.has("64BIT") {
  165. panic!("64-bit RISC-V uses the builtin rustc riscv64-unknown-none-elf target");
  166. } else {
  167. panic!("32-bit RISC-V is an unsupported architecture");
  168. }
  169. } else if cfg.has("X86_64") {
  170. ts.push("arch", "x86_64");
  171. if cfg.rustc_version_atleast(1, 86, 0) {
  172. ts.push("rustc-abi", "x86-softfloat");
  173. }
  174. ts.push(
  175. "data-layout",
  176. "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
  177. );
  178. let mut features = "-mmx,+soft-float".to_string();
  179. if cfg.has("MITIGATION_RETPOLINE") {
  180. // The kernel uses `-mretpoline-external-thunk` (for Clang), which Clang maps to the
  181. // target feature of the same name plus the other two target features in
  182. // `clang/lib/Driver/ToolChains/Arch/X86.cpp`. These should be eventually enabled via
  183. // `-Ctarget-feature` when `rustc` starts recognizing them (or via a new dedicated
  184. // flag); see <https://github.com/rust-lang/rust/issues/116852>.
  185. features += ",+retpoline-external-thunk";
  186. features += ",+retpoline-indirect-branches";
  187. features += ",+retpoline-indirect-calls";
  188. }
  189. if cfg.has("MITIGATION_SLS") {
  190. // The kernel uses `-mharden-sls=all`, which Clang maps to both these target features in
  191. // `clang/lib/Driver/ToolChains/Arch/X86.cpp`. These should be eventually enabled via
  192. // `-Ctarget-feature` when `rustc` starts recognizing them (or via a new dedicated
  193. // flag); see <https://github.com/rust-lang/rust/issues/116851>.
  194. features += ",+harden-sls-ijmp";
  195. features += ",+harden-sls-ret";
  196. }
  197. ts.push("features", features);
  198. ts.push("llvm-target", "x86_64-linux-gnu");
  199. ts.push("supported-sanitizers", ["kcfi", "kernel-address"]);
  200. if cfg.rustc_version_atleast(1, 91, 0) {
  201. ts.push("target-pointer-width", 64);
  202. } else {
  203. ts.push("target-pointer-width", "64");
  204. }
  205. } else if cfg.has("X86_32") {
  206. // This only works on UML, as i386 otherwise needs regparm support in rustc
  207. if !cfg.has("UML") {
  208. panic!("32-bit x86 only works under UML");
  209. }
  210. ts.push("arch", "x86");
  211. if cfg.rustc_version_atleast(1, 86, 0) {
  212. ts.push("rustc-abi", "x86-softfloat");
  213. }
  214. ts.push(
  215. "data-layout",
  216. "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",
  217. );
  218. let mut features = "-mmx,+soft-float".to_string();
  219. if cfg.has("MITIGATION_RETPOLINE") {
  220. features += ",+retpoline-external-thunk";
  221. }
  222. ts.push("features", features);
  223. ts.push("llvm-target", "i386-unknown-linux-gnu");
  224. if cfg.rustc_version_atleast(1, 91, 0) {
  225. ts.push("target-pointer-width", 32);
  226. } else {
  227. ts.push("target-pointer-width", "32");
  228. }
  229. } else if cfg.has("LOONGARCH") {
  230. panic!("loongarch uses the builtin rustc loongarch64-unknown-none-softfloat target");
  231. } else {
  232. panic!("Unsupported architecture");
  233. }
  234. ts.push("emit-debug-gdb-scripts", false);
  235. ts.push("frame-pointer", "may-omit");
  236. ts.push(
  237. "stack-probes",
  238. vec![("kind".to_string(), Value::String("none".to_string()))],
  239. );
  240. // Everything else is LE, whether `CPU_LITTLE_ENDIAN` is declared or not
  241. // (e.g. x86). It is also `rustc`'s default.
  242. if cfg.has("CPU_BIG_ENDIAN") {
  243. ts.push("target-endian", "big");
  244. }
  245. println!("{}", ts);
  246. }