summaryrefslogtreecommitdiff
path: root/src/execload.rs
blob: 12fe90d43db313718e6b9f0a8adcbb7b4a089204 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
// Copyright (c) 2025 taitep
// SPDX-License-Identifier: MIT
//
// 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;

pub fn load<P: AsRef<Path>>(path: P, ram: &mut [u8], ram_start: Addr) -> Result<Addr> {
    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"),
    }
}