Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: succinct-zkvm std-lib support #1

Open
wants to merge 1 commit into
base: n/succinct-target-only
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
feat: succinct-zkvm std-lib support
nhtyy committed Mar 13, 2025
commit 5501652ab7b53d9099989ebb58f73155df329bb0
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@ pub(crate) fn target() -> Target {
description: Some("Succinct's zero-knowledge Virtual Machine (RV32IM ISA)".into()),
tier: Some(3),
host_tools: Some(false),
std: None,
std: Some(true),
},
pointer_width: 32,
arch: "riscv32".into(),
7 changes: 7 additions & 0 deletions library/panic_abort/Cargo.toml
Original file line number Diff line number Diff line change
@@ -19,3 +19,10 @@ compiler_builtins = "0.1.0"

[target.'cfg(not(all(windows, target_env = "msvc")))'.dependencies]
libc = { version = "0.2", default-features = false }

[lints.rust.unexpected_cfgs]
level = "warn"
check-cfg = [
'cfg(bootstrap)',
'cfg(target_os, values("succinct-zkvm"))'
]
4 changes: 2 additions & 2 deletions library/panic_abort/src/lib.rs
Original file line number Diff line number Diff line change
@@ -20,7 +20,7 @@
#[cfg(target_os = "android")]
mod android;

#[cfg(target_os = "zkvm")]
#[cfg(any(target_os = "zkvm", target_os = "succinct-zkvm"))]
mod zkvm;

use core::any::Any;
@@ -40,7 +40,7 @@ pub unsafe fn __rust_start_panic(_payload: &mut dyn PanicPayload) -> u32 {
unsafe {
android::android_set_abort_message(_payload);
}
#[cfg(target_os = "zkvm")]
#[cfg(any(target_os = "zkvm", target_os = "succinct-zkvm"))]
unsafe {
zkvm::zkvm_set_abort_message(_payload);
}
3 changes: 1 addition & 2 deletions library/panic_abort/src/zkvm.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use alloc::string::String;
use core::panic::PanicPayload;

// Forward the abort message to zkVM's sys_panic. This is implemented by RISC Zero's
// platform crate which exposes system calls specifically for the zkVM.
/// Note this function works with both `zkvm` and `succinct-zkvm`.
pub(crate) unsafe fn zkvm_set_abort_message(payload: &mut dyn PanicPayload) {
let payload = payload.get();
let msg = match payload.downcast_ref::<&'static str>() {
2 changes: 2 additions & 0 deletions library/std/Cargo.toml
Original file line number Diff line number Diff line change
@@ -163,4 +163,6 @@ check-cfg = [
# and to the `backtrace` crate which messes-up with Cargo list
# of declared features, we therefor expect any feature cfg
'cfg(feature, values(any()))',
'cfg(target_os, values("succinct-zkvm"))'
]

2 changes: 1 addition & 1 deletion library/std/build.rs
Original file line number Diff line number Diff line change
@@ -60,9 +60,9 @@ fn main() {
|| target_os == "uefi"
|| target_os == "teeos"
|| target_os == "zkvm"
|| target_os == "succinct-zkvm"
|| target_os == "rtems"
|| target_os == "nuttx"

// See src/bootstrap/src/core/build_steps/synthetic_targets.rs
|| env::var("RUSTC_BOOTSTRAP_SYNTHETIC_TARGET").is_ok()
{
1 change: 1 addition & 0 deletions library/std/src/random.rs
Original file line number Diff line number Diff line change
@@ -48,6 +48,7 @@ use crate::sys::random as sys;
/// VxWorks | `randABytes` after waiting for `randSecure` to become ready
/// WASI | [`random_get`](https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md#-random_getbuf-pointeru8-buf_len-size---result-errno)
/// ZKVM | `sys_rand`
/// SUCCINCT-ZKVM | `sys_rand`
///
/// Note that the sources used might change over time.
///
4 changes: 2 additions & 2 deletions library/std/src/sys/alloc/mod.rs
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@ use crate::ptr;
// add fast paths for low alignment values.
#[allow(dead_code)]
const MIN_ALIGN: usize = if cfg!(any(
all(target_arch = "riscv32", any(target_os = "espidf", target_os = "zkvm")),
all(target_arch = "riscv32", any(target_os = "espidf", target_os = "zkvm", target_os = "succinct-zkvm")),
all(target_arch = "xtensa", target_os = "espidf"),
)) {
// The allocator on the esp-idf and zkvm platforms guarantees 4 byte alignment.
@@ -89,7 +89,7 @@ cfg_if::cfg_if! {
mod wasm;
} else if #[cfg(target_os = "xous")] {
mod xous;
} else if #[cfg(target_os = "zkvm")] {
} else if #[cfg(any(target_os = "zkvm", target_os = "succinct-zkvm"))] {
mod zkvm;
}
}
1 change: 1 addition & 0 deletions library/std/src/sys/alloc/zkvm.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::alloc::{GlobalAlloc, Layout, System};
use crate::sys::pal::abi;

/// Note this allocator works with both `zkvm` and `succinct-zkvm`.
#[stable(feature = "alloc_system_type", since = "1.28.0")]
unsafe impl GlobalAlloc for System {
#[inline]
3 changes: 3 additions & 0 deletions library/std/src/sys/pal/mod.rs
Original file line number Diff line number Diff line change
@@ -64,6 +64,9 @@ cfg_if::cfg_if! {
} else if #[cfg(target_os = "zkvm")] {
mod zkvm;
pub use self::zkvm::*;
} else if #[cfg(target_os = "succinct-zkvm")] {
mod succinct_zkvm;
pub use self::succinct_zkvm::*;
} else {
mod unsupported;
pub use self::unsupported::*;
35 changes: 35 additions & 0 deletions library/std/src/sys/pal/succint-zkvm/abi.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//! ABI definitions for symbols exported by <_>-zkvm targets.
//!
//! Ideally, these should be the minimum viable symbols to support the std-lib.
#![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;
}

