mirror of https://github.com/encounter/objdiff.git
Split into objdiff-core / objdiff-gui; update egui to 0.26.2
This commit is contained in:
parent
0a85c498c5
commit
4eba5f71b0
|
@ -10,7 +10,7 @@ on:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
BUILD_PROFILE: release-lto
|
BUILD_PROFILE: release-lto
|
||||||
CARGO_BIN_NAME: objdiff
|
CARGO_BIN_NAME: objdiff-gui
|
||||||
CARGO_TARGET_DIR: target
|
CARGO_TARGET_DIR: target
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
@ -25,7 +25,7 @@ jobs:
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get -y install libgtk-3-dev
|
sudo apt-get -y install libgtk-3-dev
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Setup Rust toolchain
|
- name: Setup Rust toolchain
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@stable
|
||||||
with:
|
with:
|
||||||
|
@ -42,7 +42,7 @@ jobs:
|
||||||
RUSTFLAGS: -D warnings
|
RUSTFLAGS: -D warnings
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Setup Rust toolchain
|
- name: Setup Rust toolchain
|
||||||
# We use nightly options in rustfmt.toml
|
# We use nightly options in rustfmt.toml
|
||||||
uses: dtolnay/rust-toolchain@nightly
|
uses: dtolnay/rust-toolchain@nightly
|
||||||
|
@ -62,7 +62,7 @@ jobs:
|
||||||
# Prevent new advisories from failing CI
|
# Prevent new advisories from failing CI
|
||||||
continue-on-error: ${{ matrix.checks == 'advisories' }}
|
continue-on-error: ${{ matrix.checks == 'advisories' }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: EmbarkStudios/cargo-deny-action@v1
|
- uses: EmbarkStudios/cargo-deny-action@v1
|
||||||
with:
|
with:
|
||||||
command: check ${{ matrix.checks }}
|
command: check ${{ matrix.checks }}
|
||||||
|
@ -82,7 +82,7 @@ jobs:
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get -y install libgtk-3-dev
|
sudo apt-get -y install libgtk-3-dev
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Setup Rust toolchain
|
- name: Setup Rust toolchain
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@stable
|
||||||
- name: Cargo test
|
- name: Cargo test
|
||||||
|
@ -119,7 +119,7 @@ jobs:
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get -y install ${{ matrix.packages }}
|
sudo apt-get -y install ${{ matrix.packages }}
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Setup Rust toolchain
|
- name: Setup Rust toolchain
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@stable
|
||||||
with:
|
with:
|
||||||
|
@ -127,7 +127,7 @@ jobs:
|
||||||
- name: Cargo build
|
- name: Cargo build
|
||||||
run: cargo build --profile ${{ env.BUILD_PROFILE }} --target ${{ matrix.target }} --bin ${{ env.CARGO_BIN_NAME }} --features ${{ matrix.features }}
|
run: cargo build --profile ${{ env.BUILD_PROFILE }} --target ${{ matrix.target }} --bin ${{ env.CARGO_BIN_NAME }} --features ${{ matrix.features }}
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: ${{ matrix.name }}
|
name: ${{ matrix.name }}
|
||||||
path: |
|
path: |
|
||||||
|
@ -144,7 +144,7 @@ jobs:
|
||||||
needs: [ build ]
|
needs: [ build ]
|
||||||
steps:
|
steps:
|
||||||
- name: Download artifacts
|
- name: Download artifacts
|
||||||
uses: actions/download-artifact@v3
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
path: artifacts
|
path: artifacts
|
||||||
- name: Rename artifacts
|
- name: Rename artifacts
|
||||||
|
@ -152,7 +152,7 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
mkdir ../out
|
mkdir ../out
|
||||||
for i in */*/$BUILD_PROFILE/$CARGO_BIN_NAME*; do
|
for i in */*/$BUILD_PROFILE/$CARGO_BIN_NAME*; do
|
||||||
mv "$i" "../out/$(sed -E "s/([^/]+)\/[^/]+\/$BUILD_PROFILE\/($CARGO_BIN_NAME)/\2-\1/" <<< "$i")"
|
mv "$i" "../out/$(sed -E "s/([^/]+)\/[^/]+\/$BUILD_PROFILE\/$CARGO_BIN_NAME/objdiff-\1/" <<< "$i")"
|
||||||
done
|
done
|
||||||
ls -R ../out
|
ls -R ../out
|
||||||
- name: Release
|
- name: Release
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
97
Cargo.toml
97
Cargo.toml
|
@ -1,96 +1,11 @@
|
||||||
[package]
|
[workspace]
|
||||||
name = "objdiff"
|
members = [
|
||||||
version = "1.0.0"
|
"objdiff-core",
|
||||||
edition = "2021"
|
"objdiff-gui",
|
||||||
rust-version = "1.70"
|
]
|
||||||
authors = ["Luke Street <luke@street.dev>"]
|
resolver = "2"
|
||||||
license = "MIT OR Apache-2.0"
|
|
||||||
repository = "https://github.com/encounter/objdiff"
|
|
||||||
readme = "README.md"
|
|
||||||
description = """
|
|
||||||
A local diffing tool for decompilation projects.
|
|
||||||
"""
|
|
||||||
publish = false
|
|
||||||
build = "build.rs"
|
|
||||||
|
|
||||||
[profile.release-lto]
|
[profile.release-lto]
|
||||||
inherits = "release"
|
inherits = "release"
|
||||||
lto = "thin"
|
lto = "thin"
|
||||||
strip = "debuginfo"
|
strip = "debuginfo"
|
||||||
|
|
||||||
[features]
|
|
||||||
default = ["wgpu", "wsl"]
|
|
||||||
wgpu = ["eframe/wgpu"]
|
|
||||||
wsl = []
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
anyhow = "1.0.79"
|
|
||||||
byteorder = "1.5.0"
|
|
||||||
bytes = "1.5.0"
|
|
||||||
cfg-if = "1.0.0"
|
|
||||||
const_format = "0.2.32"
|
|
||||||
cwdemangle = "0.1.6"
|
|
||||||
dirs = "5.0.1"
|
|
||||||
eframe = { version = "0.25.0", features = ["persistence"] }
|
|
||||||
egui = "0.25.0"
|
|
||||||
egui_extras = "0.25.0"
|
|
||||||
filetime = "0.2.23"
|
|
||||||
flagset = "0.4.4"
|
|
||||||
float-ord = "0.3.2"
|
|
||||||
font-kit = "0.12.0"
|
|
||||||
gimli = { version = "0.28.1", default-features = false, features = ["read-all"] }
|
|
||||||
globset = { version = "0.4.14", features = ["serde1"] }
|
|
||||||
log = "0.4.20"
|
|
||||||
memmap2 = "0.9.3"
|
|
||||||
notify = "6.1.1"
|
|
||||||
object = { version = "0.32.2", features = ["read_core", "std", "elf"], default-features = false }
|
|
||||||
png = "0.17.11"
|
|
||||||
pollster = "0.3.0"
|
|
||||||
ppc750cl = { git = "https://github.com/encounter/ppc750cl", rev = "4a2bbbc6f84dcb76255ab6f3595a8d4a0ce96618" }
|
|
||||||
rabbitizer = "1.8.1"
|
|
||||||
rfd = { version = "0.13.0" } #, default-features = false, features = ['xdg-portal']
|
|
||||||
ron = "0.8.1"
|
|
||||||
semver = "1.0.21"
|
|
||||||
serde = { version = "1", features = ["derive"] }
|
|
||||||
serde_json = "1.0.111"
|
|
||||||
serde_yaml = "0.9.30"
|
|
||||||
shell-escape = "0.1.5"
|
|
||||||
similar = "2.4.0"
|
|
||||||
tempfile = "3.9.0"
|
|
||||||
thiserror = "1.0.56"
|
|
||||||
time = { version = "0.3.31", features = ["formatting", "local-offset"] }
|
|
||||||
toml = "0.8.8"
|
|
||||||
twox-hash = "1.6.3"
|
|
||||||
|
|
||||||
# For Linux static binaries, use rustls
|
|
||||||
[target.'cfg(target_os = "linux")'.dependencies]
|
|
||||||
reqwest = { version = "0.11.23", default-features = false, features = ["blocking", "json", "multipart", "rustls"] }
|
|
||||||
self_update = { version = "0.39.0", default-features = false, features = ["rustls"] }
|
|
||||||
|
|
||||||
# For all other platforms, use native TLS
|
|
||||||
[target.'cfg(not(target_os = "linux"))'.dependencies]
|
|
||||||
reqwest = { version = "0.11.23", default-features = false, features = ["blocking", "json", "multipart", "default-tls"] }
|
|
||||||
self_update = "0.39.0"
|
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
|
||||||
path-slash = "0.2.1"
|
|
||||||
winapi = "0.3.9"
|
|
||||||
|
|
||||||
[target.'cfg(windows)'.build-dependencies]
|
|
||||||
winres = "0.1.12"
|
|
||||||
|
|
||||||
[target.'cfg(unix)'.dependencies]
|
|
||||||
exec = "0.3.1"
|
|
||||||
|
|
||||||
# native:
|
|
||||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
|
||||||
tracing-subscriber = "0.3"
|
|
||||||
|
|
||||||
# web:
|
|
||||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
|
||||||
console_error_panic_hook = "0.1.7"
|
|
||||||
tracing-wasm = "0.2"
|
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
anyhow = "1.0.79"
|
|
||||||
vergen = { version = "8.3.1", features = ["build", "cargo", "git", "gitcl"] }
|
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
[package]
|
||||||
|
name = "objdiff-core"
|
||||||
|
version = "1.0.0"
|
||||||
|
edition = "2021"
|
||||||
|
rust-version = "1.70"
|
||||||
|
authors = ["Luke Street <luke@street.dev>"]
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
repository = "https://github.com/encounter/objdiff"
|
||||||
|
readme = "../README.md"
|
||||||
|
description = """
|
||||||
|
A local diffing tool for decompilation projects.
|
||||||
|
"""
|
||||||
|
|
||||||
|
[features]
|
||||||
|
all = ["dwarf", "mips", "ppc"]
|
||||||
|
any-arch = [] # Implicit, used to check if any arch is enabled
|
||||||
|
dwarf = ["gimli"]
|
||||||
|
mips = ["any-arch", "rabbitizer"]
|
||||||
|
ppc = ["any-arch", "cwdemangle", "ppc750cl"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow = "1.0.79"
|
||||||
|
byteorder = "1.5.0"
|
||||||
|
cwdemangle = { version = "0.1.6", optional = true }
|
||||||
|
filetime = "0.2.23"
|
||||||
|
flagset = "0.4.4"
|
||||||
|
gimli = { version = "0.28.1", default-features = false, features = ["read-all"], optional = true }
|
||||||
|
log = "0.4.20"
|
||||||
|
memmap2 = "0.9.3"
|
||||||
|
num-traits = "0.2.18"
|
||||||
|
object = { version = "0.32.2", features = ["read_core", "std", "elf"], default-features = false }
|
||||||
|
ppc750cl = { git = "https://github.com/encounter/ppc750cl", rev = "4a2bbbc6f84dcb76255ab6f3595a8d4a0ce96618", optional = true }
|
||||||
|
rabbitizer = { version = "1.8.1", optional = true }
|
||||||
|
serde = { version = "1", features = ["derive"] }
|
||||||
|
similar = "2.4.0"
|
||||||
|
twox-hash = "1.6.3"
|
|
@ -12,9 +12,10 @@ use crate::{
|
||||||
editops::{editops_find, LevEditType},
|
editops::{editops_find, LevEditType},
|
||||||
DiffAlg, DiffObjConfig, ProcessCodeResult,
|
DiffAlg, DiffObjConfig, ProcessCodeResult,
|
||||||
},
|
},
|
||||||
|
obj,
|
||||||
obj::{
|
obj::{
|
||||||
mips, ppc, ObjArchitecture, ObjInfo, ObjInsArg, ObjInsArgDiff, ObjInsBranchFrom,
|
ObjArchitecture, ObjInfo, ObjInsArg, ObjInsArgDiff, ObjInsBranchFrom, ObjInsBranchTo,
|
||||||
ObjInsBranchTo, ObjInsDiff, ObjInsDiffKind, ObjReloc, ObjSymbol, ObjSymbolFlags,
|
ObjInsDiff, ObjInsDiffKind, ObjReloc, ObjSymbol, ObjSymbolFlags,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -27,9 +28,13 @@ pub fn no_diff_code(
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let code =
|
let code =
|
||||||
&data[symbol.section_address as usize..(symbol.section_address + symbol.size) as usize];
|
&data[symbol.section_address as usize..(symbol.section_address + symbol.size) as usize];
|
||||||
let out = match arch {
|
let out: ProcessCodeResult = match arch {
|
||||||
ObjArchitecture::PowerPc => ppc::process_code(code, symbol.address, relocs, line_info)?,
|
#[cfg(feature = "ppc")]
|
||||||
ObjArchitecture::Mips => mips::process_code(
|
ObjArchitecture::PowerPc => {
|
||||||
|
obj::ppc::process_code(code, symbol.address, relocs, line_info)?
|
||||||
|
}
|
||||||
|
#[cfg(feature = "mips")]
|
||||||
|
ObjArchitecture::Mips => obj::mips::process_code(
|
||||||
code,
|
code,
|
||||||
symbol.address,
|
symbol.address,
|
||||||
symbol.address + symbol.size,
|
symbol.address + symbol.size,
|
||||||
|
@ -65,19 +70,26 @@ pub fn diff_code(
|
||||||
let right_code = &right_data[right_symbol.section_address as usize
|
let right_code = &right_data[right_symbol.section_address as usize
|
||||||
..(right_symbol.section_address + right_symbol.size) as usize];
|
..(right_symbol.section_address + right_symbol.size) as usize];
|
||||||
let (left_out, right_out) = match arch {
|
let (left_out, right_out) = match arch {
|
||||||
|
#[cfg(feature = "ppc")]
|
||||||
ObjArchitecture::PowerPc => (
|
ObjArchitecture::PowerPc => (
|
||||||
ppc::process_code(left_code, left_symbol.address, left_relocs, left_line_info)?,
|
obj::ppc::process_code(left_code, left_symbol.address, left_relocs, left_line_info)?,
|
||||||
ppc::process_code(right_code, right_symbol.address, right_relocs, right_line_info)?,
|
obj::ppc::process_code(
|
||||||
|
right_code,
|
||||||
|
right_symbol.address,
|
||||||
|
right_relocs,
|
||||||
|
right_line_info,
|
||||||
|
)?,
|
||||||
),
|
),
|
||||||
|
#[cfg(feature = "mips")]
|
||||||
ObjArchitecture::Mips => (
|
ObjArchitecture::Mips => (
|
||||||
mips::process_code(
|
obj::mips::process_code(
|
||||||
left_code,
|
left_code,
|
||||||
left_symbol.address,
|
left_symbol.address,
|
||||||
left_symbol.address + left_symbol.size,
|
left_symbol.address + left_symbol.size,
|
||||||
left_relocs,
|
left_relocs,
|
||||||
left_line_info,
|
left_line_info,
|
||||||
)?,
|
)?,
|
||||||
mips::process_code(
|
obj::mips::process_code(
|
||||||
right_code,
|
right_code,
|
||||||
right_symbol.address,
|
right_symbol.address,
|
||||||
left_symbol.address + left_symbol.size,
|
left_symbol.address + left_symbol.size,
|
||||||
|
@ -235,12 +247,8 @@ fn diff_instructions_lev(
|
||||||
cur_right = right_iter.next();
|
cur_right = right_iter.next();
|
||||||
}
|
}
|
||||||
if let (Some(left), Some(right)) = (cur_left, cur_right) {
|
if let (Some(left), Some(right)) = (cur_left, cur_right) {
|
||||||
if (left.address - left_symbol.address as u32) != left_addr {
|
debug_assert_eq!(left.address - left_symbol.address as u32, left_addr);
|
||||||
return Err(anyhow::Error::msg("Instruction address mismatch (left)"));
|
debug_assert_eq!(right.address - 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 {
|
match op.op_type {
|
||||||
LevEditType::Replace => {
|
LevEditType::Replace => {
|
||||||
left_diff.push(ObjInsDiff { ins: Some(left.clone()), ..ObjInsDiff::default() });
|
left_diff.push(ObjInsDiff { ins: Some(left.clone()), ..ObjInsDiff::default() });
|
||||||
|
@ -360,8 +368,8 @@ fn arg_eq(
|
||||||
right_diff: &ObjInsDiff,
|
right_diff: &ObjInsDiff,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
return match left {
|
return match left {
|
||||||
ObjInsArg::PpcArg(l) => match right {
|
ObjInsArg::Arg(l) | ObjInsArg::ArgWithBase(l) => match right {
|
||||||
ObjInsArg::PpcArg(r) => format!("{l}") == format!("{r}"),
|
ObjInsArg::Arg(r) | ObjInsArg::ArgWithBase(r) => l == r,
|
||||||
_ => false,
|
_ => false,
|
||||||
},
|
},
|
||||||
ObjInsArg::Reloc => {
|
ObjInsArg::Reloc => {
|
||||||
|
@ -380,9 +388,6 @@ fn arg_eq(
|
||||||
right_diff.ins.as_ref().and_then(|i| i.reloc.as_ref()),
|
right_diff.ins.as_ref().and_then(|i| i.reloc.as_ref()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
ObjInsArg::MipsArg(ls) | ObjInsArg::MipsArgWithBase(ls) => {
|
|
||||||
matches!(right, ObjInsArg::MipsArg(rs) | ObjInsArg::MipsArgWithBase(rs) if ls == rs)
|
|
||||||
}
|
|
||||||
ObjInsArg::BranchOffset(_) => {
|
ObjInsArg::BranchOffset(_) => {
|
||||||
// Compare dest instruction idx after diffing
|
// Compare dest instruction idx after diffing
|
||||||
left_diff.branch_to.as_ref().map(|b| b.ins_idx)
|
left_diff.branch_to.as_ref().map(|b| b.ins_idx)
|
||||||
|
@ -436,9 +441,8 @@ fn compare_ins(
|
||||||
state.diff_count += 1;
|
state.diff_count += 1;
|
||||||
}
|
}
|
||||||
let a_str = match a {
|
let a_str = match a {
|
||||||
ObjInsArg::PpcArg(arg) => format!("{arg}"),
|
ObjInsArg::Arg(arg) | ObjInsArg::ArgWithBase(arg) => arg.to_string(),
|
||||||
ObjInsArg::Reloc | ObjInsArg::RelocWithBase => String::new(),
|
ObjInsArg::Reloc | ObjInsArg::RelocWithBase => String::new(),
|
||||||
ObjInsArg::MipsArg(str) | ObjInsArg::MipsArgWithBase(str) => str.clone(),
|
|
||||||
ObjInsArg::BranchOffset(arg) => format!("{arg}"),
|
ObjInsArg::BranchOffset(arg) => format!("{arg}"),
|
||||||
};
|
};
|
||||||
let a_diff = if let Some(idx) = state.left_args_idx.get(&a_str) {
|
let a_diff = if let Some(idx) = state.left_args_idx.get(&a_str) {
|
||||||
|
@ -450,9 +454,8 @@ fn compare_ins(
|
||||||
ObjInsArgDiff { idx }
|
ObjInsArgDiff { idx }
|
||||||
};
|
};
|
||||||
let b_str = match b {
|
let b_str = match b {
|
||||||
ObjInsArg::PpcArg(arg) => format!("{arg}"),
|
ObjInsArg::Arg(arg) | ObjInsArg::ArgWithBase(arg) => arg.to_string(),
|
||||||
ObjInsArg::Reloc | ObjInsArg::RelocWithBase => String::new(),
|
ObjInsArg::Reloc | ObjInsArg::RelocWithBase => String::new(),
|
||||||
ObjInsArg::MipsArg(str) | ObjInsArg::MipsArgWithBase(str) => str.clone(),
|
|
||||||
ObjInsArg::BranchOffset(arg) => format!("{arg}"),
|
ObjInsArg::BranchOffset(arg) => format!("{arg}"),
|
||||||
};
|
};
|
||||||
let b_diff = if let Some(idx) = state.right_args_idx.get(&b_str) {
|
let b_diff = if let Some(idx) = state.right_args_idx.get(&b_str) {
|
|
@ -4,7 +4,7 @@ use std::{
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{ensure, Result};
|
||||||
use similar::{capture_diff_slices_deadline, Algorithm};
|
use similar::{capture_diff_slices_deadline, Algorithm};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -177,15 +177,14 @@ pub fn diff_data_similar(
|
||||||
|
|
||||||
pub fn diff_data_lev(left: &mut ObjSection, right: &mut ObjSection) -> Result<()> {
|
pub fn diff_data_lev(left: &mut ObjSection, right: &mut ObjSection) -> Result<()> {
|
||||||
let matrix_size = (left.data.len() as u64).saturating_mul(right.data.len() as u64);
|
let matrix_size = (left.data.len() as u64).saturating_mul(right.data.len() as u64);
|
||||||
if matrix_size > 1_000_000_000 {
|
ensure!(
|
||||||
bail!(
|
matrix_size < 1_000_000_000,
|
||||||
"Data section {} too large for Levenshtein diff ({} * {} = {})",
|
"Data section {} too large for Levenshtein diff ({} * {} = {})",
|
||||||
left.name,
|
left.name,
|
||||||
left.data.len(),
|
left.data.len(),
|
||||||
right.data.len(),
|
right.data.len(),
|
||||||
matrix_size
|
matrix_size
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
let edit_ops = editops_find(&left.data, &right.data);
|
let edit_ops = editops_find(&left.data, &right.data);
|
||||||
if edit_ops.is_empty() && !left.data.is_empty() {
|
if edit_ops.is_empty() && !left.data.is_empty() {
|
|
@ -0,0 +1,6 @@
|
||||||
|
pub mod diff;
|
||||||
|
pub mod obj;
|
||||||
|
pub mod util;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "any-arch"))]
|
||||||
|
compile_error!("At least one architecture feature must be enabled.");
|
|
@ -1,8 +1,7 @@
|
||||||
use std::{borrow::Cow, collections::BTreeMap, fs, io::Cursor, path::Path};
|
use std::{borrow::Cow, collections::BTreeMap, fs, io::Cursor, path::Path};
|
||||||
|
|
||||||
use anyhow::{anyhow, bail, Context, Result};
|
use anyhow::{anyhow, bail, ensure, Context, Result};
|
||||||
use byteorder::{BigEndian, ReadBytesExt};
|
use byteorder::{BigEndian, ReadBytesExt};
|
||||||
use cwdemangle::demangle;
|
|
||||||
use filetime::FileTime;
|
use filetime::FileTime;
|
||||||
use flagset::Flags;
|
use flagset::Flags;
|
||||||
use object::{
|
use object::{
|
||||||
|
@ -53,9 +52,14 @@ fn to_obj_symbol(obj_file: &File<'_>, symbol: &Symbol<'_, '_>, addend: i64) -> R
|
||||||
} else {
|
} else {
|
||||||
symbol.address()
|
symbol.address()
|
||||||
};
|
};
|
||||||
|
let mut demangled_name = None;
|
||||||
|
#[cfg(feature = "ppc")]
|
||||||
|
if obj_file.architecture() == Architecture::PowerPc {
|
||||||
|
demangled_name = cwdemangle::demangle(name, &Default::default());
|
||||||
|
}
|
||||||
Ok(ObjSymbol {
|
Ok(ObjSymbol {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
demangled_name: demangle(name, &Default::default()),
|
demangled_name,
|
||||||
address: symbol.address(),
|
address: symbol.address(),
|
||||||
section_address,
|
section_address,
|
||||||
size: symbol.size(),
|
size: symbol.size(),
|
||||||
|
@ -194,16 +198,12 @@ fn relocations_by_section(
|
||||||
RelocationTarget::Symbol(idx) => obj_file
|
RelocationTarget::Symbol(idx) => obj_file
|
||||||
.symbol_by_index(idx)
|
.symbol_by_index(idx)
|
||||||
.context("Failed to locate relocation target symbol")?,
|
.context("Failed to locate relocation target symbol")?,
|
||||||
_ => {
|
_ => bail!("Unhandled relocation target: {:?}", reloc.target()),
|
||||||
return Err(anyhow::Error::msg(format!(
|
|
||||||
"Unhandled relocation target: {:?}",
|
|
||||||
reloc.target()
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
let kind = match reloc.kind() {
|
let kind = match reloc.kind() {
|
||||||
RelocationKind::Absolute => ObjRelocKind::Absolute,
|
RelocationKind::Absolute => ObjRelocKind::Absolute,
|
||||||
RelocationKind::Elf(kind) => match arch {
|
RelocationKind::Elf(kind) => match arch {
|
||||||
|
#[cfg(feature = "ppc")]
|
||||||
ObjArchitecture::PowerPc => match kind {
|
ObjArchitecture::PowerPc => match kind {
|
||||||
elf::R_PPC_ADDR16_LO => ObjRelocKind::PpcAddr16Lo,
|
elf::R_PPC_ADDR16_LO => ObjRelocKind::PpcAddr16Lo,
|
||||||
elf::R_PPC_ADDR16_HI => ObjRelocKind::PpcAddr16Hi,
|
elf::R_PPC_ADDR16_HI => ObjRelocKind::PpcAddr16Hi,
|
||||||
|
@ -211,12 +211,9 @@ fn relocations_by_section(
|
||||||
elf::R_PPC_REL24 => ObjRelocKind::PpcRel24,
|
elf::R_PPC_REL24 => ObjRelocKind::PpcRel24,
|
||||||
elf::R_PPC_REL14 => ObjRelocKind::PpcRel14,
|
elf::R_PPC_REL14 => ObjRelocKind::PpcRel14,
|
||||||
elf::R_PPC_EMB_SDA21 => ObjRelocKind::PpcEmbSda21,
|
elf::R_PPC_EMB_SDA21 => ObjRelocKind::PpcEmbSda21,
|
||||||
_ => {
|
_ => bail!("Unhandled PPC relocation type: {kind}"),
|
||||||
return Err(anyhow::Error::msg(format!(
|
|
||||||
"Unhandled PPC relocation type: {kind}"
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
#[cfg(feature = "mips")]
|
||||||
ObjArchitecture::Mips => match kind {
|
ObjArchitecture::Mips => match kind {
|
||||||
elf::R_MIPS_26 => ObjRelocKind::Mips26,
|
elf::R_MIPS_26 => ObjRelocKind::Mips26,
|
||||||
elf::R_MIPS_HI16 => ObjRelocKind::MipsHi16,
|
elf::R_MIPS_HI16 => ObjRelocKind::MipsHi16,
|
||||||
|
@ -228,12 +225,7 @@ fn relocations_by_section(
|
||||||
_ => bail!("Unhandled MIPS relocation type: {kind}"),
|
_ => bail!("Unhandled MIPS relocation type: {kind}"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
_ => {
|
_ => bail!("Unhandled relocation type: {:?}", reloc.kind()),
|
||||||
return Err(anyhow::Error::msg(format!(
|
|
||||||
"Unhandled relocation type: {:?}",
|
|
||||||
reloc.kind()
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
let target_section = match symbol.section() {
|
let target_section = match symbol.section() {
|
||||||
SymbolSection::Common => Some(".comm".to_string()),
|
SymbolSection::Common => Some(".comm".to_string()),
|
||||||
|
@ -248,12 +240,16 @@ fn relocations_by_section(
|
||||||
);
|
);
|
||||||
match kind {
|
match kind {
|
||||||
ObjRelocKind::Absolute => addend as i64,
|
ObjRelocKind::Absolute => addend as i64,
|
||||||
|
#[cfg(feature = "mips")]
|
||||||
ObjRelocKind::MipsHi16 => ((addend & 0x0000FFFF) << 16) as i32 as i64,
|
ObjRelocKind::MipsHi16 => ((addend & 0x0000FFFF) << 16) as i32 as i64,
|
||||||
|
#[cfg(feature = "mips")]
|
||||||
ObjRelocKind::MipsLo16
|
ObjRelocKind::MipsLo16
|
||||||
| ObjRelocKind::MipsGot16
|
| ObjRelocKind::MipsGot16
|
||||||
| ObjRelocKind::MipsCall16
|
| ObjRelocKind::MipsCall16
|
||||||
| ObjRelocKind::MipsGpRel16 => (addend & 0x0000FFFF) as i16 as i64,
|
| ObjRelocKind::MipsGpRel16 => (addend & 0x0000FFFF) as i16 as i64,
|
||||||
|
#[cfg(feature = "mips")]
|
||||||
ObjRelocKind::MipsGpRel32 => addend as i32 as i64,
|
ObjRelocKind::MipsGpRel32 => addend as i32 as i64,
|
||||||
|
#[cfg(feature = "mips")]
|
||||||
ObjRelocKind::Mips26 => ((addend & 0x03FFFFFF) << 2) as i64,
|
ObjRelocKind::Mips26 => ((addend & 0x03FFFFFF) << 2) as i64,
|
||||||
_ => bail!("Unsupported implicit relocation {kind:?}"),
|
_ => bail!("Unsupported implicit relocation {kind:?}"),
|
||||||
}
|
}
|
||||||
|
@ -266,9 +262,7 @@ fn relocations_by_section(
|
||||||
to_obj_symbol(obj_file, &symbol, addend)
|
to_obj_symbol(obj_file, &symbol, addend)
|
||||||
}
|
}
|
||||||
SymbolKind::Section => {
|
SymbolKind::Section => {
|
||||||
if addend < 0 {
|
ensure!(addend >= 0, "Negative addend in reloc: {addend}");
|
||||||
return Err(anyhow::Error::msg(format!("Negative addend in reloc: {addend}")));
|
|
||||||
}
|
|
||||||
find_section_symbol(obj_file, &symbol, addend as u64)
|
find_section_symbol(obj_file, &symbol, addend as u64)
|
||||||
}
|
}
|
||||||
kind => Err(anyhow!("Unhandled relocation symbol type {kind:?}")),
|
kind => Err(anyhow!("Unhandled relocation symbol type {kind:?}")),
|
||||||
|
@ -302,27 +296,30 @@ fn line_info(obj_file: &File<'_>) -> Result<Option<BTreeMap<u64, u64>>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// DWARF 2+
|
// DWARF 2+
|
||||||
let dwarf_cow = gimli::Dwarf::load(|id| {
|
#[cfg(feature = "dwarf")]
|
||||||
Ok::<_, gimli::Error>(
|
{
|
||||||
obj_file
|
let dwarf_cow = gimli::Dwarf::load(|id| {
|
||||||
.section_by_name(id.name())
|
Ok::<_, gimli::Error>(
|
||||||
.and_then(|section| section.uncompressed_data().ok())
|
obj_file
|
||||||
.unwrap_or(Cow::Borrowed(&[][..])),
|
.section_by_name(id.name())
|
||||||
)
|
.and_then(|section| section.uncompressed_data().ok())
|
||||||
})?;
|
.unwrap_or(Cow::Borrowed(&[][..])),
|
||||||
let endian = match obj_file.endianness() {
|
)
|
||||||
Endianness::Little => gimli::RunTimeEndian::Little,
|
})?;
|
||||||
Endianness::Big => gimli::RunTimeEndian::Big,
|
let endian = match obj_file.endianness() {
|
||||||
};
|
Endianness::Little => gimli::RunTimeEndian::Little,
|
||||||
let dwarf = dwarf_cow.borrow(|section| gimli::EndianSlice::new(section, endian));
|
Endianness::Big => gimli::RunTimeEndian::Big,
|
||||||
let mut iter = dwarf.units();
|
};
|
||||||
while let Some(header) = iter.next()? {
|
let dwarf = dwarf_cow.borrow(|section| gimli::EndianSlice::new(section, endian));
|
||||||
let unit = dwarf.unit(header)?;
|
let mut iter = dwarf.units();
|
||||||
if let Some(program) = unit.line_program.clone() {
|
while let Some(header) = iter.next()? {
|
||||||
let mut rows = program.rows();
|
let unit = dwarf.unit(header)?;
|
||||||
while let Some((_header, row)) = rows.next_row()? {
|
if let Some(program) = unit.line_program.clone() {
|
||||||
if let Some(line) = row.line() {
|
let mut rows = program.rows();
|
||||||
map.insert(row.address(), line.get());
|
while let Some((_header, row)) = rows.next_row()? {
|
||||||
|
if let Some(line) = row.line() {
|
||||||
|
map.insert(row.address(), line.get());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -341,14 +338,11 @@ pub fn read(obj_path: &Path) -> Result<ObjInfo> {
|
||||||
};
|
};
|
||||||
let obj_file = File::parse(&*data)?;
|
let obj_file = File::parse(&*data)?;
|
||||||
let architecture = match obj_file.architecture() {
|
let architecture = match obj_file.architecture() {
|
||||||
|
#[cfg(feature = "ppc")]
|
||||||
Architecture::PowerPc => ObjArchitecture::PowerPc,
|
Architecture::PowerPc => ObjArchitecture::PowerPc,
|
||||||
|
#[cfg(feature = "mips")]
|
||||||
Architecture::Mips => ObjArchitecture::Mips,
|
Architecture::Mips => ObjArchitecture::Mips,
|
||||||
_ => {
|
_ => bail!("Unsupported architecture: {:?}", obj_file.architecture()),
|
||||||
return Err(anyhow::Error::msg(format!(
|
|
||||||
"Unsupported architecture: {:?}",
|
|
||||||
obj_file.architecture()
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
let mut result = ObjInfo {
|
let mut result = ObjInfo {
|
||||||
architecture,
|
architecture,
|
|
@ -5,7 +5,7 @@ use rabbitizer::{config, Abi, InstrCategory, Instruction, OperandType};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
diff::ProcessCodeResult,
|
diff::ProcessCodeResult,
|
||||||
obj::{ObjIns, ObjInsArg, ObjReloc},
|
obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc},
|
||||||
};
|
};
|
||||||
|
|
||||||
fn configure_rabbitizer() {
|
fn configure_rabbitizer() {
|
||||||
|
@ -63,23 +63,27 @@ pub fn process_code(
|
||||||
args.push(ObjInsArg::Reloc);
|
args.push(ObjInsArg::Reloc);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
args.push(ObjInsArg::MipsArg(op.disassemble(&instruction, None)));
|
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(
|
||||||
|
op.disassemble(&instruction, None),
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
OperandType::cpu_immediate_base => {
|
OperandType::cpu_immediate_base => {
|
||||||
if reloc.is_some() {
|
if reloc.is_some() {
|
||||||
args.push(ObjInsArg::RelocWithBase);
|
args.push(ObjInsArg::RelocWithBase);
|
||||||
} else {
|
} else {
|
||||||
args.push(ObjInsArg::MipsArgWithBase(
|
args.push(ObjInsArg::ArgWithBase(ObjInsArgValue::Opaque(
|
||||||
OperandType::cpu_immediate.disassemble(&instruction, None),
|
OperandType::cpu_immediate.disassemble(&instruction, None),
|
||||||
));
|
)));
|
||||||
}
|
}
|
||||||
args.push(ObjInsArg::MipsArg(
|
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(
|
||||||
OperandType::cpu_rs.disassemble(&instruction, None),
|
OperandType::cpu_rs.disassemble(&instruction, None),
|
||||||
));
|
)));
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
args.push(ObjInsArg::MipsArg(op.disassemble(&instruction, None)));
|
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(
|
||||||
|
op.disassemble(&instruction, None),
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,12 +1,16 @@
|
||||||
pub mod elf;
|
pub mod elf;
|
||||||
|
#[cfg(feature = "mips")]
|
||||||
pub mod mips;
|
pub mod mips;
|
||||||
|
#[cfg(feature = "ppc")]
|
||||||
pub mod ppc;
|
pub mod ppc;
|
||||||
|
|
||||||
use std::{collections::BTreeMap, path::PathBuf};
|
use std::{collections::BTreeMap, fmt, path::PathBuf};
|
||||||
|
|
||||||
use filetime::FileTime;
|
use filetime::FileTime;
|
||||||
use flagset::{flags, FlagSet};
|
use flagset::{flags, FlagSet};
|
||||||
|
|
||||||
|
use crate::util::ReallySigned;
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
|
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
|
||||||
pub enum ObjSectionKind {
|
pub enum ObjSectionKind {
|
||||||
Code,
|
Code,
|
||||||
|
@ -23,7 +27,8 @@ flags! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[derive(Debug, Copy, Clone, Default)]
|
#[derive(Debug, Copy, Clone, Default)]
|
||||||
pub struct ObjSymbolFlagSet(pub(crate) FlagSet<ObjSymbolFlags>);
|
pub struct ObjSymbolFlagSet(pub FlagSet<ObjSymbolFlags>);
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ObjSection {
|
pub struct ObjSection {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
@ -40,11 +45,40 @@ pub struct ObjSection {
|
||||||
pub match_percent: f32,
|
pub match_percent: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
|
pub enum ObjInsArgValue {
|
||||||
|
Signed(i16),
|
||||||
|
Unsigned(u16),
|
||||||
|
Opaque(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ObjInsArgValue {
|
||||||
|
pub fn loose_eq(&self, other: &ObjInsArgValue) -> bool {
|
||||||
|
match (self, other) {
|
||||||
|
(ObjInsArgValue::Signed(a), ObjInsArgValue::Signed(b)) => a == b,
|
||||||
|
(ObjInsArgValue::Unsigned(a), ObjInsArgValue::Unsigned(b)) => a == b,
|
||||||
|
(ObjInsArgValue::Signed(a), ObjInsArgValue::Unsigned(b))
|
||||||
|
| (ObjInsArgValue::Unsigned(b), ObjInsArgValue::Signed(a)) => *a as u16 == *b,
|
||||||
|
(ObjInsArgValue::Opaque(a), ObjInsArgValue::Opaque(b)) => a == b,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for ObjInsArgValue {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
ObjInsArgValue::Signed(v) => write!(f, "{:#x}", ReallySigned(*v)),
|
||||||
|
ObjInsArgValue::Unsigned(v) => write!(f, "{:#x}", v),
|
||||||
|
ObjInsArgValue::Opaque(v) => write!(f, "{}", v),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
pub enum ObjInsArg {
|
pub enum ObjInsArg {
|
||||||
PpcArg(ppc750cl::Argument),
|
Arg(ObjInsArgValue),
|
||||||
MipsArg(String),
|
ArgWithBase(ObjInsArgValue),
|
||||||
MipsArgWithBase(String),
|
|
||||||
Reloc,
|
Reloc,
|
||||||
RelocWithBase,
|
RelocWithBase,
|
||||||
BranchOffset(i32),
|
BranchOffset(i32),
|
||||||
|
@ -53,29 +87,8 @@ pub enum ObjInsArg {
|
||||||
impl ObjInsArg {
|
impl ObjInsArg {
|
||||||
pub fn loose_eq(&self, other: &ObjInsArg) -> bool {
|
pub fn loose_eq(&self, other: &ObjInsArg) -> bool {
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
(ObjInsArg::PpcArg(a), ObjInsArg::PpcArg(b)) => {
|
(ObjInsArg::Arg(a), ObjInsArg::Arg(b)) => a.loose_eq(b),
|
||||||
a == b
|
(ObjInsArg::ArgWithBase(a), ObjInsArg::ArgWithBase(b)) => a.loose_eq(b),
|
||||||
|| match (a, b) {
|
|
||||||
// Consider Simm and Offset equivalent
|
|
||||||
(ppc750cl::Argument::Simm(simm), ppc750cl::Argument::Offset(off))
|
|
||||||
| (ppc750cl::Argument::Offset(off), ppc750cl::Argument::Simm(simm)) => {
|
|
||||||
simm.0 == off.0
|
|
||||||
}
|
|
||||||
// Consider Uimm and Offset equivalent
|
|
||||||
(ppc750cl::Argument::Uimm(uimm), ppc750cl::Argument::Offset(off))
|
|
||||||
| (ppc750cl::Argument::Offset(off), ppc750cl::Argument::Uimm(uimm)) => {
|
|
||||||
uimm.0 == off.0 as u16
|
|
||||||
}
|
|
||||||
// Consider Uimm and Simm equivalent
|
|
||||||
(ppc750cl::Argument::Uimm(uimm), ppc750cl::Argument::Simm(simm))
|
|
||||||
| (ppc750cl::Argument::Simm(simm), ppc750cl::Argument::Uimm(uimm)) => {
|
|
||||||
uimm.0 == simm.0 as u16
|
|
||||||
}
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(ObjInsArg::MipsArg(a), ObjInsArg::MipsArg(b)) => a == b,
|
|
||||||
(ObjInsArg::MipsArgWithBase(a), ObjInsArg::MipsArgWithBase(b)) => a == b,
|
|
||||||
(ObjInsArg::Reloc, ObjInsArg::Reloc) => true,
|
(ObjInsArg::Reloc, ObjInsArg::Reloc) => true,
|
||||||
(ObjInsArg::RelocWithBase, ObjInsArg::RelocWithBase) => true,
|
(ObjInsArg::RelocWithBase, ObjInsArg::RelocWithBase) => true,
|
||||||
(ObjInsArg::BranchOffset(a), ObjInsArg::BranchOffset(b)) => a == b,
|
(ObjInsArg::BranchOffset(a), ObjInsArg::BranchOffset(b)) => a == b,
|
||||||
|
@ -89,6 +102,7 @@ pub struct ObjInsArgDiff {
|
||||||
/// Incrementing index for coloring
|
/// Incrementing index for coloring
|
||||||
pub idx: usize,
|
pub idx: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ObjInsBranchFrom {
|
pub struct ObjInsBranchFrom {
|
||||||
/// Source instruction indices
|
/// Source instruction indices
|
||||||
|
@ -96,6 +110,7 @@ pub struct ObjInsBranchFrom {
|
||||||
/// Incrementing index for coloring
|
/// Incrementing index for coloring
|
||||||
pub branch_idx: usize,
|
pub branch_idx: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ObjInsBranchTo {
|
pub struct ObjInsBranchTo {
|
||||||
/// Target instruction index
|
/// Target instruction index
|
||||||
|
@ -103,6 +118,7 @@ pub struct ObjInsBranchTo {
|
||||||
/// Incrementing index for coloring
|
/// Incrementing index for coloring
|
||||||
pub branch_idx: usize,
|
pub branch_idx: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
|
||||||
pub enum ObjInsDiffKind {
|
pub enum ObjInsDiffKind {
|
||||||
#[default]
|
#[default]
|
||||||
|
@ -113,6 +129,7 @@ pub enum ObjInsDiffKind {
|
||||||
Delete,
|
Delete,
|
||||||
Insert,
|
Insert,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ObjIns {
|
pub struct ObjIns {
|
||||||
pub address: u32,
|
pub address: u32,
|
||||||
|
@ -127,6 +144,7 @@ pub struct ObjIns {
|
||||||
/// Original (unsimplified) instruction
|
/// Original (unsimplified) instruction
|
||||||
pub orig: Option<String>,
|
pub orig: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct ObjInsDiff {
|
pub struct ObjInsDiff {
|
||||||
pub ins: Option<ObjIns>,
|
pub ins: Option<ObjIns>,
|
||||||
|
@ -139,6 +157,7 @@ pub struct ObjInsDiff {
|
||||||
/// Arg diffs
|
/// Arg diffs
|
||||||
pub arg_diff: Vec<Option<ObjInsArgDiff>>,
|
pub arg_diff: Vec<Option<ObjInsArgDiff>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
|
||||||
pub enum ObjDataDiffKind {
|
pub enum ObjDataDiffKind {
|
||||||
#[default]
|
#[default]
|
||||||
|
@ -147,6 +166,7 @@ pub enum ObjDataDiffKind {
|
||||||
Delete,
|
Delete,
|
||||||
Insert,
|
Insert,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct ObjDataDiff {
|
pub struct ObjDataDiff {
|
||||||
pub data: Vec<u8>,
|
pub data: Vec<u8>,
|
||||||
|
@ -154,6 +174,7 @@ pub struct ObjDataDiff {
|
||||||
pub len: usize,
|
pub len: usize,
|
||||||
pub symbol: String,
|
pub symbol: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ObjSymbol {
|
pub struct ObjSymbol {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
@ -170,11 +191,15 @@ pub struct ObjSymbol {
|
||||||
pub instructions: Vec<ObjInsDiff>,
|
pub instructions: Vec<ObjInsDiff>,
|
||||||
pub match_percent: Option<f32>,
|
pub match_percent: Option<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub enum ObjArchitecture {
|
pub enum ObjArchitecture {
|
||||||
|
#[cfg(feature = "ppc")]
|
||||||
PowerPc,
|
PowerPc,
|
||||||
|
#[cfg(feature = "mips")]
|
||||||
Mips,
|
Mips,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ObjInfo {
|
pub struct ObjInfo {
|
||||||
pub architecture: ObjArchitecture,
|
pub architecture: ObjArchitecture,
|
||||||
|
@ -184,27 +209,46 @@ pub struct ObjInfo {
|
||||||
pub common: Vec<ObjSymbol>,
|
pub common: Vec<ObjSymbol>,
|
||||||
pub line_info: Option<BTreeMap<u64, u64>>,
|
pub line_info: Option<BTreeMap<u64, u64>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
|
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
|
||||||
pub enum ObjRelocKind {
|
pub enum ObjRelocKind {
|
||||||
Absolute,
|
Absolute,
|
||||||
|
#[cfg(feature = "ppc")]
|
||||||
PpcAddr16Hi,
|
PpcAddr16Hi,
|
||||||
|
#[cfg(feature = "ppc")]
|
||||||
PpcAddr16Ha,
|
PpcAddr16Ha,
|
||||||
|
#[cfg(feature = "ppc")]
|
||||||
PpcAddr16Lo,
|
PpcAddr16Lo,
|
||||||
|
// #[cfg(feature = "ppc")]
|
||||||
// PpcAddr32,
|
// PpcAddr32,
|
||||||
|
// #[cfg(feature = "ppc")]
|
||||||
// PpcRel32,
|
// PpcRel32,
|
||||||
|
// #[cfg(feature = "ppc")]
|
||||||
// PpcAddr24,
|
// PpcAddr24,
|
||||||
|
#[cfg(feature = "ppc")]
|
||||||
PpcRel24,
|
PpcRel24,
|
||||||
|
// #[cfg(feature = "ppc")]
|
||||||
// PpcAddr14,
|
// PpcAddr14,
|
||||||
|
#[cfg(feature = "ppc")]
|
||||||
PpcRel14,
|
PpcRel14,
|
||||||
|
#[cfg(feature = "ppc")]
|
||||||
PpcEmbSda21,
|
PpcEmbSda21,
|
||||||
|
#[cfg(feature = "mips")]
|
||||||
Mips26,
|
Mips26,
|
||||||
|
#[cfg(feature = "mips")]
|
||||||
MipsHi16,
|
MipsHi16,
|
||||||
|
#[cfg(feature = "mips")]
|
||||||
MipsLo16,
|
MipsLo16,
|
||||||
|
#[cfg(feature = "mips")]
|
||||||
MipsGot16,
|
MipsGot16,
|
||||||
|
#[cfg(feature = "mips")]
|
||||||
MipsCall16,
|
MipsCall16,
|
||||||
|
#[cfg(feature = "mips")]
|
||||||
MipsGpRel16,
|
MipsGpRel16,
|
||||||
|
#[cfg(feature = "mips")]
|
||||||
MipsGpRel32,
|
MipsGpRel32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ObjReloc {
|
pub struct ObjReloc {
|
||||||
pub kind: ObjRelocKind,
|
pub kind: ObjRelocKind,
|
|
@ -5,20 +5,26 @@ use ppc750cl::{disasm_iter, Argument, SimplifiedIns};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
diff::ProcessCodeResult,
|
diff::ProcessCodeResult,
|
||||||
obj::{ObjIns, ObjInsArg, ObjReloc, ObjRelocKind},
|
obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjRelocKind},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Relative relocation, can be Simm or BranchOffset
|
// Relative relocation, can be Simm or BranchOffset
|
||||||
fn is_relative_arg(arg: &ObjInsArg) -> bool {
|
fn is_relative_arg(arg: &ObjInsArg) -> bool {
|
||||||
matches!(arg, ObjInsArg::PpcArg(Argument::Simm(_)) | ObjInsArg::BranchOffset(_))
|
matches!(arg, ObjInsArg::Arg(ObjInsArgValue::Signed(_)) | ObjInsArg::BranchOffset(_))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Relative or absolute relocation, can be Uimm, Simm or Offset
|
// Relative or absolute relocation, can be Uimm, Simm or Offset
|
||||||
fn is_rel_abs_arg(arg: &ObjInsArg) -> bool {
|
fn is_rel_abs_arg(arg: &ObjInsArg) -> bool {
|
||||||
matches!(arg, ObjInsArg::PpcArg(arg) if matches!(arg, Argument::Uimm(_) | Argument::Simm(_) | Argument::Offset(_)))
|
matches!(
|
||||||
|
arg,
|
||||||
|
ObjInsArg::Arg(ObjInsArgValue::Signed(_) | ObjInsArgValue::Unsigned(_))
|
||||||
|
| ObjInsArg::ArgWithBase(ObjInsArgValue::Signed(_))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_offset_arg(arg: &ObjInsArg) -> bool { matches!(arg, ObjInsArg::PpcArg(Argument::Offset(_))) }
|
fn is_offset_arg(arg: &ObjInsArg) -> bool {
|
||||||
|
matches!(arg, ObjInsArg::ArgWithBase(ObjInsArgValue::Signed(_)))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn process_code(
|
pub fn process_code(
|
||||||
data: &[u8],
|
data: &[u8],
|
||||||
|
@ -48,8 +54,13 @@ pub fn process_code(
|
||||||
.args
|
.args
|
||||||
.iter()
|
.iter()
|
||||||
.map(|a| match a {
|
.map(|a| match a {
|
||||||
|
Argument::Simm(simm) => ObjInsArg::Arg(ObjInsArgValue::Signed(simm.0)),
|
||||||
|
Argument::Uimm(uimm) => ObjInsArg::Arg(ObjInsArgValue::Unsigned(uimm.0)),
|
||||||
|
Argument::Offset(offset) => {
|
||||||
|
ObjInsArg::ArgWithBase(ObjInsArgValue::Signed(offset.0))
|
||||||
|
}
|
||||||
Argument::BranchDest(dest) => ObjInsArg::BranchOffset(dest.0),
|
Argument::BranchDest(dest) => ObjInsArg::BranchOffset(dest.0),
|
||||||
_ => ObjInsArg::PpcArg(a.clone()),
|
_ => ObjInsArg::Arg(ObjInsArgValue::Opaque(a.to_string())),
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
if let Some(reloc) = reloc {
|
if let Some(reloc) = reloc {
|
|
@ -0,0 +1,24 @@
|
||||||
|
use std::fmt::{LowerHex, UpperHex};
|
||||||
|
|
||||||
|
use num_traits::PrimInt;
|
||||||
|
|
||||||
|
// https://stackoverflow.com/questions/44711012/how-do-i-format-a-signed-integer-to-a-sign-aware-hexadecimal-representation
|
||||||
|
pub(crate) struct ReallySigned<N: PrimInt>(pub(crate) N);
|
||||||
|
|
||||||
|
impl<N: PrimInt> LowerHex for ReallySigned<N> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
let num = self.0.to_i32().unwrap();
|
||||||
|
let prefix = if f.alternate() { "0x" } else { "" };
|
||||||
|
let bare_hex = format!("{:x}", num.abs());
|
||||||
|
f.pad_integral(num >= 0, prefix, &bare_hex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N: PrimInt> UpperHex for ReallySigned<N> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
let num = self.0.to_i32().unwrap();
|
||||||
|
let prefix = if f.alternate() { "0x" } else { "" };
|
||||||
|
let bare_hex = format!("{:X}", num.abs());
|
||||||
|
f.pad_integral(num >= 0, prefix, &bare_hex)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
[package]
|
||||||
|
name = "objdiff-gui"
|
||||||
|
version = "1.0.0"
|
||||||
|
edition = "2021"
|
||||||
|
rust-version = "1.70"
|
||||||
|
authors = ["Luke Street <luke@street.dev>"]
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
repository = "https://github.com/encounter/objdiff"
|
||||||
|
readme = "../README.md"
|
||||||
|
description = """
|
||||||
|
A local diffing tool for decompilation projects.
|
||||||
|
"""
|
||||||
|
publish = false
|
||||||
|
build = "build.rs"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["wgpu", "wsl"]
|
||||||
|
wgpu = ["eframe/wgpu"]
|
||||||
|
wsl = []
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow = "1.0.79"
|
||||||
|
bytes = "1.5.0"
|
||||||
|
cfg-if = "1.0.0"
|
||||||
|
const_format = "0.2.32"
|
||||||
|
cwdemangle = "0.1.6"
|
||||||
|
dirs = "5.0.1"
|
||||||
|
eframe = { version = "0.26.2", features = ["persistence"] }
|
||||||
|
egui = "0.26.2"
|
||||||
|
egui_extras = "0.26.2"
|
||||||
|
filetime = "0.2.23"
|
||||||
|
float-ord = "0.3.2"
|
||||||
|
font-kit = "0.12.0"
|
||||||
|
globset = { version = "0.4.14", features = ["serde1"] }
|
||||||
|
log = "0.4.20"
|
||||||
|
notify = "6.1.1"
|
||||||
|
objdiff-core = { path = "../objdiff-core", features = ["all"] }
|
||||||
|
png = "0.17.11"
|
||||||
|
pollster = "0.3.0"
|
||||||
|
rfd = { version = "0.14.0" } #, default-features = false, features = ['xdg-portal']
|
||||||
|
ron = "0.8.1"
|
||||||
|
semver = "1.0.21"
|
||||||
|
serde = { version = "1", features = ["derive"] }
|
||||||
|
serde_json = "1.0.111"
|
||||||
|
serde_yaml = "0.9.30"
|
||||||
|
shell-escape = "0.1.5"
|
||||||
|
tempfile = "3.9.0"
|
||||||
|
thiserror = "1.0.56"
|
||||||
|
time = { version = "0.3.31", features = ["formatting", "local-offset"] }
|
||||||
|
toml = "0.8.8"
|
||||||
|
|
||||||
|
# For Linux static binaries, use rustls
|
||||||
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
|
reqwest = { version = "0.11.23", default-features = false, features = ["blocking", "json", "multipart", "rustls"] }
|
||||||
|
self_update = { version = "0.39.0", default-features = false, features = ["rustls"] }
|
||||||
|
|
||||||
|
# For all other platforms, use native TLS
|
||||||
|
[target.'cfg(not(target_os = "linux"))'.dependencies]
|
||||||
|
reqwest = { version = "0.11.23", default-features = false, features = ["blocking", "json", "multipart", "default-tls"] }
|
||||||
|
self_update = "0.39.0"
|
||||||
|
|
||||||
|
[target.'cfg(windows)'.dependencies]
|
||||||
|
path-slash = "0.2.1"
|
||||||
|
winapi = "0.3.9"
|
||||||
|
|
||||||
|
[target.'cfg(windows)'.build-dependencies]
|
||||||
|
winres = "0.1.12"
|
||||||
|
|
||||||
|
[target.'cfg(unix)'.dependencies]
|
||||||
|
exec = "0.3.1"
|
||||||
|
|
||||||
|
# native:
|
||||||
|
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||||
|
tracing-subscriber = "0.3"
|
||||||
|
|
||||||
|
# web:
|
||||||
|
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||||
|
console_error_panic_hook = "0.1.7"
|
||||||
|
tracing-wasm = "0.2"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
anyhow = "1.0.79"
|
||||||
|
vergen = { version = "8.3.1", features = ["build", "cargo", "git", "gitcl"] }
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 8.9 KiB After Width: | Height: | Size: 8.9 KiB |
|
@ -12,12 +12,12 @@ use std::{
|
||||||
use filetime::FileTime;
|
use filetime::FileTime;
|
||||||
use globset::{Glob, GlobSet};
|
use globset::{Glob, GlobSet};
|
||||||
use notify::{RecursiveMode, Watcher};
|
use notify::{RecursiveMode, Watcher};
|
||||||
|
use objdiff_core::diff::DiffAlg;
|
||||||
use time::UtcOffset;
|
use time::UtcOffset;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
app_config::{deserialize_config, AppConfigVersion},
|
app_config::{deserialize_config, AppConfigVersion},
|
||||||
config::{build_globset, load_project_config, ProjectObject, ProjectObjectNode, ScratchConfig},
|
config::{build_globset, load_project_config, ProjectObject, ProjectObjectNode, ScratchConfig},
|
||||||
diff::DiffAlg,
|
|
||||||
jobs::{
|
jobs::{
|
||||||
objdiff::{start_build, ObjDiffConfig},
|
objdiff::{start_build, ObjDiffConfig},
|
||||||
Job, JobQueue, JobResult, JobStatus,
|
Job, JobQueue, JobResult, JobStatus,
|
|
@ -4,7 +4,7 @@ use std::{
|
||||||
path::{Component, Path, PathBuf},
|
path::{Component, Path, PathBuf},
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{ensure, Result};
|
||||||
use filetime::FileTime;
|
use filetime::FileTime;
|
||||||
use globset::{Glob, GlobSet, GlobSetBuilder};
|
use globset::{Glob, GlobSet, GlobSetBuilder};
|
||||||
|
|
||||||
|
@ -163,9 +163,10 @@ pub fn load_project_config(config: &mut AppConfig) -> Result<()> {
|
||||||
let version_str = env!("CARGO_PKG_VERSION");
|
let version_str = env!("CARGO_PKG_VERSION");
|
||||||
let version = semver::Version::parse(version_str).unwrap();
|
let version = semver::Version::parse(version_str).unwrap();
|
||||||
let version_req = semver::VersionReq::parse(&format!(">={min_version}"))?;
|
let version_req = semver::VersionReq::parse(&format!(">={min_version}"))?;
|
||||||
if !version_req.matches(&version) {
|
ensure!(
|
||||||
bail!("Project requires objdiff version {} or higher", min_version);
|
version_req.matches(&version),
|
||||||
}
|
"Project requires objdiff version {min_version} or higher"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
config.custom_make = project_config.custom_make;
|
config.custom_make = project_config.custom_make;
|
||||||
config.target_obj_dir = project_config.target_dir.map(|p| project_dir.join(p));
|
config.target_obj_dir = project_config.target_dir.map(|p| project_dir.join(p));
|
|
@ -6,13 +6,15 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::{anyhow, Context, Error, Result};
|
use anyhow::{anyhow, Context, Error, Result};
|
||||||
|
use objdiff_core::{
|
||||||
|
diff::{diff_objs, DiffAlg, DiffObjConfig},
|
||||||
|
obj::{elf, ObjInfo},
|
||||||
|
};
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
app::{AppConfig, ObjectConfig},
|
app::{AppConfig, ObjectConfig},
|
||||||
diff::{diff_objs, DiffAlg, DiffObjConfig},
|
|
||||||
jobs::{start_job, update_status, Job, JobContext, JobResult, JobState},
|
jobs::{start_job, update_status, Job, JobContext, JobResult, JobState},
|
||||||
obj::{elf, ObjInfo},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct BuildStatus {
|
pub struct BuildStatus {
|
||||||
|
@ -90,60 +92,61 @@ pub(crate) fn run_make(config: &BuildConfig, arg: &Path) -> BuildStatus {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
match (|| -> Result<BuildStatus> {
|
match run_make_cmd(config, cwd, arg) {
|
||||||
let make = config.custom_make.as_deref().unwrap_or("make");
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
let mut command = {
|
|
||||||
let mut command = Command::new(make);
|
|
||||||
command.current_dir(cwd).arg(arg);
|
|
||||||
command
|
|
||||||
};
|
|
||||||
#[cfg(windows)]
|
|
||||||
let mut command = {
|
|
||||||
use std::os::windows::process::CommandExt;
|
|
||||||
|
|
||||||
use path_slash::PathExt;
|
|
||||||
let mut command = if config.selected_wsl_distro.is_some() {
|
|
||||||
Command::new("wsl")
|
|
||||||
} else {
|
|
||||||
Command::new(make)
|
|
||||||
};
|
|
||||||
if let Some(distro) = &config.selected_wsl_distro {
|
|
||||||
command
|
|
||||||
.arg("--cd")
|
|
||||||
.arg(cwd)
|
|
||||||
.arg("-d")
|
|
||||||
.arg(distro)
|
|
||||||
.arg("--")
|
|
||||||
.arg(make)
|
|
||||||
.arg(arg.to_slash_lossy().as_ref());
|
|
||||||
} else {
|
|
||||||
command.current_dir(cwd).arg(arg.to_slash_lossy().as_ref());
|
|
||||||
}
|
|
||||||
command.creation_flags(winapi::um::winbase::CREATE_NO_WINDOW);
|
|
||||||
command
|
|
||||||
};
|
|
||||||
let mut cmdline =
|
|
||||||
shell_escape::escape(command.get_program().to_string_lossy()).into_owned();
|
|
||||||
for arg in command.get_args() {
|
|
||||||
cmdline.push(' ');
|
|
||||||
cmdline.push_str(shell_escape::escape(arg.to_string_lossy()).as_ref());
|
|
||||||
}
|
|
||||||
let output = command.output().context("Failed to execute build")?;
|
|
||||||
let stdout = from_utf8(&output.stdout).context("Failed to process stdout")?;
|
|
||||||
let stderr = from_utf8(&output.stderr).context("Failed to process stderr")?;
|
|
||||||
Ok(BuildStatus {
|
|
||||||
success: output.status.code().unwrap_or(-1) == 0,
|
|
||||||
cmdline,
|
|
||||||
stdout: stdout.to_string(),
|
|
||||||
stderr: stderr.to_string(),
|
|
||||||
})
|
|
||||||
})() {
|
|
||||||
Ok(status) => status,
|
Ok(status) => status,
|
||||||
Err(e) => BuildStatus { success: false, stderr: e.to_string(), ..Default::default() },
|
Err(e) => BuildStatus { success: false, stderr: e.to_string(), ..Default::default() },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn run_make_cmd(config: &BuildConfig, cwd: &Path, arg: &Path) -> Result<BuildStatus> {
|
||||||
|
let make = config.custom_make.as_deref().unwrap_or("make");
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
let mut command = {
|
||||||
|
let mut command = Command::new(make);
|
||||||
|
command.current_dir(cwd).arg(arg);
|
||||||
|
command
|
||||||
|
};
|
||||||
|
#[cfg(windows)]
|
||||||
|
let mut command = {
|
||||||
|
use std::os::windows::process::CommandExt;
|
||||||
|
|
||||||
|
use path_slash::PathExt;
|
||||||
|
let mut command = if config.selected_wsl_distro.is_some() {
|
||||||
|
Command::new("wsl")
|
||||||
|
} else {
|
||||||
|
Command::new(make)
|
||||||
|
};
|
||||||
|
if let Some(distro) = &config.selected_wsl_distro {
|
||||||
|
command
|
||||||
|
.arg("--cd")
|
||||||
|
.arg(cwd)
|
||||||
|
.arg("-d")
|
||||||
|
.arg(distro)
|
||||||
|
.arg("--")
|
||||||
|
.arg(make)
|
||||||
|
.arg(arg.to_slash_lossy().as_ref());
|
||||||
|
} else {
|
||||||
|
command.current_dir(cwd).arg(arg.to_slash_lossy().as_ref());
|
||||||
|
}
|
||||||
|
command.creation_flags(winapi::um::winbase::CREATE_NO_WINDOW);
|
||||||
|
command
|
||||||
|
};
|
||||||
|
let mut cmdline = shell_escape::escape(command.get_program().to_string_lossy()).into_owned();
|
||||||
|
for arg in command.get_args() {
|
||||||
|
cmdline.push(' ');
|
||||||
|
cmdline.push_str(shell_escape::escape(arg.to_string_lossy()).as_ref());
|
||||||
|
}
|
||||||
|
let output = command.output().context("Failed to execute build")?;
|
||||||
|
let stdout = from_utf8(&output.stdout).context("Failed to process stdout")?;
|
||||||
|
let stderr = from_utf8(&output.stderr).context("Failed to process stderr")?;
|
||||||
|
Ok(BuildStatus {
|
||||||
|
success: output.status.code().unwrap_or(-1) == 0,
|
||||||
|
cmdline,
|
||||||
|
stdout: stdout.to_string(),
|
||||||
|
stderr: stderr.to_string(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn run_build(
|
fn run_build(
|
||||||
context: &JobContext,
|
context: &JobContext,
|
||||||
cancel: Receiver<()>,
|
cancel: Receiver<()>,
|
|
@ -1,13 +1,21 @@
|
||||||
#![warn(clippy::all, rust_2018_idioms)]
|
#![warn(clippy::all, rust_2018_idioms)]
|
||||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release
|
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release
|
||||||
|
|
||||||
|
mod app;
|
||||||
|
mod app_config;
|
||||||
|
mod config;
|
||||||
|
mod fonts;
|
||||||
|
mod jobs;
|
||||||
|
mod update;
|
||||||
|
mod views;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::{Error, Result};
|
use anyhow::{ensure, Result};
|
||||||
use cfg_if::cfg_if;
|
use cfg_if::cfg_if;
|
||||||
use time::UtcOffset;
|
use time::UtcOffset;
|
||||||
|
|
||||||
|
@ -17,12 +25,8 @@ fn load_icon() -> Result<egui::IconData> {
|
||||||
let mut reader = decoder.read_info()?;
|
let mut reader = decoder.read_info()?;
|
||||||
let mut buf = vec![0; reader.output_buffer_size()];
|
let mut buf = vec![0; reader.output_buffer_size()];
|
||||||
let info = reader.next_frame(&mut buf)?;
|
let info = reader.next_frame(&mut buf)?;
|
||||||
if info.bit_depth != png::BitDepth::Eight {
|
ensure!(info.bit_depth == png::BitDepth::Eight);
|
||||||
return Err(Error::msg("Invalid bit depth"));
|
ensure!(info.color_type == png::ColorType::Rgba);
|
||||||
}
|
|
||||||
if info.color_type != png::ColorType::Rgba {
|
|
||||||
return Err(Error::msg("Invalid color type"));
|
|
||||||
}
|
|
||||||
buf.truncate(info.buffer_size());
|
buf.truncate(info.buffer_size());
|
||||||
Ok(egui::IconData { rgba: buf, width: info.width, height: info.height })
|
Ok(egui::IconData { rgba: buf, width: info.width, height: info.height })
|
||||||
}
|
}
|
||||||
|
@ -57,7 +61,7 @@ fn main() {
|
||||||
eframe::run_native(
|
eframe::run_native(
|
||||||
"objdiff",
|
"objdiff",
|
||||||
native_options,
|
native_options,
|
||||||
Box::new(move |cc| Box::new(objdiff::App::new(cc, utc_offset, exec_path_clone))),
|
Box::new(move |cc| Box::new(app::App::new(cc, utc_offset, exec_path_clone))),
|
||||||
)
|
)
|
||||||
.expect("Failed to run eframe application");
|
.expect("Failed to run eframe application");
|
||||||
|
|
|
@ -14,12 +14,12 @@ use egui::{
|
||||||
SelectableLabel, TextFormat, Widget, WidgetText,
|
SelectableLabel, TextFormat, Widget, WidgetText,
|
||||||
};
|
};
|
||||||
use globset::Glob;
|
use globset::Glob;
|
||||||
|
use objdiff_core::diff::DiffAlg;
|
||||||
use self_update::cargo_crate_version;
|
use self_update::cargo_crate_version;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
app::{AppConfig, AppConfigRef, ObjectConfig},
|
app::{AppConfig, AppConfigRef, ObjectConfig},
|
||||||
config::{ProjectObject, ProjectObjectNode},
|
config::{ProjectObject, ProjectObjectNode},
|
||||||
diff::DiffAlg,
|
|
||||||
jobs::{
|
jobs::{
|
||||||
check_update::{start_check_update, CheckUpdateResult},
|
check_update::{start_check_update, CheckUpdateResult},
|
||||||
update::start_update,
|
update::start_update,
|
||||||
|
@ -605,7 +605,7 @@ fn split_obj_config_ui(
|
||||||
ui.separator();
|
ui.separator();
|
||||||
|
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
subheading(ui, "Custom make program", appearance);
|
subheading(ui, "Build program", appearance);
|
||||||
ui.link(HELP_ICON).on_hover_ui(|ui| {
|
ui.link(HELP_ICON).on_hover_ui(|ui| {
|
||||||
let mut job = LayoutJob::default();
|
let mut job = LayoutJob::default();
|
||||||
job.append("By default, objdiff will build with ", 0.0, text_format.clone());
|
job.append("By default, objdiff will build with ", 0.0, text_format.clone());
|
||||||
|
@ -630,7 +630,7 @@ fn split_obj_config_ui(
|
||||||
if ui
|
if ui
|
||||||
.add_enabled(
|
.add_enabled(
|
||||||
config.project_config_info.is_none(),
|
config.project_config_info.is_none(),
|
||||||
egui::TextEdit::singleline(&mut custom_make_str),
|
egui::TextEdit::singleline(&mut custom_make_str).hint_text("make"),
|
||||||
)
|
)
|
||||||
.on_disabled_hover_text(CONFIG_DISABLED_TEXT)
|
.on_disabled_hover_text(CONFIG_DISABLED_TEXT)
|
||||||
.changed()
|
.changed()
|
|
@ -1,16 +1,14 @@
|
||||||
use std::{cmp::min, default::Default, mem::take};
|
use std::{cmp::min, default::Default, mem::take};
|
||||||
|
|
||||||
use egui::{text::LayoutJob, Align, Label, Layout, Sense, Vec2};
|
use egui::{text::LayoutJob, Align, Label, Layout, Sense, Vec2, Widget};
|
||||||
use egui_extras::{Column, TableBuilder};
|
use egui_extras::{Column, TableBuilder};
|
||||||
|
use objdiff_core::obj::{ObjDataDiff, ObjDataDiffKind, ObjInfo, ObjSection};
|
||||||
use time::format_description;
|
use time::format_description;
|
||||||
|
|
||||||
use crate::{
|
use crate::views::{
|
||||||
obj::{ObjDataDiff, ObjDataDiffKind, ObjInfo, ObjSection},
|
appearance::Appearance,
|
||||||
views::{
|
symbol_diff::{DiffViewState, SymbolReference, View},
|
||||||
appearance::Appearance,
|
write_text,
|
||||||
symbol_diff::{DiffViewState, SymbolReference, View},
|
|
||||||
write_text,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const BYTES_PER_ROW: usize = 16;
|
const BYTES_PER_ROW: usize = 16;
|
||||||
|
@ -90,7 +88,7 @@ fn data_row_ui(ui: &mut egui::Ui, address: usize, diffs: &[ObjDataDiff], appeara
|
||||||
write_text(text.as_str(), base_color, &mut job, appearance.code_font.clone());
|
write_text(text.as_str(), base_color, &mut job, appearance.code_font.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ui.add(Label::new(job).sense(Sense::click()));
|
Label::new(job).sense(Sense::click()).ui(ui);
|
||||||
// .on_hover_ui_at_pointer(|ui| ins_hover_ui(ui, ins))
|
// .on_hover_ui_at_pointer(|ui| ins_hover_ui(ui, ins))
|
||||||
// .context_menu(|ui| ins_context_menu(ui, ins));
|
// .context_menu(|ui| ins_context_menu(ui, ins));
|
||||||
}
|
}
|
||||||
|
@ -252,6 +250,7 @@ pub fn data_diff_ui(ui: &mut egui::Ui, state: &mut DiffViewState, appearance: &A
|
||||||
ui.separator();
|
ui.separator();
|
||||||
|
|
||||||
// Table
|
// Table
|
||||||
|
ui.style_mut().interaction.selectable_labels = false;
|
||||||
let available_height = ui.available_height();
|
let available_height = ui.available_height();
|
||||||
let table = TableBuilder::new(ui)
|
let table = TableBuilder::new(ui)
|
||||||
.striped(false)
|
.striped(false)
|
|
@ -3,22 +3,20 @@ use std::{
|
||||||
default::Default,
|
default::Default,
|
||||||
};
|
};
|
||||||
|
|
||||||
use cwdemangle::demangle;
|
use egui::{
|
||||||
use egui::{text::LayoutJob, Align, Color32, Label, Layout, RichText, Sense, TextFormat, Vec2};
|
text::LayoutJob, Align, Color32, Label, Layout, RichText, Sense, TextFormat, Vec2, Widget,
|
||||||
|
};
|
||||||
use egui_extras::{Column, TableBuilder, TableRow};
|
use egui_extras::{Column, TableBuilder, TableRow};
|
||||||
use ppc750cl::Argument;
|
use objdiff_core::obj::{
|
||||||
|
ObjInfo, ObjIns, ObjInsArg, ObjInsArgDiff, ObjInsArgValue, ObjInsDiff, ObjInsDiffKind,
|
||||||
|
ObjReloc, ObjRelocKind, ObjSymbol,
|
||||||
|
};
|
||||||
use time::format_description;
|
use time::format_description;
|
||||||
|
|
||||||
use crate::{
|
use crate::views::{
|
||||||
obj::{
|
appearance::Appearance,
|
||||||
ObjInfo, ObjIns, ObjInsArg, ObjInsArgDiff, ObjInsDiff, ObjInsDiffKind, ObjReloc,
|
symbol_diff::{match_color_for_symbol, DiffViewState, SymbolReference, View},
|
||||||
ObjRelocKind, ObjSymbol,
|
write_text,
|
||||||
},
|
|
||||||
views::{
|
|
||||||
appearance::Appearance,
|
|
||||||
symbol_diff::{match_color_for_symbol, DiffViewState, SymbolReference, View},
|
|
||||||
write_text,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -162,11 +160,9 @@ fn write_ins(
|
||||||
} else {
|
} else {
|
||||||
Color32::TRANSPARENT
|
Color32::TRANSPARENT
|
||||||
});
|
});
|
||||||
if ui
|
let response = Label::new(op_label).sense(Sense::click()).ui(ui);
|
||||||
.add(Label::new(op_label).sense(Sense::click()))
|
response.context_menu(|ui| ins_context_menu(ui, ins));
|
||||||
.context_menu(|ui| ins_context_menu(ui, ins))
|
if response.clicked() {
|
||||||
.clicked()
|
|
||||||
{
|
|
||||||
if highlighted_op {
|
if highlighted_op {
|
||||||
ins_view_state.highlight = HighlightKind::None;
|
ins_view_state.highlight = HighlightKind::None;
|
||||||
} else {
|
} else {
|
||||||
|
@ -215,19 +211,14 @@ fn write_ins(
|
||||||
};
|
};
|
||||||
let mut new_writing_offset = false;
|
let mut new_writing_offset = false;
|
||||||
match arg {
|
match arg {
|
||||||
ObjInsArg::PpcArg(arg) => match arg {
|
ObjInsArg::Arg(arg) => {
|
||||||
Argument::Offset(val) => {
|
job.append(&arg.to_string(), 0.0, text_format);
|
||||||
job.append(&format!("{val}"), 0.0, text_format);
|
}
|
||||||
write_text("(", base_color, &mut job, appearance.code_font.clone());
|
ObjInsArg::ArgWithBase(arg) => {
|
||||||
new_writing_offset = true;
|
job.append(&arg.to_string(), 0.0, text_format);
|
||||||
}
|
write_text("(", base_color, &mut job, appearance.code_font.clone());
|
||||||
Argument::Uimm(_) | Argument::Simm(_) => {
|
new_writing_offset = true;
|
||||||
job.append(&format!("{arg}"), 0.0, text_format);
|
}
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
job.append(&format!("{arg}"), 0.0, text_format);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ObjInsArg::Reloc => {
|
ObjInsArg::Reloc => {
|
||||||
write_reloc(
|
write_reloc(
|
||||||
ins.reloc.as_ref().unwrap(),
|
ins.reloc.as_ref().unwrap(),
|
||||||
|
@ -248,14 +239,6 @@ fn write_ins(
|
||||||
write_text("(", base_color, &mut job, appearance.code_font.clone());
|
write_text("(", base_color, &mut job, appearance.code_font.clone());
|
||||||
new_writing_offset = true;
|
new_writing_offset = true;
|
||||||
}
|
}
|
||||||
ObjInsArg::MipsArg(str) => {
|
|
||||||
job.append(str.strip_prefix('$').unwrap_or(str), 0.0, text_format);
|
|
||||||
}
|
|
||||||
ObjInsArg::MipsArgWithBase(str) => {
|
|
||||||
job.append(str.strip_prefix('$').unwrap_or(str), 0.0, text_format);
|
|
||||||
write_text("(", base_color, &mut job, appearance.code_font.clone());
|
|
||||||
new_writing_offset = true;
|
|
||||||
}
|
|
||||||
ObjInsArg::BranchOffset(offset) => {
|
ObjInsArg::BranchOffset(offset) => {
|
||||||
let addr = offset + ins.address as i32 - base_addr as i32;
|
let addr = offset + ins.address as i32 - base_addr as i32;
|
||||||
job.append(&format!("{addr:x}"), 0.0, text_format);
|
job.append(&format!("{addr:x}"), 0.0, text_format);
|
||||||
|
@ -264,12 +247,14 @@ fn write_ins(
|
||||||
if writing_offset {
|
if writing_offset {
|
||||||
write_text(")", base_color, &mut job, appearance.code_font.clone());
|
write_text(")", base_color, &mut job, appearance.code_font.clone());
|
||||||
}
|
}
|
||||||
|
// For text selection / copy
|
||||||
|
if i == ins.args.len() - 1 {
|
||||||
|
write_text("\n", base_color, &mut job, appearance.code_font.clone());
|
||||||
|
}
|
||||||
writing_offset = new_writing_offset;
|
writing_offset = new_writing_offset;
|
||||||
if ui
|
let response = Label::new(job).sense(Sense::click()).ui(ui);
|
||||||
.add(Label::new(job).sense(Sense::click()))
|
response.context_menu(|ui| ins_context_menu(ui, ins));
|
||||||
.context_menu(|ui| ins_context_menu(ui, ins))
|
if response.clicked() {
|
||||||
.clicked()
|
|
||||||
{
|
|
||||||
if highlighted_arg {
|
if highlighted_arg {
|
||||||
ins_view_state.highlight = HighlightKind::None;
|
ins_view_state.highlight = HighlightKind::None;
|
||||||
} else if matches!(arg, ObjInsArg::Reloc | ObjInsArg::RelocWithBase) {
|
} else if matches!(arg, ObjInsArg::Reloc | ObjInsArg::RelocWithBase) {
|
||||||
|
@ -297,16 +282,13 @@ fn ins_hover_ui(ui: &mut egui::Ui, ins: &ObjIns, appearance: &Appearance) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for arg in &ins.args {
|
for arg in &ins.args {
|
||||||
if let ObjInsArg::PpcArg(arg) = arg {
|
if let ObjInsArg::Arg(arg) | ObjInsArg::ArgWithBase(arg) = arg {
|
||||||
match arg {
|
match arg {
|
||||||
Argument::Uimm(v) => {
|
ObjInsArgValue::Signed(v) => {
|
||||||
ui.label(format!("{} == {}", v, v.0));
|
ui.label(format!("{arg} == {v}"));
|
||||||
}
|
}
|
||||||
Argument::Simm(v) => {
|
ObjInsArgValue::Unsigned(v) => {
|
||||||
ui.label(format!("{} == {}", v, v.0));
|
ui.label(format!("{arg} == {v}"));
|
||||||
}
|
|
||||||
Argument::Offset(v) => {
|
|
||||||
ui.label(format!("{} == {}", v, v.0));
|
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
@ -341,35 +323,25 @@ fn ins_context_menu(ui: &mut egui::Ui, ins: &ObjIns) {
|
||||||
// if ui.button("Copy hex").clicked() {}
|
// if ui.button("Copy hex").clicked() {}
|
||||||
|
|
||||||
for arg in &ins.args {
|
for arg in &ins.args {
|
||||||
if let ObjInsArg::PpcArg(arg) = arg {
|
if let ObjInsArg::Arg(arg) | ObjInsArg::ArgWithBase(arg) = arg {
|
||||||
match arg {
|
match arg {
|
||||||
Argument::Uimm(v) => {
|
ObjInsArgValue::Signed(v) => {
|
||||||
if ui.button(format!("Copy \"{v}\"")).clicked() {
|
if ui.button(format!("Copy \"{arg}\"")).clicked() {
|
||||||
ui.output_mut(|output| output.copied_text = format!("{v}"));
|
ui.output_mut(|output| output.copied_text = arg.to_string());
|
||||||
ui.close_menu();
|
ui.close_menu();
|
||||||
}
|
}
|
||||||
if ui.button(format!("Copy \"{}\"", v.0)).clicked() {
|
if ui.button(format!("Copy \"{v}\"")).clicked() {
|
||||||
ui.output_mut(|output| output.copied_text = format!("{}", v.0));
|
ui.output_mut(|output| output.copied_text = v.to_string());
|
||||||
ui.close_menu();
|
ui.close_menu();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Argument::Simm(v) => {
|
ObjInsArgValue::Unsigned(v) => {
|
||||||
|
if ui.button(format!("Copy \"{arg}\"")).clicked() {
|
||||||
|
ui.output_mut(|output| output.copied_text = arg.to_string());
|
||||||
|
ui.close_menu();
|
||||||
|
}
|
||||||
if ui.button(format!("Copy \"{v}\"")).clicked() {
|
if ui.button(format!("Copy \"{v}\"")).clicked() {
|
||||||
ui.output_mut(|output| output.copied_text = format!("{v}"));
|
ui.output_mut(|output| output.copied_text = v.to_string());
|
||||||
ui.close_menu();
|
|
||||||
}
|
|
||||||
if ui.button(format!("Copy \"{}\"", v.0)).clicked() {
|
|
||||||
ui.output_mut(|output| output.copied_text = format!("{}", v.0));
|
|
||||||
ui.close_menu();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Argument::Offset(v) => {
|
|
||||||
if ui.button(format!("Copy \"{v}\"")).clicked() {
|
|
||||||
ui.output_mut(|output| output.copied_text = format!("{v}"));
|
|
||||||
ui.close_menu();
|
|
||||||
}
|
|
||||||
if ui.button(format!("Copy \"{}\"", v.0)).clicked() {
|
|
||||||
ui.output_mut(|output| output.copied_text = format!("{}", v.0));
|
|
||||||
ui.close_menu();
|
ui.close_menu();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -451,11 +423,9 @@ fn asm_row_ui(
|
||||||
},
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
if ui
|
let response = Label::new(job).sense(Sense::click()).selectable(false).ui(ui);
|
||||||
.add(Label::new(job).sense(Sense::click()))
|
response.context_menu(|ui| ins_context_menu(ui, ins));
|
||||||
.context_menu(|ui| ins_context_menu(ui, ins))
|
if response.clicked() {
|
||||||
.clicked()
|
|
||||||
{
|
|
||||||
if addr_highlight {
|
if addr_highlight {
|
||||||
ins_view_state.highlight = HighlightKind::None;
|
ins_view_state.highlight = HighlightKind::None;
|
||||||
} else {
|
} else {
|
||||||
|
@ -484,7 +454,7 @@ fn asm_row_ui(
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
ui.add(Label::new(job));
|
Label::new(job).selectable(false).ui(ui);
|
||||||
write_ins(ins, &ins_diff.kind, &ins_diff.arg_diff, base_addr, ui, appearance, ins_view_state);
|
write_ins(ins, &ins_diff.kind, &ins_diff.arg_diff, base_addr, ui, appearance, ins_view_state);
|
||||||
if let Some(branch) = &ins_diff.branch_to {
|
if let Some(branch) = &ins_diff.branch_to {
|
||||||
let mut job = LayoutJob::default();
|
let mut job = LayoutJob::default();
|
||||||
|
@ -494,7 +464,7 @@ fn asm_row_ui(
|
||||||
&mut job,
|
&mut job,
|
||||||
appearance.code_font.clone(),
|
appearance.code_font.clone(),
|
||||||
);
|
);
|
||||||
ui.add(Label::new(job));
|
Label::new(job).selectable(false).ui(ui);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -509,13 +479,8 @@ fn asm_col_ui(
|
||||||
asm_row_ui(ui, ins_diff, symbol, appearance, ins_view_state);
|
asm_row_ui(ui, ins_diff, symbol, appearance, ins_view_state);
|
||||||
});
|
});
|
||||||
if let Some(ins) = &ins_diff.ins {
|
if let Some(ins) = &ins_diff.ins {
|
||||||
response
|
response.on_hover_ui_at_pointer(|ui| ins_hover_ui(ui, ins, appearance));
|
||||||
.on_hover_ui_at_pointer(|ui| {
|
// .context_menu(|ui| ins_context_menu(ui, ins));
|
||||||
ins_hover_ui(ui, ins, appearance);
|
|
||||||
})
|
|
||||||
.context_menu(|ui| {
|
|
||||||
ins_context_menu(ui, ins);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -604,8 +569,10 @@ pub fn function_diff_ui(ui: &mut egui::Ui, state: &mut DiffViewState, appearance
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let demangled = demangle(&selected_symbol.symbol_name, &Default::default());
|
let name = selected_symbol
|
||||||
let name = demangled.as_deref().unwrap_or(&selected_symbol.symbol_name);
|
.demangled_symbol_name
|
||||||
|
.as_deref()
|
||||||
|
.unwrap_or(&selected_symbol.symbol_name);
|
||||||
let mut job = LayoutJob::simple(
|
let mut job = LayoutJob::simple(
|
||||||
name.to_string(),
|
name.to_string(),
|
||||||
appearance.code_font.clone(),
|
appearance.code_font.clone(),
|
||||||
|
@ -681,6 +648,7 @@ pub fn function_diff_ui(ui: &mut egui::Ui, state: &mut DiffViewState, appearance
|
||||||
ui.separator();
|
ui.separator();
|
||||||
|
|
||||||
// Table
|
// Table
|
||||||
|
ui.style_mut().interaction.selectable_labels = false;
|
||||||
let available_height = ui.available_height();
|
let available_height = ui.available_height();
|
||||||
let table = TableBuilder::new(ui)
|
let table = TableBuilder::new(ui)
|
||||||
.striped(false)
|
.striped(false)
|
|
@ -5,6 +5,7 @@ use egui::{
|
||||||
SelectableLabel, TextEdit, Ui, Vec2, Widget,
|
SelectableLabel, TextEdit, Ui, Vec2, Widget,
|
||||||
};
|
};
|
||||||
use egui_extras::{Size, StripBuilder};
|
use egui_extras::{Size, StripBuilder};
|
||||||
|
use objdiff_core::obj::{ObjInfo, ObjSection, ObjSectionKind, ObjSymbol, ObjSymbolFlags};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
app::AppConfigRef,
|
app::AppConfigRef,
|
||||||
|
@ -13,12 +14,12 @@ use crate::{
|
||||||
objdiff::{BuildStatus, ObjDiffResult},
|
objdiff::{BuildStatus, ObjDiffResult},
|
||||||
Job, JobQueue, JobResult,
|
Job, JobQueue, JobResult,
|
||||||
},
|
},
|
||||||
obj::{ObjInfo, ObjSection, ObjSectionKind, ObjSymbol, ObjSymbolFlags},
|
|
||||||
views::{appearance::Appearance, function_diff::FunctionViewState, write_text},
|
views::{appearance::Appearance, function_diff::FunctionViewState, write_text},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct SymbolReference {
|
pub struct SymbolReference {
|
||||||
pub symbol_name: String,
|
pub symbol_name: String,
|
||||||
|
pub demangled_symbol_name: Option<String>,
|
||||||
pub section_name: String,
|
pub section_name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,19 +211,21 @@ fn symbol_ui(
|
||||||
write_text(name, appearance.highlight_color, &mut job, appearance.code_font.clone());
|
write_text(name, appearance.highlight_color, &mut job, appearance.code_font.clone());
|
||||||
let response = SelectableLabel::new(selected, job)
|
let response = SelectableLabel::new(selected, job)
|
||||||
.ui(ui)
|
.ui(ui)
|
||||||
.context_menu(|ui| symbol_context_menu_ui(ui, symbol))
|
|
||||||
.on_hover_ui_at_pointer(|ui| symbol_hover_ui(ui, symbol, appearance));
|
.on_hover_ui_at_pointer(|ui| symbol_hover_ui(ui, symbol, appearance));
|
||||||
|
response.context_menu(|ui| symbol_context_menu_ui(ui, symbol));
|
||||||
if response.clicked() {
|
if response.clicked() {
|
||||||
if let Some(section) = section {
|
if let Some(section) = section {
|
||||||
if section.kind == ObjSectionKind::Code {
|
if section.kind == ObjSectionKind::Code {
|
||||||
state.selected_symbol = Some(SymbolReference {
|
state.selected_symbol = Some(SymbolReference {
|
||||||
symbol_name: symbol.name.clone(),
|
symbol_name: symbol.name.clone(),
|
||||||
|
demangled_symbol_name: symbol.demangled_name.clone(),
|
||||||
section_name: section.name.clone(),
|
section_name: section.name.clone(),
|
||||||
});
|
});
|
||||||
ret = Some(View::FunctionDiff);
|
ret = Some(View::FunctionDiff);
|
||||||
} else if section.kind == ObjSectionKind::Data {
|
} else if section.kind == ObjSectionKind::Data {
|
||||||
state.selected_symbol = Some(SymbolReference {
|
state.selected_symbol = Some(SymbolReference {
|
||||||
symbol_name: section.name.clone(),
|
symbol_name: section.name.clone(),
|
||||||
|
demangled_symbol_name: None,
|
||||||
section_name: section.name.clone(),
|
section_name: section.name.clone(),
|
||||||
});
|
});
|
||||||
ret = Some(View::DataDiff);
|
ret = Some(View::DataDiff);
|
13
src/lib.rs
13
src/lib.rs
|
@ -1,13 +0,0 @@
|
||||||
#![warn(clippy::all, rust_2018_idioms)]
|
|
||||||
|
|
||||||
pub use app::App;
|
|
||||||
|
|
||||||
mod app;
|
|
||||||
mod app_config;
|
|
||||||
mod config;
|
|
||||||
mod diff;
|
|
||||||
mod fonts;
|
|
||||||
mod jobs;
|
|
||||||
mod obj;
|
|
||||||
mod update;
|
|
||||||
mod views;
|
|
Loading…
Reference in New Issue