From afba78eacb9bf4ae389eacc1619e1954cdf47612 Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Thu, 24 Feb 2022 16:27:08 +0100 Subject: [PATCH] rust: generate target specification files on the fly Now that we have host programs support, add a "Rust script" to generate the `rustc` target spec file. The script has been written so that its output matches exactly the current `arch/*/rust/*.json` static target files we have at the moment, to have a baseline and reduce the changes done by this commit. Thus the static files are now gone. The build system is set up in a way that we do not need to rebuild any Rust code as long as the `target.json` contents do not change, even if the kernel configuration changes for something else. Furthermore, the script does not need to be rebuilt even if the kernel configuration changes. With this, `KBUILD_RUST_TARGET` is also gone -- one should modify the script instead of trying to override the target file. After this, the script should be cleaned (e.g. remove unneeded keys) and fixed to configure the architectures properly (e.g. enable CPU features as needed, etc.). There were several design choices: - The language: this could have been done in a shell script, C or Perl. But as soon as Rust is an option, it is more convenient than any of those. And it is an ideal showcase for the Rust host programs support. Python could have also been a natural option for the script, though it cannot be used as part as the kernel compilation because it is not required (as far as I can see in the docs). Even if it were, Rust offers some advantages anyway (though the `json` module would have been fairly useful here). Compiling the Rust host program takes a lot of time compared to other languages, at least currently (e.g. 300ms), but there is no need to recompile it on config changes, so it is fine. And, of course, running it is very fast. - Whether or not to use conditional compilation. Initially I took advantage of the `rustc_cfg` flags, to make the script look closer to the actual Rust kernel code. However, that means the script depends on the config, which means it is not an independent tool anymore (though there is at least one other host program that gets passed the flags, but it is not common), plus requires recompiling it every time the configuration changes just to generate the actual file. It also made things more tricky on the build system. - Splitting the logic into `arch/*` or not. In order to have arch maintainers work independently, we could move part of the logic into their trees. However, given how small the script is and that some logic is shared anyway, it looks a little bit odd. So I decided to avoid it. We can revisit this if needed. - Whether to use a proper JSON generator or not, whether to use a `HashMap`, whether to set up a type that knows about all the keys that can be generated (or maybe even import part of the `rustc` code to be in sync), etc. The script has been written to be as simple as possible while making it easy to change the actual business logic. We should reevaluate as it gets developed. Signed-off-by: Miguel Ojeda --- Documentation/kbuild/makefiles.rst | 3 + Makefile | 5 +- arch/arm/rust/target.json | 27 --- arch/arm64/rust/target.json | 34 ---- arch/powerpc/rust/target.json | 29 --- arch/riscv/Makefile | 1 - arch/riscv/rust/rv32ima.json | 36 ---- arch/riscv/rust/rv32imac.json | 36 ---- arch/riscv/rust/rv64ima.json | 36 ---- arch/riscv/rust/rv64imac.json | 36 ---- arch/x86/rust/target.json | 36 ---- rust/.gitignore | 1 + rust/Makefile | 11 +- scripts/.gitignore | 1 + scripts/Makefile | 3 + scripts/Makefile.build | 2 +- scripts/generate_rust_target.rs | 317 +++++++++++++++++++++++++++++ 17 files changed, 338 insertions(+), 276 deletions(-) delete mode 100644 arch/arm/rust/target.json delete mode 100644 arch/arm64/rust/target.json delete mode 100644 arch/powerpc/rust/target.json delete mode 100644 arch/riscv/rust/rv32ima.json delete mode 100644 arch/riscv/rust/rv32imac.json delete mode 100644 arch/riscv/rust/rv64ima.json delete mode 100644 arch/riscv/rust/rv64imac.json delete mode 100644 arch/x86/rust/target.json create mode 100644 scripts/generate_rust_target.rs diff --git a/Documentation/kbuild/makefiles.rst b/Documentation/kbuild/makefiles.rst index 21a8fbb9c103cb..1cd9b8ac90eee8 100644 --- a/Documentation/kbuild/makefiles.rst +++ b/Documentation/kbuild/makefiles.rst @@ -1205,6 +1205,9 @@ When kbuild executes, the following steps are followed (roughly): Often, the KBUILD_RUSTFLAGS variable depends on the configuration. + Note that target specification file generation (for ``--target``) + is handled in ``scripts/generate_rust_target.rs``. + KBUILD_AFLAGS_KERNEL Assembler options specific for built-in diff --git a/Makefile b/Makefile index fd1b9ed9a25680..077b34fb091bab 100644 --- a/Makefile +++ b/Makefile @@ -551,7 +551,6 @@ KBUILD_CFLAGS := -Wall -Wundef -Werror=strict-prototypes -Wno-trigraphs \ -Werror=return-type -Wno-format-security \ -std=gnu89 KBUILD_CPPFLAGS := -D__KERNEL__ -KBUILD_RUST_TARGET := $(srctree)/arch/$(SRCARCH)/rust/target.json KBUILD_RUSTFLAGS := $(rust_common_flags) \ -Cpanic=abort -Cembed-bitcode=n -Clto=n -Crpath=n \ -Cforce-unwind-tables=n -Ccodegen-units=1 \ @@ -592,7 +591,7 @@ export KBUILD_HOSTCXXFLAGS KBUILD_HOSTLDFLAGS KBUILD_HOSTLDLIBS LDFLAGS_MODULE export KBUILD_CPPFLAGS NOSTDINC_FLAGS LINUXINCLUDE OBJCOPYFLAGS KBUILD_LDFLAGS export KBUILD_CFLAGS CFLAGS_KERNEL CFLAGS_MODULE -export KBUILD_RUST_TARGET KBUILD_RUSTFLAGS RUSTFLAGS_KERNEL RUSTFLAGS_MODULE +export KBUILD_RUSTFLAGS RUSTFLAGS_KERNEL RUSTFLAGS_MODULE export KBUILD_AFLAGS AFLAGS_KERNEL AFLAGS_MODULE export KBUILD_AFLAGS_MODULE KBUILD_CFLAGS_MODULE KBUILD_RUSTFLAGS_MODULE KBUILD_LDFLAGS_MODULE export KBUILD_AFLAGS_KERNEL KBUILD_CFLAGS_KERNEL KBUILD_RUSTFLAGS_KERNEL @@ -1578,7 +1577,7 @@ MRPROPER_FILES += include/config include/generated \ certs/x509.genkey \ vmlinux-gdb.py \ *.spec \ - rust/libmacros.so + rust/target.json rust/libmacros.so # clean - Delete most, but leave enough to build external modules # diff --git a/arch/arm/rust/target.json b/arch/arm/rust/target.json deleted file mode 100644 index 3f845b8221dcd1..00000000000000 --- a/arch/arm/rust/target.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "arch": "arm", - "crt-static-respected": true, - "data-layout": "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", - "dynamic-linking": true, - "env": "gnu", - "executables": true, - "features": "+strict-align,+v6", - "function-sections": false, - "has-elf-tls": true, - "has-rpath": true, - "linker-is-gnu": true, - "llvm-target": "arm-unknown-linux-gnueabi", - "max-atomic-width": 64, - "os": "linux", - "position-independent-executables": true, - "pre-link-args": { - "gcc": [ - "-Wl,--as-needed", - "-Wl,-z,noexecstack" - ] - }, - "relocation-model": "static", - "target-family": "unix", - "target-mcount": "\u0001__gnu_mcount_nc", - "target-pointer-width": "32" -} diff --git a/arch/arm64/rust/target.json b/arch/arm64/rust/target.json deleted file mode 100644 index 09a264df26c7a1..00000000000000 --- a/arch/arm64/rust/target.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "arch": "aarch64", - "data-layout": "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128", - "disable-redzone": true, - "emit-debug-gdb-scripts": false, - "env": "gnu", - "features": "+strict-align,+neon,+fp-armv8", - "frame-pointer": "always", - "function-sections": false, - "linker-flavor": "gcc", - "linker-is-gnu": true, - "llvm-target": "aarch64-unknown-none", - "max-atomic-width": 128, - "needs-plt": true, - "os": "none", - "panic-strategy": "abort", - "position-independent-executables": true, - "pre-link-args": { - "gcc": [ - "-Wl,--as-needed", - "-Wl,-z,noexecstack", - "-m64" - ] - }, - "relocation-model": "static", - "relro-level": "full", - "stack-probes": { - "kind": "none" - }, - "target-c-int-width": "32", - "target-endian": "little", - "target-pointer-width": "64", - "vendor": "" -} diff --git a/arch/powerpc/rust/target.json b/arch/powerpc/rust/target.json deleted file mode 100644 index 2420c8e6a5203a..00000000000000 --- a/arch/powerpc/rust/target.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "arch": "powerpc64", - "code-model": "large", - "cpu": "ppc64le", - "data-layout": "e-m:e-i64:64-n32:64", - "env": "gnu", - "features": "-altivec,-vsx,-hard-float", - "function-sections": false, - "linker-flavor": "gcc", - "linker-is-gnu": true, - "llvm-target": "powerpc64le-elf", - "max-atomic-width": 64, - "os": "none", - "panic-strategy": "abort", - "position-independent-executables": true, - "pre-link-args": { - "gcc": [ - "-Wl,--as-needed", - "-Wl,-z,noexecstack", - "-m64" - ] - }, - "relocation-model": "static", - "relro-level": "full", - "target-family": "unix", - "target-mcount": "_mcount", - "target-endian": "little", - "target-pointer-width": "64" -} diff --git a/arch/riscv/Makefile b/arch/riscv/Makefile index 20fe32c7c5f12f..7d81102cffd48e 100644 --- a/arch/riscv/Makefile +++ b/arch/riscv/Makefile @@ -58,7 +58,6 @@ riscv-march-$(toolchain-need-zicsr-zifencei) := $(riscv-march-y)_zicsr_zifencei KBUILD_CFLAGS += -march=$(subst fd,,$(riscv-march-y)) KBUILD_AFLAGS += -march=$(riscv-march-y) -KBUILD_RUST_TARGET := $(srctree)/arch/riscv/rust/$(subst fd,,$(riscv-march-y)).json KBUILD_CFLAGS += -mno-save-restore KBUILD_CFLAGS += -DCONFIG_PAGE_OFFSET=$(CONFIG_PAGE_OFFSET) diff --git a/arch/riscv/rust/rv32ima.json b/arch/riscv/rust/rv32ima.json deleted file mode 100644 index bcdda88c160415..00000000000000 --- a/arch/riscv/rust/rv32ima.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "arch": "riscv32", - "code-model": "medium", - "cpu": "generic-rv32", - "data-layout": "e-m:e-p:32:32-i64:64-n32-S128", - "disable-redzone": true, - "emit-debug-gdb-scripts": false, - "env": "gnu", - "features": "+m,+a", - "frame-pointer": "always", - "function-sections": false, - "linker-flavor": "gcc", - "linker-is-gnu": true, - "llvm-target": "riscv32", - "max-atomic-width": 32, - "needs-plt": true, - "os": "none", - "panic-strategy": "abort", - "position-independent-executables": true, - "pre-link-args": { - "gcc": [ - "-Wl,--as-needed", - "-Wl,-z,noexecstack", - "-m32" - ] - }, - "relocation-model": "static", - "relro-level": "full", - "stack-probes": { - "kind": "none" - }, - "target-c-int-width": "32", - "target-endian": "little", - "target-pointer-width": "32", - "vendor": "" -} diff --git a/arch/riscv/rust/rv32imac.json b/arch/riscv/rust/rv32imac.json deleted file mode 100644 index 45873c10a5c332..00000000000000 --- a/arch/riscv/rust/rv32imac.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "arch": "riscv32", - "code-model": "medium", - "cpu": "generic-rv32", - "data-layout": "e-m:e-p:32:32-i64:64-n32-S128", - "disable-redzone": true, - "emit-debug-gdb-scripts": false, - "env": "gnu", - "features": "+m,+a,+c", - "frame-pointer": "always", - "function-sections": false, - "linker-flavor": "gcc", - "linker-is-gnu": true, - "llvm-target": "riscv32", - "max-atomic-width": 32, - "needs-plt": true, - "os": "none", - "panic-strategy": "abort", - "position-independent-executables": true, - "pre-link-args": { - "gcc": [ - "-Wl,--as-needed", - "-Wl,-z,noexecstack", - "-m32" - ] - }, - "relocation-model": "static", - "relro-level": "full", - "stack-probes": { - "kind": "none" - }, - "target-c-int-width": "32", - "target-endian": "little", - "target-pointer-width": "32", - "vendor": "" -} diff --git a/arch/riscv/rust/rv64ima.json b/arch/riscv/rust/rv64ima.json deleted file mode 100644 index 853d758c54610f..00000000000000 --- a/arch/riscv/rust/rv64ima.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "arch": "riscv64", - "code-model": "medium", - "cpu": "generic-rv64", - "data-layout": "e-m:e-p:64:64-i64:64-i128:128-n64-S128", - "disable-redzone": true, - "emit-debug-gdb-scripts": false, - "env": "gnu", - "features": "+m,+a", - "frame-pointer": "always", - "function-sections": false, - "linker-flavor": "gcc", - "linker-is-gnu": true, - "llvm-target": "riscv64", - "max-atomic-width": 64, - "needs-plt": true, - "os": "none", - "panic-strategy": "abort", - "position-independent-executables": true, - "pre-link-args": { - "gcc": [ - "-Wl,--as-needed", - "-Wl,-z,noexecstack", - "-m64" - ] - }, - "relocation-model": "static", - "relro-level": "full", - "stack-probes": { - "kind": "none" - }, - "target-c-int-width": "32", - "target-endian": "little", - "target-pointer-width": "64", - "vendor": "" -} diff --git a/arch/riscv/rust/rv64imac.json b/arch/riscv/rust/rv64imac.json deleted file mode 100644 index ce50ee8e8c9307..00000000000000 --- a/arch/riscv/rust/rv64imac.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "arch": "riscv64", - "code-model": "medium", - "cpu": "generic-rv64", - "data-layout": "e-m:e-p:64:64-i64:64-i128:128-n64-S128", - "disable-redzone": true, - "emit-debug-gdb-scripts": false, - "env": "gnu", - "features": "+m,+a,+c", - "frame-pointer": "always", - "function-sections": false, - "linker-flavor": "gcc", - "linker-is-gnu": true, - "llvm-target": "riscv64", - "max-atomic-width": 64, - "needs-plt": true, - "os": "none", - "panic-strategy": "abort", - "position-independent-executables": true, - "pre-link-args": { - "gcc": [ - "-Wl,--as-needed", - "-Wl,-z,noexecstack", - "-m64" - ] - }, - "relocation-model": "static", - "relro-level": "full", - "stack-probes": { - "kind": "none" - }, - "target-c-int-width": "32", - "target-endian": "little", - "target-pointer-width": "64", - "vendor": "" -} diff --git a/arch/x86/rust/target.json b/arch/x86/rust/target.json deleted file mode 100644 index 379cf39e8941be..00000000000000 --- a/arch/x86/rust/target.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "arch": "x86_64", - "code-model": "kernel", - "cpu": "x86-64", - "data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128", - "disable-redzone": true, - "emit-debug-gdb-scripts": false, - "env": "gnu", - "features": "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-3dnow,-3dnowa,-avx,-avx2,+soft-float", - "frame-pointer": "always", - "function-sections": false, - "linker-flavor": "gcc", - "linker-is-gnu": true, - "llvm-target": "x86_64-elf", - "max-atomic-width": 64, - "needs-plt": true, - "os": "none", - "panic-strategy": "abort", - "position-independent-executables": true, - "pre-link-args": { - "gcc": [ - "-Wl,--as-needed", - "-Wl,-z,noexecstack", - "-m64" - ] - }, - "relocation-model": "static", - "relro-level": "full", - "stack-probes": { - "kind": "none" - }, - "target-c-int-width": "32", - "target-endian": "little", - "target-pointer-width": "64", - "vendor": "unknown" -} diff --git a/rust/.gitignore b/rust/.gitignore index 168cb26a31b999..9bd1af8e05a163 100644 --- a/rust/.gitignore +++ b/rust/.gitignore @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 +target.json bindings_generated.rs bindings_helpers_generated.rs exports_*_generated.h diff --git a/rust/Makefile b/rust/Makefile index f78dba9b3db63a..523abfbafacab5 100644 --- a/rust/Makefile +++ b/rust/Makefile @@ -1,5 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 +always-$(CONFIG_RUST) += target.json +no-clean-files += target.json + obj-$(CONFIG_RUST) += core.o compiler_builtins.o always-$(CONFIG_RUST) += exports_core_generated.h @@ -206,6 +209,11 @@ rusttest-kernel: $(srctree)/rust/kernel/lib.rs rusttest-prepare \ $(call if_changed,rustc_test_library) $(call if_changed,rustdoc_test) +filechk_rust_target = $(objtree)/scripts/generate_rust_target < $< + +$(objtree)/rust/target.json: $(objtree)/include/config/auto.conf FORCE + $(call filechk,rust_target) + ifdef CONFIG_CC_IS_CLANG bindgen_c_flags = $(c_flags) else @@ -364,7 +372,8 @@ $(objtree)/rust/kernel.o: $(srctree)/rust/kernel/lib.rs $(objtree)/rust/alloc.o $(objtree)/rust/core.o: private skip_clippy = 1 $(objtree)/rust/core.o: private skip_flags = -Dunreachable_pub --edition=2021 $(objtree)/rust/core.o: private rustc_target_flags = $(core-cfgs) --edition=2018 -$(objtree)/rust/core.o: $(RUST_LIB_SRC)/core/src/lib.rs FORCE +$(objtree)/rust/core.o: $(RUST_LIB_SRC)/core/src/lib.rs \ + $(objtree)/rust/target.json FORCE $(call if_changed_dep,rustc_library) rustdoc-core: private rustc_target_flags = $(core-cfgs) diff --git a/scripts/.gitignore b/scripts/.gitignore index eed308bef604a1..b7aec8eb1bd443 100644 --- a/scripts/.gitignore +++ b/scripts/.gitignore @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only /asn1_compiler /bin2c +/generate_rust_target /insert-sys-cert /kallsyms /module.lds diff --git a/scripts/Makefile b/scripts/Makefile index ce5aa9030b740a..a278345e7820af 100644 --- a/scripts/Makefile +++ b/scripts/Makefile @@ -10,6 +10,9 @@ hostprogs-always-$(CONFIG_BUILDTIME_TABLE_SORT) += sorttable hostprogs-always-$(CONFIG_ASN1) += asn1_compiler hostprogs-always-$(CONFIG_MODULE_SIG_FORMAT) += sign-file hostprogs-always-$(CONFIG_SYSTEM_EXTRA_CERTIFICATE) += insert-sys-cert +hostprogs-always-$(CONFIG_RUST) += generate_rust_target + +generate_rust_target-rust := y HOSTCFLAGS_sorttable.o = -I$(srctree)/tools/include HOSTLDLIBS_sorttable = -lpthread diff --git a/scripts/Makefile.build b/scripts/Makefile.build index d72fd2987cdd17..6f0aa115934437 100644 --- a/scripts/Makefile.build +++ b/scripts/Makefile.build @@ -328,7 +328,7 @@ $(obj)/%.lst: $(src)/%.c FORCE # Compile Rust sources (.rs) # --------------------------------------------------------------------------- -rust_cross_flags := --target=$(KBUILD_RUST_TARGET) +rust_cross_flags := --target=$(objtree)/rust/target.json rust_allowed_features := allocator_api,bench_black_box,concat_idents,generic_associated_types rust_common_cmd = \ diff --git a/scripts/generate_rust_target.rs b/scripts/generate_rust_target.rs new file mode 100644 index 00000000000000..b4605d5933b21f --- /dev/null +++ b/scripts/generate_rust_target.rs @@ -0,0 +1,317 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! The custom target specification file generator for `rustc`. +//! +//! To configure a target from scratch, a JSON-encoded file has to be passed +//! to `rustc` (introduced in [RFC 131]). These options and the file itself are +//! unstable. Eventually, `rustc` should provide a way to do this in a stable +//! manner. For instance, via command-line arguments. +//! +//! [RFC 131]: https://rust-lang.github.io/rfcs/0131-target-specification.html + +use std::{ + collections::HashMap, + fmt::{Display, Formatter, Result}, + io::BufRead, +}; + +enum Value { + Boolean(bool), + Number(i32), + String(String), + Array(Array), + Object(Object), +} + +type Array = Vec; +type Object = Vec<(String, Value)>; + +/// Minimal "almost JSON" generator (e.g. no `null`s, no escaping), enough +/// for this purpose. +impl Display for Value { + fn fmt(&self, formatter: &mut Formatter<'_>) -> Result { + match self { + Value::Boolean(boolean) => write!(formatter, "{}", boolean), + Value::Number(number) => write!(formatter, "{}", number), + Value::String(string) => write!(formatter, "\"{}\"", string), + Value::Array(array) => { + formatter.write_str("[")?; + if let [ref rest @ .., ref last] = array[..] { + for value in rest { + write!(formatter, "{},", value)?; + } + write!(formatter, "{}", last)?; + } + formatter.write_str("]") + } + Value::Object(object) => { + formatter.write_str("{")?; + if let [ref rest @ .., ref last] = object[..] { + for (key, value) in rest { + write!(formatter, "\"{}\":{},", key, value)?; + } + write!(formatter, "\"{}\":{}", last.0, last.1)?; + } + formatter.write_str("}") + } + } + } +} + +struct TargetSpec(Object); + +impl TargetSpec { + fn new() -> TargetSpec { + TargetSpec(Vec::new()) + } +} + +trait Push { + fn push(&mut self, key: &str, value: T); +} + +impl Push for TargetSpec { + fn push(&mut self, key: &str, value: bool) { + self.0.push((key.to_string(), Value::Boolean(value))); + } +} + +impl Push for TargetSpec { + fn push(&mut self, key: &str, value: i32) { + self.0.push((key.to_string(), Value::Number(value))); + } +} + +impl Push for TargetSpec { + fn push(&mut self, key: &str, value: String) { + self.0.push((key.to_string(), Value::String(value))); + } +} + +impl Push<&str> for TargetSpec { + fn push(&mut self, key: &str, value: &str) { + self.push(key, value.to_string()); + } +} + +impl Push for TargetSpec { + fn push(&mut self, key: &str, value: Object) { + self.0.push((key.to_string(), Value::Object(value))); + } +} + +impl Display for TargetSpec { + fn fmt(&self, formatter: &mut Formatter<'_>) -> Result { + // We add some newlines for clarity. + formatter.write_str("{\n")?; + if let [ref rest @ .., ref last] = self.0[..] { + for (key, value) in rest { + write!(formatter, "\"{}\":{},\n", key, value)?; + } + write!(formatter, "\"{}\":{}\n", last.0, last.1)?; + } + formatter.write_str("}") + } +} + +struct KernelConfig(HashMap); + +impl KernelConfig { + /// Parses `include/config/auto.conf` from `stdin`. + fn from_stdin() -> KernelConfig { + let mut result = HashMap::new(); + + let stdin = std::io::stdin(); + let mut handle = stdin.lock(); + let mut line = String::new(); + + loop { + line.clear(); + + if handle.read_line(&mut line).unwrap() == 0 { + break; + } + + if line.starts_with('#') { + continue; + } + + let (key, value) = line.split_once('=').expect("Missing `=` in line."); + result.insert(key.to_string(), value.trim_end_matches('\n').to_string()); + } + + KernelConfig(result) + } + + /// Does the option exist in the configuration (any value)? + /// + /// The argument must be passed without the `CONFIG_` prefix. + /// This avoids repetition and it also avoids `fixdep` making us + /// depending on it. + fn has(&self, option: &str) -> bool { + let option = "CONFIG_".to_owned() + option; + self.0.contains_key(&option) + } +} + +fn main() { + let cfg = KernelConfig::from_stdin(); + let mut ts = TargetSpec::new(); + + let pre_link_args = vec![( + "gcc".to_string(), + Value::Array(vec![ + Value::String("-Wl,--as-needed".to_string()), + Value::String("-Wl,-z,noexecstack".to_string()), + ]), + )]; + + let pre_link_args_32 = vec![( + "gcc".to_string(), + Value::Array(vec![ + Value::String("-Wl,--as-needed".to_string()), + Value::String("-Wl,-z,noexecstack".to_string()), + Value::String("-m32".to_string()), + ]), + )]; + + let pre_link_args_64 = vec![( + "gcc".to_string(), + Value::Array(vec![ + Value::String("-Wl,--as-needed".to_string()), + Value::String("-Wl,-z,noexecstack".to_string()), + Value::String("-m64".to_string()), + ]), + )]; + + let stack_probes = vec![("kind".to_string(), Value::String("none".to_string()))]; + + if cfg.has("ARM") { + ts.push("arch", "arm"); + ts.push( + "data-layout", + "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", + ); + ts.push("dynamic-linking", true); + ts.push("crt-static-respected", true); + ts.push("executables", true); + ts.push("features", "+strict-align,+v6"); + ts.push("has-elf-tls", true); + ts.push("has-rpath", true); + ts.push("llvm-target", "arm-unknown-linux-gnueabi"); + ts.push("max-atomic-width", 64); + ts.push("pre-link-args", pre_link_args); + ts.push("target-family", "unix"); + ts.push("target-mcount", "\\u0001__gnu_mcount_nc"); + ts.push("target-pointer-width", "32"); + } else if cfg.has("ARM64") { + ts.push("arch", "aarch64"); + ts.push( + "data-layout", + "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128", + ); + ts.push("disable-redzone", true); + ts.push("emit-debug-gdb-scripts", false); + ts.push("features", "+strict-align,+neon,+fp-armv8"); + ts.push("frame-pointer", "always"); + ts.push("llvm-target", "aarch64-unknown-none"); + ts.push("max-atomic-width", 128); + ts.push("needs-plt", true); + ts.push("pre-link-args", pre_link_args_64); + ts.push("stack-probes", stack_probes); + ts.push("target-c-int-width", "32"); + ts.push("target-pointer-width", "64"); + ts.push("vendor", ""); + } else if cfg.has("PPC") { + ts.push("arch", "powerpc64"); + ts.push("code-model", "large"); + ts.push("cpu", "ppc64le"); + ts.push("data-layout", "e-m:e-i64:64-n32:64"); + ts.push("features", "-altivec,-vsx,-hard-float"); + ts.push("llvm-target", "powerpc64le-elf"); + ts.push("max-atomic-width", 64); + ts.push("pre-link-args", pre_link_args_64); + ts.push("target-family", "unix"); + ts.push("target-mcount", "_mcount"); + ts.push("target-pointer-width", "64"); + } else if cfg.has("RISCV") { + if cfg.has("64BIT") { + ts.push("arch", "riscv64"); + ts.push("cpu", "generic-rv64"); + ts.push("data-layout", "e-m:e-p:64:64-i64:64-i128:128-n64-S128"); + ts.push("llvm-target", "riscv64"); + ts.push("max-atomic-width", 64); + ts.push("pre-link-args", pre_link_args_64); + ts.push("target-pointer-width", "64"); + } else { + ts.push("arch", "riscv32"); + ts.push("cpu", "generic-rv32"); + ts.push("data-layout", "e-m:e-p:32:32-i64:64-n32-S128"); + ts.push("llvm-target", "riscv32"); + ts.push("max-atomic-width", 32); + ts.push("pre-link-args", pre_link_args_32); + ts.push("target-pointer-width", "32"); + } + ts.push("code-model", "medium"); + ts.push("disable-redzone", true); + ts.push("emit-debug-gdb-scripts", false); + let mut features = "+m,+a".to_string(); + if cfg.has("RISCV_ISA_C") { + features += ",+c"; + } + ts.push("features", features); + ts.push("frame-pointer", "always"); + ts.push("needs-plt", true); + ts.push("target-c-int-width", "32"); + ts.push("stack-probes", stack_probes); + ts.push("vendor", ""); + } else if cfg.has("X86") { + ts.push("arch", "x86_64"); + ts.push("code-model", "kernel"); + ts.push("cpu", "x86-64"); + ts.push( + "data-layout", + "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128", + ); + ts.push("disable-redzone", true); + ts.push("emit-debug-gdb-scripts", false); + ts.push( + "features", + "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-3dnow,-3dnowa,-avx,-avx2,+soft-float", + ); + ts.push("frame-pointer", "always"); + ts.push("llvm-target", "x86_64-elf"); + ts.push("max-atomic-width", 64); + ts.push("needs-plt", true); + ts.push("pre-link-args", pre_link_args_64); + ts.push("stack-probes", stack_probes); + ts.push("target-c-int-width", "32"); + ts.push("target-pointer-width", "64"); + ts.push("vendor", "unknown"); + } else { + panic!("Unsupported architecture"); + } + + ts.push("env", "gnu"); + ts.push("function-sections", false); + ts.push("linker-is-gnu", true); + ts.push("os", if cfg.has("ARM") { "linux" } else { "none" }); + ts.push("position-independent-executables", true); + ts.push("relocation-model", "static"); + + if !cfg.has("ARM") { + ts.push("linker-flavor", "gcc"); + ts.push("panic-strategy", "abort"); + ts.push("relro-level", "full"); + + if cfg.has("CPU_BIG_ENDIAN") { + ts.push("target-endian", "big"); + } else { + // Everything else is LE, whether `CPU_LITTLE_ENDIAN` + // is declared or not (e.g. x86). + ts.push("target-endian", "little"); + } + } + + println!("{}", ts); +}