unsafe extern "C" {
pub fn sys_halt();
pub fn sys_rand(recv_buf: *mut u8, words: usize);
pub fn sys_panic(msg_ptr: *const u8, len: 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_argv(out_words: *mut u32, out_nwords: usize, arg_index: usize) -> usize;
pub fn sys_alloc_aligned(nwords: usize, align: usize) -> *mut u8;
}

// Note: sys_read is not implemented for succinct-zkvm.
// However, it is a function used in the standard library, so we need to
// implement it.
pub unsafe extern "C" fn sys_read(_: u32, _: *mut u8, _: usize) -> usize {
panic!("sys_read not implemented for succinct");
}
61 changes: 61 additions & 0 deletions library/std/src/sys/pal/succint-zkvm/args.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
use crate::ffi::OsString;
use crate::fmt;

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

pub fn args() -> Args {
panic!("args not implemented for succinct");
}

impl Args {
#[cfg(target_os = "succinct-zkvm")]
fn argv(_: usize) -> OsString {
panic!("argv not implemented for succinct");
}
}

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/succint-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";
}
53 changes: 53 additions & 0 deletions library/std/src/sys/pal/succint-zkvm/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//! System bindings for the succinct-zkvm platform
//!
//! This module contains the facade (aka platform-specific) implementations of
//! OS level functionality for succinct-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.
#![forbid(unsafe_op_in_unsafe_fn)]

pub mod abi;
#[path = "../succinct-zkvm/args.rs"]
pub mod args;
pub mod env;
pub mod os;
#[path = "../unsupported/pipe.rs"]
pub mod pipe;
#[path = "../unsupported/process.rs"]
pub mod process;
#[path = "../unsupported/thread.rs"]
pub mod thread;
#[path = "../unsupported/time.rs"]
pub mod time;

use crate::io as std_io;

// 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::Error::UNSUPPORTED_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();
}
118 changes: 118 additions & 0 deletions library/std/src/sys/pal/succint-zkvm/os.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
use super::unsupported;
use crate::error::Error as StdError;
use crate::ffi::{OsStr, OsString};
use crate::marker::PhantomData;
use crate::path::{self, PathBuf};
use crate::{fmt, io};

pub fn errno() -> i32 {
0
}

pub fn error_string(_errno: i32) -> String {
"operation successful".to_string()
}

pub fn getcwd() -> io::Result<PathBuf> {
unsupported()
}

pub fn chdir(_: &path::Path) -> io::Result<()> {
unsupported()
}

pub struct SplitPaths<'a>(!, PhantomData<&'a ()>);

pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> {
panic!("unsupported")
}

impl<'a> Iterator for SplitPaths<'a> {
type Item = PathBuf;
fn next(&mut self) -> Option<PathBuf> {
self.0
}
}

#[derive(Debug)]
pub struct JoinPathsError;

pub fn join_paths<I, T>(_paths: I) -> Result<OsString, JoinPathsError>
where
I: Iterator<Item = T>,
T: AsRef<OsStr>,
{
Err(JoinPathsError)
}

