Skip to main content

sbpf_runtime/
elf.rs

1use {
2    crate::errors::{RuntimeError, RuntimeResult},
3    either::Either,
4    sbpf_common::{inst_param::Number, instruction::Instruction, opcode::Opcode},
5    sbpf_disassembler::{
6        program::{Disassembly, Parsed, Program},
7        rodata::RodataSection,
8    },
9    sbpf_vm::memory::Memory,
10};
11
12/// Parse an ELF binary and return instructions, rodata, and entrypoint.
13pub fn load_elf(elf_bytes: &[u8]) -> RuntimeResult<(Vec<Instruction>, Vec<u8>, usize)> {
14    let program = Program::from_bytes(elf_bytes)
15        .map_err(|e| RuntimeError::ElfParseError(format!("{:?}", e)))?;
16
17    let Disassembly {
18        instructions,
19        rodata: rodata_section,
20        entrypoint: entrypoint_idx,
21    } = program
22        .to_ixs()
23        .and_then(Parsed::into_strict)
24        .map_err(|e| RuntimeError::ElfParseError(format!("{:?}", e)))?;
25    let entrypoint = entrypoint_idx.unwrap_or(0);
26
27    // into_strict fails on any decode error, so every entry is an instruction.
28    let mut instructions: Vec<Instruction> = instructions
29        .into_iter()
30        .map(|ix| ix.expect_left("strict disassembly contains no decode errors"))
31        .collect();
32
33    let mut rodata = rodata_section
34        .as_ref()
35        .map(|s| s.data.clone())
36        .unwrap_or_default();
37
38    if let Some(ref section) = rodata_section {
39        apply_relocations(&mut instructions, &mut rodata, section);
40    }
41
42    Ok((instructions, rodata, entrypoint))
43}
44
45/// Apply all relocations.
46fn apply_relocations(instructions: &mut [Instruction], rodata: &mut [u8], section: &RodataSection) {
47    let elf_base = section.base_address;
48    let elf_end = elf_base + section.data.len() as u64;
49
50    // 1. Relocate lddw immediates that reference rodata addresses (for v0 only).
51    if elf_base != Memory::RODATA_START {
52        for ix in instructions.iter_mut() {
53            if ix.opcode == Opcode::Lddw
54                && let Some(Either::Right(Number::Int(imm))) = &ix.imm
55            {
56                let addr = *imm as u64;
57                if addr >= elf_base && addr < elf_end {
58                    ix.imm = Some(Either::Right(Number::Int(
59                        (Memory::RODATA_START + addr - elf_base) as i64,
60                    )));
61                }
62            }
63        }
64    }
65
66    // 2. Apply data relocations.
67    for &offset in &section.data_relocations {
68        if offset + 8 <= rodata.len() {
69            let ptr = u64::from_le_bytes(rodata[offset..offset + 8].try_into().unwrap());
70            if ptr >= elf_base && ptr < elf_end {
71                let relocated = Memory::RODATA_START + (ptr - elf_base);
72                rodata[offset..offset + 8].copy_from_slice(&relocated.to_le_bytes());
73            }
74        }
75    }
76
77    // 3. Apply text relocations.
78    for &(offset, ix_idx) in &section.text_relocations {
79        if offset + 8 <= rodata.len() {
80            rodata[offset..offset + 8].copy_from_slice(&(ix_idx as u64).to_le_bytes());
81        }
82    }
83}