Linker scripts, addr_of and undefined behaviour

⚓ Rust    📅 2026-01-16    👤 surdeus    👁️ 1      

surdeus

A common pattern used in the embedded world works as follows:

First you define an area in the linker script, and expose its boundaries through symbols (keep in mind that in this specific example the area has a fixed size, but this is not always the case, and more often then note the actual size depends on the compilation artifact itself)

/* ... */
.heap (NOLOAD)
{
    __heap_start = .;
    . += 4M;  
    __heap_end = .;
} > SRAM
/* ... */

Then in the application code you access that area in this way:

unsafe extern "C" {
    unsafe static mut __heap_start: u8;
    unsafe static mut __heap_end: u8;
}

unsafe {
    allocator::init(addr_of_mut!(__heap_start), addr_of_mut!(__heap_end));
}

My question is, aren't all accesses through addr_of_mut!(__heap_start) and addr_of_mut!(__heap_end) UB?

The main reason for that any access outside that u8 is technically outside its allocation because Rust doesn't now about our linker script shenanigans.

For example, the cortex-m crate does exactly this.

If this is UB, what would be the correct way of doing this? Is casting the pointer to usize and then back to pointer through with_exposed_provenance a better approach? Since it deletes provenance and then gives the compiler a chance to assign a better one? Or is this something we just have to deal with and the compiler is somehow guaranteed to always behave in these scenarios?

1 post - 1 participant

Read full topic

🏷️ Rust_feed