mirror of
https://github.com/encounter/objdiff.git
synced 2026-07-10 12:18:36 -07:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f7cb494a62 | |||
| 7cc6ed2b43 | |||
| 532b684682 |
Generated
+4
-4
@@ -3435,7 +3435,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "objdiff-cli"
|
||||
version = "3.1.0"
|
||||
version = "3.1.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"argp",
|
||||
@@ -3458,7 +3458,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "objdiff-core"
|
||||
version = "3.1.0"
|
||||
version = "3.1.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arm-attr",
|
||||
@@ -3513,7 +3513,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "objdiff-gui"
|
||||
version = "3.1.0"
|
||||
version = "3.1.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"argp",
|
||||
@@ -3550,7 +3550,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "objdiff-wasm"
|
||||
version = "3.1.0"
|
||||
version = "3.1.1"
|
||||
dependencies = [
|
||||
"log",
|
||||
"objdiff-core",
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ default-members = [
|
||||
resolver = "3"
|
||||
|
||||
[workspace.package]
|
||||
version = "3.1.0"
|
||||
version = "3.1.1"
|
||||
authors = ["Luke Street <luke@street.dev>"]
|
||||
edition = "2024"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
@@ -124,7 +124,40 @@ impl Arch for ArchX86 {
|
||||
opcode: DATA_OPCODE,
|
||||
branch_dest: None,
|
||||
});
|
||||
|
||||
reloc_iter.next();
|
||||
|
||||
// support .byte arrays after jump tables (they're typically known as indirect tables)
|
||||
|
||||
let indirect_array_address = address.wrapping_add(size as u64);
|
||||
let indirect_array_pos = decoder.position();
|
||||
|
||||
let max_size = code.len().saturating_sub(indirect_array_pos);
|
||||
|
||||
let indirect_array_size = reloc_iter
|
||||
.peek()
|
||||
.map(|next_reloc| {
|
||||
next_reloc.address.saturating_sub(indirect_array_address)
|
||||
as usize
|
||||
})
|
||||
.unwrap_or(max_size)
|
||||
.min(max_size);
|
||||
|
||||
if indirect_array_size > 0 {
|
||||
for i in 0..indirect_array_size {
|
||||
out.push(InstructionRef {
|
||||
address: indirect_array_address + i as u64,
|
||||
size: 1,
|
||||
opcode: DATA_OPCODE,
|
||||
branch_dest: None,
|
||||
});
|
||||
}
|
||||
// move decoder to after the array (there can be multiple jump+indirect tables in one function)
|
||||
let _ =
|
||||
decoder.set_position(indirect_array_pos + indirect_array_size);
|
||||
decoder.set_ip(indirect_array_address + indirect_array_size as u64);
|
||||
}
|
||||
|
||||
continue 'outer;
|
||||
}
|
||||
}
|
||||
@@ -156,6 +189,7 @@ impl Arch for ArchX86 {
|
||||
) -> Result<()> {
|
||||
if resolved.ins_ref.opcode == DATA_OPCODE {
|
||||
let (mnemonic, imm) = match resolved.ins_ref.size {
|
||||
1 => (".byte", resolved.code[0] as u64),
|
||||
2 => (".word", self.endianness.read_u16_bytes(resolved.code.try_into()?) as u64),
|
||||
4 => (".dword", self.endianness.read_u32_bytes(resolved.code.try_into()?) as u64),
|
||||
_ => bail!("Unsupported x86 inline data size {}", resolved.ins_ref.size),
|
||||
@@ -791,4 +825,33 @@ mod test {
|
||||
.unwrap();
|
||||
assert_eq!(parts, &[InstructionPart::opcode("call", opcode), InstructionPart::reloc()]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_display_1_byte_inline_data() {
|
||||
let arch = ArchX86 { arch: Architecture::X86, endianness: object::Endianness::Little };
|
||||
let code = [0xAB];
|
||||
let mut parts = Vec::new();
|
||||
arch.display_instruction(
|
||||
ResolvedInstructionRef {
|
||||
ins_ref: InstructionRef {
|
||||
address: 0x1234,
|
||||
size: 1,
|
||||
opcode: DATA_OPCODE,
|
||||
branch_dest: None,
|
||||
},
|
||||
code: &code,
|
||||
..Default::default()
|
||||
},
|
||||
&DiffObjConfig::default(),
|
||||
&mut |part| {
|
||||
parts.push(part.into_static());
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(parts, &[
|
||||
InstructionPart::opcode(".byte", DATA_OPCODE),
|
||||
InstructionPart::unsigned(0xABu64),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -680,16 +680,16 @@ fn symbol_section_kind(obj: &Object, symbol: &Symbol) -> SectionKind {
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if a symbol is a compiler-generated literal like @1234.
|
||||
fn is_symbol_compiler_generated_literal(symbol: &Symbol) -> bool {
|
||||
if !symbol.name.starts_with('@') {
|
||||
return false;
|
||||
}
|
||||
if !symbol.name[1..].chars().all(char::is_numeric) {
|
||||
/// Check if a symbol is a compiler-generated like @1234 or _$E1234.
|
||||
fn is_symbol_compiler_generated(symbol: &Symbol) -> bool {
|
||||
if symbol.name.starts_with('@') && symbol.name[1..].chars().all(char::is_numeric) {
|
||||
// Exclude @stringBase0, @GUARD@, etc.
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
true
|
||||
if symbol.name.starts_with("_$E") && symbol.name[3..].chars().all(char::is_numeric) {
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn find_symbol(
|
||||
@@ -705,8 +705,8 @@ fn find_symbol(
|
||||
|
||||
// Match compiler-generated symbols against each other (e.g. @251 -> @60)
|
||||
// If they are in the same section and have the same value
|
||||
if is_symbol_compiler_generated_literal(in_symbol)
|
||||
&& matches!(section_kind, SectionKind::Data | SectionKind::Bss)
|
||||
if is_symbol_compiler_generated(in_symbol)
|
||||
&& matches!(section_kind, SectionKind::Code | SectionKind::Data | SectionKind::Bss)
|
||||
{
|
||||
let mut closest_match_symbol_idx = None;
|
||||
let mut closest_match_percent = 0.0;
|
||||
@@ -717,12 +717,12 @@ fn find_symbol(
|
||||
if obj.sections[section_index].name != section_name {
|
||||
continue;
|
||||
}
|
||||
if !is_symbol_compiler_generated_literal(symbol) {
|
||||
if !is_symbol_compiler_generated(symbol) {
|
||||
continue;
|
||||
}
|
||||
match section_kind {
|
||||
SectionKind::Data => {
|
||||
// For data, pick the first symbol with exactly matching bytes and relocations.
|
||||
SectionKind::Data | SectionKind::Code => {
|
||||
// For code or data, pick the first symbol with exactly matching bytes and relocations.
|
||||
// If no symbols match exactly, and `fuzzy_literals` is true, pick the closest
|
||||
// plausible match instead.
|
||||
if let Ok((left_diff, _right_diff)) =
|
||||
|
||||
@@ -104,3 +104,21 @@ fn read_x86_local_labels() {
|
||||
.unwrap();
|
||||
insta::assert_debug_snapshot!(obj);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "x86")]
|
||||
fn read_x86_indirect_table() {
|
||||
let diff_config = diff::DiffObjConfig::default();
|
||||
let obj = obj::read::parse(
|
||||
include_object!("data/x86/indirect_table.obj"),
|
||||
&diff_config,
|
||||
diff::DiffSide::Base,
|
||||
)
|
||||
.unwrap();
|
||||
insta::assert_debug_snapshot!(obj);
|
||||
let symbol_idx = obj.symbols.iter().position(|s| s.name == "?process@@YAHHHH@Z").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);
|
||||
}
|
||||
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "objdiff-wasm",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "objdiff-wasm",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "^1.9.3",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "objdiff-wasm",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"description": "A local diffing tool for decompilation projects.",
|
||||
"author": {
|
||||
"name": "Luke Street",
|
||||
|
||||
Reference in New Issue
Block a user