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:
|
||||
BUILD_PROFILE: release-lto
|
||||
CARGO_BIN_NAME: objdiff
|
||||
CARGO_BIN_NAME: objdiff-gui
|
||||
CARGO_TARGET_DIR: target
|
||||
|
||||
jobs:
|
||||
|
@ -25,7 +25,7 @@ jobs:
|
|||
sudo apt-get update
|
||||
sudo apt-get -y install libgtk-3-dev
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
|
@ -42,7 +42,7 @@ jobs:
|
|||
RUSTFLAGS: -D warnings
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup Rust toolchain
|
||||
# We use nightly options in rustfmt.toml
|
||||
uses: dtolnay/rust-toolchain@nightly
|
||||
|
@ -62,7 +62,7 @@ jobs:
|
|||
# Prevent new advisories from failing CI
|
||||
continue-on-error: ${{ matrix.checks == 'advisories' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: EmbarkStudios/cargo-deny-action@v1
|
||||
with:
|
||||
command: check ${{ matrix.checks }}
|
||||
|
@ -82,7 +82,7 @@ jobs:
|
|||
sudo apt-get update
|
||||
sudo apt-get -y install libgtk-3-dev
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
- name: Cargo test
|
||||
|
@ -119,7 +119,7 @@ jobs:
|
|||
sudo apt-get update
|
||||
sudo apt-get -y install ${{ matrix.packages }}
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
|
@ -127,7 +127,7 @@ jobs:
|
|||
- name: Cargo build
|
||||
run: cargo build --profile ${{ env.BUILD_PROFILE }} --target ${{ matrix.target }} --bin ${{ env.CARGO_BIN_NAME }} --features ${{ matrix.features }}
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.name }}
|
||||
path: |
|
||||
|
@ -144,7 +144,7 @@ jobs:
|
|||
needs: [ build ]
|
||||
steps:
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: artifacts
|
||||
- name: Rename artifacts
|
||||
|
@ -152,7 +152,7 @@ jobs:
|
|||
run: |
|
||||
mkdir ../out
|
||||
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
|
||||
ls -R ../out
|
||||
- name: Release
|
||||
|
|
File diff suppressed because it is too large
Load Diff
97
Cargo.toml
97
Cargo.toml
|
@ -1,96 +1,11 @@
|
|||
[package]
|
||||
name = "objdiff"
|
||||
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"
|
||||
[workspace]
|
||||
members = [
|
||||
"objdiff-core",
|
||||
"objdiff-gui",
|
||||
]
|
||||
resolver = "2"
|
||||
|
||||
[profile.release-lto]
|
||||
inherits = "release"
|
||||
lto = "thin"
|
||||
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},
|
||||
DiffAlg, DiffObjConfig, ProcessCodeResult,
|
||||
},
|
||||
obj,
|
||||
obj::{
|
||||
mips, ppc, ObjArchitecture, ObjInfo, ObjInsArg, ObjInsArgDiff, ObjInsBranchFrom,
|
||||
ObjInsBranchTo, ObjInsDiff, ObjInsDiffKind, ObjReloc, ObjSymbol, ObjSymbolFlags,
|
||||
ObjArchitecture, ObjInfo, ObjInsArg, ObjInsArgDiff, ObjInsBranchFrom, ObjInsBranchTo,
|
||||
ObjInsDiff, ObjInsDiffKind, ObjReloc, ObjSymbol, ObjSymbolFlags,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -27,9 +28,13 @@ pub fn no_diff_code(
|
|||
) -> Result<()> {
|
||||
let code =
|
||||
&data[symbol.section_address as usize..(symbol.section_address + symbol.size) as usize];
|
||||
let out = match arch {
|
||||
ObjArchitecture::PowerPc => ppc::process_code(code, symbol.address, relocs, line_info)?,
|
||||
ObjArchitecture::Mips => mips::process_code(
|
||||
let out: ProcessCodeResult = match arch {
|
||||
#[cfg(feature = "ppc")]
|
||||
ObjArchitecture::PowerPc => {
|
||||
obj::ppc::process_code(code, symbol.address, relocs, line_info)?
|
||||
}
|
||||
#[cfg(feature = "mips")]
|
||||
ObjArchitecture::Mips => obj::mips::process_code(
|
||||
code,
|
||||
symbol.address,
|
||||
symbol.address + symbol.size,
|
||||
|
@ -65,19 +70,26 @@ pub fn diff_code(
|
|||
let right_code = &right_data[right_symbol.section_address as usize
|
||||
..(right_symbol.section_address + right_symbol.size) as usize];
|
||||
let (left_out, right_out) = match arch {
|
||||
#[cfg(feature = "ppc")]
|
||||
ObjArchitecture::PowerPc => (
|
||||
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(left_code, left_symbol.address, left_relocs, left_line_info)?,
|
||||
obj::ppc::process_code(
|
||||
right_code,
|
||||
right_symbol.address,
|
||||
right_relocs,
|
||||
right_line_info,
|
||||
)?,
|
||||
),
|
||||
#[cfg(feature = "mips")]
|
||||
ObjArchitecture::Mips => (
|
||||
mips::process_code(
|
||||
obj::mips::process_code(
|
||||
left_code,
|
||||
left_symbol.address,
|
||||
left_symbol.address + left_symbol.size,
|
||||
left_relocs,
|
||||
left_line_info,
|
||||
)?,
|
||||
mips::process_code(
|
||||
obj::mips::process_code(
|
||||
right_code,
|
||||
right_symbol.address,
|
||||
left_symbol.address + left_symbol.size,
|
||||
|
@ -235,12 +247,8 @@ fn diff_instructions_lev(
|
|||
cur_right = right_iter.next();
|
||||
}
|
||||
if let (Some(left), Some(right)) = (cur_left, cur_right) {
|
||||
if (left.address - left_symbol.address as u32) != left_addr {
|
||||
return Err(anyhow::Error::msg("Instruction address mismatch (left)"));
|
||||
}
|
||||
if (right.address - right_symbol.address as u32) != right_addr {
|
||||
return Err(anyhow::Error::msg("Instruction address mismatch (right)"));
|
||||
}
|
||||
debug_assert_eq!(left.address - left_symbol.address as u32, left_addr);
|
||||
debug_assert_eq!(right.address - right_symbol.address as u32, right_addr);
|
||||
match op.op_type {
|
||||
LevEditType::Replace => {
|
||||
left_diff.push(ObjInsDiff { ins: Some(left.clone()), ..ObjInsDiff::default() });
|
||||
|
@ -360,8 +368,8 @@ fn arg_eq(
|
|||
right_diff: &ObjInsDiff,
|
||||
) -> bool {
|
||||
return match left {
|
||||
ObjInsArg::PpcArg(l) => match right {
|
||||
ObjInsArg::PpcArg(r) => format!("{l}") == format!("{r}"),
|
||||
ObjInsArg::Arg(l) | ObjInsArg::ArgWithBase(l) => match right {
|
||||
ObjInsArg::Arg(r) | ObjInsArg::ArgWithBase(r) => l == r,
|
||||
_ => false,
|
||||
},
|
||||
ObjInsArg::Reloc => {
|
||||
|
@ -380,9 +388,6 @@ fn arg_eq(
|
|||
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(_) => {
|
||||
// Compare dest instruction idx after diffing
|
||||
left_diff.branch_to.as_ref().map(|b| b.ins_idx)
|
||||
|
@ -436,9 +441,8 @@ fn compare_ins(
|
|||
state.diff_count += 1;
|
||||
}
|
||||
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::MipsArg(str) | ObjInsArg::MipsArgWithBase(str) => str.clone(),
|
||||
ObjInsArg::BranchOffset(arg) => format!("{arg}"),
|
||||
};
|
||||
let a_diff = if let Some(idx) = state.left_args_idx.get(&a_str) {
|
||||
|
@ -450,9 +454,8 @@ fn compare_ins(
|
|||
ObjInsArgDiff { idx }
|
||||
};
|
||||
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::MipsArg(str) | ObjInsArg::MipsArgWithBase(str) => str.clone(),
|
||||
ObjInsArg::BranchOffset(arg) => format!("{arg}"),
|
||||
};
|
||||
let b_diff = if let Some(idx) = state.right_args_idx.get(&b_str) {
|
|
@ -4,7 +4,7 @@ use std::{
|
|||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use anyhow::{ensure, Result};
|
||||
use similar::{capture_diff_slices_deadline, Algorithm};
|
||||
|
||||
use crate::{
|
||||
|
@ -177,15 +177,14 @@ pub fn diff_data_similar(
|
|||
|
||||
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);
|
||||
if matrix_size > 1_000_000_000 {
|
||||
bail!(
|
||||
"Data section {} too large for Levenshtein diff ({} * {} = {})",
|
||||
left.name,
|
||||
left.data.len(),
|
||||
right.data.len(),
|
||||
matrix_size
|
||||
);
|
||||
}
|
||||
ensure!(
|
||||
matrix_size < 1_000_000_000,
|
||||
"Data section {} too large for Levenshtein diff ({} * {} = {})",
|
||||
left.name,
|
||||
left.data.len(),
|
||||
right.data.len(),
|
||||
matrix_size
|
||||
);
|
||||
|
||||
let edit_ops = editops_find(&left.data, &right.data);
|
||||
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 anyhow::{anyhow, bail, Context, Result};
|
||||
use anyhow::{anyhow, bail, ensure, Context, Result};
|
||||
use byteorder::{BigEndian, ReadBytesExt};
|
||||
use cwdemangle::demangle;
|
||||
use filetime::FileTime;
|
||||
use flagset::Flags;
|
||||
use object::{
|
||||
|
@ -53,9 +52,14 @@ fn to_obj_symbol(obj_file: &File<'_>, symbol: &Symbol<'_, '_>, addend: i64) -> R
|
|||
} else {
|
||||
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 {
|
||||
name: name.to_string(),
|
||||
demangled_name: demangle(name, &Default::default()),
|
||||
demangled_name,
|
||||
address: symbol.address(),
|
||||
section_address,
|
||||
size: symbol.size(),
|
||||
|
@ -194,16 +198,12 @@ fn relocations_by_section(
|
|||
RelocationTarget::Symbol(idx) => obj_file
|
||||
.symbol_by_index(idx)
|
||||
.context("Failed to locate relocation target symbol")?,
|
||||
_ => {
|
||||
return Err(anyhow::Error::msg(format!(
|
||||
"Unhandled relocation target: {:?}",
|
||||
reloc.target()
|
||||
)));
|
||||
}
|
||||
_ => bail!("Unhandled relocation target: {:?}", reloc.target()),
|
||||
};
|
||||
let kind = match reloc.kind() {
|
||||
RelocationKind::Absolute => ObjRelocKind::Absolute,
|
||||
RelocationKind::Elf(kind) => match arch {
|
||||
#[cfg(feature = "ppc")]
|
||||
ObjArchitecture::PowerPc => match kind {
|
||||
elf::R_PPC_ADDR16_LO => ObjRelocKind::PpcAddr16Lo,
|
||||
elf::R_PPC_ADDR16_HI => ObjRelocKind::PpcAddr16Hi,
|
||||
|
@ -211,12 +211,9 @@ fn relocations_by_section(
|
|||
elf::R_PPC_REL24 => ObjRelocKind::PpcRel24,
|
||||
elf::R_PPC_REL14 => ObjRelocKind::PpcRel14,
|
||||
elf::R_PPC_EMB_SDA21 => ObjRelocKind::PpcEmbSda21,
|
||||
_ => {
|
||||
return Err(anyhow::Error::msg(format!(
|
||||
"Unhandled PPC relocation type: {kind}"
|
||||
)))
|
||||
}
|
||||
_ => bail!("Unhandled PPC relocation type: {kind}"),
|
||||
},
|
||||
#[cfg(feature = "mips")]
|
||||
ObjArchitecture::Mips => match kind {
|
||||
elf::R_MIPS_26 => ObjRelocKind::Mips26,
|
||||
elf::R_MIPS_HI16 => ObjRelocKind::MipsHi16,
|
||||
|
@ -228,12 +225,7 @@ fn relocations_by_section(
|
|||
_ => bail!("Unhandled MIPS relocation type: {kind}"),
|
||||
},
|
||||
},
|
||||
_ => {
|
||||
return Err(anyhow::Error::msg(format!(
|
||||
"Unhandled relocation type: {:?}",
|
||||
reloc.kind()
|
||||
)))
|
||||
}
|
||||
_ => bail!("Unhandled relocation type: {:?}", reloc.kind()),
|
||||
};
|
||||
let target_section = match symbol.section() {
|
||||
SymbolSection::Common => Some(".comm".to_string()),
|
||||
|
@ -248,12 +240,16 @@ fn relocations_by_section(
|
|||
);
|
||||
match kind {
|
||||
ObjRelocKind::Absolute => addend as i64,
|
||||
#[cfg(feature = "mips")]
|
||||
ObjRelocKind::MipsHi16 => ((addend & 0x0000FFFF) << 16) as i32 as i64,
|
||||
#[cfg(feature = "mips")]
|
||||
ObjRelocKind::MipsLo16
|
||||
| ObjRelocKind::MipsGot16
|
||||
| ObjRelocKind::MipsCall16
|
||||
| ObjRelocKind::MipsGpRel16 => (addend & 0x0000FFFF) as i16 as i64,
|
||||
#[cfg(feature = "mips")]
|
||||
ObjRelocKind::MipsGpRel32 => addend as i32 as i64,
|
||||
#[cfg(feature = "mips")]
|
||||
ObjRelocKind::Mips26 => ((addend & 0x03FFFFFF) << 2) as i64,
|
||||
_ => bail!("Unsupported implicit relocation {kind:?}"),
|
||||
}
|
||||
|
@ -266,9 +262,7 @@ fn relocations_by_section(
|
|||
to_obj_symbol(obj_file, &symbol, addend)
|
||||
}
|
||||
SymbolKind::Section => {
|
||||
if addend < 0 {
|
||||
return Err(anyhow::Error::msg(format!("Negative addend in reloc: {addend}")));
|
||||
}
|
||||
ensure!(addend >= 0, "Negative addend in reloc: {addend}");
|
||||
find_section_symbol(obj_file, &symbol, addend as u64)
|
||||
}
|
||||
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+
|
||||
let dwarf_cow = gimli::Dwarf::load(|id| {
|
||||
Ok::<_, gimli::Error>(
|
||||
obj_file
|
||||
.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 dwarf = dwarf_cow.borrow(|section| gimli::EndianSlice::new(section, endian));
|
||||
let mut iter = dwarf.units();
|
||||
while let Some(header) = iter.next()? {
|
||||
let unit = dwarf.unit(header)?;
|
||||
if let Some(program) = unit.line_program.clone() {
|
||||
let mut rows = program.rows();
|
||||
while let Some((_header, row)) = rows.next_row()? {
|
||||
if let Some(line) = row.line() {
|
||||
map.insert(row.address(), line.get());
|
||||
#[cfg(feature = "dwarf")]
|
||||
{
|
||||
let dwarf_cow = gimli::Dwarf::load(|id| {
|
||||
Ok::<_, gimli::Error>(
|
||||
obj_file
|
||||
.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 dwarf = dwarf_cow.borrow(|section| gimli::EndianSlice::new(section, endian));
|
||||
let mut iter = dwarf.units();
|
||||
while let Some(header) = iter.next()? {
|
||||
let unit = dwarf.unit(header)?;
|
||||
if let Some(program) = unit.line_program.clone() {
|
||||
let mut rows = program.rows();
|
||||
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 architecture = match obj_file.architecture() {
|
||||
#[cfg(feature = "ppc")]
|
||||
Architecture::PowerPc => ObjArchitecture::PowerPc,
|
||||
#[cfg(feature = "mips")]
|
||||
Architecture::Mips => ObjArchitecture::Mips,
|
||||
_ => {
|
||||
return Err(anyhow::Error::msg(format!(
|
||||
"Unsupported architecture: {:?}",
|
||||
obj_file.architecture()
|
||||
)))
|
||||
}
|
||||
_ => bail!("Unsupported architecture: {:?}", obj_file.architecture()),
|
||||
};
|
||||
let mut result = ObjInfo {
|
||||
architecture,
|
|
@ -5,7 +5,7 @@ use rabbitizer::{config, Abi, InstrCategory, Instruction, OperandType};
|
|||
|
||||
use crate::{
|
||||
diff::ProcessCodeResult,
|
||||
obj::{ObjIns, ObjInsArg, ObjReloc},
|
||||
obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc},
|
||||
};
|
||||
|
||||
fn configure_rabbitizer() {
|
||||
|
@ -63,23 +63,27 @@ pub fn process_code(
|
|||
args.push(ObjInsArg::Reloc);
|
||||
}
|
||||
} else {
|
||||
args.push(ObjInsArg::MipsArg(op.disassemble(&instruction, None)));
|
||||
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(
|
||||
op.disassemble(&instruction, None),
|
||||
)));
|
||||
}
|
||||
}
|
||||
OperandType::cpu_immediate_base => {
|
||||
if reloc.is_some() {
|
||||
args.push(ObjInsArg::RelocWithBase);
|
||||
} else {
|
||||
args.push(ObjInsArg::MipsArgWithBase(
|
||||
args.push(ObjInsArg::ArgWithBase(ObjInsArgValue::Opaque(
|
||||
OperandType::cpu_immediate.disassemble(&instruction, None),
|
||||
));
|
||||
)));
|
||||
}
|
||||
args.push(ObjInsArg::MipsArg(
|
||||
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(
|
||||
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;
|
||||
#[cfg(feature = "mips")]
|
||||
pub mod mips;
|
||||
#[cfg(feature = "ppc")]
|
||||
pub mod ppc;
|
||||
|
||||
use std::{collections::BTreeMap, path::PathBuf};
|
||||
use std::{collections::BTreeMap, fmt, path::PathBuf};
|
||||
|
||||
use filetime::FileTime;
|
||||
use flagset::{flags, FlagSet};
|
||||
|
||||
use crate::util::ReallySigned;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
|
||||
pub enum ObjSectionKind {
|
||||
Code,
|
||||
|
@ -23,7 +27,8 @@ flags! {
|
|||
}
|
||||
}
|
||||
#[derive(Debug, Copy, Clone, Default)]
|
||||
pub struct ObjSymbolFlagSet(pub(crate) FlagSet<ObjSymbolFlags>);
|
||||
pub struct ObjSymbolFlagSet(pub FlagSet<ObjSymbolFlags>);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ObjSection {
|
||||
pub name: String,
|
||||
|
@ -40,11 +45,40 @@ pub struct ObjSection {
|
|||
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)]
|
||||
pub enum ObjInsArg {
|
||||
PpcArg(ppc750cl::Argument),
|
||||
MipsArg(String),
|
||||
MipsArgWithBase(String),
|
||||
Arg(ObjInsArgValue),
|
||||
ArgWithBase(ObjInsArgValue),
|
||||
Reloc,
|
||||
RelocWithBase,
|
||||
BranchOffset(i32),
|
||||
|
@ -53,29 +87,8 @@ pub enum ObjInsArg {
|
|||
impl ObjInsArg {
|
||||
pub fn loose_eq(&self, other: &ObjInsArg) -> bool {
|
||||
match (self, other) {
|
||||
(ObjInsArg::PpcArg(a), ObjInsArg::PpcArg(b)) => {
|
||||
a == 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::Arg(a), ObjInsArg::Arg(b)) => a.loose_eq(b),
|
||||
(ObjInsArg::ArgWithBase(a), ObjInsArg::ArgWithBase(b)) => a.loose_eq(b),
|
||||
(ObjInsArg::Reloc, ObjInsArg::Reloc) => true,
|
||||
(ObjInsArg::RelocWithBase, ObjInsArg::RelocWithBase) => true,
|
||||
(ObjInsArg::BranchOffset(a), ObjInsArg::BranchOffset(b)) => a == b,
|
||||
|
@ -89,6 +102,7 @@ pub struct ObjInsArgDiff {
|
|||
/// Incrementing index for coloring
|
||||
pub idx: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ObjInsBranchFrom {
|
||||
/// Source instruction indices
|
||||
|
@ -96,6 +110,7 @@ pub struct ObjInsBranchFrom {
|
|||
/// Incrementing index for coloring
|
||||
pub branch_idx: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ObjInsBranchTo {
|
||||
/// Target instruction index
|
||||
|
@ -103,6 +118,7 @@ pub struct ObjInsBranchTo {
|
|||
/// Incrementing index for coloring
|
||||
pub branch_idx: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
|
||||
pub enum ObjInsDiffKind {
|
||||
#[default]
|
||||
|
@ -113,6 +129,7 @@ pub enum ObjInsDiffKind {
|
|||
Delete,
|
||||
Insert,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ObjIns {
|
||||
pub address: u32,
|
||||
|
@ -127,6 +144,7 @@ pub struct ObjIns {
|
|||
/// Original (unsimplified) instruction
|
||||
pub orig: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct ObjInsDiff {
|
||||
pub ins: Option<ObjIns>,
|
||||
|
@ -139,6 +157,7 @@ pub struct ObjInsDiff {
|
|||
/// Arg diffs
|
||||
pub arg_diff: Vec<Option<ObjInsArgDiff>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
|
||||
pub enum ObjDataDiffKind {
|
||||
#[default]
|
||||
|
@ -147,6 +166,7 @@ pub enum ObjDataDiffKind {
|
|||
Delete,
|
||||
Insert,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct ObjDataDiff {
|
||||
pub data: Vec<u8>,
|
||||
|
@ -154,6 +174,7 @@ pub struct ObjDataDiff {
|
|||
pub len: usize,
|
||||
pub symbol: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ObjSymbol {
|
||||
pub name: String,
|
||||
|
@ -170,11 +191,15 @@ pub struct ObjSymbol {
|
|||
pub instructions: Vec<ObjInsDiff>,
|
||||
pub match_percent: Option<f32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum ObjArchitecture {
|
||||
#[cfg(feature = "ppc")]
|
||||
PowerPc,
|
||||
#[cfg(feature = "mips")]
|
||||
Mips,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ObjInfo {
|
||||
pub architecture: ObjArchitecture,
|
||||
|
@ -184,27 +209,46 @@ pub struct ObjInfo {
|
|||
pub common: Vec<ObjSymbol>,
|
||||
pub line_info: Option<BTreeMap<u64, u64>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
|
||||
pub enum ObjRelocKind {
|
||||
Absolute,
|
||||
#[cfg(feature = "ppc")]
|
||||
PpcAddr16Hi,
|
||||
#[cfg(feature = "ppc")]
|
||||
PpcAddr16Ha,
|
||||
#[cfg(feature = "ppc")]
|
||||
PpcAddr16Lo,
|
||||
// #[cfg(feature = "ppc")]
|
||||
// PpcAddr32,
|
||||
// #[cfg(feature = "ppc")]
|
||||
// PpcRel32,
|
||||
// #[cfg(feature = "ppc")]
|
||||
// PpcAddr24,
|
||||
#[cfg(feature = "ppc")]
|
||||
PpcRel24,
|
||||
// #[cfg(feature = "ppc")]
|
||||
// PpcAddr14,
|
||||
#[cfg(feature = "ppc")]
|
||||
PpcRel14,
|
||||
#[cfg(feature = "ppc")]
|
||||
PpcEmbSda21,
|
||||
#[cfg(feature = "mips")]
|
||||
Mips26,
|
||||
#[cfg(feature = "mips")]
|
||||
MipsHi16,
|
||||
#[cfg(feature = "mips")]
|
||||
MipsLo16,
|
||||
#[cfg(feature = "mips")]
|
||||
MipsGot16,
|
||||
#[cfg(feature = "mips")]
|
||||
MipsCall16,
|
||||
#[cfg(feature = "mips")]
|
||||
MipsGpRel16,
|
||||
#[cfg(feature = "mips")]
|
||||
MipsGpRel32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ObjReloc {
|
||||
pub kind: ObjRelocKind,
|
|
@ -5,20 +5,26 @@ use ppc750cl::{disasm_iter, Argument, SimplifiedIns};
|
|||
|
||||
use crate::{
|
||||
diff::ProcessCodeResult,
|
||||
obj::{ObjIns, ObjInsArg, ObjReloc, ObjRelocKind},
|
||||
obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjRelocKind},
|
||||
};
|
||||
|
||||
// Relative relocation, can be Simm or BranchOffset
|
||||
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
|
||||
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(
|
||||
data: &[u8],
|
||||
|
@ -48,8 +54,13 @@ pub fn process_code(
|
|||
.args
|
||||
.iter()
|
||||
.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),
|
||||
_ => ObjInsArg::PpcArg(a.clone()),
|
||||
_ => ObjInsArg::Arg(ObjInsArgValue::Opaque(a.to_string())),
|
||||
})
|
||||
.collect();
|
||||
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 globset::{Glob, GlobSet};
|
||||
use notify::{RecursiveMode, Watcher};
|
||||
use objdiff_core::diff::DiffAlg;
|
||||
use time::UtcOffset;
|
||||
|
||||
use crate::{
|
||||
app_config::{deserialize_config, AppConfigVersion},
|
||||
config::{build_globset, load_project_config, ProjectObject, ProjectObjectNode, ScratchConfig},
|
||||
diff::DiffAlg,
|
||||
jobs::{
|
||||
objdiff::{start_build, ObjDiffConfig},
|
||||
Job, JobQueue, JobResult, JobStatus,
|
|
@ -4,7 +4,7 @@ use std::{
|
|||
path::{Component, Path, PathBuf},
|
||||
};
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use anyhow::{ensure, Result};
|
||||
use filetime::FileTime;
|
||||
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 = semver::Version::parse(version_str).unwrap();
|
||||
let version_req = semver::VersionReq::parse(&format!(">={min_version}"))?;
|
||||
if !version_req.matches(&version) {
|
||||
bail!("Project requires objdiff version {} or higher", min_version);
|
||||
}
|
||||
ensure!(
|
||||
version_req.matches(&version),
|
||||
"Project requires objdiff version {min_version} or higher"
|
||||
);
|
||||
}
|
||||
config.custom_make = project_config.custom_make;
|
||||
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 objdiff_core::{
|
||||
diff::{diff_objs, DiffAlg, DiffObjConfig},
|
||||
obj::{elf, ObjInfo},
|
||||
};
|
||||
use time::OffsetDateTime;
|
||||
|
||||
use crate::{
|
||||
app::{AppConfig, ObjectConfig},
|
||||
diff::{diff_objs, DiffAlg, DiffObjConfig},
|
||||
jobs::{start_job, update_status, Job, JobContext, JobResult, JobState},
|
||||
obj::{elf, ObjInfo},
|
||||
};
|
||||
|
||||
pub struct BuildStatus {
|
||||
|
@ -90,60 +92,61 @@ pub(crate) fn run_make(config: &BuildConfig, arg: &Path) -> BuildStatus {
|
|||
..Default::default()
|
||||
};
|
||||
};
|
||||
match (|| -> 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(),
|
||||
})
|
||||
})() {
|
||||
match run_make_cmd(config, cwd, arg) {
|
||||
Ok(status) => status,
|
||||
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(
|
||||
context: &JobContext,
|
||||
cancel: Receiver<()>,
|
|
@ -1,13 +1,21 @@
|
|||
#![warn(clippy::all, rust_2018_idioms)]
|
||||
#![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::{
|
||||
path::PathBuf,
|
||||
rc::Rc,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use anyhow::{Error, Result};
|
||||
use anyhow::{ensure, Result};
|
||||
use cfg_if::cfg_if;
|
||||
use time::UtcOffset;
|
||||
|
||||
|
@ -17,12 +25,8 @@ fn load_icon() -> Result<egui::IconData> {
|
|||
let mut reader = decoder.read_info()?;
|
||||
let mut buf = vec![0; reader.output_buffer_size()];
|
||||
let info = reader.next_frame(&mut buf)?;
|
||||
if info.bit_depth != png::BitDepth::Eight {
|
||||
return Err(Error::msg("Invalid bit depth"));
|
||||
}
|
||||
if info.color_type != png::ColorType::Rgba {
|
||||
return Err(Error::msg("Invalid color type"));
|
||||
}
|
||||
ensure!(info.bit_depth == png::BitDepth::Eight);
|
||||
ensure!(info.color_type == png::ColorType::Rgba);
|
||||
buf.truncate(info.buffer_size());
|
||||
Ok(egui::IconData { rgba: buf, width: info.width, height: info.height })
|
||||
}
|
||||
|
@ -57,7 +61,7 @@ fn main() {
|
|||
eframe::run_native(
|
||||
"objdiff",
|
||||
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");
|
||||
|
|
@ -14,12 +14,12 @@ use egui::{
|
|||
SelectableLabel, TextFormat, Widget, WidgetText,
|
||||
};
|
||||
use globset::Glob;
|
||||
use objdiff_core::diff::DiffAlg;
|
||||
use self_update::cargo_crate_version;
|
||||
|
||||
use crate::{
|
||||
app::{AppConfig, AppConfigRef, ObjectConfig},
|
||||
config::{ProjectObject, ProjectObjectNode},
|
||||
diff::DiffAlg,
|
||||
jobs::{
|
||||
check_update::{start_check_update, CheckUpdateResult},
|
||||
update::start_update,
|
||||
|
@ -605,7 +605,7 @@ fn split_obj_config_ui(
|
|||
ui.separator();
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
subheading(ui, "Custom make program", appearance);
|
||||
subheading(ui, "Build program", appearance);
|
||||
ui.link(HELP_ICON).on_hover_ui(|ui| {
|
||||
let mut job = LayoutJob::default();
|
||||
job.append("By default, objdiff will build with ", 0.0, text_format.clone());
|
||||
|
@ -630,7 +630,7 @@ fn split_obj_config_ui(
|
|||
if ui
|
||||
.add_enabled(
|
||||
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)
|
||||
.changed()
|
|
@ -1,16 +1,14 @@
|
|||
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 objdiff_core::obj::{ObjDataDiff, ObjDataDiffKind, ObjInfo, ObjSection};
|
||||
use time::format_description;
|
||||
|
||||
use crate::{
|
||||
obj::{ObjDataDiff, ObjDataDiffKind, ObjInfo, ObjSection},
|
||||
views::{
|
||||
appearance::Appearance,
|
||||
symbol_diff::{DiffViewState, SymbolReference, View},
|
||||
write_text,
|
||||
},
|
||||
use crate::views::{
|
||||
appearance::Appearance,
|
||||
symbol_diff::{DiffViewState, SymbolReference, View},
|
||||
write_text,
|
||||
};
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
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))
|
||||
// .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();
|
||||
|
||||
// Table
|
||||
ui.style_mut().interaction.selectable_labels = false;
|
||||
let available_height = ui.available_height();
|
||||
let table = TableBuilder::new(ui)
|
||||
.striped(false)
|
|
@ -3,22 +3,20 @@ use std::{
|
|||
default::Default,
|
||||
};
|
||||
|
||||
use cwdemangle::demangle;
|
||||
use egui::{text::LayoutJob, Align, Color32, Label, Layout, RichText, Sense, TextFormat, Vec2};
|
||||
use egui::{
|
||||
text::LayoutJob, Align, Color32, Label, Layout, RichText, Sense, TextFormat, Vec2, Widget,
|
||||
};
|
||||
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 crate::{
|
||||
obj::{
|
||||
ObjInfo, ObjIns, ObjInsArg, ObjInsArgDiff, ObjInsDiff, ObjInsDiffKind, ObjReloc,
|
||||
ObjRelocKind, ObjSymbol,
|
||||
},
|
||||
views::{
|
||||
appearance::Appearance,
|
||||
symbol_diff::{match_color_for_symbol, DiffViewState, SymbolReference, View},
|
||||
write_text,
|
||||
},
|
||||
use crate::views::{
|
||||
appearance::Appearance,
|
||||
symbol_diff::{match_color_for_symbol, DiffViewState, SymbolReference, View},
|
||||
write_text,
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
|
@ -162,11 +160,9 @@ fn write_ins(
|
|||
} else {
|
||||
Color32::TRANSPARENT
|
||||
});
|
||||
if ui
|
||||
.add(Label::new(op_label).sense(Sense::click()))
|
||||
.context_menu(|ui| ins_context_menu(ui, ins))
|
||||
.clicked()
|
||||
{
|
||||
let response = Label::new(op_label).sense(Sense::click()).ui(ui);
|
||||
response.context_menu(|ui| ins_context_menu(ui, ins));
|
||||
if response.clicked() {
|
||||
if highlighted_op {
|
||||
ins_view_state.highlight = HighlightKind::None;
|
||||
} else {
|
||||
|
@ -215,19 +211,14 @@ fn write_ins(
|
|||
};
|
||||
let mut new_writing_offset = false;
|
||||
match arg {
|
||||
ObjInsArg::PpcArg(arg) => match arg {
|
||||
Argument::Offset(val) => {
|
||||
job.append(&format!("{val}"), 0.0, text_format);
|
||||
write_text("(", base_color, &mut job, appearance.code_font.clone());
|
||||
new_writing_offset = true;
|
||||
}
|
||||
Argument::Uimm(_) | Argument::Simm(_) => {
|
||||
job.append(&format!("{arg}"), 0.0, text_format);
|
||||
}
|
||||
_ => {
|
||||
job.append(&format!("{arg}"), 0.0, text_format);
|
||||
}
|
||||
},
|
||||
ObjInsArg::Arg(arg) => {
|
||||
job.append(&arg.to_string(), 0.0, text_format);
|
||||
}
|
||||
ObjInsArg::ArgWithBase(arg) => {
|
||||
job.append(&arg.to_string(), 0.0, text_format);
|
||||
write_text("(", base_color, &mut job, appearance.code_font.clone());
|
||||
new_writing_offset = true;
|
||||
}
|
||||
ObjInsArg::Reloc => {
|
||||
write_reloc(
|
||||
ins.reloc.as_ref().unwrap(),
|
||||
|
@ -248,14 +239,6 @@ fn write_ins(
|
|||
write_text("(", base_color, &mut job, appearance.code_font.clone());
|
||||
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) => {
|
||||
let addr = offset + ins.address as i32 - base_addr as i32;
|
||||
job.append(&format!("{addr:x}"), 0.0, text_format);
|
||||
|
@ -264,12 +247,14 @@ fn write_ins(
|
|||
if writing_offset {
|
||||
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;
|
||||
if ui
|
||||
.add(Label::new(job).sense(Sense::click()))
|
||||
.context_menu(|ui| ins_context_menu(ui, ins))
|
||||
.clicked()
|
||||
{
|
||||
let response = Label::new(job).sense(Sense::click()).ui(ui);
|
||||
response.context_menu(|ui| ins_context_menu(ui, ins));
|
||||
if response.clicked() {
|
||||
if highlighted_arg {
|
||||
ins_view_state.highlight = HighlightKind::None;
|
||||
} 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 {
|
||||
if let ObjInsArg::PpcArg(arg) = arg {
|
||||
if let ObjInsArg::Arg(arg) | ObjInsArg::ArgWithBase(arg) = arg {
|
||||
match arg {
|
||||
Argument::Uimm(v) => {
|
||||
ui.label(format!("{} == {}", v, v.0));
|
||||
ObjInsArgValue::Signed(v) => {
|
||||
ui.label(format!("{arg} == {v}"));
|
||||
}
|
||||
Argument::Simm(v) => {
|
||||
ui.label(format!("{} == {}", v, v.0));
|
||||
}
|
||||
Argument::Offset(v) => {
|
||||
ui.label(format!("{} == {}", v, v.0));
|
||||
ObjInsArgValue::Unsigned(v) => {
|
||||
ui.label(format!("{arg} == {v}"));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
@ -341,35 +323,25 @@ fn ins_context_menu(ui: &mut egui::Ui, ins: &ObjIns) {
|
|||
// if ui.button("Copy hex").clicked() {}
|
||||
|
||||
for arg in &ins.args {
|
||||
if let ObjInsArg::PpcArg(arg) = arg {
|
||||
if let ObjInsArg::Arg(arg) | ObjInsArg::ArgWithBase(arg) = arg {
|
||||
match arg {
|
||||
Argument::Uimm(v) => {
|
||||
if ui.button(format!("Copy \"{v}\"")).clicked() {
|
||||
ui.output_mut(|output| output.copied_text = format!("{v}"));
|
||||
ObjInsArgValue::Signed(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.0)).clicked() {
|
||||
ui.output_mut(|output| output.copied_text = format!("{}", v.0));
|
||||
if ui.button(format!("Copy \"{v}\"")).clicked() {
|
||||
ui.output_mut(|output| output.copied_text = v.to_string());
|
||||
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() {
|
||||
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();
|
||||
}
|
||||
}
|
||||
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.output_mut(|output| output.copied_text = v.to_string());
|
||||
ui.close_menu();
|
||||
}
|
||||
}
|
||||
|
@ -451,11 +423,9 @@ fn asm_row_ui(
|
|||
},
|
||||
..Default::default()
|
||||
});
|
||||
if ui
|
||||
.add(Label::new(job).sense(Sense::click()))
|
||||
.context_menu(|ui| ins_context_menu(ui, ins))
|
||||
.clicked()
|
||||
{
|
||||
let response = Label::new(job).sense(Sense::click()).selectable(false).ui(ui);
|
||||
response.context_menu(|ui| ins_context_menu(ui, ins));
|
||||
if response.clicked() {
|
||||
if addr_highlight {
|
||||
ins_view_state.highlight = HighlightKind::None;
|
||||
} else {
|
||||
|
@ -484,7 +454,7 @@ fn asm_row_ui(
|
|||
..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);
|
||||
if let Some(branch) = &ins_diff.branch_to {
|
||||
let mut job = LayoutJob::default();
|
||||
|
@ -494,7 +464,7 @@ fn asm_row_ui(
|
|||
&mut job,
|
||||
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);
|
||||
});
|
||||
if let Some(ins) = &ins_diff.ins {
|
||||
response
|
||||
.on_hover_ui_at_pointer(|ui| {
|
||||
ins_hover_ui(ui, ins, appearance);
|
||||
})
|
||||
.context_menu(|ui| {
|
||||
ins_context_menu(ui, ins);
|
||||
});
|
||||
response.on_hover_ui_at_pointer(|ui| 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 = demangled.as_deref().unwrap_or(&selected_symbol.symbol_name);
|
||||
let name = selected_symbol
|
||||
.demangled_symbol_name
|
||||
.as_deref()
|
||||
.unwrap_or(&selected_symbol.symbol_name);
|
||||
let mut job = LayoutJob::simple(
|
||||
name.to_string(),
|
||||
appearance.code_font.clone(),
|
||||
|
@ -681,6 +648,7 @@ pub fn function_diff_ui(ui: &mut egui::Ui, state: &mut DiffViewState, appearance
|
|||
ui.separator();
|
||||
|
||||
// Table
|
||||
ui.style_mut().interaction.selectable_labels = false;
|
||||
let available_height = ui.available_height();
|
||||
let table = TableBuilder::new(ui)
|
||||
.striped(false)
|
|
@ -5,6 +5,7 @@ use egui::{
|
|||
SelectableLabel, TextEdit, Ui, Vec2, Widget,
|
||||
};
|
||||
use egui_extras::{Size, StripBuilder};
|
||||
use objdiff_core::obj::{ObjInfo, ObjSection, ObjSectionKind, ObjSymbol, ObjSymbolFlags};
|
||||
|
||||
use crate::{
|
||||
app::AppConfigRef,
|
||||
|
@ -13,12 +14,12 @@ use crate::{
|
|||
objdiff::{BuildStatus, ObjDiffResult},
|
||||
Job, JobQueue, JobResult,
|
||||
},
|
||||
obj::{ObjInfo, ObjSection, ObjSectionKind, ObjSymbol, ObjSymbolFlags},
|
||||
views::{appearance::Appearance, function_diff::FunctionViewState, write_text},
|
||||
};
|
||||
|
||||
pub struct SymbolReference {
|
||||
pub symbol_name: String,
|
||||
pub demangled_symbol_name: Option<String>,
|
||||
pub section_name: String,
|
||||
}
|
||||
|
||||
|
@ -210,19 +211,21 @@ fn symbol_ui(
|
|||
write_text(name, appearance.highlight_color, &mut job, appearance.code_font.clone());
|
||||
let response = SelectableLabel::new(selected, job)
|
||||
.ui(ui)
|
||||
.context_menu(|ui| symbol_context_menu_ui(ui, symbol))
|
||||
.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 let Some(section) = section {
|
||||
if section.kind == ObjSectionKind::Code {
|
||||
state.selected_symbol = Some(SymbolReference {
|
||||
symbol_name: symbol.name.clone(),
|
||||
demangled_symbol_name: symbol.demangled_name.clone(),
|
||||
section_name: section.name.clone(),
|
||||
});
|
||||
ret = Some(View::FunctionDiff);
|
||||
} else if section.kind == ObjSectionKind::Data {
|
||||
state.selected_symbol = Some(SymbolReference {
|
||||
symbol_name: section.name.clone(),
|
||||
demangled_symbol_name: None,
|
||||
section_name: section.name.clone(),
|
||||
});
|
||||
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