mirror of
https://github.com/encounter/objdiff.git
synced 2025-12-17 17:05:29 +00:00
Compare commits
5 Commits
v2.0.0-bet
...
v2.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
| e1ae369d17 | |||
| ce05d6d6c0 | |||
| c16a926d9b | |||
|
|
a32d99923c | ||
| 68606dfdcb |
109
.github/workflows/build.yaml
vendored
109
.github/workflows/build.yaml
vendored
@@ -25,6 +25,21 @@ jobs:
|
|||||||
sudo apt-get -y install libgtk-3-dev
|
sudo apt-get -y install libgtk-3-dev
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
- name: Check git tag against Cargo version
|
||||||
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -eou pipefail
|
||||||
|
tag='${{github.ref}}'
|
||||||
|
tag="${tag#refs/tags/}"
|
||||||
|
for file in */Cargo.toml; do
|
||||||
|
version=$(grep '^version' $file | head -1 | awk -F' = ' '{print $2}' | tr -d '"')
|
||||||
|
version="v$version"
|
||||||
|
if [ "$tag" != "$version" ]; then
|
||||||
|
echo "::error::Git tag doesn't match the Cargo version! ($tag != $version)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
- name: Setup Rust toolchain
|
- name: Setup Rust toolchain
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@stable
|
||||||
with:
|
with:
|
||||||
@@ -100,8 +115,82 @@ jobs:
|
|||||||
SCCACHE_GHA_ENABLED: "true"
|
SCCACHE_GHA_ENABLED: "true"
|
||||||
run: cargo test --release
|
run: cargo test --release
|
||||||
|
|
||||||
build:
|
build-cli:
|
||||||
name: Build
|
name: Build objdiff-cli
|
||||||
|
env:
|
||||||
|
CARGO_BIN_NAME: objdiff-cli
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- platform: ubuntu-latest
|
||||||
|
target: x86_64-unknown-linux-musl
|
||||||
|
name: linux-x86_64
|
||||||
|
build: zigbuild
|
||||||
|
features: default
|
||||||
|
- platform: ubuntu-latest
|
||||||
|
target: i686-unknown-linux-musl
|
||||||
|
name: linux-i686
|
||||||
|
build: zigbuild
|
||||||
|
features: default
|
||||||
|
- platform: ubuntu-latest
|
||||||
|
target: aarch64-unknown-linux-musl
|
||||||
|
name: linux-aarch64
|
||||||
|
build: zigbuild
|
||||||
|
features: default
|
||||||
|
- platform: ubuntu-latest
|
||||||
|
target: armv7-unknown-linux-musleabi
|
||||||
|
name: linux-armv7l
|
||||||
|
build: zigbuild
|
||||||
|
features: default
|
||||||
|
- platform: windows-latest
|
||||||
|
target: x86_64-pc-windows-msvc
|
||||||
|
name: windows-x86_64
|
||||||
|
build: build
|
||||||
|
features: default
|
||||||
|
- platform: windows-latest
|
||||||
|
target: aarch64-pc-windows-msvc
|
||||||
|
name: windows-arm64
|
||||||
|
build: build
|
||||||
|
features: default
|
||||||
|
- platform: macos-latest
|
||||||
|
target: x86_64-apple-darwin
|
||||||
|
name: macos-x86_64
|
||||||
|
build: build
|
||||||
|
features: default
|
||||||
|
- platform: macos-latest
|
||||||
|
target: aarch64-apple-darwin
|
||||||
|
name: macos-arm64
|
||||||
|
build: build
|
||||||
|
features: default
|
||||||
|
fail-fast: false
|
||||||
|
runs-on: ${{ matrix.platform }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Install cargo-zigbuild
|
||||||
|
if: matrix.build == 'zigbuild'
|
||||||
|
run: pip install ziglang==0.13.0 cargo-zigbuild==0.19.1
|
||||||
|
- name: Setup Rust toolchain
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
with:
|
||||||
|
targets: ${{ matrix.target }}
|
||||||
|
- name: Cargo build
|
||||||
|
run: >
|
||||||
|
cargo ${{ matrix.build }} --profile ${{ env.BUILD_PROFILE }} --target ${{ matrix.target }}
|
||||||
|
--bin ${{ env.CARGO_BIN_NAME }} --features ${{ matrix.features }}
|
||||||
|
- name: Upload artifacts
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: ${{ env.CARGO_BIN_NAME }}-${{ matrix.name }}
|
||||||
|
path: |
|
||||||
|
${{ env.CARGO_TARGET_DIR }}/${{ matrix.target }}/${{ env.BUILD_PROFILE }}/${{ env.CARGO_BIN_NAME }}
|
||||||
|
${{ env.CARGO_TARGET_DIR }}/${{ matrix.target }}/${{ env.BUILD_PROFILE }}/${{ env.CARGO_BIN_NAME }}.exe
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
|
build-gui:
|
||||||
|
name: Build objdiff-gui
|
||||||
|
env:
|
||||||
|
CARGO_BIN_NAME: objdiff
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
@@ -144,23 +233,21 @@ jobs:
|
|||||||
SCCACHE_GHA_ENABLED: "true"
|
SCCACHE_GHA_ENABLED: "true"
|
||||||
run: >
|
run: >
|
||||||
cargo build --profile ${{ env.BUILD_PROFILE }} --target ${{ matrix.target }}
|
cargo build --profile ${{ env.BUILD_PROFILE }} --target ${{ matrix.target }}
|
||||||
--bin objdiff-cli --bin objdiff --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: ${{ matrix.name }}
|
name: ${{ env.CARGO_BIN_NAME }}-${{ matrix.name }}
|
||||||
path: |
|
path: |
|
||||||
${{ env.CARGO_TARGET_DIR }}/${{ matrix.target }}/${{ env.BUILD_PROFILE }}/objdiff-cli
|
${{ env.CARGO_TARGET_DIR }}/${{ matrix.target }}/${{ env.BUILD_PROFILE }}/${{ env.CARGO_BIN_NAME }}
|
||||||
${{ env.CARGO_TARGET_DIR }}/${{ matrix.target }}/${{ env.BUILD_PROFILE }}/objdiff-cli.exe
|
${{ env.CARGO_TARGET_DIR }}/${{ matrix.target }}/${{ env.BUILD_PROFILE }}/${{ env.CARGO_BIN_NAME }}.exe
|
||||||
${{ env.CARGO_TARGET_DIR }}/${{ matrix.target }}/${{ env.BUILD_PROFILE }}/objdiff
|
|
||||||
${{ env.CARGO_TARGET_DIR }}/${{ matrix.target }}/${{ env.BUILD_PROFILE }}/objdiff.exe
|
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
release:
|
release:
|
||||||
name: Release
|
name: Release
|
||||||
if: startsWith(github.ref, 'refs/tags/')
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [ build ]
|
needs: [ check, build-cli, build-gui ]
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
steps:
|
steps:
|
||||||
@@ -183,7 +270,9 @@ jobs:
|
|||||||
else
|
else
|
||||||
ext=".$ext"
|
ext=".$ext"
|
||||||
fi
|
fi
|
||||||
dst="../out/${name}-${dir%/}${ext}"
|
arch="${dir%/}" # remove trailing slash
|
||||||
|
arch="${arch##"$name-"}" # remove bin name
|
||||||
|
dst="../out/${name}-${arch}${ext}"
|
||||||
mv "$file" "$dst"
|
mv "$file" "$dst"
|
||||||
done
|
done
|
||||||
done
|
done
|
||||||
|
|||||||
12
Cargo.lock
generated
12
Cargo.lock
generated
@@ -2835,7 +2835,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "objdiff-cli"
|
name = "objdiff-cli"
|
||||||
version = "2.0.0-beta.5"
|
version = "2.0.0-beta.6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"argp",
|
"argp",
|
||||||
@@ -2856,7 +2856,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "objdiff-core"
|
name = "objdiff-core"
|
||||||
version = "2.0.0-beta.5"
|
version = "2.0.0-beta.6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"arm-attr",
|
"arm-attr",
|
||||||
@@ -2875,7 +2875,7 @@ dependencies = [
|
|||||||
"memmap2",
|
"memmap2",
|
||||||
"msvc-demangler",
|
"msvc-demangler",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"object 0.35.0",
|
"object 0.36.4",
|
||||||
"pbjson",
|
"pbjson",
|
||||||
"pbjson-build",
|
"pbjson-build",
|
||||||
"ppc750cl",
|
"ppc750cl",
|
||||||
@@ -2895,7 +2895,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "objdiff-gui"
|
name = "objdiff-gui"
|
||||||
version = "2.0.0-beta.5"
|
version = "2.0.0-beta.6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
@@ -2950,9 +2950,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "object"
|
name = "object"
|
||||||
version = "0.35.0"
|
version = "0.36.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b8ec7ab813848ba4522158d5517a6093db1ded27575b070f4177b8d12b41db5e"
|
checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|||||||
70
README.md
70
README.md
@@ -49,91 +49,89 @@ See [Configuration](#configuration) for more information.
|
|||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
While **not required** (most settings can be specified in the UI), projects can add an `objdiff.json` (or
|
While **not required** (most settings can be specified in the UI), projects can add an `objdiff.json` file to configure the tool automatically. The configuration file must be located in
|
||||||
`objdiff.yaml`, `objdiff.yml`) file to configure the tool automatically. The configuration file must be located in
|
|
||||||
the root project directory.
|
the root project directory.
|
||||||
|
|
||||||
If your project has a generator script (e.g. `configure.py`), it's recommended to generate the objdiff configuration
|
If your project has a generator script (e.g. `configure.py`), it's recommended to generate the objdiff configuration
|
||||||
file as well. You can then add `objdiff.json` to your `.gitignore` to prevent it from being committed.
|
file as well. You can then add `objdiff.json` to your `.gitignore` to prevent it from being committed.
|
||||||
|
|
||||||
```json5
|
```json
|
||||||
// objdiff.json
|
|
||||||
{
|
{
|
||||||
|
"$schema": "https://raw.githubusercontent.com/encounter/objdiff/main/config.schema.json",
|
||||||
"custom_make": "ninja",
|
"custom_make": "ninja",
|
||||||
"custom_args": [
|
"custom_args": [
|
||||||
"-d",
|
"-d",
|
||||||
"keeprsp"
|
"keeprsp"
|
||||||
],
|
],
|
||||||
|
"build_target": false,
|
||||||
// Only required if objects use "path" instead of "target_path" and "base_path".
|
"build_base": true,
|
||||||
"target_dir": "build/asm",
|
|
||||||
"base_dir": "build/src",
|
|
||||||
|
|
||||||
"build_target": true,
|
|
||||||
"watch_patterns": [
|
"watch_patterns": [
|
||||||
"*.c",
|
"*.c",
|
||||||
"*.cp",
|
"*.cp",
|
||||||
"*.cpp",
|
"*.cpp",
|
||||||
|
"*.cxx",
|
||||||
"*.h",
|
"*.h",
|
||||||
|
"*.hp",
|
||||||
"*.hpp",
|
"*.hpp",
|
||||||
"*.py"
|
"*.hxx",
|
||||||
|
"*.s",
|
||||||
|
"*.S",
|
||||||
|
"*.asm",
|
||||||
|
"*.inc",
|
||||||
|
"*.py",
|
||||||
|
"*.yml",
|
||||||
|
"*.txt",
|
||||||
|
"*.json"
|
||||||
],
|
],
|
||||||
"objects": [
|
"units": [
|
||||||
{
|
{
|
||||||
"name": "main/MetroTRK/mslsupp",
|
"name": "main/MetroTRK/mslsupp",
|
||||||
|
|
||||||
// Option 1: Relative to target_dir and base_dir
|
|
||||||
"path": "MetroTRK/mslsupp.o",
|
|
||||||
// Option 2: Explicit paths from project root
|
|
||||||
// Useful for more complex directory layouts
|
|
||||||
"target_path": "build/asm/MetroTRK/mslsupp.o",
|
"target_path": "build/asm/MetroTRK/mslsupp.o",
|
||||||
"base_path": "build/src/MetroTRK/mslsupp.o",
|
"base_path": "build/src/MetroTRK/mslsupp.o",
|
||||||
|
"metadata": {}
|
||||||
"reverse_fn_order": false
|
}
|
||||||
},
|
|
||||||
// ...
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Schema
|
||||||
|
|
||||||
|
View [config.schema.json](config.schema.json) for all available options. The below list is a summary of the most important options.
|
||||||
|
|
||||||
`custom_make` _(optional)_: By default, objdiff will use `make` to build the project.
|
`custom_make` _(optional)_: By default, objdiff will use `make` to build the project.
|
||||||
If the project uses a different build system (e.g. `ninja`), specify it here.
|
If the project uses a different build system (e.g. `ninja`), specify it here.
|
||||||
The build command will be `[custom_make] [custom_args] path/to/object.o`.
|
The build command will be `[custom_make] [custom_args] path/to/object.o`.
|
||||||
|
|
||||||
`custom_args` _(optional)_: Additional arguments to pass to the build command prior to the object path.
|
`custom_args` _(optional)_: Additional arguments to pass to the build command prior to the object path.
|
||||||
|
|
||||||
`target_dir` _(optional)_: Relative from the root of the project, this where the "target" or "expected" objects are located.
|
|
||||||
These are the **intended result** of the match.
|
|
||||||
|
|
||||||
`base_dir` _(optional)_: Relative from the root of the project, this is where the "base" or "actual" objects are located.
|
|
||||||
These are objects built from the **current source code**.
|
|
||||||
|
|
||||||
`build_target`: If true, objdiff will tell the build system to build the target objects before diffing (e.g.
|
`build_target`: If true, objdiff will tell the build system to build the target objects before diffing (e.g.
|
||||||
`make path/to/target.o`).
|
`make path/to/target.o`).
|
||||||
This is useful if the target objects are not built by default or can change based on project configuration or edits
|
This is useful if the target objects are not built by default or can change based on project configuration or edits
|
||||||
to assembly files.
|
to assembly files.
|
||||||
Requires the build system to be configured properly.
|
Requires the build system to be configured properly.
|
||||||
|
|
||||||
|
`build_base`: If true, objdiff will tell the build system to build the base objects before diffing (e.g. `make path/to/base.o`).
|
||||||
|
It's unlikely you'll want to disable this, unless you're using an external tool to rebuild the base object on source file changes.
|
||||||
|
|
||||||
`watch_patterns` _(optional)_: A list of glob patterns to watch for changes.
|
`watch_patterns` _(optional)_: A list of glob patterns to watch for changes.
|
||||||
([Supported syntax](https://docs.rs/globset/latest/globset/#syntax))
|
([Supported syntax](https://docs.rs/globset/latest/globset/#syntax))
|
||||||
If any of these files change, objdiff will automatically rebuild the objects and re-compare them.
|
If any of these files change, objdiff will automatically rebuild the objects and re-compare them.
|
||||||
If not specified, objdiff will use the default patterns listed above.
|
If not specified, objdiff will use the default patterns listed above.
|
||||||
|
|
||||||
`objects` _(optional)_: If specified, objdiff will display a list of objects in the sidebar for easy navigation.
|
`units` _(optional)_: If specified, objdiff will display a list of objects in the sidebar for easy navigation.
|
||||||
|
|
||||||
> `name` _(optional)_: The name of the object in the UI. If not specified, the object's `path` will be used.
|
> `name` _(optional)_: The name of the object in the UI. If not specified, the object's `path` will be used.
|
||||||
>
|
>
|
||||||
> `path`: Relative path to the object from the `target_dir` and `base_dir`.
|
> `target_path`: Path to the "target" or "expected" object from the project root.
|
||||||
> Requires `target_dir` and `base_dir` to be specified.
|
> This object is the **intended result** of the match.
|
||||||
>
|
>
|
||||||
> `target_path`: Path to the target object from the project root.
|
> `base_path`: Path to the "base" or "actual" object from the project root.
|
||||||
> Required if `path` is not specified.
|
> This object is built from the **current source code**.
|
||||||
>
|
>
|
||||||
> `base_path`: Path to the base object from the project root.
|
> `metadata.auto_generated` _(optional)_: Hides the object from the object list, but still includes it in reports.
|
||||||
> Required if `path` is not specified.
|
|
||||||
>
|
>
|
||||||
> `reverse_fn_order` _(optional)_: Displays function symbols in reversed order.
|
> `metadata.complete` _(optional)_: Marks the object as "complete" (or "linked") in the object list.
|
||||||
Used to support MWCC's `-inline deferred` option, which reverses the order of functions in the object file.
|
> This is useful for marking objects that are fully decompiled. A value of `false` will mark the object as "incomplete".
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
|
|||||||
221
config.schema.json
Normal file
221
config.schema.json
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json-schema.org/draft-07/schema",
|
||||||
|
"$id": "https://raw.githubusercontent.com/encounter/objdiff/main/config.schema.json",
|
||||||
|
"title": "objdiff configuration",
|
||||||
|
"description": "Configuration file for objdiff",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"min_version": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Minimum version of objdiff required to load this configuration file.",
|
||||||
|
"examples": [
|
||||||
|
"1.0.0",
|
||||||
|
"2.0.0-beta.1"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"custom_make": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "By default, objdiff will use make to build the project.\nIf the project uses a different build system (e.g. ninja), specify it here.\nThe build command will be `[custom_make] [custom_args] path/to/object.o`.",
|
||||||
|
"examples": [
|
||||||
|
"make",
|
||||||
|
"ninja"
|
||||||
|
],
|
||||||
|
"default": "make"
|
||||||
|
},
|
||||||
|
"custom_args": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "Additional arguments to pass to the build command prior to the object path.",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"target_dir": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Relative from the root of the project, this where the \"target\" or \"expected\" objects are located.\nThese are the intended result of the match.",
|
||||||
|
"deprecated": true
|
||||||
|
},
|
||||||
|
"base_dir": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Relative from the root of the project, this is where the \"base\" or \"actual\" objects are located.\nThese are objects built from the current source code.",
|
||||||
|
"deprecated": true
|
||||||
|
},
|
||||||
|
"build_target": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "If true, objdiff will tell the build system to build the target objects before diffing (e.g. `make path/to/target.o`).\nThis is useful if the target objects are not built by default or can change based on project configuration or edits to assembly files.\nRequires the build system to be configured properly.",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"build_base": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "If true, objdiff will tell the build system to build the base objects before diffing (e.g. `make path/to/base.o`).\nIt's unlikely you'll want to disable this, unless you're using an external tool to rebuild the base object on source file changes.",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
"watch_patterns": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "List of glob patterns to watch for changes in the project.\nIf any of these files change, objdiff will automatically rebuild the objects and re-compare them.\nSupported syntax: https://docs.rs/globset/latest/globset/#syntax",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"default": [
|
||||||
|
"*.c",
|
||||||
|
"*.cp",
|
||||||
|
"*.cpp",
|
||||||
|
"*.cxx",
|
||||||
|
"*.h",
|
||||||
|
"*.hp",
|
||||||
|
"*.hpp",
|
||||||
|
"*.hxx",
|
||||||
|
"*.s",
|
||||||
|
"*.S",
|
||||||
|
"*.asm",
|
||||||
|
"*.inc",
|
||||||
|
"*.py",
|
||||||
|
"*.yml",
|
||||||
|
"*.txt",
|
||||||
|
"*.json"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"objects": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "Use units instead.",
|
||||||
|
"deprecated": true,
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/$defs/unit"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"units": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "If specified, objdiff will display a list of objects in the sidebar for easy navigation.",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/$defs/unit"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"progress_categories": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "Progress categories used for objdiff-cli report.",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/$defs/progress_category"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"$defs": {
|
||||||
|
"unit": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The name of the object in the UI. If not specified, the object's path will be used."
|
||||||
|
},
|
||||||
|
"path": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Relative path to the object from the target_dir and base_dir.\nRequires target_dir and base_dir to be specified.",
|
||||||
|
"deprecated": true
|
||||||
|
},
|
||||||
|
"target_path": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Path to the target object from the project root.\nRequired if path is not specified."
|
||||||
|
},
|
||||||
|
"base_path": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Path to the base object from the project root.\nRequired if path is not specified."
|
||||||
|
},
|
||||||
|
"reverse_fn_order": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Displays function symbols in reversed order.\nUsed to support MWCC's -inline deferred option, which reverses the order of functions in the object file.",
|
||||||
|
"deprecated": true
|
||||||
|
},
|
||||||
|
"complete": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Marks the object as \"complete\" (or \"linked\") in the object list.\nThis is useful for marking objects that are fully decompiled. A value of `false` will mark the object as \"incomplete\".",
|
||||||
|
"deprecated": true
|
||||||
|
},
|
||||||
|
"scratch": {
|
||||||
|
"ref": "#/$defs/scratch"
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"ref": "#/$defs/metadata"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scratch": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "If present, objdiff will display a button to create a decomp.me scratch.",
|
||||||
|
"properties": {
|
||||||
|
"platform": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The decomp.me platform ID to use for the scratch.",
|
||||||
|
"examples": [
|
||||||
|
"gc_wii",
|
||||||
|
"n64"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"compiler": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The decomp.me compiler ID to use for the scratch.",
|
||||||
|
"examples": [
|
||||||
|
"mwcc_242_81",
|
||||||
|
"ido7.1"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"c_flags": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "C flags to use for the scratch. Exclude any include paths."
|
||||||
|
},
|
||||||
|
"ctx_path": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Path to the context file to use for the scratch."
|
||||||
|
},
|
||||||
|
"build_ctx": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "If true, objdiff will run the build command with the context file as an argument to generate it.",
|
||||||
|
"default": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"platform",
|
||||||
|
"compiler"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"complete": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Marks the object as \"complete\" (or \"linked\") in the object list.\nThis is useful for marking objects that are fully decompiled. A value of `false` will mark the object as \"incomplete\"."
|
||||||
|
},
|
||||||
|
"reverse_fn_order": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Displays function symbols in reversed order.\nUsed to support MWCC's -inline deferred option, which reverses the order of functions in the object file."
|
||||||
|
},
|
||||||
|
"source_path": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Path to the source file that generated the object."
|
||||||
|
},
|
||||||
|
"progress_categories": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "Progress categories used for objdiff-cli report.",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Unique identifier for the category. (See progress_categories)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"auto_generated": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Hides the object from the object list by default, but still includes it in reports."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"progress_category": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Unique identifier for the category."
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Human-readable name of the category."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "objdiff-cli"
|
name = "objdiff-cli"
|
||||||
version = "2.0.0-beta.5"
|
version = "2.0.0-beta.6"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.70"
|
rust-version = "1.70"
|
||||||
authors = ["Luke Street <luke@street.dev>"]
|
authors = ["Luke Street <luke@street.dev>"]
|
||||||
|
|||||||
@@ -87,9 +87,10 @@ fn generate(args: GenerateArgs) -> Result<()> {
|
|||||||
let project_dir = args.project.as_deref().unwrap_or_else(|| Path::new("."));
|
let project_dir = args.project.as_deref().unwrap_or_else(|| Path::new("."));
|
||||||
info!("Loading project {}", project_dir.display());
|
info!("Loading project {}", project_dir.display());
|
||||||
|
|
||||||
let config = objdiff_core::config::try_project_config(project_dir);
|
let mut project = match objdiff_core::config::try_project_config(project_dir) {
|
||||||
let Some((Ok(mut project), _)) = config else {
|
Some((Ok(config), _)) => config,
|
||||||
bail!("No project configuration found");
|
Some((Err(err), _)) => bail!("Failed to load project configuration: {}", err),
|
||||||
|
None => bail!("No project configuration found"),
|
||||||
};
|
};
|
||||||
info!(
|
info!(
|
||||||
"Generating report for {} units (using {} threads)",
|
"Generating report for {} units (using {} threads)",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "objdiff-core"
|
name = "objdiff-core"
|
||||||
version = "2.0.0-beta.5"
|
version = "2.0.0-beta.6"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.70"
|
rust-version = "1.70"
|
||||||
authors = ["Luke Street <luke@street.dev>"]
|
authors = ["Luke Street <luke@street.dev>"]
|
||||||
@@ -34,7 +34,7 @@ flagset = "0.4.5"
|
|||||||
log = "0.4.21"
|
log = "0.4.21"
|
||||||
memmap2 = "0.9.4"
|
memmap2 = "0.9.4"
|
||||||
num-traits = "0.2.18"
|
num-traits = "0.2.18"
|
||||||
object = { version = "0.35.0", features = ["read_core", "std", "elf", "pe"], default-features = false }
|
object = { version = "0.36.0", features = ["read_core", "std", "elf", "pe"], default-features = false }
|
||||||
pbjson = { version = "0.7.0", optional = true }
|
pbjson = { version = "0.7.0", optional = true }
|
||||||
prost = { version = "0.13.1", optional = true }
|
prost = { version = "0.13.1", optional = true }
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
use std::{collections::HashSet, fs, io::Cursor, path::Path};
|
use std::{collections::HashSet, fs, io::Cursor, mem::size_of, path::Path};
|
||||||
|
|
||||||
use anyhow::{anyhow, bail, ensure, Context, Result};
|
use anyhow::{anyhow, bail, ensure, Context, Result};
|
||||||
use cwextab::decode_extab;
|
use cwextab::decode_extab;
|
||||||
use filetime::FileTime;
|
use filetime::FileTime;
|
||||||
use flagset::Flags;
|
use flagset::Flags;
|
||||||
use object::{
|
use object::{
|
||||||
|
endian::LittleEndian as LE,
|
||||||
|
pe::{ImageAuxSymbolFunctionBeginEnd, ImageLinenumber},
|
||||||
|
read::coff::{CoffFile, CoffHeader, ImageSymbol},
|
||||||
Architecture, BinaryFormat, File, Object, ObjectSection, ObjectSymbol, RelocationTarget,
|
Architecture, BinaryFormat, File, Object, ObjectSection, ObjectSymbol, RelocationTarget,
|
||||||
SectionIndex, SectionKind, Symbol, SymbolKind, SymbolScope, SymbolSection,
|
SectionIndex, SectionKind, Symbol, SymbolIndex, SymbolKind, SymbolScope, SymbolSection,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -401,7 +404,7 @@ fn relocations_by_section(
|
|||||||
Ok(relocations)
|
Ok(relocations)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn line_info(obj_file: &File<'_>, sections: &mut [ObjSection]) -> Result<()> {
|
fn line_info(obj_file: &File<'_>, sections: &mut [ObjSection], obj_data: &[u8]) -> Result<()> {
|
||||||
// DWARF 1.1
|
// DWARF 1.1
|
||||||
if let Some(section) = obj_file.section_by_name(".line") {
|
if let Some(section) = obj_file.section_by_name(".line") {
|
||||||
let data = section.uncompressed_data()?;
|
let data = section.uncompressed_data()?;
|
||||||
@@ -490,6 +493,121 @@ fn line_info(obj_file: &File<'_>, sections: &mut [ObjSection]) -> Result<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// COFF
|
||||||
|
if let File::Coff(coff) = obj_file {
|
||||||
|
line_info_coff(coff, sections, obj_data)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn line_info_coff(coff: &CoffFile, sections: &mut [ObjSection], obj_data: &[u8]) -> Result<()> {
|
||||||
|
let symbol_table = coff.coff_header().symbols(obj_data)?;
|
||||||
|
|
||||||
|
// Enumerate over all sections.
|
||||||
|
for sect in coff.sections() {
|
||||||
|
let ptr_linenums = sect.coff_section().pointer_to_linenumbers.get(LE) as usize;
|
||||||
|
let num_linenums = sect.coff_section().number_of_linenumbers.get(LE) as usize;
|
||||||
|
|
||||||
|
// If we have no line number, skip this section.
|
||||||
|
if num_linenums == 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find this section in our out_section. If it's not in out_section,
|
||||||
|
// skip it.
|
||||||
|
let Some(out_section) = sections.iter_mut().find(|s| s.orig_index == sect.index().0) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Turn the line numbers into an ImageLinenumber slice.
|
||||||
|
let Some(linenums) =
|
||||||
|
&obj_data.get(ptr_linenums..ptr_linenums + num_linenums * size_of::<ImageLinenumber>())
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let Ok(linenums) = object::pod::slice_from_all_bytes::<ImageLinenumber>(linenums) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
// In COFF, the line numbers are stored relative to the start of the
|
||||||
|
// function. Because of this, we need to know the line number where the
|
||||||
|
// function starts, so we can sum the two and get the line number
|
||||||
|
// relative to the start of the file.
|
||||||
|
//
|
||||||
|
// This variable stores the line number where the function currently
|
||||||
|
// being processed starts. It is set to None when we failed to find the
|
||||||
|
// line number of the start of the function.
|
||||||
|
let mut cur_fun_start_linenumber = None;
|
||||||
|
for linenum in linenums {
|
||||||
|
let line_number = linenum.linenumber.get(LE);
|
||||||
|
if line_number == 0 {
|
||||||
|
// Starting a new function. We need to find the line where that
|
||||||
|
// function is located in the file. To do this, we need to find
|
||||||
|
// the `.bf` symbol "associated" with this function. The .bf
|
||||||
|
// symbol will have a Function Begin/End Auxillary Record, which
|
||||||
|
// contains the line number of the start of the function.
|
||||||
|
|
||||||
|
// First, set cur_fun_start_linenumber to None. If we fail to
|
||||||
|
// find the start of the function, this will make sure the
|
||||||
|
// subsequent line numbers will be ignored until the next start
|
||||||
|
// of function.
|
||||||
|
cur_fun_start_linenumber = None;
|
||||||
|
|
||||||
|
// Get the symbol associated with this function. We'll need it
|
||||||
|
// for logging purposes, but also to acquire its Function
|
||||||
|
// Auxillary Record, which tells us where to find our .bf symbol.
|
||||||
|
let symtable_entry = linenum.symbol_table_index_or_virtual_address.get(LE);
|
||||||
|
let Ok(symbol) = symbol_table.symbol(SymbolIndex(symtable_entry as usize)) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let Ok(aux_fun) = symbol_table.aux_function(SymbolIndex(symtable_entry as usize))
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get the .bf symbol associated with this symbol. To do so, we
|
||||||
|
// look at the Function Auxillary Record's tag_index, which is
|
||||||
|
// an index in the symbol table pointing to our .bf symbol.
|
||||||
|
if aux_fun.tag_index.get(LE) == 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let Ok(bf_symbol) =
|
||||||
|
symbol_table.symbol(SymbolIndex(aux_fun.tag_index.get(LE) as usize))
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
// Do some sanity checks that we are, indeed, looking at a .bf
|
||||||
|
// symbol.
|
||||||
|
if bf_symbol.name(symbol_table.strings()) != Ok(b".bf") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Get the Function Begin/End Auxillary Record associated with
|
||||||
|
// our .bf symbol, where we'll fine the linenumber of the start
|
||||||
|
// of our function.
|
||||||
|
let Ok(bf_aux) = symbol_table.get::<ImageAuxSymbolFunctionBeginEnd>(
|
||||||
|
SymbolIndex(aux_fun.tag_index.get(LE) as usize),
|
||||||
|
1,
|
||||||
|
) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
// Set cur_fun_start_linenumber so the following linenumber
|
||||||
|
// records will know at what line the current function start.
|
||||||
|
cur_fun_start_linenumber = Some(bf_aux.linenumber.get(LE) as u32);
|
||||||
|
// Let's also synthesize a line number record from the start of
|
||||||
|
// the function, as the linenumber records don't always cover it.
|
||||||
|
out_section.line_info.insert(
|
||||||
|
sect.address() + symbol.value() as u64,
|
||||||
|
bf_aux.linenumber.get(LE) as u32,
|
||||||
|
);
|
||||||
|
} else if let Some(cur_linenumber) = cur_fun_start_linenumber {
|
||||||
|
let vaddr = linenum.symbol_table_index_or_virtual_address.get(LE);
|
||||||
|
out_section
|
||||||
|
.line_info
|
||||||
|
.insert(sect.address() + vaddr as u64, cur_linenumber + line_number as u32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -620,7 +738,7 @@ pub fn parse(data: &[u8], config: &DiffObjConfig) -> Result<ObjInfo> {
|
|||||||
if config.combine_data_sections {
|
if config.combine_data_sections {
|
||||||
combine_data_sections(&mut sections)?;
|
combine_data_sections(&mut sections)?;
|
||||||
}
|
}
|
||||||
line_info(&obj_file, &mut sections)?;
|
line_info(&obj_file, &mut sections, data)?;
|
||||||
let common = common_symbols(arch.as_ref(), &obj_file, split_meta.as_ref())?;
|
let common = common_symbols(arch.as_ref(), &obj_file, split_meta.as_ref())?;
|
||||||
let extab = exception_tables(&mut sections, &obj_file)?;
|
let extab = exception_tables(&mut sections, &obj_file)?;
|
||||||
Ok(ObjInfo { arch, path: None, timestamp: None, sections, common, extab, split_meta })
|
Ok(ObjInfo { arch, path: None, timestamp: None, sections, common, extab, split_meta })
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "objdiff-gui"
|
name = "objdiff-gui"
|
||||||
version = "2.0.0-beta.5"
|
version = "2.0.0-beta.6"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.70"
|
rust-version = "1.70"
|
||||||
authors = ["Luke Street <luke@street.dev>"]
|
authors = ["Luke Street <luke@street.dev>"]
|
||||||
|
|||||||
Reference in New Issue
Block a user