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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 CV*?=o literal 0 HcmV?d00001 diff --git a/assets/icon.png b/assets/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..8d865d0793f43fc430605b30fe221b83f6f61ffd GIT binary patch literal 11784 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 Z9#xDOi%APA_QGMI(^A)0%Tu}G_djN 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,