mirror of
https://github.com/encounter/objdiff.git
synced 2026-07-10 12:18:36 -07:00
Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8250d26b77 | |||
| fd555a6e0f | |||
| 3710b6a91e | |||
| faebddbc5e | |||
| a733a950a3 | |||
| cad9b70632 | |||
| cf937b0be9 | |||
| 23b6d33a98 | |||
| f17ee83622 | |||
| 615ec4c50a | |||
| 2cc10b0d06 | |||
| 8091941448 | |||
| de74dfdba7 | |||
| 177bd5e895 | |||
| e1ccee1e73 | |||
| 952b6a63c3 | |||
| 09cc9952df | |||
| fc598af329 | |||
| 871407622d | |||
| e3fff7b0dc | |||
| 75b0e7d9e5 | |||
| 9f71ce9fea | |||
| d9fb48853e |
@@ -22,3 +22,4 @@ android.keystore
|
||||
*.frag
|
||||
*.vert
|
||||
*.metal
|
||||
.vscode/launch.json
|
||||
|
||||
Generated
+170
-21
@@ -110,9 +110,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.82"
|
||||
version = "1.0.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519"
|
||||
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
|
||||
|
||||
[[package]]
|
||||
name = "arboard"
|
||||
@@ -524,9 +524,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.6.0"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
|
||||
checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952"
|
||||
|
||||
[[package]]
|
||||
name = "calloop"
|
||||
@@ -970,6 +970,15 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2e06f9bce634a3c898eb1e5cb949ff63133cbb218af93cc9b38b31d6f3ea285"
|
||||
|
||||
[[package]]
|
||||
name = "cwextab"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a92e840be31d11ead5f357b8fc503133d3657845d0ccf539afcc7d1212fad226"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "d3d12"
|
||||
version = "0.19.0"
|
||||
@@ -1466,6 +1475,12 @@ dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fixedbitset"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flagset"
|
||||
version = "0.4.5"
|
||||
@@ -1922,6 +1937,12 @@ version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.3.9"
|
||||
@@ -2183,6 +2204,15 @@ dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.11"
|
||||
@@ -2274,9 +2304,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.154"
|
||||
version = "0.2.155"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346"
|
||||
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
@@ -2470,6 +2500,12 @@ dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "multimap"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03"
|
||||
|
||||
[[package]]
|
||||
name = "naga"
|
||||
version = "0.19.2"
|
||||
@@ -2789,13 +2825,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "objdiff-cli"
|
||||
version = "2.0.0-alpha.5"
|
||||
version = "2.0.0-beta.4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"argp",
|
||||
"crossterm",
|
||||
"enable-ansi-support",
|
||||
"memmap2",
|
||||
"objdiff-core",
|
||||
"pbjson",
|
||||
"pbjson-build",
|
||||
"prost",
|
||||
"prost-build",
|
||||
"ratatui",
|
||||
"rayon",
|
||||
"serde",
|
||||
@@ -2808,13 +2849,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "objdiff-core"
|
||||
version = "2.0.0-alpha.5"
|
||||
version = "2.0.0-beta.4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arm-attr",
|
||||
"byteorder",
|
||||
"cpp_demangle",
|
||||
"cwdemangle",
|
||||
"cwextab",
|
||||
"filetime",
|
||||
"flagset",
|
||||
"gimli 0.29.0",
|
||||
@@ -2838,7 +2880,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "objdiff-gui"
|
||||
version = "2.0.0-alpha.5"
|
||||
version = "2.0.0-beta.4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@@ -2846,6 +2888,7 @@ dependencies = [
|
||||
"console_error_panic_hook",
|
||||
"const_format",
|
||||
"cwdemangle",
|
||||
"cwextab",
|
||||
"dirs",
|
||||
"eframe",
|
||||
"egui",
|
||||
@@ -2861,8 +2904,10 @@ dependencies = [
|
||||
"path-slash",
|
||||
"png",
|
||||
"pollster",
|
||||
"regex",
|
||||
"reqwest",
|
||||
"rfd",
|
||||
"rlwinmdec",
|
||||
"ron",
|
||||
"self_update",
|
||||
"serde",
|
||||
@@ -2905,9 +2950,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.64"
|
||||
version = "0.10.66"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f"
|
||||
checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1"
|
||||
dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
"cfg-if",
|
||||
@@ -2937,9 +2982,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.102"
|
||||
version = "0.9.103"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2"
|
||||
checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
@@ -3047,12 +3092,44 @@ dependencies = [
|
||||
"rustc_version",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pbjson"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7e6349fa080353f4a597daffd05cb81572a9c031a6d4fff7e504947496fcc68"
|
||||
dependencies = [
|
||||
"base64 0.21.7",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pbjson-build"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6eea3058763d6e656105d1403cb04e0a41b7bbac6362d413e7c33be0c32279c9"
|
||||
dependencies = [
|
||||
"heck 0.5.0",
|
||||
"itertools 0.13.0",
|
||||
"prost",
|
||||
"prost-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||
|
||||
[[package]]
|
||||
name = "petgraph"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db"
|
||||
dependencies = [
|
||||
"fixedbitset",
|
||||
"indexmap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
version = "1.1.5"
|
||||
@@ -3165,6 +3242,16 @@ version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa"
|
||||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.2.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn 2.0.60",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-crate"
|
||||
version = "1.3.1"
|
||||
@@ -3199,6 +3286,59 @@ version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43d84d1d7a6ac92673717f9f6d1518374ef257669c24ebc5ac25d5033828be58"
|
||||
|
||||
[[package]]
|
||||
name = "prost"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e13db3d3fde688c61e2446b4d843bc27a7e8af269a69440c0308021dc92333cc"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"prost-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prost-build"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5bb182580f71dd070f88d01ce3de9f4da5021db7115d2e1c3605a754153b77c1"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"heck 0.5.0",
|
||||
"itertools 0.13.0",
|
||||
"log",
|
||||
"multimap",
|
||||
"once_cell",
|
||||
"petgraph",
|
||||
"prettyplease",
|
||||
"prost",
|
||||
"prost-types",
|
||||
"regex",
|
||||
"syn 2.0.60",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prost-derive"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "18bec9b0adc4eba778b33684b7ba3e7137789434769ee3ce3930463ef904cfca"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools 0.13.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.60",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prost-types"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cee5168b05f49d4b0ca581206eb14a7b22fafd963efe729ac48eb03266e25cc2"
|
||||
dependencies = [
|
||||
"prost",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pulldown-cmark"
|
||||
version = "0.9.6"
|
||||
@@ -3296,7 +3436,7 @@ dependencies = [
|
||||
"compact_str",
|
||||
"crossterm",
|
||||
"indoc",
|
||||
"itertools",
|
||||
"itertools 0.12.1",
|
||||
"lru",
|
||||
"paste",
|
||||
"stability",
|
||||
@@ -3377,9 +3517,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.10.4"
|
||||
version = "1.10.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
|
||||
checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
@@ -3509,6 +3649,15 @@ dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rlwinmdec"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2076dbc187938f3db71c03c85d143febf01026631189dc8ca85f8c886d90ea12"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ron"
|
||||
version = "0.8.1"
|
||||
@@ -3966,7 +4115,7 @@ version = "0.26.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"heck 0.4.1",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
@@ -4330,9 +4479,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "unarm"
|
||||
version = "1.3.0"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c6bff109f0171a299559d82a7236e056093fc0dcd2a7da86aa745f82281e2d31"
|
||||
checksum = "379762d9433a2e6e498cde97801fb238318b024a513d0843eeac98b9056b9f3c"
|
||||
|
||||
[[package]]
|
||||
name = "unicase"
|
||||
@@ -4743,9 +4892,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wgpu-hal"
|
||||
version = "0.19.4"
|
||||
version = "0.19.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc1a4924366df7ab41a5d8546d6534f1f33231aa5b3f72b9930e300f254e39c3"
|
||||
checksum = "bfabcfc55fd86611a855816326b2d54c3b2fd7972c27ce414291562650552703"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"arrayvec",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "objdiff-cli"
|
||||
version = "2.0.0-alpha.5"
|
||||
version = "2.0.0-beta.4"
|
||||
edition = "2021"
|
||||
rust-version = "1.70"
|
||||
authors = ["Luke Street <luke@street.dev>"]
|
||||
@@ -18,7 +18,10 @@ anyhow = "1.0.82"
|
||||
argp = "0.3.0"
|
||||
crossterm = "0.27.0"
|
||||
enable-ansi-support = "0.2.1"
|
||||
memmap2 = "0.9.4"
|
||||
objdiff-core = { path = "../objdiff-core", features = ["all"] }
|
||||
pbjson = "0.7.0"
|
||||
prost = "0.13.1"
|
||||
ratatui = "0.26.2"
|
||||
rayon = "1.10.0"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
@@ -27,3 +30,8 @@ supports-color = "3.0.0"
|
||||
time = { version = "0.3.36", features = ["formatting", "local-offset"] }
|
||||
tracing = "0.1.40"
|
||||
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
||||
|
||||
[build-dependencies]
|
||||
prost-build = "0.13.1"
|
||||
pbjson-build = "0.7.0"
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
fn main() {
|
||||
let output = std::process::Command::new("git")
|
||||
.args(["rev-parse", "HEAD"])
|
||||
@@ -6,4 +8,55 @@ fn main() {
|
||||
let rev = String::from_utf8(output.stdout).expect("Failed to parse git output");
|
||||
println!("cargo:rustc-env=GIT_COMMIT_SHA={rev}");
|
||||
println!("cargo:rustc-rerun-if-changed=.git/HEAD");
|
||||
|
||||
let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("protos");
|
||||
let descriptor_path = root.join("proto_descriptor.bin");
|
||||
println!("cargo:rerun-if-changed={}", descriptor_path.display());
|
||||
let descriptor_mtime = std::fs::metadata(&descriptor_path)
|
||||
.map(|m| m.modified().unwrap())
|
||||
.unwrap_or(std::time::SystemTime::UNIX_EPOCH);
|
||||
let mut run_protoc = false;
|
||||
let proto_files = vec![root.join("report.proto")];
|
||||
for proto_file in &proto_files {
|
||||
println!("cargo:rerun-if-changed={}", proto_file.display());
|
||||
let mtime = match std::fs::metadata(proto_file) {
|
||||
Ok(m) => m.modified().unwrap(),
|
||||
Err(e) => panic!("Failed to stat proto file {}: {:?}", proto_file.display(), e),
|
||||
};
|
||||
if mtime > descriptor_mtime {
|
||||
run_protoc = true;
|
||||
}
|
||||
}
|
||||
|
||||
fn prost_config(descriptor_path: &Path, run_protoc: bool) -> prost_build::Config {
|
||||
let mut config = prost_build::Config::new();
|
||||
config.file_descriptor_set_path(descriptor_path);
|
||||
// If our cached descriptor is up-to-date, we don't need to run protoc.
|
||||
// This is helpful so that users don't need to have protoc installed
|
||||
// unless they're updating the protos.
|
||||
if !run_protoc {
|
||||
config.skip_protoc_run();
|
||||
}
|
||||
config
|
||||
}
|
||||
if let Err(e) =
|
||||
prost_config(&descriptor_path, run_protoc).compile_protos(&proto_files, &[root.as_path()])
|
||||
{
|
||||
if e.kind() == std::io::ErrorKind::NotFound && e.to_string().contains("protoc") {
|
||||
eprintln!("protoc not found, skipping protobuf compilation");
|
||||
prost_config(&descriptor_path, false)
|
||||
.compile_protos(&proto_files, &[root.as_path()])
|
||||
.expect("Failed to compile protos");
|
||||
} else {
|
||||
panic!("Failed to compile protos: {e:?}");
|
||||
}
|
||||
}
|
||||
|
||||
let descriptor_set = std::fs::read(descriptor_path).expect("Failed to read descriptor set");
|
||||
pbjson_build::Builder::new()
|
||||
.register_descriptors(&descriptor_set)
|
||||
.expect("Failed to register descriptors")
|
||||
.preserve_proto_field_names()
|
||||
.build(&[".objdiff"])
|
||||
.expect("Failed to build pbjson");
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,135 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package objdiff.report;
|
||||
|
||||
// Progress info for a report or unit
|
||||
message Measures {
|
||||
// Overall match percent, including partially matched functions and data
|
||||
float fuzzy_match_percent = 1;
|
||||
// Total size of code in bytes
|
||||
uint64 total_code = 2;
|
||||
// Fully matched code size in bytes
|
||||
uint64 matched_code = 3;
|
||||
// Fully matched code percent
|
||||
float matched_code_percent = 4;
|
||||
// Total size of data in bytes
|
||||
uint64 total_data = 5;
|
||||
// Fully matched data size in bytes
|
||||
uint64 matched_data = 6;
|
||||
// Fully matched data percent
|
||||
float matched_data_percent = 7;
|
||||
// Total number of functions
|
||||
uint32 total_functions = 8;
|
||||
// Fully matched functions
|
||||
uint32 matched_functions = 9;
|
||||
// Fully matched functions percent
|
||||
float matched_functions_percent = 10;
|
||||
}
|
||||
|
||||
// Project progress report
|
||||
message Report {
|
||||
// Overall progress info
|
||||
Measures measures = 1;
|
||||
// Units within this report
|
||||
repeated ReportUnit units = 2;
|
||||
}
|
||||
|
||||
// A unit of the report (usually a translation unit)
|
||||
message ReportUnit {
|
||||
// The name of the unit
|
||||
string name = 1;
|
||||
// Progress info for this unit
|
||||
Measures measures = 2;
|
||||
// Sections within this unit
|
||||
repeated ReportItem sections = 3;
|
||||
// Functions within this unit
|
||||
repeated ReportItem functions = 4;
|
||||
// Extra metadata for this unit
|
||||
optional ReportUnitMetadata metadata = 5;
|
||||
}
|
||||
|
||||
// Extra metadata for a unit
|
||||
message ReportUnitMetadata {
|
||||
// Whether this unit is marked as complete (or "linked")
|
||||
optional bool complete = 1;
|
||||
// The name of the module this unit belongs to
|
||||
optional string module_name = 2;
|
||||
// The ID of the module this unit belongs to
|
||||
optional uint32 module_id = 3;
|
||||
// The path to the source file of this unit
|
||||
optional string source_path = 4;
|
||||
}
|
||||
|
||||
// A section or function within a unit
|
||||
message ReportItem {
|
||||
// The name of the item
|
||||
string name = 1;
|
||||
// The size of the item in bytes
|
||||
uint64 size = 2;
|
||||
// The overall match percent for this item
|
||||
float fuzzy_match_percent = 3;
|
||||
// Extra metadata for this item
|
||||
optional ReportItemMetadata metadata = 4;
|
||||
}
|
||||
|
||||
// Extra metadata for an item
|
||||
message ReportItemMetadata {
|
||||
// The demangled name of the function
|
||||
optional string demangled_name = 1;
|
||||
// The virtual address of the function or section
|
||||
optional uint64 virtual_address = 2;
|
||||
}
|
||||
|
||||
// A pair of reports to compare and generate changes
|
||||
message ChangesInput {
|
||||
// The previous report
|
||||
Report from = 1;
|
||||
// The current report
|
||||
Report to = 2;
|
||||
}
|
||||
|
||||
// Changes between two reports
|
||||
message Changes {
|
||||
// The progress info for the previous report
|
||||
Measures from = 1;
|
||||
// The progress info for the current report
|
||||
Measures to = 2;
|
||||
// Units that changed
|
||||
repeated ChangeUnit units = 3;
|
||||
}
|
||||
|
||||
// A changed unit
|
||||
message ChangeUnit {
|
||||
// The name of the unit
|
||||
string name = 1;
|
||||
// The previous progress info (omitted if new)
|
||||
optional Measures from = 2;
|
||||
// The current progress info (omitted if removed)
|
||||
optional Measures to = 3;
|
||||
// Sections that changed
|
||||
repeated ChangeItem sections = 4;
|
||||
// Functions that changed
|
||||
repeated ChangeItem functions = 5;
|
||||
// Extra metadata for this unit
|
||||
optional ReportUnitMetadata metadata = 6;
|
||||
}
|
||||
|
||||
// A changed section or function
|
||||
message ChangeItem {
|
||||
// The name of the item
|
||||
string name = 1;
|
||||
// The previous progress info (omitted if new)
|
||||
optional ChangeItemInfo from = 2;
|
||||
// The current progress info (omitted if removed)
|
||||
optional ChangeItemInfo to = 3;
|
||||
// Extra metadata for this item
|
||||
optional ReportItemMetadata metadata = 4;
|
||||
}
|
||||
|
||||
// Progress info for a section or function
|
||||
message ChangeItemInfo {
|
||||
// The overall match percent for this item
|
||||
float fuzzy_match_percent = 1;
|
||||
// The size of the item in bytes
|
||||
uint64 size = 2;
|
||||
}
|
||||
@@ -750,8 +750,11 @@ impl FunctionDiffUi {
|
||||
base_color = COLOR_ROTATION[diff.idx % COLOR_ROTATION.len()]
|
||||
}
|
||||
}
|
||||
DiffText::BranchDest(addr) => {
|
||||
DiffText::BranchDest(addr, diff) => {
|
||||
label_text = format!("{addr:x}");
|
||||
if let Some(diff) = diff {
|
||||
base_color = COLOR_ROTATION[diff.idx % COLOR_ROTATION.len()]
|
||||
}
|
||||
}
|
||||
DiffText::Symbol(sym) => {
|
||||
let name = sym.demangled_name.as_ref().unwrap_or(&sym.name);
|
||||
@@ -817,6 +820,12 @@ impl FunctionDiffUi {
|
||||
mips_abi: Default::default(), // TODO
|
||||
mips_instr_category: Default::default(), // TODO
|
||||
arm_arch_version: Default::default(), // TODO
|
||||
arm_unified_syntax: true, // TODO
|
||||
arm_av_registers: false, // TODO
|
||||
arm_r9_usage: Default::default(), // TODO
|
||||
arm_sl_usage: false, // TODO
|
||||
arm_fp_usage: false, // TODO
|
||||
arm_ip_usage: false, // TODO
|
||||
};
|
||||
let target = self
|
||||
.target_path
|
||||
|
||||
+164
-260
File diff suppressed because it is too large
Load Diff
@@ -96,7 +96,7 @@ fn main() {
|
||||
// Try to enable ANSI support on Windows.
|
||||
let _ = enable_ansi_support();
|
||||
// Disable isatty check for supports-color. (e.g. when used with ninja)
|
||||
env::set_var("IGNORE_IS_TERMINAL", "1");
|
||||
unsafe { env::set_var("IGNORE_IS_TERMINAL", "1") };
|
||||
supports_color::on(Stream::Stdout).is_some_and(|c| c.has_basic)
|
||||
};
|
||||
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
pub mod report;
|
||||
pub mod term;
|
||||
|
||||
@@ -0,0 +1,226 @@
|
||||
use anyhow::{bail, Result};
|
||||
use prost::Message;
|
||||
use serde_json::error::Category;
|
||||
|
||||
// Protobuf report types
|
||||
include!(concat!(env!("OUT_DIR"), "/objdiff.report.rs"));
|
||||
include!(concat!(env!("OUT_DIR"), "/objdiff.report.serde.rs"));
|
||||
|
||||
impl Report {
|
||||
pub fn parse(data: &[u8]) -> Result<Self> {
|
||||
if data.is_empty() {
|
||||
bail!(std::io::Error::from(std::io::ErrorKind::UnexpectedEof));
|
||||
}
|
||||
if data[0] == b'{' {
|
||||
// Load as JSON
|
||||
Self::from_json(data).map_err(anyhow::Error::new)
|
||||
} else {
|
||||
// Load as binary protobuf
|
||||
Self::decode(data).map_err(anyhow::Error::new)
|
||||
}
|
||||
}
|
||||
|
||||
fn from_json(bytes: &[u8]) -> Result<Self, serde_json::Error> {
|
||||
match serde_json::from_slice::<Self>(bytes) {
|
||||
Ok(report) => Ok(report),
|
||||
Err(e) => {
|
||||
match e.classify() {
|
||||
Category::Io | Category::Eof | Category::Syntax => Err(e),
|
||||
Category::Data => {
|
||||
// Try to load as legacy report
|
||||
match serde_json::from_slice::<LegacyReport>(bytes) {
|
||||
Ok(legacy_report) => Ok(Report::from(legacy_report)),
|
||||
Err(_) => Err(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Measures {
|
||||
/// Average the fuzzy match percentage over total code bytes.
|
||||
pub fn calc_fuzzy_match_percent(&mut self) {
|
||||
if self.total_code == 0 {
|
||||
self.fuzzy_match_percent = 100.0;
|
||||
} else {
|
||||
self.fuzzy_match_percent /= self.total_code as f32;
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate the percentage of matched code, data, and functions.
|
||||
pub fn calc_matched_percent(&mut self) {
|
||||
self.matched_code_percent = if self.total_code == 0 {
|
||||
100.0
|
||||
} else {
|
||||
self.matched_code as f32 / self.total_code as f32 * 100.0
|
||||
};
|
||||
self.matched_data_percent = if self.total_data == 0 {
|
||||
100.0
|
||||
} else {
|
||||
self.matched_data as f32 / self.total_data as f32 * 100.0
|
||||
};
|
||||
self.matched_functions_percent = if self.total_functions == 0 {
|
||||
100.0
|
||||
} else {
|
||||
self.matched_functions as f32 / self.total_functions as f32 * 100.0
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Allows [collect](Iterator::collect) to be used on an iterator of [Measures].
|
||||
impl FromIterator<Measures> for Measures {
|
||||
fn from_iter<T>(iter: T) -> Self
|
||||
where T: IntoIterator<Item = Measures> {
|
||||
let mut measures = Measures::default();
|
||||
for other in iter {
|
||||
measures.fuzzy_match_percent += other.fuzzy_match_percent * other.total_code as f32;
|
||||
measures.total_code += other.total_code;
|
||||
measures.matched_code += other.matched_code;
|
||||
measures.total_data += other.total_data;
|
||||
measures.matched_data += other.matched_data;
|
||||
measures.total_functions += other.total_functions;
|
||||
measures.matched_functions += other.matched_functions;
|
||||
}
|
||||
measures.calc_fuzzy_match_percent();
|
||||
measures.calc_matched_percent();
|
||||
measures
|
||||
}
|
||||
}
|
||||
|
||||
// Older JSON report types
|
||||
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
|
||||
struct LegacyReport {
|
||||
fuzzy_match_percent: f32,
|
||||
total_code: u64,
|
||||
matched_code: u64,
|
||||
matched_code_percent: f32,
|
||||
total_data: u64,
|
||||
matched_data: u64,
|
||||
matched_data_percent: f32,
|
||||
total_functions: u32,
|
||||
matched_functions: u32,
|
||||
matched_functions_percent: f32,
|
||||
units: Vec<LegacyReportUnit>,
|
||||
}
|
||||
|
||||
impl From<LegacyReport> for Report {
|
||||
fn from(value: LegacyReport) -> Self {
|
||||
Self {
|
||||
measures: Some(Measures {
|
||||
fuzzy_match_percent: value.fuzzy_match_percent,
|
||||
total_code: value.total_code,
|
||||
matched_code: value.matched_code,
|
||||
matched_code_percent: value.matched_code_percent,
|
||||
total_data: value.total_data,
|
||||
matched_data: value.matched_data,
|
||||
matched_data_percent: value.matched_data_percent,
|
||||
total_functions: value.total_functions,
|
||||
matched_functions: value.matched_functions,
|
||||
matched_functions_percent: value.matched_functions_percent,
|
||||
}),
|
||||
units: value.units.into_iter().map(ReportUnit::from).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
|
||||
struct LegacyReportUnit {
|
||||
name: String,
|
||||
fuzzy_match_percent: f32,
|
||||
total_code: u64,
|
||||
matched_code: u64,
|
||||
total_data: u64,
|
||||
matched_data: u64,
|
||||
total_functions: u32,
|
||||
matched_functions: u32,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
complete: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
module_name: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
module_id: Option<u32>,
|
||||
sections: Vec<LegacyReportItem>,
|
||||
functions: Vec<LegacyReportItem>,
|
||||
}
|
||||
|
||||
impl From<LegacyReportUnit> for ReportUnit {
|
||||
fn from(value: LegacyReportUnit) -> Self {
|
||||
let mut measures = Measures {
|
||||
fuzzy_match_percent: value.fuzzy_match_percent,
|
||||
total_code: value.total_code,
|
||||
matched_code: value.matched_code,
|
||||
total_data: value.total_data,
|
||||
matched_data: value.matched_data,
|
||||
total_functions: value.total_functions,
|
||||
matched_functions: value.matched_functions,
|
||||
..Default::default()
|
||||
};
|
||||
measures.calc_matched_percent();
|
||||
Self {
|
||||
name: value.name.clone(),
|
||||
measures: Some(measures),
|
||||
sections: value.sections.into_iter().map(ReportItem::from).collect(),
|
||||
functions: value.functions.into_iter().map(ReportItem::from).collect(),
|
||||
metadata: Some(ReportUnitMetadata {
|
||||
complete: value.complete,
|
||||
module_name: value.module_name.clone(),
|
||||
module_id: value.module_id,
|
||||
..Default::default()
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
|
||||
struct LegacyReportItem {
|
||||
name: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
demangled_name: Option<String>,
|
||||
#[serde(
|
||||
default,
|
||||
skip_serializing_if = "Option::is_none",
|
||||
serialize_with = "serialize_hex",
|
||||
deserialize_with = "deserialize_hex"
|
||||
)]
|
||||
address: Option<u64>,
|
||||
size: u64,
|
||||
fuzzy_match_percent: f32,
|
||||
}
|
||||
|
||||
impl From<LegacyReportItem> for ReportItem {
|
||||
fn from(value: LegacyReportItem) -> Self {
|
||||
Self {
|
||||
name: value.name,
|
||||
size: value.size,
|
||||
fuzzy_match_percent: value.fuzzy_match_percent,
|
||||
metadata: Some(ReportItemMetadata {
|
||||
demangled_name: value.demangled_name,
|
||||
virtual_address: value.address,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize_hex<S>(x: &Option<u64>, s: S) -> Result<S::Ok, S::Error>
|
||||
where S: serde::Serializer {
|
||||
if let Some(x) = x {
|
||||
s.serialize_str(&format!("{:#x}", x))
|
||||
} else {
|
||||
s.serialize_none()
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_hex<'de, D>(d: D) -> Result<Option<u64>, D::Error>
|
||||
where D: serde::Deserializer<'de> {
|
||||
use serde::Deserialize;
|
||||
let s = String::deserialize(d)?;
|
||||
if s.is_empty() {
|
||||
Ok(None)
|
||||
} else if !s.starts_with("0x") {
|
||||
Err(serde::de::Error::custom("expected hex string"))
|
||||
} else {
|
||||
u64::from_str_radix(&s[2..], 16).map(Some).map_err(serde::de::Error::custom)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "objdiff-core"
|
||||
version = "2.0.0-alpha.5"
|
||||
version = "2.0.0-beta.4"
|
||||
edition = "2021"
|
||||
rust-version = "1.70"
|
||||
authors = ["Luke Street <luke@street.dev>"]
|
||||
@@ -17,7 +17,7 @@ any-arch = [] # Implicit, used to check if any arch is enabled
|
||||
config = ["globset", "semver", "serde_json", "serde_yaml"]
|
||||
dwarf = ["gimli"]
|
||||
mips = ["any-arch", "rabbitizer"]
|
||||
ppc = ["any-arch", "cwdemangle", "ppc750cl"]
|
||||
ppc = ["any-arch", "cwdemangle", "cwextab", "ppc750cl"]
|
||||
x86 = ["any-arch", "cpp_demangle", "iced-x86", "msvc-demangler"]
|
||||
arm = ["any-arch", "cpp_demangle", "unarm", "arm-attr"]
|
||||
|
||||
@@ -45,6 +45,7 @@ gimli = { version = "0.29.0", default-features = false, features = ["read-all"],
|
||||
|
||||
# ppc
|
||||
cwdemangle = { version = "1.0.0", optional = true }
|
||||
cwextab = { version = "0.2.3", optional = true }
|
||||
ppc750cl = { git = "https://github.com/encounter/ppc750cl", rev = "6cbd7d888c7082c2c860f66cbb9848d633f753ed", optional = true }
|
||||
|
||||
# mips
|
||||
@@ -56,5 +57,5 @@ iced-x86 = { version = "1.21.0", default-features = false, features = ["std", "d
|
||||
msvc-demangler = { version = "0.10.0", optional = true }
|
||||
|
||||
# arm
|
||||
unarm = { version = "1.3.0", optional = true }
|
||||
unarm = { version = "1.4.0", optional = true }
|
||||
arm-attr = { version = "0.1.1", optional = true }
|
||||
|
||||
@@ -13,12 +13,12 @@ use object::{
|
||||
use unarm::{
|
||||
args::{Argument, OffsetImm, OffsetReg, Register},
|
||||
parse::{ArmVersion, ParseMode, Parser},
|
||||
ParsedIns,
|
||||
DisplayOptions, ParseFlags, ParsedIns, RegNames,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
arch::{ObjArch, ProcessCodeResult},
|
||||
diff::{ArmArchVersion, DiffObjConfig},
|
||||
diff::{ArmArchVersion, ArmR9Usage, DiffObjConfig},
|
||||
obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection},
|
||||
};
|
||||
|
||||
@@ -149,7 +149,24 @@ impl ObjArch for ObjArchArm {
|
||||
object::Endianness::Little => unarm::Endian::Little,
|
||||
object::Endianness::Big => unarm::Endian::Big,
|
||||
};
|
||||
let mut parser = Parser::new(version, first_mapping, start_addr, endian, code);
|
||||
|
||||
let parse_flags = ParseFlags { ual: config.arm_unified_syntax };
|
||||
|
||||
let mut parser = Parser::new(version, first_mapping, start_addr, endian, parse_flags, code);
|
||||
|
||||
let display_options = DisplayOptions {
|
||||
reg_names: RegNames {
|
||||
av_registers: config.arm_av_registers,
|
||||
r9_use: match config.arm_r9_usage {
|
||||
ArmR9Usage::GeneralPurpose => unarm::R9Use::GeneralPurpose,
|
||||
ArmR9Usage::Sb => unarm::R9Use::Pid,
|
||||
ArmR9Usage::Tr => unarm::R9Use::Tls,
|
||||
},
|
||||
explicit_stack_limit: config.arm_sl_usage,
|
||||
frame_pointer: config.arm_fp_usage,
|
||||
ip: config.arm_ip_usage,
|
||||
},
|
||||
};
|
||||
|
||||
while let Some((address, op, ins)) = parser.next() {
|
||||
if let Some(next) = next_mapping {
|
||||
@@ -187,7 +204,7 @@ impl ObjArch for ObjArchArm {
|
||||
let (args, branch_dest) = if reloc.is_some() && parser.mode == ParseMode::Data {
|
||||
(vec![ObjInsArg::Reloc], None)
|
||||
} else {
|
||||
push_args(&ins, config, reloc_arg, address)?
|
||||
push_args(&ins, config, reloc_arg, address, display_options)?
|
||||
};
|
||||
|
||||
ops.push(op.id());
|
||||
@@ -200,7 +217,7 @@ impl ObjArch for ObjArchArm {
|
||||
reloc,
|
||||
branch_dest,
|
||||
line,
|
||||
formatted: ins.to_string(),
|
||||
formatted: ins.display(display_options).to_string(),
|
||||
orig: None,
|
||||
});
|
||||
}
|
||||
@@ -210,6 +227,7 @@ impl ObjArch for ObjArchArm {
|
||||
|
||||
fn implcit_addend(
|
||||
&self,
|
||||
_file: &File<'_>,
|
||||
section: &ObjSection,
|
||||
address: u64,
|
||||
reloc: &Relocation,
|
||||
@@ -281,6 +299,7 @@ fn push_args(
|
||||
config: &DiffObjConfig,
|
||||
reloc_arg: Option<usize>,
|
||||
cur_addr: u32,
|
||||
display_options: DisplayOptions,
|
||||
) -> Result<(Vec<ObjInsArg>, Option<u64>)> {
|
||||
let mut args = vec![];
|
||||
let mut branch_dest = None;
|
||||
@@ -318,7 +337,9 @@ fn push_args(
|
||||
deref = true;
|
||||
args.push(ObjInsArg::PlainText("[".into()));
|
||||
}
|
||||
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(reg.reg.to_string().into())));
|
||||
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(
|
||||
reg.reg.display(display_options.reg_names).to_string().into(),
|
||||
)));
|
||||
if reg.writeback {
|
||||
if reg.deref {
|
||||
writeback = true;
|
||||
@@ -336,7 +357,10 @@ fn push_args(
|
||||
args.push(ObjInsArg::PlainText(config.separator().into()));
|
||||
}
|
||||
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(
|
||||
Register::parse(i).to_string().into(),
|
||||
Register::parse(i)
|
||||
.display(display_options.reg_names)
|
||||
.to_string()
|
||||
.into(),
|
||||
)));
|
||||
first = false;
|
||||
}
|
||||
@@ -376,14 +400,16 @@ fn push_args(
|
||||
Argument::ShiftReg(shift) => {
|
||||
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(shift.op.to_string().into())));
|
||||
args.push(ObjInsArg::PlainText(" ".into()));
|
||||
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(shift.reg.to_string().into())));
|
||||
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(
|
||||
shift.reg.display(display_options.reg_names).to_string().into(),
|
||||
)));
|
||||
}
|
||||
Argument::OffsetReg(offset) => {
|
||||
if !offset.add {
|
||||
args.push(ObjInsArg::PlainText("-".into()));
|
||||
}
|
||||
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(
|
||||
offset.reg.to_string().into(),
|
||||
offset.reg.display(display_options.reg_names).to_string().into(),
|
||||
)));
|
||||
}
|
||||
Argument::CpsrMode(mode) => {
|
||||
@@ -398,9 +424,9 @@ fn push_args(
|
||||
| Argument::StatusMask(_)
|
||||
| Argument::Shift(_)
|
||||
| Argument::CpsrFlags(_)
|
||||
| Argument::Endian(_) => {
|
||||
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(arg.to_string().into())))
|
||||
}
|
||||
| Argument::Endian(_) => args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(
|
||||
arg.display(display_options).to_string().into(),
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
use std::{borrow::Cow, collections::BTreeMap, sync::Mutex};
|
||||
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use object::{elf, Endian, Endianness, File, FileFlags, Object, Relocation, RelocationFlags};
|
||||
use object::{
|
||||
elf, Endian, Endianness, File, FileFlags, Object, ObjectSection, ObjectSymbol, Relocation,
|
||||
RelocationFlags, RelocationTarget,
|
||||
};
|
||||
use rabbitizer::{config, Abi, InstrCategory, Instruction, OperandType};
|
||||
|
||||
use crate::{
|
||||
@@ -22,6 +25,7 @@ pub struct ObjArchMips {
|
||||
pub endianness: Endianness,
|
||||
pub abi: Abi,
|
||||
pub instr_category: InstrCategory,
|
||||
pub ri_gp_value: i32,
|
||||
}
|
||||
|
||||
const EF_MIPS_ABI: u32 = 0x0000F000;
|
||||
@@ -30,6 +34,8 @@ const EF_MIPS_MACH: u32 = 0x00FF0000;
|
||||
const EF_MIPS_MACH_ALLEGREX: u32 = 0x00840000;
|
||||
const EF_MIPS_MACH_5900: u32 = 0x00920000;
|
||||
|
||||
const R_MIPS15_S3: u32 = 119;
|
||||
|
||||
impl ObjArchMips {
|
||||
pub fn new(object: &File) -> Result<Self> {
|
||||
let mut abi = Abi::NUMERIC;
|
||||
@@ -56,7 +62,19 @@ impl ObjArchMips {
|
||||
}
|
||||
_ => bail!("Unsupported MIPS file flags"),
|
||||
}
|
||||
Ok(Self { endianness: object.endianness(), abi, instr_category })
|
||||
|
||||
// Parse the ri_gp_value stored in .reginfo to be able to correctly
|
||||
// calculate R_MIPS_GPREL16 relocations later. The value is stored
|
||||
// 0x14 bytes into .reginfo (on 32 bit platforms)
|
||||
let ri_gp_value = object
|
||||
.section_by_name(".reginfo")
|
||||
.and_then(|section| section.data().ok())
|
||||
.and_then(|data| data.get(0x14..0x18))
|
||||
.and_then(|s| s.try_into().ok())
|
||||
.map(|bytes| object.endianness().read_i32_bytes(bytes))
|
||||
.unwrap_or(0);
|
||||
|
||||
Ok(Self { endianness: object.endianness(), abi, instr_category, ri_gp_value })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,7 +122,7 @@ impl ObjArch for ObjArchMips {
|
||||
let mnemonic = instruction.opcode_name().to_string();
|
||||
let is_branch = instruction.is_branch();
|
||||
let branch_offset = instruction.branch_offset();
|
||||
let branch_dest = if is_branch {
|
||||
let mut branch_dest = if is_branch {
|
||||
cur_addr.checked_add_signed(branch_offset).map(|a| a as u64)
|
||||
} else {
|
||||
None
|
||||
@@ -121,9 +139,7 @@ impl ObjArch for ObjArchMips {
|
||||
OperandType::cpu_immediate
|
||||
| OperandType::cpu_label
|
||||
| OperandType::cpu_branch_target_label => {
|
||||
if let Some(branch_dest) = branch_dest {
|
||||
args.push(ObjInsArg::BranchDest(branch_dest));
|
||||
} else if let Some(reloc) = reloc {
|
||||
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
|
||||
@@ -131,7 +147,10 @@ impl ObjArch for ObjArchMips {
|
||||
args.push(ObjInsArg::BranchDest(reloc.target.address));
|
||||
} else {
|
||||
push_reloc(&mut args, reloc)?;
|
||||
branch_dest = None;
|
||||
}
|
||||
} else if let Some(branch_dest) = branch_dest {
|
||||
args.push(ObjInsArg::BranchDest(branch_dest));
|
||||
} else {
|
||||
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(
|
||||
op.disassemble(&instruction, None).into(),
|
||||
@@ -152,6 +171,18 @@ impl ObjArch for ObjArchMips {
|
||||
)));
|
||||
args.push(ObjInsArg::PlainText(")".into()));
|
||||
}
|
||||
// OperandType::r5900_immediate15 => match reloc {
|
||||
// Some(reloc)
|
||||
// if reloc.flags == RelocationFlags::Elf { r_type: R_MIPS15_S3 } =>
|
||||
// {
|
||||
// push_reloc(&mut args, reloc)?;
|
||||
// }
|
||||
// _ => {
|
||||
// args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(
|
||||
// op.disassemble(&instruction, None).into(),
|
||||
// )));
|
||||
// }
|
||||
// },
|
||||
_ => {
|
||||
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(
|
||||
op.disassemble(&instruction, None).into(),
|
||||
@@ -179,6 +210,7 @@ impl ObjArch for ObjArchMips {
|
||||
|
||||
fn implcit_addend(
|
||||
&self,
|
||||
file: &File<'_>,
|
||||
section: &ObjSection,
|
||||
address: u64,
|
||||
reloc: &Relocation,
|
||||
@@ -187,14 +219,29 @@ impl ObjArch for ObjArchMips {
|
||||
let addend = self.endianness.read_u32_bytes(data);
|
||||
Ok(match reloc.flags() {
|
||||
RelocationFlags::Elf { r_type: elf::R_MIPS_32 } => addend as i64,
|
||||
RelocationFlags::Elf { r_type: elf::R_MIPS_26 } => ((addend & 0x03FFFFFF) << 2) as i64,
|
||||
RelocationFlags::Elf { r_type: elf::R_MIPS_HI16 } => {
|
||||
((addend & 0x0000FFFF) << 16) as i32 as i64
|
||||
}
|
||||
RelocationFlags::Elf {
|
||||
r_type:
|
||||
elf::R_MIPS_LO16 | elf::R_MIPS_GOT16 | elf::R_MIPS_CALL16 | elf::R_MIPS_GPREL16,
|
||||
r_type: elf::R_MIPS_LO16 | elf::R_MIPS_GOT16 | elf::R_MIPS_CALL16,
|
||||
} => (addend & 0x0000FFFF) as i16 as i64,
|
||||
RelocationFlags::Elf { r_type: elf::R_MIPS_26 } => ((addend & 0x03FFFFFF) << 2) as i64,
|
||||
RelocationFlags::Elf { r_type: elf::R_MIPS_GPREL16 | elf::R_MIPS_LITERAL } => {
|
||||
let RelocationTarget::Symbol(idx) = reloc.target() else {
|
||||
bail!("Unsupported R_MIPS_GPREL16 relocation against a non-symbol");
|
||||
};
|
||||
let sym = file.symbol_by_index(idx)?;
|
||||
|
||||
// if the symbol we are relocating against is in a local section we need to add
|
||||
// the ri_gp_value from .reginfo to the addend.
|
||||
if sym.section().index().is_some() {
|
||||
((addend & 0x0000FFFF) as i16 as i64) + self.ri_gp_value as i64
|
||||
} else {
|
||||
(addend & 0x0000FFFF) as i16 as i64
|
||||
}
|
||||
}
|
||||
RelocationFlags::Elf { r_type: elf::R_MIPS_PC16 } => 0, // PC-relative relocation
|
||||
RelocationFlags::Elf { r_type: R_MIPS15_S3 } => ((addend & 0x001FFFC0) >> 3) as i64,
|
||||
flags => bail!("Unsupported MIPS implicit relocation {flags:?}"),
|
||||
})
|
||||
}
|
||||
@@ -202,13 +249,16 @@ impl ObjArch for ObjArchMips {
|
||||
fn display_reloc(&self, flags: RelocationFlags) -> Cow<'static, str> {
|
||||
match flags {
|
||||
RelocationFlags::Elf { r_type } => match r_type {
|
||||
elf::R_MIPS_HI16 => Cow::Borrowed("R_MIPS_HI16"),
|
||||
elf::R_MIPS_LO16 => Cow::Borrowed("R_MIPS_LO16"),
|
||||
elf::R_MIPS_GOT16 => Cow::Borrowed("R_MIPS_GOT16"),
|
||||
elf::R_MIPS_CALL16 => Cow::Borrowed("R_MIPS_CALL16"),
|
||||
elf::R_MIPS_GPREL16 => Cow::Borrowed("R_MIPS_GPREL16"),
|
||||
elf::R_MIPS_32 => Cow::Borrowed("R_MIPS_32"),
|
||||
elf::R_MIPS_26 => Cow::Borrowed("R_MIPS_26"),
|
||||
elf::R_MIPS_HI16 => Cow::Borrowed("R_MIPS_HI16"),
|
||||
elf::R_MIPS_LO16 => Cow::Borrowed("R_MIPS_LO16"),
|
||||
elf::R_MIPS_GPREL16 => Cow::Borrowed("R_MIPS_GPREL16"),
|
||||
elf::R_MIPS_LITERAL => Cow::Borrowed("R_MIPS_LITERAL"),
|
||||
elf::R_MIPS_GOT16 => Cow::Borrowed("R_MIPS_GOT16"),
|
||||
elf::R_MIPS_PC16 => Cow::Borrowed("R_MIPS_PC16"),
|
||||
elf::R_MIPS_CALL16 => Cow::Borrowed("R_MIPS_CALL16"),
|
||||
R_MIPS15_S3 => Cow::Borrowed("R_MIPS15_S3"),
|
||||
_ => Cow::Owned(format!("<{flags:?}>")),
|
||||
},
|
||||
_ => Cow::Owned(format!("<{flags:?}>")),
|
||||
@@ -244,7 +294,11 @@ fn push_reloc(args: &mut Vec<ObjInsArg>, reloc: &ObjReloc) -> Result<()> {
|
||||
args.push(ObjInsArg::Reloc);
|
||||
args.push(ObjInsArg::PlainText(")".into()));
|
||||
}
|
||||
elf::R_MIPS_32 | elf::R_MIPS_26 => {
|
||||
elf::R_MIPS_32
|
||||
| elf::R_MIPS_26
|
||||
| elf::R_MIPS_LITERAL
|
||||
| elf::R_MIPS_PC16
|
||||
| R_MIPS15_S3 => {
|
||||
args.push(ObjInsArg::Reloc);
|
||||
}
|
||||
_ => bail!("Unsupported ELF MIPS relocation type {r_type}"),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::{borrow::Cow, collections::BTreeMap};
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use object::{Architecture, Object, ObjectSymbol, Relocation, RelocationFlags, Symbol};
|
||||
use object::{Architecture, File, Object, ObjectSymbol, Relocation, RelocationFlags, Symbol};
|
||||
|
||||
use crate::{
|
||||
diff::DiffObjConfig,
|
||||
@@ -28,8 +28,13 @@ pub trait ObjArch: Send + Sync {
|
||||
config: &DiffObjConfig,
|
||||
) -> Result<ProcessCodeResult>;
|
||||
|
||||
fn implcit_addend(&self, section: &ObjSection, address: u64, reloc: &Relocation)
|
||||
-> Result<i64>;
|
||||
fn implcit_addend(
|
||||
&self,
|
||||
file: &File<'_>,
|
||||
section: &ObjSection,
|
||||
address: u64,
|
||||
reloc: &Relocation,
|
||||
) -> Result<i64>;
|
||||
|
||||
fn demangle(&self, _name: &str) -> Option<String> { None }
|
||||
|
||||
|
||||
@@ -150,6 +150,7 @@ impl ObjArch for ObjArchPpc {
|
||||
|
||||
fn implcit_addend(
|
||||
&self,
|
||||
_file: &File<'_>,
|
||||
_section: &ObjSection,
|
||||
address: u64,
|
||||
reloc: &Relocation,
|
||||
|
||||
@@ -128,6 +128,7 @@ impl ObjArch for ObjArchX86 {
|
||||
|
||||
fn implcit_addend(
|
||||
&self,
|
||||
_file: &File<'_>,
|
||||
section: &ObjSection,
|
||||
address: u64,
|
||||
reloc: &Relocation,
|
||||
|
||||
@@ -11,27 +11,6 @@ use crate::{
|
||||
obj::{ObjInfo, ObjSection, SymbolRef},
|
||||
};
|
||||
|
||||
/// Compare the addresses and sizes of each symbol in the BSS sections.
|
||||
pub fn diff_bss_section(
|
||||
left: &ObjSection,
|
||||
right: &ObjSection,
|
||||
) -> Result<(ObjSectionDiff, ObjSectionDiff)> {
|
||||
let deadline = Instant::now() + Duration::from_secs(5);
|
||||
let left_sizes = left.symbols.iter().map(|s| (s.section_address, s.size)).collect::<Vec<_>>();
|
||||
let right_sizes = right.symbols.iter().map(|s| (s.section_address, s.size)).collect::<Vec<_>>();
|
||||
let ops = capture_diff_slices_deadline(
|
||||
Algorithm::Patience,
|
||||
&left_sizes,
|
||||
&right_sizes,
|
||||
Some(deadline),
|
||||
);
|
||||
let match_percent = get_diff_ratio(&ops, left_sizes.len(), right_sizes.len()) * 100.0;
|
||||
Ok((
|
||||
ObjSectionDiff { symbols: vec![], data_diff: vec![], match_percent: Some(match_percent) },
|
||||
ObjSectionDiff { symbols: vec![], data_diff: vec![], match_percent: Some(match_percent) },
|
||||
))
|
||||
}
|
||||
|
||||
pub fn diff_bss_symbol(
|
||||
left_obj: &ObjInfo,
|
||||
right_obj: &ObjInfo,
|
||||
@@ -65,10 +44,14 @@ pub fn no_diff_symbol(_obj: &ObjInfo, symbol_ref: SymbolRef) -> ObjSymbolDiff {
|
||||
pub fn diff_data_section(
|
||||
left: &ObjSection,
|
||||
right: &ObjSection,
|
||||
left_section_diff: &ObjSectionDiff,
|
||||
right_section_diff: &ObjSectionDiff,
|
||||
) -> Result<(ObjSectionDiff, ObjSectionDiff)> {
|
||||
let deadline = Instant::now() + Duration::from_secs(5);
|
||||
let left_max = left.symbols.iter().map(|s| s.section_address + s.size).max().unwrap_or(0);
|
||||
let right_max = right.symbols.iter().map(|s| s.section_address + s.size).max().unwrap_or(0);
|
||||
let left_max =
|
||||
left.symbols.iter().map(|s| s.section_address + s.size).max().unwrap_or(0).min(left.size);
|
||||
let right_max =
|
||||
right.symbols.iter().map(|s| s.section_address + s.size).max().unwrap_or(0).min(right.size);
|
||||
let left_data = &left.data[..left_max as usize];
|
||||
let right_data = &right.data[..right_max as usize];
|
||||
let ops =
|
||||
@@ -143,18 +126,18 @@ pub fn diff_data_section(
|
||||
}
|
||||
}
|
||||
|
||||
Ok((
|
||||
ObjSectionDiff {
|
||||
symbols: vec![],
|
||||
data_diff: left_diff,
|
||||
match_percent: Some(match_percent),
|
||||
},
|
||||
ObjSectionDiff {
|
||||
symbols: vec![],
|
||||
data_diff: right_diff,
|
||||
match_percent: Some(match_percent),
|
||||
},
|
||||
))
|
||||
let (mut left_section_diff, mut right_section_diff) =
|
||||
diff_generic_section(left, right, left_section_diff, right_section_diff)?;
|
||||
left_section_diff.data_diff = left_diff;
|
||||
right_section_diff.data_diff = right_diff;
|
||||
// Use the highest match percent between two options:
|
||||
// - Left symbols matching right symbols by name
|
||||
// - Diff of the data itself
|
||||
if left_section_diff.match_percent.unwrap_or(-1.0) < match_percent {
|
||||
left_section_diff.match_percent = Some(match_percent);
|
||||
right_section_diff.match_percent = Some(match_percent);
|
||||
}
|
||||
Ok((left_section_diff, right_section_diff))
|
||||
}
|
||||
|
||||
pub fn diff_data_symbol(
|
||||
@@ -195,21 +178,56 @@ pub fn diff_data_symbol(
|
||||
))
|
||||
}
|
||||
|
||||
/// Compare the text sections of two object files.
|
||||
/// This essentially adds up the match percentage of each symbol in the text section.
|
||||
pub fn diff_text_section(
|
||||
/// Compares a section of two object files.
|
||||
/// This essentially adds up the match percentage of each symbol in the section.
|
||||
pub fn diff_generic_section(
|
||||
left: &ObjSection,
|
||||
_right: &ObjSection,
|
||||
left_diff: &ObjSectionDiff,
|
||||
_right_diff: &ObjSectionDiff,
|
||||
) -> Result<(ObjSectionDiff, ObjSectionDiff)> {
|
||||
let match_percent = left
|
||||
.symbols
|
||||
.iter()
|
||||
.zip(left_diff.symbols.iter())
|
||||
.map(|(s, d)| d.match_percent.unwrap_or(0.0) * s.size as f32)
|
||||
.sum::<f32>()
|
||||
/ left.size as f32;
|
||||
let match_percent = if left_diff.symbols.iter().all(|d| d.match_percent == Some(100.0)) {
|
||||
100.0 // Avoid fp precision issues
|
||||
} else {
|
||||
left.symbols
|
||||
.iter()
|
||||
.zip(left_diff.symbols.iter())
|
||||
.map(|(s, d)| d.match_percent.unwrap_or(0.0) * s.size as f32)
|
||||
.sum::<f32>()
|
||||
/ left.size as f32
|
||||
};
|
||||
Ok((
|
||||
ObjSectionDiff { symbols: vec![], data_diff: vec![], match_percent: Some(match_percent) },
|
||||
ObjSectionDiff { symbols: vec![], data_diff: vec![], match_percent: Some(match_percent) },
|
||||
))
|
||||
}
|
||||
|
||||
/// Compare the addresses and sizes of each symbol in the BSS sections.
|
||||
pub fn diff_bss_section(
|
||||
left: &ObjSection,
|
||||
right: &ObjSection,
|
||||
left_diff: &ObjSectionDiff,
|
||||
right_diff: &ObjSectionDiff,
|
||||
) -> Result<(ObjSectionDiff, ObjSectionDiff)> {
|
||||
let deadline = Instant::now() + Duration::from_secs(5);
|
||||
let left_sizes = left.symbols.iter().map(|s| (s.section_address, s.size)).collect::<Vec<_>>();
|
||||
let right_sizes = right.symbols.iter().map(|s| (s.section_address, s.size)).collect::<Vec<_>>();
|
||||
let ops = capture_diff_slices_deadline(
|
||||
Algorithm::Patience,
|
||||
&left_sizes,
|
||||
&right_sizes,
|
||||
Some(deadline),
|
||||
);
|
||||
let mut match_percent = get_diff_ratio(&ops, left_sizes.len(), right_sizes.len()) * 100.0;
|
||||
|
||||
// Use the highest match percent between two options:
|
||||
// - Left symbols matching right symbols by name
|
||||
// - Diff of the addresses and sizes of each symbol
|
||||
let (generic_diff, _) = diff_generic_section(left, right, left_diff, right_diff)?;
|
||||
if generic_diff.match_percent.unwrap_or(-1.0) > match_percent {
|
||||
match_percent = generic_diff.match_percent.unwrap();
|
||||
}
|
||||
|
||||
Ok((
|
||||
ObjSectionDiff { symbols: vec![], data_diff: vec![], match_percent: Some(match_percent) },
|
||||
ObjSectionDiff { symbols: vec![], data_diff: vec![], match_percent: Some(match_percent) },
|
||||
|
||||
@@ -20,7 +20,7 @@ pub enum DiffText<'a> {
|
||||
/// Instruction argument
|
||||
Argument(&'a ObjInsArgValue, Option<&'a ObjInsArgDiff>),
|
||||
/// Branch destination
|
||||
BranchDest(u64),
|
||||
BranchDest(u64, Option<&'a ObjInsArgDiff>),
|
||||
/// Symbol name
|
||||
Symbol(&'a ObjSymbol),
|
||||
/// Number of spaces
|
||||
@@ -62,12 +62,12 @@ pub fn display_diff<E>(
|
||||
if i == 0 {
|
||||
cb(DiffText::Spacing(1))?;
|
||||
}
|
||||
let diff = ins_diff.arg_diff.get(i).and_then(|o| o.as_ref());
|
||||
match arg {
|
||||
ObjInsArg::PlainText(s) => {
|
||||
cb(DiffText::Basic(s))?;
|
||||
}
|
||||
ObjInsArg::Arg(v) => {
|
||||
let diff = ins_diff.arg_diff.get(i).and_then(|o| o.as_ref());
|
||||
cb(DiffText::Argument(v, diff))?;
|
||||
}
|
||||
ObjInsArg::Reloc => {
|
||||
@@ -75,7 +75,7 @@ pub fn display_diff<E>(
|
||||
}
|
||||
ObjInsArg::BranchDest(dest) => {
|
||||
if let Some(dest) = dest.checked_sub(base_addr) {
|
||||
cb(DiffText::BranchDest(dest))?;
|
||||
cb(DiffText::BranchDest(dest, diff))?;
|
||||
} else {
|
||||
cb(DiffText::Basic("<unknown>"))?;
|
||||
}
|
||||
@@ -107,7 +107,9 @@ impl PartialEq<DiffText<'_>> for HighlightKind {
|
||||
(HighlightKind::Opcode(a), DiffText::Opcode(_, b)) => a == b,
|
||||
(HighlightKind::Arg(a), DiffText::Argument(b, _)) => a.loose_eq(b),
|
||||
(HighlightKind::Symbol(a), DiffText::Symbol(b)) => a == &b.name,
|
||||
(HighlightKind::Address(a), DiffText::Address(b) | DiffText::BranchDest(b)) => a == b,
|
||||
(HighlightKind::Address(a), DiffText::Address(b) | DiffText::BranchDest(b, _)) => {
|
||||
a == b
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@@ -123,7 +125,7 @@ impl From<DiffText<'_>> for HighlightKind {
|
||||
DiffText::Opcode(_, op) => HighlightKind::Opcode(op),
|
||||
DiffText::Argument(arg, _) => HighlightKind::Arg(arg.clone()),
|
||||
DiffText::Symbol(sym) => HighlightKind::Symbol(sym.name.to_string()),
|
||||
DiffText::Address(addr) | DiffText::BranchDest(addr) => HighlightKind::Address(addr),
|
||||
DiffText::Address(addr) | DiffText::BranchDest(addr, _) => HighlightKind::Address(addr),
|
||||
_ => HighlightKind::None,
|
||||
}
|
||||
}
|
||||
|
||||
+105
-12
@@ -7,7 +7,7 @@ use crate::{
|
||||
code::{diff_code, no_diff_code, process_code_symbol},
|
||||
data::{
|
||||
diff_bss_section, diff_bss_symbol, diff_data_section, diff_data_symbol,
|
||||
diff_text_section, no_diff_symbol,
|
||||
diff_generic_section, no_diff_symbol,
|
||||
},
|
||||
},
|
||||
obj::{ObjInfo, ObjIns, ObjSection, ObjSectionKind, ObjSymbol, SymbolRef},
|
||||
@@ -117,6 +117,34 @@ pub enum ArmArchVersion {
|
||||
V6K,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Debug,
|
||||
Copy,
|
||||
Clone,
|
||||
Default,
|
||||
Eq,
|
||||
PartialEq,
|
||||
serde::Deserialize,
|
||||
serde::Serialize,
|
||||
strum::VariantArray,
|
||||
strum::EnumMessage,
|
||||
)]
|
||||
pub enum ArmR9Usage {
|
||||
#[default]
|
||||
#[strum(
|
||||
message = "R9 or V6 (default)",
|
||||
detailed_message = "Use R9 as a general-purpose register."
|
||||
)]
|
||||
GeneralPurpose,
|
||||
#[strum(
|
||||
message = "SB (static base)",
|
||||
detailed_message = "Used for position-independent data (PID)."
|
||||
)]
|
||||
Sb,
|
||||
#[strum(message = "TR (TLS register)", detailed_message = "Used for thread-local storage.")]
|
||||
Tr,
|
||||
}
|
||||
|
||||
#[inline]
|
||||
const fn default_true() -> bool { true }
|
||||
|
||||
@@ -134,6 +162,12 @@ pub struct DiffObjConfig {
|
||||
pub mips_instr_category: MipsInstrCategory,
|
||||
// ARM
|
||||
pub arm_arch_version: ArmArchVersion,
|
||||
pub arm_unified_syntax: bool,
|
||||
pub arm_av_registers: bool,
|
||||
pub arm_r9_usage: ArmR9Usage,
|
||||
pub arm_sl_usage: bool,
|
||||
pub arm_fp_usage: bool,
|
||||
pub arm_ip_usage: bool,
|
||||
}
|
||||
|
||||
impl Default for DiffObjConfig {
|
||||
@@ -146,6 +180,12 @@ impl Default for DiffObjConfig {
|
||||
mips_abi: Default::default(),
|
||||
mips_instr_category: Default::default(),
|
||||
arm_arch_version: Default::default(),
|
||||
arm_unified_syntax: true,
|
||||
arm_av_registers: false,
|
||||
arm_r9_usage: Default::default(),
|
||||
arm_sl_usage: false,
|
||||
arm_fp_usage: false,
|
||||
arm_ip_usage: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -446,7 +486,7 @@ pub fn diff_objs(
|
||||
ObjSectionKind::Code => {
|
||||
let left_section_diff = left_out.section_diff(left_section_idx);
|
||||
let right_section_diff = right_out.section_diff(right_section_idx);
|
||||
let (left_diff, right_diff) = diff_text_section(
|
||||
let (left_diff, right_diff) = diff_generic_section(
|
||||
left_section,
|
||||
right_section,
|
||||
left_section_diff,
|
||||
@@ -456,12 +496,26 @@ pub fn diff_objs(
|
||||
right_out.section_diff_mut(right_section_idx).merge(right_diff);
|
||||
}
|
||||
ObjSectionKind::Data => {
|
||||
let (left_diff, right_diff) = diff_data_section(left_section, right_section)?;
|
||||
let left_section_diff = left_out.section_diff(left_section_idx);
|
||||
let right_section_diff = right_out.section_diff(right_section_idx);
|
||||
let (left_diff, right_diff) = diff_data_section(
|
||||
left_section,
|
||||
right_section,
|
||||
left_section_diff,
|
||||
right_section_diff,
|
||||
)?;
|
||||
left_out.section_diff_mut(left_section_idx).merge(left_diff);
|
||||
right_out.section_diff_mut(right_section_idx).merge(right_diff);
|
||||
}
|
||||
ObjSectionKind::Bss => {
|
||||
let (left_diff, right_diff) = diff_bss_section(left_section, right_section)?;
|
||||
let left_section_diff = left_out.section_diff(left_section_idx);
|
||||
let right_section_diff = right_out.section_diff(right_section_idx);
|
||||
let (left_diff, right_diff) = diff_bss_section(
|
||||
left_section,
|
||||
right_section,
|
||||
left_section_diff,
|
||||
right_section_diff,
|
||||
)?;
|
||||
left_out.section_diff_mut(left_section_idx).merge(left_diff);
|
||||
right_out.section_diff_mut(right_section_idx).merge(right_diff);
|
||||
}
|
||||
@@ -504,8 +558,8 @@ fn matching_symbols(
|
||||
for (symbol_idx, symbol) in section.symbols.iter().enumerate() {
|
||||
let symbol_match = SymbolMatch {
|
||||
left: Some(SymbolRef { section_idx, symbol_idx }),
|
||||
right: find_symbol(right, symbol, section),
|
||||
prev: find_symbol(prev, symbol, section),
|
||||
right: find_symbol(right, symbol, section, Some(&right_used)),
|
||||
prev: find_symbol(prev, symbol, section, None),
|
||||
section_kind: section.kind,
|
||||
};
|
||||
matches.push(symbol_match);
|
||||
@@ -537,7 +591,7 @@ fn matching_symbols(
|
||||
matches.push(SymbolMatch {
|
||||
left: None,
|
||||
right: Some(symbol_ref),
|
||||
prev: find_symbol(prev, symbol, section),
|
||||
prev: find_symbol(prev, symbol, section, None),
|
||||
section_kind: section.kind,
|
||||
});
|
||||
}
|
||||
@@ -558,10 +612,25 @@ fn matching_symbols(
|
||||
Ok(matches)
|
||||
}
|
||||
|
||||
fn unmatched_symbols<'section, 'used>(
|
||||
section: &'section ObjSection,
|
||||
section_idx: usize,
|
||||
used: Option<&'used HashSet<SymbolRef>>,
|
||||
) -> impl Iterator<Item = (usize, &'section ObjSymbol)> + 'used
|
||||
where
|
||||
'section: 'used,
|
||||
{
|
||||
section.symbols.iter().enumerate().filter(move |&(symbol_idx, _)| {
|
||||
// Skip symbols that have already been matched
|
||||
!used.map(|u| u.contains(&SymbolRef { section_idx, symbol_idx })).unwrap_or(false)
|
||||
})
|
||||
}
|
||||
|
||||
fn find_symbol(
|
||||
obj: Option<&ObjInfo>,
|
||||
in_symbol: &ObjSymbol,
|
||||
in_section: &ObjSection,
|
||||
used: Option<&HashSet<SymbolRef>>,
|
||||
) -> Option<SymbolRef> {
|
||||
let obj = obj?;
|
||||
// Try to find an exact name match
|
||||
@@ -569,8 +638,8 @@ fn find_symbol(
|
||||
if section.kind != in_section.kind {
|
||||
continue;
|
||||
}
|
||||
if let Some(symbol_idx) =
|
||||
section.symbols.iter().position(|symbol| symbol.name == in_symbol.name)
|
||||
if let Some((symbol_idx, _)) = unmatched_symbols(section, section_idx, used)
|
||||
.find(|(_, symbol)| symbol.name == in_symbol.name)
|
||||
{
|
||||
return Some(SymbolRef { section_idx, symbol_idx });
|
||||
}
|
||||
@@ -583,9 +652,33 @@ fn find_symbol(
|
||||
if let Some((section_idx, section)) =
|
||||
obj.sections.iter().enumerate().find(|(_, s)| s.name == in_section.name)
|
||||
{
|
||||
if let Some(symbol_idx) = section.symbols.iter().position(|symbol| {
|
||||
symbol.address == in_symbol.address && symbol.name.starts_with('@')
|
||||
}) {
|
||||
if let Some((symbol_idx, _)) =
|
||||
unmatched_symbols(section, section_idx, used).find(|(_, symbol)| {
|
||||
symbol.address == in_symbol.address && symbol.name.starts_with('@')
|
||||
})
|
||||
{
|
||||
return Some(SymbolRef { section_idx, symbol_idx });
|
||||
}
|
||||
}
|
||||
}
|
||||
// Match Metrowerks symbol$1234 against symbol$2345
|
||||
if let Some((prefix, suffix)) = in_symbol.name.split_once('$') {
|
||||
if !suffix.chars().all(char::is_numeric) {
|
||||
return None;
|
||||
}
|
||||
for (section_idx, section) in obj.sections.iter().enumerate() {
|
||||
if section.kind != in_section.kind {
|
||||
continue;
|
||||
}
|
||||
if let Some((symbol_idx, _)) =
|
||||
unmatched_symbols(section, section_idx, used).find(|&(_, symbol)| {
|
||||
if let Some((p, s)) = symbol.name.split_once('$') {
|
||||
prefix == p && s.chars().all(char::is_numeric)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
{
|
||||
return Some(SymbolRef { section_idx, symbol_idx });
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user