impl fmt::Display for JoinPathsError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
"not supported on this platform yet".fmt(f)
}
}

impl StdError for JoinPathsError {
#[allow(deprecated)]
fn description(&self) -> &str {
"not supported on this platform yet"
}
}

pub fn current_exe() -> io::Result<PathBuf> {
unsupported()
}

pub struct Env(!);

impl Iterator for Env {
type Item = (OsString, OsString);
fn next(&mut self) -> Option<(OsString, OsString)> {
self.0
}
}

pub fn env() -> Env {
panic!("not supported on this platform")
}

impl Env {
pub fn str_debug(&self) -> impl fmt::Debug + '_ {
let Self(inner) = self;
match *inner {}
}
}

impl fmt::Debug for Env {
fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self(inner) = self;
match *inner {}
}
}

pub fn getenv(_: &OsStr) -> Option<OsString> {
panic!("getenv not implemented for succinct");
}

pub unsafe fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> {
Err(io::const_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform"))
}

pub unsafe fn unsetenv(_: &OsStr) -> io::Result<()> {
Err(io::const_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform"))
}

pub fn temp_dir() -> PathBuf {
panic!("no filesystem on this platform")
}

pub fn home_dir() -> Option<PathBuf> {
None
}

pub fn exit(_code: i32) -> ! {
crate::intrinsics::abort()
}

pub fn getpid() -> u32 {
panic!("no pids on this platform")
}
5 changes: 4 additions & 1 deletion library/std/src/sys/random/mod.rs
Original file line number Diff line number Diff line change
@@ -72,9 +72,12 @@ cfg_if::cfg_if! {
} else if #[cfg(target_os = "wasi")] {
mod wasi;
pub use wasi::fill_bytes;
} else if #[cfg(target_os = "zkvm")] {
} else if #[cfg(any(target_os = "zkvm", target_os = "succinct-zkvm"))] {
mod zkvm;
pub use zkvm::fill_bytes;
} else if #[cfg(target_os = "succinct-zkvm")] {
mod succinct_zkvm;
pub use succinct_zkvm::fill_bytes;
} else if #[cfg(any(
all(target_family = "wasm", target_os = "unknown"),
target_os = "xous",
5 changes: 5 additions & 0 deletions library/std/src/sys/random/succinct_zkvm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
use crate::sys::pal::abi;

pub fn fill_bytes(bytes: &mut [u8]) {
unsafe { abi::sys_rand(bytes.as_mut_ptr(), bytes.len()) };
}
6 changes: 6 additions & 0 deletions library/std/src/sys/random/zkvm.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::sys::pal::abi;

#[cfg(target_vendor = "risc0")]
pub fn fill_bytes(bytes: &mut [u8]) {
let (pre, words, post) = unsafe { bytes.align_to_mut::<u32>() };
if !words.is_empty() {
@@ -19,3 +20,8 @@ pub fn fill_bytes(bytes: &mut [u8]) {
pre.copy_from_slice(&buf[..pre.len()]);
post.copy_from_slice(&buf[pre.len()..pre.len() + post.len()]);
}

#[cfg(target_vendor = "succinct")]
pub fn fill_bytes(bytes: &mut [u8]) {
unsafe { abi::sys_rand(bytes.as_mut_ptr(), bytes.len()) };
}
3 changes: 3 additions & 0 deletions library/std/src/sys/stdio/mod.rs
Original file line number Diff line number Diff line change
@@ -34,6 +34,9 @@ cfg_if::cfg_if! {
} else if #[cfg(target_os = "zkvm")] {
mod zkvm;
pub use zkvm::*;
} else if #[cfg(target_os = "succinct-zkvm")] {
mod succinct_zkvm;
pub use succinct_zkvm::*;
} else {
mod unsupported;
pub use unsupported::*;
72 changes: 72 additions & 0 deletions library/std/src/sys/stdio/succinct_zkvm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
use crate::io::{self, BorrowedCursor};
use crate::sys::pal::abi::{self, fileno};

pub struct Stdin;
pub struct Stdout;
pub struct Stderr;

impl Stdin {
pub const fn new() -> Stdin {
Stdin
}
}

impl io::Read for Stdin {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
Ok(unsafe { abi::sys_read(fileno::STDIN, buf.as_mut_ptr(), buf.len()) })
}

fn read_buf(&mut self, mut buf: BorrowedCursor<'_>) -> io::Result<()> {
unsafe {
let n = abi::sys_read(fileno::STDIN, buf.as_mut().as_mut_ptr().cast(), buf.capacity());
buf.advance_unchecked(n);
}
Ok(())
}
}

