ckb-vm-debug-utils 1.1.1

Utils for ckb-debugger
Documentation
use ckb_vm::{
    Error, Memory, Register, SupportMachine, Syscalls,
    registers::{A0, A1, A2, A7},
};
use libc::{SEEK_CUR, SEEK_END, SEEK_SET};
use nix::{
    sys::stat::fstat,
    unistd::{Whence, close, lseek, read, write},
};
use std::mem::size_of;
use std::slice::from_raw_parts;

#[derive(Clone, Debug, Default)]
#[repr(C)]
struct AbiStat {
    dev: u64,
    ino: u64,
    mode: u32,
    nlink: i32,
    uid: u32,
    gid: u32,
    rdev: u64,
    __pad1: u64,
    size: i64,
    blksize: i32,
    __pad2: i32,
    blocks: i64,
    atime: i64,
    atime_nsec: i64,
    mtime: i64,
    mtime_nsec: i64,
    ctime: i64,
    ctime_nsec: i64,
    __unused4: i32,
    __unused5: i32,
}

#[derive(Default)]
pub struct Stdio {
    keep_stdios: bool,
}

impl Stdio {
    pub fn new(keep_stdios: bool) -> Self {
        Stdio { keep_stdios }
    }

    fn close<Mac: SupportMachine>(&mut self, machine: &mut Mac) -> Result<(), Error> {
        let fd = machine.registers()[A0].to_i32();
        if (fd >= 0 && fd <= 2) && self.keep_stdios {
            machine.set_register(A0, Mac::REG::zero());
            return Ok(());
        }
        let ret = match close(fd) {
            Ok(()) => 0,
            Err(e) => {
                debug!("close error: {:?}", e);
                (-1i64) as u64
            }
        };
        machine.set_register(A0, Mac::REG::from_u64(ret));
        Ok(())
    }

    fn fstat<Mac: SupportMachine>(&mut self, machine: &mut Mac) -> Result<(), Error> {
        let stat = match fstat(machine.registers()[A0].to_i32()) {
            Ok(stat) => stat,
            Err(e) => {
                debug!("fstat error: {:?}", e);
                machine.set_register(A0, Mac::REG::from_i8(-1));
                return Ok(());
            }
        };
        let mut abi_stat = AbiStat::default();
        abi_stat.dev = stat.st_dev as u64;
        abi_stat.ino = stat.st_ino;
        abi_stat.mode = stat.st_mode as u32;
        abi_stat.nlink = stat.st_nlink as i32;
        abi_stat.uid = stat.st_uid;
        abi_stat.gid = stat.st_gid;
        abi_stat.rdev = stat.st_rdev as u64;
        abi_stat.size = stat.st_size;
        abi_stat.blksize = stat.st_blksize as i32;
        abi_stat.blocks = stat.st_blocks;
        abi_stat.atime = stat.st_atime;
        abi_stat.atime_nsec = stat.st_atime_nsec;
        abi_stat.mtime = stat.st_mtime;
        abi_stat.mtime_nsec = stat.st_mtime_nsec;
        abi_stat.ctime = stat.st_ctime;
        abi_stat.ctime_nsec = stat.st_ctime_nsec;
        let len = size_of::<AbiStat>();
        let b: &[u8] = unsafe { from_raw_parts(&abi_stat as *const AbiStat as *const u8, len) };
        let addr = machine.registers()[A1].to_u64();
        machine.memory_mut().store_bytes(addr, b)?;
        machine.set_register(A0, Mac::REG::zero());
        Ok(())
    }

    fn lseek<Mac: SupportMachine>(&mut self, machine: &mut Mac) -> Result<(), Error> {
        let fd = machine.registers()[A0].to_i32();
        let offset = machine.registers()[A1].to_i64();
        let whence = match machine.registers()[A2].to_i32() {
            SEEK_SET => Whence::SeekSet,
            SEEK_CUR => Whence::SeekCur,
            SEEK_END => Whence::SeekEnd,
            _ => return Err(Error::Unexpected("Unexpected whence".into())),
        };
        let ret = lseek(fd, offset, whence).unwrap_or_else(|e| {
            debug!("lseek error: {:?}", e);
            -1
        });
        machine.set_register(A0, Mac::REG::from_i64(ret));
        Ok(())
    }

    fn read<Mac: SupportMachine>(&mut self, machine: &mut Mac) -> Result<(), Error> {
        let fd = machine.registers()[A0].to_i32();
        let addr = machine.registers()[A1].to_u64();
        let size = machine.registers()[A2].to_u64() as usize;
        let mut buf = vec![0u8; size];

        match read(fd, &mut buf) {
            Ok(read_size) => {
                machine.memory_mut().store_bytes(addr, &buf[0..read_size])?;
                machine.set_register(A0, Mac::REG::from_u64(read_size as u64));
            }
            Err(e) => {
                debug!("read error: {:?}", e);
                machine.set_register(A0, Mac::REG::from_i64(-1i64));
            }
        };
        Ok(())
    }

    fn write<Mac: SupportMachine>(&mut self, machine: &mut Mac) -> Result<(), Error> {
        let fd = machine.registers()[A0].to_i32();
        let addr = machine.registers()[A1].to_u64();
        let size = machine.registers()[A2].to_u64() as usize;
        let mut buf = vec![0u8; size];
        for i in 0..size {
            buf[i] = machine.memory_mut().load8(&Mac::REG::from_u64(addr + i as u64))?.to_u8();
        }
        let ret = write(fd, &buf).unwrap_or_else(|e| {
            debug!("write error: {:?}", e);
            (-1isize) as usize
        });
        machine.set_register(A0, Mac::REG::from_u64(ret as u64));
        Ok(())
    }

    fn writev<Mac: SupportMachine>(&mut self, machine: &mut Mac) -> Result<(), Error> {
        let fd = machine.registers()[A0].to_i32();
        let iov = machine.registers()[A1].to_u64();
        let iovcnt = machine.registers()[A2].to_u64();

        let mut written = 0;
        for i in 0..iovcnt {
            let base = machine.memory_mut().load64(&Mac::REG::from_u64(iov + i * 16))?.to_u64();
            let len = machine.memory_mut().load64(&Mac::REG::from_u64(iov + i * 16 + 8))?.to_u64();

            let buf = machine.memory_mut().load_bytes(base, len)?;

            written += match write(fd, &buf) {
                Ok(w) => w as u64,
                Err(e) => {
                    debug!("write error: {:?}", e);
                    machine.set_register(A0, Mac::REG::from_i64(-1));
                    return Ok(());
                }
            };
        }
        machine.set_register(A0, Mac::REG::from_u64(written));
        Ok(())
    }
}

impl<Mac: SupportMachine> Syscalls<Mac> for Stdio {
    fn initialize(&mut self, _machine: &mut Mac) -> Result<(), Error> {
        Ok(())
    }

    fn ecall(&mut self, machine: &mut Mac) -> Result<bool, Error> {
        match machine.registers()[A7].to_u64() {
            57 => self.close(machine)?,
            62 => self.lseek(machine)?,
            63 => self.read(machine)?,
            64 => self.write(machine)?,
            66 => self.writev(machine)?,
            80 => self.fstat(machine)?,
            _ => return Ok(false),
        };
        Ok(true)
    }
}