Skip to content

Instantly share code, notes, and snippets.

@skoe
Last active September 12, 2022 18:32
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save skoe/dbd3add2fc3baa600e9ebc995ddf0302 to your computer and use it in GitHub Desktop.
Save skoe/dbd3add2fc3baa600e9ebc995ddf0302 to your computer and use it in GitHub Desktop.
Pure Rust Start-Up Code - Attempt 2
Add this to the linker script:
.text :
{
. = ALIGN(4);
/* Let the linker do the pointer arithmetic. The following values must
* have the same layout as struct MemInfo in the Rust init
* implementation. It must only contain FFI-safe types. For `LONG` refer
* to the GNU ld manual which is referenced by the LLVM lld manual:
* https://sourceware.org/binutils/docs/ld/Output-Section-Data.html
*
* struct MemInfo {
*/
_mem_info = .;
LONG(_sbss);
LONG((_ebss - _sbss) / 4);
LONG(_sdata);
LONG((_edata - _sdata) / 4);
LONG(_sidata);
/* } */
*(.text .text.*);
. = ALIGN(4);
} > FLASH
#[no_mangle]
pub unsafe extern "C" fn ResetHandler() -> ! {
run();
}
/// Contains information about the DATA and BSS output sections.
///
/// This struct is used to transfer information about these sections from the
/// linker script to the init code. This is done in an FFI-safe manner by
/// providing a raw pointer to machine sized words and a length per memory area.
/// Note that directly importing a slice is not considered FFI-safe at the time
/// of writing.
///
/// Importing pointers like `_sdata` and `_edata` and performing pointer
/// arithmetic on them directly may lead to Undefined Behavior, because the
/// compiler may assume they come from different allocations and thus performing
/// undesirable optimizations on them. => I wonder why it should assume that.
///
/// TODO: Check whether something like `MaybeUninit` is needed here.
/// => I think not, because the compiler cannot assume any state of the target
/// memory and therefore has to write to it.
/// TODO: Make it generic regarding word size.
#[repr(C)]
struct MemInfo {
/// BSS segment, must be machine word aligned.
sbss: *mut u32,
/// Number of words in the BSS segment.
lbss: usize,
/// DATA segment in RAM, must be machine word aligned.
sdata: *mut u32,
/// Number of words in the DATA segment.
ldata: usize,
/// LMA address of DATA segment, must be machine word aligned.
sidata: *const u32,
}
pub unsafe fn run() -> ! {
extern "C" { static _mem_info: MemInfo; }
let bss = slice::from_raw_parts_mut(_mem_info.sbss, _mem_info.lbss);
for i in bss {
*i = 0;
}
let data = slice::from_raw_parts_mut(_mem_info.sdata, _mem_info.ldata);
let idata = slice::from_raw_parts(_mem_info.sidata, _mem_info.ldata);
data.copy_from_slice(idata);
// Don't cross this line with loads and stores. The initializations
// done above could be "invisible" to the compiler, because we write to the
// same memory location that is used by statics after this point.
// Additionally, we assume that no statics are accessed before this point.
atomic::compiler_fence(Ordering::AcqRel);
main();
}
pub fn main() -> ! {
loop {
// to work around https://github.com/rust-lang/rust/issues/28728
atomic::spin_loop_hint();
}
}
@berkus
Copy link

berkus commented Sep 12, 2022

for i in bss {
*i = 0;
}

This is converted to a call to memset intrinsic.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment