Instruction hover / context menu improvements

This commit is contained in:
Luke Street 2024-05-20 17:38:20 -06:00
parent 8b36fa4fc6
commit 5bfaaaaf65
10 changed files with 68 additions and 25 deletions

View File

@ -476,7 +476,7 @@ fn read_report(path: &Path) -> Result<Report> {
fn serialize_hex<S>(x: &Option<u64>, s: S) -> Result<S::Ok, S::Error> fn serialize_hex<S>(x: &Option<u64>, s: S) -> Result<S::Ok, S::Error>
where S: serde::Serializer { where S: serde::Serializer {
if let Some(x) = x { if let Some(x) = x {
s.serialize_str(&format!("{:#X}", x)) s.serialize_str(&format!("{:#x}", x))
} else { } else {
s.serialize_none() s.serialize_none()
} }

View File

@ -49,6 +49,7 @@ impl ObjArch for ObjArchMips {
let code = self.endianness.read_u32_bytes(chunk.try_into()?); let code = self.endianness.read_u32_bytes(chunk.try_into()?);
let instruction = Instruction::new(code, cur_addr, InstrCategory::CPU); let instruction = Instruction::new(code, cur_addr, InstrCategory::CPU);
let formatted = instruction.disassemble(None, 0);
let op = instruction.unique_id as u16; let op = instruction.unique_id as u16;
ops.push(op); ops.push(op);
@ -123,6 +124,7 @@ impl ObjArch for ObjArchMips {
reloc: reloc.cloned(), reloc: reloc.cloned(),
branch_dest, branch_dest,
line, line,
formatted,
orig: None, orig: None,
}); });
cur_addr += 4; cur_addr += 4;

View File

@ -59,6 +59,7 @@ impl ObjArch for ObjArchPpc {
let orig = ins.basic().to_string(); let orig = ins.basic().to_string();
let simplified = ins.simplified(); let simplified = ins.simplified();
let formatted = simplified.to_string();
let mut reloc_arg = None; let mut reloc_arg = None;
if let Some(reloc) = reloc { if let Some(reloc) = reloc {
@ -144,6 +145,7 @@ impl ObjArch for ObjArchPpc {
op: ins.op as u16, op: ins.op as u16,
branch_dest, branch_dest,
line, line,
formatted,
orig: Some(orig), orig: Some(orig),
}); });
} }

View File

@ -57,6 +57,7 @@ impl ObjArch for ObjArchX86 {
reloc: None, reloc: None,
branch_dest: None, branch_dest: None,
line: None, line: None,
formatted: String::new(),
orig: None, orig: None,
}, },
error: None, error: None,
@ -81,6 +82,7 @@ impl ObjArch for ObjArchX86 {
reloc: reloc.cloned(), reloc: reloc.cloned(),
branch_dest: None, branch_dest: None,
line: obj.line_info.as_ref().and_then(|m| m.get(&address).cloned()), line: obj.line_info.as_ref().and_then(|m| m.get(&address).cloned()),
formatted: String::new(),
orig: None, orig: None,
}; };
// Run the formatter, which will populate output.ins // Run the formatter, which will populate output.ins
@ -89,7 +91,7 @@ impl ObjArch for ObjArchX86 {
return Err(error); return Err(error);
} }
ensure!(output.ins_operands.len() == output.ins.args.len()); ensure!(output.ins_operands.len() == output.ins.args.len());
output.ins.orig = Some(output.formatted.clone()); output.ins.formatted.clone_from(&output.formatted);
// Make sure we've put the relocation somewhere in the instruction // Make sure we've put the relocation somewhere in the instruction
if reloc.is_some() && !output.ins.args.iter().any(|a| matches!(a, ObjInsArg::Reloc)) { if reloc.is_some() && !output.ins.args.iter().any(|a| matches!(a, ObjInsArg::Reloc)) {

View File

@ -5,7 +5,7 @@ use crate::{
obj::{ObjInsArg, ObjInsArgValue, ObjReloc, ObjSymbol}, obj::{ObjInsArg, ObjInsArgValue, ObjReloc, ObjSymbol},
}; };
#[derive(Debug, Clone)] #[derive(Debug, Copy, Clone)]
pub enum DiffText<'a> { pub enum DiffText<'a> {
/// Basic text /// Basic text
Basic(&'a str), Basic(&'a str),
@ -95,8 +95,8 @@ fn display_reloc_name<E>(
) -> Result<(), E> { ) -> Result<(), E> {
cb(DiffText::Symbol(&reloc.target))?; cb(DiffText::Symbol(&reloc.target))?;
match reloc.target.addend.cmp(&0i64) { match reloc.target.addend.cmp(&0i64) {
Ordering::Greater => cb(DiffText::Basic(&format!("+{:#X}", reloc.target.addend))), Ordering::Greater => cb(DiffText::Basic(&format!("+{:#x}", reloc.target.addend))),
Ordering::Less => cb(DiffText::Basic(&format!("-{:#X}", -reloc.target.addend))), Ordering::Less => cb(DiffText::Basic(&format!("-{:#x}", -reloc.target.addend))),
_ => Ok(()), _ => Ok(()),
} }
} }

View File

@ -101,6 +101,8 @@ pub struct ObjIns {
pub branch_dest: Option<u64>, pub branch_dest: Option<u64>,
/// Line number /// Line number
pub line: Option<u64>, pub line: Option<u64>,
/// Formatted instruction
pub formatted: String,
/// Original (unsimplified) instruction /// Original (unsimplified) instruction
pub orig: Option<String>, pub orig: Option<String>,
} }

