Skip to content

Commit

Permalink
Check relocation addends when diffing functions (#158)
Browse files Browse the repository at this point in the history
* Check relocation addends when diffing functions

* Also highlight addend when reloc differs
  • Loading branch information
LagoLunatic authored Feb 10, 2025
1 parent 674c942 commit 3e6efb7
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 34 deletions.
14 changes: 14 additions & 0 deletions objdiff-cli/src/views/function_diff.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::cmp::Ordering;

use anyhow::{bail, Result};
use crossterm::event::{Event, KeyCode, KeyEventKind, KeyModifiers, MouseButton, MouseEventKind};
use objdiff_core::{
Expand Down Expand Up @@ -563,6 +565,18 @@ impl FunctionDiffUi {
base_color = Color::White;
}
}
DiffText::Addend(addend, diff) => {
label_text = match addend.cmp(&0i64) {
Ordering::Greater => format!("+{:#x}", addend),
Ordering::Less => format!("-{:#x}", -addend),
_ => "".to_string(),
};
if let Some(diff) = diff {
base_color = COLOR_ROTATION[diff.idx % COLOR_ROTATION.len()]
} else {
base_color = Color::White;
}
}
DiffText::Spacing(n) => {
line.spans.push(Span::raw(" ".repeat(n)));
sx += n as u16;
Expand Down
25 changes: 18 additions & 7 deletions objdiff-core/src/diff/code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,8 +192,18 @@ fn resolve_branches(vec: &mut [ObjInsDiff]) {
}
}

fn address_eq(left: &ObjReloc, right: &ObjReloc) -> bool {
left.target.address as i64 + left.addend == right.target.address as i64 + right.addend
pub fn address_eq(left: &ObjReloc, right: &ObjReloc) -> bool {
if right.target.size == 0 && left.target.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
} 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
}
}

pub fn section_name_eq(
Expand Down Expand Up @@ -235,13 +245,14 @@ fn reloc_eq(
return true;
}

let symbol_name_matches = left.target.name == right.target.name;
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) {
(Some(sl), Some(sr)) => {
// Match if section and name or address match
// Match if section and name+addend or address match
section_name_eq(left_obj, right_obj, *sl, *sr)
&& (config.function_reloc_diffs == FunctionRelocDiffs::DataValue
|| symbol_name_matches
|| symbol_name_addend_matches
|| address_eq(left, right))
&& (config.function_reloc_diffs == FunctionRelocDiffs::NameAddress
|| left.target.kind != ObjSymbolKind::Object
Expand All @@ -251,9 +262,9 @@ fn reloc_eq(
(Some(_), None) => false,
(None, Some(_)) => {
// Match if possibly stripped weak symbol
symbol_name_matches && right.target.flags.0.contains(ObjSymbolFlags::Weak)
symbol_name_addend_matches && right.target.flags.0.contains(ObjSymbolFlags::Weak)
}
(None, None) => symbol_name_matches,
(None, None) => symbol_name_addend_matches,
}
}

Expand Down
26 changes: 6 additions & 20 deletions objdiff-core/src/diff/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::{
use anyhow::{anyhow, Result};
use similar::{capture_diff_slices_deadline, get_diff_ratio, Algorithm};

use super::code::section_name_eq;
use super::code::{address_eq, section_name_eq};
use crate::{
diff::{ObjDataDiff, ObjDataDiffKind, ObjDataRelocDiff, ObjSectionDiff, ObjSymbolDiff},
obj::{ObjInfo, ObjReloc, ObjSection, ObjSymbolFlags, SymbolRef},
Expand Down Expand Up @@ -41,39 +41,25 @@ pub fn no_diff_symbol(_obj: &ObjInfo, symbol_ref: SymbolRef) -> ObjSymbolDiff {
ObjSymbolDiff { symbol_ref, target_symbol: None, instructions: vec![], match_percent: None }
}

fn address_eq(left: &ObjReloc, right: &ObjReloc) -> bool {
if right.target.size == 0 && left.target.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, 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
} 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
}
}

fn reloc_eq(left_obj: &ObjInfo, right_obj: &ObjInfo, left: &ObjReloc, right: &ObjReloc) -> bool {
if left.flags != right.flags {
return false;
}

let symbol_name_matches = left.target.name == right.target.name;
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) {
(Some(sl), Some(sr)) => {
// Match if section and name+addend or address match
section_name_eq(left_obj, right_obj, *sl, *sr)
&& ((symbol_name_matches && left.addend == right.addend) || address_eq(left, right))
&& (symbol_name_addend_matches || address_eq(left, right))
}
(Some(_), None) => false,
(None, Some(_)) => {
// Match if possibly stripped weak symbol
(symbol_name_matches && left.addend == right.addend)
&& right.target.flags.0.contains(ObjSymbolFlags::Weak)
symbol_name_addend_matches && right.target.flags.0.contains(ObjSymbolFlags::Weak)
}
(None, None) => symbol_name_matches,
(None, None) => symbol_name_addend_matches,
}
}

Expand Down
10 changes: 3 additions & 7 deletions objdiff-core/src/diff/display.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use std::cmp::Ordering;

use crate::{
diff::{ObjInsArgDiff, ObjInsDiff},
obj::{ObjInsArg, ObjInsArgValue, ObjReloc, ObjSymbol},
Expand All @@ -23,6 +21,8 @@ pub enum DiffText<'a> {
BranchDest(u64, Option<&'a ObjInsArgDiff>),
/// Symbol name
Symbol(&'a ObjSymbol, Option<&'a ObjInsArgDiff>),
/// Relocation addend
Addend(i64, Option<&'a ObjInsArgDiff>),
/// Number of spaces
Spacing(usize),
/// End of line
Expand Down Expand Up @@ -99,11 +99,7 @@ fn display_reloc_name<E>(
diff: Option<&ObjInsArgDiff>,
) -> Result<(), E> {
cb(DiffText::Symbol(&reloc.target, diff))?;
match reloc.addend.cmp(&0i64) {
Ordering::Greater => cb(DiffText::Basic(&format!("+{:#x}", reloc.addend))),
Ordering::Less => cb(DiffText::Basic(&format!("-{:#x}", -reloc.addend))),
_ => Ok(()),
}
cb(DiffText::Addend(reloc.addend, diff))
}

impl PartialEq<DiffText<'_>> for HighlightKind {
Expand Down
12 changes: 12 additions & 0 deletions objdiff-gui/src/views/function_diff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,18 @@ fn diff_text_ui(
base_color = appearance.emphasized_text_color;
}
}
DiffText::Addend(addend, diff) => {
label_text = match addend.cmp(&0i64) {
Ordering::Greater => format!("+{:#x}", addend),
Ordering::Less => format!("-{:#x}", -addend),
_ => "".to_string(),
};
if let Some(diff) = diff {
base_color = appearance.diff_colors[diff.idx % appearance.diff_colors.len()]
} else {
base_color = appearance.emphasized_text_color;
}
}
DiffText::Spacing(n) => {
ui.add_space(n as f32 * space_width);
return ret;
Expand Down

0 comments on commit 3e6efb7

Please sign in to comment.