start with 64 bit
Some checks failed
Rust / build (push) Has been cancelled

This commit is contained in:
nora 2025-03-26 21:10:48 +01:00
parent 0144740228
commit e9a689aa1a
6 changed files with 449 additions and 193 deletions

View file

@ -32,18 +32,30 @@ pub struct Header {
}
#[derive(Debug)]
pub struct Phdr32 {
pub struct Phdr {
pub p_type: u32,
pub p_offset: u32,
pub p_vaddr: u32,
pub p_paddr: u32,
pub p_filesz: u32,
pub p_memsz: u32,
pub p_offset: Offset,
pub p_vaddr: Addr,
pub p_paddr: Addr,
pub p_filesz: u64,
pub p_memsz: u64,
pub p_flags: u32,
pub p_align: u32,
pub p_align: u64,
}
impl Elf<'_> {
fn class(&self) -> Result<ElfClass> {
let (_, class) = self.content.split_u32()?;
let (class, _) = class.split_bytes(1)?;
Ok(match class[0] {
// ELFCLASS32
1 => ElfClass::Elf32,
// ELFCLASS64
2 => ElfClass::Elf64,
_ => bail!("not a ELF32 or ELF64 file (EI_CLASS={})", class[0]),
})
}
pub fn header(&self) -> Result<Header> {
let (ident, rest) = self.content.split_bytes(16)?;
if ident[..4] != *b"\x7fELF" {
@ -140,8 +152,9 @@ impl Elf<'_> {
})
}
pub fn segments_32(&self) -> Result<Vec<Phdr32>> {
pub fn segments(&self) -> Result<Vec<Phdr>> {
let header = self.header()?;
let class = self.class()?;
let (_, phdrs) = self.content.split_bytes(header.e_phoff.0 as usize)?;
let (mut phdrs, _) = phdrs.split_bytes((header.e_phentsize * header.e_phnum) as usize)?;
@ -152,25 +165,51 @@ impl Elf<'_> {
let phdr;
(phdr, phdrs) = phdrs.split_bytes(header.e_phentsize as usize)?;
let (p_type, phdr) = phdr.split_u32()?;
let (p_offset, phdr) = phdr.split_u32()?;
let (p_vaddr, phdr) = phdr.split_u32()?;
let (p_paddr, phdr) = phdr.split_u32()?;
let (p_filesz, phdr) = phdr.split_u32()?;
let (p_memsz, phdr) = phdr.split_u32()?;
let (p_flags, phdr) = phdr.split_u32()?;
let (p_align, _) = phdr.split_u32()?;
let phdr = match class {
ElfClass::Elf32 => {
let (p_type, phdr) = phdr.split_u32()?;
let (p_offset, phdr) = phdr.split_u32()?;
let (p_vaddr, phdr) = phdr.split_u32()?;
let (p_paddr, phdr) = phdr.split_u32()?;
let (p_filesz, phdr) = phdr.split_u32()?;
let (p_memsz, phdr) = phdr.split_u32()?;
let (p_flags, phdr) = phdr.split_u32()?;
let (p_align, _) = phdr.split_u32()?;
parsed_phdrs.push(Phdr32 {
p_type,
p_offset,
p_vaddr,
p_paddr,
p_filesz,
p_memsz,
p_flags,
p_align,
});
Phdr {
p_type,
p_offset: Offset(p_offset as u64),
p_vaddr: Addr(p_vaddr as u64),
p_paddr: Addr(p_paddr as u64),
p_filesz: p_filesz as u64,
p_memsz: p_memsz as u64,
p_flags,
p_align: p_align as u64,
}
}
ElfClass::Elf64 => {
let (p_type, phdr) = phdr.split_u32()?;
let (p_flags, phdr) = phdr.split_u32()?;
let (p_offset, phdr) = phdr.split_u64()?;
let (p_vaddr, phdr) = phdr.split_u64()?;
let (p_paddr, phdr) = phdr.split_u64()?;
let (p_filesz, phdr) = phdr.split_u64()?;
let (p_memsz, phdr) = phdr.split_u64()?;
let (p_align, _) = phdr.split_u64()?;
Phdr {
p_type,
p_offset: Offset(p_offset),
p_vaddr: Addr(p_vaddr),
p_paddr: Addr(p_paddr),
p_filesz,
p_memsz,
p_flags,
p_align,
}
}
};
parsed_phdrs.push(phdr);
}
Ok(parsed_phdrs)

View file

@ -20,44 +20,50 @@ impl Memory {
Ok(())
}
}
pub fn slice<XLEN: XLen>(&self, addr: XLEN, len: u32) -> Result<&[u8], Status> {
pub fn slice<XLEN: XLen>(&self, addr: XLEN, len: XLEN) -> Result<&[u8], Status> {
self.mem
.get((addr.as_usize())..)
.ok_or(Status::InvalidMemoryAccess(addr.as_usize()))?
.get(..(len as usize))
.get(..(len.as_usize()))
.ok_or(Status::InvalidMemoryAccess(addr.as_usize()))
}
pub fn slice_mut<XLEN: XLen>(&mut self, addr: XLEN, len: u32) -> Result<&mut [u8], Status> {
pub fn slice_mut<XLEN: XLen>(&mut self, addr: XLEN, len: XLEN) -> Result<&mut [u8], Status> {
self.mem
.get_mut((addr.as_usize())..)
.ok_or(Status::InvalidMemoryAccess(addr.as_usize()))?
.get_mut(..(len as usize))
.get_mut(..(len.as_usize()))
.ok_or(Status::InvalidMemoryAccess(addr.as_usize()))
}
pub fn load_u8<XLEN: XLen>(&self, addr: XLEN) -> Result<u8, Status> {
Ok(u8::from_le_bytes(self.slice(addr, 1)?.try_into().unwrap()))
Ok(u8::from_le_bytes(
self.slice(addr, XLEN::from_32_z(1))?.try_into().unwrap(),
))
}
pub fn load_u16<XLEN: XLen>(&self, addr: XLEN) -> Result<u16, Status> {
Ok(u16::from_le_bytes(self.slice(addr, 2)?.try_into().unwrap()))
Ok(u16::from_le_bytes(
self.slice(addr, XLEN::from_32_z(2))?.try_into().unwrap(),
))
}
pub fn load_u32<XLEN: XLen>(&self, addr: XLEN) -> Result<u32, Status> {
Ok(u32::from_le_bytes(self.slice(addr, 4)?.try_into().unwrap()))
Ok(u32::from_le_bytes(
self.slice(addr, XLEN::from_32_z(4))?.try_into().unwrap(),
))
}
pub fn store_u8<XLEN: XLen>(&mut self, addr: XLEN, value: u8) -> Result<(), Status> {
self.slice_mut(addr, 1)?
self.slice_mut(addr, XLEN::from_32_z(1))?
.copy_from_slice(&value.to_le_bytes());
Ok(())
}
pub fn store_u16<XLEN: XLen>(&mut self, addr: XLEN, value: u16) -> Result<(), Status> {
self.check_align(addr, 2)?;
self.slice_mut(addr, 2)?
self.slice_mut(addr, XLEN::from_32_z(2))?
.copy_from_slice(&value.to_le_bytes());
Ok(())
}
pub fn store_u32<XLEN: XLen>(&mut self, addr: XLEN, value: u32) -> Result<(), Status> {
self.check_align(addr, 4)?;
self.slice_mut(addr, 4)?
self.slice_mut(addr, XLEN::from_32_z(4))?
.copy_from_slice(&value.to_le_bytes());
Ok(())
}
@ -139,6 +145,28 @@ fn hash_color(value: usize) -> impl Display {
}
impl<XLEN: XLen> Emulator<XLEN> {
pub fn new(
mem: Memory,
start: XLEN,
break_addr: XLEN,
debug: bool,
ecall_handler: Box<dyn FnMut(&mut Memory, &mut [XLEN; 32]) -> Result<(), Status>>,
) -> Self {
Self {
mem,
xreg: [XLEN::ZERO; 32],
xreg0_value: XLEN::ZERO,
pc: start,
reservation_set: None,
is_breaking: false,
break_pc: break_addr,
debug,
ecall_handler,
}
}
pub fn start_linux(&mut self) -> Status {
self.setup_linux_stack().unwrap();
@ -210,7 +238,7 @@ impl<XLEN: XLen> Emulator<XLEN> {
print!("0x{:x} ", self.pc.as_usize());
}
let (inst, was_compressed) = Inst::decode(code)?;
let (inst, was_compressed) = Inst::decode(code, XLEN::XLEN)?;
if self.debug {
println!(
@ -649,11 +677,21 @@ impl<XLEN: XLen> Emulator<XLEN> {
}
pub trait XLen: Copy + PartialEq + Eq {
type Signed;
type NextUnsigned;
type NextSigned;
const XLEN: rvdc::Xlen;
const ZERO: Self;
const SIGNED_MIN: Self;
const MAX: Self;
fn from_bool(v: bool) -> Self;
fn switch<R>(self, on_32: impl FnOnce(u32) -> R, on_64: impl FnOnce(u64) -> R) -> R;
fn from_bool(v: bool) -> Self {
Self::from_32_z(v as u32)
}
fn from_8_z(v: u8) -> Self {
Self::from_32_z(v as u32)
}
@ -704,14 +742,107 @@ pub trait XLen: Copy + PartialEq + Eq {
fn unsigned_rem(self, other: Self) -> Self;
}
impl XLen for u32 {
const ZERO: Self = 0;
const SIGNED_MIN: Self = i32::MIN as u32;
const MAX: Self = u32::MAX;
macro_rules! xlen_impl {
() => {
const ZERO: Self = 0;
const SIGNED_MIN: Self = Self::Signed::MIN as Self;
const MAX: Self = Self::MAX;
fn from_bool(v: bool) -> Self {
v as u32
fn as_usize(self) -> usize {
self as usize
}
fn truncate32(self) -> u32 {
self as u32
}
fn add(self, other: Self) -> Self {
self.wrapping_add(other)
}
fn sub(self, other: Self) -> Self {
self.wrapping_sub(other)
}
fn and(self, other: Self) -> Self {
self & other
}
fn or(self, other: Self) -> Self {
self | other
}
fn xor(self, other: Self) -> Self {
self ^ other
}
fn signed_lt(self, other: Self) -> bool {
(self as Self::Signed) < (other as Self::Signed)
}
fn unsigned_lt(self, other: Self) -> bool {
self < other
}
fn signed_ge(self, other: Self) -> bool {
(self as Self::Signed) >= (other as Self::Signed)
}
fn unsigned_ge(self, other: Self) -> bool {
self >= other
}
fn shl(self, other: u32) -> Self {
self.wrapping_shl(other)
}
fn unsigned_shr(self, other: u32) -> Self {
self.wrapping_shr(other)
}
fn signed_shr(self, other: u32) -> Self {
((self as Self::Signed).wrapping_shr(other)) as Self
}
fn signed_min(self, other: Self) -> Self {
(self as Self::Signed).min(other as Self::Signed) as Self
}
fn unsigned_min(self, other: Self) -> Self {
self.min(other)
}
fn signed_max(self, other: Self) -> Self {
(self as Self::Signed).max(other as Self::Signed) as Self
}
fn unsigned_max(self, other: Self) -> Self {
self.max(other)
}
fn mul_lower(self, other: Self) -> Self {
(self as Self::Signed).wrapping_mul(other as Self::Signed) as Self
}
fn signed_mul_upper(self, other: Self) -> Self {
let mul_result = (self as Self::Signed as Self::NextSigned)
.wrapping_mul(other as Self::Signed as Self::NextSigned);
let shifted = (mul_result as Self::NextUnsigned) >> Self::BITS;
shifted as Self
}
fn unsigned_mul_upper(self, other: Self) -> Self {
let shifted = ((self as Self::NextUnsigned).wrapping_mul(other as Self::NextUnsigned))
>> Self::BITS;
shifted as Self
}
fn signed_div(self, other: Self) -> Self {
((self as Self::Signed) / (other as Self::Signed)) as Self
}
fn unsigned_div(self, other: Self) -> Self {
self / other
}
fn signed_rem(self, other: Self) -> Self {
((self as Self::Signed) % (other as Self::Signed)) as Self
}
fn unsigned_rem(self, other: Self) -> Self {
self % other
}
};
}
impl XLen for u32 {
type Signed = i32;
type NextUnsigned = u64;
type NextSigned = i64;
const XLEN: rvdc::Xlen = rvdc::Xlen::Rv32;
fn switch<R>(self, on_32: impl FnOnce(u32) -> R, _: impl FnOnce(u64) -> R) -> R {
on_32(self)
}
fn from_32_s(v: u32) -> Self {
v
}
@ -721,84 +852,30 @@ impl XLen for u32 {
fn from_imm(v: Imm) -> Self {
v.as_u32()
}
fn as_usize(self) -> usize {
self as usize
}
fn truncate32(self) -> u32 {
self
}
fn add(self, other: Self) -> Self {
self.wrapping_add(other)
}
fn sub(self, other: Self) -> Self {
self.wrapping_sub(other)
}
fn and(self, other: Self) -> Self {
self & other
}
fn or(self, other: Self) -> Self {
self | other
}
fn xor(self, other: Self) -> Self {
self ^ other
}
fn signed_lt(self, other: Self) -> bool {
(self as i32) < (other as i32)
}
fn unsigned_lt(self, other: Self) -> bool {
self < other
}
fn signed_ge(self, other: Self) -> bool {
(self as i32) >= (other as i32)
}
fn unsigned_ge(self, other: Self) -> bool {
self >= other
}
fn shl(self, other: u32) -> Self {
self.wrapping_shl(other)
}
fn unsigned_shr(self, other: u32) -> Self {
self.wrapping_shr(other)
}
fn signed_shr(self, other: u32) -> Self {
((self as i32).wrapping_shr(other)) as u32
}
fn signed_min(self, other: Self) -> Self {
(self as i32).min(other as i32) as u32
}
fn unsigned_min(self, other: Self) -> Self {
self.min(other)
}
fn signed_max(self, other: Self) -> Self {
(self as i32).max(other as i32) as u32
}
fn unsigned_max(self, other: Self) -> Self {
self.max(other)
}
fn mul_lower(self, other: Self) -> Self {
(self as i32).wrapping_mul(other as i32) as u32
}
fn signed_mul_upper(self, other: Self) -> Self {
let mul_result = (self as i32 as i64).wrapping_mul(other as i32 as i64);
let shifted = (mul_result as u64) >> 32;
shifted as u32
}
fn unsigned_mul_upper(self, other: Self) -> Self {
let shifted = ((self as u64).wrapping_mul(other as u64)) >> 32;
shifted as u32
}
fn signed_div(self, other: Self) -> Self {
((self as i32) / (other as i32)) as u32
}
fn unsigned_div(self, other: Self) -> Self {
self / other
}
fn signed_rem(self, other: Self) -> Self {
((self as i32) % (other as i32)) as u32
}
fn unsigned_rem(self, other: Self) -> Self {
self % other
}
xlen_impl!();
}
impl XLen for u64 {
type Signed = i64;
type NextUnsigned = u128;
type NextSigned = i128;
const XLEN: rvdc::Xlen = rvdc::Xlen::Rv64;
fn switch<R>(self, _: impl FnOnce(u32) -> R, on_64: impl FnOnce(u64) -> R) -> R {
on_64(self)
}
fn from_32_s(v: u32) -> Self {
v as i32 as i64 as u64
}
fn from_32_z(v: u32) -> Self {
v as u64
}
fn from_imm(v: Imm) -> Self {
v.as_u64()
}
xlen_impl!();
}

View file

@ -9,13 +9,14 @@ const MEMORY_SIZE: usize = 2 << 21;
pub fn execute_linux_elf(
elf: &[u8],
debug: bool,
break_addr: u32,
ecall_handler: Box<dyn FnMut(&mut emu::Memory, &mut [u32; 32]) -> Result<(), emu::Status>>,
break_addr: u64,
ecall_handler32: Box<dyn FnMut(&mut emu::Memory, &mut [u32; 32]) -> Result<(), emu::Status>>,
ecall_handler64: Box<dyn FnMut(&mut emu::Memory, &mut [u64; 32]) -> Result<(), emu::Status>>,
) -> eyre::Result<emu::Status> {
let elf = elf::Elf { content: elf };
let header = elf.header()?;
let segments = elf.segments_32()?;
let segments = elf.segments()?;
let mut mem = emu::Memory {
mem: vec![0; MEMORY_SIZE],
@ -30,13 +31,13 @@ pub fn execute_linux_elf(
if phdr.p_filesz > 0 {
let contents = &elf
.content
.get((phdr.p_offset as usize)..)
.get((phdr.p_offset.0 as usize)..)
.ok_or_eyre("invalid offset")?
.get(..(phdr.p_filesz as usize))
.ok_or_eyre("invalid offset")?;
mem.mem
.get_mut((phdr.p_vaddr as usize)..)
.get_mut((phdr.p_vaddr.0 as usize)..)
.ok_or_eyre("invalid offset")?
.get_mut(..(phdr.p_filesz as usize))
.ok_or_eyre("invalid offset")?
@ -61,19 +62,16 @@ pub fn execute_linux_elf(
let start = header.e_entry;
let mut emu = emu::Emulator {
mem,
xreg: [0; 32],
xreg0_value: 0,
pc: start.0 as u32,
reservation_set: None,
is_breaking: false,
break_pc: break_addr,
debug,
ecall_handler,
};
Ok(emu.start_linux())
match header.class {
elf::ElfClass::Elf32 => {
let mut emu =
emu::Emulator::<u32>::new(mem, start.0 as u32, break_addr as u32, debug, ecall_handler32);
Ok(emu.start_linux())
}
elf::ElfClass::Elf64 => {
let mut emu =
emu::Emulator::<u64>::new(mem, start.0, break_addr, debug, ecall_handler64);
Ok(emu.start_linux())
}
}
}

View file

@ -1,7 +1,7 @@
use std::io::Write;
use std::{cell::RefCell, io::Write, sync::Arc};
use eyre::eyre;
use rustv32i::emu::{self, Memory};
use rustv32i::emu::{self, Memory, XLen};
use rvdc::Reg;
fn main() -> eyre::Result<()> {
@ -12,22 +12,26 @@ fn main() -> eyre::Result<()> {
.nth(1)
.map(|addr| {
if let Some(addr) = addr.strip_prefix("0x") {
u32::from_str_radix(addr, 16)
u64::from_str_radix(addr, 16)
} else {
u32::from_str_radix(&addr, 10)
u64::from_str_radix(&addr, 10)
}
})
.unwrap_or(Ok(0))?;
let debug = std::env::args().any(|arg| arg == "--debug");
let mut syscall_state = SyscallState { set_child_tid: 0 };
let syscall_state = Arc::new(RefCell::new(SyscallState { set_child_tid: 0 }));
let syscall_state64 = syscall_state.clone();
let status = rustv32i::execute_linux_elf(
&content,
debug,
break_addr,
Box::new(move |mem, xreg| ecall_handler(mem, xreg, &mut syscall_state)),
Box::new(move |mem, xreg| ecall_handler::<u32>(mem, xreg, &mut syscall_state.borrow_mut())),
Box::new(move |mem, xreg| {
ecall_handler::<u64>(mem, xreg, &mut syscall_state64.borrow_mut())
}),
)?;
std::io::stdout().flush()?;
@ -44,12 +48,12 @@ fn main() -> eyre::Result<()> {
}
struct SyscallState {
set_child_tid: u32,
set_child_tid: u64,
}
fn ecall_handler(
fn ecall_handler<XLEN: Into<u64> + From<u32> + XLen>(
mem: &mut Memory,
xreg: &mut [u32; 32],
xreg: &mut [XLEN; 32],
syscall_state: &mut SyscallState,
) -> Result<(), emu::Status> {
let nr = xreg[Reg::A7.0 as usize];
@ -60,22 +64,22 @@ fn ecall_handler(
// https://jborza.com/post/2021-05-11-riscv-linux-syscalls/
// https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/unistd.h
match nr {
match nr.into() {
// ioctl
29 => {
let fd = arg0;
let request = arg1;
match request {
match request.into() {
// TIOCGWINSZ
0x5413 => {
let wsz_ptr = xreg[Reg::A2.0 as usize];
let wsz_ptr = xreg[Reg::A2.0 as usize].into();
let mut wsz: libc::winsize = unsafe { std::mem::zeroed() };
let r = unsafe { libc::ioctl(fd as i32, libc::TIOCGWINSZ, &mut wsz) };
let r = unsafe { libc::ioctl(fd.into() as i32, libc::TIOCGWINSZ, &mut wsz) };
xreg[Reg::A0.0 as usize] = r as u32;
xreg[Reg::A0.0 as usize] = (r as u32).into();
if r >= 0 {
mem.store_u16(wsz_ptr, wsz.ws_row)?;
mem.store_u16(wsz_ptr + 2, wsz.ws_col)?;
@ -83,16 +87,16 @@ fn ecall_handler(
mem.store_u16(wsz_ptr + 6, wsz.ws_ypixel)?;
}
}
_ => todo!("unknown ioctl: {request}"),
_ => todo!("unknown ioctl: {}", request.into()),
}
}
// read
63 => {
let fd = arg0;
let fd = arg0.into();
let ptr = xreg[Reg::A1.0 as usize];
let len = xreg[Reg::A2.0 as usize];
let buf = mem.slice_mut(ptr, len)?;
let buf = mem.slice_mut::<XLEN>(ptr, len)?;
let len = unsafe { libc::read(fd as i32, buf.as_mut_ptr().cast(), buf.len()) };
let ret = if len < 0 {
@ -101,11 +105,11 @@ fn ecall_handler(
len as u32
};
xreg[Reg::A0.0 as usize] = ret;
xreg[Reg::A0.0 as usize] = ret.into();
}
// write
64 => {
let fd = arg0;
let fd = arg0.into();
let ptr = xreg[Reg::A1.0 as usize];
let len = xreg[Reg::A2.0 as usize];
@ -118,13 +122,13 @@ fn ecall_handler(
len as u32
};
xreg[Reg::A0.0 as usize] = ret;
xreg[Reg::A0.0 as usize] = ret.into();
}
// https://man7.org/linux/man-pages/man3/writev.3p.html
66 => {
let fd = arg0;
let iovec = arg1;
let iovcnt = arg2;
let fd = arg0.into();
let iovec = arg1.into();
let iovcnt = arg2.into();
let mut written = 0;
@ -142,27 +146,27 @@ fn ecall_handler(
};
if (ret as i32) < 0 {
xreg[Reg::A0.0 as usize] = ret;
xreg[Reg::A0.0 as usize] = ret.into();
return Ok(());
} else {
written += ret;
}
}
xreg[Reg::A0.0 as usize] = written;
xreg[Reg::A0.0 as usize] = written.into();
}
// exit | exit_group
93 | 94 => {
return Err(emu::Status::Exit {
code: xreg[Reg::A0.0 as usize] as i32,
code: xreg[Reg::A0.0 as usize].into() as i32,
});
}
// <https://man7.org/linux/man-pages/man2/set_tid_address.2.html>
96 => {
let tidptr = arg0;
syscall_state.set_child_tid = tidptr;
syscall_state.set_child_tid = tidptr.into();
xreg[Reg::A0.0 as usize] = 1; // thread ID
xreg[Reg::A0.0 as usize] = 1.into(); // thread ID
}
// ppoll - called for some stdin/stdout/stderr check.
414 => {
@ -170,10 +174,10 @@ fn ecall_handler(
// and opens /dev/null for them if they are.
// They're always valid here, so just get out.
xreg[Reg::A0.0 as usize] = 0;
xreg[Reg::A0.0 as usize] = 0.into();
}
_ => {
todo!("unkonwn syscall: {nr}");
todo!("unkonwn syscall: {}", nr.into());
}
}