Compare commits

...

27 Commits

Author SHA1 Message Date
bd3ed0d5ad Version v3.0.0-beta.10 2025-06-17 13:03:14 -06:00
Mark Langen
e638d0b17a Implementation of basic data flow analysis for PowerPC (#212)
* WIP implementation

* * Move flow analysis to dedicated file
* Show string constants inline
* Handle calls to MWCC "sled" helpers which otherwise disrupt flow analysis

* Run cargo insta review

* Apply clippy feedback

* Update more tests.

* Remove std use from ppc flow analysis

* Try to make wasm build work again

* More test changes

* Probably last wasm fix

* Formatting

* Fix WASM

* One more clippy thing

* Fixed display of float constants in a LFS or LFD instruction in case where there is a branch to the subsequent instruction with a different register value.

* On lines with a reloc, only hide Symbol type data flow values rather than all data flow values.

* Formatting
2025-06-17 12:59:04 -06:00
Ryan Burns
f58616b6dd Use symbol name when comparing against an externed reloc (#214)
* Use symbol name when comparing against an externed reloc

For partial matching files, often a symbol is externed even though it
should exist in the target object. We can still compare the symbol name,
instead of always returning a mismatch.

* Combine cases, and apply change reloc_eq in code.rs
2025-05-30 15:00:02 -06:00
Alex Page
e9762e24c2 Add support for x86 ELF object files (#213) 2025-05-30 13:19:06 -06:00
dab79d96a1 Version v3.0.0-beta.9 2025-05-27 21:32:57 -06:00
a57e5db983 WASM API updates, support symbol mapping 2025-05-27 21:31:29 -06:00
LagoLunatic
d0afd3b83e Fix scroll hotkeys not working in data diff view (#208) 2025-05-27 09:27:00 -06:00
Anghelo Carvajal
a367af612b Make encoding_rs an optional dependency (#205) 2025-05-17 23:14:15 -06:00
LagoLunatic
22052ea10b Data diff view: Show bytes with relocations as ?? instead of 00 (#204)
* Data diff view: Show bytes with relocations as `xx`

* xx -> ??
2025-05-14 21:12:59 -06:00
f7c3501eae Version v3.0.0-beta.8 2025-05-13 23:15:46 -06:00
07ef93f16a Ignore extern symbols with symbol name lookups
When searching for a symbol by name, only look at
symbols that are defined within the object,
ignoring extern symbols (symbols without section).

Fixes #180
Fixes #181
2025-05-13 22:51:26 -06:00
8e8ab6bef8 Skip label symbols when inferring symbol sizes
COFF objects in particular don't contain the size of
symbols. We infer the size of these symbols by
extending them to the next symbol. If a tool emits
symbols for branch targets, this causes the inferred
size to be too small.

This checks if a symbol starts with a certain prefix
(right now, just .L or LAB_), and skips over it
during symbol size inference.

Resolves #174
2025-05-13 22:36:02 -06:00
e865f3d598 Fix symbol mapping mismatched match %
We have specific diff logic that relies on knowing
which object is the target object, and which is the
base. generate_mapping_symbols was designed in such
a way that it would reverse the target/base, leading
to a match percent shown that's different when it
gets applied.

Fixes #200
2025-05-13 21:57:16 -06:00
2b13e9886a Fix hidden symbol regression
The flagset .contains check doesn't work like this.

Fixes #199
2025-05-13 21:37:29 -06:00
1750af736a Try target-feature=+crt-static 2025-05-13 21:28:57 -06:00
LagoLunatic
731b604c24 Fix highlighting of signed vs unsigned arguments (#202)
* Fix signed and unsigned arguments not being considered equal when highlighting

* Remove unused Eq derive
2025-05-13 14:03:00 -06:00
2d643eb071 Add scratch.preset_id to config.schema.json 2025-05-09 12:51:18 -06:00
0c48d711c7 Improve local branch relocation handling
Reworks the local-branch handling logic to be more
unified: scan_instructions does all the work up front,
and process_instruction / display_instruction can
simply use the calculated branch destination instead
of performing their own is-relocation-target-
function-local checks.

(Hopefully) Fixes #192
2025-05-07 22:53:10 -06:00
34220a8e26 Add address to ReportItem, stabilize sections/functions ordering 2025-05-07 17:36:49 -06:00
0c9e5526d4 Combine sections when generating report 2025-05-07 16:47:20 -06:00
3db0727469 Omit match % for right sections, improve multi-section diffing
Fixes #120
2025-05-07 16:47:20 -06:00
8b5bf21f38 Mark combined sections as SectionKind::Unknown 2025-05-07 16:45:00 -06:00
b77df77000 Minor cleanup, remove Section::symbol_data 2025-05-07 16:43:34 -06:00
7e08f9715b Update dependencies 2025-05-07 16:42:02 -06:00
3c05852d00 Document SuperH support 2025-05-06 23:25:29 -06:00
a51ff44be1 Fix superh wasm (no_std) build 2025-05-06 23:21:07 -06:00
d225cac205 Add superh feature to wasm build 2025-05-06 23:14:38 -06:00
66 changed files with 3167 additions and 941 deletions

View File

@@ -1,5 +1,4 @@
[target.x86_64-pc-windows-msvc]
linker = "rust-lld"
[target.aarch64-pc-windows-msvc]
linker = "rust-lld"
# statically link the C runtime so the executable does not depend on
# that shared/dynamic library.
[target.'cfg(all(target_env = "msvc", target_os = "windows"))']
rustflags = ["-C", "target-feature=+crt-static"]

689
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -16,11 +16,12 @@ Features:
Supports:
- PowerPC 750CL (GameCube, Wii)
- MIPS (N64, PS1, PS2, PSP)
- x86 (COFF only at the moment)
- ARM (GBA, DS, 3DS)
- ARM64 (Switch, experimental)
- ARM64 (Switch)
- MIPS (N64, PS1, PS2, PSP)
- PowerPC (GameCube, Wii)
- SuperH (Saturn, Dreamcast)
- x86, x86_64 (PC)
See [Usage](#usage) for more information.

View File

@@ -175,6 +175,10 @@
"type": "boolean",
"description": "If true, objdiff will run the build command with the context file as an argument to generate it.",
"default": false
},
"preset_id": {
"type": "number",
"description": "The decomp.me preset ID to use for the scratch.\nCompiler and flags in the config will take precedence over the preset, but the preset is useful for organizational purposes."
}
},
"required": [

View File

@@ -103,6 +103,7 @@ allow = [
"0BSD",
"OFL-1.1",
"Ubuntu-font-1.0",
"CDLA-Permissive-2.0",
]
# The confidence threshold for detecting a license from license text.
# The higher the value, the more closely the license text must be to the

View File

@@ -28,7 +28,7 @@ supports-color = "3.0"
time = { version = "0.3", features = ["formatting", "local-offset"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
typed-path = "0.10"
typed-path = "0.11"
[target.'cfg(target_env = "musl")'.dependencies]
mimalloc = "0.1"

View File

@@ -85,6 +85,9 @@ pub fn run(args: Args) -> Result<()> {
fn generate(args: GenerateArgs) -> Result<()> {
let mut diff_config = diff::DiffObjConfig {
function_reloc_diffs: diff::FunctionRelocDiffs::None,
combine_data_sections: true,
combine_text_sections: true,
ppc_calculate_pool_relocations: false,
..Default::default()
};
apply_config_args(&mut diff_config, &args.config)?;
@@ -225,6 +228,7 @@ fn report_object(
demangled_name: None,
virtual_address: section.virtual_address,
}),
address: None,
});
match section.kind {
@@ -272,6 +276,7 @@ fn report_object(
demangled_name: symbol.demangled_name.clone(),
virtual_address: symbol.virtual_address,
}),
address: symbol.address.checked_sub(section.address),
});
if match_percent == 100.0 {
measures.matched_functions += 1;
@@ -279,6 +284,16 @@ fn report_object(
measures.total_functions += 1;
}
}
sections.sort_by(|a, b| a.name.cmp(&b.name));
let reverse_fn_order = object.metadata.reverse_fn_order.unwrap_or(false);
functions.sort_by(|a, b| {
if reverse_fn_order {
b.address.unwrap_or(0).cmp(&a.address.unwrap_or(0))
} else {
a.address.unwrap_or(u64::MAX).cmp(&b.address.unwrap_or(u64::MAX))
}
.then_with(|| a.size.cmp(&b.size))
});
if metadata.complete.unwrap_or(false) {
measures.complete_code = measures.total_code;
measures.complete_data = measures.total_data;

View File

@@ -450,11 +450,11 @@ impl UiView for FunctionDiffUi {
fn reload(&mut self, state: &AppState) -> Result<()> {
let left_sym =
state.left_obj.as_ref().and_then(|(o, _)| find_function(o, &self.symbol_name));
state.left_obj.as_ref().and_then(|(o, _)| o.symbol_by_name(&self.symbol_name));
let right_sym =
state.right_obj.as_ref().and_then(|(o, _)| find_function(o, &self.symbol_name));
state.right_obj.as_ref().and_then(|(o, _)| o.symbol_by_name(&self.symbol_name));
let prev_sym =
state.prev_obj.as_ref().and_then(|(o, _)| find_function(o, &self.symbol_name));
state.prev_obj.as_ref().and_then(|(o, _)| o.symbol_by_name(&self.symbol_name));
self.num_rows = match (
get_symbol(state.left_obj.as_ref(), left_sym),
get_symbol(state.right_obj.as_ref(), right_sym),
@@ -570,6 +570,7 @@ impl FunctionDiffUi {
DiffTextColor::Normal => Color::Gray,
DiffTextColor::Dim => Color::DarkGray,
DiffTextColor::Bright => Color::White,
DiffTextColor::DataFlow => Color::LightCyan,
DiffTextColor::Replace => Color::Cyan,
DiffTextColor::Delete => Color::Red,
DiffTextColor::Insert => Color::Green,
@@ -650,12 +651,3 @@ fn get_symbol(
let sym = sym?;
Some((obj, sym, &diff.symbols[sym]))
}
fn find_function(obj: &Object, name: &str) -> Option<usize> {
for (symbol_idx, symbol) in obj.symbols.iter().enumerate() {
if symbol.name == name {
return Some(symbol_idx);
}
}
None
}

View File

@@ -41,6 +41,7 @@ any-arch = [
"dep:regex",
"dep:similar",
"dep:syn",
"dep:encoding_rs"
]
bindings = [
"dep:prost",
@@ -133,7 +134,7 @@ prost = { version = "0.13", default-features = false, features = ["prost-derive"
regex = { version = "1.11", default-features = false, features = [], optional = true }
serde = { version = "1.0", default-features = false, features = ["derive"], optional = true }
similar = { version = "2.7", default-features = false, features = ["hashbrown"], optional = true, git = "https://github.com/encounter/similar.git", branch = "no_std" }
typed-path = { version = "0.10", default-features = false, optional = true }
typed-path = { version = "0.11", default-features = false, optional = true }
# config
globset = { version = "0.4", default-features = false, optional = true }
@@ -158,7 +159,7 @@ iced-x86 = { version = "1.21", default-features = false, features = ["decoder",
msvc-demangler = { version = "0.11", optional = true }
# arm
unarm = { version = "1.7", optional = true }
unarm = { version = "1.8", optional = true }
arm-attr = { version = "0.2", optional = true }
# arm64
@@ -171,10 +172,10 @@ notify-debouncer-full = { version = "0.5.0", optional = true }
shell-escape = { version = "0.1", optional = true }
tempfile = { version = "3.19", optional = true }
time = { version = "0.3", optional = true }
encoding_rs = "0.8.35"
encoding_rs = { version = "0.8.35", optional = true }
[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", optional = true }
winapi = { version = "0.3", optional = true, features = ["winbase"] }
# For Linux static binaries, use rustls
[target.'cfg(target_os = "linux")'.dependencies]
@@ -200,4 +201,4 @@ syn = { version = "2.0", optional = true }
[dev-dependencies]
# Enable all features for tests
objdiff-core = { path = ".", features = ["all"] }
insta = "1.42"
insta = "1.43"

View File

@@ -5,11 +5,12 @@ objdiff-core contains the core functionality of [objdiff](https://github.com/enc
## Crate feature flags
- **`all`**: Enables all main features.
- **`bindings`**: Enables serialization and deserialization of objdiff data structures.
- **`config`**: Enables objdiff configuration file support.
- **`dwarf`**: Enables extraction of line number information from DWARF debug sections.
- **`mips`**: Enables the MIPS backend powered by [rabbitizer](https://github.com/Decompollaborate/rabbitizer). (Note: C library with Rust bindings)
- **`ppc`**: Enables the PowerPC backend powered by [ppc750cl](https://github.com/encounter/ppc750cl).
- **`x86`**: Enables the x86 backend powered by [iced-x86](https://crates.io/crates/iced-x86).
- **`arm`**: Enables the ARM backend powered by [unarm](https://github.com/AetiasHax/unarm).
- **`arm64`**: Enables the ARM64 backend powered by [yaxpeax-arm](https://github.com/iximeow/yaxpeax-arm).
- **`bindings`**: Enables serialization and deserialization of objdiff data structures.
- **`arm`**: Enables the ARM backend powered by [unarm](https://github.com/AetiasHax/unarm).
- **`mips`**: Enables the MIPS backend powered by [rabbitizer](https://github.com/Decompollaborate/rabbitizer).
- **`ppc`**: Enables the PowerPC backend powered by [ppc750cl](https://github.com/encounter/ppc750cl).
- **`superh`**: Enables the SuperH backend powered by an included disassembler.
- **`x86`**: Enables the x86 backend powered by [iced-x86](https://crates.io/crates/iced-x86).

View File

@@ -25,6 +25,20 @@
}
]
},
{
"id": "analyzeDataFlow",
"type": "boolean",
"default": false,
"name": "(Experimental) Perform data flow analysis",
"description": "Use data flow analysis to display known information about register contents where possible"
},
{
"id": "showDataFlow",
"type": "boolean",
"default": true,
"name": "Show data flow",
"description": "Show data flow analysis results in place of register name where present"
},
{
"id": "spaceBetweenArgs",
"type": "boolean",
@@ -264,7 +278,8 @@
"id": "ppc",
"name": "PowerPC",
"properties": [
"ppc.calculatePoolRelocations"
"ppc.calculatePoolRelocations",
"analyzeDataFlow"
]
},
{

View File

@@ -99,6 +99,8 @@ message ReportItem {
float fuzzy_match_percent = 3;
// Extra metadata for this item
optional ReportItemMetadata metadata = 4;
// Address of the item (section-relative offset)
optional uint64 address = 5;
}
// Extra metadata for an item

View File

@@ -15,7 +15,7 @@ use crate::{
diff::{ArmArchVersion, ArmR9Usage, DiffObjConfig, display::InstructionPart},
obj::{
InstructionRef, Relocation, RelocationFlags, ResolvedInstructionRef, ResolvedRelocation,
ScannedInstruction, Section, SectionKind, Symbol, SymbolFlag, SymbolFlagSet, SymbolKind,
Section, SectionKind, Symbol, SymbolFlag, SymbolFlagSet, SymbolKind,
},
};
@@ -187,14 +187,14 @@ impl Arch for ArchArm {
self.disasm_modes = Self::get_mapping_symbols(sections, symbols);
}
fn scan_instructions(
fn scan_instructions_internal(
&self,
address: u64,
code: &[u8],
section_index: usize,
_relocations: &[Relocation],
diff_config: &DiffObjConfig,
) -> Result<Vec<ScannedInstruction>> {
) -> Result<Vec<InstructionRef>> {
let start_addr = address as u32;
let end_addr = start_addr + code.len() as u32;
@@ -219,13 +219,13 @@ impl Arch for ArchArm {
let mut next_mapping = mappings_iter.next();
let ins_count = code.len() / mode.instruction_size(start_addr);
let mut ops = Vec::<ScannedInstruction>::with_capacity(ins_count);
let mut ops = Vec::<InstructionRef>::with_capacity(ins_count);
let parse_flags = self.parse_flags(diff_config);
let mut address = start_addr;
while address < end_addr {
while let Some(next) = next_mapping.take_if(|x| address >= x.address) {
while let Some(next) = next_mapping.filter(|x| address >= x.address) {
// Change mapping
mode = next.mapping;
next_mapping = mappings_iter.next();
@@ -235,12 +235,10 @@ impl Arch for ArchArm {
let data = &code[(address - start_addr) as usize..];
if data.len() < ins_size {
// Push the remainder as data
ops.push(ScannedInstruction {
ins_ref: InstructionRef {
address: address as u64,
size: data.len() as u8,
opcode: u16::MAX,
},
ops.push(InstructionRef {
address: address as u64,
size: data.len() as u8,
opcode: u16::MAX,
branch_dest: None,
});
break;
@@ -256,12 +254,10 @@ impl Arch for ArchArm {
}
_ => {
// Invalid instruction size
ops.push(ScannedInstruction {
ins_ref: InstructionRef {
address: address as u64,
size: ins_size as u8,
opcode: u16::MAX,
},
ops.push(InstructionRef {
address: address as u64,
size: ins_size as u8,
opcode: u16::MAX,
branch_dest: None,
});
address += ins_size as u32;
@@ -325,8 +321,10 @@ impl Arch for ArchArm {
unarm::ParseMode::Data => (u16::MAX, None),
};
ops.push(ScannedInstruction {
ins_ref: InstructionRef { address: address as u64, size: ins_size as u8, opcode },
ops.push(InstructionRef {
address: address as u64,
size: ins_size as u8,
opcode,
branch_dest: branch_dest.map(|x| x as u64),
});
address += ins_size as u32;

View File

@@ -18,7 +18,6 @@ use crate::{
diff::{DiffObjConfig, display::InstructionPart},
obj::{
InstructionRef, Relocation, RelocationFlags, ResolvedInstructionRef, ResolvedRelocation,
ScannedInstruction,
},
};
@@ -30,16 +29,16 @@ impl ArchArm64 {
}
impl Arch for ArchArm64 {
fn scan_instructions(
fn scan_instructions_internal(
&self,
address: u64,
code: &[u8],
_section_index: usize,
_relocations: &[Relocation],
_diff_config: &DiffObjConfig,
) -> Result<Vec<ScannedInstruction>> {
) -> Result<Vec<InstructionRef>> {
let start_address = address;
let mut ops = Vec::<ScannedInstruction>::with_capacity(code.len() / 4);
let mut ops = Vec::<InstructionRef>::with_capacity(code.len() / 4);
let mut reader = U8Reader::new(code);
let decoder = InstDecoder::default();
@@ -58,8 +57,10 @@ impl Arch for ArchArm64 {
DecodeError::InvalidOpcode
| DecodeError::InvalidOperand
| DecodeError::IncompleteDecoder => {
ops.push(ScannedInstruction {
ins_ref: InstructionRef { address, size: 4, opcode: u16::MAX },
ops.push(InstructionRef {
address,
size: 4,
opcode: u16::MAX,
branch_dest: None,
});
continue;
@@ -68,9 +69,9 @@ impl Arch for ArchArm64 {
}
let opcode = opcode_to_u16(ins.opcode);
let ins_ref = InstructionRef { address, size: 4, opcode };
let branch_dest = branch_dest(ins_ref, &code[offset as usize..offset as usize + 4]);
ops.push(ScannedInstruction { ins_ref, branch_dest });
let branch_dest =
branch_dest(opcode, address, &code[offset as usize..offset as usize + 4]);
ops.push(InstructionRef { address, size: 4, opcode, branch_dest });
}
Ok(ops)
@@ -163,7 +164,7 @@ impl Arch for ArchArm64 {
}
}
fn branch_dest(ins_ref: InstructionRef, code: &[u8]) -> Option<u64> {
fn branch_dest(opcode: u16, address: u64, code: &[u8]) -> Option<u64> {
const OPCODE_B: u16 = opcode_to_u16(Opcode::B);
const OPCODE_BL: u16 = opcode_to_u16(Opcode::BL);
const OPCODE_BCC: u16 = opcode_to_u16(Opcode::Bcc(0));
@@ -173,21 +174,21 @@ fn branch_dest(ins_ref: InstructionRef, code: &[u8]) -> Option<u64> {
const OPCODE_TBNZ: u16 = opcode_to_u16(Opcode::TBNZ);
let word = u32::from_le_bytes(code.try_into().ok()?);
match ins_ref.opcode {
match opcode {
OPCODE_B | OPCODE_BL => {
let offset = ((word & 0x03ff_ffff) << 2) as i32;
let extended_offset = (offset << 4) >> 4;
ins_ref.address.checked_add_signed(extended_offset as i64)
address.checked_add_signed(extended_offset as i64)
}
OPCODE_BCC | OPCODE_CBZ | OPCODE_CBNZ => {
let offset = (word as i32 & 0x00ff_ffe0) >> 3;
let extended_offset = (offset << 11) >> 11;
ins_ref.address.checked_add_signed(extended_offset as i64)
address.checked_add_signed(extended_offset as i64)
}
OPCODE_TBZ | OPCODE_TBNZ => {
let offset = (word as i32 & 0x0007_ffe0) >> 3;
let extended_offset = (offset << 16) >> 16;
ins_ref.address.checked_add_signed(extended_offset as i64)
address.checked_add_signed(extended_offset as i64)
}
_ => None,
}

View File

@@ -3,7 +3,6 @@ use alloc::{
string::{String, ToString},
vec::Vec,
};
use core::ops::Range;
use anyhow::{Result, bail};
use object::{Endian as _, Object as _, ObjectSection as _, ObjectSymbol as _, elf};
@@ -19,7 +18,7 @@ use crate::{
diff::{DiffObjConfig, MipsAbi, MipsInstrCategory, display::InstructionPart},
obj::{
InstructionArg, InstructionArgValue, InstructionRef, Relocation, RelocationFlags,
ResolvedInstructionRef, ResolvedRelocation, ScannedInstruction, SymbolFlag, SymbolFlagSet,
ResolvedInstructionRef, ResolvedRelocation, SymbolFlag, SymbolFlagSet,
},
};
@@ -189,16 +188,16 @@ impl ArchMips {
}
impl Arch for ArchMips {
fn scan_instructions(
fn scan_instructions_internal(
&self,
address: u64,
code: &[u8],
_section_index: usize,
_relocations: &[Relocation],
diff_config: &DiffObjConfig,
) -> Result<Vec<ScannedInstruction>> {
) -> Result<Vec<InstructionRef>> {
let instruction_flags = self.instruction_flags(diff_config);
let mut ops = Vec::<ScannedInstruction>::with_capacity(code.len() / 4);
let mut ops = Vec::<InstructionRef>::with_capacity(code.len() / 4);
let mut cur_addr = address as u32;
for chunk in code.chunks_exact(4) {
let code = self.endianness.read_u32_bytes(chunk.try_into()?);
@@ -206,10 +205,7 @@ impl Arch for ArchMips {
rabbitizer::Instruction::new(code, Vram::new(cur_addr), instruction_flags);
let opcode = instruction.opcode() as u16;
let branch_dest = instruction.get_branch_vram_generic().map(|v| v.inner() as u64);
ops.push(ScannedInstruction {
ins_ref: InstructionRef { address: cur_addr as u64, size: 4, opcode },
branch_dest,
});
ops.push(InstructionRef { address: cur_addr as u64, size: 4, opcode, branch_dest });
cur_addr += 4;
}
Ok(ops)
@@ -225,16 +221,7 @@ impl Arch for ArchMips {
let display_flags = self.instruction_display_flags(diff_config);
let opcode = instruction.opcode();
cb(InstructionPart::opcode(opcode.name(), opcode as u16))?;
let start_address = resolved.symbol.address;
let function_range = start_address..start_address + resolved.symbol.size;
push_args(
&instruction,
resolved.relocation,
function_range,
resolved.section_index,
&display_flags,
cb,
)?;
push_args(&instruction, resolved.relocation, &display_flags, cb)?;
Ok(())
}
@@ -338,8 +325,6 @@ impl Arch for ArchMips {
fn push_args(
instruction: &rabbitizer::Instruction,
relocation: Option<ResolvedRelocation>,
function_range: Range<u64>,
section_index: usize,
display_flags: &rabbitizer::InstructionDisplayFlags,
mut arg_cb: impl FnMut(InstructionPart) -> Result<()>,
) -> Result<()> {
@@ -362,23 +347,7 @@ fn push_args(
}
ValuedOperand::core_label(..) | ValuedOperand::core_branch_target_label(..) => {
if let Some(resolved) = relocation {
// If the relocation target is within the current function, we can
// convert it into a relative branch target. Note that we check
// target_address > start_address instead of >= so that recursive
// tail calls are not considered branch targets.
let target_address =
resolved.symbol.address.checked_add_signed(resolved.relocation.addend);
if resolved.symbol.section == Some(section_index)
&& target_address.is_some_and(|addr| {
addr > function_range.start && addr < function_range.end
})
{
// TODO move this logic up a level
let target_address = target_address.unwrap();
arg_cb(InstructionPart::branch_dest(target_address))?;
} else {
push_reloc(resolved.relocation, &mut arg_cb)?;
}
push_reloc(resolved.relocation, &mut arg_cb)?;
} else if let Some(branch_dest) = instruction
.get_branch_offset_generic()
.map(|o| (instruction.vram() + o).inner() as u64)

View File

@@ -1,5 +1,8 @@
use alloc::{borrow::Cow, boxed::Box, format, string::String, vec::Vec};
use core::{ffi::CStr, fmt, fmt::Debug};
use core::{
ffi::CStr,
fmt::{self, Debug},
};
use anyhow::{Result, bail};
use encoding_rs::SHIFT_JIS;
@@ -11,8 +14,9 @@ use crate::{
display::{ContextItem, HoverItem, InstructionPart},
},
obj::{
InstructionArg, Object, ParsedInstruction, Relocation, RelocationFlags,
ResolvedInstructionRef, ScannedInstruction, Section, Symbol, SymbolFlagSet, SymbolKind,
FlowAnalysisResult, InstructionArg, InstructionRef, Object, ParsedInstruction, Relocation,
RelocationFlags, ResolvedInstructionRef, ResolvedSymbol, Section, Symbol, SymbolFlagSet,
SymbolKind,
},
util::ReallySigned,
};
@@ -31,6 +35,7 @@ pub mod superh;
pub mod x86;
/// Represents the type of data associated with an instruction
#[derive(PartialEq)]
pub enum DataType {
Int8,
Int16,
@@ -182,46 +187,108 @@ impl DataType {
}
}
pub trait Arch: Send + Sync + Debug {
// Finishes arch-specific initialization that must be done after sections have been combined.
fn post_init(&mut self, _sections: &[Section], _symbols: &[Symbol]) {}
impl dyn Arch {
/// Generate a list of instructions references (offset, size, opcode) from the given code.
///
/// The opcode IDs are used to generate the initial diff. Implementations should do as little
/// parsing as possible here: just enough to identify the base instruction opcode, size, and
/// possible branch destination (for visual representation). As needed, instructions are parsed
/// via `process_instruction` to compare their arguments.
fn scan_instructions(
/// See [`scan_instructions_internal`] for more details.
pub fn scan_instructions(
&self,
address: u64,
code: &[u8],
section_index: usize,
relocations: &[Relocation],
resolved: ResolvedSymbol,
diff_config: &DiffObjConfig,
) -> Result<Vec<ScannedInstruction>>;
) -> Result<Vec<InstructionRef>> {
let mut result = self.scan_instructions_internal(
resolved.symbol.address,
resolved.data,
resolved.section_index,
&resolved.section.relocations,
diff_config,
)?;
let function_start = resolved.symbol.address;
let function_end = function_start + resolved.symbol.size;
// Remove any branch destinations that are outside the function range
for ins in result.iter_mut() {
if let Some(branch_dest) = ins.branch_dest {
if branch_dest < function_start || branch_dest >= function_end {
ins.branch_dest = None;
}
}
}
// Resolve relocation targets within the same function to branch destinations
let mut ins_iter = result.iter_mut().peekable();
'outer: for reloc in resolved
.section
.relocations
.iter()
.skip_while(|r| r.address < function_start)
.take_while(|r| r.address < function_end)
{
let ins = loop {
let Some(ins) = ins_iter.peek_mut() else {
break 'outer;
};
if reloc.address < ins.address {
continue 'outer;
}
let ins = ins_iter.next().unwrap();
if reloc.address >= ins.address && reloc.address < ins.address + ins.size as u64 {
break ins;
}
};
// Clear existing branch destination for instructions with relocations
ins.branch_dest = None;
let Some(target) = resolved.obj.symbols.get(reloc.target_symbol) else {
continue;
};
if target.section != Some(resolved.section_index) {
continue;
}
let Some(target_address) = target.address.checked_add_signed(reloc.addend) else {
continue;
};
// If the target address is within the function range, set it as a branch destination
if target_address >= function_start && target_address < function_end {
ins.branch_dest = Some(target_address);
}
}
Ok(result)
}
/// Parse an instruction to gather its mnemonic and arguments for more detailed comparison.
///
/// This is called only when we need to compare the arguments of an instruction.
fn process_instruction(
pub fn process_instruction(
&self,
resolved: ResolvedInstructionRef,
diff_config: &DiffObjConfig,
) -> Result<ParsedInstruction> {
let mut mnemonic = None;
let mut args = Vec::with_capacity(8);
let mut relocation_emitted = false;
self.display_instruction(resolved, diff_config, &mut |part| {
match part {
InstructionPart::Opcode(m, _) => mnemonic = Some(Cow::Owned(m.into_owned())),
InstructionPart::Arg(arg) => args.push(arg.into_static()),
InstructionPart::Arg(arg) => {
if arg == InstructionArg::Reloc {
relocation_emitted = true;
// If the relocation was resolved to a branch destination, emit that instead.
if let Some(dest) = resolved.ins_ref.branch_dest {
args.push(InstructionArg::BranchDest(dest));
return Ok(());
}
}
args.push(arg.into_static());
}
_ => {}
}
Ok(())
})?;
// If the instruction has a relocation, but we didn't format it in the display, add it to
// the end of the arguments list.
if resolved.relocation.is_some() && !args.contains(&InstructionArg::Reloc) {
if resolved.relocation.is_some() && !relocation_emitted {
args.push(InstructionArg::Reloc);
}
Ok(ParsedInstruction {
@@ -230,6 +297,26 @@ pub trait Arch: Send + Sync + Debug {
args,
})
}
}
pub trait Arch: Send + Sync + Debug {
/// Finishes arch-specific initialization that must be done after sections have been combined.
fn post_init(&mut self, _sections: &[Section], _symbols: &[Symbol]) {}
/// Generate a list of instructions references (offset, size, opcode) from the given code.
///
/// The opcode IDs are used to generate the initial diff. Implementations should do as little
/// parsing as possible here: just enough to identify the base instruction opcode, size, and
/// possible branch destination (for visual representation). As needed, instructions are parsed
/// via `process_instruction` to compare their arguments.
fn scan_instructions_internal(
&self,
address: u64,
code: &[u8],
section_index: usize,
relocations: &[Relocation],
diff_config: &DiffObjConfig,
) -> Result<Vec<InstructionRef>>;
/// Format an instruction for display.
///
@@ -253,6 +340,17 @@ pub trait Arch: Send + Sync + Debug {
Vec::new()
}
// Perform detailed data flow analysis
fn data_flow_analysis(
&self,
_obj: &Object,
_symbol: &Symbol,
_code: &[u8],
_relocations: &[Relocation],
) -> Option<Box<dyn FlowAnalysisResult>> {
None
}
fn implcit_addend(
&self,
file: &object::File<'_>,
@@ -332,14 +430,14 @@ impl ArchDummy {
}
impl Arch for ArchDummy {
fn scan_instructions(
fn scan_instructions_internal(
&self,
_address: u64,
_code: &[u8],
_section_index: usize,
_relocations: &[Relocation],
_diff_config: &DiffObjConfig,
) -> Result<Vec<ScannedInstruction>> {
) -> Result<Vec<InstructionRef>> {
Ok(Vec::new())
}

View File

@@ -0,0 +1,642 @@
use alloc::{
boxed::Box,
collections::{BTreeMap, BTreeSet},
format,
string::{String, ToString},
vec::Vec,
};
use core::{
ffi::CStr,
ops::{Index, IndexMut},
};
use itertools::Itertools;
use ppc750cl::Simm;
use crate::{
arch::DataType,
obj::{FlowAnalysisResult, FlowAnalysisValue, Object, Relocation, Symbol},
util::{RawDouble, RawFloat},
};
fn is_store_instruction(op: ppc750cl::Opcode) -> bool {
use ppc750cl::Opcode;
matches!(
op,
Opcode::Stbux
| Opcode::Stbx
| Opcode::Stfdux
| Opcode::Stfdx
| Opcode::Stfiwx
| Opcode::Stfsux
| Opcode::Stfsx
| Opcode::Sthbrx
| Opcode::Sthux
| Opcode::Sthx
| Opcode::Stswi
| Opcode::Stswx
| Opcode::Stwbrx
| Opcode::Stwcx_
| Opcode::Stwux
| Opcode::Stwx
| Opcode::Stwu
| Opcode::Stb
| Opcode::Stbu
| Opcode::Sth
| Opcode::Sthu
| Opcode::Stmw
| Opcode::Stfs
| Opcode::Stfsu
| Opcode::Stfd
| Opcode::Stfdu
)
}
pub fn guess_data_type_from_load_store_inst_op(inst_op: ppc750cl::Opcode) -> Option<DataType> {
use ppc750cl::Opcode;
match inst_op {
Opcode::Lbz | Opcode::Lbzu | Opcode::Lbzux | Opcode::Lbzx => Some(DataType::Int8),
Opcode::Lhz | Opcode::Lhzu | Opcode::Lhzux | Opcode::Lhzx => Some(DataType::Int16),
Opcode::Lha | Opcode::Lhau | Opcode::Lhaux | Opcode::Lhax => Some(DataType::Int16),
Opcode::Lwz | Opcode::Lwzu | Opcode::Lwzux | Opcode::Lwzx => Some(DataType::Int32),
Opcode::Lfs | Opcode::Lfsu | Opcode::Lfsux | Opcode::Lfsx => Some(DataType::Float),
Opcode::Lfd | Opcode::Lfdu | Opcode::Lfdux | Opcode::Lfdx => Some(DataType::Double),
Opcode::Stb | Opcode::Stbu | Opcode::Stbux | Opcode::Stbx => Some(DataType::Int8),
Opcode::Sth | Opcode::Sthu | Opcode::Sthux | Opcode::Sthx => Some(DataType::Int16),
Opcode::Stw | Opcode::Stwu | Opcode::Stwux | Opcode::Stwx => Some(DataType::Int32),
Opcode::Stfs | Opcode::Stfsu | Opcode::Stfsux | Opcode::Stfsx => Some(DataType::Float),
Opcode::Stfd | Opcode::Stfdu | Opcode::Stfdux | Opcode::Stfdx => Some(DataType::Double),
_ => None,
}
}
#[derive(Default, PartialEq, Eq, Copy, Clone, Debug, PartialOrd, Ord)]
enum RegisterContent {
#[default]
Unknown,
Variable, // Multiple potential values
FloatConstant(RawFloat),
DoubleConstant(RawDouble),
IntConstant(i32),
InputRegister(u8),
Symbol(usize),
}
impl core::fmt::Display for RegisterContent {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
RegisterContent::Unknown => write!(f, "unknown"),
RegisterContent::Variable => write!(f, "variable"),
RegisterContent::IntConstant(i) =>
// -i is safe because it's at most a 16 bit constant in the i32
{
if *i >= 0 {
write!(f, "0x{:x}", i)
} else {
write!(f, "-0x{:x}", -i)
}
}
RegisterContent::FloatConstant(RawFloat(fp)) => write!(f, "{fp:?}f"),
RegisterContent::DoubleConstant(RawDouble(fp)) => write!(f, "{fp:?}d"),
RegisterContent::InputRegister(p) => write!(f, "input{p}"),
RegisterContent::Symbol(_u) => write!(f, "relocation"),
}
}
}
#[derive(Clone, PartialEq, Eq, Ord, PartialOrd)]
struct RegisterState {
gpr: [RegisterContent; 32],
fpr: [RegisterContent; 32],
}
impl RegisterState {
fn new() -> Self {
RegisterState { gpr: [RegisterContent::Unknown; 32], fpr: [RegisterContent::Unknown; 32] }
}
// During a function call, these registers must be assumed trashed.
fn clear_volatile(&mut self) {
self[ppc750cl::GPR(0)] = RegisterContent::Unknown;
for i in 0..=13 {
self[ppc750cl::GPR(i)] = RegisterContent::Unknown;
}
for i in 0..=13 {
self[ppc750cl::FPR(i)] = RegisterContent::Unknown;
}
}
// Mark potential input values.
// Subsequent flow analysis will "realize" that they are not actually inputs if
// they get overwritten with another value before getting read.
fn set_potential_inputs(&mut self) {
for g_reg in 3..=13 {
self[ppc750cl::GPR(g_reg)] = RegisterContent::InputRegister(g_reg);
}
for f_reg in 1..=13 {
self[ppc750cl::FPR(f_reg)] = RegisterContent::InputRegister(f_reg);
}
}
// If the there is no value, we can take the new known value.
// If there's a known value different than the new value, the content
// must is variable.
// Returns whether the current value was updated.
fn unify_values(current: &mut RegisterContent, new: &RegisterContent) -> bool {
if *current == *new {
false
} else if *current == RegisterContent::Unknown {
*current = *new;
true
} else if *current == RegisterContent::Variable {
// Already variable
false
} else {
*current = RegisterContent::Variable;
true
}
}
// Unify currently known register contents in a give situation with new
// information about the register contents in that situation.
// Currently unknown register contents can be filled, but if there are
// conflicting contents, we go back to unknown.
fn unify(&mut self, other: &RegisterState) -> bool {
let mut updated = false;
for i in 0..32 {
updated |= Self::unify_values(&mut self.gpr[i], &other.gpr[i]);
updated |= Self::unify_values(&mut self.fpr[i], &other.fpr[i]);
}
updated
}
}
impl Index<ppc750cl::GPR> for RegisterState {
type Output = RegisterContent;
fn index(&self, gpr: ppc750cl::GPR) -> &Self::Output { &self.gpr[gpr.0 as usize] }
}
impl IndexMut<ppc750cl::GPR> for RegisterState {
fn index_mut(&mut self, gpr: ppc750cl::GPR) -> &mut Self::Output {
&mut self.gpr[gpr.0 as usize]
}
}
impl Index<ppc750cl::FPR> for RegisterState {
type Output = RegisterContent;
fn index(&self, fpr: ppc750cl::FPR) -> &Self::Output { &self.fpr[fpr.0 as usize] }
}
impl IndexMut<ppc750cl::FPR> for RegisterState {
fn index_mut(&mut self, fpr: ppc750cl::FPR) -> &mut Self::Output {
&mut self.fpr[fpr.0 as usize]
}
}
fn execute_instruction(
registers: &mut RegisterState,
op: &ppc750cl::Opcode,
args: &ppc750cl::Arguments,
) {
use ppc750cl::{Argument, GPR, Opcode};
match (op, args[0], args[1], args[2]) {
(Opcode::Or, Argument::GPR(a), Argument::GPR(b), Argument::GPR(c)) => {
// Move is implemented as or with self for ints
if b == c {
registers[a] = registers[b];
} else {
registers[a] = RegisterContent::Unknown;
}
}
(Opcode::Fmr, Argument::FPR(a), Argument::FPR(b), _) => {
registers[a] = registers[b];
}
(Opcode::Addi, Argument::GPR(a), Argument::GPR(GPR(0)), Argument::Simm(c)) => {
// Load immidiate implemented as addi with addend = r0
// Let Addi with other addends fall through to the case which
// overwrites the destination
registers[a] = RegisterContent::IntConstant(c.0 as i32);
}
(Opcode::Bcctr, _, _, _) => {
// Called a function pointer, may have erased volatile registers
registers.clear_volatile();
}
(Opcode::B, _, _, _) => {
if get_branch_offset(args) == 0 {
// Call to another function
registers.clear_volatile();
}
}
(
Opcode::Stbu | Opcode::Sthu | Opcode::Stwu | Opcode::Stfsu | Opcode::Stfdu,
_,
_,
Argument::GPR(rel),
) => {
// Storing with update, clear updated register (third arg)
registers[rel] = RegisterContent::Unknown;
}
(
Opcode::Stbux | Opcode::Sthux | Opcode::Stwux | Opcode::Stfsux | Opcode::Stfdux,
_,
Argument::GPR(rel),
_,
) => {
// Storing indexed with update, clear updated register (second arg)
registers[rel] = RegisterContent::Unknown;
}
(Opcode::Lmw, Argument::GPR(target), _, _) => {
// `lmw` overwrites all registers from rd to r31.
for reg in target.0..31 {
registers[GPR(reg)] = RegisterContent::Unknown;
}
}
(_, Argument::GPR(a), _, _) => {
// Store instructions don't modify the GPR
if !is_store_instruction(*op) {
// Other operations which write to GPR a
registers[a] = RegisterContent::Unknown;
}
}
(_, Argument::FPR(a), _, _) => {
// Store instructions don't modify the FPR
if !is_store_instruction(*op) {
// Other operations which write to FPR a
registers[a] = RegisterContent::Unknown;
}
}
(_, _, _, _) => {}
}
}
fn get_branch_offset(args: &ppc750cl::Arguments) -> i32 {
for arg in args.iter() {
match arg {
ppc750cl::Argument::BranchDest(dest) => return dest.0 / 4,
ppc750cl::Argument::None => break,
_ => {}
}
}
0
}
#[derive(Debug, Default)]
struct PPCFlowAnalysisResult {
argument_contents: BTreeMap<(u64, u8), FlowAnalysisValue>,
}
impl PPCFlowAnalysisResult {
fn set_argument_value_at_address(
&mut self,
address: u64,
argument: u8,
value: FlowAnalysisValue,
) {
self.argument_contents.insert((address, argument), value);
}
fn new() -> Self { PPCFlowAnalysisResult { argument_contents: Default::default() } }
}
impl FlowAnalysisResult for PPCFlowAnalysisResult {
fn get_argument_value_at_address(
&self,
address: u64,
argument: u8,
) -> Option<&FlowAnalysisValue> {
self.argument_contents.get(&(address, argument))
}
}
fn clamp_text_length(s: String, max: usize) -> String {
if s.len() <= max { s } else { format!("{}", s.chars().take(max - 3).collect::<String>()) }
}
fn get_register_content_from_reloc(
reloc: &Relocation,
obj: &Object,
op: ppc750cl::Opcode,
) -> RegisterContent {
if let Some(bytes) = obj.symbol_data(reloc.target_symbol) {
match guess_data_type_from_load_store_inst_op(op) {
Some(DataType::Float) => {
RegisterContent::FloatConstant(RawFloat(match obj.endianness {
object::Endianness::Little => {
f32::from_le_bytes(bytes.try_into().unwrap_or([0; 4]))
}
object::Endianness::Big => {
f32::from_be_bytes(bytes.try_into().unwrap_or([0; 4]))
}
}))
}
Some(DataType::Double) => {
RegisterContent::DoubleConstant(RawDouble(match obj.endianness {
object::Endianness::Little => {
f64::from_le_bytes(bytes.try_into().unwrap_or([0; 8]))
}
object::Endianness::Big => {
f64::from_be_bytes(bytes.try_into().unwrap_or([0; 8]))
}
}))
}
_ => RegisterContent::Symbol(reloc.target_symbol),
}
} else {
RegisterContent::Symbol(reloc.target_symbol)
}
}
// Executing op with args at cur_address, update current_state with symbols that
// come from relocations. That is, references to globals, floating point
// constants, string constants, etc.
fn fill_registers_from_relocation(
reloc: &Relocation,
current_state: &mut RegisterState,
obj: &Object,
op: ppc750cl::Opcode,
args: &ppc750cl::Arguments,
) {
// Only update the register state for loads. We may store to a reloc
// address but that doesn't update register contents.
if !is_store_instruction(op) {
match (op, args[0]) {
// Everything else is a load of some sort
(_, ppc750cl::Argument::GPR(gpr)) => {
current_state[gpr] = get_register_content_from_reloc(reloc, obj, op);
}
(_, ppc750cl::Argument::FPR(fpr)) => {
current_state[fpr] = get_register_content_from_reloc(reloc, obj, op);
}
_ => {}
}
}
}
// Special helper fragments generated by MWCC.
// See: https://github.com/encounter/decomp-toolkit/blob/main/src/analysis/pass.rs
const SLEDS: [&str; 6] = ["_savefpr_", "_restfpr_", "_savegpr_", "_restgpr_", "_savev", "_restv"];
fn is_sled_function(name: &str) -> bool { SLEDS.iter().any(|sled| name.starts_with(sled)) }
pub fn ppc_data_flow_analysis(
obj: &Object,
func_symbol: &Symbol,
code: &[u8],
relocations: &[Relocation],
) -> Box<dyn FlowAnalysisResult> {
use alloc::collections::VecDeque;
use ppc750cl::InsIter;
let instructions = InsIter::new(code, func_symbol.address as u32)
.map(|(_addr, ins)| (ins.op, ins.basic().args))
.collect_vec();
let func_address = func_symbol.address;
// Get initial register values from function parameters
let mut initial_register_state = RegisterState::new();
initial_register_state.set_potential_inputs();
let mut execution_queue = VecDeque::<(usize, RegisterState)>::new();
execution_queue.push_back((0, initial_register_state));
// Execute the instructions against abstract data
let mut failsafe_counter = 0;
let mut taken_branches = BTreeSet::<(usize, RegisterState)>::new();
let mut register_state_at = Vec::<RegisterState>::new();
let mut completed_first_pass = false;
register_state_at.resize_with(instructions.len(), RegisterState::new);
while let Some((mut index, mut current_state)) = execution_queue.pop_front() {
while let Some((op, args)) = instructions.get(index) {
// Record the state at this index
// If recording does not result in any changes to the known values
// we're done, because the subsequent values are a function of the
// current values so we'll get the same result as the last time
// we went down this path.
// Don't break out if we haven't even completed the first pass
// through the function though.
if !register_state_at[index].unify(&current_state) && completed_first_pass {
break;
}
// Get symbol used in this instruction
let cur_addr = (func_address as u32) + ((index * 4) as u32);
let reloc = relocations.iter().find(|r| (r.address as u32 & !3) == cur_addr);
// Is this a branch to a compiler generated helper? These helpers
// do not trash registers like normal function calls, so we don't
// want to treat this as normal execution.
let symbol = reloc.and_then(|r| obj.symbols.get(r.target_symbol));
let is_sled_invocation = symbol.is_some_and(|x| is_sled_function(&x.name));
// Execute the instruction to update the state
// Since sled invocations are only used to save / restore registers
// as part of prelude / cleanup in a function call we don't have to
// do any execution for them.
if !is_sled_invocation {
execute_instruction(&mut current_state, op, args);
}
// Fill in register state coming from relocations at this line. This
// handles references to global variables, floating point constants,
// etc.
if let Some(reloc) = reloc {
fill_registers_from_relocation(reloc, &mut current_state, obj, *op, args);
}
// Add conditional branches to execution queue
// Only take a given (address, register state) combination once. If
// the known register state is different we have to take the branch
// again to stabilize the known values for backwards branches.
if op == &ppc750cl::Opcode::Bc {
let branch_state = (index, current_state.clone());
if !taken_branches.contains(&branch_state) {
let offset = get_branch_offset(args);
let target_index = ((index as i32) + offset) as usize;
execution_queue.push_back((target_index, current_state.clone()));
taken_branches.insert(branch_state);
// We should never hit this case, but avoid getting stuck in
// an infinite loop if we hit some kind of bad behavior.
failsafe_counter += 1;
if failsafe_counter > 256 {
//println!("Analysis of {} failed to stabilize", func_symbol.name);
return Box::new(PPCFlowAnalysisResult::new());
}
}
}
// Update index
if op == &ppc750cl::Opcode::B {
// Unconditional branch
let offset = get_branch_offset(args);
if offset > 0 {
// Jump table or branch to over else clause.
index += offset as usize;
} else if offset == 0 {
// Function call with relocation. We'll return to
// the next instruction.
index += 1;
} else {
// Unconditional branch (E.g.: loop { ... })
// Also some compilations of loops put the conditional at
// the end and B to it for the check of the first iteration.
let branch_state = (index, current_state.clone());
if taken_branches.contains(&branch_state) {
break;
}
taken_branches.insert(branch_state);
index = ((index as i32) + offset) as usize;
}
} else {
// Normal execution of next instruction
index += 1;
}
}
// Mark that we've completed at least one pass over the function, at
// this point we can break out if the code we're running doesn't change
// any register outcomes.
completed_first_pass = true;
}
// Store the relevant data flow values for simplified instructions
generate_flow_analysis_result(obj, func_address, code, register_state_at, relocations)
}
fn get_string_data(obj: &Object, symbol_index: usize, offset: Simm) -> Option<&str> {
if let Some(sym) = obj.symbols.get(symbol_index) {
if sym.name.starts_with("@stringBase") && offset.0 != 0 {
if let Some(data) = obj.symbol_data(symbol_index) {
let bytes = &data[offset.0 as usize..];
if let Ok(Ok(str)) = CStr::from_bytes_until_nul(bytes).map(|x| x.to_str()) {
return Some(str);
}
}
}
}
None
}
// Write the relevant part of the flow analysis out into the FlowAnalysisResult
// the rest of the application will use to query results of the flow analysis.
// Flow analysis will compute the known contents of every register at every
// line, but we only need to record the values of registers that are actually
// referenced at each line.
fn generate_flow_analysis_result(
obj: &Object,
base_address: u64,
code: &[u8],
register_state_at: Vec<RegisterState>,
relocations: &[Relocation],
) -> Box<PPCFlowAnalysisResult> {
use ppc750cl::{Argument, InsIter};
let mut analysis_result = PPCFlowAnalysisResult::new();
let default_register_state = RegisterState::new();
for (addr, ins) in InsIter::new(code, 0) {
let ins_address = base_address + (addr as u64);
let index = addr / 4;
let ppc750cl::ParsedIns { mnemonic: _, args } = ins.simplified();
// If we're already showing relocations on a line don't also show data flow
let reloc = relocations.iter().find(|r| (r.address & !3) == ins_address);
// Special case to show float and double constants on the line where
// they are being loaded.
// We need to do this before we break out on showing relocations in the
// subsequent if statement.
if let (ppc750cl::Opcode::Lfs | ppc750cl::Opcode::Lfd, Some(reloc)) = (ins.op, reloc) {
let content = get_register_content_from_reloc(reloc, obj, ins.op);
if matches!(
content,
RegisterContent::FloatConstant(_) | RegisterContent::DoubleConstant(_)
) {
analysis_result.set_argument_value_at_address(
ins_address,
1,
FlowAnalysisValue::Text(content.to_string()),
);
// Don't need to show any other data flow if we're showing that
continue;
}
}
// Special case to show string constants on the line where they are
// being indexed to. This will typically be "addi t, stringbase, offset"
let registers = register_state_at.get(index as usize).unwrap_or(&default_register_state);
if let (ppc750cl::Opcode::Addi, Argument::GPR(rel), Argument::Simm(offset)) =
(ins.op, args[1], args[2])
{
if let RegisterContent::Symbol(sym_index) = registers[rel] {
if let Some(str) = get_string_data(obj, sym_index, offset) {
// Show the string constant in the analysis result
let formatted = format!("\"{str}\"");
analysis_result.set_argument_value_at_address(
ins_address,
2,
FlowAnalysisValue::Text(clamp_text_length(formatted, 20)),
);
// Don't continue, we want to show the stringbase value as well
}
}
}
let is_store = is_store_instruction(ins.op);
for (arg_index, arg) in args.into_iter().enumerate() {
// Hacky shorthand for determining which arguments are sources,
// We only want to show data flow for source registers, not target
// registers. Technically there are some non-"st_" operations which
// read from their first argument but they're rare.
if (arg_index == 0) && !is_store {
continue;
}
let content = match arg {
Argument::GPR(gpr) => Some(registers[gpr]),
Argument::FPR(fpr) => Some(registers[fpr]),
_ => None,
};
let analysis_value = match content {
Some(RegisterContent::Symbol(s)) => {
if reloc.is_none() {
// Only symbols if there isn't already a relocation, because
// code other than the data flow analysis will be showing
// the symbol for a relocation on the line it is for. If we
// also showed it as data flow analysis value we would be
// showing redundant information.
obj.symbols.get(s).map(|sym| {
FlowAnalysisValue::Text(clamp_text_length(
sym.demangled_name.as_ref().unwrap_or(&sym.name).clone(),
20,
))
})
} else {
None
}
}
Some(RegisterContent::InputRegister(reg)) => {
let reg_name = match arg {
Argument::GPR(_) => format!("in_r{reg}"),
Argument::FPR(_) => format!("in_f{reg}"),
_ => panic!("Register content should only be in a register"),
};
Some(FlowAnalysisValue::Text(reg_name))
}
Some(RegisterContent::Unknown) | Some(RegisterContent::Variable) => None,
Some(value) => Some(FlowAnalysisValue::Text(format!("{value}"))),
None => None,
};
if let Some(analysis_value) = analysis_value {
analysis_result.set_argument_value_at_address(
ins_address,
arg_index as u8,
analysis_value,
);
}
}
}
Box::new(analysis_result)
}

View File

@@ -1,4 +1,5 @@
use alloc::{
boxed::Box,
collections::{BTreeMap, BTreeSet},
string::{String, ToString},
vec,
@@ -18,11 +19,13 @@ use crate::{
display::{ContextItem, HoverItem, HoverItemColor, InstructionPart, SymbolNavigationKind},
},
obj::{
InstructionRef, Object, Relocation, RelocationFlags, ResolvedInstructionRef,
ResolvedRelocation, ScannedInstruction, Symbol, SymbolFlag, SymbolFlagSet,
FlowAnalysisResult, InstructionRef, Object, Relocation, RelocationFlags,
ResolvedInstructionRef, ResolvedRelocation, Symbol, SymbolFlag, SymbolFlagSet,
},
};
mod flow_analysis;
// Relative relocation, can be Simm, Offset or BranchDest
fn is_relative_arg(arg: &ppc750cl::Argument) -> bool {
matches!(
@@ -82,24 +85,22 @@ impl ArchPpc {
}
impl Arch for ArchPpc {
fn scan_instructions(
fn scan_instructions_internal(
&self,
address: u64,
code: &[u8],
_section_index: usize,
_relocations: &[Relocation],
_diff_config: &DiffObjConfig,
) -> Result<Vec<ScannedInstruction>> {
) -> Result<Vec<InstructionRef>> {
ensure!(code.len() & 3 == 0, "Code length must be a multiple of 4");
let ins_count = code.len() / 4;
let mut insts = Vec::<ScannedInstruction>::with_capacity(ins_count);
let mut insts = Vec::<InstructionRef>::with_capacity(ins_count);
for (cur_addr, ins) in ppc750cl::InsIter::new(code, address as u32) {
insts.push(ScannedInstruction {
ins_ref: InstructionRef {
address: cur_addr as u64,
size: 4,
opcode: u8::from(ins.op) as u16,
},
insts.push(InstructionRef {
address: cur_addr as u64,
size: 4,
opcode: u8::from(ins.op) as u16,
branch_dest: ins.branch_dest(cur_addr).map(u64::from),
});
}
@@ -159,6 +160,7 @@ impl Arch for ArchPpc {
Ok(())
}
// Could be replaced by data_flow_analysis once that feature stabilizes
fn generate_pooled_relocations(
&self,
address: u64,
@@ -169,6 +171,16 @@ impl Arch for ArchPpc {
generate_fake_pool_relocations_for_function(address, code, relocations, symbols)
}
fn data_flow_analysis(
&self,
obj: &Object,
symbol: &Symbol,
code: &[u8],
relocations: &[Relocation],
) -> Option<Box<dyn FlowAnalysisResult>> {
Some(flow_analysis::ppc_data_flow_analysis(obj, symbol, code, relocations))
}
fn implcit_addend(
&self,
_file: &object::File<'_>,
@@ -227,7 +239,7 @@ impl Arch for ArchPpc {
return Some(DataType::String);
}
let opcode = ppc750cl::Opcode::from(resolved.ins_ref.opcode as u8);
if let Some(ty) = guess_data_type_from_load_store_inst_op(opcode) {
if let Some(ty) = flow_analysis::guess_data_type_from_load_store_inst_op(opcode) {
// Numeric type.
return Some(ty);
}
@@ -503,25 +515,6 @@ fn make_symbol_ref(symbol: &object::Symbol) -> Result<ExtabSymbolRef> {
Ok(ExtabSymbolRef { original_index: symbol.index().0 - 1, name, demangled_name })
}
fn guess_data_type_from_load_store_inst_op(inst_op: ppc750cl::Opcode) -> Option<DataType> {
use ppc750cl::Opcode;
match inst_op {
Opcode::Lbz | Opcode::Lbzu | Opcode::Lbzux | Opcode::Lbzx => Some(DataType::Int8),
Opcode::Lhz | Opcode::Lhzu | Opcode::Lhzux | Opcode::Lhzx => Some(DataType::Int16),
Opcode::Lha | Opcode::Lhau | Opcode::Lhaux | Opcode::Lhax => Some(DataType::Int16),
Opcode::Lwz | Opcode::Lwzu | Opcode::Lwzux | Opcode::Lwzx => Some(DataType::Int32),
Opcode::Lfs | Opcode::Lfsu | Opcode::Lfsux | Opcode::Lfsx => Some(DataType::Float),
Opcode::Lfd | Opcode::Lfdu | Opcode::Lfdux | Opcode::Lfdx => Some(DataType::Double),
Opcode::Stb | Opcode::Stbu | Opcode::Stbux | Opcode::Stbx => Some(DataType::Int8),
Opcode::Sth | Opcode::Sthu | Opcode::Sthux | Opcode::Sthx => Some(DataType::Int16),
Opcode::Stw | Opcode::Stwu | Opcode::Stwux | Opcode::Stwx => Some(DataType::Int32),
Opcode::Stfs | Opcode::Stfsu | Opcode::Stfsux | Opcode::Stfsx => Some(DataType::Float),
Opcode::Stfd | Opcode::Stfdu | Opcode::Stfdux | Opcode::Stfdx => Some(DataType::Double),
_ => None,
}
}
#[derive(Debug)]
struct PoolReference {
addr_src_gpr: ppc750cl::GPR,
@@ -538,7 +531,7 @@ fn get_pool_reference_for_inst(
) -> Option<PoolReference> {
use ppc750cl::{Argument, Opcode};
let args = &simplified.args;
if guess_data_type_from_load_store_inst_op(opcode).is_some() {
if flow_analysis::guess_data_type_from_load_store_inst_op(opcode).is_some() {
match (args[1], args[2]) {
(Argument::Offset(offset), Argument::GPR(addr_src_gpr)) => {
// e.g. lwz. Immediate offset.
@@ -670,7 +663,7 @@ fn make_fake_pool_reloc(
// and returns a Vec of "fake pool relocations" that simulate what a relocation for that instruction
// would look like if data hadn't been pooled.
// This method tries to follow the function's proper control flow. It keeps track of a queue of
// states it hasn't traversed yet, where each state holds an instruction address and a HashMap of
// states it hasn't traversed yet, where each state holds an instruction address and a map of
// which registers hold which pool relocations at that point.
// When a conditional or unconditional branch is encountered, the destination of the branch is added
// to the queue. Conditional branches will traverse both the path where the branch is taken and the

View File

@@ -1,3 +1,5 @@
use alloc::{format, vec::Vec};
use crate::{diff::display::InstructionPart, obj::ResolvedInstructionRef};
static REG_NAMES: [&str; 16] = [

View File

@@ -1,5 +1,4 @@
use alloc::{string::String, vec::Vec};
use std::collections::HashMap;
use alloc::{collections::BTreeMap, format, string::String, vec, vec::Vec};
use anyhow::{Result, bail};
use object::elf;
@@ -7,9 +6,7 @@ use object::elf;
use crate::{
arch::{Arch, superh::disasm::sh2_disasm},
diff::{DiffObjConfig, display::InstructionPart},
obj::{
InstructionRef, Relocation, RelocationFlags, ResolvedInstructionRef, ScannedInstruction,
},
obj::{InstructionRef, Relocation, RelocationFlags, ResolvedInstructionRef},
};
pub mod disasm;
@@ -27,15 +24,15 @@ struct DataInfo {
}
impl Arch for ArchSuperH {
fn scan_instructions(
fn scan_instructions_internal(
&self,
address: u64,
code: &[u8],
_section_index: usize,
_relocations: &[Relocation],
_diff_config: &DiffObjConfig,
) -> Result<Vec<ScannedInstruction>> {
let mut ops = Vec::<ScannedInstruction>::with_capacity(code.len() / 2);
) -> Result<Vec<InstructionRef>> {
let mut ops = Vec::<InstructionRef>::with_capacity(code.len() / 2);
let mut offset = address;
for chunk in code.chunks_exact(2) {
@@ -56,9 +53,7 @@ impl Arch for ArchSuperH {
Some(InstructionPart::Opcode(_, val)) => *val,
_ => 0,
};
let ins_ref: InstructionRef =
InstructionRef { address: offset, size: 2, opcode: opcode_enum };
ops.push(ScannedInstruction { ins_ref, branch_dest });
ops.push(InstructionRef { address: offset, size: 2, opcode: opcode_enum, branch_dest });
offset += 2;
}
@@ -77,10 +72,12 @@ impl Arch for ArchSuperH {
sh2_disasm(0, opcode, true, &mut parts, &resolved, &mut branch_dest);
if let Some(symbol_data) = resolved.section.symbol_data(resolved.symbol) {
if let Some(symbol_data) =
resolved.section.data_range(resolved.symbol.address, resolved.symbol.size as usize)
{
// scan for data
// map of instruction offsets to data target offsets
let mut data_offsets: HashMap<u64, DataInfo> = HashMap::<u64, DataInfo>::new();
let mut data_offsets = BTreeMap::<u64, DataInfo>::new();
let mut pos: u64 = 0;
for chunk in symbol_data.chunks_exact(2) {
@@ -255,7 +252,7 @@ mod test {
arch.display_instruction(
ResolvedInstructionRef {
ins_ref: InstructionRef { address: 0x1000, size: 2, opcode },
ins_ref: InstructionRef { address: 0x1000, size: 2, opcode, branch_dest: None },
code: &code,
..Default::default()
},
@@ -333,7 +330,7 @@ mod test {
arch.display_instruction(
ResolvedInstructionRef {
ins_ref: InstructionRef { address: 0x1000, size: 2, opcode },
ins_ref: InstructionRef { address: 0x1000, size: 2, opcode, branch_dest: None },
code: &code,
..Default::default()
},
@@ -416,7 +413,7 @@ mod test {
arch.display_instruction(
ResolvedInstructionRef {
ins_ref: InstructionRef { address: 0x1000, size: 2, opcode },
ins_ref: InstructionRef { address: 0x1000, size: 2, opcode, branch_dest: None },
code: &code,
..Default::default()
},
@@ -453,7 +450,7 @@ mod test {
arch.display_instruction(
ResolvedInstructionRef {
ins_ref: InstructionRef { address: 0x1000, size: 2, opcode },
ins_ref: InstructionRef { address: 0x1000, size: 2, opcode, branch_dest: None },
code: &code,
..Default::default()
},
@@ -502,7 +499,12 @@ mod test {
arch.display_instruction(
ResolvedInstructionRef {
ins_ref: InstructionRef { address: addr as u64, size: 2, opcode },
ins_ref: InstructionRef {
address: addr as u64,
size: 2,
opcode,
branch_dest: None,
},
code: &code,
..Default::default()
},
@@ -538,7 +540,12 @@ mod test {
arch.display_instruction(
ResolvedInstructionRef {
ins_ref: InstructionRef { address: addr as u64, size: 2, opcode },
ins_ref: InstructionRef {
address: addr as u64,
size: 2,
opcode,
branch_dest: None,
},
code: &code,
..Default::default()
},
@@ -577,7 +584,12 @@ mod test {
arch.display_instruction(
ResolvedInstructionRef {
ins_ref: InstructionRef { address: addr as u64, size: 2, opcode },
ins_ref: InstructionRef {
address: addr as u64,
size: 2,
opcode,
branch_dest: None,
},
code: &code,
..Default::default()
},
@@ -616,7 +628,12 @@ mod test {
arch.display_instruction(
ResolvedInstructionRef {
ins_ref: InstructionRef { address: addr as u64, size: 2, opcode },
ins_ref: InstructionRef {
address: addr as u64,
size: 2,
opcode,
branch_dest: None,
},
code: &code,
..Default::default()
},
@@ -648,7 +665,12 @@ mod test {
arch.display_instruction(
ResolvedInstructionRef {
ins_ref: InstructionRef { address: addr as u64, size: 2, opcode },
ins_ref: InstructionRef {
address: addr as u64,
size: 2,
opcode,
branch_dest: None,
},
code: &code,
..Default::default()
},
@@ -677,7 +699,12 @@ mod test {
arch.display_instruction(
ResolvedInstructionRef {
ins_ref: InstructionRef { address: addr as u64, size: 2, opcode },
ins_ref: InstructionRef {
address: addr as u64,
size: 2,
opcode,
branch_dest: None,
},
code: &code,
..Default::default()
},
@@ -709,7 +736,12 @@ mod test {
arch.display_instruction(
ResolvedInstructionRef {
ins_ref: InstructionRef { address: addr as u64, size: 2, opcode },
ins_ref: InstructionRef {
address: addr as u64,
size: 2,
opcode,
branch_dest: None,
},
code: &opcode.to_be_bytes(),
symbol: &Symbol {
address: 0x0606F378, // func base address
@@ -754,7 +786,12 @@ mod test {
arch.display_instruction(
ResolvedInstructionRef {
ins_ref: InstructionRef { address: addr as u64, size: 2, opcode },
ins_ref: InstructionRef {
address: addr as u64,
size: 2,
opcode,
branch_dest: None,
},
code: &opcode.to_be_bytes(),
symbol: &Symbol {
address: 0x0606F378, // func base address

View File

@@ -6,14 +6,12 @@ use iced_x86::{
Decoder, DecoderOptions, DecoratorKind, FormatterOutput, FormatterTextKind, GasFormatter,
Instruction, IntelFormatter, MasmFormatter, NasmFormatter, NumberKind, OpKind, Register,
};
use object::{Endian as _, Object as _, ObjectSection as _, pe};
use object::{Endian as _, Object as _, ObjectSection as _, elf, pe};
use crate::{
arch::Arch,
diff::{DiffObjConfig, X86Formatter, display::InstructionPart},
obj::{
InstructionRef, Relocation, RelocationFlags, ResolvedInstructionRef, ScannedInstruction,
},
obj::{InstructionRef, Relocation, RelocationFlags, ResolvedInstructionRef},
};
#[derive(Debug)]
@@ -69,7 +67,11 @@ impl ArchX86 {
pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32 => Some(4),
_ => None,
},
_ => None,
RelocationFlags::Elf(typ) => match typ {
elf::R_386_32 | elf::R_386_PC32 => Some(4),
elf::R_386_16 => Some(2),
_ => None,
},
},
Architecture::X86_64 => match flags {
RelocationFlags::Coff(typ) => match typ {
@@ -77,7 +79,11 @@ impl ArchX86 {
pe::IMAGE_REL_AMD64_ADDR64 => Some(8),
_ => None,
},
_ => None,
RelocationFlags::Elf(typ) => match typ {
elf::R_X86_64_PC32 => Some(4),
elf::R_X86_64_64 => Some(8),
_ => None,
},
},
}
}
@@ -86,14 +92,14 @@ impl ArchX86 {
const DATA_OPCODE: u16 = u16::MAX - 1;
impl Arch for ArchX86 {
fn scan_instructions(
fn scan_instructions_internal(
&self,
address: u64,
code: &[u8],
_section_index: usize,
relocations: &[Relocation],
_diff_config: &DiffObjConfig,
) -> Result<Vec<ScannedInstruction>> {
) -> Result<Vec<InstructionRef>> {
let mut out = Vec::with_capacity(code.len() / 2);
let mut decoder = self.decoder(code, address);
let mut instruction = Instruction::default();
@@ -112,12 +118,10 @@ impl Arch for ArchX86 {
})?;
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,
},
out.push(InstructionRef {
address,
size: size as u8,
opcode: DATA_OPCODE,
branch_dest: None,
});
reloc_iter.next();
@@ -134,12 +138,10 @@ impl Arch for ArchX86 {
OpKind::NearBranch64 => Some(instruction.near_branch64()),
_ => None,
};
out.push(ScannedInstruction {
ins_ref: InstructionRef {
address,
size: instruction.len() as u8,
opcode: instruction.mnemonic() as u16,
},
out.push(InstructionRef {
address,
size: instruction.len() as u8,
opcode: instruction.mnemonic() as u16,
branch_dest,
});
}
@@ -233,7 +235,8 @@ impl Arch for ArchX86 {
) -> Result<i64> {
match self.arch {
Architecture::X86 => match flags {
RelocationFlags::Coff(pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32) => {
RelocationFlags::Coff(pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32)
| RelocationFlags::Elf(elf::R_386_32 | elf::R_386_PC32) => {
let data =
section.data()?[address as usize..address as usize + 4].try_into()?;
Ok(self.endianness.read_i32_bytes(data) as i64)
@@ -241,12 +244,14 @@ impl Arch for ArchX86 {
flags => bail!("Unsupported x86 implicit relocation {flags:?}"),
},
Architecture::X86_64 => match flags {
RelocationFlags::Coff(pe::IMAGE_REL_AMD64_ADDR32NB | pe::IMAGE_REL_AMD64_REL32) => {
RelocationFlags::Coff(pe::IMAGE_REL_AMD64_ADDR32NB | pe::IMAGE_REL_AMD64_REL32)
| RelocationFlags::Elf(elf::R_X86_64_32 | elf::R_X86_64_PC32) => {
let data =
section.data()?[address as usize..address as usize + 4].try_into()?;
Ok(self.endianness.read_i32_bytes(data) as i64)
}
RelocationFlags::Coff(pe::IMAGE_REL_AMD64_ADDR64) => {
RelocationFlags::Coff(pe::IMAGE_REL_AMD64_ADDR64)
| RelocationFlags::Elf(elf::R_X86_64_64) => {
let data =
section.data()?[address as usize..address as usize + 8].try_into()?;
Ok(self.endianness.read_i64_bytes(data))
@@ -457,15 +462,16 @@ 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_internal(0, &code, 0, &[], &DiffObjConfig::default()).unwrap();
assert_eq!(scanned.len(), 2);
assert_eq!(scanned[0].ins_ref.address, 0);
assert_eq!(scanned[0].ins_ref.size, 10);
assert_eq!(scanned[0].ins_ref.opcode, iced_x86::Mnemonic::Mov as u16);
assert_eq!(scanned[0].address, 0);
assert_eq!(scanned[0].size, 10);
assert_eq!(scanned[0].opcode, iced_x86::Mnemonic::Mov as u16);
assert_eq!(scanned[0].branch_dest, None);
assert_eq!(scanned[1].ins_ref.address, 10);
assert_eq!(scanned[1].ins_ref.size, 7);
assert_eq!(scanned[1].ins_ref.opcode, iced_x86::Mnemonic::Mov as u16);
assert_eq!(scanned[1].address, 10);
assert_eq!(scanned[1].size, 7);
assert_eq!(scanned[1].opcode, iced_x86::Mnemonic::Mov as u16);
assert_eq!(scanned[1].branch_dest, None);
}
@@ -477,7 +483,7 @@ mod test {
let mut parts = Vec::new();
arch.display_instruction(
ResolvedInstructionRef {
ins_ref: InstructionRef { address: 0x1234, size: 10, opcode },
ins_ref: InstructionRef { address: 0x1234, size: 10, opcode, branch_dest: None },
code: &code,
..Default::default()
},
@@ -513,7 +519,7 @@ mod test {
let mut parts = Vec::new();
arch.display_instruction(
ResolvedInstructionRef {
ins_ref: InstructionRef { address: 0x1234, size: 10, opcode },
ins_ref: InstructionRef { address: 0x1234, size: 10, opcode, branch_dest: None },
code: &code,
relocation: Some(ResolvedRelocation {
relocation: &Relocation {
@@ -558,7 +564,7 @@ mod test {
let mut parts = Vec::new();
arch.display_instruction(
ResolvedInstructionRef {
ins_ref: InstructionRef { address: 0x1234, size: 7, opcode },
ins_ref: InstructionRef { address: 0x1234, size: 7, opcode, branch_dest: None },
code: &code,
relocation: Some(ResolvedRelocation {
relocation: &Relocation {
@@ -601,7 +607,7 @@ mod test {
let mut parts = Vec::new();
arch.display_instruction(
ResolvedInstructionRef {
ins_ref: InstructionRef { address: 0x1234, size: 5, opcode },
ins_ref: InstructionRef { address: 0x1234, size: 5, opcode, branch_dest: None },
code: &code,
relocation: Some(ResolvedRelocation {
relocation: &Relocation {
@@ -632,7 +638,7 @@ mod test {
let mut parts = Vec::new();
arch.display_instruction(
ResolvedInstructionRef {
ins_ref: InstructionRef { address: 0x1234, size: 6, opcode },
ins_ref: InstructionRef { address: 0x1234, size: 6, opcode, branch_dest: None },
code: &code,
relocation: Some(ResolvedRelocation {
relocation: &Relocation {
@@ -671,7 +677,7 @@ mod test {
let mut parts = Vec::new();
arch.display_instruction(
ResolvedInstructionRef {
ins_ref: InstructionRef { address: 0x1234, size: 7, opcode },
ins_ref: InstructionRef { address: 0x1234, size: 7, opcode, branch_dest: None },
code: &code,
relocation: Some(ResolvedRelocation {
relocation: &Relocation {
@@ -710,7 +716,7 @@ mod test {
let mut parts = Vec::new();
arch.display_instruction(
ResolvedInstructionRef {
ins_ref: InstructionRef { address: 0x1234, size: 5, opcode },
ins_ref: InstructionRef { address: 0x1234, size: 5, opcode, branch_dest: None },
code: &code,
relocation: Some(ResolvedRelocation {
relocation: &Relocation {

View File

@@ -434,6 +434,7 @@ impl From<LegacyReportItem> for ReportItem {
demangled_name: value.demangled_name,
virtual_address: value.address,
}),
address: None,
}
}
}

View File

@@ -14,15 +14,15 @@ use super::{
};
use crate::obj::{
InstructionArg, InstructionArgValue, InstructionRef, Object, ResolvedInstructionRef,
ResolvedRelocation, ScannedInstruction, SymbolFlag, SymbolKind,
ResolvedRelocation, ResolvedSymbol, SymbolFlag, SymbolKind,
};
pub fn no_diff_code(
obj: &Object,
symbol_idx: usize,
symbol_index: usize,
diff_config: &DiffObjConfig,
) -> Result<SymbolDiff> {
let symbol = &obj.symbols[symbol_idx];
let symbol = &obj.symbols[symbol_index];
let section_index = symbol.section.ok_or_else(|| anyhow!("Missing section for symbol"))?;
let section = &obj.sections[section_index];
let data = section.data_range(symbol.address, symbol.size as usize).ok_or_else(|| {
@@ -33,18 +33,14 @@ pub fn no_diff_code(
)
})?;
let ops = obj.arch.scan_instructions(
symbol.address,
data,
section_index,
&section.relocations,
ResolvedSymbol { obj, symbol_index, symbol, section_index, section, data },
diff_config,
)?;
let mut instruction_rows = Vec::<InstructionDiffRow>::new();
for i in &ops {
instruction_rows
.push(InstructionDiffRow { ins_ref: Some(i.ins_ref), ..Default::default() });
instruction_rows.push(InstructionDiffRow { ins_ref: Some(*i), ..Default::default() });
}
resolve_branches(obj, section_index, &ops, &mut instruction_rows);
resolve_branches(&ops, &mut instruction_rows);
Ok(SymbolDiff { target_symbol: None, match_percent: None, diff_score: None, instruction_rows })
}
@@ -92,22 +88,30 @@ pub fn diff_code(
let left_section_idx = left_symbol.section.unwrap();
let right_section_idx = right_symbol.section.unwrap();
let left_ops = left_obj.arch.scan_instructions(
left_symbol.address,
left_data,
left_section_idx,
&left_section.relocations,
ResolvedSymbol {
obj: left_obj,
symbol_index: left_symbol_idx,
symbol: left_symbol,
section_index: left_section_idx,
section: left_section,
data: left_data,
},
diff_config,
)?;
let right_ops = right_obj.arch.scan_instructions(
right_symbol.address,
right_data,
right_section_idx,
&right_section.relocations,
ResolvedSymbol {
obj: right_obj,
symbol_index: right_symbol_idx,
symbol: right_symbol,
section_index: right_section_idx,
section: right_section,
data: right_data,
},
diff_config,
)?;
let (mut left_rows, mut right_rows) = diff_instructions(&left_ops, &right_ops)?;
resolve_branches(left_obj, left_section_idx, &left_ops, &mut left_rows);
resolve_branches(right_obj, right_section_idx, &right_ops, &mut right_rows);
resolve_branches(&left_ops, &mut left_rows);
resolve_branches(&right_ops, &mut right_rows);
let mut diff_state = InstructionDiffState::default();
for (left_row, right_row) in left_rows.iter_mut().zip(right_rows.iter_mut()) {
@@ -154,21 +158,21 @@ pub fn diff_code(
}
fn diff_instructions(
left_insts: &[ScannedInstruction],
right_insts: &[ScannedInstruction],
left_insts: &[InstructionRef],
right_insts: &[InstructionRef],
) -> Result<(Vec<InstructionDiffRow>, Vec<InstructionDiffRow>)> {
let left_ops = left_insts.iter().map(|i| i.ins_ref.opcode).collect::<Vec<_>>();
let right_ops = right_insts.iter().map(|i| i.ins_ref.opcode).collect::<Vec<_>>();
let left_ops = left_insts.iter().map(|i| i.opcode).collect::<Vec<_>>();
let right_ops = right_insts.iter().map(|i| i.opcode).collect::<Vec<_>>();
let ops = similar::capture_diff_slices(similar::Algorithm::Patience, &left_ops, &right_ops);
if ops.is_empty() {
ensure!(left_insts.len() == right_insts.len());
let left_diff = left_insts
.iter()
.map(|i| InstructionDiffRow { ins_ref: Some(i.ins_ref), ..Default::default() })
.map(|i| InstructionDiffRow { ins_ref: Some(*i), ..Default::default() })
.collect();
let right_diff = right_insts
.iter()
.map(|i| InstructionDiffRow { ins_ref: Some(i.ins_ref), ..Default::default() })
.map(|i| InstructionDiffRow { ins_ref: Some(*i), ..Default::default() })
.collect();
return Ok((left_diff, right_diff));
}
@@ -187,14 +191,17 @@ fn diff_instructions(
for op in ops {
let (_tag, left_range, right_range) = op.as_tag_tuple();
let len = left_range.len().max(right_range.len());
left_diff.extend(left_range.clone().map(|i| InstructionDiffRow {
ins_ref: Some(left_insts[i].ins_ref),
..Default::default()
}));
right_diff.extend(right_range.clone().map(|i| InstructionDiffRow {
ins_ref: Some(right_insts[i].ins_ref),
..Default::default()
}));
left_diff.extend(
left_range
.clone()
.map(|i| InstructionDiffRow { ins_ref: Some(left_insts[i]), ..Default::default() }),
);
right_diff.extend(
right_range.clone().map(|i| InstructionDiffRow {
ins_ref: Some(right_insts[i]),
..Default::default()
}),
);
if left_range.len() < len {
left_diff.extend((left_range.len()..len).map(|_| InstructionDiffRow::default()));
}
@@ -215,13 +222,7 @@ fn arg_to_string(arg: &InstructionArg, reloc: Option<ResolvedRelocation>) -> Str
}
}
fn resolve_branches(
obj: &Object,
section_index: usize,
ops: &[ScannedInstruction],
rows: &mut [InstructionDiffRow],
) {
let section = &obj.sections[section_index];
fn resolve_branches(ops: &[InstructionRef], rows: &mut [InstructionDiffRow]) {
let mut branch_idx = 0u32;
// Map addresses to indices
let mut addr_map = BTreeMap::<u64, u32>::new();
@@ -235,17 +236,7 @@ fn resolve_branches(
for ((i, ins_diff), ins) in
rows.iter_mut().enumerate().filter(|(_, row)| row.ins_ref.is_some()).zip(ops)
{
let branch_dest = if let Some(resolved) = section.relocation_at(obj, ins.ins_ref) {
if resolved.symbol.section == Some(section_index) {
// If the relocation target is in the same section, use it as the branch destination
resolved.symbol.address.checked_add_signed(resolved.relocation.addend)
} else {
None
}
} else {
ins.branch_dest
};
if let Some(ins_idx) = branch_dest.and_then(|a| addr_map.get(&a).copied()) {
if let Some(ins_idx) = ins.branch_dest.and_then(|a| addr_map.get(&a).copied()) {
match branches.entry(ins_idx) {
btree_map::Entry::Vacant(e) => {
ins_diff.branch_to = Some(InstructionBranchTo { ins_idx, branch_idx });
@@ -334,12 +325,11 @@ fn reloc_eq(
|| display_ins_data_literals(left_obj, left_ins)
== display_ins_data_literals(right_obj, right_ins))
}
(Some(_), None) => false,
(None, Some(_)) => {
// Match if possibly stripped weak symbol
symbol_name_addend_matches && right_reloc.symbol.flags.contains(SymbolFlag::Weak)
}
(None, None) => symbol_name_addend_matches,
(Some(_), None) | (None, None) => symbol_name_addend_matches,
}
}

View File

@@ -53,12 +53,11 @@ fn reloc_eq(
section_name_eq(left_obj, right_obj, sl, sr)
&& (symbol_name_addend_matches || address_eq(left, right))
}
(Some(_), None) => false,
(None, Some(_)) => {
// Match if possibly stripped weak symbol
symbol_name_addend_matches && right.symbol.flags.contains(SymbolFlag::Weak)
}
(None, None) => symbol_name_addend_matches,
(Some(_), None) | (None, None) => symbol_name_addend_matches,
}
}
@@ -274,7 +273,6 @@ pub fn diff_data_section(
// We only do this when all relocations on the left side match.
if left_section_diff.match_percent.unwrap_or(-1.0) < match_percent {
left_section_diff.match_percent = Some(match_percent);
right_section_diff.match_percent = Some(match_percent);
}
}
Ok((left_section_diff, right_section_diff))
@@ -413,7 +411,7 @@ pub fn diff_generic_section(
};
Ok((
SectionDiff { match_percent: Some(match_percent), data_diff: vec![], reloc_diff: vec![] },
SectionDiff { match_percent: Some(match_percent), data_diff: vec![], reloc_diff: vec![] },
SectionDiff { match_percent: None, data_diff: vec![], reloc_diff: vec![] },
))
}
@@ -454,7 +452,7 @@ pub fn diff_bss_section(
Ok((
SectionDiff { match_percent: Some(match_percent), data_diff: vec![], reloc_diff: vec![] },
SectionDiff { match_percent: Some(match_percent), data_diff: vec![], reloc_diff: vec![] },
SectionDiff { match_percent: None, data_diff: vec![], reloc_diff: vec![] },
))
}

View File

@@ -14,8 +14,9 @@ use regex::Regex;
use crate::{
diff::{DiffObjConfig, InstructionDiffKind, InstructionDiffRow, ObjectDiff, SymbolDiff},
obj::{
InstructionArg, InstructionArgValue, Object, ParsedInstruction, ResolvedInstructionRef,
ResolvedRelocation, SectionFlag, SectionKind, Symbol, SymbolFlag, SymbolKind,
FlowAnalysisValue, InstructionArg, InstructionArgValue, Object, ParsedInstruction,
ResolvedInstructionRef, ResolvedRelocation, SectionFlag, SectionKind, Symbol, SymbolFlag,
SymbolKind,
},
};
@@ -47,11 +48,12 @@ pub enum DiffText<'a> {
pub enum DiffTextColor {
#[default]
Normal, // Grey
Dim, // Dark grey
Bright, // White
Replace, // Blue
Delete, // Red
Insert, // Green
Dim, // Dark grey
Bright, // White
DataFlow, // Light blue
Replace, // Blue
Delete, // Red
Insert, // Green
Rotating(u8),
}
@@ -77,7 +79,7 @@ impl<'a> DiffTextSegment<'a> {
const EOL_SEGMENT: DiffTextSegment<'static> =
DiffTextSegment { text: DiffText::Eol, color: DiffTextColor::Normal, pad_to: 0 };
#[derive(Debug, Default, Clone, PartialEq, Eq)]
#[derive(Debug, Default, Clone)]
pub enum HighlightKind {
#[default]
None,
@@ -186,6 +188,11 @@ pub fn display_row(
}
let mut arg_idx = 0;
let mut displayed_relocation = false;
let analysis_result = if diff_config.show_data_flow {
obj.flow_analysis_results.get(&resolved.symbol.address)
} else {
None
};
obj.arch.display_instruction(resolved, diff_config, &mut |part| match part {
InstructionPart::Basic(text) => {
if text.chars().all(|c| c == ' ') {
@@ -205,16 +212,33 @@ pub fn display_row(
InstructionPart::Arg(arg) => {
let diff_index = ins_row.arg_diff.get(arg_idx).copied().unwrap_or_default();
arg_idx += 1;
match arg {
InstructionArg::Value(value) => cb(DiffTextSegment {
text: DiffText::Argument(value),
color: diff_index
if arg == InstructionArg::Reloc {
displayed_relocation = true;
}
let data_flow_value =
analysis_result.and_then(|result|
result.as_ref().get_argument_value_at_address(
ins_ref.address, (arg_idx - 1) as u8));
match (arg, data_flow_value, resolved.ins_ref.branch_dest) {
// If we have a flow analysis result, always use that over anything else.
(InstructionArg::Value(_) | InstructionArg::Reloc, Some(FlowAnalysisValue::Text(text)), _) => {
cb(DiffTextSegment {
text: DiffText::Argument(InstructionArgValue::Opaque(Cow::Borrowed(text))),
color: DiffTextColor::DataFlow,
pad_to: 0,
})
},
(InstructionArg::Value(value), None, _) => {
let color = diff_index
.get()
.map_or(base_color, |i| DiffTextColor::Rotating(i as u8)),
pad_to: 0,
}),
InstructionArg::Reloc => {
displayed_relocation = true;
.map_or(base_color, |i| DiffTextColor::Rotating(i as u8));
cb(DiffTextSegment {
text: DiffText::Argument(value),
color,
pad_to: 0,
})
},
(InstructionArg::Reloc, _, None) => {
let resolved = resolved.relocation.unwrap();
let color = diff_index
.get()
@@ -233,7 +257,9 @@ pub fn display_row(
}
Ok(())
}
InstructionArg::BranchDest(dest) => {
(InstructionArg::BranchDest(dest), _, _) |
// If the relocation was resolved to a branch destination, emit that instead.
(InstructionArg::Reloc, _, Some(dest)) => {
if let Some(addr) = dest.checked_sub(resolved.symbol.address) {
cb(DiffTextSegment {
text: DiffText::BranchDest(addr),
@@ -284,6 +310,18 @@ pub fn display_row(
Ok(())
}
impl PartialEq<HighlightKind> for HighlightKind {
fn eq(&self, other: &HighlightKind) -> bool {
match (self, other) {
(HighlightKind::Opcode(a), HighlightKind::Opcode(b)) => a == b,
(HighlightKind::Argument(a), HighlightKind::Argument(b)) => a.loose_eq(b),
(HighlightKind::Symbol(a), HighlightKind::Symbol(b)) => a == b,
(HighlightKind::Address(a), HighlightKind::Address(b)) => a == b,
_ => false,
}
}
}
impl PartialEq<DiffText<'_>> for HighlightKind {
fn eq(&self, other: &DiffText) -> bool {
match (self, other) {
@@ -684,24 +722,25 @@ pub fn display_sections(
.collect::<Vec<_>>();
if let Some(section_idx) = section_idx {
let section = &obj.sections[section_idx];
if section.kind == SectionKind::Unknown || section.flags.contains(SectionFlag::Hidden) {
if section.kind == SectionKind::Unknown {
// Skip unknown and hidden sections
continue;
}
let section_diff = &diff.sections[section_idx];
if section.kind == SectionKind::Code && reverse_fn_order {
symbols.sort_by(|a, b| {
let a_symbol = &obj.symbols[a.symbol];
let b_symbol = &obj.symbols[b.symbol];
symbol_sort_reverse(a_symbol, b_symbol)
});
} else {
symbols.sort_by(|a, b| {
let a_symbol = &obj.symbols[a.symbol];
let b_symbol = &obj.symbols[b.symbol];
symbol_sort(a_symbol, b_symbol)
});
}
let reverse_fn_order = section.kind == SectionKind::Code && reverse_fn_order;
symbols.sort_by(|a, b| {
let a = &obj.symbols[a.symbol];
let b = &obj.symbols[b.symbol];
section_symbol_sort(a, b)
.then_with(|| {
if reverse_fn_order {
b.address.cmp(&a.address)
} else {
a.address.cmp(&b.address)
}
})
.then_with(|| a.size.cmp(&b.size))
});
sections.push(SectionDisplay {
id: section.id.clone(),
name: if section.flags.contains(SectionFlag::Combined) {
@@ -739,14 +778,6 @@ fn section_symbol_sort(a: &Symbol, b: &Symbol) -> Ordering {
Ordering::Equal
}
fn symbol_sort(a: &Symbol, b: &Symbol) -> Ordering {
section_symbol_sort(a, b).then(a.address.cmp(&b.address)).then(a.size.cmp(&b.size))
}
fn symbol_sort_reverse(a: &Symbol, b: &Symbol) -> Ordering {
section_symbol_sort(a, b).then(b.address.cmp(&a.address)).then(b.size.cmp(&a.size))
}
pub fn display_ins_data_labels(obj: &Object, resolved: ResolvedInstructionRef) -> Vec<String> {
let Some(reloc) = resolved.relocation else {
return Vec::new();

View File

@@ -341,11 +341,25 @@ pub fn diff_objs(
if let (Some((right_obj, right_out)), Some((left_obj, left_out))) =
(right.as_mut(), left.as_mut())
{
if let Some(right_name) = &mapping_config.selecting_left {
generate_mapping_symbols(right_obj, right_name, left_obj, left_out, diff_config)?;
if let Some(right_name) = mapping_config.selecting_left.as_deref() {
generate_mapping_symbols(
left_obj,
left_out,
right_obj,
right_out,
MappingSymbol::Right(right_name),
diff_config,
)?;
}
if let Some(left_name) = &mapping_config.selecting_right {
generate_mapping_symbols(left_obj, left_name, right_obj, right_out, diff_config)?;
if let Some(left_name) = mapping_config.selecting_right.as_deref() {
generate_mapping_symbols(
left_obj,
left_out,
right_obj,
right_out,
MappingSymbol::Left(left_name),
diff_config,
)?;
}
}
@@ -356,17 +370,28 @@ pub fn diff_objs(
})
}
#[derive(Clone, Copy)]
enum MappingSymbol<'a> {
Left(&'a str),
Right(&'a str),
}
/// When we're selecting a symbol to use as a comparison, we'll create comparisons for all
/// symbols in the other object that match the selected symbol's section and kind. This allows
/// us to display match percentages for all symbols in the other object that could be selected.
fn generate_mapping_symbols(
base_obj: &Object,
base_name: &str,
target_obj: &Object,
target_out: &mut ObjectDiff,
left_obj: &Object,
left_out: &mut ObjectDiff,
right_obj: &Object,
right_out: &mut ObjectDiff,
mapping_symbol: MappingSymbol,
config: &DiffObjConfig,
) -> Result<()> {
let Some(base_symbol_ref) = symbol_ref_by_name(base_obj, base_name) else {
let (base_obj, base_name, target_obj) = match mapping_symbol {
MappingSymbol::Left(name) => (left_obj, name, right_obj),
MappingSymbol::Right(name) => (right_obj, name, left_obj),
};
let Some(base_symbol_ref) = base_obj.symbol_by_name(base_name) else {
return Ok(());
};
let base_section_kind = symbol_section_kind(base_obj, &base_obj.symbols[base_symbol_ref]);
@@ -377,32 +402,30 @@ fn generate_mapping_symbols(
{
continue;
}
match base_section_kind {
let (left_symbol_idx, right_symbol_idx) = match mapping_symbol {
MappingSymbol::Left(_) => (base_symbol_ref, target_symbol_index),
MappingSymbol::Right(_) => (target_symbol_index, base_symbol_ref),
};
let (left_diff, right_diff) = match base_section_kind {
SectionKind::Code => {
let (left_diff, _right_diff) =
diff_code(target_obj, base_obj, target_symbol_index, base_symbol_ref, config)?;
target_out.mapping_symbols.push(MappingSymbolDiff {
symbol_index: target_symbol_index,
symbol_diff: left_diff,
});
diff_code(left_obj, right_obj, left_symbol_idx, right_symbol_idx, config)
}
SectionKind::Data => {
let (left_diff, _right_diff) =
diff_data_symbol(target_obj, base_obj, target_symbol_index, base_symbol_ref)?;
target_out.mapping_symbols.push(MappingSymbolDiff {
symbol_index: target_symbol_index,
symbol_diff: left_diff,
});
diff_data_symbol(left_obj, right_obj, left_symbol_idx, right_symbol_idx)
}
SectionKind::Bss | SectionKind::Common => {
let (left_diff, _right_diff) =
diff_bss_symbol(target_obj, base_obj, target_symbol_index, base_symbol_ref)?;
target_out.mapping_symbols.push(MappingSymbolDiff {
symbol_index: target_symbol_index,
symbol_diff: left_diff,
});
diff_bss_symbol(left_obj, right_obj, left_symbol_idx, right_symbol_idx)
}
SectionKind::Unknown => {}
SectionKind::Unknown => continue,
}?;
match mapping_symbol {
MappingSymbol::Left(_) => right_out.mapping_symbols.push(MappingSymbolDiff {
symbol_index: right_symbol_idx,
symbol_diff: right_diff,
}),
MappingSymbol::Right(_) => left_out
.mapping_symbols
.push(MappingSymbolDiff { symbol_index: left_symbol_idx, symbol_diff: left_diff }),
}
}
Ok(())
@@ -434,10 +457,6 @@ pub struct MappingConfig {
pub selecting_right: Option<String>,
}
fn symbol_ref_by_name(obj: &Object, name: &str) -> Option<usize> {
obj.symbols.iter().position(|s| s.name == name)
}
fn apply_symbol_mappings(
left: &Object,
right: &Object,
@@ -449,25 +468,25 @@ fn apply_symbol_mappings(
// If we're selecting a symbol to use as a comparison, mark it as used
// This ensures that we don't match it to another symbol at any point
if let Some(left_name) = &mapping_config.selecting_left {
if let Some(left_symbol) = symbol_ref_by_name(left, left_name) {
if let Some(left_symbol) = left.symbol_by_name(left_name) {
left_used.insert(left_symbol);
}
}
if let Some(right_name) = &mapping_config.selecting_right {
if let Some(right_symbol) = symbol_ref_by_name(right, right_name) {
if let Some(right_symbol) = right.symbol_by_name(right_name) {
right_used.insert(right_symbol);
}
}
// Apply manual symbol mappings
for (left_name, right_name) in &mapping_config.mappings {
let Some(left_symbol_index) = symbol_ref_by_name(left, left_name) else {
let Some(left_symbol_index) = left.symbol_by_name(left_name) else {
continue;
};
if left_used.contains(&left_symbol_index) {
continue;
}
let Some(right_symbol_index) = symbol_ref_by_name(right, right_name) else {
let Some(right_symbol_index) = right.symbol_by_name(right_name) else {
continue;
};
if right_used.contains(&right_symbol_index) {
@@ -658,7 +677,11 @@ fn find_symbol(
/// Find matching sections between each object.
fn matching_sections(left: Option<&Object>, right: Option<&Object>) -> Result<Vec<SectionMatch>> {
let mut matches = Vec::new();
let mut matches = Vec::with_capacity(
left.as_ref()
.map_or(0, |o| o.sections.len())
.max(right.as_ref().map_or(0, |o| o.sections.len())),
);
if let Some(left) = left {
for (section_idx, section) in left.sections.iter().enumerate() {
if section.kind == SectionKind::Unknown {
@@ -666,7 +689,7 @@ fn matching_sections(left: Option<&Object>, right: Option<&Object>) -> Result<Ve
}
matches.push(SectionMatch {
left: Some(section_idx),
right: find_section(right, &section.name, section.kind),
right: find_section(right, &section.name, section.kind, &matches),
section_kind: section.kind,
});
}
@@ -689,6 +712,13 @@ fn matching_sections(left: Option<&Object>, right: Option<&Object>) -> Result<Ve
Ok(matches)
}
fn find_section(obj: Option<&Object>, name: &str, section_kind: SectionKind) -> Option<usize> {
obj?.sections.iter().position(|s| s.kind == section_kind && s.name == name)
fn find_section(
obj: Option<&Object>,
name: &str,
section_kind: SectionKind,
matches: &[SectionMatch],
) -> Option<usize> {
obj?.sections.iter().enumerate().position(|(i, s)| {
s.kind == section_kind && s.name == name && !matches.iter().any(|m| m.right == Some(i))
})
}

View File

@@ -57,7 +57,6 @@ flags! {
pub enum SectionFlag: u8 {
/// Section combined from multiple input sections
Combined,
Hidden,
}
}
@@ -99,19 +98,8 @@ impl fmt::Debug for SectionData {
impl Section {
pub fn data_range(&self, address: u64, size: usize) -> Option<&[u8]> {
let start = self.address;
let end = start + self.size;
if address >= start && address + size as u64 <= end {
let offset = (address - start) as usize;
Some(&self.data[offset..offset + size])
} else {
None
}
}
pub fn symbol_data(&self, symbol: &Symbol) -> Option<&[u8]> {
let offset = symbol.address.checked_sub(self.address)?;
self.data.get(offset as usize..offset as usize + symbol.size as usize)
let offset = address.checked_sub(self.address)?;
self.data.get(offset as usize..offset as usize + size)
}
// The alignment to use when "Combine data/text sections" is enabled.
@@ -130,7 +118,7 @@ impl Section {
Err(i) => self
.relocations
.get(i)
.take_if(|r| r.address < ins_ref.address + ins_ref.size as u64),
.filter(|r| r.address < ins_ref.address + ins_ref.size as u64),
}
.and_then(|relocation| {
let symbol = obj.symbols.get(relocation.target_symbol)?;
@@ -226,11 +214,6 @@ pub struct InstructionRef {
pub address: u64,
pub size: u8,
pub opcode: u16,
}
#[derive(Copy, Clone, Debug)]
pub struct ScannedInstruction {
pub ins_ref: InstructionRef,
pub branch_dest: Option<u64>,
}
@@ -250,6 +233,19 @@ pub enum SymbolKind {
Section,
}
#[derive(Debug)]
pub enum FlowAnalysisValue {
Text(String),
}
pub trait FlowAnalysisResult: core::fmt::Debug + Send {
fn get_argument_value_at_address(
&self,
address: u64,
argument: u8,
) -> Option<&FlowAnalysisValue>;
}
#[derive(Debug, Clone, Eq, PartialEq, Hash, Default)]
pub struct Symbol {
pub name: String,
@@ -277,6 +273,7 @@ pub struct Object {
pub path: Option<std::path::PathBuf>,
#[cfg(feature = "std")]
pub timestamp: Option<filetime::FileTime>,
pub flow_analysis_results: BTreeMap<u64, Box<dyn FlowAnalysisResult>>,
}
impl Default for Object {
@@ -291,6 +288,7 @@ impl Default for Object {
path: None,
#[cfg(feature = "std")]
timestamp: None,
flow_analysis_results: BTreeMap::<u64, Box<dyn FlowAnalysisResult>>::new(),
}
}
}
@@ -325,6 +323,12 @@ impl Object {
let offset = symbol.address.checked_sub(section.address)?;
section.data.get(offset as usize..offset as usize + symbol.size as usize)
}
pub fn symbol_by_name(&self, name: &str) -> Option<usize> {
self.symbols.iter().position(|symbol| symbol.section.is_some() && symbol.name == name)
}
pub fn has_flow_analysis_result(&self) -> bool { !self.flow_analysis_results.is_empty() }
}
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
@@ -347,6 +351,16 @@ pub struct ResolvedRelocation<'a> {
pub symbol: &'a Symbol,
}
#[derive(Debug, Copy, Clone)]
pub struct ResolvedSymbol<'obj> {
pub obj: &'obj Object,
pub symbol_index: usize,
pub symbol: &'obj Symbol,
pub section_index: usize,
pub section: &'obj Section,
pub data: &'obj [u8],
}
#[derive(Debug, Copy, Clone)]
pub struct ResolvedInstructionRef<'obj> {
pub ins_ref: InstructionRef,

View File

@@ -121,6 +121,15 @@ fn map_symbols(
Ok((symbols, symbol_indices))
}
/// When inferring a symbol's size, we ignore symbols that start with specific prefixes. They are
/// usually emitted as branch targets and do not represent the start of a function or object.
fn is_local_label(symbol: &Symbol) -> bool {
const LABEL_PREFIXES: &[&str] = &[".L", "LAB_"];
symbol.size == 0
&& symbol.flags.contains(SymbolFlag::Local)
&& LABEL_PREFIXES.iter().any(|p| symbol.name.starts_with(p))
}
fn infer_symbol_sizes(symbols: &mut [Symbol], sections: &[Section]) {
// Create a sorted list of symbol indices by section
let mut symbols_with_section = Vec::<usize>::with_capacity(symbols.len());
@@ -167,27 +176,28 @@ fn infer_symbol_sizes(symbols: &mut [Symbol], sections: &[Section]) {
if last_end.0 == section_idx && last_end.1 > symbol.address {
continue;
}
let next_symbol = match symbol.kind {
// For function/object symbols, find the next function/object symbol (in other words:
// skip over labels)
SymbolKind::Function | SymbolKind::Object => loop {
if iter_idx >= symbols_with_section.len() {
break None;
let next_symbol = loop {
if iter_idx >= symbols_with_section.len() {
break None;
}
let next_symbol = &symbols[symbols_with_section[iter_idx]];
if next_symbol.section != Some(section_idx) {
break None;
}
if match symbol.kind {
SymbolKind::Function | SymbolKind::Object => {
// For function/object symbols, find the next function/object
matches!(next_symbol.kind, SymbolKind::Function | SymbolKind::Object)
}
let next_symbol = &symbols[symbols_with_section[iter_idx]];
if next_symbol.section != Some(section_idx) {
break None;
SymbolKind::Unknown | SymbolKind::Section => {
// For labels (or anything else), stop at any symbol
true
}
if let SymbolKind::Function | SymbolKind::Object = next_symbol.kind {
break Some(next_symbol);
}
iter_idx += 1;
},
// For labels (or anything else), simply use the next symbol's address
SymbolKind::Unknown | SymbolKind::Section => symbols_with_section
.get(iter_idx)
.map(|&i| &symbols[i])
.take_if(|s| s.section == Some(section_idx)),
} && !is_local_label(next_symbol)
{
break Some(next_symbol);
}
iter_idx += 1;
};
let next_address = next_symbol.map(|s| s.address).unwrap_or_else(|| {
let section = &sections[section_idx];
@@ -341,7 +351,7 @@ fn map_section_relocations(
let idx = if let Some(section_symbol) = obj_file
.symbol_by_index(idx)
.ok()
.take_if(|s| s.kind() == object::SymbolKind::Section)
.filter(|s| s.kind() == object::SymbolKind::Section)
{
let section_index =
section_symbol.section_index().context("Section symbol without section")?;
@@ -422,17 +432,18 @@ fn map_relocations(
Ok(())
}
fn calculate_pooled_relocations(
arch: &dyn Arch,
sections: &mut [Section],
symbols: &[Symbol],
) -> Result<()> {
for (section_index, section) in sections.iter_mut().enumerate() {
fn perform_data_flow_analysis(obj: &mut Object, config: &DiffObjConfig) -> Result<()> {
// If neither of these settings are on, no flow analysis to perform
if !config.analyze_data_flow && !config.ppc_calculate_pool_relocations {
return Ok(());
}
let mut generated_relocations = Vec::<(usize, Vec<Relocation>)>::new();
for (section_index, section) in obj.sections.iter().enumerate() {
if section.kind != SectionKind::Code {
continue;
}
let mut fake_pool_relocs = Vec::new();
for symbol in symbols {
for symbol in obj.symbols.iter() {
if symbol.section != Some(section_index) {
continue;
}
@@ -447,14 +458,32 @@ fn calculate_pooled_relocations(
symbol.address + symbol.size
)
})?;
fake_pool_relocs.append(&mut arch.generate_pooled_relocations(
symbol.address,
code,
&section.relocations,
symbols,
));
// Optional pooled relocation computation
// Long view: This could be replaced by the full data flow analysis
// once that feature has stabilized.
if config.ppc_calculate_pool_relocations {
let relocations = obj.arch.generate_pooled_relocations(
symbol.address,
code,
&section.relocations,
&obj.symbols,
);
generated_relocations.push((section_index, relocations));
}
// Optional full data flow analysis
if config.analyze_data_flow {
obj.arch.data_flow_analysis(obj, symbol, code, &section.relocations).and_then(
|flow_result| obj.flow_analysis_results.insert(symbol.address, flow_result),
);
}
}
section.relocations.append(&mut fake_pool_relocs);
}
for (section_index, mut relocations) in generated_relocations {
obj.sections[section_index].relocations.append(&mut relocations);
}
for section in obj.sections.iter_mut() {
section.relocations.sort_by_key(|r| r.address);
}
Ok(())
@@ -764,7 +793,7 @@ fn do_combine_sections(
line_info.append(&mut section.line_info.iter().map(|(&a, &l)| (a + offset, l)).collect());
section.line_info.clear();
if offset > 0 {
section.flags |= SectionFlag::Hidden;
section.kind = SectionKind::Unknown;
}
}
{
@@ -855,15 +884,12 @@ pub fn parse(data: &[u8], config: &DiffObjConfig) -> Result<Object> {
let (mut symbols, symbol_indices) =
map_symbols(arch.as_ref(), &obj_file, &sections, &section_indices, split_meta.as_ref())?;
map_relocations(arch.as_ref(), &obj_file, &mut sections, &section_indices, &symbol_indices)?;
if config.ppc_calculate_pool_relocations {
calculate_pooled_relocations(arch.as_ref(), &mut sections, &symbols)?;
}
parse_line_info(&obj_file, &mut sections, &section_indices, data)?;
if config.combine_data_sections || config.combine_text_sections {
combine_sections(&mut sections, &mut symbols, config)?;
}
arch.post_init(&sections, &symbols);
Ok(Object {
let mut obj = Object {
arch,
endianness: obj_file.endianness(),
symbols,
@@ -873,7 +899,14 @@ pub fn parse(data: &[u8], config: &DiffObjConfig) -> Result<Object> {
path: None,
#[cfg(feature = "std")]
timestamp: None,
})
flow_analysis_results: Default::default(),
};
// Need to construct the obj first so that we have a convinient package to
// pass to flow analysis. Then the flow analysis will mutate obj adding
// additional data to it.
perform_data_flow_analysis(&mut obj, config)?;
Ok(obj)
}
#[cfg(feature = "std")]

View File

@@ -84,11 +84,11 @@ expression: "(sections, symbols)"
name: ".data",
address: 0,
size: 0,
kind: Data,
kind: Unknown,
data: SectionData(
0,
),
flags: FlagSet(Hidden),
flags: FlagSet(),
align: None,
relocations: [],
line_info: {},
@@ -99,11 +99,11 @@ expression: "(sections, symbols)"
name: ".data",
address: 0,
size: 0,
kind: Data,
kind: Unknown,
data: SectionData(
0,
),
flags: FlagSet(Hidden),
flags: FlagSet(),
align: None,
relocations: [],
line_info: {},

View File

@@ -57,6 +57,35 @@ pub fn align_data_to_4<W: std::io::Write + ?Sized>(
pub fn align_u64_to(len: u64, align: u64) -> u64 { len + ((align - (len % align)) % align) }
pub fn align_data_slice_to(data: &mut Vec<u8>, align: u64) {
const ALIGN_BYTE: u8 = 0;
data.resize(align_u64_to(data.len() as u64, align) as usize, ALIGN_BYTE);
data.resize(align_u64_to(data.len() as u64, align) as usize, 0);
}
// Float where we specifically care about comparing the raw bits rather than
// caring about IEEE semantics.
#[derive(Copy, Clone, Debug)]
pub struct RawFloat(pub f32);
impl PartialEq for RawFloat {
fn eq(&self, other: &Self) -> bool { self.0.to_bits() == other.0.to_bits() }
}
impl Eq for RawFloat {}
impl Ord for RawFloat {
fn cmp(&self, other: &Self) -> core::cmp::Ordering { self.0.to_bits().cmp(&other.0.to_bits()) }
}
impl PartialOrd for RawFloat {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> { Some(self.cmp(other)) }
}
// Double where we specifically care about comparing the raw bits rather than
// caring about IEEE semantics.
#[derive(Copy, Clone, Debug)]
pub struct RawDouble(pub f64);
impl PartialEq for RawDouble {
fn eq(&self, other: &Self) -> bool { self.0.to_bits() == other.0.to_bits() }
}
impl Eq for RawDouble {}
impl Ord for RawDouble {
fn cmp(&self, other: &Self) -> core::cmp::Ordering { self.0.to_bits().cmp(&other.0.to_bits()) }
}
impl PartialOrd for RawDouble {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> { Some(self.cmp(other)) }
}

View File

@@ -68,3 +68,12 @@ fn read_x86_jumptable() {
let output = common::display_diff(&obj, &diff, symbol_idx, &diff_config);
insta::assert_snapshot!(output);
}
// Inferred size of functions should ignore symbols with specific prefixes
#[test]
#[cfg(feature = "x86")]
fn read_x86_local_labels() {
let diff_config = diff::DiffObjConfig::default();
let obj = obj::read::parse(include_object!("data/x86/local_labels.obj"), &diff_config).unwrap();
insta::assert_debug_snapshot!(obj);
}

Binary file not shown.

View File

@@ -1,6 +1,5 @@
---
source: objdiff-core/tests/arch_arm.rs
assertion_line: 43
expression: diff.instruction_rows
---
[
@@ -10,6 +9,7 @@ expression: diff.instruction_rows
address: 76,
size: 4,
opcode: 32799,
branch_dest: None,
},
),
kind: None,
@@ -23,6 +23,7 @@ expression: diff.instruction_rows
address: 80,
size: 4,
opcode: 32779,
branch_dest: None,
},
),
kind: None,
@@ -36,6 +37,7 @@ expression: diff.instruction_rows
address: 84,
size: 4,
opcode: 65535,
branch_dest: None,
},
),
kind: None,

View File

@@ -9,6 +9,7 @@ expression: diff.instruction_rows
address: 40,
size: 4,
opcode: 32895,
branch_dest: None,
},
),
kind: None,
@@ -22,6 +23,7 @@ expression: diff.instruction_rows
address: 44,
size: 4,
opcode: 32818,
branch_dest: None,
},
),
kind: None,
@@ -35,6 +37,7 @@ expression: diff.instruction_rows
address: 48,
size: 4,
opcode: 32818,
branch_dest: None,
},
),
kind: None,
@@ -48,6 +51,7 @@ expression: diff.instruction_rows
address: 52,
size: 4,
opcode: 32774,
branch_dest: None,
},
),
kind: None,
@@ -61,6 +65,7 @@ expression: diff.instruction_rows
address: 56,
size: 4,
opcode: 32799,
branch_dest: None,
},
),
kind: None,
@@ -74,6 +79,7 @@ expression: diff.instruction_rows
address: 60,
size: 4,
opcode: 32786,
branch_dest: None,
},
),
kind: None,
@@ -87,6 +93,7 @@ expression: diff.instruction_rows
address: 64,
size: 4,
opcode: 32770,
branch_dest: None,
},
),
kind: None,
@@ -100,6 +107,9 @@ expression: diff.instruction_rows
address: 68,
size: 4,
opcode: 32773,
branch_dest: Some(
240,
),
},
),
kind: None,
@@ -118,6 +128,9 @@ expression: diff.instruction_rows
address: 72,
size: 4,
opcode: 32773,
branch_dest: Some(
240,
),
},
),
kind: None,
@@ -136,6 +149,9 @@ expression: diff.instruction_rows
address: 76,
size: 4,
opcode: 32773,
branch_dest: Some(
240,
),
},
),
kind: None,
@@ -154,6 +170,9 @@ expression: diff.instruction_rows
address: 80,
size: 4,
opcode: 32773,
branch_dest: Some(
240,
),
},
),
kind: None,
@@ -172,6 +191,9 @@ expression: diff.instruction_rows
address: 84,
size: 4,
opcode: 32773,
branch_dest: Some(
232,
),
},
),
kind: None,
@@ -190,6 +212,9 @@ expression: diff.instruction_rows
address: 88,
size: 4,
opcode: 32773,
branch_dest: Some(
164,
),
},
),
kind: None,
@@ -208,6 +233,9 @@ expression: diff.instruction_rows
address: 92,
size: 4,
opcode: 32773,
branch_dest: Some(
240,
),
},
),
kind: None,
@@ -226,6 +254,9 @@ expression: diff.instruction_rows
address: 96,
size: 4,
opcode: 32773,
branch_dest: Some(
180,
),
},
),
kind: None,
@@ -244,6 +275,9 @@ expression: diff.instruction_rows
address: 100,
size: 4,
opcode: 32773,
branch_dest: Some(
116,
),
},
),
kind: None,
@@ -262,6 +296,9 @@ expression: diff.instruction_rows
address: 104,
size: 4,
opcode: 32773,
branch_dest: Some(
192,
),
},
),
kind: None,
@@ -280,6 +317,9 @@ expression: diff.instruction_rows
address: 108,
size: 4,
opcode: 32773,
branch_dest: Some(
204,
),
},
),
kind: None,
@@ -298,6 +338,9 @@ expression: diff.instruction_rows
address: 112,
size: 4,
opcode: 32773,
branch_dest: Some(
204,
),
},
),
kind: None,
@@ -316,6 +359,7 @@ expression: diff.instruction_rows
address: 116,
size: 4,
opcode: 32799,
branch_dest: None,
},
),
kind: None,
@@ -336,6 +380,7 @@ expression: diff.instruction_rows
address: 120,
size: 4,
opcode: 32799,
branch_dest: None,
},
),
kind: None,
@@ -349,6 +394,7 @@ expression: diff.instruction_rows
address: 124,
size: 4,
opcode: 32774,
branch_dest: None,
},
),
kind: None,
@@ -362,6 +408,7 @@ expression: diff.instruction_rows
address: 128,
size: 4,
opcode: 32800,
branch_dest: None,
},
),
kind: None,
@@ -375,6 +422,7 @@ expression: diff.instruction_rows
address: 132,
size: 4,
opcode: 32786,
branch_dest: None,
},
),
kind: None,
@@ -388,6 +436,9 @@ expression: diff.instruction_rows
address: 136,
size: 4,
opcode: 32773,
branch_dest: Some(
148,
),
},
),
kind: None,
@@ -406,6 +457,9 @@ expression: diff.instruction_rows
address: 140,
size: 4,
opcode: 32774,
branch_dest: Some(
464,
),
},
),
kind: None,
@@ -424,6 +478,7 @@ expression: diff.instruction_rows
address: 144,
size: 4,
opcode: 32774,
branch_dest: None,
},
),
kind: None,
@@ -437,6 +492,7 @@ expression: diff.instruction_rows
address: 148,
size: 4,
opcode: 32799,
branch_dest: None,
},
),
kind: None,
@@ -457,6 +513,7 @@ expression: diff.instruction_rows
address: 152,
size: 4,
opcode: 32799,
branch_dest: None,
},
),
kind: None,
@@ -470,6 +527,7 @@ expression: diff.instruction_rows
address: 156,
size: 4,
opcode: 32777,
branch_dest: None,
},
),
kind: None,
@@ -483,6 +541,9 @@ expression: diff.instruction_rows
address: 160,
size: 4,
opcode: 32773,
branch_dest: Some(
240,
),
},
),
kind: None,
@@ -501,6 +562,7 @@ expression: diff.instruction_rows
address: 164,
size: 4,
opcode: 32818,
branch_dest: None,
},
),
kind: None,
@@ -521,6 +583,7 @@ expression: diff.instruction_rows
address: 168,
size: 4,
opcode: 32818,
branch_dest: None,
},
),
kind: None,
@@ -534,6 +597,7 @@ expression: diff.instruction_rows
address: 172,
size: 4,
opcode: 32774,
branch_dest: None,
},
),
kind: None,
@@ -547,6 +611,9 @@ expression: diff.instruction_rows
address: 176,
size: 4,
opcode: 32773,
branch_dest: Some(
240,
),
},
),
kind: None,
@@ -565,6 +632,7 @@ expression: diff.instruction_rows
address: 180,
size: 4,
opcode: 32818,
branch_dest: None,
},
),
kind: None,
@@ -585,6 +653,7 @@ expression: diff.instruction_rows
address: 184,
size: 4,
opcode: 32774,
branch_dest: None,
},
),
kind: None,
@@ -598,6 +667,9 @@ expression: diff.instruction_rows
address: 188,
size: 4,
opcode: 32773,
branch_dest: Some(
240,
),
},
),
kind: None,
@@ -616,6 +688,7 @@ expression: diff.instruction_rows
address: 192,
size: 4,
opcode: 32818,
branch_dest: None,
},
),
kind: None,
@@ -636,6 +709,7 @@ expression: diff.instruction_rows
address: 196,
size: 4,
opcode: 32774,
branch_dest: None,
},
),
kind: None,
@@ -649,6 +723,9 @@ expression: diff.instruction_rows
address: 200,
size: 4,
opcode: 32773,
branch_dest: Some(
240,
),
},
),
kind: None,
@@ -667,6 +744,7 @@ expression: diff.instruction_rows
address: 204,
size: 4,
opcode: 32799,
branch_dest: None,
},
),
kind: None,
@@ -688,6 +766,7 @@ expression: diff.instruction_rows
address: 208,
size: 4,
opcode: 32818,
branch_dest: None,
},
),
kind: None,
@@ -701,6 +780,7 @@ expression: diff.instruction_rows
address: 212,
size: 4,
opcode: 32799,
branch_dest: None,
},
),
kind: None,
@@ -714,6 +794,7 @@ expression: diff.instruction_rows
address: 216,
size: 4,
opcode: 32818,
branch_dest: None,
},
),
kind: None,
@@ -727,6 +808,7 @@ expression: diff.instruction_rows
address: 220,
size: 4,
opcode: 32899,
branch_dest: None,
},
),
kind: None,
@@ -740,6 +822,7 @@ expression: diff.instruction_rows
address: 224,
size: 4,
opcode: 32774,
branch_dest: None,
},
),
kind: None,
@@ -753,6 +836,9 @@ expression: diff.instruction_rows
address: 228,
size: 4,
opcode: 32773,
branch_dest: Some(
240,
),
},
),
kind: None,
@@ -771,6 +857,7 @@ expression: diff.instruction_rows
address: 232,
size: 4,
opcode: 32818,
branch_dest: None,
},
),
kind: None,
@@ -791,6 +878,7 @@ expression: diff.instruction_rows
address: 236,
size: 4,
opcode: 32774,
branch_dest: None,
},
),
kind: None,
@@ -804,6 +892,7 @@ expression: diff.instruction_rows
address: 240,
size: 4,
opcode: 32799,
branch_dest: None,
},
),
kind: None,
@@ -833,6 +922,7 @@ expression: diff.instruction_rows
address: 244,
size: 4,
opcode: 32829,
branch_dest: None,
},
),
kind: None,
@@ -846,6 +936,7 @@ expression: diff.instruction_rows
address: 248,
size: 4,
opcode: 32786,
branch_dest: None,
},
),
kind: None,
@@ -859,6 +950,9 @@ expression: diff.instruction_rows
address: 252,
size: 4,
opcode: 32773,
branch_dest: Some(
276,
),
},
),
kind: None,
@@ -877,6 +971,7 @@ expression: diff.instruction_rows
address: 256,
size: 4,
opcode: 32818,
branch_dest: None,
},
),
kind: None,
@@ -890,6 +985,7 @@ expression: diff.instruction_rows
address: 260,
size: 4,
opcode: 32774,
branch_dest: None,
},
),
kind: None,
@@ -903,6 +999,7 @@ expression: diff.instruction_rows
address: 264,
size: 4,
opcode: 32799,
branch_dest: None,
},
),
kind: None,
@@ -916,6 +1013,7 @@ expression: diff.instruction_rows
address: 268,
size: 4,
opcode: 32799,
branch_dest: None,
},
),
kind: None,
@@ -929,6 +1027,7 @@ expression: diff.instruction_rows
address: 272,
size: 4,
opcode: 32778,
branch_dest: None,
},
),
kind: None,
@@ -942,6 +1041,7 @@ expression: diff.instruction_rows
address: 276,
size: 4,
opcode: 32799,
branch_dest: None,
},
),
kind: None,
@@ -962,6 +1062,7 @@ expression: diff.instruction_rows
address: 280,
size: 4,
opcode: 32786,
branch_dest: None,
},
),
kind: None,
@@ -975,6 +1076,9 @@ expression: diff.instruction_rows
address: 284,
size: 4,
opcode: 32773,
branch_dest: Some(
328,
),
},
),
kind: None,
@@ -993,6 +1097,9 @@ expression: diff.instruction_rows
address: 288,
size: 4,
opcode: 32773,
branch_dest: Some(
336,
),
},
),
kind: None,
@@ -1011,6 +1118,7 @@ expression: diff.instruction_rows
address: 292,
size: 4,
opcode: 32786,
branch_dest: None,
},
),
kind: None,
@@ -1024,6 +1132,9 @@ expression: diff.instruction_rows
address: 296,
size: 4,
opcode: 32773,
branch_dest: Some(
348,
),
},
),
kind: None,
@@ -1042,6 +1153,7 @@ expression: diff.instruction_rows
address: 300,
size: 4,
opcode: 32829,
branch_dest: None,
},
),
kind: None,
@@ -1055,6 +1167,7 @@ expression: diff.instruction_rows
address: 304,
size: 4,
opcode: 32786,
branch_dest: None,
},
),
kind: None,
@@ -1068,6 +1181,9 @@ expression: diff.instruction_rows
address: 308,
size: 4,
opcode: 32773,
branch_dest: Some(
348,
),
},
),
kind: None,
@@ -1086,6 +1202,7 @@ expression: diff.instruction_rows
address: 312,
size: 4,
opcode: 32786,
branch_dest: None,
},
),
kind: None,
@@ -1099,6 +1216,7 @@ expression: diff.instruction_rows
address: 316,
size: 4,
opcode: 32786,
branch_dest: None,
},
),
kind: None,
@@ -1112,6 +1230,9 @@ expression: diff.instruction_rows
address: 320,
size: 4,
opcode: 32773,
branch_dest: Some(
380,
),
},
),
kind: None,
@@ -1130,6 +1251,9 @@ expression: diff.instruction_rows
address: 324,
size: 4,
opcode: 32773,
branch_dest: Some(
348,
),
},
),
kind: None,
@@ -1148,6 +1272,7 @@ expression: diff.instruction_rows
address: 328,
size: 4,
opcode: 32786,
branch_dest: None,
},
),
kind: None,
@@ -1168,6 +1293,9 @@ expression: diff.instruction_rows
address: 332,
size: 4,
opcode: 32773,
branch_dest: Some(
348,
),
},
),
kind: None,
@@ -1186,6 +1314,7 @@ expression: diff.instruction_rows
address: 336,
size: 4,
opcode: 32818,
branch_dest: None,
},
),
kind: None,
@@ -1206,6 +1335,7 @@ expression: diff.instruction_rows
address: 340,
size: 4,
opcode: 32774,
branch_dest: None,
},
),
kind: None,
@@ -1219,6 +1349,9 @@ expression: diff.instruction_rows
address: 344,
size: 4,
opcode: 32773,
branch_dest: Some(
380,
),
},
),
kind: None,
@@ -1237,6 +1370,7 @@ expression: diff.instruction_rows
address: 348,
size: 4,
opcode: 32818,
branch_dest: None,
},
),
kind: None,
@@ -1260,6 +1394,7 @@ expression: diff.instruction_rows
address: 352,
size: 4,
opcode: 32774,
branch_dest: None,
},
),
kind: None,
@@ -1273,6 +1408,7 @@ expression: diff.instruction_rows
address: 356,
size: 4,
opcode: 32786,
branch_dest: None,
},
),
kind: None,
@@ -1286,6 +1422,7 @@ expression: diff.instruction_rows
address: 360,
size: 4,
opcode: 32786,
branch_dest: None,
},
),
kind: None,
@@ -1299,6 +1436,9 @@ expression: diff.instruction_rows
address: 364,
size: 4,
opcode: 32773,
branch_dest: Some(
380,
),
},
),
kind: None,
@@ -1317,6 +1457,7 @@ expression: diff.instruction_rows
address: 368,
size: 4,
opcode: 32774,
branch_dest: None,
},
),
kind: None,
@@ -1330,6 +1471,7 @@ expression: diff.instruction_rows
address: 372,
size: 4,
opcode: 32818,
branch_dest: None,
},
),
kind: None,
@@ -1343,6 +1485,7 @@ expression: diff.instruction_rows
address: 376,
size: 4,
opcode: 32899,
branch_dest: None,
},
),
kind: None,
@@ -1356,6 +1499,7 @@ expression: diff.instruction_rows
address: 380,
size: 4,
opcode: 32829,
branch_dest: None,
},
),
kind: None,
@@ -1378,6 +1522,7 @@ expression: diff.instruction_rows
address: 384,
size: 4,
opcode: 32770,
branch_dest: None,
},
),
kind: None,
@@ -1391,6 +1536,7 @@ expression: diff.instruction_rows
address: 388,
size: 4,
opcode: 32770,
branch_dest: None,
},
),
kind: None,
@@ -1404,6 +1550,7 @@ expression: diff.instruction_rows
address: 392,
size: 4,
opcode: 32898,
branch_dest: None,
},
),
kind: None,
@@ -1417,6 +1564,7 @@ expression: diff.instruction_rows
address: 396,
size: 4,
opcode: 32786,
branch_dest: None,
},
),
kind: None,
@@ -1430,6 +1578,9 @@ expression: diff.instruction_rows
address: 400,
size: 4,
opcode: 32773,
branch_dest: Some(
424,
),
},
),
kind: None,
@@ -1448,6 +1599,7 @@ expression: diff.instruction_rows
address: 404,
size: 4,
opcode: 32818,
branch_dest: None,
},
),
kind: None,
@@ -1468,6 +1620,7 @@ expression: diff.instruction_rows
address: 408,
size: 4,
opcode: 32774,
branch_dest: None,
},
),
kind: None,
@@ -1481,6 +1634,7 @@ expression: diff.instruction_rows
address: 412,
size: 4,
opcode: 32770,
branch_dest: None,
},
),
kind: None,
@@ -1494,6 +1648,7 @@ expression: diff.instruction_rows
address: 416,
size: 4,
opcode: 32786,
branch_dest: None,
},
),
kind: None,
@@ -1507,6 +1662,9 @@ expression: diff.instruction_rows
address: 420,
size: 4,
opcode: 32773,
branch_dest: Some(
404,
),
},
),
kind: None,
@@ -1525,6 +1683,7 @@ expression: diff.instruction_rows
address: 424,
size: 4,
opcode: 32799,
branch_dest: None,
},
),
kind: None,
@@ -1545,6 +1704,7 @@ expression: diff.instruction_rows
address: 428,
size: 4,
opcode: 32799,
branch_dest: None,
},
),
kind: None,
@@ -1558,6 +1718,7 @@ expression: diff.instruction_rows
address: 432,
size: 4,
opcode: 32800,
branch_dest: None,
},
),
kind: None,
@@ -1571,6 +1732,7 @@ expression: diff.instruction_rows
address: 436,
size: 4,
opcode: 32786,
branch_dest: None,
},
),
kind: None,
@@ -1584,6 +1746,9 @@ expression: diff.instruction_rows
address: 440,
size: 4,
opcode: 32773,
branch_dest: Some(
448,
),
},
),
kind: None,
@@ -1602,6 +1767,7 @@ expression: diff.instruction_rows
address: 444,
size: 4,
opcode: 32774,
branch_dest: None,
},
),
kind: None,
@@ -1615,6 +1781,7 @@ expression: diff.instruction_rows
address: 448,
size: 4,
opcode: 32818,
branch_dest: None,
},
),
kind: None,
@@ -1635,6 +1802,7 @@ expression: diff.instruction_rows
address: 452,
size: 4,
opcode: 32899,
branch_dest: None,
},
),
kind: None,
@@ -1648,6 +1816,7 @@ expression: diff.instruction_rows
address: 456,
size: 4,
opcode: 32793,
branch_dest: None,
},
),
kind: None,
@@ -1661,6 +1830,7 @@ expression: diff.instruction_rows
address: 460,
size: 4,
opcode: 65535,
branch_dest: None,
},
),
kind: None,
@@ -1674,6 +1844,7 @@ expression: diff.instruction_rows
address: 464,
size: 4,
opcode: 65535,
branch_dest: None,
},
),
kind: None,
@@ -1694,6 +1865,7 @@ expression: diff.instruction_rows
address: 468,
size: 4,
opcode: 65535,
branch_dest: None,
},
),
kind: None,

