diff --git a/src/disassembler.rs b/src/disassembler.rs index b020b374..32748656 100644 --- a/src/disassembler.rs +++ b/src/disassembler.rs @@ -267,19 +267,16 @@ pub fn disassemble_instruction( ebpf::JSLE_REG => { name = "jsle"; desc = jmp_reg_str(name, insn, cfg_nodes); }, ebpf::CALL_IMM => { let key = sbpf_version.calculate_call_imm_target_pc(pc, insn.imm); - let function_name = function_registry.lookup_by_key(key).map(|(function_name, _)| String::from_utf8_lossy(function_name).to_string()); - let function_name = if let Some(function_name) = function_name { - name = "call"; - function_name - } else { + let mut name = "call"; + let mut function_name = function_registry.lookup_by_key(key).map(|(function_name, _)| String::from_utf8_lossy(function_name).to_string()); + if !sbpf_version.static_syscalls() && function_name.is_none() { name = "syscall"; - loader.get_function_registry(sbpf_version).lookup_by_key(insn.imm as u32).map(|(function_name, _)| String::from_utf8_lossy(function_name).to_string()).unwrap_or_else(|| "[invalid]".to_string()) - }; - desc = format!("{name} {function_name}"); + function_name = loader.get_function_registry(sbpf_version).lookup_by_key(insn.imm as u32).map(|(function_name, _)| String::from_utf8_lossy(function_name).to_string()); + } + desc = format!("{} {}", name, function_name.unwrap_or_else(|| "[invalid]".to_string())); }, ebpf::CALL_REG => { name = "callx"; desc = format!("{} r{}", name, if sbpf_version.callx_uses_src_reg() { insn.src } else { insn.imm as u8 }); }, - ebpf::EXIT - | ebpf::RETURN if !sbpf_version.static_syscalls() => { name = "exit"; desc = name.to_string(); }, + ebpf::EXIT if !sbpf_version.static_syscalls() => { name = "exit"; desc = name.to_string(); }, ebpf::RETURN if sbpf_version.static_syscalls() => { name = "return"; desc = name.to_string(); }, ebpf::SYSCALL if sbpf_version.static_syscalls() => { desc = format!("syscall {}", insn.imm); }, diff --git a/src/jit.rs b/src/jit.rs index f151d2c3..ab967a10 100644 --- a/src/jit.rs +++ b/src/jit.rs @@ -1719,12 +1719,14 @@ impl<'a, C: ContextObject> JitCompiler<'a, C> { mod tests { use super::*; use crate::{ + disassembler::disassemble_instruction, program::{BuiltinProgram, FunctionRegistry, SBPFVersion}, + static_analysis::CfgNode, syscalls, vm::TestContextObject, }; use byteorder::{ByteOrder, LittleEndian}; - use std::sync::Arc; + use std::{collections::BTreeMap, sync::Arc}; #[test] fn test_runtime_environment_slots() { @@ -1838,35 +1840,45 @@ mod tests { <= MACHINE_CODE_PER_INSTRUCTION_METER_CHECKPOINT ); + let mut cfg_nodes = BTreeMap::new(); + cfg_nodes.insert( + 8, + CfgNode { + label: std::string::String::from("label"), + ..CfgNode::default() + }, + ); + for sbpf_version in [SBPFVersion::V0, SBPFVersion::V3] { + println!("opcode;machine_code_length_per_instruction;assembly"); let empty_program_machine_code_length = empty_program_machine_code_length_per_version[sbpf_version as usize]; for mut opcode in 0x00..=0xFF { let (registers, immediate) = match opcode { - 0x85 | 0x8D => (0x88, 8), - 0x86 => { + 0x85 if !sbpf_version.static_syscalls() => (0x00, Some(8)), + 0x85 if sbpf_version.static_syscalls() => (0x00, None), + 0x8D => (0x88, Some(0)), + 0x95 if sbpf_version.static_syscalls() => (0x00, Some(1)), + 0xE5 if !sbpf_version.static_syscalls() => { // Put external function calls on a separate loop iteration opcode = 0x85; - (0x00, 0x91020CDD) + (0x00, Some(0x91020CDD)) } - 0x87 => { + 0xF5 => { // Put invalid function calls on a separate loop iteration opcode = 0x85; - (0x88, 0x91020CDD) + (0x00, Some(0x91020CD0)) } - 0x95 => { - // Put a valid syscall - (0, 1) - } - 0xD4 | 0xDC => (0x88, 16), - _ => (0x88, 0xFFFFFFFF), + 0xD4 | 0xDC => (0x88, Some(16)), + _ => (0x88, Some(0x11223344)), }; for pc in 0..INSTRUCTION_COUNT { prog[pc * ebpf::INSN_SIZE] = opcode; prog[pc * ebpf::INSN_SIZE + 1] = registers; - prog[pc * ebpf::INSN_SIZE + 2] = 0xFF; - prog[pc * ebpf::INSN_SIZE + 3] = 0xFF; + let offset = 7_u16.wrapping_sub(pc as u16); + LittleEndian::write_u16(&mut prog[pc * ebpf::INSN_SIZE + 2..], offset); + let immediate = immediate.unwrap_or_else(|| 7_u32.wrapping_sub(pc as u32)); LittleEndian::write_u32(&mut prog[pc * ebpf::INSN_SIZE + 4..], immediate); } let config = Config { @@ -1900,12 +1912,19 @@ mod tests { (machine_code_length_per_instruction + 0.5) as usize <= MAX_MACHINE_CODE_LENGTH_PER_INSTRUCTION ); - /*println!("opcode={:02X} machine_code_length_per_instruction={}", opcode, machine_code_length_per_instruction); - let analysis = crate::static_analysis::Analysis::from_executable(&executable).unwrap(); - { - let stdout = std::io::stdout(); - analysis.disassemble(&mut stdout.lock()).unwrap(); - }*/ + let insn = ebpf::get_insn_unchecked(&prog, 0); + let assembly = disassemble_instruction( + &insn, + 0, + &cfg_nodes, + executable.get_function_registry(), + executable.get_loader(), + executable.get_sbpf_version(), + ); + println!( + "{:02X};{:>7.3};{}", + opcode, machine_code_length_per_instruction, assembly + ); } } } diff --git a/src/static_analysis.rs b/src/static_analysis.rs index 89fc8ab7..535fb6ea 100644 --- a/src/static_analysis.rs +++ b/src/static_analysis.rs @@ -230,24 +230,13 @@ impl<'a> Analysis<'a> { self.cfg_nodes.entry(*pc).or_default(); } let mut cfg_edges = BTreeMap::new(); - for insn in self.instructions.iter() { + for (pc, insn) in self.instructions.iter().enumerate() { let target_pc = (insn.ptr as isize + insn.off as isize + 1) as usize; match insn.opc { ebpf::CALL_IMM => { - if let Some((function_name, _function)) = self - .executable - .get_loader() - .get_function_registry(sbpf_version) - .lookup_by_key(insn.imm as u32) - { - if function_name == b"abort" { - self.cfg_nodes.entry(insn.ptr + 1).or_default(); - cfg_edges.insert(insn.ptr, (insn.opc, Vec::new())); - } - } else if let Some((_function_name, target_pc)) = self - .executable - .get_function_registry() - .lookup_by_key(insn.imm as u32) + let key = sbpf_version.calculate_call_imm_target_pc(pc, insn.imm); + if let Some((_function_name, target_pc)) = + self.executable.get_function_registry().lookup_by_key(key) { self.cfg_nodes.entry(insn.ptr + 1).or_default(); self.cfg_nodes.entry(target_pc).or_default(); @@ -269,7 +258,11 @@ impl<'a> Analysis<'a> { }; cfg_edges.insert(insn.ptr, (insn.opc, destinations)); } - ebpf::EXIT => { + ebpf::EXIT if !sbpf_version.static_syscalls() => { + self.cfg_nodes.entry(insn.ptr + 1).or_default(); + cfg_edges.insert(insn.ptr, (insn.opc, Vec::new())); + } + ebpf::RETURN if sbpf_version.static_syscalls() => { self.cfg_nodes.entry(insn.ptr + 1).or_default(); cfg_edges.insert(insn.ptr, (insn.opc, Vec::new())); }