Skip to content

Commit

Permalink
Read args streaming
Browse files Browse the repository at this point in the history
  • Loading branch information
mohanson committed Jan 2, 2025
1 parent 68a5331 commit 48b2a3b
Show file tree
Hide file tree
Showing 23 changed files with 407 additions and 181 deletions.
30 changes: 30 additions & 0 deletions definitions/src/asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,36 @@ pub struct AsmCoreMachine {
pub frames_ptr: u64,
}

impl Clone for AsmCoreMachine {
fn clone(&self) -> Self {
let mut r = Self { ..*self };
let memory_layout = Layout::array::<u8>(r.memory_size as usize).unwrap();
let flags_layout = Layout::array::<u8>(r.flags_size as usize).unwrap();
let frames_layout = Layout::array::<u8>(r.frames_size as usize).unwrap();
unsafe {
r.memory_ptr = alloc(memory_layout) as u64;
std::ptr::copy_nonoverlapping(
self.memory_ptr as *const u8,
r.memory_ptr as *mut u8,
r.memory_size as usize,
);
r.flags_ptr = alloc_zeroed(flags_layout) as u64;
std::ptr::copy_nonoverlapping(
self.flags_ptr as *const u8,
r.flags_ptr as *mut u8,
r.flags_size as usize,
);
r.frames_ptr = alloc_zeroed(frames_layout) as u64;
std::ptr::copy_nonoverlapping(
self.frames_ptr as *const u8,
r.frames_ptr as *mut u8,
r.frames_size as usize,
);
}
r
}
}

