A local diffing tool for decompilation projects. Inspired by decomp.me and asm-differ.
Features:
- Compare entire object files: functions and data
- Built-in C++ symbol demangling (GCC, MSVC, CodeWarrior, Itanium)
- Automatic rebuild on source file changes
- Project integration via configuration file
- Search and filter objects with quick switching
- Click-to-highlight values and registers
- Detailed progress reporting (powers decomp.dev)
- WebAssembly API, web interface and Visual Studio Code extension (WIP)
Supports:
- ARM (GBA, DS, 3DS)
- ARM64 (Switch)
- MIPS (N64, PS1, PS2, PSP)
- PowerPC (GameCube, Wii, PS3, Xbox 360)
- SuperH (Saturn, Dreamcast)
- x86, x86_64 (PC)
See Usage for more information.
To build from source, see Building.
For Linux and macOS, run chmod +x objdiff-*
to make the binary executable.
CLI binaries are available on the releases page.
objdiff compares two relocatable object files (.o
). Here's how it works:
-
Create an
objdiff.json
configuration file in your project root (or generate it with your build script).
This file lists all objects in the project with their target ("expected") and base ("current") paths. -
Load the project in objdiff.
-
Select an object from the sidebar to begin diffing.
-
objdiff automatically:
- Executes the build system to compile the base object (from current source code)
- Compares the two objects and displays the differences
- Watches for source file changes and rebuilds when detected
The configuration file allows complete flexibility in project structure - your build directories can have any layout as long as the paths are specified correctly.
See Configuration for setup details.
Projects can add an objdiff.json
file to configure the tool automatically. The configuration file must be located in
the root project directory.
If your project has a generator script (e.g. configure.py
), it's highly recommended to generate the objdiff configuration
file as well. You can then add objdiff.json
to your .gitignore
to prevent it from being committed.
{
"$schema": "https://raw.githubusercontent.com/encounter/objdiff/main/config.schema.json",
"custom_make": "ninja",
"custom_args": [
"-d",
"keeprsp"
],
"build_target": false,
"build_base": true,
"watch_patterns": [
"*.c",
"*.cc",
"*.cp",
"*.cpp",
"*.cxx",
"*.c++",
"*.h",
"*.hh",
"*.hp",
"*.hpp",
"*.hxx",
"*.h++",
"*.pch",
"*.pch++",
"*.inc",
"*.s",
"*.S",
"*.asm",
"*.py",
"*.yml",
"*.txt",
"*.json"
],
"ignore_patterns": [
"build/**/*"
],
"units": [
{
"name": "main/MetroTRK/mslsupp",
"target_path": "build/asm/MetroTRK/mslsupp.o",
"base_path": "build/src/MetroTRK/mslsupp.o",
"metadata": {}
}
]
}
Note
View config.schema.json for all available options. Below is a summary of the most important options.
custom_make
(optional, default: "make"
)
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
.
custom_args
(optional)
Additional arguments to pass to the build command prior to the object path.
build_target
(default: false
)
If true, objdiff will build the target objects before diffing (e.g. make path/to/target.o
). Useful if target objects are not built by default or can change based on project configuration. Requires proper build system configuration.
build_base
(default: true
)
If true, objdiff will build the base objects before diffing (e.g. make path/to/base.o
). It's unlikely you'll want to disable this unless using an external tool to rebuild the base object.
watch_patterns
(optional, default: listed above)
A list of glob patterns to watch for changes (supported syntax). When these files change, objdiff automatically rebuilds and re-compares objects.
ignore_patterns
(optional, default: listed above)
A list of glob patterns to explicitly ignore when watching for changes (supported syntax).
units
(optional)
If specified, objdiff displays a list of objects in the sidebar for easy navigation. Each unit contains:
name
(optional) - The display name in the UI. Defaults to the object'spath
.target_path
(optional) - Path to the "target" or "expected" object (the intended result).base_path
(optional) - Path to the "base" or "current" object (built from current source code). Omit if there is no source object yet.metadata.auto_generated
(optional) - Hides the object from the sidebar but includes it in progress reports.metadata.complete
(optional) - Marks the object as "complete" (linked) whentrue
or "incomplete" whenfalse
.
Install Rust via rustup.
git clone https://github.com/encounter/objdiff.git
cd objdiff
cargo run --release
Or install directly with cargo:
cargo install --locked --git https://github.com/encounter/objdiff.git objdiff-gui objdiff-cli
Binaries will be installed to ~/.cargo/bin
as objdiff
and objdiff-cli
.
Install pre-commit
to run linting and formatting automatically:
rustup toolchain install nightly # Required for cargo fmt/clippy
cargo install --locked cargo-deny # https://github.com/EmbarkStudios/cargo-deny
uv tool install pre-commit # https://docs.astral.sh/uv, or use pipx or pip
pre-commit install
Licensed under either of
- Apache License, Version 2.0, (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.