impl Stdout {
pub const fn new() -> Stdout {
Stdout
}
}

impl io::Write for Stdout {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
unsafe { abi::sys_write(fileno::STDOUT, buf.as_ptr(), buf.len()) }

Ok(buf.len())
}

fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}

impl Stderr {
pub const fn new() -> Stderr {
Stderr
}
}

impl io::Write for Stderr {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
unsafe { abi::sys_write(fileno::STDERR, buf.as_ptr(), buf.len()) }

Ok(buf.len())
}

fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}

pub const STDIN_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE;

pub fn is_ebadf(_err: &io::Error) -> bool {
true
}

pub fn panic_output() -> Option<impl io::Write> {
Some(Stderr::new())
}
2 changes: 2 additions & 0 deletions library/std/src/sys/thread_local/mod.rs
Original file line number Diff line number Diff line change
@@ -29,6 +29,7 @@ cfg_if::cfg_if! {
target_os = "uefi",
target_os = "zkvm",
target_os = "trusty",
target_os = "succinct-zkvm",
))] {
mod statik;
pub use statik::{EagerStorage, LazyStorage, thread_local_inner};
@@ -93,6 +94,7 @@ pub(crate) mod guard {
target_os = "uefi",
target_os = "zkvm",
target_os = "trusty",
target_os = "succinct-zkvm",
))] {
pub(crate) fn enable() {
// FIXME: Right now there is no concept of "thread exit" on
6 changes: 6 additions & 0 deletions library/test/Cargo.toml
Original file line number Diff line number Diff line change
@@ -12,3 +12,9 @@ core = { path = "../core", public = true }

[target.'cfg(not(all(windows, target_env = "msvc")))'.dependencies]
libc = { version = "0.2.150", default-features = false }

[lints.rust.unexpected_cfgs]
level = "warn"
check-cfg = [
'cfg(target_os, values("succinct-zkvm"))'
]
2 changes: 1 addition & 1 deletion library/test/src/console.rs
Original file line number Diff line number Diff line change
@@ -317,7 +317,7 @@ pub fn run_tests_console(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> io::Resu
// - It's currently not supported for wasm targets without Emscripten nor WASI.
// - It's currently not supported for zkvm targets.
let is_instant_unsupported =
(cfg!(target_family = "wasm") && cfg!(target_os = "unknown")) || cfg!(target_os = "zkvm");
(cfg!(target_family = "wasm") && cfg!(target_os = "unknown")) || cfg!(target_os = "zkvm") || cfg!(target_os = "succinct-zkvm");

let start_time = (!is_instant_unsupported).then(Instant::now);
run_tests(opts, tests, |x| on_test_event(&x, &mut st, &mut *out))?;
6 changes: 4 additions & 2 deletions library/test/src/lib.rs
Original file line number Diff line number Diff line change
@@ -555,7 +555,7 @@ pub fn run_test(

// Emscripten can catch panics but other wasm targets cannot
let ignore_because_no_process_support = desc.should_panic != ShouldPanic::No
&& (cfg!(target_family = "wasm") || cfg!(target_os = "zkvm"))
&& (cfg!(target_family = "wasm") || cfg!(target_os = "zkvm") || cfg!(target_os = "succinct-zkvm"))
&& !cfg!(target_os = "emscripten");

if force_ignore || desc.ignore || ignore_because_no_process_support {
@@ -604,7 +604,9 @@ pub fn run_test(
// level.
let supports_threads = !cfg!(target_os = "emscripten")
&& !cfg!(target_family = "wasm")
&& !cfg!(target_os = "zkvm");
&& !cfg!(target_os = "zkvm")
&& !cfg!(target_os = "succinct-zkvm");

if supports_threads {
let cfg = thread::Builder::new().name(name.as_slice().to_owned());
let mut runtest = Arc::new(Mutex::new(Some(runtest)));
2 changes: 1 addition & 1 deletion src/bootstrap/src/core/sanity.rs
Original file line number Diff line number Diff line change
@@ -35,7 +35,7 @@ pub struct Finder {
const STAGE0_MISSING_TARGETS: &[&str] = &[
// just a dummy comment so the list doesn't get onelined
"wasm32-wali-linux-musl",
"riscv32im-succinct-zkvm-elf"
"riscv32im-succinct-zkvm-elf",
];

/// Minimum version threshold for libstdc++ required when using prebuilt LLVM