diff --git a/objdiff-core/src/arch/superh/mod.rs b/objdiff-core/src/arch/superh/mod.rs index 5f9f852..be00d73 100644 --- a/objdiff-core/src/arch/superh/mod.rs +++ b/objdiff-core/src/arch/superh/mod.rs @@ -1,4 +1,5 @@ use alloc::{string::String, vec::Vec}; +use std::collections::HashMap; use anyhow::{Result, bail}; use object::elf; @@ -20,6 +21,11 @@ impl ArchSuperH { pub fn new(_file: &object::File) -> Result { Ok(Self {}) } } +struct DataInfo { + address: u64, + size: u32, +} + impl Arch for ArchSuperH { fn scan_instructions( &self, @@ -31,6 +37,7 @@ impl Arch for ArchSuperH { ) -> Result> { let mut ops = Vec::::with_capacity(code.len() / 2); let mut offset = address; + for chunk in code.chunks_exact(2) { let opcode = u16::from_be_bytes(chunk.try_into().unwrap()); let mut parts: Vec = vec![]; @@ -67,8 +74,60 @@ impl Arch for ArchSuperH { let opcode = u16::from_be_bytes(resolved.code.try_into().unwrap()); let mut parts: Vec = vec![]; let mut branch_dest: Option = None; + sh2_disasm(0, opcode, true, &mut parts, &resolved, &mut branch_dest); + if let Some(symbol_data) = resolved.section.symbol_data(resolved.symbol) { + // scan for data + // map of instruction offsets to data target offsets + let mut data_offsets: HashMap = HashMap::::new(); + + let mut pos: u64 = 0; + for chunk in symbol_data.chunks_exact(2) { + let opcode = u16::from_be_bytes(chunk.try_into().unwrap()); + // mov.w + if (opcode & 0xf000) == 0x9000 { + let target = (opcode as u64 & 0xff) * 2 + 4 + pos; + let data_info = DataInfo { address: target, size: 2 }; + data_offsets.insert(pos, data_info); + } + // mov.l + else if (opcode & 0xf000) == 0xd000 { + let target = ((opcode as u64 & 0xff) * 4 + 4 + pos) & 0xfffffffc; + let data_info = DataInfo { address: target, size: 4 }; + data_offsets.insert(pos, data_info); + } + pos += 2; + } + + let pos = resolved.ins_ref.address - resolved.symbol.address; + + // add the data info + if let Some(value) = data_offsets.get(&pos) { + if value.size == 2 && value.address as usize + 1 < symbol_data.len() { + let data = u16::from_be_bytes( + symbol_data[value.address as usize..value.address as usize + 2] + .try_into() + .unwrap(), + ); + parts.push(InstructionPart::basic(" /* ")); + parts.push(InstructionPart::basic("0x")); + parts.push(InstructionPart::basic(format!("{:04X}", data))); + parts.push(InstructionPart::basic(" */")); + } else if value.size == 4 && value.address as usize + 3 < symbol_data.len() { + let data = u32::from_be_bytes( + symbol_data[value.address as usize..value.address as usize + 4] + .try_into() + .unwrap(), + ); + parts.push(InstructionPart::basic(" /* ")); + parts.push(InstructionPart::basic("0x")); + parts.push(InstructionPart::basic(format!("{:08X}", data))); + parts.push(InstructionPart::basic(" */")); + } + } + } + for part in parts { cb(part)?; } @@ -153,7 +212,7 @@ mod test { use std::fmt::{self, Display}; use super::*; - use crate::obj::InstructionArg; + use crate::obj::{InstructionArg, Section, SectionData, Symbol}; impl Display for InstructionPart<'_> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -634,4 +693,92 @@ mod test { assert_eq!(joined_str, expected_str.to_string()); } } + + #[test] + fn test_func_0606_f378_mov_w_data_labeling() { + let arch = ArchSuperH {}; + let ops: &[(u16, u32, &str)] = &[(0x9000, 0x0606F378, "mov.w @(0x4, pc), r0 /* 0x00B0 */")]; + + let mut code = Vec::new(); + code.extend_from_slice(&0x9000_u16.to_be_bytes()); + code.extend_from_slice(&0x0009_u16.to_be_bytes()); + code.extend_from_slice(&0x00B0_u16.to_be_bytes()); + + for &(opcode, addr, expected_str) in ops { + let mut parts = Vec::new(); + + arch.display_instruction( + ResolvedInstructionRef { + ins_ref: InstructionRef { address: addr as u64, size: 2, opcode }, + code: &opcode.to_be_bytes(), + symbol: &Symbol { + address: 0x0606F378, // func base address + size: code.len() as u64, + ..Default::default() + }, + section: &Section { + address: 0x0606F378, + size: code.len() as u64, + data: SectionData(code.clone()), + ..Default::default() + }, + ..Default::default() + }, + &DiffObjConfig::default(), + &mut |part| { + parts.push(part.into_static()); + Ok(()) + }, + ) + .unwrap(); + + let joined_str: String = parts.iter().map(|part| format!("{}", part)).collect(); + assert_eq!(joined_str, expected_str.to_string()); + } + } + + #[test] + fn test_func_0606_f378_mov_l_data_labeling() { + let arch = ArchSuperH {}; + let ops: &[(u16, u32, &str)] = + &[(0xd000, 0x0606F378, "mov.l @(0x4, pc), r0 /* 0x00B000B0 */")]; + + let mut code = Vec::new(); + code.extend_from_slice(&0xd000_u16.to_be_bytes()); + code.extend_from_slice(&0x0009_u16.to_be_bytes()); + code.extend_from_slice(&0x00B0_u16.to_be_bytes()); + code.extend_from_slice(&0x00B0_u16.to_be_bytes()); + + for &(opcode, addr, expected_str) in ops { + let mut parts = Vec::new(); + + arch.display_instruction( + ResolvedInstructionRef { + ins_ref: InstructionRef { address: addr as u64, size: 2, opcode }, + code: &opcode.to_be_bytes(), + symbol: &Symbol { + address: 0x0606F378, // func base address + size: code.len() as u64, + ..Default::default() + }, + section: &Section { + address: 0x0606F378, + size: code.len() as u64, + data: SectionData(code.clone()), + ..Default::default() + }, + ..Default::default() + }, + &DiffObjConfig::default(), + &mut |part| { + parts.push(part.into_static()); + Ok(()) + }, + ) + .unwrap(); + + let joined_str: String = parts.iter().map(|part| format!("{}", part)).collect(); + assert_eq!(joined_str, expected_str.to_string()); + } + } } diff --git a/objdiff-core/src/obj/mod.rs b/objdiff-core/src/obj/mod.rs index 02a9e02..46d6cc7 100644 --- a/objdiff-core/src/obj/mod.rs +++ b/objdiff-core/src/obj/mod.rs @@ -109,6 +109,11 @@ impl Section { } } + pub fn symbol_data(&self, symbol: &Symbol) -> Option<&[u8]> { + let offset = symbol.address.checked_sub(self.address)?; + self.data.get(offset as usize..offset as usize + symbol.size as usize) + } + // The alignment to use when "Combine data/text sections" is enabled. pub fn combined_alignment(&self) -> u64 { const MIN_ALIGNMENT: u64 = 4;