Compare commits

...

6 Commits

Author SHA1 Message Date
809e2ffddc cargo fmt 2025-03-11 21:43:32 -06:00
87fa29e8b0 objdiff-wasm: Fix symbol filtering
regex crate needed the `unicode-case` feature
2025-03-11 21:42:14 -06:00
42d4c38079 mips: Ignore .NON_MATCHING functions from INLINE_ASM macros 2025-03-11 21:39:17 -06:00
fa26200ed7 Silence warning 2025-03-10 22:22:05 -06:00
a0e7f9bc37 Fix x86 mov relocations with uint32 2025-03-10 22:09:58 -06:00
5898d7aebf Fix section ordering with many same-named sections
Before, this was comparing, for example, `.text-2`
with `.text-10` with standard string comparison,
yielding `.text-10` before `.text-2`.

Instead, this simply uses a stable sort by name,
which preserves the relative ordering of sections.
2025-03-10 21:51:54 -06:00
23 changed files with 896 additions and 103 deletions

75
Cargo.lock generated
View File

@@ -450,6 +450,18 @@ version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
[[package]]
name = "auditable-serde"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c7bf8143dfc3c0258df908843e169b5cc5fcf76c7718bd66135ef4a9cd558c5"
dependencies = [
"semver",
"serde",
"serde_json",
"topological-sort",
]
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.4.0" version = "1.4.0"
@@ -2652,7 +2664,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"windows-targets 0.48.5", "windows-targets 0.52.6",
] ]
[[package]] [[package]]
@@ -3268,7 +3280,7 @@ dependencies = [
[[package]] [[package]]
name = "objdiff-cli" name = "objdiff-cli"
version = "3.0.0-beta.2" version = "3.0.0-beta.3"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"argp", "argp",
@@ -3291,7 +3303,7 @@ dependencies = [
[[package]] [[package]]
name = "objdiff-core" name = "objdiff-core"
version = "3.0.0-beta.2" version = "3.0.0-beta.3"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"arm-attr", "arm-attr",
@@ -3344,7 +3356,7 @@ dependencies = [
[[package]] [[package]]
name = "objdiff-gui" name = "objdiff-gui"
version = "3.0.0-beta.2" version = "3.0.0-beta.3"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"cfg-if", "cfg-if",
@@ -3380,7 +3392,7 @@ dependencies = [
[[package]] [[package]]
name = "objdiff-wasm" name = "objdiff-wasm"
version = "3.0.0-beta.2" version = "3.0.0-beta.3"
dependencies = [ dependencies = [
"log", "log",
"objdiff-core", "objdiff-core",
@@ -4474,6 +4486,9 @@ name = "semver"
version = "1.0.25" version = "1.0.25"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03" checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "serde" name = "serde"
@@ -5165,6 +5180,12 @@ dependencies = [
"winnow 0.7.3", "winnow 0.7.3",
] ]
[[package]]
name = "topological-sort"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea68304e134ecd095ac6c3574494fc62b909f416c4fca77e440530221e549d3d"
[[package]] [[package]]
name = "tower" name = "tower"
version = "0.5.2" version = "0.5.2"
@@ -5537,9 +5558,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-encoder" name = "wasm-encoder"
version = "0.225.0" version = "0.227.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f7eac0445cac73bcf09e6a97f83248d64356dccf9f2b100199769b6b42464e5" checksum = "80bb72f02e7fbf07183443b27b0f3d4144abf8c114189f2e088ed95b696a7822"
dependencies = [ dependencies = [
"leb128fmt", "leb128fmt",
"wasmparser", "wasmparser",
@@ -5547,11 +5568,13 @@ dependencies = [
[[package]] [[package]]
name = "wasm-metadata" name = "wasm-metadata"
version = "0.225.0" version = "0.227.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1d20d0bf2c73c32a5114cf35a5c10ccf9f9aa37a3a2c0114b3e11cbf6faac12" checksum = "ce1ef0faabbbba6674e97a56bee857ccddf942785a336c8b47b42373c922a91d"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"auditable-serde",
"flate2",
"indexmap", "indexmap",
"serde", "serde",
"serde_derive", "serde_derive",
@@ -5577,9 +5600,9 @@ dependencies = [
[[package]] [[package]]
name = "wasmparser" name = "wasmparser"
version = "0.225.0" version = "0.227.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36e5456165f81e64cb9908a0fe9b9d852c2c74582aa3fe2be3c2da57f937d3ae" checksum = "0f51cad774fb3c9461ab9bccc9c62dfb7388397b5deda31bf40e8108ccd678b2"
dependencies = [ dependencies = [
"bitflags 2.9.0", "bitflags 2.9.0",
"hashbrown", "hashbrown",
@@ -6300,19 +6323,19 @@ dependencies = [
[[package]] [[package]]
name = "wit-bindgen" name = "wit-bindgen"
version = "0.39.0" version = "0.40.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4dd9a372b25d6f35456b0a730d2adabeb0c4878066ba8f8089800349be6ecb5" checksum = "8e7091ed6c9abfa4e0a2ef3b39d0539da992d841fcf32c255f64fb98764ffee5"
dependencies = [ dependencies = [
"wit-bindgen-rt 0.39.0", "wit-bindgen-rt 0.40.0",
"wit-bindgen-rust-macro", "wit-bindgen-rust-macro",
] ]
[[package]] [[package]]
name = "wit-bindgen-core" name = "wit-bindgen-core"
version = "0.39.0" version = "0.40.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f108fa9b77a346372858b30c11ea903680e7e2b9d820b1a5883e9d530bf51c7e" checksum = "398c650cec1278cfb72e214ba26ef3440ab726e66401bd39c04f465ee3979e6b"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"heck", "heck",
@@ -6330,18 +6353,18 @@ dependencies = [
[[package]] [[package]]
name = "wit-bindgen-rt" name = "wit-bindgen-rt"
version = "0.39.0" version = "0.40.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" checksum = "68faed92ae696b93ea9a7b67ba6c37bf09d72c6d9a70fa824a743c3020212f11"
dependencies = [ dependencies = [
"bitflags 2.9.0", "bitflags 2.9.0",
] ]
[[package]] [[package]]
name = "wit-bindgen-rust" name = "wit-bindgen-rust"
version = "0.39.0" version = "0.40.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5ba5b852e976d35dbf6cb745746bf1bd4fc26782bab1e0c615fc71a7d8aac05" checksum = "83903c8dcd8084a8a67ae08190122cf0e25dc37bdc239070a00f47e22d3f0aae"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"heck", "heck",
@@ -6355,9 +6378,9 @@ dependencies = [
[[package]] [[package]]
name = "wit-bindgen-rust-macro" name = "wit-bindgen-rust-macro"
version = "0.39.0" version = "0.40.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "401529c9af9304a20ed99fa01799e467b7d37727126f0c9a958895471268ad7a" checksum = "a7bf7f20495bcc7dc9f24c5fbcac9e919ca5ebdb7a1b1841d74447d3c8dd0c60"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"prettyplease", "prettyplease",
@@ -6370,9 +6393,9 @@ dependencies = [
[[package]] [[package]]
name = "wit-component" name = "wit-component"
version = "0.225.0" version = "0.227.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2505c917564c1d74774563bbcd3e4f8c216a6508050862fd5f449ee56e3c5125" checksum = "635c3adc595422cbf2341a17fb73a319669cc8d33deed3a48368a841df86b676"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bitflags 2.9.0", "bitflags 2.9.0",
@@ -6414,9 +6437,9 @@ dependencies = [
[[package]] [[package]]
name = "wit-parser" name = "wit-parser"
version = "0.225.0" version = "0.227.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebefaa234e47224f10ce60480c5bfdece7497d0f3b87a12b41ff39e5c8377a78" checksum = "ddf445ed5157046e4baf56f9138c124a0824d4d1657e7204d71886ad8ce2fc11"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"id-arena", "id-arena",

View File

@@ -14,7 +14,7 @@ strip = "debuginfo"
codegen-units = 1 codegen-units = 1
[workspace.package] [workspace.package]
version = "3.0.0-beta.2" version = "3.0.0-beta.3"
authors = ["Luke Street <luke@street.dev>"] authors = ["Luke Street <luke@street.dev>"]
edition = "2024" edition = "2024"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"

View File

@@ -232,6 +232,7 @@ fn report_object(
if symbol.section != Some(section_idx) if symbol.section != Some(section_idx)
|| symbol.size == 0 || symbol.size == 0
|| symbol.flags.contains(SymbolFlag::Hidden) || symbol.flags.contains(SymbolFlag::Hidden)
|| symbol.flags.contains(SymbolFlag::Ignored)
{ {
continue; continue;
} }

View File

@@ -1,4 +1,8 @@
use alloc::{collections::BTreeMap, string::ToString, vec::Vec}; use alloc::{
collections::{BTreeMap, BTreeSet},
string::ToString,
vec::Vec,
};
use core::ops::Range; use core::ops::Range;
use anyhow::{Result, bail}; use anyhow::{Result, bail};
@@ -15,7 +19,7 @@ use crate::{
diff::{DiffObjConfig, MipsAbi, MipsInstrCategory, display::InstructionPart}, diff::{DiffObjConfig, MipsAbi, MipsInstrCategory, display::InstructionPart},
obj::{ obj::{
InstructionArg, InstructionArgValue, InstructionRef, Relocation, RelocationFlags, InstructionArg, InstructionArgValue, InstructionRef, Relocation, RelocationFlags,
ResolvedInstructionRef, ResolvedRelocation, ScannedInstruction, ResolvedInstructionRef, ResolvedRelocation, ScannedInstruction, SymbolFlag, SymbolFlagSet,
}, },
}; };
@@ -26,6 +30,7 @@ pub struct ArchMips {
pub isa_extension: Option<IsaExtension>, pub isa_extension: Option<IsaExtension>,
pub ri_gp_value: i32, pub ri_gp_value: i32,
pub paired_relocations: Vec<BTreeMap<u64, i64>>, pub paired_relocations: Vec<BTreeMap<u64, i64>>,
pub ignored_symbols: BTreeSet<usize>,
} }
const EF_MIPS_ABI: u32 = 0x0000F000; const EF_MIPS_ABI: u32 = 0x0000F000;
@@ -118,7 +123,25 @@ impl ArchMips {
paired_relocations[section_index] = addends; paired_relocations[section_index] = addends;
} }
Ok(Self { endianness, abi, isa_extension, ri_gp_value, paired_relocations }) let mut ignored_symbols = BTreeSet::new();
for obj_symbol in object.symbols() {
let Ok(name) = obj_symbol.name() else { continue };
if let Some(prefix) = name.strip_suffix(".NON_MATCHING") {
ignored_symbols.insert(obj_symbol.index().0);
if let Some(target_symbol) = object.symbol_by_name(prefix) {
ignored_symbols.insert(target_symbol.index().0);
}
}
}
Ok(Self {
endianness,
abi,
isa_extension,
ri_gp_value,
paired_relocations,
ignored_symbols,
})
} }
fn instruction_flags(&self, diff_config: &DiffObjConfig) -> rabbitizer::InstructionFlags { fn instruction_flags(&self, diff_config: &DiffObjConfig) -> rabbitizer::InstructionFlags {
@@ -294,6 +317,14 @@ impl Arch for ArchMips {
_ => 1, _ => 1,
} }
} }
fn extra_symbol_flags(&self, symbol: &object::Symbol) -> SymbolFlagSet {
let mut flags = SymbolFlagSet::default();
if self.ignored_symbols.contains(&symbol.index().0) {
flags |= SymbolFlag::Ignored;
}
flags
}
} }
fn push_args( fn push_args(

View File

@@ -137,7 +137,7 @@ impl Arch for ArchX86 {
{ {
instruction.set_memory_displacement64(PLACEHOLDER); instruction.set_memory_displacement64(PLACEHOLDER);
// Formatter always writes the displacement as Int32 // Formatter always writes the displacement as Int32
reloc_replace = Some((OpKind::Memory, NumberKind::Int32, PLACEHOLDER)); reloc_replace = Some((OpKind::Memory, 4, PLACEHOLDER));
} else if reloc_offset == offsets.immediate_offset() as u64 } else if reloc_offset == offsets.immediate_offset() as u64
&& reloc_size == offsets.immediate_size() && reloc_size == offsets.immediate_size()
{ {
@@ -155,18 +155,12 @@ impl Arch for ArchX86 {
_ => OpKind::default(), _ => OpKind::default(),
} }
}; };
let number_kind = match reloc_size {
2 => NumberKind::UInt16,
4 => NumberKind::UInt32,
8 => NumberKind::UInt64,
_ => NumberKind::default(),
};
if is_branch { if is_branch {
instruction.set_near_branch64(PLACEHOLDER); instruction.set_near_branch64(PLACEHOLDER);
} else { } else {
instruction.set_immediate32(PLACEHOLDER as u32); instruction.set_immediate32(PLACEHOLDER as u32);
} }
reloc_replace = Some((op_kind, number_kind, PLACEHOLDER)); reloc_replace = Some((op_kind, reloc_size, PLACEHOLDER));
} }
} }
@@ -251,7 +245,7 @@ impl Arch for ArchX86 {
struct InstructionFormatterOutput<'a> { struct InstructionFormatterOutput<'a> {
cb: &'a mut dyn FnMut(InstructionPart<'_>) -> Result<()>, cb: &'a mut dyn FnMut(InstructionPart<'_>) -> Result<()>,
reloc_replace: Option<(OpKind, NumberKind, u64)>, reloc_replace: Option<(OpKind, usize, u64)>,
error: Option<anyhow::Error>, error: Option<anyhow::Error>,
skip_next: bool, skip_next: bool,
} }
@@ -326,11 +320,18 @@ impl FormatterOutput for InstructionFormatterOutput<'_> {
return; return;
} }
if let (Some(operand), Some((target_op_kind, target_number_kind, target_value))) = if let (Some(operand), Some((target_op_kind, reloc_size, target_value))) =
(instruction_operand, self.reloc_replace) (instruction_operand, self.reloc_replace)
{ {
#[allow(clippy::match_like_matches_macro)]
if instruction.op_kind(operand) == target_op_kind if instruction.op_kind(operand) == target_op_kind
&& number_kind == target_number_kind && match (number_kind, reloc_size) {
(NumberKind::Int8 | NumberKind::UInt8, 1)
| (NumberKind::Int16 | NumberKind::UInt16, 2)
| (NumberKind::Int32 | NumberKind::UInt32, 4)
| (NumberKind::Int64 | NumberKind::UInt64, 8) => true,
_ => false,
}
&& value == target_value && value == target_value
{ {
if let Err(e) = (self.cb)(InstructionPart::reloc()) { if let Err(e) = (self.cb)(InstructionPart::reloc()) {
@@ -571,4 +572,43 @@ mod test {
.unwrap(); .unwrap();
assert_eq!(parts, &[InstructionPart::opcode("call", opcode), InstructionPart::reloc()]); assert_eq!(parts, &[InstructionPart::opcode("call", opcode), InstructionPart::reloc()]);
} }
#[test]
fn test_process_instruction_with_reloc_4() {
let arch = ArchX86 { arch: Architecture::X86, endianness: object::Endianness::Little };
let code = [0x8b, 0x15, 0xa4, 0x21, 0x7e, 0x00];
let opcode = iced_x86::Mnemonic::Mov as u16;
let mut parts = Vec::new();
arch.display_instruction(
ResolvedInstructionRef {
ins_ref: InstructionRef { address: 0x1234, size: 6, opcode },
code: &code,
relocation: Some(ResolvedRelocation {
relocation: &Relocation {
flags: RelocationFlags::Coff(pe::IMAGE_REL_I386_DIR32),
address: 0x1234 + 2,
target_symbol: 0,
addend: 0,
},
symbol: &Default::default(),
}),
..Default::default()
},
&DiffObjConfig::default(),
&mut |part| {
parts.push(part.into_static());
Ok(())
},
)
.unwrap();
assert_eq!(parts, &[
InstructionPart::opcode("mov", opcode),
InstructionPart::opaque("edx"),
InstructionPart::basic(","),
InstructionPart::basic(" "),
InstructionPart::basic("["),
InstructionPart::reloc(),
InstructionPart::basic("]"),
]);
}
} }

View File

@@ -139,27 +139,13 @@ pub fn diff_data_section(
) -> Result<(SectionDiff, SectionDiff)> { ) -> Result<(SectionDiff, SectionDiff)> {
let left_section = &left_obj.sections[left_section_idx]; let left_section = &left_obj.sections[left_section_idx];
let right_section = &right_obj.sections[right_section_idx]; let right_section = &right_obj.sections[right_section_idx];
let left_max = left_obj let left_max = symbols_matching_section(&left_obj.symbols, left_section_idx)
.symbols .filter_map(|(_, s)| s.address.checked_sub(left_section.address).map(|a| a + s.size))
.iter()
.filter_map(|s| {
if s.section != Some(left_section_idx) || s.kind == SymbolKind::Section {
return None;
}
s.address.checked_sub(left_section.address).map(|a| a + s.size)
})
.max() .max()
.unwrap_or(0) .unwrap_or(0)
.min(left_section.size); .min(left_section.size);
let right_max = right_obj let right_max = symbols_matching_section(&right_obj.symbols, right_section_idx)
.symbols .filter_map(|(_, s)| s.address.checked_sub(right_section.address).map(|a| a + s.size))
.iter()
.filter_map(|s| {
if s.section != Some(right_section_idx) || s.kind == SymbolKind::Section {
return None;
}
s.address.checked_sub(right_section.address).map(|a| a + s.size)
})
.max() .max()
.unwrap_or(0) .unwrap_or(0)
.min(right_section.size); .min(right_section.size);
@@ -412,21 +398,13 @@ pub fn diff_generic_section(
left_section_idx: usize, left_section_idx: usize,
_right_section_idx: usize, _right_section_idx: usize,
) -> Result<(SectionDiff, SectionDiff)> { ) -> Result<(SectionDiff, SectionDiff)> {
let match_percent = if left_obj let match_percent = if symbols_matching_section(&left_obj.symbols, left_section_idx)
.symbols
.iter()
.enumerate()
.filter(|(_, s)| s.section == Some(left_section_idx) && s.kind != SymbolKind::Section)
.map(|(i, _)| &left_diff.symbols[i]) .map(|(i, _)| &left_diff.symbols[i])
.all(|d| d.match_percent == Some(100.0)) .all(|d| d.match_percent == Some(100.0))
{ {
100.0 // Avoid fp precision issues 100.0 // Avoid fp precision issues
} else { } else {
let (matched, total) = left_obj let (matched, total) = symbols_matching_section(&left_obj.symbols, left_section_idx)
.symbols
.iter()
.enumerate()
.filter(|(_, s)| s.section == Some(left_section_idx) && s.kind != SymbolKind::Section)
.map(|(i, s)| (s, &left_diff.symbols[i])) .map(|(i, s)| (s, &left_diff.symbols[i]))
.fold((0.0, 0.0), |(matched, total), (s, d)| { .fold((0.0, 0.0), |(matched, total), (s, d)| {
(matched + d.match_percent.unwrap_or(0.0) * s.size as f32, total + s.size as f32) (matched + d.match_percent.unwrap_or(0.0) * s.size as f32, total + s.size as f32)
@@ -449,19 +427,11 @@ pub fn diff_bss_section(
right_section_idx: usize, right_section_idx: usize,
) -> Result<(SectionDiff, SectionDiff)> { ) -> Result<(SectionDiff, SectionDiff)> {
let left_section = &left_obj.sections[left_section_idx]; let left_section = &left_obj.sections[left_section_idx];
let left_sizes = left_obj let left_sizes = symbols_matching_section(&left_obj.symbols, left_section_idx)
.symbols
.iter()
.enumerate()
.filter(|(_, s)| s.section == Some(left_section_idx) && s.kind != SymbolKind::Section)
.filter_map(|(_, s)| s.address.checked_sub(left_section.address).map(|a| (a, s.size))) .filter_map(|(_, s)| s.address.checked_sub(left_section.address).map(|a| (a, s.size)))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let right_section = &right_obj.sections[right_section_idx]; let right_section = &right_obj.sections[right_section_idx];
let right_sizes = right_obj let right_sizes = symbols_matching_section(&right_obj.symbols, right_section_idx)
.symbols
.iter()
.enumerate()
.filter(|(_, s)| s.section == Some(right_section_idx) && s.kind != SymbolKind::Section)
.filter_map(|(_, s)| s.address.checked_sub(right_section.address).map(|a| (a, s.size))) .filter_map(|(_, s)| s.address.checked_sub(right_section.address).map(|a| (a, s.size)))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let ops = capture_diff_slices(Algorithm::Patience, &left_sizes, &right_sizes); let ops = capture_diff_slices(Algorithm::Patience, &left_sizes, &right_sizes);
@@ -487,3 +457,15 @@ pub fn diff_bss_section(
SectionDiff { match_percent: Some(match_percent), data_diff: vec![], reloc_diff: vec![] }, SectionDiff { match_percent: Some(match_percent), data_diff: vec![], reloc_diff: vec![] },
)) ))
} }
fn symbols_matching_section(
symbols: &[Symbol],
section_idx: usize,
) -> impl Iterator<Item = (usize, &Symbol)> + '_ {
symbols.iter().enumerate().filter(move |(_, s)| {
s.section == Some(section_idx)
&& s.kind != SymbolKind::Section
&& s.size > 0
&& !s.flags.contains(SymbolFlag::Ignored)
})
}

View File

@@ -563,7 +563,7 @@ pub fn instruction_hover(
out out
} }
#[derive(Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub enum SymbolFilter<'a> { pub enum SymbolFilter<'a> {
None, None,
Search(&'a Regex), Search(&'a Regex),
@@ -580,7 +580,11 @@ fn symbol_matches_filter(
if symbol.section.is_none() && !symbol.flags.contains(SymbolFlag::Common) { if symbol.section.is_none() && !symbol.flags.contains(SymbolFlag::Common) {
return false; return false;
} }
if !show_hidden_symbols && (symbol.size == 0 || symbol.flags.contains(SymbolFlag::Hidden)) { if !show_hidden_symbols
&& (symbol.size == 0
|| symbol.flags.contains(SymbolFlag::Hidden)
|| symbol.flags.contains(SymbolFlag::Ignored))
{
return false; return false;
} }
match filter { match filter {
@@ -701,7 +705,7 @@ pub fn display_sections(
}); });
} }
} }
sections.sort_by(|a, b| a.id.cmp(&b.id)); sections.sort_by(|a, b| a.name.cmp(&b.name));
sections sections
} }

View File

@@ -371,7 +371,10 @@ fn generate_mapping_symbols(
}; };
let base_section_kind = symbol_section_kind(base_obj, &base_obj.symbols[base_symbol_ref]); let base_section_kind = symbol_section_kind(base_obj, &base_obj.symbols[base_symbol_ref]);
for (target_symbol_index, target_symbol) in target_obj.symbols.iter().enumerate() { for (target_symbol_index, target_symbol) in target_obj.symbols.iter().enumerate() {
if symbol_section_kind(target_obj, target_symbol) != base_section_kind { if target_symbol.size == 0
|| target_symbol.flags.contains(SymbolFlag::Ignored)
|| symbol_section_kind(target_obj, target_symbol) != base_section_kind
{
continue; continue;
} }
match base_section_kind { match base_section_kind {
@@ -526,6 +529,9 @@ fn matching_symbols(
)?; )?;
} }
for (symbol_idx, symbol) in left.symbols.iter().enumerate() { for (symbol_idx, symbol) in left.symbols.iter().enumerate() {
if symbol.size == 0 || symbol.flags.contains(SymbolFlag::Ignored) {
continue;
}
let section_kind = symbol_section_kind(left, symbol); let section_kind = symbol_section_kind(left, symbol);
if section_kind == SectionKind::Unknown { if section_kind == SectionKind::Unknown {
continue; continue;
@@ -547,6 +553,9 @@ fn matching_symbols(
} }
if let Some(right) = right { if let Some(right) = right {
for (symbol_idx, symbol) in right.symbols.iter().enumerate() { for (symbol_idx, symbol) in right.symbols.iter().enumerate() {
if symbol.size == 0 || symbol.flags.contains(SymbolFlag::Ignored) {
continue;
}
let section_kind = symbol_section_kind(right, symbol); let section_kind = symbol_section_kind(right, symbol);
if section_kind == SectionKind::Unknown { if section_kind == SectionKind::Unknown {
continue; continue;
@@ -572,9 +581,10 @@ fn unmatched_symbols<'obj, 'used>(
where where
'obj: 'used, 'obj: 'used,
{ {
obj.symbols.iter().enumerate().filter(move |&(symbol_idx, _)| { obj.symbols.iter().enumerate().filter(move |&(symbol_idx, symbol)| {
// Skip symbols that have already been matched !symbol.flags.contains(SymbolFlag::Ignored)
!used.is_some_and(|u| u.contains(&symbol_idx)) // Skip symbols that have already been matched
&& !used.is_some_and(|u| u.contains(&symbol_idx))
}) })
} }

View File

@@ -42,6 +42,8 @@ flags! {
HasExtra, HasExtra,
/// Symbol size was missing and was inferred /// Symbol size was missing and was inferred
SizeInferred, SizeInferred,
/// Symbol should be ignored by any diffing
Ignored,
} }
} }

View File

@@ -37,3 +37,11 @@ fn cross_endian_diff() {
assert_eq!(left_diff.instruction_rows[2].kind, diff::InstructionDiffKind::None); assert_eq!(left_diff.instruction_rows[2].kind, diff::InstructionDiffKind::None);
assert_eq!(right_diff.instruction_rows[2].kind, diff::InstructionDiffKind::None); assert_eq!(right_diff.instruction_rows[2].kind, diff::InstructionDiffKind::None);
} }
#[test]
#[cfg(feature = "mips")]
fn filter_non_matching() {
let diff_config = diff::DiffObjConfig::default();
let obj = obj::read::parse(include_object!("data/mips/vw_main.c.o"), &diff_config).unwrap();
insta::assert_debug_snapshot!(obj.symbols);
}

View File

@@ -61,7 +61,7 @@ fn diff_ppc() {
let base_diff = diff.right.as_ref().unwrap(); let base_diff = diff.right.as_ref().unwrap();
let sections_display = display::display_sections( let sections_display = display::display_sections(
&target_obj, &target_obj,
&target_diff, target_diff,
display::SymbolFilter::None, display::SymbolFilter::None,
false, false,
false, false,

View File

@@ -1,4 +1,4 @@
use objdiff_core::{diff, obj}; use objdiff_core::{diff, diff::display::SymbolFilter, obj};
mod common; mod common;
@@ -40,3 +40,18 @@ fn read_x86_64() {
let output = common::display_diff(&obj, &diff, symbol_idx, &diff_config); let output = common::display_diff(&obj, &diff, symbol_idx, &diff_config);
insta::assert_snapshot!(output); insta::assert_snapshot!(output);
} }
#[test]
#[cfg(feature = "x86")]
fn display_section_ordering() {
let diff_config = diff::DiffObjConfig::default();
let obj = obj::read::parse(include_object!("data/x86/basenode.obj"), &diff_config).unwrap();
let obj_diff =
diff::diff_objs(Some(&obj), None, None, &diff_config, &diff::MappingConfig::default())
.unwrap()
.left
.unwrap();
let section_display =
diff::display::display_sections(&obj, &obj_diff, SymbolFilter::None, false, false, false);
insta::assert_debug_snapshot!(section_display);
}

View File

@@ -13,7 +13,7 @@ pub fn display_diff(
for row in &diff.instruction_rows { for row in &diff.instruction_rows {
output.push('['); output.push('[');
let mut separator = false; let mut separator = false;
objdiff_core::diff::display::display_row(&obj, symbol_idx, row, &diff_config, |segment| { objdiff_core::diff::display::display_row(obj, symbol_idx, row, diff_config, |segment| {
if separator { if separator {
output.push_str(", "); output.push_str(", ");
} else { } else {
@@ -47,6 +47,6 @@ macro_rules! include_bytes_align_as {
#[macro_export] #[macro_export]
macro_rules! include_object { macro_rules! include_object {
($path:literal) => { ($path:literal) => {
include_bytes_align_as!(u32, $path) include_bytes_align_as!(u64, $path)
}; };
} }

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,452 @@
---
source: objdiff-core/tests/arch_mips.rs
expression: obj.symbols
---
[
Symbol {
name: "build/src/bodyprog/view/vw_main.i",
demangled_name: None,
address: 0,
size: 0,
kind: Unknown,
section: None,
flags: FlagSet(Local),
align: None,
virtual_address: None,
},
Symbol {
name: "[.text]",
demangled_name: None,
address: 0,
size: 0,
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(
2,
),
flags: FlagSet(Local),
align: None,
virtual_address: None,
},
Symbol {
name: "[.bss]",
demangled_name: None,
address: 0,
size: 0,
kind: Section,
section: Some(
3,
),
flags: FlagSet(Local),
align: None,
virtual_address: None,
},
Symbol {
name: "gcc2_compiled.",
demangled_name: None,
address: 0,
size: 0,
kind: Unknown,
section: Some(
0,
),
flags: FlagSet(Local),
align: None,
virtual_address: None,
},
Symbol {
name: "__gnu_compiled_c",
demangled_name: None,
address: 0,
size: 0,
kind: Unknown,
section: Some(
0,
),
flags: FlagSet(Local),
align: None,
virtual_address: None,
},
Symbol {
name: ".endfunc_80048AF4",
demangled_name: None,
address: 424,
size: 0,
kind: Unknown,
section: Some(
0,
),
flags: FlagSet(Local),
align: None,
virtual_address: None,
},
Symbol {
name: ".endfunc_80048DA8",
demangled_name: None,
address: 1028,
size: 0,
kind: Unknown,
section: Some(
0,
),
flags: FlagSet(Local),
align: None,
virtual_address: None,
},
Symbol {
name: ".endfunc_80048E3C",
demangled_name: None,
address: 1264,
size: 0,
kind: Unknown,
section: Some(
0,
),
flags: FlagSet(Local),
align: None,
virtual_address: None,
},
Symbol {
name: "[.reginfo]",
demangled_name: None,
address: 0,
size: 24,
kind: Section,
section: Some(
4,
),
flags: FlagSet(Local),
align: None,
virtual_address: None,
},
Symbol {
name: "[.MIPS.abiflags]",
demangled_name: None,
address: 0,
size: 24,
kind: Section,
section: Some(
5,
),
flags: FlagSet(Local),
align: None,
virtual_address: None,
},
Symbol {
name: "[.pdr]",
demangled_name: None,
address: 0,
size: 320,
kind: Section,
section: Some(
6,
),
flags: FlagSet(Local),
align: None,
virtual_address: None,
},
Symbol {
name: "[.gnu.attributes]",
demangled_name: None,
address: 0,
size: 16,
kind: Section,
section: Some(
8,
),
flags: FlagSet(Local),
align: None,
virtual_address: None,
},
Symbol {
name: "vwInitViewInfo",
demangled_name: None,
address: 0,
size: 88,
kind: Function,
section: Some(
0,
),
flags: FlagSet(Global),
align: None,
virtual_address: None,
},
Symbol {
name: "vwViewPointInfo",
demangled_name: None,
address: 0,
size: 0,
kind: Unknown,
section: None,
flags: FlagSet(Global),
align: None,
virtual_address: None,
},
Symbol {
name: "GsInitCoordinate2",
demangled_name: None,
address: 0,
size: 0,
kind: Unknown,
section: None,
flags: FlagSet(Global),
align: None,
virtual_address: None,
},
Symbol {
name: "vwSetViewInfo",
demangled_name: None,
address: 784,
size: 96,
kind: Function,
section: Some(
0,
),
flags: FlagSet(Global),
align: None,
virtual_address: None,
},
Symbol {
name: "vwGetViewCoord",
demangled_name: None,
address: 88,
size: 12,
kind: Function,
section: Some(
0,
),
flags: FlagSet(Global),
align: None,
virtual_address: None,
},
Symbol {
name: "vwGetViewPosition",
demangled_name: None,
address: 100,
size: 40,
kind: Function,
section: Some(
0,
),
flags: FlagSet(Global),
align: None,
virtual_address: None,
},
Symbol {
name: "vwGetViewAngle",
demangled_name: None,
address: 140,
size: 48,
kind: Function,
section: Some(
0,
),
flags: FlagSet(Global),
align: None,
virtual_address: None,
},
Symbol {
name: "func_80048AF4",
demangled_name: None,
address: 188,
size: 236,
kind: Function,
section: Some(
0,
),
flags: FlagSet(Global | Ignored),
align: None,
virtual_address: None,
},
Symbol {
name: "ratan2",
demangled_name: None,
address: 0,
size: 0,
kind: Unknown,
section: None,
flags: FlagSet(Global),
align: None,
virtual_address: None,
},
Symbol {
name: "SquareRoot0",
demangled_name: None,
address: 0,
size: 0,
kind: Unknown,
section: None,
flags: FlagSet(Global),
align: None,
virtual_address: None,
},
Symbol {
name: "func_80096C94",
demangled_name: None,
address: 0,
size: 0,
kind: Unknown,
section: None,
flags: FlagSet(Global),
align: None,
virtual_address: None,
},
Symbol {
name: "vwSetViewInfoDirectMatrix",
demangled_name: None,
address: 696,
size: 88,
kind: Function,
section: Some(
0,
),
flags: FlagSet(Global),
align: None,
virtual_address: None,
},
Symbol {
name: "func_80048AF4.NON_MATCHING",
demangled_name: None,
address: 188,
size: 236,
kind: Function,
section: Some(
0,
),
flags: FlagSet(Global | Ignored),
align: None,
virtual_address: None,
},
Symbol {
name: "vwSetCoordRefAndEntou",
demangled_name: None,
address: 424,
size: 272,
kind: Function,
section: Some(
0,
),
flags: FlagSet(Global),
align: None,
virtual_address: None,
},
Symbol {
name: "func_80096E78",
demangled_name: None,
address: 0,
size: 0,
kind: Unknown,
section: None,
flags: FlagSet(Global),
align: None,
virtual_address: None,
},
Symbol {
name: "shRsin",
demangled_name: None,
address: 0,
size: 0,
kind: Unknown,
section: None,
flags: FlagSet(Global),
align: None,
virtual_address: None,
},
Symbol {
name: "shRcos",
demangled_name: None,
address: 0,
size: 0,
kind: Unknown,
section: None,
flags: FlagSet(Global),
align: None,
virtual_address: None,
},
Symbol {
name: "vbSetRefView",
demangled_name: None,
address: 0,
size: 0,
kind: Unknown,
section: None,
flags: FlagSet(Global),
align: None,
virtual_address: None,
},
Symbol {
name: "vwMatrixToAngleYXZ",
demangled_name: None,
address: 0,
size: 0,
kind: Unknown,
section: None,
flags: FlagSet(Global),
align: None,
virtual_address: None,
},
Symbol {
name: "func_80048DA8",
demangled_name: None,
address: 880,
size: 148,
kind: Function,
section: Some(
0,
),
flags: FlagSet(Global | Ignored),
align: None,
virtual_address: None,
},
Symbol {
name: "func_80048DA8.NON_MATCHING",
demangled_name: None,
address: 880,
size: 148,
kind: Function,
section: Some(
0,
),
flags: FlagSet(Global | Ignored),
align: None,
virtual_address: None,
},
Symbol {
name: "func_80048E3C",
demangled_name: None,
address: 1028,
size: 236,
kind: Function,
section: Some(
0,
),
flags: FlagSet(Global | Ignored),
align: None,
virtual_address: None,
},
Symbol {
name: "func_80048E3C.NON_MATCHING",
demangled_name: None,
address: 1028,
size: 236,
kind: Function,
section: Some(
0,
),
flags: FlagSet(Global | Ignored),
align: None,
virtual_address: None,
},
]

View File

@@ -50,6 +50,7 @@ Object {
{}, {},
{}, {},
], ],
ignored_symbols: {},
}, },
endianness: Little, endianness: Little,
symbols: [ symbols: [

View File

@@ -0,0 +1,210 @@
---
source: objdiff-core/tests/arch_x86.rs
expression: section_display
---
[
SectionDisplay {
id: ".text-0",
name: ".text",
size: 47,
match_percent: None,
symbols: [
SectionDisplaySymbol {
symbol: 28,
is_mapping_symbol: false,
},
],
},
SectionDisplay {
id: ".text-1",
name: ".text",
size: 65,
match_percent: None,
symbols: [
SectionDisplaySymbol {
symbol: 29,
is_mapping_symbol: false,
},
],
},
SectionDisplay {
id: ".text-2",
name: ".text",
size: 5,
match_percent: None,
symbols: [
SectionDisplaySymbol {
symbol: 30,
is_mapping_symbol: false,
},
],
},
SectionDisplay {
id: ".text-3",
name: ".text",
size: 141,
match_percent: None,
symbols: [
SectionDisplaySymbol {
symbol: 31,
is_mapping_symbol: false,
},
],
},
SectionDisplay {
id: ".text-4",
name: ".text",
size: 120,
match_percent: None,
symbols: [
SectionDisplaySymbol {
symbol: 34,
is_mapping_symbol: false,
},
],
},
SectionDisplay {
id: ".text-5",
name: ".text",
size: 378,
match_percent: None,
symbols: [
SectionDisplaySymbol {
symbol: 37,
is_mapping_symbol: false,
},
],
},
SectionDisplay {
id: ".text-6",
name: ".text",
size: 130,
match_percent: None,
symbols: [
SectionDisplaySymbol {
symbol: 38,
is_mapping_symbol: false,
},
],
},
SectionDisplay {
id: ".text-7",
name: ".text",
size: 123,
match_percent: None,
symbols: [
SectionDisplaySymbol {
symbol: 39,
is_mapping_symbol: false,
},
],
},
SectionDisplay {
id: ".text-8",
name: ".text",
size: 70,
match_percent: None,
symbols: [
SectionDisplaySymbol {
symbol: 40,
is_mapping_symbol: false,
},
],
},
SectionDisplay {
id: ".text-9",
name: ".text",
size: 90,
match_percent: None,
symbols: [
SectionDisplaySymbol {
symbol: 41,
is_mapping_symbol: false,
},
],
},
SectionDisplay {
id: ".text-10",
name: ".text",
size: 82,
match_percent: None,
symbols: [
SectionDisplaySymbol {
symbol: 42,
is_mapping_symbol: false,
},
],
},
SectionDisplay {
id: ".text-11",
name: ".text",
size: 336,
match_percent: None,
symbols: [
SectionDisplaySymbol {
symbol: 43,
is_mapping_symbol: false,
},
],
},
SectionDisplay {
id: ".text-12",
name: ".text",
size: 193,
match_percent: None,
symbols: [
SectionDisplaySymbol {
symbol: 50,
is_mapping_symbol: false,
},
],
},
SectionDisplay {
id: ".text-13",
name: ".text",
size: 544,
match_percent: None,
symbols: [
SectionDisplaySymbol {
symbol: 53,
is_mapping_symbol: false,
},
],
},
SectionDisplay {
id: ".text-14",
name: ".text",
size: 250,
match_percent: None,
symbols: [
SectionDisplaySymbol {
symbol: 56,
is_mapping_symbol: false,
},
],
},
SectionDisplay {
id: ".text-15",
name: ".text",
size: 89,
match_percent: None,
symbols: [
SectionDisplaySymbol {
symbol: 57,
is_mapping_symbol: false,
},
],
},
SectionDisplay {
id: ".text-16",
name: ".text",
size: 119,
match_percent: None,
symbols: [
SectionDisplaySymbol {
symbol: 60,
is_mapping_symbol: false,
},
],
},
]

View File

@@ -24,7 +24,7 @@ std = ["objdiff-core/std"]
[dependencies] [dependencies]
log = { version = "0.4", default-features = false } log = { version = "0.4", default-features = false }
regex = { version = "1.11", default-features = false } regex = { version = "1.11", default-features = false, features = ["unicode-case"] }
[dependencies.objdiff-core] [dependencies.objdiff-core]
path = "../objdiff-core" path = "../objdiff-core"
@@ -35,7 +35,7 @@ features = ["arm", "arm64", "mips", "ppc", "x86", "dwarf"]
talc = { version = "4.4", default-features = false, features = ["lock_api"] } talc = { version = "4.4", default-features = false, features = ["lock_api"] }
[target.'cfg(target_os = "wasi")'.dependencies] [target.'cfg(target_os = "wasi")'.dependencies]
wit-bindgen = { version = "0.39", default-features = false, features = ["macros"] } wit-bindgen = { version = "0.40", default-features = false, features = ["macros"] }
[build-dependencies] [build-dependencies]
wit-deps = "0.5" wit-deps = "0.5"

View File

@@ -1,12 +1,12 @@
{ {
"name": "objdiff-wasm", "name": "objdiff-wasm",
"version": "3.0.0-alpha.3", "version": "3.0.0-beta.3",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "objdiff-wasm", "name": "objdiff-wasm",
"version": "3.0.0-alpha.3", "version": "3.0.0-beta.3",
"license": "MIT OR Apache-2.0", "license": "MIT OR Apache-2.0",
"devDependencies": { "devDependencies": {
"@biomejs/biome": "^1.9.3", "@biomejs/biome": "^1.9.3",

View File

@@ -1,6 +1,6 @@
{ {
"name": "objdiff-wasm", "name": "objdiff-wasm",
"version": "3.0.0-beta.2", "version": "3.0.0-beta.3",
"description": "A local diffing tool for decompilation projects.", "description": "A local diffing tool for decompilation projects.",
"author": { "author": {
"name": "Luke Street", "name": "Luke Street",

View File

@@ -8,7 +8,7 @@ use alloc::{
use core::cell::RefCell; use core::cell::RefCell;
use objdiff_core::{diff, obj}; use objdiff_core::{diff, obj};
use regex::RegexBuilder; use regex::{Regex, RegexBuilder};
use super::logging; use super::logging;
@@ -86,19 +86,31 @@ impl GuestDiff for Component {
} }
} }
fn build_regex(s: &str) -> Option<Regex> {
if s.is_empty() {
return None;
}
let e = match RegexBuilder::new(s).case_insensitive(true).build() {
Ok(regex) => return Some(regex),
Err(e) => e,
};
// Use the string as a literal if the regex fails to compile
let escaped = regex::escape(s);
if let Ok(regex) = RegexBuilder::new(&escaped).case_insensitive(true).build() {
return Some(regex);
}
// Use the original error if the escaped string also fails
log::warn!("Failed to compile regex: {}", e);
None
}
impl GuestDisplay for Component { impl GuestDisplay for Component {
fn display_sections( fn display_sections(
diff: ObjectDiffBorrow, diff: ObjectDiffBorrow,
filter: SymbolFilter, filter: SymbolFilter,
config: DisplayConfig, config: DisplayConfig,
) -> Vec<SectionDisplay> { ) -> Vec<SectionDisplay> {
let regex = filter.regex.as_ref().and_then(|s| { let regex = filter.regex.as_deref().and_then(build_regex);
RegexBuilder::new(s).case_insensitive(true).build().ok().or_else(|| {
// Use the string as a literal if the regex fails to compile
let escaped = regex::escape(s);
RegexBuilder::new(&escaped).case_insensitive(true).build().ok()
})
});
let filter = if let Some(mapping) = filter.mapping { let filter = if let Some(mapping) = filter.mapping {
diff::display::SymbolFilter::Mapping(mapping as usize, regex.as_ref()) diff::display::SymbolFilter::Mapping(mapping as usize, regex.as_ref())
} else if let Some(regex) = &regex { } else if let Some(regex) = &regex {
@@ -320,6 +332,7 @@ impl From<obj::SymbolFlagSet> for SymbolFlags {
obj::SymbolFlag::Hidden => SymbolFlags::HIDDEN, obj::SymbolFlag::Hidden => SymbolFlags::HIDDEN,
obj::SymbolFlag::HasExtra => SymbolFlags::HAS_EXTRA, obj::SymbolFlag::HasExtra => SymbolFlags::HAS_EXTRA,
obj::SymbolFlag::SizeInferred => SymbolFlags::SIZE_INFERRED, obj::SymbolFlag::SizeInferred => SymbolFlags::SIZE_INFERRED,
obj::SymbolFlag::Ignored => SymbolFlags::IGNORED,
}; };
} }
out out

View File

@@ -89,6 +89,7 @@ interface display {
hidden, hidden,
has-extra, has-extra,
size-inferred, size-inferred,
ignored,
} }
record symbol-display { record symbol-display {