View File

@@ -1,6 +1,5 @@
---
source: objdiff-core/tests/arch_arm.rs
assertion_line: 16
expression: output
---
[(Address(0), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stmdb", 32895), Normal, 10), (Argument(Opaque("sp")), Normal, 0), (Argument(Opaque("!")), Normal, 0), (Basic(", "), Normal, 0), (Basic("{"), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r6")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("lr")), Normal, 0), (Basic("}"), Normal, 0), (Eol, Normal, 0)]
@@ -28,7 +27,7 @@ expression: output
[(Address(88), Normal, 5), (Spacing(4), Normal, 0), (Opcode("ldrb", 32800), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(224)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
[(Address(92), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmp", 32786), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(0)), Normal, 0), (Eol, Normal, 0)]
[(Address(96), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 32773), Normal, 10), (BranchDest(108), Normal, 0), (Basic(" ~>"), Rotating(7), 0), (Eol, Normal, 0)]
[(Address(100), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32774), Normal, 10), (Symbol(Symbol { name: "_ZN13LinkStateItem15GetEquipBombchuEv", demangled_name: Some("LinkStateItem::GetEquipBombchu()"), address: 472, size: 16, kind: Function, section: Some(0), flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Addend(-8), Bright, 0), (Basic(" ~>"), Rotating(8), 0), (Eol, Normal, 0)]
[(Address(100), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32774), Normal, 10), (BranchDest(424), Normal, 0), (Basic(" ~>"), Rotating(8), 0), (Eol, Normal, 0)]
[(Address(104), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32774), Normal, 10), (Symbol(Symbol { name: "_ZN12EquipBombchu19func_ov014_0213ec64Ev", demangled_name: Some("EquipBombchu::func_ov014_0213ec64()"), address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global | Weak), align: None, virtual_address: None }), Bright, 0), (Addend(-8), Bright, 0), (Eol, Normal, 0)]
[(Address(108), Normal, 5), (Basic(" ~> "), Rotating(7), 0), (Opcode("ldr", 32799), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("pc")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(308)), Normal, 0), (Basic("]"), Normal, 0), (Basic(" (->"), Normal, 0), (BranchDest(424), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(112), Normal, 5), (Spacing(4), Normal, 0), (Opcode("ldr", 32799), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(0)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]

View File

@@ -1954,4 +1954,5 @@ Object {
split_meta: None,
path: None,
timestamp: None,
flow_analysis_results: {},
}

View File

@@ -9,6 +9,7 @@ expression: diff.instruction_rows
address: 0,
size: 2,
opcode: 56,
branch_dest: None,
},
),
kind: None,
@@ -22,6 +23,7 @@ expression: diff.instruction_rows
address: 2,
size: 2,
opcode: 74,
branch_dest: None,
},
),
kind: None,
@@ -35,6 +37,7 @@ expression: diff.instruction_rows
address: 4,
size: 2,
opcode: 47,
branch_dest: None,
},
),
kind: None,
@@ -48,6 +51,7 @@ expression: diff.instruction_rows
address: 6,
size: 2,
opcode: 47,
branch_dest: None,
},
),
kind: None,
@@ -61,6 +65,7 @@ expression: diff.instruction_rows
address: 8,
size: 2,
opcode: 47,
branch_dest: None,
},
),
kind: None,
@@ -74,6 +79,7 @@ expression: diff.instruction_rows
address: 10,
size: 2,
opcode: 47,
branch_dest: None,
},
),
kind: None,
@@ -87,6 +93,7 @@ expression: diff.instruction_rows
address: 12,
size: 2,
opcode: 66,
branch_dest: None,
},
),
kind: None,
@@ -100,6 +107,7 @@ expression: diff.instruction_rows
address: 14,
size: 4,
opcode: 19,
branch_dest: None,
},
),
kind: None,
@@ -113,6 +121,7 @@ expression: diff.instruction_rows
address: 18,
size: 2,
opcode: 25,
branch_dest: None,
},
),
kind: None,
@@ -126,6 +135,9 @@ expression: diff.instruction_rows
address: 20,
size: 2,
opcode: 15,
branch_dest: Some(
212,
),
},
),
kind: None,
@@ -144,6 +156,7 @@ expression: diff.instruction_rows
address: 22,
size: 2,
opcode: 38,
branch_dest: None,
},
),
kind: None,
@@ -157,6 +170,7 @@ expression: diff.instruction_rows
address: 24,
size: 2,
opcode: 25,
branch_dest: None,
},
),
kind: None,
@@ -170,6 +184,9 @@ expression: diff.instruction_rows
address: 26,
size: 2,
opcode: 15,
branch_dest: Some(
48,
),
},
),
kind: None,
@@ -188,6 +205,7 @@ expression: diff.instruction_rows
address: 28,
size: 2,
opcode: 34,
branch_dest: None,
},
),
kind: None,
@@ -201,6 +219,7 @@ expression: diff.instruction_rows
address: 30,
size: 2,
opcode: 26,
branch_dest: None,
},
),
kind: None,
@@ -214,6 +233,9 @@ expression: diff.instruction_rows
address: 32,
size: 2,
opcode: 15,
branch_dest: Some(
94,
),
},
),
kind: None,
@@ -232,6 +254,7 @@ expression: diff.instruction_rows
address: 34,
size: 2,
opcode: 35,
branch_dest: None,
},
),
kind: None,
@@ -245,6 +268,7 @@ expression: diff.instruction_rows
address: 36,
size: 2,
opcode: 47,
branch_dest: None,
},
),
kind: None,
@@ -258,6 +282,7 @@ expression: diff.instruction_rows
address: 38,
size: 2,
opcode: 47,
branch_dest: None,
},
),
kind: None,
@@ -271,6 +296,7 @@ expression: diff.instruction_rows
address: 40,
size: 4,
opcode: 19,
branch_dest: None,
},
),
kind: None,
@@ -284,6 +310,7 @@ expression: diff.instruction_rows
address: 44,
size: 2,
opcode: 7,
branch_dest: None,
},
),
kind: None,
@@ -297,6 +324,7 @@ expression: diff.instruction_rows
address: 46,
size: 2,
opcode: 55,
branch_dest: None,
},
),
kind: None,
@@ -310,6 +338,7 @@ expression: diff.instruction_rows
address: 48,
size: 2,
opcode: 47,
branch_dest: None,
},
),
kind: None,
@@ -330,6 +359,7 @@ expression: diff.instruction_rows
address: 50,
size: 2,
opcode: 47,
branch_dest: None,
},
),
kind: None,
@@ -343,6 +373,7 @@ expression: diff.instruction_rows
address: 52,
size: 4,
opcode: 19,
branch_dest: None,
},
),
kind: None,
@@ -356,6 +387,7 @@ expression: diff.instruction_rows
address: 56,
size: 2,
opcode: 25,
branch_dest: None,
},
),
kind: None,
@@ -369,6 +401,9 @@ expression: diff.instruction_rows
address: 58,
size: 2,
opcode: 15,
branch_dest: Some(
212,
),
},
),
kind: None,
@@ -387,6 +422,7 @@ expression: diff.instruction_rows
address: 60,
size: 2,
opcode: 34,
branch_dest: None,
},
),
kind: None,
@@ -400,6 +436,7 @@ expression: diff.instruction_rows
address: 62,
size: 2,
opcode: 33,
branch_dest: None,
},
),
kind: None,
@@ -413,6 +450,7 @@ expression: diff.instruction_rows
address: 64,
size: 2,
opcode: 36,
branch_dest: None,
},
),
kind: None,
@@ -426,6 +464,7 @@ expression: diff.instruction_rows
address: 66,
size: 2,
opcode: 42,
branch_dest: None,
},
),
kind: None,
@@ -439,6 +478,7 @@ expression: diff.instruction_rows
address: 68,
size: 2,
opcode: 44,
branch_dest: None,
},
),
kind: None,
@@ -452,6 +492,9 @@ expression: diff.instruction_rows
address: 70,
size: 2,
opcode: 15,
branch_dest: Some(
212,
),
},
),
kind: None,
@@ -470,6 +513,7 @@ expression: diff.instruction_rows
address: 72,
size: 2,
opcode: 46,
branch_dest: None,
},
),
kind: None,
@@ -483,6 +527,7 @@ expression: diff.instruction_rows
address: 74,
size: 2,
opcode: 17,
branch_dest: None,
},
),
kind: None,
@@ -496,6 +541,7 @@ expression: diff.instruction_rows
address: 76,
size: 2,
opcode: 46,
branch_dest: None,
},
),
kind: None,
@@ -509,6 +555,7 @@ expression: diff.instruction_rows
address: 78,
size: 2,
opcode: 54,
branch_dest: None,
},
),
kind: None,
@@ -522,6 +569,7 @@ expression: diff.instruction_rows
address: 80,
size: 2,
opcode: 67,
branch_dest: None,
},
),
kind: None,
@@ -535,6 +583,7 @@ expression: diff.instruction_rows
address: 82,
size: 2,
opcode: 36,
branch_dest: None,
},
),
kind: None,
@@ -548,6 +597,7 @@ expression: diff.instruction_rows
address: 84,
size: 2,
opcode: 46,
branch_dest: None,
},
),
kind: None,
@@ -561,6 +611,7 @@ expression: diff.instruction_rows
address: 86,
size: 2,
opcode: 7,
branch_dest: None,
},
),
kind: None,
@@ -574,6 +625,7 @@ expression: diff.instruction_rows
address: 88,
size: 2,
opcode: 54,
branch_dest: None,
},
),
kind: None,
@@ -587,6 +639,7 @@ expression: diff.instruction_rows
address: 90,
size: 2,
opcode: 67,
branch_dest: None,
},
),
kind: None,
@@ -600,6 +653,7 @@ expression: diff.instruction_rows
address: 92,
size: 2,
opcode: 55,
branch_dest: None,
},
),
kind: None,
@@ -613,6 +667,7 @@ expression: diff.instruction_rows
address: 94,
size: 2,
opcode: 34,
branch_dest: None,
},
),
kind: None,
@@ -633,6 +688,7 @@ expression: diff.instruction_rows
address: 96,
size: 2,
opcode: 34,
branch_dest: None,
},
),
kind: None,
@@ -646,6 +702,7 @@ expression: diff.instruction_rows
address: 98,
size: 2,
opcode: 4,
branch_dest: None,
},
),
kind: None,
@@ -659,6 +716,7 @@ expression: diff.instruction_rows
address: 100,
size: 4,
opcode: 19,
branch_dest: None,
},
),
kind: None,
@@ -672,6 +730,7 @@ expression: diff.instruction_rows
address: 104,
size: 2,
opcode: 66,
branch_dest: None,
},
),
kind: None,
@@ -685,6 +744,7 @@ expression: diff.instruction_rows
address: 106,
size: 2,
opcode: 47,
branch_dest: None,
},
),
kind: None,
@@ -698,6 +758,7 @@ expression: diff.instruction_rows
address: 108,
size: 4,
opcode: 19,
branch_dest: None,
},
),
kind: None,
@@ -711,6 +772,7 @@ expression: diff.instruction_rows
address: 112,
size: 2,
opcode: 47,
branch_dest: None,
},
),
kind: None,
@@ -724,6 +786,7 @@ expression: diff.instruction_rows
address: 114,
size: 2,
opcode: 6,
branch_dest: None,
},
),
kind: None,
@@ -737,6 +800,7 @@ expression: diff.instruction_rows
address: 116,
size: 2,
opcode: 66,
branch_dest: None,
},
),
kind: None,
@@ -750,6 +814,7 @@ expression: diff.instruction_rows
address: 118,
size: 2,
opcode: 35,
branch_dest: None,
},
),
kind: None,
@@ -763,6 +828,7 @@ expression: diff.instruction_rows
address: 120,
size: 2,
opcode: 47,
branch_dest: None,
},
),
kind: None,
@@ -776,6 +842,7 @@ expression: diff.instruction_rows
address: 122,
size: 2,
opcode: 47,
branch_dest: None,
},
),
kind: None,
@@ -789,6 +856,7 @@ expression: diff.instruction_rows
address: 124,
size: 2,
opcode: 47,
branch_dest: None,
},
),
kind: None,
@@ -802,6 +870,7 @@ expression: diff.instruction_rows
address: 126,
size: 4,
opcode: 19,
branch_dest: None,
},
),
kind: None,
@@ -815,6 +884,7 @@ expression: diff.instruction_rows
address: 130,
size: 2,
opcode: 25,
branch_dest: None,
},
),
kind: None,
@@ -828,6 +898,9 @@ expression: diff.instruction_rows
address: 132,
size: 2,
opcode: 15,
branch_dest: Some(
168,
),
},
),
kind: None,
@@ -846,6 +919,7 @@ expression: diff.instruction_rows
address: 134,
size: 2,
opcode: 47,
branch_dest: None,
},
),
kind: None,
@@ -859,6 +933,7 @@ expression: diff.instruction_rows
address: 136,
size: 4,
opcode: 19,
branch_dest: None,
},
),
kind: None,
@@ -872,6 +947,7 @@ expression: diff.instruction_rows
address: 140,
size: 2,
opcode: 47,
branch_dest: None,
},
),
kind: None,
@@ -885,6 +961,7 @@ expression: diff.instruction_rows
address: 142,
size: 2,
opcode: 25,
branch_dest: None,
},
),
kind: None,
@@ -898,6 +975,9 @@ expression: diff.instruction_rows
address: 144,
size: 2,
opcode: 15,
branch_dest: Some(
168,
),
},
),
kind: None,
@@ -916,6 +996,7 @@ expression: diff.instruction_rows
address: 146,
size: 2,
opcode: 34,
branch_dest: None,
},
),
kind: None,
@@ -929,6 +1010,7 @@ expression: diff.instruction_rows
address: 148,
size: 2,
opcode: 33,
branch_dest: None,
},
),
kind: None,
@@ -942,6 +1024,7 @@ expression: diff.instruction_rows
address: 150,
size: 2,
opcode: 36,
branch_dest: None,
},
),
kind: None,
@@ -955,6 +1038,7 @@ expression: diff.instruction_rows
address: 152,
size: 2,
opcode: 42,
branch_dest: None,
},
),
kind: None,
@@ -968,6 +1052,7 @@ expression: diff.instruction_rows
address: 154,
size: 2,
opcode: 44,
branch_dest: None,
},
),
kind: None,
@@ -981,6 +1066,9 @@ expression: diff.instruction_rows
address: 156,
size: 2,
opcode: 15,
branch_dest: Some(
168,
),
},
),
kind: None,
@@ -999,6 +1087,7 @@ expression: diff.instruction_rows
address: 158,
size: 2,
opcode: 46,
branch_dest: None,
},
),
kind: None,
@@ -1012,6 +1101,7 @@ expression: diff.instruction_rows
address: 160,
size: 2,
opcode: 17,
branch_dest: None,
},
),
kind: None,
@@ -1025,6 +1115,7 @@ expression: diff.instruction_rows
address: 162,
size: 2,
opcode: 46,
branch_dest: None,
},
),
kind: None,
@@ -1038,6 +1129,7 @@ expression: diff.instruction_rows
address: 164,
size: 2,
opcode: 54,
branch_dest: None,
},
),
kind: None,
@@ -1051,6 +1143,7 @@ expression: diff.instruction_rows
address: 166,
size: 2,
opcode: 67,
branch_dest: None,
},
),
kind: None,
@@ -1064,6 +1157,7 @@ expression: diff.instruction_rows
address: 168,
size: 2,
opcode: 25,
branch_dest: None,
},
),
kind: None,
@@ -1086,6 +1180,9 @@ expression: diff.instruction_rows
address: 170,
size: 2,
opcode: 15,
branch_dest: Some(
200,
),
},
),
kind: None,
@@ -1104,6 +1201,7 @@ expression: diff.instruction_rows
address: 172,
size: 2,
opcode: 35,
branch_dest: None,
},
),
kind: None,
@@ -1117,6 +1215,7 @@ expression: diff.instruction_rows
address: 174,
size: 2,
opcode: 25,
branch_dest: None,
},
),
kind: None,
@@ -1130,6 +1229,9 @@ expression: diff.instruction_rows
address: 176,
size: 2,
opcode: 15,
branch_dest: Some(
200,
),
},
),
kind: None,
@@ -1148,6 +1250,7 @@ expression: diff.instruction_rows
address: 178,
size: 2,
opcode: 34,
branch_dest: None,
},
),
kind: None,
@@ -1161,6 +1264,7 @@ expression: diff.instruction_rows
address: 180,
size: 2,
opcode: 37,
branch_dest: None,
},
),
kind: None,
@@ -1174,6 +1278,7 @@ expression: diff.instruction_rows
address: 182,
size: 2,
opcode: 42,
branch_dest: None,
},
),
kind: None,
@@ -1187,6 +1292,7 @@ expression: diff.instruction_rows
address: 184,
size: 2,
opcode: 44,
branch_dest: None,
},
),
kind: None,
@@ -1200,6 +1306,9 @@ expression: diff.instruction_rows
address: 186,
size: 2,
opcode: 15,
branch_dest: Some(
200,
),
},
),
kind: None,
@@ -1218,6 +1327,7 @@ expression: diff.instruction_rows
address: 188,
size: 2,
opcode: 32,
branch_dest: None,
},
),
kind: None,
@@ -1231,6 +1341,7 @@ expression: diff.instruction_rows
address: 190,
size: 2,
opcode: 34,
branch_dest: None,
},
),
kind: None,
@@ -1244,6 +1355,7 @@ expression: diff.instruction_rows
address: 192,
size: 2,
opcode: 46,
branch_dest: None,
},
),
kind: None,
@@ -1257,6 +1369,7 @@ expression: diff.instruction_rows
address: 194,
size: 2,
opcode: 46,
branch_dest: None,
},
),
kind: None,
@@ -1270,6 +1383,7 @@ expression: diff.instruction_rows
address: 196,
size: 4,
opcode: 19,
branch_dest: None,
},
),
kind: None,
@@ -1283,6 +1397,7 @@ expression: diff.instruction_rows
address: 200,
size: 2,
opcode: 34,
branch_dest: None,
},
),
kind: None,
@@ -1305,6 +1420,7 @@ expression: diff.instruction_rows
address: 202,
size: 2,
opcode: 35,
branch_dest: None,
},
),
kind: None,
@@ -1318,6 +1434,7 @@ expression: diff.instruction_rows
address: 204,
size: 2,
opcode: 34,
branch_dest: None,
},
),
kind: None,
@@ -1331,6 +1448,7 @@ expression: diff.instruction_rows
address: 206,
size: 2,
opcode: 4,
branch_dest: None,
},
),
kind: None,
@@ -1344,6 +1462,7 @@ expression: diff.instruction_rows
address: 208,
size: 4,
opcode: 19,
branch_dest: None,
},
),
kind: None,
@@ -1357,6 +1476,7 @@ expression: diff.instruction_rows
address: 212,
size: 2,
opcode: 7,
branch_dest: None,
},
),
kind: None,
@@ -1379,6 +1499,7 @@ expression: diff.instruction_rows
address: 214,
size: 2,
opcode: 55,
branch_dest: None,
},
),
kind: None,
@@ -1392,6 +1513,7 @@ expression: diff.instruction_rows
address: 216,
size: 4,
opcode: 65535,
branch_dest: None,
},
),
kind: None,
@@ -1405,6 +1527,7 @@ expression: diff.instruction_rows
address: 220,
size: 4,
opcode: 65535,
branch_dest: None,
},
),
kind: None,
@@ -1418,6 +1541,7 @@ expression: diff.instruction_rows
address: 224,
size: 4,
opcode: 65535,
branch_dest: None,
},
),
kind: None,
@@ -1431,6 +1555,7 @@ expression: diff.instruction_rows
address: 228,
size: 4,
opcode: 65535,
branch_dest: None,
},
),
kind: None,
@@ -1444,6 +1569,7 @@ expression: diff.instruction_rows
address: 232,
size: 4,
opcode: 65535,
branch_dest: None,
},
),
kind: None,
@@ -1457,6 +1583,7 @@ expression: diff.instruction_rows
address: 236,
size: 4,
opcode: 65535,
branch_dest: None,
},
),
kind: None,
@@ -1470,6 +1597,7 @@ expression: diff.instruction_rows
address: 240,
size: 4,
opcode: 65535,
branch_dest: None,
},
),
kind: None,

