WIP objdiff 3.0 refactor

This commit is contained in:
2025-02-20 17:48:00 -07:00
parent 6d3c63ccd8
commit f3c157ff06
79 changed files with 14886 additions and 6820 deletions

View File

@@ -1,428 +1,561 @@
use alloc::{
collections::BTreeMap,
collections::{btree_map, BTreeMap},
string::{String, ToString},
vec,
vec::Vec,
};
use anyhow::{anyhow, Result};
use similar::{capture_diff_slices, Algorithm};
use anyhow::{anyhow, ensure, Result};
use super::FunctionRelocDiffs;
use crate::{
arch::ProcessCodeResult,
diff::{
DiffObjConfig, ObjInsArgDiff, ObjInsBranchFrom, ObjInsBranchTo, ObjInsDiff, ObjInsDiffKind,
ObjSymbolDiff,
},
obj::{
ObjInfo, ObjIns, ObjInsArg, ObjReloc, ObjSection, ObjSymbol, ObjSymbolFlags, ObjSymbolKind,
SymbolRef,
},
use super::{
DiffObjConfig, FunctionRelocDiffs, InstructionArgDiffIndex, InstructionBranchFrom,
InstructionBranchTo, InstructionDiffKind, InstructionDiffRow, SymbolDiff,
};
use crate::obj::{
InstructionArg, InstructionArgValue, InstructionRef, Object, ResolvedRelocation,
ScannedInstruction, SymbolFlag, SymbolKind,
};
pub fn process_code_symbol(
obj: &ObjInfo,
symbol_ref: SymbolRef,
config: &DiffObjConfig,
) -> Result<ProcessCodeResult> {
let (section, symbol) = obj.section_symbol(symbol_ref);
let section = section.ok_or_else(|| anyhow!("Code symbol section not found"))?;
let code = &section.data
[symbol.section_address as usize..(symbol.section_address + symbol.size) as usize];
let mut res = obj.arch.process_code(
symbol.address,
code,
section.orig_index,
&section.relocations,
&section.line_info,
config,
)?;
for inst in res.insts.iter_mut() {
if let Some(reloc) = &mut inst.reloc {
if reloc.target.size == 0 && reloc.target.name.is_empty() {
// Fake target symbol we added as a placeholder. We need to find the real one.
if let Some(real_target) =
find_symbol_matching_fake_symbol_in_sections(&reloc.target, &obj.sections)
{
reloc.addend = (reloc.target.address - real_target.address) as i64;
reloc.target = real_target;
}
}
}
pub fn no_diff_code(
obj: &Object,
symbol_idx: usize,
diff_config: &DiffObjConfig,
) -> Result<SymbolDiff> {
let symbol = &obj.symbols[symbol_idx];
let section_index = symbol.section.ok_or_else(|| anyhow!("Missing section for symbol"))?;
let section = &obj.sections[section_index];
let data = section.data_range(symbol.address, symbol.size as usize).ok_or_else(|| {
anyhow!(
"Symbol data out of bounds: {:#x}..{:#x}",
symbol.address,
symbol.address + symbol.size
)
})?;
let ops = obj.arch.scan_instructions(symbol.address, data, section_index, diff_config)?;
let mut instruction_rows = Vec::<InstructionDiffRow>::new();
for i in &ops {
instruction_rows
.push(InstructionDiffRow { ins_ref: Some(i.ins_ref), ..Default::default() });
}
Ok(res)
resolve_branches(obj, section_index, &ops, &mut instruction_rows);
Ok(SymbolDiff { target_symbol: None, match_percent: None, diff_score: None, instruction_rows })
}
pub fn no_diff_code(out: &ProcessCodeResult, symbol_ref: SymbolRef) -> Result<ObjSymbolDiff> {
let mut diff = Vec::<ObjInsDiff>::new();
for i in &out.insts {
diff.push(ObjInsDiff {
ins: Some(i.clone()),
kind: ObjInsDiffKind::None,
..Default::default()
});
}
resolve_branches(&mut diff);
Ok(ObjSymbolDiff { symbol_ref, target_symbol: None, instructions: diff, match_percent: None })
}
const PENALTY_IMM_DIFF: u64 = 1;
const PENALTY_REG_DIFF: u64 = 5;
const PENALTY_REPLACE: u64 = 60;
const PENALTY_INSERT_DELETE: u64 = 100;
pub fn diff_code(
left_obj: &ObjInfo,
right_obj: &ObjInfo,
left_out: &ProcessCodeResult,
right_out: &ProcessCodeResult,
left_symbol_ref: SymbolRef,
right_symbol_ref: SymbolRef,
config: &DiffObjConfig,
) -> Result<(ObjSymbolDiff, ObjSymbolDiff)> {
let mut left_diff = Vec::<ObjInsDiff>::new();
let mut right_diff = Vec::<ObjInsDiff>::new();
diff_instructions(&mut left_diff, &mut right_diff, left_out, right_out)?;
left_obj: &Object,
right_obj: &Object,
left_symbol_idx: usize,
right_symbol_idx: usize,
diff_config: &DiffObjConfig,
) -> Result<(SymbolDiff, SymbolDiff)> {
let left_symbol = &left_obj.symbols[left_symbol_idx];
let right_symbol = &right_obj.symbols[right_symbol_idx];
let left_section = left_symbol
.section
.and_then(|i| left_obj.sections.get(i))
.ok_or_else(|| anyhow!("Missing section for symbol"))?;
let right_section = right_symbol
.section
.and_then(|i| right_obj.sections.get(i))
.ok_or_else(|| anyhow!("Missing section for symbol"))?;
let left_data = left_section
.data_range(left_symbol.address, left_symbol.size as usize)
.ok_or_else(|| {
anyhow!(
"Symbol data out of bounds: {:#x}..{:#x}",
left_symbol.address,
left_symbol.address + left_symbol.size
)
})?;
let right_data = right_section
.data_range(right_symbol.address, right_symbol.size as usize)
.ok_or_else(|| {
anyhow!(
"Symbol data out of bounds: {:#x}..{:#x}",
right_symbol.address,
right_symbol.address + right_symbol.size
)
})?;
resolve_branches(&mut left_diff);
resolve_branches(&mut right_diff);
let left_section_idx = left_symbol.section.unwrap();
let right_section_idx = right_symbol.section.unwrap();
let left_ops = left_obj.arch.scan_instructions(
left_symbol.address,
left_data,
left_section_idx,
diff_config,
)?;
let right_ops = left_obj.arch.scan_instructions(
right_symbol.address,
right_data,
right_section_idx,
diff_config,
)?;
let (mut left_rows, mut right_rows) = diff_instructions(&left_ops, &right_ops)?;
resolve_branches(left_obj, left_section_idx, &left_ops, &mut left_rows);
resolve_branches(right_obj, right_section_idx, &right_ops, &mut right_rows);
let mut diff_state = InsDiffState::default();
for (left, right) in left_diff.iter_mut().zip(right_diff.iter_mut()) {
let result = compare_ins(config, left_obj, right_obj, 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 mut diff_state = InstructionDiffState::default();
for (left_row, right_row) in left_rows.iter_mut().zip(right_rows.iter_mut()) {
let result = diff_instruction(
left_obj,
right_obj,
left_symbol_idx,
right_symbol_idx,
left_row.ins_ref.as_ref(),
right_row.ins_ref.as_ref(),
left_row,
right_row,
diff_config,
&mut diff_state,
)?;
left_row.kind = result.kind;
right_row.kind = result.kind;
left_row.arg_diff = result.left_args_diff;
right_row.arg_diff = result.right_args_diff;
}
let total = left_out.insts.len().max(right_out.insts.len());
let percent = if diff_state.diff_count >= total {
0.0
let max_score = left_ops.len() as u64 * PENALTY_INSERT_DELETE;
let diff_score = diff_state.diff_score.min(max_score);
let match_percent = if max_score == 0 {
100.0
} else {
((total - diff_state.diff_count) as f32 / total as f32) * 100.0
((1.0 - (diff_score as f64 / max_score as f64)) * 100.0) as f32
};
Ok((
ObjSymbolDiff {
symbol_ref: left_symbol_ref,
target_symbol: Some(right_symbol_ref),
instructions: left_diff,
match_percent: Some(percent),
SymbolDiff {
target_symbol: Some(right_symbol_idx),
match_percent: Some(match_percent),
diff_score: Some((diff_score, max_score)),
instruction_rows: left_rows,
},
ObjSymbolDiff {
symbol_ref: right_symbol_ref,
target_symbol: Some(left_symbol_ref),
instructions: right_diff,
match_percent: Some(percent),
SymbolDiff {
target_symbol: Some(left_symbol_idx),
match_percent: Some(match_percent),
diff_score: Some((diff_score, max_score)),
instruction_rows: right_rows,
},
))
}
fn diff_instructions(
left_diff: &mut Vec<ObjInsDiff>,
right_diff: &mut Vec<ObjInsDiff>,
left_code: &ProcessCodeResult,
right_code: &ProcessCodeResult,
) -> Result<()> {
let ops = capture_diff_slices(Algorithm::Patience, &left_code.ops, &right_code.ops);
left_insts: &[ScannedInstruction],
right_insts: &[ScannedInstruction],
) -> Result<(Vec<InstructionDiffRow>, Vec<InstructionDiffRow>)> {
let left_ops = left_insts.iter().map(|i| i.ins_ref.opcode).collect::<Vec<_>>();
let right_ops = right_insts.iter().map(|i| i.ins_ref.opcode).collect::<Vec<_>>();
let ops = similar::capture_diff_slices(similar::Algorithm::Patience, &left_ops, &right_ops);
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(());
ensure!(left_insts.len() == right_insts.len());
let left_diff = left_insts
.iter()
.map(|i| InstructionDiffRow { ins_ref: Some(i.ins_ref), ..Default::default() })
.collect();
let right_diff = right_insts
.iter()
.map(|i| InstructionDiffRow { ins_ref: Some(i.ins_ref), ..Default::default() })
.collect();
return Ok((left_diff, right_diff));
}
let row_count = ops
.iter()
.map(|op| match *op {
similar::DiffOp::Equal { len, .. } => len,
similar::DiffOp::Delete { old_len, .. } => old_len,
similar::DiffOp::Insert { new_len, .. } => new_len,
similar::DiffOp::Replace { old_len, new_len, .. } => old_len.max(new_len),
})
.sum();
let mut left_diff = Vec::<InstructionDiffRow>::with_capacity(row_count);
let mut right_diff = Vec::<InstructionDiffRow>::with_capacity(row_count);
for op in ops {
let (_tag, left_range, right_range) = op.as_tag_tuple();
let len = left_range.len().max(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() }),
);
left_diff.extend(left_range.clone().map(|i| InstructionDiffRow {
ins_ref: Some(left_insts[i].ins_ref),
..Default::default()
}));
right_diff.extend(right_range.clone().map(|i| InstructionDiffRow {
ins_ref: Some(right_insts[i].ins_ref),
..Default::default()
}));
if left_range.len() < len {
left_diff.extend((left_range.len()..len).map(|_| ObjInsDiff::default()));
left_diff.extend((left_range.len()..len).map(|_| InstructionDiffRow::default()));
}
if right_range.len() < len {
right_diff.extend((right_range.len()..len).map(|_| ObjInsDiff::default()));
right_diff.extend((right_range.len()..len).map(|_| InstructionDiffRow::default()));
}
}
Ok(())
Ok((left_diff, right_diff))
}
fn resolve_branches(vec: &mut [ObjInsDiff]) {
let mut branch_idx = 0usize;
fn arg_to_string(arg: &InstructionArg, reloc: Option<&ResolvedRelocation>) -> String {
match arg {
InstructionArg::Value(arg) => arg.to_string(),
InstructionArg::Reloc => {
reloc.as_ref().map_or_else(|| "<unknown>".to_string(), |r| r.symbol.name.clone())
}
InstructionArg::BranchDest(arg) => arg.to_string(),
}
}
fn resolve_branches(
obj: &Object,
section_index: usize,
ops: &[ScannedInstruction],
rows: &mut [InstructionDiffRow],
) {
let section = &obj.sections[section_index];
let mut branch_idx = 0u32;
// Map addresses to indices
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);
let mut addr_map = BTreeMap::<u64, u32>::new();
for (i, ins_diff) in rows.iter().enumerate() {
if let Some(ins) = ins_diff.ins_ref {
addr_map.insert(ins.address, i as u32);
}
}
// 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 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 });
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 });
let mut branches = BTreeMap::<u32, InstructionBranchFrom>::new();
for ((i, ins_diff), ins) in
rows.iter_mut().enumerate().filter(|(_, row)| row.ins_ref.is_some()).zip(ops)
{
let branch_dest = if let Some(resolved) = section.relocation_at(ins.ins_ref.address, obj) {
if resolved.symbol.section == Some(section_index) {
// If the relocation target is in the same section, use it as the branch destination
resolved.symbol.address.checked_add_signed(resolved.relocation.addend)
} else {
None
}
} else {
ins.branch_dest
};
if let Some(ins_idx) = branch_dest.and_then(|a| addr_map.get(&a).copied()) {
match branches.entry(ins_idx) {
btree_map::Entry::Vacant(e) => {
ins_diff.branch_to = Some(InstructionBranchTo { ins_idx, branch_idx });
e.insert(InstructionBranchFrom { ins_idx: vec![i as u32], branch_idx });
branch_idx += 1;
}
btree_map::Entry::Occupied(e) => {
let branch = e.into_mut();
ins_diff.branch_to =
Some(InstructionBranchTo { ins_idx, branch_idx: branch.branch_idx });
branch.ins_idx.push(i as u32);
}
}
}
}
// Store branch from
for (i, branch) in branches {
vec[i].branch_from = Some(branch);
rows[i as usize].branch_from = Some(branch);
}
}
pub fn address_eq(left: &ObjReloc, right: &ObjReloc) -> bool {
if right.target.size == 0 && left.target.size != 0 {
pub(crate) fn address_eq(left: &ResolvedRelocation, right: &ResolvedRelocation) -> bool {
if right.symbol.size == 0 && left.symbol.size != 0 {
// The base relocation is against a pool but the target relocation isn't.
// This can happen in rare cases where the compiler will generate a pool+addend relocation
// in the base's data, but the one detected in the target is direct with no addend.
// Just check that the final address is the same so these count as a match.
left.target.address as i64 + left.addend == right.target.address as i64 + right.addend
left.symbol.address as i64 + left.relocation.addend
== right.symbol.address as i64 + right.relocation.addend
} else {
// But otherwise, if the compiler isn't using a pool, we're more strict and check that the
// target symbol address and relocation addend both match exactly.
left.target.address == right.target.address && left.addend == right.addend
left.symbol.address == right.symbol.address
&& left.relocation.addend == right.relocation.addend
}
}
pub fn section_name_eq(
left_obj: &ObjInfo,
right_obj: &ObjInfo,
left_orig_section_index: usize,
right_orig_section_index: usize,
pub(crate) fn section_name_eq(
left_obj: &Object,
right_obj: &Object,
left_section_index: usize,
right_section_index: usize,
) -> bool {
let Some(left_section) =
left_obj.sections.iter().find(|s| s.orig_index == left_orig_section_index)
else {
return false;
};
let Some(right_section) =
right_obj.sections.iter().find(|s| s.orig_index == right_orig_section_index)
else {
return false;
};
left_section.name == right_section.name
left_obj.sections.get(left_section_index).is_some_and(|left_section| {
right_obj
.sections
.get(right_section_index)
.is_some_and(|right_section| left_section.name == right_section.name)
})
}
fn reloc_eq(
config: &DiffObjConfig,
left_obj: &ObjInfo,
right_obj: &ObjInfo,
left_ins: Option<&ObjIns>,
right_ins: Option<&ObjIns>,
left_obj: &Object,
right_obj: &Object,
left_reloc: Option<ResolvedRelocation>,
right_reloc: Option<ResolvedRelocation>,
diff_config: &DiffObjConfig,
) -> bool {
let (Some(left_ins), Some(right_ins)) = (left_ins, right_ins) else {
return false;
let relax_reloc_diffs = diff_config.function_reloc_diffs == FunctionRelocDiffs::None;
let (left_reloc, right_reloc) = match (left_reloc, right_reloc) {
(Some(left_reloc), Some(right_reloc)) => (left_reloc, right_reloc),
// If relocations are relaxed, match if left is missing a reloc
(None, Some(_)) => return relax_reloc_diffs,
(None, None) => return true,
_ => return false,
};
let (Some(left), Some(right)) = (&left_ins.reloc, &right_ins.reloc) else {
return false;
};
if left.flags != right.flags {
if left_reloc.relocation.flags != right_reloc.relocation.flags {
return false;
}
if config.function_reloc_diffs == FunctionRelocDiffs::None {
if relax_reloc_diffs {
return true;
}
let symbol_name_addend_matches =
left.target.name == right.target.name && left.addend == right.addend;
match (&left.target.orig_section_index, &right.target.orig_section_index) {
let symbol_name_addend_matches = left_reloc.symbol.name == right_reloc.symbol.name
&& left_reloc.relocation.addend == right_reloc.relocation.addend;
match (&left_reloc.symbol.section, &right_reloc.symbol.section) {
(Some(sl), Some(sr)) => {
// Match if section and name+addend or address match
// Match if section and name or address match
section_name_eq(left_obj, right_obj, *sl, *sr)
&& (config.function_reloc_diffs == FunctionRelocDiffs::DataValue
&& (diff_config.function_reloc_diffs == FunctionRelocDiffs::DataValue
|| symbol_name_addend_matches
|| address_eq(left, right))
&& (config.function_reloc_diffs == FunctionRelocDiffs::NameAddress
|| left.target.kind != ObjSymbolKind::Object
|| left_obj.arch.display_ins_data_labels(left_ins)
== left_obj.arch.display_ins_data_labels(right_ins))
|| address_eq(&left_reloc, &right_reloc))
&& (
diff_config.function_reloc_diffs == FunctionRelocDiffs::NameAddress
|| left_reloc.symbol.kind != SymbolKind::Object
// TODO
// || left_obj.arch.display_ins_data_labels(left_ins)
// == left_obj.arch.display_ins_data_labels(right_ins))
)
}
(Some(_), None) => false,
(None, Some(_)) => {
// Match if possibly stripped weak symbol
symbol_name_addend_matches && right.target.flags.0.contains(ObjSymbolFlags::Weak)
symbol_name_addend_matches && right_reloc.symbol.flags.contains(SymbolFlag::Weak)
}
(None, None) => symbol_name_addend_matches,
}
}
fn arg_eq(
config: &DiffObjConfig,
left_obj: &ObjInfo,
right_obj: &ObjInfo,
left: &ObjInsArg,
right: &ObjInsArg,
left_diff: &ObjInsDiff,
right_diff: &ObjInsDiff,
left_obj: &Object,
right_obj: &Object,
left_row: &InstructionDiffRow,
right_row: &InstructionDiffRow,
left_arg: &InstructionArg,
right_arg: &InstructionArg,
left_reloc: Option<ResolvedRelocation>,
right_reloc: Option<ResolvedRelocation>,
diff_config: &DiffObjConfig,
) -> bool {
match left {
ObjInsArg::PlainText(l) => match right {
ObjInsArg::PlainText(r) => l == r,
_ => false,
},
ObjInsArg::Arg(l) => match right {
ObjInsArg::Arg(r) => l.loose_eq(r),
match left_arg {
InstructionArg::Value(l) => match right_arg {
InstructionArg::Value(r) => l.loose_eq(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.function_reloc_diffs == FunctionRelocDiffs::None,
InstructionArg::Reloc => diff_config.function_reloc_diffs == FunctionRelocDiffs::None,
_ => false,
},
ObjInsArg::Reloc => {
matches!(right, ObjInsArg::Reloc)
&& reloc_eq(
config,
left_obj,
right_obj,
left_diff.ins.as_ref(),
right_diff.ins.as_ref(),
)
InstructionArg::Reloc => {
matches!(right_arg, InstructionArg::Reloc)
&& reloc_eq(left_obj, right_obj, left_reloc, right_reloc, diff_config)
}
ObjInsArg::BranchDest(_) => match right {
InstructionArg::BranchDest(_) => match right_arg {
// Compare dest instruction idx after diffing
ObjInsArg::BranchDest(_) => {
left_diff.branch_to.as_ref().map(|b| b.ins_idx)
== right_diff.branch_to.as_ref().map(|b| b.ins_idx)
InstructionArg::BranchDest(_) => {
left_row.branch_to.as_ref().map(|b| b.ins_idx)
== right_row.branch_to.as_ref().map(|b| b.ins_idx)
}
// 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.function_reloc_diffs == FunctionRelocDiffs::None,
InstructionArg::Reloc => diff_config.function_reloc_diffs == FunctionRelocDiffs::None,
_ => false,
},
}
}
#[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>,
struct InstructionDiffState {
diff_score: u64,
left_arg_idx: u32,
right_arg_idx: u32,
left_args_idx: BTreeMap<String, u32>,
right_args_idx: BTreeMap<String, u32>,
}
#[derive(Default)]
struct InsDiffResult {
kind: ObjInsDiffKind,
left_args_diff: Vec<Option<ObjInsArgDiff>>,
right_args_diff: Vec<Option<ObjInsArgDiff>>,
struct InstructionDiffResult {
kind: InstructionDiffKind,
left_args_diff: Vec<InstructionArgDiffIndex>,
right_args_diff: Vec<InstructionArgDiffIndex>,
}
fn compare_ins(
config: &DiffObjConfig,
left_obj: &ObjInfo,
right_obj: &ObjInfo,
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) {
// Count only non-PlainText args
let left_args_count = left_ins.iter_args().count();
let right_args_count = right_ins.iter_args().count();
if left_args_count != right_args_count || left_ins.op != right_ins.op {
// Totally different op
result.kind = ObjInsDiffKind::Replace;
state.diff_count += 1;
return Ok(result);
impl InstructionDiffResult {
#[inline]
const fn new(kind: InstructionDiffKind) -> Self {
Self { kind, left_args_diff: Vec::new(), right_args_diff: Vec::new() }
}
}
fn diff_instruction(
left_obj: &Object,
right_obj: &Object,
left_symbol_idx: usize,
right_symbol_idx: usize,
l: Option<&InstructionRef>,
r: Option<&InstructionRef>,
left_row: &InstructionDiffRow,
right_row: &InstructionDiffRow,
diff_config: &DiffObjConfig,
state: &mut InstructionDiffState,
) -> Result<InstructionDiffResult> {
let (l, r) = match (l, r) {
(Some(l), Some(r)) => (l, r),
(Some(_), None) => {
state.diff_score += PENALTY_INSERT_DELETE;
return Ok(InstructionDiffResult::new(InstructionDiffKind::Delete));
}
(None, Some(_)) => {
state.diff_score += PENALTY_INSERT_DELETE;
return Ok(InstructionDiffResult::new(InstructionDiffKind::Insert));
}
(None, None) => return Ok(InstructionDiffResult::new(InstructionDiffKind::None)),
};
// If opcodes don't match, replace
if l.opcode != r.opcode {
state.diff_score += PENALTY_REPLACE;
return Ok(InstructionDiffResult::new(InstructionDiffKind::Replace));
}
let left_symbol = &left_obj.symbols[left_symbol_idx];
let right_symbol = &right_obj.symbols[right_symbol_idx];
let left_section = left_symbol
.section
.and_then(|i| left_obj.sections.get(i))
.ok_or_else(|| anyhow!("Missing section for symbol"))?;
let right_section = right_symbol
.section
.and_then(|i| right_obj.sections.get(i))
.ok_or_else(|| anyhow!("Missing section for symbol"))?;
// Resolve relocations
let left_reloc = left_section.relocation_at(l.address, left_obj);
let right_reloc = right_section.relocation_at(r.address, right_obj);
// Compare instruction data
let left_data = left_section.data_range(l.address, l.size as usize).ok_or_else(|| {
anyhow!(
"Instruction data out of bounds: {:#x}..{:#x}",
l.address,
l.address + l.size as u64
)
})?;
let right_data = right_section.data_range(r.address, r.size as usize).ok_or_else(|| {
anyhow!(
"Instruction data out of bounds: {:#x}..{:#x}",
r.address,
r.address + r.size as u64
)
})?;
if left_data != right_data {
// If data doesn't match, process instructions and compare args
let left_ins = left_obj.arch.process_instruction(
*l,
left_data,
left_reloc,
left_symbol.address..left_symbol.address + left_symbol.size,
left_symbol.section.unwrap(),
diff_config,
)?;
let right_ins = left_obj.arch.process_instruction(
*r,
right_data,
right_reloc,
right_symbol.address..right_symbol.address + right_symbol.size,
right_symbol.section.unwrap(),
diff_config,
)?;
if left_ins.args.len() != right_ins.args.len() {
state.diff_score += PENALTY_REPLACE;
return Ok(InstructionDiffResult::new(InstructionDiffKind::Replace));
}
let mut result = InstructionDiffResult::new(InstructionDiffKind::None);
if left_ins.mnemonic != right_ins.mnemonic {
// Same op but different mnemonic, still cmp args
result.kind = ObjInsDiffKind::OpMismatch;
state.diff_count += 1;
state.diff_score += PENALTY_REG_DIFF;
result.kind = InstructionDiffKind::OpMismatch;
}
for (a, b) in left_ins.iter_args().zip(right_ins.iter_args()) {
if arg_eq(config, left_obj, right_obj, a, b, left, right) {
result.left_args_diff.push(None);
result.right_args_diff.push(None);
for (a, b) in left_ins.args.iter().zip(right_ins.args.iter()) {
if arg_eq(
left_obj,
right_obj,
left_row,
right_row,
a,
b,
left_reloc,
right_reloc,
diff_config,
) {
result.left_args_diff.push(InstructionArgDiffIndex::NONE);
result.right_args_diff.push(InstructionArgDiffIndex::NONE);
} else {
if result.kind == ObjInsDiffKind::None {
result.kind = ObjInsDiffKind::ArgMismatch;
state.diff_count += 1;
state.diff_score += if let InstructionArg::Value(
InstructionArgValue::Signed(_) | InstructionArgValue::Unsigned(_),
) = a
{
PENALTY_IMM_DIFF
} else {
PENALTY_REG_DIFF
};
if result.kind == InstructionDiffKind::None {
result.kind = InstructionDiffKind::ArgMismatch;
}
let a_str = match a {
ObjInsArg::PlainText(arg) => arg.to_string(),
ObjInsArg::Arg(arg) => arg.to_string(),
ObjInsArg::Reloc => left_ins
.reloc
.as_ref()
.map_or_else(|| "<unknown>".to_string(), |r| r.target.name.clone()),
ObjInsArg::BranchDest(arg) => arg.to_string(),
let a_str = arg_to_string(a, left_reloc.as_ref());
let a_diff = match state.left_args_idx.entry(a_str) {
btree_map::Entry::Vacant(e) => {
let idx = state.left_arg_idx;
state.left_arg_idx = idx + 1;
e.insert(idx);
idx
}
btree_map::Entry::Occupied(e) => *e.get(),
};
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 = arg_to_string(b, right_reloc.as_ref());
let b_diff = match state.right_args_idx.entry(b_str) {
btree_map::Entry::Vacant(e) => {
let idx = state.right_arg_idx;
state.right_arg_idx = idx + 1;
e.insert(idx);
idx
}
btree_map::Entry::Occupied(e) => *e.get(),
};
let b_str = match b {
ObjInsArg::PlainText(arg) => arg.to_string(),
ObjInsArg::Arg(arg) => arg.to_string(),
ObjInsArg::Reloc => right_ins
.reloc
.as_ref()
.map_or_else(|| "<unknown>".to_string(), |r| r.target.name.clone()),
ObjInsArg::BranchDest(arg) => arg.to_string(),
};
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));
result.left_args_diff.push(InstructionArgDiffIndex::new(a_diff));
result.right_args_diff.push(InstructionArgDiffIndex::new(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;
return Ok(result);
}
Ok(result)
// Compare relocations
if !reloc_eq(left_obj, right_obj, left_reloc, right_reloc, diff_config) {
state.diff_score += PENALTY_REG_DIFF;
return Ok(InstructionDiffResult::new(InstructionDiffKind::ArgMismatch));
}
Ok(InstructionDiffResult::new(InstructionDiffKind::None))
}
fn find_symbol_matching_fake_symbol_in_sections(
fake_symbol: &ObjSymbol,
sections: &[ObjSection],
) -> Option<ObjSymbol> {
let orig_section_index = fake_symbol.orig_section_index?;
let section = sections.iter().find(|s| s.orig_index == orig_section_index)?;
let real_symbol = section
.symbols
.iter()
.find(|s| s.size > 0 && (s.address..s.address + s.size).contains(&fake_symbol.address))?;
Some(real_symbol.clone())
}
// TODO
// fn find_symbol_matching_fake_symbol_in_sections(
// fake_symbol: &ObjSymbol,
// sections: &[ObjSection],
// ) -> Option<ObjSymbol> {
// let orig_section_index = fake_symbol.orig_section_index?;
// let section = sections.iter().find(|s| s.orig_index == orig_section_index)?;
// let real_symbol = section
// .symbols
// .iter()
// .find(|s| s.size > 0 && (s.address..s.address + s.size).contains(&fake_symbol.address))?;
// Some(real_symbol.clone())
// }

View File

@@ -4,119 +4,130 @@ use core::{cmp::Ordering, ops::Range};
use anyhow::{anyhow, Result};
use similar::{capture_diff_slices, get_diff_ratio, Algorithm};
use super::code::{address_eq, section_name_eq};
use crate::{
diff::{ObjDataDiff, ObjDataDiffKind, ObjDataRelocDiff, ObjSectionDiff, ObjSymbolDiff},
obj::{ObjInfo, ObjReloc, ObjSection, ObjSymbolFlags, SymbolRef},
use super::{
code::{address_eq, section_name_eq},
DataDiff, DataDiffKind, DataRelocationDiff, ObjectDiff, SectionDiff, SymbolDiff,
};
use crate::obj::{Object, Relocation, ResolvedRelocation, SymbolFlag, SymbolKind};
pub fn diff_bss_symbol(
left_obj: &ObjInfo,
right_obj: &ObjInfo,
left_symbol_ref: SymbolRef,
right_symbol_ref: SymbolRef,
) -> Result<(ObjSymbolDiff, ObjSymbolDiff)> {
let (_, left_symbol) = left_obj.section_symbol(left_symbol_ref);
let (_, right_symbol) = right_obj.section_symbol(right_symbol_ref);
left_obj: &Object,
right_obj: &Object,
left_symbol_ref: usize,
right_symbol_ref: usize,
) -> Result<(SymbolDiff, SymbolDiff)> {
let left_symbol = &left_obj.symbols[left_symbol_ref];
let right_symbol = &right_obj.symbols[right_symbol_ref];
let percent = if left_symbol.size == right_symbol.size { 100.0 } else { 50.0 };
Ok((
ObjSymbolDiff {
symbol_ref: left_symbol_ref,
SymbolDiff {
target_symbol: Some(right_symbol_ref),
instructions: vec![],
match_percent: Some(percent),
diff_score: None,
instruction_rows: vec![],
},
ObjSymbolDiff {
symbol_ref: right_symbol_ref,
SymbolDiff {
target_symbol: Some(left_symbol_ref),
instructions: vec![],
match_percent: Some(percent),
diff_score: None,
instruction_rows: vec![],
},
))
}
pub fn no_diff_symbol(_obj: &ObjInfo, symbol_ref: SymbolRef) -> ObjSymbolDiff {
ObjSymbolDiff { symbol_ref, target_symbol: None, instructions: vec![], match_percent: None }
}
fn reloc_eq(left_obj: &ObjInfo, right_obj: &ObjInfo, left: &ObjReloc, right: &ObjReloc) -> bool {
if left.flags != right.flags {
fn reloc_eq(
left_obj: &Object,
right_obj: &Object,
left: &ResolvedRelocation,
right: &ResolvedRelocation,
) -> bool {
if left.relocation.flags != right.relocation.flags {
return false;
}
let symbol_name_addend_matches =
left.target.name == right.target.name && left.addend == right.addend;
match (&left.target.orig_section_index, &right.target.orig_section_index) {
left.symbol.name == right.symbol.name && left.relocation.addend == right.relocation.addend;
match (left.symbol.section, right.symbol.section) {
(Some(sl), Some(sr)) => {
// Match if section and name+addend or address match
section_name_eq(left_obj, right_obj, *sl, *sr)
section_name_eq(left_obj, right_obj, sl, sr)
&& (symbol_name_addend_matches || address_eq(left, right))
}
(Some(_), None) => false,
(None, Some(_)) => {
// Match if possibly stripped weak symbol
symbol_name_addend_matches && right.target.flags.0.contains(ObjSymbolFlags::Weak)
symbol_name_addend_matches && right.symbol.flags.contains(SymbolFlag::Weak)
}
(None, None) => symbol_name_addend_matches,
}
}
#[inline]
fn resolve_relocation<'obj>(
obj: &'obj Object,
reloc: &'obj Relocation,
) -> ResolvedRelocation<'obj> {
let symbol = &obj.symbols[reloc.target_symbol];
ResolvedRelocation { relocation: reloc, symbol }
}
/// Compares relocations contained with a certain data range.
/// The ObjDataDiffKind for each diff will either be `None`` (if the relocation matches),
/// The DataDiffKind for each diff will either be `None`` (if the relocation matches),
/// or `Replace` (if a relocation was changed, added, or removed).
/// `Insert` and `Delete` are not used when a relocation is added or removed to avoid confusing diffs
/// where it looks like the bytes themselves were changed but actually only the relocations changed.
fn diff_data_relocs_for_range(
left_obj: &ObjInfo,
right_obj: &ObjInfo,
left: &ObjSection,
right: &ObjSection,
fn diff_data_relocs_for_range<'left, 'right>(
left_obj: &'left Object,
right_obj: &'right Object,
left_section_idx: usize,
right_section_idx: usize,
left_range: Range<usize>,
right_range: Range<usize>,
) -> Vec<(ObjDataDiffKind, Option<ObjReloc>, Option<ObjReloc>)> {
) -> Vec<(DataDiffKind, Option<ResolvedRelocation<'left>>, Option<ResolvedRelocation<'right>>)> {
let left_section = &left_obj.sections[left_section_idx];
let right_section = &right_obj.sections[right_section_idx];
let mut diffs = Vec::new();
for left_reloc in left.relocations.iter() {
for left_reloc in left_section.relocations.iter() {
if !left_range.contains(&(left_reloc.address as usize)) {
continue;
}
let left_offset = left_reloc.address as usize - left_range.start;
let Some(right_reloc) = right.relocations.iter().find(|r| {
let left_reloc = resolve_relocation(left_obj, left_reloc);
let Some(right_reloc) = right_section.relocations.iter().find(|r| {
if !right_range.contains(&(r.address as usize)) {
return false;
}
let right_offset = r.address as usize - right_range.start;
right_offset == left_offset
}) else {
diffs.push((ObjDataDiffKind::Delete, Some(left_reloc.clone()), None));
diffs.push((DataDiffKind::Delete, Some(left_reloc), None));
continue;
};
if reloc_eq(left_obj, right_obj, left_reloc, right_reloc) {
diffs.push((
ObjDataDiffKind::None,
Some(left_reloc.clone()),
Some(right_reloc.clone()),
));
let right_reloc = resolve_relocation(right_obj, right_reloc);
if reloc_eq(left_obj, right_obj, &left_reloc, &right_reloc) {
diffs.push((DataDiffKind::None, Some(left_reloc), Some(right_reloc)));
} else {
diffs.push((
ObjDataDiffKind::Replace,
Some(left_reloc.clone()),
Some(right_reloc.clone()),
DataDiffKind::Replace,
Some(left_reloc),
Some(right_reloc),
));
}
}
for right_reloc in right.relocations.iter() {
for right_reloc in right_section.relocations.iter() {
if !right_range.contains(&(right_reloc.address as usize)) {
continue;
}
let right_offset = right_reloc.address as usize - right_range.start;
let Some(_) = left.relocations.iter().find(|r| {
let right_reloc = resolve_relocation(right_obj, right_reloc);
let Some(_) = left_section.relocations.iter().find(|r| {
if !left_range.contains(&(r.address as usize)) {
return false;
}
let left_offset = r.address as usize - left_range.start;
left_offset == right_offset
}) else {
diffs.push((ObjDataDiffKind::Insert, None, Some(right_reloc.clone())));
diffs.push((DataDiffKind::Insert, None, Some(right_reloc)));
continue;
};
// No need to check the cases for relocations being deleted or matching again.
@@ -127,81 +138,103 @@ fn diff_data_relocs_for_range(
/// Compare the data sections of two object files.
pub fn diff_data_section(
left_obj: &ObjInfo,
right_obj: &ObjInfo,
left: &ObjSection,
right: &ObjSection,
left_section_diff: &ObjSectionDiff,
right_section_diff: &ObjSectionDiff,
) -> Result<(ObjSectionDiff, ObjSectionDiff)> {
let left_max =
left.symbols.iter().map(|s| s.section_address + s.size).max().unwrap_or(0).min(left.size);
let right_max =
right.symbols.iter().map(|s| s.section_address + s.size).max().unwrap_or(0).min(right.size);
let left_data = &left.data[..left_max as usize];
let right_data = &right.data[..right_max as usize];
left_obj: &Object,
right_obj: &Object,
left_diff: &ObjectDiff,
right_diff: &ObjectDiff,
left_section_idx: usize,
right_section_idx: usize,
) -> Result<(SectionDiff, SectionDiff)> {
let left_section = &left_obj.sections[left_section_idx];
let right_section = &right_obj.sections[right_section_idx];
let left_max = left_obj
.symbols
.iter()
.filter_map(|s| {
if s.section != Some(left_section_idx) || s.kind == SymbolKind::Section {
return None;
}
s.address.checked_sub(left_section.address).map(|a| a + s.size)
})
.max()
.unwrap_or(0)
.min(left_section.size);
let right_max = right_obj
.symbols
.iter()
.filter_map(|s| {
if s.section != Some(right_section_idx) || s.kind == SymbolKind::Section {
return None;
}
s.address.checked_sub(right_section.address).map(|a| a + s.size)
})
.max()
.unwrap_or(0)
.min(right_section.size);
let left_data = &left_section.data[..left_max as usize];
let right_data = &right_section.data[..right_max as usize];
let ops = capture_diff_slices(Algorithm::Patience, left_data, right_data);
let match_percent = get_diff_ratio(&ops, left_data.len(), right_data.len()) * 100.0;
let mut left_diff = Vec::<ObjDataDiff>::new();
let mut right_diff = Vec::<ObjDataDiff>::new();
let mut left_data_diff = Vec::<DataDiff>::new();
let mut right_data_diff = Vec::<DataDiff>::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 = left_len.max(right_len);
let kind = match tag {
similar::DiffTag::Equal => ObjDataDiffKind::None,
similar::DiffTag::Delete => ObjDataDiffKind::Delete,
similar::DiffTag::Insert => ObjDataDiffKind::Insert,
similar::DiffTag::Equal => DataDiffKind::None,
similar::DiffTag::Delete => DataDiffKind::Delete,
similar::DiffTag::Insert => DataDiffKind::Insert,
similar::DiffTag::Replace => {
// Ensure replacements are equal length
len = left_len.min(right_len);
ObjDataDiffKind::Replace
DataDiffKind::Replace
}
};
let left_data = &left.data[left_range];
let right_data = &right.data[right_range];
left_diff.push(ObjDataDiff {
let left_data = &left_section.data[left_range];
let right_data = &right_section.data[right_range];
left_data_diff.push(DataDiff {
data: left_data[..len.min(left_data.len())].to_vec(),
kind,
len,
..Default::default()
});
right_diff.push(ObjDataDiff {
right_data_diff.push(DataDiff {
data: right_data[..len.min(right_data.len())].to_vec(),
kind,
len,
..Default::default()
});
if kind == ObjDataDiffKind::Replace {
if kind == DataDiffKind::Replace {
match left_len.cmp(&right_len) {
Ordering::Less => {
let len = right_len - left_len;
left_diff.push(ObjDataDiff {
left_data_diff.push(DataDiff {
data: vec![],
kind: ObjDataDiffKind::Insert,
kind: DataDiffKind::Insert,
len,
..Default::default()
});
right_diff.push(ObjDataDiff {
right_data_diff.push(DataDiff {
data: right_data[left_len..right_len].to_vec(),
kind: ObjDataDiffKind::Insert,
kind: DataDiffKind::Insert,
len,
..Default::default()
});
}
Ordering::Greater => {
let len = left_len - right_len;
left_diff.push(ObjDataDiff {
left_data_diff.push(DataDiff {
data: left_data[right_len..left_len].to_vec(),
kind: ObjDataDiffKind::Delete,
kind: DataDiffKind::Delete,
len,
..Default::default()
});
right_diff.push(ObjDataDiff {
right_data_diff.push(DataDiff {
data: vec![],
kind: ObjDataDiffKind::Delete,
kind: DataDiffKind::Delete,
len,
..Default::default()
});
@@ -216,28 +249,36 @@ pub fn diff_data_section(
for (diff_kind, left_reloc, right_reloc) in diff_data_relocs_for_range(
left_obj,
right_obj,
left,
right,
left_section_idx,
right_section_idx,
0..left_max as usize,
0..right_max as usize,
) {
if let Some(left_reloc) = left_reloc {
let len = left_obj.arch.get_reloc_byte_size(left_reloc.flags);
let range = left_reloc.address as usize..left_reloc.address as usize + len;
left_reloc_diffs.push(ObjDataRelocDiff { reloc: left_reloc, kind: diff_kind, range });
let len = left_obj.arch.get_reloc_byte_size(left_reloc.relocation.flags);
let range = left_reloc.relocation.address as usize
..left_reloc.relocation.address as usize + len;
left_reloc_diffs.push(DataRelocationDiff { kind: diff_kind, range });
}
if let Some(right_reloc) = right_reloc {
let len = right_obj.arch.get_reloc_byte_size(right_reloc.flags);
let range = right_reloc.address as usize..right_reloc.address as usize + len;
right_reloc_diffs.push(ObjDataRelocDiff { reloc: right_reloc, kind: diff_kind, range });
let len = right_obj.arch.get_reloc_byte_size(right_reloc.relocation.flags);
let range = right_reloc.relocation.address as usize
..right_reloc.relocation.address as usize + len;
right_reloc_diffs.push(DataRelocationDiff { kind: diff_kind, range });
}
}
let (mut left_section_diff, mut right_section_diff) =
diff_generic_section(left, right, left_section_diff, right_section_diff)?;
let all_left_relocs_match = left_reloc_diffs.iter().all(|d| d.kind == ObjDataDiffKind::None);
left_section_diff.data_diff = left_diff;
right_section_diff.data_diff = right_diff;
let (mut left_section_diff, mut right_section_diff) = diff_generic_section(
left_obj,
right_obj,
left_diff,
right_diff,
left_section_idx,
right_section_idx,
)?;
let all_left_relocs_match = left_reloc_diffs.iter().all(|d| d.kind == DataDiffKind::None);
left_section_diff.data_diff = left_data_diff;
right_section_diff.data_diff = right_data_diff;
left_section_diff.reloc_diff = left_reloc_diffs;
right_section_diff.reloc_diff = right_reloc_diffs;
if all_left_relocs_match {
@@ -254,29 +295,58 @@ pub fn diff_data_section(
}
pub fn diff_data_symbol(
left_obj: &ObjInfo,
right_obj: &ObjInfo,
left_symbol_ref: SymbolRef,
right_symbol_ref: SymbolRef,
) -> Result<(ObjSymbolDiff, ObjSymbolDiff)> {
let (left_section, left_symbol) = left_obj.section_symbol(left_symbol_ref);
let (right_section, right_symbol) = right_obj.section_symbol(right_symbol_ref);
left_obj: &Object,
right_obj: &Object,
left_symbol_idx: usize,
right_symbol_idx: usize,
) -> Result<(SymbolDiff, SymbolDiff)> {
let left_symbol = &left_obj.symbols[left_symbol_idx];
let right_symbol = &right_obj.symbols[right_symbol_idx];
let left_section = left_section.ok_or_else(|| anyhow!("Data symbol section not found"))?;
let right_section = right_section.ok_or_else(|| anyhow!("Data symbol section not found"))?;
let left_section_idx =
left_symbol.section.ok_or_else(|| anyhow!("Data symbol section not found"))?;
let right_section_idx =
right_symbol.section.ok_or_else(|| anyhow!("Data symbol section not found"))?;
let left_range = left_symbol.section_address as usize
..(left_symbol.section_address + left_symbol.size) as usize;
let right_range = right_symbol.section_address as usize
..(right_symbol.section_address + right_symbol.size) as usize;
let left_section = &left_obj.sections[left_section_idx];
let right_section = &right_obj.sections[right_section_idx];
let left_start = left_symbol
.address
.checked_sub(left_section.address)
.ok_or_else(|| anyhow!("Symbol address out of section bounds"))?;
let right_start = right_symbol
.address
.checked_sub(right_section.address)
.ok_or_else(|| anyhow!("Symbol address out of section bounds"))?;
let left_end = left_start + left_symbol.size;
if left_end > left_section.size {
return Err(anyhow!(
"Symbol {} size out of section bounds ({} > {})",
left_symbol.name,
left_end,
left_section.size
));
}
let right_end = right_start + right_symbol.size;
if right_end > right_section.size {
return Err(anyhow!(
"Symbol {} size out of section bounds ({} > {})",
right_symbol.name,
right_end,
right_section.size
));
}
let left_range = left_start as usize..left_end as usize;
let right_range = right_start as usize..right_end as usize;
let left_data = &left_section.data[left_range.clone()];
let right_data = &right_section.data[right_range.clone()];
let reloc_diffs = diff_data_relocs_for_range(
left_obj,
right_obj,
left_section,
right_section,
left_section_idx,
right_section_idx,
left_range,
right_range,
);
@@ -291,11 +361,15 @@ pub fn diff_data_symbol(
for (diff_kind, left_reloc, right_reloc) in reloc_diffs {
let reloc_diff_len = match (left_reloc, right_reloc) {
(None, None) => unreachable!(),
(None, Some(right_reloc)) => right_obj.arch.get_reloc_byte_size(right_reloc.flags),
(Some(left_reloc), _) => left_obj.arch.get_reloc_byte_size(left_reloc.flags),
(None, Some(right_reloc)) => {
right_obj.arch.get_reloc_byte_size(right_reloc.relocation.flags)
}
(Some(left_reloc), _) => {
left_obj.arch.get_reloc_byte_size(left_reloc.relocation.flags)
}
};
total_reloc_bytes += reloc_diff_len;
if diff_kind == ObjDataDiffKind::None {
if diff_kind == DataDiffKind::None {
matching_reloc_bytes += reloc_diff_len;
}
}
@@ -315,17 +389,17 @@ pub fn diff_data_symbol(
let match_percent = match_ratio * 100.0;
Ok((
ObjSymbolDiff {
symbol_ref: left_symbol_ref,
target_symbol: Some(right_symbol_ref),
instructions: vec![],
SymbolDiff {
target_symbol: Some(right_symbol_idx),
match_percent: Some(match_percent),
diff_score: None,
instruction_rows: vec![],
},
ObjSymbolDiff {
symbol_ref: right_symbol_ref,
target_symbol: Some(left_symbol_ref),
instructions: vec![],
SymbolDiff {
target_symbol: Some(left_symbol_idx),
match_percent: Some(match_percent),
diff_score: None,
instruction_rows: vec![],
},
))
}
@@ -333,69 +407,89 @@ pub fn diff_data_symbol(
/// Compares a section of two object files.
/// This essentially adds up the match percentage of each symbol in the section.
pub fn diff_generic_section(
left: &ObjSection,
_right: &ObjSection,
left_diff: &ObjSectionDiff,
_right_diff: &ObjSectionDiff,
) -> Result<(ObjSectionDiff, ObjSectionDiff)> {
let match_percent = if left_diff.symbols.iter().all(|d| d.match_percent == Some(100.0)) {
left_obj: &Object,
_right_obj: &Object,
left_diff: &ObjectDiff,
_right_diff: &ObjectDiff,
left_section_idx: usize,
_right_section_idx: usize,
) -> Result<(SectionDiff, SectionDiff)> {
let match_percent = if left_obj
.symbols
.iter()
.enumerate()
.filter(|(_, s)| s.section == Some(left_section_idx) && s.kind != SymbolKind::Section)
.map(|(i, _)| &left_diff.symbols[i])
.all(|d| d.match_percent == Some(100.0))
{
100.0 // Avoid fp precision issues
} else {
left.symbols
let (matched, total) = left_obj
.symbols
.iter()
.zip(left_diff.symbols.iter())
.map(|(s, d)| d.match_percent.unwrap_or(0.0) * s.size as f32)
.sum::<f32>()
/ left.size as f32
.enumerate()
.filter(|(_, s)| s.section == Some(left_section_idx) && s.kind != SymbolKind::Section)
.map(|(i, s)| (s, &left_diff.symbols[i]))
.fold((0.0, 0.0), |(matched, total), (s, d)| {
(matched + d.match_percent.unwrap_or(0.0) * s.size as f32, total + s.size as f32)
});
if total == 0.0 {
100.0
} else {
matched / total
}
};
Ok((
ObjSectionDiff {
symbols: vec![],
data_diff: vec![],
reloc_diff: vec![],
match_percent: Some(match_percent),
},
ObjSectionDiff {
symbols: vec![],
data_diff: vec![],
reloc_diff: vec![],
match_percent: Some(match_percent),
},
SectionDiff { match_percent: Some(match_percent), data_diff: vec![], reloc_diff: vec![] },
SectionDiff { match_percent: Some(match_percent), data_diff: vec![], reloc_diff: vec![] },
))
}
/// Compare the addresses and sizes of each symbol in the BSS sections.
pub fn diff_bss_section(
left: &ObjSection,
right: &ObjSection,
left_diff: &ObjSectionDiff,
right_diff: &ObjSectionDiff,
) -> Result<(ObjSectionDiff, ObjSectionDiff)> {
let left_sizes = left.symbols.iter().map(|s| (s.section_address, s.size)).collect::<Vec<_>>();
let right_sizes = right.symbols.iter().map(|s| (s.section_address, s.size)).collect::<Vec<_>>();
left_obj: &Object,
right_obj: &Object,
left_diff: &ObjectDiff,
right_diff: &ObjectDiff,
left_section_idx: usize,
right_section_idx: usize,
) -> Result<(SectionDiff, SectionDiff)> {
let left_section = &left_obj.sections[left_section_idx];
let left_sizes = left_obj
.symbols
.iter()
.enumerate()
.filter(|(_, s)| s.section == Some(left_section_idx) && s.kind != SymbolKind::Section)
.filter_map(|(_, s)| s.address.checked_sub(left_section.address).map(|a| (a, s.size)))
.collect::<Vec<_>>();
let right_section = &right_obj.sections[right_section_idx];
let right_sizes = right_obj
.symbols
.iter()
.enumerate()
.filter(|(_, s)| s.section == Some(right_section_idx) && s.kind != SymbolKind::Section)
.filter_map(|(_, s)| s.address.checked_sub(right_section.address).map(|a| (a, s.size)))
.collect::<Vec<_>>();
let ops = capture_diff_slices(Algorithm::Patience, &left_sizes, &right_sizes);
let mut match_percent = get_diff_ratio(&ops, left_sizes.len(), right_sizes.len()) * 100.0;
// Use the highest match percent between two options:
// - Left symbols matching right symbols by name
// - Diff of the addresses and sizes of each symbol
let (generic_diff, _) = diff_generic_section(left, right, left_diff, right_diff)?;
let (generic_diff, _) = diff_generic_section(
left_obj,
right_obj,
left_diff,
right_diff,
left_section_idx,
right_section_idx,
)?;
if generic_diff.match_percent.unwrap_or(-1.0) > match_percent {
match_percent = generic_diff.match_percent.unwrap();
}
Ok((
ObjSectionDiff {
symbols: vec![],
data_diff: vec![],
reloc_diff: vec![],
match_percent: Some(match_percent),
},
ObjSectionDiff {
symbols: vec![],
data_diff: vec![],
reloc_diff: vec![],
match_percent: Some(match_percent),
},
SectionDiff { match_percent: Some(match_percent), data_diff: vec![], reloc_diff: vec![] },
SectionDiff { match_percent: Some(match_percent), data_diff: vec![], reloc_diff: vec![] },
))
}

View File

@@ -1,16 +1,28 @@
use alloc::string::{String, ToString};
use alloc::{
borrow::Cow,
collections::BTreeSet,
format,
string::{String, ToString},
vec::Vec,
};
use core::cmp::Ordering;
use anyhow::Result;
use itertools::Itertools;
use regex::Regex;
use crate::{
diff::{ObjInsArgDiff, ObjInsDiff},
obj::{ObjInsArg, ObjInsArgValue, ObjReloc, ObjSymbol},
diff::{DiffObjConfig, InstructionArgDiffIndex, InstructionDiffRow, ObjectDiff, SymbolDiff},
obj::{
InstructionArg, InstructionArgValue, Object, SectionFlag, SectionKind, Symbol, SymbolFlag,
SymbolKind,
},
};
#[derive(Debug, Copy, Clone)]
pub enum DiffText<'a> {
/// Basic text
Basic(&'a str),
/// Colored text
BasicColor(&'a str, usize),
/// Line number
Line(u32),
/// Instruction address
@@ -18,13 +30,13 @@ pub enum DiffText<'a> {
/// Instruction mnemonic
Opcode(&'a str, u16),
/// Instruction argument
Argument(&'a ObjInsArgValue, Option<&'a ObjInsArgDiff>),
Argument(&'a InstructionArgValue),
/// Branch destination
BranchDest(u64, Option<&'a ObjInsArgDiff>),
BranchDest(u64),
/// Symbol name
Symbol(&'a ObjSymbol, Option<&'a ObjInsArgDiff>),
Symbol(&'a Symbol),
/// Relocation addend
Addend(i64, Option<&'a ObjInsArgDiff>),
Addend(i64),
/// Number of spaces
Spacing(usize),
/// End of line
@@ -36,83 +48,113 @@ pub enum HighlightKind {
#[default]
None,
Opcode(u16),
Arg(ObjInsArgValue),
Argument(InstructionArgValue),
Symbol(String),
Address(u64),
}
pub fn display_diff<E>(
ins_diff: &ObjInsDiff,
base_addr: u64,
mut cb: impl FnMut(DiffText) -> Result<(), E>,
) -> Result<(), E> {
let Some(ins) = &ins_diff.ins else {
cb(DiffText::Eol)?;
return Ok(());
};
if let Some(line) = ins.line {
cb(DiffText::Line(line))?;
}
cb(DiffText::Address(ins.address - base_addr))?;
if let Some(branch) = &ins_diff.branch_from {
cb(DiffText::BasicColor(" ~> ", branch.branch_idx))?;
} else {
cb(DiffText::Spacing(4))?;
}
cb(DiffText::Opcode(&ins.mnemonic, ins.op))?;
let mut arg_diff_idx = 0; // non-PlainText index
for (i, arg) in ins.args.iter().enumerate() {
if i == 0 {
cb(DiffText::Spacing(1))?;
}
let diff = ins_diff.arg_diff.get(arg_diff_idx).and_then(|o| o.as_ref());
match arg {
ObjInsArg::PlainText(s) => {
cb(DiffText::Basic(s))?;
}
ObjInsArg::Arg(v) => {
cb(DiffText::Argument(v, diff))?;
arg_diff_idx += 1;
}
ObjInsArg::Reloc => {
display_reloc_name(ins.reloc.as_ref().unwrap(), &mut cb, diff)?;
arg_diff_idx += 1;
}
ObjInsArg::BranchDest(dest) => {
if let Some(dest) = dest.checked_sub(base_addr) {
cb(DiffText::BranchDest(dest, diff))?;
} else {
cb(DiffText::Basic("<unknown>"))?;
}
arg_diff_idx += 1;
}
}
}
if let Some(branch) = &ins_diff.branch_to {
cb(DiffText::BasicColor(" ~>", branch.branch_idx))?;
}
cb(DiffText::Eol)?;
Ok(())
pub enum InstructionPart {
Basic(&'static str),
Opcode(Cow<'static, str>, u16),
Arg(InstructionArg),
Separator,
}
fn display_reloc_name<E>(
reloc: &ObjReloc,
mut cb: impl FnMut(DiffText) -> Result<(), E>,
diff: Option<&ObjInsArgDiff>,
) -> Result<(), E> {
cb(DiffText::Symbol(&reloc.target, diff))?;
cb(DiffText::Addend(reloc.addend, diff))
pub fn display_row(
obj: &Object,
symbol_index: usize,
ins_row: &InstructionDiffRow,
diff_config: &DiffObjConfig,
mut cb: impl FnMut(DiffText, InstructionArgDiffIndex) -> Result<()>,
) -> Result<()> {
let Some(ins_ref) = ins_row.ins_ref else {
cb(DiffText::Eol, InstructionArgDiffIndex::NONE)?;
return Ok(());
};
let symbol = &obj.symbols[symbol_index];
let Some(section_index) = symbol.section else {
cb(DiffText::Eol, InstructionArgDiffIndex::NONE)?;
return Ok(());
};
let section = &obj.sections[section_index];
let Some(data) = section.data_range(ins_ref.address, ins_ref.size as usize) else {
cb(DiffText::Eol, InstructionArgDiffIndex::NONE)?;
return Ok(());
};
if let Some(line) = section.line_info.range(..=ins_ref.address).last().map(|(_, &b)| b) {
cb(DiffText::Line(line), InstructionArgDiffIndex::NONE)?;
}
cb(
DiffText::Address(ins_ref.address.saturating_sub(symbol.address)),
InstructionArgDiffIndex::NONE,
)?;
if let Some(branch) = &ins_row.branch_from {
cb(DiffText::Basic(" ~> "), InstructionArgDiffIndex::new(branch.branch_idx))?;
} else {
cb(DiffText::Spacing(4), InstructionArgDiffIndex::NONE)?;
}
let mut arg_idx = 0;
let relocation = section.relocation_at(ins_ref.address, obj);
obj.arch.display_instruction(
ins_ref,
data,
relocation,
symbol.address..symbol.address + symbol.size,
section_index,
diff_config,
&mut |part| match part {
InstructionPart::Basic(text) => {
cb(DiffText::Basic(text), InstructionArgDiffIndex::NONE)
}
InstructionPart::Opcode(mnemonic, opcode) => {
cb(DiffText::Opcode(mnemonic.as_ref(), opcode), InstructionArgDiffIndex::NONE)
}
InstructionPart::Arg(arg) => {
let diff_index = ins_row.arg_diff.get(arg_idx).copied().unwrap_or_default();
arg_idx += 1;
match arg {
InstructionArg::Value(ref value) => cb(DiffText::Argument(value), diff_index),
InstructionArg::Reloc => {
let resolved = relocation.unwrap();
cb(DiffText::Symbol(resolved.symbol), diff_index)?;
if resolved.relocation.addend != 0 {
cb(DiffText::Addend(resolved.relocation.addend), diff_index)?;
}
Ok(())
}
InstructionArg::BranchDest(dest) => {
if let Some(addr) = dest.checked_sub(symbol.address) {
cb(DiffText::BranchDest(addr), diff_index)
} else {
cb(
DiffText::Argument(&InstructionArgValue::Opaque(Cow::Borrowed(
"<invalid>",
))),
diff_index,
)
}
}
}
}
InstructionPart::Separator => {
cb(DiffText::Basic(diff_config.separator()), InstructionArgDiffIndex::NONE)
}
},
)?;
if let Some(branch) = &ins_row.branch_to {
cb(DiffText::Basic(" ~>"), InstructionArgDiffIndex::new(branch.branch_idx))?;
}
cb(DiffText::Eol, InstructionArgDiffIndex::NONE)?;
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::BranchDest(b, _)) => {
a == b
}
(HighlightKind::Argument(a), DiffText::Argument(b)) => a.loose_eq(b),
(HighlightKind::Symbol(a), DiffText::Symbol(b)) => a == &b.name,
(HighlightKind::Address(a), DiffText::Address(b) | DiffText::BranchDest(b)) => a == b,
_ => false,
}
}
@@ -126,10 +168,245 @@ impl From<DiffText<'_>> for HighlightKind {
fn from(value: DiffText<'_>) -> Self {
match value {
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::BranchDest(addr, _) => HighlightKind::Address(addr),
DiffText::Argument(arg) => HighlightKind::Argument(arg.clone()),
DiffText::Symbol(sym) => HighlightKind::Symbol(sym.name.to_string()),
DiffText::Address(addr) | DiffText::BranchDest(addr) => HighlightKind::Address(addr),
_ => HighlightKind::None,
}
}
}
pub enum ContextMenuItem {
Copy { value: String, label: Option<String> },
Navigate { label: String },
}
pub enum HoverItemColor {
Normal, // Gray
Emphasized, // White
Special, // Blue
}
pub struct HoverItem {
pub text: String,
pub color: HoverItemColor,
}
pub fn symbol_context(_obj: &Object, symbol: &Symbol) -> Vec<ContextMenuItem> {
let mut out = Vec::new();
if let Some(name) = &symbol.demangled_name {
out.push(ContextMenuItem::Copy { value: name.clone(), label: None });
}
out.push(ContextMenuItem::Copy { value: symbol.name.clone(), label: None });
if let Some(address) = symbol.virtual_address {
out.push(ContextMenuItem::Copy {
value: format!("{:#x}", address),
label: Some("virtual address".to_string()),
});
}
// if let Some(_extab) = obj.arch.ppc().and_then(|ppc| ppc.extab_for_symbol(symbol)) {
// out.push(ContextMenuItem::Navigate { label: "Decode exception table".to_string() });
// }
out
}
pub fn symbol_hover(_obj: &Object, symbol: &Symbol) -> Vec<HoverItem> {
let mut out = Vec::new();
out.push(HoverItem {
text: format!("Name: {}", symbol.name),
color: HoverItemColor::Emphasized,
});
out.push(HoverItem {
text: format!("Address: {:x}", symbol.address),
color: HoverItemColor::Emphasized,
});
if symbol.flags.contains(SymbolFlag::SizeInferred) {
out.push(HoverItem {
text: format!("Size: {:x} (inferred)", symbol.size),
color: HoverItemColor::Emphasized,
});
} else {
out.push(HoverItem {
text: format!("Size: {:x}", symbol.size),
color: HoverItemColor::Emphasized,
});
}
if let Some(address) = symbol.virtual_address {
out.push(HoverItem {
text: format!("Virtual address: {:#x}", address),
color: HoverItemColor::Special,
});
}
// if let Some(extab) = obj.arch.ppc().and_then(|ppc| ppc.extab_for_symbol(symbol)) {
// out.push(HoverItem {
// text: format!("extab symbol: {}", extab.etb_symbol.name),
// color: HoverItemColor::Special,
// });
// out.push(HoverItem {
// text: format!("extabindex symbol: {}", extab.eti_symbol.name),
// color: HoverItemColor::Special,
// });
// }
out
}
#[derive(Copy, Clone)]
pub enum SymbolFilter<'a> {
None,
Search(&'a Regex),
Mapping(usize, Option<&'a Regex>),
}
fn symbol_matches_filter(
symbol: &Symbol,
diff: &SymbolDiff,
filter: SymbolFilter<'_>,
show_hidden_symbols: bool,
) -> bool {
// Ignore absolute symbols
if symbol.section.is_none() && !symbol.flags.contains(SymbolFlag::Common) {
return false;
}
if !show_hidden_symbols && symbol.flags.contains(SymbolFlag::Hidden) {
return false;
}
match filter {
SymbolFilter::None => true,
SymbolFilter::Search(regex) => {
regex.is_match(&symbol.name)
|| symbol.demangled_name.as_deref().is_some_and(|s| regex.is_match(s))
}
SymbolFilter::Mapping(symbol_ref, regex) => {
diff.target_symbol == Some(symbol_ref)
&& regex.is_none_or(|r| {
r.is_match(&symbol.name)
|| symbol.demangled_name.as_deref().is_some_and(|s| r.is_match(s))
})
}
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct SectionDisplaySymbol {
pub symbol: usize,
pub is_mapping_symbol: bool,
}
#[derive(Debug, Clone)]
pub struct SectionDisplay {
pub id: String,
pub name: String,
pub size: u64,
pub match_percent: Option<f32>,
pub symbols: Vec<SectionDisplaySymbol>,
}
pub fn display_sections(
obj: &Object,
diff: &ObjectDiff,
filter: SymbolFilter<'_>,
show_hidden_symbols: bool,
show_mapped_symbols: bool,
reverse_fn_order: bool,
) -> Vec<SectionDisplay> {
let mut mapping = BTreeSet::new();
let is_mapping_symbol = if let SymbolFilter::Mapping(_, _) = filter {
for mapping_diff in &diff.mapping_symbols {
let symbol = &obj.symbols[mapping_diff.symbol_index];
if !symbol_matches_filter(
symbol,
&mapping_diff.symbol_diff,
filter,
show_hidden_symbols,
) {
continue;
}
if !show_mapped_symbols {
let symbol_diff = &diff.symbols[mapping_diff.symbol_index];
if symbol_diff.target_symbol.is_some() {
continue;
}
}
mapping.insert((symbol.section, mapping_diff.symbol_index));
}
true
} else {
for (symbol_idx, (symbol, symbol_diff)) in obj.symbols.iter().zip(&diff.symbols).enumerate()
{
if !symbol_matches_filter(symbol, symbol_diff, filter, show_hidden_symbols) {
continue;
}
mapping.insert((symbol.section, symbol_idx));
}
false
};
let num_sections = mapping.iter().map(|(section_idx, _)| *section_idx).dedup().count();
let mut sections = Vec::with_capacity(num_sections);
for (section_idx, group) in &mapping.iter().chunk_by(|(section_idx, _)| *section_idx) {
let mut symbols = group
.map(|&(_, symbol)| SectionDisplaySymbol { symbol, is_mapping_symbol })
.collect::<Vec<_>>();
if let Some(section_idx) = section_idx {
let section = &obj.sections[section_idx];
if section.kind == SectionKind::Unknown || section.flags.contains(SectionFlag::Hidden) {
// Skip unknown and hidden sections
continue;
}
let section_diff = &diff.sections[section_idx];
if section.kind == SectionKind::Code && reverse_fn_order {
symbols.sort_by(|a, b| {
let a_symbol = &obj.symbols[a.symbol];
let b_symbol = &obj.symbols[b.symbol];
symbol_sort_reverse(a_symbol, b_symbol)
});
} else {
symbols.sort_by(|a, b| {
let a_symbol = &obj.symbols[a.symbol];
let b_symbol = &obj.symbols[b.symbol];
symbol_sort(a_symbol, b_symbol)
});
}
sections.push(SectionDisplay {
id: section.id.clone(),
name: if section.flags.contains(SectionFlag::Combined) {
format!("{} [combined]", section.name)
} else {
section.name.clone()
},
size: section.size,
match_percent: section_diff.match_percent,
symbols,
});
} else {
// Don't sort, preserve order of absolute symbols
sections.push(SectionDisplay {
id: ".comm".to_string(),
name: ".comm".to_string(),
size: 0,
match_percent: None,
symbols,
});
}
}
sections.sort_by(|a, b| a.id.cmp(&b.id));
sections
}
fn section_symbol_sort(a: &Symbol, b: &Symbol) -> Ordering {
if a.kind == SymbolKind::Section {
if b.kind != SymbolKind::Section {
return Ordering::Less;
}
} else if b.kind == SymbolKind::Section {
return Ordering::Greater;
}
Ordering::Equal
}
fn symbol_sort(a: &Symbol, b: &Symbol) -> Ordering {
section_symbol_sort(a, b).then(a.address.cmp(&b.address)).then(a.size.cmp(&b.size))
}
fn symbol_sort_reverse(a: &Symbol, b: &Symbol) -> Ordering {
section_symbol_sort(a, b).then(b.address.cmp(&a.address)).then(b.size.cmp(&a.size))
}

View File

@@ -4,21 +4,19 @@ use alloc::{
vec,
vec::Vec,
};
use core::ops::Range;
use core::{num::NonZeroU32, ops::Range};
use anyhow::Result;
use crate::{
diff::{
code::{diff_code, no_diff_code, process_code_symbol},
code::{diff_code, no_diff_code},
data::{
diff_bss_section, diff_bss_symbol, diff_data_section, diff_data_symbol,
diff_generic_section, no_diff_symbol,
diff_generic_section,
},
},
obj::{
ObjInfo, ObjIns, ObjReloc, ObjSection, ObjSectionKind, ObjSymbol, SymbolRef, SECTION_COMMON,
},
obj::{InstructionRef, Object, SectionKind, Symbol, SymbolFlag},
};
pub mod code;
@@ -38,47 +36,44 @@ impl DiffObjConfig {
}
#[derive(Debug, Clone)]
pub struct ObjSectionDiff {
pub symbols: Vec<ObjSymbolDiff>,
pub data_diff: Vec<ObjDataDiff>,
pub reloc_diff: Vec<ObjDataRelocDiff>,
pub struct SectionDiff {
// pub target_section: Option<usize>,
pub match_percent: Option<f32>,
}
impl ObjSectionDiff {
fn merge(&mut self, other: ObjSectionDiff) {
// symbols ignored
self.data_diff = other.data_diff;
self.reloc_diff = other.reloc_diff;
self.match_percent = other.match_percent;
}
pub data_diff: Vec<DataDiff>,
pub reloc_diff: Vec<DataRelocationDiff>,
}
#[derive(Debug, Clone, Default)]
pub struct ObjSymbolDiff {
/// The symbol ref this object
pub symbol_ref: SymbolRef,
/// The symbol ref in the _other_ object that this symbol was diffed against
pub target_symbol: Option<SymbolRef>,
pub instructions: Vec<ObjInsDiff>,
pub struct SymbolDiff {
/// The symbol index in the _other_ object that this symbol was diffed against
pub target_symbol: Option<usize>,
pub match_percent: Option<f32>,
pub diff_score: Option<(u64, u64)>,
pub instruction_rows: Vec<InstructionDiffRow>,
}
#[derive(Debug, Clone, Default)]
pub struct ObjInsDiff {
pub ins: Option<ObjIns>,
pub struct MappingSymbolDiff {
pub symbol_index: usize,
pub symbol_diff: SymbolDiff,
}
#[derive(Debug, Clone, Default)]
pub struct InstructionDiffRow {
/// Instruction reference
pub ins_ref: Option<InstructionRef>,
/// Diff kind
pub kind: ObjInsDiffKind,
/// Branches from instruction
pub branch_from: Option<ObjInsBranchFrom>,
pub kind: InstructionDiffKind,
/// Branches from instruction(s)
pub branch_from: Option<InstructionBranchFrom>,
/// Branches to instruction
pub branch_to: Option<ObjInsBranchTo>,
/// Arg diffs (only contains non-PlainText args)
pub arg_diff: Vec<Option<ObjInsArgDiff>>,
pub branch_to: Option<InstructionBranchTo>,
/// Arg diffs
pub arg_diff: Vec<InstructionArgDiffIndex>,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
pub enum ObjInsDiffKind {
pub enum InstructionDiffKind {
#[default]
None,
OpMismatch,
@@ -89,22 +84,21 @@ pub enum ObjInsDiffKind {
}
#[derive(Debug, Clone, Default)]
pub struct ObjDataDiff {
pub struct DataDiff {
pub data: Vec<u8>,
pub kind: ObjDataDiffKind,
pub kind: DataDiffKind,
pub len: usize,
pub symbol: String,
}
#[derive(Debug, Clone)]
pub struct ObjDataRelocDiff {
pub reloc: ObjReloc,
pub kind: ObjDataDiffKind,
pub struct DataRelocationDiff {
pub kind: DataDiffKind,
pub range: Range<usize>,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
pub enum ObjDataDiffKind {
pub enum DataDiffKind {
#[default]
None,
Replace,
@@ -112,127 +106,102 @@ pub enum ObjDataDiffKind {
Insert,
}
#[derive(Debug, Copy, Clone)]
pub struct ObjInsArgDiff {
/// Incrementing index for coloring
pub idx: usize,
/// Index of the argument diff for coloring.
#[repr(transparent)]
#[derive(Debug, Copy, Clone, Default)]
pub struct InstructionArgDiffIndex(pub Option<NonZeroU32>);
impl InstructionArgDiffIndex {
pub const NONE: Self = Self(None);
#[inline(always)]
pub fn new(idx: u32) -> Self {
Self(Some(unsafe { NonZeroU32::new_unchecked(idx.saturating_add(1)) }))
}
#[inline(always)]
pub fn get(&self) -> Option<u32> { self.0.map(|idx| idx.get() - 1) }
#[inline(always)]
pub fn is_some(&self) -> bool { self.0.is_some() }
#[inline(always)]
pub fn is_none(&self) -> bool { self.0.is_none() }
}
#[derive(Debug, Clone)]
pub struct ObjInsBranchFrom {
pub struct InstructionBranchFrom {
/// Source instruction indices
pub ins_idx: Vec<usize>,
pub ins_idx: Vec<u32>,
/// Incrementing index for coloring
pub branch_idx: usize,
pub branch_idx: u32,
}
#[derive(Debug, Clone)]
pub struct ObjInsBranchTo {
pub struct InstructionBranchTo {
/// Target instruction index
pub ins_idx: usize,
pub ins_idx: u32,
/// Incrementing index for coloring
pub branch_idx: usize,
pub branch_idx: u32,
}
#[derive(Default)]
pub struct ObjDiff {
#[derive(Debug, Default)]
pub struct ObjectDiff {
/// A list of all symbol diffs in the object.
pub symbols: Vec<SymbolDiff>,
/// A list of all section diffs in the object.
pub sections: Vec<ObjSectionDiff>,
/// Common BSS symbols don't live in a section, so they're stored separately.
pub common: Vec<ObjSymbolDiff>,
pub sections: Vec<SectionDiff>,
/// If `selecting_left` or `selecting_right` is set, this is the list of symbols
/// that are being mapped to the other object.
pub mapping_symbols: Vec<ObjSymbolDiff>,
pub mapping_symbols: Vec<MappingSymbolDiff>,
}
impl ObjDiff {
pub fn new_from_obj(obj: &ObjInfo) -> Self {
impl ObjectDiff {
pub fn new_from_obj(obj: &Object) -> Self {
let mut result = Self {
symbols: Vec::with_capacity(obj.symbols.len()),
sections: Vec::with_capacity(obj.sections.len()),
common: Vec::with_capacity(obj.common.len()),
mapping_symbols: vec![],
};
for (section_idx, section) in obj.sections.iter().enumerate() {
let mut symbols = Vec::with_capacity(section.symbols.len());
for (symbol_idx, _) in section.symbols.iter().enumerate() {
symbols.push(ObjSymbolDiff {
symbol_ref: SymbolRef { section_idx, symbol_idx },
target_symbol: None,
instructions: vec![],
match_percent: None,
});
}
result.sections.push(ObjSectionDiff {
symbols,
data_diff: vec![ObjDataDiff {
data: section.data.clone(),
kind: ObjDataDiffKind::None,
len: section.data.len(),
symbol: section.name.clone(),
}],
reloc_diff: vec![],
for _ in obj.symbols.iter() {
result.symbols.push(SymbolDiff {
target_symbol: None,
match_percent: None,
diff_score: None,
instruction_rows: vec![],
});
}
for (symbol_idx, _) in obj.common.iter().enumerate() {
result.common.push(ObjSymbolDiff {
symbol_ref: SymbolRef { section_idx: SECTION_COMMON, symbol_idx },
target_symbol: None,
instructions: vec![],
for _ in obj.sections.iter() {
result.sections.push(SectionDiff {
// target_section: None,
match_percent: None,
data_diff: vec![],
reloc_diff: vec![],
});
}
result
}
#[inline]
pub fn section_diff(&self, section_idx: usize) -> &ObjSectionDiff {
&self.sections[section_idx]
}
#[inline]
pub fn section_diff_mut(&mut self, section_idx: usize) -> &mut ObjSectionDiff {
&mut self.sections[section_idx]
}
#[inline]
pub fn symbol_diff(&self, symbol_ref: SymbolRef) -> &ObjSymbolDiff {
if symbol_ref.section_idx == SECTION_COMMON {
&self.common[symbol_ref.symbol_idx]
} else {
&self.section_diff(symbol_ref.section_idx).symbols[symbol_ref.symbol_idx]
}
}
#[inline]
pub fn symbol_diff_mut(&mut self, symbol_ref: SymbolRef) -> &mut ObjSymbolDiff {
if symbol_ref.section_idx == SECTION_COMMON {
&mut self.common[symbol_ref.symbol_idx]
} else {
&mut self.section_diff_mut(symbol_ref.section_idx).symbols[symbol_ref.symbol_idx]
}
}
}
#[derive(Default)]
#[derive(Debug, Default)]
pub struct DiffObjsResult {
pub left: Option<ObjDiff>,
pub right: Option<ObjDiff>,
pub prev: Option<ObjDiff>,
pub left: Option<ObjectDiff>,
pub right: Option<ObjectDiff>,
pub prev: Option<ObjectDiff>,
}
pub fn diff_objs(
left: Option<&Object>,
right: Option<&Object>,
prev: Option<&Object>,
diff_config: &DiffObjConfig,
mapping_config: &MappingConfig,
left: Option<&ObjInfo>,
right: Option<&ObjInfo>,
prev: Option<&ObjInfo>,
) -> Result<DiffObjsResult> {
let symbol_matches = matching_symbols(left, right, prev, mapping_config)?;
let section_matches = matching_sections(left, right)?;
let mut left = left.map(|p| (p, ObjDiff::new_from_obj(p)));
let mut right = right.map(|p| (p, ObjDiff::new_from_obj(p)));
let mut prev = prev.map(|p| (p, ObjDiff::new_from_obj(p)));
let mut left = left.map(|p| (p, ObjectDiff::new_from_obj(p)));
let mut right = right.map(|p| (p, ObjectDiff::new_from_obj(p)));
let mut prev = prev.map(|p| (p, ObjectDiff::new_from_obj(p)));
for symbol_match in symbol_matches {
match symbol_match {
@@ -245,87 +214,76 @@ pub fn diff_objs(
let (left_obj, left_out) = left.as_mut().unwrap();
let (right_obj, right_out) = right.as_mut().unwrap();
match section_kind {
ObjSectionKind::Code => {
let left_code =
process_code_symbol(left_obj, left_symbol_ref, diff_config)?;
let right_code =
process_code_symbol(right_obj, right_symbol_ref, diff_config)?;
SectionKind::Code => {
let (left_diff, right_diff) = diff_code(
left_obj,
right_obj,
&left_code,
&right_code,
left_symbol_ref,
right_symbol_ref,
diff_config,
)?;
*left_out.symbol_diff_mut(left_symbol_ref) = left_diff;
*right_out.symbol_diff_mut(right_symbol_ref) = right_diff;
left_out.symbols[left_symbol_ref] = left_diff;
right_out.symbols[right_symbol_ref] = right_diff;
if let Some(prev_symbol_ref) = prev_symbol_ref {
let (prev_obj, prev_out) = prev.as_mut().unwrap();
let prev_code =
process_code_symbol(prev_obj, prev_symbol_ref, diff_config)?;
let (_prev_obj, prev_out) = prev.as_mut().unwrap();
let (_, prev_diff) = diff_code(
left_obj,
right_obj,
&right_code,
&prev_code,
right_symbol_ref,
prev_symbol_ref,
diff_config,
)?;
*prev_out.symbol_diff_mut(prev_symbol_ref) = prev_diff;
prev_out.symbols[prev_symbol_ref] = prev_diff;
}
}
ObjSectionKind::Data => {
SectionKind::Data => {
let (left_diff, right_diff) = diff_data_symbol(
left_obj,
right_obj,
left_symbol_ref,
right_symbol_ref,
)?;
*left_out.symbol_diff_mut(left_symbol_ref) = left_diff;
*right_out.symbol_diff_mut(right_symbol_ref) = right_diff;
left_out.symbols[left_symbol_ref] = left_diff;
right_out.symbols[right_symbol_ref] = right_diff;
}
ObjSectionKind::Bss => {
SectionKind::Bss | SectionKind::Common => {
let (left_diff, right_diff) = diff_bss_symbol(
left_obj,
right_obj,
left_symbol_ref,
right_symbol_ref,
)?;
*left_out.symbol_diff_mut(left_symbol_ref) = left_diff;
*right_out.symbol_diff_mut(right_symbol_ref) = right_diff;
left_out.symbols[left_symbol_ref] = left_diff;
right_out.symbols[right_symbol_ref] = right_diff;
}
SectionKind::Unknown => unreachable!(),
}
}
SymbolMatch { left: Some(left_symbol_ref), right: None, prev: _, section_kind } => {
let (left_obj, left_out) = left.as_mut().unwrap();
match section_kind {
ObjSectionKind::Code => {
let code = process_code_symbol(left_obj, left_symbol_ref, diff_config)?;
*left_out.symbol_diff_mut(left_symbol_ref) =
no_diff_code(&code, left_symbol_ref)?;
SectionKind::Code => {
left_out.symbols[left_symbol_ref] =
no_diff_code(left_obj, left_symbol_ref, diff_config)?;
}
ObjSectionKind::Data | ObjSectionKind::Bss => {
*left_out.symbol_diff_mut(left_symbol_ref) =
no_diff_symbol(left_obj, left_symbol_ref);
SectionKind::Data | SectionKind::Bss | SectionKind::Common => {
// Nothing needs to be done
}
SectionKind::Unknown => unreachable!(),
}
}
SymbolMatch { left: None, right: Some(right_symbol_ref), prev: _, section_kind } => {
let (right_obj, right_out) = right.as_mut().unwrap();
match section_kind {
ObjSectionKind::Code => {
let code = process_code_symbol(right_obj, right_symbol_ref, diff_config)?;
*right_out.symbol_diff_mut(right_symbol_ref) =
no_diff_code(&code, right_symbol_ref)?;
SectionKind::Code => {
right_out.symbols[right_symbol_ref] =
no_diff_code(right_obj, right_symbol_ref, diff_config)?;
}
ObjSectionKind::Data | ObjSectionKind::Bss => {
*right_out.symbol_diff_mut(right_symbol_ref) =
no_diff_symbol(right_obj, right_symbol_ref);
SectionKind::Data | SectionKind::Bss | SectionKind::Common => {
// Nothing needs to be done
}
SectionKind::Unknown => unreachable!(),
}
}
SymbolMatch { left: None, right: None, .. } => {
@@ -343,47 +301,44 @@ pub fn diff_objs(
{
let (left_obj, left_out) = left.as_mut().unwrap();
let (right_obj, right_out) = right.as_mut().unwrap();
let left_section = &left_obj.sections[left_section_idx];
let right_section = &right_obj.sections[right_section_idx];
match section_kind {
ObjSectionKind::Code => {
let left_section_diff = left_out.section_diff(left_section_idx);
let right_section_diff = right_out.section_diff(right_section_idx);
SectionKind::Code => {
let (left_diff, right_diff) = diff_generic_section(
left_section,
right_section,
left_section_diff,
right_section_diff,
left_obj,
right_obj,
left_out,
right_out,
left_section_idx,
right_section_idx,
)?;
left_out.section_diff_mut(left_section_idx).merge(left_diff);
right_out.section_diff_mut(right_section_idx).merge(right_diff);
left_out.sections[left_section_idx] = left_diff;
right_out.sections[right_section_idx] = right_diff;
}
ObjSectionKind::Data => {
let left_section_diff = left_out.section_diff(left_section_idx);
let right_section_diff = right_out.section_diff(right_section_idx);
SectionKind::Data => {
let (left_diff, right_diff) = diff_data_section(
left_obj,
right_obj,
left_section,
right_section,
left_section_diff,
right_section_diff,
left_out,
right_out,
left_section_idx,
right_section_idx,
)?;
left_out.section_diff_mut(left_section_idx).merge(left_diff);
right_out.section_diff_mut(right_section_idx).merge(right_diff);
left_out.sections[left_section_idx] = left_diff;
right_out.sections[right_section_idx] = right_diff;
}
ObjSectionKind::Bss => {
let left_section_diff = left_out.section_diff(left_section_idx);
let right_section_diff = right_out.section_diff(right_section_idx);
SectionKind::Bss | SectionKind::Common => {
let (left_diff, right_diff) = diff_bss_section(
left_section,
right_section,
left_section_diff,
right_section_diff,
left_obj,
right_obj,
left_out,
right_out,
left_section_idx,
right_section_idx,
)?;
left_out.section_diff_mut(left_section_idx).merge(left_diff);
right_out.section_diff_mut(right_section_idx).merge(right_diff);
left_out.sections[left_section_idx] = left_diff;
right_out.sections[right_section_idx] = right_diff;
}
SectionKind::Unknown => unreachable!(),
}
}
}
@@ -410,54 +365,46 @@ pub fn diff_objs(
/// symbols in the other object that match the selected symbol's section and kind. This allows
/// us to display match percentages for all symbols in the other object that could be selected.
fn generate_mapping_symbols(
base_obj: &ObjInfo,
base_obj: &Object,
base_name: &str,
target_obj: &ObjInfo,
target_out: &mut ObjDiff,
target_obj: &Object,
target_out: &mut ObjectDiff,
config: &DiffObjConfig,
) -> Result<()> {
let Some(base_symbol_ref) = symbol_ref_by_name(base_obj, base_name) else {
return Ok(());
};
let (base_section, _base_symbol) = base_obj.section_symbol(base_symbol_ref);
let Some(base_section) = base_section else {
return Ok(());
};
let base_code = match base_section.kind {
ObjSectionKind::Code => Some(process_code_symbol(base_obj, base_symbol_ref, config)?),
_ => None,
};
for (target_section_index, target_section) in
target_obj.sections.iter().enumerate().filter(|(_, s)| s.kind == base_section.kind)
{
for (target_symbol_index, _target_symbol) in target_section.symbols.iter().enumerate() {
let target_symbol_ref =
SymbolRef { section_idx: target_section_index, symbol_idx: target_symbol_index };
match base_section.kind {
ObjSectionKind::Code => {
let target_code = process_code_symbol(target_obj, target_symbol_ref, config)?;
let (left_diff, _right_diff) = diff_code(
target_obj,
base_obj,
&target_code,
base_code.as_ref().unwrap(),
target_symbol_ref,
base_symbol_ref,
config,
)?;
target_out.mapping_symbols.push(left_diff);
}
ObjSectionKind::Data => {
let (left_diff, _right_diff) =
diff_data_symbol(target_obj, base_obj, target_symbol_ref, base_symbol_ref)?;
target_out.mapping_symbols.push(left_diff);
}
ObjSectionKind::Bss => {
let (left_diff, _right_diff) =
diff_bss_symbol(target_obj, base_obj, target_symbol_ref, base_symbol_ref)?;
target_out.mapping_symbols.push(left_diff);
}
let base_section_kind = symbol_section_kind(base_obj, &base_obj.symbols[base_symbol_ref]);
for (target_symbol_index, target_symbol) in target_obj.symbols.iter().enumerate() {
if symbol_section_kind(target_obj, target_symbol) != base_section_kind {
continue;
}
match base_section_kind {
SectionKind::Code => {
let (left_diff, _right_diff) =
diff_code(target_obj, base_obj, target_symbol_index, base_symbol_ref, config)?;
target_out.mapping_symbols.push(MappingSymbolDiff {
symbol_index: target_symbol_index,
symbol_diff: left_diff,
});
}
SectionKind::Data => {
let (left_diff, _right_diff) =
diff_data_symbol(target_obj, base_obj, target_symbol_index, base_symbol_ref)?;
target_out.mapping_symbols.push(MappingSymbolDiff {
symbol_index: target_symbol_index,
symbol_diff: left_diff,
});
}
SectionKind::Bss | SectionKind::Common => {
let (left_diff, _right_diff) =
diff_bss_symbol(target_obj, base_obj, target_symbol_index, base_symbol_ref)?;
target_out.mapping_symbols.push(MappingSymbolDiff {
symbol_index: target_symbol_index,
symbol_diff: left_diff,
});
}
SectionKind::Unknown => {}
}
}
Ok(())
@@ -465,17 +412,17 @@ fn generate_mapping_symbols(
#[derive(Copy, Clone, Eq, PartialEq)]
struct SymbolMatch {
left: Option<SymbolRef>,
right: Option<SymbolRef>,
prev: Option<SymbolRef>,
section_kind: ObjSectionKind,
left: Option<usize>,
right: Option<usize>,
prev: Option<usize>,
section_kind: SectionKind,
}
#[derive(Copy, Clone, Eq, PartialEq)]
struct SectionMatch {
left: Option<usize>,
right: Option<usize>,
section_kind: ObjSectionKind,
section_kind: SectionKind,
}
#[derive(Debug, Clone, Default)]
@@ -489,23 +436,16 @@ pub struct MappingConfig {
pub selecting_right: Option<String>,
}
fn symbol_ref_by_name(obj: &ObjInfo, name: &str) -> Option<SymbolRef> {
for (section_idx, section) in obj.sections.iter().enumerate() {
for (symbol_idx, symbol) in section.symbols.iter().enumerate() {
if symbol.name == name {
return Some(SymbolRef { section_idx, symbol_idx });
}
}
}
None
fn symbol_ref_by_name(obj: &Object, name: &str) -> Option<usize> {
obj.symbols.iter().position(|s| s.name == name)
}
fn apply_symbol_mappings(
left: &ObjInfo,
right: &ObjInfo,
left: &Object,
right: &Object,
mapping_config: &MappingConfig,
left_used: &mut BTreeSet<SymbolRef>,
right_used: &mut BTreeSet<SymbolRef>,
left_used: &mut BTreeSet<usize>,
right_used: &mut BTreeSet<usize>,
matches: &mut Vec<SymbolMatch>,
) -> Result<()> {
// If we're selecting a symbol to use as a comparison, mark it as used
@@ -523,47 +463,57 @@ fn apply_symbol_mappings(
// Apply manual symbol mappings
for (left_name, right_name) in &mapping_config.mappings {
let Some(left_symbol) = symbol_ref_by_name(left, left_name) else {
let Some(left_symbol_index) = symbol_ref_by_name(left, left_name) else {
continue;
};
if left_used.contains(&left_symbol) {
if left_used.contains(&left_symbol_index) {
continue;
}
let Some(right_symbol) = symbol_ref_by_name(right, right_name) else {
let Some(right_symbol_index) = symbol_ref_by_name(right, right_name) else {
continue;
};
if right_used.contains(&right_symbol) {
if right_used.contains(&right_symbol_index) {
continue;
}
let left_section = &left.sections[left_symbol.section_idx];
let right_section = &right.sections[right_symbol.section_idx];
if left_section.kind != right_section.kind {
let left_section_kind = left
.symbols
.get(left_symbol_index)
.and_then(|s| s.section)
.and_then(|section_index| left.sections.get(section_index))
.map_or(SectionKind::Unknown, |s| s.kind);
let right_section_kind = right
.symbols
.get(right_symbol_index)
.and_then(|s| s.section)
.and_then(|section_index| right.sections.get(section_index))
.map_or(SectionKind::Unknown, |s| s.kind);
if left_section_kind != right_section_kind {
log::warn!(
"Symbol section kind mismatch: {} ({:?}) vs {} ({:?})",
left_name,
left_section.kind,
left_section_kind,
right_name,
right_section.kind
right_section_kind
);
continue;
}
matches.push(SymbolMatch {
left: Some(left_symbol),
right: Some(right_symbol),
left: Some(left_symbol_index),
right: Some(right_symbol_index),
prev: None, // TODO
section_kind: left_section.kind,
section_kind: left_section_kind,
});
left_used.insert(left_symbol);
right_used.insert(right_symbol);
left_used.insert(left_symbol_index);
right_used.insert(right_symbol_index);
}
Ok(())
}
/// Find matching symbols between each object.
fn matching_symbols(
left: Option<&ObjInfo>,
right: Option<&ObjInfo>,
prev: Option<&ObjInfo>,
left: Option<&Object>,
right: Option<&Object>,
prev: Option<&Object>,
mappings: &MappingConfig,
) -> Result<Vec<SymbolMatch>> {
let mut matches = Vec::new();
@@ -580,34 +530,19 @@ fn matching_symbols(
&mut matches,
)?;
}
for (section_idx, section) in left.sections.iter().enumerate() {
for (symbol_idx, symbol) in section.symbols.iter().enumerate() {
let symbol_ref = SymbolRef { section_idx, symbol_idx };
if left_used.contains(&symbol_ref) {
continue;
}
let symbol_match = SymbolMatch {
left: Some(symbol_ref),
right: find_symbol(right, symbol, section, Some(&right_used)),
prev: find_symbol(prev, symbol, section, None),
section_kind: section.kind,
};
matches.push(symbol_match);
if let Some(right) = symbol_match.right {
right_used.insert(right);
}
for (symbol_idx, symbol) in left.symbols.iter().enumerate() {
let section_kind = symbol_section_kind(left, symbol);
if section_kind == SectionKind::Unknown {
continue;
}
}
for (symbol_idx, symbol) in left.common.iter().enumerate() {
let symbol_ref = SymbolRef { section_idx: SECTION_COMMON, symbol_idx };
if left_used.contains(&symbol_ref) {
if left_used.contains(&symbol_idx) {
continue;
}
let symbol_match = SymbolMatch {
left: Some(symbol_ref),
right: find_common_symbol(right, symbol),
prev: find_common_symbol(prev, symbol),
section_kind: ObjSectionKind::Bss,
left: Some(symbol_idx),
right: find_symbol(right, left, symbol, Some(&right_used)),
prev: find_symbol(prev, left, symbol, None),
section_kind,
};
matches.push(symbol_match);
if let Some(right) = symbol_match.right {
@@ -616,83 +551,84 @@ fn matching_symbols(
}
}
if let Some(right) = right {
for (section_idx, section) in right.sections.iter().enumerate() {
for (symbol_idx, symbol) in section.symbols.iter().enumerate() {
let symbol_ref = SymbolRef { section_idx, symbol_idx };
if right_used.contains(&symbol_ref) {
continue;
}
matches.push(SymbolMatch {
left: None,
right: Some(symbol_ref),
prev: find_symbol(prev, symbol, section, None),
section_kind: section.kind,
});
for (symbol_idx, symbol) in right.symbols.iter().enumerate() {
let section_kind = symbol_section_kind(right, symbol);
if section_kind == SectionKind::Unknown {
continue;
}
}
for (symbol_idx, symbol) in right.common.iter().enumerate() {
let symbol_ref = SymbolRef { section_idx: SECTION_COMMON, symbol_idx };
if right_used.contains(&symbol_ref) {
if right_used.contains(&symbol_idx) {
continue;
}
matches.push(SymbolMatch {
left: None,
right: Some(symbol_ref),
prev: find_common_symbol(prev, symbol),
section_kind: ObjSectionKind::Bss,
right: Some(symbol_idx),
prev: find_symbol(prev, right, symbol, None),
section_kind,
});
}
}
Ok(matches)
}
fn unmatched_symbols<'section, 'used>(
section: &'section ObjSection,
section_idx: usize,
used: Option<&'used BTreeSet<SymbolRef>>,
) -> impl Iterator<Item = (usize, &'section ObjSymbol)> + 'used
fn unmatched_symbols<'obj, 'used>(
obj: &'obj Object,
used: Option<&'used BTreeSet<usize>>,
) -> impl Iterator<Item = (usize, &'obj Symbol)> + 'used
where
'section: 'used,
'obj: 'used,
{
section.symbols.iter().enumerate().filter(move |&(symbol_idx, _)| {
obj.symbols.iter().enumerate().filter(move |&(symbol_idx, _)| {
// Skip symbols that have already been matched
!used.map(|u| u.contains(&SymbolRef { section_idx, symbol_idx })).unwrap_or(false)
!used.is_some_and(|u| u.contains(&symbol_idx))
})
}
fn symbol_section<'obj>(obj: &'obj Object, symbol: &Symbol) -> Option<(&'obj str, SectionKind)> {
if let Some(section) = symbol.section.and_then(|section_idx| obj.sections.get(section_idx)) {
Some((section.name.as_str(), section.kind))
} else if symbol.flags.contains(SymbolFlag::Common) {
Some((".comm", SectionKind::Common))
} else {
None
}
}
fn symbol_section_kind(obj: &Object, symbol: &Symbol) -> SectionKind {
match symbol.section {
Some(section_index) => obj.sections[section_index].kind,
None if symbol.flags.contains(SymbolFlag::Common) => SectionKind::Common,
None => SectionKind::Unknown,
}
}
fn find_symbol(
obj: Option<&ObjInfo>,
in_symbol: &ObjSymbol,
in_section: &ObjSection,
used: Option<&BTreeSet<SymbolRef>>,
) -> Option<SymbolRef> {
obj: Option<&Object>,
in_obj: &Object,
in_symbol: &Symbol,
used: Option<&BTreeSet<usize>>,
) -> Option<usize> {
let obj = obj?;
let (section_name, section_kind) = symbol_section(in_obj, in_symbol)?;
// Try to find an exact name match
for (section_idx, section) in obj.sections.iter().enumerate() {
if section.kind != in_section.kind {
continue;
}
if let Some((symbol_idx, _)) = unmatched_symbols(section, section_idx, used)
.find(|(_, symbol)| symbol.name == in_symbol.name)
{
return Some(SymbolRef { section_idx, symbol_idx });
}
if let Some((symbol_idx, _)) = unmatched_symbols(obj, used).find(|(_, symbol)| {
symbol.name == in_symbol.name && symbol_section_kind(obj, symbol) == section_kind
}) {
return Some(symbol_idx);
}
// Match compiler-generated symbols against each other (e.g. @251 -> @60)
// If they are at the same address in the same section
if in_symbol.name.starts_with('@')
&& matches!(in_section.kind, ObjSectionKind::Data | ObjSectionKind::Bss)
&& matches!(section_kind, SectionKind::Data | SectionKind::Bss)
{
if let Some((section_idx, section)) =
obj.sections.iter().enumerate().find(|(_, s)| s.name == in_section.name)
{
if let Some((symbol_idx, _)) =
unmatched_symbols(section, section_idx, used).find(|(_, symbol)| {
symbol.address == in_symbol.address && symbol.name.starts_with('@')
})
{
return Some(SymbolRef { section_idx, symbol_idx });
}
if let Some((symbol_idx, _)) = unmatched_symbols(obj, used).find(|(_, symbol)| {
let Some(section_index) = symbol.section else {
return false;
};
symbol.name.starts_with('@')
&& symbol.address == in_symbol.address
&& obj.sections[section_index].name == section_name
}) {
return Some(symbol_idx);
}
}
// Match Metrowerks symbol$1234 against symbol$2345
@@ -700,41 +636,29 @@ fn find_symbol(
if !suffix.chars().all(char::is_numeric) {
return None;
}
for (section_idx, section) in obj.sections.iter().enumerate() {
if section.kind != in_section.kind {
continue;
if let Some((symbol_idx, _)) = unmatched_symbols(obj, used).find(|&(_, symbol)| {
if let Some((p, s)) = symbol.name.split_once('$') {
prefix == p
&& s.chars().all(char::is_numeric)
&& symbol_section_kind(obj, symbol) == section_kind
} else {
false
}
if let Some((symbol_idx, _)) =
unmatched_symbols(section, section_idx, used).find(|&(_, symbol)| {
if let Some((p, s)) = symbol.name.split_once('$') {
prefix == p && s.chars().all(char::is_numeric)
} else {
false
}
})
{
return Some(SymbolRef { section_idx, symbol_idx });
}
}
}
None
}
fn find_common_symbol(obj: Option<&ObjInfo>, in_symbol: &ObjSymbol) -> Option<SymbolRef> {
let obj = obj?;
for (symbol_idx, symbol) in obj.common.iter().enumerate() {
if symbol.name == in_symbol.name {
return Some(SymbolRef { section_idx: SECTION_COMMON, symbol_idx });
}) {
return Some(symbol_idx);
}
}
None
}
/// Find matching sections between each object.
fn matching_sections(left: Option<&ObjInfo>, right: Option<&ObjInfo>) -> Result<Vec<SectionMatch>> {
fn matching_sections(left: Option<&Object>, right: Option<&Object>) -> Result<Vec<SectionMatch>> {
let mut matches = Vec::new();
if let Some(left) = left {
for (section_idx, section) in left.sections.iter().enumerate() {
if section.kind == SectionKind::Unknown {
continue;
}
matches.push(SectionMatch {
left: Some(section_idx),
right: find_section(right, &section.name, section.kind),
@@ -744,6 +668,9 @@ fn matching_sections(left: Option<&ObjInfo>, right: Option<&ObjInfo>) -> Result<
}
if let Some(right) = right {
for (section_idx, section) in right.sections.iter().enumerate() {
if section.kind == SectionKind::Unknown {
continue;
}
if matches.iter().any(|m| m.right == Some(section_idx)) {
continue;
}
@@ -757,14 +684,6 @@ fn matching_sections(left: Option<&ObjInfo>, right: Option<&ObjInfo>) -> Result<
Ok(matches)
}
fn find_section(obj: Option<&ObjInfo>, name: &str, section_kind: ObjSectionKind) -> Option<usize> {
for (section_idx, section) in obj?.sections.iter().enumerate() {
if section.kind != section_kind {
continue;
}
if section.name == name {
return Some(section_idx);
}
}
None
fn find_section(obj: Option<&Object>, name: &str, section_kind: SectionKind) -> Option<usize> {
obj?.sections.iter().position(|s| s.kind == section_kind && s.name == name)
}