mirror of
https://github.com/encounter/objdiff.git
synced 2025-12-16 16:37:06 +00:00
Compare commits
107 Commits
v3.0.0-alp
...
v3.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
| a015971c20 | |||
| e67d5998b3 | |||
| 91bc23edfc | |||
| c9c3b32376 | |||
| 0dc123b064 | |||
| 1e62d4664c | |||
| 1205e8ceb4 | |||
| c917cad5f0 | |||
| dd653329f5 | |||
| f5d3d5f10a | |||
| c327ed3ea8 | |||
|
|
85fb18a21a | ||
| 00ad0d8094 | |||
| 8fac63c42c | |||
| 0fb7f3901c | |||
|
|
3385f58341 | ||
| 60b227f45e | |||
|
|
127ae5ae44 | ||
| 5f48e69775 | |||
| 8756eee07b | |||
| bd3ed0d5ad | |||
|
|
e638d0b17a | ||
|
|
f58616b6dd | ||
|
|
e9762e24c2 | ||
| dab79d96a1 | |||
| a57e5db983 | |||
|
|
d0afd3b83e | ||
|
|
a367af612b | ||
|
|
22052ea10b | ||
| f7c3501eae | |||
| 07ef93f16a | |||
| 8e8ab6bef8 | |||
| e865f3d598 | |||
| 2b13e9886a | |||
| 1750af736a | |||
|
|
731b604c24 | ||
| 2d643eb071 | |||
| 0c48d711c7 | |||
| 34220a8e26 | |||
| 0c9e5526d4 | |||
| 3db0727469 | |||
| 8b5bf21f38 | |||
| b77df77000 | |||
| 7e08f9715b | |||
| 3c05852d00 | |||
| a51ff44be1 | |||
| d225cac205 | |||
| 737b3782db | |||
|
|
1d782243e0 | ||
| a1499f475d | |||
|
|
f263e490e3 | ||
|
|
d0e6c5c057 | ||
|
|
e1c51ac297 | ||
| 39b1b49985 | |||
| 6c7160ab7e | |||
|
|
644d4762f0 | ||
|
|
b40fae5140 | ||
|
|
fbf85632ab | ||
| 73a89d2768 | |||
| a162c2f840 | |||
| a474b27d55 | |||
| 3d7f2b70dc | |||
| fe886f862d | |||
| 2bcbc34850 | |||
|
|
9b557e4c8e | ||
|
|
b9ba5796ed | ||
|
|
e101610416 | ||
|
|
196c003a92 | ||
| 7b00a9e9f2 | |||
| 311de887ec | |||
| 485b259c32 | |||
| d8fdfaa2c0 | |||
| 2612cda1fb | |||
| bc46e17824 | |||
| e735adbd3d | |||
| 6768df9d80 | |||
| eaba2391e0 | |||
| bd8f5ede23 | |||
| acf46c6b54 | |||
| 9358d8ec60 | |||
| 809e2ffddc | |||
| 87fa29e8b0 | |||
| 42d4c38079 | |||
| fa26200ed7 | |||
| a0e7f9bc37 | |||
| 5898d7aebf | |||
| ffb38d1bb0 | |||
| d56dda72f0 | |||
| c6971f3f2d | |||
| 3965a035fa | |||
| f1fc29f77e | |||
| 7c4f1c5d13 | |||
| fa4a6cadbb | |||
| 799971d54e | |||
| 8eef37e8df | |||
| 5f36916087 | |||
| ee667a2dde | |||
|
|
cf5fc54cfa | ||
| 1cdfa1e857 | |||
| 3f157f33a5 | |||
|
|
a1ea2919f8 | ||
| 9c31c82a37 | |||
| 4f34dfa194 | |||
| ae6d37a10b | |||
| 06e54f3456 | |||
| 6ed07bfaf1 | |||
| 80e939653a |
@@ -1,5 +1,4 @@
|
||||
[target.x86_64-pc-windows-msvc]
|
||||
linker = "rust-lld"
|
||||
|
||||
[target.aarch64-pc-windows-msvc]
|
||||
linker = "rust-lld"
|
||||
# statically link the C runtime so the executable does not depend on
|
||||
# that shared/dynamic library.
|
||||
[target.'cfg(all(target_env = "msvc", target_os = "windows"))']
|
||||
rustflags = ["-C", "target-feature=+crt-static"]
|
||||
|
||||
2
.editorconfig
Normal file
2
.editorconfig
Normal file
@@ -0,0 +1,2 @@
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
125
.github/workflows/build.yaml
vendored
125
.github/workflows/build.yaml
vendored
@@ -4,10 +4,14 @@ on:
|
||||
pull_request:
|
||||
push:
|
||||
paths-ignore:
|
||||
- '*.md'
|
||||
- 'LICENSE*'
|
||||
- "*.md"
|
||||
- "LICENSE*"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
# For npm publish provenance
|
||||
id-token: write
|
||||
|
||||
env:
|
||||
BUILD_PROFILE: release-lto
|
||||
CARGO_INCREMENTAL: 0
|
||||
@@ -32,9 +36,9 @@ jobs:
|
||||
- name: Cache Rust workspace
|
||||
uses: Swatinem/rust-cache@v2
|
||||
- name: Cargo check
|
||||
run: cargo check --features all
|
||||
run: cargo check --all-targets --all-features
|
||||
- name: Cargo clippy
|
||||
run: cargo clippy --features all
|
||||
run: cargo clippy --all-targets --all-features
|
||||
|
||||
fmt:
|
||||
name: Format
|
||||
@@ -148,7 +152,7 @@ jobs:
|
||||
python3 -m venv .venv
|
||||
. .venv/bin/activate
|
||||
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
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -159,7 +163,7 @@ jobs:
|
||||
key: ${{ matrix.target }}
|
||||
- name: Cargo build
|
||||
run: >
|
||||
cargo ${{ matrix.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 }}
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
@@ -178,21 +182,26 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- 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
|
||||
packages: libgtk-3-dev
|
||||
build: zigbuild
|
||||
features: default
|
||||
- platform: windows-latest
|
||||
target: x86_64-pc-windows-msvc
|
||||
name: windows-x86_64
|
||||
build: build
|
||||
features: default
|
||||
- platform: macos-latest
|
||||
target: x86_64-apple-darwin
|
||||
name: macos-x86_64
|
||||
build: build
|
||||
features: default
|
||||
- platform: macos-latest
|
||||
target: aarch64-apple-darwin
|
||||
name: macos-arm64
|
||||
build: build
|
||||
features: default
|
||||
fail-fast: false
|
||||
runs-on: ${{ matrix.platform }}
|
||||
@@ -204,25 +213,32 @@ jobs:
|
||||
sudo apt-get -y install ${{ matrix.packages }}
|
||||
- name: Checkout
|
||||
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
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
targets: ${{ matrix.target }}
|
||||
targets: ${{ matrix.target_base || matrix.target }}
|
||||
- name: Cache Rust workspace
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
key: ${{ matrix.target }}
|
||||
- name: Cargo build
|
||||
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 }}
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ env.CARGO_BIN_NAME }}-${{ matrix.name }}
|
||||
path: |
|
||||
target/${{ 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 }}
|
||||
target/${{ matrix.target_base || matrix.target }}/${{ env.BUILD_PROFILE }}/${{ env.CARGO_BIN_NAME }}.exe
|
||||
if-no-files-found: error
|
||||
|
||||
build-wasm:
|
||||
@@ -237,22 +253,30 @@ jobs:
|
||||
components: rust-src
|
||||
- name: Cache Rust workspace
|
||||
uses: Swatinem/rust-cache@v2
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: lts/*
|
||||
- name: Install dependencies
|
||||
run: npm -C objdiff-wasm install
|
||||
run: npm -C objdiff-wasm ci
|
||||
- name: Build
|
||||
run: npm -C objdiff-wasm run build
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: wasm
|
||||
path: objdiff-wasm/dist/
|
||||
if-no-files-found: error
|
||||
|
||||
release:
|
||||
name: Release
|
||||
check-version:
|
||||
name: Check package versions
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
runs-on: ubuntu-latest
|
||||
needs: [ build-cli, build-gui ]
|
||||
permissions:
|
||||
contents: write
|
||||
needs: [ build-cli, build-gui, build-wasm ]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Check git tag against Cargo version
|
||||
- name: Check git tag against package versions
|
||||
shell: bash
|
||||
run: |
|
||||
set -eou pipefail
|
||||
@@ -264,9 +288,24 @@ jobs:
|
||||
echo "::error::Git tag doesn't match the Cargo version! ($tag != $version)"
|
||||
exit 1
|
||||
fi
|
||||
version="v$(jq -r .version objdiff-wasm/package.json)"
|
||||
if [ "$tag" != "$version" ]; then
|
||||
echo "::error::Git tag doesn't match the npm version! ($tag != $version)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
release-github:
|
||||
name: Release (GitHub)
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
runs-on: ubuntu-latest
|
||||
needs: [ check-version ]
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
pattern: objdiff-*
|
||||
path: artifacts
|
||||
- name: Rename artifacts
|
||||
working-directory: artifacts
|
||||
@@ -296,3 +335,53 @@ jobs:
|
||||
files: out/*
|
||||
draft: true
|
||||
generate_release_notes: true
|
||||
|
||||
release-cargo:
|
||||
name: Release (Cargo)
|
||||
if: 'false' # TODO re-enable when all dependencies are published
|
||||
runs-on: ubuntu-latest
|
||||
needs: [ check-version ]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
- name: Publish
|
||||
env:
|
||||
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
|
||||
run: cargo publish -p objdiff-core
|
||||
|
||||
release-npm:
|
||||
name: Release (npm)
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
runs-on: ubuntu-latest
|
||||
needs: [ check-version ]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: lts/*
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: wasm
|
||||
path: objdiff-wasm/dist
|
||||
- name: Publish
|
||||
working-directory: objdiff-wasm
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
version=$(jq -r '.version' package.json)
|
||||
tag="latest"
|
||||
# Check for prerelease by looking for a dash
|
||||
case "$version" in
|
||||
*-*)
|
||||
tag=$(echo "$version" | sed -e 's/^[^-]*-//' -e 's/\..*$//')
|
||||
;;
|
||||
esac
|
||||
echo "Publishing version $version with tag '$tag'..."
|
||||
npm publish --provenance --access public --tag "$tag"
|
||||
|
||||
36
.pre-commit-config.yaml
Normal file
36
.pre-commit-config.yaml
Normal file
@@ -0,0 +1,36 @@
|
||||
# See https://pre-commit.com for more information
|
||||
# See https://pre-commit.com/hooks.html for more hooks
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v5.0.0
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
args: [--markdown-linebreak-ext=md]
|
||||
- id: end-of-file-fixer
|
||||
- id: fix-byte-order-marker
|
||||
- id: check-yaml
|
||||
- id: check-added-large-files
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: cargo-fmt
|
||||
name: cargo fmt
|
||||
description: Run cargo fmt on all project files.
|
||||
language: system
|
||||
entry: cargo
|
||||
args: ["+nightly", "fmt", "--all"]
|
||||
pass_filenames: false
|
||||
- id: cargo clippy
|
||||
name: cargo clippy
|
||||
description: Run cargo clippy on all project files.
|
||||
language: system
|
||||
entry: cargo
|
||||
args: ["+nightly", "clippy", "--all-targets", "--all-features"]
|
||||
pass_filenames: false
|
||||
- id: cargo-deny
|
||||
name: cargo deny
|
||||
description: Run cargo deny on all project files.
|
||||
language: system
|
||||
entry: cargo
|
||||
args: ["deny", "check"]
|
||||
pass_filenames: false
|
||||
always_run: true
|
||||
2163
Cargo.lock
generated
2163
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,7 @@ members = [
|
||||
"objdiff-gui",
|
||||
"objdiff-wasm",
|
||||
]
|
||||
resolver = "2"
|
||||
resolver = "3"
|
||||
|
||||
[profile.release-lto]
|
||||
inherits = "release"
|
||||
@@ -14,9 +14,9 @@ strip = "debuginfo"
|
||||
codegen-units = 1
|
||||
|
||||
[workspace.package]
|
||||
version = "3.0.0-alpha.1"
|
||||
version = "3.0.0-beta.13"
|
||||
authors = ["Luke Street <luke@street.dev>"]
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/encounter/objdiff"
|
||||
rust-version = "1.82"
|
||||
rust-version = "1.88"
|
||||
|
||||
32
README.md
32
README.md
@@ -16,11 +16,12 @@ Features:
|
||||
|
||||
Supports:
|
||||
|
||||
- PowerPC 750CL (GameCube, Wii)
|
||||
- MIPS (N64, PS1, PS2, PSP)
|
||||
- x86 (COFF only at the moment)
|
||||
- ARM (GBA, DS, 3DS)
|
||||
- ARM64 (Switch, experimental)
|
||||
- ARM64 (Switch)
|
||||
- MIPS (N64, PS1, PS2, PSP)
|
||||
- PowerPC (GameCube, Wii, PS3, Xbox 360)
|
||||
- SuperH (Saturn, Dreamcast)
|
||||
- x86, x86_64 (PC)
|
||||
|
||||
See [Usage](#usage) for more information.
|
||||
|
||||
@@ -143,15 +144,15 @@ If not specified, objdiff will use the default patterns listed above.
|
||||
`units` _(optional)_: If specified, objdiff will display a list of objects in the sidebar for easy navigation.
|
||||
|
||||
> `name` _(optional)_: The name of the object in the UI. If not specified, the object's `path` will be used.
|
||||
>
|
||||
>
|
||||
> `target_path`: Path to the "target" or "expected" object from the project root.
|
||||
> This object is the **intended result** of the match.
|
||||
>
|
||||
>
|
||||
> `base_path`: Path to the "base" or "actual" object from the project root.
|
||||
> This object is built from the **current source code**.
|
||||
>
|
||||
>
|
||||
> `metadata.auto_generated` _(optional)_: Hides the object from the object list, but still includes it in reports.
|
||||
>
|
||||
>
|
||||
> `metadata.complete` _(optional)_: Marks the object as "complete" (or "linked") in the object list.
|
||||
> This is useful for marking objects that are fully decompiled. A value of `false` will mark the object as "incomplete".
|
||||
|
||||
@@ -173,6 +174,21 @@ $ cargo install --locked --git https://github.com/encounter/objdiff.git objdiff-
|
||||
|
||||
The binaries will be installed to `~/.cargo/bin` as `objdiff` and `objdiff-cli`.
|
||||
|
||||
## Installing `pre-commit`
|
||||
|
||||
When contributing, it's recommended to install `pre-commit` to automatically run the linter and formatter before a commit.
|
||||
|
||||
[`uv`](https://github.com/astral-sh/uv#installation) is recommended to manage Python version and tools.
|
||||
|
||||
Rust nightly is required for `cargo +nightly fmt` and `cargo +nightly clippy`.
|
||||
|
||||
```shell
|
||||
$ cargo install --locked cargo-deny
|
||||
$ rustup toolchain install nightly
|
||||
$ uv tool install pre-commit
|
||||
$ pre-commit install
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
Licensed under either of
|
||||
|
||||
@@ -175,6 +175,10 @@
|
||||
"type": "boolean",
|
||||
"description": "If true, objdiff will run the build command with the context file as an argument to generate it.",
|
||||
"default": false
|
||||
},
|
||||
"preset_id": {
|
||||
"type": "number",
|
||||
"description": "The decomp.me preset ID to use for the scratch.\nCompiler and flags in the config will take precedence over the preset, but the preset is useful for organizational purposes."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
||||
@@ -73,6 +73,7 @@ ignore = [
|
||||
#{ 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
|
||||
#{ 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 false, then it uses a built-in git library.
|
||||
@@ -102,6 +103,7 @@ allow = [
|
||||
"0BSD",
|
||||
"OFL-1.1",
|
||||
"Ubuntu-font-1.0",
|
||||
"CDLA-Permissive-2.0",
|
||||
]
|
||||
# The confidence threshold for detecting a license from license text.
|
||||
# The higher the value, the more closely the license text must be to the
|
||||
@@ -239,8 +241,6 @@ allow-git = []
|
||||
[sources.allow-org]
|
||||
# github.com organizations to allow git sources for
|
||||
github = [
|
||||
"CelestialAmber", # cwextab, rlwinmdec
|
||||
"Decompollaborate", # rabbitizer
|
||||
"enarx", # flagset
|
||||
"encounter",
|
||||
]
|
||||
|
||||
@@ -15,11 +15,11 @@ publish = false
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
argp = "0.4"
|
||||
crossterm = "0.28"
|
||||
crossterm = "0.29"
|
||||
enable-ansi-support = "0.2"
|
||||
memmap2 = "0.9"
|
||||
objdiff-core = { path = "../objdiff-core", features = ["all"] }
|
||||
prost = "0.13"
|
||||
prost = "0.14"
|
||||
ratatui = "0.29"
|
||||
rayon = "1.10"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
@@ -28,7 +28,7 @@ supports-color = "3.0"
|
||||
time = { version = "0.3", features = ["formatting", "local-offset"] }
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
typed-path = "0.10"
|
||||
typed-path = "0.11"
|
||||
|
||||
[target.'cfg(target_env = "musl")'.dependencies]
|
||||
mimalloc = "0.1"
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
//! For now, this only adds a --version/-V option which causes early-exit.
|
||||
use std::ffi::OsStr;
|
||||
|
||||
use argp::{parser::ParseGlobalOptions, EarlyExit, FromArgs, TopLevelCommand};
|
||||
use argp::{EarlyExit, FromArgs, TopLevelCommand, parser::ParseGlobalOptions};
|
||||
|
||||
struct ArgsOrVersion<T>(T)
|
||||
where T: FromArgs;
|
||||
|
||||
@@ -1,42 +1,37 @@
|
||||
use std::{
|
||||
io::stdout,
|
||||
mem,
|
||||
str::FromStr,
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
atomic::{AtomicBool, Ordering},
|
||||
},
|
||||
task::{Wake, Waker},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, bail, Context, Result};
|
||||
use anyhow::{Context, Result, anyhow, bail};
|
||||
use argp::FromArgs;
|
||||
use crossterm::{
|
||||
event,
|
||||
event::{DisableMouseCapture, EnableMouseCapture},
|
||||
terminal::{
|
||||
disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen, SetTitle,
|
||||
EnterAlternateScreen, LeaveAlternateScreen, SetTitle, disable_raw_mode, enable_raw_mode,
|
||||
},
|
||||
};
|
||||
use objdiff_core::{
|
||||
bindings::diff::DiffResult,
|
||||
build::{
|
||||
watcher::{create_watcher, Watcher},
|
||||
BuildConfig,
|
||||
BuildConfig, BuildStatus,
|
||||
watcher::{Watcher, create_watcher},
|
||||
},
|
||||
config::{
|
||||
build_globset,
|
||||
ProjectConfig, ProjectObject, ProjectObjectMetadata, build_globset,
|
||||
path::{check_path_buf, platform_path, platform_path_serde_option},
|
||||
ProjectConfig, ProjectObject, ProjectObjectMetadata,
|
||||
},
|
||||
diff::{
|
||||
self, ConfigEnum, ConfigPropertyId, ConfigPropertyKind, DiffObjConfig, MappingConfig,
|
||||
ObjectDiff,
|
||||
},
|
||||
diff::{self, DiffObjConfig, MappingConfig, ObjectDiff},
|
||||
jobs::{
|
||||
objdiff::{start_build, ObjDiffConfig},
|
||||
Job, JobQueue, JobResult,
|
||||
objdiff::{ObjDiffConfig, start_build},
|
||||
},
|
||||
obj::{self, Object},
|
||||
};
|
||||
@@ -44,11 +39,12 @@ use ratatui::prelude::*;
|
||||
use typed_path::{Utf8PlatformPath, Utf8PlatformPathBuf};
|
||||
|
||||
use crate::{
|
||||
cmd::apply_config_args,
|
||||
util::{
|
||||
output::{write_output, OutputFormat},
|
||||
output::{OutputFormat, write_output},
|
||||
term::crossterm_panic_handler,
|
||||
},
|
||||
views::{function_diff::FunctionDiffUi, EventControlFlow, EventResult, UiView},
|
||||
views::{EventControlFlow, EventResult, UiView, function_diff::FunctionDiffUi},
|
||||
};
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
@@ -184,28 +180,7 @@ pub fn run(args: Args) -> Result<()> {
|
||||
|
||||
fn build_config_from_args(args: &Args) -> Result<(DiffObjConfig, MappingConfig)> {
|
||||
let mut diff_config = DiffObjConfig::default();
|
||||
for config in &args.config {
|
||||
let (key, value) = config.split_once('=').context("--config expects \"key=value\"")?;
|
||||
let property_id = ConfigPropertyId::from_str(key)
|
||||
.map_err(|()| anyhow!("Invalid configuration property: {}", key))?;
|
||||
diff_config.set_property_value_str(property_id, value).map_err(|()| {
|
||||
let mut options = String::new();
|
||||
match property_id.kind() {
|
||||
ConfigPropertyKind::Boolean => {
|
||||
options = "true, false".to_string();
|
||||
}
|
||||
ConfigPropertyKind::Choice(variants) => {
|
||||
for (i, variant) in variants.iter().enumerate() {
|
||||
if i > 0 {
|
||||
options.push_str(", ");
|
||||
}
|
||||
options.push_str(variant.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
anyhow!("Invalid value for {}. Expected one of: {}", property_id.name(), options)
|
||||
})?;
|
||||
}
|
||||
apply_config_args(&mut diff_config, &args.config)?;
|
||||
let mut mapping_config = MappingConfig {
|
||||
mappings: Default::default(),
|
||||
selecting_left: args.selecting_left.clone(),
|
||||
@@ -228,14 +203,10 @@ fn run_oneshot(
|
||||
let output_format = OutputFormat::from_option(args.format.as_deref())?;
|
||||
let (diff_config, mapping_config) = build_config_from_args(args)?;
|
||||
let target = target_path
|
||||
.map(|p| {
|
||||
obj::read::read(p.as_ref(), &diff_config).with_context(|| format!("Loading {}", p))
|
||||
})
|
||||
.map(|p| obj::read::read(p.as_ref(), &diff_config).with_context(|| format!("Loading {p}")))
|
||||
.transpose()?;
|
||||
let base = base_path
|
||||
.map(|p| {
|
||||
obj::read::read(p.as_ref(), &diff_config).with_context(|| format!("Loading {}", p))
|
||||
})
|
||||
.map(|p| obj::read::read(p.as_ref(), &diff_config).with_context(|| format!("Loading {p}")))
|
||||
.transpose()?;
|
||||
let result =
|
||||
diff::diff_objs(target.as_ref(), base.as_ref(), None, &diff_config, &mapping_config)?;
|
||||
@@ -252,6 +223,8 @@ pub struct AppState {
|
||||
pub project_config: Option<ProjectConfig>,
|
||||
pub target_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 right_obj: Option<(Object, ObjectDiff)>,
|
||||
pub prev_obj: Option<(Object, ObjectDiff)>,
|
||||
@@ -349,6 +322,8 @@ impl AppState {
|
||||
JobResult::None => unreachable!("Unexpected JobResult::None"),
|
||||
JobResult::ObjDiff(result) => {
|
||||
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.right_obj = result.second_obj;
|
||||
self.reload_time = Some(result.time);
|
||||
@@ -389,6 +364,8 @@ fn run_interactive(
|
||||
project_config,
|
||||
target_path,
|
||||
base_path,
|
||||
left_status: None,
|
||||
right_status: None,
|
||||
left_obj: None,
|
||||
right_obj: None,
|
||||
prev_obj: None,
|
||||
@@ -418,7 +395,7 @@ fn run_interactive(
|
||||
stdout(),
|
||||
EnterAlternateScreen,
|
||||
EnableMouseCapture,
|
||||
SetTitle(format!("{} - objdiff", symbol_name)),
|
||||
SetTitle(format!("{symbol_name} - objdiff")),
|
||||
)?;
|
||||
let backend = CrosstermBackend::new(stdout());
|
||||
let mut terminal = Terminal::new(backend)?;
|
||||
@@ -426,15 +403,17 @@ fn run_interactive(
|
||||
let mut result = EventResult { redraw: true, ..Default::default() };
|
||||
'outer: loop {
|
||||
if result.redraw {
|
||||
terminal.draw(|f| loop {
|
||||
result.redraw = false;
|
||||
view.draw(&state, f, &mut result);
|
||||
result.click_xy = None;
|
||||
if !result.redraw {
|
||||
break;
|
||||
terminal.draw(|f| {
|
||||
loop {
|
||||
result.redraw = false;
|
||||
view.draw(&state, f, &mut result);
|
||||
result.click_xy = None;
|
||||
if !result.redraw {
|
||||
break;
|
||||
}
|
||||
// Clear buffer on redraw
|
||||
f.buffer_mut().reset();
|
||||
}
|
||||
// Clear buffer on redraw
|
||||
f.buffer_mut().reset();
|
||||
})?;
|
||||
}
|
||||
loop {
|
||||
|
||||
@@ -1,2 +1,33 @@
|
||||
pub mod diff;
|
||||
pub mod report;
|
||||
|
||||
use std::str::FromStr;
|
||||
|
||||
use anyhow::{Context, Result, anyhow};
|
||||
use objdiff_core::diff::{ConfigEnum, ConfigPropertyId, ConfigPropertyKind, DiffObjConfig};
|
||||
|
||||
pub fn apply_config_args(diff_config: &mut DiffObjConfig, args: &[String]) -> Result<()> {
|
||||
for config in args {
|
||||
let (key, value) = config.split_once('=').context("--config expects \"key=value\"")?;
|
||||
let property_id = ConfigPropertyId::from_str(key)
|
||||
.map_err(|()| anyhow!("Invalid configuration property: {}", key))?;
|
||||
diff_config.set_property_value_str(property_id, value).map_err(|()| {
|
||||
let mut options = String::new();
|
||||
match property_id.kind() {
|
||||
ConfigPropertyKind::Boolean => {
|
||||
options = "true, false".to_string();
|
||||
}
|
||||
ConfigPropertyKind::Choice(variants) => {
|
||||
for (i, variant) in variants.iter().enumerate() {
|
||||
if i > 0 {
|
||||
options.push_str(", ");
|
||||
}
|
||||
options.push_str(variant.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
anyhow!("Invalid value for {}. Expected one of: {}", property_id.name(), options)
|
||||
})?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
use std::{collections::HashSet, fs::File, io::Read, time::Instant};
|
||||
|
||||
use anyhow::{bail, Context, Result};
|
||||
use anyhow::{Context, Result, bail};
|
||||
use argp::FromArgs;
|
||||
use objdiff_core::{
|
||||
bindings::report::{
|
||||
ChangeItem, ChangeItemInfo, ChangeUnit, Changes, ChangesInput, Measures, Report,
|
||||
ReportCategory, ReportItem, ReportItemMetadata, ReportUnit, ReportUnitMetadata,
|
||||
REPORT_VERSION,
|
||||
ChangeItem, ChangeItemInfo, ChangeUnit, Changes, ChangesInput, Measures, REPORT_VERSION,
|
||||
Report, ReportCategory, ReportItem, ReportItemMetadata, ReportUnit, ReportUnitMetadata,
|
||||
},
|
||||
config::path::platform_path,
|
||||
diff, obj,
|
||||
@@ -18,8 +17,8 @@ use tracing::{info, warn};
|
||||
use typed_path::{Utf8PlatformPath, Utf8PlatformPathBuf};
|
||||
|
||||
use crate::{
|
||||
cmd::diff::ObjectConfig,
|
||||
util::output::{write_output, OutputFormat},
|
||||
cmd::{apply_config_args, diff::ObjectConfig},
|
||||
util::output::{OutputFormat, write_output},
|
||||
};
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
@@ -53,6 +52,9 @@ pub struct GenerateArgs {
|
||||
#[argp(option, short = 'f')]
|
||||
/// Output format (json, json-pretty, proto) (default: json)
|
||||
format: Option<String>,
|
||||
#[argp(option, short = 'c')]
|
||||
/// Configuration property (key=value)
|
||||
config: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
@@ -81,6 +83,15 @@ pub fn run(args: Args) -> Result<()> {
|
||||
}
|
||||
|
||||
fn generate(args: GenerateArgs) -> Result<()> {
|
||||
let mut diff_config = diff::DiffObjConfig {
|
||||
function_reloc_diffs: diff::FunctionRelocDiffs::None,
|
||||
combine_data_sections: true,
|
||||
combine_text_sections: true,
|
||||
ppc_calculate_pool_relocations: false,
|
||||
..Default::default()
|
||||
};
|
||||
apply_config_args(&mut diff_config, &args.config)?;
|
||||
|
||||
let output_format = OutputFormat::from_option(args.format.as_deref())?;
|
||||
let project_dir = args.project.as_deref().unwrap_or_else(|| Utf8PlatformPath::new("."));
|
||||
info!("Loading project {}", project_dir);
|
||||
@@ -115,14 +126,15 @@ fn generate(args: GenerateArgs) -> Result<()> {
|
||||
if args.deduplicate {
|
||||
// If deduplicating, we need to run single-threaded
|
||||
for object in &objects {
|
||||
if let Some(unit) = report_object(object, Some(&mut existing_functions))? {
|
||||
if let Some(unit) = report_object(object, &diff_config, Some(&mut existing_functions))?
|
||||
{
|
||||
units.push(unit);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let vec = objects
|
||||
.par_iter()
|
||||
.map(|object| report_object(object, None))
|
||||
.map(|object| report_object(object, &diff_config, None))
|
||||
.collect::<Result<Vec<Option<ReportUnit>>>>()?;
|
||||
units = vec.into_iter().flatten().collect();
|
||||
}
|
||||
@@ -146,6 +158,7 @@ fn generate(args: GenerateArgs) -> Result<()> {
|
||||
|
||||
fn report_object(
|
||||
object: &ObjectConfig,
|
||||
diff_config: &diff::DiffObjConfig,
|
||||
mut existing_functions: Option<&mut HashSet<String>>,
|
||||
) -> Result<Option<ReportUnit>> {
|
||||
match (&object.target_path, &object.base_path) {
|
||||
@@ -159,29 +172,23 @@ fn report_object(
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
let diff_config = diff::DiffObjConfig {
|
||||
function_reloc_diffs: diff::FunctionRelocDiffs::None,
|
||||
..Default::default()
|
||||
};
|
||||
let mapping_config = diff::MappingConfig::default();
|
||||
let target = object
|
||||
.target_path
|
||||
.as_ref()
|
||||
.map(|p| {
|
||||
obj::read::read(p.as_ref(), &diff_config)
|
||||
.with_context(|| format!("Failed to open {}", p))
|
||||
obj::read::read(p.as_ref(), diff_config).with_context(|| format!("Failed to open {p}"))
|
||||
})
|
||||
.transpose()?;
|
||||
let base = object
|
||||
.base_path
|
||||
.as_ref()
|
||||
.map(|p| {
|
||||
obj::read::read(p.as_ref(), &diff_config)
|
||||
.with_context(|| format!("Failed to open {}", p))
|
||||
obj::read::read(p.as_ref(), diff_config).with_context(|| format!("Failed to open {p}"))
|
||||
})
|
||||
.transpose()?;
|
||||
let result =
|
||||
diff::diff_objs(target.as_ref(), base.as_ref(), None, &diff_config, &mapping_config)?;
|
||||
diff::diff_objs(target.as_ref(), base.as_ref(), None, diff_config, &mapping_config)?;
|
||||
|
||||
let metadata = ReportUnitMetadata {
|
||||
complete: object.metadata.complete,
|
||||
@@ -203,14 +210,13 @@ fn report_object(
|
||||
for ((section_idx, section), section_diff) in
|
||||
obj.sections.iter().enumerate().zip(&obj_diff.sections)
|
||||
{
|
||||
if section.kind == SectionKind::Unknown {
|
||||
continue;
|
||||
}
|
||||
let section_match_percent = section_diff.match_percent.unwrap_or_else(|| {
|
||||
// Support cases where we don't have a target object,
|
||||
// assume complete means 100% match
|
||||
if object.complete.unwrap_or(false) {
|
||||
100.0
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
if object.complete.unwrap_or(false) { 100.0 } else { 0.0 }
|
||||
});
|
||||
sections.push(ReportItem {
|
||||
name: section.name.clone(),
|
||||
@@ -220,6 +226,7 @@ fn report_object(
|
||||
demangled_name: None,
|
||||
virtual_address: section.virtual_address,
|
||||
}),
|
||||
address: None,
|
||||
});
|
||||
|
||||
match section.kind {
|
||||
@@ -237,25 +244,21 @@ fn report_object(
|
||||
if symbol.section != Some(section_idx)
|
||||
|| symbol.size == 0
|
||||
|| symbol.flags.contains(SymbolFlag::Hidden)
|
||||
|| symbol.flags.contains(SymbolFlag::Ignored)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if let Some(existing_functions) = &mut existing_functions {
|
||||
if (symbol.flags.contains(SymbolFlag::Global)
|
||||
if let Some(existing_functions) = &mut existing_functions
|
||||
&& (symbol.flags.contains(SymbolFlag::Global)
|
||||
|| symbol.flags.contains(SymbolFlag::Weak))
|
||||
&& !existing_functions.insert(symbol.name.clone())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
&& !existing_functions.insert(symbol.name.clone())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
let match_percent = symbol_diff.match_percent.unwrap_or_else(|| {
|
||||
// Support cases where we don't have a target object,
|
||||
// assume complete means 100% match
|
||||
if object.complete.unwrap_or(false) {
|
||||
100.0
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
if object.complete.unwrap_or(false) { 100.0 } else { 0.0 }
|
||||
});
|
||||
measures.fuzzy_match_percent += match_percent * symbol.size as f32;
|
||||
measures.total_code += symbol.size;
|
||||
@@ -270,6 +273,7 @@ fn report_object(
|
||||
demangled_name: symbol.demangled_name.clone(),
|
||||
virtual_address: symbol.virtual_address,
|
||||
}),
|
||||
address: symbol.address.checked_sub(section.address),
|
||||
});
|
||||
if match_percent == 100.0 {
|
||||
measures.matched_functions += 1;
|
||||
@@ -277,6 +281,16 @@ fn report_object(
|
||||
measures.total_functions += 1;
|
||||
}
|
||||
}
|
||||
sections.sort_by(|a, b| a.name.cmp(&b.name));
|
||||
let reverse_fn_order = object.metadata.reverse_fn_order.unwrap_or(false);
|
||||
functions.sort_by(|a, b| {
|
||||
if reverse_fn_order {
|
||||
b.address.unwrap_or(0).cmp(&a.address.unwrap_or(0))
|
||||
} else {
|
||||
a.address.unwrap_or(u64::MAX).cmp(&b.address.unwrap_or(u64::MAX))
|
||||
}
|
||||
.then_with(|| a.size.cmp(&b.size))
|
||||
});
|
||||
if metadata.complete.unwrap_or(false) {
|
||||
measures.complete_code = measures.total_code;
|
||||
measures.complete_data = measures.total_data;
|
||||
@@ -416,8 +430,8 @@ fn read_report(path: &Utf8PlatformPath) -> Result<Report> {
|
||||
std::io::stdin().read_to_end(&mut data)?;
|
||||
return Report::parse(&data).with_context(|| "Failed to load report from stdin");
|
||||
}
|
||||
let file = File::open(path).with_context(|| format!("Failed to open {}", path))?;
|
||||
let file = File::open(path).with_context(|| format!("Failed to open {path}"))?;
|
||||
let mmap =
|
||||
unsafe { memmap2::Mmap::map(&file) }.with_context(|| format!("Failed to map {}", path))?;
|
||||
Report::parse(mmap.as_ref()).with_context(|| format!("Failed to load report {}", path))
|
||||
unsafe { memmap2::Mmap::map(&file) }.with_context(|| format!("Failed to map {path}"))?;
|
||||
Report::parse(mmap.as_ref()).with_context(|| format!("Failed to load report {path}"))
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ use anyhow::{Error, Result};
|
||||
use argp::{FromArgValue, FromArgs};
|
||||
use enable_ansi_support::enable_ansi_support;
|
||||
use supports_color::Stream;
|
||||
use tracing_subscriber::{filter::LevelFilter, EnvFilter};
|
||||
use tracing_subscriber::{EnvFilter, filter::LevelFilter};
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
|
||||
enum LogLevel {
|
||||
|
||||
@@ -5,7 +5,7 @@ use std::{
|
||||
path::Path,
|
||||
};
|
||||
|
||||
use anyhow::{bail, Context, Result};
|
||||
use anyhow::{Context, Result, bail};
|
||||
use tracing::info;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::{io::stdout, panic};
|
||||
use crossterm::{
|
||||
cursor::Show,
|
||||
event::DisableMouseCapture,
|
||||
terminal::{disable_raw_mode, LeaveAlternateScreen},
|
||||
terminal::{LeaveAlternateScreen, disable_raw_mode},
|
||||
};
|
||||
|
||||
pub fn crossterm_panic_handler() {
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
use core::cmp::Ordering;
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use anyhow::Result;
|
||||
use crossterm::event::{Event, KeyCode, KeyEventKind, KeyModifiers, MouseButton, MouseEventKind};
|
||||
use objdiff_core::{
|
||||
build::BuildStatus,
|
||||
diff::{
|
||||
display::{display_row, DiffText, DiffTextColor, HighlightKind},
|
||||
DiffObjConfig, FunctionRelocDiffs, InstructionDiffKind, ObjectDiff, SymbolDiff,
|
||||
display::{DiffText, DiffTextColor, HighlightKind, display_row},
|
||||
},
|
||||
obj::Object,
|
||||
};
|
||||
use ratatui::{
|
||||
Frame,
|
||||
prelude::*,
|
||||
widgets::{Block, Borders, Clear, Paragraph, Scrollbar, ScrollbarOrientation, ScrollbarState},
|
||||
Frame,
|
||||
};
|
||||
|
||||
use super::{EventControlFlow, EventResult, UiView};
|
||||
@@ -86,7 +87,7 @@ impl UiView for FunctionDiffUi {
|
||||
.and_then(|(_, _, d)| d.match_percent)
|
||||
{
|
||||
line_r.spans.push(Span::styled(
|
||||
format!("{:.2}% ", percent),
|
||||
format!("{percent:.2}% "),
|
||||
Style::new().fg(match_percent_color(percent)),
|
||||
));
|
||||
}
|
||||
@@ -96,7 +97,7 @@ impl UiView for FunctionDiffUi {
|
||||
.and_then(|t| t.format(&state.time_format).ok())
|
||||
.unwrap_or_else(|| "N/A".to_string());
|
||||
line_r.spans.push(Span::styled(
|
||||
format!("Last reload: {}", reload_time),
|
||||
format!("Last reload: {reload_time}"),
|
||||
Style::new().fg(Color::White),
|
||||
));
|
||||
line_r.spans.push(Span::styled(
|
||||
@@ -126,6 +127,11 @@ impl UiView for FunctionDiffUi {
|
||||
);
|
||||
max_width = max_width.max(text.width());
|
||||
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;
|
||||
@@ -155,36 +161,40 @@ impl UiView for FunctionDiffUi {
|
||||
let rect = content_chunks[1].inner(Margin::new(1, 1));
|
||||
self.print_margin(&mut text, symbol_diff, rect);
|
||||
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_margin_text = None;
|
||||
if self.three_way {
|
||||
if let Some((obj, symbol_idx, symbol_diff)) =
|
||||
if self.three_way
|
||||
&& let Some((obj, symbol_idx, symbol_diff)) =
|
||||
get_symbol(state.prev_obj.as_ref(), self.prev_sym)
|
||||
{
|
||||
let mut text = Text::default();
|
||||
let rect = content_chunks[4].inner(Margin::new(0, 1));
|
||||
self.print_sym(
|
||||
&mut text,
|
||||
obj,
|
||||
symbol_idx,
|
||||
symbol_diff,
|
||||
&state.diff_obj_config,
|
||||
rect,
|
||||
&self.right_highlight,
|
||||
result,
|
||||
true,
|
||||
);
|
||||
max_width = max_width.max(text.width());
|
||||
prev_text = Some(text);
|
||||
{
|
||||
let mut text = Text::default();
|
||||
let rect = content_chunks[4].inner(Margin::new(0, 1));
|
||||
self.print_sym(
|
||||
&mut text,
|
||||
obj,
|
||||
symbol_idx,
|
||||
symbol_diff,
|
||||
&state.diff_obj_config,
|
||||
rect,
|
||||
&self.right_highlight,
|
||||
result,
|
||||
true,
|
||||
);
|
||||
max_width = max_width.max(text.width());
|
||||
prev_text = Some(text);
|
||||
|
||||
// Render margin
|
||||
let mut text = Text::default();
|
||||
let rect = content_chunks[3].inner(Margin::new(1, 1));
|
||||
self.print_margin(&mut text, symbol_diff, rect);
|
||||
prev_margin_text = Some(text);
|
||||
}
|
||||
// Render margin
|
||||
let mut text = Text::default();
|
||||
let rect = content_chunks[3].inner(Margin::new(1, 1));
|
||||
self.print_margin(&mut text, symbol_diff, rect);
|
||||
prev_margin_text = Some(text);
|
||||
}
|
||||
|
||||
let max_scroll_x =
|
||||
@@ -439,11 +449,11 @@ impl UiView for FunctionDiffUi {
|
||||
|
||||
fn reload(&mut self, state: &AppState) -> Result<()> {
|
||||
let left_sym =
|
||||
state.left_obj.as_ref().and_then(|(o, _)| find_function(o, &self.symbol_name));
|
||||
state.left_obj.as_ref().and_then(|(o, _)| o.symbol_by_name(&self.symbol_name));
|
||||
let right_sym =
|
||||
state.right_obj.as_ref().and_then(|(o, _)| find_function(o, &self.symbol_name));
|
||||
state.right_obj.as_ref().and_then(|(o, _)| o.symbol_by_name(&self.symbol_name));
|
||||
let prev_sym =
|
||||
state.prev_obj.as_ref().and_then(|(o, _)| find_function(o, &self.symbol_name));
|
||||
state.prev_obj.as_ref().and_then(|(o, _)| o.symbol_by_name(&self.symbol_name));
|
||||
self.num_rows = match (
|
||||
get_symbol(state.left_obj.as_ref(), left_sym),
|
||||
get_symbol(state.right_obj.as_ref(), right_sym),
|
||||
@@ -453,7 +463,7 @@ impl UiView for FunctionDiffUi {
|
||||
}
|
||||
(Some((_l, _ls, ld)), None) => ld.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.right_sym = right_sym;
|
||||
@@ -527,7 +537,7 @@ impl FunctionDiffUi {
|
||||
let label_text = match segment.text {
|
||||
DiffText::Basic(text) => text.to_string(),
|
||||
DiffText::Line(num) => format!("{num} "),
|
||||
DiffText::Address(addr) => format!("{:x}:", addr),
|
||||
DiffText::Address(addr) => format!("{addr:x}:"),
|
||||
DiffText::Opcode(mnemonic, _op) => format!("{mnemonic} "),
|
||||
DiffText::Argument(arg) => arg.to_string(),
|
||||
DiffText::BranchDest(addr) => format!("{addr:x}"),
|
||||
@@ -535,7 +545,7 @@ impl FunctionDiffUi {
|
||||
sym.demangled_name.as_ref().unwrap_or(&sym.name).clone()
|
||||
}
|
||||
DiffText::Addend(addend) => match addend.cmp(&0i64) {
|
||||
Ordering::Greater => format!("+{:#x}", addend),
|
||||
Ordering::Greater => format!("+{addend:#x}"),
|
||||
Ordering::Less => format!("-{:#x}", -addend),
|
||||
_ => String::new(),
|
||||
},
|
||||
@@ -550,15 +560,18 @@ impl FunctionDiffUi {
|
||||
let len = label_text.len();
|
||||
let highlighted =
|
||||
highlight_kind != HighlightKind::None && *highlight == highlight_kind;
|
||||
if let Some((cx, cy)) = result.click_xy {
|
||||
if cx >= sx && cx < sx + len as u16 && cy == sy {
|
||||
new_highlight = Some(highlight_kind);
|
||||
}
|
||||
if let Some((cx, cy)) = result.click_xy
|
||||
&& cx >= sx
|
||||
&& cx < sx + len as u16
|
||||
&& cy == sy
|
||||
{
|
||||
new_highlight = Some(highlight_kind);
|
||||
}
|
||||
let mut style = Style::new().fg(match segment.color {
|
||||
DiffTextColor::Normal => Color::Gray,
|
||||
DiffTextColor::Dim => Color::DarkGray,
|
||||
DiffTextColor::Bright => Color::White,
|
||||
DiffTextColor::DataFlow => Color::LightCyan,
|
||||
DiffTextColor::Replace => Color::Cyan,
|
||||
DiffTextColor::Delete => Color::Red,
|
||||
DiffTextColor::Insert => Color::Green,
|
||||
@@ -596,6 +609,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] = [
|
||||
@@ -627,12 +652,3 @@ fn get_symbol(
|
||||
let sym = sym?;
|
||||
Some((obj, sym, &diff.symbols[sym]))
|
||||
}
|
||||
|
||||
fn find_function(obj: &Object, name: &str) -> Option<usize> {
|
||||
for (symbol_idx, symbol) in obj.symbols.iter().enumerate() {
|
||||
if symbol.name == name {
|
||||
return Some(symbol_idx);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ all = [
|
||||
"mips",
|
||||
"ppc",
|
||||
"x86",
|
||||
"superh"
|
||||
]
|
||||
# Implicit, used to check if any arch is enabled
|
||||
any-arch = [
|
||||
@@ -40,6 +41,7 @@ any-arch = [
|
||||
"dep:regex",
|
||||
"dep:similar",
|
||||
"dep:syn",
|
||||
"dep:encoding_rs"
|
||||
]
|
||||
bindings = [
|
||||
"dep:prost",
|
||||
@@ -60,7 +62,10 @@ config = [
|
||||
"dep:semver",
|
||||
"dep:typed-path",
|
||||
]
|
||||
dwarf = ["dep:gimli"]
|
||||
dwarf = [
|
||||
"dep:gimli",
|
||||
"dep:typed-arena",
|
||||
]
|
||||
serde = [
|
||||
"dep:pbjson",
|
||||
"dep:pbjson-build",
|
||||
@@ -75,19 +80,23 @@ std = [
|
||||
"object/std",
|
||||
"prost?/std",
|
||||
"serde?/std",
|
||||
"similar?/std",
|
||||
"typed-arena?/std",
|
||||
"typed-path?/std",
|
||||
"dep:filetime",
|
||||
"dep:memmap2",
|
||||
]
|
||||
mips = [
|
||||
"any-arch",
|
||||
"dep:cpp_demangle",
|
||||
"dep:cwdemangle",
|
||||
"dep:rabbitizer",
|
||||
]
|
||||
ppc = [
|
||||
"any-arch",
|
||||
"dep:cwdemangle",
|
||||
"dep:cwextab",
|
||||
"dep:ppc750cl",
|
||||
"dep:powerpc",
|
||||
"dep:rlwinmdec",
|
||||
]
|
||||
x86 = [
|
||||
@@ -108,6 +117,9 @@ arm64 = [
|
||||
"dep:yaxpeax-arch",
|
||||
"dep:yaxpeax-arm",
|
||||
]
|
||||
superh = [
|
||||
"any-arch",
|
||||
]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["all"]
|
||||
@@ -115,18 +127,18 @@ features = ["all"]
|
||||
[dependencies]
|
||||
anyhow = { version = "1.0", default-features = false }
|
||||
filetime = { version = "0.2", optional = true }
|
||||
flagset = { version = "0.4", default-features = false, optional = true, git = "https://github.com/enarx/flagset.git", rev = "a1fe9369b3741e43fec45da1998e83b9d78966a2" }
|
||||
flagset = { version = "0.4", default-features = false, optional = true }
|
||||
itertools = { version = "0.14", default-features = false, features = ["use_alloc"] }
|
||||
log = { version = "0.4", default-features = false, optional = true }
|
||||
memmap2 = { version = "0.9", optional = true }
|
||||
num-traits = { version = "0.2", default-features = false, optional = true }
|
||||
object = { version = "0.36", default-features = false, features = ["read_core", "elf", "pe"] }
|
||||
pbjson = { version = "0.7", default-features = false, optional = true }
|
||||
prost = { version = "0.13", default-features = false, features = ["prost-derive"], optional = true }
|
||||
object = { git = "https://github.com/gimli-rs/object", rev = "16ff70aa6fbd97d6bb7b92375929f4d72414c32b", default-features = false, features = ["read_core", "elf", "coff"] }
|
||||
pbjson = { version = "0.8", default-features = false, optional = true }
|
||||
prost = { version = "0.14", default-features = false, features = ["derive"], optional = true }
|
||||
regex = { version = "1.11", default-features = false, features = [], optional = true }
|
||||
serde = { version = "1.0", default-features = false, features = ["derive"], optional = true }
|
||||
similar = { version = "2.7", default-features = false, optional = true, git = "https://github.com/encounter/similar.git", branch = "no_std" }
|
||||
typed-path = { version = "0.10", default-features = false, optional = true }
|
||||
similar = { version = "2.7", default-features = false, features = ["hashbrown"], optional = true, git = "https://github.com/encounter/similar.git", branch = "no_std" }
|
||||
typed-path = { version = "0.11", default-features = false, optional = true }
|
||||
|
||||
# config
|
||||
globset = { version = "0.4", default-features = false, optional = true }
|
||||
@@ -134,16 +146,17 @@ semver = { version = "1.0", default-features = false, optional = true }
|
||||
serde_json = { version = "1.0", default-features = false, features = ["alloc"], optional = true }
|
||||
|
||||
# dwarf
|
||||
gimli = { version = "0.31", default-features = false, features = ["read"], optional = true }
|
||||
gimli = { version = "0.32", default-features = false, features = ["read"], optional = true }
|
||||
typed-arena = { version = "2.0", default-features = false, optional = true }
|
||||
|
||||
# ppc
|
||||
cwdemangle = { version = "1.0", optional = true }
|
||||
cwextab = { version = "1.0", optional = true, git = "https://github.com/CelestialAmber/cwextab.git" }
|
||||
ppc750cl = { version = "0.3", optional = true }
|
||||
rlwinmdec = { version = "1.1", optional = true, git = "https://github.com/CelestialAmber/rlwinmdec.git" }
|
||||
cwextab = { version = "1.1", optional = true }
|
||||
powerpc = { version = "0.4", optional = true }
|
||||
rlwinmdec = { version = "1.1", optional = true }
|
||||
|
||||
# mips
|
||||
rabbitizer = { git = "https://github.com/Decompollaborate/rabbitizer.git", branch = "🦀", default-features = false, features = ["all_extensions"], optional = true }
|
||||
rabbitizer = { version = "2.0.0-alpha.4", default-features = false, features = ["all_extensions"], optional = true }
|
||||
|
||||
# x86
|
||||
cpp_demangle = { version = "0.4", default-features = false, features = ["alloc"], optional = true }
|
||||
@@ -151,7 +164,7 @@ iced-x86 = { version = "1.21", default-features = false, features = ["decoder",
|
||||
msvc-demangler = { version = "0.11", optional = true }
|
||||
|
||||
# arm
|
||||
unarm = { version = "1.7", optional = true }
|
||||
unarm = { version = "1.9", optional = true }
|
||||
arm-attr = { version = "0.2", optional = true }
|
||||
|
||||
# arm64
|
||||
@@ -159,14 +172,15 @@ yaxpeax-arch = { version = "0.3", default-features = false, optional = true }
|
||||
yaxpeax-arm = { version = "0.3", default-features = false, optional = true }
|
||||
|
||||
# build
|
||||
notify = { version = "8.0.0", optional = true }
|
||||
notify = { version = "8.1.0", optional = true }
|
||||
notify-debouncer-full = { version = "0.5.0", optional = true }
|
||||
shell-escape = { version = "0.1", optional = true }
|
||||
tempfile = { version = "3.17", optional = true }
|
||||
tempfile = { version = "3.20", optional = true }
|
||||
time = { version = "0.3", optional = true }
|
||||
encoding_rs = { version = "0.8.35", optional = true }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winapi = { version = "0.3", optional = true }
|
||||
winapi = { version = "0.3", optional = true, features = ["winbase"] }
|
||||
|
||||
# For Linux static binaries, use rustls
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
@@ -180,10 +194,10 @@ self_update = { version = "0.42", optional = true }
|
||||
|
||||
[build-dependencies]
|
||||
heck = { version = "0.5", optional = true }
|
||||
pbjson-build = { version = "0.7", optional = true }
|
||||
pbjson-build = { version = "0.8", optional = true }
|
||||
prettyplease = { version = "0.2", optional = true }
|
||||
proc-macro2 = { version = "1.0", optional = true }
|
||||
prost-build = { version = "0.13", optional = true }
|
||||
prost-build = { version = "0.14", optional = true }
|
||||
quote = { version = "1.0", optional = true }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = { version = "1.0" }
|
||||
@@ -192,4 +206,4 @@ syn = { version = "2.0", optional = true }
|
||||
[dev-dependencies]
|
||||
# Enable all features for tests
|
||||
objdiff-core = { path = ".", features = ["all"] }
|
||||
insta = "1.42"
|
||||
insta = "1.43"
|
||||
|
||||
@@ -5,11 +5,12 @@ objdiff-core contains the core functionality of [objdiff](https://github.com/enc
|
||||
## Crate feature flags
|
||||
|
||||
- **`all`**: Enables all main features.
|
||||
- **`bindings`**: Enables serialization and deserialization of objdiff data structures.
|
||||
- **`config`**: Enables objdiff configuration file support.
|
||||
- **`dwarf`**: Enables extraction of line number information from DWARF debug sections.
|
||||
- **`mips`**: Enables the MIPS backend powered by [rabbitizer](https://github.com/Decompollaborate/rabbitizer). (Note: C library with Rust bindings)
|
||||
- **`ppc`**: Enables the PowerPC backend powered by [ppc750cl](https://github.com/encounter/ppc750cl).
|
||||
- **`x86`**: Enables the x86 backend powered by [iced-x86](https://crates.io/crates/iced-x86).
|
||||
- **`arm`**: Enables the ARM backend powered by [unarm](https://github.com/AetiasHax/unarm).
|
||||
- **`arm64`**: Enables the ARM64 backend powered by [yaxpeax-arm](https://github.com/iximeow/yaxpeax-arm).
|
||||
- **`bindings`**: Enables serialization and deserialization of objdiff data structures.
|
||||
- **`arm`**: Enables the ARM backend powered by [unarm](https://github.com/AetiasHax/unarm).
|
||||
- **`mips`**: Enables the MIPS backend powered by [rabbitizer](https://github.com/Decompollaborate/rabbitizer).
|
||||
- **`ppc`**: Enables the PowerPC backend powered by [powerpc](https://github.com/encounter/powerpc-rs).
|
||||
- **`superh`**: Enables the SuperH backend powered by an included disassembler.
|
||||
- **`x86`**: Enables the x86 backend powered by [iced-x86](https://crates.io/crates/iced-x86).
|
||||
|
||||
@@ -19,7 +19,15 @@ fn compile_protos() {
|
||||
.map(|m| m.modified().unwrap())
|
||||
.unwrap_or(std::time::SystemTime::UNIX_EPOCH);
|
||||
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 {
|
||||
println!("cargo:rerun-if-changed={}", proto_file.display());
|
||||
let mtime = match std::fs::metadata(proto_file) {
|
||||
|
||||
@@ -25,6 +25,20 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "analyzeDataFlow",
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"name": "(Experimental) Perform data flow analysis",
|
||||
"description": "Use data flow analysis to display known information about register contents where possible"
|
||||
},
|
||||
{
|
||||
"id": "showDataFlow",
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"name": "Show data flow",
|
||||
"description": "Show data flow analysis results in place of register name where present"
|
||||
},
|
||||
{
|
||||
"id": "spaceBetweenArgs",
|
||||
"type": "boolean",
|
||||
@@ -187,6 +201,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",
|
||||
"type": "choice",
|
||||
@@ -242,7 +270,16 @@
|
||||
"name": "MIPS",
|
||||
"properties": [
|
||||
"mips.abi",
|
||||
"mips.instrCategory"
|
||||
"mips.instrCategory",
|
||||
"mips.registerPrefix"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "ppc",
|
||||
"name": "PowerPC",
|
||||
"properties": [
|
||||
"ppc.calculatePoolRelocations",
|
||||
"analyzeDataFlow"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -253,4 +290,4 @@
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,10 +60,10 @@ pub struct ConfigGroup {
|
||||
}
|
||||
|
||||
fn build_doc(name: &str, description: Option<&str>) -> TokenStream {
|
||||
let mut doc = format!(" {}", name);
|
||||
let mut doc = format!(" {name}");
|
||||
let mut out = quote! { #[doc = #doc] };
|
||||
if let Some(description) = description {
|
||||
doc = format!(" {}", description);
|
||||
doc = format!(" {description}");
|
||||
out.extend(quote! { #[doc = ""] });
|
||||
out.extend(quote! { #[doc = #doc] });
|
||||
}
|
||||
@@ -443,9 +443,9 @@ pub fn generate_diff_config() {
|
||||
}
|
||||
impl core::fmt::Display for ConfigPropertyValue {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
match self {
|
||||
ConfigPropertyValue::Boolean(value) => write!(f, "{}", value),
|
||||
ConfigPropertyValue::Choice(value) => write!(f, "{}", value),
|
||||
match *self {
|
||||
ConfigPropertyValue::Boolean(value) => write!(f, "{value}"),
|
||||
ConfigPropertyValue::Choice(value) => f.write_str(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
59
objdiff-core/protos/changes.proto
Normal file
59
objdiff-core/protos/changes.proto
Normal 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;
|
||||
}
|
||||
Binary file not shown.
@@ -2,6 +2,18 @@ syntax = "proto3";
|
||||
|
||||
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
|
||||
message Measures {
|
||||
// Overall match percent, including partially matched functions and data
|
||||
@@ -38,18 +50,6 @@ message Measures {
|
||||
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 {
|
||||
// The ID of the category
|
||||
string id = 1;
|
||||
@@ -99,6 +99,8 @@ message ReportItem {
|
||||
float fuzzy_match_percent = 3;
|
||||
// Extra metadata for this item
|
||||
optional ReportItemMetadata metadata = 4;
|
||||
// Address of the item (section-relative offset)
|
||||
optional uint64 address = 5;
|
||||
}
|
||||
|
||||
// Extra metadata for an item
|
||||
@@ -108,57 +110,3 @@ message ReportItemMetadata {
|
||||
// The virtual address of the function or section
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -5,17 +5,17 @@ use alloc::{
|
||||
vec::Vec,
|
||||
};
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use arm_attr::{enums::CpuArch, tag::Tag, BuildAttrs};
|
||||
use object::{elf, Endian as _, Object as _, ObjectSection as _, ObjectSymbol as _};
|
||||
use anyhow::{Result, bail};
|
||||
use arm_attr::{BuildAttrs, enums::CpuArch, tag::Tag};
|
||||
use object::{Endian as _, Object as _, ObjectSection as _, ObjectSymbol as _, elf};
|
||||
use unarm::{args, arm, thumb};
|
||||
|
||||
use crate::{
|
||||
arch::Arch,
|
||||
diff::{display::InstructionPart, ArmArchVersion, ArmR9Usage, DiffObjConfig},
|
||||
arch::{Arch, RelocationOverride, RelocationOverrideTarget},
|
||||
diff::{ArmArchVersion, ArmR9Usage, DiffObjConfig, display::InstructionPart},
|
||||
obj::{
|
||||
InstructionRef, RelocationFlags, ResolvedInstructionRef, ResolvedRelocation,
|
||||
ScannedInstruction, SymbolFlag, SymbolFlagSet, SymbolKind,
|
||||
InstructionRef, Relocation, RelocationFlags, ResolvedInstructionRef, ResolvedRelocation,
|
||||
Section, SectionKind, Symbol, SymbolFlag, SymbolFlagSet, SymbolKind,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -32,7 +32,8 @@ impl ArchArm {
|
||||
let endianness = file.endianness();
|
||||
match file {
|
||||
object::File::Elf32(_) => {
|
||||
let disasm_modes = Self::elf_get_mapping_symbols(file);
|
||||
// The disasm_modes mapping is populated later in the post_init step so that we have access to merged sections.
|
||||
let disasm_modes = BTreeMap::new();
|
||||
let detected_version = Self::elf_detect_arm_version(file)?;
|
||||
Ok(Self { disasm_modes, detected_version, endianness })
|
||||
}
|
||||
@@ -58,11 +59,7 @@ impl ArchArm {
|
||||
}
|
||||
// Only checking first CpuArch tag. Others may exist, but that's very unlikely.
|
||||
let cpu_arch = subsection.into_public_tag_iter()?.find_map(|(_, tag)| {
|
||||
if let Tag::CpuArch(cpu_arch) = tag {
|
||||
Some(cpu_arch)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
if let Tag::CpuArch(cpu_arch) = tag { Some(cpu_arch) } else { None }
|
||||
});
|
||||
match cpu_arch {
|
||||
Some(CpuArch::V4T) => return Ok(Some(unarm::ArmVersion::V4T)),
|
||||
@@ -77,29 +74,26 @@ impl ArchArm {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn elf_get_mapping_symbols(file: &object::File) -> BTreeMap<usize, Vec<DisasmMode>> {
|
||||
file.sections()
|
||||
.filter(|s| s.kind() == object::SectionKind::Text)
|
||||
.map(|s| {
|
||||
let index = s.index();
|
||||
let mut mapping_symbols: Vec<_> = file
|
||||
.symbols()
|
||||
.filter(|s| s.section_index().map(|i| i == index).unwrap_or(false))
|
||||
.filter_map(|s| DisasmMode::from_symbol(&s))
|
||||
fn get_mapping_symbols(
|
||||
sections: &[Section],
|
||||
symbols: &[Symbol],
|
||||
) -> BTreeMap<usize, Vec<DisasmMode>> {
|
||||
sections
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, section)| section.kind == SectionKind::Code)
|
||||
.map(|(index, _)| {
|
||||
let mut mapping_symbols: Vec<_> = symbols
|
||||
.iter()
|
||||
.filter(|s| s.section.map(|i| i == index).unwrap_or(false))
|
||||
.filter_map(DisasmMode::from_symbol)
|
||||
.collect();
|
||||
mapping_symbols.sort_unstable_by_key(|x| x.address);
|
||||
(s.index().0, mapping_symbols)
|
||||
(index, mapping_symbols)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn endian(&self) -> unarm::Endian {
|
||||
match self.endianness {
|
||||
object::Endianness::Little => unarm::Endian::Little,
|
||||
object::Endianness::Big => unarm::Endian::Big,
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_flags(&self, diff_config: &DiffObjConfig) -> unarm::ParseFlags {
|
||||
unarm::ParseFlags {
|
||||
ual: diff_config.arm_unified_syntax,
|
||||
@@ -134,6 +128,31 @@ impl ArchArm {
|
||||
code: &[u8],
|
||||
diff_config: &DiffObjConfig,
|
||||
) -> Result<(unarm::Ins, unarm::ParsedIns)> {
|
||||
if ins_ref.opcode == thumb::Opcode::BlH as u16 && ins_ref.size == 4 {
|
||||
// Special case: combined thumb BL instruction
|
||||
let parse_flags = self.parse_flags(diff_config);
|
||||
let first_ins = thumb::Ins {
|
||||
code: match self.endianness {
|
||||
object::Endianness::Little => u16::from_le_bytes([code[0], code[1]]),
|
||||
object::Endianness::Big => u16::from_be_bytes([code[0], code[1]]),
|
||||
} as u32,
|
||||
op: thumb::Opcode::BlH,
|
||||
};
|
||||
let second_ins = thumb::Ins::new(
|
||||
match self.endianness {
|
||||
object::Endianness::Little => u16::from_le_bytes([code[2], code[3]]),
|
||||
object::Endianness::Big => u16::from_be_bytes([code[2], code[3]]),
|
||||
} as u32,
|
||||
&parse_flags,
|
||||
);
|
||||
let first_parsed = first_ins.parse(&parse_flags);
|
||||
let second_parsed = second_ins.parse(&parse_flags);
|
||||
return Ok((
|
||||
unarm::Ins::Thumb(first_ins),
|
||||
first_parsed.combine_thumb_bl(&second_parsed),
|
||||
));
|
||||
}
|
||||
|
||||
let code = match (self.endianness, ins_ref.size) {
|
||||
(object::Endianness::Little, 2) => u16::from_le_bytes([code[0], code[1]]) as u32,
|
||||
(object::Endianness::Little, 4) => {
|
||||
@@ -157,24 +176,25 @@ impl ArchArm {
|
||||
} else {
|
||||
let ins = thumb::Ins { code, op: thumb::Opcode::from(ins_ref.opcode as u8) };
|
||||
let parsed = ins.parse(&self.parse_flags(diff_config));
|
||||
if ins.is_half_bl() {
|
||||
todo!("Combine thumb BL instructions");
|
||||
} else {
|
||||
(unarm::Ins::Thumb(ins), parsed)
|
||||
}
|
||||
(unarm::Ins::Thumb(ins), parsed)
|
||||
};
|
||||
Ok((ins, parsed_ins))
|
||||
}
|
||||
}
|
||||
|
||||
impl Arch for ArchArm {
|
||||
fn scan_instructions(
|
||||
fn post_init(&mut self, sections: &[Section], symbols: &[Symbol]) {
|
||||
self.disasm_modes = Self::get_mapping_symbols(sections, symbols);
|
||||
}
|
||||
|
||||
fn scan_instructions_internal(
|
||||
&self,
|
||||
address: u64,
|
||||
code: &[u8],
|
||||
section_index: usize,
|
||||
_relocations: &[Relocation],
|
||||
diff_config: &DiffObjConfig,
|
||||
) -> Result<Vec<ScannedInstruction>> {
|
||||
) -> Result<Vec<InstructionRef>> {
|
||||
let start_addr = address as u32;
|
||||
let end_addr = start_addr + code.len() as u32;
|
||||
|
||||
@@ -188,61 +208,126 @@ impl Arch for ArchArm {
|
||||
.unwrap_or(&fallback_mappings);
|
||||
let first_mapping_idx = mapping_symbols
|
||||
.binary_search_by_key(&start_addr, |x| x.address)
|
||||
.unwrap_or_else(|idx| idx - 1);
|
||||
let first_mapping = mapping_symbols[first_mapping_idx].mapping;
|
||||
.unwrap_or_else(|idx| idx.saturating_sub(1));
|
||||
let mut mode = mapping_symbols[first_mapping_idx].mapping;
|
||||
|
||||
let mut mappings_iter =
|
||||
mapping_symbols.iter().skip(first_mapping_idx + 1).take_while(|x| x.address < end_addr);
|
||||
let mut mappings_iter = mapping_symbols
|
||||
.iter()
|
||||
.copied()
|
||||
.skip(first_mapping_idx + 1)
|
||||
.take_while(|x| x.address < end_addr);
|
||||
let mut next_mapping = mappings_iter.next();
|
||||
|
||||
let ins_count = code.len() / first_mapping.instruction_size(start_addr);
|
||||
let mut ops = Vec::<ScannedInstruction>::with_capacity(ins_count);
|
||||
let ins_count = code.len() / mode.instruction_size(start_addr);
|
||||
let mut ops = Vec::<InstructionRef>::with_capacity(ins_count);
|
||||
|
||||
let endian = self.endian();
|
||||
let parse_flags = self.parse_flags(diff_config);
|
||||
let mut parser = unarm::Parser::new(first_mapping, start_addr, endian, parse_flags, code);
|
||||
|
||||
while let Some((address, ins, _parsed_ins)) = parser.next() {
|
||||
let size = parser.mode.instruction_size(address);
|
||||
if let Some(next) = next_mapping {
|
||||
let next_address = parser.address;
|
||||
if next_address >= next.address {
|
||||
// Change mapping
|
||||
parser.mode = next.mapping;
|
||||
next_mapping = mappings_iter.next();
|
||||
}
|
||||
let mut address = start_addr;
|
||||
while address < end_addr {
|
||||
while let Some(next) = next_mapping.filter(|x| address >= x.address) {
|
||||
// Change mapping
|
||||
mode = next.mapping;
|
||||
next_mapping = mappings_iter.next();
|
||||
}
|
||||
let (opcode, branch_dest) = match ins {
|
||||
unarm::Ins::Arm(x) => {
|
||||
let opcode = x.op as u16 | (1 << 15);
|
||||
let branch_dest = match x.op {
|
||||
|
||||
let mut ins_size = mode.instruction_size(address);
|
||||
let data = &code[(address - start_addr) as usize..];
|
||||
if data.len() < ins_size {
|
||||
// Push the remainder as data
|
||||
ops.push(InstructionRef {
|
||||
address: address as u64,
|
||||
size: data.len() as u8,
|
||||
opcode: u16::MAX,
|
||||
branch_dest: None,
|
||||
});
|
||||
break;
|
||||
}
|
||||
let code = match (self.endianness, ins_size) {
|
||||
(object::Endianness::Little, 2) => u16::from_le_bytes([data[0], data[1]]) as u32,
|
||||
(object::Endianness::Little, 4) => {
|
||||
u32::from_le_bytes([data[0], data[1], data[2], data[3]])
|
||||
}
|
||||
(object::Endianness::Big, 2) => u16::from_be_bytes([data[0], data[1]]) as u32,
|
||||
(object::Endianness::Big, 4) => {
|
||||
u32::from_be_bytes([data[0], data[1], data[2], data[3]])
|
||||
}
|
||||
_ => {
|
||||
// Invalid instruction size
|
||||
ops.push(InstructionRef {
|
||||
address: address as u64,
|
||||
size: ins_size as u8,
|
||||
opcode: u16::MAX,
|
||||
branch_dest: None,
|
||||
});
|
||||
address += ins_size as u32;
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let (opcode, branch_dest) = match mode {
|
||||
unarm::ParseMode::Arm => {
|
||||
let ins = arm::Ins::new(code, &parse_flags);
|
||||
let opcode = ins.op as u16 | (1 << 15);
|
||||
let branch_dest = match ins.op {
|
||||
arm::Opcode::B | arm::Opcode::Bl => {
|
||||
address.checked_add_signed(x.field_branch_offset())
|
||||
address.checked_add_signed(ins.field_branch_offset())
|
||||
}
|
||||
arm::Opcode::BlxI => address.checked_add_signed(x.field_blx_offset()),
|
||||
arm::Opcode::BlxI => address.checked_add_signed(ins.field_blx_offset()),
|
||||
_ => None,
|
||||
};
|
||||
(opcode, branch_dest)
|
||||
}
|
||||
unarm::Ins::Thumb(x) => {
|
||||
let opcode = x.op as u16;
|
||||
let branch_dest = match x.op {
|
||||
unarm::ParseMode::Thumb => {
|
||||
let ins = thumb::Ins::new(code, &parse_flags);
|
||||
let opcode = ins.op as u16;
|
||||
let branch_dest = match ins.op {
|
||||
thumb::Opcode::B | thumb::Opcode::Bl => {
|
||||
address.checked_add_signed(x.field_branch_offset_8())
|
||||
address.checked_add_signed(ins.field_branch_offset_8())
|
||||
}
|
||||
thumb::Opcode::BlH if data.len() >= 4 => {
|
||||
// Combine BL instructions
|
||||
let second_ins = thumb::Ins::new(
|
||||
match self.endianness {
|
||||
object::Endianness::Little => {
|
||||
u16::from_le_bytes([data[2], data[3]]) as u32
|
||||
}
|
||||
object::Endianness::Big => {
|
||||
u16::from_be_bytes([data[2], data[3]]) as u32
|
||||
}
|
||||
},
|
||||
&parse_flags,
|
||||
);
|
||||
if let Some(low) = match second_ins.op {
|
||||
thumb::Opcode::Bl => Some(second_ins.field_low_branch_offset_11()),
|
||||
thumb::Opcode::BlxI => Some(second_ins.field_low_blx_offset_11()),
|
||||
_ => None,
|
||||
} {
|
||||
ins_size = 4;
|
||||
address.checked_add_signed(
|
||||
(ins.field_high_branch_offset_11() + (low as i32)) << 9 >> 9,
|
||||
)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
thumb::Opcode::BLong => {
|
||||
address.checked_add_signed(x.field_branch_offset_11())
|
||||
address.checked_add_signed(ins.field_branch_offset_11())
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
(opcode, branch_dest)
|
||||
}
|
||||
unarm::Ins::Data => (u16::MAX, None),
|
||||
unarm::ParseMode::Data => (u16::MAX, None),
|
||||
};
|
||||
ops.push(ScannedInstruction {
|
||||
ins_ref: InstructionRef { address: address as u64, size: size as u8, opcode },
|
||||
|
||||
ops.push(InstructionRef {
|
||||
address: address as u64,
|
||||
size: ins_size as u8,
|
||||
opcode,
|
||||
branch_dest: branch_dest.map(|x| x as u64),
|
||||
});
|
||||
address += ins_size as u32;
|
||||
}
|
||||
|
||||
Ok(ops)
|
||||
@@ -260,6 +345,7 @@ impl Arch for ArchArm {
|
||||
cb(InstructionPart::reloc())?;
|
||||
} else {
|
||||
push_args(
|
||||
ins,
|
||||
&parsed_ins,
|
||||
resolved.relocation,
|
||||
resolved.ins_ref.address as u32,
|
||||
@@ -270,47 +356,57 @@ impl Arch for ArchArm {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn implcit_addend(
|
||||
fn relocation_override(
|
||||
&self,
|
||||
_file: &object::File<'_>,
|
||||
section: &object::Section,
|
||||
address: u64,
|
||||
_relocation: &object::Relocation,
|
||||
flags: RelocationFlags,
|
||||
) -> Result<i64> {
|
||||
let section_data = section.data()?;
|
||||
let address = address as usize;
|
||||
Ok(match flags {
|
||||
// ARM calls
|
||||
RelocationFlags::Elf(elf::R_ARM_PC24)
|
||||
| RelocationFlags::Elf(elf::R_ARM_XPC25)
|
||||
| RelocationFlags::Elf(elf::R_ARM_CALL) => {
|
||||
let data = section_data[address..address + 4].try_into()?;
|
||||
let addend = self.endianness.read_i32_bytes(data);
|
||||
let imm24 = addend & 0xffffff;
|
||||
(imm24 << 2) << 8 >> 8
|
||||
relocation: &object::Relocation,
|
||||
) -> Result<Option<RelocationOverride>> {
|
||||
match relocation.flags() {
|
||||
// Handle ELF implicit relocations
|
||||
object::RelocationFlags::Elf { r_type } => {
|
||||
if relocation.has_implicit_addend() {
|
||||
let section_data = section.data()?;
|
||||
let address = address as usize;
|
||||
let addend = match r_type {
|
||||
// ARM calls
|
||||
elf::R_ARM_PC24 | elf::R_ARM_XPC25 | elf::R_ARM_CALL => {
|
||||
let data = section_data[address..address + 4].try_into()?;
|
||||
let addend = self.endianness.read_i32_bytes(data);
|
||||
let imm24 = addend & 0xffffff;
|
||||
(imm24 << 2) << 8 >> 8
|
||||
}
|
||||
|
||||
// Thumb calls
|
||||
elf::R_ARM_THM_PC22 | elf::R_ARM_THM_XPC22 => {
|
||||
let data = section_data[address..address + 2].try_into()?;
|
||||
let high = self.endianness.read_i16_bytes(data) as i32;
|
||||
let data = section_data[address + 2..address + 4].try_into()?;
|
||||
let low = self.endianness.read_i16_bytes(data) as i32;
|
||||
|
||||
let imm22 = ((high & 0x7ff) << 11) | (low & 0x7ff);
|
||||
(imm22 << 1) << 9 >> 9
|
||||
}
|
||||
|
||||
// Data
|
||||
elf::R_ARM_ABS32 => {
|
||||
let data = section_data[address..address + 4].try_into()?;
|
||||
self.endianness.read_i32_bytes(data)
|
||||
}
|
||||
|
||||
flags => bail!("Unsupported ARM implicit relocation {flags:?}"),
|
||||
};
|
||||
Ok(Some(RelocationOverride {
|
||||
target: RelocationOverrideTarget::Keep,
|
||||
addend: addend as i64,
|
||||
}))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
// Thumb calls
|
||||
RelocationFlags::Elf(elf::R_ARM_THM_PC22)
|
||||
| RelocationFlags::Elf(elf::R_ARM_THM_XPC22) => {
|
||||
let data = section_data[address..address + 2].try_into()?;
|
||||
let high = self.endianness.read_i16_bytes(data) as i32;
|
||||
let data = section_data[address + 2..address + 4].try_into()?;
|
||||
let low = self.endianness.read_i16_bytes(data) as i32;
|
||||
|
||||
let imm22 = ((high & 0x7ff) << 11) | (low & 0x7ff);
|
||||
(imm22 << 1) << 9 >> 9
|
||||
}
|
||||
|
||||
// Data
|
||||
RelocationFlags::Elf(elf::R_ARM_ABS32) => {
|
||||
let data = section_data[address..address + 4].try_into()?;
|
||||
self.endianness.read_i32_bytes(data)
|
||||
}
|
||||
|
||||
flags => bail!("Unsupported ARM implicit relocation {flags:?}"),
|
||||
} as i64)
|
||||
_ => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
fn demangle(&self, name: &str) -> Option<String> {
|
||||
@@ -358,16 +454,12 @@ impl Arch for ArchArm {
|
||||
}
|
||||
|
||||
fn symbol_address(&self, address: u64, kind: SymbolKind) -> u64 {
|
||||
if kind == SymbolKind::Function {
|
||||
address & !1
|
||||
} else {
|
||||
address
|
||||
}
|
||||
if kind == SymbolKind::Function { address & !1 } else { address }
|
||||
}
|
||||
|
||||
fn extra_symbol_flags(&self, symbol: &object::Symbol) -> SymbolFlagSet {
|
||||
let mut flags = SymbolFlagSet::default();
|
||||
if DisasmMode::from_symbol(symbol).is_some() {
|
||||
if DisasmMode::from_object_symbol(symbol).is_some() {
|
||||
flags |= SymbolFlag::Hidden;
|
||||
}
|
||||
flags
|
||||
@@ -381,15 +473,21 @@ struct DisasmMode {
|
||||
}
|
||||
|
||||
impl DisasmMode {
|
||||
fn from_symbol<'a>(sym: &object::Symbol<'a, '_, &'a [u8]>) -> Option<Self> {
|
||||
fn from_object_symbol<'a>(sym: &object::Symbol<'a, '_, &'a [u8]>) -> Option<Self> {
|
||||
sym.name()
|
||||
.ok()
|
||||
.and_then(unarm::ParseMode::from_mapping_symbol)
|
||||
.map(|mapping| DisasmMode { address: sym.address() as u32, mapping })
|
||||
}
|
||||
|
||||
fn from_symbol(sym: &Symbol) -> Option<Self> {
|
||||
unarm::ParseMode::from_mapping_symbol(&sym.name)
|
||||
.map(|mapping| DisasmMode { address: sym.address as u32, mapping })
|
||||
}
|
||||
}
|
||||
|
||||
fn push_args(
|
||||
ins: unarm::Ins,
|
||||
parsed_ins: &unarm::ParsedIns,
|
||||
relocation: Option<ResolvedRelocation>,
|
||||
cur_addr: u32,
|
||||
@@ -399,7 +497,7 @@ fn push_args(
|
||||
let reloc_arg = find_reloc_arg(parsed_ins, relocation);
|
||||
let mut writeback = false;
|
||||
let mut deref = false;
|
||||
for (i, arg) in parsed_ins.args_iter().enumerate() {
|
||||
for (i, &arg) in parsed_ins.args_iter().enumerate() {
|
||||
// Emit punctuation before separator
|
||||
if deref {
|
||||
match arg {
|
||||
@@ -471,23 +569,23 @@ fn push_args(
|
||||
| args::Argument::CoOpcode(value)
|
||||
| args::Argument::SatImm(value) => {
|
||||
arg_cb(InstructionPart::basic("#"))?;
|
||||
arg_cb(InstructionPart::unsigned(*value))?;
|
||||
arg_cb(InstructionPart::unsigned(value))?;
|
||||
}
|
||||
args::Argument::SImm(value)
|
||||
| args::Argument::OffsetImm(args::OffsetImm { post_indexed: _, value }) => {
|
||||
arg_cb(InstructionPart::basic("#"))?;
|
||||
arg_cb(InstructionPart::signed(*value))?;
|
||||
arg_cb(InstructionPart::signed(value))?;
|
||||
}
|
||||
args::Argument::BranchDest(value) => {
|
||||
arg_cb(InstructionPart::branch_dest(cur_addr.wrapping_add_signed(*value)))?;
|
||||
arg_cb(InstructionPart::branch_dest(cur_addr.wrapping_add_signed(value)))?;
|
||||
}
|
||||
args::Argument::CoOption(value) => {
|
||||
arg_cb(InstructionPart::basic("{"))?;
|
||||
arg_cb(InstructionPart::unsigned(*value))?;
|
||||
arg_cb(InstructionPart::unsigned(value))?;
|
||||
arg_cb(InstructionPart::basic("}"))?;
|
||||
}
|
||||
args::Argument::CoprocNum(value) => {
|
||||
arg_cb(InstructionPart::opaque(format!("p{}", value)))?;
|
||||
arg_cb(InstructionPart::opaque(format!("p{value}")))?;
|
||||
}
|
||||
args::Argument::ShiftImm(shift) => {
|
||||
arg_cb(InstructionPart::opaque(shift.op.to_string()))?;
|
||||
@@ -535,6 +633,14 @@ fn push_args(
|
||||
arg_cb(InstructionPart::opaque("!"))?;
|
||||
}
|
||||
}
|
||||
|
||||
let branch_dest = get_pc_relative_load_address(ins, cur_addr);
|
||||
if let Some(branch_dest) = branch_dest {
|
||||
arg_cb(InstructionPart::basic(" (->"))?;
|
||||
arg_cb(InstructionPart::branch_dest(branch_dest))?;
|
||||
arg_cb(InstructionPart::basic(")"))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -562,3 +668,21 @@ fn find_reloc_arg(
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn get_pc_relative_load_address(ins: unarm::Ins, address: u32) -> Option<u32> {
|
||||
match ins {
|
||||
unarm::Ins::Arm(ins)
|
||||
if ins.op == arm::Opcode::Ldr
|
||||
&& ins.modifier_addr_ldr_str() == arm::AddrLdrStr::Imm
|
||||
&& ins.field_rn_deref().reg == args::Register::Pc =>
|
||||
{
|
||||
let offset = ins.field_offset_12().value;
|
||||
Some(address.wrapping_add_signed(offset + 8))
|
||||
}
|
||||
unarm::Ins::Thumb(ins) if ins.op == thumb::Opcode::LdrPc => {
|
||||
let offset = ins.field_rel_immed_8().value;
|
||||
Some((address & !3).wrapping_add_signed(offset + 4))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use alloc::{
|
||||
};
|
||||
use core::cmp::Ordering;
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use anyhow::Result;
|
||||
use object::elf;
|
||||
use yaxpeax_arch::{Arch as YaxpeaxArch, Decoder, Reader, U8Reader};
|
||||
use yaxpeax_arm::armv8::a64::{
|
||||
@@ -15,10 +15,9 @@ use yaxpeax_arm::armv8::a64::{
|
||||
|
||||
use crate::{
|
||||
arch::Arch,
|
||||
diff::{display::InstructionPart, DiffObjConfig},
|
||||
diff::{DiffObjConfig, display::InstructionPart},
|
||||
obj::{
|
||||
InstructionRef, RelocationFlags, ResolvedInstructionRef, ResolvedRelocation,
|
||||
ScannedInstruction,
|
||||
InstructionRef, Relocation, RelocationFlags, ResolvedInstructionRef, ResolvedRelocation,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -30,15 +29,16 @@ impl ArchArm64 {
|
||||
}
|
||||
|
||||
impl Arch for ArchArm64 {
|
||||
fn scan_instructions(
|
||||
fn scan_instructions_internal(
|
||||
&self,
|
||||
address: u64,
|
||||
code: &[u8],
|
||||
_section_index: usize,
|
||||
_relocations: &[Relocation],
|
||||
_diff_config: &DiffObjConfig,
|
||||
) -> Result<Vec<ScannedInstruction>> {
|
||||
) -> Result<Vec<InstructionRef>> {
|
||||
let start_address = address;
|
||||
let mut ops = Vec::<ScannedInstruction>::with_capacity(code.len() / 4);
|
||||
let mut ops = Vec::<InstructionRef>::with_capacity(code.len() / 4);
|
||||
|
||||
let mut reader = U8Reader::new(code);
|
||||
let decoder = InstDecoder::default();
|
||||
@@ -57,8 +57,10 @@ impl Arch for ArchArm64 {
|
||||
DecodeError::InvalidOpcode
|
||||
| DecodeError::InvalidOperand
|
||||
| DecodeError::IncompleteDecoder => {
|
||||
ops.push(ScannedInstruction {
|
||||
ins_ref: InstructionRef { address, size: 4, opcode: u16::MAX },
|
||||
ops.push(InstructionRef {
|
||||
address,
|
||||
size: 4,
|
||||
opcode: u16::MAX,
|
||||
branch_dest: None,
|
||||
});
|
||||
continue;
|
||||
@@ -67,9 +69,9 @@ impl Arch for ArchArm64 {
|
||||
}
|
||||
|
||||
let opcode = opcode_to_u16(ins.opcode);
|
||||
let ins_ref = InstructionRef { address, size: 4, opcode };
|
||||
let branch_dest = branch_dest(ins_ref, &code[offset as usize..offset as usize + 4]);
|
||||
ops.push(ScannedInstruction { ins_ref, branch_dest });
|
||||
let branch_dest =
|
||||
branch_dest(opcode, address, &code[offset as usize..offset as usize + 4]);
|
||||
ops.push(InstructionRef { address, size: 4, opcode, branch_dest });
|
||||
}
|
||||
|
||||
Ok(ops)
|
||||
@@ -106,17 +108,6 @@ impl Arch for ArchArm64 {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn implcit_addend(
|
||||
&self,
|
||||
_file: &object::File<'_>,
|
||||
_section: &object::Section,
|
||||
address: u64,
|
||||
_relocation: &object::Relocation,
|
||||
flags: RelocationFlags,
|
||||
) -> Result<i64> {
|
||||
bail!("Unsupported ARM64 implicit relocation {:#x}:{:?}", address, flags)
|
||||
}
|
||||
|
||||
fn demangle(&self, name: &str) -> Option<String> {
|
||||
cpp_demangle::Symbol::new(name)
|
||||
.ok()
|
||||
@@ -162,7 +153,7 @@ impl Arch for ArchArm64 {
|
||||
}
|
||||
}
|
||||
|
||||
fn branch_dest(ins_ref: InstructionRef, code: &[u8]) -> Option<u64> {
|
||||
fn branch_dest(opcode: u16, address: u64, code: &[u8]) -> Option<u64> {
|
||||
const OPCODE_B: u16 = opcode_to_u16(Opcode::B);
|
||||
const OPCODE_BL: u16 = opcode_to_u16(Opcode::BL);
|
||||
const OPCODE_BCC: u16 = opcode_to_u16(Opcode::Bcc(0));
|
||||
@@ -172,21 +163,21 @@ fn branch_dest(ins_ref: InstructionRef, code: &[u8]) -> Option<u64> {
|
||||
const OPCODE_TBNZ: u16 = opcode_to_u16(Opcode::TBNZ);
|
||||
|
||||
let word = u32::from_le_bytes(code.try_into().ok()?);
|
||||
match ins_ref.opcode {
|
||||
match opcode {
|
||||
OPCODE_B | OPCODE_BL => {
|
||||
let offset = ((word & 0x03ff_ffff) << 2) as i32;
|
||||
let extended_offset = (offset << 4) >> 4;
|
||||
ins_ref.address.checked_add_signed(extended_offset as i64)
|
||||
address.checked_add_signed(extended_offset as i64)
|
||||
}
|
||||
OPCODE_BCC | OPCODE_CBZ | OPCODE_CBNZ => {
|
||||
let offset = (word as i32 & 0x00ff_ffe0) >> 3;
|
||||
let extended_offset = (offset << 11) >> 11;
|
||||
ins_ref.address.checked_add_signed(extended_offset as i64)
|
||||
address.checked_add_signed(extended_offset as i64)
|
||||
}
|
||||
OPCODE_TBZ | OPCODE_TBNZ => {
|
||||
let offset = (word as i32 & 0x0007_ffe0) >> 3;
|
||||
let extended_offset = (offset << 16) >> 16;
|
||||
ins_ref.address.checked_add_signed(extended_offset as i64)
|
||||
address.checked_add_signed(extended_offset as i64)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
@@ -216,11 +207,7 @@ where Cb: FnMut(InstructionPart<'static>) {
|
||||
unreachable!("movn operand 1 is always ImmShift");
|
||||
};
|
||||
let imm = if let Operand::Register(size, _) = ins.operands[0] {
|
||||
if size == SizeCode::W {
|
||||
imm as u32 as u64
|
||||
} else {
|
||||
imm
|
||||
}
|
||||
if size == SizeCode::W { imm as u32 as u64 } else { imm }
|
||||
} else {
|
||||
unreachable!("movn operand 0 is always Register");
|
||||
};
|
||||
@@ -237,11 +224,7 @@ where Cb: FnMut(InstructionPart<'static>) {
|
||||
unreachable!("movz operand is always ImmShift");
|
||||
};
|
||||
let imm = if let Operand::Register(size, _) = ins.operands[0] {
|
||||
if size == SizeCode::W {
|
||||
imm as u32 as u64
|
||||
} else {
|
||||
imm
|
||||
}
|
||||
if size == SizeCode::W { imm as u32 as u64 } else { imm }
|
||||
} else {
|
||||
unreachable!("movz operand 0 is always Register");
|
||||
};
|
||||
@@ -526,25 +509,25 @@ where Cb: FnMut(InstructionPart<'static>) {
|
||||
return "ubfx";
|
||||
}
|
||||
Opcode::SBFM => {
|
||||
if let Operand::Immediate(63) = ins.operands[3] {
|
||||
if let Operand::Register(SizeCode::X, _) = ins.operands[0] {
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[1], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
return "asr";
|
||||
}
|
||||
if let Operand::Immediate(63) = ins.operands[3]
|
||||
&& let Operand::Register(SizeCode::X, _) = ins.operands[0]
|
||||
{
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[1], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
return "asr";
|
||||
}
|
||||
if let Operand::Immediate(31) = ins.operands[3] {
|
||||
if let Operand::Register(SizeCode::W, _) = ins.operands[0] {
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[1], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
return "asr";
|
||||
}
|
||||
if let Operand::Immediate(31) = ins.operands[3]
|
||||
&& let Operand::Register(SizeCode::W, _) = ins.operands[0]
|
||||
{
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[1], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
return "asr";
|
||||
}
|
||||
if let Operand::Immediate(0) = ins.operands[2] {
|
||||
let newsrc = if let Operand::Register(_size, srcnum) = ins.operands[1] {
|
||||
@@ -571,26 +554,21 @@ where Cb: FnMut(InstructionPart<'static>) {
|
||||
}
|
||||
if let (Operand::Immediate(imms), Operand::Immediate(immr)) =
|
||||
(ins.operands[2], ins.operands[3])
|
||||
&& immr < imms
|
||||
{
|
||||
if immr < imms {
|
||||
let size = if let Operand::Register(size, _) = ins.operands[0] {
|
||||
if size == SizeCode::W {
|
||||
32
|
||||
} else {
|
||||
64
|
||||
}
|
||||
} else {
|
||||
unreachable!("operand 0 is always a register");
|
||||
};
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[1], ctx);
|
||||
push_separator(args);
|
||||
push_unsigned(args, (size - imms) as u64);
|
||||
push_separator(args);
|
||||
push_unsigned(args, (immr + 1) as u64);
|
||||
return "sbfiz";
|
||||
}
|
||||
let size = if let Operand::Register(size, _) = ins.operands[0] {
|
||||
if size == SizeCode::W { 32 } else { 64 }
|
||||
} else {
|
||||
unreachable!("operand 0 is always a register");
|
||||
};
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[1], ctx);
|
||||
push_separator(args);
|
||||
push_unsigned(args, (size - imms) as u64);
|
||||
push_separator(args);
|
||||
push_unsigned(args, (immr + 1) as u64);
|
||||
return "sbfiz";
|
||||
}
|
||||
// `sbfm` is never actually displayed: in the remaining case, it is always aliased to `sbfx`
|
||||
let width = if let (Operand::Immediate(lsb), Operand::Immediate(width)) =
|
||||
@@ -614,15 +592,14 @@ where Cb: FnMut(InstructionPart<'static>) {
|
||||
Opcode::EXTR => {
|
||||
if let (Operand::Register(_, rn), Operand::Register(_, rm)) =
|
||||
(ins.operands[1], ins.operands[2])
|
||||
&& rn == rm
|
||||
{
|
||||
if rn == rm {
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[3], ctx);
|
||||
return "ror";
|
||||
}
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[3], ctx);
|
||||
return "ror";
|
||||
}
|
||||
"extr"
|
||||
}
|
||||
@@ -825,27 +802,24 @@ where Cb: FnMut(InstructionPart<'static>) {
|
||||
"csneg"
|
||||
}
|
||||
Opcode::CSINC => {
|
||||
if let (
|
||||
Operand::Register(_, n),
|
||||
Operand::Register(_, m),
|
||||
Operand::ConditionCode(cond),
|
||||
) = (ins.operands[1], ins.operands[2], ins.operands[3])
|
||||
if let (Operand::Register(_, n), Operand::Register(_, m), Operand::ConditionCode(cond)) =
|
||||
(ins.operands[1], ins.operands[2], ins.operands[3])
|
||||
&& n == m
|
||||
&& cond < 0b1110
|
||||
{
|
||||
if n == m && cond < 0b1110 {
|
||||
return if n == 31 {
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_condition_code(args, cond ^ 0x01);
|
||||
"cset"
|
||||
} else {
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[1], ctx);
|
||||
push_separator(args);
|
||||
push_condition_code(args, cond ^ 0x01);
|
||||
"cinc"
|
||||
};
|
||||
}
|
||||
return if n == 31 {
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_condition_code(args, cond ^ 0x01);
|
||||
"cset"
|
||||
} else {
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[1], ctx);
|
||||
push_separator(args);
|
||||
push_condition_code(args, cond ^ 0x01);
|
||||
"cinc"
|
||||
};
|
||||
}
|
||||
"csinc"
|
||||
}
|
||||
@@ -1221,15 +1195,13 @@ where Cb: FnMut(InstructionPart<'static>) {
|
||||
Operand::Register(reg_sz, _),
|
||||
Operand::SIMDRegisterElementsLane(_, _, elem_sz, _),
|
||||
) = (ins.operands[0], ins.operands[1])
|
||||
&& ((reg_sz == SizeCode::W && elem_sz == SIMDSizeCode::S)
|
||||
|| (reg_sz == SizeCode::X && elem_sz == SIMDSizeCode::D))
|
||||
{
|
||||
if (reg_sz == SizeCode::W && elem_sz == SIMDSizeCode::S)
|
||||
|| (reg_sz == SizeCode::X && elem_sz == SIMDSizeCode::D)
|
||||
{
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[1], ctx);
|
||||
return "mov";
|
||||
}
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[1], ctx);
|
||||
return "mov";
|
||||
}
|
||||
"umov"
|
||||
}
|
||||
@@ -1329,14 +1301,15 @@ where Cb: FnMut(InstructionPart<'static>) {
|
||||
}
|
||||
}
|
||||
Opcode::LDADDB(ar) => {
|
||||
if let Operand::Register(_, rt) = ins.operands[1] {
|
||||
if rt == 31 && ar & 0b10 == 0b00 {
|
||||
let inst = if ar & 0b01 == 0b00 { "staddb" } else { "staddlb" };
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
return inst;
|
||||
}
|
||||
if let Operand::Register(_, rt) = ins.operands[1]
|
||||
&& rt == 31
|
||||
&& ar & 0b10 == 0b00
|
||||
{
|
||||
let inst = if ar & 0b01 == 0b00 { "staddb" } else { "staddlb" };
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
return inst;
|
||||
}
|
||||
if ar == 0 {
|
||||
"ldaddb"
|
||||
@@ -1349,14 +1322,15 @@ where Cb: FnMut(InstructionPart<'static>) {
|
||||
}
|
||||
}
|
||||
Opcode::LDCLRB(ar) => {
|
||||
if let Operand::Register(_, rt) = ins.operands[1] {
|
||||
if rt == 31 && ar & 0b10 == 0b00 {
|
||||
let inst = if ar & 0b01 == 0b00 { "stclrb" } else { "stclrlb" };
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
return inst;
|
||||
}
|
||||
if let Operand::Register(_, rt) = ins.operands[1]
|
||||
&& rt == 31
|
||||
&& ar & 0b10 == 0b00
|
||||
{
|
||||
let inst = if ar & 0b01 == 0b00 { "stclrb" } else { "stclrlb" };
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
return inst;
|
||||
}
|
||||
if ar == 0 {
|
||||
"ldclrb"
|
||||
@@ -1369,14 +1343,15 @@ where Cb: FnMut(InstructionPart<'static>) {
|
||||
}
|
||||
}
|
||||
Opcode::LDEORB(ar) => {
|
||||
if let Operand::Register(_, rt) = ins.operands[1] {
|
||||
if rt == 31 && ar & 0b10 == 0b00 {
|
||||
let inst = if ar & 0b01 == 0b00 { "steorb" } else { "steorlb" };
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
return inst;
|
||||
}
|
||||
if let Operand::Register(_, rt) = ins.operands[1]
|
||||
&& rt == 31
|
||||
&& ar & 0b10 == 0b00
|
||||
{
|
||||
let inst = if ar & 0b01 == 0b00 { "steorb" } else { "steorlb" };
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
return inst;
|
||||
}
|
||||
if ar == 0 {
|
||||
"ldeorb"
|
||||
@@ -1389,14 +1364,15 @@ where Cb: FnMut(InstructionPart<'static>) {
|
||||
}
|
||||
}
|
||||
Opcode::LDSETB(ar) => {
|
||||
if let Operand::Register(_, rt) = ins.operands[1] {
|
||||
if rt == 31 && ar & 0b10 == 0b00 {
|
||||
let inst = if ar & 0b01 == 0b00 { "stsetb" } else { "stsetlb" };
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
return inst;
|
||||
}
|
||||
if let Operand::Register(_, rt) = ins.operands[1]
|
||||
&& rt == 31
|
||||
&& ar & 0b10 == 0b00
|
||||
{
|
||||
let inst = if ar & 0b01 == 0b00 { "stsetb" } else { "stsetlb" };
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
return inst;
|
||||
}
|
||||
if ar == 0 {
|
||||
"ldsetb"
|
||||
@@ -1409,14 +1385,15 @@ where Cb: FnMut(InstructionPart<'static>) {
|
||||
}
|
||||
}
|
||||
Opcode::LDSMAXB(ar) => {
|
||||
if let Operand::Register(_, rt) = ins.operands[1] {
|
||||
if rt == 31 && ar & 0b10 == 0b00 {
|
||||
let inst = if ar & 0b01 == 0b00 { "stsmaxb" } else { "stsmaxlb" };
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
return inst;
|
||||
}
|
||||
if let Operand::Register(_, rt) = ins.operands[1]
|
||||
&& rt == 31
|
||||
&& ar & 0b10 == 0b00
|
||||
{
|
||||
let inst = if ar & 0b01 == 0b00 { "stsmaxb" } else { "stsmaxlb" };
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
return inst;
|
||||
}
|
||||
if ar == 0 {
|
||||
"ldsmaxb"
|
||||
@@ -1429,14 +1406,15 @@ where Cb: FnMut(InstructionPart<'static>) {
|
||||
}
|
||||
}
|
||||
Opcode::LDSMINB(ar) => {
|
||||
if let Operand::Register(_, rt) = ins.operands[1] {
|
||||
if rt == 31 && ar & 0b10 == 0b00 {
|
||||
let inst = if ar & 0b01 == 0b00 { "stsminb" } else { "stsminlb" };
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
return inst;
|
||||
}
|
||||
if let Operand::Register(_, rt) = ins.operands[1]
|
||||
&& rt == 31
|
||||
&& ar & 0b10 == 0b00
|
||||
{
|
||||
let inst = if ar & 0b01 == 0b00 { "stsminb" } else { "stsminlb" };
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
return inst;
|
||||
}
|
||||
if ar == 0 {
|
||||
"ldsminb"
|
||||
@@ -1449,14 +1427,15 @@ where Cb: FnMut(InstructionPart<'static>) {
|
||||
}
|
||||
}
|
||||
Opcode::LDUMAXB(ar) => {
|
||||
if let Operand::Register(_, rt) = ins.operands[1] {
|
||||
if rt == 31 && ar & 0b10 == 0b00 {
|
||||
let inst = if ar & 0b01 == 0b00 { "stumaxb" } else { "stumaxlb" };
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
return inst;
|
||||
}
|
||||
if let Operand::Register(_, rt) = ins.operands[1]
|
||||
&& rt == 31
|
||||
&& ar & 0b10 == 0b00
|
||||
{
|
||||
let inst = if ar & 0b01 == 0b00 { "stumaxb" } else { "stumaxlb" };
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
return inst;
|
||||
}
|
||||
if ar == 0 {
|
||||
"ldumaxb"
|
||||
@@ -1469,14 +1448,15 @@ where Cb: FnMut(InstructionPart<'static>) {
|
||||
}
|
||||
}
|
||||
Opcode::LDUMINB(ar) => {
|
||||
if let Operand::Register(_, rt) = ins.operands[1] {
|
||||
if rt == 31 && ar & 0b10 == 0b00 {
|
||||
let inst = if ar & 0b01 == 0b00 { "stuminb" } else { "stuminlb" };
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
return inst;
|
||||
}
|
||||
if let Operand::Register(_, rt) = ins.operands[1]
|
||||
&& rt == 31
|
||||
&& ar & 0b10 == 0b00
|
||||
{
|
||||
let inst = if ar & 0b01 == 0b00 { "stuminb" } else { "stuminlb" };
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
return inst;
|
||||
}
|
||||
// write!(fmt, "{}", self.opcode)?;
|
||||
if ar == 0 {
|
||||
@@ -1490,14 +1470,15 @@ where Cb: FnMut(InstructionPart<'static>) {
|
||||
}
|
||||
}
|
||||
Opcode::LDADDH(ar) => {
|
||||
if let Operand::Register(_, rt) = ins.operands[1] {
|
||||
if rt == 31 && ar & 0b10 == 0b00 {
|
||||
let inst = if ar & 0b01 == 0b00 { "staddh" } else { "staddlh" };
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
return inst;
|
||||
}
|
||||
if let Operand::Register(_, rt) = ins.operands[1]
|
||||
&& rt == 31
|
||||
&& ar & 0b10 == 0b00
|
||||
{
|
||||
let inst = if ar & 0b01 == 0b00 { "staddh" } else { "staddlh" };
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
return inst;
|
||||
}
|
||||
if ar == 0 {
|
||||
"ldaddh"
|
||||
@@ -1510,14 +1491,15 @@ where Cb: FnMut(InstructionPart<'static>) {
|
||||
}
|
||||
}
|
||||
Opcode::LDCLRH(ar) => {
|
||||
if let Operand::Register(_, rt) = ins.operands[1] {
|
||||
if rt == 31 && ar & 0b10 == 0b00 {
|
||||
let inst = if ar & 0b01 == 0b00 { "stclrh" } else { "stclrlh" };
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
return inst;
|
||||
}
|
||||
if let Operand::Register(_, rt) = ins.operands[1]
|
||||
&& rt == 31
|
||||
&& ar & 0b10 == 0b00
|
||||
{
|
||||
let inst = if ar & 0b01 == 0b00 { "stclrh" } else { "stclrlh" };
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
return inst;
|
||||
}
|
||||
if ar == 0 {
|
||||
"ldclrh"
|
||||
@@ -1530,14 +1512,15 @@ where Cb: FnMut(InstructionPart<'static>) {
|
||||
}
|
||||
}
|
||||
Opcode::LDEORH(ar) => {
|
||||
if let Operand::Register(_, rt) = ins.operands[1] {
|
||||
if rt == 31 && ar & 0b10 == 0b00 {
|
||||
let inst = if ar & 0b01 == 0b00 { "steorh" } else { "steorlh" };
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
return inst;
|
||||
}
|
||||
if let Operand::Register(_, rt) = ins.operands[1]
|
||||
&& rt == 31
|
||||
&& ar & 0b10 == 0b00
|
||||
{
|
||||
let inst = if ar & 0b01 == 0b00 { "steorh" } else { "steorlh" };
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
return inst;
|
||||
}
|
||||
if ar == 0 {
|
||||
"ldeorh"
|
||||
@@ -1550,14 +1533,15 @@ where Cb: FnMut(InstructionPart<'static>) {
|
||||
}
|
||||
}
|
||||
Opcode::LDSETH(ar) => {
|
||||
if let Operand::Register(_, rt) = ins.operands[1] {
|
||||
if rt == 31 && ar & 0b10 == 0b00 {
|
||||
let inst = if ar & 0b01 == 0b00 { "stseth" } else { "stsetlh" };
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
return inst;
|
||||
}
|
||||
if let Operand::Register(_, rt) = ins.operands[1]
|
||||
&& rt == 31
|
||||
&& ar & 0b10 == 0b00
|
||||
{
|
||||
let inst = if ar & 0b01 == 0b00 { "stseth" } else { "stsetlh" };
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
return inst;
|
||||
}
|
||||
if ar == 0 {
|
||||
"ldseth"
|
||||
@@ -1570,14 +1554,15 @@ where Cb: FnMut(InstructionPart<'static>) {
|
||||
}
|
||||
}
|
||||
Opcode::LDSMAXH(ar) => {
|
||||
if let Operand::Register(_, rt) = ins.operands[1] {
|
||||
if rt == 31 && ar & 0b10 == 0b00 {
|
||||
let inst = if ar & 0b01 == 0b00 { "stsmaxh" } else { "stsmaxlh" };
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
return inst;
|
||||
}
|
||||
if let Operand::Register(_, rt) = ins.operands[1]
|
||||
&& rt == 31
|
||||
&& ar & 0b10 == 0b00
|
||||
{
|
||||
let inst = if ar & 0b01 == 0b00 { "stsmaxh" } else { "stsmaxlh" };
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
return inst;
|
||||
}
|
||||
if ar == 0 {
|
||||
"ldsmaxh"
|
||||
@@ -1590,14 +1575,15 @@ where Cb: FnMut(InstructionPart<'static>) {
|
||||
}
|
||||
}
|
||||
Opcode::LDSMINH(ar) => {
|
||||
if let Operand::Register(_, rt) = ins.operands[1] {
|
||||
if rt == 31 && ar & 0b10 == 0b00 {
|
||||
let inst = if ar & 0b01 == 0b00 { "stsminh" } else { "stsminlh" };
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
return inst;
|
||||
}
|
||||
if let Operand::Register(_, rt) = ins.operands[1]
|
||||
&& rt == 31
|
||||
&& ar & 0b10 == 0b00
|
||||
{
|
||||
let inst = if ar & 0b01 == 0b00 { "stsminh" } else { "stsminlh" };
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
return inst;
|
||||
}
|
||||
if ar == 0 {
|
||||
"ldsminh"
|
||||
@@ -1610,14 +1596,15 @@ where Cb: FnMut(InstructionPart<'static>) {
|
||||
}
|
||||
}
|
||||
Opcode::LDUMAXH(ar) => {
|
||||
if let Operand::Register(_, rt) = ins.operands[1] {
|
||||
if rt == 31 && ar & 0b10 == 0b00 {
|
||||
let inst = if ar & 0b01 == 0b00 { "stumaxh" } else { "stumaxlh" };
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
return inst;
|
||||
}
|
||||
if let Operand::Register(_, rt) = ins.operands[1]
|
||||
&& rt == 31
|
||||
&& ar & 0b10 == 0b00
|
||||
{
|
||||
let inst = if ar & 0b01 == 0b00 { "stumaxh" } else { "stumaxlh" };
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
return inst;
|
||||
}
|
||||
if ar == 0 {
|
||||
"ldumaxh"
|
||||
@@ -1630,14 +1617,15 @@ where Cb: FnMut(InstructionPart<'static>) {
|
||||
}
|
||||
}
|
||||
Opcode::LDUMINH(ar) => {
|
||||
if let Operand::Register(_, rt) = ins.operands[1] {
|
||||
if rt == 31 && ar & 0b10 == 0b00 {
|
||||
let inst = if ar & 0b01 == 0b00 { "stuminh" } else { "stuminlh" };
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
return inst;
|
||||
}
|
||||
if let Operand::Register(_, rt) = ins.operands[1]
|
||||
&& rt == 31
|
||||
&& ar & 0b10 == 0b00
|
||||
{
|
||||
let inst = if ar & 0b01 == 0b00 { "stuminh" } else { "stuminlh" };
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
return inst;
|
||||
}
|
||||
if ar == 0 {
|
||||
"lduminh"
|
||||
@@ -1650,14 +1638,15 @@ where Cb: FnMut(InstructionPart<'static>) {
|
||||
}
|
||||
}
|
||||
Opcode::LDADD(ar) => {
|
||||
if let Operand::Register(_, rt) = ins.operands[1] {
|
||||
if rt == 31 && ar & 0b10 == 0b00 {
|
||||
let inst = if ar & 0b01 == 0b00 { "stadd" } else { "staddl" };
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
return inst;
|
||||
}
|
||||
if let Operand::Register(_, rt) = ins.operands[1]
|
||||
&& rt == 31
|
||||
&& ar & 0b10 == 0b00
|
||||
{
|
||||
let inst = if ar & 0b01 == 0b00 { "stadd" } else { "staddl" };
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
return inst;
|
||||
}
|
||||
if ar == 0 {
|
||||
"ldadd"
|
||||
@@ -1670,14 +1659,15 @@ where Cb: FnMut(InstructionPart<'static>) {
|
||||
}
|
||||
}
|
||||
Opcode::LDCLR(ar) => {
|
||||
if let Operand::Register(_, rt) = ins.operands[1] {
|
||||
if rt == 31 && ar & 0b10 == 0b00 {
|
||||
let inst = if ar & 0b01 == 0b00 { "stclr" } else { "stclrl" };
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
return inst;
|
||||
}
|
||||
if let Operand::Register(_, rt) = ins.operands[1]
|
||||
&& rt == 31
|
||||
&& ar & 0b10 == 0b00
|
||||
{
|
||||
let inst = if ar & 0b01 == 0b00 { "stclr" } else { "stclrl" };
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
return inst;
|
||||
}
|
||||
if ar == 0 {
|
||||
"ldclr"
|
||||
@@ -1690,14 +1680,15 @@ where Cb: FnMut(InstructionPart<'static>) {
|
||||
}
|
||||
}
|
||||
Opcode::LDEOR(ar) => {
|
||||
if let Operand::Register(_, rt) = ins.operands[1] {
|
||||
if rt == 31 && ar & 0b10 == 0b00 {
|
||||
let inst = if ar & 0b01 == 0b00 { "steor" } else { "steorl" };
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
return inst;
|
||||
}
|
||||
if let Operand::Register(_, rt) = ins.operands[1]
|
||||
&& rt == 31
|
||||
&& ar & 0b10 == 0b00
|
||||
{
|
||||
let inst = if ar & 0b01 == 0b00 { "steor" } else { "steorl" };
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
return inst;
|
||||
}
|
||||
if ar == 0 {
|
||||
"ldeor"
|
||||
@@ -1710,14 +1701,15 @@ where Cb: FnMut(InstructionPart<'static>) {
|
||||
}
|
||||
}
|
||||
Opcode::LDSET(ar) => {
|
||||
if let Operand::Register(_, rt) = ins.operands[1] {
|
||||
if rt == 31 && ar & 0b10 == 0b00 {
|
||||
let inst = if ar & 0b01 == 0b00 { "stset" } else { "stsetl" };
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
return inst;
|
||||
}
|
||||
if let Operand::Register(_, rt) = ins.operands[1]
|
||||
&& rt == 31
|
||||
&& ar & 0b10 == 0b00
|
||||
{
|
||||
let inst = if ar & 0b01 == 0b00 { "stset" } else { "stsetl" };
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
return inst;
|
||||
}
|
||||
if ar == 0 {
|
||||
"ldset"
|
||||
@@ -1730,14 +1722,15 @@ where Cb: FnMut(InstructionPart<'static>) {
|
||||
}
|
||||
}
|
||||
Opcode::LDSMAX(ar) => {
|
||||
if let Operand::Register(_, rt) = ins.operands[1] {
|
||||
if rt == 31 && ar & 0b10 == 0b00 {
|
||||
let inst = if ar & 0b01 == 0b00 { "stsmax" } else { "stsmaxl" };
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
return inst;
|
||||
}
|
||||
if let Operand::Register(_, rt) = ins.operands[1]
|
||||
&& rt == 31
|
||||
&& ar & 0b10 == 0b00
|
||||
{
|
||||
let inst = if ar & 0b01 == 0b00 { "stsmax" } else { "stsmaxl" };
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
return inst;
|
||||
}
|
||||
if ar == 0 {
|
||||
"ldsmax"
|
||||
@@ -1750,14 +1743,15 @@ where Cb: FnMut(InstructionPart<'static>) {
|
||||
}
|
||||
}
|
||||
Opcode::LDSMIN(ar) => {
|
||||
if let Operand::Register(_, rt) = ins.operands[1] {
|
||||
if rt == 31 && ar & 0b10 == 0b00 {
|
||||
let inst = if ar & 0b01 == 0b00 { "stsmin" } else { "stsminl" };
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
return inst;
|
||||
}
|
||||
if let Operand::Register(_, rt) = ins.operands[1]
|
||||
&& rt == 31
|
||||
&& ar & 0b10 == 0b00
|
||||
{
|
||||
let inst = if ar & 0b01 == 0b00 { "stsmin" } else { "stsminl" };
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
return inst;
|
||||
}
|
||||
if ar == 0 {
|
||||
"ldsmin"
|
||||
@@ -1770,14 +1764,15 @@ where Cb: FnMut(InstructionPart<'static>) {
|
||||
}
|
||||
}
|
||||
Opcode::LDUMAX(ar) => {
|
||||
if let Operand::Register(_, rt) = ins.operands[1] {
|
||||
if rt == 31 && ar & 0b10 == 0b00 {
|
||||
let inst = if ar & 0b01 == 0b00 { "stumax" } else { "stumaxl" };
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
return inst;
|
||||
}
|
||||
if let Operand::Register(_, rt) = ins.operands[1]
|
||||
&& rt == 31
|
||||
&& ar & 0b10 == 0b00
|
||||
{
|
||||
let inst = if ar & 0b01 == 0b00 { "stumax" } else { "stumaxl" };
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
return inst;
|
||||
}
|
||||
if ar == 0 {
|
||||
"ldumax"
|
||||
@@ -1790,14 +1785,15 @@ where Cb: FnMut(InstructionPart<'static>) {
|
||||
}
|
||||
}
|
||||
Opcode::LDUMIN(ar) => {
|
||||
if let Operand::Register(_, rt) = ins.operands[1] {
|
||||
if rt == 31 && ar & 0b10 == 0b00 {
|
||||
let inst = if ar & 0b01 == 0b00 { "stumin" } else { "stuminl" };
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
return inst;
|
||||
}
|
||||
if let Operand::Register(_, rt) = ins.operands[1]
|
||||
&& rt == 31
|
||||
&& ar & 0b10 == 0b00
|
||||
{
|
||||
let inst = if ar & 0b01 == 0b00 { "stumin" } else { "stuminl" };
|
||||
push_operand(args, &ins.operands[0], ctx);
|
||||
push_separator(args);
|
||||
push_operand(args, &ins.operands[2], ctx);
|
||||
return inst;
|
||||
}
|
||||
if ar == 0 {
|
||||
"ldumin"
|
||||
@@ -2088,16 +2084,15 @@ where Cb: FnMut(InstructionPart<'static>) {
|
||||
|
||||
/// Relocations that appear in Operand::PCOffset.
|
||||
fn is_pc_offset_reloc(reloc: Option<ResolvedRelocation>) -> Option<ResolvedRelocation> {
|
||||
if let Some(resolved) = reloc {
|
||||
if let RelocationFlags::Elf(
|
||||
if let Some(resolved) = reloc
|
||||
&& let RelocationFlags::Elf(
|
||||
elf::R_AARCH64_ADR_PREL_PG_HI21
|
||||
| elf::R_AARCH64_JUMP26
|
||||
| elf::R_AARCH64_CALL26
|
||||
| elf::R_AARCH64_ADR_GOT_PAGE,
|
||||
) = resolved.relocation.flags
|
||||
{
|
||||
return Some(resolved);
|
||||
}
|
||||
{
|
||||
return Some(resolved);
|
||||
}
|
||||
None
|
||||
}
|
||||
@@ -2278,7 +2273,7 @@ where Cb: FnMut(InstructionPart<'static>) {
|
||||
push_plain(args, "]");
|
||||
push_separator(args);
|
||||
// TODO does 31 have to be handled separate?
|
||||
args(InstructionPart::opaque(format!("x{}", offset_reg)));
|
||||
args(InstructionPart::opaque(format!("x{offset_reg}")));
|
||||
}
|
||||
// Fall back to original logic
|
||||
Operand::SIMDRegister(_, _)
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
use alloc::{string::ToString, vec::Vec};
|
||||
use core::ops::Range;
|
||||
use alloc::{
|
||||
collections::{BTreeMap, BTreeSet},
|
||||
string::{String, ToString},
|
||||
vec::Vec,
|
||||
};
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use object::{elf, Endian as _, Object as _, ObjectSection as _, ObjectSymbol as _};
|
||||
use anyhow::{Result, bail};
|
||||
use object::{Endian as _, Object as _, ObjectSection as _, ObjectSymbol as _, elf};
|
||||
use rabbitizer::{
|
||||
abi::Abi,
|
||||
operands::{ValuedOperand, IU16},
|
||||
registers_meta::Register,
|
||||
IsaExtension, IsaVersion, Vram,
|
||||
IsaExtension, IsaVersion, Vram, abi::Abi, operands::ValuedOperand, registers_meta::Register,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
arch::Arch,
|
||||
diff::{display::InstructionPart, DiffObjConfig, MipsAbi, MipsInstrCategory},
|
||||
arch::{Arch, RelocationOverride, RelocationOverrideTarget},
|
||||
diff::{DiffObjConfig, MipsAbi, MipsInstrCategory, display::InstructionPart},
|
||||
obj::{
|
||||
InstructionArg, InstructionArgValue, InstructionRef, Relocation, RelocationFlags,
|
||||
ResolvedInstructionRef, ResolvedRelocation, ScannedInstruction,
|
||||
ResolvedInstructionRef, ResolvedRelocation, Section, Symbol, SymbolFlag, SymbolFlagSet,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -25,6 +25,8 @@ pub struct ArchMips {
|
||||
pub abi: Abi,
|
||||
pub isa_extension: Option<IsaExtension>,
|
||||
pub ri_gp_value: i32,
|
||||
pub paired_relocations: Vec<BTreeMap<u64, i64>>,
|
||||
pub ignored_symbols: BTreeSet<usize>,
|
||||
}
|
||||
|
||||
const EF_MIPS_ABI: u32 = 0x0000F000;
|
||||
@@ -64,16 +66,86 @@ impl ArchMips {
|
||||
|
||||
// Parse the ri_gp_value stored in .reginfo to be able to correctly
|
||||
// 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
|
||||
.section_by_name(".reginfo")
|
||||
.and_then(|section| section.data().ok())
|
||||
.and_then(|data| data.get(0x14..0x18))
|
||||
.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);
|
||||
|
||||
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 default_instruction_flags(&self) -> rabbitizer::InstructionFlags {
|
||||
match self.isa_extension {
|
||||
Some(extension) => rabbitizer::InstructionFlags::new_extension(extension),
|
||||
None => rabbitizer::InstructionFlags::new(IsaVersion::MIPS_III),
|
||||
}
|
||||
.with_abi(self.abi)
|
||||
}
|
||||
|
||||
fn instruction_flags(&self, diff_config: &DiffObjConfig) -> rabbitizer::InstructionFlags {
|
||||
@@ -87,7 +159,7 @@ impl ArchMips {
|
||||
};
|
||||
match isa_extension {
|
||||
Some(extension) => rabbitizer::InstructionFlags::new_extension(extension),
|
||||
None => rabbitizer::InstructionFlags::new_isa(IsaVersion::MIPS_III, None),
|
||||
None => rabbitizer::InstructionFlags::new(IsaVersion::MIPS_III),
|
||||
}
|
||||
.with_abi(match diff_config.mips_abi {
|
||||
MipsAbi::Auto => self.abi,
|
||||
@@ -99,9 +171,11 @@ impl ArchMips {
|
||||
|
||||
fn instruction_display_flags(
|
||||
&self,
|
||||
_diff_config: &DiffObjConfig,
|
||||
diff_config: &DiffObjConfig,
|
||||
) -> 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(
|
||||
@@ -119,28 +193,24 @@ impl ArchMips {
|
||||
}
|
||||
|
||||
impl Arch for ArchMips {
|
||||
fn scan_instructions(
|
||||
fn scan_instructions_internal(
|
||||
&self,
|
||||
address: u64,
|
||||
code: &[u8],
|
||||
_section_index: usize,
|
||||
_relocations: &[Relocation],
|
||||
diff_config: &DiffObjConfig,
|
||||
) -> Result<Vec<ScannedInstruction>> {
|
||||
) -> Result<Vec<InstructionRef>> {
|
||||
let instruction_flags = self.instruction_flags(diff_config);
|
||||
let start_address = address;
|
||||
let mut ops = Vec::<ScannedInstruction>::with_capacity(code.len() / 4);
|
||||
let mut cur_addr = start_address as u32;
|
||||
let mut ops = Vec::<InstructionRef>::with_capacity(code.len() / 4);
|
||||
let mut cur_addr = address as u32;
|
||||
for chunk in code.chunks_exact(4) {
|
||||
let code = self.endianness.read_u32_bytes(chunk.try_into()?);
|
||||
let vram = Vram::new(cur_addr);
|
||||
let instruction = rabbitizer::Instruction::new(code, vram, instruction_flags);
|
||||
let instruction =
|
||||
rabbitizer::Instruction::new(code, Vram::new(cur_addr), instruction_flags);
|
||||
let opcode = instruction.opcode() as u16;
|
||||
let branch_dest =
|
||||
instruction.get_branch_offset_generic().map(|o| (vram + o).inner() as u64);
|
||||
ops.push(ScannedInstruction {
|
||||
ins_ref: InstructionRef { address, size: 4, opcode },
|
||||
branch_dest,
|
||||
});
|
||||
let branch_dest = instruction.get_branch_vram_generic().map(|v| v.inner() as u64);
|
||||
ops.push(InstructionRef { address: cur_addr as u64, size: 4, opcode, branch_dest });
|
||||
cur_addr += 4;
|
||||
}
|
||||
Ok(ops)
|
||||
@@ -156,55 +226,77 @@ impl Arch for ArchMips {
|
||||
let display_flags = self.instruction_display_flags(diff_config);
|
||||
let opcode = instruction.opcode();
|
||||
cb(InstructionPart::opcode(opcode.name(), opcode as u16))?;
|
||||
let start_address = resolved.symbol.address;
|
||||
let function_range = start_address..start_address + resolved.symbol.size;
|
||||
push_args(
|
||||
&instruction,
|
||||
resolved.relocation,
|
||||
function_range,
|
||||
resolved.section_index,
|
||||
&display_flags,
|
||||
cb,
|
||||
)?;
|
||||
push_args(&instruction, resolved.relocation, &display_flags, cb)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn implcit_addend(
|
||||
fn relocation_override(
|
||||
&self,
|
||||
file: &object::File<'_>,
|
||||
section: &object::Section,
|
||||
address: u64,
|
||||
reloc: &object::Relocation,
|
||||
flags: RelocationFlags,
|
||||
) -> Result<i64> {
|
||||
let data = section.data()?;
|
||||
let code = data[address as usize..address as usize + 4].try_into()?;
|
||||
let addend = self.endianness.read_u32_bytes(code);
|
||||
Ok(match flags {
|
||||
RelocationFlags::Elf(elf::R_MIPS_32) => addend as i64,
|
||||
RelocationFlags::Elf(elf::R_MIPS_26) => ((addend & 0x03FFFFFF) << 2) as i64,
|
||||
RelocationFlags::Elf(elf::R_MIPS_HI16) => ((addend & 0x0000FFFF) << 16) as i32 as i64,
|
||||
RelocationFlags::Elf(elf::R_MIPS_LO16 | elf::R_MIPS_GOT16 | elf::R_MIPS_CALL16) => {
|
||||
(addend & 0x0000FFFF) as i16 as i64
|
||||
}
|
||||
RelocationFlags::Elf(elf::R_MIPS_GPREL16 | elf::R_MIPS_LITERAL) => {
|
||||
let object::RelocationTarget::Symbol(idx) = reloc.target() else {
|
||||
bail!("Unsupported R_MIPS_GPREL16 relocation against a non-symbol");
|
||||
};
|
||||
let sym = file.symbol_by_index(idx)?;
|
||||
relocation: &object::Relocation,
|
||||
) -> Result<Option<RelocationOverride>> {
|
||||
match relocation.flags() {
|
||||
// Handle ELF implicit relocations
|
||||
object::RelocationFlags::Elf { r_type } => {
|
||||
if relocation.has_implicit_addend() {
|
||||
// Check for paired R_MIPS_HI16 and R_MIPS_LO16 relocations.
|
||||
if let elf::R_MIPS_HI16 | elf::R_MIPS_LO16 = r_type
|
||||
&& let Some(addend) = self
|
||||
.paired_relocations
|
||||
.get(section.index().0)
|
||||
.and_then(|m| m.get(&address).copied())
|
||||
{
|
||||
return Ok(Some(RelocationOverride {
|
||||
target: RelocationOverrideTarget::Keep,
|
||||
addend,
|
||||
}));
|
||||
}
|
||||
|
||||
// if the symbol we are relocating against is in a local section we need to add
|
||||
// the ri_gp_value from .reginfo to the addend.
|
||||
if sym.section().index().is_some() {
|
||||
((addend & 0x0000FFFF) as i16 as i64) + self.ri_gp_value as i64
|
||||
let data = section.data()?;
|
||||
let code = self
|
||||
.endianness
|
||||
.read_u32_bytes(data[address as usize..address as usize + 4].try_into()?);
|
||||
let addend = match r_type {
|
||||
elf::R_MIPS_32 => code as i64,
|
||||
elf::R_MIPS_26 => ((code & 0x03FFFFFF) << 2) as i64,
|
||||
elf::R_MIPS_HI16 => ((code & 0x0000FFFF) << 16) as i32 as i64,
|
||||
elf::R_MIPS_LO16 | elf::R_MIPS_GOT16 | elf::R_MIPS_CALL16 => {
|
||||
(code & 0x0000FFFF) as i16 as i64
|
||||
}
|
||||
elf::R_MIPS_GPREL16 | elf::R_MIPS_LITERAL => {
|
||||
let object::RelocationTarget::Symbol(idx) = relocation.target() else {
|
||||
bail!("Unsupported R_MIPS_GPREL16 relocation against a non-symbol");
|
||||
};
|
||||
let sym = file.symbol_by_index(idx)?;
|
||||
|
||||
// if the symbol we are relocating against is in a local section we need to add
|
||||
// the ri_gp_value from .reginfo to the addend.
|
||||
if sym.section().index().is_some() {
|
||||
((code & 0x0000FFFF) as i16 as i64) + self.ri_gp_value as i64
|
||||
} else {
|
||||
(code & 0x0000FFFF) as i16 as i64
|
||||
}
|
||||
}
|
||||
elf::R_MIPS_PC16 => 0, // PC-relative relocation
|
||||
R_MIPS15_S3 => ((code & 0x001FFFC0) >> 3) as i64,
|
||||
flags => bail!("Unsupported MIPS implicit relocation {flags:?}"),
|
||||
};
|
||||
Ok(Some(RelocationOverride { target: RelocationOverrideTarget::Keep, addend }))
|
||||
} else {
|
||||
(addend & 0x0000FFFF) as i16 as i64
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
RelocationFlags::Elf(elf::R_MIPS_PC16) => 0, // PC-relative relocation
|
||||
RelocationFlags::Elf(R_MIPS15_S3) => ((addend & 0x001FFFC0) >> 3) as i64,
|
||||
flags => bail!("Unsupported MIPS implicit relocation {flags:?}"),
|
||||
})
|
||||
_ => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
fn demangle(&self, name: &str) -> Option<String> {
|
||||
cpp_demangle::Symbol::new(name)
|
||||
.ok()
|
||||
.and_then(|s| s.demangle(&cpp_demangle::DemangleOptions::default()).ok())
|
||||
.or_else(|| cwdemangle::demangle(name, &cwdemangle::DemangleOptions::default()))
|
||||
}
|
||||
|
||||
fn reloc_name(&self, flags: RelocationFlags) -> Option<&'static str> {
|
||||
@@ -238,13 +330,49 @@ impl Arch for ArchMips {
|
||||
_ => 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 infer_function_size(
|
||||
&self,
|
||||
symbol: &Symbol,
|
||||
section: &Section,
|
||||
next_address: u64,
|
||||
) -> Result<u64> {
|
||||
// Trim any trailing 4-byte zeroes from the end (nops)
|
||||
let mut new_address = next_address;
|
||||
while new_address >= symbol.address + 4
|
||||
&& let Some(data) = section.data_range(new_address - 4, 4)
|
||||
&& data == [0u8; 4]
|
||||
{
|
||||
new_address -= 4;
|
||||
}
|
||||
// Check if the last instruction has a delay slot, if so, include the delay slot nop
|
||||
if new_address + 4 <= next_address
|
||||
&& new_address >= symbol.address + 4
|
||||
&& let Some(data) = section.data_range(new_address - 4, 4)
|
||||
&& let instruction = rabbitizer::Instruction::new(
|
||||
self.endianness.read_u32_bytes(data.try_into().unwrap()),
|
||||
Vram::new((new_address - 4) as u32),
|
||||
self.default_instruction_flags(),
|
||||
)
|
||||
&& instruction.opcode().has_delay_slot()
|
||||
{
|
||||
new_address += 4;
|
||||
}
|
||||
Ok(new_address.saturating_sub(symbol.address))
|
||||
}
|
||||
}
|
||||
|
||||
fn push_args(
|
||||
instruction: &rabbitizer::Instruction,
|
||||
relocation: Option<ResolvedRelocation>,
|
||||
function_range: Range<u64>,
|
||||
section_index: usize,
|
||||
display_flags: &rabbitizer::InstructionDisplayFlags,
|
||||
mut arg_cb: impl FnMut(InstructionPart) -> Result<()>,
|
||||
) -> Result<()> {
|
||||
@@ -255,35 +383,23 @@ fn push_args(
|
||||
}
|
||||
|
||||
match op {
|
||||
ValuedOperand::core_immediate(imm) => {
|
||||
ValuedOperand::core_imm_i16(imm) => {
|
||||
if let Some(resolved) = relocation {
|
||||
push_reloc(resolved.relocation, &mut arg_cb)?;
|
||||
} else {
|
||||
arg_cb(match imm {
|
||||
IU16::Integer(s) => InstructionPart::signed(s),
|
||||
IU16::Unsigned(u) => InstructionPart::unsigned(u),
|
||||
})?;
|
||||
arg_cb(InstructionPart::signed(imm))?;
|
||||
}
|
||||
}
|
||||
ValuedOperand::core_imm_u16(imm) => {
|
||||
if let Some(resolved) = relocation {
|
||||
push_reloc(resolved.relocation, &mut arg_cb)?;
|
||||
} else {
|
||||
arg_cb(InstructionPart::unsigned(imm))?;
|
||||
}
|
||||
}
|
||||
ValuedOperand::core_label(..) | ValuedOperand::core_branch_target_label(..) => {
|
||||
if let Some(resolved) = relocation {
|
||||
// If the relocation target is within the current function, we can
|
||||
// convert it into a relative branch target. Note that we check
|
||||
// target_address > start_address instead of >= so that recursive
|
||||
// tail calls are not considered branch targets.
|
||||
let target_address =
|
||||
resolved.symbol.address.checked_add_signed(resolved.relocation.addend);
|
||||
if resolved.symbol.section == Some(section_index)
|
||||
&& target_address.is_some_and(|addr| {
|
||||
addr > function_range.start && addr < function_range.end
|
||||
})
|
||||
{
|
||||
// TODO move this logic up a level
|
||||
let target_address = target_address.unwrap();
|
||||
arg_cb(InstructionPart::branch_dest(target_address))?;
|
||||
} else {
|
||||
push_reloc(resolved.relocation, &mut arg_cb)?;
|
||||
}
|
||||
push_reloc(resolved.relocation, &mut arg_cb)?;
|
||||
} else if let Some(branch_dest) = instruction
|
||||
.get_branch_offset_generic()
|
||||
.map(|o| (instruction.vram() + o).inner() as u64)
|
||||
@@ -295,19 +411,20 @@ fn push_args(
|
||||
))?;
|
||||
}
|
||||
}
|
||||
ValuedOperand::core_immediate_base(imm, base) => {
|
||||
ValuedOperand::core_imm_rs(imm, base) => {
|
||||
if let Some(resolved) = relocation {
|
||||
push_reloc(resolved.relocation, &mut arg_cb)?;
|
||||
} else {
|
||||
arg_cb(InstructionPart::Arg(InstructionArg::Value(match imm {
|
||||
IU16::Integer(s) => InstructionArgValue::Signed(s as i64),
|
||||
IU16::Unsigned(u) => InstructionArgValue::Unsigned(u as u64),
|
||||
})))?;
|
||||
arg_cb(InstructionPart::Arg(InstructionArg::Value(
|
||||
InstructionArgValue::Signed(imm as i64),
|
||||
)))?;
|
||||
}
|
||||
arg_cb(InstructionPart::basic("("))?;
|
||||
arg_cb(InstructionPart::opaque(
|
||||
base.either_name(instruction.flags().abi(), display_flags.named_gpr()),
|
||||
))?;
|
||||
arg_cb(InstructionPart::opaque(base.either_name(
|
||||
instruction.flags().abi(),
|
||||
display_flags.named_gpr(),
|
||||
!display_flags.use_dollar(),
|
||||
)))?;
|
||||
arg_cb(InstructionPart::basic(")"))?;
|
||||
}
|
||||
// ValuedOperand::r5900_immediate15(..) => match relocation {
|
||||
|
||||
@@ -1,17 +1,28 @@
|
||||
use alloc::{borrow::Cow, boxed::Box, format, string::String, vec::Vec};
|
||||
use core::{ffi::CStr, fmt, fmt::Debug};
|
||||
use alloc::{
|
||||
borrow::Cow,
|
||||
boxed::Box,
|
||||
format,
|
||||
string::{String, ToString},
|
||||
vec::Vec,
|
||||
};
|
||||
use core::{
|
||||
ffi::CStr,
|
||||
fmt::{self, Debug},
|
||||
};
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use anyhow::{Result, bail};
|
||||
use encoding_rs::SHIFT_JIS;
|
||||
use object::Endian as _;
|
||||
|
||||
use crate::{
|
||||
diff::{
|
||||
display::{ContextItem, HoverItem, InstructionPart},
|
||||
DiffObjConfig,
|
||||
display::{ContextItem, HoverItem, InstructionPart},
|
||||
},
|
||||
obj::{
|
||||
InstructionArg, Object, ParsedInstruction, RelocationFlags, ResolvedInstructionRef,
|
||||
ScannedInstruction, SymbolFlagSet, SymbolKind,
|
||||
FlowAnalysisResult, InstructionArg, InstructionRef, Object, ParsedInstruction, Relocation,
|
||||
RelocationFlags, ResolvedInstructionRef, ResolvedSymbol, Section, Symbol, SymbolFlagSet,
|
||||
SymbolKind,
|
||||
},
|
||||
util::ReallySigned,
|
||||
};
|
||||
@@ -24,10 +35,13 @@ pub mod arm64;
|
||||
pub mod mips;
|
||||
#[cfg(feature = "ppc")]
|
||||
pub mod ppc;
|
||||
#[cfg(feature = "superh")]
|
||||
pub mod superh;
|
||||
#[cfg(feature = "x86")]
|
||||
pub mod x86;
|
||||
|
||||
/// Represents the type of data associated with an instruction
|
||||
#[derive(PartialEq)]
|
||||
pub enum DataType {
|
||||
Int8,
|
||||
Int16,
|
||||
@@ -41,32 +55,39 @@ pub enum DataType {
|
||||
|
||||
impl fmt::Display for DataType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
DataType::Int8 => write!(f, "Int8"),
|
||||
DataType::Int16 => write!(f, "Int16"),
|
||||
DataType::Int32 => write!(f, "Int32"),
|
||||
DataType::Int64 => write!(f, "Int64"),
|
||||
DataType::Float => write!(f, "Float"),
|
||||
DataType::Double => write!(f, "Double"),
|
||||
DataType::Bytes => write!(f, "Bytes"),
|
||||
DataType::String => write!(f, "String"),
|
||||
}
|
||||
f.write_str(match self {
|
||||
DataType::Int8 => "Int8",
|
||||
DataType::Int16 => "Int16",
|
||||
DataType::Int32 => "Int32",
|
||||
DataType::Int64 => "Int64",
|
||||
DataType::Float => "Float",
|
||||
DataType::Double => "Double",
|
||||
DataType::Bytes => "Bytes",
|
||||
DataType::String => "String",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl DataType {
|
||||
pub fn display_labels(&self, endian: object::Endianness, bytes: &[u8]) -> Vec<String> {
|
||||
let mut strs = Vec::new();
|
||||
for literal in self.display_literals(endian, bytes) {
|
||||
strs.push(format!("{}: {}", self, literal))
|
||||
for (literal, label_override) in self.display_literals(endian, bytes) {
|
||||
let label = label_override.unwrap_or_else(|| self.to_string());
|
||||
strs.push(format!("{label}: {literal}"))
|
||||
}
|
||||
strs
|
||||
}
|
||||
|
||||
pub fn display_literals(&self, endian: object::Endianness, bytes: &[u8]) -> Vec<String> {
|
||||
pub fn display_literals(
|
||||
&self,
|
||||
endian: object::Endianness,
|
||||
bytes: &[u8],
|
||||
) -> Vec<(String, Option<String>)> {
|
||||
let mut strs = Vec::new();
|
||||
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;
|
||||
}
|
||||
let mut bytes = bytes;
|
||||
@@ -85,56 +106,72 @@ impl DataType {
|
||||
match self {
|
||||
DataType::Int8 => {
|
||||
let i = i8::from_ne_bytes(bytes.try_into().unwrap());
|
||||
strs.push(format!("{:#x}", i));
|
||||
strs.push((format!("{i:#x}"), None));
|
||||
|
||||
if i < 0 {
|
||||
strs.push(format!("{:#x}", ReallySigned(i)));
|
||||
strs.push((format!("{:#x}", ReallySigned(i)), None));
|
||||
}
|
||||
}
|
||||
DataType::Int16 => {
|
||||
let i = endian.read_i16_bytes(bytes.try_into().unwrap());
|
||||
strs.push(format!("{:#x}", i));
|
||||
strs.push((format!("{i:#x}"), None));
|
||||
|
||||
if i < 0 {
|
||||
strs.push(format!("{:#x}", ReallySigned(i)));
|
||||
strs.push((format!("{:#x}", ReallySigned(i)), None));
|
||||
}
|
||||
}
|
||||
DataType::Int32 => {
|
||||
let i = endian.read_i32_bytes(bytes.try_into().unwrap());
|
||||
strs.push(format!("{:#x}", i));
|
||||
strs.push((format!("{i:#x}"), None));
|
||||
|
||||
if i < 0 {
|
||||
strs.push(format!("{:#x}", ReallySigned(i)));
|
||||
strs.push((format!("{:#x}", ReallySigned(i)), None));
|
||||
}
|
||||
}
|
||||
DataType::Int64 => {
|
||||
let i = endian.read_i64_bytes(bytes.try_into().unwrap());
|
||||
strs.push(format!("{:#x}", i));
|
||||
strs.push((format!("{i:#x}"), None));
|
||||
|
||||
if i < 0 {
|
||||
strs.push(format!("{:#x}", ReallySigned(i)));
|
||||
strs.push((format!("{:#x}", ReallySigned(i)), None));
|
||||
}
|
||||
}
|
||||
DataType::Float => {
|
||||
let bytes: [u8; 4] = bytes.try_into().unwrap();
|
||||
strs.push(format!("{:?}f", match endian {
|
||||
object::Endianness::Little => f32::from_le_bytes(bytes),
|
||||
object::Endianness::Big => f32::from_be_bytes(bytes),
|
||||
}));
|
||||
strs.push((
|
||||
format!("{:?}f", match endian {
|
||||
object::Endianness::Little => f32::from_le_bytes(bytes),
|
||||
object::Endianness::Big => f32::from_be_bytes(bytes),
|
||||
}),
|
||||
None,
|
||||
));
|
||||
}
|
||||
DataType::Double => {
|
||||
let bytes: [u8; 8] = bytes.try_into().unwrap();
|
||||
strs.push(format!("{:?}f", match endian {
|
||||
object::Endianness::Little => f64::from_le_bytes(bytes),
|
||||
object::Endianness::Big => f64::from_be_bytes(bytes),
|
||||
}));
|
||||
strs.push((
|
||||
format!("{:?}", match endian {
|
||||
object::Endianness::Little => f64::from_le_bytes(bytes),
|
||||
object::Endianness::Big => f64::from_be_bytes(bytes),
|
||||
}),
|
||||
None,
|
||||
));
|
||||
}
|
||||
DataType::Bytes => {
|
||||
strs.push(format!("{:#?}", bytes));
|
||||
strs.push((format!("{bytes:#?}"), None));
|
||||
}
|
||||
DataType::String => {
|
||||
if let Ok(cstr) = CStr::from_bytes_until_nul(bytes) {
|
||||
strs.push(format!("{:?}", cstr));
|
||||
strs.push((format!("{cstr:?}"), None));
|
||||
}
|
||||
if let Some(nul_idx) = bytes.iter().position(|&c| c == b'\0') {
|
||||
let (cow, _, had_errors) = SHIFT_JIS.decode(&bytes[..nul_idx]);
|
||||
if !had_errors {
|
||||
let str = format!("{cow:?}");
|
||||
// Only add the Shift JIS string if it's different from the ASCII string.
|
||||
if !strs.iter().any(|x| x.0 == str) {
|
||||
strs.push((str, Some("Shift JIS".into())));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -156,42 +193,108 @@ impl DataType {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Arch: Send + Sync + Debug {
|
||||
impl dyn Arch {
|
||||
/// Generate a list of instructions references (offset, size, opcode) from the given code.
|
||||
///
|
||||
/// The opcode IDs are used to generate the initial diff. Implementations should do as little
|
||||
/// parsing as possible here: just enough to identify the base instruction opcode, size, and
|
||||
/// possible branch destination (for visual representation). As needed, instructions are parsed
|
||||
/// via `process_instruction` to compare their arguments.
|
||||
fn scan_instructions(
|
||||
/// See [`scan_instructions_internal`] for more details.
|
||||
pub fn scan_instructions(
|
||||
&self,
|
||||
address: u64,
|
||||
code: &[u8],
|
||||
section_index: usize,
|
||||
resolved: ResolvedSymbol,
|
||||
diff_config: &DiffObjConfig,
|
||||
) -> Result<Vec<ScannedInstruction>>;
|
||||
) -> Result<Vec<InstructionRef>> {
|
||||
let mut result = self.scan_instructions_internal(
|
||||
resolved.symbol.address,
|
||||
resolved.data,
|
||||
resolved.section_index,
|
||||
&resolved.section.relocations,
|
||||
diff_config,
|
||||
)?;
|
||||
|
||||
let function_start = resolved.symbol.address;
|
||||
let function_end = function_start + resolved.symbol.size;
|
||||
|
||||
// Remove any branch destinations that are outside the function range
|
||||
for ins in result.iter_mut() {
|
||||
if let Some(branch_dest) = ins.branch_dest
|
||||
&& (branch_dest < function_start || branch_dest >= function_end)
|
||||
{
|
||||
ins.branch_dest = None;
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve relocation targets within the same function to branch destinations
|
||||
let mut ins_iter = result.iter_mut().peekable();
|
||||
'outer: for reloc in resolved
|
||||
.section
|
||||
.relocations
|
||||
.iter()
|
||||
.skip_while(|r| r.address < function_start)
|
||||
.take_while(|r| r.address < function_end)
|
||||
{
|
||||
let ins = loop {
|
||||
let Some(ins) = ins_iter.peek_mut() else {
|
||||
break 'outer;
|
||||
};
|
||||
if reloc.address < ins.address {
|
||||
continue 'outer;
|
||||
}
|
||||
let ins = ins_iter.next().unwrap();
|
||||
if reloc.address >= ins.address && reloc.address < ins.address + ins.size as u64 {
|
||||
break ins;
|
||||
}
|
||||
};
|
||||
// Clear existing branch destination for instructions with relocations
|
||||
ins.branch_dest = None;
|
||||
let Some(target) = resolved.obj.symbols.get(reloc.target_symbol) else {
|
||||
continue;
|
||||
};
|
||||
if target.section != Some(resolved.section_index) {
|
||||
continue;
|
||||
}
|
||||
let Some(target_address) = target.address.checked_add_signed(reloc.addend) else {
|
||||
continue;
|
||||
};
|
||||
// If the target address is within the function range, set it as a branch destination
|
||||
if target_address >= function_start && target_address < function_end {
|
||||
ins.branch_dest = Some(target_address);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Parse an instruction to gather its mnemonic and arguments for more detailed comparison.
|
||||
///
|
||||
/// This is called only when we need to compare the arguments of an instruction.
|
||||
fn process_instruction(
|
||||
pub fn process_instruction(
|
||||
&self,
|
||||
resolved: ResolvedInstructionRef,
|
||||
diff_config: &DiffObjConfig,
|
||||
) -> Result<ParsedInstruction> {
|
||||
let mut mnemonic = None;
|
||||
let mut args = Vec::with_capacity(8);
|
||||
let mut relocation_emitted = false;
|
||||
self.display_instruction(resolved, diff_config, &mut |part| {
|
||||
match part {
|
||||
InstructionPart::Opcode(m, _) => mnemonic = Some(Cow::Owned(m.into_owned())),
|
||||
InstructionPart::Arg(arg) => args.push(arg.into_static()),
|
||||
InstructionPart::Arg(arg) => {
|
||||
if arg == InstructionArg::Reloc {
|
||||
relocation_emitted = true;
|
||||
// If the relocation was resolved to a branch destination, emit that instead.
|
||||
if let Some(dest) = resolved.ins_ref.branch_dest {
|
||||
args.push(InstructionArg::BranchDest(dest));
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
args.push(arg.into_static());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
// If the instruction has a relocation, but we didn't format it in the display, add it to
|
||||
// the end of the arguments list.
|
||||
if resolved.relocation.is_some() && !args.contains(&InstructionArg::Reloc) {
|
||||
if resolved.relocation.is_some() && !relocation_emitted {
|
||||
args.push(InstructionArg::Reloc);
|
||||
}
|
||||
Ok(ParsedInstruction {
|
||||
@@ -200,6 +303,26 @@ pub trait Arch: Send + Sync + Debug {
|
||||
args,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Arch: Send + Sync + Debug {
|
||||
/// Finishes arch-specific initialization that must be done after sections have been combined.
|
||||
fn post_init(&mut self, _sections: &[Section], _symbols: &[Symbol]) {}
|
||||
|
||||
/// Generate a list of instructions references (offset, size, opcode) from the given code.
|
||||
///
|
||||
/// The opcode IDs are used to generate the initial diff. Implementations should do as little
|
||||
/// parsing as possible here: just enough to identify the base instruction opcode, size, and
|
||||
/// possible branch destination (for visual representation). As needed, instructions are parsed
|
||||
/// via `process_instruction` to compare their arguments.
|
||||
fn scan_instructions_internal(
|
||||
&self,
|
||||
address: u64,
|
||||
code: &[u8],
|
||||
section_index: usize,
|
||||
relocations: &[Relocation],
|
||||
diff_config: &DiffObjConfig,
|
||||
) -> Result<Vec<InstructionRef>>;
|
||||
|
||||
/// Format an instruction for display.
|
||||
///
|
||||
@@ -212,14 +335,37 @@ pub trait Arch: Send + Sync + Debug {
|
||||
cb: &mut dyn FnMut(InstructionPart) -> Result<()>,
|
||||
) -> Result<()>;
|
||||
|
||||
fn implcit_addend(
|
||||
/// Generate a list of fake relocations from the given code that represent pooled data accesses.
|
||||
fn generate_pooled_relocations(
|
||||
&self,
|
||||
file: &object::File<'_>,
|
||||
section: &object::Section,
|
||||
address: u64,
|
||||
relocation: &object::Relocation,
|
||||
flags: RelocationFlags,
|
||||
) -> Result<i64>;
|
||||
_address: u64,
|
||||
_code: &[u8],
|
||||
_relocations: &[Relocation],
|
||||
_symbols: &[Symbol],
|
||||
) -> Vec<Relocation> {
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
// Perform detailed data flow analysis
|
||||
fn data_flow_analysis(
|
||||
&self,
|
||||
_obj: &Object,
|
||||
_symbol: &Symbol,
|
||||
_code: &[u8],
|
||||
_relocations: &[Relocation],
|
||||
) -> Option<Box<dyn FlowAnalysisResult>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn relocation_override(
|
||||
&self,
|
||||
_file: &object::File<'_>,
|
||||
_section: &object::Section,
|
||||
_address: u64,
|
||||
_relocation: &object::Relocation,
|
||||
) -> Result<Option<RelocationOverride>> {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn demangle(&self, _name: &str) -> Option<String> { None }
|
||||
|
||||
@@ -233,7 +379,13 @@ pub trait Arch: Send + Sync + Debug {
|
||||
SymbolFlagSet::default()
|
||||
}
|
||||
|
||||
fn guess_data_type(&self, _resolved: ResolvedInstructionRef) -> Option<DataType> { None }
|
||||
fn guess_data_type(
|
||||
&self,
|
||||
_resolved: ResolvedInstructionRef,
|
||||
_bytes: &[u8],
|
||||
) -> Option<DataType> {
|
||||
None
|
||||
}
|
||||
|
||||
fn symbol_hover(&self, _obj: &Object, _symbol_index: usize) -> Vec<HoverItem> { Vec::new() }
|
||||
|
||||
@@ -254,13 +406,24 @@ pub trait Arch: Send + Sync + Debug {
|
||||
) -> Vec<ContextItem> {
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
fn infer_function_size(
|
||||
&self,
|
||||
symbol: &Symbol,
|
||||
_section: &Section,
|
||||
next_address: u64,
|
||||
) -> Result<u64> {
|
||||
Ok(next_address.saturating_sub(symbol.address))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_arch(object: &object::File) -> Result<Box<dyn Arch>> {
|
||||
use object::Object as _;
|
||||
Ok(match object.architecture() {
|
||||
#[cfg(feature = "ppc")]
|
||||
object::Architecture::PowerPc => Box::new(ppc::ArchPpc::new(object)?),
|
||||
object::Architecture::PowerPc | object::Architecture::PowerPc64 => {
|
||||
Box::new(ppc::ArchPpc::new(object)?)
|
||||
}
|
||||
#[cfg(feature = "mips")]
|
||||
object::Architecture::Mips => Box::new(mips::ArchMips::new(object)?),
|
||||
#[cfg(feature = "x86")]
|
||||
@@ -271,6 +434,8 @@ pub fn new_arch(object: &object::File) -> Result<Box<dyn Arch>> {
|
||||
object::Architecture::Arm => Box::new(arm::ArchArm::new(object)?),
|
||||
#[cfg(feature = "arm64")]
|
||||
object::Architecture::Aarch64 => Box::new(arm64::ArchArm64::new(object)?),
|
||||
#[cfg(feature = "superh")]
|
||||
object::Architecture::SuperH => Box::new(superh::ArchSuperH::new(object)?),
|
||||
arch => bail!("Unsupported architecture: {arch:?}"),
|
||||
})
|
||||
}
|
||||
@@ -283,13 +448,14 @@ impl ArchDummy {
|
||||
}
|
||||
|
||||
impl Arch for ArchDummy {
|
||||
fn scan_instructions(
|
||||
fn scan_instructions_internal(
|
||||
&self,
|
||||
_address: u64,
|
||||
_code: &[u8],
|
||||
_section_index: usize,
|
||||
_relocations: &[Relocation],
|
||||
_diff_config: &DiffObjConfig,
|
||||
) -> Result<Vec<ScannedInstruction>> {
|
||||
) -> Result<Vec<InstructionRef>> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
@@ -302,16 +468,19 @@ impl Arch for ArchDummy {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn implcit_addend(
|
||||
&self,
|
||||
_file: &object::File<'_>,
|
||||
_section: &object::Section,
|
||||
_address: u64,
|
||||
_relocation: &object::Relocation,
|
||||
_flags: RelocationFlags,
|
||||
) -> Result<i64> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn data_reloc_size(&self, _flags: RelocationFlags) -> usize { 0 }
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum RelocationOverrideTarget {
|
||||
Keep,
|
||||
Skip,
|
||||
Symbol(object::SymbolIndex),
|
||||
Section(object::SectionIndex),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct RelocationOverride {
|
||||
pub target: RelocationOverrideTarget,
|
||||
pub addend: i64,
|
||||
}
|
||||
|
||||
@@ -1,831 +0,0 @@
|
||||
use alloc::{
|
||||
collections::BTreeMap,
|
||||
string::{String, ToString},
|
||||
vec,
|
||||
vec::Vec,
|
||||
};
|
||||
|
||||
use anyhow::{bail, ensure, Result};
|
||||
use cwextab::{decode_extab, ExceptionTableData};
|
||||
use flagset::Flags;
|
||||
use object::{elf, Object as _, ObjectSection as _, ObjectSymbol as _};
|
||||
|
||||
use crate::{
|
||||
arch::{Arch, DataType},
|
||||
diff::{
|
||||
display::{ContextItem, HoverItem, HoverItemColor, InstructionPart, SymbolNavigationKind},
|
||||
DiffObjConfig,
|
||||
},
|
||||
obj::{
|
||||
InstructionRef, Object, Relocation, RelocationFlags, ResolvedInstructionRef,
|
||||
ResolvedRelocation, ScannedInstruction, SymbolFlag, SymbolFlagSet,
|
||||
},
|
||||
};
|
||||
|
||||
// Relative relocation, can be Simm, Offset or BranchDest
|
||||
fn is_relative_arg(arg: &ppc750cl::Argument) -> bool {
|
||||
matches!(
|
||||
arg,
|
||||
ppc750cl::Argument::Simm(_)
|
||||
| ppc750cl::Argument::Offset(_)
|
||||
| ppc750cl::Argument::BranchDest(_)
|
||||
)
|
||||
}
|
||||
|
||||
// Relative or absolute relocation, can be Uimm, Simm or Offset
|
||||
fn is_rel_abs_arg(arg: &ppc750cl::Argument) -> bool {
|
||||
matches!(
|
||||
arg,
|
||||
ppc750cl::Argument::Uimm(_) | ppc750cl::Argument::Simm(_) | ppc750cl::Argument::Offset(_)
|
||||
)
|
||||
}
|
||||
|
||||
fn is_offset_arg(arg: &ppc750cl::Argument) -> bool { matches!(arg, ppc750cl::Argument::Offset(_)) }
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ArchPpc {
|
||||
/// Exception info
|
||||
pub extab: Option<BTreeMap<usize, ExceptionInfo>>,
|
||||
}
|
||||
|
||||
impl ArchPpc {
|
||||
pub fn new(file: &object::File) -> Result<Self> {
|
||||
Ok(Self { extab: decode_exception_info(file)? })
|
||||
}
|
||||
|
||||
fn parse_ins_ref(&self, resolved: ResolvedInstructionRef) -> Result<ppc750cl::Ins> {
|
||||
let mut code = u32::from_be_bytes(resolved.code.try_into()?);
|
||||
if let Some(reloc) = resolved.relocation {
|
||||
code = zero_reloc(code, reloc.relocation);
|
||||
}
|
||||
let op = ppc750cl::Opcode::from(resolved.ins_ref.opcode as u8);
|
||||
Ok(ppc750cl::Ins { code, op })
|
||||
}
|
||||
|
||||
fn find_reloc_arg(
|
||||
&self,
|
||||
ins: &ppc750cl::ParsedIns,
|
||||
resolved: Option<ResolvedRelocation>,
|
||||
) -> Option<usize> {
|
||||
match resolved?.relocation.flags {
|
||||
RelocationFlags::Elf(elf::R_PPC_EMB_SDA21) => Some(1),
|
||||
RelocationFlags::Elf(elf::R_PPC_REL24 | elf::R_PPC_REL14) => {
|
||||
ins.args.iter().rposition(is_relative_arg)
|
||||
}
|
||||
RelocationFlags::Elf(
|
||||
elf::R_PPC_ADDR16_HI | elf::R_PPC_ADDR16_HA | elf::R_PPC_ADDR16_LO,
|
||||
) => ins.args.iter().rposition(is_rel_abs_arg),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Arch for ArchPpc {
|
||||
fn scan_instructions(
|
||||
&self,
|
||||
address: u64,
|
||||
code: &[u8],
|
||||
_section_index: usize,
|
||||
_diff_config: &DiffObjConfig,
|
||||
) -> Result<Vec<ScannedInstruction>> {
|
||||
ensure!(code.len() & 3 == 0, "Code length must be a multiple of 4");
|
||||
let ins_count = code.len() / 4;
|
||||
let mut insts = Vec::<ScannedInstruction>::with_capacity(ins_count);
|
||||
for (cur_addr, ins) in ppc750cl::InsIter::new(code, address as u32) {
|
||||
insts.push(ScannedInstruction {
|
||||
ins_ref: InstructionRef {
|
||||
address: cur_addr as u64,
|
||||
size: 4,
|
||||
opcode: u8::from(ins.op) as u16,
|
||||
},
|
||||
branch_dest: ins.branch_dest(cur_addr).map(u64::from),
|
||||
});
|
||||
}
|
||||
Ok(insts)
|
||||
}
|
||||
|
||||
fn display_instruction(
|
||||
&self,
|
||||
resolved: ResolvedInstructionRef,
|
||||
_diff_config: &DiffObjConfig,
|
||||
cb: &mut dyn FnMut(InstructionPart) -> Result<()>,
|
||||
) -> Result<()> {
|
||||
let ins = self.parse_ins_ref(resolved)?.simplified();
|
||||
|
||||
cb(InstructionPart::opcode(ins.mnemonic, resolved.ins_ref.opcode))?;
|
||||
|
||||
let reloc_arg = self.find_reloc_arg(&ins, resolved.relocation);
|
||||
|
||||
let mut writing_offset = false;
|
||||
for (idx, arg) in ins.args_iter().enumerate() {
|
||||
if idx > 0 && !writing_offset {
|
||||
cb(InstructionPart::separator())?;
|
||||
}
|
||||
|
||||
if reloc_arg == Some(idx) {
|
||||
let reloc = resolved.relocation.unwrap();
|
||||
display_reloc(reloc, cb)?;
|
||||
// For @sda21, we can omit the register argument
|
||||
if matches!(reloc.relocation.flags, RelocationFlags::Elf(elf::R_PPC_EMB_SDA21))
|
||||
// Sanity check: the next argument should be r0
|
||||
&& matches!(ins.args.get(idx + 1), Some(ppc750cl::Argument::GPR(ppc750cl::GPR(0))))
|
||||
{
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
match arg {
|
||||
ppc750cl::Argument::Simm(simm) => cb(InstructionPart::signed(simm.0)),
|
||||
ppc750cl::Argument::Uimm(uimm) => cb(InstructionPart::unsigned(uimm.0)),
|
||||
ppc750cl::Argument::Offset(offset) => cb(InstructionPart::signed(offset.0)),
|
||||
ppc750cl::Argument::BranchDest(dest) => cb(InstructionPart::branch_dest(
|
||||
(resolved.ins_ref.address as u32).wrapping_add_signed(dest.0),
|
||||
)),
|
||||
_ => cb(InstructionPart::opaque(arg.to_string())),
|
||||
}?;
|
||||
}
|
||||
|
||||
if writing_offset {
|
||||
cb(InstructionPart::basic(")"))?;
|
||||
writing_offset = false;
|
||||
}
|
||||
if is_offset_arg(arg) {
|
||||
cb(InstructionPart::basic("("))?;
|
||||
writing_offset = true;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn implcit_addend(
|
||||
&self,
|
||||
_file: &object::File<'_>,
|
||||
_section: &object::Section,
|
||||
address: u64,
|
||||
_relocation: &object::Relocation,
|
||||
flags: RelocationFlags,
|
||||
) -> Result<i64> {
|
||||
bail!("Unsupported PPC implicit relocation {:#x}:{:?}", address, flags)
|
||||
}
|
||||
|
||||
fn demangle(&self, name: &str) -> Option<String> {
|
||||
cwdemangle::demangle(name, &cwdemangle::DemangleOptions::default())
|
||||
}
|
||||
|
||||
fn reloc_name(&self, flags: RelocationFlags) -> Option<&'static str> {
|
||||
match flags {
|
||||
RelocationFlags::Elf(r_type) => match r_type {
|
||||
elf::R_PPC_NONE => Some("R_PPC_NONE"), // We use this for fake pool relocs
|
||||
elf::R_PPC_ADDR16_LO => Some("R_PPC_ADDR16_LO"),
|
||||
elf::R_PPC_ADDR16_HI => Some("R_PPC_ADDR16_HI"),
|
||||
elf::R_PPC_ADDR16_HA => Some("R_PPC_ADDR16_HA"),
|
||||
elf::R_PPC_EMB_SDA21 => Some("R_PPC_EMB_SDA21"),
|
||||
elf::R_PPC_ADDR32 => Some("R_PPC_ADDR32"),
|
||||
elf::R_PPC_UADDR32 => Some("R_PPC_UADDR32"),
|
||||
elf::R_PPC_REL24 => Some("R_PPC_REL24"),
|
||||
elf::R_PPC_REL14 => Some("R_PPC_REL14"),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn data_reloc_size(&self, flags: RelocationFlags) -> usize {
|
||||
match flags {
|
||||
RelocationFlags::Elf(r_type) => match r_type {
|
||||
elf::R_PPC_ADDR32 => 4,
|
||||
elf::R_PPC_UADDR32 => 4,
|
||||
_ => 1,
|
||||
},
|
||||
_ => 1,
|
||||
}
|
||||
}
|
||||
|
||||
fn extra_symbol_flags(&self, symbol: &object::Symbol) -> SymbolFlagSet {
|
||||
if self.extab.as_ref().is_some_and(|extab| extab.contains_key(&(symbol.index().0 - 1))) {
|
||||
SymbolFlag::HasExtra.into()
|
||||
} else {
|
||||
SymbolFlag::none()
|
||||
}
|
||||
}
|
||||
|
||||
fn guess_data_type(&self, resolved: ResolvedInstructionRef) -> Option<DataType> {
|
||||
if resolved.relocation.is_some_and(|r| r.symbol.name.starts_with("@stringBase")) {
|
||||
return Some(DataType::String);
|
||||
}
|
||||
let opcode = ppc750cl::Opcode::from(resolved.ins_ref.opcode as u8);
|
||||
guess_data_type_from_load_store_inst_op(opcode)
|
||||
}
|
||||
|
||||
fn symbol_hover(&self, _obj: &Object, symbol_index: usize) -> Vec<HoverItem> {
|
||||
let mut out = Vec::new();
|
||||
if let Some(extab) = self.extab_for_symbol(symbol_index) {
|
||||
out.push(HoverItem::Text {
|
||||
label: "extab symbol".into(),
|
||||
value: extab.etb_symbol.name.clone(),
|
||||
color: HoverItemColor::Special,
|
||||
});
|
||||
out.push(HoverItem::Text {
|
||||
label: "extabindex symbol".into(),
|
||||
value: extab.eti_symbol.name.clone(),
|
||||
color: HoverItemColor::Special,
|
||||
});
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
fn symbol_context(&self, _obj: &Object, symbol_index: usize) -> Vec<ContextItem> {
|
||||
let mut out = Vec::new();
|
||||
if let Some(_extab) = self.extab_for_symbol(symbol_index) {
|
||||
out.push(ContextItem::Navigate {
|
||||
label: "Decode exception table".to_string(),
|
||||
symbol_index,
|
||||
kind: SymbolNavigationKind::Extab,
|
||||
});
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
fn instruction_hover(&self, _obj: &Object, resolved: ResolvedInstructionRef) -> Vec<HoverItem> {
|
||||
let Ok(ins) = self.parse_ins_ref(resolved) else {
|
||||
return Vec::new();
|
||||
};
|
||||
let orig = ins.basic().to_string();
|
||||
let simplified = ins.simplified().to_string();
|
||||
let show_orig = orig != simplified;
|
||||
let rlwinm_decoded = rlwinmdec::decode(&orig);
|
||||
let mut out = Vec::with_capacity(2);
|
||||
if show_orig {
|
||||
out.push(HoverItem::Text {
|
||||
label: "Original".into(),
|
||||
value: orig,
|
||||
color: HoverItemColor::Normal,
|
||||
});
|
||||
}
|
||||
if let Some(decoded) = rlwinm_decoded {
|
||||
for line in decoded.lines() {
|
||||
out.push(HoverItem::Text {
|
||||
label: Default::default(),
|
||||
value: line.to_string(),
|
||||
color: HoverItemColor::Special,
|
||||
});
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
fn instruction_context(
|
||||
&self,
|
||||
_obj: &Object,
|
||||
resolved: ResolvedInstructionRef,
|
||||
) -> Vec<ContextItem> {
|
||||
let Ok(ins) = self.parse_ins_ref(resolved) else {
|
||||
return Vec::new();
|
||||
};
|
||||
let orig = ins.basic().to_string();
|
||||
let simplified = ins.simplified().to_string();
|
||||
let show_orig = orig != simplified;
|
||||
let mut out = Vec::with_capacity(2);
|
||||
out.push(ContextItem::Copy { value: simplified, label: None });
|
||||
if show_orig {
|
||||
out.push(ContextItem::Copy { value: orig, label: Some("original".to_string()) });
|
||||
}
|
||||
out
|
||||
}
|
||||
}
|
||||
|
||||
impl ArchPpc {
|
||||
pub fn extab_for_symbol(&self, symbol_index: usize) -> Option<&ExceptionInfo> {
|
||||
self.extab.as_ref()?.get(&symbol_index)
|
||||
}
|
||||
}
|
||||
|
||||
fn zero_reloc(code: u32, reloc: &Relocation) -> u32 {
|
||||
match reloc.flags {
|
||||
RelocationFlags::Elf(elf::R_PPC_EMB_SDA21) => code & !0x1FFFFF,
|
||||
RelocationFlags::Elf(elf::R_PPC_REL24) => code & !0x3FFFFFC,
|
||||
RelocationFlags::Elf(elf::R_PPC_REL14) => code & !0xFFFC,
|
||||
RelocationFlags::Elf(
|
||||
elf::R_PPC_ADDR16_HI | elf::R_PPC_ADDR16_HA | elf::R_PPC_ADDR16_LO,
|
||||
) => code & !0xFFFF,
|
||||
_ => code,
|
||||
}
|
||||
}
|
||||
|
||||
fn display_reloc(
|
||||
resolved: ResolvedRelocation,
|
||||
cb: &mut dyn FnMut(InstructionPart) -> Result<()>,
|
||||
) -> Result<()> {
|
||||
match resolved.relocation.flags {
|
||||
RelocationFlags::Elf(r_type) => match r_type {
|
||||
elf::R_PPC_ADDR16_LO => {
|
||||
cb(InstructionPart::reloc())?;
|
||||
cb(InstructionPart::basic("@l"))?;
|
||||
}
|
||||
elf::R_PPC_ADDR16_HI => {
|
||||
cb(InstructionPart::reloc())?;
|
||||
cb(InstructionPart::basic("@h"))?;
|
||||
}
|
||||
elf::R_PPC_ADDR16_HA => {
|
||||
cb(InstructionPart::reloc())?;
|
||||
cb(InstructionPart::basic("@ha"))?;
|
||||
}
|
||||
elf::R_PPC_EMB_SDA21 => {
|
||||
cb(InstructionPart::reloc())?;
|
||||
cb(InstructionPart::basic("@sda21"))?;
|
||||
}
|
||||
elf::R_PPC_ADDR32 | elf::R_PPC_UADDR32 | elf::R_PPC_REL24 | elf::R_PPC_REL14 => {
|
||||
cb(InstructionPart::reloc())?;
|
||||
}
|
||||
elf::R_PPC_NONE => {
|
||||
// Fake pool relocation.
|
||||
cb(InstructionPart::basic("<"))?;
|
||||
cb(InstructionPart::reloc())?;
|
||||
cb(InstructionPart::basic(">"))?;
|
||||
}
|
||||
_ => cb(InstructionPart::reloc())?,
|
||||
},
|
||||
_ => cb(InstructionPart::reloc())?,
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ExtabSymbolRef {
|
||||
pub original_index: usize,
|
||||
pub name: String,
|
||||
pub demangled_name: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ExceptionInfo {
|
||||
pub eti_symbol: ExtabSymbolRef,
|
||||
pub etb_symbol: ExtabSymbolRef,
|
||||
pub data: ExceptionTableData,
|
||||
pub dtors: Vec<ExtabSymbolRef>,
|
||||
}
|
||||
|
||||
fn decode_exception_info(
|
||||
file: &object::File<'_>,
|
||||
) -> Result<Option<BTreeMap<usize, ExceptionInfo>>> {
|
||||
let Some(extab_section) = file.section_by_name("extab") else {
|
||||
return Ok(None);
|
||||
};
|
||||
let Some(extabindex_section) = file.section_by_name("extabindex") else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let mut result = BTreeMap::new();
|
||||
let extab_relocations =
|
||||
extab_section.relocations().collect::<BTreeMap<u64, object::Relocation>>();
|
||||
let extabindex_relocations =
|
||||
extabindex_section.relocations().collect::<BTreeMap<u64, object::Relocation>>();
|
||||
|
||||
for extabindex in file.symbols().filter(|symbol| {
|
||||
symbol.section_index() == Some(extabindex_section.index())
|
||||
&& symbol.kind() == object::SymbolKind::Data
|
||||
}) {
|
||||
if extabindex.size() != 12 {
|
||||
log::warn!("Invalid extabindex entry size {}", extabindex.size());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Each extabindex entry has two relocations:
|
||||
// - 0x0: The function that the exception table is for
|
||||
// - 0x8: The relevant entry in extab section
|
||||
let Some(extab_func_reloc) = extabindex_relocations.get(&extabindex.address()) else {
|
||||
log::warn!("Failed to find function relocation for extabindex entry");
|
||||
continue;
|
||||
};
|
||||
let Some(extab_reloc) = extabindex_relocations.get(&(extabindex.address() + 8)) else {
|
||||
log::warn!("Failed to find extab relocation for extabindex entry");
|
||||
continue;
|
||||
};
|
||||
|
||||
// Resolve the function and extab symbols
|
||||
let Some(extab_func) = relocation_symbol(file, extab_func_reloc)? else {
|
||||
log::warn!("Failed to find function symbol for extabindex entry");
|
||||
continue;
|
||||
};
|
||||
let extab_func_name = extab_func.name()?;
|
||||
let Some(extab) = relocation_symbol(file, extab_reloc)? else {
|
||||
log::warn!("Failed to find extab symbol for extabindex entry");
|
||||
continue;
|
||||
};
|
||||
|
||||
let extab_start_addr = extab.address() - extab_section.address();
|
||||
let extab_end_addr = extab_start_addr + extab.size();
|
||||
|
||||
// All relocations in the extab section are dtors
|
||||
let mut dtors: Vec<ExtabSymbolRef> = vec![];
|
||||
for (_, reloc) in extab_relocations.range(extab_start_addr..extab_end_addr) {
|
||||
let Some(symbol) = relocation_symbol(file, reloc)? else {
|
||||
log::warn!("Failed to find symbol for extab relocation");
|
||||
continue;
|
||||
};
|
||||
dtors.push(make_symbol_ref(&symbol)?);
|
||||
}
|
||||
|
||||
// Decode the extab data
|
||||
let Some(extab_data) = extab_section.data_range(extab_start_addr, extab.size())? else {
|
||||
log::warn!("Failed to get extab data for function {}", extab_func_name);
|
||||
continue;
|
||||
};
|
||||
let data = match decode_extab(extab_data) {
|
||||
Ok(decoded_data) => decoded_data,
|
||||
Err(e) => {
|
||||
log::warn!(
|
||||
"Exception table decoding failed for function {}, reason: {}",
|
||||
extab_func_name,
|
||||
e.to_string()
|
||||
);
|
||||
return Ok(None);
|
||||
}
|
||||
};
|
||||
|
||||
//Add the new entry to the list
|
||||
result.insert(extab_func.index().0 - 1, ExceptionInfo {
|
||||
eti_symbol: make_symbol_ref(&extabindex)?,
|
||||
etb_symbol: make_symbol_ref(&extab)?,
|
||||
data,
|
||||
dtors,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(Some(result))
|
||||
}
|
||||
|
||||
fn relocation_symbol<'data, 'file>(
|
||||
file: &'file object::File<'data>,
|
||||
relocation: &object::Relocation,
|
||||
) -> Result<Option<object::Symbol<'data, 'file>>> {
|
||||
let addend = relocation.addend();
|
||||
match relocation.target() {
|
||||
object::RelocationTarget::Symbol(idx) => {
|
||||
ensure!(addend == 0, "Symbol relocations must have zero addend");
|
||||
Ok(Some(file.symbol_by_index(idx)?))
|
||||
}
|
||||
object::RelocationTarget::Section(idx) => {
|
||||
ensure!(addend >= 0, "Section relocations must have non-negative addend");
|
||||
let addend = addend as u64;
|
||||
Ok(file
|
||||
.symbols()
|
||||
.find(|symbol| symbol.section_index() == Some(idx) && symbol.address() == addend))
|
||||
}
|
||||
target => bail!("Unsupported relocation target: {target:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
fn make_symbol_ref(symbol: &object::Symbol) -> Result<ExtabSymbolRef> {
|
||||
let name = symbol.name()?.to_string();
|
||||
let demangled_name = cwdemangle::demangle(&name, &cwdemangle::DemangleOptions::default());
|
||||
Ok(ExtabSymbolRef { original_index: symbol.index().0 - 1, name, demangled_name })
|
||||
}
|
||||
|
||||
fn guess_data_type_from_load_store_inst_op(inst_op: ppc750cl::Opcode) -> Option<DataType> {
|
||||
use ppc750cl::Opcode;
|
||||
match inst_op {
|
||||
Opcode::Lbz | Opcode::Lbzu | Opcode::Lbzux | Opcode::Lbzx => Some(DataType::Int8),
|
||||
Opcode::Lhz | Opcode::Lhzu | Opcode::Lhzux | Opcode::Lhzx => Some(DataType::Int16),
|
||||
Opcode::Lha | Opcode::Lhau | Opcode::Lhaux | Opcode::Lhax => Some(DataType::Int16),
|
||||
Opcode::Lwz | Opcode::Lwzu | Opcode::Lwzux | Opcode::Lwzx => Some(DataType::Int32),
|
||||
Opcode::Lfs | Opcode::Lfsu | Opcode::Lfsux | Opcode::Lfsx => Some(DataType::Float),
|
||||
Opcode::Lfd | Opcode::Lfdu | Opcode::Lfdux | Opcode::Lfdx => Some(DataType::Double),
|
||||
|
||||
Opcode::Stb | Opcode::Stbu | Opcode::Stbux | Opcode::Stbx => Some(DataType::Int8),
|
||||
Opcode::Sth | Opcode::Sthu | Opcode::Sthux | Opcode::Sthx => Some(DataType::Int16),
|
||||
Opcode::Stw | Opcode::Stwu | Opcode::Stwux | Opcode::Stwx => Some(DataType::Int32),
|
||||
Opcode::Stfs | Opcode::Stfsu | Opcode::Stfsux | Opcode::Stfsx => Some(DataType::Float),
|
||||
Opcode::Stfd | Opcode::Stfdu | Opcode::Stfdux | Opcode::Stfdx => Some(DataType::Double),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
// Given an instruction, determine if it could accessing data at the address in a register.
|
||||
// If so, return the offset added to the register's address, the register containing that address,
|
||||
// and (optionally) which destination register the address is being copied into.
|
||||
#[expect(unused)]
|
||||
fn get_offset_and_addr_gpr_for_possible_pool_reference(
|
||||
opcode: ppc750cl::Opcode,
|
||||
simplified: &ppc750cl::ParsedIns,
|
||||
) -> Option<(i16, ppc750cl::GPR, Option<ppc750cl::GPR>)> {
|
||||
use ppc750cl::{Argument, Opcode};
|
||||
let args = &simplified.args;
|
||||
if guess_data_type_from_load_store_inst_op(opcode).is_some() {
|
||||
match (args[1], args[2]) {
|
||||
(Argument::Offset(offset), Argument::GPR(addr_src_gpr)) => {
|
||||
// e.g. lwz. Immediate offset.
|
||||
Some((offset.0, addr_src_gpr, None))
|
||||
}
|
||||
(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.
|
||||
// Treat the offset as being 0 in this case to show the first element 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
|
||||
// this would be much more complicated, so it's not currently done.
|
||||
Some((0, addr_src_gpr, None))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
// If it's not a load/store instruction, there's two more possibilities we need to handle.
|
||||
// 1. It could be loading a pointer to a string.
|
||||
// 2. It could be moving the relocation address plus an offset into a different register to
|
||||
// load from later.
|
||||
// If either of these match, we also want to return the destination register that the
|
||||
// address is being copied into so that we can detect any future references to that new
|
||||
// register as well.
|
||||
match (opcode, args[0], args[1], args[2]) {
|
||||
(
|
||||
Opcode::Addi,
|
||||
Argument::GPR(addr_dst_gpr),
|
||||
Argument::GPR(addr_src_gpr),
|
||||
Argument::Simm(simm),
|
||||
) => Some((simm.0, addr_src_gpr, Some(addr_dst_gpr))),
|
||||
(
|
||||
// `mr` or `mr.`
|
||||
Opcode::Or,
|
||||
Argument::GPR(addr_dst_gpr),
|
||||
Argument::GPR(addr_src_gpr),
|
||||
Argument::None,
|
||||
) => Some((0, addr_src_gpr, Some(addr_dst_gpr))),
|
||||
(
|
||||
Opcode::Add,
|
||||
Argument::GPR(addr_dst_gpr),
|
||||
Argument::GPR(addr_src_gpr),
|
||||
Argument::GPR(_offset_gpr),
|
||||
) => Some((0, addr_src_gpr, Some(addr_dst_gpr))),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
#[expect(unused)]
|
||||
fn clear_overwritten_gprs(ins: ppc750cl::Ins, gpr_pool_relocs: &mut BTreeMap<u8, Relocation>) {
|
||||
use ppc750cl::{Argument, Arguments, Opcode};
|
||||
let mut def_args = Arguments::default();
|
||||
ins.parse_defs(&mut def_args);
|
||||
for arg in def_args {
|
||||
if let Argument::GPR(gpr) = arg {
|
||||
if ins.op == Opcode::Lmw {
|
||||
// `lmw` overwrites all registers from rd to r31.
|
||||
// ppc750cl only returns rd itself, so we manually clear the rest of them.
|
||||
for reg in gpr.0..31 {
|
||||
gpr_pool_relocs.remove(®);
|
||||
}
|
||||
break;
|
||||
}
|
||||
gpr_pool_relocs.remove(&gpr.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO
|
||||
// // 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
|
||||
// // 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.
|
||||
// // Also, if this instruction is accessing the middle of a symbol instead of the start, we add an
|
||||
// // addend to indicate that.
|
||||
// fn make_fake_pool_reloc(offset: i16, cur_addr: u32, pool_reloc: &Relocation) -> Option<Relocation> {
|
||||
// let offset_from_pool = pool_reloc.addend + offset as i64;
|
||||
// let target_address = pool_reloc.sy.address.checked_add_signed(offset_from_pool)?;
|
||||
// let target;
|
||||
// let addend;
|
||||
// if pool_reloc.target.orig_section_index.is_some() {
|
||||
// // If the target symbol is within this current object, then we also need to create a fake
|
||||
// // target symbol to go inside our fake relocation. This is because we don't have access to
|
||||
// // list of all symbols in this section, so we can't find the real symbol within the pool
|
||||
// // based on its address yet. Instead we make a placeholder that has the correct
|
||||
// // `orig_section_index` and `address` fields, and then later on when this information is
|
||||
// // displayed to the user, we can find the real symbol by searching through the object's
|
||||
// // section's symbols for one that contains this address.
|
||||
// target = ObjSymbol {
|
||||
// name: "".to_string(),
|
||||
// demangled_name: None,
|
||||
// address: target_address,
|
||||
// section_address: 0,
|
||||
// size: 0,
|
||||
// size_known: false,
|
||||
// kind: Default::default(),
|
||||
// flags: Default::default(),
|
||||
// orig_section_index: pool_reloc.target.orig_section_index,
|
||||
// virtual_address: None,
|
||||
// original_index: None,
|
||||
// bytes: vec![],
|
||||
// };
|
||||
// // The addend is also fake because we don't know yet if the `target_address` here is the exact
|
||||
// // start of the symbol or if it's in the middle of it.
|
||||
// addend = 0;
|
||||
// } else {
|
||||
// // But if the target symbol is in a different object (extern), then we simply copy the pool
|
||||
// // relocation's target. This is because it won't be possible to locate the actual symbol
|
||||
// // later on based only off of an offset without knowing the object or section it's in. And
|
||||
// // doing that for external symbols would also be unnecessary, because when the compiler
|
||||
// // 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
|
||||
// // offset into it works fine in this case, no fake symbol to hold an address is necessary.
|
||||
// target = pool_reloc.target.clone();
|
||||
// addend = pool_reloc.addend;
|
||||
// };
|
||||
// Some(ObjReloc {
|
||||
// flags: RelocationFlags::Elf { r_type: elf::R_PPC_NONE },
|
||||
// address: cur_addr as u64,
|
||||
// target,
|
||||
// addend,
|
||||
// })
|
||||
// }
|
||||
//
|
||||
// // Searches through all instructions in a function, determining which registers have the addresses
|
||||
// // of pooled data relocations in them, finding which instructions load data from those addresses,
|
||||
// // and constructing a mapping of the address of that instruction to a "fake pool relocation" that
|
||||
// // simulates what that instruction's relocation would look like if data hadn't been pooled.
|
||||
// // This method tries to follow the function's proper control flow. It keeps track of a queue of
|
||||
// // states it hasn't traversed yet, where each state holds an instruction address and a HashMap of
|
||||
// // which registers hold which pool relocations at that point.
|
||||
// // When a conditional or unconditional branch is encountered, the destination of the branch is added
|
||||
// // to the queue. Conditional branches will traverse both the path where the branch is taken and the
|
||||
// // one where it's not. Unconditional branches only follow the branch, ignoring any code immediately
|
||||
// // after the branch instruction.
|
||||
// // Limitations: This method cannot read jump tables. This is because the jump tables are located in
|
||||
// // the .data section, but ObjArch.process_code only has access to the .text section. In order to
|
||||
// // work around this limitation and avoid completely missing most code inside switch statements that
|
||||
// // use jump tables, we instead guess that any parts of a function we missed were switch cases, and
|
||||
// // traverse them as if the last `bctr` before that address had branched there. This should be fairly
|
||||
// // accurate in practice - in testing the only instructions it seems to miss are double branches that
|
||||
// // the compiler generates in error which can never be reached during normal execution anyway.
|
||||
// fn generate_fake_pool_reloc_for_addr_mapping(
|
||||
// func_address: u64,
|
||||
// code: &[u8],
|
||||
// relocations: &[ObjReloc],
|
||||
// ) -> BTreeMap<u32, ObjReloc> {
|
||||
// let mut visited_ins_addrs = BTreeSet::new();
|
||||
// let mut pool_reloc_for_addr = BTreeMap::new();
|
||||
// let mut ins_iters_with_gpr_state =
|
||||
// vec![(InsIter::new(code, func_address as u32), BTreeMap::new())];
|
||||
// let mut gpr_state_at_bctr = BTreeMap::new();
|
||||
// while let Some((ins_iter, mut gpr_pool_relocs)) = ins_iters_with_gpr_state.pop() {
|
||||
// for (cur_addr, ins) in ins_iter {
|
||||
// if visited_ins_addrs.contains(&cur_addr) {
|
||||
// // Avoid getting stuck in an infinite loop when following looping branches.
|
||||
// break;
|
||||
// }
|
||||
// visited_ins_addrs.insert(cur_addr);
|
||||
//
|
||||
// let simplified = ins.simplified();
|
||||
//
|
||||
// // First handle traversing the function's control flow.
|
||||
// let mut branch_dest = None;
|
||||
// for arg in simplified.args_iter() {
|
||||
// if let Argument::BranchDest(dest) = arg {
|
||||
// let dest = cur_addr.wrapping_add_signed(dest.0);
|
||||
// branch_dest = Some(dest);
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// if let Some(branch_dest) = branch_dest {
|
||||
// if branch_dest >= func_address as u32
|
||||
// && (branch_dest - func_address as u32) < code.len() as u32
|
||||
// {
|
||||
// let dest_offset_into_func = branch_dest - func_address as u32;
|
||||
// let dest_code_slice = &code[dest_offset_into_func as usize..];
|
||||
// match ins.op {
|
||||
// Opcode::Bc => {
|
||||
// // Conditional branch.
|
||||
// // Add the branch destination to the queue to do later.
|
||||
// ins_iters_with_gpr_state.push((
|
||||
// InsIter::new(dest_code_slice, branch_dest),
|
||||
// gpr_pool_relocs.clone(),
|
||||
// ));
|
||||
// // Then continue on with the current iterator.
|
||||
// }
|
||||
// Opcode::B => {
|
||||
// if simplified.mnemonic != "bl" {
|
||||
// // Unconditional branch.
|
||||
// // Add the branch destination to the queue.
|
||||
// ins_iters_with_gpr_state.push((
|
||||
// InsIter::new(dest_code_slice, branch_dest),
|
||||
// gpr_pool_relocs.clone(),
|
||||
// ));
|
||||
// // Break out of the current iterator so we can do the newly added one.
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// _ => unreachable!(),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// if let Opcode::Bcctr = ins.op {
|
||||
// if simplified.mnemonic == "bctr" {
|
||||
// // Unconditional branch to count register.
|
||||
// // Likely a jump table.
|
||||
// gpr_state_at_bctr.insert(cur_addr, gpr_pool_relocs.clone());
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // 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 Some(reloc) = reloc {
|
||||
// // This instruction has a real relocation, so it may be a pool load we want to keep
|
||||
// // track of.
|
||||
// let args = &simplified.args;
|
||||
// match (ins.op, args[0], args[1], args[2]) {
|
||||
// (
|
||||
// // `lis` + `addi`
|
||||
// Opcode::Addi,
|
||||
// Argument::GPR(addr_dst_gpr),
|
||||
// Argument::GPR(_addr_src_gpr),
|
||||
// Argument::Simm(_simm),
|
||||
// ) => {
|
||||
// gpr_pool_relocs.insert(addr_dst_gpr.0, reloc.clone());
|
||||
// }
|
||||
// (
|
||||
// // `lis` + `ori`
|
||||
// Opcode::Ori,
|
||||
// Argument::GPR(addr_dst_gpr),
|
||||
// Argument::GPR(_addr_src_gpr),
|
||||
// Argument::Uimm(_uimm),
|
||||
// ) => {
|
||||
// gpr_pool_relocs.insert(addr_dst_gpr.0, reloc.clone());
|
||||
// }
|
||||
// (Opcode::B, _, _, _) => {
|
||||
// if simplified.mnemonic == "bl" {
|
||||
// // 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);
|
||||
// for gpr in 3..12 {
|
||||
// gpr_pool_relocs.remove(&gpr);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// _ => {
|
||||
// clear_overwritten_gprs(ins, &mut gpr_pool_relocs);
|
||||
// }
|
||||
// }
|
||||
// } else if let Some((offset, addr_src_gpr, addr_dst_gpr)) =
|
||||
// get_offset_and_addr_gpr_for_possible_pool_reference(ins.op, &simplified)
|
||||
// {
|
||||
// // This instruction doesn't have a real relocation, so it may be a reference to one of
|
||||
// // the already-loaded pools.
|
||||
// if let Some(pool_reloc) = gpr_pool_relocs.get(&addr_src_gpr.0) {
|
||||
// if let Some(fake_pool_reloc) =
|
||||
// make_fake_pool_reloc(offset, cur_addr, pool_reloc)
|
||||
// {
|
||||
// pool_reloc_for_addr.insert(cur_addr, fake_pool_reloc);
|
||||
// }
|
||||
// if let Some(addr_dst_gpr) = addr_dst_gpr {
|
||||
// // If the address of the pool relocation got copied into another register, we
|
||||
// // 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
|
||||
// // register the symbol's address was originally loaded into.
|
||||
// // 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
|
||||
// // with the offset within the .data section of an array variable into r21.
|
||||
// // 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);
|
||||
// } else {
|
||||
// clear_overwritten_gprs(ins, &mut gpr_pool_relocs);
|
||||
// }
|
||||
// } else {
|
||||
// clear_overwritten_gprs(ins, &mut gpr_pool_relocs);
|
||||
// }
|
||||
// } else {
|
||||
// clear_overwritten_gprs(ins, &mut gpr_pool_relocs);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // Finally, if we're about to finish the outer loop and don't have any more control flow to
|
||||
// // follow, we check if there are any instruction addresses in this function that we missed.
|
||||
// // 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.
|
||||
// if ins_iters_with_gpr_state.is_empty() {
|
||||
// let unseen_addrs = (func_address as u32..func_address as u32 + code.len() as u32)
|
||||
// .step_by(4)
|
||||
// .filter(|addr| !visited_ins_addrs.contains(addr));
|
||||
// for unseen_addr in unseen_addrs {
|
||||
// let prev_bctr_gpr_state = gpr_state_at_bctr
|
||||
// .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;
|
||||
// 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
|
||||
// }
|
||||
649
objdiff-core/src/arch/ppc/flow_analysis.rs
Normal file
649
objdiff-core/src/arch/ppc/flow_analysis.rs
Normal file
@@ -0,0 +1,649 @@
|
||||
use alloc::{
|
||||
boxed::Box,
|
||||
collections::{BTreeMap, BTreeSet},
|
||||
format,
|
||||
string::{String, ToString},
|
||||
vec::Vec,
|
||||
};
|
||||
use core::{
|
||||
ffi::CStr,
|
||||
ops::{Index, IndexMut},
|
||||
};
|
||||
|
||||
use itertools::Itertools;
|
||||
use powerpc::{Extensions, Simm};
|
||||
|
||||
use crate::{
|
||||
arch::DataType,
|
||||
obj::{FlowAnalysisResult, FlowAnalysisValue, Object, Relocation, Symbol},
|
||||
util::{RawDouble, RawFloat},
|
||||
};
|
||||
|
||||
fn is_store_instruction(op: powerpc::Opcode) -> bool {
|
||||
use powerpc::Opcode;
|
||||
matches!(
|
||||
op,
|
||||
Opcode::Stbux
|
||||
| Opcode::Stbx
|
||||
| Opcode::Stfdux
|
||||
| Opcode::Stfdx
|
||||
| Opcode::Stfiwx
|
||||
| Opcode::Stfsux
|
||||
| Opcode::Stfsx
|
||||
| Opcode::Sthbrx
|
||||
| Opcode::Sthux
|
||||
| Opcode::Sthx
|
||||
| Opcode::Stswi
|
||||
| Opcode::Stswx
|
||||
| Opcode::Stwbrx
|
||||
| Opcode::Stwcx_
|
||||
| Opcode::Stwux
|
||||
| Opcode::Stwx
|
||||
| Opcode::Stwu
|
||||
| Opcode::Stb
|
||||
| Opcode::Stbu
|
||||
| Opcode::Sth
|
||||
| Opcode::Sthu
|
||||
| Opcode::Stmw
|
||||
| Opcode::Stfs
|
||||
| Opcode::Stfsu
|
||||
| Opcode::Stfd
|
||||
| Opcode::Stfdu
|
||||
)
|
||||
}
|
||||
|
||||
pub fn guess_data_type_from_load_store_inst_op(inst_op: powerpc::Opcode) -> Option<DataType> {
|
||||
use powerpc::Opcode;
|
||||
match inst_op {
|
||||
Opcode::Lbz | Opcode::Lbzu | Opcode::Lbzux | Opcode::Lbzx => Some(DataType::Int8),
|
||||
Opcode::Lhz | Opcode::Lhzu | Opcode::Lhzux | Opcode::Lhzx => Some(DataType::Int16),
|
||||
Opcode::Lha | Opcode::Lhau | Opcode::Lhaux | Opcode::Lhax => Some(DataType::Int16),
|
||||
Opcode::Lwz | Opcode::Lwzu | Opcode::Lwzux | Opcode::Lwzx => Some(DataType::Int32),
|
||||
Opcode::Lfs | Opcode::Lfsu | Opcode::Lfsux | Opcode::Lfsx => Some(DataType::Float),
|
||||
Opcode::Lfd | Opcode::Lfdu | Opcode::Lfdux | Opcode::Lfdx => Some(DataType::Double),
|
||||
|
||||
Opcode::Stb | Opcode::Stbu | Opcode::Stbux | Opcode::Stbx => Some(DataType::Int8),
|
||||
Opcode::Sth | Opcode::Sthu | Opcode::Sthux | Opcode::Sthx => Some(DataType::Int16),
|
||||
Opcode::Stw | Opcode::Stwu | Opcode::Stwux | Opcode::Stwx => Some(DataType::Int32),
|
||||
Opcode::Stfs | Opcode::Stfsu | Opcode::Stfsux | Opcode::Stfsx => Some(DataType::Float),
|
||||
Opcode::Stfd | Opcode::Stfdu | Opcode::Stfdux | Opcode::Stfdx => Some(DataType::Double),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, PartialEq, Eq, Copy, Clone, Debug, PartialOrd, Ord)]
|
||||
enum RegisterContent {
|
||||
#[default]
|
||||
Unknown,
|
||||
Variable, // Multiple potential values
|
||||
FloatConstant(RawFloat),
|
||||
DoubleConstant(RawDouble),
|
||||
IntConstant(i32),
|
||||
InputRegister(u8),
|
||||
Symbol(usize),
|
||||
}
|
||||
|
||||
impl core::fmt::Display for RegisterContent {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
match self {
|
||||
RegisterContent::Unknown => write!(f, "unknown"),
|
||||
RegisterContent::Variable => write!(f, "variable"),
|
||||
RegisterContent::IntConstant(i) =>
|
||||
// -i is safe because it's at most a 16 bit constant in the i32
|
||||
{
|
||||
if *i >= 0 {
|
||||
write!(f, "0x{i:x}")
|
||||
} else {
|
||||
write!(f, "-0x{:x}", -i)
|
||||
}
|
||||
}
|
||||
RegisterContent::FloatConstant(RawFloat(fp)) => write!(f, "{fp:?}f"),
|
||||
RegisterContent::DoubleConstant(RawDouble(fp)) => write!(f, "{fp:?}d"),
|
||||
RegisterContent::InputRegister(p) => write!(f, "input{p}"),
|
||||
RegisterContent::Symbol(_u) => write!(f, "relocation"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Ord, PartialOrd)]
|
||||
struct RegisterState {
|
||||
gpr: [RegisterContent; 32],
|
||||
fpr: [RegisterContent; 32],
|
||||
}
|
||||
|
||||
impl RegisterState {
|
||||
fn new() -> Self {
|
||||
RegisterState { gpr: [RegisterContent::Unknown; 32], fpr: [RegisterContent::Unknown; 32] }
|
||||
}
|
||||
|
||||
// During a function call, these registers must be assumed trashed.
|
||||
fn clear_volatile(&mut self) {
|
||||
self[powerpc::GPR(0)] = RegisterContent::Unknown;
|
||||
for i in 0..=13 {
|
||||
self[powerpc::GPR(i)] = RegisterContent::Unknown;
|
||||
}
|
||||
for i in 0..=13 {
|
||||
self[powerpc::FPR(i)] = RegisterContent::Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
// Mark potential input values.
|
||||
// Subsequent flow analysis will "realize" that they are not actually inputs if
|
||||
// they get overwritten with another value before getting read.
|
||||
fn set_potential_inputs(&mut self) {
|
||||
for g_reg in 3..=13 {
|
||||
self[powerpc::GPR(g_reg)] = RegisterContent::InputRegister(g_reg);
|
||||
}
|
||||
for f_reg in 1..=13 {
|
||||
self[powerpc::FPR(f_reg)] = RegisterContent::InputRegister(f_reg);
|
||||
}
|
||||
}
|
||||
|
||||
// If the there is no value, we can take the new known value.
|
||||
// If there's a known value different than the new value, the content
|
||||
// must is variable.
|
||||
// Returns whether the current value was updated.
|
||||
fn unify_values(current: &mut RegisterContent, new: &RegisterContent) -> bool {
|
||||
if *current == *new {
|
||||
false
|
||||
} else if *current == RegisterContent::Unknown {
|
||||
*current = *new;
|
||||
true
|
||||
} else if *current == RegisterContent::Variable {
|
||||
// Already variable
|
||||
false
|
||||
} else {
|
||||
*current = RegisterContent::Variable;
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
// Unify currently known register contents in a give situation with new
|
||||
// information about the register contents in that situation.
|
||||
// Currently unknown register contents can be filled, but if there are
|
||||
// conflicting contents, we go back to unknown.
|
||||
fn unify(&mut self, other: &RegisterState) -> bool {
|
||||
let mut updated = false;
|
||||
for i in 0..32 {
|
||||
updated |= Self::unify_values(&mut self.gpr[i], &other.gpr[i]);
|
||||
updated |= Self::unify_values(&mut self.fpr[i], &other.fpr[i]);
|
||||
}
|
||||
updated
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<powerpc::GPR> for RegisterState {
|
||||
type Output = RegisterContent;
|
||||
|
||||
fn index(&self, gpr: powerpc::GPR) -> &Self::Output { &self.gpr[gpr.0 as usize] }
|
||||
}
|
||||
impl IndexMut<powerpc::GPR> for RegisterState {
|
||||
fn index_mut(&mut self, gpr: powerpc::GPR) -> &mut Self::Output {
|
||||
&mut self.gpr[gpr.0 as usize]
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<powerpc::FPR> for RegisterState {
|
||||
type Output = RegisterContent;
|
||||
|
||||
fn index(&self, fpr: powerpc::FPR) -> &Self::Output { &self.fpr[fpr.0 as usize] }
|
||||
}
|
||||
impl IndexMut<powerpc::FPR> for RegisterState {
|
||||
fn index_mut(&mut self, fpr: powerpc::FPR) -> &mut Self::Output {
|
||||
&mut self.fpr[fpr.0 as usize]
|
||||
}
|
||||
}
|
||||
|
||||
fn execute_instruction(
|
||||
registers: &mut RegisterState,
|
||||
op: &powerpc::Opcode,
|
||||
args: &powerpc::Arguments,
|
||||
) {
|
||||
use powerpc::{Argument, GPR, Opcode};
|
||||
match (op, args[0], args[1], args[2]) {
|
||||
(Opcode::Or, Argument::GPR(a), Argument::GPR(b), Argument::GPR(c)) => {
|
||||
// Move is implemented as or with self for ints
|
||||
if b == c {
|
||||
registers[a] = registers[b];
|
||||
} else {
|
||||
registers[a] = RegisterContent::Unknown;
|
||||
}
|
||||
}
|
||||
(Opcode::Fmr, Argument::FPR(a), Argument::FPR(b), _) => {
|
||||
registers[a] = registers[b];
|
||||
}
|
||||
(Opcode::Addi, Argument::GPR(a), Argument::GPR(GPR(0)), Argument::Simm(c)) => {
|
||||
// Load immidiate implemented as addi with addend = r0
|
||||
// Let Addi with other addends fall through to the case which
|
||||
// overwrites the destination
|
||||
registers[a] = RegisterContent::IntConstant(c.0 as i32);
|
||||
}
|
||||
(Opcode::Bcctr, _, _, _) => {
|
||||
// Called a function pointer, may have erased volatile registers
|
||||
registers.clear_volatile();
|
||||
}
|
||||
(Opcode::B, _, _, _) => {
|
||||
if get_branch_offset(args) == 0 {
|
||||
// Call to another function
|
||||
registers.clear_volatile();
|
||||
}
|
||||
}
|
||||
(
|
||||
Opcode::Stbu | Opcode::Sthu | Opcode::Stwu | Opcode::Stfsu | Opcode::Stfdu,
|
||||
_,
|
||||
_,
|
||||
Argument::GPR(rel),
|
||||
) => {
|
||||
// Storing with update, clear updated register (third arg)
|
||||
registers[rel] = RegisterContent::Unknown;
|
||||
}
|
||||
(
|
||||
Opcode::Stbux | Opcode::Sthux | Opcode::Stwux | Opcode::Stfsux | Opcode::Stfdux,
|
||||
_,
|
||||
Argument::GPR(rel),
|
||||
_,
|
||||
) => {
|
||||
// Storing indexed with update, clear updated register (second arg)
|
||||
registers[rel] = RegisterContent::Unknown;
|
||||
}
|
||||
(Opcode::Lmw, Argument::GPR(target), _, _) => {
|
||||
// `lmw` overwrites all registers from rd to r31.
|
||||
for reg in target.0..31 {
|
||||
registers[GPR(reg)] = RegisterContent::Unknown;
|
||||
}
|
||||
}
|
||||
(_, Argument::GPR(a), _, _) => {
|
||||
// Store instructions don't modify the GPR
|
||||
if !is_store_instruction(*op) {
|
||||
// Other operations which write to GPR a
|
||||
registers[a] = RegisterContent::Unknown;
|
||||
}
|
||||
}
|
||||
(_, Argument::FPR(a), _, _) => {
|
||||
// Store instructions don't modify the FPR
|
||||
if !is_store_instruction(*op) {
|
||||
// Other operations which write to FPR a
|
||||
registers[a] = RegisterContent::Unknown;
|
||||
}
|
||||
}
|
||||
(_, _, _, _) => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_branch_offset(args: &powerpc::Arguments) -> i32 {
|
||||
for arg in args.iter() {
|
||||
match arg {
|
||||
powerpc::Argument::BranchDest(dest) => return dest.0 / 4,
|
||||
powerpc::Argument::None => break,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct PPCFlowAnalysisResult {
|
||||
argument_contents: BTreeMap<(u64, u8), FlowAnalysisValue>,
|
||||
}
|
||||
|
||||
impl PPCFlowAnalysisResult {
|
||||
fn set_argument_value_at_address(
|
||||
&mut self,
|
||||
address: u64,
|
||||
argument: u8,
|
||||
value: FlowAnalysisValue,
|
||||
) {
|
||||
self.argument_contents.insert((address, argument), value);
|
||||
}
|
||||
|
||||
fn new() -> Self { PPCFlowAnalysisResult { argument_contents: Default::default() } }
|
||||
}
|
||||
|
||||
impl FlowAnalysisResult for PPCFlowAnalysisResult {
|
||||
fn get_argument_value_at_address(
|
||||
&self,
|
||||
address: u64,
|
||||
argument: u8,
|
||||
) -> Option<&FlowAnalysisValue> {
|
||||
self.argument_contents.get(&(address, argument))
|
||||
}
|
||||
}
|
||||
|
||||
fn clamp_text_length(s: String, max: usize) -> String {
|
||||
if s.len() <= max { s } else { format!("{}…", s.chars().take(max - 3).collect::<String>()) }
|
||||
}
|
||||
|
||||
fn get_register_content_from_reloc(
|
||||
reloc: &Relocation,
|
||||
obj: &Object,
|
||||
op: powerpc::Opcode,
|
||||
) -> RegisterContent {
|
||||
if let Some(bytes) = obj.symbol_data(reloc.target_symbol) {
|
||||
match guess_data_type_from_load_store_inst_op(op) {
|
||||
Some(DataType::Float) => {
|
||||
RegisterContent::FloatConstant(RawFloat(match obj.endianness {
|
||||
object::Endianness::Little => {
|
||||
f32::from_le_bytes(bytes.try_into().unwrap_or([0; 4]))
|
||||
}
|
||||
object::Endianness::Big => {
|
||||
f32::from_be_bytes(bytes.try_into().unwrap_or([0; 4]))
|
||||
}
|
||||
}))
|
||||
}
|
||||
Some(DataType::Double) => {
|
||||
RegisterContent::DoubleConstant(RawDouble(match obj.endianness {
|
||||
object::Endianness::Little => {
|
||||
f64::from_le_bytes(bytes.try_into().unwrap_or([0; 8]))
|
||||
}
|
||||
object::Endianness::Big => {
|
||||
f64::from_be_bytes(bytes.try_into().unwrap_or([0; 8]))
|
||||
}
|
||||
}))
|
||||
}
|
||||
_ => RegisterContent::Symbol(reloc.target_symbol),
|
||||
}
|
||||
} else {
|
||||
RegisterContent::Symbol(reloc.target_symbol)
|
||||
}
|
||||
}
|
||||
|
||||
// Executing op with args at cur_address, update current_state with symbols that
|
||||
// come from relocations. That is, references to globals, floating point
|
||||
// constants, string constants, etc.
|
||||
fn fill_registers_from_relocation(
|
||||
reloc: &Relocation,
|
||||
current_state: &mut RegisterState,
|
||||
obj: &Object,
|
||||
op: powerpc::Opcode,
|
||||
args: &powerpc::Arguments,
|
||||
) {
|
||||
// Only update the register state for loads. We may store to a reloc
|
||||
// address but that doesn't update register contents.
|
||||
if !is_store_instruction(op) {
|
||||
match (op, args[0]) {
|
||||
// Everything else is a load of some sort
|
||||
(_, powerpc::Argument::GPR(gpr)) => {
|
||||
current_state[gpr] = get_register_content_from_reloc(reloc, obj, op);
|
||||
}
|
||||
(_, powerpc::Argument::FPR(fpr)) => {
|
||||
current_state[fpr] = get_register_content_from_reloc(reloc, obj, op);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Special helper fragments generated by MWCC.
|
||||
// See: https://github.com/encounter/decomp-toolkit/blob/main/src/analysis/pass.rs
|
||||
const SLEDS: [&str; 6] = ["_savefpr_", "_restfpr_", "_savegpr_", "_restgpr_", "_savev", "_restv"];
|
||||
|
||||
fn is_sled_function(name: &str) -> bool { SLEDS.iter().any(|sled| name.starts_with(sled)) }
|
||||
|
||||
pub fn ppc_data_flow_analysis(
|
||||
obj: &Object,
|
||||
func_symbol: &Symbol,
|
||||
code: &[u8],
|
||||
relocations: &[Relocation],
|
||||
extensions: Extensions,
|
||||
) -> Box<dyn FlowAnalysisResult> {
|
||||
use alloc::collections::VecDeque;
|
||||
|
||||
use powerpc::InsIter;
|
||||
let instructions = InsIter::new(code, func_symbol.address as u32, extensions)
|
||||
.map(|(_addr, ins)| (ins.op, ins.basic().args))
|
||||
.collect_vec();
|
||||
|
||||
let func_address = func_symbol.address;
|
||||
|
||||
// Get initial register values from function parameters
|
||||
let mut initial_register_state = RegisterState::new();
|
||||
initial_register_state.set_potential_inputs();
|
||||
|
||||
let mut execution_queue = VecDeque::<(usize, RegisterState)>::new();
|
||||
execution_queue.push_back((0, initial_register_state));
|
||||
|
||||
// Execute the instructions against abstract data
|
||||
let mut failsafe_counter = 0;
|
||||
let mut taken_branches = BTreeSet::<(usize, RegisterState)>::new();
|
||||
let mut register_state_at = Vec::<RegisterState>::new();
|
||||
let mut completed_first_pass = false;
|
||||
register_state_at.resize_with(instructions.len(), RegisterState::new);
|
||||
while let Some((mut index, mut current_state)) = execution_queue.pop_front() {
|
||||
while let Some((op, args)) = instructions.get(index) {
|
||||
// Record the state at this index
|
||||
// If recording does not result in any changes to the known values
|
||||
// we're done, because the subsequent values are a function of the
|
||||
// current values so we'll get the same result as the last time
|
||||
// we went down this path.
|
||||
// Don't break out if we haven't even completed the first pass
|
||||
// through the function though.
|
||||
if !register_state_at[index].unify(¤t_state) && completed_first_pass {
|
||||
break;
|
||||
}
|
||||
|
||||
// Get symbol used in this instruction
|
||||
let cur_addr = (func_address as u32) + ((index * 4) as u32);
|
||||
let reloc = relocations.iter().find(|r| (r.address as u32 & !3) == cur_addr);
|
||||
|
||||
// Is this a branch to a compiler generated helper? These helpers
|
||||
// do not trash registers like normal function calls, so we don't
|
||||
// want to treat this as normal execution.
|
||||
let symbol = reloc.and_then(|r| obj.symbols.get(r.target_symbol));
|
||||
let is_sled_invocation = symbol.is_some_and(|x| is_sled_function(&x.name));
|
||||
|
||||
// Execute the instruction to update the state
|
||||
// Since sled invocations are only used to save / restore registers
|
||||
// as part of prelude / cleanup in a function call we don't have to
|
||||
// do any execution for them.
|
||||
if !is_sled_invocation {
|
||||
execute_instruction(&mut current_state, op, args);
|
||||
}
|
||||
|
||||
// Fill in register state coming from relocations at this line. This
|
||||
// handles references to global variables, floating point constants,
|
||||
// etc.
|
||||
if let Some(reloc) = reloc {
|
||||
fill_registers_from_relocation(reloc, &mut current_state, obj, *op, args);
|
||||
}
|
||||
|
||||
// Add conditional branches to execution queue
|
||||
// Only take a given (address, register state) combination once. If
|
||||
// the known register state is different we have to take the branch
|
||||
// again to stabilize the known values for backwards branches.
|
||||
if op == &powerpc::Opcode::Bc {
|
||||
let branch_state = (index, current_state.clone());
|
||||
if !taken_branches.contains(&branch_state) {
|
||||
let offset = get_branch_offset(args);
|
||||
let target_index = ((index as i32) + offset) as usize;
|
||||
execution_queue.push_back((target_index, current_state.clone()));
|
||||
taken_branches.insert(branch_state);
|
||||
|
||||
// We should never hit this case, but avoid getting stuck in
|
||||
// an infinite loop if we hit some kind of bad behavior.
|
||||
failsafe_counter += 1;
|
||||
if failsafe_counter > 256 {
|
||||
//println!("Analysis of {} failed to stabilize", func_symbol.name);
|
||||
return Box::new(PPCFlowAnalysisResult::new());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update index
|
||||
if op == &powerpc::Opcode::B {
|
||||
// Unconditional branch
|
||||
let offset = get_branch_offset(args);
|
||||
if offset > 0 {
|
||||
// Jump table or branch to over else clause.
|
||||
index += offset as usize;
|
||||
} else if offset == 0 {
|
||||
// Function call with relocation. We'll return to
|
||||
// the next instruction.
|
||||
index += 1;
|
||||
} else {
|
||||
// Unconditional branch (E.g.: loop { ... })
|
||||
// Also some compilations of loops put the conditional at
|
||||
// the end and B to it for the check of the first iteration.
|
||||
let branch_state = (index, current_state.clone());
|
||||
if taken_branches.contains(&branch_state) {
|
||||
break;
|
||||
}
|
||||
taken_branches.insert(branch_state);
|
||||
index = ((index as i32) + offset) as usize;
|
||||
}
|
||||
} else {
|
||||
// Normal execution of next instruction
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Mark that we've completed at least one pass over the function, at
|
||||
// this point we can break out if the code we're running doesn't change
|
||||
// any register outcomes.
|
||||
completed_first_pass = true;
|
||||
}
|
||||
|
||||
// Store the relevant data flow values for simplified instructions
|
||||
generate_flow_analysis_result(
|
||||
obj,
|
||||
func_address,
|
||||
code,
|
||||
register_state_at,
|
||||
relocations,
|
||||
extensions,
|
||||
)
|
||||
}
|
||||
|
||||
fn get_string_data(obj: &Object, symbol_index: usize, offset: Simm) -> Option<&str> {
|
||||
if let Some(sym) = obj.symbols.get(symbol_index)
|
||||
&& sym.name.starts_with("@stringBase")
|
||||
&& offset.0 != 0
|
||||
&& let Some(data) = obj.symbol_data(symbol_index)
|
||||
{
|
||||
let bytes = &data[offset.0 as usize..];
|
||||
if let Ok(Ok(str)) = CStr::from_bytes_until_nul(bytes).map(|x| x.to_str()) {
|
||||
return Some(str);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
// Write the relevant part of the flow analysis out into the FlowAnalysisResult
|
||||
// the rest of the application will use to query results of the flow analysis.
|
||||
// Flow analysis will compute the known contents of every register at every
|
||||
// line, but we only need to record the values of registers that are actually
|
||||
// referenced at each line.
|
||||
fn generate_flow_analysis_result(
|
||||
obj: &Object,
|
||||
base_address: u64,
|
||||
code: &[u8],
|
||||
register_state_at: Vec<RegisterState>,
|
||||
relocations: &[Relocation],
|
||||
extensions: Extensions,
|
||||
) -> Box<PPCFlowAnalysisResult> {
|
||||
use powerpc::{Argument, InsIter};
|
||||
let mut analysis_result = PPCFlowAnalysisResult::new();
|
||||
let default_register_state = RegisterState::new();
|
||||
for (addr, ins) in InsIter::new(code, 0, extensions) {
|
||||
let ins_address = base_address + (addr as u64);
|
||||
let index = addr / 4;
|
||||
let powerpc::ParsedIns { mnemonic: _, args } = ins.simplified();
|
||||
|
||||
// If we're already showing relocations on a line don't also show data flow
|
||||
let reloc = relocations.iter().find(|r| (r.address & !3) == ins_address);
|
||||
|
||||
// Special case to show float and double constants on the line where
|
||||
// they are being loaded.
|
||||
// We need to do this before we break out on showing relocations in the
|
||||
// subsequent if statement.
|
||||
if let (powerpc::Opcode::Lfs | powerpc::Opcode::Lfd, Some(reloc)) = (ins.op, reloc) {
|
||||
let content = get_register_content_from_reloc(reloc, obj, ins.op);
|
||||
if matches!(
|
||||
content,
|
||||
RegisterContent::FloatConstant(_) | RegisterContent::DoubleConstant(_)
|
||||
) {
|
||||
analysis_result.set_argument_value_at_address(
|
||||
ins_address,
|
||||
1,
|
||||
FlowAnalysisValue::Text(content.to_string()),
|
||||
);
|
||||
|
||||
// Don't need to show any other data flow if we're showing that
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Special case to show string constants on the line where they are
|
||||
// being indexed to. This will typically be "addi t, stringbase, offset"
|
||||
let registers = register_state_at.get(index as usize).unwrap_or(&default_register_state);
|
||||
if let (powerpc::Opcode::Addi, Argument::GPR(rel), Argument::Simm(offset)) =
|
||||
(ins.op, args[1], args[2])
|
||||
&& let RegisterContent::Symbol(sym_index) = registers[rel]
|
||||
&& let Some(str) = get_string_data(obj, sym_index, offset)
|
||||
{
|
||||
// Show the string constant in the analysis result
|
||||
let formatted = format!("\"{str}\"");
|
||||
analysis_result.set_argument_value_at_address(
|
||||
ins_address,
|
||||
2,
|
||||
FlowAnalysisValue::Text(clamp_text_length(formatted, 20)),
|
||||
);
|
||||
// Don't continue, we want to show the stringbase value as well
|
||||
}
|
||||
|
||||
let is_store = is_store_instruction(ins.op);
|
||||
for (arg_index, arg) in args.into_iter().enumerate() {
|
||||
// Hacky shorthand for determining which arguments are sources,
|
||||
// We only want to show data flow for source registers, not target
|
||||
// registers. Technically there are some non-"st_" operations which
|
||||
// read from their first argument but they're rare.
|
||||
if (arg_index == 0) && !is_store {
|
||||
continue;
|
||||
}
|
||||
|
||||
let content = match arg {
|
||||
Argument::GPR(gpr) => Some(registers[gpr]),
|
||||
Argument::FPR(fpr) => Some(registers[fpr]),
|
||||
_ => None,
|
||||
};
|
||||
let analysis_value = match content {
|
||||
Some(RegisterContent::Symbol(s)) => {
|
||||
if reloc.is_none() {
|
||||
// Only symbols if there isn't already a relocation, because
|
||||
// code other than the data flow analysis will be showing
|
||||
// the symbol for a relocation on the line it is for. If we
|
||||
// also showed it as data flow analysis value we would be
|
||||
// showing redundant information.
|
||||
obj.symbols.get(s).map(|sym| {
|
||||
FlowAnalysisValue::Text(clamp_text_length(
|
||||
sym.demangled_name.as_ref().unwrap_or(&sym.name).clone(),
|
||||
20,
|
||||
))
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
Some(RegisterContent::InputRegister(reg)) => {
|
||||
let reg_name = match arg {
|
||||
Argument::GPR(_) => format!("in_r{reg}"),
|
||||
Argument::FPR(_) => format!("in_f{reg}"),
|
||||
_ => panic!("Register content should only be in a register"),
|
||||
};
|
||||
Some(FlowAnalysisValue::Text(reg_name))
|
||||
}
|
||||
Some(RegisterContent::Unknown) | Some(RegisterContent::Variable) => None,
|
||||
Some(value) => Some(FlowAnalysisValue::Text(value.to_string())),
|
||||
None => None,
|
||||
};
|
||||
if let Some(analysis_value) = analysis_value {
|
||||
analysis_result.set_argument_value_at_address(
|
||||
ins_address,
|
||||
arg_index as u8,
|
||||
analysis_value,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Box::new(analysis_result)
|
||||
}
|
||||
1009
objdiff-core/src/arch/ppc/mod.rs
Normal file
1009
objdiff-core/src/arch/ppc/mod.rs
Normal file
File diff suppressed because it is too large
Load Diff
1372
objdiff-core/src/arch/superh/disasm.rs
Normal file
1372
objdiff-core/src/arch/superh/disasm.rs
Normal file
File diff suppressed because it is too large
Load Diff
810
objdiff-core/src/arch/superh/mod.rs
Normal file
810
objdiff-core/src/arch/superh/mod.rs
Normal file
@@ -0,0 +1,810 @@
|
||||
use alloc::{collections::BTreeMap, format, string::String, vec, vec::Vec};
|
||||
|
||||
use anyhow::Result;
|
||||
use object::elf;
|
||||
|
||||
use crate::{
|
||||
arch::{Arch, superh::disasm::sh2_disasm},
|
||||
diff::{DiffObjConfig, display::InstructionPart},
|
||||
obj::{InstructionRef, Relocation, RelocationFlags, ResolvedInstructionRef},
|
||||
};
|
||||
|
||||
pub mod disasm;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ArchSuperH {}
|
||||
|
||||
impl ArchSuperH {
|
||||
pub fn new(_file: &object::File) -> Result<Self> { Ok(Self {}) }
|
||||
}
|
||||
|
||||
struct DataInfo {
|
||||
address: u64,
|
||||
size: u32,
|
||||
}
|
||||
|
||||
impl Arch for ArchSuperH {
|
||||
fn scan_instructions_internal(
|
||||
&self,
|
||||
address: u64,
|
||||
code: &[u8],
|
||||
_section_index: usize,
|
||||
_relocations: &[Relocation],
|
||||
_diff_config: &DiffObjConfig,
|
||||
) -> Result<Vec<InstructionRef>> {
|
||||
let mut ops = Vec::<InstructionRef>::with_capacity(code.len() / 2);
|
||||
let mut offset = address;
|
||||
|
||||
for chunk in code.chunks_exact(2) {
|
||||
let opcode = u16::from_be_bytes(chunk.try_into().unwrap());
|
||||
let mut parts: Vec<InstructionPart> = vec![];
|
||||
let resolved: ResolvedInstructionRef = Default::default();
|
||||
let mut branch_dest: Option<u64> = None;
|
||||
sh2_disasm(
|
||||
offset.try_into().unwrap(),
|
||||
opcode,
|
||||
true,
|
||||
&mut parts,
|
||||
&resolved,
|
||||
&mut branch_dest,
|
||||
);
|
||||
|
||||
let opcode_enum: u16 = match parts.first() {
|
||||
Some(InstructionPart::Opcode(_, val)) => *val,
|
||||
_ => 0,
|
||||
};
|
||||
ops.push(InstructionRef { address: offset, size: 2, opcode: opcode_enum, branch_dest });
|
||||
offset += 2;
|
||||
}
|
||||
|
||||
Ok(ops)
|
||||
}
|
||||
|
||||
fn display_instruction(
|
||||
&self,
|
||||
resolved: ResolvedInstructionRef,
|
||||
_diff_config: &DiffObjConfig,
|
||||
cb: &mut dyn FnMut(InstructionPart) -> Result<()>,
|
||||
) -> Result<()> {
|
||||
let opcode = u16::from_be_bytes(resolved.code.try_into().unwrap());
|
||||
let mut parts: Vec<InstructionPart> = vec![];
|
||||
let mut branch_dest: Option<u64> = None;
|
||||
|
||||
sh2_disasm(0, opcode, true, &mut parts, &resolved, &mut branch_dest);
|
||||
|
||||
if let Some(symbol_data) =
|
||||
resolved.section.data_range(resolved.symbol.address, resolved.symbol.size as usize)
|
||||
{
|
||||
// scan for data
|
||||
// map of instruction offsets to data target offsets
|
||||
let mut data_offsets = BTreeMap::<u64, DataInfo>::new();
|
||||
|
||||
let mut pos: u64 = 0;
|
||||
for chunk in symbol_data.chunks_exact(2) {
|
||||
let opcode = u16::from_be_bytes(chunk.try_into().unwrap());
|
||||
// mov.w
|
||||
if (opcode & 0xf000) == 0x9000 {
|
||||
let target = (opcode as u64 & 0xff) * 2 + 4 + pos;
|
||||
let data_info = DataInfo { address: target, size: 2 };
|
||||
data_offsets.insert(pos, data_info);
|
||||
}
|
||||
// mov.l
|
||||
else if (opcode & 0xf000) == 0xd000 {
|
||||
let target = ((opcode as u64 & 0xff) * 4 + 4 + pos) & 0xfffffffc;
|
||||
let data_info = DataInfo { address: target, size: 4 };
|
||||
data_offsets.insert(pos, data_info);
|
||||
}
|
||||
pos += 2;
|
||||
}
|
||||
|
||||
let pos = resolved.ins_ref.address - resolved.symbol.address;
|
||||
|
||||
// add the data info
|
||||
if let Some(value) = data_offsets.get(&pos) {
|
||||
if value.size == 2 && value.address as usize + 1 < symbol_data.len() {
|
||||
let data = u16::from_be_bytes(
|
||||
symbol_data[value.address as usize..value.address as usize + 2]
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
);
|
||||
parts.push(InstructionPart::basic(" /* "));
|
||||
parts.push(InstructionPart::basic("0x"));
|
||||
parts.push(InstructionPart::basic(format!("{data:04X}")));
|
||||
parts.push(InstructionPart::basic(" */"));
|
||||
} else if value.size == 4 && value.address as usize + 3 < symbol_data.len() {
|
||||
let data = u32::from_be_bytes(
|
||||
symbol_data[value.address as usize..value.address as usize + 4]
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
);
|
||||
parts.push(InstructionPart::basic(" /* "));
|
||||
parts.push(InstructionPart::basic("0x"));
|
||||
parts.push(InstructionPart::basic(format!("{data:08X}")));
|
||||
parts.push(InstructionPart::basic(" */"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for part in parts {
|
||||
cb(part)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn demangle(&self, name: &str) -> Option<String> {
|
||||
cpp_demangle::Symbol::new(name)
|
||||
.ok()
|
||||
.and_then(|s| s.demangle(&cpp_demangle::DemangleOptions::default()).ok())
|
||||
}
|
||||
|
||||
fn reloc_name(&self, flags: RelocationFlags) -> Option<&'static str> {
|
||||
match flags {
|
||||
RelocationFlags::Elf(r_type) => match r_type {
|
||||
elf::R_SH_NONE => Some("R_SH_NONE"),
|
||||
elf::R_SH_DIR32 => Some("R_SH_DIR32"),
|
||||
elf::R_SH_REL32 => Some("R_SH_REL32"),
|
||||
elf::R_SH_DIR8WPN => Some("R_SH_DIR8WPN"),
|
||||
elf::R_SH_IND12W => Some("R_SH_IND12W"),
|
||||
elf::R_SH_DIR8WPL => Some("R_SH_DIR8WPL"),
|
||||
elf::R_SH_DIR8WPZ => Some("R_SH_DIR8WPZ"),
|
||||
elf::R_SH_DIR8BP => Some("R_SH_DIR8BP"),
|
||||
elf::R_SH_DIR8W => Some("R_SH_DIR8W"),
|
||||
elf::R_SH_DIR8L => Some("R_SH_DIR8L"),
|
||||
elf::R_SH_SWITCH16 => Some("R_SH_SWITCH16"),
|
||||
elf::R_SH_SWITCH32 => Some("R_SH_SWITCH32"),
|
||||
elf::R_SH_USES => Some("R_SH_USES"),
|
||||
elf::R_SH_COUNT => Some("R_SH_COUNT"),
|
||||
elf::R_SH_ALIGN => Some("R_SH_ALIGN"),
|
||||
elf::R_SH_CODE => Some("R_SH_CODE"),
|
||||
elf::R_SH_DATA => Some("R_SH_DATA"),
|
||||
elf::R_SH_LABEL => Some("R_SH_LABEL"),
|
||||
elf::R_SH_SWITCH8 => Some("R_SH_SWITCH8"),
|
||||
elf::R_SH_GNU_VTINHERIT => Some("R_SH_GNU_VTINHERIT"),
|
||||
elf::R_SH_GNU_VTENTRY => Some("R_SH_GNU_VTENTRY"),
|
||||
elf::R_SH_TLS_GD_32 => Some("R_SH_TLS_GD_32"),
|
||||
elf::R_SH_TLS_LD_32 => Some("R_SH_TLS_LD_32"),
|
||||
elf::R_SH_TLS_LDO_32 => Some("R_SH_TLS_LDO_32"),
|
||||
elf::R_SH_TLS_IE_32 => Some("R_SH_TLS_IE_32"),
|
||||
elf::R_SH_TLS_LE_32 => Some("R_SH_TLS_LE_32"),
|
||||
elf::R_SH_TLS_DTPMOD32 => Some("R_SH_TLS_DTPMOD32"),
|
||||
elf::R_SH_TLS_DTPOFF32 => Some("R_SH_TLS_DTPOFF32"),
|
||||
elf::R_SH_TLS_TPOFF32 => Some("R_SH_TLS_TPOFF32"),
|
||||
elf::R_SH_GOT32 => Some("R_SH_GOT32"),
|
||||
elf::R_SH_PLT32 => Some("R_SH_PLT32"),
|
||||
elf::R_SH_COPY => Some("R_SH_COPY"),
|
||||
elf::R_SH_GLOB_DAT => Some("R_SH_GLOB_DAT"),
|
||||
elf::R_SH_JMP_SLOT => Some("R_SH_JMP_SLOT"),
|
||||
elf::R_SH_RELATIVE => Some("R_SH_RELATIVE"),
|
||||
elf::R_SH_GOTOFF => Some("R_SH_GOTOFF"),
|
||||
elf::R_SH_GOTPC => Some("R_SH_GOTPC"),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn data_reloc_size(&self, flags: RelocationFlags) -> usize {
|
||||
match flags {
|
||||
RelocationFlags::Elf(elf::R_SH_DIR32) => 4,
|
||||
RelocationFlags::Elf(_) => 1,
|
||||
_ => 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::fmt::{self, Display};
|
||||
|
||||
use super::*;
|
||||
use crate::obj::{InstructionArg, Section, SectionData, Symbol};
|
||||
|
||||
impl Display for InstructionPart<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
InstructionPart::Basic(s) => f.write_str(s),
|
||||
InstructionPart::Opcode(s, _o) => write!(f, "{s} "),
|
||||
InstructionPart::Arg(arg) => write!(f, "{arg}"),
|
||||
InstructionPart::Separator => f.write_str(", "),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for InstructionArg<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
InstructionArg::Value(v) => write!(f, "{v}"),
|
||||
InstructionArg::BranchDest(v) => write!(f, "{v}"),
|
||||
InstructionArg::Reloc => f.write_str("reloc"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sh2_display_instruction_basic_ops() {
|
||||
let arch = ArchSuperH {};
|
||||
let ops: [(u16, &str); 8] = [
|
||||
(0x0008, "clrt "),
|
||||
(0x0028, "clrmac "),
|
||||
(0x0019, "div0u "),
|
||||
(0x0009, "nop "),
|
||||
(0x002b, "rte "),
|
||||
(0x000b, "rts "),
|
||||
(0x0018, "sett "),
|
||||
(0x001b, "sleep "),
|
||||
];
|
||||
|
||||
for (opcode, expected_str) in ops {
|
||||
let code = opcode.to_be_bytes();
|
||||
let mut parts = Vec::new();
|
||||
|
||||
arch.display_instruction(
|
||||
ResolvedInstructionRef {
|
||||
ins_ref: InstructionRef { address: 0x1000, size: 2, opcode, branch_dest: None },
|
||||
code: &code,
|
||||
..Default::default()
|
||||
},
|
||||
&DiffObjConfig::default(),
|
||||
&mut |part| {
|
||||
parts.push(part.into_static());
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let joined_str: String = parts.iter().map(<_>::to_string).collect();
|
||||
assert_eq!(joined_str, expected_str.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sh2_display_instruction_f0ff_ops() {
|
||||
let arch = ArchSuperH {};
|
||||
let ops: [(u16, &str); 49] = [
|
||||
(0x4015, "cmp/pl r0"),
|
||||
(0x4115, "cmp/pl r1"),
|
||||
(0x4215, "cmp/pl r2"),
|
||||
(0x4315, "cmp/pl r3"),
|
||||
(0x4011, "cmp/pz r0"),
|
||||
(0x4010, "dt r0"),
|
||||
(0x0029, "movt r0"),
|
||||
(0x4004, "rotl r0"),
|
||||
(0x4005, "rotr r0"),
|
||||
(0x4024, "rotcl r0"),
|
||||
(0x4025, "rotcr r0"),
|
||||
(0x4020, "shal r0"),
|
||||
(0x4021, "shar r0"),
|
||||
(0x4000, "shll r0"),
|
||||
(0x4001, "shlr r0"),
|
||||
(0x4008, "shll2 r0"),
|
||||
(0x4009, "shlr2 r0"),
|
||||
(0x4018, "shll8 r0"),
|
||||
(0x4019, "shlr8 r0"),
|
||||
(0x4028, "shll16 r0"),
|
||||
(0x4029, "shlr16 r0"),
|
||||
(0x0002, "stc sr, r0"),
|
||||
(0x0012, "stc gbr, r0"),
|
||||
(0x0022, "stc vbr, r0"),
|
||||
(0x000a, "sts mach, r0"),
|
||||
(0x001a, "sts macl, r0"),
|
||||
(0x402a, "lds r0, pr"),
|
||||
(0x401b, "tas.b r0"),
|
||||
(0x4003, "stc.l sr, @-r0"),
|
||||
(0x4013, "stc.l gbr, @-r0"),
|
||||
(0x4023, "stc.l vbr, @-r0"),
|
||||
(0x4002, "sts.l mach, @-r0"),
|
||||
(0x4012, "sts.l macl, @-r0"),
|
||||
(0x4022, "sts.l pr, @-r0"),
|
||||
(0x400e, "ldc r0, sr"),
|
||||
(0x401e, "ldc r0, gbr"),
|
||||
(0x402e, "ldc r0, vbr"),
|
||||
(0x400a, "lds r0, mach"),
|
||||
(0x401a, "lds r0, macl"),
|
||||
(0x402b, "jmp @r0"),
|
||||
(0x400b, "jsr @r0"),
|
||||
(0x4007, "ldc.l @r0+, sr"),
|
||||
(0x4017, "ldc.l @r0+, gbr"),
|
||||
(0x4027, "ldc.l @r0+, vbr"),
|
||||
(0x4006, "lds.l @r0+, mach"),
|
||||
(0x4016, "lds.l @r0+, macl"),
|
||||
(0x4026, "lds.l @r0+, pr"),
|
||||
(0x0023, "braf r0"),
|
||||
(0x0003, "bsrf r0"),
|
||||
];
|
||||
|
||||
for (opcode, expected_str) in ops {
|
||||
let code = opcode.to_be_bytes();
|
||||
let mut parts = Vec::new();
|
||||
|
||||
arch.display_instruction(
|
||||
ResolvedInstructionRef {
|
||||
ins_ref: InstructionRef { address: 0x1000, size: 2, opcode, branch_dest: None },
|
||||
code: &code,
|
||||
..Default::default()
|
||||
},
|
||||
&DiffObjConfig::default(),
|
||||
&mut |part| {
|
||||
parts.push(part.into_static());
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let joined_str: String = parts.iter().map(<_>::to_string).collect();
|
||||
assert_eq!(joined_str, expected_str.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sh2_display_instructions_f00f() {
|
||||
let arch = ArchSuperH {};
|
||||
let ops: [(u16, &str); 54] = [
|
||||
(0x300c, "add r0, r0"),
|
||||
(0x300e, "addc r0, r0"),
|
||||
(0x300f, "addv r0, r0"),
|
||||
(0x2009, "and r0, r0"),
|
||||
(0x3000, "cmp/eq r0, r0"),
|
||||
(0x3002, "cmp/hs r0, r0"),
|
||||
(0x3003, "cmp/ge r0, r0"),
|
||||
(0x3006, "cmp/hi r0, r0"),
|
||||
(0x3007, "cmp/gt r0, r0"),
|
||||
(0x200c, "cmp/str r0, r0"),
|
||||
(0x3004, "div1 r0, r0"),
|
||||
(0x2007, "div0s r0, r0"),
|
||||
(0x300d, "dmuls.l r0, r0"),
|
||||
(0x3005, "dmulu.l r0, r0"),
|
||||
(0x600e, "exts.b r0, r0"),
|
||||
(0x600f, "exts.w r0, r0"),
|
||||
(0x600c, "extu.b r0, r0"),
|
||||
(0x600d, "extu.w r0, r0"),
|
||||
(0x6003, "mov r0, r0"),
|
||||
(0x0007, "mul.l r0, r0"),
|
||||
(0x200f, "muls r0, r0"),
|
||||
(0x200e, "mulu r0, r0"),
|
||||
(0x600b, "neg r0, r0"),
|
||||
(0x600a, "negc r0, r0"),
|
||||
(0x6007, "not r0, r0"),
|
||||
(0x200b, "or r0, r0"),
|
||||
(0x3008, "sub r0, r0"),
|
||||
(0x300a, "subc r0, r0"),
|
||||
(0x300b, "subv r0, r0"),
|
||||
(0x6008, "swap.b r0, r0"),
|
||||
(0x6009, "swap.w r0, r0"),
|
||||
(0x2008, "tst r0, r0"),
|
||||
(0x200a, "xor r0, r0"),
|
||||
(0x200d, "xtrct r0, r0"),
|
||||
(0x2000, "mov.b r0, @r0"),
|
||||
(0x2001, "mov.w r0, @r0"),
|
||||
(0x2002, "mov.l r0, @r0"),
|
||||
(0x6000, "mov.b @r0, r0"),
|
||||
(0x6001, "mov.w @r0, r0"),
|
||||
(0x6002, "mov.l @r0, r0"),
|
||||
(0x000f, "mac.l @r0+, @r0+"),
|
||||
(0x400f, "mac.w @r0+, @r0+"),
|
||||
(0x6004, "mov.b @r0+, r0"),
|
||||
(0x6005, "mov.w @r0+, r0"),
|
||||
(0x6006, "mov.l @r0+, r0"),
|
||||
(0x2004, "mov.b r0, @-r0"),
|
||||
(0x2005, "mov.w r0, @-r0"),
|
||||
(0x2006, "mov.l r0, @-r0"),
|
||||
(0x0004, "mov.b r0, @(r0, r0)"),
|
||||
(0x0005, "mov.w r0, @(r0, r0)"),
|
||||
(0x0006, "mov.l r0, @(r0, r0)"),
|
||||
(0x000c, "mov.b @(r0, r0), r0"),
|
||||
(0x000d, "mov.w @(r0, r0), r0"),
|
||||
(0x000e, "mov.l @(r0, r0), r0"),
|
||||
];
|
||||
|
||||
for (opcode, expected_str) in ops {
|
||||
let code = opcode.to_be_bytes();
|
||||
let mut parts = Vec::new();
|
||||
|
||||
arch.display_instruction(
|
||||
ResolvedInstructionRef {
|
||||
ins_ref: InstructionRef { address: 0x1000, size: 2, opcode, branch_dest: None },
|
||||
code: &code,
|
||||
..Default::default()
|
||||
},
|
||||
&DiffObjConfig::default(),
|
||||
&mut |part| {
|
||||
parts.push(part.into_static());
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let joined_str: String = parts.iter().map(<_>::to_string).collect();
|
||||
assert_eq!(joined_str, expected_str.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sh2_display_instruction_mov_immediate_offset() {
|
||||
let arch = ArchSuperH {};
|
||||
let ops: [(u16, &str); 8] = [
|
||||
(0x8000, "mov.b r0, @(0x0, r0)"),
|
||||
(0x8011, "mov.b r0, @(0x1, r1)"),
|
||||
(0x8102, "mov.w r0, @(0x4, r0)"),
|
||||
(0x8113, "mov.w r0, @(0x6, r1)"),
|
||||
(0x8404, "mov.b @(0x4, r0), r0"),
|
||||
(0x8415, "mov.b @(0x5, r1), r0"),
|
||||
(0x8506, "mov.w @(0xc, r0), r0"),
|
||||
(0x8517, "mov.w @(0xe, r1), r0"),
|
||||
];
|
||||
|
||||
for (opcode, expected_str) in ops {
|
||||
let code = opcode.to_be_bytes();
|
||||
let mut parts = Vec::new();
|
||||
|
||||
arch.display_instruction(
|
||||
ResolvedInstructionRef {
|
||||
ins_ref: InstructionRef { address: 0x1000, size: 2, opcode, branch_dest: None },
|
||||
code: &code,
|
||||
..Default::default()
|
||||
},
|
||||
&DiffObjConfig::default(),
|
||||
&mut |part| {
|
||||
parts.push(part.into_static());
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let joined_str: String = parts.iter().map(<_>::to_string).collect();
|
||||
assert_eq!(joined_str, expected_str.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sh2_display_instruction_gbr_and_branches() {
|
||||
let arch = ArchSuperH {};
|
||||
let ops: &[(u16, u32, &str)] = &[
|
||||
(0xc000, 0x0000, "mov.b r0, @(0x0, gbr)"),
|
||||
(0xc07f, 0x0000, "mov.b r0, @(0x7f, gbr)"),
|
||||
(0xc100, 0x0000, "mov.w r0, @(0x0, gbr)"),
|
||||
(0xc17f, 0x0000, "mov.w r0, @(0xfe, gbr)"),
|
||||
(0xc200, 0x0000, "mov.l r0, @(0x0, gbr)"),
|
||||
(0xc27f, 0x0000, "mov.l r0, @(0x1fc, gbr)"),
|
||||
(0xc400, 0x0000, "mov.b @(0x0, gbr), r0"),
|
||||
(0xc47f, 0x0000, "mov.b @(0x7f, gbr), r0"),
|
||||
(0xc500, 0x0000, "mov.w @(0x0, gbr), r0"),
|
||||
(0xc57f, 0x0000, "mov.w @(0xfe, gbr), r0"),
|
||||
(0xc600, 0x0000, "mov.l @(0x0, gbr), r0"),
|
||||
(0xc67f, 0x0000, "mov.l @(0x1fc, gbr), r0"),
|
||||
(0x8b20, 0x1000, "bf 0x44"),
|
||||
(0x8b80, 0x1000, "bf 0xffffff04"),
|
||||
(0x8f10, 0x2000, "bf.s 0x24"),
|
||||
(0x8f90, 0x2000, "bf.s 0xffffff24"),
|
||||
(0x8904, 0x3000, "bt 0xc"),
|
||||
(0x8980, 0x3000, "bt 0xffffff04"),
|
||||
(0x8d04, 0x4000, "bt.s 0xc"),
|
||||
(0x8d80, 0x4000, "bt.s 0xffffff04"),
|
||||
];
|
||||
|
||||
for &(opcode, addr, expected_str) in ops {
|
||||
let code = opcode.to_be_bytes();
|
||||
let mut parts = Vec::new();
|
||||
|
||||
arch.display_instruction(
|
||||
ResolvedInstructionRef {
|
||||
ins_ref: InstructionRef {
|
||||
address: addr as u64,
|
||||
size: 2,
|
||||
opcode,
|
||||
branch_dest: None,
|
||||
},
|
||||
code: &code,
|
||||
..Default::default()
|
||||
},
|
||||
&DiffObjConfig::default(),
|
||||
&mut |part| {
|
||||
parts.push(part.into_static());
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let joined_str: String = parts.iter().map(<_>::to_string).collect();
|
||||
assert_eq!(joined_str, expected_str.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sh2_display_instruction_mov_l() {
|
||||
let arch = ArchSuperH {};
|
||||
let ops: &[(u16, u32, &str)] = &[
|
||||
// mov.l rX, @(0xXXX, rY)
|
||||
(0x1000, 0x0000, "mov.l r0, @(0x0, r0)"),
|
||||
(0x1001, 0x0000, "mov.l r0, @(0x4, r0)"),
|
||||
(0x100f, 0x0000, "mov.l r0, @(0x3c, r0)"),
|
||||
(0x101f, 0x0000, "mov.l r1, @(0x3c, r0)"),
|
||||
// mov.l @(0xXXX, rY), rX
|
||||
(0x5000, 0x0000, "mov.l @(0x0, r0), r0"),
|
||||
];
|
||||
|
||||
for &(opcode, addr, expected_str) in ops {
|
||||
let code = opcode.to_be_bytes();
|
||||
let mut parts = Vec::new();
|
||||
|
||||
arch.display_instruction(
|
||||
ResolvedInstructionRef {
|
||||
ins_ref: InstructionRef {
|
||||
address: addr as u64,
|
||||
size: 2,
|
||||
opcode,
|
||||
branch_dest: None,
|
||||
},
|
||||
code: &code,
|
||||
..Default::default()
|
||||
},
|
||||
&DiffObjConfig::default(),
|
||||
&mut |part| {
|
||||
parts.push(part.into_static());
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let joined_str: String = parts.iter().map(<_>::to_string).collect();
|
||||
assert_eq!(joined_str, expected_str.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sh2_display_instruction_bra_bsr() {
|
||||
let arch: ArchSuperH = ArchSuperH {};
|
||||
let ops: &[(u16, u32, &str)] = &[
|
||||
// bra
|
||||
(0xa000, 0x0000, "bra 0x4"),
|
||||
(0xa001, 0x0000, "bra 0x6"),
|
||||
(0xa800, 0x0000, "bra 0xfffff004"),
|
||||
(0xa801, 0x0000, "bra 0xfffff006"),
|
||||
// bsr
|
||||
(0xb000, 0x0000, "bsr 0x4"),
|
||||
(0xb001, 0x0000, "bsr 0x6"),
|
||||
(0xb800, 0x0000, "bsr 0xfffff004"),
|
||||
(0xb801, 0x0000, "bsr 0xfffff006"),
|
||||
];
|
||||
|
||||
for &(opcode, addr, expected_str) in ops {
|
||||
let code = opcode.to_be_bytes();
|
||||
let mut parts = Vec::new();
|
||||
|
||||
arch.display_instruction(
|
||||
ResolvedInstructionRef {
|
||||
ins_ref: InstructionRef {
|
||||
address: addr as u64,
|
||||
size: 2,
|
||||
opcode,
|
||||
branch_dest: None,
|
||||
},
|
||||
code: &code,
|
||||
..Default::default()
|
||||
},
|
||||
&DiffObjConfig::default(),
|
||||
&mut |part| {
|
||||
parts.push(part.into_static());
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let joined_str: String = parts.iter().map(<_>::to_string).collect();
|
||||
assert_eq!(joined_str, expected_str.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sh2_display_instruction_operations() {
|
||||
let arch = ArchSuperH {};
|
||||
let ops: &[(u16, u32, &str)] = &[
|
||||
(0xcdff, 0x0000, "and.b #0xff, @(r0, gbr)"),
|
||||
(0xcfff, 0x0000, "or.b #0xff, @(r0, gbr)"),
|
||||
(0xccff, 0x0000, "tst.b #0xff, @(r0, gbr)"),
|
||||
(0xceff, 0x0000, "xor.b #0xff, @(r0, gbr)"),
|
||||
(0xc9ff, 0x0000, "and #0xff, r0"),
|
||||
(0x88ff, 0x0000, "cmp/eq #0xff, r0"),
|
||||
(0xcbff, 0x0000, "or #0xff, r0"),
|
||||
(0xc8ff, 0x0000, "tst #0xff, r0"),
|
||||
(0xcaff, 0x0000, "xor #0xff, r0"),
|
||||
(0xc3ff, 0x0000, "trapa #0xff"),
|
||||
];
|
||||
|
||||
for &(opcode, addr, expected_str) in ops {
|
||||
let code = opcode.to_be_bytes();
|
||||
let mut parts = Vec::new();
|
||||
|
||||
arch.display_instruction(
|
||||
ResolvedInstructionRef {
|
||||
ins_ref: InstructionRef {
|
||||
address: addr as u64,
|
||||
size: 2,
|
||||
opcode,
|
||||
branch_dest: None,
|
||||
},
|
||||
code: &code,
|
||||
..Default::default()
|
||||
},
|
||||
&DiffObjConfig::default(),
|
||||
&mut |part| {
|
||||
parts.push(part.into_static());
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let joined_str: String = parts.iter().map(<_>::to_string).collect();
|
||||
assert_eq!(joined_str, expected_str.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sh2_add_mov_unknown_instructions() {
|
||||
let arch = ArchSuperH {};
|
||||
let ops: &[(u16, u32, &str)] = &[
|
||||
(0x70FF, 0x0000, "add #0xff, r0"),
|
||||
(0xE0FF, 0x0000, "mov #0xff, r0"),
|
||||
(0x0000, 0x0000, ".word 0x0000 /* unknown instruction */"),
|
||||
];
|
||||
|
||||
for &(opcode, addr, expected_str) in ops {
|
||||
let code = opcode.to_be_bytes();
|
||||
let mut parts = Vec::new();
|
||||
|
||||
arch.display_instruction(
|
||||
ResolvedInstructionRef {
|
||||
ins_ref: InstructionRef {
|
||||
address: addr as u64,
|
||||
size: 2,
|
||||
opcode,
|
||||
branch_dest: None,
|
||||
},
|
||||
code: &code,
|
||||
..Default::default()
|
||||
},
|
||||
&DiffObjConfig::default(),
|
||||
&mut |part| {
|
||||
parts.push(part.into_static());
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let joined_str: String = parts.iter().map(<_>::to_string).collect();
|
||||
assert_eq!(joined_str, expected_str.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sh2_mov_instructions_with_labels() {
|
||||
let arch = ArchSuperH {};
|
||||
let ops: &[(u16, u32, &str)] =
|
||||
&[(0x9000, 0x0000, "mov.w @(0x4, pc), r0"), (0xd000, 0x0000, "mov.l @(0x4, pc), r0")];
|
||||
|
||||
for &(opcode, addr, expected_str) in ops {
|
||||
let code = opcode.to_be_bytes();
|
||||
let mut parts = Vec::new();
|
||||
|
||||
arch.display_instruction(
|
||||
ResolvedInstructionRef {
|
||||
ins_ref: InstructionRef {
|
||||
address: addr as u64,
|
||||
size: 2,
|
||||
opcode,
|
||||
branch_dest: None,
|
||||
},
|
||||
code: &code,
|
||||
..Default::default()
|
||||
},
|
||||
&DiffObjConfig::default(),
|
||||
&mut |part| {
|
||||
parts.push(part.into_static());
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let joined_str: String = parts.iter().map(<_>::to_string).collect();
|
||||
assert_eq!(joined_str, expected_str.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_func_0606_f378_mov_w_data_labeling() {
|
||||
let arch = ArchSuperH {};
|
||||
let ops: &[(u16, u32, &str)] = &[(0x9000, 0x0606F378, "mov.w @(0x4, pc), r0 /* 0x00B0 */")];
|
||||
|
||||
let mut code = Vec::new();
|
||||
code.extend_from_slice(&0x9000_u16.to_be_bytes());
|
||||
code.extend_from_slice(&0x0009_u16.to_be_bytes());
|
||||
code.extend_from_slice(&0x00B0_u16.to_be_bytes());
|
||||
|
||||
for &(opcode, addr, expected_str) in ops {
|
||||
let mut parts = Vec::new();
|
||||
|
||||
arch.display_instruction(
|
||||
ResolvedInstructionRef {
|
||||
ins_ref: InstructionRef {
|
||||
address: addr as u64,
|
||||
size: 2,
|
||||
opcode,
|
||||
branch_dest: None,
|
||||
},
|
||||
code: &opcode.to_be_bytes(),
|
||||
symbol: &Symbol {
|
||||
address: 0x0606F378, // func base address
|
||||
size: code.len() as u64,
|
||||
..Default::default()
|
||||
},
|
||||
section: &Section {
|
||||
address: 0x0606F378,
|
||||
size: code.len() as u64,
|
||||
data: SectionData(code.clone()),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
&DiffObjConfig::default(),
|
||||
&mut |part| {
|
||||
parts.push(part.into_static());
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let joined_str: String = parts.iter().map(<_>::to_string).collect();
|
||||
assert_eq!(joined_str, expected_str.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_func_0606_f378_mov_l_data_labeling() {
|
||||
let arch = ArchSuperH {};
|
||||
let ops: &[(u16, u32, &str)] =
|
||||
&[(0xd000, 0x0606F378, "mov.l @(0x4, pc), r0 /* 0x00B000B0 */")];
|
||||
|
||||
let mut code = Vec::new();
|
||||
code.extend_from_slice(&0xd000_u16.to_be_bytes());
|
||||
code.extend_from_slice(&0x0009_u16.to_be_bytes());
|
||||
code.extend_from_slice(&0x00B0_u16.to_be_bytes());
|
||||
code.extend_from_slice(&0x00B0_u16.to_be_bytes());
|
||||
|
||||
for &(opcode, addr, expected_str) in ops {
|
||||
let mut parts = Vec::new();
|
||||
|
||||
arch.display_instruction(
|
||||
ResolvedInstructionRef {
|
||||
ins_ref: InstructionRef {
|
||||
address: addr as u64,
|
||||
size: 2,
|
||||
opcode,
|
||||
branch_dest: None,
|
||||
},
|
||||
code: &opcode.to_be_bytes(),
|
||||
symbol: &Symbol {
|
||||
address: 0x0606F378, // func base address
|
||||
size: code.len() as u64,
|
||||
..Default::default()
|
||||
},
|
||||
section: &Section {
|
||||
address: 0x0606F378,
|
||||
size: code.len() as u64,
|
||||
data: SectionData(code.clone()),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
&DiffObjConfig::default(),
|
||||
&mut |part| {
|
||||
parts.push(part.into_static());
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let joined_str: String = parts.iter().map(<_>::to_string).collect();
|
||||
assert_eq!(joined_str, expected_str.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +1,51 @@
|
||||
use alloc::{boxed::Box, string::String, vec::Vec};
|
||||
use alloc::{boxed::Box, format, string::String, vec::Vec};
|
||||
use core::cmp::Ordering;
|
||||
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use anyhow::{Context, Result, anyhow, bail};
|
||||
use iced_x86::{
|
||||
Decoder, DecoderOptions, DecoratorKind, FormatterOutput, FormatterTextKind, GasFormatter,
|
||||
Instruction, IntelFormatter, MasmFormatter, NasmFormatter, NumberKind, OpKind, Register,
|
||||
};
|
||||
use object::{pe, Endian as _, Object as _, ObjectSection as _};
|
||||
use object::{Endian as _, Object as _, ObjectSection as _, elf, pe};
|
||||
|
||||
use crate::{
|
||||
arch::Arch,
|
||||
diff::{display::InstructionPart, DiffObjConfig, X86Formatter},
|
||||
obj::{InstructionRef, RelocationFlags, ResolvedInstructionRef, ScannedInstruction},
|
||||
arch::{Arch, RelocationOverride, RelocationOverrideTarget},
|
||||
diff::{DiffObjConfig, X86Formatter, display::InstructionPart},
|
||||
obj::{InstructionRef, Relocation, RelocationFlags, ResolvedInstructionRef, Section, Symbol},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ArchX86 {
|
||||
bits: u32,
|
||||
arch: Architecture,
|
||||
endianness: object::Endianness,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Architecture {
|
||||
X86,
|
||||
X86_64,
|
||||
}
|
||||
|
||||
impl ArchX86 {
|
||||
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> {
|
||||
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> {
|
||||
@@ -38,20 +58,79 @@ impl ArchX86 {
|
||||
formatter.options_mut().set_space_after_operand_separator(diff_config.space_between_args);
|
||||
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,
|
||||
},
|
||||
RelocationFlags::Elf(typ) => match typ {
|
||||
elf::R_386_32 | elf::R_386_PC32 => Some(4),
|
||||
elf::R_386_16 => Some(2),
|
||||
_ => None,
|
||||
},
|
||||
},
|
||||
Architecture::X86_64 => match flags {
|
||||
RelocationFlags::Coff(typ) => match typ {
|
||||
pe::IMAGE_REL_AMD64_ADDR32NB | pe::IMAGE_REL_AMD64_REL32 => Some(4),
|
||||
pe::IMAGE_REL_AMD64_ADDR64 => Some(8),
|
||||
_ => None,
|
||||
},
|
||||
RelocationFlags::Elf(typ) => match typ {
|
||||
elf::R_X86_64_PC32 => Some(4),
|
||||
elf::R_X86_64_64 => Some(8),
|
||||
_ => None,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const DATA_OPCODE: u16 = u16::MAX - 1;
|
||||
|
||||
impl Arch for ArchX86 {
|
||||
fn scan_instructions(
|
||||
fn scan_instructions_internal(
|
||||
&self,
|
||||
address: u64,
|
||||
code: &[u8],
|
||||
_section_index: usize,
|
||||
relocations: &[Relocation],
|
||||
_diff_config: &DiffObjConfig,
|
||||
) -> Result<Vec<ScannedInstruction>> {
|
||||
) -> Result<Vec<InstructionRef>> {
|
||||
let mut out = Vec::with_capacity(code.len() / 2);
|
||||
let mut decoder = self.decoder(code, address);
|
||||
let mut instruction = Instruction::default();
|
||||
while decoder.can_decode() {
|
||||
let mut reloc_iter = relocations.iter().peekable();
|
||||
'outer: while decoder.can_decode() {
|
||||
let address = decoder.ip();
|
||||
while let Some(reloc) = reloc_iter.peek() {
|
||||
match reloc.address.cmp(&address) {
|
||||
Ordering::Less => {
|
||||
reloc_iter.next();
|
||||
}
|
||||
Ordering::Equal => {
|
||||
// If the instruction starts at a relocation, it's inline data
|
||||
let size = self.reloc_size(reloc.flags).with_context(|| {
|
||||
format!("Unsupported inline x86 relocation {:?}", reloc.flags)
|
||||
})?;
|
||||
if decoder.set_position(decoder.position() + size).is_ok() {
|
||||
decoder.set_ip(address + size as u64);
|
||||
out.push(InstructionRef {
|
||||
address,
|
||||
size: size as u8,
|
||||
opcode: DATA_OPCODE,
|
||||
branch_dest: None,
|
||||
});
|
||||
reloc_iter.next();
|
||||
continue 'outer;
|
||||
}
|
||||
}
|
||||
Ordering::Greater => break,
|
||||
}
|
||||
}
|
||||
decoder.decode_out(&mut instruction);
|
||||
let branch_dest = match instruction.op0_kind() {
|
||||
OpKind::NearBranch16 => Some(instruction.near_branch16() as u64),
|
||||
@@ -59,12 +138,10 @@ impl Arch for ArchX86 {
|
||||
OpKind::NearBranch64 => Some(instruction.near_branch64()),
|
||||
_ => None,
|
||||
};
|
||||
out.push(ScannedInstruction {
|
||||
ins_ref: InstructionRef {
|
||||
address: instruction.ip(),
|
||||
size: instruction.len() as u8,
|
||||
opcode: instruction.mnemonic() as u16,
|
||||
},
|
||||
out.push(InstructionRef {
|
||||
address,
|
||||
size: instruction.len() as u8,
|
||||
opcode: instruction.mnemonic() as u16,
|
||||
branch_dest,
|
||||
});
|
||||
}
|
||||
@@ -77,6 +154,21 @@ impl Arch for ArchX86 {
|
||||
diff_config: &DiffObjConfig,
|
||||
cb: &mut dyn FnMut(InstructionPart) -> Result<()>,
|
||||
) -> Result<()> {
|
||||
if resolved.ins_ref.opcode == DATA_OPCODE {
|
||||
let (mnemonic, imm) = match resolved.ins_ref.size {
|
||||
2 => (".word", self.endianness.read_u16_bytes(resolved.code.try_into()?) as u64),
|
||||
4 => (".dword", self.endianness.read_u32_bytes(resolved.code.try_into()?) as u64),
|
||||
_ => bail!("Unsupported x86 inline data size {}", resolved.ins_ref.size),
|
||||
};
|
||||
cb(InstructionPart::opcode(mnemonic, DATA_OPCODE))?;
|
||||
if resolved.relocation.is_some() {
|
||||
cb(InstructionPart::reloc())?;
|
||||
} else {
|
||||
cb(InstructionPart::unsigned(imm))?;
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut decoder = self.decoder(resolved.code, resolved.ins_ref.address);
|
||||
let mut formatter = self.formatter(diff_config);
|
||||
let mut instruction = Instruction::default();
|
||||
@@ -88,17 +180,16 @@ impl Arch for ArchX86 {
|
||||
// memory operand.
|
||||
let mut reloc_replace = None;
|
||||
if let Some(reloc) = resolved.relocation {
|
||||
const PLACEHOLDER: u64 = 0x7BDE3E7D; // chosen by fair dice roll.
|
||||
// guaranteed to be random.
|
||||
const PLACEHOLDER: u64 = 0x7BDE3E7D; // chosen by fair dice roll. guaranteed to be random.
|
||||
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);
|
||||
if reloc_offset == offsets.displacement_offset() as u64
|
||||
&& reloc_size == offsets.displacement_size()
|
||||
{
|
||||
instruction.set_memory_displacement64(PLACEHOLDER);
|
||||
// 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
|
||||
&& reloc_size == offsets.immediate_size()
|
||||
{
|
||||
@@ -116,18 +207,12 @@ impl Arch for ArchX86 {
|
||||
_ => OpKind::default(),
|
||||
}
|
||||
};
|
||||
let number_kind = match reloc_size {
|
||||
2 => NumberKind::UInt16,
|
||||
4 => NumberKind::UInt32,
|
||||
8 => NumberKind::UInt64,
|
||||
_ => NumberKind::default(),
|
||||
};
|
||||
if is_branch {
|
||||
instruction.set_near_branch64(PLACEHOLDER);
|
||||
} else {
|
||||
instruction.set_immediate32(PLACEHOLDER as u32);
|
||||
}
|
||||
reloc_replace = Some((op_kind, number_kind, PLACEHOLDER));
|
||||
reloc_replace = Some((op_kind, reloc_size, PLACEHOLDER));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,21 +225,47 @@ impl Arch for ArchX86 {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn implcit_addend(
|
||||
fn relocation_override(
|
||||
&self,
|
||||
_file: &object::File<'_>,
|
||||
section: &object::Section,
|
||||
address: u64,
|
||||
_relocation: &object::Relocation,
|
||||
flags: RelocationFlags,
|
||||
) -> Result<i64> {
|
||||
match flags {
|
||||
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()?;
|
||||
Ok(self.endianness.read_i32_bytes(data) as i64)
|
||||
}
|
||||
flags => bail!("Unsupported x86 implicit relocation {flags:?}"),
|
||||
relocation: &object::Relocation,
|
||||
) -> Result<Option<RelocationOverride>> {
|
||||
if !relocation.has_implicit_addend() {
|
||||
return Ok(None);
|
||||
}
|
||||
let addend = match self.arch {
|
||||
Architecture::X86 => match relocation.flags() {
|
||||
object::RelocationFlags::Coff {
|
||||
typ: pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32,
|
||||
}
|
||||
| object::RelocationFlags::Elf { r_type: elf::R_386_32 | elf::R_386_PC32 } => {
|
||||
let data =
|
||||
section.data()?[address as usize..address as usize + 4].try_into()?;
|
||||
self.endianness.read_i32_bytes(data) as i64
|
||||
}
|
||||
flags => bail!("Unsupported x86 implicit relocation {flags:?}"),
|
||||
},
|
||||
Architecture::X86_64 => match relocation.flags() {
|
||||
object::RelocationFlags::Coff {
|
||||
typ: pe::IMAGE_REL_AMD64_ADDR32NB | pe::IMAGE_REL_AMD64_REL32,
|
||||
}
|
||||
| object::RelocationFlags::Elf { r_type: elf::R_X86_64_32 | elf::R_X86_64_PC32 } => {
|
||||
let data =
|
||||
section.data()?[address as usize..address as usize + 4].try_into()?;
|
||||
self.endianness.read_i32_bytes(data) as i64
|
||||
}
|
||||
object::RelocationFlags::Coff { typ: pe::IMAGE_REL_AMD64_ADDR64 }
|
||||
| object::RelocationFlags::Elf { r_type: elf::R_X86_64_64 } => {
|
||||
let data =
|
||||
section.data()?[address as usize..address as usize + 8].try_into()?;
|
||||
self.endianness.read_i64_bytes(data)
|
||||
}
|
||||
flags => bail!("Unsupported x86-64 implicit relocation {flags:?}"),
|
||||
},
|
||||
};
|
||||
Ok(Some(RelocationOverride { target: RelocationOverrideTarget::Keep, addend }))
|
||||
}
|
||||
|
||||
fn demangle(&self, name: &str) -> Option<String> {
|
||||
@@ -168,33 +279,81 @@ impl Arch for ArchX86 {
|
||||
}
|
||||
|
||||
fn reloc_name(&self, flags: RelocationFlags) -> Option<&'static str> {
|
||||
match flags {
|
||||
RelocationFlags::Coff(typ) => match typ {
|
||||
pe::IMAGE_REL_I386_DIR32 => Some("IMAGE_REL_I386_DIR32"),
|
||||
pe::IMAGE_REL_I386_REL32 => Some("IMAGE_REL_I386_REL32"),
|
||||
match self.arch {
|
||||
Architecture::X86 => match flags {
|
||||
RelocationFlags::Coff(typ) => match typ {
|
||||
pe::IMAGE_REL_I386_DIR32 => Some("IMAGE_REL_I386_DIR32"),
|
||||
pe::IMAGE_REL_I386_REL32 => Some("IMAGE_REL_I386_REL32"),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
},
|
||||
Architecture::X86_64 => match flags {
|
||||
RelocationFlags::Coff(typ) => match typ {
|
||||
pe::IMAGE_REL_AMD64_ADDR64 => Some("IMAGE_REL_AMD64_ADDR64"),
|
||||
pe::IMAGE_REL_AMD64_ADDR32NB => Some("IMAGE_REL_AMD64_ADDR32NB"),
|
||||
pe::IMAGE_REL_AMD64_REL32 => Some("IMAGE_REL_AMD64_REL32"),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn data_reloc_size(&self, flags: RelocationFlags) -> usize { reloc_size(flags).unwrap_or(1) }
|
||||
}
|
||||
fn data_reloc_size(&self, flags: RelocationFlags) -> usize {
|
||||
self.reloc_size(flags).unwrap_or(1)
|
||||
}
|
||||
|
||||
fn reloc_size(flags: RelocationFlags) -> Option<usize> {
|
||||
match flags {
|
||||
RelocationFlags::Coff(typ) => match typ {
|
||||
pe::IMAGE_REL_I386_DIR16 | pe::IMAGE_REL_I386_REL16 => Some(2),
|
||||
pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32 => Some(4),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
fn infer_function_size(
|
||||
&self,
|
||||
symbol: &Symbol,
|
||||
section: &Section,
|
||||
next_address: u64,
|
||||
) -> Result<u64> {
|
||||
let Ok(size) = (next_address - symbol.address).try_into() else {
|
||||
return Ok(next_address.saturating_sub(symbol.address));
|
||||
};
|
||||
let Some(code) = section.data_range(symbol.address, size) else {
|
||||
return Ok(0);
|
||||
};
|
||||
// Decode instructions to find the last non-NOP instruction
|
||||
let mut decoder = self.decoder(code, symbol.address);
|
||||
let mut instruction = Instruction::default();
|
||||
let mut new_address = 0;
|
||||
let mut reloc_iter = section.relocations.iter().peekable();
|
||||
'outer: while decoder.can_decode() {
|
||||
let address = decoder.ip();
|
||||
while let Some(reloc) = reloc_iter.peek() {
|
||||
match reloc.address.cmp(&address) {
|
||||
Ordering::Less => {
|
||||
reloc_iter.next();
|
||||
}
|
||||
Ordering::Equal => {
|
||||
// If the instruction starts at a relocation, it's inline data
|
||||
let reloc_size = self.reloc_size(reloc.flags).with_context(|| {
|
||||
format!("Unsupported inline x86 relocation {:?}", reloc.flags)
|
||||
})?;
|
||||
if decoder.set_position(decoder.position() + reloc_size).is_ok() {
|
||||
new_address = address + reloc_size as u64;
|
||||
decoder.set_ip(new_address);
|
||||
continue 'outer;
|
||||
}
|
||||
}
|
||||
Ordering::Greater => break,
|
||||
}
|
||||
}
|
||||
decoder.decode_out(&mut instruction);
|
||||
if instruction.mnemonic() != iced_x86::Mnemonic::Nop {
|
||||
new_address = instruction.next_ip();
|
||||
}
|
||||
}
|
||||
Ok(new_address.saturating_sub(symbol.address))
|
||||
}
|
||||
}
|
||||
|
||||
struct InstructionFormatterOutput<'a> {
|
||||
cb: &'a mut dyn FnMut(InstructionPart<'_>) -> Result<()>,
|
||||
reloc_replace: Option<(OpKind, NumberKind, u64)>,
|
||||
reloc_replace: Option<(OpKind, usize, u64)>,
|
||||
error: Option<anyhow::Error>,
|
||||
skip_next: bool,
|
||||
}
|
||||
@@ -269,11 +428,19 @@ impl FormatterOutput for InstructionFormatterOutput<'_> {
|
||||
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)
|
||||
{
|
||||
#[allow(clippy::match_like_matches_macro)]
|
||||
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, 4) // x86_64
|
||||
| (NumberKind::Int64 | NumberKind::UInt64, 8) => true,
|
||||
_ => false,
|
||||
}
|
||||
&& value == target_value
|
||||
{
|
||||
if let Err(e) = (self.cb)(InstructionPart::reloc()) {
|
||||
@@ -343,32 +510,33 @@ mod test {
|
||||
|
||||
#[test]
|
||||
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 = [
|
||||
0xc7, 0x85, 0x68, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x8b, 0x04, 0x85, 0x00,
|
||||
0x00, 0x00, 0x00,
|
||||
];
|
||||
let scanned = arch.scan_instructions(0, &code, 0, &DiffObjConfig::default()).unwrap();
|
||||
let scanned =
|
||||
arch.scan_instructions_internal(0, &code, 0, &[], &DiffObjConfig::default()).unwrap();
|
||||
assert_eq!(scanned.len(), 2);
|
||||
assert_eq!(scanned[0].ins_ref.address, 0);
|
||||
assert_eq!(scanned[0].ins_ref.size, 10);
|
||||
assert_eq!(scanned[0].ins_ref.opcode, iced_x86::Mnemonic::Mov as u16);
|
||||
assert_eq!(scanned[0].address, 0);
|
||||
assert_eq!(scanned[0].size, 10);
|
||||
assert_eq!(scanned[0].opcode, iced_x86::Mnemonic::Mov as u16);
|
||||
assert_eq!(scanned[0].branch_dest, None);
|
||||
assert_eq!(scanned[1].ins_ref.address, 10);
|
||||
assert_eq!(scanned[1].ins_ref.size, 7);
|
||||
assert_eq!(scanned[1].ins_ref.opcode, iced_x86::Mnemonic::Mov as u16);
|
||||
assert_eq!(scanned[1].address, 10);
|
||||
assert_eq!(scanned[1].size, 7);
|
||||
assert_eq!(scanned[1].opcode, iced_x86::Mnemonic::Mov as u16);
|
||||
assert_eq!(scanned[1].branch_dest, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
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 opcode = iced_x86::Mnemonic::Mov as u16;
|
||||
let mut parts = Vec::new();
|
||||
arch.display_instruction(
|
||||
ResolvedInstructionRef {
|
||||
ins_ref: InstructionRef { address: 0x1234, size: 10, opcode },
|
||||
ins_ref: InstructionRef { address: 0x1234, size: 10, opcode, branch_dest: None },
|
||||
code: &code,
|
||||
..Default::default()
|
||||
},
|
||||
@@ -398,13 +566,13 @@ mod test {
|
||||
|
||||
#[test]
|
||||
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 opcode = iced_x86::Mnemonic::Mov as u16;
|
||||
let mut parts = Vec::new();
|
||||
arch.display_instruction(
|
||||
ResolvedInstructionRef {
|
||||
ins_ref: InstructionRef { address: 0x1234, size: 10, opcode },
|
||||
ins_ref: InstructionRef { address: 0x1234, size: 10, opcode, branch_dest: None },
|
||||
code: &code,
|
||||
relocation: Some(ResolvedRelocation {
|
||||
relocation: &Relocation {
|
||||
@@ -443,13 +611,13 @@ mod test {
|
||||
|
||||
#[test]
|
||||
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 opcode = iced_x86::Mnemonic::Mov as u16;
|
||||
let mut parts = Vec::new();
|
||||
arch.display_instruction(
|
||||
ResolvedInstructionRef {
|
||||
ins_ref: InstructionRef { address: 0x1234, size: 7, opcode },
|
||||
ins_ref: InstructionRef { address: 0x1234, size: 7, opcode, branch_dest: None },
|
||||
code: &code,
|
||||
relocation: Some(ResolvedRelocation {
|
||||
relocation: &Relocation {
|
||||
@@ -486,13 +654,13 @@ mod test {
|
||||
|
||||
#[test]
|
||||
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 opcode = iced_x86::Mnemonic::Call as u16;
|
||||
let mut parts = Vec::new();
|
||||
arch.display_instruction(
|
||||
ResolvedInstructionRef {
|
||||
ins_ref: InstructionRef { address: 0x1234, size: 5, opcode },
|
||||
ins_ref: InstructionRef { address: 0x1234, size: 5, opcode, branch_dest: None },
|
||||
code: &code,
|
||||
relocation: Some(ResolvedRelocation {
|
||||
relocation: &Relocation {
|
||||
@@ -514,4 +682,113 @@ mod test {
|
||||
.unwrap();
|
||||
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, branch_dest: None },
|
||||
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("]"),
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_process_x86_64_instruction_with_reloc_1() {
|
||||
let arch = ArchX86 { arch: Architecture::X86_64, endianness: object::Endianness::Little };
|
||||
let code = [0x48, 0x8b, 0x05, 0x00, 0x00, 0x00, 0x00];
|
||||
let opcode = iced_x86::Mnemonic::Mov as u16;
|
||||
let mut parts = Vec::new();
|
||||
arch.display_instruction(
|
||||
ResolvedInstructionRef {
|
||||
ins_ref: InstructionRef { address: 0x1234, size: 7, opcode, branch_dest: None },
|
||||
code: &code,
|
||||
relocation: Some(ResolvedRelocation {
|
||||
relocation: &Relocation {
|
||||
flags: RelocationFlags::Coff(pe::IMAGE_REL_AMD64_REL32),
|
||||
address: 0x1234 + 3,
|
||||
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("rax"),
|
||||
InstructionPart::basic(","),
|
||||
InstructionPart::basic(" "),
|
||||
InstructionPart::basic("["),
|
||||
InstructionPart::reloc(),
|
||||
InstructionPart::basic("]"),
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_process_x86_64_instruction_with_reloc_2() {
|
||||
let arch = ArchX86 { arch: Architecture::X86_64, endianness: object::Endianness::Little };
|
||||
let code = [0xe8, 0x00, 0x00, 0x00, 0x00];
|
||||
let opcode = iced_x86::Mnemonic::Call as u16;
|
||||
let mut parts = Vec::new();
|
||||
arch.display_instruction(
|
||||
ResolvedInstructionRef {
|
||||
ins_ref: InstructionRef { address: 0x1234, size: 5, opcode, branch_dest: None },
|
||||
code: &code,
|
||||
relocation: Some(ResolvedRelocation {
|
||||
relocation: &Relocation {
|
||||
flags: RelocationFlags::Coff(pe::IMAGE_REL_AMD64_REL32),
|
||||
address: 0x1234 + 1,
|
||||
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("call", opcode), InstructionPart::reloc()]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ use alloc::{
|
||||
};
|
||||
use core::ops::AddAssign;
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use anyhow::{Result, bail};
|
||||
use prost::Message;
|
||||
|
||||
// Protobuf report types
|
||||
@@ -434,6 +434,7 @@ impl From<LegacyReportItem> for ReportItem {
|
||||
demangled_name: value.demangled_name,
|
||||
virtual_address: value.address,
|
||||
}),
|
||||
address: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -441,11 +442,7 @@ impl From<LegacyReportItem> for ReportItem {
|
||||
#[cfg(feature = "serde")]
|
||||
fn serialize_hex<S>(x: &Option<u64>, s: S) -> Result<S::Ok, S::Error>
|
||||
where S: serde::Serializer {
|
||||
if let Some(x) = x {
|
||||
s.serialize_str(&format!("{:#x}", x))
|
||||
} else {
|
||||
s.serialize_none()
|
||||
}
|
||||
if let Some(x) = x { s.serialize_str(&format!("{x:#x}")) } else { s.serialize_none() }
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
|
||||
@@ -2,8 +2,8 @@ use std::{
|
||||
fs,
|
||||
path::{Path, PathBuf},
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
atomic::{AtomicBool, Ordering},
|
||||
},
|
||||
task::Waker,
|
||||
time::Duration,
|
||||
@@ -11,7 +11,7 @@ use std::{
|
||||
|
||||
use globset::GlobSet;
|
||||
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<
|
||||
notify::RecommendedWatcher,
|
||||
|
||||
@@ -6,7 +6,7 @@ use alloc::{
|
||||
vec::Vec,
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use anyhow::{Context, Result, anyhow};
|
||||
use globset::{Glob, GlobSet, GlobSetBuilder};
|
||||
use path::unix_path_serde_option;
|
||||
use typed_path::Utf8UnixPathBuf;
|
||||
|
||||
@@ -31,11 +31,7 @@ pub mod unix_path_serde_option {
|
||||
|
||||
pub fn serialize<S>(path: &Option<Utf8UnixPathBuf>, s: S) -> Result<S::Ok, S::Error>
|
||||
where S: Serializer {
|
||||
if let Some(path) = path {
|
||||
s.serialize_some(path.as_str())
|
||||
} else {
|
||||
s.serialize_none()
|
||||
}
|
||||
if let Some(path) = path { s.serialize_some(path.as_str()) } else { s.serialize_none() }
|
||||
}
|
||||
|
||||
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>
|
||||
where S: Serializer {
|
||||
if let Some(path) = path {
|
||||
s.serialize_some(path.as_str())
|
||||
} else {
|
||||
s.serialize_none()
|
||||
}
|
||||
if let Some(path) = path { s.serialize_some(path.as_str()) } else { s.serialize_none() }
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Utf8PlatformPathBuf>, D::Error>
|
||||
|
||||
@@ -1,27 +1,28 @@
|
||||
use alloc::{
|
||||
collections::{btree_map, BTreeMap},
|
||||
collections::{BTreeMap, btree_map},
|
||||
string::{String, ToString},
|
||||
vec,
|
||||
vec::Vec,
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, ensure, Context, Result};
|
||||
use anyhow::{Context, Result, anyhow, ensure};
|
||||
|
||||
use super::{
|
||||
DiffObjConfig, FunctionRelocDiffs, InstructionArgDiffIndex, InstructionBranchFrom,
|
||||
InstructionBranchTo, InstructionDiffKind, InstructionDiffRow, SymbolDiff,
|
||||
display::display_ins_data_literals,
|
||||
};
|
||||
use crate::obj::{
|
||||
InstructionArg, InstructionArgValue, InstructionRef, Object, ResolvedRelocation,
|
||||
ScannedInstruction, SymbolFlag, SymbolKind,
|
||||
InstructionArg, InstructionArgValue, InstructionRef, Object, ResolvedInstructionRef,
|
||||
ResolvedRelocation, ResolvedSymbol, SymbolFlag, SymbolKind,
|
||||
};
|
||||
|
||||
pub fn no_diff_code(
|
||||
obj: &Object,
|
||||
symbol_idx: usize,
|
||||
symbol_index: usize,
|
||||
diff_config: &DiffObjConfig,
|
||||
) -> Result<SymbolDiff> {
|
||||
let symbol = &obj.symbols[symbol_idx];
|
||||
let symbol = &obj.symbols[symbol_index];
|
||||
let section_index = symbol.section.ok_or_else(|| anyhow!("Missing section for symbol"))?;
|
||||
let section = &obj.sections[section_index];
|
||||
let data = section.data_range(symbol.address, symbol.size as usize).ok_or_else(|| {
|
||||
@@ -31,13 +32,15 @@ pub fn no_diff_code(
|
||||
symbol.address + symbol.size
|
||||
)
|
||||
})?;
|
||||
let ops = obj.arch.scan_instructions(symbol.address, data, section_index, diff_config)?;
|
||||
let ops = obj.arch.scan_instructions(
|
||||
ResolvedSymbol { obj, symbol_index, symbol, section_index, section, data },
|
||||
diff_config,
|
||||
)?;
|
||||
let mut instruction_rows = Vec::<InstructionDiffRow>::new();
|
||||
for i in &ops {
|
||||
instruction_rows
|
||||
.push(InstructionDiffRow { ins_ref: Some(i.ins_ref), ..Default::default() });
|
||||
instruction_rows.push(InstructionDiffRow { ins_ref: Some(*i), ..Default::default() });
|
||||
}
|
||||
resolve_branches(obj, section_index, &ops, &mut instruction_rows);
|
||||
resolve_branches(&ops, &mut instruction_rows);
|
||||
Ok(SymbolDiff { target_symbol: None, match_percent: None, diff_score: None, instruction_rows })
|
||||
}
|
||||
|
||||
@@ -85,20 +88,30 @@ pub fn diff_code(
|
||||
let left_section_idx = left_symbol.section.unwrap();
|
||||
let right_section_idx = right_symbol.section.unwrap();
|
||||
let left_ops = left_obj.arch.scan_instructions(
|
||||
left_symbol.address,
|
||||
left_data,
|
||||
left_section_idx,
|
||||
ResolvedSymbol {
|
||||
obj: left_obj,
|
||||
symbol_index: left_symbol_idx,
|
||||
symbol: left_symbol,
|
||||
section_index: left_section_idx,
|
||||
section: left_section,
|
||||
data: left_data,
|
||||
},
|
||||
diff_config,
|
||||
)?;
|
||||
let right_ops = left_obj.arch.scan_instructions(
|
||||
right_symbol.address,
|
||||
right_data,
|
||||
right_section_idx,
|
||||
let right_ops = right_obj.arch.scan_instructions(
|
||||
ResolvedSymbol {
|
||||
obj: right_obj,
|
||||
symbol_index: right_symbol_idx,
|
||||
symbol: right_symbol,
|
||||
section_index: right_section_idx,
|
||||
section: right_section,
|
||||
data: right_data,
|
||||
},
|
||||
diff_config,
|
||||
)?;
|
||||
let (mut left_rows, mut right_rows) = diff_instructions(&left_ops, &right_ops)?;
|
||||
resolve_branches(left_obj, left_section_idx, &left_ops, &mut left_rows);
|
||||
resolve_branches(right_obj, right_section_idx, &right_ops, &mut right_rows);
|
||||
resolve_branches(&left_ops, &mut left_rows);
|
||||
resolve_branches(&right_ops, &mut right_rows);
|
||||
|
||||
let mut diff_state = InstructionDiffState::default();
|
||||
for (left_row, right_row) in left_rows.iter_mut().zip(right_rows.iter_mut()) {
|
||||
@@ -145,21 +158,21 @@ pub fn diff_code(
|
||||
}
|
||||
|
||||
fn diff_instructions(
|
||||
left_insts: &[ScannedInstruction],
|
||||
right_insts: &[ScannedInstruction],
|
||||
left_insts: &[InstructionRef],
|
||||
right_insts: &[InstructionRef],
|
||||
) -> Result<(Vec<InstructionDiffRow>, Vec<InstructionDiffRow>)> {
|
||||
let left_ops = left_insts.iter().map(|i| i.ins_ref.opcode).collect::<Vec<_>>();
|
||||
let right_ops = right_insts.iter().map(|i| i.ins_ref.opcode).collect::<Vec<_>>();
|
||||
let left_ops = left_insts.iter().map(|i| i.opcode).collect::<Vec<_>>();
|
||||
let right_ops = right_insts.iter().map(|i| i.opcode).collect::<Vec<_>>();
|
||||
let ops = similar::capture_diff_slices(similar::Algorithm::Patience, &left_ops, &right_ops);
|
||||
if ops.is_empty() {
|
||||
ensure!(left_insts.len() == right_insts.len());
|
||||
let left_diff = left_insts
|
||||
.iter()
|
||||
.map(|i| InstructionDiffRow { ins_ref: Some(i.ins_ref), ..Default::default() })
|
||||
.map(|i| InstructionDiffRow { ins_ref: Some(*i), ..Default::default() })
|
||||
.collect();
|
||||
let right_diff = right_insts
|
||||
.iter()
|
||||
.map(|i| InstructionDiffRow { ins_ref: Some(i.ins_ref), ..Default::default() })
|
||||
.map(|i| InstructionDiffRow { ins_ref: Some(*i), ..Default::default() })
|
||||
.collect();
|
||||
return Ok((left_diff, right_diff));
|
||||
}
|
||||
@@ -178,14 +191,17 @@ fn diff_instructions(
|
||||
for op in ops {
|
||||
let (_tag, left_range, right_range) = op.as_tag_tuple();
|
||||
let len = left_range.len().max(right_range.len());
|
||||
left_diff.extend(left_range.clone().map(|i| InstructionDiffRow {
|
||||
ins_ref: Some(left_insts[i].ins_ref),
|
||||
..Default::default()
|
||||
}));
|
||||
right_diff.extend(right_range.clone().map(|i| InstructionDiffRow {
|
||||
ins_ref: Some(right_insts[i].ins_ref),
|
||||
..Default::default()
|
||||
}));
|
||||
left_diff.extend(
|
||||
left_range
|
||||
.clone()
|
||||
.map(|i| InstructionDiffRow { ins_ref: Some(left_insts[i]), ..Default::default() }),
|
||||
);
|
||||
right_diff.extend(
|
||||
right_range.clone().map(|i| InstructionDiffRow {
|
||||
ins_ref: Some(right_insts[i]),
|
||||
..Default::default()
|
||||
}),
|
||||
);
|
||||
if left_range.len() < len {
|
||||
left_diff.extend((left_range.len()..len).map(|_| InstructionDiffRow::default()));
|
||||
}
|
||||
@@ -206,13 +222,7 @@ fn arg_to_string(arg: &InstructionArg, reloc: Option<ResolvedRelocation>) -> Str
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_branches(
|
||||
obj: &Object,
|
||||
section_index: usize,
|
||||
ops: &[ScannedInstruction],
|
||||
rows: &mut [InstructionDiffRow],
|
||||
) {
|
||||
let section = &obj.sections[section_index];
|
||||
fn resolve_branches(ops: &[InstructionRef], rows: &mut [InstructionDiffRow]) {
|
||||
let mut branch_idx = 0u32;
|
||||
// Map addresses to indices
|
||||
let mut addr_map = BTreeMap::<u64, u32>::new();
|
||||
@@ -226,17 +236,7 @@ fn resolve_branches(
|
||||
for ((i, ins_diff), ins) in
|
||||
rows.iter_mut().enumerate().filter(|(_, row)| row.ins_ref.is_some()).zip(ops)
|
||||
{
|
||||
let branch_dest = if let Some(resolved) = section.relocation_at(obj, ins.ins_ref) {
|
||||
if resolved.symbol.section == Some(section_index) {
|
||||
// If the relocation target is in the same section, use it as the branch destination
|
||||
resolved.symbol.address.checked_add_signed(resolved.relocation.addend)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
ins.branch_dest
|
||||
};
|
||||
if let Some(ins_idx) = branch_dest.and_then(|a| addr_map.get(&a).copied()) {
|
||||
if let Some(ins_idx) = ins.branch_dest.and_then(|a| addr_map.get(&a).copied()) {
|
||||
match branches.entry(ins_idx) {
|
||||
btree_map::Entry::Vacant(e) => {
|
||||
ins_diff.branch_to = Some(InstructionBranchTo { ins_idx, branch_idx });
|
||||
@@ -291,12 +291,12 @@ pub(crate) fn section_name_eq(
|
||||
fn reloc_eq(
|
||||
left_obj: &Object,
|
||||
right_obj: &Object,
|
||||
left_reloc: Option<ResolvedRelocation>,
|
||||
right_reloc: Option<ResolvedRelocation>,
|
||||
left_ins: ResolvedInstructionRef,
|
||||
right_ins: ResolvedInstructionRef,
|
||||
diff_config: &DiffObjConfig,
|
||||
) -> bool {
|
||||
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),
|
||||
// If relocations are relaxed, match if left is missing a reloc
|
||||
(None, Some(_)) => return relax_reloc_diffs,
|
||||
@@ -319,20 +319,17 @@ fn reloc_eq(
|
||||
&& (diff_config.function_reloc_diffs == FunctionRelocDiffs::DataValue
|
||||
|| symbol_name_addend_matches
|
||||
|| address_eq(left_reloc, right_reloc))
|
||||
&& (
|
||||
diff_config.function_reloc_diffs == FunctionRelocDiffs::NameAddress
|
||||
|| left_reloc.symbol.kind != SymbolKind::Object
|
||||
// TODO
|
||||
// || left_obj.arch.display_ins_data_labels(left_ins)
|
||||
// == left_obj.arch.display_ins_data_labels(right_ins))
|
||||
)
|
||||
&& (diff_config.function_reloc_diffs == FunctionRelocDiffs::NameAddress
|
||||
|| left_reloc.symbol.kind != SymbolKind::Object
|
||||
|| right_reloc.symbol.size == 0 // Likely a pool symbol like ...data, don't treat this as a diff
|
||||
|| display_ins_data_literals(left_obj, left_ins)
|
||||
== display_ins_data_literals(right_obj, right_ins))
|
||||
}
|
||||
(Some(_), None) => false,
|
||||
(None, Some(_)) => {
|
||||
// Match if possibly stripped weak symbol
|
||||
symbol_name_addend_matches && right_reloc.symbol.flags.contains(SymbolFlag::Weak)
|
||||
}
|
||||
(None, None) => symbol_name_addend_matches,
|
||||
(Some(_), None) | (None, None) => symbol_name_addend_matches,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -343,8 +340,8 @@ fn arg_eq(
|
||||
right_row: &InstructionDiffRow,
|
||||
left_arg: &InstructionArg,
|
||||
right_arg: &InstructionArg,
|
||||
left_reloc: Option<ResolvedRelocation>,
|
||||
right_reloc: Option<ResolvedRelocation>,
|
||||
left_ins: ResolvedInstructionRef,
|
||||
right_ins: ResolvedInstructionRef,
|
||||
diff_config: &DiffObjConfig,
|
||||
) -> bool {
|
||||
match left_arg {
|
||||
@@ -357,7 +354,7 @@ fn arg_eq(
|
||||
},
|
||||
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 {
|
||||
// Compare dest instruction idx after diffing
|
||||
@@ -434,10 +431,12 @@ fn diff_instruction(
|
||||
.resolve_instruction_ref(right_symbol_idx, r)
|
||||
.context("Failed to resolve right instruction")?;
|
||||
|
||||
if left_resolved.code != right_resolved.code {
|
||||
// If data doesn't match, process instructions and compare args
|
||||
if left_resolved.code != right_resolved.code
|
||||
|| !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 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() {
|
||||
state.diff_score += PENALTY_REPLACE;
|
||||
return Ok(InstructionDiffResult::new(InstructionDiffKind::Replace));
|
||||
@@ -455,8 +454,8 @@ fn diff_instruction(
|
||||
right_row,
|
||||
a,
|
||||
b,
|
||||
left_resolved.relocation,
|
||||
right_resolved.relocation,
|
||||
left_resolved,
|
||||
right_resolved,
|
||||
diff_config,
|
||||
) {
|
||||
result.left_args_diff.push(InstructionArgDiffIndex::NONE);
|
||||
@@ -500,32 +499,5 @@ fn diff_instruction(
|
||||
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))
|
||||
}
|
||||
|
||||
// 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())
|
||||
// }
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
use alloc::{vec, vec::Vec};
|
||||
use core::{cmp::Ordering, ops::Range};
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use similar::{capture_diff_slices, get_diff_ratio, Algorithm};
|
||||
use anyhow::{Result, anyhow};
|
||||
use similar::{Algorithm, capture_diff_slices, get_diff_ratio};
|
||||
|
||||
use super::{
|
||||
code::{address_eq, section_name_eq},
|
||||
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(
|
||||
left_obj: &Object,
|
||||
@@ -53,29 +53,24 @@ fn reloc_eq(
|
||||
section_name_eq(left_obj, right_obj, sl, sr)
|
||||
&& (symbol_name_addend_matches || address_eq(left, right))
|
||||
}
|
||||
(Some(_), None) => false,
|
||||
(None, Some(_)) => {
|
||||
// Match if possibly stripped weak symbol
|
||||
symbol_name_addend_matches && right.symbol.flags.contains(SymbolFlag::Weak)
|
||||
}
|
||||
(None, None) => symbol_name_addend_matches,
|
||||
(Some(_), None) | (None, None) => symbol_name_addend_matches,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn resolve_relocation<'obj>(
|
||||
obj: &'obj Object,
|
||||
pub fn resolve_relocation<'obj>(
|
||||
symbols: &'obj [Symbol],
|
||||
reloc: &'obj Relocation,
|
||||
) -> ResolvedRelocation<'obj> {
|
||||
let symbol = &obj.symbols[reloc.target_symbol];
|
||||
let symbol = &symbols[reloc.target_symbol];
|
||||
ResolvedRelocation { relocation: reloc, symbol }
|
||||
}
|
||||
|
||||
/// 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>(
|
||||
left_obj: &'left Object,
|
||||
right_obj: &'right Object,
|
||||
@@ -92,7 +87,7 @@ fn diff_data_relocs_for_range<'left, 'right>(
|
||||
continue;
|
||||
}
|
||||
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| {
|
||||
if !right_range.contains(&(r.address as usize)) {
|
||||
return false;
|
||||
@@ -103,7 +98,7 @@ fn diff_data_relocs_for_range<'left, 'right>(
|
||||
diffs.push((DataDiffKind::Delete, Some(left_reloc), None));
|
||||
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) {
|
||||
diffs.push((DataDiffKind::None, Some(left_reloc), Some(right_reloc)));
|
||||
} else {
|
||||
@@ -115,7 +110,7 @@ fn diff_data_relocs_for_range<'left, 'right>(
|
||||
continue;
|
||||
}
|
||||
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| {
|
||||
if !left_range.contains(&(r.address as usize)) {
|
||||
return false;
|
||||
@@ -143,27 +138,13 @@ pub fn diff_data_section(
|
||||
) -> Result<(SectionDiff, SectionDiff)> {
|
||||
let left_section = &left_obj.sections[left_section_idx];
|
||||
let right_section = &right_obj.sections[right_section_idx];
|
||||
let left_max = left_obj
|
||||
.symbols
|
||||
.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)
|
||||
})
|
||||
let left_max = symbols_matching_section(&left_obj.symbols, left_section_idx)
|
||||
.filter_map(|(_, s)| s.address.checked_sub(left_section.address).map(|a| a + s.size))
|
||||
.max()
|
||||
.unwrap_or(0)
|
||||
.min(left_section.size);
|
||||
let right_max = right_obj
|
||||
.symbols
|
||||
.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)
|
||||
})
|
||||
let right_max = symbols_matching_section(&right_obj.symbols, right_section_idx)
|
||||
.filter_map(|(_, s)| s.address.checked_sub(right_section.address).map(|a| a + s.size))
|
||||
.max()
|
||||
.unwrap_or(0)
|
||||
.min(right_section.size);
|
||||
@@ -254,13 +235,21 @@ pub fn diff_data_section(
|
||||
let len = left_obj.arch.data_reloc_size(left_reloc.relocation.flags);
|
||||
let range = left_reloc.relocation.address as usize
|
||||
..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 {
|
||||
let len = right_obj.arch.data_reloc_size(right_reloc.relocation.flags);
|
||||
let range = right_reloc.relocation.address as usize
|
||||
..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,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -284,7 +273,6 @@ pub fn diff_data_section(
|
||||
// We only do this when all relocations on the left side match.
|
||||
if left_section_diff.match_percent.unwrap_or(-1.0) < match_percent {
|
||||
left_section_diff.match_percent = Some(match_percent);
|
||||
right_section_diff.match_percent = Some(match_percent);
|
||||
}
|
||||
}
|
||||
Ok((left_section_diff, right_section_diff))
|
||||
@@ -408,34 +396,22 @@ pub fn diff_generic_section(
|
||||
left_section_idx: usize,
|
||||
_right_section_idx: usize,
|
||||
) -> Result<(SectionDiff, SectionDiff)> {
|
||||
let match_percent = if left_obj
|
||||
.symbols
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, s)| s.section == Some(left_section_idx) && s.kind != SymbolKind::Section)
|
||||
let match_percent = if symbols_matching_section(&left_obj.symbols, left_section_idx)
|
||||
.map(|(i, _)| &left_diff.symbols[i])
|
||||
.all(|d| d.match_percent == Some(100.0))
|
||||
{
|
||||
100.0 // Avoid fp precision issues
|
||||
} else {
|
||||
let (matched, total) = left_obj
|
||||
.symbols
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, s)| s.section == Some(left_section_idx) && s.kind != SymbolKind::Section)
|
||||
let (matched, total) = symbols_matching_section(&left_obj.symbols, left_section_idx)
|
||||
.map(|(i, s)| (s, &left_diff.symbols[i]))
|
||||
.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)
|
||||
});
|
||||
if total == 0.0 {
|
||||
100.0
|
||||
} else {
|
||||
matched / total
|
||||
}
|
||||
if total == 0.0 { 100.0 } else { matched / total }
|
||||
};
|
||||
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![] },
|
||||
SectionDiff { match_percent: None, data_diff: vec![], reloc_diff: vec![] },
|
||||
))
|
||||
}
|
||||
|
||||
@@ -449,19 +425,11 @@ pub fn diff_bss_section(
|
||||
right_section_idx: usize,
|
||||
) -> Result<(SectionDiff, SectionDiff)> {
|
||||
let left_section = &left_obj.sections[left_section_idx];
|
||||
let left_sizes = left_obj
|
||||
.symbols
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, s)| s.section == Some(left_section_idx) && s.kind != SymbolKind::Section)
|
||||
let left_sizes = symbols_matching_section(&left_obj.symbols, left_section_idx)
|
||||
.filter_map(|(_, s)| s.address.checked_sub(left_section.address).map(|a| (a, s.size)))
|
||||
.collect::<Vec<_>>();
|
||||
let right_section = &right_obj.sections[right_section_idx];
|
||||
let right_sizes = right_obj
|
||||
.symbols
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, s)| s.section == Some(right_section_idx) && s.kind != SymbolKind::Section)
|
||||
let right_sizes = symbols_matching_section(&right_obj.symbols, right_section_idx)
|
||||
.filter_map(|(_, s)| s.address.checked_sub(right_section.address).map(|a| (a, s.size)))
|
||||
.collect::<Vec<_>>();
|
||||
let ops = capture_diff_slices(Algorithm::Patience, &left_sizes, &right_sizes);
|
||||
@@ -484,6 +452,18 @@ pub fn diff_bss_section(
|
||||
|
||||
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![] },
|
||||
SectionDiff { match_percent: None, 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)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -14,8 +14,9 @@ use regex::Regex;
|
||||
use crate::{
|
||||
diff::{DiffObjConfig, InstructionDiffKind, InstructionDiffRow, ObjectDiff, SymbolDiff},
|
||||
obj::{
|
||||
InstructionArg, InstructionArgValue, Object, ParsedInstruction, ResolvedInstructionRef,
|
||||
SectionFlag, SectionKind, Symbol, SymbolFlag, SymbolKind,
|
||||
FlowAnalysisValue, InstructionArg, InstructionArgValue, Object, ParsedInstruction,
|
||||
ResolvedInstructionRef, ResolvedRelocation, SectionFlag, SectionKind, Symbol, SymbolFlag,
|
||||
SymbolKind,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -47,11 +48,12 @@ pub enum DiffText<'a> {
|
||||
pub enum DiffTextColor {
|
||||
#[default]
|
||||
Normal, // Grey
|
||||
Dim, // Dark grey
|
||||
Bright, // White
|
||||
Replace, // Blue
|
||||
Delete, // Red
|
||||
Insert, // Green
|
||||
Dim, // Dark grey
|
||||
Bright, // White
|
||||
DataFlow, // Light blue
|
||||
Replace, // Blue
|
||||
Delete, // Red
|
||||
Insert, // Green
|
||||
Rotating(u8),
|
||||
}
|
||||
|
||||
@@ -77,7 +79,7 @@ impl<'a> DiffTextSegment<'a> {
|
||||
const EOL_SEGMENT: DiffTextSegment<'static> =
|
||||
DiffTextSegment { text: DiffText::Eol, color: DiffTextColor::Normal, pad_to: 0 };
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub enum HighlightKind {
|
||||
#[default]
|
||||
None,
|
||||
@@ -186,6 +188,11 @@ pub fn display_row(
|
||||
}
|
||||
let mut arg_idx = 0;
|
||||
let mut displayed_relocation = false;
|
||||
let analysis_result = if diff_config.show_data_flow {
|
||||
obj.get_flow_analysis_result(resolved.symbol)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
obj.arch.display_instruction(resolved, diff_config, &mut |part| match part {
|
||||
InstructionPart::Basic(text) => {
|
||||
if text.chars().all(|c| c == ' ') {
|
||||
@@ -205,16 +212,33 @@ pub fn display_row(
|
||||
InstructionPart::Arg(arg) => {
|
||||
let diff_index = ins_row.arg_diff.get(arg_idx).copied().unwrap_or_default();
|
||||
arg_idx += 1;
|
||||
match arg {
|
||||
InstructionArg::Value(value) => cb(DiffTextSegment {
|
||||
text: DiffText::Argument(value),
|
||||
color: diff_index
|
||||
if arg == InstructionArg::Reloc {
|
||||
displayed_relocation = true;
|
||||
}
|
||||
let data_flow_value =
|
||||
analysis_result.and_then(|result|
|
||||
result.get_argument_value_at_address(
|
||||
ins_ref.address, (arg_idx - 1) as u8));
|
||||
match (arg, data_flow_value, resolved.ins_ref.branch_dest) {
|
||||
// If we have a flow analysis result, always use that over anything else.
|
||||
(InstructionArg::Value(_) | InstructionArg::Reloc, Some(FlowAnalysisValue::Text(text)), _) => {
|
||||
cb(DiffTextSegment {
|
||||
text: DiffText::Argument(InstructionArgValue::Opaque(Cow::Borrowed(text))),
|
||||
color: DiffTextColor::DataFlow,
|
||||
pad_to: 0,
|
||||
})
|
||||
},
|
||||
(InstructionArg::Value(value), None, _) => {
|
||||
let color = diff_index
|
||||
.get()
|
||||
.map_or(base_color, |i| DiffTextColor::Rotating(i as u8)),
|
||||
pad_to: 0,
|
||||
}),
|
||||
InstructionArg::Reloc => {
|
||||
displayed_relocation = true;
|
||||
.map_or(base_color, |i| DiffTextColor::Rotating(i as u8));
|
||||
cb(DiffTextSegment {
|
||||
text: DiffText::Argument(value),
|
||||
color,
|
||||
pad_to: 0,
|
||||
})
|
||||
},
|
||||
(InstructionArg::Reloc, _, None) => {
|
||||
let resolved = resolved.relocation.unwrap();
|
||||
let color = diff_index
|
||||
.get()
|
||||
@@ -233,7 +257,9 @@ pub fn display_row(
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
InstructionArg::BranchDest(dest) => {
|
||||
(InstructionArg::BranchDest(dest), _, _) |
|
||||
// If the relocation was resolved to a branch destination, emit that instead.
|
||||
(InstructionArg::Reloc, _, Some(dest)) => {
|
||||
if let Some(addr) = dest.checked_sub(resolved.symbol.address) {
|
||||
cb(DiffTextSegment {
|
||||
text: DiffText::BranchDest(addr),
|
||||
@@ -284,6 +310,18 @@ pub fn display_row(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl PartialEq<HighlightKind> for HighlightKind {
|
||||
fn eq(&self, other: &HighlightKind) -> bool {
|
||||
match (self, other) {
|
||||
(HighlightKind::Opcode(a), HighlightKind::Opcode(b)) => a == b,
|
||||
(HighlightKind::Argument(a), HighlightKind::Argument(b)) => a.loose_eq(b),
|
||||
(HighlightKind::Symbol(a), HighlightKind::Symbol(b)) => a == b,
|
||||
(HighlightKind::Address(a), HighlightKind::Address(b)) => a == b,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<DiffText<'_>> for HighlightKind {
|
||||
fn eq(&self, other: &DiffText) -> bool {
|
||||
match (self, other) {
|
||||
@@ -325,10 +363,14 @@ pub enum SymbolNavigationKind {
|
||||
Extab,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Eq, PartialEq)]
|
||||
pub enum HoverItemColor {
|
||||
Normal, // Gray
|
||||
#[default]
|
||||
Normal, // Gray
|
||||
Emphasized, // White
|
||||
Special, // Blue
|
||||
Delete, // Red
|
||||
Insert, // Green
|
||||
}
|
||||
|
||||
pub enum HoverItem {
|
||||
@@ -343,22 +385,27 @@ pub fn symbol_context(obj: &Object, symbol_index: usize) -> Vec<ContextItem> {
|
||||
if let Some(name) = &symbol.demangled_name {
|
||||
out.push(ContextItem::Copy { value: name.clone(), label: None });
|
||||
}
|
||||
if symbol.section.is_some() {
|
||||
if let Some(address) = symbol.virtual_address {
|
||||
out.push(ContextItem::Copy {
|
||||
value: format!("{:#x}", address),
|
||||
label: Some("virtual address".to_string()),
|
||||
});
|
||||
}
|
||||
if symbol.section.is_some()
|
||||
&& let Some(address) = symbol.virtual_address
|
||||
{
|
||||
out.push(ContextItem::Copy {
|
||||
value: format!("{address:x}"),
|
||||
label: Some("virtual address".to_string()),
|
||||
});
|
||||
}
|
||||
out.append(&mut obj.arch.symbol_context(obj, symbol_index));
|
||||
out
|
||||
}
|
||||
|
||||
pub fn symbol_hover(obj: &Object, symbol_index: usize, addend: i64) -> Vec<HoverItem> {
|
||||
pub fn symbol_hover(
|
||||
obj: &Object,
|
||||
symbol_index: usize,
|
||||
addend: i64,
|
||||
override_color: Option<HoverItemColor>,
|
||||
) -> Vec<HoverItem> {
|
||||
let symbol = &obj.symbols[symbol_index];
|
||||
let addend_str = match addend.cmp(&0i64) {
|
||||
Ordering::Greater => format!("+{:x}", addend),
|
||||
Ordering::Greater => format!("+{addend:x}"),
|
||||
Ordering::Less => format!("-{:x}", -addend),
|
||||
_ => String::new(),
|
||||
};
|
||||
@@ -366,51 +413,51 @@ pub fn symbol_hover(obj: &Object, symbol_index: usize, addend: i64) -> Vec<Hover
|
||||
out.push(HoverItem::Text {
|
||||
label: "Name".into(),
|
||||
value: format!("{}{}", symbol.name, addend_str),
|
||||
color: HoverItemColor::Normal,
|
||||
color: override_color.clone().unwrap_or_default(),
|
||||
});
|
||||
if let Some(demangled_name) = &symbol.demangled_name {
|
||||
out.push(HoverItem::Text {
|
||||
label: "Demangled".into(),
|
||||
value: demangled_name.into(),
|
||||
color: HoverItemColor::Normal,
|
||||
color: override_color.clone().unwrap_or_default(),
|
||||
});
|
||||
}
|
||||
if let Some(section) = symbol.section {
|
||||
out.push(HoverItem::Text {
|
||||
label: "Section".into(),
|
||||
value: obj.sections[section].name.clone(),
|
||||
color: HoverItemColor::Normal,
|
||||
color: override_color.clone().unwrap_or_default(),
|
||||
});
|
||||
out.push(HoverItem::Text {
|
||||
label: "Address".into(),
|
||||
value: format!("{:x}{}", symbol.address, addend_str),
|
||||
color: HoverItemColor::Normal,
|
||||
color: override_color.clone().unwrap_or_default(),
|
||||
});
|
||||
if symbol.flags.contains(SymbolFlag::SizeInferred) {
|
||||
out.push(HoverItem::Text {
|
||||
label: "Size".into(),
|
||||
value: format!("{:x} (inferred)", symbol.size),
|
||||
color: HoverItemColor::Normal,
|
||||
color: override_color.clone().unwrap_or_default(),
|
||||
});
|
||||
} else {
|
||||
out.push(HoverItem::Text {
|
||||
label: "Size".into(),
|
||||
value: format!("{:x}", symbol.size),
|
||||
color: HoverItemColor::Normal,
|
||||
color: override_color.clone().unwrap_or_default(),
|
||||
});
|
||||
}
|
||||
if let Some(align) = symbol.align {
|
||||
out.push(HoverItem::Text {
|
||||
label: "Alignment".into(),
|
||||
value: align.get().to_string(),
|
||||
color: HoverItemColor::Normal,
|
||||
color: override_color.clone().unwrap_or_default(),
|
||||
});
|
||||
}
|
||||
if let Some(address) = symbol.virtual_address {
|
||||
out.push(HoverItem::Text {
|
||||
label: "Virtual address".into(),
|
||||
value: format!("{:#x}", address),
|
||||
color: HoverItemColor::Special,
|
||||
value: format!("{address:x}"),
|
||||
color: override_color.clone().unwrap_or(HoverItemColor::Special),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
@@ -424,6 +471,53 @@ pub fn symbol_hover(obj: &Object, symbol_index: usize, addend: i64) -> Vec<Hover
|
||||
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, label_override) in literals {
|
||||
out.push(ContextItem::Copy { value: literal, label: label_override });
|
||||
}
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
pub fn relocation_hover(
|
||||
obj: &Object,
|
||||
reloc: ResolvedRelocation,
|
||||
override_color: Option<HoverItemColor>,
|
||||
) -> 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: override_color.clone().unwrap_or_default(),
|
||||
});
|
||||
} else {
|
||||
out.push(HoverItem::Text {
|
||||
label: "Relocation".into(),
|
||||
value: format!("<{:?}>", reloc.relocation.flags),
|
||||
color: override_color.clone().unwrap_or_default(),
|
||||
});
|
||||
}
|
||||
out.append(&mut symbol_hover(
|
||||
obj,
|
||||
reloc.relocation.target_symbol,
|
||||
reloc.relocation.addend,
|
||||
override_color,
|
||||
));
|
||||
out
|
||||
}
|
||||
|
||||
pub fn instruction_context(
|
||||
obj: &Object,
|
||||
resolved: ResolvedInstructionRef,
|
||||
@@ -432,7 +526,7 @@ pub fn instruction_context(
|
||||
let mut out = Vec::new();
|
||||
let mut hex_string = String::new();
|
||||
for byte in resolved.code {
|
||||
hex_string.push_str(&format!("{:02x}", byte));
|
||||
hex_string.push_str(&format!("{byte:02x}"));
|
||||
}
|
||||
out.push(ContextItem::Copy { value: hex_string, label: Some("instruction bytes".to_string()) });
|
||||
out.append(&mut obj.arch.instruction_context(obj, resolved));
|
||||
@@ -458,11 +552,8 @@ pub fn instruction_context(
|
||||
}
|
||||
}
|
||||
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.append(&mut symbol_context(obj, reloc.relocation.target_symbol));
|
||||
out.append(&mut relocation_context(obj, reloc, Some(resolved)));
|
||||
}
|
||||
out
|
||||
}
|
||||
@@ -483,7 +574,7 @@ pub fn instruction_hover(
|
||||
let offset = resolved.ins_ref.address - resolved.symbol.address;
|
||||
out.push(HoverItem::Text {
|
||||
label: "Virtual address".into(),
|
||||
value: format!("{:#x}", virtual_address + offset),
|
||||
value: format!("{:x}", virtual_address + offset),
|
||||
color: HoverItemColor::Special,
|
||||
});
|
||||
}
|
||||
@@ -509,26 +600,27 @@ pub fn instruction_hover(
|
||||
}
|
||||
}
|
||||
if let Some(reloc) = resolved.relocation {
|
||||
if let Some(name) = obj.arch.reloc_name(reloc.relocation.flags) {
|
||||
out.push(HoverItem::Text {
|
||||
label: "Relocation type".into(),
|
||||
value: name.to_string(),
|
||||
color: HoverItemColor::Normal,
|
||||
});
|
||||
} else {
|
||||
out.push(HoverItem::Text {
|
||||
label: "Relocation type".into(),
|
||||
value: format!("<{:?}>", reloc.relocation.flags),
|
||||
color: HoverItemColor::Normal,
|
||||
});
|
||||
}
|
||||
out.push(HoverItem::Separator);
|
||||
out.append(&mut symbol_hover(obj, reloc.relocation.target_symbol, reloc.relocation.addend));
|
||||
out.append(&mut relocation_hover(obj, reloc, None));
|
||||
let bytes = obj.symbol_data(reloc.relocation.target_symbol).unwrap_or(&[]);
|
||||
if let Some(ty) = obj.arch.guess_data_type(resolved, bytes) {
|
||||
let literals = display_ins_data_literals(obj, resolved);
|
||||
if !literals.is_empty() {
|
||||
out.push(HoverItem::Separator);
|
||||
for (literal, label_override) in literals {
|
||||
out.push(HoverItem::Text {
|
||||
label: label_override.unwrap_or_else(|| ty.to_string()),
|
||||
value: literal,
|
||||
color: HoverItemColor::Normal,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum SymbolFilter<'a> {
|
||||
None,
|
||||
Search(&'a Regex),
|
||||
@@ -545,7 +637,11 @@ fn symbol_matches_filter(
|
||||
if symbol.section.is_none() && !symbol.flags.contains(SymbolFlag::Common) {
|
||||
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;
|
||||
}
|
||||
match filter {
|
||||
@@ -626,24 +722,25 @@ pub fn display_sections(
|
||||
.collect::<Vec<_>>();
|
||||
if let Some(section_idx) = section_idx {
|
||||
let section = &obj.sections[section_idx];
|
||||
if section.kind == SectionKind::Unknown || section.flags.contains(SectionFlag::Hidden) {
|
||||
if section.kind == SectionKind::Unknown {
|
||||
// Skip unknown and hidden sections
|
||||
continue;
|
||||
}
|
||||
let section_diff = &diff.sections[section_idx];
|
||||
if section.kind == SectionKind::Code && reverse_fn_order {
|
||||
symbols.sort_by(|a, b| {
|
||||
let a_symbol = &obj.symbols[a.symbol];
|
||||
let b_symbol = &obj.symbols[b.symbol];
|
||||
symbol_sort_reverse(a_symbol, b_symbol)
|
||||
});
|
||||
} else {
|
||||
symbols.sort_by(|a, b| {
|
||||
let a_symbol = &obj.symbols[a.symbol];
|
||||
let b_symbol = &obj.symbols[b.symbol];
|
||||
symbol_sort(a_symbol, b_symbol)
|
||||
});
|
||||
}
|
||||
let reverse_fn_order = section.kind == SectionKind::Code && reverse_fn_order;
|
||||
symbols.sort_by(|a, b| {
|
||||
let a = &obj.symbols[a.symbol];
|
||||
let b = &obj.symbols[b.symbol];
|
||||
section_symbol_sort(a, b)
|
||||
.then_with(|| {
|
||||
if reverse_fn_order {
|
||||
b.address.cmp(&a.address)
|
||||
} else {
|
||||
a.address.cmp(&b.address)
|
||||
}
|
||||
})
|
||||
.then_with(|| a.size.cmp(&b.size))
|
||||
});
|
||||
sections.push(SectionDisplay {
|
||||
id: section.id.clone(),
|
||||
name: if section.flags.contains(SectionFlag::Combined) {
|
||||
@@ -666,7 +763,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
|
||||
}
|
||||
|
||||
@@ -681,14 +778,6 @@ fn section_symbol_sort(a: &Symbol, b: &Symbol) -> Ordering {
|
||||
Ordering::Equal
|
||||
}
|
||||
|
||||
fn symbol_sort(a: &Symbol, b: &Symbol) -> Ordering {
|
||||
section_symbol_sort(a, b).then(a.address.cmp(&b.address)).then(a.size.cmp(&b.size))
|
||||
}
|
||||
|
||||
fn symbol_sort_reverse(a: &Symbol, b: &Symbol) -> Ordering {
|
||||
section_symbol_sort(a, b).then(b.address.cmp(&a.address)).then(b.size.cmp(&a.size))
|
||||
}
|
||||
|
||||
pub fn display_ins_data_labels(obj: &Object, resolved: ResolvedInstructionRef) -> Vec<String> {
|
||||
let Some(reloc) = resolved.relocation else {
|
||||
return Vec::new();
|
||||
@@ -701,12 +790,15 @@ pub fn display_ins_data_labels(obj: &Object, resolved: ResolvedInstructionRef) -
|
||||
};
|
||||
let bytes = &data[reloc.relocation.addend as usize..];
|
||||
obj.arch
|
||||
.guess_data_type(resolved)
|
||||
.guess_data_type(resolved, bytes)
|
||||
.map(|ty| ty.display_labels(obj.endianness, bytes))
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn display_ins_data_literals(obj: &Object, resolved: ResolvedInstructionRef) -> Vec<String> {
|
||||
pub fn display_ins_data_literals(
|
||||
obj: &Object,
|
||||
resolved: ResolvedInstructionRef,
|
||||
) -> Vec<(String, Option<String>)> {
|
||||
let Some(reloc) = resolved.relocation else {
|
||||
return Vec::new();
|
||||
};
|
||||
@@ -718,7 +810,7 @@ pub fn display_ins_data_literals(obj: &Object, resolved: ResolvedInstructionRef)
|
||||
};
|
||||
let bytes = &data[reloc.relocation.addend as usize..];
|
||||
obj.arch
|
||||
.guess_data_type(resolved)
|
||||
.guess_data_type(resolved, bytes)
|
||||
.map(|ty| ty.display_literals(obj.endianness, bytes))
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ use crate::{
|
||||
diff_generic_section,
|
||||
},
|
||||
},
|
||||
obj::{InstructionRef, Object, SectionKind, Symbol, SymbolFlag},
|
||||
obj::{InstructionRef, Object, Relocation, SectionKind, Symbol, SymbolFlag},
|
||||
};
|
||||
|
||||
pub mod code;
|
||||
@@ -26,13 +26,7 @@ pub mod display;
|
||||
include!(concat!(env!("OUT_DIR"), "/config.gen.rs"));
|
||||
|
||||
impl DiffObjConfig {
|
||||
pub fn separator(&self) -> &'static str {
|
||||
if self.space_between_args {
|
||||
", "
|
||||
} else {
|
||||
","
|
||||
}
|
||||
}
|
||||
pub fn separator(&self) -> &'static str { if self.space_between_args { ", " } else { "," } }
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -93,6 +87,7 @@ pub struct DataDiff {
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DataRelocationDiff {
|
||||
pub reloc: Relocation,
|
||||
pub kind: DataDiffKind,
|
||||
pub range: Range<usize>,
|
||||
}
|
||||
@@ -346,11 +341,25 @@ pub fn diff_objs(
|
||||
if let (Some((right_obj, right_out)), Some((left_obj, left_out))) =
|
||||
(right.as_mut(), left.as_mut())
|
||||
{
|
||||
if let Some(right_name) = &mapping_config.selecting_left {
|
||||
generate_mapping_symbols(right_obj, right_name, left_obj, left_out, diff_config)?;
|
||||
if let Some(right_name) = mapping_config.selecting_left.as_deref() {
|
||||
generate_mapping_symbols(
|
||||
left_obj,
|
||||
left_out,
|
||||
right_obj,
|
||||
right_out,
|
||||
MappingSymbol::Right(right_name),
|
||||
diff_config,
|
||||
)?;
|
||||
}
|
||||
if let Some(left_name) = &mapping_config.selecting_right {
|
||||
generate_mapping_symbols(left_obj, left_name, right_obj, right_out, diff_config)?;
|
||||
if let Some(left_name) = mapping_config.selecting_right.as_deref() {
|
||||
generate_mapping_symbols(
|
||||
left_obj,
|
||||
left_out,
|
||||
right_obj,
|
||||
right_out,
|
||||
MappingSymbol::Left(left_name),
|
||||
diff_config,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,50 +370,62 @@ pub fn diff_objs(
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum MappingSymbol<'a> {
|
||||
Left(&'a str),
|
||||
Right(&'a str),
|
||||
}
|
||||
|
||||
/// When we're selecting a symbol to use as a comparison, we'll create comparisons for all
|
||||
/// symbols in the other object that match the selected symbol's section and kind. This allows
|
||||
/// us to display match percentages for all symbols in the other object that could be selected.
|
||||
fn generate_mapping_symbols(
|
||||
base_obj: &Object,
|
||||
base_name: &str,
|
||||
target_obj: &Object,
|
||||
target_out: &mut ObjectDiff,
|
||||
left_obj: &Object,
|
||||
left_out: &mut ObjectDiff,
|
||||
right_obj: &Object,
|
||||
right_out: &mut ObjectDiff,
|
||||
mapping_symbol: MappingSymbol,
|
||||
config: &DiffObjConfig,
|
||||
) -> Result<()> {
|
||||
let Some(base_symbol_ref) = symbol_ref_by_name(base_obj, base_name) else {
|
||||
let (base_obj, base_name, target_obj) = match mapping_symbol {
|
||||
MappingSymbol::Left(name) => (left_obj, name, right_obj),
|
||||
MappingSymbol::Right(name) => (right_obj, name, left_obj),
|
||||
};
|
||||
let Some(base_symbol_ref) = base_obj.symbol_by_name(base_name) else {
|
||||
return Ok(());
|
||||
};
|
||||
let base_section_kind = symbol_section_kind(base_obj, &base_obj.symbols[base_symbol_ref]);
|
||||
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;
|
||||
}
|
||||
match base_section_kind {
|
||||
let (left_symbol_idx, right_symbol_idx) = match mapping_symbol {
|
||||
MappingSymbol::Left(_) => (base_symbol_ref, target_symbol_index),
|
||||
MappingSymbol::Right(_) => (target_symbol_index, base_symbol_ref),
|
||||
};
|
||||
let (left_diff, right_diff) = match base_section_kind {
|
||||
SectionKind::Code => {
|
||||
let (left_diff, _right_diff) =
|
||||
diff_code(target_obj, base_obj, target_symbol_index, base_symbol_ref, config)?;
|
||||
target_out.mapping_symbols.push(MappingSymbolDiff {
|
||||
symbol_index: target_symbol_index,
|
||||
symbol_diff: left_diff,
|
||||
});
|
||||
diff_code(left_obj, right_obj, left_symbol_idx, right_symbol_idx, config)
|
||||
}
|
||||
SectionKind::Data => {
|
||||
let (left_diff, _right_diff) =
|
||||
diff_data_symbol(target_obj, base_obj, target_symbol_index, base_symbol_ref)?;
|
||||
target_out.mapping_symbols.push(MappingSymbolDiff {
|
||||
symbol_index: target_symbol_index,
|
||||
symbol_diff: left_diff,
|
||||
});
|
||||
diff_data_symbol(left_obj, right_obj, left_symbol_idx, right_symbol_idx)
|
||||
}
|
||||
SectionKind::Bss | SectionKind::Common => {
|
||||
let (left_diff, _right_diff) =
|
||||
diff_bss_symbol(target_obj, base_obj, target_symbol_index, base_symbol_ref)?;
|
||||
target_out.mapping_symbols.push(MappingSymbolDiff {
|
||||
symbol_index: target_symbol_index,
|
||||
symbol_diff: left_diff,
|
||||
});
|
||||
diff_bss_symbol(left_obj, right_obj, left_symbol_idx, right_symbol_idx)
|
||||
}
|
||||
SectionKind::Unknown => {}
|
||||
SectionKind::Unknown => continue,
|
||||
}?;
|
||||
match mapping_symbol {
|
||||
MappingSymbol::Left(_) => right_out.mapping_symbols.push(MappingSymbolDiff {
|
||||
symbol_index: right_symbol_idx,
|
||||
symbol_diff: right_diff,
|
||||
}),
|
||||
MappingSymbol::Right(_) => left_out
|
||||
.mapping_symbols
|
||||
.push(MappingSymbolDiff { symbol_index: left_symbol_idx, symbol_diff: left_diff }),
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@@ -436,10 +457,6 @@ pub struct MappingConfig {
|
||||
pub selecting_right: Option<String>,
|
||||
}
|
||||
|
||||
fn symbol_ref_by_name(obj: &Object, name: &str) -> Option<usize> {
|
||||
obj.symbols.iter().position(|s| s.name == name)
|
||||
}
|
||||
|
||||
fn apply_symbol_mappings(
|
||||
left: &Object,
|
||||
right: &Object,
|
||||
@@ -450,26 +467,26 @@ fn apply_symbol_mappings(
|
||||
) -> Result<()> {
|
||||
// If we're selecting a symbol to use as a comparison, mark it as used
|
||||
// This ensures that we don't match it to another symbol at any point
|
||||
if let Some(left_name) = &mapping_config.selecting_left {
|
||||
if let Some(left_symbol) = symbol_ref_by_name(left, left_name) {
|
||||
left_used.insert(left_symbol);
|
||||
}
|
||||
if let Some(left_name) = &mapping_config.selecting_left
|
||||
&& let Some(left_symbol) = left.symbol_by_name(left_name)
|
||||
{
|
||||
left_used.insert(left_symbol);
|
||||
}
|
||||
if let Some(right_name) = &mapping_config.selecting_right {
|
||||
if let Some(right_symbol) = symbol_ref_by_name(right, right_name) {
|
||||
right_used.insert(right_symbol);
|
||||
}
|
||||
if let Some(right_name) = &mapping_config.selecting_right
|
||||
&& let Some(right_symbol) = right.symbol_by_name(right_name)
|
||||
{
|
||||
right_used.insert(right_symbol);
|
||||
}
|
||||
|
||||
// Apply manual symbol mappings
|
||||
for (left_name, right_name) in &mapping_config.mappings {
|
||||
let Some(left_symbol_index) = symbol_ref_by_name(left, left_name) else {
|
||||
let Some(left_symbol_index) = left.symbol_by_name(left_name) else {
|
||||
continue;
|
||||
};
|
||||
if left_used.contains(&left_symbol_index) {
|
||||
continue;
|
||||
}
|
||||
let Some(right_symbol_index) = symbol_ref_by_name(right, right_name) else {
|
||||
let Some(right_symbol_index) = right.symbol_by_name(right_name) else {
|
||||
continue;
|
||||
};
|
||||
if right_used.contains(&right_symbol_index) {
|
||||
@@ -489,11 +506,7 @@ fn apply_symbol_mappings(
|
||||
.map_or(SectionKind::Unknown, |s| s.kind);
|
||||
if left_section_kind != right_section_kind {
|
||||
log::warn!(
|
||||
"Symbol section kind mismatch: {} ({:?}) vs {} ({:?})",
|
||||
left_name,
|
||||
left_section_kind,
|
||||
right_name,
|
||||
right_section_kind
|
||||
"Symbol section kind mismatch: {left_name} ({left_section_kind:?}) vs {right_name} ({right_section_kind:?})"
|
||||
);
|
||||
continue;
|
||||
}
|
||||
@@ -531,6 +544,9 @@ fn matching_symbols(
|
||||
)?;
|
||||
}
|
||||
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);
|
||||
if section_kind == SectionKind::Unknown {
|
||||
continue;
|
||||
@@ -552,6 +568,9 @@ fn matching_symbols(
|
||||
}
|
||||
if let Some(right) = right {
|
||||
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);
|
||||
if section_kind == SectionKind::Unknown {
|
||||
continue;
|
||||
@@ -577,9 +596,10 @@ fn unmatched_symbols<'obj, 'used>(
|
||||
where
|
||||
'obj: 'used,
|
||||
{
|
||||
obj.symbols.iter().enumerate().filter(move |&(symbol_idx, _)| {
|
||||
// Skip symbols that have already been matched
|
||||
!used.is_some_and(|u| u.contains(&symbol_idx))
|
||||
obj.symbols.iter().enumerate().filter(move |&(symbol_idx, symbol)| {
|
||||
!symbol.flags.contains(SymbolFlag::Ignored)
|
||||
// Skip symbols that have already been matched
|
||||
&& !used.is_some_and(|u| u.contains(&symbol_idx))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -619,17 +639,16 @@ fn find_symbol(
|
||||
// If they are at the same address in the same section
|
||||
if in_symbol.name.starts_with('@')
|
||||
&& matches!(section_kind, SectionKind::Data | SectionKind::Bss)
|
||||
{
|
||||
if let Some((symbol_idx, _)) = unmatched_symbols(obj, used).find(|(_, symbol)| {
|
||||
&& let Some((symbol_idx, _)) = unmatched_symbols(obj, used).find(|(_, symbol)| {
|
||||
let Some(section_index) = symbol.section else {
|
||||
return false;
|
||||
};
|
||||
symbol.name.starts_with('@')
|
||||
&& symbol.address == in_symbol.address
|
||||
&& obj.sections[section_index].name == section_name
|
||||
}) {
|
||||
return Some(symbol_idx);
|
||||
}
|
||||
})
|
||||
{
|
||||
return Some(symbol_idx);
|
||||
}
|
||||
// Match Metrowerks symbol$1234 against symbol$2345
|
||||
if let Some((prefix, suffix)) = in_symbol.name.split_once('$') {
|
||||
@@ -653,7 +672,11 @@ fn find_symbol(
|
||||
|
||||
/// Find matching sections between each object.
|
||||
fn matching_sections(left: Option<&Object>, right: Option<&Object>) -> Result<Vec<SectionMatch>> {
|
||||
let mut matches = Vec::new();
|
||||
let mut matches = Vec::with_capacity(
|
||||
left.as_ref()
|
||||
.map_or(0, |o| o.sections.len())
|
||||
.max(right.as_ref().map_or(0, |o| o.sections.len())),
|
||||
);
|
||||
if let Some(left) = left {
|
||||
for (section_idx, section) in left.sections.iter().enumerate() {
|
||||
if section.kind == SectionKind::Unknown {
|
||||
@@ -661,7 +684,7 @@ fn matching_sections(left: Option<&Object>, right: Option<&Object>) -> Result<Ve
|
||||
}
|
||||
matches.push(SectionMatch {
|
||||
left: Some(section_idx),
|
||||
right: find_section(right, §ion.name, section.kind),
|
||||
right: find_section(right, §ion.name, section.kind, &matches),
|
||||
section_kind: section.kind,
|
||||
});
|
||||
}
|
||||
@@ -684,6 +707,13 @@ fn matching_sections(left: Option<&Object>, right: Option<&Object>) -> Result<Ve
|
||||
Ok(matches)
|
||||
}
|
||||
|
||||
fn find_section(obj: Option<&Object>, name: &str, section_kind: SectionKind) -> Option<usize> {
|
||||
obj?.sections.iter().position(|s| s.kind == section_kind && s.name == name)
|
||||
fn find_section(
|
||||
obj: Option<&Object>,
|
||||
name: &str,
|
||||
section_kind: SectionKind,
|
||||
matches: &[SectionMatch],
|
||||
) -> Option<usize> {
|
||||
obj?.sections.iter().enumerate().position(|(i, s)| {
|
||||
s.kind == section_kind && s.name == name && !matches.iter().any(|m| m.right == Some(i))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ use self_update::{
|
||||
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 build_updater: fn() -> Result<Box<dyn ReleaseUpdate>>,
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
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 crate::{
|
||||
build::{run_make, BuildConfig, BuildStatus},
|
||||
jobs::{start_job, update_status, Job, JobContext, JobResult, JobState},
|
||||
build::{BuildConfig, BuildStatus, run_make},
|
||||
jobs::{Job, JobContext, JobResult, JobState, start_job, update_status},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use std::{
|
||||
sync::{
|
||||
Arc, RwLock,
|
||||
atomic::{AtomicUsize, Ordering},
|
||||
mpsc::{Receiver, Sender, TryRecvError},
|
||||
Arc, RwLock,
|
||||
},
|
||||
task::Waker,
|
||||
thread::JoinHandle,
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
use std::{sync::mpsc::Receiver, task::Waker};
|
||||
|
||||
use anyhow::{bail, Error, Result};
|
||||
use anyhow::{Error, Result, bail};
|
||||
use time::OffsetDateTime;
|
||||
use typed_path::Utf8PlatformPathBuf;
|
||||
|
||||
use crate::{
|
||||
build::{run_make, BuildConfig, BuildStatus},
|
||||
diff::{diff_objs, DiffObjConfig, MappingConfig, ObjectDiff},
|
||||
jobs::{start_job, update_status, Job, JobContext, JobResult, JobState},
|
||||
obj::{read, Object},
|
||||
build::{BuildConfig, BuildStatus, run_make},
|
||||
diff::{DiffObjConfig, MappingConfig, ObjectDiff, diff_objs},
|
||||
jobs::{Job, JobContext, JobResult, JobState, start_job, update_status},
|
||||
obj::{Object, read},
|
||||
};
|
||||
|
||||
pub struct ObjDiffConfig {
|
||||
@@ -79,7 +79,7 @@ fn run_build(
|
||||
Some(target_path_rel) if config.build_target => {
|
||||
update_status(
|
||||
context,
|
||||
format!("Building target {}", target_path_rel),
|
||||
format!("Building target {target_path_rel}"),
|
||||
step_idx,
|
||||
total,
|
||||
&cancel,
|
||||
@@ -94,7 +94,7 @@ fn run_build(
|
||||
Some(base_path_rel) if config.build_base => {
|
||||
update_status(
|
||||
context,
|
||||
format!("Building base {}", base_path_rel),
|
||||
format!("Building base {base_path_rel}"),
|
||||
step_idx,
|
||||
total,
|
||||
&cancel,
|
||||
@@ -111,7 +111,7 @@ fn run_build(
|
||||
Some(target_path) if first_status.success => {
|
||||
update_status(
|
||||
context,
|
||||
format!("Loading target {}", target_path),
|
||||
format!("Loading target {target_path}"),
|
||||
step_idx,
|
||||
total,
|
||||
&cancel,
|
||||
@@ -122,8 +122,8 @@ fn run_build(
|
||||
Err(e) => {
|
||||
first_status = BuildStatus {
|
||||
success: false,
|
||||
stdout: format!("Loading object '{}'", target_path),
|
||||
stderr: format!("{:#}", e),
|
||||
stdout: format!("Loading object '{target_path}'"),
|
||||
stderr: format!("{e:#}"),
|
||||
..Default::default()
|
||||
};
|
||||
None
|
||||
@@ -139,21 +139,15 @@ fn run_build(
|
||||
|
||||
let second_obj = match &config.base_path {
|
||||
Some(base_path) if second_status.success => {
|
||||
update_status(
|
||||
context,
|
||||
format!("Loading base {}", base_path),
|
||||
step_idx,
|
||||
total,
|
||||
&cancel,
|
||||
)?;
|
||||
update_status(context, format!("Loading base {base_path}"), step_idx, total, &cancel)?;
|
||||
step_idx += 1;
|
||||
match read::read(base_path.as_ref(), &config.diff_obj_config) {
|
||||
Ok(obj) => Some(obj),
|
||||
Err(e) => {
|
||||
second_status = BuildStatus {
|
||||
success: false,
|
||||
stdout: format!("Loading object '{}'", base_path),
|
||||
stderr: format!("{:#}", e),
|
||||
stdout: format!("Loading object '{base_path}'"),
|
||||
stderr: format!("{e:#}"),
|
||||
..Default::default()
|
||||
};
|
||||
None
|
||||
|
||||
@@ -10,7 +10,7 @@ use anyhow::{Context, Result};
|
||||
pub use self_update; // Re-export self_update crate
|
||||
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 build_updater: fn() -> Result<Box<dyn ReleaseUpdate>>,
|
||||
|
||||
109
objdiff-core/src/obj/dwarf2.rs
Normal file
109
objdiff-core/src/obj/dwarf2.rs
Normal file
@@ -0,0 +1,109 @@
|
||||
use alloc::{borrow::Cow, vec::Vec};
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use object::{Object as _, ObjectSection as _};
|
||||
use typed_arena::Arena;
|
||||
|
||||
use crate::obj::{Section, SectionKind};
|
||||
|
||||
/// Parse line information from DWARF 2+ sections.
|
||||
pub(crate) fn parse_line_info_dwarf2(
|
||||
obj_file: &object::File,
|
||||
sections: &mut [Section],
|
||||
) -> Result<()> {
|
||||
let arena_data = Arena::new();
|
||||
let arena_relocations = Arena::new();
|
||||
let endian = match obj_file.endianness() {
|
||||
object::Endianness::Little => gimli::RunTimeEndian::Little,
|
||||
object::Endianness::Big => gimli::RunTimeEndian::Big,
|
||||
};
|
||||
let dwarf = gimli::Dwarf::load(|id: gimli::SectionId| -> Result<_> {
|
||||
load_file_section(id, obj_file, endian, &arena_data, &arena_relocations)
|
||||
})
|
||||
.context("loading DWARF sections")?;
|
||||
|
||||
let mut iter = dwarf.units();
|
||||
if let Some(header) = iter.next().map_err(|e| gimli_error(e, "iterating over DWARF units"))? {
|
||||
let unit = dwarf.unit(header).map_err(|e| gimli_error(e, "loading DWARF unit"))?;
|
||||
if let Some(program) = unit.line_program.clone() {
|
||||
let mut text_sections = sections.iter_mut().filter(|s| s.kind == SectionKind::Code);
|
||||
let mut lines = text_sections.next().map(|section| &mut section.line_info);
|
||||
|
||||
let mut rows = program.rows();
|
||||
while let Some((_header, row)) =
|
||||
rows.next_row().map_err(|e| gimli_error(e, "loading program row"))?
|
||||
{
|
||||
if let (Some(line), Some(lines)) = (row.line(), &mut lines) {
|
||||
lines.insert(row.address(), line.get() as u32);
|
||||
}
|
||||
if row.end_sequence() {
|
||||
// The next row is the start of a new sequence, which means we must
|
||||
// advance to the next .text section.
|
||||
lines = text_sections.next().map(|section| &mut section.line_info);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if iter.next().map_err(|e| gimli_error(e, "checking for next unit"))?.is_some() {
|
||||
log::warn!("Multiple units found in DWARF data, only processing the first");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct RelocationMap(object::read::RelocationMap);
|
||||
|
||||
impl RelocationMap {
|
||||
fn add(&mut self, file: &object::File, section: &object::Section) {
|
||||
for (offset, relocation) in section.relocations() {
|
||||
if let Err(e) = self.0.add(file, offset, relocation) {
|
||||
log::error!(
|
||||
"Relocation error for section {} at offset 0x{:08x}: {}",
|
||||
section.name().unwrap(),
|
||||
offset,
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl gimli::read::Relocate for &'_ RelocationMap {
|
||||
fn relocate_address(&self, offset: usize, value: u64) -> gimli::Result<u64> {
|
||||
Ok(self.0.relocate(offset as u64, value))
|
||||
}
|
||||
|
||||
fn relocate_offset(&self, offset: usize, value: usize) -> gimli::Result<usize> {
|
||||
<usize as gimli::ReaderOffset>::from_u64(self.0.relocate(offset as u64, value as u64))
|
||||
}
|
||||
}
|
||||
|
||||
type Relocate<'a, R> = gimli::RelocateReader<R, &'a RelocationMap>;
|
||||
|
||||
fn load_file_section<'input, 'arena, Endian: gimli::Endianity>(
|
||||
id: gimli::SectionId,
|
||||
file: &object::File<'input>,
|
||||
endian: Endian,
|
||||
arena_data: &'arena Arena<Cow<'input, [u8]>>,
|
||||
arena_relocations: &'arena Arena<RelocationMap>,
|
||||
) -> Result<Relocate<'arena, gimli::EndianSlice<'arena, Endian>>> {
|
||||
let mut relocations = RelocationMap::default();
|
||||
let data = match file.section_by_name(id.name()) {
|
||||
Some(ref section) => {
|
||||
relocations.add(file, section);
|
||||
section.uncompressed_data()?
|
||||
}
|
||||
// Use a non-zero capacity so that `ReaderOffsetId`s are unique.
|
||||
None => Cow::Owned(Vec::with_capacity(1)),
|
||||
};
|
||||
let data_ref = arena_data.alloc(data);
|
||||
let section = gimli::EndianSlice::new(data_ref, endian);
|
||||
let relocations = arena_relocations.alloc(relocations);
|
||||
Ok(Relocate::new(section, relocations))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn gimli_error(e: gimli::Error, context: &str) -> anyhow::Error {
|
||||
anyhow::anyhow!("gimli error {context}: {e:?}")
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
#[cfg(feature = "dwarf")]
|
||||
mod dwarf2;
|
||||
pub mod read;
|
||||
pub mod split_meta;
|
||||
|
||||
@@ -9,9 +11,12 @@ use alloc::{
|
||||
vec,
|
||||
vec::Vec,
|
||||
};
|
||||
use core::{fmt, num::NonZeroU32};
|
||||
use core::{
|
||||
fmt,
|
||||
num::{NonZeroU32, NonZeroU64},
|
||||
};
|
||||
|
||||
use flagset::{flags, FlagSet};
|
||||
use flagset::{FlagSet, flags};
|
||||
|
||||
use crate::{
|
||||
arch::{Arch, ArchDummy},
|
||||
@@ -42,6 +47,8 @@ flags! {
|
||||
HasExtra,
|
||||
/// Symbol size was missing and was inferred
|
||||
SizeInferred,
|
||||
/// Symbol should be ignored by any diffing
|
||||
Ignored,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +59,6 @@ flags! {
|
||||
pub enum SectionFlag: u8 {
|
||||
/// Section combined from multiple input sections
|
||||
Combined,
|
||||
Hidden,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,6 +74,7 @@ pub struct Section {
|
||||
pub kind: SectionKind,
|
||||
pub data: SectionData,
|
||||
pub flags: SectionFlagSet,
|
||||
pub align: Option<NonZeroU64>,
|
||||
pub relocations: Vec<Relocation>,
|
||||
/// Line number info (.line or .debug_line section)
|
||||
pub line_info: BTreeMap<u64, u32>,
|
||||
@@ -93,14 +100,14 @@ impl fmt::Debug for SectionData {
|
||||
|
||||
impl Section {
|
||||
pub fn data_range(&self, address: u64, size: usize) -> Option<&[u8]> {
|
||||
let start = self.address;
|
||||
let end = start + self.size;
|
||||
if address >= start && address + size as u64 <= end {
|
||||
let offset = (address - start) as usize;
|
||||
Some(&self.data[offset..offset + size])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
let offset = address.checked_sub(self.address)?;
|
||||
self.data.get(offset as usize..offset as usize + size)
|
||||
}
|
||||
|
||||
// The alignment to use when "Combine data/text sections" is enabled.
|
||||
pub fn combined_alignment(&self) -> u64 {
|
||||
const MIN_ALIGNMENT: u64 = 4;
|
||||
self.align.map(|align| align.get().max(MIN_ALIGNMENT)).unwrap_or(MIN_ALIGNMENT)
|
||||
}
|
||||
|
||||
pub fn relocation_at<'obj>(
|
||||
@@ -109,11 +116,21 @@ impl Section {
|
||||
ins_ref: InstructionRef,
|
||||
) -> Option<ResolvedRelocation<'obj>> {
|
||||
match self.relocations.binary_search_by_key(&ins_ref.address, |r| r.address) {
|
||||
Ok(i) => self.relocations.get(i),
|
||||
Ok(mut i) => {
|
||||
// Find the first relocation at the address
|
||||
while i
|
||||
.checked_sub(1)
|
||||
.and_then(|n| self.relocations.get(n))
|
||||
.is_some_and(|r| r.address == ins_ref.address)
|
||||
{
|
||||
i -= 1;
|
||||
}
|
||||
self.relocations.get(i)
|
||||
}
|
||||
Err(i) => self
|
||||
.relocations
|
||||
.get(i)
|
||||
.take_if(|r| r.address < ins_ref.address + ins_ref.size as u64),
|
||||
.filter(|r| r.address < ins_ref.address + ins_ref.size as u64),
|
||||
}
|
||||
.and_then(|relocation| {
|
||||
let symbol = obj.symbols.get(relocation.target_symbol)?;
|
||||
@@ -164,8 +181,8 @@ impl fmt::Display for InstructionArgValue<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
InstructionArgValue::Signed(v) => write!(f, "{:#x}", ReallySigned(*v)),
|
||||
InstructionArgValue::Unsigned(v) => write!(f, "{:#x}", v),
|
||||
InstructionArgValue::Opaque(v) => write!(f, "{}", v),
|
||||
InstructionArgValue::Unsigned(v) => write!(f, "{v:#x}"),
|
||||
InstructionArgValue::Opaque(v) => write!(f, "{v}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -209,11 +226,6 @@ pub struct InstructionRef {
|
||||
pub address: u64,
|
||||
pub size: u8,
|
||||
pub opcode: u16,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct ScannedInstruction {
|
||||
pub ins_ref: InstructionRef,
|
||||
pub branch_dest: Option<u64>,
|
||||
}
|
||||
|
||||
@@ -233,6 +245,19 @@ pub enum SymbolKind {
|
||||
Section,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum FlowAnalysisValue {
|
||||
Text(String),
|
||||
}
|
||||
|
||||
pub trait FlowAnalysisResult: core::fmt::Debug + Send {
|
||||
fn get_argument_value_at_address(
|
||||
&self,
|
||||
address: u64,
|
||||
argument: u8,
|
||||
) -> Option<&FlowAnalysisValue>;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash, Default)]
|
||||
pub struct Symbol {
|
||||
pub name: String,
|
||||
@@ -260,6 +285,7 @@ pub struct Object {
|
||||
pub path: Option<std::path::PathBuf>,
|
||||
#[cfg(feature = "std")]
|
||||
pub timestamp: Option<filetime::FileTime>,
|
||||
pub flow_analysis_results: BTreeMap<u64, Box<dyn FlowAnalysisResult>>,
|
||||
}
|
||||
|
||||
impl Default for Object {
|
||||
@@ -274,6 +300,7 @@ impl Default for Object {
|
||||
path: None,
|
||||
#[cfg(feature = "std")]
|
||||
timestamp: None,
|
||||
flow_analysis_results: BTreeMap::<u64, Box<dyn FlowAnalysisResult>>::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -283,7 +310,7 @@ impl Object {
|
||||
&self,
|
||||
symbol_index: usize,
|
||||
ins_ref: InstructionRef,
|
||||
) -> Option<ResolvedInstructionRef> {
|
||||
) -> Option<ResolvedInstructionRef<'_>> {
|
||||
let symbol = self.symbols.get(symbol_index)?;
|
||||
let section_index = symbol.section?;
|
||||
let section = self.sections.get(section_index)?;
|
||||
@@ -308,6 +335,26 @@ impl Object {
|
||||
let offset = symbol.address.checked_sub(section.address)?;
|
||||
section.data.get(offset as usize..offset as usize + symbol.size as usize)
|
||||
}
|
||||
|
||||
pub fn symbol_by_name(&self, name: &str) -> Option<usize> {
|
||||
self.symbols.iter().position(|symbol| symbol.section.is_some() && symbol.name == name)
|
||||
}
|
||||
|
||||
pub fn get_flow_analysis_result(&self, symbol: &Symbol) -> Option<&dyn FlowAnalysisResult> {
|
||||
let key = symbol.section.unwrap_or_default() as u64 * 1024 * 1024 * 1024 + symbol.address;
|
||||
self.flow_analysis_results.get(&key).map(|result| result.as_ref())
|
||||
}
|
||||
|
||||
pub fn add_flow_analysis_result(
|
||||
&mut self,
|
||||
symbol: &Symbol,
|
||||
result: Box<dyn FlowAnalysisResult>,
|
||||
) {
|
||||
let key = symbol.section.unwrap_or_default() as u64 * 1024 * 1024 * 1024 + symbol.address;
|
||||
self.flow_analysis_results.insert(key, result);
|
||||
}
|
||||
|
||||
pub fn has_flow_analysis_result(&self) -> bool { !self.flow_analysis_results.is_empty() }
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
@@ -330,6 +377,16 @@ pub struct ResolvedRelocation<'a> {
|
||||
pub symbol: &'a Symbol,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct ResolvedSymbol<'obj> {
|
||||
pub obj: &'obj Object,
|
||||
pub symbol_index: usize,
|
||||
pub symbol: &'obj Symbol,
|
||||
pub section_index: usize,
|
||||
pub section: &'obj Section,
|
||||
pub data: &'obj [u8],
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct ResolvedInstructionRef<'obj> {
|
||||
pub ins_ref: InstructionRef,
|
||||
@@ -361,6 +418,7 @@ static DUMMY_SECTION: Section = Section {
|
||||
kind: SectionKind::Unknown,
|
||||
data: SectionData(Vec::new()),
|
||||
flags: SectionFlagSet::empty(),
|
||||
align: None,
|
||||
relocations: Vec::new(),
|
||||
line_info: BTreeMap::new(),
|
||||
virtual_address: None,
|
||||
|
||||
@@ -1,30 +1,36 @@
|
||||
use alloc::{
|
||||
boxed::Box,
|
||||
collections::BTreeMap,
|
||||
format,
|
||||
string::{String, ToString},
|
||||
vec::Vec,
|
||||
};
|
||||
use core::cmp::Ordering;
|
||||
use core::{cmp::Ordering, num::NonZeroU64};
|
||||
|
||||
use anyhow::{bail, ensure, Context, Result};
|
||||
use anyhow::{Context, Result, anyhow, bail, ensure};
|
||||
use object::{Object as _, ObjectSection as _, ObjectSymbol as _};
|
||||
|
||||
use crate::{
|
||||
arch::{new_arch, Arch},
|
||||
arch::{Arch, RelocationOverride, RelocationOverrideTarget, new_arch},
|
||||
diff::DiffObjConfig,
|
||||
obj::{
|
||||
split_meta::{SplitMeta, SPLITMETA_SECTION},
|
||||
Object, Relocation, RelocationFlags, Section, SectionData, SectionFlag, SectionKind,
|
||||
Symbol, SymbolFlag, SymbolKind,
|
||||
FlowAnalysisResult, Object, Relocation, RelocationFlags, Section, SectionData, SectionFlag,
|
||||
SectionKind, Symbol, SymbolFlag, SymbolKind,
|
||||
split_meta::{SPLITMETA_SECTION, SplitMeta},
|
||||
},
|
||||
util::{read_u16, read_u32},
|
||||
util::{align_data_slice_to, align_u64_to, read_u16, read_u32},
|
||||
};
|
||||
|
||||
fn map_section_kind(section: &object::Section) -> SectionKind {
|
||||
match section.kind() {
|
||||
object::SectionKind::Text => SectionKind::Code,
|
||||
object::SectionKind::Data | object::SectionKind::ReadOnlyData => SectionKind::Data,
|
||||
object::SectionKind::UninitializedData => SectionKind::Bss,
|
||||
object::SectionKind::Data
|
||||
| object::SectionKind::ReadOnlyData
|
||||
| object::SectionKind::ReadOnlyString
|
||||
| object::SectionKind::Tls => SectionKind::Data,
|
||||
object::SectionKind::UninitializedData
|
||||
| object::SectionKind::UninitializedTls
|
||||
| object::SectionKind::Common => SectionKind::Bss,
|
||||
_ => SectionKind::Unknown,
|
||||
}
|
||||
}
|
||||
@@ -42,7 +48,7 @@ fn map_symbol(
|
||||
(symbol.kind(), symbol.section_index().and_then(|i| file.section_by_index(i).ok()))
|
||||
{
|
||||
let section_name = section.name().context("Failed to process section name")?;
|
||||
name = format!("[{}]", section_name);
|
||||
name = format!("[{section_name}]");
|
||||
// For section symbols, set the size to zero. If the size is non-zero, it will be included
|
||||
// in the diff. Most of the time, this is duplicative, given that we'll have function or
|
||||
// object symbols that cover the same range. In the case of an empty section, the size
|
||||
@@ -116,12 +122,21 @@ fn map_symbols(
|
||||
}
|
||||
|
||||
// Infer symbol sizes for 0-size symbols
|
||||
infer_symbol_sizes(&mut symbols, sections);
|
||||
infer_symbol_sizes(arch, &mut symbols, sections)?;
|
||||
|
||||
Ok((symbols, symbol_indices))
|
||||
}
|
||||
|
||||
fn infer_symbol_sizes(symbols: &mut [Symbol], sections: &[Section]) {
|
||||
/// When inferring a symbol's size, we ignore symbols that start with specific prefixes. They are
|
||||
/// usually emitted as branch targets and do not represent the start of a function or object.
|
||||
fn is_local_label(symbol: &Symbol) -> bool {
|
||||
const LABEL_PREFIXES: &[&str] = &[".L", "LAB_"];
|
||||
symbol.size == 0
|
||||
&& symbol.flags.contains(SymbolFlag::Local)
|
||||
&& LABEL_PREFIXES.iter().any(|p| symbol.name.starts_with(p))
|
||||
}
|
||||
|
||||
fn infer_symbol_sizes(arch: &dyn Arch, symbols: &mut [Symbol], sections: &[Section]) -> Result<()> {
|
||||
// Create a sorted list of symbol indices by section
|
||||
let mut symbols_with_section = Vec::<usize>::with_capacity(symbols.len());
|
||||
for (i, symbol) in symbols.iter().enumerate() {
|
||||
@@ -151,41 +166,53 @@ fn infer_symbol_sizes(symbols: &mut [Symbol], sections: &[Section]) {
|
||||
|
||||
// Set symbol sizes based on the next symbol's address
|
||||
let mut iter_idx = 0;
|
||||
let mut last_end = (0, 0);
|
||||
while iter_idx < symbols_with_section.len() {
|
||||
let symbol_idx = symbols_with_section[iter_idx];
|
||||
let symbol = &symbols[symbol_idx];
|
||||
let section_idx = symbol.section.unwrap();
|
||||
iter_idx += 1;
|
||||
if symbol.size != 0 {
|
||||
if symbol.kind != SymbolKind::Section {
|
||||
last_end = (section_idx, symbol.address + symbol.size);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
let section_idx = symbol.section.unwrap();
|
||||
let next_symbol = match symbol.kind {
|
||||
// For function/object symbols, find the next function/object symbol (in other words:
|
||||
// skip over labels)
|
||||
SymbolKind::Function | SymbolKind::Object => loop {
|
||||
if iter_idx >= symbols_with_section.len() {
|
||||
break None;
|
||||
// Skip over symbols that are contained within the previous symbol
|
||||
if last_end.0 == section_idx && last_end.1 > symbol.address {
|
||||
continue;
|
||||
}
|
||||
let next_symbol = loop {
|
||||
if iter_idx >= symbols_with_section.len() {
|
||||
break None;
|
||||
}
|
||||
let next_symbol = &symbols[symbols_with_section[iter_idx]];
|
||||
if next_symbol.section != Some(section_idx) {
|
||||
break None;
|
||||
}
|
||||
if match symbol.kind {
|
||||
SymbolKind::Function | SymbolKind::Object => {
|
||||
// For function/object symbols, find the next function/object
|
||||
matches!(next_symbol.kind, SymbolKind::Function | SymbolKind::Object)
|
||||
}
|
||||
let next_symbol = &symbols[symbols_with_section[iter_idx]];
|
||||
if next_symbol.section != Some(section_idx) {
|
||||
break None;
|
||||
SymbolKind::Unknown | SymbolKind::Section => {
|
||||
// For labels (or anything else), stop at any symbol
|
||||
true
|
||||
}
|
||||
if let SymbolKind::Function | SymbolKind::Object = next_symbol.kind {
|
||||
break Some(next_symbol);
|
||||
}
|
||||
iter_idx += 1;
|
||||
},
|
||||
// For labels (or anything else), simply use the next symbol's address
|
||||
SymbolKind::Unknown | SymbolKind::Section => symbols_with_section
|
||||
.get(iter_idx)
|
||||
.map(|&i| &symbols[i])
|
||||
.take_if(|s| s.section == Some(section_idx)),
|
||||
} && !is_local_label(next_symbol)
|
||||
{
|
||||
break Some(next_symbol);
|
||||
}
|
||||
iter_idx += 1;
|
||||
};
|
||||
let section = §ions[section_idx];
|
||||
let next_address =
|
||||
next_symbol.map(|s| s.address).unwrap_or_else(|| section.address + section.size);
|
||||
let new_size = if section.kind == SectionKind::Code {
|
||||
arch.infer_function_size(symbol, section, next_address)?
|
||||
} else {
|
||||
next_address.saturating_sub(symbol.address)
|
||||
};
|
||||
let next_address = next_symbol.map(|s| s.address).unwrap_or_else(|| {
|
||||
let section = §ions[section_idx];
|
||||
section.address + section.size
|
||||
});
|
||||
let new_size = next_address.saturating_sub(symbol.address);
|
||||
if new_size > 0 {
|
||||
let symbol = &mut symbols[symbol_idx];
|
||||
symbol.size = new_size;
|
||||
@@ -194,7 +221,7 @@ fn infer_symbol_sizes(symbols: &mut [Symbol], sections: &[Section]) {
|
||||
}
|
||||
// Set symbol kind if unknown and size is non-zero
|
||||
if symbol.kind == SymbolKind::Unknown {
|
||||
symbol.kind = match sections[section_idx].kind {
|
||||
symbol.kind = match section.kind {
|
||||
SectionKind::Code => SymbolKind::Function,
|
||||
SectionKind::Data | SectionKind::Bss => SymbolKind::Object,
|
||||
_ => SymbolKind::Unknown,
|
||||
@@ -202,6 +229,7 @@ fn infer_symbol_sizes(symbols: &mut [Symbol], sections: &[Section]) {
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn map_sections(
|
||||
@@ -234,7 +262,7 @@ fn map_sections(
|
||||
});
|
||||
|
||||
let unique_id = section_names.entry(name.to_string()).or_insert(0);
|
||||
let id = format!("{}-{}", name, unique_id);
|
||||
let id = format!("{name}-{unique_id}");
|
||||
*unique_id += 1;
|
||||
|
||||
if section_indices.len() <= section.index().0 {
|
||||
@@ -249,6 +277,7 @@ fn map_sections(
|
||||
kind,
|
||||
data: SectionData(data),
|
||||
flags: Default::default(),
|
||||
align: NonZeroU64::new(section.align()),
|
||||
relocations: Default::default(),
|
||||
virtual_address,
|
||||
line_info: Default::default(),
|
||||
@@ -263,7 +292,7 @@ const LOW_PRIORITY_SYMBOLS: &[&str] =
|
||||
fn best_symbol<'r, 'data, 'file>(
|
||||
symbols: &'r [object::Symbol<'data, 'file>],
|
||||
address: u64,
|
||||
) -> Option<object::SymbolIndex> {
|
||||
) -> Option<(object::SymbolIndex, u64)> {
|
||||
let mut closest_symbol_index = match symbols.binary_search_by_key(&address, |s| s.address()) {
|
||||
Ok(index) => Some(index),
|
||||
Err(index) => index.checked_sub(1),
|
||||
@@ -297,84 +326,248 @@ fn best_symbol<'r, 'data, 'file>(
|
||||
best_symbol = Some(symbol);
|
||||
}
|
||||
}
|
||||
best_symbol.map(|s| s.index())
|
||||
best_symbol.map(|s| (s.index(), s.address()))
|
||||
}
|
||||
|
||||
fn map_section_relocations(
|
||||
arch: &dyn Arch,
|
||||
obj_file: &object::File,
|
||||
obj_section: &object::Section,
|
||||
symbol_indices: &[usize],
|
||||
ordered_symbols: &[Vec<object::Symbol>],
|
||||
) -> Result<Vec<Relocation>> {
|
||||
let mut relocations = Vec::<Relocation>::with_capacity(obj_section.relocations().count());
|
||||
for (address, reloc) in obj_section.relocations() {
|
||||
let mut target_reloc = RelocationOverride {
|
||||
target: match reloc.target() {
|
||||
object::RelocationTarget::Symbol(symbol) => {
|
||||
RelocationOverrideTarget::Symbol(symbol)
|
||||
}
|
||||
object::RelocationTarget::Section(section) => {
|
||||
RelocationOverrideTarget::Section(section)
|
||||
}
|
||||
_ => RelocationOverrideTarget::Skip,
|
||||
},
|
||||
addend: reloc.addend(),
|
||||
};
|
||||
|
||||
// Allow the architecture to override the relocation target and addend
|
||||
match arch.relocation_override(obj_file, obj_section, address, &reloc)? {
|
||||
Some(reloc_override) => {
|
||||
match reloc_override.target {
|
||||
RelocationOverrideTarget::Keep => {}
|
||||
target => {
|
||||
target_reloc.target = target;
|
||||
}
|
||||
}
|
||||
target_reloc.addend = reloc_override.addend;
|
||||
}
|
||||
None => {
|
||||
ensure!(
|
||||
!reloc.has_implicit_addend(),
|
||||
"Unsupported {:?} implicit relocation {:?}",
|
||||
obj_file.architecture(),
|
||||
reloc.flags()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve the relocation target symbol
|
||||
let (symbol_index, addend) = match target_reloc.target {
|
||||
RelocationOverrideTarget::Keep => unreachable!(),
|
||||
RelocationOverrideTarget::Skip => continue,
|
||||
RelocationOverrideTarget::Symbol(symbol_index) => {
|
||||
// Sometimes used to indicate "absolute"
|
||||
if symbol_index.0 == u32::MAX as usize {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the target is a section symbol, try to resolve a better symbol as the target
|
||||
if let Some(section_symbol) = obj_file
|
||||
.symbol_by_index(symbol_index)
|
||||
.ok()
|
||||
.filter(|s| s.kind() == object::SymbolKind::Section)
|
||||
{
|
||||
let section_index =
|
||||
section_symbol.section_index().context("Section symbol without section")?;
|
||||
let target_address =
|
||||
section_symbol.address().wrapping_add_signed(target_reloc.addend);
|
||||
if let Some((new_idx, addr)) = ordered_symbols
|
||||
.get(section_index.0)
|
||||
.and_then(|symbols| best_symbol(symbols, target_address))
|
||||
{
|
||||
(new_idx, target_address.wrapping_sub(addr) as i64)
|
||||
} else {
|
||||
(symbol_index, target_reloc.addend)
|
||||
}
|
||||
} else {
|
||||
(symbol_index, target_reloc.addend)
|
||||
}
|
||||
}
|
||||
RelocationOverrideTarget::Section(section_index) => {
|
||||
let section = match obj_file.section_by_index(section_index) {
|
||||
Ok(section) => section,
|
||||
Err(e) => {
|
||||
log::warn!("Invalid relocation section: {e}");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let Ok(target_address) = u64::try_from(target_reloc.addend) else {
|
||||
log::warn!(
|
||||
"Negative section relocation addend: {}{}",
|
||||
section.name()?,
|
||||
target_reloc.addend
|
||||
);
|
||||
continue;
|
||||
};
|
||||
let Some(symbols) = ordered_symbols.get(section_index.0) else {
|
||||
log::warn!(
|
||||
"Couldn't resolve relocation target symbol for section {} (no symbols)",
|
||||
section.name()?
|
||||
);
|
||||
continue;
|
||||
};
|
||||
// Attempt to resolve a target symbol for the relocation
|
||||
if let Some((new_idx, addr)) = best_symbol(symbols, target_address) {
|
||||
(new_idx, target_address.wrapping_sub(addr) as i64)
|
||||
} else if let Some(section_symbol) =
|
||||
symbols.iter().find(|s| s.kind() == object::SymbolKind::Section)
|
||||
{
|
||||
(
|
||||
section_symbol.index(),
|
||||
target_address.wrapping_sub(section_symbol.address()) as i64,
|
||||
)
|
||||
} else {
|
||||
log::warn!(
|
||||
"Couldn't resolve relocation target symbol for section {}",
|
||||
section.name()?
|
||||
);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let flags = match reloc.flags() {
|
||||
object::RelocationFlags::Elf { r_type } => RelocationFlags::Elf(r_type),
|
||||
object::RelocationFlags::Coff { typ } => RelocationFlags::Coff(typ),
|
||||
flags => bail!("Unhandled relocation flags: {:?}", flags),
|
||||
};
|
||||
let target_symbol = match symbol_indices.get(symbol_index.0).copied() {
|
||||
Some(i) => i,
|
||||
None => {
|
||||
log::warn!("Invalid symbol index {}", symbol_index.0);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
relocations.push(Relocation { address, flags, target_symbol, addend });
|
||||
}
|
||||
relocations.sort_by_key(|r| r.address);
|
||||
Ok(relocations)
|
||||
}
|
||||
|
||||
fn map_relocations(
|
||||
arch: &dyn Arch,
|
||||
obj_file: &object::File,
|
||||
obj_section: &object::Section,
|
||||
sections: &mut [Section],
|
||||
section_indices: &[usize],
|
||||
symbol_indices: &[usize],
|
||||
) -> Result<Vec<Relocation>> {
|
||||
let mut relocations = Vec::<Relocation>::with_capacity(obj_section.relocations().count());
|
||||
let mut ordered_symbols = None;
|
||||
for (address, reloc) in obj_section.relocations() {
|
||||
let target_symbol = match reloc.target() {
|
||||
object::RelocationTarget::Symbol(idx) => {
|
||||
if idx.0 == u32::MAX as usize {
|
||||
// ???
|
||||
continue;
|
||||
}
|
||||
// If the target is a section symbol, try to resolve a better symbol as the target
|
||||
let idx = if let Some(section_symbol) = obj_file
|
||||
.symbol_by_index(idx)
|
||||
.ok()
|
||||
.take_if(|s| s.kind() == object::SymbolKind::Section)
|
||||
{
|
||||
let section_index =
|
||||
section_symbol.section_index().context("Section symbol without section")?;
|
||||
let ordered_symbols = ordered_symbols.get_or_insert_with(|| {
|
||||
let mut vec = obj_file
|
||||
.symbols()
|
||||
.filter(|s| {
|
||||
s.section_index() == Some(section_index)
|
||||
&& s.kind() != object::SymbolKind::Section
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
vec.sort_by(|a, b| {
|
||||
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 {
|
||||
idx
|
||||
};
|
||||
match symbol_indices.get(idx.0).copied() {
|
||||
Some(i) => i,
|
||||
None => {
|
||||
log::warn!("Invalid symbol index {}", idx.0);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
object::RelocationTarget::Absolute => {
|
||||
let section_name = obj_section.name()?;
|
||||
log::warn!("Ignoring absolute relocation @ {}:{:#x}", section_name, address);
|
||||
) -> 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 perform_data_flow_analysis(obj: &mut Object, config: &DiffObjConfig) -> Result<()> {
|
||||
// If neither of these settings are on, no flow analysis to perform
|
||||
if !config.analyze_data_flow && !config.ppc_calculate_pool_relocations {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut generated_relocations = Vec::<(usize, Vec<Relocation>)>::new();
|
||||
let mut generated_flow_results = Vec::<(Symbol, Box<dyn FlowAnalysisResult>)>::new();
|
||||
for (section_index, section) in obj.sections.iter().enumerate() {
|
||||
if section.kind != SectionKind::Code {
|
||||
continue;
|
||||
}
|
||||
for symbol in obj.symbols.iter() {
|
||||
if symbol.section != Some(section_index) {
|
||||
continue;
|
||||
}
|
||||
_ => 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);
|
||||
if symbol.kind != SymbolKind::Function {
|
||||
continue;
|
||||
}
|
||||
};
|
||||
// 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 });
|
||||
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
|
||||
)
|
||||
})?;
|
||||
|
||||
// Optional pooled relocation computation
|
||||
// Long view: This could be replaced by the full data flow analysis
|
||||
// once that feature has stabilized.
|
||||
if config.ppc_calculate_pool_relocations {
|
||||
let relocations = obj.arch.generate_pooled_relocations(
|
||||
symbol.address,
|
||||
code,
|
||||
§ion.relocations,
|
||||
&obj.symbols,
|
||||
);
|
||||
generated_relocations.push((section_index, relocations));
|
||||
}
|
||||
|
||||
// Optional full data flow analysis
|
||||
if config.analyze_data_flow
|
||||
&& let Some(flow_result) =
|
||||
obj.arch.data_flow_analysis(obj, symbol, code, §ion.relocations)
|
||||
{
|
||||
generated_flow_results.push((symbol.clone(), flow_result));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(relocations)
|
||||
for (symbol, flow_result) in generated_flow_results {
|
||||
obj.add_flow_analysis_result(&symbol, flow_result);
|
||||
}
|
||||
for (section_index, mut relocations) in generated_relocations {
|
||||
obj.sections[section_index].relocations.append(&mut relocations);
|
||||
}
|
||||
for section in obj.sections.iter_mut() {
|
||||
section.relocations.sort_by_key(|r| r.address);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_line_info(
|
||||
@@ -384,6 +577,28 @@ fn parse_line_info(
|
||||
obj_data: &[u8],
|
||||
) -> Result<()> {
|
||||
// DWARF 1.1
|
||||
if let Err(e) = parse_line_info_dwarf1(obj_file, sections) {
|
||||
log::warn!("Failed to parse DWARF 1.1 line info: {e}");
|
||||
}
|
||||
|
||||
// DWARF 2+
|
||||
#[cfg(feature = "dwarf")]
|
||||
if let Err(e) = super::dwarf2::parse_line_info_dwarf2(obj_file, sections) {
|
||||
log::warn!("Failed to parse DWARF 2+ line info: {e}");
|
||||
}
|
||||
|
||||
// COFF
|
||||
if let object::File::Coff(coff) = obj_file
|
||||
&& let Err(e) = parse_line_info_coff(coff, sections, section_indices, obj_data)
|
||||
{
|
||||
log::warn!("Failed to parse COFF line info: {e}");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Parse .line section from DWARF 1.1 format.
|
||||
fn parse_line_info_dwarf1(obj_file: &object::File, sections: &mut [Section]) -> Result<()> {
|
||||
if let Some(section) = obj_file.section_by_name(".line") {
|
||||
let data = section.uncompressed_data()?;
|
||||
let mut reader: &[u8] = data.as_ref();
|
||||
@@ -404,62 +619,13 @@ fn parse_line_info(
|
||||
let line_number = read_u32(obj_file, &mut section_data)?;
|
||||
let statement_pos = read_u16(obj_file, &mut section_data)?;
|
||||
if statement_pos != 0xFFFF {
|
||||
log::warn!("Unhandled statement pos {}", statement_pos);
|
||||
log::warn!("Unhandled statement pos {statement_pos}");
|
||||
}
|
||||
let address_delta = read_u32(obj_file, &mut section_data)? as u64;
|
||||
out_section.line_info.insert(base_address + address_delta, line_number);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DWARF 2+
|
||||
#[cfg(feature = "dwarf")]
|
||||
{
|
||||
fn gimli_error(e: gimli::Error) -> anyhow::Error { anyhow::anyhow!("DWARF error: {e:?}") }
|
||||
let dwarf_cow = gimli::DwarfSections::load(|id| {
|
||||
Ok::<_, gimli::Error>(
|
||||
obj_file
|
||||
.section_by_name(id.name())
|
||||
.and_then(|section| section.uncompressed_data().ok())
|
||||
.unwrap_or(alloc::borrow::Cow::Borrowed(&[][..])),
|
||||
)
|
||||
})
|
||||
.map_err(gimli_error)?;
|
||||
let endian = match obj_file.endianness() {
|
||||
object::Endianness::Little => gimli::RunTimeEndian::Little,
|
||||
object::Endianness::Big => gimli::RunTimeEndian::Big,
|
||||
};
|
||||
let dwarf = dwarf_cow.borrow(|section| gimli::EndianSlice::new(section, endian));
|
||||
let mut iter = dwarf.units();
|
||||
if let Some(header) = iter.next().map_err(gimli_error)? {
|
||||
let unit = dwarf.unit(header).map_err(gimli_error)?;
|
||||
if let Some(program) = unit.line_program.clone() {
|
||||
let mut text_sections = sections.iter_mut().filter(|s| s.kind == SectionKind::Code);
|
||||
let mut lines = text_sections.next().map(|section| &mut section.line_info);
|
||||
|
||||
let mut rows = program.rows();
|
||||
while let Some((_header, row)) = rows.next_row().map_err(gimli_error)? {
|
||||
if let (Some(line), Some(lines)) = (row.line(), &mut lines) {
|
||||
lines.insert(row.address(), line.get() as u32);
|
||||
}
|
||||
if row.end_sequence() {
|
||||
// The next row is the start of a new sequence, which means we must
|
||||
// advance to the next .text section.
|
||||
lines = text_sections.next().map(|section| &mut section.line_info);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if iter.next().map_err(gimli_error)?.is_some() {
|
||||
log::warn!("Multiple units found in DWARF data, only processing the first");
|
||||
}
|
||||
}
|
||||
|
||||
// COFF
|
||||
if let object::File::Coff(coff) = obj_file {
|
||||
parse_line_info_coff(coff, sections, section_indices, obj_data)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -657,7 +823,10 @@ fn do_combine_sections(
|
||||
}
|
||||
offsets.push(current_offset);
|
||||
current_offset += section.size;
|
||||
let align = section.combined_alignment();
|
||||
current_offset = align_u64_to(current_offset, align);
|
||||
data_size += section.data.len();
|
||||
data_size = align_u64_to(data_size as u64, align) as usize;
|
||||
num_relocations += section.relocations.len();
|
||||
}
|
||||
if data_size > 0 {
|
||||
@@ -672,12 +841,13 @@ fn do_combine_sections(
|
||||
let section = &mut sections[i];
|
||||
section.size = 0;
|
||||
data.append(&mut section.data.0);
|
||||
align_data_slice_to(&mut data, section.combined_alignment());
|
||||
section.relocations.iter_mut().for_each(|r| r.address += offset);
|
||||
relocations.append(&mut section.relocations);
|
||||
line_info.append(&mut section.line_info.iter().map(|(&a, &l)| (a + offset, l)).collect());
|
||||
section.line_info.clear();
|
||||
if offset > 0 {
|
||||
section.flags |= SectionFlag::Hidden;
|
||||
section.kind = SectionKind::Unknown;
|
||||
}
|
||||
}
|
||||
{
|
||||
@@ -761,24 +931,19 @@ pub fn read(obj_path: &std::path::Path, config: &DiffObjConfig) -> Result<Object
|
||||
|
||||
pub fn parse(data: &[u8], config: &DiffObjConfig) -> Result<Object> {
|
||||
let obj_file = object::File::parse(data)?;
|
||||
let arch = new_arch(&obj_file)?;
|
||||
let mut arch = new_arch(&obj_file)?;
|
||||
let split_meta = parse_split_meta(&obj_file)?;
|
||||
let (mut sections, section_indices) =
|
||||
map_sections(arch.as_ref(), &obj_file, split_meta.as_ref())?;
|
||||
let (mut symbols, symbol_indices) =
|
||||
map_symbols(arch.as_ref(), &obj_file, §ions, §ion_indices, split_meta.as_ref())?;
|
||||
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_relocations(arch.as_ref(), &obj_file, &obj_section, &symbol_indices)?;
|
||||
}
|
||||
}
|
||||
map_relocations(arch.as_ref(), &obj_file, &mut sections, §ion_indices, &symbol_indices)?;
|
||||
parse_line_info(&obj_file, &mut sections, §ion_indices, data)?;
|
||||
if config.combine_data_sections || config.combine_text_sections {
|
||||
combine_sections(&mut sections, &mut symbols, config)?;
|
||||
}
|
||||
Ok(Object {
|
||||
arch.post_init(§ions, &symbols);
|
||||
let mut obj = Object {
|
||||
arch,
|
||||
endianness: obj_file.endianness(),
|
||||
symbols,
|
||||
@@ -788,7 +953,14 @@ pub fn parse(data: &[u8], config: &DiffObjConfig) -> Result<Object> {
|
||||
path: None,
|
||||
#[cfg(feature = "std")]
|
||||
timestamp: None,
|
||||
})
|
||||
flow_analysis_results: Default::default(),
|
||||
};
|
||||
|
||||
// Need to construct the obj first so that we have a convinient package to
|
||||
// pass to flow analysis. Then the flow analysis will mutate obj adding
|
||||
// additional data to it.
|
||||
perform_data_flow_analysis(&mut obj, config)?;
|
||||
Ok(obj)
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
|
||||
@@ -14,6 +14,7 @@ expression: "(sections, symbols)"
|
||||
8,
|
||||
),
|
||||
flags: FlagSet(),
|
||||
align: None,
|
||||
relocations: [
|
||||
Relocation {
|
||||
flags: Elf(
|
||||
@@ -53,6 +54,7 @@ expression: "(sections, symbols)"
|
||||
12,
|
||||
),
|
||||
flags: FlagSet(Combined),
|
||||
align: None,
|
||||
relocations: [
|
||||
Relocation {
|
||||
flags: Elf(
|
||||
@@ -82,11 +84,12 @@ expression: "(sections, symbols)"
|
||||
name: ".data",
|
||||
address: 0,
|
||||
size: 0,
|
||||
kind: Data,
|
||||
kind: Unknown,
|
||||
data: SectionData(
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(Hidden),
|
||||
flags: FlagSet(),
|
||||
align: None,
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
@@ -96,11 +99,12 @@ expression: "(sections, symbols)"
|
||||
name: ".data",
|
||||
address: 0,
|
||||
size: 0,
|
||||
kind: Data,
|
||||
kind: Unknown,
|
||||
data: SectionData(
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(Hidden),
|
||||
flags: FlagSet(),
|
||||
align: None,
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
use alloc::{string::String, vec, vec::Vec};
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use object::{elf::SHT_NOTE, Endian, ObjectSection};
|
||||
use anyhow::{Result, anyhow};
|
||||
use object::{Endian, ObjectSection, elf::SHT_NOTE};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use crate::util::align_data_to_4;
|
||||
use crate::util::align_size_to_4;
|
||||
|
||||
pub const SPLITMETA_SECTION: &str = ".note.split";
|
||||
pub const SHT_SPLITMETA: u32 = SHT_NOTE;
|
||||
@@ -190,17 +194,6 @@ where E: Endian
|
||||
}
|
||||
}
|
||||
|
||||
fn align_size_to_4(size: usize) -> usize { (size + 3) & !3 }
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
fn align_data_to_4<W: std::io::Write + ?Sized>(writer: &mut W, len: usize) -> std::io::Result<()> {
|
||||
const ALIGN_BYTES: &[u8] = &[0; 4];
|
||||
if len % 4 != 0 {
|
||||
writer.write_all(&ALIGN_BYTES[..4 - len % 4])?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// ELF note format:
|
||||
// Name Size | 4 bytes (integer)
|
||||
// Desc Size | 4 bytes (integer)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use alloc::format;
|
||||
use alloc::{format, vec::Vec};
|
||||
use core::fmt;
|
||||
|
||||
use anyhow::{ensure, Result};
|
||||
use anyhow::{Result, ensure};
|
||||
use num_traits::PrimInt;
|
||||
use object::{Endian, Object};
|
||||
|
||||
@@ -39,3 +39,53 @@ pub fn read_u16(obj_file: &object::File, reader: &mut &[u8]) -> Result<u16> {
|
||||
*reader = &reader[2..];
|
||||
Ok(obj_file.endianness().read_u16(value))
|
||||
}
|
||||
|
||||
pub fn align_size_to_4(size: usize) -> usize { (size + 3) & !3 }
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub fn align_data_to_4<W: std::io::Write + ?Sized>(
|
||||
writer: &mut W,
|
||||
len: usize,
|
||||
) -> std::io::Result<()> {
|
||||
const ALIGN_BYTES: &[u8] = &[0; 4];
|
||||
if !len.is_multiple_of(4) {
|
||||
writer.write_all(&ALIGN_BYTES[..4 - len % 4])?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn align_u64_to(len: u64, align: u64) -> u64 { len + ((align - (len % align)) % align) }
|
||||
|
||||
pub fn align_data_slice_to(data: &mut Vec<u8>, align: u64) {
|
||||
data.resize(align_u64_to(data.len() as u64, align) as usize, 0);
|
||||
}
|
||||
|
||||
// Float where we specifically care about comparing the raw bits rather than
|
||||
// caring about IEEE semantics.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct RawFloat(pub f32);
|
||||
impl PartialEq for RawFloat {
|
||||
fn eq(&self, other: &Self) -> bool { self.0.to_bits() == other.0.to_bits() }
|
||||
}
|
||||
impl Eq for RawFloat {}
|
||||
impl Ord for RawFloat {
|
||||
fn cmp(&self, other: &Self) -> core::cmp::Ordering { self.0.to_bits().cmp(&other.0.to_bits()) }
|
||||
}
|
||||
impl PartialOrd for RawFloat {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> { Some(self.cmp(other)) }
|
||||
}
|
||||
|
||||
// Double where we specifically care about comparing the raw bits rather than
|
||||
// caring about IEEE semantics.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct RawDouble(pub f64);
|
||||
impl PartialEq for RawDouble {
|
||||
fn eq(&self, other: &Self) -> bool { self.0.to_bits() == other.0.to_bits() }
|
||||
}
|
||||
impl Eq for RawDouble {}
|
||||
impl Ord for RawDouble {
|
||||
fn cmp(&self, other: &Self) -> core::cmp::Ordering { self.0.to_bits().cmp(&other.0.to_bits()) }
|
||||
}
|
||||
impl PartialOrd for RawDouble {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> { Some(self.cmp(other)) }
|
||||
}
|
||||
|
||||
46
objdiff-core/tests/arch_arm.rs
Normal file
46
objdiff-core/tests/arch_arm.rs
Normal file
@@ -0,0 +1,46 @@
|
||||
use objdiff_core::{diff, obj};
|
||||
|
||||
mod common;
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "arm")]
|
||||
fn read_arm() {
|
||||
let diff_config = diff::DiffObjConfig { ..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);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "arm")]
|
||||
fn read_thumb() {
|
||||
let diff_config = diff::DiffObjConfig { ..Default::default() };
|
||||
let obj = obj::read::parse(include_object!("data/arm/thumb.o"), &diff_config).unwrap();
|
||||
insta::assert_debug_snapshot!(obj);
|
||||
let symbol_idx = obj
|
||||
.symbols
|
||||
.iter()
|
||||
.position(|s| s.name == "THUMB_BRANCH_ServerDisplay_UncategorizedMove")
|
||||
.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 = "arm")]
|
||||
fn combine_text_sections() {
|
||||
let diff_config = diff::DiffObjConfig { combine_text_sections: true, ..Default::default() };
|
||||
let obj = obj::read::parse(include_object!("data/arm/enemy300.o"), &diff_config).unwrap();
|
||||
let symbol_idx = obj.symbols.iter().position(|s| s.name == "Enemy300Draw").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);
|
||||
}
|
||||
47
objdiff-core/tests/arch_mips.rs
Normal file
47
objdiff-core/tests/arch_mips.rs
Normal 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);
|
||||
}
|
||||
@@ -61,7 +61,7 @@ fn diff_ppc() {
|
||||
let base_diff = diff.right.as_ref().unwrap();
|
||||
let sections_display = display::display_sections(
|
||||
&target_obj,
|
||||
&target_diff,
|
||||
target_diff,
|
||||
display::SymbolFilter::None,
|
||||
false,
|
||||
false,
|
||||
@@ -85,3 +85,17 @@ fn diff_ppc() {
|
||||
assert_eq!(base_symbol_diff.target_symbol, Some(target_symbol_idx));
|
||||
insta::assert_debug_snapshot!((target_symbol_diff, base_symbol_diff));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "ppc")]
|
||||
fn read_vmx128_coff() {
|
||||
let diff_config = diff::DiffObjConfig { combine_data_sections: true, ..Default::default() };
|
||||
let obj = obj::read::parse(include_object!("data/ppc/vmx128.obj"), &diff_config).unwrap();
|
||||
insta::assert_debug_snapshot!(obj);
|
||||
let symbol_idx =
|
||||
obj.symbols.iter().position(|s| s.name == "?FloatingPointExample@@YAXXZ").unwrap();
|
||||
let diff = diff::code::no_diff_code(&obj, symbol_idx, &diff_config).unwrap();
|
||||
insta::assert_debug_snapshot!(diff.instruction_rows);
|
||||
let output = common::display_diff(&obj, &diff, symbol_idx, &diff_config);
|
||||
insta::assert_snapshot!(output);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use objdiff_core::{diff, obj};
|
||||
use objdiff_core::{diff, diff::display::SymbolFilter, obj};
|
||||
|
||||
mod common;
|
||||
|
||||
@@ -26,3 +26,54 @@ fn read_x86_combine_sections() {
|
||||
let obj = obj::read::parse(include_object!("data/x86/rtest.obj"), &diff_config).unwrap();
|
||||
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);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "x86")]
|
||||
fn read_x86_jumptable() {
|
||||
let diff_config = diff::DiffObjConfig::default();
|
||||
let obj = obj::read::parse(include_object!("data/x86/jumptable.o"), &diff_config).unwrap();
|
||||
insta::assert_debug_snapshot!(obj);
|
||||
let symbol_idx = obj.symbols.iter().position(|s| s.name == "?test@@YAHH@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);
|
||||
}
|
||||
|
||||
// Inferred size of functions should ignore symbols with specific prefixes
|
||||
#[test]
|
||||
#[cfg(feature = "x86")]
|
||||
fn read_x86_local_labels() {
|
||||
let diff_config = diff::DiffObjConfig::default();
|
||||
let obj = obj::read::parse(include_object!("data/x86/local_labels.obj"), &diff_config).unwrap();
|
||||
insta::assert_debug_snapshot!(obj);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use objdiff_core::{
|
||||
diff::{display::DiffTextSegment, DiffObjConfig, SymbolDiff},
|
||||
diff::{DiffObjConfig, SymbolDiff, display::DiffTextSegment},
|
||||
obj::Object,
|
||||
};
|
||||
|
||||
@@ -13,14 +13,14 @@ pub fn display_diff(
|
||||
for row in &diff.instruction_rows {
|
||||
output.push('[');
|
||||
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 {
|
||||
output.push_str(", ");
|
||||
} else {
|
||||
separator = true;
|
||||
}
|
||||
let DiffTextSegment { text, color, pad_to } = segment;
|
||||
output.push_str(&format!("({:?}, {:?}, {:?})", text, color, pad_to));
|
||||
output.push_str(&format!("({text:?}, {color:?}, {pad_to:?})"));
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
@@ -47,6 +47,6 @@ macro_rules! include_bytes_align_as {
|
||||
#[macro_export]
|
||||
macro_rules! include_object {
|
||||
($path:literal) => {
|
||||
include_bytes_align_as!(u32, $path)
|
||||
include_bytes_align_as!(u64, $path)
|
||||
};
|
||||
}
|
||||
|
||||
BIN
objdiff-core/tests/data/arm/LinkStateItem.o
Normal file
BIN
objdiff-core/tests/data/arm/LinkStateItem.o
Normal file
Binary file not shown.
BIN
objdiff-core/tests/data/arm/enemy300.o
Normal file
BIN
objdiff-core/tests/data/arm/enemy300.o
Normal file
Binary file not shown.
BIN
objdiff-core/tests/data/arm/thumb.o
Normal file
BIN
objdiff-core/tests/data/arm/thumb.o
Normal file
Binary file not shown.
BIN
objdiff-core/tests/data/mips/code_be.o
Normal file
BIN
objdiff-core/tests/data/mips/code_be.o
Normal file
Binary file not shown.
BIN
objdiff-core/tests/data/mips/code_le.o
Normal file
BIN
objdiff-core/tests/data/mips/code_le.o
Normal file
Binary file not shown.
BIN
objdiff-core/tests/data/mips/main.c.o
Normal file
BIN
objdiff-core/tests/data/mips/main.c.o
Normal file
Binary file not shown.
BIN
objdiff-core/tests/data/mips/vw_main.c.o
Normal file
BIN
objdiff-core/tests/data/mips/vw_main.c.o
Normal file
Binary file not shown.
BIN
objdiff-core/tests/data/ppc/vmx128.obj
Normal file
BIN
objdiff-core/tests/data/ppc/vmx128.obj
Normal file
Binary file not shown.
BIN
objdiff-core/tests/data/x86/basenode.obj
Normal file
BIN
objdiff-core/tests/data/x86/basenode.obj
Normal file
Binary file not shown.
BIN
objdiff-core/tests/data/x86/jumptable.o
Normal file
BIN
objdiff-core/tests/data/x86/jumptable.o
Normal file
Binary file not shown.
BIN
objdiff-core/tests/data/x86/local_labels.obj
Normal file
BIN
objdiff-core/tests/data/x86/local_labels.obj
Normal file
Binary file not shown.
BIN
objdiff-core/tests/data/x86_64/vs2022.o
Normal file
BIN
objdiff-core/tests/data/x86_64/vs2022.o
Normal file
Binary file not shown.
@@ -0,0 +1,7 @@
|
||||
---
|
||||
source: objdiff-core/tests/arch_arm.rs
|
||||
expression: output
|
||||
---
|
||||
[(Line(90), Dim, 5), (Address(0), Normal, 5), (Spacing(4), Normal, 0), (Opcode("ldr", 32799), Normal, 10), (Argument(Opaque("r12")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("pc")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(0)), Normal, 0), (Basic("]"), Normal, 0), (Basic(" (->"), Normal, 0), (BranchDest(8), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(90), Dim, 5), (Address(4), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bx", 32779), Normal, 10), (Argument(Opaque("r12")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(90), Dim, 5), (Address(8), Normal, 5), (Spacing(4), Normal, 0), (Opcode(".word", 65535), Normal, 10), (Symbol(Symbol { name: "esEnemyDraw", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
|
||||
@@ -0,0 +1,48 @@
|
||||
---
|
||||
source: objdiff-core/tests/arch_arm.rs
|
||||
expression: diff.instruction_rows
|
||||
---
|
||||
[
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 76,
|
||||
size: 4,
|
||||
opcode: 32799,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 80,
|
||||
size: 4,
|
||||
opcode: 32779,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 84,
|
||||
size: 4,
|
||||
opcode: 65535,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
]
|
||||
1876
objdiff-core/tests/snapshots/arch_arm__read_arm-2.snap
Normal file
1876
objdiff-core/tests/snapshots/arch_arm__read_arm-2.snap
Normal file
File diff suppressed because it is too large
Load Diff
112
objdiff-core/tests/snapshots/arch_arm__read_arm-3.snap
Normal file
112
objdiff-core/tests/snapshots/arch_arm__read_arm-3.snap
Normal 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), (Basic(" (->"), Normal, 0), (BranchDest(420), 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), (BranchDest(424), Normal, 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), (Basic(" (->"), Normal, 0), (BranchDest(424), 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), (Basic(" (->"), Normal, 0), (BranchDest(420), 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), (Basic(" (->"), Normal, 0), (BranchDest(428), 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(".word", 65535), Normal, 10), (Symbol(Symbol { name: "data_027e103c", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global | Weak), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
|
||||
[(Address(424), Normal, 5), (Basic(" ~> "), Rotating(8), 0), (Opcode(".word", 65535), Normal, 10), (Symbol(Symbol { name: "data_027e1098", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global | Weak), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
|
||||
[(Address(428), Normal, 5), (Spacing(4), Normal, 0), (Opcode(".word", 65535), Normal, 10), (Symbol(Symbol { name: "gPlayerControl", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global | Weak), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
|
||||
1958
objdiff-core/tests/snapshots/arch_arm__read_arm.snap
Normal file
1958
objdiff-core/tests/snapshots/arch_arm__read_arm.snap
Normal file
File diff suppressed because it is too large
Load Diff
1608
objdiff-core/tests/snapshots/arch_arm__read_thumb-2.snap
Normal file
1608
objdiff-core/tests/snapshots/arch_arm__read_thumb-2.snap
Normal file
File diff suppressed because it is too large
Load Diff
111
objdiff-core/tests/snapshots/arch_arm__read_thumb-3.snap
Normal file
111
objdiff-core/tests/snapshots/arch_arm__read_thumb-3.snap
Normal file
@@ -0,0 +1,111 @@
|
||||
---
|
||||
source: objdiff-core/tests/arch_arm.rs
|
||||
assertion_line: 33
|
||||
expression: output
|
||||
---
|
||||
[(Line(37), Dim, 5), (Address(0), Normal, 5), (Spacing(4), Normal, 0), (Opcode("push", 56), Normal, 10), (Basic("{"), Normal, 0), (Argument(Opaque("r3")), 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("r7")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("lr")), Normal, 0), (Basic("}"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(37), Dim, 5), (Address(2), Normal, 5), (Spacing(4), Normal, 0), (Opcode("sub", 74), Normal, 10), (Argument(Opaque("sp")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("sp")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(16)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(37), Dim, 5), (Address(4), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mov", 47), Normal, 10), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(39), Dim, 5), (Address(6), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mov", 47), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(39), Dim, 5), (Address(8), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mov", 47), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(39), Dim, 5), (Address(10), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mov", 47), Normal, 10), (Argument(Opaque("r6")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r2")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(39), Dim, 5), (Address(12), Normal, 5), (Spacing(4), Normal, 0), (Opcode("str", 66), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("sp")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(4)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(39), Dim, 5), (Address(14), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 19), Normal, 10), (Symbol(Symbol { name: "PokeSet_IsRemovedAll", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Addend(-4), Bright, 0), (Eol, Normal, 0)]
|
||||
[(Line(39), Dim, 5), (Address(18), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmp", 25), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(0)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(39), Dim, 5), (Address(20), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 15), Normal, 10), (BranchDest(212), Normal, 0), (Basic(" ~>"), Rotating(0), 0), (Eol, Normal, 0)]
|
||||
[(Line(44), Dim, 5), (Address(22), Normal, 5), (Spacing(4), Normal, 0), (Opcode("ldrh", 38), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(0)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(44), Dim, 5), (Address(24), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmp", 25), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(164)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(44), Dim, 5), (Address(26), Normal, 5), (Spacing(4), Normal, 0), (Opcode("beq", 15), Normal, 10), (BranchDest(48), Normal, 0), (Basic(" ~>"), Rotating(1), 0), (Eol, Normal, 0)]
|
||||
[(Line(44), Dim, 5), (Address(28), Normal, 5), (Spacing(4), Normal, 0), (Opcode("ldr", 34), 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(184)), Normal, 0), (Basic("]"), Normal, 0), (Basic(" (->"), Normal, 0), (BranchDest(216), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(44), Dim, 5), (Address(30), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmp", 26), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(44), Dim, 5), (Address(32), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 15), Normal, 10), (BranchDest(94), Normal, 0), (Basic(" ~>"), Rotating(2), 0), (Eol, Normal, 0)]
|
||||
[(Line(47), Dim, 5), (Address(34), Normal, 5), (Spacing(4), Normal, 0), (Opcode("ldr", 35), Normal, 10), (Argument(Opaque("r2")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("sp")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(4)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(47), Dim, 5), (Address(36), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mov", 47), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(47), Dim, 5), (Address(38), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mov", 47), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r6")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(47), Dim, 5), (Address(40), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 19), Normal, 10), (Symbol(Symbol { name: "ServerDisplay_SkillSwap", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Addend(-4), Bright, 0), (Eol, Normal, 0)]
|
||||
[(Line(86), Dim, 5), (Address(44), Normal, 5), (Spacing(4), Normal, 0), (Opcode("add", 7), Normal, 10), (Argument(Opaque("sp")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(16)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(86), Dim, 5), (Address(46), Normal, 5), (Spacing(4), Normal, 0), (Opcode("pop", 55), Normal, 10), (Basic("{"), Normal, 0), (Argument(Opaque("r3")), 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("r7")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("pc")), Normal, 0), (Basic("}"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(50), Dim, 5), (Address(48), Normal, 5), (Basic(" ~> "), Rotating(1), 0), (Opcode("mov", 47), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(50), Dim, 5), (Address(50), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mov", 47), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r6")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(50), Dim, 5), (Address(52), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 19), Normal, 10), (Symbol(Symbol { name: "ServerEvent_CreateSubstitute", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Addend(-4), Bright, 0), (Eol, Normal, 0)]
|
||||
[(Line(50), Dim, 5), (Address(56), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmp", 25), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(0)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(50), Dim, 5), (Address(58), Normal, 5), (Spacing(4), Normal, 0), (Opcode("beq", 15), Normal, 10), (BranchDest(212), Normal, 0), (Basic(" ~>"), Rotating(0), 0), (Eol, Normal, 0)]
|
||||
[(Line(52), Dim, 5), (Address(60), Normal, 5), (Spacing(4), Normal, 0), (Opcode("ldr", 34), 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(156)), Normal, 0), (Basic("]"), Normal, 0), (Basic(" (->"), Normal, 0), (BranchDest(220), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(52), Dim, 5), (Address(62), Normal, 5), (Spacing(4), Normal, 0), (Opcode("ldr", 33), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(52), Dim, 5), (Address(64), Normal, 5), (Spacing(4), Normal, 0), (Opcode("ldrb", 36), Normal, 10), (Argument(Opaque("r2")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(5)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(52), Dim, 5), (Address(66), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lsl", 42), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r2")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(31)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(52), Dim, 5), (Address(68), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lsr", 44), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(31)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(52), Dim, 5), (Address(70), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 15), Normal, 10), (BranchDest(212), Normal, 0), (Basic(" ~>"), Rotating(0), 0), (Eol, Normal, 0)]
|
||||
[(Line(52), Dim, 5), (Address(72), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mov", 46), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(1)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(52), Dim, 5), (Address(74), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bic", 17), Normal, 10), (Argument(Opaque("r2")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(52), Dim, 5), (Address(76), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mov", 46), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(1)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(52), Dim, 5), (Address(78), Normal, 5), (Spacing(4), Normal, 0), (Opcode("orr", 54), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r2")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(52), Dim, 5), (Address(80), Normal, 5), (Spacing(4), Normal, 0), (Opcode("strb", 67), 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(5)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(52), Dim, 5), (Address(82), Normal, 5), (Spacing(4), Normal, 0), (Opcode("ldrb", 36), Normal, 10), (Argument(Opaque("r2")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(5)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(52), Dim, 5), (Address(84), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mov", 46), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(2)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(86), Dim, 5), (Address(86), Normal, 5), (Spacing(4), Normal, 0), (Opcode("add", 7), Normal, 10), (Argument(Opaque("sp")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(16)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(52), Dim, 5), (Address(88), Normal, 5), (Spacing(4), Normal, 0), (Opcode("orr", 54), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r2")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(52), Dim, 5), (Address(90), Normal, 5), (Spacing(4), Normal, 0), (Opcode("strb", 67), 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(5)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(86), Dim, 5), (Address(92), Normal, 5), (Spacing(4), Normal, 0), (Opcode("pop", 55), Normal, 10), (Basic("{"), Normal, 0), (Argument(Opaque("r3")), 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("r7")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("pc")), Normal, 0), (Basic("}"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(57), Dim, 5), (Address(94), Normal, 5), (Basic(" ~> "), Rotating(2), 0), (Opcode("ldr", 34), 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(128)), Normal, 0), (Basic("]"), Normal, 0), (Basic(" (->"), Normal, 0), (BranchDest(224), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(57), Dim, 5), (Address(96), Normal, 5), (Spacing(4), Normal, 0), (Opcode("ldr", 34), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("pc")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(128)), Normal, 0), (Basic("]"), Normal, 0), (Basic(" (->"), Normal, 0), (BranchDest(228), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(57), Dim, 5), (Address(98), Normal, 5), (Spacing(4), Normal, 0), (Opcode("add", 4), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(57), Dim, 5), (Address(100), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 19), Normal, 10), (Symbol(Symbol { name: "HEManager_PushState", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Addend(-4), Bright, 0), (Eol, Normal, 0)]
|
||||
[(Line(57), Dim, 5), (Address(104), Normal, 5), (Spacing(4), Normal, 0), (Opcode("str", 66), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("sp")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(8)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(61), Dim, 5), (Address(106), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mov", 47), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(61), Dim, 5), (Address(108), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 19), Normal, 10), (Symbol(Symbol { name: "BattleHandler_Result", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Addend(-4), Bright, 0), (Eol, Normal, 0)]
|
||||
[(Line(61), Dim, 5), (Address(112), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mov", 47), Normal, 10), (Argument(Opaque("r7")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(63), Dim, 5), (Address(114), Normal, 5), (Spacing(4), Normal, 0), (Opcode("add", 6), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("sp")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(12)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(63), Dim, 5), (Address(116), Normal, 5), (Spacing(4), Normal, 0), (Opcode("str", 66), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("sp")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(0)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(63), Dim, 5), (Address(118), Normal, 5), (Spacing(4), Normal, 0), (Opcode("ldr", 35), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("sp")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(4)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(63), Dim, 5), (Address(120), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mov", 47), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(63), Dim, 5), (Address(122), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mov", 47), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(63), Dim, 5), (Address(124), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mov", 47), Normal, 10), (Argument(Opaque("r2")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r6")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(63), Dim, 5), (Address(126), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 19), Normal, 10), (Symbol(Symbol { name: "ServerEvent_UncategorizedMove", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Addend(-4), Bright, 0), (Eol, Normal, 0)]
|
||||
[(Line(63), Dim, 5), (Address(130), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmp", 25), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(0)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(63), Dim, 5), (Address(132), Normal, 5), (Spacing(4), Normal, 0), (Opcode("beq", 15), Normal, 10), (BranchDest(168), Normal, 0), (Basic(" ~>"), Rotating(3), 0), (Eol, Normal, 0)]
|
||||
[(Line(65), Dim, 5), (Address(134), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mov", 47), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(65), Dim, 5), (Address(136), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 19), Normal, 10), (Symbol(Symbol { name: "BattleHandler_Result", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Addend(-4), Bright, 0), (Eol, Normal, 0)]
|
||||
[(Line(65), Dim, 5), (Address(140), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mov", 47), Normal, 10), (Argument(Opaque("r7")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(66), Dim, 5), (Address(142), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmp", 25), Normal, 10), (Argument(Opaque("r7")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(2)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(66), Dim, 5), (Address(144), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 15), Normal, 10), (BranchDest(168), Normal, 0), (Basic(" ~>"), Rotating(3), 0), (Eol, Normal, 0)]
|
||||
[(Line(68), Dim, 5), (Address(146), Normal, 5), (Spacing(4), Normal, 0), (Opcode("ldr", 34), 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(72)), Normal, 0), (Basic("]"), Normal, 0), (Basic(" (->"), Normal, 0), (BranchDest(220), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(68), Dim, 5), (Address(148), Normal, 5), (Spacing(4), Normal, 0), (Opcode("ldr", 33), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(68), Dim, 5), (Address(150), Normal, 5), (Spacing(4), Normal, 0), (Opcode("ldrb", 36), Normal, 10), (Argument(Opaque("r2")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(5)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(68), Dim, 5), (Address(152), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lsl", 42), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r2")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(31)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(68), Dim, 5), (Address(154), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lsr", 44), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(31)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(68), Dim, 5), (Address(156), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 15), Normal, 10), (BranchDest(168), Normal, 0), (Basic(" ~>"), Rotating(3), 0), (Eol, Normal, 0)]
|
||||
[(Line(68), Dim, 5), (Address(158), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mov", 46), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(1)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(68), Dim, 5), (Address(160), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bic", 17), Normal, 10), (Argument(Opaque("r2")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(68), Dim, 5), (Address(162), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mov", 46), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(1)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(68), Dim, 5), (Address(164), Normal, 5), (Spacing(4), Normal, 0), (Opcode("orr", 54), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r2")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(68), Dim, 5), (Address(166), Normal, 5), (Spacing(4), Normal, 0), (Opcode("strb", 67), 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(5)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(72), Dim, 5), (Address(168), Normal, 5), (Basic(" ~> "), Rotating(3), 0), (Opcode("cmp", 25), Normal, 10), (Argument(Opaque("r7")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(1)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(72), Dim, 5), (Address(170), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bhi", 15), Normal, 10), (BranchDest(200), Normal, 0), (Basic(" ~>"), Rotating(4), 0), (Eol, Normal, 0)]
|
||||
[(Line(75), Dim, 5), (Address(172), Normal, 5), (Spacing(4), Normal, 0), (Opcode("ldr", 35), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("sp")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(12)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(75), Dim, 5), (Address(174), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmp", 25), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(0)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(75), Dim, 5), (Address(176), Normal, 5), (Spacing(4), Normal, 0), (Opcode("beq", 15), Normal, 10), (BranchDest(200), Normal, 0), (Basic(" ~>"), Rotating(4), 0), (Eol, Normal, 0)]
|
||||
[(Line(75), Dim, 5), (Address(178), Normal, 5), (Spacing(4), Normal, 0), (Opcode("ldr", 34), 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(52)), Normal, 0), (Basic("]"), Normal, 0), (Basic(" (->"), Normal, 0), (BranchDest(232), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(75), Dim, 5), (Address(180), Normal, 5), (Spacing(4), Normal, 0), (Opcode("ldrb", 37), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(75), Dim, 5), (Address(182), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lsl", 42), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(27)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(75), Dim, 5), (Address(184), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lsr", 44), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(31)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(75), Dim, 5), (Address(186), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 15), Normal, 10), (BranchDest(200), Normal, 0), (Basic(" ~>"), Rotating(4), 0), (Eol, Normal, 0)]
|
||||
[(Line(77), Dim, 5), (Address(188), Normal, 5), (Spacing(4), Normal, 0), (Opcode("ldr", 32), 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(12)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(77), Dim, 5), (Address(190), Normal, 5), (Spacing(4), Normal, 0), (Opcode("ldr", 34), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("pc")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(44)), Normal, 0), (Basic("]"), Normal, 0), (Basic(" (->"), Normal, 0), (BranchDest(236), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(77), Dim, 5), (Address(192), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mov", 46), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(90)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(77), Dim, 5), (Address(194), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mov", 46), Normal, 10), (Argument(Opaque("r2")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Unsigned(71)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(77), Dim, 5), (Address(196), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 19), Normal, 10), (Symbol(Symbol { name: "SCQUE_PUT_MsgImpl", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Addend(-4), Bright, 0), (Eol, Normal, 0)]
|
||||
[(Line(81), Dim, 5), (Address(200), Normal, 5), (Basic(" ~> "), Rotating(4), 0), (Opcode("ldr", 34), 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(20)), Normal, 0), (Basic("]"), Normal, 0), (Basic(" (->"), Normal, 0), (BranchDest(224), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(81), Dim, 5), (Address(202), Normal, 5), (Spacing(4), Normal, 0), (Opcode("ldr", 35), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("sp")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(8)), Normal, 0), (Basic("]"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(81), Dim, 5), (Address(204), Normal, 5), (Spacing(4), Normal, 0), (Opcode("ldr", 34), Normal, 10), (Argument(Opaque("r2")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("pc")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(32)), Normal, 0), (Basic("]"), Normal, 0), (Basic(" (->"), Normal, 0), (BranchDest(240), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(81), Dim, 5), (Address(206), Normal, 5), (Spacing(4), Normal, 0), (Opcode("add", 4), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(81), Dim, 5), (Address(208), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 19), Normal, 10), (Symbol(Symbol { name: "HEManager_PopState", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Addend(-4), Bright, 0), (Eol, Normal, 0)]
|
||||
[(Line(86), Dim, 5), (Address(212), Normal, 5), (Basic(" ~> "), Rotating(0), 0), (Opcode("add", 7), Normal, 10), (Argument(Opaque("sp")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(16)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(86), Dim, 5), (Address(214), Normal, 5), (Spacing(4), Normal, 0), (Opcode("pop", 55), Normal, 10), (Basic("{"), Normal, 0), (Argument(Opaque("r3")), 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("r7")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("pc")), Normal, 0), (Basic("}"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(86), Dim, 5), (Address(216), Normal, 5), (Spacing(4), Normal, 0), (Opcode(".word", 65535), Normal, 10), (Basic("#"), Normal, 0), (Argument(Unsigned(285)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(86), Dim, 5), (Address(220), Normal, 5), (Spacing(4), Normal, 0), (Opcode(".word", 65535), Normal, 10), (Basic("#"), Normal, 0), (Argument(Unsigned(1192)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(86), Dim, 5), (Address(224), Normal, 5), (Spacing(4), Normal, 0), (Opcode(".word", 65535), Normal, 10), (Basic("#"), Normal, 0), (Argument(Unsigned(7544)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(86), Dim, 5), (Address(228), Normal, 5), (Spacing(4), Normal, 0), (Opcode(".word", 65535), Normal, 10), (Basic("#"), Normal, 0), (Argument(Unsigned(9103)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(86), Dim, 5), (Address(232), Normal, 5), (Spacing(4), Normal, 0), (Opcode(".word", 65535), Normal, 10), (Basic("#"), Normal, 0), (Argument(Unsigned(1930)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(86), Dim, 5), (Address(236), Normal, 5), (Spacing(4), Normal, 0), (Opcode(".word", 65535), Normal, 10), (Basic("#"), Normal, 0), (Argument(Unsigned(4294901760)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(86), Dim, 5), (Address(240), Normal, 5), (Spacing(4), Normal, 0), (Opcode(".word", 65535), Normal, 10), (Basic("#"), Normal, 0), (Argument(Unsigned(9129)), Normal, 0), (Eol, Normal, 0)]
|
||||
3830
objdiff-core/tests/snapshots/arch_arm__read_thumb.snap
Normal file
3830
objdiff-core/tests/snapshots/arch_arm__read_thumb.snap
Normal file
File diff suppressed because it is too large
Load Diff
452
objdiff-core/tests/snapshots/arch_mips__filter_non_matching.snap
Normal file
452
objdiff-core/tests/snapshots/arch_mips__filter_non_matching.snap
Normal 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,
|
||||
},
|
||||
]
|
||||
694
objdiff-core/tests/snapshots/arch_mips__read_mips-2.snap
Normal file
694
objdiff-core/tests/snapshots/arch_mips__read_mips-2.snap
Normal file
@@ -0,0 +1,694 @@
|
||||
---
|
||||
source: objdiff-core/tests/arch_mips.rs
|
||||
expression: diff.instruction_rows
|
||||
---
|
||||
[
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 0,
|
||||
size: 4,
|
||||
opcode: 12,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 4,
|
||||
size: 4,
|
||||
opcode: 44,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 8,
|
||||
size: 4,
|
||||
opcode: 44,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 12,
|
||||
size: 4,
|
||||
opcode: 44,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 16,
|
||||
size: 4,
|
||||
opcode: 44,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 20,
|
||||
size: 4,
|
||||
opcode: 2,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 24,
|
||||
size: 4,
|
||||
opcode: 113,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 28,
|
||||
size: 4,
|
||||
opcode: 26,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 32,
|
||||
size: 4,
|
||||
opcode: 20,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 36,
|
||||
size: 4,
|
||||
opcode: 97,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 40,
|
||||
size: 4,
|
||||
opcode: 2,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 44,
|
||||
size: 4,
|
||||
opcode: 12,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 48,
|
||||
size: 4,
|
||||
opcode: 20,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 52,
|
||||
size: 4,
|
||||
opcode: 26,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 56,
|
||||
size: 4,
|
||||
opcode: 2,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 60,
|
||||
size: 4,
|
||||
opcode: 12,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 64,
|
||||
size: 4,
|
||||
opcode: 26,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 68,
|
||||
size: 4,
|
||||
opcode: 97,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 72,
|
||||
size: 4,
|
||||
opcode: 2,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 76,
|
||||
size: 4,
|
||||
opcode: 97,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 80,
|
||||
size: 4,
|
||||
opcode: 2,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
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,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 88,
|
||||
size: 4,
|
||||
opcode: 56,
|
||||
branch_dest: Some(
|
||||
80,
|
||||
),
|
||||
},
|
||||
),
|
||||
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,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 96,
|
||||
size: 4,
|
||||
opcode: 2,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 100,
|
||||
size: 4,
|
||||
opcode: 20,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 104,
|
||||
size: 4,
|
||||
opcode: 2,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 108,
|
||||
size: 4,
|
||||
opcode: 12,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 112,
|
||||
size: 4,
|
||||
opcode: 2,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 116,
|
||||
size: 4,
|
||||
opcode: 16,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 120,
|
||||
size: 4,
|
||||
opcode: 20,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 124,
|
||||
size: 4,
|
||||
opcode: 12,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 128,
|
||||
size: 4,
|
||||
opcode: 2,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
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,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 136,
|
||||
size: 4,
|
||||
opcode: 2,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 140,
|
||||
size: 4,
|
||||
opcode: 113,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 144,
|
||||
size: 4,
|
||||
opcode: 55,
|
||||
branch_dest: Some(
|
||||
128,
|
||||
),
|
||||
},
|
||||
),
|
||||
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,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 152,
|
||||
size: 4,
|
||||
opcode: 3,
|
||||
branch_dest: Some(
|
||||
128,
|
||||
),
|
||||
},
|
||||
),
|
||||
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,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 160,
|
||||
size: 4,
|
||||
opcode: 2,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 164,
|
||||
size: 4,
|
||||
opcode: 60,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 168,
|
||||
size: 4,
|
||||
opcode: 77,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 172,
|
||||
size: 4,
|
||||
opcode: 113,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 176,
|
||||
size: 4,
|
||||
opcode: 54,
|
||||
branch_dest: Some(
|
||||
128,
|
||||
),
|
||||
},
|
||||
),
|
||||
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,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
]
|
||||
50
objdiff-core/tests/snapshots/arch_mips__read_mips-3.snap
Normal file
50
objdiff-core/tests/snapshots/arch_mips__read_mips-3.snap
Normal 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)]
|
||||
1494
objdiff-core/tests/snapshots/arch_mips__read_mips.snap
Normal file
1494
objdiff-core/tests/snapshots/arch_mips__read_mips.snap
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,6 @@
|
||||
---
|
||||
source: objdiff-core/tests/arch_ppc.rs
|
||||
assertion_line: 70
|
||||
expression: sections_display
|
||||
---
|
||||
[
|
||||
@@ -46,7 +47,7 @@ expression: sections_display
|
||||
name: ".text",
|
||||
size: 3060,
|
||||
match_percent: Some(
|
||||
59.02353,
|
||||
58.662746,
|
||||
),
|
||||
symbols: [
|
||||
SectionDisplaySymbol {
|
||||
|
||||
@@ -4,6 +4,9 @@ expression: obj
|
||||
---
|
||||
Object {
|
||||
arch: ArchPpc {
|
||||
extensions: Extensions(
|
||||
2,
|
||||
),
|
||||
extab: Some(
|
||||
{
|
||||
10: ExceptionInfo {
|
||||
@@ -317,6 +320,9 @@ Object {
|
||||
552,
|
||||
),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
4,
|
||||
),
|
||||
relocations: [
|
||||
Relocation {
|
||||
flags: Elf(
|
||||
@@ -340,6 +346,9 @@ Object {
|
||||
40,
|
||||
),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
4,
|
||||
),
|
||||
relocations: [
|
||||
Relocation {
|
||||
flags: Elf(
|
||||
@@ -363,6 +372,9 @@ Object {
|
||||
36,
|
||||
),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
4,
|
||||
),
|
||||
relocations: [
|
||||
Relocation {
|
||||
flags: Elf(
|
||||
@@ -426,6 +438,9 @@ Object {
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
4,
|
||||
),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
@@ -440,6 +455,9 @@ Object {
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
4,
|
||||
),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
@@ -454,6 +472,9 @@ Object {
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
4,
|
||||
),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
@@ -468,6 +489,9 @@ Object {
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
4,
|
||||
),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
@@ -482,6 +506,9 @@ Object {
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
1,
|
||||
),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
@@ -496,6 +523,9 @@ Object {
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
1,
|
||||
),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
@@ -510,6 +540,9 @@ Object {
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
1,
|
||||
),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
@@ -518,4 +551,5 @@ Object {
|
||||
split_meta: None,
|
||||
path: None,
|
||||
timestamp: None,
|
||||
flow_analysis_results: {},
|
||||
}
|
||||
|
||||
@@ -8,7 +8,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 0,
|
||||
size: 4,
|
||||
opcode: 60,
|
||||
opcode: 283,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -21,7 +22,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 4,
|
||||
size: 4,
|
||||
opcode: 38,
|
||||
opcode: 260,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -34,7 +36,10 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 8,
|
||||
size: 4,
|
||||
opcode: 43,
|
||||
opcode: 265,
|
||||
branch_dest: Some(
|
||||
20,
|
||||
),
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -52,7 +57,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 12,
|
||||
size: 4,
|
||||
opcode: 41,
|
||||
opcode: 263,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -65,7 +71,10 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 16,
|
||||
size: 4,
|
||||
opcode: 45,
|
||||
opcode: 267,
|
||||
branch_dest: Some(
|
||||
32,
|
||||
),
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -83,7 +92,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 20,
|
||||
size: 4,
|
||||
opcode: 42,
|
||||
opcode: 264,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -103,7 +113,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 24,
|
||||
size: 4,
|
||||
opcode: 41,
|
||||
opcode: 263,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -116,7 +127,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 28,
|
||||
size: 4,
|
||||
opcode: 94,
|
||||
opcode: 323,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -129,7 +141,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 32,
|
||||
size: 4,
|
||||
opcode: 60,
|
||||
opcode: 283,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -149,7 +162,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 36,
|
||||
size: 4,
|
||||
opcode: 166,
|
||||
opcode: 445,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -162,7 +176,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 40,
|
||||
size: 4,
|
||||
opcode: 38,
|
||||
opcode: 260,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -175,7 +190,10 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 44,
|
||||
size: 4,
|
||||
opcode: 43,
|
||||
opcode: 265,
|
||||
branch_dest: Some(
|
||||
56,
|
||||
),
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -193,7 +211,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 48,
|
||||
size: 4,
|
||||
opcode: 41,
|
||||
opcode: 263,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -206,7 +225,10 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 52,
|
||||
size: 4,
|
||||
opcode: 45,
|
||||
opcode: 267,
|
||||
branch_dest: Some(
|
||||
68,
|
||||
),
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -224,7 +246,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 56,
|
||||
size: 4,
|
||||
opcode: 42,
|
||||
opcode: 264,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -244,7 +267,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 60,
|
||||
size: 4,
|
||||
opcode: 41,
|
||||
opcode: 263,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -257,7 +281,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 64,
|
||||
size: 4,
|
||||
opcode: 94,
|
||||
opcode: 323,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -270,7 +295,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 68,
|
||||
size: 4,
|
||||
opcode: 60,
|
||||
opcode: 283,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -290,7 +316,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 72,
|
||||
size: 4,
|
||||
opcode: 41,
|
||||
opcode: 263,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -303,7 +330,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 76,
|
||||
size: 4,
|
||||
opcode: 38,
|
||||
opcode: 260,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -316,7 +344,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 80,
|
||||
size: 4,
|
||||
opcode: 166,
|
||||
opcode: 445,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -329,7 +358,10 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 84,
|
||||
size: 4,
|
||||
opcode: 43,
|
||||
opcode: 265,
|
||||
branch_dest: Some(
|
||||
96,
|
||||
),
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -347,7 +379,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 88,
|
||||
size: 4,
|
||||
opcode: 41,
|
||||
opcode: 263,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -360,7 +393,10 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 92,
|
||||
size: 4,
|
||||
opcode: 45,
|
||||
opcode: 267,
|
||||
branch_dest: Some(
|
||||
108,
|
||||
),
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -378,7 +414,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 96,
|
||||
size: 4,
|
||||
opcode: 42,
|
||||
opcode: 264,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -398,7 +435,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 100,
|
||||
size: 4,
|
||||
opcode: 41,
|
||||
opcode: 263,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -411,7 +449,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 104,
|
||||
size: 4,
|
||||
opcode: 94,
|
||||
opcode: 323,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -424,7 +463,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 108,
|
||||
size: 4,
|
||||
opcode: 60,
|
||||
opcode: 283,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -444,7 +484,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 112,
|
||||
size: 4,
|
||||
opcode: 41,
|
||||
opcode: 263,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -457,7 +498,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 116,
|
||||
size: 4,
|
||||
opcode: 38,
|
||||
opcode: 260,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -470,7 +512,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 120,
|
||||
size: 4,
|
||||
opcode: 166,
|
||||
opcode: 445,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -483,7 +526,10 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 124,
|
||||
size: 4,
|
||||
opcode: 43,
|
||||
opcode: 265,
|
||||
branch_dest: Some(
|
||||
136,
|
||||
),
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -501,7 +547,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 128,
|
||||
size: 4,
|
||||
opcode: 41,
|
||||
opcode: 263,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -514,7 +561,10 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 132,
|
||||
size: 4,
|
||||
opcode: 45,
|
||||
opcode: 267,
|
||||
branch_dest: Some(
|
||||
148,
|
||||
),
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -532,7 +582,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 136,
|
||||
size: 4,
|
||||
opcode: 42,
|
||||
opcode: 264,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -552,7 +603,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 140,
|
||||
size: 4,
|
||||
opcode: 41,
|
||||
opcode: 263,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -565,7 +617,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 144,
|
||||
size: 4,
|
||||
opcode: 94,
|
||||
opcode: 323,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -578,7 +631,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 148,
|
||||
size: 4,
|
||||
opcode: 41,
|
||||
opcode: 263,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -598,7 +652,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 152,
|
||||
size: 4,
|
||||
opcode: 41,
|
||||
opcode: 263,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -611,7 +666,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 156,
|
||||
size: 4,
|
||||
opcode: 166,
|
||||
opcode: 445,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -624,7 +680,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 160,
|
||||
size: 4,
|
||||
opcode: 42,
|
||||
opcode: 264,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -637,7 +694,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 164,
|
||||
size: 4,
|
||||
opcode: 41,
|
||||
opcode: 263,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -650,7 +708,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 168,
|
||||
size: 4,
|
||||
opcode: 166,
|
||||
opcode: 445,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -663,7 +722,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 172,
|
||||
size: 4,
|
||||
opcode: 41,
|
||||
opcode: 263,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -676,7 +736,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 176,
|
||||
size: 4,
|
||||
opcode: 162,
|
||||
opcode: 441,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -689,7 +750,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 180,
|
||||
size: 4,
|
||||
opcode: 94,
|
||||
opcode: 323,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -702,7 +764,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 184,
|
||||
size: 4,
|
||||
opcode: 66,
|
||||
opcode: 289,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -715,7 +778,10 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 188,
|
||||
size: 4,
|
||||
opcode: 43,
|
||||
opcode: 265,
|
||||
branch_dest: Some(
|
||||
196,
|
||||
),
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -733,7 +799,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 192,
|
||||
size: 4,
|
||||
opcode: 166,
|
||||
opcode: 445,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -746,7 +813,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 196,
|
||||
size: 4,
|
||||
opcode: 163,
|
||||
opcode: 442,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -766,7 +834,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 200,
|
||||
size: 4,
|
||||
opcode: 94,
|
||||
opcode: 323,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -779,7 +848,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 204,
|
||||
size: 4,
|
||||
opcode: 66,
|
||||
opcode: 289,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -792,7 +862,10 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 208,
|
||||
size: 4,
|
||||
opcode: 43,
|
||||
opcode: 265,
|
||||
branch_dest: Some(
|
||||
216,
|
||||
),
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -810,7 +883,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 212,
|
||||
size: 4,
|
||||
opcode: 166,
|
||||
opcode: 445,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -823,7 +897,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 216,
|
||||
size: 4,
|
||||
opcode: 163,
|
||||
opcode: 442,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -843,7 +918,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 220,
|
||||
size: 4,
|
||||
opcode: 94,
|
||||
opcode: 323,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -856,7 +932,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 224,
|
||||
size: 4,
|
||||
opcode: 66,
|
||||
opcode: 289,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -869,7 +946,10 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 228,
|
||||
size: 4,
|
||||
opcode: 43,
|
||||
opcode: 265,
|
||||
branch_dest: Some(
|
||||
236,
|
||||
),
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -887,7 +967,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 232,
|
||||
size: 4,
|
||||
opcode: 166,
|
||||
opcode: 445,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -900,7 +981,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 236,
|
||||
size: 4,
|
||||
opcode: 163,
|
||||
opcode: 442,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -920,7 +1002,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 240,
|
||||
size: 4,
|
||||
opcode: 94,
|
||||
opcode: 323,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -933,7 +1016,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 244,
|
||||
size: 4,
|
||||
opcode: 66,
|
||||
opcode: 289,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -946,7 +1030,10 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 248,
|
||||
size: 4,
|
||||
opcode: 43,
|
||||
opcode: 265,
|
||||
branch_dest: Some(
|
||||
256,
|
||||
),
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -964,7 +1051,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 252,
|
||||
size: 4,
|
||||
opcode: 166,
|
||||
opcode: 445,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -977,7 +1065,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 256,
|
||||
size: 4,
|
||||
opcode: 41,
|
||||
opcode: 263,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -997,7 +1086,8 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 260,
|
||||
size: 4,
|
||||
opcode: 47,
|
||||
opcode: 269,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
|
||||
@@ -2,69 +2,69 @@
|
||||
source: objdiff-core/tests/arch_ppc.rs
|
||||
expression: output
|
||||
---
|
||||
[(Address(0), Normal, 5), (Spacing(4), Normal, 0), (Opcode("srwi", 60), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("24")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(4), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmpwi", 38), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(8), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 43), Normal, 10), (BranchDest(20), Normal, 0), (Basic(" ~>"), Rotating(0), 0), (Eol, Normal, 0)]
|
||||
[(Address(12), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 41), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(16), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 45), Normal, 10), (BranchDest(32), Normal, 0), (Basic(" ~>"), Rotating(1), 0), (Eol, Normal, 0)]
|
||||
[(Address(20), Normal, 5), (Basic(" ~> "), Rotating(0), 0), (Opcode("lis", 42), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@ha"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(24), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 41), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@l"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(28), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(32), Normal, 5), (Basic(" ~> "), Rotating(1), 0), (Opcode("extrwi", 60), Normal, 10), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("8")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("8")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(36), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 166), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), Bright, 0), (Basic("@sda21"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(40), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmpwi", 38), Normal, 10), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(44), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 43), Normal, 10), (BranchDest(56), Normal, 0), (Basic(" ~>"), Rotating(2), 0), (Eol, Normal, 0)]
|
||||
[(Address(48), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 41), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(52), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 45), Normal, 10), (BranchDest(68), Normal, 0), (Basic(" ~>"), Rotating(3), 0), (Eol, Normal, 0)]
|
||||
[(Address(56), Normal, 5), (Basic(" ~> "), Rotating(2), 0), (Opcode("lis", 42), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@ha"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(60), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 41), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@l"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(64), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(68), Normal, 5), (Basic(" ~> "), Rotating(3), 0), (Opcode("extrwi", 60), Normal, 10), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("8")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("16")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(72), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 41), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), Bright, 0), (Basic("@sda21"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(76), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmpwi", 38), Normal, 10), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(80), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 166), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(1)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(84), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 43), Normal, 10), (BranchDest(96), Normal, 0), (Basic(" ~>"), Rotating(4), 0), (Eol, Normal, 0)]
|
||||
[(Address(88), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 41), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(92), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 45), Normal, 10), (BranchDest(108), Normal, 0), (Basic(" ~>"), Rotating(5), 0), (Eol, Normal, 0)]
|
||||
[(Address(96), Normal, 5), (Basic(" ~> "), Rotating(4), 0), (Opcode("lis", 42), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@ha"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(100), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 41), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@l"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(104), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(108), Normal, 5), (Basic(" ~> "), Rotating(5), 0), (Opcode("clrlwi", 60), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("24")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(112), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 41), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), Bright, 0), (Basic("@sda21"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(116), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmpwi", 38), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(120), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 166), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(2)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(124), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 43), Normal, 10), (BranchDest(136), Normal, 0), (Basic(" ~>"), Rotating(6), 0), (Eol, Normal, 0)]
|
||||
[(Address(128), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 41), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(132), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 45), Normal, 10), (BranchDest(148), Normal, 0), (Basic(" ~>"), Rotating(7), 0), (Eol, Normal, 0)]
|
||||
[(Address(136), Normal, 5), (Basic(" ~> "), Rotating(6), 0), (Opcode("lis", 42), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@ha"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(140), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 41), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@l"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(144), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(148), Normal, 5), (Basic(" ~> "), Rotating(7), 0), (Opcode("li", 41), Normal, 10), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), Bright, 0), (Basic("@sda21"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(152), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 41), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(0)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(156), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 166), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(3)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(160), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lis", 42), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__ctype_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@ha"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(164), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 41), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__ctype_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@l"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(168), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 166), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(4)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(172), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 41), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(45)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(176), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbz", 162), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), Bright, 0), (Basic("@sda21"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(180), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(184), Normal, 5), (Spacing(4), Normal, 0), (Opcode("andi.", 66), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Unsigned(220)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(188), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 43), Normal, 10), (BranchDest(196), Normal, 0), (Basic(" ~>"), Rotating(8), 0), (Eol, Normal, 0)]
|
||||
[(Address(192), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 166), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(0)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(196), Normal, 5), (Basic(" ~> "), Rotating(8), 0), (Opcode("lbzu", 163), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(1)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(200), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(204), Normal, 5), (Spacing(4), Normal, 0), (Opcode("andi.", 66), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Unsigned(220)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(208), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 43), Normal, 10), (BranchDest(216), Normal, 0), (Basic(" ~>"), Rotating(9), 0), (Eol, Normal, 0)]
|
||||
[(Address(212), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 166), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(0)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(216), Normal, 5), (Basic(" ~> "), Rotating(9), 0), (Opcode("lbzu", 163), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(1)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(220), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(224), Normal, 5), (Spacing(4), Normal, 0), (Opcode("andi.", 66), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Unsigned(220)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(228), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 43), Normal, 10), (BranchDest(236), Normal, 0), (Basic(" ~>"), Rotating(10), 0), (Eol, Normal, 0)]
|
||||
[(Address(232), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 166), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(0)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(236), Normal, 5), (Basic(" ~> "), Rotating(10), 0), (Opcode("lbzu", 163), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(1)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(240), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(244), Normal, 5), (Spacing(4), Normal, 0), (Opcode("andi.", 66), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Unsigned(220)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(248), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 43), Normal, 10), (BranchDest(256), Normal, 0), (Basic(" ~>"), Rotating(11), 0), (Eol, Normal, 0)]
|
||||
[(Address(252), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 166), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(0)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(256), Normal, 5), (Basic(" ~> "), Rotating(11), 0), (Opcode("li", 41), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), Bright, 0), (Basic("@sda21"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(260), Normal, 5), (Spacing(4), Normal, 0), (Opcode("blr", 47), Normal, 10), (Eol, Normal, 0)]
|
||||
[(Address(0), Normal, 5), (Spacing(4), Normal, 0), (Opcode("srwi", 283), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("24")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(4), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmpwi", 260), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(8), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 265), Normal, 10), (BranchDest(20), Normal, 0), (Basic(" ~>"), Rotating(0), 0), (Eol, Normal, 0)]
|
||||
[(Address(12), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 263), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(16), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 267), Normal, 10), (BranchDest(32), Normal, 0), (Basic(" ~>"), Rotating(1), 0), (Eol, Normal, 0)]
|
||||
[(Address(20), Normal, 5), (Basic(" ~> "), Rotating(0), 0), (Opcode("lis", 264), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@ha"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(24), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@l"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(28), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 323), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(" <"), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic(">"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(32), Normal, 5), (Basic(" ~> "), Rotating(1), 0), (Opcode("extrwi", 283), Normal, 10), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("8")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("8")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(36), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 445), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), Bright, 0), (Basic("@sda21"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(40), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmpwi", 260), Normal, 10), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(44), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 265), Normal, 10), (BranchDest(56), Normal, 0), (Basic(" ~>"), Rotating(2), 0), (Eol, Normal, 0)]
|
||||
[(Address(48), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 263), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(52), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 267), Normal, 10), (BranchDest(68), Normal, 0), (Basic(" ~>"), Rotating(3), 0), (Eol, Normal, 0)]
|
||||
[(Address(56), Normal, 5), (Basic(" ~> "), Rotating(2), 0), (Opcode("lis", 264), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@ha"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(60), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@l"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(64), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 323), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(" <"), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic(">"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(68), Normal, 5), (Basic(" ~> "), Rotating(3), 0), (Opcode("extrwi", 283), Normal, 10), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("8")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("16")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(72), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 263), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), Bright, 0), (Basic("@sda21"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(76), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmpwi", 260), Normal, 10), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(80), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 445), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(1)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(84), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 265), Normal, 10), (BranchDest(96), Normal, 0), (Basic(" ~>"), Rotating(4), 0), (Eol, Normal, 0)]
|
||||
[(Address(88), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 263), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(92), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 267), Normal, 10), (BranchDest(108), Normal, 0), (Basic(" ~>"), Rotating(5), 0), (Eol, Normal, 0)]
|
||||
[(Address(96), Normal, 5), (Basic(" ~> "), Rotating(4), 0), (Opcode("lis", 264), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@ha"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(100), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@l"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(104), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 323), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(" <"), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic(">"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(108), Normal, 5), (Basic(" ~> "), Rotating(5), 0), (Opcode("clrlwi", 283), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("24")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(112), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 263), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), Bright, 0), (Basic("@sda21"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(116), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmpwi", 260), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(120), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 445), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(2)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(124), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 265), Normal, 10), (BranchDest(136), Normal, 0), (Basic(" ~>"), Rotating(6), 0), (Eol, Normal, 0)]
|
||||
[(Address(128), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 263), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(132), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 267), Normal, 10), (BranchDest(148), Normal, 0), (Basic(" ~>"), Rotating(7), 0), (Eol, Normal, 0)]
|
||||
[(Address(136), Normal, 5), (Basic(" ~> "), Rotating(6), 0), (Opcode("lis", 264), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@ha"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(140), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@l"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(144), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 323), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(" <"), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic(">"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(148), Normal, 5), (Basic(" ~> "), Rotating(7), 0), (Opcode("li", 263), Normal, 10), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), Bright, 0), (Basic("@sda21"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(152), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 263), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(0)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(156), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 445), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(3)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(160), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lis", 264), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__ctype_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@ha"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(164), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__ctype_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@l"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(168), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 445), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(4)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(172), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 263), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(45)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(176), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbz", 441), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), Bright, 0), (Basic("@sda21"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(180), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 323), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(" <"), Normal, 0), (Symbol(Symbol { name: "__ctype_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic(">"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(184), Normal, 5), (Spacing(4), Normal, 0), (Opcode("andi.", 289), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Unsigned(220)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(188), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 265), Normal, 10), (BranchDest(196), Normal, 0), (Basic(" ~>"), Rotating(8), 0), (Eol, Normal, 0)]
|
||||
[(Address(192), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 445), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(0)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(196), Normal, 5), (Basic(" ~> "), Rotating(8), 0), (Opcode("lbzu", 442), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(1)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(200), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 323), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(" <"), Normal, 0), (Symbol(Symbol { name: "__ctype_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic(">"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(204), Normal, 5), (Spacing(4), Normal, 0), (Opcode("andi.", 289), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Unsigned(220)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(208), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 265), Normal, 10), (BranchDest(216), Normal, 0), (Basic(" ~>"), Rotating(9), 0), (Eol, Normal, 0)]
|
||||
[(Address(212), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 445), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(0)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(216), Normal, 5), (Basic(" ~> "), Rotating(9), 0), (Opcode("lbzu", 442), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(1)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(220), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 323), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(" <"), Normal, 0), (Symbol(Symbol { name: "__ctype_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic(">"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(224), Normal, 5), (Spacing(4), Normal, 0), (Opcode("andi.", 289), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Unsigned(220)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(228), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 265), Normal, 10), (BranchDest(236), Normal, 0), (Basic(" ~>"), Rotating(10), 0), (Eol, Normal, 0)]
|
||||
[(Address(232), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 445), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(0)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(236), Normal, 5), (Basic(" ~> "), Rotating(10), 0), (Opcode("lbzu", 442), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(1)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(240), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 323), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(" <"), Normal, 0), (Symbol(Symbol { name: "__ctype_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic(">"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(244), Normal, 5), (Spacing(4), Normal, 0), (Opcode("andi.", 289), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Unsigned(220)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(248), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 265), Normal, 10), (BranchDest(256), Normal, 0), (Basic(" ~>"), Rotating(11), 0), (Eol, Normal, 0)]
|
||||
[(Address(252), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 445), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(0)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(256), Normal, 5), (Basic(" ~> "), Rotating(11), 0), (Opcode("li", 263), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), Bright, 0), (Basic("@sda21"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(260), Normal, 5), (Spacing(4), Normal, 0), (Opcode("blr", 269), Normal, 10), (Eol, Normal, 0)]
|
||||
|
||||
@@ -4,6 +4,9 @@ expression: obj
|
||||
---
|
||||
Object {
|
||||
arch: ArchPpc {
|
||||
extensions: Extensions(
|
||||
2,
|
||||
),
|
||||
extab: None,
|
||||
},
|
||||
endianness: Big,
|
||||
@@ -166,6 +169,9 @@ Object {
|
||||
284,
|
||||
),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
4,
|
||||
),
|
||||
relocations: [
|
||||
Relocation {
|
||||
flags: Elf(
|
||||
@@ -183,6 +189,14 @@ Object {
|
||||
target_symbol: 8,
|
||||
addend: 0,
|
||||
},
|
||||
Relocation {
|
||||
flags: Elf(
|
||||
0,
|
||||
),
|
||||
address: 28,
|
||||
target_symbol: 8,
|
||||
addend: 0,
|
||||
},
|
||||
Relocation {
|
||||
flags: Elf(
|
||||
109,
|
||||
@@ -207,6 +221,14 @@ Object {
|
||||
target_symbol: 8,
|
||||
addend: 0,
|
||||
},
|
||||
Relocation {
|
||||
flags: Elf(
|
||||
0,
|
||||
),
|
||||
address: 64,
|
||||
target_symbol: 8,
|
||||
addend: 0,
|
||||
},
|
||||
Relocation {
|
||||
flags: Elf(
|
||||
109,
|
||||
@@ -231,6 +253,14 @@ Object {
|
||||
target_symbol: 8,
|
||||
addend: 0,
|
||||
},
|
||||
Relocation {
|
||||
flags: Elf(
|
||||
0,
|
||||
),
|
||||
address: 104,
|
||||
target_symbol: 8,
|
||||
addend: 0,
|
||||
},
|
||||
Relocation {
|
||||
flags: Elf(
|
||||
109,
|
||||
@@ -255,6 +285,14 @@ Object {
|
||||
target_symbol: 8,
|
||||
addend: 0,
|
||||
},
|
||||
Relocation {
|
||||
flags: Elf(
|
||||
0,
|
||||
),
|
||||
address: 144,
|
||||
target_symbol: 8,
|
||||
addend: 0,
|
||||
},
|
||||
Relocation {
|
||||
flags: Elf(
|
||||
109,
|
||||
@@ -287,6 +325,38 @@ Object {
|
||||
target_symbol: 5,
|
||||
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 {
|
||||
flags: Elf(
|
||||
109,
|
||||
@@ -327,6 +397,9 @@ Object {
|
||||
4,
|
||||
),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
4,
|
||||
),
|
||||
relocations: [
|
||||
Relocation {
|
||||
flags: Elf(
|
||||
@@ -352,6 +425,9 @@ Object {
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
8,
|
||||
),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: Some(
|
||||
@@ -368,6 +444,9 @@ Object {
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
4,
|
||||
),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
@@ -382,6 +461,9 @@ Object {
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
4,
|
||||
),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
@@ -396,6 +478,9 @@ Object {
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
4,
|
||||
),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
@@ -410,6 +495,9 @@ Object {
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
1,
|
||||
),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
@@ -424,6 +512,9 @@ Object {
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
1,
|
||||
),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
@@ -438,6 +529,9 @@ Object {
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
1,
|
||||
),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
@@ -452,6 +546,9 @@ Object {
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
4,
|
||||
),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
@@ -487,4 +584,5 @@ Object {
|
||||
),
|
||||
path: None,
|
||||
timestamp: None,
|
||||
flow_analysis_results: {},
|
||||
}
|
||||
|
||||
1826
objdiff-core/tests/snapshots/arch_ppc__read_vmx128_coff-2.snap
Normal file
1826
objdiff-core/tests/snapshots/arch_ppc__read_vmx128_coff-2.snap
Normal file
File diff suppressed because it is too large
Load Diff
134
objdiff-core/tests/snapshots/arch_ppc__read_vmx128_coff-3.snap
Normal file
134
objdiff-core/tests/snapshots/arch_ppc__read_vmx128_coff-3.snap
Normal file
@@ -0,0 +1,134 @@
|
||||
---
|
||||
source: objdiff-core/tests/arch_ppc.rs
|
||||
expression: output
|
||||
---
|
||||
[(Address(0), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mflr", 342), Normal, 10), (Argument(Opaque("r12")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(4), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stw", 443), Normal, 10), (Argument(Opaque("r12")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-8)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(8), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stwu", 444), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-336)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(12), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lis", 264), Normal, 10), (Argument(Opaque("r11")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__real@3f800000", demangled_name: None, address: 388, size: 4, kind: Object, section: Some(4), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@h"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(16), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lfs", 455), Normal, 10), (Argument(Opaque("f0")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__real@3f800000", demangled_name: None, address: 388, size: 4, kind: Object, section: Some(4), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@l"), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r11")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(20), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stfs", 459), Normal, 10), (Argument(Opaque("f0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(272)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(24), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lis", 264), Normal, 10), (Argument(Opaque("r10")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__real@40000000", demangled_name: None, address: 384, size: 4, kind: Object, section: Some(4), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@h"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(28), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lfs", 455), Normal, 10), (Argument(Opaque("f13")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__real@40000000", demangled_name: None, address: 384, size: 4, kind: Object, section: Some(4), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@l"), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r10")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(32), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stfs", 459), Normal, 10), (Argument(Opaque("f13")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(276)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(36), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lis", 264), Normal, 10), (Argument(Opaque("r9")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__real@40400000", demangled_name: None, address: 380, size: 4, kind: Object, section: Some(4), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@h"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(40), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lfs", 455), Normal, 10), (Argument(Opaque("f12")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__real@40400000", demangled_name: None, address: 380, size: 4, kind: Object, section: Some(4), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@l"), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r9")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(44), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stfs", 459), Normal, 10), (Argument(Opaque("f12")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(280)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(48), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lis", 264), Normal, 10), (Argument(Opaque("r8")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__real@40800000", demangled_name: None, address: 376, size: 4, kind: Object, section: Some(4), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@h"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(52), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lfs", 455), Normal, 10), (Argument(Opaque("f11")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__real@40800000", demangled_name: None, address: 376, size: 4, kind: Object, section: Some(4), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@l"), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r8")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(56), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stfs", 459), Normal, 10), (Argument(Opaque("f11")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(284)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(60), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lis", 264), Normal, 10), (Argument(Opaque("r7")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__real@40a00000", demangled_name: None, address: 372, size: 4, kind: Object, section: Some(4), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@h"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(64), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lfs", 455), Normal, 10), (Argument(Opaque("f10")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__real@40a00000", demangled_name: None, address: 372, size: 4, kind: Object, section: Some(4), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@l"), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r7")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(68), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stfs", 459), Normal, 10), (Argument(Opaque("f10")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(256)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(72), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lis", 264), Normal, 10), (Argument(Opaque("r6")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__real@40c00000", demangled_name: None, address: 368, size: 4, kind: Object, section: Some(4), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@h"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(76), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lfs", 455), Normal, 10), (Argument(Opaque("f9")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__real@40c00000", demangled_name: None, address: 368, size: 4, kind: Object, section: Some(4), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@l"), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r6")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(80), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stfs", 459), Normal, 10), (Argument(Opaque("f9")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(260)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(84), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lis", 264), Normal, 10), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__real@40e00000", demangled_name: None, address: 364, size: 4, kind: Object, section: Some(4), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@h"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(88), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lfs", 455), Normal, 10), (Argument(Opaque("f8")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__real@40e00000", demangled_name: None, address: 364, size: 4, kind: Object, section: Some(4), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@l"), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(92), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stfs", 459), Normal, 10), (Argument(Opaque("f8")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(264)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(96), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lis", 264), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__real@41000000", demangled_name: None, address: 360, size: 4, kind: Object, section: Some(4), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@h"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(100), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lfs", 455), Normal, 10), (Argument(Opaque("f7")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__real@41000000", demangled_name: None, address: 360, size: 4, kind: Object, section: Some(4), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@l"), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(104), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stfs", 459), Normal, 10), (Argument(Opaque("f7")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(268)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(108), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lis", 264), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__real@3f000000", demangled_name: None, address: 356, size: 4, kind: Object, section: Some(4), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@h"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(112), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lfs", 455), Normal, 10), (Argument(Opaque("f6")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__real@3f000000", demangled_name: None, address: 356, size: 4, kind: Object, section: Some(4), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@l"), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(116), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stfs", 459), Normal, 10), (Argument(Opaque("f6")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(224)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(120), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lis", 264), Normal, 10), (Argument(Opaque("r11")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__real@3f000000", demangled_name: None, address: 356, size: 4, kind: Object, section: Some(4), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@h"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(124), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lfs", 455), Normal, 10), (Argument(Opaque("f5")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__real@3f000000", demangled_name: None, address: 356, size: 4, kind: Object, section: Some(4), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@l"), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r11")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(128), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stfs", 459), Normal, 10), (Argument(Opaque("f5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(228)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(132), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lis", 264), Normal, 10), (Argument(Opaque("r10")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__real@3f000000", demangled_name: None, address: 356, size: 4, kind: Object, section: Some(4), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@h"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(136), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lfs", 455), Normal, 10), (Argument(Opaque("f4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__real@3f000000", demangled_name: None, address: 356, size: 4, kind: Object, section: Some(4), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@l"), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r10")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(140), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stfs", 459), Normal, 10), (Argument(Opaque("f4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(232)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(144), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lis", 264), Normal, 10), (Argument(Opaque("r9")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__real@3f000000", demangled_name: None, address: 356, size: 4, kind: Object, section: Some(4), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@h"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(148), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lfs", 455), Normal, 10), (Argument(Opaque("f3")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__real@3f000000", demangled_name: None, address: 356, size: 4, kind: Object, section: Some(4), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@l"), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r9")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(152), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stfs", 459), Normal, 10), (Argument(Opaque("f3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(236)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(156), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 263), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(0)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(160), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(272)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(164), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 267), Normal, 10), (Symbol(Symbol { name: "__lvx", demangled_name: None, address: 640, size: 24, kind: Function, section: Some(5), flags: FlagSet(Local | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
|
||||
[(Address(168), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r8")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(80)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(172), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stvx128", 194), Normal, 10), (Argument(Opaque("v1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r8")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(176), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r7")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(80)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(180), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lvx128", 187), Normal, 10), (Argument(Opaque("v0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r7")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(184), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r6")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(240)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(188), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stvx128", 194), Normal, 10), (Argument(Opaque("v0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r6")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(192), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 263), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(0)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(196), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(256)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(200), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 267), Normal, 10), (Symbol(Symbol { name: "__lvx", demangled_name: None, address: 640, size: 24, kind: Function, section: Some(5), flags: FlagSet(Local | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
|
||||
[(Address(204), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(96)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(208), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stvx128", 194), Normal, 10), (Argument(Opaque("v1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(212), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(96)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(216), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lvx128", 187), Normal, 10), (Argument(Opaque("v13")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(220), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(208)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(224), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stvx128", 194), Normal, 10), (Argument(Opaque("v13")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(228), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 263), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(0)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(232), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(224)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(236), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 267), Normal, 10), (Symbol(Symbol { name: "__lvx", demangled_name: None, address: 640, size: 24, kind: Function, section: Some(5), flags: FlagSet(Local | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
|
||||
[(Address(240), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r11")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(112)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(244), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stvx128", 194), Normal, 10), (Argument(Opaque("v1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r11")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(248), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r10")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(112)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(252), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lvx128", 187), Normal, 10), (Argument(Opaque("v12")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r10")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(256), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r9")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(304)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(260), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stvx128", 194), Normal, 10), (Argument(Opaque("v12")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r9")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(264), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r8")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(304)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(268), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lvx128", 187), Normal, 10), (Argument(Opaque("v11")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r8")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(272), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r7")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(208)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(276), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lvx128", 187), Normal, 10), (Argument(Opaque("v10")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r7")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(280), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r6")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(240)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(284), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lvx128", 187), Normal, 10), (Argument(Opaque("v9")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r6")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(288), Normal, 5), (Spacing(4), Normal, 0), (Opcode("vmaddfp", 76), Normal, 10), (Argument(Opaque("v8")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("v9")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("v10")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("v11")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(292), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(128)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(296), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stvx128", 194), Normal, 10), (Argument(Opaque("v8")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(300), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(128)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(304), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lvx128", 187), Normal, 10), (Argument(Opaque("v7")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(308), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(192)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(312), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stvx128", 194), Normal, 10), (Argument(Opaque("v7")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(316), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r11")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(208)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(320), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lvx128", 187), Normal, 10), (Argument(Opaque("v6")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r11")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(324), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r10")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(240)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(328), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lvx128", 187), Normal, 10), (Argument(Opaque("v5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r10")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(332), Normal, 5), (Spacing(4), Normal, 0), (Opcode("vmsum4fp128", 203), Normal, 10), (Argument(Opaque("v4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("v5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("v6")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(336), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r9")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(144)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(340), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stvx128", 194), Normal, 10), (Argument(Opaque("v4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r9")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(344), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r8")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(144)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(348), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lvx128", 187), Normal, 10), (Argument(Opaque("v3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r8")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(352), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r7")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(176)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(356), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stvx128", 194), Normal, 10), (Argument(Opaque("v3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r7")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(360), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 263), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(0)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(364), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(288)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(368), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r6")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(192)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(372), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lvx128", 187), Normal, 10), (Argument(Opaque("v1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r6")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(376), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 267), Normal, 10), (Symbol(Symbol { name: "__stvx", demangled_name: None, address: 664, size: 40, kind: Function, section: Some(5), flags: FlagSet(Local | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
|
||||
[(Address(380), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 263), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(0)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(384), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(160)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(388), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(176)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(392), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lvx128", 187), Normal, 10), (Argument(Opaque("v1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(396), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 267), Normal, 10), (Symbol(Symbol { name: "__stvx", demangled_name: None, address: 664, size: 40, kind: Function, section: Some(5), flags: FlagSet(Local | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
|
||||
[(Address(400), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lis", 264), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "$SG4433", demangled_name: None, address: 32, size: 32, kind: Object, section: Some(4), flags: FlagSet(Local | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@h"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(404), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "$SG4433", demangled_name: None, address: 32, size: 32, kind: Object, section: Some(4), flags: FlagSet(Local | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@l"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(408), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 267), Normal, 10), (Symbol(Symbol { name: "printf", demangled_name: None, address: 0, size: 0, kind: Function, section: None, flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
|
||||
[(Address(412), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(240)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(416), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lvx128", 187), Normal, 10), (Argument(Opaque("v1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(420), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lis", 264), Normal, 10), (Argument(Opaque("r11")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "$SG4434", demangled_name: None, address: 64, size: 8, kind: Object, section: Some(4), flags: FlagSet(Local | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@h"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(424), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r11")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "$SG4434", demangled_name: None, address: 64, size: 8, kind: Object, section: Some(4), flags: FlagSet(Local | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@l"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(428), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 267), Normal, 10), (Symbol(Symbol { name: "?PrintVector@@YAXPBDU__vector4@@@Z", demangled_name: Some("void __cdecl PrintVector(char const *, struct __vector4)"), address: 0, size: 120, kind: Function, section: Some(5), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
|
||||
[(Address(432), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r10")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(208)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(436), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lvx128", 187), Normal, 10), (Argument(Opaque("v1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r10")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(440), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lis", 264), Normal, 10), (Argument(Opaque("r9")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "$SG4435", demangled_name: None, address: 72, size: 8, kind: Object, section: Some(4), flags: FlagSet(Local | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@h"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(444), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r9")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "$SG4435", demangled_name: None, address: 72, size: 8, kind: Object, section: Some(4), flags: FlagSet(Local | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@l"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(448), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 267), Normal, 10), (Symbol(Symbol { name: "?PrintVector@@YAXPBDU__vector4@@@Z", demangled_name: Some("void __cdecl PrintVector(char const *, struct __vector4)"), address: 0, size: 120, kind: Function, section: Some(5), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
|
||||
[(Address(452), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r8")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(192)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(456), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lvx128", 187), Normal, 10), (Argument(Opaque("v1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r8")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(460), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lis", 264), Normal, 10), (Argument(Opaque("r7")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "$SG4436", demangled_name: None, address: 80, size: 12, kind: Object, section: Some(4), flags: FlagSet(Local | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@h"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(464), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r7")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "$SG4436", demangled_name: None, address: 80, size: 12, kind: Object, section: Some(4), flags: FlagSet(Local | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@l"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(468), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 267), Normal, 10), (Symbol(Symbol { name: "?PrintVector@@YAXPBDU__vector4@@@Z", demangled_name: Some("void __cdecl PrintVector(char const *, struct __vector4)"), address: 0, size: 120, kind: Function, section: Some(5), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
|
||||
[(Address(472), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r6")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(176)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(476), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lvx128", 187), Normal, 10), (Argument(Opaque("v1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r6")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(480), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lis", 264), Normal, 10), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "$SG4437", demangled_name: None, address: 92, size: 12, kind: Object, section: Some(4), flags: FlagSet(Local | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@h"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(484), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "$SG4437", demangled_name: None, address: 92, size: 12, kind: Object, section: Some(4), flags: FlagSet(Local | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@l"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(488), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 267), Normal, 10), (Symbol(Symbol { name: "?PrintVector@@YAXPBDU__vector4@@@Z", demangled_name: Some("void __cdecl PrintVector(char const *, struct __vector4)"), address: 0, size: 120, kind: Function, section: Some(5), flags: FlagSet(Global | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
|
||||
[(Address(492), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lis", 264), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "$SG4438", demangled_name: None, address: 104, size: 4, kind: Object, section: Some(4), flags: FlagSet(Local | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@h"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(496), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "$SG4438", demangled_name: None, address: 104, size: 4, kind: Object, section: Some(4), flags: FlagSet(Local | SizeInferred), align: None, virtual_address: None }), Bright, 0), (Basic("@l"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(500), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bl", 267), Normal, 10), (Symbol(Symbol { name: "printf", demangled_name: None, address: 0, size: 0, kind: Function, section: None, flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
|
||||
[(Address(504), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 263), Normal, 10), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(336)), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(508), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lwz", 439), Normal, 10), (Argument(Opaque("r12")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-8)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r1")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(512), Normal, 5), (Spacing(4), Normal, 0), (Opcode("mtlr", 348), Normal, 10), (Argument(Opaque("r12")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Address(516), Normal, 5), (Spacing(4), Normal, 0), (Opcode("blr", 269), Normal, 10), (Eol, Normal, 0)]
|
||||
2442
objdiff-core/tests/snapshots/arch_ppc__read_vmx128_coff.snap
Normal file
2442
objdiff-core/tests/snapshots/arch_ppc__read_vmx128_coff.snap
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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,
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
@@ -9,6 +9,7 @@ expression: diff.instruction_rows
|
||||
address: 0,
|
||||
size: 1,
|
||||
opcode: 640,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -22,6 +23,7 @@ expression: diff.instruction_rows
|
||||
address: 1,
|
||||
size: 2,
|
||||
opcode: 414,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -35,6 +37,7 @@ expression: diff.instruction_rows
|
||||
address: 3,
|
||||
size: 5,
|
||||
opcode: 640,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -48,6 +51,7 @@ expression: diff.instruction_rows
|
||||
address: 8,
|
||||
size: 5,
|
||||
opcode: 59,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -61,6 +65,7 @@ expression: diff.instruction_rows
|
||||
address: 13,
|
||||
size: 3,
|
||||
opcode: 7,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -74,6 +79,7 @@ expression: diff.instruction_rows
|
||||
address: 16,
|
||||
size: 1,
|
||||
opcode: 590,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
@@ -87,6 +93,7 @@ expression: diff.instruction_rows
|
||||
address: 17,
|
||||
size: 1,
|
||||
opcode: 662,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
|
||||
@@ -4,7 +4,7 @@ expression: obj
|
||||
---
|
||||
Object {
|
||||
arch: ArchX86 {
|
||||
bits: 32,
|
||||
arch: X86,
|
||||
endianness: Little,
|
||||
},
|
||||
endianness: Little,
|
||||
@@ -136,6 +136,9 @@ Object {
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
1,
|
||||
),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
@@ -150,6 +153,9 @@ Object {
|
||||
10,
|
||||
),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
4,
|
||||
),
|
||||
relocations: [
|
||||
Relocation {
|
||||
flags: Coff(
|
||||
@@ -173,6 +179,9 @@ Object {
|
||||
18,
|
||||
),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
16,
|
||||
),
|
||||
relocations: [
|
||||
Relocation {
|
||||
flags: Coff(
|
||||
@@ -198,4 +207,5 @@ Object {
|
||||
split_meta: None,
|
||||
path: None,
|
||||
timestamp: None,
|
||||
flow_analysis_results: {},
|
||||
}
|
||||
|
||||
300
objdiff-core/tests/snapshots/arch_x86__read_x86_64-2.snap
Normal file
300
objdiff-core/tests/snapshots/arch_x86__read_x86_64-2.snap
Normal file
@@ -0,0 +1,300 @@
|
||||
---
|
||||
source: objdiff-core/tests/arch_x86.rs
|
||||
expression: diff.instruction_rows
|
||||
---
|
||||
[
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 0,
|
||||
size: 5,
|
||||
opcode: 414,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 5,
|
||||
size: 5,
|
||||
opcode: 414,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 10,
|
||||
size: 1,
|
||||
opcode: 640,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 11,
|
||||
size: 4,
|
||||
opcode: 740,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 15,
|
||||
size: 5,
|
||||
opcode: 414,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 20,
|
||||
size: 5,
|
||||
opcode: 414,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 25,
|
||||
size: 4,
|
||||
opcode: 448,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 29,
|
||||
size: 4,
|
||||
opcode: 460,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 33,
|
||||
size: 5,
|
||||
opcode: 414,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 38,
|
||||
size: 5,
|
||||
opcode: 414,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 43,
|
||||
size: 5,
|
||||
opcode: 448,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 48,
|
||||
size: 5,
|
||||
opcode: 460,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 53,
|
||||
size: 4,
|
||||
opcode: 11,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 57,
|
||||
size: 5,
|
||||
opcode: 414,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 62,
|
||||
size: 5,
|
||||
opcode: 414,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 67,
|
||||
size: 5,
|
||||
opcode: 448,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 72,
|
||||
size: 5,
|
||||
opcode: 460,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 77,
|
||||
size: 4,
|
||||
opcode: 11,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 81,
|
||||
size: 4,
|
||||
opcode: 7,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 85,
|
||||
size: 1,
|
||||
opcode: 590,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
InstructionDiffRow {
|
||||
ins_ref: Some(
|
||||
InstructionRef {
|
||||
address: 86,
|
||||
size: 1,
|
||||
opcode: 662,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
branch_from: None,
|
||||
branch_to: None,
|
||||
arg_diff: [],
|
||||
},
|
||||
]
|
||||
25
objdiff-core/tests/snapshots/arch_x86__read_x86_64-3.snap
Normal file
25
objdiff-core/tests/snapshots/arch_x86__read_x86_64-3.snap
Normal 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)]
|
||||
1578
objdiff-core/tests/snapshots/arch_x86__read_x86_64.snap
Normal file
1578
objdiff-core/tests/snapshots/arch_x86__read_x86_64.snap
Normal file
File diff suppressed because it is too large
Load Diff
@@ -13,6 +13,9 @@ expression: obj.sections
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
1,
|
||||
),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
@@ -27,6 +30,9 @@ expression: obj.sections
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
1,
|
||||
),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
@@ -36,11 +42,14 @@ expression: obj.sections
|
||||
name: ".rdata",
|
||||
address: 0,
|
||||
size: 0,
|
||||
kind: Data,
|
||||
kind: Unknown,
|
||||
data: SectionData(
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(Hidden),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
4,
|
||||
),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
@@ -50,11 +59,14 @@ expression: obj.sections
|
||||
name: ".rdata",
|
||||
address: 0,
|
||||
size: 0,
|
||||
kind: Data,
|
||||
kind: Unknown,
|
||||
data: SectionData(
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(Hidden),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
4,
|
||||
),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
@@ -64,11 +76,14 @@ expression: obj.sections
|
||||
name: ".text$mn",
|
||||
address: 0,
|
||||
size: 0,
|
||||
kind: Code,
|
||||
kind: Unknown,
|
||||
data: SectionData(
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(Hidden),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
16,
|
||||
),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
@@ -83,6 +98,9 @@ expression: obj.sections
|
||||
56,
|
||||
),
|
||||
flags: FlagSet(Combined),
|
||||
align: Some(
|
||||
4,
|
||||
),
|
||||
relocations: [
|
||||
Relocation {
|
||||
flags: Coff(
|
||||
@@ -132,12 +150,15 @@ expression: obj.sections
|
||||
id: ".rdata-combined",
|
||||
name: ".rdata",
|
||||
address: 0,
|
||||
size: 295,
|
||||
size: 304,
|
||||
kind: Data,
|
||||
data: SectionData(
|
||||
295,
|
||||
304,
|
||||
),
|
||||
flags: FlagSet(Combined),
|
||||
align: Some(
|
||||
4,
|
||||
),
|
||||
relocations: [
|
||||
Relocation {
|
||||
flags: Coff(
|
||||
@@ -159,7 +180,7 @@ expression: obj.sections
|
||||
flags: Coff(
|
||||
6,
|
||||
),
|
||||
address: 21,
|
||||
address: 24,
|
||||
target_symbol: 13,
|
||||
addend: 0,
|
||||
},
|
||||
@@ -167,7 +188,7 @@ expression: obj.sections
|
||||
flags: Coff(
|
||||
6,
|
||||
),
|
||||
address: 45,
|
||||
address: 48,
|
||||
target_symbol: 15,
|
||||
addend: 0,
|
||||
},
|
||||
@@ -175,7 +196,7 @@ expression: obj.sections
|
||||
flags: Coff(
|
||||
6,
|
||||
),
|
||||
address: 61,
|
||||
address: 64,
|
||||
target_symbol: 25,
|
||||
addend: 0,
|
||||
},
|
||||
@@ -183,7 +204,7 @@ expression: obj.sections
|
||||
flags: Coff(
|
||||
6,
|
||||
),
|
||||
address: 65,
|
||||
address: 68,
|
||||
target_symbol: 27,
|
||||
addend: 0,
|
||||
},
|
||||
@@ -191,7 +212,7 @@ expression: obj.sections
|
||||
flags: Coff(
|
||||
6,
|
||||
),
|
||||
address: 70,
|
||||
address: 76,
|
||||
target_symbol: 21,
|
||||
addend: 0,
|
||||
},
|
||||
@@ -199,7 +220,7 @@ expression: obj.sections
|
||||
flags: Coff(
|
||||
6,
|
||||
),
|
||||
address: 94,
|
||||
address: 100,
|
||||
target_symbol: 23,
|
||||
addend: 0,
|
||||
},
|
||||
@@ -207,7 +228,7 @@ expression: obj.sections
|
||||
flags: Coff(
|
||||
6,
|
||||
),
|
||||
address: 110,
|
||||
address: 116,
|
||||
target_symbol: 31,
|
||||
addend: 0,
|
||||
},
|
||||
@@ -215,7 +236,7 @@ expression: obj.sections
|
||||
flags: Coff(
|
||||
6,
|
||||
),
|
||||
address: 114,
|
||||
address: 120,
|
||||
target_symbol: 33,
|
||||
addend: 0,
|
||||
},
|
||||
@@ -223,7 +244,7 @@ expression: obj.sections
|
||||
flags: Coff(
|
||||
6,
|
||||
),
|
||||
address: 130,
|
||||
address: 136,
|
||||
target_symbol: 35,
|
||||
addend: 0,
|
||||
},
|
||||
@@ -231,7 +252,7 @@ expression: obj.sections
|
||||
flags: Coff(
|
||||
6,
|
||||
),
|
||||
address: 134,
|
||||
address: 140,
|
||||
target_symbol: 37,
|
||||
addend: 0,
|
||||
},
|
||||
@@ -239,7 +260,7 @@ expression: obj.sections
|
||||
flags: Coff(
|
||||
6,
|
||||
),
|
||||
address: 138,
|
||||
address: 144,
|
||||
target_symbol: 19,
|
||||
addend: 0,
|
||||
},
|
||||
@@ -247,7 +268,7 @@ expression: obj.sections
|
||||
flags: Coff(
|
||||
6,
|
||||
),
|
||||
address: 142,
|
||||
address: 148,
|
||||
target_symbol: 39,
|
||||
addend: 0,
|
||||
},
|
||||
@@ -255,7 +276,7 @@ expression: obj.sections
|
||||
flags: Coff(
|
||||
6,
|
||||
),
|
||||
address: 147,
|
||||
address: 156,
|
||||
target_symbol: 31,
|
||||
addend: 0,
|
||||
},
|
||||
@@ -263,7 +284,7 @@ expression: obj.sections
|
||||
flags: Coff(
|
||||
6,
|
||||
),
|
||||
address: 171,
|
||||
address: 180,
|
||||
target_symbol: 33,
|
||||
addend: 0,
|
||||
},
|
||||
@@ -271,7 +292,7 @@ expression: obj.sections
|
||||
flags: Coff(
|
||||
6,
|
||||
),
|
||||
address: 175,
|
||||
address: 184,
|
||||
target_symbol: 21,
|
||||
addend: 0,
|
||||
},
|
||||
@@ -279,7 +300,7 @@ expression: obj.sections
|
||||
flags: Coff(
|
||||
6,
|
||||
),
|
||||
address: 199,
|
||||
address: 208,
|
||||
target_symbol: 23,
|
||||
addend: 0,
|
||||
},
|
||||
@@ -287,7 +308,7 @@ expression: obj.sections
|
||||
flags: Coff(
|
||||
6,
|
||||
),
|
||||
address: 215,
|
||||
address: 224,
|
||||
target_symbol: 31,
|
||||
addend: 0,
|
||||
},
|
||||
@@ -295,7 +316,7 @@ expression: obj.sections
|
||||
flags: Coff(
|
||||
6,
|
||||
),
|
||||
address: 219,
|
||||
address: 228,
|
||||
target_symbol: 33,
|
||||
addend: 0,
|
||||
},
|
||||
@@ -303,7 +324,7 @@ expression: obj.sections
|
||||
flags: Coff(
|
||||
6,
|
||||
),
|
||||
address: 235,
|
||||
address: 244,
|
||||
target_symbol: 13,
|
||||
addend: 0,
|
||||
},
|
||||
@@ -311,7 +332,7 @@ expression: obj.sections
|
||||
flags: Coff(
|
||||
6,
|
||||
),
|
||||
address: 239,
|
||||
address: 248,
|
||||
target_symbol: 15,
|
||||
addend: 0,
|
||||
},
|
||||
@@ -319,7 +340,7 @@ expression: obj.sections
|
||||
flags: Coff(
|
||||
6,
|
||||
),
|
||||
address: 255,
|
||||
address: 264,
|
||||
target_symbol: 21,
|
||||
addend: 0,
|
||||
},
|
||||
@@ -327,7 +348,7 @@ expression: obj.sections
|
||||
flags: Coff(
|
||||
6,
|
||||
),
|
||||
address: 259,
|
||||
address: 268,
|
||||
target_symbol: 23,
|
||||
addend: 0,
|
||||
},
|
||||
@@ -335,7 +356,7 @@ expression: obj.sections
|
||||
flags: Coff(
|
||||
6,
|
||||
),
|
||||
address: 263,
|
||||
address: 272,
|
||||
target_symbol: 29,
|
||||
addend: 0,
|
||||
},
|
||||
@@ -343,7 +364,7 @@ expression: obj.sections
|
||||
flags: Coff(
|
||||
6,
|
||||
),
|
||||
address: 267,
|
||||
address: 276,
|
||||
target_symbol: 11,
|
||||
addend: 0,
|
||||
},
|
||||
@@ -351,7 +372,7 @@ expression: obj.sections
|
||||
flags: Coff(
|
||||
6,
|
||||
),
|
||||
address: 271,
|
||||
address: 280,
|
||||
target_symbol: 43,
|
||||
addend: 0,
|
||||
},
|
||||
@@ -359,7 +380,7 @@ expression: obj.sections
|
||||
flags: Coff(
|
||||
6,
|
||||
),
|
||||
address: 275,
|
||||
address: 284,
|
||||
target_symbol: 41,
|
||||
addend: 0,
|
||||
},
|
||||
@@ -367,7 +388,7 @@ expression: obj.sections
|
||||
flags: Coff(
|
||||
6,
|
||||
),
|
||||
address: 279,
|
||||
address: 288,
|
||||
target_symbol: 70,
|
||||
addend: 0,
|
||||
},
|
||||
@@ -375,7 +396,7 @@ expression: obj.sections
|
||||
flags: Coff(
|
||||
6,
|
||||
),
|
||||
address: 283,
|
||||
address: 292,
|
||||
target_symbol: 56,
|
||||
addend: 0,
|
||||
},
|
||||
@@ -383,7 +404,7 @@ expression: obj.sections
|
||||
flags: Coff(
|
||||
6,
|
||||
),
|
||||
address: 287,
|
||||
address: 296,
|
||||
target_symbol: 72,
|
||||
addend: 0,
|
||||
},
|
||||
@@ -391,7 +412,7 @@ expression: obj.sections
|
||||
flags: Coff(
|
||||
6,
|
||||
),
|
||||
address: 291,
|
||||
address: 300,
|
||||
target_symbol: 59,
|
||||
addend: 0,
|
||||
},
|
||||
@@ -404,11 +425,14 @@ expression: obj.sections
|
||||
name: ".rdata$r",
|
||||
address: 0,
|
||||
size: 0,
|
||||
kind: Data,
|
||||
kind: Unknown,
|
||||
data: SectionData(
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(Hidden),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
4,
|
||||
),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
@@ -418,11 +442,14 @@ expression: obj.sections
|
||||
name: ".rdata$r",
|
||||
address: 0,
|
||||
size: 0,
|
||||
kind: Data,
|
||||
kind: Unknown,
|
||||
data: SectionData(
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(Hidden),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
4,
|
||||
),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
@@ -432,11 +459,14 @@ expression: obj.sections
|
||||
name: ".data$rs",
|
||||
address: 0,
|
||||
size: 0,
|
||||
kind: Data,
|
||||
kind: Unknown,
|
||||
data: SectionData(
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(Hidden),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
4,
|
||||
),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
@@ -446,11 +476,14 @@ expression: obj.sections
|
||||
name: ".rdata$r",
|
||||
address: 0,
|
||||
size: 0,
|
||||
kind: Data,
|
||||
kind: Unknown,
|
||||
data: SectionData(
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(Hidden),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
4,
|
||||
),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
@@ -460,11 +493,14 @@ expression: obj.sections
|
||||
name: ".rdata$r",
|
||||
address: 0,
|
||||
size: 0,
|
||||
kind: Data,
|
||||
kind: Unknown,
|
||||
data: SectionData(
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(Hidden),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
4,
|
||||
),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
@@ -474,11 +510,14 @@ expression: obj.sections
|
||||
name: ".rdata$r",
|
||||
address: 0,
|
||||
size: 0,
|
||||
kind: Data,
|
||||
kind: Unknown,
|
||||
data: SectionData(
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(Hidden),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
4,
|
||||
),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
@@ -488,11 +527,14 @@ expression: obj.sections
|
||||
name: ".rdata$r",
|
||||
address: 0,
|
||||
size: 0,
|
||||
kind: Data,
|
||||
kind: Unknown,
|
||||
data: SectionData(
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(Hidden),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
4,
|
||||
),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
@@ -502,11 +544,14 @@ expression: obj.sections
|
||||
name: ".data$rs",
|
||||
address: 0,
|
||||
size: 0,
|
||||
kind: Data,
|
||||
kind: Unknown,
|
||||
data: SectionData(
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(Hidden),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
4,
|
||||
),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
@@ -516,11 +561,14 @@ expression: obj.sections
|
||||
name: ".rdata$r",
|
||||
address: 0,
|
||||
size: 0,
|
||||
kind: Data,
|
||||
kind: Unknown,
|
||||
data: SectionData(
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(Hidden),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
4,
|
||||
),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
@@ -530,11 +578,14 @@ expression: obj.sections
|
||||
name: ".rdata$r",
|
||||
address: 0,
|
||||
size: 0,
|
||||
kind: Data,
|
||||
kind: Unknown,
|
||||
data: SectionData(
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(Hidden),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
4,
|
||||
),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
@@ -544,11 +595,14 @@ expression: obj.sections
|
||||
name: ".rdata$r",
|
||||
address: 0,
|
||||
size: 0,
|
||||
kind: Data,
|
||||
kind: Unknown,
|
||||
data: SectionData(
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(Hidden),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
4,
|
||||
),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
@@ -558,11 +612,14 @@ expression: obj.sections
|
||||
name: ".rdata$r",
|
||||
address: 0,
|
||||
size: 0,
|
||||
kind: Data,
|
||||
kind: Unknown,
|
||||
data: SectionData(
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(Hidden),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
4,
|
||||
),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
@@ -572,11 +629,14 @@ expression: obj.sections
|
||||
name: ".text$mn",
|
||||
address: 0,
|
||||
size: 0,
|
||||
kind: Code,
|
||||
kind: Unknown,
|
||||
data: SectionData(
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(Hidden),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
1,
|
||||
),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
@@ -586,11 +646,14 @@ expression: obj.sections
|
||||
name: ".rdata$r",
|
||||
address: 0,
|
||||
size: 0,
|
||||
kind: Data,
|
||||
kind: Unknown,
|
||||
data: SectionData(
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(Hidden),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
4,
|
||||
),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
@@ -600,11 +663,14 @@ expression: obj.sections
|
||||
name: ".text$mn",
|
||||
address: 0,
|
||||
size: 0,
|
||||
kind: Code,
|
||||
kind: Unknown,
|
||||
data: SectionData(
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(Hidden),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
16,
|
||||
),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
@@ -614,11 +680,14 @@ expression: obj.sections
|
||||
name: ".text$mn",
|
||||
address: 0,
|
||||
size: 0,
|
||||
kind: Code,
|
||||
kind: Unknown,
|
||||
data: SectionData(
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(Hidden),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
16,
|
||||
),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
@@ -628,11 +697,14 @@ expression: obj.sections
|
||||
name: ".text$mn",
|
||||
address: 0,
|
||||
size: 0,
|
||||
kind: Code,
|
||||
kind: Unknown,
|
||||
data: SectionData(
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(Hidden),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
16,
|
||||
),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
@@ -642,11 +714,14 @@ expression: obj.sections
|
||||
name: ".text$mn",
|
||||
address: 0,
|
||||
size: 0,
|
||||
kind: Code,
|
||||
kind: Unknown,
|
||||
data: SectionData(
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(Hidden),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
16,
|
||||
),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
@@ -656,11 +731,14 @@ expression: obj.sections
|
||||
name: ".text$mn",
|
||||
address: 0,
|
||||
size: 0,
|
||||
kind: Code,
|
||||
kind: Unknown,
|
||||
data: SectionData(
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(Hidden),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
16,
|
||||
),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
@@ -669,12 +747,15 @@ expression: obj.sections
|
||||
id: ".text-combined",
|
||||
name: ".text",
|
||||
address: 0,
|
||||
size: 268,
|
||||
size: 320,
|
||||
kind: Code,
|
||||
data: SectionData(
|
||||
268,
|
||||
320,
|
||||
),
|
||||
flags: FlagSet(Combined),
|
||||
align: Some(
|
||||
16,
|
||||
),
|
||||
relocations: [
|
||||
Relocation {
|
||||
flags: Coff(
|
||||
@@ -696,7 +777,7 @@ expression: obj.sections
|
||||
flags: Coff(
|
||||
20,
|
||||
),
|
||||
address: 29,
|
||||
address: 43,
|
||||
target_symbol: 60,
|
||||
addend: 0,
|
||||
},
|
||||
@@ -704,7 +785,7 @@ expression: obj.sections
|
||||
flags: Coff(
|
||||
20,
|
||||
),
|
||||
address: 48,
|
||||
address: 62,
|
||||
target_symbol: 52,
|
||||
addend: 0,
|
||||
},
|
||||
@@ -712,7 +793,7 @@ expression: obj.sections
|
||||
flags: Coff(
|
||||
20,
|
||||
),
|
||||
address: 68,
|
||||
address: 84,
|
||||
target_symbol: 11,
|
||||
addend: 0,
|
||||
},
|
||||
@@ -720,7 +801,7 @@ expression: obj.sections
|
||||
flags: Coff(
|
||||
6,
|
||||
),
|
||||
address: 84,
|
||||
address: 100,
|
||||
target_symbol: 64,
|
||||
addend: 0,
|
||||
},
|
||||
@@ -728,7 +809,7 @@ expression: obj.sections
|
||||
flags: Coff(
|
||||
6,
|
||||
),
|
||||
address: 104,
|
||||
address: 124,
|
||||
target_symbol: 66,
|
||||
addend: 0,
|
||||
},
|
||||
@@ -736,7 +817,7 @@ expression: obj.sections
|
||||
flags: Coff(
|
||||
6,
|
||||
),
|
||||
address: 124,
|
||||
address: 156,
|
||||
target_symbol: 6,
|
||||
addend: 0,
|
||||
},
|
||||
@@ -744,7 +825,7 @@ expression: obj.sections
|
||||
flags: Coff(
|
||||
6,
|
||||
),
|
||||
address: 134,
|
||||
address: 166,
|
||||
target_symbol: 8,
|
||||
addend: 0,
|
||||
},
|
||||
@@ -752,7 +833,7 @@ expression: obj.sections
|
||||
flags: Coff(
|
||||
20,
|
||||
),
|
||||
address: 145,
|
||||
address: 177,
|
||||
target_symbol: 57,
|
||||
addend: 0,
|
||||
},
|
||||
@@ -760,7 +841,7 @@ expression: obj.sections
|
||||
flags: Coff(
|
||||
20,
|
||||
),
|
||||
address: 153,
|
||||
address: 185,
|
||||
target_symbol: 54,
|
||||
addend: 0,
|
||||
},
|
||||
@@ -768,7 +849,7 @@ expression: obj.sections
|
||||
flags: Coff(
|
||||
20,
|
||||
),
|
||||
address: 172,
|
||||
address: 219,
|
||||
target_symbol: 54,
|
||||
addend: 0,
|
||||
},
|
||||
@@ -776,7 +857,7 @@ expression: obj.sections
|
||||
flags: Coff(
|
||||
20,
|
||||
),
|
||||
address: 191,
|
||||
address: 238,
|
||||
target_symbol: 52,
|
||||
addend: 0,
|
||||
},
|
||||
@@ -784,7 +865,7 @@ expression: obj.sections
|
||||
flags: Coff(
|
||||
20,
|
||||
),
|
||||
address: 218,
|
||||
address: 267,
|
||||
target_symbol: 57,
|
||||
addend: 0,
|
||||
},
|
||||
@@ -792,7 +873,7 @@ expression: obj.sections
|
||||
flags: Coff(
|
||||
20,
|
||||
),
|
||||
address: 237,
|
||||
address: 286,
|
||||
target_symbol: 52,
|
||||
addend: 0,
|
||||
},
|
||||
@@ -800,7 +881,7 @@ expression: obj.sections
|
||||
flags: Coff(
|
||||
6,
|
||||
),
|
||||
address: 257,
|
||||
address: 308,
|
||||
target_symbol: 68,
|
||||
addend: 0,
|
||||
},
|
||||
@@ -808,7 +889,7 @@ expression: obj.sections
|
||||
flags: Coff(
|
||||
20,
|
||||
),
|
||||
address: 262,
|
||||
address: 313,
|
||||
target_symbol: 60,
|
||||
addend: 0,
|
||||
},
|
||||
@@ -821,11 +902,14 @@ expression: obj.sections
|
||||
name: ".text$yd",
|
||||
address: 0,
|
||||
size: 0,
|
||||
kind: Code,
|
||||
kind: Unknown,
|
||||
data: SectionData(
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(Hidden),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
16,
|
||||
),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
@@ -835,11 +919,14 @@ expression: obj.sections
|
||||
name: ".rdata",
|
||||
address: 0,
|
||||
size: 0,
|
||||
kind: Data,
|
||||
kind: Unknown,
|
||||
data: SectionData(
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(Hidden),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
4,
|
||||
),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
@@ -849,11 +936,14 @@ expression: obj.sections
|
||||
name: ".rdata",
|
||||
address: 0,
|
||||
size: 0,
|
||||
kind: Data,
|
||||
kind: Unknown,
|
||||
data: SectionData(
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(Hidden),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
4,
|
||||
),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
@@ -863,11 +953,14 @@ expression: obj.sections
|
||||
name: ".data",
|
||||
address: 0,
|
||||
size: 0,
|
||||
kind: Data,
|
||||
kind: Unknown,
|
||||
data: SectionData(
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(Hidden),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
4,
|
||||
),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
@@ -877,11 +970,14 @@ expression: obj.sections
|
||||
name: ".rdata$r",
|
||||
address: 0,
|
||||
size: 0,
|
||||
kind: Data,
|
||||
kind: Unknown,
|
||||
data: SectionData(
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(Hidden),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
4,
|
||||
),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
@@ -891,11 +987,14 @@ expression: obj.sections
|
||||
name: ".rdata$r",
|
||||
address: 0,
|
||||
size: 0,
|
||||
kind: Data,
|
||||
kind: Unknown,
|
||||
data: SectionData(
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(Hidden),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
4,
|
||||
),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
@@ -910,6 +1009,9 @@ expression: obj.sections
|
||||
4,
|
||||
),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
4,
|
||||
),
|
||||
relocations: [
|
||||
Relocation {
|
||||
flags: Coff(
|
||||
@@ -933,6 +1035,9 @@ expression: obj.sections
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(),
|
||||
align: Some(
|
||||
16,
|
||||
),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user