View File

@@ -3826,4 +3826,5 @@ Object {
split_meta: None,
path: None,
timestamp: None,
flow_analysis_results: {},
}

View File

@@ -9,6 +9,7 @@ expression: diff.instruction_rows
address: 0,
size: 4,
opcode: 12,
branch_dest: None,
},
),
kind: None,
@@ -22,6 +23,7 @@ expression: diff.instruction_rows
address: 4,
size: 4,
opcode: 44,
branch_dest: None,
},
),
kind: None,
@@ -35,6 +37,7 @@ expression: diff.instruction_rows
address: 8,
size: 4,
opcode: 44,
branch_dest: None,
},
),
kind: None,
@@ -48,6 +51,7 @@ expression: diff.instruction_rows
address: 12,
size: 4,
opcode: 44,
branch_dest: None,
},
),
kind: None,
@@ -61,6 +65,7 @@ expression: diff.instruction_rows
address: 16,
size: 4,
opcode: 44,
branch_dest: None,
},
),
kind: None,
@@ -74,6 +79,7 @@ expression: diff.instruction_rows
address: 20,
size: 4,
opcode: 2,
branch_dest: None,
},
),
kind: None,
@@ -87,6 +93,7 @@ expression: diff.instruction_rows
address: 24,
size: 4,
opcode: 113,
branch_dest: None,
},
),
kind: None,
@@ -100,6 +107,7 @@ expression: diff.instruction_rows
address: 28,
size: 4,
opcode: 26,
branch_dest: None,
},
),
kind: None,
@@ -113,6 +121,7 @@ expression: diff.instruction_rows
address: 32,
size: 4,
opcode: 20,
branch_dest: None,
},
),
kind: None,
@@ -126,6 +135,7 @@ expression: diff.instruction_rows
address: 36,
size: 4,
opcode: 97,
branch_dest: None,
},
),
kind: None,
@@ -139,6 +149,7 @@ expression: diff.instruction_rows
address: 40,
size: 4,
opcode: 2,
branch_dest: None,
},
),
kind: None,
@@ -152,6 +163,7 @@ expression: diff.instruction_rows
address: 44,
size: 4,
opcode: 12,
branch_dest: None,
},
),
kind: None,
@@ -165,6 +177,7 @@ expression: diff.instruction_rows
address: 48,
size: 4,
opcode: 20,
branch_dest: None,
},
),
kind: None,
@@ -178,6 +191,7 @@ expression: diff.instruction_rows
address: 52,
size: 4,
opcode: 26,
branch_dest: None,
},
),
kind: None,
@@ -191,6 +205,7 @@ expression: diff.instruction_rows
address: 56,
size: 4,
opcode: 2,
branch_dest: None,
},
),
kind: None,
@@ -204,6 +219,7 @@ expression: diff.instruction_rows
address: 60,
size: 4,
opcode: 12,
branch_dest: None,
},
),
kind: None,
@@ -217,6 +233,7 @@ expression: diff.instruction_rows
address: 64,
size: 4,
opcode: 26,
branch_dest: None,
},
),
kind: None,
@@ -230,6 +247,7 @@ expression: diff.instruction_rows
address: 68,
size: 4,
opcode: 97,
branch_dest: None,
},
),
kind: None,
@@ -243,6 +261,7 @@ expression: diff.instruction_rows
address: 72,
size: 4,
opcode: 2,
branch_dest: None,
},
),
kind: None,
@@ -256,6 +275,7 @@ expression: diff.instruction_rows
address: 76,
size: 4,
opcode: 97,
branch_dest: None,
},
),
kind: None,
@@ -269,6 +289,7 @@ expression: diff.instruction_rows
address: 80,
size: 4,
opcode: 2,
branch_dest: None,
},
),
kind: None,
@@ -289,6 +310,7 @@ expression: diff.instruction_rows
address: 84,
size: 4,
opcode: 97,
branch_dest: None,
},
),
kind: None,
@@ -302,6 +324,9 @@ expression: diff.instruction_rows
address: 88,
size: 4,
opcode: 56,
branch_dest: Some(
80,
),
},
),
kind: None,
@@ -320,6 +345,7 @@ expression: diff.instruction_rows
address: 92,
size: 4,
opcode: 113,
branch_dest: None,
},
),
kind: None,
@@ -333,6 +359,7 @@ expression: diff.instruction_rows
address: 96,
size: 4,
opcode: 2,
branch_dest: None,
},
),
kind: None,
@@ -346,6 +373,7 @@ expression: diff.instruction_rows
address: 100,
size: 4,
opcode: 20,
branch_dest: None,
},
),
kind: None,
@@ -359,6 +387,7 @@ expression: diff.instruction_rows
address: 104,
size: 4,
opcode: 2,
branch_dest: None,
},
),
kind: None,
@@ -372,6 +401,7 @@ expression: diff.instruction_rows
address: 108,
size: 4,
opcode: 12,
branch_dest: None,
},
),
kind: None,
@@ -385,6 +415,7 @@ expression: diff.instruction_rows
address: 112,
size: 4,
opcode: 2,
branch_dest: None,
},
),
kind: None,
@@ -398,6 +429,7 @@ expression: diff.instruction_rows
address: 116,
size: 4,
opcode: 16,
branch_dest: None,
},
),
kind: None,
@@ -411,6 +443,7 @@ expression: diff.instruction_rows
address: 120,
size: 4,
opcode: 20,
branch_dest: None,
},
),
kind: None,
@@ -424,6 +457,7 @@ expression: diff.instruction_rows
address: 124,
size: 4,
opcode: 12,
branch_dest: None,
},
),
kind: None,
@@ -437,6 +471,7 @@ expression: diff.instruction_rows
address: 128,
size: 4,
opcode: 2,
branch_dest: None,
},
),
kind: None,
@@ -459,6 +494,7 @@ expression: diff.instruction_rows
address: 132,
size: 4,
opcode: 12,
branch_dest: None,
},
),
kind: None,
@@ -472,6 +508,7 @@ expression: diff.instruction_rows
address: 136,
size: 4,
opcode: 2,
branch_dest: None,
},
),
kind: None,
@@ -485,6 +522,7 @@ expression: diff.instruction_rows
address: 140,
size: 4,
opcode: 113,
branch_dest: None,
},
),
kind: None,
@@ -498,6 +536,9 @@ expression: diff.instruction_rows
address: 144,
size: 4,
opcode: 55,
branch_dest: Some(
128,
),
},
),
kind: None,
@@ -516,6 +557,7 @@ expression: diff.instruction_rows
address: 148,
size: 4,
opcode: 90,
branch_dest: None,
},
),
kind: None,
@@ -529,6 +571,9 @@ expression: diff.instruction_rows
address: 152,
size: 4,
opcode: 3,
branch_dest: Some(
128,
),
},
),
kind: None,
@@ -547,6 +592,7 @@ expression: diff.instruction_rows
address: 156,
size: 4,
opcode: 113,
branch_dest: None,
},
),
kind: None,
@@ -560,6 +606,7 @@ expression: diff.instruction_rows
address: 160,
size: 4,
opcode: 2,
branch_dest: None,
},
),
kind: None,
@@ -573,6 +620,7 @@ expression: diff.instruction_rows
address: 164,
size: 4,
opcode: 60,
branch_dest: None,
},
),
kind: None,
@@ -586,6 +634,7 @@ expression: diff.instruction_rows
address: 168,
size: 4,
opcode: 77,
branch_dest: None,
},
),
kind: None,
@@ -599,6 +648,7 @@ expression: diff.instruction_rows
address: 172,
size: 4,
opcode: 113,
branch_dest: None,
},
),
kind: None,
@@ -612,6 +662,9 @@ expression: diff.instruction_rows
address: 176,
size: 4,
opcode: 54,
branch_dest: Some(
128,
),
},
),
kind: None,
@@ -630,6 +683,7 @@ expression: diff.instruction_rows
address: 180,
size: 4,
opcode: 113,
branch_dest: None,
},
),
kind: None,

