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",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ansi_term"
|
||||
version = "0.12.1"
|
||||
|
@ -134,6 +143,17 @@ version = "0.1.8"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
|
@ -146,6 +166,29 @@ version = "0.13.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
|
@ -225,6 +268,15 @@ version = "1.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
|
||||
|
||||
[[package]]
|
||||
name = "cexpr"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
|
||||
dependencies = [
|
||||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-expr"
|
||||
version = "0.10.3"
|
||||
|
@ -249,6 +301,41 @@ dependencies = [
|
|||
"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]]
|
||||
name = "clipboard-win"
|
||||
version = "4.4.2"
|
||||
|
@ -433,7 +520,8 @@ checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35"
|
|||
|
||||
[[package]]
|
||||
name = "cwdemangle"
|
||||
version = "0.1.0"
|
||||
version = "0.1.2"
|
||||
source = "git+https://github.com/encounter/cwdemangle?rev=ba448f403320f32b808e0dcf3040c6424664acab#ba448f403320f32b808e0dcf3040c6424664acab"
|
||||
dependencies = [
|
||||
"argh",
|
||||
]
|
||||
|
@ -617,6 +705,12 @@ dependencies = [
|
|||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
|
||||
|
||||
[[package]]
|
||||
name = "emath"
|
||||
version = "0.19.0"
|
||||
|
@ -627,6 +721,19 @@ dependencies = [
|
|||
"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]]
|
||||
name = "epaint"
|
||||
version = "0.19.0"
|
||||
|
@ -867,6 +974,12 @@ dependencies = [
|
|||
"system-deps",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
|
||||
|
||||
[[package]]
|
||||
name = "glow"
|
||||
version = "0.11.2"
|
||||
|
@ -973,6 +1086,12 @@ dependencies = [
|
|||
"system-deps",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.3.3"
|
||||
|
@ -988,6 +1107,21 @@ version = "0.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "ident_case"
|
||||
version = "1.0.1"
|
||||
|
@ -1005,6 +1139,16 @@ dependencies = [
|
|||
"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]]
|
||||
name = "inotify"
|
||||
version = "0.9.6"
|
||||
|
@ -1098,6 +1242,12 @@ version = "1.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "lazycell"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.132"
|
||||
|
@ -1427,6 +1577,7 @@ dependencies = [
|
|||
"notify",
|
||||
"object",
|
||||
"ppc750cl",
|
||||
"rabbitizer",
|
||||
"rfd",
|
||||
"serde",
|
||||
"thiserror",
|
||||
|
@ -1450,6 +1601,12 @@ version = "1.14.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f7254b99e31cad77da24b08ebf628882739a608578bb1bcdfc1f9c21260d7c0"
|
||||
|
||||
[[package]]
|
||||
name = "os_str_bytes"
|
||||
version = "6.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff"
|
||||
|
||||
[[package]]
|
||||
name = "osmesa-sys"
|
||||
version = "0.1.2"
|
||||
|
@ -1503,6 +1660,12 @@ dependencies = [
|
|||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "peeking_take_while"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.1.0"
|
||||
|
@ -1571,6 +1734,17 @@ dependencies = [
|
|||
"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]]
|
||||
name = "raw-window-handle"
|
||||
version = "0.4.3"
|
||||
|
@ -1609,6 +1783,23 @@ dependencies = [
|
|||
"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]]
|
||||
name = "rfd"
|
||||
version = "0.10.0"
|
||||
|
@ -1644,6 +1835,12 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||
|
||||
[[package]]
|
||||
name = "safe_arch"
|
||||
version = "0.5.2"
|
||||
|
@ -1746,6 +1943,12 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3"
|
||||
|
||||
[[package]]
|
||||
name = "slotmap"
|
||||
version = "1.0.6"
|
||||
|
@ -1826,6 +2029,21 @@ dependencies = [
|
|||
"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]]
|
||||
name = "thiserror"
|
||||
version = "1.0.33"
|
||||
|
@ -2232,6 +2450,17 @@ dependencies = [
|
|||
"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]]
|
||||
name = "widestring"
|
||||
version = "0.5.1"
|
||||
|
|
|
@ -20,11 +20,12 @@ thiserror = "1.0.33"
|
|||
flagset = "0.4.3"
|
||||
object = "0.29.0"
|
||||
notify = "5.0.0"
|
||||
cwdemangle = "0.1.1"
|
||||
cwdemangle = { git = "https://github.com/encounter/cwdemangle", rev = "ba448f403320f32b808e0dcf3040c6424664acab" }
|
||||
log = "0.4.17"
|
||||
rfd = { version = "0.10.0" } # , default-features = false, features = ['xdg-portal']
|
||||
egui_extras = "0.19.0"
|
||||
ppc750cl = { git = "https://github.com/terorie/ppc750cl" }
|
||||
rabbitizer = { git = "https://github.com/encounter/rabbitizer-rs", rev = "10c279b2ef251c62885b1dcdcfe740b0db8e9956" }
|
||||
|
||||
# native:
|
||||
[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 egui::Widget;
|
||||
use notify::{RecursiveMode, Watcher};
|
||||
|
||||
use crate::{
|
||||
jobs::{
|
||||
build::{queue_build, BuildResult},
|
||||
build::{queue_build, BuildResult, BuildStatus},
|
||||
Job, JobResult, JobState,
|
||||
},
|
||||
views::{
|
||||
|
@ -30,6 +31,13 @@ pub enum View {
|
|||
FunctionDiff,
|
||||
}
|
||||
|
||||
#[derive(Default, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
|
||||
pub enum DiffKind {
|
||||
#[default]
|
||||
SplitObj,
|
||||
WholeBinary,
|
||||
}
|
||||
|
||||
#[derive(Default, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(default)]
|
||||
pub struct ViewState {
|
||||
|
@ -43,17 +51,24 @@ pub struct ViewState {
|
|||
pub selected_symbol: Option<String>,
|
||||
#[serde(skip)]
|
||||
pub current_view: View,
|
||||
#[serde(skip)]
|
||||
pub show_config: bool,
|
||||
// Config
|
||||
pub diff_kind: DiffKind,
|
||||
pub reverse_fn_order: bool,
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(default)]
|
||||
pub struct AppConfig {
|
||||
// Split obj
|
||||
pub project_dir: Option<PathBuf>,
|
||||
pub build_asm_dir: Option<PathBuf>,
|
||||
pub build_src_dir: Option<PathBuf>,
|
||||
pub build_obj: Option<String>,
|
||||
// Whole binary
|
||||
pub left_obj: Option<PathBuf>,
|
||||
pub right_obj: Option<PathBuf>,
|
||||
#[serde(skip)]
|
||||
pub project_dir_change: bool,
|
||||
}
|
||||
|
@ -118,6 +133,9 @@ impl eframe::App for App {
|
|||
if ui.button("Quit").clicked() {
|
||||
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 let Some(handle) = &job.handle {
|
||||
return !handle.is_finished();
|
||||
|
@ -156,7 +202,6 @@ impl eframe::App for App {
|
|||
}) {
|
||||
ctx.request_repaint();
|
||||
} else {
|
||||
ctx.request_repaint();
|
||||
ctx.request_repaint_after(Duration::from_millis(100));
|
||||
}
|
||||
}
|
||||
|
@ -187,6 +232,20 @@ impl eframe::App for App {
|
|||
JobResult::Build(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) => {
|
||||
|
|
172
src/diff.rs
172
src/diff.rs
|
@ -1,87 +1,18 @@
|
|||
use std::collections::BTreeMap;
|
||||
|
||||
use anyhow::Result;
|
||||
use ppc750cl::{disasm_iter, Argument};
|
||||
|
||||
use crate::{
|
||||
editops::{editops_find, LevEditType},
|
||||
obj::{
|
||||
ObjInfo, ObjIns, ObjInsArg, ObjInsArgDiff, ObjInsBranchFrom, ObjInsBranchTo, ObjInsDiff,
|
||||
ObjInsDiffKind, ObjReloc, ObjRelocKind, ObjSection, ObjSectionKind, ObjSymbol,
|
||||
ObjSymbolFlags,
|
||||
mips, ppc, ObjArchitecture, ObjInfo, ObjInsArg, ObjInsArgDiff, ObjInsBranchFrom,
|
||||
ObjInsBranchTo, ObjInsDiff, ObjInsDiffKind, ObjReloc, ObjSection, ObjSectionKind,
|
||||
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(
|
||||
arch: ObjArchitecture,
|
||||
left_data: &[u8],
|
||||
right_data: &[u8],
|
||||
left_symbol: &mut ObjSymbol,
|
||||
|
@ -89,12 +20,30 @@ pub fn diff_code(
|
|||
left_relocs: &[ObjReloc],
|
||||
right_relocs: &[ObjReloc],
|
||||
) -> Result<()> {
|
||||
let left_code =
|
||||
&left_data[left_symbol.address as usize..(left_symbol.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.address as usize..(right_symbol.address + right_symbol.size) as usize];
|
||||
let (right_ops, right_insts) = process_code(right_code, right_symbol.address, right_relocs)?;
|
||||
let left_code = &left_data[left_symbol.section_address as usize
|
||||
..(left_symbol.section_address + left_symbol.size) as usize];
|
||||
let right_code = &right_data[right_symbol.section_address as usize
|
||||
..(right_symbol.section_address + right_symbol.size) as usize];
|
||||
let ((left_ops, left_insts), (right_ops, right_insts)) = match arch {
|
||||
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 right_diff = Vec::<ObjInsDiff>::new();
|
||||
|
@ -111,7 +60,7 @@ pub fn diff_code(
|
|||
let left_addr = op.first_start as u32 * 4;
|
||||
let right_addr = op.second_start as u32 * 4;
|
||||
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() });
|
||||
right_diff
|
||||
.push(ObjInsDiff { ins: Some(right.clone()), ..ObjInsDiff::default() });
|
||||
|
@ -122,10 +71,10 @@ pub fn diff_code(
|
|||
cur_right = right_iter.next();
|
||||
}
|
||||
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)"));
|
||||
}
|
||||
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)"));
|
||||
}
|
||||
match op.op_type {
|
||||
|
@ -178,7 +127,11 @@ pub fn diff_code(
|
|||
}
|
||||
|
||||
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;
|
||||
right_symbol.match_percent = percent;
|
||||
|
||||
|
@ -194,17 +147,22 @@ fn resolve_branches(vec: &mut [ObjInsDiff]) {
|
|||
let mut addr_map = BTreeMap::<u32, usize>::new();
|
||||
for (i, ins_diff) in vec.iter().enumerate() {
|
||||
if let Some(ins) = &ins_diff.ins {
|
||||
addr_map.insert(ins.ins.addr, i);
|
||||
addr_map.insert(ins.address, i);
|
||||
}
|
||||
}
|
||||
// Generate branches
|
||||
let mut branches = BTreeMap::<usize, ObjInsBranchFrom>::new();
|
||||
for (i, ins_diff) in vec.iter_mut().enumerate() {
|
||||
if let Some(ins) = &ins_diff.ins {
|
||||
if ins.ins.is_blr() || ins.reloc.is_some() {
|
||||
continue;
|
||||
}
|
||||
if let Some(ins_idx) = ins.ins.branch_dest().and_then(|dest| addr_map.get(&dest)) {
|
||||
// if ins.ins.is_blr() || ins.reloc.is_some() {
|
||||
// continue;
|
||||
// }
|
||||
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) {
|
||||
ins_diff.branch_to =
|
||||
Some(ObjInsBranchTo { ins_idx: *ins_idx, branch_idx: branch.branch_idx });
|
||||
|
@ -253,15 +211,8 @@ fn arg_eq(
|
|||
right_diff: &ObjInsDiff,
|
||||
) -> bool {
|
||||
return match left {
|
||||
ObjInsArg::Arg(l) => match right {
|
||||
ObjInsArg::Arg(r) => match 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),
|
||||
},
|
||||
ObjInsArg::PpcArg(l) => match right {
|
||||
ObjInsArg::PpcArg(r) => format!("{}", l) == format!("{}", r),
|
||||
_ => false,
|
||||
},
|
||||
ObjInsArg::Reloc => {
|
||||
|
@ -271,13 +222,21 @@ fn arg_eq(
|
|||
right_diff.ins.as_ref().and_then(|i| i.reloc.as_ref()),
|
||||
)
|
||||
}
|
||||
ObjInsArg::RelocOffset => {
|
||||
matches!(right, ObjInsArg::RelocOffset)
|
||||
ObjInsArg::RelocWithBase => {
|
||||
matches!(right, ObjInsArg::RelocWithBase)
|
||||
&& reloc_eq(
|
||||
left_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> {
|
||||
let mut result = InsDiffResult::default();
|
||||
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
|
||||
result.kind = ObjInsDiffKind::Replace;
|
||||
state.diff_count += 1;
|
||||
|
@ -324,8 +283,10 @@ fn compare_ins(
|
|||
state.diff_count += 1;
|
||||
}
|
||||
let a_str = match a {
|
||||
ObjInsArg::Arg(arg) => format!("{}", arg),
|
||||
ObjInsArg::Reloc | ObjInsArg::RelocOffset => String::new(),
|
||||
ObjInsArg::PpcArg(arg) => format!("{}", arg),
|
||||
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) {
|
||||
ObjInsArgDiff { idx: *idx }
|
||||
|
@ -336,8 +297,10 @@ fn compare_ins(
|
|||
ObjInsArgDiff { idx }
|
||||
};
|
||||
let b_str = match b {
|
||||
ObjInsArg::Arg(arg) => format!("{}", arg),
|
||||
ObjInsArg::Reloc | ObjInsArg::RelocOffset => String::new(),
|
||||
ObjInsArg::PpcArg(arg) => format!("{}", arg),
|
||||
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) {
|
||||
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());
|
||||
if left_section.kind == ObjSectionKind::Code {
|
||||
diff_code(
|
||||
left.architecture,
|
||||
&left_section.data,
|
||||
&right_section.data,
|
||||
left_symbol,
|
||||
|
|
168
src/elf.rs
168
src/elf.rs
|
@ -4,13 +4,13 @@ use anyhow::{Context, Result};
|
|||
use cwdemangle::demangle;
|
||||
use flagset::Flags;
|
||||
use object::{
|
||||
Object, ObjectSection, ObjectSymbol, RelocationKind, RelocationTarget, SectionKind, SymbolKind,
|
||||
SymbolSection,
|
||||
Architecture, File, Object, ObjectSection, ObjectSymbol, RelocationKind, RelocationTarget,
|
||||
SectionKind, Symbol, SymbolKind, SymbolSection,
|
||||
};
|
||||
|
||||
use crate::obj::{
|
||||
ObjInfo, ObjReloc, ObjRelocKind, ObjSection, ObjSectionKind, ObjSymbol, ObjSymbolFlagSet,
|
||||
ObjSymbolFlags,
|
||||
ObjArchitecture, ObjInfo, ObjReloc, ObjRelocKind, ObjSection, ObjSectionKind, ObjSymbol,
|
||||
ObjSymbolFlagSet, ObjSymbolFlags,
|
||||
};
|
||||
|
||||
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")?;
|
||||
if name.is_empty() {
|
||||
println!("Found empty sym: {:?}", symbol);
|
||||
|
@ -41,10 +41,18 @@ fn to_obj_symbol(symbol: &object::Symbol<'_, '_>) -> Result<ObjSymbol> {
|
|||
if symbol.is_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 {
|
||||
name: name.to_string(),
|
||||
demangled_name: demangle(name),
|
||||
address: symbol.address(),
|
||||
section_address,
|
||||
size: symbol.size(),
|
||||
size_known: symbol.size() != 0,
|
||||
flags,
|
||||
|
@ -61,7 +69,11 @@ const R_PPC_REL24: u32 = 10;
|
|||
const R_PPC_REL14: u32 = 11;
|
||||
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();
|
||||
for section in obj_file.sections() {
|
||||
if section.size() == 0 {
|
||||
|
@ -91,7 +103,7 @@ fn filter_sections(obj_file: &object::File<'_>) -> Result<Vec<ObjSection>> {
|
|||
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();
|
||||
for symbol in obj_file.symbols() {
|
||||
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 {
|
||||
// TODO strip local syms in diff?
|
||||
let name = symbol.name().context("Failed to process symbol name")?;
|
||||
if name.starts_with("lbl_") {
|
||||
if symbol.size() == 0 || name.starts_with("lbl_") {
|
||||
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)
|
||||
}
|
||||
|
||||
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();
|
||||
for symbol in obj_file.symbols() {
|
||||
if symbol.is_common() {
|
||||
result.push(to_obj_symbol(&symbol)?);
|
||||
result.push(to_obj_symbol(obj_file, &symbol)?);
|
||||
}
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn locate_section_symbol(
|
||||
obj_file: &object::File<'_>,
|
||||
target: &object::Symbol<'_, '_>,
|
||||
fn find_section_symbol(
|
||||
obj_file: &File<'_>,
|
||||
target: &Symbol<'_, '_>,
|
||||
address: u64,
|
||||
) -> Result<ObjSymbol> {
|
||||
let 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() {
|
||||
if !matches!(symbol.section_index(), Some(idx) if idx == section_index) {
|
||||
continue;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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(
|
||||
obj_file: &object::File<'_>,
|
||||
section: &ObjSection,
|
||||
arch: ObjArchitecture,
|
||||
obj_file: &File<'_>,
|
||||
section: &mut ObjSection,
|
||||
) -> Result<Vec<ObjReloc>> {
|
||||
let obj_section = obj_file
|
||||
.section_by_name(§ion.name)
|
||||
|
@ -175,19 +213,32 @@ fn relocations_by_section(
|
|||
};
|
||||
let kind = match reloc.kind() {
|
||||
RelocationKind::Absolute => ObjRelocKind::Absolute,
|
||||
RelocationKind::Elf(kind) => match kind {
|
||||
R_PPC_ADDR16_LO => ObjRelocKind::PpcAddr16Lo,
|
||||
R_PPC_ADDR16_HI => ObjRelocKind::PpcAddr16Hi,
|
||||
R_PPC_ADDR16_HA => ObjRelocKind::PpcAddr16Ha,
|
||||
R_PPC_REL24 => ObjRelocKind::PpcRel24,
|
||||
R_PPC_REL14 => ObjRelocKind::PpcRel14,
|
||||
R_PPC_EMB_SDA21 => ObjRelocKind::PpcEmbSda21,
|
||||
_ => {
|
||||
return Err(anyhow::Error::msg(format!(
|
||||
"Unhandled ELF relocation type: {}",
|
||||
kind
|
||||
)))
|
||||
}
|
||||
RelocationKind::Elf(kind) => match arch {
|
||||
ObjArchitecture::PowerPc => match kind {
|
||||
R_PPC_ADDR16_LO => ObjRelocKind::PpcAddr16Lo,
|
||||
R_PPC_ADDR16_HI => ObjRelocKind::PpcAddr16Hi,
|
||||
R_PPC_ADDR16_HA => ObjRelocKind::PpcAddr16Ha,
|
||||
R_PPC_REL24 => ObjRelocKind::PpcRel24,
|
||||
R_PPC_REL14 => ObjRelocKind::PpcRel14,
|
||||
R_PPC_EMB_SDA21 => ObjRelocKind::PpcEmbSda21,
|
||||
_ => {
|
||||
return Err(anyhow::Error::msg(format!(
|
||||
"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!(
|
||||
|
@ -198,23 +249,39 @@ fn relocations_by_section(
|
|||
};
|
||||
let target_section = match symbol.section() {
|
||||
SymbolSection::Common => Some(".comm".to_string()),
|
||||
SymbolSection::Section(idx) => obj_file
|
||||
.section_by_index(idx)
|
||||
.map(|s| s.name().map(|s| s.to_string()).ok())
|
||||
.ok()
|
||||
.flatten(),
|
||||
SymbolSection::Section(idx) => {
|
||||
obj_file.section_by_index(idx).and_then(|s| s.name().map(|s| s.to_string())).ok()
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
// println!("Reloc: {:?}", reloc.addend());
|
||||
// println!("Reloc: {:?}, symbol: {:?}", reloc, symbol);
|
||||
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 => {
|
||||
let addend = reloc.addend();
|
||||
if addend < 0 {
|
||||
Err(anyhow::Error::msg(format!("Negative addend in section reloc: {}", addend)))
|
||||
let addend = if reloc.has_implicit_addend() {
|
||||
let addend = u32::from_be_bytes(
|
||||
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 {
|
||||
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!(
|
||||
"Unhandled relocation symbol type {:?}",
|
||||
|
@ -228,15 +295,26 @@ fn relocations_by_section(
|
|||
|
||||
pub fn read(obj_path: &Path) -> Result<ObjInfo> {
|
||||
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 {
|
||||
architecture,
|
||||
path: obj_path.to_owned(),
|
||||
sections: filter_sections(&obj_file)?,
|
||||
common: common_symbols(&obj_file)?,
|
||||
};
|
||||
for section in &mut result.sections {
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -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 crate::jobs::build::BuildResult;
|
||||
use crate::jobs::{bindiff::BinDiffResult, build::BuildResult};
|
||||
|
||||
pub mod bindiff;
|
||||
pub mod build;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
|
||||
pub enum Job {
|
||||
Build,
|
||||
BinDiff,
|
||||
}
|
||||
pub static JOB_ID: AtomicUsize = AtomicUsize::new(0);
|
||||
pub struct JobState {
|
||||
|
@ -37,6 +39,7 @@ pub struct JobStatus {
|
|||
pub enum JobResult {
|
||||
None,
|
||||
Build(Box<BuildResult>),
|
||||
BinDiff(Box<BinDiffResult>),
|
||||
}
|
||||
|
||||
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 flagset::{flags, FlagSet};
|
||||
|
@ -31,9 +34,11 @@ pub struct ObjSection {
|
|||
}
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ObjInsArg {
|
||||
Arg(ppc750cl::Argument),
|
||||
PpcArg(ppc750cl::Argument),
|
||||
MipsArg(String),
|
||||
Reloc,
|
||||
RelocOffset,
|
||||
RelocWithBase,
|
||||
BranchOffset(i32),
|
||||
}
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct ObjInsArgDiff {
|
||||
|
@ -66,10 +71,13 @@ pub enum ObjInsDiffKind {
|
|||
}
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ObjIns {
|
||||
pub ins: ppc750cl::Ins,
|
||||
pub address: u32,
|
||||
pub code: u32,
|
||||
pub op: u8,
|
||||
pub mnemonic: String,
|
||||
pub args: Vec<ObjInsArg>,
|
||||
pub reloc: Option<ObjReloc>,
|
||||
pub branch_dest: Option<u32>,
|
||||
}
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct ObjInsDiff {
|
||||
|
@ -88,6 +96,7 @@ pub struct ObjSymbol {
|
|||
pub name: String,
|
||||
pub demangled_name: Option<String>,
|
||||
pub address: u64,
|
||||
pub section_address: u64,
|
||||
pub size: u64,
|
||||
pub size_known: bool,
|
||||
pub flags: ObjSymbolFlagSet,
|
||||
|
@ -97,8 +106,14 @@ pub struct ObjSymbol {
|
|||
pub instructions: Vec<ObjInsDiff>,
|
||||
pub match_percent: f32,
|
||||
}
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum ObjArchitecture {
|
||||
PowerPc,
|
||||
Mips,
|
||||
}
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ObjInfo {
|
||||
pub architecture: ObjArchitecture,
|
||||
pub path: PathBuf,
|
||||
pub sections: Vec<ObjSection>,
|
||||
pub common: Vec<ObjSymbol>,
|
||||
|
@ -116,6 +131,9 @@ pub enum ObjRelocKind {
|
|||
// PpcAddr14,
|
||||
PpcRel14,
|
||||
PpcEmbSda21,
|
||||
Mips26,
|
||||
MipsHi16,
|
||||
MipsLo16,
|
||||
}
|
||||
#[derive(Debug, Clone)]
|
||||
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 crate::{
|
||||
app::{AppConfig, ViewState},
|
||||
jobs::build::queue_build,
|
||||
app::{AppConfig, DiffKind, ViewState},
|
||||
jobs::{bindiff::queue_bindiff, build::queue_build},
|
||||
};
|
||||
|
||||
pub fn config_ui(ui: &mut egui::Ui, config: &Arc<RwLock<AppConfig>>, view_state: &mut ViewState) {
|
||||
let mut config_guard = config.write().unwrap();
|
||||
let AppConfig { project_dir, project_dir_change, build_asm_dir, build_src_dir, build_obj } =
|
||||
&mut *config_guard;
|
||||
let AppConfig {
|
||||
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 let Some(path) = rfd::FileDialog::new().pick_folder() {
|
||||
*project_dir = Some(path);
|
||||
*project_dir_change = true;
|
||||
*build_asm_dir = None;
|
||||
*build_src_dir = None;
|
||||
*build_obj = 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);
|
||||
if view_state.diff_kind == DiffKind::SplitObj {
|
||||
if ui.button("Select project dir").clicked() {
|
||||
if let Some(path) = rfd::FileDialog::new().pick_folder() {
|
||||
*project_dir = Some(path);
|
||||
*project_dir_change = true;
|
||||
*build_asm_dir = None;
|
||||
*build_src_dir = None;
|
||||
*build_obj = None;
|
||||
}
|
||||
}
|
||||
if let Some(dir) = build_asm_dir {
|
||||
if let Some(dir) = project_dir {
|
||||
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(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(dir) = build_src_dir {
|
||||
ui.label(dir.to_string_lossy());
|
||||
if let Some(dir) = build_asm_dir {
|
||||
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()
|
||||
.set_directory(&build_src_dir)
|
||||
.add_filter("Object file", &["o"])
|
||||
.pick_file()
|
||||
{
|
||||
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) {
|
||||
if let Some(build_src_dir) = build_src_dir {
|
||||
if ui.button("Select obj").clicked() {
|
||||
if let Some(path) = rfd::FileDialog::new()
|
||||
.set_directory(&build_src_dir)
|
||||
.add_filter("Object file", &["o", "elf"])
|
||||
.pick_file()
|
||||
{
|
||||
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());
|
||||
}
|
||||
}
|
||||
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());
|
||||
view_state.jobs.push(queue_build(new_build_obj, config.clone()));
|
||||
}
|
||||
if let Some(build_obj) = build_obj {
|
||||
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 {
|
||||
ui.label(&*build_obj);
|
||||
if ui.button("Build").clicked() {
|
||||
view_state.jobs.push(queue_build(build_obj.clone(), config.clone()));
|
||||
} else if view_state.diff_kind == DiffKind::WholeBinary {
|
||||
if ui.button("Select left obj").clicked() {
|
||||
if let Some(path) =
|
||||
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)");
|
||||
|
|
|
@ -25,13 +25,39 @@ fn write_text(str: &str, color: Color32, job: &mut LayoutJob) {
|
|||
|
||||
fn write_reloc(reloc: &ObjReloc, job: &mut LayoutJob) {
|
||||
let name = reloc.target.demangled_name.as_ref().unwrap_or(&reloc.target.name);
|
||||
write_text(name, Color32::LIGHT_GRAY, job);
|
||||
match reloc.kind {
|
||||
ObjRelocKind::PpcAddr16Lo => write_text("@l", Color32::GRAY, job),
|
||||
ObjRelocKind::PpcAddr16Hi => write_text("@h", Color32::GRAY, job),
|
||||
ObjRelocKind::PpcAddr16Ha => write_text("@ha", Color32::GRAY, job),
|
||||
ObjRelocKind::PpcEmbSda21 => write_text("@sda21", Color32::GRAY, job),
|
||||
_ => {}
|
||||
ObjRelocKind::PpcAddr16Lo => {
|
||||
write_text(name, Color32::LIGHT_GRAY, job);
|
||||
write_text("@l", 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,
|
||||
};
|
||||
write_text(
|
||||
&ins.mnemonic,
|
||||
&format!("{:<11}", ins.mnemonic),
|
||||
match diff_kind {
|
||||
ObjInsDiffKind::OpMismatch => Color32::LIGHT_BLUE,
|
||||
_ => base_color,
|
||||
|
@ -72,17 +98,13 @@ fn write_ins(
|
|||
base_color
|
||||
};
|
||||
match arg {
|
||||
ObjInsArg::Arg(arg) => match arg {
|
||||
ObjInsArg::PpcArg(arg) => match arg {
|
||||
Argument::Offset(val) => {
|
||||
write_text(&format!("{}", val), color, job);
|
||||
write_text("(", base_color, job);
|
||||
writing_offset = true;
|
||||
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(_) => {
|
||||
write_text(&format!("{}", arg), color, job);
|
||||
}
|
||||
|
@ -93,12 +115,19 @@ fn write_ins(
|
|||
ObjInsArg::Reloc => {
|
||||
write_reloc(ins.reloc.as_ref().unwrap(), job);
|
||||
}
|
||||
ObjInsArg::RelocOffset => {
|
||||
ObjInsArg::RelocWithBase => {
|
||||
write_reloc(ins.reloc.as_ref().unwrap(), job);
|
||||
write_text("(", base_color, job);
|
||||
writing_offset = true;
|
||||
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 {
|
||||
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().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 {
|
||||
if let ObjInsArg::Arg(arg) = arg {
|
||||
if let ObjInsArg::PpcArg(arg) = arg {
|
||||
match arg {
|
||||
Argument::Uimm(v) => {
|
||||
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() {}
|
||||
|
||||
for arg in &ins.args {
|
||||
if let ObjInsArg::Arg(arg) = arg {
|
||||
if let ObjInsArg::PpcArg(arg) = arg {
|
||||
match arg {
|
||||
Argument::Uimm(v) => {
|
||||
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,
|
||||
};
|
||||
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,
|
||||
&mut job,
|
||||
);
|
||||
|
|
|
@ -66,21 +66,28 @@ fn symbol_ui(
|
|||
..Default::default()
|
||||
});
|
||||
}
|
||||
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 {
|
||||
job.append("] ", 0.0, TextFormat {
|
||||
font_id: font_id.clone(),
|
||||
color: Color32::GRAY,
|
||||
..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() });
|
||||
let response = SelectableLabel::new(selected, job).ui(ui);
|
||||
if response.clicked() {
|
||||
|
|
Loading…
Reference in New Issue