Use zksync-os linker scripts (#178)

This commit is contained in:
Han
2025-10-24 19:57:11 +08:00
committed by GitHub
parent 6a2c6e1d54
commit 5c342eec44
9 changed files with 99 additions and 49 deletions

View File

@@ -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)
}
}

View File

@@ -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(),
});
}

View File

@@ -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 | */

View File

@@ -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);

View File

@@ -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}")]

View File

@@ -253,10 +253,9 @@ mod tests {
use ere_zkvm_interface::{Compiler, NetworkProverConfig, ProofKind, ProverResourceType, zkVM};
use std::sync::OnceLock;
static BASIC_PROGRAM: OnceLock<Vec<u8>> = OnceLock::new();
fn basic_program() -> Vec<u8> {
BASIC_PROGRAM
static PROGRAM: OnceLock<Vec<u8>> = OnceLock::new();
PROGRAM
.get_or_init(|| {
RustRv32imaCustomized
.compile(&testing_guest_directory("sp1", "basic"))

View File

@@ -149,10 +149,9 @@ mod tests {
use ere_zkvm_interface::{Compiler, ProofKind, ProverResourceType, zkVM};
use std::sync::OnceLock;
static BASIC_PROGRAM: OnceLock<Vec<u8>> = OnceLock::new();
fn basic_program() -> Vec<u8> {
BASIC_PROGRAM
static PROGRAM: OnceLock<Vec<u8>> = OnceLock::new();
PROGRAM
.get_or_init(|| {
RustMips32r2Customized
.compile(&testing_guest_directory("ziren", "basic"))

View File

@@ -132,10 +132,9 @@ mod tests {
/// so we have a lock to avoid that.
static PROVE_LOCK: Mutex<()> = Mutex::new(());
static BASIC_PROGRAM: OnceLock<Vec<u8>> = OnceLock::new();
fn basic_program() -> Vec<u8> {
BASIC_PROGRAM
static PROGRAM: OnceLock<Vec<u8>> = OnceLock::new();
PROGRAM
.get_or_init(|| {
RustRv64imaCustomized
.compile(&testing_guest_directory("zisk", "basic"))

View File

@@ -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) {}