Skip to content

Commit

Permalink
Add feature to preserve cmsis-pack metadata in output elf
Browse files Browse the repository at this point in the history
  • Loading branch information
9names committed Feb 21, 2024
1 parent 35b9d6f commit 321f9c1
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 5 deletions.
2 changes: 1 addition & 1 deletion .cargo/config
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

rustflags = [
"-C", "link-arg=--nmagic",
"-C", "link-arg=-Tlink.x",
"-C", "link-arg=-Tlink_real.x",

# Code-size optimizations.
"-C", "inline-threshold=5",
Expand Down
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ flash-algorithm = { version = "0.4.0", default-features = false, features = [
"panic-handler",
] }

[features]
# Keep extra metadata in the final binary that probe-rs doesn't strictly need.
# Other tools use of these datastructures: probe-rs' target-gen, pyocd, jlink
cmsis-pack-compat = []

# this lets you use `cargo fix`!
[[bin]]
name = "flash-algo"
Expand Down
28 changes: 24 additions & 4 deletions build.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,41 @@
use std::env;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
use std::path::{Path, PathBuf};

fn main() {
// Put `link.x` in our output directory and ensure it's
// on the linker search path.
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
File::create(out.join("link.x"))

println!("cargo:rustc-link-search={}", out.display());

// Call another function to copy the linker script into the output directory.
// This allows us to use a different linker script based on whether the
// cmsis-pack-compat feature is enabled or not
setup_linker_script(out);
}

#[cfg(not(feature = "cmsis-pack-compat"))]
fn setup_linker_script(out: &Path) {
// rename the linker output to ensure we use the one in the
// build output instead of the one in the project root
File::create(out.join("link_output.x"))
.unwrap()
.write_all(include_bytes!("link.x"))
.unwrap();
println!("cargo:rustc-link-search={}", out.display());

// By default, Cargo will re-run a build script whenever
// any file in the project changes. By specifying `link.x`
// here, we ensure the build script is only re-run when
// `link.x` is changed.
println!("cargo:rerun-if-changed=link.x");
}

#[cfg(feature = "cmsis-pack-compat")]
fn setup_linker_script(out: &Path) {
File::create(out.join("link_output.x"))
.unwrap()
.write_all(include_bytes!("link_metadata.x"))
.unwrap();
println!("cargo:rerun-if-changed=link_metadata.x");
}
33 changes: 33 additions & 0 deletions build_w_metadata.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#!/bin/bash

set -euo pipefail

# macOS base64 doesn't take -w argument and defaults to a single line.
if [[ $(uname) = "Darwin" ]]; then
BASE64_FLAGS=""
else
BASE64_FLAGS="-w0"
fi

cargo build --release --features cmsis-pack-compat
ELF=target/thumbv6m-none-eabi/release/flash-algo

rust-objdump --disassemble $ELF > target/disassembly.s
rust-objdump -x $ELF > target/dump.txt
rust-nm $ELF -n > target/nm.txt

function bin {
rust-objcopy $ELF -O binary - | base64 $BASE64_FLAGS
}

function sym {
echo $((0x$(rust-nm $ELF | grep -w $1 | cut -d ' ' -f 1) + 1))
}

cat <<EOF
instructions: $(bin)
pc_init: $(sym Init)
pc_uninit: $(sym UnInit)
pc_program_page: $(sym ProgramPage)
pc_erase_sector: $(sym EraseSector)
EOF
70 changes: 70 additions & 0 deletions link_metadata.x
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
SECTIONS {
. = 0x0;

/*
* The PrgCode output section name comes from the CMSIS-Pack flash algo
* templates and armlink. It is used here because several tools that work
* with these flash algos expect this section name.
*
* All input sections are combined into PrgCode because RWPI using R9 is not
* currently stable in Rust, thus having separate PrgData sections that the
* debug host might locate at a different offset from PrgCode is not safe.
*/
PrgCode : {
KEEP(*(.entry))
KEEP(*(.entry.*))

*(.text)
*(.text.*)

*(.rodata)
*(.rodata.*)

*(.data)
*(.data.*)

*(.sdata)
*(.sdata.*)

*(.bss)
*(.bss.*)

*(.uninit)
*(.uninit.*)

. = ALIGN(4);
}

/* Section for data, specified by flashloader standard. */
PrgData : {
/*
* We're explicitly putting a single object here (PRGDATA_Start in main.c) as this is required by some tools.
* It is not used by this algorithm
*
* The KEEP statement ensures it's not removed by accident.
*/
KEEP(*(PrgData))

. = ALIGN(4);
}

/* Description of the flash algorithm */
DevDscr . : {
/* The device data content is only for external tools,
* and usually not referenced by the code.
* All rules have exceptions: device data is used by this flash algo.
*
* The KEEP statement ensures it's not removed by accident.
*/
KEEP(*(DeviceData))

. = ALIGN(4);
}

/DISCARD/ : {
/* Unused exception related info that only wastes space */
*(.ARM.exidx);
*(.ARM.exidx.*);
*(.ARM.extab.*);
}
}
11 changes: 11 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,14 @@ impl Drop for RP2040Algo {
(self.funcs.flash_enter_cmd_xip)();
}
}

/// Some tools (eg Segger's debugger) require the PrgData section to exist in the target binary
///
/// They scan the flashloader binary for this symbol to determine the section location
/// If they cannot find it, the tool exits. This variable serves no other purpose
#[allow(non_upper_case_globals)]
#[no_mangle]
#[used]
#[link_section = "PrgData"]
#[cfg(feature = "cmsis-pack-compat")]
pub static mut PRGDATA_Start: usize = 0;

0 comments on commit 321f9c1

Please sign in to comment.