Compare commits

..

27 Commits

Author SHA1 Message Date
809e2ffddc cargo fmt 2025-03-11 21:43:32 -06:00
87fa29e8b0 objdiff-wasm: Fix symbol filtering
regex crate needed the `unicode-case` feature
2025-03-11 21:42:14 -06:00
42d4c38079 mips: Ignore .NON_MATCHING functions from INLINE_ASM macros 2025-03-11 21:39:17 -06:00
fa26200ed7 Silence warning 2025-03-10 22:22:05 -06:00
a0e7f9bc37 Fix x86 mov relocations with uint32 2025-03-10 22:09:58 -06:00
5898d7aebf Fix section ordering with many same-named sections
Before, this was comparing, for example, `.text-2`
with `.text-10` with standard string comparison,
yielding `.text-10` before `.text-2`.

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

Reimplements #140

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

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

* Improve readability of PPC pool relocs code

* Fix regression causing extern pool relocs to be ignored

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

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

* Add PPC "Calculate pooled data references" option

* Fix objdiff-wasm compilation errors

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

Reimplements #108

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

Reimplements #153

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

This reimplements #154

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

* Fix up some comments
2025-03-04 08:56:46 -07:00
9c31c82a37 cargo fmt 2025-03-03 21:08:36 -07:00
4f34dfa194 Don't infer sizes for labels within another symbol 2025-03-03 21:01:22 -07:00
ae6d37a10b Version v3.0.0-alpha.2 2025-03-03 18:25:38 -07:00
06e54f3456 cargo fmt 2025-03-03 17:16:35 -07:00
6ed07bfaf1 MIPS relocation pairing, '$' prefix option & fixes
Implements MIPS relocation pairing logic.
New option for "Register '$' prefix", off by default.
Fixes various regressions introduced by refactoring.

Resolves #122
Resolves #156
2025-03-03 17:14:32 -07:00
80e939653a Build objdiff-gui against older glibc
Resolves #165
2025-03-03 13:34:05 -07:00
87 changed files with 9813 additions and 917 deletions

View File

@@ -148,7 +148,7 @@ jobs:
python3 -m venv .venv python3 -m venv .venv
. .venv/bin/activate . .venv/bin/activate
echo PATH=$PATH >> $GITHUB_ENV echo PATH=$PATH >> $GITHUB_ENV
pip install ziglang==0.13.0 cargo-zigbuild==0.19.1 pip install ziglang==0.13.0.post1 cargo-zigbuild==0.19.8
- name: Setup Rust toolchain - name: Setup Rust toolchain
uses: dtolnay/rust-toolchain@stable uses: dtolnay/rust-toolchain@stable
with: with:
@@ -178,21 +178,26 @@ jobs:
matrix: matrix:
include: include:
- platform: ubuntu-latest - platform: ubuntu-latest
target: x86_64-unknown-linux-gnu target: x86_64-unknown-linux-gnu.2.31
target_base: x86_64-unknown-linux-gnu
name: linux-x86_64 name: linux-x86_64
packages: libgtk-3-dev packages: libgtk-3-dev
build: zigbuild
features: default features: default
- platform: windows-latest - platform: windows-latest
target: x86_64-pc-windows-msvc target: x86_64-pc-windows-msvc
name: windows-x86_64 name: windows-x86_64
build: build
features: default features: default
- platform: macos-latest - platform: macos-latest
target: x86_64-apple-darwin target: x86_64-apple-darwin
name: macos-x86_64 name: macos-x86_64
build: build
features: default features: default
- platform: macos-latest - platform: macos-latest
target: aarch64-apple-darwin target: aarch64-apple-darwin
name: macos-arm64 name: macos-arm64
build: build
features: default features: default
fail-fast: false fail-fast: false
runs-on: ${{ matrix.platform }} runs-on: ${{ matrix.platform }}
@@ -204,25 +209,32 @@ jobs:
sudo apt-get -y install ${{ matrix.packages }} sudo apt-get -y install ${{ matrix.packages }}
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Install cargo-zigbuild
if: matrix.build == 'zigbuild'
run: |
python3 -m venv .venv
. .venv/bin/activate
echo PATH=$PATH >> $GITHUB_ENV
pip install ziglang==0.13.0.post1 cargo-zigbuild==0.19.8
- name: Setup Rust toolchain - name: Setup Rust toolchain
uses: dtolnay/rust-toolchain@stable uses: dtolnay/rust-toolchain@stable
with: with:
targets: ${{ matrix.target }} targets: ${{ matrix.target_base || matrix.target }}
- name: Cache Rust workspace - name: Cache Rust workspace
uses: Swatinem/rust-cache@v2 uses: Swatinem/rust-cache@v2
with: with:
key: ${{ matrix.target }} key: ${{ matrix.target }}
- name: Cargo build - name: Cargo build
run: > run: >
cargo build --profile ${{ env.BUILD_PROFILE }} --target ${{ matrix.target }} cargo ${{ matrix.build }} --profile ${{ env.BUILD_PROFILE }} --target ${{ matrix.target }}
--bin ${{ env.CARGO_BIN_NAME }} --features ${{ matrix.features }} --bin ${{ env.CARGO_BIN_NAME }} --features ${{ matrix.features }}
- name: Upload artifacts - name: Upload artifacts
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: ${{ env.CARGO_BIN_NAME }}-${{ matrix.name }} name: ${{ env.CARGO_BIN_NAME }}-${{ matrix.name }}
path: | path: |
target/${{ matrix.target }}/${{ env.BUILD_PROFILE }}/${{ env.CARGO_BIN_NAME }} target/${{ matrix.target_base || matrix.target }}/${{ env.BUILD_PROFILE }}/${{ env.CARGO_BIN_NAME }}
target/${{ matrix.target }}/${{ env.BUILD_PROFILE }}/${{ env.CARGO_BIN_NAME }}.exe target/${{ matrix.target_base || matrix.target }}/${{ env.BUILD_PROFILE }}/${{ env.CARGO_BIN_NAME }}.exe
if-no-files-found: error if-no-files-found: error
build-wasm: build-wasm:

84
Cargo.lock generated
View File

