Skip to content

Commit

Permalink
zkvm: add partial std support
Browse files Browse the repository at this point in the history
Co-authored-by: Frank Laub <[email protected]>
Co-authored-by: nils <[email protected]>
Co-authored-by: Victor Graf <[email protected]>
Co-authored-by: weikengchen <[email protected]>
  • Loading branch information
5 people committed Jan 22, 2024
1 parent 966b94e commit 75d7d70
Show file tree
Hide file tree
Showing 11 changed files with 487 additions and 5 deletions.
1 change: 1 addition & 0 deletions library/std/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ fn main() {
|| target.contains("hurd")
|| target.contains("uefi")
|| target.contains("teeos")
|| target.contains("zkvm")
// See src/bootstrap/synthetic_targets.rs
|| env::var("RUSTC_BOOTSTRAP_SYNTHETIC_TARGET").is_ok()
{
Expand Down
10 changes: 5 additions & 5 deletions library/std/src/sys/pal/common/alloc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use crate::ptr;
target_arch = "sparc",
target_arch = "wasm32",
target_arch = "hexagon",
all(target_arch = "riscv32", not(target_os = "espidf")),
all(target_arch = "riscv32", not(any(target_os = "espidf", target_os = "zkvm"))),
all(target_arch = "xtensa", not(target_os = "espidf")),
))]
pub const MIN_ALIGN: usize = 8;
Expand All @@ -32,11 +32,11 @@ pub const MIN_ALIGN: usize = 8;
target_arch = "wasm64",
))]
pub const MIN_ALIGN: usize = 16;
// The allocator on the esp-idf platform guarantees 4 byte alignment.
#[cfg(any(
all(target_arch = "riscv32", target_os = "espidf"),
// The allocator on the esp-idf and zkvm platforms guarantee 4 byte alignment.
#[cfg(all(any(
all(target_arch = "riscv32", any(target_os = "espidf", target_os = "zkvm")),
all(target_arch = "xtensa", target_os = "espidf"),
))]
)))]
pub const MIN_ALIGN: usize = 4;

pub unsafe fn realloc_fallback(
Expand Down
3 changes: 3 additions & 0 deletions library/std/src/sys/pal/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ cfg_if::cfg_if! {
} else if #[cfg(target_os = "teeos")] {
mod teeos;
pub use self::teeos::*;
} else if #[cfg(target_os = "zkvm")] {
mod zkvm;
pub use self::zkvm::*;
} else {
mod unsupported;
pub use self::unsupported::*;
Expand Down
55 changes: 55 additions & 0 deletions library/std/src/sys/pal/zkvm/abi.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
//! ABI definitions for symbols exported by risc0-zkvm-platform.
// Included here so we don't have to depend on risc0-zkvm-platform.
//
// FIXME: Should we move this to the "libc" crate? It seems like other
// architectures put a lot of this kind of stuff there. But there's
// currently no risc0 fork of the libc crate, so we'd either have to
// fork it or upstream it.

#![allow(dead_code)]
pub const DIGEST_WORDS: usize = 8;

/// Standard IO file descriptors for use with sys_read and sys_write.
pub mod fileno {
pub const STDIN: u32 = 0;
pub const STDOUT: u32 = 1;
pub const STDERR: u32 = 2;
pub const JOURNAL: u32 = 3;
}

extern "C" {
// Wrappers around syscalls provided by risc0-zkvm-platform:
pub fn sys_halt();
pub fn sys_output(output_id: u32, output_value: u32);
pub fn sys_sha_compress(
out_state: *mut [u32; DIGEST_WORDS],
in_state: *const [u32; DIGEST_WORDS],
block1_ptr: *const [u32; DIGEST_WORDS],
block2_ptr: *const [u32; DIGEST_WORDS],
);
pub fn sys_sha_buffer(
out_state: *mut [u32; DIGEST_WORDS],
in_state: *const [u32; DIGEST_WORDS],
buf: *const u8,
count: u32,
);
pub fn sys_rand(recv_buf: *mut u32, words: usize);
pub fn sys_panic(msg_ptr: *const u8, len: usize) -> !;
pub fn sys_log(msg_ptr: *const u8, len: usize);
pub fn sys_cycle_count() -> usize;
pub fn sys_read(fd: u32, recv_buf: *mut u8, nrequested: usize) -> usize;
pub fn sys_write(fd: u32, write_buf: *const u8, nbytes: usize);
pub fn sys_getenv(
recv_buf: *mut u32,
words: usize,
varname: *const u8,
varname_len: usize,
) -> usize;
pub fn sys_argc() -> usize;
pub fn sys_argv(out_words: *mut u32, out_nwords: usize, arg_index: usize) -> usize;

// Allocate memory from global HEAP.
pub fn sys_alloc_words(nwords: usize) -> *mut u32;
pub fn sys_alloc_aligned(nwords: usize, align: usize) -> *mut u8;
}
15 changes: 15 additions & 0 deletions library/std/src/sys/pal/zkvm/alloc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use super::abi;
use crate::alloc::{GlobalAlloc, Layout, System};

#[stable(feature = "alloc_system_type", since = "1.28.0")]
unsafe impl GlobalAlloc for System {
#[inline]
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
abi::sys_alloc_aligned(layout.size(), layout.align())
}

#[inline]
unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {
// this allocator never deallocates memory
}
}
80 changes: 80 additions & 0 deletions library/std/src/sys/pal/zkvm/args.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
use super::{abi, WORD_SIZE};
use crate::ffi::OsString;
use crate::fmt;
use crate::sys_common::FromInner;

