Compare commits

..

38 Commits

Author SHA1 Message Date
678210d58a Change file watcher log message to debug 2025-08-15 16:38:06 -06:00
4302821615 Fix Windows build 2025-08-15 16:31:33 -06:00
c4b4244b59 Version v3.0.0 2025-08-15 16:27:27 -06:00
52c138bf06 Add "ignore_patterns" option to config
This allows explicitly ignoring changes to certain
files or directories, even if the changed file ends
up matching `watch_patterns`. The default value,
`build/**/*` will ensure that changes in the build
directory will not trigger a duplicate rebuild.

Resolves #143
Resolves #215
2025-08-15 16:24:26 -06:00
813c8aa539 Add "Diff fill color" option to Appearance
Allows configuring the background color of
lines with a diff.

Resolves #230
2025-08-15 15:43:23 -06:00
0f0aaab795 Fix WSL path handling
Resolves #170
2025-08-15 15:34:58 -06:00
b21892be31 Add CLI args to objdiff-gui (incl. --project-dir/-p)
Resolves #41
Resolves #211
2025-08-15 15:25:55 -06:00
247d6da94b Restore extab diff view 2025-08-15 15:06:16 -06:00
bd95faa9c3 Remove objdiff-cli diff JSON output mode
This has been unimplemented since v3.0.0-alpha.1,
and I don't currently have plans to bring it back.
If you need it for something, please open an issue!
2025-08-15 14:57:34 -06:00
2c57e4960f Add ARM logic for inferred function size padding
Resolves #237
2025-08-15 14:48:26 -06:00
cff4be2979 Update gimli, object
Resolves #228
2025-08-15 14:47:06 -06:00
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
68 changed files with 7740 additions and 3537 deletions

