Compare commits

...

44 Commits

Author SHA1 Message Date
LagoLunatic
e1da90943c Version v3.0.0-beta.14 2025-08-13 01:45:05 -04:00
LagoLunatic
b5713db333 Update dependencies 2025-08-13 01:42:34 -04:00
LagoLunatic
4c3f5e9836 Merge pull request #236 from LagoLunatic/symbol-ctx
Fix context menu not appearing when right clicking the function name in the function diff view
2025-08-11 17:28:26 -04:00
LagoLunatic
e9ce02feb0 Disable double tooltip for elided function name in function diff view 2025-08-08 20:36:48 -04:00
LagoLunatic
9a378d85ed Fix context menu on function name in function diff view 2025-08-08 20:35:16 -04:00
LagoLunatic
c8ff45f2c8 Merge pull request #234 from LagoLunatic/no-diff-data
Fix data section not showing when there is no section on the other side
2025-08-08 20:32:28 -04:00
LagoLunatic
34e4513c69 Fix data section not showing when there is no section on the other side 2025-08-06 16:09:18 -04:00
a015971c20 Use deprecated egui::menu as temp workaround
egui 0.32 refactored menus, and now having a
ComboBox within the menu does not work properly.
Temporarily use the deprecated menu instead.
2025-08-02 13:34:10 -06:00
e67d5998b3 Enable PS instructions for any 32-bit PPC ELF
Fixes an issue where ProDG for GameCube objects
were not enabling PS instruction support.
2025-08-02 11:36:35 -06:00
91bc23edfc Fix objdiff-wasm build 2025-08-02 11:32:04 -06:00
c9c3b32376 Use let chains (a.k.a. cargo clippy --fix) 2025-08-02 11:27:28 -06:00
0dc123b064 Don't fail on line info parsing; use gimli::RelocateReader
Workaround for #228
2025-08-02 11:27:28 -06:00
1e62d4664c Make function size inference logic arch-specific
For MIPS, account for delay slot nops. For x86,
check for trailing nops (0x90). For PPC, check
for 4-byte 0x00 padding.

Resolves #229
2025-08-02 10:56:26 -06:00
1205e8ceb4 Version v3.0.0-beta.12 2025-07-29 21:31:12 -06:00
c917cad5f0 Strip zeros from end of inferred function sizes
Resolves #3
2025-07-29 21:20:47 -06:00
dd653329f5 Fix reading IMAGE_REL_PPC_REFHI/REFLO without PAIR 2025-07-29 20:58:34 -06:00
f5d3d5f10a gui: Split OpenGL into OpenGL (glow), OpenGL ES (wgpu) 2025-07-29 20:49:09 -06:00
c327ed3ea8 Update to egui 0.32 (& update all deps) 2025-07-28 17:30:52 -06:00
Ethan Roseman
85fb18a21a Update rabbitizer to v2.0.0-alpha.4 (#226)
* Update rabbitizer to v2.0.0-alpha.4

* Cargo update
2025-07-27 22:13:42 -06:00
00ad0d8094 Support PPC64 ELFs (PS3); refactor relocation processing 2025-07-21 21:01:03 -06:00
8fac63c42c Remove temporary test 2025-07-17 21:23:12 -06:00
0fb7f3901c Add PPC COFF tests; fix IMAGE_REL_PPC_PAIR handling 2025-07-17 21:19:16 -06:00
Mark Langen
3385f58341 Fix data flow analysis for multiple text sections (#220)
* Fix data flow analysis for multiple text sections

* Data flow analysis results were only keyed by the symbol (function)
  address. That doen't work if there are multiple text sections, the
  result from the first function in one section will stomp the result
  from the first function in another because both have address zero.

* Remove the ambiguity by keying off of the section address as well.

* Formatting

* Satisfy wasm build

* Clippy

* Formatting again

* Thought that section was the section address not the section number.

---------

Co-authored-by: Luke Street <luke.street@encounterpc.com>
2025-07-17 16:04:56 -06:00
60b227f45e Support PowerPC COFF (Xenon, Xbox 360)
ppc750cl is superseded by the powerpc crate, which
supports PPC64, AltiVec and VMX128 extensions.
2025-07-17 13:33:12 -06:00
LagoLunatic
127ae5ae44 PPC pooled relocations: Ignore hidden symbols (#221)
* PPC pooled relocations: Ignore hidden symbols

* PPC pooled relocations: Also ignore 'ignored' symbols
2025-07-07 17:29:05 -06:00
5f48e69775 More clippy fixes 2025-07-07 15:29:30 -06:00
8756eee07b clippy fixes & version bump 2025-07-07 14:56:41 -06:00
bd3ed0d5ad Version v3.0.0-beta.10 2025-06-17 13:03:14 -06:00
Mark Langen
e638d0b17a Implementation of basic data flow analysis for PowerPC (#212)
* WIP implementation

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

* Run cargo insta review

* Apply clippy feedback

* Update more tests.

* Remove std use from ppc flow analysis

* Try to make wasm build work again

* More test changes

* Probably last wasm fix

* Formatting

* Fix WASM

* One more clippy thing

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

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

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

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

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

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

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

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

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

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

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

* Remove unused Eq derive
2025-05-13 14:03:00 -06:00
2d643eb071 Add scratch.preset_id to config.schema.json 2025-05-09 12:51:18 -06:00
71 changed files with 8666 additions and 3225 deletions

View File

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

1586
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -14,9 +14,9 @@ strip = "debuginfo"
codegen-units = 1
[workspace.package]
version = "3.0.0-beta.7"
version = "3.0.0-beta.14"
authors = ["Luke Street <luke@street.dev>"]
edition = "2024"
license = "MIT OR Apache-2.0"
repository = "https://github.com/encounter/objdiff"
rust-version = "1.85"
rust-version = "1.88"

View File

@@ -19,9 +19,9 @@ Supports:
- ARM (GBA, DS, 3DS)
- ARM64 (Switch)
- MIPS (N64, PS1, PS2, PSP)
- PowerPC (GameCube, Wii)
- PowerPC (GameCube, Wii, PS3, Xbox 360)
- SuperH (Saturn, Dreamcast)
- x86 (COFF only)
- x86, x86_64 (PC)
See [Usage](#usage) for more information.

View File

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

View File

@@ -15,11 +15,11 @@ publish = false
[dependencies]
anyhow = "1.0"
argp = "0.4"
crossterm = "0.28"
crossterm = "0.29"
enable-ansi-support = "0.2"
memmap2 = "0.9"
objdiff-core = { path = "../objdiff-core", features = ["all"] }
prost = "0.13"
prost = "0.14"
ratatui = "0.29"
rayon = "1.10"
serde = { version = "1.0", features = ["derive"] }

View File

@@ -203,14 +203,10 @@ fn run_oneshot(
let output_format = OutputFormat::from_option(args.format.as_deref())?;
let (diff_config, mapping_config) = build_config_from_args(args)?;
let target = target_path
.map(|p| {
obj::read::read(p.as_ref(), &diff_config).with_context(|| format!("Loading {}", p))
})
.map(|p| obj::read::read(p.as_ref(), &diff_config).with_context(|| format!("Loading {p}")))
.transpose()?;
let base = base_path
.map(|p| {
obj::read::read(p.as_ref(), &diff_config).with_context(|| format!("Loading {}", p))
})
.map(|p| obj::read::read(p.as_ref(), &diff_config).with_context(|| format!("Loading {p}")))
.transpose()?;
let result =
diff::diff_objs(target.as_ref(), base.as_ref(), None, &diff_config, &mapping_config)?;
@@ -399,7 +395,7 @@ fn run_interactive(
stdout(),
EnterAlternateScreen,
EnableMouseCapture,
SetTitle(format!("{} - objdiff", symbol_name)),
SetTitle(format!("{symbol_name} - objdiff")),
)?;
let backend = CrosstermBackend::new(stdout());
let mut terminal = Terminal::new(backend)?;

View File

@@ -177,16 +177,14 @@ fn report_object(
.target_path
.as_ref()
.map(|p| {
obj::read::read(p.as_ref(), diff_config)
.with_context(|| format!("Failed to open {}", p))
obj::read::read(p.as_ref(), diff_config).with_context(|| format!("Failed to open {p}"))
})
.transpose()?;
let base = object
.base_path
.as_ref()
.map(|p| {
obj::read::read(p.as_ref(), diff_config)
.with_context(|| format!("Failed to open {}", p))
obj::read::read(p.as_ref(), diff_config).with_context(|| format!("Failed to open {p}"))
})
.transpose()?;
let result =
@@ -245,16 +243,17 @@ fn report_object(
for (symbol, symbol_diff) in obj.symbols.iter().zip(&obj_diff.symbols) {
if symbol.section != Some(section_idx)
|| symbol.size == 0
|| symbol.flags.contains(SymbolFlag::Hidden | SymbolFlag::Ignored)
|| symbol.flags.contains(SymbolFlag::Hidden)
|| symbol.flags.contains(SymbolFlag::Ignored)
{
continue;
}
if let Some(existing_functions) = &mut existing_functions {
if symbol.flags.contains(SymbolFlag::Global | SymbolFlag::Weak)
&& !existing_functions.insert(symbol.name.clone())
{
continue;
}
if let Some(existing_functions) = &mut existing_functions
&& (symbol.flags.contains(SymbolFlag::Global)
|| symbol.flags.contains(SymbolFlag::Weak))
&& !existing_functions.insert(symbol.name.clone())
{
continue;
}
let match_percent = symbol_diff.match_percent.unwrap_or_else(|| {
// Support cases where we don't have a target object,
@@ -431,8 +430,8 @@ fn read_report(path: &Utf8PlatformPath) -> Result<Report> {
std::io::stdin().read_to_end(&mut data)?;
return Report::parse(&data).with_context(|| "Failed to load report from stdin");
}
let file = File::open(path).with_context(|| format!("Failed to open {}", path))?;
let file = File::open(path).with_context(|| format!("Failed to open {path}"))?;
let mmap =
unsafe { memmap2::Mmap::map(&file) }.with_context(|| format!("Failed to map {}", path))?;
Report::parse(mmap.as_ref()).with_context(|| format!("Failed to load report {}", path))
unsafe { memmap2::Mmap::map(&file) }.with_context(|| format!("Failed to map {path}"))?;
Report::parse(mmap.as_ref()).with_context(|| format!("Failed to load report {path}"))
}

View File

@@ -87,7 +87,7 @@ impl UiView for FunctionDiffUi {
.and_then(|(_, _, d)| d.match_percent)
{
line_r.spans.push(Span::styled(
format!("{:.2}% ", percent),
format!("{percent:.2}% "),
Style::new().fg(match_percent_color(percent)),
));
}
@@ -97,7 +97,7 @@ impl UiView for FunctionDiffUi {
.and_then(|t| t.format(&state.time_format).ok())
.unwrap_or_else(|| "N/A".to_string());
line_r.spans.push(Span::styled(
format!("Last reload: {}", reload_time),
format!("Last reload: {reload_time}"),
Style::new().fg(Color::White),
));
line_r.spans.push(Span::styled(
@@ -170,32 +170,31 @@ impl UiView for FunctionDiffUi {
let mut prev_text = None;
let mut prev_margin_text = None;
if self.three_way {
if let Some((obj, symbol_idx, symbol_diff)) =
if self.three_way
&& let Some((obj, symbol_idx, symbol_diff)) =
get_symbol(state.prev_obj.as_ref(), self.prev_sym)
{
let mut text = Text::default();
let rect = content_chunks[4].inner(Margin::new(0, 1));
self.print_sym(
&mut text,
obj,
symbol_idx,
symbol_diff,
&state.diff_obj_config,
rect,
&self.right_highlight,
result,
true,
);
max_width = max_width.max(text.width());
prev_text = Some(text);
{
let mut text = Text::default();
let rect = content_chunks[4].inner(Margin::new(0, 1));
self.print_sym(
&mut text,
obj,
symbol_idx,
symbol_diff,
&state.diff_obj_config,
rect,
&self.right_highlight,
result,
true,
);
max_width = max_width.max(text.width());
prev_text = Some(text);
// Render margin
let mut text = Text::default();
let rect = content_chunks[3].inner(Margin::new(1, 1));
self.print_margin(&mut text, symbol_diff, rect);
prev_margin_text = Some(text);
}
// Render margin
let mut text = Text::default();
let rect = content_chunks[3].inner(Margin::new(1, 1));
self.print_margin(&mut text, symbol_diff, rect);
prev_margin_text = Some(text);
}
let max_scroll_x =
@@ -450,11 +449,11 @@ impl UiView for FunctionDiffUi {
fn reload(&mut self, state: &AppState) -> Result<()> {
let left_sym =
state.left_obj.as_ref().and_then(|(o, _)| find_function(o, &self.symbol_name));
state.left_obj.as_ref().and_then(|(o, _)| o.symbol_by_name(&self.symbol_name));
let right_sym =
state.right_obj.as_ref().and_then(|(o, _)| find_function(o, &self.symbol_name));
state.right_obj.as_ref().and_then(|(o, _)| o.symbol_by_name(&self.symbol_name));
let prev_sym =
state.prev_obj.as_ref().and_then(|(o, _)| find_function(o, &self.symbol_name));
state.prev_obj.as_ref().and_then(|(o, _)| o.symbol_by_name(&self.symbol_name));
self.num_rows = match (
get_symbol(state.left_obj.as_ref(), left_sym),
get_symbol(state.right_obj.as_ref(), right_sym),
@@ -538,7 +537,7 @@ impl FunctionDiffUi {
let label_text = match segment.text {
DiffText::Basic(text) => text.to_string(),
DiffText::Line(num) => format!("{num} "),
DiffText::Address(addr) => format!("{:x}:", addr),
DiffText::Address(addr) => format!("{addr:x}:"),
DiffText::Opcode(mnemonic, _op) => format!("{mnemonic} "),
DiffText::Argument(arg) => arg.to_string(),
DiffText::BranchDest(addr) => format!("{addr:x}"),
@@ -546,7 +545,7 @@ impl FunctionDiffUi {
sym.demangled_name.as_ref().unwrap_or(&sym.name).clone()
}
DiffText::Addend(addend) => match addend.cmp(&0i64) {
Ordering::Greater => format!("+{:#x}", addend),
Ordering::Greater => format!("+{addend:#x}"),
Ordering::Less => format!("-{:#x}", -addend),
_ => String::new(),
},
@@ -561,15 +560,18 @@ impl FunctionDiffUi {
let len = label_text.len();
let highlighted =
highlight_kind != HighlightKind::None && *highlight == highlight_kind;
if let Some((cx, cy)) = result.click_xy {
if cx >= sx && cx < sx + len as u16 && cy == sy {
new_highlight = Some(highlight_kind);
}
if let Some((cx, cy)) = result.click_xy
&& cx >= sx
&& cx < sx + len as u16
&& cy == sy
{
new_highlight = Some(highlight_kind);
}
let mut style = Style::new().fg(match segment.color {
DiffTextColor::Normal => Color::Gray,
DiffTextColor::Dim => Color::DarkGray,
DiffTextColor::Bright => Color::White,
DiffTextColor::DataFlow => Color::LightCyan,
DiffTextColor::Replace => Color::Cyan,
DiffTextColor::Delete => Color::Red,
DiffTextColor::Insert => Color::Green,
@@ -650,12 +652,3 @@ fn get_symbol(
let sym = sym?;
Some((obj, sym, &diff.symbols[sym]))
}
fn find_function(obj: &Object, name: &str) -> Option<usize> {
for (symbol_idx, symbol) in obj.symbols.iter().enumerate() {
if symbol.name == name {
return Some(symbol_idx);
}
}
None
}

View File

@@ -41,6 +41,7 @@ any-arch = [
"dep:regex",
"dep:similar",
"dep:syn",
"dep:encoding_rs"
]
bindings = [
"dep:prost",
@@ -61,7 +62,10 @@ config = [
"dep:semver",
"dep:typed-path",
]
dwarf = ["dep:gimli"]
dwarf = [
"dep:gimli",
"dep:typed-arena",
]
serde = [
"dep:pbjson",
"dep:pbjson-build",
@@ -77,6 +81,7 @@ std = [
"prost?/std",
"serde?/std",
"similar?/std",
"typed-arena?/std",
"typed-path?/std",
"dep:filetime",
"dep:memmap2",
@@ -91,7 +96,7 @@ ppc = [
"any-arch",
"dep:cwdemangle",
"dep:cwextab",
"dep:ppc750cl",
"dep:powerpc",
"dep:rlwinmdec",
]
x86 = [
@@ -122,14 +127,14 @@ features = ["all"]
[dependencies]
anyhow = { version = "1.0", default-features = false }
filetime = { version = "0.2", optional = true }
flagset = { version = "0.4", default-features = false, optional = true, git = "https://github.com/enarx/flagset.git", rev = "a1fe9369b3741e43fec45da1998e83b9d78966a2" }
flagset = { version = "0.4", default-features = false, optional = true }
itertools = { version = "0.14", default-features = false, features = ["use_alloc"] }
log = { version = "0.4", default-features = false, optional = true }
memmap2 = { version = "0.9", optional = true }
num-traits = { version = "0.2", default-features = false, optional = true }
object = { git = "https://github.com/gimli-rs/object", rev = "a74579249e21ab8fcd3a86be588de336f18297cb", default-features = false, features = ["read_core", "elf", "pe"] }
pbjson = { version = "0.7", default-features = false, optional = true }
prost = { version = "0.13", default-features = false, features = ["prost-derive"], optional = true }
object = { git = "https://github.com/gimli-rs/object", rev = "16ff70aa6fbd97d6bb7b92375929f4d72414c32b", default-features = false, features = ["read_core", "elf", "coff"] }
pbjson = { version = "0.8", default-features = false, optional = true }
prost = { version = "0.14", default-features = false, features = ["derive"], optional = true }
regex = { version = "1.11", default-features = false, features = [], optional = true }
serde = { version = "1.0", default-features = false, features = ["derive"], optional = true }
similar = { version = "2.7", default-features = false, features = ["hashbrown"], optional = true, git = "https://github.com/encounter/similar.git", branch = "no_std" }
@@ -141,16 +146,17 @@ semver = { version = "1.0", default-features = false, optional = true }
serde_json = { version = "1.0", default-features = false, features = ["alloc"], optional = true }
# dwarf
gimli = { version = "0.31", default-features = false, features = ["read"], optional = true }
gimli = { version = "0.32", default-features = false, features = ["read"], optional = true }
typed-arena = { version = "2.0", default-features = false, optional = true }
# ppc
cwdemangle = { version = "1.0", optional = true }
cwextab = { version = "1.0", optional = true }
ppc750cl = { version = "0.3", optional = true }
cwextab = { version = "1.1", optional = true }
powerpc = { version = "0.4", optional = true }
rlwinmdec = { version = "1.1", optional = true }
# mips
rabbitizer = { version = "2.0.0-alpha.1", default-features = false, features = ["all_extensions"], optional = true }
rabbitizer = { version = "2.0.0-alpha.4", default-features = false, features = ["all_extensions"], optional = true }
# x86
cpp_demangle = { version = "0.4", default-features = false, features = ["alloc"], optional = true }
@@ -158,7 +164,7 @@ iced-x86 = { version = "1.21", default-features = false, features = ["decoder",
msvc-demangler = { version = "0.11", optional = true }
# arm
unarm = { version = "1.8", optional = true }
unarm = { version = "1.9", optional = true }
arm-attr = { version = "0.2", optional = true }
# arm64
@@ -166,15 +172,15 @@ yaxpeax-arch = { version = "0.3", default-features = false, optional = true }
yaxpeax-arm = { version = "0.3", default-features = false, optional = true }
# build
notify = { version = "8.0.0", optional = true }
notify = { version = "8.1.0", optional = true }
notify-debouncer-full = { version = "0.5.0", optional = true }
shell-escape = { version = "0.1", optional = true }
tempfile = { version = "3.19", optional = true }
tempfile = { version = "3.20", optional = true }
time = { version = "0.3", optional = true }
encoding_rs = "0.8.35"
encoding_rs = { version = "0.8.35", optional = true }
[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", optional = true }
winapi = { version = "0.3", optional = true, features = ["winbase"] }
# For Linux static binaries, use rustls
[target.'cfg(target_os = "linux")'.dependencies]
@@ -188,10 +194,10 @@ self_update = { version = "0.42", optional = true }
[build-dependencies]
heck = { version = "0.5", optional = true }
pbjson-build = { version = "0.7", optional = true }
pbjson-build = { version = "0.8", optional = true }
prettyplease = { version = "0.2", optional = true }
proc-macro2 = { version = "1.0", optional = true }
prost-build = { version = "0.13", optional = true }
prost-build = { version = "0.14", optional = true }
quote = { version = "1.0", optional = true }
serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0" }

View File

@@ -11,6 +11,6 @@ objdiff-core contains the core functionality of [objdiff](https://github.com/enc
- **`arm64`**: Enables the ARM64 backend powered by [yaxpeax-arm](https://github.com/iximeow/yaxpeax-arm).
- **`arm`**: Enables the ARM backend powered by [unarm](https://github.com/AetiasHax/unarm).
- **`mips`**: Enables the MIPS backend powered by [rabbitizer](https://github.com/Decompollaborate/rabbitizer).
- **`ppc`**: Enables the PowerPC backend powered by [ppc750cl](https://github.com/encounter/ppc750cl).
- **`ppc`**: Enables the PowerPC backend powered by [powerpc](https://github.com/encounter/powerpc-rs).
- **`superh`**: Enables the SuperH backend powered by an included disassembler.
- **`x86`**: Enables the x86 backend powered by [iced-x86](https://crates.io/crates/iced-x86).

View File

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

View File

@@ -60,10 +60,10 @@ pub struct ConfigGroup {
}
fn build_doc(name: &str, description: Option<&str>) -> TokenStream {
let mut doc = format!(" {}", name);
let mut doc = format!(" {name}");
let mut out = quote! { #[doc = #doc] };
if let Some(description) = description {
doc = format!(" {}", description);
doc = format!(" {description}");
out.extend(quote! { #[doc = ""] });
out.extend(quote! { #[doc = #doc] });
}
@@ -443,9 +443,9 @@ pub fn generate_diff_config() {
}
impl core::fmt::Display for ConfigPropertyValue {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
ConfigPropertyValue::Boolean(value) => write!(f, "{}", value),
ConfigPropertyValue::Choice(value) => write!(f, "{}", value),
match *self {
ConfigPropertyValue::Boolean(value) => write!(f, "{value}"),
ConfigPropertyValue::Choice(value) => f.write_str(value),
}
}
}

View File

@@ -11,7 +11,7 @@ use object::{Endian as _, Object as _, ObjectSection as _, ObjectSymbol as _, el
use unarm::{args, arm, thumb};
use crate::{
arch::Arch,
arch::{Arch, RelocationOverride, RelocationOverrideTarget},
diff::{ArmArchVersion, ArmR9Usage, DiffObjConfig, display::InstructionPart},
obj::{
InstructionRef, Relocation, RelocationFlags, ResolvedInstructionRef, ResolvedRelocation,
@@ -225,7 +225,7 @@ impl Arch for ArchArm {
let mut address = start_addr;
while address < end_addr {
while let Some(next) = next_mapping.take_if(|x| address >= x.address) {
while let Some(next) = next_mapping.filter(|x| address >= x.address) {
// Change mapping
mode = next.mapping;
next_mapping = mappings_iter.next();
@@ -356,47 +356,57 @@ impl Arch for ArchArm {
Ok(())
}
fn implcit_addend(
fn relocation_override(
&self,
_file: &object::File<'_>,
section: &object::Section,
address: u64,
_relocation: &object::Relocation,
flags: RelocationFlags,
) -> Result<i64> {
let section_data = section.data()?;
let address = address as usize;
Ok(match flags {
// ARM calls
RelocationFlags::Elf(elf::R_ARM_PC24)
| RelocationFlags::Elf(elf::R_ARM_XPC25)
| RelocationFlags::Elf(elf::R_ARM_CALL) => {
let data = section_data[address..address + 4].try_into()?;
let addend = self.endianness.read_i32_bytes(data);
let imm24 = addend & 0xffffff;
(imm24 << 2) << 8 >> 8
relocation: &object::Relocation,
) -> Result<Option<RelocationOverride>> {
match relocation.flags() {
// Handle ELF implicit relocations
object::RelocationFlags::Elf { r_type } => {
if relocation.has_implicit_addend() {
let section_data = section.data()?;
let address = address as usize;
let addend = match r_type {
// ARM calls
elf::R_ARM_PC24 | elf::R_ARM_XPC25 | elf::R_ARM_CALL => {
let data = section_data[address..address + 4].try_into()?;
let addend = self.endianness.read_i32_bytes(data);
let imm24 = addend & 0xffffff;
(imm24 << 2) << 8 >> 8
}
// Thumb calls
elf::R_ARM_THM_PC22 | elf::R_ARM_THM_XPC22 => {
let data = section_data[address..address + 2].try_into()?;
let high = self.endianness.read_i16_bytes(data) as i32;
let data = section_data[address + 2..address + 4].try_into()?;
let low = self.endianness.read_i16_bytes(data) as i32;
let imm22 = ((high & 0x7ff) << 11) | (low & 0x7ff);
(imm22 << 1) << 9 >> 9
}
// Data
elf::R_ARM_ABS32 => {
let data = section_data[address..address + 4].try_into()?;
self.endianness.read_i32_bytes(data)
}
flags => bail!("Unsupported ARM implicit relocation {flags:?}"),
};
Ok(Some(RelocationOverride {
target: RelocationOverrideTarget::Keep,
addend: addend as i64,
}))
} else {
Ok(None)
}
}
// Thumb calls
RelocationFlags::Elf(elf::R_ARM_THM_PC22)
| RelocationFlags::Elf(elf::R_ARM_THM_XPC22) => {
let data = section_data[address..address + 2].try_into()?;
let high = self.endianness.read_i16_bytes(data) as i32;
let data = section_data[address + 2..address + 4].try_into()?;
let low = self.endianness.read_i16_bytes(data) as i32;
let imm22 = ((high & 0x7ff) << 11) | (low & 0x7ff);
(imm22 << 1) << 9 >> 9
}
// Data
RelocationFlags::Elf(elf::R_ARM_ABS32) => {
let data = section_data[address..address + 4].try_into()?;
self.endianness.read_i32_bytes(data)
}
flags => bail!("Unsupported ARM implicit relocation {flags:?}"),
} as i64)
_ => Ok(None),
}
}
fn demangle(&self, name: &str) -> Option<String> {
@@ -575,7 +585,7 @@ fn push_args(
arg_cb(InstructionPart::basic("}"))?;
}
args::Argument::CoprocNum(value) => {
arg_cb(InstructionPart::opaque(format!("p{}", value)))?;
arg_cb(InstructionPart::opaque(format!("p{value}")))?;
}
args::Argument::ShiftImm(shift) => {
arg_cb(InstructionPart::opaque(shift.op.to_string()))?;

View File

@@ -5,7 +5,7 @@ use alloc::{
};
use core::cmp::Ordering;
use anyhow::{Result, bail};
use anyhow::Result;
use object::elf;
use yaxpeax_arch::{Arch as YaxpeaxArch, Decoder, Reader, U8Reader};
use yaxpeax_arm::armv8::a64::{
@@ -108,17 +108,6 @@ impl Arch for ArchArm64 {
Ok(())
}
fn implcit_addend(
&self,
_file: &object::File<'_>,
_section: &object::Section,
address: u64,
_relocation: &object::Relocation,
flags: RelocationFlags,
) -> Result<i64> {
bail!("Unsupported ARM64 implicit relocation {:#x}:{:?}", address, flags)
}
fn demangle(&self, name: &str) -> Option<String> {
cpp_demangle::Symbol::new(name)
.ok()
@@ -520,25 +509,25 @@ where Cb: FnMut(InstructionPart<'static>) {
return "ubfx";
}
Opcode::SBFM => {
if let Operand::Immediate(63) = ins.operands[3] {
if let Operand::Register(SizeCode::X, _) = ins.operands[0] {
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[1], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
return "asr";
}
if let Operand::Immediate(63) = ins.operands[3]
&& let Operand::Register(SizeCode::X, _) = ins.operands[0]
{
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[1], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
return "asr";
}
if let Operand::Immediate(31) = ins.operands[3] {
if let Operand::Register(SizeCode::W, _) = ins.operands[0] {
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[1], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
return "asr";
}
if let Operand::Immediate(31) = ins.operands[3]
&& let Operand::Register(SizeCode::W, _) = ins.operands[0]
{
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[1], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
return "asr";
}
if let Operand::Immediate(0) = ins.operands[2] {
let newsrc = if let Operand::Register(_size, srcnum) = ins.operands[1] {
@@ -565,22 +554,21 @@ where Cb: FnMut(InstructionPart<'static>) {
}
if let (Operand::Immediate(imms), Operand::Immediate(immr)) =
(ins.operands[2], ins.operands[3])
&& immr < imms
{
if immr < imms {
let size = if let Operand::Register(size, _) = ins.operands[0] {
if size == SizeCode::W { 32 } else { 64 }
} else {
unreachable!("operand 0 is always a register");
};
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[1], ctx);
push_separator(args);
push_unsigned(args, (size - imms) as u64);
push_separator(args);
push_unsigned(args, (immr + 1) as u64);
return "sbfiz";
}
let size = if let Operand::Register(size, _) = ins.operands[0] {
if size == SizeCode::W { 32 } else { 64 }
} else {
unreachable!("operand 0 is always a register");
};
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[1], ctx);
push_separator(args);
push_unsigned(args, (size - imms) as u64);
push_separator(args);
push_unsigned(args, (immr + 1) as u64);
return "sbfiz";
}
// `sbfm` is never actually displayed: in the remaining case, it is always aliased to `sbfx`
let width = if let (Operand::Immediate(lsb), Operand::Immediate(width)) =
@@ -604,15 +592,14 @@ where Cb: FnMut(InstructionPart<'static>) {
Opcode::EXTR => {
if let (Operand::Register(_, rn), Operand::Register(_, rm)) =
(ins.operands[1], ins.operands[2])
&& rn == rm
{
if rn == rm {
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
push_separator(args);
push_operand(args, &ins.operands[3], ctx);
return "ror";
}
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
push_separator(args);
push_operand(args, &ins.operands[3], ctx);
return "ror";
}
"extr"
}
@@ -815,27 +802,24 @@ where Cb: FnMut(InstructionPart<'static>) {
"csneg"
}
Opcode::CSINC => {
if let (
Operand::Register(_, n),
Operand::Register(_, m),
Operand::ConditionCode(cond),
) = (ins.operands[1], ins.operands[2], ins.operands[3])
if let (Operand::Register(_, n), Operand::Register(_, m), Operand::ConditionCode(cond)) =
(ins.operands[1], ins.operands[2], ins.operands[3])
&& n == m
&& cond < 0b1110
{
if n == m && cond < 0b1110 {
return if n == 31 {
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_condition_code(args, cond ^ 0x01);
"cset"
} else {
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[1], ctx);
push_separator(args);
push_condition_code(args, cond ^ 0x01);
"cinc"
};
}
return if n == 31 {
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_condition_code(args, cond ^ 0x01);
"cset"
} else {
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[1], ctx);
push_separator(args);
push_condition_code(args, cond ^ 0x01);
"cinc"
};
}
"csinc"
}
@@ -1211,15 +1195,13 @@ where Cb: FnMut(InstructionPart<'static>) {
Operand::Register(reg_sz, _),
Operand::SIMDRegisterElementsLane(_, _, elem_sz, _),
) = (ins.operands[0], ins.operands[1])
&& ((reg_sz == SizeCode::W && elem_sz == SIMDSizeCode::S)
|| (reg_sz == SizeCode::X && elem_sz == SIMDSizeCode::D))
{
if (reg_sz == SizeCode::W && elem_sz == SIMDSizeCode::S)
|| (reg_sz == SizeCode::X && elem_sz == SIMDSizeCode::D)
{
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[1], ctx);
return "mov";
}
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[1], ctx);
return "mov";
}
"umov"
}
@@ -1319,14 +1301,15 @@ where Cb: FnMut(InstructionPart<'static>) {
}
}
Opcode::LDADDB(ar) => {
if let Operand::Register(_, rt) = ins.operands[1] {
if rt == 31 && ar & 0b10 == 0b00 {
let inst = if ar & 0b01 == 0b00 { "staddb" } else { "staddlb" };
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
return inst;
}
if let Operand::Register(_, rt) = ins.operands[1]
&& rt == 31
&& ar & 0b10 == 0b00
{
let inst = if ar & 0b01 == 0b00 { "staddb" } else { "staddlb" };
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
return inst;
}
if ar == 0 {
"ldaddb"
@@ -1339,14 +1322,15 @@ where Cb: FnMut(InstructionPart<'static>) {
}
}
Opcode::LDCLRB(ar) => {
if let Operand::Register(_, rt) = ins.operands[1] {
if rt == 31 && ar & 0b10 == 0b00 {
let inst = if ar & 0b01 == 0b00 { "stclrb" } else { "stclrlb" };
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
return inst;
}
if let Operand::Register(_, rt) = ins.operands[1]
&& rt == 31
&& ar & 0b10 == 0b00
{
let inst = if ar & 0b01 == 0b00 { "stclrb" } else { "stclrlb" };
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
return inst;
}
if ar == 0 {
"ldclrb"
@@ -1359,14 +1343,15 @@ where Cb: FnMut(InstructionPart<'static>) {
}
}
Opcode::LDEORB(ar) => {
if let Operand::Register(_, rt) = ins.operands[1] {
if rt == 31 && ar & 0b10 == 0b00 {
let inst = if ar & 0b01 == 0b00 { "steorb" } else { "steorlb" };
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
return inst;
}
if let Operand::Register(_, rt) = ins.operands[1]
&& rt == 31
&& ar & 0b10 == 0b00
{
let inst = if ar & 0b01 == 0b00 { "steorb" } else { "steorlb" };
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
return inst;
}
if ar == 0 {
"ldeorb"
@@ -1379,14 +1364,15 @@ where Cb: FnMut(InstructionPart<'static>) {
}
}
Opcode::LDSETB(ar) => {
if let Operand::Register(_, rt) = ins.operands[1] {
if rt == 31 && ar & 0b10 == 0b00 {
let inst = if ar & 0b01 == 0b00 { "stsetb" } else { "stsetlb" };
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
return inst;
}
if let Operand::Register(_, rt) = ins.operands[1]
&& rt == 31
&& ar & 0b10 == 0b00
{
let inst = if ar & 0b01 == 0b00 { "stsetb" } else { "stsetlb" };
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
return inst;
}
if ar == 0 {
"ldsetb"
@@ -1399,14 +1385,15 @@ where Cb: FnMut(InstructionPart<'static>) {
}
}
Opcode::LDSMAXB(ar) => {
if let Operand::Register(_, rt) = ins.operands[1] {
if rt == 31 && ar & 0b10 == 0b00 {
let inst = if ar & 0b01 == 0b00 { "stsmaxb" } else { "stsmaxlb" };
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
return inst;
}
if let Operand::Register(_, rt) = ins.operands[1]
&& rt == 31
&& ar & 0b10 == 0b00
{
let inst = if ar & 0b01 == 0b00 { "stsmaxb" } else { "stsmaxlb" };
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
return inst;
}
if ar == 0 {
"ldsmaxb"
@@ -1419,14 +1406,15 @@ where Cb: FnMut(InstructionPart<'static>) {
}
}
Opcode::LDSMINB(ar) => {
if let Operand::Register(_, rt) = ins.operands[1] {
if rt == 31 && ar & 0b10 == 0b00 {
let inst = if ar & 0b01 == 0b00 { "stsminb" } else { "stsminlb" };
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
return inst;
}
if let Operand::Register(_, rt) = ins.operands[1]
&& rt == 31
&& ar & 0b10 == 0b00
{
let inst = if ar & 0b01 == 0b00 { "stsminb" } else { "stsminlb" };
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
return inst;
}
if ar == 0 {
"ldsminb"
@@ -1439,14 +1427,15 @@ where Cb: FnMut(InstructionPart<'static>) {
}
}
Opcode::LDUMAXB(ar) => {
if let Operand::Register(_, rt) = ins.operands[1] {
if rt == 31 && ar & 0b10 == 0b00 {
let inst = if ar & 0b01 == 0b00 { "stumaxb" } else { "stumaxlb" };
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
return inst;
}
if let Operand::Register(_, rt) = ins.operands[1]
&& rt == 31
&& ar & 0b10 == 0b00
{
let inst = if ar & 0b01 == 0b00 { "stumaxb" } else { "stumaxlb" };
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
return inst;
}
if ar == 0 {
"ldumaxb"
@@ -1459,14 +1448,15 @@ where Cb: FnMut(InstructionPart<'static>) {
}
}
Opcode::LDUMINB(ar) => {
if let Operand::Register(_, rt) = ins.operands[1] {
if rt == 31 && ar & 0b10 == 0b00 {
let inst = if ar & 0b01 == 0b00 { "stuminb" } else { "stuminlb" };
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
return inst;
}
if let Operand::Register(_, rt) = ins.operands[1]
&& rt == 31
&& ar & 0b10 == 0b00
{
let inst = if ar & 0b01 == 0b00 { "stuminb" } else { "stuminlb" };
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
return inst;
}
// write!(fmt, "{}", self.opcode)?;
if ar == 0 {
@@ -1480,14 +1470,15 @@ where Cb: FnMut(InstructionPart<'static>) {
}
}
Opcode::LDADDH(ar) => {
if let Operand::Register(_, rt) = ins.operands[1] {
if rt == 31 && ar & 0b10 == 0b00 {
let inst = if ar & 0b01 == 0b00 { "staddh" } else { "staddlh" };
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
return inst;
}
if let Operand::Register(_, rt) = ins.operands[1]
&& rt == 31
&& ar & 0b10 == 0b00
{
let inst = if ar & 0b01 == 0b00 { "staddh" } else { "staddlh" };
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
return inst;
}
if ar == 0 {
"ldaddh"
@@ -1500,14 +1491,15 @@ where Cb: FnMut(InstructionPart<'static>) {
}
}
Opcode::LDCLRH(ar) => {
if let Operand::Register(_, rt) = ins.operands[1] {
if rt == 31 && ar & 0b10 == 0b00 {
let inst = if ar & 0b01 == 0b00 { "stclrh" } else { "stclrlh" };
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
return inst;
}
if let Operand::Register(_, rt) = ins.operands[1]
&& rt == 31
&& ar & 0b10 == 0b00
{
let inst = if ar & 0b01 == 0b00 { "stclrh" } else { "stclrlh" };
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
return inst;
}
if ar == 0 {
"ldclrh"
@@ -1520,14 +1512,15 @@ where Cb: FnMut(InstructionPart<'static>) {
}
}
Opcode::LDEORH(ar) => {
if let Operand::Register(_, rt) = ins.operands[1] {
if rt == 31 && ar & 0b10 == 0b00 {
let inst = if ar & 0b01 == 0b00 { "steorh" } else { "steorlh" };
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
return inst;
}
if let Operand::Register(_, rt) = ins.operands[1]
&& rt == 31
&& ar & 0b10 == 0b00
{
let inst = if ar & 0b01 == 0b00 { "steorh" } else { "steorlh" };
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
return inst;
}
if ar == 0 {
"ldeorh"
@@ -1540,14 +1533,15 @@ where Cb: FnMut(InstructionPart<'static>) {
}
}
Opcode::LDSETH(ar) => {
if let Operand::Register(_, rt) = ins.operands[1] {
if rt == 31 && ar & 0b10 == 0b00 {
let inst = if ar & 0b01 == 0b00 { "stseth" } else { "stsetlh" };
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
return inst;
}
if let Operand::Register(_, rt) = ins.operands[1]
&& rt == 31
&& ar & 0b10 == 0b00
{
let inst = if ar & 0b01 == 0b00 { "stseth" } else { "stsetlh" };
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
return inst;
}
if ar == 0 {
"ldseth"
@@ -1560,14 +1554,15 @@ where Cb: FnMut(InstructionPart<'static>) {
}
}
Opcode::LDSMAXH(ar) => {
if let Operand::Register(_, rt) = ins.operands[1] {
if rt == 31 && ar & 0b10 == 0b00 {
let inst = if ar & 0b01 == 0b00 { "stsmaxh" } else { "stsmaxlh" };
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
return inst;
}
if let Operand::Register(_, rt) = ins.operands[1]
&& rt == 31
&& ar & 0b10 == 0b00
{
let inst = if ar & 0b01 == 0b00 { "stsmaxh" } else { "stsmaxlh" };
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
return inst;
}
if ar == 0 {
"ldsmaxh"
@@ -1580,14 +1575,15 @@ where Cb: FnMut(InstructionPart<'static>) {
}
}
Opcode::LDSMINH(ar) => {
if let Operand::Register(_, rt) = ins.operands[1] {
if rt == 31 && ar & 0b10 == 0b00 {
let inst = if ar & 0b01 == 0b00 { "stsminh" } else { "stsminlh" };
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
return inst;
}
if let Operand::Register(_, rt) = ins.operands[1]
&& rt == 31
&& ar & 0b10 == 0b00
{
let inst = if ar & 0b01 == 0b00 { "stsminh" } else { "stsminlh" };
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
return inst;
}
if ar == 0 {
"ldsminh"
@@ -1600,14 +1596,15 @@ where Cb: FnMut(InstructionPart<'static>) {
}
}
Opcode::LDUMAXH(ar) => {
if let Operand::Register(_, rt) = ins.operands[1] {
if rt == 31 && ar & 0b10 == 0b00 {
let inst = if ar & 0b01 == 0b00 { "stumaxh" } else { "stumaxlh" };
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
return inst;
}
if let Operand::Register(_, rt) = ins.operands[1]
&& rt == 31
&& ar & 0b10 == 0b00
{
let inst = if ar & 0b01 == 0b00 { "stumaxh" } else { "stumaxlh" };
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
return inst;
}
if ar == 0 {
"ldumaxh"
@@ -1620,14 +1617,15 @@ where Cb: FnMut(InstructionPart<'static>) {
}
}
Opcode::LDUMINH(ar) => {
if let Operand::Register(_, rt) = ins.operands[1] {
if rt == 31 && ar & 0b10 == 0b00 {
let inst = if ar & 0b01 == 0b00 { "stuminh" } else { "stuminlh" };
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
return inst;
}
if let Operand::Register(_, rt) = ins.operands[1]
&& rt == 31
&& ar & 0b10 == 0b00
{
let inst = if ar & 0b01 == 0b00 { "stuminh" } else { "stuminlh" };
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
return inst;
}
if ar == 0 {
"lduminh"
@@ -1640,14 +1638,15 @@ where Cb: FnMut(InstructionPart<'static>) {
}
}
Opcode::LDADD(ar) => {
if let Operand::Register(_, rt) = ins.operands[1] {
if rt == 31 && ar & 0b10 == 0b00 {
let inst = if ar & 0b01 == 0b00 { "stadd" } else { "staddl" };
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
return inst;
}
if let Operand::Register(_, rt) = ins.operands[1]
&& rt == 31
&& ar & 0b10 == 0b00
{
let inst = if ar & 0b01 == 0b00 { "stadd" } else { "staddl" };
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
return inst;
}
if ar == 0 {
"ldadd"
@@ -1660,14 +1659,15 @@ where Cb: FnMut(InstructionPart<'static>) {
}
}
Opcode::LDCLR(ar) => {
if let Operand::Register(_, rt) = ins.operands[1] {
if rt == 31 && ar & 0b10 == 0b00 {
let inst = if ar & 0b01 == 0b00 { "stclr" } else { "stclrl" };
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
return inst;
}
if let Operand::Register(_, rt) = ins.operands[1]
&& rt == 31
&& ar & 0b10 == 0b00
{
let inst = if ar & 0b01 == 0b00 { "stclr" } else { "stclrl" };
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
return inst;
}
if ar == 0 {
"ldclr"
@@ -1680,14 +1680,15 @@ where Cb: FnMut(InstructionPart<'static>) {
}
}
Opcode::LDEOR(ar) => {
if let Operand::Register(_, rt) = ins.operands[1] {
if rt == 31 && ar & 0b10 == 0b00 {
let inst = if ar & 0b01 == 0b00 { "steor" } else { "steorl" };
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
return inst;
}
if let Operand::Register(_, rt) = ins.operands[1]
&& rt == 31
&& ar & 0b10 == 0b00
{
let inst = if ar & 0b01 == 0b00 { "steor" } else { "steorl" };
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
return inst;
}
if ar == 0 {
"ldeor"
@@ -1700,14 +1701,15 @@ where Cb: FnMut(InstructionPart<'static>) {
}
}
Opcode::LDSET(ar) => {
if let Operand::Register(_, rt) = ins.operands[1] {
if rt == 31 && ar & 0b10 == 0b00 {
let inst = if ar & 0b01 == 0b00 { "stset" } else { "stsetl" };
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
return inst;
}
if let Operand::Register(_, rt) = ins.operands[1]
&& rt == 31
&& ar & 0b10 == 0b00
{
let inst = if ar & 0b01 == 0b00 { "stset" } else { "stsetl" };
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
return inst;
}
if ar == 0 {
"ldset"
@@ -1720,14 +1722,15 @@ where Cb: FnMut(InstructionPart<'static>) {
}
}
Opcode::LDSMAX(ar) => {
if let Operand::Register(_, rt) = ins.operands[1] {
if rt == 31 && ar & 0b10 == 0b00 {
let inst = if ar & 0b01 == 0b00 { "stsmax" } else { "stsmaxl" };
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
return inst;
}
if let Operand::Register(_, rt) = ins.operands[1]
&& rt == 31
&& ar & 0b10 == 0b00
{
let inst = if ar & 0b01 == 0b00 { "stsmax" } else { "stsmaxl" };
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
return inst;
}
if ar == 0 {
"ldsmax"
@@ -1740,14 +1743,15 @@ where Cb: FnMut(InstructionPart<'static>) {
}
}
Opcode::LDSMIN(ar) => {
if let Operand::Register(_, rt) = ins.operands[1] {
if rt == 31 && ar & 0b10 == 0b00 {
let inst = if ar & 0b01 == 0b00 { "stsmin" } else { "stsminl" };
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
return inst;
}
if let Operand::Register(_, rt) = ins.operands[1]
&& rt == 31
&& ar & 0b10 == 0b00
{
let inst = if ar & 0b01 == 0b00 { "stsmin" } else { "stsminl" };
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
return inst;
}
if ar == 0 {
"ldsmin"
@@ -1760,14 +1764,15 @@ where Cb: FnMut(InstructionPart<'static>) {
}
}
Opcode::LDUMAX(ar) => {
if let Operand::Register(_, rt) = ins.operands[1] {
if rt == 31 && ar & 0b10 == 0b00 {
let inst = if ar & 0b01 == 0b00 { "stumax" } else { "stumaxl" };
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
return inst;
}
if let Operand::Register(_, rt) = ins.operands[1]
&& rt == 31
&& ar & 0b10 == 0b00
{
let inst = if ar & 0b01 == 0b00 { "stumax" } else { "stumaxl" };
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
return inst;
}
if ar == 0 {
"ldumax"
@@ -1780,14 +1785,15 @@ where Cb: FnMut(InstructionPart<'static>) {
}
}
Opcode::LDUMIN(ar) => {
if let Operand::Register(_, rt) = ins.operands[1] {
if rt == 31 && ar & 0b10 == 0b00 {
let inst = if ar & 0b01 == 0b00 { "stumin" } else { "stuminl" };
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
return inst;
}
if let Operand::Register(_, rt) = ins.operands[1]
&& rt == 31
&& ar & 0b10 == 0b00
{
let inst = if ar & 0b01 == 0b00 { "stumin" } else { "stuminl" };
push_operand(args, &ins.operands[0], ctx);
push_separator(args);
push_operand(args, &ins.operands[2], ctx);
return inst;
}
if ar == 0 {
"ldumin"
@@ -2078,16 +2084,15 @@ where Cb: FnMut(InstructionPart<'static>) {
/// Relocations that appear in Operand::PCOffset.
fn is_pc_offset_reloc(reloc: Option<ResolvedRelocation>) -> Option<ResolvedRelocation> {
if let Some(resolved) = reloc {
if let RelocationFlags::Elf(
if let Some(resolved) = reloc
&& let RelocationFlags::Elf(
elf::R_AARCH64_ADR_PREL_PG_HI21
| elf::R_AARCH64_JUMP26
| elf::R_AARCH64_CALL26
| elf::R_AARCH64_ADR_GOT_PAGE,
) = resolved.relocation.flags
{
return Some(resolved);
}
{
return Some(resolved);
}
None
}
@@ -2268,7 +2273,7 @@ where Cb: FnMut(InstructionPart<'static>) {
push_plain(args, "]");
push_separator(args);
// TODO does 31 have to be handled separate?
args(InstructionPart::opaque(format!("x{}", offset_reg)));
args(InstructionPart::opaque(format!("x{offset_reg}")));
}
// Fall back to original logic
Operand::SIMDRegister(_, _)

View File

@@ -7,18 +7,15 @@ use alloc::{
use anyhow::{Result, bail};
use object::{Endian as _, Object as _, ObjectSection as _, ObjectSymbol as _, elf};
use rabbitizer::{
IsaExtension, IsaVersion, Vram,
abi::Abi,
operands::{IU16, ValuedOperand},
registers_meta::Register,
IsaExtension, IsaVersion, Vram, abi::Abi, operands::ValuedOperand, registers_meta::Register,
};
use crate::{
arch::Arch,
arch::{Arch, RelocationOverride, RelocationOverrideTarget},
diff::{DiffObjConfig, MipsAbi, MipsInstrCategory, display::InstructionPart},
obj::{
InstructionArg, InstructionArgValue, InstructionRef, Relocation, RelocationFlags,
ResolvedInstructionRef, ResolvedRelocation, SymbolFlag, SymbolFlagSet,
ResolvedInstructionRef, ResolvedRelocation, Section, Symbol, SymbolFlag, SymbolFlagSet,
},
};
@@ -143,6 +140,14 @@ impl ArchMips {
})
}
fn default_instruction_flags(&self) -> rabbitizer::InstructionFlags {
match self.isa_extension {
Some(extension) => rabbitizer::InstructionFlags::new_extension(extension),
None => rabbitizer::InstructionFlags::new(IsaVersion::MIPS_III),
}
.with_abi(self.abi)
}
fn instruction_flags(&self, diff_config: &DiffObjConfig) -> rabbitizer::InstructionFlags {
let isa_extension = match diff_config.mips_instr_category {
MipsInstrCategory::Auto => self.isa_extension,
@@ -154,7 +159,7 @@ impl ArchMips {
};
match isa_extension {
Some(extension) => rabbitizer::InstructionFlags::new_extension(extension),
None => rabbitizer::InstructionFlags::new_isa(IsaVersion::MIPS_III, None),
None => rabbitizer::InstructionFlags::new(IsaVersion::MIPS_III),
}
.with_abi(match diff_config.mips_abi {
MipsAbi::Auto => self.abi,
@@ -225,53 +230,66 @@ impl Arch for ArchMips {
Ok(())
}
fn implcit_addend(
fn relocation_override(
&self,
file: &object::File<'_>,
section: &object::Section,
address: u64,
reloc: &object::Relocation,
flags: RelocationFlags,
) -> Result<i64> {
// Check for paired R_MIPS_HI16 and R_MIPS_LO16 relocations.
if let RelocationFlags::Elf(elf::R_MIPS_HI16 | elf::R_MIPS_LO16) = flags {
if let Some(addend) = self
.paired_relocations
.get(section.index().0)
.and_then(|m| m.get(&address).copied())
{
return Ok(addend);
}
}
relocation: &object::Relocation,
) -> Result<Option<RelocationOverride>> {
match relocation.flags() {
// Handle ELF implicit relocations
object::RelocationFlags::Elf { r_type } => {
if relocation.has_implicit_addend() {
// Check for paired R_MIPS_HI16 and R_MIPS_LO16 relocations.
if let elf::R_MIPS_HI16 | elf::R_MIPS_LO16 = r_type
&& let Some(addend) = self
.paired_relocations
.get(section.index().0)
.and_then(|m| m.get(&address).copied())
{
return Ok(Some(RelocationOverride {
target: RelocationOverrideTarget::Keep,
addend,
}));
}
let data = section.data()?;
let code = data[address as usize..address as usize + 4].try_into()?;
let addend = self.endianness.read_u32_bytes(code);
Ok(match flags {
RelocationFlags::Elf(elf::R_MIPS_32) => addend as i64,
RelocationFlags::Elf(elf::R_MIPS_26) => ((addend & 0x03FFFFFF) << 2) as i64,
RelocationFlags::Elf(elf::R_MIPS_HI16) => ((addend & 0x0000FFFF) << 16) as i32 as i64,
RelocationFlags::Elf(elf::R_MIPS_LO16 | elf::R_MIPS_GOT16 | elf::R_MIPS_CALL16) => {
(addend & 0x0000FFFF) as i16 as i64
}
RelocationFlags::Elf(elf::R_MIPS_GPREL16 | elf::R_MIPS_LITERAL) => {
let object::RelocationTarget::Symbol(idx) = reloc.target() else {
bail!("Unsupported R_MIPS_GPREL16 relocation against a non-symbol");
};
let sym = file.symbol_by_index(idx)?;
let data = section.data()?;
let code = self
.endianness
.read_u32_bytes(data[address as usize..address as usize + 4].try_into()?);
let addend = match r_type {
elf::R_MIPS_32 => code as i64,
elf::R_MIPS_26 => ((code & 0x03FFFFFF) << 2) as i64,
elf::R_MIPS_HI16 => ((code & 0x0000FFFF) << 16) as i32 as i64,
elf::R_MIPS_LO16 | elf::R_MIPS_GOT16 | elf::R_MIPS_CALL16 => {
(code & 0x0000FFFF) as i16 as i64
}
elf::R_MIPS_GPREL16 | elf::R_MIPS_LITERAL => {
let object::RelocationTarget::Symbol(idx) = relocation.target() else {
bail!("Unsupported R_MIPS_GPREL16 relocation against a non-symbol");
};
let sym = file.symbol_by_index(idx)?;
// if the symbol we are relocating against is in a local section we need to add
// the ri_gp_value from .reginfo to the addend.
if sym.section().index().is_some() {
((addend & 0x0000FFFF) as i16 as i64) + self.ri_gp_value as i64
// if the symbol we are relocating against is in a local section we need to add
// the ri_gp_value from .reginfo to the addend.
if sym.section().index().is_some() {
((code & 0x0000FFFF) as i16 as i64) + self.ri_gp_value as i64
} else {
(code & 0x0000FFFF) as i16 as i64
}
}
elf::R_MIPS_PC16 => 0, // PC-relative relocation
R_MIPS15_S3 => ((code & 0x001FFFC0) >> 3) as i64,
flags => bail!("Unsupported MIPS implicit relocation {flags:?}"),
};
Ok(Some(RelocationOverride { target: RelocationOverrideTarget::Keep, addend }))
} else {
(addend & 0x0000FFFF) as i16 as i64
Ok(None)
}
}
RelocationFlags::Elf(elf::R_MIPS_PC16) => 0, // PC-relative relocation
RelocationFlags::Elf(R_MIPS15_S3) => ((addend & 0x001FFFC0) >> 3) as i64,
flags => bail!("Unsupported MIPS implicit relocation {flags:?}"),
})
_ => Ok(None),
}
}
fn demangle(&self, name: &str) -> Option<String> {
@@ -320,6 +338,36 @@ impl Arch for ArchMips {
}
flags
}
fn infer_function_size(
&self,
symbol: &Symbol,
section: &Section,
next_address: u64,
) -> Result<u64> {
// Trim any trailing 4-byte zeroes from the end (nops)
let mut new_address = next_address;
while new_address >= symbol.address + 4
&& let Some(data) = section.data_range(new_address - 4, 4)
&& data == [0u8; 4]
{
new_address -= 4;
}
// Check if the last instruction has a delay slot, if so, include the delay slot nop
if new_address + 4 <= next_address
&& new_address >= symbol.address + 4
&& let Some(data) = section.data_range(new_address - 4, 4)
&& let instruction = rabbitizer::Instruction::new(
self.endianness.read_u32_bytes(data.try_into().unwrap()),
Vram::new((new_address - 4) as u32),
self.default_instruction_flags(),
)
&& instruction.opcode().has_delay_slot()
{
new_address += 4;
}
Ok(new_address.saturating_sub(symbol.address))
}
}
fn push_args(
@@ -335,14 +383,18 @@ fn push_args(
}
match op {
ValuedOperand::core_immediate(imm) => {
ValuedOperand::core_imm_i16(imm) => {
if let Some(resolved) = relocation {
push_reloc(resolved.relocation, &mut arg_cb)?;
} else {
arg_cb(match imm {
IU16::Integer(s) => InstructionPart::signed(s),
IU16::Unsigned(u) => InstructionPart::unsigned(u),
})?;
arg_cb(InstructionPart::signed(imm))?;
}
}
ValuedOperand::core_imm_u16(imm) => {
if let Some(resolved) = relocation {
push_reloc(resolved.relocation, &mut arg_cb)?;
} else {
arg_cb(InstructionPart::unsigned(imm))?;
}
}
ValuedOperand::core_label(..) | ValuedOperand::core_branch_target_label(..) => {
@@ -359,14 +411,13 @@ fn push_args(
))?;
}
}
ValuedOperand::core_immediate_base(imm, base) => {
ValuedOperand::core_imm_rs(imm, base) => {
if let Some(resolved) = relocation {
push_reloc(resolved.relocation, &mut arg_cb)?;
} else {
arg_cb(InstructionPart::Arg(InstructionArg::Value(match imm {
IU16::Integer(s) => InstructionArgValue::Signed(s as i64),
IU16::Unsigned(u) => InstructionArgValue::Unsigned(u as u64),
})))?;
arg_cb(InstructionPart::Arg(InstructionArg::Value(
InstructionArgValue::Signed(imm as i64),
)))?;
}
arg_cb(InstructionPart::basic("("))?;
arg_cb(InstructionPart::opaque(base.either_name(

View File

@@ -1,5 +1,14 @@
use alloc::{borrow::Cow, boxed::Box, format, string::String, vec::Vec};
use core::{ffi::CStr, fmt, fmt::Debug};
use alloc::{
borrow::Cow,
boxed::Box,
format,
string::{String, ToString},
vec::Vec,
};
use core::{
ffi::CStr,
fmt::{self, Debug},
};
use anyhow::{Result, bail};
use encoding_rs::SHIFT_JIS;
@@ -11,8 +20,9 @@ use crate::{
display::{ContextItem, HoverItem, InstructionPart},
},
obj::{
InstructionArg, InstructionRef, Object, ParsedInstruction, Relocation, RelocationFlags,
ResolvedInstructionRef, ResolvedSymbol, Section, Symbol, SymbolFlagSet, SymbolKind,
FlowAnalysisResult, InstructionArg, InstructionRef, Object, ParsedInstruction, Relocation,
RelocationFlags, ResolvedInstructionRef, ResolvedSymbol, Section, Symbol, SymbolFlagSet,
SymbolKind,
},
util::ReallySigned,
};
@@ -31,6 +41,7 @@ pub mod superh;
pub mod x86;
/// Represents the type of data associated with an instruction
#[derive(PartialEq)]
pub enum DataType {
Int8,
Int16,
@@ -44,16 +55,16 @@ pub enum DataType {
impl fmt::Display for DataType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
DataType::Int8 => write!(f, "Int8"),
DataType::Int16 => write!(f, "Int16"),
DataType::Int32 => write!(f, "Int32"),
DataType::Int64 => write!(f, "Int64"),
DataType::Float => write!(f, "Float"),
DataType::Double => write!(f, "Double"),
DataType::Bytes => write!(f, "Bytes"),
DataType::String => write!(f, "String"),
}
f.write_str(match self {
DataType::Int8 => "Int8",
DataType::Int16 => "Int16",
DataType::Int32 => "Int32",
DataType::Int64 => "Int64",
DataType::Float => "Float",
DataType::Double => "Double",
DataType::Bytes => "Bytes",
DataType::String => "String",
})
}
}
@@ -61,8 +72,8 @@ impl DataType {
pub fn display_labels(&self, endian: object::Endianness, bytes: &[u8]) -> Vec<String> {
let mut strs = Vec::new();
for (literal, label_override) in self.display_literals(endian, bytes) {
let label = label_override.unwrap_or_else(|| format!("{}", self));
strs.push(format!("{}: {}", label, literal))
let label = label_override.unwrap_or_else(|| self.to_string());
strs.push(format!("{label}: {literal}"))
}
strs
}
@@ -95,7 +106,7 @@ impl DataType {
match self {
DataType::Int8 => {
let i = i8::from_ne_bytes(bytes.try_into().unwrap());
strs.push((format!("{:#x}", i), None));
strs.push((format!("{i:#x}"), None));
if i < 0 {
strs.push((format!("{:#x}", ReallySigned(i)), None));
@@ -103,7 +114,7 @@ impl DataType {
}
DataType::Int16 => {
let i = endian.read_i16_bytes(bytes.try_into().unwrap());
strs.push((format!("{:#x}", i), None));
strs.push((format!("{i:#x}"), None));
if i < 0 {
strs.push((format!("{:#x}", ReallySigned(i)), None));
@@ -111,7 +122,7 @@ impl DataType {
}
DataType::Int32 => {
let i = endian.read_i32_bytes(bytes.try_into().unwrap());
strs.push((format!("{:#x}", i), None));
strs.push((format!("{i:#x}"), None));
if i < 0 {
strs.push((format!("{:#x}", ReallySigned(i)), None));
@@ -119,7 +130,7 @@ impl DataType {
}
DataType::Int64 => {
let i = endian.read_i64_bytes(bytes.try_into().unwrap());
strs.push((format!("{:#x}", i), None));
strs.push((format!("{i:#x}"), None));
if i < 0 {
strs.push((format!("{:#x}", ReallySigned(i)), None));
@@ -146,16 +157,16 @@ impl DataType {
));
}
DataType::Bytes => {
strs.push((format!("{:#?}", bytes), None));
strs.push((format!("{bytes:#?}"), None));
}
DataType::String => {
if let Ok(cstr) = CStr::from_bytes_until_nul(bytes) {
strs.push((format!("{:?}", cstr), None));
strs.push((format!("{cstr:?}"), None));
}
if let Some(nul_idx) = bytes.iter().position(|&c| c == b'\0') {
let (cow, _, had_errors) = SHIFT_JIS.decode(&bytes[..nul_idx]);
if !had_errors {
let str = format!("{:?}", cow);
let str = format!("{cow:?}");
// Only add the Shift JIS string if it's different from the ASCII string.
if !strs.iter().any(|x| x.0 == str) {
strs.push((str, Some("Shift JIS".into())));
@@ -204,10 +215,10 @@ impl dyn Arch {
// Remove any branch destinations that are outside the function range
for ins in result.iter_mut() {
if let Some(branch_dest) = ins.branch_dest {
if branch_dest < function_start || branch_dest >= function_end {
ins.branch_dest = None;
}
if let Some(branch_dest) = ins.branch_dest
&& (branch_dest < function_start || branch_dest >= function_end)
{
ins.branch_dest = None;
}
}
@@ -335,14 +346,26 @@ pub trait Arch: Send + Sync + Debug {
Vec::new()
}
fn implcit_addend(
// Perform detailed data flow analysis
fn data_flow_analysis(
&self,
file: &object::File<'_>,
section: &object::Section,
address: u64,
relocation: &object::Relocation,
flags: RelocationFlags,
) -> Result<i64>;
_obj: &Object,
_symbol: &Symbol,
_code: &[u8],
_relocations: &[Relocation],
) -> Option<Box<dyn FlowAnalysisResult>> {
None
}
fn relocation_override(
&self,
_file: &object::File<'_>,
_section: &object::Section,
_address: u64,
_relocation: &object::Relocation,
) -> Result<Option<RelocationOverride>> {
Ok(None)
}
fn demangle(&self, _name: &str) -> Option<String> { None }
@@ -383,13 +406,24 @@ pub trait Arch: Send + Sync + Debug {
) -> Vec<ContextItem> {
Vec::new()
}
fn infer_function_size(
&self,
symbol: &Symbol,
_section: &Section,
next_address: u64,
) -> Result<u64> {
Ok(next_address.saturating_sub(symbol.address))
}
}
pub fn new_arch(object: &object::File) -> Result<Box<dyn Arch>> {
use object::Object as _;
Ok(match object.architecture() {
#[cfg(feature = "ppc")]
object::Architecture::PowerPc => Box::new(ppc::ArchPpc::new(object)?),
object::Architecture::PowerPc | object::Architecture::PowerPc64 => {
Box::new(ppc::ArchPpc::new(object)?)
}
#[cfg(feature = "mips")]
object::Architecture::Mips => Box::new(mips::ArchMips::new(object)?),
#[cfg(feature = "x86")]
@@ -434,16 +468,19 @@ impl Arch for ArchDummy {
Ok(())
}
fn implcit_addend(
&self,
_file: &object::File<'_>,
_section: &object::Section,
_address: u64,
_relocation: &object::Relocation,
_flags: RelocationFlags,
) -> Result<i64> {
Ok(0)
}
fn data_reloc_size(&self, _flags: RelocationFlags) -> usize { 0 }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RelocationOverrideTarget {
Keep,
Skip,
Symbol(object::SymbolIndex),
Section(object::SectionIndex),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RelocationOverride {
pub target: RelocationOverrideTarget,
pub addend: i64,
}

View File

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

View File

@@ -1,81 +1,108 @@
use alloc::{
boxed::Box,
collections::{BTreeMap, BTreeSet},
string::{String, ToString},
vec,
vec::Vec,
};
use anyhow::{Result, bail, ensure};
use anyhow::{Result, anyhow, bail, ensure};
use cwextab::{ExceptionTableData, decode_extab};
use flagset::Flags;
use object::{Object as _, ObjectSection as _, ObjectSymbol as _, elf};
use object::{Object as _, ObjectSection as _, ObjectSymbol as _, elf, pe};
use crate::{
arch::{Arch, DataType},
arch::{Arch, DataType, RelocationOverride, RelocationOverrideTarget},
diff::{
DiffObjConfig,
data::resolve_relocation,
display::{ContextItem, HoverItem, HoverItemColor, InstructionPart, SymbolNavigationKind},
},
obj::{
InstructionRef, Object, Relocation, RelocationFlags, ResolvedInstructionRef,
ResolvedRelocation, Symbol, SymbolFlag, SymbolFlagSet,
FlowAnalysisResult, InstructionRef, Object, Relocation, RelocationFlags,
ResolvedInstructionRef, ResolvedRelocation, Section, Symbol, SymbolFlag, SymbolFlagSet,
},
};
mod flow_analysis;
// Relative relocation, can be Simm, Offset or BranchDest
fn is_relative_arg(arg: &ppc750cl::Argument) -> bool {
fn is_relative_arg(arg: &powerpc::Argument) -> bool {
matches!(
arg,
ppc750cl::Argument::Simm(_)
| ppc750cl::Argument::Offset(_)
| ppc750cl::Argument::BranchDest(_)
powerpc::Argument::Simm(_)
| powerpc::Argument::Offset(_)
| powerpc::Argument::BranchDest(_)
)
}
// Relative or absolute relocation, can be Uimm, Simm or Offset
fn is_rel_abs_arg(arg: &ppc750cl::Argument) -> bool {
fn is_rel_abs_arg(arg: &powerpc::Argument) -> bool {
matches!(
arg,
ppc750cl::Argument::Uimm(_) | ppc750cl::Argument::Simm(_) | ppc750cl::Argument::Offset(_)
powerpc::Argument::Uimm(_) | powerpc::Argument::Simm(_) | powerpc::Argument::Offset(_)
)
}
fn is_offset_arg(arg: &ppc750cl::Argument) -> bool { matches!(arg, ppc750cl::Argument::Offset(_)) }
fn is_offset_arg(arg: &powerpc::Argument) -> bool { matches!(arg, powerpc::Argument::Offset(_)) }
#[derive(Debug)]
pub struct ArchPpc {
pub extensions: powerpc::Extensions,
/// Exception info
pub extab: Option<BTreeMap<usize, ExceptionInfo>>,
}
impl ArchPpc {
pub fn new(file: &object::File) -> Result<Self> {
Ok(Self { extab: decode_exception_info(file)? })
let extensions = match file.flags() {
object::FileFlags::Coff { .. } => powerpc::Extensions::xenon(),
object::FileFlags::Elf { e_flags, .. }
if (e_flags & elf::EF_PPC_EMB) == elf::EF_PPC_EMB =>
{
powerpc::Extensions::gekko_broadway()
}
_ => {
if file.is_64() {
powerpc::Extension::Ppc64 | powerpc::Extension::AltiVec
} else {
// Gekko/Broadway objects often use the EF_PPC_EMB flag,
// but ProDG in particular does not emit it.
powerpc::Extensions::gekko_broadway()
}
}
};
let extab = decode_exception_info(file)?;
Ok(Self { extensions, extab })
}
fn parse_ins_ref(&self, resolved: ResolvedInstructionRef) -> Result<ppc750cl::Ins> {
fn parse_ins_ref(&self, resolved: ResolvedInstructionRef) -> Result<powerpc::Ins> {
let mut code = u32::from_be_bytes(resolved.code.try_into()?);
if let Some(reloc) = resolved.relocation {
code = zero_reloc(code, reloc.relocation);
}
let op = ppc750cl::Opcode::from(resolved.ins_ref.opcode as u8);
Ok(ppc750cl::Ins { code, op })
let op = powerpc::Opcode::from(resolved.ins_ref.opcode);
Ok(powerpc::Ins { code, op })
}
fn find_reloc_arg(
&self,
ins: &ppc750cl::ParsedIns,
ins: &powerpc::ParsedIns,
resolved: Option<ResolvedRelocation>,
) -> Option<usize> {
match resolved?.relocation.flags {
RelocationFlags::Elf(elf::R_PPC_EMB_SDA21) => Some(1),
RelocationFlags::Elf(elf::R_PPC_REL24 | elf::R_PPC_REL14) => {
RelocationFlags::Elf(elf::R_PPC_REL24 | elf::R_PPC_REL14)
| RelocationFlags::Coff(pe::IMAGE_REL_PPC_REL24 | pe::IMAGE_REL_PPC_REL14) => {
ins.args.iter().rposition(is_relative_arg)
}
RelocationFlags::Elf(
elf::R_PPC_ADDR16_HI | elf::R_PPC_ADDR16_HA | elf::R_PPC_ADDR16_LO,
) => ins.args.iter().rposition(is_rel_abs_arg),
)
| RelocationFlags::Coff(pe::IMAGE_REL_PPC_REFHI | pe::IMAGE_REL_PPC_REFLO) => {
ins.args.iter().rposition(is_rel_abs_arg)
}
RelocationFlags::Elf(elf::R_PPC64_TOC16) => ins.args.iter().rposition(is_offset_arg),
_ => None,
}
}
@@ -93,11 +120,11 @@ impl Arch for ArchPpc {
ensure!(code.len() & 3 == 0, "Code length must be a multiple of 4");
let ins_count = code.len() / 4;
let mut insts = Vec::<InstructionRef>::with_capacity(ins_count);
for (cur_addr, ins) in ppc750cl::InsIter::new(code, address as u32) {
for (cur_addr, ins) in powerpc::InsIter::new(code, address as u32, self.extensions) {
insts.push(InstructionRef {
address: cur_addr as u64,
size: 4,
opcode: u8::from(ins.op) as u16,
opcode: u16::from(ins.op),
branch_dest: ins.branch_dest(cur_addr).map(u64::from),
});
}
@@ -128,16 +155,16 @@ impl Arch for ArchPpc {
// For @sda21, we can omit the register argument
if matches!(reloc.relocation.flags, RelocationFlags::Elf(elf::R_PPC_EMB_SDA21))
// Sanity check: the next argument should be r0
&& matches!(ins.args.get(idx + 1), Some(ppc750cl::Argument::GPR(ppc750cl::GPR(0))))
&& matches!(ins.args.get(idx + 1), Some(powerpc::Argument::GPR(powerpc::GPR(0))))
{
break;
}
} else {
match arg {
ppc750cl::Argument::Simm(simm) => cb(InstructionPart::signed(simm.0)),
ppc750cl::Argument::Uimm(uimm) => cb(InstructionPart::unsigned(uimm.0)),
ppc750cl::Argument::Offset(offset) => cb(InstructionPart::signed(offset.0)),
ppc750cl::Argument::BranchDest(dest) => cb(InstructionPart::branch_dest(
powerpc::Argument::Simm(simm) => cb(InstructionPart::signed(simm.0)),
powerpc::Argument::Uimm(uimm) => cb(InstructionPart::unsigned(uimm.0)),
powerpc::Argument::Offset(offset) => cb(InstructionPart::signed(offset.0)),
powerpc::Argument::BranchDest(dest) => cb(InstructionPart::branch_dest(
(resolved.ins_ref.address as u32).wrapping_add_signed(dest.0),
)),
_ => cb(InstructionPart::opaque(arg.to_string())),
@@ -157,6 +184,7 @@ impl Arch for ArchPpc {
Ok(())
}
// Could be replaced by data_flow_analysis once that feature stabilizes
fn generate_pooled_relocations(
&self,
address: u64,
@@ -164,22 +192,131 @@ impl Arch for ArchPpc {
relocations: &[Relocation],
symbols: &[Symbol],
) -> Vec<Relocation> {
generate_fake_pool_relocations_for_function(address, code, relocations, symbols)
generate_fake_pool_relocations_for_function(
address,
code,
relocations,
symbols,
self.extensions,
)
}
fn implcit_addend(
fn data_flow_analysis(
&self,
_file: &object::File<'_>,
_section: &object::Section,
address: u64,
_relocation: &object::Relocation,
flags: RelocationFlags,
) -> Result<i64> {
bail!("Unsupported PPC implicit relocation {:#x}:{:?}", address, flags)
obj: &Object,
symbol: &Symbol,
code: &[u8],
relocations: &[Relocation],
) -> Option<Box<dyn FlowAnalysisResult>> {
Some(flow_analysis::ppc_data_flow_analysis(obj, symbol, code, relocations, self.extensions))
}
fn demangle(&self, name: &str) -> Option<String> {
cwdemangle::demangle(name, &cwdemangle::DemangleOptions::default())
fn relocation_override(
&self,
file: &object::File<'_>,
section: &object::Section,
address: u64,
relocation: &object::Relocation,
) -> Result<Option<RelocationOverride>> {
match relocation.flags() {
// IMAGE_REL_PPC_PAIR contains the REF{HI,LO} displacement instead of a symbol index
object::RelocationFlags::Coff {
typ: pe::IMAGE_REL_PPC_REFHI | pe::IMAGE_REL_PPC_REFLO,
} => section
.relocations()
.skip_while(|&(a, _)| a < address)
.take_while(|&(a, _)| a == address)
.find(|(_, reloc)| {
matches!(reloc.flags(), object::RelocationFlags::Coff {
typ: pe::IMAGE_REL_PPC_PAIR
})
})
.map_or(
Ok(Some(RelocationOverride {
target: RelocationOverrideTarget::Keep,
addend: 0,
})),
|(_, reloc)| match reloc.target() {
object::RelocationTarget::Symbol(index) => Ok(Some(RelocationOverride {
target: RelocationOverrideTarget::Keep,
addend: index.0 as u16 as i16 as i64,
})),
target => Err(anyhow!("Unsupported IMAGE_REL_PPC_PAIR target {target:?}")),
},
),
// Skip PAIR relocations as they are handled by the previous case
object::RelocationFlags::Coff { typ: pe::IMAGE_REL_PPC_PAIR } => {
Ok(Some(RelocationOverride { target: RelocationOverrideTarget::Skip, addend: 0 }))
}
// Any other COFF relocation has an addend of 0
object::RelocationFlags::Coff { .. } => {
Ok(Some(RelocationOverride { target: RelocationOverrideTarget::Keep, addend: 0 }))
}
// Handle ELF implicit relocations
flags @ object::RelocationFlags::Elf { r_type } => {
ensure!(
!relocation.has_implicit_addend(),
"Unsupported implicit relocation {:?}",
flags
);
match r_type {
elf::R_PPC64_TOC16 => {
let offset = u64::try_from(relocation.addend())
.map_err(|_| anyhow!("Negative addend for R_PPC64_TOC16 relocation"))?;
let Some(toc_section) = file.section_by_name(".toc") else {
bail!("Missing .toc section for R_PPC64_TOC16 relocation");
};
// If TOC target is a relocation, replace it with the target symbol
let Some((_, toc_relocation)) =
toc_section.relocations().find(|&(a, _)| a == offset)
else {
return Ok(None);
};
if toc_relocation.has_implicit_addend() {
log::warn!(
"Unsupported implicit addend for R_PPC64_TOC16 relocation: {toc_relocation:?}"
);
return Ok(None);
}
let addend = toc_relocation.addend();
match toc_relocation.target() {
object::RelocationTarget::Symbol(symbol_index) => {
Ok(Some(RelocationOverride {
target: RelocationOverrideTarget::Symbol(symbol_index),
addend,
}))
}
object::RelocationTarget::Section(section_index) => {
Ok(Some(RelocationOverride {
target: RelocationOverrideTarget::Section(section_index),
addend,
}))
}
target => {
log::warn!(
"Unsupported R_PPC64_TOC16 relocation target {target:?}"
);
Ok(None)
}
}
}
_ => Ok(None),
}
}
_ => Ok(None),
}
}
fn demangle(&self, mut name: &str) -> Option<String> {
if name.starts_with('?') {
msvc_demangler::demangle(name, msvc_demangler::DemangleFlags::llvm()).ok()
} else {
name = name.trim_start_matches('.');
cpp_demangle::Symbol::new(name)
.ok()
.and_then(|s| s.demangle(&cpp_demangle::DemangleOptions::default()).ok())
.or_else(|| cwdemangle::demangle(name, &cwdemangle::DemangleOptions::default()))
}
}
fn reloc_name(&self, flags: RelocationFlags) -> Option<&'static str> {
@@ -194,9 +331,18 @@ impl Arch for ArchPpc {
elf::R_PPC_UADDR32 => Some("R_PPC_UADDR32"),
elf::R_PPC_REL24 => Some("R_PPC_REL24"),
elf::R_PPC_REL14 => Some("R_PPC_REL14"),
elf::R_PPC64_TOC16 => Some("R_PPC64_TOC16"),
_ => None,
},
RelocationFlags::Coff(r_type) => match r_type {
pe::IMAGE_REL_PPC_ADDR32 => Some("IMAGE_REL_PPC_ADDR32"),
pe::IMAGE_REL_PPC_REFHI => Some("IMAGE_REL_PPC_REFHI"),
pe::IMAGE_REL_PPC_REFLO => Some("IMAGE_REL_PPC_REFLO"),
pe::IMAGE_REL_PPC_REL24 => Some("IMAGE_REL_PPC_REL24"),
pe::IMAGE_REL_PPC_REL14 => Some("IMAGE_REL_PPC_REL14"),
pe::IMAGE_REL_PPC_PAIR => Some("IMAGE_REL_PPC_PAIR"),
_ => None,
},
_ => None,
}
}
@@ -224,8 +370,8 @@ impl Arch for ArchPpc {
// Pooled string.
return Some(DataType::String);
}
let opcode = ppc750cl::Opcode::from(resolved.ins_ref.opcode as u8);
if let Some(ty) = guess_data_type_from_load_store_inst_op(opcode) {
let opcode = powerpc::Opcode::from(resolved.ins_ref.opcode);
if let Some(ty) = flow_analysis::guess_data_type_from_load_store_inst_op(opcode) {
// Numeric type.
return Some(ty);
}
@@ -311,6 +457,22 @@ impl Arch for ArchPpc {
}
out
}
fn infer_function_size(
&self,
symbol: &Symbol,
section: &Section,
mut next_address: u64,
) -> Result<u64> {
// Trim any trailing 4-byte zeroes from the end (padding)
while next_address >= symbol.address + 4
&& let Some(data) = section.data_range(next_address - 4, 4)
&& data == [0u8; 4]
{
next_address -= 4;
}
Ok(next_address.saturating_sub(symbol.address))
}
}
impl ArchPpc {
@@ -322,11 +484,18 @@ impl ArchPpc {
fn zero_reloc(code: u32, reloc: &Relocation) -> u32 {
match reloc.flags {
RelocationFlags::Elf(elf::R_PPC_EMB_SDA21) => code & !0x1FFFFF,
RelocationFlags::Elf(elf::R_PPC_REL24) => code & !0x3FFFFFC,
RelocationFlags::Elf(elf::R_PPC_REL14) => code & !0xFFFC,
RelocationFlags::Elf(elf::R_PPC_REL24) | RelocationFlags::Coff(pe::IMAGE_REL_PPC_REL24) => {
code & !0x3FFFFFC
}
RelocationFlags::Elf(elf::R_PPC_REL14) | RelocationFlags::Coff(pe::IMAGE_REL_PPC_REL14) => {
code & !0xFFFC
}
RelocationFlags::Elf(
elf::R_PPC_ADDR16_HI | elf::R_PPC_ADDR16_HA | elf::R_PPC_ADDR16_LO,
) => code & !0xFFFF,
)
| RelocationFlags::Coff(pe::IMAGE_REL_PPC_REFHI | pe::IMAGE_REL_PPC_REFLO) => {
code & !0xFFFF
}
_ => code,
}
}
@@ -336,34 +505,38 @@ fn display_reloc(
cb: &mut dyn FnMut(InstructionPart) -> Result<()>,
) -> Result<()> {
match resolved.relocation.flags {
RelocationFlags::Elf(r_type) => match r_type {
elf::R_PPC_ADDR16_LO => {
cb(InstructionPart::reloc())?;
cb(InstructionPart::basic("@l"))?;
}
elf::R_PPC_ADDR16_HI => {
cb(InstructionPart::reloc())?;
cb(InstructionPart::basic("@h"))?;
}
elf::R_PPC_ADDR16_HA => {
cb(InstructionPart::reloc())?;
cb(InstructionPart::basic("@ha"))?;
}
elf::R_PPC_EMB_SDA21 => {
cb(InstructionPart::reloc())?;
cb(InstructionPart::basic("@sda21"))?;
}
elf::R_PPC_ADDR32 | elf::R_PPC_UADDR32 | elf::R_PPC_REL24 | elf::R_PPC_REL14 => {
cb(InstructionPart::reloc())?;
}
elf::R_PPC_NONE => {
// Fake pool relocation.
cb(InstructionPart::basic("<"))?;
cb(InstructionPart::reloc())?;
cb(InstructionPart::basic(">"))?;
}
_ => cb(InstructionPart::reloc())?,
},
RelocationFlags::Elf(elf::R_PPC_ADDR16_LO)
| RelocationFlags::Coff(pe::IMAGE_REL_PPC_REFLO) => {
cb(InstructionPart::reloc())?;
cb(InstructionPart::basic("@l"))?;
}
RelocationFlags::Elf(elf::R_PPC_ADDR16_HI)
| RelocationFlags::Coff(pe::IMAGE_REL_PPC_REFHI) => {
cb(InstructionPart::reloc())?;
cb(InstructionPart::basic("@h"))?;
}
RelocationFlags::Elf(elf::R_PPC_ADDR16_HA) => {
cb(InstructionPart::reloc())?;
cb(InstructionPart::basic("@ha"))?;
}
RelocationFlags::Elf(elf::R_PPC_EMB_SDA21) => {
cb(InstructionPart::reloc())?;
cb(InstructionPart::basic("@sda21"))?;
}
RelocationFlags::Elf(
elf::R_PPC_ADDR32 | elf::R_PPC_UADDR32 | elf::R_PPC_REL24 | elf::R_PPC_REL14,
)
| RelocationFlags::Coff(
pe::IMAGE_REL_PPC_ADDR32 | pe::IMAGE_REL_PPC_REL24 | pe::IMAGE_REL_PPC_REL14,
) => {
cb(InstructionPart::reloc())?;
}
RelocationFlags::Elf(elf::R_PPC_NONE) => {
// Fake pool relocation.
cb(InstructionPart::basic("<"))?;
cb(InstructionPart::reloc())?;
cb(InstructionPart::basic(">"))?;
}
_ => cb(InstructionPart::reloc())?,
};
Ok(())
@@ -447,16 +620,14 @@ fn decode_exception_info(
// Decode the extab data
let Some(extab_data) = extab_section.data_range(extab_start_addr, extab.size())? else {
log::warn!("Failed to get extab data for function {}", extab_func_name);
log::warn!("Failed to get extab data for function {extab_func_name}");
continue;
};
let data = match decode_extab(extab_data) {
Ok(decoded_data) => decoded_data,
Err(e) => {
log::warn!(
"Exception table decoding failed for function {}, reason: {}",
extab_func_name,
e
"Exception table decoding failed for function {extab_func_name}, reason: {e}"
);
return Ok(None);
}
@@ -501,42 +672,23 @@ fn make_symbol_ref(symbol: &object::Symbol) -> Result<ExtabSymbolRef> {
Ok(ExtabSymbolRef { original_index: symbol.index().0 - 1, name, demangled_name })
}
fn guess_data_type_from_load_store_inst_op(inst_op: ppc750cl::Opcode) -> Option<DataType> {
use ppc750cl::Opcode;
match inst_op {
Opcode::Lbz | Opcode::Lbzu | Opcode::Lbzux | Opcode::Lbzx => Some(DataType::Int8),
Opcode::Lhz | Opcode::Lhzu | Opcode::Lhzux | Opcode::Lhzx => Some(DataType::Int16),
Opcode::Lha | Opcode::Lhau | Opcode::Lhaux | Opcode::Lhax => Some(DataType::Int16),
Opcode::Lwz | Opcode::Lwzu | Opcode::Lwzux | Opcode::Lwzx => Some(DataType::Int32),
Opcode::Lfs | Opcode::Lfsu | Opcode::Lfsux | Opcode::Lfsx => Some(DataType::Float),
Opcode::Lfd | Opcode::Lfdu | Opcode::Lfdux | Opcode::Lfdx => Some(DataType::Double),
Opcode::Stb | Opcode::Stbu | Opcode::Stbux | Opcode::Stbx => Some(DataType::Int8),
Opcode::Sth | Opcode::Sthu | Opcode::Sthux | Opcode::Sthx => Some(DataType::Int16),
Opcode::Stw | Opcode::Stwu | Opcode::Stwux | Opcode::Stwx => Some(DataType::Int32),
Opcode::Stfs | Opcode::Stfsu | Opcode::Stfsux | Opcode::Stfsx => Some(DataType::Float),
Opcode::Stfd | Opcode::Stfdu | Opcode::Stfdux | Opcode::Stfdx => Some(DataType::Double),
_ => None,
}
}
#[derive(Debug)]
struct PoolReference {
addr_src_gpr: ppc750cl::GPR,
addr_src_gpr: powerpc::GPR,
addr_offset: i16,
addr_dst_gpr: Option<ppc750cl::GPR>,
addr_dst_gpr: Option<powerpc::GPR>,
}
// 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,
simplified: &ppc750cl::ParsedIns,
opcode: powerpc::Opcode,
simplified: &powerpc::ParsedIns,
) -> Option<PoolReference> {
use ppc750cl::{Argument, Opcode};
use powerpc::{Argument, Opcode};
let args = &simplified.args;
if guess_data_type_from_load_store_inst_op(opcode).is_some() {
if flow_analysis::guess_data_type_from_load_store_inst_op(opcode).is_some() {
match (args[1], args[2]) {
(Argument::Offset(offset), Argument::GPR(addr_src_gpr)) => {
// e.g. lwz. Immediate offset.
@@ -599,15 +751,15 @@ fn get_pool_reference_for_inst(
// 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.
fn clear_overwritten_gprs(ins: ppc750cl::Ins, gpr_pool_relocs: &mut BTreeMap<u8, Relocation>) {
use ppc750cl::{Argument, Arguments, Opcode};
fn clear_overwritten_gprs(ins: powerpc::Ins, gpr_pool_relocs: &mut BTreeMap<u8, Relocation>) {
use powerpc::{Argument, Arguments, Opcode};
let mut def_args = Arguments::default();
ins.parse_defs(&mut def_args);
for arg in def_args {
if let Argument::GPR(gpr) = arg {
if ins.op == Opcode::Lmw {
// `lmw` overwrites all registers from rd to r31.
// ppc750cl only returns rd itself, so we manually clear the rest of them.
// powerpc only returns rd itself, so we manually clear the rest of them.
for reg in gpr.0..31 {
gpr_pool_relocs.remove(&reg);
}
@@ -640,6 +792,8 @@ fn make_fake_pool_reloc(
target_symbol = symbols.iter().position(|s| {
s.section == Some(section_index)
&& s.size > 0
&& !s.flags.contains(SymbolFlag::Hidden)
&& !s.flags.contains(SymbolFlag::Ignored)
&& (s.address..s.address + s.size).contains(&target_address)
})?;
addend = target_address.checked_sub(symbols[target_symbol].address)? as i64;
@@ -668,7 +822,7 @@ fn make_fake_pool_reloc(
// and returns a Vec of "fake pool relocations" that simulate what a relocation for that instruction
// would look like if data hadn't been pooled.
// This method tries to follow the function's proper control flow. It keeps track of a queue of
// states it hasn't traversed yet, where each state holds an instruction address and a HashMap of
// states it hasn't traversed yet, where each state holds an instruction address and a map of
// which registers hold which pool relocations at that point.
// When a conditional or unconditional branch is encountered, the destination of the branch is added
// to the queue. Conditional branches will traverse both the path where the branch is taken and the
@@ -687,12 +841,13 @@ fn generate_fake_pool_relocations_for_function(
code: &[u8],
relocations: &[Relocation],
symbols: &[Symbol],
extensions: powerpc::Extensions,
) -> Vec<Relocation> {
use ppc750cl::{Argument, InsIter, Opcode};
use powerpc::{Argument, InsIter, Opcode};
let mut visited_ins_addrs = BTreeSet::new();
let mut pool_reloc_for_addr = BTreeMap::new();
let mut ins_iters_with_gpr_state =
vec![(InsIter::new(code, func_address as u32), BTreeMap::new())];
vec![(InsIter::new(code, func_address as u32, extensions), BTreeMap::new())];
let mut gpr_state_at_bctr = BTreeMap::new();
while let Some((ins_iter, mut gpr_pool_relocs)) = ins_iters_with_gpr_state.pop() {
for (cur_addr, ins) in ins_iter {
@@ -713,44 +868,43 @@ fn generate_fake_pool_relocations_for_function(
break;
}
}
if let Some(branch_dest) = branch_dest {
if branch_dest >= func_address as u32
&& (branch_dest - func_address as u32) < code.len() as u32
{
let dest_offset_into_func = branch_dest - func_address as u32;
let dest_code_slice = &code[dest_offset_into_func as usize..];
match ins.op {
Opcode::Bc => {
// Conditional branch.
// Add the branch destination to the queue to do later.
if let Some(branch_dest) = branch_dest
&& branch_dest >= func_address as u32
&& (branch_dest - func_address as u32) < code.len() as u32
{
let dest_offset_into_func = branch_dest - func_address as u32;
let dest_code_slice = &code[dest_offset_into_func as usize..];
match ins.op {
Opcode::Bc => {
// Conditional branch.
// Add the branch destination to the queue to do later.
ins_iters_with_gpr_state.push((
InsIter::new(dest_code_slice, branch_dest, extensions),
gpr_pool_relocs.clone(),
));
// Then continue on with the current iterator.
}
Opcode::B => {
if simplified.mnemonic != "bl" {
// Unconditional branch.
// Add the branch destination to the queue.
ins_iters_with_gpr_state.push((
InsIter::new(dest_code_slice, branch_dest),
InsIter::new(dest_code_slice, branch_dest, extensions),
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.
// Add the branch destination to the queue.
ins_iters_with_gpr_state.push((
InsIter::new(dest_code_slice, branch_dest),
gpr_pool_relocs.clone(),
));
// Break out of the current iterator so we can do the newly added one.
break;
}
}
_ => unreachable!(),
}
_ => unreachable!(),
}
}
if let Opcode::Bcctr = ins.op {
if simplified.mnemonic == "bctr" {
// Unconditional branch to count register.
// Likely a jump table.
gpr_state_at_bctr.insert(cur_addr, gpr_pool_relocs.clone());
}
if let Opcode::Bcctr = ins.op
&& simplified.mnemonic == "bctr"
{
// Unconditional branch to count register.
// Likely a jump table.
gpr_state_at_bctr.insert(cur_addr, gpr_pool_relocs.clone());
}
// Then handle keeping track of which GPR contains which pool relocation.
@@ -842,7 +996,7 @@ fn generate_fake_pool_relocations_for_function(
let dest_offset_into_func = unseen_addr - func_address as u32;
let dest_code_slice = &code[dest_offset_into_func as usize..];
ins_iters_with_gpr_state.push((
InsIter::new(dest_code_slice, unseen_addr),
InsIter::new(dest_code_slice, unseen_addr, extensions),
gpr_pool_relocs.clone(),
));
break;

View File

@@ -197,7 +197,7 @@ fn match_ni_f(
}
_ => {
parts.push(InstructionPart::basic(".word 0x"));
parts.push(InstructionPart::basic(format!("{:04X}", op)));
parts.push(InstructionPart::basic(format!("{op:04X}")));
parts.push(InstructionPart::basic(" /* unknown instruction */"));
}
}

View File

@@ -1,6 +1,6 @@
use alloc::{collections::BTreeMap, format, string::String, vec, vec::Vec};
use anyhow::{Result, bail};
use anyhow::Result;
use object::elf;
use crate::{
@@ -109,7 +109,7 @@ impl Arch for ArchSuperH {
);
parts.push(InstructionPart::basic(" /* "));
parts.push(InstructionPart::basic("0x"));
parts.push(InstructionPart::basic(format!("{:04X}", data)));
parts.push(InstructionPart::basic(format!("{data:04X}")));
parts.push(InstructionPart::basic(" */"));
} else if value.size == 4 && value.address as usize + 3 < symbol_data.len() {
let data = u32::from_be_bytes(
@@ -119,7 +119,7 @@ impl Arch for ArchSuperH {
);
parts.push(InstructionPart::basic(" /* "));
parts.push(InstructionPart::basic("0x"));
parts.push(InstructionPart::basic(format!("{:08X}", data)));
parts.push(InstructionPart::basic(format!("{data:08X}")));
parts.push(InstructionPart::basic(" */"));
}
}
@@ -132,17 +132,6 @@ impl Arch for ArchSuperH {
Ok(())
}
fn implcit_addend(
&self,
_file: &object::File<'_>,
_section: &object::Section,
address: u64,
_relocation: &object::Relocation,
flags: RelocationFlags,
) -> Result<i64> {
bail!("Unsupported SuperH implicit relocation {:#x}:{:?}", address, flags)
}
fn demangle(&self, name: &str) -> Option<String> {
cpp_demangle::Symbol::new(name)
.ok()
@@ -214,10 +203,10 @@ mod test {
impl Display for InstructionPart<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
InstructionPart::Basic(s) => write!(f, "{}", s),
InstructionPart::Opcode(s, _o) => write!(f, "{} ", s),
InstructionPart::Arg(arg) => write!(f, "{}", arg),
InstructionPart::Separator => write!(f, ", "),
InstructionPart::Basic(s) => f.write_str(s),
InstructionPart::Opcode(s, _o) => write!(f, "{s} "),
InstructionPart::Arg(arg) => write!(f, "{arg}"),
InstructionPart::Separator => f.write_str(", "),
}
}
}
@@ -225,9 +214,9 @@ mod test {
impl Display for InstructionArg<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
InstructionArg::Value(v) => write!(f, "{}", v),
InstructionArg::BranchDest(v) => write!(f, "{}", v),
InstructionArg::Reloc => write!(f, "reloc"),
InstructionArg::Value(v) => write!(f, "{v}"),
InstructionArg::BranchDest(v) => write!(f, "{v}"),
InstructionArg::Reloc => f.write_str("reloc"),
}
}
}
@@ -264,7 +253,7 @@ mod test {
)
.unwrap();
let joined_str: String = parts.iter().map(|part| format!("{}", part)).collect();
let joined_str: String = parts.iter().map(<_>::to_string).collect();
assert_eq!(joined_str, expected_str.to_string());
}
}
@@ -342,7 +331,7 @@ mod test {
)
.unwrap();
let joined_str: String = parts.iter().map(|part| format!("{}", part)).collect();
let joined_str: String = parts.iter().map(<_>::to_string).collect();
assert_eq!(joined_str, expected_str.to_string());
}
}
@@ -425,7 +414,7 @@ mod test {
)
.unwrap();
let joined_str: String = parts.iter().map(|part| format!("{}", part)).collect();
let joined_str: String = parts.iter().map(<_>::to_string).collect();
assert_eq!(joined_str, expected_str.to_string());
}
}
@@ -462,7 +451,7 @@ mod test {
)
.unwrap();
let joined_str: String = parts.iter().map(|part| format!("{}", part)).collect();
let joined_str: String = parts.iter().map(<_>::to_string).collect();
assert_eq!(joined_str, expected_str.to_string());
}
}
@@ -516,7 +505,7 @@ mod test {
)
.unwrap();
let joined_str: String = parts.iter().map(|part| format!("{}", part)).collect();
let joined_str: String = parts.iter().map(<_>::to_string).collect();
assert_eq!(joined_str, expected_str.to_string());
}
}
@@ -557,7 +546,7 @@ mod test {
)
.unwrap();
let joined_str: String = parts.iter().map(|part| format!("{}", part)).collect();
let joined_str: String = parts.iter().map(<_>::to_string).collect();
assert_eq!(joined_str, expected_str.to_string());
}
}
@@ -601,7 +590,7 @@ mod test {
)
.unwrap();
let joined_str: String = parts.iter().map(|part| format!("{}", part)).collect();
let joined_str: String = parts.iter().map(<_>::to_string).collect();
assert_eq!(joined_str, expected_str.to_string());
}
}
@@ -645,7 +634,7 @@ mod test {
)
.unwrap();
let joined_str: String = parts.iter().map(|part| format!("{}", part)).collect();
let joined_str: String = parts.iter().map(<_>::to_string).collect();
assert_eq!(joined_str, expected_str.to_string());
}
}
@@ -682,7 +671,7 @@ mod test {
)
.unwrap();
let joined_str: String = parts.iter().map(|part| format!("{}", part)).collect();
let joined_str: String = parts.iter().map(<_>::to_string).collect();
assert_eq!(joined_str, expected_str.to_string());
}
}
@@ -716,7 +705,7 @@ mod test {
)
.unwrap();
let joined_str: String = parts.iter().map(|part| format!("{}", part)).collect();
let joined_str: String = parts.iter().map(<_>::to_string).collect();
assert_eq!(joined_str, expected_str.to_string());
}
}
@@ -764,7 +753,7 @@ mod test {
)
.unwrap();
let joined_str: String = parts.iter().map(|part| format!("{}", part)).collect();
let joined_str: String = parts.iter().map(<_>::to_string).collect();
assert_eq!(joined_str, expected_str.to_string());
}
}
@@ -814,7 +803,7 @@ mod test {
)
.unwrap();
let joined_str: String = parts.iter().map(|part| format!("{}", part)).collect();
let joined_str: String = parts.iter().map(<_>::to_string).collect();
assert_eq!(joined_str, expected_str.to_string());
}
}

View File

@@ -6,12 +6,12 @@ use iced_x86::{
Decoder, DecoderOptions, DecoratorKind, FormatterOutput, FormatterTextKind, GasFormatter,
Instruction, IntelFormatter, MasmFormatter, NasmFormatter, NumberKind, OpKind, Register,
};
use object::{Endian as _, Object as _, ObjectSection as _, pe};
use object::{Endian as _, Object as _, ObjectSection as _, elf, pe};
use crate::{
arch::Arch,
arch::{Arch, RelocationOverride, RelocationOverrideTarget},
diff::{DiffObjConfig, X86Formatter, display::InstructionPart},
obj::{InstructionRef, Relocation, RelocationFlags, ResolvedInstructionRef},
obj::{InstructionRef, Relocation, RelocationFlags, ResolvedInstructionRef, Section, Symbol},
};
#[derive(Debug)]
@@ -67,7 +67,11 @@ impl ArchX86 {
pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32 => Some(4),
_ => None,
},
_ => None,
RelocationFlags::Elf(typ) => match typ {
elf::R_386_32 | elf::R_386_PC32 => Some(4),
elf::R_386_16 => Some(2),
_ => None,
},
},
Architecture::X86_64 => match flags {
RelocationFlags::Coff(typ) => match typ {
@@ -75,7 +79,11 @@ impl ArchX86 {
pe::IMAGE_REL_AMD64_ADDR64 => Some(8),
_ => None,
},
_ => None,
RelocationFlags::Elf(typ) => match typ {
elf::R_X86_64_PC32 => Some(4),
elf::R_X86_64_64 => Some(8),
_ => None,
},
},
}
}
@@ -217,37 +225,47 @@ impl Arch for ArchX86 {
Ok(())
}
fn implcit_addend(
fn relocation_override(
&self,
_file: &object::File<'_>,
section: &object::Section,
address: u64,
_relocation: &object::Relocation,
flags: RelocationFlags,
) -> Result<i64> {
match self.arch {
Architecture::X86 => match flags {
RelocationFlags::Coff(pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32) => {
relocation: &object::Relocation,
) -> Result<Option<RelocationOverride>> {
if !relocation.has_implicit_addend() {
return Ok(None);
}
let addend = match self.arch {
Architecture::X86 => match relocation.flags() {
object::RelocationFlags::Coff {
typ: pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32,
}
| object::RelocationFlags::Elf { r_type: elf::R_386_32 | elf::R_386_PC32 } => {
let data =
section.data()?[address as usize..address as usize + 4].try_into()?;
Ok(self.endianness.read_i32_bytes(data) as i64)
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) => {
Architecture::X86_64 => match relocation.flags() {
object::RelocationFlags::Coff {
typ: pe::IMAGE_REL_AMD64_ADDR32NB | pe::IMAGE_REL_AMD64_REL32,
}
| object::RelocationFlags::Elf { r_type: elf::R_X86_64_32 | elf::R_X86_64_PC32 } => {
let data =
section.data()?[address as usize..address as usize + 4].try_into()?;
Ok(self.endianness.read_i32_bytes(data) as i64)
self.endianness.read_i32_bytes(data) as i64
}
RelocationFlags::Coff(pe::IMAGE_REL_AMD64_ADDR64) => {
object::RelocationFlags::Coff { typ: pe::IMAGE_REL_AMD64_ADDR64 }
| object::RelocationFlags::Elf { r_type: elf::R_X86_64_64 } => {
let data =
section.data()?[address as usize..address as usize + 8].try_into()?;
Ok(self.endianness.read_i64_bytes(data))
self.endianness.read_i64_bytes(data)
}
flags => bail!("Unsupported x86-64 implicit relocation {flags:?}"),
},
}
};
Ok(Some(RelocationOverride { target: RelocationOverrideTarget::Keep, addend }))
}
fn demangle(&self, name: &str) -> Option<String> {
@@ -285,6 +303,52 @@ impl Arch for ArchX86 {
fn data_reloc_size(&self, flags: RelocationFlags) -> usize {
self.reloc_size(flags).unwrap_or(1)
}
fn infer_function_size(
&self,
symbol: &Symbol,
section: &Section,
next_address: u64,
) -> Result<u64> {
let Ok(size) = (next_address - symbol.address).try_into() else {
return Ok(next_address.saturating_sub(symbol.address));
};
let Some(code) = section.data_range(symbol.address, size) else {
return Ok(0);
};
// Decode instructions to find the last non-NOP instruction
let mut decoder = self.decoder(code, symbol.address);
let mut instruction = Instruction::default();
let mut new_address = 0;
let mut reloc_iter = section.relocations.iter().peekable();
'outer: while decoder.can_decode() {
let address = decoder.ip();
while let Some(reloc) = reloc_iter.peek() {
match reloc.address.cmp(&address) {
Ordering::Less => {
reloc_iter.next();
}
Ordering::Equal => {
// If the instruction starts at a relocation, it's inline data
let reloc_size = self.reloc_size(reloc.flags).with_context(|| {
format!("Unsupported inline x86 relocation {:?}", reloc.flags)
})?;
if decoder.set_position(decoder.position() + reloc_size).is_ok() {
new_address = address + reloc_size as u64;
decoder.set_ip(new_address);
continue 'outer;
}
}
Ordering::Greater => break,
}
}
decoder.decode_out(&mut instruction);
if instruction.mnemonic() != iced_x86::Mnemonic::Nop {
new_address = instruction.next_ip();
}
}
Ok(new_address.saturating_sub(symbol.address))
}
}
struct InstructionFormatterOutput<'a> {

View File

@@ -442,7 +442,7 @@ impl From<LegacyReportItem> for ReportItem {
#[cfg(feature = "serde")]
fn serialize_hex<S>(x: &Option<u64>, s: S) -> Result<S::Ok, S::Error>
where S: serde::Serializer {
if let Some(x) = x { s.serialize_str(&format!("{:#x}", x)) } else { s.serialize_none() }
if let Some(x) = x { s.serialize_str(&format!("{x:#x}")) } else { s.serialize_none() }
}
#[cfg(feature = "serde")]

View File

@@ -325,12 +325,11 @@ fn reloc_eq(
|| display_ins_data_literals(left_obj, left_ins)
== display_ins_data_literals(right_obj, right_ins))
}
(Some(_), None) => false,
(None, Some(_)) => {
// Match if possibly stripped weak symbol
symbol_name_addend_matches && right_reloc.symbol.flags.contains(SymbolFlag::Weak)
}
(None, None) => symbol_name_addend_matches,
(Some(_), None) | (None, None) => symbol_name_addend_matches,
}
}

View File

@@ -53,12 +53,11 @@ fn reloc_eq(
section_name_eq(left_obj, right_obj, sl, sr)
&& (symbol_name_addend_matches || address_eq(left, right))
}
(Some(_), None) => false,
(None, Some(_)) => {
// Match if possibly stripped weak symbol
symbol_name_addend_matches && right.symbol.flags.contains(SymbolFlag::Weak)
}
(None, None) => symbol_name_addend_matches,
(Some(_), None) | (None, None) => symbol_name_addend_matches,
}
}
@@ -128,6 +127,28 @@ fn diff_data_relocs_for_range<'left, 'right>(
diffs
}
pub fn no_diff_data_section(obj: &Object, section_idx: usize) -> Result<SectionDiff> {
let section = &obj.sections[section_idx];
let len = section.data.len();
let data = &section.data[0..len];
let data_diff =
vec![DataDiff { data: data.to_vec(), kind: DataDiffKind::None, len, ..Default::default() }];
let mut reloc_diffs = Vec::new();
for reloc in section.relocations.iter() {
let reloc_len = obj.arch.data_reloc_size(reloc.flags);
let range = reloc.address as usize..reloc.address as usize + reloc_len;
reloc_diffs.push(DataRelocationDiff {
reloc: reloc.clone(),
kind: DataDiffKind::None,
range,
});
}
Ok(SectionDiff { match_percent: Some(0.0), data_diff, reloc_diff: reloc_diffs })
}
/// Compare the data sections of two object files.
pub fn diff_data_section(
left_obj: &Object,
@@ -416,6 +437,10 @@ pub fn diff_generic_section(
))
}
pub fn no_diff_bss_section() -> Result<SectionDiff> {
Ok(SectionDiff { match_percent: Some(0.0), data_diff: vec![], reloc_diff: vec![] })
}
/// Compare the addresses and sizes of each symbol in the BSS sections.
pub fn diff_bss_section(
left_obj: &Object,

View File

@@ -14,8 +14,9 @@ use regex::Regex;
use crate::{
diff::{DiffObjConfig, InstructionDiffKind, InstructionDiffRow, ObjectDiff, SymbolDiff},
obj::{
InstructionArg, InstructionArgValue, Object, ParsedInstruction, ResolvedInstructionRef,
ResolvedRelocation, SectionFlag, SectionKind, Symbol, SymbolFlag, SymbolKind,
FlowAnalysisValue, InstructionArg, InstructionArgValue, Object, ParsedInstruction,
ResolvedInstructionRef, ResolvedRelocation, SectionFlag, SectionKind, Symbol, SymbolFlag,
SymbolKind,
},
};
@@ -47,11 +48,12 @@ pub enum DiffText<'a> {
pub enum DiffTextColor {
#[default]
Normal, // Grey
Dim, // Dark grey
Bright, // White
Replace, // Blue
Delete, // Red
Insert, // Green
Dim, // Dark grey
Bright, // White
DataFlow, // Light blue
Replace, // Blue
Delete, // Red
Insert, // Green
Rotating(u8),
}
@@ -77,7 +79,7 @@ impl<'a> DiffTextSegment<'a> {
const EOL_SEGMENT: DiffTextSegment<'static> =
DiffTextSegment { text: DiffText::Eol, color: DiffTextColor::Normal, pad_to: 0 };
#[derive(Debug, Default, Clone, PartialEq, Eq)]
#[derive(Debug, Default, Clone)]
pub enum HighlightKind {
#[default]
None,
@@ -186,6 +188,11 @@ pub fn display_row(
}
let mut arg_idx = 0;
let mut displayed_relocation = false;
let analysis_result = if diff_config.show_data_flow {
obj.get_flow_analysis_result(resolved.symbol)
} else {
None
};
obj.arch.display_instruction(resolved, diff_config, &mut |part| match part {
InstructionPart::Basic(text) => {
if text.chars().all(|c| c == ' ') {
@@ -208,15 +215,30 @@ pub fn display_row(
if arg == InstructionArg::Reloc {
displayed_relocation = true;
}
match (arg, resolved.ins_ref.branch_dest) {
(InstructionArg::Value(value), _) => cb(DiffTextSegment {
text: DiffText::Argument(value),
color: diff_index
let data_flow_value =
analysis_result.and_then(|result|
result.get_argument_value_at_address(
ins_ref.address, (arg_idx - 1) as u8));
match (arg, data_flow_value, resolved.ins_ref.branch_dest) {
// If we have a flow analysis result, always use that over anything else.
(InstructionArg::Value(_) | InstructionArg::Reloc, Some(FlowAnalysisValue::Text(text)), _) => {
cb(DiffTextSegment {
text: DiffText::Argument(InstructionArgValue::Opaque(Cow::Borrowed(text))),
color: DiffTextColor::DataFlow,
pad_to: 0,
})
},
(InstructionArg::Value(value), None, _) => {
let color = diff_index
.get()
.map_or(base_color, |i| DiffTextColor::Rotating(i as u8)),
pad_to: 0,
}),
(InstructionArg::Reloc, None) => {
.map_or(base_color, |i| DiffTextColor::Rotating(i as u8));
cb(DiffTextSegment {
text: DiffText::Argument(value),
color,
pad_to: 0,
})
},
(InstructionArg::Reloc, _, None) => {
let resolved = resolved.relocation.unwrap();
let color = diff_index
.get()
@@ -235,9 +257,9 @@ pub fn display_row(
}
Ok(())
}
(InstructionArg::BranchDest(dest), _) |
(InstructionArg::BranchDest(dest), _, _) |
// If the relocation was resolved to a branch destination, emit that instead.
(InstructionArg::Reloc, Some(dest)) => {
(InstructionArg::Reloc, _, Some(dest)) => {
if let Some(addr) = dest.checked_sub(resolved.symbol.address) {
cb(DiffTextSegment {
text: DiffText::BranchDest(addr),
@@ -288,6 +310,18 @@ pub fn display_row(
Ok(())
}
impl PartialEq<HighlightKind> for HighlightKind {
fn eq(&self, other: &HighlightKind) -> bool {
match (self, other) {
(HighlightKind::Opcode(a), HighlightKind::Opcode(b)) => a == b,
(HighlightKind::Argument(a), HighlightKind::Argument(b)) => a.loose_eq(b),
(HighlightKind::Symbol(a), HighlightKind::Symbol(b)) => a == b,
(HighlightKind::Address(a), HighlightKind::Address(b)) => a == b,
_ => false,
}
}
}
impl PartialEq<DiffText<'_>> for HighlightKind {
fn eq(&self, other: &DiffText) -> bool {
match (self, other) {
@@ -351,13 +385,13 @@ pub fn symbol_context(obj: &Object, symbol_index: usize) -> Vec<ContextItem> {
if let Some(name) = &symbol.demangled_name {
out.push(ContextItem::Copy { value: name.clone(), label: None });
}
if symbol.section.is_some() {
if let Some(address) = symbol.virtual_address {
out.push(ContextItem::Copy {
value: format!("{:x}", address),
label: Some("virtual address".to_string()),
});
}
if symbol.section.is_some()
&& let Some(address) = symbol.virtual_address
{
out.push(ContextItem::Copy {
value: format!("{address:x}"),
label: Some("virtual address".to_string()),
});
}
out.append(&mut obj.arch.symbol_context(obj, symbol_index));
out
@@ -371,7 +405,7 @@ pub fn symbol_hover(
) -> Vec<HoverItem> {
let symbol = &obj.symbols[symbol_index];
let addend_str = match addend.cmp(&0i64) {
Ordering::Greater => format!("+{:x}", addend),
Ordering::Greater => format!("+{addend:x}"),
Ordering::Less => format!("-{:x}", -addend),
_ => String::new(),
};
@@ -422,7 +456,7 @@ pub fn symbol_hover(
if let Some(address) = symbol.virtual_address {
out.push(HoverItem::Text {
label: "Virtual address".into(),
value: format!("{:x}", address),
value: format!("{address:x}"),
color: override_color.clone().unwrap_or(HoverItemColor::Special),
});
}
@@ -492,7 +526,7 @@ pub fn instruction_context(
let mut out = Vec::new();
let mut hex_string = String::new();
for byte in resolved.code {
hex_string.push_str(&format!("{:02x}", byte));
hex_string.push_str(&format!("{byte:02x}"));
}
out.push(ContextItem::Copy { value: hex_string, label: Some("instruction bytes".to_string()) });
out.append(&mut obj.arch.instruction_context(obj, resolved));
@@ -575,7 +609,7 @@ pub fn instruction_hover(
out.push(HoverItem::Separator);
for (literal, label_override) in literals {
out.push(HoverItem::Text {
label: label_override.unwrap_or_else(|| format!("{}", ty)),
label: label_override.unwrap_or_else(|| ty.to_string()),
value: literal,
color: HoverItemColor::Normal,
});
@@ -604,7 +638,9 @@ fn symbol_matches_filter(
return false;
}
if !show_hidden_symbols
&& (symbol.size == 0 || symbol.flags.contains(SymbolFlag::Hidden | SymbolFlag::Ignored))
&& (symbol.size == 0
|| symbol.flags.contains(SymbolFlag::Hidden)
|| symbol.flags.contains(SymbolFlag::Ignored))
{
return false;
}

View File

@@ -13,7 +13,7 @@ use crate::{
code::{diff_code, no_diff_code},
data::{
diff_bss_section, diff_bss_symbol, diff_data_section, diff_data_symbol,
diff_generic_section,
diff_generic_section, no_diff_bss_section, no_diff_data_section,
},
},
obj::{InstructionRef, Object, Relocation, SectionKind, Symbol, SymbolFlag},
@@ -288,52 +288,84 @@ pub fn diff_objs(
}
for section_match in section_matches {
if let SectionMatch {
left: Some(left_section_idx),
right: Some(right_section_idx),
section_kind,
} = section_match
{
let (left_obj, left_out) = left.as_mut().unwrap();
let (right_obj, right_out) = right.as_mut().unwrap();
match section_kind {
SectionKind::Code => {
let (left_diff, right_diff) = diff_generic_section(
left_obj,
right_obj,
left_out,
right_out,
left_section_idx,
right_section_idx,
)?;
left_out.sections[left_section_idx] = left_diff;
right_out.sections[right_section_idx] = right_diff;
match section_match {
SectionMatch {
left: Some(left_section_idx),
right: Some(right_section_idx),
section_kind,
} => {
let (left_obj, left_out) = left.as_mut().unwrap();
let (right_obj, right_out) = right.as_mut().unwrap();
match section_kind {
SectionKind::Code => {
let (left_diff, right_diff) = diff_generic_section(
left_obj,
right_obj,
left_out,
right_out,
left_section_idx,
right_section_idx,
)?;
left_out.sections[left_section_idx] = left_diff;
right_out.sections[right_section_idx] = right_diff;
}
SectionKind::Data => {
let (left_diff, right_diff) = diff_data_section(
left_obj,
right_obj,
left_out,
right_out,
left_section_idx,
right_section_idx,
)?;
left_out.sections[left_section_idx] = left_diff;
right_out.sections[right_section_idx] = right_diff;
}
SectionKind::Bss | SectionKind::Common => {
let (left_diff, right_diff) = diff_bss_section(
left_obj,
right_obj,
left_out,
right_out,
left_section_idx,
right_section_idx,
)?;
left_out.sections[left_section_idx] = left_diff;
right_out.sections[right_section_idx] = right_diff;
}
SectionKind::Unknown => unreachable!(),
}
SectionKind::Data => {
let (left_diff, right_diff) = diff_data_section(
left_obj,
right_obj,
left_out,
right_out,
left_section_idx,
right_section_idx,
)?;
left_out.sections[left_section_idx] = left_diff;
right_out.sections[right_section_idx] = right_diff;
}
SectionMatch { left: Some(left_section_idx), right: None, section_kind } => {
let (left_obj, left_out) = left.as_mut().unwrap();
match section_kind {
SectionKind::Code => {}
SectionKind::Data => {
left_out.sections[left_section_idx] =
no_diff_data_section(left_obj, left_section_idx)?;
}
SectionKind::Bss | SectionKind::Common => {
left_out.sections[left_section_idx] = no_diff_bss_section()?;
}
SectionKind::Unknown => unreachable!(),
}
SectionKind::Bss | SectionKind::Common => {
let (left_diff, right_diff) = diff_bss_section(
left_obj,
right_obj,
left_out,
right_out,
left_section_idx,
right_section_idx,
)?;
left_out.sections[left_section_idx] = left_diff;
right_out.sections[right_section_idx] = right_diff;
}
SectionMatch { left: None, right: Some(right_section_idx), section_kind } => {
let (right_obj, right_out) = right.as_mut().unwrap();
match section_kind {
SectionKind::Code => {}
SectionKind::Data => {
right_out.sections[right_section_idx] =
no_diff_data_section(right_obj, right_section_idx)?;
}
SectionKind::Bss | SectionKind::Common => {
right_out.sections[right_section_idx] = no_diff_bss_section()?;
}
SectionKind::Unknown => unreachable!(),
}
SectionKind::Unknown => unreachable!(),
}
SectionMatch { left: None, right: None, .. } => {
// Should not happen
}
}
}
@@ -341,11 +373,25 @@ pub fn diff_objs(
if let (Some((right_obj, right_out)), Some((left_obj, left_out))) =
(right.as_mut(), left.as_mut())
{
if let Some(right_name) = &mapping_config.selecting_left {
generate_mapping_symbols(right_obj, right_name, left_obj, left_out, diff_config)?;
if let Some(right_name) = mapping_config.selecting_left.as_deref() {
generate_mapping_symbols(
left_obj,
left_out,
right_obj,
right_out,
MappingSymbol::Right(right_name),
diff_config,
)?;
}
if let Some(left_name) = &mapping_config.selecting_right {
generate_mapping_symbols(left_obj, left_name, right_obj, right_out, diff_config)?;
if let Some(left_name) = mapping_config.selecting_right.as_deref() {
generate_mapping_symbols(
left_obj,
left_out,
right_obj,
right_out,
MappingSymbol::Left(left_name),
diff_config,
)?;
}
}
@@ -356,17 +402,28 @@ pub fn diff_objs(
})
}
#[derive(Clone, Copy)]
enum MappingSymbol<'a> {
Left(&'a str),
Right(&'a str),
}
/// When we're selecting a symbol to use as a comparison, we'll create comparisons for all
/// symbols in the other object that match the selected symbol's section and kind. This allows
/// us to display match percentages for all symbols in the other object that could be selected.
fn generate_mapping_symbols(
base_obj: &Object,
base_name: &str,
target_obj: &Object,
target_out: &mut ObjectDiff,
left_obj: &Object,
left_out: &mut ObjectDiff,
right_obj: &Object,
right_out: &mut ObjectDiff,
mapping_symbol: MappingSymbol,
config: &DiffObjConfig,
) -> Result<()> {
let Some(base_symbol_ref) = symbol_ref_by_name(base_obj, base_name) else {
let (base_obj, base_name, target_obj) = match mapping_symbol {
MappingSymbol::Left(name) => (left_obj, name, right_obj),
MappingSymbol::Right(name) => (right_obj, name, left_obj),
};
let Some(base_symbol_ref) = base_obj.symbol_by_name(base_name) else {
return Ok(());
};
let base_section_kind = symbol_section_kind(base_obj, &base_obj.symbols[base_symbol_ref]);
@@ -377,32 +434,30 @@ fn generate_mapping_symbols(
{
continue;
}
match base_section_kind {
let (left_symbol_idx, right_symbol_idx) = match mapping_symbol {
MappingSymbol::Left(_) => (base_symbol_ref, target_symbol_index),
MappingSymbol::Right(_) => (target_symbol_index, base_symbol_ref),
};
let (left_diff, right_diff) = match base_section_kind {
SectionKind::Code => {
let (left_diff, _right_diff) =
diff_code(target_obj, base_obj, target_symbol_index, base_symbol_ref, config)?;
target_out.mapping_symbols.push(MappingSymbolDiff {
symbol_index: target_symbol_index,
symbol_diff: left_diff,
});
diff_code(left_obj, right_obj, left_symbol_idx, right_symbol_idx, config)
}
SectionKind::Data => {
let (left_diff, _right_diff) =
diff_data_symbol(target_obj, base_obj, target_symbol_index, base_symbol_ref)?;
target_out.mapping_symbols.push(MappingSymbolDiff {
symbol_index: target_symbol_index,
symbol_diff: left_diff,
});
diff_data_symbol(left_obj, right_obj, left_symbol_idx, right_symbol_idx)
}
SectionKind::Bss | SectionKind::Common => {
let (left_diff, _right_diff) =
diff_bss_symbol(target_obj, base_obj, target_symbol_index, base_symbol_ref)?;
target_out.mapping_symbols.push(MappingSymbolDiff {
symbol_index: target_symbol_index,
symbol_diff: left_diff,
});
diff_bss_symbol(left_obj, right_obj, left_symbol_idx, right_symbol_idx)
}
SectionKind::Unknown => {}
SectionKind::Unknown => continue,
}?;
match mapping_symbol {
MappingSymbol::Left(_) => right_out.mapping_symbols.push(MappingSymbolDiff {
symbol_index: right_symbol_idx,
symbol_diff: right_diff,
}),
MappingSymbol::Right(_) => left_out
.mapping_symbols
.push(MappingSymbolDiff { symbol_index: left_symbol_idx, symbol_diff: left_diff }),
}
}
Ok(())
@@ -434,10 +489,6 @@ pub struct MappingConfig {
pub selecting_right: Option<String>,
}
fn symbol_ref_by_name(obj: &Object, name: &str) -> Option<usize> {
obj.symbols.iter().position(|s| s.name == name)
}
fn apply_symbol_mappings(
left: &Object,
right: &Object,
@@ -448,26 +499,26 @@ fn apply_symbol_mappings(
) -> Result<()> {
// If we're selecting a symbol to use as a comparison, mark it as used
// This ensures that we don't match it to another symbol at any point
if let Some(left_name) = &mapping_config.selecting_left {
if let Some(left_symbol) = symbol_ref_by_name(left, left_name) {
left_used.insert(left_symbol);
}
if let Some(left_name) = &mapping_config.selecting_left
&& let Some(left_symbol) = left.symbol_by_name(left_name)
{
left_used.insert(left_symbol);
}
if let Some(right_name) = &mapping_config.selecting_right {
if let Some(right_symbol) = symbol_ref_by_name(right, right_name) {
right_used.insert(right_symbol);
}
if let Some(right_name) = &mapping_config.selecting_right
&& let Some(right_symbol) = right.symbol_by_name(right_name)
{
right_used.insert(right_symbol);
}
// Apply manual symbol mappings
for (left_name, right_name) in &mapping_config.mappings {
let Some(left_symbol_index) = symbol_ref_by_name(left, left_name) else {
let Some(left_symbol_index) = left.symbol_by_name(left_name) else {
continue;
};
if left_used.contains(&left_symbol_index) {
continue;
}
let Some(right_symbol_index) = symbol_ref_by_name(right, right_name) else {
let Some(right_symbol_index) = right.symbol_by_name(right_name) else {
continue;
};
if right_used.contains(&right_symbol_index) {
@@ -487,11 +538,7 @@ fn apply_symbol_mappings(
.map_or(SectionKind::Unknown, |s| s.kind);
if left_section_kind != right_section_kind {
log::warn!(
"Symbol section kind mismatch: {} ({:?}) vs {} ({:?})",
left_name,
left_section_kind,
right_name,
right_section_kind
"Symbol section kind mismatch: {left_name} ({left_section_kind:?}) vs {right_name} ({right_section_kind:?})"
);
continue;
}
@@ -624,17 +671,16 @@ fn find_symbol(
// If they are at the same address in the same section
if in_symbol.name.starts_with('@')
&& matches!(section_kind, SectionKind::Data | SectionKind::Bss)
{
if let Some((symbol_idx, _)) = unmatched_symbols(obj, used).find(|(_, symbol)| {
&& let Some((symbol_idx, _)) = unmatched_symbols(obj, used).find(|(_, symbol)| {
let Some(section_index) = symbol.section else {
return false;
};
symbol.name.starts_with('@')
&& symbol.address == in_symbol.address
&& obj.sections[section_index].name == section_name
}) {
return Some(symbol_idx);
}
})
{
return Some(symbol_idx);
}
// Match Metrowerks symbol$1234 against symbol$2345
if let Some((prefix, suffix)) = in_symbol.name.split_once('$') {

View File

@@ -79,7 +79,7 @@ fn run_build(
Some(target_path_rel) if config.build_target => {
update_status(
context,
format!("Building target {}", target_path_rel),
format!("Building target {target_path_rel}"),
step_idx,
total,
&cancel,
@@ -94,7 +94,7 @@ fn run_build(
Some(base_path_rel) if config.build_base => {
update_status(
context,
format!("Building base {}", base_path_rel),
format!("Building base {base_path_rel}"),
step_idx,
total,
&cancel,
@@ -111,7 +111,7 @@ fn run_build(
Some(target_path) if first_status.success => {
update_status(
context,
format!("Loading target {}", target_path),
format!("Loading target {target_path}"),
step_idx,
total,
&cancel,
@@ -122,8 +122,8 @@ fn run_build(
Err(e) => {
first_status = BuildStatus {
success: false,
stdout: format!("Loading object '{}'", target_path),
stderr: format!("{:#}", e),
stdout: format!("Loading object '{target_path}'"),
stderr: format!("{e:#}"),
..Default::default()
};
None
@@ -139,21 +139,15 @@ fn run_build(
let second_obj = match &config.base_path {
Some(base_path) if second_status.success => {
update_status(
context,
format!("Loading base {}", base_path),
step_idx,
total,
&cancel,
)?;
update_status(context, format!("Loading base {base_path}"), step_idx, total, &cancel)?;
step_idx += 1;
match read::read(base_path.as_ref(), &config.diff_obj_config) {
Ok(obj) => Some(obj),
Err(e) => {
second_status = BuildStatus {
success: false,
stdout: format!("Loading object '{}'", base_path),
stderr: format!("{:#}", e),
stdout: format!("Loading object '{base_path}'"),
stderr: format!("{e:#}"),
..Default::default()
};
None

View File

@@ -0,0 +1,109 @@
use alloc::{borrow::Cow, vec::Vec};
use anyhow::{Context, Result};
use object::{Object as _, ObjectSection as _};
use typed_arena::Arena;
use crate::obj::{Section, SectionKind};
/// Parse line information from DWARF 2+ sections.
pub(crate) fn parse_line_info_dwarf2(
obj_file: &object::File,
sections: &mut [Section],
) -> Result<()> {
let arena_data = Arena::new();
let arena_relocations = Arena::new();
let endian = match obj_file.endianness() {
object::Endianness::Little => gimli::RunTimeEndian::Little,
object::Endianness::Big => gimli::RunTimeEndian::Big,
};
let dwarf = gimli::Dwarf::load(|id: gimli::SectionId| -> Result<_> {
load_file_section(id, obj_file, endian, &arena_data, &arena_relocations)
})
.context("loading DWARF sections")?;
let mut iter = dwarf.units();
if let Some(header) = iter.next().map_err(|e| gimli_error(e, "iterating over DWARF units"))? {
let unit = dwarf.unit(header).map_err(|e| gimli_error(e, "loading DWARF unit"))?;
if let Some(program) = unit.line_program.clone() {
let mut text_sections = sections.iter_mut().filter(|s| s.kind == SectionKind::Code);
let mut lines = text_sections.next().map(|section| &mut section.line_info);
let mut rows = program.rows();
while let Some((_header, row)) =
rows.next_row().map_err(|e| gimli_error(e, "loading program row"))?
{
if let (Some(line), Some(lines)) = (row.line(), &mut lines) {
lines.insert(row.address(), line.get() as u32);
}
if row.end_sequence() {
// The next row is the start of a new sequence, which means we must
// advance to the next .text section.
lines = text_sections.next().map(|section| &mut section.line_info);
}
}
}
}
if iter.next().map_err(|e| gimli_error(e, "checking for next unit"))?.is_some() {
log::warn!("Multiple units found in DWARF data, only processing the first");
}
Ok(())
}
#[derive(Debug, Default)]
struct RelocationMap(object::read::RelocationMap);
impl RelocationMap {
fn add(&mut self, file: &object::File, section: &object::Section) {
for (offset, relocation) in section.relocations() {
if let Err(e) = self.0.add(file, offset, relocation) {
log::error!(
"Relocation error for section {} at offset 0x{:08x}: {}",
section.name().unwrap(),
offset,
e
);
}
}
}
}
impl gimli::read::Relocate for &'_ RelocationMap {
fn relocate_address(&self, offset: usize, value: u64) -> gimli::Result<u64> {
Ok(self.0.relocate(offset as u64, value))
}
fn relocate_offset(&self, offset: usize, value: usize) -> gimli::Result<usize> {
<usize as gimli::ReaderOffset>::from_u64(self.0.relocate(offset as u64, value as u64))
}
}
type Relocate<'a, R> = gimli::RelocateReader<R, &'a RelocationMap>;
fn load_file_section<'input, 'arena, Endian: gimli::Endianity>(
id: gimli::SectionId,
file: &object::File<'input>,
endian: Endian,
arena_data: &'arena Arena<Cow<'input, [u8]>>,
arena_relocations: &'arena Arena<RelocationMap>,
) -> Result<Relocate<'arena, gimli::EndianSlice<'arena, Endian>>> {
let mut relocations = RelocationMap::default();
let data = match file.section_by_name(id.name()) {
Some(ref section) => {
relocations.add(file, section);
section.uncompressed_data()?
}
// Use a non-zero capacity so that `ReaderOffsetId`s are unique.
None => Cow::Owned(Vec::with_capacity(1)),
};
let data_ref = arena_data.alloc(data);
let section = gimli::EndianSlice::new(data_ref, endian);
let relocations = arena_relocations.alloc(relocations);
Ok(Relocate::new(section, relocations))
}
#[inline]
fn gimli_error(e: gimli::Error, context: &str) -> anyhow::Error {
anyhow::anyhow!("gimli error {context}: {e:?}")
}

View File

@@ -1,3 +1,5 @@
#[cfg(feature = "dwarf")]
mod dwarf2;
pub mod read;
pub mod split_meta;
@@ -114,11 +116,21 @@ impl Section {
ins_ref: InstructionRef,
) -> Option<ResolvedRelocation<'obj>> {
match self.relocations.binary_search_by_key(&ins_ref.address, |r| r.address) {
Ok(i) => self.relocations.get(i),
Ok(mut i) => {
// Find the first relocation at the address
while i
.checked_sub(1)
.and_then(|n| self.relocations.get(n))
.is_some_and(|r| r.address == ins_ref.address)
{
i -= 1;
}
self.relocations.get(i)
}
Err(i) => self
.relocations
.get(i)
.take_if(|r| r.address < ins_ref.address + ins_ref.size as u64),
.filter(|r| r.address < ins_ref.address + ins_ref.size as u64),
}
.and_then(|relocation| {
let symbol = obj.symbols.get(relocation.target_symbol)?;
@@ -169,8 +181,8 @@ impl fmt::Display for InstructionArgValue<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
InstructionArgValue::Signed(v) => write!(f, "{:#x}", ReallySigned(*v)),
InstructionArgValue::Unsigned(v) => write!(f, "{:#x}", v),
InstructionArgValue::Opaque(v) => write!(f, "{}", v),
InstructionArgValue::Unsigned(v) => write!(f, "{v:#x}"),
InstructionArgValue::Opaque(v) => write!(f, "{v}"),
}
}
}
@@ -233,6 +245,19 @@ pub enum SymbolKind {
Section,
}
#[derive(Debug)]
pub enum FlowAnalysisValue {
Text(String),
}
pub trait FlowAnalysisResult: core::fmt::Debug + Send {
fn get_argument_value_at_address(
&self,
address: u64,
argument: u8,
) -> Option<&FlowAnalysisValue>;
}
#[derive(Debug, Clone, Eq, PartialEq, Hash, Default)]
pub struct Symbol {
pub name: String,
@@ -260,6 +285,7 @@ pub struct Object {
pub path: Option<std::path::PathBuf>,
#[cfg(feature = "std")]
pub timestamp: Option<filetime::FileTime>,
pub flow_analysis_results: BTreeMap<u64, Box<dyn FlowAnalysisResult>>,
}
impl Default for Object {
@@ -274,6 +300,7 @@ impl Default for Object {
path: None,
#[cfg(feature = "std")]
timestamp: None,
flow_analysis_results: BTreeMap::<u64, Box<dyn FlowAnalysisResult>>::new(),
}
}
}
@@ -283,7 +310,7 @@ impl Object {
&self,
symbol_index: usize,
ins_ref: InstructionRef,
) -> Option<ResolvedInstructionRef> {
) -> Option<ResolvedInstructionRef<'_>> {
let symbol = self.symbols.get(symbol_index)?;
let section_index = symbol.section?;
let section = self.sections.get(section_index)?;
@@ -308,6 +335,26 @@ impl Object {
let offset = symbol.address.checked_sub(section.address)?;
section.data.get(offset as usize..offset as usize + symbol.size as usize)
}
pub fn symbol_by_name(&self, name: &str) -> Option<usize> {
self.symbols.iter().position(|symbol| symbol.section.is_some() && symbol.name == name)
}
pub fn get_flow_analysis_result(&self, symbol: &Symbol) -> Option<&dyn FlowAnalysisResult> {
let key = symbol.section.unwrap_or_default() as u64 * 1024 * 1024 * 1024 + symbol.address;
self.flow_analysis_results.get(&key).map(|result| result.as_ref())
}
pub fn add_flow_analysis_result(
&mut self,
symbol: &Symbol,
result: Box<dyn FlowAnalysisResult>,
) {
let key = symbol.section.unwrap_or_default() as u64 * 1024 * 1024 * 1024 + symbol.address;
self.flow_analysis_results.insert(key, result);
}
pub fn has_flow_analysis_result(&self) -> bool { !self.flow_analysis_results.is_empty() }
}
#[derive(Debug, Clone, Eq, PartialEq, Hash)]

View File

@@ -1,4 +1,5 @@
use alloc::{
boxed::Box,
collections::BTreeMap,
format,
string::{String, ToString},
@@ -10,11 +11,11 @@ use anyhow::{Context, Result, anyhow, bail, ensure};
use object::{Object as _, ObjectSection as _, ObjectSymbol as _};
use crate::{
arch::{Arch, new_arch},
arch::{Arch, RelocationOverride, RelocationOverrideTarget, new_arch},
diff::DiffObjConfig,
obj::{
Object, Relocation, RelocationFlags, Section, SectionData, SectionFlag, SectionKind,
Symbol, SymbolFlag, SymbolKind,
FlowAnalysisResult, Object, Relocation, RelocationFlags, Section, SectionData, SectionFlag,
SectionKind, Symbol, SymbolFlag, SymbolKind,
split_meta::{SPLITMETA_SECTION, SplitMeta},
},
util::{align_data_slice_to, align_u64_to, read_u16, read_u32},
@@ -23,8 +24,13 @@ use crate::{
fn map_section_kind(section: &object::Section) -> SectionKind {
match section.kind() {
object::SectionKind::Text => SectionKind::Code,
object::SectionKind::Data | object::SectionKind::ReadOnlyData => SectionKind::Data,
object::SectionKind::UninitializedData => SectionKind::Bss,
object::SectionKind::Data
| object::SectionKind::ReadOnlyData
| object::SectionKind::ReadOnlyString
| object::SectionKind::Tls => SectionKind::Data,
object::SectionKind::UninitializedData
| object::SectionKind::UninitializedTls
| object::SectionKind::Common => SectionKind::Bss,
_ => SectionKind::Unknown,
}
}
@@ -42,7 +48,7 @@ fn map_symbol(
(symbol.kind(), symbol.section_index().and_then(|i| file.section_by_index(i).ok()))
{
let section_name = section.name().context("Failed to process section name")?;
name = format!("[{}]", section_name);
name = format!("[{section_name}]");
// For section symbols, set the size to zero. If the size is non-zero, it will be included
// in the diff. Most of the time, this is duplicative, given that we'll have function or
// object symbols that cover the same range. In the case of an empty section, the size
@@ -116,12 +122,21 @@ fn map_symbols(
}
// Infer symbol sizes for 0-size symbols
infer_symbol_sizes(&mut symbols, sections);
infer_symbol_sizes(arch, &mut symbols, sections)?;
Ok((symbols, symbol_indices))
}
fn infer_symbol_sizes(symbols: &mut [Symbol], sections: &[Section]) {
/// When inferring a symbol's size, we ignore symbols that start with specific prefixes. They are
/// usually emitted as branch targets and do not represent the start of a function or object.
fn is_local_label(symbol: &Symbol) -> bool {
const LABEL_PREFIXES: &[&str] = &[".L", "LAB_"];
symbol.size == 0
&& symbol.flags.contains(SymbolFlag::Local)
&& LABEL_PREFIXES.iter().any(|p| symbol.name.starts_with(p))
}
fn infer_symbol_sizes(arch: &dyn Arch, symbols: &mut [Symbol], sections: &[Section]) -> Result<()> {
// Create a sorted list of symbol indices by section
let mut symbols_with_section = Vec::<usize>::with_capacity(symbols.len());
for (i, symbol) in symbols.iter().enumerate() {
@@ -167,33 +182,37 @@ fn infer_symbol_sizes(symbols: &mut [Symbol], sections: &[Section]) {
if last_end.0 == section_idx && last_end.1 > symbol.address {
continue;
}
let next_symbol = match symbol.kind {
// For function/object symbols, find the next function/object symbol (in other words:
// skip over labels)
SymbolKind::Function | SymbolKind::Object => loop {
if iter_idx >= symbols_with_section.len() {
break None;
let next_symbol = loop {
if iter_idx >= symbols_with_section.len() {
break None;
}
let next_symbol = &symbols[symbols_with_section[iter_idx]];
if next_symbol.section != Some(section_idx) {
break None;
}
if match symbol.kind {
SymbolKind::Function | SymbolKind::Object => {
// For function/object symbols, find the next function/object
matches!(next_symbol.kind, SymbolKind::Function | SymbolKind::Object)
}
let next_symbol = &symbols[symbols_with_section[iter_idx]];
if next_symbol.section != Some(section_idx) {
break None;
SymbolKind::Unknown | SymbolKind::Section => {
// For labels (or anything else), stop at any symbol
true
}
if let SymbolKind::Function | SymbolKind::Object = next_symbol.kind {
break Some(next_symbol);
}
iter_idx += 1;
},
// For labels (or anything else), simply use the next symbol's address
SymbolKind::Unknown | SymbolKind::Section => symbols_with_section
.get(iter_idx)
.map(|&i| &symbols[i])
.take_if(|s| s.section == Some(section_idx)),
} && !is_local_label(next_symbol)
{
break Some(next_symbol);
}
iter_idx += 1;
};
let section = &sections[section_idx];
let next_address =
next_symbol.map(|s| s.address).unwrap_or_else(|| section.address + section.size);
let new_size = if section.kind == SectionKind::Code {
arch.infer_function_size(symbol, section, next_address)?
} else {
next_address.saturating_sub(symbol.address)
};
let next_address = next_symbol.map(|s| s.address).unwrap_or_else(|| {
let section = &sections[section_idx];
section.address + section.size
});
let new_size = next_address.saturating_sub(symbol.address);
if new_size > 0 {
let symbol = &mut symbols[symbol_idx];
symbol.size = new_size;
@@ -202,7 +221,7 @@ fn infer_symbol_sizes(symbols: &mut [Symbol], sections: &[Section]) {
}
// Set symbol kind if unknown and size is non-zero
if symbol.kind == SymbolKind::Unknown {
symbol.kind = match sections[section_idx].kind {
symbol.kind = match section.kind {
SectionKind::Code => SymbolKind::Function,
SectionKind::Data | SectionKind::Bss => SymbolKind::Object,
_ => SymbolKind::Unknown,
@@ -210,6 +229,7 @@ fn infer_symbol_sizes(symbols: &mut [Symbol], sections: &[Section]) {
}
}
}
Ok(())
}
fn map_sections(
@@ -242,7 +262,7 @@ fn map_sections(
});
let unique_id = section_names.entry(name.to_string()).or_insert(0);
let id = format!("{}-{}", name, unique_id);
let id = format!("{name}-{unique_id}");
*unique_id += 1;
if section_indices.len() <= section.index().0 {
@@ -318,60 +338,126 @@ fn map_section_relocations(
) -> Result<Vec<Relocation>> {
let mut relocations = Vec::<Relocation>::with_capacity(obj_section.relocations().count());
for (address, reloc) in obj_section.relocations() {
let flags = match reloc.flags() {
object::RelocationFlags::Elf { r_type } => RelocationFlags::Elf(r_type),
object::RelocationFlags::Coff { typ } => RelocationFlags::Coff(typ),
flags => {
bail!("Unhandled relocation flags: {:?}", flags);
let mut target_reloc = RelocationOverride {
target: match reloc.target() {
object::RelocationTarget::Symbol(symbol) => {
RelocationOverrideTarget::Symbol(symbol)
}
object::RelocationTarget::Section(section) => {
RelocationOverrideTarget::Section(section)
}
_ => RelocationOverrideTarget::Skip,
},
addend: reloc.addend(),
};
// Allow the architecture to override the relocation target and addend
match arch.relocation_override(obj_file, obj_section, address, &reloc)? {
Some(reloc_override) => {
match reloc_override.target {
RelocationOverrideTarget::Keep => {}
target => {
target_reloc.target = target;
}
}
target_reloc.addend = reloc_override.addend;
}
};
// TODO validate reloc here?
let mut addend = if reloc.has_implicit_addend() {
arch.implcit_addend(obj_file, obj_section, address, &reloc, flags)?
} else {
reloc.addend()
};
let target_symbol = match reloc.target() {
object::RelocationTarget::Symbol(idx) => {
if idx.0 == u32::MAX as usize {
// ???
None => {
ensure!(
!reloc.has_implicit_addend(),
"Unsupported {:?} implicit relocation {:?}",
obj_file.architecture(),
reloc.flags()
);
}
}
// Resolve the relocation target symbol
let (symbol_index, addend) = match target_reloc.target {
RelocationOverrideTarget::Keep => unreachable!(),
RelocationOverrideTarget::Skip => continue,
RelocationOverrideTarget::Symbol(symbol_index) => {
// Sometimes used to indicate "absolute"
if symbol_index.0 == u32::MAX as usize {
continue;
}
// If the target is a section symbol, try to resolve a better symbol as the target
let idx = if let Some(section_symbol) = obj_file
.symbol_by_index(idx)
if let Some(section_symbol) = obj_file
.symbol_by_index(symbol_index)
.ok()
.take_if(|s| s.kind() == object::SymbolKind::Section)
.filter(|s| s.kind() == object::SymbolKind::Section)
{
let section_index =
section_symbol.section_index().context("Section symbol without section")?;
let target_address = section_symbol.address().wrapping_add_signed(addend);
let target_address =
section_symbol.address().wrapping_add_signed(target_reloc.addend);
if let Some((new_idx, addr)) = ordered_symbols
.get(section_index.0)
.and_then(|symbols| best_symbol(symbols, target_address))
{
addend = target_address.wrapping_sub(addr) as i64;
new_idx
(new_idx, target_address.wrapping_sub(addr) as i64)
} else {
idx
(symbol_index, target_reloc.addend)
}
} else {
idx
};
match symbol_indices.get(idx.0).copied() {
Some(i) => i,
None => {
log::warn!("Invalid symbol index {}", idx.0);
continue;
}
(symbol_index, target_reloc.addend)
}
}
object::RelocationTarget::Absolute => {
let section_name = obj_section.name()?;
log::warn!("Ignoring absolute relocation @ {}:{:#x}", section_name, address);
RelocationOverrideTarget::Section(section_index) => {
let section = match obj_file.section_by_index(section_index) {
Ok(section) => section,
Err(e) => {
log::warn!("Invalid relocation section: {e}");
continue;
}
};
let Ok(target_address) = u64::try_from(target_reloc.addend) else {
log::warn!(
"Negative section relocation addend: {}{}",
section.name()?,
target_reloc.addend
);
continue;
};
let Some(symbols) = ordered_symbols.get(section_index.0) else {
log::warn!(
"Couldn't resolve relocation target symbol for section {} (no symbols)",
section.name()?
);
continue;
};
// Attempt to resolve a target symbol for the relocation
if let Some((new_idx, addr)) = best_symbol(symbols, target_address) {
(new_idx, target_address.wrapping_sub(addr) as i64)
} else if let Some(section_symbol) =
symbols.iter().find(|s| s.kind() == object::SymbolKind::Section)
{
(
section_symbol.index(),
target_address.wrapping_sub(section_symbol.address()) as i64,
)
} else {
log::warn!(
"Couldn't resolve relocation target symbol for section {}",
section.name()?
);
continue;
}
}
};
let flags = match reloc.flags() {
object::RelocationFlags::Elf { r_type } => RelocationFlags::Elf(r_type),
object::RelocationFlags::Coff { typ } => RelocationFlags::Coff(typ),
flags => bail!("Unhandled relocation flags: {:?}", flags),
};
let target_symbol = match symbol_indices.get(symbol_index.0).copied() {
Some(i) => i,
None => {
log::warn!("Invalid symbol index {}", symbol_index.0);
continue;
}
_ => bail!("Unhandled relocation target: {:?}", reloc.target()),
};
relocations.push(Relocation { address, flags, target_symbol, addend });
}
@@ -422,17 +508,19 @@ fn map_relocations(
Ok(())
}
fn calculate_pooled_relocations(
arch: &dyn Arch,
sections: &mut [Section],
symbols: &[Symbol],
) -> Result<()> {
for (section_index, section) in sections.iter_mut().enumerate() {
fn perform_data_flow_analysis(obj: &mut Object, config: &DiffObjConfig) -> Result<()> {
// If neither of these settings are on, no flow analysis to perform
if !config.analyze_data_flow && !config.ppc_calculate_pool_relocations {
return Ok(());
}
let mut generated_relocations = Vec::<(usize, Vec<Relocation>)>::new();
let mut generated_flow_results = Vec::<(Symbol, Box<dyn FlowAnalysisResult>)>::new();
for (section_index, section) in obj.sections.iter().enumerate() {
if section.kind != SectionKind::Code {
continue;
}
let mut fake_pool_relocs = Vec::new();
for symbol in symbols {
for symbol in obj.symbols.iter() {
if symbol.section != Some(section_index) {
continue;
}
@@ -447,14 +535,36 @@ fn calculate_pooled_relocations(
symbol.address + symbol.size
)
})?;
fake_pool_relocs.append(&mut arch.generate_pooled_relocations(
symbol.address,
code,
&section.relocations,
symbols,
));
// Optional pooled relocation computation
// Long view: This could be replaced by the full data flow analysis
// once that feature has stabilized.
if config.ppc_calculate_pool_relocations {
let relocations = obj.arch.generate_pooled_relocations(
symbol.address,
code,
&section.relocations,
&obj.symbols,
);
generated_relocations.push((section_index, relocations));
}
// Optional full data flow analysis
if config.analyze_data_flow
&& let Some(flow_result) =
obj.arch.data_flow_analysis(obj, symbol, code, &section.relocations)
{
generated_flow_results.push((symbol.clone(), flow_result));
}
}
section.relocations.append(&mut fake_pool_relocs);
}
for (symbol, flow_result) in generated_flow_results {
obj.add_flow_analysis_result(&symbol, flow_result);
}
for (section_index, mut relocations) in generated_relocations {
obj.sections[section_index].relocations.append(&mut relocations);
}
for section in obj.sections.iter_mut() {
section.relocations.sort_by_key(|r| r.address);
}
Ok(())
@@ -467,6 +577,28 @@ fn parse_line_info(
obj_data: &[u8],
) -> Result<()> {
// DWARF 1.1
if let Err(e) = parse_line_info_dwarf1(obj_file, sections) {
log::warn!("Failed to parse DWARF 1.1 line info: {e}");
}
// DWARF 2+
#[cfg(feature = "dwarf")]
if let Err(e) = super::dwarf2::parse_line_info_dwarf2(obj_file, sections) {
log::warn!("Failed to parse DWARF 2+ line info: {e}");
}
// COFF
if let object::File::Coff(coff) = obj_file
&& let Err(e) = parse_line_info_coff(coff, sections, section_indices, obj_data)
{
log::warn!("Failed to parse COFF line info: {e}");
}
Ok(())
}
/// Parse .line section from DWARF 1.1 format.
fn parse_line_info_dwarf1(obj_file: &object::File, sections: &mut [Section]) -> Result<()> {
if let Some(section) = obj_file.section_by_name(".line") {
let data = section.uncompressed_data()?;
let mut reader: &[u8] = data.as_ref();
@@ -487,62 +619,13 @@ fn parse_line_info(
let line_number = read_u32(obj_file, &mut section_data)?;
let statement_pos = read_u16(obj_file, &mut section_data)?;
if statement_pos != 0xFFFF {
log::warn!("Unhandled statement pos {}", statement_pos);
log::warn!("Unhandled statement pos {statement_pos}");
}
let address_delta = read_u32(obj_file, &mut section_data)? as u64;
out_section.line_info.insert(base_address + address_delta, line_number);
}
}
}
// DWARF 2+
#[cfg(feature = "dwarf")]
{
fn gimli_error(e: gimli::Error) -> anyhow::Error { anyhow::anyhow!("DWARF error: {e:?}") }
let dwarf_cow = gimli::DwarfSections::load(|id| {
Ok::<_, gimli::Error>(
obj_file
.section_by_name(id.name())
.and_then(|section| section.uncompressed_data().ok())
.unwrap_or(alloc::borrow::Cow::Borrowed(&[][..])),
)
})
.map_err(gimli_error)?;
let endian = match obj_file.endianness() {
object::Endianness::Little => gimli::RunTimeEndian::Little,
object::Endianness::Big => gimli::RunTimeEndian::Big,
};
let dwarf = dwarf_cow.borrow(|section| gimli::EndianSlice::new(section, endian));
let mut iter = dwarf.units();
if let Some(header) = iter.next().map_err(gimli_error)? {
let unit = dwarf.unit(header).map_err(gimli_error)?;
if let Some(program) = unit.line_program.clone() {
let mut text_sections = sections.iter_mut().filter(|s| s.kind == SectionKind::Code);
let mut lines = text_sections.next().map(|section| &mut section.line_info);
let mut rows = program.rows();
while let Some((_header, row)) = rows.next_row().map_err(gimli_error)? {
if let (Some(line), Some(lines)) = (row.line(), &mut lines) {
lines.insert(row.address(), line.get() as u32);
}
if row.end_sequence() {
// The next row is the start of a new sequence, which means we must
// advance to the next .text section.
lines = text_sections.next().map(|section| &mut section.line_info);
}
}
}
}
if iter.next().map_err(gimli_error)?.is_some() {
log::warn!("Multiple units found in DWARF data, only processing the first");
}
}
// COFF
if let object::File::Coff(coff) = obj_file {
parse_line_info_coff(coff, sections, section_indices, obj_data)?;
}
Ok(())
}
@@ -855,15 +938,12 @@ pub fn parse(data: &[u8], config: &DiffObjConfig) -> Result<Object> {
let (mut symbols, symbol_indices) =
map_symbols(arch.as_ref(), &obj_file, &sections, &section_indices, split_meta.as_ref())?;
map_relocations(arch.as_ref(), &obj_file, &mut sections, &section_indices, &symbol_indices)?;
if config.ppc_calculate_pool_relocations {
calculate_pooled_relocations(arch.as_ref(), &mut sections, &symbols)?;
}
parse_line_info(&obj_file, &mut sections, &section_indices, data)?;
if config.combine_data_sections || config.combine_text_sections {
combine_sections(&mut sections, &mut symbols, config)?;
}
arch.post_init(&sections, &symbols);
Ok(Object {
let mut obj = Object {
arch,
endianness: obj_file.endianness(),
symbols,
@@ -873,7 +953,14 @@ pub fn parse(data: &[u8], config: &DiffObjConfig) -> Result<Object> {
path: None,
#[cfg(feature = "std")]
timestamp: None,
})
flow_analysis_results: Default::default(),
};
// Need to construct the obj first so that we have a convinient package to
// pass to flow analysis. Then the flow analysis will mutate obj adding
// additional data to it.
perform_data_flow_analysis(&mut obj, config)?;
Ok(obj)
}
#[cfg(feature = "std")]

View File

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

View File

@@ -85,3 +85,17 @@ fn diff_ppc() {
assert_eq!(base_symbol_diff.target_symbol, Some(target_symbol_idx));
insta::assert_debug_snapshot!((target_symbol_diff, base_symbol_diff));
}
#[test]
#[cfg(feature = "ppc")]
fn read_vmx128_coff() {
let diff_config = diff::DiffObjConfig { combine_data_sections: true, ..Default::default() };
let obj = obj::read::parse(include_object!("data/ppc/vmx128.obj"), &diff_config).unwrap();
insta::assert_debug_snapshot!(obj);
let symbol_idx =
obj.symbols.iter().position(|s| s.name == "?FloatingPointExample@@YAXXZ").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

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

View File

@@ -20,7 +20,7 @@ pub fn display_diff(
separator = true;
}
let DiffTextSegment { text, color, pad_to } = segment;
output.push_str(&format!("({:?}, {:?}, {:?})", text, color, pad_to));
output.push_str(&format!("({text:?}, {color:?}, {pad_to:?})"));
Ok(())
})
.unwrap();

Binary file not shown.

Binary file not shown.

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -4,6 +4,9 @@ expression: obj
---
Object {
arch: ArchPpc {
extensions: Extensions(
2,
),
extab: Some(
{
10: ExceptionInfo {
@@ -548,4 +551,5 @@ Object {
split_meta: None,
path: None,
timestamp: None,
flow_analysis_results: {},
}

View File

@@ -8,7 +8,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 0,
size: 4,
opcode: 60,
opcode: 283,
branch_dest: None,
},
),
@@ -22,7 +22,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 4,
size: 4,
opcode: 38,
opcode: 260,
branch_dest: None,
},
),
@@ -36,7 +36,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 8,
size: 4,
opcode: 43,
opcode: 265,
branch_dest: Some(
20,
),
@@ -57,7 +57,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 12,
size: 4,
opcode: 41,
opcode: 263,
branch_dest: None,
},
),
@@ -71,7 +71,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 16,
size: 4,
opcode: 45,
opcode: 267,
branch_dest: Some(
32,
),
@@ -92,7 +92,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 20,
size: 4,
opcode: 42,
opcode: 264,
branch_dest: None,
},
),
@@ -113,7 +113,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 24,
size: 4,
opcode: 41,
opcode: 263,
branch_dest: None,
},
),
@@ -127,7 +127,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 28,
size: 4,
opcode: 94,
opcode: 323,
branch_dest: None,
},
),
@@ -141,7 +141,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 32,
size: 4,
opcode: 60,
opcode: 283,
branch_dest: None,
},
),
@@ -162,7 +162,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 36,
size: 4,
opcode: 166,
opcode: 445,
branch_dest: None,
},
),
@@ -176,7 +176,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 40,
size: 4,
opcode: 38,
opcode: 260,
branch_dest: None,
},
),
@@ -190,7 +190,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 44,
size: 4,
opcode: 43,
opcode: 265,
branch_dest: Some(
56,
),
@@ -211,7 +211,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 48,
size: 4,
opcode: 41,
opcode: 263,
branch_dest: None,
},
),
@@ -225,7 +225,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 52,
size: 4,
opcode: 45,
opcode: 267,
branch_dest: Some(
68,
),
@@ -246,7 +246,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 56,
size: 4,
opcode: 42,
opcode: 264,
branch_dest: None,
},
),
@@ -267,7 +267,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 60,
size: 4,
opcode: 41,
opcode: 263,
branch_dest: None,
},
),
@@ -281,7 +281,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 64,
size: 4,
opcode: 94,
opcode: 323,
branch_dest: None,
},
),
@@ -295,7 +295,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 68,
size: 4,
opcode: 60,
opcode: 283,
branch_dest: None,
},
),
@@ -316,7 +316,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 72,
size: 4,
opcode: 41,
opcode: 263,
branch_dest: None,
},
),
@@ -330,7 +330,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 76,
size: 4,
opcode: 38,
opcode: 260,
branch_dest: None,
},
),
@@ -344,7 +344,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 80,
size: 4,
opcode: 166,
opcode: 445,
branch_dest: None,
},
),
@@ -358,7 +358,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 84,
size: 4,
opcode: 43,
opcode: 265,
branch_dest: Some(
96,
),
@@ -379,7 +379,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 88,
size: 4,
opcode: 41,
opcode: 263,
branch_dest: None,
},
),
@@ -393,7 +393,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 92,
size: 4,
opcode: 45,
opcode: 267,
branch_dest: Some(
108,
),
@@ -414,7 +414,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 96,
size: 4,
opcode: 42,
opcode: 264,
branch_dest: None,
},
),
@@ -435,7 +435,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 100,
size: 4,
opcode: 41,
opcode: 263,
branch_dest: None,
},
),
@@ -449,7 +449,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 104,
size: 4,
opcode: 94,
opcode: 323,
branch_dest: None,
},
),
@@ -463,7 +463,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 108,
size: 4,
opcode: 60,
opcode: 283,
branch_dest: None,
},
),
@@ -484,7 +484,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 112,
size: 4,
opcode: 41,
opcode: 263,
branch_dest: None,
},
),
@@ -498,7 +498,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 116,
size: 4,
opcode: 38,
opcode: 260,
branch_dest: None,
},
),
@@ -512,7 +512,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 120,
size: 4,
opcode: 166,
opcode: 445,
branch_dest: None,
},
),
@@ -526,7 +526,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 124,
size: 4,
opcode: 43,
opcode: 265,
branch_dest: Some(
136,
),
@@ -547,7 +547,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 128,
size: 4,
opcode: 41,
opcode: 263,
branch_dest: None,
},
),
@@ -561,7 +561,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 132,
size: 4,
opcode: 45,
opcode: 267,
branch_dest: Some(
148,
),
@@ -582,7 +582,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 136,
size: 4,
opcode: 42,
opcode: 264,
branch_dest: None,
},
),
@@ -603,7 +603,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 140,
size: 4,
opcode: 41,
opcode: 263,
branch_dest: None,
},
),
@@ -617,7 +617,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 144,
size: 4,
opcode: 94,
opcode: 323,
branch_dest: None,
},
),
@@ -631,7 +631,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 148,
size: 4,
opcode: 41,
opcode: 263,
branch_dest: None,
},
),
@@ -652,7 +652,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 152,
size: 4,
opcode: 41,
opcode: 263,
branch_dest: None,
},
),
@@ -666,7 +666,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 156,
size: 4,
opcode: 166,
opcode: 445,
branch_dest: None,
},
),
@@ -680,7 +680,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 160,
size: 4,
opcode: 42,
opcode: 264,
branch_dest: None,
},
),
@@ -694,7 +694,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 164,
size: 4,
opcode: 41,
opcode: 263,
branch_dest: None,
},
),
@@ -708,7 +708,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 168,
size: 4,
opcode: 166,
opcode: 445,
branch_dest: None,
},
),
@@ -722,7 +722,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 172,
size: 4,
opcode: 41,
opcode: 263,
branch_dest: None,
},
),
@@ -736,7 +736,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 176,
size: 4,
opcode: 162,
opcode: 441,
branch_dest: None,
},
),
@@ -750,7 +750,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 180,
size: 4,
opcode: 94,
opcode: 323,
branch_dest: None,
},
),
@@ -764,7 +764,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 184,
size: 4,
opcode: 66,
opcode: 289,
branch_dest: None,
},
),
@@ -778,7 +778,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 188,
size: 4,
opcode: 43,
opcode: 265,
branch_dest: Some(
196,
),
@@ -799,7 +799,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 192,
size: 4,
opcode: 166,
opcode: 445,
branch_dest: None,
},
),
@@ -813,7 +813,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 196,
size: 4,
opcode: 163,
opcode: 442,
branch_dest: None,
},
),
@@ -834,7 +834,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 200,
size: 4,
opcode: 94,
opcode: 323,
branch_dest: None,
},
),
@@ -848,7 +848,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 204,
size: 4,
opcode: 66,
opcode: 289,
branch_dest: None,
},
),
@@ -862,7 +862,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 208,
size: 4,
opcode: 43,
opcode: 265,
branch_dest: Some(
216,
),
@@ -883,7 +883,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 212,
size: 4,
opcode: 166,
opcode: 445,
branch_dest: None,
},
),
@@ -897,7 +897,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 216,
size: 4,
opcode: 163,
opcode: 442,
branch_dest: None,
},
),
@@ -918,7 +918,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 220,
size: 4,
opcode: 94,
opcode: 323,
branch_dest: None,
},
),
@@ -932,7 +932,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 224,
size: 4,
opcode: 66,
opcode: 289,
branch_dest: None,
},
),
@@ -946,7 +946,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 228,
size: 4,
opcode: 43,
opcode: 265,
branch_dest: Some(
236,
),
@@ -967,7 +967,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 232,
size: 4,
opcode: 166,
opcode: 445,
branch_dest: None,
},
),
@@ -981,7 +981,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 236,
size: 4,
opcode: 163,
opcode: 442,
branch_dest: None,
},
),
@@ -1002,7 +1002,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 240,
size: 4,
opcode: 94,
opcode: 323,
branch_dest: None,
},
),
@@ -1016,7 +1016,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 244,
size: 4,
opcode: 66,
opcode: 289,
branch_dest: None,
},
),
@@ -1030,7 +1030,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 248,
size: 4,
opcode: 43,
opcode: 265,
branch_dest: Some(
256,
),
@@ -1051,7 +1051,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 252,
size: 4,
opcode: 166,
opcode: 445,
branch_dest: None,
},
),
@@ -1065,7 +1065,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 256,
size: 4,
opcode: 41,
opcode: 263,
branch_dest: None,
},
),
@@ -1086,7 +1086,7 @@ expression: diff.instruction_rows
InstructionRef {
address: 260,
size: 4,
opcode: 47,
opcode: 269,
branch_dest: None,
},
),

View File

@@ -1,71 +1,70 @@
---
source: objdiff-core/tests/arch_ppc.rs
assertion_line: 20
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(4), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmpwi", 38), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 0), (Eol, Normal, 0)]
[(Address(8), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 43), Normal, 10), (BranchDest(20), Normal, 0), (Basic(" ~>"), Rotating(0), 0), (Eol, Normal, 0)]
[(Address(12), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 41), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 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(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), (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(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(44), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 43), Normal, 10), (BranchDest(56), Normal, 0), (Basic(" ~>"), Rotating(2), 0), (Eol, Normal, 0)]
[(Address(48), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 41), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 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(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), (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(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(80), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 166), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(1)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(84), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 43), Normal, 10), (BranchDest(96), Normal, 0), (Basic(" ~>"), Rotating(4), 0), (Eol, Normal, 0)]
[(Address(88), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 41), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 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(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), (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(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(120), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 166), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(2)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(124), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 43), Normal, 10), (BranchDest(136), Normal, 0), (Basic(" ~>"), Rotating(6), 0), (Eol, Normal, 0)]
[(Address(128), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 41), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 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(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), (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(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(160), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lis", 42), Normal, 10), (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("@ha"), Normal, 0), (Eol, Normal, 0)]
[(Address(164), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 41), Normal, 10), (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("@l"), 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(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), (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(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(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), (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(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(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), (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(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(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), (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(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(256), Normal, 5), (Basic(" ~> "), Rotating(11), 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(260), Normal, 5), (Spacing(4), Normal, 0), (Opcode("blr", 47), Normal, 10), (Eol, Normal, 0)]
[(Address(0), Normal, 5), (Spacing(4), Normal, 0), (Opcode("srwi", 283), 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(4), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmpwi", 260), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 0), (Eol, Normal, 0)]
[(Address(8), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 265), Normal, 10), (BranchDest(20), Normal, 0), (Basic(" ~>"), Rotating(0), 0), (Eol, Normal, 0)]
[(Address(12), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 263), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 0), (Eol, Normal, 0)]
[(Address(16), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 267), Normal, 10), (BranchDest(32), Normal, 0), (Basic(" ~>"), Rotating(1), 0), (Eol, Normal, 0)]
[(Address(20), Normal, 5), (Basic(" ~> "), Rotating(0), 0), (Opcode("lis", 264), 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", 263), 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", 323), 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", 283), 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", 445), 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", 260), Normal, 10), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 0), (Eol, Normal, 0)]
[(Address(44), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 265), Normal, 10), (BranchDest(56), Normal, 0), (Basic(" ~>"), Rotating(2), 0), (Eol, Normal, 0)]
[(Address(48), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 263), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 0), (Eol, Normal, 0)]
[(Address(52), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 267), Normal, 10), (BranchDest(68), Normal, 0), (Basic(" ~>"), Rotating(3), 0), (Eol, Normal, 0)]
[(Address(56), Normal, 5), (Basic(" ~> "), Rotating(2), 0), (Opcode("lis", 264), 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", 263), 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", 323), 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", 283), 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", 263), 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", 260), Normal, 10), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 0), (Eol, Normal, 0)]
[(Address(80), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 445), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(1)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(84), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 265), Normal, 10), (BranchDest(96), Normal, 0), (Basic(" ~>"), Rotating(4), 0), (Eol, Normal, 0)]
[(Address(88), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 263), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 0), (Eol, Normal, 0)]
[(Address(92), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 267), Normal, 10), (BranchDest(108), Normal, 0), (Basic(" ~>"), Rotating(5), 0), (Eol, Normal, 0)]
[(Address(96), Normal, 5), (Basic(" ~> "), Rotating(4), 0), (Opcode("lis", 264), 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", 263), 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", 323), 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", 283), 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", 263), 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", 260), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 0), (Eol, Normal, 0)]
[(Address(120), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 445), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(2)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(124), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 265), Normal, 10), (BranchDest(136), Normal, 0), (Basic(" ~>"), Rotating(6), 0), (Eol, Normal, 0)]
[(Address(128), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 263), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 0), (Eol, Normal, 0)]
[(Address(132), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 267), Normal, 10), (BranchDest(148), Normal, 0), (Basic(" ~>"), Rotating(7), 0), (Eol, Normal, 0)]
[(Address(136), Normal, 5), (Basic(" ~> "), Rotating(6), 0), (Opcode("lis", 264), 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", 263), 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", 323), 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", 263), 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", 263), 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", 445), 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(160), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lis", 264), Normal, 10), (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("@ha"), Normal, 0), (Eol, Normal, 0)]
[(Address(164), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (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("@l"), Normal, 0), (Eol, Normal, 0)]
[(Address(168), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 445), 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", 263), 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", 441), 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", 323), 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.", 289), 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", 265), Normal, 10), (BranchDest(196), Normal, 0), (Basic(" ~>"), Rotating(8), 0), (Eol, Normal, 0)]
[(Address(192), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 445), 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", 442), 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", 323), 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.", 289), 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", 265), Normal, 10), (BranchDest(216), Normal, 0), (Basic(" ~>"), Rotating(9), 0), (Eol, Normal, 0)]
[(Address(212), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 445), 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", 442), 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", 323), 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.", 289), 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", 265), Normal, 10), (BranchDest(236), Normal, 0), (Basic(" ~>"), Rotating(10), 0), (Eol, Normal, 0)]
[(Address(232), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 445), 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", 442), 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", 323), 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.", 289), 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", 265), Normal, 10), (BranchDest(256), Normal, 0), (Basic(" ~>"), Rotating(11), 0), (Eol, Normal, 0)]
[(Address(252), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 445), 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(256), Normal, 5), (Basic(" ~> "), Rotating(11), 0), (Opcode("li", 263), 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(260), Normal, 5), (Spacing(4), Normal, 0), (Opcode("blr", 269), Normal, 10), (Eol, Normal, 0)]

View File

@@ -4,6 +4,9 @@ expression: obj
---
Object {
arch: ArchPpc {
extensions: Extensions(
2,
),
extab: None,
},
endianness: Big,
@@ -581,4 +584,5 @@ Object {
),
path: None,
timestamp: None,
flow_analysis_results: {},
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,134 @@
---
source: objdiff-core/tests/arch_ppc.rs
expression: output
---
[(Address(0), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mflr", 342), Normal, 10), (Argument(Opaque("r12")), Normal, 0), (Eol, Normal, 0)]
[(Address(4), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stw", 443), Normal, 10), (Argument(Opaque("r12")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-8)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(8), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stwu", 444), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-336)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(12), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lis", 264), Normal, 10), (Argument(Opaque("r11")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__real@3f800000", demangled_name: None, address: 388, size: 4, kind: Object, section: Some(4), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@h"), Normal, 0), (Eol, Normal, 0)]
[(Address(16), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lfs", 455), Normal, 10), (Argument(Opaque("f0")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__real@3f800000", demangled_name: None, address: 388, size: 4, kind: Object, section: Some(4), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@l"), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r11")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(20), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stfs", 459), Normal, 10), (Argument(Opaque("f0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(272)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(24), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lis", 264), Normal, 10), (Argument(Opaque("r10")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__real@40000000", demangled_name: None, address: 384, size: 4, kind: Object, section: Some(4), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@h"), Normal, 0), (Eol, Normal, 0)]
[(Address(28), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lfs", 455), Normal, 10), (Argument(Opaque("f13")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__real@40000000", demangled_name: None, address: 384, size: 4, kind: Object, section: Some(4), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@l"), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r10")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(32), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stfs", 459), Normal, 10), (Argument(Opaque("f13")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(276)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(36), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lis", 264), Normal, 10), (Argument(Opaque("r9")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__real@40400000", demangled_name: None, address: 380, size: 4, kind: Object, section: Some(4), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@h"), Normal, 0), (Eol, Normal, 0)]
[(Address(40), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lfs", 455), Normal, 10), (Argument(Opaque("f12")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__real@40400000", demangled_name: None, address: 380, size: 4, kind: Object, section: Some(4), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@l"), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r9")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(44), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stfs", 459), Normal, 10), (Argument(Opaque("f12")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(280)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(48), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lis", 264), Normal, 10), (Argument(Opaque("r8")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__real@40800000", demangled_name: None, address: 376, size: 4, kind: Object, section: Some(4), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@h"), Normal, 0), (Eol, Normal, 0)]
[(Address(52), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lfs", 455), Normal, 10), (Argument(Opaque("f11")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__real@40800000", demangled_name: None, address: 376, size: 4, kind: Object, section: Some(4), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@l"), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r8")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(56), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stfs", 459), Normal, 10), (Argument(Opaque("f11")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(284)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(60), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lis", 264), Normal, 10), (Argument(Opaque("r7")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__real@40a00000", demangled_name: None, address: 372, size: 4, kind: Object, section: Some(4), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@h"), Normal, 0), (Eol, Normal, 0)]
[(Address(64), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lfs", 455), Normal, 10), (Argument(Opaque("f10")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__real@40a00000", demangled_name: None, address: 372, size: 4, kind: Object, section: Some(4), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@l"), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r7")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(68), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stfs", 459), Normal, 10), (Argument(Opaque("f10")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(256)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(72), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lis", 264), Normal, 10), (Argument(Opaque("r6")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__real@40c00000", demangled_name: None, address: 368, size: 4, kind: Object, section: Some(4), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@h"), Normal, 0), (Eol, Normal, 0)]
[(Address(76), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lfs", 455), Normal, 10), (Argument(Opaque("f9")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__real@40c00000", demangled_name: None, address: 368, size: 4, kind: Object, section: Some(4), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@l"), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r6")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(80), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stfs", 459), Normal, 10), (Argument(Opaque("f9")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(260)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(84), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lis", 264), Normal, 10), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__real@40e00000", demangled_name: None, address: 364, size: 4, kind: Object, section: Some(4), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@h"), Normal, 0), (Eol, Normal, 0)]
[(Address(88), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lfs", 455), Normal, 10), (Argument(Opaque("f8")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__real@40e00000", demangled_name: None, address: 364, size: 4, kind: Object, section: Some(4), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@l"), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(92), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stfs", 459), Normal, 10), (Argument(Opaque("f8")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(264)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(96), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lis", 264), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__real@41000000", demangled_name: None, address: 360, size: 4, kind: Object, section: Some(4), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@h"), Normal, 0), (Eol, Normal, 0)]
[(Address(100), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lfs", 455), Normal, 10), (Argument(Opaque("f7")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__real@41000000", demangled_name: None, address: 360, size: 4, kind: Object, section: Some(4), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@l"), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(104), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stfs", 459), Normal, 10), (Argument(Opaque("f7")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(268)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(108), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lis", 264), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__real@3f000000", demangled_name: None, address: 356, size: 4, kind: Object, section: Some(4), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@h"), Normal, 0), (Eol, Normal, 0)]
[(Address(112), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lfs", 455), Normal, 10), (Argument(Opaque("f6")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__real@3f000000", demangled_name: None, address: 356, size: 4, kind: Object, section: Some(4), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@l"), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(116), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stfs", 459), Normal, 10), (Argument(Opaque("f6")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(224)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(120), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lis", 264), Normal, 10), (Argument(Opaque("r11")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__real@3f000000", demangled_name: None, address: 356, size: 4, kind: Object, section: Some(4), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@h"), Normal, 0), (Eol, Normal, 0)]
[(Address(124), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lfs", 455), Normal, 10), (Argument(Opaque("f5")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__real@3f000000", demangled_name: None, address: 356, size: 4, kind: Object, section: Some(4), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@l"), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r11")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(128), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stfs", 459), Normal, 10), (Argument(Opaque("f5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(228)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(132), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lis", 264), Normal, 10), (Argument(Opaque("r10")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__real@3f000000", demangled_name: None, address: 356, size: 4, kind: Object, section: Some(4), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@h"), Normal, 0), (Eol, Normal, 0)]
[(Address(136), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lfs", 455), Normal, 10), (Argument(Opaque("f4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__real@3f000000", demangled_name: None, address: 356, size: 4, kind: Object, section: Some(4), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@l"), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r10")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(140), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stfs", 459), Normal, 10), (Argument(Opaque("f4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(232)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(144), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lis", 264), Normal, 10), (Argument(Opaque("r9")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__real@3f000000", demangled_name: None, address: 356, size: 4, kind: Object, section: Some(4), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@h"), Normal, 0), (Eol, Normal, 0)]
[(Address(148), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lfs", 455), Normal, 10), (Argument(Opaque("f3")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__real@3f000000", demangled_name: None, address: 356, size: 4, kind: Object, section: Some(4), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@l"), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r9")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(152), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stfs", 459), Normal, 10), (Argument(Opaque("f3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(236)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(156), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 263), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(0)), Normal, 0), (Eol, Normal, 0)]
[(Address(160), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(272)), Normal, 0), (Eol, Normal, 0)]
[(Address(164), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 267), Normal, 10), (Symbol(Symbol { name: "__lvx", demangled_name: None, address: 640, size: 24, kind: Function, section: Some(5), flags: FlagSet(Local | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
[(Address(168), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r8")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(80)), Normal, 0), (Eol, Normal, 0)]
[(Address(172), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stvx128", 194), Normal, 10), (Argument(Opaque("v1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r8")), Normal, 0), (Eol, Normal, 0)]
[(Address(176), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r7")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(80)), Normal, 0), (Eol, Normal, 0)]
[(Address(180), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lvx128", 187), Normal, 10), (Argument(Opaque("v0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r7")), Normal, 0), (Eol, Normal, 0)]
[(Address(184), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r6")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(240)), Normal, 0), (Eol, Normal, 0)]
[(Address(188), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stvx128", 194), Normal, 10), (Argument(Opaque("v0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r6")), Normal, 0), (Eol, Normal, 0)]
[(Address(192), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 263), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(0)), Normal, 0), (Eol, Normal, 0)]
[(Address(196), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(256)), Normal, 0), (Eol, Normal, 0)]
[(Address(200), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 267), Normal, 10), (Symbol(Symbol { name: "__lvx", demangled_name: None, address: 640, size: 24, kind: Function, section: Some(5), flags: FlagSet(Local | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
[(Address(204), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(96)), Normal, 0), (Eol, Normal, 0)]
[(Address(208), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stvx128", 194), Normal, 10), (Argument(Opaque("v1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Eol, Normal, 0)]
[(Address(212), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(96)), Normal, 0), (Eol, Normal, 0)]
[(Address(216), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lvx128", 187), Normal, 10), (Argument(Opaque("v13")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Eol, Normal, 0)]
[(Address(220), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(208)), Normal, 0), (Eol, Normal, 0)]
[(Address(224), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stvx128", 194), Normal, 10), (Argument(Opaque("v13")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Eol, Normal, 0)]
[(Address(228), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 263), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(0)), Normal, 0), (Eol, Normal, 0)]
[(Address(232), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(224)), Normal, 0), (Eol, Normal, 0)]
[(Address(236), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 267), Normal, 10), (Symbol(Symbol { name: "__lvx", demangled_name: None, address: 640, size: 24, kind: Function, section: Some(5), flags: FlagSet(Local | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
[(Address(240), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r11")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(112)), Normal, 0), (Eol, Normal, 0)]
[(Address(244), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stvx128", 194), Normal, 10), (Argument(Opaque("v1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r11")), Normal, 0), (Eol, Normal, 0)]
[(Address(248), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r10")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(112)), Normal, 0), (Eol, Normal, 0)]
[(Address(252), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lvx128", 187), Normal, 10), (Argument(Opaque("v12")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r10")), Normal, 0), (Eol, Normal, 0)]
[(Address(256), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r9")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(304)), Normal, 0), (Eol, Normal, 0)]
[(Address(260), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stvx128", 194), Normal, 10), (Argument(Opaque("v12")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r9")), Normal, 0), (Eol, Normal, 0)]
[(Address(264), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r8")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(304)), Normal, 0), (Eol, Normal, 0)]
[(Address(268), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lvx128", 187), Normal, 10), (Argument(Opaque("v11")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r8")), Normal, 0), (Eol, Normal, 0)]
[(Address(272), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r7")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(208)), Normal, 0), (Eol, Normal, 0)]
[(Address(276), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lvx128", 187), Normal, 10), (Argument(Opaque("v10")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r7")), Normal, 0), (Eol, Normal, 0)]
[(Address(280), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r6")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(240)), Normal, 0), (Eol, Normal, 0)]
[(Address(284), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lvx128", 187), Normal, 10), (Argument(Opaque("v9")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r6")), Normal, 0), (Eol, Normal, 0)]
[(Address(288), Normal, 5), (Spacing(4), Normal, 0), (Opcode("vmaddfp", 76), Normal, 10), (Argument(Opaque("v8")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("v9")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("v10")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("v11")), Normal, 0), (Eol, Normal, 0)]
[(Address(292), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(128)), Normal, 0), (Eol, Normal, 0)]
[(Address(296), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stvx128", 194), Normal, 10), (Argument(Opaque("v8")), Normal, 0), (Basic(", "), Normal, 0), (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("addi", 263), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(128)), Normal, 0), (Eol, Normal, 0)]
[(Address(304), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lvx128", 187), Normal, 10), (Argument(Opaque("v7")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Eol, Normal, 0)]
[(Address(308), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(192)), Normal, 0), (Eol, Normal, 0)]
[(Address(312), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stvx128", 194), Normal, 10), (Argument(Opaque("v7")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Eol, Normal, 0)]
[(Address(316), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r11")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(208)), Normal, 0), (Eol, Normal, 0)]
[(Address(320), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lvx128", 187), Normal, 10), (Argument(Opaque("v6")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r11")), Normal, 0), (Eol, Normal, 0)]
[(Address(324), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r10")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(240)), Normal, 0), (Eol, Normal, 0)]
[(Address(328), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lvx128", 187), Normal, 10), (Argument(Opaque("v5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r10")), Normal, 0), (Eol, Normal, 0)]
[(Address(332), Normal, 5), (Spacing(4), Normal, 0), (Opcode("vmsum4fp128", 203), Normal, 10), (Argument(Opaque("v4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("v5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("v6")), Normal, 0), (Eol, Normal, 0)]
[(Address(336), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r9")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(144)), Normal, 0), (Eol, Normal, 0)]
[(Address(340), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stvx128", 194), Normal, 10), (Argument(Opaque("v4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r9")), Normal, 0), (Eol, Normal, 0)]
[(Address(344), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r8")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(144)), Normal, 0), (Eol, Normal, 0)]
[(Address(348), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lvx128", 187), Normal, 10), (Argument(Opaque("v3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r8")), Normal, 0), (Eol, Normal, 0)]
[(Address(352), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r7")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(176)), Normal, 0), (Eol, Normal, 0)]
[(Address(356), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stvx128", 194), Normal, 10), (Argument(Opaque("v3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r7")), Normal, 0), (Eol, Normal, 0)]
[(Address(360), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 263), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(0)), Normal, 0), (Eol, Normal, 0)]
[(Address(364), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(288)), Normal, 0), (Eol, Normal, 0)]
[(Address(368), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r6")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(192)), Normal, 0), (Eol, Normal, 0)]
[(Address(372), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lvx128", 187), Normal, 10), (Argument(Opaque("v1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r6")), Normal, 0), (Eol, Normal, 0)]
[(Address(376), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 267), Normal, 10), (Symbol(Symbol { name: "__stvx", demangled_name: None, address: 664, size: 40, kind: Function, section: Some(5), flags: FlagSet(Local | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
[(Address(380), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 263), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(0)), Normal, 0), (Eol, Normal, 0)]
[(Address(384), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(160)), Normal, 0), (Eol, Normal, 0)]
[(Address(388), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(176)), Normal, 0), (Eol, Normal, 0)]
[(Address(392), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lvx128", 187), Normal, 10), (Argument(Opaque("v1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Eol, Normal, 0)]
[(Address(396), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 267), Normal, 10), (Symbol(Symbol { name: "__stvx", demangled_name: None, address: 664, size: 40, kind: Function, section: Some(5), flags: FlagSet(Local | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
[(Address(400), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lis", 264), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "$SG4433", demangled_name: None, address: 32, size: 32, kind: Object, section: Some(4), flags: FlagSet(Local | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@h"), Normal, 0), (Eol, Normal, 0)]
[(Address(404), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "$SG4433", demangled_name: None, address: 32, size: 32, kind: Object, section: Some(4), flags: FlagSet(Local | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@l"), Normal, 0), (Eol, Normal, 0)]
[(Address(408), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 267), Normal, 10), (Symbol(Symbol { name: "printf", demangled_name: None, address: 0, size: 0, kind: Function, section: None, flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
[(Address(412), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(240)), Normal, 0), (Eol, Normal, 0)]
[(Address(416), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lvx128", 187), Normal, 10), (Argument(Opaque("v1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Eol, Normal, 0)]
[(Address(420), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lis", 264), Normal, 10), (Argument(Opaque("r11")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "$SG4434", demangled_name: None, address: 64, size: 8, kind: Object, section: Some(4), flags: FlagSet(Local | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@h"), Normal, 0), (Eol, Normal, 0)]
[(Address(424), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r11")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "$SG4434", demangled_name: None, address: 64, size: 8, kind: Object, section: Some(4), flags: FlagSet(Local | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@l"), Normal, 0), (Eol, Normal, 0)]
[(Address(428), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 267), Normal, 10), (Symbol(Symbol { name: "?PrintVector@@YAXPBDU__vector4@@@Z", demangled_name: Some("void __cdecl PrintVector(char const *, struct __vector4)"), address: 0, size: 120, kind: Function, section: Some(5), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
[(Address(432), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r10")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(208)), Normal, 0), (Eol, Normal, 0)]
[(Address(436), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lvx128", 187), Normal, 10), (Argument(Opaque("v1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r10")), Normal, 0), (Eol, Normal, 0)]
[(Address(440), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lis", 264), Normal, 10), (Argument(Opaque("r9")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "$SG4435", demangled_name: None, address: 72, size: 8, kind: Object, section: Some(4), flags: FlagSet(Local | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@h"), Normal, 0), (Eol, Normal, 0)]
[(Address(444), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r9")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "$SG4435", demangled_name: None, address: 72, size: 8, kind: Object, section: Some(4), flags: FlagSet(Local | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@l"), Normal, 0), (Eol, Normal, 0)]
[(Address(448), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 267), Normal, 10), (Symbol(Symbol { name: "?PrintVector@@YAXPBDU__vector4@@@Z", demangled_name: Some("void __cdecl PrintVector(char const *, struct __vector4)"), address: 0, size: 120, kind: Function, section: Some(5), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
[(Address(452), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r8")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(192)), Normal, 0), (Eol, Normal, 0)]
[(Address(456), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lvx128", 187), Normal, 10), (Argument(Opaque("v1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r8")), Normal, 0), (Eol, Normal, 0)]
[(Address(460), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lis", 264), Normal, 10), (Argument(Opaque("r7")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "$SG4436", demangled_name: None, address: 80, size: 12, kind: Object, section: Some(4), flags: FlagSet(Local | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@h"), Normal, 0), (Eol, Normal, 0)]
[(Address(464), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r7")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "$SG4436", demangled_name: None, address: 80, size: 12, kind: Object, section: Some(4), flags: FlagSet(Local | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@l"), Normal, 0), (Eol, Normal, 0)]
[(Address(468), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 267), Normal, 10), (Symbol(Symbol { name: "?PrintVector@@YAXPBDU__vector4@@@Z", demangled_name: Some("void __cdecl PrintVector(char const *, struct __vector4)"), address: 0, size: 120, kind: Function, section: Some(5), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
[(Address(472), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r6")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(176)), Normal, 0), (Eol, Normal, 0)]
[(Address(476), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lvx128", 187), Normal, 10), (Argument(Opaque("v1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r6")), Normal, 0), (Eol, Normal, 0)]
[(Address(480), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lis", 264), Normal, 10), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "$SG4437", demangled_name: None, address: 92, size: 12, kind: Object, section: Some(4), flags: FlagSet(Local | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@h"), Normal, 0), (Eol, Normal, 0)]
[(Address(484), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "$SG4437", demangled_name: None, address: 92, size: 12, kind: Object, section: Some(4), flags: FlagSet(Local | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@l"), Normal, 0), (Eol, Normal, 0)]
[(Address(488), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 267), Normal, 10), (Symbol(Symbol { name: "?PrintVector@@YAXPBDU__vector4@@@Z", demangled_name: Some("void __cdecl PrintVector(char const *, struct __vector4)"), address: 0, size: 120, kind: Function, section: Some(5), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
[(Address(492), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lis", 264), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "$SG4438", demangled_name: None, address: 104, size: 4, kind: Object, section: Some(4), flags: FlagSet(Local | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@h"), Normal, 0), (Eol, Normal, 0)]
[(Address(496), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "$SG4438", demangled_name: None, address: 104, size: 4, kind: Object, section: Some(4), flags: FlagSet(Local | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@l"), Normal, 0), (Eol, Normal, 0)]
[(Address(500), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 267), Normal, 10), (Symbol(Symbol { name: "printf", demangled_name: None, address: 0, size: 0, kind: Function, section: None, flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
[(Address(504), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(336)), Normal, 0), (Eol, Normal, 0)]
[(Address(508), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lwz", 439), Normal, 10), (Argument(Opaque("r12")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-8)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(512), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mtlr", 348), Normal, 10), (Argument(Opaque("r12")), Normal, 0), (Eol, Normal, 0)]
[(Address(516), Normal, 5), (Spacing(4), Normal, 0), (Opcode("blr", 269), Normal, 10), (Eol, Normal, 0)]

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@@ -507,116 +507,4 @@ expression: diff.instruction_rows
),
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 88,
size: 1,
opcode: 465,
branch_dest: None,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 89,
size: 1,
opcode: 465,
branch_dest: None,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 90,
size: 1,
opcode: 465,
branch_dest: None,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 91,
size: 1,
opcode: 465,
branch_dest: None,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 92,
size: 1,
opcode: 465,
branch_dest: None,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 93,
size: 1,
opcode: 465,
branch_dest: None,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 94,
size: 1,
opcode: 465,
branch_dest: None,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 95,
size: 1,
opcode: 465,
branch_dest: None,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
]

View File

@@ -29,11 +29,3 @@ expression: output
[(Address(76), Normal, 5), (Spacing(4), Normal, 0), (Opcode(".dword", 65534), Normal, 10), (BranchDest(41), Normal, 0), (Basic(" ~>"), Rotating(6), 0), (Eol, Normal, 0)]
[(Address(80), Normal, 5), (Spacing(4), Normal, 0), (Opcode(".dword", 65534), Normal, 10), (BranchDest(47), Normal, 0), (Basic(" ~>"), Rotating(7), 0), (Eol, Normal, 0)]
[(Address(84), Normal, 5), (Spacing(4), Normal, 0), (Opcode(".dword", 65534), Normal, 10), (BranchDest(53), Normal, 0), (Basic(" ~>"), Rotating(8), 0), (Eol, Normal, 0)]
[(Address(88), Normal, 5), (Spacing(4), Normal, 0), (Opcode("nop", 465), Normal, 10), (Eol, Normal, 0)]
[(Address(89), Normal, 5), (Spacing(4), Normal, 0), (Opcode("nop", 465), Normal, 10), (Eol, Normal, 0)]
[(Address(90), Normal, 5), (Spacing(4), Normal, 0), (Opcode("nop", 465), Normal, 10), (Eol, Normal, 0)]
[(Address(91), Normal, 5), (Spacing(4), Normal, 0), (Opcode("nop", 465), Normal, 10), (Eol, Normal, 0)]
[(Address(92), Normal, 5), (Spacing(4), Normal, 0), (Opcode("nop", 465), Normal, 10), (Eol, Normal, 0)]
[(Address(93), Normal, 5), (Spacing(4), Normal, 0), (Opcode("nop", 465), Normal, 10), (Eol, Normal, 0)]
[(Address(94), Normal, 5), (Spacing(4), Normal, 0), (Opcode("nop", 465), Normal, 10), (Eol, Normal, 0)]
[(Address(95), Normal, 5), (Spacing(4), Normal, 0), (Opcode("nop", 465), Normal, 10), (Eol, Normal, 0)]

View File

@@ -63,7 +63,7 @@ Object {
"int __cdecl test(int)",
),
address: 0,
size: 96,
size: 88,
kind: Function,
section: Some(
1,
@@ -311,4 +311,5 @@ Object {
split_meta: None,
path: None,
timestamp: None,
flow_analysis_results: {},
}

View File

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

View File

@@ -29,8 +29,8 @@ cfg-if = "1.0"
const_format = "0.2"
cwdemangle = "1.0"
dirs = "6.0"
egui = "0.31"
egui_extras = "0.31"
egui = "0.32"
egui_extras = "0.32"
filetime = "0.2"
float-ord = "0.3"
font-kit = "0.14"
@@ -43,18 +43,19 @@ pollster = "0.4"
regex = "1.11"
rfd = { version = "0.15" } #, default-features = false, features = ['xdg-portal']
rlwinmdec = "1.1"
ron = "0.8"
ron = "0.10"
serde = { version = "1.0", features = ["derive"] }
time = { version = "0.3", features = ["formatting", "local-offset"] }
typed-path = "0.11"
winit = { version = "0.30", features = ["wayland-csd-adwaita"] }
winit = { version = "0.30", features = ["default"] }
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
# Keep version in sync with egui
[dependencies.eframe]
version = "0.31"
version = "0.32"
features = [
"default_fonts",
"glow",
"persistence",
"wayland",
"x11",
@@ -63,10 +64,12 @@ default-features = false
# Keep version in sync with eframe
[dependencies.wgpu]
version = "24.0"
version = "25.0"
features = [
"dx12",
"metal",
"gles",
"vulkan",
"webgpu",
]
optional = true

View File

@@ -461,11 +461,11 @@ impl App {
use eframe::egui_wgpu::wgpu::Backend;
let info = wgpu_render_state.adapter.get_info();
app.view_state.graphics_state.active_backend = match info.backend {
Backend::Empty => "Unknown",
Backend::Noop => "None",
Backend::Vulkan => "Vulkan",
Backend::Metal => "Metal",
Backend::Dx12 => "DirectX 12",
Backend::Gl => "OpenGL",
Backend::Gl => "OpenGL ES",
Backend::BrowserWebGpu => "WebGPU",
}
.to_string();
@@ -474,7 +474,7 @@ impl App {
#[cfg(feature = "glow")]
if let Some(gl) = &cc.gl {
use eframe::glow::HasContext;
app.view_state.graphics_state.active_backend = "OpenGL (Fallback)".to_string();
app.view_state.graphics_state.active_backend = "OpenGL".to_string();
app.view_state.graphics_state.active_device =
unsafe { gl.get_parameter_string(0x1F01) }; // GL_RENDERER
}
@@ -526,14 +526,12 @@ impl App {
mod_check = true;
}
if mod_check {
if let Some(info) = &state.project_config_info {
if let Some(last_ts) = info.timestamp {
if file_modified(&info.path, last_ts) {
state.config_change = true;
}
}
}
if mod_check
&& let Some(info) = &state.project_config_info
&& let Some(last_ts) = info.timestamp
&& file_modified(&info.path, last_ts)
{
state.config_change = true;
}
if state.config_change {
@@ -542,7 +540,7 @@ impl App {
Ok(()) => state.config_error = None,
Err(e) => {
log::error!("Failed to load project config: {e}");
state.config_error = Some(format!("{e}"));
state.config_error = Some(e.to_string());
}
}
}
@@ -581,22 +579,20 @@ impl App {
state.queue_build = true;
}
if let Some(result) = &diff_state.build {
if mod_check {
if let Some((obj, _)) = &result.first_obj {
if let (Some(path), Some(timestamp)) = (&obj.path, obj.timestamp) {
if file_modified(path, timestamp) {
state.queue_reload = true;
}
}
}
if let Some((obj, _)) = &result.second_obj {
if let (Some(path), Some(timestamp)) = (&obj.path, obj.timestamp) {
if file_modified(path, timestamp) {
state.queue_reload = true;
}
}
}
if let Some(result) = &diff_state.build
&& mod_check
{
if let Some((obj, _)) = &result.first_obj
&& let (Some(path), Some(timestamp)) = (&obj.path, obj.timestamp)
&& file_modified(path, timestamp)
{
state.queue_reload = true;
}
if let Some((obj, _)) = &result.second_obj
&& let (Some(path), Some(timestamp)) = (&obj.path, obj.timestamp)
&& file_modified(path, timestamp)
{
state.queue_reload = true;
}
}
@@ -618,13 +614,12 @@ impl App {
state.queue_reload = false;
}
if graphics_state.should_relaunch {
if let Some(app_path) = &self.app_path {
if let Ok(mut guard) = self.relaunch_path.lock() {
*guard = Some(app_path.clone());
self.should_relaunch = true;
}
}
if graphics_state.should_relaunch
&& let Some(app_path) = &self.app_path
&& let Ok(mut guard) = self.relaunch_path.lock()
{
*guard = Some(app_path.clone());
self.should_relaunch = true;
}
}
}
@@ -665,6 +660,9 @@ impl eframe::App for App {
let side_panel_available = diff_state.current_view == View::SymbolDiff;
egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
// Temporarily use pre-egui 0.32 menu. ComboBox within menu
// is currently broken. Issue TBD
#[allow(deprecated)]
egui::menu::bar(ui, |ui| {
if ui
.add_enabled(
@@ -677,15 +675,16 @@ impl eframe::App for App {
*show_side_panel = !*show_side_panel;
}
ui.separator();
ui.menu_button("File", |ui| {
let bar_state = egui::menu::BarState::load(ui.ctx(), ui.id());
egui::menu::menu_button(ui, "File", |ui| {
#[cfg(debug_assertions)]
if ui.button("Debug…").clicked() {
*show_debug = !*show_debug;
ui.close_menu();
ui.close();
}
if ui.button("Project…").clicked() {
*show_project_config = !*show_project_config;
ui.close_menu();
ui.close();
}
let recent_projects = if let Ok(guard) = state.read() {
guard.config.recent_projects.clone()
@@ -694,49 +693,56 @@ impl eframe::App for App {
};
if recent_projects.is_empty() {
ui.add_enabled(false, egui::Button::new("Recent projects…"));
} else {
ui.menu_button("Recent Projects…", |ui| {
if ui.button("Clear").clicked() {
state.write().unwrap().config.recent_projects.clear();
};
ui.separator();
for path in recent_projects {
if ui.button(&path).clicked() {
state
.write()
.unwrap()
.set_project_dir(Utf8PlatformPathBuf::from(path));
ui.close_menu();
} else if let Some(menu_root) = bar_state.as_ref() {
egui::menu::submenu_button(
ui,
menu_root.menu_state.clone(),
"Recent Projects…",
|ui| {
if ui.button("Clear").clicked() {
state.write().unwrap().config.recent_projects.clear();
};
ui.separator();
for path in recent_projects {
if ui.button(&path).clicked() {
state
.write()
.unwrap()
.set_project_dir(Utf8PlatformPathBuf::from(path));
ui.close();
}
}
}
});
},
);
} else {
ui.add_enabled(false, egui::Button::new("Recent projects…"));
}
if ui.button("Appearance…").clicked() {
*show_appearance_config = !*show_appearance_config;
ui.close_menu();
ui.close();
}
if ui.button("Graphics…").clicked() {
*show_graphics = !*show_graphics;
ui.close_menu();
ui.close();
}
if ui.button("Quit").clicked() {
ctx.send_viewport_cmd(egui::ViewportCommand::Close);
}
});
ui.menu_button("Tools", |ui| {
egui::menu::menu_button(ui, "Tools", |ui| {
if ui.button("Demangle…").clicked() {
*show_demangle = !*show_demangle;
ui.close_menu();
ui.close();
}
if ui.button("Rlwinm Decoder…").clicked() {
*show_rlwinm_decode = !*show_rlwinm_decode;
ui.close_menu();
ui.close();
}
});
ui.menu_button("Diff Options", |ui| {
egui::menu::menu_button(ui, "Diff Options", |ui| {
if ui.button("Arch Settings…").clicked() {
*show_arch_config = !*show_arch_config;
ui.close_menu();
ui.close();
}
let mut state = state.write().unwrap();
let response = ui

View File

@@ -25,7 +25,7 @@ pub fn load_font_family(
) -> Option<LoadedFontFamily> {
let family_handle = source.select_family_by_name(name).ok()?;
if family_handle.fonts().is_empty() {
log::warn!("No fonts found for family '{}'", name);
log::warn!("No fonts found for family '{name}'");
return None;
}
let handles = family_handle.fonts().to_vec();
@@ -34,7 +34,7 @@ pub fn load_font_family(
match font_kit::loaders::default::Font::from_handle(handle) {
Ok(font) => loaded.push(font),
Err(err) => {
log::warn!("Failed to load font '{}': {}", name, err);
log::warn!("Failed to load font '{name}': {err}");
return None;
}
}
@@ -89,7 +89,7 @@ pub fn load_font_if_needed(
egui::FontFamily::Name(v) => v,
};
let family = load_font_family(source, family_name)
.with_context(|| format!("Failed to load font family '{}'", family_name))?;
.with_context(|| format!("Failed to load font family '{family_name}'"))?;
let default_fonts = fonts.families.get(&base_family).cloned().unwrap_or_default();
// FIXME clean up
let default_font_ref = family.fonts.get(family.default_index).unwrap();

View File

@@ -95,7 +95,8 @@ fn main() -> ExitCode {
GraphicsBackend::Dx12 => wgpu::Backends::DX12,
GraphicsBackend::Metal => wgpu::Backends::METAL,
GraphicsBackend::Vulkan => wgpu::Backends::VULKAN,
GraphicsBackend::OpenGL => wgpu::Backends::GL,
GraphicsBackend::OpenGLES => wgpu::Backends::GL,
GraphicsBackend::OpenGL => wgpu::Backends::empty(),
};
WgpuSetup::CreateNew(setup)
}
@@ -172,23 +173,23 @@ fn main() -> ExitCode {
}
// Attempt to relaunch application from the updated path
if let Ok(mut guard) = exec_path.lock() {
if let Some(path) = guard.take() {
cfg_if! {
if #[cfg(unix)] {
let e = exec::Command::new(path)
.args(&std::env::args().collect::<Vec<String>>())
.exec();
if let Ok(mut guard) = exec_path.lock()
&& let Some(path) = guard.take()
{
cfg_if! {
if #[cfg(unix)] {
let e = exec::Command::new(path)
.args(&std::env::args().collect::<Vec<String>>())
.exec();
log::error!("Failed to relaunch: {e:?}");
return ExitCode::FAILURE;
} else {
let result = std::process::Command::new(path)
.args(std::env::args())
.spawn();
if let Err(e) = result {
log::error!("Failed to relaunch: {e:?}");
return ExitCode::FAILURE;
} else {
let result = std::process::Command::new(path)
.args(std::env::args())
.spawn();
if let Err(e) = result {
log::error!("Failed to relaunch: {e:?}");
return ExitCode::FAILURE;
}
}
}
}

View File

@@ -23,6 +23,8 @@ pub struct Appearance {
#[serde(skip)]
pub highlight_color: Color32, // WHITE
#[serde(skip)]
pub dataflow_color: Color32, //
#[serde(skip)]
pub replace_color: Color32, // LIGHT_BLUE
#[serde(skip)]
pub insert_color: Color32, // GREEN
@@ -61,6 +63,7 @@ impl Default for Appearance {
emphasized_text_color: Color32::LIGHT_GRAY,
deemphasized_text_color: Color32::DARK_GRAY,
highlight_color: Color32::WHITE,
dataflow_color: Color32::from_rgb(0, 128, 128),
replace_color: Color32::LIGHT_BLUE,
insert_color: Color32::GREEN,
delete_color: Color32::from_rgb(200, 40, 41),
@@ -104,6 +107,7 @@ impl Appearance {
self.emphasized_text_color = Color32::LIGHT_GRAY;
self.deemphasized_text_color = Color32::DARK_GRAY;
self.highlight_color = Color32::WHITE;
self.dataflow_color = Color32::from_rgb(0, 128, 128);
self.replace_color = Color32::LIGHT_BLUE;
self.insert_color = Color32::GREEN;
self.delete_color = Color32::from_rgb(200, 40, 41);
@@ -114,6 +118,7 @@ impl Appearance {
self.emphasized_text_color = Color32::DARK_GRAY;
self.deemphasized_text_color = Color32::LIGHT_GRAY;
self.highlight_color = Color32::BLACK;
self.dataflow_color = Color32::from_rgb(0, 128, 128);
self.replace_color = Color32::DARK_BLUE;
self.insert_color = Color32::DARK_GREEN;
self.delete_color = Color32::from_rgb(200, 40, 41);
@@ -136,7 +141,7 @@ impl Appearance {
) {
Ok(()) => self.ui_font = next_ui_font,
Err(e) => {
log::error!("Failed to load font: {}", e)
log::error!("Failed to load font: {e}")
}
}
}
@@ -150,7 +155,7 @@ impl Appearance {
) {
Ok(()) => self.code_font = next_code_font,
Err(e) => {
log::error!("Failed to load font: {}", e)
log::error!("Failed to load font: {e}")
}
}
}
@@ -167,7 +172,7 @@ impl Appearance {
) {
Ok(_) => {}
Err(e) => {
log::error!("Failed to load font: {}", e);
log::error!("Failed to load font: {e}");
// Revert to default
self.ui_font = DEFAULT_UI_FONT;
}
@@ -181,7 +186,7 @@ impl Appearance {
) {
Ok(_) => {}
Err(e) => {
log::error!("Failed to load font: {}", e);
log::error!("Failed to load font: {e}");
// Revert to default
self.code_font = DEFAULT_CODE_FONT;
}

View File

@@ -5,8 +5,8 @@ use std::{mem::take, path::MAIN_SEPARATOR};
#[cfg(all(windows, feature = "wsl"))]
use anyhow::{Context, Result};
use egui::{
CollapsingHeader, FontFamily, FontId, RichText, SelectableLabel, TextFormat, Widget,
output::OpenUrl, text::LayoutJob,
CollapsingHeader, FontFamily, FontId, RichText, TextFormat, Widget, output::OpenUrl,
text::LayoutJob,
};
use globset::Glob;
use objdiff_core::{
@@ -185,16 +185,15 @@ pub fn config_ui(
if result.update_available {
ui.colored_label(appearance.insert_color, "Update available");
ui.horizontal(|ui| {
if let Some(bin_name) = &result.found_binary {
if ui
if let Some(bin_name) = &result.found_binary
&& ui
.add_enabled(!config_state.update_running, egui::Button::new("Automatic"))
.on_hover_text_at_pointer(
"Automatically download and replace the current build",
)
.clicked()
{
config_state.queue_update = Some(bin_name.clone());
}
{
config_state.queue_update = Some(bin_name.clone());
}
if ui
.button("Manual")
@@ -278,7 +277,7 @@ pub fn config_ui(
{
filters_text = filters_text.color(appearance.replace_color);
}
egui::menu::menu_button(ui, filters_text, |ui| {
egui::containers::menu::MenuButton::new(filters_text).ui(ui, |ui| {
ui.checkbox(&mut config_state.filter_diffable, "Diffable")
.on_hover_text_at_pointer("Only show objects with a source file");
ui.checkbox(&mut config_state.filter_incomplete, "Incomplete")
@@ -329,12 +328,12 @@ pub fn config_ui(
});
});
}
if new_selected_index != selected_index {
if let Some(idx) = new_selected_index {
// Will set obj_changed, which will trigger a rebuild
let config = objects[idx].clone();
state_guard.set_selected_obj(config);
}
if new_selected_index != selected_index
&& let Some(idx) = new_selected_index
{
// Will set obj_changed, which will trigger a rebuild
let config = objects[idx].clone();
state_guard.set_selected_obj(config);
}
}
@@ -355,7 +354,7 @@ fn display_unit(
} else {
appearance.text_color
};
let response = SelectableLabel::new(
let response = egui::Button::selectable(
selected,
RichText::new(name)
.font(FontId {
@@ -374,18 +373,17 @@ fn display_unit(
}
fn object_context_ui(ui: &mut egui::Ui, object: &ObjectConfig) {
if let Some(source_path) = &object.source_path {
if ui
if let Some(source_path) = &object.source_path
&& ui
.button("Open source file")
.on_hover_text("Open the source file in the default editor")
.clicked()
{
log::info!("Opening file {}", source_path);
if let Err(e) = open::that_detached(source_path.as_str()) {
log::error!("Failed to open source file: {e}");
}
ui.close_menu();
{
log::info!("Opening file {source_path}");
if let Err(e) = open::that_detached(source_path.as_str()) {
log::error!("Failed to open source file: {e}");
}
ui.close();
}
}
@@ -509,7 +507,7 @@ fn format_path(path: &Option<Utf8PlatformPathBuf>, appearance: &Appearance) -> R
.and_then(|home| check_path_buf(home).ok())
.and_then(|home| dir.strip_prefix(&home).ok())
{
format!("~{}{}", MAIN_SEPARATOR, rel)
format!("~{MAIN_SEPARATOR}{rel}")
} else {
dir.to_string()
}
@@ -808,7 +806,7 @@ fn split_obj_config_ui(
for (idx, glob) in state.config.watch_patterns.iter().enumerate() {
ui.horizontal(|ui| {
ui.label(
RichText::new(format!("{}", glob))
RichText::new(glob.to_string())
.color(appearance.text_color)
.family(FontFamily::Monospace),
);
@@ -835,12 +833,11 @@ fn split_obj_config_ui(
.add_enabled(state.project_config_info.is_none(), egui::Button::new("+").small())
.on_disabled_hover_text(CONFIG_DISABLED_TEXT)
.clicked()
&& let Ok(glob) = Glob::new(&config_state.watch_pattern_text)
{
if let Ok(glob) = Glob::new(&config_state.watch_pattern_text) {
state.config.watch_patterns.push(glob);
state.watcher_change = true;
config_state.watch_pattern_text.clear();
}
state.config.watch_patterns.push(glob);
state.watcher_change = true;
config_state.watch_pattern_text.clear();
}
});
}

View File

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

View File

@@ -49,7 +49,9 @@ impl<'a> DiffColumnContext<'a> {
let selected_symbol = match view {
View::SymbolDiff => None,
View::FunctionDiff | View::ExtabDiff => match (obj, selected_symbol) {
(Some(obj), Some(s)) => find_symbol(&obj.0, s).map(SelectedSymbol::Symbol),
(Some(obj), Some(s)) => {
obj.0.symbol_by_name(&s.symbol_name).map(SelectedSymbol::Symbol)
}
_ => None,
},
View::DataDiff => match (obj, selected_symbol) {
@@ -126,10 +128,10 @@ pub fn diff_view_ui(
let mut navigation = current_navigation.clone();
if let Some((_symbol, symbol_diff, _symbol_idx)) = left_ctx.symbol {
// If a matching symbol appears, select it
if !right_ctx.has_symbol() {
if let Some(target_symbol_ref) = symbol_diff.target_symbol {
navigation.right_symbol = Some(target_symbol_ref);
}
if !right_ctx.has_symbol()
&& let Some(target_symbol_ref) = symbol_diff.target_symbol
{
navigation.right_symbol = Some(target_symbol_ref);
}
} else if navigation.left_symbol.is_some()
&& left_ctx.obj.is_some()
@@ -140,10 +142,10 @@ pub fn diff_view_ui(
}
if let Some((_symbol, symbol_diff, _symbol_idx)) = right_ctx.symbol {
// If a matching symbol appears, select it
if !left_ctx.has_symbol() {
if let Some(target_symbol_ref) = symbol_diff.target_symbol {
navigation.left_symbol = Some(target_symbol_ref);
}
if !left_ctx.has_symbol()
&& let Some(target_symbol_ref) = symbol_diff.target_symbol
{
navigation.left_symbol = Some(target_symbol_ref);
}
} else if navigation.right_symbol.is_some()
&& right_ctx.obj.is_some()
@@ -245,16 +247,15 @@ pub fn diff_view_ui(
// Third row
if left_ctx.has_symbol() && right_ctx.has_symbol() {
if state.current_view == View::FunctionDiff
if (state.current_view == View::FunctionDiff
&& ui
.button("Change target")
.on_hover_text_at_pointer("Choose a different symbol to use as the target")
.clicked()
|| hotkeys::consume_change_target_shortcut(ui.ctx())
|| hotkeys::consume_change_target_shortcut(ui.ctx()))
&& let Some(symbol_ref) = state.symbol_state.right_symbol.as_ref()
{
if let Some(symbol_ref) = state.symbol_state.right_symbol.as_ref() {
ret = Some(DiffViewAction::SelectingLeft(symbol_ref.clone()));
}
ret = Some(DiffViewAction::SelectingLeft(symbol_ref.clone()));
}
} else if left_ctx.status.success && !left_ctx.has_symbol() {
ui.horizontal(|ui| {
@@ -279,6 +280,24 @@ pub fn diff_view_ui(
})
});
}
// Only need to check the first Object. Technically the first could not have a flow analysis
// result while the second does but we don't want to waste space on two separate checkboxes.
if state.current_view == View::FunctionDiff
&& result
.first_obj
.as_ref()
.is_some_and(|(first, _)| first.has_flow_analysis_result())
{
let mut value = diff_config.show_data_flow;
if ui
.checkbox(&mut value, "Show data flow")
.on_hover_text("Show data flow analysis results in place of register names")
.clicked()
{
ret = Some(DiffViewAction::SetShowDataFlow(value));
}
}
} else if column == 1 {
// Right column
@@ -370,14 +389,14 @@ pub fn diff_view_ui(
let mut needs_separator = false;
if let Some(match_percent) = symbol_diff.match_percent {
let response = ui.label(
RichText::new(format!("{:.2}%", match_percent))
RichText::new(format!("{match_percent:.2}%"))
.font(appearance.code_font.clone())
.color(match_color_for_symbol(match_percent, appearance)),
);
if let Some((diff_score, max_score)) = symbol_diff.diff_score {
response.on_hover_ui_at_pointer(|ui| {
ui.label(
RichText::new(format!("Score: {}/{}", diff_score, max_score))
RichText::new(format!("Score: {diff_score}/{max_score}"))
.font(appearance.code_font.clone())
.color(appearance.text_color),
);
@@ -389,17 +408,16 @@ pub fn diff_view_ui(
if needs_separator {
ui.separator();
}
if ui
if (ui
.button("Change base")
.on_hover_text_at_pointer(
"Choose a different symbol to use as the base",
)
.clicked()
|| hotkeys::consume_change_base_shortcut(ui.ctx())
|| hotkeys::consume_change_base_shortcut(ui.ctx()))
&& let Some(symbol_ref) = state.symbol_state.left_symbol.as_ref()
{
if let Some(symbol_ref) = state.symbol_state.left_symbol.as_ref() {
ret = Some(DiffViewAction::SelectingRight(symbol_ref.clone()));
}
ret = Some(DiffViewAction::SelectingRight(symbol_ref.clone()));
}
}
} else if right_ctx.status.success && !right_ctx.has_symbol() {
@@ -497,6 +515,7 @@ pub fn diff_view_ui(
(state.current_view, left_ctx.obj, right_ctx.obj, left_ctx.section, right_ctx.section)
{
// Joint diff view
hotkeys::check_scroll_hotkeys(ui, true);
let left_total_bytes =
left_section_diff.data_diff.iter().fold(0usize, |accum, item| accum + item.len);
let right_total_bytes =
@@ -562,8 +581,8 @@ pub fn diff_view_ui(
) {
ret = Some(action);
}
} else if column == 1 {
if let Some(action) = diff_col_ui(
} else if column == 1
&& let Some(action) = diff_col_ui(
ui,
state,
appearance,
@@ -573,9 +592,9 @@ pub fn diff_view_ui(
available_width,
open_sections.1,
diff_config,
) {
ret = Some(action);
}
)
{
ret = Some(action);
}
});
}
@@ -600,9 +619,7 @@ fn symbol_label_ui(
.font(appearance.code_font.clone())
.color(appearance.highlight_color),
)
.selectable(false)
// TODO .show_tooltip_when_elided(false)
// https://github.com/emilk/egui/commit/071e090e2b2601e5ed4726a63a753188503dfaf2
.show_tooltip_when_elided(false)
.ui(ui)
.on_hover_ui_at_pointer(|ui| symbol_hover_ui(ui, ctx, symbol_idx, appearance))
.context_menu(|ui| {
@@ -779,10 +796,6 @@ fn missing_obj_ui(ui: &mut Ui, appearance: &Appearance) {
});
}
fn find_symbol(obj: &Object, selected_symbol: &SymbolRefByName) -> Option<usize> {
obj.symbols.iter().position(|symbol| symbol.name == selected_symbol.symbol_name)
}
fn find_section(obj: &Object, section_name: &str) -> Option<usize> {
obj.sections.iter().position(|section| section.name == section_name)
}
@@ -856,7 +869,7 @@ pub fn context_menu_items_ui(
}
if ui.button(job).clicked() {
ui.ctx().copy_text(value);
ui.close_menu();
ui.close();
}
}
ContextItem::Navigate { label, symbol_index, kind } => {
@@ -866,7 +879,7 @@ pub fn context_menu_items_ui(
symbol_index,
column,
)));
ui.close_menu();
ui.close();
}
}
ContextItem::Separator => {

View File

@@ -146,17 +146,17 @@ fn diff_text_ui(
let label_text = match segment.text {
DiffText::Basic(text) => text.to_string(),
DiffText::Line(num) => format!("{num} "),
DiffText::Address(addr) => format!("{:x}:", addr),
DiffText::Address(addr) => format!("{addr:x}:"),
DiffText::Opcode(mnemonic, _op) => format!("{mnemonic} "),
DiffText::Argument(arg) => match arg {
InstructionArgValue::Signed(v) => format!("{:#x}", ReallySigned(v)),
InstructionArgValue::Unsigned(v) => format!("{:#x}", v),
InstructionArgValue::Unsigned(v) => format!("{v:#x}"),
InstructionArgValue::Opaque(v) => v.into_owned(),
},
DiffText::BranchDest(addr) => format!("{addr:x}"),
DiffText::Symbol(sym) => sym.demangled_name.as_ref().unwrap_or(&sym.name).clone(),
DiffText::Addend(addend) => match addend.cmp(&0i64) {
Ordering::Greater => format!("+{:#x}", addend),
Ordering::Greater => format!("+{addend:#x}"),
Ordering::Less => format!("-{:#x}", -addend),
_ => String::new(),
},
@@ -174,6 +174,7 @@ fn diff_text_ui(
DiffTextColor::Normal => appearance.text_color,
DiffTextColor::Dim => appearance.deemphasized_text_color,
DiffTextColor::Bright => appearance.emphasized_text_color,
DiffTextColor::DataFlow => appearance.dataflow_color,
DiffTextColor::Replace => appearance.replace_color,
DiffTextColor::Delete => appearance.delete_color,
DiffTextColor::Insert => appearance.insert_color,

View File

@@ -26,7 +26,8 @@ pub enum GraphicsBackend {
Vulkan,
Metal,
Dx12,
OpenGL,
OpenGL, // glow
OpenGLES, // wgpu
}
static ALL_BACKENDS: &[GraphicsBackend] = &[
@@ -35,6 +36,7 @@ static ALL_BACKENDS: &[GraphicsBackend] = &[
GraphicsBackend::Metal,
GraphicsBackend::Dx12,
GraphicsBackend::OpenGL,
GraphicsBackend::OpenGLES,
];
#[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize)]
@@ -54,7 +56,7 @@ pub fn load_graphics_config(path: &Path) -> Result<Option<GraphicsConfig>> {
pub fn save_graphics_config(path: &Path, config: &GraphicsConfig) -> Result<()> {
let file = BufWriter::new(File::create(path)?);
ron::ser::to_writer(file, config)?;
ron::Options::default().to_io_writer(file, config)?;
Ok(())
}
@@ -67,7 +69,8 @@ impl GraphicsBackend {
}
GraphicsBackend::Metal => cfg!(all(feature = "wgpu", target_os = "macos")),
GraphicsBackend::Dx12 => cfg!(all(feature = "wgpu", target_os = "windows")),
GraphicsBackend::OpenGL => true,
GraphicsBackend::OpenGL => cfg!(feature = "glow"),
GraphicsBackend::OpenGLES => cfg!(all(feature = "wgpu", target_os = "windows")),
}
}
@@ -78,6 +81,7 @@ impl GraphicsBackend {
GraphicsBackend::Metal => "Metal",
GraphicsBackend::Dx12 => "DirectX 12",
GraphicsBackend::OpenGL => "OpenGL",
GraphicsBackend::OpenGLES => "OpenGL ES",
}
}
}
@@ -157,7 +161,7 @@ pub fn graphics_window(
state.should_relaunch = true;
}
Err(e) => {
log::error!("Failed to save graphics config: {:?}", e);
log::error!("Failed to save graphics config: {e:?}");
state.graphics_config.desired_backend = prev_backend;
}
}

View File

@@ -37,7 +37,7 @@ pub fn jobs_ui(ui: &mut egui::Ui, jobs: &mut JobQueue, appearance: &Appearance)
bar.ui(ui);
const STATUS_LENGTH: usize = 80;
if let Some(err) = &status.error {
let err_string = format!("{:#}", err);
let err_string = format!("{err:#}");
ui.colored_label(
appearance.delete_color,
if err_string.len() > STATUS_LENGTH - 10 {
@@ -119,7 +119,7 @@ pub fn jobs_menu_ui(ui: &mut egui::Ui, jobs: &mut JobQueue, appearance: &Appeara
}
Ordering::Greater => {
spinner.ui(ui);
clicked |= ui.link(format!("{} running", running_jobs)).clicked();
clicked |= ui.link(format!("{running_jobs} running")).clicked();
}
_ => (),
}
@@ -135,9 +135,7 @@ pub fn jobs_menu_ui(ui: &mut egui::Ui, jobs: &mut JobQueue, appearance: &Appeara
}
Ordering::Greater => {
clicked |= ui
.link(
RichText::new(format!("{} errors", error_jobs)).color(appearance.delete_color),
)
.link(RichText::new(format!("{error_jobs} errors")).color(appearance.delete_color))
.clicked();
}
_ => (),

View File

@@ -1,8 +1,8 @@
use std::mem::take;
use egui::{
CollapsingHeader, Color32, Id, OpenUrl, ScrollArea, SelectableLabel, Ui, Widget,
style::ScrollAnimation, text::LayoutJob,
CollapsingHeader, Color32, Id, OpenUrl, ScrollArea, Ui, Widget, style::ScrollAnimation,
text::LayoutJob,
};
use objdiff_core::{
diff::{
@@ -79,6 +79,8 @@ pub enum DiffViewAction {
SetMapping(usize, usize),
/// Set the show_mapped_symbols flag
SetShowMappedSymbols(bool),
/// Set the show_data_flow flag
SetShowDataFlow(bool),
}
#[derive(Debug, Clone, Default, Eq, PartialEq)]
@@ -209,19 +211,19 @@ impl DiffViewState {
let mut resolved_left = self.resolve_symbol(nav.left_symbol, 0);
let mut resolved_right = self.resolve_symbol(nav.right_symbol, 1);
if let Some(resolved_right) = &resolved_right {
if resolved_left.is_none() {
resolved_left = resolved_right
.target_symbol
.and_then(|idx| self.resolve_symbol(Some(idx), 0));
}
if let Some(resolved_right) = &resolved_right
&& resolved_left.is_none()
{
resolved_left = resolved_right
.target_symbol
.and_then(|idx| self.resolve_symbol(Some(idx), 0));
}
if let Some(resolved_left) = &resolved_left {
if resolved_right.is_none() {
resolved_right = resolved_left
.target_symbol
.and_then(|idx| self.resolve_symbol(Some(idx), 1));
}
if let Some(resolved_left) = &resolved_left
&& resolved_right.is_none()
{
resolved_right = resolved_left
.target_symbol
.and_then(|idx| self.resolve_symbol(Some(idx), 1));
}
let resolved_nav = resolve_navigation(nav.kind, resolved_left, resolved_right);
if (resolved_nav.left_symbol.is_some() && resolved_nav.right_symbol.is_some())
@@ -279,7 +281,7 @@ impl DiffViewState {
if let Some(source_path) =
state.config.selected_obj.as_ref().and_then(|obj| obj.source_path.as_ref())
{
log::info!("Opening file {}", source_path);
log::info!("Opening file {source_path}");
open::that_detached(source_path.as_str()).unwrap_or_else(|err| {
log::error!("Failed to open source file: {err}");
});
@@ -350,10 +352,20 @@ impl DiffViewState {
DiffViewAction::SetShowMappedSymbols(value) => {
self.symbol_state.show_mapped_symbols = value;
}
DiffViewAction::SetShowDataFlow(value) => {
let Ok(mut state) = state.write() else {
return;
};
state.config.diff_obj_config.show_data_flow = value;
}
}
}
fn resolve_symbol(&self, symbol_idx: Option<usize>, column: usize) -> Option<ResolvedSymbol> {
fn resolve_symbol(
&self,
symbol_idx: Option<usize>,
column: usize,
) -> Option<ResolvedSymbol<'_>> {
let symbol_idx = symbol_idx?;
let result = self.build.as_deref()?;
let (obj, diff) = match column {
@@ -488,16 +500,16 @@ pub fn symbol_context_menu_ui(
ret = Some(action);
}
if let Some(section) = section {
if ui.button("Map symbol").clicked() {
let symbol_ref = SymbolRefByName::new(symbol, Some(section));
if column == 0 {
ret = Some(DiffViewAction::SelectingRight(symbol_ref));
} else {
ret = Some(DiffViewAction::SelectingLeft(symbol_ref));
}
ui.close_menu();
if let Some(section) = section
&& ui.button("Map symbol").clicked()
{
let symbol_ref = SymbolRefByName::new(symbol, Some(section));
if column == 0 {
ret = Some(DiffViewAction::SelectingRight(symbol_ref));
} else {
ret = Some(DiffViewAction::SelectingLeft(symbol_ref));
}
ui.close();
}
});
ret
@@ -574,7 +586,7 @@ fn symbol_ui(
write_text(") ", appearance.text_color, &mut job, appearance.code_font.clone());
}
write_text(name, appearance.highlight_color, &mut job, appearance.code_font.clone());
let response = SelectableLabel::new(selected, job)
let response = egui::Button::selectable(selected, job)
.ui(ui)
.on_hover_ui_at_pointer(|ui| symbol_hover_ui(ui, ctx, symbol_idx, appearance));
response.context_menu(|ui| {
@@ -652,10 +664,10 @@ pub fn symbol_list_ui(
let mut ret = None;
ScrollArea::both().auto_shrink([false, false]).show(ui, |ui| {
let mut show_mapped_symbols = state.show_mapped_symbols;
if let SymbolFilter::Mapping(_, _) = filter {
if ui.checkbox(&mut show_mapped_symbols, "Show mapped symbols").changed() {
ret = Some(DiffViewAction::SetShowMappedSymbols(show_mapped_symbols));
}
if let SymbolFilter::Mapping(_, _) = filter
&& ui.checkbox(&mut show_mapped_symbols, "Show mapped symbols").changed()
{
ret = Some(DiffViewAction::SetShowMappedSymbols(show_mapped_symbols));
}
let section_display = display_sections(
ctx.obj,

View File

@@ -34,7 +34,7 @@ features = ["arm", "arm64", "mips", "ppc", "superh", "x86", "dwarf"]
talc = { version = "4.4", default-features = false, features = ["lock_api"] }
[target.'cfg(target_os = "wasi")'.dependencies]
wit-bindgen = { version = "0.42", default-features = false, features = ["macros"] }
wit-bindgen = { version = "0.43", default-features = false, features = ["macros"] }
[build-dependencies]
wit-deps = "0.5"

View File

@@ -1,12 +1,12 @@
{
"name": "objdiff-wasm",
"version": "3.0.0-beta.7",
"version": "3.0.0-beta.14",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "objdiff-wasm",
"version": "3.0.0-beta.7",
"version": "3.0.0-beta.14",
"license": "MIT OR Apache-2.0",
"devDependencies": {
"@biomejs/biome": "^1.9.3",
@@ -461,115 +461,6 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
"node_modules/@microsoft/api-extractor": {
"version": "7.50.0",
"resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.50.0.tgz",
"integrity": "sha512-Ds/PHTiVzuENQsmXrJKkSdfgNkr/SDG/2rDef0AWl3BchAnXdO7gXaYsAkNx4gWiC4OngNA3fQfd3+BcQxP1DQ==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"@microsoft/api-extractor-model": "7.30.3",
"@microsoft/tsdoc": "~0.15.1",
"@microsoft/tsdoc-config": "~0.17.1",
"@rushstack/node-core-library": "5.11.0",
"@rushstack/rig-package": "0.5.3",
"@rushstack/terminal": "0.15.0",
"@rushstack/ts-command-line": "4.23.5",
"lodash": "~4.17.15",
"minimatch": "~3.0.3",
"resolve": "~1.22.1",
"semver": "~7.5.4",
"source-map": "~0.6.1",
"typescript": "5.7.2"
},
"bin": {
"api-extractor": "bin/api-extractor"
}
},
"node_modules/@microsoft/api-extractor-model": {
"version": "7.30.3",
"resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.30.3.tgz",
"integrity": "sha512-yEAvq0F78MmStXdqz9TTT4PZ05Xu5R8nqgwI5xmUmQjWBQ9E6R2n8HB/iZMRciG4rf9iwI2mtuQwIzDXBvHn1w==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"@microsoft/tsdoc": "~0.15.1",
"@microsoft/tsdoc-config": "~0.17.1",
"@rushstack/node-core-library": "5.11.0"
}
},
"node_modules/@microsoft/api-extractor/node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"node_modules/@microsoft/api-extractor/node_modules/minimatch": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz",
"integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==",
"dev": true,
"license": "ISC",
"optional": true,
"peer": true,
"dependencies": {
"brace-expansion": "^1.1.7"
},
"engines": {
"node": "*"
}
},
"node_modules/@microsoft/api-extractor/node_modules/typescript": {
"version": "5.7.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz",
"integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==",
"dev": true,
"license": "Apache-2.0",
"optional": true,
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/@microsoft/tsdoc": {
"version": "0.15.1",
"resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.15.1.tgz",
"integrity": "sha512-4aErSrCR/On/e5G2hDP0wjooqDdauzEbIq8hIkIe5pXV0rtWJZvdCEKL0ykZxex+IxIwBp0eGeV48hQN07dXtw==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true
},
"node_modules/@microsoft/tsdoc-config": {
"version": "0.17.1",
"resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.17.1.tgz",
"integrity": "sha512-UtjIFe0C6oYgTnad4q1QP4qXwLhe6tIpNTRStJ2RZEPIkqQPREAwE5spzVxsdn9UaEMUqhh0AqSx3X4nWAKXWw==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"@microsoft/tsdoc": "0.15.1",
"ajv": "~8.12.0",
"jju": "~1.4.0",
"resolve": "~1.22.2"
}
},
"node_modules/@module-federation/error-codes": {
"version": "0.8.4",
"resolved": "https://registry.npmjs.org/@module-federation/error-codes/-/error-codes-0.8.4.tgz",
@@ -1186,101 +1077,6 @@
"node": ">=16.0.0"
}
},
"node_modules/@rushstack/node-core-library": {
"version": "5.11.0",
"resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-5.11.0.tgz",
"integrity": "sha512-I8+VzG9A0F3nH2rLpPd7hF8F7l5Xb7D+ldrWVZYegXM6CsKkvWc670RlgK3WX8/AseZfXA/vVrh0bpXe2Y2UDQ==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"ajv": "~8.13.0",
"ajv-draft-04": "~1.0.0",
"ajv-formats": "~3.0.1",
"fs-extra": "~11.3.0",
"import-lazy": "~4.0.0",
"jju": "~1.4.0",
"resolve": "~1.22.1",
"semver": "~7.5.4"
},
"peerDependencies": {
"@types/node": "*"
},
"peerDependenciesMeta": {
"@types/node": {
"optional": true
}
}
},
"node_modules/@rushstack/node-core-library/node_modules/ajv": {
"version": "8.13.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz",
"integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.3",
"json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2",
"uri-js": "^4.4.1"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/@rushstack/rig-package": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.5.3.tgz",
"integrity": "sha512-olzSSjYrvCNxUFZowevC3uz8gvKr3WTpHQ7BkpjtRpA3wK+T0ybep/SRUMfr195gBzJm5gaXw0ZMgjIyHqJUow==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"resolve": "~1.22.1",
"strip-json-comments": "~3.1.1"
}
},
"node_modules/@rushstack/terminal": {
"version": "0.15.0",
"resolved": "https://registry.npmjs.org/@rushstack/terminal/-/terminal-0.15.0.tgz",
"integrity": "sha512-vXQPRQ+vJJn4GVqxkwRe+UGgzNxdV8xuJZY2zem46Y0p3tlahucH9/hPmLGj2i9dQnUBFiRnoM9/KW7PYw8F4Q==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"@rushstack/node-core-library": "5.11.0",
"supports-color": "~8.1.1"
},
"peerDependencies": {
"@types/node": "*"
},
"peerDependenciesMeta": {
"@types/node": {
"optional": true
}
}
},
"node_modules/@rushstack/ts-command-line": {
"version": "4.23.5",
"resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.23.5.tgz",
"integrity": "sha512-jg70HfoK44KfSP3MTiL5rxsZH7X1ktX3cZs9Sl8eDu1/LxJSbPsh0MOFRC710lIuYYSgxWjI5AjbCBAl7u3RxA==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"@rushstack/terminal": "0.15.0",
"@types/argparse": "1.0.38",
"argparse": "~1.0.9",
"string-argv": "~0.3.1"
}
},
"node_modules/@swc/helpers": {
"version": "0.5.15",
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz",
@@ -1302,15 +1098,6 @@
"tslib": "^2.4.0"
}
},
"node_modules/@types/argparse": {
"version": "1.0.38",
"resolved": "https://registry.npmjs.org/@types/argparse/-/argparse-1.0.38.tgz",
"integrity": "sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true
},
"node_modules/acorn": {
"version": "8.14.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
@@ -1324,62 +1111,6 @@
"node": ">=0.4.0"
}
},
"node_modules/ajv": {
"version": "8.12.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
"integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2",
"uri-js": "^4.2.2"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/ajv-draft-04": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz",
"integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"peerDependencies": {
"ajv": "^8.5.0"
},
"peerDependenciesMeta": {
"ajv": {
"optional": true
}
}
},
"node_modules/ajv-formats": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz",
"integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"ajv": "^8.0.0"
},
"peerDependencies": {
"ajv": "^8.0.0"
},
"peerDependenciesMeta": {
"ajv": {
"optional": true
}
}
},
"node_modules/ansi-regex": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
@@ -1393,27 +1124,6 @@
"url": "https://github.com/chalk/ansi-regex?sponsor=1"
}
},
"node_modules/argparse": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"sprintf-js": "~1.0.2"
}
},
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true
},
"node_modules/base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
@@ -1620,15 +1330,6 @@
"node": ">=18"
}
},
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true
},
"node_modules/core-js": {
"version": "3.40.0",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.40.0.tgz",
@@ -1775,15 +1476,6 @@
"dev": true,
"license": "MIT"
},
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true
},
"node_modules/fd-slicer": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
@@ -1811,35 +1503,6 @@
"dev": true,
"license": "MIT"
},
"node_modules/fs-extra": {
"version": "11.3.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz",
"integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^2.0.0"
},
"engines": {
"node": ">=14.14"
}
},
"node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-east-asian-width": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz",
@@ -1874,33 +1537,6 @@
"dev": true,
"license": "ISC"
},
"node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"engines": {
"node": ">=8"
}
},
"node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
@@ -1922,18 +1558,6 @@
],
"license": "BSD-3-Clause"
},
"node_modules/import-lazy": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz",
"integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"engines": {
"node": ">=8"
}
},
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
@@ -1941,24 +1565,6 @@
"dev": true,
"license": "ISC"
},
"node_modules/is-core-module": {
"version": "2.16.1",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
"integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"hasown": "^2.0.2"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-interactive": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz",
@@ -2019,48 +1625,6 @@
"node": ">=14.17.6"
}
},
"node_modules/jju": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz",
"integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true
},
"node_modules/json-schema-traverse": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true
},
"node_modules/jsonfile": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"universalify": "^2.0.0"
},
"optionalDependencies": {
"graceful-fs": "^4.1.6"
}
},
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true
},
"node_modules/log-symbols": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz",
@@ -2091,21 +1655,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"dev": true,
"license": "ISC",
"optional": true,
"peer": true,
"dependencies": {
"yallist": "^4.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/magic-string": {
"version": "0.30.17",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz",
@@ -2228,15 +1777,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true
},
"node_modules/pend": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
@@ -2291,18 +1831,6 @@
"dev": true,
"license": "MIT"
},
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"engines": {
"node": ">=6"
}
},
"node_modules/readable-stream": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
@@ -2326,41 +1854,6 @@
"dev": true,
"license": "MIT"
},
"node_modules/require-from-string": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/resolve": {
"version": "1.22.10",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
"integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"is-core-module": "^2.16.0",
"path-parse": "^1.0.7",
"supports-preserve-symlinks-flag": "^1.0.0"
},
"bin": {
"resolve": "bin/resolve"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/restore-cursor": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz",
@@ -2448,24 +1941,6 @@
"dev": true,
"license": "MIT"
},
"node_modules/semver": {
"version": "7.5.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
"dev": true,
"license": "ISC",
"optional": true,
"peer": true,
"dependencies": {
"lru-cache": "^6.0.0"
},
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/signal-exit": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
@@ -2500,15 +1975,6 @@
"source-map": "^0.6.0"
}
},
"node_modules/sprintf-js": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
"integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
"dev": true,
"license": "BSD-3-Clause",
"optional": true,
"peer": true
},
"node_modules/stdin-discarder": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz",
@@ -2539,18 +2005,6 @@
"dev": true,
"license": "MIT"
},
"node_modules/string-argv": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz",
"integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"engines": {
"node": ">=0.6.19"
}
},
"node_modules/string-width": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
@@ -2595,54 +2049,6 @@
"is-natural-number": "^4.0.1"
}
},
"node_modules/strip-json-comments": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/supports-color": {
"version": "8.1.1",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
"integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"has-flag": "^4.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/supports-color?sponsor=1"
}
},
"node_modules/supports-preserve-symlinks-flag": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/tar-stream": {
"version": "1.6.2",
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz",
@@ -2779,30 +2185,6 @@
"through": "^2.3.8"
}
},
"node_modules/universalify": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"engines": {
"node": ">= 10.0.0"
}
},
"node_modules/uri-js": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
"dev": true,
"license": "BSD-2-Clause",
"optional": true,
"peer": true,
"dependencies": {
"punycode": "^2.1.0"
}
},
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@@ -2827,15 +2209,6 @@
"node": ">=0.4"
}
},
"node_modules/yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"dev": true,
"license": "ISC",
"optional": true,
"peer": true
},
"node_modules/yauzl": {
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",

View File

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

View File

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

View File

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