diff --git a/Cargo.lock b/Cargo.lock index 831e970..cfbca1a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3437,7 +3437,7 @@ dependencies = [ [[package]] name = "objdiff-cli" -version = "3.0.0-beta.12" +version = "3.0.0-beta.13" dependencies = [ "anyhow", "argp", @@ -3460,7 +3460,7 @@ dependencies = [ [[package]] name = "objdiff-core" -version = "3.0.0-beta.12" +version = "3.0.0-beta.13" dependencies = [ "anyhow", "arm-attr", @@ -3514,7 +3514,7 @@ dependencies = [ [[package]] name = "objdiff-gui" -version = "3.0.0-beta.12" +version = "3.0.0-beta.13" dependencies = [ "anyhow", "cfg-if", @@ -3550,7 +3550,7 @@ dependencies = [ [[package]] name = "objdiff-wasm" -version = "3.0.0-beta.12" +version = "3.0.0-beta.13" dependencies = [ "log", "objdiff-core", diff --git a/Cargo.toml b/Cargo.toml index ab547e6..7e2ad37 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ strip = "debuginfo" codegen-units = 1 [workspace.package] -version = "3.0.0-beta.12" +version = "3.0.0-beta.13" authors = ["Luke Street "] edition = "2024" license = "MIT OR Apache-2.0" diff --git a/objdiff-core/src/arch/mips.rs b/objdiff-core/src/arch/mips.rs index 5d41d01..0260989 100644 --- a/objdiff-core/src/arch/mips.rs +++ b/objdiff-core/src/arch/mips.rs @@ -15,7 +15,7 @@ use crate::{ diff::{DiffObjConfig, MipsAbi, MipsInstrCategory, display::InstructionPart}, obj::{ InstructionArg, InstructionArgValue, InstructionRef, Relocation, RelocationFlags, - ResolvedInstructionRef, ResolvedRelocation, SymbolFlag, SymbolFlagSet, + ResolvedInstructionRef, ResolvedRelocation, Section, Symbol, SymbolFlag, SymbolFlagSet, }, }; @@ -140,6 +140,14 @@ impl ArchMips { }) } + fn default_instruction_flags(&self) -> rabbitizer::InstructionFlags { + match self.isa_extension { + Some(extension) => rabbitizer::InstructionFlags::new_extension(extension), + None => rabbitizer::InstructionFlags::new(IsaVersion::MIPS_III), + } + .with_abi(self.abi) + } + fn instruction_flags(&self, diff_config: &DiffObjConfig) -> rabbitizer::InstructionFlags { let isa_extension = match diff_config.mips_instr_category { MipsInstrCategory::Auto => self.isa_extension, @@ -151,7 +159,7 @@ impl ArchMips { }; match isa_extension { Some(extension) => rabbitizer::InstructionFlags::new_extension(extension), - None => rabbitizer::InstructionFlags::new_isa(IsaVersion::MIPS_III, None), + None => rabbitizer::InstructionFlags::new(IsaVersion::MIPS_III), } .with_abi(match diff_config.mips_abi { MipsAbi::Auto => self.abi, @@ -331,6 +339,36 @@ impl Arch for ArchMips { } flags } + + fn infer_function_size( + &self, + symbol: &Symbol, + section: &Section, + next_address: u64, + ) -> Result { + // Trim any trailing 4-byte zeroes from the end (nops) + let mut new_address = next_address; + while new_address >= symbol.address + 4 + && let Some(data) = section.data_range(new_address - 4, 4) + && data == [0u8; 4] + { + new_address -= 4; + } + // Check if the last instruction has a delay slot, if so, include the delay slot nop + if new_address + 4 <= next_address + && new_address >= symbol.address + 4 + && let Some(data) = section.data_range(new_address - 4, 4) + && let instruction = rabbitizer::Instruction::new( + self.endianness.read_u32_bytes(data.try_into().unwrap()), + Vram::new((new_address - 4) as u32), + self.default_instruction_flags(), + ) + && instruction.opcode().has_delay_slot() + { + new_address += 4; + } + Ok(new_address.saturating_sub(symbol.address)) + } } fn push_args( diff --git a/objdiff-core/src/arch/mod.rs b/objdiff-core/src/arch/mod.rs index 64a633b..0b882ba 100644 --- a/objdiff-core/src/arch/mod.rs +++ b/objdiff-core/src/arch/mod.rs @@ -406,6 +406,15 @@ pub trait Arch: Send + Sync + Debug { ) -> Vec { Vec::new() } + + fn infer_function_size( + &self, + symbol: &Symbol, + _section: &Section, + next_address: u64, + ) -> Result { + Ok(next_address.saturating_sub(symbol.address)) + } } pub fn new_arch(object: &object::File) -> Result> { diff --git a/objdiff-core/src/arch/ppc/mod.rs b/objdiff-core/src/arch/ppc/mod.rs index b7cc034..ab6ce73 100644 --- a/objdiff-core/src/arch/ppc/mod.rs +++ b/objdiff-core/src/arch/ppc/mod.rs @@ -20,7 +20,7 @@ use crate::{ }, obj::{ FlowAnalysisResult, InstructionRef, Object, Relocation, RelocationFlags, - ResolvedInstructionRef, ResolvedRelocation, Symbol, SymbolFlag, SymbolFlagSet, + ResolvedInstructionRef, ResolvedRelocation, Section, Symbol, SymbolFlag, SymbolFlagSet, }, }; @@ -455,6 +455,22 @@ impl Arch for ArchPpc { } out } + + fn infer_function_size( + &self, + symbol: &Symbol, + section: &Section, + mut next_address: u64, + ) -> Result { + // Trim any trailing 4-byte zeroes from the end (padding) + while next_address >= symbol.address + 4 + && let Some(data) = section.data_range(next_address - 4, 4) + && data == [0u8; 4] + { + next_address -= 4; + } + Ok(next_address.saturating_sub(symbol.address)) + } } impl ArchPpc { diff --git a/objdiff-core/src/arch/x86.rs b/objdiff-core/src/arch/x86.rs index 4188719..c748112 100644 --- a/objdiff-core/src/arch/x86.rs +++ b/objdiff-core/src/arch/x86.rs @@ -11,7 +11,7 @@ use object::{Endian as _, Object as _, ObjectSection as _, elf, pe}; use crate::{ arch::{Arch, RelocationOverride, RelocationOverrideTarget}, diff::{DiffObjConfig, X86Formatter, display::InstructionPart}, - obj::{InstructionRef, Relocation, RelocationFlags, ResolvedInstructionRef}, + obj::{InstructionRef, Relocation, RelocationFlags, ResolvedInstructionRef, Section, Symbol}, }; #[derive(Debug)] @@ -303,6 +303,52 @@ impl Arch for ArchX86 { fn data_reloc_size(&self, flags: RelocationFlags) -> usize { self.reloc_size(flags).unwrap_or(1) } + + fn infer_function_size( + &self, + symbol: &Symbol, + section: &Section, + next_address: u64, + ) -> Result { + let Ok(size) = (next_address - symbol.address).try_into() else { + return Ok(next_address.saturating_sub(symbol.address)); + }; + let Some(code) = section.data_range(symbol.address, size) else { + return Ok(0); + }; + // Decode instructions to find the last non-NOP instruction + let mut decoder = self.decoder(code, symbol.address); + let mut instruction = Instruction::default(); + let mut new_address = 0; + let mut reloc_iter = section.relocations.iter().peekable(); + 'outer: while decoder.can_decode() { + let address = decoder.ip(); + while let Some(reloc) = reloc_iter.peek() { + match reloc.address.cmp(&address) { + Ordering::Less => { + reloc_iter.next(); + } + Ordering::Equal => { + // If the instruction starts at a relocation, it's inline data + let reloc_size = self.reloc_size(reloc.flags).with_context(|| { + format!("Unsupported inline x86 relocation {:?}", reloc.flags) + })?; + if decoder.set_position(decoder.position() + reloc_size).is_ok() { + new_address = address + reloc_size as u64; + decoder.set_ip(new_address); + continue 'outer; + } + } + Ordering::Greater => break, + } + } + decoder.decode_out(&mut instruction); + if instruction.mnemonic() != iced_x86::Mnemonic::Nop { + new_address = instruction.next_ip(); + } + } + Ok(new_address.saturating_sub(symbol.address)) + } } struct InstructionFormatterOutput<'a> { diff --git a/objdiff-core/src/obj/read.rs b/objdiff-core/src/obj/read.rs index 30798b3..f86c0de 100644 --- a/objdiff-core/src/obj/read.rs +++ b/objdiff-core/src/obj/read.rs @@ -122,7 +122,7 @@ fn map_symbols( } // Infer symbol sizes for 0-size symbols - infer_symbol_sizes(&mut symbols, sections); + infer_symbol_sizes(arch, &mut symbols, sections)?; Ok((symbols, symbol_indices)) } @@ -136,7 +136,7 @@ fn is_local_label(symbol: &Symbol) -> bool { && LABEL_PREFIXES.iter().any(|p| symbol.name.starts_with(p)) } -fn infer_symbol_sizes(symbols: &mut [Symbol], sections: &[Section]) { +fn infer_symbol_sizes(arch: &dyn Arch, symbols: &mut [Symbol], sections: &[Section]) -> Result<()> { // 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() { @@ -206,18 +206,13 @@ fn infer_symbol_sizes(symbols: &mut [Symbol], sections: &[Section]) { iter_idx += 1; }; let section = §ions[section_idx]; - let mut next_address = + let next_address = next_symbol.map(|s| s.address).unwrap_or_else(|| section.address + section.size); - if section.kind == SectionKind::Code { - // For functions, trim any trailing 4-byte zeroes from the end (padding, nops) - while next_address > symbol.address + 4 - && let Some(data) = section.data_range(next_address - 4, 4) - && data == [0u8; 4] - { - next_address -= 4; - } - } - let new_size = next_address.saturating_sub(symbol.address); + let new_size = if section.kind == SectionKind::Code { + arch.infer_function_size(symbol, section, next_address)? + } else { + next_address.saturating_sub(symbol.address) + }; if new_size > 0 { let symbol = &mut symbols[symbol_idx]; symbol.size = new_size; @@ -234,6 +229,7 @@ fn infer_symbol_sizes(symbols: &mut [Symbol], sections: &[Section]) { } } } + Ok(()) } fn map_sections( diff --git a/objdiff-core/tests/snapshots/arch_x86__read_x86_jumptable-2.snap b/objdiff-core/tests/snapshots/arch_x86__read_x86_jumptable-2.snap index bf713fb..281b5bc 100644 --- a/objdiff-core/tests/snapshots/arch_x86__read_x86_jumptable-2.snap +++ b/objdiff-core/tests/snapshots/arch_x86__read_x86_jumptable-2.snap @@ -507,116 +507,4 @@ expression: diff.instruction_rows ), arg_diff: [], }, - InstructionDiffRow { - ins_ref: Some( - InstructionRef { - address: 88, - size: 1, - opcode: 465, - branch_dest: None, - }, - ), - kind: None, - branch_from: None, - branch_to: None, - arg_diff: [], - }, - InstructionDiffRow { - ins_ref: Some( - InstructionRef { - address: 89, - size: 1, - opcode: 465, - branch_dest: None, - }, - ), - kind: None, - branch_from: None, - branch_to: None, - arg_diff: [], - }, - InstructionDiffRow { - ins_ref: Some( - InstructionRef { - address: 90, - size: 1, - opcode: 465, - branch_dest: None, - }, - ), - kind: None, - branch_from: None, - branch_to: None, - arg_diff: [], - }, - InstructionDiffRow { - ins_ref: Some( - InstructionRef { - address: 91, - size: 1, - opcode: 465, - branch_dest: None, - }, - ), - kind: None, - branch_from: None, - branch_to: None, - arg_diff: [], - }, - InstructionDiffRow { - ins_ref: Some( - InstructionRef { - address: 92, - size: 1, - opcode: 465, - branch_dest: None, - }, - ), - kind: None, - branch_from: None, - branch_to: None, - arg_diff: [], - }, - InstructionDiffRow { - ins_ref: Some( - InstructionRef { - address: 93, - size: 1, - opcode: 465, - branch_dest: None, - }, - ), - kind: None, - branch_from: None, - branch_to: None, - arg_diff: [], - }, - InstructionDiffRow { - ins_ref: Some( - InstructionRef { - address: 94, - size: 1, - opcode: 465, - branch_dest: None, - }, - ), - kind: None, - branch_from: None, - branch_to: None, - arg_diff: [], - }, - InstructionDiffRow { - ins_ref: Some( - InstructionRef { - address: 95, - size: 1, - opcode: 465, - branch_dest: None, - }, - ), - kind: None, - branch_from: None, - branch_to: None, - arg_diff: [], - }, ] diff --git a/objdiff-core/tests/snapshots/arch_x86__read_x86_jumptable-3.snap b/objdiff-core/tests/snapshots/arch_x86__read_x86_jumptable-3.snap index afdb512..a761404 100644 --- a/objdiff-core/tests/snapshots/arch_x86__read_x86_jumptable-3.snap +++ b/objdiff-core/tests/snapshots/arch_x86__read_x86_jumptable-3.snap @@ -29,11 +29,3 @@ expression: output [(Address(76), Normal, 5), (Spacing(4), Normal, 0), (Opcode(".dword", 65534), Normal, 10), (BranchDest(41), Normal, 0), (Basic(" ~>"), Rotating(6), 0), (Eol, Normal, 0)] [(Address(80), Normal, 5), (Spacing(4), Normal, 0), (Opcode(".dword", 65534), Normal, 10), (BranchDest(47), Normal, 0), (Basic(" ~>"), Rotating(7), 0), (Eol, Normal, 0)] [(Address(84), Normal, 5), (Spacing(4), Normal, 0), (Opcode(".dword", 65534), Normal, 10), (BranchDest(53), Normal, 0), (Basic(" ~>"), Rotating(8), 0), (Eol, Normal, 0)] -[(Address(88), Normal, 5), (Spacing(4), Normal, 0), (Opcode("nop", 465), Normal, 10), (Eol, Normal, 0)] -[(Address(89), Normal, 5), (Spacing(4), Normal, 0), (Opcode("nop", 465), Normal, 10), (Eol, Normal, 0)] -[(Address(90), Normal, 5), (Spacing(4), Normal, 0), (Opcode("nop", 465), Normal, 10), (Eol, Normal, 0)] -[(Address(91), Normal, 5), (Spacing(4), Normal, 0), (Opcode("nop", 465), Normal, 10), (Eol, Normal, 0)] -[(Address(92), Normal, 5), (Spacing(4), Normal, 0), (Opcode("nop", 465), Normal, 10), (Eol, Normal, 0)] -[(Address(93), Normal, 5), (Spacing(4), Normal, 0), (Opcode("nop", 465), Normal, 10), (Eol, Normal, 0)] -[(Address(94), Normal, 5), (Spacing(4), Normal, 0), (Opcode("nop", 465), Normal, 10), (Eol, Normal, 0)] -[(Address(95), Normal, 5), (Spacing(4), Normal, 0), (Opcode("nop", 465), Normal, 10), (Eol, Normal, 0)] diff --git a/objdiff-core/tests/snapshots/arch_x86__read_x86_jumptable.snap b/objdiff-core/tests/snapshots/arch_x86__read_x86_jumptable.snap index 6ef775b..ab3f2f1 100644 --- a/objdiff-core/tests/snapshots/arch_x86__read_x86_jumptable.snap +++ b/objdiff-core/tests/snapshots/arch_x86__read_x86_jumptable.snap @@ -63,7 +63,7 @@ Object { "int __cdecl test(int)", ), address: 0, - size: 96, + size: 88, kind: Function, section: Some( 1, diff --git a/objdiff-wasm/package-lock.json b/objdiff-wasm/package-lock.json index f422771..7f07530 100644 --- a/objdiff-wasm/package-lock.json +++ b/objdiff-wasm/package-lock.json @@ -1,12 +1,12 @@ { "name": "objdiff-wasm", - "version": "3.0.0-beta.12", + "version": "3.0.0-beta.13", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "objdiff-wasm", - "version": "3.0.0-beta.12", + "version": "3.0.0-beta.13", "license": "MIT OR Apache-2.0", "devDependencies": { "@biomejs/biome": "^1.9.3", diff --git a/objdiff-wasm/package.json b/objdiff-wasm/package.json index 107922b..d14cdaf 100644 --- a/objdiff-wasm/package.json +++ b/objdiff-wasm/package.json @@ -1,6 +1,6 @@ { "name": "objdiff-wasm", - "version": "3.0.0-beta.12", + "version": "3.0.0-beta.13", "description": "A local diffing tool for decompilation projects.", "author": { "name": "Luke Street",