pub struct Args {
i_forward: usize,
i_back: usize,
count: usize,
}

pub fn args() -> Args {
let count = unsafe { abi::sys_argc() };
Args { i_forward: 0, i_back: 0, count }
}

impl Args {
/// Use sys_argv to get the arg at the requested index. Does not check that i is less than argc
/// and will not return if the index is out of bounds.
fn argv(i: usize) -> OsString {
let arg_len = unsafe { abi::sys_argv(crate::ptr::null_mut(), 0, i) };

let arg_len_words = (arg_len + WORD_SIZE - 1) / WORD_SIZE;
let words = unsafe { abi::sys_alloc_words(arg_len_words) };

let arg_len2 = unsafe { abi::sys_argv(words, arg_len_words, i) };
debug_assert_eq!(arg_len, arg_len2);

// Convert to OsString.
//
// FIXME: We can probably get rid of the extra copy here if we
// reimplement "os_str" instead of just using the generic unix
// "os_str".
let arg_bytes: &[u8] =
unsafe { crate::slice::from_raw_parts(words.cast() as *const u8, arg_len) };
OsString::from_inner(super::os_str::Buf { inner: arg_bytes.to_vec() })
}
}

impl fmt::Debug for Args {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list().finish()
}
}

impl Iterator for Args {
type Item = OsString;

fn next(&mut self) -> Option<OsString> {
if self.i_forward >= self.count - self.i_back {
None
} else {
let arg = Self::argv(self.i_forward);
self.i_forward += 1;
Some(arg)
}
}

fn size_hint(&self) -> (usize, Option<usize>) {
(self.count, Some(self.count))
}
}

impl ExactSizeIterator for Args {
fn len(&self) -> usize {
self.count
}
}

impl DoubleEndedIterator for Args {
fn next_back(&mut self) -> Option<OsString> {
if self.i_back >= self.count - self.i_forward {
None
} else {
let arg = Self::argv(self.count - 1 - self.i_back);
self.i_back += 1;
Some(arg)
}
}
}
9 changes: 9 additions & 0 deletions library/std/src/sys/pal/zkvm/env.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
pub mod os {
pub const FAMILY: &str = "";
pub const OS: &str = "";
pub const DLL_PREFIX: &str = "";
pub const DLL_SUFFIX: &str = ".elf";
pub const DLL_EXTENSION: &str = "elf";
pub const EXE_SUFFIX: &str = ".elf";
pub const EXE_EXTENSION: &str = "elf";
}
93 changes: 93 additions & 0 deletions library/std/src/sys/pal/zkvm/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
//! System bindings for the risc0 zkvm platform
//!
//! This module contains the facade (aka platform-specific) implementations of
//! OS level functionality for zkvm.
//!
//! This is all super highly experimental and not actually intended for
//! wide/production use yet, it's still all in the experimental category. This
//! will likely change over time.
const WORD_SIZE: usize = core::mem::size_of::<u32>();

pub mod alloc;
#[path = "../zkvm/args.rs"]
pub mod args;
#[path = "../unix/cmath.rs"]
pub mod cmath;
pub mod env;
#[path = "../unsupported/fs.rs"]
pub mod fs;
#[path = "../unsupported/io.rs"]
pub mod io;
#[path = "../unsupported/net.rs"]
pub mod net;
#[path = "../unsupported/once.rs"]
pub mod once;
pub mod os;
#[path = "../unix/os_str.rs"]
pub mod os_str;
#[path = "../unix/path.rs"]
pub mod path;
#[path = "../unsupported/pipe.rs"]
pub mod pipe;
#[path = "../unsupported/process.rs"]
pub mod process;
pub mod stdio;
pub mod thread_local_key;
#[path = "../unsupported/time.rs"]
pub mod time;

#[path = "../unsupported/locks/mod.rs"]
pub mod locks;
#[path = "../unsupported/thread.rs"]
pub mod thread;

#[path = "../unsupported/thread_parking.rs"]
pub mod thread_parking;

mod abi;

use crate::io as std_io;

pub mod memchr {
pub use core::slice::memchr::{memchr, memrchr};
}

// SAFETY: must be called only once during runtime initialization.
// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) {}

// SAFETY: must be called only once during runtime cleanup.
// NOTE: this is not guaranteed to run, for example when the program aborts.
pub unsafe fn cleanup() {}

pub fn unsupported<T>() -> std_io::Result<T> {
Err(unsupported_err())
}

pub fn unsupported_err() -> std_io::Error {
std_io::const_io_error!(
std_io::ErrorKind::Unsupported,
"operation not supported on this platform",
)
}

pub fn is_interrupted(_code: i32) -> bool {
false
}

pub fn decode_error_kind(_code: i32) -> crate::io::ErrorKind {
crate::io::ErrorKind::Uncategorized
}

pub fn abort_internal() -> ! {
core::intrinsics::abort();
}

pub fn hashmap_random_keys() -> (u64, u64) {
let mut buf = [0u32; 4];
unsafe {
abi::sys_rand(buf.as_mut_ptr(), 4);
};
((buf[0] as u64) << 32 + buf[1] as u64, (buf[2] as u64) << 32 + buf[3] as u64)
}
Loading

0 comments on commit 75d7d70

Please sign in to comment.