View File

@ -252,7 +252,7 @@ fn relocations_by_section(
} else { } else {
reloc.addend() reloc.addend()
}; };
// println!("Reloc: {reloc:?}, symbol: {symbol:?}, addend: {addend:#X}"); // println!("Reloc: {reloc:?}, symbol: {symbol:?}, addend: {addend:#x}");
let target = match symbol.kind() { let target = match symbol.kind() {
SymbolKind::Text | SymbolKind::Data | SymbolKind::Label | SymbolKind::Unknown => { SymbolKind::Text | SymbolKind::Data | SymbolKind::Label | SymbolKind::Unknown => {
to_obj_symbol(arch, obj_file, &symbol, addend, split_meta) to_obj_symbol(arch, obj_file, &symbol, addend, split_meta)

View File

@ -26,7 +26,7 @@ fn data_row_ui(ui: &mut egui::Ui, address: usize, diffs: &[ObjDataDiff], appeara
} }
let mut job = LayoutJob::default(); let mut job = LayoutJob::default();
write_text( write_text(
format!("{address:08X}: ").as_str(), format!("{address:08x}: ").as_str(),
appearance.text_color, appearance.text_color,
&mut job, &mut job,
appearance.code_font.clone(), appearance.code_font.clone(),
@ -47,7 +47,7 @@ fn data_row_ui(ui: &mut egui::Ui, address: usize, diffs: &[ObjDataDiff], appeara
} else { } else {
let mut text = String::new(); let mut text = String::new();
for byte in &diff.data { for byte in &diff.data {
text.push_str(format!("{byte:02X} ").as_str()); text.push_str(format!("{byte:02x} ").as_str());
cur_addr += 1; cur_addr += 1;
if cur_addr % 8 == 0 { if cur_addr % 8 == 0 {
text.push(' '); text.push(' ');

View File

@ -1,6 +1,6 @@
use std::default::Default; use std::default::Default;
use egui::{text::LayoutJob, Align, Label, Layout, Sense, Vec2, Widget}; use egui::{text::LayoutJob, Align, Label, Layout, Response, Sense, Vec2, Widget};
use egui_extras::{Column, TableBuilder, TableRow}; use egui_extras::{Column, TableBuilder, TableRow};
use objdiff_core::{ use objdiff_core::{
arch::ObjArch, arch::ObjArch,
@ -27,6 +27,7 @@ fn ins_hover_ui(
arch: &dyn ObjArch, arch: &dyn ObjArch,
section: &ObjSection, section: &ObjSection,
ins: &ObjIns, ins: &ObjIns,
symbol: &ObjSymbol,
appearance: &Appearance, appearance: &Appearance,
) { ) {
ui.scope(|ui| { ui.scope(|ui| {
@ -35,10 +36,18 @@ fn ins_hover_ui(
let offset = ins.address - section.address; let offset = ins.address - section.address;
ui.label(format!( ui.label(format!(
"{:02X?}", "{:02x?}",
&section.data[offset as usize..(offset + ins.size as u64) as usize] &section.data[offset as usize..(offset + ins.size as u64) as usize]
)); ));
if let Some(virtual_address) = symbol.virtual_address {
let offset = ins.address - symbol.address;
ui.colored_label(
appearance.replace_color,
format!("Virtual address: {:#x}", virtual_address + offset),
);
}
if let Some(orig) = &ins.orig { if let Some(orig) = &ins.orig {
ui.label(format!("Original: {}", orig)); ui.label(format!("Original: {}", orig));
} }
@ -77,12 +86,33 @@ fn ins_hover_ui(
}); });
} }
fn ins_context_menu(ui: &mut egui::Ui, ins: &ObjIns) { fn ins_context_menu(ui: &mut egui::Ui, 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 = Some(false); ui.style_mut().wrap = Some(false);
// if ui.button("Copy hex").clicked() {} if ui.button(format!("Copy \"{}\"", ins.formatted)).clicked() {
ui.output_mut(|output| output.copied_text.clone_from(&ins.formatted));
ui.close_menu();
}
let mut hex_string = "0x".to_string();
for byte in &section.data[ins.address as usize..(ins.address + ins.size as u64) as usize] {
hex_string.push_str(&format!("{:02x}", byte));
}
if ui.button(format!("Copy \"{hex_string}\" (instruction bytes)")).clicked() {
ui.output_mut(|output| output.copied_text = hex_string);
ui.close_menu();
}
if let Some(virtual_address) = symbol.virtual_address {
let offset = ins.address - symbol.address;
let offset_string = format!("{:#x}", virtual_address + offset);
if ui.button(format!("Copy \"{offset_string}\" (virtual address)")).clicked() {
ui.output_mut(|output| output.copied_text = offset_string);
ui.close_menu();
}
}
for arg in &ins.args { for arg in &ins.args {
if let ObjInsArg::Arg(arg) = arg { if let ObjInsArg::Arg(arg) = arg {
@ -144,6 +174,7 @@ fn diff_text_ui(
appearance: &Appearance, appearance: &Appearance,
ins_view_state: &mut FunctionViewState, ins_view_state: &mut FunctionViewState,
space_width: f32, space_width: f32,
response_cb: impl Fn(Response) -> Response,
) { ) {
let label_text; let label_text;
let mut base_color = match ins_diff.kind { let mut base_color = match ins_diff.kind {
@ -204,13 +235,13 @@ fn diff_text_ui(
let len = label_text.len(); let len = label_text.len();
let highlight = ins_view_state.highlight == text; let highlight = ins_view_state.highlight == text;
let response = Label::new(LayoutJob::single_section( let mut response = Label::new(LayoutJob::single_section(
label_text, label_text,
appearance.code_text_format(base_color, highlight), appearance.code_text_format(base_color, highlight),
)) ))
.sense(Sense::click()) .sense(Sense::click())
.ui(ui); .ui(ui);
response.context_menu(|ui| ins_context_menu(ui, ins_diff.ins.as_ref().unwrap())); response = response_cb(response);
if response.clicked() { if response.clicked() {
if highlight { if highlight {
ins_view_state.highlight = HighlightKind::None; ins_view_state.highlight = HighlightKind::None;
@ -229,6 +260,7 @@ fn asm_row_ui(
symbol: &ObjSymbol, symbol: &ObjSymbol,
appearance: &Appearance, appearance: &Appearance,
ins_view_state: &mut FunctionViewState, ins_view_state: &mut FunctionViewState,
response_cb: impl Fn(Response) -> Response,
) { ) {
ui.spacing_mut().item_spacing.x = 0.0; ui.spacing_mut().item_spacing.x = 0.0;
if ins_diff.kind != ObjInsDiffKind::None { if ins_diff.kind != ObjInsDiffKind::None {
@ -236,7 +268,7 @@ fn asm_row_ui(
} }
let space_width = ui.fonts(|f| f.glyph_width(&appearance.code_font, ' ')); let space_width = ui.fonts(|f| f.glyph_width(&appearance.code_font, ' '));
display_diff(ins_diff, symbol.address, |text| { display_diff(ins_diff, symbol.address, |text| {
diff_text_ui(ui, text, ins_diff, appearance, ins_view_state, space_width); diff_text_ui(ui, text, ins_diff, appearance, ins_view_state, space_width, &response_cb);
Ok::<_, ()>(()) Ok::<_, ()>(())
}) })
.unwrap(); .unwrap();
@ -251,14 +283,20 @@ fn asm_col_ui(
) { ) {
let (section, symbol) = obj.0.section_symbol(symbol_ref); let (section, symbol) = obj.0.section_symbol(symbol_ref);
let ins_diff = &obj.1.symbol_diff(symbol_ref).instructions[row.index()]; let ins_diff = &obj.1.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.on_hover_ui_at_pointer(|ui| {
ins_hover_ui(ui, obj.0.arch.as_ref(), section, ins, symbol, appearance)
})
} else {
response
}
};
let (_, response) = row.col(|ui| { let (_, response) = row.col(|ui| {
asm_row_ui(ui, ins_diff, symbol, appearance, ins_view_state); asm_row_ui(ui, ins_diff, symbol, appearance, ins_view_state, response_cb);
}); });
if let Some(ins) = &ins_diff.ins { response_cb(response);
response.on_hover_ui_at_pointer(|ui| {
ins_hover_ui(ui, obj.0.arch.as_ref(), section, ins, appearance)
});
}
} }
fn empty_col_ui(row: &mut TableRow<'_, '_>) { fn empty_col_ui(row: &mut TableRow<'_, '_>) {

View File

@ -171,10 +171,7 @@ fn symbol_hover_ui(ui: &mut Ui, symbol: &ObjSymbol, appearance: &Appearance) {
); );
} }
if let Some(address) = symbol.virtual_address { if let Some(address) = symbol.virtual_address {
ui.colored_label( ui.colored_label(appearance.replace_color, format!("Virtual address: {:#x}", address));
appearance.highlight_color,
format!("Virtual address: {:#x}", address),
);
} }
}); });
} }