Skip to content
This repository has been archived by the owner on Jan 10, 2025. It is now read-only.

Commit

Permalink
Keeps hashed syscall names around for the disassembler and the CLI tool.
Browse files Browse the repository at this point in the history
  • Loading branch information
Lichtso committed Jun 4, 2021
1 parent 113b4ba commit 5028ec7
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 35 deletions.
6 changes: 3 additions & 3 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,13 +137,13 @@ fn main() {
let mut file = File::open(&Path::new(matches.value_of("elf").unwrap())).unwrap();
let mut elf = Vec::new();
file.read_to_end(&mut elf).unwrap();
<dyn Executable::<UserError, TestInstructionMeter>>::from_elf(&elf, verifier, config)
<dyn Executable<UserError, TestInstructionMeter>>::from_elf(&elf, verifier, config)
.map_err(|err| format!("Executable constructor failed: {:?}", err))
}
}
.unwrap();

let (syscalls, _functions) = executable.get_symbols();
let syscalls = executable.get_syscall_symbols();
let mut syscall_registry = SyscallRegistry::default();
for hash in syscalls.keys() {
let _ = syscall_registry.register_syscall_by_hash(*hash, MockSyscall::call);
Expand Down Expand Up @@ -192,7 +192,7 @@ fn main() {
];
let heap_region = MemoryRegion::new_from_slice(&heap, ebpf::MM_HEAP_START, 0, true);
let mut vm = EbpfVm::new(executable.as_ref(), &mut mem, &[heap_region]).unwrap();
for (hash, name) in &analysis.syscalls {
for (hash, name) in analysis.executable.get_syscall_symbols() {
vm.bind_syscall_context_object(Box::new(MockSyscall { name: name.clone() }), Some(*hash))
.unwrap();
}
Expand Down
2 changes: 1 addition & 1 deletion src/disassembler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ pub fn disassemble_instruction<E: UserDefinedError, I: InstructionMeter>(insn: &
ebpf::JSLE_IMM => { name = "jsle"; desc = jmp_imm_str(name, &insn, analysis); },
ebpf::JSLE_REG => { name = "jsle"; desc = jmp_reg_str(name, &insn, analysis); },
ebpf::CALL_IMM => {
desc = if let Some(syscall_name) = analysis.syscalls.get(&(insn.imm as u32)) {
desc = if let Some(syscall_name) = analysis.executable.get_syscall_symbols().get(&(insn.imm as u32)) {
name = "syscall";
format!("{} {}", name, syscall_name)
} else {
Expand Down
54 changes: 30 additions & 24 deletions src/elf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,7 @@ use goblin::{
elf::{header::*, reloc::*, section_header::*, Elf},
error::Error as GoblinError,
};
use std::{
collections::BTreeMap,
fmt::Debug,
mem,
ops::Range,
str,
};
use std::{collections::BTreeMap, fmt::Debug, mem, ops::Range, str};

/// Error definitions
#[derive(Debug, thiserror::Error, PartialEq, Eq)]
Expand Down Expand Up @@ -217,8 +211,10 @@ pub struct EBpfElf<E: UserDefinedError, I: InstructionMeter> {
text_section_info: SectionInfo,
/// Read-only section info
ro_section_infos: Vec<SectionInfo>,
/// Call resolution map (pc, hash, name)
/// Call resolution map (hash, pc, name)
bpf_functions: BTreeMap<u32, (usize, String)>,
/// Syscall symbol map (hash, name)
syscall_symbols: BTreeMap<u32, String>,
/// Syscall resolution map
syscall_registry: SyscallRegistry,
/// Compiled program and argument
Expand Down Expand Up @@ -327,23 +323,17 @@ impl<E: UserDefinedError, I: InstructionMeter> Executable<E, I> for EBpfElf<E, I
}

/// Get syscalls and BPF functions (if debug symbols are not stripped)
fn get_symbols(&self) -> (BTreeMap<u32, String>, BTreeMap<usize, (u32, String)>) {
let mut syscalls = BTreeMap::new();
fn get_function_symbols(&self) -> BTreeMap<usize, (u32, String)> {
let mut bpf_functions = BTreeMap::new();
if let Ok(elf) = Elf::parse(self.elf_bytes.as_slice()) {
for symbol in &elf.dynsyms {
if symbol.st_info != 0x10 {
continue;
}
let name = elf.dynstrtab.get(symbol.st_name).unwrap();
let hash = ebpf::hash_symbol_name(&name.as_bytes());
syscalls.insert(hash, name.to_string());
}
}
for (hash, (pc, name)) in self.bpf_functions.iter() {
bpf_functions.insert(*pc, (*hash, name.clone()));
}
(syscalls, bpf_functions)
bpf_functions
}

/// Get syscalls symbols
fn get_syscall_symbols(&self) -> &BTreeMap<u32, String> {
&self.syscall_symbols
}
}

Expand All @@ -368,6 +358,7 @@ impl<'a, E: UserDefinedError, I: InstructionMeter> EBpfElf<E, I> {
},
ro_section_infos: vec![],
bpf_functions,
syscall_symbols: BTreeMap::default(),
syscall_registry: SyscallRegistry::default(),
compiled_program: None,
}
Expand Down Expand Up @@ -397,7 +388,13 @@ impl<'a, E: UserDefinedError, I: InstructionMeter> EBpfElf<E, I> {

// relocate symbols
let mut bpf_functions = BTreeMap::default();
Self::relocate(&mut bpf_functions, &elf, elf_bytes.as_slice_mut())?;
let mut syscall_symbols = BTreeMap::default();
Self::relocate(
&mut bpf_functions,
&mut syscall_symbols,
&elf,
elf_bytes.as_slice_mut(),
)?;

// calculate entrypoint offset into the text section
let offset = elf.header.e_entry - text_section.sh_addr;
Expand Down Expand Up @@ -439,6 +436,7 @@ impl<'a, E: UserDefinedError, I: InstructionMeter> EBpfElf<E, I> {
text_section_info,
ro_section_infos,
bpf_functions,
syscall_symbols,
syscall_registry: SyscallRegistry::default(),
compiled_program: None,
})
Expand Down Expand Up @@ -547,6 +545,7 @@ impl<'a, E: UserDefinedError, I: InstructionMeter> EBpfElf<E, I> {
/// Relocates the ELF in-place
fn relocate(
bpf_functions: &mut BTreeMap<u32, (usize, String)>,
syscall_symbols: &mut BTreeMap<u32, String>,
elf: &Elf,
elf_bytes: &mut [u8],
) -> Result<(), ElfError> {
Expand Down Expand Up @@ -644,9 +643,10 @@ impl<'a, E: UserDefinedError, I: InstructionMeter> EBpfElf<E, I> {
register_bpf_function(bpf_functions, target_pc, name)?
} else {
// syscall
*syscall_cache
syscall_cache
.entry(sym.st_name)
.or_insert_with(|| ebpf::hash_symbol_name(name.as_bytes()))
.or_insert_with(|| (ebpf::hash_symbol_name(name.as_bytes()), name))
.0
};
let mut checked_slice = elf_bytes
.get_mut(imm_offset..imm_offset.saturating_add(BYTE_LENGTH_IMMEIDATE))
Expand All @@ -657,6 +657,12 @@ impl<'a, E: UserDefinedError, I: InstructionMeter> EBpfElf<E, I> {
}
}

