Hi!
I am following the Embedonomicon book to try to make a minimum bare metal program run on RP2040 Pico. RP2040 MCUs require a bootloader stage2 in size of 256 bytes at the beginning of the flash, just before the vector table. I have had build.rs
to compile the bootloader stage2 assembly source file, and added the section of the bootloader to link.x
. However it turns out that the section was not linked into the program as expected.
Please help. Thanks!
Output of building (cargo build --verbose
):
Compiling cc v1.0.79
Running `rustc --crate-name cc --edition=2018 D:\devel\rust\.cargo\registry\src\github.com-1ecc6299db9ec823\cc-1.0.79\src\lib.rs --error-format=json --json=diagnostic-rendered-ansi,artifacts,future-incompat --diagnostic-width=179 --crate-type lib --emit=dep-info,metadata,link -C embed-bitcode=no -C debuginfo=2 -C metadata=e6c181523489c735 -C extra-filename=-e6c181523489c735 --out-dir D:\devel_projects\rust_playground\bare_metal\pico_min\target\debug\deps -L dependency=D:\devel_projects\rust_playground\bare_metal\pico_min\target\debug\deps --cap-lints allow`
Compiling pico_min v0.1.0 (D:\devel_projects\rust_playground\bare_metal\pico_min)
Running `rustc --crate-name build_script_build --edition=2021 build.rs --error-format=json --json=diagnostic-rendered-ansi,artifacts,future-incompat --diagnostic-width=179 --crate-type bin --emit=dep-info,link -C embed-bitcode=no -C debuginfo=2 -C metadata=10c9512b47d3e6da -C extra-filename=-10c9512b47d3e6da --out-dir D:\devel_projects\rust_playground\bare_metal\pico_min\target\debug\build\pico_min-10c9512b47d3e6da -C incremental=D:\devel_projects\rust_playground\bare_metal\pico_min\target\debug\incremental -L dependency=D:\devel_projects\rust_playground\bare_metal\pico_min\target\debug\deps --extern cc=D:\devel_projects\rust_playground\bare_metal\pico_min\target\debug\deps\libcc-e6c181523489c735.rlib`
Running `D:\devel_projects\rust_playground\bare_metal\pico_min\target\debug\build\pico_min-10c9512b47d3e6da\build-script-build`
Running `rustc --crate-name pico_min --edition=2021 src\main.rs --error-format=json --json=diagnostic-rendered-ansi,artifacts,future-incompat --diagnostic-width=179 --crate-type bin --emit=dep-info,link -C embed-bitcode=no -C debuginfo=2 -C metadata=0df10126ed1b3803 -C extra-filename=-0df10126ed1b3803 --out-dir D:\devel_projects\rust_playground\bare_metal\pico_min\target\thumbv6m-none-eabi\debug\deps --target thumbv6m-none-eabi -C incremental=D:\devel_projects\rust_playground\bare_metal\pico_min\target\thumbv6m-none-eabi\debug\incremental -L dependency=D:\devel_projects\rust_playground\bare_metal\pico_min\target\thumbv6m-none-eabi\debug\deps -L dependency=D:\devel_projects\rust_playground\bare_metal\pico_min\target\debug\deps -C link-arg=-Tlink.x -L D:\devel_projects\rust_playground\bare_metal\pico_min\target\thumbv6m-none-eabi\debug\build\pico_min-3cd12687556d8125\out -L native=D:\devel_projects\rust_playground\bare_metal\pico_min\target\thumbv6m-none-eabi\debug\build\pico_min-3cd12687556d8125\out -l static=bs2_default_padded_checksummed`
Finished dev [unoptimized + debuginfo] target(s) in 7.98s
Examine the bootloader object file, and things seem OK. The section named .boot2
with the right size (256 bytes) is there:
> arm-none-eabi-objdump -x .\target\thumbv6m-none-eabi\debug\build\pico_min-3cd12687556d8125\out\bs2_default_padded_checksummed.o
.\target\thumbv6m-none-eabi\debug\build\pico_min-3cd12687556d8125\out\bs2_default_padded_checksummed.o: file format elf32-littlearm
.\target\thumbv6m-none-eabi\debug\build\pico_min-3cd12687556d8125\out\bs2_default_padded_checksummed.o
architecture: armv6s-m, flags 0x00000010:
HAS_SYMS
start address 0x00000000
private flags = 0x5000000: [Version5 EABI]
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 00000000 00000000 00000000 00000034 2**1
CONTENTS, ALLOC, LOAD, READONLY, CODE
1 .data 00000000 00000000 00000000 00000034 2**0
CONTENTS, ALLOC, LOAD, DATA
2 .bss 00000000 00000000 00000000 00000034 2**0
ALLOC
3 .boot2 00000100 00000000 00000000 00000034 2**0
CONTENTS, ALLOC, LOAD, READONLY, CODE
4 .ARM.attributes 00000022 00000000 00000000 00000134 2**0
CONTENTS, READONLY
SYMBOL TABLE:
00000000 l d .text 00000000 .text
00000000 l d .data 00000000 .data
00000000 l d .bss 00000000 .bss
00000000 l d .boot2 00000000 .boot2
00000000 l d .ARM.attributes 00000000 .ARM.attributes
But that section .boot2
does not end up in the final ELF program: (It was expected to be at address 0, before .vector_table
)
> arm-none-eabi-objdump .\target\thumbv6m-none-eabi\debug\pico_min -x
.\target\thumbv6m-none-eabi\debug\pico_min: file format elf32-littlearm
.\target\thumbv6m-none-eabi\debug\pico_min
architecture: armv6s-m, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x00000009
Program Header:
LOAD off 0x00010000 vaddr 0x00000000 paddr 0x00000000 align 2**16
filesz 0x00000008 memsz 0x00000008 flags r--
LOAD off 0x00010008 vaddr 0x00000008 paddr 0x00000008 align 2**16
filesz 0x00000004 memsz 0x00000004 flags r-x
STACK off 0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**0
filesz 0x00000000 memsz 0x00000000 flags rw-
private flags = 0x5000200: [Version5 EABI] [soft-float ABI]
Sections:
Idx Name Size VMA LMA File off Algn
0 .vector_table 00000008 00000000 00000000 00010000 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
1 .text 00000004 00000008 00000008 00010008 2**1
CONTENTS, ALLOC, LOAD, READONLY, CODE
2 .debug_abbrev 00000127 00000000 00000000 0001000c 2**0
CONTENTS, READONLY, DEBUGGING, OCTETS
3 .debug_info 000005e6 00000000 00000000 00010133 2**0
CONTENTS, READONLY, DEBUGGING, OCTETS
4 .debug_aranges 00000030 00000000 00000000 00010719 2**0
CONTENTS, READONLY, DEBUGGING, OCTETS
5 .debug_ranges 00000018 00000000 00000000 00010749 2**0
CONTENTS, READONLY, DEBUGGING, OCTETS
6 .debug_str 000004d6 00000000 00000000 00010761 2**0
CONTENTS, READONLY, DEBUGGING, OCTETS
7 .debug_pubnames 000000ce 00000000 00000000 00010c37 2**0
CONTENTS, READONLY, DEBUGGING, OCTETS
8 .debug_pubtypes 0000038a 00000000 00000000 00010d05 2**0
CONTENTS, READONLY, DEBUGGING, OCTETS
9 .ARM.attributes 00000032 00000000 00000000 0001108f 2**0
CONTENTS, READONLY
10 .debug_frame 00000038 00000000 00000000 000110c4 2**2
CONTENTS, READONLY, DEBUGGING, OCTETS
11 .debug_line 00000051 00000000 00000000 000110fc 2**0
CONTENTS, READONLY, DEBUGGING, OCTETS
12 .comment 00000013 00000000 00000000 0001114d 2**0
CONTENTS, READONLY
SYMBOL TABLE:
00000000 l df *ABS* 00000000 12t2aefmlsgsvu09
00000004 g O .vector_table 00000004 RESET_VECTOR
00000008 g F .text 00000004 Reset
All of the source files are as following.
Cargo.toml
:
[package]
name = "pico_min"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
[build-dependencies]
cc = "1.0.25"
.cargo/config.toml
:
[target.thumbv6m-none-eabi]
rustflags = [
# "-C", "linker=arm-none-eabi-gcc",
"-C", "link-arg=-Tlink.x",
]
[build]
target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+
link.x
:
/* Memory layout of the LM3S6965 microcontroller */
/* 1K = 1 KiBi = 1024 bytes */
MEMORY
{
FLASH : ORIGIN = 0, LENGTH = 2M
RAM : ORIGIN = 0x20000000, LENGTH = 256K
}
/* The entry point is the reset handler */
ENTRY(Reset);
/* EXTERN(RESET_VECTOR); */
SECTIONS
{
.boot2 ORIGIN(FLASH) :
{
KEEP(*(.boot2));
} > FLASH
.vector_table :
{
/* First entry: initial Stack Pointer value */
LONG(ORIGIN(RAM) + LENGTH(RAM));
/* Second entry: reset vector */
KEEP(*(.vector_table.reset_vector));
} > FLASH
.text :
{
*(.text .text.*);
} > FLASH
/DISCARD/ :
{
*(.ARM.exidx .ARM.exidx.*);
}
}
src/main.rs
:
#![no_main]
#![no_std]
use core::panic::PanicInfo;
#[no_mangle]
pub unsafe extern "C" fn Reset() -> ! {
// let _x = 42;
// can't return so we go into an infinite loop here
loop {}
}
// The reset vector, a pointer into the reset handler
#[link_section = ".vector_table.reset_vector"]
#[no_mangle]
pub static RESET_VECTOR: unsafe extern "C" fn() -> ! = Reset;
#[panic_handler]
fn panic(_panic: &PanicInfo<'_>) -> ! {
loop {}
}
bs2_default_padded_checksummed.S
, the bootloader:
// Bootloader stage2 for RP2040 Pico
.cpu cortex-m0plus
.thumb
.section .boot2, "ax"
.byte 0x00, 0xb5, 0x32, 0x4b, 0x21, 0x20, 0x58, 0x60, 0x98, 0x68, 0x02, 0x21, 0x88, 0x43, 0x98, 0x60
.byte 0xd8, 0x60, 0x18, 0x61, 0x58, 0x61, 0x2e, 0x4b, 0x00, 0x21, 0x99, 0x60, 0x02, 0x21, 0x59, 0x61
.byte 0x01, 0x21, 0xf0, 0x22, 0x99, 0x50, 0x2b, 0x49, 0x19, 0x60, 0x01, 0x21, 0x99, 0x60, 0x35, 0x20
.byte 0x00, 0xf0, 0x44, 0xf8, 0x02, 0x22, 0x90, 0x42, 0x14, 0xd0, 0x06, 0x21, 0x19, 0x66, 0x00, 0xf0
.byte 0x34, 0xf8, 0x19, 0x6e, 0x01, 0x21, 0x19, 0x66, 0x00, 0x20, 0x18, 0x66, 0x1a, 0x66, 0x00, 0xf0
.byte 0x2c, 0xf8, 0x19, 0x6e, 0x19, 0x6e, 0x19, 0x6e, 0x05, 0x20, 0x00, 0xf0, 0x2f, 0xf8, 0x01, 0x21
.byte 0x08, 0x42, 0xf9, 0xd1, 0x00, 0x21, 0x99, 0x60, 0x1b, 0x49, 0x19, 0x60, 0x00, 0x21, 0x59, 0x60
.byte 0x1a, 0x49, 0x1b, 0x48, 0x01, 0x60, 0x01, 0x21, 0x99, 0x60, 0xeb, 0x21, 0x19, 0x66, 0xa0, 0x21
.byte 0x19, 0x66, 0x00, 0xf0, 0x12, 0xf8, 0x00, 0x21, 0x99, 0x60, 0x16, 0x49, 0x14, 0x48, 0x01, 0x60
.byte 0x01, 0x21, 0x99, 0x60, 0x01, 0xbc, 0x00, 0x28, 0x00, 0xd0, 0x00, 0x47, 0x12, 0x48, 0x13, 0x49
.byte 0x08, 0x60, 0x03, 0xc8, 0x80, 0xf3, 0x08, 0x88, 0x08, 0x47, 0x03, 0xb5, 0x99, 0x6a, 0x04, 0x20
.byte 0x01, 0x42, 0xfb, 0xd0, 0x01, 0x20, 0x01, 0x42, 0xf8, 0xd1, 0x03, 0xbd, 0x02, 0xb5, 0x18, 0x66
.byte 0x18, 0x66, 0xff, 0xf7, 0xf2, 0xff, 0x18, 0x6e, 0x18, 0x6e, 0x02, 0xbd, 0x00, 0x00, 0x02, 0x40
.byte 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x07, 0x00, 0x00, 0x03, 0x5f, 0x00, 0x21, 0x22, 0x00, 0x00
.byte 0xf4, 0x00, 0x00, 0x18, 0x22, 0x20, 0x00, 0xa0, 0x00, 0x01, 0x00, 0x10, 0x08, 0xed, 0x00, 0xe0
.byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0xb2, 0x4e, 0x7a
build.rs
:
use std::{env, error::Error, fs::File, io::Write, path::PathBuf};
use cc::Build;
fn main() -> Result<(), Box<dyn Error>> {
// build directory for this crate
let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
// extend the library search path
println!("cargo:rustc-link-search={}", out_dir.display());
// put `link.x` in the build directory
File::create(out_dir.join("link.x"))?.write_all(include_bytes!("link.x"))?;
// assemble the `asm.s` file
Build::new().file("bs2_default_padded_checksummed.S").compile("bs2_default_padded_checksummed"); // <- NEW!
// rebuild if `asm.s` changed
println!("cargo:rerun-if-changed=bs2_default_padded_checksummed.S"); // <- NEW!
Ok(())
}