mirror of
https://github.com/encounter/objdiff.git
synced 2025-06-07 15:13:47 +00:00
Implement context menu copy functionality for data values (#163)
* Implement context menu copy functionality for data values * Clippy fixes
This commit is contained in:
parent
6b7dcabbed
commit
674c942d7d
@ -34,11 +34,36 @@ pub enum DataType {
|
|||||||
String,
|
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 {
|
impl DataType {
|
||||||
pub fn display_bytes<Endian: ByteOrder>(&self, bytes: &[u8]) -> Option<String> {
|
pub fn display_labels<Endian: ByteOrder>(&self, bytes: &[u8]) -> Vec<String> {
|
||||||
|
let mut strs = Vec::new();
|
||||||
|
for literal in self.display_literals::<Endian>(bytes) {
|
||||||
|
strs.push(format!("{}: {}", self, literal))
|
||||||
|
}
|
||||||
|
strs
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn display_literals<Endian: ByteOrder>(&self, bytes: &[u8]) -> Vec<String> {
|
||||||
|
let mut strs = Vec::new();
|
||||||
if self.required_len().is_some_and(|l| bytes.len() < l) {
|
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.");
|
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;
|
let mut bytes = bytes;
|
||||||
if self.required_len().is_some_and(|l| bytes.len() > l) {
|
if self.required_len().is_some_and(|l| bytes.len() > l) {
|
||||||
@ -56,58 +81,61 @@ impl DataType {
|
|||||||
match self {
|
match self {
|
||||||
DataType::Int8 => {
|
DataType::Int8 => {
|
||||||
let i = i8::from_ne_bytes(bytes.try_into().unwrap());
|
let i = i8::from_ne_bytes(bytes.try_into().unwrap());
|
||||||
|
strs.push(format!("{:#x}", i));
|
||||||
|
|
||||||
if i < 0 {
|
if i < 0 {
|
||||||
format!("Int8: {:#x} ({:#x})", i, ReallySigned(i))
|
strs.push(format!("{:#x}", ReallySigned(i)));
|
||||||
} else {
|
|
||||||
format!("Int8: {:#x}", i)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType::Int16 => {
|
DataType::Int16 => {
|
||||||
let i = Endian::read_i16(bytes);
|
let i = Endian::read_i16(bytes);
|
||||||
|
strs.push(format!("{:#x}", i));
|
||||||
|
|
||||||
if i < 0 {
|
if i < 0 {
|
||||||
format!("Int16: {:#x} ({:#x})", i, ReallySigned(i))
|
strs.push(format!("{:#x}", ReallySigned(i)));
|
||||||
} else {
|
|
||||||
format!("Int16: {:#x}", i)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType::Int32 => {
|
DataType::Int32 => {
|
||||||
let i = Endian::read_i32(bytes);
|
let i = Endian::read_i32(bytes);
|
||||||
|
strs.push(format!("{:#x}", i));
|
||||||
|
|
||||||
if i < 0 {
|
if i < 0 {
|
||||||
format!("Int32: {:#x} ({:#x})", i, ReallySigned(i))
|
strs.push(format!("{:#x}", ReallySigned(i)));
|
||||||
} else {
|
|
||||||
format!("Int32: {:#x}", i)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType::Int64 => {
|
DataType::Int64 => {
|
||||||
let i = Endian::read_i64(bytes);
|
let i = Endian::read_i64(bytes);
|
||||||
|
strs.push(format!("{:#x}", i));
|
||||||
|
|
||||||
if i < 0 {
|
if i < 0 {
|
||||||
format!("Int64: {:#x} ({:#x})", i, ReallySigned(i))
|
strs.push(format!("{:#x}", ReallySigned(i)));
|
||||||
} else {
|
|
||||||
format!("Int64: {:#x}", i)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType::Int128 => {
|
DataType::Int128 => {
|
||||||
let i = Endian::read_i128(bytes);
|
let i = Endian::read_i128(bytes);
|
||||||
|
strs.push(format!("{:#x}", i));
|
||||||
|
|
||||||
if i < 0 {
|
if i < 0 {
|
||||||
format!("Int128: {:#x} ({:#x})", i, ReallySigned(i))
|
strs.push(format!("{:#x}", ReallySigned(i)));
|
||||||
} else {
|
|
||||||
format!("Int128: {:#x}", i)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType::Float => {
|
DataType::Float => {
|
||||||
format!("Float: {:?}f", Endian::read_f32(bytes))
|
strs.push(format!("{:?}f", Endian::read_f32(bytes)));
|
||||||
}
|
}
|
||||||
DataType::Double => {
|
DataType::Double => {
|
||||||
format!("Double: {:?}", Endian::read_f64(bytes))
|
strs.push(format!("{:?}", Endian::read_f64(bytes)));
|
||||||
}
|
}
|
||||||
DataType::Bytes => {
|
DataType::Bytes => {
|
||||||
format!("Bytes: {:#?}", bytes)
|
strs.push(format!("{:#?}", bytes));
|
||||||
}
|
}
|
||||||
DataType::String => {
|
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<usize> {
|
fn required_len(&self) -> Option<usize> {
|
||||||
@ -154,19 +182,42 @@ pub trait ObjArch: Send + Sync {
|
|||||||
|
|
||||||
fn guess_data_type(&self, _instruction: &ObjIns) -> Option<DataType> { None }
|
fn guess_data_type(&self, _instruction: &ObjIns) -> Option<DataType> { None }
|
||||||
|
|
||||||
fn display_data_type(&self, _ty: DataType, bytes: &[u8]) -> Option<String> {
|
fn display_data_labels(&self, _ty: DataType, bytes: &[u8]) -> Vec<String> {
|
||||||
Some(format!("Bytes: {:#x?}", bytes))
|
vec![format!("Bytes: {:#x?}", bytes)]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn display_ins_data(&self, ins: &ObjIns) -> Option<String> {
|
fn display_data_literals(&self, _ty: DataType, bytes: &[u8]) -> Vec<String> {
|
||||||
let reloc = ins.reloc.as_ref()?;
|
vec![format!("{:#?}", bytes)]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn display_ins_data_labels(&self, ins: &ObjIns) -> Vec<String> {
|
||||||
|
let Some(reloc) = ins.reloc.as_ref() else {
|
||||||
|
return Vec::new();
|
||||||
|
};
|
||||||
if reloc.addend >= 0 && reloc.target.bytes.len() > reloc.addend as usize {
|
if reloc.addend >= 0 && reloc.target.bytes.len() > reloc.addend as usize {
|
||||||
self.guess_data_type(ins).and_then(|ty| {
|
return self
|
||||||
self.display_data_type(ty, &reloc.target.bytes[reloc.addend as usize..])
|
.guess_data_type(ins)
|
||||||
})
|
.map(|ty| {
|
||||||
} else {
|
self.display_data_labels(ty, &reloc.target.bytes[reloc.addend as usize..])
|
||||||
None
|
})
|
||||||
|
.unwrap_or_default();
|
||||||
}
|
}
|
||||||
|
Vec::new()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn display_ins_data_literals(&self, ins: &ObjIns) -> Vec<String> {
|
||||||
|
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
|
// Downcast methods
|
||||||
|
@ -221,8 +221,12 @@ impl ObjArch for ObjArchPpc {
|
|||||||
guess_data_type_from_load_store_inst_op(Opcode::from(instruction.op as u8))
|
guess_data_type_from_load_store_inst_op(Opcode::from(instruction.op as u8))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn display_data_type(&self, ty: DataType, bytes: &[u8]) -> Option<String> {
|
fn display_data_labels(&self, ty: DataType, bytes: &[u8]) -> Vec<String> {
|
||||||
ty.display_bytes::<BigEndian>(bytes)
|
ty.display_labels::<BigEndian>(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn display_data_literals(&self, ty: DataType, bytes: &[u8]) -> Vec<String> {
|
||||||
|
ty.display_literals::<BigEndian>(bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ppc(&self) -> Option<&ObjArchPpc> { Some(self) }
|
fn ppc(&self) -> Option<&ObjArchPpc> { Some(self) }
|
||||||
|
@ -245,8 +245,8 @@ fn reloc_eq(
|
|||||||
|| address_eq(left, right))
|
|| address_eq(left, right))
|
||||||
&& (config.function_reloc_diffs == FunctionRelocDiffs::NameAddress
|
&& (config.function_reloc_diffs == FunctionRelocDiffs::NameAddress
|
||||||
|| left.target.kind != ObjSymbolKind::Object
|
|| left.target.kind != ObjSymbolKind::Object
|
||||||
|| left_obj.arch.display_ins_data(left_ins)
|
|| left_obj.arch.display_ins_data_labels(left_ins)
|
||||||
== left_obj.arch.display_ins_data(right_ins))
|
== left_obj.arch.display_ins_data_labels(right_ins))
|
||||||
}
|
}
|
||||||
(Some(_), None) => false,
|
(Some(_), None) => false,
|
||||||
(None, Some(_)) => {
|
(None, Some(_)) => {
|
||||||
|
@ -149,8 +149,8 @@ fn ins_hover_ui(
|
|||||||
appearance.highlight_color,
|
appearance.highlight_color,
|
||||||
format!("Size: {:x}", reloc.target.size),
|
format!("Size: {:x}", reloc.target.size),
|
||||||
);
|
);
|
||||||
if let Some(s) = obj.arch.display_ins_data(ins) {
|
for label in obj.arch.display_ins_data_labels(ins) {
|
||||||
ui.colored_label(appearance.highlight_color, s);
|
ui.colored_label(appearance.highlight_color, label);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ui.colored_label(appearance.highlight_color, "Extern".to_string());
|
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.scope(|ui| {
|
||||||
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
|
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
|
||||||
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
|
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 {
|
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 let Some(name) = &reloc.target.demangled_name {
|
||||||
if ui.button(format!("Copy \"{name}\"")).clicked() {
|
if ui.button(format!("Copy \"{name}\"")).clicked() {
|
||||||
ui.output_mut(|output| output.copied_text.clone_from(name));
|
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 ins_diff = &ctx.diff.symbol_diff(symbol_ref).instructions[row.index()];
|
||||||
let response_cb = |response: Response| {
|
let response_cb = |response: Response| {
|
||||||
if let Some(ins) = &ins_diff.ins {
|
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| {
|
response.on_hover_ui_at_pointer(|ui| {
|
||||||
ins_hover_ui(ui, ctx.obj, section, ins, symbol, appearance)
|
ins_hover_ui(ui, ctx.obj, section, ins, symbol, appearance)
|
||||||
})
|
})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user