diff --git a/crates/asm/src/lib.rs b/crates/asm/src/lib.rs index 1ed703c..19abc0e 100644 --- a/crates/asm/src/lib.rs +++ b/crates/asm/src/lib.rs @@ -98,12 +98,15 @@ pub const unsafe extern "C" fn strlen(s: *const u8) -> usize { /// Function pointer type for the main application entry point. /// The function receives argc and argv and should return an exit code. +#[cfg(not(test))] pub type MainFn = unsafe extern "C" fn(i32, *const *const u8) -> i32; +#[cfg(not(test))] static mut MAIN_FN: Option = None; /// Register the main function to be called from the entry point. /// This must be called before the program starts (e.g., in a constructor). +#[cfg(not(test))] pub fn register_main(main_fn: MainFn) { unsafe { MAIN_FN = Some(main_fn); @@ -125,6 +128,7 @@ pub fn register_main(main_fn: MainFn) { /// ```rust,ignore /// unsafe extern "C" fn main(argc: i32, argv: *const *const u8) -> i32` /// ``` +#[cfg(not(test))] #[unsafe(no_mangle)] pub unsafe extern "C" fn entry_rust(stack: *const usize) -> i32 { // Read argc and argv from stack @@ -141,155 +145,11 @@ pub unsafe extern "C" fn entry_rust(stack: *const usize) -> i32 { // External main function that must be defined by the binary using this crate. // Signature: `unsafe extern "C" fn main(argc: i32, argv: *const *const u8) -> // i32` +#[cfg(not(test))] unsafe extern "C" { fn main(argc: i32, argv: *const *const u8) -> i32; } -#[cfg(target_arch = "x86_64")] -mod entry { - use core::arch::naked_asm; - - /// Entry point that receives stack pointer directly from kernel. - /// On `x86_64` Linux at program start: - /// - /// - `[rsp]` = argc - /// - `[rsp+8]` = argv[0] - /// - `[rsp+16]` = argv[1] - /// - ... - /// - `[rsp+8n]` = NULL - /// - `[rsp+8n+8]` = envp[0] - /// - /// # Safety - /// - /// This is a naked function with no prologue or epilogue. It directly - /// manipulates the stack pointer (`rsp`) and assumes it was called by the - /// kernel with a valid stack containing argc and argv. The function: - /// - /// - Reads from `[rsp]` without validating the pointer - /// - Modifies `rsp` directly (16-byte alignment) - /// - Does not preserve any registers - /// - Does not return normally (exits via syscall) - /// - /// This function MUST only be used as the program entry point (`_start`). - /// Calling it from any other context is undefined behavior. This has been - /// your safety notice. I WILL put UB in your Rust program. - #[unsafe(no_mangle)] - #[unsafe(naked)] - pub unsafe extern "C" fn _start() { - naked_asm!( - // Move stack pointer to first argument register - "mov rdi, rsp", - // Align stack to 16-byte boundary (System V AMD64 ABI requirement) - "and rsp, -16", - // Call into Rust code - "call {entry_rust}", - // Move return code to syscall argument - "mov rdi, rax", - // Exit syscall - "mov rax, 60", // SYS_exit - "syscall", - entry_rust = sym super::entry_rust, - ); - } -} - -#[cfg(target_arch = "aarch64")] -mod entry { - use core::arch::naked_asm; - - /// Entry point that receives stack pointer directly from kernel. - /// On `aarch64` Linux at program start, the stack layout is identical - /// to x86_64: - /// - /// - `[sp]` = argc - /// - `[sp+8]` = argv[0] - /// - ... - /// - /// # Safety - /// - /// This is a naked function with no prologue or epilogue. It directly - /// manipulates the stack pointer (`sp`) and assumes it was called by the - /// kernel with a valid stack containing argc and argv. The function: - /// - /// - Reads from `[sp]` without validating the pointer - /// - Modifies `sp` directly (16-byte alignment) - /// - Does not preserve any registers - /// - Does not return normally (exits via SVC instruction) - /// - /// This function MUST only be used as the program entry point (`_start`). - /// Calling it from any other context is undefined behavior. - #[unsafe(no_mangle)] - #[unsafe(naked)] - pub unsafe extern "C" fn _start() { - naked_asm!( - // Move stack pointer to first argument register - "mov x0, sp", - // Align stack to 16-byte boundary (AArch64 ABI requirement) - "mov x9, sp", - "and x9, x9, #-16", - "mov sp, x9", - // Call into Rust code - "bl {entry_rust}", - // Move return code to syscall argument - "mov x0, x0", - // Exit syscall - "mov x8, 93", // SYS_exit - "svc #0", - entry_rust = sym super::entry_rust, - ); - } -} - -#[cfg(target_arch = "riscv64")] -mod entry { - use core::arch::naked_asm; - - /// Entry point that receives stack pointer directly from kernel. - /// On `riscv64` Linux at program start, the stack layout is identical - /// to x86_64: - /// - /// - `[sp]` = argc - /// - `[sp+8]` = argv[0] - /// - ... - /// - /// # Safety - /// - /// This is a naked function with no prologue or epilogue. It directly - /// manipulates the stack pointer (`sp`) and assumes it was called by the - /// kernel with a valid stack containing argc and argv. The function: - /// - /// - Reads from `[sp]` without validating the pointer - /// - Modifies `sp` directly (16-byte alignment) - /// - Does not preserve any registers - /// - Does not return normally (exits via ECALL instruction) - /// - /// This function MUST only be used as the program entry point (`_start`). - /// Calling it from any other context is undefined behavior. - #[unsafe(no_mangle)] - #[unsafe(naked)] - pub unsafe extern "C" fn _start() { - naked_asm!( - // Move stack pointer to first argument register - "mv a0, sp", - // Align stack to 16-byte boundary (RISC-V ABI requirement) - "andi sp, sp, -16", - // Call into Rust code - "call {entry_rust}", - // Move return code to syscall argument - "mv a0, a0", - // Exit syscall - "li a7, 93", // SYS_exit - "ecall", - entry_rust = sym super::entry_rust, - ); - } -} - -// Re-export the entry point -#[cfg(target_arch = "x86_64")] pub use entry::_start; -#[cfg(target_arch = "aarch64")] pub use entry::_start; -#[cfg(target_arch = "riscv64")] pub use entry::_start; - /// Direct syscall to open a file /// /// # Returns diff --git a/microfetch/src/main.rs b/microfetch/src/main.rs index 25e855c..fcefcc8 100644 --- a/microfetch/src/main.rs +++ b/microfetch/src/main.rs @@ -3,12 +3,59 @@ extern crate alloc; -use core::panic::PanicInfo; +use core::{arch::naked_asm, panic::PanicInfo}; use microfetch_alloc::BumpAllocator; // Re-export libc replacement functions from asm crate pub use microfetch_asm::{memcpy, memset, strlen}; -use microfetch_asm::{sys_exit, sys_write}; +use microfetch_asm::{entry_rust, sys_exit, sys_write}; + +#[cfg(target_arch = "x86_64")] +#[unsafe(no_mangle)] +#[unsafe(naked)] +unsafe extern "C" fn _start() { + naked_asm!( + "mov rdi, rsp", + "and rsp, -16", + "call {entry_rust}", + "mov rdi, rax", + "mov rax, 60", + "syscall", + entry_rust = sym entry_rust, + ); +} + +#[cfg(target_arch = "aarch64")] +#[unsafe(no_mangle)] +#[unsafe(naked)] +unsafe extern "C" fn _start() { + naked_asm!( + "mov x0, sp", + "mov x9, sp", + "and x9, x9, #-16", + "mov sp, x9", + "bl {entry_rust}", + "mov x0, x0", + "mov x8, 93", + "svc #0", + entry_rust = sym entry_rust, + ); +} + +#[cfg(target_arch = "riscv64")] +#[unsafe(no_mangle)] +#[unsafe(naked)] +unsafe extern "C" fn _start() { + naked_asm!( + "mv a0, sp", + "andi sp, sp, -16", + "call {entry_rust}", + "mv a0, a0", + "li a7, 93", + "ecall", + entry_rust = sym entry_rust, + ); +} // Global allocator #[global_allocator]