Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ci/main/app-unsound/.cargo
7 changes: 7 additions & 0 deletions ci/main/app-unsound/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
edition = "2024"
name = "app"
version = "0.1.0"

[dependencies]
rt = { path = "../rt-unsound" }
23 changes: 23 additions & 0 deletions ci/main/app-unsound/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#![no_main]
#![no_std]

use core::{arch::asm, ptr};

use rt::entry;

entry!(main);

static mut DATA: i32 = 1;

#[allow(static_mut_refs)]
fn main() -> ! {
unsafe {
// check that DATA is properly initialized
if ptr::read_volatile(&DATA) != 1 {
// this makes QEMU crash
asm!("BKPT");
}
}

loop {}
}
1 change: 1 addition & 0 deletions ci/main/rt-unsound/Cargo.toml
1 change: 1 addition & 0 deletions ci/main/rt-unsound/build.rs
56 changes: 56 additions & 0 deletions ci/main/rt-unsound/link.x
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/* Memory layout of the LM3S6965 microcontroller */
/* 1K = 1 KiBi = 1024 bytes */
MEMORY
{
FLASH : ORIGIN = 0x00000000, LENGTH = 256K
RAM : ORIGIN = 0x20000000, LENGTH = 64K
}

/* The entry point is the reset handler */
ENTRY(Reset);

EXTERN(RESET_VECTOR);

SECTIONS
{
.vector_table ORIGIN(FLASH) :
{
/* 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

/* CHANGED! */
.rodata :
{
*(.rodata .rodata.*);
} > FLASH

.bss :
{
_sbss = .;
*(.bss .bss.*);
_ebss = .;
} > RAM

.data : AT(ADDR(.rodata) + SIZEOF(.rodata))
{
_sdata = .;
*(.data .data.*);
_edata = .;
} > RAM

_sidata = LOADADDR(.data);

/DISCARD/ :
{
*(.ARM.exidx .ARM.exidx.*);
}
}
55 changes: 55 additions & 0 deletions ci/main/rt-unsound/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#![no_std]

use core::panic::PanicInfo;
use core::ptr;

#[unsafe(no_mangle)]
#[allow(static_mut_refs)]
pub unsafe extern "C" fn Reset() -> ! {
// NEW!
// Initialize RAM
unsafe extern "C" {
static mut _sbss: u8;
static mut _ebss: u8;

static mut _sdata: u8;
static mut _edata: u8;
static _sidata: u8;
}

let count = unsafe { &_ebss as *const u8 as usize - &_sbss as *const u8 as usize };
unsafe { ptr::write_bytes(&mut _sbss as *mut u8, 0, count) };

let count = unsafe { &_edata as *const u8 as usize - &_sdata as *const u8 as usize };
unsafe { ptr::copy_nonoverlapping(&_sidata as *const u8, &mut _sdata as *mut u8, count) };

// Call user entry point
unsafe extern "Rust" {
safe fn main() -> !;
}

main()
}

// The reset vector, a pointer into the reset handler
#[unsafe(link_section = ".vector_table.reset_vector")]
#[unsafe(no_mangle)]
pub static RESET_VECTOR: unsafe extern "C" fn() -> ! = Reset;

#[panic_handler]
fn panic(_panic: &PanicInfo<'_>) -> ! {
loop {}
}

#[macro_export]
macro_rules! entry {
($path:path) => {
#[unsafe(export_name = "main")]
pub unsafe fn __main() -> ! {
// type check the given path
let f: fn() -> ! = $path;

f()
}
};
}
79 changes: 52 additions & 27 deletions ci/main/rt2/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,34 +1,59 @@
#![no_std]

use core::panic::PanicInfo;
use core::ptr;

#[unsafe(no_mangle)]
#[allow(static_mut_refs)]
pub unsafe extern "C" fn Reset() -> ! {
// NEW!
// Initialize RAM
unsafe extern "C" {
static mut _sbss: u8;
static mut _ebss: u8;

static mut _sdata: u8;
static mut _edata: u8;
static _sidata: u8;
}

let count = unsafe { &_ebss as *const u8 as usize - &_sbss as *const u8 as usize };
unsafe { ptr::write_bytes(&mut _sbss as *mut u8, 0, count) };

let count = unsafe { &_edata as *const u8 as usize - &_sdata as *const u8 as usize };
unsafe { ptr::copy_nonoverlapping(&_sidata as *const u8, &mut _sdata as *mut u8, count) };

// Call user entry point
unsafe extern "Rust" {
safe fn main() -> !;
}

main()
use core::arch::global_asm;

global_asm!(
".text

.syntax unified
.global _sbss
.global _ebss

.global _sdata
.global _edata
.global _sidata

.global main
.global Reset

.type Reset,%function
.thumb_func
Reset:

_init_bss:
movs r2, #0
ldr r0, =_sbss
ldr r1, =_ebss

1:
cmp r1, r0
beq _init_data
strb r2, [r0]
add r0, #1
b 1b

_init_data:
ldr r0, =_sdata
ldr r1, =_edata
ldr r2, =_sidata

1:
cmp r0, r1
beq _main_trampoline
ldrb r3, [r2]
strb r3, [r0]
add r0, #1
add r2, #1
b 1b
_main_trampoline:
ldr r0, =main
bx r0"
);

unsafe extern "C" {
pub safe fn Reset() -> !;
}

// The reset vector, a pointer into the reset handler
Expand Down
6 changes: 6 additions & 0 deletions ci/script.sh
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,12 @@ main() {
edition_check
popd

pushd app-unsound
cargo build
qemu_check target/thumbv7m-none-eabi/debug/app
edition_check
popd

popd


Expand Down
1 change: 1 addition & 0 deletions src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
- [The smallest `#![no_std]` program](./smallest-no-std.md)
- [Memory layout](./memory-layout.md)
- [A `main` interface](./main.md)
- [Why don't we initialize `.data` and `.bss` using Rust](./sections-in-rust.md)
- [Exception handling](./exceptions.md)
- [Assembly on stable](./asm.md)
- [Logging with symbols](./logging.md)
Expand Down
18 changes: 12 additions & 6 deletions src/main.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ Let's go into the details of these changes:
```

We associate symbols to the start and end addresses of the `.bss` and `.data` sections, which we'll
later use from Rust code.
later use to initialize them.

``` text
{{#include ../ci/main/rt2/link.x:43}}
Expand All @@ -210,18 +210,24 @@ memory (Flash); the LMA is where in Flash those initial values are stored.

Finally, we associate a symbol to the LMA of `.data`.

On the Rust side, we zero the `.bss` section and initialize the `.data` section. We can reference
the symbols we created in the linker script from the Rust code. The *addresses*[^1] of these symbols are
Using our initialization code, we zero the `.bss` section and initialize the `.data` section. We can reference
the symbols we created in the linker script from the code. The *addresses*[^1] of these symbols are
the boundaries of the `.bss` and `.data` sections.

The updated reset handler is shown below:
We could write the initialization `.bss` and `.data` section code in pure Rust code. In fact, earlier
versions of this book did so. However, several soundness questions have been raised over time,
and it is no longer considered good practice to initialize them in Rust code. See the
[Why don't we initialize .data and .bss using Rust](./sections-in-rust.md) section of the book for more details.
We will write the initialization code using the `global_asm!` macro to define our reset handler.

The updated reset handler, now written in `Thumb-2` assembly, is shown below:

``` console
$ head -n33 ../rt/src/lib.rs
$ head -n53 ../rt/src/lib.rs
```

``` rust
{{#include ../ci/main/rt2/src/lib.rs:1:32}}
{{#include ../ci/main/rt2/src/lib.rs:1:53}}
```

Now end users can directly and indirectly make use of `static` variables without running into
Expand Down
Loading
Loading