diff --git a/objdiff-core/src/arch/mod.rs b/objdiff-core/src/arch/mod.rs index 829f027..515ee3f 100644 --- a/objdiff-core/src/arch/mod.rs +++ b/objdiff-core/src/arch/mod.rs @@ -34,11 +34,36 @@ pub enum DataType { String, } +impl std::fmt::Display for DataType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + DataType::Int8 => write!(f, "Int8"), + DataType::Int16 => write!(f, "Int16"), + DataType::Int32 => write!(f, "Int32"), + DataType::Int64 => write!(f, "Int64"), + DataType::Int128 => write!(f, "Int128"), + DataType::Float => write!(f, "Float"), + DataType::Double => write!(f, "Double"), + DataType::Bytes => write!(f, "Bytes"), + DataType::String => write!(f, "String"), + } + } +} + impl DataType { - pub fn display_bytes(&self, bytes: &[u8]) -> Option { + pub fn display_labels(&self, bytes: &[u8]) -> Vec { + let mut strs = Vec::new(); + for literal in self.display_literals::(bytes) { + strs.push(format!("{}: {}", self, literal)) + } + strs + } + + pub fn display_literals(&self, bytes: &[u8]) -> Vec { + let mut strs = Vec::new(); if self.required_len().is_some_and(|l| bytes.len() < l) { log::warn!("Failed to display a symbol value for a symbol whose size is too small for instruction referencing it."); - return None; + return strs; } let mut bytes = bytes; if self.required_len().is_some_and(|l| bytes.len() > l) { @@ -56,58 +81,61 @@ impl DataType { match self { DataType::Int8 => { let i = i8::from_ne_bytes(bytes.try_into().unwrap()); + strs.push(format!("{:#x}", i)); + if i < 0 { - format!("Int8: {:#x} ({:#x})", i, ReallySigned(i)) - } else { - format!("Int8: {:#x}", i) + strs.push(format!("{:#x}", ReallySigned(i))); } } DataType::Int16 => { let i = Endian::read_i16(bytes); + strs.push(format!("{:#x}", i)); + if i < 0 { - format!("Int16: {:#x} ({:#x})", i, ReallySigned(i)) - } else { - format!("Int16: {:#x}", i) + strs.push(format!("{:#x}", ReallySigned(i))); } } DataType::Int32 => { let i = Endian::read_i32(bytes); + strs.push(format!("{:#x}", i)); + if i < 0 { - format!("Int32: {:#x} ({:#x})", i, ReallySigned(i)) - } else { - format!("Int32: {:#x}", i) + strs.push(format!("{:#x}", ReallySigned(i))); } } DataType::Int64 => { let i = Endian::read_i64(bytes); + strs.push(format!("{:#x}", i)); + if i < 0 { - format!("Int64: {:#x} ({:#x})", i, ReallySigned(i)) - } else { - format!("Int64: {:#x}", i) + strs.push(format!("{:#x}", ReallySigned(i))); } } DataType::Int128 => { let i = Endian::read_i128(bytes); + strs.push(format!("{:#x}", i)); + if i < 0 { - format!("Int128: {:#x} ({:#x})", i, ReallySigned(i)) - } else { - format!("Int128: {:#x}", i) + strs.push(format!("{:#x}", ReallySigned(i))); } } DataType::Float => { - format!("Float: {:?}f", Endian::read_f32(bytes)) + strs.push(format!("{:?}f", Endian::read_f32(bytes))); } DataType::Double => { - format!("Double: {:?}", Endian::read_f64(bytes)) + strs.push(format!("{:?}", Endian::read_f64(bytes))); } DataType::Bytes => { - format!("Bytes: {:#?}", bytes) + strs.push(format!("{:#?}", bytes)); } DataType::String => { - format!("String: {:?}", CStr::from_bytes_until_nul(bytes).ok()?) + if let Ok(cstr) = CStr::from_bytes_until_nul(bytes) { + strs.push(format!("{:?}", cstr)); + } } } - .into() + + strs } fn required_len(&self) -> Option { @@ -154,19 +182,42 @@ pub trait ObjArch: Send + Sync { fn guess_data_type(&self, _instruction: &ObjIns) -> Option { None } - fn display_data_type(&self, _ty: DataType, bytes: &[u8]) -> Option { - Some(format!("Bytes: {:#x?}", bytes)) + fn display_data_labels(&self, _ty: DataType, bytes: &[u8]) -> Vec { + vec![format!("Bytes: {:#x?}", bytes)] } - fn display_ins_data(&self, ins: &ObjIns) -> Option { - let reloc = ins.reloc.as_ref()?; + fn display_data_literals(&self, _ty: DataType, bytes: &[u8]) -> Vec { + vec![format!("{:#?}", bytes)] + } + + fn display_ins_data_labels(&self, ins: &ObjIns) -> Vec { + let Some(reloc) = ins.reloc.as_ref() else { + return Vec::new(); + }; if reloc.addend >= 0 && reloc.target.bytes.len() > reloc.addend as usize { - self.guess_data_type(ins).and_then(|ty| { - self.display_data_type(ty, &reloc.target.bytes[reloc.addend as usize..]) - }) - } else { - None + return self + .guess_data_type(ins) + .map(|ty| { + self.display_data_labels(ty, &reloc.target.bytes[reloc.addend as usize..]) + }) + .unwrap_or_default(); } + Vec::new() + } + + fn display_ins_data_literals(&self, ins: &ObjIns) -> Vec { + let Some(reloc) = ins.reloc.as_ref() else { + return Vec::new(); + }; + if reloc.addend >= 0 && reloc.target.bytes.len() > reloc.addend as usize { + return self + .guess_data_type(ins) + .map(|ty| { + self.display_data_literals(ty, &reloc.target.bytes[reloc.addend as usize..]) + }) + .unwrap_or_default(); + } + Vec::new() } // Downcast methods diff --git a/objdiff-core/src/arch/ppc.rs b/objdiff-core/src/arch/ppc.rs index 7e0052a..caa09c0 100644 --- a/objdiff-core/src/arch/ppc.rs +++ b/objdiff-core/src/arch/ppc.rs @@ -221,8 +221,12 @@ impl ObjArch for ObjArchPpc { guess_data_type_from_load_store_inst_op(Opcode::from(instruction.op as u8)) } - fn display_data_type(&self, ty: DataType, bytes: &[u8]) -> Option { - ty.display_bytes::(bytes) + fn display_data_labels(&self, ty: DataType, bytes: &[u8]) -> Vec { + ty.display_labels::(bytes) + } + + fn display_data_literals(&self, ty: DataType, bytes: &[u8]) -> Vec { + ty.display_literals::(bytes) } fn ppc(&self) -> Option<&ObjArchPpc> { Some(self) } diff --git a/objdiff-core/src/diff/code.rs b/objdiff-core/src/diff/code.rs index 54784a5..62e6caf 100644 --- a/objdiff-core/src/diff/code.rs +++ b/objdiff-core/src/diff/code.rs @@ -245,8 +245,8 @@ fn reloc_eq( || address_eq(left, right)) && (config.function_reloc_diffs == FunctionRelocDiffs::NameAddress || left.target.kind != ObjSymbolKind::Object - || left_obj.arch.display_ins_data(left_ins) - == left_obj.arch.display_ins_data(right_ins)) + || left_obj.arch.display_ins_data_labels(left_ins) + == left_obj.arch.display_ins_data_labels(right_ins)) } (Some(_), None) => false, (None, Some(_)) => { diff --git a/objdiff-gui/src/views/function_diff.rs b/objdiff-gui/src/views/function_diff.rs index b2d3bbd..b07e22c 100644 --- a/objdiff-gui/src/views/function_diff.rs +++ b/objdiff-gui/src/views/function_diff.rs @@ -149,8 +149,8 @@ fn ins_hover_ui( appearance.highlight_color, format!("Size: {:x}", reloc.target.size), ); - if let Some(s) = obj.arch.display_ins_data(ins) { - ui.colored_label(appearance.highlight_color, s); + for label in obj.arch.display_ins_data_labels(ins) { + ui.colored_label(appearance.highlight_color, label); } } else { ui.colored_label(appearance.highlight_color, "Extern".to_string()); @@ -163,7 +163,13 @@ fn ins_hover_ui( }); } -fn ins_context_menu(ui: &mut egui::Ui, section: &ObjSection, ins: &ObjIns, symbol: &ObjSymbol) { +fn ins_context_menu( + ui: &mut egui::Ui, + obj: &ObjInfo, + section: &ObjSection, + ins: &ObjIns, + symbol: &ObjSymbol, +) { ui.scope(|ui| { ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace); ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend); @@ -219,6 +225,12 @@ fn ins_context_menu(ui: &mut egui::Ui, section: &ObjSection, ins: &ObjIns, symbo } } if let Some(reloc) = &ins.reloc { + for literal in obj.arch.display_ins_data_literals(ins) { + if ui.button(format!("Copy \"{literal}\"")).clicked() { + ui.output_mut(|output| output.copied_text.clone_from(&literal)); + ui.close_menu(); + } + } if let Some(name) = &reloc.target.demangled_name { if ui.button(format!("Copy \"{name}\"")).clicked() { ui.output_mut(|output| output.copied_text.clone_from(name)); @@ -390,7 +402,7 @@ fn asm_col_ui( let ins_diff = &ctx.diff.symbol_diff(symbol_ref).instructions[row.index()]; let response_cb = |response: Response| { if let Some(ins) = &ins_diff.ins { - response.context_menu(|ui| ins_context_menu(ui, section, ins, symbol)); + response.context_menu(|ui| ins_context_menu(ui, ctx.obj, section, ins, symbol)); response.on_hover_ui_at_pointer(|ui| { ins_hover_ui(ui, ctx.obj, section, ins, symbol, appearance) })