View File

@@ -1490,4 +1490,5 @@ Object {
split_meta: None,
path: None,
timestamp: None,
flow_analysis_results: {},
}

File diff suppressed because it is too large Load Diff

View File

@@ -548,4 +548,5 @@ Object {
split_meta: None,
path: None,
timestamp: None,
flow_analysis_results: {},
}

View File

@@ -9,6 +9,7 @@ expression: diff.instruction_rows
address: 0,
size: 4,
opcode: 60,
branch_dest: None,
},
),
kind: None,
@@ -22,6 +23,7 @@ expression: diff.instruction_rows
address: 4,
size: 4,
opcode: 38,
branch_dest: None,
},
),
kind: None,
@@ -35,6 +37,9 @@ expression: diff.instruction_rows
address: 8,
size: 4,
opcode: 43,
branch_dest: Some(
20,
),
},
),
kind: None,
@@ -53,6 +58,7 @@ expression: diff.instruction_rows
address: 12,
size: 4,
opcode: 41,
branch_dest: None,
},
),
kind: None,
@@ -66,6 +72,9 @@ expression: diff.instruction_rows
address: 16,
size: 4,
opcode: 45,
branch_dest: Some(
32,
),
},
),
kind: None,
@@ -84,6 +93,7 @@ expression: diff.instruction_rows
address: 20,
size: 4,
opcode: 42,
branch_dest: None,
},
),
kind: None,
@@ -104,6 +114,7 @@ expression: diff.instruction_rows
address: 24,
size: 4,
opcode: 41,
branch_dest: None,
},
),
kind: None,
@@ -117,6 +128,7 @@ expression: diff.instruction_rows
address: 28,
size: 4,
opcode: 94,
branch_dest: None,
},
),
kind: None,
@@ -130,6 +142,7 @@ expression: diff.instruction_rows
address: 32,
size: 4,
opcode: 60,
branch_dest: None,
},
),
kind: None,
@@ -150,6 +163,7 @@ expression: diff.instruction_rows
address: 36,
size: 4,
opcode: 166,
branch_dest: None,
},
),
kind: None,
@@ -163,6 +177,7 @@ expression: diff.instruction_rows
address: 40,
size: 4,
opcode: 38,
branch_dest: None,
},
),
kind: None,
@@ -176,6 +191,9 @@ expression: diff.instruction_rows
address: 44,
size: 4,
opcode: 43,
branch_dest: Some(
56,
),
},
),
kind: None,
@@ -194,6 +212,7 @@ expression: diff.instruction_rows
address: 48,
size: 4,
opcode: 41,
branch_dest: None,
},
),
kind: None,
@@ -207,6 +226,9 @@ expression: diff.instruction_rows
address: 52,
size: 4,
opcode: 45,
branch_dest: Some(
68,
),
},
),
kind: None,
@@ -225,6 +247,7 @@ expression: diff.instruction_rows
address: 56,
size: 4,
opcode: 42,
branch_dest: None,
},
),
kind: None,
@@ -245,6 +268,7 @@ expression: diff.instruction_rows
address: 60,
size: 4,
opcode: 41,
branch_dest: None,
},
),
kind: None,
@@ -258,6 +282,7 @@ expression: diff.instruction_rows
address: 64,
size: 4,
opcode: 94,
branch_dest: None,
},
),
kind: None,
@@ -271,6 +296,7 @@ expression: diff.instruction_rows
address: 68,
size: 4,
opcode: 60,
branch_dest: None,
},
),
kind: None,
@@ -291,6 +317,7 @@ expression: diff.instruction_rows
address: 72,
size: 4,
opcode: 41,
branch_dest: None,
},
),
kind: None,
@@ -304,6 +331,7 @@ expression: diff.instruction_rows
address: 76,
size: 4,
opcode: 38,
branch_dest: None,
},
),
kind: None,
@@ -317,6 +345,7 @@ expression: diff.instruction_rows
address: 80,
size: 4,
opcode: 166,
branch_dest: None,
},
),
kind: None,
@@ -330,6 +359,9 @@ expression: diff.instruction_rows
address: 84,
size: 4,
opcode: 43,
branch_dest: Some(
96,
),
},
),
kind: None,
@@ -348,6 +380,7 @@ expression: diff.instruction_rows
address: 88,
size: 4,
opcode: 41,
branch_dest: None,
},
),
kind: None,
@@ -361,6 +394,9 @@ expression: diff.instruction_rows
address: 92,
size: 4,
opcode: 45,
branch_dest: Some(
108,
),
},
),
kind: None,
@@ -379,6 +415,7 @@ expression: diff.instruction_rows
address: 96,
size: 4,
opcode: 42,
branch_dest: None,
},
),
kind: None,
@@ -399,6 +436,7 @@ expression: diff.instruction_rows
address: 100,
size: 4,
opcode: 41,
branch_dest: None,
},
),
kind: None,
@@ -412,6 +450,7 @@ expression: diff.instruction_rows
address: 104,
size: 4,
opcode: 94,
branch_dest: None,
},
),
kind: None,
@@ -425,6 +464,7 @@ expression: diff.instruction_rows
address: 108,
size: 4,
opcode: 60,
branch_dest: None,
},
),
kind: None,
@@ -445,6 +485,7 @@ expression: diff.instruction_rows
address: 112,
size: 4,
opcode: 41,
branch_dest: None,
},
),
kind: None,
@@ -458,6 +499,7 @@ expression: diff.instruction_rows
address: 116,
size: 4,
opcode: 38,
branch_dest: None,
},
),
kind: None,
@@ -471,6 +513,7 @@ expression: diff.instruction_rows
address: 120,
size: 4,
opcode: 166,
branch_dest: None,
},
),
kind: None,
@@ -484,6 +527,9 @@ expression: diff.instruction_rows
address: 124,
size: 4,
opcode: 43,
branch_dest: Some(
136,
),
},
),
kind: None,
@@ -502,6 +548,7 @@ expression: diff.instruction_rows
address: 128,
size: 4,
opcode: 41,
branch_dest: None,
},
),
kind: None,
@@ -515,6 +562,9 @@ expression: diff.instruction_rows
address: 132,
size: 4,
opcode: 45,
branch_dest: Some(
148,
),
},
),
kind: None,
@@ -533,6 +583,7 @@ expression: diff.instruction_rows
address: 136,
size: 4,
opcode: 42,
branch_dest: None,
},
),
kind: None,
@@ -553,6 +604,7 @@ expression: diff.instruction_rows
address: 140,
size: 4,
opcode: 41,
branch_dest: None,
},
),
kind: None,
@@ -566,6 +618,7 @@ expression: diff.instruction_rows
address: 144,
size: 4,
opcode: 94,
branch_dest: None,
},
),
kind: None,
@@ -579,6 +632,7 @@ expression: diff.instruction_rows
address: 148,
size: 4,
opcode: 41,
branch_dest: None,
},
),
kind: None,
@@ -599,6 +653,7 @@ expression: diff.instruction_rows
address: 152,
size: 4,
opcode: 41,
branch_dest: None,
},
),
kind: None,
@@ -612,6 +667,7 @@ expression: diff.instruction_rows
address: 156,
size: 4,
opcode: 166,
branch_dest: None,
},
),
kind: None,
@@ -625,6 +681,7 @@ expression: diff.instruction_rows
address: 160,
size: 4,
opcode: 42,
branch_dest: None,
},
),
kind: None,
@@ -638,6 +695,7 @@ expression: diff.instruction_rows
address: 164,
size: 4,
opcode: 41,
branch_dest: None,
},
),
kind: None,
@@ -651,6 +709,7 @@ expression: diff.instruction_rows
address: 168,
size: 4,
opcode: 166,
branch_dest: None,
},
),
kind: None,
@@ -664,6 +723,7 @@ expression: diff.instruction_rows
address: 172,
size: 4,
opcode: 41,
branch_dest: None,
},
),
kind: None,
@@ -677,6 +737,7 @@ expression: diff.instruction_rows
address: 176,
size: 4,
opcode: 162,
branch_dest: None,
},
),
kind: None,
@@ -690,6 +751,7 @@ expression: diff.instruction_rows
address: 180,
size: 4,
opcode: 94,
branch_dest: None,
},
),
kind: None,
@@ -703,6 +765,7 @@ expression: diff.instruction_rows
address: 184,
size: 4,
opcode: 66,
branch_dest: None,
},
),
kind: None,
@@ -716,6 +779,9 @@ expression: diff.instruction_rows
address: 188,
size: 4,
opcode: 43,
branch_dest: Some(
196,
),
},
),
kind: None,
@@ -734,6 +800,7 @@ expression: diff.instruction_rows
address: 192,
size: 4,
opcode: 166,
branch_dest: None,
},
),
kind: None,
@@ -747,6 +814,7 @@ expression: diff.instruction_rows
address: 196,
size: 4,
opcode: 163,
branch_dest: None,
},
),
kind: None,
@@ -767,6 +835,7 @@ expression: diff.instruction_rows
address: 200,
size: 4,
opcode: 94,
branch_dest: None,
},
),
kind: None,
@@ -780,6 +849,7 @@ expression: diff.instruction_rows
address: 204,
size: 4,
opcode: 66,
branch_dest: None,
},
),
kind: None,
@@ -793,6 +863,9 @@ expression: diff.instruction_rows
address: 208,
size: 4,
opcode: 43,
branch_dest: Some(
216,
),
},
),
kind: None,
@@ -811,6 +884,7 @@ expression: diff.instruction_rows
address: 212,
size: 4,
opcode: 166,
branch_dest: None,
},
),
kind: None,
@@ -824,6 +898,7 @@ expression: diff.instruction_rows
address: 216,
size: 4,
opcode: 163,
branch_dest: None,
},
),
kind: None,
@@ -844,6 +919,7 @@ expression: diff.instruction_rows
address: 220,
size: 4,
opcode: 94,
branch_dest: None,
},
),
kind: None,
@@ -857,6 +933,7 @@ expression: diff.instruction_rows
address: 224,
size: 4,
opcode: 66,
branch_dest: None,
},
),
kind: None,
@@ -870,6 +947,9 @@ expression: diff.instruction_rows
address: 228,
size: 4,
opcode: 43,
branch_dest: Some(
236,
),
},
),
kind: None,
@@ -888,6 +968,7 @@ expression: diff.instruction_rows
address: 232,
size: 4,
opcode: 166,
branch_dest: None,
},
),
kind: None,
@@ -901,6 +982,7 @@ expression: diff.instruction_rows
address: 236,
size: 4,
opcode: 163,
branch_dest: None,
},
),
kind: None,
@@ -921,6 +1003,7 @@ expression: diff.instruction_rows
address: 240,
size: 4,
opcode: 94,
branch_dest: None,
},
),
kind: None,
@@ -934,6 +1017,7 @@ expression: diff.instruction_rows
address: 244,
size: 4,
opcode: 66,
branch_dest: None,
},
),
kind: None,
@@ -947,6 +1031,9 @@ expression: diff.instruction_rows
address: 248,
size: 4,
opcode: 43,
branch_dest: Some(
256,
),
},
),
kind: None,
@@ -965,6 +1052,7 @@ expression: diff.instruction_rows
address: 252,
size: 4,
opcode: 166,
branch_dest: None,
},
),
kind: None,
@@ -978,6 +1066,7 @@ expression: diff.instruction_rows
address: 256,
size: 4,
opcode: 41,
branch_dest: None,
},
),
kind: None,
@@ -998,6 +1087,7 @@ expression: diff.instruction_rows
address: 260,
size: 4,
opcode: 47,
branch_dest: None,
},
),
kind: None,

