Compare commits

..

17 Commits

Author SHA1 Message Date
ffb38d1bb0 Fix cargo deny advisories 2025-03-09 22:59:42 -06:00
d56dda72f0 Version v3.0.0-beta.2 2025-03-09 22:56:28 -06:00
c6971f3f2d Add initial support for x86-64 relocations 2025-03-09 22:51:43 -06:00
3965a035fa objdiff-cli diff: Show build errors/log 2025-03-09 22:51:43 -06:00
f1fc29f77e Split report changes into separate proto 2025-03-08 14:39:15 -07:00
7c4f1c5d13 Fix left/right arch mismatches in diff code 2025-03-08 10:44:44 -07:00
fa4a6cadbb Downgrade objdiff-wasm Rust edition temporarily 2025-03-04 23:27:17 -07:00
799971d54e Migrate to Rust edition 2024 2025-03-04 22:31:38 -07:00
8eef37e8df Version v3.0.0-beta.1 2025-03-04 22:24:55 -07:00
5f36916087 Fix unintended unwrap in load_font_if_needed 2025-03-04 22:24:20 -07:00
ee667a2dde Update config-schema.json: PPC -> PowerPC 2025-03-04 22:23:45 -07:00
LagoLunatic
cf5fc54cfa PPC: Reimplement pooled data reference calculation (#167)
* PPC: Calculate pooled relocations

Reimplements #140

The relocations are now generated when the object is first read in `parse`, right after the real relocations are read.

`resolve_relocation` was changed to take `obj.symbols` instead of `obj` as an argument, because `obj` itself doesn't exist yet at the time the relocations are being read.

* Improve readability of PPC pool relocs code

* Fix regression causing extern pool relocs to be ignored

* Fix showing incorrect diff when diffing weak stripped symbol with an addend

This is a regression that was introduced by #158 diffing addends in addition to symbol names. But it's not really a bug in that PR, rather it seems like I simply never added the offset into the addend when creating a fake pool relocation for an extern symbol. So this commit fixes that root issue instead.

* Add PPC "Calculate pooled data references" option

* Fix objdiff-wasm compilation errors

* Update PPC test snapshots
2025-03-04 20:40:34 -07:00
1cdfa1e857 Update rabbitizer & utilize use_dollar option 2025-03-04 09:38:59 -07:00
3f157f33a5 Reorder tooltip/context items slightly 2025-03-04 09:10:54 -07:00
LagoLunatic
a1ea2919f8 Reimplement function data value tooltips, function data value diffing, and data relocation diffing (#166)
* Show data literal values on instruction hover

Reimplements #108

* Show reloc diffs in func view when data's content differs

Reimplements #153

* Data diff view: Show relocs on hover and in context menu

This reimplements #154

Note that colorizing the text depending on the kind of diff has still not been reimplemented yet

* Fix up some comments
2025-03-04 08:56:46 -07:00
9c31c82a37 cargo fmt 2025-03-03 21:08:36 -07:00
4f34dfa194 Don't infer sizes for labels within another symbol 2025-03-03 21:01:22 -07:00
75 changed files with 6603 additions and 780 deletions

21
Cargo.lock generated
View File

@@ -1,6 +1,6 @@
# This file is automatically @generated by Cargo. # This file is automatically @generated by Cargo.
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 version = 4
[[package]] [[package]]
name = "ab_glyph" name = "ab_glyph"
@@ -2652,7 +2652,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"windows-targets 0.52.6", "windows-targets 0.48.5",
] ]
[[package]] [[package]]
@@ -3268,7 +3268,7 @@ dependencies = [
[[package]] [[package]]
name = "objdiff-cli" name = "objdiff-cli"
version = "3.0.0-alpha.2" version = "3.0.0-beta.2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"argp", "argp",
@@ -3291,7 +3291,7 @@ dependencies = [
[[package]] [[package]]
name = "objdiff-core" name = "objdiff-core"
version = "3.0.0-alpha.2" version = "3.0.0-beta.2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"arm-attr", "arm-attr",
@@ -3344,13 +3344,12 @@ dependencies = [
[[package]] [[package]]
name = "objdiff-gui" name = "objdiff-gui"
version = "3.0.0-alpha.2" version = "3.0.0-beta.2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"cfg-if", "cfg-if",
"const_format", "const_format",
"cwdemangle", "cwdemangle",
"cwextab",
"dirs 6.0.0", "dirs 6.0.0",
"eframe", "eframe",
"egui", "egui",
@@ -3370,8 +3369,6 @@ dependencies = [
"rlwinmdec", "rlwinmdec",
"ron", "ron",
"serde", "serde",
"serde_json",
"shell-escape",
"tauri-winres", "tauri-winres",
"time", "time",
"tracing-subscriber", "tracing-subscriber",
@@ -3383,7 +3380,7 @@ dependencies = [
[[package]] [[package]]
name = "objdiff-wasm" name = "objdiff-wasm"
version = "3.0.0-alpha.2" version = "3.0.0-beta.2"
dependencies = [ dependencies = [
"log", "log",
"objdiff-core", "objdiff-core",
@@ -3897,7 +3894,7 @@ dependencies = [
[[package]] [[package]]
name = "rabbitizer" name = "rabbitizer"
version = "2.0.0-dev0" version = "2.0.0-dev0"
source = "git+https://github.com/Decompollaborate/rabbitizer.git?branch=🦀#706fd145b788ec3d068d55904dd112c7989e0412" source = "git+https://github.com/Decompollaborate/rabbitizer.git?branch=%F0%9F%A6%80#06dc8b6c826c3d60e112d4c2cd70aa54e308be12"
dependencies = [ dependencies = [
"bitflags 2.9.0", "bitflags 2.9.0",
] ]
@@ -4208,9 +4205,9 @@ dependencies = [
[[package]] [[package]]
name = "ring" name = "ring"
version = "0.17.11" version = "0.17.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da5349ae27d3887ca812fb375b45a4fbb36d8d12d2df394968cd86e35683fe73" checksum = "70ac5d832aa16abd7d1def883a8545280c20a60f523a370aa3a9617c2b8550ee"
dependencies = [ dependencies = [
"cc", "cc",
"cfg-if", "cfg-if",

View File

@@ -5,7 +5,7 @@ members = [
"objdiff-gui", "objdiff-gui",
"objdiff-wasm", "objdiff-wasm",
] ]
resolver = "2" resolver = "3"
[profile.release-lto] [profile.release-lto]
inherits = "release" inherits = "release"
@@ -14,9 +14,9 @@ strip = "debuginfo"
codegen-units = 1 codegen-units = 1
[workspace.package] [workspace.package]
version = "3.0.0-alpha.2" version = "3.0.0-beta.2"
authors = ["Luke Street <luke@street.dev>"] authors = ["Luke Street <luke@street.dev>"]
edition = "2021" edition = "2024"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
repository = "https://github.com/encounter/objdiff" repository = "https://github.com/encounter/objdiff"
rust-version = "1.82" rust-version = "1.85"

View File

@@ -73,6 +73,7 @@ ignore = [
#{ id = "RUSTSEC-0000-0000", reason = "you can specify a reason the advisory is ignored" }, #{ id = "RUSTSEC-0000-0000", reason = "you can specify a reason the advisory is ignored" },
#"a-crate-that-is-yanked@0.1.1", # you can also ignore yanked crate versions if you wish #"a-crate-that-is-yanked@0.1.1", # you can also ignore yanked crate versions if you wish
#{ crate = "a-crate-that-is-yanked@0.1.1", reason = "you can specify why you are ignoring the yanked crate" }, #{ crate = "a-crate-that-is-yanked@0.1.1", reason = "you can specify why you are ignoring the yanked crate" },
{ id = "RUSTSEC-2024-0436", reason = "Unmaintained paste crate is an indirect dependency" },
] ]
# If this is true, then cargo deny will use the git executable to fetch advisory database. # 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. # If this is false, then it uses a built-in git library.

View File

@@ -4,7 +4,7 @@
//! For now, this only adds a --version/-V option which causes early-exit. //! For now, this only adds a --version/-V option which causes early-exit.
use std::ffi::OsStr; use std::ffi::OsStr;
use argp::{parser::ParseGlobalOptions, EarlyExit, FromArgs, TopLevelCommand}; use argp::{EarlyExit, FromArgs, TopLevelCommand, parser::ParseGlobalOptions};
struct ArgsOrVersion<T>(T) struct ArgsOrVersion<T>(T)
where T: FromArgs; where T: FromArgs;

View File

@@ -3,40 +3,39 @@ use std::{
mem, mem,
str::FromStr, str::FromStr,
sync::{ sync::{
atomic::{AtomicBool, Ordering},
Arc, Arc,
atomic::{AtomicBool, Ordering},
}, },
task::{Wake, Waker}, task::{Wake, Waker},
time::Duration, time::Duration,
}; };
use anyhow::{anyhow, bail, Context, Result}; use anyhow::{Context, Result, anyhow, bail};
use argp::FromArgs; use argp::FromArgs;
use crossterm::{ use crossterm::{
event, event,
event::{DisableMouseCapture, EnableMouseCapture}, event::{DisableMouseCapture, EnableMouseCapture},
terminal::{ terminal::{
disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen, SetTitle, EnterAlternateScreen, LeaveAlternateScreen, SetTitle, disable_raw_mode, enable_raw_mode,
}, },
}; };
use objdiff_core::{ use objdiff_core::{
bindings::diff::DiffResult, bindings::diff::DiffResult,
build::{ build::{
watcher::{create_watcher, Watcher}, BuildConfig, BuildStatus,
BuildConfig, watcher::{Watcher, create_watcher},
}, },
config::{ config::{
build_globset, ProjectConfig, ProjectObject, ProjectObjectMetadata, build_globset,
path::{check_path_buf, platform_path, platform_path_serde_option}, path::{check_path_buf, platform_path, platform_path_serde_option},
ProjectConfig, ProjectObject, ProjectObjectMetadata,
}, },
diff::{ diff::{
self, ConfigEnum, ConfigPropertyId, ConfigPropertyKind, DiffObjConfig, MappingConfig, self, ConfigEnum, ConfigPropertyId, ConfigPropertyKind, DiffObjConfig, MappingConfig,
ObjectDiff, ObjectDiff,
}, },
jobs::{ jobs::{
objdiff::{start_build, ObjDiffConfig},
Job, JobQueue, JobResult, Job, JobQueue, JobResult,
objdiff::{ObjDiffConfig, start_build},
}, },
obj::{self, Object}, obj::{self, Object},
}; };
@@ -45,10 +44,10 @@ use typed_path::{Utf8PlatformPath, Utf8PlatformPathBuf};
use crate::{ use crate::{
util::{ util::{
output::{write_output, OutputFormat}, output::{OutputFormat, write_output},
term::crossterm_panic_handler, term::crossterm_panic_handler,
}, },
views::{function_diff::FunctionDiffUi, EventControlFlow, EventResult, UiView}, views::{EventControlFlow, EventResult, UiView, function_diff::FunctionDiffUi},
}; };
#[derive(FromArgs, PartialEq, Debug)] #[derive(FromArgs, PartialEq, Debug)]
@@ -252,6 +251,8 @@ pub struct AppState {
pub project_config: Option<ProjectConfig>, pub project_config: Option<ProjectConfig>,
pub target_path: Option<Utf8PlatformPathBuf>, pub target_path: Option<Utf8PlatformPathBuf>,
pub base_path: Option<Utf8PlatformPathBuf>, pub base_path: Option<Utf8PlatformPathBuf>,
pub left_status: Option<BuildStatus>,
pub right_status: Option<BuildStatus>,
pub left_obj: Option<(Object, ObjectDiff)>, pub left_obj: Option<(Object, ObjectDiff)>,
pub right_obj: Option<(Object, ObjectDiff)>, pub right_obj: Option<(Object, ObjectDiff)>,
pub prev_obj: Option<(Object, ObjectDiff)>, pub prev_obj: Option<(Object, ObjectDiff)>,
@@ -349,6 +350,8 @@ impl AppState {
JobResult::None => unreachable!("Unexpected JobResult::None"), JobResult::None => unreachable!("Unexpected JobResult::None"),
JobResult::ObjDiff(result) => { JobResult::ObjDiff(result) => {
let result = result.unwrap(); let result = result.unwrap();
self.left_status = Some(result.first_status);
self.right_status = Some(result.second_status);
self.left_obj = result.first_obj; self.left_obj = result.first_obj;
self.right_obj = result.second_obj; self.right_obj = result.second_obj;
self.reload_time = Some(result.time); self.reload_time = Some(result.time);
@@ -389,6 +392,8 @@ fn run_interactive(
project_config, project_config,
target_path, target_path,
base_path, base_path,
left_status: None,
right_status: None,
left_obj: None, left_obj: None,
right_obj: None, right_obj: None,
prev_obj: None, prev_obj: None,
@@ -426,15 +431,17 @@ fn run_interactive(
let mut result = EventResult { redraw: true, ..Default::default() }; let mut result = EventResult { redraw: true, ..Default::default() };
'outer: loop { 'outer: loop {
if result.redraw { if result.redraw {
terminal.draw(|f| loop { terminal.draw(|f| {
result.redraw = false; loop {
view.draw(&state, f, &mut result); result.redraw = false;
result.click_xy = None; view.draw(&state, f, &mut result);
if !result.redraw { result.click_xy = None;
break; if !result.redraw {
break;
}
// Clear buffer on redraw
f.buffer_mut().reset();
} }
// Clear buffer on redraw
f.buffer_mut().reset();
})?; })?;
} }
loop { loop {

View File

@@ -1,12 +1,11 @@
use std::{collections::HashSet, fs::File, io::Read, time::Instant}; use std::{collections::HashSet, fs::File, io::Read, time::Instant};
use anyhow::{bail, Context, Result}; use anyhow::{Context, Result, bail};
use argp::FromArgs; use argp::FromArgs;
use objdiff_core::{ use objdiff_core::{
bindings::report::{ bindings::report::{
ChangeItem, ChangeItemInfo, ChangeUnit, Changes, ChangesInput, Measures, Report, ChangeItem, ChangeItemInfo, ChangeUnit, Changes, ChangesInput, Measures, REPORT_VERSION,
ReportCategory, ReportItem, ReportItemMetadata, ReportUnit, ReportUnitMetadata, Report, ReportCategory, ReportItem, ReportItemMetadata, ReportUnit, ReportUnitMetadata,
REPORT_VERSION,
}, },
config::path::platform_path, config::path::platform_path,
diff, obj, diff, obj,
@@ -19,7 +18,7 @@ use typed_path::{Utf8PlatformPath, Utf8PlatformPathBuf};
use crate::{ use crate::{
cmd::diff::ObjectConfig, cmd::diff::ObjectConfig,
util::output::{write_output, OutputFormat}, util::output::{OutputFormat, write_output},
}; };
#[derive(FromArgs, PartialEq, Debug)] #[derive(FromArgs, PartialEq, Debug)]
@@ -206,11 +205,7 @@ fn report_object(
let section_match_percent = section_diff.match_percent.unwrap_or_else(|| { let section_match_percent = section_diff.match_percent.unwrap_or_else(|| {
// Support cases where we don't have a target object, // Support cases where we don't have a target object,
// assume complete means 100% match // assume complete means 100% match
if object.complete.unwrap_or(false) { if object.complete.unwrap_or(false) { 100.0 } else { 0.0 }
100.0
} else {
0.0
}
}); });
sections.push(ReportItem { sections.push(ReportItem {
name: section.name.clone(), name: section.name.clone(),
@@ -251,11 +246,7 @@ fn report_object(
let match_percent = symbol_diff.match_percent.unwrap_or_else(|| { let match_percent = symbol_diff.match_percent.unwrap_or_else(|| {
// Support cases where we don't have a target object, // Support cases where we don't have a target object,
// assume complete means 100% match // assume complete means 100% match
if object.complete.unwrap_or(false) { if object.complete.unwrap_or(false) { 100.0 } else { 0.0 }
100.0
} else {
0.0
}
}); });
measures.fuzzy_match_percent += match_percent * symbol.size as f32; measures.fuzzy_match_percent += match_percent * symbol.size as f32;
measures.total_code += symbol.size; measures.total_code += symbol.size;

View File

@@ -17,7 +17,7 @@ use anyhow::{Error, Result};
use argp::{FromArgValue, FromArgs}; use argp::{FromArgValue, FromArgs};
use enable_ansi_support::enable_ansi_support; use enable_ansi_support::enable_ansi_support;
use supports_color::Stream; use supports_color::Stream;
use tracing_subscriber::{filter::LevelFilter, EnvFilter}; use tracing_subscriber::{EnvFilter, filter::LevelFilter};
#[derive(Debug, Eq, PartialEq, Copy, Clone)] #[derive(Debug, Eq, PartialEq, Copy, Clone)]
enum LogLevel { enum LogLevel {

View File

@@ -5,7 +5,7 @@ use std::{
path::Path, path::Path,
}; };
use anyhow::{bail, Context, Result}; use anyhow::{Context, Result, bail};
use tracing::info; use tracing::info;
#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]

View File

@@ -3,7 +3,7 @@ use std::{io::stdout, panic};
use crossterm::{ use crossterm::{
cursor::Show, cursor::Show,
event::DisableMouseCapture, event::DisableMouseCapture,
terminal::{disable_raw_mode, LeaveAlternateScreen}, terminal::{LeaveAlternateScreen, disable_raw_mode},
}; };
pub fn crossterm_panic_handler() { pub fn crossterm_panic_handler() {

View File

@@ -1,18 +1,19 @@
use core::cmp::Ordering; use core::cmp::Ordering;
use anyhow::{bail, Result}; use anyhow::Result;
use crossterm::event::{Event, KeyCode, KeyEventKind, KeyModifiers, MouseButton, MouseEventKind}; use crossterm::event::{Event, KeyCode, KeyEventKind, KeyModifiers, MouseButton, MouseEventKind};
use objdiff_core::{ use objdiff_core::{
build::BuildStatus,
diff::{ diff::{
display::{display_row, DiffText, DiffTextColor, HighlightKind},
DiffObjConfig, FunctionRelocDiffs, InstructionDiffKind, ObjectDiff, SymbolDiff, DiffObjConfig, FunctionRelocDiffs, InstructionDiffKind, ObjectDiff, SymbolDiff,
display::{DiffText, DiffTextColor, HighlightKind, display_row},
}, },
obj::Object, obj::Object,
}; };
use ratatui::{ use ratatui::{
Frame,
prelude::*, prelude::*,
widgets::{Block, Borders, Clear, Paragraph, Scrollbar, ScrollbarOrientation, ScrollbarState}, widgets::{Block, Borders, Clear, Paragraph, Scrollbar, ScrollbarOrientation, ScrollbarState},
Frame,
}; };
use super::{EventControlFlow, EventResult, UiView}; use super::{EventControlFlow, EventResult, UiView};
@@ -126,6 +127,11 @@ impl UiView for FunctionDiffUi {
); );
max_width = max_width.max(text.width()); max_width = max_width.max(text.width());
left_text = Some(text); left_text = Some(text);
} else if let Some(status) = &state.left_status {
let mut text = Text::default();
self.print_build_status(&mut text, status);
max_width = max_width.max(text.width());
left_text = Some(text);
} }
let mut right_text = None; let mut right_text = None;
@@ -155,6 +161,11 @@ impl UiView for FunctionDiffUi {
let rect = content_chunks[1].inner(Margin::new(1, 1)); let rect = content_chunks[1].inner(Margin::new(1, 1));
self.print_margin(&mut text, symbol_diff, rect); self.print_margin(&mut text, symbol_diff, rect);
margin_text = Some(text); margin_text = Some(text);
} else if let Some(status) = &state.right_status {
let mut text = Text::default();
self.print_build_status(&mut text, status);
max_width = max_width.max(text.width());
right_text = Some(text);
} }
let mut prev_text = None; let mut prev_text = None;
@@ -453,7 +464,7 @@ impl UiView for FunctionDiffUi {
} }
(Some((_l, _ls, ld)), None) => ld.instruction_rows.len(), (Some((_l, _ls, ld)), None) => ld.instruction_rows.len(),
(None, Some((_r, _rs, rd))) => rd.instruction_rows.len(), (None, Some((_r, _rs, rd))) => rd.instruction_rows.len(),
(None, None) => bail!("Symbol not found: {}", self.symbol_name), (None, None) => 0,
}; };
self.left_sym = left_sym; self.left_sym = left_sym;
self.right_sym = right_sym; self.right_sym = right_sym;
@@ -596,6 +607,18 @@ impl FunctionDiffUi {
} }
} }
} }
fn print_build_status<'a>(&self, out: &mut Text<'a>, status: &'a BuildStatus) {
if !status.cmdline.is_empty() {
out.lines.push(Line::styled(status.cmdline.clone(), Style::new().fg(Color::LightBlue)));
}
for line in status.stdout.lines() {
out.lines.push(Line::styled(line, Style::new().fg(Color::White)));
}
for line in status.stderr.lines() {
out.lines.push(Line::styled(line, Style::new().fg(Color::Red)));
}
}
} }
pub const COLOR_ROTATION: [Color; 7] = [ pub const COLOR_ROTATION: [Color; 7] = [

View File

@@ -19,7 +19,15 @@ fn compile_protos() {
.map(|m| m.modified().unwrap()) .map(|m| m.modified().unwrap())
.unwrap_or(std::time::SystemTime::UNIX_EPOCH); .unwrap_or(std::time::SystemTime::UNIX_EPOCH);
let mut run_protoc = false; let mut run_protoc = false;
let proto_files = vec![root.join("report.proto")]; let proto_files = root
.read_dir()
.unwrap()
.filter_map(|e| {
let e = e.unwrap();
let path = e.path();
(path.extension() == Some(std::ffi::OsStr::new("proto"))).then_some(path)
})
.collect::<Vec<_>>();
for proto_file in &proto_files { for proto_file in &proto_files {
println!("cargo:rerun-if-changed={}", proto_file.display()); println!("cargo:rerun-if-changed={}", proto_file.display());
let mtime = match std::fs::metadata(proto_file) { let mtime = match std::fs::metadata(proto_file) {

View File

@@ -194,6 +194,13 @@
"name": "Register '$' prefix", "name": "Register '$' prefix",
"description": "Display MIPS register names with a '$' prefix." "description": "Display MIPS register names with a '$' prefix."
}, },
{
"id": "ppc.calculatePoolRelocations",
"type": "boolean",
"default": true,
"name": "Calculate pooled data references",
"description": "Display pooled data references in functions as fake relocations."
},
{ {
"id": "x86.formatter", "id": "x86.formatter",
"type": "choice", "type": "choice",
@@ -253,6 +260,13 @@
"mips.registerPrefix" "mips.registerPrefix"
] ]
}, },
{
"id": "ppc",
"name": "PowerPC",
"properties": [
"ppc.calculatePoolRelocations"
]
},
{ {
"id": "x86", "id": "x86",
"name": "x86", "name": "x86",

View File

@@ -0,0 +1,59 @@
syntax = "proto3";
import "report.proto";
package objdiff.report;
// A pair of reports to compare and generate changes
message ChangesInput {
// The previous report
Report from = 1;
// The current report
Report to = 2;
}
// Changes between two reports
message Changes {
// The progress info for the previous report
Measures from = 1;
// The progress info for the current report
Measures to = 2;
// Units that changed
repeated ChangeUnit units = 3;
}
// A changed unit
message ChangeUnit {
// The name of the unit
string name = 1;
// The previous progress info (omitted if new)
optional Measures from = 2;
// The current progress info (omitted if removed)
optional Measures to = 3;
// Sections that changed
repeated ChangeItem sections = 4;
// Functions that changed
repeated ChangeItem functions = 5;
// Extra metadata for this unit
optional ReportUnitMetadata metadata = 6;
}
// A changed section or function
message ChangeItem {
// The name of the item
string name = 1;
// The previous progress info (omitted if new)
optional ChangeItemInfo from = 2;
// The current progress info (omitted if removed)
optional ChangeItemInfo to = 3;
// Extra metadata for this item
optional ReportItemMetadata metadata = 4;
}
// Progress info for a section or function
message ChangeItemInfo {
// The overall match percent for this item
float fuzzy_match_percent = 1;
// The size of the item in bytes
uint64 size = 2;
}

View File

@@ -2,6 +2,18 @@ syntax = "proto3";
package objdiff.report; package objdiff.report;
// Project progress report
message Report {
// Overall progress info
Measures measures = 1;
// Units within this report
repeated ReportUnit units = 2;
// Report version
uint32 version = 3;
// Progress categories
repeated ReportCategory categories = 4;
}
// Progress info for a report or unit // Progress info for a report or unit
message Measures { message Measures {
// Overall match percent, including partially matched functions and data // Overall match percent, including partially matched functions and data
@@ -38,18 +50,6 @@ message Measures {
uint32 complete_units = 16; uint32 complete_units = 16;
} }
// Project progress report
message Report {
// Overall progress info
Measures measures = 1;
// Units within this report
repeated ReportUnit units = 2;
// Report version
uint32 version = 3;
// Progress categories
repeated ReportCategory categories = 4;
}
message ReportCategory { message ReportCategory {
// The ID of the category // The ID of the category
string id = 1; string id = 1;
@@ -108,57 +108,3 @@ message ReportItemMetadata {
// The virtual address of the function or section // The virtual address of the function or section
optional uint64 virtual_address = 2; optional uint64 virtual_address = 2;
} }
// A pair of reports to compare and generate changes
message ChangesInput {
// The previous report
Report from = 1;
// The current report
Report to = 2;
}
// Changes between two reports
message Changes {
// The progress info for the previous report
Measures from = 1;
// The progress info for the current report
Measures to = 2;
// Units that changed
repeated ChangeUnit units = 3;
}
// A changed unit
message ChangeUnit {
// The name of the unit
string name = 1;
// The previous progress info (omitted if new)
optional Measures from = 2;
// The current progress info (omitted if removed)
optional Measures to = 3;
// Sections that changed
repeated ChangeItem sections = 4;
// Functions that changed
repeated ChangeItem functions = 5;
// Extra metadata for this unit
optional ReportUnitMetadata metadata = 6;
}
// A changed section or function
message ChangeItem {
// The name of the item
string name = 1;
// The previous progress info (omitted if new)
optional ChangeItemInfo from = 2;
// The current progress info (omitted if removed)
optional ChangeItemInfo to = 3;
// Extra metadata for this item
optional ReportItemMetadata metadata = 4;
}
// Progress info for a section or function
message ChangeItemInfo {
// The overall match percent for this item
float fuzzy_match_percent = 1;
// The size of the item in bytes
uint64 size = 2;
}

View File

@@ -5,14 +5,14 @@ use alloc::{
vec::Vec, vec::Vec,
}; };
use anyhow::{bail, Result}; use anyhow::{Result, bail};
use arm_attr::{enums::CpuArch, tag::Tag, BuildAttrs}; use arm_attr::{BuildAttrs, enums::CpuArch, tag::Tag};
use object::{elf, Endian as _, Object as _, ObjectSection as _, ObjectSymbol as _}; use object::{Endian as _, Object as _, ObjectSection as _, ObjectSymbol as _, elf};
use unarm::{args, arm, thumb}; use unarm::{args, arm, thumb};
use crate::{ use crate::{
arch::Arch, arch::Arch,
diff::{display::InstructionPart, ArmArchVersion, ArmR9Usage, DiffObjConfig}, diff::{ArmArchVersion, ArmR9Usage, DiffObjConfig, display::InstructionPart},
obj::{ obj::{
InstructionRef, RelocationFlags, ResolvedInstructionRef, ResolvedRelocation, InstructionRef, RelocationFlags, ResolvedInstructionRef, ResolvedRelocation,
ScannedInstruction, SymbolFlag, SymbolFlagSet, SymbolKind, ScannedInstruction, SymbolFlag, SymbolFlagSet, SymbolKind,
@@ -58,11 +58,7 @@ impl ArchArm {
} }
// Only checking first CpuArch tag. Others may exist, but that's very unlikely. // Only checking first CpuArch tag. Others may exist, but that's very unlikely.
let cpu_arch = subsection.into_public_tag_iter()?.find_map(|(_, tag)| { let cpu_arch = subsection.into_public_tag_iter()?.find_map(|(_, tag)| {
if let Tag::CpuArch(cpu_arch) = tag { if let Tag::CpuArch(cpu_arch) = tag { Some(cpu_arch) } else { None }
Some(cpu_arch)
} else {
None
}
}); });
match cpu_arch { match cpu_arch {
Some(CpuArch::V4T) => return Ok(Some(unarm::ArmVersion::V4T)), Some(CpuArch::V4T) => return Ok(Some(unarm::ArmVersion::V4T)),
@@ -358,11 +354,7 @@ impl Arch for ArchArm {
} }
fn symbol_address(&self, address: u64, kind: SymbolKind) -> u64 { fn symbol_address(&self, address: u64, kind: SymbolKind) -> u64 {
if kind == SymbolKind::Function { if kind == SymbolKind::Function { address & !1 } else { address }
address & !1
} else {
address
}
} }
fn extra_symbol_flags(&self, symbol: &object::Symbol) -> SymbolFlagSet { fn extra_symbol_flags(&self, symbol: &object::Symbol) -> SymbolFlagSet {

View File

@@ -5,7 +5,7 @@ use alloc::{
}; };
use core::cmp::Ordering; use core::cmp::Ordering;
use anyhow::{bail, Result}; use anyhow::{Result, bail};
use object::elf; use object::elf;
use yaxpeax_arch::{Arch as YaxpeaxArch, Decoder, Reader, U8Reader}; use yaxpeax_arch::{Arch as YaxpeaxArch, Decoder, Reader, U8Reader};
use yaxpeax_arm::armv8::a64::{ use yaxpeax_arm::armv8::a64::{
@@ -15,7 +15,7 @@ use yaxpeax_arm::armv8::a64::{
use crate::{ use crate::{
arch::Arch, arch::Arch,
diff::{display::InstructionPart, DiffObjConfig}, diff::{DiffObjConfig, display::InstructionPart},
obj::{ obj::{
InstructionRef, RelocationFlags, ResolvedInstructionRef, ResolvedRelocation, InstructionRef, RelocationFlags, ResolvedInstructionRef, ResolvedRelocation,
ScannedInstruction, ScannedInstruction,
@@ -216,11 +216,7 @@ where Cb: FnMut(InstructionPart<'static>) {
unreachable!("movn operand 1 is always ImmShift"); unreachable!("movn operand 1 is always ImmShift");
}; };
let imm = if let Operand::Register(size, _) = ins.operands[0] { let imm = if let Operand::Register(size, _) = ins.operands[0] {
if size == SizeCode::W { if size == SizeCode::W { imm as u32 as u64 } else { imm }
imm as u32 as u64
} else {
imm
}
} else { } else {
unreachable!("movn operand 0 is always Register"); unreachable!("movn operand 0 is always Register");
}; };
@@ -237,11 +233,7 @@ where Cb: FnMut(InstructionPart<'static>) {
unreachable!("movz operand is always ImmShift"); unreachable!("movz operand is always ImmShift");
}; };
let imm = if let Operand::Register(size, _) = ins.operands[0] { let imm = if let Operand::Register(size, _) = ins.operands[0] {
if size == SizeCode::W { if size == SizeCode::W { imm as u32 as u64 } else { imm }
imm as u32 as u64
} else {
imm
}
} else { } else {
unreachable!("movz operand 0 is always Register"); unreachable!("movz operand 0 is always Register");
}; };
@@ -574,11 +566,7 @@ where Cb: FnMut(InstructionPart<'static>) {
{ {
if immr < imms { if immr < imms {
let size = if let Operand::Register(size, _) = ins.operands[0] { let size = if let Operand::Register(size, _) = ins.operands[0] {
if size == SizeCode::W { if size == SizeCode::W { 32 } else { 64 }
32
} else {
64
}
} else { } else {
unreachable!("operand 0 is always a register"); unreachable!("operand 0 is always a register");
}; };

View File

@@ -1,18 +1,18 @@
use alloc::{collections::BTreeMap, string::ToString, vec::Vec}; use alloc::{collections::BTreeMap, string::ToString, vec::Vec};
use core::ops::Range; use core::ops::Range;
use anyhow::{bail, Result}; use anyhow::{Result, bail};
use object::{elf, Endian as _, Object as _, ObjectSection as _, ObjectSymbol as _}; use object::{Endian as _, Object as _, ObjectSection as _, ObjectSymbol as _, elf};
use rabbitizer::{ use rabbitizer::{
abi::Abi,
operands::{ValuedOperand, IU16},
registers_meta::Register,
IsaExtension, IsaVersion, Vram, IsaExtension, IsaVersion, Vram,
abi::Abi,
operands::{IU16, ValuedOperand},
registers_meta::Register,
}; };
use crate::{ use crate::{
arch::Arch, arch::Arch,
diff::{display::InstructionPart, DiffObjConfig, MipsAbi, MipsInstrCategory}, diff::{DiffObjConfig, MipsAbi, MipsInstrCategory, display::InstructionPart},
obj::{ obj::{
InstructionArg, InstructionArgValue, InstructionRef, Relocation, RelocationFlags, InstructionArg, InstructionArgValue, InstructionRef, Relocation, RelocationFlags,
ResolvedInstructionRef, ResolvedRelocation, ScannedInstruction, ResolvedInstructionRef, ResolvedRelocation, ScannedInstruction,
@@ -144,9 +144,11 @@ impl ArchMips {
fn instruction_display_flags( fn instruction_display_flags(
&self, &self,
_diff_config: &DiffObjConfig, diff_config: &DiffObjConfig,
) -> rabbitizer::InstructionDisplayFlags { ) -> rabbitizer::InstructionDisplayFlags {
rabbitizer::InstructionDisplayFlags::default().with_unknown_instr_comment(false) rabbitizer::InstructionDisplayFlags::default()
.with_unknown_instr_comment(false)
.with_use_dollar(diff_config.mips_register_prefix)
} }
fn parse_ins_ref( fn parse_ins_ref(
@@ -207,7 +209,6 @@ impl Arch for ArchMips {
function_range, function_range,
resolved.section_index, resolved.section_index,
&display_flags, &display_flags,
diff_config,
cb, cb,
)?; )?;
Ok(()) Ok(())
@@ -301,7 +302,6 @@ fn push_args(
function_range: Range<u64>, function_range: Range<u64>,
section_index: usize, section_index: usize,
display_flags: &rabbitizer::InstructionDisplayFlags, display_flags: &rabbitizer::InstructionDisplayFlags,
diff_config: &DiffObjConfig,
mut arg_cb: impl FnMut(InstructionPart) -> Result<()>, mut arg_cb: impl FnMut(InstructionPart) -> Result<()>,
) -> Result<()> { ) -> Result<()> {
let operands = instruction.valued_operands_iter(); let operands = instruction.valued_operands_iter();
@@ -361,14 +361,11 @@ fn push_args(
})))?; })))?;
} }
arg_cb(InstructionPart::basic("("))?; arg_cb(InstructionPart::basic("("))?;
let mut value = arg_cb(InstructionPart::opaque(base.either_name(
base.either_name(instruction.flags().abi(), display_flags.named_gpr()); instruction.flags().abi(),
if !diff_config.mips_register_prefix { display_flags.named_gpr(),
if let Some(trimmed) = value.strip_prefix('$') { !display_flags.use_dollar(),
value = trimmed; )))?;
}
}
arg_cb(InstructionPart::opaque(value))?;
arg_cb(InstructionPart::basic(")"))?; arg_cb(InstructionPart::basic(")"))?;
} }
// ValuedOperand::r5900_immediate15(..) => match relocation { // ValuedOperand::r5900_immediate15(..) => match relocation {
@@ -382,14 +379,9 @@ fn push_args(
// } // }
// }, // },
_ => { _ => {
let value = op.display(instruction, display_flags, None::<&str>).to_string(); arg_cb(InstructionPart::opaque(
if !diff_config.mips_register_prefix { op.display(instruction, display_flags, None::<&str>).to_string(),
if let Some(value) = value.strip_prefix('$') { ))?;
arg_cb(InstructionPart::opaque(value))?;
continue;
}
}
arg_cb(InstructionPart::opaque(value))?;
} }
} }
} }

View File

@@ -1,17 +1,17 @@
use alloc::{borrow::Cow, boxed::Box, format, string::String, vec::Vec}; use alloc::{borrow::Cow, boxed::Box, format, string::String, vec::Vec};
use core::{ffi::CStr, fmt, fmt::Debug}; use core::{ffi::CStr, fmt, fmt::Debug};
use anyhow::{bail, Result}; use anyhow::{Result, bail};
use object::Endian as _; use object::Endian as _;
use crate::{ use crate::{
diff::{ diff::{
display::{ContextItem, HoverItem, InstructionPart},
DiffObjConfig, DiffObjConfig,
display::{ContextItem, HoverItem, InstructionPart},
}, },
obj::{ obj::{
InstructionArg, Object, ParsedInstruction, RelocationFlags, ResolvedInstructionRef, InstructionArg, Object, ParsedInstruction, Relocation, RelocationFlags,
ScannedInstruction, SymbolFlagSet, SymbolKind, ResolvedInstructionRef, ScannedInstruction, Symbol, SymbolFlagSet, SymbolKind,
}, },
util::ReallySigned, util::ReallySigned,
}; };
@@ -66,7 +66,9 @@ impl DataType {
pub fn display_literals(&self, endian: object::Endianness, bytes: &[u8]) -> Vec<String> { pub fn display_literals(&self, endian: object::Endianness, bytes: &[u8]) -> Vec<String> {
let mut strs = Vec::new(); let mut strs = Vec::new();
if self.required_len().is_some_and(|l| bytes.len() < l) { if self.required_len().is_some_and(|l| bytes.len() < l) {
log::warn!("Failed to display a symbol value for a symbol whose size is too small for instruction referencing it."); log::warn!(
"Failed to display a symbol value for a symbol whose size is too small for instruction referencing it."
);
return strs; return strs;
} }
let mut bytes = bytes; let mut bytes = bytes;
@@ -124,7 +126,7 @@ impl DataType {
} }
DataType::Double => { DataType::Double => {
let bytes: [u8; 8] = bytes.try_into().unwrap(); let bytes: [u8; 8] = bytes.try_into().unwrap();
strs.push(format!("{:?}f", match endian { strs.push(format!("{:?}", match endian {
object::Endianness::Little => f64::from_le_bytes(bytes), object::Endianness::Little => f64::from_le_bytes(bytes),
object::Endianness::Big => f64::from_be_bytes(bytes), object::Endianness::Big => f64::from_be_bytes(bytes),
})); }));
@@ -212,6 +214,17 @@ pub trait Arch: Send + Sync + Debug {
cb: &mut dyn FnMut(InstructionPart) -> Result<()>, cb: &mut dyn FnMut(InstructionPart) -> Result<()>,
) -> Result<()>; ) -> Result<()>;
/// Generate a list of fake relocations from the given code that represent pooled data accesses.
fn generate_pooled_relocations(
&self,
_address: u64,
_code: &[u8],
_relocations: &[Relocation],
_symbols: &[Symbol],
) -> Vec<Relocation> {
Vec::new()
}
fn implcit_addend( fn implcit_addend(
&self, &self,
file: &object::File<'_>, file: &object::File<'_>,

View File

@@ -1,24 +1,25 @@
use alloc::{ use alloc::{
collections::BTreeMap, collections::{BTreeMap, BTreeSet},
string::{String, ToString}, string::{String, ToString},
vec, vec,
vec::Vec, vec::Vec,
}; };
use anyhow::{bail, ensure, Result}; use anyhow::{Result, bail, ensure};
use cwextab::{decode_extab, ExceptionTableData}; use cwextab::{ExceptionTableData, decode_extab};
use flagset::Flags; use flagset::Flags;
use object::{elf, Object as _, ObjectSection as _, ObjectSymbol as _}; use object::{Object as _, ObjectSection as _, ObjectSymbol as _, elf};
use crate::{ use crate::{
arch::{Arch, DataType}, arch::{Arch, DataType},
diff::{ diff::{
display::{ContextItem, HoverItem, HoverItemColor, InstructionPart, SymbolNavigationKind},
DiffObjConfig, DiffObjConfig,
data::resolve_relocation,
display::{ContextItem, HoverItem, HoverItemColor, InstructionPart, SymbolNavigationKind},
}, },
obj::{ obj::{
InstructionRef, Object, Relocation, RelocationFlags, ResolvedInstructionRef, InstructionRef, Object, Relocation, RelocationFlags, ResolvedInstructionRef,
ResolvedRelocation, ScannedInstruction, SymbolFlag, SymbolFlagSet, ResolvedRelocation, ScannedInstruction, Symbol, SymbolFlag, SymbolFlagSet,
}, },
}; };
@@ -157,6 +158,16 @@ impl Arch for ArchPpc {
Ok(()) Ok(())
} }
fn generate_pooled_relocations(
&self,
address: u64,
code: &[u8],
relocations: &[Relocation],
symbols: &[Symbol],
) -> Vec<Relocation> {
generate_fake_pool_relocations_for_function(address, code, relocations, symbols)
}
fn implcit_addend( fn implcit_addend(
&self, &self,
_file: &object::File<'_>, _file: &object::File<'_>,
@@ -501,21 +512,26 @@ fn guess_data_type_from_load_store_inst_op(inst_op: ppc750cl::Opcode) -> Option<
} }
} }
// Given an instruction, determine if it could accessing data at the address in a register. struct PoolReference {
// If so, return the offset added to the register's address, the register containing that address, addr_src_gpr: ppc750cl::GPR,
// and (optionally) which destination register the address is being copied into. addr_offset: i16,
#[expect(unused)] addr_dst_gpr: Option<ppc750cl::GPR>,
fn get_offset_and_addr_gpr_for_possible_pool_reference( }
// Given an instruction, check if it could be accessing pooled data at the address in a register.
// If so, return information pertaining to where the instruction is getting that address from and
// what it's doing with the address (e.g. copying it into another register, adding an offset, etc).
fn get_pool_reference_for_inst(
opcode: ppc750cl::Opcode, opcode: ppc750cl::Opcode,
simplified: &ppc750cl::ParsedIns, simplified: &ppc750cl::ParsedIns,
) -> Option<(i16, ppc750cl::GPR, Option<ppc750cl::GPR>)> { ) -> Option<PoolReference> {
use ppc750cl::{Argument, Opcode}; use ppc750cl::{Argument, Opcode};
let args = &simplified.args; let args = &simplified.args;
if guess_data_type_from_load_store_inst_op(opcode).is_some() { if guess_data_type_from_load_store_inst_op(opcode).is_some() {
match (args[1], args[2]) { match (args[1], args[2]) {
(Argument::Offset(offset), Argument::GPR(addr_src_gpr)) => { (Argument::Offset(offset), Argument::GPR(addr_src_gpr)) => {
// e.g. lwz. Immediate offset. // e.g. lwz. Immediate offset.
Some((offset.0, addr_src_gpr, None)) Some(PoolReference { addr_src_gpr, addr_offset: offset.0, addr_dst_gpr: None })
} }
(Argument::GPR(addr_src_gpr), Argument::GPR(_offset_gpr)) => { (Argument::GPR(addr_src_gpr), Argument::GPR(_offset_gpr)) => {
// e.g. lwzx. The offset is in a register and was likely calculated from an index. // e.g. lwzx. The offset is in a register and was likely calculated from an index.
@@ -523,7 +539,7 @@ fn get_offset_and_addr_gpr_for_possible_pool_reference(
// It may be possible to show all elements by figuring out the stride of the array // It may be possible to show all elements by figuring out the stride of the array
// from the calculations performed on the index before it's put into offset_gpr, but // from the calculations performed on the index before it's put into offset_gpr, but
// this would be much more complicated, so it's not currently done. // this would be much more complicated, so it's not currently done.
Some((0, addr_src_gpr, None)) Some(PoolReference { addr_src_gpr, addr_offset: 0, addr_dst_gpr: None })
} }
_ => None, _ => None,
} }
@@ -541,20 +557,32 @@ fn get_offset_and_addr_gpr_for_possible_pool_reference(
Argument::GPR(addr_dst_gpr), Argument::GPR(addr_dst_gpr),
Argument::GPR(addr_src_gpr), Argument::GPR(addr_src_gpr),
Argument::Simm(simm), Argument::Simm(simm),
) => Some((simm.0, addr_src_gpr, Some(addr_dst_gpr))), ) => Some(PoolReference {
addr_src_gpr,
addr_offset: simm.0,
addr_dst_gpr: Some(addr_dst_gpr),
}),
( (
// `mr` or `mr.` // `mr` or `mr.`
Opcode::Or, Opcode::Or,
Argument::GPR(addr_dst_gpr), Argument::GPR(addr_dst_gpr),
Argument::GPR(addr_src_gpr), Argument::GPR(addr_src_gpr),
Argument::None, Argument::None,
) => Some((0, addr_src_gpr, Some(addr_dst_gpr))), ) => Some(PoolReference {
addr_src_gpr,
addr_offset: 0,
addr_dst_gpr: Some(addr_dst_gpr),
}),
( (
Opcode::Add, Opcode::Add,
Argument::GPR(addr_dst_gpr), Argument::GPR(addr_dst_gpr),
Argument::GPR(addr_src_gpr), Argument::GPR(addr_src_gpr),
Argument::GPR(_offset_gpr), Argument::GPR(_offset_gpr),
) => Some((0, addr_src_gpr, Some(addr_dst_gpr))), ) => Some(PoolReference {
addr_src_gpr,
addr_offset: 0,
addr_dst_gpr: Some(addr_dst_gpr),
}),
_ => None, _ => None,
} }
} }
@@ -562,7 +590,6 @@ fn get_offset_and_addr_gpr_for_possible_pool_reference(
// Remove the relocation we're keeping track of in a particular register when an instruction reuses // Remove the relocation we're keeping track of in a particular register when an instruction reuses
// that register to hold some other value, unrelated to pool relocation addresses. // that register to hold some other value, unrelated to pool relocation addresses.
#[expect(unused)]
fn clear_overwritten_gprs(ins: ppc750cl::Ins, gpr_pool_relocs: &mut BTreeMap<u8, Relocation>) { fn clear_overwritten_gprs(ins: ppc750cl::Ins, gpr_pool_relocs: &mut BTreeMap<u8, Relocation>) {
use ppc750cl::{Argument, Arguments, Opcode}; use ppc750cl::{Argument, Arguments, Opcode};
let mut def_args = Arguments::default(); let mut def_args = Arguments::default();
@@ -582,250 +609,238 @@ fn clear_overwritten_gprs(ins: ppc750cl::Ins, gpr_pool_relocs: &mut BTreeMap<u8,
} }
} }
// TODO // We create a fake relocation for an instruction, vaguely simulating what the actual relocation
// // We create a fake relocation for an instruction, vaguely simulating what the actual relocation // might have looked like if it wasn't pooled. This is so minimal changes are needed to display
// // might have looked like if it wasn't pooled. This is so minimal changes are needed to display // pooled accesses vs non-pooled accesses. We set the relocation type to R_PPC_NONE to indicate that
// // pooled accesses vs non-pooled accesses. We set the relocation type to R_PPC_NONE to indicate that // there isn't really a relocation here, as copying the pool relocation's type wouldn't make sense.
// // there isn't really a relocation here, as copying the pool relocation's type wouldn't make sense. // Also, if this instruction is accessing the middle of a symbol instead of the start, we add an
// // Also, if this instruction is accessing the middle of a symbol instead of the start, we add an // addend to indicate that.
// // addend to indicate that. fn make_fake_pool_reloc(
// fn make_fake_pool_reloc(offset: i16, cur_addr: u32, pool_reloc: &Relocation) -> Option<Relocation> { offset: i16,
// let offset_from_pool = pool_reloc.addend + offset as i64; cur_addr: u32,
// let target_address = pool_reloc.sy.address.checked_add_signed(offset_from_pool)?; pool_reloc: &Relocation,
// let target; symbols: &[Symbol],
// let addend; ) -> Option<Relocation> {
// if pool_reloc.target.orig_section_index.is_some() { let pool_reloc = resolve_relocation(symbols, pool_reloc);
// // If the target symbol is within this current object, then we also need to create a fake let offset_from_pool = pool_reloc.relocation.addend + offset as i64;
// // target symbol to go inside our fake relocation. This is because we don't have access to let target_address = pool_reloc.symbol.address.checked_add_signed(offset_from_pool)?;
// // list of all symbols in this section, so we can't find the real symbol within the pool let target_symbol;
// // based on its address yet. Instead we make a placeholder that has the correct let addend;
// // `orig_section_index` and `address` fields, and then later on when this information is if let Some(section_index) = pool_reloc.symbol.section {
// // displayed to the user, we can find the real symbol by searching through the object's // Find the exact data symbol within the pool being accessed here based on the address.
// // section's symbols for one that contains this address. target_symbol = symbols.iter().position(|s| {
// target = ObjSymbol { s.section == Some(section_index)
// name: "".to_string(), && s.size > 0
// demangled_name: None, && (s.address..s.address + s.size).contains(&target_address)
// address: target_address, })?;
// section_address: 0, addend = target_address.checked_sub(symbols[target_symbol].address)? as i64;
// size: 0, } else {
// size_known: false, // If the target symbol is in a different object (extern), we simply copy the pool
// kind: Default::default(), // relocation's target. This is because it's not possible to locate the actual symbol if
// flags: Default::default(), // it's extern. And doing that for external symbols would also be unnecessary, because when
// orig_section_index: pool_reloc.target.orig_section_index, // the compiler generates an instruction that accesses an external "pool" plus some offset,
// virtual_address: None, // that won't be a normal pool that contains other symbols within it that we want to
// original_index: None, // display. It will be something like a vtable for a class with multiple inheritance (for
// bytes: vec![], // example, dCcD_Cyl in The Wind Waker). So just showing that vtable symbol plus an addend
// }; // to represent the offset into it works fine in this case.
// // The addend is also fake because we don't know yet if the `target_address` here is the exact target_symbol = pool_reloc.relocation.target_symbol;
// // start of the symbol or if it's in the middle of it. addend = pool_reloc.relocation.addend + offset_from_pool;
// addend = 0; }
// } else { Some(Relocation {
// // But if the target symbol is in a different object (extern), then we simply copy the pool flags: RelocationFlags::Elf(elf::R_PPC_NONE),
// // relocation's target. This is because it won't be possible to locate the actual symbol address: cur_addr as u64,
// // later on based only off of an offset without knowing the object or section it's in. And target_symbol,
// // doing that for external symbols would also be unnecessary, because when the compiler addend,
// // generates an instruction that accesses an external "pool" plus some offset, that won't be })
// // a normal pool that contains other symbols within it that we want to display. It will be }
// // something like a vtable for a class with multiple inheritance (for example, dCcD_Cyl in
// // The Wind Waker). So just showing that vtable symbol plus an addend to represent the // Searches through all instructions in a function, determining which registers have the addresses
// // offset into it works fine in this case, no fake symbol to hold an address is necessary. // of pooled data relocations in them, finding which instructions load data from those addresses,
// target = pool_reloc.target.clone(); // and returns a Vec of "fake pool relocations" that simulate what a relocation for that instruction
// addend = pool_reloc.addend; // 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
// Some(ObjReloc { // states it hasn't traversed yet, where each state holds an instruction address and a HashMap of
// flags: RelocationFlags::Elf { r_type: elf::R_PPC_NONE }, // which registers hold which pool relocations at that point.
// address: cur_addr as u64, // When a conditional or unconditional branch is encountered, the destination of the branch is added
// target, // to the queue. Conditional branches will traverse both the path where the branch is taken and the
// addend, // one where it's not. Unconditional branches only follow the branch, ignoring any code immediately
// }) // after the branch instruction.
// } // Limitations: This method does not currently read switch statement jump tables.
// // Instead, we guess that any parts of a function we missed were switch cases, and traverse them as
// // Searches through all instructions in a function, determining which registers have the addresses // if the last `bctr` before that address had branched there. This should be fairly accurate in
// // of pooled data relocations in them, finding which instructions load data from those addresses, // practice - in testing the only instructions it seems to miss are double branches that the
// // and constructing a mapping of the address of that instruction to a "fake pool relocation" that // compiler generates in error which can never be reached during normal execution anyway.
// // simulates what that instruction's relocation would look like if data hadn't been pooled. // It should be possible to implement jump tables properly by reading them out of .data. But this
// // This method tries to follow the function's proper control flow. It keeps track of a queue of // will require keeping track of what value is loaded into each register so we can retrieve the jump
// // states it hasn't traversed yet, where each state holds an instruction address and a HashMap of // table symbol when we encounter a `bctr`.
// // which registers hold which pool relocations at that point. fn generate_fake_pool_relocations_for_function(
// // When a conditional or unconditional branch is encountered, the destination of the branch is added func_address: u64,
// // to the queue. Conditional branches will traverse both the path where the branch is taken and the code: &[u8],
// // one where it's not. Unconditional branches only follow the branch, ignoring any code immediately relocations: &[Relocation],
// // after the branch instruction. symbols: &[Symbol],
// // Limitations: This method cannot read jump tables. This is because the jump tables are located in ) -> Vec<Relocation> {
// // the .data section, but ObjArch.process_code only has access to the .text section. In order to use ppc750cl::{Argument, InsIter, Opcode};
// // work around this limitation and avoid completely missing most code inside switch statements that let mut visited_ins_addrs = BTreeSet::new();
// // use jump tables, we instead guess that any parts of a function we missed were switch cases, and let mut pool_reloc_for_addr = BTreeMap::new();
// // traverse them as if the last `bctr` before that address had branched there. This should be fairly let mut ins_iters_with_gpr_state =
// // accurate in practice - in testing the only instructions it seems to miss are double branches that vec![(InsIter::new(code, func_address as u32), BTreeMap::new())];
// // the compiler generates in error which can never be reached during normal execution anyway. let mut gpr_state_at_bctr = BTreeMap::new();
// fn generate_fake_pool_reloc_for_addr_mapping( while let Some((ins_iter, mut gpr_pool_relocs)) = ins_iters_with_gpr_state.pop() {
// func_address: u64, for (cur_addr, ins) in ins_iter {
// code: &[u8], if visited_ins_addrs.contains(&cur_addr) {
// relocations: &[ObjReloc], // Avoid getting stuck in an infinite loop when following looping branches.
// ) -> BTreeMap<u32, ObjReloc> { break;
// let mut visited_ins_addrs = BTreeSet::new(); }
// let mut pool_reloc_for_addr = BTreeMap::new(); visited_ins_addrs.insert(cur_addr);
// let mut ins_iters_with_gpr_state =
// vec![(InsIter::new(code, func_address as u32), BTreeMap::new())]; let simplified = ins.simplified();
// let mut gpr_state_at_bctr = BTreeMap::new();
// while let Some((ins_iter, mut gpr_pool_relocs)) = ins_iters_with_gpr_state.pop() { // First handle traversing the function's control flow.
// for (cur_addr, ins) in ins_iter { let mut branch_dest = None;
// if visited_ins_addrs.contains(&cur_addr) { for arg in simplified.args_iter() {
// // Avoid getting stuck in an infinite loop when following looping branches. if let Argument::BranchDest(dest) = arg {
// break; let dest = cur_addr.wrapping_add_signed(dest.0);
// } branch_dest = Some(dest);
// visited_ins_addrs.insert(cur_addr); break;
// }
// let simplified = ins.simplified(); }
// if let Some(branch_dest) = branch_dest {
// // First handle traversing the function's control flow. if branch_dest >= func_address as u32
// let mut branch_dest = None; && (branch_dest - func_address as u32) < code.len() as u32
// for arg in simplified.args_iter() { {
// if let Argument::BranchDest(dest) = arg { let dest_offset_into_func = branch_dest - func_address as u32;
// let dest = cur_addr.wrapping_add_signed(dest.0); let dest_code_slice = &code[dest_offset_into_func as usize..];
// branch_dest = Some(dest); match ins.op {
// break; Opcode::Bc => {
// } // Conditional branch.
// } // Add the branch destination to the queue to do later.
// if let Some(branch_dest) = branch_dest { ins_iters_with_gpr_state.push((
// if branch_dest >= func_address as u32 InsIter::new(dest_code_slice, branch_dest),
// && (branch_dest - func_address as u32) < code.len() as u32 gpr_pool_relocs.clone(),
// { ));
// let dest_offset_into_func = branch_dest - func_address as u32; // Then continue on with the current iterator.
// let dest_code_slice = &code[dest_offset_into_func as usize..]; }
// match ins.op { Opcode::B => {
// Opcode::Bc => { if simplified.mnemonic != "bl" {
// // Conditional branch. // Unconditional branch.
// // Add the branch destination to the queue to do later. // Add the branch destination to the queue.
// ins_iters_with_gpr_state.push(( ins_iters_with_gpr_state.push((
// InsIter::new(dest_code_slice, branch_dest), InsIter::new(dest_code_slice, branch_dest),
// gpr_pool_relocs.clone(), gpr_pool_relocs.clone(),
// )); ));
// // Then continue on with the current iterator. // Break out of the current iterator so we can do the newly added one.
// } break;
// Opcode::B => { }
// if simplified.mnemonic != "bl" { }
// // Unconditional branch. _ => unreachable!(),
// // Add the branch destination to the queue. }
// ins_iters_with_gpr_state.push(( }
// InsIter::new(dest_code_slice, branch_dest), }
// gpr_pool_relocs.clone(), if let Opcode::Bcctr = ins.op {
// )); if simplified.mnemonic == "bctr" {
// // Break out of the current iterator so we can do the newly added one. // Unconditional branch to count register.
// break; // Likely a jump table.
// } gpr_state_at_bctr.insert(cur_addr, gpr_pool_relocs.clone());
// } }
// _ => unreachable!(), }
// }
// } // Then handle keeping track of which GPR contains which pool relocation.
// } let reloc = relocations.iter().find(|r| (r.address as u32 & !3) == cur_addr);
// if let Opcode::Bcctr = ins.op { if let Some(reloc) = reloc {
// if simplified.mnemonic == "bctr" { // This instruction has a real relocation, so it may be a pool load we want to keep
// // Unconditional branch to count register. // track of.
// // Likely a jump table. let args = &simplified.args;
// gpr_state_at_bctr.insert(cur_addr, gpr_pool_relocs.clone()); match (ins.op, args[0], args[1], args[2]) {
// } (
// } // `lis` + `addi`
// Opcode::Addi,
// // Then handle keeping track of which GPR contains which pool relocation. Argument::GPR(addr_dst_gpr),
// let reloc = relocations.iter().find(|r| (r.address as u32 & !3) == cur_addr); Argument::GPR(_addr_src_gpr),
// if let Some(reloc) = reloc { Argument::Simm(_simm),
// // This instruction has a real relocation, so it may be a pool load we want to keep ) => {
// // track of. gpr_pool_relocs.insert(addr_dst_gpr.0, reloc.clone());
// let args = &simplified.args; }
// match (ins.op, args[0], args[1], args[2]) { (
// ( // `lis` + `ori`
// // `lis` + `addi` Opcode::Ori,
// Opcode::Addi, Argument::GPR(addr_dst_gpr),
// Argument::GPR(addr_dst_gpr), Argument::GPR(_addr_src_gpr),
// Argument::GPR(_addr_src_gpr), Argument::Uimm(_uimm),
// Argument::Simm(_simm), ) => {
// ) => { gpr_pool_relocs.insert(addr_dst_gpr.0, reloc.clone());
// gpr_pool_relocs.insert(addr_dst_gpr.0, reloc.clone()); }
// } (Opcode::B, _, _, _) => {
// ( if simplified.mnemonic == "bl" {
// // `lis` + `ori` // When encountering a function call, clear any active pool relocations from
// Opcode::Ori, // the volatile registers (r0, r3-r12), but not the nonvolatile registers.
// Argument::GPR(addr_dst_gpr), gpr_pool_relocs.remove(&0);
// Argument::GPR(_addr_src_gpr), for gpr in 3..12 {
// Argument::Uimm(_uimm), gpr_pool_relocs.remove(&gpr);
// ) => { }
// gpr_pool_relocs.insert(addr_dst_gpr.0, reloc.clone()); }
// } }
// (Opcode::B, _, _, _) => { _ => {
// if simplified.mnemonic == "bl" { clear_overwritten_gprs(ins, &mut gpr_pool_relocs);
// // When encountering a function call, clear any active pool relocations from }
// // the volatile registers (r0, r3-r12), but not the nonvolatile registers. }
// gpr_pool_relocs.remove(&0); } else if let Some(pool_ref) = get_pool_reference_for_inst(ins.op, &simplified) {
// for gpr in 3..12 { // This instruction doesn't have a real relocation, so it may be a reference to one of
// gpr_pool_relocs.remove(&gpr); // the already-loaded pools.
// } if let Some(pool_reloc) = gpr_pool_relocs.get(&pool_ref.addr_src_gpr.0) {
// } if let Some(fake_pool_reloc) =
// } make_fake_pool_reloc(pool_ref.addr_offset, cur_addr, pool_reloc, symbols)
// _ => { {
// clear_overwritten_gprs(ins, &mut gpr_pool_relocs); pool_reloc_for_addr.insert(cur_addr, fake_pool_reloc);
// } }
// } if let Some(addr_dst_gpr) = pool_ref.addr_dst_gpr {
// } else if let Some((offset, addr_src_gpr, addr_dst_gpr)) = // If the address of the pool relocation got copied into another register, we
// get_offset_and_addr_gpr_for_possible_pool_reference(ins.op, &simplified) // need to keep track of it in that register too as future instructions may
// { // reference the symbol indirectly via this new register, instead of the
// // This instruction doesn't have a real relocation, so it may be a reference to one of // register the symbol's address was originally loaded into.
// // the already-loaded pools. // For example, the start of the function might `lis` + `addi` the start of the
// if let Some(pool_reloc) = gpr_pool_relocs.get(&addr_src_gpr.0) { // ...data pool into r25, and then later the start of a loop will `addi` r25
// if let Some(fake_pool_reloc) = // with the offset within the .data section of an array variable into r21.
// make_fake_pool_reloc(offset, cur_addr, pool_reloc) // Then the body of the loop will `lwzx` one of the array elements from r21.
// { let mut new_reloc = pool_reloc.clone();
// pool_reloc_for_addr.insert(cur_addr, fake_pool_reloc); new_reloc.addend += pool_ref.addr_offset as i64;
// } gpr_pool_relocs.insert(addr_dst_gpr.0, new_reloc);
// if let Some(addr_dst_gpr) = addr_dst_gpr { } else {
// // If the address of the pool relocation got copied into another register, we clear_overwritten_gprs(ins, &mut gpr_pool_relocs);
// // need to keep track of it in that register too as future instructions may }
// // reference the symbol indirectly via this new register, instead of the } else {
// // register the symbol's address was originally loaded into. clear_overwritten_gprs(ins, &mut gpr_pool_relocs);
// // For example, the start of the function might `lis` + `addi` the start of the }
// // ...data pool into r25, and then later the start of a loop will `addi` r25 } else {
// // with the offset within the .data section of an array variable into r21. clear_overwritten_gprs(ins, &mut gpr_pool_relocs);
// // Then the body of the loop will `lwzx` one of the array elements from r21. }
// let mut new_reloc = pool_reloc.clone(); }
// new_reloc.addend += offset as i64;
// gpr_pool_relocs.insert(addr_dst_gpr.0, new_reloc); // Finally, if we're about to finish the outer loop and don't have any more control flow to
// } else { // follow, we check if there are any instruction addresses in this function that we missed.
// clear_overwritten_gprs(ins, &mut gpr_pool_relocs); // If so, and if there were any `bctr` instructions before those points in this function,
// } // then we try to traverse those missing spots as switch cases.
// } else { if ins_iters_with_gpr_state.is_empty() {
// clear_overwritten_gprs(ins, &mut gpr_pool_relocs); let unseen_addrs = (func_address as u32..func_address as u32 + code.len() as u32)
// } .step_by(4)
// } else { .filter(|addr| !visited_ins_addrs.contains(addr));
// clear_overwritten_gprs(ins, &mut gpr_pool_relocs); for unseen_addr in unseen_addrs {
// } let prev_bctr_gpr_state = gpr_state_at_bctr
// } .iter()
// .filter(|&(&addr, _)| addr < unseen_addr)
// // Finally, if we're about to finish the outer loop and don't have any more control flow to .min_by_key(|&(&addr, _)| addr)
// // follow, we check if there are any instruction addresses in this function that we missed. .map(|(_, gpr_state)| gpr_state);
// // If so, and if there were any `bctr` instructions before those points in this function, if let Some(gpr_pool_relocs) = prev_bctr_gpr_state {
// // then we try to traverse those missing spots as switch cases. let dest_offset_into_func = unseen_addr - func_address as u32;
// if ins_iters_with_gpr_state.is_empty() { let dest_code_slice = &code[dest_offset_into_func as usize..];
// let unseen_addrs = (func_address as u32..func_address as u32 + code.len() as u32) ins_iters_with_gpr_state.push((
// .step_by(4) InsIter::new(dest_code_slice, unseen_addr),
// .filter(|addr| !visited_ins_addrs.contains(addr)); gpr_pool_relocs.clone(),
// for unseen_addr in unseen_addrs { ));
// let prev_bctr_gpr_state = gpr_state_at_bctr break;
// .iter() }
// .filter(|(&addr, _)| addr < unseen_addr) }
// .min_by_key(|(&addr, _)| addr) }
// .map(|(_, gpr_state)| gpr_state); }
// if let Some(gpr_pool_relocs) = prev_bctr_gpr_state {
// let dest_offset_into_func = unseen_addr - func_address as u32; pool_reloc_for_addr.values().cloned().collect()
// let dest_code_slice = &code[dest_offset_into_func as usize..]; }
// ins_iters_with_gpr_state.push((
// InsIter::new(dest_code_slice, unseen_addr),
// gpr_pool_relocs.clone(),
// ));
// break;
// }
// }
// }
// }
//
// pool_reloc_for_addr
// }

View File

@@ -1,31 +1,50 @@
use alloc::{boxed::Box, string::String, vec::Vec}; use alloc::{boxed::Box, string::String, vec::Vec};
use anyhow::{anyhow, bail, Result}; use anyhow::{Result, anyhow, bail};
use iced_x86::{ use iced_x86::{
Decoder, DecoderOptions, DecoratorKind, FormatterOutput, FormatterTextKind, GasFormatter, Decoder, DecoderOptions, DecoratorKind, FormatterOutput, FormatterTextKind, GasFormatter,
Instruction, IntelFormatter, MasmFormatter, NasmFormatter, NumberKind, OpKind, Register, Instruction, IntelFormatter, MasmFormatter, NasmFormatter, NumberKind, OpKind, Register,
}; };
use object::{pe, Endian as _, Object as _, ObjectSection as _}; use object::{Endian as _, Object as _, ObjectSection as _, pe};
use crate::{ use crate::{
arch::Arch, arch::Arch,
diff::{display::InstructionPart, DiffObjConfig, X86Formatter}, diff::{DiffObjConfig, X86Formatter, display::InstructionPart},
obj::{InstructionRef, RelocationFlags, ResolvedInstructionRef, ScannedInstruction}, obj::{InstructionRef, RelocationFlags, ResolvedInstructionRef, ScannedInstruction},
}; };
#[derive(Debug)] #[derive(Debug)]
pub struct ArchX86 { pub struct ArchX86 {
bits: u32, arch: Architecture,
endianness: object::Endianness, endianness: object::Endianness,
} }
#[derive(Debug)]
enum Architecture {
X86,
X86_64,
}
impl ArchX86 { impl ArchX86 {
pub fn new(object: &object::File) -> Result<Self> { pub fn new(object: &object::File) -> Result<Self> {
Ok(Self { bits: if object.is_64() { 64 } else { 32 }, endianness: object.endianness() }) let arch = match object.architecture() {
object::Architecture::I386 => Architecture::X86,
object::Architecture::X86_64 => Architecture::X86_64,
_ => bail!("Unsupported architecture for ArchX86: {:?}", object.architecture()),
};
Ok(Self { arch, endianness: object.endianness() })
} }
fn decoder<'a>(&self, code: &'a [u8], address: u64) -> Decoder<'a> { fn decoder<'a>(&self, code: &'a [u8], address: u64) -> Decoder<'a> {
Decoder::with_ip(self.bits, code, address, DecoderOptions::NONE) Decoder::with_ip(
match self.arch {
Architecture::X86 => 32,
Architecture::X86_64 => 64,
},
code,
address,
DecoderOptions::NONE,
)
} }
fn formatter(&self, diff_config: &DiffObjConfig) -> Box<dyn iced_x86::Formatter> { fn formatter(&self, diff_config: &DiffObjConfig) -> Box<dyn iced_x86::Formatter> {
@@ -38,6 +57,27 @@ impl ArchX86 {
formatter.options_mut().set_space_after_operand_separator(diff_config.space_between_args); formatter.options_mut().set_space_after_operand_separator(diff_config.space_between_args);
formatter formatter
} }
fn reloc_size(&self, flags: RelocationFlags) -> Option<usize> {
match self.arch {
Architecture::X86 => match flags {
RelocationFlags::Coff(typ) => match typ {
pe::IMAGE_REL_I386_DIR16 | pe::IMAGE_REL_I386_REL16 => Some(2),
pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32 => Some(4),
_ => None,
},
_ => None,
},
Architecture::X86_64 => match flags {
RelocationFlags::Coff(typ) => match typ {
pe::IMAGE_REL_AMD64_ADDR32NB | pe::IMAGE_REL_AMD64_REL32 => Some(4),
pe::IMAGE_REL_AMD64_ADDR64 => Some(8),
_ => None,
},
_ => None,
},
}
}
} }
impl Arch for ArchX86 { impl Arch for ArchX86 {
@@ -88,10 +128,9 @@ impl Arch for ArchX86 {
// memory operand. // memory operand.
let mut reloc_replace = None; let mut reloc_replace = None;
if let Some(reloc) = resolved.relocation { if let Some(reloc) = resolved.relocation {
const PLACEHOLDER: u64 = 0x7BDE3E7D; // chosen by fair dice roll. const PLACEHOLDER: u64 = 0x7BDE3E7D; // chosen by fair dice roll. guaranteed to be random.
// guaranteed to be random.
let reloc_offset = reloc.relocation.address - resolved.ins_ref.address; let reloc_offset = reloc.relocation.address - resolved.ins_ref.address;
let reloc_size = reloc_size(reloc.relocation.flags).unwrap_or(usize::MAX); let reloc_size = self.reloc_size(reloc.relocation.flags).unwrap_or(usize::MAX);
let offsets = decoder.get_constant_offsets(&instruction); let offsets = decoder.get_constant_offsets(&instruction);
if reloc_offset == offsets.displacement_offset() as u64 if reloc_offset == offsets.displacement_offset() as u64
&& reloc_size == offsets.displacement_size() && reloc_size == offsets.displacement_size()
@@ -148,12 +187,28 @@ impl Arch for ArchX86 {
_relocation: &object::Relocation, _relocation: &object::Relocation,
flags: RelocationFlags, flags: RelocationFlags,
) -> Result<i64> { ) -> Result<i64> {
match flags { match self.arch {
RelocationFlags::Coff(pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32) => { Architecture::X86 => match flags {
let data = section.data()?[address as usize..address as usize + 4].try_into()?; RelocationFlags::Coff(pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32) => {
Ok(self.endianness.read_i32_bytes(data) as i64) let data =
} section.data()?[address as usize..address as usize + 4].try_into()?;
flags => bail!("Unsupported x86 implicit relocation {flags:?}"), Ok(self.endianness.read_i32_bytes(data) as i64)
}
flags => bail!("Unsupported x86 implicit relocation {flags:?}"),
},
Architecture::X86_64 => match flags {
RelocationFlags::Coff(pe::IMAGE_REL_AMD64_ADDR32NB | pe::IMAGE_REL_AMD64_REL32) => {
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) => {
let data =
section.data()?[address as usize..address as usize + 8].try_into()?;
Ok(self.endianness.read_i64_bytes(data))
}
flags => bail!("Unsupported x86-64 implicit relocation {flags:?}"),
},
} }
} }
@@ -168,27 +223,29 @@ impl Arch for ArchX86 {
} }
fn reloc_name(&self, flags: RelocationFlags) -> Option<&'static str> { fn reloc_name(&self, flags: RelocationFlags) -> Option<&'static str> {
match flags { match self.arch {
RelocationFlags::Coff(typ) => match typ { Architecture::X86 => match flags {
pe::IMAGE_REL_I386_DIR32 => Some("IMAGE_REL_I386_DIR32"), RelocationFlags::Coff(typ) => match typ {
pe::IMAGE_REL_I386_REL32 => Some("IMAGE_REL_I386_REL32"), pe::IMAGE_REL_I386_DIR32 => Some("IMAGE_REL_I386_DIR32"),
pe::IMAGE_REL_I386_REL32 => Some("IMAGE_REL_I386_REL32"),
_ => None,
},
_ => None,
},
Architecture::X86_64 => match flags {
RelocationFlags::Coff(typ) => match typ {
pe::IMAGE_REL_AMD64_ADDR64 => Some("IMAGE_REL_AMD64_ADDR64"),
pe::IMAGE_REL_AMD64_ADDR32NB => Some("IMAGE_REL_AMD64_ADDR32NB"),
pe::IMAGE_REL_AMD64_REL32 => Some("IMAGE_REL_AMD64_REL32"),
_ => None,
},
_ => None, _ => None,
}, },
_ => None,
} }
} }
fn data_reloc_size(&self, flags: RelocationFlags) -> usize { reloc_size(flags).unwrap_or(1) } fn data_reloc_size(&self, flags: RelocationFlags) -> usize {
} self.reloc_size(flags).unwrap_or(1)
fn reloc_size(flags: RelocationFlags) -> Option<usize> {
match flags {
RelocationFlags::Coff(typ) => match typ {
pe::IMAGE_REL_I386_DIR16 | pe::IMAGE_REL_I386_REL16 => Some(2),
pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32 => Some(4),
_ => None,
},
_ => None,
} }
} }
@@ -343,7 +400,7 @@ mod test {
#[test] #[test]
fn test_scan_instructions() { fn test_scan_instructions() {
let arch = ArchX86 { bits: 32, endianness: object::Endianness::Little }; let arch = ArchX86 { arch: Architecture::X86, endianness: object::Endianness::Little };
let code = [ let code = [
0xc7, 0x85, 0x68, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x8b, 0x04, 0x85, 0x00, 0xc7, 0x85, 0x68, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x8b, 0x04, 0x85, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -362,7 +419,7 @@ mod test {
#[test] #[test]
fn test_process_instruction() { fn test_process_instruction() {
let arch = ArchX86 { bits: 32, endianness: object::Endianness::Little }; let arch = ArchX86 { arch: Architecture::X86, endianness: object::Endianness::Little };
let code = [0xc7, 0x85, 0x68, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00]; let code = [0xc7, 0x85, 0x68, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00];
let opcode = iced_x86::Mnemonic::Mov as u16; let opcode = iced_x86::Mnemonic::Mov as u16;
let mut parts = Vec::new(); let mut parts = Vec::new();
@@ -398,7 +455,7 @@ mod test {
#[test] #[test]
fn test_process_instruction_with_reloc_1() { fn test_process_instruction_with_reloc_1() {
let arch = ArchX86 { bits: 32, endianness: object::Endianness::Little }; let arch = ArchX86 { arch: Architecture::X86, endianness: object::Endianness::Little };
let code = [0xc7, 0x85, 0x68, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00]; let code = [0xc7, 0x85, 0x68, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00];
let opcode = iced_x86::Mnemonic::Mov as u16; let opcode = iced_x86::Mnemonic::Mov as u16;
let mut parts = Vec::new(); let mut parts = Vec::new();
@@ -443,7 +500,7 @@ mod test {
#[test] #[test]
fn test_process_instruction_with_reloc_2() { fn test_process_instruction_with_reloc_2() {
let arch = ArchX86 { bits: 32, endianness: object::Endianness::Little }; let arch = ArchX86 { arch: Architecture::X86, endianness: object::Endianness::Little };
let code = [0x8b, 0x04, 0x85, 0x00, 0x00, 0x00, 0x00]; let code = [0x8b, 0x04, 0x85, 0x00, 0x00, 0x00, 0x00];
let opcode = iced_x86::Mnemonic::Mov as u16; let opcode = iced_x86::Mnemonic::Mov as u16;
let mut parts = Vec::new(); let mut parts = Vec::new();
@@ -486,7 +543,7 @@ mod test {
#[test] #[test]
fn test_process_instruction_with_reloc_3() { fn test_process_instruction_with_reloc_3() {
let arch = ArchX86 { bits: 32, endianness: object::Endianness::Little }; let arch = ArchX86 { arch: Architecture::X86, endianness: object::Endianness::Little };
let code = [0xe8, 0x00, 0x00, 0x00, 0x00]; let code = [0xe8, 0x00, 0x00, 0x00, 0x00];
let opcode = iced_x86::Mnemonic::Call as u16; let opcode = iced_x86::Mnemonic::Call as u16;
let mut parts = Vec::new(); let mut parts = Vec::new();

View File

@@ -7,7 +7,7 @@ use alloc::{
}; };
use core::ops::AddAssign; use core::ops::AddAssign;
use anyhow::{bail, Result}; use anyhow::{Result, bail};
use prost::Message; use prost::Message;
// Protobuf report types // Protobuf report types
@@ -441,11 +441,7 @@ impl From<LegacyReportItem> for ReportItem {
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
fn serialize_hex<S>(x: &Option<u64>, s: S) -> Result<S::Ok, S::Error> fn serialize_hex<S>(x: &Option<u64>, s: S) -> Result<S::Ok, S::Error>
where S: serde::Serializer { where S: serde::Serializer {
if let Some(x) = x { if let Some(x) = x { s.serialize_str(&format!("{:#x}", x)) } else { s.serialize_none() }
s.serialize_str(&format!("{:#x}", x))
} else {
s.serialize_none()
}
} }
#[cfg(feature = "serde")] #[cfg(feature = "serde")]

View File

@@ -2,8 +2,8 @@ use std::{
fs, fs,
path::{Path, PathBuf}, path::{Path, PathBuf},
sync::{ sync::{
atomic::{AtomicBool, Ordering},
Arc, Arc,
atomic::{AtomicBool, Ordering},
}, },
task::Waker, task::Waker,
time::Duration, time::Duration,
@@ -11,7 +11,7 @@ use std::{
use globset::GlobSet; use globset::GlobSet;
use notify::RecursiveMode; use notify::RecursiveMode;
use notify_debouncer_full::{new_debouncer_opt, DebounceEventResult}; use notify_debouncer_full::{DebounceEventResult, new_debouncer_opt};
pub type Watcher = notify_debouncer_full::Debouncer< pub type Watcher = notify_debouncer_full::Debouncer<
notify::RecommendedWatcher, notify::RecommendedWatcher,

View File

@@ -6,7 +6,7 @@ use alloc::{
vec::Vec, vec::Vec,
}; };
use anyhow::{anyhow, Context, Result}; use anyhow::{Context, Result, anyhow};
use globset::{Glob, GlobSet, GlobSetBuilder}; use globset::{Glob, GlobSet, GlobSetBuilder};
use path::unix_path_serde_option; use path::unix_path_serde_option;
use typed_path::Utf8UnixPathBuf; use typed_path::Utf8UnixPathBuf;

View File

@@ -31,11 +31,7 @@ pub mod unix_path_serde_option {
pub fn serialize<S>(path: &Option<Utf8UnixPathBuf>, s: S) -> Result<S::Ok, S::Error> pub fn serialize<S>(path: &Option<Utf8UnixPathBuf>, s: S) -> Result<S::Ok, S::Error>
where S: Serializer { where S: Serializer {
if let Some(path) = path { if let Some(path) = path { s.serialize_some(path.as_str()) } else { s.serialize_none() }
s.serialize_some(path.as_str())
} else {
s.serialize_none()
}
} }
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Utf8UnixPathBuf>, D::Error> pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Utf8UnixPathBuf>, D::Error>
@@ -51,11 +47,7 @@ pub mod platform_path_serde_option {
pub fn serialize<S>(path: &Option<Utf8PlatformPathBuf>, s: S) -> Result<S::Ok, S::Error> pub fn serialize<S>(path: &Option<Utf8PlatformPathBuf>, s: S) -> Result<S::Ok, S::Error>
where S: Serializer { where S: Serializer {
if let Some(path) = path { if let Some(path) = path { s.serialize_some(path.as_str()) } else { s.serialize_none() }
s.serialize_some(path.as_str())
} else {
s.serialize_none()
}
} }
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Utf8PlatformPathBuf>, D::Error> pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Utf8PlatformPathBuf>, D::Error>

View File

@@ -1,19 +1,20 @@
use alloc::{ use alloc::{
collections::{btree_map, BTreeMap}, collections::{BTreeMap, btree_map},
string::{String, ToString}, string::{String, ToString},
vec, vec,
vec::Vec, vec::Vec,
}; };
use anyhow::{anyhow, ensure, Context, Result}; use anyhow::{Context, Result, anyhow, ensure};
use super::{ use super::{
DiffObjConfig, FunctionRelocDiffs, InstructionArgDiffIndex, InstructionBranchFrom, DiffObjConfig, FunctionRelocDiffs, InstructionArgDiffIndex, InstructionBranchFrom,
InstructionBranchTo, InstructionDiffKind, InstructionDiffRow, SymbolDiff, InstructionBranchTo, InstructionDiffKind, InstructionDiffRow, SymbolDiff,
display::display_ins_data_literals,
}; };
use crate::obj::{ use crate::obj::{
InstructionArg, InstructionArgValue, InstructionRef, Object, ResolvedRelocation, InstructionArg, InstructionArgValue, InstructionRef, Object, ResolvedInstructionRef,
ScannedInstruction, SymbolFlag, SymbolKind, ResolvedRelocation, ScannedInstruction, SymbolFlag, SymbolKind,
}; };
pub fn no_diff_code( pub fn no_diff_code(
@@ -90,7 +91,7 @@ pub fn diff_code(
left_section_idx, left_section_idx,
diff_config, diff_config,
)?; )?;
let right_ops = left_obj.arch.scan_instructions( let right_ops = right_obj.arch.scan_instructions(
right_symbol.address, right_symbol.address,
right_data, right_data,
right_section_idx, right_section_idx,
@@ -291,12 +292,12 @@ pub(crate) fn section_name_eq(
fn reloc_eq( fn reloc_eq(
left_obj: &Object, left_obj: &Object,
right_obj: &Object, right_obj: &Object,
left_reloc: Option<ResolvedRelocation>, left_ins: ResolvedInstructionRef,
right_reloc: Option<ResolvedRelocation>, right_ins: ResolvedInstructionRef,
diff_config: &DiffObjConfig, diff_config: &DiffObjConfig,
) -> bool { ) -> bool {
let relax_reloc_diffs = diff_config.function_reloc_diffs == FunctionRelocDiffs::None; let relax_reloc_diffs = diff_config.function_reloc_diffs == FunctionRelocDiffs::None;
let (left_reloc, right_reloc) = match (left_reloc, right_reloc) { let (left_reloc, right_reloc) = match (left_ins.relocation, right_ins.relocation) {
(Some(left_reloc), Some(right_reloc)) => (left_reloc, right_reloc), (Some(left_reloc), Some(right_reloc)) => (left_reloc, right_reloc),
// If relocations are relaxed, match if left is missing a reloc // If relocations are relaxed, match if left is missing a reloc
(None, Some(_)) => return relax_reloc_diffs, (None, Some(_)) => return relax_reloc_diffs,
@@ -319,13 +320,10 @@ fn reloc_eq(
&& (diff_config.function_reloc_diffs == FunctionRelocDiffs::DataValue && (diff_config.function_reloc_diffs == FunctionRelocDiffs::DataValue
|| symbol_name_addend_matches || symbol_name_addend_matches
|| address_eq(left_reloc, right_reloc)) || address_eq(left_reloc, right_reloc))
&& ( && (diff_config.function_reloc_diffs == FunctionRelocDiffs::NameAddress
diff_config.function_reloc_diffs == FunctionRelocDiffs::NameAddress || left_reloc.symbol.kind != SymbolKind::Object
|| left_reloc.symbol.kind != SymbolKind::Object || display_ins_data_literals(left_obj, left_ins)
// TODO == display_ins_data_literals(right_obj, right_ins))
// || left_obj.arch.display_ins_data_labels(left_ins)
// == left_obj.arch.display_ins_data_labels(right_ins))
)
} }
(Some(_), None) => false, (Some(_), None) => false,
(None, Some(_)) => { (None, Some(_)) => {
@@ -343,8 +341,8 @@ fn arg_eq(
right_row: &InstructionDiffRow, right_row: &InstructionDiffRow,
left_arg: &InstructionArg, left_arg: &InstructionArg,
right_arg: &InstructionArg, right_arg: &InstructionArg,
left_reloc: Option<ResolvedRelocation>, left_ins: ResolvedInstructionRef,
right_reloc: Option<ResolvedRelocation>, right_ins: ResolvedInstructionRef,
diff_config: &DiffObjConfig, diff_config: &DiffObjConfig,
) -> bool { ) -> bool {
match left_arg { match left_arg {
@@ -357,7 +355,7 @@ fn arg_eq(
}, },
InstructionArg::Reloc => { InstructionArg::Reloc => {
matches!(right_arg, InstructionArg::Reloc) matches!(right_arg, InstructionArg::Reloc)
&& reloc_eq(left_obj, right_obj, left_reloc, right_reloc, diff_config) && reloc_eq(left_obj, right_obj, left_ins, right_ins, diff_config)
} }
InstructionArg::BranchDest(_) => match right_arg { InstructionArg::BranchDest(_) => match right_arg {
// Compare dest instruction idx after diffing // Compare dest instruction idx after diffing
@@ -434,10 +432,12 @@ fn diff_instruction(
.resolve_instruction_ref(right_symbol_idx, r) .resolve_instruction_ref(right_symbol_idx, r)
.context("Failed to resolve right instruction")?; .context("Failed to resolve right instruction")?;
if left_resolved.code != right_resolved.code { if left_resolved.code != right_resolved.code
// If data doesn't match, process instructions and compare args || !reloc_eq(left_obj, right_obj, left_resolved, right_resolved, diff_config)
{
// If either the raw code bytes or relocations don't match, process instructions and compare args
let left_ins = left_obj.arch.process_instruction(left_resolved, diff_config)?; let left_ins = left_obj.arch.process_instruction(left_resolved, diff_config)?;
let right_ins = left_obj.arch.process_instruction(right_resolved, diff_config)?; let right_ins = right_obj.arch.process_instruction(right_resolved, diff_config)?;
if left_ins.args.len() != right_ins.args.len() { if left_ins.args.len() != right_ins.args.len() {
state.diff_score += PENALTY_REPLACE; state.diff_score += PENALTY_REPLACE;
return Ok(InstructionDiffResult::new(InstructionDiffKind::Replace)); return Ok(InstructionDiffResult::new(InstructionDiffKind::Replace));
@@ -455,8 +455,8 @@ fn diff_instruction(
right_row, right_row,
a, a,
b, b,
left_resolved.relocation, left_resolved,
right_resolved.relocation, right_resolved,
diff_config, diff_config,
) { ) {
result.left_args_diff.push(InstructionArgDiffIndex::NONE); result.left_args_diff.push(InstructionArgDiffIndex::NONE);
@@ -500,32 +500,5 @@ fn diff_instruction(
return Ok(result); return Ok(result);
} }
// Compare relocations
if !reloc_eq(
left_obj,
right_obj,
left_resolved.relocation,
right_resolved.relocation,
diff_config,
) {
state.diff_score += PENALTY_REG_DIFF;
// TODO add relocation diff to args
return Ok(InstructionDiffResult::new(InstructionDiffKind::ArgMismatch));
}
Ok(InstructionDiffResult::new(InstructionDiffKind::None)) Ok(InstructionDiffResult::new(InstructionDiffKind::None))
} }
// TODO
// fn find_symbol_matching_fake_symbol_in_sections(
// fake_symbol: &ObjSymbol,
// sections: &[ObjSection],
// ) -> Option<ObjSymbol> {
// let orig_section_index = fake_symbol.orig_section_index?;
// let section = sections.iter().find(|s| s.orig_index == orig_section_index)?;
// let real_symbol = section
// .symbols
// .iter()
// .find(|s| s.size > 0 && (s.address..s.address + s.size).contains(&fake_symbol.address))?;
// Some(real_symbol.clone())
// }

View File

@@ -1,14 +1,14 @@
use alloc::{vec, vec::Vec}; use alloc::{vec, vec::Vec};
use core::{cmp::Ordering, ops::Range}; use core::{cmp::Ordering, ops::Range};
use anyhow::{anyhow, Result}; use anyhow::{Result, anyhow};
use similar::{capture_diff_slices, get_diff_ratio, Algorithm}; use similar::{Algorithm, capture_diff_slices, get_diff_ratio};
use super::{ use super::{
code::{address_eq, section_name_eq},
DataDiff, DataDiffKind, DataRelocationDiff, ObjectDiff, SectionDiff, SymbolDiff, DataDiff, DataDiffKind, DataRelocationDiff, ObjectDiff, SectionDiff, SymbolDiff,
code::{address_eq, section_name_eq},
}; };
use crate::obj::{Object, Relocation, ResolvedRelocation, SymbolFlag, SymbolKind}; use crate::obj::{Object, Relocation, ResolvedRelocation, Symbol, SymbolFlag, SymbolKind};
pub fn diff_bss_symbol( pub fn diff_bss_symbol(
left_obj: &Object, left_obj: &Object,
@@ -63,19 +63,15 @@ fn reloc_eq(
} }
#[inline] #[inline]
fn resolve_relocation<'obj>( pub fn resolve_relocation<'obj>(
obj: &'obj Object, symbols: &'obj [Symbol],
reloc: &'obj Relocation, reloc: &'obj Relocation,
) -> ResolvedRelocation<'obj> { ) -> ResolvedRelocation<'obj> {
let symbol = &obj.symbols[reloc.target_symbol]; let symbol = &symbols[reloc.target_symbol];
ResolvedRelocation { relocation: reloc, symbol } ResolvedRelocation { relocation: reloc, symbol }
} }
/// Compares relocations contained with a certain data range. /// Compares relocations contained with a certain data range.
/// The DataDiffKind for each diff will either be `None`` (if the relocation matches),
/// or `Replace` (if a relocation was changed, added, or removed).
/// `Insert` and `Delete` are not used when a relocation is added or removed to avoid confusing diffs
/// where it looks like the bytes themselves were changed but actually only the relocations changed.
fn diff_data_relocs_for_range<'left, 'right>( fn diff_data_relocs_for_range<'left, 'right>(
left_obj: &'left Object, left_obj: &'left Object,
right_obj: &'right Object, right_obj: &'right Object,
@@ -92,7 +88,7 @@ fn diff_data_relocs_for_range<'left, 'right>(
continue; continue;
} }
let left_offset = left_reloc.address as usize - left_range.start; let left_offset = left_reloc.address as usize - left_range.start;
let left_reloc = resolve_relocation(left_obj, left_reloc); let left_reloc = resolve_relocation(&left_obj.symbols, left_reloc);
let Some(right_reloc) = right_section.relocations.iter().find(|r| { let Some(right_reloc) = right_section.relocations.iter().find(|r| {
if !right_range.contains(&(r.address as usize)) { if !right_range.contains(&(r.address as usize)) {
return false; return false;
@@ -103,7 +99,7 @@ fn diff_data_relocs_for_range<'left, 'right>(
diffs.push((DataDiffKind::Delete, Some(left_reloc), None)); diffs.push((DataDiffKind::Delete, Some(left_reloc), None));
continue; continue;
}; };
let right_reloc = resolve_relocation(right_obj, right_reloc); let right_reloc = resolve_relocation(&right_obj.symbols, right_reloc);
if reloc_eq(left_obj, right_obj, left_reloc, right_reloc) { if reloc_eq(left_obj, right_obj, left_reloc, right_reloc) {
diffs.push((DataDiffKind::None, Some(left_reloc), Some(right_reloc))); diffs.push((DataDiffKind::None, Some(left_reloc), Some(right_reloc)));
} else { } else {
@@ -115,7 +111,7 @@ fn diff_data_relocs_for_range<'left, 'right>(
continue; continue;
} }
let right_offset = right_reloc.address as usize - right_range.start; let right_offset = right_reloc.address as usize - right_range.start;
let right_reloc = resolve_relocation(right_obj, right_reloc); let right_reloc = resolve_relocation(&right_obj.symbols, right_reloc);
let Some(_) = left_section.relocations.iter().find(|r| { let Some(_) = left_section.relocations.iter().find(|r| {
if !left_range.contains(&(r.address as usize)) { if !left_range.contains(&(r.address as usize)) {
return false; return false;
@@ -254,13 +250,21 @@ pub fn diff_data_section(
let len = left_obj.arch.data_reloc_size(left_reloc.relocation.flags); let len = left_obj.arch.data_reloc_size(left_reloc.relocation.flags);
let range = left_reloc.relocation.address as usize let range = left_reloc.relocation.address as usize
..left_reloc.relocation.address as usize + len; ..left_reloc.relocation.address as usize + len;
left_reloc_diffs.push(DataRelocationDiff { kind: diff_kind, range }); left_reloc_diffs.push(DataRelocationDiff {
reloc: left_reloc.relocation.clone(),
kind: diff_kind,
range,
});
} }
if let Some(right_reloc) = right_reloc { if let Some(right_reloc) = right_reloc {
let len = right_obj.arch.data_reloc_size(right_reloc.relocation.flags); let len = right_obj.arch.data_reloc_size(right_reloc.relocation.flags);
let range = right_reloc.relocation.address as usize let range = right_reloc.relocation.address as usize
..right_reloc.relocation.address as usize + len; ..right_reloc.relocation.address as usize + len;
right_reloc_diffs.push(DataRelocationDiff { kind: diff_kind, range }); right_reloc_diffs.push(DataRelocationDiff {
reloc: right_reloc.relocation.clone(),
kind: diff_kind,
range,
});
} }
} }
@@ -427,11 +431,7 @@ pub fn diff_generic_section(
.fold((0.0, 0.0), |(matched, total), (s, d)| { .fold((0.0, 0.0), |(matched, total), (s, d)| {
(matched + d.match_percent.unwrap_or(0.0) * s.size as f32, total + s.size as f32) (matched + d.match_percent.unwrap_or(0.0) * s.size as f32, total + s.size as f32)
}); });
if total == 0.0 { if total == 0.0 { 100.0 } else { matched / total }
100.0
} else {
matched / total
}
}; };
Ok(( 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![] },

View File

@@ -15,7 +15,7 @@ use crate::{
diff::{DiffObjConfig, InstructionDiffKind, InstructionDiffRow, ObjectDiff, SymbolDiff}, diff::{DiffObjConfig, InstructionDiffKind, InstructionDiffRow, ObjectDiff, SymbolDiff},
obj::{ obj::{
InstructionArg, InstructionArgValue, Object, ParsedInstruction, ResolvedInstructionRef, InstructionArg, InstructionArgValue, Object, ParsedInstruction, ResolvedInstructionRef,
SectionFlag, SectionKind, Symbol, SymbolFlag, SymbolKind, ResolvedRelocation, SectionFlag, SectionKind, Symbol, SymbolFlag, SymbolKind,
}, },
}; };
@@ -346,7 +346,7 @@ pub fn symbol_context(obj: &Object, symbol_index: usize) -> Vec<ContextItem> {
if symbol.section.is_some() { if symbol.section.is_some() {
if let Some(address) = symbol.virtual_address { if let Some(address) = symbol.virtual_address {
out.push(ContextItem::Copy { out.push(ContextItem::Copy {
value: format!("{:#x}", address), value: format!("{:x}", address),
label: Some("virtual address".to_string()), label: Some("virtual address".to_string()),
}); });
} }
@@ -409,7 +409,7 @@ pub fn symbol_hover(obj: &Object, symbol_index: usize, addend: i64) -> Vec<Hover
if let Some(address) = symbol.virtual_address { if let Some(address) = symbol.virtual_address {
out.push(HoverItem::Text { out.push(HoverItem::Text {
label: "Virtual address".into(), label: "Virtual address".into(),
value: format!("{:#x}", address), value: format!("{:x}", address),
color: HoverItemColor::Special, color: HoverItemColor::Special,
}); });
} }
@@ -424,6 +424,44 @@ pub fn symbol_hover(obj: &Object, symbol_index: usize, addend: i64) -> Vec<Hover
out out
} }
pub fn relocation_context(
obj: &Object,
reloc: ResolvedRelocation,
ins: Option<ResolvedInstructionRef>,
) -> Vec<ContextItem> {
let mut out = Vec::new();
out.append(&mut symbol_context(obj, reloc.relocation.target_symbol));
if let Some(ins) = ins {
let literals = display_ins_data_literals(obj, ins);
if !literals.is_empty() {
out.push(ContextItem::Separator);
for literal in literals {
out.push(ContextItem::Copy { value: literal, label: None });
}
}
}
out
}
pub fn relocation_hover(obj: &Object, reloc: ResolvedRelocation) -> Vec<HoverItem> {
let mut out = Vec::new();
if let Some(name) = obj.arch.reloc_name(reloc.relocation.flags) {
out.push(HoverItem::Text {
label: "Relocation".into(),
value: name.to_string(),
color: HoverItemColor::Normal,
});
} else {
out.push(HoverItem::Text {
label: "Relocation".into(),
value: format!("<{:?}>", reloc.relocation.flags),
color: HoverItemColor::Normal,
});
}
out.append(&mut symbol_hover(obj, reloc.relocation.target_symbol, reloc.relocation.addend));
out
}
pub fn instruction_context( pub fn instruction_context(
obj: &Object, obj: &Object,
resolved: ResolvedInstructionRef, resolved: ResolvedInstructionRef,
@@ -458,11 +496,8 @@ pub fn instruction_context(
} }
} }
if let Some(reloc) = resolved.relocation { if let Some(reloc) = resolved.relocation {
for literal in display_ins_data_literals(obj, resolved) {
out.push(ContextItem::Copy { value: literal, label: None });
}
out.push(ContextItem::Separator); out.push(ContextItem::Separator);
out.append(&mut symbol_context(obj, reloc.relocation.target_symbol)); out.append(&mut relocation_context(obj, reloc, Some(resolved)));
} }
out out
} }
@@ -483,7 +518,7 @@ pub fn instruction_hover(
let offset = resolved.ins_ref.address - resolved.symbol.address; let offset = resolved.ins_ref.address - resolved.symbol.address;
out.push(HoverItem::Text { out.push(HoverItem::Text {
label: "Virtual address".into(), label: "Virtual address".into(),
value: format!("{:#x}", virtual_address + offset), value: format!("{:x}", virtual_address + offset),
color: HoverItemColor::Special, color: HoverItemColor::Special,
}); });
} }
@@ -509,21 +544,21 @@ pub fn instruction_hover(
} }
} }
if let Some(reloc) = resolved.relocation { if let Some(reloc) = resolved.relocation {
if let Some(name) = obj.arch.reloc_name(reloc.relocation.flags) {
out.push(HoverItem::Text {
label: "Relocation type".into(),
value: name.to_string(),
color: HoverItemColor::Normal,
});
} else {
out.push(HoverItem::Text {
label: "Relocation type".into(),
value: format!("<{:?}>", reloc.relocation.flags),
color: HoverItemColor::Normal,
});
}
out.push(HoverItem::Separator); out.push(HoverItem::Separator);
out.append(&mut symbol_hover(obj, reloc.relocation.target_symbol, reloc.relocation.addend)); out.append(&mut relocation_hover(obj, reloc));
if let Some(ty) = obj.arch.guess_data_type(resolved) {
let literals = display_ins_data_literals(obj, resolved);
if !literals.is_empty() {
out.push(HoverItem::Separator);
for literal in literals {
out.push(HoverItem::Text {
label: format!("{}", ty),
value: literal,
color: HoverItemColor::Normal,
});
}
}
}
} }
out out
} }

View File

@@ -16,7 +16,7 @@ use crate::{
diff_generic_section, diff_generic_section,
}, },
}, },
obj::{InstructionRef, Object, SectionKind, Symbol, SymbolFlag}, obj::{InstructionRef, Object, Relocation, SectionKind, Symbol, SymbolFlag},
}; };
pub mod code; pub mod code;
@@ -26,13 +26,7 @@ pub mod display;
include!(concat!(env!("OUT_DIR"), "/config.gen.rs")); include!(concat!(env!("OUT_DIR"), "/config.gen.rs"));
impl DiffObjConfig { impl DiffObjConfig {
pub fn separator(&self) -> &'static str { pub fn separator(&self) -> &'static str { if self.space_between_args { ", " } else { "," } }
if self.space_between_args {
", "
} else {
","
}
}
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -93,6 +87,7 @@ pub struct DataDiff {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct DataRelocationDiff { pub struct DataRelocationDiff {
pub reloc: Relocation,
pub kind: DataDiffKind, pub kind: DataDiffKind,
pub range: Range<usize>, pub range: Range<usize>,
} }

View File

@@ -6,7 +6,7 @@ use self_update::{
update::{Release, ReleaseUpdate}, update::{Release, ReleaseUpdate},
}; };
use crate::jobs::{start_job, update_status, Job, JobContext, JobResult, JobState}; use crate::jobs::{Job, JobContext, JobResult, JobState, start_job, update_status};
pub struct CheckUpdateConfig { pub struct CheckUpdateConfig {
pub build_updater: fn() -> Result<Box<dyn ReleaseUpdate>>, pub build_updater: fn() -> Result<Box<dyn ReleaseUpdate>>,

View File

@@ -1,11 +1,11 @@
use std::{fs, sync::mpsc::Receiver, task::Waker}; use std::{fs, sync::mpsc::Receiver, task::Waker};
use anyhow::{anyhow, bail, Context, Result}; use anyhow::{Context, Result, anyhow, bail};
use typed_path::{Utf8PlatformPathBuf, Utf8UnixPathBuf}; use typed_path::{Utf8PlatformPathBuf, Utf8UnixPathBuf};
use crate::{ use crate::{
build::{run_make, BuildConfig, BuildStatus}, build::{BuildConfig, BuildStatus, run_make},
jobs::{start_job, update_status, Job, JobContext, JobResult, JobState}, jobs::{Job, JobContext, JobResult, JobState, start_job, update_status},
}; };
#[derive(Debug, Clone)] #[derive(Debug, Clone)]

View File

@@ -1,8 +1,8 @@
use std::{ use std::{
sync::{ sync::{
Arc, RwLock,
atomic::{AtomicUsize, Ordering}, atomic::{AtomicUsize, Ordering},
mpsc::{Receiver, Sender, TryRecvError}, mpsc::{Receiver, Sender, TryRecvError},
Arc, RwLock,
}, },
task::Waker, task::Waker,
thread::JoinHandle, thread::JoinHandle,

View File

@@ -1,14 +1,14 @@
use std::{sync::mpsc::Receiver, task::Waker}; use std::{sync::mpsc::Receiver, task::Waker};
use anyhow::{bail, Error, Result}; use anyhow::{Error, Result, bail};
use time::OffsetDateTime; use time::OffsetDateTime;
use typed_path::Utf8PlatformPathBuf; use typed_path::Utf8PlatformPathBuf;
use crate::{ use crate::{
build::{run_make, BuildConfig, BuildStatus}, build::{BuildConfig, BuildStatus, run_make},
diff::{diff_objs, DiffObjConfig, MappingConfig, ObjectDiff}, diff::{DiffObjConfig, MappingConfig, ObjectDiff, diff_objs},
jobs::{start_job, update_status, Job, JobContext, JobResult, JobState}, jobs::{Job, JobContext, JobResult, JobState, start_job, update_status},
obj::{read, Object}, obj::{Object, read},
}; };
pub struct ObjDiffConfig { pub struct ObjDiffConfig {

View File

@@ -10,7 +10,7 @@ use anyhow::{Context, Result};
pub use self_update; // Re-export self_update crate pub use self_update; // Re-export self_update crate
use self_update::update::ReleaseUpdate; use self_update::update::ReleaseUpdate;
use crate::jobs::{start_job, update_status, Job, JobContext, JobResult, JobState}; use crate::jobs::{Job, JobContext, JobResult, JobState, start_job, update_status};
pub struct UpdateConfig { pub struct UpdateConfig {
pub build_updater: fn() -> Result<Box<dyn ReleaseUpdate>>, pub build_updater: fn() -> Result<Box<dyn ReleaseUpdate>>,

View File

@@ -11,7 +11,7 @@ use alloc::{
}; };
use core::{fmt, num::NonZeroU32}; use core::{fmt, num::NonZeroU32};
use flagset::{flags, FlagSet}; use flagset::{FlagSet, flags};
use crate::{ use crate::{
arch::{Arch, ArchDummy}, arch::{Arch, ArchDummy},

View File

@@ -6,16 +6,16 @@ use alloc::{
}; };
use core::cmp::Ordering; use core::cmp::Ordering;
use anyhow::{bail, ensure, Context, Result}; use anyhow::{Context, Result, anyhow, bail, ensure};
use object::{Object as _, ObjectSection as _, ObjectSymbol as _}; use object::{Object as _, ObjectSection as _, ObjectSymbol as _};
use crate::{ use crate::{
arch::{new_arch, Arch}, arch::{Arch, new_arch},
diff::DiffObjConfig, diff::DiffObjConfig,
obj::{ obj::{
split_meta::{SplitMeta, SPLITMETA_SECTION},
Object, Relocation, RelocationFlags, Section, SectionData, SectionFlag, SectionKind, Object, Relocation, RelocationFlags, Section, SectionData, SectionFlag, SectionKind,
Symbol, SymbolFlag, SymbolKind, Symbol, SymbolFlag, SymbolKind,
split_meta::{SPLITMETA_SECTION, SplitMeta},
}, },
util::{read_u16, read_u32}, util::{read_u16, read_u32},
}; };
@@ -151,14 +151,22 @@ fn infer_symbol_sizes(symbols: &mut [Symbol], sections: &[Section]) {
// Set symbol sizes based on the next symbol's address // Set symbol sizes based on the next symbol's address
let mut iter_idx = 0; let mut iter_idx = 0;
let mut last_end = (0, 0);
while iter_idx < symbols_with_section.len() { while iter_idx < symbols_with_section.len() {
let symbol_idx = symbols_with_section[iter_idx]; let symbol_idx = symbols_with_section[iter_idx];
let symbol = &symbols[symbol_idx]; let symbol = &symbols[symbol_idx];
let section_idx = symbol.section.unwrap();
iter_idx += 1; iter_idx += 1;
if symbol.size != 0 { if symbol.size != 0 {
if symbol.kind != SymbolKind::Section {
last_end = (section_idx, symbol.address + symbol.size);
}
continue;
}
// Skip over symbols that are contained within the previous symbol
if last_end.0 == section_idx && last_end.1 > symbol.address {
continue; continue;
} }
let section_idx = symbol.section.unwrap();
let next_symbol = match symbol.kind { let next_symbol = match symbol.kind {
// For function/object symbols, find the next function/object symbol (in other words: // For function/object symbols, find the next function/object symbol (in other words:
// skip over labels) // skip over labels)
@@ -413,6 +421,44 @@ fn map_relocations(
Ok(()) Ok(())
} }
fn calculate_pooled_relocations(
arch: &dyn Arch,
sections: &mut [Section],
symbols: &[Symbol],
) -> Result<()> {
for (section_index, section) in sections.iter_mut().enumerate() {
if section.kind != SectionKind::Code {
continue;
}
let mut fake_pool_relocs = Vec::new();
for symbol in symbols {
if symbol.section != Some(section_index) {
continue;
}
if symbol.kind != SymbolKind::Function {
continue;
}
let code =
section.data_range(symbol.address, symbol.size as usize).ok_or_else(|| {
anyhow!(
"Symbol data out of bounds: {:#x}..{:#x}",
symbol.address,
symbol.address + symbol.size
)
})?;
fake_pool_relocs.append(&mut arch.generate_pooled_relocations(
symbol.address,
code,
&section.relocations,
symbols,
));
}
section.relocations.append(&mut fake_pool_relocs);
section.relocations.sort_by_key(|r| r.address);
}
Ok(())
}
fn parse_line_info( fn parse_line_info(
obj_file: &object::File, obj_file: &object::File,
sections: &mut [Section], sections: &mut [Section],
@@ -804,6 +850,9 @@ pub fn parse(data: &[u8], config: &DiffObjConfig) -> Result<Object> {
let (mut symbols, symbol_indices) = let (mut symbols, symbol_indices) =
map_symbols(arch.as_ref(), &obj_file, &sections, &section_indices, split_meta.as_ref())?; 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)?; 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)?; parse_line_info(&obj_file, &mut sections, &section_indices, data)?;
if config.combine_data_sections || config.combine_text_sections { if config.combine_data_sections || config.combine_text_sections {
combine_sections(&mut sections, &mut symbols, config)?; combine_sections(&mut sections, &mut symbols, config)?;

View File

@@ -1,7 +1,7 @@
use alloc::{string::String, vec, vec::Vec}; use alloc::{string::String, vec, vec::Vec};
use anyhow::{anyhow, Result}; use anyhow::{Result, anyhow};
use object::{elf::SHT_NOTE, Endian, ObjectSection}; use object::{Endian, ObjectSection, elf::SHT_NOTE};
pub const SPLITMETA_SECTION: &str = ".note.split"; pub const SPLITMETA_SECTION: &str = ".note.split";
pub const SHT_SPLITMETA: u32 = SHT_NOTE; pub const SHT_SPLITMETA: u32 = SHT_NOTE;

View File

@@ -1,7 +1,7 @@
use alloc::format; use alloc::format;
use core::fmt; use core::fmt;
use anyhow::{ensure, Result}; use anyhow::{Result, ensure};
use num_traits::PrimInt; use num_traits::PrimInt;
use object::{Endian, Object}; use object::{Endian, Object};

View File

@@ -0,0 +1,17 @@
use objdiff_core::{diff, obj};
mod common;
#[test]
#[cfg(feature = "arm")]
fn read_arm() {
let diff_config = diff::DiffObjConfig { mips_register_prefix: true, ..Default::default() };
let obj = obj::read::parse(include_object!("data/arm/LinkStateItem.o"), &diff_config).unwrap();
insta::assert_debug_snapshot!(obj);
let symbol_idx =
obj.symbols.iter().position(|s| s.name == "_ZN13LinkStateItem12OnStateLeaveEi").unwrap();
let diff = diff::code::no_diff_code(&obj, symbol_idx, &diff_config).unwrap();
insta::assert_debug_snapshot!(diff.instruction_rows);
let output = common::display_diff(&obj, &diff, symbol_idx, &diff_config);
insta::assert_snapshot!(output);
}

View File

@@ -14,3 +14,26 @@ fn read_mips() {
let output = common::display_diff(&obj, &diff, symbol_idx, &diff_config); let output = common::display_diff(&obj, &diff, symbol_idx, &diff_config);
insta::assert_snapshot!(output); insta::assert_snapshot!(output);
} }
#[test]
#[cfg(feature = "mips")]
fn cross_endian_diff() {
let diff_config = diff::DiffObjConfig::default();
let obj_be = obj::read::parse(include_object!("data/mips/code_be.o"), &diff_config).unwrap();
assert_eq!(obj_be.endianness, object::Endianness::Big);
let obj_le = obj::read::parse(include_object!("data/mips/code_le.o"), &diff_config).unwrap();
assert_eq!(obj_le.endianness, object::Endianness::Little);
let left_symbol_idx = obj_be.symbols.iter().position(|s| s.name == "func_00000000").unwrap();
let right_symbol_idx =
obj_le.symbols.iter().position(|s| s.name == "func_00000000__FPcPc").unwrap();
let (left_diff, right_diff) =
diff::code::diff_code(&obj_be, &obj_le, left_symbol_idx, right_symbol_idx, &diff_config)
.unwrap();
// Although the objects differ in endianness, the instructions should match.
assert_eq!(left_diff.instruction_rows[0].kind, diff::InstructionDiffKind::None);
assert_eq!(right_diff.instruction_rows[0].kind, diff::InstructionDiffKind::None);
assert_eq!(left_diff.instruction_rows[1].kind, diff::InstructionDiffKind::None);
assert_eq!(right_diff.instruction_rows[1].kind, diff::InstructionDiffKind::None);
assert_eq!(left_diff.instruction_rows[2].kind, diff::InstructionDiffKind::None);
assert_eq!(right_diff.instruction_rows[2].kind, diff::InstructionDiffKind::None);
}

View File

@@ -26,3 +26,17 @@ fn read_x86_combine_sections() {
let obj = obj::read::parse(include_object!("data/x86/rtest.obj"), &diff_config).unwrap(); let obj = obj::read::parse(include_object!("data/x86/rtest.obj"), &diff_config).unwrap();
insta::assert_debug_snapshot!(obj.sections); insta::assert_debug_snapshot!(obj.sections);
} }
#[test]
#[cfg(feature = "x86")]
fn read_x86_64() {
let diff_config = diff::DiffObjConfig::default();
let obj = obj::read::parse(include_object!("data/x86_64/vs2022.o"), &diff_config).unwrap();
insta::assert_debug_snapshot!(obj);
let symbol_idx =
obj.symbols.iter().position(|s| s.name == "?Dot@Vector@@QEAAMPEAU1@@Z").unwrap();
let diff = diff::code::no_diff_code(&obj, symbol_idx, &diff_config).unwrap();
insta::assert_debug_snapshot!(diff.instruction_rows);
let output = common::display_diff(&obj, &diff, symbol_idx, &diff_config);
insta::assert_snapshot!(output);
}

View File

@@ -1,5 +1,5 @@
use objdiff_core::{ use objdiff_core::{
diff::{display::DiffTextSegment, DiffObjConfig, SymbolDiff}, diff::{DiffObjConfig, SymbolDiff, display::DiffTextSegment},
obj::Object, obj::Object,
}; };

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,112 @@
---
source: objdiff-core/tests/arch_arm.rs
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)]
[(Address(4), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mov", 32818), Normal, 10), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Eol, Normal, 0)]
[(Address(8), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mov", 32818), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Eol, Normal, 0)]
[(Address(12), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32774), 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(16), Normal, 5), (Spacing(4), Normal, 0), (Opcode("ldr", 32799), 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), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmp", 32786), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(10)), Normal, 0), (Eol, Normal, 0)]
[(Address(24), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addls", 32770), 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), (Basic(" #"), Normal, 0), (Argument(Unsigned(2)), Normal, 0), (Eol, Normal, 0)]
[(Address(28), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 32773), Normal, 10), (BranchDest(200), Normal, 0), (Basic(" ~>"), Rotating(0), 0), (Eol, Normal, 0)]
[(Address(32), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 32773), Normal, 10), (BranchDest(200), Normal, 0), (Basic(" ~>"), Rotating(0), 0), (Eol, Normal, 0)]
[(Address(36), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 32773), Normal, 10), (BranchDest(200), Normal, 0), (Basic(" ~>"), Rotating(0), 0), (Eol, Normal, 0)]
[(Address(40), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 32773), Normal, 10), (BranchDest(200), Normal, 0), (Basic(" ~>"), Rotating(0), 0), (Eol, Normal, 0)]
[(Address(44), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 32773), Normal, 10), (BranchDest(192), Normal, 0), (Basic(" ~>"), Rotating(1), 0), (Eol, Normal, 0)]
[(Address(48), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 32773), Normal, 10), (BranchDest(124), Normal, 0), (Basic(" ~>"), Rotating(2), 0), (Eol, Normal, 0)]
[(Address(52), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 32773), Normal, 10), (BranchDest(200), Normal, 0), (Basic(" ~>"), Rotating(0), 0), (Eol, Normal, 0)]
[(Address(56), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 32773), Normal, 10), (BranchDest(140), Normal, 0), (Basic(" ~>"), Rotating(3), 0), (Eol, Normal, 0)]
[(Address(60), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 32773), Normal, 10), (BranchDest(76), Normal, 0), (Basic(" ~>"), Rotating(4), 0), (Eol, Normal, 0)]
[(Address(64), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 32773), Normal, 10), (BranchDest(152), Normal, 0), (Basic(" ~>"), Rotating(5), 0), (Eol, Normal, 0)]
[(Address(68), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 32773), Normal, 10), (BranchDest(164), Normal, 0), (Basic(" ~>"), Rotating(6), 0), (Eol, Normal, 0)]
[(Address(72), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 32773), Normal, 10), (BranchDest(164), Normal, 0), (Basic(" ~>"), Rotating(6), 0), (Eol, Normal, 0)]
[(Address(76), Normal, 5), (Basic(" ~> "), Rotating(4), 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(336)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
[(Address(80), 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)]
[(Address(84), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32774), 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(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(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), (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)]
[(Address(116), Normal, 5), (Spacing(4), Normal, 0), (Opcode("blx", 32777), 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(120), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 32773), Normal, 10), (BranchDest(200), Normal, 0), (Basic(" ~>"), Rotating(0), 0), (Eol, Normal, 0)]
[(Address(124), Normal, 5), (Basic(" ~> "), Rotating(2), 0), (Opcode("mov", 32818), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Eol, Normal, 0)]
[(Address(128), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mov", 32818), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Eol, Normal, 0)]
[(Address(132), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32774), 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(136), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 32773), Normal, 10), (BranchDest(200), Normal, 0), (Basic(" ~>"), Rotating(0), 0), (Eol, Normal, 0)]
[(Address(140), Normal, 5), (Basic(" ~> "), Rotating(3), 0), (Opcode("mov", 32818), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Eol, Normal, 0)]
[(Address(144), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32774), 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(148), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 32773), Normal, 10), (BranchDest(200), Normal, 0), (Basic(" ~>"), Rotating(0), 0), (Eol, Normal, 0)]
[(Address(152), Normal, 5), (Basic(" ~> "), Rotating(5), 0), (Opcode("mov", 32818), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Eol, Normal, 0)]
[(Address(156), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32774), 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(160), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 32773), Normal, 10), (BranchDest(200), Normal, 0), (Basic(" ~>"), Rotating(0), 0), (Eol, Normal, 0)]
[(Address(164), Normal, 5), (Basic(" ~> "), Rotating(6), 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(248)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
[(Address(168), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mov", 32818), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(0)), Normal, 0), (Eol, Normal, 0)]
[(Address(172), 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)]
[(Address(176), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mov", 32818), Normal, 10), (Argument(Opaque("r2")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Eol, Normal, 0)]
[(Address(180), Normal, 5), (Spacing(4), Normal, 0), (Opcode("strb", 32899), 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), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32774), 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(188), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 32773), Normal, 10), (BranchDest(200), Normal, 0), (Basic(" ~>"), Rotating(0), 0), (Eol, Normal, 0)]
[(Address(192), Normal, 5), (Basic(" ~> "), Rotating(1), 0), (Opcode("mov", 32818), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Eol, Normal, 0)]
[(Address(196), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32774), 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(200), Normal, 5), (Basic(" ~> "), Rotating(0), 0), (Opcode("ldr", 32799), 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), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mvn", 32829), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(0)), Normal, 0), (Eol, Normal, 0)]
[(Address(208), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmp", 32786), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Eol, Normal, 0)]
[(Address(212), Normal, 5), (Spacing(4), Normal, 0), (Opcode("beq", 32773), Normal, 10), (BranchDest(236), Normal, 0), (Basic(" ~>"), Rotating(9), 0), (Eol, Normal, 0)]
[(Address(216), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mov", 32818), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Eol, Normal, 0)]
[(Address(220), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32774), 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(224), Normal, 5), (Spacing(4), Normal, 0), (Opcode("ldr", 32799), 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), Normal, 5), (Spacing(4), Normal, 0), (Opcode("ldr", 32799), 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), Normal, 5), (Spacing(4), Normal, 0), (Opcode("blx", 32778), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Eol, Normal, 0)]
[(Address(236), Normal, 5), (Basic(" ~> "), Rotating(9), 0), (Opcode("ldr", 32799), 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(240), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmp", 32786), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(9)), Normal, 0), (Eol, Normal, 0)]
[(Address(244), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bgt", 32773), Normal, 10), (BranchDest(288), Normal, 0), (Basic(" ~>"), Rotating(10), 0), (Eol, Normal, 0)]
[(Address(248), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bge", 32773), Normal, 10), (BranchDest(296), Normal, 0), (Basic(" ~>"), Rotating(11), 0), (Eol, Normal, 0)]
[(Address(252), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmp", 32786), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(1)), Normal, 0), (Eol, Normal, 0)]
[(Address(256), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bgt", 32773), Normal, 10), (BranchDest(308), Normal, 0), (Basic(" ~>"), Rotating(12), 0), (Eol, Normal, 0)]
[(Address(260), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mvn", 32829), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(0)), Normal, 0), (Eol, Normal, 0)]
[(Address(264), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmp", 32786), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Eol, Normal, 0)]
[(Address(268), Normal, 5), (Spacing(4), Normal, 0), (Opcode("blt", 32773), Normal, 10), (BranchDest(308), Normal, 0), (Basic(" ~>"), Rotating(12), 0), (Eol, Normal, 0)]
[(Address(272), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmpne", 32786), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(0)), Normal, 0), (Eol, Normal, 0)]
[(Address(276), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmpne", 32786), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(1)), Normal, 0), (Eol, Normal, 0)]
[(Address(280), Normal, 5), (Spacing(4), Normal, 0), (Opcode("beq", 32773), Normal, 10), (BranchDest(340), Normal, 0), (Basic(" ~>"), Rotating(13), 0), (Eol, Normal, 0)]
[(Address(284), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 32773), Normal, 10), (BranchDest(308), Normal, 0), (Basic(" ~>"), Rotating(12), 0), (Eol, Normal, 0)]
[(Address(288), Normal, 5), (Basic(" ~> "), Rotating(10), 0), (Opcode("cmp", 32786), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(10)), Normal, 0), (Eol, Normal, 0)]
[(Address(292), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 32773), Normal, 10), (BranchDest(308), Normal, 0), (Basic(" ~>"), Rotating(12), 0), (Eol, Normal, 0)]
[(Address(296), Normal, 5), (Basic(" ~> "), Rotating(11), 0), (Opcode("mov", 32818), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Eol, Normal, 0)]
[(Address(300), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32774), 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(304), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 32773), Normal, 10), (BranchDest(340), Normal, 0), (Basic(" ~>"), Rotating(13), 0), (Eol, Normal, 0)]
[(Address(308), Normal, 5), (Basic(" ~> "), Rotating(12), 0), (Opcode("mov", 32818), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Eol, Normal, 0)]
[(Address(312), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32774), 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(316), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmp", 32786), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(4)), Normal, 0), (Eol, Normal, 0)]
[(Address(320), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmpne", 32786), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(2)), Normal, 0), (Eol, Normal, 0)]
[(Address(324), Normal, 5), (Spacing(4), Normal, 0), (Opcode("beq", 32773), Normal, 10), (BranchDest(340), Normal, 0), (Basic(" ~>"), Rotating(13), 0), (Eol, Normal, 0)]
[(Address(328), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32774), 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(332), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mov", 32818), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(1)), Normal, 0), (Eol, Normal, 0)]
[(Address(336), Normal, 5), (Spacing(4), Normal, 0), (Opcode("strb", 32899), 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), Normal, 5), (Basic(" ~> "), Rotating(13), 0), (Opcode("mvn", 32829), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(0)), Normal, 0), (Eol, Normal, 0)]
[(Address(344), Normal, 5), (Spacing(4), Normal, 0), (Opcode("add", 32770), Normal, 10), (Argument(Opaque("r6")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(80)), Normal, 0), (Eol, Normal, 0)]
[(Address(348), Normal, 5), (Spacing(4), Normal, 0), (Opcode("add", 32770), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(88)), Normal, 0), (Eol, Normal, 0)]
[(Address(352), Normal, 5), (Spacing(4), Normal, 0), (Opcode("str", 32898), 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(24)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
[(Address(356), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmp", 32786), Normal, 10), (Argument(Opaque("r6")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Eol, Normal, 0)]
[(Address(360), Normal, 5), (Spacing(4), Normal, 0), (Opcode("beq", 32773), Normal, 10), (BranchDest(384), Normal, 0), (Basic(" ~>"), Rotating(14), 0), (Eol, Normal, 0)]
[(Address(364), Normal, 5), (Basic(" ~> "), Rotating(15), 0), (Opcode("mov", 32818), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r6")), Normal, 0), (Eol, Normal, 0)]
[(Address(368), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32774), 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(372), Normal, 5), (Spacing(4), Normal, 0), (Opcode("add", 32770), 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), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmp", 32786), Normal, 10), (Argument(Opaque("r6")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Eol, Normal, 0)]
[(Address(380), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 32773), Normal, 10), (BranchDest(364), Normal, 0), (Basic(" ~>"), Rotating(15), 0), (Eol, Normal, 0)]
[(Address(384), Normal, 5), (Basic(" ~> "), Rotating(14), 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(36)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
[(Address(388), 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)]
[(Address(392), Normal, 5), (Spacing(4), Normal, 0), (Opcode("ldrb", 32800), 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), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmp", 32786), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(0)), Normal, 0), (Eol, Normal, 0)]
[(Address(400), Normal, 5), (Spacing(4), Normal, 0), (Opcode("beq", 32773), Normal, 10), (BranchDest(408), Normal, 0), (Basic(" ~>"), Rotating(16), 0), (Eol, Normal, 0)]
[(Address(404), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32774), 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(408), Normal, 5), (Basic(" ~> "), Rotating(16), 0), (Opcode("mov", 32818), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(0)), Normal, 0), (Eol, Normal, 0)]
[(Address(412), Normal, 5), (Spacing(4), Normal, 0), (Opcode("strb", 32899), 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), Normal, 5), (Spacing(4), Normal, 0), (Opcode("ldmia", 32793), 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("pc")), Normal, 0), (Basic("}"), Normal, 0), (Eol, Normal, 0)]
[(Address(420), Normal, 5), (Spacing(4), Normal, 0), (Opcode("andeq", 32771), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(" <"), Normal, 0), (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), (Basic(">"), Normal, 0), (Eol, Normal, 0)]
[(Address(424), Normal, 5), (Basic(" ~> "), Rotating(8), 0), (Opcode("andeq", 32771), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(" <"), Normal, 0), (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), (Basic(">"), Normal, 0), (Eol, Normal, 0)]
[(Address(428), Normal, 5), (Spacing(4), Normal, 0), (Opcode("andeq", 32771), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(" <"), Normal, 0), (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), (Basic(">"), Normal, 0), (Eol, Normal, 0)]

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,6 @@
--- ---
source: objdiff-core/tests/arch_ppc.rs source: objdiff-core/tests/arch_ppc.rs
assertion_line: 70
expression: sections_display expression: sections_display
--- ---
[ [
@@ -46,7 +47,7 @@ expression: sections_display
name: ".text", name: ".text",
size: 3060, size: 3060,
match_percent: Some( match_percent: Some(
59.02353, 58.662746,
), ),
symbols: [ symbols: [
SectionDisplaySymbol { SectionDisplaySymbol {

View File

@@ -1,5 +1,6 @@
--- ---
source: objdiff-core/tests/arch_ppc.rs source: objdiff-core/tests/arch_ppc.rs
assertion_line: 20
expression: output expression: output
--- ---
[(Address(0), Normal, 5), (Spacing(4), Normal, 0), (Opcode("srwi", 60), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("24")), Normal, 0), (Eol, Normal, 0)] [(Address(0), Normal, 5), (Spacing(4), Normal, 0), (Opcode("srwi", 60), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("24")), Normal, 0), (Eol, Normal, 0)]
@@ -9,7 +10,7 @@ expression: output
[(Address(16), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 45), Normal, 10), (BranchDest(32), Normal, 0), (Basic(" ~>"), Rotating(1), 0), (Eol, Normal, 0)] [(Address(16), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 45), Normal, 10), (BranchDest(32), Normal, 0), (Basic(" ~>"), Rotating(1), 0), (Eol, Normal, 0)]
[(Address(20), Normal, 5), (Basic(" ~> "), Rotating(0), 0), (Opcode("lis", 42), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@ha"), Normal, 0), (Eol, Normal, 0)] [(Address(20), Normal, 5), (Basic(" ~> "), Rotating(0), 0), (Opcode("lis", 42), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@ha"), Normal, 0), (Eol, Normal, 0)]
[(Address(24), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 41), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@l"), Normal, 0), (Eol, Normal, 0)] [(Address(24), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 41), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@l"), Normal, 0), (Eol, Normal, 0)]
[(Address(28), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Eol, Normal, 0)] [(Address(28), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(" <"), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic(">"), Normal, 0), (Eol, Normal, 0)]
[(Address(32), Normal, 5), (Basic(" ~> "), Rotating(1), 0), (Opcode("extrwi", 60), Normal, 10), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("8")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("8")), Normal, 0), (Eol, Normal, 0)] [(Address(32), Normal, 5), (Basic(" ~> "), Rotating(1), 0), (Opcode("extrwi", 60), Normal, 10), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("8")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("8")), Normal, 0), (Eol, Normal, 0)]
[(Address(36), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 166), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), Bright, 0), (Basic("@sda21"), Normal, 0), (Eol, Normal, 0)] [(Address(36), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 166), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), Bright, 0), (Basic("@sda21"), Normal, 0), (Eol, Normal, 0)]
[(Address(40), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmpwi", 38), Normal, 10), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 0), (Eol, Normal, 0)] [(Address(40), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmpwi", 38), Normal, 10), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 0), (Eol, Normal, 0)]
@@ -18,7 +19,7 @@ expression: output
[(Address(52), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 45), Normal, 10), (BranchDest(68), Normal, 0), (Basic(" ~>"), Rotating(3), 0), (Eol, Normal, 0)] [(Address(52), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 45), Normal, 10), (BranchDest(68), Normal, 0), (Basic(" ~>"), Rotating(3), 0), (Eol, Normal, 0)]
[(Address(56), Normal, 5), (Basic(" ~> "), Rotating(2), 0), (Opcode("lis", 42), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@ha"), Normal, 0), (Eol, Normal, 0)] [(Address(56), Normal, 5), (Basic(" ~> "), Rotating(2), 0), (Opcode("lis", 42), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@ha"), Normal, 0), (Eol, Normal, 0)]
[(Address(60), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 41), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@l"), Normal, 0), (Eol, Normal, 0)] [(Address(60), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 41), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@l"), Normal, 0), (Eol, Normal, 0)]
[(Address(64), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Eol, Normal, 0)] [(Address(64), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(" <"), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic(">"), Normal, 0), (Eol, Normal, 0)]
[(Address(68), Normal, 5), (Basic(" ~> "), Rotating(3), 0), (Opcode("extrwi", 60), Normal, 10), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("8")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("16")), Normal, 0), (Eol, Normal, 0)] [(Address(68), Normal, 5), (Basic(" ~> "), Rotating(3), 0), (Opcode("extrwi", 60), Normal, 10), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("8")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("16")), Normal, 0), (Eol, Normal, 0)]
[(Address(72), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 41), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), Bright, 0), (Basic("@sda21"), Normal, 0), (Eol, Normal, 0)] [(Address(72), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 41), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), Bright, 0), (Basic("@sda21"), Normal, 0), (Eol, Normal, 0)]
[(Address(76), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmpwi", 38), Normal, 10), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 0), (Eol, Normal, 0)] [(Address(76), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmpwi", 38), Normal, 10), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 0), (Eol, Normal, 0)]
@@ -28,7 +29,7 @@ expression: output
[(Address(92), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 45), Normal, 10), (BranchDest(108), Normal, 0), (Basic(" ~>"), Rotating(5), 0), (Eol, Normal, 0)] [(Address(92), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 45), Normal, 10), (BranchDest(108), Normal, 0), (Basic(" ~>"), Rotating(5), 0), (Eol, Normal, 0)]
[(Address(96), Normal, 5), (Basic(" ~> "), Rotating(4), 0), (Opcode("lis", 42), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@ha"), Normal, 0), (Eol, Normal, 0)] [(Address(96), Normal, 5), (Basic(" ~> "), Rotating(4), 0), (Opcode("lis", 42), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@ha"), Normal, 0), (Eol, Normal, 0)]
[(Address(100), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 41), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@l"), Normal, 0), (Eol, Normal, 0)] [(Address(100), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 41), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@l"), Normal, 0), (Eol, Normal, 0)]
[(Address(104), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Eol, Normal, 0)] [(Address(104), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(" <"), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic(">"), Normal, 0), (Eol, Normal, 0)]
[(Address(108), Normal, 5), (Basic(" ~> "), Rotating(5), 0), (Opcode("clrlwi", 60), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("24")), Normal, 0), (Eol, Normal, 0)] [(Address(108), Normal, 5), (Basic(" ~> "), Rotating(5), 0), (Opcode("clrlwi", 60), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("24")), Normal, 0), (Eol, Normal, 0)]
[(Address(112), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 41), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), Bright, 0), (Basic("@sda21"), Normal, 0), (Eol, Normal, 0)] [(Address(112), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 41), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), Bright, 0), (Basic("@sda21"), Normal, 0), (Eol, Normal, 0)]
[(Address(116), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmpwi", 38), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 0), (Eol, Normal, 0)] [(Address(116), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmpwi", 38), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 0), (Eol, Normal, 0)]
@@ -38,7 +39,7 @@ expression: output
[(Address(132), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 45), Normal, 10), (BranchDest(148), Normal, 0), (Basic(" ~>"), Rotating(7), 0), (Eol, Normal, 0)] [(Address(132), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 45), Normal, 10), (BranchDest(148), Normal, 0), (Basic(" ~>"), Rotating(7), 0), (Eol, Normal, 0)]
[(Address(136), Normal, 5), (Basic(" ~> "), Rotating(6), 0), (Opcode("lis", 42), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@ha"), Normal, 0), (Eol, Normal, 0)] [(Address(136), Normal, 5), (Basic(" ~> "), Rotating(6), 0), (Opcode("lis", 42), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@ha"), Normal, 0), (Eol, Normal, 0)]
[(Address(140), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 41), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@l"), Normal, 0), (Eol, Normal, 0)] [(Address(140), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 41), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@l"), Normal, 0), (Eol, Normal, 0)]
[(Address(144), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Eol, Normal, 0)] [(Address(144), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(" <"), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic(">"), Normal, 0), (Eol, Normal, 0)]
[(Address(148), Normal, 5), (Basic(" ~> "), Rotating(7), 0), (Opcode("li", 41), Normal, 10), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), Bright, 0), (Basic("@sda21"), Normal, 0), (Eol, Normal, 0)] [(Address(148), Normal, 5), (Basic(" ~> "), Rotating(7), 0), (Opcode("li", 41), Normal, 10), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), Bright, 0), (Basic("@sda21"), Normal, 0), (Eol, Normal, 0)]
[(Address(152), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 41), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(0)), Normal, 0), (Eol, Normal, 0)] [(Address(152), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 41), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(0)), Normal, 0), (Eol, Normal, 0)]
[(Address(156), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 166), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(3)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)] [(Address(156), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 166), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(3)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
@@ -47,22 +48,22 @@ expression: output
[(Address(168), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 166), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(4)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)] [(Address(168), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 166), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(4)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(172), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 41), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(45)), Normal, 0), (Eol, Normal, 0)] [(Address(172), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 41), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(45)), Normal, 0), (Eol, Normal, 0)]
[(Address(176), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbz", 162), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), Bright, 0), (Basic("@sda21"), Normal, 0), (Eol, Normal, 0)] [(Address(176), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbz", 162), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), Bright, 0), (Basic("@sda21"), Normal, 0), (Eol, Normal, 0)]
[(Address(180), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Eol, Normal, 0)] [(Address(180), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(" <"), Normal, 0), (Symbol(Symbol { name: "__ctype_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic(">"), Normal, 0), (Eol, Normal, 0)]
[(Address(184), Normal, 5), (Spacing(4), Normal, 0), (Opcode("andi.", 66), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Unsigned(220)), Normal, 0), (Eol, Normal, 0)] [(Address(184), Normal, 5), (Spacing(4), Normal, 0), (Opcode("andi.", 66), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Unsigned(220)), Normal, 0), (Eol, Normal, 0)]
[(Address(188), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 43), Normal, 10), (BranchDest(196), Normal, 0), (Basic(" ~>"), Rotating(8), 0), (Eol, Normal, 0)] [(Address(188), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 43), Normal, 10), (BranchDest(196), Normal, 0), (Basic(" ~>"), Rotating(8), 0), (Eol, Normal, 0)]
[(Address(192), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 166), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(0)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)] [(Address(192), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 166), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(0)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(196), Normal, 5), (Basic(" ~> "), Rotating(8), 0), (Opcode("lbzu", 163), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(1)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)] [(Address(196), Normal, 5), (Basic(" ~> "), Rotating(8), 0), (Opcode("lbzu", 163), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(1)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(200), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Eol, Normal, 0)] [(Address(200), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(" <"), Normal, 0), (Symbol(Symbol { name: "__ctype_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic(">"), Normal, 0), (Eol, Normal, 0)]
[(Address(204), Normal, 5), (Spacing(4), Normal, 0), (Opcode("andi.", 66), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Unsigned(220)), Normal, 0), (Eol, Normal, 0)] [(Address(204), Normal, 5), (Spacing(4), Normal, 0), (Opcode("andi.", 66), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Unsigned(220)), Normal, 0), (Eol, Normal, 0)]
[(Address(208), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 43), Normal, 10), (BranchDest(216), Normal, 0), (Basic(" ~>"), Rotating(9), 0), (Eol, Normal, 0)] [(Address(208), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 43), Normal, 10), (BranchDest(216), Normal, 0), (Basic(" ~>"), Rotating(9), 0), (Eol, Normal, 0)]
[(Address(212), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 166), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(0)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)] [(Address(212), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 166), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(0)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(216), Normal, 5), (Basic(" ~> "), Rotating(9), 0), (Opcode("lbzu", 163), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(1)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)] [(Address(216), Normal, 5), (Basic(" ~> "), Rotating(9), 0), (Opcode("lbzu", 163), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(1)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(220), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Eol, Normal, 0)] [(Address(220), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(" <"), Normal, 0), (Symbol(Symbol { name: "__ctype_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic(">"), Normal, 0), (Eol, Normal, 0)]
[(Address(224), Normal, 5), (Spacing(4), Normal, 0), (Opcode("andi.", 66), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Unsigned(220)), Normal, 0), (Eol, Normal, 0)] [(Address(224), Normal, 5), (Spacing(4), Normal, 0), (Opcode("andi.", 66), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Unsigned(220)), Normal, 0), (Eol, Normal, 0)]
[(Address(228), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 43), Normal, 10), (BranchDest(236), Normal, 0), (Basic(" ~>"), Rotating(10), 0), (Eol, Normal, 0)] [(Address(228), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 43), Normal, 10), (BranchDest(236), Normal, 0), (Basic(" ~>"), Rotating(10), 0), (Eol, Normal, 0)]
[(Address(232), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 166), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(0)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)] [(Address(232), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 166), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(0)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(236), Normal, 5), (Basic(" ~> "), Rotating(10), 0), (Opcode("lbzu", 163), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(1)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)] [(Address(236), Normal, 5), (Basic(" ~> "), Rotating(10), 0), (Opcode("lbzu", 163), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(1)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(240), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Eol, Normal, 0)] [(Address(240), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(" <"), Normal, 0), (Symbol(Symbol { name: "__ctype_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic(">"), Normal, 0), (Eol, Normal, 0)]
[(Address(244), Normal, 5), (Spacing(4), Normal, 0), (Opcode("andi.", 66), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Unsigned(220)), Normal, 0), (Eol, Normal, 0)] [(Address(244), Normal, 5), (Spacing(4), Normal, 0), (Opcode("andi.", 66), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Unsigned(220)), Normal, 0), (Eol, Normal, 0)]
[(Address(248), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 43), Normal, 10), (BranchDest(256), Normal, 0), (Basic(" ~>"), Rotating(11), 0), (Eol, Normal, 0)] [(Address(248), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 43), Normal, 10), (BranchDest(256), Normal, 0), (Basic(" ~>"), Rotating(11), 0), (Eol, Normal, 0)]
[(Address(252), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 166), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(0)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)] [(Address(252), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 166), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(0)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]

View File

@@ -1,5 +1,6 @@
--- ---
source: objdiff-core/tests/arch_ppc.rs source: objdiff-core/tests/arch_ppc.rs
assertion_line: 14
expression: obj expression: obj
--- ---
Object { Object {
@@ -183,6 +184,14 @@ Object {
target_symbol: 8, target_symbol: 8,
addend: 0, addend: 0,
}, },
Relocation {
flags: Elf(
0,
),
address: 28,
target_symbol: 8,
addend: 0,
},
Relocation { Relocation {
flags: Elf( flags: Elf(
109, 109,
@@ -207,6 +216,14 @@ Object {
target_symbol: 8, target_symbol: 8,
addend: 0, addend: 0,
}, },
Relocation {
flags: Elf(
0,
),
address: 64,
target_symbol: 8,
addend: 0,
},
Relocation { Relocation {
flags: Elf( flags: Elf(
109, 109,
@@ -231,6 +248,14 @@ Object {
target_symbol: 8, target_symbol: 8,
addend: 0, addend: 0,
}, },
Relocation {
flags: Elf(
0,
),
address: 104,
target_symbol: 8,
addend: 0,
},
Relocation { Relocation {
flags: Elf( flags: Elf(
109, 109,
@@ -255,6 +280,14 @@ Object {
target_symbol: 8, target_symbol: 8,
addend: 0, addend: 0,
}, },
Relocation {
flags: Elf(
0,
),
address: 144,
target_symbol: 8,
addend: 0,
},
Relocation { Relocation {
flags: Elf( flags: Elf(
109, 109,
@@ -287,6 +320,38 @@ Object {
target_symbol: 5, target_symbol: 5,
addend: 0, addend: 0,
}, },
Relocation {
flags: Elf(
0,
),
address: 180,
target_symbol: 9,
addend: 0,
},
Relocation {
flags: Elf(
0,
),
address: 200,
target_symbol: 9,
addend: 0,
},
Relocation {
flags: Elf(
0,
),
address: 220,
target_symbol: 9,
addend: 0,
},
Relocation {
flags: Elf(
0,
),
address: 240,
target_symbol: 9,
addend: 0,
},
Relocation { Relocation {
flags: Elf( flags: Elf(
109, 109,

View File

@@ -4,7 +4,7 @@ expression: obj
--- ---
Object { Object {
arch: ArchX86 { arch: ArchX86 {
bits: 32, arch: X86,
endianness: Little, endianness: Little,
}, },
endianness: Little, endianness: Little,

View File

@@ -0,0 +1,279 @@
---
source: objdiff-core/tests/arch_x86.rs
expression: diff.instruction_rows
---
[
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 0,
size: 5,
opcode: 414,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 5,
size: 5,
opcode: 414,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 10,
size: 1,
opcode: 640,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 11,
size: 4,
opcode: 740,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 15,
size: 5,
opcode: 414,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 20,
size: 5,
opcode: 414,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 25,
size: 4,
opcode: 448,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 29,
size: 4,
opcode: 460,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 33,
size: 5,
opcode: 414,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 38,
size: 5,
opcode: 414,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 43,
size: 5,
opcode: 448,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 48,
size: 5,
opcode: 460,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 53,
size: 4,
opcode: 11,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 57,
size: 5,
opcode: 414,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 62,
size: 5,
opcode: 414,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 67,
size: 5,
opcode: 448,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 72,
size: 5,
opcode: 460,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 77,
size: 4,
opcode: 11,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 81,
size: 4,
opcode: 7,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 85,
size: 1,
opcode: 590,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 86,
size: 1,
opcode: 662,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
]

View File

@@ -0,0 +1,25 @@
---
source: objdiff-core/tests/arch_x86.rs
expression: output
---
[(Address(0), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mov", 414), Normal, 10), (Basic("["), Normal, 0), (Argument(Opaque("rsp")), Normal, 0), (Argument(Opaque("+")), Normal, 0), (Argument(Signed(16)), Normal, 0), (Basic("]"), Normal, 0), (Basic(","), Normal, 0), (Spacing(1), Normal, 0), (Argument(Opaque("rdx")), Normal, 0), (Eol, Normal, 0)]
[(Address(5), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mov", 414), Normal, 10), (Basic("["), Normal, 0), (Argument(Opaque("rsp")), Normal, 0), (Argument(Opaque("+")), Normal, 0), (Argument(Signed(8)), Normal, 0), (Basic("]"), Normal, 0), (Basic(","), Normal, 0), (Spacing(1), Normal, 0), (Argument(Opaque("rcx")), Normal, 0), (Eol, Normal, 0)]
[(Address(10), Normal, 5), (Spacing(4), Normal, 0), (Opcode("push", 640), Normal, 10), (Argument(Opaque("rdi")), Normal, 0), (Eol, Normal, 0)]
[(Address(11), Normal, 5), (Spacing(4), Normal, 0), (Opcode("sub", 740), Normal, 10), (Argument(Opaque("rsp")), Normal, 0), (Basic(","), Normal, 0), (Spacing(1), Normal, 0), (Argument(Unsigned(16)), Normal, 0), (Eol, Normal, 0)]
[(Address(15), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mov", 414), Normal, 10), (Argument(Opaque("rax")), Normal, 0), (Basic(","), Normal, 0), (Spacing(1), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("rsp")), Normal, 0), (Argument(Opaque("+")), Normal, 0), (Argument(Signed(32)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
[(Address(20), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mov", 414), Normal, 10), (Argument(Opaque("rcx")), Normal, 0), (Basic(","), Normal, 0), (Spacing(1), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("rsp")), Normal, 0), (Argument(Opaque("+")), Normal, 0), (Argument(Signed(40)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
[(Address(25), Normal, 5), (Spacing(4), Normal, 0), (Opcode("movss", 448), Normal, 10), (Argument(Opaque("xmm0")), Normal, 0), (Basic(","), Normal, 0), (Spacing(1), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("rax")), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
[(Address(29), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mulss", 460), Normal, 10), (Argument(Opaque("xmm0")), Normal, 0), (Basic(","), Normal, 0), (Spacing(1), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("rcx")), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
[(Address(33), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mov", 414), Normal, 10), (Argument(Opaque("rax")), Normal, 0), (Basic(","), Normal, 0), (Spacing(1), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("rsp")), Normal, 0), (Argument(Opaque("+")), Normal, 0), (Argument(Signed(32)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
[(Address(38), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mov", 414), Normal, 10), (Argument(Opaque("rcx")), Normal, 0), (Basic(","), Normal, 0), (Spacing(1), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("rsp")), Normal, 0), (Argument(Opaque("+")), Normal, 0), (Argument(Signed(40)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
[(Address(43), Normal, 5), (Spacing(4), Normal, 0), (Opcode("movss", 448), Normal, 10), (Argument(Opaque("xmm1")), Normal, 0), (Basic(","), Normal, 0), (Spacing(1), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("rax")), Normal, 0), (Argument(Opaque("+")), Normal, 0), (Argument(Signed(4)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
[(Address(48), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mulss", 460), Normal, 10), (Argument(Opaque("xmm1")), Normal, 0), (Basic(","), Normal, 0), (Spacing(1), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("rcx")), Normal, 0), (Argument(Opaque("+")), Normal, 0), (Argument(Signed(4)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
[(Address(53), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addss", 11), Normal, 10), (Argument(Opaque("xmm0")), Normal, 0), (Basic(","), Normal, 0), (Spacing(1), Normal, 0), (Argument(Opaque("xmm1")), Normal, 0), (Eol, Normal, 0)]
[(Address(57), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mov", 414), Normal, 10), (Argument(Opaque("rax")), Normal, 0), (Basic(","), Normal, 0), (Spacing(1), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("rsp")), Normal, 0), (Argument(Opaque("+")), Normal, 0), (Argument(Signed(32)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
[(Address(62), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mov", 414), Normal, 10), (Argument(Opaque("rcx")), Normal, 0), (Basic(","), Normal, 0), (Spacing(1), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("rsp")), Normal, 0), (Argument(Opaque("+")), Normal, 0), (Argument(Signed(40)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
[(Address(67), Normal, 5), (Spacing(4), Normal, 0), (Opcode("movss", 448), Normal, 10), (Argument(Opaque("xmm1")), Normal, 0), (Basic(","), Normal, 0), (Spacing(1), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("rax")), Normal, 0), (Argument(Opaque("+")), Normal, 0), (Argument(Signed(8)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
[(Address(72), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mulss", 460), Normal, 10), (Argument(Opaque("xmm1")), Normal, 0), (Basic(","), Normal, 0), (Spacing(1), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("rcx")), Normal, 0), (Argument(Opaque("+")), Normal, 0), (Argument(Signed(8)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
[(Address(77), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addss", 11), Normal, 10), (Argument(Opaque("xmm0")), Normal, 0), (Basic(","), Normal, 0), (Spacing(1), Normal, 0), (Argument(Opaque("xmm1")), Normal, 0), (Eol, Normal, 0)]
[(Address(81), Normal, 5), (Spacing(4), Normal, 0), (Opcode("add", 7), Normal, 10), (Argument(Opaque("rsp")), Normal, 0), (Basic(","), Normal, 0), (Spacing(1), Normal, 0), (Argument(Unsigned(16)), Normal, 0), (Eol, Normal, 0)]
[(Address(85), Normal, 5), (Spacing(4), Normal, 0), (Opcode("pop", 590), Normal, 10), (Argument(Opaque("rdi")), Normal, 0), (Eol, Normal, 0)]
[(Address(86), Normal, 5), (Spacing(4), Normal, 0), (Opcode("ret", 662), Normal, 10), (Eol, Normal, 0)]

File diff suppressed because it is too large Load Diff

View File

@@ -28,7 +28,6 @@ anyhow = "1.0"
cfg-if = "1.0" cfg-if = "1.0"
const_format = "0.2" const_format = "0.2"
cwdemangle = "1.0" cwdemangle = "1.0"
cwextab = { version = "1.0", git = "https://github.com/CelestialAmber/cwextab.git" }
dirs = "6.0" dirs = "6.0"
egui = "0.31" egui = "0.31"
egui_extras = "0.31" egui_extras = "0.31"
@@ -46,8 +45,6 @@ rfd = { version = "0.15" } #, default-features = false, features = ['xdg-portal'
rlwinmdec = { version = "1.0", git = "https://github.com/CelestialAmber/rlwinmdec.git" } rlwinmdec = { version = "1.0", git = "https://github.com/CelestialAmber/rlwinmdec.git" }
ron = "0.8" ron = "0.8"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
shell-escape = "0.1"
time = { version = "0.3", features = ["formatting", "local-offset"] } time = { version = "0.3", features = ["formatting", "local-offset"] }
typed-path = "0.10" typed-path = "0.10"
winit = { version = "0.30", features = ["wayland-csd-adwaita"] } winit = { version = "0.30", features = ["wayland-csd-adwaita"] }

View File

@@ -5,8 +5,8 @@ use std::{
path::{Path, PathBuf}, path::{Path, PathBuf},
rc::Rc, rc::Rc,
sync::{ sync::{
atomic::{AtomicBool, Ordering},
Arc, Mutex, RwLock, Arc, Mutex, RwLock,
atomic::{AtomicBool, Ordering},
}, },
time::Instant, time::Instant,
}; };
@@ -14,11 +14,11 @@ use std::{
use filetime::FileTime; use filetime::FileTime;
use globset::Glob; use globset::Glob;
use objdiff_core::{ use objdiff_core::{
build::watcher::{create_watcher, Watcher}, build::watcher::{Watcher, create_watcher},
config::{ config::{
DEFAULT_WATCH_PATTERNS, ProjectConfig, ProjectConfigInfo, ProjectObject, ScratchConfig,
build_globset, default_watch_patterns, path::platform_path_serde_option, build_globset, default_watch_patterns, path::platform_path_serde_option,
save_project_config, ProjectConfig, ProjectConfigInfo, ProjectObject, ScratchConfig, save_project_config,
DEFAULT_WATCH_PATTERNS,
}, },
diff::DiffObjConfig, diff::DiffObjConfig,
jobs::{Job, JobQueue, JobResult}, jobs::{Job, JobQueue, JobResult},
@@ -27,22 +27,22 @@ use time::UtcOffset;
use typed_path::{Utf8PlatformPath, Utf8PlatformPathBuf}; use typed_path::{Utf8PlatformPath, Utf8PlatformPathBuf};
use crate::{ use crate::{
app_config::{deserialize_config, AppConfigVersion}, app_config::{AppConfigVersion, deserialize_config},
config::{load_project_config, ProjectObjectNode}, config::{ProjectObjectNode, load_project_config},
jobs::{create_objdiff_config, egui_waker, start_build}, jobs::{create_objdiff_config, egui_waker, start_build},
views::{ views::{
appearance::{appearance_window, Appearance}, appearance::{Appearance, appearance_window},
config::{ config::{
arch_config_window, config_ui, general_config_ui, project_window, ConfigViewState, CONFIG_DISABLED_TEXT, ConfigViewState, arch_config_window, config_ui,
CONFIG_DISABLED_TEXT, general_config_ui, project_window,
}, },
debug::debug_window, debug::debug_window,
demangle::{demangle_window, DemangleViewState}, demangle::{DemangleViewState, demangle_window},
diff::diff_view_ui, diff::diff_view_ui,
frame_history::FrameHistory, frame_history::FrameHistory,
graphics::{graphics_window, GraphicsConfig, GraphicsViewState}, graphics::{GraphicsConfig, GraphicsViewState, graphics_window},
jobs::{jobs_menu_ui, jobs_window}, jobs::{jobs_menu_ui, jobs_window},
rlwinm::{rlwinm_decode_window, RlwinmDecodeViewState}, rlwinm::{RlwinmDecodeViewState, rlwinm_decode_window},
symbol_diff::{DiffViewAction, DiffViewState, ResolvedNavigation, View}, symbol_diff::{DiffViewAction, DiffViewState, ResolvedNavigation, View},
}, },
}; };

View File

@@ -11,7 +11,7 @@ use objdiff_core::{
}; };
use typed_path::{Utf8PlatformPathBuf, Utf8UnixPathBuf}; use typed_path::{Utf8PlatformPathBuf, Utf8UnixPathBuf};
use crate::app::{AppConfig, ObjectConfig, CONFIG_KEY}; use crate::app::{AppConfig, CONFIG_KEY, ObjectConfig};
#[derive(Clone, serde::Deserialize, serde::Serialize)] #[derive(Clone, serde::Deserialize, serde::Serialize)]
pub struct AppConfigVersion { pub struct AppConfigVersion {

View File

@@ -1,6 +1,6 @@
use anyhow::Result; use anyhow::Result;
use globset::Glob; use globset::Glob;
use objdiff_core::config::{try_project_config, DEFAULT_WATCH_PATTERNS}; use objdiff_core::config::{DEFAULT_WATCH_PATTERNS, try_project_config};
use typed_path::{Utf8UnixComponent, Utf8UnixPath}; use typed_path::{Utf8UnixComponent, Utf8UnixPath};
use crate::app::{AppState, ObjectConfig}; use crate::app::{AppState, ObjectConfig};

View File

@@ -94,7 +94,7 @@ pub fn load_font_if_needed(
// FIXME clean up // FIXME clean up
let default_font_ref = family.fonts.get(family.default_index).unwrap(); let default_font_ref = family.fonts.get(family.default_index).unwrap();
let default_font = family.handles.get(family.default_index).unwrap(); let default_font = family.handles.get(family.default_index).unwrap();
let default_font_data = load_font(default_font).unwrap(); let default_font_data = load_font(default_font)?;
log::info!("Loaded font family '{}'", family.family_name); log::info!("Loaded font family '{}'", family.family_name);
fonts.font_data.insert(default_font_ref.full_name(), Arc::new(default_font_data.font_data)); fonts.font_data.insert(default_font_ref.full_name(), Arc::new(default_font_data.font_data));
fonts fonts

View File

@@ -1,5 +1,5 @@
use egui::{ use egui::{
style::ScrollAnimation, vec2, Context, Key, KeyboardShortcut, Modifiers, PointerButton, Context, Key, KeyboardShortcut, Modifiers, PointerButton, style::ScrollAnimation, vec2,
}; };
fn any_widget_focused(ctx: &Context) -> bool { ctx.memory(|mem| mem.focused().is_some()) } fn any_widget_focused(ctx: &Context) -> bool { ctx.memory(|mem| mem.focused().is_some()) }

View File

@@ -3,18 +3,18 @@ use std::{
task::{Wake, Waker}, task::{Wake, Waker},
}; };
use anyhow::{bail, Result}; use anyhow::{Result, bail};
use jobs::create_scratch; use jobs::create_scratch;
use objdiff_core::{ use objdiff_core::{
build::BuildConfig, build::BuildConfig,
diff::MappingConfig, diff::MappingConfig,
jobs, jobs,
jobs::{check_update::CheckUpdateConfig, objdiff, update::UpdateConfig, Job, JobQueue}, jobs::{Job, JobQueue, check_update::CheckUpdateConfig, objdiff, update::UpdateConfig},
}; };
use crate::{ use crate::{
app::{AppConfig, AppState}, app::{AppConfig, AppState},
update::{build_updater, BIN_NAME_NEW, BIN_NAME_OLD}, update::{BIN_NAME_NEW, BIN_NAME_OLD, build_updater},
}; };
struct EguiWaker(egui::Context); struct EguiWaker(egui::Context);

View File

@@ -17,12 +17,12 @@ use std::{
sync::{Arc, Mutex}, sync::{Arc, Mutex},
}; };
use anyhow::{ensure, Result}; use anyhow::{Result, ensure};
use cfg_if::cfg_if; use cfg_if::cfg_if;
use time::UtcOffset; use time::UtcOffset;
use tracing_subscriber::EnvFilter; use tracing_subscriber::EnvFilter;
use crate::views::graphics::{load_graphics_config, GraphicsBackend, GraphicsConfig}; use crate::views::graphics::{GraphicsBackend, GraphicsConfig, load_graphics_config};
fn load_icon() -> Result<egui::IconData> { fn load_icon() -> Result<egui::IconData> {
let decoder = png::Decoder::new(include_bytes!("../assets/icon_64.png").as_ref()); let decoder = png::Decoder::new(include_bytes!("../assets/icon_64.png").as_ref());
@@ -86,7 +86,7 @@ fn main() -> ExitCode {
} }
#[cfg(feature = "wgpu")] #[cfg(feature = "wgpu")]
{ {
use eframe::egui_wgpu::{wgpu, WgpuSetup}; use eframe::egui_wgpu::{WgpuSetup, wgpu};
if graphics_config.desired_backend.is_supported() { if graphics_config.desired_backend.is_supported() {
native_options.wgpu_options.wgpu_setup = match native_options.wgpu_options.wgpu_setup { native_options.wgpu_options.wgpu_setup = match native_options.wgpu_options.wgpu_setup {
WgpuSetup::CreateNew(mut setup) => { WgpuSetup::CreateNew(mut setup) => {

View File

@@ -1,6 +1,6 @@
use std::sync::Arc; use std::sync::Arc;
use egui::{text::LayoutJob, Color32, FontFamily, FontId, TextFormat, TextStyle, Widget}; use egui::{Color32, FontFamily, FontId, TextFormat, TextStyle, Widget, text::LayoutJob};
use time::UtcOffset; use time::UtcOffset;
use crate::fonts::load_font_if_needed; use crate::fonts::load_font_if_needed;

View File

@@ -5,17 +5,17 @@ use std::{mem::take, path::MAIN_SEPARATOR};
#[cfg(all(windows, feature = "wsl"))] #[cfg(all(windows, feature = "wsl"))]
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use egui::{ use egui::{
output::OpenUrl, text::LayoutJob, CollapsingHeader, FontFamily, FontId, RichText, CollapsingHeader, FontFamily, FontId, RichText, SelectableLabel, TextFormat, Widget,
SelectableLabel, TextFormat, Widget, output::OpenUrl, text::LayoutJob,
}; };
use globset::Glob; use globset::Glob;
use objdiff_core::{ use objdiff_core::{
config::{path::check_path_buf, DEFAULT_WATCH_PATTERNS}, config::{DEFAULT_WATCH_PATTERNS, path::check_path_buf},
diff::{ diff::{
ConfigEnum, ConfigEnumVariantInfo, ConfigPropertyId, ConfigPropertyKind, CONFIG_GROUPS, ConfigEnum, ConfigEnumVariantInfo, ConfigPropertyId, ConfigPropertyKind,
ConfigPropertyValue, CONFIG_GROUPS, ConfigPropertyValue,
}, },
jobs::{check_update::CheckUpdateResult, Job, JobQueue, JobResult}, jobs::{Job, JobQueue, JobResult, check_update::CheckUpdateResult},
}; };
use typed_path::Utf8PlatformPathBuf; use typed_path::Utf8PlatformPathBuf;
@@ -347,11 +347,7 @@ fn display_unit(
let color = if selected { let color = if selected {
appearance.emphasized_text_color appearance.emphasized_text_color
} else if let Some(complete) = object.complete { } else if let Some(complete) = object.complete {
if complete { if complete { appearance.insert_color } else { appearance.delete_color }
appearance.insert_color
} else {
appearance.delete_color
}
} else { } else {
appearance.text_color appearance.text_color
}; };

View File

@@ -1,100 +1,90 @@
use std::{cmp::min, default::Default, mem::take}; use std::{cmp::min, default::Default, mem::take};
use egui::{text::LayoutJob, Label, Sense, Widget}; use egui::{Label, Sense, Widget, text::LayoutJob};
use objdiff_core::{ use objdiff_core::{
diff::{DataDiff, DataDiffKind, DataRelocationDiff}, diff::{
DataDiff, DataDiffKind, DataRelocationDiff,
data::resolve_relocation,
display::{ContextItem, HoverItem, relocation_context, relocation_hover},
},
obj::Object, obj::Object,
}; };
use super::diff::{context_menu_items_ui, hover_items_ui};
use crate::views::{appearance::Appearance, write_text}; use crate::views::{appearance::Appearance, write_text};
pub(crate) const BYTES_PER_ROW: usize = 16; pub(crate) const BYTES_PER_ROW: usize = 16;
fn data_row_hover(obj: &Object, diffs: &[(DataDiff, Vec<DataRelocationDiff>)]) -> Vec<HoverItem> {
let mut out = Vec::new();
let reloc_diffs = diffs.iter().flat_map(|(_, reloc_diffs)| reloc_diffs);
let mut prev_reloc = None;
for reloc_diff in reloc_diffs {
let reloc = &reloc_diff.reloc;
if prev_reloc == Some(reloc) {
// Avoid showing consecutive duplicate relocations.
// We do this because a single relocation can span across multiple diffs if the
// bytes in the relocation changed (e.g. first byte is added, second is unchanged).
continue;
}
prev_reloc = Some(reloc);
// TODO: Change hover text color depending on Insert/Delete/Replace kind
// let color = get_color_for_diff_kind(reloc_diff.kind, appearance);
let reloc = resolve_relocation(&obj.symbols, reloc);
out.append(&mut relocation_hover(obj, reloc));
}
out
}
fn data_row_context(
obj: &Object,
diffs: &[(DataDiff, Vec<DataRelocationDiff>)],
) -> Vec<ContextItem> {
let mut out = Vec::new();
let reloc_diffs = diffs.iter().flat_map(|(_, reloc_diffs)| reloc_diffs);
let mut prev_reloc = None;
for reloc_diff in reloc_diffs {
let reloc = &reloc_diff.reloc;
if prev_reloc == Some(reloc) {
// Avoid showing consecutive duplicate relocations.
// We do this because a single relocation can span across multiple diffs if the
// bytes in the relocation changed (e.g. first byte is added, second is unchanged).
continue;
}
prev_reloc = Some(reloc);
let reloc = resolve_relocation(&obj.symbols, reloc);
out.append(&mut relocation_context(obj, reloc, None));
}
out
}
fn data_row_hover_ui( fn data_row_hover_ui(
ui: &mut egui::Ui, ui: &mut egui::Ui,
_obj: &Object, obj: &Object,
_diffs: &[(DataDiff, Vec<DataRelocationDiff>)], diffs: &[(DataDiff, Vec<DataRelocationDiff>)],
_appearance: &Appearance, appearance: &Appearance,
) { ) {
ui.scope(|ui| { ui.scope(|ui| {
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace); ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend); ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
hover_items_ui(ui, data_row_hover(obj, diffs), appearance);
// TODO
// let reloc_diffs = diffs.iter().flat_map(|(_, reloc_diffs)| reloc_diffs);
// let mut prev_reloc = None;
// for reloc_diff in reloc_diffs {
// let reloc = &reloc_diff.reloc;
// if prev_reloc == Some(reloc) {
// // Avoid showing consecutive duplicate relocations.
// // We do this because a single relocation can span across multiple diffs if the
// // bytes in the relocation changed (e.g. first byte is added, second is unchanged).
// continue;
// }
// prev_reloc = Some(reloc);
//
// let color = get_color_for_diff_kind(reloc_diff.kind, appearance);
//
// // TODO: Most of this code is copy-pasted from ins_hover_ui.
// // Try to separate this out into a shared function.
// ui.label(format!("Relocation type: {}", obj.arch.display_reloc(reloc.flags)));
// ui.label(format!("Relocation address: {:x}", reloc.address));
// let addend_str = match reloc.addend.cmp(&0i64) {
// Ordering::Greater => format!("+{:x}", reloc.addend),
// Ordering::Less => format!("-{:x}", -reloc.addend),
// _ => "".to_string(),
// };
// ui.colored_label(color, format!("Name: {}{}", reloc.target.name, addend_str));
// if let Some(orig_section_index) = reloc.target.orig_section_index {
// if let Some(section) =
// obj.sections.iter().find(|s| s.orig_index == orig_section_index)
// {
// ui.colored_label(color, format!("Section: {}", section.name));
// }
// ui.colored_label(
// color,
// format!("Address: {:x}{}", reloc.target.address, addend_str),
// );
// ui.colored_label(color, format!("Size: {:x}", reloc.target.size));
// if reloc.addend >= 0 && reloc.target.bytes.len() > reloc.addend as usize {}
// } else {
// ui.colored_label(color, "Extern".to_string());
// }
// }
}); });
} }
fn data_row_context_menu(ui: &mut egui::Ui, _diffs: &[(DataDiff, Vec<DataRelocationDiff>)]) { fn data_row_context_menu(
ui: &mut egui::Ui,
obj: &Object,
diffs: &[(DataDiff, Vec<DataRelocationDiff>)],
column: usize,
appearance: &Appearance,
) {
ui.scope(|ui| { ui.scope(|ui| {
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace); ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend); ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
context_menu_items_ui(ui, data_row_context(obj, diffs), column, appearance);
// TODO
// let reloc_diffs = diffs.iter().flat_map(|(_, reloc_diffs)| reloc_diffs);
// let mut prev_reloc = None;
// for reloc_diff in reloc_diffs {
// let reloc = &reloc_diff.reloc;
// if prev_reloc == Some(reloc) {
// // Avoid showing consecutive duplicate relocations.
// // We do this because a single relocation can span across multiple diffs if the
// // bytes in the relocation changed (e.g. first byte is added, second is unchanged).
// continue;
// }
// prev_reloc = Some(reloc);
//
// // TODO: This code is copy-pasted from ins_context_menu.
// // Try to separate this out into a shared function.
// if let Some(name) = &reloc.target.demangled_name {
// if ui.button(format!("Copy \"{name}\"")).clicked() {
// ui.output_mut(|output| output.copied_text.clone_from(name));
// ui.close_menu();
// }
// }
// if ui.button(format!("Copy \"{}\"", reloc.target.name)).clicked() {
// ui.output_mut(|output| output.copied_text.clone_from(&reloc.target.name));
// ui.close_menu();
// }
// }
}); });
} }
@@ -113,6 +103,7 @@ pub(crate) fn data_row_ui(
address: usize, address: usize,
diffs: &[(DataDiff, Vec<DataRelocationDiff>)], diffs: &[(DataDiff, Vec<DataRelocationDiff>)],
appearance: &Appearance, appearance: &Appearance,
column: usize,
) { ) {
if diffs.iter().any(|(dd, rds)| { if diffs.iter().any(|(dd, rds)| {
dd.kind != DataDiffKind::None || rds.iter().any(|rd| rd.kind != DataDiffKind::None) dd.kind != DataDiffKind::None || rds.iter().any(|rd| rd.kind != DataDiffKind::None)
@@ -191,9 +182,8 @@ pub(crate) fn data_row_ui(
let response = Label::new(job).sense(Sense::click()).ui(ui); let response = Label::new(job).sense(Sense::click()).ui(ui);
if let Some(obj) = obj { if let Some(obj) = obj {
response response.context_menu(|ui| data_row_context_menu(ui, obj, diffs, column, appearance));
.on_hover_ui_at_pointer(|ui| data_row_hover_ui(ui, obj, diffs, appearance)) response.on_hover_ui_at_pointer(|ui| data_row_hover_ui(ui, obj, diffs, appearance));
.context_menu(|ui| data_row_context_menu(ui, diffs));
} }
} }

View File

@@ -1,9 +1,9 @@
use egui::{text::LayoutJob, Id, Layout, RichText, ScrollArea, TextEdit, Ui, Widget}; use egui::{Id, Layout, RichText, ScrollArea, TextEdit, Ui, Widget, text::LayoutJob};
use objdiff_core::{ use objdiff_core::{
build::BuildStatus, build::BuildStatus,
diff::{ diff::{
display::{ContextItem, HoverItem, HoverItemColor, SymbolFilter, SymbolNavigationKind},
DiffObjConfig, ObjectDiff, SectionDiff, SymbolDiff, DiffObjConfig, ObjectDiff, SectionDiff, SymbolDiff,
display::{ContextItem, HoverItem, HoverItemColor, SymbolFilter, SymbolNavigationKind},
}, },
obj::{Object, Section, Symbol}, obj::{Object, Section, Symbol},
}; };
@@ -14,13 +14,12 @@ use crate::{
views::{ views::{
appearance::Appearance, appearance::Appearance,
column_layout::{render_header, render_strips, render_table}, column_layout::{render_header, render_strips, render_table},
data_diff::{data_row_ui, split_diffs, BYTES_PER_ROW}, data_diff::{BYTES_PER_ROW, data_row_ui, split_diffs},
extab_diff::extab_ui, extab_diff::extab_ui,
function_diff::{asm_col_ui, FunctionDiffContext}, function_diff::{FunctionDiffContext, asm_col_ui},
symbol_diff::{ symbol_diff::{
match_color_for_symbol, symbol_context_menu_ui, symbol_hover_ui, symbol_list_ui,
DiffViewAction, DiffViewNavigation, DiffViewState, SymbolDiffContext, SymbolRefByName, DiffViewAction, DiffViewNavigation, DiffViewState, SymbolDiffContext, SymbolRefByName,
View, View, match_color_for_symbol, symbol_context_menu_ui, symbol_hover_ui, symbol_list_ui,
}, },
write_text, write_text,
}, },
@@ -525,9 +524,23 @@ pub fn diff_view_ui(
let address = i * BYTES_PER_ROW; let address = i * BYTES_PER_ROW;
row.col(|ui| { row.col(|ui| {
if column == 0 { if column == 0 {
data_row_ui(ui, Some(left_obj), address, &left_diffs[i], appearance); data_row_ui(
ui,
Some(left_obj),
address,
&left_diffs[i],
appearance,
column,
);
} else if column == 1 { } else if column == 1 {
data_row_ui(ui, Some(right_obj), address, &right_diffs[i], appearance); data_row_ui(
ui,
Some(right_obj),
address,
&right_diffs[i],
appearance,
column,
);
} }
}); });
}, },
@@ -667,7 +680,7 @@ fn diff_col_ui(
let i = row.index(); let i = row.index();
let address = i * BYTES_PER_ROW; let address = i * BYTES_PER_ROW;
row.col(|ui| { row.col(|ui| {
data_row_ui(ui, Some(obj), address, &diffs[i], appearance); data_row_ui(ui, Some(obj), address, &diffs[i], appearance, column);
}); });
}, },
); );

View File

@@ -1,14 +1,14 @@
use std::{cmp::Ordering, default::Default}; use std::{cmp::Ordering, default::Default};
use egui::{text::LayoutJob, Label, Response, Sense, Widget}; use egui::{Label, Response, Sense, Widget, text::LayoutJob};
use egui_extras::TableRow; use egui_extras::TableRow;
use objdiff_core::{ use objdiff_core::{
diff::{ diff::{
display::{
display_row, instruction_context, instruction_hover, DiffText, DiffTextColor,
DiffTextSegment, HighlightKind,
},
DiffObjConfig, InstructionDiffKind, InstructionDiffRow, ObjectDiff, DiffObjConfig, InstructionDiffKind, InstructionDiffRow, ObjectDiff,
display::{
DiffText, DiffTextColor, DiffTextSegment, HighlightKind, display_row,
instruction_context, instruction_hover,
},
}, },
obj::{InstructionArgValue, InstructionRef, Object}, obj::{InstructionArgValue, InstructionRef, Object},
util::ReallySigned, util::ReallySigned,

View File

@@ -5,7 +5,7 @@ use std::{
}; };
use anyhow::Result; use anyhow::Result;
use egui::{text::LayoutJob, Context, FontId, RichText, TextFormat, TextStyle, Window}; use egui::{Context, FontId, RichText, TextFormat, TextStyle, Window, text::LayoutJob};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::views::{appearance::Appearance, frame_history::FrameHistory}; use crate::views::{appearance::Appearance, frame_history::FrameHistory};

View File

@@ -1,4 +1,4 @@
use egui::{text::LayoutJob, Color32, FontId, TextFormat}; use egui::{Color32, FontId, TextFormat, text::LayoutJob};
pub(crate) mod appearance; pub(crate) mod appearance;
pub(crate) mod column_layout; pub(crate) mod column_layout;

View File

@@ -1,18 +1,18 @@
use std::mem::take; use std::mem::take;
use egui::{ use egui::{
style::ScrollAnimation, text::LayoutJob, CollapsingHeader, Color32, Id, OpenUrl, ScrollArea, CollapsingHeader, Color32, Id, OpenUrl, ScrollArea, SelectableLabel, Ui, Widget,
SelectableLabel, Ui, Widget, style::ScrollAnimation, text::LayoutJob,
}; };
use objdiff_core::{ use objdiff_core::{
diff::{ diff::{
display::{
display_sections, symbol_context, symbol_hover, HighlightKind, SectionDisplay,
SymbolFilter, SymbolNavigationKind,
},
ObjectDiff, SymbolDiff, ObjectDiff, SymbolDiff,
display::{
HighlightKind, SectionDisplay, SymbolFilter, SymbolNavigationKind, display_sections,
symbol_context, symbol_hover,
},
}, },
jobs::{create_scratch::CreateScratchResult, objdiff::ObjDiffResult, Job, JobQueue, JobResult}, jobs::{Job, JobQueue, JobResult, create_scratch::CreateScratchResult, objdiff::ObjDiffResult},
obj::{Object, Section, SectionKind, Symbol, SymbolFlag}, obj::{Object, Section, SectionKind, Symbol, SymbolFlag},
}; };
use regex::{Regex, RegexBuilder}; use regex::{Regex, RegexBuilder};

View File

@@ -1,7 +1,9 @@
[package] [package]
name = "objdiff-wasm" name = "objdiff-wasm"
version.workspace = true version.workspace = true
edition.workspace = true # TODO: Update to 2024
# https://github.com/bytecodealliance/wit-bindgen/pull/1183
edition = "2021"
rust-version.workspace = true rust-version.workspace = true
authors.workspace = true authors.workspace = true
license.workspace = true license.workspace = true

View File

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

View File

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