// Minimal ELF64 little-endian loader for BPF instruction sections.
pub(all) suberror ElfError {
NotElf
UnsupportedClass
UnsupportedEndian
UnsupportedMachine(Int)
Truncated
BadSection
NoText
}
pub(all) struct ElfSymbol {
name : String
value : Int
size : Int
info : Int
}
pub(all) struct ElfSection {
name : String
typ : Int
flags : Int
offset : Int
size : Int
}
pub(all) struct ElfImage {
entry : Int
text : Array[Insn]
symbols : Array[ElfSymbol]
sections : Array[ElfSection]
}
fn u8(bytes : Array[Int], off : Int) -> Int raise ElfError {
if off < 0 || off >= bytes.length() {
raise Truncated
}
bytes[off]
}
fn u16le(bytes : Array[Int], off : Int) -> Int raise ElfError {
u8(bytes, off) | (u8(bytes, off + 1) << 8)
}
fn u32le(bytes : Array[Int], off : Int) -> Int64 raise ElfError {
let mut value : Int64 = 0L
for i in 0..<4 {
value = value | (u8(bytes, off + i).to_int64() << (8 * i))
}
value
}
fn u64le(bytes : Array[Int], off : Int) -> Int64 raise ElfError {
let low = u32le(bytes, off).land(0xffffffffL)
let high = u32le(bytes, off + 4).land(0xffffffffL)
low | (high << 32)
}
fn i32le(bytes : Array[Int], off : Int) -> Int raise ElfError {
u32le(bytes, off).to_int()
}
fn i16le(bytes : Array[Int], off : Int) -> Int raise ElfError {
let value = u16le(bytes, off)
if value >= 0x8000 {
value - 0x10000
} else {
value
}
}
pub fn is_elf(bytes : Array[Int]) -> Bool {
bytes.length() >= 4 && bytes[0] == 0x7f && bytes[1] == 0x45 &&
bytes[2] == 0x4c && bytes[3] == 0x46
}
pub fn decode_instructions(bytes : Array[Int]) -> Array[Insn] raise ElfError {
if bytes.length() % 8 != 0 {
raise BadSection
}
let out : Array[Insn] = Array::new()
let mut pos = 0
while pos < bytes.length() {
let op = bytes[pos]
let regs = bytes[pos + 1]
let dst = regs & 0x0f
let src = (regs >> 4) & 0x0f
let off = i16le(bytes, pos + 2)
let imm = i32le(bytes, pos + 4)
out.push({ op, dst, src, off, imm })
pos = pos + 8
}
out
}
struct Section {
name_off : Int
typ : Int
flags : Int
offset : Int
size : Int
}
fn section_at(
bytes : Array[Int],
shoff : Int,
index : Int,
shentsize : Int,
) -> Section raise ElfError {
let base = shoff + index * shentsize
if base + 64 > bytes.length() {
raise Truncated
}
{
name_off: u32le(bytes, base).to_int(),
typ: u32le(bytes, base + 4).to_int(),
flags: u64le(bytes, base + 8).to_int(),
offset: u64le(bytes, base + 24).to_int(),
size: u64le(bytes, base + 32).to_int(),
}
}
fn section_name(strtab : Array[Int], offset : Int) -> String {
let out = StringBuilder()
let mut i = offset
while i >= 0 && i < strtab.length() && strtab[i] != 0 {
out.write_char(Char::from_int(strtab[i]))
i = i + 1
}
out.to_string()
}
fn slice(bytes : Array[Int], start : Int, size : Int) -> Array[Int] raise ElfError {
if start < 0 || size < 0 || start + size > bytes.length() {
raise Truncated
}
let out : Array[Int] = Array::new()
for i in 0.. ElfImage raise ElfError {
if !is_elf(bytes) {
raise NotElf
}
if u8(bytes, 4) != 2 {
raise UnsupportedClass
}
if u8(bytes, 5) != 1 {
raise UnsupportedEndian
}
let machine = u16le(bytes, 18)
if machine != 247 {
raise UnsupportedMachine(machine)
}
let entry = u64le(bytes, 24).to_int()
let shoff = u64le(bytes, 40).to_int()
let shentsize = u16le(bytes, 58)
let shnum = u16le(bytes, 60)
let shstrndx = u16le(bytes, 62)
if shentsize < 64 || shnum == 0 || shstrndx >= shnum {
raise BadSection
}
if shoff + shnum * shentsize > bytes.length() {
raise Truncated
}
let shstr = section_at(bytes, shoff, shstrndx, shentsize)
let strtab = slice(bytes, shstr.offset, shstr.size)
let mut text_bytes : Array[Int]? = None
let mut symtab : Section? = None
let mut symstr : Section? = None
let sections : Array[ElfSection] = Array::new()
for i in 0.. 0 && section.typ != 0 {
sections.push({
name,
typ: section.typ,
flags: section.flags,
offset: section.offset,
size: section.size,
})
}
if name == ".text" {
text_bytes = Some(slice(bytes, section.offset, section.size))
} else if name == ".symtab" {
symtab = Some(section)
} else if name == ".strtab" {
symstr = Some(section)
}
}
let text = match text_bytes {
Some(raw) => decode_instructions(raw)
None => raise NoText
}
let symbols : Array[ElfSymbol] = Array::new()
match (symtab, symstr) {
(Some(table), Some(names)) => {
let names_bytes = slice(bytes, names.offset, names.size)
let count = table.size / 24
for i in 0.. 0 {
symbols.push({
name,
value: u64le(bytes, base + 8).to_int(),
size: u64le(bytes, base + 16).to_int(),
info: u8(bytes, base + 4),
})
}
}
}
}
_ => ()
}
{ entry, text, symbols, sections }
}