//! Debug structs use crate as androscalpel_serializer; use crate::{ Error, ReadSeek, Result, Serializable, SerializableUntil, Sleb128, Uleb128, Uleb128p1, NO_INDEX, }; use std::io::Write; /// #[derive(Debug, PartialEq, Eq, Clone)] pub struct DebugInfoItem { pub line_start: Uleb128, //pub parameters_size: Uleb128, pub parameter_names: Vec, /// List of opcode. Notice that the trailling [`DbgBytecode::EndSequence`] /// is not stored in this vec. pub bytecode: Vec, } #[derive(Serializable, Debug, PartialEq, Eq, Copy, Clone)] #[prefix_type(u8)] pub enum DbgBytecode { #[prefix(0x00)] EndSequence, #[prefix(0x01)] AdvancePC { addr_diff: Uleb128 }, #[prefix(0x02)] AdvanceLine { line_diff: Sleb128 }, #[prefix(0x03)] StartLocal { register_num: Uleb128, name_idx: Uleb128p1, type_idx: Uleb128p1, }, #[prefix(0x04)] StartLocalExtended { register_num: Uleb128, name_idx: Uleb128p1, type_idx: Uleb128p1, sig_idx: Uleb128p1, }, #[prefix(0x05)] EndLocal { register_num: Uleb128 }, #[prefix(0x06)] RestartLocal { register_num: Uleb128 }, #[prefix(0x07)] SetPrologueEnd, #[prefix(0x08)] SetEpilogueBegin, #[prefix(0x09)] SetFile { name_idx: Uleb128p1 }, #[default_variant] SpecialOpcode(u8), } impl DebugInfoItem { pub fn parameters_size_field(&self) -> Uleb128 { Uleb128(self.parameter_names.len() as u32) } } impl Serializable for DebugInfoItem { fn serialize(&self, output: &mut dyn Write) -> Result<()> { self.line_start.serialize(output)?; self.parameters_size_field().serialize(output)?; for item in &self.parameter_names { item.serialize(output)?; } self.bytecode.serialize(output, DbgBytecode::EndSequence)?; Ok(()) } fn deserialize(input: &mut dyn ReadSeek) -> Result { let line_start = Uleb128::deserialize(input)?; let Uleb128(parameters_size) = Uleb128::deserialize(input)?; let mut parameter_names = vec![]; for _ in 0..parameters_size { parameter_names.push(Uleb128p1::deserialize(input)?); } let bytecode = Vec::::deserialize(input, DbgBytecode::EndSequence)?; Ok(Self { line_start, parameter_names, bytecode, }) } fn size(&self) -> usize { self.line_start.size() + self.parameters_size_field().size() + self .parameter_names .iter() .map(|param| param.size()) .sum::() + self.bytecode.size(DbgBytecode::EndSequence) } } /// The name and type of the variable in a register. #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub struct DebugRegState { pub type_idx: Option, pub name_idx: Option, pub sig_idx: Option, pub in_scope: bool, } /// A simplified debug information #[derive(Debug, PartialEq, Eq, Clone)] pub enum DebugInfo { DefLocal { addr: u32, reg: u32, val: DebugRegState, }, EndLocal { addr: u32, reg: u32, }, PrologueEnd { addr: u32, }, EpilogueBegin { addr: u32, }, SetSourceFile { addr: u32, source_file_idx: Option, }, SetLineNumber { addr: u32, line_num: u32, }, EndOfData, } impl DebugInfo { pub fn get_addr(&self) -> u32 { match self { Self::DefLocal { addr, .. } => *addr, Self::EndLocal { addr, .. } => *addr, Self::PrologueEnd { addr } => *addr, Self::EpilogueBegin { addr } => *addr, Self::SetSourceFile { addr, .. } => *addr, Self::SetLineNumber { addr, .. } => *addr, Self::EndOfData => u32::MAX, // TODO should be an Option::None? } } } /// A state machine that interpret a [`DebugInfoItem`]. #[derive(Debug, PartialEq, Eq, Clone)] pub struct DebugInfoReader { debug_info: DebugInfoItem, pub pc: usize, pub address: u32, pub line: u32, // Those are registers described in the doc but not necessary in the end //pub source_file_idx: Option, //pub prologue_end: bool, //pub epilogue_begin: bool, pub register_states: Vec, } impl DebugInfoReader { pub fn new( debug_info: DebugInfoItem, //source_file_idx: Option, //nb_reg: usize ) -> Self { let line = debug_info.line_start.0; Self { debug_info, pc: 0, address: 0, line, //source_file_idx, //prologue_end: false, //epilogue_begin: false, //register_states: vec![ // DebugRegState { // name_idx: None, // type_idx: None, // sig_idx: None, // in_scope: false, // }; // nb_reg //], register_states: vec![], // In the end, it's easier to grow this on the fly } } pub fn get_ins(&self) -> Result { if self.pc >= self.debug_info.bytecode.len() { return Err(Error::OutOfBound( "Try to read an instruction out of bound, maybe after the end of the debug sequence." .into() )); } Ok(self.debug_info.bytecode[self.pc]) } pub fn tick(&mut self) -> Option { let ins = if let Ok(ins) = self.get_ins() { ins } else { return Some(DebugInfo::EndOfData); }; self.pc += 1; match ins { DbgBytecode::EndSequence => { self.pc = self.debug_info.bytecode.len(); Some(DebugInfo::EndOfData) } DbgBytecode::AdvancePC { addr_diff: Uleb128(addr_diff), } => { self.address += addr_diff; None } DbgBytecode::AdvanceLine { line_diff: Sleb128(line_diff), } => { self.line = (self.line as i32 + line_diff) as u32; None } DbgBytecode::StartLocal { register_num: Uleb128(register_num), name_idx, type_idx, } => { while self.register_states.len() < (register_num + 1) as usize { self.register_states.push(DebugRegState { name_idx: None, type_idx: None, sig_idx: None, in_scope: false, }) } self.register_states[register_num as usize] = DebugRegState { name_idx: if name_idx == NO_INDEX { None } else { Some(name_idx.0) }, type_idx: if type_idx == NO_INDEX { None } else { Some(type_idx.0) }, sig_idx: None, in_scope: true, }; Some(DebugInfo::DefLocal { addr: self.address, reg: register_num, val: self.register_states[register_num as usize], }) } DbgBytecode::StartLocalExtended { register_num: Uleb128(register_num), name_idx, type_idx, sig_idx, } => { while self.register_states.len() < (register_num + 1) as usize { self.register_states.push(DebugRegState { name_idx: None, type_idx: None, sig_idx: None, in_scope: false, }) } self.register_states[register_num as usize] = DebugRegState { name_idx: if name_idx == NO_INDEX { None } else { Some(name_idx.0) }, type_idx: if type_idx == NO_INDEX { None } else { Some(type_idx.0) }, sig_idx: if sig_idx == NO_INDEX { None } else { Some(sig_idx.0) }, in_scope: true, }; Some(DebugInfo::DefLocal { addr: self.address, reg: register_num, val: self.register_states[register_num as usize], }) } DbgBytecode::EndLocal { register_num: Uleb128(register_num), } => { // Yes this can happen while self.register_states.len() < (register_num + 1) as usize { self.register_states.push(DebugRegState { name_idx: None, type_idx: None, sig_idx: None, in_scope: false, }) } self.register_states[register_num as usize].in_scope = false; Some(DebugInfo::EndLocal { addr: self.address, reg: register_num, }) } DbgBytecode::RestartLocal { register_num: Uleb128(register_num), } => { while self.register_states.len() < (register_num + 1) as usize { self.register_states.push(DebugRegState { name_idx: None, type_idx: None, sig_idx: None, in_scope: false, }) } self.register_states[register_num as usize].in_scope = true; Some(DebugInfo::DefLocal { addr: self.address, reg: register_num, val: self.register_states[register_num as usize], }) } DbgBytecode::SetPrologueEnd => { //self.prologue_end = true; Some(DebugInfo::PrologueEnd { addr: self.address }) } DbgBytecode::SetEpilogueBegin => { //self.epilogue_begin = true; Some(DebugInfo::EpilogueBegin { addr: self.address }) } DbgBytecode::SetFile { name_idx: NO_INDEX } => { //self.source_file_idx = None; Some(DebugInfo::SetSourceFile { addr: self.address, source_file_idx: None, }) } DbgBytecode::SetFile { name_idx: Uleb128p1(name_idx), } => { //self.source_file_idx = Some(name_idx); Some(DebugInfo::SetSourceFile { addr: self.address, source_file_idx: Some(name_idx), }) } DbgBytecode::SpecialOpcode(op) => { //if op >= 0x0a { // self.prologue_end = false; // self.epilogue_begin = true; //} // See let adjusted_opcode = op as u32 - 0x0a; self.line = (self.line as i32 + (adjusted_opcode as i32 % 15) - 4) as u32; self.address += adjusted_opcode / 15; Some(DebugInfo::SetLineNumber { addr: self.address, line_num: self.line, }) } } } pub fn next_info(&mut self) -> DebugInfo { loop { if let Some(info) = self.tick() { return info; } } } } /// A state machine that generate a [`DebugInfoItem`]. #[derive(Debug, PartialEq, Eq, Clone)] pub struct DebugInfoBuilder { debug_infos: Vec, line_start: Option, parameter_names: Vec, //pub pc: usize, address: u32, line: u32, // Those are registers described in the doc but not necessary in the end //pub source_file_idx: Option, //pub prologue_end: bool, //pub epilogue_begin: bool, register_states: Vec, finished: bool, } impl DebugInfoBuilder { pub fn new(parameter_names: Vec) -> Self { Self { debug_infos: vec![], line_start: None, parameter_names, //pc: 0, address: 0, line: 0, //source_file_idx, //prologue_end: false, //epilogue_begin: false, //register_states: vec![ // DebugRegState { // name_idx: None, // type_idx: None, // sig_idx: None, // in_scope: false, // }; // nb_reg //], register_states: vec![], // In the end, it's easier to grow this on the fly finished: false, } } pub fn add_info(&mut self, info: &DebugInfo) -> Result<()> { if self.finished { return Err(Error::SerializationError( "Cannot add more information: EndSequence has already been send".into(), )); } match info { DebugInfo::DefLocal { addr, reg, val } => { if *addr < self.address { return Err(Error::SerializationError(format!( "The address register can only increase, \ found 0x{addr:02x} while register is already \ 0x{:02x}", self.address ))); } while self.register_states.len() < (reg + 1) as usize { self.register_states.push(DebugRegState { name_idx: None, type_idx: None, sig_idx: None, in_scope: false, }); } if *addr != self.address { let addr_diff = *addr - self.address; self.debug_infos.push(DbgBytecode::AdvancePC { addr_diff: Uleb128(addr_diff), }); self.address += addr_diff; } let mut old_val = self.register_states[*reg as usize]; let old_val_in_scope = old_val.in_scope; old_val.in_scope = true; if old_val_in_scope && old_val == *val { self.register_states[*reg as usize].in_scope = true; self.debug_infos.push(DbgBytecode::RestartLocal { register_num: Uleb128(*reg), }); } else { self.register_states[*reg as usize] = *val; if val.sig_idx.is_some() { self.debug_infos.push(DbgBytecode::StartLocalExtended { register_num: Uleb128(*reg), name_idx: if let Some(name_idx) = val.name_idx { Uleb128p1(name_idx) } else { NO_INDEX }, type_idx: if let Some(type_idx) = val.type_idx { Uleb128p1(type_idx) } else { NO_INDEX }, sig_idx: if let Some(sig_idx) = val.sig_idx { Uleb128p1(sig_idx) } else { NO_INDEX }, }) } else { self.debug_infos.push(DbgBytecode::StartLocal { register_num: Uleb128(*reg), name_idx: if let Some(name_idx) = val.name_idx { Uleb128p1(name_idx) } else { NO_INDEX }, type_idx: if let Some(type_idx) = val.type_idx { Uleb128p1(type_idx) } else { NO_INDEX }, }) } } Ok(()) } DebugInfo::EndLocal { addr, reg } => { if *addr < self.address { return Err(Error::SerializationError(format!( "The address register can only increase, \ found 0x{addr:02x} while register is already \ 0x{:02x}", self.address ))); } while self.register_states.len() < (reg + 1) as usize { self.register_states.push(DebugRegState { name_idx: None, type_idx: None, sig_idx: None, in_scope: false, }); } if *addr != self.address { let addr_diff = *addr - self.address; self.debug_infos.push(DbgBytecode::AdvancePC { addr_diff: Uleb128(addr_diff), }); self.address += addr_diff; } self.debug_infos.push(DbgBytecode::EndLocal { register_num: Uleb128(*reg), }); Ok(()) } DebugInfo::PrologueEnd { addr } => { if *addr < self.address { return Err(Error::SerializationError(format!( "The address register can only increase, \ found 0x{addr:02x} while register is already \ 0x{:02x}", self.address ))); } if *addr != self.address { let addr_diff = *addr - self.address; self.debug_infos.push(DbgBytecode::AdvancePC { addr_diff: Uleb128(addr_diff), }); self.address += addr_diff; } self.debug_infos.push(DbgBytecode::SetPrologueEnd); Ok(()) } DebugInfo::EpilogueBegin { addr } => { if *addr < self.address { return Err(Error::SerializationError(format!( "The address register can only increase, \ found 0x{addr:02x} while register is already \ 0x{:02x}", self.address ))); } if *addr != self.address { let addr_diff = *addr - self.address; self.debug_infos.push(DbgBytecode::AdvancePC { addr_diff: Uleb128(addr_diff), }); self.address += addr_diff; } self.debug_infos.push(DbgBytecode::SetEpilogueBegin); Ok(()) } DebugInfo::SetLineNumber { addr, line_num } => { if *addr < self.address { return Err(Error::SerializationError(format!( "The address register can only increase, \ found 0x{addr:02x} while register is already \ 0x{:02x}", self.address ))); } if self.line_start.is_none() { self.line_start = Some(*line_num); self.line = *line_num; } let mut line_diff = *line_num as i32 - self.line as i32; let mut addr_diff = addr - self.address; if !(-4..15 - 4).contains(&line_diff) { self.debug_infos.push(DbgBytecode::AdvanceLine { line_diff: Sleb128(line_diff), }); self.line = *line_num; line_diff = 0; } if addr_diff as i32 * 15 + 0x0a + line_diff + 4 > 0xff { self.debug_infos.push(DbgBytecode::AdvancePC { addr_diff: Uleb128(addr_diff), }); self.address = *addr; addr_diff = 0; } let op = 0x0a + addr_diff as u8 * 15 + (line_diff + 4) as u8; self.debug_infos.push(DbgBytecode::SpecialOpcode(op)); self.address += addr_diff; self.line = (self.line as i32 + line_diff) as u32; Ok(()) } DebugInfo::SetSourceFile { addr, source_file_idx, } => { if *addr != self.address { let addr_diff = *addr - self.address; self.debug_infos.push(DbgBytecode::AdvancePC { addr_diff: Uleb128(addr_diff), }); self.address += addr_diff; } self.debug_infos.push(DbgBytecode::SetFile { name_idx: if let Some(source_file_idx) = source_file_idx { Uleb128p1(*source_file_idx) } else { NO_INDEX }, }); Ok(()) } DebugInfo::EndOfData => { self.finished = true; Ok(()) } } } /// If they are no debug information, return None, else compute and return the [`DebugInfoItem`]. pub fn build(self) -> Option { if self.debug_infos.is_empty() && self.parameter_names.iter().all(|&idx| idx == NO_INDEX) { None } else { Some(DebugInfoItem { line_start: Uleb128(self.line_start.unwrap_or(0)), parameter_names: self.parameter_names, bytecode: self.debug_infos, }) } } } #[cfg(test)] mod test { use super::DbgBytecode::*; use super::*; #[test] fn test_debug_reserialize() { let debug = DebugInfoItem { line_start: Uleb128(2902), parameter_names: vec![], bytecode: vec![ SpecialOpcode(14), AdvanceLine { line_diff: Sleb128(-1551), }, AdvancePC { addr_diff: Uleb128(51), }, SpecialOpcode(14), // End a local that do not already exist EndLocal { register_num: Uleb128(41), }, ], }; assert_eq!( debug, DebugInfoItem::deserialize_from_slice(&debug.serialize_to_vec().unwrap()).unwrap() ); } #[test] fn test_advance_line_reserialize() { let advance_line = AdvanceLine { line_diff: Sleb128(-1551), }; assert_eq!( advance_line, DbgBytecode::deserialize_from_slice(&advance_line.serialize_to_vec().unwrap()).unwrap() ); } #[test] fn test_get_expl_debug() { const RAW_DEBUG: [u8; 10] = [23, 0, 14, 135, 3, 0, 16, 2, 150, 0]; let debug = DebugInfoItem::deserialize_from_slice(&RAW_DEBUG).unwrap(); let mut reader = DebugInfoReader::new(debug.clone()); let mut list_info = vec![]; loop { list_info.push(reader.next_info()); if list_info.last() == Some(&DebugInfo::EndOfData) { break; } } let mut builder = DebugInfoBuilder::new(debug.parameter_names.clone()); for info in list_info { builder.add_info(&info).unwrap(); } let debug_computed = builder.build().unwrap(); assert_eq!( &RAW_DEBUG, &(debug_computed.serialize_to_vec().unwrap()).as_slice() ); } }