View File

@@ -581,4 +581,5 @@ Object {
),
path: None,
timestamp: None,
flow_analysis_results: {},
}

View File

@@ -9,6 +9,7 @@ expression: diff.instruction_rows
address: 0,
size: 1,
opcode: 640,
branch_dest: None,
},
),
kind: None,
@@ -22,6 +23,7 @@ expression: diff.instruction_rows
address: 1,
size: 2,
opcode: 414,
branch_dest: None,
},
),
kind: None,
@@ -35,6 +37,7 @@ expression: diff.instruction_rows
address: 3,
size: 5,
opcode: 640,
branch_dest: None,
},
),
kind: None,
@@ -48,6 +51,7 @@ expression: diff.instruction_rows
address: 8,
size: 5,
opcode: 59,
branch_dest: None,
},
),
kind: None,
@@ -61,6 +65,7 @@ expression: diff.instruction_rows
address: 13,
size: 3,
opcode: 7,
branch_dest: None,
},
),
kind: None,
@@ -74,6 +79,7 @@ expression: diff.instruction_rows
address: 16,
size: 1,
opcode: 590,
branch_dest: None,
},
),
kind: None,
@@ -87,6 +93,7 @@ expression: diff.instruction_rows
address: 17,
size: 1,
opcode: 662,
branch_dest: None,
},
),
kind: None,

