From 5c342eec441c5d4cb3c79b57767dfc7041108189 Mon Sep 17 00:00:00 2001 From: Han Date: Fri, 24 Oct 2025 19:57:11 +0800 Subject: [PATCH] Use `zksync-os` linker scripts (#178) --- crates/test-utils/src/host.rs | 2 +- crates/zkvm/airbender/src/client.rs | 1 + .../src/compiler/rust_rv32ima/link.x | 48 ++++++++----- .../src/compiler/rust_rv32ima/memory.x | 7 +- crates/zkvm/airbender/src/error.rs | 4 +- crates/zkvm/sp1/src/lib.rs | 5 +- crates/zkvm/ziren/src/lib.rs | 5 +- crates/zkvm/zisk/src/lib.rs | 5 +- tests/airbender/basic/src/airbender_rt.rs | 71 ++++++++++++++----- 9 files changed, 99 insertions(+), 49 deletions(-) diff --git a/crates/test-utils/src/host.rs b/crates/test-utils/src/host.rs index 222b9ab..a94152f 100644 --- a/crates/test-utils/src/host.rs +++ b/crates/test-utils/src/host.rs @@ -92,6 +92,6 @@ where fn assert_output(&self, public_values: &[u8]) { let output = T::Program::compute(self.inner.clone()); let digest = D::digest(T::Program::io_serde().serialize(&output).unwrap()); - assert_eq!(digest.as_slice(), public_values) + assert_eq!(&*digest, public_values) } } diff --git a/crates/zkvm/airbender/src/client.rs b/crates/zkvm/airbender/src/client.rs index 12391d5..0594c50 100644 --- a/crates/zkvm/airbender/src/client.rs +++ b/crates/zkvm/airbender/src/client.rs @@ -66,6 +66,7 @@ impl AirbenderSdk { if !output.status.success() { return Err(AirbenderError::AirbenderRunFailed { status: output.status, + stderr: String::from_utf8_lossy(&output.stderr).into_owned(), }); } diff --git a/crates/zkvm/airbender/src/compiler/rust_rv32ima/link.x b/crates/zkvm/airbender/src/compiler/rust_rv32ima/link.x index 1001fe2..693dc08 100644 --- a/crates/zkvm/airbender/src/compiler/rust_rv32ima/link.x +++ b/crates/zkvm/airbender/src/compiler/rust_rv32ima/link.x @@ -1,4 +1,4 @@ -/* Copied from https://github.com/matter-labs/zksync-airbender/blob/v0.5.0/examples/scripts/lds/link.x */ +/* Copied from https://github.com/matter-labs/zksync-os/blob/main/zksync_os/src/lds/link.x */ PROVIDE(_stext = ORIGIN(REGION_TEXT)); PROVIDE(_max_hart_id = 0); @@ -41,6 +41,7 @@ PROVIDE(_machine_start_trap = machine_default_start_trap); PHDRS { text PT_LOAD; + rodata PT_LOAD; data PT_LOAD; bss PT_LOAD; } @@ -66,17 +67,6 @@ SECTIONS *(.text .text.*); } > REGION_TEXT AT > REGION_TEXT :text - .rodata : ALIGN(4) - { - *(.srodata .srodata.*); - *(.rodata .rodata.*); - - /* 4-byte align the end (VMA) of this section. - This is required by LLD to ensure the LMA of the following - section will have the correct alignment. */ - . = ALIGN(4); - } > REGION_RODATA AT > REGION_RODATA :text - /* fictitious region that represents the memory available for the stack */ .stack ORIGIN(REGION_STACK) (NOLOAD) : ALIGN(4096) { @@ -86,6 +76,21 @@ SECTIONS _sstack = .; } > REGION_STACK + .rodata : ALIGN(4) + { + _sirodata = LOADADDR(.rodata); + _srodata = .; + *(.srodata .srodata.*); + *(.rodata .rodata.*); + + /* 4-byte align the end (VMA) of this section. + This is required by LLD to ensure the LMA of the following + section will have the correct alignment. */ + . = ALIGN(4); + + _erodata = .; + } > REGION_RODATA AT > REGION_RODATAINIT :rodata + .data : ALIGN(4096) { _sidata = LOADADDR(.data); @@ -132,6 +137,9 @@ SECTIONS ASSERT(ORIGIN(REGION_TEXT) % 4 == 0, " ERROR(riscv-rt): the start of the REGION_TEXT must be 4-byte aligned"); +ASSERT(ORIGIN(REGION_RODATAINIT) % 4 == 0, " +ERROR(riscv-rt): the start of the REGION_RODATAINIT must be 4-byte aligned"); + ASSERT(ORIGIN(REGION_RODATA) % 4 == 0, " ERROR(riscv-rt): the start of the REGION_RODATA must be 4-byte aligned"); @@ -153,6 +161,12 @@ ERROR(riscv-rt): the start of the REGION_STACK must be 4-byte aligned"); ASSERT(_stext % 4 == 0, " ERROR(riscv-rt): `_stext` must be 4-byte aligned"); +ASSERT(_srodata % 4 == 0 && _erodata % 4 == 0, " +BUG(riscv-rt): .rodata is not 4-byte aligned"); + +ASSERT(_sirodata % 4 == 0, " +BUG(riscv-rt): the LMA of .rodata is not 4-byte aligned"); + ASSERT(_sdata % 4 == 0 && _edata % 4 == 0, " BUG(riscv-rt): .data is not 4-byte aligned"); @@ -169,6 +183,10 @@ ASSERT(_stext + SIZEOF(.text) < ORIGIN(REGION_TEXT) + LENGTH(REGION_TEXT), " ERROR(riscv-rt): The .text section must be placed inside the REGION_TEXT region. Set _stext to an address smaller than 'ORIGIN(REGION_TEXT) + LENGTH(REGION_TEXT)'"); +/* ASSERT(_sirodata + SIZEOF(.rodata) < ORIGIN(REGION_RODATAINIT) + LENGTH(REGION_RODATAINIT), " +ERROR(riscv-rt): The init data for .rodata section must be placed inside the REGION_RODATAINIT region. +Set _sirodata to an address smaller than 'ORIGIN(REGION_RODATAINIT) + LENGTH(REGION_RODATAINIT)'"); */ + ASSERT(_sidata + SIZEOF(.data) < ORIGIN(REGION_DATAINIT) + LENGTH(REGION_DATAINIT), " ERROR(riscv-rt): The init data for .data section must be placed inside the REGION_DATAINIT region. Set _sidata to an address smaller than 'ORIGIN(REGION_DATAINIT) + LENGTH(REGION_DATAINIT)'"); @@ -184,10 +202,4 @@ then modify your build script to compile the C code _without_ the -fPIC flag. See the documentation of the `gcc::Config.fpic` method for details."); -ASSERT(SIZEOF(.data) == 0, " -.data section detected in the input files. Global variables with non-trivial -initialization are not supported yet. Variables with zero-initialization can be -linked to .bss section instead, as the platform guarantees zero-initialization -of all RAM space."); - /* Do not exceed this mark in the error messages above | */ diff --git a/crates/zkvm/airbender/src/compiler/rust_rv32ima/memory.x b/crates/zkvm/airbender/src/compiler/rust_rv32ima/memory.x index 37ee407..be1f5cb 100644 --- a/crates/zkvm/airbender/src/compiler/rust_rv32ima/memory.x +++ b/crates/zkvm/airbender/src/compiler/rust_rv32ima/memory.x @@ -1,4 +1,4 @@ -/* Copied from https://github.com/matter-labs/zksync-airbender/blob/v0.5.0/examples/scripts/lds/memory.x */ +/* Copied from https://github.com/matter-labs/zksync-os/blob/main/zksync_os/src/lds/memory.x */ MEMORY { @@ -7,9 +7,10 @@ MEMORY } REGION_ALIAS("REGION_TEXT", ROM); -REGION_ALIAS("REGION_RODATA", ROM); REGION_ALIAS("REGION_DATAINIT", ROM); +REGION_ALIAS("REGION_RODATAINIT", ROM); REGION_ALIAS("REGION_STACK", RAM); REGION_ALIAS("REGION_DATA", RAM); +REGION_ALIAS("REGION_RODATA", RAM); REGION_ALIAS("REGION_BSS", RAM); -REGION_ALIAS("REGION_HEAP", RAM); +REGION_ALIAS("REGION_HEAP", RAM); \ No newline at end of file diff --git a/crates/zkvm/airbender/src/error.rs b/crates/zkvm/airbender/src/error.rs index d29e1c9..55a87ee 100644 --- a/crates/zkvm/airbender/src/error.rs +++ b/crates/zkvm/airbender/src/error.rs @@ -42,8 +42,8 @@ pub enum AirbenderError { // Execution #[error("Failed to execute `airbender-cli run`: {0}")] AirbenderRun(#[source] io::Error), - #[error("`airbender-cli run` failed with status: {status}")] - AirbenderRunFailed { status: ExitStatus }, + #[error("`airbender-cli run` failed with status: {status}\nstderr: {stderr}")] + AirbenderRunFailed { status: ExitStatus, stderr: String }, #[error("Failed to parse public value from stdout: {0}")] ParsePublicValue(String), #[error("Failed to parse cycles from stdout: {0}")] diff --git a/crates/zkvm/sp1/src/lib.rs b/crates/zkvm/sp1/src/lib.rs index 9c9c6de..b9a1a8b 100644 --- a/crates/zkvm/sp1/src/lib.rs +++ b/crates/zkvm/sp1/src/lib.rs @@ -253,10 +253,9 @@ mod tests { use ere_zkvm_interface::{Compiler, NetworkProverConfig, ProofKind, ProverResourceType, zkVM}; use std::sync::OnceLock; - static BASIC_PROGRAM: OnceLock> = OnceLock::new(); - fn basic_program() -> Vec { - BASIC_PROGRAM + static PROGRAM: OnceLock> = OnceLock::new(); + PROGRAM .get_or_init(|| { RustRv32imaCustomized .compile(&testing_guest_directory("sp1", "basic")) diff --git a/crates/zkvm/ziren/src/lib.rs b/crates/zkvm/ziren/src/lib.rs index a32c5f9..0b08916 100644 --- a/crates/zkvm/ziren/src/lib.rs +++ b/crates/zkvm/ziren/src/lib.rs @@ -149,10 +149,9 @@ mod tests { use ere_zkvm_interface::{Compiler, ProofKind, ProverResourceType, zkVM}; use std::sync::OnceLock; - static BASIC_PROGRAM: OnceLock> = OnceLock::new(); - fn basic_program() -> Vec { - BASIC_PROGRAM + static PROGRAM: OnceLock> = OnceLock::new(); + PROGRAM .get_or_init(|| { RustMips32r2Customized .compile(&testing_guest_directory("ziren", "basic")) diff --git a/crates/zkvm/zisk/src/lib.rs b/crates/zkvm/zisk/src/lib.rs index 3864333..c3bca47 100644 --- a/crates/zkvm/zisk/src/lib.rs +++ b/crates/zkvm/zisk/src/lib.rs @@ -132,10 +132,9 @@ mod tests { /// so we have a lock to avoid that. static PROVE_LOCK: Mutex<()> = Mutex::new(()); - static BASIC_PROGRAM: OnceLock> = OnceLock::new(); - fn basic_program() -> Vec { - BASIC_PROGRAM + static PROGRAM: OnceLock> = OnceLock::new(); + PROGRAM .get_or_init(|| { RustRv64imaCustomized .compile(&testing_guest_directory("zisk", "basic")) diff --git a/tests/airbender/basic/src/airbender_rt.rs b/tests/airbender/basic/src/airbender_rt.rs index 54217db..b464dc2 100644 --- a/tests/airbender/basic/src/airbender_rt.rs +++ b/tests/airbender/basic/src/airbender_rt.rs @@ -1,17 +1,53 @@ -use core::alloc::{GlobalAlloc, Layout}; +use core::{ + alloc::{GlobalAlloc, Layout}, + ptr::addr_of_mut, +}; core::arch::global_asm!(include_str!("./asm_reduced.S")); -#[link_section = ".init.rust"] -#[export_name = "_start_rust"] +unsafe extern "C" { + // Boundaries of the heap + static mut _sheap: usize; + static mut _eheap: usize; + + // Boundaries of the .data section (and it's part in ROM) + static mut _sidata: usize; + static mut _sdata: usize; + static mut _edata: usize; + + // Boundaries of the .rodata section + static mut _sirodata: usize; + static mut _srodata: usize; + static mut _erodata: usize; +} + +unsafe fn load_to_ram(src: *const u8, dst_start: *mut u8, dst_end: *mut u8) { + let offset = dst_end.addr() - dst_start.addr(); + + unsafe { core::ptr::copy_nonoverlapping(src, dst_start, offset) }; +} + +#[unsafe(link_section = ".init.rust")] +#[unsafe(export_name = "_start_rust")] unsafe extern "C" fn start_rust() -> ! { + unsafe { + load_to_ram( + addr_of_mut!(_sirodata) as *const u8, + addr_of_mut!(_srodata) as *mut u8, + addr_of_mut!(_erodata) as *mut u8, + ); + load_to_ram( + addr_of_mut!(_sidata) as *const u8, + addr_of_mut!(_sdata) as *mut u8, + addr_of_mut!(_edata) as *mut u8, + ); + }; + crate::main(); + unsafe { core::hint::unreachable_unchecked() } } -/// A simple heap allocator. -/// -/// Allocates memory from left to right, without any deallocation. struct SimpleAlloc; unsafe impl GlobalAlloc for SimpleAlloc { @@ -29,17 +65,10 @@ static mut HEAP_POS: usize = 0; #[unsafe(no_mangle)] pub unsafe extern "C" fn sys_alloc_aligned(bytes: usize, align: usize) -> *mut u8 { - unsafe extern "C" { - // start/end of heap defined in `link.x`. - unsafe static _sheap: u8; - unsafe static _eheap: u8; - } - - // SAFETY: Single threaded, so nothing else can touch this while we're working. let mut heap_pos = unsafe { HEAP_POS }; if heap_pos == 0 { - heap_pos = unsafe { (&_sheap) as *const u8 as usize }; + heap_pos = addr_of_mut!(_sheap) as *const u8 as usize; } let offset = heap_pos & (align - 1); @@ -50,11 +79,21 @@ pub unsafe extern "C" fn sys_alloc_aligned(bytes: usize, align: usize) -> *mut u let ptr = heap_pos as *mut u8; let (heap_pos, overflowed) = heap_pos.overflowing_add(bytes); - let eheap = unsafe { (&_eheap) as *const u8 as usize }; + let eheap = addr_of_mut!(_eheap) as *const u8 as usize; if overflowed || heap_pos > eheap { - panic!("Heap exhausted"); + panic!("heap exhausted"); } unsafe { HEAP_POS = heap_pos }; ptr } + +#[cfg(all(target_arch = "riscv32", target_feature = "a"))] +#[unsafe(no_mangle)] +fn _critical_section_1_0_acquire() -> u32 { + 0 +} + +#[cfg(all(target_arch = "riscv32", target_feature = "a"))] +#[unsafe(no_mangle)] +fn _critical_section_1_0_release(_: u32) {}