Instruction disassembly in `dol diff`

When a function diff is detected in
`dol diff`, objdiff-core is used to
print a detailed view highlighting
any differences.

Resolves #28
This commit is contained in:
Luke Street 2024-06-03 20:31:06 -06:00
parent d63111466b
commit 255123796e
7 changed files with 730 additions and 51 deletions

249
Cargo.lock generated
View File

@ -167,6 +167,12 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
[[package]] [[package]]
name = "block-buffer" name = "block-buffer"
version = "0.10.4" version = "0.10.4"
@ -255,7 +261,7 @@ checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
dependencies = [ dependencies = [
"ansi_term", "ansi_term",
"atty", "atty",
"bitflags", "bitflags 1.3.2",
"strsim", "strsim",
"textwrap", "textwrap",
"unicode-width", "unicode-width",
@ -318,6 +324,31 @@ version = "0.8.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
[[package]]
name = "crossterm"
version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df"
dependencies = [
"bitflags 2.5.0",
"crossterm_winapi",
"libc",
"mio",
"parking_lot",
"signal-hook",
"signal-hook-mio",
"winapi",
]
[[package]]
name = "crossterm_winapi"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b"
dependencies = [
"winapi",
]
[[package]] [[package]]
name = "crypto-common" name = "crypto-common"
version = "0.1.6" version = "0.1.6"
@ -336,13 +367,14 @@ checksum = "c2e06f9bce634a3c898eb1e5cb949ff63133cbb218af93cc9b38b31d6f3ea285"
[[package]] [[package]]
name = "decomp-toolkit" name = "decomp-toolkit"
version = "0.8.3" version = "0.9.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"ar", "ar",
"argp", "argp",
"base16ct", "base16ct",
"base64", "base64",
"crossterm",
"cwdemangle", "cwdemangle",
"enable-ansi-support", "enable-ansi-support",
"filetime", "filetime",
@ -455,7 +487,7 @@ checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
"redox_syscall", "redox_syscall 0.4.1",
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
@ -542,6 +574,12 @@ version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]] [[package]]
name = "hermit-abi" name = "hermit-abi"
version = "0.1.19" version = "0.1.19"
@ -687,6 +725,16 @@ dependencies = [
"pkg-config", "pkg-config",
] ]
[[package]]
name = "lock_api"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.21" version = "0.4.21"
@ -736,6 +784,18 @@ dependencies = [
"adler", "adler",
] ]
[[package]]
name = "mio"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
dependencies = [
"libc",
"log",
"wasi",
"windows-sys 0.48.0",
]
[[package]] [[package]]
name = "multimap" name = "multimap"
version = "0.10.0" version = "0.10.0"
@ -855,8 +915,8 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
[[package]] [[package]]
name = "objdiff-core" name = "objdiff-core"
version = "1.0.0" version = "2.0.0-alpha.3"
source = "git+https://github.com/encounter/objdiff?rev=8b36fa4fc657d9edf767797af9d394bb32898711#8b36fa4fc657d9edf767797af9d394bb32898711" source = "git+https://github.com/encounter/objdiff?rev=a5a6a3928e392d5af5d92826e73b77e074b8788c#a5a6a3928e392d5af5d92826e73b77e074b8788c"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"byteorder", "byteorder",
@ -870,6 +930,7 @@ dependencies = [
"ppc750cl", "ppc750cl",
"serde", "serde",
"similar", "similar",
"strum",
] ]
[[package]] [[package]]
@ -933,6 +994,29 @@ dependencies = [
"supports-color 2.1.0", "supports-color 2.1.0",
] ]
[[package]]
name = "parking_lot"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
dependencies = [
"cfg-if",
"libc",
"redox_syscall 0.5.1",
"smallvec",
"windows-targets 0.52.4",
]
[[package]] [[package]]
name = "path-slash" name = "path-slash"
version = "0.2.1" version = "0.2.1"
@ -996,7 +1080,7 @@ version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77a1a2f1f0a7ecff9c31abbe177637be0e97a0aef46cf8738ece09327985d998" checksum = "77a1a2f1f0a7ecff9c31abbe177637be0e97a0aef46cf8738ece09327985d998"
dependencies = [ dependencies = [
"bitflags", "bitflags 1.3.2",
"getopts", "getopts",
"memchr", "memchr",
"unicase", "unicase",
@ -1047,7 +1131,16 @@ version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
dependencies = [ dependencies = [
"bitflags", "bitflags 1.3.2",
]
[[package]]
name = "redox_syscall"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e"
dependencies = [
"bitflags 2.5.0",
] ]
[[package]] [[package]]
@ -1106,6 +1199,12 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rustversion"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
[[package]] [[package]]
name = "ryu" name = "ryu"
version = "1.0.17" version = "1.0.17"
@ -1127,6 +1226,12 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19d36299972b96b8ae7e8f04ecbf75fb41a27bf3781af00abcf57609774cb911" checksum = "19d36299972b96b8ae7e8f04ecbf75fb41a27bf3781af00abcf57609774cb911"
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.199" version = "1.0.199"
@ -1213,6 +1318,36 @@ dependencies = [
"lazy_static", "lazy_static",
] ]
[[package]]
name = "signal-hook"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801"
dependencies = [
"libc",
"signal-hook-registry",
]
[[package]]
name = "signal-hook-mio"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af"
dependencies = [
"libc",
"mio",
"signal-hook",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "similar" name = "similar"
version = "2.5.0" version = "2.5.0"
@ -1246,7 +1381,7 @@ version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4b19911debfb8c2fb1107bc6cb2d61868aaf53a988449213959bb1b5b1ed95f" checksum = "b4b19911debfb8c2fb1107bc6cb2d61868aaf53a988449213959bb1b5b1ed95f"
dependencies = [ dependencies = [
"heck", "heck 0.4.1",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.52", "syn 2.0.52",
@ -1258,6 +1393,28 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "strum"
version = "0.26.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29"
dependencies = [
"strum_macros",
]
[[package]]
name = "strum_macros"
version = "0.26.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7993a8e3a9e88a00351486baae9522c91b123a088f76469e5bd5cc17198ea87"
dependencies = [
"heck 0.5.0",
"proc-macro2",
"quote",
"rustversion",
"syn 2.0.52",
]
[[package]] [[package]]
name = "supports-color" name = "supports-color"
version = "2.1.0" version = "2.1.0"
@ -1306,7 +1463,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "874dcfa363995604333cf947ae9f751ca3af4522c60886774c4963943b4746b1" checksum = "874dcfa363995604333cf947ae9f751ca3af4522c60886774c4963943b4746b1"
dependencies = [ dependencies = [
"bincode", "bincode",
"bitflags", "bitflags 1.3.2",
"fancy-regex", "fancy-regex",
"flate2", "flate2",
"fnv", "fnv",
@ -1497,6 +1654,12 @@ dependencies = [
"winapi-util", "winapi-util",
] ]
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]] [[package]]
name = "winapi" name = "winapi"
version = "0.3.9" version = "0.3.9"
@ -1543,13 +1706,37 @@ dependencies = [
"windows_x86_64_msvc 0.42.2", "windows_x86_64_msvc 0.42.2",
] ]
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets 0.48.5",
]
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.52.0" version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [ dependencies = [
"windows-targets", "windows-targets 0.52.4",
]
[[package]]
name = "windows-targets"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
"windows_aarch64_gnullvm 0.48.5",
"windows_aarch64_msvc 0.48.5",
"windows_i686_gnu 0.48.5",
"windows_i686_msvc 0.48.5",
"windows_x86_64_gnu 0.48.5",
"windows_x86_64_gnullvm 0.48.5",
"windows_x86_64_msvc 0.48.5",
] ]
[[package]] [[package]]
@ -1573,6 +1760,12 @@ version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]] [[package]]
name = "windows_aarch64_gnullvm" name = "windows_aarch64_gnullvm"
version = "0.52.4" version = "0.52.4"
@ -1585,6 +1778,12 @@ version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]] [[package]]
name = "windows_aarch64_msvc" name = "windows_aarch64_msvc"
version = "0.52.4" version = "0.52.4"
@ -1597,6 +1796,12 @@ version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]] [[package]]
name = "windows_i686_gnu" name = "windows_i686_gnu"
version = "0.52.4" version = "0.52.4"
@ -1609,6 +1814,12 @@ version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]] [[package]]
name = "windows_i686_msvc" name = "windows_i686_msvc"
version = "0.52.4" version = "0.52.4"
@ -1621,6 +1832,12 @@ version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]] [[package]]
name = "windows_x86_64_gnu" name = "windows_x86_64_gnu"
version = "0.52.4" version = "0.52.4"
@ -1633,6 +1850,12 @@ version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]] [[package]]
name = "windows_x86_64_gnullvm" name = "windows_x86_64_gnullvm"
version = "0.52.4" version = "0.52.4"
@ -1645,6 +1868,12 @@ version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]] [[package]]
name = "windows_x86_64_msvc" name = "windows_x86_64_msvc"
version = "0.52.4" version = "0.52.4"

View File

@ -3,13 +3,13 @@ name = "decomp-toolkit"
description = "Yet another GameCube/Wii decompilation toolkit." description = "Yet another GameCube/Wii decompilation toolkit."
authors = ["Luke Street <luke@street.dev>"] authors = ["Luke Street <luke@street.dev>"]
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
version = "0.8.3" version = "0.9.0"
edition = "2021" edition = "2021"
publish = false publish = false
repository = "https://github.com/encounter/decomp-toolkit" repository = "https://github.com/encounter/decomp-toolkit"
readme = "README.md" readme = "README.md"
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
rust-version = "1.70.0" rust-version = "1.73.0"
[[bin]] [[bin]]
name = "dtk" name = "dtk"
@ -29,6 +29,7 @@ ar = { git = "https://github.com/bjorn3/rust-ar.git", branch = "write_symbol_tab
argp = "0.3.0" argp = "0.3.0"
base16ct = "0.2.0" base16ct = "0.2.0"
base64 = "0.22.1" base64 = "0.22.1"
crossterm = "0.27.0"
cwdemangle = "1.0.0" cwdemangle = "1.0.0"
enable-ansi-support = "0.2.1" enable-ansi-support = "0.2.1"
filetime = "0.2.23" filetime = "0.2.23"
@ -47,7 +48,7 @@ nintendo-lz = "0.1.3"
nodtool = { git = "https://github.com/encounter/nod-rs", rev = "03b83484cb17f94408fa0ef8e50d94951464d1b2" } nodtool = { git = "https://github.com/encounter/nod-rs", rev = "03b83484cb17f94408fa0ef8e50d94951464d1b2" }
#nodtool = { path = "../nod-rs/nodtool" } #nodtool = { path = "../nod-rs/nodtool" }
num_enum = "0.7.2" num_enum = "0.7.2"
objdiff-core = { git = "https://github.com/encounter/objdiff", rev = "8b36fa4fc657d9edf767797af9d394bb32898711", features = ["ppc"] } objdiff-core = { git = "https://github.com/encounter/objdiff", rev = "a5a6a3928e392d5af5d92826e73b77e074b8788c", features = ["ppc"] }
#objdiff-core = { path = "../objdiff/objdiff-core", features = ["ppc"] } #objdiff-core = { path = "../objdiff/objdiff-core", features = ["ppc"] }
object = { version = "0.35.0", features = ["read_core", "std", "elf", "write_std"], default-features = false } object = { version = "0.35.0", features = ["read_core", "std", "elf", "write_std"], default-features = false }
once_cell = "1.19.0" once_cell = "1.19.0"

View File

@ -45,6 +45,7 @@ use crate::{
write_splits_file, write_symbols_file, SectionAddressRef, write_splits_file, write_symbols_file, SectionAddressRef,
}, },
dep::DepFile, dep::DepFile,
diff::{calc_diff_ranges, print_diff, process_code},
dol::process_dol, dol::process_dol,
elf::{process_elf, write_elf}, elf::{process_elf, write_elf},
file::{ file::{
@ -1503,6 +1504,31 @@ where P: AsRef<Path> {
Ok(()) Ok(())
} }
/// Check if two symbols' names match, allowing for differences in compiler-generated names,
/// like @1234 and @5678, or init$1234 and init$5678.
fn symbol_name_fuzzy_eq(a: &ObjSymbol, b: &ObjSymbol) -> bool {
if a.name == b.name {
return true;
}
// Match e.g. @1234 and @5678
if a.name.starts_with('@') && b.name.starts_with('@') {
if let (Ok(_), Ok(_)) = (a.name[1..].parse::<u32>(), b.name[1..].parse::<u32>()) {
return true;
}
}
// Match e.g. init$1234 and init$5678
if let (Some(a_dollar), Some(b_dollar)) = (a.name.rfind('$'), b.name.rfind('$')) {
if a.name[..a_dollar] == b.name[..b_dollar] {
if let (Ok(_), Ok(_)) =
(a.name[a_dollar + 1..].parse::<u32>(), b.name[b_dollar + 1..].parse::<u32>())
{
return true;
}
}
}
false
}
fn diff(args: DiffArgs) -> Result<()> { fn diff(args: DiffArgs) -> Result<()> {
log::info!("Loading {}", args.config.display()); log::info!("Loading {}", args.config.display());
let mut config_file = buf_reader(&args.config)?; let mut config_file = buf_reader(&args.config)?;
@ -1545,7 +1571,7 @@ fn diff(args: DiffArgs) -> Result<()> {
}); });
let mut found = false; let mut found = false;
if let Some((_, linked_sym)) = linked_sym { if let Some((_, linked_sym)) = linked_sym {
if linked_sym.name.starts_with(&orig_sym.name) { if symbol_name_fuzzy_eq(linked_sym, orig_sym) {
if linked_sym.size != orig_sym.size && if linked_sym.size != orig_sym.size &&
// TODO validate common symbol sizes // TODO validate common symbol sizes
// (need to account for inflation bug) // (need to account for inflation bug)
@ -1643,8 +1669,37 @@ fn diff(args: DiffArgs) -> Result<()> {
orig_sym.size, orig_sym.size,
orig_sym.address orig_sym.address
); );
// Disassemble and print the diff using objdiff-core if it's a function
let mut handled = false;
if orig_sym.kind == ObjSymbolKind::Function
&& orig_section.kind == ObjSectionKind::Code
&& linked_sym.kind == ObjSymbolKind::Function
&& linked_section.kind == ObjSectionKind::Code
{
let config = objdiff_core::diff::DiffObjConfig::default();
let orig_code = process_code(&obj, orig_sym, orig_section, &config)?;
let linked_code = process_code(&linked_obj, linked_sym, linked_section, &config)?;
let (left_diff, right_diff) = objdiff_core::diff::code::diff_code(
&orig_code,
&linked_code,
objdiff_core::obj::SymbolRef::default(),
objdiff_core::obj::SymbolRef::default(),
&config,
)?;
let ranges = calc_diff_ranges(&left_diff.instructions, &right_diff.instructions, 3);
// objdiff may miss relocation differences, so fall back to printing the data diff
// if we don't have any instruction ranges to print
if !ranges.is_empty() {
print_diff(&left_diff, &right_diff, &ranges)?;
handled = true;
}
}
if !handled {
log::error!("Original: {}", hex::encode_upper(orig_data)); log::error!("Original: {}", hex::encode_upper(orig_data));
log::error!("Linked: {}", hex::encode_upper(linked_data)); log::error!("Linked: {}", hex::encode_upper(linked_data));
}
std::process::exit(1); std::process::exit(1);
} }
} }

