mirror of
https://github.com/encounter/objdiff.git
synced 2025-12-08 21:17:59 +00:00
WIP objdiff 3.0 refactor
This commit is contained in:
@@ -38,7 +38,7 @@ float-ord = "0.3"
|
||||
font-kit = "0.14"
|
||||
globset = { version = "0.4", features = ["serde1"] }
|
||||
log = "0.4"
|
||||
objdiff-core = { path = "../objdiff-core", features = ["all"] }
|
||||
objdiff-core = { path = "../objdiff-core", features = ["ppc", "arm", "arm64", "mips", "std", "config", "dwarf", "bindings", "serde", "build"] }
|
||||
open = "5.3"
|
||||
png = "0.17"
|
||||
pollster = "0.4"
|
||||
@@ -52,6 +52,8 @@ shell-escape = "0.1"
|
||||
strum = { version = "0.26", features = ["derive"] }
|
||||
time = { version = "0.3", features = ["formatting", "local-offset"] }
|
||||
typed-path = "0.10"
|
||||
winit = { version = "0.30", features = ["wayland-csd-adwaita"] }
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
|
||||
# Keep version in sync with egui
|
||||
[dependencies.eframe]
|
||||
@@ -81,15 +83,6 @@ winapi = "0.3"
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
exec = "0.3"
|
||||
|
||||
# native:
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
|
||||
# web:
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
console_error_panic_hook = "0.1"
|
||||
tracing-wasm = "0.2"
|
||||
|
||||
[build-dependencies]
|
||||
anyhow = "1.0"
|
||||
|
||||
|
||||
@@ -783,7 +783,8 @@ impl eframe::App for App {
|
||||
|
||||
let mut action = None;
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
action = diff_view_ui(ui, diff_state, appearance);
|
||||
let state = state.read().unwrap();
|
||||
action = diff_view_ui(ui, diff_state, appearance, &state.config.diff_obj_config);
|
||||
});
|
||||
|
||||
project_window(ctx, state, show_project_config, config_state, appearance);
|
||||
|
||||
@@ -273,6 +273,7 @@ impl DiffObjConfigV1 {
|
||||
},
|
||||
space_between_args: self.space_between_args,
|
||||
combine_data_sections: self.combine_data_sections,
|
||||
combine_text_sections: false,
|
||||
x86_formatter: self.x86_formatter,
|
||||
mips_abi: self.mips_abi,
|
||||
mips_instr_category: self.mips_instr_category,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#![allow(clippy::too_many_arguments)]
|
||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release
|
||||
|
||||
mod app;
|
||||
@@ -37,8 +38,6 @@ fn load_icon() -> Result<egui::IconData> {
|
||||
|
||||
const APP_NAME: &str = "objdiff";
|
||||
|
||||
// When compiling natively:
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn main() -> ExitCode {
|
||||
// Log to stdout (if you run with `RUST_LOG=debug`).
|
||||
tracing_subscriber::fmt()
|
||||
@@ -229,21 +228,3 @@ fn run_eframe(
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
// when compiling to web using trunk.
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
fn main() {
|
||||
// Make sure panics are logged using `console.error`.
|
||||
console_error_panic_hook::set_once();
|
||||
|
||||
// Redirect tracing to console.log and friends:
|
||||
tracing_wasm::set_as_global_default();
|
||||
|
||||
let web_options = eframe::WebOptions::default();
|
||||
eframe::start_web(
|
||||
"the_canvas_id", // hardcode it
|
||||
web_options,
|
||||
Box::new(|cc| Box::new(eframe_template::TemplateApp::new(cc))),
|
||||
)
|
||||
.expect("failed to start eframe");
|
||||
}
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
use std::{
|
||||
cmp::{min, Ordering},
|
||||
default::Default,
|
||||
mem::take,
|
||||
};
|
||||
use std::{cmp::min, default::Default, mem::take};
|
||||
|
||||
use egui::{text::LayoutJob, Label, Sense, Widget};
|
||||
use objdiff_core::{
|
||||
diff::{ObjDataDiff, ObjDataDiffKind, ObjDataRelocDiff},
|
||||
obj::ObjInfo,
|
||||
diff::{DataDiff, DataDiffKind, DataRelocationDiff},
|
||||
obj::Object,
|
||||
};
|
||||
|
||||
use crate::views::{appearance::Appearance, write_text};
|
||||
@@ -16,108 +12,110 @@ pub(crate) const BYTES_PER_ROW: usize = 16;
|
||||
|
||||
fn data_row_hover_ui(
|
||||
ui: &mut egui::Ui,
|
||||
obj: &ObjInfo,
|
||||
diffs: &[(ObjDataDiff, Vec<ObjDataRelocDiff>)],
|
||||
appearance: &Appearance,
|
||||
_obj: &Object,
|
||||
_diffs: &[(DataDiff, Vec<DataRelocationDiff>)],
|
||||
_appearance: &Appearance,
|
||||
) {
|
||||
ui.scope(|ui| {
|
||||
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
|
||||
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
|
||||
|
||||
let reloc_diffs = diffs.iter().flat_map(|(_, reloc_diffs)| reloc_diffs);
|
||||
let mut prev_reloc = None;
|
||||
for reloc_diff in reloc_diffs {
|
||||
let reloc = &reloc_diff.reloc;
|
||||
if prev_reloc == Some(reloc) {
|
||||
// Avoid showing consecutive duplicate relocations.
|
||||
// We do this because a single relocation can span across multiple diffs if the
|
||||
// bytes in the relocation changed (e.g. first byte is added, second is unchanged).
|
||||
continue;
|
||||
}
|
||||
prev_reloc = Some(reloc);
|
||||
|
||||
let color = get_color_for_diff_kind(reloc_diff.kind, appearance);
|
||||
|
||||
// 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)));
|
||||
ui.label(format!("Relocation address: {:x}", reloc.address));
|
||||
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());
|
||||
}
|
||||
}
|
||||
// TODO
|
||||
// let reloc_diffs = diffs.iter().flat_map(|(_, reloc_diffs)| reloc_diffs);
|
||||
// let mut prev_reloc = None;
|
||||
// for reloc_diff in reloc_diffs {
|
||||
// let reloc = &reloc_diff.reloc;
|
||||
// if prev_reloc == Some(reloc) {
|
||||
// // Avoid showing consecutive duplicate relocations.
|
||||
// // We do this because a single relocation can span across multiple diffs if the
|
||||
// // bytes in the relocation changed (e.g. first byte is added, second is unchanged).
|
||||
// continue;
|
||||
// }
|
||||
// prev_reloc = Some(reloc);
|
||||
//
|
||||
// let color = get_color_for_diff_kind(reloc_diff.kind, appearance);
|
||||
//
|
||||
// // 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)));
|
||||
// ui.label(format!("Relocation address: {:x}", reloc.address));
|
||||
// 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, Vec<ObjDataRelocDiff>)]) {
|
||||
fn data_row_context_menu(ui: &mut egui::Ui, _diffs: &[(DataDiff, Vec<DataRelocationDiff>)]) {
|
||||
ui.scope(|ui| {
|
||||
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
|
||||
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
|
||||
|
||||
let reloc_diffs = diffs.iter().flat_map(|(_, reloc_diffs)| reloc_diffs);
|
||||
let mut prev_reloc = None;
|
||||
for reloc_diff in reloc_diffs {
|
||||
let reloc = &reloc_diff.reloc;
|
||||
if prev_reloc == Some(reloc) {
|
||||
// Avoid showing consecutive duplicate relocations.
|
||||
// We do this because a single relocation can span across multiple diffs if the
|
||||
// bytes in the relocation changed (e.g. first byte is added, second is unchanged).
|
||||
continue;
|
||||
}
|
||||
prev_reloc = Some(reloc);
|
||||
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
// TODO
|
||||
// let reloc_diffs = diffs.iter().flat_map(|(_, reloc_diffs)| reloc_diffs);
|
||||
// let mut prev_reloc = None;
|
||||
// for reloc_diff in reloc_diffs {
|
||||
// let reloc = &reloc_diff.reloc;
|
||||
// if prev_reloc == Some(reloc) {
|
||||
// // Avoid showing consecutive duplicate relocations.
|
||||
// // We do this because a single relocation can span across multiple diffs if the
|
||||
// // bytes in the relocation changed (e.g. first byte is added, second is unchanged).
|
||||
// continue;
|
||||
// }
|
||||
// prev_reloc = Some(reloc);
|
||||
//
|
||||
// // 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 get_color_for_diff_kind(diff_kind: ObjDataDiffKind, appearance: &Appearance) -> egui::Color32 {
|
||||
fn get_color_for_diff_kind(diff_kind: DataDiffKind, appearance: &Appearance) -> egui::Color32 {
|
||||
match diff_kind {
|
||||
ObjDataDiffKind::None => appearance.text_color,
|
||||
ObjDataDiffKind::Replace => appearance.replace_color,
|
||||
ObjDataDiffKind::Delete => appearance.delete_color,
|
||||
ObjDataDiffKind::Insert => appearance.insert_color,
|
||||
DataDiffKind::None => appearance.text_color,
|
||||
DataDiffKind::Replace => appearance.replace_color,
|
||||
DataDiffKind::Delete => appearance.delete_color,
|
||||
DataDiffKind::Insert => appearance.insert_color,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn data_row_ui(
|
||||
ui: &mut egui::Ui,
|
||||
obj: Option<&ObjInfo>,
|
||||
obj: Option<&Object>,
|
||||
address: usize,
|
||||
diffs: &[(ObjDataDiff, Vec<ObjDataRelocDiff>)],
|
||||
diffs: &[(DataDiff, Vec<DataRelocationDiff>)],
|
||||
appearance: &Appearance,
|
||||
) {
|
||||
if diffs.iter().any(|(dd, rds)| {
|
||||
dd.kind != ObjDataDiffKind::None || rds.iter().any(|rd| rd.kind != ObjDataDiffKind::None)
|
||||
dd.kind != DataDiffKind::None || rds.iter().any(|rd| rd.kind != DataDiffKind::None)
|
||||
}) {
|
||||
ui.painter().rect_filled(ui.available_rect_before_wrap(), 0.0, ui.visuals().faint_bg_color);
|
||||
}
|
||||
@@ -145,7 +143,7 @@ pub(crate) fn data_row_ui(
|
||||
for byte in &diff.data {
|
||||
let mut byte_color = base_color;
|
||||
if let Some(reloc_diff) = reloc_diffs.iter().find(|reloc_diff| {
|
||||
reloc_diff.kind != ObjDataDiffKind::None
|
||||
reloc_diff.kind != DataDiffKind::None
|
||||
&& reloc_diff.range.contains(&cur_addr_actual)
|
||||
}) {
|
||||
byte_color = get_color_for_diff_kind(reloc_diff.kind, appearance);
|
||||
@@ -200,11 +198,11 @@ pub(crate) fn data_row_ui(
|
||||
}
|
||||
|
||||
pub(crate) fn split_diffs(
|
||||
diffs: &[ObjDataDiff],
|
||||
reloc_diffs: &[ObjDataRelocDiff],
|
||||
) -> Vec<Vec<(ObjDataDiff, Vec<ObjDataRelocDiff>)>> {
|
||||
let mut split_diffs = Vec::<Vec<(ObjDataDiff, Vec<ObjDataRelocDiff>)>>::new();
|
||||
let mut row_diffs = Vec::<(ObjDataDiff, Vec<ObjDataRelocDiff>)>::new();
|
||||
diffs: &[DataDiff],
|
||||
reloc_diffs: &[DataRelocationDiff],
|
||||
) -> Vec<Vec<(DataDiff, Vec<DataRelocationDiff>)>> {
|
||||
let mut split_diffs = Vec::<Vec<(DataDiff, Vec<DataRelocationDiff>)>>::new();
|
||||
let mut row_diffs = Vec::<(DataDiff, Vec<DataRelocationDiff>)>::new();
|
||||
// The offset shown on the side of the GUI, shifted by insertions/deletions.
|
||||
let mut cur_addr = 0usize;
|
||||
// The offset into the actual bytes of the section on this side, ignoring differences.
|
||||
@@ -216,7 +214,7 @@ pub(crate) fn split_diffs(
|
||||
let mut remaining_in_row = BYTES_PER_ROW - (cur_addr % BYTES_PER_ROW);
|
||||
let len = min(remaining_len, remaining_in_row);
|
||||
|
||||
let data_diff = ObjDataDiff {
|
||||
let data_diff = DataDiff {
|
||||
data: if diff.data.is_empty() {
|
||||
Vec::new()
|
||||
} else {
|
||||
@@ -226,7 +224,7 @@ pub(crate) fn split_diffs(
|
||||
len,
|
||||
symbol: String::new(), // TODO
|
||||
};
|
||||
let row_reloc_diffs: Vec<ObjDataRelocDiff> = if diff.data.is_empty() {
|
||||
let row_reloc_diffs: Vec<DataRelocationDiff> = if diff.data.is_empty() {
|
||||
Vec::new()
|
||||
} else {
|
||||
let diff_range = cur_addr_actual + cur_len..cur_addr_actual + cur_len + len;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use egui::{Id, Layout, RichText, ScrollArea, TextEdit, Ui, Widget};
|
||||
use objdiff_core::{
|
||||
build::BuildStatus,
|
||||
diff::{ObjDiff, ObjSectionDiff, ObjSymbolDiff},
|
||||
obj::{ObjInfo, ObjSection, ObjSectionKind, ObjSymbol, SymbolRef},
|
||||
diff::{display::SymbolFilter, DiffObjConfig, ObjectDiff, SectionDiff, SymbolDiff},
|
||||
obj::{Object, Section, SectionKind, Symbol},
|
||||
};
|
||||
use time::format_description;
|
||||
|
||||
@@ -16,30 +16,30 @@ use crate::{
|
||||
function_diff::{asm_col_ui, FunctionDiffContext},
|
||||
symbol_diff::{
|
||||
match_color_for_symbol, symbol_list_ui, DiffViewAction, DiffViewNavigation,
|
||||
DiffViewState, SymbolDiffContext, SymbolFilter, SymbolRefByName, View,
|
||||
DiffViewState, SymbolDiffContext, SymbolRefByName, View,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum SelectedSymbol {
|
||||
Symbol(SymbolRef),
|
||||
Symbol(usize),
|
||||
Section(usize),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct DiffColumnContext<'a> {
|
||||
status: &'a BuildStatus,
|
||||
obj: Option<&'a (ObjInfo, ObjDiff)>,
|
||||
section: Option<(&'a ObjSection, &'a ObjSectionDiff)>,
|
||||
symbol: Option<(&'a ObjSymbol, &'a ObjSymbolDiff)>,
|
||||
obj: Option<&'a (Object, ObjectDiff)>,
|
||||
section: Option<(&'a Section, &'a SectionDiff, usize)>,
|
||||
symbol: Option<(&'a Symbol, &'a SymbolDiff, usize)>,
|
||||
}
|
||||
|
||||
impl<'a> DiffColumnContext<'a> {
|
||||
pub fn new(
|
||||
view: View,
|
||||
status: &'a BuildStatus,
|
||||
obj: Option<&'a (ObjInfo, ObjDiff)>,
|
||||
obj: Option<&'a (Object, ObjectDiff)>,
|
||||
selected_symbol: Option<&SymbolRefByName>,
|
||||
) -> Self {
|
||||
let selected_symbol = match view {
|
||||
@@ -57,15 +57,18 @@ impl<'a> DiffColumnContext<'a> {
|
||||
};
|
||||
let (section, symbol) = match (obj, selected_symbol) {
|
||||
(Some((obj, obj_diff)), Some(SelectedSymbol::Symbol(symbol_ref))) => {
|
||||
let (section, symbol) = obj.section_symbol(symbol_ref);
|
||||
let symbol = &obj.symbols[symbol_ref];
|
||||
(
|
||||
section.map(|s| (s, obj_diff.section_diff(symbol_ref.section_idx))),
|
||||
Some((symbol, obj_diff.symbol_diff(symbol_ref))),
|
||||
symbol.section.map(|section_idx| {
|
||||
(&obj.sections[section_idx], &obj_diff.sections[section_idx], section_idx)
|
||||
}),
|
||||
Some((symbol, &obj_diff.symbols[symbol_ref], symbol_ref)),
|
||||
)
|
||||
}
|
||||
(Some((obj, obj_diff)), Some(SelectedSymbol::Section(section_idx))) => {
|
||||
(Some((&obj.sections[section_idx], obj_diff.section_diff(section_idx))), None)
|
||||
}
|
||||
(Some((obj, obj_diff)), Some(SelectedSymbol::Section(section_idx))) => (
|
||||
Some((&obj.sections[section_idx], &obj_diff.sections[section_idx], section_idx)),
|
||||
None,
|
||||
),
|
||||
_ => (None, None),
|
||||
};
|
||||
Self { status, obj, section, symbol }
|
||||
@@ -77,8 +80,8 @@ impl<'a> DiffColumnContext<'a> {
|
||||
#[inline]
|
||||
pub fn id(&self) -> Option<&str> {
|
||||
self.symbol
|
||||
.map(|(symbol, _)| symbol.name.as_str())
|
||||
.or_else(|| self.section.map(|(section, _)| section.name.as_str()))
|
||||
.map(|(symbol, _, _)| symbol.name.as_str())
|
||||
.or_else(|| self.section.map(|(section, _, _)| section.name.as_str()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,6 +90,7 @@ pub fn diff_view_ui(
|
||||
ui: &mut Ui,
|
||||
state: &DiffViewState,
|
||||
appearance: &Appearance,
|
||||
diff_config: &DiffObjConfig,
|
||||
) -> Option<DiffViewAction> {
|
||||
let mut ret = None;
|
||||
let Some(result) = &state.build else {
|
||||
@@ -113,12 +117,15 @@ pub fn diff_view_ui(
|
||||
right_symbol: state.symbol_state.right_symbol.clone(),
|
||||
};
|
||||
let mut navigation = current_navigation.clone();
|
||||
if let Some((_symbol, symbol_diff)) = left_ctx.symbol {
|
||||
if let Some((_symbol, symbol_diff, _symbol_idx)) = left_ctx.symbol {
|
||||
// If a matching symbol appears, select it
|
||||
if !right_ctx.has_symbol() {
|
||||
if let Some(target_symbol_ref) = symbol_diff.target_symbol {
|
||||
let (target_section, target_symbol) =
|
||||
right_ctx.obj.unwrap().0.section_symbol(target_symbol_ref);
|
||||
let (right_obj, _) = right_ctx.obj.unwrap();
|
||||
let target_symbol = &right_obj.symbols[target_symbol_ref];
|
||||
let target_section = target_symbol
|
||||
.section
|
||||
.and_then(|section_idx| right_obj.sections.get(section_idx));
|
||||
navigation.right_symbol = Some(SymbolRefByName::new(target_symbol, target_section));
|
||||
}
|
||||
}
|
||||
@@ -129,12 +136,15 @@ pub fn diff_view_ui(
|
||||
// Clear selection if symbol goes missing
|
||||
navigation.left_symbol = None;
|
||||
}
|
||||
if let Some((_symbol, symbol_diff)) = right_ctx.symbol {
|
||||
if let Some((_symbol, symbol_diff, _symbol_idx)) = right_ctx.symbol {
|
||||
// If a matching symbol appears, select it
|
||||
if !left_ctx.has_symbol() {
|
||||
if let Some(target_symbol_ref) = symbol_diff.target_symbol {
|
||||
let (target_section, target_symbol) =
|
||||
left_ctx.obj.unwrap().0.section_symbol(target_symbol_ref);
|
||||
let (left_obj, _) = left_ctx.obj.unwrap();
|
||||
let target_symbol = &left_obj.symbols[target_symbol_ref];
|
||||
let target_section = target_symbol
|
||||
.section
|
||||
.and_then(|section_idx| left_obj.sections.get(section_idx));
|
||||
navigation.left_symbol = Some(SymbolRefByName::new(target_symbol, target_section));
|
||||
}
|
||||
}
|
||||
@@ -170,7 +180,7 @@ pub fn diff_view_ui(
|
||||
ret = Some(DiffViewAction::Navigate(DiffViewNavigation::symbol_diff()));
|
||||
}
|
||||
|
||||
if let Some((symbol, _)) = left_ctx.symbol {
|
||||
if let Some((symbol, _, _)) = left_ctx.symbol {
|
||||
ui.separator();
|
||||
if ui
|
||||
.add_enabled(
|
||||
@@ -210,13 +220,13 @@ pub fn diff_view_ui(
|
||||
.color(appearance.replace_color),
|
||||
);
|
||||
}
|
||||
} else if let Some((symbol, _)) = left_ctx.symbol {
|
||||
} else if let Some((symbol, _, _)) = left_ctx.symbol {
|
||||
ui.label(
|
||||
RichText::new(symbol.demangled_name.as_deref().unwrap_or(&symbol.name))
|
||||
.font(appearance.code_font.clone())
|
||||
.color(appearance.highlight_color),
|
||||
);
|
||||
} else if let Some((section, _)) = left_ctx.section {
|
||||
} else if let Some((section, _, _)) = left_ctx.section {
|
||||
ui.label(
|
||||
RichText::new(section.name.clone())
|
||||
.font(appearance.code_font.clone())
|
||||
@@ -331,13 +341,13 @@ pub fn diff_view_ui(
|
||||
.color(appearance.replace_color),
|
||||
);
|
||||
}
|
||||
} else if let Some((symbol, _)) = right_ctx.symbol {
|
||||
} else if let Some((symbol, _, _)) = right_ctx.symbol {
|
||||
ui.label(
|
||||
RichText::new(symbol.demangled_name.as_deref().unwrap_or(&symbol.name))
|
||||
.font(appearance.code_font.clone())
|
||||
.color(appearance.highlight_color),
|
||||
);
|
||||
} else if let Some((section, _)) = right_ctx.section {
|
||||
} else if let Some((section, _, _)) = right_ctx.section {
|
||||
ui.label(
|
||||
RichText::new(section.name.clone())
|
||||
.font(appearance.code_font.clone())
|
||||
@@ -359,16 +369,29 @@ pub fn diff_view_ui(
|
||||
|
||||
// Third row
|
||||
ui.horizontal(|ui| {
|
||||
if let Some((_, symbol_diff)) = right_ctx.symbol {
|
||||
if let Some((_, symbol_diff, _symbol_idx)) = right_ctx.symbol {
|
||||
let mut needs_separator = false;
|
||||
if let Some(match_percent) = symbol_diff.match_percent {
|
||||
ui.label(
|
||||
RichText::new(format!("{:.0}%", match_percent.floor()))
|
||||
let response = ui.label(
|
||||
RichText::new(format!("{:.2}%", match_percent))
|
||||
.font(appearance.code_font.clone())
|
||||
.color(match_color_for_symbol(match_percent, appearance)),
|
||||
);
|
||||
if let Some((diff_score, max_score)) = symbol_diff.diff_score {
|
||||
response.on_hover_ui_at_pointer(|ui| {
|
||||
ui.label(
|
||||
RichText::new(format!("Score: {}/{}", diff_score, max_score))
|
||||
.font(appearance.code_font.clone())
|
||||
.color(appearance.text_color),
|
||||
);
|
||||
});
|
||||
}
|
||||
needs_separator = true;
|
||||
}
|
||||
if state.current_view == View::FunctionDiff && left_ctx.has_symbol() {
|
||||
ui.separator();
|
||||
if needs_separator {
|
||||
ui.separator();
|
||||
}
|
||||
if ui
|
||||
.button("Change base")
|
||||
.on_hover_text_at_pointer(
|
||||
@@ -413,17 +436,17 @@ pub fn diff_view_ui(
|
||||
View::FunctionDiff,
|
||||
Some((left_obj, left_diff)),
|
||||
Some((right_obj, right_diff)),
|
||||
Some((_, left_symbol_diff)),
|
||||
Some((_, right_symbol_diff)),
|
||||
Some((_, left_symbol_diff, left_symbol_idx)),
|
||||
Some((_, right_symbol_diff, right_symbol_idx)),
|
||||
) = (state.current_view, left_ctx.obj, right_ctx.obj, left_ctx.symbol, right_ctx.symbol)
|
||||
{
|
||||
// Joint diff view
|
||||
hotkeys::check_scroll_hotkeys(ui, true);
|
||||
if left_symbol_diff.instructions.len() != right_symbol_diff.instructions.len() {
|
||||
if left_symbol_diff.instruction_rows.len() != right_symbol_diff.instruction_rows.len() {
|
||||
ui.label("Instruction count mismatch");
|
||||
return;
|
||||
}
|
||||
let instructions_len = left_symbol_diff.instructions.len();
|
||||
let instructions_len = left_symbol_diff.instruction_rows.len();
|
||||
render_table(
|
||||
ui,
|
||||
available_width,
|
||||
@@ -437,10 +460,11 @@ pub fn diff_view_ui(
|
||||
FunctionDiffContext {
|
||||
obj: left_obj,
|
||||
diff: left_diff,
|
||||
symbol_ref: Some(left_symbol_diff.symbol_ref),
|
||||
symbol_ref: Some(left_symbol_idx),
|
||||
},
|
||||
appearance,
|
||||
&state.function_state,
|
||||
diff_config,
|
||||
column,
|
||||
) {
|
||||
ret = Some(action);
|
||||
@@ -451,10 +475,11 @@ pub fn diff_view_ui(
|
||||
FunctionDiffContext {
|
||||
obj: right_obj,
|
||||
diff: right_diff,
|
||||
symbol_ref: Some(right_symbol_diff.symbol_ref),
|
||||
symbol_ref: Some(right_symbol_idx),
|
||||
},
|
||||
appearance,
|
||||
&state.function_state,
|
||||
diff_config,
|
||||
column,
|
||||
) {
|
||||
ret = Some(action);
|
||||
@@ -469,8 +494,8 @@ pub fn diff_view_ui(
|
||||
View::DataDiff,
|
||||
Some((left_obj, _left_diff)),
|
||||
Some((right_obj, _right_diff)),
|
||||
Some((_left_section, left_section_diff)),
|
||||
Some((_right_section, right_section_diff)),
|
||||
Some((_left_section, left_section_diff, _left_symbol_idx)),
|
||||
Some((_right_section, right_section_diff, _right_symbol_idx)),
|
||||
) =
|
||||
(state.current_view, left_ctx.obj, right_ctx.obj, left_ctx.section, right_ctx.section)
|
||||
{
|
||||
@@ -522,6 +547,7 @@ pub fn diff_view_ui(
|
||||
right_ctx,
|
||||
available_width,
|
||||
open_sections.0,
|
||||
diff_config,
|
||||
) {
|
||||
ret = Some(action);
|
||||
}
|
||||
@@ -535,6 +561,7 @@ pub fn diff_view_ui(
|
||||
left_ctx,
|
||||
available_width,
|
||||
open_sections.1,
|
||||
diff_config,
|
||||
) {
|
||||
ret = Some(action);
|
||||
}
|
||||
@@ -547,7 +574,6 @@ pub fn diff_view_ui(
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn diff_col_ui(
|
||||
ui: &mut Ui,
|
||||
state: &DiffViewState,
|
||||
@@ -557,14 +583,15 @@ fn diff_col_ui(
|
||||
other_ctx: DiffColumnContext,
|
||||
available_width: f32,
|
||||
open_sections: Option<bool>,
|
||||
diff_config: &DiffObjConfig,
|
||||
) -> Option<DiffViewAction> {
|
||||
let mut ret = None;
|
||||
if !ctx.status.success {
|
||||
build_log_ui(ui, ctx.status, appearance);
|
||||
} else if let Some((obj, diff)) = ctx.obj {
|
||||
if let Some((_symbol, symbol_diff)) = ctx.symbol {
|
||||
if let Some((_symbol, symbol_diff, symbol_idx)) = ctx.symbol {
|
||||
hotkeys::check_scroll_hotkeys(ui, false);
|
||||
let ctx = FunctionDiffContext { obj, diff, symbol_ref: Some(symbol_diff.symbol_ref) };
|
||||
let ctx = FunctionDiffContext { obj, diff, symbol_ref: Some(symbol_idx) };
|
||||
if state.current_view == View::ExtabDiff {
|
||||
extab_ui(ui, ctx, appearance, column);
|
||||
} else {
|
||||
@@ -573,11 +600,16 @@ fn diff_col_ui(
|
||||
available_width / 2.0,
|
||||
1,
|
||||
appearance.code_font.size,
|
||||
symbol_diff.instructions.len(),
|
||||
symbol_diff.instruction_rows.len(),
|
||||
|row, column| {
|
||||
if let Some(action) =
|
||||
asm_col_ui(row, ctx, appearance, &state.function_state, column)
|
||||
{
|
||||
if let Some(action) = asm_col_ui(
|
||||
row,
|
||||
ctx,
|
||||
appearance,
|
||||
&state.function_state,
|
||||
diff_config,
|
||||
column,
|
||||
) {
|
||||
ret = Some(action);
|
||||
}
|
||||
if row.response().clicked() {
|
||||
@@ -586,7 +618,7 @@ fn diff_col_ui(
|
||||
},
|
||||
);
|
||||
}
|
||||
} else if let Some((_section, section_diff)) = ctx.section {
|
||||
} else if let Some((_section, section_diff, _section_idx)) = ctx.section {
|
||||
hotkeys::check_scroll_hotkeys(ui, false);
|
||||
let total_bytes =
|
||||
section_diff.data_diff.iter().fold(0usize, |accum, item| accum + item.len);
|
||||
@@ -610,8 +642,8 @@ fn diff_col_ui(
|
||||
},
|
||||
);
|
||||
} else if let (
|
||||
Some((other_section, _other_section_diff)),
|
||||
Some((other_symbol, other_symbol_diff)),
|
||||
Some((other_section, _other_section_diff, _other_section_idx)),
|
||||
Some((other_symbol, _other_symbol_diff, other_symbol_idx)),
|
||||
) = (other_ctx.section, other_ctx.symbol)
|
||||
{
|
||||
if let Some(action) = symbol_list_ui(
|
||||
@@ -619,7 +651,7 @@ fn diff_col_ui(
|
||||
SymbolDiffContext { obj, diff },
|
||||
None,
|
||||
&state.symbol_state,
|
||||
SymbolFilter::Mapping(other_symbol_diff.symbol_ref, None),
|
||||
SymbolFilter::Mapping(other_symbol_idx, None),
|
||||
appearance,
|
||||
column,
|
||||
open_sections,
|
||||
@@ -634,7 +666,7 @@ fn diff_col_ui(
|
||||
) => {
|
||||
ret = Some(DiffViewAction::SetMapping(
|
||||
match other_section.kind {
|
||||
ObjSectionKind::Code => View::FunctionDiff,
|
||||
SectionKind::Code => View::FunctionDiff,
|
||||
_ => View::SymbolDiff,
|
||||
},
|
||||
left_symbol_ref,
|
||||
@@ -650,7 +682,7 @@ fn diff_col_ui(
|
||||
) => {
|
||||
ret = Some(DiffViewAction::SetMapping(
|
||||
match other_section.kind {
|
||||
ObjSectionKind::Code => View::FunctionDiff,
|
||||
SectionKind::Code => View::FunctionDiff,
|
||||
_ => View::SymbolDiff,
|
||||
},
|
||||
SymbolRefByName::new(other_symbol, Some(other_section)),
|
||||
@@ -725,17 +757,10 @@ fn missing_obj_ui(ui: &mut Ui, appearance: &Appearance) {
|
||||
});
|
||||
}
|
||||
|
||||
fn find_symbol(obj: &ObjInfo, selected_symbol: &SymbolRefByName) -> Option<SymbolRef> {
|
||||
for (section_idx, section) in obj.sections.iter().enumerate() {
|
||||
for (symbol_idx, symbol) in section.symbols.iter().enumerate() {
|
||||
if symbol.name == selected_symbol.symbol_name {
|
||||
return Some(SymbolRef { section_idx, symbol_idx });
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
fn find_symbol(obj: &Object, selected_symbol: &SymbolRefByName) -> Option<usize> {
|
||||
obj.symbols.iter().position(|symbol| symbol.name == selected_symbol.symbol_name)
|
||||
}
|
||||
|
||||
fn find_section(obj: &ObjInfo, section_name: &str) -> Option<usize> {
|
||||
fn find_section(obj: &Object, section_name: &str) -> Option<usize> {
|
||||
obj.sections.iter().position(|section| section.name == section_name)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use egui::ScrollArea;
|
||||
use objdiff_core::{
|
||||
arch::ppc::ExceptionInfo,
|
||||
obj::{ObjInfo, ObjSymbol},
|
||||
obj::{Object, Symbol},
|
||||
};
|
||||
|
||||
use crate::views::{appearance::Appearance, function_diff::FunctionDiffContext};
|
||||
@@ -26,14 +26,16 @@ fn decode_extab(extab: &ExceptionInfo) -> String {
|
||||
text
|
||||
}
|
||||
|
||||
fn find_extab_entry<'a>(obj: &'a ObjInfo, symbol: &ObjSymbol) -> Option<&'a ExceptionInfo> {
|
||||
obj.arch.ppc().and_then(|ppc| ppc.extab_for_symbol(symbol))
|
||||
fn find_extab_entry<'a>(_obj: &'a Object, _symbol: &Symbol) -> Option<&'a ExceptionInfo> {
|
||||
// TODO
|
||||
// obj.arch.ppc().and_then(|ppc| ppc.extab_for_symbol(symbol))
|
||||
None
|
||||
}
|
||||
|
||||
fn extab_text_ui(
|
||||
ui: &mut egui::Ui,
|
||||
ctx: FunctionDiffContext<'_>,
|
||||
symbol: &ObjSymbol,
|
||||
symbol: &Symbol,
|
||||
appearance: &Appearance,
|
||||
) -> Option<()> {
|
||||
if let Some(extab_entry) = find_extab_entry(ctx.obj, symbol) {
|
||||
@@ -56,8 +58,8 @@ pub(crate) fn extab_ui(
|
||||
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
|
||||
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
|
||||
|
||||
if let Some((_section, symbol)) =
|
||||
ctx.symbol_ref.map(|symbol_ref| ctx.obj.section_symbol(symbol_ref))
|
||||
if let Some(symbol) =
|
||||
ctx.symbol_ref.and_then(|symbol_ref| ctx.obj.symbols.get(symbol_ref))
|
||||
{
|
||||
extab_text_ui(ui, ctx, symbol, appearance);
|
||||
}
|
||||
|
||||
@@ -61,14 +61,9 @@ impl FrameHistory {
|
||||
);
|
||||
egui::warn_if_debug_build(ui);
|
||||
|
||||
if !cfg!(target_arch = "wasm32") {
|
||||
egui::CollapsingHeader::new("📊 CPU usage history").default_open(false).show(
|
||||
ui,
|
||||
|ui| {
|
||||
self.graph(ui);
|
||||
},
|
||||
);
|
||||
}
|
||||
egui::CollapsingHeader::new("📊 CPU usage history").default_open(false).show(ui, |ui| {
|
||||
self.graph(ui);
|
||||
});
|
||||
}
|
||||
|
||||
fn graph(&mut self, ui: &mut egui::Ui) -> egui::Response {
|
||||
|
||||
@@ -4,10 +4,14 @@ use egui::{text::LayoutJob, Label, Response, Sense, Widget};
|
||||
use egui_extras::TableRow;
|
||||
use objdiff_core::{
|
||||
diff::{
|
||||
display::{display_diff, DiffText, HighlightKind},
|
||||
ObjDiff, ObjInsDiff, ObjInsDiffKind,
|
||||
display::{display_row, DiffText, HighlightKind},
|
||||
DiffObjConfig, InstructionArgDiffIndex, InstructionDiffKind, InstructionDiffRow,
|
||||
ObjectDiff,
|
||||
},
|
||||
obj::{
|
||||
InstructionArg, InstructionArgValue, InstructionRef, Object, ParsedInstruction,
|
||||
ResolvedRelocation, Section, Symbol,
|
||||
},
|
||||
obj::{ObjInfo, ObjIns, ObjInsArg, ObjInsArgValue, ObjSection, ObjSymbol, SymbolRef},
|
||||
};
|
||||
|
||||
use crate::views::{appearance::Appearance, symbol_diff::DiffViewAction};
|
||||
@@ -63,43 +67,94 @@ impl FunctionViewState {
|
||||
}
|
||||
}
|
||||
|
||||
#[expect(unused)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct ResolvedInstructionRef<'obj> {
|
||||
pub symbol: &'obj Symbol,
|
||||
pub section_idx: usize,
|
||||
pub section: &'obj Section,
|
||||
pub data: &'obj [u8],
|
||||
pub relocation: Option<ResolvedRelocation<'obj>>,
|
||||
}
|
||||
|
||||
fn resolve_instruction_ref(
|
||||
obj: &Object,
|
||||
symbol_idx: usize,
|
||||
ins_ref: InstructionRef,
|
||||
) -> Option<ResolvedInstructionRef> {
|
||||
let symbol = &obj.symbols[symbol_idx];
|
||||
let section_idx = symbol.section?;
|
||||
let section = &obj.sections[section_idx];
|
||||
let offset = ins_ref.address.checked_sub(section.address)?;
|
||||
let data = section.data.get(offset as usize..offset as usize + ins_ref.size as usize)?;
|
||||
let relocation = section.relocation_at(ins_ref.address, obj);
|
||||
Some(ResolvedInstructionRef { symbol, section, section_idx, data, relocation })
|
||||
}
|
||||
|
||||
fn resolve_instruction<'obj>(
|
||||
obj: &'obj Object,
|
||||
symbol_idx: usize,
|
||||
ins_ref: InstructionRef,
|
||||
diff_config: &DiffObjConfig,
|
||||
) -> Option<(ResolvedInstructionRef<'obj>, ParsedInstruction)> {
|
||||
let resolved = resolve_instruction_ref(obj, symbol_idx, ins_ref)?;
|
||||
let ins = obj
|
||||
.arch
|
||||
.process_instruction(
|
||||
ins_ref,
|
||||
resolved.data,
|
||||
resolved.relocation,
|
||||
resolved.symbol.address..resolved.symbol.address + resolved.symbol.size,
|
||||
resolved.section_idx,
|
||||
diff_config,
|
||||
)
|
||||
.ok()?;
|
||||
Some((resolved, ins))
|
||||
}
|
||||
|
||||
fn ins_hover_ui(
|
||||
ui: &mut egui::Ui,
|
||||
obj: &ObjInfo,
|
||||
section: &ObjSection,
|
||||
ins: &ObjIns,
|
||||
symbol: &ObjSymbol,
|
||||
obj: &Object,
|
||||
symbol_idx: usize,
|
||||
ins_ref: InstructionRef,
|
||||
diff_config: &DiffObjConfig,
|
||||
appearance: &Appearance,
|
||||
) {
|
||||
let Some((
|
||||
ResolvedInstructionRef { symbol, section_idx: _, section: _, data, relocation },
|
||||
ins,
|
||||
)) = resolve_instruction(obj, symbol_idx, ins_ref, diff_config)
|
||||
else {
|
||||
ui.colored_label(appearance.delete_color, "Failed to resolve instruction");
|
||||
return;
|
||||
};
|
||||
|
||||
ui.scope(|ui| {
|
||||
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
|
||||
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
|
||||
|
||||
let offset = ins.address - section.address;
|
||||
ui.label(format!(
|
||||
"{:02x?}",
|
||||
§ion.data[offset as usize..(offset + ins.size as u64) as usize]
|
||||
));
|
||||
ui.label(format!("{:02x?}", data));
|
||||
|
||||
if let Some(virtual_address) = symbol.virtual_address {
|
||||
let offset = ins.address - symbol.address;
|
||||
let offset = ins_ref.address - symbol.address;
|
||||
ui.colored_label(
|
||||
appearance.replace_color,
|
||||
format!("Virtual address: {:#x}", virtual_address + offset),
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(orig) = &ins.orig {
|
||||
ui.label(format!("Original: {}", orig));
|
||||
}
|
||||
// TODO
|
||||
// if let Some(orig) = &ins.orig {
|
||||
// ui.label(format!("Original: {}", orig));
|
||||
// }
|
||||
|
||||
for arg in &ins.args {
|
||||
if let ObjInsArg::Arg(arg) = arg {
|
||||
if let InstructionArg::Value(arg) = arg {
|
||||
match arg {
|
||||
ObjInsArgValue::Signed(v) => {
|
||||
InstructionArgValue::Signed(v) => {
|
||||
ui.label(format!("{arg} == {v}"));
|
||||
}
|
||||
ObjInsArgValue::Unsigned(v) => {
|
||||
InstructionArgValue::Unsigned(v) => {
|
||||
ui.label(format!("{arg} == {v}"));
|
||||
}
|
||||
_ => {}
|
||||
@@ -107,66 +162,76 @@ fn ins_hover_ui(
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(reloc) = &ins.reloc {
|
||||
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),
|
||||
if let Some(resolved) = relocation {
|
||||
ui.label(format!(
|
||||
"Relocation type: {}",
|
||||
obj.arch.display_reloc(resolved.relocation.flags)
|
||||
));
|
||||
let addend_str = match resolved.relocation.addend.cmp(&0i64) {
|
||||
Ordering::Greater => format!("+{:x}", resolved.relocation.addend),
|
||||
Ordering::Less => format!("-{:x}", -resolved.relocation.addend),
|
||||
_ => "".to_string(),
|
||||
};
|
||||
ui.colored_label(
|
||||
appearance.highlight_color,
|
||||
format!("Name: {}{}", reloc.target.name, addend_str),
|
||||
format!("Name: {}{}", resolved.symbol.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(
|
||||
appearance.highlight_color,
|
||||
format!("Section: {}", section.name),
|
||||
);
|
||||
}
|
||||
if let Some(orig_section_index) = resolved.symbol.section {
|
||||
let section = &obj.sections[orig_section_index];
|
||||
ui.colored_label(appearance.highlight_color, format!("Section: {}", section.name));
|
||||
ui.colored_label(
|
||||
appearance.highlight_color,
|
||||
format!("Address: {:x}{}", reloc.target.address, addend_str),
|
||||
format!("Address: {:x}{}", resolved.symbol.address, addend_str),
|
||||
);
|
||||
ui.colored_label(
|
||||
appearance.highlight_color,
|
||||
format!("Size: {:x}", reloc.target.size),
|
||||
format!("Size: {:x}", resolved.symbol.size),
|
||||
);
|
||||
for label in obj.arch.display_ins_data_labels(ins) {
|
||||
ui.colored_label(appearance.highlight_color, label);
|
||||
}
|
||||
// TODO
|
||||
// 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());
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(decoded) = rlwinmdec::decode(&ins.formatted) {
|
||||
ui.colored_label(appearance.highlight_color, decoded.trim());
|
||||
}
|
||||
// TODO
|
||||
// if let Some(decoded) = rlwinmdec::decode(&ins.formatted) {
|
||||
// ui.colored_label(appearance.highlight_color, decoded.trim());
|
||||
// }
|
||||
});
|
||||
}
|
||||
|
||||
fn ins_context_menu(
|
||||
ui: &mut egui::Ui,
|
||||
obj: &ObjInfo,
|
||||
section: &ObjSection,
|
||||
ins: &ObjIns,
|
||||
symbol: &ObjSymbol,
|
||||
obj: &Object,
|
||||
symbol_idx: usize,
|
||||
ins_ref: InstructionRef,
|
||||
diff_config: &DiffObjConfig,
|
||||
appearance: &Appearance,
|
||||
) {
|
||||
let Some((
|
||||
ResolvedInstructionRef { symbol, section_idx: _, section: _, data, relocation },
|
||||
ins,
|
||||
)) = resolve_instruction(obj, symbol_idx, ins_ref, diff_config)
|
||||
else {
|
||||
ui.colored_label(appearance.delete_color, "Failed to resolve instruction");
|
||||
return;
|
||||
};
|
||||
|
||||
ui.scope(|ui| {
|
||||
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
|
||||
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
|
||||
|
||||
if ui.button(format!("Copy \"{}\"", ins.formatted)).clicked() {
|
||||
ui.output_mut(|output| output.copied_text.clone_from(&ins.formatted));
|
||||
ui.close_menu();
|
||||
}
|
||||
// TODO
|
||||
// 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 §ion.data[ins.address as usize..(ins.address + ins.size as u64) as usize] {
|
||||
for byte in data {
|
||||
hex_string.push_str(&format!("{:02x}", byte));
|
||||
}
|
||||
if ui.button(format!("Copy \"{hex_string}\" (instruction bytes)")).clicked() {
|
||||
@@ -175,7 +240,7 @@ fn ins_context_menu(
|
||||
}
|
||||
|
||||
if let Some(virtual_address) = symbol.virtual_address {
|
||||
let offset = ins.address - symbol.address;
|
||||
let offset = ins_ref.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);
|
||||
@@ -184,9 +249,9 @@ fn ins_context_menu(
|
||||
}
|
||||
|
||||
for arg in &ins.args {
|
||||
if let ObjInsArg::Arg(arg) = arg {
|
||||
if let InstructionArg::Value(arg) = arg {
|
||||
match arg {
|
||||
ObjInsArgValue::Signed(v) => {
|
||||
InstructionArgValue::Signed(v) => {
|
||||
if ui.button(format!("Copy \"{arg}\"")).clicked() {
|
||||
ui.output_mut(|output| output.copied_text = arg.to_string());
|
||||
ui.close_menu();
|
||||
@@ -196,7 +261,7 @@ fn ins_context_menu(
|
||||
ui.close_menu();
|
||||
}
|
||||
}
|
||||
ObjInsArgValue::Unsigned(v) => {
|
||||
InstructionArgValue::Unsigned(v) => {
|
||||
if ui.button(format!("Copy \"{arg}\"")).clicked() {
|
||||
ui.output_mut(|output| output.copied_text = arg.to_string());
|
||||
ui.close_menu();
|
||||
@@ -210,21 +275,23 @@ fn ins_context_menu(
|
||||
}
|
||||
}
|
||||
}
|
||||
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(resolved) = relocation {
|
||||
// TODO
|
||||
// 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) = &resolved.symbol.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));
|
||||
if ui.button(format!("Copy \"{}\"", resolved.symbol.name)).clicked() {
|
||||
ui.output_mut(|output| output.copied_text.clone_from(&resolved.symbol.name));
|
||||
ui.close_menu();
|
||||
}
|
||||
}
|
||||
@@ -232,11 +299,11 @@ fn ins_context_menu(
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
#[expect(clippy::too_many_arguments)]
|
||||
fn diff_text_ui(
|
||||
ui: &mut egui::Ui,
|
||||
text: DiffText<'_>,
|
||||
ins_diff: &ObjInsDiff,
|
||||
diff: InstructionArgDiffIndex,
|
||||
ins_diff: &InstructionDiffRow,
|
||||
appearance: &Appearance,
|
||||
ins_view_state: &FunctionViewState,
|
||||
column: usize,
|
||||
@@ -246,22 +313,18 @@ fn diff_text_ui(
|
||||
let mut ret = None;
|
||||
let label_text;
|
||||
let mut base_color = match ins_diff.kind {
|
||||
ObjInsDiffKind::None | ObjInsDiffKind::OpMismatch | ObjInsDiffKind::ArgMismatch => {
|
||||
appearance.text_color
|
||||
}
|
||||
ObjInsDiffKind::Replace => appearance.replace_color,
|
||||
ObjInsDiffKind::Delete => appearance.delete_color,
|
||||
ObjInsDiffKind::Insert => appearance.insert_color,
|
||||
InstructionDiffKind::None
|
||||
| InstructionDiffKind::OpMismatch
|
||||
| InstructionDiffKind::ArgMismatch => appearance.text_color,
|
||||
InstructionDiffKind::Replace => appearance.replace_color,
|
||||
InstructionDiffKind::Delete => appearance.delete_color,
|
||||
InstructionDiffKind::Insert => appearance.insert_color,
|
||||
};
|
||||
let mut pad_to = 0;
|
||||
match text {
|
||||
DiffText::Basic(text) => {
|
||||
label_text = text.to_string();
|
||||
}
|
||||
DiffText::BasicColor(s, idx) => {
|
||||
label_text = s.to_string();
|
||||
base_color = appearance.diff_colors[idx % appearance.diff_colors.len()];
|
||||
}
|
||||
DiffText::Line(num) => {
|
||||
label_text = num.to_string();
|
||||
base_color = appearance.deemphasized_text_color;
|
||||
@@ -273,43 +336,29 @@ fn diff_text_ui(
|
||||
}
|
||||
DiffText::Opcode(mnemonic, _op) => {
|
||||
label_text = mnemonic.to_string();
|
||||
if ins_diff.kind == ObjInsDiffKind::OpMismatch {
|
||||
if ins_diff.kind == InstructionDiffKind::OpMismatch {
|
||||
base_color = appearance.replace_color;
|
||||
}
|
||||
pad_to = 8;
|
||||
}
|
||||
DiffText::Argument(arg, diff) => {
|
||||
DiffText::Argument(arg) => {
|
||||
label_text = arg.to_string();
|
||||
if let Some(diff) = diff {
|
||||
base_color = appearance.diff_colors[diff.idx % appearance.diff_colors.len()]
|
||||
}
|
||||
}
|
||||
DiffText::BranchDest(addr, diff) => {
|
||||
DiffText::BranchDest(addr) => {
|
||||
label_text = format!("{addr:x}");
|
||||
if let Some(diff) = diff {
|
||||
base_color = appearance.diff_colors[diff.idx % appearance.diff_colors.len()]
|
||||
}
|
||||
}
|
||||
DiffText::Symbol(sym, diff) => {
|
||||
DiffText::Symbol(sym) => {
|
||||
let name = sym.demangled_name.as_ref().unwrap_or(&sym.name);
|
||||
label_text = name.clone();
|
||||
if let Some(diff) = diff {
|
||||
base_color = appearance.diff_colors[diff.idx % appearance.diff_colors.len()]
|
||||
} else {
|
||||
base_color = appearance.emphasized_text_color;
|
||||
}
|
||||
base_color = appearance.emphasized_text_color;
|
||||
}
|
||||
DiffText::Addend(addend, diff) => {
|
||||
DiffText::Addend(addend) => {
|
||||
label_text = match addend.cmp(&0i64) {
|
||||
Ordering::Greater => format!("+{:#x}", addend),
|
||||
Ordering::Less => format!("-{:#x}", -addend),
|
||||
_ => "".to_string(),
|
||||
};
|
||||
if let Some(diff) = diff {
|
||||
base_color = appearance.diff_colors[diff.idx % appearance.diff_colors.len()]
|
||||
} else {
|
||||
base_color = appearance.emphasized_text_color;
|
||||
}
|
||||
base_color = appearance.emphasized_text_color;
|
||||
}
|
||||
DiffText::Spacing(n) => {
|
||||
ui.add_space(n as f32 * space_width);
|
||||
@@ -319,6 +368,9 @@ fn diff_text_ui(
|
||||
label_text = "\n".to_string();
|
||||
}
|
||||
}
|
||||
if let Some(diff_idx) = diff.get() {
|
||||
base_color = appearance.diff_colors[diff_idx as usize % appearance.diff_colors.len()];
|
||||
}
|
||||
|
||||
let len = label_text.len();
|
||||
let highlight = *ins_view_state.highlight(column) == text;
|
||||
@@ -341,24 +393,27 @@ fn diff_text_ui(
|
||||
#[must_use]
|
||||
fn asm_row_ui(
|
||||
ui: &mut egui::Ui,
|
||||
ins_diff: &ObjInsDiff,
|
||||
symbol: &ObjSymbol,
|
||||
obj: &Object,
|
||||
ins_diff: &InstructionDiffRow,
|
||||
symbol_idx: usize,
|
||||
appearance: &Appearance,
|
||||
ins_view_state: &FunctionViewState,
|
||||
diff_config: &DiffObjConfig,
|
||||
column: usize,
|
||||
response_cb: impl Fn(Response) -> Response,
|
||||
) -> Option<DiffViewAction> {
|
||||
let mut ret = None;
|
||||
ui.spacing_mut().item_spacing.x = 0.0;
|
||||
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
|
||||
if ins_diff.kind != ObjInsDiffKind::None {
|
||||
if ins_diff.kind != InstructionDiffKind::None {
|
||||
ui.painter().rect_filled(ui.available_rect_before_wrap(), 0.0, ui.visuals().faint_bg_color);
|
||||
}
|
||||
let space_width = ui.fonts(|f| f.glyph_width(&appearance.code_font, ' '));
|
||||
display_diff(ins_diff, symbol.address, |text| {
|
||||
display_row(obj, symbol_idx, ins_diff, diff_config, |text, diff| {
|
||||
if let Some(action) = diff_text_ui(
|
||||
ui,
|
||||
text,
|
||||
diff,
|
||||
ins_diff,
|
||||
appearance,
|
||||
ins_view_state,
|
||||
@@ -368,7 +423,7 @@ fn asm_row_ui(
|
||||
) {
|
||||
ret = Some(action);
|
||||
}
|
||||
Ok::<_, ()>(())
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
ret
|
||||
@@ -380,27 +435,36 @@ pub(crate) fn asm_col_ui(
|
||||
ctx: FunctionDiffContext<'_>,
|
||||
appearance: &Appearance,
|
||||
ins_view_state: &FunctionViewState,
|
||||
diff_config: &DiffObjConfig,
|
||||
column: usize,
|
||||
) -> Option<DiffViewAction> {
|
||||
let mut ret = None;
|
||||
let symbol_ref = ctx.symbol_ref?;
|
||||
let (section, symbol) = ctx.obj.section_symbol(symbol_ref);
|
||||
let section = section?;
|
||||
let ins_diff = &ctx.diff.symbol_diff(symbol_ref).instructions[row.index()];
|
||||
let ins_row = &ctx.diff.symbols[symbol_ref].instruction_rows[row.index()];
|
||||
let response_cb = |response: Response| {
|
||||
if let Some(ins) = &ins_diff.ins {
|
||||
response.context_menu(|ui| ins_context_menu(ui, ctx.obj, section, ins, symbol));
|
||||
if let Some(ins_ref) = ins_row.ins_ref {
|
||||
response.context_menu(|ui| {
|
||||
ins_context_menu(ui, ctx.obj, symbol_ref, ins_ref, diff_config, appearance)
|
||||
});
|
||||
response.on_hover_ui_at_pointer(|ui| {
|
||||
ins_hover_ui(ui, ctx.obj, section, ins, symbol, appearance)
|
||||
ins_hover_ui(ui, ctx.obj, symbol_ref, ins_ref, diff_config, appearance)
|
||||
})
|
||||
} else {
|
||||
response
|
||||
}
|
||||
};
|
||||
let (_, response) = row.col(|ui| {
|
||||
if let Some(action) =
|
||||
asm_row_ui(ui, ins_diff, symbol, appearance, ins_view_state, column, response_cb)
|
||||
{
|
||||
if let Some(action) = asm_row_ui(
|
||||
ui,
|
||||
ctx.obj,
|
||||
ins_row,
|
||||
symbol_ref,
|
||||
appearance,
|
||||
ins_view_state,
|
||||
diff_config,
|
||||
column,
|
||||
response_cb,
|
||||
) {
|
||||
ret = Some(action);
|
||||
}
|
||||
});
|
||||
@@ -410,7 +474,7 @@ pub(crate) fn asm_col_ui(
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct FunctionDiffContext<'a> {
|
||||
pub obj: &'a ObjInfo,
|
||||
pub diff: &'a ObjDiff,
|
||||
pub symbol_ref: Option<SymbolRef>,
|
||||
pub obj: &'a Object,
|
||||
pub diff: &'a ObjectDiff,
|
||||
pub symbol_ref: Option<usize>,
|
||||
}
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
use std::{collections::BTreeMap, mem::take, ops::Bound};
|
||||
use std::mem::take;
|
||||
|
||||
use egui::{
|
||||
style::ScrollAnimation, text::LayoutJob, CollapsingHeader, Color32, Id, OpenUrl, ScrollArea,
|
||||
SelectableLabel, Ui, Widget,
|
||||
};
|
||||
use objdiff_core::{
|
||||
arch::ObjArch,
|
||||
diff::{display::HighlightKind, ObjDiff, ObjSymbolDiff},
|
||||
jobs::{create_scratch::CreateScratchResult, objdiff::ObjDiffResult, Job, JobQueue, JobResult},
|
||||
obj::{
|
||||
ObjInfo, ObjSection, ObjSectionKind, ObjSymbol, ObjSymbolFlags, SymbolRef, SECTION_COMMON,
|
||||
diff::{
|
||||
display::{
|
||||
display_sections, symbol_context, symbol_hover, ContextMenuItem, HighlightKind,
|
||||
HoverItem, HoverItemColor, SectionDisplay, SymbolFilter,
|
||||
},
|
||||
ObjectDiff, SymbolDiff,
|
||||
},
|
||||
jobs::{create_scratch::CreateScratchResult, objdiff::ObjDiffResult, Job, JobQueue, JobResult},
|
||||
obj::{Object, Section, SectionKind, Symbol, SymbolFlag},
|
||||
};
|
||||
use regex::{Regex, RegexBuilder};
|
||||
|
||||
@@ -28,7 +31,7 @@ pub struct SymbolRefByName {
|
||||
}
|
||||
|
||||
impl SymbolRefByName {
|
||||
pub fn new(symbol: &ObjSymbol, section: Option<&ObjSection>) -> Self {
|
||||
pub fn new(symbol: &Symbol, section: Option<&Section>) -> Self {
|
||||
Self { symbol_name: symbol.name.clone(), section_name: section.map(|s| s.name.clone()) }
|
||||
}
|
||||
}
|
||||
@@ -50,7 +53,7 @@ pub enum DiffViewAction {
|
||||
/// Navigate to a new diff view
|
||||
Navigate(DiffViewNavigation),
|
||||
/// Set the highlighted symbols in the symbols view, optionally scrolling them into view.
|
||||
SetSymbolHighlight(Option<SymbolRef>, Option<SymbolRef>, bool),
|
||||
SetSymbolHighlight(Option<usize>, Option<usize>, bool),
|
||||
/// Set the symbols view search filter
|
||||
SetSearch(String),
|
||||
/// Submit the current function to decomp.me
|
||||
@@ -88,15 +91,17 @@ impl DiffViewNavigation {
|
||||
pub fn with_symbols(
|
||||
view: View,
|
||||
other_ctx: Option<SymbolDiffContext<'_>>,
|
||||
symbol: &ObjSymbol,
|
||||
section: &ObjSection,
|
||||
symbol_diff: &ObjSymbolDiff,
|
||||
symbol: &Symbol,
|
||||
section: &Section,
|
||||
symbol_diff: &SymbolDiff,
|
||||
column: usize,
|
||||
) -> Self {
|
||||
let symbol1 = Some(SymbolRefByName::new(symbol, Some(section)));
|
||||
let symbol2 = symbol_diff.target_symbol.and_then(|symbol_ref| {
|
||||
other_ctx.map(|ctx| {
|
||||
let (section, symbol) = ctx.obj.section_symbol(symbol_ref);
|
||||
let symbol = &ctx.obj.symbols[symbol_ref];
|
||||
let section =
|
||||
symbol.section.and_then(|section_idx| ctx.obj.sections.get(section_idx));
|
||||
SymbolRefByName::new(symbol, section)
|
||||
})
|
||||
});
|
||||
@@ -107,7 +112,7 @@ impl DiffViewNavigation {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn data_diff(section: &ObjSection, column: usize) -> Self {
|
||||
pub fn data_diff(section: &Section, column: usize) -> Self {
|
||||
let symbol = Some(SymbolRefByName {
|
||||
symbol_name: "".to_string(),
|
||||
section_name: Some(section.name.clone()),
|
||||
@@ -143,7 +148,7 @@ pub struct DiffViewState {
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct SymbolViewState {
|
||||
pub highlighted_symbol: (Option<SymbolRef>, Option<SymbolRef>),
|
||||
pub highlighted_symbol: (Option<usize>, Option<usize>),
|
||||
pub autoscroll_to_highlighted_symbols: bool,
|
||||
pub left_symbol: Option<SymbolRefByName>,
|
||||
pub right_symbol: Option<SymbolRefByName>,
|
||||
@@ -365,9 +370,9 @@ fn symbol_context_menu_ui(
|
||||
ui: &mut Ui,
|
||||
ctx: SymbolDiffContext<'_>,
|
||||
other_ctx: Option<SymbolDiffContext<'_>>,
|
||||
symbol: &ObjSymbol,
|
||||
symbol_diff: &ObjSymbolDiff,
|
||||
section: Option<&ObjSection>,
|
||||
symbol: &Symbol,
|
||||
symbol_diff: &SymbolDiff,
|
||||
section: Option<&Section>,
|
||||
column: usize,
|
||||
) -> Option<DiffViewNavigation> {
|
||||
let mut ret = None;
|
||||
@@ -375,37 +380,37 @@ fn symbol_context_menu_ui(
|
||||
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
|
||||
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
|
||||
|
||||
if let Some(name) = &symbol.demangled_name {
|
||||
if ui.button(format!("Copy \"{name}\"")).clicked() {
|
||||
ui.output_mut(|output| output.copied_text.clone_from(name));
|
||||
ui.close_menu();
|
||||
for item in symbol_context(ctx.obj, symbol) {
|
||||
match item {
|
||||
ContextMenuItem::Copy { value, label } => {
|
||||
let label = if let Some(extra) = label {
|
||||
format!("Copy \"{value}\" ({extra})")
|
||||
} else {
|
||||
format!("Copy \"{value}\"")
|
||||
};
|
||||
if ui.button(label).clicked() {
|
||||
ui.output_mut(|output| output.copied_text = value);
|
||||
ui.close_menu();
|
||||
}
|
||||
}
|
||||
ContextMenuItem::Navigate { label } => {
|
||||
if ui.button(label).clicked() {
|
||||
// TODO other navigation
|
||||
ret = Some(DiffViewNavigation::with_symbols(
|
||||
View::ExtabDiff,
|
||||
other_ctx,
|
||||
symbol,
|
||||
section.unwrap(),
|
||||
symbol_diff,
|
||||
column,
|
||||
));
|
||||
ui.close_menu();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ui.button(format!("Copy \"{}\"", symbol.name)).clicked() {
|
||||
ui.output_mut(|output| output.copied_text.clone_from(&symbol.name));
|
||||
ui.close_menu();
|
||||
}
|
||||
if let Some(address) = symbol.virtual_address {
|
||||
if ui.button(format!("Copy \"{:#x}\" (virtual address)", address)).clicked() {
|
||||
ui.output_mut(|output| output.copied_text = format!("{:#x}", address));
|
||||
ui.close_menu();
|
||||
}
|
||||
}
|
||||
if let Some(section) = section {
|
||||
let has_extab =
|
||||
ctx.obj.arch.ppc().and_then(|ppc| ppc.extab_for_symbol(symbol)).is_some();
|
||||
if has_extab && ui.button("Decode exception table").clicked() {
|
||||
ret = Some(DiffViewNavigation::with_symbols(
|
||||
View::ExtabDiff,
|
||||
other_ctx,
|
||||
symbol,
|
||||
section,
|
||||
symbol_diff,
|
||||
column,
|
||||
));
|
||||
ui.close_menu();
|
||||
}
|
||||
|
||||
if let Some(section) = section {
|
||||
if ui.button("Map symbol").clicked() {
|
||||
let symbol_ref = SymbolRefByName::new(symbol, Some(section));
|
||||
if column == 0 {
|
||||
@@ -428,54 +433,41 @@ fn symbol_context_menu_ui(
|
||||
ret
|
||||
}
|
||||
|
||||
fn symbol_hover_ui(ui: &mut Ui, arch: &dyn ObjArch, symbol: &ObjSymbol, appearance: &Appearance) {
|
||||
fn symbol_hover_ui(
|
||||
ui: &mut Ui,
|
||||
ctx: SymbolDiffContext<'_>,
|
||||
symbol: &Symbol,
|
||||
appearance: &Appearance,
|
||||
) {
|
||||
ui.scope(|ui| {
|
||||
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
|
||||
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
|
||||
|
||||
ui.colored_label(appearance.highlight_color, format!("Name: {}", symbol.name));
|
||||
ui.colored_label(appearance.highlight_color, format!("Address: {:x}", symbol.address));
|
||||
if symbol.size_known {
|
||||
ui.colored_label(appearance.highlight_color, format!("Size: {:x}", symbol.size));
|
||||
} else {
|
||||
ui.colored_label(
|
||||
appearance.highlight_color,
|
||||
format!("Size: {:x} (assumed)", symbol.size),
|
||||
);
|
||||
}
|
||||
if let Some(address) = symbol.virtual_address {
|
||||
ui.colored_label(appearance.replace_color, format!("Virtual address: {:#x}", address));
|
||||
}
|
||||
if let Some(extab) = arch.ppc().and_then(|ppc| ppc.extab_for_symbol(symbol)) {
|
||||
ui.colored_label(
|
||||
appearance.highlight_color,
|
||||
format!("extab symbol: {}", &extab.etb_symbol.name),
|
||||
);
|
||||
ui.colored_label(
|
||||
appearance.highlight_color,
|
||||
format!("extabindex symbol: {}", &extab.eti_symbol.name),
|
||||
);
|
||||
for HoverItem { text, color } in symbol_hover(ctx.obj, symbol) {
|
||||
let color = match color {
|
||||
HoverItemColor::Normal => appearance.text_color,
|
||||
HoverItemColor::Emphasized => appearance.highlight_color,
|
||||
HoverItemColor::Special => appearance.replace_color,
|
||||
};
|
||||
ui.colored_label(color, text);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
#[expect(clippy::too_many_arguments)]
|
||||
fn symbol_ui(
|
||||
ui: &mut Ui,
|
||||
ctx: SymbolDiffContext<'_>,
|
||||
other_ctx: Option<SymbolDiffContext<'_>>,
|
||||
symbol: &ObjSymbol,
|
||||
symbol_diff: &ObjSymbolDiff,
|
||||
section: Option<&ObjSection>,
|
||||
symbol: &Symbol,
|
||||
symbol_diff: &SymbolDiff,
|
||||
symbol_idx: usize,
|
||||
section: Option<&Section>,
|
||||
state: &SymbolViewState,
|
||||
appearance: &Appearance,
|
||||
column: usize,
|
||||
) -> Option<DiffViewAction> {
|
||||
let mut ret = None;
|
||||
if symbol.flags.0.contains(ObjSymbolFlags::Hidden) && !state.show_hidden_symbols {
|
||||
return ret;
|
||||
}
|
||||
let mut job = LayoutJob::default();
|
||||
let name: &str =
|
||||
if let Some(demangled) = &symbol.demangled_name { demangled } else { &symbol.name };
|
||||
@@ -483,24 +475,24 @@ fn symbol_ui(
|
||||
if let Some(sym_ref) =
|
||||
if column == 0 { state.highlighted_symbol.0 } else { state.highlighted_symbol.1 }
|
||||
{
|
||||
selected = symbol_diff.symbol_ref == sym_ref;
|
||||
selected = symbol_idx == sym_ref;
|
||||
}
|
||||
if !symbol.flags.0.is_empty() {
|
||||
if !symbol.flags.is_empty() {
|
||||
write_text("[", appearance.text_color, &mut job, appearance.code_font.clone());
|
||||
if symbol.flags.0.contains(ObjSymbolFlags::Common) {
|
||||
if symbol.flags.contains(SymbolFlag::Common) {
|
||||
write_text("c", appearance.replace_color, &mut job, appearance.code_font.clone());
|
||||
} else if symbol.flags.0.contains(ObjSymbolFlags::Global) {
|
||||
} else if symbol.flags.contains(SymbolFlag::Global) {
|
||||
write_text("g", appearance.insert_color, &mut job, appearance.code_font.clone());
|
||||
} else if symbol.flags.0.contains(ObjSymbolFlags::Local) {
|
||||
} else if symbol.flags.contains(SymbolFlag::Local) {
|
||||
write_text("l", appearance.text_color, &mut job, appearance.code_font.clone());
|
||||
}
|
||||
if symbol.flags.0.contains(ObjSymbolFlags::Weak) {
|
||||
if symbol.flags.contains(SymbolFlag::Weak) {
|
||||
write_text("w", appearance.text_color, &mut job, appearance.code_font.clone());
|
||||
}
|
||||
if symbol.flags.0.contains(ObjSymbolFlags::HasExtra) {
|
||||
if symbol.flags.contains(SymbolFlag::HasExtra) {
|
||||
write_text("e", appearance.text_color, &mut job, appearance.code_font.clone());
|
||||
}
|
||||
if symbol.flags.0.contains(ObjSymbolFlags::Hidden) {
|
||||
if symbol.flags.contains(SymbolFlag::Hidden) {
|
||||
write_text(
|
||||
"h",
|
||||
appearance.deemphasized_text_color,
|
||||
@@ -521,9 +513,9 @@ fn symbol_ui(
|
||||
write_text(") ", appearance.text_color, &mut job, appearance.code_font.clone());
|
||||
}
|
||||
write_text(name, appearance.highlight_color, &mut job, appearance.code_font.clone());
|
||||
let response = SelectableLabel::new(selected, job).ui(ui).on_hover_ui_at_pointer(|ui| {
|
||||
symbol_hover_ui(ui, ctx.obj.arch.as_ref(), symbol, appearance)
|
||||
});
|
||||
let response = SelectableLabel::new(selected, job)
|
||||
.ui(ui)
|
||||
.on_hover_ui_at_pointer(|ui| symbol_hover_ui(ui, ctx, symbol, appearance));
|
||||
response.context_menu(|ui| {
|
||||
if let Some(result) =
|
||||
symbol_context_menu_ui(ui, ctx, other_ctx, symbol, symbol_diff, section, column)
|
||||
@@ -542,7 +534,7 @@ fn symbol_ui(
|
||||
if response.clicked() || (selected && hotkeys::enter_pressed(ui.ctx())) {
|
||||
if let Some(section) = section {
|
||||
match section.kind {
|
||||
ObjSectionKind::Code => {
|
||||
SectionKind::Code => {
|
||||
ret = Some(DiffViewAction::Navigate(DiffViewNavigation::with_symbols(
|
||||
View::FunctionDiff,
|
||||
other_ctx,
|
||||
@@ -552,62 +544,56 @@ fn symbol_ui(
|
||||
column,
|
||||
)));
|
||||
}
|
||||
ObjSectionKind::Data => {
|
||||
SectionKind::Data => {
|
||||
ret = Some(DiffViewAction::Navigate(DiffViewNavigation::data_diff(
|
||||
section, column,
|
||||
)));
|
||||
}
|
||||
ObjSectionKind::Bss => {}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
} else if response.hovered() {
|
||||
ret = Some(if column == 0 {
|
||||
DiffViewAction::SetSymbolHighlight(
|
||||
Some(symbol_diff.symbol_ref),
|
||||
symbol_diff.target_symbol,
|
||||
false,
|
||||
)
|
||||
DiffViewAction::SetSymbolHighlight(Some(symbol_idx), symbol_diff.target_symbol, false)
|
||||
} else {
|
||||
DiffViewAction::SetSymbolHighlight(
|
||||
symbol_diff.target_symbol,
|
||||
Some(symbol_diff.symbol_ref),
|
||||
false,
|
||||
)
|
||||
DiffViewAction::SetSymbolHighlight(symbol_diff.target_symbol, Some(symbol_idx), false)
|
||||
});
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
fn symbol_matches_filter(
|
||||
symbol: &ObjSymbol,
|
||||
diff: &ObjSymbolDiff,
|
||||
filter: SymbolFilter<'_>,
|
||||
) -> bool {
|
||||
match filter {
|
||||
SymbolFilter::None => true,
|
||||
SymbolFilter::Search(regex) => {
|
||||
regex.is_match(&symbol.name)
|
||||
|| symbol.demangled_name.as_deref().is_some_and(|s| regex.is_match(s))
|
||||
}
|
||||
SymbolFilter::Mapping(symbol_ref, regex) => {
|
||||
diff.target_symbol == Some(symbol_ref)
|
||||
&& regex.is_none_or(|r| {
|
||||
r.is_match(&symbol.name)
|
||||
|| symbol.demangled_name.as_deref().is_some_and(|s| r.is_match(s))
|
||||
})
|
||||
}
|
||||
}
|
||||
fn find_prev_symbol(section_display: &[SectionDisplay], current: usize) -> Option<usize> {
|
||||
section_display
|
||||
.iter()
|
||||
.flat_map(|s| s.symbols.iter())
|
||||
.rev()
|
||||
.skip_while(|s| s.symbol != current)
|
||||
.nth(1)
|
||||
.map(|s| s.symbol)
|
||||
// Wrap around to the last symbol if we're at the beginning of the list
|
||||
.or_else(|| find_last_symbol(section_display))
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum SymbolFilter<'a> {
|
||||
None,
|
||||
Search(&'a Regex),
|
||||
Mapping(SymbolRef, Option<&'a Regex>),
|
||||
fn find_next_symbol(section_display: &[SectionDisplay], current: usize) -> Option<usize> {
|
||||
section_display
|
||||
.iter()
|
||||
.flat_map(|s| s.symbols.iter())
|
||||
.skip_while(|s| s.symbol != current)
|
||||
.nth(1)
|
||||
.map(|s| s.symbol)
|
||||
// Wrap around to the first symbol if we're at the end of the list
|
||||
.or_else(|| find_first_symbol(section_display))
|
||||
}
|
||||
|
||||
fn find_first_symbol(section_display: &[SectionDisplay]) -> Option<usize> {
|
||||
section_display.iter().flat_map(|s| s.symbols.iter()).next().map(|s| s.symbol)
|
||||
}
|
||||
|
||||
fn find_last_symbol(section_display: &[SectionDisplay]) -> Option<usize> {
|
||||
section_display.iter().flat_map(|s| s.symbols.iter()).next_back().map(|s| s.symbol)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
#[expect(clippy::too_many_arguments)]
|
||||
pub fn symbol_list_ui(
|
||||
ui: &mut Ui,
|
||||
ctx: SymbolDiffContext<'_>,
|
||||
@@ -620,41 +606,20 @@ pub fn symbol_list_ui(
|
||||
) -> Option<DiffViewAction> {
|
||||
let mut ret = None;
|
||||
ScrollArea::both().auto_shrink([false, false]).show(ui, |ui| {
|
||||
let mut mapping = BTreeMap::new();
|
||||
let mut show_mapped_symbols = state.show_mapped_symbols;
|
||||
if let SymbolFilter::Mapping(_, _) = filter {
|
||||
let mut show_mapped_symbols = state.show_mapped_symbols;
|
||||
if ui.checkbox(&mut show_mapped_symbols, "Show mapped symbols").changed() {
|
||||
ret = Some(DiffViewAction::SetShowMappedSymbols(show_mapped_symbols));
|
||||
}
|
||||
for mapping_diff in &ctx.diff.mapping_symbols {
|
||||
let symbol = ctx.obj.section_symbol(mapping_diff.symbol_ref).1;
|
||||
if !symbol_matches_filter(symbol, mapping_diff, filter) {
|
||||
continue;
|
||||
}
|
||||
if !show_mapped_symbols {
|
||||
let symbol_diff = ctx.diff.symbol_diff(mapping_diff.symbol_ref);
|
||||
if symbol_diff.target_symbol.is_some() {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
mapping.insert(mapping_diff.symbol_ref, mapping_diff);
|
||||
}
|
||||
} else {
|
||||
for (symbol, diff) in ctx.obj.common.iter().zip(&ctx.diff.common) {
|
||||
if !symbol_matches_filter(symbol, diff, filter) {
|
||||
continue;
|
||||
}
|
||||
mapping.insert(diff.symbol_ref, diff);
|
||||
}
|
||||
for (section, section_diff) in ctx.obj.sections.iter().zip(&ctx.diff.sections) {
|
||||
for (symbol, symbol_diff) in section.symbols.iter().zip(§ion_diff.symbols) {
|
||||
if !symbol_matches_filter(symbol, symbol_diff, filter) {
|
||||
continue;
|
||||
}
|
||||
mapping.insert(symbol_diff.symbol_ref, symbol_diff);
|
||||
}
|
||||
}
|
||||
}
|
||||
let section_display = display_sections(
|
||||
ctx.obj,
|
||||
ctx.diff,
|
||||
filter,
|
||||
state.show_hidden_symbols,
|
||||
show_mapped_symbols,
|
||||
state.reverse_fn_order,
|
||||
);
|
||||
|
||||
hotkeys::check_scroll_hotkeys(ui, false);
|
||||
|
||||
@@ -669,14 +634,11 @@ pub fn symbol_list_ui(
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if let Some(mut up) = up {
|
||||
if state.reverse_fn_order {
|
||||
up = !up;
|
||||
}
|
||||
if let Some(up) = up {
|
||||
new_key_value_to_highlight = if up {
|
||||
mapping.range(..sym_ref).next_back()
|
||||
find_prev_symbol(§ion_display, sym_ref)
|
||||
} else {
|
||||
mapping.range((Bound::Excluded(sym_ref), Bound::Unbounded)).next()
|
||||
find_next_symbol(§ion_display, sym_ref)
|
||||
};
|
||||
};
|
||||
} else {
|
||||
@@ -685,26 +647,15 @@ pub fn symbol_list_ui(
|
||||
// we do when a symbol is highlighted. This is so that if only one column has a symbol
|
||||
// highlighted, that one takes precedence over the one with nothing highlighted.
|
||||
if hotkeys::up_pressed(ui.ctx()) || hotkeys::down_pressed(ui.ctx()) {
|
||||
new_key_value_to_highlight = if state.reverse_fn_order {
|
||||
mapping.last_key_value()
|
||||
} else {
|
||||
mapping.first_key_value()
|
||||
};
|
||||
new_key_value_to_highlight = find_first_symbol(§ion_display);
|
||||
}
|
||||
}
|
||||
if let Some((new_sym_ref, new_symbol_diff)) = new_key_value_to_highlight {
|
||||
if let Some(new_sym_ref) = new_key_value_to_highlight {
|
||||
let target_symbol = ctx.diff.symbols[new_sym_ref].target_symbol;
|
||||
ret = Some(if column == 0 {
|
||||
DiffViewAction::SetSymbolHighlight(
|
||||
Some(*new_sym_ref),
|
||||
new_symbol_diff.target_symbol,
|
||||
true,
|
||||
)
|
||||
DiffViewAction::SetSymbolHighlight(Some(new_sym_ref), target_symbol, true)
|
||||
} else {
|
||||
DiffViewAction::SetSymbolHighlight(
|
||||
new_symbol_diff.target_symbol,
|
||||
Some(*new_sym_ref),
|
||||
true,
|
||||
)
|
||||
DiffViewAction::SetSymbolHighlight(target_symbol, Some(new_sym_ref), true)
|
||||
});
|
||||
}
|
||||
|
||||
@@ -712,44 +663,21 @@ pub fn symbol_list_ui(
|
||||
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
|
||||
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
|
||||
|
||||
// Skip sections with all symbols filtered out
|
||||
if mapping.keys().any(|symbol_ref| symbol_ref.section_idx == SECTION_COMMON) {
|
||||
CollapsingHeader::new(".comm").default_open(true).show(ui, |ui| {
|
||||
for (symbol_ref, symbol_diff) in mapping
|
||||
.iter()
|
||||
.filter(|(symbol_ref, _)| symbol_ref.section_idx == SECTION_COMMON)
|
||||
{
|
||||
let symbol = ctx.obj.section_symbol(*symbol_ref).1;
|
||||
if let Some(result) = symbol_ui(
|
||||
ui,
|
||||
ctx,
|
||||
other_ctx,
|
||||
symbol,
|
||||
symbol_diff,
|
||||
None,
|
||||
state,
|
||||
appearance,
|
||||
column,
|
||||
) {
|
||||
ret = Some(result);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for ((section_index, section), section_diff) in
|
||||
ctx.obj.sections.iter().enumerate().zip(&ctx.diff.sections)
|
||||
{
|
||||
// Skip sections with all symbols filtered out
|
||||
if !mapping.keys().any(|symbol_ref| symbol_ref.section_idx == section_index) {
|
||||
continue;
|
||||
}
|
||||
for section_display in section_display {
|
||||
let mut header = LayoutJob::simple_singleline(
|
||||
format!("{} ({:x})", section.name, section.size),
|
||||
section_display.name.clone(),
|
||||
appearance.code_font.clone(),
|
||||
Color32::PLACEHOLDER,
|
||||
);
|
||||
if let Some(match_percent) = section_diff.match_percent {
|
||||
if section_display.size > 0 {
|
||||
write_text(
|
||||
&format!(" ({:x})", section_display.size),
|
||||
Color32::PLACEHOLDER,
|
||||
&mut header,
|
||||
appearance.code_font.clone(),
|
||||
);
|
||||
}
|
||||
if let Some(match_percent) = section_display.match_percent {
|
||||
write_text(
|
||||
" (",
|
||||
Color32::PLACEHOLDER,
|
||||
@@ -770,50 +698,38 @@ pub fn symbol_list_ui(
|
||||
);
|
||||
}
|
||||
CollapsingHeader::new(header)
|
||||
.id_salt(Id::new(section.name.clone()).with(section.orig_index))
|
||||
.id_salt(Id::new(§ion_display.id))
|
||||
.default_open(true)
|
||||
.open(open_sections)
|
||||
.show(ui, |ui| {
|
||||
if section.kind == ObjSectionKind::Code && state.reverse_fn_order {
|
||||
for (symbol, symbol_diff) in mapping
|
||||
.iter()
|
||||
.filter(|(symbol_ref, _)| symbol_ref.section_idx == section_index)
|
||||
.rev()
|
||||
{
|
||||
let symbol = ctx.obj.section_symbol(*symbol).1;
|
||||
if let Some(result) = symbol_ui(
|
||||
ui,
|
||||
ctx,
|
||||
other_ctx,
|
||||
symbol,
|
||||
symbol_diff,
|
||||
Some(section),
|
||||
state,
|
||||
appearance,
|
||||
column,
|
||||
) {
|
||||
ret = Some(result);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (symbol, symbol_diff) in mapping
|
||||
.iter()
|
||||
.filter(|(symbol_ref, _)| symbol_ref.section_idx == section_index)
|
||||
{
|
||||
let symbol = ctx.obj.section_symbol(*symbol).1;
|
||||
if let Some(result) = symbol_ui(
|
||||
ui,
|
||||
ctx,
|
||||
other_ctx,
|
||||
symbol,
|
||||
symbol_diff,
|
||||
Some(section),
|
||||
state,
|
||||
appearance,
|
||||
column,
|
||||
) {
|
||||
ret = Some(result);
|
||||
}
|
||||
for symbol_display in §ion_display.symbols {
|
||||
let symbol = &ctx.obj.symbols[symbol_display.symbol];
|
||||
let section = symbol
|
||||
.section
|
||||
.and_then(|section_idx| ctx.obj.sections.get(section_idx));
|
||||
let symbol_diff = if symbol_display.is_mapping_symbol {
|
||||
ctx.diff
|
||||
.mapping_symbols
|
||||
.iter()
|
||||
.find(|d| d.symbol_index == symbol_display.symbol)
|
||||
.map(|d| &d.symbol_diff)
|
||||
.unwrap()
|
||||
} else {
|
||||
&ctx.diff.symbols[symbol_display.symbol]
|
||||
};
|
||||
if let Some(result) = symbol_ui(
|
||||
ui,
|
||||
ctx,
|
||||
other_ctx,
|
||||
symbol,
|
||||
symbol_diff,
|
||||
symbol_display.symbol,
|
||||
section,
|
||||
state,
|
||||
appearance,
|
||||
column,
|
||||
) {
|
||||
ret = Some(result);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -825,6 +741,6 @@ pub fn symbol_list_ui(
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct SymbolDiffContext<'a> {
|
||||
pub obj: &'a ObjInfo,
|
||||
pub diff: &'a ObjDiff,
|
||||
pub obj: &'a Object,
|
||||
pub diff: &'a ObjectDiff,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user