1621
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.10"
version = "3.0.0"
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,7 +19,7 @@ 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, x86_64 (PC)
@@ -106,6 +106,9 @@ file as well. You can then add `objdiff.json` to your `.gitignore` to prevent it
"*.txt",
"*.json"
],
"ignore_patterns": [
"build/**/*"
],
"units": [
{
"name": "main/MetroTRK/mslsupp",
@@ -141,6 +144,10 @@ It's unlikely you'll want to disable this, unless you're using an external tool
If any of these files change, objdiff will automatically rebuild the objects and re-compare them.
If not specified, objdiff will use the default patterns listed above.
`ignore_patterns` _(optional)_: A list of glob patterns to explicitly ignore when watching for changes.
([Supported syntax](https://docs.rs/globset/latest/globset/#syntax))
If not specified, objdiff will use the default patterns listed above.
`units` _(optional)_: If specified, objdiff will display a list of objects in the sidebar for easy navigation.
> `name` _(optional)_: The name of the object in the UI. If not specified, the object's `path` will be used.

View File

@@ -74,6 +74,16 @@
"*.json"
]
},
"ignore_patterns": {
"type": "array",
"description": "List of glob patterns to explicitly ignore when watching for changes.\nFiles matching these patterns will not trigger a rebuild.\nSupported syntax: https://docs.rs/globset/latest/globset/#syntax",
"items": {
"type": "string"
},
"default": [
"build/**/*"
]
},
"objects": {
"type": "array",
"description": "Use units instead.",

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

@@ -19,7 +19,6 @@ use crossterm::{
},
};
use objdiff_core::{
bindings::diff::DiffResult,
build::{
BuildConfig, BuildStatus,
watcher::{Watcher, create_watcher},
@@ -28,7 +27,7 @@ use objdiff_core::{
ProjectConfig, ProjectObject, ProjectObjectMetadata, build_globset,
path::{check_path_buf, platform_path, platform_path_serde_option},
},
diff::{self, DiffObjConfig, MappingConfig, ObjectDiff},
diff::{DiffObjConfig, MappingConfig, ObjectDiff},
jobs::{
Job, JobQueue, JobResult,
objdiff::{ObjDiffConfig, start_build},
@@ -40,10 +39,7 @@ use typed_path::{Utf8PlatformPath, Utf8PlatformPathBuf};
use crate::{
cmd::apply_config_args,
util::{
output::{OutputFormat, write_output},
term::crossterm_panic_handler,
},
util::term::crossterm_panic_handler,
views::{EventControlFlow, EventResult, UiView, function_diff::FunctionDiffUi},
};
@@ -63,12 +59,6 @@ pub struct Args {
#[argp(option, short = 'u')]
/// Unit name within project
unit: Option<String>,
#[argp(option, short = 'o', from_str_fn(platform_path))]
/// Output file (one-shot mode) ("-" for stdout)
output: Option<Utf8PlatformPathBuf>,
#[argp(option)]
/// Output format (json, json-pretty, proto) (default: json)
format: Option<String>,
#[argp(positional)]
/// Function symbol to diff
symbol: Option<String>,
@@ -171,11 +161,7 @@ pub fn run(args: Args) -> Result<()> {
_ => bail!("Either target and base or project and unit must be specified"),
};
if let Some(output) = &args.output {
run_oneshot(&args, output, target_path.as_deref(), base_path.as_deref())
} else {
run_interactive(args, target_path, base_path, project_config)
}
run_interactive(args, target_path, base_path, project_config)
}
fn build_config_from_args(args: &Args) -> Result<(DiffObjConfig, MappingConfig)> {
@@ -194,32 +180,6 @@ fn build_config_from_args(args: &Args) -> Result<(DiffObjConfig, MappingConfig)>
Ok((diff_config, mapping_config))
}
fn run_oneshot(
args: &Args,
output: &Utf8PlatformPath,
target_path: Option<&Utf8PlatformPath>,
base_path: Option<&Utf8PlatformPath>,
) -> Result<()> {
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))
})
.transpose()?;
let base = base_path
.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)?;
let left = target.as_ref().and_then(|o| result.left.as_ref().map(|d| (o, d)));
let right = base.as_ref().and_then(|o| result.right.as_ref().map(|d| (o, d)));
write_output(&DiffResult::new(left, right), Some(output), output_format)?;
Ok(())
}
pub struct AppState {
pub jobs: JobQueue,
pub waker: Arc<TermWaker>,
@@ -382,10 +342,12 @@ fn run_interactive(
};
if let (Some(project_dir), Some(project_config)) = (&state.project_dir, &state.project_config) {
let watch_patterns = project_config.build_watch_patterns()?;
let ignore_patterns = project_config.build_ignore_patterns()?;
state.watcher = Some(create_watcher(
state.modified.clone(),
project_dir.as_ref(),
build_globset(&watch_patterns)?,
build_globset(&ignore_patterns)?,
Waker::from(state.waker.clone()),
)?);
}
@@ -399,7 +361,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 =
@@ -250,13 +248,12 @@ fn report_object(
{
continue;
}
if let Some(existing_functions) = &mut existing_functions {
if (symbol.flags.contains(SymbolFlag::Global)
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;
}
&& !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,
@@ -433,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 =
@@ -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,10 +560,12 @@ 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,

View File

@@ -62,7 +62,10 @@ config = [
"dep:semver",
"dep:typed-path",
]
dwarf = ["dep:gimli"]
dwarf = [
"dep:gimli",
"dep:typed-arena",
]
serde = [
"dep:pbjson",
"dep:pbjson-build",
@@ -78,6 +81,7 @@ std = [
"prost?/std",
"serde?/std",
"similar?/std",
"typed-arena?/std",
"typed-path?/std",
"dep:filetime",
"dep:memmap2",
@@ -92,7 +96,7 @@ ppc = [
"any-arch",
"dep:cwdemangle",
"dep:cwextab",
"dep:ppc750cl",
"dep:powerpc",
"dep:rlwinmdec",
]
x86 = [
@@ -123,17 +127,17 @@ 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 = { version = "0.37", 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" }
similar = { git = "https://github.com/encounter/similar.git", branch = "no_std", default-features = false, features = ["hashbrown"], optional = true }
typed-path = { version = "0.11", default-features = false, optional = true }
# config
@@ -142,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 = { git = "https://github.com/gimli-rs/gimli", rev = "7335f00e7c39fd501511584fefb0ba974117c950", 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 }
@@ -159,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
@@ -167,10 +172,10 @@ 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 = { version = "0.8.35", optional = true }
@@ -189,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

@@ -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

@@ -1,165 +0,0 @@
syntax = "proto3";
package objdiff.diff;
// A symbol
message Symbol {
// Name of the symbol
string name = 1;
// Demangled name of the symbol
optional string demangled_name = 2;
// Symbol address
uint64 address = 3;
// Symbol size
uint64 size = 4;
// Bitmask of SymbolFlag
uint32 flags = 5;
}
// Symbol visibility flags
enum SymbolFlag {
SYMBOL_NONE = 0;
SYMBOL_GLOBAL = 1;
SYMBOL_LOCAL = 2;
SYMBOL_WEAK = 4;
SYMBOL_COMMON = 8;
SYMBOL_HIDDEN = 16;
}
// A single parsed instruction
message Instruction {
// Instruction address
uint64 address = 1;
// Instruction size
uint32 size = 2;
// Instruction opcode
uint32 opcode = 3;
// Instruction mnemonic
string mnemonic = 4;
// Instruction formatted string
string formatted = 5;
// Original (unsimplified) instruction string
optional string original = 6;
// Instruction arguments
repeated Argument arguments = 7;
// Instruction relocation
optional Relocation relocation = 8;
// Instruction branch destination
optional uint64 branch_dest = 9;
// Instruction line number
optional uint32 line_number = 10;
}
// An instruction argument
message Argument {
oneof value {
// Plain text
string plain_text = 1;
// Value
ArgumentValue argument = 2;
// Relocation
ArgumentRelocation relocation = 3;
// Branch destination
uint64 branch_dest = 4;
}
}
// An instruction argument value
message ArgumentValue {
oneof value {
// Signed integer
int64 signed = 1;
// Unsigned integer
uint64 unsigned = 2;
// Opaque value
string opaque = 3;
}
}
// Marker type for relocation arguments
message ArgumentRelocation {
}
message Relocation {
uint32 type = 1;
string type_name = 2;
RelocationTarget target = 3;
}
message RelocationTarget {
uint32 symbol_index = 1;
int64 addend = 2;
}
message InstructionDiffRow {
DiffKind diff_kind = 1;
optional Instruction instruction = 2;
optional InstructionBranchFrom branch_from = 3;
optional InstructionBranchTo branch_to = 4;
repeated ArgumentDiff arg_diff = 5;
}
message ArgumentDiff {
optional uint32 diff_index = 1;
}
enum DiffKind {
DIFF_NONE = 0;
DIFF_REPLACE = 1;
DIFF_DELETE = 2;
DIFF_INSERT = 3;
DIFF_OP_MISMATCH = 4;
DIFF_ARG_MISMATCH = 5;
}
message InstructionBranchFrom {
repeated uint32 instruction_index = 1;
uint32 branch_index = 2;
}
message InstructionBranchTo {
uint32 instruction_index = 1;
uint32 branch_index = 2;
}
message SymbolDiff {
Symbol symbol = 1;
repeated InstructionDiffRow instruction_rows = 2;
optional float match_percent = 3;
// The symbol index in the _other_ object that this symbol was diffed against
optional uint32 target_symbol = 5;
}
message DataDiff {
DiffKind kind = 1;
bytes data = 2;
// May be larger than data
uint64 size = 3;
}
message SectionDiff {
string name = 1;
SectionKind kind = 2;
uint64 size = 3;
uint64 address = 4;
reserved 5;
repeated DataDiff data = 6;
optional float match_percent = 7;
}
enum SectionKind {
SECTION_UNKNOWN = 0;
SECTION_TEXT = 1;
SECTION_DATA = 2;
SECTION_BSS = 3;
}
message ObjectDiff {
repeated SectionDiff sections = 1;
repeated SymbolDiff symbols = 2;
}
message DiffResult {
optional ObjectDiff left = 1;
optional ObjectDiff right = 2;
}

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,
@@ -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> {
@@ -454,6 +464,22 @@ impl Arch for ArchArm {
}
flags
}
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))
}
}
#[derive(Clone, Copy, Debug)]
@@ -575,7 +601,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,12 @@
use alloc::{borrow::Cow, boxed::Box, format, string::String, vec::Vec};
use alloc::{
borrow::Cow,
boxed::Box,
format,
string::{String, ToString},
vec::Vec,
};
use core::{
any::Any,
ffi::CStr,
fmt::{self, Debug},
};
@@ -49,16 +56,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",
})
}
}
@@ -66,8 +73,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
}
@@ -100,7 +107,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));
@@ -108,7 +115,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));
@@ -116,7 +123,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));
@@ -124,7 +131,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));
@@ -151,16 +158,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())));
@@ -209,10 +216,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;
}
}
@@ -299,7 +306,7 @@ impl dyn Arch {
}
}
pub trait Arch: Send + Sync + Debug {
pub trait Arch: Any + Debug + Send + Sync {
/// Finishes arch-specific initialization that must be done after sections have been combined.
fn post_init(&mut self, _sections: &[Section], _symbols: &[Symbol]) {}
@@ -351,14 +358,15 @@ pub trait Arch: Send + Sync + Debug {
None
}
fn implcit_addend(
fn relocation_override(
&self,
file: &object::File<'_>,
section: &object::Section,
address: u64,
relocation: &object::Relocation,
flags: RelocationFlags,
) -> Result<i64>;
_file: &object::File<'_>,
_section: &object::Section,
_address: u64,
_relocation: &object::Relocation,
) -> Result<Option<RelocationOverride>> {
Ok(None)
}
fn demangle(&self, _name: &str) -> Option<String> { None }
@@ -399,13 +407,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")]
@@ -450,16 +469,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

@@ -11,7 +11,7 @@ use core::{
};
use itertools::Itertools;
use ppc750cl::Simm;
use powerpc::{Extensions, Simm};
use crate::{
arch::DataType,
@@ -19,8 +19,8 @@ use crate::{
util::{RawDouble, RawFloat},
};
fn is_store_instruction(op: ppc750cl::Opcode) -> bool {
use ppc750cl::Opcode;
fn is_store_instruction(op: powerpc::Opcode) -> bool {
use powerpc::Opcode;
matches!(
op,
Opcode::Stbux
@@ -52,8 +52,8 @@ fn is_store_instruction(op: ppc750cl::Opcode) -> bool {
)
}
pub fn guess_data_type_from_load_store_inst_op(inst_op: ppc750cl::Opcode) -> Option<DataType> {
use ppc750cl::Opcode;
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),
@@ -92,7 +92,7 @@ impl core::fmt::Display for RegisterContent {
// -i is safe because it's at most a 16 bit constant in the i32
{
if *i >= 0 {
write!(f, "0x{:x}", i)
write!(f, "0x{i:x}")
} else {
write!(f, "-0x{:x}", -i)
}
@@ -118,12 +118,12 @@ impl RegisterState {
// During a function call, these registers must be assumed trashed.
fn clear_volatile(&mut self) {
self[ppc750cl::GPR(0)] = RegisterContent::Unknown;
self[powerpc::GPR(0)] = RegisterContent::Unknown;
for i in 0..=13 {
self[ppc750cl::GPR(i)] = RegisterContent::Unknown;
self[powerpc::GPR(i)] = RegisterContent::Unknown;
}
for i in 0..=13 {
self[ppc750cl::FPR(i)] = RegisterContent::Unknown;
self[powerpc::FPR(i)] = RegisterContent::Unknown;
}
}
@@ -132,10 +132,10 @@ impl RegisterState {
// they get overwritten with another value before getting read.
fn set_potential_inputs(&mut self) {
for g_reg in 3..=13 {
self[ppc750cl::GPR(g_reg)] = RegisterContent::InputRegister(g_reg);
self[powerpc::GPR(g_reg)] = RegisterContent::InputRegister(g_reg);
}
for f_reg in 1..=13 {
self[ppc750cl::FPR(f_reg)] = RegisterContent::InputRegister(f_reg);
self[powerpc::FPR(f_reg)] = RegisterContent::InputRegister(f_reg);
}
}
@@ -172,34 +172,34 @@ impl RegisterState {
}
}
impl Index<ppc750cl::GPR> for RegisterState {
impl Index<powerpc::GPR> for RegisterState {
type Output = RegisterContent;
fn index(&self, gpr: ppc750cl::GPR) -> &Self::Output { &self.gpr[gpr.0 as usize] }
fn index(&self, gpr: powerpc::GPR) -> &Self::Output { &self.gpr[gpr.0 as usize] }
}
impl IndexMut<ppc750cl::GPR> for RegisterState {
fn index_mut(&mut self, gpr: ppc750cl::GPR) -> &mut Self::Output {
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<ppc750cl::FPR> for RegisterState {
impl Index<powerpc::FPR> for RegisterState {
type Output = RegisterContent;
fn index(&self, fpr: ppc750cl::FPR) -> &Self::Output { &self.fpr[fpr.0 as usize] }
fn index(&self, fpr: powerpc::FPR) -> &Self::Output { &self.fpr[fpr.0 as usize] }
}
impl IndexMut<ppc750cl::FPR> for RegisterState {
fn index_mut(&mut self, fpr: ppc750cl::FPR) -> &mut Self::Output {
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: &ppc750cl::Opcode,
args: &ppc750cl::Arguments,
op: &powerpc::Opcode,
args: &powerpc::Arguments,
) {
use ppc750cl::{Argument, GPR, Opcode};
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
@@ -270,11 +270,11 @@ fn execute_instruction(
}
}
fn get_branch_offset(args: &ppc750cl::Arguments) -> i32 {
fn get_branch_offset(args: &powerpc::Arguments) -> i32 {
for arg in args.iter() {
match arg {
ppc750cl::Argument::BranchDest(dest) => return dest.0 / 4,
ppc750cl::Argument::None => break,
powerpc::Argument::BranchDest(dest) => return dest.0 / 4,
powerpc::Argument::None => break,
_ => {}
}
}
@@ -316,7 +316,7 @@ fn clamp_text_length(s: String, max: usize) -> String {
fn get_register_content_from_reloc(
reloc: &Relocation,
obj: &Object,
op: ppc750cl::Opcode,
op: powerpc::Opcode,
) -> RegisterContent {
if let Some(bytes) = obj.symbol_data(reloc.target_symbol) {
match guess_data_type_from_load_store_inst_op(op) {
@@ -354,18 +354,18 @@ fn fill_registers_from_relocation(
reloc: &Relocation,
current_state: &mut RegisterState,
obj: &Object,
op: ppc750cl::Opcode,
args: &ppc750cl::Arguments,
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
(_, ppc750cl::Argument::GPR(gpr)) => {
(_, powerpc::Argument::GPR(gpr)) => {
current_state[gpr] = get_register_content_from_reloc(reloc, obj, op);
}
(_, ppc750cl::Argument::FPR(fpr)) => {
(_, powerpc::Argument::FPR(fpr)) => {
current_state[fpr] = get_register_content_from_reloc(reloc, obj, op);
}
_ => {}
@@ -384,11 +384,12 @@ pub fn ppc_data_flow_analysis(
func_symbol: &Symbol,
code: &[u8],
relocations: &[Relocation],
extensions: Extensions,
) -> Box<dyn FlowAnalysisResult> {
use alloc::collections::VecDeque;
use ppc750cl::InsIter;
let instructions = InsIter::new(code, func_symbol.address as u32)
use powerpc::InsIter;
let instructions = InsIter::new(code, func_symbol.address as u32, extensions)
.map(|(_addr, ins)| (ins.op, ins.basic().args))
.collect_vec();
@@ -449,7 +450,7 @@ pub fn ppc_data_flow_analysis(
// Only take a given (address, register state) combination once. If
// the known register state is different we have to take the branch
// again to stabilize the known values for backwards branches.
if op == &ppc750cl::Opcode::Bc {
if op == &powerpc::Opcode::Bc {
let branch_state = (index, current_state.clone());
if !taken_branches.contains(&branch_state) {
let offset = get_branch_offset(args);
@@ -468,7 +469,7 @@ pub fn ppc_data_flow_analysis(
}
// Update index
if op == &ppc750cl::Opcode::B {
if op == &powerpc::Opcode::B {
// Unconditional branch
let offset = get_branch_offset(args);
if offset > 0 {
@@ -502,18 +503,25 @@ pub fn ppc_data_flow_analysis(
}
// Store the relevant data flow values for simplified instructions
generate_flow_analysis_result(obj, func_address, code, register_state_at, relocations)
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) {
if sym.name.starts_with("@stringBase") && offset.0 != 0 {
if let Some(data) = obj.symbol_data(symbol_index) {
let bytes = &data[offset.0 as usize..];
if let Ok(Ok(str)) = CStr::from_bytes_until_nul(bytes).map(|x| x.to_str()) {
return Some(str);
}
}
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
@@ -530,14 +538,15 @@ fn generate_flow_analysis_result(
code: &[u8],
register_state_at: Vec<RegisterState>,
relocations: &[Relocation],
extensions: Extensions,
) -> Box<PPCFlowAnalysisResult> {
use ppc750cl::{Argument, InsIter};
use powerpc::{Argument, InsIter};
let mut analysis_result = PPCFlowAnalysisResult::new();
let default_register_state = RegisterState::new();
for (addr, ins) in InsIter::new(code, 0) {
for (addr, ins) in InsIter::new(code, 0, extensions) {
let ins_address = base_address + (addr as u64);
let index = addr / 4;
let ppc750cl::ParsedIns { mnemonic: _, args } = ins.simplified();
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);
@@ -546,7 +555,7 @@ fn generate_flow_analysis_result(
// they are being loaded.
// We need to do this before we break out on showing relocations in the
// subsequent if statement.
if let (ppc750cl::Opcode::Lfs | ppc750cl::Opcode::Lfd, Some(reloc)) = (ins.op, reloc) {
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,
@@ -566,21 +575,19 @@ fn generate_flow_analysis_result(
// Special case to show string constants on the line where they are
// being indexed to. This will typically be "addi t, stringbase, offset"
let registers = register_state_at.get(index as usize).unwrap_or(&default_register_state);
if let (ppc750cl::Opcode::Addi, Argument::GPR(rel), Argument::Simm(offset)) =
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)
{
if let RegisterContent::Symbol(sym_index) = registers[rel] {
if let Some(str) = get_string_data(obj, sym_index, offset) {
// Show the string constant in the analysis result
let formatted = format!("\"{str}\"");
analysis_result.set_argument_value_at_address(
ins_address,
2,
FlowAnalysisValue::Text(clamp_text_length(formatted, 20)),
);
// Don't continue, we want to show the stringbase value as well
}
}
// 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);
@@ -625,7 +632,7 @@ fn generate_flow_analysis_result(
Some(FlowAnalysisValue::Text(reg_name))
}
Some(RegisterContent::Unknown) | Some(RegisterContent::Variable) => None,
Some(value) => Some(FlowAnalysisValue::Text(format!("{value}"))),
Some(value) => Some(FlowAnalysisValue::Text(value.to_string())),
None => None,
};
if let Some(analysis_value) = analysis_value {

View File

@@ -6,13 +6,13 @@ use alloc::{
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,
@@ -20,65 +20,89 @@ use crate::{
},
obj::{
FlowAnalysisResult, InstructionRef, Object, Relocation, RelocationFlags,
ResolvedInstructionRef, ResolvedRelocation, Symbol, SymbolFlag, SymbolFlagSet,
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,
}
}
@@ -96,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),
});
}
@@ -131,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())),
@@ -168,7 +192,13 @@ 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 data_flow_analysis(
@@ -178,22 +208,115 @@ impl Arch for ArchPpc {
code: &[u8],
relocations: &[Relocation],
) -> Option<Box<dyn FlowAnalysisResult>> {
Some(flow_analysis::ppc_data_flow_analysis(obj, symbol, code, relocations))
Some(flow_analysis::ppc_data_flow_analysis(obj, symbol, code, relocations, self.extensions))
}
fn implcit_addend(
fn relocation_override(
&self,
_file: &object::File<'_>,
_section: &object::Section,
file: &object::File<'_>,
section: &object::Section,
address: u64,
_relocation: &object::Relocation,
flags: RelocationFlags,
) -> Result<i64> {
bail!("Unsupported PPC implicit relocation {:#x}:{:?}", address, flags)
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, name: &str) -> Option<String> {
cwdemangle::demangle(name, &cwdemangle::DemangleOptions::default())
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> {
@@ -208,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,
}
}
@@ -238,7 +370,7 @@ impl Arch for ArchPpc {
// Pooled string.
return Some(DataType::String);
}
let opcode = ppc750cl::Opcode::from(resolved.ins_ref.opcode as u8);
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);
@@ -325,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 {
@@ -336,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,
}
}
@@ -350,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(())
@@ -461,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);
}
@@ -517,19 +674,19 @@ fn make_symbol_ref(symbol: &object::Symbol) -> Result<ExtabSymbolRef> {
#[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 flow_analysis::guess_data_type_from_load_store_inst_op(opcode).is_some() {
match (args[1], args[2]) {
@@ -594,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);
}
@@ -635,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;
@@ -682,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 {
@@ -708,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.
@@ -837,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

@@ -9,9 +9,9 @@ use iced_x86::{
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)]
@@ -225,40 +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)
| RelocationFlags::Elf(elf::R_386_32 | elf::R_386_PC32) => {
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)
| RelocationFlags::Elf(elf::R_X86_64_32 | elf::R_X86_64_PC32) => {
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)
| RelocationFlags::Elf(elf::R_X86_64_64) => {
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> {
@@ -296,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

@@ -1,242 +0,0 @@
#![allow(clippy::needless_lifetimes)] // Generated serde code
use crate::{diff, obj};
// Protobuf diff types
include!(concat!(env!("OUT_DIR"), "/objdiff.diff.rs"));
#[cfg(feature = "serde")]
include!(concat!(env!("OUT_DIR"), "/objdiff.diff.serde.rs"));
impl DiffResult {
pub fn new(
_left: Option<(&obj::Object, &diff::ObjectDiff)>,
_right: Option<(&obj::Object, &diff::ObjectDiff)>,
) -> Self {
Self {
// TODO
// left: left.map(|(obj, diff)| ObjectDiff::new(obj, diff)),
// right: right.map(|(obj, diff)| ObjectDiff::new(obj, diff)),
left: None,
right: None,
}
}
}
// impl ObjectDiff {
// pub fn new(obj: &obj::Object, diff: &diff::ObjectDiff) -> Self {
// Self {
// sections: diff
// .sections
// .iter()
// .enumerate()
// .map(|(i, d)| SectionDiff::new(obj, i, d))
// .collect(),
// }
// }
// }
//
// impl SectionDiff {
// pub fn new(obj: &obj::Object, section_index: usize, section_diff: &diff::SectionDiff) -> Self {
// let section = &obj.sections[section_index];
// let symbols = section_diff.symbols.iter().map(|d| SymbolDiff::new(obj, d)).collect();
// let data = section_diff.data_diff.iter().map(|d| DataDiff::new(obj, d)).collect();
// // TODO: section_diff.reloc_diff
// Self {
// name: section.name.to_string(),
// kind: SectionKind::from(section.kind) as i32,
// size: section.size,
// address: section.address,
// symbols,
// data,
// match_percent: section_diff.match_percent,
// }
// }
// }
//
// impl From<obj::SectionKind> for SectionKind {
// fn from(value: obj::SectionKind) -> Self {
// match value {
// obj::SectionKind::Code => SectionKind::SectionText,
// obj::SectionKind::Data => SectionKind::SectionData,
// obj::SectionKind::Bss => SectionKind::SectionBss,
// // TODO common
// }
// }
// }
//
// impl SymbolDiff {
// pub fn new(object: &obj::Object, symbol_diff: &diff::SymbolDiff) -> Self {
// let symbol = object.symbols[symbol_diff.symbol_index];
// let instructions = symbol_diff
// .instruction_rows
// .iter()
// .map(|ins_diff| InstructionDiff::new(object, ins_diff))
// .collect();
// Self {
// symbol: Some(Symbol::new(symbol)),
// instructions,
// match_percent: symbol_diff.match_percent,
// target: symbol_diff.target_symbol.map(SymbolRef::from),
// }
// }
// }
//
// impl DataDiff {
// pub fn new(_object: &obj::Object, data_diff: &diff::DataDiff) -> Self {
// Self {
// kind: DiffKind::from(data_diff.kind) as i32,
// data: data_diff.data.clone(),
// size: data_diff.len as u64,
// }
// }
// }
//
// impl Symbol {
// pub fn new(value: &ObjSymbol) -> Self {
// Self {
// name: value.name.to_string(),
// demangled_name: value.demangled_name.clone(),
// address: value.address,
// size: value.size,
// flags: symbol_flags(value.flags),
// }
// }
// }
//
// fn symbol_flags(value: ObjSymbolFlagSet) -> u32 {
// let mut flags = 0u32;
// if value.0.contains(ObjSymbolFlags::Global) {
// flags |= SymbolFlag::SymbolGlobal as u32;
// }
// if value.0.contains(ObjSymbolFlags::Local) {
// flags |= SymbolFlag::SymbolLocal as u32;
// }
// if value.0.contains(ObjSymbolFlags::Weak) {
// flags |= SymbolFlag::SymbolWeak as u32;
// }
// if value.0.contains(ObjSymbolFlags::Common) {
// flags |= SymbolFlag::SymbolCommon as u32;
// }
// if value.0.contains(ObjSymbolFlags::Hidden) {
// flags |= SymbolFlag::SymbolHidden as u32;
// }
// flags
// }
//
// impl Instruction {
// pub fn new(object: &obj::Object, instruction: &ObjIns) -> Self {
// Self {
// address: instruction.address,
// size: instruction.size as u32,
// opcode: instruction.op as u32,
// mnemonic: instruction.mnemonic.to_string(),
// formatted: instruction.formatted.clone(),
// arguments: instruction.args.iter().map(Argument::new).collect(),
// relocation: instruction.reloc.as_ref().map(|reloc| Relocation::new(object, reloc)),
// branch_dest: instruction.branch_dest,
// line_number: instruction.line,
// original: instruction.orig.clone(),
// }
// }
// }
//
// impl Argument {
// pub fn new(value: &ObjInsArg) -> Self {
// Self {
// value: Some(match value {
// ObjInsArg::PlainText(s) => argument::Value::PlainText(s.to_string()),
// ObjInsArg::Arg(v) => argument::Value::Argument(ArgumentValue::new(v)),
// ObjInsArg::Reloc => argument::Value::Relocation(ArgumentRelocation {}),
// ObjInsArg::BranchDest(dest) => argument::Value::BranchDest(*dest),
// }),
// }
// }
// }
//
// impl ArgumentValue {
// pub fn new(value: &ObjInsArgValue) -> Self {
// Self {
// value: Some(match value {
// ObjInsArgValue::Signed(v) => argument_value::Value::Signed(*v),
// ObjInsArgValue::Unsigned(v) => argument_value::Value::Unsigned(*v),
// ObjInsArgValue::Opaque(v) => argument_value::Value::Opaque(v.to_string()),
// }),
// }
// }
// }
//
// impl Relocation {
// pub fn new(object: &obj::Object, reloc: &ObjReloc) -> Self {
// Self {
// r#type: match reloc.flags {
// object::RelocationFlags::Elf { r_type } => r_type,
// object::RelocationFlags::MachO { r_type, .. } => r_type as u32,
// object::RelocationFlags::Coff { typ } => typ as u32,
// object::RelocationFlags::Xcoff { r_rtype, .. } => r_rtype as u32,
// _ => unreachable!(),
// },
// type_name: object.arch.display_reloc(reloc.flags).into_owned(),
// target: Some(RelocationTarget {
// symbol: Some(Symbol::new(&reloc.target)),
// addend: reloc.addend,
// }),
// }
// }
// }
//
// impl InstructionDiff {
// pub fn new(object: &obj::Object, instruction_diff: &ObjInsDiff) -> Self {
// Self {
// instruction: instruction_diff.ins.as_ref().map(|ins| Instruction::new(object, ins)),
// diff_kind: DiffKind::from(instruction_diff.kind) as i32,
// branch_from: instruction_diff.branch_from.as_ref().map(InstructionBranchFrom::new),
// branch_to: instruction_diff.branch_to.as_ref().map(InstructionBranchTo::new),
// arg_diff: instruction_diff.arg_diff.iter().map(ArgumentDiff::new).collect(),
// }
// }
// }
//
// impl ArgumentDiff {
// pub fn new(value: &Option<ObjInsArgDiff>) -> Self {
// Self { diff_index: value.as_ref().map(|v| v.idx as u32) }
// }
// }
//
// impl From<ObjInsDiffKind> for DiffKind {
// fn from(value: ObjInsDiffKind) -> Self {
// match value {
// ObjInsDiffKind::None => DiffKind::DiffNone,
// ObjInsDiffKind::OpMismatch => DiffKind::DiffOpMismatch,
// ObjInsDiffKind::ArgMismatch => DiffKind::DiffArgMismatch,
// ObjInsDiffKind::Replace => DiffKind::DiffReplace,
// ObjInsDiffKind::Delete => DiffKind::DiffDelete,
// ObjInsDiffKind::Insert => DiffKind::DiffInsert,
// }
// }
// }
//
// impl From<ObjDataDiffKind> for DiffKind {
// fn from(value: ObjDataDiffKind) -> Self {
// match value {
// ObjDataDiffKind::None => DiffKind::DiffNone,
// ObjDataDiffKind::Replace => DiffKind::DiffReplace,
// ObjDataDiffKind::Delete => DiffKind::DiffDelete,
// ObjDataDiffKind::Insert => DiffKind::DiffInsert,
// }
// }
// }
//
// impl InstructionBranchFrom {
// pub fn new(value: &ObjInsBranchFrom) -> Self {
// Self {
// instruction_index: value.ins_idx.iter().map(|&x| x as u32).collect(),
// branch_index: value.branch_idx as u32,
// }
// }
// }
//
// impl InstructionBranchTo {
// pub fn new(value: &ObjInsBranchTo) -> Self {
// Self { instruction_index: value.ins_idx as u32, branch_index: value.branch_idx as u32 }
// }
// }

View File

@@ -1,3 +1 @@
#[cfg(feature = "any-arch")]
pub mod diff;
pub mod report;

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

@@ -49,6 +49,7 @@ pub fn run_make(config: &BuildConfig, arg: &Utf8UnixPath) -> BuildStatus {
};
#[cfg(windows)]
let mut command = {
use alloc::borrow::Cow;
use std::os::windows::process::CommandExt;
let mut command = if config.selected_wsl_distro.is_some() {
@@ -60,13 +61,17 @@ pub fn run_make(config: &BuildConfig, arg: &Utf8UnixPath) -> BuildStatus {
// Strip distro root prefix \\wsl.localhost\{distro}
let wsl_path_prefix = format!("\\\\wsl.localhost\\{}", distro);
let cwd = match cwd.strip_prefix(wsl_path_prefix) {
Ok(new_cwd) => Utf8UnixPath::new("/").join(new_cwd.with_unix_encoding()),
Err(_) => cwd.with_unix_encoding(),
// Convert to absolute Unix path
Ok(new_cwd) => Cow::Owned(
Utf8UnixPath::new("/").join(new_cwd.with_unix_encoding()).into_string(),
),
// Otherwise, use the Windows path as is
Err(_) => Cow::Borrowed(cwd.as_str()),
};
command
.arg("--cd")
.arg(cwd.as_str())
.arg::<&str>(cwd.as_ref())
.arg("-d")
.arg(distro)
.arg("--")

View File

@@ -29,6 +29,7 @@ pub fn create_watcher(
modified: Arc<AtomicBool>,
project_dir: &Path,
patterns: GlobSet,
ignore_patterns: GlobSet,
waker: Waker,
) -> notify::Result<Watcher> {
let base_dir = fs::canonicalize(project_dir)?;
@@ -54,8 +55,8 @@ pub fn create_watcher(
let Ok(path) = path.strip_prefix(&base_dir_clone) else {
continue;
};
if patterns.is_match(path) {
// log::info!("File modified: {}", path.display());
if patterns.is_match(path) && !ignore_patterns.is_match(path) {
log::debug!("File modified: {}", path.display());
any_match = true;
}
}

View File

@@ -36,6 +36,8 @@ pub struct ProjectConfig {
pub build_target: Option<bool>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub watch_patterns: Option<Vec<String>>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub ignore_patterns: Option<Vec<String>>,
#[cfg_attr(
feature = "serde",
serde(alias = "objects", skip_serializing_if = "Option::is_none")
@@ -66,7 +68,18 @@ impl ProjectConfig {
.map(|s| Glob::new(s))
.collect::<Result<Vec<Glob>, globset::Error>>()?
} else {
DEFAULT_WATCH_PATTERNS.iter().map(|s| Glob::new(s).unwrap()).collect()
default_watch_patterns()
})
}
pub fn build_ignore_patterns(&self) -> Result<Vec<Glob>, globset::Error> {
Ok(if let Some(ignore_patterns) = &self.ignore_patterns {
ignore_patterns
.iter()
.map(|s| Glob::new(s))
.collect::<Result<Vec<Glob>, globset::Error>>()?
} else {
default_ignore_patterns()
})
}
}
@@ -195,10 +208,16 @@ pub const DEFAULT_WATCH_PATTERNS: &[&str] = &[
"*.inc", "*.py", "*.yml", "*.txt", "*.json",
];
pub const DEFAULT_IGNORE_PATTERNS: &[&str] = &["build/**/*"];
pub fn default_watch_patterns() -> Vec<Glob> {
DEFAULT_WATCH_PATTERNS.iter().map(|s| Glob::new(s).unwrap()).collect()
}
pub fn default_ignore_patterns() -> Vec<Glob> {
DEFAULT_IGNORE_PATTERNS.iter().map(|s| Glob::new(s).unwrap()).collect()
}
#[cfg(feature = "std")]
#[derive(Clone, Eq, PartialEq)]
pub struct ProjectConfigInfo {

View File

@@ -127,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,
@@ -415,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

@@ -189,7 +189,7 @@ pub fn display_row(
let mut arg_idx = 0;
let mut displayed_relocation = false;
let analysis_result = if diff_config.show_data_flow {
obj.flow_analysis_results.get(&resolved.symbol.address)
obj.get_flow_analysis_result(resolved.symbol)
} else {
None
};
@@ -217,7 +217,7 @@ pub fn display_row(
}
let data_flow_value =
analysis_result.and_then(|result|
result.as_ref().get_argument_value_at_address(
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.
@@ -385,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
@@ -405,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(),
};
@@ -456,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),
});
}
@@ -526,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));
@@ -609,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,
});

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
}
}
}
@@ -467,15 +499,15 @@ 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) = left.symbol_by_name(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) = right.symbol_by_name(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
@@ -506,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;
}
@@ -643,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,7 +116,17 @@ 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)
@@ -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}"),
}
}
}
@@ -298,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)?;
@@ -328,6 +340,20 @@ impl Object {
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() }
}

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,7 +122,7 @@ 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))
}
@@ -130,7 +136,7 @@ fn is_local_label(symbol: &Symbol) -> bool {
&& LABEL_PREFIXES.iter().any(|p| symbol.name.starts_with(p))
}
fn infer_symbol_sizes(symbols: &mut [Symbol], sections: &[Section]) {
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() {
@@ -199,11 +205,14 @@ fn infer_symbol_sizes(symbols: &mut [Symbol], sections: &[Section]) {
}
iter_idx += 1;
};
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);
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)
};
if new_size > 0 {
let symbol = &mut symbols[symbol_idx];
symbol.size = new_size;
@@ -212,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,
@@ -220,6 +229,7 @@ fn infer_symbol_sizes(symbols: &mut [Symbol], sections: &[Section]) {
}
}
}
Ok(())
}
fn map_sections(
@@ -252,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 {
@@ -328,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()
.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 });
}
@@ -439,6 +515,7 @@ fn perform_data_flow_analysis(obj: &mut Object, config: &DiffObjConfig) -> Resul
}
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;
@@ -473,13 +550,17 @@ fn perform_data_flow_analysis(obj: &mut Object, config: &DiffObjConfig) -> Resul
}
// Optional full data flow analysis
if config.analyze_data_flow {
obj.arch.data_flow_analysis(obj, symbol, code, &section.relocations).and_then(
|flow_result| obj.flow_analysis_results.insert(symbol.address, flow_result),
);
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));
}
}
}
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);
}
@@ -496,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();
@@ -516,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(())
}

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(())

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