View File

@@ -207,4 +207,5 @@ Object {
split_meta: None,
path: None,
timestamp: None,
flow_analysis_results: {},
}

View File

@@ -9,6 +9,7 @@ expression: diff.instruction_rows
address: 0,
size: 5,
opcode: 414,
branch_dest: None,
},
),
kind: None,
@@ -22,6 +23,7 @@ expression: diff.instruction_rows
address: 5,
size: 5,
opcode: 414,
branch_dest: None,
},
),
kind: None,
@@ -35,6 +37,7 @@ expression: diff.instruction_rows
address: 10,
size: 1,
opcode: 640,
branch_dest: None,
},
),
kind: None,
@@ -48,6 +51,7 @@ expression: diff.instruction_rows
address: 11,
size: 4,
opcode: 740,
branch_dest: None,
},
),
kind: None,
@@ -61,6 +65,7 @@ expression: diff.instruction_rows
address: 15,
size: 5,
opcode: 414,
branch_dest: None,
},
),
kind: None,
@@ -74,6 +79,7 @@ expression: diff.instruction_rows
address: 20,
size: 5,
opcode: 414,
branch_dest: None,
},
),
kind: None,
@@ -87,6 +93,7 @@ expression: diff.instruction_rows
address: 25,
size: 4,
opcode: 448,
branch_dest: None,
},
),
kind: None,
@@ -100,6 +107,7 @@ expression: diff.instruction_rows
address: 29,
size: 4,
opcode: 460,
branch_dest: None,
},
),
kind: None,
@@ -113,6 +121,7 @@ expression: diff.instruction_rows
address: 33,
size: 5,
opcode: 414,
branch_dest: None,
},
),
kind: None,
@@ -126,6 +135,7 @@ expression: diff.instruction_rows
address: 38,
size: 5,
opcode: 414,
branch_dest: None,
},
),
kind: None,
@@ -139,6 +149,7 @@ expression: diff.instruction_rows
address: 43,
size: 5,
opcode: 448,
branch_dest: None,
},
),
kind: None,
@@ -152,6 +163,7 @@ expression: diff.instruction_rows
address: 48,
size: 5,
opcode: 460,
branch_dest: None,
},
),
kind: None,
@@ -165,6 +177,7 @@ expression: diff.instruction_rows
address: 53,
size: 4,
opcode: 11,
branch_dest: None,
},
),
kind: None,
@@ -178,6 +191,7 @@ expression: diff.instruction_rows
address: 57,
size: 5,
opcode: 414,
branch_dest: None,
},
),
kind: None,
@@ -191,6 +205,7 @@ expression: diff.instruction_rows
address: 62,
size: 5,
opcode: 414,
branch_dest: None,
},
),
kind: None,
@@ -204,6 +219,7 @@ expression: diff.instruction_rows
address: 67,
size: 5,
opcode: 448,
branch_dest: None,
},
),
kind: None,
@@ -217,6 +233,7 @@ expression: diff.instruction_rows
address: 72,
size: 5,
opcode: 460,
branch_dest: None,
},
),
kind: None,
@@ -230,6 +247,7 @@ expression: diff.instruction_rows
address: 77,
size: 4,
opcode: 11,
branch_dest: None,
},
),
kind: None,
@@ -243,6 +261,7 @@ expression: diff.instruction_rows
address: 81,
size: 4,
opcode: 7,
branch_dest: None,
},
),
kind: None,
@@ -256,6 +275,7 @@ expression: diff.instruction_rows
address: 85,
size: 1,
opcode: 590,
branch_dest: None,
},
),
kind: None,
@@ -269,6 +289,7 @@ expression: diff.instruction_rows
address: 86,
size: 1,
opcode: 662,
branch_dest: None,
},
),
kind: None,

