diff --git a/Cargo.lock b/Cargo.lock index 5db4211..b1fd8b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -43,6 +43,15 @@ dependencies = [ "version_check", ] +[[package]] +name = "aho-corasick" +version = "0.7.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" +dependencies = [ + "memchr", +] + [[package]] name = "ansi_term" version = "0.12.1" @@ -134,6 +143,17 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73b5e5f48b927f04e952dedc932f31995a65a0bf65ec971c74436e51bf6e970d" +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -146,6 +166,29 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +[[package]] +name = "bindgen" +version = "0.60.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "062dddbc1ba4aca46de6338e2bf87771414c335f7b2f2036e8f3e9befebf88e6" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "clap", + "env_logger", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "which", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -225,6 +268,15 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + [[package]] name = "cfg-expr" version = "0.10.3" @@ -249,6 +301,41 @@ dependencies = [ "libc", ] +[[package]] +name = "clang-sys" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a050e2153c5be08febd6734e29298e844fdb0fa21aeddd63b4eb7baa106c69b" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "clap" +version = "3.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b71c3ce99b7611011217b366d923f1d0a7e07a92bb2dbf1e84508c673ca3bd" +dependencies = [ + "atty", + "bitflags", + "clap_lex", + "indexmap", + "strsim", + "termcolor", + "textwrap", +] + +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", +] + [[package]] name = "clipboard-win" version = "4.4.2" @@ -433,7 +520,8 @@ checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" [[package]] name = "cwdemangle" -version = "0.1.0" +version = "0.1.2" +source = "git+https://github.com/encounter/cwdemangle?rev=ba448f403320f32b808e0dcf3040c6424664acab#ba448f403320f32b808e0dcf3040c6424664acab" dependencies = [ "argh", ] @@ -617,6 +705,12 @@ dependencies = [ "web-sys", ] +[[package]] +name = "either" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" + [[package]] name = "emath" version = "0.19.0" @@ -627,6 +721,19 @@ dependencies = [ "serde", ] +[[package]] +name = "env_logger" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + [[package]] name = "epaint" version = "0.19.0" @@ -867,6 +974,12 @@ dependencies = [ "system-deps", ] +[[package]] +name = "glob" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" + [[package]] name = "glow" version = "0.11.2" @@ -973,6 +1086,12 @@ dependencies = [ "system-deps", ] +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + [[package]] name = "heck" version = "0.3.3" @@ -988,6 +1107,21 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + [[package]] name = "ident_case" version = "1.0.1" @@ -1005,6 +1139,16 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "indexmap" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +dependencies = [ + "autocfg", + "hashbrown", +] + [[package]] name = "inotify" version = "0.9.6" @@ -1098,6 +1242,12 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "libc" version = "0.2.132" @@ -1427,6 +1577,7 @@ dependencies = [ "notify", "object", "ppc750cl", + "rabbitizer", "rfd", "serde", "thiserror", @@ -1450,6 +1601,12 @@ version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f7254b99e31cad77da24b08ebf628882739a608578bb1bcdfc1f9c21260d7c0" +[[package]] +name = "os_str_bytes" +version = "6.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" + [[package]] name = "osmesa-sys" version = "0.1.2" @@ -1503,6 +1660,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + [[package]] name = "percent-encoding" version = "2.1.0" @@ -1571,6 +1734,17 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rabbitizer" +version = "0.1.0" +source = "git+https://github.com/encounter/rabbitizer-rs?rev=10c279b2ef251c62885b1dcdcfe740b0db8e9956#10c279b2ef251c62885b1dcdcfe740b0db8e9956" +dependencies = [ + "bindgen", + "cc", + "cty", + "glob", +] + [[package]] name = "raw-window-handle" version = "0.4.3" @@ -1609,6 +1783,23 @@ dependencies = [ "thiserror", ] +[[package]] +name = "regex" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" + [[package]] name = "rfd" version = "0.10.0" @@ -1644,6 +1835,12 @@ dependencies = [ "serde", ] +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "safe_arch" version = "0.5.2" @@ -1746,6 +1943,12 @@ dependencies = [ "libc", ] +[[package]] +name = "shlex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" + [[package]] name = "slotmap" version = "1.0.6" @@ -1826,6 +2029,21 @@ dependencies = [ "version-compare", ] +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" + [[package]] name = "thiserror" version = "1.0.33" @@ -2232,6 +2450,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "which" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c831fbbee9e129a8cf93e7747a82da9d95ba8e16621cae60ec2cdc849bacb7b" +dependencies = [ + "either", + "libc", + "once_cell", +] + [[package]] name = "widestring" version = "0.5.1" diff --git a/Cargo.toml b/Cargo.toml index 2f2ae1e..191afbe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,11 +20,12 @@ thiserror = "1.0.33" flagset = "0.4.3" object = "0.29.0" notify = "5.0.0" -cwdemangle = "0.1.1" +cwdemangle = { git = "https://github.com/encounter/cwdemangle", rev = "ba448f403320f32b808e0dcf3040c6424664acab" } log = "0.4.17" rfd = { version = "0.10.0" } # , default-features = false, features = ['xdg-portal'] egui_extras = "0.19.0" ppc750cl = { git = "https://github.com/terorie/ppc750cl" } +rabbitizer = { git = "https://github.com/encounter/rabbitizer-rs", rev = "10c279b2ef251c62885b1dcdcfe740b0db8e9956" } # native: [target.'cfg(not(target_arch = "wasm32"))'.dependencies] diff --git a/src/app.rs b/src/app.rs index 24617d0..6e9f7d9 100644 --- a/src/app.rs +++ b/src/app.rs @@ -10,11 +10,12 @@ use std::{ }; use eframe::Frame; +use egui::Widget; use notify::{RecursiveMode, Watcher}; use crate::{ jobs::{ - build::{queue_build, BuildResult}, + build::{queue_build, BuildResult, BuildStatus}, Job, JobResult, JobState, }, views::{ @@ -30,6 +31,13 @@ pub enum View { FunctionDiff, } +#[derive(Default, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub enum DiffKind { + #[default] + SplitObj, + WholeBinary, +} + #[derive(Default, serde::Deserialize, serde::Serialize)] #[serde(default)] pub struct ViewState { @@ -43,17 +51,24 @@ pub struct ViewState { pub selected_symbol: Option, #[serde(skip)] pub current_view: View, + #[serde(skip)] + pub show_config: bool, // Config + pub diff_kind: DiffKind, pub reverse_fn_order: bool, } #[derive(Default, Clone, serde::Deserialize, serde::Serialize)] #[serde(default)] pub struct AppConfig { + // Split obj pub project_dir: Option, pub build_asm_dir: Option, pub build_src_dir: Option, pub build_obj: Option, + // Whole binary + pub left_obj: Option, + pub right_obj: Option, #[serde(skip)] pub project_dir_change: bool, } @@ -118,6 +133,9 @@ impl eframe::App for App { if ui.button("Quit").clicked() { frame.close(); } + if ui.button("Show config").clicked() { + view_state.show_config = !view_state.show_config; + } }); }); }); @@ -148,6 +166,34 @@ impl eframe::App for App { }); } + egui::Window::new("Config").open(&mut view_state.show_config).show(ctx, |ui| { + ui.label("Diff type:"); + + if egui::RadioButton::new( + view_state.diff_kind == DiffKind::SplitObj, + "Split object diff", + ) + .ui(ui) + .on_hover_text("Compare individual object files") + .clicked() + { + view_state.diff_kind = DiffKind::SplitObj; + } + + if egui::RadioButton::new( + view_state.diff_kind == DiffKind::WholeBinary, + "Whole binary diff", + ) + .ui(ui) + .on_hover_text("Compare two full binaries") + .clicked() + { + view_state.diff_kind = DiffKind::WholeBinary; + } + + ui.separator(); + }); + if view_state.jobs.iter().any(|job| { if let Some(handle) = &job.handle { return !handle.is_finished(); @@ -156,7 +202,6 @@ impl eframe::App for App { }) { ctx.request_repaint(); } else { - ctx.request_repaint(); ctx.request_repaint_after(Duration::from_millis(100)); } } @@ -187,6 +232,20 @@ impl eframe::App for App { JobResult::Build(state) => { self.view_state.build = Some(state); } + JobResult::BinDiff(state) => { + self.view_state.build = Some(Box::new(BuildResult { + first_status: BuildStatus { + success: true, + log: "".to_string(), + }, + second_status: BuildStatus { + success: true, + log: "".to_string(), + }, + first_obj: Some(state.first_obj), + second_obj: Some(state.second_obj), + })); + } } } Err(e) => { diff --git a/src/diff.rs b/src/diff.rs index 21980d5..2eb356d 100644 --- a/src/diff.rs +++ b/src/diff.rs @@ -1,87 +1,18 @@ use std::collections::BTreeMap; use anyhow::Result; -use ppc750cl::{disasm_iter, Argument}; use crate::{ editops::{editops_find, LevEditType}, obj::{ - ObjInfo, ObjIns, ObjInsArg, ObjInsArgDiff, ObjInsBranchFrom, ObjInsBranchTo, ObjInsDiff, - ObjInsDiffKind, ObjReloc, ObjRelocKind, ObjSection, ObjSectionKind, ObjSymbol, - ObjSymbolFlags, + mips, ppc, ObjArchitecture, ObjInfo, ObjInsArg, ObjInsArgDiff, ObjInsBranchFrom, + ObjInsBranchTo, ObjInsDiff, ObjInsDiffKind, ObjReloc, ObjSection, ObjSectionKind, + ObjSymbol, ObjSymbolFlags, }, }; -// Relative relocation, can be Simm or BranchDest -fn is_relative_arg(arg: &ObjInsArg) -> bool { - matches!(arg, ObjInsArg::Arg(arg) if matches!(arg, Argument::Simm(_) | Argument::BranchDest(_))) -} - -// Relative or absolute relocation, can be Uimm, Simm or Offset -fn is_rel_abs_arg(arg: &ObjInsArg) -> bool { - matches!(arg, ObjInsArg::Arg(arg) if matches!(arg, Argument::Uimm(_) | Argument::Simm(_) | Argument::Offset(_))) -} - -fn is_offset_arg(arg: &ObjInsArg) -> bool { matches!(arg, ObjInsArg::Arg(Argument::Offset(_))) } - -fn process_code(data: &[u8], address: u64, relocs: &[ObjReloc]) -> Result<(Vec, Vec)> { - let ins_count = data.len() / 4; - let mut ops = Vec::::with_capacity(ins_count); - let mut insts = Vec::::with_capacity(ins_count); - for mut ins in disasm_iter(data, address as u32) { - let reloc = relocs.iter().find(|r| (r.address as u32 & !3) == ins.addr); - if let Some(reloc) = reloc { - // Zero out relocations - ins.code = match reloc.kind { - ObjRelocKind::PpcEmbSda21 => ins.code & !0x1FFFFF, - ObjRelocKind::PpcRel24 => ins.code & !0x3FFFFFC, - ObjRelocKind::PpcRel14 => ins.code & !0xFFFC, - ObjRelocKind::PpcAddr16Hi - | ObjRelocKind::PpcAddr16Ha - | ObjRelocKind::PpcAddr16Lo => ins.code & !0xFFFF, - _ => ins.code, - }; - } - let simplified = ins.simplified(); - let mut args: Vec = - simplified.args.iter().map(|a| ObjInsArg::Arg(a.clone())).collect(); - if let Some(reloc) = reloc { - match reloc.kind { - ObjRelocKind::PpcEmbSda21 => { - args = vec![args[0].clone(), ObjInsArg::Reloc]; - } - ObjRelocKind::PpcRel24 | ObjRelocKind::PpcRel14 => { - let arg = args - .iter_mut() - .rfind(|a| is_relative_arg(a)) - .ok_or_else(|| anyhow::Error::msg("Failed to locate rel arg for reloc"))?; - *arg = ObjInsArg::Reloc; - } - ObjRelocKind::PpcAddr16Hi - | ObjRelocKind::PpcAddr16Ha - | ObjRelocKind::PpcAddr16Lo => { - let arg = args.iter_mut().rfind(|a| is_rel_abs_arg(a)).ok_or_else(|| { - anyhow::Error::msg("Failed to locate rel/abs arg for reloc") - })?; - *arg = - if is_offset_arg(arg) { ObjInsArg::RelocOffset } else { ObjInsArg::Reloc }; - } - _ => {} - } - } - ops.push(simplified.ins.op as u8); - let suffix = simplified.ins.suffix(); - insts.push(ObjIns { - ins: simplified.ins, - mnemonic: format!("{}{}", simplified.mnemonic, suffix), - args, - reloc: reloc.cloned(), - }); - } - Ok((ops, insts)) -} - pub fn diff_code( + arch: ObjArchitecture, left_data: &[u8], right_data: &[u8], left_symbol: &mut ObjSymbol, @@ -89,12 +20,30 @@ pub fn diff_code( left_relocs: &[ObjReloc], right_relocs: &[ObjReloc], ) -> Result<()> { - let left_code = - &left_data[left_symbol.address as usize..(left_symbol.address + left_symbol.size) as usize]; - let (left_ops, left_insts) = process_code(left_code, left_symbol.address, left_relocs)?; - let right_code = &right_data - [right_symbol.address as usize..(right_symbol.address + right_symbol.size) as usize]; - let (right_ops, right_insts) = process_code(right_code, right_symbol.address, right_relocs)?; + let left_code = &left_data[left_symbol.section_address as usize + ..(left_symbol.section_address + left_symbol.size) as usize]; + let right_code = &right_data[right_symbol.section_address as usize + ..(right_symbol.section_address + right_symbol.size) as usize]; + let ((left_ops, left_insts), (right_ops, right_insts)) = match arch { + ObjArchitecture::PowerPc => ( + ppc::process_code(left_code, left_symbol.address, left_relocs)?, + ppc::process_code(right_code, right_symbol.address, right_relocs)?, + ), + ObjArchitecture::Mips => ( + mips::process_code( + left_code, + left_symbol.address, + left_symbol.address + left_symbol.size, + left_relocs, + )?, + mips::process_code( + right_code, + right_symbol.address, + left_symbol.address + left_symbol.size, + right_relocs, + )?, + ), + }; let mut left_diff = Vec::::new(); let mut right_diff = Vec::::new(); @@ -111,7 +60,7 @@ pub fn diff_code( let left_addr = op.first_start as u32 * 4; let right_addr = op.second_start as u32 * 4; while let (Some(left), Some(right)) = (cur_left, cur_right) { - if (left.ins.addr - left_symbol.address as u32) < left_addr { + if (left.address - left_symbol.address as u32) < left_addr { left_diff.push(ObjInsDiff { ins: Some(left.clone()), ..ObjInsDiff::default() }); right_diff .push(ObjInsDiff { ins: Some(right.clone()), ..ObjInsDiff::default() }); @@ -122,10 +71,10 @@ pub fn diff_code( cur_right = right_iter.next(); } if let (Some(left), Some(right)) = (cur_left, cur_right) { - if (left.ins.addr - left_symbol.address as u32) != left_addr { + if (left.address - left_symbol.address as u32) != left_addr { return Err(anyhow::Error::msg("Instruction address mismatch (left)")); } - if (right.ins.addr - right_symbol.address as u32) != right_addr { + if (right.address - right_symbol.address as u32) != right_addr { return Err(anyhow::Error::msg("Instruction address mismatch (right)")); } match op.op_type { @@ -178,7 +127,11 @@ pub fn diff_code( } let total = left_insts.len(); - let percent = ((total - diff_state.diff_count) as f32 / total as f32) * 100.0; + let percent = if diff_state.diff_count >= total { + 0.0 + } else { + ((total - diff_state.diff_count) as f32 / total as f32) * 100.0 + }; left_symbol.match_percent = percent; right_symbol.match_percent = percent; @@ -194,17 +147,22 @@ fn resolve_branches(vec: &mut [ObjInsDiff]) { let mut addr_map = BTreeMap::::new(); for (i, ins_diff) in vec.iter().enumerate() { if let Some(ins) = &ins_diff.ins { - addr_map.insert(ins.ins.addr, i); + addr_map.insert(ins.address, i); } } // Generate branches let mut branches = BTreeMap::::new(); for (i, ins_diff) in vec.iter_mut().enumerate() { if let Some(ins) = &ins_diff.ins { - if ins.ins.is_blr() || ins.reloc.is_some() { - continue; - } - if let Some(ins_idx) = ins.ins.branch_dest().and_then(|dest| addr_map.get(&dest)) { + // if ins.ins.is_blr() || ins.reloc.is_some() { + // continue; + // } + if let Some(ins_idx) = ins + .args + .iter() + .find_map(|a| if let ObjInsArg::BranchOffset(offs) = a { Some(offs) } else { None }) + .and_then(|offs| addr_map.get(&((ins.address as i32 + offs) as u32))) + { if let Some(branch) = branches.get_mut(ins_idx) { ins_diff.branch_to = Some(ObjInsBranchTo { ins_idx: *ins_idx, branch_idx: branch.branch_idx }); @@ -253,15 +211,8 @@ fn arg_eq( right_diff: &ObjInsDiff, ) -> bool { return match left { - ObjInsArg::Arg(l) => match right { - ObjInsArg::Arg(r) => match r { - Argument::BranchDest(_) => { - // Compare dest instruction idx after diffing - left_diff.branch_to.as_ref().map(|b| b.ins_idx) - == right_diff.branch_to.as_ref().map(|b| b.ins_idx) - } - _ => format!("{}", l) == format!("{}", r), - }, + ObjInsArg::PpcArg(l) => match right { + ObjInsArg::PpcArg(r) => format!("{}", l) == format!("{}", r), _ => false, }, ObjInsArg::Reloc => { @@ -271,13 +222,21 @@ fn arg_eq( right_diff.ins.as_ref().and_then(|i| i.reloc.as_ref()), ) } - ObjInsArg::RelocOffset => { - matches!(right, ObjInsArg::RelocOffset) + ObjInsArg::RelocWithBase => { + matches!(right, ObjInsArg::RelocWithBase) && reloc_eq( left_diff.ins.as_ref().and_then(|i| i.reloc.as_ref()), right_diff.ins.as_ref().and_then(|i| i.reloc.as_ref()), ) } + ObjInsArg::MipsArg(ls) => { + matches!(right, ObjInsArg::MipsArg(rs) if ls == rs) + } + ObjInsArg::BranchOffset(_) => { + // Compare dest instruction idx after diffing + left_diff.branch_to.as_ref().map(|b| b.ins_idx) + == right_diff.branch_to.as_ref().map(|b| b.ins_idx) + } }; } @@ -303,7 +262,7 @@ fn compare_ins( ) -> Result { let mut result = InsDiffResult::default(); if let (Some(left_ins), Some(right_ins)) = (&left.ins, &right.ins) { - if left_ins.args.len() != right_ins.args.len() || left_ins.ins.op != right_ins.ins.op { + if left_ins.args.len() != right_ins.args.len() || left_ins.op != right_ins.op { // Totally different op result.kind = ObjInsDiffKind::Replace; state.diff_count += 1; @@ -324,8 +283,10 @@ fn compare_ins( state.diff_count += 1; } let a_str = match a { - ObjInsArg::Arg(arg) => format!("{}", arg), - ObjInsArg::Reloc | ObjInsArg::RelocOffset => String::new(), + ObjInsArg::PpcArg(arg) => format!("{}", arg), + ObjInsArg::Reloc | ObjInsArg::RelocWithBase => String::new(), + ObjInsArg::MipsArg(str) => str.clone(), + ObjInsArg::BranchOffset(arg) => format!("{}", arg), }; let a_diff = if let Some(idx) = state.left_args_idx.get(&a_str) { ObjInsArgDiff { idx: *idx } @@ -336,8 +297,10 @@ fn compare_ins( ObjInsArgDiff { idx } }; let b_str = match b { - ObjInsArg::Arg(arg) => format!("{}", arg), - ObjInsArg::Reloc | ObjInsArg::RelocOffset => String::new(), + ObjInsArg::PpcArg(arg) => format!("{}", arg), + ObjInsArg::Reloc | ObjInsArg::RelocWithBase => String::new(), + ObjInsArg::MipsArg(str) => str.clone(), + ObjInsArg::BranchOffset(arg) => format!("{}", arg), }; let b_diff = if let Some(idx) = state.right_args_idx.get(&b_str) { ObjInsArgDiff { idx: *idx } @@ -380,6 +343,7 @@ pub fn diff_objs(left: &mut ObjInfo, right: &mut ObjInfo) -> Result<()> { right_symbol.diff_symbol = Some(left_symbol.name.clone()); if left_section.kind == ObjSectionKind::Code { diff_code( + left.architecture, &left_section.data, &right_section.data, left_symbol, diff --git a/src/elf.rs b/src/elf.rs index 30a8f97..8297b39 100644 --- a/src/elf.rs +++ b/src/elf.rs @@ -4,13 +4,13 @@ use anyhow::{Context, Result}; use cwdemangle::demangle; use flagset::Flags; use object::{ - Object, ObjectSection, ObjectSymbol, RelocationKind, RelocationTarget, SectionKind, SymbolKind, - SymbolSection, + Architecture, File, Object, ObjectSection, ObjectSymbol, RelocationKind, RelocationTarget, + SectionKind, Symbol, SymbolKind, SymbolSection, }; use crate::obj::{ - ObjInfo, ObjReloc, ObjRelocKind, ObjSection, ObjSectionKind, ObjSymbol, ObjSymbolFlagSet, - ObjSymbolFlags, + ObjArchitecture, ObjInfo, ObjReloc, ObjRelocKind, ObjSection, ObjSectionKind, ObjSymbol, + ObjSymbolFlagSet, ObjSymbolFlags, }; fn to_obj_section_kind(kind: SectionKind) -> ObjSectionKind { @@ -22,7 +22,7 @@ fn to_obj_section_kind(kind: SectionKind) -> ObjSectionKind { } } -fn to_obj_symbol(symbol: &object::Symbol<'_, '_>) -> Result { +fn to_obj_symbol(obj_file: &File<'_>, symbol: &Symbol<'_, '_>) -> Result { let mut name = symbol.name().context("Failed to process symbol name")?; if name.is_empty() { println!("Found empty sym: {:?}", symbol); @@ -41,10 +41,18 @@ fn to_obj_symbol(symbol: &object::Symbol<'_, '_>) -> Result { if symbol.is_weak() { flags = ObjSymbolFlagSet(flags.0 | ObjSymbolFlags::Weak); } + let section_address = if let Some(section) = + symbol.section_index().and_then(|idx| obj_file.section_by_index(idx).ok()) + { + symbol.address() - section.address() + } else { + symbol.address() + }; Ok(ObjSymbol { name: name.to_string(), demangled_name: demangle(name), address: symbol.address(), + section_address, size: symbol.size(), size_known: symbol.size() != 0, flags, @@ -61,7 +69,11 @@ const R_PPC_REL24: u32 = 10; const R_PPC_REL14: u32 = 11; const R_PPC_EMB_SDA21: u32 = 109; -fn filter_sections(obj_file: &object::File<'_>) -> Result> { +const R_MIPS_26: u32 = 4; +const R_MIPS_HI16: u32 = 5; +const R_MIPS_LO16: u32 = 6; + +fn filter_sections(obj_file: &File<'_>) -> Result> { let mut result = Vec::::new(); for section in obj_file.sections() { if section.size() == 0 { @@ -91,7 +103,7 @@ fn filter_sections(obj_file: &object::File<'_>) -> Result> { Ok(result) } -fn symbols_by_section(obj_file: &object::File<'_>, section: &ObjSection) -> Result> { +fn symbols_by_section(obj_file: &File<'_>, section: &ObjSection) -> Result> { let mut result = Vec::::new(); for symbol in obj_file.symbols() { if symbol.kind() == SymbolKind::Section { @@ -102,11 +114,11 @@ fn symbols_by_section(obj_file: &object::File<'_>, section: &ObjSection) -> Resu if symbol.is_local() && section.kind == ObjSectionKind::Code { // TODO strip local syms in diff? let name = symbol.name().context("Failed to process symbol name")?; - if name.starts_with("lbl_") { + if symbol.size() == 0 || name.starts_with("lbl_") { continue; } } - result.push(to_obj_symbol(&symbol)?); + result.push(to_obj_symbol(obj_file, &symbol)?); } } } @@ -124,38 +136,64 @@ fn symbols_by_section(obj_file: &object::File<'_>, section: &ObjSection) -> Resu Ok(result) } -fn common_symbols(obj_file: &object::File<'_>) -> Result> { +fn common_symbols(obj_file: &File<'_>) -> Result> { let mut result = Vec::::new(); for symbol in obj_file.symbols() { if symbol.is_common() { - result.push(to_obj_symbol(&symbol)?); + result.push(to_obj_symbol(obj_file, &symbol)?); } } Ok(result) } -fn locate_section_symbol( - obj_file: &object::File<'_>, - target: &object::Symbol<'_, '_>, +fn find_section_symbol( + obj_file: &File<'_>, + target: &Symbol<'_, '_>, address: u64, ) -> Result { let section_index = target.section_index().ok_or_else(|| anyhow::Error::msg("Unknown section index"))?; + let section = obj_file.section_by_index(section_index)?; + let mut closest_symbol: Option> = None; for symbol in obj_file.symbols() { if !matches!(symbol.section_index(), Some(idx) if idx == section_index) { continue; } if symbol.kind() == SymbolKind::Section || symbol.address() != address { + if symbol.address() < address + && symbol.size() != 0 + && (closest_symbol.is_none() + || matches!(&closest_symbol, Some(s) if s.address() <= symbol.address())) + { + closest_symbol = Some(symbol); + } continue; } - return to_obj_symbol(&symbol); + return to_obj_symbol(obj_file, &symbol); } - Err(anyhow::Error::msg("Failed to locate reloc offset sym")) + let (name, offset) = closest_symbol + .and_then(|s| s.name().map(|n| (n, s.address())).ok()) + .or_else(|| section.name().map(|n| (n, section.address())).ok()) + .unwrap_or(("", 0)); + let offset_addr = address - offset; + Ok(ObjSymbol { + name: if offset_addr > 0 { format!("{}+{:#X}", name, address) } else { name.to_string() }, + demangled_name: None, + address, + section_address: address - section.address(), + size: 0, + size_known: false, + flags: Default::default(), + diff_symbol: None, + instructions: vec![], + match_percent: 0.0, + }) } fn relocations_by_section( - obj_file: &object::File<'_>, - section: &ObjSection, + arch: ObjArchitecture, + obj_file: &File<'_>, + section: &mut ObjSection, ) -> Result> { let obj_section = obj_file .section_by_name(§ion.name) @@ -175,19 +213,32 @@ fn relocations_by_section( }; let kind = match reloc.kind() { RelocationKind::Absolute => ObjRelocKind::Absolute, - RelocationKind::Elf(kind) => match kind { - R_PPC_ADDR16_LO => ObjRelocKind::PpcAddr16Lo, - R_PPC_ADDR16_HI => ObjRelocKind::PpcAddr16Hi, - R_PPC_ADDR16_HA => ObjRelocKind::PpcAddr16Ha, - R_PPC_REL24 => ObjRelocKind::PpcRel24, - R_PPC_REL14 => ObjRelocKind::PpcRel14, - R_PPC_EMB_SDA21 => ObjRelocKind::PpcEmbSda21, - _ => { - return Err(anyhow::Error::msg(format!( - "Unhandled ELF relocation type: {}", - kind - ))) - } + RelocationKind::Elf(kind) => match arch { + ObjArchitecture::PowerPc => match kind { + R_PPC_ADDR16_LO => ObjRelocKind::PpcAddr16Lo, + R_PPC_ADDR16_HI => ObjRelocKind::PpcAddr16Hi, + R_PPC_ADDR16_HA => ObjRelocKind::PpcAddr16Ha, + R_PPC_REL24 => ObjRelocKind::PpcRel24, + R_PPC_REL14 => ObjRelocKind::PpcRel14, + R_PPC_EMB_SDA21 => ObjRelocKind::PpcEmbSda21, + _ => { + return Err(anyhow::Error::msg(format!( + "Unhandled PPC relocation type: {}", + kind + ))) + } + }, + ObjArchitecture::Mips => match kind { + R_MIPS_26 => ObjRelocKind::Mips26, + R_MIPS_HI16 => ObjRelocKind::MipsHi16, + R_MIPS_LO16 => ObjRelocKind::MipsLo16, + _ => { + return Err(anyhow::Error::msg(format!( + "Unhandled MIPS relocation type: {}", + kind + ))) + } + }, }, _ => { return Err(anyhow::Error::msg(format!( @@ -198,23 +249,39 @@ fn relocations_by_section( }; let target_section = match symbol.section() { SymbolSection::Common => Some(".comm".to_string()), - SymbolSection::Section(idx) => obj_file - .section_by_index(idx) - .map(|s| s.name().map(|s| s.to_string()).ok()) - .ok() - .flatten(), + SymbolSection::Section(idx) => { + obj_file.section_by_index(idx).and_then(|s| s.name().map(|s| s.to_string())).ok() + } _ => None, }; - // println!("Reloc: {:?}", reloc.addend()); + // println!("Reloc: {:?}, symbol: {:?}", reloc, symbol); let target = match symbol.kind() { - SymbolKind::Text | SymbolKind::Data | SymbolKind::Unknown => to_obj_symbol(&symbol), + SymbolKind::Text | SymbolKind::Data | SymbolKind::Unknown => { + to_obj_symbol(obj_file, &symbol) + } SymbolKind::Section => { - let addend = reloc.addend(); - if addend < 0 { - Err(anyhow::Error::msg(format!("Negative addend in section reloc: {}", addend))) + let addend = if reloc.has_implicit_addend() { + let addend = u32::from_be_bytes( + section.data[address as usize..address as usize + 4].try_into()?, + ); + match kind { + ObjRelocKind::MipsHi16 | ObjRelocKind::MipsLo16 => { + (addend & 0x0000FFFF) * 4 + } + ObjRelocKind::Mips26 => (addend & 0x03FFFFFF) * 4, + _ => todo!(), + } } else { - locate_section_symbol(obj_file, &symbol, addend as u64) - } + let addend = reloc.addend(); + if addend < 0 { + return Err(anyhow::Error::msg(format!( + "Negative addend in section reloc: {}", + addend + ))); + } + addend as u32 + }; + find_section_symbol(obj_file, &symbol, addend as u64) } _ => Err(anyhow::Error::msg(format!( "Unhandled relocation symbol type {:?}", @@ -228,15 +295,26 @@ fn relocations_by_section( pub fn read(obj_path: &Path) -> Result { let bin_data = fs::read(obj_path)?; - let obj_file = object::File::parse(&*bin_data)?; + let obj_file = File::parse(&*bin_data)?; + let architecture = match obj_file.architecture() { + Architecture::PowerPc => ObjArchitecture::PowerPc, + Architecture::Mips => ObjArchitecture::Mips, + _ => { + return Err(anyhow::Error::msg(format!( + "Unsupported architecture: {:?}", + obj_file.architecture() + ))) + } + }; let mut result = ObjInfo { + architecture, path: obj_path.to_owned(), sections: filter_sections(&obj_file)?, common: common_symbols(&obj_file)?, }; for section in &mut result.sections { section.symbols = symbols_by_section(&obj_file, section)?; - section.relocations = relocations_by_section(&obj_file, section)?; + section.relocations = relocations_by_section(architecture, &obj_file, section)?; } Ok(result) } diff --git a/src/jobs/bindiff.rs b/src/jobs/bindiff.rs new file mode 100644 index 0000000..0cdad49 --- /dev/null +++ b/src/jobs/bindiff.rs @@ -0,0 +1,45 @@ +use std::sync::{mpsc::Receiver, Arc, RwLock}; + +use anyhow::{Error, Result}; + +use crate::{ + app::AppConfig, + diff::diff_objs, + elf, + jobs::{queue_job, update_status, Job, JobResult, JobState, Status}, + obj::ObjInfo, +}; + +pub struct BinDiffResult { + pub first_obj: ObjInfo, + pub second_obj: ObjInfo, +} + +fn run_build( + status: &Status, + cancel: Receiver<()>, + config: Arc>, +) -> Result> { + let config = config.read().map_err(|_| Error::msg("Failed to lock app config"))?.clone(); + let left_path = config.left_obj.as_ref().ok_or_else(|| Error::msg("Missing left obj path"))?; + let right_path = + config.right_obj.as_ref().ok_or_else(|| Error::msg("Missing right obj path"))?; + + update_status(status, "Loading left obj".to_string(), 0, 3, &cancel)?; + let mut left_obj = elf::read(left_path)?; + + update_status(status, "Loading right obj".to_string(), 1, 3, &cancel)?; + let mut right_obj = elf::read(right_path)?; + + update_status(status, "Performing diff".to_string(), 2, 3, &cancel)?; + diff_objs(&mut left_obj, &mut right_obj)?; + + update_status(status, "Complete".to_string(), 3, 3, &cancel)?; + Ok(Box::new(BinDiffResult { first_obj: left_obj, second_obj: right_obj })) +} + +pub fn queue_bindiff(config: Arc>) -> JobState { + queue_job(Job::BinDiff, move |status, cancel| { + run_build(status, cancel, config).map(JobResult::BinDiff) + }) +} diff --git a/src/jobs/mod.rs b/src/jobs/mod.rs index d44d082..e9af63e 100644 --- a/src/jobs/mod.rs +++ b/src/jobs/mod.rs @@ -9,13 +9,15 @@ use std::{ use anyhow::Result; -use crate::jobs::build::BuildResult; +use crate::jobs::{bindiff::BinDiffResult, build::BuildResult}; +pub mod bindiff; pub mod build; #[derive(Debug, Eq, PartialEq, Copy, Clone)] pub enum Job { Build, + BinDiff, } pub static JOB_ID: AtomicUsize = AtomicUsize::new(0); pub struct JobState { @@ -37,6 +39,7 @@ pub struct JobStatus { pub enum JobResult { None, Build(Box), + BinDiff(Box), } fn should_cancel(rx: &Receiver<()>) -> bool { diff --git a/src/obj.rs b/src/obj.rs index a33f50d..fb8222d 100644 --- a/src/obj.rs +++ b/src/obj.rs @@ -1,3 +1,6 @@ +pub mod mips; +pub mod ppc; + use std::path::PathBuf; use flagset::{flags, FlagSet}; @@ -31,9 +34,11 @@ pub struct ObjSection { } #[derive(Debug, Clone)] pub enum ObjInsArg { - Arg(ppc750cl::Argument), + PpcArg(ppc750cl::Argument), + MipsArg(String), Reloc, - RelocOffset, + RelocWithBase, + BranchOffset(i32), } #[derive(Debug, Copy, Clone)] pub struct ObjInsArgDiff { @@ -66,10 +71,13 @@ pub enum ObjInsDiffKind { } #[derive(Debug, Clone)] pub struct ObjIns { - pub ins: ppc750cl::Ins, + pub address: u32, + pub code: u32, + pub op: u8, pub mnemonic: String, pub args: Vec, pub reloc: Option, + pub branch_dest: Option, } #[derive(Debug, Clone, Default)] pub struct ObjInsDiff { @@ -88,6 +96,7 @@ pub struct ObjSymbol { pub name: String, pub demangled_name: Option, pub address: u64, + pub section_address: u64, pub size: u64, pub size_known: bool, pub flags: ObjSymbolFlagSet, @@ -97,8 +106,14 @@ pub struct ObjSymbol { pub instructions: Vec, pub match_percent: f32, } +#[derive(Debug, Copy, Clone)] +pub enum ObjArchitecture { + PowerPc, + Mips, +} #[derive(Debug, Clone)] pub struct ObjInfo { + pub architecture: ObjArchitecture, pub path: PathBuf, pub sections: Vec, pub common: Vec, @@ -116,6 +131,9 @@ pub enum ObjRelocKind { // PpcAddr14, PpcRel14, PpcEmbSda21, + Mips26, + MipsHi16, + MipsLo16, } #[derive(Debug, Clone)] pub struct ObjReloc { diff --git a/src/obj/mips.rs b/src/obj/mips.rs new file mode 100644 index 0000000..4374ece --- /dev/null +++ b/src/obj/mips.rs @@ -0,0 +1,74 @@ +use anyhow::Result; +use rabbitizer::{config_set_register_fpr_abi_names, Abi, Instruction, SimpleOperandType}; + +use crate::obj::{ObjIns, ObjInsArg, ObjReloc}; + +pub fn process_code( + data: &[u8], + start_address: u64, + end_address: u64, + relocs: &[ObjReloc], +) -> Result<(Vec, Vec)> { + config_set_register_fpr_abi_names(Abi::RABBITIZER_ABI_O32); + + let ins_count = data.len() / 4; + let mut ops = Vec::::with_capacity(ins_count); + let mut insts = Vec::::with_capacity(ins_count); + let mut cur_addr = start_address as u32; + for chunk in data.chunks_exact(4) { + let reloc = relocs.iter().find(|r| (r.address as u32 & !3) == cur_addr); + let code = u32::from_be_bytes(chunk.try_into()?); + let mut instruction = Instruction::new(code, cur_addr); + + let op = instruction.instr_id() as u8; + ops.push(op); + + let mnemonic = instruction.instr_id().get_opcode_name().unwrap_or_default().to_string(); + let is_branch = instruction.is_branch(); + let branch_offset = instruction.branch_offset(); + let branch_dest = + if is_branch { Some((cur_addr as i32 + branch_offset) as u32) } else { None }; + let args = instruction + .simple_operands() + .iter() + .map(|op| match op.kind { + SimpleOperandType::Imm | SimpleOperandType::Label => { + if is_branch { + ObjInsArg::BranchOffset(branch_offset) + } else if let Some(reloc) = reloc { + if matches!(&reloc.target_section, Some(s) if s == ".text") + && reloc.target.address > start_address + && reloc.target.address < end_address + { + // Inter-function reloc, convert to branch offset + ObjInsArg::BranchOffset(reloc.target.address as i32 - cur_addr as i32) + } else { + ObjInsArg::Reloc + } + } else { + ObjInsArg::MipsArg(op.disassembled.clone()) + } + } + SimpleOperandType::ImmBase => { + if reloc.is_some() { + ObjInsArg::RelocWithBase + } else { + ObjInsArg::MipsArg(op.disassembled.clone()) + } + } + _ => ObjInsArg::MipsArg(op.disassembled.clone()), + }) + .collect(); + insts.push(ObjIns { + address: cur_addr, + code, + op, + mnemonic, + args, + reloc: reloc.cloned(), + branch_dest, + }); + cur_addr += 4; + } + Ok((ops, insts)) +} diff --git a/src/obj/ppc.rs b/src/obj/ppc.rs new file mode 100644 index 0000000..55c32d0 --- /dev/null +++ b/src/obj/ppc.rs @@ -0,0 +1,89 @@ +use anyhow::Result; +use ppc750cl::{disasm_iter, Argument}; + +use crate::obj::{ObjIns, ObjInsArg, ObjReloc, ObjRelocKind}; + +// Relative relocation, can be Simm or BranchOffset +fn is_relative_arg(arg: &ObjInsArg) -> bool { + matches!(arg, ObjInsArg::PpcArg(Argument::Simm(_)) | ObjInsArg::BranchOffset(_)) +} + +// Relative or absolute relocation, can be Uimm, Simm or Offset +fn is_rel_abs_arg(arg: &ObjInsArg) -> bool { + matches!(arg, ObjInsArg::PpcArg(arg) if matches!(arg, Argument::Uimm(_) | Argument::Simm(_) | Argument::Offset(_))) +} + +fn is_offset_arg(arg: &ObjInsArg) -> bool { matches!(arg, ObjInsArg::PpcArg(Argument::Offset(_))) } + +pub fn process_code( + data: &[u8], + address: u64, + relocs: &[ObjReloc], +) -> Result<(Vec, Vec)> { + let ins_count = data.len() / 4; + let mut ops = Vec::::with_capacity(ins_count); + let mut insts = Vec::::with_capacity(ins_count); + for mut ins in disasm_iter(data, address as u32) { + let reloc = relocs.iter().find(|r| (r.address as u32 & !3) == ins.addr); + if let Some(reloc) = reloc { + // Zero out relocations + ins.code = match reloc.kind { + ObjRelocKind::PpcEmbSda21 => ins.code & !0x1FFFFF, + ObjRelocKind::PpcRel24 => ins.code & !0x3FFFFFC, + ObjRelocKind::PpcRel14 => ins.code & !0xFFFC, + ObjRelocKind::PpcAddr16Hi + | ObjRelocKind::PpcAddr16Ha + | ObjRelocKind::PpcAddr16Lo => ins.code & !0xFFFF, + _ => ins.code, + }; + } + let simplified = ins.simplified(); + let mut args: Vec = simplified + .args + .iter() + .map(|a| match a { + Argument::BranchDest(dest) => ObjInsArg::BranchOffset(dest.0), + _ => ObjInsArg::PpcArg(a.clone()), + }) + .collect(); + if let Some(reloc) = reloc { + match reloc.kind { + ObjRelocKind::PpcEmbSda21 => { + args = vec![args[0].clone(), ObjInsArg::Reloc]; + } + ObjRelocKind::PpcRel24 | ObjRelocKind::PpcRel14 => { + let arg = args + .iter_mut() + .rfind(|a| is_relative_arg(a)) + .ok_or_else(|| anyhow::Error::msg("Failed to locate rel arg for reloc"))?; + *arg = ObjInsArg::Reloc; + } + ObjRelocKind::PpcAddr16Hi + | ObjRelocKind::PpcAddr16Ha + | ObjRelocKind::PpcAddr16Lo => { + let arg = args.iter_mut().rfind(|a| is_rel_abs_arg(a)).ok_or_else(|| { + anyhow::Error::msg("Failed to locate rel/abs arg for reloc") + })?; + *arg = if is_offset_arg(arg) { + ObjInsArg::RelocWithBase + } else { + ObjInsArg::Reloc + }; + } + _ => {} + } + } + ops.push(simplified.ins.op as u8); + let suffix = simplified.ins.suffix(); + insts.push(ObjIns { + address: simplified.ins.addr, + code: simplified.ins.code, + mnemonic: format!("{}{}", simplified.mnemonic, suffix), + args, + reloc: reloc.cloned(), + op: 0, + branch_dest: None, + }); + } + Ok((ops, insts)) +} diff --git a/src/views/config.rs b/src/views/config.rs index c923dc0..32019a5 100644 --- a/src/views/config.rs +++ b/src/views/config.rs @@ -1,85 +1,124 @@ use std::sync::{Arc, RwLock}; use crate::{ - app::{AppConfig, ViewState}, - jobs::build::queue_build, + app::{AppConfig, DiffKind, ViewState}, + jobs::{bindiff::queue_bindiff, build::queue_build}, }; pub fn config_ui(ui: &mut egui::Ui, config: &Arc>, view_state: &mut ViewState) { let mut config_guard = config.write().unwrap(); - let AppConfig { project_dir, project_dir_change, build_asm_dir, build_src_dir, build_obj } = - &mut *config_guard; + let AppConfig { + project_dir, + project_dir_change, + build_asm_dir, + build_src_dir, + build_obj, + left_obj, + right_obj, + } = &mut *config_guard; - if ui.button("Select project dir").clicked() { - if let Some(path) = rfd::FileDialog::new().pick_folder() { - *project_dir = Some(path); - *project_dir_change = true; - *build_asm_dir = None; - *build_src_dir = None; - *build_obj = None; - } - } - if let Some(dir) = project_dir { - ui.label(dir.to_string_lossy()); - } - - ui.separator(); - - if let Some(project_dir) = project_dir { - if ui.button("Select asm build dir").clicked() { - if let Some(path) = rfd::FileDialog::new().set_directory(&project_dir).pick_folder() { - *build_asm_dir = Some(path); + if view_state.diff_kind == DiffKind::SplitObj { + if ui.button("Select project dir").clicked() { + if let Some(path) = rfd::FileDialog::new().pick_folder() { + *project_dir = Some(path); + *project_dir_change = true; + *build_asm_dir = None; + *build_src_dir = None; *build_obj = None; } } - if let Some(dir) = build_asm_dir { + if let Some(dir) = project_dir { ui.label(dir.to_string_lossy()); } ui.separator(); - if ui.button("Select src build dir").clicked() { - if let Some(path) = rfd::FileDialog::new().set_directory(&project_dir).pick_folder() { - *build_src_dir = Some(path); - *build_obj = None; + if let Some(project_dir) = project_dir { + if ui.button("Select asm build dir").clicked() { + if let Some(path) = rfd::FileDialog::new().set_directory(&project_dir).pick_folder() + { + *build_asm_dir = Some(path); + *build_obj = None; + } } - } - if let Some(dir) = build_src_dir { - ui.label(dir.to_string_lossy()); + if let Some(dir) = build_asm_dir { + ui.label(dir.to_string_lossy()); + } + + ui.separator(); + + if ui.button("Select src build dir").clicked() { + if let Some(path) = rfd::FileDialog::new().set_directory(&project_dir).pick_folder() + { + *build_src_dir = Some(path); + *build_obj = None; + } + } + if let Some(dir) = build_src_dir { + ui.label(dir.to_string_lossy()); + } + + ui.separator(); } - ui.separator(); - } - - if let Some(build_src_dir) = build_src_dir { - if ui.button("Select obj").clicked() { - if let Some(path) = rfd::FileDialog::new() - .set_directory(&build_src_dir) - .add_filter("Object file", &["o"]) - .pick_file() - { - let mut new_build_obj: Option = None; - if let Ok(obj_path) = path.strip_prefix(&build_src_dir) { - new_build_obj = Some(obj_path.display().to_string()); - } else if let Some(build_asm_dir) = build_asm_dir { - if let Ok(obj_path) = path.strip_prefix(&build_asm_dir) { + if let Some(build_src_dir) = build_src_dir { + if ui.button("Select obj").clicked() { + if let Some(path) = rfd::FileDialog::new() + .set_directory(&build_src_dir) + .add_filter("Object file", &["o", "elf"]) + .pick_file() + { + let mut new_build_obj: Option = None; + if let Ok(obj_path) = path.strip_prefix(&build_src_dir) { new_build_obj = Some(obj_path.display().to_string()); + } else if let Some(build_asm_dir) = build_asm_dir { + if let Ok(obj_path) = path.strip_prefix(&build_asm_dir) { + new_build_obj = Some(obj_path.display().to_string()); + } + } + if let Some(new_build_obj) = new_build_obj { + *build_obj = Some(new_build_obj.clone()); + view_state.jobs.push(queue_build(new_build_obj, config.clone())); } } - if let Some(new_build_obj) = new_build_obj { - *build_obj = Some(new_build_obj.clone()); - view_state.jobs.push(queue_build(new_build_obj, config.clone())); + } + if let Some(build_obj) = build_obj { + ui.label(&*build_obj); + if ui.button("Build").clicked() { + view_state.jobs.push(queue_build(build_obj.clone(), config.clone())); } } + + ui.separator(); } - if let Some(build_obj) = build_obj { - ui.label(&*build_obj); - if ui.button("Build").clicked() { - view_state.jobs.push(queue_build(build_obj.clone(), config.clone())); + } else if view_state.diff_kind == DiffKind::WholeBinary { + if ui.button("Select left obj").clicked() { + if let Some(path) = + rfd::FileDialog::new().add_filter("Object file", &["o", "elf"]).pick_file() + { + *left_obj = Some(path); } } + if let Some(obj) = left_obj { + ui.label(obj.to_string_lossy()); + } - ui.separator(); + if ui.button("Select right obj").clicked() { + if let Some(path) = + rfd::FileDialog::new().add_filter("Object file", &["o", "elf"]).pick_file() + { + *right_obj = Some(path); + } + } + if let Some(obj) = right_obj { + ui.label(obj.to_string_lossy()); + } + + if let (Some(_), Some(_)) = (left_obj, right_obj) { + if ui.button("Build").clicked() { + view_state.jobs.push(queue_bindiff(config.clone())); + } + } } ui.checkbox(&mut view_state.reverse_fn_order, "Reverse function order (deferred)"); diff --git a/src/views/function_diff.rs b/src/views/function_diff.rs index 0e8929b..18b56ff 100644 --- a/src/views/function_diff.rs +++ b/src/views/function_diff.rs @@ -25,13 +25,39 @@ fn write_text(str: &str, color: Color32, job: &mut LayoutJob) { fn write_reloc(reloc: &ObjReloc, job: &mut LayoutJob) { let name = reloc.target.demangled_name.as_ref().unwrap_or(&reloc.target.name); - write_text(name, Color32::LIGHT_GRAY, job); match reloc.kind { - ObjRelocKind::PpcAddr16Lo => write_text("@l", Color32::GRAY, job), - ObjRelocKind::PpcAddr16Hi => write_text("@h", Color32::GRAY, job), - ObjRelocKind::PpcAddr16Ha => write_text("@ha", Color32::GRAY, job), - ObjRelocKind::PpcEmbSda21 => write_text("@sda21", Color32::GRAY, job), - _ => {} + ObjRelocKind::PpcAddr16Lo => { + write_text(name, Color32::LIGHT_GRAY, job); + write_text("@l", Color32::GRAY, job); + } + ObjRelocKind::PpcAddr16Hi => { + write_text(name, Color32::LIGHT_GRAY, job); + write_text("@h", Color32::GRAY, job); + } + ObjRelocKind::PpcAddr16Ha => { + write_text(name, Color32::LIGHT_GRAY, job); + write_text("@ha", Color32::GRAY, job); + } + ObjRelocKind::PpcEmbSda21 => { + write_text(name, Color32::LIGHT_GRAY, job); + write_text("@sda21", Color32::GRAY, job); + } + ObjRelocKind::MipsHi16 => { + write_text("%hi(", Color32::GRAY, job); + write_text(name, Color32::LIGHT_GRAY, job); + write_text(")", Color32::GRAY, job); + } + ObjRelocKind::MipsLo16 => { + write_text("%lo(", Color32::GRAY, job); + write_text(name, Color32::LIGHT_GRAY, job); + write_text(")", Color32::GRAY, job); + } + ObjRelocKind::Absolute + | ObjRelocKind::PpcRel24 + | ObjRelocKind::PpcRel14 + | ObjRelocKind::Mips26 => { + write_text(name, Color32::LIGHT_GRAY, job); + } }; } @@ -51,7 +77,7 @@ fn write_ins( ObjInsDiffKind::Insert => Color32::GREEN, }; write_text( - &ins.mnemonic, + &format!("{:<11}", ins.mnemonic), match diff_kind { ObjInsDiffKind::OpMismatch => Color32::LIGHT_BLUE, _ => base_color, @@ -72,17 +98,13 @@ fn write_ins( base_color }; match arg { - ObjInsArg::Arg(arg) => match arg { + ObjInsArg::PpcArg(arg) => match arg { Argument::Offset(val) => { write_text(&format!("{}", val), color, job); write_text("(", base_color, job); writing_offset = true; continue; } - Argument::BranchDest(dest) => { - let addr = dest.0 + ins.ins.addr as i32 - base_addr as i32; - write_text(&format!("{:x}", addr), color, job); - } Argument::Uimm(_) | Argument::Simm(_) => { write_text(&format!("{}", arg), color, job); } @@ -93,12 +115,19 @@ fn write_ins( ObjInsArg::Reloc => { write_reloc(ins.reloc.as_ref().unwrap(), job); } - ObjInsArg::RelocOffset => { + ObjInsArg::RelocWithBase => { write_reloc(ins.reloc.as_ref().unwrap(), job); write_text("(", base_color, job); writing_offset = true; continue; } + ObjInsArg::MipsArg(str) => { + write_text(str.strip_prefix('$').unwrap_or(str), color, job); + } + ObjInsArg::BranchOffset(offset) => { + let addr = offset + ins.address as i32 - base_addr as i32; + write_text(&format!("{:x}", addr), color, job); + } } if writing_offset { write_text(")", base_color, job); @@ -112,10 +141,10 @@ fn ins_hover_ui(ui: &mut egui::Ui, ins: &ObjIns) { ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace); ui.style_mut().wrap = Some(false); - ui.label(format!("{:02X?}", ins.ins.code.to_be_bytes())); + ui.label(format!("{:02X?}", ins.code.to_be_bytes())); for arg in &ins.args { - if let ObjInsArg::Arg(arg) = arg { + if let ObjInsArg::PpcArg(arg) = arg { match arg { Argument::Uimm(v) => { ui.label(format!("{} == {}", v, v.0)); @@ -153,7 +182,7 @@ fn ins_context_menu(ui: &mut egui::Ui, ins: &ObjIns) { // if ui.button("Copy hex").clicked() {} for arg in &ins.args { - if let ObjInsArg::Arg(arg) = arg { + if let ObjInsArg::PpcArg(arg) = arg { match arg { Argument::Uimm(v) => { if ui.button(format!("Copy \"{}\"", v)).clicked() { @@ -236,7 +265,7 @@ fn asm_row_ui(ui: &mut egui::Ui, ins_diff: &ObjInsDiff, symbol: &ObjSymbol) { ObjInsDiffKind::Insert => Color32::GREEN, }; write_text( - &format!("{:<6}", format!("{:x}:", ins.ins.addr - symbol.address as u32)), + &format!("{:<6}", format!("{:x}:", ins.address - symbol.address as u32)), base_color, &mut job, ); diff --git a/src/views/symbol_diff.rs b/src/views/symbol_diff.rs index 862d36c..6a65850 100644 --- a/src/views/symbol_diff.rs +++ b/src/views/symbol_diff.rs @@ -66,21 +66,28 @@ fn symbol_ui( ..Default::default() }); } - job.append("] (", 0.0, TextFormat { - font_id: font_id.clone(), - color: Color32::GRAY, - ..Default::default() - }); - job.append(&format!("{:.0}%", symbol.match_percent), 0.0, TextFormat { - font_id: font_id.clone(), - color: match_color_for_symbol(symbol), - ..Default::default() - }); - job.append(") ", 0.0, TextFormat { + job.append("] ", 0.0, TextFormat { font_id: font_id.clone(), color: Color32::GRAY, ..Default::default() }); + if symbol.match_percent > 0.0 { + job.append("(", 0.0, TextFormat { + font_id: font_id.clone(), + color: Color32::GRAY, + ..Default::default() + }); + job.append(&format!("{:.0}%", symbol.match_percent), 0.0, TextFormat { + font_id: font_id.clone(), + color: match_color_for_symbol(symbol), + ..Default::default() + }); + job.append(") ", 0.0, TextFormat { + font_id: font_id.clone(), + color: Color32::GRAY, + ..Default::default() + }); + } job.append(name, 0.0, TextFormat { font_id, color: Color32::WHITE, ..Default::default() }); let response = SelectableLabel::new(selected, job).ui(ui); if response.clicked() {