@@ -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.

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 {

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,

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

@@ -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,

View File

@@ -25,12 +25,13 @@ wsl = []
[dependencies]
anyhow = "1.0"
argp = "0.4"
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 +44,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 +65,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

@@ -16,8 +16,8 @@ use globset::Glob;
use objdiff_core::{
build::watcher::{Watcher, create_watcher},
config::{
DEFAULT_WATCH_PATTERNS, ProjectConfig, ProjectConfigInfo, ProjectObject, ScratchConfig,
build_globset, default_watch_patterns, path::platform_path_serde_option,
ProjectConfig, ProjectConfigInfo, ProjectObject, ScratchConfig, build_globset,
default_ignore_patterns, default_watch_patterns, path::platform_path_serde_option,
save_project_config,
},
diff::DiffObjConfig,
@@ -219,6 +219,8 @@ pub struct AppConfig {
#[serde(default = "default_watch_patterns")]
pub watch_patterns: Vec<Glob>,
#[serde(default)]
pub ignore_patterns: Vec<Glob>,
#[serde(default)]
pub recent_projects: Vec<String>,
#[serde(default)]
pub diff_obj_config: DiffObjConfig,
@@ -239,7 +241,8 @@ impl Default for AppConfig {
build_target: false,
rebuild_on_changes: true,
auto_update_check: true,
watch_patterns: DEFAULT_WATCH_PATTERNS.iter().map(|s| Glob::new(s).unwrap()).collect(),
watch_patterns: default_watch_patterns(),
ignore_patterns: default_ignore_patterns(),
recent_projects: vec![],
diff_obj_config: Default::default(),
}
@@ -431,6 +434,7 @@ impl App {
app_path: Option<PathBuf>,
graphics_config: GraphicsConfig,
graphics_config_path: Option<PathBuf>,
project_dir: Option<Utf8PlatformPathBuf>,
) -> Self {
// Load previous app state (if any).
// Note that you must enable the `persistence` feature for this to work.
@@ -440,18 +444,26 @@ impl App {
app.appearance = appearance;
}
if let Some(config) = deserialize_config(storage) {
let mut state = AppState { config, ..Default::default() };
if state.config.project_dir.is_some() {
state.config_change = true;
state.watcher_change = true;
}
if state.config.selected_obj.is_some() {
state.queue_build = true;
}
app.view_state.config_state.queue_check_update = state.config.auto_update_check;
let state = AppState { config, ..Default::default() };
app.state = Arc::new(RwLock::new(state));
}
}
{
let mut state = app.state.write().unwrap();
if let Some(project_dir) = project_dir
&& state.config.project_dir.as_ref().is_none_or(|p| *p != project_dir)
{
state.set_project_dir(project_dir);
}
if state.config.project_dir.is_some() {
state.config_change = true;
state.watcher_change = true;
}
if state.config.selected_obj.is_some() {
state.queue_build = true;
}
app.view_state.config_state.queue_check_update = state.config.auto_update_check;
}
app.appearance.init_fonts(&cc.egui_ctx);
app.appearance.utc_offset = utc_offset;
app.app_path = app_path;
@@ -461,11 +473,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 +486,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 +538,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 +552,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());
}
}
}
@@ -553,11 +563,17 @@ impl App {
if let Some(project_dir) = &state.config.project_dir {
match build_globset(&state.config.watch_patterns)
.map_err(anyhow::Error::new)
.and_then(|globset| {
.and_then(|patterns| {
build_globset(&state.config.ignore_patterns)
.map(|ignore_patterns| (patterns, ignore_patterns))
.map_err(anyhow::Error::new)
})
.and_then(|(patterns, ignore_patterns)| {
create_watcher(
self.modified.clone(),
project_dir.as_ref(),
globset,
patterns,
ignore_patterns,
egui_waker(ctx),
)
.map_err(anyhow::Error::new)
@@ -581,22 +597,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 +632,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 +678,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 +693,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 +711,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

@@ -0,0 +1,63 @@
// Originally from https://gist.github.com/suluke/e0c672492126be0a4f3b4f0e1115d77c
//! Extend `argp` to be better integrated with the `cargo` ecosystem
//!
//! For now, this only adds a --version/-V option which causes early-exit.
use std::ffi::OsStr;
use argp::{EarlyExit, FromArgs, TopLevelCommand, parser::ParseGlobalOptions};
struct ArgsOrVersion<T>(T)
where T: FromArgs;
impl<T> TopLevelCommand for ArgsOrVersion<T> where T: FromArgs {}
impl<T> FromArgs for ArgsOrVersion<T>
where T: FromArgs
{
fn _from_args(
command_name: &[&str],
args: &[&OsStr],
parent: Option<&mut dyn ParseGlobalOptions>,
) -> Result<Self, EarlyExit> {
/// Also use argp for catching `--version`-only invocations
#[derive(FromArgs)]
struct Version {
/// Print version information and exit.
#[argp(switch, short = 'V')]
pub version: bool,
}
match Version::from_args(command_name, args) {
Ok(v) => {
if v.version {
println!(
"{} {}",
command_name.first().unwrap_or(&""),
env!("CARGO_PKG_VERSION"),
);
std::process::exit(0);
} else {
// Pass through empty arguments
T::_from_args(command_name, args, parent).map(Self)
}
}
Err(exit) => match exit {
EarlyExit::Help(_help) => {
// TODO: Chain help info from Version
// For now, we just put the switch on T as well
T::from_args(command_name, &["--help"]).map(Self)
}
EarlyExit::Err(_) => T::_from_args(command_name, args, parent).map(Self),
},
}
}
}
/// Create a `FromArgs` type from the current processs `env::args`.
///
/// This function will exit early from the current process if argument parsing was unsuccessful or if information like `--help` was requested.
/// Error messages will be printed to stderr, and `--help` output to stdout.
pub fn from_env<T>() -> T
where T: TopLevelCommand {
argp::parse_args_or_exit::<ArgsOrVersion<T>>(argp::DEFAULT).0
}

View File

@@ -1,6 +1,6 @@
use anyhow::Result;
use globset::Glob;
use objdiff_core::config::{DEFAULT_WATCH_PATTERNS, try_project_config};
use objdiff_core::config::{default_ignore_patterns, default_watch_patterns, try_project_config};
use typed_path::{Utf8UnixComponent, Utf8UnixPath};
use crate::app::{AppState, ObjectConfig};
@@ -96,8 +96,15 @@ pub fn load_project_config(state: &mut AppState) -> Result<()> {
.map(|s| Glob::new(s))
.collect::<Result<Vec<Glob>, globset::Error>>()?;
} else {
state.config.watch_patterns =
DEFAULT_WATCH_PATTERNS.iter().map(|s| Glob::new(s).unwrap()).collect();
state.config.watch_patterns = default_watch_patterns();
}
if let Some(ignore_patterns) = &project_config.ignore_patterns {
state.config.ignore_patterns = ignore_patterns
.iter()
.map(|s| Glob::new(s))
.collect::<Result<Vec<Glob>, globset::Error>>()?;
} else {
state.config.ignore_patterns = default_ignore_patterns();
}
state.watcher_change = true;
state.objects = project_config

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

@@ -3,6 +3,7 @@
mod app;
mod app_config;
mod argp_version;
mod config;
mod fonts;
mod hotkeys;
@@ -11,19 +12,83 @@ mod update;
mod views;
use std::{
ffi::OsStr,
fmt::Display,
path::PathBuf,
process::ExitCode,
rc::Rc,
str::FromStr,
sync::{Arc, Mutex},
};
use anyhow::{Result, ensure};
use argp::{FromArgValue, FromArgs};
use cfg_if::cfg_if;
use objdiff_core::config::path::check_path_buf;
use time::UtcOffset;
use tracing_subscriber::EnvFilter;
use tracing_subscriber::{EnvFilter, filter::LevelFilter};
use typed_path::Utf8PlatformPathBuf;
use crate::views::graphics::{GraphicsBackend, GraphicsConfig, load_graphics_config};
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
enum LogLevel {
Error,
Warn,
Info,
Debug,
Trace,
}
impl FromStr for LogLevel {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"error" => Self::Error,
"warn" => Self::Warn,
"info" => Self::Info,
"debug" => Self::Debug,
"trace" => Self::Trace,
_ => return Err(()),
})
}
}
impl Display for LogLevel {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
LogLevel::Error => "error",
LogLevel::Warn => "warn",
LogLevel::Info => "info",
LogLevel::Debug => "debug",
LogLevel::Trace => "trace",
})
}
}
impl FromArgValue for LogLevel {
fn from_arg_value(value: &OsStr) -> Result<Self, String> {
String::from_arg_value(value)
.and_then(|s| Self::from_str(&s).map_err(|_| "Invalid log level".to_string()))
}
}
#[derive(FromArgs, PartialEq, Debug)]
/// A local diffing tool for decompilation projects.
struct TopLevel {
#[argp(option, short = 'L')]
/// Minimum logging level. (Default: info)
/// Possible values: error, warn, info, debug, trace
log_level: Option<LogLevel>,
#[argp(option, short = 'p')]
/// Path to the project directory.
project_dir: Option<PathBuf>,
/// Print version information and exit.
#[argp(switch, short = 'V')]
version: bool,
}
fn load_icon() -> Result<egui::IconData> {
let decoder = png::Decoder::new(include_bytes!("../assets/icon_64.png").as_ref());
let mut reader = decoder.read_info()?;
@@ -38,23 +103,63 @@ fn load_icon() -> Result<egui::IconData> {
const APP_NAME: &str = "objdiff";
fn main() -> ExitCode {
// Log to stdout (if you run with `RUST_LOG=debug`).
tracing_subscriber::fmt()
.with_env_filter(
EnvFilter::builder()
// Default to info level
.with_default_directive(tracing_subscriber::filter::LevelFilter::INFO.into())
.from_env_lossy()
// This module is noisy at info level
.add_directive("wgpu_core::device::resource=warn".parse().unwrap()),
)
.init();
let args: TopLevel = argp_version::from_env();
let builder = tracing_subscriber::fmt();
if let Some(level) = args.log_level {
builder
.with_max_level(match level {
LogLevel::Error => LevelFilter::ERROR,
LogLevel::Warn => LevelFilter::WARN,
LogLevel::Info => LevelFilter::INFO,
LogLevel::Debug => LevelFilter::DEBUG,
LogLevel::Trace => LevelFilter::TRACE,
})
.init();
} else {
builder
.with_env_filter(
EnvFilter::builder()
// Default to info level
.with_default_directive(LevelFilter::INFO.into())
.from_env_lossy()
// This module is noisy at info level
.add_directive("wgpu_core::device::resource=warn".parse().unwrap()),
)
.init();
}
// Because localtime_r is unsound in multithreaded apps,
// we must call this before initializing eframe.
// https://github.com/time-rs/time/issues/293
let utc_offset = UtcOffset::current_local_offset().unwrap_or(UtcOffset::UTC);
// Resolve project directory if provided
let project_dir = if let Some(path) = args.project_dir {
match path.canonicalize() {
Ok(path) => {
// Ensure the path is a directory
if path.is_dir() {
match check_path_buf(path) {
Ok(path) => Some(path),
Err(e) => {
log::error!("Failed to convert project directory to UTF-8 path: {}", e);
None
}
}
} else {
log::error!("Project directory is not a directory: {}", path.display());
None
}
}
Err(e) => {
log::error!("Failed to canonicalize project directory: {}", e);
None
}
}
} else {
None
};
let app_path = std::env::current_exe().ok();
let exec_path: Rc<Mutex<Option<PathBuf>>> = Rc::new(Mutex::new(None));
let mut native_options = eframe::NativeOptions {
@@ -95,7 +200,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)
}
@@ -112,6 +218,7 @@ fn main() -> ExitCode {
app_path.clone(),
graphics_config.clone(),
graphics_config_path.clone(),
project_dir.clone(),
) {
eframe_error = Some(e);
}
@@ -138,6 +245,7 @@ fn main() -> ExitCode {
app_path.clone(),
graphics_config.clone(),
graphics_config_path.clone(),
project_dir.clone(),
) {
eframe_error = Some(e);
} else {
@@ -160,6 +268,7 @@ fn main() -> ExitCode {
app_path,
graphics_config,
graphics_config_path,
project_dir,
) {
eframe_error = Some(e);
} else {
@@ -172,23 +281,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;
}
}
}
}
@@ -203,6 +312,7 @@ fn run_eframe(
app_path: Option<PathBuf>,
graphics_config: GraphicsConfig,
graphics_config_path: Option<PathBuf>,
project_dir: Option<Utf8PlatformPathBuf>,
) -> Result<(), eframe::Error> {
eframe::run_native(
APP_NAME,
@@ -215,6 +325,7 @@ fn run_eframe(
app_path,
graphics_config,
graphics_config_path,
project_dir,
)))
}),
)

