mirror of
https://github.com/encounter/objdiff.git
synced 2025-12-19 09:55:28 +00:00
Compare commits
7 Commits
v2.0.0-bet
...
v2.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
| 2cc10b0d06 | |||
| 8091941448 | |||
| de74dfdba7 | |||
| 177bd5e895 | |||
| e1ccee1e73 | |||
| 952b6a63c3 | |||
|
|
09cc9952df |
6
Cargo.lock
generated
6
Cargo.lock
generated
@@ -2798,7 +2798,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "objdiff-cli"
|
name = "objdiff-cli"
|
||||||
version = "2.0.0-beta.1"
|
version = "2.0.0-beta.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"argp",
|
"argp",
|
||||||
@@ -2817,7 +2817,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "objdiff-core"
|
name = "objdiff-core"
|
||||||
version = "2.0.0-beta.1"
|
version = "2.0.0-beta.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"arm-attr",
|
"arm-attr",
|
||||||
@@ -2848,7 +2848,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "objdiff-gui"
|
name = "objdiff-gui"
|
||||||
version = "2.0.0-beta.1"
|
version = "2.0.0-beta.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "objdiff-cli"
|
name = "objdiff-cli"
|
||||||
version = "2.0.0-beta.1"
|
version = "2.0.0-beta.2"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.70"
|
rust-version = "1.70"
|
||||||
authors = ["Luke Street <luke@street.dev>"]
|
authors = ["Luke Street <luke@street.dev>"]
|
||||||
|
|||||||
@@ -750,8 +750,11 @@ impl FunctionDiffUi {
|
|||||||
base_color = COLOR_ROTATION[diff.idx % COLOR_ROTATION.len()]
|
base_color = COLOR_ROTATION[diff.idx % COLOR_ROTATION.len()]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DiffText::BranchDest(addr) => {
|
DiffText::BranchDest(addr, diff) => {
|
||||||
label_text = format!("{addr:x}");
|
label_text = format!("{addr:x}");
|
||||||
|
if let Some(diff) = diff {
|
||||||
|
base_color = COLOR_ROTATION[diff.idx % COLOR_ROTATION.len()]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
DiffText::Symbol(sym) => {
|
DiffText::Symbol(sym) => {
|
||||||
let name = sym.demangled_name.as_ref().unwrap_or(&sym.name);
|
let name = sym.demangled_name.as_ref().unwrap_or(&sym.name);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "objdiff-core"
|
name = "objdiff-core"
|
||||||
version = "2.0.0-beta.1"
|
version = "2.0.0-beta.2"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.70"
|
rust-version = "1.70"
|
||||||
authors = ["Luke Street <luke@street.dev>"]
|
authors = ["Luke Street <luke@street.dev>"]
|
||||||
|
|||||||
@@ -227,6 +227,7 @@ impl ObjArch for ObjArchArm {
|
|||||||
|
|
||||||
fn implcit_addend(
|
fn implcit_addend(
|
||||||
&self,
|
&self,
|
||||||
|
_file: &File<'_>,
|
||||||
section: &ObjSection,
|
section: &ObjSection,
|
||||||
address: u64,
|
address: u64,
|
||||||
reloc: &Relocation,
|
reloc: &Relocation,
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
use std::{borrow::Cow, collections::BTreeMap, sync::Mutex};
|
use std::{borrow::Cow, collections::BTreeMap, sync::Mutex};
|
||||||
|
|
||||||
use anyhow::{anyhow, bail, Result};
|
use anyhow::{anyhow, bail, Result};
|
||||||
use object::{elf, Endian, Endianness, File, FileFlags, Object, Relocation, RelocationFlags};
|
use object::{
|
||||||
|
elf, Endian, Endianness, File, FileFlags, Object, ObjectSection, ObjectSymbol, Relocation,
|
||||||
|
RelocationFlags, RelocationTarget,
|
||||||
|
};
|
||||||
use rabbitizer::{config, Abi, InstrCategory, Instruction, OperandType};
|
use rabbitizer::{config, Abi, InstrCategory, Instruction, OperandType};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -22,6 +25,7 @@ pub struct ObjArchMips {
|
|||||||
pub endianness: Endianness,
|
pub endianness: Endianness,
|
||||||
pub abi: Abi,
|
pub abi: Abi,
|
||||||
pub instr_category: InstrCategory,
|
pub instr_category: InstrCategory,
|
||||||
|
pub ri_gp_value: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
const EF_MIPS_ABI: u32 = 0x0000F000;
|
const EF_MIPS_ABI: u32 = 0x0000F000;
|
||||||
@@ -56,7 +60,19 @@ impl ObjArchMips {
|
|||||||
}
|
}
|
||||||
_ => bail!("Unsupported MIPS file flags"),
|
_ => bail!("Unsupported MIPS file flags"),
|
||||||
}
|
}
|
||||||
Ok(Self { endianness: object.endianness(), abi, instr_category })
|
|
||||||
|
// Parse the ri_gp_value stored in .reginfo to be able to correctly
|
||||||
|
// calculate R_MIPS_GPREL16 relocations later. The value is stored
|
||||||
|
// 0x14 bytes into .reginfo (on 32 bit platforms)
|
||||||
|
let ri_gp_value = object
|
||||||
|
.section_by_name(".reginfo")
|
||||||
|
.and_then(|section| section.data().ok())
|
||||||
|
.and_then(|data| data.get(0x14..0x18))
|
||||||
|
.and_then(|s| s.try_into().ok())
|
||||||
|
.map(|bytes| object.endianness().read_i32_bytes(bytes))
|
||||||
|
.unwrap_or(0);
|
||||||
|
|
||||||
|
Ok(Self { endianness: object.endianness(), abi, instr_category, ri_gp_value })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,6 +195,7 @@ impl ObjArch for ObjArchMips {
|
|||||||
|
|
||||||
fn implcit_addend(
|
fn implcit_addend(
|
||||||
&self,
|
&self,
|
||||||
|
file: &File<'_>,
|
||||||
section: &ObjSection,
|
section: &ObjSection,
|
||||||
address: u64,
|
address: u64,
|
||||||
reloc: &Relocation,
|
reloc: &Relocation,
|
||||||
@@ -191,9 +208,22 @@ impl ObjArch for ObjArchMips {
|
|||||||
((addend & 0x0000FFFF) << 16) as i32 as i64
|
((addend & 0x0000FFFF) << 16) as i32 as i64
|
||||||
}
|
}
|
||||||
RelocationFlags::Elf {
|
RelocationFlags::Elf {
|
||||||
r_type:
|
r_type: elf::R_MIPS_LO16 | elf::R_MIPS_GOT16 | elf::R_MIPS_CALL16,
|
||||||
elf::R_MIPS_LO16 | elf::R_MIPS_GOT16 | elf::R_MIPS_CALL16 | elf::R_MIPS_GPREL16,
|
|
||||||
} => (addend & 0x0000FFFF) as i16 as i64,
|
} => (addend & 0x0000FFFF) as i16 as i64,
|
||||||
|
RelocationFlags::Elf { r_type: elf::R_MIPS_GPREL16 } => {
|
||||||
|
let RelocationTarget::Symbol(idx) = reloc.target() else {
|
||||||
|
bail!("Unsupported R_MIPS_GPREL16 relocation against a non-symbol");
|
||||||
|
};
|
||||||
|
let sym = file.symbol_by_index(idx)?;
|
||||||
|
|
||||||
|
// if the symbol we are relocating against is in a local section we need to add
|
||||||
|
// the ri_gp_value from .reginfo to the addend.
|
||||||
|
if sym.section().index().is_some() {
|
||||||
|
((addend & 0x0000FFFF) as i16 as i64) + self.ri_gp_value as i64
|
||||||
|
} else {
|
||||||
|
(addend & 0x0000FFFF) as i16 as i64
|
||||||
|
}
|
||||||
|
}
|
||||||
RelocationFlags::Elf { r_type: elf::R_MIPS_26 } => ((addend & 0x03FFFFFF) << 2) as i64,
|
RelocationFlags::Elf { r_type: elf::R_MIPS_26 } => ((addend & 0x03FFFFFF) << 2) as i64,
|
||||||
flags => bail!("Unsupported MIPS implicit relocation {flags:?}"),
|
flags => bail!("Unsupported MIPS implicit relocation {flags:?}"),
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use std::{borrow::Cow, collections::BTreeMap};
|
use std::{borrow::Cow, collections::BTreeMap};
|
||||||
|
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
use object::{Architecture, Object, ObjectSymbol, Relocation, RelocationFlags, Symbol};
|
use object::{Architecture, File, Object, ObjectSymbol, Relocation, RelocationFlags, Symbol};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
diff::DiffObjConfig,
|
diff::DiffObjConfig,
|
||||||
@@ -28,8 +28,13 @@ pub trait ObjArch: Send + Sync {
|
|||||||
config: &DiffObjConfig,
|
config: &DiffObjConfig,
|
||||||
) -> Result<ProcessCodeResult>;
|
) -> Result<ProcessCodeResult>;
|
||||||
|
|
||||||
fn implcit_addend(&self, section: &ObjSection, address: u64, reloc: &Relocation)
|
fn implcit_addend(
|
||||||
-> Result<i64>;
|
&self,
|
||||||
|
file: &File<'_>,
|
||||||
|
section: &ObjSection,
|
||||||
|
address: u64,
|
||||||
|
reloc: &Relocation,
|
||||||
|
) -> Result<i64>;
|
||||||
|
|
||||||
fn demangle(&self, _name: &str) -> Option<String> { None }
|
fn demangle(&self, _name: &str) -> Option<String> { None }
|
||||||
|
|
||||||
|
|||||||
@@ -150,6 +150,7 @@ impl ObjArch for ObjArchPpc {
|
|||||||
|
|
||||||
fn implcit_addend(
|
fn implcit_addend(
|
||||||
&self,
|
&self,
|
||||||
|
_file: &File<'_>,
|
||||||
_section: &ObjSection,
|
_section: &ObjSection,
|
||||||
address: u64,
|
address: u64,
|
||||||
reloc: &Relocation,
|
reloc: &Relocation,
|
||||||
|
|||||||
@@ -128,6 +128,7 @@ impl ObjArch for ObjArchX86 {
|
|||||||
|
|
||||||
fn implcit_addend(
|
fn implcit_addend(
|
||||||
&self,
|
&self,
|
||||||
|
_file: &File<'_>,
|
||||||
section: &ObjSection,
|
section: &ObjSection,
|
||||||
address: u64,
|
address: u64,
|
||||||
reloc: &Relocation,
|
reloc: &Relocation,
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ pub fn diff_data_section(
|
|||||||
let right_data = &right.data[..right_max as usize];
|
let right_data = &right.data[..right_max as usize];
|
||||||
let ops =
|
let ops =
|
||||||
capture_diff_slices_deadline(Algorithm::Patience, left_data, right_data, Some(deadline));
|
capture_diff_slices_deadline(Algorithm::Patience, left_data, right_data, Some(deadline));
|
||||||
|
let match_percent = get_diff_ratio(&ops, left_data.len(), right_data.len()) * 100.0;
|
||||||
|
|
||||||
let mut left_diff = Vec::<ObjDataDiff>::new();
|
let mut left_diff = Vec::<ObjDataDiff>::new();
|
||||||
let mut right_diff = Vec::<ObjDataDiff>::new();
|
let mut right_diff = Vec::<ObjDataDiff>::new();
|
||||||
@@ -127,6 +128,13 @@ pub fn diff_data_section(
|
|||||||
diff_generic_section(left, right, left_section_diff, right_section_diff)?;
|
diff_generic_section(left, right, left_section_diff, right_section_diff)?;
|
||||||
left_section_diff.data_diff = left_diff;
|
left_section_diff.data_diff = left_diff;
|
||||||
right_section_diff.data_diff = right_diff;
|
right_section_diff.data_diff = right_diff;
|
||||||
|
// Use the highest match percent between two options:
|
||||||
|
// - Left symbols matching right symbols by name
|
||||||
|
// - Diff of the data itself
|
||||||
|
if left_section_diff.match_percent.unwrap_or(-1.0) < match_percent {
|
||||||
|
left_section_diff.match_percent = Some(match_percent);
|
||||||
|
right_section_diff.match_percent = Some(match_percent);
|
||||||
|
}
|
||||||
Ok((left_section_diff, right_section_diff))
|
Ok((left_section_diff, right_section_diff))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,3 +199,35 @@ pub fn diff_generic_section(
|
|||||||
ObjSectionDiff { symbols: vec![], data_diff: vec![], match_percent: Some(match_percent) },
|
ObjSectionDiff { symbols: vec![], data_diff: vec![], match_percent: Some(match_percent) },
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Compare the addresses and sizes of each symbol in the BSS sections.
|
||||||
|
pub fn diff_bss_section(
|
||||||
|
left: &ObjSection,
|
||||||
|
right: &ObjSection,
|
||||||
|
left_diff: &ObjSectionDiff,
|
||||||
|
right_diff: &ObjSectionDiff,
|
||||||
|
) -> Result<(ObjSectionDiff, ObjSectionDiff)> {
|
||||||
|
let deadline = Instant::now() + Duration::from_secs(5);
|
||||||
|
let left_sizes = left.symbols.iter().map(|s| (s.section_address, s.size)).collect::<Vec<_>>();
|
||||||
|
let right_sizes = right.symbols.iter().map(|s| (s.section_address, s.size)).collect::<Vec<_>>();
|
||||||
|
let ops = capture_diff_slices_deadline(
|
||||||
|
Algorithm::Patience,
|
||||||
|
&left_sizes,
|
||||||
|
&right_sizes,
|
||||||
|
Some(deadline),
|
||||||
|
);
|
||||||
|
let mut match_percent = get_diff_ratio(&ops, left_sizes.len(), right_sizes.len()) * 100.0;
|
||||||
|
|
||||||
|
// Use the highest match percent between two options:
|
||||||
|
// - Left symbols matching right symbols by name
|
||||||
|
// - Diff of the addresses and sizes of each symbol
|
||||||
|
let (generic_diff, _) = diff_generic_section(left, right, left_diff, right_diff)?;
|
||||||
|
if generic_diff.match_percent.unwrap_or(-1.0) > match_percent {
|
||||||
|
match_percent = generic_diff.match_percent.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
ObjSectionDiff { symbols: vec![], data_diff: vec![], match_percent: Some(match_percent) },
|
||||||
|
ObjSectionDiff { symbols: vec![], data_diff: vec![], match_percent: Some(match_percent) },
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ pub enum DiffText<'a> {
|
|||||||
/// Instruction argument
|
/// Instruction argument
|
||||||
Argument(&'a ObjInsArgValue, Option<&'a ObjInsArgDiff>),
|
Argument(&'a ObjInsArgValue, Option<&'a ObjInsArgDiff>),
|
||||||
/// Branch destination
|
/// Branch destination
|
||||||
BranchDest(u64),
|
BranchDest(u64, Option<&'a ObjInsArgDiff>),
|
||||||
/// Symbol name
|
/// Symbol name
|
||||||
Symbol(&'a ObjSymbol),
|
Symbol(&'a ObjSymbol),
|
||||||
/// Number of spaces
|
/// Number of spaces
|
||||||
@@ -62,12 +62,12 @@ pub fn display_diff<E>(
|
|||||||
if i == 0 {
|
if i == 0 {
|
||||||
cb(DiffText::Spacing(1))?;
|
cb(DiffText::Spacing(1))?;
|
||||||
}
|
}
|
||||||
|
let diff = ins_diff.arg_diff.get(i).and_then(|o| o.as_ref());
|
||||||
match arg {
|
match arg {
|
||||||
ObjInsArg::PlainText(s) => {
|
ObjInsArg::PlainText(s) => {
|
||||||
cb(DiffText::Basic(s))?;
|
cb(DiffText::Basic(s))?;
|
||||||
}
|
}
|
||||||
ObjInsArg::Arg(v) => {
|
ObjInsArg::Arg(v) => {
|
||||||
let diff = ins_diff.arg_diff.get(i).and_then(|o| o.as_ref());
|
|
||||||
cb(DiffText::Argument(v, diff))?;
|
cb(DiffText::Argument(v, diff))?;
|
||||||
}
|
}
|
||||||
ObjInsArg::Reloc => {
|
ObjInsArg::Reloc => {
|
||||||
@@ -75,7 +75,7 @@ pub fn display_diff<E>(
|
|||||||
}
|
}
|
||||||
ObjInsArg::BranchDest(dest) => {
|
ObjInsArg::BranchDest(dest) => {
|
||||||
if let Some(dest) = dest.checked_sub(base_addr) {
|
if let Some(dest) = dest.checked_sub(base_addr) {
|
||||||
cb(DiffText::BranchDest(dest))?;
|
cb(DiffText::BranchDest(dest, diff))?;
|
||||||
} else {
|
} else {
|
||||||
cb(DiffText::Basic("<unknown>"))?;
|
cb(DiffText::Basic("<unknown>"))?;
|
||||||
}
|
}
|
||||||
@@ -107,7 +107,9 @@ impl PartialEq<DiffText<'_>> for HighlightKind {
|
|||||||
(HighlightKind::Opcode(a), DiffText::Opcode(_, b)) => a == b,
|
(HighlightKind::Opcode(a), DiffText::Opcode(_, b)) => a == b,
|
||||||
(HighlightKind::Arg(a), DiffText::Argument(b, _)) => a.loose_eq(b),
|
(HighlightKind::Arg(a), DiffText::Argument(b, _)) => a.loose_eq(b),
|
||||||
(HighlightKind::Symbol(a), DiffText::Symbol(b)) => a == &b.name,
|
(HighlightKind::Symbol(a), DiffText::Symbol(b)) => a == &b.name,
|
||||||
(HighlightKind::Address(a), DiffText::Address(b) | DiffText::BranchDest(b)) => a == b,
|
(HighlightKind::Address(a), DiffText::Address(b) | DiffText::BranchDest(b, _)) => {
|
||||||
|
a == b
|
||||||
|
}
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -123,7 +125,7 @@ impl From<DiffText<'_>> for HighlightKind {
|
|||||||
DiffText::Opcode(_, op) => HighlightKind::Opcode(op),
|
DiffText::Opcode(_, op) => HighlightKind::Opcode(op),
|
||||||
DiffText::Argument(arg, _) => HighlightKind::Arg(arg.clone()),
|
DiffText::Argument(arg, _) => HighlightKind::Arg(arg.clone()),
|
||||||
DiffText::Symbol(sym) => HighlightKind::Symbol(sym.name.to_string()),
|
DiffText::Symbol(sym) => HighlightKind::Symbol(sym.name.to_string()),
|
||||||
DiffText::Address(addr) | DiffText::BranchDest(addr) => HighlightKind::Address(addr),
|
DiffText::Address(addr) | DiffText::BranchDest(addr, _) => HighlightKind::Address(addr),
|
||||||
_ => HighlightKind::None,
|
_ => HighlightKind::None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ use crate::{
|
|||||||
diff::{
|
diff::{
|
||||||
code::{diff_code, no_diff_code, process_code_symbol},
|
code::{diff_code, no_diff_code, process_code_symbol},
|
||||||
data::{
|
data::{
|
||||||
diff_bss_symbol, diff_data_section, diff_data_symbol, diff_generic_section,
|
diff_bss_section, diff_bss_symbol, diff_data_section, diff_data_symbol,
|
||||||
no_diff_symbol,
|
diff_generic_section, no_diff_symbol,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
obj::{ObjInfo, ObjIns, ObjSection, ObjSectionKind, ObjSymbol, SymbolRef},
|
obj::{ObjInfo, ObjIns, ObjSection, ObjSectionKind, ObjSymbol, SymbolRef},
|
||||||
@@ -483,7 +483,7 @@ pub fn diff_objs(
|
|||||||
let left_section = &left_obj.sections[left_section_idx];
|
let left_section = &left_obj.sections[left_section_idx];
|
||||||
let right_section = &right_obj.sections[right_section_idx];
|
let right_section = &right_obj.sections[right_section_idx];
|
||||||
match section_kind {
|
match section_kind {
|
||||||
ObjSectionKind::Code | ObjSectionKind::Bss => {
|
ObjSectionKind::Code => {
|
||||||
let left_section_diff = left_out.section_diff(left_section_idx);
|
let left_section_diff = left_out.section_diff(left_section_idx);
|
||||||
let right_section_diff = right_out.section_diff(right_section_idx);
|
let right_section_diff = right_out.section_diff(right_section_idx);
|
||||||
let (left_diff, right_diff) = diff_generic_section(
|
let (left_diff, right_diff) = diff_generic_section(
|
||||||
@@ -507,6 +507,18 @@ pub fn diff_objs(
|
|||||||
left_out.section_diff_mut(left_section_idx).merge(left_diff);
|
left_out.section_diff_mut(left_section_idx).merge(left_diff);
|
||||||
right_out.section_diff_mut(right_section_idx).merge(right_diff);
|
right_out.section_diff_mut(right_section_idx).merge(right_diff);
|
||||||
}
|
}
|
||||||
|
ObjSectionKind::Bss => {
|
||||||
|
let left_section_diff = left_out.section_diff(left_section_idx);
|
||||||
|
let right_section_diff = right_out.section_diff(right_section_idx);
|
||||||
|
let (left_diff, right_diff) = diff_bss_section(
|
||||||
|
left_section,
|
||||||
|
right_section,
|
||||||
|
left_section_diff,
|
||||||
|
right_section_diff,
|
||||||
|
)?;
|
||||||
|
left_out.section_diff_mut(left_section_idx).merge(left_diff);
|
||||||
|
right_out.section_diff_mut(right_section_idx).merge(right_diff);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -546,8 +558,8 @@ fn matching_symbols(
|
|||||||
for (symbol_idx, symbol) in section.symbols.iter().enumerate() {
|
for (symbol_idx, symbol) in section.symbols.iter().enumerate() {
|
||||||
let symbol_match = SymbolMatch {
|
let symbol_match = SymbolMatch {
|
||||||
left: Some(SymbolRef { section_idx, symbol_idx }),
|
left: Some(SymbolRef { section_idx, symbol_idx }),
|
||||||
right: find_symbol(right, symbol, section),
|
right: find_symbol(right, symbol, section, Some(&right_used)),
|
||||||
prev: find_symbol(prev, symbol, section),
|
prev: find_symbol(prev, symbol, section, None),
|
||||||
section_kind: section.kind,
|
section_kind: section.kind,
|
||||||
};
|
};
|
||||||
matches.push(symbol_match);
|
matches.push(symbol_match);
|
||||||
@@ -579,7 +591,7 @@ fn matching_symbols(
|
|||||||
matches.push(SymbolMatch {
|
matches.push(SymbolMatch {
|
||||||
left: None,
|
left: None,
|
||||||
right: Some(symbol_ref),
|
right: Some(symbol_ref),
|
||||||
prev: find_symbol(prev, symbol, section),
|
prev: find_symbol(prev, symbol, section, None),
|
||||||
section_kind: section.kind,
|
section_kind: section.kind,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -600,10 +612,25 @@ fn matching_symbols(
|
|||||||
Ok(matches)
|
Ok(matches)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn unmatched_symbols<'section, 'used>(
|
||||||
|
section: &'section ObjSection,
|
||||||
|
section_idx: usize,
|
||||||
|
used: Option<&'used HashSet<SymbolRef>>,
|
||||||
|
) -> impl Iterator<Item = (usize, &'section ObjSymbol)> + 'used
|
||||||
|
where
|
||||||
|
'section: 'used,
|
||||||
|
{
|
||||||
|
section.symbols.iter().enumerate().filter(move |&(symbol_idx, _)| {
|
||||||
|
// Skip symbols that have already been matched
|
||||||
|
!used.map(|u| u.contains(&SymbolRef { section_idx, symbol_idx })).unwrap_or(false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn find_symbol(
|
fn find_symbol(
|
||||||
obj: Option<&ObjInfo>,
|
obj: Option<&ObjInfo>,
|
||||||
in_symbol: &ObjSymbol,
|
in_symbol: &ObjSymbol,
|
||||||
in_section: &ObjSection,
|
in_section: &ObjSection,
|
||||||
|
used: Option<&HashSet<SymbolRef>>,
|
||||||
) -> Option<SymbolRef> {
|
) -> Option<SymbolRef> {
|
||||||
let obj = obj?;
|
let obj = obj?;
|
||||||
// Try to find an exact name match
|
// Try to find an exact name match
|
||||||
@@ -611,8 +638,8 @@ fn find_symbol(
|
|||||||
if section.kind != in_section.kind {
|
if section.kind != in_section.kind {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if let Some(symbol_idx) =
|
if let Some((symbol_idx, _)) = unmatched_symbols(section, section_idx, used)
|
||||||
section.symbols.iter().position(|symbol| symbol.name == in_symbol.name)
|
.find(|(_, symbol)| symbol.name == in_symbol.name)
|
||||||
{
|
{
|
||||||
return Some(SymbolRef { section_idx, symbol_idx });
|
return Some(SymbolRef { section_idx, symbol_idx });
|
||||||
}
|
}
|
||||||
@@ -625,9 +652,11 @@ fn find_symbol(
|
|||||||
if let Some((section_idx, section)) =
|
if let Some((section_idx, section)) =
|
||||||
obj.sections.iter().enumerate().find(|(_, s)| s.name == in_section.name)
|
obj.sections.iter().enumerate().find(|(_, s)| s.name == in_section.name)
|
||||||
{
|
{
|
||||||
if let Some(symbol_idx) = section.symbols.iter().position(|symbol| {
|
if let Some((symbol_idx, _)) =
|
||||||
symbol.address == in_symbol.address && symbol.name.starts_with('@')
|
unmatched_symbols(section, section_idx, used).find(|(_, symbol)| {
|
||||||
}) {
|
symbol.address == in_symbol.address && symbol.name.starts_with('@')
|
||||||
|
})
|
||||||
|
{
|
||||||
return Some(SymbolRef { section_idx, symbol_idx });
|
return Some(SymbolRef { section_idx, symbol_idx });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -641,13 +670,15 @@ fn find_symbol(
|
|||||||
if section.kind != in_section.kind {
|
if section.kind != in_section.kind {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if let Some(symbol_idx) = section.symbols.iter().position(|symbol| {
|
if let Some((symbol_idx, _)) =
|
||||||
if let Some((p, s)) = symbol.name.split_once('$') {
|
unmatched_symbols(section, section_idx, used).find(|&(_, symbol)| {
|
||||||
prefix == p && s.chars().all(char::is_numeric)
|
if let Some((p, s)) = symbol.name.split_once('$') {
|
||||||
} else {
|
prefix == p && s.chars().all(char::is_numeric)
|
||||||
false
|
} else {
|
||||||
}
|
false
|
||||||
}) {
|
}
|
||||||
|
})
|
||||||
|
{
|
||||||
return Some(SymbolRef { section_idx, symbol_idx });
|
return Some(SymbolRef { section_idx, symbol_idx });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ fn symbols_by_section(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result.sort_by_key(|v| v.address);
|
result.sort_by(|a, b| a.address.cmp(&b.address).then(a.size.cmp(&b.size)));
|
||||||
let mut iter = result.iter_mut().peekable();
|
let mut iter = result.iter_mut().peekable();
|
||||||
while let Some(symbol) = iter.next() {
|
while let Some(symbol) = iter.next() {
|
||||||
if symbol.size == 0 {
|
if symbol.size == 0 {
|
||||||
@@ -160,6 +160,23 @@ fn symbols_by_section(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if result.is_empty() {
|
||||||
|
// Dummy symbol for empty sections
|
||||||
|
result.push(ObjSymbol {
|
||||||
|
name: format!("[{}]", section.name),
|
||||||
|
demangled_name: None,
|
||||||
|
has_extab: false,
|
||||||
|
extab_name: None,
|
||||||
|
extabindex_name: None,
|
||||||
|
address: 0,
|
||||||
|
section_address: 0,
|
||||||
|
size: section.size,
|
||||||
|
size_known: true,
|
||||||
|
flags: Default::default(),
|
||||||
|
addend: 0,
|
||||||
|
virtual_address: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -364,7 +381,7 @@ fn relocations_by_section(
|
|||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
let addend = if reloc.has_implicit_addend() {
|
let addend = if reloc.has_implicit_addend() {
|
||||||
arch.implcit_addend(section, address, &reloc)?
|
arch.implcit_addend(obj_file, section, address, &reloc)?
|
||||||
} else {
|
} else {
|
||||||
reloc.addend()
|
reloc.addend()
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "objdiff-gui"
|
name = "objdiff-gui"
|
||||||
version = "2.0.0-beta.1"
|
version = "2.0.0-beta.2"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.70"
|
rust-version = "1.70"
|
||||||
authors = ["Luke Street <luke@street.dev>"]
|
authors = ["Luke Street <luke@street.dev>"]
|
||||||
@@ -18,7 +18,7 @@ name = "objdiff"
|
|||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["wgpu", "wsl"]
|
default = ["glow", "wgpu", "wsl"]
|
||||||
glow = ["eframe/glow"]
|
glow = ["eframe/glow"]
|
||||||
wgpu = ["eframe/wgpu", "dep:wgpu"]
|
wgpu = ["eframe/wgpu", "dep:wgpu"]
|
||||||
wsl = []
|
wsl = []
|
||||||
|
|||||||
@@ -275,7 +275,7 @@ impl App {
|
|||||||
#[cfg(feature = "glow")]
|
#[cfg(feature = "glow")]
|
||||||
if let Some(gl) = &cc.gl {
|
if let Some(gl) = &cc.gl {
|
||||||
use eframe::glow::HasContext;
|
use eframe::glow::HasContext;
|
||||||
app.view_state.graphics_state.active_backend = "OpenGL".to_string();
|
app.view_state.graphics_state.active_backend = "OpenGL (Fallback)".to_string();
|
||||||
app.view_state.graphics_state.active_device =
|
app.view_state.graphics_state.active_device =
|
||||||
unsafe { gl.get_parameter_string(0x1F01) }; // GL_RENDERER
|
unsafe { gl.get_parameter_string(0x1F01) }; // GL_RENDERER
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ mod views;
|
|||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
|
process::ExitCode,
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
};
|
};
|
||||||
@@ -37,7 +38,7 @@ const APP_NAME: &str = "objdiff";
|
|||||||
|
|
||||||
// When compiling natively:
|
// When compiling natively:
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
fn main() {
|
fn main() -> ExitCode {
|
||||||
// Log to stdout (if you run with `RUST_LOG=debug`).
|
// Log to stdout (if you run with `RUST_LOG=debug`).
|
||||||
tracing_subscriber::fmt::init();
|
tracing_subscriber::fmt::init();
|
||||||
|
|
||||||
@@ -48,7 +49,6 @@ fn main() {
|
|||||||
|
|
||||||
let app_path = std::env::current_exe().ok();
|
let app_path = std::env::current_exe().ok();
|
||||||
let exec_path: Rc<Mutex<Option<PathBuf>>> = Rc::new(Mutex::new(None));
|
let exec_path: Rc<Mutex<Option<PathBuf>>> = Rc::new(Mutex::new(None));
|
||||||
let exec_path_clone = exec_path.clone();
|
|
||||||
let mut native_options =
|
let mut native_options =
|
||||||
eframe::NativeOptions { follow_system_theme: false, ..Default::default() };
|
eframe::NativeOptions { follow_system_theme: false, ..Default::default() };
|
||||||
match load_icon() {
|
match load_icon() {
|
||||||
@@ -56,7 +56,7 @@ fn main() {
|
|||||||
native_options.viewport.icon = Some(Arc::new(data));
|
native_options.viewport.icon = Some(Arc::new(data));
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::warn!("Failed to load application icon: {}", e);
|
log::warn!("Failed to load application icon: {e:?}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut graphics_config = GraphicsConfig::default();
|
let mut graphics_config = GraphicsConfig::default();
|
||||||
@@ -69,7 +69,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
Ok(None) => {}
|
Ok(None) => {}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!("Failed to load native config: {:?}", e);
|
log::error!("Failed to load native config: {e:?}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
graphics_config_path = Some(config_path);
|
graphics_config_path = Some(config_path);
|
||||||
@@ -87,6 +87,104 @@ fn main() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let mut eframe_error = None;
|
||||||
|
if let Err(e) = run_eframe(
|
||||||
|
native_options.clone(),
|
||||||
|
utc_offset,
|
||||||
|
exec_path.clone(),
|
||||||
|
app_path.clone(),
|
||||||
|
graphics_config.clone(),
|
||||||
|
graphics_config_path.clone(),
|
||||||
|
) {
|
||||||
|
eframe_error = Some(e);
|
||||||
|
}
|
||||||
|
#[cfg(feature = "wgpu")]
|
||||||
|
if let Some(e) = eframe_error {
|
||||||
|
// Attempt to relaunch using wgpu auto backend if the desired backend failed
|
||||||
|
#[allow(unused_mut)]
|
||||||
|
let mut should_relaunch = graphics_config.desired_backend != GraphicsBackend::Auto;
|
||||||
|
#[cfg(feature = "glow")]
|
||||||
|
{
|
||||||
|
// If the desired backend is OpenGL, we should try to relaunch using the glow renderer
|
||||||
|
should_relaunch &= graphics_config.desired_backend != GraphicsBackend::OpenGL;
|
||||||
|
}
|
||||||
|
if should_relaunch {
|
||||||
|
log::warn!("Failed to launch application: {e:?}");
|
||||||
|
log::warn!("Attempting to relaunch using auto-detected backend");
|
||||||
|
native_options.wgpu_options.supported_backends = Default::default();
|
||||||
|
if let Err(e) = run_eframe(
|
||||||
|
native_options.clone(),
|
||||||
|
utc_offset,
|
||||||
|
exec_path.clone(),
|
||||||
|
app_path.clone(),
|
||||||
|
graphics_config.clone(),
|
||||||
|
graphics_config_path.clone(),
|
||||||
|
) {
|
||||||
|
eframe_error = Some(e);
|
||||||
|
} else {
|
||||||
|
eframe_error = None;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
eframe_error = Some(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(all(feature = "wgpu", feature = "glow"))]
|
||||||
|
if let Some(e) = eframe_error {
|
||||||
|
// Attempt to relaunch using the glow renderer if the wgpu backend failed
|
||||||
|
log::warn!("Failed to launch application: {e:?}");
|
||||||
|
log::warn!("Attempting to relaunch using fallback OpenGL backend");
|
||||||
|
native_options.renderer = eframe::Renderer::Glow;
|
||||||
|
if let Err(e) = run_eframe(
|
||||||
|
native_options,
|
||||||
|
utc_offset,
|
||||||
|
exec_path.clone(),
|
||||||
|
app_path,
|
||||||
|
graphics_config,
|
||||||
|
graphics_config_path,
|
||||||
|
) {
|
||||||
|
eframe_error = Some(e);
|
||||||
|
} else {
|
||||||
|
eframe_error = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(e) = eframe_error {
|
||||||
|
log::error!("Failed to launch application: {e:?}");
|
||||||
|
return ExitCode::FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to relaunch application from the updated path
|
||||||
|
if let Ok(mut guard) = exec_path.lock() {
|
||||||
|
if let Some(path) = guard.take() {
|
||||||
|
cfg_if! {
|
||||||
|
if #[cfg(unix)] {
|
||||||
|
let e = exec::Command::new(path)
|
||||||
|
.args(&std::env::args().collect::<Vec<String>>())
|
||||||
|
.exec();
|
||||||
|
log::error!("Failed to relaunch: {e:?}");
|
||||||
|
return ExitCode::FAILURE;
|
||||||
|
} else {
|
||||||
|
let result = std::process::Command::new(path)
|
||||||
|
.args(std::env::args())
|
||||||
|
.spawn();
|
||||||
|
if let Err(e) = result {
|
||||||
|
log::error!("Failed to relaunch: {e:?}");
|
||||||
|
return ExitCode::FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ExitCode::SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_eframe(
|
||||||
|
native_options: eframe::NativeOptions,
|
||||||
|
utc_offset: UtcOffset,
|
||||||
|
exec_path_clone: Rc<Mutex<Option<PathBuf>>>,
|
||||||
|
app_path: Option<PathBuf>,
|
||||||
|
graphics_config: GraphicsConfig,
|
||||||
|
graphics_config_path: Option<PathBuf>,
|
||||||
|
) -> Result<(), eframe::Error> {
|
||||||
eframe::run_native(
|
eframe::run_native(
|
||||||
APP_NAME,
|
APP_NAME,
|
||||||
native_options,
|
native_options,
|
||||||
@@ -101,28 +199,6 @@ fn main() {
|
|||||||
))
|
))
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.expect("Failed to run eframe application");
|
|
||||||
|
|
||||||
// Attempt to relaunch application from the updated path
|
|
||||||
if let Ok(mut guard) = exec_path.lock() {
|
|
||||||
if let Some(path) = guard.take() {
|
|
||||||
cfg_if! {
|
|
||||||
if #[cfg(unix)] {
|
|
||||||
let result = exec::Command::new(path)
|
|
||||||
.args(&std::env::args().collect::<Vec<String>>())
|
|
||||||
.exec();
|
|
||||||
log::error!("Failed to relaunch: {result:?}");
|
|
||||||
} else {
|
|
||||||
let result = std::process::Command::new(path)
|
|
||||||
.args(std::env::args())
|
|
||||||
.spawn();
|
|
||||||
if let Err(e) = result {
|
|
||||||
log::error!("Failed to relaunch: {:?}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// when compiling to web using trunk.
|
// when compiling to web using trunk.
|
||||||
|
|||||||
@@ -216,8 +216,11 @@ fn diff_text_ui(
|
|||||||
base_color = appearance.diff_colors[diff.idx % appearance.diff_colors.len()]
|
base_color = appearance.diff_colors[diff.idx % appearance.diff_colors.len()]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DiffText::BranchDest(addr) => {
|
DiffText::BranchDest(addr, diff) => {
|
||||||
label_text = format!("{addr:x}");
|
label_text = format!("{addr:x}");
|
||||||
|
if let Some(diff) = diff {
|
||||||
|
base_color = appearance.diff_colors[diff.idx % appearance.diff_colors.len()]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
DiffText::Symbol(sym) => {
|
DiffText::Symbol(sym) => {
|
||||||
let name = sym.demangled_name.as_ref().unwrap_or(&sym.name);
|
let name = sym.demangled_name.as_ref().unwrap_or(&sym.name);
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ pub enum GraphicsBackend {
|
|||||||
OpenGL,
|
OpenGL,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, serde::Deserialize, serde::Serialize)]
|
#[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize)]
|
||||||
pub struct GraphicsConfig {
|
pub struct GraphicsConfig {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub desired_backend: GraphicsBackend,
|
pub desired_backend: GraphicsBackend,
|
||||||
|
|||||||
@@ -233,21 +233,28 @@ fn symbol_ui(
|
|||||||
{
|
{
|
||||||
selected = symbol_diff.symbol_ref == sym_ref;
|
selected = symbol_diff.symbol_ref == sym_ref;
|
||||||
}
|
}
|
||||||
write_text("[", appearance.text_color, &mut job, appearance.code_font.clone());
|
if !symbol.flags.0.is_empty() {
|
||||||
if symbol.flags.0.contains(ObjSymbolFlags::Common) {
|
write_text("[", appearance.text_color, &mut job, appearance.code_font.clone());
|
||||||
write_text("c", appearance.replace_color, &mut job, appearance.code_font.clone());
|
if symbol.flags.0.contains(ObjSymbolFlags::Common) {
|
||||||
} else if symbol.flags.0.contains(ObjSymbolFlags::Global) {
|
write_text("c", appearance.replace_color, &mut job, appearance.code_font.clone());
|
||||||
write_text("g", appearance.insert_color, &mut job, appearance.code_font.clone());
|
} else if symbol.flags.0.contains(ObjSymbolFlags::Global) {
|
||||||
} else if symbol.flags.0.contains(ObjSymbolFlags::Local) {
|
write_text("g", appearance.insert_color, &mut job, appearance.code_font.clone());
|
||||||
write_text("l", appearance.text_color, &mut job, appearance.code_font.clone());
|
} else if symbol.flags.0.contains(ObjSymbolFlags::Local) {
|
||||||
|
write_text("l", appearance.text_color, &mut job, appearance.code_font.clone());
|
||||||
|
}
|
||||||
|
if symbol.flags.0.contains(ObjSymbolFlags::Weak) {
|
||||||
|
write_text("w", appearance.text_color, &mut job, appearance.code_font.clone());
|
||||||
|
}
|
||||||
|
if symbol.flags.0.contains(ObjSymbolFlags::Hidden) {
|
||||||
|
write_text(
|
||||||
|
"h",
|
||||||
|
appearance.deemphasized_text_color,
|
||||||
|
&mut job,
|
||||||
|
appearance.code_font.clone(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
write_text("] ", appearance.text_color, &mut job, appearance.code_font.clone());
|
||||||
}
|
}
|
||||||
if symbol.flags.0.contains(ObjSymbolFlags::Weak) {
|
|
||||||
write_text("w", appearance.text_color, &mut job, appearance.code_font.clone());
|
|
||||||
}
|
|
||||||
if symbol.flags.0.contains(ObjSymbolFlags::Hidden) {
|
|
||||||
write_text("h", appearance.deemphasized_text_color, &mut job, appearance.code_font.clone());
|
|
||||||
}
|
|
||||||
write_text("] ", appearance.text_color, &mut job, appearance.code_font.clone());
|
|
||||||
if let Some(match_percent) = symbol_diff.match_percent {
|
if let Some(match_percent) = symbol_diff.match_percent {
|
||||||
write_text("(", appearance.text_color, &mut job, appearance.code_font.clone());
|
write_text("(", appearance.text_color, &mut job, appearance.code_font.clone());
|
||||||
write_text(
|
write_text(
|
||||||
|
|||||||
Reference in New Issue
Block a user