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::{program::Program, rodata::RodataSection},
6    sbpf_vm::memory::Memory,
7};
8
9/// Parse an ELF binary and return instructions, rodata, and entrypoint.
10pub fn load_elf(elf_bytes: &[u8]) -> RuntimeResult<(Vec<Instruction>, Vec<u8>, usize)> {
11    let program = Program::from_bytes(elf_bytes)
12        .map_err(|e| RuntimeError::ElfParseError(format!("{:?}", e)))?;
13    let (mut instructions, rodata_section, entrypoint_idx) = program
14        .to_ixs()
15        .map_err(|e| RuntimeError::ElfParseError(format!("{:?}", e)))?;
16    let entrypoint = entrypoint_idx.unwrap_or(0);
17
18    let mut rodata = rodata_section
19        .as_ref()
20        .map(|s| s.data.clone())
21        .unwrap_or_default();
22
23    if let Some(ref section) = rodata_section {
24        apply_relocations(&mut instructions, &mut rodata, section);
25    }
26
27    Ok((instructions, rodata, entrypoint))
28}
29
30/// Apply all relocations.
31fn apply_relocations(instructions: &mut [Instruction], rodata: &mut [u8], section: &RodataSection) {
32    let elf_base = section.base_address;
33    let elf_end = elf_base + section.data.len() as u64;
34
35    // 1. Relocate lddw immediates that reference rodata addresses.
36    for ix in instructions.iter_mut() {
37        if ix.opcode == Opcode::Lddw
38            && let Some(Either::Right(Number::Int(imm))) = &ix.imm
39        {
40            let addr = *imm as u64;
41            if addr >= elf_base && addr < elf_end {
42                ix.imm = Some(Either::Right(Number::Int(
43                    (Memory::RODATA_START + addr - elf_base) as i64,
44                )));
45            }
46        }
47    }
48
49    // 2. Apply data relocations.
50    for &offset in &section.data_relocations {
51        if offset + 8 <= rodata.len() {
52            let ptr = u64::from_le_bytes(rodata[offset..offset + 8].try_into().unwrap());
53            if ptr >= elf_base && ptr < elf_end {
54                let relocated = Memory::RODATA_START + (ptr - elf_base);
55                rodata[offset..offset + 8].copy_from_slice(&relocated.to_le_bytes());
56            }
57        }
58    }
59
60    // 3. Apply text relocations.
61    for &(offset, ix_idx) in &section.text_relocations {
62        if offset + 8 <= rodata.len() {
63            rodata[offset..offset + 8].copy_from_slice(&(ix_idx as u64).to_le_bytes());
64        }
65    }
66}