Выбрать главу

# target = "thumbv7em-none-eabi" # Cortex-M4 and Cortex-M7 (no FPU)

# target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU)

To cross compile for the Cortex-M3 architecture we have to use thumbv7m-none-eabi. That target is not automatically installed when installing the Rust toolchain, it would now be a good time to add that target to the toolchain, if you haven't done it yet:

rustup target add thumbv7m-none-eabi

Since the thumbv7m-none-eabi compilation target has been set as the default in your .cargo/config.toml file, the two commands below do the same:

cargo build --target thumbv7m-none-eabi

cargo build

Now we have a non-native ELF binary in target/thumbv7m-none-eabi/debug/app. We can inspect it using cargo-binutils.

With cargo-readobj we can print the ELF headers to confirm that this is an ARM binary.

cargo readobj --bin app -- -file-headers

Note that:

   • --bin app is sugar for inspect the binary at target/$TRIPLE/debug/app

   • --bin app will also (re)compile the binary, if necessary

ELF Header:

Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00

Class: ELF32

Data: 2's complement, little endian

Version: 1 (current)

OS/ABI: UNIX - System V

ABI Version: 0x0

Type: EXEC (Executable file)

Machine: ARM

Version: 0x1

Entry point address: 0x405

Start of program headers: 52 (bytes into file)

Start of section headers: 153204 (bytes into file)

Flags: 0x5000200

Size of this header: 52 (bytes)

Size of program headers: 32 (bytes)

Number of program headers: 2

Size of section headers: 40 (bytes)

Number of section headers: 19

Section header string table index: 18

cargo-size can print the size of the linker sections of the binary.

cargo size --bin app --release -- -A

we use --release to inspect the optimized version

app :

section size addr

.vector_table 1024 0x0

.text 92 0x400

.rodata 0 0x45c

.data 0 0x20000000

.bss 0 0x20000000

.debug_str 2958 0x0

.debug_loc 19 0x0

.debug_abbrev 567 0x0

.debug_info 4929 0x0

.debug_ranges 40 0x0

.debug_macinfo 1 0x0

.debug_pubnames 2035 0x0

.debug_pubtypes 1892 0x0

.ARM.attributes 46 0x0

.debug_frame 100 0x0

.debug_line 867 0x0

Total 14570

A refresher on ELF linker sections

   • .text contains the program instructions

   • .rodata contains constant values like strings

   • .data contains statically allocated variables whose initial values are not zero

   • .bss also contains statically allocated variables whose initial values are zero

   • .vector_table is a non-standard section that we use to store the vector (interrupt) table

   • .ARM.attributes and the .debug_* sections contain metadata and will not be loaded onto the target when flashing the binary.

IMPORTANT: ELF files contain metadata like debug information so their size on disk does not accurately reflect the space the program will occupy when flashed on a device. Always use cargo-size to check how big a binary really is.

cargo-objdump can be used to disassemble the binary.

cargo objdump --bin app --release -- --disassemble --no-show-raw-insn --print-imm-hex

NOTE if the above command complains about Unknown command line argument see the following bug report: https://github.com/rust-embedded/book/issues/269

NOTE this output can differ on your system. New versions of rustc, LLVM and libraries can generate different assembly. We truncated some of the instructions to keep the snippet small.

app: file format ELF32-arm-little

Disassembly of section .text:

main:

400: bl #0x256

404: b #-0x4 <main+0x4>

Reset:

406: bl #0x24e

40a: movw r0, #0x0

< .. truncated any more instructions .. >

DefaultHandler_:

656: b #-0x4 <DefaultHandler_>

UsageFault:

657: strb r7, [r4, #0x3]

DefaultPreInit:

658: bx lr

__pre_init:

659: strb r7, [r0, #0x1]

__nop:

65a: bx lr

HardFaultTrampoline:

65c: mrs r0, msp

660: b #-0x2 <HardFault_>

HardFault_:

662: b #-0x4 <HardFault_>

HardFault:

663: <unknown>

Next, let's see how to run an embedded program on QEMU! This time we'll use the hello example which actually does something.

For convenience here's the source code of examples/hello.rs:

//! Prints "Hello, world!" on the host console using semihosting

#![no_main]

#![no_std]

use panic_halt as _;

use cortex_m_rt::entry;

use cortex_m_semihosting::{debug, hprintln};

#[entry]

fn main() -> ! {

hprintln!("Hello, world!").unwrap();

// exit QEMU

// NOTE do not run this on hardware; it can corrupt OpenOCD state

debug::exit(debug::EXIT_SUCCESS);

loop {}

}

This program uses something called semihosting to print text to the host console. When using real hardware this requires a debug session but when using QEMU this Just Works.

Let's start by compiling the example:

cargo build --example hello

The output binary will be located at target/thumbv7m-none-eabi/debug/examples/hello.

To run this binary on QEMU run the following command:

qemu-system-arm \