Compare commits

..

13 Commits

Author SHA1 Message Date
LagoLunatic 66c879a95d Version 3.6.1 (#328) 2026-01-30 05:40:02 +00:00
rjkiv d53c358d2f automatically match MSVC symbols in anonymous namespaces (#325)
* automatically match MSVC symbols in anonymous namespaces

* tweak logic for detecting anonymous MSVC symbols

* fix package format

* address feedback

* lazylock the regex

* formatting

* use once_cell instead

* formatting

* remove regex
2026-01-30 00:13:10 -05:00
Avery Townsend f24c017545 merge gnu function and data sections (#327) 2026-01-29 16:58:52 -07:00
Luke Street 3b8603ab22 Version 3.6.0 2026-01-24 20:29:33 -07:00
Luke Street 86b7d6b229 Remove extra relocs in PPC data_reloc_size 2026-01-24 20:23:40 -07:00
Haydn Trigg 3f82781808 Better COFF String Detection and Big Endian Wide Strings (#321)
* Better COFF and Big Endian Strings

* Cargo Check and Format Fixes

* Rework DataType::String in display_literals

---------

Co-authored-by: Luke Street <luke@street.dev>
2026-01-25 03:23:06 +00:00
Haydn Trigg 926dd06ce9 Handle IMAGE_REL_PPC_ADDR32 size in Coff (#320) 2026-01-24 20:22:45 -07:00
Luke Street de70b6fca0 Restore objdiff-cli oneshot mode (JSON output) (#323) 2026-01-24 19:58:29 -07:00
LagoLunatic 79542a4725 Significantly improve diffing performance and fix minor bug with bss section match percents (#316)
* Move symbol name processing from diff step to read step

* Use min_by_key instead of sorted_unstable_by_key

* Sort all symbols by address when reading the object

This fixes an underreport bug in diff_bss_section.

* More symbol sorting logic on read

* Move is_name_compiler_generated into SymbolFlag & filter out File symbols

---------

Co-authored-by: Luke Street <luke@street.dev>
2026-01-24 19:41:03 -07:00
Avery Townsend 0ebd2ae459 add more i386 elf relocs (#319)
Co-authored-by: Avery Townsend <self@codeshaunted.com>
2026-01-24 19:33:42 -07:00
LagoLunatic 08ebea8bd2 Fix the symbol filter not working in the mapping view (#315)
* Fix the symbol filter not working in the mapping view

* Separate the mapping filter from the regular filter

* Make set/get search function names consistent with each other
2026-01-25 01:15:01 +00:00
Luke Street 46b8890159 Fix clippy & advisory warnings 2026-01-24 17:07:25 -07:00
LagoLunatic 8356fe670d Implement go to next/previous difference (#313) 2026-01-24 16:47:27 -07:00
48 changed files with 8035 additions and 6630 deletions
Generated
+4 -4
View File
@@ -3491,7 +3491,7 @@ dependencies = [
[[package]]
name = "objdiff-cli"
version = "3.5.1"
version = "3.6.1"
dependencies = [
"anyhow",
"argp",
@@ -3514,7 +3514,7 @@ dependencies = [
[[package]]
name = "objdiff-core"
version = "3.5.1"
version = "3.6.1"
dependencies = [
"anyhow",
"arm-attr",
@@ -3570,7 +3570,7 @@ dependencies = [
[[package]]
name = "objdiff-gui"
version = "3.5.1"
version = "3.6.1"
dependencies = [
"anyhow",
"argp",
@@ -3608,7 +3608,7 @@ dependencies = [
[[package]]
name = "objdiff-wasm"
version = "3.5.1"
version = "3.6.1"
dependencies = [
"log",
"objdiff-core",
+1 -1
View File
@@ -14,7 +14,7 @@ default-members = [
resolver = "3"
[workspace.package]
version = "3.5.1"
version = "3.6.1"
authors = ["Luke Street <luke@street.dev>"]
edition = "2024"
license = "MIT OR Apache-2.0"
+1
View File
@@ -77,6 +77,7 @@ ignore = [
{ id = "RUSTSEC-2025-0052", reason = "Unmaintained async-std crate is an indirect dependency" },
{ id = "RUSTSEC-2025-0119", reason = "Unmaintained number_prefix crate is an indirect dependency" },
{ id = "RUSTSEC-2025-0134", reason = "Unmaintained rustls-pemfile crate is an indirect dependency" },
{ id = "RUSTSEC-2026-0002", reason = "Unsound advisory in lru indirect dependency" },
]
# If this is true, then cargo deny will use the git executable to fetch advisory database.
# If this is false, then it uses a built-in git library.
+47 -3
View File
@@ -19,6 +19,7 @@ use crossterm::{
},
};
use objdiff_core::{
bindings::diff::DiffResult,
build::{
BuildConfig, BuildStatus,
watcher::{Watcher, create_watcher},
@@ -28,7 +29,7 @@ use objdiff_core::{
build_globset,
path::{check_path_buf, platform_path, platform_path_serde_option},
},
diff::{DiffObjConfig, MappingConfig, ObjectDiff},
diff::{self, DiffObjConfig, DiffSide, MappingConfig, ObjectDiff},
jobs::{
Job, JobQueue, JobResult,
objdiff::{ObjDiffConfig, start_build},
@@ -40,7 +41,10 @@ use typed_path::{Utf8PlatformPath, Utf8PlatformPathBuf};
use crate::{
cmd::apply_config_args,
util::term::crossterm_panic_handler,
util::{
output::{OutputFormat, write_output},
term::crossterm_panic_handler,
},
views::{EventControlFlow, EventResult, UiView, function_diff::FunctionDiffUi},
};
@@ -60,6 +64,12 @@ pub struct Args {
#[argp(option, short = 'u')]
/// Unit name within project
unit: Option<String>,
#[argp(option, short = 'o', from_str_fn(platform_path))]
/// Output file (one-shot mode) ("-" for stdout)
output: Option<Utf8PlatformPathBuf>,
#[argp(option)]
/// Output format (json, json-pretty, proto) (default: json)
format: Option<String>,
#[argp(positional)]
/// Function symbol to diff
symbol: Option<String>,
@@ -158,7 +168,41 @@ pub fn run(args: Args) -> Result<()> {
_ => bail!("Either target and base or project and unit must be specified"),
};
run_interactive(args, target_path, base_path, project_config, unit_options)
if let Some(output) = &args.output {
run_oneshot(&args, output, target_path.as_deref(), base_path.as_deref(), unit_options)
} else {
run_interactive(args, target_path, base_path, project_config, unit_options)
}
}
fn run_oneshot(
args: &Args,
output: &Utf8PlatformPath,
target_path: Option<&Utf8PlatformPath>,
base_path: Option<&Utf8PlatformPath>,
unit_options: Option<ProjectOptions>,
) -> Result<()> {
let output_format = OutputFormat::from_option(args.format.as_deref())?;
let (diff_config, mapping_config) = build_config_from_args(args, None, unit_options.as_ref())?;
let target = target_path
.map(|p| {
obj::read::read(p.as_ref(), &diff_config, DiffSide::Target)
.with_context(|| format!("Loading {p}"))
})
.transpose()?;
let base = base_path
.map(|p| {
obj::read::read(p.as_ref(), &diff_config, DiffSide::Base)
.with_context(|| format!("Loading {p}"))
})
.transpose()?;
let result =
diff::diff_objs(target.as_ref(), base.as_ref(), None, &diff_config, &mapping_config)?;
let left = target.as_ref().and_then(|o| result.left.as_ref().map(|d| (o, d)));
let right = base.as_ref().and_then(|o| result.right.as_ref().map(|d| (o, d)));
let diff_result = DiffResult::new(left, right, &diff_config)?;
write_output(&diff_result, Some(output), output_format)?;
Ok(())
}
fn build_config_from_args(
+162
View File
@@ -0,0 +1,162 @@
syntax = "proto3";
package objdiff.diff;
// Top-level diff result containing left and right object diffs
message DiffResult {
optional DiffObject left = 1;
optional DiffObject right = 2;
}
// Diff information for a single object file
message DiffObject {
repeated DiffSection sections = 1;
repeated DiffSymbol symbols = 2;
}
// Diff information for a section
message DiffSection {
string name = 1;
DiffSectionKind kind = 2;
uint64 size = 3;
uint64 address = 4;
optional float match_percent = 5;
repeated DiffDataSegment data_diff = 6;
repeated DiffDataRelocation reloc_diff = 7;
}
enum DiffSectionKind {
SECTION_UNKNOWN = 0;
SECTION_CODE = 1;
SECTION_DATA = 2;
SECTION_BSS = 3;
SECTION_COMMON = 4;
}
// Symbol with diff information
message DiffSymbol {
string name = 1;
optional string demangled_name = 2;
uint64 address = 3;
uint64 size = 4;
uint32 flags = 5;
// The symbol index in the _other_ object that this symbol was diffed against
optional uint32 target_symbol = 6;
optional float match_percent = 7;
// Instruction diff rows (for code symbols)
repeated DiffInstructionRow instructions = 8;
// Data diff (for data symbols)
repeated DiffDataSegment data_diff = 9;
}
// Symbol visibility flags (bitmask)
enum DiffSymbolFlag {
SYMBOL_NONE = 0;
SYMBOL_GLOBAL = 1;
SYMBOL_LOCAL = 2;
SYMBOL_WEAK = 4;
SYMBOL_COMMON = 8;
SYMBOL_HIDDEN = 16;
}
// A single instruction diff row
message DiffInstructionRow {
DiffKind diff_kind = 1;
optional DiffInstruction instruction = 2;
repeated DiffInstructionArgDiff arg_diff = 3;
}
// Parsed instruction information
message DiffInstruction {
uint64 address = 1;
uint32 size = 2;
// Formatted instruction string
string formatted = 3;
// Formatted instruction parts
repeated DiffInstructionPart parts = 4;
// Relocation information (if present)
optional DiffRelocation relocation = 5;
// Branch destination address
optional uint64 branch_dest = 6;
// Source line number (if present)
optional uint32 line_number = 7;
}
// An instruction part
message DiffInstructionPart {
oneof part {
// Basic text (whitespace, punctuation, etc.)
string basic = 1;
// Opcode/mnemonic
DiffOpcode opcode = 2;
// Argument
DiffInstructionArg arg = 3;
// Separator between arguments (comma)
bool separator = 4;
}
}
message DiffOpcode {
// Mnemonic string
string mnemonic = 1;
// Base opcode ID
uint32 opcode = 2;
}
// An instruction argument
message DiffInstructionArg {
oneof arg {
// Signed immediate value
sint64 signed = 1;
// Unsigned immediate value
uint64 unsigned = 2;
// Opaque string value (register names, etc.)
string opaque = 3;
// Relocation
bool reloc = 4;
// Branch destination address
uint64 branch_dest = 5;
}
}
// Relocation information
message DiffRelocation {
uint32 type = 1;
string type_name = 2;
uint32 target_symbol = 3;
int64 addend = 4;
}
// Argument diff information
message DiffInstructionArgDiff {
// If set, this argument differs from the other side.
// The value is a rotating index for coloring.
optional uint32 diff_index = 1;
}
// Diff kind for instructions and data
enum DiffKind {
DIFF_NONE = 0;
DIFF_REPLACE = 1;
DIFF_DELETE = 2;
DIFF_INSERT = 3;
DIFF_OP_MISMATCH = 4;
DIFF_ARG_MISMATCH = 5;
}
// Data diff segment
message DiffDataSegment {
DiffKind kind = 1;
bytes data = 2;
// Size may be larger than data length for BSS
uint64 size = 3;
}
// Data relocation diff
message DiffDataRelocation {
DiffRelocation relocation = 1;
DiffKind kind = 2;
// Address range this relocation covers
uint64 start = 3;
uint64 end = 4;
}
Binary file not shown.
+30 -17
View File
@@ -174,23 +174,36 @@ impl DataType {
strs.push((format!("{bytes:#?}"), None, None));
}
DataType::String => {
if let Some(nul_idx) = bytes.iter().position(|&c| c == b'\0') {
let str_bytes = &bytes[..nul_idx];
// Special case to display (ASCII) as the label for ASCII-only strings.
let (cow, _, had_errors) = encoding_rs::UTF_8.decode(str_bytes);
if !had_errors && cow.is_ascii() {
let string = format!("{cow}");
let copy_string = escape_special_ascii_characters(string.clone());
strs.push((string, Some("ASCII".into()), Some(copy_string)));
// Special case to display (ASCII) as the label for ASCII-only strings.
let mut is_ascii = false;
if bytes.is_ascii()
&& let Ok(str) = str::from_utf8(bytes)
{
let trimmed = str.trim_end_matches('\0');
if !trimmed.is_empty() {
let copy_string = escape_special_ascii_characters(trimmed);
strs.push((trimmed.to_string(), Some("ASCII".into()), Some(copy_string)));
is_ascii = true;
}
for (encoding, encoding_name) in SUPPORTED_ENCODINGS {
let (cow, _, had_errors) = encoding.decode(str_bytes);
// Avoid showing ASCII-only strings more than once if the encoding is ASCII-compatible.
if !had_errors && (!encoding.is_ascii_compatible() || !cow.is_ascii()) {
let string = format!("{cow}");
let copy_string = escape_special_ascii_characters(string.clone());
strs.push((string, Some(encoding_name.into()), Some(copy_string)));
}
}
for (encoding, encoding_name) in SUPPORTED_ENCODINGS {
// Avoid showing ASCII-only strings more than once if the encoding is ASCII-compatible.
if is_ascii && encoding.is_ascii_compatible() {
continue;
}
let (cow, _, had_errors) = encoding.decode(bytes);
if had_errors {
continue;
}
let trimmed = cow.trim_end_matches('\0');
if !trimmed.is_empty() {
let copy_string = escape_special_ascii_characters(trimmed);
strs.push((
trimmed.to_string(),
Some(encoding_name.into()),
Some(copy_string),
));
}
}
}
@@ -508,7 +521,7 @@ pub struct RelocationOverride {
/// Escape ASCII characters such as \n or \t, but not Unicode characters such as \u{3000}.
/// Suitable for copying to clipboard.
fn escape_special_ascii_characters(value: String) -> String {
fn escape_special_ascii_characters(value: &str) -> String {
let mut escaped = String::new();
escaped.push('"');
for c in value.chars() {
+7 -2
View File
@@ -342,7 +342,10 @@ impl Arch for ArchPpc {
elf::R_PPC_UADDR32 => 4,
_ => 1,
},
_ => 1,
RelocationFlags::Coff(r_type) => match r_type {
pe::IMAGE_REL_PPC_ADDR32 => 4,
_ => 1,
},
}
}
@@ -355,7 +358,9 @@ impl Arch for ArchPpc {
}
fn guess_data_type(&self, resolved: ResolvedInstructionRef, bytes: &[u8]) -> Option<DataType> {
if resolved.relocation.is_some_and(|r| r.symbol.name.starts_with("@stringBase")) {
if resolved.relocation.is_some_and(|r| {
r.symbol.name.starts_with("@stringBase") || r.symbol.name.starts_with("$SG")
}) {
// Pooled string.
return Some(DataType::String);
}
+25 -3
View File
@@ -68,7 +68,12 @@ impl ArchX86 {
_ => None,
},
RelocationFlags::Elf(typ) => match typ {
elf::R_386_32 | elf::R_386_PC32 => Some(4),
elf::R_386_32
| elf::R_386_PC32
| elf::R_386_GOT32
| elf::R_386_PLT32
| elf::R_386_GOTOFF
| elf::R_386_GOTPC => Some(4),
elf::R_386_16 => Some(2),
_ => None,
},
@@ -272,7 +277,15 @@ impl Arch for ArchX86 {
object::RelocationFlags::Coff {
typ: pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32,
}
| object::RelocationFlags::Elf { r_type: elf::R_386_32 | elf::R_386_PC32 } => {
| object::RelocationFlags::Elf {
r_type:
elf::R_386_32
| elf::R_386_PC32
| elf::R_386_GOT32
| elf::R_386_PLT32
| elf::R_386_GOTOFF
| elf::R_386_GOTPC,
} => {
let data =
section.data()?[address as usize..address as usize + 4].try_into()?;
self.endianness.read_i32_bytes(data) as i64
@@ -308,7 +321,16 @@ impl Arch for ArchX86 {
pe::IMAGE_REL_I386_REL32 => Some("IMAGE_REL_I386_REL32"),
_ => None,
},
_ => None,
RelocationFlags::Elf(typ) => match typ {
elf::R_386_32 => Some("R_386_32"),
elf::R_386_PC32 => Some("R_386_PC32"),
elf::R_386_GOT32 => Some("R_386_GOT32"),
elf::R_386_PLT32 => Some("R_386_PLT32"),
elf::R_386_GOTOFF => Some("R_386_GOTOFF"),
elf::R_386_GOTPC => Some("R_386_GOTPC"),
elf::R_386_16 => Some("R_386_16"),
_ => None,
},
},
Architecture::X86_64 => match flags {
RelocationFlags::Coff(typ) => match typ {
+350
View File
@@ -0,0 +1,350 @@
#![allow(clippy::needless_lifetimes)] // Generated serde code
use alloc::{
string::{String, ToString},
vec,
vec::Vec,
};
use core::fmt::Write;
use anyhow::Result;
use crate::{
diff::{self, DiffObjConfig, display::InstructionPart},
obj::{self, Object, SymbolFlag},
};
// Protobuf diff types
include!(concat!(env!("OUT_DIR"), "/objdiff.diff.rs"));
#[cfg(feature = "serde")]
include!(concat!(env!("OUT_DIR"), "/objdiff.diff.serde.rs"));
impl DiffResult {
pub fn new(
left: Option<(&Object, &diff::ObjectDiff)>,
right: Option<(&Object, &diff::ObjectDiff)>,
diff_config: &DiffObjConfig,
) -> Result<Self> {
Ok(Self {
left: left.map(|(obj, diff)| DiffObject::new(obj, diff, diff_config)).transpose()?,
right: right.map(|(obj, diff)| DiffObject::new(obj, diff, diff_config)).transpose()?,
})
}
}
impl DiffObject {
pub fn new(obj: &Object, diff: &diff::ObjectDiff, diff_config: &DiffObjConfig) -> Result<Self> {
let mut sections = Vec::with_capacity(obj.sections.len());
for (section_idx, section) in obj.sections.iter().enumerate() {
let section_diff = &diff.sections[section_idx];
sections.push(DiffSection::new(obj, section, section_diff));
}
let mut symbols = Vec::with_capacity(obj.symbols.len());
for (symbol_idx, symbol) in obj.symbols.iter().enumerate() {
let symbol_diff = &diff.symbols[symbol_idx];
if symbol.size == 0 || symbol.flags.contains(SymbolFlag::Ignored) {
continue;
}
symbols.push(DiffSymbol::new(obj, symbol_idx, symbol, symbol_diff, diff_config)?);
}
Ok(Self { sections, symbols })
}
}
impl DiffSection {
pub fn new(obj: &Object, section: &obj::Section, section_diff: &diff::SectionDiff) -> Self {
Self {
name: section.name.clone(),
kind: DiffSectionKind::from(section.kind) as i32,
size: section.size,
address: section.address,
match_percent: section_diff.match_percent,
data_diff: section_diff.data_diff.iter().map(DiffDataSegment::from).collect(),
reloc_diff: section_diff
.reloc_diff
.iter()
.map(|r| DiffDataRelocation::new(obj, r))
.collect(),
}
}
}
impl From<obj::SectionKind> for DiffSectionKind {
fn from(value: obj::SectionKind) -> Self {
match value {
obj::SectionKind::Unknown => DiffSectionKind::SectionUnknown,
obj::SectionKind::Code => DiffSectionKind::SectionCode,
obj::SectionKind::Data => DiffSectionKind::SectionData,
obj::SectionKind::Bss => DiffSectionKind::SectionBss,
obj::SectionKind::Common => DiffSectionKind::SectionCommon,
}
}
}
impl DiffSymbol {
pub fn new(
obj: &Object,
symbol_idx: usize,
symbol: &obj::Symbol,
symbol_diff: &diff::SymbolDiff,
diff_config: &DiffObjConfig,
) -> Result<Self> {
// Convert instruction rows
let instructions = symbol_diff
.instruction_rows
.iter()
.map(|row| DiffInstructionRow::new(obj, symbol_idx, row, diff_config))
.collect::<Result<Vec<_>>>()?;
// Convert data diff - flatten DataDiffRow segments into a single list
let data_diff: Vec<DiffDataSegment> = symbol_diff
.data_rows
.iter()
.flat_map(|row| row.segments.iter().map(DiffDataSegment::from))
.collect();
Ok(Self {
// Symbol metadata
name: symbol.name.clone(),
demangled_name: symbol.demangled_name.clone(),
address: symbol.address,
size: symbol.size,
flags: symbol_flags(&symbol.flags),
// Diff information
target_symbol: symbol_diff.target_symbol.map(|i| i as u32),
match_percent: symbol_diff.match_percent,
instructions,
data_diff,
})
}
}
fn symbol_flags(flags: &obj::SymbolFlagSet) -> u32 {
let mut result = 0u32;
if flags.contains(SymbolFlag::Global) {
result |= DiffSymbolFlag::SymbolGlobal as u32;
}
if flags.contains(SymbolFlag::Local) {
result |= DiffSymbolFlag::SymbolLocal as u32;
}
if flags.contains(SymbolFlag::Weak) {
result |= DiffSymbolFlag::SymbolWeak as u32;
}
if flags.contains(SymbolFlag::Common) {
result |= DiffSymbolFlag::SymbolCommon as u32;
}
if flags.contains(SymbolFlag::Hidden) {
result |= DiffSymbolFlag::SymbolHidden as u32;
}
result
}
impl DiffInstructionRow {
pub fn new(
obj: &Object,
symbol_idx: usize,
row: &diff::InstructionDiffRow,
diff_config: &DiffObjConfig,
) -> Result<Self> {
let instruction = if let Some(ins_ref) = row.ins_ref {
let resolved = obj.resolve_instruction_ref(symbol_idx, ins_ref);
resolved.map(|r| DiffInstruction::new(obj, r, diff_config)).transpose()?
} else {
None
};
let arg_diff =
row.arg_diff.iter().map(|d| DiffInstructionArgDiff { diff_index: d.get() }).collect();
Ok(Self { diff_kind: DiffKind::from(row.kind) as i32, instruction, arg_diff })
}
}
impl DiffInstruction {
pub fn new(
obj: &Object,
resolved: obj::ResolvedInstructionRef,
diff_config: &DiffObjConfig,
) -> Result<Self> {
let mut formatted = String::new();
let mut parts = vec![];
let separator = diff_config.separator();
// Use the arch's display_instruction to get formatted parts
obj.arch.display_instruction(resolved, diff_config, &mut |part| {
write_instruction_part(&mut formatted, &part, separator, resolved.relocation);
parts
.push(DiffInstructionPart { part: Some(diff_instruction_part::Part::from(&part)) });
Ok(())
})?;
let relocation = resolved.relocation.map(|r| DiffRelocation::new(obj, r));
let line_number = resolved
.section
.line_info
.range(..=resolved.ins_ref.address)
.last()
.map(|(_, &line)| line);
Ok(Self {
address: resolved.ins_ref.address,
size: resolved.ins_ref.size as u32,
formatted,
parts,
relocation,
branch_dest: resolved.ins_ref.branch_dest,
line_number,
})
}
}
fn write_instruction_part(
out: &mut String,
part: &InstructionPart,
separator: &str,
reloc: Option<obj::ResolvedRelocation>,
) {
match part {
InstructionPart::Basic(s) => out.push_str(s),
InstructionPart::Opcode(s, _) => {
out.push_str(s);
out.push(' ');
}
InstructionPart::Arg(arg) => match arg {
obj::InstructionArg::Value(v) => {
let _ = write!(out, "{}", v);
}
obj::InstructionArg::Reloc => {
if let Some(resolved) = reloc {
out.push_str(&resolved.symbol.name);
if resolved.relocation.addend != 0 {
if resolved.relocation.addend < 0 {
let _ = write!(out, "-{:#x}", -resolved.relocation.addend);
} else {
let _ = write!(out, "+{:#x}", resolved.relocation.addend);
}
}
}
}
obj::InstructionArg::BranchDest(dest) => {
let _ = write!(out, "{:#x}", dest);
}
},
InstructionPart::Separator => out.push_str(separator),
}
}
impl diff_instruction_part::Part {
fn from(part: &InstructionPart) -> Self {
match part {
InstructionPart::Basic(s) => diff_instruction_part::Part::Basic(s.to_string()),
InstructionPart::Opcode(mnemonic, opcode) => {
diff_instruction_part::Part::Opcode(DiffOpcode {
mnemonic: mnemonic.to_string(),
opcode: *opcode as u32,
})
}
InstructionPart::Arg(arg) => {
diff_instruction_part::Part::Arg(DiffInstructionArg::from(arg))
}
InstructionPart::Separator => diff_instruction_part::Part::Separator(true),
}
}
}
impl From<&obj::InstructionArg<'_>> for DiffInstructionArg {
fn from(arg: &obj::InstructionArg) -> Self {
let arg = match arg {
obj::InstructionArg::Value(v) => match v {
obj::InstructionArgValue::Signed(v) => diff_instruction_arg::Arg::Signed(*v),
obj::InstructionArgValue::Unsigned(v) => diff_instruction_arg::Arg::Unsigned(*v),
obj::InstructionArgValue::Opaque(v) => {
diff_instruction_arg::Arg::Opaque(v.to_string())
}
},
obj::InstructionArg::Reloc => diff_instruction_arg::Arg::Reloc(true),
obj::InstructionArg::BranchDest(dest) => diff_instruction_arg::Arg::BranchDest(*dest),
};
DiffInstructionArg { arg: Some(arg) }
}
}
impl DiffRelocation {
pub fn new(obj: &Object, resolved: obj::ResolvedRelocation) -> Self {
let type_val = relocation_type(resolved.relocation.flags);
let type_name = obj
.arch
.reloc_name(resolved.relocation.flags)
.map(|s| s.to_string())
.unwrap_or_default();
Self {
r#type: type_val,
type_name,
target_symbol: resolved.relocation.target_symbol as u32,
addend: resolved.relocation.addend,
}
}
}
fn relocation_type(flags: obj::RelocationFlags) -> u32 {
match flags {
obj::RelocationFlags::Elf(r_type) => r_type,
obj::RelocationFlags::Coff(typ) => typ as u32,
}
}
impl From<diff::InstructionDiffKind> for DiffKind {
fn from(value: diff::InstructionDiffKind) -> Self {
match value {
diff::InstructionDiffKind::None => DiffKind::DiffNone,
diff::InstructionDiffKind::OpMismatch => DiffKind::DiffOpMismatch,
diff::InstructionDiffKind::ArgMismatch => DiffKind::DiffArgMismatch,
diff::InstructionDiffKind::Replace => DiffKind::DiffReplace,
diff::InstructionDiffKind::Delete => DiffKind::DiffDelete,
diff::InstructionDiffKind::Insert => DiffKind::DiffInsert,
}
}
}
impl From<diff::DataDiffKind> for DiffKind {
fn from(value: diff::DataDiffKind) -> Self {
match value {
diff::DataDiffKind::None => DiffKind::DiffNone,
diff::DataDiffKind::Replace => DiffKind::DiffReplace,
diff::DataDiffKind::Delete => DiffKind::DiffDelete,
diff::DataDiffKind::Insert => DiffKind::DiffInsert,
}
}
}
impl From<&diff::DataDiff> for DiffDataSegment {
fn from(value: &diff::DataDiff) -> Self {
Self {
kind: DiffKind::from(value.kind) as i32,
data: value.data.clone(),
size: value.size as u64,
}
}
}
impl DiffDataRelocation {
pub fn new(obj: &Object, value: &diff::DataRelocationDiff) -> Self {
let type_val = relocation_type(value.reloc.flags);
let type_name =
obj.arch.reloc_name(value.reloc.flags).map(|s| s.to_string()).unwrap_or_default();
Self {
relocation: Some(DiffRelocation {
r#type: type_val,
type_name,
target_symbol: value.reloc.target_symbol as u32,
addend: value.reloc.addend,
}),
kind: DiffKind::from(value.kind) as i32,
start: value.range.start,
end: value.range.end,
}
}
}
+2
View File
@@ -1 +1,3 @@
#[cfg(feature = "any-arch")]
pub mod diff;
pub mod report;
+6 -24
View File
@@ -35,31 +35,13 @@ pub fn diff_bss_symbol(
))
}
pub fn symbol_name_matches(left_name: &str, right_name: &str) -> bool {
if let Some((left_prefix, left_suffix)) = left_name.split_once("@class$")
&& let Some((right_prefix, right_suffix)) = right_name.split_once("@class$")
pub fn symbol_name_matches(left: &Symbol, right: &Symbol) -> bool {
if let Some(left_name) = &left.normalized_name
&& let Some(right_name) = &right.normalized_name
{
// Match Metrowerks anonymous class symbol names, ignoring the unique ID.
// e.g. __dt__Q29dCamera_c23@class$3665d_camera_cppFv
if left_prefix == right_prefix
&& let Some(left_idx) = left_suffix.chars().position(|c| !c.is_numeric())
&& let Some(right_idx) = right_suffix.chars().position(|c| !c.is_numeric())
{
// e.g. d_camera_cppFv (after the unique ID)
left_suffix[left_idx..] == right_suffix[right_idx..]
} else {
false
}
} else if let Some((prefix, suffix)) = left_name.split_once(['$', '.'])
&& suffix.chars().all(char::is_numeric)
{
// Match Metrowerks symbol$1234 against symbol$2345
// and GCC symbol.1234 against symbol.2345
right_name
.split_once(['$', '.'])
.is_some_and(|(p, s)| p == prefix && s.chars().all(char::is_numeric))
} else {
left_name == right_name
} else {
left.name == right.name
}
}
@@ -73,7 +55,7 @@ fn reloc_eq(
return false;
}
let symbol_name_addend_matches = symbol_name_matches(&left.symbol.name, &right.symbol.name)
let symbol_name_addend_matches = symbol_name_matches(left.symbol, right.symbol)
&& left.relocation.addend == right.relocation.addend;
match (left.symbol.section, right.symbol.section) {
(Some(sl), Some(sr)) => {
+10 -15
View File
@@ -298,9 +298,8 @@ pub fn display_row(
}
})?;
// Fallback for relocation that wasn't displayed
if resolved.relocation.is_some() && !displayed_relocation {
if !displayed_relocation && let Some(resolved) = resolved.relocation {
cb(DiffTextSegment::basic(" <", base_color))?;
let resolved = resolved.relocation.unwrap();
let diff_index = ins_row.arg_diff.get(arg_idx).copied().unwrap_or_default();
let color =
diff_index.get().map_or(DiffTextColor::Bright, |i| DiffTextColor::Rotating(i as u8));
@@ -813,19 +812,15 @@ pub fn display_sections(
}
let section_diff = &diff.sections[section_idx];
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))
});
if 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(|| b.address.cmp(&a.address))
.then_with(|| a.size.cmp(&b.size))
});
}
sections.push(SectionDisplay {
id: section.id.clone(),
name: if section.flags.contains(SectionFlag::Combined) {
+7 -24
View File
@@ -7,7 +7,6 @@ use alloc::{
use core::{num::NonZeroU32, ops::Range};
use anyhow::Result;
use itertools::Itertools;
use crate::{
diff::{
@@ -687,18 +686,6 @@ fn symbol_section_kind(obj: &Object, symbol: &Symbol) -> SectionKind {
}
}
/// Check if a symbol is a compiler-generated like @1234 or _$E1234.
fn is_symbol_compiler_generated(symbol: &Symbol) -> bool {
if symbol.name.starts_with('@') && symbol.name[1..].chars().all(char::is_numeric) {
// Exclude @stringBase0, @GUARD@, etc.
return true;
}
if symbol.name.starts_with("_$E") && symbol.name[3..].chars().all(char::is_numeric) {
return true;
}
false
}
fn find_symbol(
obj: Option<&Object>,
in_obj: &Object,
@@ -712,7 +699,7 @@ fn find_symbol(
// Match compiler-generated symbols against each other (e.g. @251 -> @60)
// If they are in the same section and have the same value
if is_symbol_compiler_generated(in_symbol)
if in_symbol.flags.contains(SymbolFlag::CompilerGenerated)
&& matches!(section_kind, SectionKind::Code | SectionKind::Data | SectionKind::Bss)
{
let mut closest_match_symbol_idx = None;
@@ -724,7 +711,7 @@ fn find_symbol(
if obj.sections[section_index].name != section_name {
continue;
}
if !is_symbol_compiler_generated(symbol) {
if !symbol.flags.contains(SymbolFlag::CompilerGenerated) {
continue;
}
match section_kind {
@@ -761,15 +748,11 @@ fn find_symbol(
}
// Try to find a symbol with a matching name
if let Some((symbol_idx, _)) = unmatched_symbols(obj, used)
.filter(|&(_, symbol)| {
symbol_name_matches(&in_symbol.name, &symbol.name)
&& symbol_section_kind(obj, symbol) == section_kind
&& symbol_section(obj, symbol).is_some_and(|(name, _)| name == section_name)
})
.sorted_unstable_by_key(|&(_, symbol)| (symbol.section, symbol.address))
.next()
{
if let Some((symbol_idx, _)) = unmatched_symbols(obj, used).find(|&(_, symbol)| {
symbol_name_matches(in_symbol, symbol)
&& symbol_section_kind(obj, symbol) == section_kind
&& symbol_section(obj, symbol).is_some_and(|(name, _)| name == section_name)
}) {
return Some(symbol_idx);
}
+5 -1
View File
@@ -37,7 +37,7 @@ pub enum SectionKind {
flags! {
#[derive(Hash)]
pub enum SymbolFlag: u8 {
pub enum SymbolFlag: u16 {
Global,
Local,
Weak,
@@ -50,6 +50,8 @@ flags! {
SizeInferred,
/// Symbol should be ignored by any diffing
Ignored,
/// Symbol name is compiler-generated; compare by value instead of name
CompilerGenerated,
}
}
@@ -264,6 +266,7 @@ pub trait FlowAnalysisResult: core::fmt::Debug + Send {
pub struct Symbol {
pub name: String,
pub demangled_name: Option<String>,
pub normalized_name: Option<String>,
pub address: u64,
pub size: u64,
pub kind: SymbolKind,
@@ -403,6 +406,7 @@ pub struct ResolvedInstructionRef<'obj> {
static DUMMY_SYMBOL: Symbol = Symbol {
name: String::new(),
demangled_name: None,
normalized_name: None,
address: 0,
size: 0,
kind: SymbolKind::Unknown,
+119 -46
View File
@@ -3,6 +3,7 @@ use alloc::{
collections::BTreeMap,
format,
string::{String, ToString},
vec,
vec::Vec,
};
use core::{cmp::Ordering, num::NonZeroU64};
@@ -35,6 +36,67 @@ fn map_section_kind(section: &object::Section) -> SectionKind {
}
}
/// Check if a symbol's name is partially compiler-generated, and if so normalize it for pairing.
/// e.g. symbol$1234 and symbol$2345 will both be replaced with symbol$0000 internally.
fn get_normalized_symbol_name(name: &str) -> Option<String> {
const DUMMY_UNIQUE_ID: &str = "0000";
const DUMMY_UNIQUE_MSVC_ID: &str = "00000000";
if let Some((prefix, suffix)) = name.split_once("@class$")
&& let Some(idx) = suffix.chars().position(|c| !c.is_numeric())
&& idx > 0
{
// Match Metrowerks anonymous class symbol names, ignoring the unique ID.
// e.g. __dt__Q29dCamera_c23@class$3665d_camera_cppFv
// and: __dt__Q29dCamera_c23@class$1727d_camera_cppFv
let suffix = &suffix[idx..];
Some(format!("{prefix}@class${DUMMY_UNIQUE_ID}{suffix}"))
} else if let Some((prefix, suffix)) = name.split_once('$')
&& suffix.chars().all(char::is_numeric)
{
// Match Metrowerks symbol$1234 against symbol$2345
Some(format!("{prefix}${DUMMY_UNIQUE_ID}"))
} else if let Some((prefix, suffix)) = name.split_once('.')
&& suffix.chars().all(char::is_numeric)
{
// Match GCC symbol.1234 against symbol.2345
Some(format!("{prefix}.{DUMMY_UNIQUE_ID}"))
} else if name.starts_with('?') {
// Match MSVC anonymous class symbol names, ignoring the unique ID.
// e.g. ?CheckContextOr@?A0x24773155@@YA_NPBVDataArray@@@Z
// and: ?CheckContextOr@?A0xddf6240c@@YA_NPBVDataArray@@@Z
let mut name_str = String::from(name);
let anon_indices: Vec<usize> = name_str.match_indices("?A0x").map(|(idx, _)| idx).collect();
if !anon_indices.is_empty() {
for idx in anon_indices {
// the str sequence we're looking for is: ?A0xXXXXXXXX@@
if u32::from_str_radix(&name_str[idx + 4..idx + 12], 16).is_ok()
&& &name_str[idx + 12..idx + 14] == "@@"
{
// if the two above checks passed, we're good to replace the hash
name_str.replace_range(idx + 4..idx + 12, DUMMY_UNIQUE_MSVC_ID);
}
}
Some(name_str)
} else {
None
}
} else {
None
}
}
/// Check if a symbol's name is entirely compiler-generated, such as @1234 or _$E1234.
/// This enables pairing these symbols up by their value instead of their name.
fn is_symbol_name_compiler_generated(name: &str) -> bool {
if name.starts_with('@') && name[1..].chars().all(char::is_numeric) {
// Exclude @stringBase0, @GUARD@, etc.
return true;
} else if name.starts_with("_$E") && name[3..].chars().all(char::is_numeric) {
return true;
}
false
}
fn map_symbol(
arch: &dyn Arch,
file: &object::File,
@@ -97,10 +159,15 @@ fn map_symbol(
.and_then(|m| m.virtual_addresses.as_ref())
.and_then(|v| v.get(symbol.index().0).cloned());
let section = symbol.section_index().and_then(|i| section_indices.get(i.0).copied());
let normalized_name = get_normalized_symbol_name(&name);
if is_symbol_name_compiler_generated(&name) {
flags |= SymbolFlag::CompilerGenerated;
}
Ok(Symbol {
name,
demangled_name,
normalized_name,
address,
size,
kind,
@@ -119,13 +186,38 @@ fn map_symbols(
split_meta: Option<&SplitMeta>,
config: &DiffObjConfig,
) -> Result<(Vec<Symbol>, Vec<usize>)> {
let symbol_count = obj_file.symbols().count();
let mut symbols = Vec::<Symbol>::with_capacity(symbol_count + obj_file.sections().count());
let mut symbol_indices = Vec::<usize>::with_capacity(symbol_count + 1);
for obj_symbol in obj_file.symbols() {
if symbol_indices.len() <= obj_symbol.index().0 {
symbol_indices.resize(obj_symbol.index().0 + 1, usize::MAX);
}
// symbols() is not guaranteed to be sorted by address.
// We sort it here to fix pairing bugs with diff algorithms that assume the symbols are ordered.
// Sorting everything here once is less expensive than sorting subsets later in expensive loops.
let mut max_index = 0;
let mut obj_symbols = obj_file
.symbols()
.filter(|s| s.kind() != object::SymbolKind::File)
.inspect(|sym| max_index = max_index.max(sym.index().0))
.collect::<Vec<_>>();
obj_symbols.sort_by(|a, b| {
// Sort symbols by section index, placing absolute symbols last
a.section_index()
.map_or(usize::MAX, |s| s.0)
.cmp(&b.section_index().map_or(usize::MAX, |s| s.0))
.then_with(|| {
// Sort section symbols first in a section
if a.kind() == object::SymbolKind::Section {
Ordering::Less
} else if b.kind() == object::SymbolKind::Section {
Ordering::Greater
} else {
Ordering::Equal
}
})
// Sort by address within section
.then_with(|| a.address().cmp(&b.address()))
// If there are multiple symbols with the same address, smaller symbol first
.then_with(|| a.size().cmp(&b.size()))
});
let mut symbols = Vec::<Symbol>::with_capacity(obj_symbols.len() + obj_file.sections().count());
let mut symbol_indices = vec![usize::MAX; max_index + 1];
for obj_symbol in obj_symbols {
let symbol = map_symbol(arch, obj_file, &obj_symbol, section_indices, split_meta, config)?;
symbol_indices[obj_symbol.index().0] = symbols.len();
symbols.push(symbol);
@@ -172,6 +264,7 @@ fn add_section_symbols(sections: &[Section], symbols: &mut Vec<Symbol>) {
symbols.push(Symbol {
name,
demangled_name: None,
normalized_name: None,
address: 0,
size,
kind: SymbolKind::Section,
@@ -193,40 +286,18 @@ fn is_local_label(symbol: &Symbol) -> bool {
}
fn infer_symbol_sizes(arch: &dyn Arch, symbols: &mut [Symbol], sections: &[Section]) -> Result<()> {
// Create a sorted list of symbol indices by section
let mut symbols_with_section = Vec::<usize>::with_capacity(symbols.len());
for (i, symbol) in symbols.iter().enumerate() {
if symbol.section.is_some() {
symbols_with_section.push(i);
}
}
symbols_with_section.sort_by(|a, b| {
let a = &symbols[*a];
let b = &symbols[*b];
a.section
.unwrap_or(usize::MAX)
.cmp(&b.section.unwrap_or(usize::MAX))
.then_with(|| {
// Sort section symbols first
if a.kind == SymbolKind::Section {
Ordering::Less
} else if b.kind == SymbolKind::Section {
Ordering::Greater
} else {
Ordering::Equal
}
})
.then_with(|| a.address.cmp(&b.address))
.then_with(|| a.size.cmp(&b.size))
});
// Above, we've sorted the symbols by section and then by address.
// Set symbol sizes based on the next symbol's address
let mut iter_idx = 0;
let mut last_end = (0, 0);
while iter_idx < symbols_with_section.len() {
let symbol_idx = symbols_with_section[iter_idx];
while iter_idx < symbols.len() {
let symbol_idx = iter_idx;
let symbol = &symbols[symbol_idx];
let section_idx = symbol.section.unwrap();
let Some(section_idx) = symbol.section else {
// Start of absolute symbols
break;
};
iter_idx += 1;
if symbol.size != 0 {
if symbol.kind != SymbolKind::Section {
@@ -239,10 +310,9 @@ fn infer_symbol_sizes(arch: &dyn Arch, symbols: &mut [Symbol], sections: &[Secti
continue;
}
let next_symbol = loop {
if iter_idx >= symbols_with_section.len() {
let Some(next_symbol) = symbols.get(iter_idx) else {
break None;
}
let next_symbol = &symbols[symbols_with_section[iter_idx]];
};
if next_symbol.section != Some(section_idx) {
break None;
}
@@ -298,9 +368,11 @@ fn map_sections(
split_meta: Option<&SplitMeta>,
) -> Result<(Vec<Section>, Vec<usize>)> {
let mut section_names = BTreeMap::<String, usize>::new();
let section_count = obj_file.sections().count();
let mut max_index = 0;
let section_count =
obj_file.sections().inspect(|s| max_index = max_index.max(s.index().0)).count();
let mut result = Vec::<Section>::with_capacity(section_count);
let mut section_indices = Vec::<usize>::with_capacity(section_count + 1);
let mut section_indices = vec![usize::MAX; max_index + 1];
for section in obj_file.sections() {
let name = section.name().context("Failed to process section name")?;
let kind = map_section_kind(&section);
@@ -325,9 +397,6 @@ fn map_sections(
let id = format!("{name}-{unique_id}");
*unique_id += 1;
if section_indices.len() <= section.index().0 {
section_indices.resize(section.index().0 + 1, usize::MAX);
}
section_indices[section.index().0] = result.len();
result.push(Section {
id,
@@ -826,8 +895,12 @@ fn combine_sections(
let mut data_sections = BTreeMap::<String, Vec<usize>>::new();
let mut text_sections = BTreeMap::<String, Vec<usize>>::new();
for (i, section) in sections.iter().enumerate() {
let base_name =
if let Some(i) = section.name.rfind('$') { &section.name[..i] } else { &section.name };
let base_name = section
.name
.get(1..)
.and_then(|s| s.rfind(['$', '.']))
.and_then(|i| section.name.get(..i + 1))
.unwrap_or(&section.name);
match section.kind {
SectionKind::Data | SectionKind::Bss => {
data_sections.entry(base_name.to_string()).or_default().push(i);
@@ -114,6 +114,7 @@ expression: "(sections, symbols)"
Symbol {
name: ".data",
demangled_name: None,
normalized_name: None,
address: 0,
size: 0,
kind: Section,
@@ -127,6 +128,7 @@ expression: "(sections, symbols)"
Symbol {
name: "symbol",
demangled_name: None,
normalized_name: None,
address: 4,
size: 4,
kind: Object,
@@ -140,6 +142,7 @@ expression: "(sections, symbols)"
Symbol {
name: "function",
demangled_name: None,
normalized_name: None,
address: 0,
size: 8,
kind: Function,
@@ -153,6 +156,7 @@ expression: "(sections, symbols)"
Symbol {
name: ".data",
demangled_name: None,
normalized_name: None,
address: 0,
size: 0,
kind: Unknown,
@@ -4,4 +4,4 @@ expression: output
---
[(Line(90), Dim, 5), (Address(0), Dim, 5), (Spacing(4), Normal, 0), (Opcode("ldr", 32792), Normal, 10), (Argument(Opaque("r12")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("pc")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(0)), Normal, 0), (Basic("]"), Normal, 0), (Basic(" (->"), Normal, 0), (BranchDest(8), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Line(90), Dim, 5), (Address(4), Dim, 5), (Spacing(4), Normal, 0), (Opcode("bx", 32777), Normal, 10), (Argument(Opaque("r12")), Normal, 0), (Eol, Normal, 0)]
[(Line(90), Dim, 5), (Address(8), Dim, 5), (Spacing(4), Normal, 0), (Opcode(".word", 65534), Normal, 10), (Symbol(Symbol { name: "esEnemyDraw", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
[(Line(90), Dim, 5), (Address(8), Dim, 5), (Spacing(4), Normal, 0), (Opcode(".word", 65534), Normal, 10), (Symbol(Symbol { name: "esEnemyDraw", demangled_name: None, normalized_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
@@ -5,7 +5,7 @@ expression: output
[(Address(0), Dim, 5), (Spacing(4), Normal, 0), (Opcode("stmdb", 32883), Normal, 10), (Argument(Opaque("sp")), Normal, 0), (Basic("!"), 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)]
[(Address(4), Dim, 5), (Spacing(4), Normal, 0), (Opcode("mov", 32811), Normal, 10), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Eol, Normal, 0)]
[(Address(8), Dim, 5), (Spacing(4), Normal, 0), (Opcode("mov", 32811), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Eol, Normal, 0)]
[(Address(12), Dim, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32775), Normal, 10), (Symbol(Symbol { name: "_ZN13LinkStateBase12OnStateLeaveEi", demangled_name: Some("LinkStateBase::OnStateLeave(int)"), 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(12), Dim, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32775), Normal, 10), (Symbol(Symbol { name: "_ZN13LinkStateBase12OnStateLeaveEi", demangled_name: Some("LinkStateBase::OnStateLeave(int)"), normalized_name: None, 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(16), Dim, 5), (Spacing(4), Normal, 0), (Opcode("ldr", 32792), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(20)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
[(Address(20), Dim, 5), (Spacing(4), Normal, 0), (Opcode("cmp", 32784), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(10)), Normal, 0), (Eol, Normal, 0)]
[(Address(24), Dim, 5), (Spacing(4), Normal, 0), (Opcode("addls", 32769), Normal, 10), (Argument(Opaque("pc")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("pc")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("lsl")), Normal, 0), (Spacing(1), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(2)), Normal, 0), (Eol, Normal, 0)]
@@ -23,41 +23,41 @@ expression: output
[(Address(72), Dim, 5), (Spacing(4), Normal, 0), (Opcode("b", 32772), Normal, 10), (BranchDest(164), Normal, 0), (BranchArrow(41), Rotating(6), 0), (Eol, Normal, 0)]
[(Address(76), Dim, 5), (BranchArrow(15), Rotating(4), 0), (Opcode("ldr", 32792), 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(336)), Normal, 0), (Basic("]"), Normal, 0), (Basic(" (->"), Normal, 0), (BranchDest(420), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(80), Dim, 5), (Spacing(4), Normal, 0), (Opcode("ldr", 32792), 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)]
[(Address(84), Dim, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32775), Normal, 10), (Symbol(Symbol { name: "_ZN18UnkStruct_027e103c19func_ov000_020cf01cEv", demangled_name: Some("UnkStruct_027e103c::func_ov000_020cf01c()"), 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(84), Dim, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32775), Normal, 10), (Symbol(Symbol { name: "_ZN18UnkStruct_027e103c19func_ov000_020cf01cEv", demangled_name: Some("UnkStruct_027e103c::func_ov000_020cf01c()"), normalized_name: None, 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(88), Dim, 5), (Spacing(4), Normal, 0), (Opcode("ldrb", 32793), 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), Dim, 5), (Spacing(4), Normal, 0), (Opcode("cmp", 32784), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(0)), Normal, 0), (Eol, Normal, 0)]
[(Address(96), Dim, 5), (Spacing(4), Normal, 0), (Opcode("bne", 32772), Normal, 10), (BranchDest(108), Normal, 0), (BranchArrow(27), Rotating(7), 0), (Eol, Normal, 0)]
[(Address(100), Dim, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32775), Normal, 10), (BranchDest(424), Normal, 0), (BranchArrow(106), Rotating(8), 0), (Eol, Normal, 0)]
[(Address(104), Dim, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32775), 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(104), Dim, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32775), Normal, 10), (Symbol(Symbol { name: "_ZN12EquipBombchu19func_ov014_0213ec64Ev", demangled_name: Some("EquipBombchu::func_ov014_0213ec64()"), normalized_name: None, 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), Dim, 5), (BranchArrow(24), Rotating(7), 0), (Opcode("ldr", 32792), 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), Dim, 5), (Spacing(4), Normal, 0), (Opcode("ldr", 32792), 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)]
[(Address(116), Dim, 5), (Spacing(4), Normal, 0), (Opcode("blx", 32776), Normal, 10), (Symbol(Symbol { name: "_Z19func_ov014_0211fd04Pi", demangled_name: Some("func_ov014_0211fd04(int*)"), 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(116), Dim, 5), (Spacing(4), Normal, 0), (Opcode("blx", 32776), Normal, 10), (Symbol(Symbol { name: "_Z19func_ov014_0211fd04Pi", demangled_name: Some("func_ov014_0211fd04(int*)"), normalized_name: None, 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(120), Dim, 5), (Spacing(4), Normal, 0), (Opcode("b", 32772), Normal, 10), (BranchDest(200), Normal, 0), (BranchArrow(50), Rotating(0), 0), (Eol, Normal, 0)]
[(Address(124), Dim, 5), (BranchArrow(12), Rotating(2), 0), (Opcode("mov", 32811), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Eol, Normal, 0)]
[(Address(128), Dim, 5), (Spacing(4), Normal, 0), (Opcode("mov", 32811), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Eol, Normal, 0)]
[(Address(132), Dim, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32775), Normal, 10), (Symbol(Symbol { name: "_ZN13LinkStateItem13StopUsingBombEi", demangled_name: Some("LinkStateItem::StopUsingBomb(int)"), 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(132), Dim, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32775), Normal, 10), (Symbol(Symbol { name: "_ZN13LinkStateItem13StopUsingBombEi", demangled_name: Some("LinkStateItem::StopUsingBomb(int)"), normalized_name: None, 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(136), Dim, 5), (Spacing(4), Normal, 0), (Opcode("b", 32772), Normal, 10), (BranchDest(200), Normal, 0), (BranchArrow(50), Rotating(0), 0), (Eol, Normal, 0)]
[(Address(140), Dim, 5), (BranchArrow(14), Rotating(3), 0), (Opcode("mov", 32811), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Eol, Normal, 0)]
[(Address(144), Dim, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32775), Normal, 10), (Symbol(Symbol { name: "_ZN13LinkStateItem13StopUsingRopeEv", demangled_name: Some("LinkStateItem::StopUsingRope()"), 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(144), Dim, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32775), Normal, 10), (Symbol(Symbol { name: "_ZN13LinkStateItem13StopUsingRopeEv", demangled_name: Some("LinkStateItem::StopUsingRope()"), normalized_name: None, 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(148), Dim, 5), (Spacing(4), Normal, 0), (Opcode("b", 32772), Normal, 10), (BranchDest(200), Normal, 0), (BranchArrow(50), Rotating(0), 0), (Eol, Normal, 0)]
[(Address(152), Dim, 5), (BranchArrow(16), Rotating(5), 0), (Opcode("mov", 32811), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Eol, Normal, 0)]
[(Address(156), Dim, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32775), Normal, 10), (Symbol(Symbol { name: "_ZN13LinkStateItem15StopUsingHammerEv", demangled_name: Some("LinkStateItem::StopUsingHammer()"), 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(156), Dim, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32775), Normal, 10), (Symbol(Symbol { name: "_ZN13LinkStateItem15StopUsingHammerEv", demangled_name: Some("LinkStateItem::StopUsingHammer()"), normalized_name: None, 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(160), Dim, 5), (Spacing(4), Normal, 0), (Opcode("b", 32772), Normal, 10), (BranchDest(200), Normal, 0), (BranchArrow(50), Rotating(0), 0), (Eol, Normal, 0)]
[(Address(164), Dim, 5), (BranchArrow(17), Rotating(6), 0), (Opcode("ldr", 32792), 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(248)), Normal, 0), (Basic("]"), Normal, 0), (Basic(" (->"), Normal, 0), (BranchDest(420), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(168), Dim, 5), (Spacing(4), Normal, 0), (Opcode("mov", 32811), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(0)), Normal, 0), (Eol, Normal, 0)]
[(Address(172), Dim, 5), (Spacing(4), Normal, 0), (Opcode("ldr", 32792), 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)]
[(Address(176), Dim, 5), (Spacing(4), Normal, 0), (Opcode("mov", 32811), Normal, 10), (Argument(Opaque("r2")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Eol, Normal, 0)]
[(Address(180), Dim, 5), (Spacing(4), Normal, 0), (Opcode("strb", 32885), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(42)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
[(Address(184), Dim, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32775), Normal, 10), (Symbol(Symbol { name: "_ZN18UnkStruct_027e103c19func_ov000_020cf9dcEii", demangled_name: Some("UnkStruct_027e103c::func_ov000_020cf9dc(int, int)"), 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(184), Dim, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32775), Normal, 10), (Symbol(Symbol { name: "_ZN18UnkStruct_027e103c19func_ov000_020cf9dcEii", demangled_name: Some("UnkStruct_027e103c::func_ov000_020cf9dc(int, int)"), normalized_name: None, 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(188), Dim, 5), (Spacing(4), Normal, 0), (Opcode("b", 32772), Normal, 10), (BranchDest(200), Normal, 0), (BranchArrow(50), Rotating(0), 0), (Eol, Normal, 0)]
[(Address(192), Dim, 5), (BranchArrow(11), Rotating(1), 0), (Opcode("mov", 32811), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Eol, Normal, 0)]
[(Address(196), Dim, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32775), Normal, 10), (Symbol(Symbol { name: "_ZN13LinkStateItem14StopUsingScoopEv", demangled_name: Some("LinkStateItem::StopUsingScoop()"), 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(196), Dim, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32775), Normal, 10), (Symbol(Symbol { name: "_ZN13LinkStateItem14StopUsingScoopEv", demangled_name: Some("LinkStateItem::StopUsingScoop()"), normalized_name: None, 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(200), Dim, 5), (BranchArrow(7), Rotating(0), 0), (Opcode("ldr", 32792), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(20)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
[(Address(204), Dim, 5), (Spacing(4), Normal, 0), (Opcode("mvn", 32819), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(0)), Normal, 0), (Eol, Normal, 0)]
[(Address(208), Dim, 5), (Spacing(4), Normal, 0), (Opcode("cmp", 32784), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Eol, Normal, 0)]
[(Address(212), Dim, 5), (Spacing(4), Normal, 0), (Opcode("beq", 32772), Normal, 10), (BranchDest(236), Normal, 0), (BranchArrow(59), Rotating(9), 0), (Eol, Normal, 0)]
[(Address(216), Dim, 5), (Spacing(4), Normal, 0), (Opcode("mov", 32811), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Eol, Normal, 0)]
[(Address(220), Dim, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32775), Normal, 10), (Symbol(Symbol { name: "_ZN13LinkStateBase12GetEquipItemEi", demangled_name: Some("LinkStateBase::GetEquipItem(int)"), 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(220), Dim, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32775), Normal, 10), (Symbol(Symbol { name: "_ZN13LinkStateBase12GetEquipItemEi", demangled_name: Some("LinkStateBase::GetEquipItem(int)"), normalized_name: None, 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(224), Dim, 5), (Spacing(4), Normal, 0), (Opcode("ldr", 32792), Normal, 10), (Argument(Opaque("r1")), 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)]
[(Address(228), Dim, 5), (Spacing(4), Normal, 0), (Opcode("ldr", 32792), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(28)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
[(Address(232), Dim, 5), (Spacing(4), Normal, 0), (Opcode("blx", 32776), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Eol, Normal, 0)]
@@ -77,14 +77,14 @@ expression: output
[(Address(288), Dim, 5), (BranchArrow(61), Rotating(10), 0), (Opcode("cmp", 32784), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(10)), Normal, 0), (Eol, Normal, 0)]
[(Address(292), Dim, 5), (Spacing(4), Normal, 0), (Opcode("bne", 32772), Normal, 10), (BranchDest(308), Normal, 0), (BranchArrow(77), Rotating(12), 0), (Eol, Normal, 0)]
[(Address(296), Dim, 5), (BranchArrow(62), Rotating(11), 0), (Opcode("mov", 32811), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Eol, Normal, 0)]
[(Address(300), Dim, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32775), Normal, 10), (Symbol(Symbol { name: "_ZN13LinkStateBase18EquipItem_vfunc_28Ev", demangled_name: Some("LinkStateBase::EquipItem_vfunc_28()"), 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(300), Dim, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32775), Normal, 10), (Symbol(Symbol { name: "_ZN13LinkStateBase18EquipItem_vfunc_28Ev", demangled_name: Some("LinkStateBase::EquipItem_vfunc_28()"), normalized_name: None, 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(304), Dim, 5), (Spacing(4), Normal, 0), (Opcode("b", 32772), Normal, 10), (BranchDest(340), Normal, 0), (BranchArrow(85), Rotating(13), 0), (Eol, Normal, 0)]
[(Address(308), Dim, 5), (BranchArrow(64), Rotating(12), 0), (Opcode("mov", 32811), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Eol, Normal, 0)]
[(Address(312), Dim, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32775), Normal, 10), (Symbol(Symbol { name: "_ZN13LinkStateBase18EquipItem_vfunc_28Ev", demangled_name: Some("LinkStateBase::EquipItem_vfunc_28()"), 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(312), Dim, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32775), Normal, 10), (Symbol(Symbol { name: "_ZN13LinkStateBase18EquipItem_vfunc_28Ev", demangled_name: Some("LinkStateBase::EquipItem_vfunc_28()"), normalized_name: None, 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(316), Dim, 5), (Spacing(4), Normal, 0), (Opcode("cmp", 32784), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(4)), Normal, 0), (Eol, Normal, 0)]
[(Address(320), Dim, 5), (Spacing(4), Normal, 0), (Opcode("cmpne", 32784), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(2)), Normal, 0), (Eol, Normal, 0)]
[(Address(324), Dim, 5), (Spacing(4), Normal, 0), (Opcode("beq", 32772), Normal, 10), (BranchDest(340), Normal, 0), (BranchArrow(85), Rotating(13), 0), (Eol, Normal, 0)]
[(Address(328), Dim, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32775), Normal, 10), (Symbol(Symbol { name: "_ZN13LinkStateItem16GetLinkStateMoveEv", demangled_name: Some("LinkStateItem::GetLinkStateMove()"), address: 488, size: 16, kind: Function, section: Some(0), flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Addend(-8), Bright, 0), (Eol, Normal, 0)]
[(Address(328), Dim, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32775), Normal, 10), (Symbol(Symbol { name: "_ZN13LinkStateItem16GetLinkStateMoveEv", demangled_name: Some("LinkStateItem::GetLinkStateMove()"), normalized_name: None, address: 488, size: 16, kind: Function, section: Some(0), flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Addend(-8), Bright, 0), (Eol, Normal, 0)]
[(Address(332), Dim, 5), (Spacing(4), Normal, 0), (Opcode("mov", 32811), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(1)), Normal, 0), (Eol, Normal, 0)]
[(Address(336), Dim, 5), (Spacing(4), Normal, 0), (Opcode("strb", 32885), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(20)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
[(Address(340), Dim, 5), (BranchArrow(70), Rotating(13), 0), (Opcode("mvn", 32819), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(0)), Normal, 0), (Eol, Normal, 0)]
@@ -94,7 +94,7 @@ expression: output
[(Address(356), Dim, 5), (Spacing(4), Normal, 0), (Opcode("cmp", 32784), Normal, 10), (Argument(Opaque("r6")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Eol, Normal, 0)]
[(Address(360), Dim, 5), (Spacing(4), Normal, 0), (Opcode("beq", 32772), Normal, 10), (BranchDest(384), Normal, 0), (BranchArrow(96), Rotating(14), 0), (Eol, Normal, 0)]
[(Address(364), Dim, 5), (BranchArrow(95), Rotating(15), 0), (Opcode("mov", 32811), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r6")), Normal, 0), (Eol, Normal, 0)]
[(Address(368), Dim, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32775), Normal, 10), (Symbol(Symbol { name: "_Z19func_ov000_020b7e6cPi", demangled_name: Some("func_ov000_020b7e6c(int*)"), 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(368), Dim, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32775), Normal, 10), (Symbol(Symbol { name: "_Z19func_ov000_020b7e6cPi", demangled_name: Some("func_ov000_020b7e6c(int*)"), normalized_name: None, 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(372), Dim, 5), (Spacing(4), Normal, 0), (Opcode("add", 32769), Normal, 10), (Argument(Opaque("r6")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r6")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(4)), Normal, 0), (Eol, Normal, 0)]
[(Address(376), Dim, 5), (Spacing(4), Normal, 0), (Opcode("cmp", 32784), Normal, 10), (Argument(Opaque("r6")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Eol, Normal, 0)]
[(Address(380), Dim, 5), (Spacing(4), Normal, 0), (Opcode("bne", 32772), Normal, 10), (BranchDest(364), Normal, 0), (BranchArrow(91), Rotating(15), 0), (Eol, Normal, 0)]
@@ -103,10 +103,10 @@ expression: output
[(Address(392), Dim, 5), (Spacing(4), Normal, 0), (Opcode("ldrb", 32793), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(128)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
[(Address(396), Dim, 5), (Spacing(4), Normal, 0), (Opcode("cmp", 32784), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(0)), Normal, 0), (Eol, Normal, 0)]
[(Address(400), Dim, 5), (Spacing(4), Normal, 0), (Opcode("beq", 32772), Normal, 10), (BranchDest(408), Normal, 0), (BranchArrow(102), Rotating(16), 0), (Eol, Normal, 0)]
[(Address(404), Dim, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32775), Normal, 10), (Symbol(Symbol { name: "_ZN13PlayerControl13StopFollowingEv", demangled_name: Some("PlayerControl::StopFollowing()"), 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(404), Dim, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32775), Normal, 10), (Symbol(Symbol { name: "_ZN13PlayerControl13StopFollowingEv", demangled_name: Some("PlayerControl::StopFollowing()"), normalized_name: None, 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(408), Dim, 5), (BranchArrow(100), Rotating(16), 0), (Opcode("mov", 32811), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(0)), Normal, 0), (Eol, Normal, 0)]
[(Address(412), Dim, 5), (Spacing(4), Normal, 0), (Opcode("strb", 32885), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(38)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
[(Address(416), Dim, 5), (Spacing(4), Normal, 0), (Opcode("ldmia", 32791), Normal, 10), (Argument(Opaque("sp")), Normal, 0), (Basic("!"), 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("pc")), Normal, 0), (Basic("}"), Normal, 0), (Eol, Normal, 0)]
[(Address(420), Dim, 5), (Spacing(4), Normal, 0), (Opcode(".word", 65534), Normal, 10), (Symbol(Symbol { name: "data_027e103c", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global | Weak), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
[(Address(424), Dim, 5), (BranchArrow(25), Rotating(8), 0), (Opcode(".word", 65534), Normal, 10), (Symbol(Symbol { name: "data_027e1098", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global | Weak), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
[(Address(428), Dim, 5), (Spacing(4), Normal, 0), (Opcode(".word", 65534), Normal, 10), (Symbol(Symbol { name: "gPlayerControl", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global | Weak), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
[(Address(420), Dim, 5), (Spacing(4), Normal, 0), (Opcode(".word", 65534), Normal, 10), (Symbol(Symbol { name: "data_027e103c", demangled_name: None, normalized_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global | Weak), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
[(Address(424), Dim, 5), (BranchArrow(25), Rotating(8), 0), (Opcode(".word", 65534), Normal, 10), (Symbol(Symbol { name: "data_027e1098", demangled_name: None, normalized_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global | Weak), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
[(Address(428), Dim, 5), (Spacing(4), Normal, 0), (Opcode(".word", 65534), Normal, 10), (Symbol(Symbol { name: "gPlayerControl", demangled_name: None, normalized_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global | Weak), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More