From bbe49eb8b4d3e8abc762b8ffd45ab981c05af278 Mon Sep 17 00:00:00 2001 From: Luke Street Date: Sat, 16 Mar 2024 23:30:27 -0600 Subject: [PATCH] Initial x86 support Includes a bit of work to make adding new architectures easier in the future --- Cargo.lock | 38 ++- objdiff-cli/src/cmd/diff.rs | 16 +- objdiff-cli/src/cmd/report.rs | 6 +- objdiff-core/Cargo.toml | 14 +- objdiff-core/src/diff/code.rs | 115 ++++++-- objdiff-core/src/diff/display.rs | 126 ++------- objdiff-core/src/diff/mod.rs | 11 +- objdiff-core/src/obj/mips.rs | 86 ++++-- objdiff-core/src/obj/mod.rs | 33 ++- objdiff-core/src/obj/ppc.rs | 154 +++++++---- objdiff-core/src/obj/{elf.rs => read.rs} | 66 ++++- objdiff-core/src/obj/x86.rs | 334 +++++++++++++++++++++++ objdiff-core/src/util.rs | 4 +- objdiff-gui/src/app.rs | 36 ++- objdiff-gui/src/jobs/objdiff.rs | 13 +- objdiff-gui/src/views/config.rs | 38 ++- objdiff-gui/src/views/function_diff.rs | 43 ++- 17 files changed, 844 insertions(+), 289 deletions(-) rename objdiff-core/src/obj/{elf.rs => read.rs} (83%) create mode 100644 objdiff-core/src/obj/x86.rs diff --git a/Cargo.lock b/Cargo.lock index 0dc20c6..a0da823 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1257,7 +1257,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" dependencies = [ - "libloading 0.8.1", + "libloading 0.8.3", ] [[package]] @@ -2017,7 +2017,7 @@ dependencies = [ "glutin_glx_sys", "glutin_wgl_sys", "icrate", - "libloading 0.8.1", + "libloading 0.8.3", "objc2 0.4.1", "once_cell", "raw-window-handle 0.5.2", @@ -2157,7 +2157,7 @@ dependencies = [ "bitflags 2.4.2", "com", "libc", - "libloading 0.8.1", + "libloading 0.8.3", "thiserror", "widestring", "winapi", @@ -2281,6 +2281,15 @@ dependencies = [ "tokio-native-tls", ] +[[package]] +name = "iced-x86" +version = "1.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c447cff8c7f384a7d4f741cfcff32f75f3ad02b406432e8d6c878d56b1edf6b" +dependencies = [ + "lazy_static", +] + [[package]] name = "icrate" version = "0.0.4" @@ -2458,7 +2467,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76" dependencies = [ "libc", - "libloading 0.8.1", + "libloading 0.8.3", "pkg-config", ] @@ -2512,12 +2521,12 @@ dependencies = [ [[package]] name = "libloading" -version = "0.8.1" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" +checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" dependencies = [ "cfg-if", - "windows-sys 0.48.0", + "windows-targets 0.52.4", ] [[package]] @@ -2699,6 +2708,15 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "msvc-demangler" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2588c982e3a7fbfbd73b21f824cacc43fc6392a1103c709ffd6001c0bf33fdb3" +dependencies = [ + "bitflags 2.4.2", +] + [[package]] name = "naga" version = "0.19.0" @@ -3019,8 +3037,10 @@ dependencies = [ "flagset", "gimli", "globset", + "iced-x86", "log", "memmap2", + "msvc-demangler", "num-traits", "object 0.34.0", "ppc750cl", @@ -5050,7 +5070,7 @@ dependencies = [ "js-sys", "khronos-egl", "libc", - "libloading 0.8.1", + "libloading 0.8.3", "log", "metal", "naga", @@ -5497,7 +5517,7 @@ dependencies = [ "as-raw-xcb-connection", "gethostname", "libc", - "libloading 0.8.1", + "libloading 0.8.3", "once_cell", "rustix 0.38.31", "x11rb-protocol", diff --git a/objdiff-cli/src/cmd/diff.rs b/objdiff-cli/src/cmd/diff.rs index 3adee7d..4bb001d 100644 --- a/objdiff-cli/src/cmd/diff.rs +++ b/objdiff-cli/src/cmd/diff.rs @@ -115,7 +115,7 @@ pub fn run(args: Args) -> Result<()> { if obj .target_path .as_deref() - .map(|o| obj::elf::has_function(o, &args.symbol)) + .map(|o| obj::read::has_function(o, &args.symbol)) .transpose()? .unwrap_or(false) { @@ -523,7 +523,7 @@ impl FunctionDiffUi { rect: Rect, highlight: &HighlightKind, ) -> Option { - let base_addr = symbol.address as u32; + let base_addr = symbol.address; let mut new_highlight = None; for (y, ins_diff) in symbol.instructions.iter().skip(self.scroll_y).take(rect.height as usize).enumerate() @@ -572,7 +572,7 @@ impl FunctionDiffUi { base_color = COLOR_ROTATION[diff.idx % COLOR_ROTATION.len()] } } - DiffText::BranchTarget(addr) => { + DiffText::BranchDest(addr) => { label_text = format!("{addr:x}"); } DiffText::Symbol(sym) => { @@ -633,14 +633,18 @@ impl FunctionDiffUi { let mut target = self .target_path .as_deref() - .map(|p| obj::elf::read(p).with_context(|| format!("Loading {}", p.display()))) + .map(|p| obj::read::read(p).with_context(|| format!("Loading {}", p.display()))) .transpose()?; let mut base = self .base_path .as_deref() - .map(|p| obj::elf::read(p).with_context(|| format!("Loading {}", p.display()))) + .map(|p| obj::read::read(p).with_context(|| format!("Loading {}", p.display()))) .transpose()?; - let config = diff::DiffObjConfig { relax_reloc_diffs: self.relax_reloc_diffs }; + let config = diff::DiffObjConfig { + relax_reloc_diffs: self.relax_reloc_diffs, + space_between_args: true, // TODO + x86_formatter: Default::default(), // TODO + }; diff::diff_objs(&config, target.as_mut(), base.as_mut())?; let left_sym = target.as_ref().and_then(|o| find_function(o, &self.symbol_name)); diff --git a/objdiff-cli/src/cmd/report.rs b/objdiff-cli/src/cmd/report.rs index 315b0cd..80181db 100644 --- a/objdiff-cli/src/cmd/report.rs +++ b/objdiff-cli/src/cmd/report.rs @@ -219,14 +219,14 @@ fn report_object( let mut target = object .target_path .as_ref() - .map(|p| obj::elf::read(p).with_context(|| format!("Failed to open {}", p.display()))) + .map(|p| obj::read::read(p).with_context(|| format!("Failed to open {}", p.display()))) .transpose()?; let mut base = object .base_path .as_ref() - .map(|p| obj::elf::read(p).with_context(|| format!("Failed to open {}", p.display()))) + .map(|p| obj::read::read(p).with_context(|| format!("Failed to open {}", p.display()))) .transpose()?; - let config = diff::DiffObjConfig { relax_reloc_diffs: true }; + let config = diff::DiffObjConfig { relax_reloc_diffs: true, ..Default::default() }; diff::diff_objs(&config, target.as_mut(), base.as_mut())?; let mut unit = ReportUnit { name: object.name().to_string(), diff --git a/objdiff-core/Cargo.toml b/objdiff-core/Cargo.toml index f8428e7..a827727 100644 --- a/objdiff-core/Cargo.toml +++ b/objdiff-core/Cargo.toml @@ -12,12 +12,13 @@ A local diffing tool for decompilation projects. """ [features] -all = ["config", "dwarf", "mips", "ppc"] +all = ["config", "dwarf", "mips", "ppc", "x86"] any-arch = [] # Implicit, used to check if any arch is enabled config = [] dwarf = ["gimli"] mips = ["any-arch", "rabbitizer"] ppc = ["any-arch", "cwdemangle", "ppc750cl"] +x86 = ["any-arch", "iced-x86", "msvc-demangler"] [dependencies] anyhow = "1.0.81" @@ -29,7 +30,7 @@ gimli = { version = "0.28.1", default-features = false, features = ["read-all"], log = "0.4.21" memmap2 = "0.9.4" num-traits = "0.2.18" -object = { version = "0.34.0", features = ["read_core", "std", "elf"], default-features = false } +object = { version = "0.34.0", features = ["read_core", "std", "elf", "pe"], default-features = false } ppc750cl = { git = "https://github.com/encounter/ppc750cl", rev = "4a2bbbc6f84dcb76255ab6f3595a8d4a0ce96618", optional = true } rabbitizer = { version = "1.9.2", optional = true } serde = { version = "1", features = ["derive"] } @@ -40,3 +41,12 @@ globset = { version = "0.4.14", features = ["serde1"] } semver = "1.0.22" serde_json = "1.0.114" serde_yaml = "0.9.32" + +# x86 +msvc-demangler = { version = "0.10.0", optional = true } +[dependencies.iced-x86] +version = "1.21.0" +#default-features = false +#features = ["std", "decoder", "intel"] +features = ["exhaustive_enums"] +optional = true diff --git a/objdiff-core/src/diff/code.rs b/objdiff-core/src/diff/code.rs index e79e9b4..1267d72 100644 --- a/objdiff-core/src/diff/code.rs +++ b/objdiff-core/src/diff/code.rs @@ -17,6 +17,7 @@ use crate::{ }; pub fn no_diff_code( + config: &DiffObjConfig, arch: ObjArchitecture, data: &[u8], symbol: &mut ObjSymbol, @@ -28,16 +29,25 @@ pub fn no_diff_code( let out: ProcessCodeResult = match arch { #[cfg(feature = "ppc")] ObjArchitecture::PowerPc => { - obj::ppc::process_code(code, symbol.address, relocs, line_info)? + obj::ppc::process_code(config, code, symbol.address, relocs, line_info)? } #[cfg(feature = "mips")] ObjArchitecture::Mips => obj::mips::process_code( + config, code, symbol.address, symbol.address + symbol.size, relocs, line_info, )?, + #[cfg(feature = "x86")] + ObjArchitecture::X86_32 => { + obj::x86::process_code(config, code, 32, symbol.address, relocs, line_info)? + } + #[cfg(feature = "x86")] + ObjArchitecture::X86_64 => { + obj::x86::process_code(config, code, 64, symbol.address, relocs, line_info)? + } }; let mut diff = Vec::::new(); @@ -69,8 +79,15 @@ pub fn diff_code( let (left_out, right_out) = match arch { #[cfg(feature = "ppc")] ObjArchitecture::PowerPc => ( - obj::ppc::process_code(left_code, left_symbol.address, left_relocs, left_line_info)?, obj::ppc::process_code( + config, + left_code, + left_symbol.address, + left_relocs, + left_line_info, + )?, + obj::ppc::process_code( + config, right_code, right_symbol.address, right_relocs, @@ -80,6 +97,7 @@ pub fn diff_code( #[cfg(feature = "mips")] ObjArchitecture::Mips => ( obj::mips::process_code( + config, left_code, left_symbol.address, left_symbol.address + left_symbol.size, @@ -87,6 +105,7 @@ pub fn diff_code( left_line_info, )?, obj::mips::process_code( + config, right_code, right_symbol.address, left_symbol.address + left_symbol.size, @@ -94,6 +113,44 @@ pub fn diff_code( right_line_info, )?, ), + #[cfg(feature = "x86")] + ObjArchitecture::X86_32 => ( + obj::x86::process_code( + config, + left_code, + 32, + left_symbol.address, + left_relocs, + left_line_info, + )?, + obj::x86::process_code( + config, + right_code, + 32, + right_symbol.address, + right_relocs, + right_line_info, + )?, + ), + #[cfg(feature = "x86")] + ObjArchitecture::X86_64 => ( + obj::x86::process_code( + config, + left_code, + 64, + left_symbol.address, + left_relocs, + left_line_info, + )?, + obj::x86::process_code( + config, + right_code, + 64, + right_symbol.address, + right_relocs, + right_line_info, + )?, + ), }; let mut left_diff = Vec::::new(); @@ -183,7 +240,7 @@ fn diff_instructions( fn resolve_branches(vec: &mut [ObjInsDiff]) { let mut branch_idx = 0usize; // Map addresses to indices - let mut addr_map = BTreeMap::::new(); + 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.address, i); @@ -193,15 +250,7 @@ fn resolve_branches(vec: &mut [ObjInsDiff]) { 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 - .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(ins_idx) = ins.branch_dest.and_then(|a| addr_map.get(&a)) { if let Some(branch) = branches.get_mut(ins_idx) { ins_diff.branch_to = Some(ObjInsBranchTo { ins_idx: *ins_idx, branch_idx: branch.branch_idx }); @@ -262,8 +311,12 @@ fn arg_eq( right_diff: &ObjInsDiff, ) -> bool { return match left { - ObjInsArg::Arg(l) | ObjInsArg::ArgWithBase(l) => match right { - ObjInsArg::Arg(r) | ObjInsArg::ArgWithBase(r) => l == r, + ObjInsArg::PlainText(l) => match right { + ObjInsArg::PlainText(r) => l == r, + _ => false, + }, + ObjInsArg::Arg(l) => match right { + ObjInsArg::Arg(r) => l == r, _ => false, }, ObjInsArg::Reloc => { @@ -274,15 +327,7 @@ fn arg_eq( right_diff.ins.as_ref().and_then(|i| i.reloc.as_ref()), ) } - ObjInsArg::RelocWithBase => { - matches!(right, ObjInsArg::RelocWithBase) - && reloc_eq( - config, - 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::BranchOffset(_) => { + ObjInsArg::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) @@ -314,7 +359,15 @@ 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.op != right_ins.op { + if left_ins.args.len() != right_ins.args.len() + || left_ins.op != right_ins.op + // Check if any PlainText segments differ (punctuation and spacing) + // This indicates a more significant difference than a simple arg mismatch + || !left_ins.args.iter().zip(&right_ins.args).all(|(a, b)| match (a, b) { + (ObjInsArg::PlainText(l), ObjInsArg::PlainText(r)) => l == r, + _ => true, + }) + { // Totally different op result.kind = ObjInsDiffKind::Replace; state.diff_count += 1; @@ -335,9 +388,10 @@ fn compare_ins( state.diff_count += 1; } let a_str = match a { - ObjInsArg::Arg(arg) | ObjInsArg::ArgWithBase(arg) => arg.to_string(), - ObjInsArg::Reloc | ObjInsArg::RelocWithBase => String::new(), - ObjInsArg::BranchOffset(arg) => format!("{arg}"), + ObjInsArg::PlainText(arg) => arg.clone(), + ObjInsArg::Arg(arg) => arg.to_string(), + ObjInsArg::Reloc => String::new(), + ObjInsArg::BranchDest(arg) => format!("{arg}"), }; let a_diff = if let Some(idx) = state.left_args_idx.get(&a_str) { ObjInsArgDiff { idx: *idx } @@ -348,9 +402,10 @@ fn compare_ins( ObjInsArgDiff { idx } }; let b_str = match b { - ObjInsArg::Arg(arg) | ObjInsArg::ArgWithBase(arg) => arg.to_string(), - ObjInsArg::Reloc | ObjInsArg::RelocWithBase => String::new(), - ObjInsArg::BranchOffset(arg) => format!("{arg}"), + ObjInsArg::PlainText(arg) => arg.clone(), + ObjInsArg::Arg(arg) => arg.to_string(), + ObjInsArg::Reloc => String::new(), + ObjInsArg::BranchDest(arg) => format!("{arg}"), }; let b_diff = if let Some(idx) = state.right_args_idx.get(&b_str) { ObjInsArgDiff { idx: *idx } diff --git a/objdiff-core/src/diff/display.rs b/objdiff-core/src/diff/display.rs index 2d338ab..b85d632 100644 --- a/objdiff-core/src/diff/display.rs +++ b/objdiff-core/src/diff/display.rs @@ -1,8 +1,6 @@ use std::cmp::Ordering; -use crate::obj::{ - ObjInsArg, ObjInsArgDiff, ObjInsArgValue, ObjInsDiff, ObjReloc, ObjRelocKind, ObjSymbol, -}; +use crate::obj::{ObjInsArg, ObjInsArgDiff, ObjInsArgValue, ObjInsDiff, ObjReloc, ObjSymbol}; #[derive(Debug, Clone)] pub enum DiffText<'a> { @@ -13,13 +11,13 @@ pub enum DiffText<'a> { /// Line number Line(usize), /// Instruction address - Address(u32), + Address(u64), /// Instruction mnemonic - Opcode(&'a str, u8), + Opcode(&'a str, u16), /// Instruction argument Argument(&'a ObjInsArgValue, Option<&'a ObjInsArgDiff>), - /// Branch target - BranchTarget(u32), + /// Branch destination + BranchDest(u64), /// Symbol name Symbol(&'a ObjSymbol), /// Number of spaces @@ -32,15 +30,15 @@ pub enum DiffText<'a> { pub enum HighlightKind { #[default] None, - Opcode(u8), + Opcode(u16), Arg(ObjInsArgValue), Symbol(String), - Address(u32), + Address(u64), } pub fn display_diff( ins_diff: &ObjInsDiff, - base_addr: u32, + base_addr: u64, mut cb: impl FnMut(DiffText) -> Result<(), E>, ) -> Result<(), E> { let Some(ins) = &ins_diff.ins else { @@ -57,43 +55,25 @@ pub fn display_diff( cb(DiffText::Spacing(4))?; } cb(DiffText::Opcode(&ins.mnemonic, ins.op))?; - let mut writing_offset = false; for (i, arg) in ins.args.iter().enumerate() { if i == 0 { cb(DiffText::Spacing(1))?; } - if i > 0 && !writing_offset { - cb(DiffText::Basic(", "))?; - } - let mut new_writing_offset = false; match arg { + ObjInsArg::PlainText(s) => { + cb(DiffText::Basic(s))?; + } ObjInsArg::Arg(v) => { let diff = ins_diff.arg_diff.get(i).and_then(|o| o.as_ref()); cb(DiffText::Argument(v, diff))?; } - ObjInsArg::ArgWithBase(v) => { - let diff = ins_diff.arg_diff.get(i).and_then(|o| o.as_ref()); - cb(DiffText::Argument(v, diff))?; - cb(DiffText::Basic("("))?; - new_writing_offset = true; - } ObjInsArg::Reloc => { - display_reloc(ins.reloc.as_ref().unwrap(), &mut cb)?; + display_reloc_name(ins.reloc.as_ref().unwrap(), &mut cb)?; } - ObjInsArg::RelocWithBase => { - display_reloc(ins.reloc.as_ref().unwrap(), &mut cb)?; - cb(DiffText::Basic("("))?; - new_writing_offset = true; - } - ObjInsArg::BranchOffset(offset) => { - let addr = offset + ins.address as i32 - base_addr as i32; - cb(DiffText::BranchTarget(addr as u32))?; + ObjInsArg::BranchDest(dest) => { + cb(DiffText::BranchDest(*dest))?; } } - if writing_offset { - cb(DiffText::Basic(")"))?; - } - writing_offset = new_writing_offset; } if let Some(branch) = &ins_diff.branch_to { cb(DiffText::BasicColor(" ~>", branch.branch_idx))?; @@ -114,87 +94,13 @@ fn display_reloc_name( } } -fn display_reloc( - reloc: &ObjReloc, - mut cb: impl FnMut(DiffText) -> Result<(), E>, -) -> Result<(), E> { - match reloc.kind { - #[cfg(feature = "ppc")] - ObjRelocKind::PpcAddr16Lo => { - display_reloc_name(reloc, &mut cb)?; - cb(DiffText::Basic("@l"))?; - } - #[cfg(feature = "ppc")] - ObjRelocKind::PpcAddr16Hi => { - display_reloc_name(reloc, &mut cb)?; - cb(DiffText::Basic("@h"))?; - } - #[cfg(feature = "ppc")] - ObjRelocKind::PpcAddr16Ha => { - display_reloc_name(reloc, &mut cb)?; - cb(DiffText::Basic("@ha"))?; - } - #[cfg(feature = "ppc")] - ObjRelocKind::PpcEmbSda21 => { - display_reloc_name(reloc, &mut cb)?; - cb(DiffText::Basic("@sda21"))?; - } - #[cfg(feature = "ppc")] - ObjRelocKind::PpcRel24 | ObjRelocKind::PpcRel14 => { - display_reloc_name(reloc, &mut cb)?; - } - #[cfg(feature = "mips")] - ObjRelocKind::MipsHi16 => { - cb(DiffText::Basic("%hi("))?; - display_reloc_name(reloc, &mut cb)?; - cb(DiffText::Basic(")"))?; - } - #[cfg(feature = "mips")] - ObjRelocKind::MipsLo16 => { - cb(DiffText::Basic("%lo("))?; - display_reloc_name(reloc, &mut cb)?; - cb(DiffText::Basic(")"))?; - } - #[cfg(feature = "mips")] - ObjRelocKind::MipsGot16 => { - cb(DiffText::Basic("%got("))?; - display_reloc_name(reloc, &mut cb)?; - cb(DiffText::Basic(")"))?; - } - #[cfg(feature = "mips")] - ObjRelocKind::MipsCall16 => { - cb(DiffText::Basic("%call16("))?; - display_reloc_name(reloc, &mut cb)?; - cb(DiffText::Basic(")"))?; - } - #[cfg(feature = "mips")] - ObjRelocKind::MipsGpRel16 => { - cb(DiffText::Basic("%gp_rel("))?; - display_reloc_name(reloc, &mut cb)?; - cb(DiffText::Basic(")"))?; - } - #[cfg(feature = "mips")] - ObjRelocKind::Mips26 => { - display_reloc_name(reloc, &mut cb)?; - } - #[cfg(feature = "mips")] - ObjRelocKind::MipsGpRel32 => { - todo!("unimplemented: mips gp_rel32"); - } - ObjRelocKind::Absolute => { - cb(DiffText::Basic("[INVALID]"))?; - } - } - Ok(()) -} - impl PartialEq> for HighlightKind { fn eq(&self, other: &DiffText) -> bool { match (self, other) { (HighlightKind::Opcode(a), DiffText::Opcode(_, b)) => a == b, (HighlightKind::Arg(a), DiffText::Argument(b, _)) => a.loose_eq(b), (HighlightKind::Symbol(a), DiffText::Symbol(b)) => a == &b.name, - (HighlightKind::Address(a), DiffText::Address(b) | DiffText::BranchTarget(b)) => a == b, + (HighlightKind::Address(a), DiffText::Address(b) | DiffText::BranchDest(b)) => a == b, _ => false, } } @@ -210,7 +116,7 @@ impl From> for HighlightKind { DiffText::Opcode(_, op) => HighlightKind::Opcode(op), DiffText::Argument(arg, _) => HighlightKind::Arg(arg.clone()), DiffText::Symbol(sym) => HighlightKind::Symbol(sym.name.to_string()), - DiffText::Address(addr) | DiffText::BranchTarget(addr) => HighlightKind::Address(addr), + DiffText::Address(addr) | DiffText::BranchDest(addr) => HighlightKind::Address(addr), _ => HighlightKind::None, } } diff --git a/objdiff-core/src/diff/mod.rs b/objdiff-core/src/diff/mod.rs index bd7b20d..268bd07 100644 --- a/objdiff-core/src/diff/mod.rs +++ b/objdiff-core/src/diff/mod.rs @@ -9,16 +9,19 @@ use crate::{ code::{diff_code, find_section_and_symbol, no_diff_code}, data::{diff_bss_symbols, diff_data, no_diff_data}, }, - obj::{ObjInfo, ObjIns, ObjSectionKind}, + obj::{x86::X86Formatter, ObjInfo, ObjIns, ObjSectionKind}, }; -#[derive(Debug, Clone, Default, Eq, PartialEq)] +#[derive(Debug, Clone, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(default)] pub struct DiffObjConfig { pub relax_reloc_diffs: bool, + pub space_between_args: bool, + pub x86_formatter: X86Formatter, } pub struct ProcessCodeResult { - pub ops: Vec, + pub ops: Vec, pub insts: Vec, } @@ -54,6 +57,7 @@ pub fn diff_objs( )?; } else { no_diff_code( + config, left.architecture, &left_section.data, left_symbol, @@ -82,6 +86,7 @@ pub fn diff_objs( for right_symbol in &mut right_section.symbols { if right_symbol.instructions.is_empty() { no_diff_code( + config, right.architecture, &right_section.data, right_symbol, diff --git a/objdiff-core/src/obj/mips.rs b/objdiff-core/src/obj/mips.rs index e949635..8133e74 100644 --- a/objdiff-core/src/obj/mips.rs +++ b/objdiff-core/src/obj/mips.rs @@ -4,8 +4,8 @@ use anyhow::Result; use rabbitizer::{config, Abi, InstrCategory, Instruction, OperandType}; use crate::{ - diff::ProcessCodeResult, - obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc}, + diff::{DiffObjConfig, ProcessCodeResult}, + obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjRelocKind}, }; fn configure_rabbitizer() { @@ -15,6 +15,7 @@ fn configure_rabbitizer() { } pub fn process_code( + config: &DiffObjConfig, data: &[u8], start_address: u64, end_address: u64, @@ -24,7 +25,7 @@ pub fn process_code( configure_rabbitizer(); let ins_count = data.len() / 4; - let mut ops = Vec::::with_capacity(ins_count); + 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) { @@ -32,35 +33,43 @@ pub fn process_code( let code = u32::from_be_bytes(chunk.try_into()?); let instruction = Instruction::new(code, cur_addr, InstrCategory::CPU); - let op = instruction.unique_id as u8; + let op = instruction.unique_id as u16; ops.push(op); let mnemonic = instruction.opcode_name().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 branch_dest = if is_branch { + cur_addr.checked_add_signed(branch_offset).map(|a| a as u64) + } else { + None + }; let operands = instruction.get_operands_slice(); let mut args = Vec::with_capacity(operands.len() + 1); - for op in operands { + for (idx, op) in operands.iter().enumerate() { + if idx > 0 { + if config.space_between_args { + args.push(ObjInsArg::PlainText(", ".to_string())); + } else { + args.push(ObjInsArg::PlainText(",".to_string())); + } + } + match op { OperandType::cpu_immediate | OperandType::cpu_label | OperandType::cpu_branch_target_label => { - if is_branch { - args.push(ObjInsArg::BranchOffset(branch_offset)); + if let Some(branch_dest) = branch_dest { + args.push(ObjInsArg::BranchDest(branch_dest)); } 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 - args.push(ObjInsArg::BranchOffset( - reloc.target.address as i32 - cur_addr as i32, - )); + args.push(ObjInsArg::BranchDest(reloc.target.address)); } else { - args.push(ObjInsArg::Reloc); + push_reloc(&mut args, reloc); } } else { args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque( @@ -69,16 +78,18 @@ pub fn process_code( } } OperandType::cpu_immediate_base => { - if reloc.is_some() { - args.push(ObjInsArg::RelocWithBase); + if let Some(reloc) = reloc { + push_reloc(&mut args, reloc); } else { - args.push(ObjInsArg::ArgWithBase(ObjInsArgValue::Opaque( + args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque( OperandType::cpu_immediate.disassemble(&instruction, None), ))); } + args.push(ObjInsArg::PlainText("(".to_string())); args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque( OperandType::cpu_rs.disassemble(&instruction, None), ))); + args.push(ObjInsArg::PlainText(")".to_string())); } _ => { args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque( @@ -91,8 +102,8 @@ pub fn process_code( .as_ref() .and_then(|map| map.range(..=cur_addr as u64).last().map(|(_, &b)| b)); insts.push(ObjIns { - address: cur_addr, - code, + address: cur_addr as u64, + size: 4, op, mnemonic, args, @@ -105,3 +116,40 @@ pub fn process_code( } Ok(ProcessCodeResult { ops, insts }) } + +fn push_reloc(args: &mut Vec, reloc: &ObjReloc) { + match reloc.kind { + ObjRelocKind::MipsHi16 => { + args.push(ObjInsArg::PlainText("%hi(".to_string())); + args.push(ObjInsArg::Reloc); + args.push(ObjInsArg::PlainText(")".to_string())); + } + ObjRelocKind::MipsLo16 => { + args.push(ObjInsArg::PlainText("%lo(".to_string())); + args.push(ObjInsArg::Reloc); + args.push(ObjInsArg::PlainText(")".to_string())); + } + ObjRelocKind::MipsGot16 => { + args.push(ObjInsArg::PlainText("%got(".to_string())); + args.push(ObjInsArg::Reloc); + args.push(ObjInsArg::PlainText(")".to_string())); + } + ObjRelocKind::MipsCall16 => { + args.push(ObjInsArg::PlainText("%call16(".to_string())); + args.push(ObjInsArg::Reloc); + args.push(ObjInsArg::PlainText(")".to_string())); + } + ObjRelocKind::MipsGpRel16 => { + args.push(ObjInsArg::PlainText("%gp_rel(".to_string())); + args.push(ObjInsArg::Reloc); + args.push(ObjInsArg::PlainText(")".to_string())); + } + ObjRelocKind::Mips26 => { + args.push(ObjInsArg::Reloc); + } + ObjRelocKind::MipsGpRel32 => { + todo!("unimplemented: mips gp_rel32"); + } + kind => panic!("Unsupported MIPS relocation kind: {:?}", kind), + } +} diff --git a/objdiff-core/src/obj/mod.rs b/objdiff-core/src/obj/mod.rs index afbbf3b..a7c1525 100644 --- a/objdiff-core/src/obj/mod.rs +++ b/objdiff-core/src/obj/mod.rs @@ -1,9 +1,11 @@ -pub mod elf; #[cfg(feature = "mips")] pub mod mips; #[cfg(feature = "ppc")] pub mod ppc; +pub mod read; pub mod split_meta; +#[cfg(feature = "x86")] +pub mod x86; use std::{collections::BTreeMap, fmt, path::PathBuf}; @@ -50,8 +52,8 @@ pub struct ObjSection { #[derive(Debug, Clone, Eq, PartialEq)] pub enum ObjInsArgValue { - Signed(i16), - Unsigned(u16), + Signed(i64), + Unsigned(u64), Opaque(String), } @@ -61,7 +63,7 @@ impl ObjInsArgValue { (ObjInsArgValue::Signed(a), ObjInsArgValue::Signed(b)) => a == b, (ObjInsArgValue::Unsigned(a), ObjInsArgValue::Unsigned(b)) => a == b, (ObjInsArgValue::Signed(a), ObjInsArgValue::Unsigned(b)) - | (ObjInsArgValue::Unsigned(b), ObjInsArgValue::Signed(a)) => *a as u16 == *b, + | (ObjInsArgValue::Unsigned(b), ObjInsArgValue::Signed(a)) => *a as u64 == *b, (ObjInsArgValue::Opaque(a), ObjInsArgValue::Opaque(b)) => a == b, _ => false, } @@ -80,21 +82,18 @@ impl fmt::Display for ObjInsArgValue { #[derive(Debug, Clone, Eq, PartialEq)] pub enum ObjInsArg { + PlainText(String), Arg(ObjInsArgValue), - ArgWithBase(ObjInsArgValue), Reloc, - RelocWithBase, - BranchOffset(i32), + BranchDest(u64), } impl ObjInsArg { pub fn loose_eq(&self, other: &ObjInsArg) -> bool { match (self, other) { (ObjInsArg::Arg(a), ObjInsArg::Arg(b)) => a.loose_eq(b), - (ObjInsArg::ArgWithBase(a), ObjInsArg::ArgWithBase(b)) => a.loose_eq(b), (ObjInsArg::Reloc, ObjInsArg::Reloc) => true, - (ObjInsArg::RelocWithBase, ObjInsArg::RelocWithBase) => true, - (ObjInsArg::BranchOffset(a), ObjInsArg::BranchOffset(b)) => a == b, + (ObjInsArg::BranchDest(a), ObjInsArg::BranchDest(b)) => a == b, _ => false, } } @@ -135,13 +134,13 @@ pub enum ObjInsDiffKind { #[derive(Debug, Clone)] pub struct ObjIns { - pub address: u32, - pub code: u32, - pub op: u8, + pub address: u64, + pub size: u8, + pub op: u16, pub mnemonic: String, pub args: Vec, pub reloc: Option, - pub branch_dest: Option, + pub branch_dest: Option, /// Line number pub line: Option, /// Original (unsimplified) instruction @@ -203,6 +202,10 @@ pub enum ObjArchitecture { PowerPc, #[cfg(feature = "mips")] Mips, + #[cfg(feature = "x86")] + X86_32, + #[cfg(feature = "x86")] + X86_64, } #[derive(Debug, Clone)] @@ -256,6 +259,8 @@ pub enum ObjRelocKind { MipsGpRel16, #[cfg(feature = "mips")] MipsGpRel32, + #[cfg(feature = "x86")] + X86PcRel32, } #[derive(Debug, Clone)] diff --git a/objdiff-core/src/obj/ppc.rs b/objdiff-core/src/obj/ppc.rs index 381f801..08d473c 100644 --- a/objdiff-core/src/obj/ppc.rs +++ b/objdiff-core/src/obj/ppc.rs @@ -1,39 +1,34 @@ use std::collections::BTreeMap; -use anyhow::Result; -use ppc750cl::{disasm_iter, Argument, SimplifiedIns}; +use anyhow::{bail, Result}; +use ppc750cl::{disasm_iter, Argument, SimplifiedIns, GPR}; use crate::{ - diff::ProcessCodeResult, + diff::{DiffObjConfig, ProcessCodeResult}, obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjRelocKind}, }; -// Relative relocation, can be Simm or BranchOffset -fn is_relative_arg(arg: &ObjInsArg) -> bool { - matches!(arg, ObjInsArg::Arg(ObjInsArgValue::Signed(_)) | ObjInsArg::BranchOffset(_)) +// Relative relocation, can be Simm, Offset or BranchDest +fn is_relative_arg(arg: &Argument) -> bool { + matches!(arg, Argument::Simm(_) | Argument::Offset(_) | Argument::BranchDest(_)) } // Relative or absolute relocation, can be Uimm, Simm or Offset -fn is_rel_abs_arg(arg: &ObjInsArg) -> bool { - matches!( - arg, - ObjInsArg::Arg(ObjInsArgValue::Signed(_) | ObjInsArgValue::Unsigned(_)) - | ObjInsArg::ArgWithBase(ObjInsArgValue::Signed(_)) - ) +fn is_rel_abs_arg(arg: &Argument) -> bool { + matches!(arg, Argument::Uimm(_) | Argument::Simm(_) | Argument::Offset(_)) } -fn is_offset_arg(arg: &ObjInsArg) -> bool { - matches!(arg, ObjInsArg::ArgWithBase(ObjInsArgValue::Signed(_))) -} +fn is_offset_arg(arg: &Argument) -> bool { matches!(arg, Argument::Offset(_)) } pub fn process_code( + config: &DiffObjConfig, data: &[u8], address: u64, relocs: &[ObjReloc], line_info: &Option>, ) -> Result { let ins_count = data.len() / 4; - let mut ops = Vec::::with_capacity(ins_count); + 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); @@ -50,65 +45,120 @@ pub fn process_code( }; } let simplified = ins.clone().simplified(); - let mut args: Vec = simplified - .args - .iter() - .map(|a| match a { - Argument::Simm(simm) => ObjInsArg::Arg(ObjInsArgValue::Signed(simm.0)), - Argument::Uimm(uimm) => ObjInsArg::Arg(ObjInsArgValue::Unsigned(uimm.0)), - Argument::Offset(offset) => { - ObjInsArg::ArgWithBase(ObjInsArgValue::Signed(offset.0)) - } - Argument::BranchDest(dest) => ObjInsArg::BranchOffset(dest.0), - _ => ObjInsArg::Arg(ObjInsArgValue::Opaque(a.to_string())), - }) - .collect(); + + let mut reloc_arg = None; if let Some(reloc) = reloc { match reloc.kind { ObjRelocKind::PpcEmbSda21 => { - args = vec![args[0].clone(), ObjInsArg::Reloc]; + reloc_arg = Some(1); } 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; + reloc_arg = simplified.args.iter().rposition(is_relative_arg); } ObjRelocKind::PpcAddr16Hi | ObjRelocKind::PpcAddr16Ha | ObjRelocKind::PpcAddr16Lo => { - match args.iter_mut().rfind(|a| is_rel_abs_arg(a)) { - Some(arg) => { - *arg = if is_offset_arg(arg) { - ObjInsArg::RelocWithBase - } else { - ObjInsArg::Reloc - }; - } - None => { - log::warn!("Failed to locate rel/abs arg for reloc"); - } - }; + reloc_arg = simplified.args.iter().rposition(is_rel_abs_arg); } _ => {} } } - ops.push(simplified.ins.op as u8); + + let mut args = vec![]; + let mut branch_dest = None; + let mut writing_offset = false; + for (idx, arg) in simplified.args.iter().enumerate() { + if idx > 0 && !writing_offset { + if config.space_between_args { + args.push(ObjInsArg::PlainText(", ".to_string())); + } else { + args.push(ObjInsArg::PlainText(",".to_string())); + } + } + + if reloc_arg == Some(idx) { + let reloc = reloc.unwrap(); + push_reloc(&mut args, reloc)?; + // For @sda21, we can omit the register argument + if reloc.kind == ObjRelocKind::PpcEmbSda21 + // Sanity check: the next argument should be r0 + && matches!(simplified.args.get(idx + 1), Some(Argument::GPR(GPR(0)))) + { + break; + } + } else { + match arg { + Argument::Simm(simm) => { + args.push(ObjInsArg::Arg(ObjInsArgValue::Signed(simm.0 as i64))); + } + Argument::Uimm(uimm) => { + args.push(ObjInsArg::Arg(ObjInsArgValue::Unsigned(uimm.0 as u64))); + } + Argument::Offset(offset) => { + args.push(ObjInsArg::Arg(ObjInsArgValue::Signed(offset.0 as i64))); + } + Argument::BranchDest(dest) => { + let dest = ins.addr.wrapping_add_signed(dest.0) as u64; + args.push(ObjInsArg::BranchDest(dest)); + branch_dest = Some(dest); + } + _ => { + args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(arg.to_string()))); + } + }; + } + + if writing_offset { + args.push(ObjInsArg::PlainText(")".to_string())); + writing_offset = false; + } + if is_offset_arg(arg) { + args.push(ObjInsArg::PlainText("(".to_string())); + writing_offset = true; + } + } + + ops.push(simplified.ins.op as u16); let line = line_info .as_ref() .and_then(|map| map.range(..=simplified.ins.addr as u64).last().map(|(_, &b)| b)); insts.push(ObjIns { - address: simplified.ins.addr, - code: simplified.ins.code, + address: simplified.ins.addr as u64, + size: 4, mnemonic: format!("{}{}", simplified.mnemonic, simplified.suffix), args, reloc: reloc.cloned(), - op: ins.op as u8, - branch_dest: None, + op: ins.op as u16, + branch_dest, line, orig: Some(format!("{}", SimplifiedIns::basic_form(ins))), }); } Ok(ProcessCodeResult { ops, insts }) } + +fn push_reloc(args: &mut Vec, reloc: &ObjReloc) -> Result<()> { + match reloc.kind { + ObjRelocKind::PpcAddr16Lo => { + args.push(ObjInsArg::Reloc); + args.push(ObjInsArg::PlainText("@l".to_string())); + } + ObjRelocKind::PpcAddr16Hi => { + args.push(ObjInsArg::Reloc); + args.push(ObjInsArg::PlainText("@h".to_string())); + } + ObjRelocKind::PpcAddr16Ha => { + args.push(ObjInsArg::Reloc); + args.push(ObjInsArg::PlainText("@ha".to_string())); + } + ObjRelocKind::PpcEmbSda21 => { + args.push(ObjInsArg::Reloc); + args.push(ObjInsArg::PlainText("@sda21".to_string())); + } + ObjRelocKind::PpcRel24 | ObjRelocKind::PpcRel14 => { + args.push(ObjInsArg::Reloc); + } + kind => bail!("Unsupported PPC relocation kind: {:?}", kind), + }; + Ok(()) +} diff --git a/objdiff-core/src/obj/elf.rs b/objdiff-core/src/obj/read.rs similarity index 83% rename from objdiff-core/src/obj/elf.rs rename to objdiff-core/src/obj/read.rs index af22689..6ed5fbd 100644 --- a/objdiff-core/src/obj/elf.rs +++ b/objdiff-core/src/obj/read.rs @@ -5,8 +5,9 @@ use byteorder::{BigEndian, ReadBytesExt}; use filetime::FileTime; use flagset::Flags; use object::{ - elf, Architecture, File, Object, ObjectSection, ObjectSymbol, RelocationFlags, - RelocationTarget, SectionIndex, SectionKind, Symbol, SymbolKind, SymbolScope, SymbolSection, + elf, pe, Architecture, BinaryFormat, Endian, File, Object, ObjectSection, ObjectSymbol, + RelocationFlags, RelocationTarget, SectionIndex, SectionKind, Symbol, SymbolKind, SymbolScope, + SymbolSection, }; use crate::obj::{ @@ -48,7 +49,7 @@ fn to_obj_symbol( if symbol.is_weak() { flags = ObjSymbolFlagSet(flags.0 | ObjSymbolFlags::Weak); } - if symbol.scope() == SymbolScope::Linkage { + if obj_file.format() == BinaryFormat::Elf && symbol.scope() == SymbolScope::Linkage { flags = ObjSymbolFlagSet(flags.0 | ObjSymbolFlags::Hidden); } let section_address = if let Some(section) = @@ -63,6 +64,10 @@ fn to_obj_symbol( if obj_file.architecture() == Architecture::PowerPc { demangled_name = cwdemangle::demangle(name, &Default::default()); } + #[cfg(feature = "x86")] + if matches!(obj_file.format(), BinaryFormat::Coff | BinaryFormat::Pe) && name.starts_with('?') { + demangled_name = msvc_demangler::demangle(name, msvc_demangler::DemangleFlags::llvm()).ok(); + } // Find the virtual address for the symbol if available let virtual_address = split_meta .and_then(|m| m.virtual_addresses.as_ref()) @@ -225,9 +230,17 @@ fn relocations_by_section( let mut relocations = Vec::::new(); for (address, reloc) in obj_section.relocations() { let symbol = match reloc.target() { - RelocationTarget::Symbol(idx) => obj_file - .symbol_by_index(idx) - .context("Failed to locate relocation target symbol")?, + RelocationTarget::Symbol(idx) => { + let Ok(symbol) = obj_file.symbol_by_index(idx) else { + log::warn!( + "Failed to locate relocation {:#x} target symbol {}", + address, + idx.0 + ); + continue; + }; + symbol + } _ => bail!("Unhandled relocation target: {:?}", reloc.target()), }; let kind = match reloc.flags() { @@ -241,7 +254,7 @@ fn relocations_by_section( elf::R_PPC_REL24 => ObjRelocKind::PpcRel24, elf::R_PPC_REL14 => ObjRelocKind::PpcRel14, elf::R_PPC_EMB_SDA21 => ObjRelocKind::PpcEmbSda21, - _ => bail!("Unhandled PPC relocation type: {r_type}"), + _ => bail!("Unhandled ELF PPC relocation type: {r_type}"), }, #[cfg(feature = "mips")] ObjArchitecture::Mips => match r_type { @@ -253,8 +266,34 @@ fn relocations_by_section( elf::R_MIPS_CALL16 => ObjRelocKind::MipsCall16, elf::R_MIPS_GPREL16 => ObjRelocKind::MipsGpRel16, elf::R_MIPS_GPREL32 => ObjRelocKind::MipsGpRel32, - _ => bail!("Unhandled MIPS relocation type: {r_type}"), + _ => bail!("Unhandled ELF MIPS relocation type: {r_type}"), }, + #[cfg(feature = "x86")] + ObjArchitecture::X86_32 => match r_type { + elf::R_386_32 => ObjRelocKind::Absolute, + elf::R_386_PC32 => ObjRelocKind::X86PcRel32, + _ => bail!("Unhandled ELF x86_32 relocation type: {r_type}"), + }, + #[cfg(feature = "x86")] + ObjArchitecture::X86_64 => match r_type { + elf::R_X86_64_32 => ObjRelocKind::Absolute, + elf::R_X86_64_PC32 => ObjRelocKind::X86PcRel32, + _ => bail!("Unhandled ELF x86_64 relocation type: {r_type}"), + }, + }, + RelocationFlags::Coff { typ } => match arch { + #[cfg(feature = "ppc")] + ObjArchitecture::PowerPc => bail!("Unhandled PE/COFF PPC relocation type: {typ}"), + #[cfg(feature = "mips")] + ObjArchitecture::Mips => bail!("Unhandled PE/COFF MIPS relocation type: {typ}"), + #[cfg(feature = "x86")] + ObjArchitecture::X86_32 => match typ { + pe::IMAGE_REL_I386_DIR32 => ObjRelocKind::Absolute, + pe::IMAGE_REL_I386_REL32 => ObjRelocKind::X86PcRel32, + _ => bail!("Unhandled PE/COFF x86 relocation type: {typ}"), + }, + #[cfg(feature = "x86")] + ObjArchitecture::X86_64 => bail!("Unhandled PE/COFF x86_64 relocation type: {typ}"), }, flags => bail!("Unhandled relocation flags: {:?}", flags), }; @@ -266,9 +305,8 @@ fn relocations_by_section( _ => None, }; let addend = if reloc.has_implicit_addend() { - let addend = u32::from_be_bytes( - section.data[address as usize..address as usize + 4].try_into()?, - ); + let data = section.data[address as usize..address as usize + 4].try_into()?; + let addend = obj_file.endianness().read_u32_bytes(data); match kind { ObjRelocKind::Absolute => addend as i64, #[cfg(feature = "mips")] @@ -282,6 +320,8 @@ fn relocations_by_section( ObjRelocKind::MipsGpRel32 => addend as i32 as i64, #[cfg(feature = "mips")] ObjRelocKind::Mips26 => ((addend & 0x03FFFFFF) << 2) as i64, + #[cfg(feature = "x86")] + ObjRelocKind::X86PcRel32 => addend as i32 as i64, _ => bail!("Unsupported implicit relocation {kind:?}"), } } else { @@ -373,6 +413,10 @@ pub fn read(obj_path: &Path) -> Result { Architecture::PowerPc => ObjArchitecture::PowerPc, #[cfg(feature = "mips")] Architecture::Mips => ObjArchitecture::Mips, + #[cfg(feature = "x86")] + Architecture::I386 => ObjArchitecture::X86_32, + #[cfg(feature = "x86")] + Architecture::X86_64 => ObjArchitecture::X86_64, _ => bail!("Unsupported architecture: {:?}", obj_file.architecture()), }; let split_meta = split_meta(&obj_file)?; diff --git a/objdiff-core/src/obj/x86.rs b/objdiff-core/src/obj/x86.rs new file mode 100644 index 0000000..991e848 --- /dev/null +++ b/objdiff-core/src/obj/x86.rs @@ -0,0 +1,334 @@ +use std::collections::BTreeMap; + +use anyhow::{anyhow, bail, ensure, Result}; +use iced_x86::{ + Decoder, DecoderOptions, DecoratorKind, Formatter, FormatterOutput, FormatterTextKind, + GasFormatter, Instruction, IntelFormatter, MasmFormatter, NasmFormatter, NumberKind, OpKind, + PrefixKind, Register, SymbolResult, +}; + +use crate::{ + diff::{DiffObjConfig, ProcessCodeResult}, + obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjRelocKind}, +}; + +#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub enum X86Formatter { + #[default] + Intel, + Gas, + Nasm, + Masm, +} + +pub fn process_code( + config: &DiffObjConfig, + data: &[u8], + bitness: u32, + start_address: u64, + relocs: &[ObjReloc], + line_info: &Option>, +) -> Result { + let mut result = ProcessCodeResult { ops: Vec::new(), insts: Vec::new() }; + let mut decoder = Decoder::with_ip(bitness, data, start_address, DecoderOptions::NONE); + let mut formatter: Box = match config.x86_formatter { + X86Formatter::Intel => Box::new(IntelFormatter::new()), + X86Formatter::Gas => Box::new(GasFormatter::new()), + X86Formatter::Nasm => Box::new(NasmFormatter::new()), + X86Formatter::Masm => Box::new(MasmFormatter::new()), + }; + formatter.options_mut().set_space_after_operand_separator(config.space_between_args); + + let mut output = InstructionFormatterOutput { + formatted: String::new(), + ins: ObjIns { + address: 0, + size: 0, + op: 0, + mnemonic: "".to_string(), + args: vec![], + reloc: None, + branch_dest: None, + line: None, + orig: None, + }, + error: None, + ins_operands: vec![], + }; + let mut instruction = Instruction::default(); + while decoder.can_decode() { + decoder.decode_out(&mut instruction); + + let address = instruction.ip(); + let op = instruction.mnemonic() as u16; + let reloc = relocs + .iter() + .find(|r| r.address >= address && r.address < address + instruction.len() as u64); + output.ins = ObjIns { + address, + size: instruction.len() as u8, + op, + mnemonic: "".to_string(), + args: vec![], + reloc: reloc.cloned(), + branch_dest: None, + line: line_info.as_ref().and_then(|m| m.get(&address).cloned()), + orig: None, + }; + // Run the formatter, which will populate output.ins + formatter.format(&instruction, &mut output); + if let Some(error) = output.error.take() { + return Err(error); + } + ensure!(output.ins_operands.len() == output.ins.args.len()); + output.ins.orig = Some(output.formatted.clone()); + + // print!("{:016X} ", instruction.ip()); + // let start_index = (instruction.ip() - address) as usize; + // let instr_bytes = &data[start_index..start_index + instruction.len()]; + // for b in instr_bytes.iter() { + // print!("{:02X}", b); + // } + // if instr_bytes.len() < 32 { + // for _ in 0..32 - instr_bytes.len() { + // print!(" "); + // } + // } + // println!(" {}", output.formatted); + // + // if let Some(reloc) = reloc { + // println!("\tReloc: {:?}", reloc); + // } + // + // for i in 0..instruction.op_count() { + // let kind = instruction.op_kind(i); + // print!("{:?} ", kind); + // } + // println!(); + + // Make sure we've put the relocation somewhere in the instruction + if reloc.is_some() && !output.ins.args.iter().any(|a| matches!(a, ObjInsArg::Reloc)) { + let mut found = replace_arg( + OpKind::Memory, + ObjInsArg::Reloc, + &mut output.ins.args, + &instruction, + &output.ins_operands, + )?; + if !found { + found = replace_arg( + OpKind::Immediate32, + ObjInsArg::Reloc, + &mut output.ins.args, + &instruction, + &output.ins_operands, + )?; + } + ensure!(found, "x86: Failed to find operand for Absolute relocation"); + } + if reloc.is_some() && !output.ins.args.iter().any(|a| matches!(a, ObjInsArg::Reloc)) { + bail!("Failed to find relocation in instruction"); + } + + result.ops.push(op); + result.insts.push(output.ins.clone()); + + // Clear for next iteration + output.formatted.clear(); + output.ins_operands.clear(); + } + Ok(result) +} + +fn replace_arg( + from: OpKind, + to: ObjInsArg, + args: &mut [ObjInsArg], + instruction: &Instruction, + ins_operands: &[Option], +) -> Result { + let mut replace = None; + for i in 0..instruction.op_count() { + let op_kind = instruction.op_kind(i); + if op_kind == from { + replace = Some(i); + break; + } + } + if let Some(i) = replace { + for (j, arg) in args.iter_mut().enumerate() { + if ins_operands[j] == Some(i) { + *arg = to; + return Ok(true); + } + } + } + Ok(false) +} + +struct InstructionFormatterOutput { + formatted: String, + ins: ObjIns, + error: Option, + ins_operands: Vec>, +} + +impl InstructionFormatterOutput { + fn push_signed(&mut self, value: i64) { + // The formatter writes the '-' operator and then gives us a negative value, + // so convert it to a positive value to avoid double negatives + if value < 0 + && matches!(self.ins.args.last(), Some(ObjInsArg::Arg(ObjInsArgValue::Opaque(v))) if v == "-") + { + self.ins.args.push(ObjInsArg::Arg(ObjInsArgValue::Signed(value.wrapping_abs()))); + } else { + self.ins.args.push(ObjInsArg::Arg(ObjInsArgValue::Signed(value))); + } + } +} + +impl FormatterOutput for InstructionFormatterOutput { + fn write(&mut self, text: &str, kind: FormatterTextKind) { + // log::debug!("write {} {:?}", text, kind); + self.formatted.push_str(text); + // Skip whitespace after the mnemonic + if self.ins.args.is_empty() && kind == FormatterTextKind::Text { + return; + } + self.ins_operands.push(None); + match kind { + FormatterTextKind::Text | FormatterTextKind::Punctuation => { + self.ins.args.push(ObjInsArg::PlainText(text.to_string())); + } + FormatterTextKind::Keyword | FormatterTextKind::Operator => { + self.ins.args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(text.to_string()))); + } + _ => { + if self.error.is_none() { + self.error = Some(anyhow!("x86: Unsupported FormatterTextKind {:?}", kind)); + } + } + } + } + + fn write_prefix(&mut self, _instruction: &Instruction, text: &str, _prefix: PrefixKind) { + // log::debug!("write_prefix {} {:?}", text, prefix); + self.formatted.push_str(text); + self.ins_operands.push(None); + self.ins.args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(text.to_string()))); + } + + fn write_mnemonic(&mut self, _instruction: &Instruction, text: &str) { + // log::debug!("write_mnemonic {}", text); + self.formatted.push_str(text); + self.ins.mnemonic = text.to_string(); + } + + fn write_number( + &mut self, + _instruction: &Instruction, + _operand: u32, + instruction_operand: Option, + text: &str, + value: u64, + number_kind: NumberKind, + kind: FormatterTextKind, + ) { + // log::debug!("write_number {} {:?} {} {} {:?} {:?}", operand, instruction_operand, text, value, number_kind, kind); + self.formatted.push_str(text); + self.ins_operands.push(instruction_operand); + + // Handle relocations + match kind { + FormatterTextKind::LabelAddress => { + if let Some(reloc) = self.ins.reloc.as_ref() { + if reloc.kind == ObjRelocKind::Absolute { + self.ins.args.push(ObjInsArg::Reloc); + return; + } else if self.error.is_none() { + self.error = Some(anyhow!( + "x86: Unsupported LabelAddress relocation kind {:?}", + reloc.kind + )); + } + } + self.ins.args.push(ObjInsArg::BranchDest(value)); + self.ins.branch_dest = Some(value); + return; + } + FormatterTextKind::FunctionAddress => { + if let Some(reloc) = self.ins.reloc.as_ref() { + if reloc.kind == ObjRelocKind::X86PcRel32 { + self.ins.args.push(ObjInsArg::Reloc); + return; + } else if self.error.is_none() { + self.error = Some(anyhow!( + "x86: Unsupported FunctionAddress relocation kind {:?}", + reloc.kind + )); + } + } + } + _ => {} + } + + match number_kind { + NumberKind::Int8 => { + self.push_signed(value as i8 as i64); + } + NumberKind::Int16 => { + self.push_signed(value as i16 as i64); + } + NumberKind::Int32 => { + self.push_signed(value as i32 as i64); + } + NumberKind::Int64 => { + self.push_signed(value as i64); + } + NumberKind::UInt8 | NumberKind::UInt16 | NumberKind::UInt32 | NumberKind::UInt64 => { + self.ins.args.push(ObjInsArg::Arg(ObjInsArgValue::Unsigned(value))); + } + } + } + + fn write_decorator( + &mut self, + _instruction: &Instruction, + _operand: u32, + instruction_operand: Option, + text: &str, + _decorator: DecoratorKind, + ) { + // log::debug!("write_decorator {} {:?} {} {:?}", operand, instruction_operand, text, decorator); + self.formatted.push_str(text); + self.ins_operands.push(instruction_operand); + self.ins.args.push(ObjInsArg::PlainText(text.to_string())); + } + + fn write_register( + &mut self, + _instruction: &Instruction, + _operand: u32, + instruction_operand: Option, + text: &str, + _register: Register, + ) { + // log::debug!("write_register {} {:?} {} {:?}", operand, instruction_operand, text, register); + self.formatted.push_str(text); + self.ins_operands.push(instruction_operand); + self.ins.args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(text.to_string()))); + } + + fn write_symbol( + &mut self, + _instruction: &Instruction, + _operand: u32, + _instruction_operand: Option, + _address: u64, + _symbol: &SymbolResult<'_>, + ) { + if self.error.is_none() { + self.error = Some(anyhow!("x86: Unsupported write_symbol")); + } + } +} diff --git a/objdiff-core/src/util.rs b/objdiff-core/src/util.rs index 80832ea..16e86d9 100644 --- a/objdiff-core/src/util.rs +++ b/objdiff-core/src/util.rs @@ -7,7 +7,7 @@ pub(crate) struct ReallySigned(pub(crate) N); impl LowerHex for ReallySigned { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - let num = self.0.to_i32().unwrap(); + let num = self.0.to_i64().unwrap(); let prefix = if f.alternate() { "0x" } else { "" }; let bare_hex = format!("{:x}", num.abs()); f.pad_integral(num >= 0, prefix, &bare_hex) @@ -16,7 +16,7 @@ impl LowerHex for ReallySigned { impl UpperHex for ReallySigned { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - let num = self.0.to_i32().unwrap(); + let num = self.0.to_i64().unwrap(); let prefix = if f.alternate() { "0x" } else { "" }; let bare_hex = format!("{:X}", num.abs()); f.pad_integral(num >= 0, prefix, &bare_hex) diff --git a/objdiff-gui/src/app.rs b/objdiff-gui/src/app.rs index 15950bf..887c085 100644 --- a/objdiff-gui/src/app.rs +++ b/objdiff-gui/src/app.rs @@ -12,8 +12,11 @@ use std::{ use filetime::FileTime; use globset::{Glob, GlobSet}; use notify::{RecursiveMode, Watcher}; -use objdiff_core::config::{ - build_globset, ProjectConfigInfo, ProjectObject, ScratchConfig, DEFAULT_WATCH_PATTERNS, +use objdiff_core::{ + config::{ + build_globset, ProjectConfigInfo, ProjectObject, ScratchConfig, DEFAULT_WATCH_PATTERNS, + }, + diff::DiffObjConfig, }; use time::UtcOffset; @@ -26,7 +29,9 @@ use crate::{ }, views::{ appearance::{appearance_window, Appearance}, - config::{config_ui, project_window, ConfigViewState, CONFIG_DISABLED_TEXT}, + config::{ + config_ui, diff_config_window, project_window, ConfigViewState, CONFIG_DISABLED_TEXT, + }, data_diff::data_diff_ui, debug::debug_window, demangle::{demangle_window, DemangleViewState}, @@ -47,6 +52,7 @@ pub struct ViewState { pub show_appearance_config: bool, pub show_demangle: bool, pub show_project_config: bool, + pub show_diff_config: bool, pub show_debug: bool, } @@ -100,7 +106,7 @@ pub struct AppConfig { #[serde(default)] pub recent_projects: Vec, #[serde(default)] - pub relax_reloc_diffs: bool, + pub diff_obj_config: DiffObjConfig, #[serde(skip)] pub objects: Vec, @@ -138,7 +144,7 @@ impl Default for AppConfig { auto_update_check: true, watch_patterns: DEFAULT_WATCH_PATTERNS.iter().map(|s| Glob::new(s).unwrap()).collect(), recent_projects: vec![], - relax_reloc_diffs: false, + diff_obj_config: Default::default(), objects: vec![], object_nodes: vec![], watcher_change: false, @@ -408,6 +414,7 @@ impl eframe::App for App { show_appearance_config, show_demangle, show_project_config, + show_diff_config, show_debug, } = view_state; @@ -461,6 +468,10 @@ impl eframe::App for App { } }); ui.menu_button("Diff Options", |ui| { + if ui.button("More…").clicked() { + *show_diff_config = !*show_diff_config; + ui.close_menu(); + } let mut config = config.write().unwrap(); let response = ui .checkbox(&mut config.rebuild_on_changes, "Rebuild on changes") @@ -481,7 +492,10 @@ impl eframe::App for App { "Show hidden symbols", ); if ui - .checkbox(&mut config.relax_reloc_diffs, "Relax relocation diffs") + .checkbox( + &mut config.diff_obj_config.relax_reloc_diffs, + "Relax relocation diffs", + ) .on_hover_text( "Ignores differences in relocation targets. (Address, name, etc)", ) @@ -489,6 +503,15 @@ impl eframe::App for App { { config.queue_reload = true; } + if ui + .checkbox( + &mut config.diff_obj_config.space_between_args, + "Space between args", + ) + .changed() + { + config.queue_reload = true; + } }); }); }); @@ -518,6 +541,7 @@ impl eframe::App for App { project_window(ctx, config, show_project_config, config_state, appearance); appearance_window(ctx, show_appearance_config, appearance); demangle_window(ctx, show_demangle, demangle_state, appearance); + diff_config_window(ctx, config, show_diff_config, appearance); debug_window(ctx, show_debug, frame_history, appearance); self.post_update(ctx); diff --git a/objdiff-gui/src/jobs/objdiff.rs b/objdiff-gui/src/jobs/objdiff.rs index d09e626..7e3396e 100644 --- a/objdiff-gui/src/jobs/objdiff.rs +++ b/objdiff-gui/src/jobs/objdiff.rs @@ -8,7 +8,7 @@ use std::{ use anyhow::{anyhow, Context, Error, Result}; use objdiff_core::{ diff::{diff_objs, DiffObjConfig}, - obj::{elf, ObjInfo}, + obj::{read, ObjInfo}, }; use time::OffsetDateTime; @@ -57,7 +57,7 @@ pub struct ObjDiffConfig { pub build_base: bool, pub build_target: bool, pub selected_obj: Option, - pub relax_reloc_diffs: bool, + pub diff_obj_config: DiffObjConfig, } impl ObjDiffConfig { @@ -67,7 +67,7 @@ impl ObjDiffConfig { build_base: config.build_base, build_target: config.build_target, selected_obj: config.selected_obj.clone(), - relax_reloc_diffs: config.relax_reloc_diffs, + diff_obj_config: config.diff_obj_config.clone(), } } } @@ -224,7 +224,7 @@ fn run_build( total, &cancel, )?; - Some(elf::read(target_path).with_context(|| { + Some(read::read(target_path).with_context(|| { format!("Failed to read object '{}'", target_path.display()) })?) } @@ -241,7 +241,7 @@ fn run_build( &cancel, )?; Some( - elf::read(base_path) + read::read(base_path) .with_context(|| format!("Failed to read object '{}'", base_path.display()))?, ) } @@ -249,8 +249,7 @@ fn run_build( }; update_status(context, "Performing diff".to_string(), 4, total, &cancel)?; - let diff_config = DiffObjConfig { relax_reloc_diffs: config.relax_reloc_diffs }; - diff_objs(&diff_config, first_obj.as_mut(), second_obj.as_mut())?; + diff_objs(&config.diff_obj_config, first_obj.as_mut(), second_obj.as_mut())?; update_status(context, "Complete".to_string(), total, total, &cancel)?; Ok(Box::new(ObjDiffResult { first_status, second_status, first_obj, second_obj, time })) diff --git a/objdiff-gui/src/views/config.rs b/objdiff-gui/src/views/config.rs index a90c430..59aeb46 100644 --- a/objdiff-gui/src/views/config.rs +++ b/objdiff-gui/src/views/config.rs @@ -14,7 +14,10 @@ use egui::{ SelectableLabel, TextFormat, Widget, }; use globset::Glob; -use objdiff_core::config::{ProjectObject, DEFAULT_WATCH_PATTERNS}; +use objdiff_core::{ + config::{ProjectObject, DEFAULT_WATCH_PATTERNS}, + obj::x86::X86Formatter, +}; use self_update::cargo_crate_version; use crate::{ @@ -838,3 +841,36 @@ fn split_obj_config_ui( } }); } + +pub fn diff_config_window( + ctx: &egui::Context, + config: &AppConfigRef, + show: &mut bool, + appearance: &Appearance, +) { + let mut config_guard = config.write().unwrap(); + egui::Window::new("Diff Config").open(show).show(ctx, |ui| { + diff_config_ui(ui, &mut config_guard, appearance); + }); +} + +fn diff_config_ui(ui: &mut egui::Ui, config: &mut AppConfig, _appearance: &Appearance) { + egui::ComboBox::new("x86_formatter", "X86 Format") + .selected_text(format!("{:?}", config.diff_obj_config.x86_formatter)) + .show_ui(ui, |ui| { + for &formatter in + &[X86Formatter::Intel, X86Formatter::Gas, X86Formatter::Nasm, X86Formatter::Masm] + { + if ui + .selectable_label( + config.diff_obj_config.x86_formatter == formatter, + format!("{:?}", formatter), + ) + .clicked() + { + config.diff_obj_config.x86_formatter = formatter; + config.queue_reload = true; + } + } + }); +} diff --git a/objdiff-gui/src/views/function_diff.rs b/objdiff-gui/src/views/function_diff.rs index 3bf96bd..085ade1 100644 --- a/objdiff-gui/src/views/function_diff.rs +++ b/objdiff-gui/src/views/function_diff.rs @@ -4,7 +4,10 @@ use egui::{text::LayoutJob, Align, Label, Layout, Sense, Vec2, Widget}; use egui_extras::{Column, TableBuilder, TableRow}; use objdiff_core::{ diff::display::{display_diff, DiffText, HighlightKind}, - obj::{ObjInfo, ObjIns, ObjInsArg, ObjInsArgValue, ObjInsDiff, ObjInsDiffKind, ObjSymbol}, + obj::{ + ObjInfo, ObjIns, ObjInsArg, ObjInsArgValue, ObjInsDiff, ObjInsDiffKind, ObjSection, + ObjSymbol, + }, }; use time::format_description; @@ -18,19 +21,23 @@ pub struct FunctionViewState { pub highlight: HighlightKind, } -fn ins_hover_ui(ui: &mut egui::Ui, ins: &ObjIns, appearance: &Appearance) { +fn ins_hover_ui(ui: &mut egui::Ui, section: &ObjSection, ins: &ObjIns, appearance: &Appearance) { ui.scope(|ui| { ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace); ui.style_mut().wrap = Some(false); - ui.label(format!("{:02X?}", ins.code.to_be_bytes())); + let offset = ins.address - section.address; + ui.label(format!( + "{:02X?}", + §ion.data[offset as usize..(offset + ins.size as u64) as usize] + )); if let Some(orig) = &ins.orig { ui.label(format!("Original: {}", orig)); } for arg in &ins.args { - if let ObjInsArg::Arg(arg) | ObjInsArg::ArgWithBase(arg) = arg { + if let ObjInsArg::Arg(arg) = arg { match arg { ObjInsArgValue::Signed(v) => { ui.label(format!("{arg} == {v}")); @@ -71,7 +78,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) | ObjInsArg::ArgWithBase(arg) = arg { + if let ObjInsArg::Arg(arg) = arg { match arg { ObjInsArgValue::Signed(v) => { if ui.button(format!("Copy \"{arg}\"")).clicked() { @@ -112,9 +119,14 @@ fn ins_context_menu(ui: &mut egui::Ui, ins: &ObjIns) { }); } -fn find_symbol<'a>(obj: &'a ObjInfo, selected_symbol: &SymbolReference) -> Option<&'a ObjSymbol> { +fn find_symbol<'a>( + obj: &'a ObjInfo, + selected_symbol: &SymbolReference, +) -> Option<(&'a ObjSection, &'a ObjSymbol)> { obj.sections.iter().find_map(|section| { - section.symbols.iter().find(|symbol| symbol.name == selected_symbol.symbol_name) + section.symbols.iter().find_map(|symbol| { + (symbol.name == selected_symbol.symbol_name).then_some((section, symbol)) + }) }) } @@ -166,7 +178,7 @@ fn diff_text_ui( base_color = appearance.diff_colors[diff.idx % appearance.diff_colors.len()] } } - DiffText::BranchTarget(addr) => { + DiffText::BranchDest(addr) => { label_text = format!("{addr:x}"); } DiffText::Symbol(sym) => { @@ -216,7 +228,7 @@ fn asm_row_ui( 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 as u32, |text| { + display_diff(ins_diff, symbol.address, |text| { diff_text_ui(ui, text, ins_diff, appearance, ins_view_state, space_width); Ok::<_, ()>(()) }) @@ -226,6 +238,7 @@ fn asm_row_ui( fn asm_col_ui( row: &mut TableRow<'_, '_>, ins_diff: &ObjInsDiff, + section: &ObjSection, symbol: &ObjSymbol, appearance: &Appearance, ins_view_state: &mut FunctionViewState, @@ -234,7 +247,7 @@ fn asm_col_ui( asm_row_ui(ui, ins_diff, symbol, appearance, ins_view_state); }); if let Some(ins) = &ins_diff.ins { - response.on_hover_ui_at_pointer(|ui| ins_hover_ui(ui, ins, appearance)); + response.on_hover_ui_at_pointer(|ui| ins_hover_ui(ui, section, ins, appearance)); } } @@ -254,14 +267,15 @@ fn asm_table_ui( ) -> Option<()> { let left_symbol = left_obj.and_then(|obj| find_symbol(obj, selected_symbol)); let right_symbol = right_obj.and_then(|obj| find_symbol(obj, selected_symbol)); - let instructions_len = left_symbol.or(right_symbol).map(|s| s.instructions.len())?; + let instructions_len = left_symbol.or(right_symbol).map(|(_, s)| s.instructions.len())?; table.body(|body| { body.rows(appearance.code_font.size, instructions_len, |mut row| { let row_index = row.index(); - if let Some(symbol) = left_symbol { + if let Some((section, symbol)) = left_symbol { asm_col_ui( &mut row, &symbol.instructions[row_index], + section, symbol, appearance, ins_view_state, @@ -269,10 +283,11 @@ fn asm_table_ui( } else { empty_col_ui(&mut row); } - if let Some(symbol) = right_symbol { + if let Some((section, symbol)) = right_symbol { asm_col_ui( &mut row, &symbol.instructions[row_index], + section, symbol, appearance, ins_view_state, @@ -384,7 +399,7 @@ pub fn function_diff_ui(ui: &mut egui::Ui, state: &mut DiffViewState, appearance .second_obj .as_ref() .and_then(|obj| find_symbol(obj, selected_symbol)) - .and_then(|symbol| symbol.match_percent) + .and_then(|(_, symbol)| symbol.match_percent) { ui.colored_label( match_color_for_symbol(match_percent, appearance),