Unify context menu / hover tooltip code + UI improvements

This commit is contained in:
2025-03-02 15:20:29 -07:00
parent 8461b35cd7
commit a1f2a535e5
26 changed files with 1730 additions and 1095 deletions

View File

@@ -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"

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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 }
}

View File

@@ -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> {

View File

@@ -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());

View File

@@ -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));
}

View File

@@ -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 {

View File

@@ -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()
}

View File

@@ -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,
}
}
}

View File

@@ -780,6 +780,7 @@ pub fn parse(data: &[u8], config: &DiffObjConfig) -> Result<Object> {
}
Ok(Object {
arch,
endianness: obj_file.endianness(),
symbols,
sections,
split_meta,

View File

@@ -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() {

Binary file not shown.

View 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,
}

View File

@@ -6,6 +6,7 @@ Object {
arch: ArchPpc {
extab: None,
},
endianness: Big,
symbols: [
Symbol {
name: "IObj.cpp",

View File

@@ -7,6 +7,7 @@ Object {
bits: 32,
endianness: Little,
},
endianness: Little,
symbols: [
Symbol {
name: "objdiffstaticdebug.cpp",