@@ -1,6 +1,6 @@
# This file is automatically @generated by Cargo. # This file is automatically @generated by Cargo.
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 version = 4
[[package]] [[package]]
name = "ab_glyph" name = "ab_glyph"
@@ -450,6 +450,18 @@ version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
[[package]]
name = "auditable-serde"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c7bf8143dfc3c0258df908843e169b5cc5fcf76c7718bd66135ef4a9cd558c5"
dependencies = [
"semver",
"serde",
"serde_json",
"topological-sort",
]
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.4.0" version = "1.4.0"
@@ -3268,7 +3280,7 @@ dependencies = [
[[package]] [[package]]
name = "objdiff-cli" name = "objdiff-cli"
version = "3.0.0-alpha.1" version = "3.0.0-beta.3"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"argp", "argp",
@@ -3291,7 +3303,7 @@ dependencies = [
[[package]] [[package]]
name = "objdiff-core" name = "objdiff-core"
version = "3.0.0-alpha.1" version = "3.0.0-beta.3"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"arm-attr", "arm-attr",
@@ -3344,13 +3356,12 @@ dependencies = [
[[package]] [[package]]
name = "objdiff-gui" name = "objdiff-gui"
version = "3.0.0-alpha.1" version = "3.0.0-beta.3"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"cfg-if", "cfg-if",
"const_format", "const_format",
"cwdemangle", "cwdemangle",
"cwextab",
"dirs 6.0.0", "dirs 6.0.0",
"eframe", "eframe",
"egui", "egui",
@@ -3370,8 +3381,6 @@ dependencies = [
"rlwinmdec", "rlwinmdec",
"ron", "ron",
"serde", "serde",
"serde_json",
"shell-escape",
"tauri-winres", "tauri-winres",
"time", "time",
"tracing-subscriber", "tracing-subscriber",
@@ -3383,7 +3392,7 @@ dependencies = [
[[package]] [[package]]
name = "objdiff-wasm" name = "objdiff-wasm"
version = "3.0.0-alpha.1" version = "3.0.0-beta.3"
dependencies = [ dependencies = [
"log", "log",
"objdiff-core", "objdiff-core",
@@ -3897,7 +3906,7 @@ dependencies = [
[[package]] [[package]]
name = "rabbitizer" name = "rabbitizer"
version = "2.0.0-dev0" version = "2.0.0-dev0"
source = "git+https://github.com/Decompollaborate/rabbitizer.git?branch=🦀#706fd145b788ec3d068d55904dd112c7989e0412" source = "git+https://github.com/Decompollaborate/rabbitizer.git?branch=%F0%9F%A6%80#06dc8b6c826c3d60e112d4c2cd70aa54e308be12"
dependencies = [ dependencies = [
"bitflags 2.9.0", "bitflags 2.9.0",
] ]
@@ -4208,9 +4217,9 @@ dependencies = [
[[package]] [[package]]
name = "ring" name = "ring"
version = "0.17.11" version = "0.17.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da5349ae27d3887ca812fb375b45a4fbb36d8d12d2df394968cd86e35683fe73" checksum = "70ac5d832aa16abd7d1def883a8545280c20a60f523a370aa3a9617c2b8550ee"
dependencies = [ dependencies = [
"cc", "cc",
"cfg-if", "cfg-if",
@@ -4477,6 +4486,9 @@ name = "semver"
version = "1.0.25" version = "1.0.25"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03" checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "serde" name = "serde"
@@ -5168,6 +5180,12 @@ dependencies = [
"winnow 0.7.3", "winnow 0.7.3",
] ]
[[package]]
name = "topological-sort"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea68304e134ecd095ac6c3574494fc62b909f416c4fca77e440530221e549d3d"
[[package]] [[package]]
name = "tower" name = "tower"
version = "0.5.2" version = "0.5.2"
@@ -5540,9 +5558,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-encoder" name = "wasm-encoder"
version = "0.225.0" version = "0.227.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f7eac0445cac73bcf09e6a97f83248d64356dccf9f2b100199769b6b42464e5" checksum = "80bb72f02e7fbf07183443b27b0f3d4144abf8c114189f2e088ed95b696a7822"
dependencies = [ dependencies = [
"leb128fmt", "leb128fmt",
"wasmparser", "wasmparser",
@@ -5550,11 +5568,13 @@ dependencies = [
[[package]] [[package]]
name = "wasm-metadata" name = "wasm-metadata"
version = "0.225.0" version = "0.227.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1d20d0bf2c73c32a5114cf35a5c10ccf9f9aa37a3a2c0114b3e11cbf6faac12" checksum = "ce1ef0faabbbba6674e97a56bee857ccddf942785a336c8b47b42373c922a91d"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"auditable-serde",
"flate2",
"indexmap", "indexmap",
"serde", "serde",
"serde_derive", "serde_derive",
@@ -5580,9 +5600,9 @@ dependencies = [
[[package]] [[package]]
name = "wasmparser" name = "wasmparser"
version = "0.225.0" version = "0.227.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36e5456165f81e64cb9908a0fe9b9d852c2c74582aa3fe2be3c2da57f937d3ae" checksum = "0f51cad774fb3c9461ab9bccc9c62dfb7388397b5deda31bf40e8108ccd678b2"
dependencies = [ dependencies = [
"bitflags 2.9.0", "bitflags 2.9.0",
"hashbrown", "hashbrown",
@@ -6303,19 +6323,19 @@ dependencies = [
[[package]] [[package]]
name = "wit-bindgen" name = "wit-bindgen"
version = "0.39.0" version = "0.40.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4dd9a372b25d6f35456b0a730d2adabeb0c4878066ba8f8089800349be6ecb5" checksum = "8e7091ed6c9abfa4e0a2ef3b39d0539da992d841fcf32c255f64fb98764ffee5"
dependencies = [ dependencies = [
"wit-bindgen-rt 0.39.0", "wit-bindgen-rt 0.40.0",
"wit-bindgen-rust-macro", "wit-bindgen-rust-macro",
] ]
[[package]] [[package]]
name = "wit-bindgen-core" name = "wit-bindgen-core"
version = "0.39.0" version = "0.40.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f108fa9b77a346372858b30c11ea903680e7e2b9d820b1a5883e9d530bf51c7e" checksum = "398c650cec1278cfb72e214ba26ef3440ab726e66401bd39c04f465ee3979e6b"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"heck", "heck",
@@ -6333,18 +6353,18 @@ dependencies = [
[[package]] [[package]]
name = "wit-bindgen-rt" name = "wit-bindgen-rt"
version = "0.39.0" version = "0.40.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" checksum = "68faed92ae696b93ea9a7b67ba6c37bf09d72c6d9a70fa824a743c3020212f11"
dependencies = [ dependencies = [
"bitflags 2.9.0", "bitflags 2.9.0",
] ]
[[package]] [[package]]
name = "wit-bindgen-rust" name = "wit-bindgen-rust"
version = "0.39.0" version = "0.40.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5ba5b852e976d35dbf6cb745746bf1bd4fc26782bab1e0c615fc71a7d8aac05" checksum = "83903c8dcd8084a8a67ae08190122cf0e25dc37bdc239070a00f47e22d3f0aae"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"heck", "heck",
@@ -6358,9 +6378,9 @@ dependencies = [
[[package]] [[package]]
name = "wit-bindgen-rust-macro" name = "wit-bindgen-rust-macro"
version = "0.39.0" version = "0.40.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "401529c9af9304a20ed99fa01799e467b7d37727126f0c9a958895471268ad7a" checksum = "a7bf7f20495bcc7dc9f24c5fbcac9e919ca5ebdb7a1b1841d74447d3c8dd0c60"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"prettyplease", "prettyplease",
@@ -6373,9 +6393,9 @@ dependencies = [
[[package]] [[package]]
name = "wit-component" name = "wit-component"
version = "0.225.0" version = "0.227.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2505c917564c1d74774563bbcd3e4f8c216a6508050862fd5f449ee56e3c5125" checksum = "635c3adc595422cbf2341a17fb73a319669cc8d33deed3a48368a841df86b676"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bitflags 2.9.0", "bitflags 2.9.0",
@@ -6417,9 +6437,9 @@ dependencies = [
[[package]] [[package]]
name = "wit-parser" name = "wit-parser"
version = "0.225.0" version = "0.227.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebefaa234e47224f10ce60480c5bfdece7497d0f3b87a12b41ff39e5c8377a78" checksum = "ddf445ed5157046e4baf56f9138c124a0824d4d1657e7204d71886ad8ce2fc11"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"id-arena", "id-arena",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -187,6 +187,20 @@
} }
] ]
}, },
{
"id": "mips.registerPrefix",
"type": "boolean",
"default": false,
"name": "Register '$' prefix",
"description": "Display MIPS register names with a '$' prefix."
},
{
"id": "ppc.calculatePoolRelocations",
"type": "boolean",
"default": true,
"name": "Calculate pooled data references",
"description": "Display pooled data references in functions as fake relocations."
},
{ {
"id": "x86.formatter", "id": "x86.formatter",
"type": "choice", "type": "choice",
@@ -242,7 +256,15 @@
"name": "MIPS", "name": "MIPS",
"properties": [ "properties": [
"mips.abi", "mips.abi",
"mips.instrCategory" "mips.instrCategory",
"mips.registerPrefix"
]
},
{
"id": "ppc",
"name": "PowerPC",
"properties": [
"ppc.calculatePoolRelocations"
] ]
}, },
{ {

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,21 +1,25 @@
use alloc::{string::ToString, vec::Vec}; use alloc::{
collections::{BTreeMap, BTreeSet},
string::ToString,
vec::Vec,
};
use core::ops::Range; use core::ops::Range;
use anyhow::{bail, Result}; use anyhow::{Result, bail};
use object::{elf, Endian as _, Object as _, ObjectSection as _, ObjectSymbol as _}; use object::{Endian as _, Object as _, ObjectSection as _, ObjectSymbol as _, elf};
use rabbitizer::{ use rabbitizer::{
abi::Abi,
operands::{ValuedOperand, IU16},
registers_meta::Register,
IsaExtension, IsaVersion, Vram, IsaExtension, IsaVersion, Vram,
abi::Abi,
operands::{IU16, ValuedOperand},
registers_meta::Register,
}; };
use crate::{ use crate::{
arch::Arch, arch::Arch,
diff::{display::InstructionPart, DiffObjConfig, MipsAbi, MipsInstrCategory}, diff::{DiffObjConfig, MipsAbi, MipsInstrCategory, display::InstructionPart},
obj::{ obj::{
InstructionArg, InstructionArgValue, InstructionRef, Relocation, RelocationFlags, InstructionArg, InstructionArgValue, InstructionRef, Relocation, RelocationFlags,
ResolvedInstructionRef, ResolvedRelocation, ScannedInstruction, ResolvedInstructionRef, ResolvedRelocation, ScannedInstruction, SymbolFlag, SymbolFlagSet,
}, },
}; };
@@ -25,6 +29,8 @@ pub struct ArchMips {
pub abi: Abi, pub abi: Abi,
pub isa_extension: Option<IsaExtension>, pub isa_extension: Option<IsaExtension>,
pub ri_gp_value: i32, pub ri_gp_value: i32,
pub paired_relocations: Vec<BTreeMap<u64, i64>>,
pub ignored_symbols: BTreeSet<usize>,
} }
const EF_MIPS_ABI: u32 = 0x0000F000; const EF_MIPS_ABI: u32 = 0x0000F000;
@@ -64,16 +70,78 @@ impl ArchMips {
// Parse the ri_gp_value stored in .reginfo to be able to correctly // Parse the ri_gp_value stored in .reginfo to be able to correctly
// calculate R_MIPS_GPREL16 relocations later. The value is stored // calculate R_MIPS_GPREL16 relocations later. The value is stored
// 0x14 bytes into .reginfo (on 32 bit platforms) // 0x14 bytes into .reginfo (on 32-bit platforms)
let endianness = object.endianness();
let ri_gp_value = object let ri_gp_value = object
.section_by_name(".reginfo") .section_by_name(".reginfo")
.and_then(|section| section.data().ok()) .and_then(|section| section.data().ok())
.and_then(|data| data.get(0x14..0x18)) .and_then(|data| data.get(0x14..0x18))
.and_then(|s| s.try_into().ok()) .and_then(|s| s.try_into().ok())
.map(|bytes| object.endianness().read_i32_bytes(bytes)) .map(|bytes| endianness.read_i32_bytes(bytes))
.unwrap_or(0); .unwrap_or(0);
Ok(Self { endianness: object.endianness(), abi, isa_extension, ri_gp_value }) // Parse all relocations to pair R_MIPS_HI16 and R_MIPS_LO16. Since the instructions only
// have 16-bit immediate fields, the 32-bit addend is split across the two relocations.
// R_MIPS_LO16 relocations without an immediately preceding R_MIPS_HI16 use the last seen
// R_MIPS_HI16 addend.
// See https://refspecs.linuxfoundation.org/elf/mipsabi.pdf pages 4-17 and 4-18
let mut paired_relocations = Vec::with_capacity(object.sections().count() + 1);
for obj_section in object.sections() {
let data = obj_section.data()?;
let mut last_hi = None;
let mut last_hi_addend = 0;
let mut addends = BTreeMap::new();
for (addr, reloc) in obj_section.relocations() {
if !reloc.has_implicit_addend() {
continue;
}
match reloc.flags() {
object::RelocationFlags::Elf { r_type: elf::R_MIPS_HI16 } => {
let code = data[addr as usize..addr as usize + 4].try_into()?;
let addend = ((endianness.read_u32_bytes(code) & 0x0000FFFF) << 16) as i32;
last_hi = Some(addr);
last_hi_addend = addend;
}
object::RelocationFlags::Elf { r_type: elf::R_MIPS_LO16 } => {
let code = data[addr as usize..addr as usize + 4].try_into()?;
let addend = (endianness.read_u32_bytes(code) & 0x0000FFFF) as i16 as i32;
let full_addend = (last_hi_addend + addend) as i64;
if let Some(hi_addr) = last_hi.take() {
addends.insert(hi_addr, full_addend);
}
addends.insert(addr, full_addend);
}
_ => {
last_hi = None;
}
}
}
let section_index = obj_section.index().0;
if section_index >= paired_relocations.len() {
paired_relocations.resize_with(section_index + 1, BTreeMap::new);
}
paired_relocations[section_index] = addends;
}
let mut ignored_symbols = BTreeSet::new();
for obj_symbol in object.symbols() {
let Ok(name) = obj_symbol.name() else { continue };
if let Some(prefix) = name.strip_suffix(".NON_MATCHING") {
ignored_symbols.insert(obj_symbol.index().0);
if let Some(target_symbol) = object.symbol_by_name(prefix) {
ignored_symbols.insert(target_symbol.index().0);
}
}
}
Ok(Self {
endianness,
abi,
isa_extension,
ri_gp_value,
paired_relocations,
ignored_symbols,
})
} }
fn instruction_flags(&self, diff_config: &DiffObjConfig) -> rabbitizer::InstructionFlags { fn instruction_flags(&self, diff_config: &DiffObjConfig) -> rabbitizer::InstructionFlags {
@@ -99,9 +167,11 @@ impl ArchMips {
fn instruction_display_flags( fn instruction_display_flags(
&self, &self,
_diff_config: &DiffObjConfig, diff_config: &DiffObjConfig,
) -> rabbitizer::InstructionDisplayFlags { ) -> rabbitizer::InstructionDisplayFlags {
rabbitizer::InstructionDisplayFlags::default().with_unknown_instr_comment(false) rabbitizer::InstructionDisplayFlags::default()
.with_unknown_instr_comment(false)
.with_use_dollar(diff_config.mips_register_prefix)
} }
fn parse_ins_ref( fn parse_ins_ref(
@@ -127,18 +197,16 @@ impl Arch for ArchMips {
diff_config: &DiffObjConfig, diff_config: &DiffObjConfig,
) -> Result<Vec<ScannedInstruction>> { ) -> Result<Vec<ScannedInstruction>> {
let instruction_flags = self.instruction_flags(diff_config); let instruction_flags = self.instruction_flags(diff_config);
let start_address = address;
let mut ops = Vec::<ScannedInstruction>::with_capacity(code.len() / 4); let mut ops = Vec::<ScannedInstruction>::with_capacity(code.len() / 4);
let mut cur_addr = start_address as u32; let mut cur_addr = address as u32;
for chunk in code.chunks_exact(4) { for chunk in code.chunks_exact(4) {
let code = self.endianness.read_u32_bytes(chunk.try_into()?); let code = self.endianness.read_u32_bytes(chunk.try_into()?);
let vram = Vram::new(cur_addr); let instruction =
let instruction = rabbitizer::Instruction::new(code, vram, instruction_flags); rabbitizer::Instruction::new(code, Vram::new(cur_addr), instruction_flags);
let opcode = instruction.opcode() as u16; let opcode = instruction.opcode() as u16;
let branch_dest = let branch_dest = instruction.get_branch_vram_generic().map(|v| v.inner() as u64);
instruction.get_branch_offset_generic().map(|o| (vram + o).inner() as u64);
ops.push(ScannedInstruction { ops.push(ScannedInstruction {
ins_ref: InstructionRef { address, size: 4, opcode }, ins_ref: InstructionRef { address: cur_addr as u64, size: 4, opcode },
branch_dest, branch_dest,
}); });
cur_addr += 4; cur_addr += 4;
@@ -177,6 +245,17 @@ impl Arch for ArchMips {
reloc: &object::Relocation, reloc: &object::Relocation,
flags: RelocationFlags, flags: RelocationFlags,
) -> Result<i64> { ) -> 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);
}
}
let data = section.data()?; let data = section.data()?;
let code = data[address as usize..address as usize + 4].try_into()?; let code = data[address as usize..address as usize + 4].try_into()?;
let addend = self.endianness.read_u32_bytes(code); let addend = self.endianness.read_u32_bytes(code);
@@ -238,6 +317,14 @@ impl Arch for ArchMips {
_ => 1, _ => 1,
} }
} }
fn extra_symbol_flags(&self, symbol: &object::Symbol) -> SymbolFlagSet {
let mut flags = SymbolFlagSet::default();
if self.ignored_symbols.contains(&symbol.index().0) {
flags |= SymbolFlag::Ignored;
}
flags
}
} }
fn push_args( fn push_args(
@@ -305,9 +392,11 @@ fn push_args(
})))?; })))?;
} }
arg_cb(InstructionPart::basic("("))?; arg_cb(InstructionPart::basic("("))?;
arg_cb(InstructionPart::opaque( arg_cb(InstructionPart::opaque(base.either_name(
base.either_name(instruction.flags().abi(), display_flags.named_gpr()), instruction.flags().abi(),
))?; display_flags.named_gpr(),
!display_flags.use_dollar(),
)))?;
arg_cb(InstructionPart::basic(")"))?; arg_cb(InstructionPart::basic(")"))?;
} }
// ValuedOperand::r5900_immediate15(..) => match relocation { // ValuedOperand::r5900_immediate15(..) => match relocation {

View File

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

View File

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

View File

@@ -1,31 +1,50 @@
use alloc::{boxed::Box, string::String, vec::Vec}; use alloc::{boxed::Box, string::String, vec::Vec};
use anyhow::{anyhow, bail, Result}; use anyhow::{Result, anyhow, bail};
use iced_x86::{ use iced_x86::{
Decoder, DecoderOptions, DecoratorKind, FormatterOutput, FormatterTextKind, GasFormatter, Decoder, DecoderOptions, DecoratorKind, FormatterOutput, FormatterTextKind, GasFormatter,
Instruction, IntelFormatter, MasmFormatter, NasmFormatter, NumberKind, OpKind, Register, Instruction, IntelFormatter, MasmFormatter, NasmFormatter, NumberKind, OpKind, Register,
}; };
use object::{pe, Endian as _, Object as _, ObjectSection as _}; use object::{Endian as _, Object as _, ObjectSection as _, pe};
use crate::{ use crate::{
arch::Arch, arch::Arch,
diff::{display::InstructionPart, DiffObjConfig, X86Formatter}, diff::{DiffObjConfig, X86Formatter, display::InstructionPart},
obj::{InstructionRef, RelocationFlags, ResolvedInstructionRef, ScannedInstruction}, obj::{InstructionRef, RelocationFlags, ResolvedInstructionRef, ScannedInstruction},
}; };
#[derive(Debug)] #[derive(Debug)]
pub struct ArchX86 { pub struct ArchX86 {
bits: u32, arch: Architecture,
endianness: object::Endianness, endianness: object::Endianness,
} }
#[derive(Debug)]
enum Architecture {
X86,
X86_64,
}
impl ArchX86 { impl ArchX86 {
pub fn new(object: &object::File) -> Result<Self> { pub fn new(object: &object::File) -> Result<Self> {
Ok(Self { bits: if object.is_64() { 64 } else { 32 }, endianness: object.endianness() }) let arch = match object.architecture() {
object::Architecture::I386 => Architecture::X86,
object::Architecture::X86_64 => Architecture::X86_64,
_ => bail!("Unsupported architecture for ArchX86: {:?}", object.architecture()),
};
Ok(Self { arch, endianness: object.endianness() })
} }
fn decoder<'a>(&self, code: &'a [u8], address: u64) -> Decoder<'a> { fn decoder<'a>(&self, code: &'a [u8], address: u64) -> Decoder<'a> {
Decoder::with_ip(self.bits, code, address, DecoderOptions::NONE) Decoder::with_ip(
match self.arch {
Architecture::X86 => 32,
Architecture::X86_64 => 64,
},
code,
address,
DecoderOptions::NONE,
)
} }
fn formatter(&self, diff_config: &DiffObjConfig) -> Box<dyn iced_x86::Formatter> { fn formatter(&self, diff_config: &DiffObjConfig) -> Box<dyn iced_x86::Formatter> {
@@ -38,6 +57,27 @@ impl ArchX86 {
formatter.options_mut().set_space_after_operand_separator(diff_config.space_between_args); formatter.options_mut().set_space_after_operand_separator(diff_config.space_between_args);
formatter formatter
} }
fn reloc_size(&self, flags: RelocationFlags) -> Option<usize> {
match self.arch {
Architecture::X86 => match flags {
RelocationFlags::Coff(typ) => match typ {
pe::IMAGE_REL_I386_DIR16 | pe::IMAGE_REL_I386_REL16 => Some(2),
pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32 => Some(4),
_ => None,
},
_ => None,
},
Architecture::X86_64 => match flags {
RelocationFlags::Coff(typ) => match typ {
pe::IMAGE_REL_AMD64_ADDR32NB | pe::IMAGE_REL_AMD64_REL32 => Some(4),
pe::IMAGE_REL_AMD64_ADDR64 => Some(8),
_ => None,
},
_ => None,
},
}
}
} }
impl Arch for ArchX86 { impl Arch for ArchX86 {
@@ -88,17 +128,16 @@ impl Arch for ArchX86 {
// memory operand. // memory operand.
let mut reloc_replace = None; let mut reloc_replace = None;
if let Some(reloc) = resolved.relocation { if let Some(reloc) = resolved.relocation {
const PLACEHOLDER: u64 = 0x7BDE3E7D; // chosen by fair dice roll. const PLACEHOLDER: u64 = 0x7BDE3E7D; // chosen by fair dice roll. guaranteed to be random.
// guaranteed to be random.
let reloc_offset = reloc.relocation.address - resolved.ins_ref.address; let reloc_offset = reloc.relocation.address - resolved.ins_ref.address;
let reloc_size = reloc_size(reloc.relocation.flags).unwrap_or(usize::MAX); let reloc_size = self.reloc_size(reloc.relocation.flags).unwrap_or(usize::MAX);
let offsets = decoder.get_constant_offsets(&instruction); let offsets = decoder.get_constant_offsets(&instruction);
if reloc_offset == offsets.displacement_offset() as u64 if reloc_offset == offsets.displacement_offset() as u64
&& reloc_size == offsets.displacement_size() && reloc_size == offsets.displacement_size()
{ {
instruction.set_memory_displacement64(PLACEHOLDER); instruction.set_memory_displacement64(PLACEHOLDER);
// Formatter always writes the displacement as Int32 // Formatter always writes the displacement as Int32
reloc_replace = Some((OpKind::Memory, NumberKind::Int32, PLACEHOLDER)); reloc_replace = Some((OpKind::Memory, 4, PLACEHOLDER));
} else if reloc_offset == offsets.immediate_offset() as u64 } else if reloc_offset == offsets.immediate_offset() as u64
&& reloc_size == offsets.immediate_size() && reloc_size == offsets.immediate_size()
{ {
@@ -116,18 +155,12 @@ impl Arch for ArchX86 {
_ => OpKind::default(), _ => OpKind::default(),
} }
}; };
let number_kind = match reloc_size {
2 => NumberKind::UInt16,
4 => NumberKind::UInt32,
8 => NumberKind::UInt64,
_ => NumberKind::default(),
};
if is_branch { if is_branch {
instruction.set_near_branch64(PLACEHOLDER); instruction.set_near_branch64(PLACEHOLDER);
} else { } else {
instruction.set_immediate32(PLACEHOLDER as u32); instruction.set_immediate32(PLACEHOLDER as u32);
} }
reloc_replace = Some((op_kind, number_kind, PLACEHOLDER)); reloc_replace = Some((op_kind, reloc_size, PLACEHOLDER));
} }
} }
@@ -148,12 +181,28 @@ impl Arch for ArchX86 {
_relocation: &object::Relocation, _relocation: &object::Relocation,
flags: RelocationFlags, flags: RelocationFlags,
) -> Result<i64> { ) -> Result<i64> {
match flags { match self.arch {
Architecture::X86 => match flags {
RelocationFlags::Coff(pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32) => { RelocationFlags::Coff(pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32) => {
let data = section.data()?[address as usize..address as usize + 4].try_into()?; let data =
section.data()?[address as usize..address as usize + 4].try_into()?;
Ok(self.endianness.read_i32_bytes(data) as i64) Ok(self.endianness.read_i32_bytes(data) as i64)
} }
flags => bail!("Unsupported x86 implicit relocation {flags:?}"), flags => bail!("Unsupported x86 implicit relocation {flags:?}"),
},
Architecture::X86_64 => match flags {
RelocationFlags::Coff(pe::IMAGE_REL_AMD64_ADDR32NB | pe::IMAGE_REL_AMD64_REL32) => {
let data =
section.data()?[address as usize..address as usize + 4].try_into()?;
Ok(self.endianness.read_i32_bytes(data) as i64)
}
RelocationFlags::Coff(pe::IMAGE_REL_AMD64_ADDR64) => {
let data =
section.data()?[address as usize..address as usize + 8].try_into()?;
Ok(self.endianness.read_i64_bytes(data))
}
flags => bail!("Unsupported x86-64 implicit relocation {flags:?}"),
},
} }
} }
@@ -168,33 +217,35 @@ impl Arch for ArchX86 {
} }
fn reloc_name(&self, flags: RelocationFlags) -> Option<&'static str> { fn reloc_name(&self, flags: RelocationFlags) -> Option<&'static str> {
match flags { match self.arch {
Architecture::X86 => match flags {
RelocationFlags::Coff(typ) => match typ { RelocationFlags::Coff(typ) => match typ {
pe::IMAGE_REL_I386_DIR32 => Some("IMAGE_REL_I386_DIR32"), pe::IMAGE_REL_I386_DIR32 => Some("IMAGE_REL_I386_DIR32"),
pe::IMAGE_REL_I386_REL32 => Some("IMAGE_REL_I386_REL32"), pe::IMAGE_REL_I386_REL32 => Some("IMAGE_REL_I386_REL32"),
_ => None, _ => None,
}, },
_ => None, _ => None,
} },
} Architecture::X86_64 => match flags {
fn data_reloc_size(&self, flags: RelocationFlags) -> usize { reloc_size(flags).unwrap_or(1) }
}
fn reloc_size(flags: RelocationFlags) -> Option<usize> {
match flags {
RelocationFlags::Coff(typ) => match typ { RelocationFlags::Coff(typ) => match typ {
pe::IMAGE_REL_I386_DIR16 | pe::IMAGE_REL_I386_REL16 => Some(2), pe::IMAGE_REL_AMD64_ADDR64 => Some("IMAGE_REL_AMD64_ADDR64"),
pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32 => Some(4), pe::IMAGE_REL_AMD64_ADDR32NB => Some("IMAGE_REL_AMD64_ADDR32NB"),
pe::IMAGE_REL_AMD64_REL32 => Some("IMAGE_REL_AMD64_REL32"),
_ => None, _ => None,
}, },
_ => None, _ => None,
},
}
}
fn data_reloc_size(&self, flags: RelocationFlags) -> usize {
self.reloc_size(flags).unwrap_or(1)
} }
} }
struct InstructionFormatterOutput<'a> { struct InstructionFormatterOutput<'a> {
cb: &'a mut dyn FnMut(InstructionPart<'_>) -> Result<()>, cb: &'a mut dyn FnMut(InstructionPart<'_>) -> Result<()>,
reloc_replace: Option<(OpKind, NumberKind, u64)>, reloc_replace: Option<(OpKind, usize, u64)>,
error: Option<anyhow::Error>, error: Option<anyhow::Error>,
skip_next: bool, skip_next: bool,
} }
@@ -269,11 +320,18 @@ impl FormatterOutput for InstructionFormatterOutput<'_> {
return; return;
} }
if let (Some(operand), Some((target_op_kind, target_number_kind, target_value))) = if let (Some(operand), Some((target_op_kind, reloc_size, target_value))) =
(instruction_operand, self.reloc_replace) (instruction_operand, self.reloc_replace)
{ {
#[allow(clippy::match_like_matches_macro)]
if instruction.op_kind(operand) == target_op_kind if instruction.op_kind(operand) == target_op_kind
&& number_kind == target_number_kind && match (number_kind, reloc_size) {
(NumberKind::Int8 | NumberKind::UInt8, 1)
| (NumberKind::Int16 | NumberKind::UInt16, 2)
| (NumberKind::Int32 | NumberKind::UInt32, 4)
| (NumberKind::Int64 | NumberKind::UInt64, 8) => true,
_ => false,
}
&& value == target_value && value == target_value
{ {
if let Err(e) = (self.cb)(InstructionPart::reloc()) { if let Err(e) = (self.cb)(InstructionPart::reloc()) {
@@ -343,7 +401,7 @@ mod test {
#[test] #[test]
fn test_scan_instructions() { fn test_scan_instructions() {
let arch = ArchX86 { bits: 32, endianness: object::Endianness::Little }; let arch = ArchX86 { arch: Architecture::X86, endianness: object::Endianness::Little };
let code = [ let code = [
0xc7, 0x85, 0x68, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x8b, 0x04, 0x85, 0x00, 0xc7, 0x85, 0x68, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x8b, 0x04, 0x85, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -362,7 +420,7 @@ mod test {
#[test] #[test]
fn test_process_instruction() { fn test_process_instruction() {
let arch = ArchX86 { bits: 32, endianness: object::Endianness::Little }; let arch = ArchX86 { arch: Architecture::X86, endianness: object::Endianness::Little };
let code = [0xc7, 0x85, 0x68, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00]; let code = [0xc7, 0x85, 0x68, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00];
let opcode = iced_x86::Mnemonic::Mov as u16; let opcode = iced_x86::Mnemonic::Mov as u16;
let mut parts = Vec::new(); let mut parts = Vec::new();
@@ -398,7 +456,7 @@ mod test {
#[test] #[test]
fn test_process_instruction_with_reloc_1() { fn test_process_instruction_with_reloc_1() {
let arch = ArchX86 { bits: 32, endianness: object::Endianness::Little }; let arch = ArchX86 { arch: Architecture::X86, endianness: object::Endianness::Little };
let code = [0xc7, 0x85, 0x68, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00]; let code = [0xc7, 0x85, 0x68, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00];
let opcode = iced_x86::Mnemonic::Mov as u16; let opcode = iced_x86::Mnemonic::Mov as u16;
let mut parts = Vec::new(); let mut parts = Vec::new();
@@ -443,7 +501,7 @@ mod test {
#[test] #[test]
fn test_process_instruction_with_reloc_2() { fn test_process_instruction_with_reloc_2() {
let arch = ArchX86 { bits: 32, endianness: object::Endianness::Little }; let arch = ArchX86 { arch: Architecture::X86, endianness: object::Endianness::Little };
let code = [0x8b, 0x04, 0x85, 0x00, 0x00, 0x00, 0x00]; let code = [0x8b, 0x04, 0x85, 0x00, 0x00, 0x00, 0x00];
let opcode = iced_x86::Mnemonic::Mov as u16; let opcode = iced_x86::Mnemonic::Mov as u16;
let mut parts = Vec::new(); let mut parts = Vec::new();
@@ -486,7 +544,7 @@ mod test {
#[test] #[test]
fn test_process_instruction_with_reloc_3() { fn test_process_instruction_with_reloc_3() {
let arch = ArchX86 { bits: 32, endianness: object::Endianness::Little }; let arch = ArchX86 { arch: Architecture::X86, endianness: object::Endianness::Little };
let code = [0xe8, 0x00, 0x00, 0x00, 0x00]; let code = [0xe8, 0x00, 0x00, 0x00, 0x00];
let opcode = iced_x86::Mnemonic::Call as u16; let opcode = iced_x86::Mnemonic::Call as u16;
let mut parts = Vec::new(); let mut parts = Vec::new();
@@ -514,4 +572,43 @@ mod test {
.unwrap(); .unwrap();
assert_eq!(parts, &[InstructionPart::opcode("call", opcode), InstructionPart::reloc()]); assert_eq!(parts, &[InstructionPart::opcode("call", opcode), InstructionPart::reloc()]);
} }
#[test]
fn test_process_instruction_with_reloc_4() {
let arch = ArchX86 { arch: Architecture::X86, endianness: object::Endianness::Little };
let code = [0x8b, 0x15, 0xa4, 0x21, 0x7e, 0x00];
let opcode = iced_x86::Mnemonic::Mov as u16;
let mut parts = Vec::new();
arch.display_instruction(
ResolvedInstructionRef {
ins_ref: InstructionRef { address: 0x1234, size: 6, opcode },
code: &code,
relocation: Some(ResolvedRelocation {
relocation: &Relocation {
flags: RelocationFlags::Coff(pe::IMAGE_REL_I386_DIR32),
address: 0x1234 + 2,
target_symbol: 0,
addend: 0,
},
symbol: &Default::default(),
}),
..Default::default()
},
&DiffObjConfig::default(),
&mut |part| {
parts.push(part.into_static());
Ok(())
},
)
.unwrap();
assert_eq!(parts, &[
InstructionPart::opcode("mov", opcode),
InstructionPart::opaque("edx"),
InstructionPart::basic(","),
InstructionPart::basic(" "),
InstructionPart::basic("["),
InstructionPart::reloc(),
InstructionPart::basic("]"),
]);
}
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,14 +1,14 @@
use alloc::{vec, vec::Vec}; use alloc::{vec, vec::Vec};
use core::{cmp::Ordering, ops::Range}; use core::{cmp::Ordering, ops::Range};
use anyhow::{anyhow, Result}; use anyhow::{Result, anyhow};
use similar::{capture_diff_slices, get_diff_ratio, Algorithm}; use similar::{Algorithm, capture_diff_slices, get_diff_ratio};
use super::{ use super::{
code::{address_eq, section_name_eq},
DataDiff, DataDiffKind, DataRelocationDiff, ObjectDiff, SectionDiff, SymbolDiff, DataDiff, DataDiffKind, DataRelocationDiff, ObjectDiff, SectionDiff, SymbolDiff,
code::{address_eq, section_name_eq},
}; };
use crate::obj::{Object, Relocation, ResolvedRelocation, SymbolFlag, SymbolKind}; use crate::obj::{Object, Relocation, ResolvedRelocation, Symbol, SymbolFlag, SymbolKind};
pub fn diff_bss_symbol( pub fn diff_bss_symbol(
left_obj: &Object, left_obj: &Object,
@@ -63,19 +63,15 @@ fn reloc_eq(
} }
#[inline] #[inline]
fn resolve_relocation<'obj>( pub fn resolve_relocation<'obj>(
obj: &'obj Object, symbols: &'obj [Symbol],
reloc: &'obj Relocation, reloc: &'obj Relocation,
) -> ResolvedRelocation<'obj> { ) -> ResolvedRelocation<'obj> {
let symbol = &obj.symbols[reloc.target_symbol]; let symbol = &symbols[reloc.target_symbol];
ResolvedRelocation { relocation: reloc, symbol } ResolvedRelocation { relocation: reloc, symbol }
} }
/// Compares relocations contained with a certain data range. /// Compares relocations contained with a certain data range.
/// The DataDiffKind for each diff will either be `None`` (if the relocation matches),
/// or `Replace` (if a relocation was changed, added, or removed).
/// `Insert` and `Delete` are not used when a relocation is added or removed to avoid confusing diffs
/// where it looks like the bytes themselves were changed but actually only the relocations changed.
fn diff_data_relocs_for_range<'left, 'right>( fn diff_data_relocs_for_range<'left, 'right>(
left_obj: &'left Object, left_obj: &'left Object,
right_obj: &'right Object, right_obj: &'right Object,
@@ -92,7 +88,7 @@ fn diff_data_relocs_for_range<'left, 'right>(
continue; continue;
} }
let left_offset = left_reloc.address as usize - left_range.start; let left_offset = left_reloc.address as usize - left_range.start;
let left_reloc = resolve_relocation(left_obj, left_reloc); let left_reloc = resolve_relocation(&left_obj.symbols, left_reloc);
let Some(right_reloc) = right_section.relocations.iter().find(|r| { let Some(right_reloc) = right_section.relocations.iter().find(|r| {
if !right_range.contains(&(r.address as usize)) { if !right_range.contains(&(r.address as usize)) {
return false; return false;
@@ -103,7 +99,7 @@ fn diff_data_relocs_for_range<'left, 'right>(
diffs.push((DataDiffKind::Delete, Some(left_reloc), None)); diffs.push((DataDiffKind::Delete, Some(left_reloc), None));
continue; continue;
}; };
let right_reloc = resolve_relocation(right_obj, right_reloc); let right_reloc = resolve_relocation(&right_obj.symbols, right_reloc);
if reloc_eq(left_obj, right_obj, left_reloc, right_reloc) { if reloc_eq(left_obj, right_obj, left_reloc, right_reloc) {
diffs.push((DataDiffKind::None, Some(left_reloc), Some(right_reloc))); diffs.push((DataDiffKind::None, Some(left_reloc), Some(right_reloc)));
} else { } else {
@@ -115,7 +111,7 @@ fn diff_data_relocs_for_range<'left, 'right>(
continue; continue;
} }
let right_offset = right_reloc.address as usize - right_range.start; let right_offset = right_reloc.address as usize - right_range.start;
let right_reloc = resolve_relocation(right_obj, right_reloc); let right_reloc = resolve_relocation(&right_obj.symbols, right_reloc);
let Some(_) = left_section.relocations.iter().find(|r| { let Some(_) = left_section.relocations.iter().find(|r| {
if !left_range.contains(&(r.address as usize)) { if !left_range.contains(&(r.address as usize)) {
return false; return false;
@@ -143,27 +139,13 @@ pub fn diff_data_section(
) -> Result<(SectionDiff, SectionDiff)> { ) -> Result<(SectionDiff, SectionDiff)> {
let left_section = &left_obj.sections[left_section_idx]; let left_section = &left_obj.sections[left_section_idx];
let right_section = &right_obj.sections[right_section_idx]; let right_section = &right_obj.sections[right_section_idx];
let left_max = left_obj let left_max = symbols_matching_section(&left_obj.symbols, left_section_idx)
.symbols .filter_map(|(_, s)| s.address.checked_sub(left_section.address).map(|a| a + s.size))
.iter()
.filter_map(|s| {
if s.section != Some(left_section_idx) || s.kind == SymbolKind::Section {
return None;
}
s.address.checked_sub(left_section.address).map(|a| a + s.size)
})
.max() .max()
.unwrap_or(0) .unwrap_or(0)
.min(left_section.size); .min(left_section.size);
let right_max = right_obj let right_max = symbols_matching_section(&right_obj.symbols, right_section_idx)
.symbols .filter_map(|(_, s)| s.address.checked_sub(right_section.address).map(|a| a + s.size))
.iter()
.filter_map(|s| {
if s.section != Some(right_section_idx) || s.kind == SymbolKind::Section {
return None;
}
s.address.checked_sub(right_section.address).map(|a| a + s.size)
})
.max() .max()
.unwrap_or(0) .unwrap_or(0)
.min(right_section.size); .min(right_section.size);
@@ -254,13 +236,21 @@ pub fn diff_data_section(
let len = left_obj.arch.data_reloc_size(left_reloc.relocation.flags); let len = left_obj.arch.data_reloc_size(left_reloc.relocation.flags);
let range = left_reloc.relocation.address as usize let range = left_reloc.relocation.address as usize
..left_reloc.relocation.address as usize + len; ..left_reloc.relocation.address as usize + len;
left_reloc_diffs.push(DataRelocationDiff { kind: diff_kind, range }); left_reloc_diffs.push(DataRelocationDiff {
reloc: left_reloc.relocation.clone(),
kind: diff_kind,
range,
});
} }
if let Some(right_reloc) = right_reloc { if let Some(right_reloc) = right_reloc {
let len = right_obj.arch.data_reloc_size(right_reloc.relocation.flags); let len = right_obj.arch.data_reloc_size(right_reloc.relocation.flags);
let range = right_reloc.relocation.address as usize let range = right_reloc.relocation.address as usize
..right_reloc.relocation.address as usize + len; ..right_reloc.relocation.address as usize + len;
right_reloc_diffs.push(DataRelocationDiff { kind: diff_kind, range }); right_reloc_diffs.push(DataRelocationDiff {
reloc: right_reloc.relocation.clone(),
kind: diff_kind,
range,
});
} }
} }
@@ -408,30 +398,18 @@ pub fn diff_generic_section(
left_section_idx: usize, left_section_idx: usize,
_right_section_idx: usize, _right_section_idx: usize,
) -> Result<(SectionDiff, SectionDiff)> { ) -> Result<(SectionDiff, SectionDiff)> {
let match_percent = if left_obj let match_percent = if symbols_matching_section(&left_obj.symbols, left_section_idx)
.symbols
.iter()
.enumerate()
.filter(|(_, s)| s.section == Some(left_section_idx) && s.kind != SymbolKind::Section)
.map(|(i, _)| &left_diff.symbols[i]) .map(|(i, _)| &left_diff.symbols[i])
.all(|d| d.match_percent == Some(100.0)) .all(|d| d.match_percent == Some(100.0))
{ {
100.0 // Avoid fp precision issues 100.0 // Avoid fp precision issues
} else { } else {
let (matched, total) = left_obj let (matched, total) = symbols_matching_section(&left_obj.symbols, left_section_idx)
.symbols
.iter()
.enumerate()
.filter(|(_, s)| s.section == Some(left_section_idx) && s.kind != SymbolKind::Section)
.map(|(i, s)| (s, &left_diff.symbols[i])) .map(|(i, s)| (s, &left_diff.symbols[i]))
.fold((0.0, 0.0), |(matched, total), (s, d)| { .fold((0.0, 0.0), |(matched, total), (s, d)| {
(matched + d.match_percent.unwrap_or(0.0) * s.size as f32, total + s.size as f32) (matched + d.match_percent.unwrap_or(0.0) * s.size as f32, total + s.size as f32)
}); });
if total == 0.0 { if total == 0.0 { 100.0 } else { matched / total }
100.0
} else {
matched / total
}
}; };
Ok(( Ok((
SectionDiff { match_percent: Some(match_percent), data_diff: vec![], reloc_diff: vec![] }, SectionDiff { match_percent: Some(match_percent), data_diff: vec![], reloc_diff: vec![] },
@@ -449,19 +427,11 @@ pub fn diff_bss_section(
right_section_idx: usize, right_section_idx: usize,
) -> Result<(SectionDiff, SectionDiff)> { ) -> Result<(SectionDiff, SectionDiff)> {
let left_section = &left_obj.sections[left_section_idx]; let left_section = &left_obj.sections[left_section_idx];
let left_sizes = left_obj let left_sizes = symbols_matching_section(&left_obj.symbols, left_section_idx)
.symbols
.iter()
.enumerate()
.filter(|(_, s)| s.section == Some(left_section_idx) && s.kind != SymbolKind::Section)
.filter_map(|(_, s)| s.address.checked_sub(left_section.address).map(|a| (a, s.size))) .filter_map(|(_, s)| s.address.checked_sub(left_section.address).map(|a| (a, s.size)))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let right_section = &right_obj.sections[right_section_idx]; let right_section = &right_obj.sections[right_section_idx];
let right_sizes = right_obj let right_sizes = symbols_matching_section(&right_obj.symbols, right_section_idx)
.symbols
.iter()
.enumerate()
.filter(|(_, s)| s.section == Some(right_section_idx) && s.kind != SymbolKind::Section)
.filter_map(|(_, s)| s.address.checked_sub(right_section.address).map(|a| (a, s.size))) .filter_map(|(_, s)| s.address.checked_sub(right_section.address).map(|a| (a, s.size)))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let ops = capture_diff_slices(Algorithm::Patience, &left_sizes, &right_sizes); let ops = capture_diff_slices(Algorithm::Patience, &left_sizes, &right_sizes);
@@ -487,3 +457,15 @@ pub fn diff_bss_section(
SectionDiff { match_percent: Some(match_percent), data_diff: vec![], reloc_diff: vec![] }, SectionDiff { match_percent: Some(match_percent), data_diff: vec![], reloc_diff: vec![] },
)) ))
} }
fn symbols_matching_section(
symbols: &[Symbol],
section_idx: usize,
) -> impl Iterator<Item = (usize, &Symbol)> + '_ {
symbols.iter().enumerate().filter(move |(_, s)| {
s.section == Some(section_idx)
&& s.kind != SymbolKind::Section
&& s.size > 0
&& !s.flags.contains(SymbolFlag::Ignored)
})
}

View File

@@ -15,7 +15,7 @@ use crate::{
diff::{DiffObjConfig, InstructionDiffKind, InstructionDiffRow, ObjectDiff, SymbolDiff}, diff::{DiffObjConfig, InstructionDiffKind, InstructionDiffRow, ObjectDiff, SymbolDiff},
obj::{ obj::{
InstructionArg, InstructionArgValue, Object, ParsedInstruction, ResolvedInstructionRef, InstructionArg, InstructionArgValue, Object, ParsedInstruction, ResolvedInstructionRef,
SectionFlag, SectionKind, Symbol, SymbolFlag, SymbolKind, ResolvedRelocation, SectionFlag, SectionKind, Symbol, SymbolFlag, SymbolKind,
}, },
}; };
@@ -346,7 +346,7 @@ pub fn symbol_context(obj: &Object, symbol_index: usize) -> Vec<ContextItem> {
if symbol.section.is_some() { if symbol.section.is_some() {
if let Some(address) = symbol.virtual_address { if let Some(address) = symbol.virtual_address {
out.push(ContextItem::Copy { out.push(ContextItem::Copy {
value: format!("{:#x}", address), value: format!("{:x}", address),
label: Some("virtual address".to_string()), label: Some("virtual address".to_string()),
}); });
} }
@@ -409,7 +409,7 @@ pub fn symbol_hover(obj: &Object, symbol_index: usize, addend: i64) -> Vec<Hover
if let Some(address) = symbol.virtual_address { if let Some(address) = symbol.virtual_address {
out.push(HoverItem::Text { out.push(HoverItem::Text {
label: "Virtual address".into(), label: "Virtual address".into(),
value: format!("{:#x}", address), value: format!("{:x}", address),
color: HoverItemColor::Special, color: HoverItemColor::Special,
}); });
} }
@@ -424,6 +424,44 @@ pub fn symbol_hover(obj: &Object, symbol_index: usize, addend: i64) -> Vec<Hover
out out
} }
pub fn relocation_context(
obj: &Object,
reloc: ResolvedRelocation,
ins: Option<ResolvedInstructionRef>,
) -> Vec<ContextItem> {
let mut out = Vec::new();
out.append(&mut symbol_context(obj, reloc.relocation.target_symbol));
if let Some(ins) = ins {
let literals = display_ins_data_literals(obj, ins);
if !literals.is_empty() {
out.push(ContextItem::Separator);
for literal in literals {
out.push(ContextItem::Copy { value: literal, label: None });
}
}
}
out
}
pub fn relocation_hover(obj: &Object, reloc: ResolvedRelocation) -> Vec<HoverItem> {
let mut out = Vec::new();
if let Some(name) = obj.arch.reloc_name(reloc.relocation.flags) {
out.push(HoverItem::Text {
label: "Relocation".into(),
value: name.to_string(),
color: HoverItemColor::Normal,
});
} else {
out.push(HoverItem::Text {
label: "Relocation".into(),
value: format!("<{:?}>", reloc.relocation.flags),
color: HoverItemColor::Normal,
});
}
out.append(&mut symbol_hover(obj, reloc.relocation.target_symbol, reloc.relocation.addend));
out
}
pub fn instruction_context( pub fn instruction_context(
obj: &Object, obj: &Object,
resolved: ResolvedInstructionRef, resolved: ResolvedInstructionRef,
@@ -458,11 +496,8 @@ pub fn instruction_context(
} }
} }
if let Some(reloc) = resolved.relocation { if let Some(reloc) = resolved.relocation {
for literal in display_ins_data_literals(obj, resolved) {
out.push(ContextItem::Copy { value: literal, label: None });
}
out.push(ContextItem::Separator); out.push(ContextItem::Separator);
out.append(&mut symbol_context(obj, reloc.relocation.target_symbol)); out.append(&mut relocation_context(obj, reloc, Some(resolved)));
} }
out out
} }
@@ -483,7 +518,7 @@ pub fn instruction_hover(
let offset = resolved.ins_ref.address - resolved.symbol.address; let offset = resolved.ins_ref.address - resolved.symbol.address;
out.push(HoverItem::Text { out.push(HoverItem::Text {
label: "Virtual address".into(), label: "Virtual address".into(),
value: format!("{:#x}", virtual_address + offset), value: format!("{:x}", virtual_address + offset),
color: HoverItemColor::Special, color: HoverItemColor::Special,
}); });
} }
@@ -509,26 +544,26 @@ pub fn instruction_hover(
} }
} }
if let Some(reloc) = resolved.relocation { if let Some(reloc) = resolved.relocation {
if let Some(name) = obj.arch.reloc_name(reloc.relocation.flags) { out.push(HoverItem::Separator);
out.append(&mut relocation_hover(obj, reloc));
if let Some(ty) = obj.arch.guess_data_type(resolved) {
let literals = display_ins_data_literals(obj, resolved);
if !literals.is_empty() {
out.push(HoverItem::Separator);
for literal in literals {
out.push(HoverItem::Text { out.push(HoverItem::Text {
label: "Relocation type".into(), label: format!("{}", ty),
value: name.to_string(), value: literal,
color: HoverItemColor::Normal,
});
} else {
out.push(HoverItem::Text {
label: "Relocation type".into(),
value: format!("<{:?}>", reloc.relocation.flags),
color: HoverItemColor::Normal, color: HoverItemColor::Normal,
}); });
} }
out.push(HoverItem::Separator); }
out.append(&mut symbol_hover(obj, reloc.relocation.target_symbol, reloc.relocation.addend)); }
} }
out out
} }
#[derive(Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub enum SymbolFilter<'a> { pub enum SymbolFilter<'a> {
None, None,
Search(&'a Regex), Search(&'a Regex),
@@ -545,7 +580,11 @@ fn symbol_matches_filter(
if symbol.section.is_none() && !symbol.flags.contains(SymbolFlag::Common) { if symbol.section.is_none() && !symbol.flags.contains(SymbolFlag::Common) {
return false; return false;
} }
if !show_hidden_symbols && (symbol.size == 0 || symbol.flags.contains(SymbolFlag::Hidden)) { if !show_hidden_symbols
&& (symbol.size == 0
|| symbol.flags.contains(SymbolFlag::Hidden)
|| symbol.flags.contains(SymbolFlag::Ignored))
{
return false; return false;
} }
match filter { match filter {
@@ -666,7 +705,7 @@ pub fn display_sections(
}); });
} }
} }
sections.sort_by(|a, b| a.id.cmp(&b.id)); sections.sort_by(|a, b| a.name.cmp(&b.name));
sections sections
} }

View File

@@ -16,7 +16,7 @@ use crate::{
diff_generic_section, diff_generic_section,
}, },
}, },
obj::{InstructionRef, Object, SectionKind, Symbol, SymbolFlag}, obj::{InstructionRef, Object, Relocation, SectionKind, Symbol, SymbolFlag},
}; };
pub mod code; pub mod code;
@@ -26,13 +26,7 @@ pub mod display;
include!(concat!(env!("OUT_DIR"), "/config.gen.rs")); include!(concat!(env!("OUT_DIR"), "/config.gen.rs"));
impl DiffObjConfig { impl DiffObjConfig {
pub fn separator(&self) -> &'static str { pub fn separator(&self) -> &'static str { if self.space_between_args { ", " } else { "," } }
if self.space_between_args {
", "
} else {
","
}
}
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -93,6 +87,7 @@ pub struct DataDiff {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct DataRelocationDiff { pub struct DataRelocationDiff {
pub reloc: Relocation,
pub kind: DataDiffKind, pub kind: DataDiffKind,
pub range: Range<usize>, pub range: Range<usize>,
} }
@@ -376,7 +371,10 @@ fn generate_mapping_symbols(
}; };
let base_section_kind = symbol_section_kind(base_obj, &base_obj.symbols[base_symbol_ref]); let base_section_kind = symbol_section_kind(base_obj, &base_obj.symbols[base_symbol_ref]);
for (target_symbol_index, target_symbol) in target_obj.symbols.iter().enumerate() { for (target_symbol_index, target_symbol) in target_obj.symbols.iter().enumerate() {
if symbol_section_kind(target_obj, target_symbol) != base_section_kind { if target_symbol.size == 0
|| target_symbol.flags.contains(SymbolFlag::Ignored)
|| symbol_section_kind(target_obj, target_symbol) != base_section_kind
{
continue; continue;
} }
match base_section_kind { match base_section_kind {
@@ -531,6 +529,9 @@ fn matching_symbols(
)?; )?;
} }
for (symbol_idx, symbol) in left.symbols.iter().enumerate() { for (symbol_idx, symbol) in left.symbols.iter().enumerate() {
if symbol.size == 0 || symbol.flags.contains(SymbolFlag::Ignored) {
continue;
}
let section_kind = symbol_section_kind(left, symbol); let section_kind = symbol_section_kind(left, symbol);
if section_kind == SectionKind::Unknown { if section_kind == SectionKind::Unknown {
continue; continue;
@@ -552,6 +553,9 @@ fn matching_symbols(
} }
if let Some(right) = right { if let Some(right) = right {
for (symbol_idx, symbol) in right.symbols.iter().enumerate() { for (symbol_idx, symbol) in right.symbols.iter().enumerate() {
if symbol.size == 0 || symbol.flags.contains(SymbolFlag::Ignored) {
continue;
}
let section_kind = symbol_section_kind(right, symbol); let section_kind = symbol_section_kind(right, symbol);
if section_kind == SectionKind::Unknown { if section_kind == SectionKind::Unknown {
continue; continue;
@@ -577,9 +581,10 @@ fn unmatched_symbols<'obj, 'used>(
where where
'obj: 'used, 'obj: 'used,
{ {
obj.symbols.iter().enumerate().filter(move |&(symbol_idx, _)| { obj.symbols.iter().enumerate().filter(move |&(symbol_idx, symbol)| {
!symbol.flags.contains(SymbolFlag::Ignored)
// Skip symbols that have already been matched // Skip symbols that have already been matched
!used.is_some_and(|u| u.contains(&symbol_idx)) && !used.is_some_and(|u| u.contains(&symbol_idx))
}) })
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -11,7 +11,7 @@ use alloc::{
}; };
use core::{fmt, num::NonZeroU32}; use core::{fmt, num::NonZeroU32};
use flagset::{flags, FlagSet}; use flagset::{FlagSet, flags};
use crate::{ use crate::{
arch::{Arch, ArchDummy}, arch::{Arch, ArchDummy},
@@ -42,6 +42,8 @@ flags! {
HasExtra, HasExtra,
/// Symbol size was missing and was inferred /// Symbol size was missing and was inferred
SizeInferred, SizeInferred,
/// Symbol should be ignored by any diffing
Ignored,
} }
} }

View File

@@ -6,16 +6,16 @@ use alloc::{
}; };
use core::cmp::Ordering; use core::cmp::Ordering;
use anyhow::{bail, ensure, Context, Result}; use anyhow::{Context, Result, anyhow, bail, ensure};
use object::{Object as _, ObjectSection as _, ObjectSymbol as _}; use object::{Object as _, ObjectSection as _, ObjectSymbol as _};
use crate::{ use crate::{
arch::{new_arch, Arch}, arch::{Arch, new_arch},
diff::DiffObjConfig, diff::DiffObjConfig,
obj::{ obj::{
split_meta::{SplitMeta, SPLITMETA_SECTION},
Object, Relocation, RelocationFlags, Section, SectionData, SectionFlag, SectionKind, Object, Relocation, RelocationFlags, Section, SectionData, SectionFlag, SectionKind,
Symbol, SymbolFlag, SymbolKind, Symbol, SymbolFlag, SymbolKind,
split_meta::{SPLITMETA_SECTION, SplitMeta},
}, },
util::{read_u16, read_u32}, util::{read_u16, read_u32},
}; };
@@ -151,14 +151,22 @@ fn infer_symbol_sizes(symbols: &mut [Symbol], sections: &[Section]) {
// Set symbol sizes based on the next symbol's address // Set symbol sizes based on the next symbol's address
let mut iter_idx = 0; let mut iter_idx = 0;
let mut last_end = (0, 0);
while iter_idx < symbols_with_section.len() { while iter_idx < symbols_with_section.len() {
let symbol_idx = symbols_with_section[iter_idx]; let symbol_idx = symbols_with_section[iter_idx];
let symbol = &symbols[symbol_idx]; let symbol = &symbols[symbol_idx];
let section_idx = symbol.section.unwrap();
iter_idx += 1; iter_idx += 1;
if symbol.size != 0 { if symbol.size != 0 {
if symbol.kind != SymbolKind::Section {
last_end = (section_idx, symbol.address + symbol.size);
}
continue;
}
// Skip over symbols that are contained within the previous symbol
if last_end.0 == section_idx && last_end.1 > symbol.address {
continue; continue;
} }
let section_idx = symbol.section.unwrap();
let next_symbol = match symbol.kind { let next_symbol = match symbol.kind {
// For function/object symbols, find the next function/object symbol (in other words: // For function/object symbols, find the next function/object symbol (in other words:
// skip over labels) // skip over labels)
@@ -263,7 +271,7 @@ const LOW_PRIORITY_SYMBOLS: &[&str] =
fn best_symbol<'r, 'data, 'file>( fn best_symbol<'r, 'data, 'file>(
symbols: &'r [object::Symbol<'data, 'file>], symbols: &'r [object::Symbol<'data, 'file>],
address: u64, address: u64,
) -> Option<object::SymbolIndex> { ) -> Option<(object::SymbolIndex, u64)> {
let mut closest_symbol_index = match symbols.binary_search_by_key(&address, |s| s.address()) { let mut closest_symbol_index = match symbols.binary_search_by_key(&address, |s| s.address()) {
Ok(index) => Some(index), Ok(index) => Some(index),
Err(index) => index.checked_sub(1), Err(index) => index.checked_sub(1),
@@ -297,18 +305,31 @@ fn best_symbol<'r, 'data, 'file>(
best_symbol = Some(symbol); best_symbol = Some(symbol);
} }
} }
best_symbol.map(|s| s.index()) best_symbol.map(|s| (s.index(), s.address()))
} }
fn map_relocations( fn map_section_relocations(
arch: &dyn Arch, arch: &dyn Arch,
obj_file: &object::File, obj_file: &object::File,
obj_section: &object::Section, obj_section: &object::Section,
symbol_indices: &[usize], symbol_indices: &[usize],
ordered_symbols: &[Vec<object::Symbol>],
) -> Result<Vec<Relocation>> { ) -> Result<Vec<Relocation>> {
let mut relocations = Vec::<Relocation>::with_capacity(obj_section.relocations().count()); let mut relocations = Vec::<Relocation>::with_capacity(obj_section.relocations().count());
let mut ordered_symbols = None;
for (address, reloc) in obj_section.relocations() { 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);
}
};
// 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() { let target_symbol = match reloc.target() {
object::RelocationTarget::Symbol(idx) => { object::RelocationTarget::Symbol(idx) => {
if idx.0 == u32::MAX as usize { if idx.0 == u32::MAX as usize {
@@ -323,24 +344,16 @@ fn map_relocations(
{ {
let section_index = let section_index =
section_symbol.section_index().context("Section symbol without section")?; section_symbol.section_index().context("Section symbol without section")?;
let ordered_symbols = ordered_symbols.get_or_insert_with(|| { let target_address = section_symbol.address().wrapping_add_signed(addend);
let mut vec = obj_file if let Some((new_idx, addr)) = ordered_symbols
.symbols() .get(section_index.0)
.filter(|s| { .and_then(|symbols| best_symbol(symbols, target_address))
s.section_index() == Some(section_index) {
&& s.kind() != object::SymbolKind::Section addend = target_address.wrapping_sub(addr) as i64;
}) new_idx
.collect::<Vec<_>>(); } else {
vec.sort_by(|a, b| { idx
a.address().cmp(&b.address()).then(a.size().cmp(&b.size())) }
});
vec
});
best_symbol(
ordered_symbols,
section_symbol.address().wrapping_add_signed(reloc.addend()),
)
.unwrap_or(idx)
} else { } else {
idx idx
}; };
@@ -359,24 +372,93 @@ fn map_relocations(
} }
_ => bail!("Unhandled relocation target: {:?}", reloc.target()), _ => bail!("Unhandled relocation target: {:?}", reloc.target()),
}; };
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);
}
};
// TODO validate reloc here?
let addend = if reloc.has_implicit_addend() {
arch.implcit_addend(obj_file, obj_section, address, &reloc, flags)?
} else {
reloc.addend()
};
relocations.push(Relocation { address, flags, target_symbol, addend }); relocations.push(Relocation { address, flags, target_symbol, addend });
} }
relocations.sort_by_key(|r| r.address);
Ok(relocations) Ok(relocations)
} }
fn map_relocations(
arch: &dyn Arch,
obj_file: &object::File,
sections: &mut [Section],
section_indices: &[usize],
symbol_indices: &[usize],
) -> Result<()> {
// Generate a list of symbols for each section
let mut ordered_symbols =
Vec::<Vec<object::Symbol>>::with_capacity(obj_file.sections().count() + 1);
for symbol in obj_file.symbols() {
let Some(section_index) = symbol.section_index() else {
continue;
};
if symbol.kind() == object::SymbolKind::Section {
continue;
}
if section_index.0 >= ordered_symbols.len() {
ordered_symbols.resize_with(section_index.0 + 1, Vec::new);
}
ordered_symbols[section_index.0].push(symbol);
}
// Sort symbols by address and size
for vec in &mut ordered_symbols {
vec.sort_by(|a, b| a.address().cmp(&b.address()).then(a.size().cmp(&b.size())));
}
// Map relocations for each section. Section-relative relocations use the ordered symbols list
// to find a better target symbol, if available.
for obj_section in obj_file.sections() {
let section = &mut sections[section_indices[obj_section.index().0]];
if section.kind != SectionKind::Unknown {
section.relocations = map_section_relocations(
arch,
obj_file,
&obj_section,
symbol_indices,
&ordered_symbols,
)?;
}
}
Ok(())
}
fn calculate_pooled_relocations(
arch: &dyn Arch,
sections: &mut [Section],
symbols: &[Symbol],
) -> Result<()> {
for (section_index, section) in sections.iter_mut().enumerate() {
if section.kind != SectionKind::Code {
continue;
}
let mut fake_pool_relocs = Vec::new();
for symbol in symbols {
if symbol.section != Some(section_index) {
continue;
}
if symbol.kind != SymbolKind::Function {
continue;
}
let code =
section.data_range(symbol.address, symbol.size as usize).ok_or_else(|| {
anyhow!(
"Symbol data out of bounds: {:#x}..{:#x}",
symbol.address,
symbol.address + symbol.size
)
})?;
fake_pool_relocs.append(&mut arch.generate_pooled_relocations(
symbol.address,
code,
&section.relocations,
symbols,
));
}
section.relocations.append(&mut fake_pool_relocs);
section.relocations.sort_by_key(|r| r.address);
}
Ok(())
}
fn parse_line_info( fn parse_line_info(
obj_file: &object::File, obj_file: &object::File,
sections: &mut [Section], sections: &mut [Section],
@@ -767,12 +849,9 @@ pub fn parse(data: &[u8], config: &DiffObjConfig) -> Result<Object> {
map_sections(arch.as_ref(), &obj_file, split_meta.as_ref())?; map_sections(arch.as_ref(), &obj_file, split_meta.as_ref())?;
let (mut symbols, symbol_indices) = let (mut symbols, symbol_indices) =
map_symbols(arch.as_ref(), &obj_file, &sections, &section_indices, split_meta.as_ref())?; map_symbols(arch.as_ref(), &obj_file, &sections, &section_indices, split_meta.as_ref())?;
for obj_section in obj_file.sections() { map_relocations(arch.as_ref(), &obj_file, &mut sections, &section_indices, &symbol_indices)?;
let section = &mut sections[section_indices[obj_section.index().0]]; if config.ppc_calculate_pool_relocations {
if section.kind != SectionKind::Unknown { calculate_pooled_relocations(arch.as_ref(), &mut sections, &symbols)?;
section.relocations =
map_relocations(arch.as_ref(), &obj_file, &obj_section, &symbol_indices)?;
}
} }
parse_line_info(&obj_file, &mut sections, &section_indices, data)?; parse_line_info(&obj_file, &mut sections, &section_indices, data)?;
if config.combine_data_sections || config.combine_text_sections { if config.combine_data_sections || config.combine_text_sections {

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,47 @@
use objdiff_core::{diff, obj};
mod common;
#[test]
#[cfg(feature = "mips")]
fn read_mips() {
let diff_config = diff::DiffObjConfig { mips_register_prefix: true, ..Default::default() };
let obj = obj::read::parse(include_object!("data/mips/main.c.o"), &diff_config).unwrap();
insta::assert_debug_snapshot!(obj);
let symbol_idx = obj.symbols.iter().position(|s| s.name == "ControlEntry").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);
}
#[test]
#[cfg(feature = "mips")]
fn cross_endian_diff() {
let diff_config = diff::DiffObjConfig::default();
let obj_be = obj::read::parse(include_object!("data/mips/code_be.o"), &diff_config).unwrap();
assert_eq!(obj_be.endianness, object::Endianness::Big);
let obj_le = obj::read::parse(include_object!("data/mips/code_le.o"), &diff_config).unwrap();
assert_eq!(obj_le.endianness, object::Endianness::Little);
let left_symbol_idx = obj_be.symbols.iter().position(|s| s.name == "func_00000000").unwrap();
let right_symbol_idx =
obj_le.symbols.iter().position(|s| s.name == "func_00000000__FPcPc").unwrap();
let (left_diff, right_diff) =
diff::code::diff_code(&obj_be, &obj_le, left_symbol_idx, right_symbol_idx, &diff_config)
.unwrap();
// Although the objects differ in endianness, the instructions should match.
assert_eq!(left_diff.instruction_rows[0].kind, diff::InstructionDiffKind::None);
assert_eq!(right_diff.instruction_rows[0].kind, diff::InstructionDiffKind::None);
assert_eq!(left_diff.instruction_rows[1].kind, diff::InstructionDiffKind::None);
assert_eq!(right_diff.instruction_rows[1].kind, diff::InstructionDiffKind::None);
assert_eq!(left_diff.instruction_rows[2].kind, diff::InstructionDiffKind::None);
assert_eq!(right_diff.instruction_rows[2].kind, diff::InstructionDiffKind::None);
}
#[test]
#[cfg(feature = "mips")]
fn filter_non_matching() {
let diff_config = diff::DiffObjConfig::default();
let obj = obj::read::parse(include_object!("data/mips/vw_main.c.o"), &diff_config).unwrap();
insta::assert_debug_snapshot!(obj.symbols);
}

View File

@@ -61,7 +61,7 @@ fn diff_ppc() {
let base_diff = diff.right.as_ref().unwrap(); let base_diff = diff.right.as_ref().unwrap();
let sections_display = display::display_sections( let sections_display = display::display_sections(
&target_obj, &target_obj,
&target_diff, target_diff,
display::SymbolFilter::None, display::SymbolFilter::None,
false, false,
false, false,

View File

@@ -1,4 +1,4 @@
use objdiff_core::{diff, obj}; use objdiff_core::{diff, diff::display::SymbolFilter, obj};
mod common; mod common;
@@ -26,3 +26,32 @@ fn read_x86_combine_sections() {
let obj = obj::read::parse(include_object!("data/x86/rtest.obj"), &diff_config).unwrap(); let obj = obj::read::parse(include_object!("data/x86/rtest.obj"), &diff_config).unwrap();
insta::assert_debug_snapshot!(obj.sections); insta::assert_debug_snapshot!(obj.sections);
} }
#[test]
#[cfg(feature = "x86")]
fn read_x86_64() {
let diff_config = diff::DiffObjConfig::default();
let obj = obj::read::parse(include_object!("data/x86_64/vs2022.o"), &diff_config).unwrap();
insta::assert_debug_snapshot!(obj);
let symbol_idx =
obj.symbols.iter().position(|s| s.name == "?Dot@Vector@@QEAAMPEAU1@@Z").unwrap();
let diff = diff::code::no_diff_code(&obj, symbol_idx, &diff_config).unwrap();
insta::assert_debug_snapshot!(diff.instruction_rows);
let output = common::display_diff(&obj, &diff, symbol_idx, &diff_config);
insta::assert_snapshot!(output);
}
#[test]
#[cfg(feature = "x86")]
fn display_section_ordering() {
let diff_config = diff::DiffObjConfig::default();
let obj = obj::read::parse(include_object!("data/x86/basenode.obj"), &diff_config).unwrap();
let obj_diff =
diff::diff_objs(Some(&obj), None, None, &diff_config, &diff::MappingConfig::default())
.unwrap()
.left
.unwrap();
let section_display =
diff::display::display_sections(&obj, &obj_diff, SymbolFilter::None, false, false, false);
insta::assert_debug_snapshot!(section_display);
}

View File

@@ -1,5 +1,5 @@
use objdiff_core::{ use objdiff_core::{
diff::{display::DiffTextSegment, DiffObjConfig, SymbolDiff}, diff::{DiffObjConfig, SymbolDiff, display::DiffTextSegment},
obj::Object, obj::Object,
}; };
@@ -13,7 +13,7 @@ pub fn display_diff(
for row in &diff.instruction_rows { for row in &diff.instruction_rows {
output.push('['); output.push('[');
let mut separator = false; let mut separator = false;
objdiff_core::diff::display::display_row(&obj, symbol_idx, row, &diff_config, |segment| { objdiff_core::diff::display::display_row(obj, symbol_idx, row, diff_config, |segment| {
if separator { if separator {
output.push_str(", "); output.push_str(", ");
} else { } else {
@@ -47,6 +47,6 @@ macro_rules! include_bytes_align_as {
#[macro_export] #[macro_export]
macro_rules! include_object { macro_rules! include_object {
($path:literal) => { ($path:literal) => {
include_bytes_align_as!(u32, $path) include_bytes_align_as!(u64, $path)
}; };
} }

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,112 @@
---
source: objdiff-core/tests/arch_arm.rs
expression: output
---
[(Address(0), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stmdb", 32895), Normal, 10), (Argument(Opaque("sp")), Normal, 0), (Argument(Opaque("!")), Normal, 0), (Basic(", "), Normal, 0), (Basic("{"), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r6")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("lr")), Normal, 0), (Basic("}"), Normal, 0), (Eol, Normal, 0)]
[(Address(4), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mov", 32818), Normal, 10), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Eol, Normal, 0)]
[(Address(8), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mov", 32818), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Eol, Normal, 0)]
[(Address(12), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32774), Normal, 10), (Symbol(Symbol { name: "_ZN13LinkStateBase12OnStateLeaveEi", demangled_name: Some("LinkStateBase::OnStateLeave(int)"), address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global | Weak), align: None, virtual_address: None }), Bright, 0), (Addend(-8), Bright, 0), (Eol, Normal, 0)]
[(Address(16), Normal, 5), (Spacing(4), Normal, 0), (Opcode("ldr", 32799), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(20)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
[(Address(20), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmp", 32786), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(10)), Normal, 0), (Eol, Normal, 0)]
[(Address(24), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addls", 32770), Normal, 10), (Argument(Opaque("pc")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("pc")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("lsl")), Normal, 0), (Basic(" #"), Normal, 0), (Argument(Unsigned(2)), Normal, 0), (Eol, Normal, 0)]
[(Address(28), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 32773), Normal, 10), (BranchDest(200), Normal, 0), (Basic(" ~>"), Rotating(0), 0), (Eol, Normal, 0)]
[(Address(32), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 32773), Normal, 10), (BranchDest(200), Normal, 0), (Basic(" ~>"), Rotating(0), 0), (Eol, Normal, 0)]
[(Address(36), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 32773), Normal, 10), (BranchDest(200), Normal, 0), (Basic(" ~>"), Rotating(0), 0), (Eol, Normal, 0)]
[(Address(40), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 32773), Normal, 10), (BranchDest(200), Normal, 0), (Basic(" ~>"), Rotating(0), 0), (Eol, Normal, 0)]
[(Address(44), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 32773), Normal, 10), (BranchDest(192), Normal, 0), (Basic(" ~>"), Rotating(1), 0), (Eol, Normal, 0)]
[(Address(48), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 32773), Normal, 10), (BranchDest(124), Normal, 0), (Basic(" ~>"), Rotating(2), 0), (Eol, Normal, 0)]
[(Address(52), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 32773), Normal, 10), (BranchDest(200), Normal, 0), (Basic(" ~>"), Rotating(0), 0), (Eol, Normal, 0)]
[(Address(56), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 32773), Normal, 10), (BranchDest(140), Normal, 0), (Basic(" ~>"), Rotating(3), 0), (Eol, Normal, 0)]
[(Address(60), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 32773), Normal, 10), (BranchDest(76), Normal, 0), (Basic(" ~>"), Rotating(4), 0), (Eol, Normal, 0)]
[(Address(64), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 32773), Normal, 10), (BranchDest(152), Normal, 0), (Basic(" ~>"), Rotating(5), 0), (Eol, Normal, 0)]
[(Address(68), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 32773), Normal, 10), (BranchDest(164), Normal, 0), (Basic(" ~>"), Rotating(6), 0), (Eol, Normal, 0)]
[(Address(72), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 32773), Normal, 10), (BranchDest(164), Normal, 0), (Basic(" ~>"), Rotating(6), 0), (Eol, Normal, 0)]
[(Address(76), Normal, 5), (Basic(" ~> "), Rotating(4), 0), (Opcode("ldr", 32799), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("pc")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(336)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
[(Address(80), Normal, 5), (Spacing(4), Normal, 0), (Opcode("ldr", 32799), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(0)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
[(Address(84), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32774), Normal, 10), (Symbol(Symbol { name: "_ZN18UnkStruct_027e103c19func_ov000_020cf01cEv", demangled_name: Some("UnkStruct_027e103c::func_ov000_020cf01c()"), address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global | Weak), align: None, virtual_address: None }), Bright, 0), (Addend(-8), Bright, 0), (Eol, Normal, 0)]
[(Address(88), Normal, 5), (Spacing(4), Normal, 0), (Opcode("ldrb", 32800), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(224)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
[(Address(92), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmp", 32786), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(0)), Normal, 0), (Eol, Normal, 0)]
[(Address(96), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 32773), Normal, 10), (BranchDest(108), Normal, 0), (Basic(" ~>"), Rotating(7), 0), (Eol, Normal, 0)]
[(Address(100), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32774), Normal, 10), (Symbol(Symbol { name: "_ZN13LinkStateItem15GetEquipBombchuEv", demangled_name: Some("LinkStateItem::GetEquipBombchu()"), address: 472, size: 16, kind: Function, section: Some(0), flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Addend(-8), Bright, 0), (Basic(" ~>"), Rotating(8), 0), (Eol, Normal, 0)]
[(Address(104), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32774), Normal, 10), (Symbol(Symbol { name: "_ZN12EquipBombchu19func_ov014_0213ec64Ev", demangled_name: Some("EquipBombchu::func_ov014_0213ec64()"), address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global | Weak), align: None, virtual_address: None }), Bright, 0), (Addend(-8), Bright, 0), (Eol, Normal, 0)]
[(Address(108), Normal, 5), (Basic(" ~> "), Rotating(7), 0), (Opcode("ldr", 32799), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("pc")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(308)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
[(Address(112), Normal, 5), (Spacing(4), Normal, 0), (Opcode("ldr", 32799), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(0)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
[(Address(116), Normal, 5), (Spacing(4), Normal, 0), (Opcode("blx", 32777), Normal, 10), (Symbol(Symbol { name: "_Z19func_ov014_0211fd04Pi", demangled_name: Some("func_ov014_0211fd04(int*)"), address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global | Weak), align: None, virtual_address: None }), Bright, 0), (Addend(-8), Bright, 0), (Eol, Normal, 0)]
[(Address(120), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 32773), Normal, 10), (BranchDest(200), Normal, 0), (Basic(" ~>"), Rotating(0), 0), (Eol, Normal, 0)]
[(Address(124), Normal, 5), (Basic(" ~> "), Rotating(2), 0), (Opcode("mov", 32818), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Eol, Normal, 0)]
[(Address(128), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mov", 32818), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Eol, Normal, 0)]
[(Address(132), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32774), Normal, 10), (Symbol(Symbol { name: "_ZN13LinkStateItem13StopUsingBombEi", demangled_name: Some("LinkStateItem::StopUsingBomb(int)"), address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global | Weak), align: None, virtual_address: None }), Bright, 0), (Addend(-8), Bright, 0), (Eol, Normal, 0)]
[(Address(136), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 32773), Normal, 10), (BranchDest(200), Normal, 0), (Basic(" ~>"), Rotating(0), 0), (Eol, Normal, 0)]
[(Address(140), Normal, 5), (Basic(" ~> "), Rotating(3), 0), (Opcode("mov", 32818), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Eol, Normal, 0)]
[(Address(144), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32774), Normal, 10), (Symbol(Symbol { name: "_ZN13LinkStateItem13StopUsingRopeEv", demangled_name: Some("LinkStateItem::StopUsingRope()"), address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global | Weak), align: None, virtual_address: None }), Bright, 0), (Addend(-8), Bright, 0), (Eol, Normal, 0)]
[(Address(148), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 32773), Normal, 10), (BranchDest(200), Normal, 0), (Basic(" ~>"), Rotating(0), 0), (Eol, Normal, 0)]
[(Address(152), Normal, 5), (Basic(" ~> "), Rotating(5), 0), (Opcode("mov", 32818), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Eol, Normal, 0)]
[(Address(156), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32774), Normal, 10), (Symbol(Symbol { name: "_ZN13LinkStateItem15StopUsingHammerEv", demangled_name: Some("LinkStateItem::StopUsingHammer()"), address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global | Weak), align: None, virtual_address: None }), Bright, 0), (Addend(-8), Bright, 0), (Eol, Normal, 0)]
[(Address(160), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 32773), Normal, 10), (BranchDest(200), Normal, 0), (Basic(" ~>"), Rotating(0), 0), (Eol, Normal, 0)]
[(Address(164), Normal, 5), (Basic(" ~> "), Rotating(6), 0), (Opcode("ldr", 32799), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("pc")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(248)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
[(Address(168), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mov", 32818), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(0)), Normal, 0), (Eol, Normal, 0)]
[(Address(172), Normal, 5), (Spacing(4), Normal, 0), (Opcode("ldr", 32799), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(0)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
[(Address(176), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mov", 32818), Normal, 10), (Argument(Opaque("r2")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Eol, Normal, 0)]
[(Address(180), Normal, 5), (Spacing(4), Normal, 0), (Opcode("strb", 32899), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(42)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
[(Address(184), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32774), Normal, 10), (Symbol(Symbol { name: "_ZN18UnkStruct_027e103c19func_ov000_020cf9dcEii", demangled_name: Some("UnkStruct_027e103c::func_ov000_020cf9dc(int, int)"), address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global | Weak), align: None, virtual_address: None }), Bright, 0), (Addend(-8), Bright, 0), (Eol, Normal, 0)]
[(Address(188), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 32773), Normal, 10), (BranchDest(200), Normal, 0), (Basic(" ~>"), Rotating(0), 0), (Eol, Normal, 0)]
[(Address(192), Normal, 5), (Basic(" ~> "), Rotating(1), 0), (Opcode("mov", 32818), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Eol, Normal, 0)]
[(Address(196), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32774), Normal, 10), (Symbol(Symbol { name: "_ZN13LinkStateItem14StopUsingScoopEv", demangled_name: Some("LinkStateItem::StopUsingScoop()"), address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global | Weak), align: None, virtual_address: None }), Bright, 0), (Addend(-8), Bright, 0), (Eol, Normal, 0)]
[(Address(200), Normal, 5), (Basic(" ~> "), Rotating(0), 0), (Opcode("ldr", 32799), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(20)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
[(Address(204), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mvn", 32829), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(0)), Normal, 0), (Eol, Normal, 0)]
[(Address(208), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmp", 32786), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Eol, Normal, 0)]
[(Address(212), Normal, 5), (Spacing(4), Normal, 0), (Opcode("beq", 32773), Normal, 10), (BranchDest(236), Normal, 0), (Basic(" ~>"), Rotating(9), 0), (Eol, Normal, 0)]
[(Address(216), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mov", 32818), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Eol, Normal, 0)]
[(Address(220), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32774), Normal, 10), (Symbol(Symbol { name: "_ZN13LinkStateBase12GetEquipItemEi", demangled_name: Some("LinkStateBase::GetEquipItem(int)"), address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global | Weak), align: None, virtual_address: None }), Bright, 0), (Addend(-8), Bright, 0), (Eol, Normal, 0)]
[(Address(224), Normal, 5), (Spacing(4), Normal, 0), (Opcode("ldr", 32799), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(0)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
[(Address(228), Normal, 5), (Spacing(4), Normal, 0), (Opcode("ldr", 32799), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(28)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
[(Address(232), Normal, 5), (Spacing(4), Normal, 0), (Opcode("blx", 32778), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Eol, Normal, 0)]
[(Address(236), Normal, 5), (Basic(" ~> "), Rotating(9), 0), (Opcode("ldr", 32799), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(20)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
[(Address(240), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmp", 32786), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(9)), Normal, 0), (Eol, Normal, 0)]
[(Address(244), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bgt", 32773), Normal, 10), (BranchDest(288), Normal, 0), (Basic(" ~>"), Rotating(10), 0), (Eol, Normal, 0)]
[(Address(248), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bge", 32773), Normal, 10), (BranchDest(296), Normal, 0), (Basic(" ~>"), Rotating(11), 0), (Eol, Normal, 0)]
[(Address(252), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmp", 32786), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(1)), Normal, 0), (Eol, Normal, 0)]
[(Address(256), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bgt", 32773), Normal, 10), (BranchDest(308), Normal, 0), (Basic(" ~>"), Rotating(12), 0), (Eol, Normal, 0)]
[(Address(260), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mvn", 32829), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(0)), Normal, 0), (Eol, Normal, 0)]
[(Address(264), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmp", 32786), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Eol, Normal, 0)]
[(Address(268), Normal, 5), (Spacing(4), Normal, 0), (Opcode("blt", 32773), Normal, 10), (BranchDest(308), Normal, 0), (Basic(" ~>"), Rotating(12), 0), (Eol, Normal, 0)]
[(Address(272), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmpne", 32786), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(0)), Normal, 0), (Eol, Normal, 0)]
[(Address(276), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmpne", 32786), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(1)), Normal, 0), (Eol, Normal, 0)]
[(Address(280), Normal, 5), (Spacing(4), Normal, 0), (Opcode("beq", 32773), Normal, 10), (BranchDest(340), Normal, 0), (Basic(" ~>"), Rotating(13), 0), (Eol, Normal, 0)]
[(Address(284), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 32773), Normal, 10), (BranchDest(308), Normal, 0), (Basic(" ~>"), Rotating(12), 0), (Eol, Normal, 0)]
[(Address(288), Normal, 5), (Basic(" ~> "), Rotating(10), 0), (Opcode("cmp", 32786), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(10)), Normal, 0), (Eol, Normal, 0)]
[(Address(292), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 32773), Normal, 10), (BranchDest(308), Normal, 0), (Basic(" ~>"), Rotating(12), 0), (Eol, Normal, 0)]
[(Address(296), Normal, 5), (Basic(" ~> "), Rotating(11), 0), (Opcode("mov", 32818), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Eol, Normal, 0)]
[(Address(300), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32774), Normal, 10), (Symbol(Symbol { name: "_ZN13LinkStateBase18EquipItem_vfunc_28Ev", demangled_name: Some("LinkStateBase::EquipItem_vfunc_28()"), address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global | Weak), align: None, virtual_address: None }), Bright, 0), (Addend(-8), Bright, 0), (Eol, Normal, 0)]
[(Address(304), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 32773), Normal, 10), (BranchDest(340), Normal, 0), (Basic(" ~>"), Rotating(13), 0), (Eol, Normal, 0)]
[(Address(308), Normal, 5), (Basic(" ~> "), Rotating(12), 0), (Opcode("mov", 32818), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Eol, Normal, 0)]
[(Address(312), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32774), Normal, 10), (Symbol(Symbol { name: "_ZN13LinkStateBase18EquipItem_vfunc_28Ev", demangled_name: Some("LinkStateBase::EquipItem_vfunc_28()"), address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global | Weak), align: None, virtual_address: None }), Bright, 0), (Addend(-8), Bright, 0), (Eol, Normal, 0)]
[(Address(316), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmp", 32786), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(4)), Normal, 0), (Eol, Normal, 0)]
[(Address(320), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmpne", 32786), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(2)), Normal, 0), (Eol, Normal, 0)]
[(Address(324), Normal, 5), (Spacing(4), Normal, 0), (Opcode("beq", 32773), Normal, 10), (BranchDest(340), Normal, 0), (Basic(" ~>"), Rotating(13), 0), (Eol, Normal, 0)]
[(Address(328), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32774), Normal, 10), (Symbol(Symbol { name: "_ZN13LinkStateItem16GetLinkStateMoveEv", demangled_name: Some("LinkStateItem::GetLinkStateMove()"), address: 488, size: 16, kind: Function, section: Some(0), flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Addend(-8), Bright, 0), (Eol, Normal, 0)]
[(Address(332), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mov", 32818), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(1)), Normal, 0), (Eol, Normal, 0)]
[(Address(336), Normal, 5), (Spacing(4), Normal, 0), (Opcode("strb", 32899), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(20)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
[(Address(340), Normal, 5), (Basic(" ~> "), Rotating(13), 0), (Opcode("mvn", 32829), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(0)), Normal, 0), (Eol, Normal, 0)]
[(Address(344), Normal, 5), (Spacing(4), Normal, 0), (Opcode("add", 32770), Normal, 10), (Argument(Opaque("r6")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(80)), Normal, 0), (Eol, Normal, 0)]
[(Address(348), Normal, 5), (Spacing(4), Normal, 0), (Opcode("add", 32770), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(88)), Normal, 0), (Eol, Normal, 0)]
[(Address(352), Normal, 5), (Spacing(4), Normal, 0), (Opcode("str", 32898), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(24)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
[(Address(356), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmp", 32786), Normal, 10), (Argument(Opaque("r6")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Eol, Normal, 0)]
[(Address(360), Normal, 5), (Spacing(4), Normal, 0), (Opcode("beq", 32773), Normal, 10), (BranchDest(384), Normal, 0), (Basic(" ~>"), Rotating(14), 0), (Eol, Normal, 0)]
[(Address(364), Normal, 5), (Basic(" ~> "), Rotating(15), 0), (Opcode("mov", 32818), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r6")), Normal, 0), (Eol, Normal, 0)]
[(Address(368), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32774), Normal, 10), (Symbol(Symbol { name: "_Z19func_ov000_020b7e6cPi", demangled_name: Some("func_ov000_020b7e6c(int*)"), address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global | Weak), align: None, virtual_address: None }), Bright, 0), (Addend(-8), Bright, 0), (Eol, Normal, 0)]
[(Address(372), Normal, 5), (Spacing(4), Normal, 0), (Opcode("add", 32770), Normal, 10), (Argument(Opaque("r6")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r6")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(4)), Normal, 0), (Eol, Normal, 0)]
[(Address(376), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmp", 32786), Normal, 10), (Argument(Opaque("r6")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Eol, Normal, 0)]
[(Address(380), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 32773), Normal, 10), (BranchDest(364), Normal, 0), (Basic(" ~>"), Rotating(15), 0), (Eol, Normal, 0)]
[(Address(384), Normal, 5), (Basic(" ~> "), Rotating(14), 0), (Opcode("ldr", 32799), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("pc")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(36)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
[(Address(388), Normal, 5), (Spacing(4), Normal, 0), (Opcode("ldr", 32799), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(0)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
[(Address(392), Normal, 5), (Spacing(4), Normal, 0), (Opcode("ldrb", 32800), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(128)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
[(Address(396), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmp", 32786), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(0)), Normal, 0), (Eol, Normal, 0)]
[(Address(400), Normal, 5), (Spacing(4), Normal, 0), (Opcode("beq", 32773), Normal, 10), (BranchDest(408), Normal, 0), (Basic(" ~>"), Rotating(16), 0), (Eol, Normal, 0)]
[(Address(404), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 32774), Normal, 10), (Symbol(Symbol { name: "_ZN13PlayerControl13StopFollowingEv", demangled_name: Some("PlayerControl::StopFollowing()"), address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global | Weak), align: None, virtual_address: None }), Bright, 0), (Addend(-8), Bright, 0), (Eol, Normal, 0)]
[(Address(408), Normal, 5), (Basic(" ~> "), Rotating(16), 0), (Opcode("mov", 32818), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(0)), Normal, 0), (Eol, Normal, 0)]
[(Address(412), Normal, 5), (Spacing(4), Normal, 0), (Opcode("strb", 32899), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(38)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
[(Address(416), Normal, 5), (Spacing(4), Normal, 0), (Opcode("ldmia", 32793), Normal, 10), (Argument(Opaque("sp")), Normal, 0), (Argument(Opaque("!")), Normal, 0), (Basic(", "), Normal, 0), (Basic("{"), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r6")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("pc")), Normal, 0), (Basic("}"), Normal, 0), (Eol, Normal, 0)]
[(Address(420), Normal, 5), (Spacing(4), Normal, 0), (Opcode("andeq", 32771), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(" <"), Normal, 0), (Symbol(Symbol { name: "data_027e103c", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global | Weak), align: None, virtual_address: None }), Bright, 0), (Basic(">"), Normal, 0), (Eol, Normal, 0)]
[(Address(424), Normal, 5), (Basic(" ~> "), Rotating(8), 0), (Opcode("andeq", 32771), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(" <"), Normal, 0), (Symbol(Symbol { name: "data_027e1098", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global | Weak), align: None, virtual_address: None }), Bright, 0), (Basic(">"), Normal, 0), (Eol, Normal, 0)]
[(Address(428), Normal, 5), (Spacing(4), Normal, 0), (Opcode("andeq", 32771), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(" <"), Normal, 0), (Symbol(Symbol { name: "gPlayerControl", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global | Weak), align: None, virtual_address: None }), Bright, 0), (Basic(">"), Normal, 0), (Eol, Normal, 0)]

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,452 @@
---
source: objdiff-core/tests/arch_mips.rs
expression: obj.symbols
---
[
Symbol {
name: "build/src/bodyprog/view/vw_main.i",
demangled_name: None,
address: 0,
size: 0,
kind: Unknown,
section: None,
flags: FlagSet(Local),
align: None,
virtual_address: None,
},
Symbol {
name: "[.text]",
demangled_name: None,
address: 0,
size: 0,
kind: Section,
section: Some(
0,
),
flags: FlagSet(Local),
align: None,
virtual_address: None,
},
Symbol {
name: "[.data]",
demangled_name: None,
address: 0,
size: 0,
kind: Section,
section: Some(
2,
),
flags: FlagSet(Local),
align: None,
virtual_address: None,
},
Symbol {
name: "[.bss]",
demangled_name: None,
address: 0,
size: 0,
kind: Section,
section: Some(
3,
),
flags: FlagSet(Local),
align: None,
virtual_address: None,
},
Symbol {
name: "gcc2_compiled.",
demangled_name: None,
address: 0,
size: 0,
kind: Unknown,
section: Some(
0,
),
flags: FlagSet(Local),
align: None,
virtual_address: None,
},
Symbol {
name: "__gnu_compiled_c",
demangled_name: None,
address: 0,
size: 0,
kind: Unknown,
section: Some(
0,
),
flags: FlagSet(Local),
align: None,
virtual_address: None,
},
Symbol {
name: ".endfunc_80048AF4",
demangled_name: None,
address: 424,
size: 0,
kind: Unknown,
section: Some(
0,
),
flags: FlagSet(Local),
align: None,
virtual_address: None,
},
Symbol {
name: ".endfunc_80048DA8",
demangled_name: None,
address: 1028,
size: 0,
kind: Unknown,
section: Some(
0,
),
flags: FlagSet(Local),
align: None,
virtual_address: None,
},
Symbol {
name: ".endfunc_80048E3C",
demangled_name: None,
address: 1264,
size: 0,
kind: Unknown,
section: Some(
0,
),
flags: FlagSet(Local),
align: None,
virtual_address: None,
},
Symbol {
name: "[.reginfo]",
demangled_name: None,
address: 0,
size: 24,
kind: Section,
section: Some(
4,
),
flags: FlagSet(Local),
align: None,
virtual_address: None,
},
Symbol {
name: "[.MIPS.abiflags]",
demangled_name: None,
address: 0,
size: 24,
kind: Section,
section: Some(
5,
),
flags: FlagSet(Local),
align: None,
virtual_address: None,
},
Symbol {
name: "[.pdr]",
demangled_name: None,
address: 0,
size: 320,
kind: Section,
section: Some(
6,
),
flags: FlagSet(Local),
align: None,
virtual_address: None,
},
Symbol {
name: "[.gnu.attributes]",
demangled_name: None,
address: 0,
size: 16,
kind: Section,
section: Some(
8,
),
flags: FlagSet(Local),
align: None,
virtual_address: None,
},
Symbol {
name: "vwInitViewInfo",
demangled_name: None,
address: 0,
size: 88,
kind: Function,
section: Some(
0,
),
flags: FlagSet(Global),
align: None,
virtual_address: None,
},
Symbol {
name: "vwViewPointInfo",
demangled_name: None,
address: 0,
size: 0,
kind: Unknown,
section: None,
flags: FlagSet(Global),
align: None,
virtual_address: None,
},
Symbol {
name: "GsInitCoordinate2",
demangled_name: None,
address: 0,
size: 0,
kind: Unknown,
section: None,
flags: FlagSet(Global),
align: None,
virtual_address: None,
},
Symbol {
name: "vwSetViewInfo",
demangled_name: None,
address: 784,
size: 96,
kind: Function,
section: Some(
0,
),
flags: FlagSet(Global),
align: None,
virtual_address: None,
},
Symbol {
name: "vwGetViewCoord",
demangled_name: None,
address: 88,
size: 12,
kind: Function,
section: Some(
0,
),
flags: FlagSet(Global),
align: None,
virtual_address: None,
},
Symbol {
name: "vwGetViewPosition",
demangled_name: None,
address: 100,
size: 40,
kind: Function,
section: Some(
0,
),
flags: FlagSet(Global),
align: None,
virtual_address: None,
},
Symbol {
name: "vwGetViewAngle",
demangled_name: None,
address: 140,
size: 48,
kind: Function,
section: Some(
0,
),
flags: FlagSet(Global),
align: None,
virtual_address: None,
},
Symbol {
name: "func_80048AF4",
demangled_name: None,
address: 188,
size: 236,
kind: Function,
section: Some(
0,
),
flags: FlagSet(Global | Ignored),
align: None,
virtual_address: None,
},
Symbol {
name: "ratan2",
demangled_name: None,
address: 0,
size: 0,
kind: Unknown,
section: None,
flags: FlagSet(Global),
align: None,
virtual_address: None,
},
Symbol {
name: "SquareRoot0",
demangled_name: None,
address: 0,
size: 0,
kind: Unknown,
section: None,
flags: FlagSet(Global),
align: None,
virtual_address: None,
},
Symbol {
name: "func_80096C94",
demangled_name: None,
address: 0,
size: 0,
kind: Unknown,
section: None,
flags: FlagSet(Global),
align: None,
virtual_address: None,
},
Symbol {
name: "vwSetViewInfoDirectMatrix",
demangled_name: None,
address: 696,
size: 88,
kind: Function,
section: Some(
0,
),
flags: FlagSet(Global),
align: None,
virtual_address: None,
},
Symbol {
name: "func_80048AF4.NON_MATCHING",
demangled_name: None,
address: 188,
size: 236,
kind: Function,
section: Some(
0,
),
flags: FlagSet(Global | Ignored),
align: None,
virtual_address: None,
},
Symbol {
name: "vwSetCoordRefAndEntou",
demangled_name: None,
address: 424,
size: 272,
kind: Function,
section: Some(
0,
),
flags: FlagSet(Global),
align: None,
virtual_address: None,
},
Symbol {
name: "func_80096E78",
demangled_name: None,
address: 0,
size: 0,
kind: Unknown,
section: None,
flags: FlagSet(Global),
align: None,
virtual_address: None,
},
Symbol {
name: "shRsin",
demangled_name: None,
address: 0,
size: 0,
kind: Unknown,
section: None,
flags: FlagSet(Global),
align: None,
virtual_address: None,
},
Symbol {
name: "shRcos",
demangled_name: None,
address: 0,
size: 0,
kind: Unknown,
section: None,
flags: FlagSet(Global),
align: None,
virtual_address: None,
},
Symbol {
name: "vbSetRefView",
demangled_name: None,
address: 0,
size: 0,
kind: Unknown,
section: None,
flags: FlagSet(Global),
align: None,
virtual_address: None,
},
Symbol {
name: "vwMatrixToAngleYXZ",
demangled_name: None,
address: 0,
size: 0,
kind: Unknown,
section: None,
flags: FlagSet(Global),
align: None,
virtual_address: None,
},
Symbol {
name: "func_80048DA8",
demangled_name: None,
address: 880,
size: 148,
kind: Function,
section: Some(
0,
),
flags: FlagSet(Global | Ignored),
align: None,
virtual_address: None,
},
Symbol {
name: "func_80048DA8.NON_MATCHING",
demangled_name: None,
address: 880,
size: 148,
kind: Function,
section: Some(
0,
),
flags: FlagSet(Global | Ignored),
align: None,
virtual_address: None,
},
Symbol {
name: "func_80048E3C",
demangled_name: None,
address: 1028,
size: 236,
kind: Function,
section: Some(
0,
),
flags: FlagSet(Global | Ignored),
align: None,
virtual_address: None,
},
Symbol {
name: "func_80048E3C.NON_MATCHING",
demangled_name: None,
address: 1028,
size: 236,
kind: Function,
section: Some(
0,
),
flags: FlagSet(Global | Ignored),
align: None,
virtual_address: None,
},
]

View File

@@ -0,0 +1,640 @@
---
source: objdiff-core/tests/arch_mips.rs
expression: diff.instruction_rows
---
[
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 0,
size: 4,
opcode: 12,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 4,
size: 4,
opcode: 44,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 8,
size: 4,
opcode: 44,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 12,
size: 4,
opcode: 44,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 16,
size: 4,
opcode: 44,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 20,
size: 4,
opcode: 2,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 24,
size: 4,
opcode: 113,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 28,
size: 4,
opcode: 26,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 32,
size: 4,
opcode: 20,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 36,
size: 4,
opcode: 97,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 40,
size: 4,
opcode: 2,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 44,
size: 4,
opcode: 12,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 48,
size: 4,
opcode: 20,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 52,
size: 4,
opcode: 26,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 56,
size: 4,
opcode: 2,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 60,
size: 4,
opcode: 12,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 64,
size: 4,
opcode: 26,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 68,
size: 4,
opcode: 97,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 72,
size: 4,
opcode: 2,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 76,
size: 4,
opcode: 97,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 80,
size: 4,
opcode: 2,
},
),
kind: None,
branch_from: Some(
InstructionBranchFrom {
ins_idx: [
22,
],
branch_idx: 0,
},
),
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 84,
size: 4,
opcode: 97,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 88,
size: 4,
opcode: 56,
},
),
kind: None,
branch_from: None,
branch_to: Some(
InstructionBranchTo {
ins_idx: 20,
branch_idx: 0,
},
),
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 92,
size: 4,
opcode: 113,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 96,
size: 4,
opcode: 2,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 100,
size: 4,
opcode: 20,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 104,
size: 4,
opcode: 2,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 108,
size: 4,
opcode: 12,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 112,
size: 4,
opcode: 2,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 116,
size: 4,
opcode: 16,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 120,
size: 4,
opcode: 20,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 124,
size: 4,
opcode: 12,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 128,
size: 4,
opcode: 2,
},
),
kind: None,
branch_from: Some(
InstructionBranchFrom {
ins_idx: [
36,
38,
44,
],
branch_idx: 1,
},
),
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 132,
size: 4,
opcode: 12,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 136,
size: 4,
opcode: 2,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 140,
size: 4,
opcode: 113,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 144,
size: 4,
opcode: 55,
},
),
kind: None,
branch_from: None,
branch_to: Some(
InstructionBranchTo {
ins_idx: 32,
branch_idx: 1,
},
),
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 148,
size: 4,
opcode: 90,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 152,
size: 4,
opcode: 3,
},
),
kind: None,
branch_from: None,
branch_to: Some(
InstructionBranchTo {
ins_idx: 32,
branch_idx: 1,
},
),
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 156,
size: 4,
opcode: 113,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 160,
size: 4,
opcode: 2,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 164,
size: 4,
opcode: 60,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 168,
size: 4,
opcode: 77,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 172,
size: 4,
opcode: 113,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 176,
size: 4,
opcode: 54,
},
),
kind: None,
branch_from: None,
branch_to: Some(
InstructionBranchTo {
ins_idx: 32,
branch_idx: 1,
},
),
arg_diff: [],
},
InstructionDiffRow {
ins_ref: Some(
InstructionRef {
address: 180,
size: 4,
opcode: 113,
},
),
kind: None,
branch_from: None,
branch_to: None,
arg_diff: [],
},
]

View File

@@ -0,0 +1,50 @@
---
source: objdiff-core/tests/arch_mips.rs
expression: output
---
[(Address(0), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addiu", 12), Normal, 10), (Argument(Opaque("$sp")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("$sp")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-32)), Normal, 0), (Eol, Normal, 0)]
[(Address(4), Normal, 5), (Spacing(4), Normal, 0), (Opcode("sd", 44), Normal, 10), (Argument(Opaque("$s0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(0)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("$sp")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(8), Normal, 5), (Spacing(4), Normal, 0), (Opcode("sd", 44), Normal, 10), (Argument(Opaque("$s1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(8)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("$sp")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(12), Normal, 5), (Spacing(4), Normal, 0), (Opcode("sd", 44), Normal, 10), (Argument(Opaque("$s2")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(16)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("$sp")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(16), Normal, 5), (Spacing(4), Normal, 0), (Opcode("sd", 44), Normal, 10), (Argument(Opaque("$ra")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(24)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("$sp")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(20), Normal, 5), (Spacing(4), Normal, 0), (Opcode("jal", 2), Normal, 10), (Symbol(Symbol { name: "xglSleep", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
[(Address(24), Normal, 5), (Spacing(4), Normal, 0), (Opcode("nop", 113), Normal, 10), (Eol, Normal, 0)]
[(Address(28), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lw", 26), Normal, 10), (Argument(Opaque("$a1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("%gp_rel("), Normal, 0), (Symbol(Symbol { name: "WorkEnd", demangled_name: None, address: 64, size: 4, kind: Object, section: Some(8), flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Basic(")"), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("$gp")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(32), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lui", 20), Normal, 10), (Argument(Opaque("$a0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("%hi("), Normal, 0), (Symbol(Symbol { name: "[.sdata]", demangled_name: None, address: 0, size: 64, kind: Section, section: Some(8), flags: FlagSet(Local), align: None, virtual_address: None }), Bright, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(36), Normal, 5), (Spacing(4), Normal, 0), (Opcode("daddu", 97), Normal, 10), (Argument(Opaque("$a2")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("$zero")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("$zero")), Normal, 0), (Eol, Normal, 0)]
[(Address(40), Normal, 5), (Spacing(4), Normal, 0), (Opcode("jal", 2), Normal, 10), (Symbol(Symbol { name: "xglSoundLoadEffect", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
[(Address(44), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addiu", 12), Normal, 10), (Argument(Opaque("$a0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("$a0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("%lo("), Normal, 0), (Symbol(Symbol { name: "[.sdata]", demangled_name: None, address: 0, size: 64, kind: Section, section: Some(8), flags: FlagSet(Local), align: None, virtual_address: None }), Bright, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(48), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lui", 20), Normal, 10), (Argument(Opaque("$a0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("%hi("), Normal, 0), (Symbol(Symbol { name: "PacketBottomNewVu1DropMicroCode", demangled_name: None, address: 0, size: 12, kind: Object, section: Some(7), flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(52), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lw", 26), Normal, 10), (Argument(Opaque("$a1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("%gp_rel("), Normal, 0), (Symbol(Symbol { name: "WorkEnd", demangled_name: None, address: 64, size: 4, kind: Object, section: Some(8), flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Basic(")"), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("$gp")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(56), Normal, 5), (Spacing(4), Normal, 0), (Opcode("jal", 2), Normal, 10), (Symbol(Symbol { name: "xglSoundLoadSwd", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
[(Address(60), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addiu", 12), Normal, 10), (Argument(Opaque("$a0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("$a0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("%lo("), Normal, 0), (Symbol(Symbol { name: "PacketBottomNewVu1DropMicroCode", demangled_name: None, address: 0, size: 12, kind: Object, section: Some(7), flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(64), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lw", 26), Normal, 10), (Argument(Opaque("$a0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("%gp_rel("), Normal, 0), (Symbol(Symbol { name: "WorkEnd", demangled_name: None, address: 64, size: 4, kind: Object, section: Some(8), flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Basic(")"), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("$gp")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(68), Normal, 5), (Spacing(4), Normal, 0), (Opcode("daddu", 97), Normal, 10), (Argument(Opaque("$a1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("$zero")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("$zero")), Normal, 0), (Eol, Normal, 0)]
[(Address(72), Normal, 5), (Spacing(4), Normal, 0), (Opcode("jal", 2), Normal, 10), (Symbol(Symbol { name: "SsdAddWaveData", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
[(Address(76), Normal, 5), (Spacing(4), Normal, 0), (Opcode("daddu", 97), Normal, 10), (Argument(Opaque("$a2")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("$zero")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("$zero")), Normal, 0), (Eol, Normal, 0)]
[(Address(80), Normal, 5), (Basic(" ~> "), Rotating(0), 0), (Opcode("jal", 2), Normal, 10), (Symbol(Symbol { name: "SsdSpuDmaCompleted", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
[(Address(84), Normal, 5), (Spacing(4), Normal, 0), (Opcode("daddu", 97), Normal, 10), (Argument(Opaque("$a0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("$zero")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("$zero")), Normal, 0), (Eol, Normal, 0)]
[(Address(88), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bnez", 56), Normal, 10), (Argument(Opaque("$v0")), Normal, 0), (Basic(", "), Normal, 0), (BranchDest(80), Normal, 0), (Basic(" ~>"), Rotating(0), 0), (Eol, Normal, 0)]
[(Address(92), Normal, 5), (Spacing(4), Normal, 0), (Opcode("nop", 113), Normal, 10), (Eol, Normal, 0)]
[(Address(96), Normal, 5), (Spacing(4), Normal, 0), (Opcode("jal", 2), Normal, 10), (Symbol(Symbol { name: "xglRenderDispOn", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
[(Address(100), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lui", 20), Normal, 10), (Argument(Opaque("$s1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Unsigned(255)), Normal, 0), (Eol, Normal, 0)]
[(Address(104), Normal, 5), (Spacing(4), Normal, 0), (Opcode("jal", 2), Normal, 10), (Symbol(Symbol { name: "xglCdLoadOverlay", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
[(Address(108), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addiu", 12), Normal, 10), (Argument(Opaque("$a0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("$zero")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(2)), Normal, 0), (Eol, Normal, 0)]
[(Address(112), Normal, 5), (Spacing(4), Normal, 0), (Opcode("jal", 2), Normal, 10), (Symbol(Symbol { name: "LogoFirst", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
[(Address(116), Normal, 5), (Spacing(4), Normal, 0), (Opcode("ori", 16), Normal, 10), (Argument(Opaque("$s1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("$s1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Unsigned(65535)), Normal, 0), (Eol, Normal, 0)]
[(Address(120), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lui", 20), Normal, 10), (Argument(Opaque("$v0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("%hi("), Normal, 0), (Symbol(Symbol { name: "Title", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(124), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addiu", 12), Normal, 10), (Argument(Opaque("$s2")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("$v0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("%lo("), Normal, 0), (Symbol(Symbol { name: "Title", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
[(Address(128), Normal, 5), (Basic(" ~> "), Rotating(1), 0), (Opcode("jal", 2), Normal, 10), (Symbol(Symbol { name: "xglCdLoadOverlay", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
[(Address(132), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addiu", 12), Normal, 10), (Argument(Opaque("$a0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("$zero")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(2)), Normal, 0), (Eol, Normal, 0)]
[(Address(136), Normal, 5), (Spacing(4), Normal, 0), (Opcode("jal", 2), Normal, 10), (Symbol(Symbol { name: "Title", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
[(Address(140), Normal, 5), (Spacing(4), Normal, 0), (Opcode("nop", 113), Normal, 10), (Eol, Normal, 0)]
[(Address(144), Normal, 5), (Spacing(4), Normal, 0), (Opcode("beqz", 55), Normal, 10), (Argument(Opaque("$v0")), Normal, 0), (Basic(", "), Normal, 0), (BranchDest(128), Normal, 0), (Basic(" ~>"), Rotating(1), 0), (Eol, Normal, 0)]
[(Address(148), Normal, 5), (Spacing(4), Normal, 0), (Opcode("and", 90), Normal, 10), (Argument(Opaque("$s0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("$v0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("$s1")), Normal, 0), (Eol, Normal, 0)]
[(Address(152), Normal, 5), (Spacing(4), Normal, 0), (Opcode("beq", 3), Normal, 10), (Argument(Opaque("$s0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("$s2")), Normal, 0), (Basic(", "), Normal, 0), (BranchDest(128), Normal, 0), (Basic(" ~>"), Rotating(1), 0), (Eol, Normal, 0)]
[(Address(156), Normal, 5), (Spacing(4), Normal, 0), (Opcode("nop", 113), Normal, 10), (Eol, Normal, 0)]
[(Address(160), Normal, 5), (Spacing(4), Normal, 0), (Opcode("jal", 2), Normal, 10), (Symbol(Symbol { name: "xglCdLoadOverlay", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
[(Address(164), Normal, 5), (Spacing(4), Normal, 0), (Opcode("srl", 60), Normal, 10), (Argument(Opaque("$a0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("$v0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("24")), Normal, 0), (Eol, Normal, 0)]
[(Address(168), Normal, 5), (Spacing(4), Normal, 0), (Opcode("jalr", 77), Normal, 10), (Argument(Opaque("$s0")), Normal, 0), (Eol, Normal, 0)]
[(Address(172), Normal, 5), (Spacing(4), Normal, 0), (Opcode("nop", 113), Normal, 10), (Eol, Normal, 0)]
[(Address(176), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 54), Normal, 10), (BranchDest(128), Normal, 0), (Basic(" ~>"), Rotating(1), 0), (Eol, Normal, 0)]
[(Address(180), Normal, 5), (Spacing(4), Normal, 0), (Opcode("nop", 113), Normal, 10), (Eol, Normal, 0)]

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,210 @@
---
source: objdiff-core/tests/arch_x86.rs
expression: section_display
---
[
SectionDisplay {
id: ".text-0",
name: ".text",
size: 47,
match_percent: None,
symbols: [
SectionDisplaySymbol {
symbol: 28,
is_mapping_symbol: false,
},
],
},
SectionDisplay {
id: ".text-1",
name: ".text",
size: 65,
match_percent: None,
symbols: [
SectionDisplaySymbol {
symbol: 29,
is_mapping_symbol: false,
},
],
},
SectionDisplay {
id: ".text-2",
name: ".text",
size: 5,
match_percent: None,
symbols: [
SectionDisplaySymbol {
symbol: 30,
is_mapping_symbol: false,
},
],
},
SectionDisplay {
id: ".text-3",
name: ".text",
size: 141,
match_percent: None,
symbols: [
SectionDisplaySymbol {
symbol: 31,
is_mapping_symbol: false,
},
],
},
SectionDisplay {
id: ".text-4",
name: ".text",
size: 120,
match_percent: None,
symbols: [
SectionDisplaySymbol {
symbol: 34,
is_mapping_symbol: false,
},
],
},
SectionDisplay {
id: ".text-5",
name: ".text",
size: 378,
match_percent: None,
symbols: [
SectionDisplaySymbol {
symbol: 37,
is_mapping_symbol: false,
},
],
},
SectionDisplay {
id: ".text-6",
name: ".text",
size: 130,
match_percent: None,
symbols: [
SectionDisplaySymbol {
symbol: 38,
is_mapping_symbol: false,
},
],
},
SectionDisplay {
id: ".text-7",
name: ".text",
size: 123,
match_percent: None,
symbols: [
SectionDisplaySymbol {
symbol: 39,
is_mapping_symbol: false,
},
],
},
SectionDisplay {
id: ".text-8",
name: ".text",
size: 70,
match_percent: None,
symbols: [
SectionDisplaySymbol {
symbol: 40,
is_mapping_symbol: false,
},
],
},
SectionDisplay {
id: ".text-9",
name: ".text",
size: 90,
match_percent: None,
symbols: [
SectionDisplaySymbol {
symbol: 41,
is_mapping_symbol: false,
},
],
},
SectionDisplay {
id: ".text-10",
name: ".text",
size: 82,
match_percent: None,
symbols: [
SectionDisplaySymbol {
symbol: 42,
is_mapping_symbol: false,
},
],
},
SectionDisplay {
id: ".text-11",
name: ".text",
size: 336,
match_percent: None,
symbols: [
SectionDisplaySymbol {
symbol: 43,
is_mapping_symbol: false,
},
],
},
SectionDisplay {
id: ".text-12",
name: ".text",
size: 193,
match_percent: None,
symbols: [
SectionDisplaySymbol {
symbol: 50,
is_mapping_symbol: false,
},
],
},
SectionDisplay {
id: ".text-13",
name: ".text",
size: 544,
match_percent: None,
symbols: [
SectionDisplaySymbol {
symbol: 53,
is_mapping_symbol: false,
},
],
},
SectionDisplay {
id: ".text-14",
name: ".text",
size: 250,
match_percent: None,
symbols: [
SectionDisplaySymbol {
symbol: 56,
is_mapping_symbol: false,
},
],
},
SectionDisplay {
id: ".text-15",
name: ".text",
size: 89,
match_percent: None,
symbols: [
SectionDisplaySymbol {
symbol: 57,
is_mapping_symbol: false,
},
],
},
SectionDisplay {
id: ".text-16",
name: ".text",
size: 119,
match_percent: None,
symbols: [
SectionDisplaySymbol {
symbol: 60,
is_mapping_symbol: false,
},
],
},
]

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@@ -11,7 +11,7 @@ use objdiff_core::{
}; };
use typed_path::{Utf8PlatformPathBuf, Utf8UnixPathBuf}; use typed_path::{Utf8PlatformPathBuf, Utf8UnixPathBuf};
use crate::app::{AppConfig, ObjectConfig, CONFIG_KEY}; use crate::app::{AppConfig, CONFIG_KEY, ObjectConfig};
#[derive(Clone, serde::Deserialize, serde::Serialize)] #[derive(Clone, serde::Deserialize, serde::Serialize)]
pub struct AppConfigVersion { pub struct AppConfigVersion {
@@ -273,7 +273,6 @@ impl DiffObjConfigV1 {
}, },
space_between_args: self.space_between_args, space_between_args: self.space_between_args,
combine_data_sections: self.combine_data_sections, combine_data_sections: self.combine_data_sections,
combine_text_sections: false,
x86_formatter: self.x86_formatter, x86_formatter: self.x86_formatter,
mips_abi: self.mips_abi, mips_abi: self.mips_abi,
mips_instr_category: self.mips_instr_category, mips_instr_category: self.mips_instr_category,
@@ -284,6 +283,7 @@ impl DiffObjConfigV1 {
arm_sl_usage: self.arm_sl_usage, arm_sl_usage: self.arm_sl_usage,
arm_fp_usage: self.arm_fp_usage, arm_fp_usage: self.arm_fp_usage,
arm_ip_usage: self.arm_ip_usage, arm_ip_usage: self.arm_ip_usage,
..Default::default()
} }
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,7 +1,9 @@
[package] [package]
name = "objdiff-wasm" name = "objdiff-wasm"
version.workspace = true version.workspace = true
edition.workspace = true # TODO: Update to 2024
# https://github.com/bytecodealliance/wit-bindgen/pull/1183
edition = "2021"
rust-version.workspace = true rust-version.workspace = true
authors.workspace = true authors.workspace = true
license.workspace = true license.workspace = true
@@ -22,7 +24,7 @@ std = ["objdiff-core/std"]
[dependencies] [dependencies]
log = { version = "0.4", default-features = false } log = { version = "0.4", default-features = false }
regex = { version = "1.11", default-features = false } regex = { version = "1.11", default-features = false, features = ["unicode-case"] }
[dependencies.objdiff-core] [dependencies.objdiff-core]
path = "../objdiff-core" path = "../objdiff-core"
@@ -33,7 +35,7 @@ features = ["arm", "arm64", "mips", "ppc", "x86", "dwarf"]
talc = { version = "4.4", default-features = false, features = ["lock_api"] } talc = { version = "4.4", default-features = false, features = ["lock_api"] }
[target.'cfg(target_os = "wasi")'.dependencies] [target.'cfg(target_os = "wasi")'.dependencies]
wit-bindgen = { version = "0.39", default-features = false, features = ["macros"] } wit-bindgen = { version = "0.40", default-features = false, features = ["macros"] }
[build-dependencies] [build-dependencies]
wit-deps = "0.5" wit-deps = "0.5"

View File

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

View File

@@ -1,6 +1,6 @@
{ {
"name": "objdiff-wasm", "name": "objdiff-wasm",
"version": "3.0.0-alpha.1", "version": "3.0.0-beta.3",
"description": "A local diffing tool for decompilation projects.", "description": "A local diffing tool for decompilation projects.",
"author": { "author": {
"name": "Luke Street", "name": "Luke Street",
@@ -12,7 +12,9 @@
"type": "git", "type": "git",
"url": "git+https://github.com/encounter/objdiff.git" "url": "git+https://github.com/encounter/objdiff.git"
}, },
"files": ["dist/*"], "files": [
"dist/*"
],
"main": "dist/objdiff.js", "main": "dist/objdiff.js",
"types": "dist/objdiff.d.ts", "types": "dist/objdiff.d.ts",
"scripts": { "scripts": {

View File

@@ -8,7 +8,7 @@ use alloc::{
use core::cell::RefCell; use core::cell::RefCell;
use objdiff_core::{diff, obj}; use objdiff_core::{diff, obj};
use regex::RegexBuilder; use regex::{Regex, RegexBuilder};
use super::logging; use super::logging;
@@ -86,19 +86,31 @@ impl GuestDiff for Component {
} }
} }
fn build_regex(s: &str) -> Option<Regex> {
if s.is_empty() {
return None;
}
let e = match RegexBuilder::new(s).case_insensitive(true).build() {
Ok(regex) => return Some(regex),
Err(e) => e,
};
// Use the string as a literal if the regex fails to compile
let escaped = regex::escape(s);
if let Ok(regex) = RegexBuilder::new(&escaped).case_insensitive(true).build() {
return Some(regex);
}
// Use the original error if the escaped string also fails
log::warn!("Failed to compile regex: {}", e);
None
}
impl GuestDisplay for Component { impl GuestDisplay for Component {
fn display_sections( fn display_sections(
diff: ObjectDiffBorrow, diff: ObjectDiffBorrow,
filter: SymbolFilter, filter: SymbolFilter,
config: DisplayConfig, config: DisplayConfig,
) -> Vec<SectionDisplay> { ) -> Vec<SectionDisplay> {
let regex = filter.regex.as_ref().and_then(|s| { let regex = filter.regex.as_deref().and_then(build_regex);
RegexBuilder::new(s).case_insensitive(true).build().ok().or_else(|| {
// Use the string as a literal if the regex fails to compile
let escaped = regex::escape(s);
RegexBuilder::new(&escaped).case_insensitive(true).build().ok()
})
});
let filter = if let Some(mapping) = filter.mapping { let filter = if let Some(mapping) = filter.mapping {
diff::display::SymbolFilter::Mapping(mapping as usize, regex.as_ref()) diff::display::SymbolFilter::Mapping(mapping as usize, regex.as_ref())
} else if let Some(regex) = &regex { } else if let Some(regex) = &regex {
@@ -320,6 +332,7 @@ impl From<obj::SymbolFlagSet> for SymbolFlags {
obj::SymbolFlag::Hidden => SymbolFlags::HIDDEN, obj::SymbolFlag::Hidden => SymbolFlags::HIDDEN,
obj::SymbolFlag::HasExtra => SymbolFlags::HAS_EXTRA, obj::SymbolFlag::HasExtra => SymbolFlags::HAS_EXTRA,
obj::SymbolFlag::SizeInferred => SymbolFlags::SIZE_INFERRED, obj::SymbolFlag::SizeInferred => SymbolFlags::SIZE_INFERRED,
obj::SymbolFlag::Ignored => SymbolFlags::IGNORED,
}; };
} }
out out

View File

@@ -89,6 +89,7 @@ interface display {
hidden, hidden,
has-extra, has-extra,
size-inferred, size-inferred,
ignored,
} }
record symbol-display { record symbol-display {