impl Drop for AsmCoreMachine {
fn drop(&mut self) {
let memory_layout = Layout::array::<u8>(self.memory_size as usize).unwrap();
Expand Down
1 change: 1 addition & 0 deletions definitions/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub const MEMORY_FRAMESIZE: usize = 1 << MEMORY_FRAME_SHIFTS; // 256 KB
pub const MEMORY_FRAME_PAGE_SHIFTS: usize = MEMORY_FRAME_SHIFTS - RISCV_PAGE_SHIFTS;

pub const DEFAULT_MEMORY_SIZE: usize = 4 << 20; // 4 MB
pub const DEFAULT_STACK_SIZE: usize = 1 << 20; // 1 MB

pub const ISA_IMC: u8 = 0b0000_0000;
pub const ISA_B: u8 = 0b0000_0001;
Expand Down
14 changes: 11 additions & 3 deletions examples/check_real_memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use std::process::{id, Command};
use ckb_vm::{
machine::{
asm::{AsmCoreMachine, AsmMachine},
DefaultMachineBuilder, VERSION0,
ArgsIterStatic, DefaultMachineBuilder, VERSION0,
},
ISA_IMC,
};
Expand Down Expand Up @@ -168,7 +168,11 @@ fn check_asm(memory_size: usize) -> Result<(), ()> {
let core = DefaultMachineBuilder::new(asm_core).build();
let mut machine = AsmMachine::new(core);
machine
.load_program(&Bytes::from(BIN_PATH_BUFFER), &vec![Bytes::from(BIN_NAME)])
.load_program(
&Bytes::from(BIN_PATH_BUFFER),
1,
ArgsIterStatic::new(vec![Bytes::from(BIN_NAME)]),
)
.unwrap();
let result = machine.run();
assert!(result.is_ok());
Expand All @@ -192,7 +196,11 @@ fn check_asm_in_thread(memory_size: usize) -> Result<(), ()> {
let core = DefaultMachineBuilder::new(asm_core).build();
let mut machine = AsmMachine::new(core);
machine
.load_program(&Bytes::from(BIN_PATH_BUFFER), &vec![Bytes::from(BIN_NAME)])
.load_program(
&Bytes::from(BIN_PATH_BUFFER),
1,
ArgsIterStatic::new(vec![Bytes::from(BIN_NAME)]),
)
.unwrap();
let thread_join_handle = thread::spawn(move || {
let result = machine.run();
Expand Down
6 changes: 3 additions & 3 deletions examples/ckb_vm_runner.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use ckb_vm::cost_model::estimate_cycles;
use ckb_vm::registers::{A0, A7};
use ckb_vm::{Bytes, CoreMachine, Memory, Register, SupportMachine, Syscalls};
use ckb_vm::{ArgsIterStatic, Bytes, CoreMachine, Memory, Register, SupportMachine, Syscalls};

pub struct DebugSyscall {}

Expand Down Expand Up @@ -49,7 +49,7 @@ fn main_asm(code: Bytes, args: Vec<Bytes>) -> Result<(), Box<dyn std::error::Err
.syscall(Box::new(DebugSyscall {}))
.build();
let mut machine = ckb_vm::machine::asm::AsmMachine::new(core);
machine.load_program(&code, &args)?;
machine.load_program(&code, args.len(), ArgsIterStatic::new(args))?;
let exit = machine.run();
let cycles = machine.machine.cycles();
println!(
Expand All @@ -71,7 +71,7 @@ fn main_int(code: Bytes, args: Vec<Bytes>) -> Result<(), Box<dyn std::error::Err
let machine_builder = ckb_vm::DefaultMachineBuilder::new(core_machine)
.instruction_cycle_func(Box::new(estimate_cycles));
let mut machine = machine_builder.syscall(Box::new(DebugSyscall {})).build();
machine.load_program(&code, &args)?;
machine.load_program(&code, args.len(), ArgsIterStatic::new(args))?;
let exit = machine.run();
let cycles = machine.cycles();
println!(
Expand Down
13 changes: 7 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,18 @@ pub use crate::{
debugger::Debugger,
instructions::{Instruction, Register},
machine::{
trace::TraceMachine, CoreMachine, DefaultCoreMachine, DefaultMachine,
DefaultMachineBuilder, InstructionCycleFunc, Machine, SupportMachine,
trace::TraceMachine, ArgsIterStatic, ArgsIterStream, CoreMachine, DefaultCoreMachine,
DefaultMachine, DefaultMachineBuilder, InstructionCycleFunc, Machine, SupportMachine,
},
memory::{flat::FlatMemory, sparse::SparseMemory, wxorx::WXorXMemory, Memory},
syscalls::Syscalls,
};
pub use bytes::Bytes;

pub use ckb_vm_definitions::{
registers, DEFAULT_MEMORY_SIZE, ISA_A, ISA_B, ISA_IMC, ISA_MOP, MEMORY_FRAMESIZE,
MEMORY_FRAME_SHIFTS, RISCV_GENERAL_REGISTER_NUMBER, RISCV_PAGESIZE, RISCV_PAGE_SHIFTS,
registers, DEFAULT_MEMORY_SIZE, DEFAULT_STACK_SIZE, ISA_A, ISA_B, ISA_IMC, ISA_MOP,
MEMORY_FRAMESIZE, MEMORY_FRAME_SHIFTS, RISCV_GENERAL_REGISTER_NUMBER, RISCV_PAGESIZE,
RISCV_PAGE_SHIFTS,
};

pub use error::Error;
Expand All @@ -47,7 +48,7 @@ pub fn run<R: Register, M: Memory<REG = R> + Default>(
WXorXMemory::new(M::default()),
);
let mut machine = TraceMachine::new(DefaultMachineBuilder::new(core_machine).build());
machine.load_program(program, args)?;
machine.load_program(program, args.len(), ArgsIterStatic::new(args.to_vec()))?;
machine.run()
}

Expand All @@ -63,7 +64,7 @@ pub fn run_with_memory<R: Register, M: Memory<REG = R>>(
WXorXMemory::new(memory),
);
let mut machine = TraceMachine::new(DefaultMachineBuilder::new(core_machine).build());
machine.load_program(program, args)?;
machine.load_program(program, args.len(), ArgsIterStatic::new(args.to_vec()))?;
machine.run()
}

Expand Down
20 changes: 16 additions & 4 deletions src/machine/asm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,12 @@ impl<'a> FastMemory<'a> {
}
}

impl<'a> Clone for FastMemory<'a> {
fn clone(&self) -> Self {
unimplemented!()
}
}

impl<'a> Memory for FastMemory<'a> {
type REG = u64;

Expand Down Expand Up @@ -666,18 +672,24 @@ impl AsmMachine {
self.machine.inner.max_cycles = cycles;
}

pub fn load_program(&mut self, program: &Bytes, args: &[Bytes]) -> Result<u64, Error> {
self.machine.load_program(program, args)
pub fn load_program(
&mut self,
program: &Bytes,
argc: usize,
args: impl Iterator<Item = Result<Bytes, Error>>,
) -> Result<u64, Error> {
self.machine.load_program(program, argc, args)
}

pub fn load_program_with_metadata(
&mut self,
program: &Bytes,
metadata: &ProgramMetadata,
args: &[Bytes],
argc: usize,
args: impl Iterator<Item = Result<Bytes, Error>>,
) -> Result<u64, Error> {
self.machine
.load_program_with_metadata(program, metadata, args)
.load_program_with_metadata(program, metadata, argc, args)
}

pub fn run(&mut self) -> Result<i8, Error> {
Expand Down
114 changes: 103 additions & 11 deletions src/machine/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,8 @@ pub trait SupportMachine: CoreMachine {

fn initialize_stack(
&mut self,
args: &[Bytes],
argc: usize,
args: impl Iterator<Item = Result<Bytes, Error>>,
stack_start: u64,
stack_size: u64,
) -> Result<u64, Error> {
Expand All @@ -183,7 +184,7 @@ pub trait SupportMachine: CoreMachine {
// reading "argc" will return an unexpected data. This situation is not very common.
//
// See https://github.com/nervosnetwork/ckb-vm/issues/106 for more details.
if self.version() >= VERSION1 && args.is_empty() {
if self.version() >= VERSION1 && argc == 0 {
let argc_size = u64::from(Self::REG::BITS / 8);
let origin_sp = stack_start + stack_size;
let unaligned_sp_address = origin_sp - argc_size;
Expand All @@ -198,18 +199,24 @@ pub trait SupportMachine: CoreMachine {
self.set_register(SP, Self::REG::from_u64(stack_start + stack_size));
// First value in this array is argc, then it contains the address(pointer)
// of each argv object.
let mut values = vec![Self::REG::from_u64(args.len() as u64)];
let mut values = vec![Self::REG::from_u64(argc as u64)];
for arg in args {
let arg = arg?;
let len = Self::REG::from_u64(arg.len() as u64 + 1);
let address = self.registers()[SP].overflowing_sub(&len);

self.memory_mut().store_bytes(address.to_u64(), arg)?;
self.memory_mut().store_bytes(address.to_u64(), &arg)?;
self.memory_mut()
.store_byte(address.to_u64() + arg.len() as u64, 1, 0)?;

values.push(address.clone());
self.set_register(SP, address);
}
if values.len() != argc + 1 {
return Err(Error::Unexpected(String::from(
"argc and argc do not match",
)));
}
if self.version() >= VERSION1 {
// There are 2 standard requirements of the initialized stack:
// 1. argv[argc] should contain a null pointer here, hence we are
Expand Down Expand Up @@ -575,9 +582,14 @@ impl<Inner: CoreMachine> Display for DefaultMachine<Inner> {
}

impl<Inner: SupportMachine> DefaultMachine<Inner> {
pub fn load_program(&mut self, program: &Bytes, args: &[Bytes]) -> Result<u64, Error> {
pub fn load_program(
&mut self,
program: &Bytes,
argc: usize,
args: impl Iterator<Item = Result<Bytes, Error>>,
) -> Result<u64, Error> {
let elf_bytes = self.load_elf(program, true)?;
let stack_bytes = self.initialize(args)?;
let stack_bytes = self.initialize(argc, args)?;
let bytes = elf_bytes.checked_add(stack_bytes).ok_or_else(|| {
Error::Unexpected(String::from(
"The bytes count overflowed on loading program",
Expand All @@ -590,10 +602,11 @@ impl<Inner: SupportMachine> DefaultMachine<Inner> {
&mut self,
program: &Bytes,
metadata: &ProgramMetadata,
args: &[Bytes],
argc: usize,
args: impl Iterator<Item = Result<Bytes, Error>>,
) -> Result<u64, Error> {
let elf_bytes = self.load_binary(program, metadata, true)?;
let stack_bytes = self.initialize(args)?;
let stack_bytes = self.initialize(argc, args)?;
let bytes = elf_bytes.checked_add(stack_bytes).ok_or_else(|| {
Error::Unexpected(String::from(
"The bytes count overflowed on loading program",
Expand All @@ -602,7 +615,11 @@ impl<Inner: SupportMachine> DefaultMachine<Inner> {
Ok(bytes)
}

fn initialize(&mut self, args: &[Bytes]) -> Result<u64, Error> {
fn initialize(
&mut self,
argc: usize,
args: impl Iterator<Item = Result<Bytes, Error>>,
) -> Result<u64, Error> {
for syscall in &mut self.syscalls {
syscall.initialize(&mut self.inner)?;
}
Expand All @@ -611,8 +628,12 @@ impl<Inner: SupportMachine> DefaultMachine<Inner> {
}
let memory_size = self.memory().memory_size();
let stack_size = memory_size / 4;
let stack_bytes =
self.initialize_stack(args, (memory_size - stack_size) as u64, stack_size as u64)?;
let stack_bytes = self.initialize_stack(
argc,
args,
(memory_size - stack_size) as u64,
stack_size as u64,
)?;
// Make sure SP is 16 byte aligned
if self.inner.version() >= VERSION1 {
debug_assert!(self.registers()[SP].to_u64() % 16 == 0);
Expand Down Expand Up @@ -766,6 +787,77 @@ impl Pause {
}
}

pub struct ArgsIterStream<'a, M: Memory> {
memory: &'a mut M,
argc: M::REG,
argp: M::REG,
cidx: M::REG,
}

impl<'a, M: Memory> ArgsIterStream<'a, M> {
pub fn new(memory: &'a mut M, argc: M::REG, argp: M::REG) -> Self {
Self {
memory,
argc,
argp,
cidx: M::REG::zero(),
}
}
}

impl<'a, M: Memory> Iterator for ArgsIterStream<'a, M> {
type Item = Result<Bytes, Error>;

fn next(&mut self) -> Option<Self::Item> {
if self.cidx.ge(&self.argc).to_u8() == 1 {
return None;
}
let addr = match M::REG::BITS {
32 => self.memory.load32(&self.argp),
64 => self.memory.load64(&self.argp),
_ => unreachable!(),
};
if let Err(err) = addr {
return Some(Err(err));
};
let addr = addr.unwrap();
let cstr = self.memory.load_c_string(&addr);
if let Err(err) = cstr {
return Some(Err(err));
};
let cstr = cstr.unwrap();
self.cidx = self.cidx.overflowing_add(&M::REG::from_u8(1));
self.argp = self
.argp
.overflowing_add(&M::REG::from_u8(M::REG::BITS / 8));
Some(Ok(cstr))
}
}

pub struct ArgsIterStatic {
argv: Vec<Bytes>,
cidx: usize,
}

impl ArgsIterStatic {
pub fn new(argv: Vec<Bytes>) -> Self {
Self { argv, cidx: 0 }
}
}

impl Iterator for ArgsIterStatic {
type Item = Result<Bytes, Error>;

fn next(&mut self) -> Option<Self::Item> {
if self.cidx >= self.argv.len() {
return None;
}
let cstr = self.argv[self.cidx].clone();
self.cidx += 1;
return Some(Ok(cstr));
}
}

#[cfg(test)]
mod tests {
use std::sync::atomic::AtomicU8;
Expand Down
Loading

0 comments on commit 48b2a3b

Please sign in to comment.