mirror of
https://github.com/Noratrieb/rustv32i.git
synced 2026-01-14 05:15:01 +01:00
init
This commit is contained in:
commit
7183421b8f
10 changed files with 378 additions and 0 deletions
1
.envrc
Normal file
1
.envrc
Normal file
|
|
@ -0,0 +1 @@
|
|||
use nix
|
||||
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
/target
|
||||
/test/*
|
||||
!/test/*.c
|
||||
!/test/Makefile
|
||||
97
Cargo.lock
generated
Normal file
97
Cargo.lock
generated
Normal file
|
|
@ -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"
|
||||
8
Cargo.toml
Normal file
8
Cargo.toml
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
name = "rustv64i"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
aligned-vec = "0.6.2"
|
||||
eyre = "0.6.12"
|
||||
3
shell.nix
Normal file
3
shell.nix
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{ pkgs ? import <nixpkgs> { } }: pkgs.mkShell {
|
||||
packages = with pkgs; [ llvmPackages_18.clang-unwrapped llvmPackages_18.lld ];
|
||||
}
|
||||
145
src/elf.rs
Normal file
145
src/elf.rs
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
use eyre::{Result, bail};
|
||||
|
||||
pub struct Elf {
|
||||
pub content: Vec<u8>,
|
||||
}
|
||||
|
||||
#[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<Header> {
|
||||
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<Vec<Phdr>> {
|
||||
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))
|
||||
}
|
||||
}
|
||||
41
src/emu.rs
Normal file
41
src/emu.rs
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
use aligned_vec::{AVec, ConstAlign};
|
||||
|
||||
use crate::PAGE_SIZE;
|
||||
|
||||
pub struct Memory {
|
||||
pub mem: AVec<u8, ConstAlign<PAGE_SIZE>>,
|
||||
}
|
||||
|
||||
impl Memory {
|
||||
fn load_u32(&self, addr: u32) -> Result<u32, ()> {
|
||||
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!()
|
||||
}
|
||||
}
|
||||
71
src/main.rs
Normal file
71
src/main.rs
Normal file
|
|
@ -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(())
|
||||
}
|
||||
5
test/Makefile
Normal file
5
test/Makefile
Normal file
|
|
@ -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
|
||||
3
test/test.c
Normal file
3
test/test.c
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
void _start()
|
||||
{
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue