This is the MiloHax fork of encounter/objdiff. It adds automated analysis and pattern detection on top of upstream objdiff, designed to be consumed by AI agents working on decompilation at scale.
This fork was built for dc3-decomp, the Dance Central 3 decompilation project targeting Xbox 360 (PowerPC). That project has ~47,000 functions to match against the original binary. Manually inspecting every mismatch to figure out what's wrong and whether it's fixable doesn't scale -- so we made objdiff do the diagnosis automatically.
Large decompilation projects produce hundreds of functions that almost match but don't quite. Some are genuinely fixable with a one-line change. Others are stuck at 97-99% because the linker merged identical functions (ICF) or the compiler's register allocator made different choices. Telling the difference requires reading raw assembly diffs instruction-by-instruction, which is slow and error-prone for humans and opaque to AI agents.
This fork adds an analysis engine to objdiff-cli that examines instruction diffs, detects common mismatch patterns, classifies each function with a fixability verdict, and outputs everything as structured JSON or human-readable markdown. The key insight is that this output is designed to be consumed by AI agents via MCP (Model Context Protocol), not just read by humans.
In dc3-decomp, an MCP orchestrator server wraps objdiff-cli and exposes tools like run_objdiff and run_diff_inspect to Claude Code agents. An agent working on a function calls run_objdiff, gets back a verdict like LIKELY_FIXABLE with pattern details ("register swap on r3/r4, 5 occurrences -- try reordering variable declarations"), makes a code change, rebuilds in ~2-4 seconds, and checks the new diff. This tight loop runs autonomously, with 6-10 agents working in parallel on different functions using isolated git worktrees.
The structured output -- verdicts, pattern types, match percentages, call diffs, instruction clusters -- gives agents the context they need to make targeted fixes without staring at raw PowerPC assembly. It also tells them when to stop: if the verdict is AT_LIMIT because 80%+ of remaining mismatches are linker-merged calls, there's nothing more to try.
- 7 mismatch pattern detectors: LinkerMerged, BoolMask, RegisterSwap, ComparisonStyle, ControlFlow, CommutativeOpOrder, OffsetSwap
- Fixability verdicts: Complete, LikelyFixable, MaybeFixable, AtLimit, NeedsInvestigation -- so agents (and humans) can prioritize what to work on
- Pattern summaries: human-readable explanations of what's wrong and what to try next
- Call diff analysis: detects when target and base call different functions or different counts
- Insert/delete cluster detection: identifies groups of added/removed instructions and their dominant opcodes
- Diff region computation: breaks a function into localized regions with per-region match percentages
- Markdown diff output: concise, context, and full-listing rendering modes for CI reports and MCP tool responses
analysisandreportCLI subcommands: batch-analyze entire projects and export structured JSON or readable markdown- Map file support: resolve symbols from linker map files
- DWARF2 line info: extract source file and line number from debug info
In the dc3-decomp workflow, the MCP orchestrator exposes these objdiff-powered tools to agents:
| Tool | What it does |
|---|---|
run_objdiff |
Incremental build + diff a single function. Returns match %, verdict, detected patterns. |
run_diff_inspect |
Deep analysis modes: diagnose (root cause), clusters, regswaps, offsets, replaces, mismatches |
run_analyze_function |
Combined objdiff + Ghidra analysis with struct field resolution for offset mismatches |
Agents follow a structured workflow: receive pre-computed context (RB3 reference code, Ghidra decompilation, objdiff analysis) -> edit source -> rebuild + diff via run_objdiff -> respond to verdict -> iterate or accept limit. The verdict system prevents wasted effort -- agents learn that AT_LIMIT means stop, LIKELY_FIXABLE means try control flow changes, and MAYBE_FIXABLE means try variable reordering.
See the dc3-decomp docs for the full orchestrator setup, master agent prompt, and subagent strategy.
This fork tracks upstream encounter/objdiff and periodically rebases or merges. The core diffing engine, GUI, configuration format, and all existing features are preserved. The analysis features are additive -- they live in the CLI and don't affect the GUI or library API.
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.jsonconfiguration 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) whentrueor "incomplete" whenfalse.
Install Rust via rustup.
git clone https://github.com/freeqaz/objdiff.git
cd objdiff
cargo run --releaseOr install directly with cargo:
cargo install --locked --git https://github.com/freeqaz/objdiff.git objdiff-gui objdiff-cliBinaries 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 installLicensed 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.

