mirror of
https://github.com/encounter/objdiff.git
synced 2025-06-08 15:43:27 +00:00
Compare commits
No commits in common. "main" and "v2.2.1" have entirely different histories.
@ -1,4 +0,0 @@
|
|||||||
# 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"]
|
|
@ -1,2 +0,0 @@
|
|||||||
[*.md]
|
|
||||||
trim_trailing_whitespace = false
|
|
164
.github/workflows/build.yaml
vendored
164
.github/workflows/build.yaml
vendored
@ -4,16 +4,13 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
push:
|
push:
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- "*.md"
|
- '*.md'
|
||||||
- "LICENSE*"
|
- 'LICENSE*'
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
permissions:
|
|
||||||
# For npm publish provenance
|
|
||||||
id-token: write
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
BUILD_PROFILE: release-lto
|
BUILD_PROFILE: release-lto
|
||||||
|
CARGO_TARGET_DIR: target
|
||||||
CARGO_INCREMENTAL: 0
|
CARGO_INCREMENTAL: 0
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@ -33,12 +30,10 @@ jobs:
|
|||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@stable
|
||||||
with:
|
with:
|
||||||
components: clippy
|
components: clippy
|
||||||
- name: Cache Rust workspace
|
|
||||||
uses: Swatinem/rust-cache@v2
|
|
||||||
- name: Cargo check
|
- name: Cargo check
|
||||||
run: cargo check --all-targets --all-features
|
run: cargo check --all-features --all-targets
|
||||||
- name: Cargo clippy
|
- name: Cargo clippy
|
||||||
run: cargo clippy --all-targets --all-features
|
run: cargo clippy --all-features --all-targets
|
||||||
|
|
||||||
fmt:
|
fmt:
|
||||||
name: Format
|
name: Format
|
||||||
@ -68,12 +63,13 @@ jobs:
|
|||||||
continue-on-error: ${{ matrix.checks == 'advisories' }}
|
continue-on-error: ${{ matrix.checks == 'advisories' }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: EmbarkStudios/cargo-deny-action@v2
|
- uses: EmbarkStudios/cargo-deny-action@v1
|
||||||
with:
|
with:
|
||||||
command: check ${{ matrix.checks }}
|
command: check ${{ matrix.checks }}
|
||||||
|
|
||||||
test:
|
test:
|
||||||
name: Test
|
name: Test
|
||||||
|
if: 'false' # No tests yet
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
platform: [ ubuntu-latest, windows-latest, macos-latest ]
|
platform: [ ubuntu-latest, windows-latest, macos-latest ]
|
||||||
@ -89,10 +85,8 @@ jobs:
|
|||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
- name: Setup Rust toolchain
|
- name: Setup Rust toolchain
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@stable
|
||||||
- name: Cache Rust workspace
|
|
||||||
uses: Swatinem/rust-cache@v2
|
|
||||||
- name: Cargo test
|
- name: Cargo test
|
||||||
run: cargo test --release --features all
|
run: cargo test --release --all-features
|
||||||
|
|
||||||
build-cli:
|
build-cli:
|
||||||
name: Build objdiff-cli
|
name: Build objdiff-cli
|
||||||
@ -148,19 +142,11 @@ jobs:
|
|||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
- name: Install cargo-zigbuild
|
- name: Install cargo-zigbuild
|
||||||
if: matrix.build == 'zigbuild'
|
if: matrix.build == 'zigbuild'
|
||||||
run: |
|
run: pip install ziglang==0.13.0 cargo-zigbuild==0.19.1
|
||||||
python3 -m venv .venv
|
|
||||||
. .venv/bin/activate
|
|
||||||
echo PATH=$PATH >> $GITHUB_ENV
|
|
||||||
pip install ziglang==0.13.0.post1 cargo-zigbuild==0.19.8
|
|
||||||
- name: Setup Rust toolchain
|
- name: Setup Rust toolchain
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@stable
|
||||||
with:
|
with:
|
||||||
targets: ${{ matrix.target }}
|
targets: ${{ matrix.target }}
|
||||||
- name: Cache Rust workspace
|
|
||||||
uses: Swatinem/rust-cache@v2
|
|
||||||
with:
|
|
||||||
key: ${{ matrix.target }}
|
|
||||||
- name: Cargo build
|
- name: Cargo build
|
||||||
run: >
|
run: >
|
||||||
cargo ${{ matrix.build }} --profile ${{ env.BUILD_PROFILE }} --target ${{ matrix.target }}
|
cargo ${{ matrix.build }} --profile ${{ env.BUILD_PROFILE }} --target ${{ matrix.target }}
|
||||||
@ -170,8 +156,8 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: ${{ env.CARGO_BIN_NAME }}-${{ matrix.name }}
|
name: ${{ env.CARGO_BIN_NAME }}-${{ matrix.name }}
|
||||||
path: |
|
path: |
|
||||||
target/${{ matrix.target }}/${{ env.BUILD_PROFILE }}/${{ env.CARGO_BIN_NAME }}
|
${{ env.CARGO_TARGET_DIR }}/${{ matrix.target }}/${{ env.BUILD_PROFILE }}/${{ env.CARGO_BIN_NAME }}
|
||||||
target/${{ matrix.target }}/${{ env.BUILD_PROFILE }}/${{ env.CARGO_BIN_NAME }}.exe
|
${{ env.CARGO_TARGET_DIR }}/${{ matrix.target }}/${{ env.BUILD_PROFILE }}/${{ env.CARGO_BIN_NAME }}.exe
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
build-gui:
|
build-gui:
|
||||||
@ -182,26 +168,21 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- platform: ubuntu-latest
|
- platform: ubuntu-latest
|
||||||
target: x86_64-unknown-linux-gnu.2.31
|
target: x86_64-unknown-linux-gnu
|
||||||
target_base: x86_64-unknown-linux-gnu
|
|
||||||
name: linux-x86_64
|
name: linux-x86_64
|
||||||
packages: libgtk-3-dev
|
packages: libgtk-3-dev
|
||||||
build: zigbuild
|
|
||||||
features: default
|
features: default
|
||||||
- platform: windows-latest
|
- platform: windows-latest
|
||||||
target: x86_64-pc-windows-msvc
|
target: x86_64-pc-windows-msvc
|
||||||
name: windows-x86_64
|
name: windows-x86_64
|
||||||
build: build
|
|
||||||
features: default
|
features: default
|
||||||
- platform: macos-latest
|
- platform: macos-latest
|
||||||
target: x86_64-apple-darwin
|
target: x86_64-apple-darwin
|
||||||
name: macos-x86_64
|
name: macos-x86_64
|
||||||
build: build
|
|
||||||
features: default
|
features: default
|
||||||
- platform: macos-latest
|
- platform: macos-latest
|
||||||
target: aarch64-apple-darwin
|
target: aarch64-apple-darwin
|
||||||
name: macos-arm64
|
name: macos-arm64
|
||||||
build: build
|
|
||||||
features: default
|
features: default
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
runs-on: ${{ matrix.platform }}
|
runs-on: ${{ matrix.platform }}
|
||||||
@ -213,70 +194,34 @@ jobs:
|
|||||||
sudo apt-get -y install ${{ matrix.packages }}
|
sudo apt-get -y install ${{ matrix.packages }}
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
- name: Install cargo-zigbuild
|
|
||||||
if: matrix.build == 'zigbuild'
|
|
||||||
run: |
|
|
||||||
python3 -m venv .venv
|
|
||||||
. .venv/bin/activate
|
|
||||||
echo PATH=$PATH >> $GITHUB_ENV
|
|
||||||
pip install ziglang==0.13.0.post1 cargo-zigbuild==0.19.8
|
|
||||||
- name: Setup Rust toolchain
|
- name: Setup Rust toolchain
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@stable
|
||||||
with:
|
with:
|
||||||
targets: ${{ matrix.target_base || matrix.target }}
|
targets: ${{ matrix.target }}
|
||||||
- name: Cache Rust workspace
|
|
||||||
uses: Swatinem/rust-cache@v2
|
|
||||||
with:
|
|
||||||
key: ${{ matrix.target }}
|
|
||||||
- name: Cargo build
|
- name: Cargo build
|
||||||
run: >
|
run: >
|
||||||
cargo ${{ matrix.build }} --profile ${{ env.BUILD_PROFILE }} --target ${{ matrix.target }}
|
cargo build --profile ${{ env.BUILD_PROFILE }} --target ${{ matrix.target }}
|
||||||
--bin ${{ env.CARGO_BIN_NAME }} --features ${{ matrix.features }}
|
--bin ${{ env.CARGO_BIN_NAME }} --features ${{ matrix.features }}
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: ${{ env.CARGO_BIN_NAME }}-${{ matrix.name }}
|
name: ${{ env.CARGO_BIN_NAME }}-${{ matrix.name }}
|
||||||
path: |
|
path: |
|
||||||
target/${{ matrix.target_base || matrix.target }}/${{ env.BUILD_PROFILE }}/${{ env.CARGO_BIN_NAME }}
|
${{ env.CARGO_TARGET_DIR }}/${{ matrix.target }}/${{ env.BUILD_PROFILE }}/${{ env.CARGO_BIN_NAME }}
|
||||||
target/${{ matrix.target_base || matrix.target }}/${{ env.BUILD_PROFILE }}/${{ env.CARGO_BIN_NAME }}.exe
|
${{ env.CARGO_TARGET_DIR }}/${{ matrix.target }}/${{ env.BUILD_PROFILE }}/${{ env.CARGO_BIN_NAME }}.exe
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
build-wasm:
|
release:
|
||||||
name: Build objdiff-wasm
|
name: Release
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
- name: Setup Rust toolchain
|
|
||||||
uses: dtolnay/rust-toolchain@nightly
|
|
||||||
with:
|
|
||||||
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 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
|
|
||||||
|
|
||||||
check-version:
|
|
||||||
name: Check package versions
|
|
||||||
if: startsWith(github.ref, 'refs/tags/')
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [ build-cli, build-gui, build-wasm ]
|
needs: [ build-cli, build-gui ]
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
- name: Check git tag against package versions
|
- name: Check git tag against Cargo version
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
set -eou pipefail
|
set -eou pipefail
|
||||||
@ -288,24 +233,9 @@ jobs:
|
|||||||
echo "::error::Git tag doesn't match the Cargo version! ($tag != $version)"
|
echo "::error::Git tag doesn't match the Cargo version! ($tag != $version)"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
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
|
- name: Download artifacts
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
pattern: objdiff-*
|
|
||||||
path: artifacts
|
path: artifacts
|
||||||
- name: Rename artifacts
|
- name: Rename artifacts
|
||||||
working-directory: artifacts
|
working-directory: artifacts
|
||||||
@ -335,53 +265,3 @@ jobs:
|
|||||||
files: out/*
|
files: out/*
|
||||||
draft: true
|
draft: true
|
||||||
generate_release_notes: 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"
|
|
||||||
|
6
.gitignore
vendored
6
.gitignore
vendored
@ -3,6 +3,10 @@ target/
|
|||||||
**/*.rs.bk
|
**/*.rs.bk
|
||||||
generated/
|
generated/
|
||||||
|
|
||||||
|
# cargo-mobile
|
||||||
|
.cargo/
|
||||||
|
/gen
|
||||||
|
|
||||||
# macOS
|
# macOS
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
@ -18,4 +22,4 @@ android.keystore
|
|||||||
*.frag
|
*.frag
|
||||||
*.vert
|
*.vert
|
||||||
*.metal
|
*.metal
|
||||||
.vscode/
|
.vscode/launch.json
|
||||||
|
@ -1,36 +0,0 @@
|
|||||||
# 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
|
|
3636
Cargo.lock
generated
3636
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -3,9 +3,8 @@ members = [
|
|||||||
"objdiff-cli",
|
"objdiff-cli",
|
||||||
"objdiff-core",
|
"objdiff-core",
|
||||||
"objdiff-gui",
|
"objdiff-gui",
|
||||||
"objdiff-wasm",
|
|
||||||
]
|
]
|
||||||
resolver = "3"
|
resolver = "2"
|
||||||
|
|
||||||
[profile.release-lto]
|
[profile.release-lto]
|
||||||
inherits = "release"
|
inherits = "release"
|
||||||
@ -14,9 +13,9 @@ strip = "debuginfo"
|
|||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "3.0.0-beta.9"
|
version = "2.2.1"
|
||||||
authors = ["Luke Street <luke@street.dev>"]
|
authors = ["Luke Street <luke@street.dev>"]
|
||||||
edition = "2024"
|
edition = "2021"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
repository = "https://github.com/encounter/objdiff"
|
repository = "https://github.com/encounter/objdiff"
|
||||||
rust-version = "1.85"
|
rust-version = "1.74"
|
||||||
|
23
README.md
23
README.md
@ -16,12 +16,10 @@ Features:
|
|||||||
|
|
||||||
Supports:
|
Supports:
|
||||||
|
|
||||||
- ARM (GBA, DS, 3DS)
|
- PowerPC 750CL (GameCube, Wii)
|
||||||
- ARM64 (Switch)
|
|
||||||
- MIPS (N64, PS1, PS2, PSP)
|
- MIPS (N64, PS1, PS2, PSP)
|
||||||
- PowerPC (GameCube, Wii)
|
- x86 (COFF only at the moment)
|
||||||
- SuperH (Saturn, Dreamcast)
|
- ARM (GBA, DS, 3DS)
|
||||||
- x86 (COFF only)
|
|
||||||
|
|
||||||
See [Usage](#usage) for more information.
|
See [Usage](#usage) for more information.
|
||||||
|
|
||||||
@ -174,21 +172,6 @@ $ cargo install --locked --git https://github.com/encounter/objdiff.git objdiff-
|
|||||||
|
|
||||||
The binaries will be installed to `~/.cargo/bin` as `objdiff` and `objdiff-cli`.
|
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
|
## License
|
||||||
|
|
||||||
Licensed under either of
|
Licensed under either of
|
||||||
|
@ -133,13 +133,6 @@
|
|||||||
},
|
},
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"ref": "#/$defs/metadata"
|
"ref": "#/$defs/metadata"
|
||||||
},
|
|
||||||
"symbol_mappings": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "Manual symbol mappings from target to base.",
|
|
||||||
"additionalProperties": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -175,10 +168,6 @@
|
|||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "If true, objdiff will run the build command with the context file as an argument to generate it.",
|
"description": "If true, objdiff will run the build command with the context file as an argument to generate it.",
|
||||||
"default": false
|
"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": [
|
"required": [
|
||||||
|
13
deny.toml
13
deny.toml
@ -70,10 +70,10 @@ feature-depth = 1
|
|||||||
# A list of advisory IDs to ignore. Note that ignored advisories will still
|
# A list of advisory IDs to ignore. Note that ignored advisories will still
|
||||||
# output a note when they are encountered.
|
# output a note when they are encountered.
|
||||||
ignore = [
|
ignore = [
|
||||||
|
"RUSTSEC-2024-0370",
|
||||||
#{ id = "RUSTSEC-0000-0000", reason = "you can specify a reason the advisory is ignored" },
|
#{ id = "RUSTSEC-0000-0000", reason = "you can specify a reason the advisory is ignored" },
|
||||||
#"a-crate-that-is-yanked@0.1.1", # you can also ignore yanked crate versions if you wish
|
#"a-crate-that-is-yanked@0.1.1", # you can also ignore yanked crate versions if you wish
|
||||||
#{ crate = "a-crate-that-is-yanked@0.1.1", reason = "you can specify why you are ignoring the yanked crate" },
|
#{ crate = "a-crate-that-is-yanked@0.1.1", reason = "you can specify why you are ignoring the yanked crate" },
|
||||||
{ id = "RUSTSEC-2024-0436", reason = "Unmaintained paste crate is an indirect dependency" },
|
|
||||||
]
|
]
|
||||||
# If this is true, then cargo deny will use the git executable to fetch advisory database.
|
# If this is true, then cargo deny will use the git executable to fetch advisory database.
|
||||||
# If this is false, then it uses a built-in git library.
|
# If this is false, then it uses a built-in git library.
|
||||||
@ -98,12 +98,12 @@ allow = [
|
|||||||
"BSL-1.0",
|
"BSL-1.0",
|
||||||
"CC0-1.0",
|
"CC0-1.0",
|
||||||
"MPL-2.0",
|
"MPL-2.0",
|
||||||
"Unicode-3.0",
|
"Unicode-DFS-2016",
|
||||||
"Zlib",
|
"Zlib",
|
||||||
"0BSD",
|
"0BSD",
|
||||||
"OFL-1.1",
|
"OFL-1.1",
|
||||||
"Ubuntu-font-1.0",
|
"LicenseRef-UFL-1.0",
|
||||||
"CDLA-Permissive-2.0",
|
"OpenSSL",
|
||||||
]
|
]
|
||||||
# The confidence threshold for detecting a license from license text.
|
# The confidence threshold for detecting a license from license text.
|
||||||
# The higher the value, the more closely the license text must be to the
|
# The higher the value, the more closely the license text must be to the
|
||||||
@ -240,10 +240,7 @@ allow-git = []
|
|||||||
|
|
||||||
[sources.allow-org]
|
[sources.allow-org]
|
||||||
# github.com organizations to allow git sources for
|
# github.com organizations to allow git sources for
|
||||||
github = [
|
github = ["encounter"]
|
||||||
"enarx", # flagset
|
|
||||||
"encounter",
|
|
||||||
]
|
|
||||||
# gitlab.com organizations to allow git sources for
|
# gitlab.com organizations to allow git sources for
|
||||||
gitlab = []
|
gitlab = []
|
||||||
# bitbucket.org organizations to allow git sources for
|
# bitbucket.org organizations to allow git sources for
|
||||||
|
@ -14,13 +14,13 @@ publish = false
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
argp = "0.4"
|
argp = "0.3"
|
||||||
crossterm = "0.28"
|
crossterm = "0.28"
|
||||||
enable-ansi-support = "0.2"
|
enable-ansi-support = "0.2"
|
||||||
memmap2 = "0.9"
|
memmap2 = "0.9"
|
||||||
objdiff-core = { path = "../objdiff-core", features = ["all"] }
|
objdiff-core = { path = "../objdiff-core", features = ["all"] }
|
||||||
prost = "0.13"
|
prost = "0.13"
|
||||||
ratatui = "0.29"
|
ratatui = "0.28"
|
||||||
rayon = "1.10"
|
rayon = "1.10"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
@ -28,7 +28,6 @@ supports-color = "3.0"
|
|||||||
time = { version = "0.3", features = ["formatting", "local-offset"] }
|
time = { version = "0.3", features = ["formatting", "local-offset"] }
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||||
typed-path = "0.11"
|
|
||||||
|
|
||||||
[target.'cfg(target_env = "musl")'.dependencies]
|
[target.'cfg(target_env = "musl")'.dependencies]
|
||||||
mimalloc = "0.1"
|
mimalloc = "0.1"
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
//! For now, this only adds a --version/-V option which causes early-exit.
|
//! For now, this only adds a --version/-V option which causes early-exit.
|
||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
|
|
||||||
use argp::{EarlyExit, FromArgs, TopLevelCommand, parser::ParseGlobalOptions};
|
use argp::{parser::ParseGlobalOptions, EarlyExit, FromArgs, TopLevelCommand};
|
||||||
|
|
||||||
struct ArgsOrVersion<T>(T)
|
struct ArgsOrVersion<T>(T)
|
||||||
where T: FromArgs;
|
where T: FromArgs;
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,33 +1,2 @@
|
|||||||
pub mod diff;
|
pub mod diff;
|
||||||
pub mod report;
|
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,25 +1,28 @@
|
|||||||
use std::{collections::HashSet, fs::File, io::Read, time::Instant};
|
use std::{
|
||||||
|
collections::HashSet,
|
||||||
|
fs::File,
|
||||||
|
io::Read,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
time::Instant,
|
||||||
|
};
|
||||||
|
|
||||||
use anyhow::{Context, Result, bail};
|
use anyhow::{bail, Context, Result};
|
||||||
use argp::FromArgs;
|
use argp::FromArgs;
|
||||||
use objdiff_core::{
|
use objdiff_core::{
|
||||||
bindings::report::{
|
bindings::report::{
|
||||||
ChangeItem, ChangeItemInfo, ChangeUnit, Changes, ChangesInput, Measures, REPORT_VERSION,
|
ChangeItem, ChangeItemInfo, ChangeUnit, Changes, ChangesInput, Measures, Report,
|
||||||
Report, ReportCategory, ReportItem, ReportItemMetadata, ReportUnit, ReportUnitMetadata,
|
ReportCategory, ReportItem, ReportItemMetadata, ReportUnit, ReportUnitMetadata,
|
||||||
|
REPORT_VERSION,
|
||||||
},
|
},
|
||||||
config::path::platform_path,
|
config::ProjectObject,
|
||||||
diff, obj,
|
diff, obj,
|
||||||
obj::{SectionKind, SymbolFlag},
|
obj::{ObjSectionKind, ObjSymbolFlags},
|
||||||
};
|
};
|
||||||
use prost::Message;
|
use prost::Message;
|
||||||
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
|
use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator};
|
||||||
use tracing::{info, warn};
|
use tracing::{info, warn};
|
||||||
use typed_path::{Utf8PlatformPath, Utf8PlatformPathBuf};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::util::output::{write_output, OutputFormat};
|
||||||
cmd::{apply_config_args, diff::ObjectConfig},
|
|
||||||
util::output::{OutputFormat, write_output},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(FromArgs, PartialEq, Debug)]
|
#[derive(FromArgs, PartialEq, Debug)]
|
||||||
/// Generate a progress report for a project.
|
/// Generate a progress report for a project.
|
||||||
@ -40,36 +43,33 @@ pub enum SubCommand {
|
|||||||
/// Generate a progress report for a project.
|
/// Generate a progress report for a project.
|
||||||
#[argp(subcommand, name = "generate")]
|
#[argp(subcommand, name = "generate")]
|
||||||
pub struct GenerateArgs {
|
pub struct GenerateArgs {
|
||||||
#[argp(option, short = 'p', from_str_fn(platform_path))]
|
#[argp(option, short = 'p')]
|
||||||
/// Project directory
|
/// Project directory
|
||||||
project: Option<Utf8PlatformPathBuf>,
|
project: Option<PathBuf>,
|
||||||
#[argp(option, short = 'o', from_str_fn(platform_path))]
|
#[argp(option, short = 'o')]
|
||||||
/// Output file
|
/// Output file
|
||||||
output: Option<Utf8PlatformPathBuf>,
|
output: Option<PathBuf>,
|
||||||
#[argp(switch, short = 'd')]
|
#[argp(switch, short = 'd')]
|
||||||
/// Deduplicate global and weak symbols (runs single-threaded)
|
/// Deduplicate global and weak symbols (runs single-threaded)
|
||||||
deduplicate: bool,
|
deduplicate: bool,
|
||||||
#[argp(option, short = 'f')]
|
#[argp(option, short = 'f')]
|
||||||
/// Output format (json, json-pretty, proto) (default: json)
|
/// Output format (json, json-pretty, proto) (default: json)
|
||||||
format: Option<String>,
|
format: Option<String>,
|
||||||
#[argp(option, short = 'c')]
|
|
||||||
/// Configuration property (key=value)
|
|
||||||
config: Vec<String>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(FromArgs, PartialEq, Debug)]
|
#[derive(FromArgs, PartialEq, Debug)]
|
||||||
/// List any changes from a previous report.
|
/// List any changes from a previous report.
|
||||||
#[argp(subcommand, name = "changes")]
|
#[argp(subcommand, name = "changes")]
|
||||||
pub struct ChangesArgs {
|
pub struct ChangesArgs {
|
||||||
#[argp(positional, from_str_fn(platform_path))]
|
#[argp(positional)]
|
||||||
/// Previous report file
|
/// Previous report file
|
||||||
previous: Utf8PlatformPathBuf,
|
previous: PathBuf,
|
||||||
#[argp(positional, from_str_fn(platform_path))]
|
#[argp(positional)]
|
||||||
/// Current report file
|
/// Current report file
|
||||||
current: Utf8PlatformPathBuf,
|
current: PathBuf,
|
||||||
#[argp(option, short = 'o', from_str_fn(platform_path))]
|
#[argp(option, short = 'o')]
|
||||||
/// Output file
|
/// Output file
|
||||||
output: Option<Utf8PlatformPathBuf>,
|
output: Option<PathBuf>,
|
||||||
#[argp(option, short = 'f')]
|
#[argp(option, short = 'f')]
|
||||||
/// Output format (json, json-pretty, proto) (default: json)
|
/// Output format (json, json-pretty, proto) (default: json)
|
||||||
format: Option<String>,
|
format: Option<String>,
|
||||||
@ -83,64 +83,56 @@ pub fn run(args: Args) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn generate(args: GenerateArgs) -> 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 output_format = OutputFormat::from_option(args.format.as_deref())?;
|
||||||
let project_dir = args.project.as_deref().unwrap_or_else(|| Utf8PlatformPath::new("."));
|
let project_dir = args.project.as_deref().unwrap_or_else(|| Path::new("."));
|
||||||
info!("Loading project {}", project_dir);
|
info!("Loading project {}", project_dir.display());
|
||||||
|
|
||||||
let project = match objdiff_core::config::try_project_config(project_dir.as_ref()) {
|
let mut project = match objdiff_core::config::try_project_config(project_dir) {
|
||||||
Some((Ok(config), _)) => config,
|
Some((Ok(config), _)) => config,
|
||||||
Some((Err(err), _)) => bail!("Failed to load project configuration: {}", err),
|
Some((Err(err), _)) => bail!("Failed to load project configuration: {}", err),
|
||||||
None => bail!("No project configuration found"),
|
None => bail!("No project configuration found"),
|
||||||
};
|
};
|
||||||
info!(
|
info!(
|
||||||
"Generating report for {} units (using {} threads)",
|
"Generating report for {} units (using {} threads)",
|
||||||
project.units().len(),
|
project.objects.len(),
|
||||||
if args.deduplicate { 1 } else { rayon::current_num_threads() }
|
if args.deduplicate { 1 } else { rayon::current_num_threads() }
|
||||||
);
|
);
|
||||||
|
|
||||||
let target_obj_dir =
|
|
||||||
project.target_dir.as_ref().map(|p| project_dir.join(p.with_platform_encoding()));
|
|
||||||
let base_obj_dir =
|
|
||||||
project.base_dir.as_ref().map(|p| project_dir.join(p.with_platform_encoding()));
|
|
||||||
let objects = project
|
|
||||||
.units
|
|
||||||
.iter()
|
|
||||||
.flatten()
|
|
||||||
.map(|o| {
|
|
||||||
ObjectConfig::new(o, project_dir, target_obj_dir.as_deref(), base_obj_dir.as_deref())
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
let mut units = vec![];
|
let mut units = vec![];
|
||||||
let mut existing_functions: HashSet<String> = HashSet::new();
|
let mut existing_functions: HashSet<String> = HashSet::new();
|
||||||
if args.deduplicate {
|
if args.deduplicate {
|
||||||
// If deduplicating, we need to run single-threaded
|
// If deduplicating, we need to run single-threaded
|
||||||
for object in &objects {
|
for object in &mut project.objects {
|
||||||
if let Some(unit) = report_object(object, &diff_config, Some(&mut existing_functions))?
|
if let Some(unit) = report_object(
|
||||||
{
|
object,
|
||||||
|
project_dir,
|
||||||
|
project.target_dir.as_deref(),
|
||||||
|
project.base_dir.as_deref(),
|
||||||
|
Some(&mut existing_functions),
|
||||||
|
)? {
|
||||||
units.push(unit);
|
units.push(unit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let vec = objects
|
let vec = project
|
||||||
.par_iter()
|
.objects
|
||||||
.map(|object| report_object(object, &diff_config, None))
|
.par_iter_mut()
|
||||||
|
.map(|object| {
|
||||||
|
report_object(
|
||||||
|
object,
|
||||||
|
project_dir,
|
||||||
|
project.target_dir.as_deref(),
|
||||||
|
project.base_dir.as_deref(),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
})
|
||||||
.collect::<Result<Vec<Option<ReportUnit>>>>()?;
|
.collect::<Result<Vec<Option<ReportUnit>>>>()?;
|
||||||
units = vec.into_iter().flatten().collect();
|
units = vec.into_iter().flatten().collect();
|
||||||
}
|
}
|
||||||
let measures = units.iter().flat_map(|u| u.measures.into_iter()).collect();
|
let measures = units.iter().flat_map(|u| u.measures.into_iter()).collect();
|
||||||
let mut categories = Vec::new();
|
let mut categories = Vec::new();
|
||||||
for category in project.progress_categories() {
|
for category in &project.progress_categories {
|
||||||
categories.push(ReportCategory {
|
categories.push(ReportCategory {
|
||||||
id: category.id.clone(),
|
id: category.id.clone(),
|
||||||
name: category.name.clone(),
|
name: category.name.clone(),
|
||||||
@ -157,51 +149,55 @@ fn generate(args: GenerateArgs) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn report_object(
|
fn report_object(
|
||||||
object: &ObjectConfig,
|
object: &mut ProjectObject,
|
||||||
diff_config: &diff::DiffObjConfig,
|
project_dir: &Path,
|
||||||
|
target_dir: Option<&Path>,
|
||||||
|
base_dir: Option<&Path>,
|
||||||
mut existing_functions: Option<&mut HashSet<String>>,
|
mut existing_functions: Option<&mut HashSet<String>>,
|
||||||
) -> Result<Option<ReportUnit>> {
|
) -> Result<Option<ReportUnit>> {
|
||||||
|
object.resolve_paths(project_dir, target_dir, base_dir);
|
||||||
match (&object.target_path, &object.base_path) {
|
match (&object.target_path, &object.base_path) {
|
||||||
(None, Some(_)) if !object.complete.unwrap_or(false) => {
|
(None, Some(_)) if !object.complete().unwrap_or(false) => {
|
||||||
warn!("Skipping object without target: {}", object.name);
|
warn!("Skipping object without target: {}", object.name());
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
(None, None) => {
|
(None, None) => {
|
||||||
warn!("Skipping object without target or base: {}", object.name);
|
warn!("Skipping object without target or base: {}", object.name());
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
let mapping_config = diff::MappingConfig::default();
|
let config = diff::DiffObjConfig { relax_reloc_diffs: true, ..Default::default() };
|
||||||
let target = object
|
let target = object
|
||||||
.target_path
|
.target_path
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|p| {
|
.map(|p| {
|
||||||
obj::read::read(p.as_ref(), diff_config)
|
obj::read::read(p, &config).with_context(|| format!("Failed to open {}", p.display()))
|
||||||
.with_context(|| format!("Failed to open {}", p))
|
|
||||||
})
|
})
|
||||||
.transpose()?;
|
.transpose()?;
|
||||||
let base = object
|
let base = object
|
||||||
.base_path
|
.base_path
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|p| {
|
.map(|p| {
|
||||||
obj::read::read(p.as_ref(), diff_config)
|
obj::read::read(p, &config).with_context(|| format!("Failed to open {}", p.display()))
|
||||||
.with_context(|| format!("Failed to open {}", p))
|
|
||||||
})
|
})
|
||||||
.transpose()?;
|
.transpose()?;
|
||||||
let result =
|
let result = diff::diff_objs(&config, target.as_ref(), base.as_ref(), None)?;
|
||||||
diff::diff_objs(target.as_ref(), base.as_ref(), None, diff_config, &mapping_config)?;
|
|
||||||
|
|
||||||
let metadata = ReportUnitMetadata {
|
let metadata = ReportUnitMetadata {
|
||||||
complete: object.metadata.complete,
|
complete: object.complete(),
|
||||||
module_name: target
|
module_name: target
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|o| o.split_meta.as_ref())
|
.and_then(|o| o.split_meta.as_ref())
|
||||||
.and_then(|m| m.module_name.clone()),
|
.and_then(|m| m.module_name.clone()),
|
||||||
module_id: target.as_ref().and_then(|o| o.split_meta.as_ref()).and_then(|m| m.module_id),
|
module_id: target.as_ref().and_then(|o| o.split_meta.as_ref()).and_then(|m| m.module_id),
|
||||||
source_path: object.metadata.source_path.as_ref().map(|p| p.to_string()),
|
source_path: object.metadata.as_ref().and_then(|m| m.source_path.clone()),
|
||||||
progress_categories: object.metadata.progress_categories.clone().unwrap_or_default(),
|
progress_categories: object
|
||||||
auto_generated: object.metadata.auto_generated,
|
.metadata
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|m| m.progress_categories.clone())
|
||||||
|
.unwrap_or_default(),
|
||||||
|
auto_generated: object.metadata.as_ref().and_then(|m| m.auto_generated),
|
||||||
};
|
};
|
||||||
let mut measures = Measures { total_units: 1, ..Default::default() };
|
let mut measures = Measures { total_units: 1, ..Default::default() };
|
||||||
let mut sections = vec![];
|
let mut sections = vec![];
|
||||||
@ -209,16 +205,15 @@ fn report_object(
|
|||||||
|
|
||||||
let obj = target.as_ref().or(base.as_ref()).unwrap();
|
let obj = target.as_ref().or(base.as_ref()).unwrap();
|
||||||
let obj_diff = result.left.as_ref().or(result.right.as_ref()).unwrap();
|
let obj_diff = result.left.as_ref().or(result.right.as_ref()).unwrap();
|
||||||
for ((section_idx, section), section_diff) in
|
for (section, section_diff) in obj.sections.iter().zip(&obj_diff.sections) {
|
||||||
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(|| {
|
let section_match_percent = section_diff.match_percent.unwrap_or_else(|| {
|
||||||
// Support cases where we don't have a target object,
|
// Support cases where we don't have a target object,
|
||||||
// assume complete means 100% match
|
// assume complete means 100% match
|
||||||
if object.complete.unwrap_or(false) { 100.0 } else { 0.0 }
|
if object.complete().unwrap_or(false) {
|
||||||
|
100.0
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
}
|
||||||
});
|
});
|
||||||
sections.push(ReportItem {
|
sections.push(ReportItem {
|
||||||
name: section.name.clone(),
|
name: section.name.clone(),
|
||||||
@ -228,31 +223,26 @@ fn report_object(
|
|||||||
demangled_name: None,
|
demangled_name: None,
|
||||||
virtual_address: section.virtual_address,
|
virtual_address: section.virtual_address,
|
||||||
}),
|
}),
|
||||||
address: None,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
match section.kind {
|
match section.kind {
|
||||||
SectionKind::Data | SectionKind::Bss => {
|
ObjSectionKind::Data | ObjSectionKind::Bss => {
|
||||||
measures.total_data += section.size;
|
measures.total_data += section.size;
|
||||||
if section_match_percent == 100.0 {
|
if section_match_percent == 100.0 {
|
||||||
measures.matched_data += section.size;
|
measures.matched_data += section.size;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
_ => {}
|
ObjSectionKind::Code => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
for (symbol, symbol_diff) in obj.symbols.iter().zip(&obj_diff.symbols) {
|
for (symbol, symbol_diff) in section.symbols.iter().zip(§ion_diff.symbols) {
|
||||||
if symbol.section != Some(section_idx)
|
if symbol.size == 0 || symbol.flags.0.contains(ObjSymbolFlags::Hidden) {
|
||||||
|| symbol.size == 0
|
|
||||||
|| symbol.flags.contains(SymbolFlag::Hidden)
|
|
||||||
|| symbol.flags.contains(SymbolFlag::Ignored)
|
|
||||||
{
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if let Some(existing_functions) = &mut existing_functions {
|
if let Some(existing_functions) = &mut existing_functions {
|
||||||
if (symbol.flags.contains(SymbolFlag::Global)
|
if (symbol.flags.0.contains(ObjSymbolFlags::Global)
|
||||||
|| symbol.flags.contains(SymbolFlag::Weak))
|
|| symbol.flags.0.contains(ObjSymbolFlags::Weak))
|
||||||
&& !existing_functions.insert(symbol.name.clone())
|
&& !existing_functions.insert(symbol.name.clone())
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
@ -261,7 +251,11 @@ fn report_object(
|
|||||||
let match_percent = symbol_diff.match_percent.unwrap_or_else(|| {
|
let match_percent = symbol_diff.match_percent.unwrap_or_else(|| {
|
||||||
// Support cases where we don't have a target object,
|
// Support cases where we don't have a target object,
|
||||||
// assume complete means 100% match
|
// assume complete means 100% match
|
||||||
if object.complete.unwrap_or(false) { 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.fuzzy_match_percent += match_percent * symbol.size as f32;
|
||||||
measures.total_code += symbol.size;
|
measures.total_code += symbol.size;
|
||||||
@ -276,7 +270,6 @@ fn report_object(
|
|||||||
demangled_name: symbol.demangled_name.clone(),
|
demangled_name: symbol.demangled_name.clone(),
|
||||||
virtual_address: symbol.virtual_address,
|
virtual_address: symbol.virtual_address,
|
||||||
}),
|
}),
|
||||||
address: symbol.address.checked_sub(section.address),
|
|
||||||
});
|
});
|
||||||
if match_percent == 100.0 {
|
if match_percent == 100.0 {
|
||||||
measures.matched_functions += 1;
|
measures.matched_functions += 1;
|
||||||
@ -284,16 +277,6 @@ fn report_object(
|
|||||||
measures.total_functions += 1;
|
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) {
|
if metadata.complete.unwrap_or(false) {
|
||||||
measures.complete_code = measures.total_code;
|
measures.complete_code = measures.total_code;
|
||||||
measures.complete_data = measures.total_data;
|
measures.complete_data = measures.total_data;
|
||||||
@ -302,7 +285,7 @@ fn report_object(
|
|||||||
measures.calc_fuzzy_match_percent();
|
measures.calc_fuzzy_match_percent();
|
||||||
measures.calc_matched_percent();
|
measures.calc_matched_percent();
|
||||||
Ok(Some(ReportUnit {
|
Ok(Some(ReportUnit {
|
||||||
name: object.name.clone(),
|
name: object.name().to_string(),
|
||||||
measures: Some(measures),
|
measures: Some(measures),
|
||||||
sections,
|
sections,
|
||||||
functions,
|
functions,
|
||||||
@ -312,7 +295,7 @@ fn report_object(
|
|||||||
|
|
||||||
fn changes(args: ChangesArgs) -> Result<()> {
|
fn changes(args: ChangesArgs) -> Result<()> {
|
||||||
let output_format = OutputFormat::from_option(args.format.as_deref())?;
|
let output_format = OutputFormat::from_option(args.format.as_deref())?;
|
||||||
let (previous, current) = if args.previous == "-" && args.current == "-" {
|
let (previous, current) = if args.previous == Path::new("-") && args.current == Path::new("-") {
|
||||||
// Special case for comparing two reports from stdin
|
// Special case for comparing two reports from stdin
|
||||||
let mut data = vec![];
|
let mut data = vec![];
|
||||||
std::io::stdin().read_to_end(&mut data)?;
|
std::io::stdin().read_to_end(&mut data)?;
|
||||||
@ -427,14 +410,15 @@ fn process_new_items(items: &[ReportItem]) -> Vec<ChangeItem> {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_report(path: &Utf8PlatformPath) -> Result<Report> {
|
fn read_report(path: &Path) -> Result<Report> {
|
||||||
if path == Utf8PlatformPath::new("-") {
|
if path == Path::new("-") {
|
||||||
let mut data = vec![];
|
let mut data = vec![];
|
||||||
std::io::stdin().read_to_end(&mut data)?;
|
std::io::stdin().read_to_end(&mut data)?;
|
||||||
return Report::parse(&data).with_context(|| "Failed to load report from stdin");
|
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.display()))?;
|
||||||
let mmap =
|
let mmap = unsafe { memmap2::Mmap::map(&file) }
|
||||||
unsafe { memmap2::Mmap::map(&file) }.with_context(|| format!("Failed to map {}", path))?;
|
.with_context(|| format!("Failed to map {}", path.display()))?;
|
||||||
Report::parse(mmap.as_ref()).with_context(|| format!("Failed to load report {}", path))
|
Report::parse(mmap.as_ref())
|
||||||
|
.with_context(|| format!("Failed to load report {}", path.display()))
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
#![allow(clippy::too_many_arguments)]
|
|
||||||
|
|
||||||
mod argp_version;
|
mod argp_version;
|
||||||
mod cmd;
|
mod cmd;
|
||||||
mod util;
|
mod util;
|
||||||
mod views;
|
|
||||||
|
|
||||||
// musl's allocator is very slow, so use mimalloc when targeting musl.
|
// musl's allocator is very slow, so use mimalloc when targeting musl.
|
||||||
// Otherwise, use the system allocator to avoid extra code size.
|
// Otherwise, use the system allocator to avoid extra code size.
|
||||||
@ -17,7 +14,7 @@ use anyhow::{Error, Result};
|
|||||||
use argp::{FromArgValue, FromArgs};
|
use argp::{FromArgValue, FromArgs};
|
||||||
use enable_ansi_support::enable_ansi_support;
|
use enable_ansi_support::enable_ansi_support;
|
||||||
use supports_color::Stream;
|
use supports_color::Stream;
|
||||||
use tracing_subscriber::{EnvFilter, filter::LevelFilter};
|
use tracing_subscriber::{filter::LevelFilter, EnvFilter};
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
|
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
|
||||||
enum LogLevel {
|
enum LogLevel {
|
||||||
|
@ -5,7 +5,7 @@ use std::{
|
|||||||
path::Path,
|
path::Path,
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::{Context, Result, bail};
|
use anyhow::{bail, Context, Result};
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
|
||||||
@ -34,12 +34,9 @@ impl OutputFormat {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_output<T, P>(input: &T, output: Option<P>, format: OutputFormat) -> Result<()>
|
pub fn write_output<T>(input: &T, output: Option<&Path>, format: OutputFormat) -> Result<()>
|
||||||
where
|
where T: serde::Serialize + prost::Message {
|
||||||
T: serde::Serialize + prost::Message,
|
match output {
|
||||||
P: AsRef<Path>,
|
|
||||||
{
|
|
||||||
match output.as_ref().map(|p| p.as_ref()) {
|
|
||||||
Some(output) if output != Path::new("-") => {
|
Some(output) if output != Path::new("-") => {
|
||||||
info!("Writing to {}", output.display());
|
info!("Writing to {}", output.display());
|
||||||
let file = File::options()
|
let file = File::options()
|
||||||
|
@ -3,7 +3,7 @@ use std::{io::stdout, panic};
|
|||||||
use crossterm::{
|
use crossterm::{
|
||||||
cursor::Show,
|
cursor::Show,
|
||||||
event::DisableMouseCapture,
|
event::DisableMouseCapture,
|
||||||
terminal::{LeaveAlternateScreen, disable_raw_mode},
|
terminal::{disable_raw_mode, LeaveAlternateScreen},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn crossterm_panic_handler() {
|
pub fn crossterm_panic_handler() {
|
||||||
|
@ -1,652 +0,0 @@
|
|||||||
use core::cmp::Ordering;
|
|
||||||
|
|
||||||
use anyhow::Result;
|
|
||||||
use crossterm::event::{Event, KeyCode, KeyEventKind, KeyModifiers, MouseButton, MouseEventKind};
|
|
||||||
use objdiff_core::{
|
|
||||||
build::BuildStatus,
|
|
||||||
diff::{
|
|
||||||
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},
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{EventControlFlow, EventResult, UiView};
|
|
||||||
use crate::cmd::diff::AppState;
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct FunctionDiffUi {
|
|
||||||
pub symbol_name: String,
|
|
||||||
pub left_highlight: HighlightKind,
|
|
||||||
pub right_highlight: HighlightKind,
|
|
||||||
pub scroll_x: usize,
|
|
||||||
pub scroll_state_x: ScrollbarState,
|
|
||||||
pub scroll_y: usize,
|
|
||||||
pub scroll_state_y: ScrollbarState,
|
|
||||||
pub per_page: usize,
|
|
||||||
pub num_rows: usize,
|
|
||||||
pub left_sym: Option<usize>,
|
|
||||||
pub right_sym: Option<usize>,
|
|
||||||
pub prev_sym: Option<usize>,
|
|
||||||
pub open_options: bool,
|
|
||||||
pub three_way: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UiView for FunctionDiffUi {
|
|
||||||
fn draw(&mut self, state: &AppState, f: &mut Frame, result: &mut EventResult) {
|
|
||||||
let chunks = Layout::vertical([Constraint::Length(1), Constraint::Fill(1)]).split(f.area());
|
|
||||||
let header_chunks = Layout::horizontal([
|
|
||||||
Constraint::Fill(1),
|
|
||||||
Constraint::Length(3),
|
|
||||||
Constraint::Fill(1),
|
|
||||||
Constraint::Length(2),
|
|
||||||
])
|
|
||||||
.split(chunks[0]);
|
|
||||||
let content_chunks = if self.three_way {
|
|
||||||
Layout::horizontal([
|
|
||||||
Constraint::Fill(1),
|
|
||||||
Constraint::Length(3),
|
|
||||||
Constraint::Fill(1),
|
|
||||||
Constraint::Length(3),
|
|
||||||
Constraint::Fill(1),
|
|
||||||
Constraint::Length(2),
|
|
||||||
])
|
|
||||||
.split(chunks[1])
|
|
||||||
} else {
|
|
||||||
Layout::horizontal([
|
|
||||||
Constraint::Fill(1),
|
|
||||||
Constraint::Length(3),
|
|
||||||
Constraint::Fill(1),
|
|
||||||
Constraint::Length(2),
|
|
||||||
])
|
|
||||||
.split(chunks[1])
|
|
||||||
};
|
|
||||||
|
|
||||||
self.per_page = chunks[1].height.saturating_sub(2) as usize;
|
|
||||||
let max_scroll_y = self.num_rows.saturating_sub(self.per_page);
|
|
||||||
if self.scroll_y > max_scroll_y {
|
|
||||||
self.scroll_y = max_scroll_y;
|
|
||||||
}
|
|
||||||
self.scroll_state_y =
|
|
||||||
self.scroll_state_y.content_length(max_scroll_y).position(self.scroll_y);
|
|
||||||
|
|
||||||
let mut line_l = Line::default();
|
|
||||||
line_l
|
|
||||||
.spans
|
|
||||||
.push(Span::styled(self.symbol_name.clone(), Style::new().fg(Color::White).bold()));
|
|
||||||
f.render_widget(line_l, header_chunks[0]);
|
|
||||||
|
|
||||||
let mut line_r = Line::default();
|
|
||||||
if let Some(percent) = get_symbol(state.right_obj.as_ref(), self.right_sym)
|
|
||||||
.and_then(|(_, _, d)| d.match_percent)
|
|
||||||
{
|
|
||||||
line_r.spans.push(Span::styled(
|
|
||||||
format!("{:.2}% ", percent),
|
|
||||||
Style::new().fg(match_percent_color(percent)),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
let reload_time = state
|
|
||||||
.reload_time
|
|
||||||
.as_ref()
|
|
||||||
.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),
|
|
||||||
Style::new().fg(Color::White),
|
|
||||||
));
|
|
||||||
line_r.spans.push(Span::styled(
|
|
||||||
format!(" ({} jobs)", state.jobs.jobs.len()),
|
|
||||||
Style::new().fg(Color::LightYellow),
|
|
||||||
));
|
|
||||||
f.render_widget(line_r, header_chunks[2]);
|
|
||||||
|
|
||||||
let mut left_text = None;
|
|
||||||
let mut left_highlight = None;
|
|
||||||
let mut max_width = 0;
|
|
||||||
if let Some((obj, symbol_idx, symbol_diff)) =
|
|
||||||
get_symbol(state.left_obj.as_ref(), self.left_sym)
|
|
||||||
{
|
|
||||||
let mut text = Text::default();
|
|
||||||
let rect = content_chunks[0].inner(Margin::new(0, 1));
|
|
||||||
left_highlight = self.print_sym(
|
|
||||||
&mut text,
|
|
||||||
obj,
|
|
||||||
symbol_idx,
|
|
||||||
symbol_diff,
|
|
||||||
&state.diff_obj_config,
|
|
||||||
rect,
|
|
||||||
&self.left_highlight,
|
|
||||||
result,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
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;
|
|
||||||
let mut right_highlight = None;
|
|
||||||
let mut margin_text = None;
|
|
||||||
if let Some((obj, symbol_idx, symbol_diff)) =
|
|
||||||
get_symbol(state.right_obj.as_ref(), self.right_sym)
|
|
||||||
{
|
|
||||||
let mut text = Text::default();
|
|
||||||
let rect = content_chunks[2].inner(Margin::new(0, 1));
|
|
||||||
right_highlight = self.print_sym(
|
|
||||||
&mut text,
|
|
||||||
obj,
|
|
||||||
symbol_idx,
|
|
||||||
symbol_diff,
|
|
||||||
&state.diff_obj_config,
|
|
||||||
rect,
|
|
||||||
&self.right_highlight,
|
|
||||||
result,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
max_width = max_width.max(text.width());
|
|
||||||
right_text = Some(text);
|
|
||||||
|
|
||||||
// Render margin
|
|
||||||
let mut text = Text::default();
|
|
||||||
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)) =
|
|
||||||
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);
|
|
||||||
|
|
||||||
// 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 =
|
|
||||||
max_width.saturating_sub(content_chunks[0].width.min(content_chunks[2].width) as usize);
|
|
||||||
if self.scroll_x > max_scroll_x {
|
|
||||||
self.scroll_x = max_scroll_x;
|
|
||||||
}
|
|
||||||
self.scroll_state_x =
|
|
||||||
self.scroll_state_x.content_length(max_scroll_x).position(self.scroll_x);
|
|
||||||
|
|
||||||
if let Some(text) = left_text {
|
|
||||||
// Render left column
|
|
||||||
f.render_widget(
|
|
||||||
Paragraph::new(text)
|
|
||||||
.block(
|
|
||||||
Block::new()
|
|
||||||
.borders(Borders::TOP)
|
|
||||||
.border_style(Style::new().fg(Color::Gray))
|
|
||||||
.title_style(Style::new().bold())
|
|
||||||
.title("TARGET"),
|
|
||||||
)
|
|
||||||
.scroll((0, self.scroll_x as u16)),
|
|
||||||
content_chunks[0],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if let Some(text) = margin_text {
|
|
||||||
f.render_widget(text, content_chunks[1].inner(Margin::new(1, 1)));
|
|
||||||
}
|
|
||||||
if let Some(text) = right_text {
|
|
||||||
f.render_widget(
|
|
||||||
Paragraph::new(text)
|
|
||||||
.block(
|
|
||||||
Block::new()
|
|
||||||
.borders(Borders::TOP)
|
|
||||||
.border_style(Style::new().fg(Color::Gray))
|
|
||||||
.title_style(Style::new().bold())
|
|
||||||
.title("CURRENT"),
|
|
||||||
)
|
|
||||||
.scroll((0, self.scroll_x as u16)),
|
|
||||||
content_chunks[2],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.three_way {
|
|
||||||
if let Some(text) = prev_margin_text {
|
|
||||||
f.render_widget(text, content_chunks[3].inner(Margin::new(1, 1)));
|
|
||||||
}
|
|
||||||
let block = Block::new()
|
|
||||||
.borders(Borders::TOP)
|
|
||||||
.border_style(Style::new().fg(Color::Gray))
|
|
||||||
.title_style(Style::new().bold())
|
|
||||||
.title("SAVED");
|
|
||||||
if let Some(text) = prev_text {
|
|
||||||
f.render_widget(
|
|
||||||
Paragraph::new(text).block(block.clone()).scroll((0, self.scroll_x as u16)),
|
|
||||||
content_chunks[4],
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
f.render_widget(block, content_chunks[4]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Render scrollbars
|
|
||||||
f.render_stateful_widget(
|
|
||||||
Scrollbar::new(ScrollbarOrientation::VerticalRight).begin_symbol(None).end_symbol(None),
|
|
||||||
chunks[1].inner(Margin::new(0, 1)),
|
|
||||||
&mut self.scroll_state_y,
|
|
||||||
);
|
|
||||||
f.render_stateful_widget(
|
|
||||||
Scrollbar::new(ScrollbarOrientation::HorizontalBottom).thumb_symbol("■"),
|
|
||||||
content_chunks[0],
|
|
||||||
&mut self.scroll_state_x,
|
|
||||||
);
|
|
||||||
f.render_stateful_widget(
|
|
||||||
Scrollbar::new(ScrollbarOrientation::HorizontalBottom).thumb_symbol("■"),
|
|
||||||
content_chunks[2],
|
|
||||||
&mut self.scroll_state_x,
|
|
||||||
);
|
|
||||||
if self.three_way {
|
|
||||||
f.render_stateful_widget(
|
|
||||||
Scrollbar::new(ScrollbarOrientation::HorizontalBottom).thumb_symbol("■"),
|
|
||||||
content_chunks[4],
|
|
||||||
&mut self.scroll_state_x,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(new_highlight) = left_highlight {
|
|
||||||
if new_highlight == self.left_highlight {
|
|
||||||
if self.left_highlight != self.right_highlight {
|
|
||||||
self.right_highlight = self.left_highlight.clone();
|
|
||||||
} else {
|
|
||||||
self.left_highlight = HighlightKind::None;
|
|
||||||
self.right_highlight = HighlightKind::None;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.left_highlight = new_highlight;
|
|
||||||
}
|
|
||||||
result.redraw = true;
|
|
||||||
} else if let Some(new_highlight) = right_highlight {
|
|
||||||
if new_highlight == self.right_highlight {
|
|
||||||
if self.left_highlight != self.right_highlight {
|
|
||||||
self.left_highlight = self.right_highlight.clone();
|
|
||||||
} else {
|
|
||||||
self.left_highlight = HighlightKind::None;
|
|
||||||
self.right_highlight = HighlightKind::None;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.right_highlight = new_highlight;
|
|
||||||
}
|
|
||||||
result.redraw = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.open_options {
|
|
||||||
self.draw_options(f, result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_event(&mut self, state: &mut AppState, event: Event) -> EventControlFlow {
|
|
||||||
let mut result = EventResult::default();
|
|
||||||
match event {
|
|
||||||
Event::Key(event)
|
|
||||||
if matches!(event.kind, KeyEventKind::Press | KeyEventKind::Repeat) =>
|
|
||||||
{
|
|
||||||
match event.code {
|
|
||||||
// Quit
|
|
||||||
KeyCode::Esc | KeyCode::Char('q') => return EventControlFlow::Break,
|
|
||||||
// Page up
|
|
||||||
KeyCode::PageUp => {
|
|
||||||
self.page_up(false);
|
|
||||||
result.redraw = true;
|
|
||||||
}
|
|
||||||
// Page up (shift + space)
|
|
||||||
KeyCode::Char(' ') if event.modifiers.contains(KeyModifiers::SHIFT) => {
|
|
||||||
self.page_up(false);
|
|
||||||
result.redraw = true;
|
|
||||||
}
|
|
||||||
// Page down
|
|
||||||
KeyCode::Char(' ') | KeyCode::PageDown => {
|
|
||||||
self.page_down(false);
|
|
||||||
result.redraw = true;
|
|
||||||
}
|
|
||||||
// Page down (ctrl + f)
|
|
||||||
KeyCode::Char('f') if event.modifiers.contains(KeyModifiers::CONTROL) => {
|
|
||||||
self.page_down(false);
|
|
||||||
result.redraw = true;
|
|
||||||
}
|
|
||||||
// Page up (ctrl + b)
|
|
||||||
KeyCode::Char('b') if event.modifiers.contains(KeyModifiers::CONTROL) => {
|
|
||||||
self.page_up(false);
|
|
||||||
result.redraw = true;
|
|
||||||
}
|
|
||||||
// Half page down (ctrl + d)
|
|
||||||
KeyCode::Char('d') if event.modifiers.contains(KeyModifiers::CONTROL) => {
|
|
||||||
self.page_down(true);
|
|
||||||
result.redraw = true;
|
|
||||||
}
|
|
||||||
// Half page up (ctrl + u)
|
|
||||||
KeyCode::Char('u') if event.modifiers.contains(KeyModifiers::CONTROL) => {
|
|
||||||
self.page_up(true);
|
|
||||||
result.redraw = true;
|
|
||||||
}
|
|
||||||
// Scroll down
|
|
||||||
KeyCode::Down | KeyCode::Char('j') => {
|
|
||||||
self.scroll_y += 1;
|
|
||||||
result.redraw = true;
|
|
||||||
}
|
|
||||||
// Scroll up
|
|
||||||
KeyCode::Up | KeyCode::Char('k') => {
|
|
||||||
self.scroll_y = self.scroll_y.saturating_sub(1);
|
|
||||||
result.redraw = true;
|
|
||||||
}
|
|
||||||
// Scroll to start
|
|
||||||
KeyCode::Char('g') => {
|
|
||||||
self.scroll_y = 0;
|
|
||||||
result.redraw = true;
|
|
||||||
}
|
|
||||||
// Scroll to end
|
|
||||||
KeyCode::Char('G') => {
|
|
||||||
self.scroll_y = self.num_rows;
|
|
||||||
result.redraw = true;
|
|
||||||
}
|
|
||||||
// Reload
|
|
||||||
KeyCode::Char('r') => {
|
|
||||||
result.redraw = true;
|
|
||||||
return EventControlFlow::Reload;
|
|
||||||
}
|
|
||||||
// Scroll right
|
|
||||||
KeyCode::Right | KeyCode::Char('l') => {
|
|
||||||
self.scroll_x += 1;
|
|
||||||
result.redraw = true;
|
|
||||||
}
|
|
||||||
// Scroll left
|
|
||||||
KeyCode::Left | KeyCode::Char('h') => {
|
|
||||||
self.scroll_x = self.scroll_x.saturating_sub(1);
|
|
||||||
result.redraw = true;
|
|
||||||
}
|
|
||||||
// Cycle through function relocation diff mode
|
|
||||||
KeyCode::Char('x') => {
|
|
||||||
state.diff_obj_config.function_reloc_diffs =
|
|
||||||
match state.diff_obj_config.function_reloc_diffs {
|
|
||||||
FunctionRelocDiffs::None => FunctionRelocDiffs::NameAddress,
|
|
||||||
FunctionRelocDiffs::NameAddress => FunctionRelocDiffs::DataValue,
|
|
||||||
FunctionRelocDiffs::DataValue => FunctionRelocDiffs::All,
|
|
||||||
FunctionRelocDiffs::All => FunctionRelocDiffs::None,
|
|
||||||
};
|
|
||||||
result.redraw = true;
|
|
||||||
return EventControlFlow::Reload;
|
|
||||||
}
|
|
||||||
// Toggle three-way diff
|
|
||||||
KeyCode::Char('3') => {
|
|
||||||
self.three_way = !self.three_way;
|
|
||||||
result.redraw = true;
|
|
||||||
}
|
|
||||||
// Toggle options
|
|
||||||
KeyCode::Char('o') => {
|
|
||||||
self.open_options = !self.open_options;
|
|
||||||
result.redraw = true;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Event::Mouse(event) => match event.kind {
|
|
||||||
MouseEventKind::ScrollDown => {
|
|
||||||
self.scroll_y += 3;
|
|
||||||
result.redraw = true;
|
|
||||||
}
|
|
||||||
MouseEventKind::ScrollUp => {
|
|
||||||
self.scroll_y = self.scroll_y.saturating_sub(3);
|
|
||||||
result.redraw = true;
|
|
||||||
}
|
|
||||||
MouseEventKind::ScrollRight => {
|
|
||||||
self.scroll_x += 3;
|
|
||||||
result.redraw = true;
|
|
||||||
}
|
|
||||||
MouseEventKind::ScrollLeft => {
|
|
||||||
self.scroll_x = self.scroll_x.saturating_sub(3);
|
|
||||||
result.redraw = true;
|
|
||||||
}
|
|
||||||
MouseEventKind::Down(MouseButton::Left) => {
|
|
||||||
result.click_xy = Some((event.column, event.row));
|
|
||||||
result.redraw = true;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
},
|
|
||||||
Event::Resize(_, _) => {
|
|
||||||
result.redraw = true;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
EventControlFlow::Continue(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn reload(&mut self, state: &AppState) -> Result<()> {
|
|
||||||
let left_sym =
|
|
||||||
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, _)| o.symbol_by_name(&self.symbol_name));
|
|
||||||
let prev_sym =
|
|
||||||
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),
|
|
||||||
) {
|
|
||||||
(Some((_l, _ls, ld)), Some((_r, _rs, rd))) => {
|
|
||||||
ld.instruction_rows.len().max(rd.instruction_rows.len())
|
|
||||||
}
|
|
||||||
(Some((_l, _ls, ld)), None) => ld.instruction_rows.len(),
|
|
||||||
(None, Some((_r, _rs, rd))) => rd.instruction_rows.len(),
|
|
||||||
(None, None) => 0,
|
|
||||||
};
|
|
||||||
self.left_sym = left_sym;
|
|
||||||
self.right_sym = right_sym;
|
|
||||||
self.prev_sym = prev_sym;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FunctionDiffUi {
|
|
||||||
pub fn draw_options(&mut self, f: &mut Frame, _result: &mut EventResult) {
|
|
||||||
let percent_x = 50;
|
|
||||||
let percent_y = 50;
|
|
||||||
let popup_rect = Layout::vertical([
|
|
||||||
Constraint::Percentage((100 - percent_y) / 2),
|
|
||||||
Constraint::Percentage(percent_y),
|
|
||||||
Constraint::Percentage((100 - percent_y) / 2),
|
|
||||||
])
|
|
||||||
.split(f.area())[1];
|
|
||||||
let popup_rect = Layout::horizontal([
|
|
||||||
Constraint::Percentage((100 - percent_x) / 2),
|
|
||||||
Constraint::Percentage(percent_x),
|
|
||||||
Constraint::Percentage((100 - percent_x) / 2),
|
|
||||||
])
|
|
||||||
.split(popup_rect)[1];
|
|
||||||
|
|
||||||
let popup = Block::default()
|
|
||||||
.borders(Borders::ALL)
|
|
||||||
.title("Options")
|
|
||||||
.title_style(Style::default().fg(Color::White).bg(Color::Black));
|
|
||||||
f.render_widget(Clear, popup_rect);
|
|
||||||
f.render_widget(popup, popup_rect);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn page_up(&mut self, half: bool) {
|
|
||||||
self.scroll_y = self.scroll_y.saturating_sub(self.per_page / if half { 2 } else { 1 });
|
|
||||||
}
|
|
||||||
|
|
||||||
fn page_down(&mut self, half: bool) {
|
|
||||||
self.scroll_y += self.per_page / if half { 2 } else { 1 };
|
|
||||||
}
|
|
||||||
|
|
||||||
fn print_sym(
|
|
||||||
&self,
|
|
||||||
out: &mut Text<'static>,
|
|
||||||
obj: &Object,
|
|
||||||
symbol_index: usize,
|
|
||||||
symbol_diff: &SymbolDiff,
|
|
||||||
diff_config: &DiffObjConfig,
|
|
||||||
rect: Rect,
|
|
||||||
highlight: &HighlightKind,
|
|
||||||
result: &EventResult,
|
|
||||||
only_changed: bool,
|
|
||||||
) -> Option<HighlightKind> {
|
|
||||||
let mut new_highlight = None;
|
|
||||||
for (y, ins_row) in symbol_diff
|
|
||||||
.instruction_rows
|
|
||||||
.iter()
|
|
||||||
.skip(self.scroll_y)
|
|
||||||
.take(rect.height as usize)
|
|
||||||
.enumerate()
|
|
||||||
{
|
|
||||||
if only_changed && ins_row.kind == InstructionDiffKind::None {
|
|
||||||
out.lines.push(Line::default());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let mut sx = rect.x;
|
|
||||||
let sy = rect.y + y as u16;
|
|
||||||
let mut line = Line::default();
|
|
||||||
display_row(obj, symbol_index, ins_row, diff_config, |segment| {
|
|
||||||
let highlight_kind = HighlightKind::from(&segment.text);
|
|
||||||
let label_text = match segment.text {
|
|
||||||
DiffText::Basic(text) => text.to_string(),
|
|
||||||
DiffText::Line(num) => format!("{num} "),
|
|
||||||
DiffText::Address(addr) => format!("{:x}:", addr),
|
|
||||||
DiffText::Opcode(mnemonic, _op) => format!("{mnemonic} "),
|
|
||||||
DiffText::Argument(arg) => arg.to_string(),
|
|
||||||
DiffText::BranchDest(addr) => format!("{addr:x}"),
|
|
||||||
DiffText::Symbol(sym) => {
|
|
||||||
sym.demangled_name.as_ref().unwrap_or(&sym.name).clone()
|
|
||||||
}
|
|
||||||
DiffText::Addend(addend) => match addend.cmp(&0i64) {
|
|
||||||
Ordering::Greater => format!("+{:#x}", addend),
|
|
||||||
Ordering::Less => format!("-{:#x}", -addend),
|
|
||||||
_ => String::new(),
|
|
||||||
},
|
|
||||||
DiffText::Spacing(n) => {
|
|
||||||
line.spans.push(Span::raw(" ".repeat(n as usize)));
|
|
||||||
sx += n as u16;
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
DiffText::Eol => return Ok(()),
|
|
||||||
};
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let mut style = Style::new().fg(match segment.color {
|
|
||||||
DiffTextColor::Normal => Color::Gray,
|
|
||||||
DiffTextColor::Dim => Color::DarkGray,
|
|
||||||
DiffTextColor::Bright => Color::White,
|
|
||||||
DiffTextColor::Replace => Color::Cyan,
|
|
||||||
DiffTextColor::Delete => Color::Red,
|
|
||||||
DiffTextColor::Insert => Color::Green,
|
|
||||||
DiffTextColor::Rotating(i) => COLOR_ROTATION[i as usize % COLOR_ROTATION.len()],
|
|
||||||
});
|
|
||||||
if highlighted {
|
|
||||||
style = style.bg(Color::DarkGray);
|
|
||||||
}
|
|
||||||
line.spans.push(Span::styled(label_text, style));
|
|
||||||
sx += len as u16;
|
|
||||||
if segment.pad_to as usize > len {
|
|
||||||
let pad = (segment.pad_to as usize - len) as u16;
|
|
||||||
line.spans.push(Span::raw(" ".repeat(pad as usize)));
|
|
||||||
sx += pad;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
out.lines.push(line);
|
|
||||||
}
|
|
||||||
new_highlight
|
|
||||||
}
|
|
||||||
|
|
||||||
fn print_margin(&self, out: &mut Text, symbol: &SymbolDiff, rect: Rect) {
|
|
||||||
for ins_row in symbol.instruction_rows.iter().skip(self.scroll_y).take(rect.height as usize)
|
|
||||||
{
|
|
||||||
if ins_row.kind != InstructionDiffKind::None {
|
|
||||||
out.lines.push(Line::raw(match ins_row.kind {
|
|
||||||
InstructionDiffKind::Delete => "<",
|
|
||||||
InstructionDiffKind::Insert => ">",
|
|
||||||
_ => "|",
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
out.lines.push(Line::raw(" "));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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] = [
|
|
||||||
Color::Magenta,
|
|
||||||
Color::Cyan,
|
|
||||||
Color::Green,
|
|
||||||
Color::Red,
|
|
||||||
Color::Yellow,
|
|
||||||
Color::Blue,
|
|
||||||
Color::Green,
|
|
||||||
];
|
|
||||||
|
|
||||||
pub fn match_percent_color(match_percent: f32) -> Color {
|
|
||||||
if match_percent == 100.0 {
|
|
||||||
Color::Green
|
|
||||||
} else if match_percent >= 50.0 {
|
|
||||||
Color::LightBlue
|
|
||||||
} else {
|
|
||||||
Color::LightRed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn get_symbol(
|
|
||||||
obj: Option<&(Object, ObjectDiff)>,
|
|
||||||
sym: Option<usize>,
|
|
||||||
) -> Option<(&Object, usize, &SymbolDiff)> {
|
|
||||||
let (obj, diff) = obj?;
|
|
||||||
let sym = sym?;
|
|
||||||
Some((obj, sym, &diff.symbols[sym]))
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
use anyhow::Result;
|
|
||||||
use crossterm::event::Event;
|
|
||||||
use ratatui::Frame;
|
|
||||||
|
|
||||||
use crate::cmd::diff::AppState;
|
|
||||||
|
|
||||||
pub mod function_diff;
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct EventResult {
|
|
||||||
pub redraw: bool,
|
|
||||||
pub click_xy: Option<(u16, u16)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum EventControlFlow {
|
|
||||||
Break,
|
|
||||||
Continue(EventResult),
|
|
||||||
Reload,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait UiView {
|
|
||||||
fn draw(&mut self, state: &AppState, f: &mut Frame, result: &mut EventResult);
|
|
||||||
fn handle_event(&mut self, state: &mut AppState, event: Event) -> EventControlFlow;
|
|
||||||
fn reload(&mut self, state: &AppState) -> Result<()>;
|
|
||||||
}
|
|
@ -12,193 +12,69 @@ A local diffing tool for decompilation projects.
|
|||||||
"""
|
"""
|
||||||
documentation = "https://docs.rs/objdiff-core"
|
documentation = "https://docs.rs/objdiff-core"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["cdylib", "rlib"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["std"]
|
all = ["config", "dwarf", "mips", "ppc", "x86", "arm", "bindings"]
|
||||||
all = [
|
any-arch = [] # Implicit, used to check if any arch is enabled
|
||||||
# Features
|
config = ["globset", "semver", "serde_json", "serde_yaml"]
|
||||||
"bindings",
|
dwarf = ["gimli"]
|
||||||
"build",
|
mips = ["any-arch", "rabbitizer"]
|
||||||
"config",
|
ppc = ["any-arch", "cwdemangle", "cwextab", "ppc750cl"]
|
||||||
"dwarf",
|
x86 = ["any-arch", "cpp_demangle", "iced-x86", "msvc-demangler"]
|
||||||
"serde",
|
arm = ["any-arch", "cpp_demangle", "unarm", "arm-attr"]
|
||||||
# Architectures
|
bindings = ["serde_json", "prost", "pbjson"]
|
||||||
"arm",
|
wasm = ["bindings", "console_error_panic_hook", "console_log"]
|
||||||
"arm64",
|
|
||||||
"mips",
|
|
||||||
"ppc",
|
|
||||||
"x86",
|
|
||||||
"superh"
|
|
||||||
]
|
|
||||||
# Implicit, used to check if any arch is enabled
|
|
||||||
any-arch = [
|
|
||||||
"dep:flagset",
|
|
||||||
"dep:heck",
|
|
||||||
"dep:log",
|
|
||||||
"dep:num-traits",
|
|
||||||
"dep:prettyplease",
|
|
||||||
"dep:proc-macro2",
|
|
||||||
"dep:quote",
|
|
||||||
"dep:regex",
|
|
||||||
"dep:similar",
|
|
||||||
"dep:syn",
|
|
||||||
"dep:encoding_rs"
|
|
||||||
]
|
|
||||||
bindings = [
|
|
||||||
"dep:prost",
|
|
||||||
"dep:prost-build",
|
|
||||||
]
|
|
||||||
build = [
|
|
||||||
"dep:notify",
|
|
||||||
"dep:notify-debouncer-full",
|
|
||||||
"dep:reqwest",
|
|
||||||
"dep:self_update",
|
|
||||||
"dep:shell-escape",
|
|
||||||
"dep:tempfile",
|
|
||||||
"dep:time",
|
|
||||||
"dep:winapi",
|
|
||||||
]
|
|
||||||
config = [
|
|
||||||
"dep:globset",
|
|
||||||
"dep:semver",
|
|
||||||
"dep:typed-path",
|
|
||||||
]
|
|
||||||
dwarf = ["dep:gimli"]
|
|
||||||
serde = [
|
|
||||||
"dep:pbjson",
|
|
||||||
"dep:pbjson-build",
|
|
||||||
"dep:serde",
|
|
||||||
"dep:serde_json",
|
|
||||||
]
|
|
||||||
std = [
|
|
||||||
"anyhow/std",
|
|
||||||
"flagset?/std",
|
|
||||||
"log?/std",
|
|
||||||
"num-traits?/std",
|
|
||||||
"object/std",
|
|
||||||
"prost?/std",
|
|
||||||
"serde?/std",
|
|
||||||
"similar?/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:rlwinmdec",
|
|
||||||
]
|
|
||||||
x86 = [
|
|
||||||
"any-arch",
|
|
||||||
"dep:cpp_demangle",
|
|
||||||
"dep:iced-x86",
|
|
||||||
"dep:msvc-demangler",
|
|
||||||
]
|
|
||||||
arm = [
|
|
||||||
"any-arch",
|
|
||||||
"dep:arm-attr",
|
|
||||||
"dep:cpp_demangle",
|
|
||||||
"dep:unarm",
|
|
||||||
]
|
|
||||||
arm64 = [
|
|
||||||
"any-arch",
|
|
||||||
"dep:cpp_demangle",
|
|
||||||
"dep:yaxpeax-arch",
|
|
||||||
"dep:yaxpeax-arm",
|
|
||||||
]
|
|
||||||
superh = [
|
|
||||||
"any-arch",
|
|
||||||
]
|
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
features = ["all"]
|
features = ["all"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = { version = "1.0", default-features = false }
|
anyhow = "1.0"
|
||||||
filetime = { version = "0.2", optional = true }
|
byteorder = "1.5"
|
||||||
flagset = { version = "0.4", default-features = false, optional = true, git = "https://github.com/enarx/flagset.git", rev = "a1fe9369b3741e43fec45da1998e83b9d78966a2" }
|
filetime = "0.2"
|
||||||
itertools = { version = "0.14", default-features = false, features = ["use_alloc"] }
|
flagset = "0.4"
|
||||||
log = { version = "0.4", default-features = false, optional = true }
|
log = "0.4"
|
||||||
memmap2 = { version = "0.9", optional = true }
|
memmap2 = "0.9"
|
||||||
num-traits = { version = "0.2", default-features = false, optional = true }
|
num-traits = "0.2"
|
||||||
object = { git = "https://github.com/gimli-rs/object", rev = "a74579249e21ab8fcd3a86be588de336f18297cb", default-features = false, features = ["read_core", "elf", "pe"] }
|
object = { version = "0.36", features = ["read_core", "std", "elf", "pe"], default-features = false }
|
||||||
pbjson = { version = "0.7", default-features = false, optional = true }
|
pbjson = { version = "0.7", optional = true }
|
||||||
prost = { version = "0.13", default-features = false, features = ["prost-derive"], optional = true }
|
prost = { version = "0.13", optional = true }
|
||||||
regex = { version = "1.11", default-features = false, features = [], optional = true }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde = { version = "1.0", default-features = false, features = ["derive"], optional = true }
|
similar = { version = "2.6", default-features = false }
|
||||||
similar = { version = "2.7", default-features = false, features = ["hashbrown"], optional = true, git = "https://github.com/encounter/similar.git", branch = "no_std" }
|
strum = { version = "0.26", features = ["derive"] }
|
||||||
typed-path = { version = "0.11", default-features = false, optional = true }
|
wasm-bindgen = "0.2"
|
||||||
|
tsify-next = { version = "0.5", default-features = false, features = ["js"] }
|
||||||
|
console_log = { version = "1.0", optional = true }
|
||||||
|
console_error_panic_hook = { version = "0.1", optional = true }
|
||||||
|
|
||||||
# config
|
# config
|
||||||
globset = { version = "0.4", default-features = false, optional = true }
|
globset = { version = "0.4", features = ["serde1"], optional = true }
|
||||||
semver = { version = "1.0", default-features = false, optional = true }
|
semver = { version = "1.0", optional = true }
|
||||||
serde_json = { version = "1.0", default-features = false, features = ["alloc"], optional = true }
|
serde_json = { version = "1.0", optional = true }
|
||||||
|
serde_yaml = { version = "0.9", optional = true }
|
||||||
|
|
||||||
# dwarf
|
# dwarf
|
||||||
gimli = { version = "0.31", default-features = false, features = ["read"], optional = true }
|
gimli = { version = "0.31", default-features = false, features = ["read-all"], optional = true }
|
||||||
|
|
||||||
# ppc
|
# ppc
|
||||||
cwdemangle = { version = "1.0", optional = true }
|
cwdemangle = { version = "1.0", optional = true }
|
||||||
cwextab = { version = "1.0", optional = true }
|
cwextab = { version = "0.3", optional = true }
|
||||||
ppc750cl = { version = "0.3", optional = true }
|
ppc750cl = { version = "0.3", optional = true }
|
||||||
rlwinmdec = { version = "1.1", optional = true }
|
|
||||||
|
|
||||||
# mips
|
# mips
|
||||||
rabbitizer = { version = "2.0.0-alpha.1", default-features = false, features = ["all_extensions"], optional = true }
|
rabbitizer = { version = "1.12", optional = true }
|
||||||
|
|
||||||
# x86
|
# x86
|
||||||
cpp_demangle = { version = "0.4", default-features = false, features = ["alloc"], optional = true }
|
cpp_demangle = { version = "0.4", optional = true }
|
||||||
iced-x86 = { version = "1.21", default-features = false, features = ["decoder", "intel", "gas", "masm", "nasm", "exhaustive_enums", "no_std"], optional = true }
|
iced-x86 = { version = "1.21", default-features = false, features = ["std", "decoder", "intel", "gas", "masm", "nasm", "exhaustive_enums"], optional = true }
|
||||||
msvc-demangler = { version = "0.11", optional = true }
|
msvc-demangler = { version = "0.10", optional = true }
|
||||||
|
|
||||||
# arm
|
# arm
|
||||||
unarm = { version = "1.8", optional = true }
|
unarm = { version = "1.6", optional = true }
|
||||||
arm-attr = { version = "0.2", optional = true }
|
arm-attr = { version = "0.1", optional = true }
|
||||||
|
|
||||||
# arm64
|
|
||||||
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-debouncer-full = { version = "0.5.0", optional = true }
|
|
||||||
shell-escape = { version = "0.1", optional = true }
|
|
||||||
tempfile = { version = "3.19", 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 }
|
|
||||||
|
|
||||||
# For Linux static binaries, use rustls
|
|
||||||
[target.'cfg(target_os = "linux")'.dependencies]
|
|
||||||
reqwest = { version = "0.12", default-features = false, features = ["blocking", "json", "multipart", "rustls-tls"], optional = true }
|
|
||||||
self_update = { version = "0.42", default-features = false, features = ["rustls"], optional = true }
|
|
||||||
|
|
||||||
# For all other platforms, use native TLS
|
|
||||||
[target.'cfg(not(target_os = "linux"))'.dependencies]
|
|
||||||
reqwest = { version = "0.12", default-features = false, features = ["blocking", "json", "multipart", "default-tls"], optional = true }
|
|
||||||
self_update = { version = "0.42", optional = true }
|
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
heck = { version = "0.5", optional = true }
|
prost-build = "0.13"
|
||||||
pbjson-build = { version = "0.7", optional = true }
|
pbjson-build = "0.7"
|
||||||
prettyplease = { version = "0.2", optional = true }
|
|
||||||
proc-macro2 = { version = "1.0", optional = true }
|
|
||||||
prost-build = { version = "0.13", optional = true }
|
|
||||||
quote = { version = "1.0", optional = true }
|
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
|
||||||
serde_json = { version = "1.0" }
|
|
||||||
syn = { version = "2.0", optional = true }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
# Enable all features for tests
|
|
||||||
objdiff-core = { path = ".", features = ["all"] }
|
|
||||||
insta = "1.43"
|
|
||||||
|
@ -5,12 +5,10 @@ objdiff-core contains the core functionality of [objdiff](https://github.com/enc
|
|||||||
## Crate feature flags
|
## Crate feature flags
|
||||||
|
|
||||||
- **`all`**: Enables all main features.
|
- **`all`**: Enables all main features.
|
||||||
- **`bindings`**: Enables serialization and deserialization of objdiff data structures.
|
|
||||||
- **`config`**: Enables objdiff configuration file support.
|
- **`config`**: Enables objdiff configuration file support.
|
||||||
- **`dwarf`**: Enables extraction of line number information from DWARF debug sections.
|
- **`dwarf`**: Enables extraction of line number information from DWARF debug sections.
|
||||||
- **`arm64`**: Enables the ARM64 backend powered by [yaxpeax-arm](https://github.com/iximeow/yaxpeax-arm).
|
- **`mips`**: Enables the MIPS backend powered by [rabbitizer](https://github.com/Decompollaborate/rabbitizer). (Note: C library with Rust bindings)
|
||||||
- **`arm`**: Enables the ARM backend powered by [unarm](https://github.com/AetiasHax/unarm).
|
|
||||||
- **`mips`**: Enables the MIPS backend powered by [rabbitizer](https://github.com/Decompollaborate/rabbitizer).
|
|
||||||
- **`ppc`**: Enables the PowerPC backend powered by [ppc750cl](https://github.com/encounter/ppc750cl).
|
- **`ppc`**: Enables the PowerPC backend powered by [ppc750cl](https://github.com/encounter/ppc750cl).
|
||||||
- **`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).
|
- **`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).
|
||||||
|
- **`bindings`**: Enables serialization and deserialization of objdiff data structures.
|
||||||
|
@ -1,17 +1,6 @@
|
|||||||
#[cfg(feature = "any-arch")]
|
use std::path::{Path, PathBuf};
|
||||||
mod config_gen;
|
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() {
|
||||||
#[cfg(feature = "bindings")]
|
|
||||||
compile_protos();
|
|
||||||
#[cfg(feature = "any-arch")]
|
|
||||||
config_gen::generate_diff_config();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "bindings")]
|
|
||||||
fn compile_protos() {
|
|
||||||
use std::path::{Path, PathBuf};
|
|
||||||
let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("protos");
|
let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("protos");
|
||||||
let descriptor_path = root.join("proto_descriptor.bin");
|
let descriptor_path = root.join("proto_descriptor.bin");
|
||||||
println!("cargo:rerun-if-changed={}", descriptor_path.display());
|
println!("cargo:rerun-if-changed={}", descriptor_path.display());
|
||||||
@ -19,15 +8,7 @@ fn compile_protos() {
|
|||||||
.map(|m| m.modified().unwrap())
|
.map(|m| m.modified().unwrap())
|
||||||
.unwrap_or(std::time::SystemTime::UNIX_EPOCH);
|
.unwrap_or(std::time::SystemTime::UNIX_EPOCH);
|
||||||
let mut run_protoc = false;
|
let mut run_protoc = false;
|
||||||
let proto_files = root
|
let proto_files = vec![root.join("diff.proto"), root.join("report.proto")];
|
||||||
.read_dir()
|
|
||||||
.unwrap()
|
|
||||||
.filter_map(|e| {
|
|
||||||
let e = e.unwrap();
|
|
||||||
let path = e.path();
|
|
||||||
(path.extension() == Some(std::ffi::OsStr::new("proto"))).then_some(path)
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
for proto_file in &proto_files {
|
for proto_file in &proto_files {
|
||||||
println!("cargo:rerun-if-changed={}", proto_file.display());
|
println!("cargo:rerun-if-changed={}", proto_file.display());
|
||||||
let mtime = match std::fs::metadata(proto_file) {
|
let mtime = match std::fs::metadata(proto_file) {
|
||||||
@ -63,14 +44,11 @@ fn compile_protos() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
let descriptor_set = std::fs::read(descriptor_path).expect("Failed to read descriptor set");
|
||||||
{
|
pbjson_build::Builder::new()
|
||||||
let descriptor_set = std::fs::read(descriptor_path).expect("Failed to read descriptor set");
|
.register_descriptors(&descriptor_set)
|
||||||
pbjson_build::Builder::new()
|
.expect("Failed to register descriptors")
|
||||||
.register_descriptors(&descriptor_set)
|
.preserve_proto_field_names()
|
||||||
.expect("Failed to register descriptors")
|
.build(&[".objdiff"])
|
||||||
.preserve_proto_field_names()
|
.expect("Failed to build pbjson");
|
||||||
.build(&[".objdiff"])
|
|
||||||
.expect("Failed to build pbjson");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,278 +0,0 @@
|
|||||||
{
|
|
||||||
"properties": [
|
|
||||||
{
|
|
||||||
"id": "functionRelocDiffs",
|
|
||||||
"type": "choice",
|
|
||||||
"default": "name_address",
|
|
||||||
"name": "Function relocation diffs",
|
|
||||||
"description": "How relocation targets will be diffed in the function view.",
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"value": "none",
|
|
||||||
"name": "None"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "name_address",
|
|
||||||
"name": "Name or address"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "data_value",
|
|
||||||
"name": "Data value"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "all",
|
|
||||||
"name": "Name or address, data value"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "spaceBetweenArgs",
|
|
||||||
"type": "boolean",
|
|
||||||
"default": true,
|
|
||||||
"name": "Space between args",
|
|
||||||
"description": "Adds a space between arguments in the diff output."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "combineDataSections",
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false,
|
|
||||||
"name": "Combine data sections",
|
|
||||||
"description": "Combines data sections with equal names."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "combineTextSections",
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false,
|
|
||||||
"name": "Combine text sections",
|
|
||||||
"description": "Combines all text sections into one."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "arm.archVersion",
|
|
||||||
"type": "choice",
|
|
||||||
"default": "auto",
|
|
||||||
"name": "Architecture version",
|
|
||||||
"description": "ARM architecture version to use for disassembly.",
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"value": "auto",
|
|
||||||
"name": "Auto"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "v4t",
|
|
||||||
"name": "ARMv4T (GBA)"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "v5te",
|
|
||||||
"name": "ARMv5TE (DS)"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "v6k",
|
|
||||||
"name": "ARMv6K (3DS)"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "arm.unifiedSyntax",
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false,
|
|
||||||
"name": "Unified syntax",
|
|
||||||
"description": "Disassemble as unified assembly language (UAL)."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "arm.avRegisters",
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false,
|
|
||||||
"name": "Use A/V registers",
|
|
||||||
"description": "Display R0-R3 as A1-A4 and R4-R11 as V1-V8."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "arm.r9Usage",
|
|
||||||
"type": "choice",
|
|
||||||
"default": "generalPurpose",
|
|
||||||
"name": "Display R9 as",
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"value": "generalPurpose",
|
|
||||||
"name": "R9 or V6",
|
|
||||||
"description": "Use R9 as a general-purpose register."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "sb",
|
|
||||||
"name": "SB (static base)",
|
|
||||||
"description": "Used for position-independent data (PID)."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "tr",
|
|
||||||
"name": "TR (TLS register)",
|
|
||||||
"description": "Used for thread-local storage."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "arm.slUsage",
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false,
|
|
||||||
"name": "Display R10 as SL",
|
|
||||||
"description": "Used for explicit stack limits."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "arm.fpUsage",
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false,
|
|
||||||
"name": "Display R11 as FP",
|
|
||||||
"description": "Used for frame pointers."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "arm.ipUsage",
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false,
|
|
||||||
"name": "Display R12 as IP",
|
|
||||||
"description": "Used for interworking and long branches."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "mips.abi",
|
|
||||||
"type": "choice",
|
|
||||||
"default": "auto",
|
|
||||||
"name": "ABI",
|
|
||||||
"description": "MIPS ABI to use for disassembly.",
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"value": "auto",
|
|
||||||
"name": "Auto"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "o32",
|
|
||||||
"name": "O32"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "n32",
|
|
||||||
"name": "N32"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "n64",
|
|
||||||
"name": "N64"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "mips.instrCategory",
|
|
||||||
"type": "choice",
|
|
||||||
"default": "auto",
|
|
||||||
"name": "Instruction category",
|
|
||||||
"description": "MIPS instruction category to use for disassembly.",
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"value": "auto",
|
|
||||||
"name": "Auto"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "cpu",
|
|
||||||
"name": "CPU"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "rsp",
|
|
||||||
"name": "RSP (N64)"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "r3000gte",
|
|
||||||
"name": "R3000 GTE (PS1)"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "r4000allegrex",
|
|
||||||
"name": "R4000 ALLEGREX (PSP)"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "r5900",
|
|
||||||
"name": "R5900 EE (PS2)"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"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",
|
|
||||||
"default": "intel",
|
|
||||||
"name": "Format",
|
|
||||||
"description": "x86 disassembly syntax.",
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"value": "intel",
|
|
||||||
"name": "Intel"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "gas",
|
|
||||||
"name": "AT&T"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "nasm",
|
|
||||||
"name": "NASM"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "masm",
|
|
||||||
"name": "MASM"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"groups": [
|
|
||||||
{
|
|
||||||
"id": "general",
|
|
||||||
"name": "General",
|
|
||||||
"properties": [
|
|
||||||
"functionRelocDiffs",
|
|
||||||
"spaceBetweenArgs",
|
|
||||||
"combineDataSections",
|
|
||||||
"combineTextSections"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "arm",
|
|
||||||
"name": "ARM",
|
|
||||||
"properties": [
|
|
||||||
"arm.archVersion",
|
|
||||||
"arm.unifiedSyntax",
|
|
||||||
"arm.avRegisters",
|
|
||||||
"arm.r9Usage",
|
|
||||||
"arm.slUsage",
|
|
||||||
"arm.fpUsage",
|
|
||||||
"arm.ipUsage"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "mips",
|
|
||||||
"name": "MIPS",
|
|
||||||
"properties": [
|
|
||||||
"mips.abi",
|
|
||||||
"mips.instrCategory",
|
|
||||||
"mips.registerPrefix"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "ppc",
|
|
||||||
"name": "PowerPC",
|
|
||||||
"properties": [
|
|
||||||
"ppc.calculatePoolRelocations"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "x86",
|
|
||||||
"name": "x86",
|
|
||||||
"properties": [
|
|
||||||
"x86.formatter"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,500 +0,0 @@
|
|||||||
use std::{
|
|
||||||
fs::File,
|
|
||||||
path::{Path, PathBuf},
|
|
||||||
};
|
|
||||||
|
|
||||||
use heck::{ToShoutySnakeCase, ToSnakeCase, ToUpperCamelCase};
|
|
||||||
use proc_macro2::TokenStream;
|
|
||||||
use quote::{format_ident, quote};
|
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize)]
|
|
||||||
pub struct ConfigSchema {
|
|
||||||
pub properties: Vec<ConfigProperty>,
|
|
||||||
pub groups: Vec<ConfigGroup>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize)]
|
|
||||||
#[serde(tag = "type")]
|
|
||||||
pub enum ConfigProperty {
|
|
||||||
#[serde(rename = "boolean")]
|
|
||||||
Boolean(ConfigPropertyBoolean),
|
|
||||||
#[serde(rename = "choice")]
|
|
||||||
Choice(ConfigPropertyChoice),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize)]
|
|
||||||
pub struct ConfigPropertyBase {
|
|
||||||
pub id: String,
|
|
||||||
pub name: String,
|
|
||||||
pub description: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize)]
|
|
||||||
pub struct ConfigPropertyBoolean {
|
|
||||||
#[serde(flatten)]
|
|
||||||
pub base: ConfigPropertyBase,
|
|
||||||
pub default: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize)]
|
|
||||||
pub struct ConfigPropertyChoice {
|
|
||||||
#[serde(flatten)]
|
|
||||||
pub base: ConfigPropertyBase,
|
|
||||||
pub default: String,
|
|
||||||
pub items: Vec<ConfigPropertyChoiceItem>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize)]
|
|
||||||
pub struct ConfigPropertyChoiceItem {
|
|
||||||
pub value: String,
|
|
||||||
pub name: String,
|
|
||||||
pub description: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize)]
|
|
||||||
pub struct ConfigGroup {
|
|
||||||
pub id: String,
|
|
||||||
pub name: String,
|
|
||||||
pub description: Option<String>,
|
|
||||||
pub properties: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build_doc(name: &str, description: Option<&str>) -> TokenStream {
|
|
||||||
let mut doc = format!(" {}", name);
|
|
||||||
let mut out = quote! { #[doc = #doc] };
|
|
||||||
if let Some(description) = description {
|
|
||||||
doc = format!(" {}", description);
|
|
||||||
out.extend(quote! { #[doc = ""] });
|
|
||||||
out.extend(quote! { #[doc = #doc] });
|
|
||||||
}
|
|
||||||
out
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn generate_diff_config() {
|
|
||||||
let schema_path = Path::new(env!("CARGO_MANIFEST_DIR")).join("config-schema.json");
|
|
||||||
println!("cargo:rerun-if-changed={}", schema_path.display());
|
|
||||||
let schema_file = File::open(schema_path).expect("Failed to open config schema file");
|
|
||||||
let schema: ConfigSchema =
|
|
||||||
serde_json::from_reader(schema_file).expect("Failed to parse config schema");
|
|
||||||
|
|
||||||
let mut enums = TokenStream::new();
|
|
||||||
for property in &schema.properties {
|
|
||||||
let ConfigProperty::Choice(choice) = property else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
let enum_ident = format_ident!("{}", choice.base.id.to_upper_camel_case());
|
|
||||||
let mut variants = TokenStream::new();
|
|
||||||
let mut full_variants = TokenStream::new();
|
|
||||||
let mut variant_info = TokenStream::new();
|
|
||||||
let mut variant_to_str = TokenStream::new();
|
|
||||||
let mut variant_to_name = TokenStream::new();
|
|
||||||
let mut variant_to_description = TokenStream::new();
|
|
||||||
let mut variant_from_str = TokenStream::new();
|
|
||||||
for item in &choice.items {
|
|
||||||
let variant_name = item.value.to_upper_camel_case();
|
|
||||||
let variant_ident = format_ident!("{}", variant_name);
|
|
||||||
let is_default = item.value == choice.default;
|
|
||||||
variants.extend(build_doc(&item.name, item.description.as_deref()));
|
|
||||||
if is_default {
|
|
||||||
variants.extend(quote! { #[default] });
|
|
||||||
}
|
|
||||||
let value = &item.value;
|
|
||||||
variants.extend(quote! {
|
|
||||||
#[cfg_attr(feature = "serde", serde(rename = #value, alias = #variant_name))]
|
|
||||||
#variant_ident,
|
|
||||||
});
|
|
||||||
full_variants.extend(quote! { #enum_ident::#variant_ident, });
|
|
||||||
variant_to_str.extend(quote! { #enum_ident::#variant_ident => #value, });
|
|
||||||
let name = &item.name;
|
|
||||||
variant_to_name.extend(quote! { #enum_ident::#variant_ident => #name, });
|
|
||||||
if let Some(description) = &item.description {
|
|
||||||
variant_to_description.extend(quote! {
|
|
||||||
#enum_ident::#variant_ident => Some(#description),
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
variant_to_description.extend(quote! {
|
|
||||||
#enum_ident::#variant_ident => None,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
let description = if let Some(description) = &item.description {
|
|
||||||
quote! { Some(#description) }
|
|
||||||
} else {
|
|
||||||
quote! { None }
|
|
||||||
};
|
|
||||||
variant_info.extend(quote! {
|
|
||||||
ConfigEnumVariantInfo {
|
|
||||||
value: #value,
|
|
||||||
name: #name,
|
|
||||||
description: #description,
|
|
||||||
is_default: #is_default,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
variant_from_str.extend(quote! {
|
|
||||||
if s.eq_ignore_ascii_case(#value) { return Ok(#enum_ident::#variant_ident); }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
enums.extend(quote! {
|
|
||||||
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Hash)]
|
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
|
||||||
pub enum #enum_ident {
|
|
||||||
#variants
|
|
||||||
}
|
|
||||||
impl ConfigEnum for #enum_ident {
|
|
||||||
#[inline]
|
|
||||||
fn variants() -> &'static [Self] {
|
|
||||||
static VARIANTS: &[#enum_ident] = &[#full_variants];
|
|
||||||
VARIANTS
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn variant_info() -> &'static [ConfigEnumVariantInfo] {
|
|
||||||
static VARIANT_INFO: &[ConfigEnumVariantInfo] = &[
|
|
||||||
#variant_info
|
|
||||||
];
|
|
||||||
VARIANT_INFO
|
|
||||||
}
|
|
||||||
fn as_str(&self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
#variant_to_str
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn name(&self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
#variant_to_name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn description(&self) -> Option<&'static str> {
|
|
||||||
match self {
|
|
||||||
#variant_to_description
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl core::str::FromStr for #enum_ident {
|
|
||||||
type Err = ();
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
#variant_from_str
|
|
||||||
Err(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut groups = TokenStream::new();
|
|
||||||
let mut group_idents = Vec::new();
|
|
||||||
for group in &schema.groups {
|
|
||||||
let ident = format_ident!("CONFIG_GROUP_{}", group.id.to_shouty_snake_case());
|
|
||||||
let id = &group.id;
|
|
||||||
let name = &group.name;
|
|
||||||
let description = if let Some(description) = &group.description {
|
|
||||||
quote! { Some(#description) }
|
|
||||||
} else {
|
|
||||||
quote! { None }
|
|
||||||
};
|
|
||||||
let properties =
|
|
||||||
group.properties.iter().map(|p| format_ident!("{}", p.to_upper_camel_case()));
|
|
||||||
groups.extend(quote! {
|
|
||||||
ConfigPropertyGroup {
|
|
||||||
id: #id,
|
|
||||||
name: #name,
|
|
||||||
description: #description,
|
|
||||||
properties: &[#(ConfigPropertyId::#properties,)*],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
group_idents.push(ident);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut property_idents = Vec::new();
|
|
||||||
let mut property_variants = TokenStream::new();
|
|
||||||
let mut variant_info = TokenStream::new();
|
|
||||||
let mut config_property_id_to_str = TokenStream::new();
|
|
||||||
let mut config_property_id_to_name = TokenStream::new();
|
|
||||||
let mut config_property_id_to_description = TokenStream::new();
|
|
||||||
let mut config_property_id_to_kind = TokenStream::new();
|
|
||||||
let mut property_fields = TokenStream::new();
|
|
||||||
let mut default_fields = TokenStream::new();
|
|
||||||
let mut get_property_value_variants = TokenStream::new();
|
|
||||||
let mut set_property_value_variants = TokenStream::new();
|
|
||||||
let mut set_property_value_str_variants = TokenStream::new();
|
|
||||||
let mut config_property_id_from_str = TokenStream::new();
|
|
||||||
for property in &schema.properties {
|
|
||||||
let base = match property {
|
|
||||||
ConfigProperty::Boolean(b) => &b.base,
|
|
||||||
ConfigProperty::Choice(c) => &c.base,
|
|
||||||
};
|
|
||||||
let id = &base.id;
|
|
||||||
let enum_ident = format_ident!("{}", id.to_upper_camel_case());
|
|
||||||
property_idents.push(enum_ident.clone());
|
|
||||||
config_property_id_to_str.extend(quote! { Self::#enum_ident => #id, });
|
|
||||||
let name = &base.name;
|
|
||||||
config_property_id_to_name.extend(quote! { Self::#enum_ident => #name, });
|
|
||||||
if let Some(description) = &base.description {
|
|
||||||
config_property_id_to_description.extend(quote! {
|
|
||||||
Self::#enum_ident => Some(#description),
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
config_property_id_to_description.extend(quote! {
|
|
||||||
Self::#enum_ident => None,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
let doc = build_doc(name, base.description.as_deref());
|
|
||||||
property_variants.extend(quote! { #doc #enum_ident, });
|
|
||||||
property_fields.extend(doc);
|
|
||||||
let field_ident = format_ident!("{}", id.to_snake_case());
|
|
||||||
match property {
|
|
||||||
ConfigProperty::Boolean(b) => {
|
|
||||||
let default = b.default;
|
|
||||||
if default {
|
|
||||||
property_fields.extend(quote! {
|
|
||||||
#[cfg_attr(feature = "serde", serde(default = "default_true"))]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
property_fields.extend(quote! {
|
|
||||||
pub #field_ident: bool,
|
|
||||||
});
|
|
||||||
default_fields.extend(quote! {
|
|
||||||
#field_ident: #default,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
ConfigProperty::Choice(_) => {
|
|
||||||
property_fields.extend(quote! {
|
|
||||||
pub #field_ident: #enum_ident,
|
|
||||||
});
|
|
||||||
default_fields.extend(quote! {
|
|
||||||
#field_ident: #enum_ident::default(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let property_value = match property {
|
|
||||||
ConfigProperty::Boolean(_) => {
|
|
||||||
quote! { ConfigPropertyValue::Boolean(self.#field_ident) }
|
|
||||||
}
|
|
||||||
ConfigProperty::Choice(_) => {
|
|
||||||
quote! { ConfigPropertyValue::Choice(self.#field_ident.as_str()) }
|
|
||||||
}
|
|
||||||
};
|
|
||||||
get_property_value_variants.extend(quote! {
|
|
||||||
ConfigPropertyId::#enum_ident => #property_value,
|
|
||||||
});
|
|
||||||
match property {
|
|
||||||
ConfigProperty::Boolean(_) => {
|
|
||||||
set_property_value_variants.extend(quote! {
|
|
||||||
ConfigPropertyId::#enum_ident => {
|
|
||||||
if let ConfigPropertyValue::Boolean(value) = value {
|
|
||||||
self.#field_ident = value;
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(())
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
set_property_value_str_variants.extend(quote! {
|
|
||||||
ConfigPropertyId::#enum_ident => {
|
|
||||||
if let Ok(value) = value.parse() {
|
|
||||||
self.#field_ident = value;
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(())
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
ConfigProperty::Choice(_) => {
|
|
||||||
set_property_value_variants.extend(quote! {
|
|
||||||
ConfigPropertyId::#enum_ident => {
|
|
||||||
if let ConfigPropertyValue::Choice(value) = value {
|
|
||||||
if let Ok(value) = value.parse() {
|
|
||||||
self.#field_ident = value;
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Err(())
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
set_property_value_str_variants.extend(quote! {
|
|
||||||
ConfigPropertyId::#enum_ident => {
|
|
||||||
if let Ok(value) = value.parse() {
|
|
||||||
self.#field_ident = value;
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(())
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let description = if let Some(description) = &base.description {
|
|
||||||
quote! { Some(#description) }
|
|
||||||
} else {
|
|
||||||
quote! { None }
|
|
||||||
};
|
|
||||||
variant_info.extend(quote! {
|
|
||||||
ConfigEnumVariantInfo {
|
|
||||||
value: #id,
|
|
||||||
name: #name,
|
|
||||||
description: #description,
|
|
||||||
is_default: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
match property {
|
|
||||||
ConfigProperty::Boolean(_) => {
|
|
||||||
config_property_id_to_kind.extend(quote! {
|
|
||||||
Self::#enum_ident => ConfigPropertyKind::Boolean,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
ConfigProperty::Choice(_) => {
|
|
||||||
config_property_id_to_kind.extend(quote! {
|
|
||||||
Self::#enum_ident => ConfigPropertyKind::Choice(#enum_ident::variant_info()),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let snake_id = id.to_snake_case();
|
|
||||||
config_property_id_from_str.extend(quote! {
|
|
||||||
if s.eq_ignore_ascii_case(#id) || s.eq_ignore_ascii_case(#snake_id) {
|
|
||||||
return Ok(Self::#enum_ident);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let tokens = quote! {
|
|
||||||
pub trait ConfigEnum: Sized {
|
|
||||||
fn variants() -> &'static [Self];
|
|
||||||
fn variant_info() -> &'static [ConfigEnumVariantInfo];
|
|
||||||
fn as_str(&self) -> &'static str;
|
|
||||||
fn name(&self) -> &'static str;
|
|
||||||
fn description(&self) -> Option<&'static str>;
|
|
||||||
}
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct ConfigEnumVariantInfo {
|
|
||||||
pub value: &'static str,
|
|
||||||
pub name: &'static str,
|
|
||||||
pub description: Option<&'static str>,
|
|
||||||
pub is_default: bool,
|
|
||||||
}
|
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
|
||||||
pub enum ConfigPropertyId {
|
|
||||||
#property_variants
|
|
||||||
}
|
|
||||||
impl ConfigEnum for ConfigPropertyId {
|
|
||||||
#[inline]
|
|
||||||
fn variants() -> &'static [Self] {
|
|
||||||
static VARIANTS: &[ConfigPropertyId] = &[#(ConfigPropertyId::#property_idents,)*];
|
|
||||||
VARIANTS
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn variant_info() -> &'static [ConfigEnumVariantInfo] {
|
|
||||||
static VARIANT_INFO: &[ConfigEnumVariantInfo] = &[
|
|
||||||
#variant_info
|
|
||||||
];
|
|
||||||
VARIANT_INFO
|
|
||||||
}
|
|
||||||
fn as_str(&self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
#config_property_id_to_str
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn name(&self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
#config_property_id_to_name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn description(&self) -> Option<&'static str> {
|
|
||||||
match self {
|
|
||||||
#config_property_id_to_description
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl ConfigPropertyId {
|
|
||||||
pub fn kind(&self) -> ConfigPropertyKind {
|
|
||||||
match self {
|
|
||||||
#config_property_id_to_kind
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl core::str::FromStr for ConfigPropertyId {
|
|
||||||
type Err = ();
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
#config_property_id_from_str
|
|
||||||
Err(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct ConfigPropertyGroup {
|
|
||||||
pub id: &'static str,
|
|
||||||
pub name: &'static str,
|
|
||||||
pub description: Option<&'static str>,
|
|
||||||
pub properties: &'static [ConfigPropertyId],
|
|
||||||
}
|
|
||||||
pub static CONFIG_GROUPS: &[ConfigPropertyGroup] = &[#groups];
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
|
|
||||||
pub enum ConfigPropertyValue {
|
|
||||||
Boolean(bool),
|
|
||||||
Choice(&'static str),
|
|
||||||
}
|
|
||||||
impl ConfigPropertyValue {
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
pub fn to_json(&self) -> serde_json::Value {
|
|
||||||
match self {
|
|
||||||
ConfigPropertyValue::Boolean(value) => serde_json::Value::Bool(*value),
|
|
||||||
ConfigPropertyValue::Choice(value) => serde_json::Value::String(value.to_string()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub enum ConfigPropertyKind {
|
|
||||||
Boolean,
|
|
||||||
Choice(&'static [ConfigEnumVariantInfo]),
|
|
||||||
}
|
|
||||||
#enums
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
#[inline(always)]
|
|
||||||
fn default_true() -> bool { true }
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize), serde(default))]
|
|
||||||
pub struct DiffObjConfig {
|
|
||||||
#property_fields
|
|
||||||
}
|
|
||||||
impl Default for DiffObjConfig {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
#default_fields
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl DiffObjConfig {
|
|
||||||
pub fn get_property_value(&self, id: ConfigPropertyId) -> ConfigPropertyValue {
|
|
||||||
match id {
|
|
||||||
#get_property_value_variants
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[allow(clippy::result_unit_err)]
|
|
||||||
pub fn set_property_value(&mut self, id: ConfigPropertyId, value: ConfigPropertyValue) -> Result<(), ()> {
|
|
||||||
match id {
|
|
||||||
#set_property_value_variants
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[allow(clippy::result_unit_err)]
|
|
||||||
pub fn set_property_value_str(&mut self, id: ConfigPropertyId, value: &str) -> Result<(), ()> {
|
|
||||||
match id {
|
|
||||||
#set_property_value_str_variants
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let file = syn::parse2(tokens).unwrap();
|
|
||||||
let formatted = prettyplease::unparse(&file);
|
|
||||||
std::fs::write(
|
|
||||||
PathBuf::from(std::env::var_os("OUT_DIR").unwrap()).join("config.gen.rs"),
|
|
||||||
formatted,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
@ -1,59 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
@ -21,9 +21,9 @@ enum SymbolFlag {
|
|||||||
SYMBOL_NONE = 0;
|
SYMBOL_NONE = 0;
|
||||||
SYMBOL_GLOBAL = 1;
|
SYMBOL_GLOBAL = 1;
|
||||||
SYMBOL_LOCAL = 2;
|
SYMBOL_LOCAL = 2;
|
||||||
SYMBOL_WEAK = 4;
|
SYMBOL_WEAK = 3;
|
||||||
SYMBOL_COMMON = 8;
|
SYMBOL_COMMON = 4;
|
||||||
SYMBOL_HIDDEN = 16;
|
SYMBOL_HIDDEN = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
// A single parsed instruction
|
// A single parsed instruction
|
||||||
@ -87,11 +87,11 @@ message Relocation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
message RelocationTarget {
|
message RelocationTarget {
|
||||||
uint32 symbol_index = 1;
|
Symbol symbol = 1;
|
||||||
int64 addend = 2;
|
int64 addend = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message InstructionDiffRow {
|
message InstructionDiff {
|
||||||
DiffKind diff_kind = 1;
|
DiffKind diff_kind = 1;
|
||||||
optional Instruction instruction = 2;
|
optional Instruction instruction = 2;
|
||||||
optional InstructionBranchFrom branch_from = 3;
|
optional InstructionBranchFrom branch_from = 3;
|
||||||
@ -122,12 +122,10 @@ message InstructionBranchTo {
|
|||||||
uint32 branch_index = 2;
|
uint32 branch_index = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message SymbolDiff {
|
message FunctionDiff {
|
||||||
Symbol symbol = 1;
|
Symbol symbol = 1;
|
||||||
repeated InstructionDiffRow instruction_rows = 2;
|
repeated InstructionDiff instructions = 2;
|
||||||
optional float match_percent = 3;
|
optional float match_percent = 3;
|
||||||
// The symbol index in the _other_ object that this symbol was diffed against
|
|
||||||
optional uint32 target_symbol = 5;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message DataDiff {
|
message DataDiff {
|
||||||
@ -142,7 +140,7 @@ message SectionDiff {
|
|||||||
SectionKind kind = 2;
|
SectionKind kind = 2;
|
||||||
uint64 size = 3;
|
uint64 size = 3;
|
||||||
uint64 address = 4;
|
uint64 address = 4;
|
||||||
reserved 5;
|
repeated FunctionDiff functions = 5;
|
||||||
repeated DataDiff data = 6;
|
repeated DataDiff data = 6;
|
||||||
optional float match_percent = 7;
|
optional float match_percent = 7;
|
||||||
}
|
}
|
||||||
@ -152,11 +150,11 @@ enum SectionKind {
|
|||||||
SECTION_TEXT = 1;
|
SECTION_TEXT = 1;
|
||||||
SECTION_DATA = 2;
|
SECTION_DATA = 2;
|
||||||
SECTION_BSS = 3;
|
SECTION_BSS = 3;
|
||||||
|
SECTION_COMMON = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message ObjectDiff {
|
message ObjectDiff {
|
||||||
repeated SectionDiff sections = 1;
|
repeated SectionDiff sections = 1;
|
||||||
repeated SymbolDiff symbols = 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message DiffResult {
|
message DiffResult {
|
||||||
|
Binary file not shown.
@ -2,18 +2,6 @@ syntax = "proto3";
|
|||||||
|
|
||||||
package objdiff.report;
|
package objdiff.report;
|
||||||
|
|
||||||
// Project progress report
|
|
||||||
message Report {
|
|
||||||
// Overall progress info
|
|
||||||
Measures measures = 1;
|
|
||||||
// Units within this report
|
|
||||||
repeated ReportUnit units = 2;
|
|
||||||
// Report version
|
|
||||||
uint32 version = 3;
|
|
||||||
// Progress categories
|
|
||||||
repeated ReportCategory categories = 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Progress info for a report or unit
|
// Progress info for a report or unit
|
||||||
message Measures {
|
message Measures {
|
||||||
// Overall match percent, including partially matched functions and data
|
// Overall match percent, including partially matched functions and data
|
||||||
@ -50,6 +38,18 @@ message Measures {
|
|||||||
uint32 complete_units = 16;
|
uint32 complete_units = 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Project progress report
|
||||||
|
message Report {
|
||||||
|
// Overall progress info
|
||||||
|
Measures measures = 1;
|
||||||
|
// Units within this report
|
||||||
|
repeated ReportUnit units = 2;
|
||||||
|
// Report version
|
||||||
|
uint32 version = 3;
|
||||||
|
// Progress categories
|
||||||
|
repeated ReportCategory categories = 4;
|
||||||
|
}
|
||||||
|
|
||||||
message ReportCategory {
|
message ReportCategory {
|
||||||
// The ID of the category
|
// The ID of the category
|
||||||
string id = 1;
|
string id = 1;
|
||||||
@ -99,8 +99,6 @@ message ReportItem {
|
|||||||
float fuzzy_match_percent = 3;
|
float fuzzy_match_percent = 3;
|
||||||
// Extra metadata for this item
|
// Extra metadata for this item
|
||||||
optional ReportItemMetadata metadata = 4;
|
optional ReportItemMetadata metadata = 4;
|
||||||
// Address of the item (section-relative offset)
|
|
||||||
optional uint64 address = 5;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extra metadata for an item
|
// Extra metadata for an item
|
||||||
@ -110,3 +108,57 @@ message ReportItemMetadata {
|
|||||||
// The virtual address of the function or section
|
// The virtual address of the function or section
|
||||||
optional uint64 virtual_address = 2;
|
optional uint64 virtual_address = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A pair of reports to compare and generate changes
|
||||||
|
message ChangesInput {
|
||||||
|
// The previous report
|
||||||
|
Report from = 1;
|
||||||
|
// The current report
|
||||||
|
Report to = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Changes between two reports
|
||||||
|
message Changes {
|
||||||
|
// The progress info for the previous report
|
||||||
|
Measures from = 1;
|
||||||
|
// The progress info for the current report
|
||||||
|
Measures to = 2;
|
||||||
|
// Units that changed
|
||||||
|
repeated ChangeUnit units = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A changed unit
|
||||||
|
message ChangeUnit {
|
||||||
|
// The name of the unit
|
||||||
|
string name = 1;
|
||||||
|
// The previous progress info (omitted if new)
|
||||||
|
optional Measures from = 2;
|
||||||
|
// The current progress info (omitted if removed)
|
||||||
|
optional Measures to = 3;
|
||||||
|
// Sections that changed
|
||||||
|
repeated ChangeItem sections = 4;
|
||||||
|
// Functions that changed
|
||||||
|
repeated ChangeItem functions = 5;
|
||||||
|
// Extra metadata for this unit
|
||||||
|
optional ReportUnitMetadata metadata = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A changed section or function
|
||||||
|
message ChangeItem {
|
||||||
|
// The name of the item
|
||||||
|
string name = 1;
|
||||||
|
// The previous progress info (omitted if new)
|
||||||
|
optional ChangeItemInfo from = 2;
|
||||||
|
// The current progress info (omitted if removed)
|
||||||
|
optional ChangeItemInfo to = 3;
|
||||||
|
// Extra metadata for this item
|
||||||
|
optional ReportItemMetadata metadata = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Progress info for a section or function
|
||||||
|
message ChangeItemInfo {
|
||||||
|
// The overall match percent for this item
|
||||||
|
float fuzzy_match_percent = 1;
|
||||||
|
// The size of the item in bytes
|
||||||
|
uint64 size = 2;
|
||||||
|
}
|
||||||
|
@ -1,39 +1,40 @@
|
|||||||
use alloc::{
|
use std::{
|
||||||
collections::BTreeMap,
|
borrow::Cow,
|
||||||
format,
|
collections::{BTreeMap, HashMap},
|
||||||
string::{String, ToString},
|
|
||||||
vec::Vec,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::{Result, bail};
|
use anyhow::{bail, Result};
|
||||||
use arm_attr::{BuildAttrs, enums::CpuArch, tag::Tag};
|
use arm_attr::{enums::CpuArch, tag::Tag, BuildAttrs};
|
||||||
use object::{Endian as _, Object as _, ObjectSection as _, ObjectSymbol as _, elf};
|
use object::{
|
||||||
use unarm::{args, arm, thumb};
|
elf::{self, SHT_ARM_ATTRIBUTES},
|
||||||
|
Endian, File, Object, ObjectSection, ObjectSymbol, Relocation, RelocationFlags, SectionIndex,
|
||||||
|
SectionKind, Symbol, SymbolKind,
|
||||||
|
};
|
||||||
|
use unarm::{
|
||||||
|
args::{Argument, OffsetImm, OffsetReg, Register},
|
||||||
|
parse::{ArmVersion, ParseMode, Parser},
|
||||||
|
DisplayOptions, ParseFlags, ParsedIns, RegNames,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
arch::Arch,
|
arch::{ObjArch, ProcessCodeResult},
|
||||||
diff::{ArmArchVersion, ArmR9Usage, DiffObjConfig, display::InstructionPart},
|
diff::{ArmArchVersion, ArmR9Usage, DiffObjConfig},
|
||||||
obj::{
|
obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection},
|
||||||
InstructionRef, Relocation, RelocationFlags, ResolvedInstructionRef, ResolvedRelocation,
|
|
||||||
Section, SectionKind, Symbol, SymbolFlag, SymbolFlagSet, SymbolKind,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
pub struct ObjArchArm {
|
||||||
pub struct ArchArm {
|
|
||||||
/// Maps section index, to list of disasm modes (arm, thumb or data) sorted by address
|
/// Maps section index, to list of disasm modes (arm, thumb or data) sorted by address
|
||||||
disasm_modes: BTreeMap<usize, Vec<DisasmMode>>,
|
disasm_modes: HashMap<SectionIndex, Vec<DisasmMode>>,
|
||||||
detected_version: Option<unarm::ArmVersion>,
|
detected_version: Option<ArmVersion>,
|
||||||
endianness: object::Endianness,
|
endianness: object::Endianness,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ArchArm {
|
impl ObjArchArm {
|
||||||
pub fn new(file: &object::File) -> Result<Self> {
|
pub fn new(file: &File) -> Result<Self> {
|
||||||
let endianness = file.endianness();
|
let endianness = file.endianness();
|
||||||
match file {
|
match file {
|
||||||
object::File::Elf32(_) => {
|
File::Elf32(_) => {
|
||||||
// The disasm_modes mapping is populated later in the post_init step so that we have access to merged sections.
|
let disasm_modes = Self::elf_get_mapping_symbols(file);
|
||||||
let disasm_modes = BTreeMap::new();
|
|
||||||
let detected_version = Self::elf_detect_arm_version(file)?;
|
let detected_version = Self::elf_detect_arm_version(file)?;
|
||||||
Ok(Self { disasm_modes, detected_version, endianness })
|
Ok(Self { disasm_modes, detected_version, endianness })
|
||||||
}
|
}
|
||||||
@ -41,11 +42,10 @@ impl ArchArm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn elf_detect_arm_version(file: &object::File) -> Result<Option<unarm::ArmVersion>> {
|
fn elf_detect_arm_version(file: &File) -> Result<Option<ArmVersion>> {
|
||||||
// Check ARM attributes
|
// Check ARM attributes
|
||||||
if let Some(arm_attrs) = file.sections().find(|s| {
|
if let Some(arm_attrs) = file.sections().find(|s| {
|
||||||
s.kind() == object::SectionKind::Elf(elf::SHT_ARM_ATTRIBUTES)
|
s.kind() == SectionKind::Elf(SHT_ARM_ATTRIBUTES) && s.name() == Ok(".ARM.attributes")
|
||||||
&& s.name() == Ok(".ARM.attributes")
|
|
||||||
}) {
|
}) {
|
||||||
let attr_data = arm_attrs.uncompressed_data()?;
|
let attr_data = arm_attrs.uncompressed_data()?;
|
||||||
let build_attrs = BuildAttrs::new(&attr_data, match file.endianness() {
|
let build_attrs = BuildAttrs::new(&attr_data, match file.endianness() {
|
||||||
@ -59,12 +59,16 @@ impl ArchArm {
|
|||||||
}
|
}
|
||||||
// Only checking first CpuArch tag. Others may exist, but that's very unlikely.
|
// Only checking first CpuArch tag. Others may exist, but that's very unlikely.
|
||||||
let cpu_arch = subsection.into_public_tag_iter()?.find_map(|(_, tag)| {
|
let cpu_arch = subsection.into_public_tag_iter()?.find_map(|(_, tag)| {
|
||||||
if let Tag::CpuArch(cpu_arch) = tag { Some(cpu_arch) } else { None }
|
if let Tag::CpuArch(cpu_arch) = tag {
|
||||||
|
Some(cpu_arch)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
});
|
});
|
||||||
match cpu_arch {
|
match cpu_arch {
|
||||||
Some(CpuArch::V4T) => return Ok(Some(unarm::ArmVersion::V4T)),
|
Some(CpuArch::V4T) => return Ok(Some(ArmVersion::V4T)),
|
||||||
Some(CpuArch::V5TE) => return Ok(Some(unarm::ArmVersion::V5Te)),
|
Some(CpuArch::V5TE) => return Ok(Some(ArmVersion::V5Te)),
|
||||||
Some(CpuArch::V6K) => return Ok(Some(unarm::ArmVersion::V6K)),
|
Some(CpuArch::V6K) => return Ok(Some(ArmVersion::V6K)),
|
||||||
Some(arch) => bail!("ARM arch {} not supported", arch),
|
Some(arch) => bail!("ARM arch {} not supported", arch),
|
||||||
None => {}
|
None => {}
|
||||||
};
|
};
|
||||||
@ -74,315 +78,181 @@ impl ArchArm {
|
|||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_mapping_symbols(
|
fn elf_get_mapping_symbols(file: &File) -> HashMap<SectionIndex, Vec<DisasmMode>> {
|
||||||
sections: &[Section],
|
file.sections()
|
||||||
symbols: &[Symbol],
|
.filter(|s| s.kind() == SectionKind::Text)
|
||||||
) -> BTreeMap<usize, Vec<DisasmMode>> {
|
.map(|s| {
|
||||||
sections
|
let index = s.index();
|
||||||
.iter()
|
let mut mapping_symbols: Vec<_> = file
|
||||||
.enumerate()
|
.symbols()
|
||||||
.filter(|(_, section)| section.kind == SectionKind::Code)
|
.filter(|s| s.section_index().map(|i| i == index).unwrap_or(false))
|
||||||
.map(|(index, _)| {
|
.filter_map(|s| DisasmMode::from_symbol(&s))
|
||||||
let mut mapping_symbols: Vec<_> = symbols
|
|
||||||
.iter()
|
|
||||||
.filter(|s| s.section.map(|i| i == index).unwrap_or(false))
|
|
||||||
.filter_map(DisasmMode::from_symbol)
|
|
||||||
.collect();
|
.collect();
|
||||||
mapping_symbols.sort_unstable_by_key(|x| x.address);
|
mapping_symbols.sort_unstable_by_key(|x| x.address);
|
||||||
(index, mapping_symbols)
|
(s.index(), mapping_symbols)
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_flags(&self, diff_config: &DiffObjConfig) -> unarm::ParseFlags {
|
|
||||||
unarm::ParseFlags {
|
|
||||||
ual: diff_config.arm_unified_syntax,
|
|
||||||
version: match diff_config.arm_arch_version {
|
|
||||||
ArmArchVersion::Auto => self.detected_version.unwrap_or(unarm::ArmVersion::V5Te),
|
|
||||||
ArmArchVersion::V4t => unarm::ArmVersion::V4T,
|
|
||||||
ArmArchVersion::V5te => unarm::ArmVersion::V5Te,
|
|
||||||
ArmArchVersion::V6k => unarm::ArmVersion::V6K,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn display_options(&self, diff_config: &DiffObjConfig) -> unarm::DisplayOptions {
|
|
||||||
unarm::DisplayOptions {
|
|
||||||
reg_names: unarm::RegNames {
|
|
||||||
av_registers: diff_config.arm_av_registers,
|
|
||||||
r9_use: match diff_config.arm_r9_usage {
|
|
||||||
ArmR9Usage::GeneralPurpose => unarm::R9Use::GeneralPurpose,
|
|
||||||
ArmR9Usage::Sb => unarm::R9Use::Pid,
|
|
||||||
ArmR9Usage::Tr => unarm::R9Use::Tls,
|
|
||||||
},
|
|
||||||
explicit_stack_limit: diff_config.arm_sl_usage,
|
|
||||||
frame_pointer: diff_config.arm_fp_usage,
|
|
||||||
ip: diff_config.arm_ip_usage,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_ins_ref(
|
|
||||||
&self,
|
|
||||||
ins_ref: InstructionRef,
|
|
||||||
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) => {
|
|
||||||
u32::from_le_bytes([code[0], code[1], code[2], code[3]])
|
|
||||||
}
|
|
||||||
(object::Endianness::Big, 2) => u16::from_be_bytes([code[0], code[1]]) as u32,
|
|
||||||
(object::Endianness::Big, 4) => {
|
|
||||||
u32::from_be_bytes([code[0], code[1], code[2], code[3]])
|
|
||||||
}
|
|
||||||
_ => bail!("Invalid instruction size {}", ins_ref.size),
|
|
||||||
};
|
|
||||||
let (ins, parsed_ins) = if ins_ref.opcode == u16::MAX {
|
|
||||||
let mut args = args::Arguments::default();
|
|
||||||
args[0] = args::Argument::UImm(code);
|
|
||||||
let mnemonic = if ins_ref.size == 4 { ".word" } else { ".hword" };
|
|
||||||
(unarm::Ins::Data, unarm::ParsedIns { mnemonic, args })
|
|
||||||
} else if ins_ref.opcode & (1 << 15) != 0 {
|
|
||||||
let ins = arm::Ins { code, op: arm::Opcode::from(ins_ref.opcode as u8) };
|
|
||||||
let parsed = ins.parse(&self.parse_flags(diff_config));
|
|
||||||
(unarm::Ins::Arm(ins), parsed)
|
|
||||||
} else {
|
|
||||||
let ins = thumb::Ins { code, op: thumb::Opcode::from(ins_ref.opcode as u8) };
|
|
||||||
let parsed = ins.parse(&self.parse_flags(diff_config));
|
|
||||||
(unarm::Ins::Thumb(ins), parsed)
|
|
||||||
};
|
|
||||||
Ok((ins, parsed_ins))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Arch for ArchArm {
|
impl ObjArch for ObjArchArm {
|
||||||
fn post_init(&mut self, sections: &[Section], symbols: &[Symbol]) {
|
fn symbol_address(&self, symbol: &Symbol) -> u64 {
|
||||||
self.disasm_modes = Self::get_mapping_symbols(sections, symbols);
|
let address = symbol.address();
|
||||||
|
if symbol.kind() == SymbolKind::Text {
|
||||||
|
address & !1
|
||||||
|
} else {
|
||||||
|
address
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scan_instructions_internal(
|
fn process_code(
|
||||||
&self,
|
&self,
|
||||||
address: u64,
|
address: u64,
|
||||||
code: &[u8],
|
code: &[u8],
|
||||||
section_index: usize,
|
section_index: usize,
|
||||||
_relocations: &[Relocation],
|
relocations: &[ObjReloc],
|
||||||
diff_config: &DiffObjConfig,
|
line_info: &BTreeMap<u64, u32>,
|
||||||
) -> Result<Vec<InstructionRef>> {
|
config: &DiffObjConfig,
|
||||||
|
) -> Result<ProcessCodeResult> {
|
||||||
let start_addr = address as u32;
|
let start_addr = address as u32;
|
||||||
let end_addr = start_addr + code.len() as u32;
|
let end_addr = start_addr + code.len() as u32;
|
||||||
|
|
||||||
// Mapping symbols decide what kind of data comes after it. $a for ARM code, $t for Thumb code and $d for data.
|
// Mapping symbols decide what kind of data comes after it. $a for ARM code, $t for Thumb code and $d for data.
|
||||||
let fallback_mappings =
|
let fallback_mappings = [DisasmMode { address: start_addr, mapping: ParseMode::Arm }];
|
||||||
[DisasmMode { address: start_addr, mapping: unarm::ParseMode::Arm }];
|
|
||||||
let mapping_symbols = self
|
let mapping_symbols = self
|
||||||
.disasm_modes
|
.disasm_modes
|
||||||
.get(§ion_index)
|
.get(&SectionIndex(section_index))
|
||||||
.map(|x| x.as_slice())
|
.map(|x| x.as_slice())
|
||||||
.unwrap_or(&fallback_mappings);
|
.unwrap_or(&fallback_mappings);
|
||||||
let first_mapping_idx = mapping_symbols
|
let first_mapping_idx =
|
||||||
.binary_search_by_key(&start_addr, |x| x.address)
|
match mapping_symbols.binary_search_by_key(&start_addr, |x| x.address) {
|
||||||
.unwrap_or_else(|idx| idx.saturating_sub(1));
|
Ok(idx) => idx,
|
||||||
let mut mode = mapping_symbols[first_mapping_idx].mapping;
|
Err(idx) => idx - 1,
|
||||||
|
};
|
||||||
|
let first_mapping = mapping_symbols[first_mapping_idx].mapping;
|
||||||
|
|
||||||
let mut mappings_iter = mapping_symbols
|
let mut mappings_iter =
|
||||||
.iter()
|
mapping_symbols.iter().skip(first_mapping_idx + 1).take_while(|x| x.address < end_addr);
|
||||||
.copied()
|
|
||||||
.skip(first_mapping_idx + 1)
|
|
||||||
.take_while(|x| x.address < end_addr);
|
|
||||||
let mut next_mapping = mappings_iter.next();
|
let mut next_mapping = mappings_iter.next();
|
||||||
|
|
||||||
let ins_count = code.len() / mode.instruction_size(start_addr);
|
let ins_count = code.len() / first_mapping.instruction_size(start_addr);
|
||||||
let mut ops = Vec::<InstructionRef>::with_capacity(ins_count);
|
let mut ops = Vec::<u16>::with_capacity(ins_count);
|
||||||
|
let mut insts = Vec::<ObjIns>::with_capacity(ins_count);
|
||||||
|
|
||||||
let parse_flags = self.parse_flags(diff_config);
|
let version = match config.arm_arch_version {
|
||||||
|
ArmArchVersion::Auto => self.detected_version.unwrap_or(ArmVersion::V5Te),
|
||||||
|
ArmArchVersion::V4T => ArmVersion::V4T,
|
||||||
|
ArmArchVersion::V5TE => ArmVersion::V5Te,
|
||||||
|
ArmArchVersion::V6K => ArmVersion::V6K,
|
||||||
|
};
|
||||||
|
let endian = match self.endianness {
|
||||||
|
object::Endianness::Little => unarm::Endian::Little,
|
||||||
|
object::Endianness::Big => unarm::Endian::Big,
|
||||||
|
};
|
||||||
|
|
||||||
let mut address = start_addr;
|
let parse_flags = ParseFlags { ual: config.arm_unified_syntax, version };
|
||||||
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 mut ins_size = mode.instruction_size(address);
|
let mut parser = Parser::new(first_mapping, start_addr, endian, parse_flags, code);
|
||||||
let data = &code[(address - start_addr) as usize..];
|
|
||||||
if data.len() < ins_size {
|
let display_options = DisplayOptions {
|
||||||
// Push the remainder as data
|
reg_names: RegNames {
|
||||||
ops.push(InstructionRef {
|
av_registers: config.arm_av_registers,
|
||||||
address: address as u64,
|
r9_use: match config.arm_r9_usage {
|
||||||
size: data.len() as u8,
|
ArmR9Usage::GeneralPurpose => unarm::R9Use::GeneralPurpose,
|
||||||
opcode: u16::MAX,
|
ArmR9Usage::Sb => unarm::R9Use::Pid,
|
||||||
branch_dest: None,
|
ArmR9Usage::Tr => unarm::R9Use::Tls,
|
||||||
});
|
},
|
||||||
break;
|
explicit_stack_limit: config.arm_sl_usage,
|
||||||
}
|
frame_pointer: config.arm_fp_usage,
|
||||||
let code = match (self.endianness, ins_size) {
|
ip: config.arm_ip_usage,
|
||||||
(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]])
|
|
||||||
|
while let Some((address, ins, parsed_ins)) = parser.next() {
|
||||||
|
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();
|
||||||
}
|
}
|
||||||
(object::Endianness::Big, 2) => u16::from_be_bytes([data[0], data[1]]) as u32,
|
}
|
||||||
(object::Endianness::Big, 4) => {
|
let line = line_info.range(..=address as u64).last().map(|(_, &b)| b);
|
||||||
u32::from_be_bytes([data[0], data[1], data[2], data[3]])
|
|
||||||
}
|
let reloc = relocations.iter().find(|r| (r.address as u32 & !1) == address).cloned();
|
||||||
_ => {
|
|
||||||
// Invalid instruction size
|
let mut reloc_arg = None;
|
||||||
ops.push(InstructionRef {
|
if let Some(reloc) = &reloc {
|
||||||
address: address as u64,
|
match reloc.flags {
|
||||||
size: ins_size as u8,
|
// Calls
|
||||||
opcode: u16::MAX,
|
RelocationFlags::Elf { r_type: elf::R_ARM_THM_XPC22 }
|
||||||
branch_dest: None,
|
| RelocationFlags::Elf { r_type: elf::R_ARM_THM_PC22 }
|
||||||
});
|
| RelocationFlags::Elf { r_type: elf::R_ARM_PC24 }
|
||||||
address += ins_size as u32;
|
| RelocationFlags::Elf { r_type: elf::R_ARM_XPC25 }
|
||||||
continue;
|
| RelocationFlags::Elf { r_type: elf::R_ARM_CALL } => {
|
||||||
|
reloc_arg = parsed_ins
|
||||||
|
.args
|
||||||
|
.iter()
|
||||||
|
.rposition(|a| matches!(a, Argument::BranchDest(_)));
|
||||||
|
}
|
||||||
|
// Data
|
||||||
|
RelocationFlags::Elf { r_type: elf::R_ARM_ABS32 } => {
|
||||||
|
reloc_arg =
|
||||||
|
parsed_ins.args.iter().rposition(|a| matches!(a, Argument::UImm(_)));
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let (opcode, branch_dest) = match mode {
|
let (args, branch_dest) = if reloc.is_some() && parser.mode == ParseMode::Data {
|
||||||
unarm::ParseMode::Arm => {
|
(vec![ObjInsArg::Reloc], None)
|
||||||
let ins = arm::Ins::new(code, &parse_flags);
|
} else {
|
||||||
let opcode = ins.op as u16 | (1 << 15);
|
push_args(&parsed_ins, config, reloc_arg, address, display_options)?
|
||||||
let branch_dest = match ins.op {
|
|
||||||
arm::Opcode::B | arm::Opcode::Bl => {
|
|
||||||
address.checked_add_signed(ins.field_branch_offset())
|
|
||||||
}
|
|
||||||
arm::Opcode::BlxI => address.checked_add_signed(ins.field_blx_offset()),
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
(opcode, branch_dest)
|
|
||||||
}
|
|
||||||
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(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(ins.field_branch_offset_11())
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
(opcode, branch_dest)
|
|
||||||
}
|
|
||||||
unarm::ParseMode::Data => (u16::MAX, None),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ops.push(InstructionRef {
|
ops.push(ins.opcode_id());
|
||||||
|
insts.push(ObjIns {
|
||||||
address: address as u64,
|
address: address as u64,
|
||||||
size: ins_size as u8,
|
size: (parser.address - address) as u8,
|
||||||
opcode,
|
op: ins.opcode_id(),
|
||||||
branch_dest: branch_dest.map(|x| x as u64),
|
mnemonic: parsed_ins.mnemonic.to_string(),
|
||||||
|
args,
|
||||||
|
reloc,
|
||||||
|
branch_dest,
|
||||||
|
line,
|
||||||
|
formatted: parsed_ins.display(display_options).to_string(),
|
||||||
|
orig: None,
|
||||||
});
|
});
|
||||||
address += ins_size as u32;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(ops)
|
Ok(ProcessCodeResult { ops, insts })
|
||||||
}
|
|
||||||
|
|
||||||
fn display_instruction(
|
|
||||||
&self,
|
|
||||||
resolved: ResolvedInstructionRef,
|
|
||||||
diff_config: &DiffObjConfig,
|
|
||||||
cb: &mut dyn FnMut(InstructionPart) -> Result<()>,
|
|
||||||
) -> Result<()> {
|
|
||||||
let (ins, parsed_ins) = self.parse_ins_ref(resolved.ins_ref, resolved.code, diff_config)?;
|
|
||||||
cb(InstructionPart::opcode(parsed_ins.mnemonic, resolved.ins_ref.opcode))?;
|
|
||||||
if ins == unarm::Ins::Data && resolved.relocation.is_some() {
|
|
||||||
cb(InstructionPart::reloc())?;
|
|
||||||
} else {
|
|
||||||
push_args(
|
|
||||||
ins,
|
|
||||||
&parsed_ins,
|
|
||||||
resolved.relocation,
|
|
||||||
resolved.ins_ref.address as u32,
|
|
||||||
self.display_options(diff_config),
|
|
||||||
cb,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn implcit_addend(
|
fn implcit_addend(
|
||||||
&self,
|
&self,
|
||||||
_file: &object::File<'_>,
|
_file: &File<'_>,
|
||||||
section: &object::Section,
|
section: &ObjSection,
|
||||||
address: u64,
|
address: u64,
|
||||||
_relocation: &object::Relocation,
|
reloc: &Relocation,
|
||||||
flags: RelocationFlags,
|
) -> anyhow::Result<i64> {
|
||||||
) -> Result<i64> {
|
|
||||||
let section_data = section.data()?;
|
|
||||||
let address = address as usize;
|
let address = address as usize;
|
||||||
Ok(match flags {
|
Ok(match reloc.flags() {
|
||||||
// ARM calls
|
// ARM calls
|
||||||
RelocationFlags::Elf(elf::R_ARM_PC24)
|
RelocationFlags::Elf { r_type: elf::R_ARM_PC24 }
|
||||||
| RelocationFlags::Elf(elf::R_ARM_XPC25)
|
| RelocationFlags::Elf { r_type: elf::R_ARM_XPC25 }
|
||||||
| RelocationFlags::Elf(elf::R_ARM_CALL) => {
|
| RelocationFlags::Elf { r_type: elf::R_ARM_CALL } => {
|
||||||
let data = section_data[address..address + 4].try_into()?;
|
let data = section.data[address..address + 4].try_into()?;
|
||||||
let addend = self.endianness.read_i32_bytes(data);
|
let addend = self.endianness.read_i32_bytes(data);
|
||||||
let imm24 = addend & 0xffffff;
|
let imm24 = addend & 0xffffff;
|
||||||
(imm24 << 2) << 8 >> 8
|
(imm24 << 2) << 8 >> 8
|
||||||
}
|
}
|
||||||
|
|
||||||
// Thumb calls
|
// Thumb calls
|
||||||
RelocationFlags::Elf(elf::R_ARM_THM_PC22)
|
RelocationFlags::Elf { r_type: elf::R_ARM_THM_PC22 }
|
||||||
| RelocationFlags::Elf(elf::R_ARM_THM_XPC22) => {
|
| RelocationFlags::Elf { r_type: elf::R_ARM_THM_XPC22 } => {
|
||||||
let data = section_data[address..address + 2].try_into()?;
|
let data = section.data[address..address + 2].try_into()?;
|
||||||
let high = self.endianness.read_i16_bytes(data) as i32;
|
let high = self.endianness.read_i16_bytes(data) as i32;
|
||||||
let data = section_data[address + 2..address + 4].try_into()?;
|
let data = section.data[address + 2..address + 4].try_into()?;
|
||||||
let low = self.endianness.read_i16_bytes(data) as i32;
|
let low = self.endianness.read_i16_bytes(data) as i32;
|
||||||
|
|
||||||
let imm22 = ((high & 0x7ff) << 11) | (low & 0x7ff);
|
let imm22 = ((high & 0x7ff) << 11) | (low & 0x7ff);
|
||||||
@ -390,8 +260,8 @@ impl Arch for ArchArm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Data
|
// Data
|
||||||
RelocationFlags::Elf(elf::R_ARM_ABS32) => {
|
RelocationFlags::Elf { r_type: elf::R_ARM_ABS32 } => {
|
||||||
let data = section_data[address..address + 4].try_into()?;
|
let data = section.data[address..address + 4].try_into()?;
|
||||||
self.endianness.read_i32_bytes(data)
|
self.endianness.read_i32_bytes(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -405,104 +275,51 @@ impl Arch for ArchArm {
|
|||||||
.and_then(|s| s.demangle(&cpp_demangle::DemangleOptions::default()).ok())
|
.and_then(|s| s.demangle(&cpp_demangle::DemangleOptions::default()).ok())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reloc_name(&self, flags: RelocationFlags) -> Option<&'static str> {
|
fn display_reloc(&self, flags: RelocationFlags) -> Cow<'static, str> {
|
||||||
match flags {
|
Cow::Owned(format!("<{flags:?}>"))
|
||||||
RelocationFlags::Elf(r_type) => match r_type {
|
|
||||||
elf::R_ARM_NONE => Some("R_ARM_NONE"),
|
|
||||||
elf::R_ARM_ABS32 => Some("R_ARM_ABS32"),
|
|
||||||
elf::R_ARM_REL32 => Some("R_ARM_REL32"),
|
|
||||||
elf::R_ARM_ABS16 => Some("R_ARM_ABS16"),
|
|
||||||
elf::R_ARM_ABS8 => Some("R_ARM_ABS8"),
|
|
||||||
elf::R_ARM_THM_PC22 => Some("R_ARM_THM_PC22"),
|
|
||||||
elf::R_ARM_THM_XPC22 => Some("R_ARM_THM_XPC22"),
|
|
||||||
elf::R_ARM_PC24 => Some("R_ARM_PC24"),
|
|
||||||
elf::R_ARM_XPC25 => Some("R_ARM_XPC25"),
|
|
||||||
elf::R_ARM_CALL => Some("R_ARM_CALL"),
|
|
||||||
_ => None,
|
|
||||||
},
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn data_reloc_size(&self, flags: RelocationFlags) -> usize {
|
|
||||||
match flags {
|
|
||||||
RelocationFlags::Elf(r_type) => match r_type {
|
|
||||||
elf::R_ARM_NONE => 0,
|
|
||||||
elf::R_ARM_ABS32 => 4,
|
|
||||||
elf::R_ARM_REL32 => 4,
|
|
||||||
elf::R_ARM_ABS16 => 2,
|
|
||||||
elf::R_ARM_ABS8 => 1,
|
|
||||||
elf::R_ARM_THM_PC22 => 4,
|
|
||||||
elf::R_ARM_THM_XPC22 => 4,
|
|
||||||
elf::R_ARM_PC24 => 4,
|
|
||||||
elf::R_ARM_XPC25 => 4,
|
|
||||||
elf::R_ARM_CALL => 4,
|
|
||||||
_ => 1,
|
|
||||||
},
|
|
||||||
_ => 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn symbol_address(&self, address: u64, kind: SymbolKind) -> u64 {
|
|
||||||
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_object_symbol(symbol).is_some() {
|
|
||||||
flags |= SymbolFlag::Hidden;
|
|
||||||
}
|
|
||||||
flags
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
struct DisasmMode {
|
struct DisasmMode {
|
||||||
address: u32,
|
address: u32,
|
||||||
mapping: unarm::ParseMode,
|
mapping: ParseMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DisasmMode {
|
impl DisasmMode {
|
||||||
fn from_object_symbol<'a>(sym: &object::Symbol<'a, '_, &'a [u8]>) -> Option<Self> {
|
fn from_symbol<'a>(sym: &Symbol<'a, '_, &'a [u8]>) -> Option<Self> {
|
||||||
sym.name()
|
if let Ok(name) = sym.name() {
|
||||||
.ok()
|
ParseMode::from_mapping_symbol(name)
|
||||||
.and_then(unarm::ParseMode::from_mapping_symbol)
|
.map(|mapping| DisasmMode { address: sym.address() as u32, mapping })
|
||||||
.map(|mapping| DisasmMode { address: sym.address() as u32, mapping })
|
} else {
|
||||||
}
|
None
|
||||||
|
}
|
||||||
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(
|
fn push_args(
|
||||||
ins: unarm::Ins,
|
parsed_ins: &ParsedIns,
|
||||||
parsed_ins: &unarm::ParsedIns,
|
config: &DiffObjConfig,
|
||||||
relocation: Option<ResolvedRelocation>,
|
reloc_arg: Option<usize>,
|
||||||
cur_addr: u32,
|
cur_addr: u32,
|
||||||
display_options: unarm::DisplayOptions,
|
display_options: DisplayOptions,
|
||||||
mut arg_cb: impl FnMut(InstructionPart) -> Result<()>,
|
) -> Result<(Vec<ObjInsArg>, Option<u64>)> {
|
||||||
) -> Result<()> {
|
let mut args = vec![];
|
||||||
let reloc_arg = find_reloc_arg(parsed_ins, relocation);
|
let mut branch_dest = None;
|
||||||
let mut writeback = false;
|
let mut writeback = false;
|
||||||
let mut deref = 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
|
// Emit punctuation before separator
|
||||||
if deref {
|
if deref {
|
||||||
match arg {
|
match arg {
|
||||||
args::Argument::OffsetImm(args::OffsetImm { post_indexed: true, value: _ })
|
Argument::OffsetImm(OffsetImm { post_indexed: true, value: _ })
|
||||||
| args::Argument::OffsetReg(args::OffsetReg {
|
| Argument::OffsetReg(OffsetReg { add: _, post_indexed: true, reg: _ })
|
||||||
add: _,
|
| Argument::CoOption(_) => {
|
||||||
post_indexed: true,
|
|
||||||
reg: _,
|
|
||||||
})
|
|
||||||
| args::Argument::CoOption(_) => {
|
|
||||||
deref = false;
|
deref = false;
|
||||||
arg_cb(InstructionPart::basic("]"))?;
|
args.push(ObjInsArg::PlainText("]".into()));
|
||||||
if writeback {
|
if writeback {
|
||||||
writeback = false;
|
writeback = false;
|
||||||
arg_cb(InstructionPart::opaque("!"))?;
|
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque("!".into())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
@ -510,169 +327,117 @@ fn push_args(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
arg_cb(InstructionPart::separator())?;
|
args.push(ObjInsArg::PlainText(config.separator().into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if reloc_arg == Some(i) {
|
if reloc_arg == Some(i) {
|
||||||
arg_cb(InstructionPart::reloc())?;
|
args.push(ObjInsArg::Reloc);
|
||||||
} else {
|
} else {
|
||||||
match arg {
|
match arg {
|
||||||
args::Argument::None => {}
|
Argument::None => {}
|
||||||
args::Argument::Reg(reg) => {
|
Argument::Reg(reg) => {
|
||||||
if reg.deref {
|
if reg.deref {
|
||||||
deref = true;
|
deref = true;
|
||||||
arg_cb(InstructionPart::basic("["))?;
|
args.push(ObjInsArg::PlainText("[".into()));
|
||||||
}
|
}
|
||||||
arg_cb(InstructionPart::opaque(
|
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(
|
||||||
reg.reg.display(display_options.reg_names).to_string(),
|
reg.reg.display(display_options.reg_names).to_string().into(),
|
||||||
))?;
|
)));
|
||||||
if reg.writeback {
|
if reg.writeback {
|
||||||
if reg.deref {
|
if reg.deref {
|
||||||
writeback = true;
|
writeback = true;
|
||||||
} else {
|
} else {
|
||||||
arg_cb(InstructionPart::opaque("!"))?;
|
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque("!".into())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
args::Argument::RegList(reg_list) => {
|
Argument::RegList(reg_list) => {
|
||||||
arg_cb(InstructionPart::basic("{"))?;
|
args.push(ObjInsArg::PlainText("{".into()));
|
||||||
let mut first = true;
|
let mut first = true;
|
||||||
for i in 0..16 {
|
for i in 0..16 {
|
||||||
if (reg_list.regs & (1 << i)) != 0 {
|
if (reg_list.regs & (1 << i)) != 0 {
|
||||||
if !first {
|
if !first {
|
||||||
arg_cb(InstructionPart::separator())?;
|
args.push(ObjInsArg::PlainText(config.separator().into()));
|
||||||
}
|
}
|
||||||
arg_cb(InstructionPart::opaque(
|
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(
|
||||||
args::Register::parse(i)
|
Register::parse(i)
|
||||||
.display(display_options.reg_names)
|
.display(display_options.reg_names)
|
||||||
.to_string(),
|
.to_string()
|
||||||
))?;
|
.into(),
|
||||||
|
)));
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
arg_cb(InstructionPart::basic("}"))?;
|
args.push(ObjInsArg::PlainText("}".into()));
|
||||||
if reg_list.user_mode {
|
if reg_list.user_mode {
|
||||||
arg_cb(InstructionPart::opaque("^"))?;
|
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque("^".to_string().into())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
args::Argument::UImm(value)
|
Argument::UImm(value) | Argument::CoOpcode(value) | Argument::SatImm(value) => {
|
||||||
| args::Argument::CoOpcode(value)
|
args.push(ObjInsArg::PlainText("#".into()));
|
||||||
| args::Argument::SatImm(value) => {
|
args.push(ObjInsArg::Arg(ObjInsArgValue::Unsigned(*value as u64)));
|
||||||
arg_cb(InstructionPart::basic("#"))?;
|
|
||||||
arg_cb(InstructionPart::unsigned(value))?;
|
|
||||||
}
|
}
|
||||||
args::Argument::SImm(value)
|
Argument::SImm(value)
|
||||||
| args::Argument::OffsetImm(args::OffsetImm { post_indexed: _, value }) => {
|
| Argument::OffsetImm(OffsetImm { post_indexed: _, value }) => {
|
||||||
arg_cb(InstructionPart::basic("#"))?;
|
args.push(ObjInsArg::PlainText("#".into()));
|
||||||
arg_cb(InstructionPart::signed(value))?;
|
args.push(ObjInsArg::Arg(ObjInsArgValue::Signed(*value as i64)));
|
||||||
}
|
}
|
||||||
args::Argument::BranchDest(value) => {
|
Argument::BranchDest(value) => {
|
||||||
arg_cb(InstructionPart::branch_dest(cur_addr.wrapping_add_signed(value)))?;
|
let dest = cur_addr.wrapping_add_signed(*value) as u64;
|
||||||
|
args.push(ObjInsArg::BranchDest(dest));
|
||||||
|
branch_dest = Some(dest);
|
||||||
}
|
}
|
||||||
args::Argument::CoOption(value) => {
|
Argument::CoOption(value) => {
|
||||||
arg_cb(InstructionPart::basic("{"))?;
|
args.push(ObjInsArg::PlainText("{".into()));
|
||||||
arg_cb(InstructionPart::unsigned(value))?;
|
args.push(ObjInsArg::Arg(ObjInsArgValue::Unsigned(*value as u64)));
|
||||||
arg_cb(InstructionPart::basic("}"))?;
|
args.push(ObjInsArg::PlainText("}".into()));
|
||||||
}
|
}
|
||||||
args::Argument::CoprocNum(value) => {
|
Argument::CoprocNum(value) => {
|
||||||
arg_cb(InstructionPart::opaque(format!("p{}", value)))?;
|
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(format!("p{}", value).into())));
|
||||||
}
|
}
|
||||||
args::Argument::ShiftImm(shift) => {
|
Argument::ShiftImm(shift) => {
|
||||||
arg_cb(InstructionPart::opaque(shift.op.to_string()))?;
|
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(shift.op.to_string().into())));
|
||||||
arg_cb(InstructionPart::basic(" #"))?;
|
args.push(ObjInsArg::PlainText(" #".into()));
|
||||||
arg_cb(InstructionPart::unsigned(shift.imm))?;
|
args.push(ObjInsArg::Arg(ObjInsArgValue::Unsigned(shift.imm as u64)));
|
||||||
}
|
}
|
||||||
args::Argument::ShiftReg(shift) => {
|
Argument::ShiftReg(shift) => {
|
||||||
arg_cb(InstructionPart::opaque(shift.op.to_string()))?;
|
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(shift.op.to_string().into())));
|
||||||
arg_cb(InstructionPart::basic(" "))?;
|
args.push(ObjInsArg::PlainText(" ".into()));
|
||||||
arg_cb(InstructionPart::opaque(
|
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(
|
||||||
shift.reg.display(display_options.reg_names).to_string(),
|
shift.reg.display(display_options.reg_names).to_string().into(),
|
||||||
))?;
|
)));
|
||||||
}
|
}
|
||||||
args::Argument::OffsetReg(offset) => {
|
Argument::OffsetReg(offset) => {
|
||||||
if !offset.add {
|
if !offset.add {
|
||||||
arg_cb(InstructionPart::basic("-"))?;
|
args.push(ObjInsArg::PlainText("-".into()));
|
||||||
}
|
}
|
||||||
arg_cb(InstructionPart::opaque(
|
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(
|
||||||
offset.reg.display(display_options.reg_names).to_string(),
|
offset.reg.display(display_options.reg_names).to_string().into(),
|
||||||
))?;
|
)));
|
||||||
}
|
}
|
||||||
args::Argument::CpsrMode(mode) => {
|
Argument::CpsrMode(mode) => {
|
||||||
arg_cb(InstructionPart::basic("#"))?;
|
args.push(ObjInsArg::PlainText("#".into()));
|
||||||
arg_cb(InstructionPart::unsigned(mode.mode))?;
|
args.push(ObjInsArg::Arg(ObjInsArgValue::Unsigned(mode.mode as u64)));
|
||||||
if mode.writeback {
|
if mode.writeback {
|
||||||
arg_cb(InstructionPart::opaque("!"))?;
|
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque("!".into())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
args::Argument::CoReg(_)
|
Argument::CoReg(_)
|
||||||
| args::Argument::StatusReg(_)
|
| Argument::StatusReg(_)
|
||||||
| args::Argument::StatusMask(_)
|
| Argument::StatusMask(_)
|
||||||
| args::Argument::Shift(_)
|
| Argument::Shift(_)
|
||||||
| args::Argument::CpsrFlags(_)
|
| Argument::CpsrFlags(_)
|
||||||
| args::Argument::Endian(_) => {
|
| Argument::Endian(_) => args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(
|
||||||
arg_cb(InstructionPart::opaque(
|
arg.display(display_options, None).to_string().into(),
|
||||||
arg.display(display_options, None).to_string(),
|
))),
|
||||||
))?;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if deref {
|
if deref {
|
||||||
arg_cb(InstructionPart::basic("]"))?;
|
args.push(ObjInsArg::PlainText("]".into()));
|
||||||
if writeback {
|
if writeback {
|
||||||
arg_cb(InstructionPart::opaque("!"))?;
|
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque("!".into())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok((args, branch_dest))
|
||||||
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(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_reloc_arg(
|
|
||||||
parsed_ins: &unarm::ParsedIns,
|
|
||||||
relocation: Option<ResolvedRelocation>,
|
|
||||||
) -> Option<usize> {
|
|
||||||
if let Some(resolved) = relocation {
|
|
||||||
match resolved.relocation.flags {
|
|
||||||
// Calls
|
|
||||||
RelocationFlags::Elf(elf::R_ARM_THM_XPC22)
|
|
||||||
| RelocationFlags::Elf(elf::R_ARM_THM_PC22)
|
|
||||||
| RelocationFlags::Elf(elf::R_ARM_PC24)
|
|
||||||
| RelocationFlags::Elf(elf::R_ARM_XPC25)
|
|
||||||
| RelocationFlags::Elf(elf::R_ARM_CALL) => {
|
|
||||||
parsed_ins.args.iter().rposition(|a| matches!(a, args::Argument::BranchDest(_)))
|
|
||||||
}
|
|
||||||
// Data
|
|
||||||
RelocationFlags::Elf(elf::R_ARM_ABS32) => {
|
|
||||||
parsed_ins.args.iter().rposition(|a| matches!(a, args::Argument::UImm(_)))
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,35 +1,31 @@
|
|||||||
use alloc::{
|
use std::{borrow::Cow, collections::BTreeMap, sync::Mutex};
|
||||||
collections::{BTreeMap, BTreeSet},
|
|
||||||
string::{String, ToString},
|
|
||||||
vec::Vec,
|
|
||||||
};
|
|
||||||
|
|
||||||
use anyhow::{Result, bail};
|
use anyhow::{anyhow, bail, Result};
|
||||||
use object::{Endian as _, Object as _, ObjectSection as _, ObjectSymbol as _, elf};
|
use object::{
|
||||||
use rabbitizer::{
|
elf, Endian, Endianness, File, FileFlags, Object, ObjectSection, ObjectSymbol, Relocation,
|
||||||
IsaExtension, IsaVersion, Vram,
|
RelocationFlags, RelocationTarget,
|
||||||
abi::Abi,
|
|
||||||
operands::{IU16, ValuedOperand},
|
|
||||||
registers_meta::Register,
|
|
||||||
};
|
};
|
||||||
|
use rabbitizer::{config, Abi, InstrCategory, Instruction, OperandType};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
arch::Arch,
|
arch::{ObjArch, ProcessCodeResult},
|
||||||
diff::{DiffObjConfig, MipsAbi, MipsInstrCategory, display::InstructionPart},
|
diff::{DiffObjConfig, MipsAbi, MipsInstrCategory},
|
||||||
obj::{
|
obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection},
|
||||||
InstructionArg, InstructionArgValue, InstructionRef, Relocation, RelocationFlags,
|
|
||||||
ResolvedInstructionRef, ResolvedRelocation, SymbolFlag, SymbolFlagSet,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
static RABBITIZER_MUTEX: Mutex<()> = Mutex::new(());
|
||||||
pub struct ArchMips {
|
|
||||||
pub endianness: object::Endianness,
|
fn configure_rabbitizer(abi: Abi) {
|
||||||
|
unsafe {
|
||||||
|
config::RabbitizerConfig_Cfg.reg_names.fpr_abi_names = abi;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ObjArchMips {
|
||||||
|
pub endianness: Endianness,
|
||||||
pub abi: Abi,
|
pub abi: Abi,
|
||||||
pub isa_extension: Option<IsaExtension>,
|
pub instr_category: InstrCategory,
|
||||||
pub ri_gp_value: i32,
|
pub ri_gp_value: i32,
|
||||||
pub paired_relocations: Vec<BTreeMap<u64, i64>>,
|
|
||||||
pub ignored_symbols: BTreeSet<usize>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const EF_MIPS_ABI: u32 = 0x0000F000;
|
const EF_MIPS_ABI: u32 = 0x0000F000;
|
||||||
@ -40,13 +36,13 @@ const EF_MIPS_MACH_5900: u32 = 0x00920000;
|
|||||||
|
|
||||||
const R_MIPS15_S3: u32 = 119;
|
const R_MIPS15_S3: u32 = 119;
|
||||||
|
|
||||||
impl ArchMips {
|
impl ObjArchMips {
|
||||||
pub fn new(object: &object::File) -> Result<Self> {
|
pub fn new(object: &File) -> Result<Self> {
|
||||||
let mut abi = Abi::O32;
|
let mut abi = Abi::NUMERIC;
|
||||||
let mut isa_extension = None;
|
let mut instr_category = InstrCategory::CPU;
|
||||||
match object.flags() {
|
match object.flags() {
|
||||||
object::FileFlags::None => {}
|
FileFlags::None => {}
|
||||||
object::FileFlags::Elf { e_flags, .. } => {
|
FileFlags::Elf { e_flags, .. } => {
|
||||||
abi = match e_flags & EF_MIPS_ABI {
|
abi = match e_flags & EF_MIPS_ABI {
|
||||||
elf::EF_MIPS_ABI_O32 | elf::EF_MIPS_ABI_O64 => Abi::O32,
|
elf::EF_MIPS_ABI_O32 | elf::EF_MIPS_ABI_O64 => Abi::O32,
|
||||||
elf::EF_MIPS_ABI_EABI32 | elf::EF_MIPS_ABI_EABI64 => Abi::N32,
|
elf::EF_MIPS_ABI_EABI32 | elf::EF_MIPS_ABI_EABI64 => Abi::N32,
|
||||||
@ -54,14 +50,14 @@ impl ArchMips {
|
|||||||
if e_flags & elf::EF_MIPS_ABI2 != 0 {
|
if e_flags & elf::EF_MIPS_ABI2 != 0 {
|
||||||
Abi::N32
|
Abi::N32
|
||||||
} else {
|
} else {
|
||||||
Abi::O32
|
Abi::NUMERIC
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
isa_extension = match e_flags & EF_MIPS_MACH {
|
instr_category = match e_flags & EF_MIPS_MACH {
|
||||||
EF_MIPS_MACH_ALLEGREX => Some(IsaExtension::R4000ALLEGREX),
|
EF_MIPS_MACH_ALLEGREX => InstrCategory::R4000ALLEGREX,
|
||||||
EF_MIPS_MACH_5900 => Some(IsaExtension::R5900EE),
|
EF_MIPS_MACH_5900 => InstrCategory::R5900,
|
||||||
_ => None,
|
_ => InstrCategory::CPU,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
_ => bail!("Unsupported MIPS file flags"),
|
_ => bail!("Unsupported MIPS file flags"),
|
||||||
@ -69,193 +65,169 @@ impl ArchMips {
|
|||||||
|
|
||||||
// Parse the ri_gp_value stored in .reginfo to be able to correctly
|
// Parse the ri_gp_value stored in .reginfo to be able to correctly
|
||||||
// calculate R_MIPS_GPREL16 relocations later. The value is stored
|
// calculate R_MIPS_GPREL16 relocations later. The value is stored
|
||||||
// 0x14 bytes into .reginfo (on 32-bit platforms)
|
// 0x14 bytes into .reginfo (on 32 bit platforms)
|
||||||
let endianness = object.endianness();
|
|
||||||
let ri_gp_value = object
|
let ri_gp_value = object
|
||||||
.section_by_name(".reginfo")
|
.section_by_name(".reginfo")
|
||||||
.and_then(|section| section.data().ok())
|
.and_then(|section| section.data().ok())
|
||||||
.and_then(|data| data.get(0x14..0x18))
|
.and_then(|data| data.get(0x14..0x18))
|
||||||
.and_then(|s| s.try_into().ok())
|
.and_then(|s| s.try_into().ok())
|
||||||
.map(|bytes| endianness.read_i32_bytes(bytes))
|
.map(|bytes| object.endianness().read_i32_bytes(bytes))
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
|
|
||||||
// Parse all relocations to pair R_MIPS_HI16 and R_MIPS_LO16. Since the instructions only
|
Ok(Self { endianness: object.endianness(), abi, instr_category, ri_gp_value })
|
||||||
// have 16-bit immediate fields, the 32-bit addend is split across the two relocations.
|
|
||||||
// R_MIPS_LO16 relocations without an immediately preceding R_MIPS_HI16 use the last seen
|
|
||||||
// R_MIPS_HI16 addend.
|
|
||||||
// See https://refspecs.linuxfoundation.org/elf/mipsabi.pdf pages 4-17 and 4-18
|
|
||||||
let mut paired_relocations = Vec::with_capacity(object.sections().count() + 1);
|
|
||||||
for obj_section in object.sections() {
|
|
||||||
let data = obj_section.data()?;
|
|
||||||
let mut last_hi = None;
|
|
||||||
let mut last_hi_addend = 0;
|
|
||||||
let mut addends = BTreeMap::new();
|
|
||||||
for (addr, reloc) in obj_section.relocations() {
|
|
||||||
if !reloc.has_implicit_addend() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
match reloc.flags() {
|
|
||||||
object::RelocationFlags::Elf { r_type: elf::R_MIPS_HI16 } => {
|
|
||||||
let code = data[addr as usize..addr as usize + 4].try_into()?;
|
|
||||||
let addend = ((endianness.read_u32_bytes(code) & 0x0000FFFF) << 16) as i32;
|
|
||||||
last_hi = Some(addr);
|
|
||||||
last_hi_addend = addend;
|
|
||||||
}
|
|
||||||
object::RelocationFlags::Elf { r_type: elf::R_MIPS_LO16 } => {
|
|
||||||
let code = data[addr as usize..addr as usize + 4].try_into()?;
|
|
||||||
let addend = (endianness.read_u32_bytes(code) & 0x0000FFFF) as i16 as i32;
|
|
||||||
let full_addend = (last_hi_addend + addend) as i64;
|
|
||||||
if let Some(hi_addr) = last_hi.take() {
|
|
||||||
addends.insert(hi_addr, full_addend);
|
|
||||||
}
|
|
||||||
addends.insert(addr, full_addend);
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
last_hi = None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let section_index = obj_section.index().0;
|
|
||||||
if section_index >= paired_relocations.len() {
|
|
||||||
paired_relocations.resize_with(section_index + 1, BTreeMap::new);
|
|
||||||
}
|
|
||||||
paired_relocations[section_index] = addends;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut ignored_symbols = BTreeSet::new();
|
|
||||||
for obj_symbol in object.symbols() {
|
|
||||||
let Ok(name) = obj_symbol.name() else { continue };
|
|
||||||
if let Some(prefix) = name.strip_suffix(".NON_MATCHING") {
|
|
||||||
ignored_symbols.insert(obj_symbol.index().0);
|
|
||||||
if let Some(target_symbol) = object.symbol_by_name(prefix) {
|
|
||||||
ignored_symbols.insert(target_symbol.index().0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
endianness,
|
|
||||||
abi,
|
|
||||||
isa_extension,
|
|
||||||
ri_gp_value,
|
|
||||||
paired_relocations,
|
|
||||||
ignored_symbols,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn instruction_flags(&self, diff_config: &DiffObjConfig) -> rabbitizer::InstructionFlags {
|
|
||||||
let isa_extension = match diff_config.mips_instr_category {
|
|
||||||
MipsInstrCategory::Auto => self.isa_extension,
|
|
||||||
MipsInstrCategory::Cpu => None,
|
|
||||||
MipsInstrCategory::Rsp => Some(IsaExtension::RSP),
|
|
||||||
MipsInstrCategory::R3000gte => Some(IsaExtension::R3000GTE),
|
|
||||||
MipsInstrCategory::R4000allegrex => Some(IsaExtension::R4000ALLEGREX),
|
|
||||||
MipsInstrCategory::R5900 => Some(IsaExtension::R5900EE),
|
|
||||||
};
|
|
||||||
match isa_extension {
|
|
||||||
Some(extension) => rabbitizer::InstructionFlags::new_extension(extension),
|
|
||||||
None => rabbitizer::InstructionFlags::new_isa(IsaVersion::MIPS_III, None),
|
|
||||||
}
|
|
||||||
.with_abi(match diff_config.mips_abi {
|
|
||||||
MipsAbi::Auto => self.abi,
|
|
||||||
MipsAbi::O32 => Abi::O32,
|
|
||||||
MipsAbi::N32 => Abi::N32,
|
|
||||||
MipsAbi::N64 => Abi::N64,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn instruction_display_flags(
|
|
||||||
&self,
|
|
||||||
diff_config: &DiffObjConfig,
|
|
||||||
) -> rabbitizer::InstructionDisplayFlags {
|
|
||||||
rabbitizer::InstructionDisplayFlags::default()
|
|
||||||
.with_unknown_instr_comment(false)
|
|
||||||
.with_use_dollar(diff_config.mips_register_prefix)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_ins_ref(
|
|
||||||
&self,
|
|
||||||
ins_ref: InstructionRef,
|
|
||||||
code: &[u8],
|
|
||||||
diff_config: &DiffObjConfig,
|
|
||||||
) -> Result<rabbitizer::Instruction> {
|
|
||||||
Ok(rabbitizer::Instruction::new(
|
|
||||||
self.endianness.read_u32_bytes(code.try_into()?),
|
|
||||||
Vram::new(ins_ref.address as u32),
|
|
||||||
self.instruction_flags(diff_config),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Arch for ArchMips {
|
impl ObjArch for ObjArchMips {
|
||||||
fn scan_instructions_internal(
|
fn process_code(
|
||||||
&self,
|
&self,
|
||||||
address: u64,
|
address: u64,
|
||||||
code: &[u8],
|
code: &[u8],
|
||||||
_section_index: usize,
|
_section_index: usize,
|
||||||
_relocations: &[Relocation],
|
relocations: &[ObjReloc],
|
||||||
diff_config: &DiffObjConfig,
|
line_info: &BTreeMap<u64, u32>,
|
||||||
) -> Result<Vec<InstructionRef>> {
|
config: &DiffObjConfig,
|
||||||
let instruction_flags = self.instruction_flags(diff_config);
|
) -> Result<ProcessCodeResult> {
|
||||||
let mut ops = Vec::<InstructionRef>::with_capacity(code.len() / 4);
|
let _guard = RABBITIZER_MUTEX.lock().map_err(|e| anyhow!("Failed to lock mutex: {e}"))?;
|
||||||
let mut cur_addr = address as u32;
|
configure_rabbitizer(match config.mips_abi {
|
||||||
|
MipsAbi::Auto => self.abi,
|
||||||
|
MipsAbi::O32 => Abi::O32,
|
||||||
|
MipsAbi::N32 => Abi::N32,
|
||||||
|
MipsAbi::N64 => Abi::N64,
|
||||||
|
});
|
||||||
|
let instr_category = match config.mips_instr_category {
|
||||||
|
MipsInstrCategory::Auto => self.instr_category,
|
||||||
|
MipsInstrCategory::Cpu => InstrCategory::CPU,
|
||||||
|
MipsInstrCategory::Rsp => InstrCategory::RSP,
|
||||||
|
MipsInstrCategory::R3000Gte => InstrCategory::R3000GTE,
|
||||||
|
MipsInstrCategory::R4000Allegrex => InstrCategory::R4000ALLEGREX,
|
||||||
|
MipsInstrCategory::R5900 => InstrCategory::R5900,
|
||||||
|
};
|
||||||
|
|
||||||
|
let start_address = address;
|
||||||
|
let end_address = address + code.len() as u64;
|
||||||
|
let ins_count = code.len() / 4;
|
||||||
|
let mut ops = Vec::<u16>::with_capacity(ins_count);
|
||||||
|
let mut insts = Vec::<ObjIns>::with_capacity(ins_count);
|
||||||
|
let mut cur_addr = start_address as u32;
|
||||||
for chunk in code.chunks_exact(4) {
|
for chunk in code.chunks_exact(4) {
|
||||||
|
let reloc = relocations.iter().find(|r| (r.address as u32 & !3) == cur_addr);
|
||||||
let code = self.endianness.read_u32_bytes(chunk.try_into()?);
|
let code = self.endianness.read_u32_bytes(chunk.try_into()?);
|
||||||
let instruction =
|
let instruction = Instruction::new(code, cur_addr, instr_category);
|
||||||
rabbitizer::Instruction::new(code, Vram::new(cur_addr), instruction_flags);
|
|
||||||
let opcode = instruction.opcode() as u16;
|
let formatted = instruction.disassemble(None, 0);
|
||||||
let branch_dest = instruction.get_branch_vram_generic().map(|v| v.inner() as u64);
|
let op = instruction.unique_id as u16;
|
||||||
ops.push(InstructionRef { address: cur_addr as u64, size: 4, opcode, branch_dest });
|
ops.push(op);
|
||||||
|
|
||||||
|
let mnemonic = instruction.opcode_name().to_string();
|
||||||
|
let is_branch = instruction.is_branch();
|
||||||
|
let branch_offset = instruction.branch_offset();
|
||||||
|
let mut branch_dest = if is_branch {
|
||||||
|
cur_addr.checked_add_signed(branch_offset).map(|a| a as u64)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let operands = instruction.get_operands_slice();
|
||||||
|
let mut args = Vec::with_capacity(operands.len() + 1);
|
||||||
|
for (idx, op) in operands.iter().enumerate() {
|
||||||
|
if idx > 0 {
|
||||||
|
args.push(ObjInsArg::PlainText(config.separator().into()));
|
||||||
|
}
|
||||||
|
|
||||||
|
match op {
|
||||||
|
OperandType::cpu_immediate
|
||||||
|
| OperandType::cpu_label
|
||||||
|
| OperandType::cpu_branch_target_label => {
|
||||||
|
if let Some(reloc) = reloc {
|
||||||
|
if matches!(&reloc.target_section, Some(s) if s == ".text")
|
||||||
|
&& reloc.target.address > start_address
|
||||||
|
&& reloc.target.address < end_address
|
||||||
|
{
|
||||||
|
args.push(ObjInsArg::BranchDest(reloc.target.address));
|
||||||
|
} else {
|
||||||
|
push_reloc(&mut args, reloc)?;
|
||||||
|
branch_dest = None;
|
||||||
|
}
|
||||||
|
} else if let Some(branch_dest) = branch_dest {
|
||||||
|
args.push(ObjInsArg::BranchDest(branch_dest));
|
||||||
|
} else {
|
||||||
|
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(
|
||||||
|
op.disassemble(&instruction, None).into(),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OperandType::cpu_immediate_base => {
|
||||||
|
if let Some(reloc) = reloc {
|
||||||
|
push_reloc(&mut args, reloc)?;
|
||||||
|
} else {
|
||||||
|
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(
|
||||||
|
OperandType::cpu_immediate.disassemble(&instruction, None).into(),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
args.push(ObjInsArg::PlainText("(".into()));
|
||||||
|
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(
|
||||||
|
OperandType::cpu_rs.disassemble(&instruction, None).into(),
|
||||||
|
)));
|
||||||
|
args.push(ObjInsArg::PlainText(")".into()));
|
||||||
|
}
|
||||||
|
// OperandType::r5900_immediate15 => match reloc {
|
||||||
|
// Some(reloc)
|
||||||
|
// if reloc.flags == RelocationFlags::Elf { r_type: R_MIPS15_S3 } =>
|
||||||
|
// {
|
||||||
|
// push_reloc(&mut args, reloc)?;
|
||||||
|
// }
|
||||||
|
// _ => {
|
||||||
|
// args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(
|
||||||
|
// op.disassemble(&instruction, None).into(),
|
||||||
|
// )));
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
_ => {
|
||||||
|
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(
|
||||||
|
op.disassemble(&instruction, None).into(),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let line = line_info.range(..=cur_addr as u64).last().map(|(_, &b)| b);
|
||||||
|
insts.push(ObjIns {
|
||||||
|
address: cur_addr as u64,
|
||||||
|
size: 4,
|
||||||
|
op,
|
||||||
|
mnemonic,
|
||||||
|
args,
|
||||||
|
reloc: reloc.cloned(),
|
||||||
|
branch_dest,
|
||||||
|
line,
|
||||||
|
formatted,
|
||||||
|
orig: None,
|
||||||
|
});
|
||||||
cur_addr += 4;
|
cur_addr += 4;
|
||||||
}
|
}
|
||||||
Ok(ops)
|
Ok(ProcessCodeResult { ops, insts })
|
||||||
}
|
|
||||||
|
|
||||||
fn display_instruction(
|
|
||||||
&self,
|
|
||||||
resolved: ResolvedInstructionRef,
|
|
||||||
diff_config: &DiffObjConfig,
|
|
||||||
cb: &mut dyn FnMut(InstructionPart) -> Result<()>,
|
|
||||||
) -> Result<()> {
|
|
||||||
let instruction = self.parse_ins_ref(resolved.ins_ref, resolved.code, diff_config)?;
|
|
||||||
let display_flags = self.instruction_display_flags(diff_config);
|
|
||||||
let opcode = instruction.opcode();
|
|
||||||
cb(InstructionPart::opcode(opcode.name(), opcode as u16))?;
|
|
||||||
push_args(&instruction, resolved.relocation, &display_flags, cb)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn implcit_addend(
|
fn implcit_addend(
|
||||||
&self,
|
&self,
|
||||||
file: &object::File<'_>,
|
file: &File<'_>,
|
||||||
section: &object::Section,
|
section: &ObjSection,
|
||||||
address: u64,
|
address: u64,
|
||||||
reloc: &object::Relocation,
|
reloc: &Relocation,
|
||||||
flags: RelocationFlags,
|
|
||||||
) -> Result<i64> {
|
) -> Result<i64> {
|
||||||
// Check for paired R_MIPS_HI16 and R_MIPS_LO16 relocations.
|
let data = section.data[address as usize..address as usize + 4].try_into()?;
|
||||||
if let RelocationFlags::Elf(elf::R_MIPS_HI16 | elf::R_MIPS_LO16) = flags {
|
let addend = self.endianness.read_u32_bytes(data);
|
||||||
if let Some(addend) = self
|
Ok(match reloc.flags() {
|
||||||
.paired_relocations
|
RelocationFlags::Elf { r_type: elf::R_MIPS_32 } => addend as i64,
|
||||||
.get(section.index().0)
|
RelocationFlags::Elf { r_type: elf::R_MIPS_26 } => ((addend & 0x03FFFFFF) << 2) as i64,
|
||||||
.and_then(|m| m.get(&address).copied())
|
RelocationFlags::Elf { r_type: elf::R_MIPS_HI16 } => {
|
||||||
{
|
((addend & 0x0000FFFF) << 16) as i32 as i64
|
||||||
return Ok(addend);
|
|
||||||
}
|
}
|
||||||
}
|
RelocationFlags::Elf {
|
||||||
|
r_type: elf::R_MIPS_LO16 | elf::R_MIPS_GOT16 | elf::R_MIPS_CALL16,
|
||||||
let data = section.data()?;
|
} => (addend & 0x0000FFFF) as i16 as i64,
|
||||||
let code = data[address as usize..address as usize + 4].try_into()?;
|
RelocationFlags::Elf { r_type: elf::R_MIPS_GPREL16 | elf::R_MIPS_LITERAL } => {
|
||||||
let addend = self.endianness.read_u32_bytes(code);
|
let RelocationTarget::Symbol(idx) = reloc.target() else {
|
||||||
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");
|
bail!("Unsupported R_MIPS_GPREL16 relocation against a non-symbol");
|
||||||
};
|
};
|
||||||
let sym = file.symbol_by_index(idx)?;
|
let sym = file.symbol_by_index(idx)?;
|
||||||
@ -268,171 +240,66 @@ impl Arch for ArchMips {
|
|||||||
(addend & 0x0000FFFF) as i16 as i64
|
(addend & 0x0000FFFF) as i16 as i64
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RelocationFlags::Elf(elf::R_MIPS_PC16) => 0, // PC-relative relocation
|
RelocationFlags::Elf { r_type: elf::R_MIPS_PC16 } => 0, // PC-relative relocation
|
||||||
RelocationFlags::Elf(R_MIPS15_S3) => ((addend & 0x001FFFC0) >> 3) as i64,
|
RelocationFlags::Elf { r_type: R_MIPS15_S3 } => ((addend & 0x001FFFC0) >> 3) as i64,
|
||||||
flags => bail!("Unsupported MIPS implicit relocation {flags:?}"),
|
flags => bail!("Unsupported MIPS implicit relocation {flags:?}"),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn demangle(&self, name: &str) -> Option<String> {
|
fn display_reloc(&self, flags: RelocationFlags) -> Cow<'static, str> {
|
||||||
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> {
|
|
||||||
match flags {
|
match flags {
|
||||||
RelocationFlags::Elf(r_type) => match r_type {
|
RelocationFlags::Elf { r_type } => match r_type {
|
||||||
elf::R_MIPS_NONE => Some("R_MIPS_NONE"),
|
elf::R_MIPS_32 => Cow::Borrowed("R_MIPS_32"),
|
||||||
elf::R_MIPS_16 => Some("R_MIPS_16"),
|
elf::R_MIPS_26 => Cow::Borrowed("R_MIPS_26"),
|
||||||
elf::R_MIPS_32 => Some("R_MIPS_32"),
|
elf::R_MIPS_HI16 => Cow::Borrowed("R_MIPS_HI16"),
|
||||||
elf::R_MIPS_26 => Some("R_MIPS_26"),
|
elf::R_MIPS_LO16 => Cow::Borrowed("R_MIPS_LO16"),
|
||||||
elf::R_MIPS_HI16 => Some("R_MIPS_HI16"),
|
elf::R_MIPS_GPREL16 => Cow::Borrowed("R_MIPS_GPREL16"),
|
||||||
elf::R_MIPS_LO16 => Some("R_MIPS_LO16"),
|
elf::R_MIPS_LITERAL => Cow::Borrowed("R_MIPS_LITERAL"),
|
||||||
elf::R_MIPS_GPREL16 => Some("R_MIPS_GPREL16"),
|
elf::R_MIPS_GOT16 => Cow::Borrowed("R_MIPS_GOT16"),
|
||||||
elf::R_MIPS_LITERAL => Some("R_MIPS_LITERAL"),
|
elf::R_MIPS_PC16 => Cow::Borrowed("R_MIPS_PC16"),
|
||||||
elf::R_MIPS_GOT16 => Some("R_MIPS_GOT16"),
|
elf::R_MIPS_CALL16 => Cow::Borrowed("R_MIPS_CALL16"),
|
||||||
elf::R_MIPS_PC16 => Some("R_MIPS_PC16"),
|
R_MIPS15_S3 => Cow::Borrowed("R_MIPS15_S3"),
|
||||||
elf::R_MIPS_CALL16 => Some("R_MIPS_CALL16"),
|
_ => Cow::Owned(format!("<{flags:?}>")),
|
||||||
R_MIPS15_S3 => Some("R_MIPS15_S3"),
|
|
||||||
_ => None,
|
|
||||||
},
|
},
|
||||||
_ => None,
|
_ => Cow::Owned(format!("<{flags:?}>")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn data_reloc_size(&self, flags: RelocationFlags) -> usize {
|
|
||||||
match flags {
|
|
||||||
RelocationFlags::Elf(r_type) => match r_type {
|
|
||||||
elf::R_MIPS_16 => 2,
|
|
||||||
elf::R_MIPS_32 => 4,
|
|
||||||
_ => 1,
|
|
||||||
},
|
|
||||||
_ => 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn extra_symbol_flags(&self, symbol: &object::Symbol) -> SymbolFlagSet {
|
|
||||||
let mut flags = SymbolFlagSet::default();
|
|
||||||
if self.ignored_symbols.contains(&symbol.index().0) {
|
|
||||||
flags |= SymbolFlag::Ignored;
|
|
||||||
}
|
|
||||||
flags
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_args(
|
fn push_reloc(args: &mut Vec<ObjInsArg>, reloc: &ObjReloc) -> Result<()> {
|
||||||
instruction: &rabbitizer::Instruction,
|
|
||||||
relocation: Option<ResolvedRelocation>,
|
|
||||||
display_flags: &rabbitizer::InstructionDisplayFlags,
|
|
||||||
mut arg_cb: impl FnMut(InstructionPart) -> Result<()>,
|
|
||||||
) -> Result<()> {
|
|
||||||
let operands = instruction.valued_operands_iter();
|
|
||||||
for (idx, op) in operands.enumerate() {
|
|
||||||
if idx > 0 {
|
|
||||||
arg_cb(InstructionPart::separator())?;
|
|
||||||
}
|
|
||||||
|
|
||||||
match op {
|
|
||||||
ValuedOperand::core_immediate(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),
|
|
||||||
})?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ValuedOperand::core_label(..) | ValuedOperand::core_branch_target_label(..) => {
|
|
||||||
if let Some(resolved) = relocation {
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
arg_cb(InstructionPart::branch_dest(branch_dest))?;
|
|
||||||
} else {
|
|
||||||
arg_cb(InstructionPart::opaque(
|
|
||||||
op.display(instruction, display_flags, None::<&str>).to_string(),
|
|
||||||
))?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ValuedOperand::core_immediate_base(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::basic("("))?;
|
|
||||||
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 {
|
|
||||||
// Some(resolved)
|
|
||||||
// if resolved.relocation.flags == RelocationFlags::Elf(R_MIPS15_S3) =>
|
|
||||||
// {
|
|
||||||
// push_reloc(&resolved.relocation, &mut arg_cb)?;
|
|
||||||
// }
|
|
||||||
// _ => {
|
|
||||||
// arg_cb(InstructionPart::opaque(op.disassemble(&instruction, None)))?;
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
_ => {
|
|
||||||
arg_cb(InstructionPart::opaque(
|
|
||||||
op.display(instruction, display_flags, None::<&str>).to_string(),
|
|
||||||
))?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn push_reloc(
|
|
||||||
reloc: &Relocation,
|
|
||||||
mut arg_cb: impl FnMut(InstructionPart) -> Result<()>,
|
|
||||||
) -> Result<()> {
|
|
||||||
match reloc.flags {
|
match reloc.flags {
|
||||||
RelocationFlags::Elf(r_type) => match r_type {
|
RelocationFlags::Elf { r_type } => match r_type {
|
||||||
elf::R_MIPS_HI16 => {
|
elf::R_MIPS_HI16 => {
|
||||||
arg_cb(InstructionPart::basic("%hi("))?;
|
args.push(ObjInsArg::PlainText("%hi(".into()));
|
||||||
arg_cb(InstructionPart::reloc())?;
|
args.push(ObjInsArg::Reloc);
|
||||||
arg_cb(InstructionPart::basic(")"))?;
|
args.push(ObjInsArg::PlainText(")".into()));
|
||||||
}
|
}
|
||||||
elf::R_MIPS_LO16 => {
|
elf::R_MIPS_LO16 => {
|
||||||
arg_cb(InstructionPart::basic("%lo("))?;
|
args.push(ObjInsArg::PlainText("%lo(".into()));
|
||||||
arg_cb(InstructionPart::reloc())?;
|
args.push(ObjInsArg::Reloc);
|
||||||
arg_cb(InstructionPart::basic(")"))?;
|
args.push(ObjInsArg::PlainText(")".into()));
|
||||||
}
|
}
|
||||||
elf::R_MIPS_GOT16 => {
|
elf::R_MIPS_GOT16 => {
|
||||||
arg_cb(InstructionPart::basic("%got("))?;
|
args.push(ObjInsArg::PlainText("%got(".into()));
|
||||||
arg_cb(InstructionPart::reloc())?;
|
args.push(ObjInsArg::Reloc);
|
||||||
arg_cb(InstructionPart::basic(")"))?;
|
args.push(ObjInsArg::PlainText(")".into()));
|
||||||
}
|
}
|
||||||
elf::R_MIPS_CALL16 => {
|
elf::R_MIPS_CALL16 => {
|
||||||
arg_cb(InstructionPart::basic("%call16("))?;
|
args.push(ObjInsArg::PlainText("%call16(".into()));
|
||||||
arg_cb(InstructionPart::reloc())?;
|
args.push(ObjInsArg::Reloc);
|
||||||
arg_cb(InstructionPart::basic(")"))?;
|
args.push(ObjInsArg::PlainText(")".into()));
|
||||||
}
|
}
|
||||||
elf::R_MIPS_GPREL16 => {
|
elf::R_MIPS_GPREL16 => {
|
||||||
arg_cb(InstructionPart::basic("%gp_rel("))?;
|
args.push(ObjInsArg::PlainText("%gp_rel(".into()));
|
||||||
arg_cb(InstructionPart::reloc())?;
|
args.push(ObjInsArg::Reloc);
|
||||||
arg_cb(InstructionPart::basic(")"))?;
|
args.push(ObjInsArg::PlainText(")".into()));
|
||||||
}
|
}
|
||||||
elf::R_MIPS_32
|
elf::R_MIPS_32
|
||||||
| elf::R_MIPS_26
|
| elf::R_MIPS_26
|
||||||
| elf::R_MIPS_LITERAL
|
| elf::R_MIPS_LITERAL
|
||||||
| elf::R_MIPS_PC16
|
| elf::R_MIPS_PC16
|
||||||
| R_MIPS15_S3 => {
|
| R_MIPS15_S3 => {
|
||||||
arg_cb(InstructionPart::reloc())?;
|
args.push(ObjInsArg::Reloc);
|
||||||
}
|
}
|
||||||
_ => bail!("Unsupported ELF MIPS relocation type {r_type}"),
|
_ => bail!("Unsupported ELF MIPS relocation type {r_type}"),
|
||||||
},
|
},
|
||||||
|
@ -1,32 +1,21 @@
|
|||||||
use alloc::{borrow::Cow, boxed::Box, format, string::String, vec::Vec};
|
use std::{borrow::Cow, collections::BTreeMap, ffi::CStr};
|
||||||
use core::{ffi::CStr, fmt, fmt::Debug};
|
|
||||||
|
|
||||||
use anyhow::{Result, bail};
|
use anyhow::{bail, Result};
|
||||||
use encoding_rs::SHIFT_JIS;
|
use byteorder::ByteOrder;
|
||||||
use object::Endian as _;
|
use object::{Architecture, File, Object, ObjectSymbol, Relocation, RelocationFlags, Symbol};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
diff::{
|
diff::DiffObjConfig,
|
||||||
DiffObjConfig,
|
obj::{ObjIns, ObjReloc, ObjSection},
|
||||||
display::{ContextItem, HoverItem, InstructionPart},
|
|
||||||
},
|
|
||||||
obj::{
|
|
||||||
InstructionArg, InstructionRef, Object, ParsedInstruction, Relocation, RelocationFlags,
|
|
||||||
ResolvedInstructionRef, ResolvedSymbol, Section, Symbol, SymbolFlagSet, SymbolKind,
|
|
||||||
},
|
|
||||||
util::ReallySigned,
|
util::ReallySigned,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "arm")]
|
#[cfg(feature = "arm")]
|
||||||
pub mod arm;
|
mod arm;
|
||||||
#[cfg(feature = "arm64")]
|
|
||||||
pub mod arm64;
|
|
||||||
#[cfg(feature = "mips")]
|
#[cfg(feature = "mips")]
|
||||||
pub mod mips;
|
pub mod mips;
|
||||||
#[cfg(feature = "ppc")]
|
#[cfg(feature = "ppc")]
|
||||||
pub mod ppc;
|
pub mod ppc;
|
||||||
#[cfg(feature = "superh")]
|
|
||||||
pub mod superh;
|
|
||||||
#[cfg(feature = "x86")]
|
#[cfg(feature = "x86")]
|
||||||
pub mod x86;
|
pub mod x86;
|
||||||
|
|
||||||
@ -36,136 +25,74 @@ pub enum DataType {
|
|||||||
Int16,
|
Int16,
|
||||||
Int32,
|
Int32,
|
||||||
Int64,
|
Int64,
|
||||||
|
Int128,
|
||||||
Float,
|
Float,
|
||||||
Double,
|
Double,
|
||||||
Bytes,
|
Bytes,
|
||||||
String,
|
String,
|
||||||
}
|
}
|
||||||
|
|
||||||
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"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DataType {
|
impl DataType {
|
||||||
pub fn display_labels(&self, endian: object::Endianness, bytes: &[u8]) -> Vec<String> {
|
pub fn display_bytes<Endian: ByteOrder>(&self, bytes: &[u8]) -> Option<String> {
|
||||||
let mut strs = Vec::new();
|
|
||||||
for (literal, label_override) in self.display_literals(endian, bytes) {
|
|
||||||
let label = label_override.unwrap_or_else(|| format!("{}", self));
|
|
||||||
strs.push(format!("{}: {}", label, literal))
|
|
||||||
}
|
|
||||||
strs
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
if self.required_len().is_some_and(|l| bytes.len() < l) {
|
||||||
log::warn!(
|
return None;
|
||||||
"Failed to display a symbol value for a symbol whose size is too small for instruction referencing it."
|
|
||||||
);
|
|
||||||
return strs;
|
|
||||||
}
|
|
||||||
let mut bytes = bytes;
|
|
||||||
if self.required_len().is_some_and(|l| bytes.len() > l) {
|
|
||||||
// If the symbol's size is larger a single instance of this data type, we take just the
|
|
||||||
// bytes necessary for one of them in order to display the first element of the array.
|
|
||||||
bytes = &bytes[0..self.required_len().unwrap()];
|
|
||||||
// TODO: Attempt to interpret large symbols as arrays of a smaller type and show all
|
|
||||||
// elements of the array instead. https://github.com/encounter/objdiff/issues/124
|
|
||||||
// However, note that the stride of an array can not always be determined just by the
|
|
||||||
// data type guessed by the single instruction accessing it. There can also be arrays of
|
|
||||||
// structs that contain multiple elements of different types, so if other elements after
|
|
||||||
// the first one were to be displayed in this manner, they may be inaccurate.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
DataType::Int8 => {
|
DataType::Int8 => {
|
||||||
let i = i8::from_ne_bytes(bytes.try_into().unwrap());
|
let i = i8::from_ne_bytes(bytes.try_into().unwrap());
|
||||||
strs.push((format!("{:#x}", i), None));
|
|
||||||
|
|
||||||
if i < 0 {
|
if i < 0 {
|
||||||
strs.push((format!("{:#x}", ReallySigned(i)), None));
|
format!("Int8: {:#x} ({:#x})", i, ReallySigned(i))
|
||||||
|
} else {
|
||||||
|
format!("Int8: {:#x}", i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType::Int16 => {
|
DataType::Int16 => {
|
||||||
let i = endian.read_i16_bytes(bytes.try_into().unwrap());
|
let i = Endian::read_i16(bytes);
|
||||||
strs.push((format!("{:#x}", i), None));
|
|
||||||
|
|
||||||
if i < 0 {
|
if i < 0 {
|
||||||
strs.push((format!("{:#x}", ReallySigned(i)), None));
|
format!("Int16: {:#x} ({:#x})", i, ReallySigned(i))
|
||||||
|
} else {
|
||||||
|
format!("Int16: {:#x}", i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType::Int32 => {
|
DataType::Int32 => {
|
||||||
let i = endian.read_i32_bytes(bytes.try_into().unwrap());
|
let i = Endian::read_i32(bytes);
|
||||||
strs.push((format!("{:#x}", i), None));
|
|
||||||
|
|
||||||
if i < 0 {
|
if i < 0 {
|
||||||
strs.push((format!("{:#x}", ReallySigned(i)), None));
|
format!("Int32: {:#x} ({:#x})", i, ReallySigned(i))
|
||||||
|
} else {
|
||||||
|
format!("Int32: {:#x}", i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType::Int64 => {
|
DataType::Int64 => {
|
||||||
let i = endian.read_i64_bytes(bytes.try_into().unwrap());
|
let i = Endian::read_i64(bytes);
|
||||||
strs.push((format!("{:#x}", i), None));
|
|
||||||
|
|
||||||
if i < 0 {
|
if i < 0 {
|
||||||
strs.push((format!("{:#x}", ReallySigned(i)), None));
|
format!("Int64: {:#x} ({:#x})", i, ReallySigned(i))
|
||||||
|
} else {
|
||||||
|
format!("Int64: {:#x}", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType::Int128 => {
|
||||||
|
let i = Endian::read_i128(bytes);
|
||||||
|
if i < 0 {
|
||||||
|
format!("Int128: {:#x} ({:#x})", i, ReallySigned(i))
|
||||||
|
} else {
|
||||||
|
format!("Int128: {:#x}", i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType::Float => {
|
DataType::Float => {
|
||||||
let bytes: [u8; 4] = bytes.try_into().unwrap();
|
format!("Float: {}", Endian::read_f32(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 => {
|
DataType::Double => {
|
||||||
let bytes: [u8; 8] = bytes.try_into().unwrap();
|
format!("Double: {}", Endian::read_f64(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 => {
|
DataType::Bytes => {
|
||||||
strs.push((format!("{:#?}", bytes), None));
|
format!("Bytes: {:#?}", bytes)
|
||||||
}
|
}
|
||||||
DataType::String => {
|
DataType::String => {
|
||||||
if let Ok(cstr) = CStr::from_bytes_until_nul(bytes) {
|
format!("String: {:?}", CStr::from_bytes_until_nul(bytes).ok()?)
|
||||||
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())));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.into()
|
||||||
strs
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn required_len(&self) -> Option<usize> {
|
fn required_len(&self) -> Option<usize> {
|
||||||
@ -174,6 +101,7 @@ impl DataType {
|
|||||||
DataType::Int16 => Some(2),
|
DataType::Int16 => Some(2),
|
||||||
DataType::Int32 => Some(4),
|
DataType::Int32 => Some(4),
|
||||||
DataType::Int64 => Some(8),
|
DataType::Int64 => Some(8),
|
||||||
|
DataType::Int128 => Some(16),
|
||||||
DataType::Float => Some(4),
|
DataType::Float => Some(4),
|
||||||
DataType::Double => Some(8),
|
DataType::Double => Some(8),
|
||||||
DataType::Bytes => None,
|
DataType::Bytes => None,
|
||||||
@ -182,268 +110,57 @@ impl DataType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl dyn Arch {
|
pub trait ObjArch: Send + Sync {
|
||||||
/// Generate a list of instructions references (offset, size, opcode) from the given code.
|
fn process_code(
|
||||||
///
|
|
||||||
/// See [`scan_instructions_internal`] for more details.
|
|
||||||
pub fn scan_instructions(
|
|
||||||
&self,
|
|
||||||
resolved: ResolvedSymbol,
|
|
||||||
diff_config: &DiffObjConfig,
|
|
||||||
) -> 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 {
|
|
||||||
if 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.
|
|
||||||
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) => {
|
|
||||||
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() && !relocation_emitted {
|
|
||||||
args.push(InstructionArg::Reloc);
|
|
||||||
}
|
|
||||||
Ok(ParsedInstruction {
|
|
||||||
ins_ref: resolved.ins_ref,
|
|
||||||
mnemonic: mnemonic.unwrap_or_default(),
|
|
||||||
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,
|
&self,
|
||||||
address: u64,
|
address: u64,
|
||||||
code: &[u8],
|
code: &[u8],
|
||||||
section_index: usize,
|
section_index: usize,
|
||||||
relocations: &[Relocation],
|
relocations: &[ObjReloc],
|
||||||
diff_config: &DiffObjConfig,
|
line_info: &BTreeMap<u64, u32>,
|
||||||
) -> Result<Vec<InstructionRef>>;
|
config: &DiffObjConfig,
|
||||||
|
) -> Result<ProcessCodeResult>;
|
||||||
/// Format an instruction for display.
|
|
||||||
///
|
|
||||||
/// Implementations should call the callback for each part of the instruction: usually the
|
|
||||||
/// mnemonic and arguments, plus any separators and visual formatting.
|
|
||||||
fn display_instruction(
|
|
||||||
&self,
|
|
||||||
resolved: ResolvedInstructionRef,
|
|
||||||
diff_config: &DiffObjConfig,
|
|
||||||
cb: &mut dyn FnMut(InstructionPart) -> Result<()>,
|
|
||||||
) -> Result<()>;
|
|
||||||
|
|
||||||
/// Generate a list of fake relocations from the given code that represent pooled data accesses.
|
|
||||||
fn generate_pooled_relocations(
|
|
||||||
&self,
|
|
||||||
_address: u64,
|
|
||||||
_code: &[u8],
|
|
||||||
_relocations: &[Relocation],
|
|
||||||
_symbols: &[Symbol],
|
|
||||||
) -> Vec<Relocation> {
|
|
||||||
Vec::new()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn implcit_addend(
|
fn implcit_addend(
|
||||||
&self,
|
&self,
|
||||||
file: &object::File<'_>,
|
file: &File<'_>,
|
||||||
section: &object::Section,
|
section: &ObjSection,
|
||||||
address: u64,
|
address: u64,
|
||||||
relocation: &object::Relocation,
|
reloc: &Relocation,
|
||||||
flags: RelocationFlags,
|
|
||||||
) -> Result<i64>;
|
) -> Result<i64>;
|
||||||
|
|
||||||
fn demangle(&self, _name: &str) -> Option<String> { None }
|
fn demangle(&self, _name: &str) -> Option<String> { None }
|
||||||
|
|
||||||
fn reloc_name(&self, _flags: RelocationFlags) -> Option<&'static str> { None }
|
fn display_reloc(&self, flags: RelocationFlags) -> Cow<'static, str>;
|
||||||
|
|
||||||
fn data_reloc_size(&self, flags: RelocationFlags) -> usize;
|
fn symbol_address(&self, symbol: &Symbol) -> u64 { symbol.address() }
|
||||||
|
|
||||||
fn symbol_address(&self, address: u64, _kind: SymbolKind) -> u64 { address }
|
fn guess_data_type(&self, _instruction: &ObjIns) -> Option<DataType> { None }
|
||||||
|
|
||||||
fn extra_symbol_flags(&self, _symbol: &object::Symbol) -> SymbolFlagSet {
|
fn display_data_type(&self, _ty: DataType, bytes: &[u8]) -> Option<String> {
|
||||||
SymbolFlagSet::default()
|
Some(format!("Bytes: {:#x?}", bytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn guess_data_type(
|
// Downcast methods
|
||||||
&self,
|
#[cfg(feature = "ppc")]
|
||||||
_resolved: ResolvedInstructionRef,
|
fn ppc(&self) -> Option<&ppc::ObjArchPpc> { None }
|
||||||
_bytes: &[u8],
|
|
||||||
) -> Option<DataType> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn symbol_hover(&self, _obj: &Object, _symbol_index: usize) -> Vec<HoverItem> { Vec::new() }
|
|
||||||
|
|
||||||
fn symbol_context(&self, _obj: &Object, _symbol_index: usize) -> Vec<ContextItem> { Vec::new() }
|
|
||||||
|
|
||||||
fn instruction_hover(
|
|
||||||
&self,
|
|
||||||
_obj: &Object,
|
|
||||||
_resolved: ResolvedInstructionRef,
|
|
||||||
) -> Vec<HoverItem> {
|
|
||||||
Vec::new()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn instruction_context(
|
|
||||||
&self,
|
|
||||||
_obj: &Object,
|
|
||||||
_resolved: ResolvedInstructionRef,
|
|
||||||
) -> Vec<ContextItem> {
|
|
||||||
Vec::new()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_arch(object: &object::File) -> Result<Box<dyn Arch>> {
|
pub struct ProcessCodeResult {
|
||||||
use object::Object as _;
|
pub ops: Vec<u16>,
|
||||||
|
pub insts: Vec<ObjIns>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_arch(object: &File) -> Result<Box<dyn ObjArch>> {
|
||||||
Ok(match object.architecture() {
|
Ok(match object.architecture() {
|
||||||
#[cfg(feature = "ppc")]
|
#[cfg(feature = "ppc")]
|
||||||
object::Architecture::PowerPc => Box::new(ppc::ArchPpc::new(object)?),
|
Architecture::PowerPc => Box::new(ppc::ObjArchPpc::new(object)?),
|
||||||
#[cfg(feature = "mips")]
|
#[cfg(feature = "mips")]
|
||||||
object::Architecture::Mips => Box::new(mips::ArchMips::new(object)?),
|
Architecture::Mips => Box::new(mips::ObjArchMips::new(object)?),
|
||||||
#[cfg(feature = "x86")]
|
#[cfg(feature = "x86")]
|
||||||
object::Architecture::I386 | object::Architecture::X86_64 => {
|
Architecture::I386 | Architecture::X86_64 => Box::new(x86::ObjArchX86::new(object)?),
|
||||||
Box::new(x86::ArchX86::new(object)?)
|
|
||||||
}
|
|
||||||
#[cfg(feature = "arm")]
|
#[cfg(feature = "arm")]
|
||||||
object::Architecture::Arm => Box::new(arm::ArchArm::new(object)?),
|
Architecture::Arm => Box::new(arm::ObjArchArm::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:?}"),
|
arch => bail!("Unsupported architecture: {arch:?}"),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
pub struct ArchDummy {}
|
|
||||||
|
|
||||||
impl ArchDummy {
|
|
||||||
pub fn new() -> Box<Self> { Box::new(Self {}) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Arch for ArchDummy {
|
|
||||||
fn scan_instructions_internal(
|
|
||||||
&self,
|
|
||||||
_address: u64,
|
|
||||||
_code: &[u8],
|
|
||||||
_section_index: usize,
|
|
||||||
_relocations: &[Relocation],
|
|
||||||
_diff_config: &DiffObjConfig,
|
|
||||||
) -> Result<Vec<InstructionRef>> {
|
|
||||||
Ok(Vec::new())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn display_instruction(
|
|
||||||
&self,
|
|
||||||
_resolved: ResolvedInstructionRef,
|
|
||||||
_diff_config: &DiffObjConfig,
|
|
||||||
_cb: &mut dyn FnMut(InstructionPart) -> Result<()>,
|
|
||||||
) -> Result<()> {
|
|
||||||
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 }
|
|
||||||
}
|
|
||||||
|
@ -1,370 +1,254 @@
|
|||||||
use alloc::{
|
use std::{borrow::Cow, collections::BTreeMap};
|
||||||
collections::{BTreeMap, BTreeSet},
|
|
||||||
string::{String, ToString},
|
|
||||||
vec,
|
|
||||||
vec::Vec,
|
|
||||||
};
|
|
||||||
|
|
||||||
use anyhow::{Result, bail, ensure};
|
use anyhow::{bail, ensure, Result};
|
||||||
use cwextab::{ExceptionTableData, decode_extab};
|
use byteorder::BigEndian;
|
||||||
use flagset::Flags;
|
use cwextab::{decode_extab, ExceptionTableData};
|
||||||
use object::{Object as _, ObjectSection as _, ObjectSymbol as _, elf};
|
use object::{
|
||||||
|
elf, File, Object, ObjectSection, ObjectSymbol, Relocation, RelocationFlags, RelocationTarget,
|
||||||
|
Symbol, SymbolKind,
|
||||||
|
};
|
||||||
|
use ppc750cl::{Argument, InsIter, Opcode, GPR};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
arch::{Arch, DataType},
|
arch::{DataType, ObjArch, ProcessCodeResult},
|
||||||
diff::{
|
diff::DiffObjConfig,
|
||||||
DiffObjConfig,
|
obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection, ObjSymbol},
|
||||||
data::resolve_relocation,
|
|
||||||
display::{ContextItem, HoverItem, HoverItemColor, InstructionPart, SymbolNavigationKind},
|
|
||||||
},
|
|
||||||
obj::{
|
|
||||||
InstructionRef, Object, Relocation, RelocationFlags, ResolvedInstructionRef,
|
|
||||||
ResolvedRelocation, Symbol, SymbolFlag, SymbolFlagSet,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Relative relocation, can be Simm, Offset or BranchDest
|
// Relative relocation, can be Simm, Offset or BranchDest
|
||||||
fn is_relative_arg(arg: &ppc750cl::Argument) -> bool {
|
fn is_relative_arg(arg: &Argument) -> bool {
|
||||||
matches!(
|
matches!(arg, Argument::Simm(_) | Argument::Offset(_) | Argument::BranchDest(_))
|
||||||
arg,
|
|
||||||
ppc750cl::Argument::Simm(_)
|
|
||||||
| ppc750cl::Argument::Offset(_)
|
|
||||||
| ppc750cl::Argument::BranchDest(_)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Relative or absolute relocation, can be Uimm, Simm or Offset
|
// Relative or absolute relocation, can be Uimm, Simm or Offset
|
||||||
fn is_rel_abs_arg(arg: &ppc750cl::Argument) -> bool {
|
fn is_rel_abs_arg(arg: &Argument) -> bool {
|
||||||
matches!(
|
matches!(arg, Argument::Uimm(_) | Argument::Simm(_) | Argument::Offset(_))
|
||||||
arg,
|
|
||||||
ppc750cl::Argument::Uimm(_) | ppc750cl::Argument::Simm(_) | ppc750cl::Argument::Offset(_)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_offset_arg(arg: &ppc750cl::Argument) -> bool { matches!(arg, ppc750cl::Argument::Offset(_)) }
|
fn is_offset_arg(arg: &Argument) -> bool { matches!(arg, Argument::Offset(_)) }
|
||||||
|
|
||||||
#[derive(Debug)]
|
pub struct ObjArchPpc {
|
||||||
pub struct ArchPpc {
|
|
||||||
/// Exception info
|
/// Exception info
|
||||||
pub extab: Option<BTreeMap<usize, ExceptionInfo>>,
|
pub extab: Option<BTreeMap<usize, ExceptionInfo>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ArchPpc {
|
impl ObjArchPpc {
|
||||||
pub fn new(file: &object::File) -> Result<Self> {
|
pub fn new(file: &File) -> Result<Self> { Ok(Self { extab: decode_exception_info(file)? }) }
|
||||||
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 {
|
impl ObjArch for ObjArchPpc {
|
||||||
fn scan_instructions_internal(
|
fn process_code(
|
||||||
&self,
|
&self,
|
||||||
address: u64,
|
address: u64,
|
||||||
code: &[u8],
|
code: &[u8],
|
||||||
_section_index: usize,
|
_section_index: usize,
|
||||||
_relocations: &[Relocation],
|
relocations: &[ObjReloc],
|
||||||
_diff_config: &DiffObjConfig,
|
line_info: &BTreeMap<u64, u32>,
|
||||||
) -> Result<Vec<InstructionRef>> {
|
config: &DiffObjConfig,
|
||||||
ensure!(code.len() & 3 == 0, "Code length must be a multiple of 4");
|
) -> Result<ProcessCodeResult> {
|
||||||
let ins_count = code.len() / 4;
|
let ins_count = code.len() / 4;
|
||||||
let mut insts = Vec::<InstructionRef>::with_capacity(ins_count);
|
let mut ops = Vec::<u16>::with_capacity(ins_count);
|
||||||
for (cur_addr, ins) in ppc750cl::InsIter::new(code, address as u32) {
|
let mut insts = Vec::<ObjIns>::with_capacity(ins_count);
|
||||||
insts.push(InstructionRef {
|
for (cur_addr, mut ins) in InsIter::new(code, address as u32) {
|
||||||
|
let reloc = relocations.iter().find(|r| (r.address as u32 & !3) == cur_addr);
|
||||||
|
if let Some(reloc) = reloc {
|
||||||
|
// Zero out relocations
|
||||||
|
ins.code = match reloc.flags {
|
||||||
|
RelocationFlags::Elf { r_type: elf::R_PPC_EMB_SDA21 } => ins.code & !0x1FFFFF,
|
||||||
|
RelocationFlags::Elf { r_type: elf::R_PPC_REL24 } => ins.code & !0x3FFFFFC,
|
||||||
|
RelocationFlags::Elf { r_type: elf::R_PPC_REL14 } => ins.code & !0xFFFC,
|
||||||
|
RelocationFlags::Elf {
|
||||||
|
r_type: elf::R_PPC_ADDR16_HI | elf::R_PPC_ADDR16_HA | elf::R_PPC_ADDR16_LO,
|
||||||
|
} => ins.code & !0xFFFF,
|
||||||
|
_ => ins.code,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let orig = ins.basic().to_string();
|
||||||
|
let simplified = ins.simplified();
|
||||||
|
let formatted = simplified.to_string();
|
||||||
|
|
||||||
|
let mut reloc_arg = None;
|
||||||
|
if let Some(reloc) = reloc {
|
||||||
|
match reloc.flags {
|
||||||
|
RelocationFlags::Elf { r_type: elf::R_PPC_EMB_SDA21 } => {
|
||||||
|
reloc_arg = Some(1);
|
||||||
|
}
|
||||||
|
RelocationFlags::Elf { r_type: elf::R_PPC_REL24 | elf::R_PPC_REL14 } => {
|
||||||
|
reloc_arg = simplified.args.iter().rposition(is_relative_arg);
|
||||||
|
}
|
||||||
|
RelocationFlags::Elf {
|
||||||
|
r_type: elf::R_PPC_ADDR16_HI | elf::R_PPC_ADDR16_HA | elf::R_PPC_ADDR16_LO,
|
||||||
|
} => {
|
||||||
|
reloc_arg = simplified.args.iter().rposition(is_rel_abs_arg);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut args = vec![];
|
||||||
|
let mut branch_dest = None;
|
||||||
|
let mut writing_offset = false;
|
||||||
|
for (idx, arg) in simplified.args_iter().enumerate() {
|
||||||
|
if idx > 0 && !writing_offset {
|
||||||
|
args.push(ObjInsArg::PlainText(config.separator().into()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if reloc_arg == Some(idx) {
|
||||||
|
let reloc = reloc.unwrap();
|
||||||
|
push_reloc(&mut args, reloc)?;
|
||||||
|
// For @sda21, we can omit the register argument
|
||||||
|
if matches!(reloc.flags, RelocationFlags::Elf { r_type: elf::R_PPC_EMB_SDA21 })
|
||||||
|
// Sanity check: the next argument should be r0
|
||||||
|
&& matches!(simplified.args.get(idx + 1), Some(Argument::GPR(GPR(0))))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match arg {
|
||||||
|
Argument::Simm(simm) => {
|
||||||
|
args.push(ObjInsArg::Arg(ObjInsArgValue::Signed(simm.0 as i64)));
|
||||||
|
}
|
||||||
|
Argument::Uimm(uimm) => {
|
||||||
|
args.push(ObjInsArg::Arg(ObjInsArgValue::Unsigned(uimm.0 as u64)));
|
||||||
|
}
|
||||||
|
Argument::Offset(offset) => {
|
||||||
|
args.push(ObjInsArg::Arg(ObjInsArgValue::Signed(offset.0 as i64)));
|
||||||
|
}
|
||||||
|
Argument::BranchDest(dest) => {
|
||||||
|
let dest = cur_addr.wrapping_add_signed(dest.0) as u64;
|
||||||
|
args.push(ObjInsArg::BranchDest(dest));
|
||||||
|
branch_dest = Some(dest);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(
|
||||||
|
arg.to_string().into(),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if writing_offset {
|
||||||
|
args.push(ObjInsArg::PlainText(")".into()));
|
||||||
|
writing_offset = false;
|
||||||
|
}
|
||||||
|
if is_offset_arg(arg) {
|
||||||
|
args.push(ObjInsArg::PlainText("(".into()));
|
||||||
|
writing_offset = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ops.push(ins.op as u16);
|
||||||
|
let line = line_info.range(..=cur_addr as u64).last().map(|(_, &b)| b);
|
||||||
|
insts.push(ObjIns {
|
||||||
address: cur_addr as u64,
|
address: cur_addr as u64,
|
||||||
size: 4,
|
size: 4,
|
||||||
opcode: u8::from(ins.op) as u16,
|
mnemonic: simplified.mnemonic.to_string(),
|
||||||
branch_dest: ins.branch_dest(cur_addr).map(u64::from),
|
args,
|
||||||
|
reloc: reloc.cloned(),
|
||||||
|
op: ins.op as u16,
|
||||||
|
branch_dest,
|
||||||
|
line,
|
||||||
|
formatted,
|
||||||
|
orig: Some(orig),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Ok(insts)
|
Ok(ProcessCodeResult { ops, 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 generate_pooled_relocations(
|
|
||||||
&self,
|
|
||||||
address: u64,
|
|
||||||
code: &[u8],
|
|
||||||
relocations: &[Relocation],
|
|
||||||
symbols: &[Symbol],
|
|
||||||
) -> Vec<Relocation> {
|
|
||||||
generate_fake_pool_relocations_for_function(address, code, relocations, symbols)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn implcit_addend(
|
fn implcit_addend(
|
||||||
&self,
|
&self,
|
||||||
_file: &object::File<'_>,
|
_file: &File<'_>,
|
||||||
_section: &object::Section,
|
_section: &ObjSection,
|
||||||
address: u64,
|
address: u64,
|
||||||
_relocation: &object::Relocation,
|
reloc: &Relocation,
|
||||||
flags: RelocationFlags,
|
|
||||||
) -> Result<i64> {
|
) -> Result<i64> {
|
||||||
bail!("Unsupported PPC implicit relocation {:#x}:{:?}", address, flags)
|
bail!("Unsupported PPC implicit relocation {:#x}:{:?}", address, reloc.flags())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn demangle(&self, name: &str) -> Option<String> {
|
fn demangle(&self, name: &str) -> Option<String> {
|
||||||
cwdemangle::demangle(name, &cwdemangle::DemangleOptions::default())
|
cwdemangle::demangle(name, &cwdemangle::DemangleOptions::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reloc_name(&self, flags: RelocationFlags) -> Option<&'static str> {
|
fn display_reloc(&self, flags: RelocationFlags) -> Cow<'static, str> {
|
||||||
match flags {
|
match flags {
|
||||||
RelocationFlags::Elf(r_type) => match r_type {
|
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 => Cow::Borrowed("R_PPC_ADDR16_LO"),
|
||||||
elf::R_PPC_ADDR16_LO => Some("R_PPC_ADDR16_LO"),
|
elf::R_PPC_ADDR16_HI => Cow::Borrowed("R_PPC_ADDR16_HI"),
|
||||||
elf::R_PPC_ADDR16_HI => Some("R_PPC_ADDR16_HI"),
|
elf::R_PPC_ADDR16_HA => Cow::Borrowed("R_PPC_ADDR16_HA"),
|
||||||
elf::R_PPC_ADDR16_HA => Some("R_PPC_ADDR16_HA"),
|
elf::R_PPC_EMB_SDA21 => Cow::Borrowed("R_PPC_EMB_SDA21"),
|
||||||
elf::R_PPC_EMB_SDA21 => Some("R_PPC_EMB_SDA21"),
|
elf::R_PPC_ADDR32 => Cow::Borrowed("R_PPC_ADDR32"),
|
||||||
elf::R_PPC_ADDR32 => Some("R_PPC_ADDR32"),
|
elf::R_PPC_UADDR32 => Cow::Borrowed("R_PPC_UADDR32"),
|
||||||
elf::R_PPC_UADDR32 => Some("R_PPC_UADDR32"),
|
elf::R_PPC_REL24 => Cow::Borrowed("R_PPC_REL24"),
|
||||||
elf::R_PPC_REL24 => Some("R_PPC_REL24"),
|
elf::R_PPC_REL14 => Cow::Borrowed("R_PPC_REL14"),
|
||||||
elf::R_PPC_REL14 => Some("R_PPC_REL14"),
|
_ => Cow::Owned(format!("<{flags:?}>")),
|
||||||
_ => None,
|
|
||||||
},
|
},
|
||||||
|
_ => Cow::Owned(format!("<{flags:?}>")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn guess_data_type(&self, instruction: &ObjIns) -> Option<super::DataType> {
|
||||||
|
// Always shows the first string of the table. Not ideal, but it's really hard to find
|
||||||
|
// the actual string being referenced.
|
||||||
|
if instruction.reloc.as_ref().is_some_and(|r| r.target.name.starts_with("@stringBase")) {
|
||||||
|
return Some(DataType::String);
|
||||||
|
}
|
||||||
|
|
||||||
|
match Opcode::from(instruction.op as u8) {
|
||||||
|
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,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn data_reloc_size(&self, flags: RelocationFlags) -> usize {
|
fn display_data_type(&self, ty: DataType, bytes: &[u8]) -> Option<String> {
|
||||||
match flags {
|
ty.display_bytes::<BigEndian>(bytes)
|
||||||
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 {
|
fn ppc(&self) -> Option<&ObjArchPpc> { Some(self) }
|
||||||
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, bytes: &[u8]) -> Option<DataType> {
|
impl ObjArchPpc {
|
||||||
if resolved.relocation.is_some_and(|r| r.symbol.name.starts_with("@stringBase")) {
|
pub fn extab_for_symbol(&self, symbol: &ObjSymbol) -> Option<&ExceptionInfo> {
|
||||||
// Pooled string.
|
symbol.original_index.and_then(|i| self.extab.as_ref()?.get(&i))
|
||||||
return Some(DataType::String);
|
|
||||||
}
|
|
||||||
let opcode = ppc750cl::Opcode::from(resolved.ins_ref.opcode as u8);
|
|
||||||
if let Some(ty) = guess_data_type_from_load_store_inst_op(opcode) {
|
|
||||||
// Numeric type.
|
|
||||||
return Some(ty);
|
|
||||||
}
|
|
||||||
if bytes.len() >= 2 && bytes.iter().position(|&c| c == b'\0') == Some(bytes.len() - 1) {
|
|
||||||
// It may be an unpooled string if the symbol contains exactly one null byte at the end of the symbol.
|
|
||||||
return Some(DataType::String);
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
fn push_reloc(args: &mut Vec<ObjInsArg>, reloc: &ObjReloc) -> Result<()> {
|
||||||
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 {
|
match reloc.flags {
|
||||||
RelocationFlags::Elf(elf::R_PPC_EMB_SDA21) => code & !0x1FFFFF,
|
RelocationFlags::Elf { r_type } => match r_type {
|
||||||
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 => {
|
elf::R_PPC_ADDR16_LO => {
|
||||||
cb(InstructionPart::reloc())?;
|
args.push(ObjInsArg::Reloc);
|
||||||
cb(InstructionPart::basic("@l"))?;
|
args.push(ObjInsArg::PlainText("@l".into()));
|
||||||
}
|
}
|
||||||
elf::R_PPC_ADDR16_HI => {
|
elf::R_PPC_ADDR16_HI => {
|
||||||
cb(InstructionPart::reloc())?;
|
args.push(ObjInsArg::Reloc);
|
||||||
cb(InstructionPart::basic("@h"))?;
|
args.push(ObjInsArg::PlainText("@h".into()));
|
||||||
}
|
}
|
||||||
elf::R_PPC_ADDR16_HA => {
|
elf::R_PPC_ADDR16_HA => {
|
||||||
cb(InstructionPart::reloc())?;
|
args.push(ObjInsArg::Reloc);
|
||||||
cb(InstructionPart::basic("@ha"))?;
|
args.push(ObjInsArg::PlainText("@ha".into()));
|
||||||
}
|
}
|
||||||
elf::R_PPC_EMB_SDA21 => {
|
elf::R_PPC_EMB_SDA21 => {
|
||||||
cb(InstructionPart::reloc())?;
|
args.push(ObjInsArg::Reloc);
|
||||||
cb(InstructionPart::basic("@sda21"))?;
|
args.push(ObjInsArg::PlainText("@sda21".into()));
|
||||||
}
|
}
|
||||||
elf::R_PPC_ADDR32 | elf::R_PPC_UADDR32 | elf::R_PPC_REL24 | elf::R_PPC_REL14 => {
|
elf::R_PPC_ADDR32 | elf::R_PPC_UADDR32 | elf::R_PPC_REL24 | elf::R_PPC_REL14 => {
|
||||||
cb(InstructionPart::reloc())?;
|
args.push(ObjInsArg::Reloc);
|
||||||
}
|
}
|
||||||
elf::R_PPC_NONE => {
|
_ => bail!("Unsupported ELF PPC relocation type {r_type}"),
|
||||||
// Fake pool relocation.
|
|
||||||
cb(InstructionPart::basic("<"))?;
|
|
||||||
cb(InstructionPart::reloc())?;
|
|
||||||
cb(InstructionPart::basic(">"))?;
|
|
||||||
}
|
|
||||||
_ => cb(InstructionPart::reloc())?,
|
|
||||||
},
|
},
|
||||||
_ => cb(InstructionPart::reloc())?,
|
flags => bail!("Unsupported PPC relocation kind: {flags:?}"),
|
||||||
};
|
};
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -384,9 +268,7 @@ pub struct ExceptionInfo {
|
|||||||
pub dtors: Vec<ExtabSymbolRef>,
|
pub dtors: Vec<ExtabSymbolRef>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode_exception_info(
|
fn decode_exception_info(file: &File<'_>) -> Result<Option<BTreeMap<usize, ExceptionInfo>>> {
|
||||||
file: &object::File<'_>,
|
|
||||||
) -> Result<Option<BTreeMap<usize, ExceptionInfo>>> {
|
|
||||||
let Some(extab_section) = file.section_by_name("extab") else {
|
let Some(extab_section) = file.section_by_name("extab") else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
@ -395,14 +277,13 @@ fn decode_exception_info(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut result = BTreeMap::new();
|
let mut result = BTreeMap::new();
|
||||||
let extab_relocations =
|
let extab_relocations = extab_section.relocations().collect::<BTreeMap<u64, Relocation>>();
|
||||||
extab_section.relocations().collect::<BTreeMap<u64, object::Relocation>>();
|
|
||||||
let extabindex_relocations =
|
let extabindex_relocations =
|
||||||
extabindex_section.relocations().collect::<BTreeMap<u64, object::Relocation>>();
|
extabindex_section.relocations().collect::<BTreeMap<u64, Relocation>>();
|
||||||
|
|
||||||
for extabindex in file.symbols().filter(|symbol| {
|
for extabindex in file.symbols().filter(|symbol| {
|
||||||
symbol.section_index() == Some(extabindex_section.index())
|
symbol.section_index() == Some(extabindex_section.index())
|
||||||
&& symbol.kind() == object::SymbolKind::Data
|
&& symbol.kind() == SymbolKind::Data
|
||||||
}) {
|
}) {
|
||||||
if extabindex.size() != 12 {
|
if extabindex.size() != 12 {
|
||||||
log::warn!("Invalid extabindex entry size {}", extabindex.size());
|
log::warn!("Invalid extabindex entry size {}", extabindex.size());
|
||||||
@ -456,14 +337,14 @@ fn decode_exception_info(
|
|||||||
log::warn!(
|
log::warn!(
|
||||||
"Exception table decoding failed for function {}, reason: {}",
|
"Exception table decoding failed for function {}, reason: {}",
|
||||||
extab_func_name,
|
extab_func_name,
|
||||||
e
|
e.to_string()
|
||||||
);
|
);
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//Add the new entry to the list
|
//Add the new entry to the list
|
||||||
result.insert(extab_func.index().0 - 1, ExceptionInfo {
|
result.insert(extab_func.index().0, ExceptionInfo {
|
||||||
eti_symbol: make_symbol_ref(&extabindex)?,
|
eti_symbol: make_symbol_ref(&extabindex)?,
|
||||||
etb_symbol: make_symbol_ref(&extab)?,
|
etb_symbol: make_symbol_ref(&extab)?,
|
||||||
data,
|
data,
|
||||||
@ -475,16 +356,16 @@ fn decode_exception_info(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn relocation_symbol<'data, 'file>(
|
fn relocation_symbol<'data, 'file>(
|
||||||
file: &'file object::File<'data>,
|
file: &'file File<'data>,
|
||||||
relocation: &object::Relocation,
|
relocation: &Relocation,
|
||||||
) -> Result<Option<object::Symbol<'data, 'file>>> {
|
) -> Result<Option<Symbol<'data, 'file>>> {
|
||||||
let addend = relocation.addend();
|
let addend = relocation.addend();
|
||||||
match relocation.target() {
|
match relocation.target() {
|
||||||
object::RelocationTarget::Symbol(idx) => {
|
RelocationTarget::Symbol(idx) => {
|
||||||
ensure!(addend == 0, "Symbol relocations must have zero addend");
|
ensure!(addend == 0, "Symbol relocations must have zero addend");
|
||||||
Ok(Some(file.symbol_by_index(idx)?))
|
Ok(Some(file.symbol_by_index(idx)?))
|
||||||
}
|
}
|
||||||
object::RelocationTarget::Section(idx) => {
|
RelocationTarget::Section(idx) => {
|
||||||
ensure!(addend >= 0, "Section relocations must have non-negative addend");
|
ensure!(addend >= 0, "Section relocations must have non-negative addend");
|
||||||
let addend = addend as u64;
|
let addend = addend as u64;
|
||||||
Ok(file
|
Ok(file
|
||||||
@ -495,361 +376,8 @@ fn relocation_symbol<'data, 'file>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_symbol_ref(symbol: &object::Symbol) -> Result<ExtabSymbolRef> {
|
fn make_symbol_ref(symbol: &Symbol) -> Result<ExtabSymbolRef> {
|
||||||
let name = symbol.name()?.to_string();
|
let name = symbol.name()?.to_string();
|
||||||
let demangled_name = cwdemangle::demangle(&name, &cwdemangle::DemangleOptions::default());
|
let demangled_name = cwdemangle::demangle(&name, &cwdemangle::DemangleOptions::default());
|
||||||
Ok(ExtabSymbolRef { original_index: symbol.index().0 - 1, name, demangled_name })
|
Ok(ExtabSymbolRef { original_index: symbol.index().0, name, demangled_name })
|
||||||
}
|
|
||||||
|
|
||||||
fn guess_data_type_from_load_store_inst_op(inst_op: ppc750cl::Opcode) -> Option<DataType> {
|
|
||||||
use ppc750cl::Opcode;
|
|
||||||
match inst_op {
|
|
||||||
Opcode::Lbz | Opcode::Lbzu | Opcode::Lbzux | Opcode::Lbzx => Some(DataType::Int8),
|
|
||||||
Opcode::Lhz | Opcode::Lhzu | Opcode::Lhzux | Opcode::Lhzx => Some(DataType::Int16),
|
|
||||||
Opcode::Lha | Opcode::Lhau | Opcode::Lhaux | Opcode::Lhax => Some(DataType::Int16),
|
|
||||||
Opcode::Lwz | Opcode::Lwzu | Opcode::Lwzux | Opcode::Lwzx => Some(DataType::Int32),
|
|
||||||
Opcode::Lfs | Opcode::Lfsu | Opcode::Lfsux | Opcode::Lfsx => Some(DataType::Float),
|
|
||||||
Opcode::Lfd | Opcode::Lfdu | Opcode::Lfdux | Opcode::Lfdx => Some(DataType::Double),
|
|
||||||
|
|
||||||
Opcode::Stb | Opcode::Stbu | Opcode::Stbux | Opcode::Stbx => Some(DataType::Int8),
|
|
||||||
Opcode::Sth | Opcode::Sthu | Opcode::Sthux | Opcode::Sthx => Some(DataType::Int16),
|
|
||||||
Opcode::Stw | Opcode::Stwu | Opcode::Stwux | Opcode::Stwx => Some(DataType::Int32),
|
|
||||||
Opcode::Stfs | Opcode::Stfsu | Opcode::Stfsux | Opcode::Stfsx => Some(DataType::Float),
|
|
||||||
Opcode::Stfd | Opcode::Stfdu | Opcode::Stfdux | Opcode::Stfdx => Some(DataType::Double),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct PoolReference {
|
|
||||||
addr_src_gpr: ppc750cl::GPR,
|
|
||||||
addr_offset: i16,
|
|
||||||
addr_dst_gpr: Option<ppc750cl::GPR>,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Given an instruction, check if it could be accessing pooled data at the address in a register.
|
|
||||||
// If so, return information pertaining to where the instruction is getting that address from and
|
|
||||||
// what it's doing with the address (e.g. copying it into another register, adding an offset, etc).
|
|
||||||
fn get_pool_reference_for_inst(
|
|
||||||
opcode: ppc750cl::Opcode,
|
|
||||||
simplified: &ppc750cl::ParsedIns,
|
|
||||||
) -> Option<PoolReference> {
|
|
||||||
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(PoolReference { addr_src_gpr, addr_offset: offset.0, addr_dst_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(PoolReference { addr_src_gpr, addr_offset: 0, addr_dst_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(PoolReference {
|
|
||||||
addr_src_gpr,
|
|
||||||
addr_offset: simm.0,
|
|
||||||
addr_dst_gpr: Some(addr_dst_gpr),
|
|
||||||
}),
|
|
||||||
(
|
|
||||||
// `mr` or `mr.`
|
|
||||||
Opcode::Or,
|
|
||||||
Argument::GPR(addr_dst_gpr),
|
|
||||||
Argument::GPR(addr_src_gpr),
|
|
||||||
Argument::None,
|
|
||||||
) => Some(PoolReference {
|
|
||||||
addr_src_gpr,
|
|
||||||
addr_offset: 0,
|
|
||||||
addr_dst_gpr: Some(addr_dst_gpr),
|
|
||||||
}),
|
|
||||||
(
|
|
||||||
Opcode::Add,
|
|
||||||
Argument::GPR(addr_dst_gpr),
|
|
||||||
Argument::GPR(addr_src_gpr),
|
|
||||||
Argument::GPR(_offset_gpr),
|
|
||||||
) => Some(PoolReference {
|
|
||||||
addr_src_gpr,
|
|
||||||
addr_offset: 0,
|
|
||||||
addr_dst_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.
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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,
|
|
||||||
symbols: &[Symbol],
|
|
||||||
) -> Option<Relocation> {
|
|
||||||
let pool_reloc = resolve_relocation(symbols, pool_reloc);
|
|
||||||
let offset_from_pool = pool_reloc.relocation.addend + offset as i64;
|
|
||||||
let target_address = pool_reloc.symbol.address.checked_add_signed(offset_from_pool)?;
|
|
||||||
let target_symbol;
|
|
||||||
let addend;
|
|
||||||
if let Some(section_index) = pool_reloc.symbol.section {
|
|
||||||
// Find the exact data symbol within the pool being accessed here based on the address.
|
|
||||||
target_symbol = symbols.iter().position(|s| {
|
|
||||||
s.section == Some(section_index)
|
|
||||||
&& s.size > 0
|
|
||||||
&& (s.address..s.address + s.size).contains(&target_address)
|
|
||||||
})?;
|
|
||||||
addend = target_address.checked_sub(symbols[target_symbol].address)? as i64;
|
|
||||||
} else {
|
|
||||||
// If the target symbol is in a different object (extern), we simply copy the pool
|
|
||||||
// relocation's target. This is because it's not possible to locate the actual symbol if
|
|
||||||
// it's extern. 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.
|
|
||||||
target_symbol = pool_reloc.relocation.target_symbol;
|
|
||||||
addend = offset_from_pool;
|
|
||||||
}
|
|
||||||
Some(Relocation {
|
|
||||||
flags: RelocationFlags::Elf(elf::R_PPC_NONE),
|
|
||||||
address: cur_addr as u64,
|
|
||||||
target_symbol,
|
|
||||||
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 returns a Vec of "fake pool relocations" that simulate what a relocation for that instruction
|
|
||||||
// would look like if data hadn't been pooled.
|
|
||||||
// This method tries to follow the function's proper control flow. It keeps track of a queue of
|
|
||||||
// states it hasn't traversed yet, where each state holds an instruction address and a HashMap of
|
|
||||||
// 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 does not currently read switch statement jump tables.
|
|
||||||
// Instead, we guess that any parts of a function we missed were switch cases, and traverse them as
|
|
||||||
// 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.
|
|
||||||
// It should be possible to implement jump tables properly by reading them out of .data. But this
|
|
||||||
// will require keeping track of what value is loaded into each register so we can retrieve the jump
|
|
||||||
// table symbol when we encounter a `bctr`.
|
|
||||||
fn generate_fake_pool_relocations_for_function(
|
|
||||||
func_address: u64,
|
|
||||||
code: &[u8],
|
|
||||||
relocations: &[Relocation],
|
|
||||||
symbols: &[Symbol],
|
|
||||||
) -> Vec<Relocation> {
|
|
||||||
use ppc750cl::{Argument, InsIter, Opcode};
|
|
||||||
let mut visited_ins_addrs = BTreeSet::new();
|
|
||||||
let mut pool_reloc_for_addr = BTreeMap::new();
|
|
||||||
let mut ins_iters_with_gpr_state =
|
|
||||||
vec![(InsIter::new(code, func_address as u32), BTreeMap::new())];
|
|
||||||
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(pool_ref) = get_pool_reference_for_inst(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(&pool_ref.addr_src_gpr.0) {
|
|
||||||
if let Some(fake_pool_reloc) =
|
|
||||||
make_fake_pool_reloc(pool_ref.addr_offset, cur_addr, pool_reloc, symbols)
|
|
||||||
{
|
|
||||||
pool_reloc_for_addr.insert(cur_addr, fake_pool_reloc);
|
|
||||||
}
|
|
||||||
if let Some(addr_dst_gpr) = pool_ref.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 += pool_ref.addr_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.values().cloned().collect()
|
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,821 +0,0 @@
|
|||||||
use alloc::{collections::BTreeMap, format, string::String, vec, vec::Vec};
|
|
||||||
|
|
||||||
use anyhow::{Result, bail};
|
|
||||||
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!("{:04X}", data)));
|
|
||||||
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!("{:08X}", data)));
|
|
||||||
parts.push(InstructionPart::basic(" */"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for part in parts {
|
|
||||||
cb(part)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn implcit_addend(
|
|
||||||
&self,
|
|
||||||
_file: &object::File<'_>,
|
|
||||||
_section: &object::Section,
|
|
||||||
address: u64,
|
|
||||||
_relocation: &object::Relocation,
|
|
||||||
flags: RelocationFlags,
|
|
||||||
) -> Result<i64> {
|
|
||||||
bail!("Unsupported SuperH implicit relocation {:#x}:{:?}", address, flags)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn demangle(&self, name: &str) -> Option<String> {
|
|
||||||
cpp_demangle::Symbol::new(name)
|
|
||||||
.ok()
|
|
||||||
.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) => write!(f, "{}", s),
|
|
||||||
InstructionPart::Opcode(s, _o) => write!(f, "{} ", s),
|
|
||||||
InstructionPart::Arg(arg) => write!(f, "{}", arg),
|
|
||||||
InstructionPart::Separator => write!(f, ", "),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for InstructionArg<'_> {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
InstructionArg::Value(v) => write!(f, "{}", v),
|
|
||||||
InstructionArg::BranchDest(v) => write!(f, "{}", v),
|
|
||||||
InstructionArg::Reloc => write!(f, "reloc"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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(|part| format!("{}", part)).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(|part| format!("{}", part)).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(|part| format!("{}", part)).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(|part| format!("{}", part)).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(|part| format!("{}", part)).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(|part| format!("{}", part)).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(|part| format!("{}", part)).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(|part| format!("{}", part)).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(|part| format!("{}", part)).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(|part| format!("{}", part)).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(|part| format!("{}", part)).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(|part| format!("{}", part)).collect();
|
|
||||||
assert_eq!(joined_str, expected_str.to_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,263 +1,144 @@
|
|||||||
use alloc::{boxed::Box, format, string::String, vec::Vec};
|
use std::{borrow::Cow, collections::BTreeMap};
|
||||||
use core::cmp::Ordering;
|
|
||||||
|
|
||||||
use anyhow::{Context, Result, anyhow, bail};
|
use anyhow::{anyhow, bail, ensure, Result};
|
||||||
use iced_x86::{
|
use iced_x86::{
|
||||||
Decoder, DecoderOptions, DecoratorKind, FormatterOutput, FormatterTextKind, GasFormatter,
|
Decoder, DecoderOptions, DecoratorKind, Formatter, FormatterOutput, FormatterTextKind,
|
||||||
Instruction, IntelFormatter, MasmFormatter, NasmFormatter, NumberKind, OpKind, Register,
|
GasFormatter, Instruction, IntelFormatter, MasmFormatter, NasmFormatter, NumberKind, OpKind,
|
||||||
|
PrefixKind, Register,
|
||||||
};
|
};
|
||||||
use object::{Endian as _, Object as _, ObjectSection as _, elf, pe};
|
use object::{pe, Endian, Endianness, File, Object, Relocation, RelocationFlags};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
arch::Arch,
|
arch::{ObjArch, ProcessCodeResult},
|
||||||
diff::{DiffObjConfig, X86Formatter, display::InstructionPart},
|
diff::{DiffObjConfig, X86Formatter},
|
||||||
obj::{InstructionRef, Relocation, RelocationFlags, ResolvedInstructionRef},
|
obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
pub struct ObjArchX86 {
|
||||||
pub struct ArchX86 {
|
bits: u32,
|
||||||
arch: Architecture,
|
endianness: Endianness,
|
||||||
endianness: object::Endianness,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
impl ObjArchX86 {
|
||||||
enum Architecture {
|
pub fn new(object: &File) -> Result<Self> {
|
||||||
X86,
|
Ok(Self { bits: if object.is_64() { 64 } else { 32 }, endianness: object.endianness() })
|
||||||
X86_64,
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ArchX86 {
|
impl ObjArch for ObjArchX86 {
|
||||||
pub fn new(object: &object::File) -> Result<Self> {
|
fn process_code(
|
||||||
let arch = match object.architecture() {
|
&self,
|
||||||
object::Architecture::I386 => Architecture::X86,
|
address: u64,
|
||||||
object::Architecture::X86_64 => Architecture::X86_64,
|
code: &[u8],
|
||||||
_ => bail!("Unsupported architecture for ArchX86: {:?}", object.architecture()),
|
_section_index: usize,
|
||||||
};
|
relocations: &[ObjReloc],
|
||||||
Ok(Self { arch, endianness: object.endianness() })
|
line_info: &BTreeMap<u64, u32>,
|
||||||
}
|
config: &DiffObjConfig,
|
||||||
|
) -> Result<ProcessCodeResult> {
|
||||||
fn decoder<'a>(&self, code: &'a [u8], address: u64) -> Decoder<'a> {
|
let mut result = ProcessCodeResult { ops: Vec::new(), insts: Vec::new() };
|
||||||
Decoder::with_ip(
|
let mut decoder = Decoder::with_ip(self.bits, code, address, DecoderOptions::NONE);
|
||||||
match self.arch {
|
let mut formatter: Box<dyn Formatter> = match config.x86_formatter {
|
||||||
Architecture::X86 => 32,
|
|
||||||
Architecture::X86_64 => 64,
|
|
||||||
},
|
|
||||||
code,
|
|
||||||
address,
|
|
||||||
DecoderOptions::NONE,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn formatter(&self, diff_config: &DiffObjConfig) -> Box<dyn iced_x86::Formatter> {
|
|
||||||
let mut formatter: Box<dyn iced_x86::Formatter> = match diff_config.x86_formatter {
|
|
||||||
X86Formatter::Intel => Box::new(IntelFormatter::new()),
|
X86Formatter::Intel => Box::new(IntelFormatter::new()),
|
||||||
X86Formatter::Gas => Box::new(GasFormatter::new()),
|
X86Formatter::Gas => Box::new(GasFormatter::new()),
|
||||||
X86Formatter::Nasm => Box::new(NasmFormatter::new()),
|
X86Formatter::Nasm => Box::new(NasmFormatter::new()),
|
||||||
X86Formatter::Masm => Box::new(MasmFormatter::new()),
|
X86Formatter::Masm => Box::new(MasmFormatter::new()),
|
||||||
};
|
};
|
||||||
formatter.options_mut().set_space_after_operand_separator(diff_config.space_between_args);
|
formatter.options_mut().set_space_after_operand_separator(config.space_between_args);
|
||||||
formatter
|
|
||||||
}
|
|
||||||
|
|
||||||
fn reloc_size(&self, flags: RelocationFlags) -> Option<usize> {
|
let mut output = InstructionFormatterOutput {
|
||||||
match self.arch {
|
formatted: String::new(),
|
||||||
Architecture::X86 => match flags {
|
ins: ObjIns {
|
||||||
RelocationFlags::Coff(typ) => match typ {
|
address: 0,
|
||||||
pe::IMAGE_REL_I386_DIR16 | pe::IMAGE_REL_I386_REL16 => Some(2),
|
size: 0,
|
||||||
pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32 => Some(4),
|
op: 0,
|
||||||
_ => None,
|
mnemonic: String::new(),
|
||||||
},
|
args: vec![],
|
||||||
RelocationFlags::Elf(typ) => match typ {
|
reloc: None,
|
||||||
elf::R_386_32 | elf::R_386_PC32 => Some(4),
|
branch_dest: None,
|
||||||
elf::R_386_16 => Some(2),
|
line: None,
|
||||||
_ => None,
|
formatted: String::new(),
|
||||||
},
|
orig: None,
|
||||||
},
|
},
|
||||||
Architecture::X86_64 => match flags {
|
error: None,
|
||||||
RelocationFlags::Coff(typ) => match typ {
|
ins_operands: vec![],
|
||||||
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_internal(
|
|
||||||
&self,
|
|
||||||
address: u64,
|
|
||||||
code: &[u8],
|
|
||||||
_section_index: usize,
|
|
||||||
relocations: &[Relocation],
|
|
||||||
_diff_config: &DiffObjConfig,
|
|
||||||
) -> Result<Vec<InstructionRef>> {
|
|
||||||
let mut out = Vec::with_capacity(code.len() / 2);
|
|
||||||
let mut decoder = self.decoder(code, address);
|
|
||||||
let mut instruction = Instruction::default();
|
let mut instruction = Instruction::default();
|
||||||
let mut reloc_iter = relocations.iter().peekable();
|
while decoder.can_decode() {
|
||||||
'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);
|
decoder.decode_out(&mut instruction);
|
||||||
let branch_dest = match instruction.op0_kind() {
|
|
||||||
OpKind::NearBranch16 => Some(instruction.near_branch16() as u64),
|
let address = instruction.ip();
|
||||||
OpKind::NearBranch32 => Some(instruction.near_branch32() as u64),
|
let op = instruction.mnemonic() as u16;
|
||||||
OpKind::NearBranch64 => Some(instruction.near_branch64()),
|
let reloc = relocations
|
||||||
_ => None,
|
.iter()
|
||||||
};
|
.find(|r| r.address >= address && r.address < address + instruction.len() as u64);
|
||||||
out.push(InstructionRef {
|
let line = line_info.range(..=address).last().map(|(_, &b)| b);
|
||||||
|
output.ins = ObjIns {
|
||||||
address,
|
address,
|
||||||
size: instruction.len() as u8,
|
size: instruction.len() as u8,
|
||||||
opcode: instruction.mnemonic() as u16,
|
op,
|
||||||
branch_dest,
|
mnemonic: String::new(),
|
||||||
});
|
args: vec![],
|
||||||
}
|
reloc: reloc.cloned(),
|
||||||
Ok(out)
|
branch_dest: None,
|
||||||
}
|
line,
|
||||||
|
formatted: String::new(),
|
||||||
fn display_instruction(
|
orig: None,
|
||||||
&self,
|
|
||||||
resolved: ResolvedInstructionRef,
|
|
||||||
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))?;
|
// Run the formatter, which will populate output.ins
|
||||||
if resolved.relocation.is_some() {
|
formatter.format(&instruction, &mut output);
|
||||||
cb(InstructionPart::reloc())?;
|
if let Some(error) = output.error.take() {
|
||||||
} else {
|
return Err(error);
|
||||||
cb(InstructionPart::unsigned(imm))?;
|
|
||||||
}
|
}
|
||||||
return Ok(());
|
ensure!(output.ins_operands.len() == output.ins.args.len());
|
||||||
}
|
output.ins.formatted.clone_from(&output.formatted);
|
||||||
|
|
||||||
let mut decoder = self.decoder(resolved.code, resolved.ins_ref.address);
|
// Make sure we've put the relocation somewhere in the instruction
|
||||||
let mut formatter = self.formatter(diff_config);
|
if reloc.is_some() && !output.ins.args.iter().any(|a| matches!(a, ObjInsArg::Reloc)) {
|
||||||
let mut instruction = Instruction::default();
|
let mut found = replace_arg(
|
||||||
decoder.decode_out(&mut instruction);
|
OpKind::Memory,
|
||||||
|
ObjInsArg::Reloc,
|
||||||
// Determine where to insert relocation in instruction output.
|
&mut output.ins.args,
|
||||||
// We replace the immediate or displacement with a placeholder value since the formatter
|
&instruction,
|
||||||
// doesn't provide enough information to know which number is the displacement inside a
|
&output.ins_operands,
|
||||||
// memory operand.
|
)?;
|
||||||
let mut reloc_replace = None;
|
if !found {
|
||||||
if let Some(reloc) = resolved.relocation {
|
found = replace_arg(
|
||||||
const PLACEHOLDER: u64 = 0x7BDE3E7D; // chosen by fair dice roll. guaranteed to be random.
|
OpKind::Immediate32,
|
||||||
let reloc_offset = reloc.relocation.address - resolved.ins_ref.address;
|
ObjInsArg::Reloc,
|
||||||
let reloc_size = self.reloc_size(reloc.relocation.flags).unwrap_or(usize::MAX);
|
&mut output.ins.args,
|
||||||
let offsets = decoder.get_constant_offsets(&instruction);
|
&instruction,
|
||||||
if reloc_offset == offsets.displacement_offset() as u64
|
&output.ins_operands,
|
||||||
&& reloc_size == offsets.displacement_size()
|
)?;
|
||||||
{
|
|
||||||
instruction.set_memory_displacement64(PLACEHOLDER);
|
|
||||||
// Formatter always writes the displacement as Int32
|
|
||||||
reloc_replace = Some((OpKind::Memory, 4, PLACEHOLDER));
|
|
||||||
} else if reloc_offset == offsets.immediate_offset() as u64
|
|
||||||
&& reloc_size == offsets.immediate_size()
|
|
||||||
{
|
|
||||||
let is_branch = matches!(
|
|
||||||
instruction.op0_kind(),
|
|
||||||
OpKind::NearBranch16 | OpKind::NearBranch32 | OpKind::NearBranch64
|
|
||||||
);
|
|
||||||
let op_kind = if is_branch {
|
|
||||||
instruction.op0_kind()
|
|
||||||
} else {
|
|
||||||
match reloc_size {
|
|
||||||
2 => OpKind::Immediate16,
|
|
||||||
4 => OpKind::Immediate32,
|
|
||||||
8 => OpKind::Immediate64,
|
|
||||||
_ => OpKind::default(),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if is_branch {
|
|
||||||
instruction.set_near_branch64(PLACEHOLDER);
|
|
||||||
} else {
|
|
||||||
instruction.set_immediate32(PLACEHOLDER as u32);
|
|
||||||
}
|
}
|
||||||
reloc_replace = Some((op_kind, reloc_size, PLACEHOLDER));
|
ensure!(found, "x86: Failed to find operand for Absolute relocation");
|
||||||
|
}
|
||||||
|
if reloc.is_some() && !output.ins.args.iter().any(|a| matches!(a, ObjInsArg::Reloc)) {
|
||||||
|
bail!("Failed to find relocation in instruction");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let mut output =
|
result.ops.push(op);
|
||||||
InstructionFormatterOutput { cb, reloc_replace, error: None, skip_next: false };
|
result.insts.push(output.ins.clone());
|
||||||
formatter.format(&instruction, &mut output);
|
|
||||||
if let Some(error) = output.error.take() {
|
// Clear for next iteration
|
||||||
return Err(error);
|
output.formatted.clear();
|
||||||
|
output.ins_operands.clear();
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn implcit_addend(
|
fn implcit_addend(
|
||||||
&self,
|
&self,
|
||||||
_file: &object::File<'_>,
|
_file: &File<'_>,
|
||||||
section: &object::Section,
|
section: &ObjSection,
|
||||||
address: u64,
|
address: u64,
|
||||||
_relocation: &object::Relocation,
|
reloc: &Relocation,
|
||||||
flags: RelocationFlags,
|
|
||||||
) -> Result<i64> {
|
) -> Result<i64> {
|
||||||
match self.arch {
|
match reloc.flags() {
|
||||||
Architecture::X86 => match flags {
|
RelocationFlags::Coff { typ: pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32 } => {
|
||||||
RelocationFlags::Coff(pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32)
|
let data = section.data[address as usize..address as usize + 4].try_into()?;
|
||||||
| RelocationFlags::Elf(elf::R_386_32 | elf::R_386_PC32) => {
|
Ok(self.endianness.read_i32_bytes(data) as i64)
|
||||||
let data =
|
}
|
||||||
section.data()?[address as usize..address as usize + 4].try_into()?;
|
flags => bail!("Unsupported x86 implicit relocation {flags:?}"),
|
||||||
Ok(self.endianness.read_i32_bytes(data) as i64)
|
|
||||||
}
|
|
||||||
flags => bail!("Unsupported x86 implicit relocation {flags:?}"),
|
|
||||||
},
|
|
||||||
Architecture::X86_64 => match flags {
|
|
||||||
RelocationFlags::Coff(pe::IMAGE_REL_AMD64_ADDR32NB | pe::IMAGE_REL_AMD64_REL32)
|
|
||||||
| RelocationFlags::Elf(elf::R_X86_64_32 | elf::R_X86_64_PC32) => {
|
|
||||||
let data =
|
|
||||||
section.data()?[address as usize..address as usize + 4].try_into()?;
|
|
||||||
Ok(self.endianness.read_i32_bytes(data) as i64)
|
|
||||||
}
|
|
||||||
RelocationFlags::Coff(pe::IMAGE_REL_AMD64_ADDR64)
|
|
||||||
| RelocationFlags::Elf(elf::R_X86_64_64) => {
|
|
||||||
let data =
|
|
||||||
section.data()?[address as usize..address as usize + 8].try_into()?;
|
|
||||||
Ok(self.endianness.read_i64_bytes(data))
|
|
||||||
}
|
|
||||||
flags => bail!("Unsupported x86-64 implicit relocation {flags:?}"),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -271,148 +152,165 @@ impl Arch for ArchX86 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reloc_name(&self, flags: RelocationFlags) -> Option<&'static str> {
|
fn display_reloc(&self, flags: RelocationFlags) -> Cow<'static, str> {
|
||||||
match self.arch {
|
match flags {
|
||||||
Architecture::X86 => match flags {
|
RelocationFlags::Coff { typ } => match typ {
|
||||||
RelocationFlags::Coff(typ) => match typ {
|
pe::IMAGE_REL_I386_DIR32 => Cow::Borrowed("IMAGE_REL_I386_DIR32"),
|
||||||
pe::IMAGE_REL_I386_DIR32 => Some("IMAGE_REL_I386_DIR32"),
|
pe::IMAGE_REL_I386_REL32 => Cow::Borrowed("IMAGE_REL_I386_REL32"),
|
||||||
pe::IMAGE_REL_I386_REL32 => Some("IMAGE_REL_I386_REL32"),
|
_ => Cow::Owned(format!("<{flags:?}>")),
|
||||||
_ => 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,
|
|
||||||
},
|
},
|
||||||
|
_ => Cow::Owned(format!("<{flags:?}>")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn data_reloc_size(&self, flags: RelocationFlags) -> usize {
|
|
||||||
self.reloc_size(flags).unwrap_or(1)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct InstructionFormatterOutput<'a> {
|
fn replace_arg(
|
||||||
cb: &'a mut dyn FnMut(InstructionPart<'_>) -> Result<()>,
|
from: OpKind,
|
||||||
reloc_replace: Option<(OpKind, usize, u64)>,
|
to: ObjInsArg,
|
||||||
|
args: &mut [ObjInsArg],
|
||||||
|
instruction: &Instruction,
|
||||||
|
ins_operands: &[Option<u32>],
|
||||||
|
) -> Result<bool> {
|
||||||
|
let mut replace = None;
|
||||||
|
for i in 0..instruction.op_count() {
|
||||||
|
let op_kind = instruction.op_kind(i);
|
||||||
|
if op_kind == from {
|
||||||
|
replace = Some(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(i) = replace {
|
||||||
|
for (j, arg) in args.iter_mut().enumerate() {
|
||||||
|
if ins_operands[j] == Some(i) {
|
||||||
|
*arg = to;
|
||||||
|
return Ok(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct InstructionFormatterOutput {
|
||||||
|
formatted: String,
|
||||||
|
ins: ObjIns,
|
||||||
error: Option<anyhow::Error>,
|
error: Option<anyhow::Error>,
|
||||||
skip_next: bool,
|
ins_operands: Vec<Option<u32>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InstructionFormatterOutput<'_> {
|
impl InstructionFormatterOutput {
|
||||||
fn push_signed(&mut self, mut value: i64) {
|
fn push_signed(&mut self, value: i64) {
|
||||||
if self.error.is_some() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// The formatter writes the '-' operator and then gives us a negative value,
|
// The formatter writes the '-' operator and then gives us a negative value,
|
||||||
// so convert it to a positive value to avoid double negatives
|
// so convert it to a positive value to avoid double negatives
|
||||||
if value < 0 {
|
if value < 0
|
||||||
value = value.wrapping_abs();
|
&& matches!(self.ins.args.last(), Some(ObjInsArg::Arg(ObjInsArgValue::Opaque(v))) if v == "-")
|
||||||
}
|
{
|
||||||
if let Err(e) = (self.cb)(InstructionPart::signed(value)) {
|
self.ins.args.push(ObjInsArg::Arg(ObjInsArgValue::Signed(value.wrapping_abs())));
|
||||||
self.error = Some(e);
|
} else {
|
||||||
|
self.ins.args.push(ObjInsArg::Arg(ObjInsArgValue::Signed(value)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FormatterOutput for InstructionFormatterOutput<'_> {
|
impl FormatterOutput for InstructionFormatterOutput {
|
||||||
fn write(&mut self, text: &str, kind: FormatterTextKind) {
|
fn write(&mut self, text: &str, kind: FormatterTextKind) {
|
||||||
if self.error.is_some() {
|
self.formatted.push_str(text);
|
||||||
|
// Skip whitespace after the mnemonic
|
||||||
|
if self.ins.args.is_empty() && kind == FormatterTextKind::Text {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Skip whitespace after the mnemonic
|
self.ins_operands.push(None);
|
||||||
if self.skip_next {
|
|
||||||
self.skip_next = false;
|
|
||||||
if kind == FormatterTextKind::Text && text == " " {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
match kind {
|
match kind {
|
||||||
FormatterTextKind::Text | FormatterTextKind::Punctuation => {
|
FormatterTextKind::Text | FormatterTextKind::Punctuation => {
|
||||||
if let Err(e) = (self.cb)(InstructionPart::basic(text)) {
|
self.ins.args.push(ObjInsArg::PlainText(text.to_string().into()));
|
||||||
self.error = Some(e);
|
}
|
||||||
|
FormatterTextKind::Keyword | FormatterTextKind::Operator => {
|
||||||
|
self.ins.args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(text.to_string().into())));
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
if self.error.is_none() {
|
||||||
|
self.error = Some(anyhow!("x86: Unsupported FormatterTextKind {:?}", kind));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FormatterTextKind::Prefix
|
|
||||||
| FormatterTextKind::Keyword
|
|
||||||
| FormatterTextKind::Operator => {
|
|
||||||
if let Err(e) = (self.cb)(InstructionPart::opaque(text)) {
|
|
||||||
self.error = Some(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => self.error = Some(anyhow!("x86: Unsupported FormatterTextKind {:?}", kind)),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_mnemonic(&mut self, instruction: &Instruction, text: &str) {
|
fn write_prefix(&mut self, _instruction: &Instruction, text: &str, _prefix: PrefixKind) {
|
||||||
if self.error.is_some() {
|
self.formatted.push_str(text);
|
||||||
return;
|
self.ins_operands.push(None);
|
||||||
}
|
self.ins.args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(text.to_string().into())));
|
||||||
if let Err(e) = (self.cb)(InstructionPart::opcode(text, instruction.mnemonic() as u16)) {
|
}
|
||||||
self.error = Some(e);
|
|
||||||
}
|
fn write_mnemonic(&mut self, _instruction: &Instruction, text: &str) {
|
||||||
// Skip whitespace after the mnemonic
|
self.formatted.push_str(text);
|
||||||
self.skip_next = true;
|
self.ins.mnemonic = text.to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_number(
|
fn write_number(
|
||||||
&mut self,
|
&mut self,
|
||||||
instruction: &Instruction,
|
_instruction: &Instruction,
|
||||||
_operand: u32,
|
_operand: u32,
|
||||||
instruction_operand: Option<u32>,
|
instruction_operand: Option<u32>,
|
||||||
_text: &str,
|
text: &str,
|
||||||
value: u64,
|
value: u64,
|
||||||
number_kind: NumberKind,
|
number_kind: NumberKind,
|
||||||
kind: FormatterTextKind,
|
kind: FormatterTextKind,
|
||||||
) {
|
) {
|
||||||
if self.error.is_some() {
|
self.formatted.push_str(text);
|
||||||
return;
|
self.ins_operands.push(instruction_operand);
|
||||||
}
|
|
||||||
|
|
||||||
if let (Some(operand), Some((target_op_kind, reloc_size, target_value))) =
|
// Handle relocations
|
||||||
(instruction_operand, self.reloc_replace)
|
match kind {
|
||||||
{
|
FormatterTextKind::LabelAddress => {
|
||||||
#[allow(clippy::match_like_matches_macro)]
|
if let Some(reloc) = self.ins.reloc.as_ref() {
|
||||||
if instruction.op_kind(operand) == target_op_kind
|
if matches!(reloc.flags, RelocationFlags::Coff {
|
||||||
&& match (number_kind, reloc_size) {
|
typ: pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32
|
||||||
(NumberKind::Int8 | NumberKind::UInt8, 1)
|
}) {
|
||||||
| (NumberKind::Int16 | NumberKind::UInt16, 2)
|
self.ins.args.push(ObjInsArg::Reloc);
|
||||||
| (NumberKind::Int32 | NumberKind::UInt32, 4)
|
return;
|
||||||
| (NumberKind::Int64 | NumberKind::UInt64, 4) // x86_64
|
} else if self.error.is_none() {
|
||||||
| (NumberKind::Int64 | NumberKind::UInt64, 8) => true,
|
self.error = Some(anyhow!(
|
||||||
_ => false,
|
"x86: Unsupported LabelAddress relocation flags {:?}",
|
||||||
}
|
reloc.flags
|
||||||
&& value == target_value
|
));
|
||||||
{
|
}
|
||||||
if let Err(e) = (self.cb)(InstructionPart::reloc()) {
|
|
||||||
self.error = Some(e);
|
|
||||||
}
|
}
|
||||||
|
self.ins.args.push(ObjInsArg::BranchDest(value));
|
||||||
|
self.ins.branch_dest = Some(value);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
FormatterTextKind::FunctionAddress => {
|
||||||
|
if let Some(reloc) = self.ins.reloc.as_ref() {
|
||||||
if let FormatterTextKind::LabelAddress | FormatterTextKind::FunctionAddress = kind {
|
if matches!(reloc.flags, RelocationFlags::Coff {
|
||||||
if let Err(e) = (self.cb)(InstructionPart::branch_dest(value)) {
|
typ: pe::IMAGE_REL_I386_REL32
|
||||||
self.error = Some(e);
|
}) {
|
||||||
|
self.ins.args.push(ObjInsArg::Reloc);
|
||||||
|
return;
|
||||||
|
} else if self.error.is_none() {
|
||||||
|
self.error = Some(anyhow!(
|
||||||
|
"x86: Unsupported FunctionAddress relocation flags {:?}",
|
||||||
|
reloc.flags
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return;
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
match number_kind {
|
match number_kind {
|
||||||
NumberKind::Int8 => self.push_signed(value as i8 as i64),
|
NumberKind::Int8 => {
|
||||||
NumberKind::Int16 => self.push_signed(value as i16 as i64),
|
self.push_signed(value as i8 as i64);
|
||||||
NumberKind::Int32 => self.push_signed(value as i32 as i64),
|
}
|
||||||
NumberKind::Int64 => self.push_signed(value as i64),
|
NumberKind::Int16 => {
|
||||||
|
self.push_signed(value as i16 as i64);
|
||||||
|
}
|
||||||
|
NumberKind::Int32 => {
|
||||||
|
self.push_signed(value as i32 as i64);
|
||||||
|
}
|
||||||
|
NumberKind::Int64 => {
|
||||||
|
self.push_signed(value as i64);
|
||||||
|
}
|
||||||
NumberKind::UInt8 | NumberKind::UInt16 | NumberKind::UInt32 | NumberKind::UInt64 => {
|
NumberKind::UInt8 | NumberKind::UInt16 | NumberKind::UInt32 | NumberKind::UInt64 => {
|
||||||
if let Err(e) = (self.cb)(InstructionPart::unsigned(value)) {
|
self.ins.args.push(ObjInsArg::Arg(ObjInsArgValue::Unsigned(value)));
|
||||||
self.error = Some(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -421,321 +319,25 @@ impl FormatterOutput for InstructionFormatterOutput<'_> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
_instruction: &Instruction,
|
_instruction: &Instruction,
|
||||||
_operand: u32,
|
_operand: u32,
|
||||||
_instruction_operand: Option<u32>,
|
instruction_operand: Option<u32>,
|
||||||
text: &str,
|
text: &str,
|
||||||
_decorator: DecoratorKind,
|
_decorator: DecoratorKind,
|
||||||
) {
|
) {
|
||||||
if self.error.is_some() {
|
self.formatted.push_str(text);
|
||||||
return;
|
self.ins_operands.push(instruction_operand);
|
||||||
}
|
self.ins.args.push(ObjInsArg::PlainText(text.to_string().into()));
|
||||||
if let Err(e) = (self.cb)(InstructionPart::basic(text)) {
|
|
||||||
self.error = Some(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_register(
|
fn write_register(
|
||||||
&mut self,
|
&mut self,
|
||||||
_instruction: &Instruction,
|
_instruction: &Instruction,
|
||||||
_operand: u32,
|
_operand: u32,
|
||||||
_instruction_operand: Option<u32>,
|
instruction_operand: Option<u32>,
|
||||||
text: &str,
|
text: &str,
|
||||||
_register: Register,
|
_register: Register,
|
||||||
) {
|
) {
|
||||||
if self.error.is_some() {
|
self.formatted.push_str(text);
|
||||||
return;
|
self.ins_operands.push(instruction_operand);
|
||||||
}
|
self.ins.args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(text.to_string().into())));
|
||||||
if let Err(e) = (self.cb)(InstructionPart::opaque(text)) {
|
|
||||||
self.error = Some(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
use crate::obj::{Relocation, ResolvedRelocation};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_scan_instructions() {
|
|
||||||
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_internal(0, &code, 0, &[], &DiffObjConfig::default()).unwrap();
|
|
||||||
assert_eq!(scanned.len(), 2);
|
|
||||||
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].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 { 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, branch_dest: None },
|
|
||||||
code: &code,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
&DiffObjConfig::default(),
|
|
||||||
&mut |part| {
|
|
||||||
parts.push(part.into_static());
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(parts, &[
|
|
||||||
InstructionPart::opcode("mov", opcode),
|
|
||||||
InstructionPart::opaque("dword"),
|
|
||||||
InstructionPart::basic(" "),
|
|
||||||
InstructionPart::opaque("ptr"),
|
|
||||||
InstructionPart::basic(" "),
|
|
||||||
InstructionPart::basic("["),
|
|
||||||
InstructionPart::opaque("ebp"),
|
|
||||||
InstructionPart::opaque("-"),
|
|
||||||
InstructionPart::signed(152i64),
|
|
||||||
InstructionPart::basic("]"),
|
|
||||||
InstructionPart::basic(","),
|
|
||||||
InstructionPart::basic(" "),
|
|
||||||
InstructionPart::unsigned(0u64),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_process_instruction_with_reloc_1() {
|
|
||||||
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, branch_dest: None },
|
|
||||||
code: &code,
|
|
||||||
relocation: Some(ResolvedRelocation {
|
|
||||||
relocation: &Relocation {
|
|
||||||
flags: RelocationFlags::Coff(pe::IMAGE_REL_I386_DIR32),
|
|
||||||
address: 0x1234 + 6,
|
|
||||||
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("dword"),
|
|
||||||
InstructionPart::basic(" "),
|
|
||||||
InstructionPart::opaque("ptr"),
|
|
||||||
InstructionPart::basic(" "),
|
|
||||||
InstructionPart::basic("["),
|
|
||||||
InstructionPart::opaque("ebp"),
|
|
||||||
InstructionPart::opaque("-"),
|
|
||||||
InstructionPart::signed(152i64),
|
|
||||||
InstructionPart::basic("]"),
|
|
||||||
InstructionPart::basic(","),
|
|
||||||
InstructionPart::basic(" "),
|
|
||||||
InstructionPart::reloc(),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_process_instruction_with_reloc_2() {
|
|
||||||
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, branch_dest: None },
|
|
||||||
code: &code,
|
|
||||||
relocation: Some(ResolvedRelocation {
|
|
||||||
relocation: &Relocation {
|
|
||||||
flags: RelocationFlags::Coff(pe::IMAGE_REL_I386_DIR32),
|
|
||||||
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("eax"),
|
|
||||||
InstructionPart::basic(","),
|
|
||||||
InstructionPart::basic(" "),
|
|
||||||
InstructionPart::basic("["),
|
|
||||||
InstructionPart::opaque("eax"),
|
|
||||||
InstructionPart::opaque("*"),
|
|
||||||
InstructionPart::signed(4),
|
|
||||||
InstructionPart::opaque("+"),
|
|
||||||
InstructionPart::reloc(),
|
|
||||||
InstructionPart::basic("]"),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_process_instruction_with_reloc_3() {
|
|
||||||
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, branch_dest: None },
|
|
||||||
code: &code,
|
|
||||||
relocation: Some(ResolvedRelocation {
|
|
||||||
relocation: &Relocation {
|
|
||||||
flags: RelocationFlags::Coff(pe::IMAGE_REL_I386_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()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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()]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,242 +1,244 @@
|
|||||||
#![allow(clippy::needless_lifetimes)] // Generated serde code
|
use crate::{
|
||||||
|
diff::{
|
||||||
use crate::{diff, obj};
|
ObjDataDiff, ObjDataDiffKind, ObjDiff, ObjInsArgDiff, ObjInsBranchFrom, ObjInsBranchTo,
|
||||||
|
ObjInsDiff, ObjInsDiffKind, ObjSectionDiff, ObjSymbolDiff,
|
||||||
|
},
|
||||||
|
obj::{
|
||||||
|
ObjInfo, ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSectionKind, ObjSymbol,
|
||||||
|
ObjSymbolFlagSet, ObjSymbolFlags,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
// Protobuf diff types
|
// Protobuf diff types
|
||||||
include!(concat!(env!("OUT_DIR"), "/objdiff.diff.rs"));
|
include!(concat!(env!("OUT_DIR"), "/objdiff.diff.rs"));
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/objdiff.diff.serde.rs"));
|
include!(concat!(env!("OUT_DIR"), "/objdiff.diff.serde.rs"));
|
||||||
|
|
||||||
impl DiffResult {
|
impl DiffResult {
|
||||||
pub fn new(
|
pub fn new(left: Option<(&ObjInfo, &ObjDiff)>, right: Option<(&ObjInfo, &ObjDiff)>) -> Self {
|
||||||
_left: Option<(&obj::Object, &diff::ObjectDiff)>,
|
|
||||||
_right: Option<(&obj::Object, &diff::ObjectDiff)>,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
// TODO
|
left: left.map(|(obj, diff)| ObjectDiff::new(obj, diff)),
|
||||||
// left: left.map(|(obj, diff)| ObjectDiff::new(obj, diff)),
|
right: right.map(|(obj, diff)| ObjectDiff::new(obj, diff)),
|
||||||
// right: right.map(|(obj, diff)| ObjectDiff::new(obj, diff)),
|
|
||||||
left: None,
|
|
||||||
right: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// impl ObjectDiff {
|
impl ObjectDiff {
|
||||||
// pub fn new(obj: &obj::Object, diff: &diff::ObjectDiff) -> Self {
|
pub fn new(obj: &ObjInfo, diff: &ObjDiff) -> Self {
|
||||||
// Self {
|
Self {
|
||||||
// sections: diff
|
sections: diff
|
||||||
// .sections
|
.sections
|
||||||
// .iter()
|
.iter()
|
||||||
// .enumerate()
|
.enumerate()
|
||||||
// .map(|(i, d)| SectionDiff::new(obj, i, d))
|
.map(|(i, d)| SectionDiff::new(obj, i, d))
|
||||||
// .collect(),
|
.collect(),
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// impl SectionDiff {
|
impl SectionDiff {
|
||||||
// pub fn new(obj: &obj::Object, section_index: usize, section_diff: &diff::SectionDiff) -> Self {
|
pub fn new(obj: &ObjInfo, section_index: usize, section_diff: &ObjSectionDiff) -> Self {
|
||||||
// let section = &obj.sections[section_index];
|
let section = &obj.sections[section_index];
|
||||||
// let symbols = section_diff.symbols.iter().map(|d| SymbolDiff::new(obj, d)).collect();
|
let functions = section_diff.symbols.iter().map(|d| FunctionDiff::new(obj, d)).collect();
|
||||||
// let data = section_diff.data_diff.iter().map(|d| DataDiff::new(obj, d)).collect();
|
let data = section_diff.data_diff.iter().map(|d| DataDiff::new(obj, d)).collect();
|
||||||
// // TODO: section_diff.reloc_diff
|
Self {
|
||||||
// Self {
|
name: section.name.to_string(),
|
||||||
// name: section.name.to_string(),
|
kind: SectionKind::from(section.kind) as i32,
|
||||||
// kind: SectionKind::from(section.kind) as i32,
|
size: section.size,
|
||||||
// size: section.size,
|
address: section.address,
|
||||||
// address: section.address,
|
functions,
|
||||||
// symbols,
|
data,
|
||||||
// data,
|
match_percent: section_diff.match_percent,
|
||||||
// match_percent: section_diff.match_percent,
|
}
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// }
|
|
||||||
//
|
impl From<ObjSectionKind> for SectionKind {
|
||||||
// impl From<obj::SectionKind> for SectionKind {
|
fn from(value: ObjSectionKind) -> Self {
|
||||||
// fn from(value: obj::SectionKind) -> Self {
|
match value {
|
||||||
// match value {
|
ObjSectionKind::Code => SectionKind::SectionText,
|
||||||
// obj::SectionKind::Code => SectionKind::SectionText,
|
ObjSectionKind::Data => SectionKind::SectionData,
|
||||||
// obj::SectionKind::Data => SectionKind::SectionData,
|
ObjSectionKind::Bss => SectionKind::SectionBss,
|
||||||
// obj::SectionKind::Bss => SectionKind::SectionBss,
|
// TODO common
|
||||||
// // TODO common
|
}
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// }
|
|
||||||
//
|
impl FunctionDiff {
|
||||||
// impl SymbolDiff {
|
pub fn new(object: &ObjInfo, symbol_diff: &ObjSymbolDiff) -> Self {
|
||||||
// pub fn new(object: &obj::Object, symbol_diff: &diff::SymbolDiff) -> Self {
|
let (_section, symbol) = object.section_symbol(symbol_diff.symbol_ref);
|
||||||
// let symbol = object.symbols[symbol_diff.symbol_index];
|
// let diff_symbol = symbol_diff.diff_symbol.map(|symbol_ref| {
|
||||||
// let instructions = symbol_diff
|
// let (_section, symbol) = object.section_symbol(symbol_ref);
|
||||||
// .instruction_rows
|
// Symbol::from(symbol)
|
||||||
// .iter()
|
// });
|
||||||
// .map(|ins_diff| InstructionDiff::new(object, ins_diff))
|
let instructions = symbol_diff.instructions.iter().map(InstructionDiff::from).collect();
|
||||||
// .collect();
|
Self {
|
||||||
// Self {
|
symbol: Some(Symbol::from(symbol)),
|
||||||
// symbol: Some(Symbol::new(symbol)),
|
// diff_symbol,
|
||||||
// instructions,
|
instructions,
|
||||||
// match_percent: symbol_diff.match_percent,
|
match_percent: symbol_diff.match_percent,
|
||||||
// target: symbol_diff.target_symbol.map(SymbolRef::from),
|
}
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// }
|
|
||||||
//
|
impl DataDiff {
|
||||||
// impl DataDiff {
|
pub fn new(_object: &ObjInfo, data_diff: &ObjDataDiff) -> Self {
|
||||||
// pub fn new(_object: &obj::Object, data_diff: &diff::DataDiff) -> Self {
|
Self {
|
||||||
// Self {
|
kind: DiffKind::from(data_diff.kind) as i32,
|
||||||
// kind: DiffKind::from(data_diff.kind) as i32,
|
data: data_diff.data.clone(),
|
||||||
// data: data_diff.data.clone(),
|
size: data_diff.len as u64,
|
||||||
// size: data_diff.len as u64,
|
}
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// }
|
|
||||||
//
|
impl<'a> From<&'a ObjSymbol> for Symbol {
|
||||||
// impl Symbol {
|
fn from(value: &'a ObjSymbol) -> Self {
|
||||||
// pub fn new(value: &ObjSymbol) -> Self {
|
Self {
|
||||||
// Self {
|
name: value.name.to_string(),
|
||||||
// name: value.name.to_string(),
|
demangled_name: value.demangled_name.clone(),
|
||||||
// demangled_name: value.demangled_name.clone(),
|
address: value.address,
|
||||||
// address: value.address,
|
size: value.size,
|
||||||
// size: value.size,
|
flags: symbol_flags(value.flags),
|
||||||
// flags: symbol_flags(value.flags),
|
}
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// }
|
|
||||||
//
|
fn symbol_flags(value: ObjSymbolFlagSet) -> u32 {
|
||||||
// fn symbol_flags(value: ObjSymbolFlagSet) -> u32 {
|
let mut flags = 0u32;
|
||||||
// let mut flags = 0u32;
|
if value.0.contains(ObjSymbolFlags::Global) {
|
||||||
// if value.0.contains(ObjSymbolFlags::Global) {
|
flags |= SymbolFlag::SymbolNone as u32;
|
||||||
// flags |= SymbolFlag::SymbolGlobal as u32;
|
}
|
||||||
// }
|
if value.0.contains(ObjSymbolFlags::Local) {
|
||||||
// if value.0.contains(ObjSymbolFlags::Local) {
|
flags |= SymbolFlag::SymbolLocal as u32;
|
||||||
// flags |= SymbolFlag::SymbolLocal as u32;
|
}
|
||||||
// }
|
if value.0.contains(ObjSymbolFlags::Weak) {
|
||||||
// if value.0.contains(ObjSymbolFlags::Weak) {
|
flags |= SymbolFlag::SymbolWeak as u32;
|
||||||
// flags |= SymbolFlag::SymbolWeak as u32;
|
}
|
||||||
// }
|
if value.0.contains(ObjSymbolFlags::Common) {
|
||||||
// if value.0.contains(ObjSymbolFlags::Common) {
|
flags |= SymbolFlag::SymbolCommon as u32;
|
||||||
// flags |= SymbolFlag::SymbolCommon as u32;
|
}
|
||||||
// }
|
if value.0.contains(ObjSymbolFlags::Hidden) {
|
||||||
// if value.0.contains(ObjSymbolFlags::Hidden) {
|
flags |= SymbolFlag::SymbolHidden as u32;
|
||||||
// flags |= SymbolFlag::SymbolHidden as u32;
|
}
|
||||||
// }
|
flags
|
||||||
// flags
|
}
|
||||||
// }
|
|
||||||
//
|
impl<'a> From<&'a ObjIns> for Instruction {
|
||||||
// impl Instruction {
|
fn from(value: &'a ObjIns) -> Self {
|
||||||
// pub fn new(object: &obj::Object, instruction: &ObjIns) -> Self {
|
Self {
|
||||||
// Self {
|
address: value.address,
|
||||||
// address: instruction.address,
|
size: value.size as u32,
|
||||||
// size: instruction.size as u32,
|
opcode: value.op as u32,
|
||||||
// opcode: instruction.op as u32,
|
mnemonic: value.mnemonic.clone(),
|
||||||
// mnemonic: instruction.mnemonic.to_string(),
|
formatted: value.formatted.clone(),
|
||||||
// formatted: instruction.formatted.clone(),
|
arguments: value.args.iter().map(Argument::from).collect(),
|
||||||
// arguments: instruction.args.iter().map(Argument::new).collect(),
|
relocation: value.reloc.as_ref().map(Relocation::from),
|
||||||
// relocation: instruction.reloc.as_ref().map(|reloc| Relocation::new(object, reloc)),
|
branch_dest: value.branch_dest,
|
||||||
// branch_dest: instruction.branch_dest,
|
line_number: value.line,
|
||||||
// line_number: instruction.line,
|
original: value.orig.clone(),
|
||||||
// original: instruction.orig.clone(),
|
}
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// }
|
|
||||||
//
|
impl<'a> From<&'a ObjInsArg> for Argument {
|
||||||
// impl Argument {
|
fn from(value: &'a ObjInsArg) -> Self {
|
||||||
// pub fn new(value: &ObjInsArg) -> Self {
|
Self {
|
||||||
// Self {
|
value: Some(match value {
|
||||||
// value: Some(match value {
|
ObjInsArg::PlainText(s) => argument::Value::PlainText(s.to_string()),
|
||||||
// ObjInsArg::PlainText(s) => argument::Value::PlainText(s.to_string()),
|
ObjInsArg::Arg(v) => argument::Value::Argument(ArgumentValue::from(v)),
|
||||||
// ObjInsArg::Arg(v) => argument::Value::Argument(ArgumentValue::new(v)),
|
ObjInsArg::Reloc => argument::Value::Relocation(ArgumentRelocation {}),
|
||||||
// ObjInsArg::Reloc => argument::Value::Relocation(ArgumentRelocation {}),
|
ObjInsArg::BranchDest(dest) => argument::Value::BranchDest(*dest),
|
||||||
// ObjInsArg::BranchDest(dest) => argument::Value::BranchDest(*dest),
|
}),
|
||||||
// }),
|
}
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// }
|
|
||||||
//
|
impl From<&ObjInsArgValue> for ArgumentValue {
|
||||||
// impl ArgumentValue {
|
fn from(value: &ObjInsArgValue) -> Self {
|
||||||
// pub fn new(value: &ObjInsArgValue) -> Self {
|
Self {
|
||||||
// Self {
|
value: Some(match value {
|
||||||
// value: Some(match value {
|
ObjInsArgValue::Signed(v) => argument_value::Value::Signed(*v),
|
||||||
// ObjInsArgValue::Signed(v) => argument_value::Value::Signed(*v),
|
ObjInsArgValue::Unsigned(v) => argument_value::Value::Unsigned(*v),
|
||||||
// ObjInsArgValue::Unsigned(v) => argument_value::Value::Unsigned(*v),
|
ObjInsArgValue::Opaque(v) => argument_value::Value::Opaque(v.to_string()),
|
||||||
// ObjInsArgValue::Opaque(v) => argument_value::Value::Opaque(v.to_string()),
|
}),
|
||||||
// }),
|
}
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// }
|
|
||||||
//
|
impl<'a> From<&'a ObjReloc> for Relocation {
|
||||||
// impl Relocation {
|
fn from(value: &ObjReloc) -> Self {
|
||||||
// pub fn new(object: &obj::Object, reloc: &ObjReloc) -> Self {
|
Self {
|
||||||
// Self {
|
r#type: match value.flags {
|
||||||
// r#type: match reloc.flags {
|
object::RelocationFlags::Elf { r_type } => r_type,
|
||||||
// object::RelocationFlags::Elf { r_type } => r_type,
|
object::RelocationFlags::MachO { r_type, .. } => r_type as u32,
|
||||||
// object::RelocationFlags::MachO { r_type, .. } => r_type as u32,
|
object::RelocationFlags::Coff { typ } => typ as u32,
|
||||||
// object::RelocationFlags::Coff { typ } => typ as u32,
|
object::RelocationFlags::Xcoff { r_rtype, .. } => r_rtype as u32,
|
||||||
// object::RelocationFlags::Xcoff { r_rtype, .. } => r_rtype as u32,
|
_ => unreachable!(),
|
||||||
// _ => unreachable!(),
|
},
|
||||||
// },
|
type_name: String::new(), // TODO
|
||||||
// type_name: object.arch.display_reloc(reloc.flags).into_owned(),
|
target: Some(RelocationTarget::from(&value.target)),
|
||||||
// target: Some(RelocationTarget {
|
}
|
||||||
// symbol: Some(Symbol::new(&reloc.target)),
|
}
|
||||||
// addend: reloc.addend,
|
}
|
||||||
// }),
|
|
||||||
// }
|
impl<'a> From<&'a ObjSymbol> for RelocationTarget {
|
||||||
// }
|
fn from(value: &'a ObjSymbol) -> Self {
|
||||||
// }
|
Self { symbol: Some(Symbol::from(value)), addend: value.addend }
|
||||||
//
|
}
|
||||||
// impl InstructionDiff {
|
}
|
||||||
// pub fn new(object: &obj::Object, instruction_diff: &ObjInsDiff) -> Self {
|
|
||||||
// Self {
|
impl<'a> From<&'a ObjInsDiff> for InstructionDiff {
|
||||||
// instruction: instruction_diff.ins.as_ref().map(|ins| Instruction::new(object, ins)),
|
fn from(value: &'a ObjInsDiff) -> Self {
|
||||||
// diff_kind: DiffKind::from(instruction_diff.kind) as i32,
|
Self {
|
||||||
// branch_from: instruction_diff.branch_from.as_ref().map(InstructionBranchFrom::new),
|
instruction: value.ins.as_ref().map(Instruction::from),
|
||||||
// branch_to: instruction_diff.branch_to.as_ref().map(InstructionBranchTo::new),
|
diff_kind: DiffKind::from(value.kind) as i32,
|
||||||
// arg_diff: instruction_diff.arg_diff.iter().map(ArgumentDiff::new).collect(),
|
branch_from: value.branch_from.as_ref().map(InstructionBranchFrom::from),
|
||||||
// }
|
branch_to: value.branch_to.as_ref().map(InstructionBranchTo::from),
|
||||||
// }
|
arg_diff: value.arg_diff.iter().map(ArgumentDiff::from).collect(),
|
||||||
// }
|
}
|
||||||
//
|
}
|
||||||
// impl ArgumentDiff {
|
}
|
||||||
// pub fn new(value: &Option<ObjInsArgDiff>) -> Self {
|
|
||||||
// Self { diff_index: value.as_ref().map(|v| v.idx as u32) }
|
impl From<&Option<ObjInsArgDiff>> for ArgumentDiff {
|
||||||
// }
|
fn from(value: &Option<ObjInsArgDiff>) -> Self {
|
||||||
// }
|
Self { diff_index: value.as_ref().map(|v| v.idx as u32) }
|
||||||
//
|
}
|
||||||
// impl From<ObjInsDiffKind> for DiffKind {
|
}
|
||||||
// fn from(value: ObjInsDiffKind) -> Self {
|
|
||||||
// match value {
|
impl From<ObjInsDiffKind> for DiffKind {
|
||||||
// ObjInsDiffKind::None => DiffKind::DiffNone,
|
fn from(value: ObjInsDiffKind) -> Self {
|
||||||
// ObjInsDiffKind::OpMismatch => DiffKind::DiffOpMismatch,
|
match value {
|
||||||
// ObjInsDiffKind::ArgMismatch => DiffKind::DiffArgMismatch,
|
ObjInsDiffKind::None => DiffKind::DiffNone,
|
||||||
// ObjInsDiffKind::Replace => DiffKind::DiffReplace,
|
ObjInsDiffKind::OpMismatch => DiffKind::DiffOpMismatch,
|
||||||
// ObjInsDiffKind::Delete => DiffKind::DiffDelete,
|
ObjInsDiffKind::ArgMismatch => DiffKind::DiffArgMismatch,
|
||||||
// ObjInsDiffKind::Insert => DiffKind::DiffInsert,
|
ObjInsDiffKind::Replace => DiffKind::DiffReplace,
|
||||||
// }
|
ObjInsDiffKind::Delete => DiffKind::DiffDelete,
|
||||||
// }
|
ObjInsDiffKind::Insert => DiffKind::DiffInsert,
|
||||||
// }
|
}
|
||||||
//
|
}
|
||||||
// impl From<ObjDataDiffKind> for DiffKind {
|
}
|
||||||
// fn from(value: ObjDataDiffKind) -> Self {
|
|
||||||
// match value {
|
impl From<ObjDataDiffKind> for DiffKind {
|
||||||
// ObjDataDiffKind::None => DiffKind::DiffNone,
|
fn from(value: ObjDataDiffKind) -> Self {
|
||||||
// ObjDataDiffKind::Replace => DiffKind::DiffReplace,
|
match value {
|
||||||
// ObjDataDiffKind::Delete => DiffKind::DiffDelete,
|
ObjDataDiffKind::None => DiffKind::DiffNone,
|
||||||
// ObjDataDiffKind::Insert => DiffKind::DiffInsert,
|
ObjDataDiffKind::Replace => DiffKind::DiffReplace,
|
||||||
// }
|
ObjDataDiffKind::Delete => DiffKind::DiffDelete,
|
||||||
// }
|
ObjDataDiffKind::Insert => DiffKind::DiffInsert,
|
||||||
// }
|
}
|
||||||
//
|
}
|
||||||
// impl InstructionBranchFrom {
|
}
|
||||||
// pub fn new(value: &ObjInsBranchFrom) -> Self {
|
|
||||||
// Self {
|
impl<'a> From<&'a ObjInsBranchFrom> for InstructionBranchFrom {
|
||||||
// instruction_index: value.ins_idx.iter().map(|&x| x as u32).collect(),
|
fn from(value: &'a ObjInsBranchFrom) -> Self {
|
||||||
// branch_index: value.branch_idx as u32,
|
Self {
|
||||||
// }
|
instruction_index: value.ins_idx.iter().map(|&x| x as u32).collect(),
|
||||||
// }
|
branch_index: value.branch_idx as u32,
|
||||||
// }
|
}
|
||||||
//
|
}
|
||||||
// impl InstructionBranchTo {
|
}
|
||||||
// pub fn new(value: &ObjInsBranchTo) -> Self {
|
|
||||||
// Self { instruction_index: value.ins_idx as u32, branch_index: value.branch_idx as u32 }
|
impl<'a> From<&'a ObjInsBranchTo> for InstructionBranchTo {
|
||||||
// }
|
fn from(value: &'a ObjInsBranchTo) -> Self {
|
||||||
// }
|
Self { instruction_index: value.ins_idx as u32, branch_index: value.branch_idx as u32 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
#[cfg(feature = "any-arch")]
|
#[cfg(feature = "any-arch")]
|
||||||
pub mod diff;
|
pub mod diff;
|
||||||
pub mod report;
|
pub mod report;
|
||||||
|
#[cfg(feature = "wasm")]
|
||||||
|
pub mod wasm;
|
||||||
|
@ -1,18 +1,11 @@
|
|||||||
#![allow(clippy::needless_lifetimes)] // Generated serde code
|
use std::ops::AddAssign;
|
||||||
|
|
||||||
use alloc::{
|
use anyhow::{bail, Result};
|
||||||
string::{String, ToString},
|
|
||||||
vec,
|
|
||||||
vec::Vec,
|
|
||||||
};
|
|
||||||
use core::ops::AddAssign;
|
|
||||||
|
|
||||||
use anyhow::{Result, bail};
|
|
||||||
use prost::Message;
|
use prost::Message;
|
||||||
|
use serde_json::error::Category;
|
||||||
|
|
||||||
// Protobuf report types
|
// Protobuf report types
|
||||||
include!(concat!(env!("OUT_DIR"), "/objdiff.report.rs"));
|
include!(concat!(env!("OUT_DIR"), "/objdiff.report.rs"));
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/objdiff.report.serde.rs"));
|
include!(concat!(env!("OUT_DIR"), "/objdiff.report.serde.rs"));
|
||||||
|
|
||||||
pub const REPORT_VERSION: u32 = 2;
|
pub const REPORT_VERSION: u32 = 2;
|
||||||
@ -21,30 +14,23 @@ impl Report {
|
|||||||
/// Attempts to parse the report as binary protobuf or JSON.
|
/// Attempts to parse the report as binary protobuf or JSON.
|
||||||
pub fn parse(data: &[u8]) -> Result<Self> {
|
pub fn parse(data: &[u8]) -> Result<Self> {
|
||||||
if data.is_empty() {
|
if data.is_empty() {
|
||||||
bail!("Empty data");
|
bail!(std::io::Error::from(std::io::ErrorKind::UnexpectedEof));
|
||||||
}
|
}
|
||||||
let report = if data[0] == b'{' {
|
let report = if data[0] == b'{' {
|
||||||
// Load as JSON
|
// Load as JSON
|
||||||
#[cfg(feature = "serde")]
|
Self::from_json(data)?
|
||||||
{
|
|
||||||
Self::from_json(data)?
|
|
||||||
}
|
|
||||||
#[cfg(not(feature = "serde"))]
|
|
||||||
bail!("JSON report parsing requires the `serde` feature")
|
|
||||||
} else {
|
} else {
|
||||||
// Load as binary protobuf
|
// Load as binary protobuf
|
||||||
Self::decode(data).map_err(|e| anyhow::Error::msg(e.to_string()))?
|
Self::decode(data)?
|
||||||
};
|
};
|
||||||
Ok(report)
|
Ok(report)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
/// Attempts to parse the report as JSON, migrating from the legacy report format if necessary.
|
/// Attempts to parse the report as JSON, migrating from the legacy report format if necessary.
|
||||||
fn from_json(bytes: &[u8]) -> Result<Self, serde_json::Error> {
|
fn from_json(bytes: &[u8]) -> Result<Self, serde_json::Error> {
|
||||||
match serde_json::from_slice::<Self>(bytes) {
|
match serde_json::from_slice::<Self>(bytes) {
|
||||||
Ok(report) => Ok(report),
|
Ok(report) => Ok(report),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
use serde_json::error::Category;
|
|
||||||
match e.classify() {
|
match e.classify() {
|
||||||
Category::Io | Category::Eof | Category::Syntax => Err(e),
|
Category::Io | Category::Eof | Category::Syntax => Err(e),
|
||||||
Category::Data => {
|
Category::Data => {
|
||||||
@ -187,7 +173,8 @@ impl Report {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
fn is_sub_category(id: &str, parent: &str, sep: char) -> bool {
|
fn is_sub_category(id: &str, parent: &str, sep: char) -> bool {
|
||||||
id.starts_with(parent) && id.get(parent.len()..).is_some_and(|s| s.starts_with(sep))
|
id.starts_with(parent)
|
||||||
|
&& id.get(parent.len()..).map_or(false, |s| s.starts_with(sep))
|
||||||
}
|
}
|
||||||
let mut sub_categories = self
|
let mut sub_categories = self
|
||||||
.categories
|
.categories
|
||||||
@ -317,8 +304,7 @@ impl FromIterator<Measures> for Measures {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Older JSON report types
|
// Older JSON report types
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
|
||||||
struct LegacyReport {
|
struct LegacyReport {
|
||||||
fuzzy_match_percent: f32,
|
fuzzy_match_percent: f32,
|
||||||
total_code: u64,
|
total_code: u64,
|
||||||
@ -355,8 +341,7 @@ impl From<LegacyReport> for Report {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
|
||||||
struct LegacyReportUnit {
|
struct LegacyReportUnit {
|
||||||
name: String,
|
name: String,
|
||||||
fuzzy_match_percent: f32,
|
fuzzy_match_percent: f32,
|
||||||
@ -366,11 +351,11 @@ struct LegacyReportUnit {
|
|||||||
matched_data: u64,
|
matched_data: u64,
|
||||||
total_functions: u32,
|
total_functions: u32,
|
||||||
matched_functions: u32,
|
matched_functions: u32,
|
||||||
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
complete: Option<bool>,
|
complete: Option<bool>,
|
||||||
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
module_name: Option<String>,
|
module_name: Option<String>,
|
||||||
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
module_id: Option<u32>,
|
module_id: Option<u32>,
|
||||||
sections: Vec<LegacyReportItem>,
|
sections: Vec<LegacyReportItem>,
|
||||||
functions: Vec<LegacyReportItem>,
|
functions: Vec<LegacyReportItem>,
|
||||||
@ -404,20 +389,16 @@ impl From<LegacyReportUnit> for ReportUnit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
|
||||||
struct LegacyReportItem {
|
struct LegacyReportItem {
|
||||||
name: String,
|
name: String,
|
||||||
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
demangled_name: Option<String>,
|
demangled_name: Option<String>,
|
||||||
#[cfg_attr(
|
#[serde(
|
||||||
feature = "serde",
|
default,
|
||||||
serde(
|
skip_serializing_if = "Option::is_none",
|
||||||
default,
|
serialize_with = "serialize_hex",
|
||||||
skip_serializing_if = "Option::is_none",
|
deserialize_with = "deserialize_hex"
|
||||||
serialize_with = "serialize_hex",
|
|
||||||
deserialize_with = "deserialize_hex"
|
|
||||||
)
|
|
||||||
)]
|
)]
|
||||||
address: Option<u64>,
|
address: Option<u64>,
|
||||||
size: u64,
|
size: u64,
|
||||||
@ -434,18 +415,19 @@ impl From<LegacyReportItem> for ReportItem {
|
|||||||
demangled_name: value.demangled_name,
|
demangled_name: value.demangled_name,
|
||||||
virtual_address: value.address,
|
virtual_address: value.address,
|
||||||
}),
|
}),
|
||||||
address: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
fn serialize_hex<S>(x: &Option<u64>, s: S) -> Result<S::Ok, S::Error>
|
fn serialize_hex<S>(x: &Option<u64>, s: S) -> Result<S::Ok, S::Error>
|
||||||
where S: serde::Serializer {
|
where S: serde::Serializer {
|
||||||
if let Some(x) = x { 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")]
|
|
||||||
fn deserialize_hex<'de, D>(d: D) -> Result<Option<u64>, D::Error>
|
fn deserialize_hex<'de, D>(d: D) -> Result<Option<u64>, D::Error>
|
||||||
where D: serde::Deserializer<'de> {
|
where D: serde::Deserializer<'de> {
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
78
objdiff-core/src/bindings/wasm.rs
Normal file
78
objdiff-core/src/bindings/wasm.rs
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
use prost::Message;
|
||||||
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
|
use crate::{bindings::diff::DiffResult, diff, obj};
|
||||||
|
|
||||||
|
fn parse_object(
|
||||||
|
data: Option<Box<[u8]>>,
|
||||||
|
config: &diff::DiffObjConfig,
|
||||||
|
) -> Result<Option<obj::ObjInfo>, JsError> {
|
||||||
|
data.as_ref().map(|data| obj::read::parse(data, config)).transpose().to_js()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_and_run_diff(
|
||||||
|
left: Option<Box<[u8]>>,
|
||||||
|
right: Option<Box<[u8]>>,
|
||||||
|
config: diff::DiffObjConfig,
|
||||||
|
) -> Result<DiffResult, JsError> {
|
||||||
|
let target = parse_object(left, &config)?;
|
||||||
|
let base = parse_object(right, &config)?;
|
||||||
|
run_diff(target.as_ref(), base.as_ref(), config)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_diff(
|
||||||
|
left: Option<&obj::ObjInfo>,
|
||||||
|
right: Option<&obj::ObjInfo>,
|
||||||
|
config: diff::DiffObjConfig,
|
||||||
|
) -> Result<DiffResult, JsError> {
|
||||||
|
log::debug!("Running diff with config: {:?}", config);
|
||||||
|
let result = diff::diff_objs(&config, left, right, None).to_js()?;
|
||||||
|
let left = left.and_then(|o| result.left.as_ref().map(|d| (o, d)));
|
||||||
|
let right = right.and_then(|o| result.right.as_ref().map(|d| (o, d)));
|
||||||
|
Ok(DiffResult::new(left, right))
|
||||||
|
}
|
||||||
|
|
||||||
|
// #[wasm_bindgen]
|
||||||
|
// pub fn run_diff_json(
|
||||||
|
// left: Option<Box<[u8]>>,
|
||||||
|
// right: Option<Box<[u8]>>,
|
||||||
|
// config: diff::DiffObjConfig,
|
||||||
|
// ) -> Result<String, JsError> {
|
||||||
|
// let out = run_diff_opt_box(left, right, config)?;
|
||||||
|
// serde_json::to_string(&out).map_err(|e| JsError::new(&e.to_string()))
|
||||||
|
// }
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn run_diff_proto(
|
||||||
|
left: Option<Box<[u8]>>,
|
||||||
|
right: Option<Box<[u8]>>,
|
||||||
|
config: diff::DiffObjConfig,
|
||||||
|
) -> Result<Box<[u8]>, JsError> {
|
||||||
|
let out = parse_and_run_diff(left, right, config)?;
|
||||||
|
Ok(out.encode_to_vec().into_boxed_slice())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen(start)]
|
||||||
|
fn start() -> Result<(), JsError> {
|
||||||
|
console_error_panic_hook::set_once();
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
console_log::init_with_level(log::Level::Debug).to_js()?;
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
console_log::init_with_level(log::Level::Info).to_js()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn to_js_error(e: impl std::fmt::Display) -> JsError { JsError::new(&e.to_string()) }
|
||||||
|
|
||||||
|
trait ToJsResult {
|
||||||
|
type Output;
|
||||||
|
|
||||||
|
fn to_js(self) -> Result<Self::Output, JsError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, E: std::fmt::Display> ToJsResult for Result<T, E> {
|
||||||
|
type Output = T;
|
||||||
|
|
||||||
|
fn to_js(self) -> Result<T, JsError> { self.map_err(to_js_error) }
|
||||||
|
}
|
@ -1,104 +0,0 @@
|
|||||||
pub mod watcher;
|
|
||||||
|
|
||||||
use std::process::Command;
|
|
||||||
|
|
||||||
use typed_path::{Utf8PlatformPathBuf, Utf8UnixPath};
|
|
||||||
|
|
||||||
pub struct BuildStatus {
|
|
||||||
pub success: bool,
|
|
||||||
pub cmdline: String,
|
|
||||||
pub stdout: String,
|
|
||||||
pub stderr: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for BuildStatus {
|
|
||||||
fn default() -> Self {
|
|
||||||
BuildStatus {
|
|
||||||
success: true,
|
|
||||||
cmdline: String::new(),
|
|
||||||
stdout: String::new(),
|
|
||||||
stderr: String::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct BuildConfig {
|
|
||||||
pub project_dir: Option<Utf8PlatformPathBuf>,
|
|
||||||
pub custom_make: Option<String>,
|
|
||||||
pub custom_args: Option<Vec<String>>,
|
|
||||||
#[allow(unused)]
|
|
||||||
pub selected_wsl_distro: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn run_make(config: &BuildConfig, arg: &Utf8UnixPath) -> BuildStatus {
|
|
||||||
let Some(cwd) = &config.project_dir else {
|
|
||||||
return BuildStatus {
|
|
||||||
success: false,
|
|
||||||
stderr: "Missing project dir".to_string(),
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
};
|
|
||||||
let make = config.custom_make.as_deref().unwrap_or("make");
|
|
||||||
let make_args = config.custom_args.as_deref().unwrap_or(&[]);
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
let mut command = {
|
|
||||||
let mut command = Command::new(make);
|
|
||||||
command.current_dir(cwd).args(make_args).arg(arg);
|
|
||||||
command
|
|
||||||
};
|
|
||||||
#[cfg(windows)]
|
|
||||||
let mut command = {
|
|
||||||
use std::os::windows::process::CommandExt;
|
|
||||||
|
|
||||||
let mut command = if config.selected_wsl_distro.is_some() {
|
|
||||||
Command::new("wsl")
|
|
||||||
} else {
|
|
||||||
Command::new(make)
|
|
||||||
};
|
|
||||||
if let Some(distro) = &config.selected_wsl_distro {
|
|
||||||
// Strip distro root prefix \\wsl.localhost\{distro}
|
|
||||||
let wsl_path_prefix = format!("\\\\wsl.localhost\\{}", distro);
|
|
||||||
let cwd = match cwd.strip_prefix(wsl_path_prefix) {
|
|
||||||
Ok(new_cwd) => Utf8UnixPath::new("/").join(new_cwd.with_unix_encoding()),
|
|
||||||
Err(_) => cwd.with_unix_encoding(),
|
|
||||||
};
|
|
||||||
|
|
||||||
command
|
|
||||||
.arg("--cd")
|
|
||||||
.arg(cwd.as_str())
|
|
||||||
.arg("-d")
|
|
||||||
.arg(distro)
|
|
||||||
.arg("--")
|
|
||||||
.arg(make)
|
|
||||||
.args(make_args)
|
|
||||||
.arg(arg.as_str());
|
|
||||||
} else {
|
|
||||||
command.current_dir(cwd).args(make_args).arg(arg.as_str());
|
|
||||||
}
|
|
||||||
command.creation_flags(winapi::um::winbase::CREATE_NO_WINDOW);
|
|
||||||
command
|
|
||||||
};
|
|
||||||
let mut cmdline = shell_escape::escape(command.get_program().to_string_lossy()).into_owned();
|
|
||||||
for arg in command.get_args() {
|
|
||||||
cmdline.push(' ');
|
|
||||||
cmdline.push_str(shell_escape::escape(arg.to_string_lossy()).as_ref());
|
|
||||||
}
|
|
||||||
let output = match command.output() {
|
|
||||||
Ok(output) => output,
|
|
||||||
Err(e) => {
|
|
||||||
return BuildStatus {
|
|
||||||
success: false,
|
|
||||||
cmdline,
|
|
||||||
stdout: Default::default(),
|
|
||||||
stderr: e.to_string(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// Try from_utf8 first to avoid copying the buffer if it's valid, then fall back to from_utf8_lossy
|
|
||||||
let stdout = String::from_utf8(output.stdout)
|
|
||||||
.unwrap_or_else(|e| String::from_utf8_lossy(e.as_bytes()).into_owned());
|
|
||||||
let stderr = String::from_utf8(output.stderr)
|
|
||||||
.unwrap_or_else(|e| String::from_utf8_lossy(e.as_bytes()).into_owned());
|
|
||||||
BuildStatus { success: output.status.success(), cmdline, stdout, stderr }
|
|
||||||
}
|
|
@ -1,75 +0,0 @@
|
|||||||
use std::{
|
|
||||||
fs,
|
|
||||||
path::{Path, PathBuf},
|
|
||||||
sync::{
|
|
||||||
Arc,
|
|
||||||
atomic::{AtomicBool, Ordering},
|
|
||||||
},
|
|
||||||
task::Waker,
|
|
||||||
time::Duration,
|
|
||||||
};
|
|
||||||
|
|
||||||
use globset::GlobSet;
|
|
||||||
use notify::RecursiveMode;
|
|
||||||
use notify_debouncer_full::{DebounceEventResult, new_debouncer_opt};
|
|
||||||
|
|
||||||
pub type Watcher = notify_debouncer_full::Debouncer<
|
|
||||||
notify::RecommendedWatcher,
|
|
||||||
notify_debouncer_full::RecommendedCache,
|
|
||||||
>;
|
|
||||||
|
|
||||||
pub struct WatcherState {
|
|
||||||
pub config_path: Option<PathBuf>,
|
|
||||||
pub left_obj_path: Option<PathBuf>,
|
|
||||||
pub right_obj_path: Option<PathBuf>,
|
|
||||||
pub patterns: GlobSet,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_watcher(
|
|
||||||
modified: Arc<AtomicBool>,
|
|
||||||
project_dir: &Path,
|
|
||||||
patterns: GlobSet,
|
|
||||||
waker: Waker,
|
|
||||||
) -> notify::Result<Watcher> {
|
|
||||||
let base_dir = fs::canonicalize(project_dir)?;
|
|
||||||
let base_dir_clone = base_dir.clone();
|
|
||||||
let timeout = Duration::from_millis(200);
|
|
||||||
let config = notify::Config::default().with_poll_interval(Duration::from_secs(2));
|
|
||||||
let mut debouncer = new_debouncer_opt(
|
|
||||||
timeout,
|
|
||||||
None,
|
|
||||||
move |result: DebounceEventResult| match result {
|
|
||||||
Ok(events) => {
|
|
||||||
let mut any_match = false;
|
|
||||||
for event in events.iter() {
|
|
||||||
if !matches!(
|
|
||||||
event.kind,
|
|
||||||
notify::EventKind::Modify(..)
|
|
||||||
| notify::EventKind::Create(..)
|
|
||||||
| notify::EventKind::Remove(..)
|
|
||||||
) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for path in &event.paths {
|
|
||||||
let Ok(path) = path.strip_prefix(&base_dir_clone) else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
if patterns.is_match(path) {
|
|
||||||
// log::info!("File modified: {}", path.display());
|
|
||||||
any_match = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if any_match {
|
|
||||||
modified.store(true, Ordering::Relaxed);
|
|
||||||
waker.wake_by_ref();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(errors) => errors.iter().for_each(|e| log::error!("Watch error: {e:?}")),
|
|
||||||
},
|
|
||||||
notify_debouncer_full::RecommendedCache::new(),
|
|
||||||
config,
|
|
||||||
)?;
|
|
||||||
debouncer.watch(base_dir, RecursiveMode::Recursive)?;
|
|
||||||
Ok(debouncer)
|
|
||||||
}
|
|
@ -1,132 +1,81 @@
|
|||||||
pub mod path;
|
use std::{
|
||||||
|
fs::File,
|
||||||
use alloc::{
|
io::{BufReader, Read},
|
||||||
collections::BTreeMap,
|
path::{Path, PathBuf},
|
||||||
string::{String, ToString},
|
|
||||||
vec::Vec,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::{Context, Result, anyhow};
|
use anyhow::{anyhow, Context, Result};
|
||||||
|
use filetime::FileTime;
|
||||||
use globset::{Glob, GlobSet, GlobSetBuilder};
|
use globset::{Glob, GlobSet, GlobSetBuilder};
|
||||||
use path::unix_path_serde_option;
|
|
||||||
use typed_path::Utf8UnixPathBuf;
|
|
||||||
|
|
||||||
#[derive(Default, Clone)]
|
#[inline]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(default))]
|
fn bool_true() -> bool { true }
|
||||||
|
|
||||||
|
#[derive(Default, Clone, serde::Deserialize)]
|
||||||
pub struct ProjectConfig {
|
pub struct ProjectConfig {
|
||||||
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
#[serde(default)]
|
||||||
pub min_version: Option<String>,
|
pub min_version: Option<String>,
|
||||||
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
#[serde(default)]
|
||||||
pub custom_make: Option<String>,
|
pub custom_make: Option<String>,
|
||||||
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
#[serde(default)]
|
||||||
pub custom_args: Option<Vec<String>>,
|
pub custom_args: Option<Vec<String>>,
|
||||||
#[cfg_attr(
|
#[serde(default)]
|
||||||
feature = "serde",
|
pub target_dir: Option<PathBuf>,
|
||||||
serde(with = "unix_path_serde_option", skip_serializing_if = "Option::is_none")
|
#[serde(default)]
|
||||||
)]
|
pub base_dir: Option<PathBuf>,
|
||||||
pub target_dir: Option<Utf8UnixPathBuf>,
|
#[serde(default = "bool_true")]
|
||||||
#[cfg_attr(
|
pub build_base: bool,
|
||||||
feature = "serde",
|
#[serde(default)]
|
||||||
serde(with = "unix_path_serde_option", skip_serializing_if = "Option::is_none")
|
pub build_target: bool,
|
||||||
)]
|
#[serde(default)]
|
||||||
pub base_dir: Option<Utf8UnixPathBuf>,
|
pub watch_patterns: Option<Vec<Glob>>,
|
||||||
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
#[serde(default, alias = "units")]
|
||||||
pub build_base: Option<bool>,
|
pub objects: Vec<ProjectObject>,
|
||||||
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
#[serde(default)]
|
||||||
pub build_target: Option<bool>,
|
pub progress_categories: Vec<ProjectProgressCategory>,
|
||||||
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
|
||||||
pub watch_patterns: Option<Vec<String>>,
|
|
||||||
#[cfg_attr(
|
|
||||||
feature = "serde",
|
|
||||||
serde(alias = "objects", skip_serializing_if = "Option::is_none")
|
|
||||||
)]
|
|
||||||
pub units: Option<Vec<ProjectObject>>,
|
|
||||||
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
|
||||||
pub progress_categories: Option<Vec<ProjectProgressCategory>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ProjectConfig {
|
#[derive(Default, Clone, serde::Deserialize)]
|
||||||
#[inline]
|
|
||||||
pub fn units(&self) -> &[ProjectObject] { self.units.as_deref().unwrap_or_default() }
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn progress_categories(&self) -> &[ProjectProgressCategory] {
|
|
||||||
self.progress_categories.as_deref().unwrap_or_default()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn progress_categories_mut(&mut self) -> &mut Vec<ProjectProgressCategory> {
|
|
||||||
self.progress_categories.get_or_insert_with(Vec::new)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn build_watch_patterns(&self) -> Result<Vec<Glob>, globset::Error> {
|
|
||||||
Ok(if let Some(watch_patterns) = &self.watch_patterns {
|
|
||||||
watch_patterns
|
|
||||||
.iter()
|
|
||||||
.map(|s| Glob::new(s))
|
|
||||||
.collect::<Result<Vec<Glob>, globset::Error>>()?
|
|
||||||
} else {
|
|
||||||
DEFAULT_WATCH_PATTERNS.iter().map(|s| Glob::new(s).unwrap()).collect()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Clone)]
|
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(default))]
|
|
||||||
pub struct ProjectObject {
|
pub struct ProjectObject {
|
||||||
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
#[serde(default)]
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
#[cfg_attr(
|
#[serde(default)]
|
||||||
feature = "serde",
|
pub path: Option<PathBuf>,
|
||||||
serde(with = "unix_path_serde_option", skip_serializing_if = "Option::is_none")
|
#[serde(default)]
|
||||||
)]
|
pub target_path: Option<PathBuf>,
|
||||||
pub path: Option<Utf8UnixPathBuf>,
|
#[serde(default)]
|
||||||
#[cfg_attr(
|
pub base_path: Option<PathBuf>,
|
||||||
feature = "serde",
|
#[serde(default)]
|
||||||
serde(with = "unix_path_serde_option", skip_serializing_if = "Option::is_none")
|
|
||||||
)]
|
|
||||||
pub target_path: Option<Utf8UnixPathBuf>,
|
|
||||||
#[cfg_attr(
|
|
||||||
feature = "serde",
|
|
||||||
serde(with = "unix_path_serde_option", skip_serializing_if = "Option::is_none")
|
|
||||||
)]
|
|
||||||
pub base_path: Option<Utf8UnixPathBuf>,
|
|
||||||
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
|
||||||
#[deprecated(note = "Use metadata.reverse_fn_order")]
|
#[deprecated(note = "Use metadata.reverse_fn_order")]
|
||||||
pub reverse_fn_order: Option<bool>,
|
pub reverse_fn_order: Option<bool>,
|
||||||
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
#[serde(default)]
|
||||||
#[deprecated(note = "Use metadata.complete")]
|
#[deprecated(note = "Use metadata.complete")]
|
||||||
pub complete: Option<bool>,
|
pub complete: Option<bool>,
|
||||||
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
#[serde(default)]
|
||||||
pub scratch: Option<ScratchConfig>,
|
pub scratch: Option<ScratchConfig>,
|
||||||
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
#[serde(default)]
|
||||||
pub metadata: Option<ProjectObjectMetadata>,
|
pub metadata: Option<ProjectObjectMetadata>,
|
||||||
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
|
||||||
pub symbol_mappings: Option<BTreeMap<String, String>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Clone)]
|
#[derive(Default, Clone, serde::Deserialize)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(default))]
|
|
||||||
pub struct ProjectObjectMetadata {
|
pub struct ProjectObjectMetadata {
|
||||||
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
#[serde(default)]
|
||||||
pub complete: Option<bool>,
|
pub complete: Option<bool>,
|
||||||
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
#[serde(default)]
|
||||||
pub reverse_fn_order: Option<bool>,
|
pub reverse_fn_order: Option<bool>,
|
||||||
#[cfg_attr(
|
#[serde(default)]
|
||||||
feature = "serde",
|
pub source_path: Option<String>,
|
||||||
serde(with = "unix_path_serde_option", skip_serializing_if = "Option::is_none")
|
#[serde(default)]
|
||||||
)]
|
|
||||||
pub source_path: Option<Utf8UnixPathBuf>,
|
|
||||||
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
|
||||||
pub progress_categories: Option<Vec<String>>,
|
pub progress_categories: Option<Vec<String>>,
|
||||||
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
#[serde(default)]
|
||||||
pub auto_generated: Option<bool>,
|
pub auto_generated: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Clone)]
|
#[derive(Default, Clone, serde::Deserialize)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(default))]
|
|
||||||
pub struct ProjectProgressCategory {
|
pub struct ProjectProgressCategory {
|
||||||
|
#[serde(default)]
|
||||||
pub id: String,
|
pub id: String,
|
||||||
|
#[serde(default)]
|
||||||
pub name: String,
|
pub name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,19 +84,40 @@ impl ProjectObject {
|
|||||||
if let Some(name) = &self.name {
|
if let Some(name) = &self.name {
|
||||||
name
|
name
|
||||||
} else if let Some(path) = &self.path {
|
} else if let Some(path) = &self.path {
|
||||||
path.as_str()
|
path.to_str().unwrap_or("[invalid path]")
|
||||||
} else {
|
} else {
|
||||||
"[unknown]"
|
"[unknown]"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn resolve_paths(
|
||||||
|
&mut self,
|
||||||
|
project_dir: &Path,
|
||||||
|
target_obj_dir: Option<&Path>,
|
||||||
|
base_obj_dir: Option<&Path>,
|
||||||
|
) {
|
||||||
|
if let (Some(target_obj_dir), Some(path), None) =
|
||||||
|
(target_obj_dir, &self.path, &self.target_path)
|
||||||
|
{
|
||||||
|
self.target_path = Some(target_obj_dir.join(path));
|
||||||
|
} else if let Some(path) = &self.target_path {
|
||||||
|
self.target_path = Some(project_dir.join(path));
|
||||||
|
}
|
||||||
|
if let (Some(base_obj_dir), Some(path), None) = (base_obj_dir, &self.path, &self.base_path)
|
||||||
|
{
|
||||||
|
self.base_path = Some(base_obj_dir.join(path));
|
||||||
|
} else if let Some(path) = &self.base_path {
|
||||||
|
self.base_path = Some(project_dir.join(path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn complete(&self) -> Option<bool> {
|
pub fn complete(&self) -> Option<bool> {
|
||||||
#[expect(deprecated)]
|
#[allow(deprecated)]
|
||||||
self.metadata.as_ref().and_then(|m| m.complete).or(self.complete)
|
self.metadata.as_ref().and_then(|m| m.complete).or(self.complete)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reverse_fn_order(&self) -> Option<bool> {
|
pub fn reverse_fn_order(&self) -> Option<bool> {
|
||||||
#[expect(deprecated)]
|
#[allow(deprecated)]
|
||||||
self.metadata.as_ref().and_then(|m| m.reverse_fn_order).or(self.reverse_fn_order)
|
self.metadata.as_ref().and_then(|m| m.reverse_fn_order).or(self.reverse_fn_order)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,37 +125,23 @@ impl ProjectObject {
|
|||||||
self.metadata.as_ref().and_then(|m| m.auto_generated).unwrap_or(false)
|
self.metadata.as_ref().and_then(|m| m.auto_generated).unwrap_or(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn source_path(&self) -> Option<&Utf8UnixPathBuf> {
|
pub fn source_path(&self) -> Option<&String> {
|
||||||
self.metadata.as_ref().and_then(|m| m.source_path.as_ref())
|
self.metadata.as_ref().and_then(|m| m.source_path.as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn progress_categories(&self) -> &[String] {
|
|
||||||
self.metadata.as_ref().and_then(|m| m.progress_categories.as_deref()).unwrap_or_default()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn auto_generated(&self) -> Option<bool> {
|
|
||||||
self.metadata.as_ref().and_then(|m| m.auto_generated)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Clone, Eq, PartialEq)]
|
#[derive(Default, Clone, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize), serde(default))]
|
|
||||||
pub struct ScratchConfig {
|
pub struct ScratchConfig {
|
||||||
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
#[serde(default)]
|
||||||
pub platform: Option<String>,
|
pub platform: Option<String>,
|
||||||
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
#[serde(default)]
|
||||||
pub compiler: Option<String>,
|
pub compiler: Option<String>,
|
||||||
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
#[serde(default)]
|
||||||
pub c_flags: Option<String>,
|
pub c_flags: Option<String>,
|
||||||
#[cfg_attr(
|
#[serde(default)]
|
||||||
feature = "serde",
|
pub ctx_path: Option<PathBuf>,
|
||||||
serde(with = "unix_path_serde_option", skip_serializing_if = "Option::is_none")
|
#[serde(default)]
|
||||||
)]
|
pub build_ctx: bool,
|
||||||
pub ctx_path: Option<Utf8UnixPathBuf>,
|
|
||||||
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
|
||||||
pub build_ctx: Option<bool>,
|
|
||||||
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
|
||||||
pub preset_id: Option<u32>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const CONFIG_FILENAMES: [&str; 3] = ["objdiff.json", "objdiff.yml", "objdiff.yaml"];
|
pub const CONFIG_FILENAMES: [&str; 3] = ["objdiff.json", "objdiff.yml", "objdiff.yaml"];
|
||||||
@ -195,24 +151,16 @@ pub const DEFAULT_WATCH_PATTERNS: &[&str] = &[
|
|||||||
"*.inc", "*.py", "*.yml", "*.txt", "*.json",
|
"*.inc", "*.py", "*.yml", "*.txt", "*.json",
|
||||||
];
|
];
|
||||||
|
|
||||||
pub fn default_watch_patterns() -> Vec<Glob> {
|
|
||||||
DEFAULT_WATCH_PATTERNS.iter().map(|s| Glob::new(s).unwrap()).collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
#[derive(Clone, Eq, PartialEq)]
|
#[derive(Clone, Eq, PartialEq)]
|
||||||
pub struct ProjectConfigInfo {
|
pub struct ProjectConfigInfo {
|
||||||
pub path: std::path::PathBuf,
|
pub path: PathBuf,
|
||||||
pub timestamp: Option<filetime::FileTime>,
|
pub timestamp: FileTime,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
pub fn try_project_config(dir: &Path) -> Option<(Result<ProjectConfig>, ProjectConfigInfo)> {
|
||||||
pub fn try_project_config(
|
|
||||||
dir: &std::path::Path,
|
|
||||||
) -> Option<(Result<ProjectConfig>, ProjectConfigInfo)> {
|
|
||||||
for filename in CONFIG_FILENAMES.iter() {
|
for filename in CONFIG_FILENAMES.iter() {
|
||||||
let config_path = dir.join(filename);
|
let config_path = dir.join(filename);
|
||||||
let Ok(file) = std::fs::File::open(&config_path) else {
|
let Ok(file) = File::open(&config_path) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
let metadata = file.metadata();
|
let metadata = file.metadata();
|
||||||
@ -220,57 +168,29 @@ pub fn try_project_config(
|
|||||||
if !metadata.is_file() {
|
if !metadata.is_file() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let ts = filetime::FileTime::from_last_modification_time(&metadata);
|
let ts = FileTime::from_last_modification_time(&metadata);
|
||||||
let mut reader = std::io::BufReader::new(file);
|
let mut reader = BufReader::new(file);
|
||||||
let mut result = read_json_config(&mut reader);
|
let mut result = match filename.contains("json") {
|
||||||
|
true => read_json_config(&mut reader),
|
||||||
|
false => read_yml_config(&mut reader),
|
||||||
|
};
|
||||||
if let Ok(config) = &result {
|
if let Ok(config) = &result {
|
||||||
// Validate min_version if present
|
// Validate min_version if present
|
||||||
if let Err(e) = validate_min_version(config) {
|
if let Err(e) = validate_min_version(config) {
|
||||||
result = Err(e);
|
result = Err(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Some((result, ProjectConfigInfo { path: config_path, timestamp: Some(ts) }));
|
return Some((result, ProjectConfigInfo { path: config_path, timestamp: ts }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
pub fn save_project_config(
|
|
||||||
config: &ProjectConfig,
|
|
||||||
info: &ProjectConfigInfo,
|
|
||||||
) -> Result<ProjectConfigInfo> {
|
|
||||||
if let Some(last_ts) = info.timestamp {
|
|
||||||
// Check if the file has changed since we last read it
|
|
||||||
if let Ok(metadata) = std::fs::metadata(&info.path) {
|
|
||||||
let ts = filetime::FileTime::from_last_modification_time(&metadata);
|
|
||||||
if ts != last_ts {
|
|
||||||
return Err(anyhow!("Config file has changed since last read"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let mut writer = std::io::BufWriter::new(
|
|
||||||
std::fs::File::create(&info.path).context("Failed to create config file")?,
|
|
||||||
);
|
|
||||||
let ext = info.path.extension().and_then(|ext| ext.to_str()).unwrap_or("json");
|
|
||||||
match ext {
|
|
||||||
"json" => serde_json::to_writer_pretty(&mut writer, config).context("Failed to write JSON"),
|
|
||||||
_ => Err(anyhow!("Unknown config file extension: {ext}")),
|
|
||||||
}?;
|
|
||||||
let file = writer.into_inner().context("Failed to flush file")?;
|
|
||||||
let metadata = file.metadata().context("Failed to get file metadata")?;
|
|
||||||
let ts = filetime::FileTime::from_last_modification_time(&metadata);
|
|
||||||
Ok(ProjectConfigInfo { path: info.path.clone(), timestamp: Some(ts) })
|
|
||||||
}
|
|
||||||
|
|
||||||
fn validate_min_version(config: &ProjectConfig) -> Result<()> {
|
fn validate_min_version(config: &ProjectConfig) -> Result<()> {
|
||||||
let Some(min_version) = &config.min_version else { return Ok(()) };
|
let Some(min_version) = &config.min_version else { return Ok(()) };
|
||||||
let version = semver::Version::parse(env!("CARGO_PKG_VERSION"))
|
let version = semver::Version::parse(env!("CARGO_PKG_VERSION"))
|
||||||
.map_err(|e| anyhow::Error::msg(e.to_string()))
|
|
||||||
.context("Failed to parse package version")?;
|
.context("Failed to parse package version")?;
|
||||||
let min_version = semver::Version::parse(min_version)
|
let min_version = semver::Version::parse(min_version).context("Failed to parse min_version")?;
|
||||||
.map_err(|e| anyhow::Error::msg(e.to_string()))
|
|
||||||
.context("Failed to parse min_version")?;
|
|
||||||
if version >= min_version {
|
if version >= min_version {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
@ -278,12 +198,15 @@ fn validate_min_version(config: &ProjectConfig) -> Result<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
fn read_yml_config<R: Read>(reader: &mut R) -> Result<ProjectConfig> {
|
||||||
fn read_json_config<R: std::io::Read>(reader: &mut R) -> Result<ProjectConfig> {
|
Ok(serde_yaml::from_reader(reader)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_json_config<R: Read>(reader: &mut R) -> Result<ProjectConfig> {
|
||||||
Ok(serde_json::from_reader(reader)?)
|
Ok(serde_json::from_reader(reader)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_globset(vec: &[Glob]) -> Result<GlobSet, globset::Error> {
|
pub fn build_globset(vec: &[Glob]) -> std::result::Result<GlobSet, globset::Error> {
|
||||||
let mut builder = GlobSetBuilder::new();
|
let mut builder = GlobSetBuilder::new();
|
||||||
for glob in vec {
|
for glob in vec {
|
||||||
builder.add(glob.clone());
|
builder.add(glob.clone());
|
||||||
|
@ -1,57 +0,0 @@
|
|||||||
// For argp::FromArgs
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
pub fn platform_path(value: &str) -> Result<typed_path::Utf8PlatformPathBuf, String> {
|
|
||||||
Ok(typed_path::Utf8PlatformPathBuf::from(value))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks if the path is valid UTF-8 and returns it as a [`Utf8PlatformPath`].
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
pub fn check_path(
|
|
||||||
path: &std::path::Path,
|
|
||||||
) -> Result<&typed_path::Utf8PlatformPath, core::str::Utf8Error> {
|
|
||||||
typed_path::Utf8PlatformPath::from_bytes_path(typed_path::PlatformPath::new(
|
|
||||||
path.as_os_str().as_encoded_bytes(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks if the path is valid UTF-8 and returns it as a [`Utf8NativePathBuf`].
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
pub fn check_path_buf(
|
|
||||||
path: std::path::PathBuf,
|
|
||||||
) -> Result<typed_path::Utf8PlatformPathBuf, alloc::string::FromUtf8Error> {
|
|
||||||
typed_path::Utf8PlatformPathBuf::from_bytes_path_buf(typed_path::PlatformPathBuf::from(
|
|
||||||
path.into_os_string().into_encoded_bytes(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
pub mod unix_path_serde_option {
|
|
||||||
use serde::{Deserialize, Deserializer, Serializer};
|
|
||||||
use typed_path::Utf8UnixPathBuf;
|
|
||||||
|
|
||||||
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() }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Utf8UnixPathBuf>, D::Error>
|
|
||||||
where D: Deserializer<'de> {
|
|
||||||
Ok(Option::<String>::deserialize(deserializer)?.map(Utf8UnixPathBuf::from))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(feature = "serde", feature = "std"))]
|
|
||||||
pub mod platform_path_serde_option {
|
|
||||||
use serde::{Deserialize, Deserializer, Serializer};
|
|
||||||
use typed_path::Utf8PlatformPathBuf;
|
|
||||||
|
|
||||||
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() }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Utf8PlatformPathBuf>, D::Error>
|
|
||||||
where D: Deserializer<'de> {
|
|
||||||
Ok(Option::<String>::deserialize(deserializer)?.map(Utf8PlatformPathBuf::from))
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,503 +1,334 @@
|
|||||||
use alloc::{
|
use std::{cmp::max, collections::BTreeMap};
|
||||||
collections::{BTreeMap, btree_map},
|
|
||||||
string::{String, ToString},
|
use anyhow::{anyhow, Result};
|
||||||
vec,
|
use similar::{capture_diff_slices_deadline, Algorithm};
|
||||||
vec::Vec,
|
|
||||||
|
use crate::{
|
||||||
|
arch::ProcessCodeResult,
|
||||||
|
diff::{
|
||||||
|
DiffObjConfig, ObjInsArgDiff, ObjInsBranchFrom, ObjInsBranchTo, ObjInsDiff, ObjInsDiffKind,
|
||||||
|
ObjSymbolDiff,
|
||||||
|
},
|
||||||
|
obj::{ObjInfo, ObjInsArg, ObjReloc, ObjSymbol, ObjSymbolFlags, SymbolRef},
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::{Context, Result, anyhow, ensure};
|
pub fn process_code_symbol(
|
||||||
|
obj: &ObjInfo,
|
||||||
use super::{
|
symbol_ref: SymbolRef,
|
||||||
DiffObjConfig, FunctionRelocDiffs, InstructionArgDiffIndex, InstructionBranchFrom,
|
config: &DiffObjConfig,
|
||||||
InstructionBranchTo, InstructionDiffKind, InstructionDiffRow, SymbolDiff,
|
) -> Result<ProcessCodeResult> {
|
||||||
display::display_ins_data_literals,
|
let (section, symbol) = obj.section_symbol(symbol_ref);
|
||||||
};
|
let section = section.ok_or_else(|| anyhow!("Code symbol section not found"))?;
|
||||||
use crate::obj::{
|
let code = §ion.data
|
||||||
InstructionArg, InstructionArgValue, InstructionRef, Object, ResolvedInstructionRef,
|
[symbol.section_address as usize..(symbol.section_address + symbol.size) as usize];
|
||||||
ResolvedRelocation, ResolvedSymbol, SymbolFlag, SymbolKind,
|
obj.arch.process_code(
|
||||||
};
|
symbol.address,
|
||||||
|
code,
|
||||||
pub fn no_diff_code(
|
section.orig_index,
|
||||||
obj: &Object,
|
§ion.relocations,
|
||||||
symbol_index: usize,
|
§ion.line_info,
|
||||||
diff_config: &DiffObjConfig,
|
config,
|
||||||
) -> Result<SymbolDiff> {
|
)
|
||||||
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(|| {
|
|
||||||
anyhow!(
|
|
||||||
"Symbol data out of bounds: {:#x}..{:#x}",
|
|
||||||
symbol.address,
|
|
||||||
symbol.address + symbol.size
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
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), ..Default::default() });
|
|
||||||
}
|
|
||||||
resolve_branches(&ops, &mut instruction_rows);
|
|
||||||
Ok(SymbolDiff { target_symbol: None, match_percent: None, diff_score: None, instruction_rows })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const PENALTY_IMM_DIFF: u64 = 1;
|
pub fn no_diff_code(out: &ProcessCodeResult, symbol_ref: SymbolRef) -> Result<ObjSymbolDiff> {
|
||||||
const PENALTY_REG_DIFF: u64 = 5;
|
let mut diff = Vec::<ObjInsDiff>::new();
|
||||||
const PENALTY_REPLACE: u64 = 60;
|
for i in &out.insts {
|
||||||
const PENALTY_INSERT_DELETE: u64 = 100;
|
diff.push(ObjInsDiff {
|
||||||
|
ins: Some(i.clone()),
|
||||||
|
kind: ObjInsDiffKind::None,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
resolve_branches(&mut diff);
|
||||||
|
Ok(ObjSymbolDiff { symbol_ref, diff_symbol: None, instructions: diff, match_percent: None })
|
||||||
|
}
|
||||||
|
|
||||||
pub fn diff_code(
|
pub fn diff_code(
|
||||||
left_obj: &Object,
|
left_out: &ProcessCodeResult,
|
||||||
right_obj: &Object,
|
right_out: &ProcessCodeResult,
|
||||||
left_symbol_idx: usize,
|
left_symbol_ref: SymbolRef,
|
||||||
right_symbol_idx: usize,
|
right_symbol_ref: SymbolRef,
|
||||||
diff_config: &DiffObjConfig,
|
config: &DiffObjConfig,
|
||||||
) -> Result<(SymbolDiff, SymbolDiff)> {
|
) -> Result<(ObjSymbolDiff, ObjSymbolDiff)> {
|
||||||
let left_symbol = &left_obj.symbols[left_symbol_idx];
|
let mut left_diff = Vec::<ObjInsDiff>::new();
|
||||||
let right_symbol = &right_obj.symbols[right_symbol_idx];
|
let mut right_diff = Vec::<ObjInsDiff>::new();
|
||||||
let left_section = left_symbol
|
diff_instructions(&mut left_diff, &mut right_diff, left_out, right_out)?;
|
||||||
.section
|
|
||||||
.and_then(|i| left_obj.sections.get(i))
|
|
||||||
.ok_or_else(|| anyhow!("Missing section for symbol"))?;
|
|
||||||
let right_section = right_symbol
|
|
||||||
.section
|
|
||||||
.and_then(|i| right_obj.sections.get(i))
|
|
||||||
.ok_or_else(|| anyhow!("Missing section for symbol"))?;
|
|
||||||
let left_data = left_section
|
|
||||||
.data_range(left_symbol.address, left_symbol.size as usize)
|
|
||||||
.ok_or_else(|| {
|
|
||||||
anyhow!(
|
|
||||||
"Symbol data out of bounds: {:#x}..{:#x}",
|
|
||||||
left_symbol.address,
|
|
||||||
left_symbol.address + left_symbol.size
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
let right_data = right_section
|
|
||||||
.data_range(right_symbol.address, right_symbol.size as usize)
|
|
||||||
.ok_or_else(|| {
|
|
||||||
anyhow!(
|
|
||||||
"Symbol data out of bounds: {:#x}..{:#x}",
|
|
||||||
right_symbol.address,
|
|
||||||
right_symbol.address + right_symbol.size
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let left_section_idx = left_symbol.section.unwrap();
|
resolve_branches(&mut left_diff);
|
||||||
let right_section_idx = right_symbol.section.unwrap();
|
resolve_branches(&mut right_diff);
|
||||||
let left_ops = left_obj.arch.scan_instructions(
|
|
||||||
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 = 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_ops, &mut left_rows);
|
|
||||||
resolve_branches(&right_ops, &mut right_rows);
|
|
||||||
|
|
||||||
let mut diff_state = InstructionDiffState::default();
|
let mut diff_state = InsDiffState::default();
|
||||||
for (left_row, right_row) in left_rows.iter_mut().zip(right_rows.iter_mut()) {
|
for (left, right) in left_diff.iter_mut().zip(right_diff.iter_mut()) {
|
||||||
let result = diff_instruction(
|
let result = compare_ins(config, left, right, &mut diff_state)?;
|
||||||
left_obj,
|
left.kind = result.kind;
|
||||||
right_obj,
|
right.kind = result.kind;
|
||||||
left_symbol_idx,
|
left.arg_diff = result.left_args_diff;
|
||||||
right_symbol_idx,
|
right.arg_diff = result.right_args_diff;
|
||||||
left_row.ins_ref,
|
|
||||||
right_row.ins_ref,
|
|
||||||
left_row,
|
|
||||||
right_row,
|
|
||||||
diff_config,
|
|
||||||
&mut diff_state,
|
|
||||||
)?;
|
|
||||||
left_row.kind = result.kind;
|
|
||||||
right_row.kind = result.kind;
|
|
||||||
left_row.arg_diff = result.left_args_diff;
|
|
||||||
right_row.arg_diff = result.right_args_diff;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let max_score = left_ops.len() as u64 * PENALTY_INSERT_DELETE;
|
let total = left_out.insts.len();
|
||||||
let diff_score = diff_state.diff_score.min(max_score);
|
let percent = if diff_state.diff_count >= total {
|
||||||
let match_percent = if max_score == 0 {
|
0.0
|
||||||
100.0
|
|
||||||
} else {
|
} else {
|
||||||
((1.0 - (diff_score as f64 / max_score as f64)) * 100.0) as f32
|
((total - diff_state.diff_count) as f32 / total as f32) * 100.0
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
SymbolDiff {
|
ObjSymbolDiff {
|
||||||
target_symbol: Some(right_symbol_idx),
|
symbol_ref: left_symbol_ref,
|
||||||
match_percent: Some(match_percent),
|
diff_symbol: Some(right_symbol_ref),
|
||||||
diff_score: Some((diff_score, max_score)),
|
instructions: left_diff,
|
||||||
instruction_rows: left_rows,
|
match_percent: Some(percent),
|
||||||
},
|
},
|
||||||
SymbolDiff {
|
ObjSymbolDiff {
|
||||||
target_symbol: Some(left_symbol_idx),
|
symbol_ref: right_symbol_ref,
|
||||||
match_percent: Some(match_percent),
|
diff_symbol: Some(left_symbol_ref),
|
||||||
diff_score: Some((diff_score, max_score)),
|
instructions: right_diff,
|
||||||
instruction_rows: right_rows,
|
match_percent: Some(percent),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn diff_instructions(
|
fn diff_instructions(
|
||||||
left_insts: &[InstructionRef],
|
left_diff: &mut Vec<ObjInsDiff>,
|
||||||
right_insts: &[InstructionRef],
|
right_diff: &mut Vec<ObjInsDiff>,
|
||||||
) -> Result<(Vec<InstructionDiffRow>, Vec<InstructionDiffRow>)> {
|
left_code: &ProcessCodeResult,
|
||||||
let left_ops = left_insts.iter().map(|i| i.opcode).collect::<Vec<_>>();
|
right_code: &ProcessCodeResult,
|
||||||
let right_ops = right_insts.iter().map(|i| i.opcode).collect::<Vec<_>>();
|
) -> Result<()> {
|
||||||
let ops = similar::capture_diff_slices(similar::Algorithm::Patience, &left_ops, &right_ops);
|
let ops =
|
||||||
|
capture_diff_slices_deadline(Algorithm::Patience, &left_code.ops, &right_code.ops, None);
|
||||||
if ops.is_empty() {
|
if ops.is_empty() {
|
||||||
ensure!(left_insts.len() == right_insts.len());
|
|
||||||
let left_diff = left_insts
|
|
||||||
.iter()
|
|
||||||
.map(|i| InstructionDiffRow { ins_ref: Some(*i), ..Default::default() })
|
|
||||||
.collect();
|
|
||||||
let right_diff = right_insts
|
|
||||||
.iter()
|
|
||||||
.map(|i| InstructionDiffRow { ins_ref: Some(*i), ..Default::default() })
|
|
||||||
.collect();
|
|
||||||
return Ok((left_diff, right_diff));
|
|
||||||
}
|
|
||||||
|
|
||||||
let row_count = ops
|
|
||||||
.iter()
|
|
||||||
.map(|op| match *op {
|
|
||||||
similar::DiffOp::Equal { len, .. } => len,
|
|
||||||
similar::DiffOp::Delete { old_len, .. } => old_len,
|
|
||||||
similar::DiffOp::Insert { new_len, .. } => new_len,
|
|
||||||
similar::DiffOp::Replace { old_len, new_len, .. } => old_len.max(new_len),
|
|
||||||
})
|
|
||||||
.sum();
|
|
||||||
let mut left_diff = Vec::<InstructionDiffRow>::with_capacity(row_count);
|
|
||||||
let mut right_diff = Vec::<InstructionDiffRow>::with_capacity(row_count);
|
|
||||||
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_diff.extend(
|
||||||
left_range
|
left_code
|
||||||
.clone()
|
.insts
|
||||||
.map(|i| InstructionDiffRow { ins_ref: Some(left_insts[i]), ..Default::default() }),
|
.iter()
|
||||||
|
.map(|i| ObjInsDiff { ins: Some(i.clone()), ..Default::default() }),
|
||||||
);
|
);
|
||||||
right_diff.extend(
|
right_diff.extend(
|
||||||
right_range.clone().map(|i| InstructionDiffRow {
|
right_code
|
||||||
ins_ref: Some(right_insts[i]),
|
.insts
|
||||||
..Default::default()
|
.iter()
|
||||||
}),
|
.map(|i| ObjInsDiff { ins: Some(i.clone()), ..Default::default() }),
|
||||||
|
);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
for op in ops {
|
||||||
|
let (_tag, left_range, right_range) = op.as_tag_tuple();
|
||||||
|
let len = max(left_range.len(), right_range.len());
|
||||||
|
left_diff.extend(
|
||||||
|
left_code.insts[left_range.clone()]
|
||||||
|
.iter()
|
||||||
|
.map(|i| ObjInsDiff { ins: Some(i.clone()), ..Default::default() }),
|
||||||
|
);
|
||||||
|
right_diff.extend(
|
||||||
|
right_code.insts[right_range.clone()]
|
||||||
|
.iter()
|
||||||
|
.map(|i| ObjInsDiff { ins: Some(i.clone()), ..Default::default() }),
|
||||||
);
|
);
|
||||||
if left_range.len() < len {
|
if left_range.len() < len {
|
||||||
left_diff.extend((left_range.len()..len).map(|_| InstructionDiffRow::default()));
|
left_diff.extend((left_range.len()..len).map(|_| ObjInsDiff::default()));
|
||||||
}
|
}
|
||||||
if right_range.len() < len {
|
if right_range.len() < len {
|
||||||
right_diff.extend((right_range.len()..len).map(|_| InstructionDiffRow::default()));
|
right_diff.extend((right_range.len()..len).map(|_| ObjInsDiff::default()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok((left_diff, right_diff))
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn arg_to_string(arg: &InstructionArg, reloc: Option<ResolvedRelocation>) -> String {
|
fn resolve_branches(vec: &mut [ObjInsDiff]) {
|
||||||
match arg {
|
let mut branch_idx = 0usize;
|
||||||
InstructionArg::Value(arg) => arg.to_string(),
|
|
||||||
InstructionArg::Reloc => {
|
|
||||||
reloc.as_ref().map_or_else(|| "<unknown>".to_string(), |r| r.symbol.name.clone())
|
|
||||||
}
|
|
||||||
InstructionArg::BranchDest(arg) => arg.to_string(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resolve_branches(ops: &[InstructionRef], rows: &mut [InstructionDiffRow]) {
|
|
||||||
let mut branch_idx = 0u32;
|
|
||||||
// Map addresses to indices
|
// Map addresses to indices
|
||||||
let mut addr_map = BTreeMap::<u64, u32>::new();
|
let mut addr_map = BTreeMap::<u64, usize>::new();
|
||||||
for (i, ins_diff) in rows.iter().enumerate() {
|
for (i, ins_diff) in vec.iter().enumerate() {
|
||||||
if let Some(ins) = ins_diff.ins_ref {
|
if let Some(ins) = &ins_diff.ins {
|
||||||
addr_map.insert(ins.address, i as u32);
|
addr_map.insert(ins.address, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Generate branches
|
// Generate branches
|
||||||
let mut branches = BTreeMap::<u32, InstructionBranchFrom>::new();
|
let mut branches = BTreeMap::<usize, ObjInsBranchFrom>::new();
|
||||||
for ((i, ins_diff), ins) in
|
for (i, ins_diff) in vec.iter_mut().enumerate() {
|
||||||
rows.iter_mut().enumerate().filter(|(_, row)| row.ins_ref.is_some()).zip(ops)
|
if let Some(ins) = &ins_diff.ins {
|
||||||
{
|
if let Some(ins_idx) = ins.branch_dest.and_then(|a| addr_map.get(&a)) {
|
||||||
if let Some(ins_idx) = ins.branch_dest.and_then(|a| addr_map.get(&a).copied()) {
|
if let Some(branch) = branches.get_mut(ins_idx) {
|
||||||
match branches.entry(ins_idx) {
|
|
||||||
btree_map::Entry::Vacant(e) => {
|
|
||||||
ins_diff.branch_to = Some(InstructionBranchTo { ins_idx, branch_idx });
|
|
||||||
e.insert(InstructionBranchFrom { ins_idx: vec![i as u32], branch_idx });
|
|
||||||
branch_idx += 1;
|
|
||||||
}
|
|
||||||
btree_map::Entry::Occupied(e) => {
|
|
||||||
let branch = e.into_mut();
|
|
||||||
ins_diff.branch_to =
|
ins_diff.branch_to =
|
||||||
Some(InstructionBranchTo { ins_idx, branch_idx: branch.branch_idx });
|
Some(ObjInsBranchTo { ins_idx: *ins_idx, branch_idx: branch.branch_idx });
|
||||||
branch.ins_idx.push(i as u32);
|
branch.ins_idx.push(i);
|
||||||
|
} else {
|
||||||
|
ins_diff.branch_to = Some(ObjInsBranchTo { ins_idx: *ins_idx, branch_idx });
|
||||||
|
branches.insert(*ins_idx, ObjInsBranchFrom { ins_idx: vec![i], branch_idx });
|
||||||
|
branch_idx += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Store branch from
|
// Store branch from
|
||||||
for (i, branch) in branches {
|
for (i, branch) in branches {
|
||||||
rows[i as usize].branch_from = Some(branch);
|
vec[i].branch_from = Some(branch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn address_eq(left: ResolvedRelocation, right: ResolvedRelocation) -> bool {
|
fn address_eq(left: &ObjSymbol, right: &ObjSymbol) -> bool {
|
||||||
if right.symbol.size == 0 && left.symbol.size != 0 {
|
left.address as i64 + left.addend == right.address as i64 + right.addend
|
||||||
// The base relocation is against a pool but the target relocation isn't.
|
|
||||||
// This can happen in rare cases where the compiler will generate a pool+addend relocation
|
|
||||||
// in the base's data, but the one detected in the target is direct with no addend.
|
|
||||||
// Just check that the final address is the same so these count as a match.
|
|
||||||
left.symbol.address as i64 + left.relocation.addend
|
|
||||||
== right.symbol.address as i64 + right.relocation.addend
|
|
||||||
} else {
|
|
||||||
// But otherwise, if the compiler isn't using a pool, we're more strict and check that the
|
|
||||||
// target symbol address and relocation addend both match exactly.
|
|
||||||
left.symbol.address == right.symbol.address
|
|
||||||
&& left.relocation.addend == right.relocation.addend
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn section_name_eq(
|
|
||||||
left_obj: &Object,
|
|
||||||
right_obj: &Object,
|
|
||||||
left_section_index: usize,
|
|
||||||
right_section_index: usize,
|
|
||||||
) -> bool {
|
|
||||||
left_obj.sections.get(left_section_index).is_some_and(|left_section| {
|
|
||||||
right_obj
|
|
||||||
.sections
|
|
||||||
.get(right_section_index)
|
|
||||||
.is_some_and(|right_section| left_section.name == right_section.name)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reloc_eq(
|
fn reloc_eq(
|
||||||
left_obj: &Object,
|
config: &DiffObjConfig,
|
||||||
right_obj: &Object,
|
left_reloc: Option<&ObjReloc>,
|
||||||
left_ins: ResolvedInstructionRef,
|
right_reloc: Option<&ObjReloc>,
|
||||||
right_ins: ResolvedInstructionRef,
|
|
||||||
diff_config: &DiffObjConfig,
|
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let relax_reloc_diffs = diff_config.function_reloc_diffs == FunctionRelocDiffs::None;
|
let (Some(left), Some(right)) = (left_reloc, right_reloc) else {
|
||||||
let (left_reloc, right_reloc) = match (left_ins.relocation, right_ins.relocation) {
|
return false;
|
||||||
(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,
|
|
||||||
(None, None) => return true,
|
|
||||||
_ => return false,
|
|
||||||
};
|
};
|
||||||
if left_reloc.relocation.flags != right_reloc.relocation.flags {
|
if left.flags != right.flags {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if relax_reloc_diffs {
|
if config.relax_reloc_diffs {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
let symbol_name_addend_matches = left_reloc.symbol.name == right_reloc.symbol.name
|
let name_matches = left.target.name == right.target.name;
|
||||||
&& left_reloc.relocation.addend == right_reloc.relocation.addend;
|
match (&left.target_section, &right.target_section) {
|
||||||
match (&left_reloc.symbol.section, &right_reloc.symbol.section) {
|
|
||||||
(Some(sl), Some(sr)) => {
|
(Some(sl), Some(sr)) => {
|
||||||
// Match if section and name or address match
|
// Match if section and name or address match
|
||||||
section_name_eq(left_obj, right_obj, *sl, *sr)
|
sl == sr && (name_matches || address_eq(&left.target, &right.target))
|
||||||
&& (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
|
|
||||||
|| 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(_)) => {
|
(None, Some(_)) => {
|
||||||
// Match if possibly stripped weak symbol
|
// Match if possibly stripped weak symbol
|
||||||
symbol_name_addend_matches && right_reloc.symbol.flags.contains(SymbolFlag::Weak)
|
name_matches && right.target.flags.0.contains(ObjSymbolFlags::Weak)
|
||||||
}
|
}
|
||||||
(Some(_), None) | (None, None) => symbol_name_addend_matches,
|
(None, None) => name_matches,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn arg_eq(
|
fn arg_eq(
|
||||||
left_obj: &Object,
|
config: &DiffObjConfig,
|
||||||
right_obj: &Object,
|
left: &ObjInsArg,
|
||||||
left_row: &InstructionDiffRow,
|
right: &ObjInsArg,
|
||||||
right_row: &InstructionDiffRow,
|
left_diff: &ObjInsDiff,
|
||||||
left_arg: &InstructionArg,
|
right_diff: &ObjInsDiff,
|
||||||
right_arg: &InstructionArg,
|
|
||||||
left_ins: ResolvedInstructionRef,
|
|
||||||
right_ins: ResolvedInstructionRef,
|
|
||||||
diff_config: &DiffObjConfig,
|
|
||||||
) -> bool {
|
) -> bool {
|
||||||
match left_arg {
|
return match left {
|
||||||
InstructionArg::Value(l) => match right_arg {
|
ObjInsArg::PlainText(l) => match right {
|
||||||
InstructionArg::Value(r) => l.loose_eq(r),
|
ObjInsArg::PlainText(r) => l == r,
|
||||||
// If relocations are relaxed, match if left is a constant and right is a reloc
|
|
||||||
// Useful for instances where the target object is created without relocations
|
|
||||||
InstructionArg::Reloc => diff_config.function_reloc_diffs == FunctionRelocDiffs::None,
|
|
||||||
_ => false,
|
_ => false,
|
||||||
},
|
},
|
||||||
InstructionArg::Reloc => {
|
ObjInsArg::Arg(l) => match right {
|
||||||
matches!(right_arg, InstructionArg::Reloc)
|
ObjInsArg::Arg(r) => l == r,
|
||||||
&& reloc_eq(left_obj, right_obj, left_ins, right_ins, diff_config)
|
// If relocations are relaxed, match if left is a constant and right is a reloc
|
||||||
|
// Useful for instances where the target object is created without relocations
|
||||||
|
ObjInsArg::Reloc => config.relax_reloc_diffs,
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
|
ObjInsArg::Reloc => {
|
||||||
|
matches!(right, ObjInsArg::Reloc)
|
||||||
|
&& reloc_eq(
|
||||||
|
config,
|
||||||
|
left_diff.ins.as_ref().and_then(|i| i.reloc.as_ref()),
|
||||||
|
right_diff.ins.as_ref().and_then(|i| i.reloc.as_ref()),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
InstructionArg::BranchDest(_) => match right_arg {
|
ObjInsArg::BranchDest(_) => {
|
||||||
// Compare dest instruction idx after diffing
|
// Compare dest instruction idx after diffing
|
||||||
InstructionArg::BranchDest(_) => {
|
left_diff.branch_to.as_ref().map(|b| b.ins_idx)
|
||||||
left_row.branch_to.as_ref().map(|b| b.ins_idx)
|
== right_diff.branch_to.as_ref().map(|b| b.ins_idx)
|
||||||
== right_row.branch_to.as_ref().map(|b| b.ins_idx)
|
|
||||||
}
|
|
||||||
// If relocations are relaxed, match if left is a constant and right is a reloc
|
|
||||||
// Useful for instances where the target object is created without relocations
|
|
||||||
InstructionArg::Reloc => diff_config.function_reloc_diffs == FunctionRelocDiffs::None,
|
|
||||||
_ => false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
struct InstructionDiffState {
|
|
||||||
diff_score: u64,
|
|
||||||
left_arg_idx: u32,
|
|
||||||
right_arg_idx: u32,
|
|
||||||
left_args_idx: BTreeMap<String, u32>,
|
|
||||||
right_args_idx: BTreeMap<String, u32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
struct InstructionDiffResult {
|
|
||||||
kind: InstructionDiffKind,
|
|
||||||
left_args_diff: Vec<InstructionArgDiffIndex>,
|
|
||||||
right_args_diff: Vec<InstructionArgDiffIndex>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InstructionDiffResult {
|
|
||||||
#[inline]
|
|
||||||
const fn new(kind: InstructionDiffKind) -> Self {
|
|
||||||
Self { kind, left_args_diff: Vec::new(), right_args_diff: Vec::new() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn diff_instruction(
|
|
||||||
left_obj: &Object,
|
|
||||||
right_obj: &Object,
|
|
||||||
left_symbol_idx: usize,
|
|
||||||
right_symbol_idx: usize,
|
|
||||||
l: Option<InstructionRef>,
|
|
||||||
r: Option<InstructionRef>,
|
|
||||||
left_row: &InstructionDiffRow,
|
|
||||||
right_row: &InstructionDiffRow,
|
|
||||||
diff_config: &DiffObjConfig,
|
|
||||||
state: &mut InstructionDiffState,
|
|
||||||
) -> Result<InstructionDiffResult> {
|
|
||||||
let (l, r) = match (l, r) {
|
|
||||||
(Some(l), Some(r)) => (l, r),
|
|
||||||
(Some(_), None) => {
|
|
||||||
state.diff_score += PENALTY_INSERT_DELETE;
|
|
||||||
return Ok(InstructionDiffResult::new(InstructionDiffKind::Delete));
|
|
||||||
}
|
}
|
||||||
(None, Some(_)) => {
|
|
||||||
state.diff_score += PENALTY_INSERT_DELETE;
|
|
||||||
return Ok(InstructionDiffResult::new(InstructionDiffKind::Insert));
|
|
||||||
}
|
|
||||||
(None, None) => return Ok(InstructionDiffResult::new(InstructionDiffKind::None)),
|
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// If opcodes don't match, replace
|
#[derive(Default)]
|
||||||
if l.opcode != r.opcode {
|
struct InsDiffState {
|
||||||
state.diff_score += PENALTY_REPLACE;
|
diff_count: usize,
|
||||||
return Ok(InstructionDiffResult::new(InstructionDiffKind::Replace));
|
left_arg_idx: usize,
|
||||||
}
|
right_arg_idx: usize,
|
||||||
|
left_args_idx: BTreeMap<String, usize>,
|
||||||
|
right_args_idx: BTreeMap<String, usize>,
|
||||||
|
}
|
||||||
|
|
||||||
let left_resolved = left_obj
|
#[derive(Default)]
|
||||||
.resolve_instruction_ref(left_symbol_idx, l)
|
struct InsDiffResult {
|
||||||
.context("Failed to resolve left instruction")?;
|
kind: ObjInsDiffKind,
|
||||||
let right_resolved = right_obj
|
left_args_diff: Vec<Option<ObjInsArgDiff>>,
|
||||||
.resolve_instruction_ref(right_symbol_idx, r)
|
right_args_diff: Vec<Option<ObjInsArgDiff>>,
|
||||||
.context("Failed to resolve right instruction")?;
|
}
|
||||||
|
|
||||||
if left_resolved.code != right_resolved.code
|
fn compare_ins(
|
||||||
|| !reloc_eq(left_obj, right_obj, left_resolved, right_resolved, diff_config)
|
config: &DiffObjConfig,
|
||||||
{
|
left: &ObjInsDiff,
|
||||||
// If either the raw code bytes or relocations don't match, process instructions and compare args
|
right: &ObjInsDiff,
|
||||||
let left_ins = left_obj.arch.process_instruction(left_resolved, diff_config)?;
|
state: &mut InsDiffState,
|
||||||
let right_ins = right_obj.arch.process_instruction(right_resolved, diff_config)?;
|
) -> Result<InsDiffResult> {
|
||||||
if left_ins.args.len() != right_ins.args.len() {
|
let mut result = InsDiffResult::default();
|
||||||
state.diff_score += PENALTY_REPLACE;
|
if let (Some(left_ins), Some(right_ins)) = (&left.ins, &right.ins) {
|
||||||
return Ok(InstructionDiffResult::new(InstructionDiffKind::Replace));
|
if left_ins.args.len() != right_ins.args.len()
|
||||||
|
|| left_ins.op != right_ins.op
|
||||||
|
// Check if any PlainText segments differ (punctuation and spacing)
|
||||||
|
// This indicates a more significant difference than a simple arg mismatch
|
||||||
|
|| !left_ins.args.iter().zip(&right_ins.args).all(|(a, b)| match (a, b) {
|
||||||
|
(ObjInsArg::PlainText(l), ObjInsArg::PlainText(r)) => l == r,
|
||||||
|
_ => true,
|
||||||
|
})
|
||||||
|
{
|
||||||
|
// Totally different op
|
||||||
|
result.kind = ObjInsDiffKind::Replace;
|
||||||
|
state.diff_count += 1;
|
||||||
|
return Ok(result);
|
||||||
}
|
}
|
||||||
let mut result = InstructionDiffResult::new(InstructionDiffKind::None);
|
|
||||||
if left_ins.mnemonic != right_ins.mnemonic {
|
if left_ins.mnemonic != right_ins.mnemonic {
|
||||||
state.diff_score += PENALTY_REG_DIFF;
|
// Same op but different mnemonic, still cmp args
|
||||||
result.kind = InstructionDiffKind::OpMismatch;
|
result.kind = ObjInsDiffKind::OpMismatch;
|
||||||
|
state.diff_count += 1;
|
||||||
}
|
}
|
||||||
for (a, b) in left_ins.args.iter().zip(right_ins.args.iter()) {
|
for (a, b) in left_ins.args.iter().zip(&right_ins.args) {
|
||||||
if arg_eq(
|
if arg_eq(config, a, b, left, right) {
|
||||||
left_obj,
|
result.left_args_diff.push(None);
|
||||||
right_obj,
|
result.right_args_diff.push(None);
|
||||||
left_row,
|
|
||||||
right_row,
|
|
||||||
a,
|
|
||||||
b,
|
|
||||||
left_resolved,
|
|
||||||
right_resolved,
|
|
||||||
diff_config,
|
|
||||||
) {
|
|
||||||
result.left_args_diff.push(InstructionArgDiffIndex::NONE);
|
|
||||||
result.right_args_diff.push(InstructionArgDiffIndex::NONE);
|
|
||||||
} else {
|
} else {
|
||||||
state.diff_score += if let InstructionArg::Value(
|
if result.kind == ObjInsDiffKind::None {
|
||||||
InstructionArgValue::Signed(_) | InstructionArgValue::Unsigned(_),
|
result.kind = ObjInsDiffKind::ArgMismatch;
|
||||||
) = a
|
state.diff_count += 1;
|
||||||
{
|
|
||||||
PENALTY_IMM_DIFF
|
|
||||||
} else {
|
|
||||||
PENALTY_REG_DIFF
|
|
||||||
};
|
|
||||||
if result.kind == InstructionDiffKind::None {
|
|
||||||
result.kind = InstructionDiffKind::ArgMismatch;
|
|
||||||
}
|
}
|
||||||
let a_str = arg_to_string(a, left_resolved.relocation);
|
let a_str = match a {
|
||||||
let a_diff = match state.left_args_idx.entry(a_str) {
|
ObjInsArg::PlainText(arg) => arg.to_string(),
|
||||||
btree_map::Entry::Vacant(e) => {
|
ObjInsArg::Arg(arg) => arg.to_string(),
|
||||||
let idx = state.left_arg_idx;
|
ObjInsArg::Reloc => String::new(),
|
||||||
state.left_arg_idx = idx + 1;
|
ObjInsArg::BranchDest(arg) => format!("{arg}"),
|
||||||
e.insert(idx);
|
|
||||||
idx
|
|
||||||
}
|
|
||||||
btree_map::Entry::Occupied(e) => *e.get(),
|
|
||||||
};
|
};
|
||||||
let b_str = arg_to_string(b, right_resolved.relocation);
|
let a_diff = if let Some(idx) = state.left_args_idx.get(&a_str) {
|
||||||
let b_diff = match state.right_args_idx.entry(b_str) {
|
ObjInsArgDiff { idx: *idx }
|
||||||
btree_map::Entry::Vacant(e) => {
|
} else {
|
||||||
let idx = state.right_arg_idx;
|
let idx = state.left_arg_idx;
|
||||||
state.right_arg_idx = idx + 1;
|
state.left_args_idx.insert(a_str, idx);
|
||||||
e.insert(idx);
|
state.left_arg_idx += 1;
|
||||||
idx
|
ObjInsArgDiff { idx }
|
||||||
}
|
|
||||||
btree_map::Entry::Occupied(e) => *e.get(),
|
|
||||||
};
|
};
|
||||||
result.left_args_diff.push(InstructionArgDiffIndex::new(a_diff));
|
let b_str = match b {
|
||||||
result.right_args_diff.push(InstructionArgDiffIndex::new(b_diff));
|
ObjInsArg::PlainText(arg) => arg.to_string(),
|
||||||
|
ObjInsArg::Arg(arg) => arg.to_string(),
|
||||||
|
ObjInsArg::Reloc => String::new(),
|
||||||
|
ObjInsArg::BranchDest(arg) => format!("{arg}"),
|
||||||
|
};
|
||||||
|
let b_diff = if let Some(idx) = state.right_args_idx.get(&b_str) {
|
||||||
|
ObjInsArgDiff { idx: *idx }
|
||||||
|
} else {
|
||||||
|
let idx = state.right_arg_idx;
|
||||||
|
state.right_args_idx.insert(b_str, idx);
|
||||||
|
state.right_arg_idx += 1;
|
||||||
|
ObjInsArgDiff { idx }
|
||||||
|
};
|
||||||
|
result.left_args_diff.push(Some(a_diff));
|
||||||
|
result.right_args_diff.push(Some(b_diff));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Ok(result);
|
} else if left.ins.is_some() {
|
||||||
|
result.kind = ObjInsDiffKind::Delete;
|
||||||
|
state.diff_count += 1;
|
||||||
|
} else {
|
||||||
|
result.kind = ObjInsDiffKind::Insert;
|
||||||
|
state.diff_count += 1;
|
||||||
}
|
}
|
||||||
|
Ok(result)
|
||||||
Ok(InstructionDiffResult::new(InstructionDiffKind::None))
|
|
||||||
}
|
}
|
||||||
|
@ -1,217 +1,117 @@
|
|||||||
use alloc::{vec, vec::Vec};
|
use std::cmp::{max, min, Ordering};
|
||||||
use core::{cmp::Ordering, ops::Range};
|
|
||||||
|
|
||||||
use anyhow::{Result, anyhow};
|
use anyhow::{anyhow, Result};
|
||||||
use similar::{Algorithm, capture_diff_slices, get_diff_ratio};
|
use similar::{capture_diff_slices_deadline, get_diff_ratio, Algorithm};
|
||||||
|
|
||||||
use super::{
|
use crate::{
|
||||||
DataDiff, DataDiffKind, DataRelocationDiff, ObjectDiff, SectionDiff, SymbolDiff,
|
diff::{ObjDataDiff, ObjDataDiffKind, ObjSectionDiff, ObjSymbolDiff},
|
||||||
code::{address_eq, section_name_eq},
|
obj::{ObjInfo, ObjSection, SymbolRef},
|
||||||
};
|
};
|
||||||
use crate::obj::{Object, Relocation, ResolvedRelocation, Symbol, SymbolFlag, SymbolKind};
|
|
||||||
|
|
||||||
pub fn diff_bss_symbol(
|
pub fn diff_bss_symbol(
|
||||||
left_obj: &Object,
|
left_obj: &ObjInfo,
|
||||||
right_obj: &Object,
|
right_obj: &ObjInfo,
|
||||||
left_symbol_ref: usize,
|
left_symbol_ref: SymbolRef,
|
||||||
right_symbol_ref: usize,
|
right_symbol_ref: SymbolRef,
|
||||||
) -> Result<(SymbolDiff, SymbolDiff)> {
|
) -> Result<(ObjSymbolDiff, ObjSymbolDiff)> {
|
||||||
let left_symbol = &left_obj.symbols[left_symbol_ref];
|
let (_, left_symbol) = left_obj.section_symbol(left_symbol_ref);
|
||||||
let right_symbol = &right_obj.symbols[right_symbol_ref];
|
let (_, right_symbol) = right_obj.section_symbol(right_symbol_ref);
|
||||||
let percent = if left_symbol.size == right_symbol.size { 100.0 } else { 50.0 };
|
let percent = if left_symbol.size == right_symbol.size { 100.0 } else { 50.0 };
|
||||||
Ok((
|
Ok((
|
||||||
SymbolDiff {
|
ObjSymbolDiff {
|
||||||
target_symbol: Some(right_symbol_ref),
|
symbol_ref: left_symbol_ref,
|
||||||
|
diff_symbol: Some(right_symbol_ref),
|
||||||
|
instructions: vec![],
|
||||||
match_percent: Some(percent),
|
match_percent: Some(percent),
|
||||||
diff_score: None,
|
|
||||||
instruction_rows: vec![],
|
|
||||||
},
|
},
|
||||||
SymbolDiff {
|
ObjSymbolDiff {
|
||||||
target_symbol: Some(left_symbol_ref),
|
symbol_ref: right_symbol_ref,
|
||||||
|
diff_symbol: Some(left_symbol_ref),
|
||||||
|
instructions: vec![],
|
||||||
match_percent: Some(percent),
|
match_percent: Some(percent),
|
||||||
diff_score: None,
|
|
||||||
instruction_rows: vec![],
|
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reloc_eq(
|
pub fn no_diff_symbol(_obj: &ObjInfo, symbol_ref: SymbolRef) -> ObjSymbolDiff {
|
||||||
left_obj: &Object,
|
ObjSymbolDiff { symbol_ref, diff_symbol: None, instructions: vec![], match_percent: None }
|
||||||
right_obj: &Object,
|
|
||||||
left: ResolvedRelocation,
|
|
||||||
right: ResolvedRelocation,
|
|
||||||
) -> bool {
|
|
||||||
if left.relocation.flags != right.relocation.flags {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let symbol_name_addend_matches =
|
|
||||||
left.symbol.name == right.symbol.name && left.relocation.addend == right.relocation.addend;
|
|
||||||
match (left.symbol.section, right.symbol.section) {
|
|
||||||
(Some(sl), Some(sr)) => {
|
|
||||||
// Match if section and name+addend or address match
|
|
||||||
section_name_eq(left_obj, right_obj, sl, sr)
|
|
||||||
&& (symbol_name_addend_matches || address_eq(left, right))
|
|
||||||
}
|
|
||||||
(None, Some(_)) => {
|
|
||||||
// Match if possibly stripped weak symbol
|
|
||||||
symbol_name_addend_matches && right.symbol.flags.contains(SymbolFlag::Weak)
|
|
||||||
}
|
|
||||||
(Some(_), None) | (None, None) => symbol_name_addend_matches,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn resolve_relocation<'obj>(
|
|
||||||
symbols: &'obj [Symbol],
|
|
||||||
reloc: &'obj Relocation,
|
|
||||||
) -> ResolvedRelocation<'obj> {
|
|
||||||
let symbol = &symbols[reloc.target_symbol];
|
|
||||||
ResolvedRelocation { relocation: reloc, symbol }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Compares relocations contained with a certain data range.
|
|
||||||
fn diff_data_relocs_for_range<'left, 'right>(
|
|
||||||
left_obj: &'left Object,
|
|
||||||
right_obj: &'right Object,
|
|
||||||
left_section_idx: usize,
|
|
||||||
right_section_idx: usize,
|
|
||||||
left_range: Range<usize>,
|
|
||||||
right_range: Range<usize>,
|
|
||||||
) -> Vec<(DataDiffKind, Option<ResolvedRelocation<'left>>, Option<ResolvedRelocation<'right>>)> {
|
|
||||||
let left_section = &left_obj.sections[left_section_idx];
|
|
||||||
let right_section = &right_obj.sections[right_section_idx];
|
|
||||||
let mut diffs = Vec::new();
|
|
||||||
for left_reloc in left_section.relocations.iter() {
|
|
||||||
if !left_range.contains(&(left_reloc.address as usize)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let left_offset = left_reloc.address as usize - left_range.start;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
let right_offset = r.address as usize - right_range.start;
|
|
||||||
right_offset == left_offset
|
|
||||||
}) else {
|
|
||||||
diffs.push((DataDiffKind::Delete, Some(left_reloc), None));
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
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 {
|
|
||||||
diffs.push((DataDiffKind::Replace, Some(left_reloc), Some(right_reloc)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for right_reloc in right_section.relocations.iter() {
|
|
||||||
if !right_range.contains(&(right_reloc.address as usize)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let right_offset = right_reloc.address as usize - right_range.start;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
let left_offset = r.address as usize - left_range.start;
|
|
||||||
left_offset == right_offset
|
|
||||||
}) else {
|
|
||||||
diffs.push((DataDiffKind::Insert, None, Some(right_reloc)));
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
// No need to check the cases for relocations being deleted or matching again.
|
|
||||||
// They were already handled in the loop over the left relocs.
|
|
||||||
}
|
|
||||||
diffs
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compare the data sections of two object files.
|
/// Compare the data sections of two object files.
|
||||||
pub fn diff_data_section(
|
pub fn diff_data_section(
|
||||||
left_obj: &Object,
|
left: &ObjSection,
|
||||||
right_obj: &Object,
|
right: &ObjSection,
|
||||||
left_diff: &ObjectDiff,
|
left_section_diff: &ObjSectionDiff,
|
||||||
right_diff: &ObjectDiff,
|
right_section_diff: &ObjSectionDiff,
|
||||||
left_section_idx: usize,
|
) -> Result<(ObjSectionDiff, ObjSectionDiff)> {
|
||||||
right_section_idx: usize,
|
let left_max =
|
||||||
) -> Result<(SectionDiff, SectionDiff)> {
|
left.symbols.iter().map(|s| s.section_address + s.size).max().unwrap_or(0).min(left.size);
|
||||||
let left_section = &left_obj.sections[left_section_idx];
|
let right_max =
|
||||||
let right_section = &right_obj.sections[right_section_idx];
|
right.symbols.iter().map(|s| s.section_address + s.size).max().unwrap_or(0).min(right.size);
|
||||||
let left_max = symbols_matching_section(&left_obj.symbols, left_section_idx)
|
let left_data = &left.data[..left_max as usize];
|
||||||
.filter_map(|(_, s)| s.address.checked_sub(left_section.address).map(|a| a + s.size))
|
let right_data = &right.data[..right_max as usize];
|
||||||
.max()
|
let ops = capture_diff_slices_deadline(Algorithm::Patience, left_data, right_data, None);
|
||||||
.unwrap_or(0)
|
|
||||||
.min(left_section.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);
|
|
||||||
let left_data = &left_section.data[..left_max as usize];
|
|
||||||
let right_data = &right_section.data[..right_max as usize];
|
|
||||||
let ops = capture_diff_slices(Algorithm::Patience, left_data, right_data);
|
|
||||||
let match_percent = get_diff_ratio(&ops, left_data.len(), right_data.len()) * 100.0;
|
let match_percent = get_diff_ratio(&ops, left_data.len(), right_data.len()) * 100.0;
|
||||||
|
|
||||||
let mut left_data_diff = Vec::<DataDiff>::new();
|
let mut left_diff = Vec::<ObjDataDiff>::new();
|
||||||
let mut right_data_diff = Vec::<DataDiff>::new();
|
let mut right_diff = Vec::<ObjDataDiff>::new();
|
||||||
for op in ops {
|
for op in ops {
|
||||||
let (tag, left_range, right_range) = op.as_tag_tuple();
|
let (tag, left_range, right_range) = op.as_tag_tuple();
|
||||||
let left_len = left_range.len();
|
let left_len = left_range.len();
|
||||||
let right_len = right_range.len();
|
let right_len = right_range.len();
|
||||||
let mut len = left_len.max(right_len);
|
let mut len = max(left_len, right_len);
|
||||||
let kind = match tag {
|
let kind = match tag {
|
||||||
similar::DiffTag::Equal => DataDiffKind::None,
|
similar::DiffTag::Equal => ObjDataDiffKind::None,
|
||||||
similar::DiffTag::Delete => DataDiffKind::Delete,
|
similar::DiffTag::Delete => ObjDataDiffKind::Delete,
|
||||||
similar::DiffTag::Insert => DataDiffKind::Insert,
|
similar::DiffTag::Insert => ObjDataDiffKind::Insert,
|
||||||
similar::DiffTag::Replace => {
|
similar::DiffTag::Replace => {
|
||||||
// Ensure replacements are equal length
|
// Ensure replacements are equal length
|
||||||
len = left_len.min(right_len);
|
len = min(left_len, right_len);
|
||||||
DataDiffKind::Replace
|
ObjDataDiffKind::Replace
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let left_data = &left_section.data[left_range];
|
let left_data = &left.data[left_range];
|
||||||
let right_data = &right_section.data[right_range];
|
let right_data = &right.data[right_range];
|
||||||
left_data_diff.push(DataDiff {
|
left_diff.push(ObjDataDiff {
|
||||||
data: left_data[..len.min(left_data.len())].to_vec(),
|
data: left_data[..min(len, left_data.len())].to_vec(),
|
||||||
kind,
|
kind,
|
||||||
len,
|
len,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
right_data_diff.push(DataDiff {
|
right_diff.push(ObjDataDiff {
|
||||||
data: right_data[..len.min(right_data.len())].to_vec(),
|
data: right_data[..min(len, right_data.len())].to_vec(),
|
||||||
kind,
|
kind,
|
||||||
len,
|
len,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
if kind == DataDiffKind::Replace {
|
if kind == ObjDataDiffKind::Replace {
|
||||||
match left_len.cmp(&right_len) {
|
match left_len.cmp(&right_len) {
|
||||||
Ordering::Less => {
|
Ordering::Less => {
|
||||||
let len = right_len - left_len;
|
let len = right_len - left_len;
|
||||||
left_data_diff.push(DataDiff {
|
left_diff.push(ObjDataDiff {
|
||||||
data: vec![],
|
data: vec![],
|
||||||
kind: DataDiffKind::Insert,
|
kind: ObjDataDiffKind::Insert,
|
||||||
len,
|
len,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
right_data_diff.push(DataDiff {
|
right_diff.push(ObjDataDiff {
|
||||||
data: right_data[left_len..right_len].to_vec(),
|
data: right_data[left_len..right_len].to_vec(),
|
||||||
kind: DataDiffKind::Insert,
|
kind: ObjDataDiffKind::Insert,
|
||||||
len,
|
len,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Ordering::Greater => {
|
Ordering::Greater => {
|
||||||
let len = left_len - right_len;
|
let len = left_len - right_len;
|
||||||
left_data_diff.push(DataDiff {
|
left_diff.push(ObjDataDiff {
|
||||||
data: left_data[right_len..left_len].to_vec(),
|
data: left_data[right_len..left_len].to_vec(),
|
||||||
kind: DataDiffKind::Delete,
|
kind: ObjDataDiffKind::Delete,
|
||||||
len,
|
len,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
right_data_diff.push(DataDiff {
|
right_diff.push(ObjDataDiff {
|
||||||
data: vec![],
|
data: vec![],
|
||||||
kind: DataDiffKind::Delete,
|
kind: ObjDataDiffKind::Delete,
|
||||||
len,
|
len,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
@ -221,167 +121,52 @@ pub fn diff_data_section(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut left_reloc_diffs = Vec::new();
|
let (mut left_section_diff, mut right_section_diff) =
|
||||||
let mut right_reloc_diffs = Vec::new();
|
diff_generic_section(left, right, left_section_diff, right_section_diff)?;
|
||||||
for (diff_kind, left_reloc, right_reloc) in diff_data_relocs_for_range(
|
left_section_diff.data_diff = left_diff;
|
||||||
left_obj,
|
right_section_diff.data_diff = right_diff;
|
||||||
right_obj,
|
// Use the highest match percent between two options:
|
||||||
left_section_idx,
|
// - Left symbols matching right symbols by name
|
||||||
right_section_idx,
|
// - Diff of the data itself
|
||||||
0..left_max as usize,
|
if left_section_diff.match_percent.unwrap_or(-1.0) < match_percent {
|
||||||
0..right_max as usize,
|
left_section_diff.match_percent = Some(match_percent);
|
||||||
) {
|
right_section_diff.match_percent = Some(match_percent);
|
||||||
if let Some(left_reloc) = left_reloc {
|
|
||||||
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 {
|
|
||||||
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 {
|
|
||||||
reloc: right_reloc.relocation.clone(),
|
|
||||||
kind: diff_kind,
|
|
||||||
range,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let (mut left_section_diff, mut right_section_diff) = diff_generic_section(
|
|
||||||
left_obj,
|
|
||||||
right_obj,
|
|
||||||
left_diff,
|
|
||||||
right_diff,
|
|
||||||
left_section_idx,
|
|
||||||
right_section_idx,
|
|
||||||
)?;
|
|
||||||
let all_left_relocs_match = left_reloc_diffs.iter().all(|d| d.kind == DataDiffKind::None);
|
|
||||||
left_section_diff.data_diff = left_data_diff;
|
|
||||||
right_section_diff.data_diff = right_data_diff;
|
|
||||||
left_section_diff.reloc_diff = left_reloc_diffs;
|
|
||||||
right_section_diff.reloc_diff = right_reloc_diffs;
|
|
||||||
if all_left_relocs_match {
|
|
||||||
// Use the highest match percent between two options:
|
|
||||||
// - Left symbols matching right symbols by name
|
|
||||||
// - Diff of the data itself
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ok((left_section_diff, right_section_diff))
|
Ok((left_section_diff, right_section_diff))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn diff_data_symbol(
|
pub fn diff_data_symbol(
|
||||||
left_obj: &Object,
|
left_obj: &ObjInfo,
|
||||||
right_obj: &Object,
|
right_obj: &ObjInfo,
|
||||||
left_symbol_idx: usize,
|
left_symbol_ref: SymbolRef,
|
||||||
right_symbol_idx: usize,
|
right_symbol_ref: SymbolRef,
|
||||||
) -> Result<(SymbolDiff, SymbolDiff)> {
|
) -> Result<(ObjSymbolDiff, ObjSymbolDiff)> {
|
||||||
let left_symbol = &left_obj.symbols[left_symbol_idx];
|
let (left_section, left_symbol) = left_obj.section_symbol(left_symbol_ref);
|
||||||
let right_symbol = &right_obj.symbols[right_symbol_idx];
|
let (right_section, right_symbol) = right_obj.section_symbol(right_symbol_ref);
|
||||||
|
|
||||||
let left_section_idx =
|
let left_section = left_section.ok_or_else(|| anyhow!("Data symbol section not found"))?;
|
||||||
left_symbol.section.ok_or_else(|| anyhow!("Data symbol section not found"))?;
|
let right_section = right_section.ok_or_else(|| anyhow!("Data symbol section not found"))?;
|
||||||
let right_section_idx =
|
|
||||||
right_symbol.section.ok_or_else(|| anyhow!("Data symbol section not found"))?;
|
|
||||||
|
|
||||||
let left_section = &left_obj.sections[left_section_idx];
|
let left_data = &left_section.data[left_symbol.section_address as usize
|
||||||
let right_section = &right_obj.sections[right_section_idx];
|
..(left_symbol.section_address + left_symbol.size) as usize];
|
||||||
|
let right_data = &right_section.data[right_symbol.section_address as usize
|
||||||
|
..(right_symbol.section_address + right_symbol.size) as usize];
|
||||||
|
|
||||||
let left_start = left_symbol
|
let ops = capture_diff_slices_deadline(Algorithm::Patience, left_data, right_data, None);
|
||||||
.address
|
let match_percent = get_diff_ratio(&ops, left_data.len(), right_data.len()) * 100.0;
|
||||||
.checked_sub(left_section.address)
|
|
||||||
.ok_or_else(|| anyhow!("Symbol address out of section bounds"))?;
|
|
||||||
let right_start = right_symbol
|
|
||||||
.address
|
|
||||||
.checked_sub(right_section.address)
|
|
||||||
.ok_or_else(|| anyhow!("Symbol address out of section bounds"))?;
|
|
||||||
let left_end = left_start + left_symbol.size;
|
|
||||||
if left_end > left_section.size {
|
|
||||||
return Err(anyhow!(
|
|
||||||
"Symbol {} size out of section bounds ({} > {})",
|
|
||||||
left_symbol.name,
|
|
||||||
left_end,
|
|
||||||
left_section.size
|
|
||||||
));
|
|
||||||
}
|
|
||||||
let right_end = right_start + right_symbol.size;
|
|
||||||
if right_end > right_section.size {
|
|
||||||
return Err(anyhow!(
|
|
||||||
"Symbol {} size out of section bounds ({} > {})",
|
|
||||||
right_symbol.name,
|
|
||||||
right_end,
|
|
||||||
right_section.size
|
|
||||||
));
|
|
||||||
}
|
|
||||||
let left_range = left_start as usize..left_end as usize;
|
|
||||||
let right_range = right_start as usize..right_end as usize;
|
|
||||||
let left_data = &left_section.data[left_range.clone()];
|
|
||||||
let right_data = &right_section.data[right_range.clone()];
|
|
||||||
|
|
||||||
let reloc_diffs = diff_data_relocs_for_range(
|
|
||||||
left_obj,
|
|
||||||
right_obj,
|
|
||||||
left_section_idx,
|
|
||||||
right_section_idx,
|
|
||||||
left_range,
|
|
||||||
right_range,
|
|
||||||
);
|
|
||||||
|
|
||||||
let ops = capture_diff_slices(Algorithm::Patience, left_data, right_data);
|
|
||||||
let bytes_match_ratio = get_diff_ratio(&ops, left_data.len(), right_data.len());
|
|
||||||
|
|
||||||
let mut match_ratio = bytes_match_ratio;
|
|
||||||
if !reloc_diffs.is_empty() {
|
|
||||||
let mut total_reloc_bytes = 0;
|
|
||||||
let mut matching_reloc_bytes = 0;
|
|
||||||
for (diff_kind, left_reloc, right_reloc) in reloc_diffs {
|
|
||||||
let reloc_diff_len = match (left_reloc, right_reloc) {
|
|
||||||
(None, None) => unreachable!(),
|
|
||||||
(None, Some(right_reloc)) => {
|
|
||||||
right_obj.arch.data_reloc_size(right_reloc.relocation.flags)
|
|
||||||
}
|
|
||||||
(Some(left_reloc), _) => left_obj.arch.data_reloc_size(left_reloc.relocation.flags),
|
|
||||||
};
|
|
||||||
total_reloc_bytes += reloc_diff_len;
|
|
||||||
if diff_kind == DataDiffKind::None {
|
|
||||||
matching_reloc_bytes += reloc_diff_len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if total_reloc_bytes > 0 {
|
|
||||||
let relocs_match_ratio = matching_reloc_bytes as f32 / total_reloc_bytes as f32;
|
|
||||||
// Adjust the overall match ratio to include relocation differences.
|
|
||||||
// We calculate it so that bytes that contain a relocation are counted twice: once for the
|
|
||||||
// byte's raw value, and once for its relocation.
|
|
||||||
// e.g. An 8 byte symbol that has 8 matching raw bytes and a single 4 byte relocation that
|
|
||||||
// doesn't match would show as 66% (weighted average of 100% and 0%).
|
|
||||||
match_ratio = ((bytes_match_ratio * (left_data.len() as f32))
|
|
||||||
+ (relocs_match_ratio * total_reloc_bytes as f32))
|
|
||||||
/ (left_data.len() + total_reloc_bytes) as f32;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let match_percent = match_ratio * 100.0;
|
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
SymbolDiff {
|
ObjSymbolDiff {
|
||||||
target_symbol: Some(right_symbol_idx),
|
symbol_ref: left_symbol_ref,
|
||||||
|
diff_symbol: Some(right_symbol_ref),
|
||||||
|
instructions: vec![],
|
||||||
match_percent: Some(match_percent),
|
match_percent: Some(match_percent),
|
||||||
diff_score: None,
|
|
||||||
instruction_rows: vec![],
|
|
||||||
},
|
},
|
||||||
SymbolDiff {
|
ObjSymbolDiff {
|
||||||
target_symbol: Some(left_symbol_idx),
|
symbol_ref: right_symbol_ref,
|
||||||
|
diff_symbol: Some(left_symbol_ref),
|
||||||
|
instructions: vec![],
|
||||||
match_percent: Some(match_percent),
|
match_percent: Some(match_percent),
|
||||||
diff_score: None,
|
|
||||||
instruction_rows: vec![],
|
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -389,81 +174,49 @@ pub fn diff_data_symbol(
|
|||||||
/// Compares a section of two object files.
|
/// Compares a section of two object files.
|
||||||
/// This essentially adds up the match percentage of each symbol in the section.
|
/// This essentially adds up the match percentage of each symbol in the section.
|
||||||
pub fn diff_generic_section(
|
pub fn diff_generic_section(
|
||||||
left_obj: &Object,
|
left: &ObjSection,
|
||||||
_right_obj: &Object,
|
_right: &ObjSection,
|
||||||
left_diff: &ObjectDiff,
|
left_diff: &ObjSectionDiff,
|
||||||
_right_diff: &ObjectDiff,
|
_right_diff: &ObjSectionDiff,
|
||||||
left_section_idx: usize,
|
) -> Result<(ObjSectionDiff, ObjSectionDiff)> {
|
||||||
_right_section_idx: usize,
|
let match_percent = if left_diff.symbols.iter().all(|d| d.match_percent == Some(100.0)) {
|
||||||
) -> Result<(SectionDiff, SectionDiff)> {
|
|
||||||
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
|
100.0 // Avoid fp precision issues
|
||||||
} else {
|
} else {
|
||||||
let (matched, total) = symbols_matching_section(&left_obj.symbols, left_section_idx)
|
left.symbols
|
||||||
.map(|(i, s)| (s, &left_diff.symbols[i]))
|
.iter()
|
||||||
.fold((0.0, 0.0), |(matched, total), (s, d)| {
|
.zip(left_diff.symbols.iter())
|
||||||
(matched + d.match_percent.unwrap_or(0.0) * s.size as f32, total + s.size as f32)
|
.map(|(s, d)| d.match_percent.unwrap_or(0.0) * s.size as f32)
|
||||||
});
|
.sum::<f32>()
|
||||||
if total == 0.0 { 100.0 } else { matched / total }
|
/ left.size as f32
|
||||||
};
|
};
|
||||||
Ok((
|
Ok((
|
||||||
SectionDiff { match_percent: Some(match_percent), data_diff: vec![], reloc_diff: vec![] },
|
ObjSectionDiff { symbols: vec![], data_diff: vec![], match_percent: Some(match_percent) },
|
||||||
SectionDiff { match_percent: None, data_diff: vec![], reloc_diff: vec![] },
|
ObjSectionDiff { symbols: vec![], data_diff: vec![], match_percent: Some(match_percent) },
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compare the addresses and sizes of each symbol in the BSS sections.
|
/// Compare the addresses and sizes of each symbol in the BSS sections.
|
||||||
pub fn diff_bss_section(
|
pub fn diff_bss_section(
|
||||||
left_obj: &Object,
|
left: &ObjSection,
|
||||||
right_obj: &Object,
|
right: &ObjSection,
|
||||||
left_diff: &ObjectDiff,
|
left_diff: &ObjSectionDiff,
|
||||||
right_diff: &ObjectDiff,
|
right_diff: &ObjSectionDiff,
|
||||||
left_section_idx: usize,
|
) -> Result<(ObjSectionDiff, ObjSectionDiff)> {
|
||||||
right_section_idx: usize,
|
let left_sizes = left.symbols.iter().map(|s| (s.section_address, s.size)).collect::<Vec<_>>();
|
||||||
) -> Result<(SectionDiff, SectionDiff)> {
|
let right_sizes = right.symbols.iter().map(|s| (s.section_address, s.size)).collect::<Vec<_>>();
|
||||||
let left_section = &left_obj.sections[left_section_idx];
|
let ops = capture_diff_slices_deadline(Algorithm::Patience, &left_sizes, &right_sizes, None);
|
||||||
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 = 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);
|
|
||||||
let mut match_percent = get_diff_ratio(&ops, left_sizes.len(), right_sizes.len()) * 100.0;
|
let mut match_percent = get_diff_ratio(&ops, left_sizes.len(), right_sizes.len()) * 100.0;
|
||||||
|
|
||||||
// Use the highest match percent between two options:
|
// Use the highest match percent between two options:
|
||||||
// - Left symbols matching right symbols by name
|
// - Left symbols matching right symbols by name
|
||||||
// - Diff of the addresses and sizes of each symbol
|
// - Diff of the addresses and sizes of each symbol
|
||||||
let (generic_diff, _) = diff_generic_section(
|
let (generic_diff, _) = diff_generic_section(left, right, left_diff, right_diff)?;
|
||||||
left_obj,
|
|
||||||
right_obj,
|
|
||||||
left_diff,
|
|
||||||
right_diff,
|
|
||||||
left_section_idx,
|
|
||||||
right_section_idx,
|
|
||||||
)?;
|
|
||||||
if generic_diff.match_percent.unwrap_or(-1.0) > match_percent {
|
if generic_diff.match_percent.unwrap_or(-1.0) > match_percent {
|
||||||
match_percent = generic_diff.match_percent.unwrap();
|
match_percent = generic_diff.match_percent.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
SectionDiff { match_percent: Some(match_percent), data_diff: vec![], reloc_diff: vec![] },
|
ObjSectionDiff { symbols: vec![], data_diff: vec![], match_percent: Some(match_percent) },
|
||||||
SectionDiff { match_percent: None, data_diff: vec![], reloc_diff: vec![] },
|
ObjSectionDiff { symbols: vec![], data_diff: vec![], match_percent: Some(match_percent) },
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
@ -1,28 +1,16 @@
|
|||||||
use alloc::{
|
use std::cmp::Ordering;
|
||||||
borrow::Cow,
|
|
||||||
collections::BTreeSet,
|
|
||||||
format,
|
|
||||||
string::{String, ToString},
|
|
||||||
vec::Vec,
|
|
||||||
};
|
|
||||||
use core::cmp::Ordering;
|
|
||||||
|
|
||||||
use anyhow::Result;
|
|
||||||
use itertools::Itertools;
|
|
||||||
use regex::Regex;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
diff::{DiffObjConfig, InstructionDiffKind, InstructionDiffRow, ObjectDiff, SymbolDiff},
|
diff::{ObjInsArgDiff, ObjInsDiff},
|
||||||
obj::{
|
obj::{ObjInsArg, ObjInsArgValue, ObjReloc, ObjSymbol},
|
||||||
InstructionArg, InstructionArgValue, Object, ParsedInstruction, ResolvedInstructionRef,
|
|
||||||
ResolvedRelocation, SectionFlag, SectionKind, Symbol, SymbolFlag, SymbolKind,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub enum DiffText<'a> {
|
pub enum DiffText<'a> {
|
||||||
/// Basic text
|
/// Basic text
|
||||||
Basic(&'a str),
|
Basic(&'a str),
|
||||||
|
/// Colored text
|
||||||
|
BasicColor(&'a str, usize),
|
||||||
/// Line number
|
/// Line number
|
||||||
Line(u32),
|
Line(u32),
|
||||||
/// Instruction address
|
/// Instruction address
|
||||||
@ -30,273 +18,86 @@ pub enum DiffText<'a> {
|
|||||||
/// Instruction mnemonic
|
/// Instruction mnemonic
|
||||||
Opcode(&'a str, u16),
|
Opcode(&'a str, u16),
|
||||||
/// Instruction argument
|
/// Instruction argument
|
||||||
Argument(InstructionArgValue<'a>),
|
Argument(&'a ObjInsArgValue, Option<&'a ObjInsArgDiff>),
|
||||||
/// Branch destination
|
/// Branch destination
|
||||||
BranchDest(u64),
|
BranchDest(u64, Option<&'a ObjInsArgDiff>),
|
||||||
/// Symbol name
|
/// Symbol name
|
||||||
Symbol(&'a Symbol),
|
Symbol(&'a ObjSymbol),
|
||||||
/// Relocation addend
|
|
||||||
Addend(i64),
|
|
||||||
/// Number of spaces
|
/// Number of spaces
|
||||||
Spacing(u8),
|
Spacing(usize),
|
||||||
/// End of line
|
/// End of line
|
||||||
Eol,
|
Eol,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, Hash)]
|
#[derive(Default, Clone, PartialEq, Eq)]
|
||||||
pub enum DiffTextColor {
|
|
||||||
#[default]
|
|
||||||
Normal, // Grey
|
|
||||||
Dim, // Dark grey
|
|
||||||
Bright, // White
|
|
||||||
Replace, // Blue
|
|
||||||
Delete, // Red
|
|
||||||
Insert, // Green
|
|
||||||
Rotating(u8),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct DiffTextSegment<'a> {
|
|
||||||
pub text: DiffText<'a>,
|
|
||||||
pub color: DiffTextColor,
|
|
||||||
pub pad_to: u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> DiffTextSegment<'a> {
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn basic(text: &'a str, color: DiffTextColor) -> Self {
|
|
||||||
Self { text: DiffText::Basic(text), color, pad_to: 0 }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn spacing(spaces: u8) -> Self {
|
|
||||||
Self { text: DiffText::Spacing(spaces), color: DiffTextColor::Normal, pad_to: 0 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const EOL_SEGMENT: DiffTextSegment<'static> =
|
|
||||||
DiffTextSegment { text: DiffText::Eol, color: DiffTextColor::Normal, pad_to: 0 };
|
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone)]
|
|
||||||
pub enum HighlightKind {
|
pub enum HighlightKind {
|
||||||
#[default]
|
#[default]
|
||||||
None,
|
None,
|
||||||
Opcode(u16),
|
Opcode(u16),
|
||||||
Argument(InstructionArgValue<'static>),
|
Arg(ObjInsArgValue),
|
||||||
Symbol(String),
|
Symbol(String),
|
||||||
Address(u64),
|
Address(u64),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
pub fn display_diff<E>(
|
||||||
pub enum InstructionPart<'a> {
|
ins_diff: &ObjInsDiff,
|
||||||
Basic(Cow<'a, str>),
|
base_addr: u64,
|
||||||
Opcode(Cow<'a, str>, u16),
|
mut cb: impl FnMut(DiffText) -> Result<(), E>,
|
||||||
Arg(InstructionArg<'a>),
|
) -> Result<(), E> {
|
||||||
Separator,
|
let Some(ins) = &ins_diff.ins else {
|
||||||
}
|
cb(DiffText::Eol)?;
|
||||||
|
|
||||||
impl<'a> InstructionPart<'a> {
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn basic<T>(s: T) -> Self
|
|
||||||
where T: Into<Cow<'a, str>> {
|
|
||||||
InstructionPart::Basic(s.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn opcode<T>(s: T, o: u16) -> Self
|
|
||||||
where T: Into<Cow<'a, str>> {
|
|
||||||
InstructionPart::Opcode(s.into(), o)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn opaque<T>(s: T) -> Self
|
|
||||||
where T: Into<Cow<'a, str>> {
|
|
||||||
InstructionPart::Arg(InstructionArg::Value(InstructionArgValue::Opaque(s.into())))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn signed<T>(v: T) -> InstructionPart<'static>
|
|
||||||
where T: Into<i64> {
|
|
||||||
InstructionPart::Arg(InstructionArg::Value(InstructionArgValue::Signed(v.into())))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn unsigned<T>(v: T) -> InstructionPart<'static>
|
|
||||||
where T: Into<u64> {
|
|
||||||
InstructionPart::Arg(InstructionArg::Value(InstructionArgValue::Unsigned(v.into())))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn branch_dest<T>(v: T) -> InstructionPart<'static>
|
|
||||||
where T: Into<u64> {
|
|
||||||
InstructionPart::Arg(InstructionArg::BranchDest(v.into()))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn reloc() -> InstructionPart<'static> { InstructionPart::Arg(InstructionArg::Reloc) }
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn separator() -> InstructionPart<'static> { InstructionPart::Separator }
|
|
||||||
|
|
||||||
pub fn into_static(self) -> InstructionPart<'static> {
|
|
||||||
match self {
|
|
||||||
InstructionPart::Basic(s) => InstructionPart::Basic(Cow::Owned(s.into_owned())),
|
|
||||||
InstructionPart::Opcode(s, o) => InstructionPart::Opcode(Cow::Owned(s.into_owned()), o),
|
|
||||||
InstructionPart::Arg(a) => InstructionPart::Arg(a.into_static()),
|
|
||||||
InstructionPart::Separator => InstructionPart::Separator,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn display_row(
|
|
||||||
obj: &Object,
|
|
||||||
symbol_index: usize,
|
|
||||||
ins_row: &InstructionDiffRow,
|
|
||||||
diff_config: &DiffObjConfig,
|
|
||||||
mut cb: impl FnMut(DiffTextSegment) -> Result<()>,
|
|
||||||
) -> Result<()> {
|
|
||||||
let Some(ins_ref) = ins_row.ins_ref else {
|
|
||||||
cb(EOL_SEGMENT)?;
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
let Some(resolved) = obj.resolve_instruction_ref(symbol_index, ins_ref) else {
|
if let Some(line) = ins.line {
|
||||||
cb(DiffTextSegment::basic("<invalid>", DiffTextColor::Delete))?;
|
cb(DiffText::Line(line))?;
|
||||||
cb(EOL_SEGMENT)?;
|
|
||||||
return Ok(());
|
|
||||||
};
|
|
||||||
let base_color = match ins_row.kind {
|
|
||||||
InstructionDiffKind::Replace => DiffTextColor::Replace,
|
|
||||||
InstructionDiffKind::Delete => DiffTextColor::Delete,
|
|
||||||
InstructionDiffKind::Insert => DiffTextColor::Insert,
|
|
||||||
_ => DiffTextColor::Normal,
|
|
||||||
};
|
|
||||||
if let Some(line) = resolved.section.line_info.range(..=ins_ref.address).last().map(|(_, &b)| b)
|
|
||||||
{
|
|
||||||
cb(DiffTextSegment { text: DiffText::Line(line), color: DiffTextColor::Dim, pad_to: 5 })?;
|
|
||||||
}
|
}
|
||||||
cb(DiffTextSegment {
|
cb(DiffText::Address(ins.address - base_addr))?;
|
||||||
text: DiffText::Address(ins_ref.address.saturating_sub(resolved.symbol.address)),
|
if let Some(branch) = &ins_diff.branch_from {
|
||||||
color: base_color,
|
cb(DiffText::BasicColor(" ~> ", branch.branch_idx))?;
|
||||||
pad_to: 5,
|
|
||||||
})?;
|
|
||||||
if let Some(branch) = &ins_row.branch_from {
|
|
||||||
cb(DiffTextSegment::basic(" ~> ", DiffTextColor::Rotating(branch.branch_idx as u8)))?;
|
|
||||||
} else {
|
} else {
|
||||||
cb(DiffTextSegment::spacing(4))?;
|
cb(DiffText::Spacing(4))?;
|
||||||
}
|
}
|
||||||
let mut arg_idx = 0;
|
cb(DiffText::Opcode(&ins.mnemonic, ins.op))?;
|
||||||
let mut displayed_relocation = false;
|
for (i, arg) in ins.args.iter().enumerate() {
|
||||||
obj.arch.display_instruction(resolved, diff_config, &mut |part| match part {
|
if i == 0 {
|
||||||
InstructionPart::Basic(text) => {
|
cb(DiffText::Spacing(1))?;
|
||||||
if text.chars().all(|c| c == ' ') {
|
|
||||||
cb(DiffTextSegment::spacing(text.len() as u8))
|
|
||||||
} else {
|
|
||||||
cb(DiffTextSegment::basic(&text, base_color))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
InstructionPart::Opcode(mnemonic, opcode) => cb(DiffTextSegment {
|
let diff = ins_diff.arg_diff.get(i).and_then(|o| o.as_ref());
|
||||||
text: DiffText::Opcode(mnemonic.as_ref(), opcode),
|
match arg {
|
||||||
color: match ins_row.kind {
|
ObjInsArg::PlainText(s) => {
|
||||||
InstructionDiffKind::OpMismatch => DiffTextColor::Replace,
|
cb(DiffText::Basic(s))?;
|
||||||
_ => base_color,
|
|
||||||
},
|
|
||||||
pad_to: 10,
|
|
||||||
}),
|
|
||||||
InstructionPart::Arg(arg) => {
|
|
||||||
let diff_index = ins_row.arg_diff.get(arg_idx).copied().unwrap_or_default();
|
|
||||||
arg_idx += 1;
|
|
||||||
if arg == InstructionArg::Reloc {
|
|
||||||
displayed_relocation = true;
|
|
||||||
}
|
}
|
||||||
match (arg, resolved.ins_ref.branch_dest) {
|
ObjInsArg::Arg(v) => {
|
||||||
(InstructionArg::Value(value), _) => cb(DiffTextSegment {
|
cb(DiffText::Argument(v, diff))?;
|
||||||
text: DiffText::Argument(value),
|
}
|
||||||
color: diff_index
|
ObjInsArg::Reloc => {
|
||||||
.get()
|
display_reloc_name(ins.reloc.as_ref().unwrap(), &mut cb)?;
|
||||||
.map_or(base_color, |i| DiffTextColor::Rotating(i as u8)),
|
}
|
||||||
pad_to: 0,
|
ObjInsArg::BranchDest(dest) => {
|
||||||
}),
|
if let Some(dest) = dest.checked_sub(base_addr) {
|
||||||
(InstructionArg::Reloc, None) => {
|
cb(DiffText::BranchDest(dest, diff))?;
|
||||||
let resolved = resolved.relocation.unwrap();
|
} else {
|
||||||
let color = diff_index
|
cb(DiffText::Basic("<unknown>"))?;
|
||||||
.get()
|
|
||||||
.map_or(DiffTextColor::Bright, |i| DiffTextColor::Rotating(i as u8));
|
|
||||||
cb(DiffTextSegment {
|
|
||||||
text: DiffText::Symbol(resolved.symbol),
|
|
||||||
color,
|
|
||||||
pad_to: 0,
|
|
||||||
})?;
|
|
||||||
if resolved.relocation.addend != 0 {
|
|
||||||
cb(DiffTextSegment {
|
|
||||||
text: DiffText::Addend(resolved.relocation.addend),
|
|
||||||
color,
|
|
||||||
pad_to: 0,
|
|
||||||
})?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
(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),
|
|
||||||
color: diff_index
|
|
||||||
.get()
|
|
||||||
.map_or(base_color, |i| DiffTextColor::Rotating(i as u8)),
|
|
||||||
pad_to: 0,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
cb(DiffTextSegment {
|
|
||||||
text: DiffText::Argument(InstructionArgValue::Opaque(Cow::Borrowed(
|
|
||||||
"<invalid>",
|
|
||||||
))),
|
|
||||||
color: diff_index
|
|
||||||
.get()
|
|
||||||
.map_or(base_color, |i| DiffTextColor::Rotating(i as u8)),
|
|
||||||
pad_to: 0,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
InstructionPart::Separator => {
|
|
||||||
cb(DiffTextSegment::basic(diff_config.separator(), base_color))
|
|
||||||
}
|
|
||||||
})?;
|
|
||||||
// Fallback for relocation that wasn't displayed
|
|
||||||
if resolved.relocation.is_some() && !displayed_relocation {
|
|
||||||
cb(DiffTextSegment::basic(" <", base_color))?;
|
|
||||||
let resolved = resolved.relocation.unwrap();
|
|
||||||
let diff_index = ins_row.arg_diff.get(arg_idx).copied().unwrap_or_default();
|
|
||||||
let color =
|
|
||||||
diff_index.get().map_or(DiffTextColor::Bright, |i| DiffTextColor::Rotating(i as u8));
|
|
||||||
cb(DiffTextSegment { text: DiffText::Symbol(resolved.symbol), color, pad_to: 0 })?;
|
|
||||||
if resolved.relocation.addend != 0 {
|
|
||||||
cb(DiffTextSegment {
|
|
||||||
text: DiffText::Addend(resolved.relocation.addend),
|
|
||||||
color,
|
|
||||||
pad_to: 0,
|
|
||||||
})?;
|
|
||||||
}
|
|
||||||
cb(DiffTextSegment::basic(">", base_color))?;
|
|
||||||
}
|
}
|
||||||
if let Some(branch) = &ins_row.branch_to {
|
if let Some(branch) = &ins_diff.branch_to {
|
||||||
cb(DiffTextSegment::basic(" ~>", DiffTextColor::Rotating(branch.branch_idx as u8)))?;
|
cb(DiffText::BasicColor(" ~>", branch.branch_idx))?;
|
||||||
}
|
}
|
||||||
cb(EOL_SEGMENT)?;
|
cb(DiffText::Eol)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq<HighlightKind> for HighlightKind {
|
fn display_reloc_name<E>(
|
||||||
fn eq(&self, other: &HighlightKind) -> bool {
|
reloc: &ObjReloc,
|
||||||
match (self, other) {
|
mut cb: impl FnMut(DiffText) -> Result<(), E>,
|
||||||
(HighlightKind::Opcode(a), HighlightKind::Opcode(b)) => a == b,
|
) -> Result<(), E> {
|
||||||
(HighlightKind::Argument(a), HighlightKind::Argument(b)) => a.loose_eq(b),
|
cb(DiffText::Symbol(&reloc.target))?;
|
||||||
(HighlightKind::Symbol(a), HighlightKind::Symbol(b)) => a == b,
|
match reloc.target.addend.cmp(&0i64) {
|
||||||
(HighlightKind::Address(a), HighlightKind::Address(b)) => a == b,
|
Ordering::Greater => cb(DiffText::Basic(&format!("+{:#x}", reloc.target.addend))),
|
||||||
_ => false,
|
Ordering::Less => cb(DiffText::Basic(&format!("-{:#x}", -reloc.target.addend))),
|
||||||
}
|
_ => Ok(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -304,9 +105,11 @@ impl PartialEq<DiffText<'_>> for HighlightKind {
|
|||||||
fn eq(&self, other: &DiffText) -> bool {
|
fn eq(&self, other: &DiffText) -> bool {
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
(HighlightKind::Opcode(a), DiffText::Opcode(_, b)) => a == b,
|
(HighlightKind::Opcode(a), DiffText::Opcode(_, b)) => a == b,
|
||||||
(HighlightKind::Argument(a), DiffText::Argument(b)) => a.loose_eq(b),
|
(HighlightKind::Arg(a), DiffText::Argument(b, _)) => a.loose_eq(b),
|
||||||
(HighlightKind::Symbol(a), DiffText::Symbol(b)) => a == &b.name,
|
(HighlightKind::Symbol(a), DiffText::Symbol(b)) => a == &b.name,
|
||||||
(HighlightKind::Address(a), DiffText::Address(b) | DiffText::BranchDest(b)) => a == b,
|
(HighlightKind::Address(a), DiffText::Address(b) | DiffText::BranchDest(b, _)) => {
|
||||||
|
a == b
|
||||||
|
}
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -316,479 +119,14 @@ impl PartialEq<HighlightKind> for DiffText<'_> {
|
|||||||
fn eq(&self, other: &HighlightKind) -> bool { other.eq(self) }
|
fn eq(&self, other: &HighlightKind) -> bool { other.eq(self) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&DiffText<'_>> for HighlightKind {
|
impl From<DiffText<'_>> for HighlightKind {
|
||||||
fn from(value: &DiffText<'_>) -> Self {
|
fn from(value: DiffText<'_>) -> Self {
|
||||||
match value {
|
match value {
|
||||||
DiffText::Opcode(_, op) => HighlightKind::Opcode(*op),
|
DiffText::Opcode(_, op) => HighlightKind::Opcode(op),
|
||||||
DiffText::Argument(arg) => HighlightKind::Argument(arg.to_static()),
|
DiffText::Argument(arg, _) => HighlightKind::Arg(arg.clone()),
|
||||||
DiffText::Symbol(sym) => HighlightKind::Symbol(sym.name.to_string()),
|
DiffText::Symbol(sym) => HighlightKind::Symbol(sym.name.to_string()),
|
||||||
DiffText::Address(addr) | DiffText::BranchDest(addr) => HighlightKind::Address(*addr),
|
DiffText::Address(addr) | DiffText::BranchDest(addr, _) => HighlightKind::Address(addr),
|
||||||
_ => HighlightKind::None,
|
_ => HighlightKind::None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum ContextItem {
|
|
||||||
Copy { value: String, label: Option<String> },
|
|
||||||
Navigate { label: String, symbol_index: usize, kind: SymbolNavigationKind },
|
|
||||||
Separator,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Eq, PartialEq)]
|
|
||||||
pub enum SymbolNavigationKind {
|
|
||||||
#[default]
|
|
||||||
Normal,
|
|
||||||
Extab,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Eq, PartialEq)]
|
|
||||||
pub enum HoverItemColor {
|
|
||||||
#[default]
|
|
||||||
Normal, // Gray
|
|
||||||
Emphasized, // White
|
|
||||||
Special, // Blue
|
|
||||||
Delete, // Red
|
|
||||||
Insert, // Green
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum HoverItem {
|
|
||||||
Text { label: String, value: String, color: HoverItemColor },
|
|
||||||
Separator,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn symbol_context(obj: &Object, symbol_index: usize) -> Vec<ContextItem> {
|
|
||||||
let symbol = &obj.symbols[symbol_index];
|
|
||||||
let mut out = Vec::new();
|
|
||||||
out.push(ContextItem::Copy { value: symbol.name.clone(), label: None });
|
|
||||||
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()),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
out.append(&mut obj.arch.symbol_context(obj, symbol_index));
|
|
||||||
out
|
|
||||||
}
|
|
||||||
|
|
||||||
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::Less => format!("-{:x}", -addend),
|
|
||||||
_ => String::new(),
|
|
||||||
};
|
|
||||||
let mut out = Vec::new();
|
|
||||||
out.push(HoverItem::Text {
|
|
||||||
label: "Name".into(),
|
|
||||||
value: format!("{}{}", symbol.name, addend_str),
|
|
||||||
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: 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: override_color.clone().unwrap_or_default(),
|
|
||||||
});
|
|
||||||
out.push(HoverItem::Text {
|
|
||||||
label: "Address".into(),
|
|
||||||
value: format!("{:x}{}", symbol.address, addend_str),
|
|
||||||
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: override_color.clone().unwrap_or_default(),
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
out.push(HoverItem::Text {
|
|
||||||
label: "Size".into(),
|
|
||||||
value: format!("{:x}", symbol.size),
|
|
||||||
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: 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: override_color.clone().unwrap_or(HoverItemColor::Special),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
out.push(HoverItem::Text {
|
|
||||||
label: Default::default(),
|
|
||||||
value: "Extern".into(),
|
|
||||||
color: HoverItemColor::Emphasized,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
out.append(&mut obj.arch.symbol_hover(obj, symbol_index));
|
|
||||||
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,
|
|
||||||
ins: &ParsedInstruction,
|
|
||||||
) -> Vec<ContextItem> {
|
|
||||||
let mut out = Vec::new();
|
|
||||||
let mut hex_string = String::new();
|
|
||||||
for byte in resolved.code {
|
|
||||||
hex_string.push_str(&format!("{:02x}", byte));
|
|
||||||
}
|
|
||||||
out.push(ContextItem::Copy { value: hex_string, label: Some("instruction bytes".to_string()) });
|
|
||||||
out.append(&mut obj.arch.instruction_context(obj, resolved));
|
|
||||||
if let Some(virtual_address) = resolved.symbol.virtual_address {
|
|
||||||
let offset = resolved.ins_ref.address - resolved.symbol.address;
|
|
||||||
out.push(ContextItem::Copy {
|
|
||||||
value: format!("{:x}", virtual_address + offset),
|
|
||||||
label: Some("virtual address".to_string()),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
for arg in &ins.args {
|
|
||||||
if let InstructionArg::Value(arg) = arg {
|
|
||||||
out.push(ContextItem::Copy { value: arg.to_string(), label: None });
|
|
||||||
match arg {
|
|
||||||
InstructionArgValue::Signed(v) => {
|
|
||||||
out.push(ContextItem::Copy { value: v.to_string(), label: None });
|
|
||||||
}
|
|
||||||
InstructionArgValue::Unsigned(v) => {
|
|
||||||
out.push(ContextItem::Copy { value: v.to_string(), label: None });
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(reloc) = resolved.relocation {
|
|
||||||
out.push(ContextItem::Separator);
|
|
||||||
out.append(&mut relocation_context(obj, reloc, Some(resolved)));
|
|
||||||
}
|
|
||||||
out
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn instruction_hover(
|
|
||||||
obj: &Object,
|
|
||||||
resolved: ResolvedInstructionRef,
|
|
||||||
ins: &ParsedInstruction,
|
|
||||||
) -> Vec<HoverItem> {
|
|
||||||
let mut out = Vec::new();
|
|
||||||
out.push(HoverItem::Text {
|
|
||||||
label: Default::default(),
|
|
||||||
value: format!("{:02x?}", resolved.code),
|
|
||||||
color: HoverItemColor::Normal,
|
|
||||||
});
|
|
||||||
out.append(&mut obj.arch.instruction_hover(obj, resolved));
|
|
||||||
if let Some(virtual_address) = resolved.symbol.virtual_address {
|
|
||||||
let offset = resolved.ins_ref.address - resolved.symbol.address;
|
|
||||||
out.push(HoverItem::Text {
|
|
||||||
label: "Virtual address".into(),
|
|
||||||
value: format!("{:x}", virtual_address + offset),
|
|
||||||
color: HoverItemColor::Special,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
for arg in &ins.args {
|
|
||||||
if let InstructionArg::Value(arg) = arg {
|
|
||||||
match arg {
|
|
||||||
InstructionArgValue::Signed(v) => {
|
|
||||||
out.push(HoverItem::Text {
|
|
||||||
label: Default::default(),
|
|
||||||
value: format!("{arg} == {v}"),
|
|
||||||
color: HoverItemColor::Normal,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
InstructionArgValue::Unsigned(v) => {
|
|
||||||
out.push(HoverItem::Text {
|
|
||||||
label: Default::default(),
|
|
||||||
value: format!("{arg} == {v}"),
|
|
||||||
color: HoverItemColor::Normal,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(reloc) = resolved.relocation {
|
|
||||||
out.push(HoverItem::Separator);
|
|
||||||
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(|| format!("{}", ty)),
|
|
||||||
value: literal,
|
|
||||||
color: HoverItemColor::Normal,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
out
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
pub enum SymbolFilter<'a> {
|
|
||||||
None,
|
|
||||||
Search(&'a Regex),
|
|
||||||
Mapping(usize, Option<&'a Regex>),
|
|
||||||
}
|
|
||||||
|
|
||||||
fn symbol_matches_filter(
|
|
||||||
symbol: &Symbol,
|
|
||||||
diff: &SymbolDiff,
|
|
||||||
filter: SymbolFilter<'_>,
|
|
||||||
show_hidden_symbols: bool,
|
|
||||||
) -> bool {
|
|
||||||
// Ignore absolute symbols
|
|
||||||
if symbol.section.is_none() && !symbol.flags.contains(SymbolFlag::Common) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if !show_hidden_symbols
|
|
||||||
&& (symbol.size == 0
|
|
||||||
|| symbol.flags.contains(SymbolFlag::Hidden)
|
|
||||||
|| symbol.flags.contains(SymbolFlag::Ignored))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
match filter {
|
|
||||||
SymbolFilter::None => true,
|
|
||||||
SymbolFilter::Search(regex) => {
|
|
||||||
regex.is_match(&symbol.name)
|
|
||||||
|| symbol.demangled_name.as_deref().is_some_and(|s| regex.is_match(s))
|
|
||||||
}
|
|
||||||
SymbolFilter::Mapping(symbol_ref, regex) => {
|
|
||||||
diff.target_symbol == Some(symbol_ref)
|
|
||||||
&& regex.is_none_or(|r| {
|
|
||||||
r.is_match(&symbol.name)
|
|
||||||
|| symbol.demangled_name.as_deref().is_some_and(|s| r.is_match(s))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
|
||||||
pub struct SectionDisplaySymbol {
|
|
||||||
pub symbol: usize,
|
|
||||||
pub is_mapping_symbol: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct SectionDisplay {
|
|
||||||
pub id: String,
|
|
||||||
pub name: String,
|
|
||||||
pub size: u64,
|
|
||||||
pub match_percent: Option<f32>,
|
|
||||||
pub symbols: Vec<SectionDisplaySymbol>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn display_sections(
|
|
||||||
obj: &Object,
|
|
||||||
diff: &ObjectDiff,
|
|
||||||
filter: SymbolFilter<'_>,
|
|
||||||
show_hidden_symbols: bool,
|
|
||||||
show_mapped_symbols: bool,
|
|
||||||
reverse_fn_order: bool,
|
|
||||||
) -> Vec<SectionDisplay> {
|
|
||||||
let mut mapping = BTreeSet::new();
|
|
||||||
let is_mapping_symbol = if let SymbolFilter::Mapping(_, _) = filter {
|
|
||||||
for mapping_diff in &diff.mapping_symbols {
|
|
||||||
let symbol = &obj.symbols[mapping_diff.symbol_index];
|
|
||||||
if !symbol_matches_filter(
|
|
||||||
symbol,
|
|
||||||
&mapping_diff.symbol_diff,
|
|
||||||
filter,
|
|
||||||
show_hidden_symbols,
|
|
||||||
) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if !show_mapped_symbols {
|
|
||||||
let symbol_diff = &diff.symbols[mapping_diff.symbol_index];
|
|
||||||
if symbol_diff.target_symbol.is_some() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mapping.insert((symbol.section, mapping_diff.symbol_index));
|
|
||||||
}
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
for (symbol_idx, (symbol, symbol_diff)) in obj.symbols.iter().zip(&diff.symbols).enumerate()
|
|
||||||
{
|
|
||||||
if !symbol_matches_filter(symbol, symbol_diff, filter, show_hidden_symbols) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
mapping.insert((symbol.section, symbol_idx));
|
|
||||||
}
|
|
||||||
false
|
|
||||||
};
|
|
||||||
let num_sections = mapping.iter().map(|(section_idx, _)| *section_idx).dedup().count();
|
|
||||||
let mut sections = Vec::with_capacity(num_sections);
|
|
||||||
for (section_idx, group) in &mapping.iter().chunk_by(|(section_idx, _)| *section_idx) {
|
|
||||||
let mut symbols = group
|
|
||||||
.map(|&(_, symbol)| SectionDisplaySymbol { symbol, is_mapping_symbol })
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
if let Some(section_idx) = section_idx {
|
|
||||||
let section = &obj.sections[section_idx];
|
|
||||||
if section.kind == SectionKind::Unknown {
|
|
||||||
// Skip unknown and hidden sections
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let section_diff = &diff.sections[section_idx];
|
|
||||||
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) {
|
|
||||||
format!("{} [combined]", section.name)
|
|
||||||
} else {
|
|
||||||
section.name.clone()
|
|
||||||
},
|
|
||||||
size: section.size,
|
|
||||||
match_percent: section_diff.match_percent,
|
|
||||||
symbols,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// Don't sort, preserve order of absolute symbols
|
|
||||||
sections.push(SectionDisplay {
|
|
||||||
id: ".comm".to_string(),
|
|
||||||
name: ".comm".to_string(),
|
|
||||||
size: 0,
|
|
||||||
match_percent: None,
|
|
||||||
symbols,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sections.sort_by(|a, b| a.name.cmp(&b.name));
|
|
||||||
sections
|
|
||||||
}
|
|
||||||
|
|
||||||
fn section_symbol_sort(a: &Symbol, b: &Symbol) -> Ordering {
|
|
||||||
if a.kind == SymbolKind::Section {
|
|
||||||
if b.kind != SymbolKind::Section {
|
|
||||||
return Ordering::Less;
|
|
||||||
}
|
|
||||||
} else if b.kind == SymbolKind::Section {
|
|
||||||
return Ordering::Greater;
|
|
||||||
}
|
|
||||||
Ordering::Equal
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn display_ins_data_labels(obj: &Object, resolved: ResolvedInstructionRef) -> Vec<String> {
|
|
||||||
let Some(reloc) = resolved.relocation else {
|
|
||||||
return Vec::new();
|
|
||||||
};
|
|
||||||
if reloc.relocation.addend < 0 || reloc.relocation.addend as u64 >= reloc.symbol.size {
|
|
||||||
return Vec::new();
|
|
||||||
}
|
|
||||||
let Some(data) = obj.symbol_data(reloc.relocation.target_symbol) else {
|
|
||||||
return Vec::new();
|
|
||||||
};
|
|
||||||
let bytes = &data[reloc.relocation.addend as usize..];
|
|
||||||
obj.arch
|
|
||||||
.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, Option<String>)> {
|
|
||||||
let Some(reloc) = resolved.relocation else {
|
|
||||||
return Vec::new();
|
|
||||||
};
|
|
||||||
if reloc.relocation.addend < 0 || reloc.relocation.addend as u64 >= reloc.symbol.size {
|
|
||||||
return Vec::new();
|
|
||||||
}
|
|
||||||
let Some(data) = obj.symbol_data(reloc.relocation.target_symbol) else {
|
|
||||||
return Vec::new();
|
|
||||||
};
|
|
||||||
let bytes = &data[reloc.relocation.addend as usize..];
|
|
||||||
obj.arch
|
|
||||||
.guess_data_type(resolved, bytes)
|
|
||||||
.map(|ty| ty.display_literals(obj.endianness, bytes))
|
|
||||||
.unwrap_or_default()
|
|
||||||
}
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,50 +0,0 @@
|
|||||||
use std::{sync::mpsc::Receiver, task::Waker};
|
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
|
||||||
use self_update::{
|
|
||||||
cargo_crate_version,
|
|
||||||
update::{Release, ReleaseUpdate},
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::jobs::{Job, JobContext, JobResult, JobState, start_job, update_status};
|
|
||||||
|
|
||||||
pub struct CheckUpdateConfig {
|
|
||||||
pub build_updater: fn() -> Result<Box<dyn ReleaseUpdate>>,
|
|
||||||
pub bin_names: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct CheckUpdateResult {
|
|
||||||
pub update_available: bool,
|
|
||||||
pub latest_release: Release,
|
|
||||||
pub found_binary: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run_check_update(
|
|
||||||
context: &JobContext,
|
|
||||||
cancel: Receiver<()>,
|
|
||||||
config: CheckUpdateConfig,
|
|
||||||
) -> Result<Box<CheckUpdateResult>> {
|
|
||||||
update_status(context, "Fetching latest release".to_string(), 0, 1, &cancel)?;
|
|
||||||
let updater = (config.build_updater)().context("Failed to create release updater")?;
|
|
||||||
let latest_release = updater.get_latest_release()?;
|
|
||||||
let update_available =
|
|
||||||
self_update::version::bump_is_greater(cargo_crate_version!(), &latest_release.version)?;
|
|
||||||
// Find the binary name in the release assets
|
|
||||||
let mut found_binary = None;
|
|
||||||
for bin_name in &config.bin_names {
|
|
||||||
if latest_release.assets.iter().any(|a| &a.name == bin_name) {
|
|
||||||
found_binary = Some(bin_name.clone());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
update_status(context, "Complete".to_string(), 1, 1, &cancel)?;
|
|
||||||
Ok(Box::new(CheckUpdateResult { update_available, latest_release, found_binary }))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn start_check_update(waker: Waker, config: CheckUpdateConfig) -> JobState {
|
|
||||||
start_job(waker, "Check for updates", Job::CheckUpdate, move |context, cancel| {
|
|
||||||
run_check_update(&context, cancel, config)
|
|
||||||
.map(|result| JobResult::CheckUpdate(Some(result)))
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,194 +0,0 @@
|
|||||||
use std::{sync::mpsc::Receiver, task::Waker};
|
|
||||||
|
|
||||||
use anyhow::{Error, Result, bail};
|
|
||||||
use time::OffsetDateTime;
|
|
||||||
use typed_path::Utf8PlatformPathBuf;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
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 {
|
|
||||||
pub build_config: BuildConfig,
|
|
||||||
pub build_base: bool,
|
|
||||||
pub build_target: bool,
|
|
||||||
pub target_path: Option<Utf8PlatformPathBuf>,
|
|
||||||
pub base_path: Option<Utf8PlatformPathBuf>,
|
|
||||||
pub diff_obj_config: DiffObjConfig,
|
|
||||||
pub mapping_config: MappingConfig,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ObjDiffResult {
|
|
||||||
pub first_status: BuildStatus,
|
|
||||||
pub second_status: BuildStatus,
|
|
||||||
pub first_obj: Option<(Object, ObjectDiff)>,
|
|
||||||
pub second_obj: Option<(Object, ObjectDiff)>,
|
|
||||||
pub time: OffsetDateTime,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run_build(
|
|
||||||
context: &JobContext,
|
|
||||||
cancel: Receiver<()>,
|
|
||||||
config: ObjDiffConfig,
|
|
||||||
) -> Result<Box<ObjDiffResult>> {
|
|
||||||
let mut target_path_rel = None;
|
|
||||||
let mut base_path_rel = None;
|
|
||||||
if config.build_target || config.build_base {
|
|
||||||
let project_dir = config
|
|
||||||
.build_config
|
|
||||||
.project_dir
|
|
||||||
.as_ref()
|
|
||||||
.ok_or_else(|| Error::msg("Missing project dir"))?;
|
|
||||||
if let Some(target_path) = &config.target_path {
|
|
||||||
target_path_rel = match target_path.strip_prefix(project_dir) {
|
|
||||||
Ok(p) => Some(p.with_unix_encoding()),
|
|
||||||
Err(_) => {
|
|
||||||
bail!("Target path '{}' doesn't begin with '{}'", target_path, project_dir);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if let Some(base_path) = &config.base_path {
|
|
||||||
base_path_rel = match base_path.strip_prefix(project_dir) {
|
|
||||||
Ok(p) => Some(p.with_unix_encoding()),
|
|
||||||
Err(_) => {
|
|
||||||
bail!("Base path '{}' doesn't begin with '{}'", base_path, project_dir);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut total = 1;
|
|
||||||
if config.build_target && target_path_rel.is_some() {
|
|
||||||
total += 1;
|
|
||||||
}
|
|
||||||
if config.build_base && base_path_rel.is_some() {
|
|
||||||
total += 1;
|
|
||||||
}
|
|
||||||
if config.target_path.is_some() {
|
|
||||||
total += 1;
|
|
||||||
}
|
|
||||||
if config.base_path.is_some() {
|
|
||||||
total += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut step_idx = 0;
|
|
||||||
let mut first_status = match target_path_rel {
|
|
||||||
Some(target_path_rel) if config.build_target => {
|
|
||||||
update_status(
|
|
||||||
context,
|
|
||||||
format!("Building target {}", target_path_rel),
|
|
||||||
step_idx,
|
|
||||||
total,
|
|
||||||
&cancel,
|
|
||||||
)?;
|
|
||||||
step_idx += 1;
|
|
||||||
run_make(&config.build_config, target_path_rel.as_ref())
|
|
||||||
}
|
|
||||||
_ => BuildStatus::default(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut second_status = match base_path_rel {
|
|
||||||
Some(base_path_rel) if config.build_base => {
|
|
||||||
update_status(
|
|
||||||
context,
|
|
||||||
format!("Building base {}", base_path_rel),
|
|
||||||
step_idx,
|
|
||||||
total,
|
|
||||||
&cancel,
|
|
||||||
)?;
|
|
||||||
step_idx += 1;
|
|
||||||
run_make(&config.build_config, base_path_rel.as_ref())
|
|
||||||
}
|
|
||||||
_ => BuildStatus::default(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let time = OffsetDateTime::now_utc();
|
|
||||||
|
|
||||||
let first_obj = match &config.target_path {
|
|
||||||
Some(target_path) if first_status.success => {
|
|
||||||
update_status(
|
|
||||||
context,
|
|
||||||
format!("Loading target {}", target_path),
|
|
||||||
step_idx,
|
|
||||||
total,
|
|
||||||
&cancel,
|
|
||||||
)?;
|
|
||||||
step_idx += 1;
|
|
||||||
match read::read(target_path.as_ref(), &config.diff_obj_config) {
|
|
||||||
Ok(obj) => Some(obj),
|
|
||||||
Err(e) => {
|
|
||||||
first_status = BuildStatus {
|
|
||||||
success: false,
|
|
||||||
stdout: format!("Loading object '{}'", target_path),
|
|
||||||
stderr: format!("{:#}", e),
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some(_) => {
|
|
||||||
step_idx += 1;
|
|
||||||
None
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
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,
|
|
||||||
)?;
|
|
||||||
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),
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some(_) => {
|
|
||||||
step_idx += 1;
|
|
||||||
None
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
update_status(context, "Performing diff".to_string(), step_idx, total, &cancel)?;
|
|
||||||
step_idx += 1;
|
|
||||||
let result = diff_objs(
|
|
||||||
first_obj.as_ref(),
|
|
||||||
second_obj.as_ref(),
|
|
||||||
None,
|
|
||||||
&config.diff_obj_config,
|
|
||||||
&config.mapping_config,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
update_status(context, "Complete".to_string(), step_idx, total, &cancel)?;
|
|
||||||
Ok(Box::new(ObjDiffResult {
|
|
||||||
first_status,
|
|
||||||
second_status,
|
|
||||||
first_obj: first_obj.and_then(|o| result.left.map(|d| (o, d))),
|
|
||||||
second_obj: second_obj.and_then(|o| result.right.map(|d| (o, d))),
|
|
||||||
time,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn start_build(waker: Waker, config: ObjDiffConfig) -> JobState {
|
|
||||||
start_job(waker, "Build", Job::ObjDiff, move |context, cancel| {
|
|
||||||
run_build(&context, cancel, config).map(|result| JobResult::ObjDiff(Some(result)))
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,20 +1,11 @@
|
|||||||
#![allow(clippy::too_many_arguments)]
|
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
|
||||||
extern crate alloc;
|
|
||||||
|
|
||||||
#[cfg(feature = "any-arch")]
|
#[cfg(feature = "any-arch")]
|
||||||
pub mod arch;
|
pub mod arch;
|
||||||
#[cfg(feature = "bindings")]
|
#[cfg(feature = "bindings")]
|
||||||
pub mod bindings;
|
pub mod bindings;
|
||||||
#[cfg(feature = "build")]
|
|
||||||
pub mod build;
|
|
||||||
#[cfg(feature = "config")]
|
#[cfg(feature = "config")]
|
||||||
pub mod config;
|
pub mod config;
|
||||||
#[cfg(feature = "any-arch")]
|
#[cfg(feature = "any-arch")]
|
||||||
pub mod diff;
|
pub mod diff;
|
||||||
#[cfg(feature = "build")]
|
|
||||||
pub mod jobs;
|
|
||||||
#[cfg(feature = "any-arch")]
|
#[cfg(feature = "any-arch")]
|
||||||
pub mod obj;
|
pub mod obj;
|
||||||
#[cfg(feature = "any-arch")]
|
|
||||||
pub mod util;
|
pub mod util;
|
||||||
|
@ -1,40 +1,23 @@
|
|||||||
pub mod read;
|
pub mod read;
|
||||||
pub mod split_meta;
|
pub mod split_meta;
|
||||||
|
|
||||||
use alloc::{
|
use std::{borrow::Cow, collections::BTreeMap, fmt, path::PathBuf};
|
||||||
borrow::Cow,
|
|
||||||
boxed::Box,
|
|
||||||
collections::BTreeMap,
|
|
||||||
string::{String, ToString},
|
|
||||||
vec,
|
|
||||||
vec::Vec,
|
|
||||||
};
|
|
||||||
use core::{
|
|
||||||
fmt,
|
|
||||||
num::{NonZeroU32, NonZeroU64},
|
|
||||||
};
|
|
||||||
|
|
||||||
use flagset::{FlagSet, flags};
|
use filetime::FileTime;
|
||||||
|
use flagset::{flags, FlagSet};
|
||||||
|
use object::RelocationFlags;
|
||||||
|
use split_meta::SplitMeta;
|
||||||
|
|
||||||
use crate::{
|
use crate::{arch::ObjArch, util::ReallySigned};
|
||||||
arch::{Arch, ArchDummy},
|
|
||||||
obj::split_meta::SplitMeta,
|
|
||||||
util::ReallySigned,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, Copy, Clone, Default)]
|
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
|
||||||
pub enum SectionKind {
|
pub enum ObjSectionKind {
|
||||||
#[default]
|
|
||||||
Unknown = -1,
|
|
||||||
Code,
|
Code,
|
||||||
Data,
|
Data,
|
||||||
Bss,
|
Bss,
|
||||||
Common,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
flags! {
|
flags! {
|
||||||
#[derive(Hash)]
|
pub enum ObjSymbolFlags: u8 {
|
||||||
pub enum SymbolFlag: u8 {
|
|
||||||
Global,
|
Global,
|
||||||
Local,
|
Local,
|
||||||
Weak,
|
Weak,
|
||||||
@ -43,354 +26,142 @@ flags! {
|
|||||||
/// Has extra data associated with the symbol
|
/// Has extra data associated with the symbol
|
||||||
/// (e.g. exception table entry)
|
/// (e.g. exception table entry)
|
||||||
HasExtra,
|
HasExtra,
|
||||||
/// Symbol size was missing and was inferred
|
|
||||||
SizeInferred,
|
|
||||||
/// Symbol should be ignored by any diffing
|
|
||||||
Ignored,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[derive(Debug, Copy, Clone, Default)]
|
||||||
|
pub struct ObjSymbolFlagSet(pub FlagSet<ObjSymbolFlags>);
|
||||||
|
|
||||||
pub type SymbolFlagSet = FlagSet<SymbolFlag>;
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ObjSection {
|
||||||
flags! {
|
|
||||||
#[derive(Hash)]
|
|
||||||
pub enum SectionFlag: u8 {
|
|
||||||
/// Section combined from multiple input sections
|
|
||||||
Combined,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type SectionFlagSet = FlagSet<SectionFlag>;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
|
||||||
pub struct Section {
|
|
||||||
/// Unique section ID
|
|
||||||
pub id: String,
|
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
pub kind: ObjSectionKind,
|
||||||
pub address: u64,
|
pub address: u64,
|
||||||
pub size: u64,
|
pub size: u64,
|
||||||
pub kind: SectionKind,
|
pub data: Vec<u8>,
|
||||||
pub data: SectionData,
|
pub orig_index: usize,
|
||||||
pub flags: SectionFlagSet,
|
pub symbols: Vec<ObjSymbol>,
|
||||||
pub align: Option<NonZeroU64>,
|
pub relocations: Vec<ObjReloc>,
|
||||||
pub relocations: Vec<Relocation>,
|
pub virtual_address: Option<u64>,
|
||||||
/// Line number info (.line or .debug_line section)
|
/// Line number info (.line or .debug_line section)
|
||||||
pub line_info: BTreeMap<u64, u32>,
|
pub line_info: BTreeMap<u64, u32>,
|
||||||
/// Original virtual address (from .note.split section)
|
|
||||||
pub virtual_address: Option<u64>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
|
||||||
#[repr(transparent)]
|
|
||||||
pub struct SectionData(pub Vec<u8>);
|
|
||||||
|
|
||||||
impl core::ops::Deref for SectionData {
|
|
||||||
type Target = Vec<u8>;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target { &self.0 }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for SectionData {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
f.debug_tuple("SectionData").field(&self.0.len()).finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Section {
|
|
||||||
pub fn data_range(&self, address: u64, size: usize) -> Option<&[u8]> {
|
|
||||||
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>(
|
|
||||||
&'obj self,
|
|
||||||
obj: &'obj Object,
|
|
||||||
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),
|
|
||||||
Err(i) => self
|
|
||||||
.relocations
|
|
||||||
.get(i)
|
|
||||||
.filter(|r| r.address < ins_ref.address + ins_ref.size as u64),
|
|
||||||
}
|
|
||||||
.and_then(|relocation| {
|
|
||||||
let symbol = obj.symbols.get(relocation.target_symbol)?;
|
|
||||||
Some(ResolvedRelocation { relocation, symbol })
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
pub enum InstructionArgValue<'a> {
|
pub enum ObjInsArgValue {
|
||||||
Signed(i64),
|
Signed(i64),
|
||||||
Unsigned(u64),
|
Unsigned(u64),
|
||||||
Opaque(Cow<'a, str>),
|
Opaque(Cow<'static, str>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InstructionArgValue<'_> {
|
impl ObjInsArgValue {
|
||||||
pub fn loose_eq(&self, other: &InstructionArgValue) -> bool {
|
pub fn loose_eq(&self, other: &ObjInsArgValue) -> bool {
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
(InstructionArgValue::Signed(a), InstructionArgValue::Signed(b)) => a == b,
|
(ObjInsArgValue::Signed(a), ObjInsArgValue::Signed(b)) => a == b,
|
||||||
(InstructionArgValue::Unsigned(a), InstructionArgValue::Unsigned(b)) => a == b,
|
(ObjInsArgValue::Unsigned(a), ObjInsArgValue::Unsigned(b)) => a == b,
|
||||||
(InstructionArgValue::Signed(a), InstructionArgValue::Unsigned(b))
|
(ObjInsArgValue::Signed(a), ObjInsArgValue::Unsigned(b))
|
||||||
| (InstructionArgValue::Unsigned(b), InstructionArgValue::Signed(a)) => *a as u64 == *b,
|
| (ObjInsArgValue::Unsigned(b), ObjInsArgValue::Signed(a)) => *a as u64 == *b,
|
||||||
(InstructionArgValue::Opaque(a), InstructionArgValue::Opaque(b)) => a == b,
|
(ObjInsArgValue::Opaque(a), ObjInsArgValue::Opaque(b)) => a == b,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_static(&self) -> InstructionArgValue<'static> {
|
|
||||||
match self {
|
|
||||||
InstructionArgValue::Signed(v) => InstructionArgValue::Signed(*v),
|
|
||||||
InstructionArgValue::Unsigned(v) => InstructionArgValue::Unsigned(*v),
|
|
||||||
InstructionArgValue::Opaque(v) => InstructionArgValue::Opaque(v.to_string().into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn into_static(self) -> InstructionArgValue<'static> {
|
|
||||||
match self {
|
|
||||||
InstructionArgValue::Signed(v) => InstructionArgValue::Signed(v),
|
|
||||||
InstructionArgValue::Unsigned(v) => InstructionArgValue::Unsigned(v),
|
|
||||||
InstructionArgValue::Opaque(v) => {
|
|
||||||
InstructionArgValue::Opaque(Cow::Owned(v.into_owned()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for InstructionArgValue<'_> {
|
impl fmt::Display for ObjInsArgValue {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
InstructionArgValue::Signed(v) => write!(f, "{:#x}", ReallySigned(*v)),
|
ObjInsArgValue::Signed(v) => write!(f, "{:#x}", ReallySigned(*v)),
|
||||||
InstructionArgValue::Unsigned(v) => write!(f, "{:#x}", v),
|
ObjInsArgValue::Unsigned(v) => write!(f, "{:#x}", v),
|
||||||
InstructionArgValue::Opaque(v) => write!(f, "{}", v),
|
ObjInsArgValue::Opaque(v) => write!(f, "{}", v),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
pub enum InstructionArg<'a> {
|
pub enum ObjInsArg {
|
||||||
Value(InstructionArgValue<'a>),
|
PlainText(Cow<'static, str>),
|
||||||
|
Arg(ObjInsArgValue),
|
||||||
Reloc,
|
Reloc,
|
||||||
BranchDest(u64),
|
BranchDest(u64),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InstructionArg<'_> {
|
impl ObjInsArg {
|
||||||
pub fn loose_eq(&self, other: &InstructionArg) -> bool {
|
pub fn loose_eq(&self, other: &ObjInsArg) -> bool {
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
(InstructionArg::Value(a), InstructionArg::Value(b)) => a.loose_eq(b),
|
(ObjInsArg::Arg(a), ObjInsArg::Arg(b)) => a.loose_eq(b),
|
||||||
(InstructionArg::Reloc, InstructionArg::Reloc) => true,
|
(ObjInsArg::Reloc, ObjInsArg::Reloc) => true,
|
||||||
(InstructionArg::BranchDest(a), InstructionArg::BranchDest(b)) => a == b,
|
(ObjInsArg::BranchDest(a), ObjInsArg::BranchDest(b)) => a == b,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_static(&self) -> InstructionArg<'static> {
|
|
||||||
match self {
|
|
||||||
InstructionArg::Value(v) => InstructionArg::Value(v.to_static()),
|
|
||||||
InstructionArg::Reloc => InstructionArg::Reloc,
|
|
||||||
InstructionArg::BranchDest(v) => InstructionArg::BranchDest(*v),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn into_static(self) -> InstructionArg<'static> {
|
|
||||||
match self {
|
|
||||||
InstructionArg::Value(v) => InstructionArg::Value(v.into_static()),
|
|
||||||
InstructionArg::Reloc => InstructionArg::Reloc,
|
|
||||||
InstructionArg::BranchDest(v) => InstructionArg::BranchDest(v),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Default)]
|
|
||||||
pub struct InstructionRef {
|
|
||||||
pub address: u64,
|
|
||||||
pub size: u8,
|
|
||||||
pub opcode: u16,
|
|
||||||
pub branch_dest: Option<u64>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ParsedInstruction {
|
pub struct ObjIns {
|
||||||
pub ins_ref: InstructionRef,
|
pub address: u64,
|
||||||
pub mnemonic: Cow<'static, str>,
|
pub size: u8,
|
||||||
pub args: Vec<InstructionArg<'static>>,
|
pub op: u16,
|
||||||
|
pub mnemonic: String,
|
||||||
|
pub args: Vec<ObjInsArg>,
|
||||||
|
pub reloc: Option<ObjReloc>,
|
||||||
|
pub branch_dest: Option<u64>,
|
||||||
|
/// Line number
|
||||||
|
pub line: Option<u32>,
|
||||||
|
/// Formatted instruction
|
||||||
|
pub formatted: String,
|
||||||
|
/// Original (unsimplified) instruction
|
||||||
|
pub orig: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Default)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum SymbolKind {
|
pub struct ObjSymbol {
|
||||||
#[default]
|
|
||||||
Unknown,
|
|
||||||
Function,
|
|
||||||
Object,
|
|
||||||
Section,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Hash, Default)]
|
|
||||||
pub struct Symbol {
|
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub demangled_name: Option<String>,
|
pub demangled_name: Option<String>,
|
||||||
pub address: u64,
|
pub address: u64,
|
||||||
|
pub section_address: u64,
|
||||||
pub size: u64,
|
pub size: u64,
|
||||||
pub kind: SymbolKind,
|
pub size_known: bool,
|
||||||
pub section: Option<usize>,
|
pub flags: ObjSymbolFlagSet,
|
||||||
pub flags: SymbolFlagSet,
|
pub addend: i64,
|
||||||
/// Alignment (from Metrowerks .comment section)
|
|
||||||
pub align: Option<NonZeroU32>,
|
|
||||||
/// Original virtual address (from .note.split section)
|
/// Original virtual address (from .note.split section)
|
||||||
pub virtual_address: Option<u64>,
|
pub virtual_address: Option<u64>,
|
||||||
|
/// Original index in object symbol table
|
||||||
|
pub original_index: Option<usize>,
|
||||||
|
pub bytes: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
pub struct ObjInfo {
|
||||||
pub struct Object {
|
pub arch: Box<dyn ObjArch>,
|
||||||
pub arch: Box<dyn Arch>,
|
pub path: Option<PathBuf>,
|
||||||
pub endianness: object::Endianness,
|
pub timestamp: Option<FileTime>,
|
||||||
pub symbols: Vec<Symbol>,
|
pub sections: Vec<ObjSection>,
|
||||||
pub sections: Vec<Section>,
|
/// Common BSS symbols
|
||||||
|
pub common: Vec<ObjSymbol>,
|
||||||
/// Split object metadata (.note.split section)
|
/// Split object metadata (.note.split section)
|
||||||
pub split_meta: Option<SplitMeta>,
|
pub split_meta: Option<SplitMeta>,
|
||||||
#[cfg(feature = "std")]
|
|
||||||
pub path: Option<std::path::PathBuf>,
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
pub timestamp: Option<filetime::FileTime>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Object {
|
#[derive(Debug, Clone)]
|
||||||
fn default() -> Self {
|
pub struct ObjReloc {
|
||||||
Self {
|
|
||||||
arch: ArchDummy::new(),
|
|
||||||
endianness: object::Endianness::Little,
|
|
||||||
symbols: vec![],
|
|
||||||
sections: vec![],
|
|
||||||
split_meta: None,
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
path: None,
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
timestamp: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Object {
|
|
||||||
pub fn resolve_instruction_ref(
|
|
||||||
&self,
|
|
||||||
symbol_index: usize,
|
|
||||||
ins_ref: InstructionRef,
|
|
||||||
) -> Option<ResolvedInstructionRef> {
|
|
||||||
let symbol = self.symbols.get(symbol_index)?;
|
|
||||||
let section_index = symbol.section?;
|
|
||||||
let section = self.sections.get(section_index)?;
|
|
||||||
let offset = ins_ref.address.checked_sub(section.address)?;
|
|
||||||
let code = section.data.get(offset as usize..offset as usize + ins_ref.size as usize)?;
|
|
||||||
let relocation = section.relocation_at(self, ins_ref);
|
|
||||||
Some(ResolvedInstructionRef {
|
|
||||||
ins_ref,
|
|
||||||
symbol_index,
|
|
||||||
symbol,
|
|
||||||
section,
|
|
||||||
section_index,
|
|
||||||
code,
|
|
||||||
relocation,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn symbol_data(&self, symbol_index: usize) -> Option<&[u8]> {
|
|
||||||
let symbol = self.symbols.get(symbol_index)?;
|
|
||||||
let section_index = symbol.section?;
|
|
||||||
let section = self.sections.get(section_index)?;
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
|
||||||
pub struct Relocation {
|
|
||||||
pub flags: RelocationFlags,
|
pub flags: RelocationFlags,
|
||||||
pub address: u64,
|
pub address: u64,
|
||||||
pub target_symbol: usize,
|
pub target: ObjSymbol,
|
||||||
pub addend: i64,
|
pub target_section: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||||
pub enum RelocationFlags {
|
pub struct SymbolRef {
|
||||||
Elf(u32),
|
pub section_idx: usize,
|
||||||
Coff(u16),
|
pub symbol_idx: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
impl ObjInfo {
|
||||||
pub struct ResolvedRelocation<'a> {
|
pub fn section_symbol(&self, symbol_ref: SymbolRef) -> (Option<&ObjSection>, &ObjSymbol) {
|
||||||
pub relocation: &'a Relocation,
|
if symbol_ref.section_idx == self.sections.len() {
|
||||||
pub symbol: &'a Symbol,
|
let symbol = &self.common[symbol_ref.symbol_idx];
|
||||||
}
|
return (None, 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,
|
|
||||||
pub symbol_index: usize,
|
|
||||||
pub symbol: &'obj Symbol,
|
|
||||||
pub section_index: usize,
|
|
||||||
pub section: &'obj Section,
|
|
||||||
pub code: &'obj [u8],
|
|
||||||
pub relocation: Option<ResolvedRelocation<'obj>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
static DUMMY_SYMBOL: Symbol = Symbol {
|
|
||||||
name: String::new(),
|
|
||||||
demangled_name: None,
|
|
||||||
address: 0,
|
|
||||||
size: 0,
|
|
||||||
kind: SymbolKind::Unknown,
|
|
||||||
section: None,
|
|
||||||
flags: SymbolFlagSet::empty(),
|
|
||||||
align: None,
|
|
||||||
virtual_address: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
static DUMMY_SECTION: Section = Section {
|
|
||||||
id: String::new(),
|
|
||||||
name: String::new(),
|
|
||||||
address: 0,
|
|
||||||
size: 0,
|
|
||||||
kind: SectionKind::Unknown,
|
|
||||||
data: SectionData(Vec::new()),
|
|
||||||
flags: SectionFlagSet::empty(),
|
|
||||||
align: None,
|
|
||||||
relocations: Vec::new(),
|
|
||||||
line_info: BTreeMap::new(),
|
|
||||||
virtual_address: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
impl Default for ResolvedInstructionRef<'_> {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
ins_ref: InstructionRef::default(),
|
|
||||||
symbol_index: 0,
|
|
||||||
symbol: &DUMMY_SYMBOL,
|
|
||||||
section_index: 0,
|
|
||||||
section: &DUMMY_SECTION,
|
|
||||||
code: &[],
|
|
||||||
relocation: None,
|
|
||||||
}
|
}
|
||||||
|
let section = &self.sections[symbol_ref.section_idx];
|
||||||
|
let symbol = §ion.symbols[symbol_ref.symbol_idx];
|
||||||
|
(Some(section), symbol)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,165 +0,0 @@
|
|||||||
---
|
|
||||||
source: objdiff-core/src/obj/read.rs
|
|
||||||
expression: "(sections, symbols)"
|
|
||||||
---
|
|
||||||
(
|
|
||||||
[
|
|
||||||
Section {
|
|
||||||
id: ".text-0",
|
|
||||||
name: ".text",
|
|
||||||
address: 0,
|
|
||||||
size: 8,
|
|
||||||
kind: Code,
|
|
||||||
data: SectionData(
|
|
||||||
8,
|
|
||||||
),
|
|
||||||
flags: FlagSet(),
|
|
||||||
align: None,
|
|
||||||
relocations: [
|
|
||||||
Relocation {
|
|
||||||
flags: Elf(
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
address: 0,
|
|
||||||
target_symbol: 0,
|
|
||||||
addend: 4,
|
|
||||||
},
|
|
||||||
Relocation {
|
|
||||||
flags: Elf(
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
address: 2,
|
|
||||||
target_symbol: 1,
|
|
||||||
addend: 0,
|
|
||||||
},
|
|
||||||
Relocation {
|
|
||||||
flags: Elf(
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
address: 4,
|
|
||||||
target_symbol: 0,
|
|
||||||
addend: 10,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
line_info: {},
|
|
||||||
virtual_address: None,
|
|
||||||
},
|
|
||||||
Section {
|
|
||||||
id: ".data-combined",
|
|
||||||
name: ".data",
|
|
||||||
address: 0,
|
|
||||||
size: 12,
|
|
||||||
kind: Data,
|
|
||||||
data: SectionData(
|
|
||||||
12,
|
|
||||||
),
|
|
||||||
flags: FlagSet(Combined),
|
|
||||||
align: None,
|
|
||||||
relocations: [
|
|
||||||
Relocation {
|
|
||||||
flags: Elf(
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
address: 0,
|
|
||||||
target_symbol: 2,
|
|
||||||
addend: 0,
|
|
||||||
},
|
|
||||||
Relocation {
|
|
||||||
flags: Elf(
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
address: 4,
|
|
||||||
target_symbol: 2,
|
|
||||||
addend: 0,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
line_info: {
|
|
||||||
0: 1,
|
|
||||||
8: 2,
|
|
||||||
},
|
|
||||||
virtual_address: None,
|
|
||||||
},
|
|
||||||
Section {
|
|
||||||
id: ".data-1",
|
|
||||||
name: ".data",
|
|
||||||
address: 0,
|
|
||||||
size: 0,
|
|
||||||
kind: Unknown,
|
|
||||||
data: SectionData(
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
flags: FlagSet(),
|
|
||||||
align: None,
|
|
||||||
relocations: [],
|
|
||||||
line_info: {},
|
|
||||||
virtual_address: None,
|
|
||||||
},
|
|
||||||
Section {
|
|
||||||
id: ".data-2",
|
|
||||||
name: ".data",
|
|
||||||
address: 0,
|
|
||||||
size: 0,
|
|
||||||
kind: Unknown,
|
|
||||||
data: SectionData(
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
flags: FlagSet(),
|
|
||||||
align: None,
|
|
||||||
relocations: [],
|
|
||||||
line_info: {},
|
|
||||||
virtual_address: None,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[
|
|
||||||
Symbol {
|
|
||||||
name: ".data",
|
|
||||||
demangled_name: None,
|
|
||||||
address: 0,
|
|
||||||
size: 0,
|
|
||||||
kind: Section,
|
|
||||||
section: Some(
|
|
||||||
1,
|
|
||||||
),
|
|
||||||
flags: FlagSet(),
|
|
||||||
align: None,
|
|
||||||
virtual_address: None,
|
|
||||||
},
|
|
||||||
Symbol {
|
|
||||||
name: "symbol",
|
|
||||||
demangled_name: None,
|
|
||||||
address: 4,
|
|
||||||
size: 4,
|
|
||||||
kind: Object,
|
|
||||||
section: Some(
|
|
||||||
1,
|
|
||||||
),
|
|
||||||
flags: FlagSet(),
|
|
||||||
align: None,
|
|
||||||
virtual_address: None,
|
|
||||||
},
|
|
||||||
Symbol {
|
|
||||||
name: "function",
|
|
||||||
demangled_name: None,
|
|
||||||
address: 0,
|
|
||||||
size: 8,
|
|
||||||
kind: Function,
|
|
||||||
section: Some(
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
flags: FlagSet(),
|
|
||||||
align: None,
|
|
||||||
virtual_address: None,
|
|
||||||
},
|
|
||||||
Symbol {
|
|
||||||
name: ".data",
|
|
||||||
demangled_name: None,
|
|
||||||
address: 0,
|
|
||||||
size: 0,
|
|
||||||
kind: Unknown,
|
|
||||||
section: None,
|
|
||||||
flags: FlagSet(),
|
|
||||||
align: None,
|
|
||||||
virtual_address: None,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
)
|
|
@ -1,11 +1,6 @@
|
|||||||
use alloc::{string::String, vec, vec::Vec};
|
use std::{io, io::Write};
|
||||||
|
|
||||||
use anyhow::{Result, anyhow};
|
use object::{elf::SHT_NOTE, Endian, ObjectSection};
|
||||||
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 SPLITMETA_SECTION: &str = ".note.split";
|
||||||
pub const SHT_SPLITMETA: u32 = SHT_NOTE;
|
pub const SHT_SPLITMETA: u32 = SHT_NOTE;
|
||||||
@ -32,10 +27,10 @@ const NT_SPLIT_MODULE_ID: u32 = u32::from_be_bytes(*b"MODI");
|
|||||||
const NT_SPLIT_VIRTUAL_ADDRESSES: u32 = u32::from_be_bytes(*b"VIRT");
|
const NT_SPLIT_VIRTUAL_ADDRESSES: u32 = u32::from_be_bytes(*b"VIRT");
|
||||||
|
|
||||||
impl SplitMeta {
|
impl SplitMeta {
|
||||||
pub fn from_section<E>(section: object::Section, e: E, is_64: bool) -> Result<Self>
|
pub fn from_section<E>(section: object::Section, e: E, is_64: bool) -> io::Result<Self>
|
||||||
where E: Endian {
|
where E: Endian {
|
||||||
let mut result = SplitMeta::default();
|
let mut result = SplitMeta::default();
|
||||||
let data = section.uncompressed_data().map_err(object_error)?;
|
let data = section.uncompressed_data().map_err(object_io_error)?;
|
||||||
let mut iter = NoteIterator::new(data.as_ref(), section.align(), e, is_64)?;
|
let mut iter = NoteIterator::new(data.as_ref(), section.align(), e, is_64)?;
|
||||||
while let Some(note) = iter.next(e)? {
|
while let Some(note) = iter.next(e)? {
|
||||||
if note.name != ELF_NOTE_SPLIT {
|
if note.name != ELF_NOTE_SPLIT {
|
||||||
@ -43,19 +38,20 @@ impl SplitMeta {
|
|||||||
}
|
}
|
||||||
match note.n_type {
|
match note.n_type {
|
||||||
NT_SPLIT_GENERATOR => {
|
NT_SPLIT_GENERATOR => {
|
||||||
let string =
|
let string = String::from_utf8(note.desc.to_vec())
|
||||||
String::from_utf8(note.desc.to_vec()).map_err(anyhow::Error::new)?;
|
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
|
||||||
result.generator = Some(string);
|
result.generator = Some(string);
|
||||||
}
|
}
|
||||||
NT_SPLIT_MODULE_NAME => {
|
NT_SPLIT_MODULE_NAME => {
|
||||||
let string =
|
let string = String::from_utf8(note.desc.to_vec())
|
||||||
String::from_utf8(note.desc.to_vec()).map_err(anyhow::Error::new)?;
|
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
|
||||||
result.module_name = Some(string);
|
result.module_name = Some(string);
|
||||||
}
|
}
|
||||||
NT_SPLIT_MODULE_ID => {
|
NT_SPLIT_MODULE_ID => {
|
||||||
result.module_id = Some(e.read_u32_bytes(
|
result.module_id =
|
||||||
note.desc.try_into().map_err(|_| anyhow!("Invalid module ID size"))?,
|
Some(e.read_u32_bytes(note.desc.try_into().map_err(|_| {
|
||||||
));
|
io::Error::new(io::ErrorKind::InvalidData, "Invalid module ID size")
|
||||||
|
})?));
|
||||||
}
|
}
|
||||||
NT_SPLIT_VIRTUAL_ADDRESSES => {
|
NT_SPLIT_VIRTUAL_ADDRESSES => {
|
||||||
let vec = if is_64 {
|
let vec = if is_64 {
|
||||||
@ -83,11 +79,10 @@ impl SplitMeta {
|
|||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
pub fn to_writer<E, W>(&self, writer: &mut W, e: E, is_64: bool) -> io::Result<()>
|
||||||
pub fn to_writer<E, W>(&self, writer: &mut W, e: E, is_64: bool) -> std::io::Result<()>
|
|
||||||
where
|
where
|
||||||
E: Endian,
|
E: Endian,
|
||||||
W: std::io::Write + ?Sized,
|
W: Write + ?Sized,
|
||||||
{
|
{
|
||||||
if let Some(generator) = &self.generator {
|
if let Some(generator) = &self.generator {
|
||||||
write_note_header(writer, e, NT_SPLIT_GENERATOR, generator.len())?;
|
write_note_header(writer, e, NT_SPLIT_GENERATOR, generator.len())?;
|
||||||
@ -142,9 +137,10 @@ impl SplitMeta {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert an object::read::Error to a String.
|
/// Convert an object::read::Error to an io::Error.
|
||||||
#[inline]
|
fn object_io_error(err: object::read::Error) -> io::Error {
|
||||||
fn object_error(err: object::read::Error) -> anyhow::Error { anyhow::Error::new(err) }
|
io::Error::new(io::ErrorKind::InvalidData, err)
|
||||||
|
}
|
||||||
|
|
||||||
/// An ELF note entry.
|
/// An ELF note entry.
|
||||||
struct Note<'data> {
|
struct Note<'data> {
|
||||||
@ -165,27 +161,27 @@ where E: Endian
|
|||||||
impl<'data, E> NoteIterator<'data, E>
|
impl<'data, E> NoteIterator<'data, E>
|
||||||
where E: Endian
|
where E: Endian
|
||||||
{
|
{
|
||||||
fn new(data: &'data [u8], align: u64, e: E, is_64: bool) -> Result<Self> {
|
fn new(data: &'data [u8], align: u64, e: E, is_64: bool) -> io::Result<Self> {
|
||||||
Ok(if is_64 {
|
Ok(if is_64 {
|
||||||
NoteIterator::B64(
|
NoteIterator::B64(
|
||||||
object::read::elf::NoteIterator::new(e, align, data).map_err(object_error)?,
|
object::read::elf::NoteIterator::new(e, align, data).map_err(object_io_error)?,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
NoteIterator::B32(
|
NoteIterator::B32(
|
||||||
object::read::elf::NoteIterator::new(e, align as u32, data)
|
object::read::elf::NoteIterator::new(e, align as u32, data)
|
||||||
.map_err(object_error)?,
|
.map_err(object_io_error)?,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next(&mut self, e: E) -> Result<Option<Note<'data>>> {
|
fn next(&mut self, e: E) -> io::Result<Option<Note<'data>>> {
|
||||||
match self {
|
match self {
|
||||||
NoteIterator::B32(iter) => Ok(iter.next().map_err(object_error)?.map(|note| Note {
|
NoteIterator::B32(iter) => Ok(iter.next().map_err(object_io_error)?.map(|note| Note {
|
||||||
n_type: note.n_type(e),
|
n_type: note.n_type(e),
|
||||||
name: note.name(),
|
name: note.name(),
|
||||||
desc: note.desc(),
|
desc: note.desc(),
|
||||||
})),
|
})),
|
||||||
NoteIterator::B64(iter) => Ok(iter.next().map_err(object_error)?.map(|note| Note {
|
NoteIterator::B64(iter) => Ok(iter.next().map_err(object_io_error)?.map(|note| Note {
|
||||||
n_type: note.n_type(e),
|
n_type: note.n_type(e),
|
||||||
name: note.name(),
|
name: note.name(),
|
||||||
desc: note.desc(),
|
desc: note.desc(),
|
||||||
@ -194,6 +190,16 @@ where E: Endian
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn align_size_to_4(size: usize) -> usize { (size + 3) & !3 }
|
||||||
|
|
||||||
|
fn align_data_to_4<W: Write + ?Sized>(writer: &mut W, len: usize) -> io::Result<()> {
|
||||||
|
const ALIGN_BYTES: &[u8] = &[0; 4];
|
||||||
|
if len % 4 != 0 {
|
||||||
|
writer.write_all(&ALIGN_BYTES[..4 - len % 4])?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
// ELF note format:
|
// ELF note format:
|
||||||
// Name Size | 4 bytes (integer)
|
// Name Size | 4 bytes (integer)
|
||||||
// Desc Size | 4 bytes (integer)
|
// Desc Size | 4 bytes (integer)
|
||||||
@ -202,11 +208,10 @@ where E: Endian
|
|||||||
// Desc | variable size, padded to a 4 byte boundary
|
// Desc | variable size, padded to a 4 byte boundary
|
||||||
const NOTE_HEADER_SIZE: usize = 12 + ((ELF_NOTE_SPLIT.len() + 4) & !3);
|
const NOTE_HEADER_SIZE: usize = 12 + ((ELF_NOTE_SPLIT.len() + 4) & !3);
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
fn write_note_header<E, W>(writer: &mut W, e: E, kind: u32, desc_len: usize) -> io::Result<()>
|
||||||
fn write_note_header<E, W>(writer: &mut W, e: E, kind: u32, desc_len: usize) -> std::io::Result<()>
|
|
||||||
where
|
where
|
||||||
E: Endian,
|
E: Endian,
|
||||||
W: std::io::Write + ?Sized,
|
W: Write + ?Sized,
|
||||||
{
|
{
|
||||||
writer.write_all(&e.write_u32_bytes(ELF_NOTE_SPLIT.len() as u32 + 1))?; // Name Size
|
writer.write_all(&e.write_u32_bytes(ELF_NOTE_SPLIT.len() as u32 + 1))?; // Name Size
|
||||||
writer.write_all(&e.write_u32_bytes(desc_len as u32))?; // Desc Size
|
writer.write_all(&e.write_u32_bytes(desc_len as u32))?; // Desc Size
|
||||||
|
@ -1,15 +1,18 @@
|
|||||||
use alloc::{format, vec::Vec};
|
use std::{
|
||||||
use core::fmt;
|
fmt::{LowerHex, UpperHex},
|
||||||
|
io::Read,
|
||||||
|
};
|
||||||
|
|
||||||
use anyhow::{Result, ensure};
|
use anyhow::Result;
|
||||||
|
use byteorder::{NativeEndian, ReadBytesExt};
|
||||||
use num_traits::PrimInt;
|
use num_traits::PrimInt;
|
||||||
use object::{Endian, Object};
|
use object::{Endian, Object};
|
||||||
|
|
||||||
// https://stackoverflow.com/questions/44711012/how-do-i-format-a-signed-integer-to-a-sign-aware-hexadecimal-representation
|
// https://stackoverflow.com/questions/44711012/how-do-i-format-a-signed-integer-to-a-sign-aware-hexadecimal-representation
|
||||||
pub struct ReallySigned<N: PrimInt>(pub N);
|
pub struct ReallySigned<N: PrimInt>(pub(crate) N);
|
||||||
|
|
||||||
impl<N: PrimInt> fmt::LowerHex for ReallySigned<N> {
|
impl<N: PrimInt> LowerHex for ReallySigned<N> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
let num = self.0.to_i64().unwrap();
|
let num = self.0.to_i64().unwrap();
|
||||||
let prefix = if f.alternate() { "0x" } else { "" };
|
let prefix = if f.alternate() { "0x" } else { "" };
|
||||||
let bare_hex = format!("{:x}", num.abs());
|
let bare_hex = format!("{:x}", num.abs());
|
||||||
@ -17,8 +20,8 @@ impl<N: PrimInt> fmt::LowerHex for ReallySigned<N> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<N: PrimInt> fmt::UpperHex for ReallySigned<N> {
|
impl<N: PrimInt> UpperHex for ReallySigned<N> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
let num = self.0.to_i64().unwrap();
|
let num = self.0.to_i64().unwrap();
|
||||||
let prefix = if f.alternate() { "0x" } else { "" };
|
let prefix = if f.alternate() { "0x" } else { "" };
|
||||||
let bare_hex = format!("{:X}", num.abs());
|
let bare_hex = format!("{:X}", num.abs());
|
||||||
@ -26,36 +29,10 @@ impl<N: PrimInt> fmt::UpperHex for ReallySigned<N> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_u32(obj_file: &object::File, reader: &mut &[u8]) -> Result<u32> {
|
pub fn read_u32<R: Read>(obj_file: &object::File, reader: &mut R) -> Result<u32> {
|
||||||
ensure!(reader.len() >= 4, "Not enough bytes to read u32");
|
Ok(obj_file.endianness().read_u32(reader.read_u32::<NativeEndian>()?))
|
||||||
let value = u32::from_ne_bytes(reader[..4].try_into()?);
|
|
||||||
*reader = &reader[4..];
|
|
||||||
Ok(obj_file.endianness().read_u32(value))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_u16(obj_file: &object::File, reader: &mut &[u8]) -> Result<u16> {
|
pub fn read_u16<R: Read>(obj_file: &object::File, reader: &mut R) -> Result<u16> {
|
||||||
ensure!(reader.len() >= 2, "Not enough bytes to read u16");
|
Ok(obj_file.endianness().read_u16(reader.read_u16::<NativeEndian>()?))
|
||||||
let value = u16::from_ne_bytes(reader[..2].try_into()?);
|
|
||||||
*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 % 4 != 0 {
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
@ -1,46 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
@ -1,47 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
@ -1,87 +0,0 @@
|
|||||||
use objdiff_core::{
|
|
||||||
diff::{self, display},
|
|
||||||
obj,
|
|
||||||
obj::SectionKind,
|
|
||||||
};
|
|
||||||
|
|
||||||
mod common;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[cfg(feature = "ppc")]
|
|
||||||
fn read_ppc() {
|
|
||||||
let diff_config = diff::DiffObjConfig::default();
|
|
||||||
let obj = obj::read::parse(include_object!("data/ppc/IObj.o"), &diff_config).unwrap();
|
|
||||||
insta::assert_debug_snapshot!(obj);
|
|
||||||
let symbol_idx =
|
|
||||||
obj.symbols.iter().position(|s| s.name == "Type2Text__10SObjectTagFUi").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 = "ppc")]
|
|
||||||
fn read_dwarf1_line_info() {
|
|
||||||
let diff_config = diff::DiffObjConfig::default();
|
|
||||||
let obj = obj::read::parse(include_object!("data/ppc/m_Do_hostIO.o"), &diff_config).unwrap();
|
|
||||||
let line_infos = obj
|
|
||||||
.sections
|
|
||||||
.iter()
|
|
||||||
.filter(|s| s.kind == SectionKind::Code)
|
|
||||||
.map(|s| s.line_info.clone())
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
insta::assert_debug_snapshot!(line_infos);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[cfg(feature = "ppc")]
|
|
||||||
fn read_extab() {
|
|
||||||
let diff_config = diff::DiffObjConfig::default();
|
|
||||||
let obj = obj::read::parse(include_object!("data/ppc/NMWException.o"), &diff_config).unwrap();
|
|
||||||
insta::assert_debug_snapshot!(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[cfg(feature = "ppc")]
|
|
||||||
fn diff_ppc() {
|
|
||||||
let diff_config = diff::DiffObjConfig::default();
|
|
||||||
let mapping_config = diff::MappingConfig::default();
|
|
||||||
let target_obj =
|
|
||||||
obj::read::parse(include_object!("data/ppc/CDamageVulnerability_target.o"), &diff_config)
|
|
||||||
.unwrap();
|
|
||||||
let base_obj =
|
|
||||||
obj::read::parse(include_object!("data/ppc/CDamageVulnerability_base.o"), &diff_config)
|
|
||||||
.unwrap();
|
|
||||||
let diff =
|
|
||||||
diff::diff_objs(Some(&target_obj), Some(&base_obj), None, &diff_config, &mapping_config)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let target_diff = diff.left.as_ref().unwrap();
|
|
||||||
let base_diff = diff.right.as_ref().unwrap();
|
|
||||||
let sections_display = display::display_sections(
|
|
||||||
&target_obj,
|
|
||||||
target_diff,
|
|
||||||
display::SymbolFilter::None,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
insta::assert_debug_snapshot!(sections_display);
|
|
||||||
|
|
||||||
let target_symbol_idx = target_obj
|
|
||||||
.symbols
|
|
||||||
.iter()
|
|
||||||
.position(|s| s.name == "WeaponHurts__20CDamageVulnerabilityCFRC11CWeaponModei")
|
|
||||||
.unwrap();
|
|
||||||
let target_symbol_diff = &target_diff.symbols[target_symbol_idx];
|
|
||||||
let base_symbol_idx = base_obj
|
|
||||||
.symbols
|
|
||||||
.iter()
|
|
||||||
.position(|s| s.name == "WeaponHurts__20CDamageVulnerabilityCFRC11CWeaponModei")
|
|
||||||
.unwrap();
|
|
||||||
let base_symbol_diff = &base_diff.symbols[base_symbol_idx];
|
|
||||||
assert_eq!(target_symbol_diff.target_symbol, Some(base_symbol_idx));
|
|
||||||
assert_eq!(base_symbol_diff.target_symbol, Some(target_symbol_idx));
|
|
||||||
insta::assert_debug_snapshot!((target_symbol_diff, base_symbol_diff));
|
|
||||||
}
|
|
@ -1,79 +0,0 @@
|
|||||||
use objdiff_core::{diff, diff::display::SymbolFilter, obj};
|
|
||||||
|
|
||||||
mod common;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[cfg(feature = "x86")]
|
|
||||||
fn read_x86() {
|
|
||||||
let diff_config = diff::DiffObjConfig::default();
|
|
||||||
let obj = obj::read::parse(include_object!("data/x86/staticdebug.obj"), &diff_config).unwrap();
|
|
||||||
insta::assert_debug_snapshot!(obj);
|
|
||||||
let symbol_idx = obj.symbols.iter().position(|s| s.name == "?PrintThing@@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);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[cfg(feature = "x86")]
|
|
||||||
fn read_x86_combine_sections() {
|
|
||||||
let diff_config = diff::DiffObjConfig {
|
|
||||||
combine_data_sections: true,
|
|
||||||
combine_text_sections: true,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
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,52 +0,0 @@
|
|||||||
use objdiff_core::{
|
|
||||||
diff::{DiffObjConfig, SymbolDiff, display::DiffTextSegment},
|
|
||||||
obj::Object,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn display_diff(
|
|
||||||
obj: &Object,
|
|
||||||
diff: &SymbolDiff,
|
|
||||||
symbol_idx: usize,
|
|
||||||
diff_config: &DiffObjConfig,
|
|
||||||
) -> String {
|
|
||||||
let mut output = String::new();
|
|
||||||
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| {
|
|
||||||
if separator {
|
|
||||||
output.push_str(", ");
|
|
||||||
} else {
|
|
||||||
separator = true;
|
|
||||||
}
|
|
||||||
let DiffTextSegment { text, color, pad_to } = segment;
|
|
||||||
output.push_str(&format!("({:?}, {:?}, {:?})", text, color, pad_to));
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
output.push_str("]\n");
|
|
||||||
}
|
|
||||||
output
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct AlignedAs<Align, Bytes: ?Sized> {
|
|
||||||
pub _align: [Align; 0],
|
|
||||||
pub bytes: Bytes,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! include_bytes_align_as {
|
|
||||||
($align_ty:ty, $path:literal) => {{
|
|
||||||
static ALIGNED: &common::AlignedAs<$align_ty, [u8]> =
|
|
||||||
&common::AlignedAs { _align: [], bytes: *include_bytes!($path) };
|
|
||||||
&ALIGNED.bytes
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! include_object {
|
|
||||||
($path:literal) => {
|
|
||||||
include_bytes_align_as!(u64, $path)
|
|
||||||
};
|
|
||||||
}
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
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)]
|
|
@ -1,48 +0,0 @@
|
|||||||
---
|
|
||||||
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: [],
|
|
||||||
},
|
|
||||||
]
|
|
File diff suppressed because it is too large
Load Diff
@ -1,112 +0,0 @@
|
|||||||
---
|
|
||||||
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)]
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,111 +0,0 @@
|
|||||||
---
|
|
||||||
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)]
|
|
File diff suppressed because it is too large
Load Diff
@ -1,452 +0,0 @@
|
|||||||
---
|
|
||||||
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,
|
|
||||||
},
|
|
||||||
]
|
|
@ -1,694 +0,0 @@
|
|||||||
---
|
|
||||||
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: [],
|
|
||||||
},
|
|
||||||
]
|
|
@ -1,50 +0,0 @@
|
|||||||
---
|
|
||||||
source: objdiff-core/tests/arch_mips.rs
|
|
||||||
expression: output
|
|
||||||
---
|
|
||||||
[(Address(0), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addiu", 12), Normal, 10), (Argument(Opaque("$sp")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("$sp")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-32)), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(4), Normal, 5), (Spacing(4), Normal, 0), (Opcode("sd", 44), Normal, 10), (Argument(Opaque("$s0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(0)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("$sp")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(8), Normal, 5), (Spacing(4), Normal, 0), (Opcode("sd", 44), Normal, 10), (Argument(Opaque("$s1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(8)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("$sp")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(12), Normal, 5), (Spacing(4), Normal, 0), (Opcode("sd", 44), Normal, 10), (Argument(Opaque("$s2")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(16)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("$sp")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(16), Normal, 5), (Spacing(4), Normal, 0), (Opcode("sd", 44), Normal, 10), (Argument(Opaque("$ra")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(24)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("$sp")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(20), Normal, 5), (Spacing(4), Normal, 0), (Opcode("jal", 2), Normal, 10), (Symbol(Symbol { name: "xglSleep", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(24), Normal, 5), (Spacing(4), Normal, 0), (Opcode("nop", 113), Normal, 10), (Eol, Normal, 0)]
|
|
||||||
[(Address(28), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lw", 26), Normal, 10), (Argument(Opaque("$a1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("%gp_rel("), Normal, 0), (Symbol(Symbol { name: "WorkEnd", demangled_name: None, address: 64, size: 4, kind: Object, section: Some(8), flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Basic(")"), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("$gp")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(32), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lui", 20), Normal, 10), (Argument(Opaque("$a0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("%hi("), Normal, 0), (Symbol(Symbol { name: "[.sdata]", demangled_name: None, address: 0, size: 64, kind: Section, section: Some(8), flags: FlagSet(Local), align: None, virtual_address: None }), Bright, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(36), Normal, 5), (Spacing(4), Normal, 0), (Opcode("daddu", 97), Normal, 10), (Argument(Opaque("$a2")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("$zero")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("$zero")), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(40), Normal, 5), (Spacing(4), Normal, 0), (Opcode("jal", 2), Normal, 10), (Symbol(Symbol { name: "xglSoundLoadEffect", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(44), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addiu", 12), Normal, 10), (Argument(Opaque("$a0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("$a0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("%lo("), Normal, 0), (Symbol(Symbol { name: "[.sdata]", demangled_name: None, address: 0, size: 64, kind: Section, section: Some(8), flags: FlagSet(Local), align: None, virtual_address: None }), Bright, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(48), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lui", 20), Normal, 10), (Argument(Opaque("$a0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("%hi("), Normal, 0), (Symbol(Symbol { name: "PacketBottomNewVu1DropMicroCode", demangled_name: None, address: 0, size: 12, kind: Object, section: Some(7), flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(52), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lw", 26), Normal, 10), (Argument(Opaque("$a1")), Normal, 0), (Basic(", "), Normal, 0), (Basic("%gp_rel("), Normal, 0), (Symbol(Symbol { name: "WorkEnd", demangled_name: None, address: 64, size: 4, kind: Object, section: Some(8), flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Basic(")"), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("$gp")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(56), Normal, 5), (Spacing(4), Normal, 0), (Opcode("jal", 2), Normal, 10), (Symbol(Symbol { name: "xglSoundLoadSwd", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(60), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addiu", 12), Normal, 10), (Argument(Opaque("$a0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("$a0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("%lo("), Normal, 0), (Symbol(Symbol { name: "PacketBottomNewVu1DropMicroCode", demangled_name: None, address: 0, size: 12, kind: Object, section: Some(7), flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(64), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lw", 26), Normal, 10), (Argument(Opaque("$a0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("%gp_rel("), Normal, 0), (Symbol(Symbol { name: "WorkEnd", demangled_name: None, address: 64, size: 4, kind: Object, section: Some(8), flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Basic(")"), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("$gp")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(68), Normal, 5), (Spacing(4), Normal, 0), (Opcode("daddu", 97), Normal, 10), (Argument(Opaque("$a1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("$zero")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("$zero")), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(72), Normal, 5), (Spacing(4), Normal, 0), (Opcode("jal", 2), Normal, 10), (Symbol(Symbol { name: "SsdAddWaveData", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(76), Normal, 5), (Spacing(4), Normal, 0), (Opcode("daddu", 97), Normal, 10), (Argument(Opaque("$a2")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("$zero")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("$zero")), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(80), Normal, 5), (Basic(" ~> "), Rotating(0), 0), (Opcode("jal", 2), Normal, 10), (Symbol(Symbol { name: "SsdSpuDmaCompleted", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(84), Normal, 5), (Spacing(4), Normal, 0), (Opcode("daddu", 97), Normal, 10), (Argument(Opaque("$a0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("$zero")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("$zero")), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(88), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bnez", 56), Normal, 10), (Argument(Opaque("$v0")), Normal, 0), (Basic(", "), Normal, 0), (BranchDest(80), Normal, 0), (Basic(" ~>"), Rotating(0), 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(92), Normal, 5), (Spacing(4), Normal, 0), (Opcode("nop", 113), Normal, 10), (Eol, Normal, 0)]
|
|
||||||
[(Address(96), Normal, 5), (Spacing(4), Normal, 0), (Opcode("jal", 2), Normal, 10), (Symbol(Symbol { name: "xglRenderDispOn", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(100), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lui", 20), Normal, 10), (Argument(Opaque("$s1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Unsigned(255)), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(104), Normal, 5), (Spacing(4), Normal, 0), (Opcode("jal", 2), Normal, 10), (Symbol(Symbol { name: "xglCdLoadOverlay", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(108), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addiu", 12), Normal, 10), (Argument(Opaque("$a0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("$zero")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(2)), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(112), Normal, 5), (Spacing(4), Normal, 0), (Opcode("jal", 2), Normal, 10), (Symbol(Symbol { name: "LogoFirst", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(116), Normal, 5), (Spacing(4), Normal, 0), (Opcode("ori", 16), Normal, 10), (Argument(Opaque("$s1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("$s1")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Unsigned(65535)), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(120), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lui", 20), Normal, 10), (Argument(Opaque("$v0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("%hi("), Normal, 0), (Symbol(Symbol { name: "Title", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(124), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addiu", 12), Normal, 10), (Argument(Opaque("$s2")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("$v0")), Normal, 0), (Basic(", "), Normal, 0), (Basic("%lo("), Normal, 0), (Symbol(Symbol { name: "Title", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(128), Normal, 5), (Basic(" ~> "), Rotating(1), 0), (Opcode("jal", 2), Normal, 10), (Symbol(Symbol { name: "xglCdLoadOverlay", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(132), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addiu", 12), Normal, 10), (Argument(Opaque("$a0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("$zero")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(2)), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(136), Normal, 5), (Spacing(4), Normal, 0), (Opcode("jal", 2), Normal, 10), (Symbol(Symbol { name: "Title", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(140), Normal, 5), (Spacing(4), Normal, 0), (Opcode("nop", 113), Normal, 10), (Eol, Normal, 0)]
|
|
||||||
[(Address(144), Normal, 5), (Spacing(4), Normal, 0), (Opcode("beqz", 55), Normal, 10), (Argument(Opaque("$v0")), Normal, 0), (Basic(", "), Normal, 0), (BranchDest(128), Normal, 0), (Basic(" ~>"), Rotating(1), 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(148), Normal, 5), (Spacing(4), Normal, 0), (Opcode("and", 90), Normal, 10), (Argument(Opaque("$s0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("$v0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("$s1")), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(152), Normal, 5), (Spacing(4), Normal, 0), (Opcode("beq", 3), Normal, 10), (Argument(Opaque("$s0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("$s2")), Normal, 0), (Basic(", "), Normal, 0), (BranchDest(128), Normal, 0), (Basic(" ~>"), Rotating(1), 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(156), Normal, 5), (Spacing(4), Normal, 0), (Opcode("nop", 113), Normal, 10), (Eol, Normal, 0)]
|
|
||||||
[(Address(160), Normal, 5), (Spacing(4), Normal, 0), (Opcode("jal", 2), Normal, 10), (Symbol(Symbol { name: "xglCdLoadOverlay", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(164), Normal, 5), (Spacing(4), Normal, 0), (Opcode("srl", 60), Normal, 10), (Argument(Opaque("$a0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("$v0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("24")), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(168), Normal, 5), (Spacing(4), Normal, 0), (Opcode("jalr", 77), Normal, 10), (Argument(Opaque("$s0")), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(172), Normal, 5), (Spacing(4), Normal, 0), (Opcode("nop", 113), Normal, 10), (Eol, Normal, 0)]
|
|
||||||
[(Address(176), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 54), Normal, 10), (BranchDest(128), Normal, 0), (Basic(" ~>"), Rotating(1), 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(180), Normal, 5), (Spacing(4), Normal, 0), (Opcode("nop", 113), Normal, 10), (Eol, Normal, 0)]
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,87 +0,0 @@
|
|||||||
---
|
|
||||||
source: objdiff-core/tests/arch_ppc.rs
|
|
||||||
assertion_line: 70
|
|
||||||
expression: sections_display
|
|
||||||
---
|
|
||||||
[
|
|
||||||
SectionDisplay {
|
|
||||||
id: ".comm",
|
|
||||||
name: ".comm",
|
|
||||||
size: 0,
|
|
||||||
match_percent: None,
|
|
||||||
symbols: [
|
|
||||||
SectionDisplaySymbol {
|
|
||||||
symbol: 11,
|
|
||||||
is_mapping_symbol: false,
|
|
||||||
},
|
|
||||||
SectionDisplaySymbol {
|
|
||||||
symbol: 12,
|
|
||||||
is_mapping_symbol: false,
|
|
||||||
},
|
|
||||||
SectionDisplaySymbol {
|
|
||||||
symbol: 13,
|
|
||||||
is_mapping_symbol: false,
|
|
||||||
},
|
|
||||||
SectionDisplaySymbol {
|
|
||||||
symbol: 14,
|
|
||||||
is_mapping_symbol: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
SectionDisplay {
|
|
||||||
id: ".ctors-0",
|
|
||||||
name: ".ctors",
|
|
||||||
size: 4,
|
|
||||||
match_percent: Some(
|
|
||||||
100.0,
|
|
||||||
),
|
|
||||||
symbols: [
|
|
||||||
SectionDisplaySymbol {
|
|
||||||
symbol: 2,
|
|
||||||
is_mapping_symbol: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
SectionDisplay {
|
|
||||||
id: ".text-0",
|
|
||||||
name: ".text",
|
|
||||||
size: 3060,
|
|
||||||
match_percent: Some(
|
|
||||||
58.662746,
|
|
||||||
),
|
|
||||||
symbols: [
|
|
||||||
SectionDisplaySymbol {
|
|
||||||
symbol: 3,
|
|
||||||
is_mapping_symbol: false,
|
|
||||||
},
|
|
||||||
SectionDisplaySymbol {
|
|
||||||
symbol: 10,
|
|
||||||
is_mapping_symbol: false,
|
|
||||||
},
|
|
||||||
SectionDisplaySymbol {
|
|
||||||
symbol: 9,
|
|
||||||
is_mapping_symbol: false,
|
|
||||||
},
|
|
||||||
SectionDisplaySymbol {
|
|
||||||
symbol: 8,
|
|
||||||
is_mapping_symbol: false,
|
|
||||||
},
|
|
||||||
SectionDisplaySymbol {
|
|
||||||
symbol: 7,
|
|
||||||
is_mapping_symbol: false,
|
|
||||||
},
|
|
||||||
SectionDisplaySymbol {
|
|
||||||
symbol: 6,
|
|
||||||
is_mapping_symbol: false,
|
|
||||||
},
|
|
||||||
SectionDisplaySymbol {
|
|
||||||
symbol: 5,
|
|
||||||
is_mapping_symbol: false,
|
|
||||||
},
|
|
||||||
SectionDisplaySymbol {
|
|
||||||
symbol: 4,
|
|
||||||
is_mapping_symbol: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
]
|
|
@ -1,40 +0,0 @@
|
|||||||
---
|
|
||||||
source: objdiff-core/tests/arch_ppc.rs
|
|
||||||
expression: line_infos
|
|
||||||
---
|
|
||||||
[
|
|
||||||
{
|
|
||||||
0: 13,
|
|
||||||
4: 16,
|
|
||||||
32: 17,
|
|
||||||
44: 18,
|
|
||||||
60: 20,
|
|
||||||
76: 21,
|
|
||||||
84: 23,
|
|
||||||
92: 25,
|
|
||||||
108: 26,
|
|
||||||
124: 27,
|
|
||||||
136: 28,
|
|
||||||
144: 29,
|
|
||||||
152: 31,
|
|
||||||
164: 34,
|
|
||||||
184: 35,
|
|
||||||
212: 39,
|
|
||||||
228: 40,
|
|
||||||
236: 41,
|
|
||||||
260: 43,
|
|
||||||
288: 44,
|
|
||||||
292: 45,
|
|
||||||
300: 48,
|
|
||||||
436: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
0: 48,
|
|
||||||
132: 35,
|
|
||||||
244: 26,
|
|
||||||
304: 22,
|
|
||||||
312: 23,
|
|
||||||
316: 24,
|
|
||||||
320: 0,
|
|
||||||
},
|
|
||||||
]
|
|
@ -1,551 +0,0 @@
|
|||||||
---
|
|
||||||
source: objdiff-core/tests/arch_ppc.rs
|
|
||||||
expression: obj
|
|
||||||
---
|
|
||||||
Object {
|
|
||||||
arch: ArchPpc {
|
|
||||||
extab: Some(
|
|
||||||
{
|
|
||||||
10: ExceptionInfo {
|
|
||||||
eti_symbol: ExtabSymbolRef {
|
|
||||||
original_index: 5,
|
|
||||||
name: "@31",
|
|
||||||
demangled_name: None,
|
|
||||||
},
|
|
||||||
etb_symbol: ExtabSymbolRef {
|
|
||||||
original_index: 4,
|
|
||||||
name: "@30",
|
|
||||||
demangled_name: None,
|
|
||||||
},
|
|
||||||
data: ExceptionTableData {
|
|
||||||
flag_val: 8200,
|
|
||||||
has_elf_vector: false,
|
|
||||||
large_frame: true,
|
|
||||||
has_frame_pointer: false,
|
|
||||||
saved_cr: false,
|
|
||||||
fpr_save_range: 0,
|
|
||||||
gpr_save_range: 4,
|
|
||||||
et_field: 0,
|
|
||||||
pc_actions: [],
|
|
||||||
exception_actions: [],
|
|
||||||
relocations: [],
|
|
||||||
},
|
|
||||||
dtors: [],
|
|
||||||
},
|
|
||||||
11: ExceptionInfo {
|
|
||||||
eti_symbol: ExtabSymbolRef {
|
|
||||||
original_index: 7,
|
|
||||||
name: "@52",
|
|
||||||
demangled_name: None,
|
|
||||||
},
|
|
||||||
etb_symbol: ExtabSymbolRef {
|
|
||||||
original_index: 6,
|
|
||||||
name: "@51",
|
|
||||||
demangled_name: None,
|
|
||||||
},
|
|
||||||
data: ExceptionTableData {
|
|
||||||
flag_val: 8200,
|
|
||||||
has_elf_vector: false,
|
|
||||||
large_frame: true,
|
|
||||||
has_frame_pointer: false,
|
|
||||||
saved_cr: false,
|
|
||||||
fpr_save_range: 0,
|
|
||||||
gpr_save_range: 4,
|
|
||||||
et_field: 0,
|
|
||||||
pc_actions: [
|
|
||||||
PCAction {
|
|
||||||
start_pc: 96,
|
|
||||||
end_pc: 96,
|
|
||||||
action_offset: 16,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
exception_actions: [
|
|
||||||
ExceptionAction {
|
|
||||||
action_offset: 16,
|
|
||||||
action_type: DestroyLocal,
|
|
||||||
action_param: 0,
|
|
||||||
has_end_bit: true,
|
|
||||||
bytes: [
|
|
||||||
0,
|
|
||||||
8,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
relocations: [
|
|
||||||
Relocation {
|
|
||||||
offset: 20,
|
|
||||||
address: 0,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
dtors: [
|
|
||||||
ExtabSymbolRef {
|
|
||||||
original_index: 12,
|
|
||||||
name: "__dt__26__partial_array_destructorFv",
|
|
||||||
demangled_name: Some(
|
|
||||||
"__partial_array_destructor::~__partial_array_destructor()",
|
|
||||||
),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
12: ExceptionInfo {
|
|
||||||
eti_symbol: ExtabSymbolRef {
|
|
||||||
original_index: 9,
|
|
||||||
name: "@60",
|
|
||||||
demangled_name: None,
|
|
||||||
},
|
|
||||||
etb_symbol: ExtabSymbolRef {
|
|
||||||
original_index: 8,
|
|
||||||
name: "@59",
|
|
||||||
demangled_name: None,
|
|
||||||
},
|
|
||||||
data: ExceptionTableData {
|
|
||||||
flag_val: 6152,
|
|
||||||
has_elf_vector: false,
|
|
||||||
large_frame: true,
|
|
||||||
has_frame_pointer: false,
|
|
||||||
saved_cr: false,
|
|
||||||
fpr_save_range: 0,
|
|
||||||
gpr_save_range: 3,
|
|
||||||
et_field: 0,
|
|
||||||
pc_actions: [],
|
|
||||||
exception_actions: [],
|
|
||||||
relocations: [],
|
|
||||||
},
|
|
||||||
dtors: [],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
),
|
|
||||||
},
|
|
||||||
endianness: Big,
|
|
||||||
symbols: [
|
|
||||||
Symbol {
|
|
||||||
name: "NMWException.cpp",
|
|
||||||
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: "[extab]",
|
|
||||||
demangled_name: None,
|
|
||||||
address: 0,
|
|
||||||
size: 0,
|
|
||||||
kind: Section,
|
|
||||||
section: Some(
|
|
||||||
1,
|
|
||||||
),
|
|
||||||
flags: FlagSet(Local),
|
|
||||||
align: None,
|
|
||||||
virtual_address: None,
|
|
||||||
},
|
|
||||||
Symbol {
|
|
||||||
name: "[extabindex]",
|
|
||||||
demangled_name: None,
|
|
||||||
address: 0,
|
|
||||||
size: 0,
|
|
||||||
kind: Section,
|
|
||||||
section: Some(
|
|
||||||
2,
|
|
||||||
),
|
|
||||||
flags: FlagSet(Local),
|
|
||||||
align: None,
|
|
||||||
virtual_address: None,
|
|
||||||
},
|
|
||||||
Symbol {
|
|
||||||
name: "@30",
|
|
||||||
demangled_name: None,
|
|
||||||
address: 0,
|
|
||||||
size: 8,
|
|
||||||
kind: Object,
|
|
||||||
section: Some(
|
|
||||||
1,
|
|
||||||
),
|
|
||||||
flags: FlagSet(Local),
|
|
||||||
align: None,
|
|
||||||
virtual_address: None,
|
|
||||||
},
|
|
||||||
Symbol {
|
|
||||||
name: "@31",
|
|
||||||
demangled_name: None,
|
|
||||||
address: 0,
|
|
||||||
size: 12,
|
|
||||||
kind: Object,
|
|
||||||
section: Some(
|
|
||||||
2,
|
|
||||||
),
|
|
||||||
flags: FlagSet(Local),
|
|
||||||
align: None,
|
|
||||||
virtual_address: None,
|
|
||||||
},
|
|
||||||
Symbol {
|
|
||||||
name: "@51",
|
|
||||||
demangled_name: None,
|
|
||||||
address: 8,
|
|
||||||
size: 24,
|
|
||||||
kind: Object,
|
|
||||||
section: Some(
|
|
||||||
1,
|
|
||||||
),
|
|
||||||
flags: FlagSet(Local),
|
|
||||||
align: None,
|
|
||||||
virtual_address: None,
|
|
||||||
},
|
|
||||||
Symbol {
|
|
||||||
name: "@52",
|
|
||||||
demangled_name: None,
|
|
||||||
address: 12,
|
|
||||||
size: 12,
|
|
||||||
kind: Object,
|
|
||||||
section: Some(
|
|
||||||
2,
|
|
||||||
),
|
|
||||||
flags: FlagSet(Local),
|
|
||||||
align: None,
|
|
||||||
virtual_address: None,
|
|
||||||
},
|
|
||||||
Symbol {
|
|
||||||
name: "@59",
|
|
||||||
demangled_name: None,
|
|
||||||
address: 32,
|
|
||||||
size: 8,
|
|
||||||
kind: Object,
|
|
||||||
section: Some(
|
|
||||||
1,
|
|
||||||
),
|
|
||||||
flags: FlagSet(Local),
|
|
||||||
align: None,
|
|
||||||
virtual_address: None,
|
|
||||||
},
|
|
||||||
Symbol {
|
|
||||||
name: "@60",
|
|
||||||
demangled_name: None,
|
|
||||||
address: 24,
|
|
||||||
size: 12,
|
|
||||||
kind: Object,
|
|
||||||
section: Some(
|
|
||||||
2,
|
|
||||||
),
|
|
||||||
flags: FlagSet(Local),
|
|
||||||
align: None,
|
|
||||||
virtual_address: None,
|
|
||||||
},
|
|
||||||
Symbol {
|
|
||||||
name: "__destroy_arr",
|
|
||||||
demangled_name: None,
|
|
||||||
address: 0,
|
|
||||||
size: 120,
|
|
||||||
kind: Function,
|
|
||||||
section: Some(
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
flags: FlagSet(Global | HasExtra),
|
|
||||||
align: None,
|
|
||||||
virtual_address: None,
|
|
||||||
},
|
|
||||||
Symbol {
|
|
||||||
name: "__construct_array",
|
|
||||||
demangled_name: None,
|
|
||||||
address: 120,
|
|
||||||
size: 248,
|
|
||||||
kind: Function,
|
|
||||||
section: Some(
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
flags: FlagSet(Global | HasExtra),
|
|
||||||
align: None,
|
|
||||||
virtual_address: None,
|
|
||||||
},
|
|
||||||
Symbol {
|
|
||||||
name: "__dt__26__partial_array_destructorFv",
|
|
||||||
demangled_name: Some(
|
|
||||||
"__partial_array_destructor::~__partial_array_destructor()",
|
|
||||||
),
|
|
||||||
address: 368,
|
|
||||||
size: 184,
|
|
||||||
kind: Function,
|
|
||||||
section: Some(
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
flags: FlagSet(Global | Weak | HasExtra),
|
|
||||||
align: None,
|
|
||||||
virtual_address: None,
|
|
||||||
},
|
|
||||||
Symbol {
|
|
||||||
name: "__dl__FPv",
|
|
||||||
demangled_name: Some(
|
|
||||||
"operator delete(void*)",
|
|
||||||
),
|
|
||||||
address: 0,
|
|
||||||
size: 0,
|
|
||||||
kind: Unknown,
|
|
||||||
section: None,
|
|
||||||
flags: FlagSet(Global),
|
|
||||||
align: None,
|
|
||||||
virtual_address: None,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
sections: [
|
|
||||||
Section {
|
|
||||||
id: ".text-0",
|
|
||||||
name: ".text",
|
|
||||||
address: 0,
|
|
||||||
size: 552,
|
|
||||||
kind: Code,
|
|
||||||
data: SectionData(
|
|
||||||
552,
|
|
||||||
),
|
|
||||||
flags: FlagSet(),
|
|
||||||
align: Some(
|
|
||||||
4,
|
|
||||||
),
|
|
||||||
relocations: [
|
|
||||||
Relocation {
|
|
||||||
flags: Elf(
|
|
||||||
10,
|
|
||||||
),
|
|
||||||
address: 516,
|
|
||||||
target_symbol: 13,
|
|
||||||
addend: 0,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
line_info: {},
|
|
||||||
virtual_address: None,
|
|
||||||
},
|
|
||||||
Section {
|
|
||||||
id: "extab-0",
|
|
||||||
name: "extab",
|
|
||||||
address: 0,
|
|
||||||
size: 40,
|
|
||||||
kind: Data,
|
|
||||||
data: SectionData(
|
|
||||||
40,
|
|
||||||
),
|
|
||||||
flags: FlagSet(),
|
|
||||||
align: Some(
|
|
||||||
4,
|
|
||||||
),
|
|
||||||
relocations: [
|
|
||||||
Relocation {
|
|
||||||
flags: Elf(
|
|
||||||
1,
|
|
||||||
),
|
|
||||||
address: 28,
|
|
||||||
target_symbol: 12,
|
|
||||||
addend: 0,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
line_info: {},
|
|
||||||
virtual_address: None,
|
|
||||||
},
|
|
||||||
Section {
|
|
||||||
id: "extabindex-0",
|
|
||||||
name: "extabindex",
|
|
||||||
address: 0,
|
|
||||||
size: 36,
|
|
||||||
kind: Data,
|
|
||||||
data: SectionData(
|
|
||||||
36,
|
|
||||||
),
|
|
||||||
flags: FlagSet(),
|
|
||||||
align: Some(
|
|
||||||
4,
|
|
||||||
),
|
|
||||||
relocations: [
|
|
||||||
Relocation {
|
|
||||||
flags: Elf(
|
|
||||||
1,
|
|
||||||
),
|
|
||||||
address: 0,
|
|
||||||
target_symbol: 10,
|
|
||||||
addend: 0,
|
|
||||||
},
|
|
||||||
Relocation {
|
|
||||||
flags: Elf(
|
|
||||||
1,
|
|
||||||
),
|
|
||||||
address: 8,
|
|
||||||
target_symbol: 4,
|
|
||||||
addend: 0,
|
|
||||||
},
|
|
||||||
Relocation {
|
|
||||||
flags: Elf(
|
|
||||||
1,
|
|
||||||
),
|
|
||||||
address: 12,
|
|
||||||
target_symbol: 11,
|
|
||||||
addend: 0,
|
|
||||||
},
|
|
||||||
Relocation {
|
|
||||||
flags: Elf(
|
|
||||||
1,
|
|
||||||
),
|
|
||||||
address: 20,
|
|
||||||
target_symbol: 6,
|
|
||||||
addend: 0,
|
|
||||||
},
|
|
||||||
Relocation {
|
|
||||||
flags: Elf(
|
|
||||||
1,
|
|
||||||
),
|
|
||||||
address: 24,
|
|
||||||
target_symbol: 12,
|
|
||||||
addend: 0,
|
|
||||||
},
|
|
||||||
Relocation {
|
|
||||||
flags: Elf(
|
|
||||||
1,
|
|
||||||
),
|
|
||||||
address: 32,
|
|
||||||
target_symbol: 8,
|
|
||||||
addend: 0,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
line_info: {},
|
|
||||||
virtual_address: None,
|
|
||||||
},
|
|
||||||
Section {
|
|
||||||
id: ".rela.text-0",
|
|
||||||
name: ".rela.text",
|
|
||||||
address: 0,
|
|
||||||
size: 12,
|
|
||||||
kind: Unknown,
|
|
||||||
data: SectionData(
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
flags: FlagSet(),
|
|
||||||
align: Some(
|
|
||||||
4,
|
|
||||||
),
|
|
||||||
relocations: [],
|
|
||||||
line_info: {},
|
|
||||||
virtual_address: None,
|
|
||||||
},
|
|
||||||
Section {
|
|
||||||
id: ".relaextab-0",
|
|
||||||
name: ".relaextab",
|
|
||||||
address: 0,
|
|
||||||
size: 12,
|
|
||||||
kind: Unknown,
|
|
||||||
data: SectionData(
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
flags: FlagSet(),
|
|
||||||
align: Some(
|
|
||||||
4,
|
|
||||||
),
|
|
||||||
relocations: [],
|
|
||||||
line_info: {},
|
|
||||||
virtual_address: None,
|
|
||||||
},
|
|
||||||
Section {
|
|
||||||
id: ".relaextabindex-0",
|
|
||||||
name: ".relaextabindex",
|
|
||||||
address: 0,
|
|
||||||
size: 72,
|
|
||||||
kind: Unknown,
|
|
||||||
data: SectionData(
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
flags: FlagSet(),
|
|
||||||
align: Some(
|
|
||||||
4,
|
|
||||||
),
|
|
||||||
relocations: [],
|
|
||||||
line_info: {},
|
|
||||||
virtual_address: None,
|
|
||||||
},
|
|
||||||
Section {
|
|
||||||
id: ".symtab-0",
|
|
||||||
name: ".symtab",
|
|
||||||
address: 0,
|
|
||||||
size: 240,
|
|
||||||
kind: Unknown,
|
|
||||||
data: SectionData(
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
flags: FlagSet(),
|
|
||||||
align: Some(
|
|
||||||
4,
|
|
||||||
),
|
|
||||||
relocations: [],
|
|
||||||
line_info: {},
|
|
||||||
virtual_address: None,
|
|
||||||
},
|
|
||||||
Section {
|
|
||||||
id: ".strtab-0",
|
|
||||||
name: ".strtab",
|
|
||||||
address: 0,
|
|
||||||
size: 121,
|
|
||||||
kind: Unknown,
|
|
||||||
data: SectionData(
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
flags: FlagSet(),
|
|
||||||
align: Some(
|
|
||||||
1,
|
|
||||||
),
|
|
||||||
relocations: [],
|
|
||||||
line_info: {},
|
|
||||||
virtual_address: None,
|
|
||||||
},
|
|
||||||
Section {
|
|
||||||
id: ".shstrtab-0",
|
|
||||||
name: ".shstrtab",
|
|
||||||
address: 0,
|
|
||||||
size: 97,
|
|
||||||
kind: Unknown,
|
|
||||||
data: SectionData(
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
flags: FlagSet(),
|
|
||||||
align: Some(
|
|
||||||
1,
|
|
||||||
),
|
|
||||||
relocations: [],
|
|
||||||
line_info: {},
|
|
||||||
virtual_address: None,
|
|
||||||
},
|
|
||||||
Section {
|
|
||||||
id: ".comment-0",
|
|
||||||
name: ".comment",
|
|
||||||
address: 0,
|
|
||||||
size: 164,
|
|
||||||
kind: Unknown,
|
|
||||||
data: SectionData(
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
flags: FlagSet(),
|
|
||||||
align: Some(
|
|
||||||
1,
|
|
||||||
),
|
|
||||||
relocations: [],
|
|
||||||
line_info: {},
|
|
||||||
virtual_address: None,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
split_meta: None,
|
|
||||||
path: None,
|
|
||||||
timestamp: None,
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,71 +0,0 @@
|
|||||||
---
|
|
||||||
source: objdiff-core/tests/arch_ppc.rs
|
|
||||||
assertion_line: 20
|
|
||||||
expression: output
|
|
||||||
---
|
|
||||||
[(Address(0), Normal, 5), (Spacing(4), Normal, 0), (Opcode("srwi", 60), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("24")), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(4), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmpwi", 38), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(8), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 43), Normal, 10), (BranchDest(20), Normal, 0), (Basic(" ~>"), Rotating(0), 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(12), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 41), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(16), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 45), Normal, 10), (BranchDest(32), Normal, 0), (Basic(" ~>"), Rotating(1), 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(20), Normal, 5), (Basic(" ~> "), Rotating(0), 0), (Opcode("lis", 42), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@ha"), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(24), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 41), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@l"), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(28), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r0")), Normal, 0), (Basic(" <"), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic(">"), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(32), Normal, 5), (Basic(" ~> "), Rotating(1), 0), (Opcode("extrwi", 60), Normal, 10), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("8")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("8")), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(36), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 166), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), Bright, 0), (Basic("@sda21"), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(40), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmpwi", 38), Normal, 10), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(44), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 43), Normal, 10), (BranchDest(56), Normal, 0), (Basic(" ~>"), Rotating(2), 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(48), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 41), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(52), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 45), Normal, 10), (BranchDest(68), Normal, 0), (Basic(" ~>"), Rotating(3), 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(56), Normal, 5), (Basic(" ~> "), Rotating(2), 0), (Opcode("lis", 42), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@ha"), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(60), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 41), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@l"), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(64), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(" <"), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic(">"), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(68), Normal, 5), (Basic(" ~> "), Rotating(3), 0), (Opcode("extrwi", 60), Normal, 10), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("8")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("16")), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(72), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 41), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), Bright, 0), (Basic("@sda21"), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(76), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmpwi", 38), Normal, 10), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(80), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 166), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(1)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(84), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 43), Normal, 10), (BranchDest(96), Normal, 0), (Basic(" ~>"), Rotating(4), 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(88), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 41), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(92), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 45), Normal, 10), (BranchDest(108), Normal, 0), (Basic(" ~>"), Rotating(5), 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(96), Normal, 5), (Basic(" ~> "), Rotating(4), 0), (Opcode("lis", 42), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@ha"), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(100), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 41), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@l"), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(104), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(" <"), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic(">"), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(108), Normal, 5), (Basic(" ~> "), Rotating(5), 0), (Opcode("clrlwi", 60), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("24")), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(112), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 41), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), Bright, 0), (Basic("@sda21"), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(116), Normal, 5), (Spacing(4), Normal, 0), (Opcode("cmpwi", 38), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(120), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 166), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(2)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(124), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 43), Normal, 10), (BranchDest(136), Normal, 0), (Basic(" ~>"), Rotating(6), 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(128), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 41), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(-1)), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(132), Normal, 5), (Spacing(4), Normal, 0), (Opcode("b", 45), Normal, 10), (BranchDest(148), Normal, 0), (Basic(" ~>"), Rotating(7), 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(136), Normal, 5), (Basic(" ~> "), Rotating(6), 0), (Opcode("lis", 42), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@ha"), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(140), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 41), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@l"), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(144), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(" <"), Normal, 0), (Symbol(Symbol { name: "__upper_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic(">"), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(148), Normal, 5), (Basic(" ~> "), Rotating(7), 0), (Opcode("li", 41), Normal, 10), (Argument(Opaque("r5")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), Bright, 0), (Basic("@sda21"), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(152), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 41), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(0)), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(156), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 166), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(3)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(160), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lis", 42), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__ctype_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@ha"), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(164), Normal, 5), (Spacing(4), Normal, 0), (Opcode("addi", 41), Normal, 10), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "__ctype_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic("@l"), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(168), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 166), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(4)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(172), Normal, 5), (Spacing(4), Normal, 0), (Opcode("li", 41), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(45)), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(176), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbz", 162), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), Bright, 0), (Basic("@sda21"), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(180), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(" <"), Normal, 0), (Symbol(Symbol { name: "__ctype_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic(">"), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(184), Normal, 5), (Spacing(4), Normal, 0), (Opcode("andi.", 66), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Unsigned(220)), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(188), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 43), Normal, 10), (BranchDest(196), Normal, 0), (Basic(" ~>"), Rotating(8), 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(192), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 166), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(0)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(196), Normal, 5), (Basic(" ~> "), Rotating(8), 0), (Opcode("lbzu", 163), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(1)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(200), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(" <"), Normal, 0), (Symbol(Symbol { name: "__ctype_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic(">"), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(204), Normal, 5), (Spacing(4), Normal, 0), (Opcode("andi.", 66), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Unsigned(220)), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(208), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 43), Normal, 10), (BranchDest(216), Normal, 0), (Basic(" ~>"), Rotating(9), 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(212), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 166), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(0)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(216), Normal, 5), (Basic(" ~> "), Rotating(9), 0), (Opcode("lbzu", 163), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(1)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(220), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(" <"), Normal, 0), (Symbol(Symbol { name: "__ctype_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic(">"), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(224), Normal, 5), (Spacing(4), Normal, 0), (Opcode("andi.", 66), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Unsigned(220)), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(228), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 43), Normal, 10), (BranchDest(236), Normal, 0), (Basic(" ~>"), Rotating(10), 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(232), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 166), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(0)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(236), Normal, 5), (Basic(" ~> "), Rotating(10), 0), (Opcode("lbzu", 163), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(1)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(240), Normal, 5), (Spacing(4), Normal, 0), (Opcode("lbzx", 94), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r4")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(" <"), Normal, 0), (Symbol(Symbol { name: "__ctype_map", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: Some(0) }), Bright, 0), (Basic(">"), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(244), Normal, 5), (Spacing(4), Normal, 0), (Opcode("andi.", 66), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Unsigned(220)), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(248), Normal, 5), (Spacing(4), Normal, 0), (Opcode("bne", 43), Normal, 10), (BranchDest(256), Normal, 0), (Basic(" ~>"), Rotating(11), 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(252), Normal, 5), (Spacing(4), Normal, 0), (Opcode("stb", 166), Normal, 10), (Argument(Opaque("r0")), Normal, 0), (Basic(", "), Normal, 0), (Argument(Signed(0)), Normal, 0), (Basic("("), Normal, 0), (Argument(Opaque("r5")), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(256), Normal, 5), (Basic(" ~> "), Rotating(11), 0), (Opcode("li", 41), Normal, 10), (Argument(Opaque("r3")), Normal, 0), (Basic(", "), Normal, 0), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), Bright, 0), (Basic("@sda21"), Normal, 0), (Eol, Normal, 0)]
|
|
||||||
[(Address(260), Normal, 5), (Spacing(4), Normal, 0), (Opcode("blr", 47), Normal, 10), (Eol, Normal, 0)]
|
|
@ -1,584 +0,0 @@
|
|||||||
---
|
|
||||||
source: objdiff-core/tests/arch_ppc.rs
|
|
||||||
expression: obj
|
|
||||||
---
|
|
||||||
Object {
|
|
||||||
arch: ArchPpc {
|
|
||||||
extab: None,
|
|
||||||
},
|
|
||||||
endianness: Big,
|
|
||||||
symbols: [
|
|
||||||
Symbol {
|
|
||||||
name: "IObj.cpp",
|
|
||||||
demangled_name: None,
|
|
||||||
address: 0,
|
|
||||||
size: 0,
|
|
||||||
kind: Unknown,
|
|
||||||
section: None,
|
|
||||||
flags: FlagSet(Local),
|
|
||||||
align: None,
|
|
||||||
virtual_address: Some(
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
Symbol {
|
|
||||||
name: "[.text]",
|
|
||||||
demangled_name: None,
|
|
||||||
address: 0,
|
|
||||||
size: 0,
|
|
||||||
kind: Section,
|
|
||||||
section: Some(
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
flags: FlagSet(Local),
|
|
||||||
align: None,
|
|
||||||
virtual_address: Some(
|
|
||||||
2150895620,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
Symbol {
|
|
||||||
name: "[.ctors]",
|
|
||||||
demangled_name: None,
|
|
||||||
address: 0,
|
|
||||||
size: 4,
|
|
||||||
kind: Section,
|
|
||||||
section: Some(
|
|
||||||
1,
|
|
||||||
),
|
|
||||||
flags: FlagSet(Local),
|
|
||||||
align: None,
|
|
||||||
virtual_address: Some(
|
|
||||||
2151461704,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
Symbol {
|
|
||||||
name: "[.sbss]",
|
|
||||||
demangled_name: None,
|
|
||||||
address: 0,
|
|
||||||
size: 0,
|
|
||||||
kind: Section,
|
|
||||||
section: Some(
|
|
||||||
2,
|
|
||||||
),
|
|
||||||
flags: FlagSet(Local),
|
|
||||||
align: None,
|
|
||||||
virtual_address: Some(
|
|
||||||
2153420048,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
Symbol {
|
|
||||||
name: "__sinit_IObj_cpp",
|
|
||||||
demangled_name: None,
|
|
||||||
address: 264,
|
|
||||||
size: 20,
|
|
||||||
kind: Function,
|
|
||||||
section: Some(
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
flags: FlagSet(Local),
|
|
||||||
align: None,
|
|
||||||
virtual_address: Some(
|
|
||||||
2150895884,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
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,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
Symbol {
|
|
||||||
name: "Type2Text__10SObjectTagFUi",
|
|
||||||
demangled_name: Some(
|
|
||||||
"SObjectTag::Type2Text(unsigned int)",
|
|
||||||
),
|
|
||||||
address: 0,
|
|
||||||
size: 264,
|
|
||||||
kind: Function,
|
|
||||||
section: Some(
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
flags: FlagSet(Global),
|
|
||||||
align: None,
|
|
||||||
virtual_address: Some(
|
|
||||||
2150895620,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
Symbol {
|
|
||||||
name: "gkInvalidObjectTag",
|
|
||||||
demangled_name: None,
|
|
||||||
address: 0,
|
|
||||||
size: 8,
|
|
||||||
kind: Object,
|
|
||||||
section: Some(
|
|
||||||
2,
|
|
||||||
),
|
|
||||||
flags: FlagSet(Global),
|
|
||||||
align: None,
|
|
||||||
virtual_address: Some(
|
|
||||||
2153420048,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
Symbol {
|
|
||||||
name: "__upper_map",
|
|
||||||
demangled_name: None,
|
|
||||||
address: 0,
|
|
||||||
size: 0,
|
|
||||||
kind: Unknown,
|
|
||||||
section: None,
|
|
||||||
flags: FlagSet(Global),
|
|
||||||
align: None,
|
|
||||||
virtual_address: Some(
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
Symbol {
|
|
||||||
name: "__ctype_map",
|
|
||||||
demangled_name: None,
|
|
||||||
address: 0,
|
|
||||||
size: 0,
|
|
||||||
kind: Unknown,
|
|
||||||
section: None,
|
|
||||||
flags: FlagSet(Global),
|
|
||||||
align: None,
|
|
||||||
virtual_address: Some(
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
sections: [
|
|
||||||
Section {
|
|
||||||
id: ".text-0",
|
|
||||||
name: ".text",
|
|
||||||
address: 0,
|
|
||||||
size: 284,
|
|
||||||
kind: Code,
|
|
||||||
data: SectionData(
|
|
||||||
284,
|
|
||||||
),
|
|
||||||
flags: FlagSet(),
|
|
||||||
align: Some(
|
|
||||||
4,
|
|
||||||
),
|
|
||||||
relocations: [
|
|
||||||
Relocation {
|
|
||||||
flags: Elf(
|
|
||||||
6,
|
|
||||||
),
|
|
||||||
address: 22,
|
|
||||||
target_symbol: 8,
|
|
||||||
addend: 0,
|
|
||||||
},
|
|
||||||
Relocation {
|
|
||||||
flags: Elf(
|
|
||||||
4,
|
|
||||||
),
|
|
||||||
address: 26,
|
|
||||||
target_symbol: 8,
|
|
||||||
addend: 0,
|
|
||||||
},
|
|
||||||
Relocation {
|
|
||||||
flags: Elf(
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
address: 28,
|
|
||||||
target_symbol: 8,
|
|
||||||
addend: 0,
|
|
||||||
},
|
|
||||||
Relocation {
|
|
||||||
flags: Elf(
|
|
||||||
109,
|
|
||||||
),
|
|
||||||
address: 36,
|
|
||||||
target_symbol: 5,
|
|
||||||
addend: 0,
|
|
||||||
},
|
|
||||||
Relocation {
|
|
||||||
flags: Elf(
|
|
||||||
6,
|
|
||||||
),
|
|
||||||
address: 58,
|
|
||||||
target_symbol: 8,
|
|
||||||
addend: 0,
|
|
||||||
},
|
|
||||||
Relocation {
|
|
||||||
flags: Elf(
|
|
||||||
4,
|
|
||||||
),
|
|
||||||
address: 62,
|
|
||||||
target_symbol: 8,
|
|
||||||
addend: 0,
|
|
||||||
},
|
|
||||||
Relocation {
|
|
||||||
flags: Elf(
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
address: 64,
|
|
||||||
target_symbol: 8,
|
|
||||||
addend: 0,
|
|
||||||
},
|
|
||||||
Relocation {
|
|
||||||
flags: Elf(
|
|
||||||
109,
|
|
||||||
),
|
|
||||||
address: 72,
|
|
||||||
target_symbol: 5,
|
|
||||||
addend: 0,
|
|
||||||
},
|
|
||||||
Relocation {
|
|
||||||
flags: Elf(
|
|
||||||
6,
|
|
||||||
),
|
|
||||||
address: 98,
|
|
||||||
target_symbol: 8,
|
|
||||||
addend: 0,
|
|
||||||
},
|
|
||||||
Relocation {
|
|
||||||
flags: Elf(
|
|
||||||
4,
|
|
||||||
),
|
|
||||||
address: 102,
|
|
||||||
target_symbol: 8,
|
|
||||||
addend: 0,
|
|
||||||
},
|
|
||||||
Relocation {
|
|
||||||
flags: Elf(
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
address: 104,
|
|
||||||
target_symbol: 8,
|
|
||||||
addend: 0,
|
|
||||||
},
|
|
||||||
Relocation {
|
|
||||||
flags: Elf(
|
|
||||||
109,
|
|
||||||
),
|
|
||||||
address: 112,
|
|
||||||
target_symbol: 5,
|
|
||||||
addend: 0,
|
|
||||||
},
|
|
||||||
Relocation {
|
|
||||||
flags: Elf(
|
|
||||||
6,
|
|
||||||
),
|
|
||||||
address: 138,
|
|
||||||
target_symbol: 8,
|
|
||||||
addend: 0,
|
|
||||||
},
|
|
||||||
Relocation {
|
|
||||||
flags: Elf(
|
|
||||||
4,
|
|
||||||
),
|
|
||||||
address: 142,
|
|
||||||
target_symbol: 8,
|
|
||||||
addend: 0,
|
|
||||||
},
|
|
||||||
Relocation {
|
|
||||||
flags: Elf(
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
address: 144,
|
|
||||||
target_symbol: 8,
|
|
||||||
addend: 0,
|
|
||||||
},
|
|
||||||
Relocation {
|
|
||||||
flags: Elf(
|
|
||||||
109,
|
|
||||||
),
|
|
||||||
address: 148,
|
|
||||||
target_symbol: 5,
|
|
||||||
addend: 0,
|
|
||||||
},
|
|
||||||
Relocation {
|
|
||||||
flags: Elf(
|
|
||||||
6,
|
|
||||||
),
|
|
||||||
address: 162,
|
|
||||||
target_symbol: 9,
|
|
||||||
addend: 0,
|
|
||||||
},
|
|
||||||
Relocation {
|
|
||||||
flags: Elf(
|
|
||||||
4,
|
|
||||||
),
|
|
||||||
address: 166,
|
|
||||||
target_symbol: 9,
|
|
||||||
addend: 0,
|
|
||||||
},
|
|
||||||
Relocation {
|
|
||||||
flags: Elf(
|
|
||||||
109,
|
|
||||||
),
|
|
||||||
address: 176,
|
|
||||||
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,
|
|
||||||
),
|
|
||||||
address: 256,
|
|
||||||
target_symbol: 5,
|
|
||||||
addend: 0,
|
|
||||||
},
|
|
||||||
Relocation {
|
|
||||||
flags: Elf(
|
|
||||||
109,
|
|
||||||
),
|
|
||||||
address: 268,
|
|
||||||
target_symbol: 7,
|
|
||||||
addend: 0,
|
|
||||||
},
|
|
||||||
Relocation {
|
|
||||||
flags: Elf(
|
|
||||||
109,
|
|
||||||
),
|
|
||||||
address: 272,
|
|
||||||
target_symbol: 7,
|
|
||||||
addend: 0,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
line_info: {},
|
|
||||||
virtual_address: Some(
|
|
||||||
2150895620,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
Section {
|
|
||||||
id: ".ctors-0",
|
|
||||||
name: ".ctors",
|
|
||||||
address: 0,
|
|
||||||
size: 4,
|
|
||||||
kind: Data,
|
|
||||||
data: SectionData(
|
|
||||||
4,
|
|
||||||
),
|
|
||||||
flags: FlagSet(),
|
|
||||||
align: Some(
|
|
||||||
4,
|
|
||||||
),
|
|
||||||
relocations: [
|
|
||||||
Relocation {
|
|
||||||
flags: Elf(
|
|
||||||
1,
|
|
||||||
),
|
|
||||||
address: 0,
|
|
||||||
target_symbol: 4,
|
|
||||||
addend: 0,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
line_info: {},
|
|
||||||
virtual_address: Some(
|
|
||||||
2151461704,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
Section {
|
|
||||||
id: ".sbss-0",
|
|
||||||
name: ".sbss",
|
|
||||||
address: 0,
|
|
||||||
size: 16,
|
|
||||||
kind: Bss,
|
|
||||||
data: SectionData(
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
flags: FlagSet(),
|
|
||||||
align: Some(
|
|
||||||
8,
|
|
||||||
),
|
|
||||||
relocations: [],
|
|
||||||
line_info: {},
|
|
||||||
virtual_address: Some(
|
|
||||||
2153420048,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
Section {
|
|
||||||
id: ".rela.text-0",
|
|
||||||
name: ".rela.text",
|
|
||||||
address: 0,
|
|
||||||
size: 216,
|
|
||||||
kind: Unknown,
|
|
||||||
data: SectionData(
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
flags: FlagSet(),
|
|
||||||
align: Some(
|
|
||||||
4,
|
|
||||||
),
|
|
||||||
relocations: [],
|
|
||||||
line_info: {},
|
|
||||||
virtual_address: None,
|
|
||||||
},
|
|
||||||
Section {
|
|
||||||
id: ".rela.ctors-0",
|
|
||||||
name: ".rela.ctors",
|
|
||||||
address: 0,
|
|
||||||
size: 12,
|
|
||||||
kind: Unknown,
|
|
||||||
data: SectionData(
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
flags: FlagSet(),
|
|
||||||
align: Some(
|
|
||||||
4,
|
|
||||||
),
|
|
||||||
relocations: [],
|
|
||||||
line_info: {},
|
|
||||||
virtual_address: None,
|
|
||||||
},
|
|
||||||
Section {
|
|
||||||
id: ".symtab-0",
|
|
||||||
name: ".symtab",
|
|
||||||
address: 0,
|
|
||||||
size: 176,
|
|
||||||
kind: Unknown,
|
|
||||||
data: SectionData(
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
flags: FlagSet(),
|
|
||||||
align: Some(
|
|
||||||
4,
|
|
||||||
),
|
|
||||||
relocations: [],
|
|
||||||
line_info: {},
|
|
||||||
virtual_address: None,
|
|
||||||
},
|
|
||||||
Section {
|
|
||||||
id: ".strtab-0",
|
|
||||||
name: ".strtab",
|
|
||||||
address: 0,
|
|
||||||
size: 105,
|
|
||||||
kind: Unknown,
|
|
||||||
data: SectionData(
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
flags: FlagSet(),
|
|
||||||
align: Some(
|
|
||||||
1,
|
|
||||||
),
|
|
||||||
relocations: [],
|
|
||||||
line_info: {},
|
|
||||||
virtual_address: None,
|
|
||||||
},
|
|
||||||
Section {
|
|
||||||
id: ".shstrtab-0",
|
|
||||||
name: ".shstrtab",
|
|
||||||
address: 0,
|
|
||||||
size: 77,
|
|
||||||
kind: Unknown,
|
|
||||||
data: SectionData(
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
flags: FlagSet(),
|
|
||||||
align: Some(
|
|
||||||
1,
|
|
||||||
),
|
|
||||||
relocations: [],
|
|
||||||
line_info: {},
|
|
||||||
virtual_address: None,
|
|
||||||
},
|
|
||||||
Section {
|
|
||||||
id: ".comment-0",
|
|
||||||
name: ".comment",
|
|
||||||
address: 0,
|
|
||||||
size: 132,
|
|
||||||
kind: Unknown,
|
|
||||||
data: SectionData(
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
flags: FlagSet(),
|
|
||||||
align: Some(
|
|
||||||
1,
|
|
||||||
),
|
|
||||||
relocations: [],
|
|
||||||
line_info: {},
|
|
||||||
virtual_address: None,
|
|
||||||
},
|
|
||||||
Section {
|
|
||||||
id: ".note.split-0",
|
|
||||||
name: ".note.split",
|
|
||||||
address: 0,
|
|
||||||
size: 152,
|
|
||||||
kind: Unknown,
|
|
||||||
data: SectionData(
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
flags: FlagSet(),
|
|
||||||
align: Some(
|
|
||||||
4,
|
|
||||||
),
|
|
||||||
relocations: [],
|
|
||||||
line_info: {},
|
|
||||||
virtual_address: None,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
split_meta: Some(
|
|
||||||
SplitMeta {
|
|
||||||
generator: Some(
|
|
||||||
"decomp-toolkit 1.4.0",
|
|
||||||
),
|
|
||||||
module_name: Some(
|
|
||||||
"main",
|
|
||||||
),
|
|
||||||
module_id: Some(
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
virtual_addresses: Some(
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
2150895620,
|
|
||||||
2151461704,
|
|
||||||
2153420048,
|
|
||||||
2150895884,
|
|
||||||
2153420056,
|
|
||||||
2150895620,
|
|
||||||
2153420048,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
],
|
|
||||||
),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
path: None,
|
|
||||||
timestamp: None,
|
|
||||||
}
|
|
@ -1,210 +0,0 @@
|
|||||||
---
|
|
||||||
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,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
]
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user