diff --git a/objdiff-cli/src/cmd/diff.rs b/objdiff-cli/src/cmd/diff.rs index 4bb001d..d3260ea 100644 --- a/objdiff-cli/src/cmd/diff.rs +++ b/objdiff-cli/src/cmd/diff.rs @@ -16,13 +16,16 @@ use event::KeyModifiers; use objdiff_core::{ config::{ProjectConfig, ProjectObject}, diff, - diff::display::{display_diff, DiffText, HighlightKind}, + diff::{ + display::{display_diff, DiffText, HighlightKind}, + DiffObjsResult, ObjDiff, ObjInsDiffKind, ObjSymbolDiff, SymbolRef, + }, obj, - obj::{ObjInfo, ObjInsDiffKind, ObjSectionKind, ObjSymbol}, + obj::{ObjInfo, ObjSectionKind, ObjSymbol}, }; use ratatui::{ prelude::*, - widgets::{Block, Borders, Paragraph, Scrollbar, ScrollbarOrientation, ScrollbarState}, + widgets::{Block, Borders, Clear, Paragraph, Scrollbar, ScrollbarOrientation, ScrollbarState}, }; use crate::util::term::crossterm_panic_handler; @@ -146,9 +149,7 @@ pub fn run(args: Args) -> Result<()> { let time_format = time::format_description::parse_borrowed::<2>("[hour]:[minute]:[second]") .context("Failed to parse time format")?; let mut state = Box::new(FunctionDiffUi { - redraw: true, relax_reloc_diffs: args.relax_reloc_diffs, - click_xy: None, left_highlight: HighlightKind::None, right_highlight: HighlightKind::None, scroll_x: 0, @@ -161,10 +162,17 @@ pub fn run(args: Args) -> Result<()> { target_path, base_path, project_config, + left_obj: None, + right_obj: None, + prev_obj: None, + diff_result: DiffObjsResult::default(), left_sym: None, right_sym: None, + prev_sym: None, reload_time: None, time_format, + open_options: false, + three_way: false, }); state.reload()?; @@ -180,17 +188,27 @@ pub fn run(args: Args) -> Result<()> { let mut terminal = Terminal::new(backend)?; 'outer: loop { + let mut result = EventResult { redraw: true, ..Default::default() }; loop { - if state.redraw { - terminal.draw(|f| state.draw(f))?; - if state.redraw { - continue; - } + if result.redraw { + terminal.draw(|f| loop { + result.redraw = false; + state.draw(f, &mut result); + if state.open_options { + state.draw_options(f, &mut result); + } + result.click_xy = None; + if !result.redraw { + break; + } + // Clear buffer on redraw + f.buffer_mut().reset(); + })?; } match state.handle_event(event::read()?) { - FunctionDiffResult::Break => break 'outer, - FunctionDiffResult::Continue => {} - FunctionDiffResult::Reload => break, + EventControlFlow::Break => break 'outer, + EventControlFlow::Continue(r) => result = r, + EventControlFlow::Reload => break, } } state.reload()?; @@ -203,14 +221,30 @@ pub fn run(args: Args) -> Result<()> { Ok(()) } -fn find_function(obj: &ObjInfo, name: &str) -> Option { - for section in &obj.sections { +fn get_symbol(obj: Option<&ObjInfo>, sym: Option) -> Option<&ObjSymbol> { + if let (Some(obj), Some(sym)) = (obj, sym) { + obj.sections.get(sym.section_idx).and_then(|s| s.symbols.get(sym.symbol_idx)) + } else { + None + } +} + +fn get_diff_symbol(obj: Option<&ObjDiff>, sym: Option) -> Option<&ObjSymbolDiff> { + if let (Some(obj), Some(sym)) = (obj, sym) { + Some(obj.symbol_diff(sym)) + } else { + None + } +} + +fn find_function(obj: &ObjInfo, name: &str) -> Option { + for (section_idx, section) in obj.sections.iter().enumerate() { if section.kind != ObjSectionKind::Code { continue; } - for symbol in §ion.symbols { + for (symbol_idx, symbol) in section.symbols.iter().enumerate() { if symbol.name == name { - return Some(symbol.clone()); + return Some(SymbolRef { section_idx, symbol_idx }); } } } @@ -219,9 +253,7 @@ fn find_function(obj: &ObjInfo, name: &str) -> Option { #[allow(dead_code)] struct FunctionDiffUi { - redraw: bool, relax_reloc_diffs: bool, - click_xy: Option<(u16, u16)>, left_highlight: HighlightKind, right_highlight: HighlightKind, scroll_x: usize, @@ -234,20 +266,33 @@ struct FunctionDiffUi { target_path: Option, base_path: Option, project_config: Option, - left_sym: Option, - right_sym: Option, + left_obj: Option, + right_obj: Option, + prev_obj: Option, + diff_result: DiffObjsResult, + left_sym: Option, + right_sym: Option, + prev_sym: Option, reload_time: Option, time_format: Vec>, + open_options: bool, + three_way: bool, +} + +#[derive(Default)] +struct EventResult { + redraw: bool, + click_xy: Option<(u16, u16)>, } -enum FunctionDiffResult { +enum EventControlFlow { Break, - Continue, + Continue(EventResult), Reload, } impl FunctionDiffUi { - fn draw(&mut self, f: &mut Frame) { + fn draw(&mut self, f: &mut Frame, result: &mut EventResult) { let chunks = Layout::vertical([Constraint::Length(1), Constraint::Fill(1)]).split(f.size()); let header_chunks = Layout::horizontal([ Constraint::Fill(1), @@ -256,13 +301,25 @@ impl FunctionDiffUi { Constraint::Length(2), ]) .split(chunks[0]); - let content_chunks = Layout::horizontal([ - Constraint::Fill(1), - Constraint::Length(3), - Constraint::Fill(1), - Constraint::Length(2), - ]) - .split(chunks[1]); + let content_chunks = if self.three_way { + Layout::horizontal([ + Constraint::Fill(1), + Constraint::Length(3), + Constraint::Fill(1), + Constraint::Length(3), + Constraint::Fill(1), + Constraint::Length(2), + ]) + .split(chunks[1]) + } else { + Layout::horizontal([ + Constraint::Fill(1), + Constraint::Length(3), + Constraint::Fill(1), + Constraint::Length(2), + ]) + .split(chunks[1]) + }; self.per_page = chunks[1].height.saturating_sub(2) as usize; let max_scroll_y = self.num_rows.saturating_sub(self.per_page); @@ -279,7 +336,9 @@ impl FunctionDiffUi { f.render_widget(line_l, header_chunks[0]); let mut line_r = Line::default(); - if let Some(percent) = self.right_sym.as_ref().and_then(|s| s.match_percent) { + if let Some(percent) = get_diff_symbol(self.diff_result.right.as_ref(), self.right_sym) + .and_then(|s| s.match_percent) + { line_r.spans.push(Span::styled( format!("{:.2}% ", percent), Style::new().fg(match_percent_color(percent)), @@ -296,49 +355,82 @@ impl FunctionDiffUi { )); f.render_widget(line_r, header_chunks[2]); - let create_block = - |title: &'static str| Block::new().borders(Borders::TOP).gray().title(title.bold()); - + let mut left_text = None; let mut left_highlight = None; let mut max_width = 0; - if let Some(symbol) = &self.left_sym { - // Render left column + if let (Some(symbol), Some(symbol_diff)) = ( + get_symbol(self.left_obj.as_ref(), self.left_sym), + get_diff_symbol(self.diff_result.left.as_ref(), self.left_sym), + ) { let mut text = Text::default(); let rect = content_chunks[0].inner(&Margin::new(0, 1)); - let h = self.print_sym(&mut text, symbol, rect, &self.left_highlight); - max_width = max_width.max(text.width()); - f.render_widget( - Paragraph::new(text) - .block(create_block("TARGET")) - .scroll((0, self.scroll_x as u16)), - content_chunks[0], + left_highlight = self.print_sym( + &mut text, + symbol, + symbol_diff, + rect, + &self.left_highlight, + result, + false, ); - if let Some(h) = h { - left_highlight = Some(h); - } + max_width = max_width.max(text.width()); + left_text = Some(text); } + let mut right_text = None; let mut right_highlight = None; - if let Some(symbol) = &self.right_sym { + let mut margin_text = None; + if let (Some(symbol), Some(symbol_diff)) = ( + get_symbol(self.right_obj.as_ref(), self.right_sym), + get_diff_symbol(self.diff_result.right.as_ref(), self.right_sym), + ) { + let mut text = Text::default(); + let rect = content_chunks[2].inner(&Margin::new(0, 1)); + right_highlight = self.print_sym( + &mut text, + symbol, + symbol_diff, + rect, + &self.right_highlight, + result, + false, + ); + max_width = max_width.max(text.width()); + right_text = Some(text); + // Render margin let mut text = Text::default(); let rect = content_chunks[1].inner(&Margin::new(1, 1)); - self.print_margin(&mut text, symbol, rect); - f.render_widget(text, rect); + self.print_margin(&mut text, symbol_diff, rect); + margin_text = Some(text); + } - // Render right column - let mut text = Text::default(); - let rect = content_chunks[2].inner(&Margin::new(0, 1)); - let h = self.print_sym(&mut text, symbol, rect, &self.right_highlight); - max_width = max_width.max(text.width()); - f.render_widget( - Paragraph::new(text) - .block(create_block("CURRENT")) - .scroll((0, self.scroll_x as u16)), - content_chunks[2], - ); - if let Some(h) = h { - right_highlight = Some(h); + let mut prev_text = None; + let mut prev_margin_text = None; + if self.three_way { + if let (Some(symbol), Some(symbol_diff)) = ( + get_symbol(self.prev_obj.as_ref(), self.prev_sym), + get_diff_symbol(self.diff_result.prev.as_ref(), self.prev_sym), + ) { + let mut text = Text::default(); + let rect = content_chunks[4].inner(&Margin::new(0, 1)); + self.print_sym( + &mut text, + symbol, + symbol_diff, + rect, + &self.right_highlight, + result, + true, + ); + max_width = max_width.max(text.width()); + prev_text = Some(text); + + // Render margin + let mut text = Text::default(); + let rect = content_chunks[3].inner(&Margin::new(1, 1)); + self.print_margin(&mut text, symbol_diff, rect); + prev_margin_text = Some(text); } } @@ -350,6 +442,42 @@ impl FunctionDiffUi { self.scroll_state_x = self.scroll_state_x.content_length(max_scroll_x).position(self.scroll_x); + if let Some(text) = left_text { + // Render left column + f.render_widget( + Paragraph::new(text) + .block(Block::new().borders(Borders::TOP).gray().title("TARGET".bold())) + .scroll((0, self.scroll_x as u16)), + content_chunks[0], + ); + } + if let Some(text) = margin_text { + f.render_widget(text, content_chunks[1].inner(&Margin::new(1, 1))); + } + if let Some(text) = right_text { + f.render_widget( + Paragraph::new(text) + .block(Block::new().borders(Borders::TOP).gray().title("CURRENT".bold())) + .scroll((0, self.scroll_x as u16)), + content_chunks[2], + ); + } + + if self.three_way { + if let Some(text) = prev_margin_text { + f.render_widget(text, content_chunks[3].inner(&Margin::new(1, 1))); + } + let block = Block::new().borders(Borders::TOP).gray().title("SAVED".bold()); + if let Some(text) = prev_text { + f.render_widget( + Paragraph::new(text).block(block.clone()).scroll((0, self.scroll_x as u16)), + content_chunks[4], + ); + } else { + f.render_widget(block, content_chunks[4]); + } + } + // Render scrollbars f.render_stateful_widget( Scrollbar::new(ScrollbarOrientation::VerticalRight).begin_symbol(None).end_symbol(None), @@ -366,6 +494,13 @@ impl FunctionDiffUi { content_chunks[2], &mut self.scroll_state_x, ); + if self.three_way { + f.render_stateful_widget( + Scrollbar::new(ScrollbarOrientation::HorizontalBottom).thumb_symbol("■"), + content_chunks[4], + &mut self.scroll_state_x, + ); + } if let Some(new_highlight) = left_highlight { if new_highlight == self.left_highlight { @@ -378,8 +513,7 @@ impl FunctionDiffUi { } else { self.left_highlight = new_highlight; } - self.redraw = true; - self.click_xy = None; + result.redraw = true; } else if let Some(new_highlight) = right_highlight { if new_highlight == self.right_highlight { if self.left_highlight != self.right_highlight { @@ -391,86 +525,128 @@ impl FunctionDiffUi { } else { self.right_highlight = new_highlight; } - self.redraw = true; - self.click_xy = None; - } else { - self.redraw = false; - self.click_xy = None; + result.redraw = true; } } - fn handle_event(&mut self, event: Event) -> FunctionDiffResult { + fn draw_options(&mut self, f: &mut Frame, _result: &mut EventResult) { + let percent_x = 50; + let percent_y = 50; + let popup_rect = Layout::vertical([ + Constraint::Percentage((100 - percent_y) / 2), + Constraint::Percentage(percent_y), + Constraint::Percentage((100 - percent_y) / 2), + ]) + .split(f.size())[1]; + let popup_rect = Layout::horizontal([ + Constraint::Percentage((100 - percent_x) / 2), + Constraint::Percentage(percent_x), + Constraint::Percentage((100 - percent_x) / 2), + ]) + .split(popup_rect)[1]; + + let popup = Block::default() + .borders(Borders::ALL) + .title("Options") + .title_style(Style::default().fg(Color::White).bg(Color::Black)); + f.render_widget(Clear, popup_rect); + f.render_widget(popup, popup_rect); + } + + fn handle_event(&mut self, event: Event) -> EventControlFlow { + let mut result = EventResult::default(); match event { Event::Key(event) if matches!(event.kind, KeyEventKind::Press | KeyEventKind::Repeat) => { match event.code { // Quit - KeyCode::Esc | KeyCode::Char('q') => return FunctionDiffResult::Break, + KeyCode::Esc | KeyCode::Char('q') => return EventControlFlow::Break, // Page up - KeyCode::PageUp => self.page_up(false), + KeyCode::PageUp => { + self.page_up(false); + result.redraw = true; + } // Page up (shift + space) KeyCode::Char(' ') if event.modifiers.contains(KeyModifiers::SHIFT) => { - self.page_up(false) + self.page_up(false); + result.redraw = true; } // Page down - KeyCode::Char(' ') | KeyCode::PageDown => self.page_down(false), + KeyCode::Char(' ') | KeyCode::PageDown => { + self.page_down(false); + result.redraw = true; + } // Page down (ctrl + f) KeyCode::Char('f') if event.modifiers.contains(KeyModifiers::CONTROL) => { - self.page_down(false) + self.page_down(false); + result.redraw = true; } // Page up (ctrl + b) KeyCode::Char('b') if event.modifiers.contains(KeyModifiers::CONTROL) => { - self.page_up(false) + self.page_up(false); + result.redraw = true; } // Half page down (ctrl + d) KeyCode::Char('d') if event.modifiers.contains(KeyModifiers::CONTROL) => { - self.page_down(true) + self.page_down(true); + result.redraw = true; } // Half page up (ctrl + u) KeyCode::Char('u') if event.modifiers.contains(KeyModifiers::CONTROL) => { - self.page_up(true) + self.page_up(true); + result.redraw = true; } // Scroll down KeyCode::Down | KeyCode::Char('j') => { self.scroll_y += 1; - self.redraw = true; + result.redraw = true; } // Scroll up KeyCode::Up | KeyCode::Char('k') => { self.scroll_y = self.scroll_y.saturating_sub(1); - self.redraw = true; + result.redraw = true; } // Scroll to start KeyCode::Char('g') => { self.scroll_y = 0; - self.redraw = true; + result.redraw = true; } // Scroll to end KeyCode::Char('G') => { self.scroll_y = self.num_rows; - self.redraw = true; + result.redraw = true; } // Reload KeyCode::Char('r') => { - self.redraw = true; - return FunctionDiffResult::Reload; + result.redraw = true; + return EventControlFlow::Reload; } // Scroll right KeyCode::Right | KeyCode::Char('l') => { self.scroll_x += 1; - self.redraw = true; + result.redraw = true; } // Scroll left KeyCode::Left | KeyCode::Char('h') => { self.scroll_x = self.scroll_x.saturating_sub(1); - self.redraw = true; + result.redraw = true; } // Toggle relax relocation diffs KeyCode::Char('x') => { self.relax_reloc_diffs = !self.relax_reloc_diffs; - self.redraw = true; - return FunctionDiffResult::Reload; + result.redraw = true; + return EventControlFlow::Reload; + } + // Toggle three-way diff + KeyCode::Char('3') => { + self.three_way = !self.three_way; + result.redraw = true; + } + // Toggle options + KeyCode::Char('o') => { + self.open_options = !self.open_options; + result.redraw = true; } _ => {} } @@ -478,56 +654,66 @@ impl FunctionDiffUi { Event::Mouse(event) => match event.kind { MouseEventKind::ScrollDown => { self.scroll_y += 3; - self.redraw = true; + result.redraw = true; } MouseEventKind::ScrollUp => { self.scroll_y = self.scroll_y.saturating_sub(3); - self.redraw = true; + result.redraw = true; } MouseEventKind::ScrollRight => { self.scroll_x += 3; - self.redraw = true; + result.redraw = true; } MouseEventKind::ScrollLeft => { self.scroll_x = self.scroll_x.saturating_sub(3); - self.redraw = true; + result.redraw = true; } MouseEventKind::Down(MouseButton::Left) => { - self.click_xy = Some((event.column, event.row)); - self.redraw = true; + result.click_xy = Some((event.column, event.row)); + result.redraw = true; } _ => {} }, Event::Resize(_, _) => { - self.redraw = true; + result.redraw = true; } _ => {} } - FunctionDiffResult::Continue + EventControlFlow::Continue(result) } fn page_up(&mut self, half: bool) { self.scroll_y = self.scroll_y.saturating_sub(self.per_page / if half { 2 } else { 1 }); - self.redraw = true; } fn page_down(&mut self, half: bool) { self.scroll_y += self.per_page / if half { 2 } else { 1 }; - self.redraw = true; } + #[allow(clippy::too_many_arguments)] fn print_sym( &self, - out: &mut Text, + out: &mut Text<'static>, symbol: &ObjSymbol, + symbol_diff: &ObjSymbolDiff, rect: Rect, highlight: &HighlightKind, + result: &EventResult, + only_changed: bool, ) -> Option { let base_addr = symbol.address; let mut new_highlight = None; - for (y, ins_diff) in - symbol.instructions.iter().skip(self.scroll_y).take(rect.height as usize).enumerate() + for (y, ins_diff) in symbol_diff + .instructions + .iter() + .skip(self.scroll_y) + .take(rect.height as usize) + .enumerate() { + if only_changed && ins_diff.kind == ObjInsDiffKind::None { + out.lines.push(Line::default()); + continue; + } let mut sx = rect.x; let sy = rect.y + y as u16; let mut line = Line::default(); @@ -591,7 +777,7 @@ impl FunctionDiffUi { } let len = label_text.len(); let highlighted = *highlight == text; - if let Some((cx, cy)) = self.click_xy { + if let Some((cx, cy)) = result.click_xy { if cx >= sx && cx < sx + len as u16 && cy == sy { new_highlight = Some(text.into()); } @@ -615,7 +801,7 @@ impl FunctionDiffUi { new_highlight } - fn print_margin(&self, out: &mut Text, symbol: &ObjSymbol, rect: Rect) { + fn print_margin(&self, out: &mut Text, symbol: &ObjSymbolDiff, rect: Rect) { for ins_diff in symbol.instructions.iter().skip(self.scroll_y).take(rect.height as usize) { if ins_diff.kind != ObjInsDiffKind::None { out.lines.push(Line::raw(match ins_diff.kind { @@ -630,12 +816,13 @@ impl FunctionDiffUi { } fn reload(&mut self) -> Result<()> { - let mut target = self + let prev = self.right_obj.take(); + let target = self .target_path .as_deref() .map(|p| obj::read::read(p).with_context(|| format!("Loading {}", p.display()))) .transpose()?; - let mut base = self + let base = self .base_path .as_deref() .map(|p| obj::read::read(p).with_context(|| format!("Loading {}", p.display()))) @@ -645,18 +832,27 @@ impl FunctionDiffUi { space_between_args: true, // TODO x86_formatter: Default::default(), // TODO }; - diff::diff_objs(&config, target.as_mut(), base.as_mut())?; + let result = diff::diff_objs(&config, target.as_ref(), base.as_ref(), prev.as_ref())?; let left_sym = target.as_ref().and_then(|o| find_function(o, &self.symbol_name)); let right_sym = base.as_ref().and_then(|o| find_function(o, &self.symbol_name)); - self.num_rows = match (&left_sym, &right_sym) { + let prev_sym = prev.as_ref().and_then(|o| find_function(o, &self.symbol_name)); + self.num_rows = match ( + get_diff_symbol(result.left.as_ref(), left_sym), + get_diff_symbol(result.right.as_ref(), right_sym), + ) { (Some(l), Some(r)) => l.instructions.len().max(r.instructions.len()), (Some(l), None) => l.instructions.len(), (None, Some(r)) => r.instructions.len(), (None, None) => bail!("Symbol not found: {}", self.symbol_name), }; + self.left_obj = target; + self.right_obj = base; + self.prev_obj = prev; + self.diff_result = result; self.left_sym = left_sym; self.right_sym = right_sym; + self.prev_sym = prev_sym; self.reload_time = time::OffsetDateTime::now_local().ok(); Ok(()) } diff --git a/objdiff-cli/src/cmd/report.rs b/objdiff-cli/src/cmd/report.rs index 80181db..c7077a5 100644 --- a/objdiff-cli/src/cmd/report.rs +++ b/objdiff-cli/src/cmd/report.rs @@ -216,18 +216,18 @@ fn report_object( _ => {} } // println!("Checking {}", object.name()); - let mut target = object + let target = object .target_path .as_ref() .map(|p| obj::read::read(p).with_context(|| format!("Failed to open {}", p.display()))) .transpose()?; - let mut base = object + let base = object .base_path .as_ref() .map(|p| obj::read::read(p).with_context(|| format!("Failed to open {}", p.display()))) .transpose()?; let config = diff::DiffObjConfig { relax_reloc_diffs: true, ..Default::default() }; - diff::diff_objs(&config, target.as_mut(), base.as_mut())?; + let result = diff::diff_objs(&config, target.as_ref(), base.as_ref(), None)?; let mut unit = ReportUnit { name: object.name().to_string(), complete: object.complete, @@ -239,11 +239,12 @@ fn report_object( ..Default::default() }; let obj = target.as_ref().or(base.as_ref()).unwrap(); - for section in &obj.sections { + let obj_diff = result.left.as_ref().or(result.right.as_ref()).unwrap(); + for (section, section_diff) in obj.sections.iter().zip(&obj_diff.sections) { if section.kind != ObjSectionKind::Code { continue; } - for symbol in §ion.symbols { + for (symbol, symbol_diff) in section.symbols.iter().zip(§ion_diff.symbols) { if symbol.size == 0 { continue; } @@ -255,7 +256,7 @@ fn report_object( continue; } } - let match_percent = symbol.match_percent.unwrap_or_else(|| { + let match_percent = symbol_diff.match_percent.unwrap_or_else(|| { // Support cases where we don't have a target object, // assume complete means 100% match if object.complete == Some(true) { diff --git a/objdiff-core/src/arch/mips.rs b/objdiff-core/src/arch/mips.rs index cd0dbf1..2fb671d 100644 --- a/objdiff-core/src/arch/mips.rs +++ b/objdiff-core/src/arch/mips.rs @@ -5,8 +5,8 @@ use object::{elf, Endian, Endianness, File, Object, Relocation, RelocationFlags} use rabbitizer::{config, Abi, InstrCategory, Instruction, OperandType}; use crate::{ - arch::ObjArch, - diff::{DiffObjConfig, ProcessCodeResult}, + arch::{ObjArch, ProcessCodeResult}, + diff::DiffObjConfig, obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection}, }; diff --git a/objdiff-core/src/arch/mod.rs b/objdiff-core/src/arch/mod.rs index dae0f8a..03ce975 100644 --- a/objdiff-core/src/arch/mod.rs +++ b/objdiff-core/src/arch/mod.rs @@ -4,8 +4,8 @@ use anyhow::{bail, Result}; use object::{Architecture, Object, Relocation, RelocationFlags}; use crate::{ - diff::{DiffObjConfig, ProcessCodeResult}, - obj::{ObjReloc, ObjSection}, + diff::DiffObjConfig, + obj::{ObjIns, ObjReloc, ObjSection}, }; #[cfg(feature = "mips")] @@ -33,6 +33,11 @@ pub trait ObjArch: Send + Sync { fn display_reloc(&self, flags: RelocationFlags) -> Cow<'static, str>; } +pub struct ProcessCodeResult { + pub ops: Vec, + pub insts: Vec, +} + pub fn new_arch(object: &object::File) -> Result> { Ok(match object.architecture() { #[cfg(feature = "ppc")] diff --git a/objdiff-core/src/arch/ppc.rs b/objdiff-core/src/arch/ppc.rs index 4800604..e74fb39 100644 --- a/objdiff-core/src/arch/ppc.rs +++ b/objdiff-core/src/arch/ppc.rs @@ -5,8 +5,8 @@ use object::{elf, File, Relocation, RelocationFlags}; use ppc750cl::{disasm_iter, Argument, SimplifiedIns, GPR}; use crate::{ - arch::ObjArch, - diff::{DiffObjConfig, ProcessCodeResult}, + arch::{ObjArch, ProcessCodeResult}, + diff::DiffObjConfig, obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection}, }; diff --git a/objdiff-core/src/arch/x86.rs b/objdiff-core/src/arch/x86.rs index 17bae42..78f8d68 100644 --- a/objdiff-core/src/arch/x86.rs +++ b/objdiff-core/src/arch/x86.rs @@ -4,13 +4,13 @@ use anyhow::{anyhow, bail, ensure, Result}; use iced_x86::{ Decoder, DecoderOptions, DecoratorKind, Formatter, FormatterOutput, FormatterTextKind, GasFormatter, Instruction, IntelFormatter, MasmFormatter, NasmFormatter, NumberKind, OpKind, - PrefixKind, Register, SymbolResult, + PrefixKind, Register, }; use object::{pe, Endian, Endianness, File, Object, Relocation, RelocationFlags}; use crate::{ - arch::ObjArch, - diff::{DiffObjConfig, ProcessCodeResult, X86Formatter}, + arch::{ObjArch, ProcessCodeResult}, + diff::{DiffObjConfig, X86Formatter}, obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection}, }; @@ -88,29 +88,6 @@ impl ObjArch for ObjArchX86 { ensure!(output.ins_operands.len() == output.ins.args.len()); output.ins.orig = Some(output.formatted.clone()); - // print!("{:016X} ", instruction.ip()); - // let start_index = (instruction.ip() - address) as usize; - // let instr_bytes = &data[start_index..start_index + instruction.len()]; - // for b in instr_bytes.iter() { - // print!("{:02X}", b); - // } - // if instr_bytes.len() < 32 { - // for _ in 0..32 - instr_bytes.len() { - // print!(" "); - // } - // } - // println!(" {}", output.formatted); - // - // if let Some(reloc) = reloc { - // println!("\tReloc: {:?}", reloc); - // } - // - // for i in 0..instruction.op_count() { - // let kind = instruction.op_kind(i); - // print!("{:?} ", kind); - // } - // println!(); - // Make sure we've put the relocation somewhere in the instruction if reloc.is_some() && !output.ins.args.iter().any(|a| matches!(a, ObjInsArg::Reloc)) { let mut found = replace_arg( @@ -229,7 +206,6 @@ impl InstructionFormatterOutput { impl FormatterOutput for InstructionFormatterOutput { fn write(&mut self, text: &str, kind: FormatterTextKind) { - // log::debug!("write {} {:?}", text, kind); self.formatted.push_str(text); // Skip whitespace after the mnemonic if self.ins.args.is_empty() && kind == FormatterTextKind::Text { @@ -252,14 +228,12 @@ impl FormatterOutput for InstructionFormatterOutput { } fn write_prefix(&mut self, _instruction: &Instruction, text: &str, _prefix: PrefixKind) { - // log::debug!("write_prefix {} {:?}", text, prefix); self.formatted.push_str(text); self.ins_operands.push(None); self.ins.args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(text.to_string()))); } fn write_mnemonic(&mut self, _instruction: &Instruction, text: &str) { - // log::debug!("write_mnemonic {}", text); self.formatted.push_str(text); self.ins.mnemonic = text.to_string(); } @@ -274,7 +248,6 @@ impl FormatterOutput for InstructionFormatterOutput { number_kind: NumberKind, kind: FormatterTextKind, ) { - // log::debug!("write_number {} {:?} {} {} {:?} {:?}", operand, instruction_operand, text, value, number_kind, kind); self.formatted.push_str(text); self.ins_operands.push(instruction_operand); @@ -343,7 +316,6 @@ impl FormatterOutput for InstructionFormatterOutput { text: &str, _decorator: DecoratorKind, ) { - // log::debug!("write_decorator {} {:?} {} {:?}", operand, instruction_operand, text, decorator); self.formatted.push_str(text); self.ins_operands.push(instruction_operand); self.ins.args.push(ObjInsArg::PlainText(text.to_string())); @@ -357,22 +329,8 @@ impl FormatterOutput for InstructionFormatterOutput { text: &str, _register: Register, ) { - // log::debug!("write_register {} {:?} {} {:?}", operand, instruction_operand, text, register); self.formatted.push_str(text); self.ins_operands.push(instruction_operand); self.ins.args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(text.to_string()))); } - - fn write_symbol( - &mut self, - _instruction: &Instruction, - _operand: u32, - _instruction_operand: Option, - _address: u64, - _symbol: &SymbolResult<'_>, - ) { - if self.error.is_none() { - self.error = Some(anyhow!("x86: Unsupported write_symbol")); - } - } } diff --git a/objdiff-core/src/diff/code.rs b/objdiff-core/src/diff/code.rs index 3323e32..832027a 100644 --- a/objdiff-core/src/diff/code.rs +++ b/objdiff-core/src/diff/code.rs @@ -8,56 +8,66 @@ use anyhow::Result; use similar::{capture_diff_slices_deadline, Algorithm}; use crate::{ - arch::ObjArch, - diff::{DiffObjConfig, ProcessCodeResult}, - obj::{ - ObjInfo, ObjInsArg, ObjInsArgDiff, ObjInsBranchFrom, ObjInsBranchTo, ObjInsDiff, - ObjInsDiffKind, ObjReloc, ObjSymbol, ObjSymbolFlags, + arch::ProcessCodeResult, + diff::{ + get_symbol, DiffObjConfig, ObjInsArgDiff, ObjInsBranchFrom, ObjInsBranchTo, ObjInsDiff, + ObjInsDiffKind, ObjSymbolDiff, SymbolRef, }, + obj::{ObjInfo, ObjInsArg, ObjReloc, ObjSymbol, ObjSymbolFlags}, }; pub fn no_diff_code( - arch: &dyn ObjArch, + obj: &ObjInfo, + symbol_ref: SymbolRef, config: &DiffObjConfig, - data: &[u8], - symbol: &mut ObjSymbol, - relocs: &[ObjReloc], - line_info: &Option>, -) -> Result<()> { - let code = - &data[symbol.section_address as usize..(symbol.section_address + symbol.size) as usize]; - let out = arch.process_code(config, code, symbol.address, relocs, line_info)?; +) -> Result { + let section = &obj.sections[symbol_ref.section_idx]; + let symbol = §ion.symbols[symbol_ref.symbol_idx]; + let code = §ion.data + [symbol.section_address as usize..(symbol.section_address + symbol.size) as usize]; + let out = obj.arch.process_code( + config, + code, + symbol.address, + §ion.relocations, + &obj.line_info, + )?; let mut diff = Vec::::new(); for i in out.insts { diff.push(ObjInsDiff { ins: Some(i), kind: ObjInsDiffKind::None, ..Default::default() }); } resolve_branches(&mut diff); - symbol.instructions = diff; - Ok(()) + Ok(ObjSymbolDiff { diff_symbol: None, instructions: diff, match_percent: None }) } -#[allow(clippy::too_many_arguments)] pub fn diff_code( - arch: &dyn ObjArch, + left_obj: &ObjInfo, + right_obj: &ObjInfo, + left_symbol_ref: SymbolRef, + right_symbol_ref: SymbolRef, config: &DiffObjConfig, - left_data: &[u8], - right_data: &[u8], - left_symbol: &mut ObjSymbol, - right_symbol: &mut ObjSymbol, - left_relocs: &[ObjReloc], - right_relocs: &[ObjReloc], - left_line_info: &Option>, - right_line_info: &Option>, -) -> Result<()> { - let left_code = &left_data[left_symbol.section_address as usize +) -> Result<(ObjSymbolDiff, ObjSymbolDiff)> { + let (left_section, left_symbol) = get_symbol(left_obj, left_symbol_ref); + let (right_section, right_symbol) = get_symbol(right_obj, right_symbol_ref); + let left_code = &left_section.data[left_symbol.section_address as usize ..(left_symbol.section_address + left_symbol.size) as usize]; - let right_code = &right_data[right_symbol.section_address as usize + let right_code = &right_section.data[right_symbol.section_address as usize ..(right_symbol.section_address + right_symbol.size) as usize]; - let left_out = - arch.process_code(config, left_code, left_symbol.address, left_relocs, left_line_info)?; - let right_out = - arch.process_code(config, right_code, right_symbol.address, right_relocs, right_line_info)?; + let left_out = left_obj.arch.process_code( + config, + left_code, + left_symbol.address, + &left_section.relocations, + &left_obj.line_info, + )?; + let right_out = left_obj.arch.process_code( + config, + right_code, + right_symbol.address, + &right_section.relocations, + &right_obj.line_info, + )?; let mut left_diff = Vec::::new(); let mut right_diff = Vec::::new(); @@ -81,13 +91,19 @@ pub fn diff_code( } else { ((total - diff_state.diff_count) as f32 / total as f32) * 100.0 }; - left_symbol.match_percent = Some(percent); - right_symbol.match_percent = Some(percent); - left_symbol.instructions = left_diff; - right_symbol.instructions = right_diff; - - Ok(()) + Ok(( + ObjSymbolDiff { + diff_symbol: Some(right_symbol_ref), + instructions: left_diff, + match_percent: Some(percent), + }, + ObjSymbolDiff { + diff_symbol: Some(left_symbol_ref), + instructions: right_diff, + match_percent: Some(percent), + }, + )) } fn diff_instructions( @@ -223,6 +239,9 @@ fn arg_eq( }, ObjInsArg::Arg(l) => match right { ObjInsArg::Arg(r) => l == r, + // If relocations are relaxed, match if left is a constant and right is a reloc + // Useful for instances where the target object is created without relocations + ObjInsArg::Reloc => config.relax_reloc_diffs, _ => false, }, ObjInsArg::Reloc => { @@ -334,14 +353,3 @@ fn compare_ins( } Ok(result) } - -pub fn find_section_and_symbol(obj: &ObjInfo, name: &str) -> Option<(usize, usize)> { - for (section_idx, section) in obj.sections.iter().enumerate() { - let symbol_idx = match section.symbols.iter().position(|symbol| symbol.name == name) { - Some(symbol_idx) => symbol_idx, - None => continue, - }; - return Some((section_idx, symbol_idx)); - } - None -} diff --git a/objdiff-core/src/diff/data.rs b/objdiff-core/src/diff/data.rs index 30ece33..27e6b47 100644 --- a/objdiff-core/src/diff/data.rs +++ b/objdiff-core/src/diff/data.rs @@ -6,25 +6,42 @@ use std::{ use anyhow::Result; use similar::{capture_diff_slices_deadline, Algorithm}; -use crate::obj::{ObjDataDiff, ObjDataDiffKind, ObjSection, ObjSymbol}; +use crate::{ + diff::{get_symbol, ObjDataDiff, ObjDataDiffKind, ObjSectionDiff, ObjSymbolDiff, SymbolRef}, + obj::{ObjInfo, ObjSection}, +}; -pub fn diff_bss_symbols( - left_symbols: &mut [ObjSymbol], - right_symbols: &mut [ObjSymbol], -) -> Result<()> { - for left_symbol in left_symbols { - if let Some(right_symbol) = right_symbols.iter_mut().find(|s| s.name == left_symbol.name) { - left_symbol.diff_symbol = Some(right_symbol.name.clone()); - right_symbol.diff_symbol = Some(left_symbol.name.clone()); - let percent = if left_symbol.size == right_symbol.size { 100.0 } else { 50.0 }; - left_symbol.match_percent = Some(percent); - right_symbol.match_percent = Some(percent); - } - } - Ok(()) +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) = get_symbol(left_obj, left_symbol_ref); + let (_, right_symbol) = get_symbol(right_obj, right_symbol_ref); + let percent = if left_symbol.size == right_symbol.size { 100.0 } else { 50.0 }; + Ok(( + ObjSymbolDiff { + diff_symbol: Some(right_symbol_ref), + instructions: vec![], + match_percent: Some(percent), + }, + ObjSymbolDiff { + diff_symbol: Some(left_symbol_ref), + instructions: vec![], + match_percent: Some(percent), + }, + )) } -pub fn diff_data(left: &mut ObjSection, right: &mut ObjSection) -> Result<()> { +pub fn no_diff_bss_symbol(_obj: &ObjInfo, _symbol_ref: SymbolRef) -> ObjSymbolDiff { + ObjSymbolDiff { diff_symbol: None, instructions: vec![], match_percent: Some(0.0) } +} + +pub fn diff_data( + left: &ObjSection, + right: &ObjSection, +) -> Result<(ObjSectionDiff, ObjSectionDiff)> { let deadline = Instant::now() + Duration::from_secs(5); let ops = capture_diff_slices_deadline(Algorithm::Patience, &left.data, &right.data, Some(deadline)); @@ -97,16 +114,18 @@ pub fn diff_data(left: &mut ObjSection, right: &mut ObjSection) -> Result<()> { } } - left.data_diff = left_diff; - right.data_diff = right_diff; - Ok(()) -} - -pub fn no_diff_data(section: &mut ObjSection) { - section.data_diff = vec![ObjDataDiff { - data: section.data.clone(), - kind: ObjDataDiffKind::None, - len: section.data.len(), - symbol: String::new(), - }]; + Ok(( + ObjSectionDiff { + symbols: vec![], + data_diff: left_diff, + // TODO + match_percent: None, + }, + ObjSectionDiff { + symbols: vec![], + data_diff: right_diff, + // TODO + match_percent: None, + }, + )) } diff --git a/objdiff-core/src/diff/display.rs b/objdiff-core/src/diff/display.rs index b85d632..f92dd2a 100644 --- a/objdiff-core/src/diff/display.rs +++ b/objdiff-core/src/diff/display.rs @@ -1,6 +1,9 @@ use std::cmp::Ordering; -use crate::obj::{ObjInsArg, ObjInsArgDiff, ObjInsArgValue, ObjInsDiff, ObjReloc, ObjSymbol}; +use crate::{ + diff::{ObjInsArgDiff, ObjInsDiff}, + obj::{ObjInsArg, ObjInsArgValue, ObjReloc, ObjSymbol}, +}; #[derive(Debug, Clone)] pub enum DiffText<'a> { diff --git a/objdiff-core/src/diff/mod.rs b/objdiff-core/src/diff/mod.rs index f8e32a6..421b0cf 100644 --- a/objdiff-core/src/diff/mod.rs +++ b/objdiff-core/src/diff/mod.rs @@ -1,15 +1,15 @@ -pub mod code; -pub mod data; +mod code; +mod data; pub mod display; use anyhow::Result; use crate::{ diff::{ - code::{diff_code, find_section_and_symbol, no_diff_code}, - data::{diff_bss_symbols, diff_data, no_diff_data}, + code::{diff_code, no_diff_code}, + data::{diff_bss_symbol, diff_data, no_diff_bss_symbol}, }, - obj::{ObjInfo, ObjIns, ObjSectionKind}, + obj::{ObjInfo, ObjIns, ObjSection, ObjSectionKind, ObjSymbol}, }; #[derive(Debug, Copy, Clone, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize)] @@ -29,90 +29,414 @@ pub struct DiffObjConfig { pub x86_formatter: X86Formatter, } -pub struct ProcessCodeResult { - pub ops: Vec, - pub insts: Vec, +#[derive(Debug, Clone)] +pub struct ObjSectionDiff { + pub symbols: Vec, + pub data_diff: Vec, + pub match_percent: Option, +} + +impl ObjSectionDiff { + fn merge(&mut self, other: ObjSectionDiff) { + // symbols ignored + self.data_diff = other.data_diff; + self.match_percent = other.match_percent; + } +} + +#[derive(Debug, Clone, Default)] +pub struct ObjSymbolDiff { + pub diff_symbol: Option, + pub instructions: Vec, + pub match_percent: Option, +} + +#[derive(Debug, Clone, Default)] +pub struct ObjInsDiff { + pub ins: Option, + /// Diff kind + pub kind: ObjInsDiffKind, + /// Branches from instruction + pub branch_from: Option, + /// Branches to instruction + pub branch_to: Option, + /// Arg diffs + pub arg_diff: Vec>, +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)] +pub enum ObjInsDiffKind { + #[default] + None, + OpMismatch, + ArgMismatch, + Replace, + Delete, + Insert, +} + +#[derive(Debug, Clone, Default)] +pub struct ObjDataDiff { + pub data: Vec, + pub kind: ObjDataDiffKind, + pub len: usize, + pub symbol: String, +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)] +pub enum ObjDataDiffKind { + #[default] + None, + Replace, + Delete, + Insert, +} + +#[derive(Debug, Copy, Clone)] +pub struct ObjInsArgDiff { + /// Incrementing index for coloring + pub idx: usize, +} + +#[derive(Debug, Clone)] +pub struct ObjInsBranchFrom { + /// Source instruction indices + pub ins_idx: Vec, + /// Incrementing index for coloring + pub branch_idx: usize, +} + +#[derive(Debug, Clone)] +pub struct ObjInsBranchTo { + /// Target instruction index + pub ins_idx: usize, + /// Incrementing index for coloring + pub branch_idx: usize, +} + +#[derive(Default)] +pub struct ObjDiff { + pub sections: Vec, + pub common: Vec, +} + +impl ObjDiff { + pub fn new_from_obj(obj: &ObjInfo) -> Self { + let mut result = Self { + sections: Vec::with_capacity(obj.sections.len()), + common: Vec::with_capacity(obj.common.len()), + }; + for section in &obj.sections { + let mut symbols = Vec::with_capacity(section.symbols.len()); + for _ in §ion.symbols { + symbols.push(ObjSymbolDiff { + diff_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(), + }], + match_percent: None, + }); + } + for _ in &obj.common { + result.common.push(ObjSymbolDiff { + diff_symbol: None, + instructions: vec![], + match_percent: None, + }); + } + 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 { + &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 { + &mut self.section_diff_mut(symbol_ref.section_idx).symbols[symbol_ref.symbol_idx] + } +} + +#[derive(Default)] +pub struct DiffObjsResult { + pub left: Option, + pub right: Option, + pub prev: Option, } pub fn diff_objs( config: &DiffObjConfig, - mut left: Option<&mut ObjInfo>, - mut right: Option<&mut ObjInfo>, -) -> Result<()> { - if let Some(left) = left.as_mut() { - for left_section in &mut left.sections { - if left_section.kind == ObjSectionKind::Code { - for left_symbol in &mut left_section.symbols { - if let Some((right, (right_section_idx, right_symbol_idx))) = - right.as_mut().and_then(|obj| { - find_section_and_symbol(obj, &left_symbol.name).map(|s| (obj, s)) - }) - { - let right_section = &mut right.sections[right_section_idx]; - let right_symbol = &mut right_section.symbols[right_symbol_idx]; - left_symbol.diff_symbol = Some(right_symbol.name.clone()); - right_symbol.diff_symbol = Some(left_symbol.name.clone()); - diff_code( - left.arch.as_ref(), + left: Option<&ObjInfo>, + right: Option<&ObjInfo>, + prev: Option<&ObjInfo>, +) -> Result { + let symbol_matches = matching_symbols(left, right, prev)?; + 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))); + + for symbol_match in symbol_matches { + match symbol_match { + SymbolMatch { + left: Some(left_symbol_ref), + right: Some(right_symbol_ref), + prev: prev_symbol_ref, + section_kind, + } => { + 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_diff, right_diff) = diff_code( + left_obj, + right_obj, + left_symbol_ref, + right_symbol_ref, config, - &left_section.data, - &right_section.data, - left_symbol, - right_symbol, - &left_section.relocations, - &right_section.relocations, - &left.line_info, - &right.line_info, )?; - } else { - no_diff_code( - left.arch.as_ref(), - config, - &left_section.data, - left_symbol, - &left_section.relocations, - &left.line_info, + *left_out.symbol_diff_mut(left_symbol_ref) = left_diff; + *right_out.symbol_diff_mut(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_diff) = diff_code( + right_obj, + prev_obj, + right_symbol_ref, + prev_symbol_ref, + config, + )?; + *prev_out.symbol_diff_mut(prev_symbol_ref) = prev_diff; + } + } + ObjSectionKind::Data => { + // TODO diff data symbol + } + ObjSectionKind::Bss => { + 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; + } + } + } + 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 => { + *left_out.symbol_diff_mut(left_symbol_ref) = + no_diff_code(left_obj, left_symbol_ref, config)?; + } + ObjSectionKind::Data => {} + ObjSectionKind::Bss => { + *left_out.symbol_diff_mut(left_symbol_ref) = + no_diff_bss_symbol(left_obj, left_symbol_ref); } } - } else if let Some(right_section) = right - .as_mut() - .and_then(|obj| obj.sections.iter_mut().find(|s| s.name == left_section.name)) - { - if left_section.kind == ObjSectionKind::Data { - diff_data(left_section, right_section)?; - } else if left_section.kind == ObjSectionKind::Bss { - diff_bss_symbols(&mut left_section.symbols, &mut right_section.symbols)?; + } + 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 => { + *right_out.symbol_diff_mut(right_symbol_ref) = + no_diff_code(right_obj, right_symbol_ref, config)?; + } + ObjSectionKind::Data => {} + ObjSectionKind::Bss => { + *right_out.symbol_diff_mut(right_symbol_ref) = + no_diff_bss_symbol(right_obj, right_symbol_ref); + } } - } else if left_section.kind == ObjSectionKind::Data { - no_diff_data(left_section); + } + SymbolMatch { left: None, right: None, .. } => { + // Should not happen } } } - if let Some(right) = right.as_mut() { - for right_section in right.sections.iter_mut() { - if right_section.kind == ObjSectionKind::Code { - for right_symbol in &mut right_section.symbols { - if right_symbol.instructions.is_empty() { - no_diff_code( - right.arch.as_ref(), - config, - &right_section.data, - right_symbol, - &right_section.relocations, - &right.line_info, - )?; - } + + for section_match in section_matches { + if let SectionMatch { + left: Some(left_section_idx), + right: Some(right_section_idx), + section_kind, + } = section_match + { + 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 => { + // TODO? + } + ObjSectionKind::Data => { + let (left_diff, right_diff) = diff_data(left_section, right_section)?; + left_out.section_diff_mut(left_section_idx).merge(left_diff); + right_out.section_diff_mut(right_section_idx).merge(right_diff); + } + ObjSectionKind::Bss => { + // TODO + } + } + } + } + + Ok(DiffObjsResult { + left: left.map(|(_, o)| o), + right: right.map(|(_, o)| o), + prev: prev.map(|(_, o)| o), + }) +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct SymbolRef { + pub section_idx: usize, + pub symbol_idx: usize, +} + +#[inline] +pub fn get_symbol(obj: &ObjInfo, ref_: SymbolRef) -> (&ObjSection, &ObjSymbol) { + let section = &obj.sections[ref_.section_idx]; + let symbol = §ion.symbols[ref_.symbol_idx]; + (section, symbol) +} + +#[derive(Copy, Clone, Eq, PartialEq)] +struct SymbolMatch { + left: Option, + right: Option, + prev: Option, + section_kind: ObjSectionKind, +} + +#[derive(Copy, Clone, Eq, PartialEq)] +struct SectionMatch { + left: Option, + right: Option, + section_kind: ObjSectionKind, +} + +/// Find matching symbols between each object. +fn matching_symbols( + left: Option<&ObjInfo>, + right: Option<&ObjInfo>, + prev: Option<&ObjInfo>, +) -> Result> { + let mut matches = Vec::new(); + let mut right_used = Vec::new(); + if let Some(left) = left { + for (section_idx, section) in left.sections.iter().enumerate() { + for (symbol_idx, symbol) in section.symbols.iter().enumerate() { + let symbol_match = SymbolMatch { + left: Some(SymbolRef { section_idx, symbol_idx }), + right: find_symbol(right, &symbol.name, section.kind), + prev: find_symbol(prev, &symbol.name, section.kind), + section_kind: section.kind, + }; + matches.push(symbol_match); + if let Some(right) = symbol_match.right { + right_used.push(right); + } + } + } + } + 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.binary_search(&symbol_ref).is_ok() { + continue; } - } else if right_section.kind == ObjSectionKind::Data - && right_section.data_diff.is_empty() - { - no_diff_data(right_section); + matches.push(SymbolMatch { + left: None, + right: Some(symbol_ref), + prev: find_symbol(prev, &symbol.name, section.kind), + section_kind: section.kind, + }); } } } - if let (Some(left), Some(right)) = (left, right) { - diff_bss_symbols(&mut left.common, &mut right.common)?; + Ok(matches) +} + +fn find_symbol( + obj: Option<&ObjInfo>, + name: &str, + section_kind: ObjSectionKind, +) -> Option { + for (section_idx, section) in obj?.sections.iter().enumerate() { + if section.kind != section_kind { + continue; + } + let symbol_idx = match section.symbols.iter().position(|symbol| symbol.name == name) { + Some(symbol_idx) => symbol_idx, + None => continue, + }; + return Some(SymbolRef { section_idx, symbol_idx }); + } + None +} + +/// Find matching sections between each object. +fn matching_sections(left: Option<&ObjInfo>, right: Option<&ObjInfo>) -> Result> { + let mut matches = Vec::new(); + if let Some(left) = left { + for (section_idx, section) in left.sections.iter().enumerate() { + matches.push(SectionMatch { + left: Some(section_idx), + right: find_section(right, §ion.name, section.kind), + section_kind: section.kind, + }); + } + } + if let Some(right) = right { + for (section_idx, section) in right.sections.iter().enumerate() { + if matches.iter().any(|m| m.right == Some(section_idx)) { + continue; + } + matches.push(SectionMatch { + left: None, + right: Some(section_idx), + section_kind: section.kind, + }); + } + } + Ok(matches) +} + +fn find_section(obj: Option<&ObjInfo>, name: &str, section_kind: ObjSectionKind) -> Option { + for (section_idx, section) in obj?.sections.iter().enumerate() { + if section.kind != section_kind { + continue; + } + if section.name == name { + return Some(section_idx); + } } - Ok(()) + None } diff --git a/objdiff-core/src/obj/mod.rs b/objdiff-core/src/obj/mod.rs index 8ada8a7..986774b 100644 --- a/objdiff-core/src/obj/mod.rs +++ b/objdiff-core/src/obj/mod.rs @@ -39,10 +39,6 @@ pub struct ObjSection { pub symbols: Vec, pub relocations: Vec, pub virtual_address: Option, - - // Diff - pub data_diff: Vec, - pub match_percent: f32, } #[derive(Debug, Clone, Eq, PartialEq)] @@ -94,39 +90,6 @@ impl ObjInsArg { } } -#[derive(Debug, Copy, Clone)] -pub struct ObjInsArgDiff { - /// Incrementing index for coloring - pub idx: usize, -} - -#[derive(Debug, Clone)] -pub struct ObjInsBranchFrom { - /// Source instruction indices - pub ins_idx: Vec, - /// Incrementing index for coloring - pub branch_idx: usize, -} - -#[derive(Debug, Clone)] -pub struct ObjInsBranchTo { - /// Target instruction index - pub ins_idx: usize, - /// Incrementing index for coloring - pub branch_idx: usize, -} - -#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)] -pub enum ObjInsDiffKind { - #[default] - None, - OpMismatch, - ArgMismatch, - Replace, - Delete, - Insert, -} - #[derive(Debug, Clone)] pub struct ObjIns { pub address: u64, @@ -142,36 +105,6 @@ pub struct ObjIns { pub orig: Option, } -#[derive(Debug, Clone, Default)] -pub struct ObjInsDiff { - pub ins: Option, - /// Diff kind - pub kind: ObjInsDiffKind, - /// Branches from instruction - pub branch_from: Option, - /// Branches to instruction - pub branch_to: Option, - /// Arg diffs - pub arg_diff: Vec>, -} - -#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)] -pub enum ObjDataDiffKind { - #[default] - None, - Replace, - Delete, - Insert, -} - -#[derive(Debug, Clone, Default)] -pub struct ObjDataDiff { - pub data: Vec, - pub kind: ObjDataDiffKind, - pub len: usize, - pub symbol: String, -} - #[derive(Debug, Clone)] pub struct ObjSymbol { pub name: String, @@ -184,11 +117,6 @@ pub struct ObjSymbol { pub addend: i64, /// Original virtual address (from .note.split section) pub virtual_address: Option, - - // Diff - pub diff_symbol: Option, - pub instructions: Vec, - pub match_percent: Option, } pub struct ObjInfo { diff --git a/objdiff-core/src/obj/read.rs b/objdiff-core/src/obj/read.rs index 190c994..e865aed 100644 --- a/objdiff-core/src/obj/read.rs +++ b/objdiff-core/src/obj/read.rs @@ -76,9 +76,6 @@ fn to_obj_symbol( flags, addend, virtual_address, - diff_symbol: None, - instructions: vec![], - match_percent: None, }) } @@ -114,8 +111,6 @@ fn filter_sections(obj_file: &File<'_>, split_meta: Option<&SplitMeta>) -> Resul symbols: Vec::new(), relocations: Vec::new(), virtual_address, - data_diff: vec![], - match_percent: 0.0, }); } result.sort_by(|a, b| a.name.cmp(&b.name)); @@ -214,9 +209,6 @@ fn find_section_symbol( flags: Default::default(), addend: offset_addr as i64, virtual_address: None, - diff_symbol: None, - instructions: vec![], - match_percent: None, }) } diff --git a/objdiff-gui/src/app.rs b/objdiff-gui/src/app.rs index fdbe954..b91a673 100644 --- a/objdiff-gui/src/app.rs +++ b/objdiff-gui/src/app.rs @@ -364,12 +364,12 @@ impl App { } if let Some(result) = &diff_state.build { - if let Some(obj) = &result.first_obj { + if let Some((obj, _)) = &result.first_obj { if file_modified(&obj.path, obj.timestamp) { config.queue_reload = true; } } - if let Some(obj) = &result.second_obj { + if let Some((obj, _)) = &result.second_obj { if file_modified(&obj.path, obj.timestamp) { config.queue_reload = true; } diff --git a/objdiff-gui/src/jobs/objdiff.rs b/objdiff-gui/src/jobs/objdiff.rs index 7e3396e..84edbac 100644 --- a/objdiff-gui/src/jobs/objdiff.rs +++ b/objdiff-gui/src/jobs/objdiff.rs @@ -7,7 +7,7 @@ use std::{ use anyhow::{anyhow, Context, Error, Result}; use objdiff_core::{ - diff::{diff_objs, DiffObjConfig}, + diff::{diff_objs, DiffObjConfig, ObjDiff}, obj::{read, ObjInfo}, }; use time::OffsetDateTime; @@ -75,8 +75,8 @@ impl ObjDiffConfig { pub struct ObjDiffResult { pub first_status: BuildStatus, pub second_status: BuildStatus, - pub first_obj: Option, - pub second_obj: Option, + pub first_obj: Option<(ObjInfo, ObjDiff)>, + pub second_obj: Option<(ObjInfo, ObjDiff)>, pub time: OffsetDateTime, } @@ -214,7 +214,7 @@ fn run_build( let time = OffsetDateTime::now_utc(); - let mut first_obj = + let first_obj = match &obj_config.target_path { Some(target_path) if first_status.success => { update_status( @@ -231,7 +231,7 @@ fn run_build( _ => None, }; - let mut second_obj = match &obj_config.base_path { + let second_obj = match &obj_config.base_path { Some(base_path) if second_status.success => { update_status( context, @@ -249,10 +249,16 @@ fn run_build( }; update_status(context, "Performing diff".to_string(), 4, total, &cancel)?; - diff_objs(&config.diff_obj_config, first_obj.as_mut(), second_obj.as_mut())?; + let result = diff_objs(&config.diff_obj_config, first_obj.as_ref(), second_obj.as_ref(), None)?; update_status(context, "Complete".to_string(), total, total, &cancel)?; - Ok(Box::new(ObjDiffResult { first_status, second_status, first_obj, second_obj, time })) + Ok(Box::new(ObjDiffResult { + first_status, + second_status, + first_obj: first_obj.and_then(|o| result.left.map(|d| (o, d))), + second_obj: second_obj.and_then(|o| result.right.map(|d| (o, d))), + time, + })) } pub fn start_build(ctx: &egui::Context, config: ObjDiffConfig) -> JobState { diff --git a/objdiff-gui/src/views/data_diff.rs b/objdiff-gui/src/views/data_diff.rs index 6e18c14..1ad8505 100644 --- a/objdiff-gui/src/views/data_diff.rs +++ b/objdiff-gui/src/views/data_diff.rs @@ -2,19 +2,22 @@ use std::{cmp::min, default::Default, mem::take}; use egui::{text::LayoutJob, Align, Label, Layout, Sense, Vec2, Widget}; use egui_extras::{Column, TableBuilder}; -use objdiff_core::obj::{ObjDataDiff, ObjDataDiffKind, ObjInfo, ObjSection}; +use objdiff_core::{ + diff::{ObjDataDiff, ObjDataDiffKind, ObjDiff}, + obj::ObjInfo, +}; use time::format_description; use crate::views::{ appearance::Appearance, - symbol_diff::{DiffViewState, SymbolReference, View}, + symbol_diff::{DiffViewState, SymbolRefByName, View}, write_text, }; const BYTES_PER_ROW: usize = 16; -fn find_section<'a>(obj: &'a ObjInfo, selected_symbol: &SymbolReference) -> Option<&'a ObjSection> { - obj.sections.iter().find(|section| section.name == selected_symbol.section_name) +fn find_section(obj: &ObjInfo, selected_symbol: &SymbolRefByName) -> Option { + obj.sections.iter().position(|section| section.name == selected_symbol.section_name) } fn data_row_ui(ui: &mut egui::Ui, address: usize, diffs: &[ObjDataDiff], appearance: &Appearance) { @@ -130,16 +133,21 @@ fn split_diffs(diffs: &[ObjDataDiff]) -> Vec> { fn data_table_ui( table: TableBuilder<'_>, - left_obj: Option<&ObjInfo>, - right_obj: Option<&ObjInfo>, - selected_symbol: &SymbolReference, + left_obj: Option<&(ObjInfo, ObjDiff)>, + right_obj: Option<&(ObjInfo, ObjDiff)>, + selected_symbol: &SymbolRefByName, config: &Appearance, ) -> Option<()> { - let left_section = left_obj.and_then(|obj| find_section(obj, selected_symbol)); - let right_section = right_obj.and_then(|obj| find_section(obj, selected_symbol)); + let left_section = left_obj.and_then(|(obj, diff)| { + find_section(obj, selected_symbol).map(|i| (&obj.sections[i], &diff.sections[i])) + }); + let right_section = right_obj.and_then(|(obj, diff)| { + find_section(obj, selected_symbol).map(|i| (&obj.sections[i], &diff.sections[i])) + }); let total_bytes = left_section .or(right_section)? + .1 .data_diff .iter() .fold(0usize, |accum, item| accum + item.len); @@ -148,8 +156,8 @@ fn data_table_ui( } let total_rows = (total_bytes - 1) / BYTES_PER_ROW + 1; - let left_diffs = left_section.map(|section| split_diffs(§ion.data_diff)); - let right_diffs = right_section.map(|section| split_diffs(§ion.data_diff)); + let left_diffs = left_section.map(|(_, section)| split_diffs(§ion.data_diff)); + let right_diffs = right_section.map(|(_, section)| split_diffs(§ion.data_diff)); table.body(|body| { body.rows(config.code_font.size, total_rows, |mut row| { diff --git a/objdiff-gui/src/views/function_diff.rs b/objdiff-gui/src/views/function_diff.rs index 880b2a7..0fab463 100644 --- a/objdiff-gui/src/views/function_diff.rs +++ b/objdiff-gui/src/views/function_diff.rs @@ -4,17 +4,17 @@ use egui::{text::LayoutJob, Align, Label, Layout, Sense, Vec2, Widget}; use egui_extras::{Column, TableBuilder, TableRow}; use objdiff_core::{ arch::ObjArch, - diff::display::{display_diff, DiffText, HighlightKind}, - obj::{ - ObjInfo, ObjIns, ObjInsArg, ObjInsArgValue, ObjInsDiff, ObjInsDiffKind, ObjSection, - ObjSymbol, + diff::{ + display::{display_diff, DiffText, HighlightKind}, + get_symbol, ObjDiff, ObjInsDiff, ObjInsDiffKind, SymbolRef, }, + obj::{ObjInfo, ObjIns, ObjInsArg, ObjInsArgValue, ObjSection, ObjSymbol}, }; use time::format_description; use crate::views::{ appearance::Appearance, - symbol_diff::{match_color_for_symbol, DiffViewState, SymbolReference, View}, + symbol_diff::{match_color_for_symbol, DiffViewState, SymbolRefByName, View}, }; #[derive(Default)] @@ -126,19 +126,15 @@ fn ins_context_menu(ui: &mut egui::Ui, ins: &ObjIns) { }); } -fn find_symbol<'a>( - obj: &'a ObjInfo, - selected_symbol: &SymbolReference, -) -> Option<(&'a dyn ObjArch, &'a ObjSection, &'a ObjSymbol)> { - obj.sections.iter().find_map(|section| { - section.symbols.iter().find_map(|symbol| { - (symbol.name == selected_symbol.symbol_name).then_some(( - obj.arch.as_ref(), - section, - symbol, - )) - }) - }) +fn find_symbol(obj: &ObjInfo, selected_symbol: &SymbolRefByName) -> Option { + for (section_idx, section) in obj.sections.iter().enumerate() { + for (symbol_idx, symbol) in section.symbols.iter().enumerate() { + if symbol.name == selected_symbol.symbol_name { + return Some(SymbolRef { section_idx, symbol_idx }); + } + } + } + None } fn diff_text_ui( @@ -248,18 +244,21 @@ fn asm_row_ui( fn asm_col_ui( row: &mut TableRow<'_, '_>, - ins_diff: &ObjInsDiff, - arch: &dyn ObjArch, - section: &ObjSection, - symbol: &ObjSymbol, + obj: &(ObjInfo, ObjDiff), + symbol_ref: SymbolRef, appearance: &Appearance, ins_view_state: &mut FunctionViewState, ) { + let (section, symbol) = get_symbol(&obj.0, symbol_ref); + let ins_diff = &obj.1.sections[symbol_ref.section_idx].symbols[symbol_ref.symbol_idx] + .instructions[row.index()]; let (_, response) = row.col(|ui| { asm_row_ui(ui, ins_diff, symbol, appearance, ins_view_state); }); if let Some(ins) = &ins_diff.ins { - response.on_hover_ui_at_pointer(|ui| ins_hover_ui(ui, arch, section, ins, appearance)); + response.on_hover_ui_at_pointer(|ui| { + ins_hover_ui(ui, obj.0.arch.as_ref(), section, ins, appearance) + }); } } @@ -271,41 +270,40 @@ fn empty_col_ui(row: &mut TableRow<'_, '_>) { fn asm_table_ui( table: TableBuilder<'_>, - left_obj: Option<&ObjInfo>, - right_obj: Option<&ObjInfo>, - selected_symbol: &SymbolReference, + left_obj: Option<&(ObjInfo, ObjDiff)>, + right_obj: Option<&(ObjInfo, ObjDiff)>, + selected_symbol: &SymbolRefByName, appearance: &Appearance, ins_view_state: &mut FunctionViewState, ) -> Option<()> { - let left_symbol = left_obj.and_then(|obj| find_symbol(obj, selected_symbol)); - let right_symbol = right_obj.and_then(|obj| find_symbol(obj, selected_symbol)); - let instructions_len = left_symbol.or(right_symbol).map(|(_, _, s)| s.instructions.len())?; + let left_symbol = left_obj.and_then(|(obj, _)| find_symbol(obj, selected_symbol)); + let right_symbol = right_obj.and_then(|(obj, _)| find_symbol(obj, selected_symbol)); + let instructions_len = match (left_symbol, right_symbol) { + (Some(left_symbol_ref), Some(_)) => left_obj.unwrap().1.sections + [left_symbol_ref.section_idx] + .symbols[left_symbol_ref.symbol_idx] + .instructions + .len(), + (Some(left_symbol_ref), None) => left_obj.unwrap().1.sections[left_symbol_ref.section_idx] + .symbols[left_symbol_ref.symbol_idx] + .instructions + .len(), + (None, Some(right_symbol_ref)) => right_obj.unwrap().1.sections + [right_symbol_ref.section_idx] + .symbols[right_symbol_ref.symbol_idx] + .instructions + .len(), + (None, None) => return None, + }; table.body(|body| { body.rows(appearance.code_font.size, instructions_len, |mut row| { - let row_index = row.index(); - if let Some((arch, section, symbol)) = left_symbol { - asm_col_ui( - &mut row, - &symbol.instructions[row_index], - arch, - section, - symbol, - appearance, - ins_view_state, - ); + if let (Some(left_obj), Some(left_symbol_ref)) = (left_obj, left_symbol) { + asm_col_ui(&mut row, left_obj, left_symbol_ref, appearance, ins_view_state); } else { empty_col_ui(&mut row); } - if let Some((arch, section, symbol)) = right_symbol { - asm_col_ui( - &mut row, - &symbol.instructions[row_index], - arch, - section, - symbol, - appearance, - ins_view_state, - ); + if let (Some(right_obj), Some(right_symbol_ref)) = (right_obj, right_symbol) { + asm_col_ui(&mut row, right_obj, right_symbol_ref, appearance, ins_view_state); } else { empty_col_ui(&mut row); } @@ -412,8 +410,12 @@ pub fn function_diff_ui(ui: &mut egui::Ui, state: &mut DiffViewState, appearance if let Some(match_percent) = result .second_obj .as_ref() - .and_then(|obj| find_symbol(obj, selected_symbol)) - .and_then(|(_, _, symbol)| symbol.match_percent) + .and_then(|(obj, diff)| { + find_symbol(obj, selected_symbol).map(|sref| { + &diff.sections[sref.section_idx].symbols[sref.symbol_idx] + }) + }) + .and_then(|symbol| symbol.match_percent) { ui.colored_label( match_color_for_symbol(match_percent, appearance), diff --git a/objdiff-gui/src/views/symbol_diff.rs b/objdiff-gui/src/views/symbol_diff.rs index 1306f5b..1ce9081 100644 --- a/objdiff-gui/src/views/symbol_diff.rs +++ b/objdiff-gui/src/views/symbol_diff.rs @@ -5,7 +5,10 @@ use egui::{ SelectableLabel, TextEdit, Ui, Vec2, Widget, }; use egui_extras::{Size, StripBuilder}; -use objdiff_core::obj::{ObjInfo, ObjSection, ObjSectionKind, ObjSymbol, ObjSymbolFlags}; +use objdiff_core::{ + diff::{ObjDiff, ObjSymbolDiff}, + obj::{ObjInfo, ObjSection, ObjSectionKind, ObjSymbol, ObjSymbolFlags}, +}; use crate::{ app::AppConfigRef, @@ -17,7 +20,7 @@ use crate::{ views::{appearance::Appearance, function_diff::FunctionViewState, write_text}, }; -pub struct SymbolReference { +pub struct SymbolRefByName { pub symbol_name: String, pub demangled_symbol_name: Option, pub section_name: String, @@ -50,7 +53,7 @@ pub struct DiffViewState { #[derive(Default)] pub struct SymbolViewState { pub highlighted_symbol: Option, - pub selected_symbol: Option, + pub selected_symbol: Option, pub reverse_fn_order: bool, pub disable_reverse_fn_order: bool, pub show_hidden_symbols: bool, @@ -180,6 +183,7 @@ fn symbol_hover_ui(ui: &mut Ui, symbol: &ObjSymbol, appearance: &Appearance) { fn symbol_ui( ui: &mut Ui, symbol: &ObjSymbol, + symbol_diff: &ObjSymbolDiff, section: Option<&ObjSection>, state: &mut SymbolViewState, appearance: &Appearance, @@ -210,7 +214,7 @@ fn symbol_ui( write_text("h", appearance.deemphasized_text_color, &mut job, appearance.code_font.clone()); } write_text("] ", appearance.text_color, &mut job, appearance.code_font.clone()); - if let Some(match_percent) = symbol.match_percent { + if let Some(match_percent) = symbol_diff.match_percent { write_text("(", appearance.text_color, &mut job, appearance.code_font.clone()); write_text( &format!("{match_percent:.0}%"), @@ -228,14 +232,14 @@ fn symbol_ui( if response.clicked() { if let Some(section) = section { if section.kind == ObjSectionKind::Code { - state.selected_symbol = Some(SymbolReference { + state.selected_symbol = Some(SymbolRefByName { symbol_name: symbol.name.clone(), demangled_symbol_name: symbol.demangled_name.clone(), section_name: section.name.clone(), }); ret = Some(View::FunctionDiff); } else if section.kind == ObjSectionKind::Data { - state.selected_symbol = Some(SymbolReference { + state.selected_symbol = Some(SymbolRefByName { symbol_name: section.name.clone(), demangled_symbol_name: None, section_name: section.name.clone(), @@ -262,7 +266,7 @@ fn symbol_matches_search(symbol: &ObjSymbol, search_str: &str) -> bool { #[must_use] fn symbol_list_ui( ui: &mut Ui, - obj: &ObjInfo, + obj: &(ObjInfo, ObjDiff), state: &mut SymbolViewState, lower_search: &str, appearance: &Appearance, @@ -273,34 +277,50 @@ fn symbol_list_ui( ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace); ui.style_mut().wrap = Some(false); - if !obj.common.is_empty() { + if !obj.0.common.is_empty() { CollapsingHeader::new(".comm").default_open(true).show(ui, |ui| { - for symbol in &obj.common { - ret = ret.or(symbol_ui(ui, symbol, None, state, appearance)); + for (symbol, symbol_diff) in obj.0.common.iter().zip(&obj.1.common) { + ret = ret.or(symbol_ui(ui, symbol, symbol_diff, None, state, appearance)); } }); } - for section in &obj.sections { + for (section, section_diff) in obj.0.sections.iter().zip(&obj.1.sections) { CollapsingHeader::new(format!("{} ({:x})", section.name, section.size)) .id_source(Id::new(section.name.clone()).with(section.index)) .default_open(true) .show(ui, |ui| { if section.kind == ObjSectionKind::Code && state.reverse_fn_order { - for symbol in section.symbols.iter().rev() { + for (symbol, symbol_diff) in + section.symbols.iter().zip(§ion_diff.symbols).rev() + { if !symbol_matches_search(symbol, lower_search) { continue; } - ret = - ret.or(symbol_ui(ui, symbol, Some(section), state, appearance)); + ret = ret.or(symbol_ui( + ui, + symbol, + symbol_diff, + Some(section), + state, + appearance, + )); } } else { - for symbol in §ion.symbols { + for (symbol, symbol_diff) in + section.symbols.iter().zip(§ion_diff.symbols) + { if !symbol_matches_search(symbol, lower_search) { continue; } - ret = - ret.or(symbol_ui(ui, symbol, Some(section), state, appearance)); + ret = ret.or(symbol_ui( + ui, + symbol, + symbol_diff, + Some(section), + state, + appearance, + )); } } });