From 352711310e3659274863c85a7c138c4a2ec54760 Mon Sep 17 00:00:00 2001 From: Alexander Koz Date: Fri, 12 Apr 2024 16:59:15 +0400 Subject: [PATCH] Add linker-script to produce efls that acceptable for PDC, allow to use it in `cargo-playdate` with `--no-gcc`. --- Cargo.lock | 4 +- Cargo.toml | 2 +- cargo/Cargo.toml | 2 +- cargo/README.md | 9 ++-- cargo/src/build/rustflags.rs | 51 +++++++++++++----- cargo/src/cli/opts.rs | 45 ++++++++-------- support/utils/Cargo.toml | 2 +- support/utils/README.md | 2 +- support/utils/src/compile.rs | 17 +++++- support/utils/src/layout.x | 102 +++++++++++++++++++++++++++++++++++ 10 files changed, 188 insertions(+), 48 deletions(-) create mode 100644 support/utils/src/layout.x diff --git a/Cargo.lock b/Cargo.lock index 17bde5a5..8e36242e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -710,7 +710,7 @@ dependencies = [ [[package]] name = "cargo-playdate" -version = "0.4.2" +version = "0.4.3" dependencies = [ "anstyle", "anyhow", @@ -3823,7 +3823,7 @@ dependencies = [ [[package]] name = "playdate-build-utils" -version = "0.2.1" +version = "0.3.0" dependencies = [ "dirs", "log", diff --git a/Cargo.toml b/Cargo.toml index 05194978..b3c59586 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,7 @@ sys = { version = "0.3", path = "api/sys", package = "playdate-sys", default-fea tool = { version = "0.1", path = "support/tool", package = "playdate-tool" } build = { version = "0.2", path = "support/build", package = "playdate-build", default-features = false } -utils = { version = "0.2", path = "support/utils", package = "playdate-build-utils", default-features = false } +utils = { version = "0.3", path = "support/utils", package = "playdate-build-utils", default-features = false } device = { version = "0.2", path = "support/device", package = "playdate-device" } simulator = { version = "0.1", path = "support/sim-ctrl", package = "playdate-simulator-utils", default-features = false } bindgen = { version = "0.1", path = "support/bindgen", package = "playdate-bindgen", default-features = false } diff --git a/cargo/Cargo.toml b/cargo/Cargo.toml index ed890285..f3ea1bf7 100644 --- a/cargo/Cargo.toml +++ b/cargo/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cargo-playdate" -version = "0.4.2" +version = "0.4.3" readme = "README.md" description = "Build tool for neat yellow console." keywords = ["playdate", "build", "cargo", "plugin", "cargo-subcommand"] diff --git a/cargo/README.md b/cargo/README.md index 5b2c6511..ded68944 100644 --- a/cargo/README.md +++ b/cargo/README.md @@ -4,11 +4,10 @@ Cargo-playdate is a cross-platform plugin for cargo that can build programs for It can build programs written in Rust, manage assets, build package for Playdate and run on sim or device. Usually it builds static or dynamic libraries for sim and hardware, -but also it can build executable binaries for hardware and this method produces highly optimized output with dramatically minimized size (thanks to DCE & LTO). -_\* But for binaries you're need also patched pdc from [dev-forum][]._ +but also it can build executable binaries for hardware and this method produces highly optimized output with dramatically minimized size (thanks to DCE & LTO)\*. +\* For executable binaries use `--no-gcc` argument._ -[dev-forum]: https://devforum.play.date/t/sdk-2-0-b2-pdc-produces-pdx-with-broken-binary/11345/28 ## Prerequisites @@ -28,7 +27,7 @@ To build programs using `cargo-playdate` you need: To run on sim or dev with `cargo-playdate`: 1. Linux only: - `libudev`, follow [instructions for udev crate][udev-crate-deps]. -2. Windows only: +1. Windows only: - `powershell` (used as fallback) [sdk]: https://play.date/dev/#cardSDK @@ -39,6 +38,8 @@ To run on sim or dev with `cargo-playdate`: ## Installation ```bash +cargo install cargo-playdate +# or cargo install --git="https://github.com/boozook/playdate.git" --bin=cargo-playdate ``` diff --git a/cargo/src/build/rustflags.rs b/cargo/src/build/rustflags.rs index b78b4a16..aa24c3c3 100644 --- a/cargo/src/build/rustflags.rs +++ b/cargo/src/build/rustflags.rs @@ -5,6 +5,7 @@ use std::ops::Add; use anyhow::Context; use cargo::core::compiler::CompileKind; use cargo::core::compiler::CompileTarget; +use cargo::util; use playdate::compile::RUSTFLAGS_BIN_PLAYDATE; use playdate::compile::RUSTFLAGS_LIB_HOST; use playdate::compile::RUSTFLAGS_LIB_PLAYDATE; @@ -46,21 +47,43 @@ impl Rustflags { let sdk = config.sdk() .log_err_cargo(config) .with_context(|| "Playdate Sdk needed to build binaries")?; - let arm = config.gcc() - .log_err_cargo(config) - .with_context(|| "ARM GNU toolchain needed to build binaries")?; - let link_map = format!("-Clink-arg=-T{}", sdk.build_support().link_map().display()); - let lib_search_paths = arm.lib_search_paths_for_playdate() - .or_else(|_| arm.lib_search_paths_default())?; - Self::rustflags_bin_playdate().into_iter() - .map(|s| Cow::from(*s)) - .chain([link_map.into()]) - .chain(lib_search_paths.into_iter().map(|s| { - "-L".to_owned().add(s.to_string_lossy().as_ref()).into() - })) - .chain(prevent_unwinding().into_iter()) - .collect() + if config.no_gcc { + // export LINK MAP: + let target_dir = config.workspace + .config() + .target_dir() + .unwrap_or_default() + .unwrap_or_else(|| util::Filesystem::new("target".into())) + .into_path_unlocked() + .canonicalize()?; + let map = target_dir.join("pd.x"); + if !map.exists() { + std::fs::write(&map, build::compile::LINK_MAP_BIN_SRC)?; + } + let link_map = format!("-Clink-arg=-T{}", map.display()); + Self::rustflags_bin_playdate().into_iter() + .map(|s| Cow::from(*s)) + .chain([link_map.into()]) + .chain(prevent_unwinding().into_iter()) + .collect() + } else { + let arm = config.gcc() + .log_err_cargo(config) + .with_context(|| "ARM GNU toolchain needed to build binaries")?; + + let link_map = format!("-Clink-arg=-T{}", sdk.build_support().link_map().display()); + let lib_search_paths = arm.lib_search_paths_for_playdate() + .or_else(|_| arm.lib_search_paths_default())?; + Self::rustflags_bin_playdate().into_iter() + .map(|s| Cow::from(*s)) + .chain([link_map.into()]) + .chain(lib_search_paths.into_iter().map(|s| { + "-L".to_owned().add(s.to_string_lossy().as_ref()).into() + })) + .chain(prevent_unwinding().into_iter()) + .collect() + } }; diff --git a/cargo/src/cli/opts.rs b/cargo/src/cli/opts.rs index c5ba6d6b..0ce0238c 100644 --- a/cargo/src/cli/opts.rs +++ b/cargo/src/cli/opts.rs @@ -17,27 +17,27 @@ use super::target::PlaydateTarget; pub fn special_args_global() -> Vec { vec![ - Arg::new("sdk").help("Path to Playdate SDK") - .long("sdk") - .env(SDK_ENV_VAR) - .conflicts_with("no-sdk") - .value_name("DIRECTORY") - .value_hint(clap::ValueHint::DirPath) - .value_parser(clap::builder::ValueParser::path_buf()) - .num_args(1) - .global(true), - Arg::new("gcc").help("Path to GCC in ARM GNU toolchain, usually named 'arm-none-eabi-gcc'.") - .long("gcc") - .env(ARM_GCC_PATH_ENV_VAR) - .conflicts_with("no-gcc") - .value_name("EXECUTABLE") - .value_parser(clap::builder::ValueParser::path_buf()) - .num_args(1) - .global(true), - // XXX: no-sdk & no-gcc are hidden because not supported yet: - flag("no-sdk", "Do not use Playdate SDK").global(true).hide(true), - flag("no-gcc", "Do not use ARM GNU toolchain, but Rust & LLVM only.").global(true) - .hide(true) + Arg::new("sdk").help("Path to Playdate SDK") + .long("sdk") + .env(SDK_ENV_VAR) + .conflicts_with("no-sdk") + .value_name("DIRECTORY") + .value_hint(clap::ValueHint::DirPath) + .value_parser(clap::builder::ValueParser::path_buf()) + .num_args(1) + .global(true), + Arg::new("gcc").help("Path to GCC in ARM GNU toolchain, usually named 'arm-none-eabi-gcc'.") + .long("gcc") + .env(ARM_GCC_PATH_ENV_VAR) + .conflicts_with("no-gcc") + .value_name("EXECUTABLE") + .value_parser(clap::builder::ValueParser::path_buf()) + .num_args(1) + .global(true), + // XXX: no-sdk are hidden because not supported yet: + flag("no-sdk", "Do not use Playdate SDK").global(true).hide(true), + flag("no-gcc", "Do not use ARM GNU toolchain, but Rust only.").global(true) + .long_help("Do not use ARM GNU toolchain, but Rust only. Only for executable bins for device target.") ] } @@ -475,7 +475,8 @@ fn add_globals(cmd: Command) -> Command { .alias("dir") .short_alias('D') .value_hint(clap::ValueHint::DirPath) - .value_parser(clap::builder::ValueParser::path_buf())) + .value_parser(clap::builder::ValueParser::os_string()) + ) .arg(flag("frozen", "Require Cargo.lock and cache are up to date").global(true)) .arg(flag("locked", "Require Cargo.lock is up to date").global(true)) .arg(flag("offline", "Run without accessing the network").global(true)) diff --git a/support/utils/Cargo.toml b/support/utils/Cargo.toml index 32c168e3..560f5d7f 100644 --- a/support/utils/Cargo.toml +++ b/support/utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "playdate-build-utils" -version = "0.2.1" +version = "0.3.0" readme = "README.md" description = "Utils that help to build program with Rust and Playdate API" keywords = ["playdate", "utility"] diff --git a/support/utils/README.md b/support/utils/README.md index 9698ae63..cc6f9dd1 100644 --- a/support/utils/README.md +++ b/support/utils/README.md @@ -2,7 +2,7 @@ Mainly there are utils to find existing (means already installed) Playdate SDK and GNU-ARM toolchain. -Also there are some constants such as hardcoded compilation flags. +Also there are some constants such as hardcoded compilation flags and linker-script. diff --git a/support/utils/src/compile.rs b/support/utils/src/compile.rs index bf5ad939..d5963b2a 100644 --- a/support/utils/src/compile.rs +++ b/support/utils/src/compile.rs @@ -4,7 +4,7 @@ /// Do not forget -/// - fist positional - artifact path +/// - first positional - artifact path /// - _this args_ /// - output path `-o` pub const GCC_ARGS_LIB: &[&str] = &["-nostartfiles", @@ -41,12 +41,15 @@ pub const RUSTFLAGS_BIN_PLAYDATE: &[&str] = &["-Ctarget-cpu=cortex-m7", "-Clink-arg=--gc-sections", "-Clink-arg=--entry=eventHandlerShim"]; - +/// Bin that we giving to PDC. pub const PDX_BIN_NAME_ELF: &str = "pdex.elf"; +/// Bin that is product of PDC. pub const PDX_BIN_NAME_BIN: &str = "pdex.bin"; /// File-stem for bin, elf, and dylib files. pub const PDX_BIN_NAME_STEM: &str = "pdex"; +/// Extension of Playdate package (dir). pub const PDX_PKG_EXT: &str = "pdx"; +/// Playdate package manifest filename. pub const PDX_PKG_MANIFEST_FILENAME: &str = "pdxinfo"; @@ -88,3 +91,13 @@ pub fn dylib_suffix_for_opt(target_family: &str, target_os: &str) -> Option<&'st } pub const fn static_lib_suffix() -> &'static str { "a" } + + +/// Compile-time path to the linker-script [LINK_MAP_BIN_SRC]. +/// __Do note resolve, it contains file as dir.__ +/// Path is relative to crate root by default, it depends on your rustc configuration. +pub const LINK_MAP_BIN_PATH: &str = concat!(file!(), "/../", "layout.x"); + +/// Linker-script for elf that built with rustc, +/// without arm-gcc and its std lib. +pub const LINK_MAP_BIN_SRC: &str = include_str!("layout.x"); diff --git a/support/utils/src/layout.x b/support/utils/src/layout.x new file mode 100644 index 00000000..0e182729 --- /dev/null +++ b/support/utils/src/layout.x @@ -0,0 +1,102 @@ +/* Elf layout that acceptable for PDC */ + +ENTRY(eventHandlerShim) + +MEMORY +{ + /* + Stack: 61800 + Heap: 8388208 + */ + ram (rwx) : ORIGIN = 0, LENGTH = 61800 +} +PROVIDE(_fstack = ORIGIN(ram) + LENGTH(ram) - 4); + +PHDRS { + text PT_LOAD FLAGS(7); /* 7 == RWX */ +} + +SECTIONS +{ + .text : + { + *(.text) + *(.text.*) + + KEEP(*(.init)) + KEEP(*(.fini)) + + /* .ctors */ + *crtbegin.o(.ctors) + *crtbegin?.o(.ctors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) + *(SORT(.ctors.*)) + *(.ctors) + + /* .dtors */ + *crtbegin.o(.dtors) + *crtbegin?.o(.dtors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) + *(SORT(.dtors.*)) + *(.dtors) + + *(.rodata*) + + KEEP(*(.eh_frame*)) + } :text /* PUT IT IN THE text SEGMENT */ + + .data : + { + __etext = .; + + __data_start__ = .; + *(vtable) + *(.data*) + + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP(*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + + . = ALIGN(4); + /* init data */ + PROVIDE_HIDDEN (__init_array_start = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + + . = ALIGN(4); + /* fini data */ + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP(*(SORT(.fini_array.*))) + KEEP(*(.fini_array)) + PROVIDE_HIDDEN (__fini_array_end = .); + + . = ALIGN(4); + /* All data end */ + __data_end__ = .; + } :text /* PUT IT IN THE text SEGMENT */ + + .bss : + { + . = ALIGN(4); + __bss_start__ = .; + *(.bss*) + *(COMMON) + *(COM) + . = ALIGN(4); + __bss_end__ = .; + } :text /* PUT IT IN THE text SEGMENT */ + + .reloc : { + . = ALIGN(4); + /* . = RELOC_TABLE_START; */ + *(.rel*.*) /* might need to include other sections based on compiler output */ + } :text /* PUT IT IN THE text SEGMENT */ + + /DISCARD/ : + { + *(.ARM.exidx) + } +}