// Copyright (c) 2025 taitep // SPDX-License-Identifier: BSD-2-Clause // // This file is part of TRVE (https://gitea.taitep.se/taitep/trve) // See LICENSE file in the project root for full license text. use std::{fs, path::Path}; use anyhow::{Result, bail}; use goblin::{ Object, elf::{ header::{EM_RISCV, ET_EXEC}, program_header::PT_LOAD, }, }; use trve::{consts::Addr, mem::RAM_START}; pub fn load>(path: P, ram: &mut [u8]) -> Result { let buf = fs::read(path)?; match Object::parse(&buf)? { Object::Elf(elf) => { if elf.header.e_type != ET_EXEC { bail!("Executable type incorrect, may not be an executable or may be dynamic"); } if elf.header.e_machine != EM_RISCV { bail!("Executable architecture is not RISC-V"); } if !elf.is_64 { bail!("Executable is not 64-bit"); } if !elf.little_endian { bail!("Executable is not little-endian"); } for ph in elf.program_headers { if ph.p_type == PT_LOAD { let start = (ph.p_vaddr - RAM_START) as usize; let end = start + ph.p_memsz as usize; let file_end = start + ph.p_filesz as usize; if end > RAM_START as usize + ram.len() { bail!("Segment at 0x{:x} does not fit in RAM", ph.p_vaddr); } ram[start..file_end].copy_from_slice( &buf[ph.p_offset as usize..(ph.p_offset + ph.p_filesz) as usize], ); ram[file_end..end].fill(0); } } Ok(elf.entry) } Object::Unknown(_) => { eprintln!("Unrecognized executable format identifier, assuming raw binary"); if buf.len() > ram.len() { bail!("Program too large for RAM"); } ram[..buf.len()].copy_from_slice(&buf); Ok(RAM_START) } _ => bail!("Unsupported executable format"), } }