mirror of https://github.com/encounter/objdiff.git
Updates & initial MIPS support
This commit is contained in:
parent
7bbdba5566
commit
b55c919f4d
|
@ -43,6 +43,15 @@ dependencies = [
|
||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "0.7.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ansi_term"
|
name = "ansi_term"
|
||||||
version = "0.12.1"
|
version = "0.12.1"
|
||||||
|
@ -134,6 +143,17 @@ version = "0.1.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "73b5e5f48b927f04e952dedc932f31995a65a0bf65ec971c74436e51bf6e970d"
|
checksum = "73b5e5f48b927f04e952dedc932f31995a65a0bf65ec971c74436e51bf6e970d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atty"
|
||||||
|
version = "0.2.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||||
|
dependencies = [
|
||||||
|
"hermit-abi",
|
||||||
|
"libc",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
@ -146,6 +166,29 @@ version = "0.13.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
|
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bindgen"
|
||||||
|
version = "0.60.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "062dddbc1ba4aca46de6338e2bf87771414c335f7b2f2036e8f3e9befebf88e6"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"cexpr",
|
||||||
|
"clang-sys",
|
||||||
|
"clap",
|
||||||
|
"env_logger",
|
||||||
|
"lazy_static",
|
||||||
|
"lazycell",
|
||||||
|
"log",
|
||||||
|
"peeking_take_while",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"regex",
|
||||||
|
"rustc-hash",
|
||||||
|
"shlex",
|
||||||
|
"which",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "1.3.2"
|
version = "1.3.2"
|
||||||
|
@ -225,6 +268,15 @@ 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 = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
|
checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cexpr"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
|
||||||
|
dependencies = [
|
||||||
|
"nom",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-expr"
|
name = "cfg-expr"
|
||||||
version = "0.10.3"
|
version = "0.10.3"
|
||||||
|
@ -249,6 +301,41 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clang-sys"
|
||||||
|
version = "1.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a050e2153c5be08febd6734e29298e844fdb0fa21aeddd63b4eb7baa106c69b"
|
||||||
|
dependencies = [
|
||||||
|
"glob",
|
||||||
|
"libc",
|
||||||
|
"libloading",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap"
|
||||||
|
version = "3.2.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "23b71c3ce99b7611011217b366d923f1d0a7e07a92bb2dbf1e84508c673ca3bd"
|
||||||
|
dependencies = [
|
||||||
|
"atty",
|
||||||
|
"bitflags",
|
||||||
|
"clap_lex",
|
||||||
|
"indexmap",
|
||||||
|
"strsim",
|
||||||
|
"termcolor",
|
||||||
|
"textwrap",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_lex"
|
||||||
|
version = "0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
|
||||||
|
dependencies = [
|
||||||
|
"os_str_bytes",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clipboard-win"
|
name = "clipboard-win"
|
||||||
version = "4.4.2"
|
version = "4.4.2"
|
||||||
|
@ -433,7 +520,8 @@ checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cwdemangle"
|
name = "cwdemangle"
|
||||||
version = "0.1.0"
|
version = "0.1.2"
|
||||||
|
source = "git+https://github.com/encounter/cwdemangle?rev=ba448f403320f32b808e0dcf3040c6424664acab#ba448f403320f32b808e0dcf3040c6424664acab"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"argh",
|
"argh",
|
||||||
]
|
]
|
||||||
|
@ -617,6 +705,12 @@ dependencies = [
|
||||||
"web-sys",
|
"web-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "either"
|
||||||
|
version = "1.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "emath"
|
name = "emath"
|
||||||
version = "0.19.0"
|
version = "0.19.0"
|
||||||
|
@ -627,6 +721,19 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "env_logger"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3"
|
||||||
|
dependencies = [
|
||||||
|
"atty",
|
||||||
|
"humantime",
|
||||||
|
"log",
|
||||||
|
"regex",
|
||||||
|
"termcolor",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "epaint"
|
name = "epaint"
|
||||||
version = "0.19.0"
|
version = "0.19.0"
|
||||||
|
@ -867,6 +974,12 @@ dependencies = [
|
||||||
"system-deps",
|
"system-deps",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "glob"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glow"
|
name = "glow"
|
||||||
version = "0.11.2"
|
version = "0.11.2"
|
||||||
|
@ -973,6 +1086,12 @@ dependencies = [
|
||||||
"system-deps",
|
"system-deps",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.12.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heck"
|
name = "heck"
|
||||||
version = "0.3.3"
|
version = "0.3.3"
|
||||||
|
@ -988,6 +1107,21 @@ version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
|
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hermit-abi"
|
||||||
|
version = "0.1.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "humantime"
|
||||||
|
version = "2.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ident_case"
|
name = "ident_case"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
|
@ -1005,6 +1139,16 @@ dependencies = [
|
||||||
"unicode-normalization",
|
"unicode-normalization",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indexmap"
|
||||||
|
version = "1.9.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"hashbrown",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "inotify"
|
name = "inotify"
|
||||||
version = "0.9.6"
|
version = "0.9.6"
|
||||||
|
@ -1098,6 +1242,12 @@ version = "1.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazycell"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.132"
|
version = "0.2.132"
|
||||||
|
@ -1427,6 +1577,7 @@ dependencies = [
|
||||||
"notify",
|
"notify",
|
||||||
"object",
|
"object",
|
||||||
"ppc750cl",
|
"ppc750cl",
|
||||||
|
"rabbitizer",
|
||||||
"rfd",
|
"rfd",
|
||||||
"serde",
|
"serde",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
@ -1450,6 +1601,12 @@ version = "1.14.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2f7254b99e31cad77da24b08ebf628882739a608578bb1bcdfc1f9c21260d7c0"
|
checksum = "2f7254b99e31cad77da24b08ebf628882739a608578bb1bcdfc1f9c21260d7c0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "os_str_bytes"
|
||||||
|
version = "6.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "osmesa-sys"
|
name = "osmesa-sys"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
|
@ -1503,6 +1660,12 @@ dependencies = [
|
||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "peeking_take_while"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "percent-encoding"
|
name = "percent-encoding"
|
||||||
version = "2.1.0"
|
version = "2.1.0"
|
||||||
|
@ -1571,6 +1734,17 @@ dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rabbitizer"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "git+https://github.com/encounter/rabbitizer-rs?rev=10c279b2ef251c62885b1dcdcfe740b0db8e9956#10c279b2ef251c62885b1dcdcfe740b0db8e9956"
|
||||||
|
dependencies = [
|
||||||
|
"bindgen",
|
||||||
|
"cc",
|
||||||
|
"cty",
|
||||||
|
"glob",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "raw-window-handle"
|
name = "raw-window-handle"
|
||||||
version = "0.4.3"
|
version = "0.4.3"
|
||||||
|
@ -1609,6 +1783,23 @@ dependencies = [
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.6.27"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rfd"
|
name = "rfd"
|
||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
|
@ -1644,6 +1835,12 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc-hash"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "safe_arch"
|
name = "safe_arch"
|
||||||
version = "0.5.2"
|
version = "0.5.2"
|
||||||
|
@ -1746,6 +1943,12 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shlex"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slotmap"
|
name = "slotmap"
|
||||||
version = "1.0.6"
|
version = "1.0.6"
|
||||||
|
@ -1826,6 +2029,21 @@ dependencies = [
|
||||||
"version-compare",
|
"version-compare",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "termcolor"
|
||||||
|
version = "1.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "textwrap"
|
||||||
|
version = "0.15.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.33"
|
version = "1.0.33"
|
||||||
|
@ -2232,6 +2450,17 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "which"
|
||||||
|
version = "4.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1c831fbbee9e129a8cf93e7747a82da9d95ba8e16621cae60ec2cdc849bacb7b"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
"libc",
|
||||||
|
"once_cell",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "widestring"
|
name = "widestring"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
|
|
|
@ -20,11 +20,12 @@ thiserror = "1.0.33"
|
||||||
flagset = "0.4.3"
|
flagset = "0.4.3"
|
||||||
object = "0.29.0"
|
object = "0.29.0"
|
||||||
notify = "5.0.0"
|
notify = "5.0.0"
|
||||||
cwdemangle = "0.1.1"
|
cwdemangle = { git = "https://github.com/encounter/cwdemangle", rev = "ba448f403320f32b808e0dcf3040c6424664acab" }
|
||||||
log = "0.4.17"
|
log = "0.4.17"
|
||||||
rfd = { version = "0.10.0" } # , default-features = false, features = ['xdg-portal']
|
rfd = { version = "0.10.0" } # , default-features = false, features = ['xdg-portal']
|
||||||
egui_extras = "0.19.0"
|
egui_extras = "0.19.0"
|
||||||
ppc750cl = { git = "https://github.com/terorie/ppc750cl" }
|
ppc750cl = { git = "https://github.com/terorie/ppc750cl" }
|
||||||
|
rabbitizer = { git = "https://github.com/encounter/rabbitizer-rs", rev = "10c279b2ef251c62885b1dcdcfe740b0db8e9956" }
|
||||||
|
|
||||||
# native:
|
# native:
|
||||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||||
|
|
63
src/app.rs
63
src/app.rs
|
@ -10,11 +10,12 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use eframe::Frame;
|
use eframe::Frame;
|
||||||
|
use egui::Widget;
|
||||||
use notify::{RecursiveMode, Watcher};
|
use notify::{RecursiveMode, Watcher};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
jobs::{
|
jobs::{
|
||||||
build::{queue_build, BuildResult},
|
build::{queue_build, BuildResult, BuildStatus},
|
||||||
Job, JobResult, JobState,
|
Job, JobResult, JobState,
|
||||||
},
|
},
|
||||||
views::{
|
views::{
|
||||||
|
@ -30,6 +31,13 @@ pub enum View {
|
||||||
FunctionDiff,
|
FunctionDiff,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
|
||||||
|
pub enum DiffKind {
|
||||||
|
#[default]
|
||||||
|
SplitObj,
|
||||||
|
WholeBinary,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default, serde::Deserialize, serde::Serialize)]
|
#[derive(Default, serde::Deserialize, serde::Serialize)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct ViewState {
|
pub struct ViewState {
|
||||||
|
@ -43,17 +51,24 @@ pub struct ViewState {
|
||||||
pub selected_symbol: Option<String>,
|
pub selected_symbol: Option<String>,
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub current_view: View,
|
pub current_view: View,
|
||||||
|
#[serde(skip)]
|
||||||
|
pub show_config: bool,
|
||||||
// Config
|
// Config
|
||||||
|
pub diff_kind: DiffKind,
|
||||||
pub reverse_fn_order: bool,
|
pub reverse_fn_order: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Clone, serde::Deserialize, serde::Serialize)]
|
#[derive(Default, Clone, serde::Deserialize, serde::Serialize)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct AppConfig {
|
pub struct AppConfig {
|
||||||
|
// Split obj
|
||||||
pub project_dir: Option<PathBuf>,
|
pub project_dir: Option<PathBuf>,
|
||||||
pub build_asm_dir: Option<PathBuf>,
|
pub build_asm_dir: Option<PathBuf>,
|
||||||
pub build_src_dir: Option<PathBuf>,
|
pub build_src_dir: Option<PathBuf>,
|
||||||
pub build_obj: Option<String>,
|
pub build_obj: Option<String>,
|
||||||
|
// Whole binary
|
||||||
|
pub left_obj: Option<PathBuf>,
|
||||||
|
pub right_obj: Option<PathBuf>,
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub project_dir_change: bool,
|
pub project_dir_change: bool,
|
||||||
}
|
}
|
||||||
|
@ -118,6 +133,9 @@ impl eframe::App for App {
|
||||||
if ui.button("Quit").clicked() {
|
if ui.button("Quit").clicked() {
|
||||||
frame.close();
|
frame.close();
|
||||||
}
|
}
|
||||||
|
if ui.button("Show config").clicked() {
|
||||||
|
view_state.show_config = !view_state.show_config;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -148,6 +166,34 @@ impl eframe::App for App {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
egui::Window::new("Config").open(&mut view_state.show_config).show(ctx, |ui| {
|
||||||
|
ui.label("Diff type:");
|
||||||
|
|
||||||
|
if egui::RadioButton::new(
|
||||||
|
view_state.diff_kind == DiffKind::SplitObj,
|
||||||
|
"Split object diff",
|
||||||
|
)
|
||||||
|
.ui(ui)
|
||||||
|
.on_hover_text("Compare individual object files")
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
view_state.diff_kind = DiffKind::SplitObj;
|
||||||
|
}
|
||||||
|
|
||||||
|
if egui::RadioButton::new(
|
||||||
|
view_state.diff_kind == DiffKind::WholeBinary,
|
||||||
|
"Whole binary diff",
|
||||||
|
)
|
||||||
|
.ui(ui)
|
||||||
|
.on_hover_text("Compare two full binaries")
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
view_state.diff_kind = DiffKind::WholeBinary;
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.separator();
|
||||||
|
});
|
||||||
|
|
||||||
if view_state.jobs.iter().any(|job| {
|
if view_state.jobs.iter().any(|job| {
|
||||||
if let Some(handle) = &job.handle {
|
if let Some(handle) = &job.handle {
|
||||||
return !handle.is_finished();
|
return !handle.is_finished();
|
||||||
|
@ -156,7 +202,6 @@ impl eframe::App for App {
|
||||||
}) {
|
}) {
|
||||||
ctx.request_repaint();
|
ctx.request_repaint();
|
||||||
} else {
|
} else {
|
||||||
ctx.request_repaint();
|
|
||||||
ctx.request_repaint_after(Duration::from_millis(100));
|
ctx.request_repaint_after(Duration::from_millis(100));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -187,6 +232,20 @@ impl eframe::App for App {
|
||||||
JobResult::Build(state) => {
|
JobResult::Build(state) => {
|
||||||
self.view_state.build = Some(state);
|
self.view_state.build = Some(state);
|
||||||
}
|
}
|
||||||
|
JobResult::BinDiff(state) => {
|
||||||
|
self.view_state.build = Some(Box::new(BuildResult {
|
||||||
|
first_status: BuildStatus {
|
||||||
|
success: true,
|
||||||
|
log: "".to_string(),
|
||||||
|
},
|
||||||
|
second_status: BuildStatus {
|
||||||
|
success: true,
|
||||||
|
log: "".to_string(),
|
||||||
|
},
|
||||||
|
first_obj: Some(state.first_obj),
|
||||||
|
second_obj: Some(state.second_obj),
|
||||||
|
}));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
|
172
src/diff.rs
172
src/diff.rs
|
@ -1,87 +1,18 @@
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use ppc750cl::{disasm_iter, Argument};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
editops::{editops_find, LevEditType},
|
editops::{editops_find, LevEditType},
|
||||||
obj::{
|
obj::{
|
||||||
ObjInfo, ObjIns, ObjInsArg, ObjInsArgDiff, ObjInsBranchFrom, ObjInsBranchTo, ObjInsDiff,
|
mips, ppc, ObjArchitecture, ObjInfo, ObjInsArg, ObjInsArgDiff, ObjInsBranchFrom,
|
||||||
ObjInsDiffKind, ObjReloc, ObjRelocKind, ObjSection, ObjSectionKind, ObjSymbol,
|
ObjInsBranchTo, ObjInsDiff, ObjInsDiffKind, ObjReloc, ObjSection, ObjSectionKind,
|
||||||
ObjSymbolFlags,
|
ObjSymbol, ObjSymbolFlags,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Relative relocation, can be Simm or BranchDest
|
|
||||||
fn is_relative_arg(arg: &ObjInsArg) -> bool {
|
|
||||||
matches!(arg, ObjInsArg::Arg(arg) if matches!(arg, Argument::Simm(_) | Argument::BranchDest(_)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Relative or absolute relocation, can be Uimm, Simm or Offset
|
|
||||||
fn is_rel_abs_arg(arg: &ObjInsArg) -> bool {
|
|
||||||
matches!(arg, ObjInsArg::Arg(arg) if matches!(arg, Argument::Uimm(_) | Argument::Simm(_) | Argument::Offset(_)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_offset_arg(arg: &ObjInsArg) -> bool { matches!(arg, ObjInsArg::Arg(Argument::Offset(_))) }
|
|
||||||
|
|
||||||
fn process_code(data: &[u8], address: u64, relocs: &[ObjReloc]) -> Result<(Vec<u8>, Vec<ObjIns>)> {
|
|
||||||
let ins_count = data.len() / 4;
|
|
||||||
let mut ops = Vec::<u8>::with_capacity(ins_count);
|
|
||||||
let mut insts = Vec::<ObjIns>::with_capacity(ins_count);
|
|
||||||
for mut ins in disasm_iter(data, address as u32) {
|
|
||||||
let reloc = relocs.iter().find(|r| (r.address as u32 & !3) == ins.addr);
|
|
||||||
if let Some(reloc) = reloc {
|
|
||||||
// Zero out relocations
|
|
||||||
ins.code = match reloc.kind {
|
|
||||||
ObjRelocKind::PpcEmbSda21 => ins.code & !0x1FFFFF,
|
|
||||||
ObjRelocKind::PpcRel24 => ins.code & !0x3FFFFFC,
|
|
||||||
ObjRelocKind::PpcRel14 => ins.code & !0xFFFC,
|
|
||||||
ObjRelocKind::PpcAddr16Hi
|
|
||||||
| ObjRelocKind::PpcAddr16Ha
|
|
||||||
| ObjRelocKind::PpcAddr16Lo => ins.code & !0xFFFF,
|
|
||||||
_ => ins.code,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
let simplified = ins.simplified();
|
|
||||||
let mut args: Vec<ObjInsArg> =
|
|
||||||
simplified.args.iter().map(|a| ObjInsArg::Arg(a.clone())).collect();
|
|
||||||
if let Some(reloc) = reloc {
|
|
||||||
match reloc.kind {
|
|
||||||
ObjRelocKind::PpcEmbSda21 => {
|
|
||||||
args = vec![args[0].clone(), ObjInsArg::Reloc];
|
|
||||||
}
|
|
||||||
ObjRelocKind::PpcRel24 | ObjRelocKind::PpcRel14 => {
|
|
||||||
let arg = args
|
|
||||||
.iter_mut()
|
|
||||||
.rfind(|a| is_relative_arg(a))
|
|
||||||
.ok_or_else(|| anyhow::Error::msg("Failed to locate rel arg for reloc"))?;
|
|
||||||
*arg = ObjInsArg::Reloc;
|
|
||||||
}
|
|
||||||
ObjRelocKind::PpcAddr16Hi
|
|
||||||
| ObjRelocKind::PpcAddr16Ha
|
|
||||||
| ObjRelocKind::PpcAddr16Lo => {
|
|
||||||
let arg = args.iter_mut().rfind(|a| is_rel_abs_arg(a)).ok_or_else(|| {
|
|
||||||
anyhow::Error::msg("Failed to locate rel/abs arg for reloc")
|
|
||||||
})?;
|
|
||||||
*arg =
|
|
||||||
if is_offset_arg(arg) { ObjInsArg::RelocOffset } else { ObjInsArg::Reloc };
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ops.push(simplified.ins.op as u8);
|
|
||||||
let suffix = simplified.ins.suffix();
|
|
||||||
insts.push(ObjIns {
|
|
||||||
ins: simplified.ins,
|
|
||||||
mnemonic: format!("{}{}", simplified.mnemonic, suffix),
|
|
||||||
args,
|
|
||||||
reloc: reloc.cloned(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Ok((ops, insts))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn diff_code(
|
pub fn diff_code(
|
||||||
|
arch: ObjArchitecture,
|
||||||
left_data: &[u8],
|
left_data: &[u8],
|
||||||
right_data: &[u8],
|
right_data: &[u8],
|
||||||
left_symbol: &mut ObjSymbol,
|
left_symbol: &mut ObjSymbol,
|
||||||
|
@ -89,12 +20,30 @@ pub fn diff_code(
|
||||||
left_relocs: &[ObjReloc],
|
left_relocs: &[ObjReloc],
|
||||||
right_relocs: &[ObjReloc],
|
right_relocs: &[ObjReloc],
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let left_code =
|
let left_code = &left_data[left_symbol.section_address as usize
|
||||||
&left_data[left_symbol.address as usize..(left_symbol.address + left_symbol.size) as usize];
|
..(left_symbol.section_address + left_symbol.size) as usize];
|
||||||
let (left_ops, left_insts) = process_code(left_code, left_symbol.address, left_relocs)?;
|
let right_code = &right_data[right_symbol.section_address as usize
|
||||||
let right_code = &right_data
|
..(right_symbol.section_address + right_symbol.size) as usize];
|
||||||
[right_symbol.address as usize..(right_symbol.address + right_symbol.size) as usize];
|
let ((left_ops, left_insts), (right_ops, right_insts)) = match arch {
|
||||||
let (right_ops, right_insts) = process_code(right_code, right_symbol.address, right_relocs)?;
|
ObjArchitecture::PowerPc => (
|
||||||
|
ppc::process_code(left_code, left_symbol.address, left_relocs)?,
|
||||||
|
ppc::process_code(right_code, right_symbol.address, right_relocs)?,
|
||||||
|
),
|
||||||
|
ObjArchitecture::Mips => (
|
||||||
|
mips::process_code(
|
||||||
|
left_code,
|
||||||
|
left_symbol.address,
|
||||||
|
left_symbol.address + left_symbol.size,
|
||||||
|
left_relocs,
|
||||||
|
)?,
|
||||||
|
mips::process_code(
|
||||||
|
right_code,
|
||||||
|
right_symbol.address,
|
||||||
|
left_symbol.address + left_symbol.size,
|
||||||
|
right_relocs,
|
||||||
|
)?,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
let mut left_diff = Vec::<ObjInsDiff>::new();
|
let mut left_diff = Vec::<ObjInsDiff>::new();
|
||||||
let mut right_diff = Vec::<ObjInsDiff>::new();
|
let mut right_diff = Vec::<ObjInsDiff>::new();
|
||||||
|
@ -111,7 +60,7 @@ pub fn diff_code(
|
||||||
let left_addr = op.first_start as u32 * 4;
|
let left_addr = op.first_start as u32 * 4;
|
||||||
let right_addr = op.second_start as u32 * 4;
|
let right_addr = op.second_start as u32 * 4;
|
||||||
while let (Some(left), Some(right)) = (cur_left, cur_right) {
|
while let (Some(left), Some(right)) = (cur_left, cur_right) {
|
||||||
if (left.ins.addr - left_symbol.address as u32) < left_addr {
|
if (left.address - left_symbol.address as u32) < left_addr {
|
||||||
left_diff.push(ObjInsDiff { ins: Some(left.clone()), ..ObjInsDiff::default() });
|
left_diff.push(ObjInsDiff { ins: Some(left.clone()), ..ObjInsDiff::default() });
|
||||||
right_diff
|
right_diff
|
||||||
.push(ObjInsDiff { ins: Some(right.clone()), ..ObjInsDiff::default() });
|
.push(ObjInsDiff { ins: Some(right.clone()), ..ObjInsDiff::default() });
|
||||||
|
@ -122,10 +71,10 @@ pub fn diff_code(
|
||||||
cur_right = right_iter.next();
|
cur_right = right_iter.next();
|
||||||
}
|
}
|
||||||
if let (Some(left), Some(right)) = (cur_left, cur_right) {
|
if let (Some(left), Some(right)) = (cur_left, cur_right) {
|
||||||
if (left.ins.addr - left_symbol.address as u32) != left_addr {
|
if (left.address - left_symbol.address as u32) != left_addr {
|
||||||
return Err(anyhow::Error::msg("Instruction address mismatch (left)"));
|
return Err(anyhow::Error::msg("Instruction address mismatch (left)"));
|
||||||
}
|
}
|
||||||
if (right.ins.addr - right_symbol.address as u32) != right_addr {
|
if (right.address - right_symbol.address as u32) != right_addr {
|
||||||
return Err(anyhow::Error::msg("Instruction address mismatch (right)"));
|
return Err(anyhow::Error::msg("Instruction address mismatch (right)"));
|
||||||
}
|
}
|
||||||
match op.op_type {
|
match op.op_type {
|
||||||
|
@ -178,7 +127,11 @@ pub fn diff_code(
|
||||||
}
|
}
|
||||||
|
|
||||||
let total = left_insts.len();
|
let total = left_insts.len();
|
||||||
let percent = ((total - diff_state.diff_count) as f32 / total as f32) * 100.0;
|
let percent = if diff_state.diff_count >= total {
|
||||||
|
0.0
|
||||||
|
} else {
|
||||||
|
((total - diff_state.diff_count) as f32 / total as f32) * 100.0
|
||||||
|
};
|
||||||
left_symbol.match_percent = percent;
|
left_symbol.match_percent = percent;
|
||||||
right_symbol.match_percent = percent;
|
right_symbol.match_percent = percent;
|
||||||
|
|
||||||
|
@ -194,17 +147,22 @@ fn resolve_branches(vec: &mut [ObjInsDiff]) {
|
||||||
let mut addr_map = BTreeMap::<u32, usize>::new();
|
let mut addr_map = BTreeMap::<u32, usize>::new();
|
||||||
for (i, ins_diff) in vec.iter().enumerate() {
|
for (i, ins_diff) in vec.iter().enumerate() {
|
||||||
if let Some(ins) = &ins_diff.ins {
|
if let Some(ins) = &ins_diff.ins {
|
||||||
addr_map.insert(ins.ins.addr, i);
|
addr_map.insert(ins.address, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Generate branches
|
// Generate branches
|
||||||
let mut branches = BTreeMap::<usize, ObjInsBranchFrom>::new();
|
let mut branches = BTreeMap::<usize, ObjInsBranchFrom>::new();
|
||||||
for (i, ins_diff) in vec.iter_mut().enumerate() {
|
for (i, ins_diff) in vec.iter_mut().enumerate() {
|
||||||
if let Some(ins) = &ins_diff.ins {
|
if let Some(ins) = &ins_diff.ins {
|
||||||
if ins.ins.is_blr() || ins.reloc.is_some() {
|
// if ins.ins.is_blr() || ins.reloc.is_some() {
|
||||||
continue;
|
// continue;
|
||||||
}
|
// }
|
||||||
if let Some(ins_idx) = ins.ins.branch_dest().and_then(|dest| addr_map.get(&dest)) {
|
if let Some(ins_idx) = ins
|
||||||
|
.args
|
||||||
|
.iter()
|
||||||
|
.find_map(|a| if let ObjInsArg::BranchOffset(offs) = a { Some(offs) } else { None })
|
||||||
|
.and_then(|offs| addr_map.get(&((ins.address as i32 + offs) as u32)))
|
||||||
|
{
|
||||||
if let Some(branch) = branches.get_mut(ins_idx) {
|
if let Some(branch) = branches.get_mut(ins_idx) {
|
||||||
ins_diff.branch_to =
|
ins_diff.branch_to =
|
||||||
Some(ObjInsBranchTo { ins_idx: *ins_idx, branch_idx: branch.branch_idx });
|
Some(ObjInsBranchTo { ins_idx: *ins_idx, branch_idx: branch.branch_idx });
|
||||||
|
@ -253,15 +211,8 @@ fn arg_eq(
|
||||||
right_diff: &ObjInsDiff,
|
right_diff: &ObjInsDiff,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
return match left {
|
return match left {
|
||||||
ObjInsArg::Arg(l) => match right {
|
ObjInsArg::PpcArg(l) => match right {
|
||||||
ObjInsArg::Arg(r) => match r {
|
ObjInsArg::PpcArg(r) => format!("{}", l) == format!("{}", r),
|
||||||
Argument::BranchDest(_) => {
|
|
||||||
// Compare dest instruction idx after diffing
|
|
||||||
left_diff.branch_to.as_ref().map(|b| b.ins_idx)
|
|
||||||
== right_diff.branch_to.as_ref().map(|b| b.ins_idx)
|
|
||||||
}
|
|
||||||
_ => format!("{}", l) == format!("{}", r),
|
|
||||||
},
|
|
||||||
_ => false,
|
_ => false,
|
||||||
},
|
},
|
||||||
ObjInsArg::Reloc => {
|
ObjInsArg::Reloc => {
|
||||||
|
@ -271,13 +222,21 @@ fn arg_eq(
|
||||||
right_diff.ins.as_ref().and_then(|i| i.reloc.as_ref()),
|
right_diff.ins.as_ref().and_then(|i| i.reloc.as_ref()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
ObjInsArg::RelocOffset => {
|
ObjInsArg::RelocWithBase => {
|
||||||
matches!(right, ObjInsArg::RelocOffset)
|
matches!(right, ObjInsArg::RelocWithBase)
|
||||||
&& reloc_eq(
|
&& reloc_eq(
|
||||||
left_diff.ins.as_ref().and_then(|i| i.reloc.as_ref()),
|
left_diff.ins.as_ref().and_then(|i| i.reloc.as_ref()),
|
||||||
right_diff.ins.as_ref().and_then(|i| i.reloc.as_ref()),
|
right_diff.ins.as_ref().and_then(|i| i.reloc.as_ref()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
ObjInsArg::MipsArg(ls) => {
|
||||||
|
matches!(right, ObjInsArg::MipsArg(rs) if ls == rs)
|
||||||
|
}
|
||||||
|
ObjInsArg::BranchOffset(_) => {
|
||||||
|
// Compare dest instruction idx after diffing
|
||||||
|
left_diff.branch_to.as_ref().map(|b| b.ins_idx)
|
||||||
|
== right_diff.branch_to.as_ref().map(|b| b.ins_idx)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -303,7 +262,7 @@ fn compare_ins(
|
||||||
) -> Result<InsDiffResult> {
|
) -> Result<InsDiffResult> {
|
||||||
let mut result = InsDiffResult::default();
|
let mut result = InsDiffResult::default();
|
||||||
if let (Some(left_ins), Some(right_ins)) = (&left.ins, &right.ins) {
|
if let (Some(left_ins), Some(right_ins)) = (&left.ins, &right.ins) {
|
||||||
if left_ins.args.len() != right_ins.args.len() || left_ins.ins.op != right_ins.ins.op {
|
if left_ins.args.len() != right_ins.args.len() || left_ins.op != right_ins.op {
|
||||||
// Totally different op
|
// Totally different op
|
||||||
result.kind = ObjInsDiffKind::Replace;
|
result.kind = ObjInsDiffKind::Replace;
|
||||||
state.diff_count += 1;
|
state.diff_count += 1;
|
||||||
|
@ -324,8 +283,10 @@ fn compare_ins(
|
||||||
state.diff_count += 1;
|
state.diff_count += 1;
|
||||||
}
|
}
|
||||||
let a_str = match a {
|
let a_str = match a {
|
||||||
ObjInsArg::Arg(arg) => format!("{}", arg),
|
ObjInsArg::PpcArg(arg) => format!("{}", arg),
|
||||||
ObjInsArg::Reloc | ObjInsArg::RelocOffset => String::new(),
|
ObjInsArg::Reloc | ObjInsArg::RelocWithBase => String::new(),
|
||||||
|
ObjInsArg::MipsArg(str) => str.clone(),
|
||||||
|
ObjInsArg::BranchOffset(arg) => format!("{}", arg),
|
||||||
};
|
};
|
||||||
let a_diff = if let Some(idx) = state.left_args_idx.get(&a_str) {
|
let a_diff = if let Some(idx) = state.left_args_idx.get(&a_str) {
|
||||||
ObjInsArgDiff { idx: *idx }
|
ObjInsArgDiff { idx: *idx }
|
||||||
|
@ -336,8 +297,10 @@ fn compare_ins(
|
||||||
ObjInsArgDiff { idx }
|
ObjInsArgDiff { idx }
|
||||||
};
|
};
|
||||||
let b_str = match b {
|
let b_str = match b {
|
||||||
ObjInsArg::Arg(arg) => format!("{}", arg),
|
ObjInsArg::PpcArg(arg) => format!("{}", arg),
|
||||||
ObjInsArg::Reloc | ObjInsArg::RelocOffset => String::new(),
|
ObjInsArg::Reloc | ObjInsArg::RelocWithBase => String::new(),
|
||||||
|
ObjInsArg::MipsArg(str) => str.clone(),
|
||||||
|
ObjInsArg::BranchOffset(arg) => format!("{}", arg),
|
||||||
};
|
};
|
||||||
let b_diff = if let Some(idx) = state.right_args_idx.get(&b_str) {
|
let b_diff = if let Some(idx) = state.right_args_idx.get(&b_str) {
|
||||||
ObjInsArgDiff { idx: *idx }
|
ObjInsArgDiff { idx: *idx }
|
||||||
|
@ -380,6 +343,7 @@ pub fn diff_objs(left: &mut ObjInfo, right: &mut ObjInfo) -> Result<()> {
|
||||||
right_symbol.diff_symbol = Some(left_symbol.name.clone());
|
right_symbol.diff_symbol = Some(left_symbol.name.clone());
|
||||||
if left_section.kind == ObjSectionKind::Code {
|
if left_section.kind == ObjSectionKind::Code {
|
||||||
diff_code(
|
diff_code(
|
||||||
|
left.architecture,
|
||||||
&left_section.data,
|
&left_section.data,
|
||||||
&right_section.data,
|
&right_section.data,
|
||||||
left_symbol,
|
left_symbol,
|
||||||
|
|
168
src/elf.rs
168
src/elf.rs
|
@ -4,13 +4,13 @@ use anyhow::{Context, Result};
|
||||||
use cwdemangle::demangle;
|
use cwdemangle::demangle;
|
||||||
use flagset::Flags;
|
use flagset::Flags;
|
||||||
use object::{
|
use object::{
|
||||||
Object, ObjectSection, ObjectSymbol, RelocationKind, RelocationTarget, SectionKind, SymbolKind,
|
Architecture, File, Object, ObjectSection, ObjectSymbol, RelocationKind, RelocationTarget,
|
||||||
SymbolSection,
|
SectionKind, Symbol, SymbolKind, SymbolSection,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::obj::{
|
use crate::obj::{
|
||||||
ObjInfo, ObjReloc, ObjRelocKind, ObjSection, ObjSectionKind, ObjSymbol, ObjSymbolFlagSet,
|
ObjArchitecture, ObjInfo, ObjReloc, ObjRelocKind, ObjSection, ObjSectionKind, ObjSymbol,
|
||||||
ObjSymbolFlags,
|
ObjSymbolFlagSet, ObjSymbolFlags,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn to_obj_section_kind(kind: SectionKind) -> ObjSectionKind {
|
fn to_obj_section_kind(kind: SectionKind) -> ObjSectionKind {
|
||||||
|
@ -22,7 +22,7 @@ fn to_obj_section_kind(kind: SectionKind) -> ObjSectionKind {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_obj_symbol(symbol: &object::Symbol<'_, '_>) -> Result<ObjSymbol> {
|
fn to_obj_symbol(obj_file: &File<'_>, symbol: &Symbol<'_, '_>) -> Result<ObjSymbol> {
|
||||||
let mut name = symbol.name().context("Failed to process symbol name")?;
|
let mut name = symbol.name().context("Failed to process symbol name")?;
|
||||||
if name.is_empty() {
|
if name.is_empty() {
|
||||||
println!("Found empty sym: {:?}", symbol);
|
println!("Found empty sym: {:?}", symbol);
|
||||||
|
@ -41,10 +41,18 @@ fn to_obj_symbol(symbol: &object::Symbol<'_, '_>) -> Result<ObjSymbol> {
|
||||||
if symbol.is_weak() {
|
if symbol.is_weak() {
|
||||||
flags = ObjSymbolFlagSet(flags.0 | ObjSymbolFlags::Weak);
|
flags = ObjSymbolFlagSet(flags.0 | ObjSymbolFlags::Weak);
|
||||||
}
|
}
|
||||||
|
let section_address = if let Some(section) =
|
||||||
|
symbol.section_index().and_then(|idx| obj_file.section_by_index(idx).ok())
|
||||||
|
{
|
||||||
|
symbol.address() - section.address()
|
||||||
|
} else {
|
||||||
|
symbol.address()
|
||||||
|
};
|
||||||
Ok(ObjSymbol {
|
Ok(ObjSymbol {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
demangled_name: demangle(name),
|
demangled_name: demangle(name),
|
||||||
address: symbol.address(),
|
address: symbol.address(),
|
||||||
|
section_address,
|
||||||
size: symbol.size(),
|
size: symbol.size(),
|
||||||
size_known: symbol.size() != 0,
|
size_known: symbol.size() != 0,
|
||||||
flags,
|
flags,
|
||||||
|
@ -61,7 +69,11 @@ const R_PPC_REL24: u32 = 10;
|
||||||
const R_PPC_REL14: u32 = 11;
|
const R_PPC_REL14: u32 = 11;
|
||||||
const R_PPC_EMB_SDA21: u32 = 109;
|
const R_PPC_EMB_SDA21: u32 = 109;
|
||||||
|
|
||||||
fn filter_sections(obj_file: &object::File<'_>) -> Result<Vec<ObjSection>> {
|
const R_MIPS_26: u32 = 4;
|
||||||
|
const R_MIPS_HI16: u32 = 5;
|
||||||
|
const R_MIPS_LO16: u32 = 6;
|
||||||
|
|
||||||
|
fn filter_sections(obj_file: &File<'_>) -> Result<Vec<ObjSection>> {
|
||||||
let mut result = Vec::<ObjSection>::new();
|
let mut result = Vec::<ObjSection>::new();
|
||||||
for section in obj_file.sections() {
|
for section in obj_file.sections() {
|
||||||
if section.size() == 0 {
|
if section.size() == 0 {
|
||||||
|
@ -91,7 +103,7 @@ fn filter_sections(obj_file: &object::File<'_>) -> Result<Vec<ObjSection>> {
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn symbols_by_section(obj_file: &object::File<'_>, section: &ObjSection) -> Result<Vec<ObjSymbol>> {
|
fn symbols_by_section(obj_file: &File<'_>, section: &ObjSection) -> Result<Vec<ObjSymbol>> {
|
||||||
let mut result = Vec::<ObjSymbol>::new();
|
let mut result = Vec::<ObjSymbol>::new();
|
||||||
for symbol in obj_file.symbols() {
|
for symbol in obj_file.symbols() {
|
||||||
if symbol.kind() == SymbolKind::Section {
|
if symbol.kind() == SymbolKind::Section {
|
||||||
|
@ -102,11 +114,11 @@ fn symbols_by_section(obj_file: &object::File<'_>, section: &ObjSection) -> Resu
|
||||||
if symbol.is_local() && section.kind == ObjSectionKind::Code {
|
if symbol.is_local() && section.kind == ObjSectionKind::Code {
|
||||||
// TODO strip local syms in diff?
|
// TODO strip local syms in diff?
|
||||||
let name = symbol.name().context("Failed to process symbol name")?;
|
let name = symbol.name().context("Failed to process symbol name")?;
|
||||||
if name.starts_with("lbl_") {
|
if symbol.size() == 0 || name.starts_with("lbl_") {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result.push(to_obj_symbol(&symbol)?);
|
result.push(to_obj_symbol(obj_file, &symbol)?);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -124,38 +136,64 @@ fn symbols_by_section(obj_file: &object::File<'_>, section: &ObjSection) -> Resu
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn common_symbols(obj_file: &object::File<'_>) -> Result<Vec<ObjSymbol>> {
|
fn common_symbols(obj_file: &File<'_>) -> Result<Vec<ObjSymbol>> {
|
||||||
let mut result = Vec::<ObjSymbol>::new();
|
let mut result = Vec::<ObjSymbol>::new();
|
||||||
for symbol in obj_file.symbols() {
|
for symbol in obj_file.symbols() {
|
||||||
if symbol.is_common() {
|
if symbol.is_common() {
|
||||||
result.push(to_obj_symbol(&symbol)?);
|
result.push(to_obj_symbol(obj_file, &symbol)?);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn locate_section_symbol(
|
fn find_section_symbol(
|
||||||
obj_file: &object::File<'_>,
|
obj_file: &File<'_>,
|
||||||
target: &object::Symbol<'_, '_>,
|
target: &Symbol<'_, '_>,
|
||||||
address: u64,
|
address: u64,
|
||||||
) -> Result<ObjSymbol> {
|
) -> Result<ObjSymbol> {
|
||||||
let section_index =
|
let section_index =
|
||||||
target.section_index().ok_or_else(|| anyhow::Error::msg("Unknown section index"))?;
|
target.section_index().ok_or_else(|| anyhow::Error::msg("Unknown section index"))?;
|
||||||
|
let section = obj_file.section_by_index(section_index)?;
|
||||||
|
let mut closest_symbol: Option<Symbol<'_, '_>> = None;
|
||||||
for symbol in obj_file.symbols() {
|
for symbol in obj_file.symbols() {
|
||||||
if !matches!(symbol.section_index(), Some(idx) if idx == section_index) {
|
if !matches!(symbol.section_index(), Some(idx) if idx == section_index) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if symbol.kind() == SymbolKind::Section || symbol.address() != address {
|
if symbol.kind() == SymbolKind::Section || symbol.address() != address {
|
||||||
|
if symbol.address() < address
|
||||||
|
&& symbol.size() != 0
|
||||||
|
&& (closest_symbol.is_none()
|
||||||
|
|| matches!(&closest_symbol, Some(s) if s.address() <= symbol.address()))
|
||||||
|
{
|
||||||
|
closest_symbol = Some(symbol);
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
return to_obj_symbol(&symbol);
|
return to_obj_symbol(obj_file, &symbol);
|
||||||
}
|
}
|
||||||
Err(anyhow::Error::msg("Failed to locate reloc offset sym"))
|
let (name, offset) = closest_symbol
|
||||||
|
.and_then(|s| s.name().map(|n| (n, s.address())).ok())
|
||||||
|
.or_else(|| section.name().map(|n| (n, section.address())).ok())
|
||||||
|
.unwrap_or(("<unknown>", 0));
|
||||||
|
let offset_addr = address - offset;
|
||||||
|
Ok(ObjSymbol {
|
||||||
|
name: if offset_addr > 0 { format!("{}+{:#X}", name, address) } else { name.to_string() },
|
||||||
|
demangled_name: None,
|
||||||
|
address,
|
||||||
|
section_address: address - section.address(),
|
||||||
|
size: 0,
|
||||||
|
size_known: false,
|
||||||
|
flags: Default::default(),
|
||||||
|
diff_symbol: None,
|
||||||
|
instructions: vec![],
|
||||||
|
match_percent: 0.0,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn relocations_by_section(
|
fn relocations_by_section(
|
||||||
obj_file: &object::File<'_>,
|
arch: ObjArchitecture,
|
||||||
section: &ObjSection,
|
obj_file: &File<'_>,
|
||||||
|
section: &mut ObjSection,
|
||||||
) -> Result<Vec<ObjReloc>> {
|
) -> Result<Vec<ObjReloc>> {
|
||||||
let obj_section = obj_file
|
let obj_section = obj_file
|
||||||
.section_by_name(§ion.name)
|
.section_by_name(§ion.name)
|
||||||
|
@ -175,19 +213,32 @@ fn relocations_by_section(
|
||||||
};
|
};
|
||||||
let kind = match reloc.kind() {
|
let kind = match reloc.kind() {
|
||||||
RelocationKind::Absolute => ObjRelocKind::Absolute,
|
RelocationKind::Absolute => ObjRelocKind::Absolute,
|
||||||
RelocationKind::Elf(kind) => match kind {
|
RelocationKind::Elf(kind) => match arch {
|
||||||
R_PPC_ADDR16_LO => ObjRelocKind::PpcAddr16Lo,
|
ObjArchitecture::PowerPc => match kind {
|
||||||
R_PPC_ADDR16_HI => ObjRelocKind::PpcAddr16Hi,
|
R_PPC_ADDR16_LO => ObjRelocKind::PpcAddr16Lo,
|
||||||
R_PPC_ADDR16_HA => ObjRelocKind::PpcAddr16Ha,
|
R_PPC_ADDR16_HI => ObjRelocKind::PpcAddr16Hi,
|
||||||
R_PPC_REL24 => ObjRelocKind::PpcRel24,
|
R_PPC_ADDR16_HA => ObjRelocKind::PpcAddr16Ha,
|
||||||
R_PPC_REL14 => ObjRelocKind::PpcRel14,
|
R_PPC_REL24 => ObjRelocKind::PpcRel24,
|
||||||
R_PPC_EMB_SDA21 => ObjRelocKind::PpcEmbSda21,
|
R_PPC_REL14 => ObjRelocKind::PpcRel14,
|
||||||
_ => {
|
R_PPC_EMB_SDA21 => ObjRelocKind::PpcEmbSda21,
|
||||||
return Err(anyhow::Error::msg(format!(
|
_ => {
|
||||||
"Unhandled ELF relocation type: {}",
|
return Err(anyhow::Error::msg(format!(
|
||||||
kind
|
"Unhandled PPC relocation type: {}",
|
||||||
)))
|
kind
|
||||||
}
|
)))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ObjArchitecture::Mips => match kind {
|
||||||
|
R_MIPS_26 => ObjRelocKind::Mips26,
|
||||||
|
R_MIPS_HI16 => ObjRelocKind::MipsHi16,
|
||||||
|
R_MIPS_LO16 => ObjRelocKind::MipsLo16,
|
||||||
|
_ => {
|
||||||
|
return Err(anyhow::Error::msg(format!(
|
||||||
|
"Unhandled MIPS relocation type: {}",
|
||||||
|
kind
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
return Err(anyhow::Error::msg(format!(
|
return Err(anyhow::Error::msg(format!(
|
||||||
|
@ -198,23 +249,39 @@ fn relocations_by_section(
|
||||||
};
|
};
|
||||||
let target_section = match symbol.section() {
|
let target_section = match symbol.section() {
|
||||||
SymbolSection::Common => Some(".comm".to_string()),
|
SymbolSection::Common => Some(".comm".to_string()),
|
||||||
SymbolSection::Section(idx) => obj_file
|
SymbolSection::Section(idx) => {
|
||||||
.section_by_index(idx)
|
obj_file.section_by_index(idx).and_then(|s| s.name().map(|s| s.to_string())).ok()
|
||||||
.map(|s| s.name().map(|s| s.to_string()).ok())
|
}
|
||||||
.ok()
|
|
||||||
.flatten(),
|
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
// println!("Reloc: {:?}", reloc.addend());
|
// println!("Reloc: {:?}, symbol: {:?}", reloc, symbol);
|
||||||
let target = match symbol.kind() {
|
let target = match symbol.kind() {
|
||||||
SymbolKind::Text | SymbolKind::Data | SymbolKind::Unknown => to_obj_symbol(&symbol),
|
SymbolKind::Text | SymbolKind::Data | SymbolKind::Unknown => {
|
||||||
|
to_obj_symbol(obj_file, &symbol)
|
||||||
|
}
|
||||||
SymbolKind::Section => {
|
SymbolKind::Section => {
|
||||||
let addend = reloc.addend();
|
let addend = if reloc.has_implicit_addend() {
|
||||||
if addend < 0 {
|
let addend = u32::from_be_bytes(
|
||||||
Err(anyhow::Error::msg(format!("Negative addend in section reloc: {}", addend)))
|
section.data[address as usize..address as usize + 4].try_into()?,
|
||||||
|
);
|
||||||
|
match kind {
|
||||||
|
ObjRelocKind::MipsHi16 | ObjRelocKind::MipsLo16 => {
|
||||||
|
(addend & 0x0000FFFF) * 4
|
||||||
|
}
|
||||||
|
ObjRelocKind::Mips26 => (addend & 0x03FFFFFF) * 4,
|
||||||
|
_ => todo!(),
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
locate_section_symbol(obj_file, &symbol, addend as u64)
|
let addend = reloc.addend();
|
||||||
}
|
if addend < 0 {
|
||||||
|
return Err(anyhow::Error::msg(format!(
|
||||||
|
"Negative addend in section reloc: {}",
|
||||||
|
addend
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
addend as u32
|
||||||
|
};
|
||||||
|
find_section_symbol(obj_file, &symbol, addend as u64)
|
||||||
}
|
}
|
||||||
_ => Err(anyhow::Error::msg(format!(
|
_ => Err(anyhow::Error::msg(format!(
|
||||||
"Unhandled relocation symbol type {:?}",
|
"Unhandled relocation symbol type {:?}",
|
||||||
|
@ -228,15 +295,26 @@ fn relocations_by_section(
|
||||||
|
|
||||||
pub fn read(obj_path: &Path) -> Result<ObjInfo> {
|
pub fn read(obj_path: &Path) -> Result<ObjInfo> {
|
||||||
let bin_data = fs::read(obj_path)?;
|
let bin_data = fs::read(obj_path)?;
|
||||||
let obj_file = object::File::parse(&*bin_data)?;
|
let obj_file = File::parse(&*bin_data)?;
|
||||||
|
let architecture = match obj_file.architecture() {
|
||||||
|
Architecture::PowerPc => ObjArchitecture::PowerPc,
|
||||||
|
Architecture::Mips => ObjArchitecture::Mips,
|
||||||
|
_ => {
|
||||||
|
return Err(anyhow::Error::msg(format!(
|
||||||
|
"Unsupported architecture: {:?}",
|
||||||
|
obj_file.architecture()
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
};
|
||||||
let mut result = ObjInfo {
|
let mut result = ObjInfo {
|
||||||
|
architecture,
|
||||||
path: obj_path.to_owned(),
|
path: obj_path.to_owned(),
|
||||||
sections: filter_sections(&obj_file)?,
|
sections: filter_sections(&obj_file)?,
|
||||||
common: common_symbols(&obj_file)?,
|
common: common_symbols(&obj_file)?,
|
||||||
};
|
};
|
||||||
for section in &mut result.sections {
|
for section in &mut result.sections {
|
||||||
section.symbols = symbols_by_section(&obj_file, section)?;
|
section.symbols = symbols_by_section(&obj_file, section)?;
|
||||||
section.relocations = relocations_by_section(&obj_file, section)?;
|
section.relocations = relocations_by_section(architecture, &obj_file, section)?;
|
||||||
}
|
}
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
use std::sync::{mpsc::Receiver, Arc, RwLock};
|
||||||
|
|
||||||
|
use anyhow::{Error, Result};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
app::AppConfig,
|
||||||
|
diff::diff_objs,
|
||||||
|
elf,
|
||||||
|
jobs::{queue_job, update_status, Job, JobResult, JobState, Status},
|
||||||
|
obj::ObjInfo,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct BinDiffResult {
|
||||||
|
pub first_obj: ObjInfo,
|
||||||
|
pub second_obj: ObjInfo,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_build(
|
||||||
|
status: &Status,
|
||||||
|
cancel: Receiver<()>,
|
||||||
|
config: Arc<RwLock<AppConfig>>,
|
||||||
|
) -> Result<Box<BinDiffResult>> {
|
||||||
|
let config = config.read().map_err(|_| Error::msg("Failed to lock app config"))?.clone();
|
||||||
|
let left_path = config.left_obj.as_ref().ok_or_else(|| Error::msg("Missing left obj path"))?;
|
||||||
|
let right_path =
|
||||||
|
config.right_obj.as_ref().ok_or_else(|| Error::msg("Missing right obj path"))?;
|
||||||
|
|
||||||
|
update_status(status, "Loading left obj".to_string(), 0, 3, &cancel)?;
|
||||||
|
let mut left_obj = elf::read(left_path)?;
|
||||||
|
|
||||||
|
update_status(status, "Loading right obj".to_string(), 1, 3, &cancel)?;
|
||||||
|
let mut right_obj = elf::read(right_path)?;
|
||||||
|
|
||||||
|
update_status(status, "Performing diff".to_string(), 2, 3, &cancel)?;
|
||||||
|
diff_objs(&mut left_obj, &mut right_obj)?;
|
||||||
|
|
||||||
|
update_status(status, "Complete".to_string(), 3, 3, &cancel)?;
|
||||||
|
Ok(Box::new(BinDiffResult { first_obj: left_obj, second_obj: right_obj }))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn queue_bindiff(config: Arc<RwLock<AppConfig>>) -> JobState {
|
||||||
|
queue_job(Job::BinDiff, move |status, cancel| {
|
||||||
|
run_build(status, cancel, config).map(JobResult::BinDiff)
|
||||||
|
})
|
||||||
|
}
|
|
@ -9,13 +9,15 @@ use std::{
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
use crate::jobs::build::BuildResult;
|
use crate::jobs::{bindiff::BinDiffResult, build::BuildResult};
|
||||||
|
|
||||||
|
pub mod bindiff;
|
||||||
pub mod build;
|
pub mod build;
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
|
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
|
||||||
pub enum Job {
|
pub enum Job {
|
||||||
Build,
|
Build,
|
||||||
|
BinDiff,
|
||||||
}
|
}
|
||||||
pub static JOB_ID: AtomicUsize = AtomicUsize::new(0);
|
pub static JOB_ID: AtomicUsize = AtomicUsize::new(0);
|
||||||
pub struct JobState {
|
pub struct JobState {
|
||||||
|
@ -37,6 +39,7 @@ pub struct JobStatus {
|
||||||
pub enum JobResult {
|
pub enum JobResult {
|
||||||
None,
|
None,
|
||||||
Build(Box<BuildResult>),
|
Build(Box<BuildResult>),
|
||||||
|
BinDiff(Box<BinDiffResult>),
|
||||||
}
|
}
|
||||||
|
|
||||||
fn should_cancel(rx: &Receiver<()>) -> bool {
|
fn should_cancel(rx: &Receiver<()>) -> bool {
|
||||||
|
|
24
src/obj.rs
24
src/obj.rs
|
@ -1,3 +1,6 @@
|
||||||
|
pub mod mips;
|
||||||
|
pub mod ppc;
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use flagset::{flags, FlagSet};
|
use flagset::{flags, FlagSet};
|
||||||
|
@ -31,9 +34,11 @@ pub struct ObjSection {
|
||||||
}
|
}
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum ObjInsArg {
|
pub enum ObjInsArg {
|
||||||
Arg(ppc750cl::Argument),
|
PpcArg(ppc750cl::Argument),
|
||||||
|
MipsArg(String),
|
||||||
Reloc,
|
Reloc,
|
||||||
RelocOffset,
|
RelocWithBase,
|
||||||
|
BranchOffset(i32),
|
||||||
}
|
}
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct ObjInsArgDiff {
|
pub struct ObjInsArgDiff {
|
||||||
|
@ -66,10 +71,13 @@ pub enum ObjInsDiffKind {
|
||||||
}
|
}
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ObjIns {
|
pub struct ObjIns {
|
||||||
pub ins: ppc750cl::Ins,
|
pub address: u32,
|
||||||
|
pub code: u32,
|
||||||
|
pub op: u8,
|
||||||
pub mnemonic: String,
|
pub mnemonic: String,
|
||||||
pub args: Vec<ObjInsArg>,
|
pub args: Vec<ObjInsArg>,
|
||||||
pub reloc: Option<ObjReloc>,
|
pub reloc: Option<ObjReloc>,
|
||||||
|
pub branch_dest: Option<u32>,
|
||||||
}
|
}
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct ObjInsDiff {
|
pub struct ObjInsDiff {
|
||||||
|
@ -88,6 +96,7 @@ pub struct ObjSymbol {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub demangled_name: Option<String>,
|
pub demangled_name: Option<String>,
|
||||||
pub address: u64,
|
pub address: u64,
|
||||||
|
pub section_address: u64,
|
||||||
pub size: u64,
|
pub size: u64,
|
||||||
pub size_known: bool,
|
pub size_known: bool,
|
||||||
pub flags: ObjSymbolFlagSet,
|
pub flags: ObjSymbolFlagSet,
|
||||||
|
@ -97,8 +106,14 @@ pub struct ObjSymbol {
|
||||||
pub instructions: Vec<ObjInsDiff>,
|
pub instructions: Vec<ObjInsDiff>,
|
||||||
pub match_percent: f32,
|
pub match_percent: f32,
|
||||||
}
|
}
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub enum ObjArchitecture {
|
||||||
|
PowerPc,
|
||||||
|
Mips,
|
||||||
|
}
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ObjInfo {
|
pub struct ObjInfo {
|
||||||
|
pub architecture: ObjArchitecture,
|
||||||
pub path: PathBuf,
|
pub path: PathBuf,
|
||||||
pub sections: Vec<ObjSection>,
|
pub sections: Vec<ObjSection>,
|
||||||
pub common: Vec<ObjSymbol>,
|
pub common: Vec<ObjSymbol>,
|
||||||
|
@ -116,6 +131,9 @@ pub enum ObjRelocKind {
|
||||||
// PpcAddr14,
|
// PpcAddr14,
|
||||||
PpcRel14,
|
PpcRel14,
|
||||||
PpcEmbSda21,
|
PpcEmbSda21,
|
||||||
|
Mips26,
|
||||||
|
MipsHi16,
|
||||||
|
MipsLo16,
|
||||||
}
|
}
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ObjReloc {
|
pub struct ObjReloc {
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
use rabbitizer::{config_set_register_fpr_abi_names, Abi, Instruction, SimpleOperandType};
|
||||||
|
|
||||||
|
use crate::obj::{ObjIns, ObjInsArg, ObjReloc};
|
||||||
|
|
||||||
|
pub fn process_code(
|
||||||
|
data: &[u8],
|
||||||
|
start_address: u64,
|
||||||
|
end_address: u64,
|
||||||
|
relocs: &[ObjReloc],
|
||||||
|
) -> Result<(Vec<u8>, Vec<ObjIns>)> {
|
||||||
|
config_set_register_fpr_abi_names(Abi::RABBITIZER_ABI_O32);
|
||||||
|
|
||||||
|
let ins_count = data.len() / 4;
|
||||||
|
let mut ops = Vec::<u8>::with_capacity(ins_count);
|
||||||
|
let mut insts = Vec::<ObjIns>::with_capacity(ins_count);
|
||||||
|
let mut cur_addr = start_address as u32;
|
||||||
|
for chunk in data.chunks_exact(4) {
|
||||||
|
let reloc = relocs.iter().find(|r| (r.address as u32 & !3) == cur_addr);
|
||||||
|
let code = u32::from_be_bytes(chunk.try_into()?);
|
||||||
|
let mut instruction = Instruction::new(code, cur_addr);
|
||||||
|
|
||||||
|
let op = instruction.instr_id() as u8;
|
||||||
|
ops.push(op);
|
||||||
|
|
||||||
|
let mnemonic = instruction.instr_id().get_opcode_name().unwrap_or_default().to_string();
|
||||||
|
let is_branch = instruction.is_branch();
|
||||||
|
let branch_offset = instruction.branch_offset();
|
||||||
|
let branch_dest =
|
||||||
|
if is_branch { Some((cur_addr as i32 + branch_offset) as u32) } else { None };
|
||||||
|
let args = instruction
|
||||||
|
.simple_operands()
|
||||||
|
.iter()
|
||||||
|
.map(|op| match op.kind {
|
||||||
|
SimpleOperandType::Imm | SimpleOperandType::Label => {
|
||||||
|
if is_branch {
|
||||||
|
ObjInsArg::BranchOffset(branch_offset)
|
||||||
|
} else if let Some(reloc) = reloc {
|
||||||
|
if matches!(&reloc.target_section, Some(s) if s == ".text")
|
||||||
|
&& reloc.target.address > start_address
|
||||||
|
&& reloc.target.address < end_address
|
||||||
|
{
|
||||||
|
// Inter-function reloc, convert to branch offset
|
||||||
|
ObjInsArg::BranchOffset(reloc.target.address as i32 - cur_addr as i32)
|
||||||
|
} else {
|
||||||
|
ObjInsArg::Reloc
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ObjInsArg::MipsArg(op.disassembled.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SimpleOperandType::ImmBase => {
|
||||||
|
if reloc.is_some() {
|
||||||
|
ObjInsArg::RelocWithBase
|
||||||
|
} else {
|
||||||
|
ObjInsArg::MipsArg(op.disassembled.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => ObjInsArg::MipsArg(op.disassembled.clone()),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
insts.push(ObjIns {
|
||||||
|
address: cur_addr,
|
||||||
|
code,
|
||||||
|
op,
|
||||||
|
mnemonic,
|
||||||
|
args,
|
||||||
|
reloc: reloc.cloned(),
|
||||||
|
branch_dest,
|
||||||
|
});
|
||||||
|
cur_addr += 4;
|
||||||
|
}
|
||||||
|
Ok((ops, insts))
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
use ppc750cl::{disasm_iter, Argument};
|
||||||
|
|
||||||
|
use crate::obj::{ObjIns, ObjInsArg, ObjReloc, ObjRelocKind};
|
||||||
|
|
||||||
|
// Relative relocation, can be Simm or BranchOffset
|
||||||
|
fn is_relative_arg(arg: &ObjInsArg) -> bool {
|
||||||
|
matches!(arg, ObjInsArg::PpcArg(Argument::Simm(_)) | ObjInsArg::BranchOffset(_))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Relative or absolute relocation, can be Uimm, Simm or Offset
|
||||||
|
fn is_rel_abs_arg(arg: &ObjInsArg) -> bool {
|
||||||
|
matches!(arg, ObjInsArg::PpcArg(arg) if matches!(arg, Argument::Uimm(_) | Argument::Simm(_) | Argument::Offset(_)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_offset_arg(arg: &ObjInsArg) -> bool { matches!(arg, ObjInsArg::PpcArg(Argument::Offset(_))) }
|
||||||
|
|
||||||
|
pub fn process_code(
|
||||||
|
data: &[u8],
|
||||||
|
address: u64,
|
||||||
|
relocs: &[ObjReloc],
|
||||||
|
) -> Result<(Vec<u8>, Vec<ObjIns>)> {
|
||||||
|
let ins_count = data.len() / 4;
|
||||||
|
let mut ops = Vec::<u8>::with_capacity(ins_count);
|
||||||
|
let mut insts = Vec::<ObjIns>::with_capacity(ins_count);
|
||||||
|
for mut ins in disasm_iter(data, address as u32) {
|
||||||
|
let reloc = relocs.iter().find(|r| (r.address as u32 & !3) == ins.addr);
|
||||||
|
if let Some(reloc) = reloc {
|
||||||
|
// Zero out relocations
|
||||||
|
ins.code = match reloc.kind {
|
||||||
|
ObjRelocKind::PpcEmbSda21 => ins.code & !0x1FFFFF,
|
||||||
|
ObjRelocKind::PpcRel24 => ins.code & !0x3FFFFFC,
|
||||||
|
ObjRelocKind::PpcRel14 => ins.code & !0xFFFC,
|
||||||
|
ObjRelocKind::PpcAddr16Hi
|
||||||
|
| ObjRelocKind::PpcAddr16Ha
|
||||||
|
| ObjRelocKind::PpcAddr16Lo => ins.code & !0xFFFF,
|
||||||
|
_ => ins.code,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
let simplified = ins.simplified();
|
||||||
|
let mut args: Vec<ObjInsArg> = simplified
|
||||||
|
.args
|
||||||
|
.iter()
|
||||||
|
.map(|a| match a {
|
||||||
|
Argument::BranchDest(dest) => ObjInsArg::BranchOffset(dest.0),
|
||||||
|
_ => ObjInsArg::PpcArg(a.clone()),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
if let Some(reloc) = reloc {
|
||||||
|
match reloc.kind {
|
||||||
|
ObjRelocKind::PpcEmbSda21 => {
|
||||||
|
args = vec![args[0].clone(), ObjInsArg::Reloc];
|
||||||
|
}
|
||||||
|
ObjRelocKind::PpcRel24 | ObjRelocKind::PpcRel14 => {
|
||||||
|
let arg = args
|
||||||
|
.iter_mut()
|
||||||
|
.rfind(|a| is_relative_arg(a))
|
||||||
|
.ok_or_else(|| anyhow::Error::msg("Failed to locate rel arg for reloc"))?;
|
||||||
|
*arg = ObjInsArg::Reloc;
|
||||||
|
}
|
||||||
|
ObjRelocKind::PpcAddr16Hi
|
||||||
|
| ObjRelocKind::PpcAddr16Ha
|
||||||
|
| ObjRelocKind::PpcAddr16Lo => {
|
||||||
|
let arg = args.iter_mut().rfind(|a| is_rel_abs_arg(a)).ok_or_else(|| {
|
||||||
|
anyhow::Error::msg("Failed to locate rel/abs arg for reloc")
|
||||||
|
})?;
|
||||||
|
*arg = if is_offset_arg(arg) {
|
||||||
|
ObjInsArg::RelocWithBase
|
||||||
|
} else {
|
||||||
|
ObjInsArg::Reloc
|
||||||
|
};
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ops.push(simplified.ins.op as u8);
|
||||||
|
let suffix = simplified.ins.suffix();
|
||||||
|
insts.push(ObjIns {
|
||||||
|
address: simplified.ins.addr,
|
||||||
|
code: simplified.ins.code,
|
||||||
|
mnemonic: format!("{}{}", simplified.mnemonic, suffix),
|
||||||
|
args,
|
||||||
|
reloc: reloc.cloned(),
|
||||||
|
op: 0,
|
||||||
|
branch_dest: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok((ops, insts))
|
||||||
|
}
|
|
@ -1,85 +1,124 @@
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
app::{AppConfig, ViewState},
|
app::{AppConfig, DiffKind, ViewState},
|
||||||
jobs::build::queue_build,
|
jobs::{bindiff::queue_bindiff, build::queue_build},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn config_ui(ui: &mut egui::Ui, config: &Arc<RwLock<AppConfig>>, view_state: &mut ViewState) {
|
pub fn config_ui(ui: &mut egui::Ui, config: &Arc<RwLock<AppConfig>>, view_state: &mut ViewState) {
|
||||||
let mut config_guard = config.write().unwrap();
|
let mut config_guard = config.write().unwrap();
|
||||||
let AppConfig { project_dir, project_dir_change, build_asm_dir, build_src_dir, build_obj } =
|
let AppConfig {
|
||||||
&mut *config_guard;
|
project_dir,
|
||||||
|
project_dir_change,
|
||||||
|
build_asm_dir,
|
||||||
|
build_src_dir,
|
||||||
|
build_obj,
|
||||||
|
left_obj,
|
||||||
|
right_obj,
|
||||||
|
} = &mut *config_guard;
|
||||||
|
|
||||||
if ui.button("Select project dir").clicked() {
|
if view_state.diff_kind == DiffKind::SplitObj {
|
||||||
if let Some(path) = rfd::FileDialog::new().pick_folder() {
|
if ui.button("Select project dir").clicked() {
|
||||||
*project_dir = Some(path);
|
if let Some(path) = rfd::FileDialog::new().pick_folder() {
|
||||||
*project_dir_change = true;
|
*project_dir = Some(path);
|
||||||
*build_asm_dir = None;
|
*project_dir_change = true;
|
||||||
*build_src_dir = None;
|
*build_asm_dir = None;
|
||||||
*build_obj = None;
|
*build_src_dir = None;
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(dir) = project_dir {
|
|
||||||
ui.label(dir.to_string_lossy());
|
|
||||||
}
|
|
||||||
|
|
||||||
ui.separator();
|
|
||||||
|
|
||||||
if let Some(project_dir) = project_dir {
|
|
||||||
if ui.button("Select asm build dir").clicked() {
|
|
||||||
if let Some(path) = rfd::FileDialog::new().set_directory(&project_dir).pick_folder() {
|
|
||||||
*build_asm_dir = Some(path);
|
|
||||||
*build_obj = None;
|
*build_obj = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(dir) = build_asm_dir {
|
if let Some(dir) = project_dir {
|
||||||
ui.label(dir.to_string_lossy());
|
ui.label(dir.to_string_lossy());
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.separator();
|
ui.separator();
|
||||||
|
|
||||||
if ui.button("Select src build dir").clicked() {
|
if let Some(project_dir) = project_dir {
|
||||||
if let Some(path) = rfd::FileDialog::new().set_directory(&project_dir).pick_folder() {
|
if ui.button("Select asm build dir").clicked() {
|
||||||
*build_src_dir = Some(path);
|
if let Some(path) = rfd::FileDialog::new().set_directory(&project_dir).pick_folder()
|
||||||
*build_obj = None;
|
{
|
||||||
|
*build_asm_dir = Some(path);
|
||||||
|
*build_obj = None;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
if let Some(dir) = build_asm_dir {
|
||||||
if let Some(dir) = build_src_dir {
|
ui.label(dir.to_string_lossy());
|
||||||
ui.label(dir.to_string_lossy());
|
}
|
||||||
|
|
||||||
|
ui.separator();
|
||||||
|
|
||||||
|
if ui.button("Select src build dir").clicked() {
|
||||||
|
if let Some(path) = rfd::FileDialog::new().set_directory(&project_dir).pick_folder()
|
||||||
|
{
|
||||||
|
*build_src_dir = Some(path);
|
||||||
|
*build_obj = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(dir) = build_src_dir {
|
||||||
|
ui.label(dir.to_string_lossy());
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.separator();
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.separator();
|
if let Some(build_src_dir) = build_src_dir {
|
||||||
}
|
if ui.button("Select obj").clicked() {
|
||||||
|
if let Some(path) = rfd::FileDialog::new()
|
||||||
if let Some(build_src_dir) = build_src_dir {
|
.set_directory(&build_src_dir)
|
||||||
if ui.button("Select obj").clicked() {
|
.add_filter("Object file", &["o", "elf"])
|
||||||
if let Some(path) = rfd::FileDialog::new()
|
.pick_file()
|
||||||
.set_directory(&build_src_dir)
|
{
|
||||||
.add_filter("Object file", &["o"])
|
let mut new_build_obj: Option<String> = None;
|
||||||
.pick_file()
|
if let Ok(obj_path) = path.strip_prefix(&build_src_dir) {
|
||||||
{
|
|
||||||
let mut new_build_obj: Option<String> = None;
|
|
||||||
if let Ok(obj_path) = path.strip_prefix(&build_src_dir) {
|
|
||||||
new_build_obj = Some(obj_path.display().to_string());
|
|
||||||
} else if let Some(build_asm_dir) = build_asm_dir {
|
|
||||||
if let Ok(obj_path) = path.strip_prefix(&build_asm_dir) {
|
|
||||||
new_build_obj = Some(obj_path.display().to_string());
|
new_build_obj = Some(obj_path.display().to_string());
|
||||||
|
} else if let Some(build_asm_dir) = build_asm_dir {
|
||||||
|
if let Ok(obj_path) = path.strip_prefix(&build_asm_dir) {
|
||||||
|
new_build_obj = Some(obj_path.display().to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(new_build_obj) = new_build_obj {
|
||||||
|
*build_obj = Some(new_build_obj.clone());
|
||||||
|
view_state.jobs.push(queue_build(new_build_obj, config.clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(new_build_obj) = new_build_obj {
|
}
|
||||||
*build_obj = Some(new_build_obj.clone());
|
if let Some(build_obj) = build_obj {
|
||||||
view_state.jobs.push(queue_build(new_build_obj, config.clone()));
|
ui.label(&*build_obj);
|
||||||
|
if ui.button("Build").clicked() {
|
||||||
|
view_state.jobs.push(queue_build(build_obj.clone(), config.clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ui.separator();
|
||||||
}
|
}
|
||||||
if let Some(build_obj) = build_obj {
|
} else if view_state.diff_kind == DiffKind::WholeBinary {
|
||||||
ui.label(&*build_obj);
|
if ui.button("Select left obj").clicked() {
|
||||||
if ui.button("Build").clicked() {
|
if let Some(path) =
|
||||||
view_state.jobs.push(queue_build(build_obj.clone(), config.clone()));
|
rfd::FileDialog::new().add_filter("Object file", &["o", "elf"]).pick_file()
|
||||||
|
{
|
||||||
|
*left_obj = Some(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if let Some(obj) = left_obj {
|
||||||
|
ui.label(obj.to_string_lossy());
|
||||||
|
}
|
||||||
|
|
||||||
ui.separator();
|
if ui.button("Select right obj").clicked() {
|
||||||
|
if let Some(path) =
|
||||||
|
rfd::FileDialog::new().add_filter("Object file", &["o", "elf"]).pick_file()
|
||||||
|
{
|
||||||
|
*right_obj = Some(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(obj) = right_obj {
|
||||||
|
ui.label(obj.to_string_lossy());
|
||||||
|
}
|
||||||
|
|
||||||
|
if let (Some(_), Some(_)) = (left_obj, right_obj) {
|
||||||
|
if ui.button("Build").clicked() {
|
||||||
|
view_state.jobs.push(queue_bindiff(config.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.checkbox(&mut view_state.reverse_fn_order, "Reverse function order (deferred)");
|
ui.checkbox(&mut view_state.reverse_fn_order, "Reverse function order (deferred)");
|
||||||
|
|
|
@ -25,13 +25,39 @@ fn write_text(str: &str, color: Color32, job: &mut LayoutJob) {
|
||||||
|
|
||||||
fn write_reloc(reloc: &ObjReloc, job: &mut LayoutJob) {
|
fn write_reloc(reloc: &ObjReloc, job: &mut LayoutJob) {
|
||||||
let name = reloc.target.demangled_name.as_ref().unwrap_or(&reloc.target.name);
|
let name = reloc.target.demangled_name.as_ref().unwrap_or(&reloc.target.name);
|
||||||
write_text(name, Color32::LIGHT_GRAY, job);
|
|
||||||
match reloc.kind {
|
match reloc.kind {
|
||||||
ObjRelocKind::PpcAddr16Lo => write_text("@l", Color32::GRAY, job),
|
ObjRelocKind::PpcAddr16Lo => {
|
||||||
ObjRelocKind::PpcAddr16Hi => write_text("@h", Color32::GRAY, job),
|
write_text(name, Color32::LIGHT_GRAY, job);
|
||||||
ObjRelocKind::PpcAddr16Ha => write_text("@ha", Color32::GRAY, job),
|
write_text("@l", Color32::GRAY, job);
|
||||||
ObjRelocKind::PpcEmbSda21 => write_text("@sda21", Color32::GRAY, job),
|
}
|
||||||
_ => {}
|
ObjRelocKind::PpcAddr16Hi => {
|
||||||
|
write_text(name, Color32::LIGHT_GRAY, job);
|
||||||
|
write_text("@h", Color32::GRAY, job);
|
||||||
|
}
|
||||||
|
ObjRelocKind::PpcAddr16Ha => {
|
||||||
|
write_text(name, Color32::LIGHT_GRAY, job);
|
||||||
|
write_text("@ha", Color32::GRAY, job);
|
||||||
|
}
|
||||||
|
ObjRelocKind::PpcEmbSda21 => {
|
||||||
|
write_text(name, Color32::LIGHT_GRAY, job);
|
||||||
|
write_text("@sda21", Color32::GRAY, job);
|
||||||
|
}
|
||||||
|
ObjRelocKind::MipsHi16 => {
|
||||||
|
write_text("%hi(", Color32::GRAY, job);
|
||||||
|
write_text(name, Color32::LIGHT_GRAY, job);
|
||||||
|
write_text(")", Color32::GRAY, job);
|
||||||
|
}
|
||||||
|
ObjRelocKind::MipsLo16 => {
|
||||||
|
write_text("%lo(", Color32::GRAY, job);
|
||||||
|
write_text(name, Color32::LIGHT_GRAY, job);
|
||||||
|
write_text(")", Color32::GRAY, job);
|
||||||
|
}
|
||||||
|
ObjRelocKind::Absolute
|
||||||
|
| ObjRelocKind::PpcRel24
|
||||||
|
| ObjRelocKind::PpcRel14
|
||||||
|
| ObjRelocKind::Mips26 => {
|
||||||
|
write_text(name, Color32::LIGHT_GRAY, job);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,7 +77,7 @@ fn write_ins(
|
||||||
ObjInsDiffKind::Insert => Color32::GREEN,
|
ObjInsDiffKind::Insert => Color32::GREEN,
|
||||||
};
|
};
|
||||||
write_text(
|
write_text(
|
||||||
&ins.mnemonic,
|
&format!("{:<11}", ins.mnemonic),
|
||||||
match diff_kind {
|
match diff_kind {
|
||||||
ObjInsDiffKind::OpMismatch => Color32::LIGHT_BLUE,
|
ObjInsDiffKind::OpMismatch => Color32::LIGHT_BLUE,
|
||||||
_ => base_color,
|
_ => base_color,
|
||||||
|
@ -72,17 +98,13 @@ fn write_ins(
|
||||||
base_color
|
base_color
|
||||||
};
|
};
|
||||||
match arg {
|
match arg {
|
||||||
ObjInsArg::Arg(arg) => match arg {
|
ObjInsArg::PpcArg(arg) => match arg {
|
||||||
Argument::Offset(val) => {
|
Argument::Offset(val) => {
|
||||||
write_text(&format!("{}", val), color, job);
|
write_text(&format!("{}", val), color, job);
|
||||||
write_text("(", base_color, job);
|
write_text("(", base_color, job);
|
||||||
writing_offset = true;
|
writing_offset = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Argument::BranchDest(dest) => {
|
|
||||||
let addr = dest.0 + ins.ins.addr as i32 - base_addr as i32;
|
|
||||||
write_text(&format!("{:x}", addr), color, job);
|
|
||||||
}
|
|
||||||
Argument::Uimm(_) | Argument::Simm(_) => {
|
Argument::Uimm(_) | Argument::Simm(_) => {
|
||||||
write_text(&format!("{}", arg), color, job);
|
write_text(&format!("{}", arg), color, job);
|
||||||
}
|
}
|
||||||
|
@ -93,12 +115,19 @@ fn write_ins(
|
||||||
ObjInsArg::Reloc => {
|
ObjInsArg::Reloc => {
|
||||||
write_reloc(ins.reloc.as_ref().unwrap(), job);
|
write_reloc(ins.reloc.as_ref().unwrap(), job);
|
||||||
}
|
}
|
||||||
ObjInsArg::RelocOffset => {
|
ObjInsArg::RelocWithBase => {
|
||||||
write_reloc(ins.reloc.as_ref().unwrap(), job);
|
write_reloc(ins.reloc.as_ref().unwrap(), job);
|
||||||
write_text("(", base_color, job);
|
write_text("(", base_color, job);
|
||||||
writing_offset = true;
|
writing_offset = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
ObjInsArg::MipsArg(str) => {
|
||||||
|
write_text(str.strip_prefix('$').unwrap_or(str), color, job);
|
||||||
|
}
|
||||||
|
ObjInsArg::BranchOffset(offset) => {
|
||||||
|
let addr = offset + ins.address as i32 - base_addr as i32;
|
||||||
|
write_text(&format!("{:x}", addr), color, job);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if writing_offset {
|
if writing_offset {
|
||||||
write_text(")", base_color, job);
|
write_text(")", base_color, job);
|
||||||
|
@ -112,10 +141,10 @@ fn ins_hover_ui(ui: &mut egui::Ui, ins: &ObjIns) {
|
||||||
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
|
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
|
||||||
ui.style_mut().wrap = Some(false);
|
ui.style_mut().wrap = Some(false);
|
||||||
|
|
||||||
ui.label(format!("{:02X?}", ins.ins.code.to_be_bytes()));
|
ui.label(format!("{:02X?}", ins.code.to_be_bytes()));
|
||||||
|
|
||||||
for arg in &ins.args {
|
for arg in &ins.args {
|
||||||
if let ObjInsArg::Arg(arg) = arg {
|
if let ObjInsArg::PpcArg(arg) = arg {
|
||||||
match arg {
|
match arg {
|
||||||
Argument::Uimm(v) => {
|
Argument::Uimm(v) => {
|
||||||
ui.label(format!("{} == {}", v, v.0));
|
ui.label(format!("{} == {}", v, v.0));
|
||||||
|
@ -153,7 +182,7 @@ fn ins_context_menu(ui: &mut egui::Ui, ins: &ObjIns) {
|
||||||
// if ui.button("Copy hex").clicked() {}
|
// if ui.button("Copy hex").clicked() {}
|
||||||
|
|
||||||
for arg in &ins.args {
|
for arg in &ins.args {
|
||||||
if let ObjInsArg::Arg(arg) = arg {
|
if let ObjInsArg::PpcArg(arg) = arg {
|
||||||
match arg {
|
match arg {
|
||||||
Argument::Uimm(v) => {
|
Argument::Uimm(v) => {
|
||||||
if ui.button(format!("Copy \"{}\"", v)).clicked() {
|
if ui.button(format!("Copy \"{}\"", v)).clicked() {
|
||||||
|
@ -236,7 +265,7 @@ fn asm_row_ui(ui: &mut egui::Ui, ins_diff: &ObjInsDiff, symbol: &ObjSymbol) {
|
||||||
ObjInsDiffKind::Insert => Color32::GREEN,
|
ObjInsDiffKind::Insert => Color32::GREEN,
|
||||||
};
|
};
|
||||||
write_text(
|
write_text(
|
||||||
&format!("{:<6}", format!("{:x}:", ins.ins.addr - symbol.address as u32)),
|
&format!("{:<6}", format!("{:x}:", ins.address - symbol.address as u32)),
|
||||||
base_color,
|
base_color,
|
||||||
&mut job,
|
&mut job,
|
||||||
);
|
);
|
||||||
|
|
|
@ -66,21 +66,28 @@ fn symbol_ui(
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
job.append("] (", 0.0, TextFormat {
|
job.append("] ", 0.0, TextFormat {
|
||||||
font_id: font_id.clone(),
|
|
||||||
color: Color32::GRAY,
|
|
||||||
..Default::default()
|
|
||||||
});
|
|
||||||
job.append(&format!("{:.0}%", symbol.match_percent), 0.0, TextFormat {
|
|
||||||
font_id: font_id.clone(),
|
|
||||||
color: match_color_for_symbol(symbol),
|
|
||||||
..Default::default()
|
|
||||||
});
|
|
||||||
job.append(") ", 0.0, TextFormat {
|
|
||||||
font_id: font_id.clone(),
|
font_id: font_id.clone(),
|
||||||
color: Color32::GRAY,
|
color: Color32::GRAY,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
|
if symbol.match_percent > 0.0 {
|
||||||
|
job.append("(", 0.0, TextFormat {
|
||||||
|
font_id: font_id.clone(),
|
||||||
|
color: Color32::GRAY,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
job.append(&format!("{:.0}%", symbol.match_percent), 0.0, TextFormat {
|
||||||
|
font_id: font_id.clone(),
|
||||||
|
color: match_color_for_symbol(symbol),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
job.append(") ", 0.0, TextFormat {
|
||||||
|
font_id: font_id.clone(),
|
||||||
|
color: Color32::GRAY,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
}
|
||||||
job.append(name, 0.0, TextFormat { font_id, color: Color32::WHITE, ..Default::default() });
|
job.append(name, 0.0, TextFormat { font_id, color: Color32::WHITE, ..Default::default() });
|
||||||
let response = SelectableLabel::new(selected, job).ui(ui);
|
let response = SelectableLabel::new(selected, job).ui(ui);
|
||||||
if response.clicked() {
|
if response.clicked() {
|
||||||
|
|
Loading…
Reference in New Issue