rustembeddedrust-cargocortex-mstm32h7

Rust cortex-m-rt / RTIC firmware builds an empty ELF (0x00010000 start, ~150 bytes) — memory.x not applied for STM32H7


What’s the correct way to ensure cortex-m-rt (or RTIC) uses my custom memory.x so the ELF is linked to 0x08000000 instead of the default 0x00010000?

Context

I’m writing embedded Rust firmware for an STM32H753 using the RTIC framework and stm32h7xx-hal crate. The project compiles successfully, but the generated ELF is only ~150 bytes and contains no .text or vector table.

What I Expect

The ELF should have .text, .vector, etc., starting at 0x08000000, per my memory.x.

What Actually Happens
$ arm-none-eabi-readelf -l target/thumbv7em-none-eabihf/release/stm32_firmware | grep LOAD
LOAD 0x000000 0x00010000 0x00010000 0x00096 0x00096 R 0x10000

and

$ arm-none-eabi-objdump -h ... | grep .text
# (no results)

objdump:

$ objdump -s target/thumbv7em-none-eabihf/release/stm32_firmware

target/thumbv7em-none-eabihf/release/stm32_firmware:     file format elf32-little

Contents of section .defmt.end:
 10094 0000                                 ..
Contents of section .comment:
 0000 004c696e 6b65723a 204c4c44 2032302e  .Linker: LLD 20.
 0010 312e3720 282f6368 65636b6f 75742f73  1.7 (/checkout/s
 0020 72632f6c 6c766d2d 70726f6a 6563742f  rc/llvm-project/
 0030 6c6c766d 20396231 62663463 66303431  llvm 9b1bf4cf041
 0040 63316331 66653632 63663033 38393161  c1c1fe62cf03891a
 0050 63393034 33313631 35653738 30290072  c90431615e780).r
 0060 75737463 20766572 73696f6e 20312e38  ustc version 1.8
 0070 392e3020 28323934 38333838 33652032  9.0 (29483883e 2
 0080 3032352d 30382d30 342900             025-08-04).
Contents of section .ARM.attributes:
 0000 41370000 00616561 62690001 2d000000  A7...aeabi..-...
 0010 43322e30 3900060d 074d0800 09020a06  C2.09....M......
 0020 0e001101 14011501 17031801 19011b01  ................
 0030 1c012201 24012601                    ..".$.&.
Relevant Files

firmware/stm32/Cargo.toml

[package]
name = "stm32_firmware"
edition = "2021"

[dependencies]
cortex-m = { version = "0.7.7", features = ["critical-section-single-core"] }
cortex-m-rt = { workspace = true }
rtic = { version = "2.0", features = ["thumbv7-backend"] }
# ...

[package.metadata.cortex-m-rt]
memory-x = true

.cargo/config.toml

[build]
target = "thumbv7em-none-eabihf"

[target.thumbv7em-none-eabihf]
runner = "bash firmware/stm32/flash-stm32-opencd.sh"
rustflags = [
  "-C", "link-arg=-Tlink.x",
  # tried both with and without -Tmemory.x
]

memory.x

MEMORY {
  FLASH (rx)  : ORIGIN = 0x08000000, LENGTH = 2048K
  RAM_D1 (rwx): ORIGIN = 0x24000000, LENGTH = 512K
}
REGION_ALIAS(FLASH, FLASH);
REGION_ALIAS(RAM, RAM_D1);

src/main.rs

#![no_std]
#![no_main]
use rtic::app;

#[app(device = stm32h7xx_hal::pac, dispatchers = [EXTI0, EXTI1])]
mod app {
    use defmt_rtt as _;
    use panic_probe as _;
    #[init]
    fn init(_: init::Context) -> ((), ()) { loop {} }
}
What I’ve Tried

Still links to 0x00010000.

commands I'm using for reference:

$ cargo clean
$ cargo build -p stm32_firmware --release --target thumbv7em-none-eabihf

Solution

  • After defining build.rs in our project directory:

    use std::{env, path::PathBuf};
    
    
    fn main() {
        // Use cortex-m-rt's linker script
        println!("cargo:rustc-link-arg=-Tlink.x");
    
        // Make the *containing directory* of memory.x visible to the linker
        let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
        println!("cargo:rustc-link-search={}", manifest_dir.display());
    
        // Rebuild if memory.x changes
        println!(
            "cargo:rerun-if-changed={}",
            manifest_dir.join("memory.x").display()
        );
    }
    

    We got this error showing the root of problem once the memory. Script was finally being used:

    memory.x:5: redefinition of memory region 'FLASH' 
    \>\>\> REGION_ALIAS(FLASH, FLASH); \>\>\> 
    ^ error: could not compile stm32_firmware (bin "stm32_firmware") due to 1 previous error  
    

    So quite literally just needed to remove the REGION_ALIAS(FLASH, FLASH); line and the binary is looking scrumptious now

    @thebusybee thanks to him for letting me know verbose still exists lol. Using the verbose appendage I confirmed the linker flags are being appended to the build commands defined in the .config file ruling that out, revealing the memory. Wasn't being used in the first place.