Compare commits

..

21 Commits

Author SHA1 Message Date
LagoLunatic f2a591356e Improve automatic symbol pairing for nameless literals (#247)
* Improve automatic symbol pairing for nameless literals

* Fix data reloc diffing when the reloc points to an in-function static symbol

* Only pair up literals that match perfectly

* Clippy

* Do two separate passes when pairing up literals

* Fix partially-matching literal pairups not working right

* Remove duplicate $ splitting code

* Implement $ splitting for section names too

* Minor cleanup
2025-08-30 23:07:43 -06:00
Luke Street 8d24ec6373 Detect x86 instruction size differences
Check raw code length when instructions have same disassembly
but different encodings. Marks as OpMismatch to indicate
encoding difference.

Fixes #242
Fixes #233
2025-08-30 22:57:39 -06:00
Luke Street 58430d947b ci: Remove quotes from CARGO_ZIGBUILD_ZIG_PATH 2025-08-30 15:49:14 -06:00
Luke Street 1533125f9d Resolve objdiff-wasm clippy warnings 2025-08-30 15:02:10 -06:00
Luke Street 5654060dc8 ci: Use uv tool instead of venv 2025-08-30 15:02:10 -06:00
Luke Street 84079c3934 objdiff-wasm build improvements 2025-08-30 15:02:10 -06:00
LagoLunatic 48804dc2e3 Merge pull request #245 from LagoLunatic/subi
PPC pooled data references: Add support for `subi`, `addis`, and `subis` instructions
2025-08-23 18:05:47 -04:00
LagoLunatic 8cfa8b7dab Update default watch patterns to include more extensions (#246) 2025-08-23 15:37:34 -06:00
LagoLunatic 93a4d7e55d PPC pooled data references: Add support for subi, addis, and subis instructions 2025-08-23 16:37:57 -04:00
iFarbod 7c424a7966 Ignore switchD_ labels generated by Ghidra (#241)
Seeing how commonly used boricj's delinker extension is, it makes sense for this one to be included by default, before #238 is considered and worked on.
2025-08-19 10:23:07 -06:00
Luke Street 678210d58a Change file watcher log message to debug 2025-08-15 16:38:06 -06:00
Luke Street 4302821615 Fix Windows build 2025-08-15 16:31:33 -06:00
Luke Street c4b4244b59 Version v3.0.0 2025-08-15 16:27:27 -06:00
Luke Street 52c138bf06 Add "ignore_patterns" option to config
This allows explicitly ignoring changes to certain
files or directories, even if the changed file ends
up matching `watch_patterns`. The default value,
`build/**/*` will ensure that changes in the build
directory will not trigger a duplicate rebuild.

Resolves #143
Resolves #215
2025-08-15 16:24:26 -06:00
Luke Street 813c8aa539 Add "Diff fill color" option to Appearance
Allows configuring the background color of
lines with a diff.

Resolves #230
2025-08-15 15:43:23 -06:00
Luke Street 0f0aaab795 Fix WSL path handling
Resolves #170
2025-08-15 15:34:58 -06:00
Luke Street b21892be31 Add CLI args to objdiff-gui (incl. --project-dir/-p)
Resolves #41
Resolves #211
2025-08-15 15:25:55 -06:00
Luke Street 247d6da94b Restore extab diff view 2025-08-15 15:06:16 -06:00
Luke Street bd95faa9c3 Remove objdiff-cli diff JSON output mode
This has been unimplemented since v3.0.0-alpha.1,
and I don't currently have plans to bring it back.
If you need it for something, please open an issue!
2025-08-15 14:57:34 -06:00
Luke Street 2c57e4960f Add ARM logic for inferred function size padding
Resolves #237
2025-08-15 14:48:26 -06:00
Luke Street cff4be2979 Update gimli, object
Resolves #228
2025-08-15 14:47:06 -06:00
40 changed files with 743 additions and 723 deletions
+13 -11
View File
@@ -36,9 +36,9 @@ jobs:
- name: Cache Rust workspace
uses: Swatinem/rust-cache@v2
- name: Cargo check
run: cargo check --all-targets --all-features
run: cargo check --all-targets --all-features --workspace
- name: Cargo clippy
run: cargo clippy --all-targets --all-features
run: cargo clippy --all-targets --all-features --workspace
fmt:
name: Format
@@ -92,7 +92,7 @@ jobs:
- name: Cache Rust workspace
uses: Swatinem/rust-cache@v2
- name: Cargo test
run: cargo test --release --features all
run: cargo test --release --all-features --workspace
build-cli:
name: Build objdiff-cli
@@ -146,13 +146,14 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install uv
if: matrix.build == 'zigbuild'
uses: astral-sh/setup-uv@v6
- 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
uv tool install cargo-zigbuild==0.20.1 --with-executables-from ziglang==0.15.1
echo "CARGO_ZIGBUILD_ZIG_PATH=$(uv tool dir)/cargo-zigbuild/bin/python-zig" >> $GITHUB_ENV
- name: Setup Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
@@ -213,13 +214,14 @@ jobs:
sudo apt-get -y install ${{ matrix.packages }}
- name: Checkout
uses: actions/checkout@v4
- name: Install uv
if: matrix.build == 'zigbuild'
uses: astral-sh/setup-uv@v6
- 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
uv tool install cargo-zigbuild==0.20.1 --with-executables-from ziglang==0.15.1
echo "CARGO_ZIGBUILD_ZIG_PATH=$(uv tool dir)/cargo-zigbuild/bin/python-zig" >> $GITHUB_ENV
- name: Setup Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
+1 -1
View File
@@ -24,7 +24,7 @@ repos:
description: Run cargo clippy on all project files.
language: system
entry: cargo
args: ["+nightly", "clippy", "--all-targets", "--all-features"]
args: ["+nightly", "clippy", "--all-targets", "--all-features", "--workspace"]
pass_filenames: false
- id: cargo-deny
name: cargo deny
Generated
+70 -69
View File
@@ -123,7 +123,7 @@ dependencies = [
"clipboard-win",
"image",
"log",
"objc2 0.6.1",
"objc2 0.6.2",
"objc2-app-kit 0.3.1",
"objc2-core-foundation",
"objc2-core-graphics",
@@ -390,9 +390,9 @@ dependencies = [
[[package]]
name = "async-std"
version = "1.13.1"
version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "730294c1c08c2e0f85759590518f6333f0d5a0a766a27d519c1b244c3dfd8a24"
checksum = "2c8e079a4ab67ae52b7403632e4618815d6db36d2a010cfe41b02c1b1578f93b"
dependencies = [
"async-channel 1.9.0",
"async-global-executor",
@@ -436,9 +436,9 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de"
[[package]]
name = "async-trait"
version = "0.1.88"
version = "0.1.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5"
checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb"
dependencies = [
"proc-macro2",
"quote",
@@ -556,7 +556,7 @@ version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "340d2f0bdb2a43c1d3cd40513185b2bd7def0aa1052f956455114bc98f82dcf2"
dependencies = [
"objc2 0.6.1",
"objc2 0.6.2",
]
[[package]]
@@ -1126,7 +1126,7 @@ dependencies = [
"bitflags 2.9.1",
"block2 0.6.1",
"libc",
"objc2 0.6.1",
"objc2 0.6.2",
]
[[package]]
@@ -1184,9 +1184,9 @@ dependencies = [
[[package]]
name = "ecolor"
version = "0.32.0"
version = "0.32.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a631732d995184114016fab22fc7e3faf73d6841c2d7650395fe251fbcd9285"
checksum = "b6a7fc3172c2ef56966b2ce4f84177e159804c40b9a84de8861558ce4a59f422"
dependencies = [
"bytemuck",
"emath",
@@ -1195,9 +1195,9 @@ dependencies = [
[[package]]
name = "eframe"
version = "0.32.0"
version = "0.32.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c790ccfbb3dd556588342463454b2b2b13909e5fdce5bc2a1432a8aa69c8b7a"
checksum = "34037a80dc03a4147e1684bff4e4fdab2b1408d715d7b78470cd3179258964b9"
dependencies = [
"ahash",
"bytemuck",
@@ -1236,9 +1236,9 @@ dependencies = [
[[package]]
name = "egui"
version = "0.32.0"
version = "0.32.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8470210c95a42cc985d9ffebfd5067eea55bdb1c3f7611484907db9639675e28"
checksum = "49e2be082f77715496b4a39fdc6f5dc7491fefe2833111781b8697ea6ee919a7"
dependencies = [
"accesskit",
"ahash",
@@ -1256,9 +1256,9 @@ dependencies = [
[[package]]
name = "egui-wgpu"
version = "0.32.0"
version = "0.32.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14de9942d8b9e99e2d830403c208ab1a6e052e925a7456a4f6f66d567d90de1d"
checksum = "64c7277a171ec1b711080ddb3b0bfa1b3aa9358834d5386d39e83fbc16d61212"
dependencies = [
"ahash",
"bytemuck",
@@ -1276,9 +1276,9 @@ dependencies = [
[[package]]
name = "egui-winit"
version = "0.32.0"
version = "0.32.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c490804a035cec9c826082894a3e1ecf4198accd3817deb10f7919108ebafab0"
checksum = "fe6d8b0f8d6de4d43e794e343f03bacc3908aada931f0ed6fd7041871388a590"
dependencies = [
"ahash",
"arboard",
@@ -1296,9 +1296,9 @@ dependencies = [
[[package]]
name = "egui_extras"
version = "0.32.0"
version = "0.32.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f791a5937f518249016b276b3639ad2aa3824048b6f2161ec2b431ab325880a"
checksum = "8ae8f23013328beb6be7ab29c75807142e8e1c7951643780a813e54cceaa9929"
dependencies = [
"ahash",
"egui",
@@ -1310,9 +1310,9 @@ dependencies = [
[[package]]
name = "egui_glow"
version = "0.32.0"
version = "0.32.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d44f3fd4fdc5f960c9e9ef7327c26647edc3141abf96102980647129d49358e6"
checksum = "0ab645760288e42eab70283a5cccf44509a6f43b554351855d3c73594bfe3c23"
dependencies = [
"ahash",
"bytemuck",
@@ -1334,9 +1334,9 @@ checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
[[package]]
name = "emath"
version = "0.32.0"
version = "0.32.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45f057b141e7e46340c321400be74b793543b1b213036f0f989c35d35957c32e"
checksum = "935df67dc48fdeef132f2f7ada156ddc79e021344dd42c17f066b956bb88dde3"
dependencies = [
"bytemuck",
"serde",
@@ -1440,9 +1440,9 @@ dependencies = [
[[package]]
name = "epaint"
version = "0.32.0"
version = "0.32.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94cca02195f0552c17cabdc02f39aa9ab6fbd815dac60ab1cd3d5b0aa6f9551c"
checksum = "b66fc0a5a9d322917de9bd3ac7d426ca8aa3127fbf1e76fae5b6b25e051e06a3"
dependencies = [
"ab_glyph",
"ahash",
@@ -1459,9 +1459,9 @@ dependencies = [
[[package]]
name = "epaint_default_fonts"
version = "0.32.0"
version = "0.32.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8495e11ed527dff39663b8c36b6c2b2799d7e4287fb90556e455d72eca0b4d3"
checksum = "4f6cf8ce0fb817000aa24f5e630bda904a353536bd430b83ebc1dceee95b4a3a"
[[package]]
name = "equivalent"
@@ -1876,8 +1876,7 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
[[package]]
name = "gimli"
version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93563d740bc9ef04104f9ed6f86f1e3275c2cdafb95664e26584b9ca807a8ffe"
source = "git+https://github.com/gimli-rs/gimli?rev=7335f00e7c39fd501511584fefb0ba974117c950#7335f00e7c39fd501511584fefb0ba974117c950"
[[package]]
name = "gl_generator"
@@ -1942,7 +1941,7 @@ dependencies = [
"glutin_glx_sys",
"glutin_wgl_sys",
"libloading",
"objc2 0.6.1",
"objc2 0.6.2",
"objc2-app-kit 0.3.1",
"objc2-core-foundation",
"objc2-foundation 0.3.1",
@@ -3181,9 +3180,9 @@ dependencies = [
[[package]]
name = "objc2"
version = "0.6.1"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88c6597e14493ab2e44ce58f2fdecf095a51f12ca57bec060a11c57332520551"
checksum = "561f357ba7f3a2a61563a186a163d0a3a5247e1089524a3981d49adb775078bc"
dependencies = [
"objc2-encode",
]
@@ -3212,7 +3211,7 @@ checksum = "e6f29f568bec459b0ddff777cec4fe3fd8666d82d5a40ebd0ff7e66134f89bcc"
dependencies = [
"bitflags 2.9.1",
"block2 0.6.1",
"objc2 0.6.1",
"objc2 0.6.2",
"objc2-core-foundation",
"objc2-core-graphics",
"objc2-foundation 0.3.1",
@@ -3262,7 +3261,7 @@ checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166"
dependencies = [
"bitflags 2.9.1",
"dispatch2",
"objc2 0.6.1",
"objc2 0.6.2",
]
[[package]]
@@ -3273,7 +3272,7 @@ checksum = "989c6c68c13021b5c2d6b71456ebb0f9dc78d752e86a98da7c716f4f9470f5a4"
dependencies = [
"bitflags 2.9.1",
"dispatch2",
"objc2 0.6.1",
"objc2 0.6.2",
"objc2-core-foundation",
"objc2-io-surface",
]
@@ -3328,7 +3327,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c"
dependencies = [
"bitflags 2.9.1",
"objc2 0.6.1",
"objc2 0.6.2",
"objc2-core-foundation",
]
@@ -3339,7 +3338,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7282e9ac92529fa3457ce90ebb15f4ecbc383e8338060960760fa2cf75420c3c"
dependencies = [
"bitflags 2.9.1",
"objc2 0.6.1",
"objc2 0.6.2",
"objc2-core-foundation",
]
@@ -3437,7 +3436,7 @@ dependencies = [
[[package]]
name = "objdiff-cli"
version = "3.0.0-beta.14"
version = "3.0.1"
dependencies = [
"anyhow",
"argp",
@@ -3460,7 +3459,7 @@ dependencies = [
[[package]]
name = "objdiff-core"
version = "3.0.0-beta.14"
version = "3.0.1"
dependencies = [
"anyhow",
"arm-attr",
@@ -3483,7 +3482,7 @@ dependencies = [
"notify-debouncer-full",
"num-traits",
"objdiff-core",
"object 0.37.1",
"object 0.37.3",
"pbjson",
"pbjson-build",
"powerpc",
@@ -3515,9 +3514,10 @@ dependencies = [
[[package]]
name = "objdiff-gui"
version = "3.0.0-beta.14"
version = "3.0.1"
dependencies = [
"anyhow",
"argp",
"cfg-if",
"const_format",
"cwdemangle",
@@ -3551,7 +3551,7 @@ dependencies = [
[[package]]
name = "objdiff-wasm"
version = "3.0.0-beta.14"
version = "3.0.1"
dependencies = [
"log",
"objdiff-core",
@@ -3573,8 +3573,9 @@ dependencies = [
[[package]]
name = "object"
version = "0.37.1"
source = "git+https://github.com/gimli-rs/object?rev=16ff70aa6fbd97d6bb7b92375929f4d72414c32b#16ff70aa6fbd97d6bb7b92375929f4d72414c32b"
version = "0.37.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe"
dependencies = [
"memchr",
]
@@ -4452,7 +4453,7 @@ dependencies = [
"dispatch2",
"js-sys",
"log",
"objc2 0.6.1",
"objc2 0.6.2",
"objc2-app-kit 0.3.1",
"objc2-core-foundation",
"objc2-foundation 0.3.1",
@@ -5069,9 +5070,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.104"
version = "2.0.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
checksum = "7bc3fcb250e53458e712715cf74285c1f889686520d79294a9ef3bd7aa1fc619"
dependencies = [
"proc-macro2",
"quote",
@@ -5862,9 +5863,9 @@ dependencies = [
[[package]]
name = "wasm-encoder"
version = "0.235.0"
version = "0.236.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3bc393c395cb621367ff02d854179882b9a351b4e0c93d1397e6090b53a5c2a"
checksum = "724fccfd4f3c24b7e589d333fc0429c68042897a7e8a5f8694f31792471841e7"
dependencies = [
"leb128fmt",
"wasmparser",
@@ -5872,9 +5873,9 @@ dependencies = [
[[package]]
name = "wasm-metadata"
version = "0.235.0"
version = "0.236.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b055604ba04189d54b8c0ab2c2fc98848f208e103882d5c0b984f045d5ea4d20"
checksum = "c909f94a49a8de3365f3c0344f064818f1e369ff1740c5b04f455f85d454768e"
dependencies = [
"anyhow",
"indexmap",
@@ -5897,9 +5898,9 @@ dependencies = [
[[package]]
name = "wasmparser"
version = "0.235.0"
version = "0.236.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "161296c618fa2d63f6ed5fffd1112937e803cb9ec71b32b01a76321555660917"
checksum = "a9b1e81f3eb254cf7404a82cee6926a4a3ccc5aad80cc3d43608a070c67aa1d7"
dependencies = [
"bitflags 2.9.1",
"hashbrown",
@@ -6046,7 +6047,7 @@ dependencies = [
"jni",
"log",
"ndk-context",
"objc2 0.6.1",
"objc2 0.6.2",
"objc2-foundation 0.3.1",
"url",
"web-sys",
@@ -6725,19 +6726,19 @@ dependencies = [
[[package]]
name = "wit-bindgen"
version = "0.43.0"
version = "0.44.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a18712ff1ec5bd09da500fe1e91dec11256b310da0ff33f8b4ec92b927cf0c6"
checksum = "04bd9ed271234163b18c92783b0d406f08ca32c232e972f207a68c7b324c44bf"
dependencies = [
"wit-bindgen-rt 0.43.0",
"wit-bindgen-rt 0.44.0",
"wit-bindgen-rust-macro",
]
[[package]]
name = "wit-bindgen-core"
version = "0.43.0"
version = "0.44.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c53468e077362201de11999c85c07c36e12048a990a3e0d69da2bd61da355d0"
checksum = "b4103c7a3e178b75cd8b0b574fa199ed015e8399c9859b003865cc28834b474b"
dependencies = [
"anyhow",
"heck",
@@ -6755,18 +6756,18 @@ dependencies = [
[[package]]
name = "wit-bindgen-rt"
version = "0.43.0"
version = "0.44.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fd734226eac1fd7c450956964e3a9094c9cee65e9dafdf126feef8c0096db65"
checksum = "653c85dd7aee6fe6f4bded0d242406deadae9819029ce6f7d258c920c384358a"
dependencies = [
"bitflags 2.9.1",
]
[[package]]
name = "wit-bindgen-rust"
version = "0.43.0"
version = "0.44.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "531ebfcec48e56473805285febdb450e270fa75b2dacb92816861d0473b4c15f"
checksum = "95d164b3b6fbd2b0dd8b639b1012110c0bc256519a0a6def410d4020fa8ae106"
dependencies = [
"anyhow",
"heck",
@@ -6780,9 +6781,9 @@ dependencies = [
[[package]]
name = "wit-bindgen-rust-macro"
version = "0.43.0"
version = "0.44.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7852bf8a9d1ea80884d26b864ddebd7b0c7636697c6ca10f4c6c93945e023966"
checksum = "2c9100a5e1ac85e526dcd4ef49c3ff7689e026fa5e56e2a2047fd377fc682e02"
dependencies = [
"anyhow",
"prettyplease",
@@ -6795,9 +6796,9 @@ dependencies = [
[[package]]
name = "wit-component"
version = "0.235.0"
version = "0.236.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64a57a11109cc553396f89f3a38a158a97d0b1adaec113bd73e0f64d30fb601f"
checksum = "3622959ed7ed6341c38e5aa35af243632534b0a36226852faa802939ce11e00f"
dependencies = [
"anyhow",
"bitflags 2.9.1",
@@ -6839,9 +6840,9 @@ dependencies = [
[[package]]
name = "wit-parser"
version = "0.235.0"
version = "0.236.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a1f95a87d03a33e259af286b857a95911eb46236a0f726cbaec1227b3dfc67a"
checksum = "16e4833a20cd6e85d6abfea0e63a399472d6f88c6262957c17f546879a80ba15"
dependencies = [
"anyhow",
"id-arena",
+17 -7
View File
@@ -5,18 +5,28 @@ members = [
"objdiff-gui",
"objdiff-wasm",
]
default-members = [
"objdiff-cli",
"objdiff-core",
"objdiff-gui",
# Exclude objdiff-wasm by default
]
resolver = "3"
[workspace.package]
version = "3.0.1"
authors = ["Luke Street <luke@street.dev>"]
edition = "2024"
license = "MIT OR Apache-2.0"
repository = "https://github.com/encounter/objdiff"
rust-version = "1.88"
[profile.release-lto]
inherits = "release"
lto = "fat"
strip = "debuginfo"
codegen-units = 1
[workspace.package]
version = "3.0.0-beta.14"
authors = ["Luke Street <luke@street.dev>"]
edition = "2024"
license = "MIT OR Apache-2.0"
repository = "https://github.com/encounter/objdiff"
rust-version = "1.88"
[profile.release-min]
inherits = "release-lto"
opt-level = "z"
+14 -1
View File
@@ -90,22 +90,31 @@ file as well. You can then add `objdiff.json` to your `.gitignore` to prevent it
"build_base": true,
"watch_patterns": [
"*.c",
"*.cc",
"*.cp",
"*.cpp",
"*.cxx",
"*.c++",
"*.h",
"*.hh",
"*.hp",
"*.hpp",
"*.hxx",
"*.h++",
"*.pch",
"*.pch++",
"*.inc",
"*.s",
"*.S",
"*.asm",
"*.inc",
"*.py",
"*.yml",
"*.txt",
"*.json"
],
"ignore_patterns": [
"build/**/*"
],
"units": [
{
"name": "main/MetroTRK/mslsupp",
@@ -141,6 +150,10 @@ It's unlikely you'll want to disable this, unless you're using an external tool
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.
`ignore_patterns` _(optional)_: A list of glob patterns to explicitly ignore when watching for changes.
([Supported syntax](https://docs.rs/globset/latest/globset/#syntax))
If not specified, objdiff will use the default patterns listed above.
`units` _(optional)_: If specified, objdiff will display a list of objects in the sidebar for easy navigation.
> `name` _(optional)_: The name of the object in the UI. If not specified, the object's `path` will be used.
+17 -1
View File
@@ -57,23 +57,39 @@
},
"default": [
"*.c",
"*.cc",
"*.cp",
"*.cpp",
"*.cxx",
"*.c++",
"*.h",
"*.hh",
"*.hp",
"*.hpp",
"*.hxx",
"*.h++",
"*.pch",
"*.pch++",
"*.inc",
"*.s",
"*.S",
"*.asm",
"*.inc",
"*.py",
"*.yml",
"*.txt",
"*.json"
]
},
"ignore_patterns": {
"type": "array",
"description": "List of glob patterns to explicitly ignore when watching for changes.\nFiles matching these patterns will not trigger a rebuild.\nSupported syntax: https://docs.rs/globset/latest/globset/#syntax",
"items": {
"type": "string"
},
"default": [
"build/**/*"
]
},
"objects": {
"type": "array",
"description": "Use units instead.",
+2 -1
View File
@@ -74,6 +74,7 @@ ignore = [
#"a-crate-that-is-yanked@0.1.1", # you can also ignore yanked crate versions if you wish
#{ crate = "a-crate-that-is-yanked@0.1.1", reason = "you can specify why you are ignoring the yanked crate" },
{ id = "RUSTSEC-2024-0436", reason = "Unmaintained paste crate is an indirect dependency" },
{ id = "RUSTSEC-2025-0052", reason = "Unmaintained async-std crate is an indirect dependency" },
]
# If this is true, then cargo deny will use the git executable to fetch advisory database.
# If this is false, then it uses a built-in git library.
@@ -241,8 +242,8 @@ allow-git = []
[sources.allow-org]
# github.com organizations to allow git sources for
github = [
"enarx", # flagset
"encounter",
"gimli-rs", # gimli
]
# gitlab.com organizations to allow git sources for
gitlab = []
+5 -39
View File
@@ -19,7 +19,6 @@ use crossterm::{
},
};
use objdiff_core::{
bindings::diff::DiffResult,
build::{
BuildConfig, BuildStatus,
watcher::{Watcher, create_watcher},
@@ -28,7 +27,7 @@ use objdiff_core::{
ProjectConfig, ProjectObject, ProjectObjectMetadata, build_globset,
path::{check_path_buf, platform_path, platform_path_serde_option},
},
diff::{self, DiffObjConfig, MappingConfig, ObjectDiff},
diff::{DiffObjConfig, MappingConfig, ObjectDiff},
jobs::{
Job, JobQueue, JobResult,
objdiff::{ObjDiffConfig, start_build},
@@ -40,10 +39,7 @@ use typed_path::{Utf8PlatformPath, Utf8PlatformPathBuf};
use crate::{
cmd::apply_config_args,
util::{
output::{OutputFormat, write_output},
term::crossterm_panic_handler,
},
util::term::crossterm_panic_handler,
views::{EventControlFlow, EventResult, UiView, function_diff::FunctionDiffUi},
};
@@ -63,12 +59,6 @@ pub struct Args {
#[argp(option, short = 'u')]
/// Unit name within project
unit: Option<String>,
#[argp(option, short = 'o', from_str_fn(platform_path))]
/// Output file (one-shot mode) ("-" for stdout)
output: Option<Utf8PlatformPathBuf>,
#[argp(option)]
/// Output format (json, json-pretty, proto) (default: json)
format: Option<String>,
#[argp(positional)]
/// Function symbol to diff
symbol: Option<String>,
@@ -171,11 +161,7 @@ pub fn run(args: Args) -> Result<()> {
_ => bail!("Either target and base or project and unit must be specified"),
};
if let Some(output) = &args.output {
run_oneshot(&args, output, target_path.as_deref(), base_path.as_deref())
} else {
run_interactive(args, target_path, base_path, project_config)
}
run_interactive(args, target_path, base_path, project_config)
}
fn build_config_from_args(args: &Args) -> Result<(DiffObjConfig, MappingConfig)> {
@@ -194,28 +180,6 @@ fn build_config_from_args(args: &Args) -> Result<(DiffObjConfig, MappingConfig)>
Ok((diff_config, mapping_config))
}
fn run_oneshot(
args: &Args,
output: &Utf8PlatformPath,
target_path: Option<&Utf8PlatformPath>,
base_path: Option<&Utf8PlatformPath>,
) -> Result<()> {
let output_format = OutputFormat::from_option(args.format.as_deref())?;
let (diff_config, mapping_config) = build_config_from_args(args)?;
let target = target_path
.map(|p| obj::read::read(p.as_ref(), &diff_config).with_context(|| format!("Loading {p}")))
.transpose()?;
let base = base_path
.map(|p| obj::read::read(p.as_ref(), &diff_config).with_context(|| format!("Loading {p}")))
.transpose()?;
let result =
diff::diff_objs(target.as_ref(), base.as_ref(), None, &diff_config, &mapping_config)?;
let left = target.as_ref().and_then(|o| result.left.as_ref().map(|d| (o, d)));
let right = base.as_ref().and_then(|o| result.right.as_ref().map(|d| (o, d)));
write_output(&DiffResult::new(left, right), Some(output), output_format)?;
Ok(())
}
pub struct AppState {
pub jobs: JobQueue,
pub waker: Arc<TermWaker>,
@@ -378,10 +342,12 @@ fn run_interactive(
};
if let (Some(project_dir), Some(project_config)) = (&state.project_dir, &state.project_config) {
let watch_patterns = project_config.build_watch_patterns()?;
let ignore_patterns = project_config.build_ignore_patterns()?;
state.watcher = Some(create_watcher(
state.modified.clone(),
project_dir.as_ref(),
build_globset(&watch_patterns)?,
build_globset(&ignore_patterns)?,
Waker::from(state.waker.clone()),
)?);
}
+3 -3
View File
@@ -132,12 +132,12 @@ itertools = { version = "0.14", default-features = false, features = ["use_alloc
log = { version = "0.4", default-features = false, optional = true }
memmap2 = { version = "0.9", optional = true }
num-traits = { version = "0.2", default-features = false, optional = true }
object = { git = "https://github.com/gimli-rs/object", rev = "16ff70aa6fbd97d6bb7b92375929f4d72414c32b", default-features = false, features = ["read_core", "elf", "coff"] }
object = { version = "0.37", default-features = false, features = ["read_core", "elf", "coff"] }
pbjson = { version = "0.8", default-features = false, optional = true }
prost = { version = "0.14", default-features = false, features = ["derive"], optional = true }
regex = { version = "1.11", default-features = false, features = [], optional = true }
serde = { version = "1.0", default-features = false, features = ["derive"], optional = true }
similar = { version = "2.7", default-features = false, features = ["hashbrown"], optional = true, git = "https://github.com/encounter/similar.git", branch = "no_std" }
similar = { git = "https://github.com/encounter/similar.git", branch = "no_std", default-features = false, features = ["hashbrown"], optional = true }
typed-path = { version = "0.11", default-features = false, optional = true }
# config
@@ -146,7 +146,7 @@ semver = { version = "1.0", default-features = false, optional = true }
serde_json = { version = "1.0", default-features = false, features = ["alloc"], optional = true }
# dwarf
gimli = { version = "0.32", default-features = false, features = ["read"], optional = true }
gimli = { git = "https://github.com/gimli-rs/gimli", rev = "7335f00e7c39fd501511584fefb0ba974117c950", default-features = false, features = ["read"], optional = true }
typed-arena = { version = "2.0", default-features = false, optional = true }
# ppc
-165
View File
@@ -1,165 +0,0 @@
syntax = "proto3";
package objdiff.diff;
// A symbol
message Symbol {
// Name of the symbol
string name = 1;
// Demangled name of the symbol
optional string demangled_name = 2;
// Symbol address
uint64 address = 3;
// Symbol size
uint64 size = 4;
// Bitmask of SymbolFlag
uint32 flags = 5;
}
// Symbol visibility flags
enum SymbolFlag {
SYMBOL_NONE = 0;
SYMBOL_GLOBAL = 1;
SYMBOL_LOCAL = 2;
SYMBOL_WEAK = 4;
SYMBOL_COMMON = 8;
SYMBOL_HIDDEN = 16;
}
// A single parsed instruction
message Instruction {
// Instruction address
uint64 address = 1;
// Instruction size
uint32 size = 2;
// Instruction opcode
uint32 opcode = 3;
// Instruction mnemonic
string mnemonic = 4;
// Instruction formatted string
string formatted = 5;
// Original (unsimplified) instruction string
optional string original = 6;
// Instruction arguments
repeated Argument arguments = 7;
// Instruction relocation
optional Relocation relocation = 8;
// Instruction branch destination
optional uint64 branch_dest = 9;
// Instruction line number
optional uint32 line_number = 10;
}
// An instruction argument
message Argument {
oneof value {
// Plain text
string plain_text = 1;
// Value
ArgumentValue argument = 2;
// Relocation
ArgumentRelocation relocation = 3;
// Branch destination
uint64 branch_dest = 4;
}
}
// An instruction argument value
message ArgumentValue {
oneof value {
// Signed integer
int64 signed = 1;
// Unsigned integer
uint64 unsigned = 2;
// Opaque value
string opaque = 3;
}
}
// Marker type for relocation arguments
message ArgumentRelocation {
}
message Relocation {
uint32 type = 1;
string type_name = 2;
RelocationTarget target = 3;
}
message RelocationTarget {
uint32 symbol_index = 1;
int64 addend = 2;
}
message InstructionDiffRow {
DiffKind diff_kind = 1;
optional Instruction instruction = 2;
optional InstructionBranchFrom branch_from = 3;
optional InstructionBranchTo branch_to = 4;
repeated ArgumentDiff arg_diff = 5;
}
message ArgumentDiff {
optional uint32 diff_index = 1;
}
enum DiffKind {
DIFF_NONE = 0;
DIFF_REPLACE = 1;
DIFF_DELETE = 2;
DIFF_INSERT = 3;
DIFF_OP_MISMATCH = 4;
DIFF_ARG_MISMATCH = 5;
}
message InstructionBranchFrom {
repeated uint32 instruction_index = 1;
uint32 branch_index = 2;
}
message InstructionBranchTo {
uint32 instruction_index = 1;
uint32 branch_index = 2;
}
message SymbolDiff {
Symbol symbol = 1;
repeated InstructionDiffRow instruction_rows = 2;
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 {
DiffKind kind = 1;
bytes data = 2;
// May be larger than data
uint64 size = 3;
}
message SectionDiff {
string name = 1;
SectionKind kind = 2;
uint64 size = 3;
uint64 address = 4;
reserved 5;
repeated DataDiff data = 6;
optional float match_percent = 7;
}
enum SectionKind {
SECTION_UNKNOWN = 0;
SECTION_TEXT = 1;
SECTION_DATA = 2;
SECTION_BSS = 3;
}
message ObjectDiff {
repeated SectionDiff sections = 1;
repeated SymbolDiff symbols = 2;
}
message DiffResult {
optional ObjectDiff left = 1;
optional ObjectDiff right = 2;
}
Binary file not shown.
+16
View File
@@ -464,6 +464,22 @@ impl Arch for ArchArm {
}
flags
}
fn infer_function_size(
&self,
symbol: &Symbol,
section: &Section,
mut next_address: u64,
) -> Result<u64> {
// Trim any trailing 4-byte zeroes from the end (padding)
while next_address >= symbol.address + 4
&& let Some(data) = section.data_range(next_address - 4, 4)
&& data == [0u8; 4]
{
next_address -= 4;
}
Ok(next_address.saturating_sub(symbol.address))
}
}
#[derive(Clone, Copy, Debug)]
+2 -1
View File
@@ -6,6 +6,7 @@ use alloc::{
vec::Vec,
};
use core::{
any::Any,
ffi::CStr,
fmt::{self, Debug},
};
@@ -305,7 +306,7 @@ impl dyn Arch {
}
}
pub trait Arch: Send + Sync + Debug {
pub trait Arch: Any + Debug + Send + Sync {
/// Finishes arch-specific initialization that must be done after sections have been combined.
fn post_init(&mut self, _sections: &[Section], _symbols: &[Symbol]) {}
+52 -14
View File
@@ -675,7 +675,7 @@ fn make_symbol_ref(symbol: &object::Symbol) -> Result<ExtabSymbolRef> {
#[derive(Debug)]
struct PoolReference {
addr_src_gpr: powerpc::GPR,
addr_offset: i16,
addr_offset: i64,
addr_dst_gpr: Option<powerpc::GPR>,
}
@@ -683,16 +683,20 @@ struct PoolReference {
// 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: powerpc::Opcode,
ins: powerpc::Ins,
simplified: &powerpc::ParsedIns,
) -> Option<PoolReference> {
use powerpc::{Argument, Opcode};
let args = &simplified.args;
if flow_analysis::guess_data_type_from_load_store_inst_op(opcode).is_some() {
if flow_analysis::guess_data_type_from_load_store_inst_op(ins.op).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 })
Some(PoolReference {
addr_src_gpr,
addr_offset: offset.0 as i64,
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.
@@ -712,17 +716,51 @@ fn get_pool_reference_for_inst(
// 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]) {
match (ins.op, args[0], args[1], args[2]) {
(
// `addi` or `subi`
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),
}),
) => {
let offset = if simplified.mnemonic == "addi" { simm.0 } else { -simm.0 };
Some(PoolReference {
addr_src_gpr,
addr_offset: offset as i64,
addr_dst_gpr: Some(addr_dst_gpr),
})
}
(
// `addis`
Opcode::Addis,
Argument::GPR(addr_dst_gpr),
Argument::GPR(addr_src_gpr),
Argument::Uimm(uimm), // Note: `addis` uses UIMM, unlike `addi`, `subi`, and `subis`
) => {
assert_eq!(simplified.mnemonic, "addis");
let offset = (uimm.0 as i64) << 16;
Some(PoolReference {
addr_src_gpr,
addr_offset: offset,
addr_dst_gpr: Some(addr_dst_gpr),
})
}
(
// `subis`
Opcode::Addis,
Argument::GPR(addr_dst_gpr),
Argument::GPR(addr_src_gpr),
Argument::Simm(simm),
) => {
assert_eq!(simplified.mnemonic, "subis");
let offset = (simm.0 as i64) << 16;
Some(PoolReference {
addr_src_gpr,
addr_offset: offset,
addr_dst_gpr: Some(addr_dst_gpr),
})
}
(
// `mr` or `mr.`
Opcode::Or,
@@ -777,13 +815,13 @@ fn clear_overwritten_gprs(ins: powerpc::Ins, gpr_pool_relocs: &mut BTreeMap<u8,
// 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,
offset: i64,
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 offset_from_pool = pool_reloc.relocation.addend + offset;
let target_address = pool_reloc.symbol.address.checked_add_signed(offset_from_pool)?;
let target_symbol;
let addend;
@@ -946,7 +984,7 @@ fn generate_fake_pool_relocations_for_function(
clear_overwritten_gprs(ins, &mut gpr_pool_relocs);
}
}
} else if let Some(pool_ref) = get_pool_reference_for_inst(ins.op, &simplified) {
} else if let Some(pool_ref) = get_pool_reference_for_inst(ins, &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) {
@@ -965,7 +1003,7 @@ fn generate_fake_pool_relocations_for_function(
// 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;
new_reloc.addend += pool_ref.addr_offset;
gpr_pool_relocs.insert(addr_dst_gpr.0, new_reloc);
} else {
clear_overwritten_gprs(ins, &mut gpr_pool_relocs);
-242
View File
@@ -1,242 +0,0 @@
#![allow(clippy::needless_lifetimes)] // Generated serde code
use crate::{diff, obj};
// Protobuf diff types
include!(concat!(env!("OUT_DIR"), "/objdiff.diff.rs"));
#[cfg(feature = "serde")]
include!(concat!(env!("OUT_DIR"), "/objdiff.diff.serde.rs"));
impl DiffResult {
pub fn new(
_left: Option<(&obj::Object, &diff::ObjectDiff)>,
_right: Option<(&obj::Object, &diff::ObjectDiff)>,
) -> Self {
Self {
// TODO
// left: left.map(|(obj, diff)| ObjectDiff::new(obj, diff)),
// right: right.map(|(obj, diff)| ObjectDiff::new(obj, diff)),
left: None,
right: None,
}
}
}
// impl ObjectDiff {
// pub fn new(obj: &obj::Object, diff: &diff::ObjectDiff) -> Self {
// Self {
// sections: diff
// .sections
// .iter()
// .enumerate()
// .map(|(i, d)| SectionDiff::new(obj, i, d))
// .collect(),
// }
// }
// }
//
// impl SectionDiff {
// pub fn new(obj: &obj::Object, section_index: usize, section_diff: &diff::SectionDiff) -> Self {
// let section = &obj.sections[section_index];
// let symbols = section_diff.symbols.iter().map(|d| SymbolDiff::new(obj, d)).collect();
// let data = section_diff.data_diff.iter().map(|d| DataDiff::new(obj, d)).collect();
// // TODO: section_diff.reloc_diff
// Self {
// name: section.name.to_string(),
// kind: SectionKind::from(section.kind) as i32,
// size: section.size,
// address: section.address,
// symbols,
// data,
// match_percent: section_diff.match_percent,
// }
// }
// }
//
// impl From<obj::SectionKind> for SectionKind {
// fn from(value: obj::SectionKind) -> Self {
// match value {
// obj::SectionKind::Code => SectionKind::SectionText,
// obj::SectionKind::Data => SectionKind::SectionData,
// obj::SectionKind::Bss => SectionKind::SectionBss,
// // TODO common
// }
// }
// }
//
// impl SymbolDiff {
// pub fn new(object: &obj::Object, symbol_diff: &diff::SymbolDiff) -> Self {
// let symbol = object.symbols[symbol_diff.symbol_index];
// let instructions = symbol_diff
// .instruction_rows
// .iter()
// .map(|ins_diff| InstructionDiff::new(object, ins_diff))
// .collect();
// Self {
// symbol: Some(Symbol::new(symbol)),
// instructions,
// match_percent: symbol_diff.match_percent,
// target: symbol_diff.target_symbol.map(SymbolRef::from),
// }
// }
// }
//
// impl DataDiff {
// pub fn new(_object: &obj::Object, data_diff: &diff::DataDiff) -> Self {
// Self {
// kind: DiffKind::from(data_diff.kind) as i32,
// data: data_diff.data.clone(),
// size: data_diff.len as u64,
// }
// }
// }
//
// impl Symbol {
// pub fn new(value: &ObjSymbol) -> Self {
// Self {
// name: value.name.to_string(),
// demangled_name: value.demangled_name.clone(),
// address: value.address,
// size: value.size,
// flags: symbol_flags(value.flags),
// }
// }
// }
//
// fn symbol_flags(value: ObjSymbolFlagSet) -> u32 {
// let mut flags = 0u32;
// if value.0.contains(ObjSymbolFlags::Global) {
// flags |= SymbolFlag::SymbolGlobal as u32;
// }
// if value.0.contains(ObjSymbolFlags::Local) {
// flags |= SymbolFlag::SymbolLocal as u32;
// }
// if value.0.contains(ObjSymbolFlags::Weak) {
// flags |= SymbolFlag::SymbolWeak as u32;
// }
// if value.0.contains(ObjSymbolFlags::Common) {
// flags |= SymbolFlag::SymbolCommon as u32;
// }
// if value.0.contains(ObjSymbolFlags::Hidden) {
// flags |= SymbolFlag::SymbolHidden as u32;
// }
// flags
// }
//
// impl Instruction {
// pub fn new(object: &obj::Object, instruction: &ObjIns) -> Self {
// Self {
// address: instruction.address,
// size: instruction.size as u32,
// opcode: instruction.op as u32,
// mnemonic: instruction.mnemonic.to_string(),
// formatted: instruction.formatted.clone(),
// arguments: instruction.args.iter().map(Argument::new).collect(),
// relocation: instruction.reloc.as_ref().map(|reloc| Relocation::new(object, reloc)),
// branch_dest: instruction.branch_dest,
// line_number: instruction.line,
// original: instruction.orig.clone(),
// }
// }
// }
//
// impl Argument {
// pub fn new(value: &ObjInsArg) -> Self {
// Self {
// value: Some(match value {
// ObjInsArg::PlainText(s) => argument::Value::PlainText(s.to_string()),
// ObjInsArg::Arg(v) => argument::Value::Argument(ArgumentValue::new(v)),
// ObjInsArg::Reloc => argument::Value::Relocation(ArgumentRelocation {}),
// ObjInsArg::BranchDest(dest) => argument::Value::BranchDest(*dest),
// }),
// }
// }
// }
//
// impl ArgumentValue {
// pub fn new(value: &ObjInsArgValue) -> Self {
// Self {
// value: Some(match value {
// ObjInsArgValue::Signed(v) => argument_value::Value::Signed(*v),
// ObjInsArgValue::Unsigned(v) => argument_value::Value::Unsigned(*v),
// ObjInsArgValue::Opaque(v) => argument_value::Value::Opaque(v.to_string()),
// }),
// }
// }
// }
//
// impl Relocation {
// pub fn new(object: &obj::Object, reloc: &ObjReloc) -> Self {
// Self {
// r#type: match reloc.flags {
// object::RelocationFlags::Elf { r_type } => r_type,
// object::RelocationFlags::MachO { r_type, .. } => r_type as u32,
// object::RelocationFlags::Coff { typ } => typ as u32,
// object::RelocationFlags::Xcoff { r_rtype, .. } => r_rtype as u32,
// _ => unreachable!(),
// },
// type_name: object.arch.display_reloc(reloc.flags).into_owned(),
// target: Some(RelocationTarget {
// symbol: Some(Symbol::new(&reloc.target)),
// addend: reloc.addend,
// }),
// }
// }
// }
//
// impl InstructionDiff {
// pub fn new(object: &obj::Object, instruction_diff: &ObjInsDiff) -> Self {
// Self {
// instruction: instruction_diff.ins.as_ref().map(|ins| Instruction::new(object, ins)),
// diff_kind: DiffKind::from(instruction_diff.kind) as i32,
// branch_from: instruction_diff.branch_from.as_ref().map(InstructionBranchFrom::new),
// branch_to: instruction_diff.branch_to.as_ref().map(InstructionBranchTo::new),
// arg_diff: instruction_diff.arg_diff.iter().map(ArgumentDiff::new).collect(),
// }
// }
// }
//
// impl ArgumentDiff {
// pub fn new(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 {
// ObjInsDiffKind::None => DiffKind::DiffNone,
// ObjInsDiffKind::OpMismatch => DiffKind::DiffOpMismatch,
// ObjInsDiffKind::ArgMismatch => DiffKind::DiffArgMismatch,
// ObjInsDiffKind::Replace => DiffKind::DiffReplace,
// ObjInsDiffKind::Delete => DiffKind::DiffDelete,
// ObjInsDiffKind::Insert => DiffKind::DiffInsert,
// }
// }
// }
//
// impl From<ObjDataDiffKind> for DiffKind {
// fn from(value: ObjDataDiffKind) -> Self {
// match value {
// ObjDataDiffKind::None => DiffKind::DiffNone,
// ObjDataDiffKind::Replace => DiffKind::DiffReplace,
// ObjDataDiffKind::Delete => DiffKind::DiffDelete,
// ObjDataDiffKind::Insert => DiffKind::DiffInsert,
// }
// }
// }
//
// impl InstructionBranchFrom {
// pub fn new(value: &ObjInsBranchFrom) -> Self {
// 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 }
// }
// }
-2
View File
@@ -1,3 +1 @@
#[cfg(feature = "any-arch")]
pub mod diff;
pub mod report;
+8 -3
View File
@@ -49,6 +49,7 @@ pub fn run_make(config: &BuildConfig, arg: &Utf8UnixPath) -> BuildStatus {
};
#[cfg(windows)]
let mut command = {
use alloc::borrow::Cow;
use std::os::windows::process::CommandExt;
let mut command = if config.selected_wsl_distro.is_some() {
@@ -60,13 +61,17 @@ pub fn run_make(config: &BuildConfig, arg: &Utf8UnixPath) -> BuildStatus {
// 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(),
// Convert to absolute Unix path
Ok(new_cwd) => Cow::Owned(
Utf8UnixPath::new("/").join(new_cwd.with_unix_encoding()).into_string(),
),
// Otherwise, use the Windows path as is
Err(_) => Cow::Borrowed(cwd.as_str()),
};
command
.arg("--cd")
.arg(cwd.as_str())
.arg::<&str>(cwd.as_ref())
.arg("-d")
.arg(distro)
.arg("--")
+3 -2
View File
@@ -29,6 +29,7 @@ pub fn create_watcher(
modified: Arc<AtomicBool>,
project_dir: &Path,
patterns: GlobSet,
ignore_patterns: GlobSet,
waker: Waker,
) -> notify::Result<Watcher> {
let base_dir = fs::canonicalize(project_dir)?;
@@ -54,8 +55,8 @@ pub fn create_watcher(
let Ok(path) = path.strip_prefix(&base_dir_clone) else {
continue;
};
if patterns.is_match(path) {
// log::info!("File modified: {}", path.display());
if patterns.is_match(path) && !ignore_patterns.is_match(path) {
log::debug!("File modified: {}", path.display());
any_match = true;
}
}
+23 -3
View File
@@ -36,6 +36,8 @@ pub struct ProjectConfig {
pub build_target: Option<bool>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub watch_patterns: Option<Vec<String>>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub ignore_patterns: Option<Vec<String>>,
#[cfg_attr(
feature = "serde",
serde(alias = "objects", skip_serializing_if = "Option::is_none")
@@ -66,7 +68,18 @@ impl ProjectConfig {
.map(|s| Glob::new(s))
.collect::<Result<Vec<Glob>, globset::Error>>()?
} else {
DEFAULT_WATCH_PATTERNS.iter().map(|s| Glob::new(s).unwrap()).collect()
default_watch_patterns()
})
}
pub fn build_ignore_patterns(&self) -> Result<Vec<Glob>, globset::Error> {
Ok(if let Some(ignore_patterns) = &self.ignore_patterns {
ignore_patterns
.iter()
.map(|s| Glob::new(s))
.collect::<Result<Vec<Glob>, globset::Error>>()?
} else {
default_ignore_patterns()
})
}
}
@@ -191,14 +204,21 @@ pub struct ScratchConfig {
pub const CONFIG_FILENAMES: [&str; 3] = ["objdiff.json", "objdiff.yml", "objdiff.yaml"];
pub const DEFAULT_WATCH_PATTERNS: &[&str] = &[
"*.c", "*.cp", "*.cpp", "*.cxx", "*.h", "*.hp", "*.hpp", "*.hxx", "*.s", "*.S", "*.asm",
"*.inc", "*.py", "*.yml", "*.txt", "*.json",
"*.c", "*.cc", "*.cp", "*.cpp", "*.cxx", "*.c++", "*.h", "*.hh", "*.hp", "*.hpp", "*.hxx",
"*.h++", "*.pch", "*.pch++", "*.inc", "*.s", "*.S", "*.asm", "*.py", "*.yml", "*.txt",
"*.json",
];
pub const DEFAULT_IGNORE_PATTERNS: &[&str] = &["build/**/*"];
pub fn default_watch_patterns() -> Vec<Glob> {
DEFAULT_WATCH_PATTERNS.iter().map(|s| Glob::new(s).unwrap()).collect()
}
pub fn default_ignore_patterns() -> Vec<Glob> {
DEFAULT_IGNORE_PATTERNS.iter().map(|s| Glob::new(s).unwrap()).collect()
}
#[cfg(feature = "std")]
#[derive(Clone, Eq, PartialEq)]
pub struct ProjectConfigInfo {
+8
View File
@@ -496,6 +496,14 @@ fn diff_instruction(
result.right_args_diff.push(InstructionArgDiffIndex::new(b_diff));
}
}
if result.kind == InstructionDiffKind::None
&& left_resolved.code.len() != right_resolved.code.len()
{
// If everything else matches but the raw code length differs (e.g. x86 instructions
// with same disassembly but different encoding), mark as op mismatch
result.kind = InstructionDiffKind::OpMismatch;
state.diff_score += PENALTY_REG_DIFF;
}
return Ok(result);
}

Some files were not shown because too many files have changed in this diff Show More