mirror of
https://github.com/encounter/objdiff.git
synced 2025-12-11 06:27:55 +00:00
Initial x86 support
Includes a bit of work to make adding new architectures easier in the future
This commit is contained in:
@@ -17,6 +17,7 @@ use crate::{
|
||||
};
|
||||
|
||||
pub fn no_diff_code(
|
||||
config: &DiffObjConfig,
|
||||
arch: ObjArchitecture,
|
||||
data: &[u8],
|
||||
symbol: &mut ObjSymbol,
|
||||
@@ -28,16 +29,25 @@ pub fn no_diff_code(
|
||||
let out: ProcessCodeResult = match arch {
|
||||
#[cfg(feature = "ppc")]
|
||||
ObjArchitecture::PowerPc => {
|
||||
obj::ppc::process_code(code, symbol.address, relocs, line_info)?
|
||||
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 mut diff = Vec::<ObjInsDiff>::new();
|
||||
@@ -69,8 +79,15 @@ pub fn diff_code(
|
||||
let (left_out, right_out) = match arch {
|
||||
#[cfg(feature = "ppc")]
|
||||
ObjArchitecture::PowerPc => (
|
||||
obj::ppc::process_code(left_code, left_symbol.address, left_relocs, left_line_info)?,
|
||||
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,
|
||||
@@ -80,6 +97,7 @@ pub fn diff_code(
|
||||
#[cfg(feature = "mips")]
|
||||
ObjArchitecture::Mips => (
|
||||
obj::mips::process_code(
|
||||
config,
|
||||
left_code,
|
||||
left_symbol.address,
|
||||
left_symbol.address + left_symbol.size,
|
||||
@@ -87,6 +105,7 @@ pub fn diff_code(
|
||||
left_line_info,
|
||||
)?,
|
||||
obj::mips::process_code(
|
||||
config,
|
||||
right_code,
|
||||
right_symbol.address,
|
||||
left_symbol.address + left_symbol.size,
|
||||
@@ -94,6 +113,44 @@ pub fn diff_code(
|
||||
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 mut left_diff = Vec::<ObjInsDiff>::new();
|
||||
@@ -183,7 +240,7 @@ fn diff_instructions(
|
||||
fn resolve_branches(vec: &mut [ObjInsDiff]) {
|
||||
let mut branch_idx = 0usize;
|
||||
// Map addresses to indices
|
||||
let mut addr_map = BTreeMap::<u32, usize>::new();
|
||||
let mut addr_map = BTreeMap::<u64, usize>::new();
|
||||
for (i, ins_diff) in vec.iter().enumerate() {
|
||||
if let Some(ins) = &ins_diff.ins {
|
||||
addr_map.insert(ins.address, i);
|
||||
@@ -193,15 +250,7 @@ fn resolve_branches(vec: &mut [ObjInsDiff]) {
|
||||
let mut branches = BTreeMap::<usize, ObjInsBranchFrom>::new();
|
||||
for (i, ins_diff) in vec.iter_mut().enumerate() {
|
||||
if let Some(ins) = &ins_diff.ins {
|
||||
// if ins.ins.is_blr() || ins.reloc.is_some() {
|
||||
// continue;
|
||||
// }
|
||||
if let Some(ins_idx) = ins
|
||||
.args
|
||||
.iter()
|
||||
.find_map(|a| if let ObjInsArg::BranchOffset(offs) = a { Some(offs) } else { None })
|
||||
.and_then(|offs| addr_map.get(&((ins.address as i32 + offs) as u32)))
|
||||
{
|
||||
if let Some(ins_idx) = ins.branch_dest.and_then(|a| addr_map.get(&a)) {
|
||||
if let Some(branch) = branches.get_mut(ins_idx) {
|
||||
ins_diff.branch_to =
|
||||
Some(ObjInsBranchTo { ins_idx: *ins_idx, branch_idx: branch.branch_idx });
|
||||
@@ -262,8 +311,12 @@ fn arg_eq(
|
||||
right_diff: &ObjInsDiff,
|
||||
) -> bool {
|
||||
return match left {
|
||||
ObjInsArg::Arg(l) | ObjInsArg::ArgWithBase(l) => match right {
|
||||
ObjInsArg::Arg(r) | ObjInsArg::ArgWithBase(r) => l == r,
|
||||
ObjInsArg::PlainText(l) => match right {
|
||||
ObjInsArg::PlainText(r) => l == r,
|
||||
_ => false,
|
||||
},
|
||||
ObjInsArg::Arg(l) => match right {
|
||||
ObjInsArg::Arg(r) => l == r,
|
||||
_ => false,
|
||||
},
|
||||
ObjInsArg::Reloc => {
|
||||
@@ -274,15 +327,7 @@ fn arg_eq(
|
||||
right_diff.ins.as_ref().and_then(|i| i.reloc.as_ref()),
|
||||
)
|
||||
}
|
||||
ObjInsArg::RelocWithBase => {
|
||||
matches!(right, ObjInsArg::RelocWithBase)
|
||||
&& reloc_eq(
|
||||
config,
|
||||
left_diff.ins.as_ref().and_then(|i| i.reloc.as_ref()),
|
||||
right_diff.ins.as_ref().and_then(|i| i.reloc.as_ref()),
|
||||
)
|
||||
}
|
||||
ObjInsArg::BranchOffset(_) => {
|
||||
ObjInsArg::BranchDest(_) => {
|
||||
// Compare dest instruction idx after diffing
|
||||
left_diff.branch_to.as_ref().map(|b| b.ins_idx)
|
||||
== right_diff.branch_to.as_ref().map(|b| b.ins_idx)
|
||||
@@ -314,7 +359,15 @@ fn compare_ins(
|
||||
) -> Result<InsDiffResult> {
|
||||
let mut result = InsDiffResult::default();
|
||||
if let (Some(left_ins), Some(right_ins)) = (&left.ins, &right.ins) {
|
||||
if left_ins.args.len() != right_ins.args.len() || left_ins.op != right_ins.op {
|
||||
if left_ins.args.len() != right_ins.args.len()
|
||||
|| left_ins.op != right_ins.op
|
||||
// Check if any PlainText segments differ (punctuation and spacing)
|
||||
// This indicates a more significant difference than a simple arg mismatch
|
||||
|| !left_ins.args.iter().zip(&right_ins.args).all(|(a, b)| match (a, b) {
|
||||
(ObjInsArg::PlainText(l), ObjInsArg::PlainText(r)) => l == r,
|
||||
_ => true,
|
||||
})
|
||||
{
|
||||
// Totally different op
|
||||
result.kind = ObjInsDiffKind::Replace;
|
||||
state.diff_count += 1;
|
||||
@@ -335,9 +388,10 @@ fn compare_ins(
|
||||
state.diff_count += 1;
|
||||
}
|
||||
let a_str = match a {
|
||||
ObjInsArg::Arg(arg) | ObjInsArg::ArgWithBase(arg) => arg.to_string(),
|
||||
ObjInsArg::Reloc | ObjInsArg::RelocWithBase => String::new(),
|
||||
ObjInsArg::BranchOffset(arg) => format!("{arg}"),
|
||||
ObjInsArg::PlainText(arg) => arg.clone(),
|
||||
ObjInsArg::Arg(arg) => arg.to_string(),
|
||||
ObjInsArg::Reloc => String::new(),
|
||||
ObjInsArg::BranchDest(arg) => format!("{arg}"),
|
||||
};
|
||||
let a_diff = if let Some(idx) = state.left_args_idx.get(&a_str) {
|
||||
ObjInsArgDiff { idx: *idx }
|
||||
@@ -348,9 +402,10 @@ fn compare_ins(
|
||||
ObjInsArgDiff { idx }
|
||||
};
|
||||
let b_str = match b {
|
||||
ObjInsArg::Arg(arg) | ObjInsArg::ArgWithBase(arg) => arg.to_string(),
|
||||
ObjInsArg::Reloc | ObjInsArg::RelocWithBase => String::new(),
|
||||
ObjInsArg::BranchOffset(arg) => format!("{arg}"),
|
||||
ObjInsArg::PlainText(arg) => arg.clone(),
|
||||
ObjInsArg::Arg(arg) => arg.to_string(),
|
||||
ObjInsArg::Reloc => String::new(),
|
||||
ObjInsArg::BranchDest(arg) => format!("{arg}"),
|
||||
};
|
||||
let b_diff = if let Some(idx) = state.right_args_idx.get(&b_str) {
|
||||
ObjInsArgDiff { idx: *idx }
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use crate::obj::{
|
||||
ObjInsArg, ObjInsArgDiff, ObjInsArgValue, ObjInsDiff, ObjReloc, ObjRelocKind, ObjSymbol,
|
||||
};
|
||||
use crate::obj::{ObjInsArg, ObjInsArgDiff, ObjInsArgValue, ObjInsDiff, ObjReloc, ObjSymbol};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum DiffText<'a> {
|
||||
@@ -13,13 +11,13 @@ pub enum DiffText<'a> {
|
||||
/// Line number
|
||||
Line(usize),
|
||||
/// Instruction address
|
||||
Address(u32),
|
||||
Address(u64),
|
||||
/// Instruction mnemonic
|
||||
Opcode(&'a str, u8),
|
||||
Opcode(&'a str, u16),
|
||||
/// Instruction argument
|
||||
Argument(&'a ObjInsArgValue, Option<&'a ObjInsArgDiff>),
|
||||
/// Branch target
|
||||
BranchTarget(u32),
|
||||
/// Branch destination
|
||||
BranchDest(u64),
|
||||
/// Symbol name
|
||||
Symbol(&'a ObjSymbol),
|
||||
/// Number of spaces
|
||||
@@ -32,15 +30,15 @@ pub enum DiffText<'a> {
|
||||
pub enum HighlightKind {
|
||||
#[default]
|
||||
None,
|
||||
Opcode(u8),
|
||||
Opcode(u16),
|
||||
Arg(ObjInsArgValue),
|
||||
Symbol(String),
|
||||
Address(u32),
|
||||
Address(u64),
|
||||
}
|
||||
|
||||
pub fn display_diff<E>(
|
||||
ins_diff: &ObjInsDiff,
|
||||
base_addr: u32,
|
||||
base_addr: u64,
|
||||
mut cb: impl FnMut(DiffText) -> Result<(), E>,
|
||||
) -> Result<(), E> {
|
||||
let Some(ins) = &ins_diff.ins else {
|
||||
@@ -57,43 +55,25 @@ pub fn display_diff<E>(
|
||||
cb(DiffText::Spacing(4))?;
|
||||
}
|
||||
cb(DiffText::Opcode(&ins.mnemonic, ins.op))?;
|
||||
let mut writing_offset = false;
|
||||
for (i, arg) in ins.args.iter().enumerate() {
|
||||
if i == 0 {
|
||||
cb(DiffText::Spacing(1))?;
|
||||
}
|
||||
if i > 0 && !writing_offset {
|
||||
cb(DiffText::Basic(", "))?;
|
||||
}
|
||||
let mut new_writing_offset = false;
|
||||
match arg {
|
||||
ObjInsArg::PlainText(s) => {
|
||||
cb(DiffText::Basic(s))?;
|
||||
}
|
||||
ObjInsArg::Arg(v) => {
|
||||
let diff = ins_diff.arg_diff.get(i).and_then(|o| o.as_ref());
|
||||
cb(DiffText::Argument(v, diff))?;
|
||||
}
|
||||
ObjInsArg::ArgWithBase(v) => {
|
||||
let diff = ins_diff.arg_diff.get(i).and_then(|o| o.as_ref());
|
||||
cb(DiffText::Argument(v, diff))?;
|
||||
cb(DiffText::Basic("("))?;
|
||||
new_writing_offset = true;
|
||||
}
|
||||
ObjInsArg::Reloc => {
|
||||
display_reloc(ins.reloc.as_ref().unwrap(), &mut cb)?;
|
||||
display_reloc_name(ins.reloc.as_ref().unwrap(), &mut cb)?;
|
||||
}
|
||||
ObjInsArg::RelocWithBase => {
|
||||
display_reloc(ins.reloc.as_ref().unwrap(), &mut cb)?;
|
||||
cb(DiffText::Basic("("))?;
|
||||
new_writing_offset = true;
|
||||
}
|
||||
ObjInsArg::BranchOffset(offset) => {
|
||||
let addr = offset + ins.address as i32 - base_addr as i32;
|
||||
cb(DiffText::BranchTarget(addr as u32))?;
|
||||
ObjInsArg::BranchDest(dest) => {
|
||||
cb(DiffText::BranchDest(*dest))?;
|
||||
}
|
||||
}
|
||||
if writing_offset {
|
||||
cb(DiffText::Basic(")"))?;
|
||||
}
|
||||
writing_offset = new_writing_offset;
|
||||
}
|
||||
if let Some(branch) = &ins_diff.branch_to {
|
||||
cb(DiffText::BasicColor(" ~>", branch.branch_idx))?;
|
||||
@@ -114,87 +94,13 @@ fn display_reloc_name<E>(
|
||||
}
|
||||
}
|
||||
|
||||
fn display_reloc<E>(
|
||||
reloc: &ObjReloc,
|
||||
mut cb: impl FnMut(DiffText) -> Result<(), E>,
|
||||
) -> Result<(), E> {
|
||||
match reloc.kind {
|
||||
#[cfg(feature = "ppc")]
|
||||
ObjRelocKind::PpcAddr16Lo => {
|
||||
display_reloc_name(reloc, &mut cb)?;
|
||||
cb(DiffText::Basic("@l"))?;
|
||||
}
|
||||
#[cfg(feature = "ppc")]
|
||||
ObjRelocKind::PpcAddr16Hi => {
|
||||
display_reloc_name(reloc, &mut cb)?;
|
||||
cb(DiffText::Basic("@h"))?;
|
||||
}
|
||||
#[cfg(feature = "ppc")]
|
||||
ObjRelocKind::PpcAddr16Ha => {
|
||||
display_reloc_name(reloc, &mut cb)?;
|
||||
cb(DiffText::Basic("@ha"))?;
|
||||
}
|
||||
#[cfg(feature = "ppc")]
|
||||
ObjRelocKind::PpcEmbSda21 => {
|
||||
display_reloc_name(reloc, &mut cb)?;
|
||||
cb(DiffText::Basic("@sda21"))?;
|
||||
}
|
||||
#[cfg(feature = "ppc")]
|
||||
ObjRelocKind::PpcRel24 | ObjRelocKind::PpcRel14 => {
|
||||
display_reloc_name(reloc, &mut cb)?;
|
||||
}
|
||||
#[cfg(feature = "mips")]
|
||||
ObjRelocKind::MipsHi16 => {
|
||||
cb(DiffText::Basic("%hi("))?;
|
||||
display_reloc_name(reloc, &mut cb)?;
|
||||
cb(DiffText::Basic(")"))?;
|
||||
}
|
||||
#[cfg(feature = "mips")]
|
||||
ObjRelocKind::MipsLo16 => {
|
||||
cb(DiffText::Basic("%lo("))?;
|
||||
display_reloc_name(reloc, &mut cb)?;
|
||||
cb(DiffText::Basic(")"))?;
|
||||
}
|
||||
#[cfg(feature = "mips")]
|
||||
ObjRelocKind::MipsGot16 => {
|
||||
cb(DiffText::Basic("%got("))?;
|
||||
display_reloc_name(reloc, &mut cb)?;
|
||||
cb(DiffText::Basic(")"))?;
|
||||
}
|
||||
#[cfg(feature = "mips")]
|
||||
ObjRelocKind::MipsCall16 => {
|
||||
cb(DiffText::Basic("%call16("))?;
|
||||
display_reloc_name(reloc, &mut cb)?;
|
||||
cb(DiffText::Basic(")"))?;
|
||||
}
|
||||
#[cfg(feature = "mips")]
|
||||
ObjRelocKind::MipsGpRel16 => {
|
||||
cb(DiffText::Basic("%gp_rel("))?;
|
||||
display_reloc_name(reloc, &mut cb)?;
|
||||
cb(DiffText::Basic(")"))?;
|
||||
}
|
||||
#[cfg(feature = "mips")]
|
||||
ObjRelocKind::Mips26 => {
|
||||
display_reloc_name(reloc, &mut cb)?;
|
||||
}
|
||||
#[cfg(feature = "mips")]
|
||||
ObjRelocKind::MipsGpRel32 => {
|
||||
todo!("unimplemented: mips gp_rel32");
|
||||
}
|
||||
ObjRelocKind::Absolute => {
|
||||
cb(DiffText::Basic("[INVALID]"))?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl PartialEq<DiffText<'_>> for HighlightKind {
|
||||
fn eq(&self, other: &DiffText) -> bool {
|
||||
match (self, other) {
|
||||
(HighlightKind::Opcode(a), DiffText::Opcode(_, b)) => a == b,
|
||||
(HighlightKind::Arg(a), DiffText::Argument(b, _)) => a.loose_eq(b),
|
||||
(HighlightKind::Symbol(a), DiffText::Symbol(b)) => a == &b.name,
|
||||
(HighlightKind::Address(a), DiffText::Address(b) | DiffText::BranchTarget(b)) => a == b,
|
||||
(HighlightKind::Address(a), DiffText::Address(b) | DiffText::BranchDest(b)) => a == b,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@@ -210,7 +116,7 @@ impl From<DiffText<'_>> for HighlightKind {
|
||||
DiffText::Opcode(_, op) => HighlightKind::Opcode(op),
|
||||
DiffText::Argument(arg, _) => HighlightKind::Arg(arg.clone()),
|
||||
DiffText::Symbol(sym) => HighlightKind::Symbol(sym.name.to_string()),
|
||||
DiffText::Address(addr) | DiffText::BranchTarget(addr) => HighlightKind::Address(addr),
|
||||
DiffText::Address(addr) | DiffText::BranchDest(addr) => HighlightKind::Address(addr),
|
||||
_ => HighlightKind::None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,16 +9,19 @@ use crate::{
|
||||
code::{diff_code, find_section_and_symbol, no_diff_code},
|
||||
data::{diff_bss_symbols, diff_data, no_diff_data},
|
||||
},
|
||||
obj::{ObjInfo, ObjIns, ObjSectionKind},
|
||||
obj::{x86::X86Formatter, ObjInfo, ObjIns, ObjSectionKind},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Default, Eq, PartialEq)]
|
||||
#[derive(Debug, Clone, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(default)]
|
||||
pub struct DiffObjConfig {
|
||||
pub relax_reloc_diffs: bool,
|
||||
pub space_between_args: bool,
|
||||
pub x86_formatter: X86Formatter,
|
||||
}
|
||||
|
||||
pub struct ProcessCodeResult {
|
||||
pub ops: Vec<u8>,
|
||||
pub ops: Vec<u16>,
|
||||
pub insts: Vec<ObjIns>,
|
||||
}
|
||||
|
||||
@@ -54,6 +57,7 @@ pub fn diff_objs(
|
||||
)?;
|
||||
} else {
|
||||
no_diff_code(
|
||||
config,
|
||||
left.architecture,
|
||||
&left_section.data,
|
||||
left_symbol,
|
||||
@@ -82,6 +86,7 @@ pub fn diff_objs(
|
||||
for right_symbol in &mut right_section.symbols {
|
||||
if right_symbol.instructions.is_empty() {
|
||||
no_diff_code(
|
||||
config,
|
||||
right.architecture,
|
||||
&right_section.data,
|
||||
right_symbol,
|
||||
|
||||
@@ -4,8 +4,8 @@ use anyhow::Result;
|
||||
use rabbitizer::{config, Abi, InstrCategory, Instruction, OperandType};
|
||||
|
||||
use crate::{
|
||||
diff::ProcessCodeResult,
|
||||
obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc},
|
||||
diff::{DiffObjConfig, ProcessCodeResult},
|
||||
obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjRelocKind},
|
||||
};
|
||||
|
||||
fn configure_rabbitizer() {
|
||||
@@ -15,6 +15,7 @@ fn configure_rabbitizer() {
|
||||
}
|
||||
|
||||
pub fn process_code(
|
||||
config: &DiffObjConfig,
|
||||
data: &[u8],
|
||||
start_address: u64,
|
||||
end_address: u64,
|
||||
@@ -24,7 +25,7 @@ pub fn process_code(
|
||||
configure_rabbitizer();
|
||||
|
||||
let ins_count = data.len() / 4;
|
||||
let mut ops = Vec::<u8>::with_capacity(ins_count);
|
||||
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) {
|
||||
@@ -32,35 +33,43 @@ pub fn process_code(
|
||||
let code = u32::from_be_bytes(chunk.try_into()?);
|
||||
let instruction = Instruction::new(code, cur_addr, InstrCategory::CPU);
|
||||
|
||||
let op = instruction.unique_id as u8;
|
||||
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 { Some((cur_addr as i32 + branch_offset) as u32) } else { None };
|
||||
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 op in operands {
|
||||
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 is_branch {
|
||||
args.push(ObjInsArg::BranchOffset(branch_offset));
|
||||
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
|
||||
{
|
||||
// Inter-function reloc, convert to branch offset
|
||||
args.push(ObjInsArg::BranchOffset(
|
||||
reloc.target.address as i32 - cur_addr as i32,
|
||||
));
|
||||
args.push(ObjInsArg::BranchDest(reloc.target.address));
|
||||
} else {
|
||||
args.push(ObjInsArg::Reloc);
|
||||
push_reloc(&mut args, reloc);
|
||||
}
|
||||
} else {
|
||||
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(
|
||||
@@ -69,16 +78,18 @@ pub fn process_code(
|
||||
}
|
||||
}
|
||||
OperandType::cpu_immediate_base => {
|
||||
if reloc.is_some() {
|
||||
args.push(ObjInsArg::RelocWithBase);
|
||||
if let Some(reloc) = reloc {
|
||||
push_reloc(&mut args, reloc);
|
||||
} else {
|
||||
args.push(ObjInsArg::ArgWithBase(ObjInsArgValue::Opaque(
|
||||
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(
|
||||
@@ -91,8 +102,8 @@ pub fn process_code(
|
||||
.as_ref()
|
||||
.and_then(|map| map.range(..=cur_addr as u64).last().map(|(_, &b)| b));
|
||||
insts.push(ObjIns {
|
||||
address: cur_addr,
|
||||
code,
|
||||
address: cur_addr as u64,
|
||||
size: 4,
|
||||
op,
|
||||
mnemonic,
|
||||
args,
|
||||
@@ -105,3 +116,40 @@ pub fn process_code(
|
||||
}
|
||||
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,9 +1,11 @@
|
||||
pub mod elf;
|
||||
#[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};
|
||||
|
||||
@@ -50,8 +52,8 @@ pub struct ObjSection {
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum ObjInsArgValue {
|
||||
Signed(i16),
|
||||
Unsigned(u16),
|
||||
Signed(i64),
|
||||
Unsigned(u64),
|
||||
Opaque(String),
|
||||
}
|
||||
|
||||
@@ -61,7 +63,7 @@ impl ObjInsArgValue {
|
||||
(ObjInsArgValue::Signed(a), ObjInsArgValue::Signed(b)) => a == b,
|
||||
(ObjInsArgValue::Unsigned(a), ObjInsArgValue::Unsigned(b)) => a == b,
|
||||
(ObjInsArgValue::Signed(a), ObjInsArgValue::Unsigned(b))
|
||||
| (ObjInsArgValue::Unsigned(b), ObjInsArgValue::Signed(a)) => *a as u16 == *b,
|
||||
| (ObjInsArgValue::Unsigned(b), ObjInsArgValue::Signed(a)) => *a as u64 == *b,
|
||||
(ObjInsArgValue::Opaque(a), ObjInsArgValue::Opaque(b)) => a == b,
|
||||
_ => false,
|
||||
}
|
||||
@@ -80,21 +82,18 @@ impl fmt::Display for ObjInsArgValue {
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum ObjInsArg {
|
||||
PlainText(String),
|
||||
Arg(ObjInsArgValue),
|
||||
ArgWithBase(ObjInsArgValue),
|
||||
Reloc,
|
||||
RelocWithBase,
|
||||
BranchOffset(i32),
|
||||
BranchDest(u64),
|
||||
}
|
||||
|
||||
impl ObjInsArg {
|
||||
pub fn loose_eq(&self, other: &ObjInsArg) -> bool {
|
||||
match (self, other) {
|
||||
(ObjInsArg::Arg(a), ObjInsArg::Arg(b)) => a.loose_eq(b),
|
||||
(ObjInsArg::ArgWithBase(a), ObjInsArg::ArgWithBase(b)) => a.loose_eq(b),
|
||||
(ObjInsArg::Reloc, ObjInsArg::Reloc) => true,
|
||||
(ObjInsArg::RelocWithBase, ObjInsArg::RelocWithBase) => true,
|
||||
(ObjInsArg::BranchOffset(a), ObjInsArg::BranchOffset(b)) => a == b,
|
||||
(ObjInsArg::BranchDest(a), ObjInsArg::BranchDest(b)) => a == b,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@@ -135,13 +134,13 @@ pub enum ObjInsDiffKind {
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ObjIns {
|
||||
pub address: u32,
|
||||
pub code: u32,
|
||||
pub op: u8,
|
||||
pub address: u64,
|
||||
pub size: u8,
|
||||
pub op: u16,
|
||||
pub mnemonic: String,
|
||||
pub args: Vec<ObjInsArg>,
|
||||
pub reloc: Option<ObjReloc>,
|
||||
pub branch_dest: Option<u32>,
|
||||
pub branch_dest: Option<u64>,
|
||||
/// Line number
|
||||
pub line: Option<u64>,
|
||||
/// Original (unsimplified) instruction
|
||||
@@ -203,6 +202,10 @@ pub enum ObjArchitecture {
|
||||
PowerPc,
|
||||
#[cfg(feature = "mips")]
|
||||
Mips,
|
||||
#[cfg(feature = "x86")]
|
||||
X86_32,
|
||||
#[cfg(feature = "x86")]
|
||||
X86_64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -256,6 +259,8 @@ pub enum ObjRelocKind {
|
||||
MipsGpRel16,
|
||||
#[cfg(feature = "mips")]
|
||||
MipsGpRel32,
|
||||
#[cfg(feature = "x86")]
|
||||
X86PcRel32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
||||
@@ -1,39 +1,34 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use anyhow::Result;
|
||||
use ppc750cl::{disasm_iter, Argument, SimplifiedIns};
|
||||
use anyhow::{bail, Result};
|
||||
use ppc750cl::{disasm_iter, Argument, SimplifiedIns, GPR};
|
||||
|
||||
use crate::{
|
||||
diff::ProcessCodeResult,
|
||||
diff::{DiffObjConfig, ProcessCodeResult},
|
||||
obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjRelocKind},
|
||||
};
|
||||
|
||||
// Relative relocation, can be Simm or BranchOffset
|
||||
fn is_relative_arg(arg: &ObjInsArg) -> bool {
|
||||
matches!(arg, ObjInsArg::Arg(ObjInsArgValue::Signed(_)) | ObjInsArg::BranchOffset(_))
|
||||
// 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: &ObjInsArg) -> bool {
|
||||
matches!(
|
||||
arg,
|
||||
ObjInsArg::Arg(ObjInsArgValue::Signed(_) | ObjInsArgValue::Unsigned(_))
|
||||
| ObjInsArg::ArgWithBase(ObjInsArgValue::Signed(_))
|
||||
)
|
||||
fn is_rel_abs_arg(arg: &Argument) -> bool {
|
||||
matches!(arg, Argument::Uimm(_) | Argument::Simm(_) | Argument::Offset(_))
|
||||
}
|
||||
|
||||
fn is_offset_arg(arg: &ObjInsArg) -> bool {
|
||||
matches!(arg, ObjInsArg::ArgWithBase(ObjInsArgValue::Signed(_)))
|
||||
}
|
||||
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::<u8>::with_capacity(ins_count);
|
||||
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);
|
||||
@@ -50,65 +45,120 @@ pub fn process_code(
|
||||
};
|
||||
}
|
||||
let simplified = ins.clone().simplified();
|
||||
let mut args: Vec<ObjInsArg> = simplified
|
||||
.args
|
||||
.iter()
|
||||
.map(|a| match a {
|
||||
Argument::Simm(simm) => ObjInsArg::Arg(ObjInsArgValue::Signed(simm.0)),
|
||||
Argument::Uimm(uimm) => ObjInsArg::Arg(ObjInsArgValue::Unsigned(uimm.0)),
|
||||
Argument::Offset(offset) => {
|
||||
ObjInsArg::ArgWithBase(ObjInsArgValue::Signed(offset.0))
|
||||
}
|
||||
Argument::BranchDest(dest) => ObjInsArg::BranchOffset(dest.0),
|
||||
_ => ObjInsArg::Arg(ObjInsArgValue::Opaque(a.to_string())),
|
||||
})
|
||||
.collect();
|
||||
|
||||
let mut reloc_arg = None;
|
||||
if let Some(reloc) = reloc {
|
||||
match reloc.kind {
|
||||
ObjRelocKind::PpcEmbSda21 => {
|
||||
args = vec![args[0].clone(), ObjInsArg::Reloc];
|
||||
reloc_arg = Some(1);
|
||||
}
|
||||
ObjRelocKind::PpcRel24 | ObjRelocKind::PpcRel14 => {
|
||||
let arg = args
|
||||
.iter_mut()
|
||||
.rfind(|a| is_relative_arg(a))
|
||||
.ok_or_else(|| anyhow::Error::msg("Failed to locate rel arg for reloc"))?;
|
||||
*arg = ObjInsArg::Reloc;
|
||||
reloc_arg = simplified.args.iter().rposition(is_relative_arg);
|
||||
}
|
||||
ObjRelocKind::PpcAddr16Hi
|
||||
| ObjRelocKind::PpcAddr16Ha
|
||||
| ObjRelocKind::PpcAddr16Lo => {
|
||||
match args.iter_mut().rfind(|a| is_rel_abs_arg(a)) {
|
||||
Some(arg) => {
|
||||
*arg = if is_offset_arg(arg) {
|
||||
ObjInsArg::RelocWithBase
|
||||
} else {
|
||||
ObjInsArg::Reloc
|
||||
};
|
||||
}
|
||||
None => {
|
||||
log::warn!("Failed to locate rel/abs arg for reloc");
|
||||
}
|
||||
};
|
||||
reloc_arg = simplified.args.iter().rposition(is_rel_abs_arg);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
ops.push(simplified.ins.op as u8);
|
||||
|
||||
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,
|
||||
code: simplified.ins.code,
|
||||
address: simplified.ins.addr as u64,
|
||||
size: 4,
|
||||
mnemonic: format!("{}{}", simplified.mnemonic, simplified.suffix),
|
||||
args,
|
||||
reloc: reloc.cloned(),
|
||||
op: ins.op as u8,
|
||||
branch_dest: None,
|
||||
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(())
|
||||
}
|
||||
|
||||
@@ -5,8 +5,9 @@ use byteorder::{BigEndian, ReadBytesExt};
|
||||
use filetime::FileTime;
|
||||
use flagset::Flags;
|
||||
use object::{
|
||||
elf, Architecture, File, Object, ObjectSection, ObjectSymbol, RelocationFlags,
|
||||
RelocationTarget, SectionIndex, SectionKind, Symbol, SymbolKind, SymbolScope, SymbolSection,
|
||||
elf, pe, Architecture, BinaryFormat, Endian, File, Object, ObjectSection, ObjectSymbol,
|
||||
RelocationFlags, RelocationTarget, SectionIndex, SectionKind, Symbol, SymbolKind, SymbolScope,
|
||||
SymbolSection,
|
||||
};
|
||||
|
||||
use crate::obj::{
|
||||
@@ -48,7 +49,7 @@ fn to_obj_symbol(
|
||||
if symbol.is_weak() {
|
||||
flags = ObjSymbolFlagSet(flags.0 | ObjSymbolFlags::Weak);
|
||||
}
|
||||
if symbol.scope() == SymbolScope::Linkage {
|
||||
if obj_file.format() == BinaryFormat::Elf && symbol.scope() == SymbolScope::Linkage {
|
||||
flags = ObjSymbolFlagSet(flags.0 | ObjSymbolFlags::Hidden);
|
||||
}
|
||||
let section_address = if let Some(section) =
|
||||
@@ -63,6 +64,10 @@ fn to_obj_symbol(
|
||||
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();
|
||||
}
|
||||
// Find the virtual address for the symbol if available
|
||||
let virtual_address = split_meta
|
||||
.and_then(|m| m.virtual_addresses.as_ref())
|
||||
@@ -225,9 +230,17 @@ fn relocations_by_section(
|
||||
let mut relocations = Vec::<ObjReloc>::new();
|
||||
for (address, reloc) in obj_section.relocations() {
|
||||
let symbol = match reloc.target() {
|
||||
RelocationTarget::Symbol(idx) => obj_file
|
||||
.symbol_by_index(idx)
|
||||
.context("Failed to locate relocation target symbol")?,
|
||||
RelocationTarget::Symbol(idx) => {
|
||||
let Ok(symbol) = obj_file.symbol_by_index(idx) else {
|
||||
log::warn!(
|
||||
"Failed to locate relocation {:#x} target symbol {}",
|
||||
address,
|
||||
idx.0
|
||||
);
|
||||
continue;
|
||||
};
|
||||
symbol
|
||||
}
|
||||
_ => bail!("Unhandled relocation target: {:?}", reloc.target()),
|
||||
};
|
||||
let kind = match reloc.flags() {
|
||||
@@ -241,7 +254,7 @@ fn relocations_by_section(
|
||||
elf::R_PPC_REL24 => ObjRelocKind::PpcRel24,
|
||||
elf::R_PPC_REL14 => ObjRelocKind::PpcRel14,
|
||||
elf::R_PPC_EMB_SDA21 => ObjRelocKind::PpcEmbSda21,
|
||||
_ => bail!("Unhandled PPC relocation type: {r_type}"),
|
||||
_ => bail!("Unhandled ELF PPC relocation type: {r_type}"),
|
||||
},
|
||||
#[cfg(feature = "mips")]
|
||||
ObjArchitecture::Mips => match r_type {
|
||||
@@ -253,8 +266,34 @@ fn relocations_by_section(
|
||||
elf::R_MIPS_CALL16 => ObjRelocKind::MipsCall16,
|
||||
elf::R_MIPS_GPREL16 => ObjRelocKind::MipsGpRel16,
|
||||
elf::R_MIPS_GPREL32 => ObjRelocKind::MipsGpRel32,
|
||||
_ => bail!("Unhandled MIPS relocation type: {r_type}"),
|
||||
_ => 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),
|
||||
};
|
||||
@@ -266,9 +305,8 @@ fn relocations_by_section(
|
||||
_ => None,
|
||||
};
|
||||
let addend = if reloc.has_implicit_addend() {
|
||||
let addend = u32::from_be_bytes(
|
||||
section.data[address as usize..address as usize + 4].try_into()?,
|
||||
);
|
||||
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")]
|
||||
@@ -282,6 +320,8 @@ fn relocations_by_section(
|
||||
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:?}"),
|
||||
}
|
||||
} else {
|
||||
@@ -373,6 +413,10 @@ pub fn read(obj_path: &Path) -> Result<ObjInfo> {
|
||||
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 split_meta = split_meta(&obj_file)?;
|
||||
334
objdiff-core/src/obj/x86.rs
Normal file
334
objdiff-core/src/obj/x86.rs
Normal file
@@ -0,0 +1,334 @@
|
||||
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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ pub(crate) struct ReallySigned<N: PrimInt>(pub(crate) N);
|
||||
|
||||
impl<N: PrimInt> LowerHex for ReallySigned<N> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
let num = self.0.to_i32().unwrap();
|
||||
let num = self.0.to_i64().unwrap();
|
||||
let prefix = if f.alternate() { "0x" } else { "" };
|
||||
let bare_hex = format!("{:x}", num.abs());
|
||||
f.pad_integral(num >= 0, prefix, &bare_hex)
|
||||
@@ -16,7 +16,7 @@ impl<N: PrimInt> LowerHex for ReallySigned<N> {
|
||||
|
||||
impl<N: PrimInt> UpperHex for ReallySigned<N> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
let num = self.0.to_i32().unwrap();
|
||||
let num = self.0.to_i64().unwrap();
|
||||
let prefix = if f.alternate() { "0x" } else { "" };
|
||||
let bare_hex = format!("{:X}", num.abs());
|
||||
f.pad_integral(num >= 0, prefix, &bare_hex)
|
||||
|
||||
Reference in New Issue
Block a user