From a4fdb61f043af9996c8a7f88797036d677ab09fb Mon Sep 17 00:00:00 2001 From: LagoLunatic Date: Sat, 18 Jan 2025 18:20:07 -0500 Subject: [PATCH] Implement diffing relocations within data sections (#154) * Data view: Show data bytes with differing relocations as a diff * Data view: Show differing relocations on hover * Symbol list view: Adjust symbol/section match %s when relocations differ * Improve data reloc diffing logic * Don't make reloc diffs cause bytes to show as red or green * Properly detect byte size of each relocation * Data view: Add context menu for copying relocation target symbols * Also show already-matching relocations on hover/right click * Change font color for nonmatching relocs on hover --- objdiff-core/src/arch/arm.rs | 13 ++ objdiff-core/src/arch/arm64.rs | 15 ++ objdiff-core/src/arch/mips.rs | 11 ++ objdiff-core/src/arch/mod.rs | 2 + objdiff-core/src/arch/ppc.rs | 11 ++ objdiff-core/src/arch/x86.rs | 13 ++ objdiff-core/src/diff/code.rs | 2 +- objdiff-core/src/diff/data.rs | 266 +++++++++++++++++++++++++++-- objdiff-core/src/diff/mod.rs | 8 +- objdiff-gui/src/views/data_diff.rs | 109 +++++++++++- 10 files changed, 426 insertions(+), 24 deletions(-) diff --git a/objdiff-core/src/arch/arm.rs b/objdiff-core/src/arch/arm.rs index 28826c8..84a21cc 100644 --- a/objdiff-core/src/arch/arm.rs +++ b/objdiff-core/src/arch/arm.rs @@ -276,6 +276,19 @@ impl ObjArch for ObjArchArm { fn display_reloc(&self, flags: RelocationFlags) -> Cow<'static, str> { Cow::Owned(format!("<{flags:?}>")) } + + fn get_reloc_byte_size(&self, flags: RelocationFlags) -> usize { + match flags { + RelocationFlags::Elf { r_type } => match r_type { + elf::R_ARM_ABS32 => 4, + elf::R_ARM_REL32 => 4, + elf::R_ARM_ABS16 => 2, + elf::R_ARM_ABS8 => 1, + _ => 1, + }, + _ => 1, + } + } } #[derive(Clone, Copy, Debug)] diff --git a/objdiff-core/src/arch/arm64.rs b/objdiff-core/src/arch/arm64.rs index 7e396fa..ec225f3 100644 --- a/objdiff-core/src/arch/arm64.rs +++ b/objdiff-core/src/arch/arm64.rs @@ -173,6 +173,21 @@ impl ObjArch for ObjArchArm64 { _ => Cow::Owned(format!("<{flags:?}>")), } } + + fn get_reloc_byte_size(&self, flags: RelocationFlags) -> usize { + match flags { + RelocationFlags::Elf { r_type } => match r_type { + elf::R_AARCH64_ABS64 => 8, + elf::R_AARCH64_ABS32 => 4, + elf::R_AARCH64_ABS16 => 2, + elf::R_AARCH64_PREL64 => 8, + elf::R_AARCH64_PREL32 => 4, + elf::R_AARCH64_PREL16 => 2, + _ => 1, + }, + _ => 1, + } + } } struct DisplayCtx<'a> { diff --git a/objdiff-core/src/arch/mips.rs b/objdiff-core/src/arch/mips.rs index 87b6bdd..2b89b23 100644 --- a/objdiff-core/src/arch/mips.rs +++ b/objdiff-core/src/arch/mips.rs @@ -271,6 +271,17 @@ impl ObjArch for ObjArchMips { _ => Cow::Owned(format!("<{flags:?}>")), } } + + fn get_reloc_byte_size(&self, flags: RelocationFlags) -> usize { + match flags { + RelocationFlags::Elf { r_type } => match r_type { + elf::R_MIPS_16 => 2, + elf::R_MIPS_32 => 4, + _ => 1, + }, + _ => 1, + } + } } fn push_reloc(args: &mut Vec, reloc: &ObjReloc) -> Result<()> { diff --git a/objdiff-core/src/arch/mod.rs b/objdiff-core/src/arch/mod.rs index da76ca4..829f027 100644 --- a/objdiff-core/src/arch/mod.rs +++ b/objdiff-core/src/arch/mod.rs @@ -148,6 +148,8 @@ pub trait ObjArch: Send + Sync { fn display_reloc(&self, flags: RelocationFlags) -> Cow<'static, str>; + fn get_reloc_byte_size(&self, flags: RelocationFlags) -> usize; + fn symbol_address(&self, symbol: &Symbol) -> u64 { symbol.address() } fn guess_data_type(&self, _instruction: &ObjIns) -> Option { None } diff --git a/objdiff-core/src/arch/ppc.rs b/objdiff-core/src/arch/ppc.rs index 4788824..7e0052a 100644 --- a/objdiff-core/src/arch/ppc.rs +++ b/objdiff-core/src/arch/ppc.rs @@ -202,6 +202,17 @@ impl ObjArch for ObjArchPpc { } } + fn get_reloc_byte_size(&self, flags: RelocationFlags) -> usize { + match flags { + RelocationFlags::Elf { r_type } => match r_type { + elf::R_PPC_ADDR32 => 4, + elf::R_PPC_UADDR32 => 4, + _ => 1, + }, + _ => 1, + } + } + fn guess_data_type(&self, instruction: &ObjIns) -> Option { if instruction.reloc.as_ref().is_some_and(|r| r.target.name.starts_with("@stringBase")) { return Some(DataType::String); diff --git a/objdiff-core/src/arch/x86.rs b/objdiff-core/src/arch/x86.rs index 82399fb..b292993 100644 --- a/objdiff-core/src/arch/x86.rs +++ b/objdiff-core/src/arch/x86.rs @@ -162,6 +162,19 @@ impl ObjArch for ObjArchX86 { _ => Cow::Owned(format!("<{flags:?}>")), } } + + fn get_reloc_byte_size(&self, flags: RelocationFlags) -> usize { + match flags { + RelocationFlags::Coff { typ } => match typ { + pe::IMAGE_REL_I386_DIR16 => 2, + pe::IMAGE_REL_I386_REL16 => 2, + pe::IMAGE_REL_I386_DIR32 => 4, + pe::IMAGE_REL_I386_REL32 => 4, + _ => 1, + }, + _ => 1, + } + } } fn replace_arg( diff --git a/objdiff-core/src/diff/code.rs b/objdiff-core/src/diff/code.rs index 3a14e3c..54784a5 100644 --- a/objdiff-core/src/diff/code.rs +++ b/objdiff-core/src/diff/code.rs @@ -196,7 +196,7 @@ fn address_eq(left: &ObjReloc, right: &ObjReloc) -> bool { left.target.address as i64 + left.addend == right.target.address as i64 + right.addend } -fn section_name_eq( +pub fn section_name_eq( left_obj: &ObjInfo, right_obj: &ObjInfo, left_orig_section_index: usize, diff --git a/objdiff-core/src/diff/data.rs b/objdiff-core/src/diff/data.rs index 8498439..35d6e76 100644 --- a/objdiff-core/src/diff/data.rs +++ b/objdiff-core/src/diff/data.rs @@ -1,11 +1,15 @@ -use std::cmp::{max, min, Ordering}; +use std::{ + cmp::{max, min, Ordering}, + ops::Range, +}; use anyhow::{anyhow, Result}; use similar::{capture_diff_slices_deadline, get_diff_ratio, Algorithm}; +use super::code::section_name_eq; use crate::{ diff::{ObjDataDiff, ObjDataDiffKind, ObjSectionDiff, ObjSymbolDiff}, - obj::{ObjInfo, ObjSection, SymbolRef}, + obj::{ObjInfo, ObjReloc, ObjSection, ObjSymbolFlags, SymbolRef}, }; pub fn diff_bss_symbol( @@ -37,8 +41,110 @@ 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; + 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)) + } + (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) + } + (None, None) => symbol_name_matches, + } +} + +/// Compares relocations contained with a certain data range. +/// The ObjDataDiffKind 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, + left_range: Range, + right_range: Range, +) -> Vec<(ObjDataDiffKind, Option, Option)> { + let mut diffs = Vec::new(); + for left_reloc in left.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| { + 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::Replace, Some(left_reloc.clone()), 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()), + )); + } else { + diffs.push(( + ObjDataDiffKind::Replace, + Some(left_reloc.clone()), + Some(right_reloc.clone()), + )); + } + } + for right_reloc in right.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| { + 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::Replace, None, Some(right_reloc.clone()))); + continue; + }; + // No need to check the cases for relocations being deleted or matching again. + // They were already handled in the loop over the left relocs. + } + diffs +} + /// 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, @@ -70,6 +176,94 @@ pub fn diff_data_section( ObjDataDiffKind::Replace } }; + if kind == ObjDataDiffKind::None { + let mut found_any_relocs = false; + let mut left_curr_addr = left_range.start; + let mut right_curr_addr = right_range.start; + for (diff_kind, left_reloc, right_reloc) in diff_data_relocs_for_range( + left_obj, + right_obj, + left, + right, + left_range.clone(), + right_range.clone(), + ) { + found_any_relocs = true; + + if let Some(left_reloc) = left_reloc { + let left_reloc_addr = left_reloc.address as usize; + if left_reloc_addr > left_curr_addr { + let len = left_reloc_addr - left_curr_addr; + let left_data = &left.data[left_curr_addr..left_reloc_addr]; + left_diff.push(ObjDataDiff { + data: left_data[..min(len, left_data.len())].to_vec(), + kind: ObjDataDiffKind::None, + len, + ..Default::default() + }); + } + let reloc_diff_len = left_obj.arch.get_reloc_byte_size(left_reloc.flags); + let left_data = &left.data[left_reloc_addr..left_reloc_addr + reloc_diff_len]; + left_diff.push(ObjDataDiff { + data: left_data[..min(reloc_diff_len, left_data.len())].to_vec(), + kind: diff_kind, + len: reloc_diff_len, + reloc: Some(left_reloc.clone()), + ..Default::default() + }); + left_curr_addr = left_reloc_addr + reloc_diff_len; + } + + if let Some(right_reloc) = right_reloc { + let right_reloc_addr = right_reloc.address as usize; + if right_reloc_addr > right_curr_addr { + let len = right_reloc_addr - right_curr_addr; + let right_data = &right.data[right_curr_addr..right_reloc_addr]; + right_diff.push(ObjDataDiff { + data: right_data[..min(len, right_data.len())].to_vec(), + kind: ObjDataDiffKind::None, + len, + ..Default::default() + }); + } + let reloc_diff_len = right_obj.arch.get_reloc_byte_size(right_reloc.flags); + let right_data = + &right.data[right_reloc_addr..right_reloc_addr + reloc_diff_len]; + right_diff.push(ObjDataDiff { + data: right_data[..min(reloc_diff_len, right_data.len())].to_vec(), + kind: diff_kind, + len: reloc_diff_len, + reloc: Some(right_reloc.clone()), + ..Default::default() + }); + right_curr_addr = right_reloc_addr + reloc_diff_len; + } + } + + if found_any_relocs { + if left_curr_addr < left_range.end - 1 { + let len = left_range.end - left_curr_addr; + let left_data = &left.data[left_curr_addr..left_range.end]; + left_diff.push(ObjDataDiff { + data: left_data[..min(len, left_data.len())].to_vec(), + kind: ObjDataDiffKind::None, + len, + ..Default::default() + }); + } + if right_curr_addr < right_range.end - 1 { + let len = right_range.end - right_curr_addr; + let right_data = &right.data[right_curr_addr..right_range.end]; + right_diff.push(ObjDataDiff { + data: right_data[..min(len, right_data.len())].to_vec(), + kind: ObjDataDiffKind::None, + len, + ..Default::default() + }); + } + continue; + } + } let left_data = &left.data[left_range]; let right_data = &right.data[right_range]; left_diff.push(ObjDataDiff { @@ -123,14 +317,19 @@ pub fn diff_data_section( 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_diff.iter().all(|d| d.kind == ObjDataDiffKind::None || d.reloc.is_none()); left_section_diff.data_diff = left_diff; right_section_diff.data_diff = right_diff; - // Use the highest match percent between two options: - // - Left symbols matching right symbols by name - // - Diff of the data itself - if left_section_diff.match_percent.unwrap_or(-1.0) < match_percent { - left_section_diff.match_percent = Some(match_percent); - right_section_diff.match_percent = Some(match_percent); + if all_left_relocs_match { + // Use the highest match percent between two options: + // - Left symbols matching right symbols by name + // - Diff of the data itself + // We only do this when all relocations on the left side match. + if left_section_diff.match_percent.unwrap_or(-1.0) < match_percent { + left_section_diff.match_percent = Some(match_percent); + right_section_diff.match_percent = Some(match_percent); + } } Ok((left_section_diff, right_section_diff)) } @@ -147,13 +346,54 @@ pub fn diff_data_symbol( 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_data = &left_section.data[left_symbol.section_address as usize - ..(left_symbol.section_address + left_symbol.size) as usize]; - let right_data = &right_section.data[right_symbol.section_address as usize - ..(right_symbol.section_address + right_symbol.size) as usize]; + 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_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_range, + right_range, + ); let ops = capture_diff_slices_deadline(Algorithm::Patience, left_data, right_data, None); - let match_percent = get_diff_ratio(&ops, left_data.len(), right_data.len()) * 100.0; + let bytes_match_ratio = get_diff_ratio(&ops, left_data.len(), right_data.len()); + + let mut match_ratio = bytes_match_ratio; + if !reloc_diffs.is_empty() { + let mut total_reloc_bytes = 0; + let mut matching_reloc_bytes = 0; + 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), + }; + total_reloc_bytes += reloc_diff_len; + if diff_kind == ObjDataDiffKind::None { + matching_reloc_bytes += reloc_diff_len; + } + } + if total_reloc_bytes > 0 { + let relocs_match_ratio = matching_reloc_bytes as f32 / total_reloc_bytes as f32; + // Adjust the overall match ratio to include relocation differences. + // We calculate it so that bytes that contain a relocation are counted twice: once for the + // byte's raw value, and once for its relocation. + // e.g. An 8 byte symbol that has 8 matching raw bytes and a single 4 byte relocation that + // doesn't match would show as 66% (weighted average of 100% and 0%). + match_ratio = ((bytes_match_ratio * (left_data.len() as f32)) + + (relocs_match_ratio * total_reloc_bytes as f32)) + / (left_data.len() + total_reloc_bytes) as f32; + } + } + + let match_percent = match_ratio * 100.0; Ok(( ObjSymbolDiff { diff --git a/objdiff-core/src/diff/mod.rs b/objdiff-core/src/diff/mod.rs index 405645d..9157585 100644 --- a/objdiff-core/src/diff/mod.rs +++ b/objdiff-core/src/diff/mod.rs @@ -11,7 +11,9 @@ use crate::{ diff_generic_section, no_diff_symbol, }, }, - obj::{ObjInfo, ObjIns, ObjSection, ObjSectionKind, ObjSymbol, SymbolRef, SECTION_COMMON}, + obj::{ + ObjInfo, ObjIns, ObjReloc, ObjSection, ObjSectionKind, ObjSymbol, SymbolRef, SECTION_COMMON, + }, }; pub mod code; @@ -85,6 +87,7 @@ pub struct ObjDataDiff { pub kind: ObjDataDiffKind, pub len: usize, pub symbol: String, + pub reloc: Option, } #[derive(Debug, Copy, Clone, Eq, PartialEq, Default)] @@ -153,6 +156,7 @@ impl ObjDiff { kind: ObjDataDiffKind::None, len: section.data.len(), symbol: section.name.clone(), + ..Default::default() }], match_percent: None, }); @@ -345,6 +349,8 @@ pub fn diff_objs( let left_section_diff = left_out.section_diff(left_section_idx); let right_section_diff = right_out.section_diff(right_section_idx); let (left_diff, right_diff) = diff_data_section( + left_obj, + right_obj, left_section, right_section, left_section_diff, diff --git a/objdiff-gui/src/views/data_diff.rs b/objdiff-gui/src/views/data_diff.rs index 37f2a6c..d83eeab 100644 --- a/objdiff-gui/src/views/data_diff.rs +++ b/objdiff-gui/src/views/data_diff.rs @@ -1,4 +1,8 @@ -use std::{cmp::min, default::Default, mem::take}; +use std::{ + cmp::{min, Ordering}, + default::Default, + mem::take, +}; use egui::{text::LayoutJob, Id, Label, RichText, Sense, Widget}; use objdiff_core::{ @@ -23,7 +27,88 @@ fn find_section(obj: &ObjInfo, section_name: &str) -> Option { obj.sections.iter().position(|section| section.name == section_name) } -fn data_row_ui(ui: &mut egui::Ui, address: usize, diffs: &[ObjDataDiff], appearance: &Appearance) { +fn data_row_hover_ui( + ui: &mut egui::Ui, + obj: &ObjInfo, + diffs: &[ObjDataDiff], + appearance: &Appearance, +) { + ui.scope(|ui| { + ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace); + ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend); + + for diff in diffs { + let Some(reloc) = &diff.reloc else { + continue; + }; + + // Use a slightly different font color for nonmatching relocations so they stand out. + let color = match diff.kind { + ObjDataDiffKind::None => appearance.highlight_color, + _ => appearance.replace_color, + }; + + // TODO: Most of this code is copy-pasted from ins_hover_ui. + // Try to separate this out into a shared function. + ui.label(format!("Relocation type: {}", obj.arch.display_reloc(reloc.flags))); + let addend_str = match reloc.addend.cmp(&0i64) { + Ordering::Greater => format!("+{:x}", reloc.addend), + Ordering::Less => format!("-{:x}", -reloc.addend), + _ => "".to_string(), + }; + ui.colored_label(color, format!("Name: {}{}", reloc.target.name, addend_str)); + if let Some(orig_section_index) = reloc.target.orig_section_index { + if let Some(section) = + obj.sections.iter().find(|s| s.orig_index == orig_section_index) + { + ui.colored_label(color, format!("Section: {}", section.name)); + } + ui.colored_label( + color, + format!("Address: {:x}{}", reloc.target.address, addend_str), + ); + ui.colored_label(color, format!("Size: {:x}", reloc.target.size)); + if reloc.addend >= 0 && reloc.target.bytes.len() > reloc.addend as usize {} + } else { + ui.colored_label(color, "Extern".to_string()); + } + } + }); +} + +fn data_row_context_menu(ui: &mut egui::Ui, diffs: &[ObjDataDiff]) { + ui.scope(|ui| { + ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace); + ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend); + + for diff in diffs { + let Some(reloc) = &diff.reloc else { + continue; + }; + + // TODO: This code is copy-pasted from ins_context_menu. + // Try to separate this out into a shared function. + if let Some(name) = &reloc.target.demangled_name { + if ui.button(format!("Copy \"{name}\"")).clicked() { + ui.output_mut(|output| output.copied_text.clone_from(name)); + ui.close_menu(); + } + } + if ui.button(format!("Copy \"{}\"", reloc.target.name)).clicked() { + ui.output_mut(|output| output.copied_text.clone_from(&reloc.target.name)); + ui.close_menu(); + } + } + }); +} + +fn data_row_ui( + ui: &mut egui::Ui, + obj: Option<&ObjInfo>, + address: usize, + diffs: &[ObjDataDiff], + appearance: &Appearance, +) { if diffs.iter().any(|d| d.kind != ObjDataDiffKind::None) { ui.painter().rect_filled(ui.available_rect_before_wrap(), 0.0, ui.visuals().faint_bg_color); } @@ -94,9 +179,13 @@ fn data_row_ui(ui: &mut egui::Ui, address: usize, diffs: &[ObjDataDiff], appeara write_text(text.as_str(), base_color, &mut job, appearance.code_font.clone()); } } - Label::new(job).sense(Sense::click()).ui(ui); - // .on_hover_ui_at_pointer(|ui| ins_hover_ui(ui, ins)) - // .context_menu(|ui| ins_context_menu(ui, ins)); + + let response = Label::new(job).sense(Sense::click()).ui(ui); + if let Some(obj) = obj { + response + .on_hover_ui_at_pointer(|ui| data_row_hover_ui(ui, obj, diffs, appearance)) + .context_menu(|ui| data_row_context_menu(ui, diffs)); + } } fn split_diffs(diffs: &[ObjDataDiff]) -> Vec> { @@ -117,8 +206,8 @@ fn split_diffs(diffs: &[ObjDataDiff]) -> Vec> { }, kind: diff.kind, len, - // TODO - symbol: String::new(), + symbol: String::new(), // TODO + reloc: diff.reloc.clone(), }); remaining_in_row -= len; cur_len += len; @@ -161,6 +250,8 @@ fn data_table_ui( right_ctx: Option>, config: &Appearance, ) -> Option<()> { + let left_obj = left_ctx.map(|ctx| ctx.obj); + let right_obj = right_ctx.map(|ctx| ctx.obj); let left_section = left_ctx .and_then(|ctx| ctx.section_index.map(|i| (&ctx.obj.sections[i], &ctx.diff.sections[i]))); let right_section = right_ctx @@ -187,11 +278,11 @@ fn data_table_ui( row.col(|ui| { if column == 0 { if let Some(left_diffs) = &left_diffs { - data_row_ui(ui, address, &left_diffs[i], config); + data_row_ui(ui, left_obj, address, &left_diffs[i], config); } } else if column == 1 { if let Some(right_diffs) = &right_diffs { - data_row_ui(ui, address, &right_diffs[i], config); + data_row_ui(ui, right_obj, address, &right_diffs[i], config); } } });