mirror of
https://github.com/encounter/objdiff.git
synced 2025-12-11 06:27:55 +00:00
Configurable diff algorithms & new default algorithm
Uses the similar crate to support new diff algorithms: - Patience (new default) - Levenshtein (old default) - Myers - LCS (Longest Common Subsequence) Options in "Diff Options" -> "Algorithm..."
This commit is contained in:
478
src/diff/code.rs
Normal file
478
src/diff/code.rs
Normal file
@@ -0,0 +1,478 @@
|
||||
use std::{
|
||||
cmp::max,
|
||||
collections::BTreeMap,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use anyhow::Result;
|
||||
use similar::{capture_diff_slices_deadline, Algorithm};
|
||||
|
||||
use crate::{
|
||||
diff::{
|
||||
editops::{editops_find, LevEditType},
|
||||
DiffAlg, ProcessCodeResult,
|
||||
},
|
||||
obj::{
|
||||
mips, ppc, ObjArchitecture, ObjInfo, ObjInsArg, ObjInsArgDiff, ObjInsBranchFrom,
|
||||
ObjInsBranchTo, ObjInsDiff, ObjInsDiffKind, ObjReloc, ObjSymbol, ObjSymbolFlags,
|
||||
},
|
||||
};
|
||||
|
||||
pub fn no_diff_code(
|
||||
arch: ObjArchitecture,
|
||||
data: &[u8],
|
||||
symbol: &mut ObjSymbol,
|
||||
relocs: &[ObjReloc],
|
||||
line_info: &Option<BTreeMap<u32, u32>>,
|
||||
) -> Result<()> {
|
||||
let code =
|
||||
&data[symbol.section_address as usize..(symbol.section_address + symbol.size) as usize];
|
||||
let out = match arch {
|
||||
ObjArchitecture::PowerPc => ppc::process_code(code, symbol.address, relocs, line_info)?,
|
||||
ObjArchitecture::Mips => mips::process_code(
|
||||
code,
|
||||
symbol.address,
|
||||
symbol.address + symbol.size,
|
||||
relocs,
|
||||
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(())
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn diff_code(
|
||||
alg: DiffAlg,
|
||||
arch: ObjArchitecture,
|
||||
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<u32, u32>>,
|
||||
right_line_info: &Option<BTreeMap<u32, u32>>,
|
||||
) -> Result<()> {
|
||||
let left_code = &left_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
|
||||
..(right_symbol.section_address + right_symbol.size) as usize];
|
||||
let (left_out, right_out) = match arch {
|
||||
ObjArchitecture::PowerPc => (
|
||||
ppc::process_code(left_code, left_symbol.address, left_relocs, left_line_info)?,
|
||||
ppc::process_code(right_code, right_symbol.address, right_relocs, right_line_info)?,
|
||||
),
|
||||
ObjArchitecture::Mips => (
|
||||
mips::process_code(
|
||||
left_code,
|
||||
left_symbol.address,
|
||||
left_symbol.address + left_symbol.size,
|
||||
left_relocs,
|
||||
left_line_info,
|
||||
)?,
|
||||
mips::process_code(
|
||||
right_code,
|
||||
right_symbol.address,
|
||||
left_symbol.address + left_symbol.size,
|
||||
right_relocs,
|
||||
right_line_info,
|
||||
)?,
|
||||
),
|
||||
};
|
||||
|
||||
let mut left_diff = Vec::<ObjInsDiff>::new();
|
||||
let mut right_diff = Vec::<ObjInsDiff>::new();
|
||||
match alg {
|
||||
DiffAlg::Levenshtein => {
|
||||
diff_instructions_lev(
|
||||
&mut left_diff,
|
||||
&mut right_diff,
|
||||
left_symbol,
|
||||
right_symbol,
|
||||
&left_out,
|
||||
&right_out,
|
||||
)?;
|
||||
}
|
||||
DiffAlg::Lcs => {
|
||||
diff_instructions_similar(
|
||||
Algorithm::Lcs,
|
||||
&mut left_diff,
|
||||
&mut right_diff,
|
||||
&left_out,
|
||||
&right_out,
|
||||
)?;
|
||||
}
|
||||
DiffAlg::Myers => {
|
||||
diff_instructions_similar(
|
||||
Algorithm::Myers,
|
||||
&mut left_diff,
|
||||
&mut right_diff,
|
||||
&left_out,
|
||||
&right_out,
|
||||
)?;
|
||||
}
|
||||
DiffAlg::Patience => {
|
||||
diff_instructions_similar(
|
||||
Algorithm::Patience,
|
||||
&mut left_diff,
|
||||
&mut right_diff,
|
||||
&left_out,
|
||||
&right_out,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
resolve_branches(&mut left_diff);
|
||||
resolve_branches(&mut right_diff);
|
||||
|
||||
let mut diff_state = InsDiffState::default();
|
||||
for (left, right) in left_diff.iter_mut().zip(right_diff.iter_mut()) {
|
||||
let result = compare_ins(left, right, &mut diff_state)?;
|
||||
left.kind = result.kind;
|
||||
right.kind = result.kind;
|
||||
left.arg_diff = result.left_args_diff;
|
||||
right.arg_diff = result.right_args_diff;
|
||||
}
|
||||
|
||||
let total = left_out.insts.len();
|
||||
let percent = if diff_state.diff_count >= total {
|
||||
0.0
|
||||
} 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(())
|
||||
}
|
||||
|
||||
fn diff_instructions_similar(
|
||||
alg: Algorithm,
|
||||
left_diff: &mut Vec<ObjInsDiff>,
|
||||
right_diff: &mut Vec<ObjInsDiff>,
|
||||
left_code: &ProcessCodeResult,
|
||||
right_code: &ProcessCodeResult,
|
||||
) -> Result<()> {
|
||||
let deadline = Instant::now() + Duration::from_secs(5);
|
||||
let ops = capture_diff_slices_deadline(alg, &left_code.ops, &right_code.ops, Some(deadline));
|
||||
if ops.is_empty() {
|
||||
left_diff.extend(
|
||||
left_code
|
||||
.insts
|
||||
.iter()
|
||||
.map(|i| ObjInsDiff { ins: Some(i.clone()), ..Default::default() }),
|
||||
);
|
||||
right_diff.extend(
|
||||
right_code
|
||||
.insts
|
||||
.iter()
|
||||
.map(|i| ObjInsDiff { ins: Some(i.clone()), ..Default::default() }),
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
for op in ops {
|
||||
let (_tag, left_range, right_range) = op.as_tag_tuple();
|
||||
let len = max(left_range.len(), right_range.len());
|
||||
left_diff.extend(
|
||||
left_code.insts[left_range.clone()]
|
||||
.iter()
|
||||
.map(|i| ObjInsDiff { ins: Some(i.clone()), ..Default::default() }),
|
||||
);
|
||||
right_diff.extend(
|
||||
right_code.insts[right_range.clone()]
|
||||
.iter()
|
||||
.map(|i| ObjInsDiff { ins: Some(i.clone()), ..Default::default() }),
|
||||
);
|
||||
if left_range.len() < len {
|
||||
left_diff.extend((left_range.len()..len).map(|_| ObjInsDiff::default()));
|
||||
}
|
||||
if right_range.len() < len {
|
||||
right_diff.extend((right_range.len()..len).map(|_| ObjInsDiff::default()));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn diff_instructions_lev(
|
||||
left_diff: &mut Vec<ObjInsDiff>,
|
||||
right_diff: &mut Vec<ObjInsDiff>,
|
||||
left_symbol: &ObjSymbol,
|
||||
right_symbol: &ObjSymbol,
|
||||
left_code: &ProcessCodeResult,
|
||||
right_code: &ProcessCodeResult,
|
||||
) -> Result<()> {
|
||||
let edit_ops = editops_find(&left_code.ops, &right_code.ops);
|
||||
|
||||
let mut op_iter = edit_ops.iter();
|
||||
let mut left_iter = left_code.insts.iter();
|
||||
let mut right_iter = right_code.insts.iter();
|
||||
let mut cur_op = op_iter.next();
|
||||
let mut cur_left = left_iter.next();
|
||||
let mut cur_right = right_iter.next();
|
||||
while let Some(op) = cur_op {
|
||||
let left_addr = op.first_start as u32 * 4;
|
||||
let right_addr = op.second_start as u32 * 4;
|
||||
while let (Some(left), Some(right)) = (cur_left, cur_right) {
|
||||
if (left.address - left_symbol.address as u32) < left_addr {
|
||||
left_diff.push(ObjInsDiff { ins: Some(left.clone()), ..ObjInsDiff::default() });
|
||||
right_diff.push(ObjInsDiff { ins: Some(right.clone()), ..ObjInsDiff::default() });
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
cur_left = left_iter.next();
|
||||
cur_right = right_iter.next();
|
||||
}
|
||||
if let (Some(left), Some(right)) = (cur_left, cur_right) {
|
||||
if (left.address - left_symbol.address as u32) != left_addr {
|
||||
return Err(anyhow::Error::msg("Instruction address mismatch (left)"));
|
||||
}
|
||||
if (right.address - right_symbol.address as u32) != right_addr {
|
||||
return Err(anyhow::Error::msg("Instruction address mismatch (right)"));
|
||||
}
|
||||
match op.op_type {
|
||||
LevEditType::Replace => {
|
||||
left_diff.push(ObjInsDiff { ins: Some(left.clone()), ..ObjInsDiff::default() });
|
||||
right_diff
|
||||
.push(ObjInsDiff { ins: Some(right.clone()), ..ObjInsDiff::default() });
|
||||
cur_left = left_iter.next();
|
||||
cur_right = right_iter.next();
|
||||
}
|
||||
LevEditType::Insert => {
|
||||
left_diff.push(ObjInsDiff::default());
|
||||
right_diff
|
||||
.push(ObjInsDiff { ins: Some(right.clone()), ..ObjInsDiff::default() });
|
||||
cur_right = right_iter.next();
|
||||
}
|
||||
LevEditType::Delete => {
|
||||
left_diff.push(ObjInsDiff { ins: Some(left.clone()), ..ObjInsDiff::default() });
|
||||
right_diff.push(ObjInsDiff::default());
|
||||
cur_left = left_iter.next();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
cur_op = op_iter.next();
|
||||
}
|
||||
|
||||
// Finalize
|
||||
while cur_left.is_some() || cur_right.is_some() {
|
||||
left_diff.push(ObjInsDiff { ins: cur_left.cloned(), ..ObjInsDiff::default() });
|
||||
right_diff.push(ObjInsDiff { ins: cur_right.cloned(), ..ObjInsDiff::default() });
|
||||
cur_left = left_iter.next();
|
||||
cur_right = right_iter.next();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn resolve_branches(vec: &mut [ObjInsDiff]) {
|
||||
let mut branch_idx = 0usize;
|
||||
// Map addresses to indices
|
||||
let mut addr_map = BTreeMap::<u32, usize>::new();
|
||||
for (i, ins_diff) in vec.iter().enumerate() {
|
||||
if let Some(ins) = &ins_diff.ins {
|
||||
addr_map.insert(ins.address, i);
|
||||
}
|
||||
}
|
||||
// Generate branches
|
||||
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(branch) = branches.get_mut(ins_idx) {
|
||||
ins_diff.branch_to =
|
||||
Some(ObjInsBranchTo { ins_idx: *ins_idx, branch_idx: branch.branch_idx });
|
||||
branch.ins_idx.push(i);
|
||||
} else {
|
||||
ins_diff.branch_to = Some(ObjInsBranchTo { ins_idx: *ins_idx, branch_idx });
|
||||
branches.insert(*ins_idx, ObjInsBranchFrom { ins_idx: vec![i], branch_idx });
|
||||
branch_idx += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Store branch from
|
||||
for (i, branch) in branches {
|
||||
vec[i].branch_from = Some(branch);
|
||||
}
|
||||
}
|
||||
|
||||
fn address_eq(left: &ObjSymbol, right: &ObjSymbol) -> bool {
|
||||
left.address as i64 + left.addend == right.address as i64 + right.addend
|
||||
}
|
||||
|
||||
fn reloc_eq(left_reloc: Option<&ObjReloc>, right_reloc: Option<&ObjReloc>) -> bool {
|
||||
let (Some(left), Some(right)) = (left_reloc, right_reloc) else {
|
||||
return false;
|
||||
};
|
||||
if left.kind != right.kind {
|
||||
return false;
|
||||
}
|
||||
|
||||
let name_matches = left.target.name == right.target.name;
|
||||
match (&left.target_section, &right.target_section) {
|
||||
(Some(sl), Some(sr)) => {
|
||||
// Match if section and name or address match
|
||||
sl == sr && (name_matches || address_eq(&left.target, &right.target))
|
||||
}
|
||||
(Some(_), None) => false,
|
||||
(None, Some(_)) => {
|
||||
// Match if possibly stripped weak symbol
|
||||
name_matches && right.target.flags.0.contains(ObjSymbolFlags::Weak)
|
||||
}
|
||||
(None, None) => name_matches,
|
||||
}
|
||||
}
|
||||
|
||||
fn arg_eq(
|
||||
left: &ObjInsArg,
|
||||
right: &ObjInsArg,
|
||||
left_diff: &ObjInsDiff,
|
||||
right_diff: &ObjInsDiff,
|
||||
) -> bool {
|
||||
return match left {
|
||||
ObjInsArg::PpcArg(l) => match right {
|
||||
ObjInsArg::PpcArg(r) => format!("{l}") == format!("{r}"),
|
||||
_ => false,
|
||||
},
|
||||
ObjInsArg::Reloc => {
|
||||
matches!(right, ObjInsArg::Reloc)
|
||||
&& reloc_eq(
|
||||
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::RelocWithBase => {
|
||||
matches!(right, ObjInsArg::RelocWithBase)
|
||||
&& reloc_eq(
|
||||
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::MipsArg(ls) | ObjInsArg::MipsArgWithBase(ls) => {
|
||||
matches!(right, ObjInsArg::MipsArg(rs) | ObjInsArg::MipsArgWithBase(rs) if ls == rs)
|
||||
}
|
||||
ObjInsArg::BranchOffset(_) => {
|
||||
// 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)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct InsDiffState {
|
||||
diff_count: usize,
|
||||
left_arg_idx: usize,
|
||||
right_arg_idx: usize,
|
||||
left_args_idx: BTreeMap<String, usize>,
|
||||
right_args_idx: BTreeMap<String, usize>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct InsDiffResult {
|
||||
kind: ObjInsDiffKind,
|
||||
left_args_diff: Vec<Option<ObjInsArgDiff>>,
|
||||
right_args_diff: Vec<Option<ObjInsArgDiff>>,
|
||||
}
|
||||
|
||||
fn compare_ins(
|
||||
left: &ObjInsDiff,
|
||||
right: &ObjInsDiff,
|
||||
state: &mut InsDiffState,
|
||||
) -> 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 {
|
||||
// Totally different op
|
||||
result.kind = ObjInsDiffKind::Replace;
|
||||
state.diff_count += 1;
|
||||
return Ok(result);
|
||||
}
|
||||
if left_ins.mnemonic != right_ins.mnemonic {
|
||||
// Same op but different mnemonic, still cmp args
|
||||
result.kind = ObjInsDiffKind::OpMismatch;
|
||||
state.diff_count += 1;
|
||||
}
|
||||
for (a, b) in left_ins.args.iter().zip(&right_ins.args) {
|
||||
if arg_eq(a, b, left, right) {
|
||||
result.left_args_diff.push(None);
|
||||
result.right_args_diff.push(None);
|
||||
} else {
|
||||
if result.kind == ObjInsDiffKind::None {
|
||||
result.kind = ObjInsDiffKind::ArgMismatch;
|
||||
state.diff_count += 1;
|
||||
}
|
||||
let a_str = match a {
|
||||
ObjInsArg::PpcArg(arg) => format!("{arg}"),
|
||||
ObjInsArg::Reloc | ObjInsArg::RelocWithBase => String::new(),
|
||||
ObjInsArg::MipsArg(str) | ObjInsArg::MipsArgWithBase(str) => str.clone(),
|
||||
ObjInsArg::BranchOffset(arg) => format!("{arg}"),
|
||||
};
|
||||
let a_diff = if let Some(idx) = state.left_args_idx.get(&a_str) {
|
||||
ObjInsArgDiff { idx: *idx }
|
||||
} else {
|
||||
let idx = state.left_arg_idx;
|
||||
state.left_args_idx.insert(a_str, idx);
|
||||
state.left_arg_idx += 1;
|
||||
ObjInsArgDiff { idx }
|
||||
};
|
||||
let b_str = match b {
|
||||
ObjInsArg::PpcArg(arg) => format!("{arg}"),
|
||||
ObjInsArg::Reloc | ObjInsArg::RelocWithBase => String::new(),
|
||||
ObjInsArg::MipsArg(str) | ObjInsArg::MipsArgWithBase(str) => str.clone(),
|
||||
ObjInsArg::BranchOffset(arg) => format!("{arg}"),
|
||||
};
|
||||
let b_diff = if let Some(idx) = state.right_args_idx.get(&b_str) {
|
||||
ObjInsArgDiff { idx: *idx }
|
||||
} else {
|
||||
let idx = state.right_arg_idx;
|
||||
state.right_args_idx.insert(b_str, idx);
|
||||
state.right_arg_idx += 1;
|
||||
ObjInsArgDiff { idx }
|
||||
};
|
||||
result.left_args_diff.push(Some(a_diff));
|
||||
result.right_args_diff.push(Some(b_diff));
|
||||
}
|
||||
}
|
||||
} else if left.ins.is_some() {
|
||||
result.kind = ObjInsDiffKind::Delete;
|
||||
state.diff_count += 1;
|
||||
} else {
|
||||
result.kind = ObjInsDiffKind::Insert;
|
||||
state.diff_count += 1;
|
||||
}
|
||||
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
|
||||
}
|
||||
406
src/diff/data.rs
Normal file
406
src/diff/data.rs
Normal file
@@ -0,0 +1,406 @@
|
||||
use std::{
|
||||
cmp::{max, min, Ordering},
|
||||
mem::take,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use similar::{capture_diff_slices_deadline, Algorithm};
|
||||
|
||||
use crate::{
|
||||
diff::{
|
||||
editops::{editops_find, LevEditType},
|
||||
DiffAlg,
|
||||
},
|
||||
obj::{ObjDataDiff, ObjDataDiffKind, ObjSection, ObjSymbol},
|
||||
};
|
||||
|
||||
pub fn diff_data(alg: DiffAlg, left: &mut ObjSection, right: &mut ObjSection) -> Result<()> {
|
||||
match alg {
|
||||
DiffAlg::Levenshtein => diff_data_lev(left, right),
|
||||
DiffAlg::Lcs => diff_data_similar(Algorithm::Lcs, left, right),
|
||||
DiffAlg::Myers => diff_data_similar(Algorithm::Myers, left, right),
|
||||
DiffAlg::Patience => diff_data_similar(Algorithm::Patience, left, right),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn diff_bss_symbols(
|
||||
left_symbols: &mut [ObjSymbol],
|
||||
right_symbols: &mut [ObjSymbol],
|
||||
) -> Result<()> {
|
||||
for left_symbol in left_symbols {
|
||||
if let Some(right_symbol) = right_symbols.iter_mut().find(|s| s.name == left_symbol.name) {
|
||||
left_symbol.diff_symbol = Some(right_symbol.name.clone());
|
||||
right_symbol.diff_symbol = Some(left_symbol.name.clone());
|
||||
let percent = if left_symbol.size == right_symbol.size { 100.0 } else { 50.0 };
|
||||
left_symbol.match_percent = Some(percent);
|
||||
right_symbol.match_percent = Some(percent);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// WIP diff-by-symbol
|
||||
#[allow(dead_code)]
|
||||
pub fn diff_data_symbols(left: &mut ObjSection, right: &mut ObjSection) -> Result<()> {
|
||||
let mut left_ops = Vec::<u32>::with_capacity(left.symbols.len());
|
||||
let mut right_ops = Vec::<u32>::with_capacity(right.symbols.len());
|
||||
for left_symbol in &left.symbols {
|
||||
let data = &left.data
|
||||
[left_symbol.address as usize..(left_symbol.address + left_symbol.size) as usize];
|
||||
let hash = twox_hash::xxh3::hash64(data);
|
||||
left_ops.push(hash as u32);
|
||||
}
|
||||
for symbol in &right.symbols {
|
||||
let data = &right.data[symbol.address as usize..(symbol.address + symbol.size) as usize];
|
||||
let hash = twox_hash::xxh3::hash64(data);
|
||||
right_ops.push(hash as u32);
|
||||
}
|
||||
|
||||
let edit_ops = editops_find(&left_ops, &right_ops);
|
||||
if edit_ops.is_empty() && !left.data.is_empty() {
|
||||
let mut left_iter = left.symbols.iter_mut();
|
||||
let mut right_iter = right.symbols.iter_mut();
|
||||
loop {
|
||||
let (left_symbol, right_symbol) = match (left_iter.next(), right_iter.next()) {
|
||||
(Some(l), Some(r)) => (l, r),
|
||||
(None, None) => break,
|
||||
_ => return Err(anyhow::Error::msg("L/R mismatch in diff_data_symbols")),
|
||||
};
|
||||
let left_data = &left.data
|
||||
[left_symbol.address as usize..(left_symbol.address + left_symbol.size) as usize];
|
||||
let right_data = &right.data[right_symbol.address as usize
|
||||
..(right_symbol.address + right_symbol.size) as usize];
|
||||
|
||||
left.data_diff.push(ObjDataDiff {
|
||||
data: left_data.to_vec(),
|
||||
kind: ObjDataDiffKind::None,
|
||||
len: left_symbol.size as usize,
|
||||
symbol: left_symbol.name.clone(),
|
||||
});
|
||||
right.data_diff.push(ObjDataDiff {
|
||||
data: right_data.to_vec(),
|
||||
kind: ObjDataDiffKind::None,
|
||||
len: right_symbol.size as usize,
|
||||
symbol: right_symbol.name.clone(),
|
||||
});
|
||||
left_symbol.diff_symbol = Some(right_symbol.name.clone());
|
||||
left_symbol.match_percent = Some(100.0);
|
||||
right_symbol.diff_symbol = Some(left_symbol.name.clone());
|
||||
right_symbol.match_percent = Some(100.0);
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn diff_data_similar(
|
||||
alg: Algorithm,
|
||||
left: &mut ObjSection,
|
||||
right: &mut ObjSection,
|
||||
) -> Result<()> {
|
||||
let deadline = Instant::now() + Duration::from_secs(5);
|
||||
let ops = capture_diff_slices_deadline(alg, &left.data, &right.data, Some(deadline));
|
||||
|
||||
let mut left_diff = Vec::<ObjDataDiff>::new();
|
||||
let mut right_diff = Vec::<ObjDataDiff>::new();
|
||||
for op in ops {
|
||||
let (tag, left_range, right_range) = op.as_tag_tuple();
|
||||
let left_len = left_range.len();
|
||||
let right_len = right_range.len();
|
||||
let mut len = max(left_len, right_len);
|
||||
let kind = match tag {
|
||||
similar::DiffTag::Equal => ObjDataDiffKind::None,
|
||||
similar::DiffTag::Delete => ObjDataDiffKind::Delete,
|
||||
similar::DiffTag::Insert => ObjDataDiffKind::Insert,
|
||||
similar::DiffTag::Replace => {
|
||||
// Ensure replacements are equal length
|
||||
len = min(left_len, right_len);
|
||||
ObjDataDiffKind::Replace
|
||||
}
|
||||
};
|
||||
let left_data = &left.data[left_range];
|
||||
let right_data = &right.data[right_range];
|
||||
left_diff.push(ObjDataDiff {
|
||||
data: left_data[..min(len, left_data.len())].to_vec(),
|
||||
kind,
|
||||
len,
|
||||
..Default::default()
|
||||
});
|
||||
right_diff.push(ObjDataDiff {
|
||||
data: right_data[..min(len, right_data.len())].to_vec(),
|
||||
kind,
|
||||
len,
|
||||
..Default::default()
|
||||
});
|
||||
if kind == ObjDataDiffKind::Replace {
|
||||
match left_len.cmp(&right_len) {
|
||||
Ordering::Less => {
|
||||
let len = right_len - left_len;
|
||||
left_diff.push(ObjDataDiff {
|
||||
data: vec![],
|
||||
kind: ObjDataDiffKind::Insert,
|
||||
len,
|
||||
..Default::default()
|
||||
});
|
||||
right_diff.push(ObjDataDiff {
|
||||
data: right_data[left_len..right_len].to_vec(),
|
||||
kind: ObjDataDiffKind::Insert,
|
||||
len,
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
Ordering::Greater => {
|
||||
let len = left_len - right_len;
|
||||
left_diff.push(ObjDataDiff {
|
||||
data: left_data[right_len..left_len].to_vec(),
|
||||
kind: ObjDataDiffKind::Delete,
|
||||
len,
|
||||
..Default::default()
|
||||
});
|
||||
right_diff.push(ObjDataDiff {
|
||||
data: vec![],
|
||||
kind: ObjDataDiffKind::Delete,
|
||||
len,
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
Ordering::Equal => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
left.data_diff = left_diff;
|
||||
right.data_diff = right_diff;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn diff_data_lev(left: &mut ObjSection, right: &mut ObjSection) -> Result<()> {
|
||||
let matrix_size = (left.data.len() as u64).saturating_mul(right.data.len() as u64);
|
||||
if matrix_size > 1_000_000_000 {
|
||||
bail!(
|
||||
"Data section {} too large for Levenshtein diff ({} * {} = {})",
|
||||
left.name,
|
||||
left.data.len(),
|
||||
right.data.len(),
|
||||
matrix_size
|
||||
);
|
||||
}
|
||||
|
||||
let edit_ops = editops_find(&left.data, &right.data);
|
||||
if edit_ops.is_empty() && !left.data.is_empty() {
|
||||
left.data_diff = vec![ObjDataDiff {
|
||||
data: left.data.clone(),
|
||||
kind: ObjDataDiffKind::None,
|
||||
len: left.data.len(),
|
||||
symbol: String::new(),
|
||||
}];
|
||||
right.data_diff = vec![ObjDataDiff {
|
||||
data: right.data.clone(),
|
||||
kind: ObjDataDiffKind::None,
|
||||
len: right.data.len(),
|
||||
symbol: String::new(),
|
||||
}];
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut left_diff = Vec::<ObjDataDiff>::new();
|
||||
let mut right_diff = Vec::<ObjDataDiff>::new();
|
||||
let mut left_cur = 0usize;
|
||||
let mut right_cur = 0usize;
|
||||
let mut cur_op = LevEditType::Replace;
|
||||
let mut cur_left_data = Vec::<u8>::new();
|
||||
let mut cur_right_data = Vec::<u8>::new();
|
||||
for op in edit_ops {
|
||||
if cur_op != op.op_type || left_cur < op.first_start || right_cur < op.second_start {
|
||||
match cur_op {
|
||||
LevEditType::Replace => {
|
||||
let left_data = take(&mut cur_left_data);
|
||||
let right_data = take(&mut cur_right_data);
|
||||
let left_data_len = left_data.len();
|
||||
let right_data_len = right_data.len();
|
||||
left_diff.push(ObjDataDiff {
|
||||
data: left_data,
|
||||
kind: ObjDataDiffKind::Replace,
|
||||
len: left_data_len,
|
||||
symbol: String::new(),
|
||||
});
|
||||
right_diff.push(ObjDataDiff {
|
||||
data: right_data,
|
||||
kind: ObjDataDiffKind::Replace,
|
||||
len: right_data_len,
|
||||
symbol: String::new(),
|
||||
});
|
||||
}
|
||||
LevEditType::Insert => {
|
||||
let right_data = take(&mut cur_right_data);
|
||||
let right_data_len = right_data.len();
|
||||
left_diff.push(ObjDataDiff {
|
||||
data: vec![],
|
||||
kind: ObjDataDiffKind::Insert,
|
||||
len: right_data_len,
|
||||
symbol: String::new(),
|
||||
});
|
||||
right_diff.push(ObjDataDiff {
|
||||
data: right_data,
|
||||
kind: ObjDataDiffKind::Insert,
|
||||
len: right_data_len,
|
||||
symbol: String::new(),
|
||||
});
|
||||
}
|
||||
LevEditType::Delete => {
|
||||
let left_data = take(&mut cur_left_data);
|
||||
let left_data_len = left_data.len();
|
||||
left_diff.push(ObjDataDiff {
|
||||
data: left_data,
|
||||
kind: ObjDataDiffKind::Delete,
|
||||
len: left_data_len,
|
||||
symbol: String::new(),
|
||||
});
|
||||
right_diff.push(ObjDataDiff {
|
||||
data: vec![],
|
||||
kind: ObjDataDiffKind::Delete,
|
||||
len: left_data_len,
|
||||
symbol: String::new(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
if left_cur < op.first_start {
|
||||
left_diff.push(ObjDataDiff {
|
||||
data: left.data[left_cur..op.first_start].to_vec(),
|
||||
kind: ObjDataDiffKind::None,
|
||||
len: op.first_start - left_cur,
|
||||
symbol: String::new(),
|
||||
});
|
||||
left_cur = op.first_start;
|
||||
}
|
||||
if right_cur < op.second_start {
|
||||
right_diff.push(ObjDataDiff {
|
||||
data: right.data[right_cur..op.second_start].to_vec(),
|
||||
kind: ObjDataDiffKind::None,
|
||||
len: op.second_start - right_cur,
|
||||
symbol: String::new(),
|
||||
});
|
||||
right_cur = op.second_start;
|
||||
}
|
||||
match op.op_type {
|
||||
LevEditType::Replace => {
|
||||
cur_left_data.push(left.data[left_cur]);
|
||||
cur_right_data.push(right.data[right_cur]);
|
||||
left_cur += 1;
|
||||
right_cur += 1;
|
||||
}
|
||||
LevEditType::Insert => {
|
||||
cur_right_data.push(right.data[right_cur]);
|
||||
right_cur += 1;
|
||||
}
|
||||
LevEditType::Delete => {
|
||||
cur_left_data.push(left.data[left_cur]);
|
||||
left_cur += 1;
|
||||
}
|
||||
}
|
||||
cur_op = op.op_type;
|
||||
}
|
||||
// if left_cur < left.data.len() {
|
||||
// let len = left.data.len() - left_cur;
|
||||
// left_diff.push(ObjDataDiff {
|
||||
// data: left.data[left_cur..].to_vec(),
|
||||
// kind: ObjDataDiffKind::Delete,
|
||||
// len,
|
||||
// });
|
||||
// right_diff.push(ObjDataDiff { data: vec![], kind: ObjDataDiffKind::Delete, len });
|
||||
// } else if right_cur < right.data.len() {
|
||||
// let len = right.data.len() - right_cur;
|
||||
// left_diff.push(ObjDataDiff { data: vec![], kind: ObjDataDiffKind::Insert, len });
|
||||
// right_diff.push(ObjDataDiff {
|
||||
// data: right.data[right_cur..].to_vec(),
|
||||
// kind: ObjDataDiffKind::Insert,
|
||||
// len,
|
||||
// });
|
||||
// }
|
||||
|
||||
// TODO: merge with above
|
||||
match cur_op {
|
||||
LevEditType::Replace => {
|
||||
let left_data = take(&mut cur_left_data);
|
||||
let right_data = take(&mut cur_right_data);
|
||||
let left_data_len = left_data.len();
|
||||
let right_data_len = right_data.len();
|
||||
left_diff.push(ObjDataDiff {
|
||||
data: left_data,
|
||||
kind: ObjDataDiffKind::Replace,
|
||||
len: left_data_len,
|
||||
symbol: String::new(),
|
||||
});
|
||||
right_diff.push(ObjDataDiff {
|
||||
data: right_data,
|
||||
kind: ObjDataDiffKind::Replace,
|
||||
len: right_data_len,
|
||||
symbol: String::new(),
|
||||
});
|
||||
}
|
||||
LevEditType::Insert => {
|
||||
let right_data = take(&mut cur_right_data);
|
||||
let right_data_len = right_data.len();
|
||||
left_diff.push(ObjDataDiff {
|
||||
data: vec![],
|
||||
kind: ObjDataDiffKind::Insert,
|
||||
len: right_data_len,
|
||||
symbol: String::new(),
|
||||
});
|
||||
right_diff.push(ObjDataDiff {
|
||||
data: right_data,
|
||||
kind: ObjDataDiffKind::Insert,
|
||||
len: right_data_len,
|
||||
symbol: String::new(),
|
||||
});
|
||||
}
|
||||
LevEditType::Delete => {
|
||||
let left_data = take(&mut cur_left_data);
|
||||
let left_data_len = left_data.len();
|
||||
left_diff.push(ObjDataDiff {
|
||||
data: left_data,
|
||||
kind: ObjDataDiffKind::Delete,
|
||||
len: left_data_len,
|
||||
symbol: String::new(),
|
||||
});
|
||||
right_diff.push(ObjDataDiff {
|
||||
data: vec![],
|
||||
kind: ObjDataDiffKind::Delete,
|
||||
len: left_data_len,
|
||||
symbol: String::new(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if left_cur < left.data.len() {
|
||||
left_diff.push(ObjDataDiff {
|
||||
data: left.data[left_cur..].to_vec(),
|
||||
kind: ObjDataDiffKind::None,
|
||||
len: left.data.len() - left_cur,
|
||||
symbol: String::new(),
|
||||
});
|
||||
}
|
||||
if right_cur < right.data.len() {
|
||||
right_diff.push(ObjDataDiff {
|
||||
data: right.data[right_cur..].to_vec(),
|
||||
kind: ObjDataDiffKind::None,
|
||||
len: right.data.len() - right_cur,
|
||||
symbol: String::new(),
|
||||
});
|
||||
}
|
||||
|
||||
left.data_diff = left_diff;
|
||||
right.data_diff = right_diff;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
pub fn no_diff_data(section: &mut ObjSection) {
|
||||
section.data_diff = vec![ObjDataDiff {
|
||||
data: section.data.clone(),
|
||||
kind: ObjDataDiffKind::None,
|
||||
len: section.data.len(),
|
||||
symbol: String::new(),
|
||||
}];
|
||||
}
|
||||
162
src/diff/editops.rs
Normal file
162
src/diff/editops.rs
Normal file
@@ -0,0 +1,162 @@
|
||||
/// Adapted from https://crates.io/crates/rapidfuzz
|
||||
// Copyright 2020 maxbachmann
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any
|
||||
// person obtaining a copy of this software and associated
|
||||
// documentation files (the "Software"), to deal in the
|
||||
// Software without restriction, including without
|
||||
// limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of
|
||||
// the Software, and to permit persons to whom the Software
|
||||
// is furnished to do so, subject to the following
|
||||
// conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice
|
||||
// shall be included in all copies or substantial portions
|
||||
// of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||
pub enum LevEditType {
|
||||
Replace,
|
||||
Insert,
|
||||
Delete,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct LevEditOp {
|
||||
pub op_type: LevEditType, /* editing operation type */
|
||||
pub first_start: usize, /* source block position */
|
||||
pub second_start: usize, /* destination position */
|
||||
}
|
||||
|
||||
pub fn editops_find<T>(query: &[T], choice: &[T]) -> Vec<LevEditOp>
|
||||
where T: PartialEq {
|
||||
let Affix { prefix_len, suffix_len } = Affix::find(query, choice);
|
||||
|
||||
let first_string = &query[prefix_len..query.len() - suffix_len];
|
||||
let second_string = &choice[prefix_len..choice.len() - suffix_len];
|
||||
|
||||
let matrix_columns = first_string.len() + 1;
|
||||
let matrix_rows = second_string.len() + 1;
|
||||
|
||||
// TODO maybe use an actual matrix for readability
|
||||
let mut cache_matrix: Vec<usize> = vec![0; matrix_rows * matrix_columns];
|
||||
for (i, elem) in cache_matrix.iter_mut().enumerate().take(matrix_rows) {
|
||||
*elem = i;
|
||||
}
|
||||
for i in 1..matrix_columns {
|
||||
cache_matrix[matrix_rows * i] = i;
|
||||
}
|
||||
|
||||
for (i, char1) in first_string.iter().enumerate() {
|
||||
let mut prev = i * matrix_rows;
|
||||
let current = prev + matrix_rows;
|
||||
let mut x = i + 1;
|
||||
for (p, char2p) in second_string.iter().enumerate() {
|
||||
let mut c3 = cache_matrix[prev] + (char1 != char2p) as usize;
|
||||
prev += 1;
|
||||
x += 1;
|
||||
if x >= c3 {
|
||||
x = c3;
|
||||
}
|
||||
c3 = cache_matrix[prev] + 1;
|
||||
if x > c3 {
|
||||
x = c3;
|
||||
}
|
||||
cache_matrix[current + 1 + p] = x;
|
||||
}
|
||||
}
|
||||
editops_from_cost_matrix(matrix_columns, matrix_rows, prefix_len, cache_matrix)
|
||||
}
|
||||
|
||||
fn editops_from_cost_matrix(
|
||||
len1: usize,
|
||||
len2: usize,
|
||||
prefix_len: usize,
|
||||
cache_matrix: Vec<usize>,
|
||||
) -> Vec<LevEditOp> {
|
||||
let mut ops = Vec::with_capacity(cache_matrix[len1 * len2 - 1]);
|
||||
let mut dir = 0;
|
||||
let mut i = len1 - 1;
|
||||
let mut j = len2 - 1;
|
||||
let mut p = len1 * len2 - 1;
|
||||
|
||||
//TODO this is still pretty ugly
|
||||
while i > 0 || j > 0 {
|
||||
let current_value = cache_matrix[p];
|
||||
|
||||
// More than one operation can be possible at a time. We use `dir` to
|
||||
// decide when ambiguous.
|
||||
let is_insert = j > 0 && current_value == cache_matrix[p - 1] + 1;
|
||||
let is_delete = i > 0 && current_value == cache_matrix[p - len2] + 1;
|
||||
let is_replace = i > 0 && j > 0 && current_value == cache_matrix[p - len2 - 1] + 1;
|
||||
|
||||
let (op_type, new_dir) = match (dir, is_insert, is_delete, is_replace) {
|
||||
(_, false, false, false) => (None, 0),
|
||||
(-1, true, _, _) => (Some(LevEditType::Insert), -1),
|
||||
(1, _, true, _) => (Some(LevEditType::Delete), 1),
|
||||
(_, _, _, true) => (Some(LevEditType::Replace), 0),
|
||||
(0, true, _, _) => (Some(LevEditType::Insert), -1),
|
||||
(0, _, true, _) => (Some(LevEditType::Delete), 1),
|
||||
_ => panic!("something went terribly wrong"),
|
||||
};
|
||||
|
||||
match new_dir {
|
||||
-1 => {
|
||||
j -= 1;
|
||||
p -= 1;
|
||||
}
|
||||
1 => {
|
||||
i -= 1;
|
||||
p -= len2;
|
||||
}
|
||||
0 => {
|
||||
i -= 1;
|
||||
j -= 1;
|
||||
p -= len2 + 1;
|
||||
}
|
||||
_ => panic!("something went terribly wrong"),
|
||||
};
|
||||
dir = new_dir;
|
||||
|
||||
if let Some(op_type) = op_type {
|
||||
ops.insert(0, LevEditOp {
|
||||
op_type,
|
||||
first_start: i + prefix_len,
|
||||
second_start: j + prefix_len,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ops
|
||||
}
|
||||
|
||||
pub struct Affix {
|
||||
pub prefix_len: usize,
|
||||
pub suffix_len: usize,
|
||||
}
|
||||
|
||||
impl Affix {
|
||||
pub fn find<T>(s1: &[T], s2: &[T]) -> Affix
|
||||
where T: PartialEq {
|
||||
let prefix_len = s1.iter().zip(s2.iter()).take_while(|t| t.0 == t.1).count();
|
||||
let suffix_len = s1[prefix_len..]
|
||||
.iter()
|
||||
.rev()
|
||||
.zip(s2[prefix_len..].iter().rev())
|
||||
.take_while(|t| t.0 == t.1)
|
||||
.count();
|
||||
|
||||
Affix { prefix_len, suffix_len }
|
||||
}
|
||||
}
|
||||
114
src/diff/mod.rs
Normal file
114
src/diff/mod.rs
Normal file
@@ -0,0 +1,114 @@
|
||||
pub mod code;
|
||||
pub mod data;
|
||||
pub mod editops;
|
||||
|
||||
use anyhow::Result;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
diff::{
|
||||
code::{diff_code, find_section_and_symbol, no_diff_code},
|
||||
data::{diff_bss_symbols, diff_data, no_diff_data},
|
||||
},
|
||||
obj::{ObjInfo, ObjIns, ObjSectionKind},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
|
||||
pub enum DiffAlg {
|
||||
#[default]
|
||||
Patience,
|
||||
Levenshtein,
|
||||
Myers,
|
||||
Lcs,
|
||||
}
|
||||
|
||||
pub struct DiffObjConfig {
|
||||
pub code_alg: DiffAlg,
|
||||
pub data_alg: DiffAlg,
|
||||
}
|
||||
|
||||
pub struct ProcessCodeResult {
|
||||
pub ops: Vec<u8>,
|
||||
pub insts: Vec<ObjIns>,
|
||||
}
|
||||
|
||||
pub fn diff_objs(
|
||||
config: &DiffObjConfig,
|
||||
mut left: Option<&mut ObjInfo>,
|
||||
mut right: Option<&mut ObjInfo>,
|
||||
) -> Result<()> {
|
||||
if let Some(left) = left.as_mut() {
|
||||
for left_section in &mut left.sections {
|
||||
if left_section.kind == ObjSectionKind::Code {
|
||||
for left_symbol in &mut left_section.symbols {
|
||||
if let Some((right, (right_section_idx, right_symbol_idx))) =
|
||||
right.as_mut().and_then(|obj| {
|
||||
find_section_and_symbol(obj, &left_symbol.name).map(|s| (obj, s))
|
||||
})
|
||||
{
|
||||
let right_section = &mut right.sections[right_section_idx];
|
||||
let right_symbol = &mut right_section.symbols[right_symbol_idx];
|
||||
left_symbol.diff_symbol = Some(right_symbol.name.clone());
|
||||
right_symbol.diff_symbol = Some(left_symbol.name.clone());
|
||||
diff_code(
|
||||
config.code_alg,
|
||||
left.architecture,
|
||||
&left_section.data,
|
||||
&right_section.data,
|
||||
left_symbol,
|
||||
right_symbol,
|
||||
&left_section.relocations,
|
||||
&right_section.relocations,
|
||||
&left.line_info,
|
||||
&right.line_info,
|
||||
)?;
|
||||
} else {
|
||||
no_diff_code(
|
||||
left.architecture,
|
||||
&left_section.data,
|
||||
left_symbol,
|
||||
&left_section.relocations,
|
||||
&left.line_info,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
} else if let Some(right_section) = right
|
||||
.as_mut()
|
||||
.and_then(|obj| obj.sections.iter_mut().find(|s| s.name == left_section.name))
|
||||
{
|
||||
if left_section.kind == ObjSectionKind::Data {
|
||||
diff_data(config.data_alg, left_section, right_section)?;
|
||||
} else if left_section.kind == ObjSectionKind::Bss {
|
||||
diff_bss_symbols(&mut left_section.symbols, &mut right_section.symbols)?;
|
||||
}
|
||||
} else if left_section.kind == ObjSectionKind::Data {
|
||||
no_diff_data(left_section);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(right) = right.as_mut() {
|
||||
for right_section in right.sections.iter_mut() {
|
||||
if right_section.kind == ObjSectionKind::Code {
|
||||
for right_symbol in &mut right_section.symbols {
|
||||
if right_symbol.instructions.is_empty() {
|
||||
no_diff_code(
|
||||
right.architecture,
|
||||
&right_section.data,
|
||||
right_symbol,
|
||||
&right_section.relocations,
|
||||
&right.line_info,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
} else if right_section.kind == ObjSectionKind::Data
|
||||
&& right_section.data_diff.is_empty()
|
||||
{
|
||||
no_diff_data(right_section);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let (Some(left), Some(right)) = (left, right) {
|
||||
diff_bss_symbols(&mut left.common, &mut right.common)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user