Compare commits

...

11 Commits

Author SHA1 Message Date
Steven Casper
9b557e4c8e Limit left-panel scrollview to the file tree (#185)
* Limit left-panel scrollview to the file tree

Removes the redundant build button

* Expand ScrollArea to full side panel width

* Use auto_shrink(false) instead of set_width

---------

Co-authored-by: Luke Street <luke@street.dev>
2025-04-01 12:35:17 -06:00
LagoLunatic
b9ba5796ed PPC: Fix pooled relocation addends being added twice sometimes (#184) 2025-03-30 22:00:36 -06:00
LagoLunatic
e101610416 ARM: Fix subtract with overflow error when no mapping symbol at address 0 (#183) 2025-03-30 22:00:06 -06:00
LagoLunatic
196c003a92 Reimplement colorized data relocation hover diffs (#182)
* Reimplement colorized data relocation hover diffs

* Fix objdiff-wasm build

Data diffing doesn't seem to be fully implemented in objdiff-wasm yet, so just putting placeholders in so it compiles.

* Reloc hover: Add separators, override special color too
2025-03-28 21:48:14 -06:00
7b00a9e9f2 wasm: Cache objects via data hash (XXH3) 2025-03-21 08:27:19 -06:00
311de887ec Update test snapshot 2025-03-19 19:00:57 -06:00
485b259c32 Apply clippy suggestion 2025-03-19 19:00:01 -06:00
d8fdfaa2c0 Fix no_std build 2025-03-19 18:57:41 -06:00
2612cda1fb objdiff-cli report: Skip unknown sections
Regression in v3.0.0-alpha.1

Fixes #171
2025-03-19 18:54:34 -06:00
bc46e17824 x86_64: Fix relocation placement in instruction 2025-03-19 18:54:34 -06:00
e735adbd3d x86: Support inline data for jumptables 2025-03-19 18:54:34 -06:00
26 changed files with 1229 additions and 75 deletions

15
Cargo.lock generated
View File

@@ -3280,7 +3280,7 @@ dependencies = [
[[package]]
name = "objdiff-cli"
version = "3.0.0-beta.4"
version = "3.0.0-beta.5"
dependencies = [
"anyhow",
"argp",
@@ -3303,7 +3303,7 @@ dependencies = [
[[package]]
name = "objdiff-core"
version = "3.0.0-beta.4"
version = "3.0.0-beta.5"
dependencies = [
"anyhow",
"arm-attr",
@@ -3356,7 +3356,7 @@ dependencies = [
[[package]]
name = "objdiff-gui"
version = "3.0.0-beta.4"
version = "3.0.0-beta.5"
dependencies = [
"anyhow",
"cfg-if",
@@ -3392,7 +3392,7 @@ dependencies = [
[[package]]
name = "objdiff-wasm"
version = "3.0.0-beta.4"
version = "3.0.0-beta.5"
dependencies = [
"log",
"objdiff-core",
@@ -3400,6 +3400,7 @@ dependencies = [
"talc",
"wit-bindgen",
"wit-deps",
"xxhash-rust",
]
[[package]]
@@ -6547,6 +6548,12 @@ version = "0.8.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5b940ebc25896e71dd073bad2dbaa2abfe97b0a391415e22ad1326d9c54e3c4"
[[package]]
name = "xxhash-rust"
version = "0.8.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3"
[[package]]
name = "yaxpeax-arch"
version = "0.3.2"

View File

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

View File

@@ -202,6 +202,9 @@ fn report_object(
for ((section_idx, section), section_diff) in
obj.sections.iter().enumerate().zip(&obj_diff.sections)
{
if section.kind == SectionKind::Unknown {
continue;
}
let section_match_percent = section_diff.match_percent.unwrap_or_else(|| {
// Support cases where we don't have a target object,
// assume complete means 100% match

View File

@@ -14,7 +14,7 @@ use crate::{
arch::Arch,
diff::{ArmArchVersion, ArmR9Usage, DiffObjConfig, display::InstructionPart},
obj::{
InstructionRef, RelocationFlags, ResolvedInstructionRef, ResolvedRelocation,
InstructionRef, Relocation, RelocationFlags, ResolvedInstructionRef, ResolvedRelocation,
ScannedInstruction, SymbolFlag, SymbolFlagSet, SymbolKind,
},
};
@@ -183,6 +183,7 @@ impl Arch for ArchArm {
address: u64,
code: &[u8],
section_index: usize,
_relocations: &[Relocation],
diff_config: &DiffObjConfig,
) -> Result<Vec<ScannedInstruction>> {
let start_addr = address as u32;
@@ -198,7 +199,7 @@ impl Arch for ArchArm {
.unwrap_or(&fallback_mappings);
let first_mapping_idx = mapping_symbols
.binary_search_by_key(&start_addr, |x| x.address)
.unwrap_or_else(|idx| idx - 1);
.unwrap_or_else(|idx| idx.saturating_sub(1));
let mut mode = mapping_symbols[first_mapping_idx].mapping;
let mut mappings_iter = mapping_symbols

View File

@@ -17,7 +17,7 @@ use crate::{
arch::Arch,
diff::{DiffObjConfig, display::InstructionPart},
obj::{
InstructionRef, RelocationFlags, ResolvedInstructionRef, ResolvedRelocation,
InstructionRef, Relocation, RelocationFlags, ResolvedInstructionRef, ResolvedRelocation,
ScannedInstruction,
},
};
@@ -35,6 +35,7 @@ impl Arch for ArchArm64 {
address: u64,
code: &[u8],
_section_index: usize,
_relocations: &[Relocation],
_diff_config: &DiffObjConfig,
) -> Result<Vec<ScannedInstruction>> {
let start_address = address;

View File

@@ -194,6 +194,7 @@ impl Arch for ArchMips {
address: u64,
code: &[u8],
_section_index: usize,
_relocations: &[Relocation],
diff_config: &DiffObjConfig,
) -> Result<Vec<ScannedInstruction>> {
let instruction_flags = self.instruction_flags(diff_config);

View File

@@ -170,6 +170,7 @@ pub trait Arch: Send + Sync + Debug {
address: u64,
code: &[u8],
section_index: usize,
relocations: &[Relocation],
diff_config: &DiffObjConfig,
) -> Result<Vec<ScannedInstruction>>;
@@ -301,6 +302,7 @@ impl Arch for ArchDummy {
_address: u64,
_code: &[u8],
_section_index: usize,
_relocations: &[Relocation],
_diff_config: &DiffObjConfig,
) -> Result<Vec<ScannedInstruction>> {
Ok(Vec::new())

View File

@@ -87,6 +87,7 @@ impl Arch for ArchPpc {
address: u64,
code: &[u8],
_section_index: usize,
_relocations: &[Relocation],
_diff_config: &DiffObjConfig,
) -> Result<Vec<ScannedInstruction>> {
ensure!(code.len() & 3 == 0, "Code length must be a multiple of 4");
@@ -512,6 +513,7 @@ fn guess_data_type_from_load_store_inst_op(inst_op: ppc750cl::Opcode) -> Option<
}
}
#[derive(Debug)]
struct PoolReference {
addr_src_gpr: ppc750cl::GPR,
addr_offset: i16,
@@ -644,7 +646,7 @@ fn make_fake_pool_reloc(
// example, dCcD_Cyl in The Wind Waker). So just showing that vtable symbol plus an addend
// to represent the offset into it works fine in this case.
target_symbol = pool_reloc.relocation.target_symbol;
addend = pool_reloc.relocation.addend + offset_from_pool;
addend = offset_from_pool;
}
Some(Relocation {
flags: RelocationFlags::Elf(elf::R_PPC_NONE),

View File

@@ -1,6 +1,7 @@
use alloc::{boxed::Box, string::String, vec::Vec};
use alloc::{boxed::Box, format, string::String, vec::Vec};
use core::cmp::Ordering;
use anyhow::{Result, anyhow, bail};
use anyhow::{Context, Result, anyhow, bail};
use iced_x86::{
Decoder, DecoderOptions, DecoratorKind, FormatterOutput, FormatterTextKind, GasFormatter,
Instruction, IntelFormatter, MasmFormatter, NasmFormatter, NumberKind, OpKind, Register,
@@ -10,7 +11,9 @@ use object::{Endian as _, Object as _, ObjectSection as _, pe};
use crate::{
arch::Arch,
diff::{DiffObjConfig, X86Formatter, display::InstructionPart},
obj::{InstructionRef, RelocationFlags, ResolvedInstructionRef, ScannedInstruction},
obj::{
InstructionRef, Relocation, RelocationFlags, ResolvedInstructionRef, ScannedInstruction,
},
};
#[derive(Debug)]
@@ -80,18 +83,50 @@ impl ArchX86 {
}
}
const DATA_OPCODE: u16 = u16::MAX - 1;
impl Arch for ArchX86 {
fn scan_instructions(
&self,
address: u64,
code: &[u8],
_section_index: usize,
relocations: &[Relocation],
_diff_config: &DiffObjConfig,
) -> Result<Vec<ScannedInstruction>> {
let mut out = Vec::with_capacity(code.len() / 2);
let mut decoder = self.decoder(code, address);
let mut instruction = Instruction::default();
while decoder.can_decode() {
let mut reloc_iter = 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 size = self.reloc_size(reloc.flags).with_context(|| {
format!("Unsupported inline x86 relocation {:?}", reloc.flags)
})?;
if decoder.set_position(decoder.position() + size).is_ok() {
decoder.set_ip(address + size as u64);
out.push(ScannedInstruction {
ins_ref: InstructionRef {
address,
size: size as u8,
opcode: DATA_OPCODE,
},
branch_dest: None,
});
reloc_iter.next();
continue 'outer;
}
}
Ordering::Greater => break,
}
}
decoder.decode_out(&mut instruction);
let branch_dest = match instruction.op0_kind() {
OpKind::NearBranch16 => Some(instruction.near_branch16() as u64),
@@ -101,7 +136,7 @@ impl Arch for ArchX86 {
};
out.push(ScannedInstruction {
ins_ref: InstructionRef {
address: instruction.ip(),
address,
size: instruction.len() as u8,
opcode: instruction.mnemonic() as u16,
},
@@ -117,6 +152,21 @@ impl Arch for ArchX86 {
diff_config: &DiffObjConfig,
cb: &mut dyn FnMut(InstructionPart) -> Result<()>,
) -> Result<()> {
if resolved.ins_ref.opcode == DATA_OPCODE {
let (mnemonic, imm) = match resolved.ins_ref.size {
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),
};
cb(InstructionPart::opcode(mnemonic, DATA_OPCODE))?;
if resolved.relocation.is_some() {
cb(InstructionPart::reloc())?;
} else {
cb(InstructionPart::unsigned(imm))?;
}
return Ok(());
}
let mut decoder = self.decoder(resolved.code, resolved.ins_ref.address);
let mut formatter = self.formatter(diff_config);
let mut instruction = Instruction::default();
@@ -329,6 +379,7 @@ impl FormatterOutput for InstructionFormatterOutput<'_> {
(NumberKind::Int8 | NumberKind::UInt8, 1)
| (NumberKind::Int16 | NumberKind::UInt16, 2)
| (NumberKind::Int32 | NumberKind::UInt32, 4)
| (NumberKind::Int64 | NumberKind::UInt64, 4) // x86_64
| (NumberKind::Int64 | NumberKind::UInt64, 8) => true,
_ => false,
}
@@ -406,7 +457,7 @@ mod test {
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();
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);
@@ -611,4 +662,74 @@ mod test {
InstructionPart::basic("]"),
]);
}
#[test]
fn test_process_x86_64_instruction_with_reloc_1() {
let arch = ArchX86 { arch: Architecture::X86_64, endianness: object::Endianness::Little };
let code = [0x48, 0x8b, 0x05, 0x00, 0x00, 0x00, 0x00];
let opcode = iced_x86::Mnemonic::Mov as u16;
let mut parts = Vec::new();
arch.display_instruction(
ResolvedInstructionRef {
ins_ref: InstructionRef { address: 0x1234, size: 7, opcode },
code: &code,
relocation: Some(ResolvedRelocation {
relocation: &Relocation {
flags: RelocationFlags::Coff(pe::IMAGE_REL_AMD64_REL32),
address: 0x1234 + 3,
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("rax"),
InstructionPart::basic(","),
InstructionPart::basic(" "),
InstructionPart::basic("["),
InstructionPart::reloc(),
InstructionPart::basic("]"),
]);
}
#[test]
fn test_process_x86_64_instruction_with_reloc_2() {
let arch = ArchX86 { arch: Architecture::X86_64, 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(
ResolvedInstructionRef {
ins_ref: InstructionRef { address: 0x1234, size: 5, opcode },
code: &code,
relocation: Some(ResolvedRelocation {
relocation: &Relocation {
flags: RelocationFlags::Coff(pe::IMAGE_REL_AMD64_REL32),
address: 0x1234 + 1,
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("call", opcode), InstructionPart::reloc()]);
}
}

View File

@@ -32,7 +32,13 @@ pub fn no_diff_code(
symbol.address + symbol.size
)
})?;
let ops = obj.arch.scan_instructions(symbol.address, data, section_index, diff_config)?;
let ops = obj.arch.scan_instructions(
symbol.address,
data,
section_index,
&section.relocations,
diff_config,
)?;
let mut instruction_rows = Vec::<InstructionDiffRow>::new();
for i in &ops {
instruction_rows
@@ -89,12 +95,14 @@ pub fn diff_code(
left_symbol.address,
left_data,
left_section_idx,
&left_section.relocations,
diff_config,
)?;
let right_ops = right_obj.arch.scan_instructions(
right_symbol.address,
right_data,
right_section_idx,
&right_section.relocations,
diff_config,
)?;
let (mut left_rows, mut right_rows) = diff_instructions(&left_ops, &right_ops)?;

View File

@@ -325,10 +325,14 @@ pub enum SymbolNavigationKind {
Extab,
}
#[derive(Debug, Clone, Default, Eq, PartialEq)]
pub enum HoverItemColor {
#[default]
Normal, // Gray
Emphasized, // White
Special, // Blue
Delete, // Red
Insert, // Green
}
pub enum HoverItem {
@@ -355,7 +359,12 @@ pub fn symbol_context(obj: &Object, symbol_index: usize) -> Vec<ContextItem> {
out
}
pub fn symbol_hover(obj: &Object, symbol_index: usize, addend: i64) -> Vec<HoverItem> {
pub fn symbol_hover(
obj: &Object,
symbol_index: usize,
addend: i64,
override_color: Option<HoverItemColor>,
) -> Vec<HoverItem> {
let symbol = &obj.symbols[symbol_index];
let addend_str = match addend.cmp(&0i64) {
Ordering::Greater => format!("+{:x}", addend),
@@ -366,51 +375,51 @@ pub fn symbol_hover(obj: &Object, symbol_index: usize, addend: i64) -> Vec<Hover
out.push(HoverItem::Text {
label: "Name".into(),
value: format!("{}{}", symbol.name, addend_str),
color: HoverItemColor::Normal,
color: override_color.clone().unwrap_or_default(),
});
if let Some(demangled_name) = &symbol.demangled_name {
out.push(HoverItem::Text {
label: "Demangled".into(),
value: demangled_name.into(),
color: HoverItemColor::Normal,
color: override_color.clone().unwrap_or_default(),
});
}
if let Some(section) = symbol.section {
out.push(HoverItem::Text {
label: "Section".into(),
value: obj.sections[section].name.clone(),
color: HoverItemColor::Normal,
color: override_color.clone().unwrap_or_default(),
});
out.push(HoverItem::Text {
label: "Address".into(),
value: format!("{:x}{}", symbol.address, addend_str),
color: HoverItemColor::Normal,
color: override_color.clone().unwrap_or_default(),
});
if symbol.flags.contains(SymbolFlag::SizeInferred) {
out.push(HoverItem::Text {
label: "Size".into(),
value: format!("{:x} (inferred)", symbol.size),
color: HoverItemColor::Normal,
color: override_color.clone().unwrap_or_default(),
});
} else {
out.push(HoverItem::Text {
label: "Size".into(),
value: format!("{:x}", symbol.size),
color: HoverItemColor::Normal,
color: override_color.clone().unwrap_or_default(),
});
}
if let Some(align) = symbol.align {
out.push(HoverItem::Text {
label: "Alignment".into(),
value: align.get().to_string(),
color: HoverItemColor::Normal,
color: override_color.clone().unwrap_or_default(),
});
}
if let Some(address) = symbol.virtual_address {
out.push(HoverItem::Text {
label: "Virtual address".into(),
value: format!("{:x}", address),
color: HoverItemColor::Special,
color: override_color.clone().unwrap_or(HoverItemColor::Special),
});
}
} else {
@@ -443,22 +452,31 @@ pub fn relocation_context(
out
}
pub fn relocation_hover(obj: &Object, reloc: ResolvedRelocation) -> Vec<HoverItem> {
pub fn relocation_hover(
obj: &Object,
reloc: ResolvedRelocation,
override_color: Option<HoverItemColor>,
) -> Vec<HoverItem> {
let mut out = Vec::new();
if let Some(name) = obj.arch.reloc_name(reloc.relocation.flags) {
out.push(HoverItem::Text {
label: "Relocation".into(),
value: name.to_string(),
color: HoverItemColor::Normal,
color: override_color.clone().unwrap_or_default(),
});
} else {
out.push(HoverItem::Text {
label: "Relocation".into(),
value: format!("<{:?}>", reloc.relocation.flags),
color: HoverItemColor::Normal,
color: override_color.clone().unwrap_or_default(),
});
}
out.append(&mut symbol_hover(obj, reloc.relocation.target_symbol, reloc.relocation.addend));
out.append(&mut symbol_hover(
obj,
reloc.relocation.target_symbol,
reloc.relocation.addend,
override_color,
));
out
}
@@ -545,7 +563,7 @@ pub fn instruction_hover(
}
if let Some(reloc) = resolved.relocation {
out.push(HoverItem::Separator);
out.append(&mut relocation_hover(obj, reloc));
out.append(&mut relocation_hover(obj, reloc, None));
if let Some(ty) = obj.arch.guess_data_type(resolved) {
let literals = display_ins_data_literals(obj, resolved);
if !literals.is_empty() {

View File

@@ -55,3 +55,16 @@ fn display_section_ordering() {
diff::display::display_sections(&obj, &obj_diff, SymbolFilter::None, false, false, false);
insta::assert_debug_snapshot!(section_display);
}
#[test]
#[cfg(feature = "x86")]
fn read_x86_jumptable() {
let diff_config = diff::DiffObjConfig::default();
let obj = obj::read::parse(include_object!("data/x86/jumptable.o"), &diff_config).unwrap();
insta::assert_debug_snapshot!(obj);
let symbol_idx = obj.symbols.iter().position(|s| s.name == "?test@@YAHH@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.

View File

@@ -0,0 +1,569 @@
---
source: objdiff-core/tests/arch_x86.rs
expression: diff.instruction_rows
---
[
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 0,
size: 4,
opcode: 414,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 4,
size: 1,
opcode: 137,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 5,
size: 3,
opcode: 93,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 8,
size: 2,
opcode: 297,
},
),
kind: None,
branch_from: None,
branch_to: Some(
InstructionBranchTo {
ins_idx: 18,
branch_idx: 0,
},
),
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 10,
size: 7,
opcode: 308,
},
),
kind: None,
branch_from: None,
branch_to: Some(
InstructionBranchTo {
ins_idx: 20,
branch_idx: 1,
},
),
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 17,
size: 5,
opcode: 414,
},
),
kind: None,
branch_from: Some(
InstructionBranchFrom {
ins_idx: [
20,
],
branch_idx: 2,
},
),
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 22,
size: 1,
opcode: 662,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 23,
size: 5,
opcode: 414,
},
),
kind: None,
branch_from: Some(
InstructionBranchFrom {
ins_idx: [
21,
],
branch_idx: 3,
},
),
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 28,
size: 1,
opcode: 662,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 29,
size: 5,
opcode: 414,
},
),
kind: None,
branch_from: Some(
InstructionBranchFrom {
ins_idx: [
22,
],
branch_idx: 4,
},
),
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 34,
size: 1,
opcode: 662,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 35,
size: 5,
opcode: 414,
},
),
kind: None,
branch_from: Some(
InstructionBranchFrom {
ins_idx: [
23,
],
branch_idx: 5,
},
),
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 40,
size: 1,
opcode: 662,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 41,
size: 5,
opcode: 414,
},
),
kind: None,
branch_from: Some(
InstructionBranchFrom {
ins_idx: [
24,
],
branch_idx: 6,
},
),
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 46,
size: 1,
opcode: 662,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 47,
size: 5,
opcode: 414,
},
),
kind: None,
branch_from: Some(
InstructionBranchFrom {
ins_idx: [
25,
],
branch_idx: 7,
},
),
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 52,
size: 1,
opcode: 662,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 53,
size: 5,
opcode: 414,
},
),
kind: None,
branch_from: Some(
InstructionBranchFrom {
ins_idx: [
26,
],
branch_idx: 8,
},
),
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 58,
size: 1,
opcode: 662,
},
),
kind: None,
branch_from: Some(
InstructionBranchFrom {
ins_idx: [
3,
],
branch_idx: 0,
},
),
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 59,
size: 1,
opcode: 465,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 60,
size: 4,
opcode: 65534,
},
),
kind: None,
branch_from: Some(
InstructionBranchFrom {
ins_idx: [
4,
],
branch_idx: 1,
},
),
branch_to: Some(
InstructionBranchTo {
ins_idx: 5,
branch_idx: 2,
},
),
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 64,
size: 4,
opcode: 65534,
},
),
kind: None,
branch_from: None,
branch_to: Some(
InstructionBranchTo {
ins_idx: 7,
branch_idx: 3,
},
),
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 68,
size: 4,
opcode: 65534,
},
),
kind: None,
branch_from: None,
branch_to: Some(
InstructionBranchTo {
ins_idx: 9,
branch_idx: 4,
},
),
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 72,
size: 4,
opcode: 65534,
},
),
kind: None,
branch_from: None,
branch_to: Some(
InstructionBranchTo {
ins_idx: 11,
branch_idx: 5,
},
),
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 76,
size: 4,
opcode: 65534,
},
),
kind: None,
branch_from: None,
branch_to: Some(
InstructionBranchTo {
ins_idx: 13,
branch_idx: 6,
},
),
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 80,
size: 4,
opcode: 65534,
},
),
kind: None,
branch_from: None,
branch_to: Some(
InstructionBranchTo {
ins_idx: 15,
branch_idx: 7,
},
),
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 84,
size: 4,
opcode: 65534,
},
),
kind: None,
branch_from: None,
branch_to: Some(
InstructionBranchTo {
ins_idx: 17,
branch_idx: 8,
},
),
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 88,
size: 1,
opcode: 465,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 89,
size: 1,
opcode: 465,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 90,
size: 1,
opcode: 465,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 91,
size: 1,
opcode: 465,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 92,
size: 1,
opcode: 465,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 93,
size: 1,
opcode: 465,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 94,
size: 1,
opcode: 465,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 95,
size: 1,
opcode: 465,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
]

View File

@@ -0,0 +1,39 @@
---
source: objdiff-core/tests/arch_x86.rs
expression: output
---
[(Address(0), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mov", 414), Normal, 10), (Argument(Opaque("eax")), Normal, 0), (Basic(","), Normal, 0), (Spacing(1), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("esp")), Normal, 0), (Argument(Opaque("+")), Normal, 0), (Argument(Signed(4)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
[(Address(4), Normal, 5), (Spacing(4), Normal, 0), (Opcode("dec", 137), Normal, 10), (Argument(Opaque("eax")), Normal, 0), (Eol, Normal, 0)]
[(Address(5), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmp", 93), Normal, 10), (Argument(Opaque("eax")), Normal, 0), (Basic(","), Normal, 0), (Spacing(1), Normal, 0), (Argument(Unsigned(6)), Normal, 0), (Eol, Normal, 0)]
[(Address(8), Normal, 5), (Spacing(4), Normal, 0), (Opcode("ja", 297), Normal, 10), (Argument(Opaque("short")), Normal, 0), (Spacing(1), Normal, 0), (BranchDest(58), Normal, 0), (Basic(" ~>"), Rotating(0), 0), (Eol, Normal, 0)]
[(Address(10), Normal, 5), (Spacing(4), Normal, 0), (Opcode("jmp", 308), Normal, 10), (Argument(Opaque("dword")), Normal, 0), (Spacing(1), Normal, 0), (Argument(Opaque("ptr")), Normal, 0), (Spacing(1), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("eax")), Normal, 0), (Argument(Opaque("*")), Normal, 0), (Argument(Signed(4)), Normal, 0), (Argument(Opaque("+")), Normal, 0), (Symbol(Symbol { name: "$L282", demangled_name: None, address: 60, size: 0, kind: Unknown, section: Some(1), flags: FlagSet(Local), align: None, virtual_address: None }), Bright, 0), (Basic("]"), Normal, 0), (Basic(" ~>"), Rotating(1), 0), (Eol, Normal, 0)]
[(Address(17), Normal, 5), (Basic(" ~> "), Rotating(2), 0), (Opcode("mov", 414), Normal, 10), (Argument(Opaque("eax")), Normal, 0), (Basic(","), Normal, 0), (Spacing(1), Normal, 0), (Argument(Unsigned(8)), Normal, 0), (Eol, Normal, 0)]
[(Address(22), Normal, 5), (Spacing(4), Normal, 0), (Opcode("ret", 662), Normal, 10), (Eol, Normal, 0)]
[(Address(23), Normal, 5), (Basic(" ~> "), Rotating(3), 0), (Opcode("mov", 414), Normal, 10), (Argument(Opaque("eax")), Normal, 0), (Basic(","), Normal, 0), (Spacing(1), Normal, 0), (Argument(Unsigned(7)), Normal, 0), (Eol, Normal, 0)]
[(Address(28), Normal, 5), (Spacing(4), Normal, 0), (Opcode("ret", 662), Normal, 10), (Eol, Normal, 0)]
[(Address(29), Normal, 5), (Basic(" ~> "), Rotating(4), 0), (Opcode("mov", 414), Normal, 10), (Argument(Opaque("eax")), Normal, 0), (Basic(","), Normal, 0), (Spacing(1), Normal, 0), (Argument(Unsigned(6)), Normal, 0), (Eol, Normal, 0)]
[(Address(34), Normal, 5), (Spacing(4), Normal, 0), (Opcode("ret", 662), Normal, 10), (Eol, Normal, 0)]
[(Address(35), Normal, 5), (Basic(" ~> "), Rotating(5), 0), (Opcode("mov", 414), Normal, 10), (Argument(Opaque("eax")), Normal, 0), (Basic(","), Normal, 0), (Spacing(1), Normal, 0), (Argument(Unsigned(5)), Normal, 0), (Eol, Normal, 0)]
[(Address(40), Normal, 5), (Spacing(4), Normal, 0), (Opcode("ret", 662), Normal, 10), (Eol, Normal, 0)]
[(Address(41), Normal, 5), (Basic(" ~> "), Rotating(6), 0), (Opcode("mov", 414), Normal, 10), (Argument(Opaque("eax")), Normal, 0), (Basic(","), Normal, 0), (Spacing(1), Normal, 0), (Argument(Unsigned(4)), Normal, 0), (Eol, Normal, 0)]
[(Address(46), Normal, 5), (Spacing(4), Normal, 0), (Opcode("ret", 662), Normal, 10), (Eol, Normal, 0)]
[(Address(47), Normal, 5), (Basic(" ~> "), Rotating(7), 0), (Opcode("mov", 414), Normal, 10), (Argument(Opaque("eax")), Normal, 0), (Basic(","), Normal, 0), (Spacing(1), Normal, 0), (Argument(Unsigned(3)), Normal, 0), (Eol, Normal, 0)]
[(Address(52), Normal, 5), (Spacing(4), Normal, 0), (Opcode("ret", 662), Normal, 10), (Eol, Normal, 0)]
[(Address(53), Normal, 5), (Basic(" ~> "), Rotating(8), 0), (Opcode("mov", 414), Normal, 10), (Argument(Opaque("eax")), Normal, 0), (Basic(","), Normal, 0), (Spacing(1), Normal, 0), (Argument(Unsigned(2)), Normal, 0), (Eol, Normal, 0)]
[(Address(58), Normal, 5), (Basic(" ~> "), Rotating(0), 0), (Opcode("ret", 662), Normal, 10), (Eol, Normal, 0)]
[(Address(59), Normal, 5), (Spacing(4), Normal, 0), (Opcode("nop", 465), Normal, 10), (Eol, Normal, 0)]
[(Address(60), Normal, 5), (Basic(" ~> "), Rotating(1), 0), (Opcode(".dword", 65534), Normal, 10), (Symbol(Symbol { name: "$L272", demangled_name: None, address: 17, size: 0, kind: Unknown, section: Some(1), flags: FlagSet(Local), align: None, virtual_address: None }), Bright, 0), (Basic(" ~>"), Rotating(2), 0), (Eol, Normal, 0)]
[(Address(64), Normal, 5), (Spacing(4), Normal, 0), (Opcode(".dword", 65534), Normal, 10), (Symbol(Symbol { name: "$L273", demangled_name: None, address: 23, size: 0, kind: Unknown, section: Some(1), flags: FlagSet(Local), align: None, virtual_address: None }), Bright, 0), (Basic(" ~>"), Rotating(3), 0), (Eol, Normal, 0)]
[(Address(68), Normal, 5), (Spacing(4), Normal, 0), (Opcode(".dword", 65534), Normal, 10), (Symbol(Symbol { name: "$L274", demangled_name: None, address: 29, size: 0, kind: Unknown, section: Some(1), flags: FlagSet(Local), align: None, virtual_address: None }), Bright, 0), (Basic(" ~>"), Rotating(4), 0), (Eol, Normal, 0)]
[(Address(72), Normal, 5), (Spacing(4), Normal, 0), (Opcode(".dword", 65534), Normal, 10), (Symbol(Symbol { name: "$L275", demangled_name: None, address: 35, size: 0, kind: Unknown, section: Some(1), flags: FlagSet(Local), align: None, virtual_address: None }), Bright, 0), (Basic(" ~>"), Rotating(5), 0), (Eol, Normal, 0)]
[(Address(76), Normal, 5), (Spacing(4), Normal, 0), (Opcode(".dword", 65534), Normal, 10), (Symbol(Symbol { name: "$L276", demangled_name: None, address: 41, size: 0, kind: Unknown, section: Some(1), flags: FlagSet(Local), align: None, virtual_address: None }), Bright, 0), (Basic(" ~>"), Rotating(6), 0), (Eol, Normal, 0)]
[(Address(80), Normal, 5), (Spacing(4), Normal, 0), (Opcode(".dword", 65534), Normal, 10), (Symbol(Symbol { name: "$L277", demangled_name: None, address: 47, size: 0, kind: Unknown, section: Some(1), flags: FlagSet(Local), align: None, virtual_address: None }), Bright, 0), (Basic(" ~>"), Rotating(7), 0), (Eol, Normal, 0)]
[(Address(84), Normal, 5), (Spacing(4), Normal, 0), (Opcode(".dword", 65534), Normal, 10), (Symbol(Symbol { name: "$L278", demangled_name: None, address: 53, size: 0, kind: Unknown, section: Some(1), flags: FlagSet(Local), align: None, virtual_address: None }), Bright, 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)]

View File

@@ -0,0 +1,305 @@
---
source: objdiff-core/tests/arch_x86.rs
expression: obj
---
Object {
arch: ArchX86 {
arch: X86,
endianness: Little,
},
endianness: Little,
symbols: [
Symbol {
name: "Z:/tmp/code.c",
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: "[.text]",
demangled_name: None,
address: 0,
size: 0,
kind: Section,
section: Some(
1,
),
flags: FlagSet(Local),
align: None,
virtual_address: None,
},
Symbol {
name: "?test@@YAHH@Z",
demangled_name: Some(
"int __cdecl test(int)",
),
address: 0,
size: 96,
kind: Function,
section: Some(
1,
),
flags: FlagSet(Global | SizeInferred),
align: None,
virtual_address: None,
},
Symbol {
name: "$L278",
demangled_name: None,
address: 53,
size: 0,
kind: Unknown,
section: Some(
1,
),
flags: FlagSet(Local),
align: None,
virtual_address: None,
},
Symbol {
name: "$L277",
demangled_name: None,
address: 47,
size: 0,
kind: Unknown,
section: Some(
1,
),
flags: FlagSet(Local),
align: None,
virtual_address: None,
},
Symbol {
name: "$L276",
demangled_name: None,
address: 41,
size: 0,
kind: Unknown,
section: Some(
1,
),
flags: FlagSet(Local),
align: None,
virtual_address: None,
},
Symbol {
name: "$L275",
demangled_name: None,
address: 35,
size: 0,
kind: Unknown,
section: Some(
1,
),
flags: FlagSet(Local),
align: None,
virtual_address: None,
},
Symbol {
name: "$L274",
demangled_name: None,
address: 29,
size: 0,
kind: Unknown,
section: Some(
1,
),
flags: FlagSet(Local),
align: None,
virtual_address: None,
},
Symbol {
name: "$L273",
demangled_name: None,
address: 23,
size: 0,
kind: Unknown,
section: Some(
1,
),
flags: FlagSet(Local),
align: None,
virtual_address: None,
},
Symbol {
name: "$L272",
demangled_name: None,
address: 17,
size: 0,
kind: Unknown,
section: Some(
1,
),
flags: FlagSet(Local),
align: None,
virtual_address: None,
},
Symbol {
name: "$L282",
demangled_name: None,
address: 60,
size: 0,
kind: Unknown,
section: Some(
1,
),
flags: FlagSet(Local),
align: None,
virtual_address: None,
},
Symbol {
name: "[.debug$F]",
demangled_name: None,
address: 0,
size: 16,
kind: Section,
section: Some(
2,
),
flags: FlagSet(Local),
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: ".text-0",
name: ".text",
address: 0,
size: 96,
kind: Code,
data: SectionData(
96,
),
flags: FlagSet(),
relocations: [
Relocation {
flags: Coff(
6,
),
address: 13,
target_symbol: 12,
addend: 0,
},
Relocation {
flags: Coff(
6,
),
address: 60,
target_symbol: 11,
addend: 0,
},
Relocation {
flags: Coff(
6,
),
address: 64,
target_symbol: 10,
addend: 0,
},
Relocation {
flags: Coff(
6,
),
address: 68,
target_symbol: 9,
addend: 0,
},
Relocation {
flags: Coff(
6,
),
address: 72,
target_symbol: 8,
addend: 0,
},
Relocation {
flags: Coff(
6,
),
address: 76,
target_symbol: 7,
addend: 0,
},
Relocation {
flags: Coff(
6,
),
address: 80,
target_symbol: 6,
addend: 0,
},
Relocation {
flags: Coff(
6,
),
address: 84,
target_symbol: 5,
addend: 0,
},
],
line_info: {},
virtual_address: None,
},
Section {
id: ".debug$F-0",
name: ".debug$F",
address: 0,
size: 16,
kind: Unknown,
data: SectionData(
0,
),
flags: FlagSet(),
relocations: [],
line_info: {},
virtual_address: None,
},
],
split_meta: None,
path: None,
timestamp: None,
}

View File

@@ -775,10 +775,8 @@ impl eframe::App for App {
if side_panel_available {
egui::SidePanel::left("side_panel").show_animated(ctx, *show_side_panel, |ui| {
egui::ScrollArea::both().show(ui, |ui| {
config_ui(ui, state, show_project_config, config_state, appearance);
});
});
}
let mut action = None;

View File

@@ -297,6 +297,7 @@ pub fn config_ui(
node_open = NodeOpen::Open;
}
egui::ScrollArea::both().auto_shrink(false).show(ui, |ui| {
CollapsingHeader::new(RichText::new("🗀 Objects").font(FontId {
size: appearance.ui_font.size,
family: appearance.code_font.family.clone(),
@@ -316,9 +317,17 @@ pub fn config_ui(
config_state.show_hidden,
)
}) {
display_node(ui, &mut new_selected_index, objects, &node, appearance, node_open);
display_node(
ui,
&mut new_selected_index,
objects,
&node,
appearance,
node_open,
);
}
});
});
}
if new_selected_index != selected_index {
if let Some(idx) = new_selected_index {
@@ -327,11 +336,6 @@ pub fn config_ui(
state_guard.set_selected_obj(config);
}
}
if state_guard.config.selected_obj.is_some()
&& ui.add_enabled(!config_state.build_running, egui::Button::new("Build")).clicked()
{
config_state.queue_build = true;
}
}
fn display_unit(

View File

@@ -5,7 +5,7 @@ use objdiff_core::{
diff::{
DataDiff, DataDiffKind, DataRelocationDiff,
data::resolve_relocation,
display::{ContextItem, HoverItem, relocation_context, relocation_hover},
display::{ContextItem, HoverItem, HoverItemColor, relocation_context, relocation_hover},
},
obj::Object,
};
@@ -19,6 +19,7 @@ fn data_row_hover(obj: &Object, diffs: &[(DataDiff, Vec<DataRelocationDiff>)]) -
let mut out = Vec::new();
let reloc_diffs = diffs.iter().flat_map(|(_, reloc_diffs)| reloc_diffs);
let mut prev_reloc = None;
let mut first = true;
for reloc_diff in reloc_diffs {
let reloc = &reloc_diff.reloc;
if prev_reloc == Some(reloc) {
@@ -29,11 +30,16 @@ fn data_row_hover(obj: &Object, diffs: &[(DataDiff, Vec<DataRelocationDiff>)]) -
}
prev_reloc = Some(reloc);
// TODO: Change hover text color depending on Insert/Delete/Replace kind
// let color = get_color_for_diff_kind(reloc_diff.kind, appearance);
if first {
first = false;
} else {
out.push(HoverItem::Separator);
}
let color = get_hover_item_color_for_diff_kind(reloc_diff.kind);
let reloc = resolve_relocation(&obj.symbols, reloc);
out.append(&mut relocation_hover(obj, reloc));
out.append(&mut relocation_hover(obj, reloc, Some(color)));
}
out
}
@@ -97,6 +103,15 @@ fn get_color_for_diff_kind(diff_kind: DataDiffKind, appearance: &Appearance) ->
}
}
fn get_hover_item_color_for_diff_kind(diff_kind: DataDiffKind) -> HoverItemColor {
match diff_kind {
DataDiffKind::None => HoverItemColor::Normal,
DataDiffKind::Replace => HoverItemColor::Special,
DataDiffKind::Delete => HoverItemColor::Delete,
DataDiffKind::Insert => HoverItemColor::Insert,
}
}
pub(crate) fn data_row_ui(
ui: &mut egui::Ui,
obj: Option<&Object>,

View File

@@ -795,6 +795,8 @@ pub fn hover_items_ui(ui: &mut Ui, items: Vec<HoverItem>, appearance: &Appearanc
if !label.is_empty() {
let label_color = match color {
HoverItemColor::Special => appearance.replace_color,
HoverItemColor::Delete => appearance.delete_color,
HoverItemColor::Insert => appearance.insert_color,
_ => appearance.highlight_color,
};
write_text(&label, label_color, &mut job, appearance.code_font.clone());

View File

@@ -512,7 +512,7 @@ pub fn symbol_hover_ui(
ui.scope(|ui| {
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Wrap);
hover_items_ui(ui, symbol_hover(ctx.obj, symbol_idx, 0), appearance);
hover_items_ui(ui, symbol_hover(ctx.obj, symbol_idx, 0, None), appearance);
});
}

View File

@@ -25,6 +25,7 @@ std = ["objdiff-core/std"]
[dependencies]
log = { version = "0.4", default-features = false }
regex = { version = "1.11", default-features = false, features = ["unicode-case"] }
xxhash-rust = { version = "0.8", default-features = false, features = ["xxh3"] }
[dependencies.objdiff-core]
path = "../objdiff-core"

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
use alloc::{
format,
rc::Rc,
rc::{Rc, Weak},
str::FromStr,
string::{String, ToString},
vec::Vec,
@@ -9,6 +9,7 @@ use core::cell::RefCell;
use objdiff_core::{diff, obj};
use regex::{Regex, RegexBuilder};
use xxhash_rust::xxh3::xxh3_64;
use super::logging;
@@ -41,8 +42,7 @@ impl Guest for Component {
fn version() -> String { env!("CARGO_PKG_VERSION").to_string() }
}
#[repr(transparent)]
struct ResourceObject(Rc<obj::Object>);
struct ResourceObject(Rc<obj::Object>, u64);
struct ResourceObjectDiff(Rc<obj::Object>, diff::ObjectDiff);
@@ -230,7 +230,9 @@ impl GuestDisplay for Component {
) -> Vec<HoverItem> {
let obj_diff = diff.get::<ResourceObjectDiff>();
let obj = obj_diff.0.as_ref();
diff::display::symbol_hover(obj, symbol_display.symbol as usize, 0 /* TODO */)
// TODO: colorize replaced/deleted/inserted relocations
let override_color = None;
diff::display::symbol_hover(obj, symbol_display.symbol as usize, 0, override_color)
.into_iter()
.map(|item| HoverItem::from(item))
.collect()
@@ -421,13 +423,49 @@ impl GuestDiffConfig for ResourceDiffConfig {
}
}
struct CachedObject(Weak<obj::Object>, u64);
struct ObjectCache(RefCell<Vec<CachedObject>>);
impl ObjectCache {
#[inline]
const fn new() -> Self { Self(RefCell::new(Vec::new())) }
}
impl core::ops::Deref for ObjectCache {
type Target = RefCell<Vec<CachedObject>>;
fn deref(&self) -> &Self::Target { &self.0 }
}
// Assume single-threaded environment
unsafe impl Sync for ObjectCache {}
static OBJECT_CACHE: ObjectCache = ObjectCache::new();
impl GuestObject for ResourceObject {
fn parse(data: Vec<u8>, diff_config: DiffConfigBorrow) -> Result<Object, String> {
let diff_config = diff_config.get::<ResourceDiffConfig>().0.borrow();
obj::read::parse(&data, &diff_config)
.map(|o| Object::new(ResourceObject(Rc::new(o))))
.map_err(|e| e.to_string())
let hash = xxh3_64(&data);
let mut cached = None;
OBJECT_CACHE.borrow_mut().retain(|c| {
if c.0.strong_count() == 0 {
return false;
}
if c.1 == hash {
cached = c.0.upgrade();
}
true
});
if let Some(obj) = cached {
return Ok(Object::new(ResourceObject(obj, hash)));
}
let diff_config = diff_config.get::<ResourceDiffConfig>().0.borrow();
let obj = Rc::new(obj::read::parse(&data, &diff_config).map_err(|e| e.to_string())?);
OBJECT_CACHE.borrow_mut().push(CachedObject(Rc::downgrade(&obj), hash));
Ok(Object::new(ResourceObject(obj, hash)))
}
fn hash(&self) -> u64 { self.1 }
}
impl GuestObjectDiff for ResourceObjectDiff {
@@ -465,6 +503,8 @@ impl From<diff::display::HoverItemColor> for HoverItemColor {
diff::display::HoverItemColor::Normal => HoverItemColor::Normal,
diff::display::HoverItemColor::Emphasized => HoverItemColor::Emphasized,
diff::display::HoverItemColor::Special => HoverItemColor::Special,
diff::display::HoverItemColor::Delete => HoverItemColor::Delete,
diff::display::HoverItemColor::Insert => HoverItemColor::Insert,
}
}
}

View File

@@ -20,6 +20,8 @@ interface diff {
data: list<u8>,
config: borrow<diff-config>,
) -> result<object, string>;
hash: func() -> u64;
}
resource object-diff {
@@ -135,6 +137,8 @@ interface display {
normal,
emphasized,
special,
delete,
insert,
}
record hover-item-text {