Skip to content

Commit

Permalink
Restructure diffing code & initial 3-way diffing (WIP)
Browse files Browse the repository at this point in the history
  • Loading branch information
encounter committed Mar 19, 2024
1 parent 1343f4f commit 3c74b89
Show file tree
Hide file tree
Showing 17 changed files with 956 additions and 486 deletions.
412 changes: 304 additions & 108 deletions objdiff-cli/src/cmd/diff.rs

Large diffs are not rendered by default.

13 changes: 7 additions & 6 deletions objdiff-cli/src/cmd/report.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,18 +216,18 @@ fn report_object(
_ => {}
}
// println!("Checking {}", object.name());
let mut target = object
let target = object
.target_path
.as_ref()
.map(|p| obj::read::read(p).with_context(|| format!("Failed to open {}", p.display())))
.transpose()?;
let mut base = object
let base = object
.base_path
.as_ref()
.map(|p| obj::read::read(p).with_context(|| format!("Failed to open {}", p.display())))
.transpose()?;
let config = diff::DiffObjConfig { relax_reloc_diffs: true, ..Default::default() };
diff::diff_objs(&config, target.as_mut(), base.as_mut())?;
let result = diff::diff_objs(&config, target.as_ref(), base.as_ref(), None)?;
let mut unit = ReportUnit {
name: object.name().to_string(),
complete: object.complete,
Expand All @@ -239,11 +239,12 @@ fn report_object(
..Default::default()
};
let obj = target.as_ref().or(base.as_ref()).unwrap();
for section in &obj.sections {
let obj_diff = result.left.as_ref().or(result.right.as_ref()).unwrap();
for (section, section_diff) in obj.sections.iter().zip(&obj_diff.sections) {
if section.kind != ObjSectionKind::Code {
continue;
}
for symbol in &section.symbols {
for (symbol, symbol_diff) in section.symbols.iter().zip(&section_diff.symbols) {
if symbol.size == 0 {
continue;
}
Expand All @@ -255,7 +256,7 @@ fn report_object(
continue;
}
}
let match_percent = symbol.match_percent.unwrap_or_else(|| {
let match_percent = symbol_diff.match_percent.unwrap_or_else(|| {
// Support cases where we don't have a target object,
// assume complete means 100% match
if object.complete == Some(true) {
Expand Down
4 changes: 2 additions & 2 deletions objdiff-core/src/arch/mips.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ use object::{elf, Endian, Endianness, File, Object, Relocation, RelocationFlags}
use rabbitizer::{config, Abi, InstrCategory, Instruction, OperandType};

use crate::{
arch::ObjArch,
diff::{DiffObjConfig, ProcessCodeResult},
arch::{ObjArch, ProcessCodeResult},
diff::DiffObjConfig,
obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection},
};

Expand Down
9 changes: 7 additions & 2 deletions objdiff-core/src/arch/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ use anyhow::{bail, Result};
use object::{Architecture, Object, Relocation, RelocationFlags};

use crate::{
diff::{DiffObjConfig, ProcessCodeResult},
obj::{ObjReloc, ObjSection},
diff::DiffObjConfig,
obj::{ObjIns, ObjReloc, ObjSection},
};

#[cfg(feature = "mips")]
Expand Down Expand Up @@ -33,6 +33,11 @@ pub trait ObjArch: Send + Sync {
fn display_reloc(&self, flags: RelocationFlags) -> Cow<'static, str>;
}

pub struct ProcessCodeResult {
pub ops: Vec<u16>,
pub insts: Vec<ObjIns>,
}

pub fn new_arch(object: &object::File) -> Result<Box<dyn ObjArch>> {
Ok(match object.architecture() {
#[cfg(feature = "ppc")]
Expand Down
4 changes: 2 additions & 2 deletions objdiff-core/src/arch/ppc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ use object::{elf, File, Relocation, RelocationFlags};
use ppc750cl::{disasm_iter, Argument, SimplifiedIns, GPR};

use crate::{
arch::ObjArch,
diff::{DiffObjConfig, ProcessCodeResult},
arch::{ObjArch, ProcessCodeResult},
diff::DiffObjConfig,
obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection},
};

Expand Down
48 changes: 3 additions & 45 deletions objdiff-core/src/arch/x86.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ 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,
PrefixKind, Register,
};
use object::{pe, Endian, Endianness, File, Object, Relocation, RelocationFlags};

use crate::{
arch::ObjArch,
diff::{DiffObjConfig, ProcessCodeResult, X86Formatter},
arch::{ObjArch, ProcessCodeResult},
diff::{DiffObjConfig, X86Formatter},
obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection},
};

Expand Down Expand Up @@ -88,29 +88,6 @@ impl ObjArch for ObjArchX86 {
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(
Expand Down Expand Up @@ -229,7 +206,6 @@ impl InstructionFormatterOutput {

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 {
Expand All @@ -252,14 +228,12 @@ impl FormatterOutput for InstructionFormatterOutput {
}

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();
}
Expand All @@ -274,7 +248,6 @@ impl FormatterOutput for InstructionFormatterOutput {
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);

Expand Down Expand Up @@ -343,7 +316,6 @@ impl FormatterOutput for InstructionFormatterOutput {
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()));
Expand All @@ -357,22 +329,8 @@ impl FormatterOutput for InstructionFormatterOutput {
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"));
}
}
}
108 changes: 58 additions & 50 deletions objdiff-core/src/diff/code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,56 +8,66 @@ use anyhow::Result;
use similar::{capture_diff_slices_deadline, Algorithm};

use crate::{
arch::ObjArch,
diff::{DiffObjConfig, ProcessCodeResult},
obj::{
ObjInfo, ObjInsArg, ObjInsArgDiff, ObjInsBranchFrom, ObjInsBranchTo, ObjInsDiff,
ObjInsDiffKind, ObjReloc, ObjSymbol, ObjSymbolFlags,
arch::ProcessCodeResult,
diff::{
get_symbol, DiffObjConfig, ObjInsArgDiff, ObjInsBranchFrom, ObjInsBranchTo, ObjInsDiff,
ObjInsDiffKind, ObjSymbolDiff, SymbolRef,
},
obj::{ObjInfo, ObjInsArg, ObjReloc, ObjSymbol, ObjSymbolFlags},
};

pub fn no_diff_code(
arch: &dyn ObjArch,
obj: &ObjInfo,
symbol_ref: SymbolRef,
config: &DiffObjConfig,
data: &[u8],
symbol: &mut ObjSymbol,
relocs: &[ObjReloc],
line_info: &Option<BTreeMap<u64, u64>>,
) -> Result<()> {
let code =
&data[symbol.section_address as usize..(symbol.section_address + symbol.size) as usize];
let out = arch.process_code(config, code, symbol.address, relocs, line_info)?;
) -> Result<ObjSymbolDiff> {
let section = &obj.sections[symbol_ref.section_idx];
let symbol = &section.symbols[symbol_ref.symbol_idx];
let code = &section.data
[symbol.section_address as usize..(symbol.section_address + symbol.size) as usize];
let out = obj.arch.process_code(
config,
code,
symbol.address,
&section.relocations,
&obj.line_info,
)?;

let mut diff = Vec::<ObjInsDiff>::new();
for i in out.insts {
diff.push(ObjInsDiff { ins: Some(i), kind: ObjInsDiffKind::None, ..Default::default() });
}
resolve_branches(&mut diff);
symbol.instructions = diff;
Ok(())
Ok(ObjSymbolDiff { diff_symbol: None, instructions: diff, match_percent: None })
}

#[allow(clippy::too_many_arguments)]
pub fn diff_code(
arch: &dyn ObjArch,
left_obj: &ObjInfo,
right_obj: &ObjInfo,
left_symbol_ref: SymbolRef,
right_symbol_ref: SymbolRef,
config: &DiffObjConfig,
left_data: &[u8],
right_data: &[u8],
left_symbol: &mut ObjSymbol,
right_symbol: &mut ObjSymbol,
left_relocs: &[ObjReloc],
right_relocs: &[ObjReloc],
left_line_info: &Option<BTreeMap<u64, u64>>,
right_line_info: &Option<BTreeMap<u64, u64>>,
) -> Result<()> {
let left_code = &left_data[left_symbol.section_address as usize
) -> Result<(ObjSymbolDiff, ObjSymbolDiff)> {
let (left_section, left_symbol) = get_symbol(left_obj, left_symbol_ref);
let (right_section, right_symbol) = get_symbol(right_obj, right_symbol_ref);
let left_code = &left_section.data[left_symbol.section_address as usize
..(left_symbol.section_address + left_symbol.size) as usize];
let right_code = &right_data[right_symbol.section_address as usize
let right_code = &right_section.data[right_symbol.section_address as usize
..(right_symbol.section_address + right_symbol.size) as usize];
let left_out =
arch.process_code(config, left_code, left_symbol.address, left_relocs, left_line_info)?;
let right_out =
arch.process_code(config, right_code, right_symbol.address, right_relocs, right_line_info)?;
let left_out = left_obj.arch.process_code(
config,
left_code,
left_symbol.address,
&left_section.relocations,
&left_obj.line_info,
)?;
let right_out = left_obj.arch.process_code(
config,
right_code,
right_symbol.address,
&right_section.relocations,
&right_obj.line_info,
)?;

let mut left_diff = Vec::<ObjInsDiff>::new();
let mut right_diff = Vec::<ObjInsDiff>::new();
Expand All @@ -81,13 +91,19 @@ pub fn diff_code(
} else {
((total - diff_state.diff_count) as f32 / total as f32) * 100.0
};
left_symbol.match_percent = Some(percent);
right_symbol.match_percent = Some(percent);

left_symbol.instructions = left_diff;
right_symbol.instructions = right_diff;

Ok(())
Ok((
ObjSymbolDiff {
diff_symbol: Some(right_symbol_ref),
instructions: left_diff,
match_percent: Some(percent),
},
ObjSymbolDiff {
diff_symbol: Some(left_symbol_ref),
instructions: right_diff,
match_percent: Some(percent),
},
))
}

fn diff_instructions(
Expand Down Expand Up @@ -223,6 +239,9 @@ fn arg_eq(
},
ObjInsArg::Arg(l) => match right {
ObjInsArg::Arg(r) => l == r,
// If relocations are relaxed, match if left is a constant and right is a reloc
// Useful for instances where the target object is created without relocations
ObjInsArg::Reloc => config.relax_reloc_diffs,
_ => false,
},
ObjInsArg::Reloc => {
Expand Down Expand Up @@ -334,14 +353,3 @@ fn compare_ins(
}
Ok(result)
}

pub fn find_section_and_symbol(obj: &ObjInfo, name: &str) -> Option<(usize, usize)> {
for (section_idx, section) in obj.sections.iter().enumerate() {
let symbol_idx = match section.symbols.iter().position(|symbol| symbol.name == name) {
Some(symbol_idx) => symbol_idx,
None => continue,
};
return Some((section_idx, symbol_idx));
}
None
}
Loading

0 comments on commit 3c74b89

Please sign in to comment.