mirror of
https://github.com/encounter/objdiff.git
synced 2025-12-09 13:37:55 +00:00
Unify context menu / hover tooltip code + UI improvements
This commit is contained in:
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user