// Save hashed syscall names for debugging
*syscall_symbols = syscall_cache
.values()
.map(|(hash, name)| (*hash, name.to_string()))
.collect();

// Register all known function names from the symbol table
for symbol in &elf.syms {
if symbol.st_info & 0xEF != 0x02 {
Expand Down
11 changes: 6 additions & 5 deletions src/static_analysis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,6 @@ pub struct Analysis<'a, E: UserDefinedError, I: InstructionMeter> {
pub executable: &'a dyn Executable<E, I>,
/// Plain list of instructions as they occur in the executable
pub instructions: Vec<ebpf::Insn>,
/// Syscalls used by the executable (available if debug symbols are not stripped)
pub syscalls: BTreeMap<u32, String>,
/// Functions in the executable
pub functions: BTreeMap<usize, (u32, String)>,
/// Nodes of the control-flow graph
Expand All @@ -116,7 +114,7 @@ impl<'a, E: UserDefinedError, I: InstructionMeter> Analysis<'a, E, I> {
/// Analyze an executable statically
pub fn from_executable(executable: &'a dyn Executable<E, I>) -> Self {
let (_program_vm_addr, program) = executable.get_text_bytes().unwrap();
let (syscalls, functions) = executable.get_symbols();
let functions = executable.get_function_symbols();
debug_assert!(
program.len() % ebpf::INSN_SIZE == 0,
"eBPF program length must be a multiple of {:?} octets is {:?}",
Expand All @@ -140,7 +138,6 @@ impl<'a, E: UserDefinedError, I: InstructionMeter> Analysis<'a, E, I> {
let mut result = Self {
executable,
instructions,
syscalls,
functions,
cfg_nodes: BTreeMap::new(),
topological_order: Vec::new(),
Expand Down Expand Up @@ -189,7 +186,11 @@ impl<'a, E: UserDefinedError, I: InstructionMeter> Analysis<'a, E, I> {
let target_pc = (insn.ptr as isize + insn.off as isize + 1) as usize;
match insn.opc {
ebpf::CALL_IMM => {
if let Some(syscall_name) = self.syscalls.get(&(insn.imm as u32)) {
if let Some(syscall_name) = self
.executable
.get_syscall_symbols()
.get(&(insn.imm as u32))
{
if syscall_name == "abort" {
self.cfg_nodes
.entry(insn.ptr + 1)
Expand Down
6 changes: 4 additions & 2 deletions src/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,8 +226,10 @@ pub trait Executable<E: UserDefinedError, I: InstructionMeter>: Send + Sync {
fn jit_compile(&mut self) -> Result<(), EbpfError<E>>;
/// Report information on a symbol that failed to be resolved
fn report_unresolved_symbol(&self, insn_offset: usize) -> Result<u64, EbpfError<E>>;
/// Get syscalls and BPF functions (if debug symbols are not stripped)
fn get_symbols(&self) -> (BTreeMap<u32, String>, BTreeMap<usize, (u32, String)>);
/// Get BPF functions
fn get_function_symbols(&self) -> BTreeMap<usize, (u32, String)>;
/// Get syscalls symbols
fn get_syscall_symbols(&self) -> &BTreeMap<u32, String>;
}

/// Static constructors for Executable
Expand Down

0 comments on commit 5028ec7

Please sign in to comment.