mirror of
https://github.com/encounter/objdiff.git
synced 2026-03-30 11:32:16 -07:00
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.
This commit is contained in:
@@ -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),
|
||||
}
|
||||
}
|
||||
@@ -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>,
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
+45
-121
@@ -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> {
|
||||
|
||||
@@ -1,334 +0,0 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use anyhow::{anyhow, bail, ensure, Result};
|
||||
use iced_x86::{
|
||||
Decoder, DecoderOptions, DecoratorKind, Formatter, FormatterOutput, FormatterTextKind,
|
||||
GasFormatter, Instruction, IntelFormatter, MasmFormatter, NasmFormatter, NumberKind, OpKind,
|
||||
PrefixKind, Register, SymbolResult,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
diff::{DiffObjConfig, ProcessCodeResult},
|
||||
obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjRelocKind},
|
||||
};
|
||||
|
||||
#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
|
||||
pub enum X86Formatter {
|
||||
#[default]
|
||||
Intel,
|
||||
Gas,
|
||||
Nasm,
|
||||
Masm,
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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,
|
||||
};
|
||||
// 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");
|
||||
}
|
||||
|
||||
result.ops.push(op);
|
||||
result.insts.push(output.ins.clone());
|
||||
|
||||
// Clear for next iteration
|
||||
output.formatted.clear();
|
||||
output.ins_operands.clear();
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn replace_arg(
|
||||
from: OpKind,
|
||||
to: ObjInsArg,
|
||||
args: &mut [ObjInsArg],
|
||||
instruction: &Instruction,
|
||||
ins_operands: &[Option<u32>],
|
||||
) -> Result<bool> {
|
||||
let mut replace = None;
|
||||
for i in 0..instruction.op_count() {
|
||||
let op_kind = instruction.op_kind(i);
|
||||
if op_kind == from {
|
||||
replace = Some(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if let Some(i) = replace {
|
||||
for (j, arg) in args.iter_mut().enumerate() {
|
||||
if ins_operands[j] == Some(i) {
|
||||
*arg = to;
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
struct InstructionFormatterOutput {
|
||||
formatted: String,
|
||||
ins: ObjIns,
|
||||
error: Option<anyhow::Error>,
|
||||
ins_operands: Vec<Option<u32>>,
|
||||
}
|
||||
|
||||
impl InstructionFormatterOutput {
|
||||
fn push_signed(&mut self, value: i64) {
|
||||
// The formatter writes the '-' operator and then gives us a negative value,
|
||||
// so convert it to a positive value to avoid double negatives
|
||||
if value < 0
|
||||
&& matches!(self.ins.args.last(), Some(ObjInsArg::Arg(ObjInsArgValue::Opaque(v))) if v == "-")
|
||||
{
|
||||
self.ins.args.push(ObjInsArg::Arg(ObjInsArgValue::Signed(value.wrapping_abs())));
|
||||
} else {
|
||||
self.ins.args.push(ObjInsArg::Arg(ObjInsArgValue::Signed(value)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FormatterOutput for InstructionFormatterOutput {
|
||||
fn write(&mut self, text: &str, kind: FormatterTextKind) {
|
||||
// log::debug!("write {} {:?}", text, kind);
|
||||
self.formatted.push_str(text);
|
||||
// Skip whitespace after the mnemonic
|
||||
if self.ins.args.is_empty() && kind == FormatterTextKind::Text {
|
||||
return;
|
||||
}
|
||||
self.ins_operands.push(None);
|
||||
match kind {
|
||||
FormatterTextKind::Text | FormatterTextKind::Punctuation => {
|
||||
self.ins.args.push(ObjInsArg::PlainText(text.to_string()));
|
||||
}
|
||||
FormatterTextKind::Keyword | FormatterTextKind::Operator => {
|
||||
self.ins.args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(text.to_string())));
|
||||
}
|
||||
_ => {
|
||||
if self.error.is_none() {
|
||||
self.error = Some(anyhow!("x86: Unsupported FormatterTextKind {:?}", kind));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn write_prefix(&mut self, _instruction: &Instruction, text: &str, _prefix: PrefixKind) {
|
||||
// log::debug!("write_prefix {} {:?}", text, prefix);
|
||||
self.formatted.push_str(text);
|
||||
self.ins_operands.push(None);
|
||||
self.ins.args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(text.to_string())));
|
||||
}
|
||||
|
||||
fn write_mnemonic(&mut self, _instruction: &Instruction, text: &str) {
|
||||
// log::debug!("write_mnemonic {}", text);
|
||||
self.formatted.push_str(text);
|
||||
self.ins.mnemonic = text.to_string();
|
||||
}
|
||||
|
||||
fn write_number(
|
||||
&mut self,
|
||||
_instruction: &Instruction,
|
||||
_operand: u32,
|
||||
instruction_operand: Option<u32>,
|
||||
text: &str,
|
||||
value: u64,
|
||||
number_kind: NumberKind,
|
||||
kind: FormatterTextKind,
|
||||
) {
|
||||
// log::debug!("write_number {} {:?} {} {} {:?} {:?}", operand, instruction_operand, text, value, number_kind, kind);
|
||||
self.formatted.push_str(text);
|
||||
self.ins_operands.push(instruction_operand);
|
||||
|
||||
// Handle relocations
|
||||
match kind {
|
||||
FormatterTextKind::LabelAddress => {
|
||||
if let Some(reloc) = self.ins.reloc.as_ref() {
|
||||
if reloc.kind == ObjRelocKind::Absolute {
|
||||
self.ins.args.push(ObjInsArg::Reloc);
|
||||
return;
|
||||
} else if self.error.is_none() {
|
||||
self.error = Some(anyhow!(
|
||||
"x86: Unsupported LabelAddress relocation kind {:?}",
|
||||
reloc.kind
|
||||
));
|
||||
}
|
||||
}
|
||||
self.ins.args.push(ObjInsArg::BranchDest(value));
|
||||
self.ins.branch_dest = Some(value);
|
||||
return;
|
||||
}
|
||||
FormatterTextKind::FunctionAddress => {
|
||||
if let Some(reloc) = self.ins.reloc.as_ref() {
|
||||
if reloc.kind == ObjRelocKind::X86PcRel32 {
|
||||
self.ins.args.push(ObjInsArg::Reloc);
|
||||
return;
|
||||
} else if self.error.is_none() {
|
||||
self.error = Some(anyhow!(
|
||||
"x86: Unsupported FunctionAddress relocation kind {:?}",
|
||||
reloc.kind
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
match number_kind {
|
||||
NumberKind::Int8 => {
|
||||
self.push_signed(value as i8 as i64);
|
||||
}
|
||||
NumberKind::Int16 => {
|
||||
self.push_signed(value as i16 as i64);
|
||||
}
|
||||
NumberKind::Int32 => {
|
||||
self.push_signed(value as i32 as i64);
|
||||
}
|
||||
NumberKind::Int64 => {
|
||||
self.push_signed(value as i64);
|
||||
}
|
||||
NumberKind::UInt8 | NumberKind::UInt16 | NumberKind::UInt32 | NumberKind::UInt64 => {
|
||||
self.ins.args.push(ObjInsArg::Arg(ObjInsArgValue::Unsigned(value)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn write_decorator(
|
||||
&mut self,
|
||||
_instruction: &Instruction,
|
||||
_operand: u32,
|
||||
instruction_operand: Option<u32>,
|
||||
text: &str,
|
||||
_decorator: DecoratorKind,
|
||||
) {
|
||||
// log::debug!("write_decorator {} {:?} {} {:?}", operand, instruction_operand, text, decorator);
|
||||
self.formatted.push_str(text);
|
||||
self.ins_operands.push(instruction_operand);
|
||||
self.ins.args.push(ObjInsArg::PlainText(text.to_string()));
|
||||
}
|
||||
|
||||
fn write_register(
|
||||
&mut self,
|
||||
_instruction: &Instruction,
|
||||
_operand: u32,
|
||||
instruction_operand: Option<u32>,
|
||||
text: &str,
|
||||
_register: Register,
|
||||
) {
|
||||
// log::debug!("write_register {} {:?} {} {:?}", operand, instruction_operand, text, register);
|
||||
self.formatted.push_str(text);
|
||||
self.ins_operands.push(instruction_operand);
|
||||
self.ins.args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(text.to_string())));
|
||||
}
|
||||
|
||||
fn write_symbol(
|
||||
&mut self,
|
||||
_instruction: &Instruction,
|
||||
_operand: u32,
|
||||
_instruction_operand: Option<u32>,
|
||||
_address: u64,
|
||||
_symbol: &SymbolResult<'_>,
|
||||
) {
|
||||
if self.error.is_none() {
|
||||
self.error = Some(anyhow!("x86: Unsupported write_symbol"));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user