From 7219e72acf3420d33ad3d29b710e724db2899e6e Mon Sep 17 00:00:00 2001 From: Luke Street Date: Sat, 10 Dec 2022 10:34:03 -0500 Subject: [PATCH] Version 0.2.2 - Add application icon - Fixes for objects containing multiple sections with the same name --- Cargo.lock | 14 +++++++++++++- Cargo.toml | 11 ++++++++--- assets/icon.ico | Bin 0 -> 36622 bytes assets/icon.png | Bin 0 -> 11784 bytes assets/icon_64.png | Bin 0 -> 9102 bytes build.rs | 8 +++++++- src/app.rs | 7 ++++++- src/main.rs | 28 +++++++++++++++++++++++++++- src/obj/elf.rs | 6 ++---- src/views/data_diff.rs | 14 +++++++------- src/views/function_diff.rs | 20 ++++++++++---------- src/views/symbol_diff.rs | 22 ++++++++++++---------- 12 files changed, 92 insertions(+), 38 deletions(-) create mode 100644 assets/icon.ico create mode 100644 assets/icon.png create mode 100644 assets/icon_64.png diff --git a/Cargo.lock b/Cargo.lock index 51ed96f..0abcff2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1926,9 +1926,10 @@ dependencies = [ [[package]] name = "objdiff" -version = "0.2.0" +version = "0.2.2" dependencies = [ "anyhow", + "bytes", "cfg-if", "console_error_panic_hook", "const_format", @@ -1943,6 +1944,7 @@ dependencies = [ "notify", "object", "path-slash", + "png", "ppc750cl", "rabbitizer", "reqwest", @@ -1958,6 +1960,7 @@ dependencies = [ "twox-hash", "vergen", "winapi", + "winres", ] [[package]] @@ -3520,6 +3523,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "winres" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b68db261ef59e9e52806f688020631e987592bd83619edccda9c47d42cde4f6c" +dependencies = [ + "toml", +] + [[package]] name = "wio" version = "0.2.2" diff --git a/Cargo.toml b/Cargo.toml index 0ae84e3..d479483 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "objdiff" -version = "0.2.1" +version = "0.2.2" edition = "2021" rust-version = "1.62" authors = ["Luke Street "] @@ -18,6 +18,7 @@ strip = "debuginfo" [dependencies] anyhow = "1.0.66" +bytes = "1.3.0" cfg-if = "1.0.0" const_format = "0.2.30" cwdemangle = { git = "https://github.com/encounter/cwdemangle", rev = "286f3d1d29ee2457db89043782725631845c3e4c" } @@ -29,22 +30,26 @@ log = "0.4.17" memmap2 = "0.5.8" notify = "5.0.0" object = { version = "0.30.0", features = ["read_core", "std", "elf"], default-features = false } +png = "0.17.7" ppc750cl = { git = "https://github.com/encounter/ppc750cl", rev = "aa631a33de7882c679afca89350898b87cb3ba3f" } rabbitizer = { git = "https://github.com/encounter/rabbitizer-rs", rev = "10c279b2ef251c62885b1dcdcfe740b0db8e9956" } +reqwest = "0.11.13" rfd = { version = "0.10.0" } # , default-features = false, features = ['xdg-portal'] self_update = "0.32.0" serde = { version = "1", features = ["derive"] } +tempfile = "3.3.0" thiserror = "1.0.37" time = { version = "0.3.17", features = ["formatting", "local-offset"] } toml = "0.5.9" twox-hash = "1.6.3" -tempfile = "3.3.0" -reqwest = "0.11.13" [target.'cfg(windows)'.dependencies] path-slash = "0.2.1" winapi = "0.3.9" +[target.'cfg(windows)'.build-dependencies] +winres = "0.1.12" + [target.'cfg(unix)'.dependencies] exec = "0.3.1" diff --git a/assets/icon.ico b/assets/icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..27cad1bdbf191ae2ac8b5daa17a3b644008bb50f GIT binary patch literal 36622 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 SCQa1=6?LAjelF{r5}E*Jt70br literal 0 HcmV?d00001 diff --git a/build.rs b/build.rs index e10776e..1f6705a 100644 --- a/build.rs +++ b/build.rs @@ -1,4 +1,10 @@ use anyhow::Result; use vergen::{vergen, Config}; -fn main() -> Result<()> { vergen(Config::default()) } +fn main() -> Result<()> { + #[cfg(windows)] + { + winres::WindowsResource::new().set_icon("assets/icon.ico").compile()?; + } + vergen(Config::default()) +} diff --git a/src/app.rs b/src/app.rs index c66e30c..3171a31 100644 --- a/src/app.rs +++ b/src/app.rs @@ -79,6 +79,11 @@ impl Default for ViewConfig { } } +pub struct SymbolReference { + pub symbol_name: String, + pub section_index: usize, +} + #[derive(serde::Deserialize, serde::Serialize)] #[serde(default)] pub struct ViewState { @@ -89,7 +94,7 @@ pub struct ViewState { #[serde(skip)] pub highlighted_symbol: Option, #[serde(skip)] - pub selected_symbol: Option, + pub selected_symbol: Option, #[serde(skip)] pub current_view: View, #[serde(skip)] diff --git a/src/main.rs b/src/main.rs index 2ef59b2..0d99c0e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,9 +3,27 @@ use std::{path::PathBuf, rc::Rc, sync::Mutex}; +use anyhow::{Error, Result}; use cfg_if::cfg_if; +use eframe::IconData; use time::UtcOffset; +fn load_icon() -> Result { + use bytes::Buf; + let decoder = png::Decoder::new(include_bytes!("../assets/icon_64.png").reader()); + let mut reader = decoder.read_info()?; + let mut buf = vec![0; reader.output_buffer_size()]; + let info = reader.next_frame(&mut buf)?; + if info.bit_depth != png::BitDepth::Eight { + return Err(Error::msg("Invalid bit depth")); + } + if info.color_type != png::ColorType::Rgba { + return Err(Error::msg("Invalid color type")); + } + buf.truncate(info.buffer_size()); + Ok(IconData { rgba: buf, width: info.width, height: info.height }) +} + // When compiling natively: #[cfg(not(target_arch = "wasm32"))] fn main() { @@ -19,7 +37,15 @@ fn main() { let exec_path: Rc>> = Rc::new(Mutex::new(None)); let exec_path_clone = exec_path.clone(); - let native_options = eframe::NativeOptions::default(); + let mut native_options = eframe::NativeOptions::default(); + match load_icon() { + Ok(data) => { + native_options.icon_data = Some(data); + } + Err(e) => { + log::warn!("Failed to load application icon: {}", e); + } + } // native_options.renderer = eframe::Renderer::Wgpu; eframe::run_native( "objdiff", diff --git a/src/obj/elf.rs b/src/obj/elf.rs index 4e1807a..98d2f0e 100644 --- a/src/obj/elf.rs +++ b/src/obj/elf.rs @@ -9,7 +9,7 @@ use object::{ R_PPC_EMB_SDA21, R_PPC_REL14, R_PPC_REL24, }, Architecture, File, Object, ObjectSection, ObjectSymbol, RelocationKind, RelocationTarget, - SectionKind, Symbol, SymbolKind, SymbolSection, + SectionIndex, SectionKind, Symbol, SymbolKind, SymbolSection, }; use crate::obj::{ @@ -192,9 +192,7 @@ fn relocations_by_section( obj_file: &File<'_>, section: &mut ObjSection, ) -> Result> { - let obj_section = obj_file - .section_by_name(§ion.name) - .ok_or_else(|| anyhow::Error::msg("Failed to locate section"))?; + let obj_section = obj_file.section_by_index(SectionIndex(section.index))?; let mut relocations = Vec::::new(); for (address, reloc) in obj_section.relocations() { let symbol = match reloc.target() { diff --git a/src/views/data_diff.rs b/src/views/data_diff.rs index 8cae0c6..0bd1a1e 100644 --- a/src/views/data_diff.rs +++ b/src/views/data_diff.rs @@ -5,7 +5,7 @@ use egui_extras::{Size, StripBuilder, TableBuilder}; use time::format_description; use crate::{ - app::{View, ViewConfig, ViewState}, + app::{SymbolReference, View, ViewConfig, ViewState}, jobs::Job, obj::{ObjDataDiff, ObjDataDiffKind, ObjInfo, ObjSection}, views::{write_text, COLOR_RED}, @@ -13,8 +13,8 @@ use crate::{ const BYTES_PER_ROW: usize = 16; -fn find_section<'a>(obj: &'a ObjInfo, section_name: &str) -> Option<&'a ObjSection> { - obj.sections.iter().find(|s| s.name == section_name) +fn find_section<'a>(obj: &'a ObjInfo, selected_symbol: &SymbolReference) -> Option<&'a ObjSection> { + obj.sections.get(selected_symbol.section_index) } fn data_row_ui(ui: &mut egui::Ui, address: usize, diffs: &[ObjDataDiff], config: &ViewConfig) { @@ -132,11 +132,11 @@ fn data_table_ui( table: TableBuilder<'_>, left_obj: &ObjInfo, right_obj: &ObjInfo, - section_name: &str, + selected_symbol: &SymbolReference, config: &ViewConfig, ) -> Option<()> { - let left_section = find_section(left_obj, section_name)?; - let right_section = find_section(right_obj, section_name)?; + let left_section = find_section(left_obj, selected_symbol)?; + let right_section = find_section(right_obj, selected_symbol)?; let total_bytes = left_section.data_diff.iter().fold(0usize, |accum, item| accum + item.len); if total_bytes == 0 { @@ -219,7 +219,7 @@ pub fn data_diff_ui(ui: &mut egui::Ui, view_state: &mut ViewState) -> bool { ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace); ui.style_mut().wrap = Some(false); - ui.colored_label(Color32::WHITE, selected_symbol); + ui.colored_label(Color32::WHITE, &selected_symbol.symbol_name); ui.label("Diff target:"); ui.separator(); }); diff --git a/src/views/function_diff.rs b/src/views/function_diff.rs index 3bfb3d8..87de16a 100644 --- a/src/views/function_diff.rs +++ b/src/views/function_diff.rs @@ -7,7 +7,7 @@ use ppc750cl::Argument; use time::format_description; use crate::{ - app::{View, ViewConfig, ViewState}, + app::{SymbolReference, View, ViewConfig, ViewState}, jobs::Job, obj::{ ObjInfo, ObjIns, ObjInsArg, ObjInsArgDiff, ObjInsDiff, ObjInsDiffKind, ObjReloc, @@ -240,9 +240,9 @@ fn ins_context_menu(ui: &mut egui::Ui, ins: &ObjIns) { }); } -fn find_symbol<'a>(obj: &'a ObjInfo, section_name: &str, name: &str) -> Option<&'a ObjSymbol> { - let section = obj.sections.iter().find(|s| s.name == section_name)?; - section.symbols.iter().find(|s| s.name == name) +fn find_symbol<'a>(obj: &'a ObjInfo, selected_symbol: &SymbolReference) -> Option<&'a ObjSymbol> { + let section = obj.sections.get(selected_symbol.section_index)?; + section.symbols.iter().find(|s| s.name == selected_symbol.symbol_name) } fn asm_row_ui(ui: &mut egui::Ui, ins_diff: &ObjInsDiff, symbol: &ObjSymbol, config: &ViewConfig) { @@ -296,11 +296,11 @@ fn asm_table_ui( table: TableBuilder<'_>, left_obj: &ObjInfo, right_obj: &ObjInfo, - fn_name: &str, + selected_symbol: &SymbolReference, config: &ViewConfig, ) -> Option<()> { - let left_symbol = find_symbol(left_obj, ".text", fn_name); - let right_symbol = find_symbol(right_obj, ".text", fn_name); + let left_symbol = find_symbol(left_obj, selected_symbol); + let right_symbol = find_symbol(right_obj, selected_symbol); let instructions_len = left_symbol.or(right_symbol).map(|s| s.instructions.len())?; table.body(|body| { body.rows(config.code_font.size, instructions_len, |row_index, mut row| { @@ -372,7 +372,7 @@ pub fn function_diff_ui(ui: &mut egui::Ui, view_state: &mut ViewState) -> bool { }); strip.strip(|builder| { builder.sizes(Size::remainder(), 2).horizontal(|mut strip| { - let demangled = demangle(selected_symbol, &Default::default()); + let demangled = demangle(&selected_symbol.symbol_name, &Default::default()); strip.cell(|ui| { ui.scope(|ui| { ui.style_mut().override_text_style = @@ -380,7 +380,7 @@ pub fn function_diff_ui(ui: &mut egui::Ui, view_state: &mut ViewState) -> bool { ui.style_mut().wrap = Some(false); ui.colored_label( Color32::WHITE, - demangled.as_ref().unwrap_or(selected_symbol), + demangled.as_ref().unwrap_or(&selected_symbol.symbol_name), ); ui.label("Diff target:"); ui.separator(); @@ -394,7 +394,7 @@ pub fn function_diff_ui(ui: &mut egui::Ui, view_state: &mut ViewState) -> bool { if let Some(match_percent) = result .second_obj .as_ref() - .and_then(|obj| find_symbol(obj, ".text", selected_symbol)) + .and_then(|obj| find_symbol(obj, selected_symbol)) .and_then(|symbol| symbol.match_percent) { ui.colored_label( diff --git a/src/views/symbol_diff.rs b/src/views/symbol_diff.rs index 95a9474..5643f18 100644 --- a/src/views/symbol_diff.rs +++ b/src/views/symbol_diff.rs @@ -4,7 +4,7 @@ use egui::{ use egui_extras::{Size, StripBuilder}; use crate::{ - app::{View, ViewConfig, ViewState}, + app::{SymbolReference, View, ViewConfig, ViewState}, jobs::objdiff::BuildStatus, obj::{ObjInfo, ObjSection, ObjSectionKind, ObjSymbol, ObjSymbolFlags}, views::write_text, @@ -56,9 +56,9 @@ fn symbol_hover_ui(ui: &mut Ui, symbol: &ObjSymbol) { fn symbol_ui( ui: &mut Ui, symbol: &ObjSymbol, - section: Option<&ObjSection>, + section: Option<(usize, &ObjSection)>, highlighted_symbol: &mut Option, - selected_symbol: &mut Option, + selected_symbol: &mut Option, current_view: &mut View, config: &ViewConfig, ) { @@ -97,12 +97,14 @@ fn symbol_ui( .context_menu(|ui| symbol_context_menu_ui(ui, symbol)) .on_hover_ui_at_pointer(|ui| symbol_hover_ui(ui, symbol)); if response.clicked() { - if let Some(section) = section { + if let Some((section_index, section)) = section { if section.kind == ObjSectionKind::Code { - *selected_symbol = Some(symbol.name.clone()); + *selected_symbol = + Some(SymbolReference { symbol_name: symbol.name.clone(), section_index }); *current_view = View::FunctionDiff; } else if section.kind == ObjSectionKind::Data { - *selected_symbol = Some(section.name.clone()); + *selected_symbol = + Some(SymbolReference { symbol_name: section.name.clone(), section_index }); *current_view = View::DataDiff; } } @@ -126,7 +128,7 @@ fn symbol_list_ui( ui: &mut Ui, obj: &ObjInfo, highlighted_symbol: &mut Option, - selected_symbol: &mut Option, + selected_symbol: &mut Option, current_view: &mut View, reverse_function_order: bool, search: &mut String, @@ -156,7 +158,7 @@ fn symbol_list_ui( }); } - for section in &obj.sections { + for (section_index, section) in obj.sections.iter().enumerate() { CollapsingHeader::new(format!("{} ({:x})", section.name, section.size)) .default_open(true) .show(ui, |ui| { @@ -168,7 +170,7 @@ fn symbol_list_ui( symbol_ui( ui, symbol, - Some(section), + Some((section_index, section)), highlighted_symbol, selected_symbol, current_view, @@ -183,7 +185,7 @@ fn symbol_list_ui( symbol_ui( ui, symbol, - Some(section), + Some((section_index, section)), highlighted_symbol, selected_symbol, current_view,