mirror of
https://github.com/encounter/objdiff.git
synced 2025-12-15 08:06:25 +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,
|
||||
|
||||
Reference in New Issue
Block a user