diff --git a/Cargo.lock b/Cargo.lock index 6e65352..cfa4624 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,28 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "bitflags" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "errno" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +dependencies = [ + "libc", + "windows-sys", +] + [[package]] name = "eyre" version = "0.6.12" @@ -12,6 +34,24 @@ dependencies = [ "once_cell", ] +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "getrandom" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +dependencies = [ + "cfg-if", + "libc", + "wasi", + "windows-targets", +] + [[package]] name = "indenter" version = "0.3.3" @@ -24,16 +64,141 @@ version = "0.2.170" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" +[[package]] +name = "linux-raw-sys" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9c683daf087dc577b7506e9695b3d556a9f3849903fa28186283afd6809e9" + [[package]] name = "once_cell" version = "1.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" +[[package]] +name = "rustix" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dade4812df5c384711475be5fcd8c162555352945401aed22a35bffeab61f657" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + [[package]] name = "rustv32i" version = "0.1.0" dependencies = [ "eyre", "libc", + "tempfile", +] + +[[package]] +name = "tempfile" +version = "3.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c317e0a526ee6120d8dabad239c8dadca62b24b6f168914bbbc8e2fb1f0e567" +dependencies = [ + "cfg-if", + "fastrand", + "getrandom", + "once_cell", + "rustix", + "windows-sys", +] + +[[package]] +name = "wasi" +version = "0.13.3+wasi-0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "wit-bindgen-rt" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +dependencies = [ + "bitflags", ] diff --git a/Cargo.toml b/Cargo.toml index 858ea20..27f8bc6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,3 +9,6 @@ libc = "0.2.170" [profile.release] debug = 1 + +[dev-dependencies] +tempfile = "3.18.0" diff --git a/src/elf.rs b/src/elf.rs index 5d9eb3b..1e5d46a 100644 --- a/src/elf.rs +++ b/src/elf.rs @@ -1,7 +1,7 @@ use eyre::{Result, bail}; -pub struct Elf { - pub content: Vec, +pub struct Elf<'a> { + pub content: &'a [u8], } #[derive(Debug)] @@ -30,7 +30,7 @@ pub struct Phdr { pub p_align: u32, } -impl Elf { +impl<'a> Elf<'a> { pub fn header(&self) -> Result
{ let (ident, rest) = self.content.split_bytes(16)?; if ident[..4] != *b"\x7fELF" { diff --git a/src/emu.rs b/src/emu.rs index 64d6abe..bd703b5 100644 --- a/src/emu.rs +++ b/src/emu.rs @@ -10,9 +10,9 @@ pub struct Memory { } impl Memory { - fn check_align(&self, addr: u32, align: u32) -> Result<(), Error> { + fn check_align(&self, addr: u32, align: u32) -> Result<(), Status> { if addr % 2 != 0 { - Err(Error::UnaligneMemoryAccess { + Err(Status::UnaligneMemoryAccess { addr, required_align: align, }) @@ -20,44 +20,44 @@ impl Memory { Ok(()) } } - pub fn slice(&self, addr: u32, len: u32) -> Result<&[u8], Error> { + pub fn slice(&self, addr: u32, len: u32) -> Result<&[u8], Status> { Ok(self .mem .get((addr as usize)..) - .ok_or(Error::InvalidMemoryAccess(addr))? + .ok_or(Status::InvalidMemoryAccess(addr))? .get(..(len as usize)) - .ok_or(Error::InvalidMemoryAccess(addr))?) + .ok_or(Status::InvalidMemoryAccess(addr))?) } - pub fn slice_mut(&mut self, addr: u32, len: u32) -> Result<&mut [u8], Error> { + pub fn slice_mut(&mut self, addr: u32, len: u32) -> Result<&mut [u8], Status> { Ok(self .mem .get_mut((addr as usize)..) - .ok_or(Error::InvalidMemoryAccess(addr))? + .ok_or(Status::InvalidMemoryAccess(addr))? .get_mut(..(len as usize)) - .ok_or(Error::InvalidMemoryAccess(addr))?) + .ok_or(Status::InvalidMemoryAccess(addr))?) } - pub fn load_u8(&self, addr: u32) -> Result { + pub fn load_u8(&self, addr: u32) -> Result { Ok(u8::from_le_bytes(self.slice(addr, 1)?.try_into().unwrap())) } - pub fn load_u16(&self, addr: u32) -> Result { + pub fn load_u16(&self, addr: u32) -> Result { Ok(u16::from_le_bytes(self.slice(addr, 2)?.try_into().unwrap())) } - pub fn load_u32(&self, addr: u32) -> Result { + pub fn load_u32(&self, addr: u32) -> Result { Ok(u32::from_le_bytes(self.slice(addr, 4)?.try_into().unwrap())) } - pub fn store_u8(&mut self, addr: u32, value: u8) -> Result<(), Error> { + pub fn store_u8(&mut self, addr: u32, value: u8) -> Result<(), Status> { Ok(self .slice_mut(addr, 1)? .copy_from_slice(&value.to_le_bytes())) } - pub fn store_u16(&mut self, addr: u32, value: u16) -> Result<(), Error> { + pub fn store_u16(&mut self, addr: u32, value: u16) -> Result<(), Status> { self.check_align(addr, 2)?; Ok(self .slice_mut(addr, 2)? .copy_from_slice(&value.to_le_bytes())) } - pub fn store_u32(&mut self, addr: u32, value: u32) -> Result<(), Error> { + pub fn store_u32(&mut self, addr: u32, value: u32) -> Result<(), Status> { self.check_align(addr, 4)?; Ok(self .slice_mut(addr, 4)? @@ -66,7 +66,7 @@ impl Memory { } #[derive(Debug)] -pub enum Error { +pub enum Status { Trap(&'static str), IllegalInstruction(InstCode, &'static str), InvalidMemoryAccess(u32), @@ -87,8 +87,7 @@ pub struct Emulator { pub reservation_set: Option, pub debug: bool, - - pub ecall_handler: Box Result<(), Error>>, + pub ecall_handler: Box Result<(), Status>>, } impl Index for Emulator { @@ -141,14 +140,14 @@ impl Display for Reg { } impl Emulator { - pub fn start_linux(&mut self) -> Error { + pub fn start_linux(&mut self) -> Status { // set top of stack. just some yolo address. with no values there. who needs abi? self[Reg::SP] = 4096; self.execute() } - fn execute(&mut self) -> Error { + fn execute(&mut self) -> Status { loop { if let Err(err) = self.step() { return err; @@ -156,15 +155,15 @@ impl Emulator { } } - fn set_pc(&mut self, pc: u32) -> Result<(), Error> { + fn set_pc(&mut self, pc: u32) -> Result<(), Status> { if pc % 4 != 0 { - return Err(Error::UnalignedPc(pc)); + return Err(Status::UnalignedPc(pc)); } self.pc = pc; Ok(()) } - fn step(&mut self) -> Result<(), Error> { + fn step(&mut self) -> Result<(), Status> { let code = self.mem.load_u32(self.pc)?; let inst = Inst::decode(code)?; @@ -314,7 +313,7 @@ impl Emulator { Inst::Ecall => { (self.ecall_handler)(&mut self.mem, &mut self.xreg)?; } - Inst::Ebreak => return Err(Error::Ebreak), + Inst::Ebreak => return Err(Status::Ebreak), Inst::Mul { dest, src1, src2 } => { self[dest] = ((self[src1] as i32).wrapping_mul(self[src2] as i32)) as u32; } diff --git a/src/inst.rs b/src/inst.rs index 92ccf6f..6920136 100644 --- a/src/inst.rs +++ b/src/inst.rs @@ -1,4 +1,4 @@ -use crate::emu::{Error, Reg}; +use crate::emu::{Status, Reg}; use std::fmt::{Debug, Display}; use std::ops::RangeInclusive; @@ -413,7 +413,7 @@ impl Debug for InstCode { } impl Inst { - pub fn decode(code: u32) -> Result { + pub fn decode(code: u32) -> Result { let code = InstCode(code); let inst = match code.opcode() { // LUI @@ -438,7 +438,7 @@ impl Inst { base: code.rs1(), dest: code.rd(), }, - _ => return Err(Error::IllegalInstruction(code, "funct3")), + _ => return Err(Status::IllegalInstruction(code, "funct3")), }, // BRANCH 0b1100011 => match code.funct3() { @@ -472,7 +472,7 @@ impl Inst { src1: code.rs1(), src2: code.rs2(), }, - _ => return Err(Error::IllegalInstruction(code, "funct3")), + _ => return Err(Status::IllegalInstruction(code, "funct3")), }, // LOAD 0b0000011 => match code.funct3() { @@ -501,7 +501,7 @@ impl Inst { dest: code.rd(), base: code.rs1(), }, - _ => return Err(Error::IllegalInstruction(code, "funct3")), + _ => return Err(Status::IllegalInstruction(code, "funct3")), }, // STORE 0b0100011 => match code.funct3() { @@ -520,7 +520,7 @@ impl Inst { src: code.rs2(), base: code.rs1(), }, - _ => return Err(Error::IllegalInstruction(code, "funct3")), + _ => return Err(Status::IllegalInstruction(code, "funct3")), }, // OP-IMM 0b0010011 => match code.funct3() { @@ -570,9 +570,9 @@ impl Inst { dest: code.rd(), src1: code.rs1(), }, - _ => return Err(Error::IllegalInstruction(code, "funct7")), + _ => return Err(Status::IllegalInstruction(code, "funct7")), }, - _ => return Err(Error::IllegalInstruction(code, "funct3")), + _ => return Err(Status::IllegalInstruction(code, "funct3")), }, // OP 0b0110011 => { @@ -597,7 +597,7 @@ impl Inst { (0b101, 0b0000001) => Inst::Divu { dest, src1, src2 }, (0b110, 0b0000001) => Inst::Rem { dest, src1, src2 }, (0b111, 0b0000001) => Inst::Remu { dest, src1, src2 }, - _ => return Err(Error::IllegalInstruction(code, "funct3/funct7")), + _ => return Err(Status::IllegalInstruction(code, "funct3/funct7")), } } // MISC-MEM @@ -626,34 +626,34 @@ impl Inst { src: code.rs1(), }, }, - _ => return Err(Error::IllegalInstruction(code, "funct3")), + _ => return Err(Status::IllegalInstruction(code, "funct3")), } } // SYSTEM 0b1110011 => { if code.0 == 0b11000000000000000001000001110011 { - return Err(Error::Trap("unimp instruction")); + return Err(Status::Trap("unimp instruction")); } if code.rd().0 != 0 { - return Err(Error::IllegalInstruction(code, "rd")); + return Err(Status::IllegalInstruction(code, "rd")); } if code.funct3() != 0 { - return Err(Error::IllegalInstruction(code, "funct3")); + return Err(Status::IllegalInstruction(code, "funct3")); } if code.rs1().0 != 0 { - return Err(Error::IllegalInstruction(code, "rs1")); + return Err(Status::IllegalInstruction(code, "rs1")); } match code.imm_i() { 0b000000000000 => Inst::Ecall, 0b000000000001 => Inst::Ebreak, - _ => return Err(Error::IllegalInstruction(code, "imm")), + _ => return Err(Status::IllegalInstruction(code, "imm")), } } // AMO 00101111 => { // width must be W if code.funct3() != 0b010 { - return Err(Error::IllegalInstruction(code, "funct3")); + return Err(Status::IllegalInstruction(code, "funct3")); } let kind = code.extract(27..=31); @@ -666,7 +666,7 @@ impl Inst { // LR 0b00010 => { if code.rs2().0 != 0 { - return Err(Error::IllegalInstruction(code, "rs2")); + return Err(Status::IllegalInstruction(code, "rs2")); } Inst::LrW { @@ -693,7 +693,7 @@ impl Inst { 0b10100 => AmoOp::Max, 0b11000 => AmoOp::Minu, 0b11100 => AmoOp::Maxu, - _ => return Err(Error::IllegalInstruction(code, "funct7")), + _ => return Err(Status::IllegalInstruction(code, "funct7")), }; Inst::AmoW { order, @@ -705,7 +705,7 @@ impl Inst { } } } - _ => return Err(Error::IllegalInstruction(code, "opcode")), + _ => return Err(Status::IllegalInstruction(code, "opcode")), }; Ok(inst) } diff --git a/src/lib.rs b/src/lib.rs index 07e05b5..df7cfea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,76 @@ +use eyre::{OptionExt, bail}; + pub mod elf; pub mod emu; pub mod inst; + +// 2 MiB +const MEMORY_SIZE: usize = 2 << 21; + +pub fn execute_linux_elf( + elf: &[u8], + debug: bool, + ecall_handler: Box Result<(), emu::Status>>, +) -> eyre::Result { + let elf = elf::Elf { content: elf }; + let header = elf.header()?; + + let segments = elf.segments()?; + + let mut mem = emu::Memory { + mem: vec![0; MEMORY_SIZE], + }; + + for phdr in segments { + match phdr.p_type { + // PT_NULL + 0 => {} + // PT_LOAD + 1 => { + if phdr.p_filesz > 0 { + let contents = &elf + .content + .get((phdr.p_offset 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)..) + .ok_or_eyre("invalid offset")? + .get_mut(..(phdr.p_filesz as usize)) + .ok_or_eyre("invalid offset")? + .copy_from_slice(contents); + } + } + // PT_DYNAMIC + 2 => {} + // PT_PHDR + 6 => {} + // PT_GNU_EH_FRAME + 1685382480 => {} + // PT_GNU_STACK + 1685382481 => {} + // PT_GNU_RELRO + 1685382482 => {} + // PT_RISCV_ATTRIBUTES + 0x70000003 => {} + _ => bail!("unknown program header type: {}", phdr.p_type), + } + } + + let start = header.e_entry; + + let mut emu = emu::Emulator { + mem, + xreg: [0; 32], + xreg0_value: 0, + pc: start, + reservation_set: None, + + debug, + ecall_handler, + }; + + Ok(emu.start_linux()) +} diff --git a/src/main.rs b/src/main.rs index 318d2cb..0151816 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,88 +1,27 @@ -use eyre::{OptionExt, bail, eyre}; -use rustv32i::{ - elf, - emu::{self, Memory, Reg}, -}; - -// 2 MiB -const MEMORY_SIZE: usize = 2 << 21; +use eyre::eyre; +use rustv32i::emu::{self, Memory, Reg}; fn main() -> eyre::Result<()> { let content = std::fs::read(std::env::args().nth(1).unwrap()).unwrap(); - let elf = elf::Elf { content }; - let header = elf.header()?; + let status = rustv32i::execute_linux_elf( + &content, + std::env::args().any(|arg| arg == "--debug"), + Box::new(ecall_handler), + )?; - let segments = elf.segments()?; - - let mut mem = emu::Memory { - mem: vec![0; MEMORY_SIZE], - }; - - for phdr in segments { - match phdr.p_type { - // PT_NULL - 0 => {} - // PT_LOAD - 1 => { - if phdr.p_filesz > 0 { - let contents = &elf - .content - .get((phdr.p_offset 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)..) - .ok_or_eyre("invalid offset")? - .get_mut(..(phdr.p_filesz as usize)) - .ok_or_eyre("invalid offset")? - .copy_from_slice(contents); - } - } - // PT_DYNAMIC - 2 => {} - // PT_PHDR - 6 => {} - // PT_GNU_EH_FRAME - 1685382480 => {} - // PT_GNU_STACK - 1685382481 => {} - // PT_GNU_RELRO - 1685382482 => {} - // PT_RISCV_ATTRIBUTES - 0x70000003 => {} - _ => bail!("unknown program header type: {}", phdr.p_type), - } - } - - let start = header.e_entry; - - let mut emu = emu::Emulator { - mem, - xreg: [0; 32], - xreg0_value: 0, - pc: start, - reservation_set: None, - - debug: std::env::args().any(|arg| arg == "--debug"), - - ecall_handler: Box::new(ecall_handler), - }; - - match emu.start_linux() { - emu::Error::Exit { code } => { + match status { + emu::Status::Exit { code } => { eprintln!("exited with code {code}"); } - emu::Error::Trap(cause) => eprintln!("program trapped: {cause}"), + emu::Status::Trap(cause) => eprintln!("program trapped: {cause}"), e => return Err(eyre!("error: {e:?}")), } Ok(()) } -fn ecall_handler(mem: &mut Memory, xreg: &mut [u32; 32]) -> Result<(), emu::Error> { +fn ecall_handler(mem: &mut Memory, xreg: &mut [u32; 32]) -> Result<(), emu::Status> { let nr = xreg[Reg::A7.0 as usize]; match nr { @@ -122,7 +61,7 @@ fn ecall_handler(mem: &mut Memory, xreg: &mut [u32; 32]) -> Result<(), emu::Erro } // exit 93 => { - return Err(emu::Error::Exit { + return Err(emu::Status::Exit { code: xreg[Reg::A0.0 as usize] as i32, }); } diff --git a/tests/check.rs b/tests/check.rs new file mode 100644 index 0000000..fa3dbf4 --- /dev/null +++ b/tests/check.rs @@ -0,0 +1,86 @@ +use std::{ + io::Write, + path::{Path, PathBuf}, +}; + +use eyre::{Context, bail}; +use rustv32i::emu::{Reg, Status}; + +#[test] +fn check() -> eyre::Result<()> { + let tmpdir = tempfile::tempdir().wrap_err("failed to create tempdir")?; + + let dir = + Path::new(&std::env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR is not set")) + .join("tests") + .join("check"); + let files = std::fs::read_dir(&dir).wrap_err(format!("reading {}", dir.display()))?; + + for file in files { + let file = file.wrap_err(format!("reading file in {}", dir.display()))?; + + let name = file.file_name(); + let name = name.to_str().unwrap(); + if !name.ends_with(".s") { + continue; + } + + write!(std::io::stdout(), "test {name} ...")?; + std::io::stdout().flush()?; + + let output = build(&tmpdir.path(), &file.path()).wrap_err(format!("building {name}"))?; + let content = + std::fs::read(&output).wrap_err(format!("reading output from {}", output.display()))?; + + let status = rustv32i::execute_linux_elf( + &content, + matches!(std::env::var("EMULATOR_DEBUG").as_deref(), Ok(v) if v != "0"), + Box::new(|_, xreg| { + if xreg[Reg::A7.0 as usize] == u32::MAX { + if xreg[Reg::A0.0 as usize] == 1 { + Err(rustv32i::emu::Status::Exit { code: 0 }) + } else { + Err(rustv32i::emu::Status::Trap("wrong exit code")) + } + } else { + Err(rustv32i::emu::Status::Trap("wrong syscall")) + } + }), + ) + .wrap_err(format!("{name} failed"))?; + + if let Status::Exit { code: 0 } = status { + writeln!(std::io::stdout(), " ✅")?; + } else { + bail!("{name} returned an error: {status:?}"); + } + } + + Ok(()) +} + +fn build(tmpdir: &Path, src: &Path) -> eyre::Result { + let out_path = tmpdir.join(Path::new(src.file_name().unwrap()).with_extension("")); + + let mut cmd = std::process::Command::new("clang"); + cmd.args([ + "-target", + "riscv32-unknown-none-elf", + "-nostdlib", + "-march=rv32ima", + ]); + cmd.arg(src); + cmd.arg("-o"); + cmd.arg(&out_path); + + let output = cmd.output().wrap_err("failed to spawn clang")?; + if !output.status.success() { + bail!( + "failed to compile {}:\n{}", + src.display(), + String::from_utf8_lossy(&output.stderr) + ); + } + + Ok(out_path) +} diff --git a/tests/check/arith.s b/tests/check/arith.s new file mode 100644 index 0000000..29abddb --- /dev/null +++ b/tests/check/arith.s @@ -0,0 +1,23 @@ +.section .text +.globl _start +_start: + li t0, 10 + li t1, 20 + add t2, t0, t1 + li t3, 30 + bne t2, t3, fail + + li t0, 10 + li t1, -2 + add t2, t0, t1 + li t3, 8 + bne t2, t3, fail + + li a7, -1 + li a0, 1 + ecall + +fail: + li a7, -1 + li a0, 0 + ecall diff --git a/tests/check/smoke.s b/tests/check/smoke.s new file mode 100644 index 0000000..a033929 --- /dev/null +++ b/tests/check/smoke.s @@ -0,0 +1,6 @@ +.section .text +.globl _start +_start: + li a7, -1 + li a0, 1 + ecall