View File

@@ -11,6 +11,7 @@ pub struct Appearance {
pub ui_font: FontId,
pub code_font: FontId,
pub diff_colors: Vec<Color32>,
pub diff_bg_color: Option<Color32>,
pub theme: egui::Theme,
// Applied by theme
@@ -67,6 +68,7 @@ impl Default for Appearance {
replace_color: Color32::LIGHT_BLUE,
insert_color: Color32::GREEN,
delete_color: Color32::from_rgb(200, 40, 41),
diff_bg_color: None,
utc_offset: UtcOffset::UTC,
fonts: FontState::default(),
next_ui_font: None,
@@ -103,6 +105,9 @@ impl Appearance {
match self.theme {
egui::Theme::Dark => {
style.visuals = egui::Visuals::dark();
if let Some(diff_bg_color) = self.diff_bg_color {
style.visuals.faint_bg_color = diff_bg_color;
}
self.text_color = Color32::GRAY;
self.emphasized_text_color = Color32::LIGHT_GRAY;
self.deemphasized_text_color = Color32::DARK_GRAY;
@@ -114,6 +119,9 @@ impl Appearance {
}
egui::Theme::Light => {
style.visuals = egui::Visuals::light();
if let Some(diff_bg_color) = self.diff_bg_color {
style.visuals.faint_bg_color = diff_bg_color;
}
self.text_color = Color32::GRAY;
self.emphasized_text_color = Color32::DARK_GRAY;
self.deemphasized_text_color = Color32::LIGHT_GRAY;
@@ -141,7 +149,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}")
}
}
}
@@ -155,7 +163,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}")
}
}
}
@@ -172,7 +180,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;
}
@@ -186,7 +194,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;
}
@@ -294,6 +302,21 @@ pub fn appearance_window(ctx: &egui::Context, show: &mut bool, appearance: &mut
appearance,
);
ui.separator();
ui.horizontal(|ui| {
ui.label("Diff fill color:");
let mut diff_bg_color =
appearance.diff_bg_color.unwrap_or_else(|| match appearance.theme {
egui::Theme::Dark => egui::Visuals::dark().faint_bg_color,
egui::Theme::Light => egui::Visuals::light().faint_bg_color,
});
if ui.color_edit_button_srgba(&mut diff_bg_color).changed() {
appearance.diff_bg_color = Some(diff_bg_color);
}
if ui.button("Reset").clicked() {
appearance.diff_bg_color = None;
}
});
ui.separator();
ui.label("Diff colors:");
if ui.button("Reset").clicked() {
appearance.diff_colors = DEFAULT_COLOR_ROTATION.to_vec();

View File

@@ -5,12 +5,12 @@ 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::{
config::{DEFAULT_WATCH_PATTERNS, path::check_path_buf},
config::{default_ignore_patterns, default_watch_patterns, path::check_path_buf},
diff::{
CONFIG_GROUPS, ConfigEnum, ConfigEnumVariantInfo, ConfigPropertyId, ConfigPropertyKind,
ConfigPropertyValue,
@@ -41,6 +41,7 @@ pub struct ConfigViewState {
pub build_running: bool,
pub queue_build: bool,
pub watch_pattern_text: String,
pub ignore_pattern_text: String,
pub object_search: String,
pub filter_diffable: bool,
pub filter_incomplete: bool,
@@ -185,16 +186,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 +278,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 +329,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 +355,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 +374,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 +508,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()
}
@@ -792,28 +791,57 @@ fn split_obj_config_ui(
state.watcher_change = true;
};
state.watcher_change |= patterns_ui(
ui,
"File patterns",
&mut state.config.watch_patterns,
&mut config_state.watch_pattern_text,
appearance,
state.project_config_info.is_some(),
default_watch_patterns,
);
state.watcher_change |= patterns_ui(
ui,
"Ignore patterns",
&mut state.config.ignore_patterns,
&mut config_state.ignore_pattern_text,
appearance,
state.project_config_info.is_some(),
default_ignore_patterns,
);
}
fn patterns_ui(
ui: &mut egui::Ui,
text: &str,
patterns: &mut Vec<Glob>,
pattern_text: &mut String,
appearance: &Appearance,
has_project_config: bool,
on_reset: impl FnOnce() -> Vec<Glob>,
) -> bool {
let mut change = false;
ui.horizontal(|ui| {
ui.label(RichText::new("File patterns").color(appearance.text_color));
ui.label(RichText::new(text).color(appearance.text_color));
if ui
.add_enabled(state.project_config_info.is_none(), egui::Button::new("Reset"))
.add_enabled(!has_project_config, egui::Button::new("Reset"))
.on_disabled_hover_text(CONFIG_DISABLED_TEXT)
.clicked()
{
state.config.watch_patterns =
DEFAULT_WATCH_PATTERNS.iter().map(|s| Glob::new(s).unwrap()).collect();
state.watcher_change = true;
*patterns = on_reset();
change = true;
}
});
let mut remove_at: Option<usize> = None;
for (idx, glob) in state.config.watch_patterns.iter().enumerate() {
for (idx, glob) in patterns.iter().enumerate() {
ui.horizontal(|ui| {
ui.label(
RichText::new(format!("{}", glob))
RichText::new(glob.to_string())
.color(appearance.text_color)
.family(FontFamily::Monospace),
);
if ui
.add_enabled(state.project_config_info.is_none(), egui::Button::new("-").small())
.add_enabled(!has_project_config, egui::Button::new("-").small())
.on_disabled_hover_text(CONFIG_DISABLED_TEXT)
.clicked()
{
@@ -822,27 +850,27 @@ fn split_obj_config_ui(
});
}
if let Some(idx) = remove_at {
state.config.watch_patterns.remove(idx);
state.watcher_change = true;
patterns.remove(idx);
change = true;
}
ui.horizontal(|ui| {
ui.add_enabled(
state.project_config_info.is_none(),
egui::TextEdit::singleline(&mut config_state.watch_pattern_text).desired_width(100.0),
!has_project_config,
egui::TextEdit::singleline(pattern_text).desired_width(100.0),
)
.on_disabled_hover_text(CONFIG_DISABLED_TEXT);
if ui
.add_enabled(state.project_config_info.is_none(), egui::Button::new("+").small())
.add_enabled(!has_project_config, egui::Button::new("+").small())
.on_disabled_hover_text(CONFIG_DISABLED_TEXT)
.clicked()
&& let Ok(glob) = Glob::new(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();
}
patterns.push(glob);
change = true;
pattern_text.clear();
}
});
change
}
pub fn arch_config_window(

View File

@@ -164,7 +164,7 @@ pub(crate) fn data_row_ui(
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

@@ -128,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()
@@ -142,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()
@@ -247,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| {
@@ -390,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),
);
@@ -409,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() {
@@ -583,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,
@@ -594,9 +592,9 @@ pub fn diff_view_ui(
available_width,
open_sections.1,
diff_config,
) {
ret = Some(action);
}
)
{
ret = Some(action);
}
});
}
@@ -621,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| {
@@ -873,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 } => {
@@ -883,7 +879,7 @@ pub fn context_menu_items_ui(
symbol_index,
column,
)));
ui.close_menu();
ui.close();
}
}
ContextItem::Separator => {

View File

@@ -1,8 +1,7 @@
use core::any::Any;
use egui::ScrollArea;
use objdiff_core::{
arch::ppc::ExceptionInfo,
obj::{Object, Symbol},
};
use objdiff_core::{arch::ppc::ExceptionInfo, obj::Object};
use crate::views::{appearance::Appearance, function_diff::FunctionDiffContext};
@@ -26,19 +25,19 @@ fn decode_extab(extab: &ExceptionInfo) -> String {
text
}
fn find_extab_entry<'a>(_obj: &'a Object, _symbol: &Symbol) -> Option<&'a ExceptionInfo> {
// TODO
// obj.arch.ppc().and_then(|ppc| ppc.extab_for_symbol(symbol))
None
fn find_extab_entry(obj: &Object, symbol_index: usize) -> Option<&ExceptionInfo> {
(obj.arch.as_ref() as &dyn Any)
.downcast_ref::<objdiff_core::arch::ppc::ArchPpc>()
.and_then(|ppc| ppc.extab_for_symbol(symbol_index))
}
fn extab_text_ui(
ui: &mut egui::Ui,
ctx: FunctionDiffContext<'_>,
symbol: &Symbol,
symbol_index: usize,
appearance: &Appearance,
) -> Option<()> {
if let Some(extab_entry) = find_extab_entry(ctx.obj, symbol) {
if let Some(extab_entry) = find_extab_entry(ctx.obj, symbol_index) {
let text = decode_extab(extab_entry);
ui.colored_label(appearance.replace_color, &text);
return Some(());
@@ -58,10 +57,8 @@ pub(crate) fn extab_ui(
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
if let Some(symbol) =
ctx.symbol_ref.and_then(|symbol_ref| ctx.obj.symbols.get(symbol_ref))
{
extab_text_ui(ui, ctx, symbol, appearance);
if let Some(symbol_index) = ctx.symbol_ref {
extab_text_ui(ui, ctx, symbol_index, appearance);
}
});
});

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(),
},

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::{
@@ -142,6 +142,11 @@ impl DiffViewState {
JobResult::ObjDiff(result) => {
self.build = take(result);
// Clear reload flag so that we don't reload the view immediately
if let Ok(mut state) = state.write() {
state.queue_reload = false;
}
// TODO: where should this go?
if let Some(result) = self.post_build_nav.take() {
self.current_view = result.view;
@@ -211,19 +216,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())
@@ -281,7 +286,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}");
});
@@ -361,7 +366,11 @@ impl DiffViewState {
}
}
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 {
@@ -496,16 +505,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
@@ -582,7 +591,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| {
@@ -660,10 +669,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.10",
"version": "3.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "objdiff-wasm",
"version": "3.0.0-beta.10",
"version": "3.0.0",
"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.10",
"version": "3.0.0",
"description": "A local diffing tool for decompilation projects.",
"author": {
"name": "Luke Street",