View File

@ -6,6 +6,7 @@ use std::{
}; };
use anyhow::Result; use anyhow::Result;
use object::elf;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::obj::SymbolIndex; use crate::obj::SymbolIndex;
@ -64,6 +65,47 @@ pub struct ObjReloc {
pub module: Option<u32>, pub module: Option<u32>,
} }
impl ObjReloc {
/// Calculates the ELF r_offset and r_type for a relocation.
pub fn to_elf(&self, addr: u32) -> (u64, u32) {
let mut r_offset = addr as u64;
let r_type = match self.kind {
ObjRelocKind::Absolute => {
if r_offset & 3 == 0 {
elf::R_PPC_ADDR32
} else {
elf::R_PPC_UADDR32
}
}
ObjRelocKind::PpcAddr16Hi => {
r_offset = (r_offset & !3) + 2;
elf::R_PPC_ADDR16_HI
}
ObjRelocKind::PpcAddr16Ha => {
r_offset = (r_offset & !3) + 2;
elf::R_PPC_ADDR16_HA
}
ObjRelocKind::PpcAddr16Lo => {
r_offset = (r_offset & !3) + 2;
elf::R_PPC_ADDR16_LO
}
ObjRelocKind::PpcRel24 => {
r_offset &= !3;
elf::R_PPC_REL24
}
ObjRelocKind::PpcRel14 => {
r_offset &= !3;
elf::R_PPC_REL14
}
ObjRelocKind::PpcEmbSda21 => {
r_offset &= !3;
elf::R_PPC_EMB_SDA21
}
};
(r_offset, r_type)
}
}
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct ObjRelocations { pub struct ObjRelocations {
relocations: BTreeMap<u32, ObjReloc>, relocations: BTreeMap<u32, ObjReloc>,

384
src/util/diff.rs Normal file
View File

@ -0,0 +1,384 @@
//! This includes helpers to convert between decomp-toolkit types and objdiff-core types.
//! Eventually it'd be nice to share [ObjInfo] and related types between decomp-toolkit and
//! objdiff-core to avoid this conversion.
use std::{
io::{stdout, Write},
ops::Range,
};
use anyhow::Result;
use crossterm::style::Color;
use itertools::Itertools;
use objdiff_core::{
arch::{ObjArch, ProcessCodeResult},
diff::{
display::{display_diff, DiffText},
DiffObjConfig, ObjInsDiff, ObjInsDiffKind, ObjSymbolDiff,
},
};
use object::RelocationFlags;
use crate::obj::{ObjInfo, ObjReloc, ObjSection, ObjSymbol};
/// Processes code for a PPC function using objdiff-core.
/// Returns [ProcessCodeResult] for other objdiff-core functions to accept.
pub fn process_code(
obj: &ObjInfo,
symbol: &ObjSymbol,
section: &ObjSection,
config: &DiffObjConfig,
) -> Result<ProcessCodeResult> {
let arch = objdiff_core::arch::ppc::ObjArchPpc {};
let orig_relocs = section
.relocations
.range(symbol.address as u32..symbol.address as u32 + symbol.size as u32)
.map(|(a, r)| to_objdiff_reloc(obj, a, r))
.collect_vec();
let orig_data =
section.data_range(symbol.address as u32, symbol.address as u32 + symbol.size as u32)?;
arch.process_code(
symbol.address,
orig_data,
section.elf_index,
&orig_relocs,
&Default::default(),
config,
)
}
/// Calculates ranges of instructions to print, collapsing ranges of unchanged instructions.
/// (e.g. `grep -C`)
pub fn calc_diff_ranges(
left: &[ObjInsDiff],
right: &[ObjInsDiff],
collapse_lines: usize,
) -> Vec<Range<usize>> {
enum State {
None,
DiffStart(usize),
DiffRange(Range<usize>),
}
let mut state = State::None;
let mut idx = 0usize;
let mut left_iter = left.iter();
let mut right_iter = right.iter();
let mut ranges = Vec::new();
// Left and right should always have the same number of instructions
while let (Some(left_ins), Some(right_ins)) = (left_iter.next(), right_iter.next()) {
match &state {
State::None => {
if left_ins.kind != ObjInsDiffKind::None || right_ins.kind != ObjInsDiffKind::None {
state = State::DiffStart(idx.saturating_sub(collapse_lines));
}
}
State::DiffStart(start) => {
if left_ins.kind == ObjInsDiffKind::None && right_ins.kind == ObjInsDiffKind::None {
state = State::DiffRange(*start..idx);
}
}
State::DiffRange(range) => {
if left_ins.kind != ObjInsDiffKind::None || right_ins.kind != ObjInsDiffKind::None {
// Restart the range if we find a another diff
state = State::DiffStart(range.start);
} else if idx > range.end + collapse_lines * 2 {
// If we've gone collapse_lines * 2 instructions without a diff, add the range
ranges.push(range.start..range.end + collapse_lines);
state = State::None;
}
}
}
idx += 1;
}
// Handle the last range
match state {
State::None => {}
State::DiffStart(start) => {
ranges.push(start..idx);
}
State::DiffRange(range) => {
ranges.push(range.start..idx.min(range.end + collapse_lines));
}
}
ranges
}
pub fn print_diff(
left: &ObjSymbolDiff,
right: &ObjSymbolDiff,
ranges: &[Range<usize>],
) -> Result<()> {
let (w, _) = crossterm::terminal::size()?;
let mut stdout = stdout();
for range in ranges {
if range.start > 0 {
crossterm::queue!(stdout, crossterm::style::Print("...\n"))?;
}
let left_ins = left.instructions[range.clone()].iter();
let right_ins = right.instructions[range.clone()].iter();
for (left_diff, right_diff) in left_ins.zip(right_ins) {
let left_line = print_line(left_diff, 0);
let right_line = print_line(right_diff, 0);
let mut x = 0;
let hw = (w as usize - 3) / 2;
for span in left_line {
if span.color != Color::Reset {
crossterm::queue!(stdout, crossterm::style::SetForegroundColor(span.color))?;
}
let len = (hw - x).min(span.text.len());
crossterm::queue!(stdout, crossterm::style::Print(&span.text[..len]))?;
x += len;
}
if x < hw {
crossterm::queue!(stdout, crossterm::style::Print(" ".repeat(hw - x)))?;
}
if left_diff.kind != ObjInsDiffKind::None || right_diff.kind != ObjInsDiffKind::None {
crossterm::queue!(
stdout,
crossterm::style::ResetColor,
crossterm::style::Print(" | ")
)?;
} else {
crossterm::queue!(
stdout,
crossterm::style::ResetColor,
crossterm::style::Print(" ")
)?;
}
x = hw + 3;
for span in right_line {
if span.color != Color::Reset {
crossterm::queue!(stdout, crossterm::style::SetForegroundColor(span.color))?;
}
let len = (w as usize - x).min(span.text.len());
crossterm::queue!(stdout, crossterm::style::Print(&span.text[..len]))?;
x += len;
}
crossterm::queue!(stdout, crossterm::style::ResetColor, crossterm::style::Print("\n"))?;
}
}
if matches!(ranges.last().map(|r| r.end), Some(n) if n != left.instructions.len()) {
crossterm::queue!(stdout, crossterm::style::Print("...\n"))?;
}
stdout.flush()?;
Ok(())
}
const COLOR_ROTATION: [Color; 7] = [
Color::Magenta,
Color::Cyan,
Color::Green,
Color::Red,
Color::Yellow,
Color::Blue,
Color::Green,
];
struct Span {
text: String,
color: Color,
}
fn print_line(ins_diff: &ObjInsDiff, base_addr: u64) -> Vec<Span> {
let mut line = Vec::new();
display_diff(ins_diff, base_addr, |text| -> Result<()> {
let label_text;
let mut base_color = match ins_diff.kind {
ObjInsDiffKind::None | ObjInsDiffKind::OpMismatch | ObjInsDiffKind::ArgMismatch => {
Color::Grey
}
ObjInsDiffKind::Replace => Color::Cyan,
ObjInsDiffKind::Delete => Color::Red,
ObjInsDiffKind::Insert => Color::Green,
};
let mut pad_to = 0;
match text {
DiffText::Basic(text) => {
label_text = text.to_string();
}
DiffText::BasicColor(s, idx) => {
label_text = s.to_string();
base_color = COLOR_ROTATION[idx % COLOR_ROTATION.len()];
}
DiffText::Line(num) => {
label_text = format!("{num} ");
base_color = Color::DarkGrey;
pad_to = 5;
}
DiffText::Address(addr) => {
label_text = format!("{:x}:", addr);
pad_to = 5;
}
DiffText::Opcode(mnemonic, _op) => {
label_text = mnemonic.to_string();
if ins_diff.kind == ObjInsDiffKind::OpMismatch {
base_color = Color::Blue;
}
pad_to = 8;
}
DiffText::Argument(arg, diff) => {
label_text = arg.to_string();
if let Some(diff) = diff {
base_color = COLOR_ROTATION[diff.idx % COLOR_ROTATION.len()]
}
}
DiffText::BranchDest(addr) => {
label_text = format!("{addr:x}");
}
DiffText::Symbol(sym) => {
let name = sym.demangled_name.as_ref().unwrap_or(&sym.name);
label_text = name.clone();
base_color = Color::White;
}
DiffText::Spacing(n) => {
line.push(Span { text: " ".repeat(n), color: Color::Reset });
return Ok(());
}
DiffText::Eol => {
return Ok(());
}
}
let len = label_text.len();
line.push(Span { text: label_text, color: base_color });
if pad_to > len {
let pad = (pad_to - len) as u16;
line.push(Span { text: " ".repeat(pad as usize), color: Color::Reset });
}
Ok(())
})
.unwrap();
line
}
/// Converts an [ObjReloc] to an [objdiff_core::obj::ObjReloc].
fn to_objdiff_reloc(obj: &ObjInfo, address: u32, reloc: &ObjReloc) -> objdiff_core::obj::ObjReloc {
let target_symbol = &obj.symbols[reloc.target_symbol];
let target_section = target_symbol.section.map(|i| &obj.sections[i]);
let (r_offset, r_type) = reloc.to_elf(address);
objdiff_core::obj::ObjReloc {
flags: RelocationFlags::Elf { r_type },
address: r_offset,
target: to_objdiff_symbol(target_symbol, target_section, reloc.addend),
target_section: target_section.map(|s| s.name.clone()),
}
}
/// Converts an [ObjSymbol] to an [objdiff_core::obj::ObjSymbol].
fn to_objdiff_symbol(
symbol: &ObjSymbol,
section: Option<&ObjSection>,
addend: i64,
) -> objdiff_core::obj::ObjSymbol {
let mut flags = objdiff_core::obj::ObjSymbolFlagSet::default();
if symbol.flags.is_global() {
flags.0 |= objdiff_core::obj::ObjSymbolFlags::Global;
}
if symbol.flags.is_local() {
flags.0 |= objdiff_core::obj::ObjSymbolFlags::Local;
}
if symbol.flags.is_weak() {
flags.0 |= objdiff_core::obj::ObjSymbolFlags::Weak;
}
if symbol.flags.is_common() {
flags.0 |= objdiff_core::obj::ObjSymbolFlags::Common;
}
if symbol.flags.is_hidden() {
flags.0 |= objdiff_core::obj::ObjSymbolFlags::Hidden;
}
objdiff_core::obj::ObjSymbol {
name: symbol.name.clone(),
demangled_name: symbol.demangled_name.clone(),
address: symbol.address,
section_address: symbol.address - section.map(|s| s.address).unwrap_or(0),
size: symbol.size,
size_known: symbol.size_known,
flags,
addend,
virtual_address: None,
}
}
#[cfg(test)]
mod tests {
use super::*;
macro_rules! ins_diff {
($kind:expr) => {
ObjInsDiff {
ins: None,
kind: $kind,
branch_from: None,
branch_to: None,
arg_diff: vec![],
}
};
}
#[test]
fn test_get_diff_ranges() {
// Test single range
let diff = vec![
ins_diff!(ObjInsDiffKind::None),
ins_diff!(ObjInsDiffKind::None),
ins_diff!(ObjInsDiffKind::Replace),
ins_diff!(ObjInsDiffKind::Replace),
ins_diff!(ObjInsDiffKind::None),
ins_diff!(ObjInsDiffKind::None),
ins_diff!(ObjInsDiffKind::Replace),
ins_diff!(ObjInsDiffKind::None),
ins_diff!(ObjInsDiffKind::None),
ins_diff!(ObjInsDiffKind::None),
ins_diff!(ObjInsDiffKind::None),
ins_diff!(ObjInsDiffKind::None),
];
assert_eq!(calc_diff_ranges(&diff, &diff, 3), vec![0..10]);
// Test combining ranges
let diff = vec![
ins_diff!(ObjInsDiffKind::None),
ins_diff!(ObjInsDiffKind::None),
ins_diff!(ObjInsDiffKind::Replace),
ins_diff!(ObjInsDiffKind::Replace),
ins_diff!(ObjInsDiffKind::None),
ins_diff!(ObjInsDiffKind::None),
ins_diff!(ObjInsDiffKind::Replace),
ins_diff!(ObjInsDiffKind::None),
ins_diff!(ObjInsDiffKind::None),
ins_diff!(ObjInsDiffKind::None),
ins_diff!(ObjInsDiffKind::None),
ins_diff!(ObjInsDiffKind::None),
ins_diff!(ObjInsDiffKind::None),
ins_diff!(ObjInsDiffKind::None),
// This should be combined with the previous range,
// since it's within collapse_lines * 2 + 1 instructions
ins_diff!(ObjInsDiffKind::Replace),
];
assert_eq!(calc_diff_ranges(&diff, &diff, 3), vec![0..15]);
// Test separating ranges
let diff = vec![
// start range 1
ins_diff!(ObjInsDiffKind::None),
ins_diff!(ObjInsDiffKind::None),
ins_diff!(ObjInsDiffKind::Replace),
ins_diff!(ObjInsDiffKind::Replace),
ins_diff!(ObjInsDiffKind::None),
ins_diff!(ObjInsDiffKind::None),
ins_diff!(ObjInsDiffKind::None),
// end range 1
ins_diff!(ObjInsDiffKind::None),
ins_diff!(ObjInsDiffKind::None),
// start range 2
ins_diff!(ObjInsDiffKind::None),
ins_diff!(ObjInsDiffKind::None),
ins_diff!(ObjInsDiffKind::None),
ins_diff!(ObjInsDiffKind::Replace),
ins_diff!(ObjInsDiffKind::Replace),
ins_diff!(ObjInsDiffKind::None),
// end range 2
];
assert_eq!(calc_diff_ranges(&diff, &diff, 3), vec![0..7, 9..15]);
}
}

View File

@ -727,41 +727,8 @@ pub fn write_elf(obj: &ObjInfo, export_all: bool) -> Result<Vec<u8>> {
} }
writer.write_align_relocation(); writer.write_align_relocation();
ensure!(writer.len() == out_section.rela_offset); ensure!(writer.len() == out_section.rela_offset);
for (reloc_address, reloc) in section.relocations.iter() { for (addr, reloc) in section.relocations.iter() {
let mut r_offset = reloc_address as u64; let (r_offset, r_type) = reloc.to_elf(addr);
let r_type = match reloc.kind {
ObjRelocKind::Absolute => {
if r_offset & 3 == 0 {
elf::R_PPC_ADDR32
} else {
elf::R_PPC_UADDR32
}
}
ObjRelocKind::PpcAddr16Hi => {
r_offset = (r_offset & !3) + 2;
elf::R_PPC_ADDR16_HI
}
ObjRelocKind::PpcAddr16Ha => {
r_offset = (r_offset & !3) + 2;
elf::R_PPC_ADDR16_HA
}
ObjRelocKind::PpcAddr16Lo => {
r_offset = (r_offset & !3) + 2;
elf::R_PPC_ADDR16_LO
}
ObjRelocKind::PpcRel24 => {
r_offset &= !3;
elf::R_PPC_REL24
}
ObjRelocKind::PpcRel14 => {
r_offset &= !3;
elf::R_PPC_REL14
}
ObjRelocKind::PpcEmbSda21 => {
r_offset &= !3;
elf::R_PPC_EMB_SDA21
}
};
let r_sym = symbol_map[reloc.target_symbol] let r_sym = symbol_map[reloc.target_symbol]
.ok_or_else(|| anyhow!("Relocation against stripped symbol"))?; .ok_or_else(|| anyhow!("Relocation against stripped symbol"))?;
writer.write_relocation(true, &Rel { r_offset, r_sym, r_type, r_addend: reloc.addend }); writer.write_relocation(true, &Rel { r_offset, r_sym, r_type, r_addend: reloc.addend });

View File

@ -6,6 +6,7 @@ pub mod bin2c;
pub mod comment; pub mod comment;
pub mod config; pub mod config;
pub mod dep; pub mod dep;
pub mod diff;
pub mod dol; pub mod dol;
pub mod dwarf; pub mod dwarf;
pub mod elf; pub mod elf;