Compare commits

...

4 Commits

Author SHA1 Message Date
Robin Avery 9a386cefc6
Merge cf15d3b7e7 into 1343f4fd2b 2024-03-17 15:10:06 -06:00
Luke Street 1343f4fd2b cargo fmt 2024-03-17 12:20:25 -06:00
Luke Street 9df98f263e Move all architecture-specific code into modules
No more scattered relocation handling and
feature checks. Everything will go through
the ObjArch trait, which makes it easier
to add new architectures going forward.
2024-03-17 12:16:47 -06:00
Robin Avery cf15d3b7e7
objdiff-cli report: Support data sections 2024-03-01 22:03:44 -05:00
16 changed files with 901 additions and 845 deletions

View File

@ -41,7 +41,7 @@ pub struct GenerateArgs {
/// Output JSON file
output: Option<PathBuf>,
#[argp(switch, short = 'd')]
/// Deduplicate global and weak symbols
/// Deduplicate global and weak symbols (runs single-threaded)
deduplicate: bool,
}
@ -63,9 +63,12 @@ pub struct ChangesArgs {
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
struct Report {
fuzzy_match_percent: f32,
total_size: u64,
matched_size: u64,
matched_size_percent: f32,
total_code: u64,
matched_code: u64,
matched_code_percent: f32,
total_data: u64,
matched_data: u64,
matched_data_percent: f32,
total_functions: u32,
matched_functions: u32,
matched_functions_percent: f32,
@ -76,8 +79,10 @@ struct Report {
struct ReportUnit {
name: String,
fuzzy_match_percent: f32,
total_size: u64,
matched_size: u64,
total_code: u64,
matched_code: u64,
total_data: u64,
matched_data: u64,
total_functions: u32,
matched_functions: u32,
#[serde(skip_serializing_if = "Option::is_none")]
@ -86,11 +91,12 @@ struct ReportUnit {
module_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
module_id: Option<u32>,
functions: Vec<ReportFunction>,
sections: Vec<ReportItem>,
functions: Vec<ReportItem>,
}
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
struct ReportFunction {
struct ReportItem {
name: String,
#[serde(skip_serializing_if = "Option::is_none")]
demangled_name: Option<String>,
@ -159,21 +165,29 @@ fn generate(args: GenerateArgs) -> Result<()> {
report.units = units.into_iter().flatten().collect();
}
for unit in &report.units {
report.fuzzy_match_percent += unit.fuzzy_match_percent * unit.total_size as f32;
report.total_size += unit.total_size;
report.matched_size += unit.matched_size;
report.fuzzy_match_percent += unit.fuzzy_match_percent * unit.total_code as f32;
report.total_code += unit.total_code;
report.matched_code += unit.matched_code;
report.total_data += unit.total_data;
report.matched_data += unit.matched_data;
report.total_functions += unit.total_functions;
report.matched_functions += unit.matched_functions;
}
if report.total_size == 0 {
if report.total_code == 0 {
report.fuzzy_match_percent = 100.0;
} else {
report.fuzzy_match_percent /= report.total_size as f32;
report.fuzzy_match_percent /= report.total_code as f32;
}
report.matched_size_percent = if report.total_size == 0 {
report.matched_code_percent = if report.total_code == 0 {
100.0
} else {
report.matched_size as f32 / report.total_size as f32 * 100.0
report.matched_code as f32 / report.total_code as f32 * 100.0
};
report.matched_data_percent = if report.total_data == 0 {
100.0
} else {
report.matched_data as f32 / report.total_data as f32 * 100.0
};
report.matched_functions_percent = if report.total_functions == 0 {
100.0
@ -215,7 +229,6 @@ fn report_object(
}
_ => {}
}
// println!("Checking {}", object.name());
let mut target = object
.target_path
.as_ref()
@ -239,10 +252,39 @@ fn report_object(
..Default::default()
};
let obj = target.as_ref().or(base.as_ref()).unwrap();
for section in target
.as_ref()
.map_or(&vec![], |o| &o.sections)
.iter()
.chain(base.as_ref().map_or(&vec![], |o| &o.sections))
.filter(|o| o.match_percent != 0.0)
{
// TODO
println!("{}: {}", section.name, section.match_percent);
}
for section in &obj.sections {
if section.kind != ObjSectionKind::Code {
continue;
unit.sections.push(ReportItem {
name: section.name.clone(),
demangled_name: None,
fuzzy_match_percent: section.match_percent,
size: section.size,
address: section.virtual_address,
});
match section.kind {
ObjSectionKind::Data | ObjSectionKind::Bss => {
unit.total_data += section.size;
// section.data_diff
if section.match_percent == 100.0 {
unit.matched_data += section.size;
}
continue;
}
ObjSectionKind::Code => (),
}
for symbol in &section.symbols {
if symbol.size == 0 {
continue;
@ -265,11 +307,11 @@ fn report_object(
}
});
unit.fuzzy_match_percent += match_percent * symbol.size as f32;
unit.total_size += symbol.size;
unit.total_code += symbol.size;
if match_percent == 100.0 {
unit.matched_size += symbol.size;
unit.matched_code += symbol.size;
}
unit.functions.push(ReportFunction {
unit.functions.push(ReportItem {
name: symbol.name.clone(),
demangled_name: symbol.demangled_name.clone(),
size: symbol.size,
@ -282,10 +324,10 @@ fn report_object(
unit.total_functions += 1;
}
}
if unit.total_size == 0 {
if unit.total_code == 0 {
unit.fuzzy_match_percent = 100.0;
} else {
unit.fuzzy_match_percent /= unit.total_size as f32;
unit.fuzzy_match_percent /= unit.total_code as f32;
}
Ok(Some(unit))
}
@ -300,9 +342,12 @@ struct Changes {
#[derive(Debug, Clone, Default, PartialEq, serde::Serialize, serde::Deserialize)]
struct ChangeInfo {
fuzzy_match_percent: f32,
total_size: u64,
matched_size: u64,
matched_size_percent: f32,
total_code: u64,
matched_code: u64,
matched_code_percent: f32,
total_data: u64,
matched_data: u64,
matched_data_percent: f32,
total_functions: u32,
matched_functions: u32,
matched_functions_percent: f32,
@ -312,9 +357,12 @@ impl From<&Report> for ChangeInfo {
fn from(report: &Report) -> Self {
Self {
fuzzy_match_percent: report.fuzzy_match_percent,
total_size: report.total_size,
matched_size: report.matched_size,
matched_size_percent: report.matched_size_percent,
total_code: report.total_code,
matched_code: report.matched_code,
matched_code_percent: report.matched_code_percent,
total_data: report.total_data,
matched_data: report.matched_data,
matched_data_percent: report.matched_data_percent,
total_functions: report.total_functions,
matched_functions: report.matched_functions,
matched_functions_percent: report.matched_functions_percent,
@ -326,12 +374,19 @@ impl From<&ReportUnit> for ChangeInfo {
fn from(value: &ReportUnit) -> Self {
Self {
fuzzy_match_percent: value.fuzzy_match_percent,
total_size: value.total_size,
matched_size: value.matched_size,
matched_size_percent: if value.total_size == 0 {
total_code: value.total_code,
matched_code: value.matched_code,
matched_code_percent: if value.total_code == 0 {
100.0
} else {
value.matched_size as f32 / value.total_size as f32 * 100.0
value.matched_code as f32 / value.total_code as f32 * 100.0
},
total_data: value.total_data,
matched_data: value.matched_data,
matched_data_percent: if value.total_data == 0 {
100.0
} else {
value.matched_data as f32 / value.total_data as f32 * 100.0
},
total_functions: value.total_functions,
matched_functions: value.matched_functions,
@ -349,24 +404,25 @@ struct ChangeUnit {
name: String,
from: Option<ChangeInfo>,
to: Option<ChangeInfo>,
functions: Vec<ChangeFunction>,
sections: Vec<ChangeItem>,
functions: Vec<ChangeItem>,
}
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
struct ChangeFunction {
struct ChangeItem {
name: String,
from: Option<ChangeFunctionInfo>,
to: Option<ChangeFunctionInfo>,
from: Option<ChangeItemInfo>,
to: Option<ChangeItemInfo>,
}
#[derive(Debug, Clone, Default, PartialEq, serde::Serialize, serde::Deserialize)]
struct ChangeFunctionInfo {
struct ChangeItemInfo {
fuzzy_match_percent: f32,
size: u64,
}
impl From<&ReportFunction> for ChangeFunctionInfo {
fn from(value: &ReportFunction) -> Self {
impl From<&ReportItem> for ChangeItemInfo {
fn from(value: &ReportItem) -> Self {
Self { fuzzy_match_percent: value.fuzzy_match_percent, size: value.size }
}
}
@ -380,54 +436,18 @@ fn changes(args: ChangesArgs) -> Result<()> {
units: vec![],
};
for prev_unit in &previous.units {
let prev_unit_info = ChangeInfo::from(prev_unit);
let curr_unit = current.units.iter().find(|u| u.name == prev_unit.name);
let sections = process_items(prev_unit, curr_unit, |u| &u.sections);
let functions = process_items(prev_unit, curr_unit, |u| &u.functions);
let prev_unit_info = ChangeInfo::from(prev_unit);
let curr_unit_info = curr_unit.map(ChangeInfo::from);
let mut functions = vec![];
if let Some(curr_unit) = curr_unit {
for prev_func in &prev_unit.functions {
let prev_func_info = ChangeFunctionInfo::from(prev_func);
let curr_func = curr_unit.functions.iter().find(|f| f.name == prev_func.name);
let curr_func_info = curr_func.map(ChangeFunctionInfo::from);
if let Some(curr_func_info) = curr_func_info {
if prev_func_info != curr_func_info {
functions.push(ChangeFunction {
name: prev_func.name.clone(),
from: Some(prev_func_info),
to: Some(curr_func_info),
});
}
} else {
functions.push(ChangeFunction {
name: prev_func.name.clone(),
from: Some(prev_func_info),
to: None,
});
}
}
for curr_func in &curr_unit.functions {
if !prev_unit.functions.iter().any(|f| f.name == curr_func.name) {
functions.push(ChangeFunction {
name: curr_func.name.clone(),
from: None,
to: Some(ChangeFunctionInfo::from(curr_func)),
});
}
}
} else {
for prev_func in &prev_unit.functions {
functions.push(ChangeFunction {
name: prev_func.name.clone(),
from: Some(ChangeFunctionInfo::from(prev_func)),
to: None,
});
}
}
if !functions.is_empty() || !matches!(&curr_unit_info, Some(v) if v == &prev_unit_info) {
changes.units.push(ChangeUnit {
name: prev_unit.name.clone(),
from: Some(prev_unit_info),
to: curr_unit_info,
sections,
functions,
});
}
@ -438,15 +458,8 @@ fn changes(args: ChangesArgs) -> Result<()> {
name: curr_unit.name.clone(),
from: None,
to: Some(ChangeInfo::from(curr_unit)),
functions: curr_unit
.functions
.iter()
.map(|f| ChangeFunction {
name: f.name.clone(),
from: None,
to: Some(ChangeFunctionInfo::from(f)),
})
.collect(),
sections: process_new_items(&curr_unit.sections),
functions: process_new_items(&curr_unit.functions),
});
}
}
@ -464,6 +477,63 @@ fn changes(args: ChangesArgs) -> Result<()> {
Ok(())
}
fn process_items<F: Fn(&ReportUnit) -> &Vec<ReportItem>>(
prev_unit: &ReportUnit,
curr_unit: Option<&ReportUnit>,
getter: F,
) -> Vec<ChangeItem> {
let prev_items = getter(prev_unit);
let mut items = vec![];
if let Some(curr_unit) = curr_unit {
let curr_items = getter(curr_unit);
for prev_func in prev_items {
let prev_func_info = ChangeItemInfo::from(prev_func);
let curr_func = curr_items.iter().find(|f| f.name == prev_func.name);
let curr_func_info = curr_func.map(ChangeItemInfo::from);
if let Some(curr_func_info) = curr_func_info {
if prev_func_info != curr_func_info {
items.push(ChangeItem {
name: prev_func.name.clone(),
from: Some(prev_func_info),
to: Some(curr_func_info),
});
}
} else {
items.push(ChangeItem {
name: prev_func.name.clone(),
from: Some(prev_func_info),
to: None,
});
}
}
for curr_func in curr_items {
if !prev_items.iter().any(|f| f.name == curr_func.name) {
items.push(ChangeItem {
name: curr_func.name.clone(),
from: None,
to: Some(ChangeItemInfo::from(curr_func)),
});
}
}
} else {
for prev_func in prev_items {
items.push(ChangeItem {
name: prev_func.name.clone(),
from: Some(ChangeItemInfo::from(prev_func)),
to: None,
});
}
}
items
}
fn process_new_items(items: &[ReportItem]) -> Vec<ChangeItem> {
items
.iter()
.map(|f| ChangeItem { name: f.name.clone(), from: None, to: Some(ChangeItemInfo::from(f)) })
.collect()
}
fn read_report(path: &Path) -> Result<Report> {
serde_json::from_reader(BufReader::new(
File::open(path).with_context(|| format!("Failed to open {}", path.display()))?,

View File

@ -14,7 +14,7 @@ A local diffing tool for decompilation projects.
[features]
all = ["config", "dwarf", "mips", "ppc", "x86"]
any-arch = [] # Implicit, used to check if any arch is enabled
config = []
config = ["globset", "semver", "serde_json", "serde_yaml"]
dwarf = ["gimli"]
mips = ["any-arch", "rabbitizer"]
ppc = ["any-arch", "cwdemangle", "ppc750cl"]
@ -23,30 +23,35 @@ x86 = ["any-arch", "iced-x86", "msvc-demangler"]
[dependencies]
anyhow = "1.0.81"
byteorder = "1.5.0"
cwdemangle = { version = "1.0.0", optional = true }
filetime = "0.2.23"
flagset = "0.4.5"
gimli = { version = "0.28.1", default-features = false, features = ["read-all"], optional = true }
log = "0.4.21"
memmap2 = "0.9.4"
num-traits = "0.2.18"
object = { version = "0.34.0", features = ["read_core", "std", "elf", "pe"], default-features = false }
ppc750cl = { git = "https://github.com/encounter/ppc750cl", rev = "4a2bbbc6f84dcb76255ab6f3595a8d4a0ce96618", optional = true }
rabbitizer = { version = "1.9.2", optional = true }
serde = { version = "1", features = ["derive"] }
similar = { version = "2.4.0", default-features = false }
# config
globset = { version = "0.4.14", features = ["serde1"] }
semver = "1.0.22"
serde_json = "1.0.114"
serde_yaml = "0.9.32"
globset = { version = "0.4.14", features = ["serde1"], optional = true }
semver = { version = "1.0.22", optional = true }
serde_json = { version = "1.0.114", optional = true }
serde_yaml = { version = "0.9.32", optional = true }
# dwarf
gimli = { version = "0.28.1", default-features = false, features = ["read-all"], optional = true }
# ppc
cwdemangle = { version = "1.0.0", optional = true }
ppc750cl = { git = "https://github.com/encounter/ppc750cl", rev = "4a2bbbc6f84dcb76255ab6f3595a8d4a0ce96618", optional = true }
# mips
rabbitizer = { version = "1.9.2", optional = true }
# x86
msvc-demangler = { version = "0.10.0", optional = true }
[dependencies.iced-x86]
version = "1.21.0"
#default-features = false
#features = ["std", "decoder", "intel"]
features = ["exhaustive_enums"]
default-features = false
features = ["std", "decoder", "intel", "gas", "masm", "nasm", "exhaustive_enums"]
optional = true

View File

@ -0,0 +1,207 @@
use std::{borrow::Cow, collections::BTreeMap};
use anyhow::{bail, Result};
use object::{elf, Endian, Endianness, File, Object, Relocation, RelocationFlags};
use rabbitizer::{config, Abi, InstrCategory, Instruction, OperandType};
use crate::{
arch::ObjArch,
diff::{DiffObjConfig, ProcessCodeResult},
obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection},
};
fn configure_rabbitizer() {
unsafe {
config::RabbitizerConfig_Cfg.reg_names.fpr_abi_names = Abi::O32;
}
}
pub struct ObjArchMips {
pub endianness: Endianness,
}
impl ObjArchMips {
pub fn new(object: &File) -> Result<Self> { Ok(Self { endianness: object.endianness() }) }
}
impl ObjArch for ObjArchMips {
fn process_code(
&self,
config: &DiffObjConfig,
data: &[u8],
start_address: u64,
relocs: &[ObjReloc],
line_info: &Option<BTreeMap<u64, u64>>,
) -> Result<ProcessCodeResult> {
configure_rabbitizer();
let end_address = start_address + data.len() as u64;
let ins_count = data.len() / 4;
let mut ops = Vec::<u16>::with_capacity(ins_count);
let mut insts = Vec::<ObjIns>::with_capacity(ins_count);
let mut cur_addr = start_address as u32;
for chunk in data.chunks_exact(4) {
let reloc = relocs.iter().find(|r| (r.address as u32 & !3) == cur_addr);
let code = self.endianness.read_u32_bytes(chunk.try_into()?);
let instruction = Instruction::new(code, cur_addr, InstrCategory::CPU);
let op = instruction.unique_id as u16;
ops.push(op);
let mnemonic = instruction.opcode_name().to_string();
let is_branch = instruction.is_branch();
let branch_offset = instruction.branch_offset();
let branch_dest = if is_branch {
cur_addr.checked_add_signed(branch_offset).map(|a| a as u64)
} else {
None
};
let operands = instruction.get_operands_slice();
let mut args = Vec::with_capacity(operands.len() + 1);
for (idx, op) in operands.iter().enumerate() {
if idx > 0 {
if config.space_between_args {
args.push(ObjInsArg::PlainText(", ".to_string()));
} else {
args.push(ObjInsArg::PlainText(",".to_string()));
}
}
match op {
OperandType::cpu_immediate
| OperandType::cpu_label
| OperandType::cpu_branch_target_label => {
if let Some(branch_dest) = branch_dest {
args.push(ObjInsArg::BranchDest(branch_dest));
} else if let Some(reloc) = reloc {
if matches!(&reloc.target_section, Some(s) if s == ".text")
&& reloc.target.address > start_address
&& reloc.target.address < end_address
{
args.push(ObjInsArg::BranchDest(reloc.target.address));
} else {
push_reloc(&mut args, reloc)?;
}
} else {
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(
op.disassemble(&instruction, None),
)));
}
}
OperandType::cpu_immediate_base => {
if let Some(reloc) = reloc {
push_reloc(&mut args, reloc)?;
} else {
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(
OperandType::cpu_immediate.disassemble(&instruction, None),
)));
}
args.push(ObjInsArg::PlainText("(".to_string()));
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(
OperandType::cpu_rs.disassemble(&instruction, None),
)));
args.push(ObjInsArg::PlainText(")".to_string()));
}
_ => {
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(
op.disassemble(&instruction, None),
)));
}
}
}
let line = line_info
.as_ref()
.and_then(|map| map.range(..=cur_addr as u64).last().map(|(_, &b)| b));
insts.push(ObjIns {
address: cur_addr as u64,
size: 4,
op,
mnemonic,
args,
reloc: reloc.cloned(),
branch_dest,
line,
orig: None,
});
cur_addr += 4;
}
Ok(ProcessCodeResult { ops, insts })
}
fn implcit_addend(
&self,
section: &ObjSection,
address: u64,
reloc: &Relocation,
) -> Result<i64> {
let data = section.data[address as usize..address as usize + 4].try_into()?;
let addend = self.endianness.read_u32_bytes(data);
Ok(match reloc.flags() {
RelocationFlags::Elf { r_type: elf::R_MIPS_32 } => addend as i64,
RelocationFlags::Elf { r_type: elf::R_MIPS_HI16 } => {
((addend & 0x0000FFFF) << 16) as i32 as i64
}
RelocationFlags::Elf {
r_type:
elf::R_MIPS_LO16 | elf::R_MIPS_GOT16 | elf::R_MIPS_CALL16 | elf::R_MIPS_GPREL16,
} => (addend & 0x0000FFFF) as i16 as i64,
RelocationFlags::Elf { r_type: elf::R_MIPS_26 } => ((addend & 0x03FFFFFF) << 2) as i64,
flags => bail!("Unsupported MIPS implicit relocation {flags:?}"),
})
}
fn display_reloc(&self, flags: RelocationFlags) -> Cow<'static, str> {
match flags {
RelocationFlags::Elf { r_type } => match r_type {
elf::R_MIPS_HI16 => Cow::Borrowed("R_MIPS_HI16"),
elf::R_MIPS_LO16 => Cow::Borrowed("R_MIPS_LO16"),
elf::R_MIPS_GOT16 => Cow::Borrowed("R_MIPS_GOT16"),
elf::R_MIPS_CALL16 => Cow::Borrowed("R_MIPS_CALL16"),
elf::R_MIPS_GPREL16 => Cow::Borrowed("R_MIPS_GPREL16"),
elf::R_MIPS_32 => Cow::Borrowed("R_MIPS_32"),
elf::R_MIPS_26 => Cow::Borrowed("R_MIPS_26"),
_ => Cow::Owned(format!("<Elf {r_type:?}>")),
},
flags => Cow::Owned(format!("<{flags:?}>")),
}
}
}
fn push_reloc(args: &mut Vec<ObjInsArg>, reloc: &ObjReloc) -> Result<()> {
match reloc.flags {
RelocationFlags::Elf { r_type } => match r_type {
elf::R_MIPS_HI16 => {
args.push(ObjInsArg::PlainText("%hi(".to_string()));
args.push(ObjInsArg::Reloc);
args.push(ObjInsArg::PlainText(")".to_string()));
}
elf::R_MIPS_LO16 => {
args.push(ObjInsArg::PlainText("%lo(".to_string()));
args.push(ObjInsArg::Reloc);
args.push(ObjInsArg::PlainText(")".to_string()));
}
elf::R_MIPS_GOT16 => {
args.push(ObjInsArg::PlainText("%got(".to_string()));
args.push(ObjInsArg::Reloc);
args.push(ObjInsArg::PlainText(")".to_string()));
}
elf::R_MIPS_CALL16 => {
args.push(ObjInsArg::PlainText("%call16(".to_string()));
args.push(ObjInsArg::Reloc);
args.push(ObjInsArg::PlainText(")".to_string()));
}
elf::R_MIPS_GPREL16 => {
args.push(ObjInsArg::PlainText("%gp_rel(".to_string()));
args.push(ObjInsArg::Reloc);
args.push(ObjInsArg::PlainText(")".to_string()));
}
elf::R_MIPS_32 | elf::R_MIPS_26 => {
args.push(ObjInsArg::Reloc);
}
_ => bail!("Unsupported ELF MIPS relocation type {r_type}"),
},
flags => panic!("Unsupported MIPS relocation flags {flags:?}"),
}
Ok(())
}

View File

@ -0,0 +1,46 @@
use std::{borrow::Cow, collections::BTreeMap};
use anyhow::{bail, Result};
use object::{Architecture, Object, Relocation, RelocationFlags};
use crate::{
diff::{DiffObjConfig, ProcessCodeResult},
obj::{ObjReloc, ObjSection},
};
#[cfg(feature = "mips")]
mod mips;
#[cfg(feature = "ppc")]
mod ppc;
#[cfg(feature = "x86")]
mod x86;
pub trait ObjArch: Send + Sync {
fn process_code(
&self,
config: &DiffObjConfig,
data: &[u8],
address: u64,
relocs: &[ObjReloc],
line_info: &Option<BTreeMap<u64, u64>>,
) -> Result<ProcessCodeResult>;
fn implcit_addend(&self, section: &ObjSection, address: u64, reloc: &Relocation)
-> Result<i64>;
fn demangle(&self, _name: &str) -> Option<String> { None }
fn display_reloc(&self, flags: RelocationFlags) -> Cow<'static, str>;
}
pub fn new_arch(object: &object::File) -> Result<Box<dyn ObjArch>> {
Ok(match object.architecture() {
#[cfg(feature = "ppc")]
Architecture::PowerPc => Box::new(ppc::ObjArchPpc::new(object)?),
#[cfg(feature = "mips")]
Architecture::Mips => Box::new(mips::ObjArchMips::new(object)?),
#[cfg(feature = "x86")]
Architecture::I386 | Architecture::X86_64 => Box::new(x86::ObjArchX86::new(object)?),
arch => bail!("Unsupported architecture: {arch:?}"),
})
}

View File

@ -0,0 +1,208 @@
use std::{borrow::Cow, collections::BTreeMap};
use anyhow::{bail, Result};
use object::{elf, File, Relocation, RelocationFlags};
use ppc750cl::{disasm_iter, Argument, SimplifiedIns, GPR};
use crate::{
arch::ObjArch,
diff::{DiffObjConfig, ProcessCodeResult},
obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection},
};
// Relative relocation, can be Simm, Offset or BranchDest
fn is_relative_arg(arg: &Argument) -> bool {
matches!(arg, Argument::Simm(_) | Argument::Offset(_) | Argument::BranchDest(_))
}
// Relative or absolute relocation, can be Uimm, Simm or Offset
fn is_rel_abs_arg(arg: &Argument) -> bool {
matches!(arg, Argument::Uimm(_) | Argument::Simm(_) | Argument::Offset(_))
}
fn is_offset_arg(arg: &Argument) -> bool { matches!(arg, Argument::Offset(_)) }
pub struct ObjArchPpc {}
impl ObjArchPpc {
pub fn new(_file: &File) -> Result<Self> { Ok(Self {}) }
}
impl ObjArch for ObjArchPpc {
fn process_code(
&self,
config: &DiffObjConfig,
data: &[u8],
address: u64,
relocs: &[ObjReloc],
line_info: &Option<BTreeMap<u64, u64>>,
) -> Result<ProcessCodeResult> {
let ins_count = data.len() / 4;
let mut ops = Vec::<u16>::with_capacity(ins_count);
let mut insts = Vec::<ObjIns>::with_capacity(ins_count);
for mut ins in disasm_iter(data, address as u32) {
let reloc = relocs.iter().find(|r| (r.address as u32 & !3) == ins.addr);
if let Some(reloc) = reloc {
// Zero out relocations
ins.code = match reloc.flags {
RelocationFlags::Elf { r_type: elf::R_PPC_EMB_SDA21 } => ins.code & !0x1FFFFF,
RelocationFlags::Elf { r_type: elf::R_PPC_REL24 } => ins.code & !0x3FFFFFC,
RelocationFlags::Elf { r_type: elf::R_PPC_REL14 } => ins.code & !0xFFFC,
RelocationFlags::Elf {
r_type: elf::R_PPC_ADDR16_HI | elf::R_PPC_ADDR16_HA | elf::R_PPC_ADDR16_LO,
} => ins.code & !0xFFFF,
_ => ins.code,
};
}
let simplified = ins.clone().simplified();
let mut reloc_arg = None;
if let Some(reloc) = reloc {
match reloc.flags {
RelocationFlags::Elf { r_type: elf::R_PPC_EMB_SDA21 } => {
reloc_arg = Some(1);
}
RelocationFlags::Elf { r_type: elf::R_PPC_REL24 | elf::R_PPC_REL14 } => {
reloc_arg = simplified.args.iter().rposition(is_relative_arg);
}
RelocationFlags::Elf {
r_type: elf::R_PPC_ADDR16_HI | elf::R_PPC_ADDR16_HA | elf::R_PPC_ADDR16_LO,
} => {
reloc_arg = simplified.args.iter().rposition(is_rel_abs_arg);
}
_ => {}
}
}
let mut args = vec![];
let mut branch_dest = None;
let mut writing_offset = false;
for (idx, arg) in simplified.args.iter().enumerate() {
if idx > 0 && !writing_offset {
if config.space_between_args {
args.push(ObjInsArg::PlainText(", ".to_string()));
} else {
args.push(ObjInsArg::PlainText(",".to_string()));
}
}
if reloc_arg == Some(idx) {
let reloc = reloc.unwrap();
push_reloc(&mut args, reloc)?;
// For @sda21, we can omit the register argument
if matches!(reloc.flags, RelocationFlags::Elf { r_type: elf::R_PPC_EMB_SDA21 })
// Sanity check: the next argument should be r0
&& matches!(simplified.args.get(idx + 1), Some(Argument::GPR(GPR(0))))
{
break;
}
} else {
match arg {
Argument::Simm(simm) => {
args.push(ObjInsArg::Arg(ObjInsArgValue::Signed(simm.0 as i64)));
}
Argument::Uimm(uimm) => {
args.push(ObjInsArg::Arg(ObjInsArgValue::Unsigned(uimm.0 as u64)));
}
Argument::Offset(offset) => {
args.push(ObjInsArg::Arg(ObjInsArgValue::Signed(offset.0 as i64)));
}
Argument::BranchDest(dest) => {
let dest = ins.addr.wrapping_add_signed(dest.0) as u64;
args.push(ObjInsArg::BranchDest(dest));
branch_dest = Some(dest);
}
_ => {
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(arg.to_string())));
}
};
}
if writing_offset {
args.push(ObjInsArg::PlainText(")".to_string()));
writing_offset = false;
}
if is_offset_arg(arg) {
args.push(ObjInsArg::PlainText("(".to_string()));
writing_offset = true;
}
}
ops.push(simplified.ins.op as u16);
let line = line_info
.as_ref()
.and_then(|map| map.range(..=simplified.ins.addr as u64).last().map(|(_, &b)| b));
insts.push(ObjIns {
address: simplified.ins.addr as u64,
size: 4,
mnemonic: format!("{}{}", simplified.mnemonic, simplified.suffix),
args,
reloc: reloc.cloned(),
op: ins.op as u16,
branch_dest,
line,
orig: Some(format!("{}", SimplifiedIns::basic_form(ins))),
});
}
Ok(ProcessCodeResult { ops, insts })
}
fn implcit_addend(
&self,
_section: &ObjSection,
address: u64,
reloc: &Relocation,
) -> Result<i64> {
bail!("Unsupported PPC implicit relocation {:#x}:{:?}", address, reloc.flags())
}
fn demangle(&self, name: &str) -> Option<String> {
cwdemangle::demangle(name, &Default::default())
}
fn display_reloc(&self, flags: RelocationFlags) -> Cow<'static, str> {
match flags {
RelocationFlags::Elf { r_type } => match r_type {
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!("<Elf {r_type:?}>")),
},
flags => Cow::Owned(format!("<{flags:?}>")),
}
}
}
fn push_reloc(args: &mut Vec<ObjInsArg>, reloc: &ObjReloc) -> Result<()> {
match reloc.flags {
RelocationFlags::Elf { r_type } => match r_type {
elf::R_PPC_ADDR16_LO => {
args.push(ObjInsArg::Reloc);
args.push(ObjInsArg::PlainText("@l".to_string()));
}
elf::R_PPC_ADDR16_HI => {
args.push(ObjInsArg::Reloc);
args.push(ObjInsArg::PlainText("@h".to_string()));
}
elf::R_PPC_ADDR16_HA => {
args.push(ObjInsArg::Reloc);
args.push(ObjInsArg::PlainText("@ha".to_string()));
}
elf::R_PPC_EMB_SDA21 => {
args.push(ObjInsArg::Reloc);
args.push(ObjInsArg::PlainText("@sda21".to_string()));
}
elf::R_PPC_ADDR32 | elf::R_PPC_UADDR32 | elf::R_PPC_REL24 | elf::R_PPC_REL14 => {
args.push(ObjInsArg::Reloc);
}
_ => bail!("Unsupported ELF PPC relocation type {r_type}"),
},
flags => bail!("Unsupported PPC relocation kind: {flags:?}"),
};
Ok(())
}

View File

@ -1,4 +1,4 @@
use std::collections::BTreeMap;
use std::{borrow::Cow, collections::BTreeMap};
use anyhow::{anyhow, bail, ensure, Result};
use iced_x86::{
@ -6,138 +6,178 @@ use iced_x86::{
GasFormatter, Instruction, IntelFormatter, MasmFormatter, NasmFormatter, NumberKind, OpKind,
PrefixKind, Register, SymbolResult,
};
use object::{pe, Endian, Endianness, File, Object, Relocation, RelocationFlags};
use crate::{
diff::{DiffObjConfig, ProcessCodeResult},
obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjRelocKind},
arch::ObjArch,
diff::{DiffObjConfig, ProcessCodeResult, X86Formatter},
obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection},
};
#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub enum X86Formatter {
#[default]
Intel,
Gas,
Nasm,
Masm,
pub struct ObjArchX86 {
bits: u32,
endianness: Endianness,
}
pub fn process_code(
config: &DiffObjConfig,
data: &[u8],
bitness: u32,
start_address: u64,
relocs: &[ObjReloc],
line_info: &Option<BTreeMap<u64, u64>>,
) -> Result<ProcessCodeResult> {
let mut result = ProcessCodeResult { ops: Vec::new(), insts: Vec::new() };
let mut decoder = Decoder::with_ip(bitness, data, start_address, DecoderOptions::NONE);
let mut formatter: Box<dyn Formatter> = match config.x86_formatter {
X86Formatter::Intel => Box::new(IntelFormatter::new()),
X86Formatter::Gas => Box::new(GasFormatter::new()),
X86Formatter::Nasm => Box::new(NasmFormatter::new()),
X86Formatter::Masm => Box::new(MasmFormatter::new()),
};
formatter.options_mut().set_space_after_operand_separator(config.space_between_args);
impl ObjArchX86 {
pub fn new(object: &File) -> Result<Self> {
Ok(Self { bits: if object.is_64() { 64 } else { 32 }, endianness: object.endianness() })
}
}
let mut output = InstructionFormatterOutput {
formatted: String::new(),
ins: ObjIns {
address: 0,
size: 0,
op: 0,
mnemonic: "".to_string(),
args: vec![],
reloc: None,
branch_dest: None,
line: None,
orig: None,
},
error: None,
ins_operands: vec![],
};
let mut instruction = Instruction::default();
while decoder.can_decode() {
decoder.decode_out(&mut instruction);
let address = instruction.ip();
let op = instruction.mnemonic() as u16;
let reloc = relocs
.iter()
.find(|r| r.address >= address && r.address < address + instruction.len() as u64);
output.ins = ObjIns {
address,
size: instruction.len() as u8,
op,
mnemonic: "".to_string(),
args: vec![],
reloc: reloc.cloned(),
branch_dest: None,
line: line_info.as_ref().and_then(|m| m.get(&address).cloned()),
orig: None,
impl ObjArch for ObjArchX86 {
fn process_code(
&self,
config: &DiffObjConfig,
data: &[u8],
start_address: u64,
relocs: &[ObjReloc],
line_info: &Option<BTreeMap<u64, u64>>,
) -> Result<ProcessCodeResult> {
let mut result = ProcessCodeResult { ops: Vec::new(), insts: Vec::new() };
let mut decoder = Decoder::with_ip(self.bits, data, start_address, DecoderOptions::NONE);
let mut formatter: Box<dyn Formatter> = match config.x86_formatter {
X86Formatter::Intel => Box::new(IntelFormatter::new()),
X86Formatter::Gas => Box::new(GasFormatter::new()),
X86Formatter::Nasm => Box::new(NasmFormatter::new()),
X86Formatter::Masm => Box::new(MasmFormatter::new()),
};
// Run the formatter, which will populate output.ins
formatter.format(&instruction, &mut output);
if let Some(error) = output.error.take() {
return Err(error);
}
ensure!(output.ins_operands.len() == output.ins.args.len());
output.ins.orig = Some(output.formatted.clone());
formatter.options_mut().set_space_after_operand_separator(config.space_between_args);
// print!("{:016X} ", instruction.ip());
// let start_index = (instruction.ip() - address) as usize;
// let instr_bytes = &data[start_index..start_index + instruction.len()];
// for b in instr_bytes.iter() {
// print!("{:02X}", b);
// }
// if instr_bytes.len() < 32 {
// for _ in 0..32 - instr_bytes.len() {
// print!(" ");
// }
// }
// println!(" {}", output.formatted);
//
// if let Some(reloc) = reloc {
// println!("\tReloc: {:?}", reloc);
// }
//
// for i in 0..instruction.op_count() {
// let kind = instruction.op_kind(i);
// print!("{:?} ", kind);
// }
// println!();
let mut output = InstructionFormatterOutput {
formatted: String::new(),
ins: ObjIns {
address: 0,
size: 0,
op: 0,
mnemonic: String::new(),
args: vec![],
reloc: None,
branch_dest: None,
line: None,
orig: None,
},
error: None,
ins_operands: vec![],
};
let mut instruction = Instruction::default();
while decoder.can_decode() {
decoder.decode_out(&mut instruction);
// Make sure we've put the relocation somewhere in the instruction
if reloc.is_some() && !output.ins.args.iter().any(|a| matches!(a, ObjInsArg::Reloc)) {
let mut found = replace_arg(
OpKind::Memory,
ObjInsArg::Reloc,
&mut output.ins.args,
&instruction,
&output.ins_operands,
)?;
if !found {
found = replace_arg(
OpKind::Immediate32,
let address = instruction.ip();
let op = instruction.mnemonic() as u16;
let reloc = relocs
.iter()
.find(|r| r.address >= address && r.address < address + instruction.len() as u64);
output.ins = ObjIns {
address,
size: instruction.len() as u8,
op,
mnemonic: String::new(),
args: vec![],
reloc: reloc.cloned(),
branch_dest: None,
line: line_info.as_ref().and_then(|m| m.get(&address).cloned()),
orig: None,
};
// Run the formatter, which will populate output.ins
formatter.format(&instruction, &mut output);
if let Some(error) = output.error.take() {
return Err(error);
}
ensure!(output.ins_operands.len() == output.ins.args.len());
output.ins.orig = Some(output.formatted.clone());
// print!("{:016X} ", instruction.ip());
// let start_index = (instruction.ip() - address) as usize;
// let instr_bytes = &data[start_index..start_index + instruction.len()];
// for b in instr_bytes.iter() {
// print!("{:02X}", b);
// }
// if instr_bytes.len() < 32 {
// for _ in 0..32 - instr_bytes.len() {
// print!(" ");
// }
// }
// println!(" {}", output.formatted);
//
// if let Some(reloc) = reloc {
// println!("\tReloc: {:?}", reloc);
// }
//
// for i in 0..instruction.op_count() {
// let kind = instruction.op_kind(i);
// print!("{:?} ", kind);
// }
// println!();
// Make sure we've put the relocation somewhere in the instruction
if reloc.is_some() && !output.ins.args.iter().any(|a| matches!(a, ObjInsArg::Reloc)) {
let mut found = replace_arg(
OpKind::Memory,
ObjInsArg::Reloc,
&mut output.ins.args,
&instruction,
&output.ins_operands,
)?;
if !found {
found = replace_arg(
OpKind::Immediate32,
ObjInsArg::Reloc,
&mut output.ins.args,
&instruction,
&output.ins_operands,
)?;
}
ensure!(found, "x86: Failed to find operand for Absolute relocation");
}
if reloc.is_some() && !output.ins.args.iter().any(|a| matches!(a, ObjInsArg::Reloc)) {
bail!("Failed to find relocation in instruction");
}
ensure!(found, "x86: Failed to find operand for Absolute relocation");
}
if reloc.is_some() && !output.ins.args.iter().any(|a| matches!(a, ObjInsArg::Reloc)) {
bail!("Failed to find relocation in instruction");
}
result.ops.push(op);
result.insts.push(output.ins.clone());
result.ops.push(op);
result.insts.push(output.ins.clone());
// Clear for next iteration
output.formatted.clear();
output.ins_operands.clear();
// Clear for next iteration
output.formatted.clear();
output.ins_operands.clear();
}
Ok(result)
}
fn implcit_addend(
&self,
section: &ObjSection,
address: u64,
reloc: &Relocation,
) -> Result<i64> {
match reloc.flags() {
RelocationFlags::Coff { typ: pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32 } => {
let data = section.data[address as usize..address as usize + 4].try_into()?;
Ok(self.endianness.read_i32_bytes(data) as i64)
}
flags => bail!("Unsupported x86 implicit relocation {flags:?}"),
}
}
fn demangle(&self, name: &str) -> Option<String> {
if name.starts_with('?') {
msvc_demangler::demangle(name, msvc_demangler::DemangleFlags::llvm()).ok()
} else {
None
}
}
fn display_reloc(&self, flags: RelocationFlags) -> Cow<'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!("<Coff {typ:?}>")),
},
flags => Cow::Owned(format!("<{flags:?}>")),
}
}
Ok(result)
}
fn replace_arg(
@ -242,13 +282,15 @@ impl FormatterOutput for InstructionFormatterOutput {
match kind {
FormatterTextKind::LabelAddress => {
if let Some(reloc) = self.ins.reloc.as_ref() {
if reloc.kind == ObjRelocKind::Absolute {
if matches!(reloc.flags, RelocationFlags::Coff {
typ: pe::IMAGE_REL_I386_DIR32
}) {
self.ins.args.push(ObjInsArg::Reloc);
return;
} else if self.error.is_none() {
self.error = Some(anyhow!(
"x86: Unsupported LabelAddress relocation kind {:?}",
reloc.kind
"x86: Unsupported LabelAddress relocation flags {:?}",
reloc.flags
));
}
}
@ -258,13 +300,15 @@ impl FormatterOutput for InstructionFormatterOutput {
}
FormatterTextKind::FunctionAddress => {
if let Some(reloc) = self.ins.reloc.as_ref() {
if reloc.kind == ObjRelocKind::X86PcRel32 {
if matches!(reloc.flags, RelocationFlags::Coff {
typ: pe::IMAGE_REL_I386_REL32
}) {
self.ins.args.push(ObjInsArg::Reloc);
return;
} else if self.error.is_none() {
self.error = Some(anyhow!(
"x86: Unsupported FunctionAddress relocation kind {:?}",
reloc.kind
"x86: Unsupported FunctionAddress relocation flags {:?}",
reloc.flags
));
}
}

View File

@ -8,17 +8,17 @@ use anyhow::Result;
use similar::{capture_diff_slices_deadline, Algorithm};
use crate::{
arch::ObjArch,
diff::{DiffObjConfig, ProcessCodeResult},
obj,
obj::{
ObjArchitecture, ObjInfo, ObjInsArg, ObjInsArgDiff, ObjInsBranchFrom, ObjInsBranchTo,
ObjInsDiff, ObjInsDiffKind, ObjReloc, ObjSymbol, ObjSymbolFlags,
ObjInfo, ObjInsArg, ObjInsArgDiff, ObjInsBranchFrom, ObjInsBranchTo, ObjInsDiff,
ObjInsDiffKind, ObjReloc, ObjSymbol, ObjSymbolFlags,
},
};
pub fn no_diff_code(
arch: &dyn ObjArch,
config: &DiffObjConfig,
arch: ObjArchitecture,
data: &[u8],
symbol: &mut ObjSymbol,
relocs: &[ObjReloc],
@ -26,29 +26,7 @@ pub fn no_diff_code(
) -> Result<()> {
let code =
&data[symbol.section_address as usize..(symbol.section_address + symbol.size) as usize];
let out: ProcessCodeResult = match arch {
#[cfg(feature = "ppc")]
ObjArchitecture::PowerPc => {
obj::ppc::process_code(config, code, symbol.address, relocs, line_info)?
}
#[cfg(feature = "mips")]
ObjArchitecture::Mips => obj::mips::process_code(
config,
code,
symbol.address,
symbol.address + symbol.size,
relocs,
line_info,
)?,
#[cfg(feature = "x86")]
ObjArchitecture::X86_32 => {
obj::x86::process_code(config, code, 32, symbol.address, relocs, line_info)?
}
#[cfg(feature = "x86")]
ObjArchitecture::X86_64 => {
obj::x86::process_code(config, code, 64, symbol.address, relocs, line_info)?
}
};
let out = arch.process_code(config, code, symbol.address, relocs, line_info)?;
let mut diff = Vec::<ObjInsDiff>::new();
for i in out.insts {
@ -61,8 +39,8 @@ pub fn no_diff_code(
#[allow(clippy::too_many_arguments)]
pub fn diff_code(
arch: &dyn ObjArch,
config: &DiffObjConfig,
arch: ObjArchitecture,
left_data: &[u8],
right_data: &[u8],
left_symbol: &mut ObjSymbol,
@ -76,82 +54,10 @@ pub fn diff_code(
..(left_symbol.section_address + left_symbol.size) as usize];
let right_code = &right_data[right_symbol.section_address as usize
..(right_symbol.section_address + right_symbol.size) as usize];
let (left_out, right_out) = match arch {
#[cfg(feature = "ppc")]
ObjArchitecture::PowerPc => (
obj::ppc::process_code(
config,
left_code,
left_symbol.address,
left_relocs,
left_line_info,
)?,
obj::ppc::process_code(
config,
right_code,
right_symbol.address,
right_relocs,
right_line_info,
)?,
),
#[cfg(feature = "mips")]
ObjArchitecture::Mips => (
obj::mips::process_code(
config,
left_code,
left_symbol.address,
left_symbol.address + left_symbol.size,
left_relocs,
left_line_info,
)?,
obj::mips::process_code(
config,
right_code,
right_symbol.address,
left_symbol.address + left_symbol.size,
right_relocs,
right_line_info,
)?,
),
#[cfg(feature = "x86")]
ObjArchitecture::X86_32 => (
obj::x86::process_code(
config,
left_code,
32,
left_symbol.address,
left_relocs,
left_line_info,
)?,
obj::x86::process_code(
config,
right_code,
32,
right_symbol.address,
right_relocs,
right_line_info,
)?,
),
#[cfg(feature = "x86")]
ObjArchitecture::X86_64 => (
obj::x86::process_code(
config,
left_code,
64,
left_symbol.address,
left_relocs,
left_line_info,
)?,
obj::x86::process_code(
config,
right_code,
64,
right_symbol.address,
right_relocs,
right_line_info,
)?,
),
};
let left_out =
arch.process_code(config, left_code, left_symbol.address, left_relocs, left_line_info)?;
let right_out =
arch.process_code(config, right_code, right_symbol.address, right_relocs, right_line_info)?;
let mut left_diff = Vec::<ObjInsDiff>::new();
let mut right_diff = Vec::<ObjInsDiff>::new();
@ -281,7 +187,7 @@ fn reloc_eq(
let (Some(left), Some(right)) = (left_reloc, right_reloc) else {
return false;
};
if left.kind != right.kind {
if left.flags != right.flags {
return false;
}
if config.relax_reloc_diffs {

View File

@ -9,9 +9,18 @@ use crate::{
code::{diff_code, find_section_and_symbol, no_diff_code},
data::{diff_bss_symbols, diff_data, no_diff_data},
},
obj::{x86::X86Formatter, ObjInfo, ObjIns, ObjSectionKind},
obj::{ObjInfo, ObjIns, ObjSectionKind},
};
#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub enum X86Formatter {
#[default]
Intel,
Gas,
Nasm,
Masm,
}
#[derive(Debug, Clone, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(default)]
pub struct DiffObjConfig {
@ -44,8 +53,8 @@ pub fn diff_objs(
left_symbol.diff_symbol = Some(right_symbol.name.clone());
right_symbol.diff_symbol = Some(left_symbol.name.clone());
diff_code(
left.arch.as_ref(),
config,
left.architecture,
&left_section.data,
&right_section.data,
left_symbol,
@ -57,8 +66,8 @@ pub fn diff_objs(
)?;
} else {
no_diff_code(
left.arch.as_ref(),
config,
left.architecture,
&left_section.data,
left_symbol,
&left_section.relocations,
@ -86,8 +95,8 @@ pub fn diff_objs(
for right_symbol in &mut right_section.symbols {
if right_symbol.instructions.is_empty() {
no_diff_code(
right.arch.as_ref(),
config,
right.architecture,
&right_section.data,
right_symbol,
&right_section.relocations,

View File

@ -1,3 +1,4 @@
pub mod arch;
#[cfg(feature = "config")]
pub mod config;
pub mod diff;

View File

@ -1,155 +0,0 @@
use std::collections::BTreeMap;
use anyhow::Result;
use rabbitizer::{config, Abi, InstrCategory, Instruction, OperandType};
use crate::{
diff::{DiffObjConfig, ProcessCodeResult},
obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjRelocKind},
};
fn configure_rabbitizer() {
unsafe {
config::RabbitizerConfig_Cfg.reg_names.fpr_abi_names = Abi::O32;
}
}
pub fn process_code(
config: &DiffObjConfig,
data: &[u8],
start_address: u64,
end_address: u64,
relocs: &[ObjReloc],
line_info: &Option<BTreeMap<u64, u64>>,
) -> Result<ProcessCodeResult> {
configure_rabbitizer();
let ins_count = data.len() / 4;
let mut ops = Vec::<u16>::with_capacity(ins_count);
let mut insts = Vec::<ObjIns>::with_capacity(ins_count);
let mut cur_addr = start_address as u32;
for chunk in data.chunks_exact(4) {
let reloc = relocs.iter().find(|r| (r.address as u32 & !3) == cur_addr);
let code = u32::from_be_bytes(chunk.try_into()?);
let instruction = Instruction::new(code, cur_addr, InstrCategory::CPU);
let op = instruction.unique_id as u16;
ops.push(op);
let mnemonic = instruction.opcode_name().to_string();
let is_branch = instruction.is_branch();
let branch_offset = instruction.branch_offset();
let branch_dest = if is_branch {
cur_addr.checked_add_signed(branch_offset).map(|a| a as u64)
} else {
None
};
let operands = instruction.get_operands_slice();
let mut args = Vec::with_capacity(operands.len() + 1);
for (idx, op) in operands.iter().enumerate() {
if idx > 0 {
if config.space_between_args {
args.push(ObjInsArg::PlainText(", ".to_string()));
} else {
args.push(ObjInsArg::PlainText(",".to_string()));
}
}
match op {
OperandType::cpu_immediate
| OperandType::cpu_label
| OperandType::cpu_branch_target_label => {
if let Some(branch_dest) = branch_dest {
args.push(ObjInsArg::BranchDest(branch_dest));
} else if let Some(reloc) = reloc {
if matches!(&reloc.target_section, Some(s) if s == ".text")
&& reloc.target.address > start_address
&& reloc.target.address < end_address
{
args.push(ObjInsArg::BranchDest(reloc.target.address));
} else {
push_reloc(&mut args, reloc);
}
} else {
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(
op.disassemble(&instruction, None),
)));
}
}
OperandType::cpu_immediate_base => {
if let Some(reloc) = reloc {
push_reloc(&mut args, reloc);
} else {
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(
OperandType::cpu_immediate.disassemble(&instruction, None),
)));
}
args.push(ObjInsArg::PlainText("(".to_string()));
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(
OperandType::cpu_rs.disassemble(&instruction, None),
)));
args.push(ObjInsArg::PlainText(")".to_string()));
}
_ => {
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(
op.disassemble(&instruction, None),
)));
}
}
}
let line = line_info
.as_ref()
.and_then(|map| map.range(..=cur_addr as u64).last().map(|(_, &b)| b));
insts.push(ObjIns {
address: cur_addr as u64,
size: 4,
op,
mnemonic,
args,
reloc: reloc.cloned(),
branch_dest,
line,
orig: None,
});
cur_addr += 4;
}
Ok(ProcessCodeResult { ops, insts })
}
fn push_reloc(args: &mut Vec<ObjInsArg>, reloc: &ObjReloc) {
match reloc.kind {
ObjRelocKind::MipsHi16 => {
args.push(ObjInsArg::PlainText("%hi(".to_string()));
args.push(ObjInsArg::Reloc);
args.push(ObjInsArg::PlainText(")".to_string()));
}
ObjRelocKind::MipsLo16 => {
args.push(ObjInsArg::PlainText("%lo(".to_string()));
args.push(ObjInsArg::Reloc);
args.push(ObjInsArg::PlainText(")".to_string()));
}
ObjRelocKind::MipsGot16 => {
args.push(ObjInsArg::PlainText("%got(".to_string()));
args.push(ObjInsArg::Reloc);
args.push(ObjInsArg::PlainText(")".to_string()));
}
ObjRelocKind::MipsCall16 => {
args.push(ObjInsArg::PlainText("%call16(".to_string()));
args.push(ObjInsArg::Reloc);
args.push(ObjInsArg::PlainText(")".to_string()));
}
ObjRelocKind::MipsGpRel16 => {
args.push(ObjInsArg::PlainText("%gp_rel(".to_string()));
args.push(ObjInsArg::Reloc);
args.push(ObjInsArg::PlainText(")".to_string()));
}
ObjRelocKind::Mips26 => {
args.push(ObjInsArg::Reloc);
}
ObjRelocKind::MipsGpRel32 => {
todo!("unimplemented: mips gp_rel32");
}
kind => panic!("Unsupported MIPS relocation kind: {:?}", kind),
}
}

View File

@ -1,19 +1,14 @@
#[cfg(feature = "mips")]
pub mod mips;
#[cfg(feature = "ppc")]
pub mod ppc;
pub mod read;
pub mod split_meta;
#[cfg(feature = "x86")]
pub mod x86;
use std::{collections::BTreeMap, fmt, path::PathBuf};
use filetime::FileTime;
use flagset::{flags, FlagSet};
use object::RelocationFlags;
use split_meta::SplitMeta;
use crate::util::ReallySigned;
use crate::{arch::ObjArch, util::ReallySigned};
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub enum ObjSectionKind {
@ -196,21 +191,8 @@ pub struct ObjSymbol {
pub match_percent: Option<f32>,
}
#[derive(Debug, Copy, Clone)]
pub enum ObjArchitecture {
#[cfg(feature = "ppc")]
PowerPc,
#[cfg(feature = "mips")]
Mips,
#[cfg(feature = "x86")]
X86_32,
#[cfg(feature = "x86")]
X86_64,
}
#[derive(Debug, Clone)]
pub struct ObjInfo {
pub architecture: ObjArchitecture,
pub arch: Box<dyn ObjArch>,
pub path: PathBuf,
pub timestamp: FileTime,
pub sections: Vec<ObjSection>,
@ -222,50 +204,9 @@ pub struct ObjInfo {
pub split_meta: Option<SplitMeta>,
}
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub enum ObjRelocKind {
Absolute,
#[cfg(feature = "ppc")]
PpcAddr16Hi,
#[cfg(feature = "ppc")]
PpcAddr16Ha,
#[cfg(feature = "ppc")]
PpcAddr16Lo,
// #[cfg(feature = "ppc")]
// PpcAddr32,
// #[cfg(feature = "ppc")]
// PpcRel32,
// #[cfg(feature = "ppc")]
// PpcAddr24,
#[cfg(feature = "ppc")]
PpcRel24,
// #[cfg(feature = "ppc")]
// PpcAddr14,
#[cfg(feature = "ppc")]
PpcRel14,
#[cfg(feature = "ppc")]
PpcEmbSda21,
#[cfg(feature = "mips")]
Mips26,
#[cfg(feature = "mips")]
MipsHi16,
#[cfg(feature = "mips")]
MipsLo16,
#[cfg(feature = "mips")]
MipsGot16,
#[cfg(feature = "mips")]
MipsCall16,
#[cfg(feature = "mips")]
MipsGpRel16,
#[cfg(feature = "mips")]
MipsGpRel32,
#[cfg(feature = "x86")]
X86PcRel32,
}
#[derive(Debug, Clone)]
pub struct ObjReloc {
pub kind: ObjRelocKind,
pub flags: RelocationFlags,
pub address: u64,
pub target: ObjSymbol,
pub target_section: Option<String>,

View File

@ -1,164 +0,0 @@
use std::collections::BTreeMap;
use anyhow::{bail, Result};
use ppc750cl::{disasm_iter, Argument, SimplifiedIns, GPR};
use crate::{
diff::{DiffObjConfig, ProcessCodeResult},
obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjRelocKind},
};
// Relative relocation, can be Simm, Offset or BranchDest
fn is_relative_arg(arg: &Argument) -> bool {
matches!(arg, Argument::Simm(_) | Argument::Offset(_) | Argument::BranchDest(_))
}
// Relative or absolute relocation, can be Uimm, Simm or Offset
fn is_rel_abs_arg(arg: &Argument) -> bool {
matches!(arg, Argument::Uimm(_) | Argument::Simm(_) | Argument::Offset(_))
}
fn is_offset_arg(arg: &Argument) -> bool { matches!(arg, Argument::Offset(_)) }
pub fn process_code(
config: &DiffObjConfig,
data: &[u8],
address: u64,
relocs: &[ObjReloc],
line_info: &Option<BTreeMap<u64, u64>>,
) -> Result<ProcessCodeResult> {
let ins_count = data.len() / 4;
let mut ops = Vec::<u16>::with_capacity(ins_count);
let mut insts = Vec::<ObjIns>::with_capacity(ins_count);
for mut ins in disasm_iter(data, address as u32) {
let reloc = relocs.iter().find(|r| (r.address as u32 & !3) == ins.addr);
if let Some(reloc) = reloc {
// Zero out relocations
ins.code = match reloc.kind {
ObjRelocKind::PpcEmbSda21 => ins.code & !0x1FFFFF,
ObjRelocKind::PpcRel24 => ins.code & !0x3FFFFFC,
ObjRelocKind::PpcRel14 => ins.code & !0xFFFC,
ObjRelocKind::PpcAddr16Hi
| ObjRelocKind::PpcAddr16Ha
| ObjRelocKind::PpcAddr16Lo => ins.code & !0xFFFF,
_ => ins.code,
};
}
let simplified = ins.clone().simplified();
let mut reloc_arg = None;
if let Some(reloc) = reloc {
match reloc.kind {
ObjRelocKind::PpcEmbSda21 => {
reloc_arg = Some(1);
}
ObjRelocKind::PpcRel24 | ObjRelocKind::PpcRel14 => {
reloc_arg = simplified.args.iter().rposition(is_relative_arg);
}
ObjRelocKind::PpcAddr16Hi
| ObjRelocKind::PpcAddr16Ha
| ObjRelocKind::PpcAddr16Lo => {
reloc_arg = simplified.args.iter().rposition(is_rel_abs_arg);
}
_ => {}
}
}
let mut args = vec![];
let mut branch_dest = None;
let mut writing_offset = false;
for (idx, arg) in simplified.args.iter().enumerate() {
if idx > 0 && !writing_offset {
if config.space_between_args {
args.push(ObjInsArg::PlainText(", ".to_string()));
} else {
args.push(ObjInsArg::PlainText(",".to_string()));
}
}
if reloc_arg == Some(idx) {
let reloc = reloc.unwrap();
push_reloc(&mut args, reloc)?;
// For @sda21, we can omit the register argument
if reloc.kind == ObjRelocKind::PpcEmbSda21
// Sanity check: the next argument should be r0
&& matches!(simplified.args.get(idx + 1), Some(Argument::GPR(GPR(0))))
{
break;
}
} else {
match arg {
Argument::Simm(simm) => {
args.push(ObjInsArg::Arg(ObjInsArgValue::Signed(simm.0 as i64)));
}
Argument::Uimm(uimm) => {
args.push(ObjInsArg::Arg(ObjInsArgValue::Unsigned(uimm.0 as u64)));
}
Argument::Offset(offset) => {
args.push(ObjInsArg::Arg(ObjInsArgValue::Signed(offset.0 as i64)));
}
Argument::BranchDest(dest) => {
let dest = ins.addr.wrapping_add_signed(dest.0) as u64;
args.push(ObjInsArg::BranchDest(dest));
branch_dest = Some(dest);
}
_ => {
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(arg.to_string())));
}
};
}
if writing_offset {
args.push(ObjInsArg::PlainText(")".to_string()));
writing_offset = false;
}
if is_offset_arg(arg) {
args.push(ObjInsArg::PlainText("(".to_string()));
writing_offset = true;
}
}
ops.push(simplified.ins.op as u16);
let line = line_info
.as_ref()
.and_then(|map| map.range(..=simplified.ins.addr as u64).last().map(|(_, &b)| b));
insts.push(ObjIns {
address: simplified.ins.addr as u64,
size: 4,
mnemonic: format!("{}{}", simplified.mnemonic, simplified.suffix),
args,
reloc: reloc.cloned(),
op: ins.op as u16,
branch_dest,
line,
orig: Some(format!("{}", SimplifiedIns::basic_form(ins))),
});
}
Ok(ProcessCodeResult { ops, insts })
}
fn push_reloc(args: &mut Vec<ObjInsArg>, reloc: &ObjReloc) -> Result<()> {
match reloc.kind {
ObjRelocKind::PpcAddr16Lo => {
args.push(ObjInsArg::Reloc);
args.push(ObjInsArg::PlainText("@l".to_string()));
}
ObjRelocKind::PpcAddr16Hi => {
args.push(ObjInsArg::Reloc);
args.push(ObjInsArg::PlainText("@h".to_string()));
}
ObjRelocKind::PpcAddr16Ha => {
args.push(ObjInsArg::Reloc);
args.push(ObjInsArg::PlainText("@ha".to_string()));
}
ObjRelocKind::PpcEmbSda21 => {
args.push(ObjInsArg::Reloc);
args.push(ObjInsArg::PlainText("@sda21".to_string()));
}
ObjRelocKind::PpcRel24 | ObjRelocKind::PpcRel14 => {
args.push(ObjInsArg::Reloc);
}
kind => bail!("Unsupported PPC relocation kind: {:?}", kind),
};
Ok(())
}

View File

@ -5,15 +5,16 @@ use byteorder::{BigEndian, ReadBytesExt};
use filetime::FileTime;
use flagset::Flags;
use object::{
elf, pe, Architecture, BinaryFormat, Endian, File, Object, ObjectSection, ObjectSymbol,
RelocationFlags, RelocationTarget, SectionIndex, SectionKind, Symbol, SymbolKind, SymbolScope,
SymbolSection,
BinaryFormat, File, Object, ObjectSection, ObjectSymbol, RelocationTarget, SectionIndex,
SectionKind, Symbol, SymbolKind, SymbolScope, SymbolSection,
};
use crate::obj::{
split_meta::{SplitMeta, SPLITMETA_SECTION},
ObjArchitecture, ObjInfo, ObjReloc, ObjRelocKind, ObjSection, ObjSectionKind, ObjSymbol,
ObjSymbolFlagSet, ObjSymbolFlags,
use crate::{
arch::{new_arch, ObjArch},
obj::{
split_meta::{SplitMeta, SPLITMETA_SECTION},
ObjInfo, ObjReloc, ObjSection, ObjSectionKind, ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags,
},
};
fn to_obj_section_kind(kind: SectionKind) -> Option<ObjSectionKind> {
@ -26,6 +27,7 @@ fn to_obj_section_kind(kind: SectionKind) -> Option<ObjSectionKind> {
}
fn to_obj_symbol(
arch: &dyn ObjArch,
obj_file: &File<'_>,
symbol: &Symbol<'_, '_>,
addend: i64,
@ -59,15 +61,7 @@ fn to_obj_symbol(
} else {
symbol.address()
};
let mut demangled_name = None;
#[cfg(feature = "ppc")]
if obj_file.architecture() == Architecture::PowerPc {
demangled_name = cwdemangle::demangle(name, &Default::default());
}
#[cfg(feature = "x86")]
if matches!(obj_file.format(), BinaryFormat::Coff | BinaryFormat::Pe) && name.starts_with('?') {
demangled_name = msvc_demangler::demangle(name, msvc_demangler::DemangleFlags::llvm()).ok();
}
let demangled_name = arch.demangle(name);
// Find the virtual address for the symbol if available
let virtual_address = split_meta
.and_then(|m| m.virtual_addresses.as_ref())
@ -129,6 +123,7 @@ fn filter_sections(obj_file: &File<'_>, split_meta: Option<&SplitMeta>) -> Resul
}
fn symbols_by_section(
arch: &dyn ObjArch,
obj_file: &File<'_>,
section: &ObjSection,
split_meta: Option<&SplitMeta>,
@ -147,7 +142,7 @@ fn symbols_by_section(
continue;
}
}
result.push(to_obj_symbol(obj_file, &symbol, 0, split_meta)?);
result.push(to_obj_symbol(arch, obj_file, &symbol, 0, split_meta)?);
}
}
}
@ -165,15 +160,20 @@ fn symbols_by_section(
Ok(result)
}
fn common_symbols(obj_file: &File<'_>, split_meta: Option<&SplitMeta>) -> Result<Vec<ObjSymbol>> {
fn common_symbols(
arch: &dyn ObjArch,
obj_file: &File<'_>,
split_meta: Option<&SplitMeta>,
) -> Result<Vec<ObjSymbol>> {
obj_file
.symbols()
.filter(Symbol::is_common)
.map(|symbol| to_obj_symbol(obj_file, &symbol, 0, split_meta))
.map(|symbol| to_obj_symbol(arch, obj_file, &symbol, 0, split_meta))
.collect::<Result<Vec<ObjSymbol>>>()
}
fn find_section_symbol(
arch: &dyn ObjArch,
obj_file: &File<'_>,
target: &Symbol<'_, '_>,
address: u64,
@ -197,7 +197,7 @@ fn find_section_symbol(
}
continue;
}
return to_obj_symbol(obj_file, &symbol, 0, split_meta);
return to_obj_symbol(arch, obj_file, &symbol, 0, split_meta);
}
let (name, offset) = closest_symbol
.and_then(|s| s.name().map(|n| (n, s.address())).ok())
@ -221,7 +221,7 @@ fn find_section_symbol(
}
fn relocations_by_section(
arch: ObjArchitecture,
arch: &dyn ObjArch,
obj_file: &File<'_>,
section: &ObjSection,
split_meta: Option<&SplitMeta>,
@ -231,6 +231,10 @@ fn relocations_by_section(
for (address, reloc) in obj_section.relocations() {
let symbol = match reloc.target() {
RelocationTarget::Symbol(idx) => {
if idx.0 == u32::MAX as usize {
// ???
continue;
}
let Ok(symbol) = obj_file.symbol_by_index(idx) else {
log::warn!(
"Failed to locate relocation {:#x} target symbol {}",
@ -243,60 +247,7 @@ fn relocations_by_section(
}
_ => bail!("Unhandled relocation target: {:?}", reloc.target()),
};
let kind = match reloc.flags() {
RelocationFlags::Elf { r_type } => match arch {
#[cfg(feature = "ppc")]
ObjArchitecture::PowerPc => match r_type {
elf::R_PPC_ADDR32 | elf::R_PPC_UADDR32 => ObjRelocKind::Absolute,
elf::R_PPC_ADDR16_LO => ObjRelocKind::PpcAddr16Lo,
elf::R_PPC_ADDR16_HI => ObjRelocKind::PpcAddr16Hi,
elf::R_PPC_ADDR16_HA => ObjRelocKind::PpcAddr16Ha,
elf::R_PPC_REL24 => ObjRelocKind::PpcRel24,
elf::R_PPC_REL14 => ObjRelocKind::PpcRel14,
elf::R_PPC_EMB_SDA21 => ObjRelocKind::PpcEmbSda21,
_ => bail!("Unhandled ELF PPC relocation type: {r_type}"),
},
#[cfg(feature = "mips")]
ObjArchitecture::Mips => match r_type {
elf::R_MIPS_32 => ObjRelocKind::Absolute,
elf::R_MIPS_26 => ObjRelocKind::Mips26,
elf::R_MIPS_HI16 => ObjRelocKind::MipsHi16,
elf::R_MIPS_LO16 => ObjRelocKind::MipsLo16,
elf::R_MIPS_GOT16 => ObjRelocKind::MipsGot16,
elf::R_MIPS_CALL16 => ObjRelocKind::MipsCall16,
elf::R_MIPS_GPREL16 => ObjRelocKind::MipsGpRel16,
elf::R_MIPS_GPREL32 => ObjRelocKind::MipsGpRel32,
_ => bail!("Unhandled ELF MIPS relocation type: {r_type}"),
},
#[cfg(feature = "x86")]
ObjArchitecture::X86_32 => match r_type {
elf::R_386_32 => ObjRelocKind::Absolute,
elf::R_386_PC32 => ObjRelocKind::X86PcRel32,
_ => bail!("Unhandled ELF x86_32 relocation type: {r_type}"),
},
#[cfg(feature = "x86")]
ObjArchitecture::X86_64 => match r_type {
elf::R_X86_64_32 => ObjRelocKind::Absolute,
elf::R_X86_64_PC32 => ObjRelocKind::X86PcRel32,
_ => bail!("Unhandled ELF x86_64 relocation type: {r_type}"),
},
},
RelocationFlags::Coff { typ } => match arch {
#[cfg(feature = "ppc")]
ObjArchitecture::PowerPc => bail!("Unhandled PE/COFF PPC relocation type: {typ}"),
#[cfg(feature = "mips")]
ObjArchitecture::Mips => bail!("Unhandled PE/COFF MIPS relocation type: {typ}"),
#[cfg(feature = "x86")]
ObjArchitecture::X86_32 => match typ {
pe::IMAGE_REL_I386_DIR32 => ObjRelocKind::Absolute,
pe::IMAGE_REL_I386_REL32 => ObjRelocKind::X86PcRel32,
_ => bail!("Unhandled PE/COFF x86 relocation type: {typ}"),
},
#[cfg(feature = "x86")]
ObjArchitecture::X86_64 => bail!("Unhandled PE/COFF x86_64 relocation type: {typ}"),
},
flags => bail!("Unhandled relocation flags: {:?}", flags),
};
let flags = reloc.flags(); // TODO validate reloc here?
let target_section = match symbol.section() {
SymbolSection::Common => Some(".comm".to_string()),
SymbolSection::Section(idx) => {
@ -305,40 +256,22 @@ fn relocations_by_section(
_ => None,
};
let addend = if reloc.has_implicit_addend() {
let data = section.data[address as usize..address as usize + 4].try_into()?;
let addend = obj_file.endianness().read_u32_bytes(data);
match kind {
ObjRelocKind::Absolute => addend as i64,
#[cfg(feature = "mips")]
ObjRelocKind::MipsHi16 => ((addend & 0x0000FFFF) << 16) as i32 as i64,
#[cfg(feature = "mips")]
ObjRelocKind::MipsLo16
| ObjRelocKind::MipsGot16
| ObjRelocKind::MipsCall16
| ObjRelocKind::MipsGpRel16 => (addend & 0x0000FFFF) as i16 as i64,
#[cfg(feature = "mips")]
ObjRelocKind::MipsGpRel32 => addend as i32 as i64,
#[cfg(feature = "mips")]
ObjRelocKind::Mips26 => ((addend & 0x03FFFFFF) << 2) as i64,
#[cfg(feature = "x86")]
ObjRelocKind::X86PcRel32 => addend as i32 as i64,
_ => bail!("Unsupported implicit relocation {kind:?}"),
}
arch.implcit_addend(section, address, &reloc)?
} else {
reloc.addend()
};
// println!("Reloc: {reloc:?}, symbol: {symbol:?}, addend: {addend:#X}");
let target = match symbol.kind() {
SymbolKind::Text | SymbolKind::Data | SymbolKind::Label | SymbolKind::Unknown => {
to_obj_symbol(obj_file, &symbol, addend, split_meta)
to_obj_symbol(arch, obj_file, &symbol, addend, split_meta)
}
SymbolKind::Section => {
ensure!(addend >= 0, "Negative addend in reloc: {addend}");
find_section_symbol(obj_file, &symbol, addend as u64, split_meta)
find_section_symbol(arch, obj_file, &symbol, addend as u64, split_meta)
}
kind => Err(anyhow!("Unhandled relocation symbol type {kind:?}")),
}?;
relocations.push(ObjReloc { kind, address, target, target_section });
relocations.push(ObjReloc { flags, address, target, target_section });
}
Ok(relocations)
}
@ -408,34 +341,25 @@ pub fn read(obj_path: &Path) -> Result<ObjInfo> {
(unsafe { memmap2::Mmap::map(&file) }?, timestamp)
};
let obj_file = File::parse(&*data)?;
let architecture = match obj_file.architecture() {
#[cfg(feature = "ppc")]
Architecture::PowerPc => ObjArchitecture::PowerPc,
#[cfg(feature = "mips")]
Architecture::Mips => ObjArchitecture::Mips,
#[cfg(feature = "x86")]
Architecture::I386 => ObjArchitecture::X86_32,
#[cfg(feature = "x86")]
Architecture::X86_64 => ObjArchitecture::X86_64,
_ => bail!("Unsupported architecture: {:?}", obj_file.architecture()),
};
let arch = new_arch(&obj_file)?;
let split_meta = split_meta(&obj_file)?;
let mut result = ObjInfo {
architecture,
let mut sections = filter_sections(&obj_file, split_meta.as_ref())?;
for section in &mut sections {
section.symbols =
symbols_by_section(arch.as_ref(), &obj_file, section, split_meta.as_ref())?;
section.relocations =
relocations_by_section(arch.as_ref(), &obj_file, section, split_meta.as_ref())?;
}
let common = common_symbols(arch.as_ref(), &obj_file, split_meta.as_ref())?;
Ok(ObjInfo {
arch,
path: obj_path.to_owned(),
timestamp,
sections: filter_sections(&obj_file, split_meta.as_ref())?,
common: common_symbols(&obj_file, split_meta.as_ref())?,
sections,
common,
line_info: line_info(&obj_file)?,
split_meta: None,
};
for section in &mut result.sections {
section.symbols = symbols_by_section(&obj_file, section, split_meta.as_ref())?;
section.relocations =
relocations_by_section(architecture, &obj_file, section, split_meta.as_ref())?;
}
result.split_meta = split_meta;
Ok(result)
split_meta,
})
}
pub fn has_function(obj_path: &Path, symbol_name: &str) -> Result<bool> {

View File

@ -290,7 +290,7 @@ impl App {
title: "Error".to_string(),
progress_percent: 0.0,
progress_items: None,
status: "".to_string(),
status: String::new(),
error: Some(err),
}));
}

View File

@ -16,7 +16,7 @@ use egui::{
use globset::Glob;
use objdiff_core::{
config::{ProjectObject, DEFAULT_WATCH_PATTERNS},
obj::x86::X86Formatter,
diff::X86Formatter,
};
use self_update::cargo_crate_version;

View File

@ -3,6 +3,7 @@ use std::default::Default;
use egui::{text::LayoutJob, Align, Label, Layout, Sense, Vec2, Widget};
use egui_extras::{Column, TableBuilder, TableRow};
use objdiff_core::{
arch::ObjArch,
diff::display::{display_diff, DiffText, HighlightKind},
obj::{
ObjInfo, ObjIns, ObjInsArg, ObjInsArgValue, ObjInsDiff, ObjInsDiffKind, ObjSection,
@ -21,7 +22,13 @@ pub struct FunctionViewState {
pub highlight: HighlightKind,
}
fn ins_hover_ui(ui: &mut egui::Ui, section: &ObjSection, ins: &ObjIns, appearance: &Appearance) {
fn ins_hover_ui(
ui: &mut egui::Ui,
arch: &dyn ObjArch,
section: &ObjSection,
ins: &ObjIns,
appearance: &Appearance,
) {
ui.scope(|ui| {
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
ui.style_mut().wrap = Some(false);
@ -51,7 +58,7 @@ fn ins_hover_ui(ui: &mut egui::Ui, section: &ObjSection, ins: &ObjIns, appearanc
}
if let Some(reloc) = &ins.reloc {
ui.label(format!("Relocation type: {:?}", reloc.kind));
ui.label(format!("Relocation type: {}", arch.display_reloc(reloc.flags)));
ui.colored_label(appearance.highlight_color, format!("Name: {}", reloc.target.name));
if let Some(section) = &reloc.target_section {
ui.colored_label(appearance.highlight_color, format!("Section: {section}"));
@ -122,10 +129,14 @@ fn ins_context_menu(ui: &mut egui::Ui, ins: &ObjIns) {
fn find_symbol<'a>(
obj: &'a ObjInfo,
selected_symbol: &SymbolReference,
) -> Option<(&'a ObjSection, &'a ObjSymbol)> {
) -> Option<(&'a dyn ObjArch, &'a ObjSection, &'a ObjSymbol)> {
obj.sections.iter().find_map(|section| {
section.symbols.iter().find_map(|symbol| {
(symbol.name == selected_symbol.symbol_name).then_some((section, symbol))
(symbol.name == selected_symbol.symbol_name).then_some((
obj.arch.as_ref(),
section,
symbol,
))
})
})
}
@ -238,6 +249,7 @@ fn asm_row_ui(
fn asm_col_ui(
row: &mut TableRow<'_, '_>,
ins_diff: &ObjInsDiff,
arch: &dyn ObjArch,
section: &ObjSection,
symbol: &ObjSymbol,
appearance: &Appearance,
@ -247,7 +259,7 @@ fn asm_col_ui(
asm_row_ui(ui, ins_diff, symbol, appearance, ins_view_state);
});
if let Some(ins) = &ins_diff.ins {
response.on_hover_ui_at_pointer(|ui| ins_hover_ui(ui, section, ins, appearance));
response.on_hover_ui_at_pointer(|ui| ins_hover_ui(ui, arch, section, ins, appearance));
}
}
@ -267,14 +279,15 @@ fn asm_table_ui(
) -> Option<()> {
let left_symbol = left_obj.and_then(|obj| find_symbol(obj, selected_symbol));
let right_symbol = right_obj.and_then(|obj| find_symbol(obj, selected_symbol));
let instructions_len = left_symbol.or(right_symbol).map(|(_, s)| s.instructions.len())?;
let instructions_len = left_symbol.or(right_symbol).map(|(_, _, s)| s.instructions.len())?;
table.body(|body| {
body.rows(appearance.code_font.size, instructions_len, |mut row| {
let row_index = row.index();
if let Some((section, symbol)) = left_symbol {
if let Some((arch, section, symbol)) = left_symbol {
asm_col_ui(
&mut row,
&symbol.instructions[row_index],
arch,
section,
symbol,
appearance,
@ -283,10 +296,11 @@ fn asm_table_ui(
} else {
empty_col_ui(&mut row);
}
if let Some((section, symbol)) = right_symbol {
if let Some((arch, section, symbol)) = right_symbol {
asm_col_ui(
&mut row,
&symbol.instructions[row_index],
arch,
section,
symbol,
appearance,
@ -399,7 +413,7 @@ pub fn function_diff_ui(ui: &mut egui::Ui, state: &mut DiffViewState, appearance
.second_obj
.as_ref()
.and_then(|obj| find_symbol(obj, selected_symbol))
.and_then(|(_, symbol)| symbol.match_percent)
.and_then(|(_, _, symbol)| symbol.match_percent)
{
ui.colored_label(
match_color_for_symbol(match_percent, appearance),