use crate::ast::{Field, Module, Value};
use crate::format::elf::{EM_386, EM_AARCH64, EM_BPF, EM_SBF, EM_X86_64, Elf64File, Phdr64};
#[must_use]
pub fn build_module(elf: &Elf64File) -> Module {
let arch = match elf.ehdr.e_machine {
EM_X86_64 => "x86_64",
EM_386 => "i386",
EM_AARCH64 => "aarch64",
EM_BPF => "bpf",
EM_SBF => "sbf",
_ => "unknown",
};
let abi = guess_abi(elf);
let build = Value::Block(vec![
field(
"e_ident",
Value::List(elf.ehdr.e_ident.iter().copied().map(byte_value).collect()),
),
field("e_machine", Value::Int(u64::from(elf.ehdr.e_machine))),
field("e_version", Value::Int(u64::from(elf.ehdr.e_version))),
field("e_phoff", Value::Int(elf.ehdr.e_phoff)),
field("e_shoff", Value::Int(elf.ehdr.e_shoff)),
field("e_flags", Value::Int(u64::from(elf.ehdr.e_flags))),
field("e_ehsize", Value::Int(u64::from(elf.ehdr.e_ehsize))),
field("e_phentsize", Value::Int(u64::from(elf.ehdr.e_phentsize))),
field("e_phnum", Value::Int(u64::from(elf.ehdr.e_phnum))),
field("e_shentsize", Value::Int(u64::from(elf.ehdr.e_shentsize))),
field("e_shnum", Value::Int(u64::from(elf.ehdr.e_shnum))),
field("e_shstrndx", Value::Int(u64::from(elf.ehdr.e_shstrndx))),
field("file_size", Value::Int(elf.file_size())),
field(
"phdrs",
Value::List(elf.phdrs.iter().map(phdr_block).collect()),
),
field(
"shdrs",
Value::List(
elf.shdrs
.iter()
.enumerate()
.map(|(i, sh)| shdr_block(elf, i, sh))
.collect(),
),
),
field(
"padding",
Value::List(
elf.padding()
.iter()
.filter(|(_, bytes)| bytes.iter().any(|&b| b != 0))
.map(padding_block)
.collect(),
),
),
]);
let bits = match elf.class {
crate::format::elf::ElfClass::Elf32 => 32,
crate::format::elf::ElfClass::Elf64 => 64,
};
Module {
fields: vec![
field("arch", Value::String(arch.into())),
field("abi", Value::String(abi.into())),
field("format", Value::String("elf".into())),
field("bits", Value::Int(bits)),
field("endian", Value::String("little".into())),
field("type", Value::Int(u64::from(elf.ehdr.e_type))),
field("entry", Value::Int(elf.ehdr.e_entry)),
field("build", build),
],
}
}
fn byte_value(b: u8) -> Value {
Value::Int(u64::from(b))
}
fn phdr_block(p: &Phdr64) -> Value {
Value::Block(vec![
field("p_type", Value::Int(u64::from(p.p_type))),
field("p_flags", Value::Int(u64::from(p.p_flags))),
field("p_offset", Value::Int(p.p_offset)),
field("p_vaddr", Value::Int(p.p_vaddr)),
field("p_paddr", Value::Int(p.p_paddr)),
field("p_filesz", Value::Int(p.p_filesz)),
field("p_memsz", Value::Int(p.p_memsz)),
field("p_align", Value::Int(p.p_align)),
])
}
fn shdr_block(elf: &Elf64File, idx: usize, s: &crate::format::elf::Shdr64) -> Value {
let name = elf.section_name(idx).unwrap_or("").to_string();
Value::Block(vec![
field("name", Value::String(name)),
field("sh_name", Value::Int(u64::from(s.sh_name))),
field("sh_type", Value::Int(u64::from(s.sh_type))),
field("sh_flags", Value::Int(s.sh_flags)),
field("sh_addr", Value::Int(s.sh_addr)),
field("sh_offset", Value::Int(s.sh_offset)),
field("sh_size", Value::Int(s.sh_size)),
field("sh_link", Value::Int(u64::from(s.sh_link))),
field("sh_info", Value::Int(u64::from(s.sh_info))),
field("sh_addralign", Value::Int(s.sh_addralign)),
field("sh_entsize", Value::Int(s.sh_entsize)),
])
}
fn padding_block(p: &(u64, Vec<u8>)) -> Value {
Value::Block(vec![
field("offset", Value::Int(p.0)),
field(
"bytes",
Value::List(p.1.iter().copied().map(byte_value).collect()),
),
])
}
fn field(name: &str, value: Value) -> Field {
Field {
name: name.into(),
value,
}
}
fn guess_abi(elf: &Elf64File) -> &'static str {
match elf.ehdr.e_ident[7] {
0 => "sysv",
_ => "custom",
}
}