From 95868f1d19d5a46a042c4a258576cb43ad637931 Mon Sep 17 00:00:00 2001 From: Luke Street Date: Fri, 28 Feb 2025 00:17:32 -0700 Subject: [PATCH] Reimplement x86 arch, MSVC section group combining Plus display_row/DiffText refactoring --- .github/workflows/build.yaml | 1 - objdiff-cli/src/views/function_diff.rs | 102 +- objdiff-core/Cargo.toml | 2 +- objdiff-core/src/arch/arm.rs | 140 +-- objdiff-core/src/arch/arm64.rs | 330 +++--- objdiff-core/src/arch/mips.rs | 78 +- objdiff-core/src/arch/mod.rs | 13 +- objdiff-core/src/arch/ppc.rs | 73 +- objdiff-core/src/arch/x86.rs | 597 ++++++----- objdiff-core/src/diff/code.rs | 18 +- objdiff-core/src/diff/display.rs | 236 ++++- objdiff-core/src/obj/mod.rs | 75 +- objdiff-core/src/obj/read.rs | 238 +++-- ...re__obj__read__test__combine_sections.snap | 2 +- objdiff-core/src/util.rs | 2 +- objdiff-core/tests/arch_x86.rs | 28 + objdiff-core/tests/common.rs | 27 +- .../tests/snapshots/arch_ppc__diff_ppc.snap | 4 - .../tests/snapshots/arch_ppc__read_ppc-3.snap | 132 +-- .../tests/snapshots/arch_ppc__read_ppc.snap | 2 +- .../tests/snapshots/arch_x86__read_x86-2.snap | 97 ++ .../tests/snapshots/arch_x86__read_x86-3.snap | 11 + .../tests/snapshots/arch_x86__read_x86.snap | 200 ++++ .../arch_x86__read_x86_combine_sections.snap | 940 ++++++++++++++++++ objdiff-gui/src/views/function_diff.rs | 130 +-- objdiff-wasm/package-lock.json | 24 +- objdiff-wasm/package.json | 3 +- objdiff-wasm/src/api.rs | 56 +- objdiff-wasm/wit/objdiff.wit | 63 +- 29 files changed, 2580 insertions(+), 1044 deletions(-) create mode 100644 objdiff-core/tests/arch_x86.rs create mode 100644 objdiff-core/tests/snapshots/arch_x86__read_x86-2.snap create mode 100644 objdiff-core/tests/snapshots/arch_x86__read_x86-3.snap create mode 100644 objdiff-core/tests/snapshots/arch_x86__read_x86.snap create mode 100644 objdiff-core/tests/snapshots/arch_x86__read_x86_combine_sections.snap diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index a0d2efb..9136447 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -235,7 +235,6 @@ jobs: - name: Setup Rust toolchain uses: dtolnay/rust-toolchain@nightly with: - targets: wasm32-wasip2 components: rust-src - name: Cache Rust workspace uses: Swatinem/rust-cache@v2 diff --git a/objdiff-cli/src/views/function_diff.rs b/objdiff-cli/src/views/function_diff.rs index 41963c8..6cbd113 100644 --- a/objdiff-cli/src/views/function_diff.rs +++ b/objdiff-cli/src/views/function_diff.rs @@ -1,10 +1,10 @@ -use std::cmp::Ordering; +use core::cmp::Ordering; use anyhow::{bail, Result}; use crossterm::event::{Event, KeyCode, KeyEventKind, KeyModifiers, MouseButton, MouseEventKind}; use objdiff_core::{ diff::{ - display::{display_row, DiffText, HighlightKind}, + display::{display_row, DiffText, DiffTextColor, HighlightKind}, DiffObjConfig, FunctionRelocDiffs, InstructionDiffKind, ObjectDiff, SymbolDiff, }, obj::Object, @@ -522,87 +522,55 @@ impl FunctionDiffUi { let mut sx = rect.x; let sy = rect.y + y as u16; let mut line = Line::default(); - display_row(obj, symbol_index, ins_row, diff_config, |text, diff_idx| { - let label_text; - let mut base_color = match ins_row.kind { - InstructionDiffKind::None - | InstructionDiffKind::OpMismatch - | InstructionDiffKind::ArgMismatch => Color::Gray, - InstructionDiffKind::Replace => Color::Cyan, - InstructionDiffKind::Delete => Color::Red, - InstructionDiffKind::Insert => Color::Green, - }; - if let Some(idx) = diff_idx.get() { - base_color = COLOR_ROTATION[idx as usize % COLOR_ROTATION.len()]; - } - let mut pad_to = 0; - match text { - DiffText::Basic(text) => { - label_text = text.to_string(); - } - DiffText::Line(num) => { - label_text = format!("{num} "); - base_color = Color::DarkGray; - pad_to = 5; - } - DiffText::Address(addr) => { - label_text = format!("{:x}:", addr); - pad_to = 5; - } - DiffText::Opcode(mnemonic, _op) => { - label_text = mnemonic.to_string(); - if ins_row.kind == InstructionDiffKind::OpMismatch { - base_color = Color::Blue; - } - pad_to = 8; - } - DiffText::Argument(arg) => { - label_text = arg.to_string(); - } - DiffText::BranchDest(addr) => { - label_text = format!("{addr:x}"); - } + display_row(obj, symbol_index, ins_row, diff_config, |segment| { + let highlight_kind = HighlightKind::from(&segment.text); + let label_text = match segment.text { + DiffText::Basic(text) => text.to_string(), + DiffText::Line(num) => format!("{num} "), + DiffText::Address(addr) => format!("{:x}:", addr), + DiffText::Opcode(mnemonic, _op) => format!("{mnemonic} "), + DiffText::Argument(arg) => arg.to_string(), + DiffText::BranchDest(addr) => format!("{addr:x}"), DiffText::Symbol(sym) => { - let name = sym.demangled_name.as_ref().unwrap_or(&sym.name); - label_text = name.clone(); - if diff_idx.is_none() { - base_color = Color::White; - } - } - DiffText::Addend(addend) => { - label_text = match addend.cmp(&0i64) { - Ordering::Greater => format!("+{:#x}", addend), - Ordering::Less => format!("-{:#x}", -addend), - _ => "".to_string(), - }; - if diff_idx.is_none() { - base_color = Color::White; - } + sym.demangled_name.as_ref().unwrap_or(&sym.name).clone() } + DiffText::Addend(addend) => match addend.cmp(&0i64) { + Ordering::Greater => format!("+{:#x}", addend), + Ordering::Less => format!("-{:#x}", -addend), + _ => String::new(), + }, DiffText::Spacing(n) => { - line.spans.push(Span::raw(" ".repeat(n))); + line.spans.push(Span::raw(" ".repeat(n as usize))); sx += n as u16; return Ok(()); } - DiffText::Eol => { - return Ok(()); - } - } + DiffText::Eol => return Ok(()), + }; + let len = label_text.len(); - let highlighted = *highlight == text; + let highlighted = + highlight_kind != HighlightKind::None && *highlight == highlight_kind; if let Some((cx, cy)) = result.click_xy { if cx >= sx && cx < sx + len as u16 && cy == sy { - new_highlight = Some(text.into()); + new_highlight = Some(highlight_kind); } } - let mut style = Style::new().fg(base_color); + let mut style = Style::new().fg(match segment.color { + DiffTextColor::Normal => Color::Gray, + DiffTextColor::Dim => Color::DarkGray, + DiffTextColor::Bright => Color::White, + DiffTextColor::Replace => Color::Cyan, + DiffTextColor::Delete => Color::Red, + DiffTextColor::Insert => Color::Green, + DiffTextColor::Rotating(i) => COLOR_ROTATION[i as usize % COLOR_ROTATION.len()], + }); if highlighted { style = style.bg(Color::DarkGray); } line.spans.push(Span::styled(label_text, style)); sx += len as u16; - if pad_to > len { - let pad = (pad_to - len) as u16; + if segment.pad_to as usize > len { + let pad = (segment.pad_to as usize - len) as u16; line.spans.push(Span::raw(" ".repeat(pad as usize))); sx += pad; } diff --git a/objdiff-core/Cargo.toml b/objdiff-core/Cargo.toml index 56bad1d..f5dc872 100644 --- a/objdiff-core/Cargo.toml +++ b/objdiff-core/Cargo.toml @@ -26,7 +26,7 @@ all = [ "arm64", "mips", "ppc", - # "x86", + "x86", ] # Implicit, used to check if any arch is enabled any-arch = [ diff --git a/objdiff-core/src/arch/arm.rs b/objdiff-core/src/arch/arm.rs index c4aa54d..05cbd0b 100644 --- a/objdiff-core/src/arch/arm.rs +++ b/objdiff-core/src/arch/arm.rs @@ -16,8 +16,8 @@ use crate::{ arch::Arch, diff::{display::InstructionPart, ArmArchVersion, ArmR9Usage, DiffObjConfig}, obj::{ - InstructionArg, InstructionArgValue, InstructionRef, RelocationFlags, ResolvedRelocation, - ScannedInstruction, SymbolFlag, SymbolFlagSet, SymbolKind, + InstructionRef, RelocationFlags, ResolvedRelocation, ScannedInstruction, SymbolFlag, + SymbolFlagSet, SymbolKind, }, }; @@ -261,9 +261,9 @@ impl Arch for ArchArm { cb: &mut dyn FnMut(InstructionPart) -> Result<()>, ) -> Result<()> { let (ins, parsed_ins) = self.parse_ins_ref(ins_ref, code, diff_config)?; - cb(InstructionPart::Opcode(Cow::Borrowed(parsed_ins.mnemonic), ins_ref.opcode))?; + cb(InstructionPart::opcode(parsed_ins.mnemonic, ins_ref.opcode))?; if ins == unarm::Ins::Data && relocation.is_some() { - cb(InstructionPart::Arg(InstructionArg::Reloc))?; + cb(InstructionPart::reloc())?; } else { push_args( &parsed_ins, @@ -396,12 +396,10 @@ fn push_args( }) | args::Argument::CoOption(_) => { deref = false; - arg_cb(InstructionPart::Basic("]"))?; + arg_cb(InstructionPart::basic("]"))?; if writeback { writeback = false; - arg_cb(InstructionPart::Arg(InstructionArg::Value( - InstructionArgValue::Opaque("!".into()), - )))?; + arg_cb(InstructionPart::opaque("!"))?; } } _ => {} @@ -409,130 +407,98 @@ fn push_args( } if i > 0 { - arg_cb(InstructionPart::Separator)?; + arg_cb(InstructionPart::separator())?; } if reloc_arg == Some(i) { - arg_cb(InstructionPart::Arg(InstructionArg::Reloc))?; + arg_cb(InstructionPart::reloc())?; } else { match arg { args::Argument::None => {} args::Argument::Reg(reg) => { if reg.deref { deref = true; - arg_cb(InstructionPart::Basic("["))?; + arg_cb(InstructionPart::basic("["))?; } - arg_cb(InstructionPart::Arg(InstructionArg::Value( - InstructionArgValue::Opaque( - reg.reg.display(display_options.reg_names).to_string().into(), - ), - )))?; + arg_cb(InstructionPart::opaque( + reg.reg.display(display_options.reg_names).to_string(), + ))?; if reg.writeback { if reg.deref { writeback = true; } else { - arg_cb(InstructionPart::Arg(InstructionArg::Value( - InstructionArgValue::Opaque("!".into()), - )))?; + arg_cb(InstructionPart::opaque("!"))?; } } } args::Argument::RegList(reg_list) => { - arg_cb(InstructionPart::Basic("{"))?; + arg_cb(InstructionPart::basic("{"))?; let mut first = true; for i in 0..16 { if (reg_list.regs & (1 << i)) != 0 { if !first { - arg_cb(InstructionPart::Separator)?; + arg_cb(InstructionPart::separator())?; } - arg_cb(InstructionPart::Arg(InstructionArg::Value( - InstructionArgValue::Opaque( - args::Register::parse(i) - .display(display_options.reg_names) - .to_string() - .into(), - ), - )))?; + arg_cb(InstructionPart::opaque( + args::Register::parse(i) + .display(display_options.reg_names) + .to_string(), + ))?; first = false; } } - arg_cb(InstructionPart::Basic("}"))?; + arg_cb(InstructionPart::basic("}"))?; if reg_list.user_mode { - arg_cb(InstructionPart::Arg(InstructionArg::Value( - InstructionArgValue::Opaque("^".into()), - )))?; + arg_cb(InstructionPart::opaque("^"))?; } } args::Argument::UImm(value) | args::Argument::CoOpcode(value) | args::Argument::SatImm(value) => { - arg_cb(InstructionPart::Basic("#"))?; - arg_cb(InstructionPart::Arg(InstructionArg::Value( - InstructionArgValue::Unsigned(*value as u64), - )))?; + arg_cb(InstructionPart::basic("#"))?; + arg_cb(InstructionPart::unsigned(*value))?; } args::Argument::SImm(value) | args::Argument::OffsetImm(args::OffsetImm { post_indexed: _, value }) => { - arg_cb(InstructionPart::Basic("#"))?; - arg_cb(InstructionPart::Arg(InstructionArg::Value( - InstructionArgValue::Signed(*value as i64), - )))?; + arg_cb(InstructionPart::basic("#"))?; + arg_cb(InstructionPart::signed(*value))?; } args::Argument::BranchDest(value) => { - let dest = cur_addr.wrapping_add_signed(*value) as u64; - arg_cb(InstructionPart::Arg(InstructionArg::BranchDest(dest)))?; + arg_cb(InstructionPart::branch_dest(cur_addr.wrapping_add_signed(*value)))?; } args::Argument::CoOption(value) => { - arg_cb(InstructionPart::Basic("{"))?; - arg_cb(InstructionPart::Arg(InstructionArg::Value( - InstructionArgValue::Unsigned(*value as u64), - )))?; - arg_cb(InstructionPart::Basic("}"))?; + arg_cb(InstructionPart::basic("{"))?; + arg_cb(InstructionPart::unsigned(*value))?; + arg_cb(InstructionPart::basic("}"))?; } args::Argument::CoprocNum(value) => { - arg_cb(InstructionPart::Arg(InstructionArg::Value( - InstructionArgValue::Opaque(format!("p{}", value).into()), - )))?; + arg_cb(InstructionPart::opaque(format!("p{}", value)))?; } args::Argument::ShiftImm(shift) => { - arg_cb(InstructionPart::Arg(InstructionArg::Value( - InstructionArgValue::Opaque(shift.op.to_string().into()), - )))?; - arg_cb(InstructionPart::Basic(" #"))?; - arg_cb(InstructionPart::Arg(InstructionArg::Value( - InstructionArgValue::Unsigned(shift.imm as u64), - )))?; + arg_cb(InstructionPart::opaque(shift.op.to_string()))?; + arg_cb(InstructionPart::basic(" #"))?; + arg_cb(InstructionPart::unsigned(shift.imm))?; } args::Argument::ShiftReg(shift) => { - arg_cb(InstructionPart::Arg(InstructionArg::Value( - InstructionArgValue::Opaque(shift.op.to_string().into()), - )))?; - arg_cb(InstructionPart::Basic(" "))?; - arg_cb(InstructionPart::Arg(InstructionArg::Value( - InstructionArgValue::Opaque( - shift.reg.display(display_options.reg_names).to_string().into(), - ), - )))?; + arg_cb(InstructionPart::opaque(shift.op.to_string()))?; + arg_cb(InstructionPart::basic(" "))?; + arg_cb(InstructionPart::opaque( + shift.reg.display(display_options.reg_names).to_string(), + ))?; } args::Argument::OffsetReg(offset) => { if !offset.add { - arg_cb(InstructionPart::Basic("-"))?; + arg_cb(InstructionPart::basic("-"))?; } - arg_cb(InstructionPart::Arg(InstructionArg::Value( - InstructionArgValue::Opaque( - offset.reg.display(display_options.reg_names).to_string().into(), - ), - )))?; + arg_cb(InstructionPart::opaque( + offset.reg.display(display_options.reg_names).to_string(), + ))?; } args::Argument::CpsrMode(mode) => { - arg_cb(InstructionPart::Basic("#"))?; - arg_cb(InstructionPart::Arg(InstructionArg::Value( - InstructionArgValue::Unsigned(mode.mode as u64), - )))?; + arg_cb(InstructionPart::basic("#"))?; + arg_cb(InstructionPart::unsigned(mode.mode))?; if mode.writeback { - arg_cb(InstructionPart::Arg(InstructionArg::Value( - InstructionArgValue::Opaque("!".into()), - )))?; + arg_cb(InstructionPart::opaque("!"))?; } } args::Argument::CoReg(_) @@ -541,21 +507,17 @@ fn push_args( | args::Argument::Shift(_) | args::Argument::CpsrFlags(_) | args::Argument::Endian(_) => { - arg_cb(InstructionPart::Arg(InstructionArg::Value( - InstructionArgValue::Opaque( - arg.display(display_options, None).to_string().into(), - ), - )))?; + arg_cb(InstructionPart::opaque( + arg.display(display_options, None).to_string(), + ))?; } } } } if deref { - arg_cb(InstructionPart::Basic("]"))?; + arg_cb(InstructionPart::basic("]"))?; if writeback { - arg_cb(InstructionPart::Arg(InstructionArg::Value(InstructionArgValue::Opaque( - "!".into(), - ))))?; + arg_cb(InstructionPart::opaque("!"))?; } } Ok(()) diff --git a/objdiff-core/src/arch/arm64.rs b/objdiff-core/src/arch/arm64.rs index 465c3bb..f9bbf19 100644 --- a/objdiff-core/src/arch/arm64.rs +++ b/objdiff-core/src/arch/arm64.rs @@ -17,10 +17,7 @@ use yaxpeax_arm::armv8::a64::{ use crate::{ arch::Arch, diff::{display::InstructionPart, DiffObjConfig}, - obj::{ - InstructionArg, InstructionArgValue, InstructionRef, RelocationFlags, ResolvedRelocation, - ScannedInstruction, - }, + obj::{InstructionRef, RelocationFlags, ResolvedRelocation, ScannedInstruction}, }; #[derive(Debug)] @@ -83,14 +80,14 @@ impl Arch for ArchArm64 { relocation: Option, function_range: Range, _section_index: usize, - diff_config: &DiffObjConfig, + _diff_config: &DiffObjConfig, cb: &mut dyn FnMut(InstructionPart) -> Result<()>, ) -> Result<()> { let mut reader = U8Reader::new(code); let decoder = InstDecoder::default(); let mut ins = Instruction::default(); if decoder.decode_into(&mut ins, &mut reader).is_err() { - cb(InstructionPart::Opcode(Cow::Borrowed(""), u16::MAX))?; + cb(InstructionPart::opcode("", u16::MAX))?; return Ok(()); } @@ -100,12 +97,11 @@ impl Arch for ArchArm64 { start_address: function_range.start, end_address: function_range.end, reloc: relocation, - config: diff_config, }; let mut display_args = Vec::with_capacity(16); let mnemonic = display_instruction(&mut |ret| display_args.push(ret), &ins, &mut ctx); - cb(InstructionPart::Opcode(Cow::Borrowed(mnemonic), ins_ref.opcode))?; + cb(InstructionPart::opcode(mnemonic, ins_ref.opcode))?; for arg in display_args { cb(arg)?; } @@ -317,7 +313,6 @@ struct DisplayCtx<'a> { start_address: u64, end_address: u64, reloc: Option>, - config: &'a DiffObjConfig, } // Source: https://github.com/iximeow/yaxpeax-arm/blob/716a6e3fc621f5fe3300f3309e56943b8e1e65ad/src/armv8/a64.rs#L317 @@ -325,7 +320,7 @@ struct DisplayCtx<'a> { // Reworked for more structured output. The library only gives us a Display impl, and no way to // capture any of this information, so it needs to be reimplemented here. fn display_instruction(args: &mut Cb, ins: &Instruction, ctx: &mut DisplayCtx) -> &'static str -where Cb: FnMut(InstructionPart) { +where Cb: FnMut(InstructionPart<'static>) { let mnemonic = match ins.opcode { Opcode::Invalid => return "", Opcode::UDF => "udf", @@ -345,7 +340,7 @@ where Cb: FnMut(InstructionPart) { unreachable!("movn operand 0 is always Register"); }; push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_unsigned(args, imm); return "mov"; } @@ -366,7 +361,7 @@ where Cb: FnMut(InstructionPart) { unreachable!("movz operand 0 is always Register"); }; push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_unsigned(args, imm); return "mov"; } @@ -375,7 +370,7 @@ where Cb: FnMut(InstructionPart) { Opcode::SBC => { if let Operand::Register(_, 31) = ins.operands[1] { push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[2], ctx); return "ngc"; } else { @@ -385,7 +380,7 @@ where Cb: FnMut(InstructionPart) { Opcode::SBCS => { if let Operand::Register(_, 31) = ins.operands[1] { push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[2], ctx); return "ngcs"; } else { @@ -397,25 +392,25 @@ where Cb: FnMut(InstructionPart) { if let Operand::Register(_, 31) = ins.operands[1] { if let Operand::Immediate(0) = ins.operands[2] { push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[1], ctx); return "mov"; } else if let Operand::RegShift(style, amt, size, r) = ins.operands[2] { if style == ShiftStyle::LSL && amt == 0 { push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_register(args, size, r, false); return "mov"; } } else { push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[2], ctx); return "mov"; } } else if ins.operands[1] == ins.operands[2] { push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[1], ctx); return "mov"; } @@ -424,7 +419,7 @@ where Cb: FnMut(InstructionPart) { Opcode::ORN => { if let Operand::Register(_, 31) = ins.operands[1] { push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[2], ctx); return "mvn"; } @@ -437,7 +432,7 @@ where Cb: FnMut(InstructionPart) { Opcode::ANDS => { if let Operand::Register(_, 31) = ins.operands[0] { push_operand(args, &ins.operands[1], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[2], ctx); return "tst"; } @@ -446,14 +441,14 @@ where Cb: FnMut(InstructionPart) { Opcode::ADDS => { if let Operand::Register(_, 31) = ins.operands[0] { push_operand(args, &ins.operands[1], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[2], ctx); return "cmn"; } else if let Operand::RegShift(ShiftStyle::LSL, 0, size, reg) = ins.operands[2] { push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[1], ctx); - push_separator(args, ctx.config); + push_separator(args); push_register(args, size, reg, false); return "adds"; } @@ -463,20 +458,20 @@ where Cb: FnMut(InstructionPart) { if let Operand::Immediate(0) = ins.operands[2] { if let Operand::RegisterOrSP(size, 31) = ins.operands[0] { push_register(args, size, 31, true); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[1], ctx); return "mov"; } else if let Operand::RegisterOrSP(size, 31) = ins.operands[1] { push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_register(args, size, 31, true); return "mov"; } } else if let Operand::RegShift(ShiftStyle::LSL, 0, size, reg) = ins.operands[2] { push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[1], ctx); - push_separator(args, ctx.config); + push_separator(args); push_register(args, size, reg, false); return "add"; } @@ -485,19 +480,19 @@ where Cb: FnMut(InstructionPart) { Opcode::SUBS => { if let Operand::Register(_, 31) = ins.operands[0] { push_operand(args, &ins.operands[1], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[2], ctx); return "cmp"; } else if let Operand::Register(_, 31) = ins.operands[1] { push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[2], ctx); return "negs"; } else if let Operand::RegShift(ShiftStyle::LSL, 0, size, reg) = ins.operands[2] { push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[1], ctx); - push_separator(args, ctx.config); + push_separator(args); push_register(args, size, reg, false); return "subs"; } @@ -506,14 +501,14 @@ where Cb: FnMut(InstructionPart) { Opcode::SUB => { if let Operand::Register(_, 31) = ins.operands[1] { push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[2], ctx); return "neg"; } else if let Operand::RegShift(ShiftStyle::LSL, 0, size, reg) = ins.operands[2] { push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[1], ctx); - push_separator(args, ctx.config); + push_separator(args); push_register(args, size, reg, false); return "sub"; } @@ -533,18 +528,18 @@ where Cb: FnMut(InstructionPart) { }; return if rn == 31 { push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_unsigned(args, lsb as u64); - push_separator(args, ctx.config); + push_separator(args); push_unsigned(args, width as u64); "bfc" } else { push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[1], ctx); - push_separator(args, ctx.config); + push_separator(args); push_unsigned(args, lsb as u64); - push_separator(args, ctx.config); + push_separator(args); push_unsigned(args, width as u64); "bfi" }; @@ -554,11 +549,11 @@ where Cb: FnMut(InstructionPart) { let lsb = immr; let width = imms + 1 - lsb; push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[1], ctx); - push_separator(args, ctx.config); + push_separator(args); push_unsigned(args, lsb as u64); - push_separator(args, ctx.config); + push_separator(args); push_unsigned(args, width as u64); return "bfxil"; } @@ -575,12 +570,12 @@ where Cb: FnMut(InstructionPart) { { if let Operand::Immediate(7) = ins.operands[3] { push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[1], ctx); return "uxtb"; } else if let Operand::Immediate(15) = ins.operands[3] { push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[1], ctx); return "uxth"; } @@ -594,9 +589,9 @@ where Cb: FnMut(InstructionPart) { match (imms, size) { (63, SizeCode::X) | (31, SizeCode::W) => { push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[1], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[2], ctx); return "lsr"; } @@ -609,19 +604,19 @@ where Cb: FnMut(InstructionPart) { }; if imms + 1 == immr { push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[1], ctx); - push_separator(args, ctx.config); + push_separator(args); push_unsigned(args, (size - imms - 1) as u64); return "lsl"; } if imms < immr { push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[1], ctx); - push_separator(args, ctx.config); + push_separator(args); push_unsigned(args, (size - immr) as u64); - push_separator(args, ctx.config); + push_separator(args); push_unsigned(args, (imms + 1) as u64); return "ubfiz"; } @@ -637,11 +632,11 @@ where Cb: FnMut(InstructionPart) { unreachable!("last two operands of ubfm are always immediates"); }; push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[1], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[2], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &width, ctx); return "ubfx"; } @@ -649,9 +644,9 @@ where Cb: FnMut(InstructionPart) { if let Operand::Immediate(63) = ins.operands[3] { if let Operand::Register(SizeCode::X, _) = ins.operands[0] { push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[1], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[2], ctx); return "asr"; } @@ -659,9 +654,9 @@ where Cb: FnMut(InstructionPart) { if let Operand::Immediate(31) = ins.operands[3] { if let Operand::Register(SizeCode::W, _) = ins.operands[0] { push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[1], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[2], ctx); return "asr"; } @@ -674,17 +669,17 @@ where Cb: FnMut(InstructionPart) { }; if let Operand::Immediate(7) = ins.operands[3] { push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &newsrc, ctx); return "sxtb"; } else if let Operand::Immediate(15) = ins.operands[3] { push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &newsrc, ctx); return "sxth"; } else if let Operand::Immediate(31) = ins.operands[3] { push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &newsrc, ctx); return "sxtw"; } @@ -703,11 +698,11 @@ where Cb: FnMut(InstructionPart) { unreachable!("operand 0 is always a register"); }; push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[1], ctx); - push_separator(args, ctx.config); + push_separator(args); push_unsigned(args, (size - imms) as u64); - push_separator(args, ctx.config); + push_separator(args); push_unsigned(args, (immr + 1) as u64); return "sbfiz"; } @@ -721,11 +716,11 @@ where Cb: FnMut(InstructionPart) { unreachable!("last two operands of sbfm are always immediates"); }; push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[1], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[2], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &width, ctx); return "sbfx"; } @@ -737,9 +732,9 @@ where Cb: FnMut(InstructionPart) { { if rn == rm { push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[2], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[3], ctx); return "ror"; } @@ -853,25 +848,25 @@ where Cb: FnMut(InstructionPart) { Opcode::MRS => "mrs", Opcode::SYS(ops) => { push_unsigned(args, ops.op1() as u64); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[1], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[2], ctx); - push_separator(args, ctx.config); + push_separator(args); push_unsigned(args, ops.op2() as u64); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[0], ctx); return "sys"; } Opcode::SYSL(ops) => { push_operand(args, &ins.operands[2], ctx); - push_separator(args, ctx.config); + push_separator(args); push_unsigned(args, ops.op1() as u64); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[1], ctx); - push_separator(args, ctx.config); + push_separator(args); push_unsigned(args, ops.op2() as u64); return "sysl"; } @@ -933,9 +928,9 @@ where Cb: FnMut(InstructionPart) { { if cond < 0b1110 && rn == rm { push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[2], ctx); - push_separator(args, ctx.config); + push_separator(args); push_condition_code(args, cond ^ 0x01); return "cneg"; } @@ -954,14 +949,14 @@ where Cb: FnMut(InstructionPart) { if n == m && cond < 0b1110 { return if n == 31 { push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_condition_code(args, cond ^ 0x01); "cset" } else { push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[1], ctx); - push_separator(args, ctx.config); + push_separator(args); push_condition_code(args, cond ^ 0x01); "cinc" }; @@ -978,14 +973,14 @@ where Cb: FnMut(InstructionPart) { { if n == m && n != 31 && cond < 0b1110 { push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[1], ctx); - push_separator(args, ctx.config); + push_separator(args); push_condition_code(args, cond ^ 0x01); return "cinv"; } else if n == m && n == 31 && cond < 0b1110 { push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_condition_code(args, cond ^ 0x01); return "csetm"; } @@ -1003,9 +998,9 @@ where Cb: FnMut(InstructionPart) { Opcode::MADD => { if let Operand::Register(_, 31) = ins.operands[3] { push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[1], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[2], ctx); return "mul"; } @@ -1014,9 +1009,9 @@ where Cb: FnMut(InstructionPart) { Opcode::MSUB => { if let Operand::Register(_, 31) = ins.operands[3] { push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[1], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[2], ctx); return "mneg"; } @@ -1025,9 +1020,9 @@ where Cb: FnMut(InstructionPart) { Opcode::SMADDL => { if let Operand::Register(_, 31) = ins.operands[3] { push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[1], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[2], ctx); return "smull"; } @@ -1036,9 +1031,9 @@ where Cb: FnMut(InstructionPart) { Opcode::SMSUBL => { if let Operand::Register(_, 31) = ins.operands[3] { push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[1], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[2], ctx); return "smnegl"; } @@ -1048,9 +1043,9 @@ where Cb: FnMut(InstructionPart) { Opcode::UMADDL => { if let Operand::Register(_, 31) = ins.operands[3] { push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[1], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[2], ctx); return "umull"; } @@ -1059,9 +1054,9 @@ where Cb: FnMut(InstructionPart) { Opcode::UMSUBL => { if let Operand::Register(_, 31) = ins.operands[3] { push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[1], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[2], ctx); return "umnegl"; } @@ -1346,7 +1341,7 @@ where Cb: FnMut(InstructionPart) { || (reg_sz == SizeCode::X && elem_sz == SIMDSizeCode::D) { push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[1], ctx); return "mov"; } @@ -1453,7 +1448,7 @@ where Cb: FnMut(InstructionPart) { if rt == 31 && ar & 0b10 == 0b00 { let inst = if ar & 0b01 == 0b00 { "staddb" } else { "staddlb" }; push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[2], ctx); return inst; } @@ -1473,7 +1468,7 @@ where Cb: FnMut(InstructionPart) { if rt == 31 && ar & 0b10 == 0b00 { let inst = if ar & 0b01 == 0b00 { "stclrb" } else { "stclrlb" }; push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[2], ctx); return inst; } @@ -1493,7 +1488,7 @@ where Cb: FnMut(InstructionPart) { if rt == 31 && ar & 0b10 == 0b00 { let inst = if ar & 0b01 == 0b00 { "steorb" } else { "steorlb" }; push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[2], ctx); return inst; } @@ -1513,7 +1508,7 @@ where Cb: FnMut(InstructionPart) { if rt == 31 && ar & 0b10 == 0b00 { let inst = if ar & 0b01 == 0b00 { "stsetb" } else { "stsetlb" }; push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[2], ctx); return inst; } @@ -1533,7 +1528,7 @@ where Cb: FnMut(InstructionPart) { if rt == 31 && ar & 0b10 == 0b00 { let inst = if ar & 0b01 == 0b00 { "stsmaxb" } else { "stsmaxlb" }; push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[2], ctx); return inst; } @@ -1553,7 +1548,7 @@ where Cb: FnMut(InstructionPart) { if rt == 31 && ar & 0b10 == 0b00 { let inst = if ar & 0b01 == 0b00 { "stsminb" } else { "stsminlb" }; push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[2], ctx); return inst; } @@ -1573,7 +1568,7 @@ where Cb: FnMut(InstructionPart) { if rt == 31 && ar & 0b10 == 0b00 { let inst = if ar & 0b01 == 0b00 { "stumaxb" } else { "stumaxlb" }; push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[2], ctx); return inst; } @@ -1593,7 +1588,7 @@ where Cb: FnMut(InstructionPart) { if rt == 31 && ar & 0b10 == 0b00 { let inst = if ar & 0b01 == 0b00 { "stuminb" } else { "stuminlb" }; push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[2], ctx); return inst; } @@ -1614,7 +1609,7 @@ where Cb: FnMut(InstructionPart) { if rt == 31 && ar & 0b10 == 0b00 { let inst = if ar & 0b01 == 0b00 { "staddh" } else { "staddlh" }; push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[2], ctx); return inst; } @@ -1634,7 +1629,7 @@ where Cb: FnMut(InstructionPart) { if rt == 31 && ar & 0b10 == 0b00 { let inst = if ar & 0b01 == 0b00 { "stclrh" } else { "stclrlh" }; push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[2], ctx); return inst; } @@ -1654,7 +1649,7 @@ where Cb: FnMut(InstructionPart) { if rt == 31 && ar & 0b10 == 0b00 { let inst = if ar & 0b01 == 0b00 { "steorh" } else { "steorlh" }; push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[2], ctx); return inst; } @@ -1674,7 +1669,7 @@ where Cb: FnMut(InstructionPart) { if rt == 31 && ar & 0b10 == 0b00 { let inst = if ar & 0b01 == 0b00 { "stseth" } else { "stsetlh" }; push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[2], ctx); return inst; } @@ -1694,7 +1689,7 @@ where Cb: FnMut(InstructionPart) { if rt == 31 && ar & 0b10 == 0b00 { let inst = if ar & 0b01 == 0b00 { "stsmaxh" } else { "stsmaxlh" }; push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[2], ctx); return inst; } @@ -1714,7 +1709,7 @@ where Cb: FnMut(InstructionPart) { if rt == 31 && ar & 0b10 == 0b00 { let inst = if ar & 0b01 == 0b00 { "stsminh" } else { "stsminlh" }; push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[2], ctx); return inst; } @@ -1734,7 +1729,7 @@ where Cb: FnMut(InstructionPart) { if rt == 31 && ar & 0b10 == 0b00 { let inst = if ar & 0b01 == 0b00 { "stumaxh" } else { "stumaxlh" }; push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[2], ctx); return inst; } @@ -1754,7 +1749,7 @@ where Cb: FnMut(InstructionPart) { if rt == 31 && ar & 0b10 == 0b00 { let inst = if ar & 0b01 == 0b00 { "stuminh" } else { "stuminlh" }; push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[2], ctx); return inst; } @@ -1774,7 +1769,7 @@ where Cb: FnMut(InstructionPart) { if rt == 31 && ar & 0b10 == 0b00 { let inst = if ar & 0b01 == 0b00 { "stadd" } else { "staddl" }; push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[2], ctx); return inst; } @@ -1794,7 +1789,7 @@ where Cb: FnMut(InstructionPart) { if rt == 31 && ar & 0b10 == 0b00 { let inst = if ar & 0b01 == 0b00 { "stclr" } else { "stclrl" }; push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[2], ctx); return inst; } @@ -1814,7 +1809,7 @@ where Cb: FnMut(InstructionPart) { if rt == 31 && ar & 0b10 == 0b00 { let inst = if ar & 0b01 == 0b00 { "steor" } else { "steorl" }; push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[2], ctx); return inst; } @@ -1834,7 +1829,7 @@ where Cb: FnMut(InstructionPart) { if rt == 31 && ar & 0b10 == 0b00 { let inst = if ar & 0b01 == 0b00 { "stset" } else { "stsetl" }; push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[2], ctx); return inst; } @@ -1854,7 +1849,7 @@ where Cb: FnMut(InstructionPart) { if rt == 31 && ar & 0b10 == 0b00 { let inst = if ar & 0b01 == 0b00 { "stsmax" } else { "stsmaxl" }; push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[2], ctx); return inst; } @@ -1874,7 +1869,7 @@ where Cb: FnMut(InstructionPart) { if rt == 31 && ar & 0b10 == 0b00 { let inst = if ar & 0b01 == 0b00 { "stsmin" } else { "stsminl" }; push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[2], ctx); return inst; } @@ -1894,7 +1889,7 @@ where Cb: FnMut(InstructionPart) { if rt == 31 && ar & 0b10 == 0b00 { let inst = if ar & 0b01 == 0b00 { "stumax" } else { "stumaxl" }; push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[2], ctx); return inst; } @@ -1914,7 +1909,7 @@ where Cb: FnMut(InstructionPart) { if rt == 31 && ar & 0b10 == 0b00 { let inst = if ar & 0b01 == 0b00 { "stumin" } else { "stuminl" }; push_operand(args, &ins.operands[0], ctx); - push_separator(args, ctx.config); + push_separator(args); push_operand(args, &ins.operands[2], ctx); return inst; } @@ -2052,7 +2047,7 @@ where Cb: FnMut(InstructionPart) { break; } if i > 0 { - push_separator(args, ctx.config); + push_separator(args); } push_operand(args, o, ctx); } @@ -2135,13 +2130,13 @@ fn condition_code(cond: u8) -> &'static str { #[inline] fn push_register(args: &mut Cb, size: SizeCode, reg: u16, sp: bool) -where Cb: FnMut(InstructionPart) { +where Cb: FnMut(InstructionPart<'static>) { push_opaque(args, reg_name(size, reg, sp)); } #[inline] fn push_shift(args: &mut Cb, style: ShiftStyle, amount: u8) -where Cb: FnMut(InstructionPart) { +where Cb: FnMut(InstructionPart<'static>) { push_opaque(args, shift_style(style)); if amount != 0 { push_plain(args, " "); @@ -2151,12 +2146,12 @@ where Cb: FnMut(InstructionPart) { #[inline] fn push_condition_code(args: &mut Cb, cond: u8) -where Cb: FnMut(InstructionPart) { +where Cb: FnMut(InstructionPart<'static>) { push_opaque(args, condition_code(cond)); } fn push_barrier(args: &mut Cb, option: u8) -where Cb: FnMut(InstructionPart) { +where Cb: FnMut(InstructionPart<'static>) { match option { 0b0001 => push_opaque(args, "oshld"), 0b0010 => push_opaque(args, "oshst"), @@ -2175,41 +2170,35 @@ where Cb: FnMut(InstructionPart) { } #[inline] -fn push_opaque(args: &mut Cb, text: &'static str) -where Cb: FnMut(InstructionPart) { - push_arg(args, InstructionArg::Value(InstructionArgValue::Opaque(Cow::Borrowed(text)))); +fn push_opaque<'a, Cb>(args: &mut Cb, text: &'a str) +where Cb: FnMut(InstructionPart<'a>) { + args(InstructionPart::opaque(text)); } #[inline] fn push_plain(args: &mut Cb, text: &'static str) -where Cb: FnMut(InstructionPart) { - args(InstructionPart::Basic(text)); +where Cb: FnMut(InstructionPart<'static>) { + args(InstructionPart::basic(text)); } #[inline] -fn push_separator(args: &mut Cb, _config: &DiffObjConfig) -where Cb: FnMut(InstructionPart) { - args(InstructionPart::Separator); +fn push_separator(args: &mut Cb) +where Cb: FnMut(InstructionPart<'static>) { + args(InstructionPart::separator()); } #[inline] fn push_unsigned(args: &mut Cb, v: u64) -where Cb: FnMut(InstructionPart) { +where Cb: FnMut(InstructionPart<'static>) { push_plain(args, "#"); - push_arg(args, InstructionArg::Value(InstructionArgValue::Unsigned(v))); + args(InstructionPart::unsigned(v)); } #[inline] fn push_signed(args: &mut Cb, v: i64) -where Cb: FnMut(InstructionPart) { +where Cb: FnMut(InstructionPart<'static>) { push_plain(args, "#"); - push_arg(args, InstructionArg::Value(InstructionArgValue::Signed(v))); -} - -#[inline] -fn push_arg(args: &mut Cb, arg: InstructionArg) -where Cb: FnMut(InstructionPart) { - args(InstructionPart::Arg(arg)); + args(InstructionPart::signed(v)); } /// Relocations that appear in Operand::PCOffset. @@ -2248,7 +2237,7 @@ fn is_reg_index_reloc(resolved: Option) -> bool { } fn push_operand(args: &mut Cb, o: &Operand, ctx: &mut DisplayCtx) -where Cb: FnMut(InstructionPart) { +where Cb: FnMut(InstructionPart<'static>) { match o { Operand::Nothing => unreachable!(), Operand::PCOffset(off) => { @@ -2260,19 +2249,19 @@ where Cb: FnMut(InstructionPart) { { let dest = target_address.unwrap(); push_plain(args, "$"); - push_arg(args, InstructionArg::BranchDest(dest)); + args(InstructionPart::branch_dest(dest)); } else { - push_arg(args, InstructionArg::Reloc); + args(InstructionPart::reloc()); } } else { let dest = ctx.address.saturating_add_signed(*off); push_plain(args, "$"); - push_arg(args, InstructionArg::BranchDest(dest)); + args(InstructionPart::branch_dest(dest)); } } Operand::Immediate(imm) => { if is_imm_reloc(ctx.reloc) { - push_arg(args, InstructionArg::Reloc); + args(InstructionPart::reloc()); } else { push_unsigned(args, *imm as u64); } @@ -2288,7 +2277,7 @@ where Cb: FnMut(InstructionPart) { } Operand::RegisterPair(size, reg) => { push_register(args, *size, *reg, false); - push_separator(args, ctx.config); + push_separator(args); push_register(args, *size, *reg + 1, false); } Operand::RegisterOrSP(size, reg) => { @@ -2316,7 +2305,7 @@ where Cb: FnMut(InstructionPart) { Operand::ImmShift(i, shift) => { push_unsigned(args, *i as u64); if *shift > 0 { - push_separator(args, ctx.config); + push_separator(args); push_opaque(args, "lsl"); push_plain(args, " "); push_unsigned(args, *shift as u64); @@ -2325,7 +2314,7 @@ where Cb: FnMut(InstructionPart) { Operand::ImmShiftMSL(i, shift) => { push_unsigned(args, *i as u64); if *shift > 0 { - push_separator(args, ctx.config); + push_separator(args); push_opaque(args, "msl"); push_plain(args, " "); push_unsigned(args, *shift as u64); @@ -2339,7 +2328,7 @@ where Cb: FnMut(InstructionPart) { { // pass } else { - push_separator(args, ctx.config); + push_separator(args); push_shift(args, *shift_type, *amount); } } @@ -2348,7 +2337,7 @@ where Cb: FnMut(InstructionPart) { if *shift_type == ShiftStyle::LSL && *amount == 0 { // pass } else { - push_separator(args, ctx.config); + push_separator(args); push_shift(args, *shift_type, *amount); } } @@ -2356,7 +2345,7 @@ where Cb: FnMut(InstructionPart) { Operand::RegRegOffset(reg, index_reg, index_size, extend, amount) => { push_plain(args, "["); push_register(args, SizeCode::X, *reg, true); - push_separator(args, ctx.config); + push_separator(args); push_register(args, *index_size, *index_reg, false); if extend == &ShiftStyle::LSL && *amount == 0 { // pass @@ -2364,10 +2353,10 @@ where Cb: FnMut(InstructionPart) { || (extend == &ShiftStyle::UXTX && index_size == &SizeCode::X)) && *amount == 0 { - push_separator(args, ctx.config); + push_separator(args); push_shift(args, *extend, 0); } else { - push_separator(args, ctx.config); + push_separator(args); push_shift(args, *extend, *amount); } push_plain(args, "]"); @@ -2376,10 +2365,10 @@ where Cb: FnMut(InstructionPart) { push_plain(args, "["); push_register(args, SizeCode::X, *reg, true); if is_reg_index_reloc(ctx.reloc) { - push_separator(args, ctx.config); - push_arg(args, InstructionArg::Reloc); + push_separator(args); + args(InstructionPart::reloc()); } else if *offset != 0 || *wback_bit { - push_separator(args, ctx.config); + push_separator(args); push_signed(args, *offset as i64); } push_plain(args, "]"); @@ -2391,9 +2380,9 @@ where Cb: FnMut(InstructionPart) { push_plain(args, "["); push_register(args, SizeCode::X, *reg, true); push_plain(args, "]"); - push_separator(args, ctx.config); + push_separator(args); if is_reg_index_reloc(ctx.reloc) { - push_arg(args, InstructionArg::Reloc); + args(InstructionPart::reloc()); } else { push_signed(args, *offset as i64); } @@ -2402,15 +2391,9 @@ where Cb: FnMut(InstructionPart) { push_plain(args, "["); push_register(args, SizeCode::X, *reg, true); push_plain(args, "]"); - push_separator(args, ctx.config); + push_separator(args); // TODO does 31 have to be handled separate? - push_arg( - args, - InstructionArg::Value(InstructionArgValue::Opaque(Cow::Owned(format!( - "x{}", - offset_reg - )))), - ); + args(InstructionPart::opaque(format!("x{}", offset_reg))); } // Fall back to original logic Operand::SIMDRegister(_, _) @@ -2424,10 +2407,7 @@ where Cb: FnMut(InstructionPart) { | Operand::SystemReg(_) | Operand::ControlReg(_) | Operand::PstateField(_) => { - push_arg( - args, - InstructionArg::Value(InstructionArgValue::Opaque(Cow::Owned(o.to_string()))), - ); + args(InstructionPart::opaque(o.to_string())); } } } diff --git a/objdiff-core/src/arch/mips.rs b/objdiff-core/src/arch/mips.rs index ececeef..9377491 100644 --- a/objdiff-core/src/arch/mips.rs +++ b/objdiff-core/src/arch/mips.rs @@ -159,7 +159,7 @@ impl Arch for ArchMips { let instruction = self.parse_ins_ref(ins_ref, code, diff_config)?; let display_flags = self.instruction_display_flags(diff_config); let opcode = instruction.opcode(); - cb(InstructionPart::Opcode(Cow::Borrowed(opcode.name()), opcode as u16))?; + cb(InstructionPart::opcode(opcode.name(), opcode as u16))?; push_args(&instruction, relocation, function_range, section_index, &display_flags, cb)?; Ok(()) } @@ -244,7 +244,7 @@ fn push_args( let operands = instruction.valued_operands_iter(); for (idx, op) in operands.enumerate() { if idx > 0 { - arg_cb(InstructionPart::Separator)?; + arg_cb(InstructionPart::separator())?; } match op { @@ -252,10 +252,10 @@ fn push_args( if let Some(resolved) = relocation { push_reloc(resolved.relocation, &mut arg_cb)?; } else { - arg_cb(InstructionPart::Arg(InstructionArg::Value(match imm { - IU16::Integer(s) => InstructionArgValue::Signed(s as i64), - IU16::Unsigned(u) => InstructionArgValue::Unsigned(u as u64), - })))?; + arg_cb(match imm { + IU16::Integer(s) => InstructionPart::signed(s), + IU16::Unsigned(u) => InstructionPart::unsigned(u), + })?; } } ValuedOperand::core_label(..) | ValuedOperand::core_branch_target_label(..) => { @@ -273,7 +273,7 @@ fn push_args( { // TODO move this logic up a level let target_address = target_address.unwrap(); - arg_cb(InstructionPart::Arg(InstructionArg::BranchDest(target_address)))?; + arg_cb(InstructionPart::branch_dest(target_address))?; } else { push_reloc(resolved.relocation, &mut arg_cb)?; } @@ -281,13 +281,11 @@ fn push_args( .get_branch_offset_generic() .map(|o| (instruction.vram() + o).inner() as u64) { - arg_cb(InstructionPart::Arg(InstructionArg::BranchDest(branch_dest)))?; + arg_cb(InstructionPart::branch_dest(branch_dest))?; } else { - arg_cb(InstructionPart::Arg(InstructionArg::Value( - InstructionArgValue::Opaque( - op.display(instruction, display_flags, None::<&str>).to_string().into(), - ), - )))?; + arg_cb(InstructionPart::opaque( + op.display(instruction, display_flags, None::<&str>).to_string(), + ))?; } } ValuedOperand::core_immediate_base(imm, base) => { @@ -299,28 +297,26 @@ fn push_args( IU16::Unsigned(u) => InstructionArgValue::Unsigned(u as u64), })))?; } - arg_cb(InstructionPart::Basic("("))?; - arg_cb(InstructionPart::Arg(InstructionArg::Value(InstructionArgValue::Opaque( - base.either_name(instruction.flags().abi(), display_flags.named_gpr()).into(), - ))))?; - arg_cb(InstructionPart::Basic(")"))?; + arg_cb(InstructionPart::basic("("))?; + arg_cb(InstructionPart::opaque( + base.either_name(instruction.flags().abi(), display_flags.named_gpr()), + ))?; + arg_cb(InstructionPart::basic(")"))?; } // ValuedOperand::r5900_immediate15(..) => match relocation { // Some(resolved) // if resolved.relocation.flags == RelocationFlags::Elf(R_MIPS15_S3) => // { - // push_reloc(&resolved.relocation, &mut arg_cb, &mut plain_cb)?; + // push_reloc(&resolved.relocation, &mut arg_cb)?; // } // _ => { - // arg_cb(InstructionArg::Value(InstructionArgValue::Opaque( - // op.disassemble(&instruction, None).into(), - // )))?; + // arg_cb(InstructionPart::opaque(op.disassemble(&instruction, None)))?; // } // }, _ => { - arg_cb(InstructionPart::Arg(InstructionArg::Value(InstructionArgValue::Opaque( - op.display(instruction, display_flags, None::<&str>).to_string().into(), - ))))?; + arg_cb(InstructionPart::opaque( + op.display(instruction, display_flags, None::<&str>).to_string(), + ))?; } } } @@ -334,36 +330,36 @@ fn push_reloc( match reloc.flags { RelocationFlags::Elf(r_type) => match r_type { elf::R_MIPS_HI16 => { - arg_cb(InstructionPart::Basic("%hi("))?; - arg_cb(InstructionPart::Arg(InstructionArg::Reloc))?; - arg_cb(InstructionPart::Basic(")"))?; + arg_cb(InstructionPart::basic("%hi("))?; + arg_cb(InstructionPart::reloc())?; + arg_cb(InstructionPart::basic(")"))?; } elf::R_MIPS_LO16 => { - arg_cb(InstructionPart::Basic("%lo("))?; - arg_cb(InstructionPart::Arg(InstructionArg::Reloc))?; - arg_cb(InstructionPart::Basic(")"))?; + arg_cb(InstructionPart::basic("%lo("))?; + arg_cb(InstructionPart::reloc())?; + arg_cb(InstructionPart::basic(")"))?; } elf::R_MIPS_GOT16 => { - arg_cb(InstructionPart::Basic("%got("))?; - arg_cb(InstructionPart::Arg(InstructionArg::Reloc))?; - arg_cb(InstructionPart::Basic(")"))?; + arg_cb(InstructionPart::basic("%got("))?; + arg_cb(InstructionPart::reloc())?; + arg_cb(InstructionPart::basic(")"))?; } elf::R_MIPS_CALL16 => { - arg_cb(InstructionPart::Basic("%call16("))?; - arg_cb(InstructionPart::Arg(InstructionArg::Reloc))?; - arg_cb(InstructionPart::Basic(")"))?; + arg_cb(InstructionPart::basic("%call16("))?; + arg_cb(InstructionPart::reloc())?; + arg_cb(InstructionPart::basic(")"))?; } elf::R_MIPS_GPREL16 => { - arg_cb(InstructionPart::Basic("%gp_rel("))?; - arg_cb(InstructionPart::Arg(InstructionArg::Reloc))?; - arg_cb(InstructionPart::Basic(")"))?; + arg_cb(InstructionPart::basic("%gp_rel("))?; + arg_cb(InstructionPart::reloc())?; + arg_cb(InstructionPart::basic(")"))?; } elf::R_MIPS_32 | elf::R_MIPS_26 | elf::R_MIPS_LITERAL | elf::R_MIPS_PC16 | R_MIPS15_S3 => { - arg_cb(InstructionPart::Arg(InstructionArg::Reloc))?; + arg_cb(InstructionPart::reloc())?; } _ => bail!("Unsupported ELF MIPS relocation type {r_type}"), }, diff --git a/objdiff-core/src/arch/mod.rs b/objdiff-core/src/arch/mod.rs index a3eafc9..fa92b06 100644 --- a/objdiff-core/src/arch/mod.rs +++ b/objdiff-core/src/arch/mod.rs @@ -8,8 +8,8 @@ use object::{File, Relocation, Section}; use crate::{ diff::{display::InstructionPart, DiffObjConfig}, obj::{ - InstructionRef, ParsedInstruction, RelocationFlags, ResolvedRelocation, ScannedInstruction, - SymbolFlagSet, SymbolKind, + InstructionArg, InstructionRef, ParsedInstruction, RelocationFlags, ResolvedRelocation, + ScannedInstruction, SymbolFlagSet, SymbolKind, }, util::ReallySigned, }; @@ -195,13 +195,18 @@ pub trait Arch: Send + Sync + Debug { diff_config, &mut |part| { match part { - InstructionPart::Opcode(m, _) => mnemonic = Some(m), - InstructionPart::Arg(arg) => args.push(arg), + InstructionPart::Opcode(m, _) => mnemonic = Some(Cow::Owned(m.into_owned())), + InstructionPart::Arg(arg) => args.push(arg.into_static()), _ => {} } Ok(()) }, )?; + // If the instruction has a relocation, but we didn't format it in the display, add it to + // the end of the arguments list. + if relocation.is_some() && !args.contains(&InstructionArg::Reloc) { + args.push(InstructionArg::Reloc); + } Ok(ParsedInstruction { ins_ref, mnemonic: mnemonic.unwrap_or_default(), args }) } diff --git a/objdiff-core/src/arch/ppc.rs b/objdiff-core/src/arch/ppc.rs index 306f6e5..8096948 100644 --- a/objdiff-core/src/arch/ppc.rs +++ b/objdiff-core/src/arch/ppc.rs @@ -18,8 +18,8 @@ use crate::{ arch::{Arch, DataType}, diff::{display::InstructionPart, DiffObjConfig}, obj::{ - InstructionArg, InstructionArgValue, InstructionRef, Relocation, RelocationFlags, - ResolvedRelocation, ScannedInstruction, Symbol, SymbolFlag, SymbolFlagSet, + InstructionRef, Relocation, RelocationFlags, ResolvedRelocation, ScannedInstruction, + Symbol, SymbolFlag, SymbolFlagSet, }, }; @@ -113,14 +113,14 @@ impl Arch for ArchPpc { let op = ppc750cl::Opcode::from(ins_ref.opcode as u8); let ins = ppc750cl::Ins { code, op }.simplified(); - cb(InstructionPart::Opcode(Cow::Borrowed(ins.mnemonic), ins_ref.opcode))?; + cb(InstructionPart::opcode(ins.mnemonic, ins_ref.opcode))?; let reloc_arg = self.find_reloc_arg(&ins, relocation); let mut writing_offset = false; for (idx, arg) in ins.args_iter().enumerate() { if idx > 0 && !writing_offset { - cb(InstructionPart::Separator)?; + cb(InstructionPart::separator())?; } if reloc_arg == Some(idx) { @@ -135,39 +135,22 @@ impl Arch for ArchPpc { } } else { match arg { - ppc750cl::Argument::Simm(simm) => { - cb(InstructionPart::Arg(InstructionArg::Value( - InstructionArgValue::Signed(simm.0 as i64), - )))?; - } - ppc750cl::Argument::Uimm(uimm) => { - cb(InstructionPart::Arg(InstructionArg::Value( - InstructionArgValue::Unsigned(uimm.0 as u64), - )))?; - } - ppc750cl::Argument::Offset(offset) => { - cb(InstructionPart::Arg(InstructionArg::Value( - InstructionArgValue::Signed(offset.0 as i64), - )))?; - } - ppc750cl::Argument::BranchDest(dest) => { - let dest = (ins_ref.address as u32).wrapping_add_signed(dest.0) as u64; - cb(InstructionPart::Arg(InstructionArg::BranchDest(dest)))?; - } - _ => { - cb(InstructionPart::Arg(InstructionArg::Value( - InstructionArgValue::Opaque(arg.to_string().into()), - )))?; - } - }; + ppc750cl::Argument::Simm(simm) => cb(InstructionPart::signed(simm.0)), + ppc750cl::Argument::Uimm(uimm) => cb(InstructionPart::unsigned(uimm.0)), + ppc750cl::Argument::Offset(offset) => cb(InstructionPart::signed(offset.0)), + ppc750cl::Argument::BranchDest(dest) => cb(InstructionPart::branch_dest( + (ins_ref.address as u32).wrapping_add_signed(dest.0), + )), + _ => cb(InstructionPart::opaque(arg.to_string())), + }?; } if writing_offset { - cb(InstructionPart::Basic(")"))?; + cb(InstructionPart::basic(")"))?; writing_offset = false; } if is_offset_arg(arg) { - cb(InstructionPart::Basic("("))?; + cb(InstructionPart::basic("("))?; writing_offset = true; } } @@ -276,33 +259,33 @@ fn display_reloc( match resolved.relocation.flags { RelocationFlags::Elf(r_type) => match r_type { elf::R_PPC_ADDR16_LO => { - cb(InstructionPart::Arg(InstructionArg::Reloc))?; - cb(InstructionPart::Basic("@l"))?; + cb(InstructionPart::reloc())?; + cb(InstructionPart::basic("@l"))?; } elf::R_PPC_ADDR16_HI => { - cb(InstructionPart::Arg(InstructionArg::Reloc))?; - cb(InstructionPart::Basic("@h"))?; + cb(InstructionPart::reloc())?; + cb(InstructionPart::basic("@h"))?; } elf::R_PPC_ADDR16_HA => { - cb(InstructionPart::Arg(InstructionArg::Reloc))?; - cb(InstructionPart::Basic("@ha"))?; + cb(InstructionPart::reloc())?; + cb(InstructionPart::basic("@ha"))?; } elf::R_PPC_EMB_SDA21 => { - cb(InstructionPart::Arg(InstructionArg::Reloc))?; - cb(InstructionPart::Basic("@sda21"))?; + cb(InstructionPart::reloc())?; + cb(InstructionPart::basic("@sda21"))?; } elf::R_PPC_ADDR32 | elf::R_PPC_UADDR32 | elf::R_PPC_REL24 | elf::R_PPC_REL14 => { - cb(InstructionPart::Arg(InstructionArg::Reloc))?; + cb(InstructionPart::reloc())?; } elf::R_PPC_NONE => { // Fake pool relocation. - cb(InstructionPart::Basic("<"))?; - cb(InstructionPart::Arg(InstructionArg::Reloc))?; - cb(InstructionPart::Basic(">"))?; + cb(InstructionPart::basic("<"))?; + cb(InstructionPart::reloc())?; + cb(InstructionPart::basic(">"))?; } - _ => cb(InstructionPart::Arg(InstructionArg::Reloc))?, + _ => cb(InstructionPart::reloc())?, }, - _ => cb(InstructionPart::Arg(InstructionArg::Reloc))?, + _ => cb(InstructionPart::reloc())?, }; Ok(()) } diff --git a/objdiff-core/src/arch/x86.rs b/objdiff-core/src/arch/x86.rs index 5ad6e23..0fa2e38 100644 --- a/objdiff-core/src/arch/x86.rs +++ b/objdiff-core/src/arch/x86.rs @@ -1,29 +1,17 @@ -use alloc::{ - borrow::Cow, - boxed::Box, - collections::BTreeMap, - format, - string::{String, ToString}, - vec, - vec::Vec, -}; -use std::ops::Range; +use alloc::{borrow::Cow, boxed::Box, format, string::String, vec::Vec}; +use core::ops::Range; -use anyhow::{anyhow, bail, ensure, Result}; +use anyhow::{anyhow, bail, Result}; use iced_x86::{ Decoder, DecoderOptions, DecoratorKind, FormatterOutput, FormatterTextKind, GasFormatter, - Instruction, IntelFormatter, MasmFormatter, NasmFormatter, NumberKind, OpKind, PrefixKind, - Register, + Instruction, IntelFormatter, MasmFormatter, NasmFormatter, NumberKind, OpKind, Register, }; use object::{pe, Endian as _, Object as _, ObjectSection as _}; use crate::{ arch::Arch, diff::{display::InstructionPart, DiffObjConfig, X86Formatter}, - obj::{ - InstructionArg, InstructionArgValue, InstructionRef, ParsedInstruction, RelocationFlags, - ResolvedRelocation, ScannedInstruction, - }, + obj::{InstructionRef, RelocationFlags, ResolvedRelocation, ScannedInstruction}, }; #[derive(Debug)] @@ -37,6 +25,10 @@ impl ArchX86 { Ok(Self { bits: if object.is_64() { 64 } else { 32 }, endianness: object.endianness() }) } + fn decoder<'a>(&self, code: &'a [u8], address: u64) -> Decoder<'a> { + Decoder::with_ip(self.bits, code, address, DecoderOptions::NONE) + } + fn formatter(&self, diff_config: &DiffObjConfig) -> Box { let mut formatter: Box = match diff_config.x86_formatter { X86Formatter::Intel => Box::new(IntelFormatter::new()), @@ -58,11 +50,10 @@ impl Arch for ArchX86 { _diff_config: &DiffObjConfig, ) -> Result> { let mut out = Vec::with_capacity(code.len() / 2); - let mut decoder = Decoder::with_ip(self.bits, code, address, DecoderOptions::NONE); + let mut decoder = self.decoder(code, address); let mut instruction = Instruction::default(); while decoder.can_decode() { decoder.decode_out(&mut instruction); - // TODO is this right? let branch_dest = match instruction.op0_kind() { OpKind::NearBranch16 => Some(instruction.near_branch16() as u64), OpKind::NearBranch32 => Some(instruction.near_branch32() as u64), @@ -86,110 +77,72 @@ impl Arch for ArchX86 { ins_ref: InstructionRef, code: &[u8], relocation: Option, - function_range: Range, - section_index: usize, + _function_range: Range, + _section_index: usize, diff_config: &DiffObjConfig, cb: &mut dyn FnMut(InstructionPart) -> Result<()>, ) -> Result<()> { - todo!() - } - - fn process_code( - &self, - address: u64, - code: &[u8], - _section_index: usize, - relocations: &[ObjReloc], - line_info: &BTreeMap, - config: &DiffObjConfig, - ) -> Result { - let mut result = ProcessCodeResult { ops: Vec::new(), insts: Vec::new() }; - let mut decoder = Decoder::with_ip(self.bits, code, address, DecoderOptions::NONE); - let mut formatter = self.formatter(config); - - let mut output = InstructionFormatterOutput { - formatted: String::new(), - ins: ObjIns { - address: 0, - size: 0, - op: 0, - mnemonic: Cow::Borrowed(""), - args: vec![], - reloc: None, - branch_dest: None, - line: None, - formatted: String::new(), - orig: None, - }, - error: None, - ins_operands: vec![], - }; + let mut decoder = self.decoder(code, ins_ref.address); + let mut formatter = self.formatter(diff_config); let mut instruction = Instruction::default(); - while decoder.can_decode() { - decoder.decode_out(&mut instruction); + decoder.decode_out(&mut instruction); - let address = instruction.ip(); - let op = instruction.mnemonic() as u16; - let reloc = relocations - .iter() - .find(|r| r.address >= address && r.address < address + instruction.len() as u64); - let line = line_info.range(..=address).last().map(|(_, &b)| b); - output.ins = ObjIns { - address, - size: instruction.len() as u8, - op, - mnemonic: Cow::Borrowed(""), - args: vec![], - reloc: reloc.cloned(), - branch_dest: None, - line, - formatted: String::new(), - 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.formatted.clone_from(&output.formatted); - - // Make sure we've put the relocation somewhere in the instruction - if reloc.is_some() - && !output.ins.args.iter().any(|a| matches!(a, InstructionArg::Reloc)) + // Determine where to insert relocation in instruction output. + // We replace the immediate or displacement with a placeholder value since the formatter + // doesn't provide enough information to know which number is the displacement inside a + // memory operand. + let mut reloc_replace = None; + if let Some(resolved) = relocation { + const PLACEHOLDER: u64 = 0x7BDEBE7D; // chosen by fair dice roll. + // guaranteed to be random. + let reloc_offset = resolved.relocation.address - ins_ref.address; + let reloc_size = reloc_size(resolved.relocation.flags).unwrap_or(usize::MAX); + let offsets = decoder.get_constant_offsets(&instruction); + if reloc_offset == offsets.displacement_offset() as u64 + && reloc_size == offsets.displacement_size() { - let mut found = replace_arg( - OpKind::Memory, - InstructionArg::Reloc, - &mut output.ins.args, - &instruction, - &output.ins_operands, - )?; - if !found { - found = replace_arg( - OpKind::Immediate32, - InstructionArg::Reloc, - &mut output.ins.args, - &instruction, - &output.ins_operands, - )?; + instruction.set_memory_displacement64(PLACEHOLDER); + // Formatter always writes the displacement as Int32 + reloc_replace = Some((OpKind::Memory, NumberKind::Int32, PLACEHOLDER)); + } else if reloc_offset == offsets.immediate_offset() as u64 + && reloc_size == offsets.immediate_size() + { + let is_branch = matches!( + instruction.op0_kind(), + OpKind::NearBranch16 | OpKind::NearBranch32 | OpKind::NearBranch64 + ); + let op_kind = if is_branch { + instruction.op0_kind() + } else { + match reloc_size { + 2 => OpKind::Immediate16, + 4 => OpKind::Immediate32, + 8 => OpKind::Immediate64, + _ => OpKind::default(), + } + }; + let number_kind = match reloc_size { + 2 => NumberKind::UInt16, + 4 => NumberKind::UInt32, + 8 => NumberKind::UInt64, + _ => NumberKind::default(), + }; + if is_branch { + instruction.set_near_branch64(PLACEHOLDER); + } else { + instruction.set_immediate32(PLACEHOLDER as u32); } - ensure!(found, "x86: Failed to find operand for Absolute relocation"); + reloc_replace = Some((op_kind, number_kind, PLACEHOLDER)); } - if reloc.is_some() - && !output.ins.args.iter().any(|a| matches!(a, InstructionArg::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) + + let mut output = + InstructionFormatterOutput { cb, reloc_replace, error: None, skip_next: false }; + formatter.format(&instruction, &mut output); + if let Some(error) = output.error.take() { + return Err(error); + } + Ok(()) } fn implcit_addend( @@ -202,7 +155,7 @@ impl Arch for ArchX86 { ) -> Result { match flags { RelocationFlags::Coff(pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32) => { - let data = section.data()[address as usize..address as usize + 4].try_into()?; + let data = section.data()?[address as usize..address as usize + 4].try_into()?; Ok(self.endianness.read_i32_bytes(data) as i64) } flags => bail!("Unsupported x86 implicit relocation {flags:?}"), @@ -231,171 +184,128 @@ impl Arch for ArchX86 { } fn get_reloc_byte_size(&self, flags: RelocationFlags) -> usize { - match flags { - RelocationFlags::Coff(typ) => match typ { - pe::IMAGE_REL_I386_DIR16 => 2, - pe::IMAGE_REL_I386_REL16 => 2, - pe::IMAGE_REL_I386_DIR32 => 4, - pe::IMAGE_REL_I386_REL32 => 4, - _ => 1, - }, - _ => 1, - } + reloc_size(flags).unwrap_or(1) } } -fn replace_arg( - from: OpKind, - to: InstructionArg, - args: &mut [InstructionArg], - 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; - } +fn reloc_size(flags: RelocationFlags) -> Option { + match flags { + RelocationFlags::Coff(typ) => match typ { + pe::IMAGE_REL_I386_DIR16 | pe::IMAGE_REL_I386_REL16 => Some(2), + pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32 => Some(4), + _ => None, + }, + _ => None, } - 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: ParsedInstruction, +struct InstructionFormatterOutput<'a> { + cb: &'a mut dyn FnMut(InstructionPart<'_>) -> Result<()>, + reloc_replace: Option<(OpKind, NumberKind, u64)>, error: Option, - ins_operands: Vec>, + skip_next: bool, } -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(InstructionArg::Value(InstructionArgValue::Opaque(v))) if v == "-") - { - self.ins - .args - .push(InstructionArg::Value(InstructionArgValue::Signed(value.wrapping_abs()))); - } else { - self.ins.args.push(InstructionArg::Value(InstructionArgValue::Signed(value))); - } - } -} - -impl FormatterOutput for InstructionFormatterOutput { - fn write(&mut self, text: &str, kind: FormatterTextKind) { - self.formatted.push_str(text); - // Skip whitespace after the mnemonic - if self.ins.args.is_empty() && kind == FormatterTextKind::Text { +impl InstructionFormatterOutput<'_> { + fn push_signed(&mut self, mut value: i64) { + if self.error.is_some() { return; } - self.ins_operands.push(None); + // 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 { + value = value.wrapping_abs(); + } + if let Err(e) = (self.cb)(InstructionPart::signed(value)) { + self.error = Some(e); + } + } +} + +impl FormatterOutput for InstructionFormatterOutput<'_> { + fn write(&mut self, text: &str, kind: FormatterTextKind) { + if self.error.is_some() { + return; + } + // Skip whitespace after the mnemonic + if self.skip_next { + self.skip_next = false; + if kind == FormatterTextKind::Text && text == " " { + return; + } + } match kind { FormatterTextKind::Text | FormatterTextKind::Punctuation => { - self.ins.args.push(InstructionArg::PlainText(text.to_string().into())); - } - FormatterTextKind::Keyword | FormatterTextKind::Operator => { - self.ins.args.push(InstructionArg::Value(InstructionArgValue::Opaque( - text.to_string().into(), - ))); - } - _ => { - if self.error.is_none() { - self.error = Some(anyhow!("x86: Unsupported FormatterTextKind {:?}", kind)); + if let Err(e) = (self.cb)(InstructionPart::basic(text)) { + self.error = Some(e); } } + FormatterTextKind::Prefix + | FormatterTextKind::Keyword + | FormatterTextKind::Operator => { + if let Err(e) = (self.cb)(InstructionPart::opaque(text)) { + self.error = Some(e); + } + } + _ => self.error = Some(anyhow!("x86: Unsupported FormatterTextKind {:?}", kind)), } } - fn write_prefix(&mut self, _instruction: &Instruction, text: &str, _prefix: PrefixKind) { - self.formatted.push_str(text); - self.ins_operands.push(None); - self.ins - .args - .push(InstructionArg::Value(InstructionArgValue::Opaque(text.to_string().into()))); - } - - fn write_mnemonic(&mut self, _instruction: &Instruction, text: &str) { - self.formatted.push_str(text); - self.ins.mnemonic = Cow::Owned(text.to_string()); + fn write_mnemonic(&mut self, instruction: &Instruction, text: &str) { + if self.error.is_some() { + return; + } + if let Err(e) = (self.cb)(InstructionPart::opcode(text, instruction.mnemonic() as u16)) { + self.error = Some(e); + } + // Skip whitespace after the mnemonic + self.skip_next = true; } fn write_number( &mut self, - _instruction: &Instruction, + instruction: &Instruction, _operand: u32, instruction_operand: Option, - text: &str, + _text: &str, value: u64, number_kind: NumberKind, kind: FormatterTextKind, ) { - self.formatted.push_str(text); - self.ins_operands.push(instruction_operand); + if self.error.is_some() { + return; + } - // Handle relocations - match kind { - FormatterTextKind::LabelAddress => { - if let Some(reloc) = self.ins.reloc.as_ref() { - if matches!( - reloc.flags, - RelocationFlags::Coff(pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32) - ) { - self.ins.args.push(InstructionArg::Reloc); - return; - } else if self.error.is_none() { - self.error = Some(anyhow!( - "x86: Unsupported LabelAddress relocation flags {:?}", - reloc.flags - )); - } + if let (Some(operand), Some((target_op_kind, target_number_kind, target_value))) = + (instruction_operand, self.reloc_replace) + { + if instruction.op_kind(operand) == target_op_kind + && number_kind == target_number_kind + && value == target_value + { + if let Err(e) = (self.cb)(InstructionPart::reloc()) { + self.error = Some(e); } - self.ins.args.push(InstructionArg::BranchDest(value)); - self.ins.branch_dest = Some(value); return; } - FormatterTextKind::FunctionAddress => { - if let Some(reloc) = self.ins.reloc.as_ref() { - if matches!(reloc.flags, RelocationFlags::Coff(pe::IMAGE_REL_I386_REL32)) { - self.ins.args.push(InstructionArg::Reloc); - return; - } else if self.error.is_none() { - self.error = Some(anyhow!( - "x86: Unsupported FunctionAddress relocation flags {:?}", - reloc.flags - )); - } - } + } + + if let FormatterTextKind::LabelAddress | FormatterTextKind::FunctionAddress = kind { + if let Err(e) = (self.cb)(InstructionPart::branch_dest(value)) { + self.error = Some(e); } - _ => {} + return; } 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::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(InstructionArg::Value(InstructionArgValue::Unsigned(value))); + if let Err(e) = (self.cb)(InstructionPart::unsigned(value)) { + self.error = Some(e); + } } } } @@ -404,27 +314,208 @@ impl FormatterOutput for InstructionFormatterOutput { &mut self, _instruction: &Instruction, _operand: u32, - instruction_operand: Option, + _instruction_operand: Option, text: &str, _decorator: DecoratorKind, ) { - self.formatted.push_str(text); - self.ins_operands.push(instruction_operand); - self.ins.args.push(InstructionArg::PlainText(text.to_string().into())); + if self.error.is_some() { + return; + } + if let Err(e) = (self.cb)(InstructionPart::basic(text)) { + self.error = Some(e); + } } fn write_register( &mut self, _instruction: &Instruction, _operand: u32, - instruction_operand: Option, + _instruction_operand: Option, text: &str, _register: Register, ) { - self.formatted.push_str(text); - self.ins_operands.push(instruction_operand); - self.ins - .args - .push(InstructionArg::Value(InstructionArgValue::Opaque(text.to_string().into()))); + if self.error.is_some() { + return; + } + if let Err(e) = (self.cb)(InstructionPart::opaque(text)) { + self.error = Some(e); + } + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::obj::Relocation; + + #[test] + fn test_scan_instructions() { + let arch = ArchX86 { bits: 32, endianness: object::Endianness::Little }; + let code = [ + 0xc7, 0x85, 0x68, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x8b, 0x04, 0x85, 0x00, + 0x00, 0x00, 0x00, + ]; + let scanned = arch.scan_instructions(0, &code, 0, &DiffObjConfig::default()).unwrap(); + assert_eq!(scanned.len(), 2); + assert_eq!(scanned[0].ins_ref.address, 0); + assert_eq!(scanned[0].ins_ref.size, 10); + assert_eq!(scanned[0].ins_ref.opcode, iced_x86::Mnemonic::Mov as u16); + assert_eq!(scanned[0].branch_dest, None); + assert_eq!(scanned[1].ins_ref.address, 10); + assert_eq!(scanned[1].ins_ref.size, 7); + assert_eq!(scanned[1].ins_ref.opcode, iced_x86::Mnemonic::Mov as u16); + assert_eq!(scanned[1].branch_dest, None); + } + + #[test] + fn test_process_instruction() { + let arch = ArchX86 { bits: 32, endianness: object::Endianness::Little }; + let code = [0xc7, 0x85, 0x68, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00]; + let opcode = iced_x86::Mnemonic::Mov as u16; + let mut parts = Vec::new(); + arch.display_instruction( + InstructionRef { address: 0x1234, size: 10, opcode }, + &code, + None, + 0x1234..0x2000, + 0, + &DiffObjConfig::default(), + &mut |part| { + parts.push(part.into_static()); + Ok(()) + }, + ) + .unwrap(); + assert_eq!(parts, &[ + InstructionPart::opcode("mov", opcode), + InstructionPart::opaque("dword"), + InstructionPart::basic(" "), + InstructionPart::opaque("ptr"), + InstructionPart::basic(" "), + InstructionPart::basic("["), + InstructionPart::opaque("ebp"), + InstructionPart::opaque("-"), + InstructionPart::signed(152i64), + InstructionPart::basic("]"), + InstructionPart::basic(","), + InstructionPart::basic(" "), + InstructionPart::unsigned(0u64), + ]); + } + + #[test] + fn test_process_instruction_with_reloc_1() { + let arch = ArchX86 { bits: 32, endianness: object::Endianness::Little }; + let code = [0xc7, 0x85, 0x68, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00]; + let opcode = iced_x86::Mnemonic::Mov as u16; + let mut parts = Vec::new(); + arch.display_instruction( + InstructionRef { address: 0x1234, size: 10, opcode }, + &code, + Some(ResolvedRelocation { + relocation: &Relocation { + flags: RelocationFlags::Coff(pe::IMAGE_REL_I386_DIR32), + address: 0x1234 + 6, + target_symbol: 0, + addend: 0, + }, + symbol: &Default::default(), + }), + 0x1234..0x2000, + 0, + &DiffObjConfig::default(), + &mut |part| { + parts.push(part.into_static()); + Ok(()) + }, + ) + .unwrap(); + assert_eq!(parts, &[ + InstructionPart::opcode("mov", opcode), + InstructionPart::opaque("dword"), + InstructionPart::basic(" "), + InstructionPart::opaque("ptr"), + InstructionPart::basic(" "), + InstructionPart::basic("["), + InstructionPart::opaque("ebp"), + InstructionPart::opaque("-"), + InstructionPart::signed(152i64), + InstructionPart::basic("]"), + InstructionPart::basic(","), + InstructionPart::basic(" "), + InstructionPart::reloc(), + ]); + } + + #[test] + fn test_process_instruction_with_reloc_2() { + let arch = ArchX86 { bits: 32, endianness: object::Endianness::Little }; + let code = [0x8b, 0x04, 0x85, 0x00, 0x00, 0x00, 0x00]; + let opcode = iced_x86::Mnemonic::Mov as u16; + let mut parts = Vec::new(); + arch.display_instruction( + InstructionRef { address: 0x1234, size: 7, opcode }, + &code, + Some(ResolvedRelocation { + relocation: &Relocation { + flags: RelocationFlags::Coff(pe::IMAGE_REL_I386_DIR32), + address: 0x1234 + 3, + target_symbol: 0, + addend: 0, + }, + symbol: &Default::default(), + }), + 0x1234..0x2000, + 0, + &DiffObjConfig::default(), + &mut |part| { + parts.push(part.into_static()); + Ok(()) + }, + ) + .unwrap(); + assert_eq!(parts, &[ + InstructionPart::opcode("mov", opcode), + InstructionPart::opaque("eax"), + InstructionPart::basic(","), + InstructionPart::basic(" "), + InstructionPart::basic("["), + InstructionPart::opaque("eax"), + InstructionPart::opaque("*"), + InstructionPart::signed(4), + InstructionPart::opaque("+"), + InstructionPart::reloc(), + InstructionPart::basic("]"), + ]); + } + + #[test] + fn test_process_instruction_with_reloc_3() { + let arch = ArchX86 { bits: 32, endianness: object::Endianness::Little }; + let code = [0xe8, 0x00, 0x00, 0x00, 0x00]; + let opcode = iced_x86::Mnemonic::Call as u16; + let mut parts = Vec::new(); + arch.display_instruction( + InstructionRef { address: 0x1234, size: 5, opcode }, + &code, + Some(ResolvedRelocation { + relocation: &Relocation { + flags: RelocationFlags::Coff(pe::IMAGE_REL_I386_REL32), + address: 0x1234 + 1, + target_symbol: 0, + addend: 0, + }, + symbol: &Default::default(), + }), + 0x1234..0x2000, + 0, + &DiffObjConfig::default(), + &mut |part| { + parts.push(part.into_static()); + Ok(()) + }, + ) + .unwrap(); + assert_eq!(parts, &[InstructionPart::opcode("call", opcode), InstructionPart::reloc()]); } } diff --git a/objdiff-core/src/diff/code.rs b/objdiff-core/src/diff/code.rs index 65c53b8..ad5e5c4 100644 --- a/objdiff-core/src/diff/code.rs +++ b/objdiff-core/src/diff/code.rs @@ -107,8 +107,8 @@ pub fn diff_code( right_obj, left_symbol_idx, right_symbol_idx, - left_row.ins_ref.as_ref(), - right_row.ins_ref.as_ref(), + left_row.ins_ref, + right_row.ins_ref, left_row, right_row, diff_config, @@ -226,7 +226,7 @@ fn resolve_branches( for ((i, ins_diff), ins) in rows.iter_mut().enumerate().filter(|(_, row)| row.ins_ref.is_some()).zip(ops) { - let branch_dest = if let Some(resolved) = section.relocation_at(ins.ins_ref.address, obj) { + let branch_dest = if let Some(resolved) = section.relocation_at(ins.ins_ref, obj) { if resolved.symbol.section == Some(section_index) { // If the relocation target is in the same section, use it as the branch destination resolved.symbol.address.checked_add_signed(resolved.relocation.addend) @@ -401,8 +401,8 @@ fn diff_instruction( right_obj: &Object, left_symbol_idx: usize, right_symbol_idx: usize, - l: Option<&InstructionRef>, - r: Option<&InstructionRef>, + l: Option, + r: Option, left_row: &InstructionDiffRow, right_row: &InstructionDiffRow, diff_config: &DiffObjConfig, @@ -439,8 +439,8 @@ fn diff_instruction( .ok_or_else(|| anyhow!("Missing section for symbol"))?; // Resolve relocations - let left_reloc = left_section.relocation_at(l.address, left_obj); - let right_reloc = right_section.relocation_at(r.address, right_obj); + let left_reloc = left_section.relocation_at(l, left_obj); + let right_reloc = right_section.relocation_at(r, right_obj); // Compare instruction data let left_data = left_section.data_range(l.address, l.size as usize).ok_or_else(|| { @@ -460,7 +460,7 @@ fn diff_instruction( if left_data != right_data { // If data doesn't match, process instructions and compare args let left_ins = left_obj.arch.process_instruction( - *l, + l, left_data, left_reloc, left_symbol.address..left_symbol.address + left_symbol.size, @@ -468,7 +468,7 @@ fn diff_instruction( diff_config, )?; let right_ins = left_obj.arch.process_instruction( - *r, + r, right_data, right_reloc, right_symbol.address..right_symbol.address + right_symbol.size, diff --git a/objdiff-core/src/diff/display.rs b/objdiff-core/src/diff/display.rs index 6a9bcae..00d6c86 100644 --- a/objdiff-core/src/diff/display.rs +++ b/objdiff-core/src/diff/display.rs @@ -12,14 +12,14 @@ use itertools::Itertools; use regex::Regex; use crate::{ - diff::{DiffObjConfig, InstructionArgDiffIndex, InstructionDiffRow, ObjectDiff, SymbolDiff}, + diff::{DiffObjConfig, InstructionDiffKind, InstructionDiffRow, ObjectDiff, SymbolDiff}, obj::{ InstructionArg, InstructionArgValue, Object, SectionFlag, SectionKind, Symbol, SymbolFlag, SymbolKind, }, }; -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Clone)] pub enum DiffText<'a> { /// Basic text Basic(&'a str), @@ -30,7 +30,7 @@ pub enum DiffText<'a> { /// Instruction mnemonic Opcode(&'a str, u16), /// Instruction argument - Argument(&'a InstructionArgValue), + Argument(InstructionArgValue<'a>), /// Branch destination BranchDest(u64), /// Symbol name @@ -38,63 +38,155 @@ pub enum DiffText<'a> { /// Relocation addend Addend(i64), /// Number of spaces - Spacing(usize), + Spacing(u8), /// End of line Eol, } +#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, Hash)] +pub enum DiffTextColor { + #[default] + Normal, // Grey + Dim, // Dark grey + Bright, // White + Replace, // Blue + Delete, // Red + Insert, // Green + Rotating(u8), +} + +#[derive(Debug, Clone)] +pub struct DiffTextSegment<'a> { + pub text: DiffText<'a>, + pub color: DiffTextColor, + pub pad_to: u8, +} + +impl<'a> DiffTextSegment<'a> { + #[inline(always)] + pub fn basic(text: &'a str, color: DiffTextColor) -> Self { + Self { text: DiffText::Basic(text), color, pad_to: 0 } + } + + #[inline(always)] + pub fn spacing(spaces: u8) -> Self { + Self { text: DiffText::Spacing(spaces), color: DiffTextColor::Normal, pad_to: 0 } + } +} + +const EOL_SEGMENT: DiffTextSegment<'static> = + DiffTextSegment { text: DiffText::Eol, color: DiffTextColor::Normal, pad_to: 0 }; + #[derive(Debug, Default, Clone, PartialEq, Eq)] pub enum HighlightKind { #[default] None, Opcode(u16), - Argument(InstructionArgValue), + Argument(InstructionArgValue<'static>), Symbol(String), Address(u64), } -pub enum InstructionPart { - Basic(&'static str), - Opcode(Cow<'static, str>, u16), - Arg(InstructionArg), +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum InstructionPart<'a> { + Basic(Cow<'a, str>), + Opcode(Cow<'a, str>, u16), + Arg(InstructionArg<'a>), Separator, } +impl<'a> InstructionPart<'a> { + #[inline(always)] + pub fn basic(s: T) -> Self + where T: Into> { + InstructionPart::Basic(s.into()) + } + + #[inline(always)] + pub fn opcode(s: T, o: u16) -> Self + where T: Into> { + InstructionPart::Opcode(s.into(), o) + } + + #[inline(always)] + pub fn opaque(s: T) -> Self + where T: Into> { + InstructionPart::Arg(InstructionArg::Value(InstructionArgValue::Opaque(s.into()))) + } + + #[inline(always)] + pub fn signed(v: T) -> InstructionPart<'static> + where T: Into { + InstructionPart::Arg(InstructionArg::Value(InstructionArgValue::Signed(v.into()))) + } + + #[inline(always)] + pub fn unsigned(v: T) -> InstructionPart<'static> + where T: Into { + InstructionPart::Arg(InstructionArg::Value(InstructionArgValue::Unsigned(v.into()))) + } + + #[inline(always)] + pub fn branch_dest(v: T) -> InstructionPart<'static> + where T: Into { + InstructionPart::Arg(InstructionArg::BranchDest(v.into())) + } + + #[inline(always)] + pub fn reloc() -> InstructionPart<'static> { InstructionPart::Arg(InstructionArg::Reloc) } + + #[inline(always)] + pub fn separator() -> InstructionPart<'static> { InstructionPart::Separator } + + pub fn into_static(self) -> InstructionPart<'static> { + match self { + InstructionPart::Basic(s) => InstructionPart::Basic(Cow::Owned(s.into_owned())), + InstructionPart::Opcode(s, o) => InstructionPart::Opcode(Cow::Owned(s.into_owned()), o), + InstructionPart::Arg(a) => InstructionPart::Arg(a.into_static()), + InstructionPart::Separator => InstructionPart::Separator, + } + } +} + pub fn display_row( obj: &Object, symbol_index: usize, ins_row: &InstructionDiffRow, diff_config: &DiffObjConfig, - mut cb: impl FnMut(DiffText, InstructionArgDiffIndex) -> Result<()>, + mut cb: impl FnMut(DiffTextSegment) -> Result<()>, ) -> Result<()> { let Some(ins_ref) = ins_row.ins_ref else { - cb(DiffText::Eol, InstructionArgDiffIndex::NONE)?; + cb(EOL_SEGMENT)?; return Ok(()); }; let symbol = &obj.symbols[symbol_index]; let Some(section_index) = symbol.section else { - cb(DiffText::Eol, InstructionArgDiffIndex::NONE)?; + cb(DiffTextSegment::basic("", DiffTextColor::Delete))?; + cb(EOL_SEGMENT)?; return Ok(()); }; let section = &obj.sections[section_index]; let Some(data) = section.data_range(ins_ref.address, ins_ref.size as usize) else { - cb(DiffText::Eol, InstructionArgDiffIndex::NONE)?; + cb(DiffTextSegment::basic("", DiffTextColor::Delete))?; + cb(EOL_SEGMENT)?; return Ok(()); }; if let Some(line) = section.line_info.range(..=ins_ref.address).last().map(|(_, &b)| b) { - cb(DiffText::Line(line), InstructionArgDiffIndex::NONE)?; + cb(DiffTextSegment { text: DiffText::Line(line), color: DiffTextColor::Dim, pad_to: 5 })?; } - cb( - DiffText::Address(ins_ref.address.saturating_sub(symbol.address)), - InstructionArgDiffIndex::NONE, - )?; + cb(DiffTextSegment { + text: DiffText::Address(ins_ref.address.saturating_sub(symbol.address)), + color: DiffTextColor::Normal, + pad_to: 5, + })?; if let Some(branch) = &ins_row.branch_from { - cb(DiffText::Basic(" ~> "), InstructionArgDiffIndex::new(branch.branch_idx))?; + cb(DiffTextSegment::basic(" ~> ", DiffTextColor::Rotating(branch.branch_idx as u8)))?; } else { - cb(DiffText::Spacing(4), InstructionArgDiffIndex::NONE)?; + cb(DiffTextSegment::spacing(4))?; } let mut arg_idx = 0; - let relocation = section.relocation_at(ins_ref.address, obj); + let relocation = section.relocation_at(ins_ref, obj); + let mut displayed_relocation = false; obj.arch.display_instruction( ins_ref, data, @@ -104,47 +196,101 @@ pub fn display_row( diff_config, &mut |part| match part { InstructionPart::Basic(text) => { - cb(DiffText::Basic(text), InstructionArgDiffIndex::NONE) - } - InstructionPart::Opcode(mnemonic, opcode) => { - cb(DiffText::Opcode(mnemonic.as_ref(), opcode), InstructionArgDiffIndex::NONE) + if text.chars().all(|c| c == ' ') { + cb(DiffTextSegment::spacing(text.len() as u8)) + } else { + cb(DiffTextSegment::basic(&text, DiffTextColor::Normal)) + } } + InstructionPart::Opcode(mnemonic, opcode) => cb(DiffTextSegment { + text: DiffText::Opcode(mnemonic.as_ref(), opcode), + color: if ins_row.kind == InstructionDiffKind::OpMismatch { + DiffTextColor::Replace + } else { + DiffTextColor::Normal + }, + pad_to: 10, + }), InstructionPart::Arg(arg) => { let diff_index = ins_row.arg_diff.get(arg_idx).copied().unwrap_or_default(); arg_idx += 1; match arg { - InstructionArg::Value(ref value) => cb(DiffText::Argument(value), diff_index), + InstructionArg::Value(value) => cb(DiffTextSegment { + text: DiffText::Argument(value), + color: diff_index + .get() + .map_or(DiffTextColor::Normal, |i| DiffTextColor::Rotating(i as u8)), + pad_to: 0, + }), InstructionArg::Reloc => { + displayed_relocation = true; let resolved = relocation.unwrap(); - cb(DiffText::Symbol(resolved.symbol), diff_index)?; + let color = diff_index + .get() + .map_or(DiffTextColor::Bright, |i| DiffTextColor::Rotating(i as u8)); + cb(DiffTextSegment { + text: DiffText::Symbol(resolved.symbol), + color, + pad_to: 0, + })?; if resolved.relocation.addend != 0 { - cb(DiffText::Addend(resolved.relocation.addend), diff_index)?; + cb(DiffTextSegment { + text: DiffText::Addend(resolved.relocation.addend), + color, + pad_to: 0, + })?; } Ok(()) } InstructionArg::BranchDest(dest) => { if let Some(addr) = dest.checked_sub(symbol.address) { - cb(DiffText::BranchDest(addr), diff_index) + cb(DiffTextSegment { + text: DiffText::BranchDest(addr), + color: diff_index.get().map_or(DiffTextColor::Normal, |i| { + DiffTextColor::Rotating(i as u8) + }), + pad_to: 0, + }) } else { - cb( - DiffText::Argument(&InstructionArgValue::Opaque(Cow::Borrowed( - "", - ))), - diff_index, - ) + cb(DiffTextSegment { + text: DiffText::Argument(InstructionArgValue::Opaque( + Cow::Borrowed(""), + )), + color: diff_index.get().map_or(DiffTextColor::Normal, |i| { + DiffTextColor::Rotating(i as u8) + }), + pad_to: 0, + }) } } } } InstructionPart::Separator => { - cb(DiffText::Basic(diff_config.separator()), InstructionArgDiffIndex::NONE) + cb(DiffTextSegment::basic(diff_config.separator(), DiffTextColor::Normal)) } }, )?; - if let Some(branch) = &ins_row.branch_to { - cb(DiffText::Basic(" ~>"), InstructionArgDiffIndex::new(branch.branch_idx))?; + // Fallback for relocation that wasn't displayed + if relocation.is_some() && !displayed_relocation { + cb(DiffTextSegment::basic(" <", DiffTextColor::Normal))?; + let resolved = relocation.unwrap(); + let diff_index = ins_row.arg_diff.get(arg_idx).copied().unwrap_or_default(); + let color = + diff_index.get().map_or(DiffTextColor::Bright, |i| DiffTextColor::Rotating(i as u8)); + cb(DiffTextSegment { text: DiffText::Symbol(resolved.symbol), color, pad_to: 0 })?; + if resolved.relocation.addend != 0 { + cb(DiffTextSegment { + text: DiffText::Addend(resolved.relocation.addend), + color, + pad_to: 0, + })?; + } + cb(DiffTextSegment::basic(">", DiffTextColor::Normal))?; } - cb(DiffText::Eol, InstructionArgDiffIndex::NONE)?; + if let Some(branch) = &ins_row.branch_to { + cb(DiffTextSegment::basic(" ~>", DiffTextColor::Rotating(branch.branch_idx as u8)))?; + } + cb(EOL_SEGMENT)?; Ok(()) } @@ -164,13 +310,13 @@ impl PartialEq for DiffText<'_> { fn eq(&self, other: &HighlightKind) -> bool { other.eq(self) } } -impl From> for HighlightKind { - fn from(value: DiffText<'_>) -> Self { +impl From<&DiffText<'_>> for HighlightKind { + fn from(value: &DiffText<'_>) -> Self { match value { - DiffText::Opcode(_, op) => HighlightKind::Opcode(op), - DiffText::Argument(arg) => HighlightKind::Argument(arg.clone()), + DiffText::Opcode(_, op) => HighlightKind::Opcode(*op), + DiffText::Argument(arg) => HighlightKind::Argument(arg.to_static()), DiffText::Symbol(sym) => HighlightKind::Symbol(sym.name.to_string()), - DiffText::Address(addr) | DiffText::BranchDest(addr) => HighlightKind::Address(addr), + DiffText::Address(addr) | DiffText::BranchDest(addr) => HighlightKind::Address(*addr), _ => HighlightKind::None, } } @@ -267,7 +413,7 @@ fn symbol_matches_filter( if symbol.section.is_none() && !symbol.flags.contains(SymbolFlag::Common) { return false; } - if !show_hidden_symbols && symbol.flags.contains(SymbolFlag::Hidden) { + if !show_hidden_symbols && (symbol.size == 0 || symbol.flags.contains(SymbolFlag::Hidden)) { return false; } match filter { diff --git a/objdiff-core/src/obj/mod.rs b/objdiff-core/src/obj/mod.rs index 67aad97..c17df59 100644 --- a/objdiff-core/src/obj/mod.rs +++ b/objdiff-core/src/obj/mod.rs @@ -1,7 +1,14 @@ pub mod read; pub mod split_meta; -use alloc::{borrow::Cow, boxed::Box, collections::BTreeMap, string::String, vec, vec::Vec}; +use alloc::{ + borrow::Cow, + boxed::Box, + collections::BTreeMap, + string::{String, ToString}, + vec, + vec::Vec, +}; use core::{fmt, num::NonZeroU32}; use flagset::{flags, FlagSet}; @@ -98,11 +105,17 @@ impl Section { pub fn relocation_at<'obj>( &'obj self, - address: u64, + ins_ref: InstructionRef, obj: &'obj Object, ) -> Option> { - self.relocations.binary_search_by_key(&address, |r| r.address).ok().and_then(|i| { - let relocation = self.relocations.get(i)?; + match self.relocations.binary_search_by_key(&ins_ref.address, |r| r.address) { + Ok(i) => self.relocations.get(i), + Err(i) => self + .relocations + .get(i) + .take_if(|r| r.address < ins_ref.address + ins_ref.size as u64), + } + .and_then(|relocation| { let symbol = obj.symbols.get(relocation.target_symbol)?; Some(ResolvedRelocation { relocation, symbol }) }) @@ -110,13 +123,13 @@ impl Section { } #[derive(Debug, Clone, Eq, PartialEq)] -pub enum InstructionArgValue { +pub enum InstructionArgValue<'a> { Signed(i64), Unsigned(u64), - Opaque(Cow<'static, str>), + Opaque(Cow<'a, str>), } -impl InstructionArgValue { +impl InstructionArgValue<'_> { pub fn loose_eq(&self, other: &InstructionArgValue) -> bool { match (self, other) { (InstructionArgValue::Signed(a), InstructionArgValue::Signed(b)) => a == b, @@ -127,9 +140,27 @@ impl InstructionArgValue { _ => false, } } + + pub fn to_static(&self) -> InstructionArgValue<'static> { + match self { + InstructionArgValue::Signed(v) => InstructionArgValue::Signed(*v), + InstructionArgValue::Unsigned(v) => InstructionArgValue::Unsigned(*v), + InstructionArgValue::Opaque(v) => InstructionArgValue::Opaque(v.to_string().into()), + } + } + + pub fn into_static(self) -> InstructionArgValue<'static> { + match self { + InstructionArgValue::Signed(v) => InstructionArgValue::Signed(v), + InstructionArgValue::Unsigned(v) => InstructionArgValue::Unsigned(v), + InstructionArgValue::Opaque(v) => { + InstructionArgValue::Opaque(Cow::Owned(v.into_owned())) + } + } + } } -impl fmt::Display for InstructionArgValue { +impl fmt::Display for InstructionArgValue<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { InstructionArgValue::Signed(v) => write!(f, "{:#x}", ReallySigned(*v)), @@ -139,14 +170,14 @@ impl fmt::Display for InstructionArgValue { } } -#[derive(Debug, Clone)] -pub enum InstructionArg { - Value(InstructionArgValue), +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum InstructionArg<'a> { + Value(InstructionArgValue<'a>), Reloc, BranchDest(u64), } -impl InstructionArg { +impl InstructionArg<'_> { pub fn loose_eq(&self, other: &InstructionArg) -> bool { match (self, other) { (InstructionArg::Value(a), InstructionArg::Value(b)) => a.loose_eq(b), @@ -155,6 +186,22 @@ impl InstructionArg { _ => false, } } + + pub fn to_static(&self) -> InstructionArg<'static> { + match self { + InstructionArg::Value(v) => InstructionArg::Value(v.to_static()), + InstructionArg::Reloc => InstructionArg::Reloc, + InstructionArg::BranchDest(v) => InstructionArg::BranchDest(*v), + } + } + + pub fn into_static(self) -> InstructionArg<'static> { + match self { + InstructionArg::Value(v) => InstructionArg::Value(v.into_static()), + InstructionArg::Reloc => InstructionArg::Reloc, + InstructionArg::BranchDest(v) => InstructionArg::BranchDest(v), + } + } } #[derive(Copy, Clone, Debug)] @@ -174,7 +221,7 @@ pub struct ScannedInstruction { pub struct ParsedInstruction { pub ins_ref: InstructionRef, pub mnemonic: Cow<'static, str>, - pub args: Vec, + pub args: Vec>, } #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Default)] @@ -243,7 +290,7 @@ pub enum RelocationFlags { Coff(u16), } -#[derive(Clone, Copy)] +#[derive(Debug, Copy, Clone)] pub struct ResolvedRelocation<'a> { pub relocation: &'a Relocation, pub symbol: &'a Symbol, diff --git a/objdiff-core/src/obj/read.rs b/objdiff-core/src/obj/read.rs index 8133bed..be04861 100644 --- a/objdiff-core/src/obj/read.rs +++ b/objdiff-core/src/obj/read.rs @@ -4,6 +4,7 @@ use alloc::{ string::{String, ToString}, vec::Vec, }; +use core::cmp::Ordering; use anyhow::{bail, ensure, Context, Result}; use object::{Object as _, ObjectSection as _, ObjectSymbol as _}; @@ -32,16 +33,22 @@ fn map_symbol( arch: &dyn Arch, file: &object::File, symbol: &object::Symbol, + section_indices: &[usize], split_meta: Option<&SplitMeta>, ) -> Result { let mut name = symbol.name().context("Failed to process symbol name")?.to_string(); - let size = symbol.size(); + let mut size = symbol.size(); if let (object::SymbolKind::Section, Some(section)) = (symbol.kind(), symbol.section_index().and_then(|i| file.section_by_index(i).ok())) { let section_name = section.name().context("Failed to process section name")?; name = format!("[{}]", section_name); - // size = section.size(); + // For section symbols, set the size to zero. If the size is non-zero, it will be included + // in the diff. Most of the time, this is duplicative, given that we'll have function or + // object symbols that cover the same range. In the case of an empty section, the size + // inference logic below will set the size back to the section size, thus acting as a + // placeholder symbol. + size = 0; } let mut flags = arch.extra_symbol_flags(symbol); @@ -74,7 +81,7 @@ fn map_symbol( let virtual_address = split_meta .and_then(|m| m.virtual_addresses.as_ref()) .and_then(|v| v.get(symbol.index().0).cloned()); - let section = symbol.section_index().map(|i| map_section_index(file, i)); + let section = symbol.section_index().and_then(|i| section_indices.get(i.0).copied()); Ok(Symbol { name, @@ -89,39 +96,123 @@ fn map_symbol( }) } -fn map_section_index(file: &object::File, idx: object::SectionIndex) -> usize { - match file.format() { - object::BinaryFormat::Elf => idx.0 - 1, - _ => idx.0, - } -} - -fn map_symbol_index(file: &object::File, idx: object::SymbolIndex) -> usize { - match file.format() { - object::BinaryFormat::Elf => idx.0 - 1, - _ => idx.0, - } -} - fn map_symbols( arch: &dyn Arch, obj_file: &object::File, + sections: &[Section], + section_indices: &[usize], split_meta: Option<&SplitMeta>, -) -> Result> { - let mut symbols = Vec::::with_capacity(obj_file.symbols().count()); - for symbol in obj_file.symbols() { - symbols.push(map_symbol(arch, obj_file, &symbol, split_meta)?); +) -> Result<(Vec, Vec)> { + let symbol_count = obj_file.symbols().count(); + let mut symbols = Vec::::with_capacity(symbol_count); + let mut symbol_indices = Vec::::with_capacity(symbol_count + 1); + for obj_symbol in obj_file.symbols() { + if symbol_indices.len() <= obj_symbol.index().0 { + symbol_indices.resize(obj_symbol.index().0 + 1, usize::MAX); + } + let symbol = map_symbol(arch, obj_file, &obj_symbol, section_indices, split_meta)?; + symbol_indices[obj_symbol.index().0] = symbols.len(); + symbols.push(symbol); + } + + // Infer symbol sizes for 0-size symbols + infer_symbol_sizes(&mut symbols, sections); + + Ok((symbols, symbol_indices)) +} + +fn infer_symbol_sizes(symbols: &mut [Symbol], sections: &[Section]) { + // Create a sorted list of symbol indices by section + let mut symbols_with_section = Vec::::with_capacity(symbols.len()); + for (i, symbol) in symbols.iter().enumerate() { + if symbol.section.is_some() { + symbols_with_section.push(i); + } + } + symbols_with_section.sort_by(|a, b| { + let a = &symbols[*a]; + let b = &symbols[*b]; + a.section + .unwrap_or(usize::MAX) + .cmp(&b.section.unwrap_or(usize::MAX)) + .then_with(|| { + // Sort section symbols first + if a.kind == SymbolKind::Section { + Ordering::Less + } else if b.kind == SymbolKind::Section { + Ordering::Greater + } else { + Ordering::Equal + } + }) + .then_with(|| a.address.cmp(&b.address)) + .then_with(|| a.size.cmp(&b.size)) + }); + + // Set symbol sizes based on the next symbol's address + let mut iter_idx = 0; + while iter_idx < symbols_with_section.len() { + let symbol_idx = symbols_with_section[iter_idx]; + let symbol = &symbols[symbol_idx]; + iter_idx += 1; + if symbol.size != 0 { + continue; + } + let section_idx = symbol.section.unwrap(); + let next_symbol = match symbol.kind { + // For function/object symbols, find the next function/object symbol (in other words: + // skip over labels) + SymbolKind::Function | SymbolKind::Object => loop { + if iter_idx >= symbols_with_section.len() { + break None; + } + let next_symbol = &symbols[symbols_with_section[iter_idx]]; + if next_symbol.section != Some(section_idx) { + break None; + } + if let SymbolKind::Function | SymbolKind::Object = next_symbol.kind { + break Some(next_symbol); + } + iter_idx += 1; + }, + // For labels (or anything else), simply use the next symbol's address + SymbolKind::Unknown | SymbolKind::Section => symbols_with_section + .get(iter_idx) + .map(|&i| &symbols[i]) + .take_if(|s| s.section == Some(section_idx)), + }; + let next_address = next_symbol.map(|s| s.address).unwrap_or_else(|| { + let section = §ions[section_idx]; + section.address + section.size + }); + let new_size = next_address.saturating_sub(symbol.address); + if new_size > 0 { + let symbol = &mut symbols[symbol_idx]; + symbol.size = new_size; + if symbol.kind != SymbolKind::Section { + symbol.flags |= SymbolFlag::SizeInferred; + } + // Set symbol kind if unknown and size is non-zero + if symbol.kind == SymbolKind::Unknown { + symbol.kind = match sections[section_idx].kind { + SectionKind::Code => SymbolKind::Function, + SectionKind::Data | SectionKind::Bss => SymbolKind::Object, + _ => SymbolKind::Unknown, + }; + } + } } - Ok(symbols) } fn map_sections( - arch: &dyn Arch, + _arch: &dyn Arch, obj_file: &object::File, split_meta: Option<&SplitMeta>, -) -> Result> { +) -> Result<(Vec
, Vec)> { let mut section_names = BTreeMap::::new(); - let mut result = Vec::
::with_capacity(obj_file.sections().count()); + let section_count = obj_file.sections().count(); + let mut result = Vec::
::with_capacity(section_count); + let mut section_indices = Vec::::with_capacity(section_count + 1); for section in obj_file.sections() { let name = section.name().context("Failed to process section name")?; let kind = map_section_kind(§ion); @@ -142,12 +233,14 @@ fn map_sections( .and_then(|v| v.get(s.index().0).cloned()) }); - let relocations = map_relocations(arch, obj_file, §ion)?; - let unique_id = section_names.entry(name.to_string()).or_insert(0); let id = format!("{}-{}", name, unique_id); *unique_id += 1; + if section_indices.len() <= section.index().0 { + section_indices.resize(section.index().0 + 1, usize::MAX); + } + section_indices[section.index().0] = result.len(); result.push(Section { id, name: name.to_string(), @@ -156,33 +249,14 @@ fn map_sections( kind, data: SectionData(data), flags: Default::default(), - relocations, + relocations: Default::default(), virtual_address, line_info: Default::default(), }); } - Ok(result) + Ok((result, section_indices)) } -// result.sort_by(|a, b| a.address.cmp(&b.address).then(a.size.cmp(&b.size))); -// let mut iter = result.iter_mut().peekable(); -// while let Some(symbol) = iter.next() { -// if symbol.size == 0 { -// if let Some(next_symbol) = iter.peek() { -// symbol.size = next_symbol.address - symbol.address; -// } else { -// symbol.size = (section.address + section.size) - symbol.address; -// } -// // Set symbol kind if we ended up with a non-zero size -// if symbol.kind == ObjSymbolKind::Unknown && symbol.size > 0 { -// symbol.kind = match section.kind { -// ObjSectionKind::Code => ObjSymbolKind::Function, -// ObjSectionKind::Data | ObjSectionKind::Bss => ObjSymbolKind::Object, -// }; -// } -// } -// } - const LOW_PRIORITY_SYMBOLS: &[&str] = &["__gnu_compiled_c", "__gnu_compiled_cplusplus", "gcc2_compiled."]; @@ -230,6 +304,7 @@ fn map_relocations( arch: &dyn Arch, obj_file: &object::File, obj_section: &object::Section, + symbol_indices: &[usize], ) -> Result> { let mut relocations = Vec::::with_capacity(obj_section.relocations().count()); let mut ordered_symbols = None; @@ -269,7 +344,13 @@ fn map_relocations( } else { idx }; - map_symbol_index(obj_file, idx) + match symbol_indices.get(idx.0).copied() { + Some(i) => i, + None => { + log::warn!("Invalid symbol index {}", idx.0); + continue; + } + } } object::RelocationTarget::Absolute => { let section_name = obj_section.name()?; @@ -299,6 +380,7 @@ fn map_relocations( fn parse_line_info( obj_file: &object::File, sections: &mut [Section], + section_indices: &[usize], obj_data: &[u8], ) -> Result<()> { // DWARF 1.1 @@ -326,7 +408,6 @@ fn parse_line_info( } let address_delta = read_u32(obj_file, &mut section_data)? as u64; out_section.line_info.insert(base_address + address_delta, line_number); - log::debug!("Line: {:#x} -> {}", base_address + address_delta, line_number); } } } @@ -376,7 +457,7 @@ fn parse_line_info( // COFF if let object::File::Coff(coff) = obj_file { - parse_line_info_coff(coff, sections, obj_data)?; + parse_line_info_coff(coff, sections, section_indices, obj_data)?; } Ok(()) @@ -385,6 +466,7 @@ fn parse_line_info( fn parse_line_info_coff( coff: &object::coff::CoffFile, sections: &mut [Section], + section_indices: &[usize], obj_data: &[u8], ) -> Result<()> { use object::{ @@ -405,7 +487,9 @@ fn parse_line_info_coff( // Find this section in our out_section. If it's not in out_section, // skip it. - let Some(out_section) = sections.get_mut(sect.index().0) else { + let Some(out_section) = + section_indices.get(sect.index().0).and_then(|&i| sections.get_mut(i)) + else { continue; }; @@ -514,7 +598,12 @@ fn combine_sections( for (i, section) in sections.iter().enumerate() { match section.kind { SectionKind::Data | SectionKind::Bss => { - data_sections.entry(section.name.clone()).or_default().push(i); + let base_name = if let Some(i) = section.name.rfind('$') { + §ion.name[..i] + } else { + §ion.name + }; + data_sections.entry(base_name.to_string()).or_default().push(i); } SectionKind::Code => { text_sections.push(i); @@ -523,12 +612,12 @@ fn combine_sections( } } if config.combine_data_sections { - for (_, section_indices) in data_sections { - do_combine_sections(sections, symbols, §ion_indices)?; + for (combined_name, mut section_indices) in data_sections { + do_combine_sections(sections, symbols, &mut section_indices, combined_name)?; } } if config.combine_text_sections { - do_combine_sections(sections, symbols, &text_sections)?; + do_combine_sections(sections, symbols, &mut text_sections, ".text".to_string())?; } Ok(()) } @@ -536,11 +625,24 @@ fn combine_sections( fn do_combine_sections( sections: &mut [Section], symbols: &mut [Symbol], - section_indices: &[usize], + section_indices: &mut [usize], + combined_name: String, ) -> Result<()> { if section_indices.len() < 2 { return Ok(()); } + // Sort sections lexicographically by name (for COFF section groups) + section_indices.sort_by(|&a, &b| { + let a_name = §ions[a].name; + let b_name = §ions[b].name; + // .text$di < .text$mn < .text + if a_name.contains('$') && !b_name.contains('$') { + return Ordering::Less; + } else if !a_name.contains('$') && b_name.contains('$') { + return Ordering::Greater; + } + a_name.cmp(b_name) + }); let first_section_idx = section_indices[0]; // Calculate the new offset for each section @@ -548,7 +650,7 @@ fn do_combine_sections( let mut current_offset = 0; let mut data_size = 0; let mut num_relocations = 0; - for &i in section_indices { + for i in section_indices.iter().copied() { let section = §ions[i]; if section.address != 0 { bail!("Section {} ({}) has non-zero address", i, section.name); @@ -580,6 +682,8 @@ fn do_combine_sections( } { let first_section = &mut sections[first_section_idx]; + first_section.id = format!("{combined_name}-combined"); + first_section.name = combined_name; first_section.size = current_offset; first_section.data = SectionData(data); first_section.flags |= SectionFlag::Combined; @@ -659,9 +763,18 @@ pub fn parse(data: &[u8], config: &DiffObjConfig) -> Result { let obj_file = object::File::parse(data)?; let arch = new_arch(&obj_file)?; let split_meta = parse_split_meta(&obj_file)?; - let mut symbols = map_symbols(arch.as_ref(), &obj_file, split_meta.as_ref())?; - let mut sections = map_sections(arch.as_ref(), &obj_file, split_meta.as_ref())?; - parse_line_info(&obj_file, &mut sections, data)?; + let (mut sections, section_indices) = + map_sections(arch.as_ref(), &obj_file, split_meta.as_ref())?; + let (mut symbols, symbol_indices) = + map_symbols(arch.as_ref(), &obj_file, §ions, §ion_indices, split_meta.as_ref())?; + for obj_section in obj_file.sections() { + let section = &mut sections[section_indices[obj_section.index().0]]; + if section.kind != SectionKind::Unknown { + section.relocations = + map_relocations(arch.as_ref(), &obj_file, &obj_section, &symbol_indices)?; + } + } + parse_line_info(&obj_file, &mut sections, §ion_indices, data)?; if config.combine_data_sections || config.combine_text_sections { combine_sections(&mut sections, &mut symbols, config)?; } @@ -803,7 +916,8 @@ mod test { ..Default::default() }, ]; - do_combine_sections(&mut sections, &mut symbols, &[1, 2, 3]).unwrap(); + do_combine_sections(&mut sections, &mut symbols, &mut [1, 2, 3], ".data".to_string()) + .unwrap(); assert_eq!(sections[1].data.0, (1..=12).collect::>()); insta::assert_debug_snapshot!((sections, symbols)); } diff --git a/objdiff-core/src/obj/snapshots/objdiff_core__obj__read__test__combine_sections.snap b/objdiff-core/src/obj/snapshots/objdiff_core__obj__read__test__combine_sections.snap index ca8dc9c..5a8bfae 100644 --- a/objdiff-core/src/obj/snapshots/objdiff_core__obj__read__test__combine_sections.snap +++ b/objdiff-core/src/obj/snapshots/objdiff_core__obj__read__test__combine_sections.snap @@ -44,7 +44,7 @@ expression: "(sections, symbols)" virtual_address: None, }, Section { - id: ".data-0", + id: ".data-combined", name: ".data", address: 0, size: 12, diff --git a/objdiff-core/src/util.rs b/objdiff-core/src/util.rs index 42783ee..ecd60c6 100644 --- a/objdiff-core/src/util.rs +++ b/objdiff-core/src/util.rs @@ -6,7 +6,7 @@ use num_traits::PrimInt; use object::{Endian, Object}; // https://stackoverflow.com/questions/44711012/how-do-i-format-a-signed-integer-to-a-sign-aware-hexadecimal-representation -pub struct ReallySigned(pub(crate) N); +pub struct ReallySigned(pub N); impl fmt::LowerHex for ReallySigned { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { diff --git a/objdiff-core/tests/arch_x86.rs b/objdiff-core/tests/arch_x86.rs new file mode 100644 index 0000000..da0e249 --- /dev/null +++ b/objdiff-core/tests/arch_x86.rs @@ -0,0 +1,28 @@ +use objdiff_core::{diff, obj}; + +mod common; + +#[test] +#[cfg(feature = "x86")] +fn read_x86() { + let diff_config = diff::DiffObjConfig::default(); + let obj = obj::read::parse(include_object!("data/x86/staticdebug.obj"), &diff_config).unwrap(); + insta::assert_debug_snapshot!(obj); + let symbol_idx = obj.symbols.iter().position(|s| s.name == "?PrintThing@@YAXXZ").unwrap(); + let diff = diff::code::no_diff_code(&obj, symbol_idx, &diff_config).unwrap(); + insta::assert_debug_snapshot!(diff.instruction_rows); + let output = common::display_diff(&obj, &diff, symbol_idx, &diff_config); + insta::assert_snapshot!(output); +} + +#[test] +#[cfg(feature = "x86")] +fn read_x86_combine_sections() { + let diff_config = diff::DiffObjConfig { + combine_data_sections: true, + combine_text_sections: true, + ..Default::default() + }; + let obj = obj::read::parse(include_object!("data/x86/rtest.obj"), &diff_config).unwrap(); + insta::assert_debug_snapshot!(obj.sections); +} diff --git a/objdiff-core/tests/common.rs b/objdiff-core/tests/common.rs index b5fb408..3e1cb0a 100644 --- a/objdiff-core/tests/common.rs +++ b/objdiff-core/tests/common.rs @@ -1,5 +1,5 @@ use objdiff_core::{ - diff::{DiffObjConfig, SymbolDiff}, + diff::{display::DiffTextSegment, DiffObjConfig, SymbolDiff}, obj::Object, }; @@ -13,21 +13,16 @@ pub fn display_diff( for row in &diff.instruction_rows { output.push('['); let mut separator = false; - objdiff_core::diff::display::display_row( - &obj, - symbol_idx, - row, - &diff_config, - |text, diff_idx| { - if separator { - output.push_str(", "); - } else { - separator = true; - } - output.push_str(&format!("({:?}, {:?})", text, diff_idx.get())); - Ok(()) - }, - ) + objdiff_core::diff::display::display_row(&obj, symbol_idx, row, &diff_config, |segment| { + if separator { + output.push_str(", "); + } else { + separator = true; + } + let DiffTextSegment { text, color, pad_to } = segment; + output.push_str(&format!("({:?}, {:?}, {:?})", text, color, pad_to)); + Ok(()) + }) .unwrap(); output.push_str("]\n"); } diff --git a/objdiff-core/tests/snapshots/arch_ppc__diff_ppc.snap b/objdiff-core/tests/snapshots/arch_ppc__diff_ppc.snap index 97cbc82..d6cd2f4 100644 --- a/objdiff-core/tests/snapshots/arch_ppc__diff_ppc.snap +++ b/objdiff-core/tests/snapshots/arch_ppc__diff_ppc.snap @@ -49,10 +49,6 @@ expression: sections_display 59.02353, ), symbols: [ - SectionDisplaySymbol { - symbol: 1, - is_mapping_symbol: false, - }, SectionDisplaySymbol { symbol: 3, is_mapping_symbol: false, diff --git a/objdiff-core/tests/snapshots/arch_ppc__read_ppc-3.snap b/objdiff-core/tests/snapshots/arch_ppc__read_ppc-3.snap index 44e4bce..fb1ac3d 100644 --- a/objdiff-core/tests/snapshots/arch_ppc__read_ppc-3.snap +++ b/objdiff-core/tests/snapshots/arch_ppc__read_ppc-3.snap @@ -2,69 +2,69 @@ source: objdiff-core/tests/arch_ppc.rs expression: output --- -[(Address(0), None), (Spacing(4), None), (Opcode("srwi", 60), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Opaque("24")), None), (Eol, None)] -[(Address(4), None), (Spacing(4), None), (Opcode("cmpwi", 38), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Argument(Signed(-1)), None), (Eol, None)] -[(Address(8), None), (Spacing(4), None), (Opcode("bne", 43), None), (BranchDest(20), None), (Basic(" ~>"), Some(0)), (Eol, None)] -[(Address(12), None), (Spacing(4), None), (Opcode("li", 41), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Argument(Signed(-1)), None), (Eol, None)] -[(Address(16), None), (Spacing(4), None), (Opcode("b", 45), None), (BranchDest(32), None), (Basic(" ~>"), Some(1)), (Eol, None)] -[(Address(20), None), (Basic(" ~> "), Some(0)), (Opcode("lis", 42), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Unsigned(0)), None), (Eol, None)] -[(Address(24), None), (Spacing(4), None), (Opcode("addi", 41), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Signed(0)), None), (Eol, None)] -[(Address(28), None), (Spacing(4), None), (Opcode("lbzx", 94), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Opaque("r0")), None), (Eol, None)] -[(Address(32), None), (Basic(" ~> "), Some(1)), (Opcode("extrwi", 60), None), (Argument(Opaque("r5")), None), (Basic(", "), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Opaque("8")), None), (Basic(", "), None), (Argument(Opaque("8")), None), (Eol, None)] -[(Address(36), None), (Spacing(4), None), (Opcode("stb", 166), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), None), (Basic("@sda21"), None), (Eol, None)] -[(Address(40), None), (Spacing(4), None), (Opcode("cmpwi", 38), None), (Argument(Opaque("r5")), None), (Basic(", "), None), (Argument(Signed(-1)), None), (Eol, None)] -[(Address(44), None), (Spacing(4), None), (Opcode("bne", 43), None), (BranchDest(56), None), (Basic(" ~>"), Some(2)), (Eol, None)] -[(Address(48), None), (Spacing(4), None), (Opcode("li", 41), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Argument(Signed(-1)), None), (Eol, None)] -[(Address(52), None), (Spacing(4), None), (Opcode("b", 45), None), (BranchDest(68), None), (Basic(" ~>"), Some(3)), (Eol, None)] -[(Address(56), None), (Basic(" ~> "), Some(2)), (Opcode("lis", 42), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Unsigned(0)), None), (Eol, None)] -[(Address(60), None), (Spacing(4), None), (Opcode("addi", 41), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Signed(0)), None), (Eol, None)] -[(Address(64), None), (Spacing(4), None), (Opcode("lbzx", 94), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Opaque("r5")), None), (Eol, None)] -[(Address(68), None), (Basic(" ~> "), Some(3)), (Opcode("extrwi", 60), None), (Argument(Opaque("r5")), None), (Basic(", "), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Opaque("8")), None), (Basic(", "), None), (Argument(Opaque("16")), None), (Eol, None)] -[(Address(72), None), (Spacing(4), None), (Opcode("li", 41), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), None), (Basic("@sda21"), None), (Eol, None)] -[(Address(76), None), (Spacing(4), None), (Opcode("cmpwi", 38), None), (Argument(Opaque("r5")), None), (Basic(", "), None), (Argument(Signed(-1)), None), (Eol, None)] -[(Address(80), None), (Spacing(4), None), (Opcode("stb", 166), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Argument(Signed(1)), None), (Basic("("), None), (Argument(Opaque("r4")), None), (Basic(")"), None), (Eol, None)] -[(Address(84), None), (Spacing(4), None), (Opcode("bne", 43), None), (BranchDest(96), None), (Basic(" ~>"), Some(4)), (Eol, None)] -[(Address(88), None), (Spacing(4), None), (Opcode("li", 41), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Argument(Signed(-1)), None), (Eol, None)] -[(Address(92), None), (Spacing(4), None), (Opcode("b", 45), None), (BranchDest(108), None), (Basic(" ~>"), Some(5)), (Eol, None)] -[(Address(96), None), (Basic(" ~> "), Some(4)), (Opcode("lis", 42), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Unsigned(0)), None), (Eol, None)] -[(Address(100), None), (Spacing(4), None), (Opcode("addi", 41), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Signed(0)), None), (Eol, None)] -[(Address(104), None), (Spacing(4), None), (Opcode("lbzx", 94), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Opaque("r5")), None), (Eol, None)] -[(Address(108), None), (Basic(" ~> "), Some(5)), (Opcode("clrlwi", 60), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Opaque("24")), None), (Eol, None)] -[(Address(112), None), (Spacing(4), None), (Opcode("li", 41), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), None), (Basic("@sda21"), None), (Eol, None)] -[(Address(116), None), (Spacing(4), None), (Opcode("cmpwi", 38), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Signed(-1)), None), (Eol, None)] -[(Address(120), None), (Spacing(4), None), (Opcode("stb", 166), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Argument(Signed(2)), None), (Basic("("), None), (Argument(Opaque("r3")), None), (Basic(")"), None), (Eol, None)] -[(Address(124), None), (Spacing(4), None), (Opcode("bne", 43), None), (BranchDest(136), None), (Basic(" ~>"), Some(6)), (Eol, None)] -[(Address(128), None), (Spacing(4), None), (Opcode("li", 41), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Signed(-1)), None), (Eol, None)] -[(Address(132), None), (Spacing(4), None), (Opcode("b", 45), None), (BranchDest(148), None), (Basic(" ~>"), Some(7)), (Eol, None)] -[(Address(136), None), (Basic(" ~> "), Some(6)), (Opcode("lis", 42), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Unsigned(0)), None), (Eol, None)] -[(Address(140), None), (Spacing(4), None), (Opcode("addi", 41), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Signed(0)), None), (Eol, None)] -[(Address(144), None), (Spacing(4), None), (Opcode("lbzx", 94), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Opaque("r4")), None), (Eol, None)] -[(Address(148), None), (Basic(" ~> "), Some(7)), (Opcode("li", 41), None), (Argument(Opaque("r5")), None), (Basic(", "), None), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), None), (Basic("@sda21"), None), (Eol, None)] -[(Address(152), None), (Spacing(4), None), (Opcode("li", 41), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Argument(Signed(0)), None), (Eol, None)] -[(Address(156), None), (Spacing(4), None), (Opcode("stb", 166), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Signed(3)), None), (Basic("("), None), (Argument(Opaque("r5")), None), (Basic(")"), None), (Eol, None)] -[(Address(160), None), (Spacing(4), None), (Opcode("lis", 42), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Unsigned(0)), None), (Eol, None)] -[(Address(164), None), (Spacing(4), None), (Opcode("addi", 41), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Signed(0)), None), (Eol, None)] -[(Address(168), None), (Spacing(4), None), (Opcode("stb", 166), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Argument(Signed(4)), None), (Basic("("), None), (Argument(Opaque("r5")), None), (Basic(")"), None), (Eol, None)] -[(Address(172), None), (Spacing(4), None), (Opcode("li", 41), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Argument(Signed(45)), None), (Eol, None)] -[(Address(176), None), (Spacing(4), None), (Opcode("lbz", 162), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), None), (Basic("@sda21"), None), (Eol, None)] -[(Address(180), None), (Spacing(4), None), (Opcode("lbzx", 94), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Opaque("r3")), None), (Eol, None)] -[(Address(184), None), (Spacing(4), None), (Opcode("andi.", 66), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Unsigned(220)), None), (Eol, None)] -[(Address(188), None), (Spacing(4), None), (Opcode("bne", 43), None), (BranchDest(196), None), (Basic(" ~>"), Some(8)), (Eol, None)] -[(Address(192), None), (Spacing(4), None), (Opcode("stb", 166), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Argument(Signed(0)), None), (Basic("("), None), (Argument(Opaque("r5")), None), (Basic(")"), None), (Eol, None)] -[(Address(196), None), (Basic(" ~> "), Some(8)), (Opcode("lbzu", 163), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Signed(1)), None), (Basic("("), None), (Argument(Opaque("r5")), None), (Basic(")"), None), (Eol, None)] -[(Address(200), None), (Spacing(4), None), (Opcode("lbzx", 94), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Opaque("r3")), None), (Eol, None)] -[(Address(204), None), (Spacing(4), None), (Opcode("andi.", 66), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Unsigned(220)), None), (Eol, None)] -[(Address(208), None), (Spacing(4), None), (Opcode("bne", 43), None), (BranchDest(216), None), (Basic(" ~>"), Some(9)), (Eol, None)] -[(Address(212), None), (Spacing(4), None), (Opcode("stb", 166), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Argument(Signed(0)), None), (Basic("("), None), (Argument(Opaque("r5")), None), (Basic(")"), None), (Eol, None)] -[(Address(216), None), (Basic(" ~> "), Some(9)), (Opcode("lbzu", 163), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Signed(1)), None), (Basic("("), None), (Argument(Opaque("r5")), None), (Basic(")"), None), (Eol, None)] -[(Address(220), None), (Spacing(4), None), (Opcode("lbzx", 94), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Opaque("r3")), None), (Eol, None)] -[(Address(224), None), (Spacing(4), None), (Opcode("andi.", 66), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Unsigned(220)), None), (Eol, None)] -[(Address(228), None), (Spacing(4), None), (Opcode("bne", 43), None), (BranchDest(236), None), (Basic(" ~>"), Some(10)), (Eol, None)] -[(Address(232), None), (Spacing(4), None), (Opcode("stb", 166), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Argument(Signed(0)), None), (Basic("("), None), (Argument(Opaque("r5")), None), (Basic(")"), None), (Eol, None)] -[(Address(236), None), (Basic(" ~> "), Some(10)), (Opcode("lbzu", 163), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Signed(1)), None), (Basic("("), None), (Argument(Opaque("r5")), None), (Basic(")"), None), (Eol, None)] -[(Address(240), None), (Spacing(4), None), (Opcode("lbzx", 94), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Opaque("r3")), None), (Eol, None)] -[(Address(244), None), (Spacing(4), None), (Opcode("andi.", 66), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Unsigned(220)), None), (Eol, None)] -[(Address(248), None), (Spacing(4), None), (Opcode("bne", 43), None), (BranchDest(256), None), (Basic(" ~>"), Some(11)), (Eol, None)] -[(Address(252), None), (Spacing(4), None), (Opcode("stb", 166), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Argument(Signed(0)), None), (Basic("("), None), (Argument(Opaque("r5")), None), (Basic(")"), None), (Eol, None)] -[(Address(256), None), (Basic(" ~> "), Some(11)), (Opcode("li", 41), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), None), (Basic("@sda21"), None), (Eol, None)] -[(Address(260), None), (Spacing(4), None), (Opcode("blr", 47), None), (Eol, None)] +[(Address(0), Normal, 5), (Spacing(4), Normal, 0), (Opcode("srwi", 60), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("24")), Normal, 0), (Eol, Normal, 0)] +[(Address(4), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmpwi", 38), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 0), (Eol, Normal, 0)] +[(Address(8), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 43), Normal, 10), (BranchDest(20), Normal, 0), (Basic(" ~>"), Rotating(0), 0), (Eol, Normal, 0)] +[(Address(12), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 41), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 0), (Eol, Normal, 0)] +[(Address(16), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 45), Normal, 10), (BranchDest(32), Normal, 0), (Basic(" ~>"), Rotating(1), 0), (Eol, Normal, 0)] +[(Address(20), Normal, 5), (Basic(" ~> "), Rotating(0), 0), (Opcode("lis", 42), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@ha"), Normal, 0), (Eol, Normal, 0)] +[(Address(24), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 41), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@l"), Normal, 0), (Eol, Normal, 0)] +[(Address(28), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Eol, Normal, 0)] +[(Address(32), Normal, 5), (Basic(" ~> "), Rotating(1), 0), (Opcode("extrwi", 60), Normal, 10), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("8")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("8")), Normal, 0), (Eol, Normal, 0)] +[(Address(36), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 166), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), Bright, 0), (Basic("@sda21"), Normal, 0), (Eol, Normal, 0)] +[(Address(40), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmpwi", 38), Normal, 10), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 0), (Eol, Normal, 0)] +[(Address(44), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 43), Normal, 10), (BranchDest(56), Normal, 0), (Basic(" ~>"), Rotating(2), 0), (Eol, Normal, 0)] +[(Address(48), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 41), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 0), (Eol, Normal, 0)] +[(Address(52), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 45), Normal, 10), (BranchDest(68), Normal, 0), (Basic(" ~>"), Rotating(3), 0), (Eol, Normal, 0)] +[(Address(56), Normal, 5), (Basic(" ~> "), Rotating(2), 0), (Opcode("lis", 42), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@ha"), Normal, 0), (Eol, Normal, 0)] +[(Address(60), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 41), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@l"), Normal, 0), (Eol, Normal, 0)] +[(Address(64), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Eol, Normal, 0)] +[(Address(68), Normal, 5), (Basic(" ~> "), Rotating(3), 0), (Opcode("extrwi", 60), Normal, 10), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("8")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("16")), Normal, 0), (Eol, Normal, 0)] +[(Address(72), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 41), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), Bright, 0), (Basic("@sda21"), Normal, 0), (Eol, Normal, 0)] +[(Address(76), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmpwi", 38), Normal, 10), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 0), (Eol, Normal, 0)] +[(Address(80), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 166), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(1)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)] +[(Address(84), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 43), Normal, 10), (BranchDest(96), Normal, 0), (Basic(" ~>"), Rotating(4), 0), (Eol, Normal, 0)] +[(Address(88), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 41), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 0), (Eol, Normal, 0)] +[(Address(92), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 45), Normal, 10), (BranchDest(108), Normal, 0), (Basic(" ~>"), Rotating(5), 0), (Eol, Normal, 0)] +[(Address(96), Normal, 5), (Basic(" ~> "), Rotating(4), 0), (Opcode("lis", 42), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@ha"), Normal, 0), (Eol, Normal, 0)] +[(Address(100), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 41), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@l"), Normal, 0), (Eol, Normal, 0)] +[(Address(104), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Eol, Normal, 0)] +[(Address(108), Normal, 5), (Basic(" ~> "), Rotating(5), 0), (Opcode("clrlwi", 60), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("24")), Normal, 0), (Eol, Normal, 0)] +[(Address(112), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 41), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), Bright, 0), (Basic("@sda21"), Normal, 0), (Eol, Normal, 0)] +[(Address(116), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmpwi", 38), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 0), (Eol, Normal, 0)] +[(Address(120), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 166), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(2)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)] +[(Address(124), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 43), Normal, 10), (BranchDest(136), Normal, 0), (Basic(" ~>"), Rotating(6), 0), (Eol, Normal, 0)] +[(Address(128), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 41), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 0), (Eol, Normal, 0)] +[(Address(132), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 45), Normal, 10), (BranchDest(148), Normal, 0), (Basic(" ~>"), Rotating(7), 0), (Eol, Normal, 0)] +[(Address(136), Normal, 5), (Basic(" ~> "), Rotating(6), 0), (Opcode("lis", 42), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@ha"), Normal, 0), (Eol, Normal, 0)] +[(Address(140), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 41), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@l"), Normal, 0), (Eol, Normal, 0)] +[(Address(144), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Eol, Normal, 0)] +[(Address(148), Normal, 5), (Basic(" ~> "), Rotating(7), 0), (Opcode("li", 41), Normal, 10), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), Bright, 0), (Basic("@sda21"), Normal, 0), (Eol, Normal, 0)] +[(Address(152), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 41), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(0)), Normal, 0), (Eol, Normal, 0)] +[(Address(156), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 166), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(3)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)] +[(Address(160), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lis", 42), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__ctype_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@ha"), Normal, 0), (Eol, Normal, 0)] +[(Address(164), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 41), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__ctype_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@l"), Normal, 0), (Eol, Normal, 0)] +[(Address(168), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 166), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(4)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)] +[(Address(172), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 41), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(45)), Normal, 0), (Eol, Normal, 0)] +[(Address(176), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbz", 162), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), Bright, 0), (Basic("@sda21"), Normal, 0), (Eol, Normal, 0)] +[(Address(180), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Eol, Normal, 0)] +[(Address(184), Normal, 5), (Spacing(4), Normal, 0), (Opcode("andi.", 66), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Unsigned(220)), Normal, 0), (Eol, Normal, 0)] +[(Address(188), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 43), Normal, 10), (BranchDest(196), Normal, 0), (Basic(" ~>"), Rotating(8), 0), (Eol, Normal, 0)] +[(Address(192), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 166), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(0)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)] +[(Address(196), Normal, 5), (Basic(" ~> "), Rotating(8), 0), (Opcode("lbzu", 163), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(1)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)] +[(Address(200), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Eol, Normal, 0)] +[(Address(204), Normal, 5), (Spacing(4), Normal, 0), (Opcode("andi.", 66), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Unsigned(220)), Normal, 0), (Eol, Normal, 0)] +[(Address(208), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 43), Normal, 10), (BranchDest(216), Normal, 0), (Basic(" ~>"), Rotating(9), 0), (Eol, Normal, 0)] +[(Address(212), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 166), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(0)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)] +[(Address(216), Normal, 5), (Basic(" ~> "), Rotating(9), 0), (Opcode("lbzu", 163), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(1)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)] +[(Address(220), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Eol, Normal, 0)] +[(Address(224), Normal, 5), (Spacing(4), Normal, 0), (Opcode("andi.", 66), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Unsigned(220)), Normal, 0), (Eol, Normal, 0)] +[(Address(228), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 43), Normal, 10), (BranchDest(236), Normal, 0), (Basic(" ~>"), Rotating(10), 0), (Eol, Normal, 0)] +[(Address(232), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 166), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(0)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)] +[(Address(236), Normal, 5), (Basic(" ~> "), Rotating(10), 0), (Opcode("lbzu", 163), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(1)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)] +[(Address(240), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Eol, Normal, 0)] +[(Address(244), Normal, 5), (Spacing(4), Normal, 0), (Opcode("andi.", 66), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Unsigned(220)), Normal, 0), (Eol, Normal, 0)] +[(Address(248), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 43), Normal, 10), (BranchDest(256), Normal, 0), (Basic(" ~>"), Rotating(11), 0), (Eol, Normal, 0)] +[(Address(252), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 166), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(0)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)] +[(Address(256), Normal, 5), (Basic(" ~> "), Rotating(11), 0), (Opcode("li", 41), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), Bright, 0), (Basic("@sda21"), Normal, 0), (Eol, Normal, 0)] +[(Address(260), Normal, 5), (Spacing(4), Normal, 0), (Opcode("blr", 47), Normal, 10), (Eol, Normal, 0)] diff --git a/objdiff-core/tests/snapshots/arch_ppc__read_ppc.snap b/objdiff-core/tests/snapshots/arch_ppc__read_ppc.snap index c1c445d..89d1057 100644 --- a/objdiff-core/tests/snapshots/arch_ppc__read_ppc.snap +++ b/objdiff-core/tests/snapshots/arch_ppc__read_ppc.snap @@ -39,7 +39,7 @@ Object { name: "[.ctors]", demangled_name: None, address: 0, - size: 0, + size: 4, kind: Section, section: Some( 1, diff --git a/objdiff-core/tests/snapshots/arch_x86__read_x86-2.snap b/objdiff-core/tests/snapshots/arch_x86__read_x86-2.snap new file mode 100644 index 0000000..b1f6270 --- /dev/null +++ b/objdiff-core/tests/snapshots/arch_x86__read_x86-2.snap @@ -0,0 +1,97 @@ +--- +source: objdiff-core/tests/arch_x86.rs +expression: diff.instruction_rows +--- +[ + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 0, + size: 1, + opcode: 640, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1, + size: 2, + opcode: 414, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 3, + size: 5, + opcode: 640, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 8, + size: 5, + opcode: 59, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 13, + size: 3, + opcode: 7, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 16, + size: 1, + opcode: 590, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 17, + size: 1, + opcode: 662, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, +] diff --git a/objdiff-core/tests/snapshots/arch_x86__read_x86-3.snap b/objdiff-core/tests/snapshots/arch_x86__read_x86-3.snap new file mode 100644 index 0000000..a9f0e68 --- /dev/null +++ b/objdiff-core/tests/snapshots/arch_x86__read_x86-3.snap @@ -0,0 +1,11 @@ +--- +source: objdiff-core/tests/arch_x86.rs +expression: output +--- +[(Address(0), Normal, 5), (Spacing(4), Normal, 0), (Opcode("push", 640), Normal, 10), (Argument(Opaque("ebp")), Normal, 0), (Eol, Normal, 0)] +[(Address(1), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mov", 414), Normal, 10), (Argument(Opaque("ebp")), Normal, 0), (Basic(","), Normal, 0), (Spacing(1), Normal, 0), (Argument(Opaque("esp")), Normal, 0), (Eol, Normal, 0)] +[(Address(3), Normal, 5), (Spacing(4), Normal, 0), (Opcode("push", 640), Normal, 10), (Symbol(Symbol { name: "$SG526", demangled_name: None, address: 4, size: 6, kind: Object, section: Some(1), flags: FlagSet(Local | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)] +[(Address(8), Normal, 5), (Spacing(4), Normal, 0), (Opcode("call", 59), Normal, 10), (Symbol(Symbol { name: "_printf", demangled_name: None, address: 0, size: 0, kind: Function, section: None, flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)] +[(Address(13), Normal, 5), (Spacing(4), Normal, 0), (Opcode("add", 7), Normal, 10), (Argument(Opaque("esp")), Normal, 0), (Basic(","), Normal, 0), (Spacing(1), Normal, 0), (Argument(Unsigned(4)), Normal, 0), (Eol, Normal, 0)] +[(Address(16), Normal, 5), (Spacing(4), Normal, 0), (Opcode("pop", 590), Normal, 10), (Argument(Opaque("ebp")), Normal, 0), (Eol, Normal, 0)] +[(Address(17), Normal, 5), (Spacing(4), Normal, 0), (Opcode("ret", 662), Normal, 10), (Eol, Normal, 0)] diff --git a/objdiff-core/tests/snapshots/arch_x86__read_x86.snap b/objdiff-core/tests/snapshots/arch_x86__read_x86.snap new file mode 100644 index 0000000..ec87e24 --- /dev/null +++ b/objdiff-core/tests/snapshots/arch_x86__read_x86.snap @@ -0,0 +1,200 @@ +--- +source: objdiff-core/tests/arch_x86.rs +expression: obj +--- +Object { + arch: ArchX86 { + bits: 32, + endianness: Little, + }, + symbols: [ + Symbol { + name: "objdiffstaticdebug.cpp", + demangled_name: None, + address: 0, + size: 0, + kind: Unknown, + section: None, + flags: FlagSet(Local), + align: None, + virtual_address: None, + }, + Symbol { + name: "@comp.id", + demangled_name: None, + address: 0, + size: 0, + kind: Object, + section: None, + flags: FlagSet(Local), + align: None, + virtual_address: None, + }, + Symbol { + name: "[.drectve]", + demangled_name: None, + address: 0, + size: 38, + kind: Section, + section: Some( + 0, + ), + flags: FlagSet(Local), + align: None, + virtual_address: None, + }, + Symbol { + name: "[.data]", + demangled_name: None, + address: 0, + size: 0, + kind: Section, + section: Some( + 1, + ), + flags: FlagSet(Local), + align: None, + virtual_address: None, + }, + Symbol { + name: "?a@@3PAXA", + demangled_name: Some( + "void *a", + ), + address: 0, + size: 4, + kind: Object, + section: Some( + 1, + ), + flags: FlagSet(Global | SizeInferred), + align: None, + virtual_address: None, + }, + Symbol { + name: "[.text]", + demangled_name: None, + address: 0, + size: 0, + kind: Section, + section: Some( + 2, + ), + flags: FlagSet(Local), + align: None, + virtual_address: None, + }, + Symbol { + name: "?PrintThing@@YAXXZ", + demangled_name: Some( + "void __cdecl PrintThing(void)", + ), + address: 0, + size: 18, + kind: Function, + section: Some( + 2, + ), + flags: FlagSet(Local | SizeInferred), + align: None, + virtual_address: None, + }, + Symbol { + name: "_printf", + demangled_name: None, + address: 0, + size: 0, + kind: Function, + section: None, + flags: FlagSet(Global), + align: None, + virtual_address: None, + }, + Symbol { + name: "$SG526", + demangled_name: None, + address: 4, + size: 6, + kind: Object, + section: Some( + 1, + ), + flags: FlagSet(Local | SizeInferred), + align: None, + virtual_address: None, + }, + ], + sections: [ + Section { + id: ".drectve-0", + name: ".drectve", + address: 0, + size: 38, + kind: Unknown, + data: SectionData( + 0, + ), + flags: FlagSet(), + relocations: [], + line_info: {}, + virtual_address: None, + }, + Section { + id: ".data-0", + name: ".data", + address: 0, + size: 10, + kind: Data, + data: SectionData( + 10, + ), + flags: FlagSet(), + relocations: [ + Relocation { + flags: Coff( + 6, + ), + address: 0, + target_symbol: 6, + addend: 0, + }, + ], + line_info: {}, + virtual_address: None, + }, + Section { + id: ".text-0", + name: ".text", + address: 0, + size: 18, + kind: Code, + data: SectionData( + 18, + ), + flags: FlagSet(), + relocations: [ + Relocation { + flags: Coff( + 6, + ), + address: 4, + target_symbol: 8, + addend: 0, + }, + Relocation { + flags: Coff( + 20, + ), + address: 9, + target_symbol: 7, + addend: 0, + }, + ], + line_info: {}, + virtual_address: None, + }, + ], + split_meta: None, + path: None, + timestamp: None, +} diff --git a/objdiff-core/tests/snapshots/arch_x86__read_x86_combine_sections.snap b/objdiff-core/tests/snapshots/arch_x86__read_x86_combine_sections.snap new file mode 100644 index 0000000..c0f95d2 --- /dev/null +++ b/objdiff-core/tests/snapshots/arch_x86__read_x86_combine_sections.snap @@ -0,0 +1,940 @@ +--- +source: objdiff-core/tests/arch_x86.rs +expression: obj.sections +--- +[ + Section { + id: ".drectve-0", + name: ".drectve", + address: 0, + size: 47, + kind: Unknown, + data: SectionData( + 0, + ), + flags: FlagSet(), + relocations: [], + line_info: {}, + virtual_address: None, + }, + Section { + id: ".debug$S-0", + name: ".debug$S", + address: 0, + size: 100, + kind: Unknown, + data: SectionData( + 0, + ), + flags: FlagSet(), + relocations: [], + line_info: {}, + virtual_address: None, + }, + Section { + id: ".rdata-0", + name: ".rdata", + address: 0, + size: 0, + kind: Data, + data: SectionData( + 0, + ), + flags: FlagSet(Hidden), + relocations: [], + line_info: {}, + virtual_address: None, + }, + Section { + id: ".rdata-1", + name: ".rdata", + address: 0, + size: 0, + kind: Data, + data: SectionData( + 0, + ), + flags: FlagSet(Hidden), + relocations: [], + line_info: {}, + virtual_address: None, + }, + Section { + id: ".text$mn-0", + name: ".text$mn", + address: 0, + size: 0, + kind: Code, + data: SectionData( + 0, + ), + flags: FlagSet(Hidden), + relocations: [], + line_info: {}, + virtual_address: None, + }, + Section { + id: ".data-combined", + name: ".data", + address: 0, + size: 56, + kind: Data, + data: SectionData( + 56, + ), + flags: FlagSet(Combined), + relocations: [ + Relocation { + flags: Coff( + 6, + ), + address: 0, + target_symbol: 44, + addend: 0, + }, + Relocation { + flags: Coff( + 6, + ), + address: 16, + target_symbol: 44, + addend: 0, + }, + Relocation { + flags: Coff( + 6, + ), + address: 32, + target_symbol: 44, + addend: 0, + }, + Relocation { + flags: Coff( + 6, + ), + address: 48, + target_symbol: 6, + addend: 0, + }, + Relocation { + flags: Coff( + 6, + ), + address: 52, + target_symbol: 8, + addend: 0, + }, + ], + line_info: {}, + virtual_address: None, + }, + Section { + id: ".rdata-combined", + name: ".rdata", + address: 0, + size: 295, + kind: Data, + data: SectionData( + 295, + ), + flags: FlagSet(Combined), + relocations: [ + Relocation { + flags: Coff( + 6, + ), + address: 12, + target_symbol: 17, + addend: 0, + }, + Relocation { + flags: Coff( + 6, + ), + address: 16, + target_symbol: 19, + addend: 0, + }, + Relocation { + flags: Coff( + 6, + ), + address: 21, + target_symbol: 13, + addend: 0, + }, + Relocation { + flags: Coff( + 6, + ), + address: 45, + target_symbol: 15, + addend: 0, + }, + Relocation { + flags: Coff( + 6, + ), + address: 61, + target_symbol: 25, + addend: 0, + }, + Relocation { + flags: Coff( + 6, + ), + address: 65, + target_symbol: 27, + addend: 0, + }, + Relocation { + flags: Coff( + 6, + ), + address: 70, + target_symbol: 21, + addend: 0, + }, + Relocation { + flags: Coff( + 6, + ), + address: 94, + target_symbol: 23, + addend: 0, + }, + Relocation { + flags: Coff( + 6, + ), + address: 110, + target_symbol: 31, + addend: 0, + }, + Relocation { + flags: Coff( + 6, + ), + address: 114, + target_symbol: 33, + addend: 0, + }, + Relocation { + flags: Coff( + 6, + ), + address: 130, + target_symbol: 35, + addend: 0, + }, + Relocation { + flags: Coff( + 6, + ), + address: 134, + target_symbol: 37, + addend: 0, + }, + Relocation { + flags: Coff( + 6, + ), + address: 138, + target_symbol: 19, + addend: 0, + }, + Relocation { + flags: Coff( + 6, + ), + address: 142, + target_symbol: 39, + addend: 0, + }, + Relocation { + flags: Coff( + 6, + ), + address: 147, + target_symbol: 31, + addend: 0, + }, + Relocation { + flags: Coff( + 6, + ), + address: 171, + target_symbol: 33, + addend: 0, + }, + Relocation { + flags: Coff( + 6, + ), + address: 175, + target_symbol: 21, + addend: 0, + }, + Relocation { + flags: Coff( + 6, + ), + address: 199, + target_symbol: 23, + addend: 0, + }, + Relocation { + flags: Coff( + 6, + ), + address: 215, + target_symbol: 31, + addend: 0, + }, + Relocation { + flags: Coff( + 6, + ), + address: 219, + target_symbol: 33, + addend: 0, + }, + Relocation { + flags: Coff( + 6, + ), + address: 235, + target_symbol: 13, + addend: 0, + }, + Relocation { + flags: Coff( + 6, + ), + address: 239, + target_symbol: 15, + addend: 0, + }, + Relocation { + flags: Coff( + 6, + ), + address: 255, + target_symbol: 21, + addend: 0, + }, + Relocation { + flags: Coff( + 6, + ), + address: 259, + target_symbol: 23, + addend: 0, + }, + Relocation { + flags: Coff( + 6, + ), + address: 263, + target_symbol: 29, + addend: 0, + }, + Relocation { + flags: Coff( + 6, + ), + address: 267, + target_symbol: 11, + addend: 0, + }, + Relocation { + flags: Coff( + 6, + ), + address: 271, + target_symbol: 43, + addend: 0, + }, + Relocation { + flags: Coff( + 6, + ), + address: 275, + target_symbol: 41, + addend: 0, + }, + Relocation { + flags: Coff( + 6, + ), + address: 279, + target_symbol: 70, + addend: 0, + }, + Relocation { + flags: Coff( + 6, + ), + address: 283, + target_symbol: 56, + addend: 0, + }, + Relocation { + flags: Coff( + 6, + ), + address: 287, + target_symbol: 72, + addend: 0, + }, + Relocation { + flags: Coff( + 6, + ), + address: 291, + target_symbol: 59, + addend: 0, + }, + ], + line_info: {}, + virtual_address: None, + }, + Section { + id: ".rdata$r-1", + name: ".rdata$r", + address: 0, + size: 0, + kind: Data, + data: SectionData( + 0, + ), + flags: FlagSet(Hidden), + relocations: [], + line_info: {}, + virtual_address: None, + }, + Section { + id: ".rdata$r-2", + name: ".rdata$r", + address: 0, + size: 0, + kind: Data, + data: SectionData( + 0, + ), + flags: FlagSet(Hidden), + relocations: [], + line_info: {}, + virtual_address: None, + }, + Section { + id: ".data$rs-1", + name: ".data$rs", + address: 0, + size: 0, + kind: Data, + data: SectionData( + 0, + ), + flags: FlagSet(Hidden), + relocations: [], + line_info: {}, + virtual_address: None, + }, + Section { + id: ".rdata$r-3", + name: ".rdata$r", + address: 0, + size: 0, + kind: Data, + data: SectionData( + 0, + ), + flags: FlagSet(Hidden), + relocations: [], + line_info: {}, + virtual_address: None, + }, + Section { + id: ".rdata$r-4", + name: ".rdata$r", + address: 0, + size: 0, + kind: Data, + data: SectionData( + 0, + ), + flags: FlagSet(Hidden), + relocations: [], + line_info: {}, + virtual_address: None, + }, + Section { + id: ".rdata$r-5", + name: ".rdata$r", + address: 0, + size: 0, + kind: Data, + data: SectionData( + 0, + ), + flags: FlagSet(Hidden), + relocations: [], + line_info: {}, + virtual_address: None, + }, + Section { + id: ".rdata$r-6", + name: ".rdata$r", + address: 0, + size: 0, + kind: Data, + data: SectionData( + 0, + ), + flags: FlagSet(Hidden), + relocations: [], + line_info: {}, + virtual_address: None, + }, + Section { + id: ".data$rs-2", + name: ".data$rs", + address: 0, + size: 0, + kind: Data, + data: SectionData( + 0, + ), + flags: FlagSet(Hidden), + relocations: [], + line_info: {}, + virtual_address: None, + }, + Section { + id: ".rdata$r-7", + name: ".rdata$r", + address: 0, + size: 0, + kind: Data, + data: SectionData( + 0, + ), + flags: FlagSet(Hidden), + relocations: [], + line_info: {}, + virtual_address: None, + }, + Section { + id: ".rdata$r-8", + name: ".rdata$r", + address: 0, + size: 0, + kind: Data, + data: SectionData( + 0, + ), + flags: FlagSet(Hidden), + relocations: [], + line_info: {}, + virtual_address: None, + }, + Section { + id: ".rdata$r-9", + name: ".rdata$r", + address: 0, + size: 0, + kind: Data, + data: SectionData( + 0, + ), + flags: FlagSet(Hidden), + relocations: [], + line_info: {}, + virtual_address: None, + }, + Section { + id: ".rdata$r-10", + name: ".rdata$r", + address: 0, + size: 0, + kind: Data, + data: SectionData( + 0, + ), + flags: FlagSet(Hidden), + relocations: [], + line_info: {}, + virtual_address: None, + }, + Section { + id: ".text$mn-1", + name: ".text$mn", + address: 0, + size: 0, + kind: Code, + data: SectionData( + 0, + ), + flags: FlagSet(Hidden), + relocations: [], + line_info: {}, + virtual_address: None, + }, + Section { + id: ".rdata$r-11", + name: ".rdata$r", + address: 0, + size: 0, + kind: Data, + data: SectionData( + 0, + ), + flags: FlagSet(Hidden), + relocations: [], + line_info: {}, + virtual_address: None, + }, + Section { + id: ".text$mn-2", + name: ".text$mn", + address: 0, + size: 0, + kind: Code, + data: SectionData( + 0, + ), + flags: FlagSet(Hidden), + relocations: [], + line_info: {}, + virtual_address: None, + }, + Section { + id: ".text$mn-3", + name: ".text$mn", + address: 0, + size: 0, + kind: Code, + data: SectionData( + 0, + ), + flags: FlagSet(Hidden), + relocations: [], + line_info: {}, + virtual_address: None, + }, + Section { + id: ".text$mn-4", + name: ".text$mn", + address: 0, + size: 0, + kind: Code, + data: SectionData( + 0, + ), + flags: FlagSet(Hidden), + relocations: [], + line_info: {}, + virtual_address: None, + }, + Section { + id: ".text$mn-5", + name: ".text$mn", + address: 0, + size: 0, + kind: Code, + data: SectionData( + 0, + ), + flags: FlagSet(Hidden), + relocations: [], + line_info: {}, + virtual_address: None, + }, + Section { + id: ".text$mn-6", + name: ".text$mn", + address: 0, + size: 0, + kind: Code, + data: SectionData( + 0, + ), + flags: FlagSet(Hidden), + relocations: [], + line_info: {}, + virtual_address: None, + }, + Section { + id: ".text-combined", + name: ".text", + address: 0, + size: 268, + kind: Code, + data: SectionData( + 268, + ), + flags: FlagSet(Combined), + relocations: [ + Relocation { + flags: Coff( + 6, + ), + address: 4, + target_symbol: 62, + addend: 0, + }, + Relocation { + flags: Coff( + 20, + ), + address: 9, + target_symbol: 53, + addend: 0, + }, + Relocation { + flags: Coff( + 20, + ), + address: 29, + target_symbol: 60, + addend: 0, + }, + Relocation { + flags: Coff( + 20, + ), + address: 48, + target_symbol: 52, + addend: 0, + }, + Relocation { + flags: Coff( + 20, + ), + address: 68, + target_symbol: 11, + addend: 0, + }, + Relocation { + flags: Coff( + 6, + ), + address: 84, + target_symbol: 64, + addend: 0, + }, + Relocation { + flags: Coff( + 6, + ), + address: 104, + target_symbol: 66, + addend: 0, + }, + Relocation { + flags: Coff( + 6, + ), + address: 124, + target_symbol: 6, + addend: 0, + }, + Relocation { + flags: Coff( + 6, + ), + address: 134, + target_symbol: 8, + addend: 0, + }, + Relocation { + flags: Coff( + 20, + ), + address: 145, + target_symbol: 57, + addend: 0, + }, + Relocation { + flags: Coff( + 20, + ), + address: 153, + target_symbol: 54, + addend: 0, + }, + Relocation { + flags: Coff( + 20, + ), + address: 172, + target_symbol: 54, + addend: 0, + }, + Relocation { + flags: Coff( + 20, + ), + address: 191, + target_symbol: 52, + addend: 0, + }, + Relocation { + flags: Coff( + 20, + ), + address: 218, + target_symbol: 57, + addend: 0, + }, + Relocation { + flags: Coff( + 20, + ), + address: 237, + target_symbol: 52, + addend: 0, + }, + Relocation { + flags: Coff( + 6, + ), + address: 257, + target_symbol: 68, + addend: 0, + }, + Relocation { + flags: Coff( + 20, + ), + address: 262, + target_symbol: 60, + addend: 0, + }, + ], + line_info: {}, + virtual_address: None, + }, + Section { + id: ".text$yd-0", + name: ".text$yd", + address: 0, + size: 0, + kind: Code, + data: SectionData( + 0, + ), + flags: FlagSet(Hidden), + relocations: [], + line_info: {}, + virtual_address: None, + }, + Section { + id: ".rdata-2", + name: ".rdata", + address: 0, + size: 0, + kind: Data, + data: SectionData( + 0, + ), + flags: FlagSet(Hidden), + relocations: [], + line_info: {}, + virtual_address: None, + }, + Section { + id: ".rdata-3", + name: ".rdata", + address: 0, + size: 0, + kind: Data, + data: SectionData( + 0, + ), + flags: FlagSet(Hidden), + relocations: [], + line_info: {}, + virtual_address: None, + }, + Section { + id: ".data-0", + name: ".data", + address: 0, + size: 0, + kind: Data, + data: SectionData( + 0, + ), + flags: FlagSet(Hidden), + relocations: [], + line_info: {}, + virtual_address: None, + }, + Section { + id: ".rdata$r-12", + name: ".rdata$r", + address: 0, + size: 0, + kind: Data, + data: SectionData( + 0, + ), + flags: FlagSet(Hidden), + relocations: [], + line_info: {}, + virtual_address: None, + }, + Section { + id: ".rdata$r-13", + name: ".rdata$r", + address: 0, + size: 0, + kind: Data, + data: SectionData( + 0, + ), + flags: FlagSet(Hidden), + relocations: [], + line_info: {}, + virtual_address: None, + }, + Section { + id: ".CRT$XCU-0", + name: ".CRT$XCU", + address: 0, + size: 4, + kind: Data, + data: SectionData( + 4, + ), + flags: FlagSet(), + relocations: [ + Relocation { + flags: Coff( + 6, + ), + address: 0, + target_symbol: 61, + addend: 0, + }, + ], + line_info: {}, + virtual_address: None, + }, + Section { + id: ".chks64-0", + name: ".chks64", + address: 0, + size: 280, + kind: Unknown, + data: SectionData( + 0, + ), + flags: FlagSet(), + relocations: [], + line_info: {}, + virtual_address: None, + }, +] diff --git a/objdiff-gui/src/views/function_diff.rs b/objdiff-gui/src/views/function_diff.rs index 2080209..808b5af 100644 --- a/objdiff-gui/src/views/function_diff.rs +++ b/objdiff-gui/src/views/function_diff.rs @@ -4,14 +4,14 @@ use egui::{text::LayoutJob, Label, Response, Sense, Widget}; use egui_extras::TableRow; use objdiff_core::{ diff::{ - display::{display_row, DiffText, HighlightKind}, - DiffObjConfig, InstructionArgDiffIndex, InstructionDiffKind, InstructionDiffRow, - ObjectDiff, + display::{display_row, DiffText, DiffTextColor, DiffTextSegment, HighlightKind}, + DiffObjConfig, InstructionDiffKind, InstructionDiffRow, ObjectDiff, }, obj::{ InstructionArg, InstructionArgValue, InstructionRef, Object, ParsedInstruction, ResolvedRelocation, Section, Symbol, }, + util::ReallySigned, }; use crate::views::{appearance::Appearance, symbol_diff::DiffViewAction}; @@ -87,7 +87,7 @@ fn resolve_instruction_ref( let section = &obj.sections[section_idx]; let offset = ins_ref.address.checked_sub(section.address)?; let data = section.data.get(offset as usize..offset as usize + ins_ref.size as usize)?; - let relocation = section.relocation_at(ins_ref.address, obj); + let relocation = section.relocation_at(ins_ref, obj); Some(ResolvedInstructionRef { symbol, section, section_idx, data, relocation }) } @@ -301,91 +301,65 @@ fn ins_context_menu( #[must_use] fn diff_text_ui( ui: &mut egui::Ui, - text: DiffText<'_>, - diff: InstructionArgDiffIndex, - ins_diff: &InstructionDiffRow, + segment: DiffTextSegment, appearance: &Appearance, ins_view_state: &FunctionViewState, column: usize, space_width: f32, response_cb: impl Fn(Response) -> Response, ) -> Option { - let mut ret = None; - let label_text; - let mut base_color = match ins_diff.kind { - InstructionDiffKind::None - | InstructionDiffKind::OpMismatch - | InstructionDiffKind::ArgMismatch => appearance.text_color, - InstructionDiffKind::Replace => appearance.replace_color, - InstructionDiffKind::Delete => appearance.delete_color, - InstructionDiffKind::Insert => appearance.insert_color, - }; - let mut pad_to = 0; - match text { - DiffText::Basic(text) => { - label_text = text.to_string(); - } - DiffText::Line(num) => { - label_text = num.to_string(); - base_color = appearance.deemphasized_text_color; - pad_to = 5; - } - DiffText::Address(addr) => { - label_text = format!("{:x}:", addr); - pad_to = 5; - } - DiffText::Opcode(mnemonic, _op) => { - label_text = mnemonic.to_string(); - if ins_diff.kind == InstructionDiffKind::OpMismatch { - base_color = appearance.replace_color; - } - pad_to = 8; - } - DiffText::Argument(arg) => { - label_text = arg.to_string(); - } - DiffText::BranchDest(addr) => { - label_text = format!("{addr:x}"); - } - DiffText::Symbol(sym) => { - let name = sym.demangled_name.as_ref().unwrap_or(&sym.name); - label_text = name.clone(); - base_color = appearance.emphasized_text_color; - } - DiffText::Addend(addend) => { - label_text = match addend.cmp(&0i64) { - Ordering::Greater => format!("+{:#x}", addend), - Ordering::Less => format!("-{:#x}", -addend), - _ => "".to_string(), - }; - base_color = appearance.emphasized_text_color; - } + let highlight_kind = HighlightKind::from(&segment.text); + let label_text = match segment.text { + DiffText::Basic(text) => text.to_string(), + DiffText::Line(num) => format!("{num} "), + DiffText::Address(addr) => format!("{:x}:", addr), + DiffText::Opcode(mnemonic, _op) => format!("{mnemonic} "), + DiffText::Argument(arg) => match arg { + InstructionArgValue::Signed(v) => format!("{:#x}", ReallySigned(v)), + InstructionArgValue::Unsigned(v) => format!("{:#x}", v), + InstructionArgValue::Opaque(v) => v.into_owned(), + }, + DiffText::BranchDest(addr) => format!("{addr:x}"), + DiffText::Symbol(sym) => sym.demangled_name.as_ref().unwrap_or(&sym.name).clone(), + DiffText::Addend(addend) => match addend.cmp(&0i64) { + Ordering::Greater => format!("+{:#x}", addend), + Ordering::Less => format!("-{:#x}", -addend), + _ => String::new(), + }, DiffText::Spacing(n) => { ui.add_space(n as f32 * space_width); - return ret; + return None; } - DiffText::Eol => { - label_text = "\n".to_string(); - } - } - if let Some(diff_idx) = diff.get() { - base_color = appearance.diff_colors[diff_idx as usize % appearance.diff_colors.len()]; - } + DiffText::Eol => "\n".to_string(), + }; let len = label_text.len(); - let highlight = *ins_view_state.highlight(column) == text; + let highlight = highlight_kind != HighlightKind::None + && *ins_view_state.highlight(column) == highlight_kind; + let color = match segment.color { + DiffTextColor::Normal => appearance.text_color, + DiffTextColor::Dim => appearance.deemphasized_text_color, + DiffTextColor::Bright => appearance.emphasized_text_color, + DiffTextColor::Replace => appearance.replace_color, + DiffTextColor::Delete => appearance.delete_color, + DiffTextColor::Insert => appearance.insert_color, + DiffTextColor::Rotating(i) => { + appearance.diff_colors[i as usize % appearance.diff_colors.len()] + } + }; let mut response = Label::new(LayoutJob::single_section( label_text, - appearance.code_text_format(base_color, highlight), + appearance.code_text_format(color, highlight), )) .sense(Sense::click()) .ui(ui); response = response_cb(response); + let mut ret = None; if response.clicked() { - ret = Some(DiffViewAction::SetDiffHighlight(column, text.into())); + ret = Some(DiffViewAction::SetDiffHighlight(column, highlight_kind)); } - if len < pad_to { - ui.add_space((pad_to - len) as f32 * space_width); + if len < segment.pad_to as usize { + ui.add_space((segment.pad_to as usize - len) as f32 * space_width); } ret } @@ -409,18 +383,10 @@ 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_row(obj, symbol_idx, ins_diff, diff_config, |text, diff| { - if let Some(action) = diff_text_ui( - ui, - text, - diff, - ins_diff, - appearance, - ins_view_state, - column, - space_width, - &response_cb, - ) { + display_row(obj, symbol_idx, ins_diff, diff_config, |segment| { + if let Some(action) = + diff_text_ui(ui, segment, appearance, ins_view_state, column, space_width, &response_cb) + { ret = Some(action); } Ok(()) diff --git a/objdiff-wasm/package-lock.json b/objdiff-wasm/package-lock.json index b9a7741..0e94672 100644 --- a/objdiff-wasm/package-lock.json +++ b/objdiff-wasm/package-lock.json @@ -10,8 +10,7 @@ "license": "MIT OR Apache-2.0", "devDependencies": { "@biomejs/biome": "^1.9.3", - "@bytecodealliance/jco": "^1.10.1", - "@bytecodealliance/preview2-shim": "^0.17.1", + "@bytecodealliance/jco": "^1.10.2", "@rslib/core": "^0.4.1", "typescript": "^5.7.2" } @@ -196,9 +195,9 @@ } }, "node_modules/@bytecodealliance/jco": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/@bytecodealliance/jco/-/jco-1.10.1.tgz", - "integrity": "sha512-1HfO7HT+Rrvviv/l0CBm5O9/qtx3463W7ulZ+P7AV6icpHM97VeszQ1qbiegUxflT2GRXp4lVSaOaNGxoATQoQ==", + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/@bytecodealliance/jco/-/jco-1.10.2.tgz", + "integrity": "sha512-ShBb9Jul4CYo4asmfDdjmYE2+4TgJLSHhsRMyVDvROA5j6s+cAKMmlsUjOrS6QM5TL8GJb48G9jsxif6na6qYg==", "dev": true, "license": "(Apache-2.0 WITH LLVM-exception)", "workspaces": [ @@ -206,7 +205,7 @@ ], "dependencies": { "@bytecodealliance/componentize-js": "^0.17.0", - "@bytecodealliance/preview2-shim": "file:packages/preview2-shim", + "@bytecodealliance/preview2-shim": "^0.17.2", "binaryen": "^122.0.0", "chalk-template": "^1", "commander": "^12", @@ -218,17 +217,10 @@ "jco": "src/jco.js" } }, - "node_modules/@bytecodealliance/jco/node_modules/@bytecodealliance/preview2-shim": { - "resolved": "node_modules/@bytecodealliance/jco/packages/preview2-shim", - "link": true - }, - "node_modules/@bytecodealliance/jco/packages/preview2-shim": { - "dev": true - }, "node_modules/@bytecodealliance/preview2-shim": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/@bytecodealliance/preview2-shim/-/preview2-shim-0.17.1.tgz", - "integrity": "sha512-h1qLL0TN5KXk/zagY2BtbZuDX6xYjz4Br9RZXEa0ID4UpiPc0agUMhTdz9r89G4vX5SU/tqBg1A6UNv2+DJ5pg==", + "version": "0.17.2", + "resolved": "https://registry.npmjs.org/@bytecodealliance/preview2-shim/-/preview2-shim-0.17.2.tgz", + "integrity": "sha512-mNm/lblgES8UkVle8rGImXOz4TtL3eU3inHay/7TVchkKrb/lgcVvTK0+VAw8p5zQ0rgQsXm1j5dOlAAd+MeoA==", "dev": true, "license": "(Apache-2.0 WITH LLVM-exception)" }, diff --git a/objdiff-wasm/package.json b/objdiff-wasm/package.json index a50ac35..5493126 100644 --- a/objdiff-wasm/package.json +++ b/objdiff-wasm/package.json @@ -23,8 +23,7 @@ }, "devDependencies": { "@biomejs/biome": "^1.9.3", - "@bytecodealliance/jco": "^1.10.1", - "@bytecodealliance/preview2-shim": "^0.17.1", + "@bytecodealliance/jco": "^1.10.2", "@rslib/core": "^0.4.1", "typescript": "^5.7.2" } diff --git a/objdiff-wasm/src/api.rs b/objdiff-wasm/src/api.rs index aa05a7f..591891a 100644 --- a/objdiff-wasm/src/api.rs +++ b/objdiff-wasm/src/api.rs @@ -20,15 +20,15 @@ wit_bindgen::generate!({ }); use exports::objdiff::core::{ - diff::Guest as GuestDiff, - diff_types::{ - DiffConfigBorrow, DiffResult, Guest as GuestDiffTypes, GuestDiffConfig, GuestObject, + diff::{ + DiffConfigBorrow, DiffResult, Guest as GuestDiff, GuestDiffConfig, GuestObject, GuestObjectDiff, Object, ObjectBorrow, ObjectDiff, ObjectDiffBorrow, }, - display_types::{ - ContextMenuItem, DiffText, DiffTextOpcode, DiffTextSegment, DiffTextSymbol, DisplayConfig, - HoverItem, InstructionDiffKind, InstructionDiffRow, SectionDisplay, SectionDisplaySymbol, - SymbolDisplay, SymbolFilter, SymbolFlags, SymbolKind, SymbolRef, + display::{ + ContextMenuItem, DiffText, DiffTextColor, DiffTextOpcode, DiffTextSegment, DiffTextSymbol, + DisplayConfig, Guest as GuestDisplay, HoverItem, InstructionDiffKind, InstructionDiffRow, + SectionDisplay, SectionDisplaySymbol, SymbolDisplay, SymbolFilter, SymbolFlags, SymbolKind, + SymbolRef, }, }; @@ -48,13 +48,11 @@ struct ResourceObjectDiff(Rc, diff::ObjectDiff); #[repr(transparent)] struct ResourceDiffConfig(RefCell); -impl GuestDiffTypes for Component { +impl GuestDiff for Component { type DiffConfig = ResourceDiffConfig; type Object = ResourceObject; type ObjectDiff = ResourceObjectDiff; -} -impl GuestDiff for Component { fn run_diff( left: Option, right: Option, @@ -85,7 +83,9 @@ impl GuestDiff for Component { }), }) } +} +impl GuestDisplay for Component { fn symbol_context(_obj: ObjectBorrow, _symbol: SymbolRef) -> Vec { todo!() } fn symbol_hover(_obj: ObjectBorrow, _symbol: SymbolRef) -> Vec { todo!() } @@ -195,8 +195,8 @@ impl GuestDiff for Component { }; let row = &symbol_diff.instruction_rows[row_index as usize]; let diff_config = diff_config.get::().0.borrow(); - diff::display::display_row(obj, symbol_idx, row, &diff_config, |text, idx| { - segments.push(DiffTextSegment { text: DiffText::from(text), diff_index: idx.get() }); + diff::display::display_row(obj, symbol_idx, row, &diff_config, |segment| { + segments.push(DiffTextSegment::from(segment)); Ok(()) }) .unwrap(); @@ -243,9 +243,9 @@ impl From> for DiffText { DiffText::Opcode(DiffTextOpcode { mnemonic: n.to_string(), opcode: op }) } diff::display::DiffText::Argument(s) => match s { - obj::InstructionArgValue::Signed(v) => DiffText::Signed(*v), - obj::InstructionArgValue::Unsigned(v) => DiffText::Unsigned(*v), - obj::InstructionArgValue::Opaque(v) => DiffText::Opaque(v.to_string()), + obj::InstructionArgValue::Signed(v) => DiffText::Signed(v), + obj::InstructionArgValue::Unsigned(v) => DiffText::Unsigned(v), + obj::InstructionArgValue::Opaque(v) => DiffText::Opaque(v.into_owned()), }, diff::display::DiffText::BranchDest(v) => DiffText::BranchDest(v), diff::display::DiffText::Symbol(s) => DiffText::Symbol(DiffTextSymbol { @@ -253,12 +253,36 @@ impl From> for DiffText { demangled_name: s.demangled_name.clone(), }), diff::display::DiffText::Addend(v) => DiffText::Addend(v), - diff::display::DiffText::Spacing(v) => DiffText::Spacing(v as u32), + diff::display::DiffText::Spacing(v) => DiffText::Spacing(v), diff::display::DiffText::Eol => DiffText::Eol, } } } +impl From for DiffTextColor { + fn from(value: diff::display::DiffTextColor) -> Self { + match value { + diff::display::DiffTextColor::Normal => DiffTextColor::Normal, + diff::display::DiffTextColor::Dim => DiffTextColor::Dim, + diff::display::DiffTextColor::Bright => DiffTextColor::Bright, + diff::display::DiffTextColor::Replace => DiffTextColor::Replace, + diff::display::DiffTextColor::Delete => DiffTextColor::Delete, + diff::display::DiffTextColor::Insert => DiffTextColor::Insert, + diff::display::DiffTextColor::Rotating(v) => DiffTextColor::Rotating(v), + } + } +} + +impl From> for DiffTextSegment { + fn from(segment: diff::display::DiffTextSegment) -> Self { + DiffTextSegment { + text: DiffText::from(segment.text), + color: DiffTextColor::from(segment.color), + pad_to: segment.pad_to, + } + } +} + impl From for InstructionDiffKind { fn from(kind: diff::InstructionDiffKind) -> Self { match kind { diff --git a/objdiff-wasm/wit/objdiff.wit b/objdiff-wasm/wit/objdiff.wit index 38f9adc..5386f3b 100644 --- a/objdiff-wasm/wit/objdiff.wit +++ b/objdiff-wasm/wit/objdiff.wit @@ -2,7 +2,7 @@ package objdiff:core; use wasi:logging/logging@0.1.0-draft; -interface diff-types { +interface diff { resource diff-config { constructor(); set-property: func(id: string, value: string) -> result<_, string>; @@ -33,9 +33,21 @@ interface diff-types { left: option, right: option, } + + run-diff: func( + left: option>, + right: option>, + config: borrow, + ) -> result; } -interface display-types { +interface display { + use diff.{ + object, + object-diff, + diff-config + }; + type symbol-ref = u32; record display-config { @@ -153,16 +165,28 @@ interface display-types { // Relocation addend addend(s64), // Number of spaces - spacing(u32), + spacing(u8), // End of line eol, } + variant diff-text-color { + normal, + dim, + bright, + replace, + delete, + insert, + rotating(u8), + } + record diff-text-segment { // Text to display text: diff-text, - // Index for colorization - diff-index: option, + // Text color + color: diff-text-color, + // Number of spaces to pad to + pad-to: u8, } record instruction-diff-row { @@ -180,32 +204,6 @@ interface display-types { insert, delete, } -} - -interface diff { - use diff-types.{ - object, - object-diff, - diff-config, - diff-result - }; - use display-types.{ - section-display-symbol, - section-display, - symbol-ref, - symbol-filter, - symbol-display, - context-menu-item, - hover-item, - display-config, - instruction-diff-row - }; - - run-diff: func( - left: option>, - right: option>, - config: borrow, - ) -> result; display-sections: func( diff: borrow, @@ -241,8 +239,7 @@ world api { use logging.{level}; export diff; - export diff-types; - export display-types; + export display; export init: func(level: level); export version: func() -> string;