commit 7183421b8fd72c37d45474206d991044ad6b539a Author: Noratrieb <48135649+Noratrieb@users.noreply.github.com> Date: Fri Mar 7 23:27:54 2025 +0100 init diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..1d953f4 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use nix diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f006348 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/target +/test/* +!/test/*.c +!/test/Makefile \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..1922e57 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,97 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aligned-vec" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af15ccceeacb9304119d97925de463bc97ae3555ee8dc8056f67b119f66e5934" +dependencies = [ + "equator", +] + +[[package]] +name = "equator" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc" +dependencies = [ + "equator-macro", +] + +[[package]] +name = "equator-macro" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "eyre" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" +dependencies = [ + "indenter", + "once_cell", +] + +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + +[[package]] +name = "once_cell" +version = "1.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" + +[[package]] +name = "proc-macro2" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1f1914ce909e1658d9907913b4b91947430c7d9be598b15a1912935b8c04801" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustv64i" +version = "0.1.0" +dependencies = [ + "aligned-vec", + "eyre", +] + +[[package]] +name = "syn" +version = "2.0.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e02e925281e18ffd9d640e234264753c43edc62d64b2d4cf898f1bc5e75f3fc2" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..e46578e --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "rustv64i" +version = "0.1.0" +edition = "2024" + +[dependencies] +aligned-vec = "0.6.2" +eyre = "0.6.12" diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..303101f --- /dev/null +++ b/shell.nix @@ -0,0 +1,3 @@ +{ pkgs ? import { } }: pkgs.mkShell { + packages = with pkgs; [ llvmPackages_18.clang-unwrapped llvmPackages_18.lld ]; +} diff --git a/src/elf.rs b/src/elf.rs new file mode 100644 index 0000000..2b15fc0 --- /dev/null +++ b/src/elf.rs @@ -0,0 +1,145 @@ +use eyre::{Result, bail}; + +pub struct Elf { + pub content: Vec, +} + +#[derive(Debug)] +pub struct Header { + pub e_entry: u32, + pub e_phoff: u32, + pub e_shoff: u32, + pub e_flags: u32, + pub e_ehsize: u16, + pub e_phentsize: u16, + pub e_phnum: u16, + pub e_shentsize: u16, + pub e_shnum: u16, + pub e_shstrndx: u16, +} + +#[derive(Debug)] +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_flags: u32, + pub p_align: u32, +} + +impl Elf { + pub fn header(&self) -> Result
{ + let (ident, rest) = self.content.split_bytes(16)?; + if ident[..4] != *b"\x7fELF" { + bail!("not an elf file (invalid magic)"); + } + // ELFCLASS32 + if ident[5] != 1 { + bail!("not a ELF32 file (EI_CLASS={})", ident[5]); + } + + let (e_type, rest) = rest.split_u16()?; + // ET_EXEC + if e_type != 2 { + bail!("not a static executable: {e_type}"); + } + + let (e_machine, rest) = rest.split_u16()?; + // EM_RISCV + if e_machine != 243 { + bail!("not a RISC-V executable"); + } + + let (_e_version, rest) = rest.split_u32()?; + + let (e_entry, rest) = rest.split_u32()?; + let (e_phoff, rest) = rest.split_u32()?; + let (e_shoff, rest) = rest.split_u32()?; + let (e_flags, rest) = rest.split_u32()?; + let (e_ehsize, rest) = rest.split_u16()?; + let (e_phentsize, rest) = rest.split_u16()?; + let (e_phnum, rest) = rest.split_u16()?; + let (e_shentsize, rest) = rest.split_u16()?; + let (e_shnum, rest) = rest.split_u16()?; + let (e_shstrndx, _) = rest.split_u16()?; + + Ok(Header { + e_entry, + e_phoff, + e_shoff, + e_flags, + e_ehsize, + e_phentsize, + e_phnum, + e_shentsize, + e_shnum, + e_shstrndx, + }) + } + + pub fn segments(&self) -> Result> { + let header = self.header()?; + + let (_, phdrs) = self.content.split_bytes(header.e_phoff as usize)?; + let (mut phdrs, _) = phdrs.split_bytes((header.e_phentsize * header.e_phnum) as usize)?; + + let mut parsed_phdrs = vec![]; + + while !phdrs.is_empty() { + 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()?; + + parsed_phdrs.push(Phdr { + p_type, + p_offset, + p_vaddr, + p_paddr, + p_filesz, + p_memsz, + p_flags, + p_align, + }); + } + + Ok(parsed_phdrs) + } +} + +pub trait SplitAtCheckedErr { + fn split_bytes(&self, split: usize) -> Result<(&[u8], &[u8])>; + fn split_u16(&self) -> Result<(u16, &[u8])>; + fn split_u32(&self) -> Result<(u32, &[u8])>; + fn split_u64(&self) -> Result<(u64, &[u8])>; +} +impl SplitAtCheckedErr for [u8] { + fn split_bytes(&self, mid: usize) -> Result<(&[u8], &[u8])> { + if self.len() < mid { + bail!("invalid file: too short"); + } + Ok(self.split_at(mid)) + } + fn split_u16(&self) -> Result<(u16, &[u8])> { + let (bytes, rest) = self.split_bytes(2)?; + Ok((u16::from_le_bytes(bytes.try_into().unwrap()), rest)) + } + fn split_u32(&self) -> Result<(u32, &[u8])> { + let (bytes, rest) = self.split_bytes(4)?; + Ok((u32::from_le_bytes(bytes.try_into().unwrap()), rest)) + } + fn split_u64(&self) -> Result<(u64, &[u8])> { + let (bytes, rest) = self.split_bytes(8)?; + Ok((u64::from_le_bytes(bytes.try_into().unwrap()), rest)) + } +} diff --git a/src/emu.rs b/src/emu.rs new file mode 100644 index 0000000..c7d6c1a --- /dev/null +++ b/src/emu.rs @@ -0,0 +1,41 @@ +use aligned_vec::{AVec, ConstAlign}; + +use crate::PAGE_SIZE; + +pub struct Memory { + pub mem: AVec>, +} + +impl Memory { + fn load_u32(&self, addr: u32) -> Result { + Ok(u32::from_le_bytes( + self.mem + .get((addr as usize)..) + .ok_or(())? + .get(..4) + .ok_or(())? + .try_into() + .unwrap(), + )) + } +} + +pub struct Emulator { + pub mem: Memory, + pub xreg: [u32; 32], + pub pc: u32, +} + +impl Emulator { + pub fn execute(&mut self) -> Result<(), ()> { + loop { + self.step(); + } + } + + fn step(&mut self) -> Result<(), ()> { + let instruction = self.mem.load_u32(self.pc)?; + + todo!() + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..e767cdf --- /dev/null +++ b/src/main.rs @@ -0,0 +1,71 @@ +use aligned_vec::avec; +use eyre::{OptionExt, bail}; + +mod elf; +mod emu; + +const PAGE_SIZE: usize = 4096; + +// 2 MiB +const MEMORY_SIZE: usize = 2 << 21; + +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()?; + + dbg!(&header); + + let segments = elf.segments()?; + + let mut mem = emu::Memory { + mem: avec![[PAGE_SIZE]| 0; MEMORY_SIZE], + }; + + for phdr in segments { + dbg!(&phdr); + 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_PHDR + 6 => {} + // PT_GNU_EH_FRAME + 1685382480 => {} + // PT_GNU_STACK + 1685382481 => {} + // 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], + pc: start, + }; + emu.execute().unwrap(); + + Ok(()) +} diff --git a/test/Makefile b/test/Makefile new file mode 100644 index 0000000..db66864 --- /dev/null +++ b/test/Makefile @@ -0,0 +1,5 @@ +CC = clang -target riscv32-unknown-linux-gnu -fuse-ld=lld -march=rv32i +CC_STATIC = $(CC) -static -nostdlib -nodefaultlibs + +test: test.c + $(CC_STATIC) test.c -o test diff --git a/test/test.c b/test/test.c new file mode 100644 index 0000000..c0f319a --- /dev/null +++ b/test/test.c @@ -0,0 +1,3 @@ +void _start() +{ +}