mirror of https://github.com/encounter/objdiff.git
Compare commits
3 Commits
1343f4fd2b
...
e7991cb28d
Author | SHA1 | Date |
---|---|---|
Luke Street | e7991cb28d | |
Luke Street | 4dfc28fc68 | |
Luke Street | 3c74b89f15 |
|
@ -16,13 +16,16 @@ use event::KeyModifiers;
|
||||||
use objdiff_core::{
|
use objdiff_core::{
|
||||||
config::{ProjectConfig, ProjectObject},
|
config::{ProjectConfig, ProjectObject},
|
||||||
diff,
|
diff,
|
||||||
diff::display::{display_diff, DiffText, HighlightKind},
|
diff::{
|
||||||
|
display::{display_diff, DiffText, HighlightKind},
|
||||||
|
DiffObjsResult, ObjDiff, ObjInsDiffKind, ObjSymbolDiff,
|
||||||
|
},
|
||||||
obj,
|
obj,
|
||||||
obj::{ObjInfo, ObjInsDiffKind, ObjSectionKind, ObjSymbol},
|
obj::{ObjInfo, ObjSectionKind, ObjSymbol, SymbolRef},
|
||||||
};
|
};
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
prelude::*,
|
prelude::*,
|
||||||
widgets::{Block, Borders, Paragraph, Scrollbar, ScrollbarOrientation, ScrollbarState},
|
widgets::{Block, Borders, Clear, Paragraph, Scrollbar, ScrollbarOrientation, ScrollbarState},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::util::term::crossterm_panic_handler;
|
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]")
|
let time_format = time::format_description::parse_borrowed::<2>("[hour]:[minute]:[second]")
|
||||||
.context("Failed to parse time format")?;
|
.context("Failed to parse time format")?;
|
||||||
let mut state = Box::new(FunctionDiffUi {
|
let mut state = Box::new(FunctionDiffUi {
|
||||||
redraw: true,
|
|
||||||
relax_reloc_diffs: args.relax_reloc_diffs,
|
relax_reloc_diffs: args.relax_reloc_diffs,
|
||||||
click_xy: None,
|
|
||||||
left_highlight: HighlightKind::None,
|
left_highlight: HighlightKind::None,
|
||||||
right_highlight: HighlightKind::None,
|
right_highlight: HighlightKind::None,
|
||||||
scroll_x: 0,
|
scroll_x: 0,
|
||||||
|
@ -161,10 +162,17 @@ pub fn run(args: Args) -> Result<()> {
|
||||||
target_path,
|
target_path,
|
||||||
base_path,
|
base_path,
|
||||||
project_config,
|
project_config,
|
||||||
|
left_obj: None,
|
||||||
|
right_obj: None,
|
||||||
|
prev_obj: None,
|
||||||
|
diff_result: DiffObjsResult::default(),
|
||||||
left_sym: None,
|
left_sym: None,
|
||||||
right_sym: None,
|
right_sym: None,
|
||||||
|
prev_sym: None,
|
||||||
reload_time: None,
|
reload_time: None,
|
||||||
time_format,
|
time_format,
|
||||||
|
open_options: false,
|
||||||
|
three_way: false,
|
||||||
});
|
});
|
||||||
state.reload()?;
|
state.reload()?;
|
||||||
|
|
||||||
|
@ -180,17 +188,27 @@ pub fn run(args: Args) -> Result<()> {
|
||||||
let mut terminal = Terminal::new(backend)?;
|
let mut terminal = Terminal::new(backend)?;
|
||||||
|
|
||||||
'outer: loop {
|
'outer: loop {
|
||||||
|
let mut result = EventResult { redraw: true, ..Default::default() };
|
||||||
loop {
|
loop {
|
||||||
if state.redraw {
|
if result.redraw {
|
||||||
terminal.draw(|f| state.draw(f))?;
|
terminal.draw(|f| loop {
|
||||||
if state.redraw {
|
result.redraw = false;
|
||||||
continue;
|
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()?) {
|
match state.handle_event(event::read()?) {
|
||||||
FunctionDiffResult::Break => break 'outer,
|
EventControlFlow::Break => break 'outer,
|
||||||
FunctionDiffResult::Continue => {}
|
EventControlFlow::Continue(r) => result = r,
|
||||||
FunctionDiffResult::Reload => break,
|
EventControlFlow::Reload => break,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
state.reload()?;
|
state.reload()?;
|
||||||
|
@ -203,14 +221,24 @@ pub fn run(args: Args) -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_function(obj: &ObjInfo, name: &str) -> Option<ObjSymbol> {
|
#[inline]
|
||||||
for section in &obj.sections {
|
fn get_symbol(obj: Option<&ObjInfo>, sym: Option<SymbolRef>) -> Option<&ObjSymbol> {
|
||||||
|
Some(obj?.section_symbol(sym?).1)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn get_symbol_diff(obj: Option<&ObjDiff>, sym: Option<SymbolRef>) -> Option<&ObjSymbolDiff> {
|
||||||
|
Some(obj?.symbol_diff(sym?))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_function(obj: &ObjInfo, name: &str) -> Option<SymbolRef> {
|
||||||
|
for (section_idx, section) in obj.sections.iter().enumerate() {
|
||||||
if section.kind != ObjSectionKind::Code {
|
if section.kind != ObjSectionKind::Code {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for symbol in §ion.symbols {
|
for (symbol_idx, symbol) in section.symbols.iter().enumerate() {
|
||||||
if symbol.name == name {
|
if symbol.name == name {
|
||||||
return Some(symbol.clone());
|
return Some(SymbolRef { section_idx, symbol_idx });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -219,9 +247,7 @@ fn find_function(obj: &ObjInfo, name: &str) -> Option<ObjSymbol> {
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
struct FunctionDiffUi {
|
struct FunctionDiffUi {
|
||||||
redraw: bool,
|
|
||||||
relax_reloc_diffs: bool,
|
relax_reloc_diffs: bool,
|
||||||
click_xy: Option<(u16, u16)>,
|
|
||||||
left_highlight: HighlightKind,
|
left_highlight: HighlightKind,
|
||||||
right_highlight: HighlightKind,
|
right_highlight: HighlightKind,
|
||||||
scroll_x: usize,
|
scroll_x: usize,
|
||||||
|
@ -234,20 +260,33 @@ struct FunctionDiffUi {
|
||||||
target_path: Option<PathBuf>,
|
target_path: Option<PathBuf>,
|
||||||
base_path: Option<PathBuf>,
|
base_path: Option<PathBuf>,
|
||||||
project_config: Option<ProjectConfig>,
|
project_config: Option<ProjectConfig>,
|
||||||
left_sym: Option<ObjSymbol>,
|
left_obj: Option<ObjInfo>,
|
||||||
right_sym: Option<ObjSymbol>,
|
right_obj: Option<ObjInfo>,
|
||||||
|
prev_obj: Option<ObjInfo>,
|
||||||
|
diff_result: DiffObjsResult,
|
||||||
|
left_sym: Option<SymbolRef>,
|
||||||
|
right_sym: Option<SymbolRef>,
|
||||||
|
prev_sym: Option<SymbolRef>,
|
||||||
reload_time: Option<time::OffsetDateTime>,
|
reload_time: Option<time::OffsetDateTime>,
|
||||||
time_format: Vec<time::format_description::FormatItem<'static>>,
|
time_format: Vec<time::format_description::FormatItem<'static>>,
|
||||||
|
open_options: bool,
|
||||||
|
three_way: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum FunctionDiffResult {
|
#[derive(Default)]
|
||||||
|
struct EventResult {
|
||||||
|
redraw: bool,
|
||||||
|
click_xy: Option<(u16, u16)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum EventControlFlow {
|
||||||
Break,
|
Break,
|
||||||
Continue,
|
Continue(EventResult),
|
||||||
Reload,
|
Reload,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FunctionDiffUi {
|
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 chunks = Layout::vertical([Constraint::Length(1), Constraint::Fill(1)]).split(f.size());
|
||||||
let header_chunks = Layout::horizontal([
|
let header_chunks = Layout::horizontal([
|
||||||
Constraint::Fill(1),
|
Constraint::Fill(1),
|
||||||
|
@ -256,13 +295,25 @@ impl FunctionDiffUi {
|
||||||
Constraint::Length(2),
|
Constraint::Length(2),
|
||||||
])
|
])
|
||||||
.split(chunks[0]);
|
.split(chunks[0]);
|
||||||
let content_chunks = Layout::horizontal([
|
let content_chunks = if self.three_way {
|
||||||
Constraint::Fill(1),
|
Layout::horizontal([
|
||||||
Constraint::Length(3),
|
Constraint::Fill(1),
|
||||||
Constraint::Fill(1),
|
Constraint::Length(3),
|
||||||
Constraint::Length(2),
|
Constraint::Fill(1),
|
||||||
])
|
Constraint::Length(3),
|
||||||
.split(chunks[1]);
|
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;
|
self.per_page = chunks[1].height.saturating_sub(2) as usize;
|
||||||
let max_scroll_y = self.num_rows.saturating_sub(self.per_page);
|
let max_scroll_y = self.num_rows.saturating_sub(self.per_page);
|
||||||
|
@ -279,7 +330,9 @@ impl FunctionDiffUi {
|
||||||
f.render_widget(line_l, header_chunks[0]);
|
f.render_widget(line_l, header_chunks[0]);
|
||||||
|
|
||||||
let mut line_r = Line::default();
|
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_symbol_diff(self.diff_result.right.as_ref(), self.right_sym)
|
||||||
|
.and_then(|s| s.match_percent)
|
||||||
|
{
|
||||||
line_r.spans.push(Span::styled(
|
line_r.spans.push(Span::styled(
|
||||||
format!("{:.2}% ", percent),
|
format!("{:.2}% ", percent),
|
||||||
Style::new().fg(match_percent_color(percent)),
|
Style::new().fg(match_percent_color(percent)),
|
||||||
|
@ -296,49 +349,82 @@ impl FunctionDiffUi {
|
||||||
));
|
));
|
||||||
f.render_widget(line_r, header_chunks[2]);
|
f.render_widget(line_r, header_chunks[2]);
|
||||||
|
|
||||||
let create_block =
|
let mut left_text = None;
|
||||||
|title: &'static str| Block::new().borders(Borders::TOP).gray().title(title.bold());
|
|
||||||
|
|
||||||
let mut left_highlight = None;
|
let mut left_highlight = None;
|
||||||
let mut max_width = 0;
|
let mut max_width = 0;
|
||||||
if let Some(symbol) = &self.left_sym {
|
if let (Some(symbol), Some(symbol_diff)) = (
|
||||||
// Render left column
|
get_symbol(self.left_obj.as_ref(), self.left_sym),
|
||||||
|
get_symbol_diff(self.diff_result.left.as_ref(), self.left_sym),
|
||||||
|
) {
|
||||||
let mut text = Text::default();
|
let mut text = Text::default();
|
||||||
let rect = content_chunks[0].inner(&Margin::new(0, 1));
|
let rect = content_chunks[0].inner(&Margin::new(0, 1));
|
||||||
let h = self.print_sym(&mut text, symbol, rect, &self.left_highlight);
|
left_highlight = self.print_sym(
|
||||||
max_width = max_width.max(text.width());
|
&mut text,
|
||||||
f.render_widget(
|
symbol,
|
||||||
Paragraph::new(text)
|
symbol_diff,
|
||||||
.block(create_block("TARGET"))
|
rect,
|
||||||
.scroll((0, self.scroll_x as u16)),
|
&self.left_highlight,
|
||||||
content_chunks[0],
|
result,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
if let Some(h) = h {
|
max_width = max_width.max(text.width());
|
||||||
left_highlight = Some(h);
|
left_text = Some(text);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut right_text = None;
|
||||||
let mut right_highlight = 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_symbol_diff(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
|
// Render margin
|
||||||
let mut text = Text::default();
|
let mut text = Text::default();
|
||||||
let rect = content_chunks[1].inner(&Margin::new(1, 1));
|
let rect = content_chunks[1].inner(&Margin::new(1, 1));
|
||||||
self.print_margin(&mut text, symbol, rect);
|
self.print_margin(&mut text, symbol_diff, rect);
|
||||||
f.render_widget(text, rect);
|
margin_text = Some(text);
|
||||||
|
}
|
||||||
|
|
||||||
// Render right column
|
let mut prev_text = None;
|
||||||
let mut text = Text::default();
|
let mut prev_margin_text = None;
|
||||||
let rect = content_chunks[2].inner(&Margin::new(0, 1));
|
if self.three_way {
|
||||||
let h = self.print_sym(&mut text, symbol, rect, &self.right_highlight);
|
if let (Some(symbol), Some(symbol_diff)) = (
|
||||||
max_width = max_width.max(text.width());
|
get_symbol(self.prev_obj.as_ref(), self.prev_sym),
|
||||||
f.render_widget(
|
get_symbol_diff(self.diff_result.prev.as_ref(), self.prev_sym),
|
||||||
Paragraph::new(text)
|
) {
|
||||||
.block(create_block("CURRENT"))
|
let mut text = Text::default();
|
||||||
.scroll((0, self.scroll_x as u16)),
|
let rect = content_chunks[4].inner(&Margin::new(0, 1));
|
||||||
content_chunks[2],
|
self.print_sym(
|
||||||
);
|
&mut text,
|
||||||
if let Some(h) = h {
|
symbol,
|
||||||
right_highlight = Some(h);
|
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 +436,42 @@ impl FunctionDiffUi {
|
||||||
self.scroll_state_x =
|
self.scroll_state_x =
|
||||||
self.scroll_state_x.content_length(max_scroll_x).position(self.scroll_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
|
// Render scrollbars
|
||||||
f.render_stateful_widget(
|
f.render_stateful_widget(
|
||||||
Scrollbar::new(ScrollbarOrientation::VerticalRight).begin_symbol(None).end_symbol(None),
|
Scrollbar::new(ScrollbarOrientation::VerticalRight).begin_symbol(None).end_symbol(None),
|
||||||
|
@ -366,6 +488,13 @@ impl FunctionDiffUi {
|
||||||
content_chunks[2],
|
content_chunks[2],
|
||||||
&mut self.scroll_state_x,
|
&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 let Some(new_highlight) = left_highlight {
|
||||||
if new_highlight == self.left_highlight {
|
if new_highlight == self.left_highlight {
|
||||||
|
@ -378,8 +507,7 @@ impl FunctionDiffUi {
|
||||||
} else {
|
} else {
|
||||||
self.left_highlight = new_highlight;
|
self.left_highlight = new_highlight;
|
||||||
}
|
}
|
||||||
self.redraw = true;
|
result.redraw = true;
|
||||||
self.click_xy = None;
|
|
||||||
} else if let Some(new_highlight) = right_highlight {
|
} else if let Some(new_highlight) = right_highlight {
|
||||||
if new_highlight == self.right_highlight {
|
if new_highlight == self.right_highlight {
|
||||||
if self.left_highlight != self.right_highlight {
|
if self.left_highlight != self.right_highlight {
|
||||||
|
@ -391,86 +519,128 @@ impl FunctionDiffUi {
|
||||||
} else {
|
} else {
|
||||||
self.right_highlight = new_highlight;
|
self.right_highlight = new_highlight;
|
||||||
}
|
}
|
||||||
self.redraw = true;
|
result.redraw = true;
|
||||||
self.click_xy = None;
|
|
||||||
} else {
|
|
||||||
self.redraw = false;
|
|
||||||
self.click_xy = None;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
match event {
|
||||||
Event::Key(event)
|
Event::Key(event)
|
||||||
if matches!(event.kind, KeyEventKind::Press | KeyEventKind::Repeat) =>
|
if matches!(event.kind, KeyEventKind::Press | KeyEventKind::Repeat) =>
|
||||||
{
|
{
|
||||||
match event.code {
|
match event.code {
|
||||||
// Quit
|
// Quit
|
||||||
KeyCode::Esc | KeyCode::Char('q') => return FunctionDiffResult::Break,
|
KeyCode::Esc | KeyCode::Char('q') => return EventControlFlow::Break,
|
||||||
// Page up
|
// Page up
|
||||||
KeyCode::PageUp => self.page_up(false),
|
KeyCode::PageUp => {
|
||||||
|
self.page_up(false);
|
||||||
|
result.redraw = true;
|
||||||
|
}
|
||||||
// Page up (shift + space)
|
// Page up (shift + space)
|
||||||
KeyCode::Char(' ') if event.modifiers.contains(KeyModifiers::SHIFT) => {
|
KeyCode::Char(' ') if event.modifiers.contains(KeyModifiers::SHIFT) => {
|
||||||
self.page_up(false)
|
self.page_up(false);
|
||||||
|
result.redraw = true;
|
||||||
}
|
}
|
||||||
// Page down
|
// 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)
|
// Page down (ctrl + f)
|
||||||
KeyCode::Char('f') if event.modifiers.contains(KeyModifiers::CONTROL) => {
|
KeyCode::Char('f') if event.modifiers.contains(KeyModifiers::CONTROL) => {
|
||||||
self.page_down(false)
|
self.page_down(false);
|
||||||
|
result.redraw = true;
|
||||||
}
|
}
|
||||||
// Page up (ctrl + b)
|
// Page up (ctrl + b)
|
||||||
KeyCode::Char('b') if event.modifiers.contains(KeyModifiers::CONTROL) => {
|
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)
|
// Half page down (ctrl + d)
|
||||||
KeyCode::Char('d') if event.modifiers.contains(KeyModifiers::CONTROL) => {
|
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)
|
// Half page up (ctrl + u)
|
||||||
KeyCode::Char('u') if event.modifiers.contains(KeyModifiers::CONTROL) => {
|
KeyCode::Char('u') if event.modifiers.contains(KeyModifiers::CONTROL) => {
|
||||||
self.page_up(true)
|
self.page_up(true);
|
||||||
|
result.redraw = true;
|
||||||
}
|
}
|
||||||
// Scroll down
|
// Scroll down
|
||||||
KeyCode::Down | KeyCode::Char('j') => {
|
KeyCode::Down | KeyCode::Char('j') => {
|
||||||
self.scroll_y += 1;
|
self.scroll_y += 1;
|
||||||
self.redraw = true;
|
result.redraw = true;
|
||||||
}
|
}
|
||||||
// Scroll up
|
// Scroll up
|
||||||
KeyCode::Up | KeyCode::Char('k') => {
|
KeyCode::Up | KeyCode::Char('k') => {
|
||||||
self.scroll_y = self.scroll_y.saturating_sub(1);
|
self.scroll_y = self.scroll_y.saturating_sub(1);
|
||||||
self.redraw = true;
|
result.redraw = true;
|
||||||
}
|
}
|
||||||
// Scroll to start
|
// Scroll to start
|
||||||
KeyCode::Char('g') => {
|
KeyCode::Char('g') => {
|
||||||
self.scroll_y = 0;
|
self.scroll_y = 0;
|
||||||
self.redraw = true;
|
result.redraw = true;
|
||||||
}
|
}
|
||||||
// Scroll to end
|
// Scroll to end
|
||||||
KeyCode::Char('G') => {
|
KeyCode::Char('G') => {
|
||||||
self.scroll_y = self.num_rows;
|
self.scroll_y = self.num_rows;
|
||||||
self.redraw = true;
|
result.redraw = true;
|
||||||
}
|
}
|
||||||
// Reload
|
// Reload
|
||||||
KeyCode::Char('r') => {
|
KeyCode::Char('r') => {
|
||||||
self.redraw = true;
|
result.redraw = true;
|
||||||
return FunctionDiffResult::Reload;
|
return EventControlFlow::Reload;
|
||||||
}
|
}
|
||||||
// Scroll right
|
// Scroll right
|
||||||
KeyCode::Right | KeyCode::Char('l') => {
|
KeyCode::Right | KeyCode::Char('l') => {
|
||||||
self.scroll_x += 1;
|
self.scroll_x += 1;
|
||||||
self.redraw = true;
|
result.redraw = true;
|
||||||
}
|
}
|
||||||
// Scroll left
|
// Scroll left
|
||||||
KeyCode::Left | KeyCode::Char('h') => {
|
KeyCode::Left | KeyCode::Char('h') => {
|
||||||
self.scroll_x = self.scroll_x.saturating_sub(1);
|
self.scroll_x = self.scroll_x.saturating_sub(1);
|
||||||
self.redraw = true;
|
result.redraw = true;
|
||||||
}
|
}
|
||||||
// Toggle relax relocation diffs
|
// Toggle relax relocation diffs
|
||||||
KeyCode::Char('x') => {
|
KeyCode::Char('x') => {
|
||||||
self.relax_reloc_diffs = !self.relax_reloc_diffs;
|
self.relax_reloc_diffs = !self.relax_reloc_diffs;
|
||||||
self.redraw = true;
|
result.redraw = true;
|
||||||
return FunctionDiffResult::Reload;
|
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 +648,66 @@ impl FunctionDiffUi {
|
||||||
Event::Mouse(event) => match event.kind {
|
Event::Mouse(event) => match event.kind {
|
||||||
MouseEventKind::ScrollDown => {
|
MouseEventKind::ScrollDown => {
|
||||||
self.scroll_y += 3;
|
self.scroll_y += 3;
|
||||||
self.redraw = true;
|
result.redraw = true;
|
||||||
}
|
}
|
||||||
MouseEventKind::ScrollUp => {
|
MouseEventKind::ScrollUp => {
|
||||||
self.scroll_y = self.scroll_y.saturating_sub(3);
|
self.scroll_y = self.scroll_y.saturating_sub(3);
|
||||||
self.redraw = true;
|
result.redraw = true;
|
||||||
}
|
}
|
||||||
MouseEventKind::ScrollRight => {
|
MouseEventKind::ScrollRight => {
|
||||||
self.scroll_x += 3;
|
self.scroll_x += 3;
|
||||||
self.redraw = true;
|
result.redraw = true;
|
||||||
}
|
}
|
||||||
MouseEventKind::ScrollLeft => {
|
MouseEventKind::ScrollLeft => {
|
||||||
self.scroll_x = self.scroll_x.saturating_sub(3);
|
self.scroll_x = self.scroll_x.saturating_sub(3);
|
||||||
self.redraw = true;
|
result.redraw = true;
|
||||||
}
|
}
|
||||||
MouseEventKind::Down(MouseButton::Left) => {
|
MouseEventKind::Down(MouseButton::Left) => {
|
||||||
self.click_xy = Some((event.column, event.row));
|
result.click_xy = Some((event.column, event.row));
|
||||||
self.redraw = true;
|
result.redraw = true;
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
Event::Resize(_, _) => {
|
Event::Resize(_, _) => {
|
||||||
self.redraw = true;
|
result.redraw = true;
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
FunctionDiffResult::Continue
|
EventControlFlow::Continue(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn page_up(&mut self, half: bool) {
|
fn page_up(&mut self, half: bool) {
|
||||||
self.scroll_y = self.scroll_y.saturating_sub(self.per_page / if half { 2 } else { 1 });
|
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) {
|
fn page_down(&mut self, half: bool) {
|
||||||
self.scroll_y += self.per_page / if half { 2 } else { 1 };
|
self.scroll_y += self.per_page / if half { 2 } else { 1 };
|
||||||
self.redraw = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn print_sym(
|
fn print_sym(
|
||||||
&self,
|
&self,
|
||||||
out: &mut Text,
|
out: &mut Text<'static>,
|
||||||
symbol: &ObjSymbol,
|
symbol: &ObjSymbol,
|
||||||
|
symbol_diff: &ObjSymbolDiff,
|
||||||
rect: Rect,
|
rect: Rect,
|
||||||
highlight: &HighlightKind,
|
highlight: &HighlightKind,
|
||||||
|
result: &EventResult,
|
||||||
|
only_changed: bool,
|
||||||
) -> Option<HighlightKind> {
|
) -> Option<HighlightKind> {
|
||||||
let base_addr = symbol.address;
|
let base_addr = symbol.address;
|
||||||
let mut new_highlight = None;
|
let mut new_highlight = None;
|
||||||
for (y, ins_diff) in
|
for (y, ins_diff) in symbol_diff
|
||||||
symbol.instructions.iter().skip(self.scroll_y).take(rect.height as usize).enumerate()
|
.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 mut sx = rect.x;
|
||||||
let sy = rect.y + y as u16;
|
let sy = rect.y + y as u16;
|
||||||
let mut line = Line::default();
|
let mut line = Line::default();
|
||||||
|
@ -591,7 +771,7 @@ impl FunctionDiffUi {
|
||||||
}
|
}
|
||||||
let len = label_text.len();
|
let len = label_text.len();
|
||||||
let highlighted = *highlight == text;
|
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 {
|
if cx >= sx && cx < sx + len as u16 && cy == sy {
|
||||||
new_highlight = Some(text.into());
|
new_highlight = Some(text.into());
|
||||||
}
|
}
|
||||||
|
@ -615,7 +795,7 @@ impl FunctionDiffUi {
|
||||||
new_highlight
|
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) {
|
for ins_diff in symbol.instructions.iter().skip(self.scroll_y).take(rect.height as usize) {
|
||||||
if ins_diff.kind != ObjInsDiffKind::None {
|
if ins_diff.kind != ObjInsDiffKind::None {
|
||||||
out.lines.push(Line::raw(match ins_diff.kind {
|
out.lines.push(Line::raw(match ins_diff.kind {
|
||||||
|
@ -630,12 +810,13 @@ impl FunctionDiffUi {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reload(&mut self) -> Result<()> {
|
fn reload(&mut self) -> Result<()> {
|
||||||
let mut target = self
|
let prev = self.right_obj.take();
|
||||||
|
let target = self
|
||||||
.target_path
|
.target_path
|
||||||
.as_deref()
|
.as_deref()
|
||||||
.map(|p| obj::read::read(p).with_context(|| format!("Loading {}", p.display())))
|
.map(|p| obj::read::read(p).with_context(|| format!("Loading {}", p.display())))
|
||||||
.transpose()?;
|
.transpose()?;
|
||||||
let mut base = self
|
let base = self
|
||||||
.base_path
|
.base_path
|
||||||
.as_deref()
|
.as_deref()
|
||||||
.map(|p| obj::read::read(p).with_context(|| format!("Loading {}", p.display())))
|
.map(|p| obj::read::read(p).with_context(|| format!("Loading {}", p.display())))
|
||||||
|
@ -645,18 +826,27 @@ impl FunctionDiffUi {
|
||||||
space_between_args: true, // TODO
|
space_between_args: true, // TODO
|
||||||
x86_formatter: Default::default(), // 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 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));
|
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_symbol_diff(result.left.as_ref(), left_sym),
|
||||||
|
get_symbol_diff(result.right.as_ref(), right_sym),
|
||||||
|
) {
|
||||||
(Some(l), Some(r)) => l.instructions.len().max(r.instructions.len()),
|
(Some(l), Some(r)) => l.instructions.len().max(r.instructions.len()),
|
||||||
(Some(l), None) => l.instructions.len(),
|
(Some(l), None) => l.instructions.len(),
|
||||||
(None, Some(r)) => r.instructions.len(),
|
(None, Some(r)) => r.instructions.len(),
|
||||||
(None, None) => bail!("Symbol not found: {}", self.symbol_name),
|
(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.left_sym = left_sym;
|
||||||
self.right_sym = right_sym;
|
self.right_sym = right_sym;
|
||||||
|
self.prev_sym = prev_sym;
|
||||||
self.reload_time = time::OffsetDateTime::now_local().ok();
|
self.reload_time = time::OffsetDateTime::now_local().ok();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -216,18 +216,18 @@ fn report_object(
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
// println!("Checking {}", object.name());
|
// println!("Checking {}", object.name());
|
||||||
let mut target = object
|
let target = object
|
||||||
.target_path
|
.target_path
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|p| obj::read::read(p).with_context(|| format!("Failed to open {}", p.display())))
|
.map(|p| obj::read::read(p).with_context(|| format!("Failed to open {}", p.display())))
|
||||||
.transpose()?;
|
.transpose()?;
|
||||||
let mut base = object
|
let base = object
|
||||||
.base_path
|
.base_path
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|p| obj::read::read(p).with_context(|| format!("Failed to open {}", p.display())))
|
.map(|p| obj::read::read(p).with_context(|| format!("Failed to open {}", p.display())))
|
||||||
.transpose()?;
|
.transpose()?;
|
||||||
let config = diff::DiffObjConfig { relax_reloc_diffs: true, ..Default::default() };
|
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 {
|
let mut unit = ReportUnit {
|
||||||
name: object.name().to_string(),
|
name: object.name().to_string(),
|
||||||
complete: object.complete,
|
complete: object.complete,
|
||||||
|
@ -239,11 +239,12 @@ fn report_object(
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let obj = target.as_ref().or(base.as_ref()).unwrap();
|
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 {
|
if section.kind != ObjSectionKind::Code {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for symbol in §ion.symbols {
|
for (symbol, symbol_diff) in section.symbols.iter().zip(§ion_diff.symbols) {
|
||||||
if symbol.size == 0 {
|
if symbol.size == 0 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -255,7 +256,7 @@ fn report_object(
|
||||||
continue;
|
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,
|
// Support cases where we don't have a target object,
|
||||||
// assume complete means 100% match
|
// assume complete means 100% match
|
||||||
if object.complete == Some(true) {
|
if object.complete == Some(true) {
|
||||||
|
|
|
@ -5,8 +5,8 @@ use object::{elf, Endian, Endianness, File, Object, Relocation, RelocationFlags}
|
||||||
use rabbitizer::{config, Abi, InstrCategory, Instruction, OperandType};
|
use rabbitizer::{config, Abi, InstrCategory, Instruction, OperandType};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
arch::ObjArch,
|
arch::{ObjArch, ProcessCodeResult},
|
||||||
diff::{DiffObjConfig, ProcessCodeResult},
|
diff::DiffObjConfig,
|
||||||
obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection},
|
obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,8 @@ use anyhow::{bail, Result};
|
||||||
use object::{Architecture, Object, Relocation, RelocationFlags};
|
use object::{Architecture, Object, Relocation, RelocationFlags};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
diff::{DiffObjConfig, ProcessCodeResult},
|
diff::DiffObjConfig,
|
||||||
obj::{ObjReloc, ObjSection},
|
obj::{ObjIns, ObjReloc, ObjSection},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "mips")]
|
#[cfg(feature = "mips")]
|
||||||
|
@ -33,6 +33,11 @@ pub trait ObjArch: Send + Sync {
|
||||||
fn display_reloc(&self, flags: RelocationFlags) -> Cow<'static, str>;
|
fn display_reloc(&self, flags: RelocationFlags) -> Cow<'static, str>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct ProcessCodeResult {
|
||||||
|
pub ops: Vec<u16>,
|
||||||
|
pub insts: Vec<ObjIns>,
|
||||||
|
}
|
||||||
|
|
||||||
pub fn new_arch(object: &object::File) -> Result<Box<dyn ObjArch>> {
|
pub fn new_arch(object: &object::File) -> Result<Box<dyn ObjArch>> {
|
||||||
Ok(match object.architecture() {
|
Ok(match object.architecture() {
|
||||||
#[cfg(feature = "ppc")]
|
#[cfg(feature = "ppc")]
|
||||||
|
|
|
@ -5,8 +5,8 @@ use object::{elf, File, Relocation, RelocationFlags};
|
||||||
use ppc750cl::{disasm_iter, Argument, SimplifiedIns, GPR};
|
use ppc750cl::{disasm_iter, Argument, SimplifiedIns, GPR};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
arch::ObjArch,
|
arch::{ObjArch, ProcessCodeResult},
|
||||||
diff::{DiffObjConfig, ProcessCodeResult},
|
diff::DiffObjConfig,
|
||||||
obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection},
|
obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -4,13 +4,13 @@ use anyhow::{anyhow, bail, ensure, Result};
|
||||||
use iced_x86::{
|
use iced_x86::{
|
||||||
Decoder, DecoderOptions, DecoratorKind, Formatter, FormatterOutput, FormatterTextKind,
|
Decoder, DecoderOptions, DecoratorKind, Formatter, FormatterOutput, FormatterTextKind,
|
||||||
GasFormatter, Instruction, IntelFormatter, MasmFormatter, NasmFormatter, NumberKind, OpKind,
|
GasFormatter, Instruction, IntelFormatter, MasmFormatter, NasmFormatter, NumberKind, OpKind,
|
||||||
PrefixKind, Register, SymbolResult,
|
PrefixKind, Register,
|
||||||
};
|
};
|
||||||
use object::{pe, Endian, Endianness, File, Object, Relocation, RelocationFlags};
|
use object::{pe, Endian, Endianness, File, Object, Relocation, RelocationFlags};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
arch::ObjArch,
|
arch::{ObjArch, ProcessCodeResult},
|
||||||
diff::{DiffObjConfig, ProcessCodeResult, X86Formatter},
|
diff::{DiffObjConfig, X86Formatter},
|
||||||
obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection},
|
obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -88,29 +88,6 @@ impl ObjArch for ObjArchX86 {
|
||||||
ensure!(output.ins_operands.len() == output.ins.args.len());
|
ensure!(output.ins_operands.len() == output.ins.args.len());
|
||||||
output.ins.orig = Some(output.formatted.clone());
|
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
|
// 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)) {
|
if reloc.is_some() && !output.ins.args.iter().any(|a| matches!(a, ObjInsArg::Reloc)) {
|
||||||
let mut found = replace_arg(
|
let mut found = replace_arg(
|
||||||
|
@ -229,7 +206,6 @@ impl InstructionFormatterOutput {
|
||||||
|
|
||||||
impl FormatterOutput for InstructionFormatterOutput {
|
impl FormatterOutput for InstructionFormatterOutput {
|
||||||
fn write(&mut self, text: &str, kind: FormatterTextKind) {
|
fn write(&mut self, text: &str, kind: FormatterTextKind) {
|
||||||
// log::debug!("write {} {:?}", text, kind);
|
|
||||||
self.formatted.push_str(text);
|
self.formatted.push_str(text);
|
||||||
// Skip whitespace after the mnemonic
|
// Skip whitespace after the mnemonic
|
||||||
if self.ins.args.is_empty() && kind == FormatterTextKind::Text {
|
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) {
|
fn write_prefix(&mut self, _instruction: &Instruction, text: &str, _prefix: PrefixKind) {
|
||||||
// log::debug!("write_prefix {} {:?}", text, prefix);
|
|
||||||
self.formatted.push_str(text);
|
self.formatted.push_str(text);
|
||||||
self.ins_operands.push(None);
|
self.ins_operands.push(None);
|
||||||
self.ins.args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(text.to_string())));
|
self.ins.args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(text.to_string())));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_mnemonic(&mut self, _instruction: &Instruction, text: &str) {
|
fn write_mnemonic(&mut self, _instruction: &Instruction, text: &str) {
|
||||||
// log::debug!("write_mnemonic {}", text);
|
|
||||||
self.formatted.push_str(text);
|
self.formatted.push_str(text);
|
||||||
self.ins.mnemonic = text.to_string();
|
self.ins.mnemonic = text.to_string();
|
||||||
}
|
}
|
||||||
|
@ -274,7 +248,6 @@ impl FormatterOutput for InstructionFormatterOutput {
|
||||||
number_kind: NumberKind,
|
number_kind: NumberKind,
|
||||||
kind: FormatterTextKind,
|
kind: FormatterTextKind,
|
||||||
) {
|
) {
|
||||||
// log::debug!("write_number {} {:?} {} {} {:?} {:?}", operand, instruction_operand, text, value, number_kind, kind);
|
|
||||||
self.formatted.push_str(text);
|
self.formatted.push_str(text);
|
||||||
self.ins_operands.push(instruction_operand);
|
self.ins_operands.push(instruction_operand);
|
||||||
|
|
||||||
|
@ -343,7 +316,6 @@ impl FormatterOutput for InstructionFormatterOutput {
|
||||||
text: &str,
|
text: &str,
|
||||||
_decorator: DecoratorKind,
|
_decorator: DecoratorKind,
|
||||||
) {
|
) {
|
||||||
// log::debug!("write_decorator {} {:?} {} {:?}", operand, instruction_operand, text, decorator);
|
|
||||||
self.formatted.push_str(text);
|
self.formatted.push_str(text);
|
||||||
self.ins_operands.push(instruction_operand);
|
self.ins_operands.push(instruction_operand);
|
||||||
self.ins.args.push(ObjInsArg::PlainText(text.to_string()));
|
self.ins.args.push(ObjInsArg::PlainText(text.to_string()));
|
||||||
|
@ -357,22 +329,8 @@ impl FormatterOutput for InstructionFormatterOutput {
|
||||||
text: &str,
|
text: &str,
|
||||||
_register: Register,
|
_register: Register,
|
||||||
) {
|
) {
|
||||||
// log::debug!("write_register {} {:?} {} {:?}", operand, instruction_operand, text, register);
|
|
||||||
self.formatted.push_str(text);
|
self.formatted.push_str(text);
|
||||||
self.ins_operands.push(instruction_operand);
|
self.ins_operands.push(instruction_operand);
|
||||||
self.ins.args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(text.to_string())));
|
self.ins.args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(text.to_string())));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_symbol(
|
|
||||||
&mut self,
|
|
||||||
_instruction: &Instruction,
|
|
||||||
_operand: u32,
|
|
||||||
_instruction_operand: Option<u32>,
|
|
||||||
_address: u64,
|
|
||||||
_symbol: &SymbolResult<'_>,
|
|
||||||
) {
|
|
||||||
if self.error.is_none() {
|
|
||||||
self.error = Some(anyhow!("x86: Unsupported write_symbol"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,56 +8,65 @@ use anyhow::Result;
|
||||||
use similar::{capture_diff_slices_deadline, Algorithm};
|
use similar::{capture_diff_slices_deadline, Algorithm};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
arch::ObjArch,
|
arch::ProcessCodeResult,
|
||||||
diff::{DiffObjConfig, ProcessCodeResult},
|
diff::{
|
||||||
obj::{
|
DiffObjConfig, ObjInsArgDiff, ObjInsBranchFrom, ObjInsBranchTo, ObjInsDiff, ObjInsDiffKind,
|
||||||
ObjInfo, ObjInsArg, ObjInsArgDiff, ObjInsBranchFrom, ObjInsBranchTo, ObjInsDiff,
|
ObjSymbolDiff,
|
||||||
ObjInsDiffKind, ObjReloc, ObjSymbol, ObjSymbolFlags,
|
|
||||||
},
|
},
|
||||||
|
obj::{ObjInfo, ObjInsArg, ObjReloc, ObjSymbol, ObjSymbolFlags, SymbolRef},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn no_diff_code(
|
pub fn no_diff_code(
|
||||||
arch: &dyn ObjArch,
|
obj: &ObjInfo,
|
||||||
|
symbol_ref: SymbolRef,
|
||||||
config: &DiffObjConfig,
|
config: &DiffObjConfig,
|
||||||
data: &[u8],
|
) -> Result<ObjSymbolDiff> {
|
||||||
symbol: &mut ObjSymbol,
|
let (section, symbol) = obj.section_symbol(symbol_ref);
|
||||||
relocs: &[ObjReloc],
|
let code = §ion.data
|
||||||
line_info: &Option<BTreeMap<u64, u64>>,
|
[symbol.section_address as usize..(symbol.section_address + symbol.size) as usize];
|
||||||
) -> Result<()> {
|
let out = obj.arch.process_code(
|
||||||
let code =
|
config,
|
||||||
&data[symbol.section_address as usize..(symbol.section_address + symbol.size) as usize];
|
code,
|
||||||
let out = arch.process_code(config, code, symbol.address, relocs, line_info)?;
|
symbol.address,
|
||||||
|
§ion.relocations,
|
||||||
|
&obj.line_info,
|
||||||
|
)?;
|
||||||
|
|
||||||
let mut diff = Vec::<ObjInsDiff>::new();
|
let mut diff = Vec::<ObjInsDiff>::new();
|
||||||
for i in out.insts {
|
for i in out.insts {
|
||||||
diff.push(ObjInsDiff { ins: Some(i), kind: ObjInsDiffKind::None, ..Default::default() });
|
diff.push(ObjInsDiff { ins: Some(i), kind: ObjInsDiffKind::None, ..Default::default() });
|
||||||
}
|
}
|
||||||
resolve_branches(&mut diff);
|
resolve_branches(&mut diff);
|
||||||
symbol.instructions = diff;
|
Ok(ObjSymbolDiff { diff_symbol: None, instructions: diff, match_percent: None })
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
pub fn diff_code(
|
pub fn diff_code(
|
||||||
arch: &dyn ObjArch,
|
left_obj: &ObjInfo,
|
||||||
|
right_obj: &ObjInfo,
|
||||||
|
left_symbol_ref: SymbolRef,
|
||||||
|
right_symbol_ref: SymbolRef,
|
||||||
config: &DiffObjConfig,
|
config: &DiffObjConfig,
|
||||||
left_data: &[u8],
|
) -> Result<(ObjSymbolDiff, ObjSymbolDiff)> {
|
||||||
right_data: &[u8],
|
let (left_section, left_symbol) = left_obj.section_symbol(left_symbol_ref);
|
||||||
left_symbol: &mut ObjSymbol,
|
let (right_section, right_symbol) = right_obj.section_symbol(right_symbol_ref);
|
||||||
right_symbol: &mut ObjSymbol,
|
let left_code = &left_section.data[left_symbol.section_address as usize
|
||||||
left_relocs: &[ObjReloc],
|
|
||||||
right_relocs: &[ObjReloc],
|
|
||||||
left_line_info: &Option<BTreeMap<u64, u64>>,
|
|
||||||
right_line_info: &Option<BTreeMap<u64, u64>>,
|
|
||||||
) -> Result<()> {
|
|
||||||
let left_code = &left_data[left_symbol.section_address as usize
|
|
||||||
..(left_symbol.section_address + left_symbol.size) 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];
|
..(right_symbol.section_address + right_symbol.size) as usize];
|
||||||
let left_out =
|
let left_out = left_obj.arch.process_code(
|
||||||
arch.process_code(config, left_code, left_symbol.address, left_relocs, left_line_info)?;
|
config,
|
||||||
let right_out =
|
left_code,
|
||||||
arch.process_code(config, right_code, right_symbol.address, right_relocs, right_line_info)?;
|
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::<ObjInsDiff>::new();
|
let mut left_diff = Vec::<ObjInsDiff>::new();
|
||||||
let mut right_diff = Vec::<ObjInsDiff>::new();
|
let mut right_diff = Vec::<ObjInsDiff>::new();
|
||||||
|
@ -81,13 +90,19 @@ pub fn diff_code(
|
||||||
} else {
|
} else {
|
||||||
((total - diff_state.diff_count) as f32 / total as f32) * 100.0
|
((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;
|
Ok((
|
||||||
right_symbol.instructions = right_diff;
|
ObjSymbolDiff {
|
||||||
|
diff_symbol: Some(right_symbol_ref),
|
||||||
Ok(())
|
instructions: left_diff,
|
||||||
|
match_percent: Some(percent),
|
||||||
|
},
|
||||||
|
ObjSymbolDiff {
|
||||||
|
diff_symbol: Some(left_symbol_ref),
|
||||||
|
instructions: right_diff,
|
||||||
|
match_percent: Some(percent),
|
||||||
|
},
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn diff_instructions(
|
fn diff_instructions(
|
||||||
|
@ -223,6 +238,9 @@ fn arg_eq(
|
||||||
},
|
},
|
||||||
ObjInsArg::Arg(l) => match right {
|
ObjInsArg::Arg(l) => match right {
|
||||||
ObjInsArg::Arg(r) => l == r,
|
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,
|
_ => false,
|
||||||
},
|
},
|
||||||
ObjInsArg::Reloc => {
|
ObjInsArg::Reloc => {
|
||||||
|
@ -334,14 +352,3 @@ fn compare_ins(
|
||||||
}
|
}
|
||||||
Ok(result)
|
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
|
|
||||||
}
|
|
||||||
|
|
|
@ -6,25 +6,42 @@ use std::{
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use similar::{capture_diff_slices_deadline, Algorithm};
|
use similar::{capture_diff_slices_deadline, Algorithm};
|
||||||
|
|
||||||
use crate::obj::{ObjDataDiff, ObjDataDiffKind, ObjSection, ObjSymbol};
|
use crate::{
|
||||||
|
diff::{ObjDataDiff, ObjDataDiffKind, ObjSectionDiff, ObjSymbolDiff},
|
||||||
|
obj::{ObjInfo, ObjSection, SymbolRef},
|
||||||
|
};
|
||||||
|
|
||||||
pub fn diff_bss_symbols(
|
pub fn diff_bss_symbol(
|
||||||
left_symbols: &mut [ObjSymbol],
|
left_obj: &ObjInfo,
|
||||||
right_symbols: &mut [ObjSymbol],
|
right_obj: &ObjInfo,
|
||||||
) -> Result<()> {
|
left_symbol_ref: SymbolRef,
|
||||||
for left_symbol in left_symbols {
|
right_symbol_ref: SymbolRef,
|
||||||
if let Some(right_symbol) = right_symbols.iter_mut().find(|s| s.name == left_symbol.name) {
|
) -> Result<(ObjSymbolDiff, ObjSymbolDiff)> {
|
||||||
left_symbol.diff_symbol = Some(right_symbol.name.clone());
|
let (_, left_symbol) = left_obj.section_symbol(left_symbol_ref);
|
||||||
right_symbol.diff_symbol = Some(left_symbol.name.clone());
|
let (_, right_symbol) = right_obj.section_symbol(right_symbol_ref);
|
||||||
let percent = if left_symbol.size == right_symbol.size { 100.0 } else { 50.0 };
|
let percent = if left_symbol.size == right_symbol.size { 100.0 } else { 50.0 };
|
||||||
left_symbol.match_percent = Some(percent);
|
Ok((
|
||||||
right_symbol.match_percent = Some(percent);
|
ObjSymbolDiff {
|
||||||
}
|
diff_symbol: Some(right_symbol_ref),
|
||||||
}
|
instructions: vec![],
|
||||||
Ok(())
|
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 deadline = Instant::now() + Duration::from_secs(5);
|
||||||
let ops =
|
let ops =
|
||||||
capture_diff_slices_deadline(Algorithm::Patience, &left.data, &right.data, Some(deadline));
|
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;
|
Ok((
|
||||||
right.data_diff = right_diff;
|
ObjSectionDiff {
|
||||||
Ok(())
|
symbols: vec![],
|
||||||
}
|
data_diff: left_diff,
|
||||||
|
// TODO
|
||||||
pub fn no_diff_data(section: &mut ObjSection) {
|
match_percent: None,
|
||||||
section.data_diff = vec![ObjDataDiff {
|
},
|
||||||
data: section.data.clone(),
|
ObjSectionDiff {
|
||||||
kind: ObjDataDiffKind::None,
|
symbols: vec![],
|
||||||
len: section.data.len(),
|
data_diff: right_diff,
|
||||||
symbol: String::new(),
|
// TODO
|
||||||
}];
|
match_percent: None,
|
||||||
|
},
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
use std::cmp::Ordering;
|
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)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum DiffText<'a> {
|
pub enum DiffText<'a> {
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
pub mod code;
|
mod code;
|
||||||
pub mod data;
|
mod data;
|
||||||
pub mod display;
|
pub mod display;
|
||||||
|
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
diff::{
|
diff::{
|
||||||
code::{diff_code, find_section_and_symbol, no_diff_code},
|
code::{diff_code, no_diff_code},
|
||||||
data::{diff_bss_symbols, diff_data, no_diff_data},
|
data::{diff_bss_symbol, diff_data, no_diff_bss_symbol},
|
||||||
},
|
},
|
||||||
obj::{ObjInfo, ObjIns, ObjSectionKind},
|
obj::{ObjInfo, ObjIns, ObjSectionKind, SymbolRef},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
|
#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
|
||||||
|
@ -29,90 +31,401 @@ pub struct DiffObjConfig {
|
||||||
pub x86_formatter: X86Formatter,
|
pub x86_formatter: X86Formatter,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ProcessCodeResult {
|
#[derive(Debug, Clone)]
|
||||||
pub ops: Vec<u16>,
|
pub struct ObjSectionDiff {
|
||||||
pub insts: Vec<ObjIns>,
|
pub symbols: Vec<ObjSymbolDiff>,
|
||||||
|
pub data_diff: Vec<ObjDataDiff>,
|
||||||
|
pub match_percent: Option<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<SymbolRef>,
|
||||||
|
pub instructions: Vec<ObjInsDiff>,
|
||||||
|
pub match_percent: Option<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct ObjInsDiff {
|
||||||
|
pub ins: Option<ObjIns>,
|
||||||
|
/// Diff kind
|
||||||
|
pub kind: ObjInsDiffKind,
|
||||||
|
/// Branches from instruction
|
||||||
|
pub branch_from: Option<ObjInsBranchFrom>,
|
||||||
|
/// Branches to instruction
|
||||||
|
pub branch_to: Option<ObjInsBranchTo>,
|
||||||
|
/// Arg diffs
|
||||||
|
pub arg_diff: Vec<Option<ObjInsArgDiff>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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<u8>,
|
||||||
|
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<usize>,
|
||||||
|
/// 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<ObjSectionDiff>,
|
||||||
|
pub common: Vec<ObjSymbolDiff>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<ObjDiff>,
|
||||||
|
pub right: Option<ObjDiff>,
|
||||||
|
pub prev: Option<ObjDiff>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn diff_objs(
|
pub fn diff_objs(
|
||||||
config: &DiffObjConfig,
|
config: &DiffObjConfig,
|
||||||
mut left: Option<&mut ObjInfo>,
|
left: Option<&ObjInfo>,
|
||||||
mut right: Option<&mut ObjInfo>,
|
right: Option<&ObjInfo>,
|
||||||
) -> Result<()> {
|
prev: Option<&ObjInfo>,
|
||||||
if let Some(left) = left.as_mut() {
|
) -> Result<DiffObjsResult> {
|
||||||
for left_section in &mut left.sections {
|
let symbol_matches = matching_symbols(left, right, prev)?;
|
||||||
if left_section.kind == ObjSectionKind::Code {
|
let section_matches = matching_sections(left, right)?;
|
||||||
for left_symbol in &mut left_section.symbols {
|
let mut left = left.map(|p| (p, ObjDiff::new_from_obj(p)));
|
||||||
if let Some((right, (right_section_idx, right_symbol_idx))) =
|
let mut right = right.map(|p| (p, ObjDiff::new_from_obj(p)));
|
||||||
right.as_mut().and_then(|obj| {
|
let mut prev = prev.map(|p| (p, ObjDiff::new_from_obj(p)));
|
||||||
find_section_and_symbol(obj, &left_symbol.name).map(|s| (obj, s))
|
|
||||||
})
|
for symbol_match in symbol_matches {
|
||||||
{
|
match symbol_match {
|
||||||
let right_section = &mut right.sections[right_section_idx];
|
SymbolMatch {
|
||||||
let right_symbol = &mut right_section.symbols[right_symbol_idx];
|
left: Some(left_symbol_ref),
|
||||||
left_symbol.diff_symbol = Some(right_symbol.name.clone());
|
right: Some(right_symbol_ref),
|
||||||
right_symbol.diff_symbol = Some(left_symbol.name.clone());
|
prev: prev_symbol_ref,
|
||||||
diff_code(
|
section_kind,
|
||||||
left.arch.as_ref(),
|
} => {
|
||||||
|
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,
|
config,
|
||||||
&left_section.data,
|
|
||||||
&right_section.data,
|
|
||||||
left_symbol,
|
|
||||||
right_symbol,
|
|
||||||
&left_section.relocations,
|
|
||||||
&right_section.relocations,
|
|
||||||
&left.line_info,
|
|
||||||
&right.line_info,
|
|
||||||
)?;
|
)?;
|
||||||
} else {
|
*left_out.symbol_diff_mut(left_symbol_ref) = left_diff;
|
||||||
no_diff_code(
|
*right_out.symbol_diff_mut(right_symbol_ref) = right_diff;
|
||||||
left.arch.as_ref(),
|
|
||||||
config,
|
if let Some(prev_symbol_ref) = prev_symbol_ref {
|
||||||
&left_section.data,
|
let (prev_obj, prev_out) = prev.as_mut().unwrap();
|
||||||
left_symbol,
|
let (_, prev_diff) = diff_code(
|
||||||
&left_section.relocations,
|
right_obj,
|
||||||
&left.line_info,
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if let Some(right_section) = right
|
}
|
||||||
.as_mut()
|
SymbolMatch { left: Some(left_symbol_ref), right: None, prev: _, section_kind } => {
|
||||||
.and_then(|obj| obj.sections.iter_mut().find(|s| s.name == left_section.name))
|
let (left_obj, left_out) = left.as_mut().unwrap();
|
||||||
{
|
match section_kind {
|
||||||
if left_section.kind == ObjSectionKind::Data {
|
ObjSectionKind::Code => {
|
||||||
diff_data(left_section, right_section)?;
|
*left_out.symbol_diff_mut(left_symbol_ref) =
|
||||||
} else if left_section.kind == ObjSectionKind::Bss {
|
no_diff_code(left_obj, left_symbol_ref, config)?;
|
||||||
diff_bss_symbols(&mut left_section.symbols, &mut right_section.symbols)?;
|
}
|
||||||
|
ObjSectionKind::Data => {}
|
||||||
|
ObjSectionKind::Bss => {
|
||||||
|
*left_out.symbol_diff_mut(left_symbol_ref) =
|
||||||
|
no_diff_bss_symbol(left_obj, left_symbol_ref);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if left_section.kind == ObjSectionKind::Data {
|
}
|
||||||
no_diff_data(left_section);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SymbolMatch { left: None, right: None, .. } => {
|
||||||
|
// Should not happen
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(right) = right.as_mut() {
|
|
||||||
for right_section in right.sections.iter_mut() {
|
for section_match in section_matches {
|
||||||
if right_section.kind == ObjSectionKind::Code {
|
if let SectionMatch {
|
||||||
for right_symbol in &mut right_section.symbols {
|
left: Some(left_section_idx),
|
||||||
if right_symbol.instructions.is_empty() {
|
right: Some(right_section_idx),
|
||||||
no_diff_code(
|
section_kind,
|
||||||
right.arch.as_ref(),
|
} = section_match
|
||||||
config,
|
{
|
||||||
&right_section.data,
|
let (left_obj, left_out) = left.as_mut().unwrap();
|
||||||
right_symbol,
|
let (right_obj, right_out) = right.as_mut().unwrap();
|
||||||
&right_section.relocations,
|
let left_section = &left_obj.sections[left_section_idx];
|
||||||
&right.line_info,
|
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
|
||||||
}
|
}
|
||||||
} else if right_section.kind == ObjSectionKind::Data
|
|
||||||
&& right_section.data_diff.is_empty()
|
|
||||||
{
|
|
||||||
no_diff_data(right_section);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let (Some(left), Some(right)) = (left, right) {
|
|
||||||
diff_bss_symbols(&mut left.common, &mut right.common)?;
|
Ok(DiffObjsResult {
|
||||||
}
|
left: left.map(|(_, o)| o),
|
||||||
Ok(())
|
right: right.map(|(_, o)| o),
|
||||||
|
prev: prev.map(|(_, o)| o),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||||
|
struct SymbolMatch {
|
||||||
|
left: Option<SymbolRef>,
|
||||||
|
right: Option<SymbolRef>,
|
||||||
|
prev: Option<SymbolRef>,
|
||||||
|
section_kind: ObjSectionKind,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||||
|
struct SectionMatch {
|
||||||
|
left: Option<usize>,
|
||||||
|
right: Option<usize>,
|
||||||
|
section_kind: ObjSectionKind,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find matching symbols between each object.
|
||||||
|
fn matching_symbols(
|
||||||
|
left: Option<&ObjInfo>,
|
||||||
|
right: Option<&ObjInfo>,
|
||||||
|
prev: Option<&ObjInfo>,
|
||||||
|
) -> Result<Vec<SymbolMatch>> {
|
||||||
|
let mut matches = Vec::new();
|
||||||
|
let mut right_used = HashSet::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.insert(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.contains(&symbol_ref) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
matches.push(SymbolMatch {
|
||||||
|
left: None,
|
||||||
|
right: Some(symbol_ref),
|
||||||
|
prev: find_symbol(prev, &symbol.name, section.kind),
|
||||||
|
section_kind: section.kind,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(matches)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_symbol(
|
||||||
|
obj: Option<&ObjInfo>,
|
||||||
|
name: &str,
|
||||||
|
section_kind: ObjSectionKind,
|
||||||
|
) -> Option<SymbolRef> {
|
||||||
|
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<Vec<SectionMatch>> {
|
||||||
|
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<usize> {
|
||||||
|
for (section_idx, section) in obj?.sections.iter().enumerate() {
|
||||||
|
if section.kind != section_kind {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if section.name == name {
|
||||||
|
return Some(section_idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,10 +39,6 @@ pub struct ObjSection {
|
||||||
pub symbols: Vec<ObjSymbol>,
|
pub symbols: Vec<ObjSymbol>,
|
||||||
pub relocations: Vec<ObjReloc>,
|
pub relocations: Vec<ObjReloc>,
|
||||||
pub virtual_address: Option<u64>,
|
pub virtual_address: Option<u64>,
|
||||||
|
|
||||||
// Diff
|
|
||||||
pub data_diff: Vec<ObjDataDiff>,
|
|
||||||
pub match_percent: f32,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[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<usize>,
|
|
||||||
/// 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)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ObjIns {
|
pub struct ObjIns {
|
||||||
pub address: u64,
|
pub address: u64,
|
||||||
|
@ -142,36 +105,6 @@ pub struct ObjIns {
|
||||||
pub orig: Option<String>,
|
pub orig: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
|
||||||
pub struct ObjInsDiff {
|
|
||||||
pub ins: Option<ObjIns>,
|
|
||||||
/// Diff kind
|
|
||||||
pub kind: ObjInsDiffKind,
|
|
||||||
/// Branches from instruction
|
|
||||||
pub branch_from: Option<ObjInsBranchFrom>,
|
|
||||||
/// Branches to instruction
|
|
||||||
pub branch_to: Option<ObjInsBranchTo>,
|
|
||||||
/// Arg diffs
|
|
||||||
pub arg_diff: Vec<Option<ObjInsArgDiff>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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<u8>,
|
|
||||||
pub kind: ObjDataDiffKind,
|
|
||||||
pub len: usize,
|
|
||||||
pub symbol: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ObjSymbol {
|
pub struct ObjSymbol {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
@ -184,11 +117,6 @@ pub struct ObjSymbol {
|
||||||
pub addend: i64,
|
pub addend: i64,
|
||||||
/// Original virtual address (from .note.split section)
|
/// Original virtual address (from .note.split section)
|
||||||
pub virtual_address: Option<u64>,
|
pub virtual_address: Option<u64>,
|
||||||
|
|
||||||
// Diff
|
|
||||||
pub diff_symbol: Option<String>,
|
|
||||||
pub instructions: Vec<ObjInsDiff>,
|
|
||||||
pub match_percent: Option<f32>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ObjInfo {
|
pub struct ObjInfo {
|
||||||
|
@ -211,3 +139,17 @@ pub struct ObjReloc {
|
||||||
pub target: ObjSymbol,
|
pub target: ObjSymbol,
|
||||||
pub target_section: Option<String>,
|
pub target_section: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct SymbolRef {
|
||||||
|
pub section_idx: usize,
|
||||||
|
pub symbol_idx: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ObjInfo {
|
||||||
|
pub fn section_symbol(&self, symbol_ref: SymbolRef) -> (&ObjSection, &ObjSymbol) {
|
||||||
|
let section = &self.sections[symbol_ref.section_idx];
|
||||||
|
let symbol = §ion.symbols[symbol_ref.symbol_idx];
|
||||||
|
(section, symbol)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -76,9 +76,6 @@ fn to_obj_symbol(
|
||||||
flags,
|
flags,
|
||||||
addend,
|
addend,
|
||||||
virtual_address,
|
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(),
|
symbols: Vec::new(),
|
||||||
relocations: Vec::new(),
|
relocations: Vec::new(),
|
||||||
virtual_address,
|
virtual_address,
|
||||||
data_diff: vec![],
|
|
||||||
match_percent: 0.0,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
result.sort_by(|a, b| a.name.cmp(&b.name));
|
result.sort_by(|a, b| a.name.cmp(&b.name));
|
||||||
|
@ -214,9 +209,6 @@ fn find_section_symbol(
|
||||||
flags: Default::default(),
|
flags: Default::default(),
|
||||||
addend: offset_addr as i64,
|
addend: offset_addr as i64,
|
||||||
virtual_address: None,
|
virtual_address: None,
|
||||||
diff_symbol: None,
|
|
||||||
instructions: vec![],
|
|
||||||
match_percent: None,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -364,12 +364,12 @@ impl App {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(result) = &diff_state.build {
|
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) {
|
if file_modified(&obj.path, obj.timestamp) {
|
||||||
config.queue_reload = true;
|
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) {
|
if file_modified(&obj.path, obj.timestamp) {
|
||||||
config.queue_reload = true;
|
config.queue_reload = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ use std::{
|
||||||
|
|
||||||
use anyhow::{anyhow, Context, Error, Result};
|
use anyhow::{anyhow, Context, Error, Result};
|
||||||
use objdiff_core::{
|
use objdiff_core::{
|
||||||
diff::{diff_objs, DiffObjConfig},
|
diff::{diff_objs, DiffObjConfig, ObjDiff},
|
||||||
obj::{read, ObjInfo},
|
obj::{read, ObjInfo},
|
||||||
};
|
};
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
|
@ -75,8 +75,8 @@ impl ObjDiffConfig {
|
||||||
pub struct ObjDiffResult {
|
pub struct ObjDiffResult {
|
||||||
pub first_status: BuildStatus,
|
pub first_status: BuildStatus,
|
||||||
pub second_status: BuildStatus,
|
pub second_status: BuildStatus,
|
||||||
pub first_obj: Option<ObjInfo>,
|
pub first_obj: Option<(ObjInfo, ObjDiff)>,
|
||||||
pub second_obj: Option<ObjInfo>,
|
pub second_obj: Option<(ObjInfo, ObjDiff)>,
|
||||||
pub time: OffsetDateTime,
|
pub time: OffsetDateTime,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,7 +214,7 @@ fn run_build(
|
||||||
|
|
||||||
let time = OffsetDateTime::now_utc();
|
let time = OffsetDateTime::now_utc();
|
||||||
|
|
||||||
let mut first_obj =
|
let first_obj =
|
||||||
match &obj_config.target_path {
|
match &obj_config.target_path {
|
||||||
Some(target_path) if first_status.success => {
|
Some(target_path) if first_status.success => {
|
||||||
update_status(
|
update_status(
|
||||||
|
@ -231,7 +231,7 @@ fn run_build(
|
||||||
_ => None,
|
_ => 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 => {
|
Some(base_path) if second_status.success => {
|
||||||
update_status(
|
update_status(
|
||||||
context,
|
context,
|
||||||
|
@ -249,10 +249,16 @@ fn run_build(
|
||||||
};
|
};
|
||||||
|
|
||||||
update_status(context, "Performing diff".to_string(), 4, total, &cancel)?;
|
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)?;
|
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 {
|
pub fn start_build(ctx: &egui::Context, config: ObjDiffConfig) -> JobState {
|
||||||
|
|
|
@ -2,19 +2,22 @@ use std::{cmp::min, default::Default, mem::take};
|
||||||
|
|
||||||
use egui::{text::LayoutJob, Align, Label, Layout, Sense, Vec2, Widget};
|
use egui::{text::LayoutJob, Align, Label, Layout, Sense, Vec2, Widget};
|
||||||
use egui_extras::{Column, TableBuilder};
|
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 time::format_description;
|
||||||
|
|
||||||
use crate::views::{
|
use crate::views::{
|
||||||
appearance::Appearance,
|
appearance::Appearance,
|
||||||
symbol_diff::{DiffViewState, SymbolReference, View},
|
symbol_diff::{DiffViewState, SymbolRefByName, View},
|
||||||
write_text,
|
write_text,
|
||||||
};
|
};
|
||||||
|
|
||||||
const BYTES_PER_ROW: usize = 16;
|
const BYTES_PER_ROW: usize = 16;
|
||||||
|
|
||||||
fn find_section<'a>(obj: &'a ObjInfo, selected_symbol: &SymbolReference) -> Option<&'a ObjSection> {
|
fn find_section(obj: &ObjInfo, selected_symbol: &SymbolRefByName) -> Option<usize> {
|
||||||
obj.sections.iter().find(|section| section.name == selected_symbol.section_name)
|
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) {
|
fn data_row_ui(ui: &mut egui::Ui, address: usize, diffs: &[ObjDataDiff], appearance: &Appearance) {
|
||||||
|
@ -130,16 +133,21 @@ fn split_diffs(diffs: &[ObjDataDiff]) -> Vec<Vec<ObjDataDiff>> {
|
||||||
|
|
||||||
fn data_table_ui(
|
fn data_table_ui(
|
||||||
table: TableBuilder<'_>,
|
table: TableBuilder<'_>,
|
||||||
left_obj: Option<&ObjInfo>,
|
left_obj: Option<&(ObjInfo, ObjDiff)>,
|
||||||
right_obj: Option<&ObjInfo>,
|
right_obj: Option<&(ObjInfo, ObjDiff)>,
|
||||||
selected_symbol: &SymbolReference,
|
selected_symbol: &SymbolRefByName,
|
||||||
config: &Appearance,
|
config: &Appearance,
|
||||||
) -> Option<()> {
|
) -> Option<()> {
|
||||||
let left_section = left_obj.and_then(|obj| find_section(obj, selected_symbol));
|
let left_section = left_obj.and_then(|(obj, diff)| {
|
||||||
let right_section = right_obj.and_then(|obj| find_section(obj, selected_symbol));
|
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
|
let total_bytes = left_section
|
||||||
.or(right_section)?
|
.or(right_section)?
|
||||||
|
.1
|
||||||
.data_diff
|
.data_diff
|
||||||
.iter()
|
.iter()
|
||||||
.fold(0usize, |accum, item| accum + item.len);
|
.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 total_rows = (total_bytes - 1) / BYTES_PER_ROW + 1;
|
||||||
|
|
||||||
let left_diffs = left_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));
|
let right_diffs = right_section.map(|(_, section)| split_diffs(§ion.data_diff));
|
||||||
|
|
||||||
table.body(|body| {
|
table.body(|body| {
|
||||||
body.rows(config.code_font.size, total_rows, |mut row| {
|
body.rows(config.code_font.size, total_rows, |mut row| {
|
||||||
|
|
|
@ -4,17 +4,17 @@ use egui::{text::LayoutJob, Align, Label, Layout, Sense, Vec2, Widget};
|
||||||
use egui_extras::{Column, TableBuilder, TableRow};
|
use egui_extras::{Column, TableBuilder, TableRow};
|
||||||
use objdiff_core::{
|
use objdiff_core::{
|
||||||
arch::ObjArch,
|
arch::ObjArch,
|
||||||
diff::display::{display_diff, DiffText, HighlightKind},
|
diff::{
|
||||||
obj::{
|
display::{display_diff, DiffText, HighlightKind},
|
||||||
ObjInfo, ObjIns, ObjInsArg, ObjInsArgValue, ObjInsDiff, ObjInsDiffKind, ObjSection,
|
ObjDiff, ObjInsDiff, ObjInsDiffKind,
|
||||||
ObjSymbol,
|
|
||||||
},
|
},
|
||||||
|
obj::{ObjInfo, ObjIns, ObjInsArg, ObjInsArgValue, ObjSection, ObjSymbol, SymbolRef},
|
||||||
};
|
};
|
||||||
use time::format_description;
|
use time::format_description;
|
||||||
|
|
||||||
use crate::views::{
|
use crate::views::{
|
||||||
appearance::Appearance,
|
appearance::Appearance,
|
||||||
symbol_diff::{match_color_for_symbol, DiffViewState, SymbolReference, View},
|
symbol_diff::{match_color_for_symbol, DiffViewState, SymbolRefByName, View},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -126,19 +126,15 @@ fn ins_context_menu(ui: &mut egui::Ui, ins: &ObjIns) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_symbol<'a>(
|
fn find_symbol(obj: &ObjInfo, selected_symbol: &SymbolRefByName) -> Option<SymbolRef> {
|
||||||
obj: &'a ObjInfo,
|
for (section_idx, section) in obj.sections.iter().enumerate() {
|
||||||
selected_symbol: &SymbolReference,
|
for (symbol_idx, symbol) in section.symbols.iter().enumerate() {
|
||||||
) -> Option<(&'a dyn ObjArch, &'a ObjSection, &'a ObjSymbol)> {
|
if symbol.name == selected_symbol.symbol_name {
|
||||||
obj.sections.iter().find_map(|section| {
|
return Some(SymbolRef { section_idx, symbol_idx });
|
||||||
section.symbols.iter().find_map(|symbol| {
|
}
|
||||||
(symbol.name == selected_symbol.symbol_name).then_some((
|
}
|
||||||
obj.arch.as_ref(),
|
}
|
||||||
section,
|
None
|
||||||
symbol,
|
|
||||||
))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn diff_text_ui(
|
fn diff_text_ui(
|
||||||
|
@ -248,18 +244,20 @@ fn asm_row_ui(
|
||||||
|
|
||||||
fn asm_col_ui(
|
fn asm_col_ui(
|
||||||
row: &mut TableRow<'_, '_>,
|
row: &mut TableRow<'_, '_>,
|
||||||
ins_diff: &ObjInsDiff,
|
obj: &(ObjInfo, ObjDiff),
|
||||||
arch: &dyn ObjArch,
|
symbol_ref: SymbolRef,
|
||||||
section: &ObjSection,
|
|
||||||
symbol: &ObjSymbol,
|
|
||||||
appearance: &Appearance,
|
appearance: &Appearance,
|
||||||
ins_view_state: &mut FunctionViewState,
|
ins_view_state: &mut FunctionViewState,
|
||||||
) {
|
) {
|
||||||
|
let (section, symbol) = obj.0.section_symbol(symbol_ref);
|
||||||
|
let ins_diff = &obj.1.symbol_diff(symbol_ref).instructions[row.index()];
|
||||||
let (_, response) = row.col(|ui| {
|
let (_, response) = row.col(|ui| {
|
||||||
asm_row_ui(ui, ins_diff, symbol, appearance, ins_view_state);
|
asm_row_ui(ui, ins_diff, symbol, appearance, ins_view_state);
|
||||||
});
|
});
|
||||||
if let Some(ins) = &ins_diff.ins {
|
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 +269,38 @@ fn empty_col_ui(row: &mut TableRow<'_, '_>) {
|
||||||
|
|
||||||
fn asm_table_ui(
|
fn asm_table_ui(
|
||||||
table: TableBuilder<'_>,
|
table: TableBuilder<'_>,
|
||||||
left_obj: Option<&ObjInfo>,
|
left_obj: Option<&(ObjInfo, ObjDiff)>,
|
||||||
right_obj: Option<&ObjInfo>,
|
right_obj: Option<&(ObjInfo, ObjDiff)>,
|
||||||
selected_symbol: &SymbolReference,
|
selected_symbol: &SymbolRefByName,
|
||||||
appearance: &Appearance,
|
appearance: &Appearance,
|
||||||
ins_view_state: &mut FunctionViewState,
|
ins_view_state: &mut FunctionViewState,
|
||||||
) -> Option<()> {
|
) -> Option<()> {
|
||||||
let left_symbol = left_obj.and_then(|obj| find_symbol(obj, selected_symbol));
|
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 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 instructions_len = match (left_symbol, right_symbol) {
|
||||||
|
(Some(left_symbol_ref), Some(right_symbol_ref)) => {
|
||||||
|
let left_len = left_obj.unwrap().1.symbol_diff(left_symbol_ref).instructions.len();
|
||||||
|
let right_len = right_obj.unwrap().1.symbol_diff(right_symbol_ref).instructions.len();
|
||||||
|
debug_assert_eq!(left_len, right_len);
|
||||||
|
left_len
|
||||||
|
}
|
||||||
|
(Some(left_symbol_ref), None) => {
|
||||||
|
left_obj.unwrap().1.symbol_diff(left_symbol_ref).instructions.len()
|
||||||
|
}
|
||||||
|
(None, Some(right_symbol_ref)) => {
|
||||||
|
right_obj.unwrap().1.symbol_diff(right_symbol_ref).instructions.len()
|
||||||
|
}
|
||||||
|
(None, None) => return None,
|
||||||
|
};
|
||||||
table.body(|body| {
|
table.body(|body| {
|
||||||
body.rows(appearance.code_font.size, instructions_len, |mut row| {
|
body.rows(appearance.code_font.size, instructions_len, |mut row| {
|
||||||
let row_index = row.index();
|
if let (Some(left_obj), Some(left_symbol_ref)) = (left_obj, left_symbol) {
|
||||||
if let Some((arch, section, symbol)) = left_symbol {
|
asm_col_ui(&mut row, left_obj, left_symbol_ref, appearance, ins_view_state);
|
||||||
asm_col_ui(
|
|
||||||
&mut row,
|
|
||||||
&symbol.instructions[row_index],
|
|
||||||
arch,
|
|
||||||
section,
|
|
||||||
symbol,
|
|
||||||
appearance,
|
|
||||||
ins_view_state,
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
empty_col_ui(&mut row);
|
empty_col_ui(&mut row);
|
||||||
}
|
}
|
||||||
if let Some((arch, section, symbol)) = right_symbol {
|
if let (Some(right_obj), Some(right_symbol_ref)) = (right_obj, right_symbol) {
|
||||||
asm_col_ui(
|
asm_col_ui(&mut row, right_obj, right_symbol_ref, appearance, ins_view_state);
|
||||||
&mut row,
|
|
||||||
&symbol.instructions[row_index],
|
|
||||||
arch,
|
|
||||||
section,
|
|
||||||
symbol,
|
|
||||||
appearance,
|
|
||||||
ins_view_state,
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
empty_col_ui(&mut row);
|
empty_col_ui(&mut row);
|
||||||
}
|
}
|
||||||
|
@ -412,8 +407,12 @@ pub fn function_diff_ui(ui: &mut egui::Ui, state: &mut DiffViewState, appearance
|
||||||
if let Some(match_percent) = result
|
if let Some(match_percent) = result
|
||||||
.second_obj
|
.second_obj
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|obj| find_symbol(obj, selected_symbol))
|
.and_then(|(obj, diff)| {
|
||||||
.and_then(|(_, _, symbol)| symbol.match_percent)
|
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(
|
ui.colored_label(
|
||||||
match_color_for_symbol(match_percent, appearance),
|
match_color_for_symbol(match_percent, appearance),
|
||||||
|
|
|
@ -5,7 +5,10 @@ use egui::{
|
||||||
SelectableLabel, TextEdit, Ui, Vec2, Widget,
|
SelectableLabel, TextEdit, Ui, Vec2, Widget,
|
||||||
};
|
};
|
||||||
use egui_extras::{Size, StripBuilder};
|
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::{
|
use crate::{
|
||||||
app::AppConfigRef,
|
app::AppConfigRef,
|
||||||
|
@ -17,7 +20,7 @@ use crate::{
|
||||||
views::{appearance::Appearance, function_diff::FunctionViewState, write_text},
|
views::{appearance::Appearance, function_diff::FunctionViewState, write_text},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct SymbolReference {
|
pub struct SymbolRefByName {
|
||||||
pub symbol_name: String,
|
pub symbol_name: String,
|
||||||
pub demangled_symbol_name: Option<String>,
|
pub demangled_symbol_name: Option<String>,
|
||||||
pub section_name: String,
|
pub section_name: String,
|
||||||
|
@ -50,7 +53,7 @@ pub struct DiffViewState {
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct SymbolViewState {
|
pub struct SymbolViewState {
|
||||||
pub highlighted_symbol: Option<String>,
|
pub highlighted_symbol: Option<String>,
|
||||||
pub selected_symbol: Option<SymbolReference>,
|
pub selected_symbol: Option<SymbolRefByName>,
|
||||||
pub reverse_fn_order: bool,
|
pub reverse_fn_order: bool,
|
||||||
pub disable_reverse_fn_order: bool,
|
pub disable_reverse_fn_order: bool,
|
||||||
pub show_hidden_symbols: bool,
|
pub show_hidden_symbols: bool,
|
||||||
|
@ -180,6 +183,7 @@ fn symbol_hover_ui(ui: &mut Ui, symbol: &ObjSymbol, appearance: &Appearance) {
|
||||||
fn symbol_ui(
|
fn symbol_ui(
|
||||||
ui: &mut Ui,
|
ui: &mut Ui,
|
||||||
symbol: &ObjSymbol,
|
symbol: &ObjSymbol,
|
||||||
|
symbol_diff: &ObjSymbolDiff,
|
||||||
section: Option<&ObjSection>,
|
section: Option<&ObjSection>,
|
||||||
state: &mut SymbolViewState,
|
state: &mut SymbolViewState,
|
||||||
appearance: &Appearance,
|
appearance: &Appearance,
|
||||||
|
@ -210,7 +214,7 @@ fn symbol_ui(
|
||||||
write_text("h", appearance.deemphasized_text_color, &mut job, appearance.code_font.clone());
|
write_text("h", appearance.deemphasized_text_color, &mut job, appearance.code_font.clone());
|
||||||
}
|
}
|
||||||
write_text("] ", appearance.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("(", appearance.text_color, &mut job, appearance.code_font.clone());
|
||||||
write_text(
|
write_text(
|
||||||
&format!("{match_percent:.0}%"),
|
&format!("{match_percent:.0}%"),
|
||||||
|
@ -228,14 +232,14 @@ fn symbol_ui(
|
||||||
if response.clicked() {
|
if response.clicked() {
|
||||||
if let Some(section) = section {
|
if let Some(section) = section {
|
||||||
if section.kind == ObjSectionKind::Code {
|
if section.kind == ObjSectionKind::Code {
|
||||||
state.selected_symbol = Some(SymbolReference {
|
state.selected_symbol = Some(SymbolRefByName {
|
||||||
symbol_name: symbol.name.clone(),
|
symbol_name: symbol.name.clone(),
|
||||||
demangled_symbol_name: symbol.demangled_name.clone(),
|
demangled_symbol_name: symbol.demangled_name.clone(),
|
||||||
section_name: section.name.clone(),
|
section_name: section.name.clone(),
|
||||||
});
|
});
|
||||||
ret = Some(View::FunctionDiff);
|
ret = Some(View::FunctionDiff);
|
||||||
} else if section.kind == ObjSectionKind::Data {
|
} else if section.kind == ObjSectionKind::Data {
|
||||||
state.selected_symbol = Some(SymbolReference {
|
state.selected_symbol = Some(SymbolRefByName {
|
||||||
symbol_name: section.name.clone(),
|
symbol_name: section.name.clone(),
|
||||||
demangled_symbol_name: None,
|
demangled_symbol_name: None,
|
||||||
section_name: section.name.clone(),
|
section_name: section.name.clone(),
|
||||||
|
@ -262,7 +266,7 @@ fn symbol_matches_search(symbol: &ObjSymbol, search_str: &str) -> bool {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn symbol_list_ui(
|
fn symbol_list_ui(
|
||||||
ui: &mut Ui,
|
ui: &mut Ui,
|
||||||
obj: &ObjInfo,
|
obj: &(ObjInfo, ObjDiff),
|
||||||
state: &mut SymbolViewState,
|
state: &mut SymbolViewState,
|
||||||
lower_search: &str,
|
lower_search: &str,
|
||||||
appearance: &Appearance,
|
appearance: &Appearance,
|
||||||
|
@ -273,34 +277,50 @@ fn symbol_list_ui(
|
||||||
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
|
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
|
||||||
ui.style_mut().wrap = Some(false);
|
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| {
|
CollapsingHeader::new(".comm").default_open(true).show(ui, |ui| {
|
||||||
for symbol in &obj.common {
|
for (symbol, symbol_diff) in obj.0.common.iter().zip(&obj.1.common) {
|
||||||
ret = ret.or(symbol_ui(ui, symbol, None, state, appearance));
|
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))
|
CollapsingHeader::new(format!("{} ({:x})", section.name, section.size))
|
||||||
.id_source(Id::new(section.name.clone()).with(section.index))
|
.id_source(Id::new(section.name.clone()).with(section.index))
|
||||||
.default_open(true)
|
.default_open(true)
|
||||||
.show(ui, |ui| {
|
.show(ui, |ui| {
|
||||||
if section.kind == ObjSectionKind::Code && state.reverse_fn_order {
|
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) {
|
if !symbol_matches_search(symbol, lower_search) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
ret =
|
ret = ret.or(symbol_ui(
|
||||||
ret.or(symbol_ui(ui, symbol, Some(section), state, appearance));
|
ui,
|
||||||
|
symbol,
|
||||||
|
symbol_diff,
|
||||||
|
Some(section),
|
||||||
|
state,
|
||||||
|
appearance,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
} else {
|
} 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) {
|
if !symbol_matches_search(symbol, lower_search) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
ret =
|
ret = ret.or(symbol_ui(
|
||||||
ret.or(symbol_ui(ui, symbol, Some(section), state, appearance));
|
ui,
|
||||||
|
symbol,
|
||||||
|
symbol_diff,
|
||||||
|
Some(section),
|
||||||
|
state,
|
||||||
|
appearance,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue