mirror of
https://github.com/encounter/objdiff.git
synced 2025-06-07 07:03:39 +00:00
Unify context menu / hover tooltip code + UI improvements
This commit is contained in:
parent
8461b35cd7
commit
a1f2a535e5
15
Cargo.lock
generated
15
Cargo.lock
generated
@ -947,7 +947,7 @@ checksum = "c2e06f9bce634a3c898eb1e5cb949ff63133cbb218af93cc9b38b31d6f3ea285"
|
||||
[[package]]
|
||||
name = "cwextab"
|
||||
version = "1.0.4"
|
||||
source = "git+https://github.com/encounter/cwextab.git#15c344ac3302c32adbb8777c70f5ce739f432a6b"
|
||||
source = "git+https://github.com/CelestialAmber/cwextab.git#a81d42980912afebf6d3243dddf7309b138c4e28"
|
||||
dependencies = [
|
||||
"thiserror 2.0.11",
|
||||
]
|
||||
@ -1498,8 +1498,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
[[package]]
|
||||
name = "flagset"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3ea1ec5f8307826a5b71094dd91fc04d4ae75d5709b20ad351c7fb4815c86ec"
|
||||
source = "git+https://github.com/enarx/flagset.git?rev=a1fe9369b3741e43fec45da1998e83b9d78966a2#a1fe9369b3741e43fec45da1998e83b9d78966a2"
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
@ -3268,7 +3267,6 @@ version = "3.0.0-alpha.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arm-attr",
|
||||
"byteorder",
|
||||
"cpp_demangle",
|
||||
"cwdemangle",
|
||||
"cwextab",
|
||||
@ -3299,13 +3297,13 @@ dependencies = [
|
||||
"rabbitizer",
|
||||
"regex",
|
||||
"reqwest 0.12.12",
|
||||
"rlwinmdec",
|
||||
"self_update",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"shell-escape",
|
||||
"similar 2.7.0 (git+https://github.com/encounter/similar.git?branch=no_std)",
|
||||
"strum",
|
||||
"syn",
|
||||
"tempfile",
|
||||
"time",
|
||||
@ -3321,7 +3319,6 @@ name = "objdiff-gui"
|
||||
version = "3.0.0-alpha.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
"cfg-if",
|
||||
"const_format",
|
||||
"cwdemangle",
|
||||
@ -3347,7 +3344,6 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"shell-escape",
|
||||
"strum",
|
||||
"tauri-winres",
|
||||
"time",
|
||||
"tracing-subscriber",
|
||||
@ -4188,9 +4184,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rlwinmdec"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2076dbc187938f3db71c03c85d143febf01026631189dc8ca85f8c886d90ea12"
|
||||
version = "1.1.0"
|
||||
source = "git+https://github.com/CelestialAmber/rlwinmdec.git#06e9a86eec1f21466a97cc48a752e9177985e1eb"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
]
|
||||
|
@ -239,7 +239,12 @@ allow-git = []
|
||||
|
||||
[sources.allow-org]
|
||||
# github.com organizations to allow git sources for
|
||||
github = ["encounter"]
|
||||
github = [
|
||||
"CelestialAmber", # cwextab, rlwinmdec
|
||||
"Decompollaborate", # rabbitizer
|
||||
"enarx", # flagset
|
||||
"encounter",
|
||||
]
|
||||
# gitlab.com organizations to allow git sources for
|
||||
gitlab = []
|
||||
# bitbucket.org organizations to allow git sources for
|
||||
|
@ -30,7 +30,6 @@ all = [
|
||||
]
|
||||
# Implicit, used to check if any arch is enabled
|
||||
any-arch = [
|
||||
"dep:byteorder",
|
||||
"dep:flagset",
|
||||
"dep:heck",
|
||||
"dep:log",
|
||||
@ -40,7 +39,6 @@ any-arch = [
|
||||
"dep:quote",
|
||||
"dep:regex",
|
||||
"dep:similar",
|
||||
"dep:strum",
|
||||
"dep:syn",
|
||||
]
|
||||
bindings = [
|
||||
@ -71,14 +69,12 @@ serde = [
|
||||
]
|
||||
std = [
|
||||
"anyhow/std",
|
||||
"byteorder?/std",
|
||||
"flagset?/std",
|
||||
"log?/std",
|
||||
"num-traits?/std",
|
||||
"object/std",
|
||||
"prost?/std",
|
||||
"serde?/std",
|
||||
"strum?/std",
|
||||
"typed-path?/std",
|
||||
"dep:filetime",
|
||||
"dep:memmap2",
|
||||
@ -92,6 +88,7 @@ ppc = [
|
||||
"dep:cwdemangle",
|
||||
"dep:cwextab",
|
||||
"dep:ppc750cl",
|
||||
"dep:rlwinmdec",
|
||||
]
|
||||
x86 = [
|
||||
"any-arch",
|
||||
@ -117,21 +114,19 @@ features = ["all"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = { version = "1.0", default-features = false }
|
||||
byteorder = { version = "1.5", default-features = false, optional = true }
|
||||
filetime = { version = "0.2", optional = true }
|
||||
flagset = { version = "0.4", default-features = false, optional = true }
|
||||
flagset = { version = "0.4", default-features = false, optional = true, git = "https://github.com/enarx/flagset.git", rev = "a1fe9369b3741e43fec45da1998e83b9d78966a2" }
|
||||
itertools = { version = "0.14", default-features = false, features = ["use_alloc"] }
|
||||
log = { version = "0.4", default-features = false, optional = true }
|
||||
memmap2 = { version = "0.9", optional = true }
|
||||
num-traits = { version = "0.2", default-features = false, optional = true }
|
||||
object = { version = "0.36", default-features = false, features = ["read_core", "elf", "pe"] }
|
||||
pbjson = { version = "0.7", default-features = false, optional = true }
|
||||
prost = { version = "0.13", default-features = false, features = ["prost-derive"], optional = true }
|
||||
regex = { version = "1.11", default-features = false, features = [], optional = true }
|
||||
serde = { version = "1.0", default-features = false, features = ["derive"], optional = true }
|
||||
similar = { version = "2.7", default-features = false, optional = true, git = "https://github.com/encounter/similar.git", branch = "no_std" }
|
||||
strum = { version = "0.26", default-features = false, features = ["derive"], optional = true }
|
||||
typed-path = { version = "0.10", default-features = false, optional = true }
|
||||
regex = { version = "1.11", default-features = false, features = [], optional = true }
|
||||
itertools = { version = "0.14", default-features = false, features = ["use_alloc"] }
|
||||
|
||||
# config
|
||||
globset = { version = "0.4", default-features = false, optional = true }
|
||||
@ -143,8 +138,9 @@ gimli = { version = "0.31", default-features = false, features = ["read"], optio
|
||||
|
||||
# ppc
|
||||
cwdemangle = { version = "1.0", optional = true }
|
||||
cwextab = { version = "1.0", optional = true, git = "https://github.com/encounter/cwextab.git" }
|
||||
cwextab = { version = "1.0", optional = true, git = "https://github.com/CelestialAmber/cwextab.git" }
|
||||
ppc750cl = { version = "0.3", optional = true }
|
||||
rlwinmdec = { version = "1.1", optional = true, git = "https://github.com/CelestialAmber/rlwinmdec.git" }
|
||||
|
||||
# mips
|
||||
rabbitizer = { git = "https://github.com/Decompollaborate/rabbitizer.git", branch = "🦀", default-features = false, optional = true }
|
||||
@ -196,4 +192,4 @@ syn = { version = "2.0", optional = true }
|
||||
[dev-dependencies]
|
||||
# Enable all features for tests
|
||||
objdiff-core = { path = ".", features = ["all"] }
|
||||
insta = "1.42.1"
|
||||
insta = "1.42"
|
||||
|
@ -1,11 +1,9 @@
|
||||
use alloc::{
|
||||
borrow::Cow,
|
||||
collections::BTreeMap,
|
||||
format,
|
||||
string::{String, ToString},
|
||||
vec::Vec,
|
||||
};
|
||||
use core::ops::Range;
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use arm_attr::{enums::CpuArch, tag::Tag, BuildAttrs};
|
||||
@ -16,8 +14,8 @@ use crate::{
|
||||
arch::Arch,
|
||||
diff::{display::InstructionPart, ArmArchVersion, ArmR9Usage, DiffObjConfig},
|
||||
obj::{
|
||||
InstructionRef, RelocationFlags, ResolvedRelocation, ScannedInstruction, SymbolFlag,
|
||||
SymbolFlagSet, SymbolKind,
|
||||
InstructionRef, RelocationFlags, ResolvedInstructionRef, ResolvedRelocation,
|
||||
ScannedInstruction, SymbolFlag, SymbolFlagSet, SymbolKind,
|
||||
},
|
||||
};
|
||||
|
||||
@ -252,23 +250,19 @@ impl Arch for ArchArm {
|
||||
|
||||
fn display_instruction(
|
||||
&self,
|
||||
ins_ref: InstructionRef,
|
||||
code: &[u8],
|
||||
relocation: Option<ResolvedRelocation>,
|
||||
_function_range: Range<u64>,
|
||||
_section_index: usize,
|
||||
resolved: ResolvedInstructionRef,
|
||||
diff_config: &DiffObjConfig,
|
||||
cb: &mut dyn FnMut(InstructionPart) -> Result<()>,
|
||||
) -> Result<()> {
|
||||
let (ins, parsed_ins) = self.parse_ins_ref(ins_ref, code, diff_config)?;
|
||||
cb(InstructionPart::opcode(parsed_ins.mnemonic, ins_ref.opcode))?;
|
||||
if ins == unarm::Ins::Data && relocation.is_some() {
|
||||
let (ins, parsed_ins) = self.parse_ins_ref(resolved.ins_ref, resolved.code, diff_config)?;
|
||||
cb(InstructionPart::opcode(parsed_ins.mnemonic, resolved.ins_ref.opcode))?;
|
||||
if ins == unarm::Ins::Data && resolved.relocation.is_some() {
|
||||
cb(InstructionPart::reloc())?;
|
||||
} else {
|
||||
push_args(
|
||||
&parsed_ins,
|
||||
relocation,
|
||||
ins_ref.address as u32,
|
||||
resolved.relocation,
|
||||
resolved.ins_ref.address as u32,
|
||||
self.display_options(diff_config),
|
||||
cb,
|
||||
)?;
|
||||
@ -325,17 +319,38 @@ impl Arch for ArchArm {
|
||||
.and_then(|s| s.demangle(&cpp_demangle::DemangleOptions::default()).ok())
|
||||
}
|
||||
|
||||
fn display_reloc(&self, flags: RelocationFlags) -> Cow<'static, str> {
|
||||
Cow::Owned(format!("<{flags:?}>"))
|
||||
}
|
||||
|
||||
fn get_reloc_byte_size(&self, flags: RelocationFlags) -> usize {
|
||||
fn reloc_name(&self, flags: RelocationFlags) -> Option<&'static str> {
|
||||
match flags {
|
||||
RelocationFlags::Elf(r_type) => match r_type {
|
||||
elf::R_ARM_NONE => Some("R_ARM_NONE"),
|
||||
elf::R_ARM_ABS32 => Some("R_ARM_ABS32"),
|
||||
elf::R_ARM_REL32 => Some("R_ARM_REL32"),
|
||||
elf::R_ARM_ABS16 => Some("R_ARM_ABS16"),
|
||||
elf::R_ARM_ABS8 => Some("R_ARM_ABS8"),
|
||||
elf::R_ARM_THM_PC22 => Some("R_ARM_THM_PC22"),
|
||||
elf::R_ARM_THM_XPC22 => Some("R_ARM_THM_XPC22"),
|
||||
elf::R_ARM_PC24 => Some("R_ARM_PC24"),
|
||||
elf::R_ARM_XPC25 => Some("R_ARM_XPC25"),
|
||||
elf::R_ARM_CALL => Some("R_ARM_CALL"),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn data_reloc_size(&self, flags: RelocationFlags) -> usize {
|
||||
match flags {
|
||||
RelocationFlags::Elf(r_type) => match r_type {
|
||||
elf::R_ARM_NONE => 0,
|
||||
elf::R_ARM_ABS32 => 4,
|
||||
elf::R_ARM_REL32 => 4,
|
||||
elf::R_ARM_ABS16 => 2,
|
||||
elf::R_ARM_ABS8 => 1,
|
||||
elf::R_ARM_THM_PC22 => 4,
|
||||
elf::R_ARM_THM_XPC22 => 4,
|
||||
elf::R_ARM_PC24 => 4,
|
||||
elf::R_ARM_XPC25 => 4,
|
||||
elf::R_ARM_CALL => 4,
|
||||
_ => 1,
|
||||
},
|
||||
_ => 1,
|
||||
|
@ -1,10 +1,9 @@
|
||||
use alloc::{
|
||||
borrow::Cow,
|
||||
format,
|
||||
string::{String, ToString},
|
||||
vec::Vec,
|
||||
};
|
||||
use core::{cmp::Ordering, ops::Range};
|
||||
use core::cmp::Ordering;
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use object::elf;
|
||||
@ -17,7 +16,10 @@ use yaxpeax_arm::armv8::a64::{
|
||||
use crate::{
|
||||
arch::Arch,
|
||||
diff::{display::InstructionPart, DiffObjConfig},
|
||||
obj::{InstructionRef, RelocationFlags, ResolvedRelocation, ScannedInstruction},
|
||||
obj::{
|
||||
InstructionRef, RelocationFlags, ResolvedInstructionRef, ResolvedRelocation,
|
||||
ScannedInstruction,
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -75,15 +77,11 @@ impl Arch for ArchArm64 {
|
||||
|
||||
fn display_instruction(
|
||||
&self,
|
||||
ins_ref: InstructionRef,
|
||||
code: &[u8],
|
||||
relocation: Option<ResolvedRelocation>,
|
||||
function_range: Range<u64>,
|
||||
_section_index: usize,
|
||||
resolved: ResolvedInstructionRef,
|
||||
_diff_config: &DiffObjConfig,
|
||||
cb: &mut dyn FnMut(InstructionPart) -> Result<()>,
|
||||
) -> Result<()> {
|
||||
let mut reader = U8Reader::new(code);
|
||||
let mut reader = U8Reader::new(resolved.code);
|
||||
let decoder = InstDecoder::default();
|
||||
let mut ins = Instruction::default();
|
||||
if decoder.decode_into(&mut ins, &mut reader).is_err() {
|
||||
@ -92,135 +90,22 @@ impl Arch for ArchArm64 {
|
||||
}
|
||||
|
||||
let mut ctx = DisplayCtx {
|
||||
address: ins_ref.address,
|
||||
address: resolved.ins_ref.address,
|
||||
section_index: 0,
|
||||
start_address: function_range.start,
|
||||
end_address: function_range.end,
|
||||
reloc: relocation,
|
||||
start_address: resolved.symbol.address,
|
||||
end_address: resolved.symbol.address + resolved.symbol.size,
|
||||
reloc: resolved.relocation,
|
||||
};
|
||||
|
||||
let mut display_args = Vec::with_capacity(16);
|
||||
let mnemonic = display_instruction(&mut |ret| display_args.push(ret), &ins, &mut ctx);
|
||||
cb(InstructionPart::opcode(mnemonic, ins_ref.opcode))?;
|
||||
cb(InstructionPart::opcode(mnemonic, resolved.ins_ref.opcode))?;
|
||||
for arg in display_args {
|
||||
cb(arg)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// fn process_code(
|
||||
// &self,
|
||||
// address: u64,
|
||||
// code: &[u8],
|
||||
// section_index: usize,
|
||||
// relocations: &[ObjReloc],
|
||||
// line_info: &BTreeMap<u64, u32>,
|
||||
// config: &DiffObjConfig,
|
||||
// ) -> Result<ProcessCodeResult> {
|
||||
// let start_address = address;
|
||||
// let end_address = address + code.len() as u64;
|
||||
// let ins_count = code.len() / 4;
|
||||
//
|
||||
// let mut ops = Vec::with_capacity(ins_count);
|
||||
// let mut insts = Vec::with_capacity(ins_count);
|
||||
//
|
||||
// let mut reader = U8Reader::new(code);
|
||||
// let decoder = InstDecoder::default();
|
||||
// let mut ins = Instruction::default();
|
||||
// loop {
|
||||
// // This is ridiculous...
|
||||
// let address = start_address
|
||||
// + <U8Reader<'_> as Reader<
|
||||
// <ARMv8 as YaxpeaxArch>::Address,
|
||||
// <ARMv8 as YaxpeaxArch>::Word,
|
||||
// >>::total_offset(&mut reader);
|
||||
// match decoder.decode_into(&mut ins, &mut reader) {
|
||||
// Ok(()) => {}
|
||||
// Err(e) => match e {
|
||||
// DecodeError::ExhaustedInput => break,
|
||||
// DecodeError::InvalidOpcode
|
||||
// | DecodeError::InvalidOperand
|
||||
// | DecodeError::IncompleteDecoder => {
|
||||
// ops.push(u16::MAX);
|
||||
// insts.push(ObjIns {
|
||||
// address,
|
||||
// size: 4,
|
||||
// op: u16::MAX,
|
||||
// mnemonic: Cow::Borrowed("<invalid>"),
|
||||
// args: vec![],
|
||||
// reloc: None,
|
||||
// branch_dest: None,
|
||||
// line: None,
|
||||
// formatted: "".to_string(),
|
||||
// orig: None,
|
||||
// });
|
||||
// continue;
|
||||
// }
|
||||
// },
|
||||
// }
|
||||
//
|
||||
// let line = line_info.range(..=address).last().map(|(_, &b)| b);
|
||||
// let reloc = relocations.iter().find(|r| (r.address & !3) == address).cloned();
|
||||
//
|
||||
// let mut args = vec![];
|
||||
// let mut ctx = DisplayCtx {
|
||||
// address,
|
||||
// section_index,
|
||||
// start_address,
|
||||
// end_address,
|
||||
// reloc: reloc.as_ref(),
|
||||
// config,
|
||||
// branch_dest: None,
|
||||
// };
|
||||
// // Simplify instruction and process args
|
||||
// let mnemonic = display_instruction(&mut args, &ins, &mut ctx);
|
||||
//
|
||||
// // Format the instruction without simplification
|
||||
// let mut orig = ins.opcode.to_string();
|
||||
// for (i, o) in ins.operands.iter().enumerate() {
|
||||
// if let Operand::Nothing = o {
|
||||
// break;
|
||||
// }
|
||||
// if i == 0 {
|
||||
// orig.push(' ');
|
||||
// } else {
|
||||
// orig.push_str(", ");
|
||||
// }
|
||||
// orig.push_str(o.to_string().as_str());
|
||||
// }
|
||||
//
|
||||
// if let Some(reloc) = &reloc {
|
||||
// if !args.iter().any(|a| matches!(a, InstructionArg::Reloc)) {
|
||||
// push_arg(args, InstructionArg::PlainText(Cow::Borrowed(" <unhandled relocation>")));
|
||||
// log::warn!(
|
||||
// "Unhandled ARM64 relocation {:?}: {} @ {:#X}",
|
||||
// reloc.flags,
|
||||
// orig,
|
||||
// address
|
||||
// );
|
||||
// }
|
||||
// };
|
||||
//
|
||||
// let op = opcode_to_u16(ins.opcode);
|
||||
// ops.push(op);
|
||||
// let branch_dest = ctx.branch_dest;
|
||||
// insts.push(ObjIns {
|
||||
// address,
|
||||
// size: 4,
|
||||
// op,
|
||||
// mnemonic: Cow::Borrowed(mnemonic),
|
||||
// args,
|
||||
// reloc,
|
||||
// branch_dest,
|
||||
// line,
|
||||
// formatted: ins.to_string(),
|
||||
// orig: Some(orig),
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// Ok(ProcessCodeResult { ops, insts })
|
||||
// }
|
||||
|
||||
fn implcit_addend(
|
||||
&self,
|
||||
_file: &object::File<'_>,
|
||||
@ -238,30 +123,30 @@ impl Arch for ArchArm64 {
|
||||
.and_then(|s| s.demangle(&cpp_demangle::DemangleOptions::default()).ok())
|
||||
}
|
||||
|
||||
fn display_reloc(&self, flags: RelocationFlags) -> Cow<'static, str> {
|
||||
fn reloc_name(&self, flags: RelocationFlags) -> Option<&'static str> {
|
||||
match flags {
|
||||
RelocationFlags::Elf(elf::R_AARCH64_ADR_PREL_PG_HI21) => {
|
||||
Cow::Borrowed("R_AARCH64_ADR_PREL_PG_HI21")
|
||||
}
|
||||
RelocationFlags::Elf(elf::R_AARCH64_ADD_ABS_LO12_NC) => {
|
||||
Cow::Borrowed("R_AARCH64_ADD_ABS_LO12_NC")
|
||||
}
|
||||
RelocationFlags::Elf(elf::R_AARCH64_JUMP26) => Cow::Borrowed("R_AARCH64_JUMP26"),
|
||||
RelocationFlags::Elf(elf::R_AARCH64_CALL26) => Cow::Borrowed("R_AARCH64_CALL26"),
|
||||
RelocationFlags::Elf(elf::R_AARCH64_LDST32_ABS_LO12_NC) => {
|
||||
Cow::Borrowed("R_AARCH64_LDST32_ABS_LO12_NC")
|
||||
}
|
||||
RelocationFlags::Elf(elf::R_AARCH64_ADR_GOT_PAGE) => {
|
||||
Cow::Borrowed("R_AARCH64_ADR_GOT_PAGE")
|
||||
}
|
||||
RelocationFlags::Elf(elf::R_AARCH64_LD64_GOT_LO12_NC) => {
|
||||
Cow::Borrowed("R_AARCH64_LD64_GOT_LO12_NC")
|
||||
}
|
||||
_ => Cow::Owned(format!("<{flags:?}>")),
|
||||
RelocationFlags::Elf(r_type) => match r_type {
|
||||
elf::R_AARCH64_NONE => Some("R_AARCH64_NONE"),
|
||||
elf::R_AARCH64_ABS64 => Some("R_AARCH64_ABS64"),
|
||||
elf::R_AARCH64_ABS32 => Some("R_AARCH64_ABS32"),
|
||||
elf::R_AARCH64_ABS16 => Some("R_AARCH64_ABS16"),
|
||||
elf::R_AARCH64_PREL64 => Some("R_AARCH64_PREL64"),
|
||||
elf::R_AARCH64_PREL32 => Some("R_AARCH64_PREL32"),
|
||||
elf::R_AARCH64_PREL16 => Some("R_AARCH64_PREL16"),
|
||||
elf::R_AARCH64_ADR_PREL_PG_HI21 => Some("R_AARCH64_ADR_PREL_PG_HI21"),
|
||||
elf::R_AARCH64_ADD_ABS_LO12_NC => Some("R_AARCH64_ADD_ABS_LO12_NC"),
|
||||
elf::R_AARCH64_JUMP26 => Some("R_AARCH64_JUMP26"),
|
||||
elf::R_AARCH64_CALL26 => Some("R_AARCH64_CALL26"),
|
||||
elf::R_AARCH64_LDST32_ABS_LO12_NC => Some("R_AARCH64_LDST32_ABS_LO12_NC"),
|
||||
elf::R_AARCH64_ADR_GOT_PAGE => Some("R_AARCH64_ADR_GOT_PAGE"),
|
||||
elf::R_AARCH64_LD64_GOT_LO12_NC => Some("R_AARCH64_LD64_GOT_LO12_NC"),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_reloc_byte_size(&self, flags: RelocationFlags) -> usize {
|
||||
fn data_reloc_size(&self, flags: RelocationFlags) -> usize {
|
||||
match flags {
|
||||
RelocationFlags::Elf(r_type) => match r_type {
|
||||
elf::R_AARCH64_ABS64 => 8,
|
||||
|
@ -1,4 +1,4 @@
|
||||
use alloc::{borrow::Cow, format, string::ToString, vec::Vec};
|
||||
use alloc::{string::ToString, vec::Vec};
|
||||
use core::ops::Range;
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
@ -15,7 +15,7 @@ use crate::{
|
||||
diff::{display::InstructionPart, DiffObjConfig, MipsAbi, MipsInstrCategory},
|
||||
obj::{
|
||||
InstructionArg, InstructionArgValue, InstructionRef, Relocation, RelocationFlags,
|
||||
ResolvedRelocation, ScannedInstruction,
|
||||
ResolvedInstructionRef, ResolvedRelocation, ScannedInstruction,
|
||||
},
|
||||
};
|
||||
|
||||
@ -148,19 +148,24 @@ impl Arch for ArchMips {
|
||||
|
||||
fn display_instruction(
|
||||
&self,
|
||||
ins_ref: InstructionRef,
|
||||
code: &[u8],
|
||||
relocation: Option<ResolvedRelocation>,
|
||||
function_range: Range<u64>,
|
||||
section_index: usize,
|
||||
resolved: ResolvedInstructionRef,
|
||||
diff_config: &DiffObjConfig,
|
||||
cb: &mut dyn FnMut(InstructionPart) -> Result<()>,
|
||||
) -> Result<()> {
|
||||
let instruction = self.parse_ins_ref(ins_ref, code, diff_config)?;
|
||||
let instruction = self.parse_ins_ref(resolved.ins_ref, resolved.code, diff_config)?;
|
||||
let display_flags = self.instruction_display_flags(diff_config);
|
||||
let opcode = instruction.opcode();
|
||||
cb(InstructionPart::opcode(opcode.name(), opcode as u16))?;
|
||||
push_args(&instruction, relocation, function_range, section_index, &display_flags, cb)?;
|
||||
let start_address = resolved.symbol.address;
|
||||
let function_range = start_address..start_address + resolved.symbol.size;
|
||||
push_args(
|
||||
&instruction,
|
||||
resolved.relocation,
|
||||
function_range,
|
||||
resolved.section_index,
|
||||
&display_flags,
|
||||
cb,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -202,26 +207,28 @@ impl Arch for ArchMips {
|
||||
})
|
||||
}
|
||||
|
||||
fn display_reloc(&self, flags: RelocationFlags) -> Cow<'static, str> {
|
||||
fn reloc_name(&self, flags: RelocationFlags) -> Option<&'static str> {
|
||||
match flags {
|
||||
RelocationFlags::Elf(r_type) => match r_type {
|
||||
elf::R_MIPS_32 => Cow::Borrowed("R_MIPS_32"),
|
||||
elf::R_MIPS_26 => Cow::Borrowed("R_MIPS_26"),
|
||||
elf::R_MIPS_HI16 => Cow::Borrowed("R_MIPS_HI16"),
|
||||
elf::R_MIPS_LO16 => Cow::Borrowed("R_MIPS_LO16"),
|
||||
elf::R_MIPS_GPREL16 => Cow::Borrowed("R_MIPS_GPREL16"),
|
||||
elf::R_MIPS_LITERAL => Cow::Borrowed("R_MIPS_LITERAL"),
|
||||
elf::R_MIPS_GOT16 => Cow::Borrowed("R_MIPS_GOT16"),
|
||||
elf::R_MIPS_PC16 => Cow::Borrowed("R_MIPS_PC16"),
|
||||
elf::R_MIPS_CALL16 => Cow::Borrowed("R_MIPS_CALL16"),
|
||||
R_MIPS15_S3 => Cow::Borrowed("R_MIPS15_S3"),
|
||||
_ => Cow::Owned(format!("<{flags:?}>")),
|
||||
elf::R_MIPS_NONE => Some("R_MIPS_NONE"),
|
||||
elf::R_MIPS_16 => Some("R_MIPS_16"),
|
||||
elf::R_MIPS_32 => Some("R_MIPS_32"),
|
||||
elf::R_MIPS_26 => Some("R_MIPS_26"),
|
||||
elf::R_MIPS_HI16 => Some("R_MIPS_HI16"),
|
||||
elf::R_MIPS_LO16 => Some("R_MIPS_LO16"),
|
||||
elf::R_MIPS_GPREL16 => Some("R_MIPS_GPREL16"),
|
||||
elf::R_MIPS_LITERAL => Some("R_MIPS_LITERAL"),
|
||||
elf::R_MIPS_GOT16 => Some("R_MIPS_GOT16"),
|
||||
elf::R_MIPS_PC16 => Some("R_MIPS_PC16"),
|
||||
elf::R_MIPS_CALL16 => Some("R_MIPS_CALL16"),
|
||||
R_MIPS15_S3 => Some("R_MIPS15_S3"),
|
||||
_ => None,
|
||||
},
|
||||
_ => Cow::Owned(format!("<{flags:?}>")),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_reloc_byte_size(&self, flags: RelocationFlags) -> usize {
|
||||
fn data_reloc_size(&self, flags: RelocationFlags) -> usize {
|
||||
match flags {
|
||||
RelocationFlags::Elf(r_type) => match r_type {
|
||||
elf::R_MIPS_16 => 2,
|
||||
|
@ -1,23 +1,25 @@
|
||||
use alloc::{borrow::Cow, boxed::Box, format, string::String, vec, vec::Vec};
|
||||
use core::{ffi::CStr, fmt, fmt::Debug, ops::Range};
|
||||
use alloc::{borrow::Cow, boxed::Box, format, string::String, vec::Vec};
|
||||
use core::{ffi::CStr, fmt, fmt::Debug};
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use byteorder::ByteOrder;
|
||||
use object::{File, Relocation, Section};
|
||||
use object::Endian as _;
|
||||
|
||||
use crate::{
|
||||
diff::{display::InstructionPart, DiffObjConfig},
|
||||
diff::{
|
||||
display::{ContextItem, HoverItem, InstructionPart},
|
||||
DiffObjConfig,
|
||||
},
|
||||
obj::{
|
||||
InstructionArg, InstructionRef, ParsedInstruction, RelocationFlags, ResolvedRelocation,
|
||||
InstructionArg, Object, ParsedInstruction, RelocationFlags, ResolvedInstructionRef,
|
||||
ScannedInstruction, SymbolFlagSet, SymbolKind,
|
||||
},
|
||||
util::ReallySigned,
|
||||
};
|
||||
|
||||
#[cfg(feature = "arm")]
|
||||
mod arm;
|
||||
pub mod arm;
|
||||
#[cfg(feature = "arm64")]
|
||||
mod arm64;
|
||||
pub mod arm64;
|
||||
#[cfg(feature = "mips")]
|
||||
pub mod mips;
|
||||
#[cfg(feature = "ppc")]
|
||||
@ -31,7 +33,6 @@ pub enum DataType {
|
||||
Int16,
|
||||
Int32,
|
||||
Int64,
|
||||
Int128,
|
||||
Float,
|
||||
Double,
|
||||
Bytes,
|
||||
@ -45,7 +46,6 @@ impl fmt::Display for DataType {
|
||||
DataType::Int16 => write!(f, "Int16"),
|
||||
DataType::Int32 => write!(f, "Int32"),
|
||||
DataType::Int64 => write!(f, "Int64"),
|
||||
DataType::Int128 => write!(f, "Int128"),
|
||||
DataType::Float => write!(f, "Float"),
|
||||
DataType::Double => write!(f, "Double"),
|
||||
DataType::Bytes => write!(f, "Bytes"),
|
||||
@ -55,15 +55,15 @@ impl fmt::Display for DataType {
|
||||
}
|
||||
|
||||
impl DataType {
|
||||
pub fn display_labels<Endian: ByteOrder>(&self, bytes: &[u8]) -> Vec<String> {
|
||||
pub fn display_labels(&self, endian: object::Endianness, bytes: &[u8]) -> Vec<String> {
|
||||
let mut strs = Vec::new();
|
||||
for literal in self.display_literals::<Endian>(bytes) {
|
||||
for literal in self.display_literals(endian, bytes) {
|
||||
strs.push(format!("{}: {}", self, literal))
|
||||
}
|
||||
strs
|
||||
}
|
||||
|
||||
pub fn display_literals<Endian: ByteOrder>(&self, bytes: &[u8]) -> Vec<String> {
|
||||
pub fn display_literals(&self, endian: object::Endianness, bytes: &[u8]) -> Vec<String> {
|
||||
let mut strs = Vec::new();
|
||||
if self.required_len().is_some_and(|l| bytes.len() < l) {
|
||||
log::warn!("Failed to display a symbol value for a symbol whose size is too small for instruction referencing it.");
|
||||
@ -92,7 +92,7 @@ impl DataType {
|
||||
}
|
||||
}
|
||||
DataType::Int16 => {
|
||||
let i = Endian::read_i16(bytes);
|
||||
let i = endian.read_i16_bytes(bytes.try_into().unwrap());
|
||||
strs.push(format!("{:#x}", i));
|
||||
|
||||
if i < 0 {
|
||||
@ -100,7 +100,7 @@ impl DataType {
|
||||
}
|
||||
}
|
||||
DataType::Int32 => {
|
||||
let i = Endian::read_i32(bytes);
|
||||
let i = endian.read_i32_bytes(bytes.try_into().unwrap());
|
||||
strs.push(format!("{:#x}", i));
|
||||
|
||||
if i < 0 {
|
||||
@ -108,15 +108,7 @@ impl DataType {
|
||||
}
|
||||
}
|
||||
DataType::Int64 => {
|
||||
let i = Endian::read_i64(bytes);
|
||||
strs.push(format!("{:#x}", i));
|
||||
|
||||
if i < 0 {
|
||||
strs.push(format!("{:#x}", ReallySigned(i)));
|
||||
}
|
||||
}
|
||||
DataType::Int128 => {
|
||||
let i = Endian::read_i128(bytes);
|
||||
let i = endian.read_i64_bytes(bytes.try_into().unwrap());
|
||||
strs.push(format!("{:#x}", i));
|
||||
|
||||
if i < 0 {
|
||||
@ -124,10 +116,18 @@ impl DataType {
|
||||
}
|
||||
}
|
||||
DataType::Float => {
|
||||
strs.push(format!("{:?}f", Endian::read_f32(bytes)));
|
||||
let bytes: [u8; 4] = bytes.try_into().unwrap();
|
||||
strs.push(format!("{:?}f", match endian {
|
||||
object::Endianness::Little => f32::from_le_bytes(bytes),
|
||||
object::Endianness::Big => f32::from_be_bytes(bytes),
|
||||
}));
|
||||
}
|
||||
DataType::Double => {
|
||||
strs.push(format!("{:?}", Endian::read_f64(bytes)));
|
||||
let bytes: [u8; 8] = bytes.try_into().unwrap();
|
||||
strs.push(format!("{:?}f", match endian {
|
||||
object::Endianness::Little => f64::from_le_bytes(bytes),
|
||||
object::Endianness::Big => f64::from_be_bytes(bytes),
|
||||
}));
|
||||
}
|
||||
DataType::Bytes => {
|
||||
strs.push(format!("{:#?}", bytes));
|
||||
@ -148,7 +148,6 @@ impl DataType {
|
||||
DataType::Int16 => Some(2),
|
||||
DataType::Int32 => Some(4),
|
||||
DataType::Int64 => Some(8),
|
||||
DataType::Int128 => Some(16),
|
||||
DataType::Float => Some(4),
|
||||
DataType::Double => Some(8),
|
||||
DataType::Bytes => None,
|
||||
@ -177,37 +176,29 @@ pub trait Arch: Send + Sync + Debug {
|
||||
/// This is called only when we need to compare the arguments of an instruction.
|
||||
fn process_instruction(
|
||||
&self,
|
||||
ins_ref: InstructionRef,
|
||||
code: &[u8],
|
||||
relocation: Option<ResolvedRelocation>,
|
||||
function_range: Range<u64>,
|
||||
section_index: usize,
|
||||
resolved: ResolvedInstructionRef,
|
||||
diff_config: &DiffObjConfig,
|
||||
) -> Result<ParsedInstruction> {
|
||||
let mut mnemonic = None;
|
||||
let mut args = Vec::with_capacity(8);
|
||||
self.display_instruction(
|
||||
ins_ref,
|
||||
code,
|
||||
relocation,
|
||||
function_range,
|
||||
section_index,
|
||||
diff_config,
|
||||
&mut |part| {
|
||||
match part {
|
||||
InstructionPart::Opcode(m, _) => mnemonic = Some(Cow::Owned(m.into_owned())),
|
||||
InstructionPart::Arg(arg) => args.push(arg.into_static()),
|
||||
_ => {}
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
self.display_instruction(resolved, diff_config, &mut |part| {
|
||||
match part {
|
||||
InstructionPart::Opcode(m, _) => mnemonic = Some(Cow::Owned(m.into_owned())),
|
||||
InstructionPart::Arg(arg) => args.push(arg.into_static()),
|
||||
_ => {}
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
// If the instruction has a relocation, but we didn't format it in the display, add it to
|
||||
// the end of the arguments list.
|
||||
if relocation.is_some() && !args.contains(&InstructionArg::Reloc) {
|
||||
if resolved.relocation.is_some() && !args.contains(&InstructionArg::Reloc) {
|
||||
args.push(InstructionArg::Reloc);
|
||||
}
|
||||
Ok(ParsedInstruction { ins_ref, mnemonic: mnemonic.unwrap_or_default(), args })
|
||||
Ok(ParsedInstruction {
|
||||
ins_ref: resolved.ins_ref,
|
||||
mnemonic: mnemonic.unwrap_or_default(),
|
||||
args,
|
||||
})
|
||||
}
|
||||
|
||||
/// Format an instruction for display.
|
||||
@ -216,11 +207,7 @@ pub trait Arch: Send + Sync + Debug {
|
||||
/// mnemonic and arguments, plus any separators and visual formatting.
|
||||
fn display_instruction(
|
||||
&self,
|
||||
ins_ref: InstructionRef,
|
||||
code: &[u8],
|
||||
relocation: Option<ResolvedRelocation>,
|
||||
function_range: Range<u64>,
|
||||
section_index: usize,
|
||||
resolved: ResolvedInstructionRef,
|
||||
diff_config: &DiffObjConfig,
|
||||
cb: &mut dyn FnMut(InstructionPart) -> Result<()>,
|
||||
) -> Result<()>;
|
||||
@ -236,9 +223,9 @@ pub trait Arch: Send + Sync + Debug {
|
||||
|
||||
fn demangle(&self, _name: &str) -> Option<String> { None }
|
||||
|
||||
fn display_reloc(&self, flags: RelocationFlags) -> Cow<'static, str>;
|
||||
fn reloc_name(&self, _flags: RelocationFlags) -> Option<&'static str> { None }
|
||||
|
||||
fn get_reloc_byte_size(&self, flags: RelocationFlags) -> usize;
|
||||
fn data_reloc_size(&self, flags: RelocationFlags) -> usize;
|
||||
|
||||
fn symbol_address(&self, address: u64, _kind: SymbolKind) -> u64 { address }
|
||||
|
||||
@ -246,62 +233,25 @@ pub trait Arch: Send + Sync + Debug {
|
||||
SymbolFlagSet::default()
|
||||
}
|
||||
|
||||
fn guess_data_type(
|
||||
fn guess_data_type(&self, _resolved: ResolvedInstructionRef) -> Option<DataType> { None }
|
||||
|
||||
fn symbol_hover(&self, _obj: &Object, _symbol_index: usize) -> Vec<HoverItem> { Vec::new() }
|
||||
|
||||
fn symbol_context(&self, _obj: &Object, _symbol_index: usize) -> Vec<ContextItem> { Vec::new() }
|
||||
|
||||
fn instruction_hover(
|
||||
&self,
|
||||
_ins_ref: InstructionRef,
|
||||
_code: &[u8],
|
||||
_relocation: Option<ResolvedRelocation>,
|
||||
) -> Option<DataType> {
|
||||
None
|
||||
}
|
||||
|
||||
fn display_data_labels(&self, _ty: DataType, bytes: &[u8]) -> Vec<String> {
|
||||
vec![format!("Bytes: {:#x?}", bytes)]
|
||||
}
|
||||
|
||||
fn display_data_literals(&self, _ty: DataType, bytes: &[u8]) -> Vec<String> {
|
||||
vec![format!("{:#?}", bytes)]
|
||||
}
|
||||
|
||||
fn display_ins_data_labels(
|
||||
&self,
|
||||
_ins_ref: InstructionRef,
|
||||
_code: &[u8],
|
||||
_relocation: Option<ResolvedRelocation>,
|
||||
) -> Vec<String> {
|
||||
// TODO
|
||||
// let Some(reloc) = relocation else {
|
||||
// return Vec::new();
|
||||
// };
|
||||
// if reloc.relocation.addend >= 0 && reloc.symbol.bytes.len() > reloc.relocation.addend as usize {
|
||||
// return self
|
||||
// .guess_data_type(ins)
|
||||
// .map(|ty| {
|
||||
// self.display_data_labels(ty, &reloc.target.bytes[reloc.addend as usize..])
|
||||
// })
|
||||
// .unwrap_or_default();
|
||||
// }
|
||||
_obj: &Object,
|
||||
_resolved: ResolvedInstructionRef,
|
||||
) -> Vec<HoverItem> {
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
fn display_ins_data_literals(
|
||||
fn instruction_context(
|
||||
&self,
|
||||
_ins_ref: InstructionRef,
|
||||
_code: &[u8],
|
||||
_relocation: Option<ResolvedRelocation>,
|
||||
) -> Vec<String> {
|
||||
// TODO
|
||||
// let Some(reloc) = ins.reloc.as_ref() else {
|
||||
// return Vec::new();
|
||||
// };
|
||||
// if reloc.addend >= 0 && reloc.target.bytes.len() > reloc.addend as usize {
|
||||
// return self
|
||||
// .guess_data_type(ins)
|
||||
// .map(|ty| {
|
||||
// self.display_data_literals(ty, &reloc.target.bytes[reloc.addend as usize..])
|
||||
// })
|
||||
// .unwrap_or_default();
|
||||
// }
|
||||
_obj: &Object,
|
||||
_resolved: ResolvedInstructionRef,
|
||||
) -> Vec<ContextItem> {
|
||||
Vec::new()
|
||||
}
|
||||
}
|
||||
@ -345,11 +295,7 @@ impl Arch for ArchDummy {
|
||||
|
||||
fn display_instruction(
|
||||
&self,
|
||||
_ins_ref: InstructionRef,
|
||||
_code: &[u8],
|
||||
_relocation: Option<ResolvedRelocation>,
|
||||
_function_range: Range<u64>,
|
||||
_section_index: usize,
|
||||
_resolved: ResolvedInstructionRef,
|
||||
_diff_config: &DiffObjConfig,
|
||||
_cb: &mut dyn FnMut(InstructionPart) -> Result<()>,
|
||||
) -> Result<()> {
|
||||
@ -358,18 +304,14 @@ impl Arch for ArchDummy {
|
||||
|
||||
fn implcit_addend(
|
||||
&self,
|
||||
_file: &File<'_>,
|
||||
_section: &Section,
|
||||
_file: &object::File<'_>,
|
||||
_section: &object::Section,
|
||||
_address: u64,
|
||||
_relocation: &Relocation,
|
||||
_relocation: &object::Relocation,
|
||||
_flags: RelocationFlags,
|
||||
) -> Result<i64> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn display_reloc(&self, flags: RelocationFlags) -> Cow<'static, str> {
|
||||
format!("{flags:?}").into()
|
||||
}
|
||||
|
||||
fn get_reloc_byte_size(&self, _flags: RelocationFlags) -> usize { 0 }
|
||||
fn data_reloc_size(&self, _flags: RelocationFlags) -> usize { 0 }
|
||||
}
|
||||
|
@ -1,25 +1,24 @@
|
||||
use alloc::{
|
||||
borrow::Cow,
|
||||
collections::BTreeMap,
|
||||
format,
|
||||
string::{String, ToString},
|
||||
vec,
|
||||
vec::Vec,
|
||||
};
|
||||
use core::ops::Range;
|
||||
|
||||
use anyhow::{bail, ensure, Result};
|
||||
use byteorder::BigEndian;
|
||||
use cwextab::{decode_extab, ExceptionTableData};
|
||||
use flagset::Flags;
|
||||
use object::{elf, Object as _, ObjectSection as _, ObjectSymbol as _};
|
||||
|
||||
use crate::{
|
||||
arch::{Arch, DataType},
|
||||
diff::{display::InstructionPart, DiffObjConfig},
|
||||
diff::{
|
||||
display::{ContextItem, HoverItem, HoverItemColor, InstructionPart, SymbolNavigationKind},
|
||||
DiffObjConfig,
|
||||
},
|
||||
obj::{
|
||||
InstructionRef, Relocation, RelocationFlags, ResolvedRelocation, ScannedInstruction,
|
||||
Symbol, SymbolFlag, SymbolFlagSet,
|
||||
InstructionRef, Object, Relocation, RelocationFlags, ResolvedInstructionRef,
|
||||
ResolvedRelocation, ScannedInstruction, SymbolFlag, SymbolFlagSet,
|
||||
},
|
||||
};
|
||||
|
||||
@ -54,6 +53,15 @@ impl ArchPpc {
|
||||
Ok(Self { extab: decode_exception_info(file)? })
|
||||
}
|
||||
|
||||
fn parse_ins_ref(&self, resolved: ResolvedInstructionRef) -> Result<ppc750cl::Ins> {
|
||||
let mut code = u32::from_be_bytes(resolved.code.try_into()?);
|
||||
if let Some(reloc) = resolved.relocation {
|
||||
code = zero_reloc(code, reloc.relocation);
|
||||
}
|
||||
let op = ppc750cl::Opcode::from(resolved.ins_ref.opcode as u8);
|
||||
Ok(ppc750cl::Ins { code, op })
|
||||
}
|
||||
|
||||
fn find_reloc_arg(
|
||||
&self,
|
||||
ins: &ppc750cl::ParsedIns,
|
||||
@ -98,24 +106,15 @@ impl Arch for ArchPpc {
|
||||
|
||||
fn display_instruction(
|
||||
&self,
|
||||
ins_ref: InstructionRef,
|
||||
code: &[u8],
|
||||
relocation: Option<ResolvedRelocation>,
|
||||
_function_range: Range<u64>,
|
||||
_section_index: usize,
|
||||
resolved: ResolvedInstructionRef,
|
||||
_diff_config: &DiffObjConfig,
|
||||
cb: &mut dyn FnMut(InstructionPart) -> Result<()>,
|
||||
) -> Result<()> {
|
||||
let mut code = u32::from_be_bytes(code.try_into()?);
|
||||
if let Some(resolved) = relocation {
|
||||
code = zero_reloc(code, resolved.relocation);
|
||||
}
|
||||
let op = ppc750cl::Opcode::from(ins_ref.opcode as u8);
|
||||
let ins = ppc750cl::Ins { code, op }.simplified();
|
||||
let ins = self.parse_ins_ref(resolved)?.simplified();
|
||||
|
||||
cb(InstructionPart::opcode(ins.mnemonic, ins_ref.opcode))?;
|
||||
cb(InstructionPart::opcode(ins.mnemonic, resolved.ins_ref.opcode))?;
|
||||
|
||||
let reloc_arg = self.find_reloc_arg(&ins, relocation);
|
||||
let reloc_arg = self.find_reloc_arg(&ins, resolved.relocation);
|
||||
|
||||
let mut writing_offset = false;
|
||||
for (idx, arg) in ins.args_iter().enumerate() {
|
||||
@ -124,10 +123,10 @@ impl Arch for ArchPpc {
|
||||
}
|
||||
|
||||
if reloc_arg == Some(idx) {
|
||||
let resolved = relocation.unwrap();
|
||||
display_reloc(resolved, cb)?;
|
||||
let reloc = resolved.relocation.unwrap();
|
||||
display_reloc(reloc, cb)?;
|
||||
// For @sda21, we can omit the register argument
|
||||
if matches!(resolved.relocation.flags, RelocationFlags::Elf(elf::R_PPC_EMB_SDA21))
|
||||
if matches!(reloc.relocation.flags, RelocationFlags::Elf(elf::R_PPC_EMB_SDA21))
|
||||
// Sanity check: the next argument should be r0
|
||||
&& matches!(ins.args.get(idx + 1), Some(ppc750cl::Argument::GPR(ppc750cl::GPR(0))))
|
||||
{
|
||||
@ -139,7 +138,7 @@ impl Arch for ArchPpc {
|
||||
ppc750cl::Argument::Uimm(uimm) => cb(InstructionPart::unsigned(uimm.0)),
|
||||
ppc750cl::Argument::Offset(offset) => cb(InstructionPart::signed(offset.0)),
|
||||
ppc750cl::Argument::BranchDest(dest) => cb(InstructionPart::branch_dest(
|
||||
(ins_ref.address as u32).wrapping_add_signed(dest.0),
|
||||
(resolved.ins_ref.address as u32).wrapping_add_signed(dest.0),
|
||||
)),
|
||||
_ => cb(InstructionPart::opaque(arg.to_string())),
|
||||
}?;
|
||||
@ -173,33 +172,25 @@ impl Arch for ArchPpc {
|
||||
cwdemangle::demangle(name, &cwdemangle::DemangleOptions::default())
|
||||
}
|
||||
|
||||
fn display_reloc(&self, flags: RelocationFlags) -> Cow<'static, str> {
|
||||
fn reloc_name(&self, flags: RelocationFlags) -> Option<&'static str> {
|
||||
match flags {
|
||||
RelocationFlags::Elf(r_type) => match r_type {
|
||||
elf::R_PPC_NONE => Cow::Borrowed("R_PPC_NONE"), // We use this for fake pool relocs
|
||||
elf::R_PPC_ADDR16_LO => Cow::Borrowed("R_PPC_ADDR16_LO"),
|
||||
elf::R_PPC_ADDR16_HI => Cow::Borrowed("R_PPC_ADDR16_HI"),
|
||||
elf::R_PPC_ADDR16_HA => Cow::Borrowed("R_PPC_ADDR16_HA"),
|
||||
elf::R_PPC_EMB_SDA21 => Cow::Borrowed("R_PPC_EMB_SDA21"),
|
||||
elf::R_PPC_ADDR32 => Cow::Borrowed("R_PPC_ADDR32"),
|
||||
elf::R_PPC_UADDR32 => Cow::Borrowed("R_PPC_UADDR32"),
|
||||
elf::R_PPC_REL24 => Cow::Borrowed("R_PPC_REL24"),
|
||||
elf::R_PPC_REL14 => Cow::Borrowed("R_PPC_REL14"),
|
||||
_ => Cow::Owned(format!("<{flags:?}>")),
|
||||
elf::R_PPC_NONE => Some("R_PPC_NONE"), // We use this for fake pool relocs
|
||||
elf::R_PPC_ADDR16_LO => Some("R_PPC_ADDR16_LO"),
|
||||
elf::R_PPC_ADDR16_HI => Some("R_PPC_ADDR16_HI"),
|
||||
elf::R_PPC_ADDR16_HA => Some("R_PPC_ADDR16_HA"),
|
||||
elf::R_PPC_EMB_SDA21 => Some("R_PPC_EMB_SDA21"),
|
||||
elf::R_PPC_ADDR32 => Some("R_PPC_ADDR32"),
|
||||
elf::R_PPC_UADDR32 => Some("R_PPC_UADDR32"),
|
||||
elf::R_PPC_REL24 => Some("R_PPC_REL24"),
|
||||
elf::R_PPC_REL14 => Some("R_PPC_REL14"),
|
||||
_ => None,
|
||||
},
|
||||
_ => Cow::Owned(format!("<{flags:?}>")),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn extra_symbol_flags(&self, symbol: &object::Symbol) -> SymbolFlagSet {
|
||||
if self.extab.as_ref().is_some_and(|extab| extab.contains_key(&symbol.index().0)) {
|
||||
SymbolFlag::HasExtra.into()
|
||||
} else {
|
||||
SymbolFlag::none()
|
||||
}
|
||||
}
|
||||
|
||||
fn get_reloc_byte_size(&self, flags: RelocationFlags) -> usize {
|
||||
fn data_reloc_size(&self, flags: RelocationFlags) -> usize {
|
||||
match flags {
|
||||
RelocationFlags::Elf(r_type) => match r_type {
|
||||
elf::R_PPC_ADDR32 => 4,
|
||||
@ -210,33 +201,102 @@ impl Arch for ArchPpc {
|
||||
}
|
||||
}
|
||||
|
||||
fn guess_data_type(
|
||||
&self,
|
||||
ins_ref: InstructionRef,
|
||||
_code: &[u8],
|
||||
relocation: Option<ResolvedRelocation>,
|
||||
) -> Option<DataType> {
|
||||
if relocation.is_some_and(|r| r.symbol.name.starts_with("@stringBase")) {
|
||||
fn extra_symbol_flags(&self, symbol: &object::Symbol) -> SymbolFlagSet {
|
||||
if self.extab.as_ref().is_some_and(|extab| extab.contains_key(&(symbol.index().0 - 1))) {
|
||||
SymbolFlag::HasExtra.into()
|
||||
} else {
|
||||
SymbolFlag::none()
|
||||
}
|
||||
}
|
||||
|
||||
fn guess_data_type(&self, resolved: ResolvedInstructionRef) -> Option<DataType> {
|
||||
if resolved.relocation.is_some_and(|r| r.symbol.name.starts_with("@stringBase")) {
|
||||
return Some(DataType::String);
|
||||
}
|
||||
|
||||
guess_data_type_from_load_store_inst_op(ppc750cl::Opcode::from(ins_ref.opcode as u8))
|
||||
let opcode = ppc750cl::Opcode::from(resolved.ins_ref.opcode as u8);
|
||||
guess_data_type_from_load_store_inst_op(opcode)
|
||||
}
|
||||
|
||||
fn display_data_labels(&self, ty: DataType, bytes: &[u8]) -> Vec<String> {
|
||||
ty.display_labels::<BigEndian>(bytes)
|
||||
fn symbol_hover(&self, _obj: &Object, symbol_index: usize) -> Vec<HoverItem> {
|
||||
let mut out = Vec::new();
|
||||
if let Some(extab) = self.extab_for_symbol(symbol_index) {
|
||||
out.push(HoverItem::Text {
|
||||
label: "extab symbol".into(),
|
||||
value: extab.etb_symbol.name.clone(),
|
||||
color: HoverItemColor::Special,
|
||||
});
|
||||
out.push(HoverItem::Text {
|
||||
label: "extabindex symbol".into(),
|
||||
value: extab.eti_symbol.name.clone(),
|
||||
color: HoverItemColor::Special,
|
||||
});
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
fn display_data_literals(&self, ty: DataType, bytes: &[u8]) -> Vec<String> {
|
||||
ty.display_literals::<BigEndian>(bytes)
|
||||
fn symbol_context(&self, _obj: &Object, symbol_index: usize) -> Vec<ContextItem> {
|
||||
let mut out = Vec::new();
|
||||
if let Some(_extab) = self.extab_for_symbol(symbol_index) {
|
||||
out.push(ContextItem::Navigate {
|
||||
label: "Decode exception table".to_string(),
|
||||
symbol_index,
|
||||
kind: SymbolNavigationKind::Extab,
|
||||
});
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
fn instruction_hover(&self, _obj: &Object, resolved: ResolvedInstructionRef) -> Vec<HoverItem> {
|
||||
let Ok(ins) = self.parse_ins_ref(resolved) else {
|
||||
return Vec::new();
|
||||
};
|
||||
let orig = ins.basic().to_string();
|
||||
let simplified = ins.simplified().to_string();
|
||||
let show_orig = orig != simplified;
|
||||
let rlwinm_decoded = rlwinmdec::decode(&orig);
|
||||
let mut out = Vec::with_capacity(2);
|
||||
if show_orig {
|
||||
out.push(HoverItem::Text {
|
||||
label: "Original".into(),
|
||||
value: orig,
|
||||
color: HoverItemColor::Normal,
|
||||
});
|
||||
}
|
||||
if let Some(decoded) = rlwinm_decoded {
|
||||
for line in decoded.lines() {
|
||||
out.push(HoverItem::Text {
|
||||
label: Default::default(),
|
||||
value: line.to_string(),
|
||||
color: HoverItemColor::Special,
|
||||
});
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
fn instruction_context(
|
||||
&self,
|
||||
_obj: &Object,
|
||||
resolved: ResolvedInstructionRef,
|
||||
) -> Vec<ContextItem> {
|
||||
let Ok(ins) = self.parse_ins_ref(resolved) else {
|
||||
return Vec::new();
|
||||
};
|
||||
let orig = ins.basic().to_string();
|
||||
let simplified = ins.simplified().to_string();
|
||||
let show_orig = orig != simplified;
|
||||
let mut out = Vec::with_capacity(2);
|
||||
out.push(ContextItem::Copy { value: simplified, label: None });
|
||||
if show_orig {
|
||||
out.push(ContextItem::Copy { value: orig, label: Some("original".to_string()) });
|
||||
}
|
||||
out
|
||||
}
|
||||
}
|
||||
|
||||
impl ArchPpc {
|
||||
pub fn extab_for_symbol(&self, _symbol: &Symbol) -> Option<&ExceptionInfo> {
|
||||
// TODO
|
||||
// symbol.original_index.and_then(|i| self.extab.as_ref()?.get(&i))
|
||||
None
|
||||
pub fn extab_for_symbol(&self, symbol_index: usize) -> Option<&ExceptionInfo> {
|
||||
self.extab.as_ref()?.get(&symbol_index)
|
||||
}
|
||||
}
|
||||
|
||||
@ -384,7 +444,7 @@ fn decode_exception_info(
|
||||
};
|
||||
|
||||
//Add the new entry to the list
|
||||
result.insert(extab_func.index().0, ExceptionInfo {
|
||||
result.insert(extab_func.index().0 - 1, ExceptionInfo {
|
||||
eti_symbol: make_symbol_ref(&extabindex)?,
|
||||
etb_symbol: make_symbol_ref(&extab)?,
|
||||
data,
|
||||
@ -419,7 +479,7 @@ fn relocation_symbol<'data, 'file>(
|
||||
fn make_symbol_ref(symbol: &object::Symbol) -> Result<ExtabSymbolRef> {
|
||||
let name = symbol.name()?.to_string();
|
||||
let demangled_name = cwdemangle::demangle(&name, &cwdemangle::DemangleOptions::default());
|
||||
Ok(ExtabSymbolRef { original_index: symbol.index().0, name, demangled_name })
|
||||
Ok(ExtabSymbolRef { original_index: symbol.index().0 - 1, name, demangled_name })
|
||||
}
|
||||
|
||||
fn guess_data_type_from_load_store_inst_op(inst_op: ppc750cl::Opcode) -> Option<DataType> {
|
||||
|
@ -1,5 +1,4 @@
|
||||
use alloc::{borrow::Cow, boxed::Box, format, string::String, vec::Vec};
|
||||
use core::ops::Range;
|
||||
use alloc::{boxed::Box, string::String, vec::Vec};
|
||||
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use iced_x86::{
|
||||
@ -11,7 +10,7 @@ use object::{pe, Endian as _, Object as _, ObjectSection as _};
|
||||
use crate::{
|
||||
arch::Arch,
|
||||
diff::{display::InstructionPart, DiffObjConfig, X86Formatter},
|
||||
obj::{InstructionRef, RelocationFlags, ResolvedRelocation, ScannedInstruction},
|
||||
obj::{InstructionRef, RelocationFlags, ResolvedInstructionRef, ScannedInstruction},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -74,15 +73,11 @@ impl Arch for ArchX86 {
|
||||
|
||||
fn display_instruction(
|
||||
&self,
|
||||
ins_ref: InstructionRef,
|
||||
code: &[u8],
|
||||
relocation: Option<ResolvedRelocation>,
|
||||
_function_range: Range<u64>,
|
||||
_section_index: usize,
|
||||
resolved: ResolvedInstructionRef,
|
||||
diff_config: &DiffObjConfig,
|
||||
cb: &mut dyn FnMut(InstructionPart) -> Result<()>,
|
||||
) -> Result<()> {
|
||||
let mut decoder = self.decoder(code, ins_ref.address);
|
||||
let mut decoder = self.decoder(resolved.code, resolved.ins_ref.address);
|
||||
let mut formatter = self.formatter(diff_config);
|
||||
let mut instruction = Instruction::default();
|
||||
decoder.decode_out(&mut instruction);
|
||||
@ -92,11 +87,11 @@ impl Arch for ArchX86 {
|
||||
// doesn't provide enough information to know which number is the displacement inside a
|
||||
// memory operand.
|
||||
let mut reloc_replace = None;
|
||||
if let Some(resolved) = relocation {
|
||||
const PLACEHOLDER: u64 = 0x7BDEBE7D; // chosen by fair dice roll.
|
||||
if let Some(reloc) = resolved.relocation {
|
||||
const PLACEHOLDER: u64 = 0x7BDE3E7D; // chosen by fair dice roll.
|
||||
// guaranteed to be random.
|
||||
let reloc_offset = resolved.relocation.address - ins_ref.address;
|
||||
let reloc_size = reloc_size(resolved.relocation.flags).unwrap_or(usize::MAX);
|
||||
let reloc_offset = reloc.relocation.address - resolved.ins_ref.address;
|
||||
let reloc_size = reloc_size(reloc.relocation.flags).unwrap_or(usize::MAX);
|
||||
let offsets = decoder.get_constant_offsets(&instruction);
|
||||
if reloc_offset == offsets.displacement_offset() as u64
|
||||
&& reloc_size == offsets.displacement_size()
|
||||
@ -172,20 +167,18 @@ impl Arch for ArchX86 {
|
||||
}
|
||||
}
|
||||
|
||||
fn display_reloc(&self, flags: RelocationFlags) -> Cow<'static, str> {
|
||||
fn reloc_name(&self, flags: RelocationFlags) -> Option<&'static str> {
|
||||
match flags {
|
||||
RelocationFlags::Coff(typ) => match typ {
|
||||
pe::IMAGE_REL_I386_DIR32 => Cow::Borrowed("IMAGE_REL_I386_DIR32"),
|
||||
pe::IMAGE_REL_I386_REL32 => Cow::Borrowed("IMAGE_REL_I386_REL32"),
|
||||
_ => Cow::Owned(format!("<{flags:?}>")),
|
||||
pe::IMAGE_REL_I386_DIR32 => Some("IMAGE_REL_I386_DIR32"),
|
||||
pe::IMAGE_REL_I386_REL32 => Some("IMAGE_REL_I386_REL32"),
|
||||
_ => None,
|
||||
},
|
||||
_ => Cow::Owned(format!("<{flags:?}>")),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_reloc_byte_size(&self, flags: RelocationFlags) -> usize {
|
||||
reloc_size(flags).unwrap_or(1)
|
||||
}
|
||||
fn data_reloc_size(&self, flags: RelocationFlags) -> usize { reloc_size(flags).unwrap_or(1) }
|
||||
}
|
||||
|
||||
fn reloc_size(flags: RelocationFlags) -> Option<usize> {
|
||||
@ -346,7 +339,7 @@ impl FormatterOutput for InstructionFormatterOutput<'_> {
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::obj::Relocation;
|
||||
use crate::obj::{Relocation, ResolvedRelocation};
|
||||
|
||||
#[test]
|
||||
fn test_scan_instructions() {
|
||||
@ -374,11 +367,11 @@ mod test {
|
||||
let opcode = iced_x86::Mnemonic::Mov as u16;
|
||||
let mut parts = Vec::new();
|
||||
arch.display_instruction(
|
||||
InstructionRef { address: 0x1234, size: 10, opcode },
|
||||
&code,
|
||||
None,
|
||||
0x1234..0x2000,
|
||||
0,
|
||||
ResolvedInstructionRef {
|
||||
ins_ref: InstructionRef { address: 0x1234, size: 10, opcode },
|
||||
code: &code,
|
||||
..Default::default()
|
||||
},
|
||||
&DiffObjConfig::default(),
|
||||
&mut |part| {
|
||||
parts.push(part.into_static());
|
||||
@ -410,19 +403,20 @@ mod test {
|
||||
let opcode = iced_x86::Mnemonic::Mov as u16;
|
||||
let mut parts = Vec::new();
|
||||
arch.display_instruction(
|
||||
InstructionRef { address: 0x1234, size: 10, opcode },
|
||||
&code,
|
||||
Some(ResolvedRelocation {
|
||||
relocation: &Relocation {
|
||||
flags: RelocationFlags::Coff(pe::IMAGE_REL_I386_DIR32),
|
||||
address: 0x1234 + 6,
|
||||
target_symbol: 0,
|
||||
addend: 0,
|
||||
},
|
||||
symbol: &Default::default(),
|
||||
}),
|
||||
0x1234..0x2000,
|
||||
0,
|
||||
ResolvedInstructionRef {
|
||||
ins_ref: InstructionRef { address: 0x1234, size: 10, opcode },
|
||||
code: &code,
|
||||
relocation: Some(ResolvedRelocation {
|
||||
relocation: &Relocation {
|
||||
flags: RelocationFlags::Coff(pe::IMAGE_REL_I386_DIR32),
|
||||
address: 0x1234 + 6,
|
||||
target_symbol: 0,
|
||||
addend: 0,
|
||||
},
|
||||
symbol: &Default::default(),
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
&DiffObjConfig::default(),
|
||||
&mut |part| {
|
||||
parts.push(part.into_static());
|
||||
@ -454,19 +448,20 @@ mod test {
|
||||
let opcode = iced_x86::Mnemonic::Mov as u16;
|
||||
let mut parts = Vec::new();
|
||||
arch.display_instruction(
|
||||
InstructionRef { address: 0x1234, size: 7, opcode },
|
||||
&code,
|
||||
Some(ResolvedRelocation {
|
||||
relocation: &Relocation {
|
||||
flags: RelocationFlags::Coff(pe::IMAGE_REL_I386_DIR32),
|
||||
address: 0x1234 + 3,
|
||||
target_symbol: 0,
|
||||
addend: 0,
|
||||
},
|
||||
symbol: &Default::default(),
|
||||
}),
|
||||
0x1234..0x2000,
|
||||
0,
|
||||
ResolvedInstructionRef {
|
||||
ins_ref: InstructionRef { address: 0x1234, size: 7, opcode },
|
||||
code: &code,
|
||||
relocation: Some(ResolvedRelocation {
|
||||
relocation: &Relocation {
|
||||
flags: RelocationFlags::Coff(pe::IMAGE_REL_I386_DIR32),
|
||||
address: 0x1234 + 3,
|
||||
target_symbol: 0,
|
||||
addend: 0,
|
||||
},
|
||||
symbol: &Default::default(),
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
&DiffObjConfig::default(),
|
||||
&mut |part| {
|
||||
parts.push(part.into_static());
|
||||
@ -496,19 +491,20 @@ mod test {
|
||||
let opcode = iced_x86::Mnemonic::Call as u16;
|
||||
let mut parts = Vec::new();
|
||||
arch.display_instruction(
|
||||
InstructionRef { address: 0x1234, size: 5, opcode },
|
||||
&code,
|
||||
Some(ResolvedRelocation {
|
||||
relocation: &Relocation {
|
||||
flags: RelocationFlags::Coff(pe::IMAGE_REL_I386_REL32),
|
||||
address: 0x1234 + 1,
|
||||
target_symbol: 0,
|
||||
addend: 0,
|
||||
},
|
||||
symbol: &Default::default(),
|
||||
}),
|
||||
0x1234..0x2000,
|
||||
0,
|
||||
ResolvedInstructionRef {
|
||||
ins_ref: InstructionRef { address: 0x1234, size: 5, opcode },
|
||||
code: &code,
|
||||
relocation: Some(ResolvedRelocation {
|
||||
relocation: &Relocation {
|
||||
flags: RelocationFlags::Coff(pe::IMAGE_REL_I386_REL32),
|
||||
address: 0x1234 + 1,
|
||||
target_symbol: 0,
|
||||
addend: 0,
|
||||
},
|
||||
symbol: &Default::default(),
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
&DiffObjConfig::default(),
|
||||
&mut |part| {
|
||||
parts.push(part.into_static());
|
||||
|
@ -5,7 +5,7 @@ use alloc::{
|
||||
vec::Vec,
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, ensure, Result};
|
||||
use anyhow::{anyhow, ensure, Context, Result};
|
||||
|
||||
use super::{
|
||||
DiffObjConfig, FunctionRelocDiffs, InstructionArgDiffIndex, InstructionBranchFrom,
|
||||
@ -196,7 +196,7 @@ fn diff_instructions(
|
||||
Ok((left_diff, right_diff))
|
||||
}
|
||||
|
||||
fn arg_to_string(arg: &InstructionArg, reloc: Option<&ResolvedRelocation>) -> String {
|
||||
fn arg_to_string(arg: &InstructionArg, reloc: Option<ResolvedRelocation>) -> String {
|
||||
match arg {
|
||||
InstructionArg::Value(arg) => arg.to_string(),
|
||||
InstructionArg::Reloc => {
|
||||
@ -226,7 +226,7 @@ fn resolve_branches(
|
||||
for ((i, ins_diff), ins) in
|
||||
rows.iter_mut().enumerate().filter(|(_, row)| row.ins_ref.is_some()).zip(ops)
|
||||
{
|
||||
let branch_dest = if let Some(resolved) = section.relocation_at(ins.ins_ref, obj) {
|
||||
let branch_dest = if let Some(resolved) = section.relocation_at(obj, ins.ins_ref) {
|
||||
if resolved.symbol.section == Some(section_index) {
|
||||
// If the relocation target is in the same section, use it as the branch destination
|
||||
resolved.symbol.address.checked_add_signed(resolved.relocation.addend)
|
||||
@ -258,7 +258,7 @@ fn resolve_branches(
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn address_eq(left: &ResolvedRelocation, right: &ResolvedRelocation) -> bool {
|
||||
pub(crate) fn address_eq(left: ResolvedRelocation, right: ResolvedRelocation) -> bool {
|
||||
if right.symbol.size == 0 && left.symbol.size != 0 {
|
||||
// The base relocation is against a pool but the target relocation isn't.
|
||||
// This can happen in rare cases where the compiler will generate a pool+addend relocation
|
||||
@ -318,7 +318,7 @@ fn reloc_eq(
|
||||
section_name_eq(left_obj, right_obj, *sl, *sr)
|
||||
&& (diff_config.function_reloc_diffs == FunctionRelocDiffs::DataValue
|
||||
|| symbol_name_addend_matches
|
||||
|| address_eq(&left_reloc, &right_reloc))
|
||||
|| address_eq(left_reloc, right_reloc))
|
||||
&& (
|
||||
diff_config.function_reloc_diffs == FunctionRelocDiffs::NameAddress
|
||||
|| left_reloc.symbol.kind != SymbolKind::Object
|
||||
@ -427,54 +427,17 @@ fn diff_instruction(
|
||||
return Ok(InstructionDiffResult::new(InstructionDiffKind::Replace));
|
||||
}
|
||||
|
||||
let left_symbol = &left_obj.symbols[left_symbol_idx];
|
||||
let right_symbol = &right_obj.symbols[right_symbol_idx];
|
||||
let left_section = left_symbol
|
||||
.section
|
||||
.and_then(|i| left_obj.sections.get(i))
|
||||
.ok_or_else(|| anyhow!("Missing section for symbol"))?;
|
||||
let right_section = right_symbol
|
||||
.section
|
||||
.and_then(|i| right_obj.sections.get(i))
|
||||
.ok_or_else(|| anyhow!("Missing section for symbol"))?;
|
||||
let left_resolved = left_obj
|
||||
.resolve_instruction_ref(left_symbol_idx, l)
|
||||
.context("Failed to resolve left instruction")?;
|
||||
let right_resolved = right_obj
|
||||
.resolve_instruction_ref(right_symbol_idx, r)
|
||||
.context("Failed to resolve right instruction")?;
|
||||
|
||||
// Resolve relocations
|
||||
let left_reloc = left_section.relocation_at(l, left_obj);
|
||||
let right_reloc = right_section.relocation_at(r, right_obj);
|
||||
|
||||
// Compare instruction data
|
||||
let left_data = left_section.data_range(l.address, l.size as usize).ok_or_else(|| {
|
||||
anyhow!(
|
||||
"Instruction data out of bounds: {:#x}..{:#x}",
|
||||
l.address,
|
||||
l.address + l.size as u64
|
||||
)
|
||||
})?;
|
||||
let right_data = right_section.data_range(r.address, r.size as usize).ok_or_else(|| {
|
||||
anyhow!(
|
||||
"Instruction data out of bounds: {:#x}..{:#x}",
|
||||
r.address,
|
||||
r.address + r.size as u64
|
||||
)
|
||||
})?;
|
||||
if left_data != right_data {
|
||||
if left_resolved.code != right_resolved.code {
|
||||
// If data doesn't match, process instructions and compare args
|
||||
let left_ins = left_obj.arch.process_instruction(
|
||||
l,
|
||||
left_data,
|
||||
left_reloc,
|
||||
left_symbol.address..left_symbol.address + left_symbol.size,
|
||||
left_symbol.section.unwrap(),
|
||||
diff_config,
|
||||
)?;
|
||||
let right_ins = left_obj.arch.process_instruction(
|
||||
r,
|
||||
right_data,
|
||||
right_reloc,
|
||||
right_symbol.address..right_symbol.address + right_symbol.size,
|
||||
right_symbol.section.unwrap(),
|
||||
diff_config,
|
||||
)?;
|
||||
let left_ins = left_obj.arch.process_instruction(left_resolved, diff_config)?;
|
||||
let right_ins = left_obj.arch.process_instruction(right_resolved, diff_config)?;
|
||||
if left_ins.args.len() != right_ins.args.len() {
|
||||
state.diff_score += PENALTY_REPLACE;
|
||||
return Ok(InstructionDiffResult::new(InstructionDiffKind::Replace));
|
||||
@ -492,8 +455,8 @@ fn diff_instruction(
|
||||
right_row,
|
||||
a,
|
||||
b,
|
||||
left_reloc,
|
||||
right_reloc,
|
||||
left_resolved.relocation,
|
||||
right_resolved.relocation,
|
||||
diff_config,
|
||||
) {
|
||||
result.left_args_diff.push(InstructionArgDiffIndex::NONE);
|
||||
@ -510,7 +473,7 @@ fn diff_instruction(
|
||||
if result.kind == InstructionDiffKind::None {
|
||||
result.kind = InstructionDiffKind::ArgMismatch;
|
||||
}
|
||||
let a_str = arg_to_string(a, left_reloc.as_ref());
|
||||
let a_str = arg_to_string(a, left_resolved.relocation);
|
||||
let a_diff = match state.left_args_idx.entry(a_str) {
|
||||
btree_map::Entry::Vacant(e) => {
|
||||
let idx = state.left_arg_idx;
|
||||
@ -520,7 +483,7 @@ fn diff_instruction(
|
||||
}
|
||||
btree_map::Entry::Occupied(e) => *e.get(),
|
||||
};
|
||||
let b_str = arg_to_string(b, right_reloc.as_ref());
|
||||
let b_str = arg_to_string(b, right_resolved.relocation);
|
||||
let b_diff = match state.right_args_idx.entry(b_str) {
|
||||
btree_map::Entry::Vacant(e) => {
|
||||
let idx = state.right_arg_idx;
|
||||
@ -538,8 +501,15 @@ fn diff_instruction(
|
||||
}
|
||||
|
||||
// Compare relocations
|
||||
if !reloc_eq(left_obj, right_obj, left_reloc, right_reloc, diff_config) {
|
||||
if !reloc_eq(
|
||||
left_obj,
|
||||
right_obj,
|
||||
left_resolved.relocation,
|
||||
right_resolved.relocation,
|
||||
diff_config,
|
||||
) {
|
||||
state.diff_score += PENALTY_REG_DIFF;
|
||||
// TODO add relocation diff to args
|
||||
return Ok(InstructionDiffResult::new(InstructionDiffKind::ArgMismatch));
|
||||
}
|
||||
|
||||
|
@ -38,8 +38,8 @@ pub fn diff_bss_symbol(
|
||||
fn reloc_eq(
|
||||
left_obj: &Object,
|
||||
right_obj: &Object,
|
||||
left: &ResolvedRelocation,
|
||||
right: &ResolvedRelocation,
|
||||
left: ResolvedRelocation,
|
||||
right: ResolvedRelocation,
|
||||
) -> bool {
|
||||
if left.relocation.flags != right.relocation.flags {
|
||||
return false;
|
||||
@ -104,7 +104,7 @@ fn diff_data_relocs_for_range<'left, 'right>(
|
||||
continue;
|
||||
};
|
||||
let right_reloc = resolve_relocation(right_obj, right_reloc);
|
||||
if reloc_eq(left_obj, right_obj, &left_reloc, &right_reloc) {
|
||||
if reloc_eq(left_obj, right_obj, left_reloc, right_reloc) {
|
||||
diffs.push((DataDiffKind::None, Some(left_reloc), Some(right_reloc)));
|
||||
} else {
|
||||
diffs.push((DataDiffKind::Replace, Some(left_reloc), Some(right_reloc)));
|
||||
@ -251,13 +251,13 @@ pub fn diff_data_section(
|
||||
0..right_max as usize,
|
||||
) {
|
||||
if let Some(left_reloc) = left_reloc {
|
||||
let len = left_obj.arch.get_reloc_byte_size(left_reloc.relocation.flags);
|
||||
let len = left_obj.arch.data_reloc_size(left_reloc.relocation.flags);
|
||||
let range = left_reloc.relocation.address as usize
|
||||
..left_reloc.relocation.address as usize + len;
|
||||
left_reloc_diffs.push(DataRelocationDiff { kind: diff_kind, range });
|
||||
}
|
||||
if let Some(right_reloc) = right_reloc {
|
||||
let len = right_obj.arch.get_reloc_byte_size(right_reloc.relocation.flags);
|
||||
let len = right_obj.arch.data_reloc_size(right_reloc.relocation.flags);
|
||||
let range = right_reloc.relocation.address as usize
|
||||
..right_reloc.relocation.address as usize + len;
|
||||
right_reloc_diffs.push(DataRelocationDiff { kind: diff_kind, range });
|
||||
@ -358,11 +358,9 @@ pub fn diff_data_symbol(
|
||||
let reloc_diff_len = match (left_reloc, right_reloc) {
|
||||
(None, None) => unreachable!(),
|
||||
(None, Some(right_reloc)) => {
|
||||
right_obj.arch.get_reloc_byte_size(right_reloc.relocation.flags)
|
||||
}
|
||||
(Some(left_reloc), _) => {
|
||||
left_obj.arch.get_reloc_byte_size(left_reloc.relocation.flags)
|
||||
right_obj.arch.data_reloc_size(right_reloc.relocation.flags)
|
||||
}
|
||||
(Some(left_reloc), _) => left_obj.arch.data_reloc_size(left_reloc.relocation.flags),
|
||||
};
|
||||
total_reloc_bytes += reloc_diff_len;
|
||||
if diff_kind == DataDiffKind::None {
|
||||
|
@ -14,8 +14,8 @@ use regex::Regex;
|
||||
use crate::{
|
||||
diff::{DiffObjConfig, InstructionDiffKind, InstructionDiffRow, ObjectDiff, SymbolDiff},
|
||||
obj::{
|
||||
InstructionArg, InstructionArgValue, Object, SectionFlag, SectionKind, Symbol, SymbolFlag,
|
||||
SymbolKind,
|
||||
InstructionArg, InstructionArgValue, Object, ParsedInstruction, ResolvedInstructionRef,
|
||||
SectionFlag, SectionKind, Symbol, SymbolFlag, SymbolKind,
|
||||
},
|
||||
};
|
||||
|
||||
@ -159,24 +159,24 @@ pub fn display_row(
|
||||
cb(EOL_SEGMENT)?;
|
||||
return Ok(());
|
||||
};
|
||||
let symbol = &obj.symbols[symbol_index];
|
||||
let Some(section_index) = symbol.section else {
|
||||
let Some(resolved) = obj.resolve_instruction_ref(symbol_index, ins_ref) else {
|
||||
cb(DiffTextSegment::basic("<invalid>", DiffTextColor::Delete))?;
|
||||
cb(EOL_SEGMENT)?;
|
||||
return Ok(());
|
||||
};
|
||||
let section = &obj.sections[section_index];
|
||||
let Some(data) = section.data_range(ins_ref.address, ins_ref.size as usize) else {
|
||||
cb(DiffTextSegment::basic("<invalid>", DiffTextColor::Delete))?;
|
||||
cb(EOL_SEGMENT)?;
|
||||
return Ok(());
|
||||
let base_color = match ins_row.kind {
|
||||
InstructionDiffKind::Replace => DiffTextColor::Replace,
|
||||
InstructionDiffKind::Delete => DiffTextColor::Delete,
|
||||
InstructionDiffKind::Insert => DiffTextColor::Insert,
|
||||
_ => DiffTextColor::Normal,
|
||||
};
|
||||
if let Some(line) = section.line_info.range(..=ins_ref.address).last().map(|(_, &b)| b) {
|
||||
if let Some(line) = resolved.section.line_info.range(..=ins_ref.address).last().map(|(_, &b)| b)
|
||||
{
|
||||
cb(DiffTextSegment { text: DiffText::Line(line), color: DiffTextColor::Dim, pad_to: 5 })?;
|
||||
}
|
||||
cb(DiffTextSegment {
|
||||
text: DiffText::Address(ins_ref.address.saturating_sub(symbol.address)),
|
||||
color: DiffTextColor::Normal,
|
||||
text: DiffText::Address(ins_ref.address.saturating_sub(resolved.symbol.address)),
|
||||
color: base_color,
|
||||
pad_to: 5,
|
||||
})?;
|
||||
if let Some(branch) = &ins_row.branch_from {
|
||||
@ -185,95 +185,85 @@ pub fn display_row(
|
||||
cb(DiffTextSegment::spacing(4))?;
|
||||
}
|
||||
let mut arg_idx = 0;
|
||||
let relocation = section.relocation_at(ins_ref, obj);
|
||||
let mut displayed_relocation = false;
|
||||
obj.arch.display_instruction(
|
||||
ins_ref,
|
||||
data,
|
||||
relocation,
|
||||
symbol.address..symbol.address + symbol.size,
|
||||
section_index,
|
||||
diff_config,
|
||||
&mut |part| match part {
|
||||
InstructionPart::Basic(text) => {
|
||||
if text.chars().all(|c| c == ' ') {
|
||||
cb(DiffTextSegment::spacing(text.len() as u8))
|
||||
} else {
|
||||
cb(DiffTextSegment::basic(&text, DiffTextColor::Normal))
|
||||
}
|
||||
obj.arch.display_instruction(resolved, diff_config, &mut |part| match part {
|
||||
InstructionPart::Basic(text) => {
|
||||
if text.chars().all(|c| c == ' ') {
|
||||
cb(DiffTextSegment::spacing(text.len() as u8))
|
||||
} else {
|
||||
cb(DiffTextSegment::basic(&text, base_color))
|
||||
}
|
||||
InstructionPart::Opcode(mnemonic, opcode) => cb(DiffTextSegment {
|
||||
text: DiffText::Opcode(mnemonic.as_ref(), opcode),
|
||||
color: if ins_row.kind == InstructionDiffKind::OpMismatch {
|
||||
DiffTextColor::Replace
|
||||
} else {
|
||||
DiffTextColor::Normal
|
||||
},
|
||||
pad_to: 10,
|
||||
}),
|
||||
InstructionPart::Arg(arg) => {
|
||||
let diff_index = ins_row.arg_diff.get(arg_idx).copied().unwrap_or_default();
|
||||
arg_idx += 1;
|
||||
match arg {
|
||||
InstructionArg::Value(value) => cb(DiffTextSegment {
|
||||
text: DiffText::Argument(value),
|
||||
color: diff_index
|
||||
.get()
|
||||
.map_or(DiffTextColor::Normal, |i| DiffTextColor::Rotating(i as u8)),
|
||||
}
|
||||
InstructionPart::Opcode(mnemonic, opcode) => cb(DiffTextSegment {
|
||||
text: DiffText::Opcode(mnemonic.as_ref(), opcode),
|
||||
color: match ins_row.kind {
|
||||
InstructionDiffKind::OpMismatch => DiffTextColor::Replace,
|
||||
_ => base_color,
|
||||
},
|
||||
pad_to: 10,
|
||||
}),
|
||||
InstructionPart::Arg(arg) => {
|
||||
let diff_index = ins_row.arg_diff.get(arg_idx).copied().unwrap_or_default();
|
||||
arg_idx += 1;
|
||||
match arg {
|
||||
InstructionArg::Value(value) => cb(DiffTextSegment {
|
||||
text: DiffText::Argument(value),
|
||||
color: diff_index
|
||||
.get()
|
||||
.map_or(base_color, |i| DiffTextColor::Rotating(i as u8)),
|
||||
pad_to: 0,
|
||||
}),
|
||||
InstructionArg::Reloc => {
|
||||
displayed_relocation = true;
|
||||
let resolved = resolved.relocation.unwrap();
|
||||
let color = diff_index
|
||||
.get()
|
||||
.map_or(DiffTextColor::Bright, |i| DiffTextColor::Rotating(i as u8));
|
||||
cb(DiffTextSegment {
|
||||
text: DiffText::Symbol(resolved.symbol),
|
||||
color,
|
||||
pad_to: 0,
|
||||
}),
|
||||
InstructionArg::Reloc => {
|
||||
displayed_relocation = true;
|
||||
let resolved = relocation.unwrap();
|
||||
let color = diff_index
|
||||
.get()
|
||||
.map_or(DiffTextColor::Bright, |i| DiffTextColor::Rotating(i as u8));
|
||||
})?;
|
||||
if resolved.relocation.addend != 0 {
|
||||
cb(DiffTextSegment {
|
||||
text: DiffText::Symbol(resolved.symbol),
|
||||
text: DiffText::Addend(resolved.relocation.addend),
|
||||
color,
|
||||
pad_to: 0,
|
||||
})?;
|
||||
if resolved.relocation.addend != 0 {
|
||||
cb(DiffTextSegment {
|
||||
text: DiffText::Addend(resolved.relocation.addend),
|
||||
color,
|
||||
pad_to: 0,
|
||||
})?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
InstructionArg::BranchDest(dest) => {
|
||||
if let Some(addr) = dest.checked_sub(symbol.address) {
|
||||
cb(DiffTextSegment {
|
||||
text: DiffText::BranchDest(addr),
|
||||
color: diff_index.get().map_or(DiffTextColor::Normal, |i| {
|
||||
DiffTextColor::Rotating(i as u8)
|
||||
}),
|
||||
pad_to: 0,
|
||||
})
|
||||
} else {
|
||||
cb(DiffTextSegment {
|
||||
text: DiffText::Argument(InstructionArgValue::Opaque(
|
||||
Cow::Borrowed("<invalid>"),
|
||||
)),
|
||||
color: diff_index.get().map_or(DiffTextColor::Normal, |i| {
|
||||
DiffTextColor::Rotating(i as u8)
|
||||
}),
|
||||
pad_to: 0,
|
||||
})
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
InstructionArg::BranchDest(dest) => {
|
||||
if let Some(addr) = dest.checked_sub(resolved.symbol.address) {
|
||||
cb(DiffTextSegment {
|
||||
text: DiffText::BranchDest(addr),
|
||||
color: diff_index
|
||||
.get()
|
||||
.map_or(base_color, |i| DiffTextColor::Rotating(i as u8)),
|
||||
pad_to: 0,
|
||||
})
|
||||
} else {
|
||||
cb(DiffTextSegment {
|
||||
text: DiffText::Argument(InstructionArgValue::Opaque(Cow::Borrowed(
|
||||
"<invalid>",
|
||||
))),
|
||||
color: diff_index
|
||||
.get()
|
||||
.map_or(base_color, |i| DiffTextColor::Rotating(i as u8)),
|
||||
pad_to: 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
InstructionPart::Separator => {
|
||||
cb(DiffTextSegment::basic(diff_config.separator(), DiffTextColor::Normal))
|
||||
}
|
||||
},
|
||||
)?;
|
||||
}
|
||||
InstructionPart::Separator => {
|
||||
cb(DiffTextSegment::basic(diff_config.separator(), base_color))
|
||||
}
|
||||
})?;
|
||||
// Fallback for relocation that wasn't displayed
|
||||
if relocation.is_some() && !displayed_relocation {
|
||||
cb(DiffTextSegment::basic(" <", DiffTextColor::Normal))?;
|
||||
let resolved = relocation.unwrap();
|
||||
if resolved.relocation.is_some() && !displayed_relocation {
|
||||
cb(DiffTextSegment::basic(" <", base_color))?;
|
||||
let resolved = resolved.relocation.unwrap();
|
||||
let diff_index = ins_row.arg_diff.get(arg_idx).copied().unwrap_or_default();
|
||||
let color =
|
||||
diff_index.get().map_or(DiffTextColor::Bright, |i| DiffTextColor::Rotating(i as u8));
|
||||
@ -285,7 +275,7 @@ pub fn display_row(
|
||||
pad_to: 0,
|
||||
})?;
|
||||
}
|
||||
cb(DiffTextSegment::basic(">", DiffTextColor::Normal))?;
|
||||
cb(DiffTextSegment::basic(">", base_color))?;
|
||||
}
|
||||
if let Some(branch) = &ins_row.branch_to {
|
||||
cb(DiffTextSegment::basic(" ~>", DiffTextColor::Rotating(branch.branch_idx as u8)))?;
|
||||
@ -322,9 +312,17 @@ impl From<&DiffText<'_>> for HighlightKind {
|
||||
}
|
||||
}
|
||||
|
||||
pub enum ContextMenuItem {
|
||||
pub enum ContextItem {
|
||||
Copy { value: String, label: Option<String> },
|
||||
Navigate { label: String },
|
||||
Navigate { label: String, symbol_index: usize, kind: SymbolNavigationKind },
|
||||
Separator,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Eq, PartialEq)]
|
||||
pub enum SymbolNavigationKind {
|
||||
#[default]
|
||||
Normal,
|
||||
Extab,
|
||||
}
|
||||
|
||||
pub enum HoverItemColor {
|
||||
@ -333,66 +331,200 @@ pub enum HoverItemColor {
|
||||
Special, // Blue
|
||||
}
|
||||
|
||||
pub struct HoverItem {
|
||||
pub text: String,
|
||||
pub color: HoverItemColor,
|
||||
pub enum HoverItem {
|
||||
Text { label: String, value: String, color: HoverItemColor },
|
||||
Separator,
|
||||
}
|
||||
|
||||
pub fn symbol_context(_obj: &Object, symbol: &Symbol) -> Vec<ContextMenuItem> {
|
||||
pub fn symbol_context(obj: &Object, symbol_index: usize) -> Vec<ContextItem> {
|
||||
let symbol = &obj.symbols[symbol_index];
|
||||
let mut out = Vec::new();
|
||||
out.push(ContextItem::Copy { value: symbol.name.clone(), label: None });
|
||||
if let Some(name) = &symbol.demangled_name {
|
||||
out.push(ContextMenuItem::Copy { value: name.clone(), label: None });
|
||||
out.push(ContextItem::Copy { value: name.clone(), label: None });
|
||||
}
|
||||
out.push(ContextMenuItem::Copy { value: symbol.name.clone(), label: None });
|
||||
if let Some(address) = symbol.virtual_address {
|
||||
out.push(ContextMenuItem::Copy {
|
||||
value: format!("{:#x}", address),
|
||||
label: Some("virtual address".to_string()),
|
||||
});
|
||||
if symbol.section.is_some() {
|
||||
if let Some(address) = symbol.virtual_address {
|
||||
out.push(ContextItem::Copy {
|
||||
value: format!("{:#x}", address),
|
||||
label: Some("virtual address".to_string()),
|
||||
});
|
||||
}
|
||||
}
|
||||
// if let Some(_extab) = obj.arch.ppc().and_then(|ppc| ppc.extab_for_symbol(symbol)) {
|
||||
// out.push(ContextMenuItem::Navigate { label: "Decode exception table".to_string() });
|
||||
// }
|
||||
out.append(&mut obj.arch.symbol_context(obj, symbol_index));
|
||||
out
|
||||
}
|
||||
|
||||
pub fn symbol_hover(_obj: &Object, symbol: &Symbol) -> Vec<HoverItem> {
|
||||
pub fn symbol_hover(obj: &Object, symbol_index: usize, addend: i64) -> Vec<HoverItem> {
|
||||
let symbol = &obj.symbols[symbol_index];
|
||||
let addend_str = match addend.cmp(&0i64) {
|
||||
Ordering::Greater => format!("+{:x}", addend),
|
||||
Ordering::Less => format!("-{:x}", -addend),
|
||||
_ => String::new(),
|
||||
};
|
||||
let mut out = Vec::new();
|
||||
out.push(HoverItem {
|
||||
text: format!("Name: {}", symbol.name),
|
||||
color: HoverItemColor::Emphasized,
|
||||
out.push(HoverItem::Text {
|
||||
label: "Name".into(),
|
||||
value: format!("{}{}", symbol.name, addend_str),
|
||||
color: HoverItemColor::Normal,
|
||||
});
|
||||
out.push(HoverItem {
|
||||
text: format!("Address: {:x}", symbol.address),
|
||||
color: HoverItemColor::Emphasized,
|
||||
});
|
||||
if symbol.flags.contains(SymbolFlag::SizeInferred) {
|
||||
out.push(HoverItem {
|
||||
text: format!("Size: {:x} (inferred)", symbol.size),
|
||||
color: HoverItemColor::Emphasized,
|
||||
if let Some(demangled_name) = &symbol.demangled_name {
|
||||
out.push(HoverItem::Text {
|
||||
label: "Demangled".into(),
|
||||
value: demangled_name.into(),
|
||||
color: HoverItemColor::Normal,
|
||||
});
|
||||
}
|
||||
if let Some(section) = symbol.section {
|
||||
out.push(HoverItem::Text {
|
||||
label: "Section".into(),
|
||||
value: obj.sections[section].name.clone(),
|
||||
color: HoverItemColor::Normal,
|
||||
});
|
||||
out.push(HoverItem::Text {
|
||||
label: "Address".into(),
|
||||
value: format!("{:x}{}", symbol.address, addend_str),
|
||||
color: HoverItemColor::Normal,
|
||||
});
|
||||
if symbol.flags.contains(SymbolFlag::SizeInferred) {
|
||||
out.push(HoverItem::Text {
|
||||
label: "Size".into(),
|
||||
value: format!("{:x} (inferred)", symbol.size),
|
||||
color: HoverItemColor::Normal,
|
||||
});
|
||||
} else {
|
||||
out.push(HoverItem::Text {
|
||||
label: "Size".into(),
|
||||
value: format!("{:x}", symbol.size),
|
||||
color: HoverItemColor::Normal,
|
||||
});
|
||||
}
|
||||
if let Some(align) = symbol.align {
|
||||
out.push(HoverItem::Text {
|
||||
label: "Alignment".into(),
|
||||
value: align.get().to_string(),
|
||||
color: HoverItemColor::Normal,
|
||||
});
|
||||
}
|
||||
if let Some(address) = symbol.virtual_address {
|
||||
out.push(HoverItem::Text {
|
||||
label: "Virtual address".into(),
|
||||
value: format!("{:#x}", address),
|
||||
color: HoverItemColor::Special,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
out.push(HoverItem {
|
||||
text: format!("Size: {:x}", symbol.size),
|
||||
out.push(HoverItem::Text {
|
||||
label: Default::default(),
|
||||
value: "Extern".into(),
|
||||
color: HoverItemColor::Emphasized,
|
||||
});
|
||||
}
|
||||
if let Some(address) = symbol.virtual_address {
|
||||
out.push(HoverItem {
|
||||
text: format!("Virtual address: {:#x}", address),
|
||||
out.append(&mut obj.arch.symbol_hover(obj, symbol_index));
|
||||
out
|
||||
}
|
||||
|
||||
pub fn instruction_context(
|
||||
obj: &Object,
|
||||
resolved: ResolvedInstructionRef,
|
||||
ins: &ParsedInstruction,
|
||||
) -> Vec<ContextItem> {
|
||||
let mut out = Vec::new();
|
||||
let mut hex_string = String::new();
|
||||
for byte in resolved.code {
|
||||
hex_string.push_str(&format!("{:02x}", byte));
|
||||
}
|
||||
out.push(ContextItem::Copy { value: hex_string, label: Some("instruction bytes".to_string()) });
|
||||
out.append(&mut obj.arch.instruction_context(obj, resolved));
|
||||
if let Some(virtual_address) = resolved.symbol.virtual_address {
|
||||
let offset = resolved.ins_ref.address - resolved.symbol.address;
|
||||
out.push(ContextItem::Copy {
|
||||
value: format!("{:x}", virtual_address + offset),
|
||||
label: Some("virtual address".to_string()),
|
||||
});
|
||||
}
|
||||
for arg in &ins.args {
|
||||
if let InstructionArg::Value(arg) = arg {
|
||||
out.push(ContextItem::Copy { value: arg.to_string(), label: None });
|
||||
match arg {
|
||||
InstructionArgValue::Signed(v) => {
|
||||
out.push(ContextItem::Copy { value: v.to_string(), label: None });
|
||||
}
|
||||
InstructionArgValue::Unsigned(v) => {
|
||||
out.push(ContextItem::Copy { value: v.to_string(), label: None });
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(reloc) = resolved.relocation {
|
||||
for literal in display_ins_data_literals(obj, resolved) {
|
||||
out.push(ContextItem::Copy { value: literal, label: None });
|
||||
}
|
||||
out.push(ContextItem::Separator);
|
||||
out.append(&mut symbol_context(obj, reloc.relocation.target_symbol));
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
pub fn instruction_hover(
|
||||
obj: &Object,
|
||||
resolved: ResolvedInstructionRef,
|
||||
ins: &ParsedInstruction,
|
||||
) -> Vec<HoverItem> {
|
||||
let mut out = Vec::new();
|
||||
out.push(HoverItem::Text {
|
||||
label: Default::default(),
|
||||
value: format!("{:02x?}", resolved.code),
|
||||
color: HoverItemColor::Normal,
|
||||
});
|
||||
out.append(&mut obj.arch.instruction_hover(obj, resolved));
|
||||
if let Some(virtual_address) = resolved.symbol.virtual_address {
|
||||
let offset = resolved.ins_ref.address - resolved.symbol.address;
|
||||
out.push(HoverItem::Text {
|
||||
label: "Virtual address".into(),
|
||||
value: format!("{:#x}", virtual_address + offset),
|
||||
color: HoverItemColor::Special,
|
||||
});
|
||||
}
|
||||
// if let Some(extab) = obj.arch.ppc().and_then(|ppc| ppc.extab_for_symbol(symbol)) {
|
||||
// out.push(HoverItem {
|
||||
// text: format!("extab symbol: {}", extab.etb_symbol.name),
|
||||
// color: HoverItemColor::Special,
|
||||
// });
|
||||
// out.push(HoverItem {
|
||||
// text: format!("extabindex symbol: {}", extab.eti_symbol.name),
|
||||
// color: HoverItemColor::Special,
|
||||
// });
|
||||
// }
|
||||
for arg in &ins.args {
|
||||
if let InstructionArg::Value(arg) = arg {
|
||||
match arg {
|
||||
InstructionArgValue::Signed(v) => {
|
||||
out.push(HoverItem::Text {
|
||||
label: Default::default(),
|
||||
value: format!("{arg} == {v}"),
|
||||
color: HoverItemColor::Normal,
|
||||
});
|
||||
}
|
||||
InstructionArgValue::Unsigned(v) => {
|
||||
out.push(HoverItem::Text {
|
||||
label: Default::default(),
|
||||
value: format!("{arg} == {v}"),
|
||||
color: HoverItemColor::Normal,
|
||||
});
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(reloc) = resolved.relocation {
|
||||
if let Some(name) = obj.arch.reloc_name(reloc.relocation.flags) {
|
||||
out.push(HoverItem::Text {
|
||||
label: "Relocation type".into(),
|
||||
value: name.to_string(),
|
||||
color: HoverItemColor::Normal,
|
||||
});
|
||||
} else {
|
||||
out.push(HoverItem::Text {
|
||||
label: "Relocation type".into(),
|
||||
value: format!("<{:?}>", reloc.relocation.flags),
|
||||
color: HoverItemColor::Normal,
|
||||
});
|
||||
}
|
||||
out.push(HoverItem::Separator);
|
||||
out.append(&mut symbol_hover(obj, reloc.relocation.target_symbol, reloc.relocation.addend));
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
@ -556,3 +688,37 @@ fn symbol_sort(a: &Symbol, b: &Symbol) -> Ordering {
|
||||
fn symbol_sort_reverse(a: &Symbol, b: &Symbol) -> Ordering {
|
||||
section_symbol_sort(a, b).then(b.address.cmp(&a.address)).then(b.size.cmp(&a.size))
|
||||
}
|
||||
|
||||
pub fn display_ins_data_labels(obj: &Object, resolved: ResolvedInstructionRef) -> Vec<String> {
|
||||
let Some(reloc) = resolved.relocation else {
|
||||
return Vec::new();
|
||||
};
|
||||
if reloc.relocation.addend < 0 || reloc.relocation.addend as u64 >= reloc.symbol.size {
|
||||
return Vec::new();
|
||||
}
|
||||
let Some(data) = obj.symbol_data(reloc.relocation.target_symbol) else {
|
||||
return Vec::new();
|
||||
};
|
||||
let bytes = &data[reloc.relocation.addend as usize..];
|
||||
obj.arch
|
||||
.guess_data_type(resolved)
|
||||
.map(|ty| ty.display_labels(obj.endianness, bytes))
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn display_ins_data_literals(obj: &Object, resolved: ResolvedInstructionRef) -> Vec<String> {
|
||||
let Some(reloc) = resolved.relocation else {
|
||||
return Vec::new();
|
||||
};
|
||||
if reloc.relocation.addend < 0 || reloc.relocation.addend as u64 >= reloc.symbol.size {
|
||||
return Vec::new();
|
||||
}
|
||||
let Some(data) = obj.symbol_data(reloc.relocation.target_symbol) else {
|
||||
return Vec::new();
|
||||
};
|
||||
let bytes = &data[reloc.relocation.addend as usize..];
|
||||
obj.arch
|
||||
.guess_data_type(resolved)
|
||||
.map(|ty| ty.display_literals(obj.endianness, bytes))
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
@ -105,8 +105,8 @@ impl Section {
|
||||
|
||||
pub fn relocation_at<'obj>(
|
||||
&'obj self,
|
||||
ins_ref: InstructionRef,
|
||||
obj: &'obj Object,
|
||||
ins_ref: InstructionRef,
|
||||
) -> Option<ResolvedRelocation<'obj>> {
|
||||
match self.relocations.binary_search_by_key(&ins_ref.address, |r| r.address) {
|
||||
Ok(i) => self.relocations.get(i),
|
||||
@ -204,7 +204,7 @@ impl InstructionArg<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
pub struct InstructionRef {
|
||||
pub address: u64,
|
||||
pub size: u8,
|
||||
@ -251,6 +251,7 @@ pub struct Symbol {
|
||||
#[derive(Debug)]
|
||||
pub struct Object {
|
||||
pub arch: Box<dyn Arch>,
|
||||
pub endianness: object::Endianness,
|
||||
pub symbols: Vec<Symbol>,
|
||||
pub sections: Vec<Section>,
|
||||
/// Split object metadata (.note.split section)
|
||||
@ -265,6 +266,7 @@ impl Default for Object {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
arch: ArchDummy::new(),
|
||||
endianness: object::Endianness::Little,
|
||||
symbols: vec![],
|
||||
sections: vec![],
|
||||
split_meta: None,
|
||||
@ -276,6 +278,38 @@ impl Default for Object {
|
||||
}
|
||||
}
|
||||
|
||||
impl Object {
|
||||
pub fn resolve_instruction_ref(
|
||||
&self,
|
||||
symbol_index: usize,
|
||||
ins_ref: InstructionRef,
|
||||
) -> Option<ResolvedInstructionRef> {
|
||||
let symbol = self.symbols.get(symbol_index)?;
|
||||
let section_index = symbol.section?;
|
||||
let section = self.sections.get(section_index)?;
|
||||
let offset = ins_ref.address.checked_sub(section.address)?;
|
||||
let code = section.data.get(offset as usize..offset as usize + ins_ref.size as usize)?;
|
||||
let relocation = section.relocation_at(self, ins_ref);
|
||||
Some(ResolvedInstructionRef {
|
||||
ins_ref,
|
||||
symbol_index,
|
||||
symbol,
|
||||
section,
|
||||
section_index,
|
||||
code,
|
||||
relocation,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn symbol_data(&self, symbol_index: usize) -> Option<&[u8]> {
|
||||
let symbol = self.symbols.get(symbol_index)?;
|
||||
let section_index = symbol.section?;
|
||||
let section = self.sections.get(section_index)?;
|
||||
let offset = symbol.address.checked_sub(section.address)?;
|
||||
section.data.get(offset as usize..offset as usize + symbol.size as usize)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
pub struct Relocation {
|
||||
pub flags: RelocationFlags,
|
||||
@ -295,3 +329,53 @@ pub struct ResolvedRelocation<'a> {
|
||||
pub relocation: &'a Relocation,
|
||||
pub symbol: &'a Symbol,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct ResolvedInstructionRef<'obj> {
|
||||
pub ins_ref: InstructionRef,
|
||||
pub symbol_index: usize,
|
||||
pub symbol: &'obj Symbol,
|
||||
pub section_index: usize,
|
||||
pub section: &'obj Section,
|
||||
pub code: &'obj [u8],
|
||||
pub relocation: Option<ResolvedRelocation<'obj>>,
|
||||
}
|
||||
|
||||
static DUMMY_SYMBOL: Symbol = Symbol {
|
||||
name: String::new(),
|
||||
demangled_name: None,
|
||||
address: 0,
|
||||
size: 0,
|
||||
kind: SymbolKind::Unknown,
|
||||
section: None,
|
||||
flags: SymbolFlagSet::empty(),
|
||||
align: None,
|
||||
virtual_address: None,
|
||||
};
|
||||
|
||||
static DUMMY_SECTION: Section = Section {
|
||||
id: String::new(),
|
||||
name: String::new(),
|
||||
address: 0,
|
||||
size: 0,
|
||||
kind: SectionKind::Unknown,
|
||||
data: SectionData(Vec::new()),
|
||||
flags: SectionFlagSet::empty(),
|
||||
relocations: Vec::new(),
|
||||
line_info: BTreeMap::new(),
|
||||
virtual_address: None,
|
||||
};
|
||||
|
||||
impl Default for ResolvedInstructionRef<'_> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
ins_ref: InstructionRef::default(),
|
||||
symbol_index: 0,
|
||||
symbol: &DUMMY_SYMBOL,
|
||||
section_index: 0,
|
||||
section: &DUMMY_SECTION,
|
||||
code: &[],
|
||||
relocation: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -780,6 +780,7 @@ pub fn parse(data: &[u8], config: &DiffObjConfig) -> Result<Object> {
|
||||
}
|
||||
Ok(Object {
|
||||
arch,
|
||||
endianness: obj_file.endianness(),
|
||||
symbols,
|
||||
sections,
|
||||
split_meta,
|
||||
|
@ -34,6 +34,14 @@ fn read_dwarf1_line_info() {
|
||||
insta::assert_debug_snapshot!(line_infos);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "ppc")]
|
||||
fn read_extab() {
|
||||
let diff_config = diff::DiffObjConfig::default();
|
||||
let obj = obj::read::parse(include_object!("data/ppc/NMWException.o"), &diff_config).unwrap();
|
||||
insta::assert_debug_snapshot!(obj);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "ppc")]
|
||||
fn diff_ppc() {
|
||||
|
BIN
objdiff-core/tests/data/ppc/NMWException.o
Normal file
BIN
objdiff-core/tests/data/ppc/NMWException.o
Normal file
Binary file not shown.
521
objdiff-core/tests/snapshots/arch_ppc__read_extab.snap
Normal file
521
objdiff-core/tests/snapshots/arch_ppc__read_extab.snap
Normal file
@ -0,0 +1,521 @@
|
||||
---
|
||||
source: objdiff-core/tests/arch_ppc.rs
|
||||
expression: obj
|
||||
---
|
||||
Object {
|
||||
arch: ArchPpc {
|
||||
extab: Some(
|
||||
{
|
||||
10: ExceptionInfo {
|
||||
eti_symbol: ExtabSymbolRef {
|
||||
original_index: 5,
|
||||
name: "@31",
|
||||
demangled_name: None,
|
||||
},
|
||||
etb_symbol: ExtabSymbolRef {
|
||||
original_index: 4,
|
||||
name: "@30",
|
||||
demangled_name: None,
|
||||
},
|
||||
data: ExceptionTableData {
|
||||
flag_val: 8200,
|
||||
has_elf_vector: false,
|
||||
large_frame: true,
|
||||
has_frame_pointer: false,
|
||||
saved_cr: false,
|
||||
fpr_save_range: 0,
|
||||
gpr_save_range: 4,
|
||||
et_field: 0,
|
||||
pc_actions: [],
|
||||
exception_actions: [],
|
||||
relocations: [],
|
||||
},
|
||||
dtors: [],
|
||||
},
|
||||
11: ExceptionInfo {
|
||||
eti_symbol: ExtabSymbolRef {
|
||||
original_index: 7,
|
||||
name: "@52",
|
||||
demangled_name: None,
|
||||
},
|
||||
etb_symbol: ExtabSymbolRef {
|
||||
original_index: 6,
|
||||
name: "@51",
|
||||
demangled_name: None,
|
||||
},
|
||||
data: ExceptionTableData {
|
||||
flag_val: 8200,
|
||||
has_elf_vector: false,
|
||||
large_frame: true,
|
||||
has_frame_pointer: false,
|
||||
saved_cr: false,
|
||||
fpr_save_range: 0,
|
||||
gpr_save_range: 4,
|
||||
et_field: 0,
|
||||
pc_actions: [
|
||||
PCAction {
|
||||
start_pc: 96,
|
||||
end_pc: 96,
|
||||
action_offset: 16,
|
||||
},
|
||||
],
|
||||
exception_actions: [
|
||||
ExceptionAction {
|
||||
action_offset: 16,
|
||||
action_type: DestroyLocal,
|
||||
action_param: 0,
|
||||
has_end_bit: true,
|
||||
bytes: [
|
||||
0,
|
||||
8,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
},
|
||||
],
|
||||
relocations: [
|
||||
Relocation {
|
||||
offset: 20,
|
||||
address: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
dtors: [
|
||||
ExtabSymbolRef {
|
||||
original_index: 12,
|
||||
name: "__dt__26__partial_array_destructorFv",
|
||||
demangled_name: Some(
|
||||
"__partial_array_destructor::~__partial_array_destructor()",
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
12: ExceptionInfo {
|
||||
eti_symbol: ExtabSymbolRef {
|
||||
original_index: 9,
|
||||
name: "@60",
|
||||
demangled_name: None,
|
||||
},
|
||||
etb_symbol: ExtabSymbolRef {
|
||||
original_index: 8,
|
||||
name: "@59",
|
||||
demangled_name: None,
|
||||
},
|
||||
data: ExceptionTableData {
|
||||
flag_val: 6152,
|
||||
has_elf_vector: false,
|
||||
large_frame: true,
|
||||
has_frame_pointer: false,
|
||||
saved_cr: false,
|
||||
fpr_save_range: 0,
|
||||
gpr_save_range: 3,
|
||||
et_field: 0,
|
||||
pc_actions: [],
|
||||
exception_actions: [],
|
||||
relocations: [],
|
||||
},
|
||||
dtors: [],
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
endianness: Big,
|
||||
symbols: [
|
||||
Symbol {
|
||||
name: "NMWException.cpp",
|
||||
demangled_name: None,
|
||||
address: 0,
|
||||
size: 0,
|
||||
kind: Unknown,
|
||||
section: None,
|
||||
flags: FlagSet(Local),
|
||||
align: None,
|
||||
virtual_address: None,
|
||||
},
|
||||
Symbol {
|
||||
name: "[.text]",
|
||||
demangled_name: None,
|
||||
address: 0,
|
||||
size: 0,
|
||||
kind: Section,
|
||||
section: Some(
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(Local),
|
||||
align: None,
|
||||
virtual_address: None,
|
||||
},
|
||||
Symbol {
|
||||
name: "[extab]",
|
||||
demangled_name: None,
|
||||
address: 0,
|
||||
size: 0,
|
||||
kind: Section,
|
||||
section: Some(
|
||||
1,
|
||||
),
|
||||
flags: FlagSet(Local),
|
||||
align: None,
|
||||
virtual_address: None,
|
||||
},
|
||||
Symbol {
|
||||
name: "[extabindex]",
|
||||
demangled_name: None,
|
||||
address: 0,
|
||||
size: 0,
|
||||
kind: Section,
|
||||
section: Some(
|
||||
2,
|
||||
),
|
||||
flags: FlagSet(Local),
|
||||
align: None,
|
||||
virtual_address: None,
|
||||
},
|
||||
Symbol {
|
||||
name: "@30",
|
||||
demangled_name: None,
|
||||
address: 0,
|
||||
size: 8,
|
||||
kind: Object,
|
||||
section: Some(
|
||||
1,
|
||||
),
|
||||
flags: FlagSet(Local),
|
||||
align: None,
|
||||
virtual_address: None,
|
||||
},
|
||||
Symbol {
|
||||
name: "@31",
|
||||
demangled_name: None,
|
||||
address: 0,
|
||||
size: 12,
|
||||
kind: Object,
|
||||
section: Some(
|
||||
2,
|
||||
),
|
||||
flags: FlagSet(Local),
|
||||
align: None,
|
||||
virtual_address: None,
|
||||
},
|
||||
Symbol {
|
||||
name: "@51",
|
||||
demangled_name: None,
|
||||
address: 8,
|
||||
size: 24,
|
||||
kind: Object,
|
||||
section: Some(
|
||||
1,
|
||||
),
|
||||
flags: FlagSet(Local),
|
||||
align: None,
|
||||
virtual_address: None,
|
||||
},
|
||||
Symbol {
|
||||
name: "@52",
|
||||
demangled_name: None,
|
||||
address: 12,
|
||||
size: 12,
|
||||
kind: Object,
|
||||
section: Some(
|
||||
2,
|
||||
),
|
||||
flags: FlagSet(Local),
|
||||
align: None,
|
||||
virtual_address: None,
|
||||
},
|
||||
Symbol {
|
||||
name: "@59",
|
||||
demangled_name: None,
|
||||
address: 32,
|
||||
size: 8,
|
||||
kind: Object,
|
||||
section: Some(
|
||||
1,
|
||||
),
|
||||
flags: FlagSet(Local),
|
||||
align: None,
|
||||
virtual_address: None,
|
||||
},
|
||||
Symbol {
|
||||
name: "@60",
|
||||
demangled_name: None,
|
||||
address: 24,
|
||||
size: 12,
|
||||
kind: Object,
|
||||
section: Some(
|
||||
2,
|
||||
),
|
||||
flags: FlagSet(Local),
|
||||
align: None,
|
||||
virtual_address: None,
|
||||
},
|
||||
Symbol {
|
||||
name: "__destroy_arr",
|
||||
demangled_name: None,
|
||||
address: 0,
|
||||
size: 120,
|
||||
kind: Function,
|
||||
section: Some(
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(Global | HasExtra),
|
||||
align: None,
|
||||
virtual_address: None,
|
||||
},
|
||||
Symbol {
|
||||
name: "__construct_array",
|
||||
demangled_name: None,
|
||||
address: 120,
|
||||
size: 248,
|
||||
kind: Function,
|
||||
section: Some(
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(Global | HasExtra),
|
||||
align: None,
|
||||
virtual_address: None,
|
||||
},
|
||||
Symbol {
|
||||
name: "__dt__26__partial_array_destructorFv",
|
||||
demangled_name: Some(
|
||||
"__partial_array_destructor::~__partial_array_destructor()",
|
||||
),
|
||||
address: 368,
|
||||
size: 184,
|
||||
kind: Function,
|
||||
section: Some(
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(Global | Weak | HasExtra),
|
||||
align: None,
|
||||
virtual_address: None,
|
||||
},
|
||||
Symbol {
|
||||
name: "__dl__FPv",
|
||||
demangled_name: Some(
|
||||
"operator delete(void*)",
|
||||
),
|
||||
address: 0,
|
||||
size: 0,
|
||||
kind: Unknown,
|
||||
section: None,
|
||||
flags: FlagSet(Global),
|
||||
align: None,
|
||||
virtual_address: None,
|
||||
},
|
||||
],
|
||||
sections: [
|
||||
Section {
|
||||
id: ".text-0",
|
||||
name: ".text",
|
||||
address: 0,
|
||||
size: 552,
|
||||
kind: Code,
|
||||
data: SectionData(
|
||||
552,
|
||||
),
|
||||
flags: FlagSet(),
|
||||
relocations: [
|
||||
Relocation {
|
||||
flags: Elf(
|
||||
10,
|
||||
),
|
||||
address: 516,
|
||||
target_symbol: 13,
|
||||
addend: 0,
|
||||
},
|
||||
],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
},
|
||||
Section {
|
||||
id: "extab-0",
|
||||
name: "extab",
|
||||
address: 0,
|
||||
size: 40,
|
||||
kind: Data,
|
||||
data: SectionData(
|
||||
40,
|
||||
),
|
||||
flags: FlagSet(),
|
||||
relocations: [
|
||||
Relocation {
|
||||
flags: Elf(
|
||||
1,
|
||||
),
|
||||
address: 28,
|
||||
target_symbol: 12,
|
||||
addend: 0,
|
||||
},
|
||||
],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
},
|
||||
Section {
|
||||
id: "extabindex-0",
|
||||
name: "extabindex",
|
||||
address: 0,
|
||||
size: 36,
|
||||
kind: Data,
|
||||
data: SectionData(
|
||||
36,
|
||||
),
|
||||
flags: FlagSet(),
|
||||
relocations: [
|
||||
Relocation {
|
||||
flags: Elf(
|
||||
1,
|
||||
),
|
||||
address: 0,
|
||||
target_symbol: 10,
|
||||
addend: 0,
|
||||
},
|
||||
Relocation {
|
||||
flags: Elf(
|
||||
1,
|
||||
),
|
||||
address: 8,
|
||||
target_symbol: 4,
|
||||
addend: 0,
|
||||
},
|
||||
Relocation {
|
||||
flags: Elf(
|
||||
1,
|
||||
),
|
||||
address: 12,
|
||||
target_symbol: 11,
|
||||
addend: 0,
|
||||
},
|
||||
Relocation {
|
||||
flags: Elf(
|
||||
1,
|
||||
),
|
||||
address: 20,
|
||||
target_symbol: 6,
|
||||
addend: 0,
|
||||
},
|
||||
Relocation {
|
||||
flags: Elf(
|
||||
1,
|
||||
),
|
||||
address: 24,
|
||||
target_symbol: 12,
|
||||
addend: 0,
|
||||
},
|
||||
Relocation {
|
||||
flags: Elf(
|
||||
1,
|
||||
),
|
||||
address: 32,
|
||||
target_symbol: 8,
|
||||
addend: 0,
|
||||
},
|
||||
],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
},
|
||||
Section {
|
||||
id: ".rela.text-0",
|
||||
name: ".rela.text",
|
||||
address: 0,
|
||||
size: 12,
|
||||
kind: Unknown,
|
||||
data: SectionData(
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
},
|
||||
Section {
|
||||
id: ".relaextab-0",
|
||||
name: ".relaextab",
|
||||
address: 0,
|
||||
size: 12,
|
||||
kind: Unknown,
|
||||
data: SectionData(
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
},
|
||||
Section {
|
||||
id: ".relaextabindex-0",
|
||||
name: ".relaextabindex",
|
||||
address: 0,
|
||||
size: 72,
|
||||
kind: Unknown,
|
||||
data: SectionData(
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
},
|
||||
Section {
|
||||
id: ".symtab-0",
|
||||
name: ".symtab",
|
||||
address: 0,
|
||||
size: 240,
|
||||
kind: Unknown,
|
||||
data: SectionData(
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
},
|
||||
Section {
|
||||
id: ".strtab-0",
|
||||
name: ".strtab",
|
||||
address: 0,
|
||||
size: 121,
|
||||
kind: Unknown,
|
||||
data: SectionData(
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
},
|
||||
Section {
|
||||
id: ".shstrtab-0",
|
||||
name: ".shstrtab",
|
||||
address: 0,
|
||||
size: 97,
|
||||
kind: Unknown,
|
||||
data: SectionData(
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
},
|
||||
Section {
|
||||
id: ".comment-0",
|
||||
name: ".comment",
|
||||
address: 0,
|
||||
size: 164,
|
||||
kind: Unknown,
|
||||
data: SectionData(
|
||||
0,
|
||||
),
|
||||
flags: FlagSet(),
|
||||
relocations: [],
|
||||
line_info: {},
|
||||
virtual_address: None,
|
||||
},
|
||||
],
|
||||
split_meta: None,
|
||||
path: None,
|
||||
timestamp: None,
|
||||
}
|
@ -6,6 +6,7 @@ Object {
|
||||
arch: ArchPpc {
|
||||
extab: None,
|
||||
},
|
||||
endianness: Big,
|
||||
symbols: [
|
||||
Symbol {
|
||||
name: "IObj.cpp",
|
||||
|
@ -7,6 +7,7 @@ Object {
|
||||
bits: 32,
|
||||
endianness: Little,
|
||||
},
|
||||
endianness: Little,
|
||||
symbols: [
|
||||
Symbol {
|
||||
name: "objdiffstaticdebug.cpp",
|
||||
|
@ -25,11 +25,10 @@ wsl = []
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
bytes = "1.9"
|
||||
cfg-if = "1.0"
|
||||
const_format = "0.2"
|
||||
cwdemangle = "1.0"
|
||||
cwextab = { version = "1.0", git = "https://github.com/encounter/cwextab.git" }
|
||||
cwextab = { version = "1.0", git = "https://github.com/CelestialAmber/cwextab.git" }
|
||||
dirs = "5.0"
|
||||
egui = "0.30"
|
||||
egui_extras = "0.30"
|
||||
@ -44,12 +43,11 @@ png = "0.17"
|
||||
pollster = "0.4"
|
||||
regex = "1.11"
|
||||
rfd = { version = "0.15" } #, default-features = false, features = ['xdg-portal']
|
||||
rlwinmdec = "1.0"
|
||||
rlwinmdec = { version = "1.0", git = "https://github.com/CelestialAmber/rlwinmdec.git" }
|
||||
ron = "0.8"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
shell-escape = "0.1"
|
||||
strum = { version = "0.26", features = ["derive"] }
|
||||
time = { version = "0.3", features = ["formatting", "local-offset"] }
|
||||
typed-path = "0.10"
|
||||
winit = { version = "0.30", features = ["wayland-csd-adwaita"] }
|
||||
|
@ -43,7 +43,7 @@ use crate::{
|
||||
graphics::{graphics_window, GraphicsConfig, GraphicsViewState},
|
||||
jobs::{jobs_menu_ui, jobs_window},
|
||||
rlwinm::{rlwinm_decode_window, RlwinmDecodeViewState},
|
||||
symbol_diff::{DiffViewAction, DiffViewNavigation, DiffViewState, View},
|
||||
symbol_diff::{DiffViewAction, DiffViewState, ResolvedNavigation, View},
|
||||
},
|
||||
};
|
||||
|
||||
@ -762,7 +762,7 @@ impl eframe::App for App {
|
||||
ui.separator();
|
||||
if ui.button("Clear custom symbol mappings").clicked() {
|
||||
state.clear_mappings();
|
||||
diff_state.post_build_nav = Some(DiffViewNavigation::symbol_diff());
|
||||
diff_state.post_build_nav = Some(ResolvedNavigation::default());
|
||||
state.queue_reload = true;
|
||||
}
|
||||
});
|
||||
|
@ -25,8 +25,7 @@ use tracing_subscriber::EnvFilter;
|
||||
use crate::views::graphics::{load_graphics_config, GraphicsBackend, GraphicsConfig};
|
||||
|
||||
fn load_icon() -> Result<egui::IconData> {
|
||||
use bytes::Buf;
|
||||
let decoder = png::Decoder::new(include_bytes!("../assets/icon_64.png").reader());
|
||||
let decoder = png::Decoder::new(include_bytes!("../assets/icon_64.png").as_ref());
|
||||
let mut reader = decoder.read_info()?;
|
||||
let mut buf = vec![0; reader.output_buffer_size()];
|
||||
let info = reader.next_frame(&mut buf)?;
|
||||
|
@ -1,8 +1,11 @@
|
||||
use egui::{Id, Layout, RichText, ScrollArea, TextEdit, Ui, Widget};
|
||||
use egui::{text::LayoutJob, Id, Layout, RichText, ScrollArea, TextEdit, Ui, Widget};
|
||||
use objdiff_core::{
|
||||
build::BuildStatus,
|
||||
diff::{display::SymbolFilter, DiffObjConfig, ObjectDiff, SectionDiff, SymbolDiff},
|
||||
obj::{Object, Section, SectionKind, Symbol},
|
||||
diff::{
|
||||
display::{ContextItem, HoverItem, HoverItemColor, SymbolFilter, SymbolNavigationKind},
|
||||
DiffObjConfig, ObjectDiff, SectionDiff, SymbolDiff,
|
||||
},
|
||||
obj::{Object, Section, Symbol},
|
||||
};
|
||||
use time::format_description;
|
||||
|
||||
@ -15,9 +18,11 @@ use crate::{
|
||||
extab_diff::extab_ui,
|
||||
function_diff::{asm_col_ui, FunctionDiffContext},
|
||||
symbol_diff::{
|
||||
match_color_for_symbol, symbol_list_ui, DiffViewAction, DiffViewNavigation,
|
||||
DiffViewState, SymbolDiffContext, SymbolRefByName, View,
|
||||
match_color_for_symbol, symbol_context_menu_ui, symbol_hover_ui, symbol_list_ui,
|
||||
DiffViewAction, DiffViewNavigation, DiffViewState, SymbolDiffContext, SymbolRefByName,
|
||||
View,
|
||||
},
|
||||
write_text,
|
||||
},
|
||||
};
|
||||
|
||||
@ -112,21 +117,19 @@ pub fn diff_view_ui(
|
||||
|
||||
// Check if we need to perform any navigation
|
||||
let current_navigation = DiffViewNavigation {
|
||||
view: state.current_view,
|
||||
left_symbol: state.symbol_state.left_symbol.clone(),
|
||||
right_symbol: state.symbol_state.right_symbol.clone(),
|
||||
kind: match state.current_view {
|
||||
View::ExtabDiff => SymbolNavigationKind::Extab,
|
||||
_ => SymbolNavigationKind::Normal,
|
||||
},
|
||||
left_symbol: left_ctx.symbol.map(|(_, _, idx)| idx),
|
||||
right_symbol: right_ctx.symbol.map(|(_, _, idx)| idx),
|
||||
};
|
||||
let mut navigation = current_navigation.clone();
|
||||
if let Some((_symbol, symbol_diff, _symbol_idx)) = left_ctx.symbol {
|
||||
// If a matching symbol appears, select it
|
||||
if !right_ctx.has_symbol() {
|
||||
if let Some(target_symbol_ref) = symbol_diff.target_symbol {
|
||||
let (right_obj, _) = right_ctx.obj.unwrap();
|
||||
let target_symbol = &right_obj.symbols[target_symbol_ref];
|
||||
let target_section = target_symbol
|
||||
.section
|
||||
.and_then(|section_idx| right_obj.sections.get(section_idx));
|
||||
navigation.right_symbol = Some(SymbolRefByName::new(target_symbol, target_section));
|
||||
navigation.right_symbol = Some(target_symbol_ref);
|
||||
}
|
||||
}
|
||||
} else if navigation.left_symbol.is_some()
|
||||
@ -140,12 +143,7 @@ pub fn diff_view_ui(
|
||||
// If a matching symbol appears, select it
|
||||
if !left_ctx.has_symbol() {
|
||||
if let Some(target_symbol_ref) = symbol_diff.target_symbol {
|
||||
let (left_obj, _) = left_ctx.obj.unwrap();
|
||||
let target_symbol = &left_obj.symbols[target_symbol_ref];
|
||||
let target_section = target_symbol
|
||||
.section
|
||||
.and_then(|section_idx| left_obj.sections.get(section_idx));
|
||||
navigation.left_symbol = Some(SymbolRefByName::new(target_symbol, target_section));
|
||||
navigation.left_symbol = Some(target_symbol_ref);
|
||||
}
|
||||
}
|
||||
} else if navigation.right_symbol.is_some()
|
||||
@ -157,7 +155,7 @@ pub fn diff_view_ui(
|
||||
}
|
||||
// If both sides are missing a symbol, switch to symbol diff view
|
||||
if navigation.left_symbol.is_none() && navigation.right_symbol.is_none() {
|
||||
navigation.view = View::SymbolDiff;
|
||||
navigation = DiffViewNavigation::default();
|
||||
}
|
||||
// Execute navigation if it changed
|
||||
if navigation != current_navigation && state.post_build_nav.is_none() {
|
||||
@ -177,7 +175,7 @@ pub fn diff_view_ui(
|
||||
} else {
|
||||
ui.horizontal(|ui| {
|
||||
if ui.button("⏴ Back").clicked() || hotkeys::back_pressed(ui.ctx()) {
|
||||
ret = Some(DiffViewAction::Navigate(DiffViewNavigation::symbol_diff()));
|
||||
ret = Some(DiffViewAction::Navigate(DiffViewNavigation::default()));
|
||||
}
|
||||
|
||||
if let Some((symbol, _, _)) = left_ctx.symbol {
|
||||
@ -220,12 +218,12 @@ pub fn diff_view_ui(
|
||||
.color(appearance.replace_color),
|
||||
);
|
||||
}
|
||||
} else if let Some((symbol, _, _)) = left_ctx.symbol {
|
||||
ui.label(
|
||||
RichText::new(symbol.demangled_name.as_deref().unwrap_or(&symbol.name))
|
||||
.font(appearance.code_font.clone())
|
||||
.color(appearance.highlight_color),
|
||||
);
|
||||
} else if let Some((symbol, _symbol_diff, symbol_idx)) = left_ctx.symbol {
|
||||
if let Some(action) =
|
||||
symbol_label_ui(ui, left_ctx, symbol, symbol_idx, column, appearance)
|
||||
{
|
||||
ret = Some(action);
|
||||
}
|
||||
} else if let Some((section, _, _)) = left_ctx.section {
|
||||
ui.label(
|
||||
RichText::new(section.name.clone())
|
||||
@ -341,12 +339,12 @@ pub fn diff_view_ui(
|
||||
.color(appearance.replace_color),
|
||||
);
|
||||
}
|
||||
} else if let Some((symbol, _, _)) = right_ctx.symbol {
|
||||
ui.label(
|
||||
RichText::new(symbol.demangled_name.as_deref().unwrap_or(&symbol.name))
|
||||
.font(appearance.code_font.clone())
|
||||
.color(appearance.highlight_color),
|
||||
);
|
||||
} else if let Some((symbol, _symbol_diff, symbol_idx)) = right_ctx.symbol {
|
||||
if let Some(action) =
|
||||
symbol_label_ui(ui, right_ctx, symbol, symbol_idx, column, appearance)
|
||||
{
|
||||
ret = Some(action);
|
||||
}
|
||||
} else if let Some((section, _, _)) = right_ctx.section {
|
||||
ui.label(
|
||||
RichText::new(section.name.clone())
|
||||
@ -573,6 +571,38 @@ pub fn diff_view_ui(
|
||||
ret
|
||||
}
|
||||
|
||||
fn symbol_label_ui(
|
||||
ui: &mut Ui,
|
||||
ctx: DiffColumnContext,
|
||||
symbol: &Symbol,
|
||||
symbol_idx: usize,
|
||||
column: usize,
|
||||
appearance: &Appearance,
|
||||
) -> Option<DiffViewAction> {
|
||||
let (obj, diff) = ctx.obj.unwrap();
|
||||
let ctx = SymbolDiffContext { obj, diff };
|
||||
let mut ret = None;
|
||||
egui::Label::new(
|
||||
RichText::new(symbol.demangled_name.as_deref().unwrap_or(&symbol.name))
|
||||
.font(appearance.code_font.clone())
|
||||
.color(appearance.highlight_color),
|
||||
)
|
||||
.selectable(false)
|
||||
// TODO .show_tooltip_when_elided(false)
|
||||
// https://github.com/emilk/egui/commit/071e090e2b2601e5ed4726a63a753188503dfaf2
|
||||
.ui(ui)
|
||||
.on_hover_ui_at_pointer(|ui| symbol_hover_ui(ui, ctx, symbol_idx, appearance))
|
||||
.context_menu(|ui| {
|
||||
let section = symbol.section.and_then(|section_idx| ctx.obj.sections.get(section_idx));
|
||||
if let Some(result) =
|
||||
symbol_context_menu_ui(ui, ctx, symbol_idx, symbol, section, column, appearance)
|
||||
{
|
||||
ret = Some(result);
|
||||
}
|
||||
});
|
||||
ret
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
fn diff_col_ui(
|
||||
ui: &mut Ui,
|
||||
@ -641,15 +671,11 @@ fn diff_col_ui(
|
||||
});
|
||||
},
|
||||
);
|
||||
} else if let (
|
||||
Some((other_section, _other_section_diff, _other_section_idx)),
|
||||
Some((other_symbol, _other_symbol_diff, other_symbol_idx)),
|
||||
) = (other_ctx.section, other_ctx.symbol)
|
||||
} else if let Some((_other_symbol, _other_symbol_diff, other_symbol_idx)) = other_ctx.symbol
|
||||
{
|
||||
if let Some(action) = symbol_list_ui(
|
||||
ui,
|
||||
SymbolDiffContext { obj, diff },
|
||||
None,
|
||||
&state.symbol_state,
|
||||
SymbolFilter::Mapping(other_symbol_idx, None),
|
||||
appearance,
|
||||
@ -660,34 +686,20 @@ fn diff_col_ui(
|
||||
(
|
||||
0,
|
||||
DiffViewAction::Navigate(DiffViewNavigation {
|
||||
left_symbol: Some(left_symbol_ref),
|
||||
left_symbol: Some(symbol_idx),
|
||||
..
|
||||
}),
|
||||
) => {
|
||||
ret = Some(DiffViewAction::SetMapping(
|
||||
match other_section.kind {
|
||||
SectionKind::Code => View::FunctionDiff,
|
||||
_ => View::SymbolDiff,
|
||||
},
|
||||
left_symbol_ref,
|
||||
SymbolRefByName::new(other_symbol, Some(other_section)),
|
||||
));
|
||||
ret = Some(DiffViewAction::SetMapping(symbol_idx, other_symbol_idx));
|
||||
}
|
||||
(
|
||||
1,
|
||||
DiffViewAction::Navigate(DiffViewNavigation {
|
||||
right_symbol: Some(right_symbol_ref),
|
||||
right_symbol: Some(symbol_idx),
|
||||
..
|
||||
}),
|
||||
) => {
|
||||
ret = Some(DiffViewAction::SetMapping(
|
||||
match other_section.kind {
|
||||
SectionKind::Code => View::FunctionDiff,
|
||||
_ => View::SymbolDiff,
|
||||
},
|
||||
SymbolRefByName::new(other_symbol, Some(other_section)),
|
||||
right_symbol_ref,
|
||||
));
|
||||
ret = Some(DiffViewAction::SetMapping(other_symbol_idx, symbol_idx));
|
||||
}
|
||||
(_, action) => {
|
||||
ret = Some(action);
|
||||
@ -702,7 +714,6 @@ fn diff_col_ui(
|
||||
if let Some(result) = symbol_list_ui(
|
||||
ui,
|
||||
SymbolDiffContext { obj, diff },
|
||||
other_ctx.obj.map(|(obj, diff)| SymbolDiffContext { obj, diff }),
|
||||
&state.symbol_state,
|
||||
filter,
|
||||
appearance,
|
||||
@ -764,3 +775,91 @@ fn find_symbol(obj: &Object, selected_symbol: &SymbolRefByName) -> Option<usize>
|
||||
fn find_section(obj: &Object, section_name: &str) -> Option<usize> {
|
||||
obj.sections.iter().position(|section| section.name == section_name)
|
||||
}
|
||||
|
||||
pub fn hover_items_ui(ui: &mut Ui, items: Vec<HoverItem>, appearance: &Appearance) {
|
||||
for item in items {
|
||||
match item {
|
||||
HoverItem::Text { label, value, color } => {
|
||||
let mut job = LayoutJob::default();
|
||||
if !label.is_empty() {
|
||||
let label_color = match color {
|
||||
HoverItemColor::Special => appearance.replace_color,
|
||||
_ => appearance.highlight_color,
|
||||
};
|
||||
write_text(&label, label_color, &mut job, appearance.code_font.clone());
|
||||
write_text(": ", label_color, &mut job, appearance.code_font.clone());
|
||||
}
|
||||
write_text(
|
||||
&value,
|
||||
match color {
|
||||
HoverItemColor::Emphasized => appearance.highlight_color,
|
||||
_ => appearance.text_color,
|
||||
},
|
||||
&mut job,
|
||||
appearance.code_font.clone(),
|
||||
);
|
||||
ui.label(job);
|
||||
}
|
||||
HoverItem::Separator => {
|
||||
ui.separator();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn context_menu_items_ui(
|
||||
ui: &mut Ui,
|
||||
items: Vec<ContextItem>,
|
||||
column: usize,
|
||||
appearance: &Appearance,
|
||||
) -> Option<DiffViewAction> {
|
||||
let mut ret = None;
|
||||
for item in items {
|
||||
match item {
|
||||
ContextItem::Copy { value, label } => {
|
||||
let mut job = LayoutJob::default();
|
||||
write_text(
|
||||
"Copy \"",
|
||||
appearance.text_color,
|
||||
&mut job,
|
||||
appearance.code_font.clone(),
|
||||
);
|
||||
write_text(
|
||||
&value,
|
||||
appearance.highlight_color,
|
||||
&mut job,
|
||||
appearance.code_font.clone(),
|
||||
);
|
||||
write_text("\"", appearance.text_color, &mut job, appearance.code_font.clone());
|
||||
if let Some(label) = label {
|
||||
write_text(" (", appearance.text_color, &mut job, appearance.code_font.clone());
|
||||
write_text(
|
||||
&label,
|
||||
appearance.text_color,
|
||||
&mut job,
|
||||
appearance.code_font.clone(),
|
||||
);
|
||||
write_text(")", appearance.text_color, &mut job, appearance.code_font.clone());
|
||||
}
|
||||
if ui.button(job).clicked() {
|
||||
ui.output_mut(|output| output.copied_text = value);
|
||||
ui.close_menu();
|
||||
}
|
||||
}
|
||||
ContextItem::Navigate { label, symbol_index, kind } => {
|
||||
if ui.button(label).clicked() {
|
||||
ret = Some(DiffViewAction::Navigate(DiffViewNavigation::new(
|
||||
kind,
|
||||
symbol_index,
|
||||
column,
|
||||
)));
|
||||
ui.close_menu();
|
||||
}
|
||||
}
|
||||
ContextItem::Separator => {
|
||||
ui.separator();
|
||||
}
|
||||
}
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
@ -4,17 +4,21 @@ use egui::{text::LayoutJob, Label, Response, Sense, Widget};
|
||||
use egui_extras::TableRow;
|
||||
use objdiff_core::{
|
||||
diff::{
|
||||
display::{display_row, DiffText, DiffTextColor, DiffTextSegment, HighlightKind},
|
||||
display::{
|
||||
display_row, instruction_context, instruction_hover, DiffText, DiffTextColor,
|
||||
DiffTextSegment, HighlightKind,
|
||||
},
|
||||
DiffObjConfig, InstructionDiffKind, InstructionDiffRow, ObjectDiff,
|
||||
},
|
||||
obj::{
|
||||
InstructionArg, InstructionArgValue, InstructionRef, Object, ParsedInstruction,
|
||||
ResolvedRelocation, Section, Symbol,
|
||||
},
|
||||
obj::{InstructionArgValue, InstructionRef, Object},
|
||||
util::ReallySigned,
|
||||
};
|
||||
|
||||
use crate::views::{appearance::Appearance, symbol_diff::DiffViewAction};
|
||||
use crate::views::{
|
||||
appearance::Appearance,
|
||||
diff::{context_menu_items_ui, hover_items_ui},
|
||||
symbol_diff::DiffViewAction,
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct FunctionViewState {
|
||||
@ -67,51 +71,6 @@ impl FunctionViewState {
|
||||
}
|
||||
}
|
||||
|
||||
#[expect(unused)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct ResolvedInstructionRef<'obj> {
|
||||
pub symbol: &'obj Symbol,
|
||||
pub section_idx: usize,
|
||||
pub section: &'obj Section,
|
||||
pub data: &'obj [u8],
|
||||
pub relocation: Option<ResolvedRelocation<'obj>>,
|
||||
}
|
||||
|
||||
fn resolve_instruction_ref(
|
||||
obj: &Object,
|
||||
symbol_idx: usize,
|
||||
ins_ref: InstructionRef,
|
||||
) -> Option<ResolvedInstructionRef> {
|
||||
let symbol = &obj.symbols[symbol_idx];
|
||||
let section_idx = symbol.section?;
|
||||
let section = &obj.sections[section_idx];
|
||||
let offset = ins_ref.address.checked_sub(section.address)?;
|
||||
let data = section.data.get(offset as usize..offset as usize + ins_ref.size as usize)?;
|
||||
let relocation = section.relocation_at(ins_ref, obj);
|
||||
Some(ResolvedInstructionRef { symbol, section, section_idx, data, relocation })
|
||||
}
|
||||
|
||||
fn resolve_instruction<'obj>(
|
||||
obj: &'obj Object,
|
||||
symbol_idx: usize,
|
||||
ins_ref: InstructionRef,
|
||||
diff_config: &DiffObjConfig,
|
||||
) -> Option<(ResolvedInstructionRef<'obj>, ParsedInstruction)> {
|
||||
let resolved = resolve_instruction_ref(obj, symbol_idx, ins_ref)?;
|
||||
let ins = obj
|
||||
.arch
|
||||
.process_instruction(
|
||||
ins_ref,
|
||||
resolved.data,
|
||||
resolved.relocation,
|
||||
resolved.symbol.address..resolved.symbol.address + resolved.symbol.size,
|
||||
resolved.section_idx,
|
||||
diff_config,
|
||||
)
|
||||
.ok()?;
|
||||
Some((resolved, ins))
|
||||
}
|
||||
|
||||
fn ins_hover_ui(
|
||||
ui: &mut egui::Ui,
|
||||
obj: &Object,
|
||||
@ -120,86 +79,25 @@ fn ins_hover_ui(
|
||||
diff_config: &DiffObjConfig,
|
||||
appearance: &Appearance,
|
||||
) {
|
||||
let Some((
|
||||
ResolvedInstructionRef { symbol, section_idx: _, section: _, data, relocation },
|
||||
ins,
|
||||
)) = resolve_instruction(obj, symbol_idx, ins_ref, diff_config)
|
||||
else {
|
||||
let Some(resolved) = obj.resolve_instruction_ref(symbol_idx, ins_ref) else {
|
||||
ui.colored_label(appearance.delete_color, "Failed to resolve instruction");
|
||||
return;
|
||||
};
|
||||
let ins = match obj.arch.process_instruction(resolved, diff_config) {
|
||||
Ok(ins) => ins,
|
||||
Err(e) => {
|
||||
ui.colored_label(
|
||||
appearance.delete_color,
|
||||
format!("Failed to process instruction: {e}"),
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
ui.scope(|ui| {
|
||||
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
|
||||
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
|
||||
|
||||
ui.label(format!("{:02x?}", data));
|
||||
|
||||
if let Some(virtual_address) = symbol.virtual_address {
|
||||
let offset = ins_ref.address - symbol.address;
|
||||
ui.colored_label(
|
||||
appearance.replace_color,
|
||||
format!("Virtual address: {:#x}", virtual_address + offset),
|
||||
);
|
||||
}
|
||||
|
||||
// TODO
|
||||
// if let Some(orig) = &ins.orig {
|
||||
// ui.label(format!("Original: {}", orig));
|
||||
// }
|
||||
|
||||
for arg in &ins.args {
|
||||
if let InstructionArg::Value(arg) = arg {
|
||||
match arg {
|
||||
InstructionArgValue::Signed(v) => {
|
||||
ui.label(format!("{arg} == {v}"));
|
||||
}
|
||||
InstructionArgValue::Unsigned(v) => {
|
||||
ui.label(format!("{arg} == {v}"));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(resolved) = relocation {
|
||||
ui.label(format!(
|
||||
"Relocation type: {}",
|
||||
obj.arch.display_reloc(resolved.relocation.flags)
|
||||
));
|
||||
let addend_str = match resolved.relocation.addend.cmp(&0i64) {
|
||||
Ordering::Greater => format!("+{:x}", resolved.relocation.addend),
|
||||
Ordering::Less => format!("-{:x}", -resolved.relocation.addend),
|
||||
_ => "".to_string(),
|
||||
};
|
||||
ui.colored_label(
|
||||
appearance.highlight_color,
|
||||
format!("Name: {}{}", resolved.symbol.name, addend_str),
|
||||
);
|
||||
if let Some(orig_section_index) = resolved.symbol.section {
|
||||
let section = &obj.sections[orig_section_index];
|
||||
ui.colored_label(appearance.highlight_color, format!("Section: {}", section.name));
|
||||
ui.colored_label(
|
||||
appearance.highlight_color,
|
||||
format!("Address: {:x}{}", resolved.symbol.address, addend_str),
|
||||
);
|
||||
ui.colored_label(
|
||||
appearance.highlight_color,
|
||||
format!("Size: {:x}", resolved.symbol.size),
|
||||
);
|
||||
// TODO
|
||||
// for label in obj.arch.display_ins_data_labels(ins) {
|
||||
// ui.colored_label(appearance.highlight_color, label);
|
||||
// }
|
||||
} else {
|
||||
ui.colored_label(appearance.highlight_color, "Extern".to_string());
|
||||
}
|
||||
}
|
||||
|
||||
// TODO
|
||||
// if let Some(decoded) = rlwinmdec::decode(&ins.formatted) {
|
||||
// ui.colored_label(appearance.highlight_color, decoded.trim());
|
||||
// }
|
||||
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Wrap);
|
||||
hover_items_ui(ui, instruction_hover(obj, resolved, &ins), appearance);
|
||||
});
|
||||
}
|
||||
|
||||
@ -208,93 +106,29 @@ fn ins_context_menu(
|
||||
obj: &Object,
|
||||
symbol_idx: usize,
|
||||
ins_ref: InstructionRef,
|
||||
column: usize,
|
||||
diff_config: &DiffObjConfig,
|
||||
appearance: &Appearance,
|
||||
) {
|
||||
let Some((
|
||||
ResolvedInstructionRef { symbol, section_idx: _, section: _, data, relocation },
|
||||
ins,
|
||||
)) = resolve_instruction(obj, symbol_idx, ins_ref, diff_config)
|
||||
else {
|
||||
let Some(resolved) = obj.resolve_instruction_ref(symbol_idx, ins_ref) else {
|
||||
ui.colored_label(appearance.delete_color, "Failed to resolve instruction");
|
||||
return;
|
||||
};
|
||||
let ins = match obj.arch.process_instruction(resolved, diff_config) {
|
||||
Ok(ins) => ins,
|
||||
Err(e) => {
|
||||
ui.colored_label(
|
||||
appearance.delete_color,
|
||||
format!("Failed to process instruction: {e}"),
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
ui.scope(|ui| {
|
||||
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
|
||||
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
|
||||
|
||||
// TODO
|
||||
// if ui.button(format!("Copy \"{}\"", ins.formatted)).clicked() {
|
||||
// ui.output_mut(|output| output.copied_text.clone_from(&ins.formatted));
|
||||
// ui.close_menu();
|
||||
// }
|
||||
|
||||
let mut hex_string = "0x".to_string();
|
||||
for byte in data {
|
||||
hex_string.push_str(&format!("{:02x}", byte));
|
||||
}
|
||||
if ui.button(format!("Copy \"{hex_string}\" (instruction bytes)")).clicked() {
|
||||
ui.output_mut(|output| output.copied_text = hex_string);
|
||||
ui.close_menu();
|
||||
}
|
||||
|
||||
if let Some(virtual_address) = symbol.virtual_address {
|
||||
let offset = ins_ref.address - symbol.address;
|
||||
let offset_string = format!("{:#x}", virtual_address + offset);
|
||||
if ui.button(format!("Copy \"{offset_string}\" (virtual address)")).clicked() {
|
||||
ui.output_mut(|output| output.copied_text = offset_string);
|
||||
ui.close_menu();
|
||||
}
|
||||
}
|
||||
|
||||
for arg in &ins.args {
|
||||
if let InstructionArg::Value(arg) = arg {
|
||||
match arg {
|
||||
InstructionArgValue::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}\"")).clicked() {
|
||||
ui.output_mut(|output| output.copied_text = v.to_string());
|
||||
ui.close_menu();
|
||||
}
|
||||
}
|
||||
InstructionArgValue::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 = v.to_string());
|
||||
ui.close_menu();
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(resolved) = relocation {
|
||||
// TODO
|
||||
// for literal in obj.arch.display_ins_data_literals(ins) {
|
||||
// if ui.button(format!("Copy \"{literal}\"")).clicked() {
|
||||
// ui.output_mut(|output| output.copied_text.clone_from(&literal));
|
||||
// ui.close_menu();
|
||||
// }
|
||||
// }
|
||||
if let Some(name) = &resolved.symbol.demangled_name {
|
||||
if ui.button(format!("Copy \"{name}\"")).clicked() {
|
||||
ui.output_mut(|output| output.copied_text.clone_from(name));
|
||||
ui.close_menu();
|
||||
}
|
||||
}
|
||||
if ui.button(format!("Copy \"{}\"", resolved.symbol.name)).clicked() {
|
||||
ui.output_mut(|output| output.copied_text.clone_from(&resolved.symbol.name));
|
||||
ui.close_menu();
|
||||
}
|
||||
}
|
||||
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Truncate);
|
||||
context_menu_items_ui(ui, instruction_context(obj, resolved, &ins), column, appearance);
|
||||
});
|
||||
}
|
||||
|
||||
@ -410,7 +244,7 @@ pub(crate) fn asm_col_ui(
|
||||
let response_cb = |response: Response| {
|
||||
if let Some(ins_ref) = ins_row.ins_ref {
|
||||
response.context_menu(|ui| {
|
||||
ins_context_menu(ui, ctx.obj, symbol_ref, ins_ref, diff_config, appearance)
|
||||
ins_context_menu(ui, ctx.obj, symbol_ref, ins_ref, column, diff_config, appearance)
|
||||
});
|
||||
response.on_hover_ui_at_pointer(|ui| {
|
||||
ins_hover_ui(ui, ctx.obj, symbol_ref, ins_ref, diff_config, appearance)
|
||||
|
@ -7,7 +7,6 @@ use std::{
|
||||
use anyhow::Result;
|
||||
use egui::{text::LayoutJob, Context, FontId, RichText, TextFormat, TextStyle, Window};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use strum::{EnumIter, EnumMessage, IntoEnumIterator};
|
||||
|
||||
use crate::views::{appearance::Appearance, frame_history::FrameHistory};
|
||||
|
||||
@ -20,23 +19,24 @@ pub struct GraphicsViewState {
|
||||
pub should_relaunch: bool,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Copy, Clone, Debug, Default, PartialEq, Eq, EnumIter, EnumMessage, Serialize, Deserialize,
|
||||
)]
|
||||
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum GraphicsBackend {
|
||||
#[default]
|
||||
#[strum(message = "Auto")]
|
||||
Auto,
|
||||
#[strum(message = "Vulkan")]
|
||||
Vulkan,
|
||||
#[strum(message = "Metal")]
|
||||
Metal,
|
||||
#[strum(message = "DirectX 12")]
|
||||
Dx12,
|
||||
#[strum(message = "OpenGL")]
|
||||
OpenGL,
|
||||
}
|
||||
|
||||
static ALL_BACKENDS: &[GraphicsBackend] = &[
|
||||
GraphicsBackend::Auto,
|
||||
GraphicsBackend::Vulkan,
|
||||
GraphicsBackend::Metal,
|
||||
GraphicsBackend::Dx12,
|
||||
GraphicsBackend::OpenGL,
|
||||
];
|
||||
|
||||
#[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize)]
|
||||
pub struct GraphicsConfig {
|
||||
#[serde(default)]
|
||||
@ -70,6 +70,16 @@ impl GraphicsBackend {
|
||||
GraphicsBackend::OpenGL => true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn display_name(self) -> &'static str {
|
||||
match self {
|
||||
GraphicsBackend::Auto => "Auto",
|
||||
GraphicsBackend::Vulkan => "Vulkan",
|
||||
GraphicsBackend::Metal => "Metal",
|
||||
GraphicsBackend::Dx12 => "DirectX 12",
|
||||
GraphicsBackend::OpenGL => "OpenGL",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn graphics_window(
|
||||
@ -134,9 +144,9 @@ pub fn graphics_window(
|
||||
ui.add_enabled_ui(state.graphics_config_path.is_some(), |ui| {
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Desired backend:");
|
||||
for backend in GraphicsBackend::iter().filter(GraphicsBackend::is_supported) {
|
||||
for backend in ALL_BACKENDS.iter().copied().filter(GraphicsBackend::is_supported) {
|
||||
let selected = state.graphics_config.desired_backend == backend;
|
||||
if ui.selectable_label(selected, backend.get_message().unwrap()).clicked() {
|
||||
if ui.selectable_label(selected, backend.display_name()).clicked() {
|
||||
let prev_backend = state.graphics_config.desired_backend;
|
||||
state.graphics_config.desired_backend = backend;
|
||||
match save_graphics_config(
|
||||
|
@ -7,8 +7,8 @@ use egui::{
|
||||
use objdiff_core::{
|
||||
diff::{
|
||||
display::{
|
||||
display_sections, symbol_context, symbol_hover, ContextMenuItem, HighlightKind,
|
||||
HoverItem, HoverItemColor, SectionDisplay, SymbolFilter,
|
||||
display_sections, symbol_context, symbol_hover, HighlightKind, SectionDisplay,
|
||||
SymbolFilter, SymbolNavigationKind,
|
||||
},
|
||||
ObjectDiff, SymbolDiff,
|
||||
},
|
||||
@ -21,7 +21,12 @@ use crate::{
|
||||
app::AppStateRef,
|
||||
hotkeys,
|
||||
jobs::{is_create_scratch_available, start_create_scratch},
|
||||
views::{appearance::Appearance, function_diff::FunctionViewState, write_text},
|
||||
views::{
|
||||
appearance::Appearance,
|
||||
diff::{context_menu_items_ui, hover_items_ui},
|
||||
function_diff::FunctionViewState,
|
||||
write_text,
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
@ -71,62 +76,33 @@ pub enum DiffViewAction {
|
||||
/// The symbol reference is the left symbol to map to.
|
||||
SelectingRight(SymbolRefByName),
|
||||
/// Set a symbol mapping.
|
||||
SetMapping(View, SymbolRefByName, SymbolRefByName),
|
||||
SetMapping(usize, usize),
|
||||
/// Set the show_mapped_symbols flag
|
||||
SetShowMappedSymbols(bool),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Eq, PartialEq)]
|
||||
pub struct DiffViewNavigation {
|
||||
pub view: View,
|
||||
pub left_symbol: Option<SymbolRefByName>,
|
||||
pub right_symbol: Option<SymbolRefByName>,
|
||||
pub kind: SymbolNavigationKind,
|
||||
pub left_symbol: Option<usize>,
|
||||
pub right_symbol: Option<usize>,
|
||||
}
|
||||
|
||||
impl DiffViewNavigation {
|
||||
pub fn symbol_diff() -> Self {
|
||||
Self { view: View::SymbolDiff, left_symbol: None, right_symbol: None }
|
||||
}
|
||||
|
||||
pub fn with_symbols(
|
||||
view: View,
|
||||
other_ctx: Option<SymbolDiffContext<'_>>,
|
||||
symbol: &Symbol,
|
||||
section: &Section,
|
||||
symbol_diff: &SymbolDiff,
|
||||
column: usize,
|
||||
) -> Self {
|
||||
let symbol1 = Some(SymbolRefByName::new(symbol, Some(section)));
|
||||
let symbol2 = symbol_diff.target_symbol.and_then(|symbol_ref| {
|
||||
other_ctx.map(|ctx| {
|
||||
let symbol = &ctx.obj.symbols[symbol_ref];
|
||||
let section =
|
||||
symbol.section.and_then(|section_idx| ctx.obj.sections.get(section_idx));
|
||||
SymbolRefByName::new(symbol, section)
|
||||
})
|
||||
});
|
||||
pub fn new(kind: SymbolNavigationKind, symbol_idx: usize, column: usize) -> Self {
|
||||
match column {
|
||||
0 => Self { view, left_symbol: symbol1, right_symbol: symbol2 },
|
||||
1 => Self { view, left_symbol: symbol2, right_symbol: symbol1 },
|
||||
_ => unreachable!("Invalid column index"),
|
||||
0 => Self { kind, left_symbol: Some(symbol_idx), right_symbol: None },
|
||||
1 => Self { kind, left_symbol: None, right_symbol: Some(symbol_idx) },
|
||||
_ => panic!("Invalid column index"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn data_diff(section: &Section, column: usize) -> Self {
|
||||
let symbol = Some(SymbolRefByName {
|
||||
symbol_name: "".to_string(),
|
||||
section_name: Some(section.name.clone()),
|
||||
});
|
||||
match column {
|
||||
0 => Self {
|
||||
view: View::DataDiff,
|
||||
left_symbol: symbol.clone(),
|
||||
right_symbol: symbol.clone(),
|
||||
},
|
||||
1 => Self { view: View::DataDiff, left_symbol: symbol.clone(), right_symbol: symbol },
|
||||
_ => unreachable!("Invalid column index"),
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Clone, Default, Eq, PartialEq)]
|
||||
pub struct ResolvedNavigation {
|
||||
pub view: View,
|
||||
pub left_symbol: Option<SymbolRefByName>,
|
||||
pub right_symbol: Option<SymbolRefByName>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
@ -142,7 +118,7 @@ pub struct DiffViewState {
|
||||
pub scratch_available: bool,
|
||||
pub scratch_running: bool,
|
||||
pub source_path_available: bool,
|
||||
pub post_build_nav: Option<DiffViewNavigation>,
|
||||
pub post_build_nav: Option<ResolvedNavigation>,
|
||||
pub object_name: String,
|
||||
}
|
||||
|
||||
@ -230,24 +206,42 @@ impl DiffViewState {
|
||||
let Ok(mut state) = state.write() else {
|
||||
return;
|
||||
};
|
||||
if (nav.left_symbol.is_some() && nav.right_symbol.is_some())
|
||||
|| (nav.left_symbol.is_none() && nav.right_symbol.is_none())
|
||||
|| nav.view != View::FunctionDiff
|
||||
|
||||
let mut resolved_left = self.resolve_symbol(nav.left_symbol, 0);
|
||||
let mut resolved_right = self.resolve_symbol(nav.right_symbol, 1);
|
||||
if let Some(resolved_right) = &resolved_right {
|
||||
if resolved_left.is_none() {
|
||||
resolved_left = resolved_right
|
||||
.target_symbol
|
||||
.and_then(|idx| self.resolve_symbol(Some(idx), 0));
|
||||
}
|
||||
}
|
||||
if let Some(resolved_left) = &resolved_left {
|
||||
if resolved_right.is_none() {
|
||||
resolved_right = resolved_left
|
||||
.target_symbol
|
||||
.and_then(|idx| self.resolve_symbol(Some(idx), 1));
|
||||
}
|
||||
}
|
||||
let resolved_nav = resolve_navigation(nav.kind, resolved_left, resolved_right);
|
||||
if (resolved_nav.left_symbol.is_some() && resolved_nav.right_symbol.is_some())
|
||||
|| (resolved_nav.left_symbol.is_none() && resolved_nav.right_symbol.is_none())
|
||||
|| resolved_nav.view != View::FunctionDiff
|
||||
{
|
||||
// Regular navigation
|
||||
if state.is_selecting_symbol() {
|
||||
// Cancel selection and reload
|
||||
state.clear_selection();
|
||||
self.post_build_nav = Some(nav);
|
||||
self.post_build_nav = Some(resolved_nav);
|
||||
} else {
|
||||
// Navigate immediately
|
||||
self.current_view = nav.view;
|
||||
self.symbol_state.left_symbol = nav.left_symbol;
|
||||
self.symbol_state.right_symbol = nav.right_symbol;
|
||||
self.current_view = resolved_nav.view;
|
||||
self.symbol_state.left_symbol = resolved_nav.left_symbol;
|
||||
self.symbol_state.right_symbol = resolved_nav.right_symbol;
|
||||
}
|
||||
} else {
|
||||
// Enter selection mode
|
||||
match (&nav.left_symbol, &nav.right_symbol) {
|
||||
match (&resolved_nav.left_symbol, &resolved_nav.right_symbol) {
|
||||
(Some(left_ref), None) => {
|
||||
state.set_selecting_right(&left_ref.symbol_name);
|
||||
}
|
||||
@ -257,7 +251,7 @@ impl DiffViewState {
|
||||
(Some(_), Some(_)) => unreachable!(),
|
||||
(None, None) => unreachable!(),
|
||||
}
|
||||
self.post_build_nav = Some(nav);
|
||||
self.post_build_nav = Some(resolved_nav);
|
||||
}
|
||||
}
|
||||
DiffViewAction::SetSymbolHighlight(left, right, autoscroll) => {
|
||||
@ -306,7 +300,7 @@ impl DiffViewState {
|
||||
return;
|
||||
};
|
||||
state.set_selecting_left(&right_ref.symbol_name);
|
||||
self.post_build_nav = Some(DiffViewNavigation {
|
||||
self.post_build_nav = Some(ResolvedNavigation {
|
||||
view: View::FunctionDiff,
|
||||
left_symbol: None,
|
||||
right_symbol: Some(right_ref),
|
||||
@ -321,13 +315,13 @@ impl DiffViewState {
|
||||
return;
|
||||
};
|
||||
state.set_selecting_right(&left_ref.symbol_name);
|
||||
self.post_build_nav = Some(DiffViewNavigation {
|
||||
self.post_build_nav = Some(ResolvedNavigation {
|
||||
view: View::FunctionDiff,
|
||||
left_symbol: Some(left_ref),
|
||||
right_symbol: None,
|
||||
});
|
||||
}
|
||||
DiffViewAction::SetMapping(view, left_ref, right_ref) => {
|
||||
DiffViewAction::SetMapping(left_ref, right_ref) => {
|
||||
if self.post_build_nav.is_some() {
|
||||
// Ignore action if we're already navigating
|
||||
return;
|
||||
@ -335,25 +329,133 @@ impl DiffViewState {
|
||||
let Ok(mut state) = state.write() else {
|
||||
return;
|
||||
};
|
||||
state.set_symbol_mapping(
|
||||
left_ref.symbol_name.clone(),
|
||||
right_ref.symbol_name.clone(),
|
||||
);
|
||||
if view == View::SymbolDiff {
|
||||
self.post_build_nav = Some(DiffViewNavigation::symbol_diff());
|
||||
let resolved_nav = if let (Some(left_ref), Some(right_ref)) = (
|
||||
self.resolve_symbol(Some(left_ref), 0),
|
||||
self.resolve_symbol(Some(right_ref), 1),
|
||||
) {
|
||||
state.set_symbol_mapping(
|
||||
left_ref.symbol.name.clone(),
|
||||
right_ref.symbol.name.clone(),
|
||||
);
|
||||
resolve_navigation(
|
||||
SymbolNavigationKind::Normal,
|
||||
Some(left_ref),
|
||||
Some(right_ref),
|
||||
)
|
||||
} else {
|
||||
self.post_build_nav = Some(DiffViewNavigation {
|
||||
view,
|
||||
left_symbol: Some(left_ref),
|
||||
right_symbol: Some(right_ref),
|
||||
});
|
||||
}
|
||||
ResolvedNavigation::default()
|
||||
};
|
||||
self.post_build_nav = Some(resolved_nav);
|
||||
}
|
||||
DiffViewAction::SetShowMappedSymbols(value) => {
|
||||
self.symbol_state.show_mapped_symbols = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_symbol(&self, symbol_idx: Option<usize>, column: usize) -> Option<ResolvedSymbol> {
|
||||
let symbol_idx = symbol_idx?;
|
||||
let result = self.build.as_deref()?;
|
||||
let (obj, diff) = match column {
|
||||
0 => result.first_obj.as_ref()?,
|
||||
1 => result.second_obj.as_ref()?,
|
||||
_ => return None,
|
||||
};
|
||||
let symbol = obj.symbols.get(symbol_idx)?;
|
||||
let section_idx = symbol.section?;
|
||||
let section = obj.sections.get(section_idx)?;
|
||||
let symbol_diff = diff.symbols.get(symbol_idx)?;
|
||||
Some(ResolvedSymbol {
|
||||
symbol_ref: SymbolRefByName::new(symbol, Some(section)),
|
||||
symbol,
|
||||
section,
|
||||
target_symbol: symbol_diff.target_symbol,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct ResolvedSymbol<'obj> {
|
||||
symbol_ref: SymbolRefByName,
|
||||
symbol: &'obj Symbol,
|
||||
section: &'obj Section,
|
||||
target_symbol: Option<usize>,
|
||||
}
|
||||
|
||||
/// Determine the navigation target based on the resolved symbols.
|
||||
fn resolve_navigation(
|
||||
kind: SymbolNavigationKind,
|
||||
resolved_left: Option<ResolvedSymbol>,
|
||||
resolved_right: Option<ResolvedSymbol>,
|
||||
) -> ResolvedNavigation {
|
||||
match (resolved_left, resolved_right) {
|
||||
(Some(left), Some(right)) => match (left.section.kind, right.section.kind) {
|
||||
(SectionKind::Code, SectionKind::Code) => ResolvedNavigation {
|
||||
view: match kind {
|
||||
SymbolNavigationKind::Normal => View::FunctionDiff,
|
||||
SymbolNavigationKind::Extab => View::ExtabDiff,
|
||||
},
|
||||
left_symbol: Some(left.symbol_ref),
|
||||
right_symbol: Some(right.symbol_ref),
|
||||
},
|
||||
(SectionKind::Data, SectionKind::Data) => ResolvedNavigation {
|
||||
view: View::DataDiff,
|
||||
left_symbol: Some(SymbolRefByName {
|
||||
symbol_name: "".to_string(),
|
||||
section_name: Some(left.section.name.clone()),
|
||||
}),
|
||||
right_symbol: Some(SymbolRefByName {
|
||||
symbol_name: "".to_string(),
|
||||
section_name: Some(right.section.name.clone()),
|
||||
}),
|
||||
},
|
||||
_ => ResolvedNavigation::default(),
|
||||
},
|
||||
(Some(left), None) => match left.section.kind {
|
||||
SectionKind::Code => ResolvedNavigation {
|
||||
view: match kind {
|
||||
SymbolNavigationKind::Normal => View::FunctionDiff,
|
||||
SymbolNavigationKind::Extab => View::ExtabDiff,
|
||||
},
|
||||
left_symbol: Some(left.symbol_ref),
|
||||
right_symbol: None,
|
||||
},
|
||||
SectionKind::Data => ResolvedNavigation {
|
||||
view: View::DataDiff,
|
||||
left_symbol: Some(SymbolRefByName {
|
||||
symbol_name: "".to_string(),
|
||||
section_name: Some(left.section.name.clone()),
|
||||
}),
|
||||
right_symbol: Some(SymbolRefByName {
|
||||
symbol_name: "".to_string(),
|
||||
section_name: Some(left.section.name.clone()),
|
||||
}),
|
||||
},
|
||||
_ => ResolvedNavigation::default(),
|
||||
},
|
||||
(None, Some(right)) => match right.section.kind {
|
||||
SectionKind::Code => ResolvedNavigation {
|
||||
view: match kind {
|
||||
SymbolNavigationKind::Normal => View::FunctionDiff,
|
||||
SymbolNavigationKind::Extab => View::ExtabDiff,
|
||||
},
|
||||
left_symbol: None,
|
||||
right_symbol: Some(right.symbol_ref),
|
||||
},
|
||||
SectionKind::Data => ResolvedNavigation {
|
||||
view: View::DataDiff,
|
||||
left_symbol: Some(SymbolRefByName {
|
||||
symbol_name: "".to_string(),
|
||||
section_name: Some(right.section.name.clone()),
|
||||
}),
|
||||
right_symbol: Some(SymbolRefByName {
|
||||
symbol_name: "".to_string(),
|
||||
section_name: Some(right.section.name.clone()),
|
||||
}),
|
||||
},
|
||||
_ => ResolvedNavigation::default(),
|
||||
},
|
||||
(None, None) => ResolvedNavigation::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn match_color_for_symbol(match_percent: f32, appearance: &Appearance) -> Color32 {
|
||||
@ -366,65 +468,33 @@ pub fn match_color_for_symbol(match_percent: f32, appearance: &Appearance) -> Co
|
||||
}
|
||||
}
|
||||
|
||||
fn symbol_context_menu_ui(
|
||||
pub fn symbol_context_menu_ui(
|
||||
ui: &mut Ui,
|
||||
ctx: SymbolDiffContext<'_>,
|
||||
other_ctx: Option<SymbolDiffContext<'_>>,
|
||||
symbol_idx: usize,
|
||||
symbol: &Symbol,
|
||||
symbol_diff: &SymbolDiff,
|
||||
section: Option<&Section>,
|
||||
column: usize,
|
||||
) -> Option<DiffViewNavigation> {
|
||||
appearance: &Appearance,
|
||||
) -> Option<DiffViewAction> {
|
||||
let mut ret = None;
|
||||
ui.scope(|ui| {
|
||||
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
|
||||
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
|
||||
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Truncate);
|
||||
|
||||
for item in symbol_context(ctx.obj, symbol) {
|
||||
match item {
|
||||
ContextMenuItem::Copy { value, label } => {
|
||||
let label = if let Some(extra) = label {
|
||||
format!("Copy \"{value}\" ({extra})")
|
||||
} else {
|
||||
format!("Copy \"{value}\"")
|
||||
};
|
||||
if ui.button(label).clicked() {
|
||||
ui.output_mut(|output| output.copied_text = value);
|
||||
ui.close_menu();
|
||||
}
|
||||
}
|
||||
ContextMenuItem::Navigate { label } => {
|
||||
if ui.button(label).clicked() {
|
||||
// TODO other navigation
|
||||
ret = Some(DiffViewNavigation::with_symbols(
|
||||
View::ExtabDiff,
|
||||
other_ctx,
|
||||
symbol,
|
||||
section.unwrap(),
|
||||
symbol_diff,
|
||||
column,
|
||||
));
|
||||
ui.close_menu();
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(action) =
|
||||
context_menu_items_ui(ui, symbol_context(ctx.obj, symbol_idx), column, appearance)
|
||||
{
|
||||
ret = Some(action);
|
||||
}
|
||||
|
||||
if let Some(section) = section {
|
||||
if ui.button("Map symbol").clicked() {
|
||||
let symbol_ref = SymbolRefByName::new(symbol, Some(section));
|
||||
if column == 0 {
|
||||
ret = Some(DiffViewNavigation {
|
||||
view: View::FunctionDiff,
|
||||
left_symbol: Some(symbol_ref),
|
||||
right_symbol: None,
|
||||
});
|
||||
ret = Some(DiffViewAction::SelectingRight(symbol_ref));
|
||||
} else {
|
||||
ret = Some(DiffViewNavigation {
|
||||
view: View::FunctionDiff,
|
||||
left_symbol: None,
|
||||
right_symbol: Some(symbol_ref),
|
||||
});
|
||||
ret = Some(DiffViewAction::SelectingLeft(symbol_ref));
|
||||
}
|
||||
ui.close_menu();
|
||||
}
|
||||
@ -433,24 +503,16 @@ fn symbol_context_menu_ui(
|
||||
ret
|
||||
}
|
||||
|
||||
fn symbol_hover_ui(
|
||||
pub fn symbol_hover_ui(
|
||||
ui: &mut Ui,
|
||||
ctx: SymbolDiffContext<'_>,
|
||||
symbol: &Symbol,
|
||||
symbol_idx: usize,
|
||||
appearance: &Appearance,
|
||||
) {
|
||||
ui.scope(|ui| {
|
||||
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
|
||||
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
|
||||
|
||||
for HoverItem { text, color } in symbol_hover(ctx.obj, symbol) {
|
||||
let color = match color {
|
||||
HoverItemColor::Normal => appearance.text_color,
|
||||
HoverItemColor::Emphasized => appearance.highlight_color,
|
||||
HoverItemColor::Special => appearance.replace_color,
|
||||
};
|
||||
ui.colored_label(color, text);
|
||||
}
|
||||
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Wrap);
|
||||
hover_items_ui(ui, symbol_hover(ctx.obj, symbol_idx, 0), appearance);
|
||||
});
|
||||
}
|
||||
|
||||
@ -458,7 +520,6 @@ fn symbol_hover_ui(
|
||||
fn symbol_ui(
|
||||
ui: &mut Ui,
|
||||
ctx: SymbolDiffContext<'_>,
|
||||
other_ctx: Option<SymbolDiffContext<'_>>,
|
||||
symbol: &Symbol,
|
||||
symbol_diff: &SymbolDiff,
|
||||
symbol_idx: usize,
|
||||
@ -515,12 +576,12 @@ fn symbol_ui(
|
||||
write_text(name, appearance.highlight_color, &mut job, appearance.code_font.clone());
|
||||
let response = SelectableLabel::new(selected, job)
|
||||
.ui(ui)
|
||||
.on_hover_ui_at_pointer(|ui| symbol_hover_ui(ui, ctx, symbol, appearance));
|
||||
.on_hover_ui_at_pointer(|ui| symbol_hover_ui(ui, ctx, symbol_idx, appearance));
|
||||
response.context_menu(|ui| {
|
||||
if let Some(result) =
|
||||
symbol_context_menu_ui(ui, ctx, other_ctx, symbol, symbol_diff, section, column)
|
||||
symbol_context_menu_ui(ui, ctx, symbol_idx, symbol, section, column, appearance)
|
||||
{
|
||||
ret = Some(DiffViewAction::Navigate(result));
|
||||
ret = Some(result);
|
||||
}
|
||||
});
|
||||
if selected && state.autoscroll_to_highlighted_symbols {
|
||||
@ -532,26 +593,11 @@ fn symbol_ui(
|
||||
// manually scroll away.
|
||||
}
|
||||
if response.clicked() || (selected && hotkeys::enter_pressed(ui.ctx())) {
|
||||
if let Some(section) = section {
|
||||
match section.kind {
|
||||
SectionKind::Code => {
|
||||
ret = Some(DiffViewAction::Navigate(DiffViewNavigation::with_symbols(
|
||||
View::FunctionDiff,
|
||||
other_ctx,
|
||||
symbol,
|
||||
section,
|
||||
symbol_diff,
|
||||
column,
|
||||
)));
|
||||
}
|
||||
SectionKind::Data => {
|
||||
ret = Some(DiffViewAction::Navigate(DiffViewNavigation::data_diff(
|
||||
section, column,
|
||||
)));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
ret = Some(DiffViewAction::Navigate(DiffViewNavigation::new(
|
||||
SymbolNavigationKind::Normal,
|
||||
symbol_idx,
|
||||
column,
|
||||
)));
|
||||
} else if response.hovered() {
|
||||
ret = Some(if column == 0 {
|
||||
DiffViewAction::SetSymbolHighlight(Some(symbol_idx), symbol_diff.target_symbol, false)
|
||||
@ -597,7 +643,6 @@ fn find_last_symbol(section_display: &[SectionDisplay]) -> Option<usize> {
|
||||
pub fn symbol_list_ui(
|
||||
ui: &mut Ui,
|
||||
ctx: SymbolDiffContext<'_>,
|
||||
other_ctx: Option<SymbolDiffContext<'_>>,
|
||||
state: &SymbolViewState,
|
||||
filter: SymbolFilter<'_>,
|
||||
appearance: &Appearance,
|
||||
@ -720,7 +765,6 @@ pub fn symbol_list_ui(
|
||||
if let Some(result) = symbol_ui(
|
||||
ui,
|
||||
ctx,
|
||||
other_ctx,
|
||||
symbol,
|
||||
symbol_diff,
|
||||
symbol_display.symbol,
|
||||
|
Loading…
x
Reference in New Issue
Block a user