View File

@@ -1574,4 +1574,5 @@ Object {
split_meta: None,
path: None,
timestamp: None,
flow_analysis_results: {},
}

View File

@@ -42,11 +42,11 @@ expression: obj.sections
name: ".rdata",
address: 0,
size: 0,
kind: Data,
kind: Unknown,
data: SectionData(
0,
),
flags: FlagSet(Hidden),
flags: FlagSet(),
align: Some(
4,
),
@@ -59,11 +59,11 @@ expression: obj.sections
name: ".rdata",
address: 0,
size: 0,
kind: Data,
kind: Unknown,
data: SectionData(
0,
),
flags: FlagSet(Hidden),
flags: FlagSet(),
align: Some(
4,
),
@@ -76,11 +76,11 @@ expression: obj.sections
name: ".text$mn",
address: 0,
size: 0,
kind: Code,
kind: Unknown,
data: SectionData(
0,
),
flags: FlagSet(Hidden),
flags: FlagSet(),
align: Some(
16,
),
@@ -425,11 +425,11 @@ expression: obj.sections
name: ".rdata$r",
address: 0,
size: 0,
kind: Data,
kind: Unknown,
data: SectionData(
0,
),
flags: FlagSet(Hidden),
flags: FlagSet(),
align: Some(
4,
),
@@ -442,11 +442,11 @@ expression: obj.sections
name: ".rdata$r",
address: 0,
size: 0,
kind: Data,
kind: Unknown,
data: SectionData(
0,
),
flags: FlagSet(Hidden),
flags: FlagSet(),
align: Some(
4,
),
@@ -459,11 +459,11 @@ expression: obj.sections
name: ".data$rs",
address: 0,
size: 0,
kind: Data,
kind: Unknown,
data: SectionData(
0,
),
flags: FlagSet(Hidden),
flags: FlagSet(),
align: Some(
4,
),
@@ -476,11 +476,11 @@ expression: obj.sections
name: ".rdata$r",
address: 0,
size: 0,
kind: Data,
kind: Unknown,
data: SectionData(
0,
),
flags: FlagSet(Hidden),
flags: FlagSet(),
align: Some(
4,
),
@@ -493,11 +493,11 @@ expression: obj.sections
name: ".rdata$r",
address: 0,
size: 0,
kind: Data,
kind: Unknown,
data: SectionData(
0,
),
flags: FlagSet(Hidden),
flags: FlagSet(),
align: Some(
4,
),
@@ -510,11 +510,11 @@ expression: obj.sections
name: ".rdata$r",
address: 0,
size: 0,
kind: Data,
kind: Unknown,
data: SectionData(
0,
),
flags: FlagSet(Hidden),
flags: FlagSet(),
align: Some(
4,
),
@@ -527,11 +527,11 @@ expression: obj.sections
name: ".rdata$r",
address: 0,
size: 0,
kind: Data,
kind: Unknown,
data: SectionData(
0,
),
flags: FlagSet(Hidden),
flags: FlagSet(),
align: Some(
4,
),
@@ -544,11 +544,11 @@ expression: obj.sections
name: ".data$rs",
address: 0,
size: 0,
kind: Data,
kind: Unknown,
data: SectionData(
0,
),
flags: FlagSet(Hidden),
flags: FlagSet(),
align: Some(
4,
),
@@ -561,11 +561,11 @@ expression: obj.sections
name: ".rdata$r",
address: 0,
size: 0,
kind: Data,
kind: Unknown,
data: SectionData(
0,
),
flags: FlagSet(Hidden),
flags: FlagSet(),
align: Some(
4,
),
@@ -578,11 +578,11 @@ expression: obj.sections
name: ".rdata$r",
address: 0,
size: 0,
kind: Data,
kind: Unknown,
data: SectionData(
0,
),
flags: FlagSet(Hidden),
flags: FlagSet(),
align: Some(
4,
),
@@ -595,11 +595,11 @@ expression: obj.sections
name: ".rdata$r",
address: 0,
size: 0,
kind: Data,
kind: Unknown,
data: SectionData(
0,
),
flags: FlagSet(Hidden),
flags: FlagSet(),
align: Some(
4,
),
@@ -612,11 +612,11 @@ expression: obj.sections
name: ".rdata$r",
address: 0,
size: 0,
kind: Data,
kind: Unknown,
data: SectionData(
0,
),
flags: FlagSet(Hidden),
flags: FlagSet(),
align: Some(
4,
),
@@ -629,11 +629,11 @@ expression: obj.sections
name: ".text$mn",
address: 0,
size: 0,
kind: Code,
kind: Unknown,
data: SectionData(
0,
),
flags: FlagSet(Hidden),
flags: FlagSet(),
align: Some(
1,
),
@@ -646,11 +646,11 @@ expression: obj.sections
name: ".rdata$r",
address: 0,
size: 0,
kind: Data,
kind: Unknown,
data: SectionData(
0,
),
flags: FlagSet(Hidden),
flags: FlagSet(),
align: Some(
4,
),
@@ -663,11 +663,11 @@ expression: obj.sections
name: ".text$mn",
address: 0,
size: 0,
kind: Code,
kind: Unknown,
data: SectionData(
0,
),
flags: FlagSet(Hidden),
flags: FlagSet(),
align: Some(
16,
),
@@ -680,11 +680,11 @@ expression: obj.sections
name: ".text$mn",
address: 0,
size: 0,
kind: Code,
kind: Unknown,
data: SectionData(
0,
),
flags: FlagSet(Hidden),
flags: FlagSet(),
align: Some(
16,
),
@@ -697,11 +697,11 @@ expression: obj.sections
name: ".text$mn",
address: 0,
size: 0,
kind: Code,
kind: Unknown,
data: SectionData(
0,
),
flags: FlagSet(Hidden),
flags: FlagSet(),
align: Some(
16,
),
@@ -714,11 +714,11 @@ expression: obj.sections
name: ".text$mn",
address: 0,
size: 0,
kind: Code,
kind: Unknown,
data: SectionData(
0,
),
flags: FlagSet(Hidden),
flags: FlagSet(),
align: Some(
16,
),
@@ -731,11 +731,11 @@ expression: obj.sections
name: ".text$mn",
address: 0,
size: 0,
kind: Code,
kind: Unknown,
data: SectionData(
0,
),
flags: FlagSet(Hidden),
flags: FlagSet(),
align: Some(
16,
),
@@ -902,11 +902,11 @@ expression: obj.sections
name: ".text$yd",
address: 0,
size: 0,
kind: Code,
kind: Unknown,
data: SectionData(
0,
),
flags: FlagSet(Hidden),
flags: FlagSet(),
align: Some(
16,
),
@@ -919,11 +919,11 @@ expression: obj.sections
name: ".rdata",
address: 0,
size: 0,
kind: Data,
kind: Unknown,
data: SectionData(
0,
),
flags: FlagSet(Hidden),
flags: FlagSet(),
align: Some(
4,
),
@@ -936,11 +936,11 @@ expression: obj.sections
name: ".rdata",
address: 0,
size: 0,
kind: Data,
kind: Unknown,
data: SectionData(
0,
),
flags: FlagSet(Hidden),
flags: FlagSet(),
align: Some(
4,
),
@@ -953,11 +953,11 @@ expression: obj.sections
name: ".data",
address: 0,
size: 0,
kind: Data,
kind: Unknown,
data: SectionData(
0,
),
flags: FlagSet(Hidden),
flags: FlagSet(),
align: Some(
4,
),
@@ -970,11 +970,11 @@ expression: obj.sections
name: ".rdata$r",
address: 0,
size: 0,
kind: Data,
kind: Unknown,
data: SectionData(
0,
),
flags: FlagSet(Hidden),
flags: FlagSet(),
align: Some(
4,
),
@@ -987,11 +987,11 @@ expression: obj.sections
name: ".rdata$r",
address: 0,
size: 0,
kind: Data,
kind: Unknown,
data: SectionData(
0,
),
flags: FlagSet(Hidden),
flags: FlagSet(),
align: Some(
4,
),

View File

@@ -9,6 +9,7 @@ expression: diff.instruction_rows
address: 0,
size: 4,
opcode: 414,
branch_dest: None,
},
),
kind: None,
@@ -22,6 +23,7 @@ expression: diff.instruction_rows
address: 4,
size: 1,
opcode: 137,
branch_dest: None,
},
),
kind: None,
@@ -35,6 +37,7 @@ expression: diff.instruction_rows
address: 5,
size: 3,
opcode: 93,
branch_dest: None,
},
),
kind: None,
@@ -48,6 +51,9 @@ expression: diff.instruction_rows
address: 8,
size: 2,
opcode: 297,
branch_dest: Some(
58,
),
},
),
kind: None,
@@ -66,6 +72,9 @@ expression: diff.instruction_rows
address: 10,
size: 7,
opcode: 308,
branch_dest: Some(
60,
),
},
),
kind: None,
@@ -84,6 +93,7 @@ expression: diff.instruction_rows
address: 17,
size: 5,
opcode: 414,
branch_dest: None,
},
),
kind: None,
@@ -104,6 +114,7 @@ expression: diff.instruction_rows
address: 22,
size: 1,
opcode: 662,
branch_dest: None,
},
),
kind: None,
@@ -117,6 +128,7 @@ expression: diff.instruction_rows
address: 23,
size: 5,
opcode: 414,
branch_dest: None,
},
),
kind: None,
@@ -137,6 +149,7 @@ expression: diff.instruction_rows
address: 28,
size: 1,
opcode: 662,
branch_dest: None,
},
),
kind: None,
@@ -150,6 +163,7 @@ expression: diff.instruction_rows
address: 29,
size: 5,
opcode: 414,
branch_dest: None,
},
),
kind: None,
@@ -170,6 +184,7 @@ expression: diff.instruction_rows
address: 34,
size: 1,
opcode: 662,
branch_dest: None,
},
),
kind: None,
@@ -183,6 +198,7 @@ expression: diff.instruction_rows
address: 35,
size: 5,
opcode: 414,
branch_dest: None,
},
),
kind: None,
@@ -203,6 +219,7 @@ expression: diff.instruction_rows
address: 40,
size: 1,
opcode: 662,
branch_dest: None,
},
),
kind: None,
@@ -216,6 +233,7 @@ expression: diff.instruction_rows
address: 41,
size: 5,
opcode: 414,
branch_dest: None,
},
),
kind: None,
@@ -236,6 +254,7 @@ expression: diff.instruction_rows
address: 46,
size: 1,
opcode: 662,
branch_dest: None,
},
),
kind: None,
@@ -249,6 +268,7 @@ expression: diff.instruction_rows
address: 47,
size: 5,
opcode: 414,
branch_dest: None,
},
),
kind: None,
@@ -269,6 +289,7 @@ expression: diff.instruction_rows
address: 52,
size: 1,
opcode: 662,
branch_dest: None,
},
),
kind: None,
@@ -282,6 +303,7 @@ expression: diff.instruction_rows
address: 53,
size: 5,
opcode: 414,
branch_dest: None,
},
),
kind: None,
@@ -302,6 +324,7 @@ expression: diff.instruction_rows
address: 58,
size: 1,
opcode: 662,
branch_dest: None,
},
),
kind: None,
@@ -322,6 +345,7 @@ expression: diff.instruction_rows
address: 59,
size: 1,
opcode: 465,
branch_dest: None,
},
),
kind: None,
@@ -335,6 +359,9 @@ expression: diff.instruction_rows
address: 60,
size: 4,
opcode: 65534,
branch_dest: Some(
17,
),
},
),
kind: None,
@@ -360,6 +387,9 @@ expression: diff.instruction_rows
address: 64,
size: 4,
opcode: 65534,
branch_dest: Some(
23,
),
},
),
kind: None,
@@ -378,6 +408,9 @@ expression: diff.instruction_rows
address: 68,
size: 4,
opcode: 65534,
branch_dest: Some(
29,
),
},
),
kind: None,
@@ -396,6 +429,9 @@ expression: diff.instruction_rows
address: 72,
size: 4,
opcode: 65534,
branch_dest: Some(
35,
),
},
),
kind: None,
@@ -414,6 +450,9 @@ expression: diff.instruction_rows
address: 76,
size: 4,
opcode: 65534,
branch_dest: Some(
41,
),
},
),
kind: None,
@@ -432,6 +471,9 @@ expression: diff.instruction_rows
address: 80,
size: 4,
opcode: 65534,
branch_dest: Some(
47,
),
},
),
kind: None,
@@ -450,6 +492,9 @@ expression: diff.instruction_rows
address: 84,
size: 4,
opcode: 65534,
branch_dest: Some(
53,
),
},
),
kind: None,
@@ -468,6 +513,7 @@ expression: diff.instruction_rows
address: 88,
size: 1,
opcode: 465,
branch_dest: None,
},
),
kind: None,
@@ -481,6 +527,7 @@ expression: diff.instruction_rows
address: 89,
size: 1,
opcode: 465,
branch_dest: None,
},
),
kind: None,
@@ -494,6 +541,7 @@ expression: diff.instruction_rows
address: 90,
size: 1,
opcode: 465,
branch_dest: None,
},
),
kind: None,
@@ -507,6 +555,7 @@ expression: diff.instruction_rows
address: 91,
size: 1,
opcode: 465,
branch_dest: None,
},
),
kind: None,
@@ -520,6 +569,7 @@ expression: diff.instruction_rows
address: 92,
size: 1,
opcode: 465,
branch_dest: None,
},
),
kind: None,
@@ -533,6 +583,7 @@ expression: diff.instruction_rows
address: 93,
size: 1,
opcode: 465,
branch_dest: None,
},
),
kind: None,
@@ -546,6 +597,7 @@ expression: diff.instruction_rows
address: 94,
size: 1,
opcode: 465,
branch_dest: None,
},
),
kind: None,
@@ -559,6 +611,7 @@ expression: diff.instruction_rows
address: 95,
size: 1,
opcode: 465,
branch_dest: None,
},
),
kind: None,

View File

