Implement diffing relocations within data sections (#154)

* Data view: Show data bytes with differing relocations as a diff

* Data view: Show differing relocations on hover

* Symbol list view: Adjust symbol/section match %s when relocations differ

* Improve data reloc diffing logic

* Don't make reloc diffs cause bytes to show as red or green

* Properly detect byte size of each relocation

* Data view: Add context menu for copying relocation target symbols

* Also show already-matching relocations on hover/right click

* Change font color for nonmatching relocs on hover
This commit is contained in:
LagoLunatic
2025-01-18 18:20:07 -05:00
committed by GitHub
parent 2876be37a3
commit a4fdb61f04
10 changed files with 426 additions and 24 deletions

View File

@@ -1,4 +1,8 @@
use std::{cmp::min, default::Default, mem::take};
use std::{
cmp::{min, Ordering},
default::Default,
mem::take,
};
use egui::{text::LayoutJob, Id, Label, RichText, Sense, Widget};
use objdiff_core::{
@@ -23,7 +27,88 @@ fn find_section(obj: &ObjInfo, section_name: &str) -> Option<usize> {
obj.sections.iter().position(|section| section.name == section_name)
}
fn data_row_ui(ui: &mut egui::Ui, address: usize, diffs: &[ObjDataDiff], appearance: &Appearance) {
fn data_row_hover_ui(
ui: &mut egui::Ui,
obj: &ObjInfo,
diffs: &[ObjDataDiff],
appearance: &Appearance,
) {
ui.scope(|ui| {
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
for diff in diffs {
let Some(reloc) = &diff.reloc else {
continue;
};
// Use a slightly different font color for nonmatching relocations so they stand out.
let color = match diff.kind {
ObjDataDiffKind::None => appearance.highlight_color,
_ => appearance.replace_color,
};
// TODO: Most of this code is copy-pasted from ins_hover_ui.
// Try to separate this out into a shared function.
ui.label(format!("Relocation type: {}", obj.arch.display_reloc(reloc.flags)));
let addend_str = match reloc.addend.cmp(&0i64) {
Ordering::Greater => format!("+{:x}", reloc.addend),
Ordering::Less => format!("-{:x}", -reloc.addend),
_ => "".to_string(),
};
ui.colored_label(color, format!("Name: {}{}", reloc.target.name, addend_str));
if let Some(orig_section_index) = reloc.target.orig_section_index {
if let Some(section) =
obj.sections.iter().find(|s| s.orig_index == orig_section_index)
{
ui.colored_label(color, format!("Section: {}", section.name));
}
ui.colored_label(
color,
format!("Address: {:x}{}", reloc.target.address, addend_str),
);
ui.colored_label(color, format!("Size: {:x}", reloc.target.size));
if reloc.addend >= 0 && reloc.target.bytes.len() > reloc.addend as usize {}
} else {
ui.colored_label(color, "Extern".to_string());
}
}
});
}
fn data_row_context_menu(ui: &mut egui::Ui, diffs: &[ObjDataDiff]) {
ui.scope(|ui| {
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
for diff in diffs {
let Some(reloc) = &diff.reloc else {
continue;
};
// TODO: This code is copy-pasted from ins_context_menu.
// Try to separate this out into a shared function.
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));
ui.close_menu();
}
}
if ui.button(format!("Copy \"{}\"", reloc.target.name)).clicked() {
ui.output_mut(|output| output.copied_text.clone_from(&reloc.target.name));
ui.close_menu();
}
}
});
}
fn data_row_ui(
ui: &mut egui::Ui,
obj: Option<&ObjInfo>,
address: usize,
diffs: &[ObjDataDiff],
appearance: &Appearance,
) {
if diffs.iter().any(|d| d.kind != ObjDataDiffKind::None) {
ui.painter().rect_filled(ui.available_rect_before_wrap(), 0.0, ui.visuals().faint_bg_color);
}
@@ -94,9 +179,13 @@ fn data_row_ui(ui: &mut egui::Ui, address: usize, diffs: &[ObjDataDiff], appeara
write_text(text.as_str(), base_color, &mut job, appearance.code_font.clone());
}
}
Label::new(job).sense(Sense::click()).ui(ui);
// .on_hover_ui_at_pointer(|ui| ins_hover_ui(ui, ins))
// .context_menu(|ui| ins_context_menu(ui, ins));
let response = Label::new(job).sense(Sense::click()).ui(ui);
if let Some(obj) = obj {
response
.on_hover_ui_at_pointer(|ui| data_row_hover_ui(ui, obj, diffs, appearance))
.context_menu(|ui| data_row_context_menu(ui, diffs));
}
}
fn split_diffs(diffs: &[ObjDataDiff]) -> Vec<Vec<ObjDataDiff>> {
@@ -117,8 +206,8 @@ fn split_diffs(diffs: &[ObjDataDiff]) -> Vec<Vec<ObjDataDiff>> {
},
kind: diff.kind,
len,
// TODO
symbol: String::new(),
symbol: String::new(), // TODO
reloc: diff.reloc.clone(),
});
remaining_in_row -= len;
cur_len += len;
@@ -161,6 +250,8 @@ fn data_table_ui(
right_ctx: Option<SectionDiffContext<'_>>,
config: &Appearance,
) -> Option<()> {
let left_obj = left_ctx.map(|ctx| ctx.obj);
let right_obj = right_ctx.map(|ctx| ctx.obj);
let left_section = left_ctx
.and_then(|ctx| ctx.section_index.map(|i| (&ctx.obj.sections[i], &ctx.diff.sections[i])));
let right_section = right_ctx
@@ -187,11 +278,11 @@ fn data_table_ui(
row.col(|ui| {
if column == 0 {
if let Some(left_diffs) = &left_diffs {
data_row_ui(ui, address, &left_diffs[i], config);
data_row_ui(ui, left_obj, address, &left_diffs[i], config);
}
} else if column == 1 {
if let Some(right_diffs) = &right_diffs {
data_row_ui(ui, address, &right_diffs[i], config);
data_row_ui(ui, right_obj, address, &right_diffs[i], config);
}
}
});