@@ -6,7 +6,7 @@ expression: output
[(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(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), (BranchDest(60), Normal, 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)]
@@ -22,13 +22,13 @@ expression: output
[(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(60), Normal, 5), (Basic(" ~> "), Rotating(1), 0), (Opcode(".dword", 65534), Normal, 10), (BranchDest(17), Normal, 0), (Basic(" ~>"), Rotating(2), 0), (Eol, Normal, 0)]
[(Address(64), Normal, 5), (Spacing(4), Normal, 0), (Opcode(".dword", 65534), Normal, 10), (BranchDest(23), Normal, 0), (Basic(" ~>"), Rotating(3), 0), (Eol, Normal, 0)]
[(Address(68), Normal, 5), (Spacing(4), Normal, 0), (Opcode(".dword", 65534), Normal, 10), (BranchDest(29), Normal, 0), (Basic(" ~>"), Rotating(4), 0), (Eol, Normal, 0)]
[(Address(72), Normal, 5), (Spacing(4), Normal, 0), (Opcode(".dword", 65534), Normal, 10), (BranchDest(35), Normal, 0), (Basic(" ~>"), Rotating(5), 0), (Eol, Normal, 0)]
[(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)]

View File

@@ -311,4 +311,5 @@ Object {
split_meta: None,
path: None,
timestamp: None,
flow_analysis_results: {},
}

View File

@@ -0,0 +1,164 @@
---
source: objdiff-core/tests/arch_x86.rs
expression: obj
---
Object {
arch: ArchX86 {
arch: X86,
endianness: Little,
},
endianness: Little,
symbols: [
Symbol {
name: "42b830_convertToUppercaseShiftJIS.obj",
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: "LAB_0042b850",
demangled_name: None,
address: 32,
size: 0,
kind: Object,
section: Some(
0,
),
flags: FlagSet(Local),
align: None,
virtual_address: None,
},
Symbol {
name: "LAB_0042b883",
demangled_name: None,
address: 83,
size: 0,
kind: Object,
section: Some(
0,
),
flags: FlagSet(Local),
align: None,
virtual_address: None,
},
Symbol {
name: "LAB_0042b87c",
demangled_name: None,
address: 76,
size: 0,
kind: Object,
section: Some(
0,
),
flags: FlagSet(Local),
align: None,
virtual_address: None,
},
Symbol {
name: "LAB_0042b884",
demangled_name: None,
address: 84,
size: 0,
kind: Object,
section: Some(
0,
),
flags: FlagSet(Local),
align: None,
virtual_address: None,
},
Symbol {
name: "LAB_0042b889",
demangled_name: None,
address: 89,
size: 0,
kind: Object,
section: Some(
0,
),
flags: FlagSet(Local),
align: None,
virtual_address: None,
},
Symbol {
name: "LAB_0042b845",
demangled_name: None,
address: 21,
size: 0,
kind: Object,
section: Some(
0,
),
flags: FlagSet(Local),
align: None,
virtual_address: None,
},
Symbol {
name: "LAB_0042b869",
demangled_name: None,
address: 57,
size: 0,
kind: Object,
section: Some(
0,
),
flags: FlagSet(Local),
align: None,
virtual_address: None,
},
Symbol {
name: "ConvertToUppercaseShiftJIS",
demangled_name: None,
address: 0,
size: 92,
kind: Function,
section: Some(
0,
),
flags: FlagSet(Global | SizeInferred),
align: None,
virtual_address: None,
},
],
sections: [
Section {
id: ".text-0",
name: ".text",
address: 0,
size: 92,
kind: Code,
data: SectionData(
92,
),
flags: FlagSet(),
align: Some(
16,
),
relocations: [],
line_info: {},
virtual_address: None,
},
],
split_meta: None,
path: None,
timestamp: None,
flow_analysis_results: {},
}

View File

@@ -46,7 +46,7 @@ rlwinmdec = "1.1"
ron = "0.8"
serde = { version = "1.0", features = ["derive"] }
time = { version = "0.3", features = ["formatting", "local-offset"] }
typed-path = "0.10"
typed-path = "0.11"
winit = { version = "0.30", features = ["wayland-csd-adwaita"] }
tracing-subscriber = { version = "0.3", features = ["env-filter"] }

View File

@@ -23,6 +23,8 @@ pub struct Appearance {
#[serde(skip)]
pub highlight_color: Color32, // WHITE
#[serde(skip)]
pub dataflow_color: Color32, //
#[serde(skip)]
pub replace_color: Color32, // LIGHT_BLUE
#[serde(skip)]
pub insert_color: Color32, // GREEN
@@ -61,6 +63,7 @@ impl Default for Appearance {
emphasized_text_color: Color32::LIGHT_GRAY,
deemphasized_text_color: Color32::DARK_GRAY,
highlight_color: Color32::WHITE,
dataflow_color: Color32::from_rgb(0, 128, 128),
replace_color: Color32::LIGHT_BLUE,
insert_color: Color32::GREEN,
delete_color: Color32::from_rgb(200, 40, 41),
@@ -104,6 +107,7 @@ impl Appearance {
self.emphasized_text_color = Color32::LIGHT_GRAY;
self.deemphasized_text_color = Color32::DARK_GRAY;
self.highlight_color = Color32::WHITE;
self.dataflow_color = Color32::from_rgb(0, 128, 128);
self.replace_color = Color32::LIGHT_BLUE;
self.insert_color = Color32::GREEN;
self.delete_color = Color32::from_rgb(200, 40, 41);
@@ -114,6 +118,7 @@ impl Appearance {
self.emphasized_text_color = Color32::DARK_GRAY;
self.deemphasized_text_color = Color32::LIGHT_GRAY;
self.highlight_color = Color32::BLACK;
self.dataflow_color = Color32::from_rgb(0, 128, 128);
self.replace_color = Color32::DARK_BLUE;
self.insert_color = Color32::DARK_GREEN;
self.delete_color = Color32::from_rgb(200, 40, 41);

View File

@@ -147,14 +147,20 @@ pub(crate) fn data_row_ui(
cur_addr += diff.len;
} else {
for byte in &diff.data {
let mut byte_text = format!("{byte:02x} ");
let mut byte_color = base_color;
if let Some(reloc_diff) = reloc_diffs.iter().find(|reloc_diff| {
reloc_diff.kind != DataDiffKind::None
&& reloc_diff.range.contains(&cur_addr_actual)
}) {
byte_color = get_color_for_diff_kind(reloc_diff.kind, appearance);
if let Some(reloc_diff) = reloc_diffs
.iter()
.find(|reloc_diff| reloc_diff.range.contains(&cur_addr_actual))
{
if *byte == 0 {
// Display 00 data bytes with a relocation as ?? instead.
byte_text = "?? ".to_string();
}
if reloc_diff.kind != DataDiffKind::None {
byte_color = get_color_for_diff_kind(reloc_diff.kind, appearance);
}
}
let byte_text = format!("{byte:02x} ");
write_text(byte_text.as_str(), byte_color, &mut job, appearance.code_font.clone());
cur_addr += 1;
cur_addr_actual += 1;

View File

@@ -49,7 +49,9 @@ impl<'a> DiffColumnContext<'a> {
let selected_symbol = match view {
View::SymbolDiff => None,
View::FunctionDiff | View::ExtabDiff => match (obj, selected_symbol) {
(Some(obj), Some(s)) => find_symbol(&obj.0, s).map(SelectedSymbol::Symbol),
(Some(obj), Some(s)) => {
obj.0.symbol_by_name(&s.symbol_name).map(SelectedSymbol::Symbol)
}
_ => None,
},
View::DataDiff => match (obj, selected_symbol) {
@@ -279,6 +281,24 @@ pub fn diff_view_ui(
})
});
}
// Only need to check the first Object. Technically the first could not have a flow analysis
// result while the second does but we don't want to waste space on two separate checkboxes.
if state.current_view == View::FunctionDiff
&& result
.first_obj
.as_ref()
.is_some_and(|(first, _)| first.has_flow_analysis_result())
{
let mut value = diff_config.show_data_flow;
if ui
.checkbox(&mut value, "Show data flow")
.on_hover_text("Show data flow analysis results in place of register names")
.clicked()
{
ret = Some(DiffViewAction::SetShowDataFlow(value));
}
}
} else if column == 1 {
// Right column
@@ -497,6 +517,7 @@ pub fn diff_view_ui(
(state.current_view, left_ctx.obj, right_ctx.obj, left_ctx.section, right_ctx.section)
{
// Joint diff view
hotkeys::check_scroll_hotkeys(ui, true);
let left_total_bytes =
left_section_diff.data_diff.iter().fold(0usize, |accum, item| accum + item.len);
let right_total_bytes =
@@ -779,10 +800,6 @@ fn missing_obj_ui(ui: &mut Ui, appearance: &Appearance) {
});
}
fn find_symbol(obj: &Object, selected_symbol: &SymbolRefByName) -> Option<usize> {
obj.symbols.iter().position(|symbol| symbol.name == selected_symbol.symbol_name)
}
fn find_section(obj: &Object, section_name: &str) -> Option<usize> {
obj.sections.iter().position(|section| section.name == section_name)
}

View File

@@ -174,6 +174,7 @@ fn diff_text_ui(
DiffTextColor::Normal => appearance.text_color,
DiffTextColor::Dim => appearance.deemphasized_text_color,
DiffTextColor::Bright => appearance.emphasized_text_color,
DiffTextColor::DataFlow => appearance.dataflow_color,
DiffTextColor::Replace => appearance.replace_color,
DiffTextColor::Delete => appearance.delete_color,
DiffTextColor::Insert => appearance.insert_color,

View File

@@ -79,6 +79,8 @@ pub enum DiffViewAction {
SetMapping(usize, usize),
/// Set the show_mapped_symbols flag
SetShowMappedSymbols(bool),
/// Set the show_data_flow flag
SetShowDataFlow(bool),
}
#[derive(Debug, Clone, Default, Eq, PartialEq)]
@@ -350,6 +352,12 @@ impl DiffViewState {
DiffViewAction::SetShowMappedSymbols(value) => {
self.symbol_state.show_mapped_symbols = value;
}
DiffViewAction::SetShowDataFlow(value) => {
let Ok(mut state) = state.write() else {
return;
};
state.config.diff_obj_config.show_data_flow = value;
}
}
}

View File

@@ -28,13 +28,13 @@ xxhash-rust = { version = "0.8", default-features = false, features = ["xxh3"] }
[dependencies.objdiff-core]
path = "../objdiff-core"
default-features = false
features = ["arm", "arm64", "mips", "ppc", "x86", "dwarf"]
features = ["arm", "arm64", "mips", "ppc", "superh", "x86", "dwarf"]
[target.'cfg(target_family = "wasm")'.dependencies]
talc = { version = "4.4", default-features = false, features = ["lock_api"] }
[target.'cfg(target_os = "wasi")'.dependencies]
wit-bindgen = { version = "0.41", default-features = false, features = ["macros"] }
wit-bindgen = { version = "0.42", default-features = false, features = ["macros"] }
[build-dependencies]
wit-deps = "0.5"

View File

@@ -1,12 +1,12 @@
{
"name": "objdiff-wasm",
"version": "3.0.0-beta.6",
"version": "3.0.0-beta.10",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "objdiff-wasm",
"version": "3.0.0-beta.6",
"version": "3.0.0-beta.10",
"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.6",
"version": "3.0.0-beta.10",
"description": "A local diffing tool for decompilation projects.",
"author": {
"name": "Luke Street",

View File

@@ -24,14 +24,14 @@ wit_bindgen::generate!({
use exports::objdiff::core::{
diff::{
DiffConfigBorrow, DiffResult, Guest as GuestDiff, GuestDiffConfig, GuestObject,
GuestObjectDiff, Object, ObjectBorrow, ObjectDiff, ObjectDiffBorrow,
GuestObjectDiff, MappingConfig, Object, ObjectBorrow, ObjectDiff, ObjectDiffBorrow,
SymbolFlags, SymbolInfo, SymbolKind, SymbolRef,
},
display::{
ContextItem, ContextItemCopy, ContextItemNavigate, DiffText, DiffTextColor, DiffTextOpcode,
DiffTextSegment, DiffTextSymbol, DisplayConfig, Guest as GuestDisplay, HoverItem,
HoverItemColor, HoverItemText, InstructionDiffKind, InstructionDiffRow, SectionDisplay,
SectionDisplaySymbol, SymbolDisplay, SymbolFilter, SymbolFlags, SymbolKind,
SymbolNavigationKind, SymbolRef,
SymbolDisplay, SymbolFilter, SymbolNavigationKind,
},
};
@@ -59,15 +59,17 @@ impl GuestDiff for Component {
left: Option<ObjectBorrow>,
right: Option<ObjectBorrow>,
diff_config: DiffConfigBorrow,
mapping_config: MappingConfig,
) -> Result<DiffResult, String> {
let diff_config = diff_config.get::<ResourceDiffConfig>().0.borrow();
let mapping_config = diff::MappingConfig::from(mapping_config);
log::debug!("Running diff with config: {:?}", diff_config);
let result = diff::diff_objs(
left.as_ref().map(|o| o.get::<ResourceObject>().0.as_ref()),
right.as_ref().map(|o| o.get::<ResourceObject>().0.as_ref()),
None,
&diff_config,
&diff::MappingConfig::default(),
&mapping_config,
)
.map_err(|e| e.to_string())?;
Ok(DiffResult {
@@ -134,48 +136,47 @@ impl GuestDisplay for Component {
name: d.name,
size: d.size,
match_percent: d.match_percent,
symbols: d
.symbols
.into_iter()
.map(|s| SectionDisplaySymbol {
symbol: s.symbol as SymbolRef,
is_mapping_symbol: s.is_mapping_symbol,
})
.collect(),
symbols: d.symbols.into_iter().map(to_symbol_ref).collect(),
})
.collect()
}
fn display_symbol(
diff: ObjectDiffBorrow,
symbol_display: SectionDisplaySymbol,
) -> SymbolDisplay {
fn display_symbol(diff: ObjectDiffBorrow, symbol_ref: SymbolRef) -> SymbolDisplay {
let obj_diff = diff.get::<ResourceObjectDiff>();
let obj = obj_diff.0.as_ref();
let obj_diff = &obj_diff.1;
let symbol_idx = symbol_display.symbol as usize;
let Some(symbol) = obj.symbols.get(symbol_idx) else {
return SymbolDisplay { name: "<unknown>".to_string(), ..Default::default() };
let symbol_display = from_symbol_ref(symbol_ref);
let Some(symbol) = obj.symbols.get(symbol_display.symbol) else {
return SymbolDisplay {
info: SymbolInfo { name: "<unknown>".to_string(), ..Default::default() },
..Default::default()
};
};
let symbol_diff = if symbol_display.is_mapping_symbol {
obj_diff
.mapping_symbols
.iter()
.find(|s| s.symbol_index == symbol_idx)
.find(|s| s.symbol_index == symbol_display.symbol)
.map(|s| &s.symbol_diff)
} else {
obj_diff.symbols.get(symbol_idx)
obj_diff.symbols.get(symbol_display.symbol)
};
SymbolDisplay {
name: symbol.name.clone(),
demangled_name: symbol.demangled_name.clone(),
address: symbol.address,
size: symbol.size,
kind: SymbolKind::from(symbol.kind),
section: symbol.section.map(|s| s as u32),
flags: SymbolFlags::from(symbol.flags),
align: symbol.align.map(|a| a.get()),
virtual_address: symbol.virtual_address,
info: SymbolInfo {
id: to_symbol_ref(symbol_display),
name: symbol.name.clone(),
demangled_name: symbol.demangled_name.clone(),
address: symbol.address,
size: symbol.size,
kind: SymbolKind::from(symbol.kind),
section: symbol.section.map(|s| s as u32),
section_name: symbol
.section
.and_then(|s| obj.sections.get(s).map(|sec| sec.name.clone())),
flags: SymbolFlags::from(symbol.flags),
align: symbol.align.map(|a| a.get()),
virtual_address: symbol.virtual_address,
},
target_symbol: symbol_diff.and_then(|sd| sd.target_symbol.map(|s| s as u32)),
match_percent: symbol_diff.and_then(|sd| sd.match_percent),
diff_score: symbol_diff.and_then(|sd| sd.diff_score),
@@ -185,22 +186,22 @@ impl GuestDisplay for Component {
fn display_instruction_row(
diff: ObjectDiffBorrow,
symbol_display: SectionDisplaySymbol,
symbol_ref: SymbolRef,
row_index: u32,
diff_config: DiffConfigBorrow,
) -> InstructionDiffRow {
let obj_diff = diff.get::<ResourceObjectDiff>();
let obj = obj_diff.0.as_ref();
let obj_diff = &obj_diff.1;
let symbol_idx = symbol_display.symbol as usize;
let symbol_display = from_symbol_ref(symbol_ref);
let symbol_diff = if symbol_display.is_mapping_symbol {
obj_diff
.mapping_symbols
.iter()
.find(|s| s.symbol_index == symbol_idx)
.find(|s| s.symbol_index == symbol_display.symbol)
.map(|s| &s.symbol_diff)
} else {
obj_diff.symbols.get(symbol_idx)
obj_diff.symbols.get(symbol_display.symbol)
};
let Some(row) = symbol_diff.and_then(|sd| sd.instruction_rows.get(row_index as usize))
else {
@@ -208,7 +209,7 @@ impl GuestDisplay for Component {
};
let diff_config = diff_config.get::<ResourceDiffConfig>().0.borrow();
let mut segments = Vec::with_capacity(16);
diff::display::display_row(obj, symbol_idx, row, &diff_config, |segment| {
diff::display::display_row(obj, symbol_display.symbol, row, &diff_config, |segment| {
segments.push(DiffTextSegment::from(segment));
Ok(())
})
@@ -216,26 +217,22 @@ impl GuestDisplay for Component {
InstructionDiffRow { segments, diff_kind: InstructionDiffKind::from(row.kind) }
}
fn symbol_context(
diff: ObjectDiffBorrow,
symbol_display: SectionDisplaySymbol,
) -> Vec<ContextItem> {
fn symbol_context(diff: ObjectDiffBorrow, symbol_ref: SymbolRef) -> Vec<ContextItem> {
let obj_diff = diff.get::<ResourceObjectDiff>();
let obj = obj_diff.0.as_ref();
let symbol_display = from_symbol_ref(symbol_ref);
diff::display::symbol_context(obj, symbol_display.symbol as usize)
.into_iter()
.map(|item| ContextItem::from(item))
.collect()
}
fn symbol_hover(
diff: ObjectDiffBorrow,
symbol_display: SectionDisplaySymbol,
) -> Vec<HoverItem> {
fn symbol_hover(diff: ObjectDiffBorrow, symbol_ref: SymbolRef) -> Vec<HoverItem> {
let obj_diff = diff.get::<ResourceObjectDiff>();
let obj = obj_diff.0.as_ref();
let addend = 0; // TODO
let override_color = None; // TODO: colorize replaced/deleted/inserted relocations
let symbol_display = from_symbol_ref(symbol_ref);
diff::display::symbol_hover(obj, symbol_display.symbol as usize, addend, override_color)
.into_iter()
.map(|item| HoverItem::from(item))
@@ -244,22 +241,22 @@ impl GuestDisplay for Component {
fn instruction_context(
diff: ObjectDiffBorrow,
symbol_display: SectionDisplaySymbol,
symbol_ref: SymbolRef,
row_index: u32,
diff_config: DiffConfigBorrow,
) -> Vec<ContextItem> {
let obj_diff = diff.get::<ResourceObjectDiff>();
let obj = obj_diff.0.as_ref();
let obj_diff = &obj_diff.1;
let symbol_idx = symbol_display.symbol as usize;
let symbol_display = from_symbol_ref(symbol_ref);
let symbol_diff = if symbol_display.is_mapping_symbol {
obj_diff
.mapping_symbols
.iter()
.find(|s| s.symbol_index == symbol_idx)
.find(|s| s.symbol_index == symbol_display.symbol)
.map(|s| &s.symbol_diff)
} else {
obj_diff.symbols.get(symbol_idx)
obj_diff.symbols.get(symbol_display.symbol)
};
let Some(ins_ref) = symbol_diff
.and_then(|sd| sd.instruction_rows.get(row_index as usize))
@@ -268,7 +265,7 @@ impl GuestDisplay for Component {
return Vec::new();
};
let diff_config = diff_config.get::<ResourceDiffConfig>().0.borrow();
let Some(resolved) = obj.resolve_instruction_ref(symbol_idx, ins_ref) else {
let Some(resolved) = obj.resolve_instruction_ref(symbol_display.symbol, ins_ref) else {
return vec![ContextItem::Copy(ContextItemCopy {
value: "Failed to resolve instruction".to_string(),
label: Some("error".to_string()),
@@ -291,22 +288,22 @@ impl GuestDisplay for Component {
fn instruction_hover(
diff: ObjectDiffBorrow,
symbol_display: SectionDisplaySymbol,
symbol_ref: SymbolRef,
row_index: u32,
diff_config: DiffConfigBorrow,
) -> Vec<HoverItem> {
let obj_diff = diff.get::<ResourceObjectDiff>();
let obj = obj_diff.0.as_ref();
let obj_diff = &obj_diff.1;
let symbol_idx = symbol_display.symbol as usize;
let symbol_display = from_symbol_ref(symbol_ref);
let symbol_diff = if symbol_display.is_mapping_symbol {
obj_diff
.mapping_symbols
.iter()
.find(|s| s.symbol_index == symbol_idx)
.find(|s| s.symbol_index == symbol_display.symbol)
.map(|s| &s.symbol_diff)
} else {
obj_diff.symbols.get(symbol_idx)
obj_diff.symbols.get(symbol_display.symbol)
};
let Some(ins_ref) = symbol_diff
.and_then(|sd| sd.instruction_rows.get(row_index as usize))
@@ -315,7 +312,7 @@ impl GuestDisplay for Component {
return Vec::new();
};
let diff_config = diff_config.get::<ResourceDiffConfig>().0.borrow();
let Some(resolved) = obj.resolve_instruction_ref(symbol_idx, ins_ref) else {
let Some(resolved) = obj.resolve_instruction_ref(symbol_display.symbol, ins_ref) else {
return vec![HoverItem::Text(HoverItemText {
label: "Error".to_string(),
value: "Failed to resolve instruction".to_string(),
@@ -404,6 +401,7 @@ impl From<diff::display::DiffTextColor> for DiffTextColor {
diff::display::DiffTextColor::Replace => DiffTextColor::Replace,
diff::display::DiffTextColor::Delete => DiffTextColor::Delete,
diff::display::DiffTextColor::Insert => DiffTextColor::Insert,
diff::display::DiffTextColor::DataFlow => DiffTextColor::DataFlow,
diff::display::DiffTextColor::Rotating(v) => DiffTextColor::Rotating(v),
}
}
@@ -497,20 +495,56 @@ impl GuestObject for ResourceObject {
}
impl GuestObjectDiff for ResourceObjectDiff {
fn find_symbol(&self, name: String, section_name: Option<String>) -> Option<SymbolRef> {
fn find_symbol(&self, name: String, section_name: Option<String>) -> Option<SymbolInfo> {
let obj = self.0.as_ref();
obj.symbols
.iter()
.position(|s| {
s.name == name
&& match section_name.as_deref() {
Some(section_name) => {
s.section.is_some_and(|n| obj.sections[n].name == section_name)
}
None => true,
let symbol_idx = obj.symbols.iter().position(|s| {
s.name == name
&& match section_name.as_deref() {
Some(section_name) => {
s.section.is_some_and(|n| obj.sections[n].name == section_name)
}
})
.map(|i| i as SymbolRef)
None => true,
}
})?;
let symbol = obj.symbols.get(symbol_idx)?;
Some(SymbolInfo {
id: symbol_idx as SymbolRef,
name: symbol.name.clone(),
demangled_name: symbol.demangled_name.clone(),
address: symbol.address,
size: symbol.size,
kind: SymbolKind::from(symbol.kind),
section: symbol.section.map(|s| s as u32),
section_name: symbol
.section
.and_then(|s| obj.sections.get(s).map(|sec| sec.name.clone())),
flags: SymbolFlags::from(symbol.flags),
align: symbol.align.map(|a| a.get()),
virtual_address: symbol.virtual_address,
})
}
fn get_symbol(&self, symbol_ref: SymbolRef) -> Option<SymbolInfo> {
let obj = self.0.as_ref();
let symbol_display = from_symbol_ref(symbol_ref);
let Some(symbol) = obj.symbols.get(symbol_display.symbol) else {
return None;
};
Some(SymbolInfo {
id: to_symbol_ref(symbol_display),
name: symbol.name.clone(),
demangled_name: symbol.demangled_name.clone(),
address: symbol.address,
size: symbol.size,
kind: SymbolKind::from(symbol.kind),
section: symbol.section.map(|s| s as u32),
section_name: symbol
.section
.and_then(|s| obj.sections.get(s).map(|sec| sec.name.clone())),
flags: SymbolFlags::from(symbol.flags),
align: symbol.align.map(|a| a.get()),
virtual_address: symbol.virtual_address,
})
}
}
@@ -580,18 +614,28 @@ impl Default for SymbolFlags {
fn default() -> Self { Self::empty() }
}
impl Default for SymbolDisplay {
impl Default for SymbolInfo {
fn default() -> Self {
Self {
id: u32::MAX,
name: Default::default(),
demangled_name: Default::default(),
address: Default::default(),
size: Default::default(),
kind: Default::default(),
section: Default::default(),
section_name: Default::default(),
flags: Default::default(),
align: Default::default(),
virtual_address: Default::default(),
}
}
}
impl Default for SymbolDisplay {
fn default() -> Self {
Self {
info: Default::default(),
target_symbol: Default::default(),
match_percent: Default::default(),
diff_score: Default::default(),
@@ -600,4 +644,30 @@ impl Default for SymbolDisplay {
}
}
impl From<MappingConfig> for diff::MappingConfig {
fn from(config: MappingConfig) -> Self {
Self {
mappings: config.mappings.into_iter().collect(),
selecting_left: config.selecting_left,
selecting_right: config.selecting_right,
}
}
}
fn from_symbol_ref(symbol_ref: SymbolRef) -> diff::display::SectionDisplaySymbol {
diff::display::SectionDisplaySymbol {
symbol: (symbol_ref & !(1 << 31)) as usize,
is_mapping_symbol: (symbol_ref & (1 << 31)) != 0,
}
}
fn to_symbol_ref(display_symbol: diff::display::SectionDisplaySymbol) -> SymbolRef {
if display_symbol.is_mapping_symbol {
// Use the highest bit to indicate a mapping symbol
display_symbol.symbol as u32 | (1 << 31)
} else {
display_symbol.symbol as u32
}
}
export!(Component);

View File

@@ -24,58 +24,8 @@ interface diff {
hash: func() -> u64;
}
resource object-diff {
find-symbol: func(
name: string,
section-name: option<string>
) -> option<u32>;
}
record diff-result {
left: option<object-diff>,
right: option<object-diff>,
}
run-diff: func(
left: option<borrow<object>>,
right: option<borrow<object>>,
config: borrow<diff-config>,
) -> result<diff-result, string>;
}
interface display {
use diff.{
object,
object-diff,
diff-config
};
type symbol-ref = u32;
record display-config {
show-hidden-symbols: bool,
show-mapped-symbols: bool,
reverse-fn-order: bool,
}
record symbol-filter {
regex: option<string>,
mapping: option<symbol-ref>,
}
record section-display-symbol {
symbol: symbol-ref,
is-mapping-symbol: bool,
}
record section-display {
id: string,
name: string,
size: u64,
match-percent: option<f32>,
symbols: list<section-display-symbol>,
}
enum symbol-kind {
unknown,
function,
@@ -94,17 +44,74 @@ interface display {
ignored,
}
record symbol-display {
record symbol-info {
id: symbol-ref,
name: string,
demangled-name: option<string>,
address: u64,
size: u64,
kind: symbol-kind,
section: option<u32>,
section-name: option<string>,
%flags: symbol-flags,
align: option<u32>,
virtual-address: option<u64>,
}
resource object-diff {
find-symbol: func(
name: string,
section-name: option<string>
) -> option<symbol-info>;
get-symbol: func(
id: u32
) -> option<symbol-info>;
}
record diff-result {
left: option<object-diff>,
right: option<object-diff>,
}
run-diff: func(
left: option<borrow<object>>,
right: option<borrow<object>>,
config: borrow<diff-config>,
mapping: mapping-config,
) -> result<diff-result, string>;
}
interface display {
use diff.{
object,
object-diff,
diff-config,
symbol-info,
symbol-ref
};
record display-config {
show-hidden-symbols: bool,
show-mapped-symbols: bool,
reverse-fn-order: bool,
}
record symbol-filter {
regex: option<string>,
mapping: option<symbol-ref>,
}
record section-display {
id: string,
name: string,
size: u64,
match-percent: option<f32>,
symbols: list<symbol-ref>,
}
record symbol-display {
info: symbol-info,
target-symbol: option<symbol-ref>,
match-percent: option<f32>,
diff-score: option<tuple<u64, u64>>,
@@ -194,6 +201,7 @@ interface display {
dim,
bright,
replace,
data-flow,
delete,
insert,
rotating(u8),
@@ -232,36 +240,36 @@ interface display {
display-symbol: func(
diff: borrow<object-diff>,
symbol: section-display-symbol,
symbol: symbol-ref,
) -> symbol-display;
display-instruction-row: func(
diff: borrow<object-diff>,
symbol: section-display-symbol,
symbol: symbol-ref,
row-index: u32,
config: borrow<diff-config>,
) -> instruction-diff-row;
symbol-context: func(
diff: borrow<object-diff>,
symbol: section-display-symbol,
symbol: symbol-ref,
) -> list<context-item>;
symbol-hover: func(
diff: borrow<object-diff>,
symbol: section-display-symbol,
symbol: symbol-ref,
) -> list<hover-item>;
instruction-context: func(
diff: borrow<object-diff>,
symbol: section-display-symbol,
symbol: symbol-ref,
row-index: u32,
config: borrow<diff-config>,
) -> list<context-item>;
instruction-hover: func(
diff: borrow<object-diff>,
symbol: section-display-symbol,
symbol: symbol-ref,
row-index: u32,
config: borrow<diff-config>,
) -> list<hover-item>;