mirror of
https://github.com/encounter/objdiff.git
synced 2025-06-07 15:13:47 +00:00
WIP objdiff 3.0 refactor
This commit is contained in:
parent
6d3c63ccd8
commit
f3c157ff06
20
.github/workflows/build.yaml
vendored
20
.github/workflows/build.yaml
vendored
@ -71,7 +71,6 @@ jobs:
|
|||||||
|
|
||||||
test:
|
test:
|
||||||
name: Test
|
name: Test
|
||||||
if: 'false' # No tests yet
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
platform: [ ubuntu-latest, windows-latest, macos-latest ]
|
platform: [ ubuntu-latest, windows-latest, macos-latest ]
|
||||||
@ -227,6 +226,25 @@ jobs:
|
|||||||
${{ env.CARGO_TARGET_DIR }}/${{ matrix.target }}/${{ env.BUILD_PROFILE }}/${{ env.CARGO_BIN_NAME }}.exe
|
${{ env.CARGO_TARGET_DIR }}/${{ matrix.target }}/${{ env.BUILD_PROFILE }}/${{ env.CARGO_BIN_NAME }}.exe
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
|
build-wasm:
|
||||||
|
name: Build objdiff-wasm
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Setup Rust toolchain
|
||||||
|
uses: dtolnay/rust-toolchain@nightly
|
||||||
|
with:
|
||||||
|
targets: wasm32-wasip2
|
||||||
|
- name: Cache Rust workspace
|
||||||
|
uses: Swatinem/rust-cache@v2
|
||||||
|
- name: Install dependencies
|
||||||
|
working-directory: objdiff-wasm
|
||||||
|
run: npm install
|
||||||
|
- name: Build
|
||||||
|
working-directory: objdiff-wasm
|
||||||
|
run: npm run build
|
||||||
|
|
||||||
release:
|
release:
|
||||||
name: Release
|
name: Release
|
||||||
if: startsWith(github.ref, 'refs/tags/')
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
|
832
Cargo.lock
generated
832
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -3,6 +3,7 @@ members = [
|
|||||||
"objdiff-cli",
|
"objdiff-cli",
|
||||||
"objdiff-core",
|
"objdiff-core",
|
||||||
"objdiff-gui",
|
"objdiff-gui",
|
||||||
|
"objdiff-wasm",
|
||||||
]
|
]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
@ -13,7 +14,7 @@ strip = "debuginfo"
|
|||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "2.7.1"
|
version = "3.0.0-alpha.1"
|
||||||
authors = ["Luke Street <luke@street.dev>"]
|
authors = ["Luke Street <luke@street.dev>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
|
@ -18,7 +18,7 @@ argp = "0.4"
|
|||||||
crossterm = "0.28"
|
crossterm = "0.28"
|
||||||
enable-ansi-support = "0.2"
|
enable-ansi-support = "0.2"
|
||||||
memmap2 = "0.9"
|
memmap2 = "0.9"
|
||||||
objdiff-core = { path = "../objdiff-core", features = ["all"] }
|
objdiff-core = { path = "../objdiff-core", features = ["ppc", "std", "config", "dwarf", "bindings", "serde"] }
|
||||||
prost = "0.13"
|
prost = "0.13"
|
||||||
ratatui = "0.29"
|
ratatui = "0.29"
|
||||||
rayon = "1.10"
|
rayon = "1.10"
|
||||||
|
@ -30,16 +30,15 @@ use objdiff_core::{
|
|||||||
path::{check_path_buf, platform_path, platform_path_serde_option},
|
path::{check_path_buf, platform_path, platform_path_serde_option},
|
||||||
ProjectConfig, ProjectObject, ProjectObjectMetadata,
|
ProjectConfig, ProjectObject, ProjectObjectMetadata,
|
||||||
},
|
},
|
||||||
diff,
|
|
||||||
diff::{
|
diff::{
|
||||||
ConfigEnum, ConfigPropertyId, ConfigPropertyKind, DiffObjConfig, MappingConfig, ObjDiff,
|
self, ConfigEnum, ConfigPropertyId, ConfigPropertyKind, DiffObjConfig, MappingConfig,
|
||||||
|
ObjectDiff,
|
||||||
},
|
},
|
||||||
jobs::{
|
jobs::{
|
||||||
objdiff::{start_build, ObjDiffConfig},
|
objdiff::{start_build, ObjDiffConfig},
|
||||||
Job, JobQueue, JobResult,
|
Job, JobQueue, JobResult,
|
||||||
},
|
},
|
||||||
obj,
|
obj::{self, Object},
|
||||||
obj::ObjInfo,
|
|
||||||
};
|
};
|
||||||
use ratatui::prelude::*;
|
use ratatui::prelude::*;
|
||||||
use typed_path::{Utf8PlatformPath, Utf8PlatformPathBuf};
|
use typed_path::{Utf8PlatformPath, Utf8PlatformPathBuf};
|
||||||
@ -239,7 +238,7 @@ fn run_oneshot(
|
|||||||
})
|
})
|
||||||
.transpose()?;
|
.transpose()?;
|
||||||
let result =
|
let result =
|
||||||
diff::diff_objs(&diff_config, &mapping_config, target.as_ref(), base.as_ref(), None)?;
|
diff::diff_objs(target.as_ref(), base.as_ref(), None, &diff_config, &mapping_config)?;
|
||||||
let left = target.as_ref().and_then(|o| result.left.as_ref().map(|d| (o, d)));
|
let left = target.as_ref().and_then(|o| result.left.as_ref().map(|d| (o, d)));
|
||||||
let right = base.as_ref().and_then(|o| result.right.as_ref().map(|d| (o, d)));
|
let right = base.as_ref().and_then(|o| result.right.as_ref().map(|d| (o, d)));
|
||||||
write_output(&DiffResult::new(left, right), Some(output), output_format)?;
|
write_output(&DiffResult::new(left, right), Some(output), output_format)?;
|
||||||
@ -253,9 +252,9 @@ pub struct AppState {
|
|||||||
pub project_config: Option<ProjectConfig>,
|
pub project_config: Option<ProjectConfig>,
|
||||||
pub target_path: Option<Utf8PlatformPathBuf>,
|
pub target_path: Option<Utf8PlatformPathBuf>,
|
||||||
pub base_path: Option<Utf8PlatformPathBuf>,
|
pub base_path: Option<Utf8PlatformPathBuf>,
|
||||||
pub left_obj: Option<(ObjInfo, ObjDiff)>,
|
pub left_obj: Option<(Object, ObjectDiff)>,
|
||||||
pub right_obj: Option<(ObjInfo, ObjDiff)>,
|
pub right_obj: Option<(Object, ObjectDiff)>,
|
||||||
pub prev_obj: Option<(ObjInfo, ObjDiff)>,
|
pub prev_obj: Option<(Object, ObjectDiff)>,
|
||||||
pub reload_time: Option<time::OffsetDateTime>,
|
pub reload_time: Option<time::OffsetDateTime>,
|
||||||
pub time_format: Vec<time::format_description::FormatItem<'static>>,
|
pub time_format: Vec<time::format_description::FormatItem<'static>>,
|
||||||
pub watcher: Option<Watcher>,
|
pub watcher: Option<Watcher>,
|
||||||
|
@ -10,7 +10,7 @@ use objdiff_core::{
|
|||||||
},
|
},
|
||||||
config::path::platform_path,
|
config::path::platform_path,
|
||||||
diff, obj,
|
diff, obj,
|
||||||
obj::{ObjSectionKind, ObjSymbolFlags},
|
obj::{SectionKind, SymbolFlag},
|
||||||
};
|
};
|
||||||
use prost::Message;
|
use prost::Message;
|
||||||
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
|
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
|
||||||
@ -181,7 +181,7 @@ fn report_object(
|
|||||||
})
|
})
|
||||||
.transpose()?;
|
.transpose()?;
|
||||||
let result =
|
let result =
|
||||||
diff::diff_objs(&diff_config, &mapping_config, target.as_ref(), base.as_ref(), None)?;
|
diff::diff_objs(target.as_ref(), base.as_ref(), None, &diff_config, &mapping_config)?;
|
||||||
|
|
||||||
let metadata = ReportUnitMetadata {
|
let metadata = ReportUnitMetadata {
|
||||||
complete: object.metadata.complete,
|
complete: object.metadata.complete,
|
||||||
@ -200,7 +200,9 @@ fn report_object(
|
|||||||
|
|
||||||
let obj = target.as_ref().or(base.as_ref()).unwrap();
|
let obj = target.as_ref().or(base.as_ref()).unwrap();
|
||||||
let obj_diff = result.left.as_ref().or(result.right.as_ref()).unwrap();
|
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) {
|
for ((section_idx, section), section_diff) in
|
||||||
|
obj.sections.iter().enumerate().zip(&obj_diff.sections)
|
||||||
|
{
|
||||||
let section_match_percent = section_diff.match_percent.unwrap_or_else(|| {
|
let section_match_percent = section_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
|
||||||
@ -221,23 +223,26 @@ fn report_object(
|
|||||||
});
|
});
|
||||||
|
|
||||||
match section.kind {
|
match section.kind {
|
||||||
ObjSectionKind::Data | ObjSectionKind::Bss => {
|
SectionKind::Data | SectionKind::Bss => {
|
||||||
measures.total_data += section.size;
|
measures.total_data += section.size;
|
||||||
if section_match_percent == 100.0 {
|
if section_match_percent == 100.0 {
|
||||||
measures.matched_data += section.size;
|
measures.matched_data += section.size;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
ObjSectionKind::Code => (),
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (symbol, symbol_diff) in section.symbols.iter().zip(§ion_diff.symbols) {
|
for (symbol, symbol_diff) in obj.symbols.iter().zip(&obj_diff.symbols) {
|
||||||
if symbol.size == 0 || symbol.flags.0.contains(ObjSymbolFlags::Hidden) {
|
if symbol.section != Some(section_idx)
|
||||||
|
|| symbol.size == 0
|
||||||
|
|| symbol.flags.contains(SymbolFlag::Hidden)
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if let Some(existing_functions) = &mut existing_functions {
|
if let Some(existing_functions) = &mut existing_functions {
|
||||||
if (symbol.flags.0.contains(ObjSymbolFlags::Global)
|
if (symbol.flags.contains(SymbolFlag::Global)
|
||||||
|| symbol.flags.0.contains(ObjSymbolFlags::Weak))
|
|| symbol.flags.contains(SymbolFlag::Weak))
|
||||||
&& !existing_functions.insert(symbol.name.clone())
|
&& !existing_functions.insert(symbol.name.clone())
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
#![allow(clippy::too_many_arguments)]
|
||||||
|
|
||||||
mod argp_version;
|
mod argp_version;
|
||||||
mod cmd;
|
mod cmd;
|
||||||
mod util;
|
mod util;
|
||||||
|
@ -4,10 +4,10 @@ use anyhow::{bail, Result};
|
|||||||
use crossterm::event::{Event, KeyCode, KeyEventKind, KeyModifiers, MouseButton, MouseEventKind};
|
use crossterm::event::{Event, KeyCode, KeyEventKind, KeyModifiers, MouseButton, MouseEventKind};
|
||||||
use objdiff_core::{
|
use objdiff_core::{
|
||||||
diff::{
|
diff::{
|
||||||
display::{display_diff, DiffText, HighlightKind},
|
display::{display_row, DiffText, HighlightKind},
|
||||||
FunctionRelocDiffs, ObjDiff, ObjInsDiffKind, ObjSymbolDiff,
|
DiffObjConfig, FunctionRelocDiffs, InstructionDiffKind, ObjectDiff, SymbolDiff,
|
||||||
},
|
},
|
||||||
obj::{ObjInfo, ObjSectionKind, ObjSymbol, SymbolRef},
|
obj::Object,
|
||||||
};
|
};
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
prelude::*,
|
prelude::*,
|
||||||
@ -30,9 +30,9 @@ pub struct FunctionDiffUi {
|
|||||||
pub scroll_state_y: ScrollbarState,
|
pub scroll_state_y: ScrollbarState,
|
||||||
pub per_page: usize,
|
pub per_page: usize,
|
||||||
pub num_rows: usize,
|
pub num_rows: usize,
|
||||||
pub left_sym: Option<SymbolRef>,
|
pub left_sym: Option<usize>,
|
||||||
pub right_sym: Option<SymbolRef>,
|
pub right_sym: Option<usize>,
|
||||||
pub prev_sym: Option<SymbolRef>,
|
pub prev_sym: Option<usize>,
|
||||||
pub open_options: bool,
|
pub open_options: bool,
|
||||||
pub three_way: bool,
|
pub three_way: bool,
|
||||||
}
|
}
|
||||||
@ -82,8 +82,8 @@ impl UiView for 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) =
|
if let Some(percent) = get_symbol(state.right_obj.as_ref(), self.right_sym)
|
||||||
get_symbol(state.right_obj.as_ref(), self.right_sym).and_then(|(_, d)| d.match_percent)
|
.and_then(|(_, _, d)| d.match_percent)
|
||||||
{
|
{
|
||||||
line_r.spans.push(Span::styled(
|
line_r.spans.push(Span::styled(
|
||||||
format!("{:.2}% ", percent),
|
format!("{:.2}% ", percent),
|
||||||
@ -108,13 +108,17 @@ impl UiView for FunctionDiffUi {
|
|||||||
let mut left_text = None;
|
let mut left_text = None;
|
||||||
let mut left_highlight = None;
|
let mut left_highlight = None;
|
||||||
let mut max_width = 0;
|
let mut max_width = 0;
|
||||||
if let Some((symbol, symbol_diff)) = get_symbol(state.left_obj.as_ref(), self.left_sym) {
|
if let Some((obj, symbol_idx, symbol_diff)) =
|
||||||
|
get_symbol(state.left_obj.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));
|
||||||
left_highlight = self.print_sym(
|
left_highlight = self.print_sym(
|
||||||
&mut text,
|
&mut text,
|
||||||
symbol,
|
obj,
|
||||||
|
symbol_idx,
|
||||||
symbol_diff,
|
symbol_diff,
|
||||||
|
&state.diff_obj_config,
|
||||||
rect,
|
rect,
|
||||||
&self.left_highlight,
|
&self.left_highlight,
|
||||||
result,
|
result,
|
||||||
@ -127,13 +131,17 @@ impl UiView for FunctionDiffUi {
|
|||||||
let mut right_text = None;
|
let mut right_text = None;
|
||||||
let mut right_highlight = None;
|
let mut right_highlight = None;
|
||||||
let mut margin_text = None;
|
let mut margin_text = None;
|
||||||
if let Some((symbol, symbol_diff)) = get_symbol(state.right_obj.as_ref(), self.right_sym) {
|
if let Some((obj, symbol_idx, symbol_diff)) =
|
||||||
|
get_symbol(state.right_obj.as_ref(), self.right_sym)
|
||||||
|
{
|
||||||
let mut text = Text::default();
|
let mut text = Text::default();
|
||||||
let rect = content_chunks[2].inner(Margin::new(0, 1));
|
let rect = content_chunks[2].inner(Margin::new(0, 1));
|
||||||
right_highlight = self.print_sym(
|
right_highlight = self.print_sym(
|
||||||
&mut text,
|
&mut text,
|
||||||
symbol,
|
obj,
|
||||||
|
symbol_idx,
|
||||||
symbol_diff,
|
symbol_diff,
|
||||||
|
&state.diff_obj_config,
|
||||||
rect,
|
rect,
|
||||||
&self.right_highlight,
|
&self.right_highlight,
|
||||||
result,
|
result,
|
||||||
@ -152,14 +160,17 @@ impl UiView for FunctionDiffUi {
|
|||||||
let mut prev_text = None;
|
let mut prev_text = None;
|
||||||
let mut prev_margin_text = None;
|
let mut prev_margin_text = None;
|
||||||
if self.three_way {
|
if self.three_way {
|
||||||
if let Some((symbol, symbol_diff)) = get_symbol(state.prev_obj.as_ref(), self.prev_sym)
|
if let Some((obj, symbol_idx, symbol_diff)) =
|
||||||
|
get_symbol(state.prev_obj.as_ref(), self.prev_sym)
|
||||||
{
|
{
|
||||||
let mut text = Text::default();
|
let mut text = Text::default();
|
||||||
let rect = content_chunks[4].inner(Margin::new(0, 1));
|
let rect = content_chunks[4].inner(Margin::new(0, 1));
|
||||||
self.print_sym(
|
self.print_sym(
|
||||||
&mut text,
|
&mut text,
|
||||||
symbol,
|
obj,
|
||||||
|
symbol_idx,
|
||||||
symbol_diff,
|
symbol_diff,
|
||||||
|
&state.diff_obj_config,
|
||||||
rect,
|
rect,
|
||||||
&self.right_highlight,
|
&self.right_highlight,
|
||||||
result,
|
result,
|
||||||
@ -437,9 +448,11 @@ impl UiView for FunctionDiffUi {
|
|||||||
get_symbol(state.left_obj.as_ref(), left_sym),
|
get_symbol(state.left_obj.as_ref(), left_sym),
|
||||||
get_symbol(state.right_obj.as_ref(), right_sym),
|
get_symbol(state.right_obj.as_ref(), right_sym),
|
||||||
) {
|
) {
|
||||||
(Some((_l, ld)), Some((_r, rd))) => ld.instructions.len().max(rd.instructions.len()),
|
(Some((_l, _ls, ld)), Some((_r, _rs, rd))) => {
|
||||||
(Some((_l, ld)), None) => ld.instructions.len(),
|
ld.instruction_rows.len().max(rd.instruction_rows.len())
|
||||||
(None, Some((_r, rd))) => rd.instructions.len(),
|
}
|
||||||
|
(Some((_l, _ls, ld)), None) => ld.instruction_rows.len(),
|
||||||
|
(None, Some((_r, _rs, rd))) => rd.instruction_rows.len(),
|
||||||
(None, None) => bail!("Symbol not found: {}", self.symbol_name),
|
(None, None) => bail!("Symbol not found: {}", self.symbol_name),
|
||||||
};
|
};
|
||||||
self.left_sym = left_sym;
|
self.left_sym = left_sym;
|
||||||
@ -482,52 +495,51 @@ impl FunctionDiffUi {
|
|||||||
self.scroll_y += self.per_page / if half { 2 } else { 1 };
|
self.scroll_y += self.per_page / if half { 2 } else { 1 };
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
fn print_sym(
|
fn print_sym(
|
||||||
&self,
|
&self,
|
||||||
out: &mut Text<'static>,
|
out: &mut Text<'static>,
|
||||||
symbol: &ObjSymbol,
|
obj: &Object,
|
||||||
symbol_diff: &ObjSymbolDiff,
|
symbol_index: usize,
|
||||||
|
symbol_diff: &SymbolDiff,
|
||||||
|
diff_config: &DiffObjConfig,
|
||||||
rect: Rect,
|
rect: Rect,
|
||||||
highlight: &HighlightKind,
|
highlight: &HighlightKind,
|
||||||
result: &EventResult,
|
result: &EventResult,
|
||||||
only_changed: bool,
|
only_changed: bool,
|
||||||
) -> Option<HighlightKind> {
|
) -> Option<HighlightKind> {
|
||||||
let base_addr = symbol.address;
|
|
||||||
let mut new_highlight = None;
|
let mut new_highlight = None;
|
||||||
for (y, ins_diff) in symbol_diff
|
for (y, ins_row) in symbol_diff
|
||||||
.instructions
|
.instruction_rows
|
||||||
.iter()
|
.iter()
|
||||||
.skip(self.scroll_y)
|
.skip(self.scroll_y)
|
||||||
.take(rect.height as usize)
|
.take(rect.height as usize)
|
||||||
.enumerate()
|
.enumerate()
|
||||||
{
|
{
|
||||||
if only_changed && ins_diff.kind == ObjInsDiffKind::None {
|
if only_changed && ins_row.kind == InstructionDiffKind::None {
|
||||||
out.lines.push(Line::default());
|
out.lines.push(Line::default());
|
||||||
continue;
|
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();
|
||||||
display_diff(ins_diff, base_addr, |text| -> Result<()> {
|
display_row(obj, symbol_index, ins_row, diff_config, |text, diff_idx| {
|
||||||
let label_text;
|
let label_text;
|
||||||
let mut base_color = match ins_diff.kind {
|
let mut base_color = match ins_row.kind {
|
||||||
ObjInsDiffKind::None
|
InstructionDiffKind::None
|
||||||
| ObjInsDiffKind::OpMismatch
|
| InstructionDiffKind::OpMismatch
|
||||||
| ObjInsDiffKind::ArgMismatch => Color::Gray,
|
| InstructionDiffKind::ArgMismatch => Color::Gray,
|
||||||
ObjInsDiffKind::Replace => Color::Cyan,
|
InstructionDiffKind::Replace => Color::Cyan,
|
||||||
ObjInsDiffKind::Delete => Color::Red,
|
InstructionDiffKind::Delete => Color::Red,
|
||||||
ObjInsDiffKind::Insert => Color::Green,
|
InstructionDiffKind::Insert => Color::Green,
|
||||||
};
|
};
|
||||||
|
if let Some(idx) = diff_idx.get() {
|
||||||
|
base_color = COLOR_ROTATION[idx as usize % COLOR_ROTATION.len()];
|
||||||
|
}
|
||||||
let mut pad_to = 0;
|
let mut pad_to = 0;
|
||||||
match text {
|
match text {
|
||||||
DiffText::Basic(text) => {
|
DiffText::Basic(text) => {
|
||||||
label_text = text.to_string();
|
label_text = text.to_string();
|
||||||
}
|
}
|
||||||
DiffText::BasicColor(s, idx) => {
|
|
||||||
label_text = s.to_string();
|
|
||||||
base_color = COLOR_ROTATION[idx % COLOR_ROTATION.len()];
|
|
||||||
}
|
|
||||||
DiffText::Line(num) => {
|
DiffText::Line(num) => {
|
||||||
label_text = format!("{num} ");
|
label_text = format!("{num} ");
|
||||||
base_color = Color::DarkGray;
|
base_color = Color::DarkGray;
|
||||||
@ -539,41 +551,31 @@ impl FunctionDiffUi {
|
|||||||
}
|
}
|
||||||
DiffText::Opcode(mnemonic, _op) => {
|
DiffText::Opcode(mnemonic, _op) => {
|
||||||
label_text = mnemonic.to_string();
|
label_text = mnemonic.to_string();
|
||||||
if ins_diff.kind == ObjInsDiffKind::OpMismatch {
|
if ins_row.kind == InstructionDiffKind::OpMismatch {
|
||||||
base_color = Color::Blue;
|
base_color = Color::Blue;
|
||||||
}
|
}
|
||||||
pad_to = 8;
|
pad_to = 8;
|
||||||
}
|
}
|
||||||
DiffText::Argument(arg, diff) => {
|
DiffText::Argument(arg) => {
|
||||||
label_text = arg.to_string();
|
label_text = arg.to_string();
|
||||||
if let Some(diff) = diff {
|
|
||||||
base_color = COLOR_ROTATION[diff.idx % COLOR_ROTATION.len()]
|
|
||||||
}
|
}
|
||||||
}
|
DiffText::BranchDest(addr) => {
|
||||||
DiffText::BranchDest(addr, diff) => {
|
|
||||||
label_text = format!("{addr:x}");
|
label_text = format!("{addr:x}");
|
||||||
if let Some(diff) = diff {
|
|
||||||
base_color = COLOR_ROTATION[diff.idx % COLOR_ROTATION.len()]
|
|
||||||
}
|
}
|
||||||
}
|
DiffText::Symbol(sym) => {
|
||||||
DiffText::Symbol(sym, diff) => {
|
|
||||||
let name = sym.demangled_name.as_ref().unwrap_or(&sym.name);
|
let name = sym.demangled_name.as_ref().unwrap_or(&sym.name);
|
||||||
label_text = name.clone();
|
label_text = name.clone();
|
||||||
if let Some(diff) = diff {
|
if diff_idx.is_none() {
|
||||||
base_color = COLOR_ROTATION[diff.idx % COLOR_ROTATION.len()]
|
|
||||||
} else {
|
|
||||||
base_color = Color::White;
|
base_color = Color::White;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DiffText::Addend(addend, diff) => {
|
DiffText::Addend(addend) => {
|
||||||
label_text = match addend.cmp(&0i64) {
|
label_text = match addend.cmp(&0i64) {
|
||||||
Ordering::Greater => format!("+{:#x}", addend),
|
Ordering::Greater => format!("+{:#x}", addend),
|
||||||
Ordering::Less => format!("-{:#x}", -addend),
|
Ordering::Less => format!("-{:#x}", -addend),
|
||||||
_ => "".to_string(),
|
_ => "".to_string(),
|
||||||
};
|
};
|
||||||
if let Some(diff) = diff {
|
if diff_idx.is_none() {
|
||||||
base_color = COLOR_ROTATION[diff.idx % COLOR_ROTATION.len()]
|
|
||||||
} else {
|
|
||||||
base_color = Color::White;
|
base_color = Color::White;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -612,12 +614,13 @@ impl FunctionDiffUi {
|
|||||||
new_highlight
|
new_highlight
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_margin(&self, out: &mut Text, symbol: &ObjSymbolDiff, rect: Rect) {
|
fn print_margin(&self, out: &mut Text, symbol: &SymbolDiff, rect: Rect) {
|
||||||
for ins_diff in symbol.instructions.iter().skip(self.scroll_y).take(rect.height as usize) {
|
for ins_row in symbol.instruction_rows.iter().skip(self.scroll_y).take(rect.height as usize)
|
||||||
if ins_diff.kind != ObjInsDiffKind::None {
|
{
|
||||||
out.lines.push(Line::raw(match ins_diff.kind {
|
if ins_row.kind != InstructionDiffKind::None {
|
||||||
ObjInsDiffKind::Delete => "<",
|
out.lines.push(Line::raw(match ins_row.kind {
|
||||||
ObjInsDiffKind::Insert => ">",
|
InstructionDiffKind::Delete => "<",
|
||||||
|
InstructionDiffKind::Insert => ">",
|
||||||
_ => "|",
|
_ => "|",
|
||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
@ -649,23 +652,18 @@ pub fn match_percent_color(match_percent: f32) -> Color {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn get_symbol(
|
fn get_symbol(
|
||||||
obj: Option<&(ObjInfo, ObjDiff)>,
|
obj: Option<&(Object, ObjectDiff)>,
|
||||||
sym: Option<SymbolRef>,
|
sym: Option<usize>,
|
||||||
) -> Option<(&ObjSymbol, &ObjSymbolDiff)> {
|
) -> Option<(&Object, usize, &SymbolDiff)> {
|
||||||
let (obj, diff) = obj?;
|
let (obj, diff) = obj?;
|
||||||
let sym = sym?;
|
let sym = sym?;
|
||||||
Some((obj.section_symbol(sym).1, diff.symbol_diff(sym)))
|
Some((obj, sym, &diff.symbols[sym]))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_function(obj: &ObjInfo, name: &str) -> Option<SymbolRef> {
|
fn find_function(obj: &Object, name: &str) -> Option<usize> {
|
||||||
for (section_idx, section) in obj.sections.iter().enumerate() {
|
for (symbol_idx, symbol) in obj.symbols.iter().enumerate() {
|
||||||
if section.kind != ObjSectionKind::Code {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for (symbol_idx, symbol) in section.symbols.iter().enumerate() {
|
|
||||||
if symbol.name == name {
|
if symbol.name == name {
|
||||||
return Some(SymbolRef { section_idx, symbol_idx });
|
return Some(symbol_idx);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
|
@ -12,9 +12,6 @@ A local diffing tool for decompilation projects.
|
|||||||
"""
|
"""
|
||||||
documentation = "https://docs.rs/objdiff-core"
|
documentation = "https://docs.rs/objdiff-core"
|
||||||
|
|
||||||
[lib]
|
|
||||||
crate-type = ["cdylib", "rlib"]
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
all = [
|
all = [
|
||||||
@ -25,11 +22,11 @@ all = [
|
|||||||
"dwarf",
|
"dwarf",
|
||||||
"serde",
|
"serde",
|
||||||
# Architectures
|
# Architectures
|
||||||
|
"arm",
|
||||||
|
"arm64",
|
||||||
"mips",
|
"mips",
|
||||||
"ppc",
|
"ppc",
|
||||||
"x86",
|
"x86",
|
||||||
"arm",
|
|
||||||
"arm64",
|
|
||||||
]
|
]
|
||||||
# Implicit, used to check if any arch is enabled
|
# Implicit, used to check if any arch is enabled
|
||||||
any-arch = [
|
any-arch = [
|
||||||
@ -41,6 +38,7 @@ any-arch = [
|
|||||||
"dep:prettyplease",
|
"dep:prettyplease",
|
||||||
"dep:proc-macro2",
|
"dep:proc-macro2",
|
||||||
"dep:quote",
|
"dep:quote",
|
||||||
|
"dep:regex",
|
||||||
"dep:similar",
|
"dep:similar",
|
||||||
"dep:strum",
|
"dep:strum",
|
||||||
"dep:syn",
|
"dep:syn",
|
||||||
@ -113,14 +111,6 @@ arm64 = [
|
|||||||
"dep:yaxpeax-arch",
|
"dep:yaxpeax-arch",
|
||||||
"dep:yaxpeax-arm",
|
"dep:yaxpeax-arm",
|
||||||
]
|
]
|
||||||
wasm = [
|
|
||||||
"any-arch",
|
|
||||||
"bindings",
|
|
||||||
"dep:log",
|
|
||||||
"dep:talc",
|
|
||||||
"dep:spin",
|
|
||||||
"dep:wit-bindgen",
|
|
||||||
]
|
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
features = ["all"]
|
features = ["all"]
|
||||||
@ -140,6 +130,8 @@ serde = { version = "1.0", default-features = false, features = ["derive"], opti
|
|||||||
similar = { version = "2.7", default-features = false, optional = true, git = "https://github.com/encounter/similar.git", branch = "no_std" }
|
similar = { version = "2.7", default-features = false, optional = true, git = "https://github.com/encounter/similar.git", branch = "no_std" }
|
||||||
strum = { version = "0.26", default-features = false, features = ["derive"], optional = true }
|
strum = { version = "0.26", default-features = false, features = ["derive"], optional = true }
|
||||||
typed-path = { version = "0.10", default-features = false, optional = true }
|
typed-path = { version = "0.10", default-features = false, optional = true }
|
||||||
|
regex = { version = "1.11", default-features = false, features = [], optional = true }
|
||||||
|
itertools = { version = "0.14", default-features = false, features = ["use_alloc"] }
|
||||||
|
|
||||||
# config
|
# config
|
||||||
globset = { version = "0.4", default-features = false, optional = true }
|
globset = { version = "0.4", default-features = false, optional = true }
|
||||||
@ -170,13 +162,6 @@ arm-attr = { version = "0.2", optional = true }
|
|||||||
yaxpeax-arch = { version = "0.3", default-features = false, optional = true }
|
yaxpeax-arch = { version = "0.3", default-features = false, optional = true }
|
||||||
yaxpeax-arm = { version = "0.3", default-features = false, optional = true }
|
yaxpeax-arm = { version = "0.3", default-features = false, optional = true }
|
||||||
|
|
||||||
# wasm
|
|
||||||
#console_error_panic_hook = { version = "0.1", optional = true }
|
|
||||||
#console_log = { version = "1.0", optional = true }
|
|
||||||
talc = { version = "4.4", optional = true }
|
|
||||||
spin = { version = "0.9", optional = true }
|
|
||||||
wit-bindgen = { version = "0.38", default-features = false, features = ["macros"], optional = true }
|
|
||||||
|
|
||||||
# build
|
# build
|
||||||
notify = { version = "8.0.0", optional = true }
|
notify = { version = "8.0.0", optional = true }
|
||||||
notify-debouncer-full = { version = "0.5.0", optional = true }
|
notify-debouncer-full = { version = "0.5.0", optional = true }
|
||||||
@ -207,3 +192,21 @@ quote = { version = "1.0", optional = true }
|
|||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = { version = "1.0" }
|
serde_json = { version = "1.0" }
|
||||||
syn = { version = "2.0", optional = true }
|
syn = { version = "2.0", optional = true }
|
||||||
|
|
||||||
|
# Enable all features for tests
|
||||||
|
[dev-dependencies]
|
||||||
|
objdiff-core = { path = ".", features = [
|
||||||
|
# Features
|
||||||
|
"bindings",
|
||||||
|
"build",
|
||||||
|
"config",
|
||||||
|
"dwarf",
|
||||||
|
"serde",
|
||||||
|
# Architectures
|
||||||
|
"mips",
|
||||||
|
"ppc",
|
||||||
|
# "x86",
|
||||||
|
"arm",
|
||||||
|
"arm64",
|
||||||
|
] }
|
||||||
|
insta = "1.42.1"
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
#[cfg(feature = "any-arch")]
|
#[cfg(feature = "any-arch")]
|
||||||
mod config_gen;
|
mod config_gen;
|
||||||
|
|
||||||
fn main() {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
#[cfg(feature = "bindings")]
|
#[cfg(feature = "bindings")]
|
||||||
compile_protos();
|
compile_protos();
|
||||||
#[cfg(feature = "any-arch")]
|
#[cfg(feature = "any-arch")]
|
||||||
config_gen::generate_diff_config();
|
config_gen::generate_diff_config();
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "bindings")]
|
#[cfg(feature = "bindings")]
|
||||||
@ -18,7 +19,7 @@ fn compile_protos() {
|
|||||||
.map(|m| m.modified().unwrap())
|
.map(|m| m.modified().unwrap())
|
||||||
.unwrap_or(std::time::SystemTime::UNIX_EPOCH);
|
.unwrap_or(std::time::SystemTime::UNIX_EPOCH);
|
||||||
let mut run_protoc = false;
|
let mut run_protoc = false;
|
||||||
let proto_files = vec![root.join("diff.proto"), root.join("report.proto")];
|
let proto_files = vec![root.join("report.proto")];
|
||||||
for proto_file in &proto_files {
|
for proto_file in &proto_files {
|
||||||
println!("cargo:rerun-if-changed={}", proto_file.display());
|
println!("cargo:rerun-if-changed={}", proto_file.display());
|
||||||
let mtime = match std::fs::metadata(proto_file) {
|
let mtime = match std::fs::metadata(proto_file) {
|
||||||
|
@ -39,6 +39,13 @@
|
|||||||
"name": "Combine data sections",
|
"name": "Combine data sections",
|
||||||
"description": "Combines data sections with equal names."
|
"description": "Combines data sections with equal names."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "combineTextSections",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false,
|
||||||
|
"name": "Combine text sections",
|
||||||
|
"description": "Combines all text sections into one."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "arm.archVersion",
|
"id": "arm.archVersion",
|
||||||
"type": "choice",
|
"type": "choice",
|
||||||
@ -213,7 +220,8 @@
|
|||||||
"properties": [
|
"properties": [
|
||||||
"functionRelocDiffs",
|
"functionRelocDiffs",
|
||||||
"spaceBetweenArgs",
|
"spaceBetweenArgs",
|
||||||
"combineDataSections"
|
"combineDataSections",
|
||||||
|
"combineTextSections"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -87,11 +87,11 @@ message Relocation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
message RelocationTarget {
|
message RelocationTarget {
|
||||||
Symbol symbol = 1;
|
uint32 symbol_index = 1;
|
||||||
int64 addend = 2;
|
int64 addend = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message InstructionDiff {
|
message InstructionDiffRow {
|
||||||
DiffKind diff_kind = 1;
|
DiffKind diff_kind = 1;
|
||||||
optional Instruction instruction = 2;
|
optional Instruction instruction = 2;
|
||||||
optional InstructionBranchFrom branch_from = 3;
|
optional InstructionBranchFrom branch_from = 3;
|
||||||
@ -122,17 +122,12 @@ message InstructionBranchTo {
|
|||||||
uint32 branch_index = 2;
|
uint32 branch_index = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message SymbolRef {
|
|
||||||
optional uint32 section_index = 1;
|
|
||||||
uint32 symbol_index = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message SymbolDiff {
|
message SymbolDiff {
|
||||||
Symbol symbol = 1;
|
Symbol symbol = 1;
|
||||||
repeated InstructionDiff instructions = 2;
|
repeated InstructionDiffRow instruction_rows = 2;
|
||||||
optional float match_percent = 3;
|
optional float match_percent = 3;
|
||||||
// The symbol ref in the _other_ object that this symbol was diffed against
|
// The symbol index in the _other_ object that this symbol was diffed against
|
||||||
optional SymbolRef target = 5;
|
optional uint32 target_symbol = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
message DataDiff {
|
message DataDiff {
|
||||||
@ -147,7 +142,7 @@ message SectionDiff {
|
|||||||
SectionKind kind = 2;
|
SectionKind kind = 2;
|
||||||
uint64 size = 3;
|
uint64 size = 3;
|
||||||
uint64 address = 4;
|
uint64 address = 4;
|
||||||
repeated SymbolDiff symbols = 5;
|
reserved 5;
|
||||||
repeated DataDiff data = 6;
|
repeated DataDiff data = 6;
|
||||||
optional float match_percent = 7;
|
optional float match_percent = 7;
|
||||||
}
|
}
|
||||||
@ -157,11 +152,11 @@ enum SectionKind {
|
|||||||
SECTION_TEXT = 1;
|
SECTION_TEXT = 1;
|
||||||
SECTION_DATA = 2;
|
SECTION_DATA = 2;
|
||||||
SECTION_BSS = 3;
|
SECTION_BSS = 3;
|
||||||
SECTION_COMMON = 4;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message ObjectDiff {
|
message ObjectDiff {
|
||||||
repeated SectionDiff sections = 1;
|
repeated SectionDiff sections = 1;
|
||||||
|
repeated SymbolDiff symbols = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message DiffResult {
|
message DiffResult {
|
||||||
|
Binary file not shown.
@ -3,41 +3,37 @@ use alloc::{
|
|||||||
collections::BTreeMap,
|
collections::BTreeMap,
|
||||||
format,
|
format,
|
||||||
string::{String, ToString},
|
string::{String, ToString},
|
||||||
vec,
|
|
||||||
vec::Vec,
|
vec::Vec,
|
||||||
};
|
};
|
||||||
|
use core::ops::Range;
|
||||||
|
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
use arm_attr::{enums::CpuArch, tag::Tag, BuildAttrs};
|
use arm_attr::{enums::CpuArch, tag::Tag, BuildAttrs};
|
||||||
use object::{
|
use object::{elf, Endian as _, Object as _, ObjectSection as _, ObjectSymbol as _};
|
||||||
elf::{self, SHT_ARM_ATTRIBUTES},
|
use unarm::{args, arm, thumb};
|
||||||
Endian, File, Object, ObjectSection, ObjectSymbol, Relocation, RelocationFlags, SectionKind,
|
|
||||||
Symbol, SymbolKind,
|
|
||||||
};
|
|
||||||
use unarm::{
|
|
||||||
args::{Argument, OffsetImm, OffsetReg, Register},
|
|
||||||
parse::{ArmVersion, ParseMode, Parser},
|
|
||||||
DisplayOptions, ParseFlags, ParsedIns, RegNames,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
arch::{ObjArch, ProcessCodeResult},
|
arch::Arch,
|
||||||
diff::{ArmArchVersion, ArmR9Usage, DiffObjConfig},
|
diff::{display::InstructionPart, ArmArchVersion, ArmR9Usage, DiffObjConfig},
|
||||||
obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection},
|
obj::{
|
||||||
|
InstructionArg, InstructionArgValue, InstructionRef, RelocationFlags, ResolvedRelocation,
|
||||||
|
ScannedInstruction, SymbolFlag, SymbolFlagSet, SymbolKind,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct ObjArchArm {
|
#[derive(Debug)]
|
||||||
|
pub struct ArchArm {
|
||||||
/// Maps section index, to list of disasm modes (arm, thumb or data) sorted by address
|
/// Maps section index, to list of disasm modes (arm, thumb or data) sorted by address
|
||||||
disasm_modes: BTreeMap<usize, Vec<DisasmMode>>,
|
disasm_modes: BTreeMap<usize, Vec<DisasmMode>>,
|
||||||
detected_version: Option<ArmVersion>,
|
detected_version: Option<unarm::ArmVersion>,
|
||||||
endianness: object::Endianness,
|
endianness: object::Endianness,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjArchArm {
|
impl ArchArm {
|
||||||
pub fn new(file: &File) -> Result<Self> {
|
pub fn new(file: &object::File) -> Result<Self> {
|
||||||
let endianness = file.endianness();
|
let endianness = file.endianness();
|
||||||
match file {
|
match file {
|
||||||
File::Elf32(_) => {
|
object::File::Elf32(_) => {
|
||||||
let disasm_modes = Self::elf_get_mapping_symbols(file);
|
let disasm_modes = Self::elf_get_mapping_symbols(file);
|
||||||
let detected_version = Self::elf_detect_arm_version(file)?;
|
let detected_version = Self::elf_detect_arm_version(file)?;
|
||||||
Ok(Self { disasm_modes, detected_version, endianness })
|
Ok(Self { disasm_modes, detected_version, endianness })
|
||||||
@ -46,10 +42,11 @@ impl ObjArchArm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn elf_detect_arm_version(file: &File) -> Result<Option<ArmVersion>> {
|
fn elf_detect_arm_version(file: &object::File) -> Result<Option<unarm::ArmVersion>> {
|
||||||
// Check ARM attributes
|
// Check ARM attributes
|
||||||
if let Some(arm_attrs) = file.sections().find(|s| {
|
if let Some(arm_attrs) = file.sections().find(|s| {
|
||||||
s.kind() == SectionKind::Elf(SHT_ARM_ATTRIBUTES) && s.name() == Ok(".ARM.attributes")
|
s.kind() == object::SectionKind::Elf(elf::SHT_ARM_ATTRIBUTES)
|
||||||
|
&& s.name() == Ok(".ARM.attributes")
|
||||||
}) {
|
}) {
|
||||||
let attr_data = arm_attrs.uncompressed_data()?;
|
let attr_data = arm_attrs.uncompressed_data()?;
|
||||||
let build_attrs = BuildAttrs::new(&attr_data, match file.endianness() {
|
let build_attrs = BuildAttrs::new(&attr_data, match file.endianness() {
|
||||||
@ -70,9 +67,9 @@ impl ObjArchArm {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
match cpu_arch {
|
match cpu_arch {
|
||||||
Some(CpuArch::V4T) => return Ok(Some(ArmVersion::V4T)),
|
Some(CpuArch::V4T) => return Ok(Some(unarm::ArmVersion::V4T)),
|
||||||
Some(CpuArch::V5TE) => return Ok(Some(ArmVersion::V5Te)),
|
Some(CpuArch::V5TE) => return Ok(Some(unarm::ArmVersion::V5Te)),
|
||||||
Some(CpuArch::V6K) => return Ok(Some(ArmVersion::V6K)),
|
Some(CpuArch::V6K) => return Ok(Some(unarm::ArmVersion::V6K)),
|
||||||
Some(arch) => bail!("ARM arch {} not supported", arch),
|
Some(arch) => bail!("ARM arch {} not supported", arch),
|
||||||
None => {}
|
None => {}
|
||||||
};
|
};
|
||||||
@ -82,9 +79,9 @@ impl ObjArchArm {
|
|||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn elf_get_mapping_symbols(file: &File) -> BTreeMap<usize, Vec<DisasmMode>> {
|
fn elf_get_mapping_symbols(file: &object::File) -> BTreeMap<usize, Vec<DisasmMode>> {
|
||||||
file.sections()
|
file.sections()
|
||||||
.filter(|s| s.kind() == SectionKind::Text)
|
.filter(|s| s.kind() == object::SectionKind::Text)
|
||||||
.map(|s| {
|
.map(|s| {
|
||||||
let index = s.index();
|
let index = s.index();
|
||||||
let mut mapping_symbols: Vec<_> = file
|
let mut mapping_symbols: Vec<_> = file
|
||||||
@ -97,32 +94,95 @@ impl ObjArchArm {
|
|||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn endian(&self) -> unarm::Endian {
|
||||||
|
match self.endianness {
|
||||||
|
object::Endianness::Little => unarm::Endian::Little,
|
||||||
|
object::Endianness::Big => unarm::Endian::Big,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjArch for ObjArchArm {
|
fn parse_flags(&self, diff_config: &DiffObjConfig) -> unarm::ParseFlags {
|
||||||
fn symbol_address(&self, symbol: &Symbol) -> u64 {
|
unarm::ParseFlags {
|
||||||
let address = symbol.address();
|
ual: diff_config.arm_unified_syntax,
|
||||||
if symbol.kind() == SymbolKind::Text {
|
version: match diff_config.arm_arch_version {
|
||||||
address & !1
|
ArmArchVersion::Auto => self.detected_version.unwrap_or(unarm::ArmVersion::V5Te),
|
||||||
|
ArmArchVersion::V4t => unarm::ArmVersion::V4T,
|
||||||
|
ArmArchVersion::V5te => unarm::ArmVersion::V5Te,
|
||||||
|
ArmArchVersion::V6k => unarm::ArmVersion::V6K,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn display_options(&self, diff_config: &DiffObjConfig) -> unarm::DisplayOptions {
|
||||||
|
unarm::DisplayOptions {
|
||||||
|
reg_names: unarm::RegNames {
|
||||||
|
av_registers: diff_config.arm_av_registers,
|
||||||
|
r9_use: match diff_config.arm_r9_usage {
|
||||||
|
ArmR9Usage::GeneralPurpose => unarm::R9Use::GeneralPurpose,
|
||||||
|
ArmR9Usage::Sb => unarm::R9Use::Pid,
|
||||||
|
ArmR9Usage::Tr => unarm::R9Use::Tls,
|
||||||
|
},
|
||||||
|
explicit_stack_limit: diff_config.arm_sl_usage,
|
||||||
|
frame_pointer: diff_config.arm_fp_usage,
|
||||||
|
ip: diff_config.arm_ip_usage,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_ins_ref(
|
||||||
|
&self,
|
||||||
|
ins_ref: InstructionRef,
|
||||||
|
code: &[u8],
|
||||||
|
diff_config: &DiffObjConfig,
|
||||||
|
) -> Result<(unarm::Ins, unarm::ParsedIns)> {
|
||||||
|
let code = match (self.endianness, ins_ref.size) {
|
||||||
|
(object::Endianness::Little, 2) => u16::from_le_bytes([code[0], code[1]]) as u32,
|
||||||
|
(object::Endianness::Little, 4) => {
|
||||||
|
u32::from_le_bytes([code[0], code[1], code[2], code[3]])
|
||||||
|
}
|
||||||
|
(object::Endianness::Big, 2) => u16::from_be_bytes([code[0], code[1]]) as u32,
|
||||||
|
(object::Endianness::Big, 4) => {
|
||||||
|
u32::from_be_bytes([code[0], code[1], code[2], code[3]])
|
||||||
|
}
|
||||||
|
_ => bail!("Invalid instruction size {}", ins_ref.size),
|
||||||
|
};
|
||||||
|
let (ins, parsed_ins) = if ins_ref.opcode == u16::MAX {
|
||||||
|
let mut args = args::Arguments::default();
|
||||||
|
args[0] = args::Argument::UImm(code);
|
||||||
|
let mnemonic = if ins_ref.size == 4 { ".word" } else { ".hword" };
|
||||||
|
(unarm::Ins::Data, unarm::ParsedIns { mnemonic, args })
|
||||||
|
} else if ins_ref.opcode & (1 << 15) != 0 {
|
||||||
|
let ins = arm::Ins { code, op: arm::Opcode::from(ins_ref.opcode as u8) };
|
||||||
|
let parsed = ins.parse(&self.parse_flags(diff_config));
|
||||||
|
(unarm::Ins::Arm(ins), parsed)
|
||||||
} else {
|
} else {
|
||||||
address
|
let ins = thumb::Ins { code, op: thumb::Opcode::from(ins_ref.opcode as u8) };
|
||||||
|
let parsed = ins.parse(&self.parse_flags(diff_config));
|
||||||
|
if ins.is_half_bl() {
|
||||||
|
todo!("Combine thumb BL instructions");
|
||||||
|
} else {
|
||||||
|
(unarm::Ins::Thumb(ins), parsed)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok((ins, parsed_ins))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_code(
|
impl Arch for ArchArm {
|
||||||
|
fn scan_instructions(
|
||||||
&self,
|
&self,
|
||||||
address: u64,
|
address: u64,
|
||||||
code: &[u8],
|
code: &[u8],
|
||||||
section_index: usize,
|
section_index: usize,
|
||||||
relocations: &[ObjReloc],
|
diff_config: &DiffObjConfig,
|
||||||
line_info: &BTreeMap<u64, u32>,
|
) -> Result<Vec<ScannedInstruction>> {
|
||||||
config: &DiffObjConfig,
|
|
||||||
) -> Result<ProcessCodeResult> {
|
|
||||||
let start_addr = address as u32;
|
let start_addr = address as u32;
|
||||||
let end_addr = start_addr + code.len() as u32;
|
let end_addr = start_addr + code.len() as u32;
|
||||||
|
|
||||||
// Mapping symbols decide what kind of data comes after it. $a for ARM code, $t for Thumb code and $d for data.
|
// Mapping symbols decide what kind of data comes after it. $a for ARM code, $t for Thumb code and $d for data.
|
||||||
let fallback_mappings = [DisasmMode { address: start_addr, mapping: ParseMode::Arm }];
|
let fallback_mappings =
|
||||||
|
[DisasmMode { address: start_addr, mapping: unarm::ParseMode::Arm }];
|
||||||
let mapping_symbols = self
|
let mapping_symbols = self
|
||||||
.disasm_modes
|
.disasm_modes
|
||||||
.get(§ion_index)
|
.get(§ion_index)
|
||||||
@ -138,39 +198,14 @@ impl ObjArch for ObjArchArm {
|
|||||||
let mut next_mapping = mappings_iter.next();
|
let mut next_mapping = mappings_iter.next();
|
||||||
|
|
||||||
let ins_count = code.len() / first_mapping.instruction_size(start_addr);
|
let ins_count = code.len() / first_mapping.instruction_size(start_addr);
|
||||||
let mut ops = Vec::<u16>::with_capacity(ins_count);
|
let mut ops = Vec::<ScannedInstruction>::with_capacity(ins_count);
|
||||||
let mut insts = Vec::<ObjIns>::with_capacity(ins_count);
|
|
||||||
|
|
||||||
let version = match config.arm_arch_version {
|
let endian = self.endian();
|
||||||
ArmArchVersion::Auto => self.detected_version.unwrap_or(ArmVersion::V5Te),
|
let parse_flags = self.parse_flags(diff_config);
|
||||||
ArmArchVersion::V4t => ArmVersion::V4T,
|
let mut parser = unarm::Parser::new(first_mapping, start_addr, endian, parse_flags, code);
|
||||||
ArmArchVersion::V5te => ArmVersion::V5Te,
|
|
||||||
ArmArchVersion::V6k => ArmVersion::V6K,
|
|
||||||
};
|
|
||||||
let endian = match self.endianness {
|
|
||||||
object::Endianness::Little => unarm::Endian::Little,
|
|
||||||
object::Endianness::Big => unarm::Endian::Big,
|
|
||||||
};
|
|
||||||
|
|
||||||
let parse_flags = ParseFlags { ual: config.arm_unified_syntax, version };
|
while let Some((address, ins, _parsed_ins)) = parser.next() {
|
||||||
|
let size = parser.mode.instruction_size(address);
|
||||||
let mut parser = Parser::new(first_mapping, start_addr, endian, parse_flags, code);
|
|
||||||
|
|
||||||
let display_options = DisplayOptions {
|
|
||||||
reg_names: RegNames {
|
|
||||||
av_registers: config.arm_av_registers,
|
|
||||||
r9_use: match config.arm_r9_usage {
|
|
||||||
ArmR9Usage::GeneralPurpose => unarm::R9Use::GeneralPurpose,
|
|
||||||
ArmR9Usage::Sb => unarm::R9Use::Pid,
|
|
||||||
ArmR9Usage::Tr => unarm::R9Use::Tls,
|
|
||||||
},
|
|
||||||
explicit_stack_limit: config.arm_sl_usage,
|
|
||||||
frame_pointer: config.arm_fp_usage,
|
|
||||||
ip: config.arm_ip_usage,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
while let Some((address, ins, parsed_ins)) = parser.next() {
|
|
||||||
if let Some(next) = next_mapping {
|
if let Some(next) = next_mapping {
|
||||||
let next_address = parser.address;
|
let next_address = parser.address;
|
||||||
if next_address >= next.address {
|
if next_address >= next.address {
|
||||||
@ -179,82 +214,95 @@ impl ObjArch for ObjArchArm {
|
|||||||
next_mapping = mappings_iter.next();
|
next_mapping = mappings_iter.next();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let line = line_info.range(..=address as u64).last().map(|(_, &b)| b);
|
let (opcode, branch_dest) = match ins {
|
||||||
|
unarm::Ins::Arm(x) => {
|
||||||
let reloc = relocations.iter().find(|r| (r.address as u32 & !1) == address).cloned();
|
let opcode = x.op as u16 | (1 << 15);
|
||||||
|
let branch_dest = match x.op {
|
||||||
let mut reloc_arg = None;
|
arm::Opcode::B | arm::Opcode::Bl => {
|
||||||
if let Some(reloc) = &reloc {
|
address.checked_add_signed(x.field_branch_offset())
|
||||||
match reloc.flags {
|
|
||||||
// Calls
|
|
||||||
RelocationFlags::Elf { r_type: elf::R_ARM_THM_XPC22 }
|
|
||||||
| RelocationFlags::Elf { r_type: elf::R_ARM_THM_PC22 }
|
|
||||||
| RelocationFlags::Elf { r_type: elf::R_ARM_PC24 }
|
|
||||||
| RelocationFlags::Elf { r_type: elf::R_ARM_XPC25 }
|
|
||||||
| RelocationFlags::Elf { r_type: elf::R_ARM_CALL } => {
|
|
||||||
reloc_arg = parsed_ins
|
|
||||||
.args
|
|
||||||
.iter()
|
|
||||||
.rposition(|a| matches!(a, Argument::BranchDest(_)));
|
|
||||||
}
|
|
||||||
// Data
|
|
||||||
RelocationFlags::Elf { r_type: elf::R_ARM_ABS32 } => {
|
|
||||||
reloc_arg =
|
|
||||||
parsed_ins.args.iter().rposition(|a| matches!(a, Argument::UImm(_)));
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
}
|
||||||
|
arm::Opcode::BlxI => address.checked_add_signed(x.field_blx_offset()),
|
||||||
|
_ => None,
|
||||||
};
|
};
|
||||||
|
(opcode, branch_dest)
|
||||||
let (args, branch_dest) = if reloc.is_some() && parser.mode == ParseMode::Data {
|
}
|
||||||
(vec![ObjInsArg::Reloc], None)
|
unarm::Ins::Thumb(x) => {
|
||||||
} else {
|
let opcode = x.op as u16;
|
||||||
push_args(&parsed_ins, config, reloc_arg, address, display_options)?
|
let branch_dest = match x.op {
|
||||||
|
thumb::Opcode::B | thumb::Opcode::Bl => {
|
||||||
|
address.checked_add_signed(x.field_branch_offset_8())
|
||||||
|
}
|
||||||
|
thumb::Opcode::BLong => {
|
||||||
|
address.checked_add_signed(x.field_branch_offset_11())
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
};
|
};
|
||||||
|
(opcode, branch_dest)
|
||||||
ops.push(ins.opcode_id());
|
}
|
||||||
insts.push(ObjIns {
|
unarm::Ins::Data => (u16::MAX, None),
|
||||||
address: address as u64,
|
};
|
||||||
size: (parser.address - address) as u8,
|
ops.push(ScannedInstruction {
|
||||||
op: ins.opcode_id(),
|
ins_ref: InstructionRef { address: address as u64, size: size as u8, opcode },
|
||||||
mnemonic: Cow::Borrowed(parsed_ins.mnemonic),
|
branch_dest: branch_dest.map(|x| x as u64),
|
||||||
args,
|
|
||||||
reloc,
|
|
||||||
branch_dest,
|
|
||||||
line,
|
|
||||||
formatted: parsed_ins.display(display_options).to_string(),
|
|
||||||
orig: None,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(ProcessCodeResult { ops, insts })
|
Ok(ops)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn display_instruction(
|
||||||
|
&self,
|
||||||
|
ins_ref: InstructionRef,
|
||||||
|
code: &[u8],
|
||||||
|
relocation: Option<ResolvedRelocation>,
|
||||||
|
_function_range: Range<u64>,
|
||||||
|
_section_index: usize,
|
||||||
|
diff_config: &DiffObjConfig,
|
||||||
|
cb: &mut dyn FnMut(InstructionPart) -> Result<()>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let (ins, parsed_ins) = self.parse_ins_ref(ins_ref, code, diff_config)?;
|
||||||
|
cb(InstructionPart::Opcode(Cow::Borrowed(parsed_ins.mnemonic), ins_ref.opcode))?;
|
||||||
|
if ins == unarm::Ins::Data && relocation.is_some() {
|
||||||
|
cb(InstructionPart::Arg(InstructionArg::Reloc))?;
|
||||||
|
} else {
|
||||||
|
push_args(
|
||||||
|
&parsed_ins,
|
||||||
|
relocation,
|
||||||
|
ins_ref.address as u32,
|
||||||
|
self.display_options(diff_config),
|
||||||
|
cb,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn implcit_addend(
|
fn implcit_addend(
|
||||||
&self,
|
&self,
|
||||||
_file: &File<'_>,
|
_file: &object::File<'_>,
|
||||||
section: &ObjSection,
|
section: &object::Section,
|
||||||
address: u64,
|
address: u64,
|
||||||
reloc: &Relocation,
|
_relocation: &object::Relocation,
|
||||||
|
flags: RelocationFlags,
|
||||||
) -> Result<i64> {
|
) -> Result<i64> {
|
||||||
|
let section_data = section.data()?;
|
||||||
let address = address as usize;
|
let address = address as usize;
|
||||||
Ok(match reloc.flags() {
|
Ok(match flags {
|
||||||
// ARM calls
|
// ARM calls
|
||||||
RelocationFlags::Elf { r_type: elf::R_ARM_PC24 }
|
RelocationFlags::Elf(elf::R_ARM_PC24)
|
||||||
| RelocationFlags::Elf { r_type: elf::R_ARM_XPC25 }
|
| RelocationFlags::Elf(elf::R_ARM_XPC25)
|
||||||
| RelocationFlags::Elf { r_type: elf::R_ARM_CALL } => {
|
| RelocationFlags::Elf(elf::R_ARM_CALL) => {
|
||||||
let data = section.data[address..address + 4].try_into()?;
|
let data = section_data[address..address + 4].try_into()?;
|
||||||
let addend = self.endianness.read_i32_bytes(data);
|
let addend = self.endianness.read_i32_bytes(data);
|
||||||
let imm24 = addend & 0xffffff;
|
let imm24 = addend & 0xffffff;
|
||||||
(imm24 << 2) << 8 >> 8
|
(imm24 << 2) << 8 >> 8
|
||||||
}
|
}
|
||||||
|
|
||||||
// Thumb calls
|
// Thumb calls
|
||||||
RelocationFlags::Elf { r_type: elf::R_ARM_THM_PC22 }
|
RelocationFlags::Elf(elf::R_ARM_THM_PC22)
|
||||||
| RelocationFlags::Elf { r_type: elf::R_ARM_THM_XPC22 } => {
|
| RelocationFlags::Elf(elf::R_ARM_THM_XPC22) => {
|
||||||
let data = section.data[address..address + 2].try_into()?;
|
let data = section_data[address..address + 2].try_into()?;
|
||||||
let high = self.endianness.read_i16_bytes(data) as i32;
|
let high = self.endianness.read_i16_bytes(data) as i32;
|
||||||
let data = section.data[address + 2..address + 4].try_into()?;
|
let data = section_data[address + 2..address + 4].try_into()?;
|
||||||
let low = self.endianness.read_i16_bytes(data) as i32;
|
let low = self.endianness.read_i16_bytes(data) as i32;
|
||||||
|
|
||||||
let imm22 = ((high & 0x7ff) << 11) | (low & 0x7ff);
|
let imm22 = ((high & 0x7ff) << 11) | (low & 0x7ff);
|
||||||
@ -262,8 +310,8 @@ impl ObjArch for ObjArchArm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Data
|
// Data
|
||||||
RelocationFlags::Elf { r_type: elf::R_ARM_ABS32 } => {
|
RelocationFlags::Elf(elf::R_ARM_ABS32) => {
|
||||||
let data = section.data[address..address + 4].try_into()?;
|
let data = section_data[address..address + 4].try_into()?;
|
||||||
self.endianness.read_i32_bytes(data)
|
self.endianness.read_i32_bytes(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -283,7 +331,7 @@ impl ObjArch for ObjArchArm {
|
|||||||
|
|
||||||
fn get_reloc_byte_size(&self, flags: RelocationFlags) -> usize {
|
fn get_reloc_byte_size(&self, flags: RelocationFlags) -> usize {
|
||||||
match flags {
|
match flags {
|
||||||
RelocationFlags::Elf { r_type } => match r_type {
|
RelocationFlags::Elf(r_type) => match r_type {
|
||||||
elf::R_ARM_ABS32 => 4,
|
elf::R_ARM_ABS32 => 4,
|
||||||
elf::R_ARM_REL32 => 4,
|
elf::R_ARM_REL32 => 4,
|
||||||
elf::R_ARM_ABS16 => 2,
|
elf::R_ARM_ABS16 => 2,
|
||||||
@ -293,48 +341,67 @@ impl ObjArch for ObjArchArm {
|
|||||||
_ => 1,
|
_ => 1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn symbol_address(&self, address: u64, kind: SymbolKind) -> u64 {
|
||||||
|
if kind == SymbolKind::Function {
|
||||||
|
address & !1
|
||||||
|
} else {
|
||||||
|
address
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extra_symbol_flags(&self, symbol: &object::Symbol) -> SymbolFlagSet {
|
||||||
|
let mut flags = SymbolFlagSet::default();
|
||||||
|
if DisasmMode::from_symbol(symbol).is_some() {
|
||||||
|
flags |= SymbolFlag::Hidden;
|
||||||
|
}
|
||||||
|
flags
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
struct DisasmMode {
|
struct DisasmMode {
|
||||||
address: u32,
|
address: u32,
|
||||||
mapping: ParseMode,
|
mapping: unarm::ParseMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DisasmMode {
|
impl DisasmMode {
|
||||||
fn from_symbol<'a>(sym: &Symbol<'a, '_, &'a [u8]>) -> Option<Self> {
|
fn from_symbol<'a>(sym: &object::Symbol<'a, '_, &'a [u8]>) -> Option<Self> {
|
||||||
if let Ok(name) = sym.name() {
|
sym.name()
|
||||||
ParseMode::from_mapping_symbol(name)
|
.ok()
|
||||||
|
.and_then(unarm::ParseMode::from_mapping_symbol)
|
||||||
.map(|mapping| DisasmMode { address: sym.address() as u32, mapping })
|
.map(|mapping| DisasmMode { address: sym.address() as u32, mapping })
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_args(
|
fn push_args(
|
||||||
parsed_ins: &ParsedIns,
|
parsed_ins: &unarm::ParsedIns,
|
||||||
config: &DiffObjConfig,
|
relocation: Option<ResolvedRelocation>,
|
||||||
reloc_arg: Option<usize>,
|
|
||||||
cur_addr: u32,
|
cur_addr: u32,
|
||||||
display_options: DisplayOptions,
|
display_options: unarm::DisplayOptions,
|
||||||
) -> Result<(Vec<ObjInsArg>, Option<u64>)> {
|
mut arg_cb: impl FnMut(InstructionPart) -> Result<()>,
|
||||||
let mut args = vec![];
|
) -> Result<()> {
|
||||||
let mut branch_dest = None;
|
let reloc_arg = find_reloc_arg(parsed_ins, relocation);
|
||||||
let mut writeback = false;
|
let mut writeback = false;
|
||||||
let mut deref = false;
|
let mut deref = false;
|
||||||
for (i, arg) in parsed_ins.args_iter().enumerate() {
|
for (i, arg) in parsed_ins.args_iter().enumerate() {
|
||||||
// Emit punctuation before separator
|
// Emit punctuation before separator
|
||||||
if deref {
|
if deref {
|
||||||
match arg {
|
match arg {
|
||||||
Argument::OffsetImm(OffsetImm { post_indexed: true, value: _ })
|
args::Argument::OffsetImm(args::OffsetImm { post_indexed: true, value: _ })
|
||||||
| Argument::OffsetReg(OffsetReg { add: _, post_indexed: true, reg: _ })
|
| args::Argument::OffsetReg(args::OffsetReg {
|
||||||
| Argument::CoOption(_) => {
|
add: _,
|
||||||
|
post_indexed: true,
|
||||||
|
reg: _,
|
||||||
|
})
|
||||||
|
| args::Argument::CoOption(_) => {
|
||||||
deref = false;
|
deref = false;
|
||||||
args.push(ObjInsArg::PlainText("]".into()));
|
arg_cb(InstructionPart::Basic("]"))?;
|
||||||
if writeback {
|
if writeback {
|
||||||
writeback = false;
|
writeback = false;
|
||||||
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque("!".into())));
|
arg_cb(InstructionPart::Arg(InstructionArg::Value(
|
||||||
|
InstructionArgValue::Opaque("!".into()),
|
||||||
|
)))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
@ -342,117 +409,179 @@ fn push_args(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
args.push(ObjInsArg::PlainText(config.separator().into()));
|
arg_cb(InstructionPart::Separator)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if reloc_arg == Some(i) {
|
if reloc_arg == Some(i) {
|
||||||
args.push(ObjInsArg::Reloc);
|
arg_cb(InstructionPart::Arg(InstructionArg::Reloc))?;
|
||||||
} else {
|
} else {
|
||||||
match arg {
|
match arg {
|
||||||
Argument::None => {}
|
args::Argument::None => {}
|
||||||
Argument::Reg(reg) => {
|
args::Argument::Reg(reg) => {
|
||||||
if reg.deref {
|
if reg.deref {
|
||||||
deref = true;
|
deref = true;
|
||||||
args.push(ObjInsArg::PlainText("[".into()));
|
arg_cb(InstructionPart::Basic("["))?;
|
||||||
}
|
}
|
||||||
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(
|
arg_cb(InstructionPart::Arg(InstructionArg::Value(
|
||||||
|
InstructionArgValue::Opaque(
|
||||||
reg.reg.display(display_options.reg_names).to_string().into(),
|
reg.reg.display(display_options.reg_names).to_string().into(),
|
||||||
)));
|
),
|
||||||
|
)))?;
|
||||||
if reg.writeback {
|
if reg.writeback {
|
||||||
if reg.deref {
|
if reg.deref {
|
||||||
writeback = true;
|
writeback = true;
|
||||||
} else {
|
} else {
|
||||||
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque("!".into())));
|
arg_cb(InstructionPart::Arg(InstructionArg::Value(
|
||||||
|
InstructionArgValue::Opaque("!".into()),
|
||||||
|
)))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Argument::RegList(reg_list) => {
|
args::Argument::RegList(reg_list) => {
|
||||||
args.push(ObjInsArg::PlainText("{".into()));
|
arg_cb(InstructionPart::Basic("{"))?;
|
||||||
let mut first = true;
|
let mut first = true;
|
||||||
for i in 0..16 {
|
for i in 0..16 {
|
||||||
if (reg_list.regs & (1 << i)) != 0 {
|
if (reg_list.regs & (1 << i)) != 0 {
|
||||||
if !first {
|
if !first {
|
||||||
args.push(ObjInsArg::PlainText(config.separator().into()));
|
arg_cb(InstructionPart::Separator)?;
|
||||||
}
|
}
|
||||||
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(
|
arg_cb(InstructionPart::Arg(InstructionArg::Value(
|
||||||
Register::parse(i)
|
InstructionArgValue::Opaque(
|
||||||
|
args::Register::parse(i)
|
||||||
.display(display_options.reg_names)
|
.display(display_options.reg_names)
|
||||||
.to_string()
|
.to_string()
|
||||||
.into(),
|
.into(),
|
||||||
)));
|
),
|
||||||
|
)))?;
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
args.push(ObjInsArg::PlainText("}".into()));
|
arg_cb(InstructionPart::Basic("}"))?;
|
||||||
if reg_list.user_mode {
|
if reg_list.user_mode {
|
||||||
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque("^".to_string().into())));
|
arg_cb(InstructionPart::Arg(InstructionArg::Value(
|
||||||
|
InstructionArgValue::Opaque("^".into()),
|
||||||
|
)))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Argument::UImm(value) | Argument::CoOpcode(value) | Argument::SatImm(value) => {
|
args::Argument::UImm(value)
|
||||||
args.push(ObjInsArg::PlainText("#".into()));
|
| args::Argument::CoOpcode(value)
|
||||||
args.push(ObjInsArg::Arg(ObjInsArgValue::Unsigned(*value as u64)));
|
| args::Argument::SatImm(value) => {
|
||||||
|
arg_cb(InstructionPart::Basic("#"))?;
|
||||||
|
arg_cb(InstructionPart::Arg(InstructionArg::Value(
|
||||||
|
InstructionArgValue::Unsigned(*value as u64),
|
||||||
|
)))?;
|
||||||
}
|
}
|
||||||
Argument::SImm(value)
|
args::Argument::SImm(value)
|
||||||
| Argument::OffsetImm(OffsetImm { post_indexed: _, value }) => {
|
| args::Argument::OffsetImm(args::OffsetImm { post_indexed: _, value }) => {
|
||||||
args.push(ObjInsArg::PlainText("#".into()));
|
arg_cb(InstructionPart::Basic("#"))?;
|
||||||
args.push(ObjInsArg::Arg(ObjInsArgValue::Signed(*value as i64)));
|
arg_cb(InstructionPart::Arg(InstructionArg::Value(
|
||||||
|
InstructionArgValue::Signed(*value as i64),
|
||||||
|
)))?;
|
||||||
}
|
}
|
||||||
Argument::BranchDest(value) => {
|
args::Argument::BranchDest(value) => {
|
||||||
let dest = cur_addr.wrapping_add_signed(*value) as u64;
|
let dest = cur_addr.wrapping_add_signed(*value) as u64;
|
||||||
args.push(ObjInsArg::BranchDest(dest));
|
arg_cb(InstructionPart::Arg(InstructionArg::BranchDest(dest)))?;
|
||||||
branch_dest = Some(dest);
|
|
||||||
}
|
}
|
||||||
Argument::CoOption(value) => {
|
args::Argument::CoOption(value) => {
|
||||||
args.push(ObjInsArg::PlainText("{".into()));
|
arg_cb(InstructionPart::Basic("{"))?;
|
||||||
args.push(ObjInsArg::Arg(ObjInsArgValue::Unsigned(*value as u64)));
|
arg_cb(InstructionPart::Arg(InstructionArg::Value(
|
||||||
args.push(ObjInsArg::PlainText("}".into()));
|
InstructionArgValue::Unsigned(*value as u64),
|
||||||
|
)))?;
|
||||||
|
arg_cb(InstructionPart::Basic("}"))?;
|
||||||
}
|
}
|
||||||
Argument::CoprocNum(value) => {
|
args::Argument::CoprocNum(value) => {
|
||||||
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(format!("p{}", value).into())));
|
arg_cb(InstructionPart::Arg(InstructionArg::Value(
|
||||||
|
InstructionArgValue::Opaque(format!("p{}", value).into()),
|
||||||
|
)))?;
|
||||||
}
|
}
|
||||||
Argument::ShiftImm(shift) => {
|
args::Argument::ShiftImm(shift) => {
|
||||||
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(shift.op.to_string().into())));
|
arg_cb(InstructionPart::Arg(InstructionArg::Value(
|
||||||
args.push(ObjInsArg::PlainText(" #".into()));
|
InstructionArgValue::Opaque(shift.op.to_string().into()),
|
||||||
args.push(ObjInsArg::Arg(ObjInsArgValue::Unsigned(shift.imm as u64)));
|
)))?;
|
||||||
|
arg_cb(InstructionPart::Basic(" #"))?;
|
||||||
|
arg_cb(InstructionPart::Arg(InstructionArg::Value(
|
||||||
|
InstructionArgValue::Unsigned(shift.imm as u64),
|
||||||
|
)))?;
|
||||||
}
|
}
|
||||||
Argument::ShiftReg(shift) => {
|
args::Argument::ShiftReg(shift) => {
|
||||||
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(shift.op.to_string().into())));
|
arg_cb(InstructionPart::Arg(InstructionArg::Value(
|
||||||
args.push(ObjInsArg::PlainText(" ".into()));
|
InstructionArgValue::Opaque(shift.op.to_string().into()),
|
||||||
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(
|
)))?;
|
||||||
|
arg_cb(InstructionPart::Basic(" "))?;
|
||||||
|
arg_cb(InstructionPart::Arg(InstructionArg::Value(
|
||||||
|
InstructionArgValue::Opaque(
|
||||||
shift.reg.display(display_options.reg_names).to_string().into(),
|
shift.reg.display(display_options.reg_names).to_string().into(),
|
||||||
)));
|
),
|
||||||
|
)))?;
|
||||||
}
|
}
|
||||||
Argument::OffsetReg(offset) => {
|
args::Argument::OffsetReg(offset) => {
|
||||||
if !offset.add {
|
if !offset.add {
|
||||||
args.push(ObjInsArg::PlainText("-".into()));
|
arg_cb(InstructionPart::Basic("-"))?;
|
||||||
}
|
}
|
||||||
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(
|
arg_cb(InstructionPart::Arg(InstructionArg::Value(
|
||||||
|
InstructionArgValue::Opaque(
|
||||||
offset.reg.display(display_options.reg_names).to_string().into(),
|
offset.reg.display(display_options.reg_names).to_string().into(),
|
||||||
)));
|
),
|
||||||
|
)))?;
|
||||||
}
|
}
|
||||||
Argument::CpsrMode(mode) => {
|
args::Argument::CpsrMode(mode) => {
|
||||||
args.push(ObjInsArg::PlainText("#".into()));
|
arg_cb(InstructionPart::Basic("#"))?;
|
||||||
args.push(ObjInsArg::Arg(ObjInsArgValue::Unsigned(mode.mode as u64)));
|
arg_cb(InstructionPart::Arg(InstructionArg::Value(
|
||||||
|
InstructionArgValue::Unsigned(mode.mode as u64),
|
||||||
|
)))?;
|
||||||
if mode.writeback {
|
if mode.writeback {
|
||||||
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque("!".into())));
|
arg_cb(InstructionPart::Arg(InstructionArg::Value(
|
||||||
|
InstructionArgValue::Opaque("!".into()),
|
||||||
|
)))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Argument::CoReg(_)
|
args::Argument::CoReg(_)
|
||||||
| Argument::StatusReg(_)
|
| args::Argument::StatusReg(_)
|
||||||
| Argument::StatusMask(_)
|
| args::Argument::StatusMask(_)
|
||||||
| Argument::Shift(_)
|
| args::Argument::Shift(_)
|
||||||
| Argument::CpsrFlags(_)
|
| args::Argument::CpsrFlags(_)
|
||||||
| Argument::Endian(_) => args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(
|
| args::Argument::Endian(_) => {
|
||||||
|
arg_cb(InstructionPart::Arg(InstructionArg::Value(
|
||||||
|
InstructionArgValue::Opaque(
|
||||||
arg.display(display_options, None).to_string().into(),
|
arg.display(display_options, None).to_string().into(),
|
||||||
))),
|
),
|
||||||
|
)))?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if deref {
|
if deref {
|
||||||
args.push(ObjInsArg::PlainText("]".into()));
|
arg_cb(InstructionPart::Basic("]"))?;
|
||||||
if writeback {
|
if writeback {
|
||||||
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque("!".into())));
|
arg_cb(InstructionPart::Arg(InstructionArg::Value(InstructionArgValue::Opaque(
|
||||||
|
"!".into(),
|
||||||
|
))))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok((args, branch_dest))
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_reloc_arg(
|
||||||
|
parsed_ins: &unarm::ParsedIns,
|
||||||
|
relocation: Option<ResolvedRelocation>,
|
||||||
|
) -> Option<usize> {
|
||||||
|
if let Some(resolved) = relocation {
|
||||||
|
match resolved.relocation.flags {
|
||||||
|
// Calls
|
||||||
|
RelocationFlags::Elf(elf::R_ARM_THM_XPC22)
|
||||||
|
| RelocationFlags::Elf(elf::R_ARM_THM_PC22)
|
||||||
|
| RelocationFlags::Elf(elf::R_ARM_PC24)
|
||||||
|
| RelocationFlags::Elf(elf::R_ARM_XPC25)
|
||||||
|
| RelocationFlags::Elf(elf::R_ARM_CALL) => {
|
||||||
|
parsed_ins.args.iter().rposition(|a| matches!(a, args::Argument::BranchDest(_)))
|
||||||
|
}
|
||||||
|
// Data
|
||||||
|
RelocationFlags::Elf(elf::R_ARM_ABS32) => {
|
||||||
|
parsed_ins.args.iter().rposition(|a| matches!(a, args::Argument::UImm(_)))
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,57 +1,56 @@
|
|||||||
use alloc::{
|
use alloc::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
collections::BTreeMap,
|
|
||||||
format,
|
format,
|
||||||
string::{String, ToString},
|
string::{String, ToString},
|
||||||
vec,
|
|
||||||
vec::Vec,
|
vec::Vec,
|
||||||
};
|
};
|
||||||
use core::cmp::Ordering;
|
use core::{cmp::Ordering, ops::Range};
|
||||||
|
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
use object::{elf, File, Relocation, RelocationFlags};
|
use object::elf;
|
||||||
use yaxpeax_arch::{Arch, Decoder, Reader, U8Reader};
|
use yaxpeax_arch::{Arch as YaxpeaxArch, Decoder, Reader, U8Reader};
|
||||||
use yaxpeax_arm::armv8::a64::{
|
use yaxpeax_arm::armv8::a64::{
|
||||||
ARMv8, DecodeError, InstDecoder, Instruction, Opcode, Operand, SIMDSizeCode, ShiftStyle,
|
ARMv8, DecodeError, InstDecoder, Instruction, Opcode, Operand, SIMDSizeCode, ShiftStyle,
|
||||||
SizeCode,
|
SizeCode,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
arch::{ObjArch, ProcessCodeResult},
|
arch::Arch,
|
||||||
diff::DiffObjConfig,
|
diff::{display::InstructionPart, DiffObjConfig},
|
||||||
obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection},
|
obj::{
|
||||||
|
InstructionArg, InstructionArgValue, InstructionRef, RelocationFlags, ResolvedRelocation,
|
||||||
|
ScannedInstruction,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct ObjArchArm64 {}
|
#[derive(Debug)]
|
||||||
|
pub struct ArchArm64 {}
|
||||||
|
|
||||||
impl ObjArchArm64 {
|
impl ArchArm64 {
|
||||||
pub fn new(_file: &File) -> Result<Self> { Ok(Self {}) }
|
pub fn new(_file: &object::File) -> Result<Self> { Ok(Self {}) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjArch for ObjArchArm64 {
|
impl Arch for ArchArm64 {
|
||||||
fn process_code(
|
fn scan_instructions(
|
||||||
&self,
|
&self,
|
||||||
address: u64,
|
address: u64,
|
||||||
code: &[u8],
|
code: &[u8],
|
||||||
section_index: usize,
|
_section_index: usize,
|
||||||
relocations: &[ObjReloc],
|
_diff_config: &DiffObjConfig,
|
||||||
line_info: &BTreeMap<u64, u32>,
|
) -> Result<Vec<ScannedInstruction>> {
|
||||||
config: &DiffObjConfig,
|
|
||||||
) -> Result<ProcessCodeResult> {
|
|
||||||
let start_address = address;
|
let start_address = address;
|
||||||
let end_address = address + code.len() as u64;
|
let mut ops = Vec::<ScannedInstruction>::with_capacity(code.len() / 4);
|
||||||
let ins_count = code.len() / 4;
|
|
||||||
|
|
||||||
let mut ops = Vec::with_capacity(ins_count);
|
|
||||||
let mut insts = Vec::with_capacity(ins_count);
|
|
||||||
|
|
||||||
let mut reader = U8Reader::new(code);
|
let mut reader = U8Reader::new(code);
|
||||||
let decoder = InstDecoder::default();
|
let decoder = InstDecoder::default();
|
||||||
let mut ins = Instruction::default();
|
let mut ins = Instruction::default();
|
||||||
loop {
|
loop {
|
||||||
// This is ridiculous...
|
// This is ridiculous...
|
||||||
let address =
|
let offset = <U8Reader<'_> as Reader<
|
||||||
start_address + <U8Reader<'_> as Reader<<ARMv8 as Arch>::Address, <ARMv8 as Arch>::Word>>::total_offset(&mut reader);
|
<ARMv8 as YaxpeaxArch>::Address,
|
||||||
|
<ARMv8 as YaxpeaxArch>::Word,
|
||||||
|
>>::total_offset(&mut reader);
|
||||||
|
let address = start_address + offset;
|
||||||
match decoder.decode_into(&mut ins, &mut reader) {
|
match decoder.decode_into(&mut ins, &mut reader) {
|
||||||
Ok(()) => {}
|
Ok(()) => {}
|
||||||
Err(e) => match e {
|
Err(e) => match e {
|
||||||
@ -59,94 +58,182 @@ impl ObjArch for ObjArchArm64 {
|
|||||||
DecodeError::InvalidOpcode
|
DecodeError::InvalidOpcode
|
||||||
| DecodeError::InvalidOperand
|
| DecodeError::InvalidOperand
|
||||||
| DecodeError::IncompleteDecoder => {
|
| DecodeError::IncompleteDecoder => {
|
||||||
ops.push(u16::MAX);
|
ops.push(ScannedInstruction {
|
||||||
insts.push(ObjIns {
|
ins_ref: InstructionRef { address, size: 4, opcode: u16::MAX },
|
||||||
address,
|
|
||||||
size: 4,
|
|
||||||
op: u16::MAX,
|
|
||||||
mnemonic: Cow::Borrowed("<invalid>"),
|
|
||||||
args: vec![],
|
|
||||||
reloc: None,
|
|
||||||
branch_dest: None,
|
branch_dest: None,
|
||||||
line: None,
|
|
||||||
formatted: "".to_string(),
|
|
||||||
orig: None,
|
|
||||||
});
|
});
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
let line = line_info.range(..=address).last().map(|(_, &b)| b);
|
let opcode = opcode_to_u16(ins.opcode);
|
||||||
let reloc = relocations.iter().find(|r| (r.address & !3) == address).cloned();
|
let ins_ref = InstructionRef { address, size: 4, opcode };
|
||||||
|
let branch_dest = branch_dest(ins_ref, &code[offset as usize..offset as usize + 4]);
|
||||||
|
ops.push(ScannedInstruction { ins_ref, branch_dest });
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(ops)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn display_instruction(
|
||||||
|
&self,
|
||||||
|
ins_ref: InstructionRef,
|
||||||
|
code: &[u8],
|
||||||
|
relocation: Option<ResolvedRelocation>,
|
||||||
|
function_range: Range<u64>,
|
||||||
|
_section_index: usize,
|
||||||
|
diff_config: &DiffObjConfig,
|
||||||
|
cb: &mut dyn FnMut(InstructionPart) -> Result<()>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let mut reader = U8Reader::new(code);
|
||||||
|
let decoder = InstDecoder::default();
|
||||||
|
let mut ins = Instruction::default();
|
||||||
|
if decoder.decode_into(&mut ins, &mut reader).is_err() {
|
||||||
|
cb(InstructionPart::Opcode(Cow::Borrowed("<invalid>"), u16::MAX))?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
let mut args = vec![];
|
|
||||||
let mut ctx = DisplayCtx {
|
let mut ctx = DisplayCtx {
|
||||||
address,
|
address: ins_ref.address,
|
||||||
section_index,
|
section_index: 0,
|
||||||
start_address,
|
start_address: function_range.start,
|
||||||
end_address,
|
end_address: function_range.end,
|
||||||
reloc: reloc.as_ref(),
|
reloc: relocation,
|
||||||
config,
|
config: diff_config,
|
||||||
branch_dest: None,
|
|
||||||
};
|
|
||||||
// Simplify instruction and process args
|
|
||||||
let mnemonic = display_instruction(&mut args, &ins, &mut ctx);
|
|
||||||
|
|
||||||
// Format the instruction without simplification
|
|
||||||
let mut orig = ins.opcode.to_string();
|
|
||||||
for (i, o) in ins.operands.iter().enumerate() {
|
|
||||||
if let Operand::Nothing = o {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if i == 0 {
|
|
||||||
orig.push(' ');
|
|
||||||
} else {
|
|
||||||
orig.push_str(", ");
|
|
||||||
}
|
|
||||||
orig.push_str(o.to_string().as_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(reloc) = &reloc {
|
|
||||||
if !args.iter().any(|a| matches!(a, ObjInsArg::Reloc)) {
|
|
||||||
args.push(ObjInsArg::PlainText(Cow::Borrowed(" <unhandled relocation>")));
|
|
||||||
log::warn!(
|
|
||||||
"Unhandled ARM64 relocation {:?}: {} @ {:#X}",
|
|
||||||
reloc.flags,
|
|
||||||
orig,
|
|
||||||
address
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let op = opcode_to_u16(ins.opcode);
|
let mut display_args = Vec::with_capacity(16);
|
||||||
ops.push(op);
|
let mnemonic = display_instruction(&mut |ret| display_args.push(ret), &ins, &mut ctx);
|
||||||
let branch_dest = ctx.branch_dest;
|
cb(InstructionPart::Opcode(Cow::Borrowed(mnemonic), ins_ref.opcode))?;
|
||||||
insts.push(ObjIns {
|
for arg in display_args {
|
||||||
address,
|
cb(arg)?;
|
||||||
size: 4,
|
}
|
||||||
op,
|
Ok(())
|
||||||
mnemonic: Cow::Borrowed(mnemonic),
|
|
||||||
args,
|
|
||||||
reloc,
|
|
||||||
branch_dest,
|
|
||||||
line,
|
|
||||||
formatted: ins.to_string(),
|
|
||||||
orig: Some(orig),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(ProcessCodeResult { ops, insts })
|
// fn process_code(
|
||||||
}
|
// &self,
|
||||||
|
// address: u64,
|
||||||
|
// code: &[u8],
|
||||||
|
// section_index: usize,
|
||||||
|
// relocations: &[ObjReloc],
|
||||||
|
// line_info: &BTreeMap<u64, u32>,
|
||||||
|
// config: &DiffObjConfig,
|
||||||
|
// ) -> Result<ProcessCodeResult> {
|
||||||
|
// let start_address = address;
|
||||||
|
// let end_address = address + code.len() as u64;
|
||||||
|
// let ins_count = code.len() / 4;
|
||||||
|
//
|
||||||
|
// let mut ops = Vec::with_capacity(ins_count);
|
||||||
|
// let mut insts = Vec::with_capacity(ins_count);
|
||||||
|
//
|
||||||
|
// let mut reader = U8Reader::new(code);
|
||||||
|
// let decoder = InstDecoder::default();
|
||||||
|
// let mut ins = Instruction::default();
|
||||||
|
// loop {
|
||||||
|
// // This is ridiculous...
|
||||||
|
// let address = start_address
|
||||||
|
// + <U8Reader<'_> as Reader<
|
||||||
|
// <ARMv8 as YaxpeaxArch>::Address,
|
||||||
|
// <ARMv8 as YaxpeaxArch>::Word,
|
||||||
|
// >>::total_offset(&mut reader);
|
||||||
|
// match decoder.decode_into(&mut ins, &mut reader) {
|
||||||
|
// Ok(()) => {}
|
||||||
|
// Err(e) => match e {
|
||||||
|
// DecodeError::ExhaustedInput => break,
|
||||||
|
// DecodeError::InvalidOpcode
|
||||||
|
// | DecodeError::InvalidOperand
|
||||||
|
// | DecodeError::IncompleteDecoder => {
|
||||||
|
// ops.push(u16::MAX);
|
||||||
|
// insts.push(ObjIns {
|
||||||
|
// address,
|
||||||
|
// size: 4,
|
||||||
|
// op: u16::MAX,
|
||||||
|
// mnemonic: Cow::Borrowed("<invalid>"),
|
||||||
|
// args: vec![],
|
||||||
|
// reloc: None,
|
||||||
|
// branch_dest: None,
|
||||||
|
// line: None,
|
||||||
|
// formatted: "".to_string(),
|
||||||
|
// orig: None,
|
||||||
|
// });
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// let line = line_info.range(..=address).last().map(|(_, &b)| b);
|
||||||
|
// let reloc = relocations.iter().find(|r| (r.address & !3) == address).cloned();
|
||||||
|
//
|
||||||
|
// let mut args = vec![];
|
||||||
|
// let mut ctx = DisplayCtx {
|
||||||
|
// address,
|
||||||
|
// section_index,
|
||||||
|
// start_address,
|
||||||
|
// end_address,
|
||||||
|
// reloc: reloc.as_ref(),
|
||||||
|
// config,
|
||||||
|
// branch_dest: None,
|
||||||
|
// };
|
||||||
|
// // Simplify instruction and process args
|
||||||
|
// let mnemonic = display_instruction(&mut args, &ins, &mut ctx);
|
||||||
|
//
|
||||||
|
// // Format the instruction without simplification
|
||||||
|
// let mut orig = ins.opcode.to_string();
|
||||||
|
// for (i, o) in ins.operands.iter().enumerate() {
|
||||||
|
// if let Operand::Nothing = o {
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// if i == 0 {
|
||||||
|
// orig.push(' ');
|
||||||
|
// } else {
|
||||||
|
// orig.push_str(", ");
|
||||||
|
// }
|
||||||
|
// orig.push_str(o.to_string().as_str());
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if let Some(reloc) = &reloc {
|
||||||
|
// if !args.iter().any(|a| matches!(a, InstructionArg::Reloc)) {
|
||||||
|
// push_arg(args, InstructionArg::PlainText(Cow::Borrowed(" <unhandled relocation>")));
|
||||||
|
// log::warn!(
|
||||||
|
// "Unhandled ARM64 relocation {:?}: {} @ {:#X}",
|
||||||
|
// reloc.flags,
|
||||||
|
// orig,
|
||||||
|
// address
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// let op = opcode_to_u16(ins.opcode);
|
||||||
|
// ops.push(op);
|
||||||
|
// let branch_dest = ctx.branch_dest;
|
||||||
|
// insts.push(ObjIns {
|
||||||
|
// address,
|
||||||
|
// size: 4,
|
||||||
|
// op,
|
||||||
|
// mnemonic: Cow::Borrowed(mnemonic),
|
||||||
|
// args,
|
||||||
|
// reloc,
|
||||||
|
// branch_dest,
|
||||||
|
// line,
|
||||||
|
// formatted: ins.to_string(),
|
||||||
|
// orig: Some(orig),
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Ok(ProcessCodeResult { ops, insts })
|
||||||
|
// }
|
||||||
|
|
||||||
fn implcit_addend(
|
fn implcit_addend(
|
||||||
&self,
|
&self,
|
||||||
_file: &File<'_>,
|
_file: &object::File<'_>,
|
||||||
_section: &ObjSection,
|
_section: &object::Section,
|
||||||
address: u64,
|
address: u64,
|
||||||
reloc: &Relocation,
|
_relocation: &object::Relocation,
|
||||||
|
flags: RelocationFlags,
|
||||||
) -> Result<i64> {
|
) -> Result<i64> {
|
||||||
bail!("Unsupported ARM64 implicit relocation {:#x}:{:?}", address, reloc.flags())
|
bail!("Unsupported ARM64 implicit relocation {:#x}:{:?}", address, flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn demangle(&self, name: &str) -> Option<String> {
|
fn demangle(&self, name: &str) -> Option<String> {
|
||||||
@ -157,25 +244,21 @@ impl ObjArch for ObjArchArm64 {
|
|||||||
|
|
||||||
fn display_reloc(&self, flags: RelocationFlags) -> Cow<'static, str> {
|
fn display_reloc(&self, flags: RelocationFlags) -> Cow<'static, str> {
|
||||||
match flags {
|
match flags {
|
||||||
RelocationFlags::Elf { r_type: elf::R_AARCH64_ADR_PREL_PG_HI21 } => {
|
RelocationFlags::Elf(elf::R_AARCH64_ADR_PREL_PG_HI21) => {
|
||||||
Cow::Borrowed("R_AARCH64_ADR_PREL_PG_HI21")
|
Cow::Borrowed("R_AARCH64_ADR_PREL_PG_HI21")
|
||||||
}
|
}
|
||||||
RelocationFlags::Elf { r_type: elf::R_AARCH64_ADD_ABS_LO12_NC } => {
|
RelocationFlags::Elf(elf::R_AARCH64_ADD_ABS_LO12_NC) => {
|
||||||
Cow::Borrowed("R_AARCH64_ADD_ABS_LO12_NC")
|
Cow::Borrowed("R_AARCH64_ADD_ABS_LO12_NC")
|
||||||
}
|
}
|
||||||
RelocationFlags::Elf { r_type: elf::R_AARCH64_JUMP26 } => {
|
RelocationFlags::Elf(elf::R_AARCH64_JUMP26) => Cow::Borrowed("R_AARCH64_JUMP26"),
|
||||||
Cow::Borrowed("R_AARCH64_JUMP26")
|
RelocationFlags::Elf(elf::R_AARCH64_CALL26) => Cow::Borrowed("R_AARCH64_CALL26"),
|
||||||
}
|
RelocationFlags::Elf(elf::R_AARCH64_LDST32_ABS_LO12_NC) => {
|
||||||
RelocationFlags::Elf { r_type: elf::R_AARCH64_CALL26 } => {
|
|
||||||
Cow::Borrowed("R_AARCH64_CALL26")
|
|
||||||
}
|
|
||||||
RelocationFlags::Elf { r_type: elf::R_AARCH64_LDST32_ABS_LO12_NC } => {
|
|
||||||
Cow::Borrowed("R_AARCH64_LDST32_ABS_LO12_NC")
|
Cow::Borrowed("R_AARCH64_LDST32_ABS_LO12_NC")
|
||||||
}
|
}
|
||||||
RelocationFlags::Elf { r_type: elf::R_AARCH64_ADR_GOT_PAGE } => {
|
RelocationFlags::Elf(elf::R_AARCH64_ADR_GOT_PAGE) => {
|
||||||
Cow::Borrowed("R_AARCH64_ADR_GOT_PAGE")
|
Cow::Borrowed("R_AARCH64_ADR_GOT_PAGE")
|
||||||
}
|
}
|
||||||
RelocationFlags::Elf { r_type: elf::R_AARCH64_LD64_GOT_LO12_NC } => {
|
RelocationFlags::Elf(elf::R_AARCH64_LD64_GOT_LO12_NC) => {
|
||||||
Cow::Borrowed("R_AARCH64_LD64_GOT_LO12_NC")
|
Cow::Borrowed("R_AARCH64_LD64_GOT_LO12_NC")
|
||||||
}
|
}
|
||||||
_ => Cow::Owned(format!("<{flags:?}>")),
|
_ => Cow::Owned(format!("<{flags:?}>")),
|
||||||
@ -184,7 +267,7 @@ impl ObjArch for ObjArchArm64 {
|
|||||||
|
|
||||||
fn get_reloc_byte_size(&self, flags: RelocationFlags) -> usize {
|
fn get_reloc_byte_size(&self, flags: RelocationFlags) -> usize {
|
||||||
match flags {
|
match flags {
|
||||||
RelocationFlags::Elf { r_type } => match r_type {
|
RelocationFlags::Elf(r_type) => match r_type {
|
||||||
elf::R_AARCH64_ABS64 => 8,
|
elf::R_AARCH64_ABS64 => 8,
|
||||||
elf::R_AARCH64_ABS32 => 4,
|
elf::R_AARCH64_ABS32 => 4,
|
||||||
elf::R_AARCH64_ABS16 => 2,
|
elf::R_AARCH64_ABS16 => 2,
|
||||||
@ -198,25 +281,51 @@ impl ObjArch for ObjArchArm64 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn branch_dest(ins_ref: InstructionRef, code: &[u8]) -> Option<u64> {
|
||||||
|
const OPCODE_B: u16 = opcode_to_u16(Opcode::B);
|
||||||
|
const OPCODE_BL: u16 = opcode_to_u16(Opcode::BL);
|
||||||
|
const OPCODE_BCC: u16 = opcode_to_u16(Opcode::Bcc(0));
|
||||||
|
const OPCODE_CBZ: u16 = opcode_to_u16(Opcode::CBZ);
|
||||||
|
const OPCODE_CBNZ: u16 = opcode_to_u16(Opcode::CBNZ);
|
||||||
|
const OPCODE_TBZ: u16 = opcode_to_u16(Opcode::TBZ);
|
||||||
|
const OPCODE_TBNZ: u16 = opcode_to_u16(Opcode::TBNZ);
|
||||||
|
|
||||||
|
let word = u32::from_le_bytes(code.try_into().ok()?);
|
||||||
|
match ins_ref.opcode {
|
||||||
|
OPCODE_B | OPCODE_BL => {
|
||||||
|
let offset = ((word & 0x03ff_ffff) << 2) as i32;
|
||||||
|
let extended_offset = (offset << 4) >> 4;
|
||||||
|
ins_ref.address.checked_add_signed(extended_offset as i64)
|
||||||
|
}
|
||||||
|
OPCODE_BCC | OPCODE_CBZ | OPCODE_CBNZ => {
|
||||||
|
let offset = (word as i32 & 0x00ff_ffe0) >> 3;
|
||||||
|
let extended_offset = (offset << 11) >> 11;
|
||||||
|
ins_ref.address.checked_add_signed(extended_offset as i64)
|
||||||
|
}
|
||||||
|
OPCODE_TBZ | OPCODE_TBNZ => {
|
||||||
|
let offset = (word as i32 & 0x0007_ffe0) >> 3;
|
||||||
|
let extended_offset = (offset << 16) >> 16;
|
||||||
|
ins_ref.address.checked_add_signed(extended_offset as i64)
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct DisplayCtx<'a> {
|
struct DisplayCtx<'a> {
|
||||||
address: u64,
|
address: u64,
|
||||||
section_index: usize,
|
section_index: usize,
|
||||||
start_address: u64,
|
start_address: u64,
|
||||||
end_address: u64,
|
end_address: u64,
|
||||||
reloc: Option<&'a ObjReloc>,
|
reloc: Option<ResolvedRelocation<'a>>,
|
||||||
config: &'a DiffObjConfig,
|
config: &'a DiffObjConfig,
|
||||||
branch_dest: Option<u64>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Source: https://github.com/iximeow/yaxpeax-arm/blob/716a6e3fc621f5fe3300f3309e56943b8e1e65ad/src/armv8/a64.rs#L317
|
// Source: https://github.com/iximeow/yaxpeax-arm/blob/716a6e3fc621f5fe3300f3309e56943b8e1e65ad/src/armv8/a64.rs#L317
|
||||||
// License: 0BSD
|
// License: 0BSD
|
||||||
// Reworked for more structured output. The library only gives us a Display impl, and no way to
|
// Reworked for more structured output. The library only gives us a Display impl, and no way to
|
||||||
// capture any of this information, so it needs to be reimplemented here.
|
// capture any of this information, so it needs to be reimplemented here.
|
||||||
fn display_instruction(
|
fn display_instruction<Cb>(args: &mut Cb, ins: &Instruction, ctx: &mut DisplayCtx) -> &'static str
|
||||||
args: &mut Vec<ObjInsArg>,
|
where Cb: FnMut(InstructionPart) {
|
||||||
ins: &Instruction,
|
|
||||||
ctx: &mut DisplayCtx,
|
|
||||||
) -> &'static str {
|
|
||||||
let mnemonic = match ins.opcode {
|
let mnemonic = match ins.opcode {
|
||||||
Opcode::Invalid => return "<invalid>",
|
Opcode::Invalid => return "<invalid>",
|
||||||
Opcode::UDF => "udf",
|
Opcode::UDF => "udf",
|
||||||
@ -2025,12 +2134,14 @@ fn condition_code(cond: u8) -> &'static str {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn push_register(args: &mut Vec<ObjInsArg>, size: SizeCode, reg: u16, sp: bool) {
|
fn push_register<Cb>(args: &mut Cb, size: SizeCode, reg: u16, sp: bool)
|
||||||
|
where Cb: FnMut(InstructionPart) {
|
||||||
push_opaque(args, reg_name(size, reg, sp));
|
push_opaque(args, reg_name(size, reg, sp));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn push_shift(args: &mut Vec<ObjInsArg>, style: ShiftStyle, amount: u8) {
|
fn push_shift<Cb>(args: &mut Cb, style: ShiftStyle, amount: u8)
|
||||||
|
where Cb: FnMut(InstructionPart) {
|
||||||
push_opaque(args, shift_style(style));
|
push_opaque(args, shift_style(style));
|
||||||
if amount != 0 {
|
if amount != 0 {
|
||||||
push_plain(args, " ");
|
push_plain(args, " ");
|
||||||
@ -2039,11 +2150,13 @@ fn push_shift(args: &mut Vec<ObjInsArg>, style: ShiftStyle, amount: u8) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn push_condition_code(args: &mut Vec<ObjInsArg>, cond: u8) {
|
fn push_condition_code<Cb>(args: &mut Cb, cond: u8)
|
||||||
|
where Cb: FnMut(InstructionPart) {
|
||||||
push_opaque(args, condition_code(cond));
|
push_opaque(args, condition_code(cond));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_barrier(args: &mut Vec<ObjInsArg>, option: u8) {
|
fn push_barrier<Cb>(args: &mut Cb, option: u8)
|
||||||
|
where Cb: FnMut(InstructionPart) {
|
||||||
match option {
|
match option {
|
||||||
0b0001 => push_opaque(args, "oshld"),
|
0b0001 => push_opaque(args, "oshld"),
|
||||||
0b0010 => push_opaque(args, "oshst"),
|
0b0010 => push_opaque(args, "oshst"),
|
||||||
@ -2062,89 +2175,104 @@ fn push_barrier(args: &mut Vec<ObjInsArg>, option: u8) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn push_opaque(args: &mut Vec<ObjInsArg>, text: &'static str) {
|
fn push_opaque<Cb>(args: &mut Cb, text: &'static str)
|
||||||
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(Cow::Borrowed(text))));
|
where Cb: FnMut(InstructionPart) {
|
||||||
|
push_arg(args, InstructionArg::Value(InstructionArgValue::Opaque(Cow::Borrowed(text))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn push_plain(args: &mut Vec<ObjInsArg>, text: &'static str) {
|
fn push_plain<Cb>(args: &mut Cb, text: &'static str)
|
||||||
args.push(ObjInsArg::PlainText(Cow::Borrowed(text)));
|
where Cb: FnMut(InstructionPart) {
|
||||||
|
args(InstructionPart::Basic(text));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn push_separator(args: &mut Vec<ObjInsArg>, config: &DiffObjConfig) {
|
fn push_separator<Cb>(args: &mut Cb, _config: &DiffObjConfig)
|
||||||
args.push(ObjInsArg::PlainText(Cow::Borrowed(config.separator())));
|
where Cb: FnMut(InstructionPart) {
|
||||||
|
args(InstructionPart::Separator);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn push_unsigned(args: &mut Vec<ObjInsArg>, v: u64) {
|
fn push_unsigned<Cb>(args: &mut Cb, v: u64)
|
||||||
|
where Cb: FnMut(InstructionPart) {
|
||||||
push_plain(args, "#");
|
push_plain(args, "#");
|
||||||
args.push(ObjInsArg::Arg(ObjInsArgValue::Unsigned(v)));
|
push_arg(args, InstructionArg::Value(InstructionArgValue::Unsigned(v)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn push_signed(args: &mut Vec<ObjInsArg>, v: i64) {
|
fn push_signed<Cb>(args: &mut Cb, v: i64)
|
||||||
|
where Cb: FnMut(InstructionPart) {
|
||||||
push_plain(args, "#");
|
push_plain(args, "#");
|
||||||
args.push(ObjInsArg::Arg(ObjInsArgValue::Signed(v)));
|
push_arg(args, InstructionArg::Value(InstructionArgValue::Signed(v)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn push_arg<Cb>(args: &mut Cb, arg: InstructionArg)
|
||||||
|
where Cb: FnMut(InstructionPart) {
|
||||||
|
args(InstructionPart::Arg(arg));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Relocations that appear in Operand::PCOffset.
|
/// Relocations that appear in Operand::PCOffset.
|
||||||
fn is_pc_offset_reloc(reloc: Option<&ObjReloc>) -> Option<&ObjReloc> {
|
fn is_pc_offset_reloc(reloc: Option<ResolvedRelocation>) -> Option<ResolvedRelocation> {
|
||||||
if let Some(reloc) = reloc {
|
if let Some(resolved) = reloc {
|
||||||
if let RelocationFlags::Elf {
|
if let RelocationFlags::Elf(
|
||||||
r_type:
|
|
||||||
elf::R_AARCH64_ADR_PREL_PG_HI21
|
elf::R_AARCH64_ADR_PREL_PG_HI21
|
||||||
| elf::R_AARCH64_JUMP26
|
| elf::R_AARCH64_JUMP26
|
||||||
| elf::R_AARCH64_CALL26
|
| elf::R_AARCH64_CALL26
|
||||||
| elf::R_AARCH64_ADR_GOT_PAGE,
|
| elf::R_AARCH64_ADR_GOT_PAGE,
|
||||||
} = reloc.flags
|
) = resolved.relocation.flags
|
||||||
{
|
{
|
||||||
return Some(reloc);
|
return Some(resolved);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Relocations that appear in Operand::Immediate.
|
/// Relocations that appear in Operand::Immediate.
|
||||||
fn is_imm_reloc(reloc: Option<&ObjReloc>) -> bool {
|
fn is_imm_reloc(resolved: Option<ResolvedRelocation>) -> bool {
|
||||||
matches!(reloc, Some(reloc) if matches!(reloc.flags, RelocationFlags::Elf {
|
resolved.is_some_and(|r| {
|
||||||
r_type: elf::R_AARCH64_ADD_ABS_LO12_NC,
|
matches!(r.relocation.flags, RelocationFlags::Elf(elf::R_AARCH64_ADD_ABS_LO12_NC))
|
||||||
}))
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Relocations that appear in Operand::RegPreIndex/RegPostIndex.
|
/// Relocations that appear in Operand::RegPreIndex/RegPostIndex.
|
||||||
fn is_reg_index_reloc(reloc: Option<&ObjReloc>) -> bool {
|
fn is_reg_index_reloc(resolved: Option<ResolvedRelocation>) -> bool {
|
||||||
matches!(reloc, Some(reloc) if matches!(reloc.flags, RelocationFlags::Elf {
|
resolved.is_some_and(|r| {
|
||||||
r_type: elf::R_AARCH64_LDST32_ABS_LO12_NC | elf::R_AARCH64_LD64_GOT_LO12_NC,
|
matches!(
|
||||||
}))
|
r.relocation.flags,
|
||||||
|
RelocationFlags::Elf(
|
||||||
|
elf::R_AARCH64_LDST32_ABS_LO12_NC | elf::R_AARCH64_LD64_GOT_LO12_NC
|
||||||
|
)
|
||||||
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_operand(args: &mut Vec<ObjInsArg>, o: &Operand, ctx: &mut DisplayCtx) {
|
fn push_operand<Cb>(args: &mut Cb, o: &Operand, ctx: &mut DisplayCtx)
|
||||||
|
where Cb: FnMut(InstructionPart) {
|
||||||
match o {
|
match o {
|
||||||
Operand::Nothing => unreachable!(),
|
Operand::Nothing => unreachable!(),
|
||||||
Operand::PCOffset(off) => {
|
Operand::PCOffset(off) => {
|
||||||
if let Some(reloc) = is_pc_offset_reloc(ctx.reloc) {
|
if let Some(resolved) = is_pc_offset_reloc(ctx.reloc) {
|
||||||
let target_address = reloc.target.address.checked_add_signed(reloc.addend);
|
let target_address =
|
||||||
if reloc.target.orig_section_index == Some(ctx.section_index)
|
resolved.symbol.address.checked_add_signed(resolved.relocation.addend);
|
||||||
|
if resolved.symbol.section == Some(ctx.section_index)
|
||||||
&& matches!(target_address, Some(addr) if addr > ctx.start_address && addr < ctx.end_address)
|
&& matches!(target_address, Some(addr) if addr > ctx.start_address && addr < ctx.end_address)
|
||||||
{
|
{
|
||||||
let dest = target_address.unwrap();
|
let dest = target_address.unwrap();
|
||||||
push_plain(args, "$");
|
push_plain(args, "$");
|
||||||
args.push(ObjInsArg::BranchDest(dest));
|
push_arg(args, InstructionArg::BranchDest(dest));
|
||||||
ctx.branch_dest = Some(dest);
|
|
||||||
} else {
|
} else {
|
||||||
args.push(ObjInsArg::Reloc);
|
push_arg(args, InstructionArg::Reloc);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let dest = ctx.address.saturating_add_signed(*off);
|
let dest = ctx.address.saturating_add_signed(*off);
|
||||||
push_plain(args, "$");
|
push_plain(args, "$");
|
||||||
args.push(ObjInsArg::BranchDest(dest));
|
push_arg(args, InstructionArg::BranchDest(dest));
|
||||||
ctx.branch_dest = Some(dest);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operand::Immediate(imm) => {
|
Operand::Immediate(imm) => {
|
||||||
if is_imm_reloc(ctx.reloc) {
|
if is_imm_reloc(ctx.reloc) {
|
||||||
args.push(ObjInsArg::Reloc);
|
push_arg(args, InstructionArg::Reloc);
|
||||||
} else {
|
} else {
|
||||||
push_unsigned(args, *imm as u64);
|
push_unsigned(args, *imm as u64);
|
||||||
}
|
}
|
||||||
@ -2249,7 +2377,7 @@ fn push_operand(args: &mut Vec<ObjInsArg>, o: &Operand, ctx: &mut DisplayCtx) {
|
|||||||
push_register(args, SizeCode::X, *reg, true);
|
push_register(args, SizeCode::X, *reg, true);
|
||||||
if is_reg_index_reloc(ctx.reloc) {
|
if is_reg_index_reloc(ctx.reloc) {
|
||||||
push_separator(args, ctx.config);
|
push_separator(args, ctx.config);
|
||||||
args.push(ObjInsArg::Reloc);
|
push_arg(args, InstructionArg::Reloc);
|
||||||
} else if *offset != 0 || *wback_bit {
|
} else if *offset != 0 || *wback_bit {
|
||||||
push_separator(args, ctx.config);
|
push_separator(args, ctx.config);
|
||||||
push_signed(args, *offset as i64);
|
push_signed(args, *offset as i64);
|
||||||
@ -2265,7 +2393,7 @@ fn push_operand(args: &mut Vec<ObjInsArg>, o: &Operand, ctx: &mut DisplayCtx) {
|
|||||||
push_plain(args, "]");
|
push_plain(args, "]");
|
||||||
push_separator(args, ctx.config);
|
push_separator(args, ctx.config);
|
||||||
if is_reg_index_reloc(ctx.reloc) {
|
if is_reg_index_reloc(ctx.reloc) {
|
||||||
args.push(ObjInsArg::Reloc);
|
push_arg(args, InstructionArg::Reloc);
|
||||||
} else {
|
} else {
|
||||||
push_signed(args, *offset as i64);
|
push_signed(args, *offset as i64);
|
||||||
}
|
}
|
||||||
@ -2276,10 +2404,13 @@ fn push_operand(args: &mut Vec<ObjInsArg>, o: &Operand, ctx: &mut DisplayCtx) {
|
|||||||
push_plain(args, "]");
|
push_plain(args, "]");
|
||||||
push_separator(args, ctx.config);
|
push_separator(args, ctx.config);
|
||||||
// TODO does 31 have to be handled separate?
|
// TODO does 31 have to be handled separate?
|
||||||
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(Cow::Owned(format!(
|
push_arg(
|
||||||
|
args,
|
||||||
|
InstructionArg::Value(InstructionArgValue::Opaque(Cow::Owned(format!(
|
||||||
"x{}",
|
"x{}",
|
||||||
offset_reg
|
offset_reg
|
||||||
)))));
|
)))),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
// Fall back to original logic
|
// Fall back to original logic
|
||||||
Operand::SIMDRegister(_, _)
|
Operand::SIMDRegister(_, _)
|
||||||
@ -2293,13 +2424,16 @@ fn push_operand(args: &mut Vec<ObjInsArg>, o: &Operand, ctx: &mut DisplayCtx) {
|
|||||||
| Operand::SystemReg(_)
|
| Operand::SystemReg(_)
|
||||||
| Operand::ControlReg(_)
|
| Operand::ControlReg(_)
|
||||||
| Operand::PstateField(_) => {
|
| Operand::PstateField(_) => {
|
||||||
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(Cow::Owned(o.to_string()))));
|
push_arg(
|
||||||
|
args,
|
||||||
|
InstructionArg::Value(InstructionArgValue::Opaque(Cow::Owned(o.to_string()))),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Opcode is #[repr(u16)], but the tuple variants negate that, so we have to do this instead.
|
// Opcode is #[repr(u16)], but the tuple variants negate that, so we have to do this instead.
|
||||||
fn opcode_to_u16(opcode: Opcode) -> u16 {
|
const fn opcode_to_u16(opcode: Opcode) -> u16 {
|
||||||
match opcode {
|
match opcode {
|
||||||
Opcode::Invalid => u16::MAX,
|
Opcode::Invalid => u16::MAX,
|
||||||
Opcode::UDF => 0,
|
Opcode::UDF => 0,
|
||||||
|
@ -1,25 +1,27 @@
|
|||||||
use alloc::{borrow::Cow, collections::BTreeMap, format, string::ToString, vec::Vec};
|
use alloc::{borrow::Cow, format, string::ToString, vec::Vec};
|
||||||
|
use core::ops::Range;
|
||||||
|
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
use object::{
|
use object::{elf, Endian as _, Object as _, ObjectSection as _, ObjectSymbol as _};
|
||||||
elf, Endian, Endianness, File, FileFlags, Object, ObjectSection, ObjectSymbol, Relocation,
|
|
||||||
RelocationFlags, RelocationTarget,
|
|
||||||
};
|
|
||||||
use rabbitizer::{
|
use rabbitizer::{
|
||||||
abi::Abi,
|
abi::Abi,
|
||||||
operands::{ValuedOperand, IU16},
|
operands::{ValuedOperand, IU16},
|
||||||
registers_meta::Register,
|
registers_meta::Register,
|
||||||
Instruction, InstructionDisplayFlags, InstructionFlags, IsaExtension, IsaVersion, Vram,
|
IsaExtension, IsaVersion, Vram,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
arch::{ObjArch, ProcessCodeResult},
|
arch::Arch,
|
||||||
diff::{DiffObjConfig, MipsAbi, MipsInstrCategory},
|
diff::{display::InstructionPart, DiffObjConfig, MipsAbi, MipsInstrCategory},
|
||||||
obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection},
|
obj::{
|
||||||
|
InstructionArg, InstructionArgValue, InstructionRef, Relocation, RelocationFlags,
|
||||||
|
ResolvedRelocation, ScannedInstruction,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct ObjArchMips {
|
#[derive(Debug)]
|
||||||
pub endianness: Endianness,
|
pub struct ArchMips {
|
||||||
|
pub endianness: object::Endianness,
|
||||||
pub abi: Abi,
|
pub abi: Abi,
|
||||||
pub isa_extension: Option<IsaExtension>,
|
pub isa_extension: Option<IsaExtension>,
|
||||||
pub ri_gp_value: i32,
|
pub ri_gp_value: i32,
|
||||||
@ -33,13 +35,13 @@ const EF_MIPS_MACH_5900: u32 = 0x00920000;
|
|||||||
|
|
||||||
const R_MIPS15_S3: u32 = 119;
|
const R_MIPS15_S3: u32 = 119;
|
||||||
|
|
||||||
impl ObjArchMips {
|
impl ArchMips {
|
||||||
pub fn new(object: &File) -> Result<Self> {
|
pub fn new(object: &object::File) -> Result<Self> {
|
||||||
let mut abi = Abi::O32;
|
let mut abi = Abi::O32;
|
||||||
let mut isa_extension = None;
|
let mut isa_extension = None;
|
||||||
match object.flags() {
|
match object.flags() {
|
||||||
FileFlags::None => {}
|
object::FileFlags::None => {}
|
||||||
FileFlags::Elf { e_flags, .. } => {
|
object::FileFlags::Elf { e_flags, .. } => {
|
||||||
abi = match e_flags & EF_MIPS_ABI {
|
abi = match e_flags & EF_MIPS_ABI {
|
||||||
elf::EF_MIPS_ABI_O32 | elf::EF_MIPS_ABI_O64 => Abi::O32,
|
elf::EF_MIPS_ABI_O32 | elf::EF_MIPS_ABI_O64 => Abi::O32,
|
||||||
elf::EF_MIPS_ABI_EABI32 | elf::EF_MIPS_ABI_EABI64 => Abi::N32,
|
elf::EF_MIPS_ABI_EABI32 | elf::EF_MIPS_ABI_EABI64 => Abi::N32,
|
||||||
@ -73,19 +75,9 @@ impl ObjArchMips {
|
|||||||
|
|
||||||
Ok(Self { endianness: object.endianness(), abi, isa_extension, ri_gp_value })
|
Ok(Self { endianness: object.endianness(), abi, isa_extension, ri_gp_value })
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl ObjArch for ObjArchMips {
|
fn instruction_flags(&self, diff_config: &DiffObjConfig) -> rabbitizer::InstructionFlags {
|
||||||
fn process_code(
|
let isa_extension = match diff_config.mips_instr_category {
|
||||||
&self,
|
|
||||||
address: u64,
|
|
||||||
code: &[u8],
|
|
||||||
section_index: usize,
|
|
||||||
relocations: &[ObjReloc],
|
|
||||||
line_info: &BTreeMap<u64, u32>,
|
|
||||||
config: &DiffObjConfig,
|
|
||||||
) -> Result<ProcessCodeResult> {
|
|
||||||
let isa_extension = match config.mips_instr_category {
|
|
||||||
MipsInstrCategory::Auto => self.isa_extension,
|
MipsInstrCategory::Auto => self.isa_extension,
|
||||||
MipsInstrCategory::Cpu => None,
|
MipsInstrCategory::Cpu => None,
|
||||||
MipsInstrCategory::Rsp => Some(IsaExtension::RSP),
|
MipsInstrCategory::Rsp => Some(IsaExtension::RSP),
|
||||||
@ -93,158 +85,105 @@ impl ObjArch for ObjArchMips {
|
|||||||
MipsInstrCategory::R4000allegrex => Some(IsaExtension::R4000ALLEGREX),
|
MipsInstrCategory::R4000allegrex => Some(IsaExtension::R4000ALLEGREX),
|
||||||
MipsInstrCategory::R5900 => Some(IsaExtension::R5900),
|
MipsInstrCategory::R5900 => Some(IsaExtension::R5900),
|
||||||
};
|
};
|
||||||
let instruction_flags = match isa_extension {
|
match isa_extension {
|
||||||
Some(extension) => InstructionFlags::new_extension(extension),
|
Some(extension) => rabbitizer::InstructionFlags::new_extension(extension),
|
||||||
None => InstructionFlags::new_isa(IsaVersion::MIPS_III, None),
|
None => rabbitizer::InstructionFlags::new_isa(IsaVersion::MIPS_III, None),
|
||||||
}
|
}
|
||||||
.with_abi(match config.mips_abi {
|
.with_abi(match diff_config.mips_abi {
|
||||||
MipsAbi::Auto => self.abi,
|
MipsAbi::Auto => self.abi,
|
||||||
MipsAbi::O32 => Abi::O32,
|
MipsAbi::O32 => Abi::O32,
|
||||||
MipsAbi::N32 => Abi::N32,
|
MipsAbi::N32 => Abi::N32,
|
||||||
MipsAbi::N64 => Abi::N64,
|
MipsAbi::N64 => Abi::N64,
|
||||||
});
|
})
|
||||||
let display_flags = InstructionDisplayFlags::default().with_unknown_instr_comment(false);
|
}
|
||||||
|
|
||||||
|
fn instruction_display_flags(
|
||||||
|
&self,
|
||||||
|
_diff_config: &DiffObjConfig,
|
||||||
|
) -> rabbitizer::InstructionDisplayFlags {
|
||||||
|
rabbitizer::InstructionDisplayFlags::default().with_unknown_instr_comment(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_ins_ref(
|
||||||
|
&self,
|
||||||
|
ins_ref: InstructionRef,
|
||||||
|
code: &[u8],
|
||||||
|
diff_config: &DiffObjConfig,
|
||||||
|
) -> Result<rabbitizer::Instruction> {
|
||||||
|
Ok(rabbitizer::Instruction::new(
|
||||||
|
self.endianness.read_u32_bytes(code.try_into()?),
|
||||||
|
Vram::new(ins_ref.address as u32),
|
||||||
|
self.instruction_flags(diff_config),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Arch for ArchMips {
|
||||||
|
fn scan_instructions(
|
||||||
|
&self,
|
||||||
|
address: u64,
|
||||||
|
code: &[u8],
|
||||||
|
_section_index: usize,
|
||||||
|
diff_config: &DiffObjConfig,
|
||||||
|
) -> Result<Vec<ScannedInstruction>> {
|
||||||
|
let instruction_flags = self.instruction_flags(diff_config);
|
||||||
let start_address = address;
|
let start_address = address;
|
||||||
let end_address = address + code.len() as u64;
|
let mut ops = Vec::<ScannedInstruction>::with_capacity(code.len() / 4);
|
||||||
let ins_count = code.len() / 4;
|
|
||||||
let mut ops = Vec::<u16>::with_capacity(ins_count);
|
|
||||||
let mut insts = Vec::<ObjIns>::with_capacity(ins_count);
|
|
||||||
let mut cur_addr = start_address as u32;
|
let mut cur_addr = start_address as u32;
|
||||||
for chunk in code.chunks_exact(4) {
|
for chunk in code.chunks_exact(4) {
|
||||||
let reloc = relocations.iter().find(|r| (r.address as u32 & !3) == cur_addr);
|
|
||||||
let code = self.endianness.read_u32_bytes(chunk.try_into()?);
|
let code = self.endianness.read_u32_bytes(chunk.try_into()?);
|
||||||
let instruction = Instruction::new(code, Vram::new(cur_addr), instruction_flags);
|
let vram = Vram::new(cur_addr);
|
||||||
|
let instruction = rabbitizer::Instruction::new(code, vram, instruction_flags);
|
||||||
let formatted = instruction.display(&display_flags, None::<&str>, 0).to_string();
|
let opcode = instruction.opcode() as u16;
|
||||||
let op = instruction.opcode() as u16;
|
let branch_dest =
|
||||||
ops.push(op);
|
instruction.get_branch_offset_generic().map(|o| (vram + o).inner() as u64);
|
||||||
|
ops.push(ScannedInstruction {
|
||||||
let mnemonic = instruction.opcode().name();
|
ins_ref: InstructionRef { address, size: 4, opcode },
|
||||||
let mut branch_dest = instruction.get_branch_offset_generic().map(|a| a.inner() as u64);
|
|
||||||
|
|
||||||
let operands = instruction.valued_operands_iter();
|
|
||||||
|
|
||||||
let mut args = Vec::with_capacity(6);
|
|
||||||
for (idx, op) in operands.enumerate() {
|
|
||||||
if idx > 0 {
|
|
||||||
args.push(ObjInsArg::PlainText(config.separator().into()));
|
|
||||||
}
|
|
||||||
|
|
||||||
match op {
|
|
||||||
ValuedOperand::core_immediate(imm) => {
|
|
||||||
if let Some(reloc) = reloc {
|
|
||||||
push_reloc(&mut args, reloc)?;
|
|
||||||
} else {
|
|
||||||
args.push(ObjInsArg::Arg(match imm {
|
|
||||||
IU16::Integer(s) => ObjInsArgValue::Signed(s as i64),
|
|
||||||
IU16::Unsigned(u) => ObjInsArgValue::Unsigned(u as u64),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ValuedOperand::core_label(..) | ValuedOperand::core_branch_target_label(..) => {
|
|
||||||
if let Some(reloc) = reloc {
|
|
||||||
// If the relocation target is within the current function, we can
|
|
||||||
// convert it into a relative branch target. Note that we check
|
|
||||||
// target_address > start_address instead of >= so that recursive
|
|
||||||
// tail calls are not considered branch targets.
|
|
||||||
let target_address =
|
|
||||||
reloc.target.address.checked_add_signed(reloc.addend);
|
|
||||||
if reloc.target.orig_section_index == Some(section_index)
|
|
||||||
&& matches!(target_address, Some(addr) if addr > start_address && addr < end_address)
|
|
||||||
{
|
|
||||||
let target_address = target_address.unwrap();
|
|
||||||
args.push(ObjInsArg::BranchDest(target_address));
|
|
||||||
branch_dest = Some(target_address);
|
|
||||||
} else {
|
|
||||||
push_reloc(&mut args, reloc)?;
|
|
||||||
branch_dest = None;
|
|
||||||
}
|
|
||||||
} else if let Some(branch_dest) = branch_dest {
|
|
||||||
args.push(ObjInsArg::BranchDest(branch_dest));
|
|
||||||
} else {
|
|
||||||
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(
|
|
||||||
op.display(&instruction, &display_flags, None::<&str>)
|
|
||||||
.to_string()
|
|
||||||
.into(),
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ValuedOperand::core_immediate_base(imm, base) => {
|
|
||||||
if let Some(reloc) = reloc {
|
|
||||||
push_reloc(&mut args, reloc)?;
|
|
||||||
} else {
|
|
||||||
args.push(ObjInsArg::Arg(match imm {
|
|
||||||
IU16::Integer(s) => ObjInsArgValue::Signed(s as i64),
|
|
||||||
IU16::Unsigned(u) => ObjInsArgValue::Unsigned(u as u64),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
args.push(ObjInsArg::PlainText("(".into()));
|
|
||||||
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(
|
|
||||||
base.either_name(instruction.flags().abi(), display_flags.named_gpr())
|
|
||||||
.into(),
|
|
||||||
)));
|
|
||||||
args.push(ObjInsArg::PlainText(")".into()));
|
|
||||||
}
|
|
||||||
// ValuedOperand::r5900_immediate15(..) => match reloc {
|
|
||||||
// Some(reloc)
|
|
||||||
// if reloc.flags == RelocationFlags::Elf { r_type: R_MIPS15_S3 } =>
|
|
||||||
// {
|
|
||||||
// push_reloc(&mut args, reloc)?;
|
|
||||||
// }
|
|
||||||
// _ => {
|
|
||||||
// args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(
|
|
||||||
// op.disassemble(&instruction, None).into(),
|
|
||||||
// )));
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
_ => {
|
|
||||||
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(
|
|
||||||
op.display(&instruction, &display_flags, None::<&str>)
|
|
||||||
.to_string()
|
|
||||||
.into(),
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let line = line_info.range(..=cur_addr as u64).last().map(|(_, &b)| b);
|
|
||||||
insts.push(ObjIns {
|
|
||||||
address: cur_addr as u64,
|
|
||||||
size: 4,
|
|
||||||
op,
|
|
||||||
mnemonic: Cow::Borrowed(mnemonic),
|
|
||||||
args,
|
|
||||||
reloc: reloc.cloned(),
|
|
||||||
branch_dest,
|
branch_dest,
|
||||||
line,
|
|
||||||
formatted,
|
|
||||||
orig: None,
|
|
||||||
});
|
});
|
||||||
cur_addr += 4;
|
cur_addr += 4;
|
||||||
}
|
}
|
||||||
Ok(ProcessCodeResult { ops, insts })
|
Ok(ops)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn display_instruction(
|
||||||
|
&self,
|
||||||
|
ins_ref: InstructionRef,
|
||||||
|
code: &[u8],
|
||||||
|
relocation: Option<ResolvedRelocation>,
|
||||||
|
function_range: Range<u64>,
|
||||||
|
section_index: usize,
|
||||||
|
diff_config: &DiffObjConfig,
|
||||||
|
cb: &mut dyn FnMut(InstructionPart) -> Result<()>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let instruction = self.parse_ins_ref(ins_ref, code, diff_config)?;
|
||||||
|
let display_flags = self.instruction_display_flags(diff_config);
|
||||||
|
let opcode = instruction.opcode();
|
||||||
|
cb(InstructionPart::Opcode(Cow::Borrowed(opcode.name()), opcode as u16))?;
|
||||||
|
push_args(&instruction, relocation, function_range, section_index, &display_flags, cb)?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn implcit_addend(
|
fn implcit_addend(
|
||||||
&self,
|
&self,
|
||||||
file: &File<'_>,
|
file: &object::File<'_>,
|
||||||
section: &ObjSection,
|
section: &object::Section,
|
||||||
address: u64,
|
address: u64,
|
||||||
reloc: &Relocation,
|
reloc: &object::Relocation,
|
||||||
|
flags: RelocationFlags,
|
||||||
) -> Result<i64> {
|
) -> Result<i64> {
|
||||||
let data = section.data[address as usize..address as usize + 4].try_into()?;
|
let data = section.data()?;
|
||||||
let addend = self.endianness.read_u32_bytes(data);
|
let code = data[address as usize..address as usize + 4].try_into()?;
|
||||||
Ok(match reloc.flags() {
|
let addend = self.endianness.read_u32_bytes(code);
|
||||||
RelocationFlags::Elf { r_type: elf::R_MIPS_32 } => addend as i64,
|
Ok(match flags {
|
||||||
RelocationFlags::Elf { r_type: elf::R_MIPS_26 } => ((addend & 0x03FFFFFF) << 2) as i64,
|
RelocationFlags::Elf(elf::R_MIPS_32) => addend as i64,
|
||||||
RelocationFlags::Elf { r_type: elf::R_MIPS_HI16 } => {
|
RelocationFlags::Elf(elf::R_MIPS_26) => ((addend & 0x03FFFFFF) << 2) as i64,
|
||||||
((addend & 0x0000FFFF) << 16) as i32 as i64
|
RelocationFlags::Elf(elf::R_MIPS_HI16) => ((addend & 0x0000FFFF) << 16) as i32 as i64,
|
||||||
|
RelocationFlags::Elf(elf::R_MIPS_LO16 | elf::R_MIPS_GOT16 | elf::R_MIPS_CALL16) => {
|
||||||
|
(addend & 0x0000FFFF) as i16 as i64
|
||||||
}
|
}
|
||||||
RelocationFlags::Elf {
|
RelocationFlags::Elf(elf::R_MIPS_GPREL16 | elf::R_MIPS_LITERAL) => {
|
||||||
r_type: elf::R_MIPS_LO16 | elf::R_MIPS_GOT16 | elf::R_MIPS_CALL16,
|
let object::RelocationTarget::Symbol(idx) = reloc.target() else {
|
||||||
} => (addend & 0x0000FFFF) as i16 as i64,
|
|
||||||
RelocationFlags::Elf { r_type: elf::R_MIPS_GPREL16 | elf::R_MIPS_LITERAL } => {
|
|
||||||
let RelocationTarget::Symbol(idx) = reloc.target() else {
|
|
||||||
bail!("Unsupported R_MIPS_GPREL16 relocation against a non-symbol");
|
bail!("Unsupported R_MIPS_GPREL16 relocation against a non-symbol");
|
||||||
};
|
};
|
||||||
let sym = file.symbol_by_index(idx)?;
|
let sym = file.symbol_by_index(idx)?;
|
||||||
@ -257,15 +196,15 @@ impl ObjArch for ObjArchMips {
|
|||||||
(addend & 0x0000FFFF) as i16 as i64
|
(addend & 0x0000FFFF) as i16 as i64
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RelocationFlags::Elf { r_type: elf::R_MIPS_PC16 } => 0, // PC-relative relocation
|
RelocationFlags::Elf(elf::R_MIPS_PC16) => 0, // PC-relative relocation
|
||||||
RelocationFlags::Elf { r_type: R_MIPS15_S3 } => ((addend & 0x001FFFC0) >> 3) as i64,
|
RelocationFlags::Elf(R_MIPS15_S3) => ((addend & 0x001FFFC0) >> 3) as i64,
|
||||||
flags => bail!("Unsupported MIPS implicit relocation {flags:?}"),
|
flags => bail!("Unsupported MIPS implicit relocation {flags:?}"),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn display_reloc(&self, flags: RelocationFlags) -> Cow<'static, str> {
|
fn display_reloc(&self, flags: RelocationFlags) -> Cow<'static, str> {
|
||||||
match flags {
|
match flags {
|
||||||
RelocationFlags::Elf { r_type } => match r_type {
|
RelocationFlags::Elf(r_type) => match r_type {
|
||||||
elf::R_MIPS_32 => Cow::Borrowed("R_MIPS_32"),
|
elf::R_MIPS_32 => Cow::Borrowed("R_MIPS_32"),
|
||||||
elf::R_MIPS_26 => Cow::Borrowed("R_MIPS_26"),
|
elf::R_MIPS_26 => Cow::Borrowed("R_MIPS_26"),
|
||||||
elf::R_MIPS_HI16 => Cow::Borrowed("R_MIPS_HI16"),
|
elf::R_MIPS_HI16 => Cow::Borrowed("R_MIPS_HI16"),
|
||||||
@ -284,7 +223,7 @@ impl ObjArch for ObjArchMips {
|
|||||||
|
|
||||||
fn get_reloc_byte_size(&self, flags: RelocationFlags) -> usize {
|
fn get_reloc_byte_size(&self, flags: RelocationFlags) -> usize {
|
||||||
match flags {
|
match flags {
|
||||||
RelocationFlags::Elf { r_type } => match r_type {
|
RelocationFlags::Elf(r_type) => match r_type {
|
||||||
elf::R_MIPS_16 => 2,
|
elf::R_MIPS_16 => 2,
|
||||||
elf::R_MIPS_32 => 4,
|
elf::R_MIPS_32 => 4,
|
||||||
_ => 1,
|
_ => 1,
|
||||||
@ -294,40 +233,139 @@ impl ObjArch for ObjArchMips {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_reloc(args: &mut Vec<ObjInsArg>, reloc: &ObjReloc) -> Result<()> {
|
fn push_args(
|
||||||
|
instruction: &rabbitizer::Instruction,
|
||||||
|
relocation: Option<ResolvedRelocation>,
|
||||||
|
function_range: Range<u64>,
|
||||||
|
section_index: usize,
|
||||||
|
display_flags: &rabbitizer::InstructionDisplayFlags,
|
||||||
|
mut arg_cb: impl FnMut(InstructionPart) -> Result<()>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let operands = instruction.valued_operands_iter();
|
||||||
|
for (idx, op) in operands.enumerate() {
|
||||||
|
if idx > 0 {
|
||||||
|
arg_cb(InstructionPart::Separator)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
match op {
|
||||||
|
ValuedOperand::core_immediate(imm) => {
|
||||||
|
if let Some(resolved) = relocation {
|
||||||
|
push_reloc(resolved.relocation, &mut arg_cb)?;
|
||||||
|
} else {
|
||||||
|
arg_cb(InstructionPart::Arg(InstructionArg::Value(match imm {
|
||||||
|
IU16::Integer(s) => InstructionArgValue::Signed(s as i64),
|
||||||
|
IU16::Unsigned(u) => InstructionArgValue::Unsigned(u as u64),
|
||||||
|
})))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ValuedOperand::core_label(..) | ValuedOperand::core_branch_target_label(..) => {
|
||||||
|
if let Some(resolved) = relocation {
|
||||||
|
// If the relocation target is within the current function, we can
|
||||||
|
// convert it into a relative branch target. Note that we check
|
||||||
|
// target_address > start_address instead of >= so that recursive
|
||||||
|
// tail calls are not considered branch targets.
|
||||||
|
let target_address =
|
||||||
|
resolved.symbol.address.checked_add_signed(resolved.relocation.addend);
|
||||||
|
if resolved.symbol.section == Some(section_index)
|
||||||
|
&& target_address.is_some_and(|addr| {
|
||||||
|
addr > function_range.start && addr < function_range.end
|
||||||
|
})
|
||||||
|
{
|
||||||
|
// TODO move this logic up a level
|
||||||
|
let target_address = target_address.unwrap();
|
||||||
|
arg_cb(InstructionPart::Arg(InstructionArg::BranchDest(target_address)))?;
|
||||||
|
} else {
|
||||||
|
push_reloc(resolved.relocation, &mut arg_cb)?;
|
||||||
|
}
|
||||||
|
} else if let Some(branch_dest) = instruction
|
||||||
|
.get_branch_offset_generic()
|
||||||
|
.map(|o| (instruction.vram() + o).inner() as u64)
|
||||||
|
{
|
||||||
|
arg_cb(InstructionPart::Arg(InstructionArg::BranchDest(branch_dest)))?;
|
||||||
|
} else {
|
||||||
|
arg_cb(InstructionPart::Arg(InstructionArg::Value(
|
||||||
|
InstructionArgValue::Opaque(
|
||||||
|
op.display(instruction, display_flags, None::<&str>)
|
||||||
|
.to_string()
|
||||||
|
.into(),
|
||||||
|
),
|
||||||
|
)))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ValuedOperand::core_immediate_base(imm, base) => {
|
||||||
|
if let Some(resolved) = relocation {
|
||||||
|
push_reloc(resolved.relocation, &mut arg_cb)?;
|
||||||
|
} else {
|
||||||
|
arg_cb(InstructionPart::Arg(InstructionArg::Value(match imm {
|
||||||
|
IU16::Integer(s) => InstructionArgValue::Signed(s as i64),
|
||||||
|
IU16::Unsigned(u) => InstructionArgValue::Unsigned(u as u64),
|
||||||
|
})))?;
|
||||||
|
}
|
||||||
|
arg_cb(InstructionPart::Basic("("))?;
|
||||||
|
arg_cb(InstructionPart::Arg(InstructionArg::Value(InstructionArgValue::Opaque(
|
||||||
|
base.either_name(instruction.flags().abi(), display_flags.named_gpr()).into(),
|
||||||
|
))))?;
|
||||||
|
arg_cb(InstructionPart::Basic(")"))?;
|
||||||
|
}
|
||||||
|
// ValuedOperand::r5900_immediate15(..) => match relocation {
|
||||||
|
// Some(resolved)
|
||||||
|
// if resolved.relocation.flags == RelocationFlags::Elf(R_MIPS15_S3) =>
|
||||||
|
// {
|
||||||
|
// push_reloc(&resolved.relocation, &mut arg_cb, &mut plain_cb)?;
|
||||||
|
// }
|
||||||
|
// _ => {
|
||||||
|
// arg_cb(InstructionArg::Value(InstructionArgValue::Opaque(
|
||||||
|
// op.disassemble(&instruction, None).into(),
|
||||||
|
// )))?;
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
_ => {
|
||||||
|
arg_cb(InstructionPart::Arg(InstructionArg::Value(InstructionArgValue::Opaque(
|
||||||
|
op.display(instruction, display_flags, None::<&str>).to_string().into(),
|
||||||
|
))))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_reloc(
|
||||||
|
reloc: &Relocation,
|
||||||
|
mut arg_cb: impl FnMut(InstructionPart) -> Result<()>,
|
||||||
|
) -> Result<()> {
|
||||||
match reloc.flags {
|
match reloc.flags {
|
||||||
RelocationFlags::Elf { r_type } => match r_type {
|
RelocationFlags::Elf(r_type) => match r_type {
|
||||||
elf::R_MIPS_HI16 => {
|
elf::R_MIPS_HI16 => {
|
||||||
args.push(ObjInsArg::PlainText("%hi(".into()));
|
arg_cb(InstructionPart::Basic("%hi("))?;
|
||||||
args.push(ObjInsArg::Reloc);
|
arg_cb(InstructionPart::Arg(InstructionArg::Reloc))?;
|
||||||
args.push(ObjInsArg::PlainText(")".into()));
|
arg_cb(InstructionPart::Basic(")"))?;
|
||||||
}
|
}
|
||||||
elf::R_MIPS_LO16 => {
|
elf::R_MIPS_LO16 => {
|
||||||
args.push(ObjInsArg::PlainText("%lo(".into()));
|
arg_cb(InstructionPart::Basic("%lo("))?;
|
||||||
args.push(ObjInsArg::Reloc);
|
arg_cb(InstructionPart::Arg(InstructionArg::Reloc))?;
|
||||||
args.push(ObjInsArg::PlainText(")".into()));
|
arg_cb(InstructionPart::Basic(")"))?;
|
||||||
}
|
}
|
||||||
elf::R_MIPS_GOT16 => {
|
elf::R_MIPS_GOT16 => {
|
||||||
args.push(ObjInsArg::PlainText("%got(".into()));
|
arg_cb(InstructionPart::Basic("%got("))?;
|
||||||
args.push(ObjInsArg::Reloc);
|
arg_cb(InstructionPart::Arg(InstructionArg::Reloc))?;
|
||||||
args.push(ObjInsArg::PlainText(")".into()));
|
arg_cb(InstructionPart::Basic(")"))?;
|
||||||
}
|
}
|
||||||
elf::R_MIPS_CALL16 => {
|
elf::R_MIPS_CALL16 => {
|
||||||
args.push(ObjInsArg::PlainText("%call16(".into()));
|
arg_cb(InstructionPart::Basic("%call16("))?;
|
||||||
args.push(ObjInsArg::Reloc);
|
arg_cb(InstructionPart::Arg(InstructionArg::Reloc))?;
|
||||||
args.push(ObjInsArg::PlainText(")".into()));
|
arg_cb(InstructionPart::Basic(")"))?;
|
||||||
}
|
}
|
||||||
elf::R_MIPS_GPREL16 => {
|
elf::R_MIPS_GPREL16 => {
|
||||||
args.push(ObjInsArg::PlainText("%gp_rel(".into()));
|
arg_cb(InstructionPart::Basic("%gp_rel("))?;
|
||||||
args.push(ObjInsArg::Reloc);
|
arg_cb(InstructionPart::Arg(InstructionArg::Reloc))?;
|
||||||
args.push(ObjInsArg::PlainText(")".into()));
|
arg_cb(InstructionPart::Basic(")"))?;
|
||||||
}
|
}
|
||||||
elf::R_MIPS_32
|
elf::R_MIPS_32
|
||||||
| elf::R_MIPS_26
|
| elf::R_MIPS_26
|
||||||
| elf::R_MIPS_LITERAL
|
| elf::R_MIPS_LITERAL
|
||||||
| elf::R_MIPS_PC16
|
| elf::R_MIPS_PC16
|
||||||
| R_MIPS15_S3 => {
|
| R_MIPS15_S3 => {
|
||||||
args.push(ObjInsArg::Reloc);
|
arg_cb(InstructionPart::Arg(InstructionArg::Reloc))?;
|
||||||
}
|
}
|
||||||
_ => bail!("Unsupported ELF MIPS relocation type {r_type}"),
|
_ => bail!("Unsupported ELF MIPS relocation type {r_type}"),
|
||||||
},
|
},
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
use alloc::{borrow::Cow, boxed::Box, collections::BTreeMap, format, string::String, vec::Vec};
|
use alloc::{borrow::Cow, boxed::Box, format, string::String, vec, vec::Vec};
|
||||||
use core::ffi::CStr;
|
use core::{ffi::CStr, fmt, fmt::Debug, ops::Range};
|
||||||
|
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
use byteorder::ByteOrder;
|
use byteorder::ByteOrder;
|
||||||
use object::{Architecture, File, Object, ObjectSymbol, Relocation, RelocationFlags, Symbol};
|
use object::{File, Relocation, Section};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
diff::DiffObjConfig,
|
diff::{display::InstructionPart, DiffObjConfig},
|
||||||
obj::{ObjIns, ObjReloc, ObjSection},
|
obj::{
|
||||||
|
InstructionRef, ParsedInstruction, RelocationFlags, ResolvedRelocation, ScannedInstruction,
|
||||||
|
SymbolFlagSet, SymbolKind,
|
||||||
|
},
|
||||||
util::ReallySigned,
|
util::ReallySigned,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -35,8 +38,8 @@ pub enum DataType {
|
|||||||
String,
|
String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for DataType {
|
impl fmt::Display for DataType {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
DataType::Int8 => write!(f, "Int8"),
|
DataType::Int8 => write!(f, "Int8"),
|
||||||
DataType::Int16 => write!(f, "Int16"),
|
DataType::Int16 => write!(f, "Int16"),
|
||||||
@ -154,23 +157,76 @@ impl DataType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ObjArch: Send + Sync {
|
pub trait Arch: Send + Sync + Debug {
|
||||||
fn process_code(
|
/// Generate a list of instructions references (offset, size, opcode) from the given code.
|
||||||
|
///
|
||||||
|
/// The opcode IDs are used to generate the initial diff. Implementations should do as little
|
||||||
|
/// parsing as possible here: just enough to identify the base instruction opcode, size, and
|
||||||
|
/// possible branch destination (for visual representation). As needed, instructions are parsed
|
||||||
|
/// via `process_instruction` to compare their arguments.
|
||||||
|
fn scan_instructions(
|
||||||
&self,
|
&self,
|
||||||
address: u64,
|
address: u64,
|
||||||
code: &[u8],
|
code: &[u8],
|
||||||
section_index: usize,
|
section_index: usize,
|
||||||
relocations: &[ObjReloc],
|
diff_config: &DiffObjConfig,
|
||||||
line_info: &BTreeMap<u64, u32>,
|
) -> Result<Vec<ScannedInstruction>>;
|
||||||
config: &DiffObjConfig,
|
|
||||||
) -> Result<ProcessCodeResult>;
|
/// Parse an instruction to gather its mnemonic and arguments for more detailed comparison.
|
||||||
|
///
|
||||||
|
/// This is called only when we need to compare the arguments of an instruction.
|
||||||
|
fn process_instruction(
|
||||||
|
&self,
|
||||||
|
ins_ref: InstructionRef,
|
||||||
|
code: &[u8],
|
||||||
|
relocation: Option<ResolvedRelocation>,
|
||||||
|
function_range: Range<u64>,
|
||||||
|
section_index: usize,
|
||||||
|
diff_config: &DiffObjConfig,
|
||||||
|
) -> Result<ParsedInstruction> {
|
||||||
|
let mut mnemonic = None;
|
||||||
|
let mut args = Vec::with_capacity(8);
|
||||||
|
self.display_instruction(
|
||||||
|
ins_ref,
|
||||||
|
code,
|
||||||
|
relocation,
|
||||||
|
function_range,
|
||||||
|
section_index,
|
||||||
|
diff_config,
|
||||||
|
&mut |part| {
|
||||||
|
match part {
|
||||||
|
InstructionPart::Opcode(m, _) => mnemonic = Some(m),
|
||||||
|
InstructionPart::Arg(arg) => args.push(arg),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
Ok(ParsedInstruction { ins_ref, mnemonic: mnemonic.unwrap_or_default(), args })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Format an instruction for display.
|
||||||
|
///
|
||||||
|
/// Implementations should call the callback for each part of the instruction: usually the
|
||||||
|
/// mnemonic and arguments, plus any separators and visual formatting.
|
||||||
|
fn display_instruction(
|
||||||
|
&self,
|
||||||
|
ins_ref: InstructionRef,
|
||||||
|
code: &[u8],
|
||||||
|
relocation: Option<ResolvedRelocation>,
|
||||||
|
function_range: Range<u64>,
|
||||||
|
section_index: usize,
|
||||||
|
diff_config: &DiffObjConfig,
|
||||||
|
cb: &mut dyn FnMut(InstructionPart) -> Result<()>,
|
||||||
|
) -> Result<()>;
|
||||||
|
|
||||||
fn implcit_addend(
|
fn implcit_addend(
|
||||||
&self,
|
&self,
|
||||||
file: &File<'_>,
|
file: &object::File<'_>,
|
||||||
section: &ObjSection,
|
section: &object::Section,
|
||||||
address: u64,
|
address: u64,
|
||||||
reloc: &Relocation,
|
relocation: &object::Relocation,
|
||||||
|
flags: RelocationFlags,
|
||||||
) -> Result<i64>;
|
) -> Result<i64>;
|
||||||
|
|
||||||
fn demangle(&self, _name: &str) -> Option<String> { None }
|
fn demangle(&self, _name: &str) -> Option<String> { None }
|
||||||
@ -179,9 +235,20 @@ pub trait ObjArch: Send + Sync {
|
|||||||
|
|
||||||
fn get_reloc_byte_size(&self, flags: RelocationFlags) -> usize;
|
fn get_reloc_byte_size(&self, flags: RelocationFlags) -> usize;
|
||||||
|
|
||||||
fn symbol_address(&self, symbol: &Symbol) -> u64 { symbol.address() }
|
fn symbol_address(&self, address: u64, _kind: SymbolKind) -> u64 { address }
|
||||||
|
|
||||||
fn guess_data_type(&self, _instruction: &ObjIns) -> Option<DataType> { None }
|
fn extra_symbol_flags(&self, _symbol: &object::Symbol) -> SymbolFlagSet {
|
||||||
|
SymbolFlagSet::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn guess_data_type(
|
||||||
|
&self,
|
||||||
|
_ins_ref: InstructionRef,
|
||||||
|
_code: &[u8],
|
||||||
|
_relocation: Option<ResolvedRelocation>,
|
||||||
|
) -> Option<DataType> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn display_data_labels(&self, _ty: DataType, bytes: &[u8]) -> Vec<String> {
|
fn display_data_labels(&self, _ty: DataType, bytes: &[u8]) -> Vec<String> {
|
||||||
vec![format!("Bytes: {:#x?}", bytes)]
|
vec![format!("Bytes: {:#x?}", bytes)]
|
||||||
@ -191,58 +258,113 @@ pub trait ObjArch: Send + Sync {
|
|||||||
vec![format!("{:#?}", bytes)]
|
vec![format!("{:#?}", bytes)]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn display_ins_data_labels(&self, ins: &ObjIns) -> Vec<String> {
|
fn display_ins_data_labels(
|
||||||
let Some(reloc) = ins.reloc.as_ref() else {
|
&self,
|
||||||
return Vec::new();
|
_ins_ref: InstructionRef,
|
||||||
};
|
_code: &[u8],
|
||||||
if reloc.addend >= 0 && reloc.target.bytes.len() > reloc.addend as usize {
|
_relocation: Option<ResolvedRelocation>,
|
||||||
return self
|
) -> Vec<String> {
|
||||||
.guess_data_type(ins)
|
// TODO
|
||||||
.map(|ty| {
|
// let Some(reloc) = relocation else {
|
||||||
self.display_data_labels(ty, &reloc.target.bytes[reloc.addend as usize..])
|
// return Vec::new();
|
||||||
})
|
// };
|
||||||
.unwrap_or_default();
|
// if reloc.relocation.addend >= 0 && reloc.symbol.bytes.len() > reloc.relocation.addend as usize {
|
||||||
}
|
// return self
|
||||||
|
// .guess_data_type(ins)
|
||||||
|
// .map(|ty| {
|
||||||
|
// self.display_data_labels(ty, &reloc.target.bytes[reloc.addend as usize..])
|
||||||
|
// })
|
||||||
|
// .unwrap_or_default();
|
||||||
|
// }
|
||||||
Vec::new()
|
Vec::new()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn display_ins_data_literals(&self, ins: &ObjIns) -> Vec<String> {
|
fn display_ins_data_literals(
|
||||||
let Some(reloc) = ins.reloc.as_ref() else {
|
&self,
|
||||||
return Vec::new();
|
_ins_ref: InstructionRef,
|
||||||
};
|
_code: &[u8],
|
||||||
if reloc.addend >= 0 && reloc.target.bytes.len() > reloc.addend as usize {
|
_relocation: Option<ResolvedRelocation>,
|
||||||
return self
|
) -> Vec<String> {
|
||||||
.guess_data_type(ins)
|
// TODO
|
||||||
.map(|ty| {
|
// let Some(reloc) = ins.reloc.as_ref() else {
|
||||||
self.display_data_literals(ty, &reloc.target.bytes[reloc.addend as usize..])
|
// return Vec::new();
|
||||||
})
|
// };
|
||||||
.unwrap_or_default();
|
// if reloc.addend >= 0 && reloc.target.bytes.len() > reloc.addend as usize {
|
||||||
}
|
// return self
|
||||||
|
// .guess_data_type(ins)
|
||||||
|
// .map(|ty| {
|
||||||
|
// self.display_data_literals(ty, &reloc.target.bytes[reloc.addend as usize..])
|
||||||
|
// })
|
||||||
|
// .unwrap_or_default();
|
||||||
|
// }
|
||||||
Vec::new()
|
Vec::new()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Downcast methods
|
|
||||||
#[cfg(feature = "ppc")]
|
|
||||||
fn ppc(&self) -> Option<&ppc::ObjArchPpc> { None }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ProcessCodeResult {
|
pub fn new_arch(object: &object::File) -> Result<Box<dyn Arch>> {
|
||||||
pub ops: Vec<u16>,
|
use object::Object as _;
|
||||||
pub insts: Vec<ObjIns>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_arch(object: &File) -> Result<Box<dyn ObjArch>> {
|
|
||||||
Ok(match object.architecture() {
|
Ok(match object.architecture() {
|
||||||
#[cfg(feature = "ppc")]
|
#[cfg(feature = "ppc")]
|
||||||
Architecture::PowerPc => Box::new(ppc::ObjArchPpc::new(object)?),
|
object::Architecture::PowerPc => Box::new(ppc::ArchPpc::new(object)?),
|
||||||
#[cfg(feature = "mips")]
|
#[cfg(feature = "mips")]
|
||||||
Architecture::Mips => Box::new(mips::ObjArchMips::new(object)?),
|
object::Architecture::Mips => Box::new(mips::ArchMips::new(object)?),
|
||||||
#[cfg(feature = "x86")]
|
#[cfg(feature = "x86")]
|
||||||
Architecture::I386 | Architecture::X86_64 => Box::new(x86::ObjArchX86::new(object)?),
|
object::Architecture::I386 | object::Architecture::X86_64 => {
|
||||||
|
Box::new(x86::ArchX86::new(object)?)
|
||||||
|
}
|
||||||
#[cfg(feature = "arm")]
|
#[cfg(feature = "arm")]
|
||||||
Architecture::Arm => Box::new(arm::ObjArchArm::new(object)?),
|
object::Architecture::Arm => Box::new(arm::ArchArm::new(object)?),
|
||||||
#[cfg(feature = "arm64")]
|
#[cfg(feature = "arm64")]
|
||||||
Architecture::Aarch64 => Box::new(arm64::ObjArchArm64::new(object)?),
|
object::Architecture::Aarch64 => Box::new(arm64::ArchArm64::new(object)?),
|
||||||
arch => bail!("Unsupported architecture: {arch:?}"),
|
arch => bail!("Unsupported architecture: {arch:?}"),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct ArchDummy {}
|
||||||
|
|
||||||
|
impl ArchDummy {
|
||||||
|
pub fn new() -> Box<Self> { Box::new(Self {}) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Arch for ArchDummy {
|
||||||
|
fn scan_instructions(
|
||||||
|
&self,
|
||||||
|
_address: u64,
|
||||||
|
_code: &[u8],
|
||||||
|
_section_index: usize,
|
||||||
|
_diff_config: &DiffObjConfig,
|
||||||
|
) -> Result<Vec<ScannedInstruction>> {
|
||||||
|
Ok(Vec::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn display_instruction(
|
||||||
|
&self,
|
||||||
|
_ins_ref: InstructionRef,
|
||||||
|
_code: &[u8],
|
||||||
|
_relocation: Option<ResolvedRelocation>,
|
||||||
|
_function_range: Range<u64>,
|
||||||
|
_section_index: usize,
|
||||||
|
_diff_config: &DiffObjConfig,
|
||||||
|
_cb: &mut dyn FnMut(InstructionPart) -> Result<()>,
|
||||||
|
) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn implcit_addend(
|
||||||
|
&self,
|
||||||
|
_file: &File<'_>,
|
||||||
|
_section: &Section,
|
||||||
|
_address: u64,
|
||||||
|
_relocation: &Relocation,
|
||||||
|
_flags: RelocationFlags,
|
||||||
|
) -> Result<i64> {
|
||||||
|
Ok(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn display_reloc(&self, flags: RelocationFlags) -> Cow<'static, str> {
|
||||||
|
format!("{flags:?}").into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_reloc_byte_size(&self, _flags: RelocationFlags) -> usize { 0 }
|
||||||
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -7,33 +7,93 @@ use alloc::{
|
|||||||
vec,
|
vec,
|
||||||
vec::Vec,
|
vec::Vec,
|
||||||
};
|
};
|
||||||
|
use std::ops::Range;
|
||||||
|
|
||||||
use anyhow::{anyhow, bail, ensure, Result};
|
use anyhow::{anyhow, bail, ensure, Result};
|
||||||
use iced_x86::{
|
use iced_x86::{
|
||||||
Decoder, DecoderOptions, DecoratorKind, Formatter, FormatterOutput, FormatterTextKind,
|
Decoder, DecoderOptions, DecoratorKind, FormatterOutput, FormatterTextKind, GasFormatter,
|
||||||
GasFormatter, Instruction, IntelFormatter, MasmFormatter, NasmFormatter, NumberKind, OpKind,
|
Instruction, IntelFormatter, MasmFormatter, NasmFormatter, NumberKind, OpKind, PrefixKind,
|
||||||
PrefixKind, Register,
|
Register,
|
||||||
};
|
};
|
||||||
use object::{pe, Endian, Endianness, File, Object, Relocation, RelocationFlags};
|
use object::{pe, Endian as _, Object as _, ObjectSection as _};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
arch::{ObjArch, ProcessCodeResult},
|
arch::Arch,
|
||||||
diff::{DiffObjConfig, X86Formatter},
|
diff::{display::InstructionPart, DiffObjConfig, X86Formatter},
|
||||||
obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection},
|
obj::{
|
||||||
|
InstructionArg, InstructionArgValue, InstructionRef, ParsedInstruction, RelocationFlags,
|
||||||
|
ResolvedRelocation, ScannedInstruction,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct ObjArchX86 {
|
#[derive(Debug)]
|
||||||
|
pub struct ArchX86 {
|
||||||
bits: u32,
|
bits: u32,
|
||||||
endianness: Endianness,
|
endianness: object::Endianness,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjArchX86 {
|
impl ArchX86 {
|
||||||
pub fn new(object: &File) -> Result<Self> {
|
pub fn new(object: &object::File) -> Result<Self> {
|
||||||
Ok(Self { bits: if object.is_64() { 64 } else { 32 }, endianness: object.endianness() })
|
Ok(Self { bits: if object.is_64() { 64 } else { 32 }, endianness: object.endianness() })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn formatter(&self, diff_config: &DiffObjConfig) -> Box<dyn iced_x86::Formatter> {
|
||||||
|
let mut formatter: Box<dyn iced_x86::Formatter> = match diff_config.x86_formatter {
|
||||||
|
X86Formatter::Intel => Box::new(IntelFormatter::new()),
|
||||||
|
X86Formatter::Gas => Box::new(GasFormatter::new()),
|
||||||
|
X86Formatter::Nasm => Box::new(NasmFormatter::new()),
|
||||||
|
X86Formatter::Masm => Box::new(MasmFormatter::new()),
|
||||||
|
};
|
||||||
|
formatter.options_mut().set_space_after_operand_separator(diff_config.space_between_args);
|
||||||
|
formatter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Arch for ArchX86 {
|
||||||
|
fn scan_instructions(
|
||||||
|
&self,
|
||||||
|
address: u64,
|
||||||
|
code: &[u8],
|
||||||
|
_section_index: usize,
|
||||||
|
_diff_config: &DiffObjConfig,
|
||||||
|
) -> Result<Vec<ScannedInstruction>> {
|
||||||
|
let mut out = Vec::with_capacity(code.len() / 2);
|
||||||
|
let mut decoder = Decoder::with_ip(self.bits, code, address, DecoderOptions::NONE);
|
||||||
|
let mut instruction = Instruction::default();
|
||||||
|
while decoder.can_decode() {
|
||||||
|
decoder.decode_out(&mut instruction);
|
||||||
|
// TODO is this right?
|
||||||
|
let branch_dest = match instruction.op0_kind() {
|
||||||
|
OpKind::NearBranch16 => Some(instruction.near_branch16() as u64),
|
||||||
|
OpKind::NearBranch32 => Some(instruction.near_branch32() as u64),
|
||||||
|
OpKind::NearBranch64 => Some(instruction.near_branch64()),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
out.push(ScannedInstruction {
|
||||||
|
ins_ref: InstructionRef {
|
||||||
|
address: instruction.ip(),
|
||||||
|
size: instruction.len() as u8,
|
||||||
|
opcode: instruction.mnemonic() as u16,
|
||||||
|
},
|
||||||
|
branch_dest,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn display_instruction(
|
||||||
|
&self,
|
||||||
|
ins_ref: InstructionRef,
|
||||||
|
code: &[u8],
|
||||||
|
relocation: Option<ResolvedRelocation>,
|
||||||
|
function_range: Range<u64>,
|
||||||
|
section_index: usize,
|
||||||
|
diff_config: &DiffObjConfig,
|
||||||
|
cb: &mut dyn FnMut(InstructionPart) -> Result<()>,
|
||||||
|
) -> Result<()> {
|
||||||
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjArch for ObjArchX86 {
|
|
||||||
fn process_code(
|
fn process_code(
|
||||||
&self,
|
&self,
|
||||||
address: u64,
|
address: u64,
|
||||||
@ -45,13 +105,7 @@ impl ObjArch for ObjArchX86 {
|
|||||||
) -> Result<ProcessCodeResult> {
|
) -> Result<ProcessCodeResult> {
|
||||||
let mut result = ProcessCodeResult { ops: Vec::new(), insts: Vec::new() };
|
let mut result = ProcessCodeResult { ops: Vec::new(), insts: Vec::new() };
|
||||||
let mut decoder = Decoder::with_ip(self.bits, code, address, DecoderOptions::NONE);
|
let mut decoder = Decoder::with_ip(self.bits, code, address, DecoderOptions::NONE);
|
||||||
let mut formatter: Box<dyn Formatter> = match config.x86_formatter {
|
let mut formatter = self.formatter(config);
|
||||||
X86Formatter::Intel => Box::new(IntelFormatter::new()),
|
|
||||||
X86Formatter::Gas => Box::new(GasFormatter::new()),
|
|
||||||
X86Formatter::Nasm => Box::new(NasmFormatter::new()),
|
|
||||||
X86Formatter::Masm => Box::new(MasmFormatter::new()),
|
|
||||||
};
|
|
||||||
formatter.options_mut().set_space_after_operand_separator(config.space_between_args);
|
|
||||||
|
|
||||||
let mut output = InstructionFormatterOutput {
|
let mut output = InstructionFormatterOutput {
|
||||||
formatted: String::new(),
|
formatted: String::new(),
|
||||||
@ -101,10 +155,12 @@ impl ObjArch for ObjArchX86 {
|
|||||||
output.ins.formatted.clone_from(&output.formatted);
|
output.ins.formatted.clone_from(&output.formatted);
|
||||||
|
|
||||||
// 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, InstructionArg::Reloc))
|
||||||
|
{
|
||||||
let mut found = replace_arg(
|
let mut found = replace_arg(
|
||||||
OpKind::Memory,
|
OpKind::Memory,
|
||||||
ObjInsArg::Reloc,
|
InstructionArg::Reloc,
|
||||||
&mut output.ins.args,
|
&mut output.ins.args,
|
||||||
&instruction,
|
&instruction,
|
||||||
&output.ins_operands,
|
&output.ins_operands,
|
||||||
@ -112,7 +168,7 @@ impl ObjArch for ObjArchX86 {
|
|||||||
if !found {
|
if !found {
|
||||||
found = replace_arg(
|
found = replace_arg(
|
||||||
OpKind::Immediate32,
|
OpKind::Immediate32,
|
||||||
ObjInsArg::Reloc,
|
InstructionArg::Reloc,
|
||||||
&mut output.ins.args,
|
&mut output.ins.args,
|
||||||
&instruction,
|
&instruction,
|
||||||
&output.ins_operands,
|
&output.ins_operands,
|
||||||
@ -120,7 +176,9 @@ impl ObjArch for ObjArchX86 {
|
|||||||
}
|
}
|
||||||
ensure!(found, "x86: Failed to find operand for Absolute relocation");
|
ensure!(found, "x86: Failed to find operand for Absolute relocation");
|
||||||
}
|
}
|
||||||
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, InstructionArg::Reloc))
|
||||||
|
{
|
||||||
bail!("Failed to find relocation in instruction");
|
bail!("Failed to find relocation in instruction");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,14 +194,15 @@ impl ObjArch for ObjArchX86 {
|
|||||||
|
|
||||||
fn implcit_addend(
|
fn implcit_addend(
|
||||||
&self,
|
&self,
|
||||||
_file: &File<'_>,
|
_file: &object::File<'_>,
|
||||||
section: &ObjSection,
|
section: &object::Section,
|
||||||
address: u64,
|
address: u64,
|
||||||
reloc: &Relocation,
|
_relocation: &object::Relocation,
|
||||||
|
flags: RelocationFlags,
|
||||||
) -> Result<i64> {
|
) -> Result<i64> {
|
||||||
match reloc.flags() {
|
match flags {
|
||||||
RelocationFlags::Coff { typ: pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32 } => {
|
RelocationFlags::Coff(pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32) => {
|
||||||
let data = section.data[address as usize..address as usize + 4].try_into()?;
|
let data = section.data()[address as usize..address as usize + 4].try_into()?;
|
||||||
Ok(self.endianness.read_i32_bytes(data) as i64)
|
Ok(self.endianness.read_i32_bytes(data) as i64)
|
||||||
}
|
}
|
||||||
flags => bail!("Unsupported x86 implicit relocation {flags:?}"),
|
flags => bail!("Unsupported x86 implicit relocation {flags:?}"),
|
||||||
@ -162,7 +221,7 @@ impl ObjArch for ObjArchX86 {
|
|||||||
|
|
||||||
fn display_reloc(&self, flags: RelocationFlags) -> Cow<'static, str> {
|
fn display_reloc(&self, flags: RelocationFlags) -> Cow<'static, str> {
|
||||||
match flags {
|
match flags {
|
||||||
RelocationFlags::Coff { typ } => match typ {
|
RelocationFlags::Coff(typ) => match typ {
|
||||||
pe::IMAGE_REL_I386_DIR32 => Cow::Borrowed("IMAGE_REL_I386_DIR32"),
|
pe::IMAGE_REL_I386_DIR32 => Cow::Borrowed("IMAGE_REL_I386_DIR32"),
|
||||||
pe::IMAGE_REL_I386_REL32 => Cow::Borrowed("IMAGE_REL_I386_REL32"),
|
pe::IMAGE_REL_I386_REL32 => Cow::Borrowed("IMAGE_REL_I386_REL32"),
|
||||||
_ => Cow::Owned(format!("<{flags:?}>")),
|
_ => Cow::Owned(format!("<{flags:?}>")),
|
||||||
@ -173,7 +232,7 @@ impl ObjArch for ObjArchX86 {
|
|||||||
|
|
||||||
fn get_reloc_byte_size(&self, flags: RelocationFlags) -> usize {
|
fn get_reloc_byte_size(&self, flags: RelocationFlags) -> usize {
|
||||||
match flags {
|
match flags {
|
||||||
RelocationFlags::Coff { typ } => match typ {
|
RelocationFlags::Coff(typ) => match typ {
|
||||||
pe::IMAGE_REL_I386_DIR16 => 2,
|
pe::IMAGE_REL_I386_DIR16 => 2,
|
||||||
pe::IMAGE_REL_I386_REL16 => 2,
|
pe::IMAGE_REL_I386_REL16 => 2,
|
||||||
pe::IMAGE_REL_I386_DIR32 => 4,
|
pe::IMAGE_REL_I386_DIR32 => 4,
|
||||||
@ -187,8 +246,8 @@ impl ObjArch for ObjArchX86 {
|
|||||||
|
|
||||||
fn replace_arg(
|
fn replace_arg(
|
||||||
from: OpKind,
|
from: OpKind,
|
||||||
to: ObjInsArg,
|
to: InstructionArg,
|
||||||
args: &mut [ObjInsArg],
|
args: &mut [InstructionArg],
|
||||||
instruction: &Instruction,
|
instruction: &Instruction,
|
||||||
ins_operands: &[Option<u32>],
|
ins_operands: &[Option<u32>],
|
||||||
) -> Result<bool> {
|
) -> Result<bool> {
|
||||||
@ -213,7 +272,7 @@ fn replace_arg(
|
|||||||
|
|
||||||
struct InstructionFormatterOutput {
|
struct InstructionFormatterOutput {
|
||||||
formatted: String,
|
formatted: String,
|
||||||
ins: ObjIns,
|
ins: ParsedInstruction,
|
||||||
error: Option<anyhow::Error>,
|
error: Option<anyhow::Error>,
|
||||||
ins_operands: Vec<Option<u32>>,
|
ins_operands: Vec<Option<u32>>,
|
||||||
}
|
}
|
||||||
@ -223,11 +282,13 @@ impl InstructionFormatterOutput {
|
|||||||
// The formatter writes the '-' operator and then gives us a negative value,
|
// The formatter writes the '-' operator and then gives us a negative value,
|
||||||
// so convert it to a positive value to avoid double negatives
|
// so convert it to a positive value to avoid double negatives
|
||||||
if value < 0
|
if value < 0
|
||||||
&& matches!(self.ins.args.last(), Some(ObjInsArg::Arg(ObjInsArgValue::Opaque(v))) if v == "-")
|
&& matches!(self.ins.args.last(), Some(InstructionArg::Value(InstructionArgValue::Opaque(v))) if v == "-")
|
||||||
{
|
{
|
||||||
self.ins.args.push(ObjInsArg::Arg(ObjInsArgValue::Signed(value.wrapping_abs())));
|
self.ins
|
||||||
|
.args
|
||||||
|
.push(InstructionArg::Value(InstructionArgValue::Signed(value.wrapping_abs())));
|
||||||
} else {
|
} else {
|
||||||
self.ins.args.push(ObjInsArg::Arg(ObjInsArgValue::Signed(value)));
|
self.ins.args.push(InstructionArg::Value(InstructionArgValue::Signed(value)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -242,10 +303,12 @@ impl FormatterOutput for InstructionFormatterOutput {
|
|||||||
self.ins_operands.push(None);
|
self.ins_operands.push(None);
|
||||||
match kind {
|
match kind {
|
||||||
FormatterTextKind::Text | FormatterTextKind::Punctuation => {
|
FormatterTextKind::Text | FormatterTextKind::Punctuation => {
|
||||||
self.ins.args.push(ObjInsArg::PlainText(text.to_string().into()));
|
self.ins.args.push(InstructionArg::PlainText(text.to_string().into()));
|
||||||
}
|
}
|
||||||
FormatterTextKind::Keyword | FormatterTextKind::Operator => {
|
FormatterTextKind::Keyword | FormatterTextKind::Operator => {
|
||||||
self.ins.args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(text.to_string().into())));
|
self.ins.args.push(InstructionArg::Value(InstructionArgValue::Opaque(
|
||||||
|
text.to_string().into(),
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
if self.error.is_none() {
|
if self.error.is_none() {
|
||||||
@ -258,12 +321,13 @@ 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) {
|
||||||
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().into())));
|
self.ins
|
||||||
|
.args
|
||||||
|
.push(InstructionArg::Value(InstructionArgValue::Opaque(text.to_string().into())));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_mnemonic(&mut self, _instruction: &Instruction, text: &str) {
|
fn write_mnemonic(&mut self, _instruction: &Instruction, text: &str) {
|
||||||
self.formatted.push_str(text);
|
self.formatted.push_str(text);
|
||||||
// TODO: can iced-x86 guarantee 'static here?
|
|
||||||
self.ins.mnemonic = Cow::Owned(text.to_string());
|
self.ins.mnemonic = Cow::Owned(text.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -284,10 +348,11 @@ impl FormatterOutput for InstructionFormatterOutput {
|
|||||||
match kind {
|
match kind {
|
||||||
FormatterTextKind::LabelAddress => {
|
FormatterTextKind::LabelAddress => {
|
||||||
if let Some(reloc) = self.ins.reloc.as_ref() {
|
if let Some(reloc) = self.ins.reloc.as_ref() {
|
||||||
if matches!(reloc.flags, RelocationFlags::Coff {
|
if matches!(
|
||||||
typ: pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32
|
reloc.flags,
|
||||||
}) {
|
RelocationFlags::Coff(pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32)
|
||||||
self.ins.args.push(ObjInsArg::Reloc);
|
) {
|
||||||
|
self.ins.args.push(InstructionArg::Reloc);
|
||||||
return;
|
return;
|
||||||
} else if self.error.is_none() {
|
} else if self.error.is_none() {
|
||||||
self.error = Some(anyhow!(
|
self.error = Some(anyhow!(
|
||||||
@ -296,16 +361,14 @@ impl FormatterOutput for InstructionFormatterOutput {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.ins.args.push(ObjInsArg::BranchDest(value));
|
self.ins.args.push(InstructionArg::BranchDest(value));
|
||||||
self.ins.branch_dest = Some(value);
|
self.ins.branch_dest = Some(value);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
FormatterTextKind::FunctionAddress => {
|
FormatterTextKind::FunctionAddress => {
|
||||||
if let Some(reloc) = self.ins.reloc.as_ref() {
|
if let Some(reloc) = self.ins.reloc.as_ref() {
|
||||||
if matches!(reloc.flags, RelocationFlags::Coff {
|
if matches!(reloc.flags, RelocationFlags::Coff(pe::IMAGE_REL_I386_REL32)) {
|
||||||
typ: pe::IMAGE_REL_I386_REL32
|
self.ins.args.push(InstructionArg::Reloc);
|
||||||
}) {
|
|
||||||
self.ins.args.push(ObjInsArg::Reloc);
|
|
||||||
return;
|
return;
|
||||||
} else if self.error.is_none() {
|
} else if self.error.is_none() {
|
||||||
self.error = Some(anyhow!(
|
self.error = Some(anyhow!(
|
||||||
@ -332,7 +395,7 @@ impl FormatterOutput for InstructionFormatterOutput {
|
|||||||
self.push_signed(value as i64);
|
self.push_signed(value as i64);
|
||||||
}
|
}
|
||||||
NumberKind::UInt8 | NumberKind::UInt16 | NumberKind::UInt32 | NumberKind::UInt64 => {
|
NumberKind::UInt8 | NumberKind::UInt16 | NumberKind::UInt32 | NumberKind::UInt64 => {
|
||||||
self.ins.args.push(ObjInsArg::Arg(ObjInsArgValue::Unsigned(value)));
|
self.ins.args.push(InstructionArg::Value(InstructionArgValue::Unsigned(value)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -347,7 +410,7 @@ impl FormatterOutput for InstructionFormatterOutput {
|
|||||||
) {
|
) {
|
||||||
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().into()));
|
self.ins.args.push(InstructionArg::PlainText(text.to_string().into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_register(
|
fn write_register(
|
||||||
@ -360,6 +423,8 @@ impl FormatterOutput for InstructionFormatterOutput {
|
|||||||
) {
|
) {
|
||||||
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().into())));
|
self.ins
|
||||||
|
.args
|
||||||
|
.push(InstructionArg::Value(InstructionArgValue::Opaque(text.to_string().into())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,6 @@
|
|||||||
#![allow(clippy::needless_lifetimes)] // Generated serde code
|
#![allow(clippy::needless_lifetimes)] // Generated serde code
|
||||||
|
|
||||||
use alloc::string::ToString;
|
use crate::{diff, obj};
|
||||||
|
|
||||||
use crate::{
|
|
||||||
diff::{
|
|
||||||
ObjDataDiff, ObjDataDiffKind, ObjDiff, ObjInsArgDiff, ObjInsBranchFrom, ObjInsBranchTo,
|
|
||||||
ObjInsDiff, ObjInsDiffKind, ObjSectionDiff, ObjSymbolDiff,
|
|
||||||
},
|
|
||||||
obj,
|
|
||||||
obj::{
|
|
||||||
ObjInfo, ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSectionKind, ObjSymbol,
|
|
||||||
ObjSymbolFlagSet, ObjSymbolFlags,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// Protobuf diff types
|
// Protobuf diff types
|
||||||
include!(concat!(env!("OUT_DIR"), "/objdiff.diff.rs"));
|
include!(concat!(env!("OUT_DIR"), "/objdiff.diff.rs"));
|
||||||
@ -20,242 +8,235 @@ include!(concat!(env!("OUT_DIR"), "/objdiff.diff.rs"));
|
|||||||
include!(concat!(env!("OUT_DIR"), "/objdiff.diff.serde.rs"));
|
include!(concat!(env!("OUT_DIR"), "/objdiff.diff.serde.rs"));
|
||||||
|
|
||||||
impl DiffResult {
|
impl DiffResult {
|
||||||
pub fn new(left: Option<(&ObjInfo, &ObjDiff)>, right: Option<(&ObjInfo, &ObjDiff)>) -> Self {
|
pub fn new(
|
||||||
|
_left: Option<(&obj::Object, &diff::ObjectDiff)>,
|
||||||
|
_right: Option<(&obj::Object, &diff::ObjectDiff)>,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
left: left.map(|(obj, diff)| ObjectDiff::new(obj, diff)),
|
// TODO
|
||||||
right: right.map(|(obj, diff)| ObjectDiff::new(obj, diff)),
|
// left: left.map(|(obj, diff)| ObjectDiff::new(obj, diff)),
|
||||||
|
// right: right.map(|(obj, diff)| ObjectDiff::new(obj, diff)),
|
||||||
|
left: None,
|
||||||
|
right: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectDiff {
|
// impl ObjectDiff {
|
||||||
pub fn new(obj: &ObjInfo, diff: &ObjDiff) -> Self {
|
// pub fn new(obj: &obj::Object, diff: &diff::ObjectDiff) -> Self {
|
||||||
Self {
|
// Self {
|
||||||
sections: diff
|
// sections: diff
|
||||||
.sections
|
// .sections
|
||||||
.iter()
|
// .iter()
|
||||||
.enumerate()
|
// .enumerate()
|
||||||
.map(|(i, d)| SectionDiff::new(obj, i, d))
|
// .map(|(i, d)| SectionDiff::new(obj, i, d))
|
||||||
.collect(),
|
// .collect(),
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
impl SectionDiff {
|
// impl SectionDiff {
|
||||||
pub fn new(obj: &ObjInfo, section_index: usize, section_diff: &ObjSectionDiff) -> Self {
|
// pub fn new(obj: &obj::Object, section_index: usize, section_diff: &diff::SectionDiff) -> Self {
|
||||||
let section = &obj.sections[section_index];
|
// let section = &obj.sections[section_index];
|
||||||
let symbols = section_diff.symbols.iter().map(|d| SymbolDiff::new(obj, d)).collect();
|
// let symbols = section_diff.symbols.iter().map(|d| SymbolDiff::new(obj, d)).collect();
|
||||||
let data = section_diff.data_diff.iter().map(|d| DataDiff::new(obj, d)).collect();
|
// let data = section_diff.data_diff.iter().map(|d| DataDiff::new(obj, d)).collect();
|
||||||
// TODO: section_diff.reloc_diff
|
// // TODO: section_diff.reloc_diff
|
||||||
Self {
|
// Self {
|
||||||
name: section.name.to_string(),
|
// name: section.name.to_string(),
|
||||||
kind: SectionKind::from(section.kind) as i32,
|
// kind: SectionKind::from(section.kind) as i32,
|
||||||
size: section.size,
|
// size: section.size,
|
||||||
address: section.address,
|
// address: section.address,
|
||||||
symbols,
|
// symbols,
|
||||||
data,
|
// data,
|
||||||
match_percent: section_diff.match_percent,
|
// match_percent: section_diff.match_percent,
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
impl From<ObjSectionKind> for SectionKind {
|
// impl From<obj::SectionKind> for SectionKind {
|
||||||
fn from(value: ObjSectionKind) -> Self {
|
// fn from(value: obj::SectionKind) -> Self {
|
||||||
match value {
|
// match value {
|
||||||
ObjSectionKind::Code => SectionKind::SectionText,
|
// obj::SectionKind::Code => SectionKind::SectionText,
|
||||||
ObjSectionKind::Data => SectionKind::SectionData,
|
// obj::SectionKind::Data => SectionKind::SectionData,
|
||||||
ObjSectionKind::Bss => SectionKind::SectionBss,
|
// obj::SectionKind::Bss => SectionKind::SectionBss,
|
||||||
// TODO common
|
// // TODO common
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
impl From<obj::SymbolRef> for SymbolRef {
|
// impl SymbolDiff {
|
||||||
fn from(value: obj::SymbolRef) -> Self {
|
// pub fn new(object: &obj::Object, symbol_diff: &diff::SymbolDiff) -> Self {
|
||||||
Self {
|
// let symbol = object.symbols[symbol_diff.symbol_index];
|
||||||
section_index: if value.section_idx == obj::SECTION_COMMON {
|
// let instructions = symbol_diff
|
||||||
None
|
// .instruction_rows
|
||||||
} else {
|
// .iter()
|
||||||
Some(value.section_idx as u32)
|
// .map(|ins_diff| InstructionDiff::new(object, ins_diff))
|
||||||
},
|
// .collect();
|
||||||
symbol_index: value.symbol_idx as u32,
|
// Self {
|
||||||
}
|
// symbol: Some(Symbol::new(symbol)),
|
||||||
}
|
// instructions,
|
||||||
}
|
// match_percent: symbol_diff.match_percent,
|
||||||
|
// target: symbol_diff.target_symbol.map(SymbolRef::from),
|
||||||
impl SymbolDiff {
|
// }
|
||||||
pub fn new(object: &ObjInfo, symbol_diff: &ObjSymbolDiff) -> Self {
|
// }
|
||||||
let (_section, symbol) = object.section_symbol(symbol_diff.symbol_ref);
|
// }
|
||||||
let instructions = symbol_diff
|
//
|
||||||
.instructions
|
// impl DataDiff {
|
||||||
.iter()
|
// pub fn new(_object: &obj::Object, data_diff: &diff::DataDiff) -> Self {
|
||||||
.map(|ins_diff| InstructionDiff::new(object, ins_diff))
|
// Self {
|
||||||
.collect();
|
// kind: DiffKind::from(data_diff.kind) as i32,
|
||||||
Self {
|
// data: data_diff.data.clone(),
|
||||||
symbol: Some(Symbol::new(symbol)),
|
// size: data_diff.len as u64,
|
||||||
instructions,
|
// }
|
||||||
match_percent: symbol_diff.match_percent,
|
// }
|
||||||
target: symbol_diff.target_symbol.map(SymbolRef::from),
|
// }
|
||||||
}
|
//
|
||||||
}
|
// impl Symbol {
|
||||||
}
|
// pub fn new(value: &ObjSymbol) -> Self {
|
||||||
|
// Self {
|
||||||
impl DataDiff {
|
// name: value.name.to_string(),
|
||||||
pub fn new(_object: &ObjInfo, data_diff: &ObjDataDiff) -> Self {
|
// demangled_name: value.demangled_name.clone(),
|
||||||
Self {
|
// address: value.address,
|
||||||
kind: DiffKind::from(data_diff.kind) as i32,
|
// size: value.size,
|
||||||
data: data_diff.data.clone(),
|
// flags: symbol_flags(value.flags),
|
||||||
size: data_diff.len as u64,
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
//
|
||||||
|
// fn symbol_flags(value: ObjSymbolFlagSet) -> u32 {
|
||||||
impl Symbol {
|
// let mut flags = 0u32;
|
||||||
pub fn new(value: &ObjSymbol) -> Self {
|
// if value.0.contains(ObjSymbolFlags::Global) {
|
||||||
Self {
|
// flags |= SymbolFlag::SymbolGlobal as u32;
|
||||||
name: value.name.to_string(),
|
// }
|
||||||
demangled_name: value.demangled_name.clone(),
|
// if value.0.contains(ObjSymbolFlags::Local) {
|
||||||
address: value.address,
|
// flags |= SymbolFlag::SymbolLocal as u32;
|
||||||
size: value.size,
|
// }
|
||||||
flags: symbol_flags(value.flags),
|
// if value.0.contains(ObjSymbolFlags::Weak) {
|
||||||
}
|
// flags |= SymbolFlag::SymbolWeak as u32;
|
||||||
}
|
// }
|
||||||
}
|
// if value.0.contains(ObjSymbolFlags::Common) {
|
||||||
|
// flags |= SymbolFlag::SymbolCommon as u32;
|
||||||
fn symbol_flags(value: ObjSymbolFlagSet) -> u32 {
|
// }
|
||||||
let mut flags = 0u32;
|
// if value.0.contains(ObjSymbolFlags::Hidden) {
|
||||||
if value.0.contains(ObjSymbolFlags::Global) {
|
// flags |= SymbolFlag::SymbolHidden as u32;
|
||||||
flags |= SymbolFlag::SymbolGlobal as u32;
|
// }
|
||||||
}
|
// flags
|
||||||
if value.0.contains(ObjSymbolFlags::Local) {
|
// }
|
||||||
flags |= SymbolFlag::SymbolLocal as u32;
|
//
|
||||||
}
|
// impl Instruction {
|
||||||
if value.0.contains(ObjSymbolFlags::Weak) {
|
// pub fn new(object: &obj::Object, instruction: &ObjIns) -> Self {
|
||||||
flags |= SymbolFlag::SymbolWeak as u32;
|
// Self {
|
||||||
}
|
// address: instruction.address,
|
||||||
if value.0.contains(ObjSymbolFlags::Common) {
|
// size: instruction.size as u32,
|
||||||
flags |= SymbolFlag::SymbolCommon as u32;
|
// opcode: instruction.op as u32,
|
||||||
}
|
// mnemonic: instruction.mnemonic.to_string(),
|
||||||
if value.0.contains(ObjSymbolFlags::Hidden) {
|
// formatted: instruction.formatted.clone(),
|
||||||
flags |= SymbolFlag::SymbolHidden as u32;
|
// arguments: instruction.args.iter().map(Argument::new).collect(),
|
||||||
}
|
// relocation: instruction.reloc.as_ref().map(|reloc| Relocation::new(object, reloc)),
|
||||||
flags
|
// branch_dest: instruction.branch_dest,
|
||||||
}
|
// line_number: instruction.line,
|
||||||
|
// original: instruction.orig.clone(),
|
||||||
impl Instruction {
|
// }
|
||||||
pub fn new(object: &ObjInfo, instruction: &ObjIns) -> Self {
|
// }
|
||||||
Self {
|
// }
|
||||||
address: instruction.address,
|
//
|
||||||
size: instruction.size as u32,
|
// impl Argument {
|
||||||
opcode: instruction.op as u32,
|
// pub fn new(value: &ObjInsArg) -> Self {
|
||||||
mnemonic: instruction.mnemonic.to_string(),
|
// Self {
|
||||||
formatted: instruction.formatted.clone(),
|
// value: Some(match value {
|
||||||
arguments: instruction.args.iter().map(Argument::new).collect(),
|
// ObjInsArg::PlainText(s) => argument::Value::PlainText(s.to_string()),
|
||||||
relocation: instruction.reloc.as_ref().map(|reloc| Relocation::new(object, reloc)),
|
// ObjInsArg::Arg(v) => argument::Value::Argument(ArgumentValue::new(v)),
|
||||||
branch_dest: instruction.branch_dest,
|
// ObjInsArg::Reloc => argument::Value::Relocation(ArgumentRelocation {}),
|
||||||
line_number: instruction.line,
|
// ObjInsArg::BranchDest(dest) => argument::Value::BranchDest(*dest),
|
||||||
original: instruction.orig.clone(),
|
// }),
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
impl Argument {
|
// impl ArgumentValue {
|
||||||
pub fn new(value: &ObjInsArg) -> Self {
|
// pub fn new(value: &ObjInsArgValue) -> Self {
|
||||||
Self {
|
// Self {
|
||||||
value: Some(match value {
|
// value: Some(match value {
|
||||||
ObjInsArg::PlainText(s) => argument::Value::PlainText(s.to_string()),
|
// ObjInsArgValue::Signed(v) => argument_value::Value::Signed(*v),
|
||||||
ObjInsArg::Arg(v) => argument::Value::Argument(ArgumentValue::new(v)),
|
// ObjInsArgValue::Unsigned(v) => argument_value::Value::Unsigned(*v),
|
||||||
ObjInsArg::Reloc => argument::Value::Relocation(ArgumentRelocation {}),
|
// ObjInsArgValue::Opaque(v) => argument_value::Value::Opaque(v.to_string()),
|
||||||
ObjInsArg::BranchDest(dest) => argument::Value::BranchDest(*dest),
|
// }),
|
||||||
}),
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
//
|
||||||
|
// impl Relocation {
|
||||||
impl ArgumentValue {
|
// pub fn new(object: &obj::Object, reloc: &ObjReloc) -> Self {
|
||||||
pub fn new(value: &ObjInsArgValue) -> Self {
|
// Self {
|
||||||
Self {
|
// r#type: match reloc.flags {
|
||||||
value: Some(match value {
|
// object::RelocationFlags::Elf { r_type } => r_type,
|
||||||
ObjInsArgValue::Signed(v) => argument_value::Value::Signed(*v),
|
// object::RelocationFlags::MachO { r_type, .. } => r_type as u32,
|
||||||
ObjInsArgValue::Unsigned(v) => argument_value::Value::Unsigned(*v),
|
// object::RelocationFlags::Coff { typ } => typ as u32,
|
||||||
ObjInsArgValue::Opaque(v) => argument_value::Value::Opaque(v.to_string()),
|
// object::RelocationFlags::Xcoff { r_rtype, .. } => r_rtype as u32,
|
||||||
}),
|
// _ => unreachable!(),
|
||||||
}
|
// },
|
||||||
}
|
// type_name: object.arch.display_reloc(reloc.flags).into_owned(),
|
||||||
}
|
// target: Some(RelocationTarget {
|
||||||
|
// symbol: Some(Symbol::new(&reloc.target)),
|
||||||
impl Relocation {
|
// addend: reloc.addend,
|
||||||
pub fn new(object: &ObjInfo, reloc: &ObjReloc) -> Self {
|
// }),
|
||||||
Self {
|
// }
|
||||||
r#type: match reloc.flags {
|
// }
|
||||||
object::RelocationFlags::Elf { r_type } => r_type,
|
// }
|
||||||
object::RelocationFlags::MachO { r_type, .. } => r_type as u32,
|
//
|
||||||
object::RelocationFlags::Coff { typ } => typ as u32,
|
// impl InstructionDiff {
|
||||||
object::RelocationFlags::Xcoff { r_rtype, .. } => r_rtype as u32,
|
// pub fn new(object: &obj::Object, instruction_diff: &ObjInsDiff) -> Self {
|
||||||
_ => unreachable!(),
|
// Self {
|
||||||
},
|
// instruction: instruction_diff.ins.as_ref().map(|ins| Instruction::new(object, ins)),
|
||||||
type_name: object.arch.display_reloc(reloc.flags).into_owned(),
|
// diff_kind: DiffKind::from(instruction_diff.kind) as i32,
|
||||||
target: Some(RelocationTarget {
|
// branch_from: instruction_diff.branch_from.as_ref().map(InstructionBranchFrom::new),
|
||||||
symbol: Some(Symbol::new(&reloc.target)),
|
// branch_to: instruction_diff.branch_to.as_ref().map(InstructionBranchTo::new),
|
||||||
addend: reloc.addend,
|
// arg_diff: instruction_diff.arg_diff.iter().map(ArgumentDiff::new).collect(),
|
||||||
}),
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
//
|
||||||
|
// impl ArgumentDiff {
|
||||||
impl InstructionDiff {
|
// pub fn new(value: &Option<ObjInsArgDiff>) -> Self {
|
||||||
pub fn new(object: &ObjInfo, instruction_diff: &ObjInsDiff) -> Self {
|
// Self { diff_index: value.as_ref().map(|v| v.idx as u32) }
|
||||||
Self {
|
// }
|
||||||
instruction: instruction_diff.ins.as_ref().map(|ins| Instruction::new(object, ins)),
|
// }
|
||||||
diff_kind: DiffKind::from(instruction_diff.kind) as i32,
|
//
|
||||||
branch_from: instruction_diff.branch_from.as_ref().map(InstructionBranchFrom::new),
|
// impl From<ObjInsDiffKind> for DiffKind {
|
||||||
branch_to: instruction_diff.branch_to.as_ref().map(InstructionBranchTo::new),
|
// fn from(value: ObjInsDiffKind) -> Self {
|
||||||
arg_diff: instruction_diff.arg_diff.iter().map(ArgumentDiff::new).collect(),
|
// match value {
|
||||||
}
|
// ObjInsDiffKind::None => DiffKind::DiffNone,
|
||||||
}
|
// ObjInsDiffKind::OpMismatch => DiffKind::DiffOpMismatch,
|
||||||
}
|
// ObjInsDiffKind::ArgMismatch => DiffKind::DiffArgMismatch,
|
||||||
|
// ObjInsDiffKind::Replace => DiffKind::DiffReplace,
|
||||||
impl ArgumentDiff {
|
// ObjInsDiffKind::Delete => DiffKind::DiffDelete,
|
||||||
pub fn new(value: &Option<ObjInsArgDiff>) -> Self {
|
// ObjInsDiffKind::Insert => DiffKind::DiffInsert,
|
||||||
Self { diff_index: value.as_ref().map(|v| v.idx as u32) }
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
impl From<ObjInsDiffKind> for DiffKind {
|
// impl From<ObjDataDiffKind> for DiffKind {
|
||||||
fn from(value: ObjInsDiffKind) -> Self {
|
// fn from(value: ObjDataDiffKind) -> Self {
|
||||||
match value {
|
// match value {
|
||||||
ObjInsDiffKind::None => DiffKind::DiffNone,
|
// ObjDataDiffKind::None => DiffKind::DiffNone,
|
||||||
ObjInsDiffKind::OpMismatch => DiffKind::DiffOpMismatch,
|
// ObjDataDiffKind::Replace => DiffKind::DiffReplace,
|
||||||
ObjInsDiffKind::ArgMismatch => DiffKind::DiffArgMismatch,
|
// ObjDataDiffKind::Delete => DiffKind::DiffDelete,
|
||||||
ObjInsDiffKind::Replace => DiffKind::DiffReplace,
|
// ObjDataDiffKind::Insert => DiffKind::DiffInsert,
|
||||||
ObjInsDiffKind::Delete => DiffKind::DiffDelete,
|
// }
|
||||||
ObjInsDiffKind::Insert => DiffKind::DiffInsert,
|
// }
|
||||||
}
|
// }
|
||||||
}
|
//
|
||||||
}
|
// impl InstructionBranchFrom {
|
||||||
|
// pub fn new(value: &ObjInsBranchFrom) -> Self {
|
||||||
impl From<ObjDataDiffKind> for DiffKind {
|
// Self {
|
||||||
fn from(value: ObjDataDiffKind) -> Self {
|
// instruction_index: value.ins_idx.iter().map(|&x| x as u32).collect(),
|
||||||
match value {
|
// branch_index: value.branch_idx as u32,
|
||||||
ObjDataDiffKind::None => DiffKind::DiffNone,
|
// }
|
||||||
ObjDataDiffKind::Replace => DiffKind::DiffReplace,
|
// }
|
||||||
ObjDataDiffKind::Delete => DiffKind::DiffDelete,
|
// }
|
||||||
ObjDataDiffKind::Insert => DiffKind::DiffInsert,
|
//
|
||||||
}
|
// impl InstructionBranchTo {
|
||||||
}
|
// pub fn new(value: &ObjInsBranchTo) -> Self {
|
||||||
}
|
// Self { instruction_index: value.ins_idx as u32, branch_index: value.branch_idx as u32 }
|
||||||
|
// }
|
||||||
impl InstructionBranchFrom {
|
// }
|
||||||
pub fn new(value: &ObjInsBranchFrom) -> Self {
|
|
||||||
Self {
|
|
||||||
instruction_index: value.ins_idx.iter().map(|&x| x as u32).collect(),
|
|
||||||
branch_index: value.branch_idx as u32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InstructionBranchTo {
|
|
||||||
pub fn new(value: &ObjInsBranchTo) -> Self {
|
|
||||||
Self { instruction_index: value.ins_idx as u32, branch_index: value.branch_idx as u32 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,428 +1,561 @@
|
|||||||
use alloc::{
|
use alloc::{
|
||||||
collections::BTreeMap,
|
collections::{btree_map, BTreeMap},
|
||||||
string::{String, ToString},
|
string::{String, ToString},
|
||||||
vec,
|
vec,
|
||||||
vec::Vec,
|
vec::Vec,
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, ensure, Result};
|
||||||
use similar::{capture_diff_slices, Algorithm};
|
|
||||||
|
|
||||||
use super::FunctionRelocDiffs;
|
use super::{
|
||||||
use crate::{
|
DiffObjConfig, FunctionRelocDiffs, InstructionArgDiffIndex, InstructionBranchFrom,
|
||||||
arch::ProcessCodeResult,
|
InstructionBranchTo, InstructionDiffKind, InstructionDiffRow, SymbolDiff,
|
||||||
diff::{
|
};
|
||||||
DiffObjConfig, ObjInsArgDiff, ObjInsBranchFrom, ObjInsBranchTo, ObjInsDiff, ObjInsDiffKind,
|
use crate::obj::{
|
||||||
ObjSymbolDiff,
|
InstructionArg, InstructionArgValue, InstructionRef, Object, ResolvedRelocation,
|
||||||
},
|
ScannedInstruction, SymbolFlag, SymbolKind,
|
||||||
obj::{
|
|
||||||
ObjInfo, ObjIns, ObjInsArg, ObjReloc, ObjSection, ObjSymbol, ObjSymbolFlags, ObjSymbolKind,
|
|
||||||
SymbolRef,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn process_code_symbol(
|
pub fn no_diff_code(
|
||||||
obj: &ObjInfo,
|
obj: &Object,
|
||||||
symbol_ref: SymbolRef,
|
symbol_idx: usize,
|
||||||
config: &DiffObjConfig,
|
diff_config: &DiffObjConfig,
|
||||||
) -> Result<ProcessCodeResult> {
|
) -> Result<SymbolDiff> {
|
||||||
let (section, symbol) = obj.section_symbol(symbol_ref);
|
let symbol = &obj.symbols[symbol_idx];
|
||||||
let section = section.ok_or_else(|| anyhow!("Code symbol section not found"))?;
|
let section_index = symbol.section.ok_or_else(|| anyhow!("Missing section for symbol"))?;
|
||||||
let code = §ion.data
|
let section = &obj.sections[section_index];
|
||||||
[symbol.section_address as usize..(symbol.section_address + symbol.size) as usize];
|
let data = section.data_range(symbol.address, symbol.size as usize).ok_or_else(|| {
|
||||||
let mut res = obj.arch.process_code(
|
anyhow!(
|
||||||
|
"Symbol data out of bounds: {:#x}..{:#x}",
|
||||||
symbol.address,
|
symbol.address,
|
||||||
code,
|
symbol.address + symbol.size
|
||||||
section.orig_index,
|
)
|
||||||
§ion.relocations,
|
})?;
|
||||||
§ion.line_info,
|
let ops = obj.arch.scan_instructions(symbol.address, data, section_index, diff_config)?;
|
||||||
config,
|
let mut instruction_rows = Vec::<InstructionDiffRow>::new();
|
||||||
)?;
|
for i in &ops {
|
||||||
|
instruction_rows
|
||||||
for inst in res.insts.iter_mut() {
|
.push(InstructionDiffRow { ins_ref: Some(i.ins_ref), ..Default::default() });
|
||||||
if let Some(reloc) = &mut inst.reloc {
|
|
||||||
if reloc.target.size == 0 && reloc.target.name.is_empty() {
|
|
||||||
// Fake target symbol we added as a placeholder. We need to find the real one.
|
|
||||||
if let Some(real_target) =
|
|
||||||
find_symbol_matching_fake_symbol_in_sections(&reloc.target, &obj.sections)
|
|
||||||
{
|
|
||||||
reloc.addend = (reloc.target.address - real_target.address) as i64;
|
|
||||||
reloc.target = real_target;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
resolve_branches(obj, section_index, &ops, &mut instruction_rows);
|
||||||
|
Ok(SymbolDiff { target_symbol: None, match_percent: None, diff_score: None, instruction_rows })
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(res)
|
const PENALTY_IMM_DIFF: u64 = 1;
|
||||||
}
|
const PENALTY_REG_DIFF: u64 = 5;
|
||||||
|
const PENALTY_REPLACE: u64 = 60;
|
||||||
pub fn no_diff_code(out: &ProcessCodeResult, symbol_ref: SymbolRef) -> Result<ObjSymbolDiff> {
|
const PENALTY_INSERT_DELETE: u64 = 100;
|
||||||
let mut diff = Vec::<ObjInsDiff>::new();
|
|
||||||
for i in &out.insts {
|
|
||||||
diff.push(ObjInsDiff {
|
|
||||||
ins: Some(i.clone()),
|
|
||||||
kind: ObjInsDiffKind::None,
|
|
||||||
..Default::default()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
resolve_branches(&mut diff);
|
|
||||||
Ok(ObjSymbolDiff { symbol_ref, target_symbol: None, instructions: diff, match_percent: None })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn diff_code(
|
pub fn diff_code(
|
||||||
left_obj: &ObjInfo,
|
left_obj: &Object,
|
||||||
right_obj: &ObjInfo,
|
right_obj: &Object,
|
||||||
left_out: &ProcessCodeResult,
|
left_symbol_idx: usize,
|
||||||
right_out: &ProcessCodeResult,
|
right_symbol_idx: usize,
|
||||||
left_symbol_ref: SymbolRef,
|
diff_config: &DiffObjConfig,
|
||||||
right_symbol_ref: SymbolRef,
|
) -> Result<(SymbolDiff, SymbolDiff)> {
|
||||||
config: &DiffObjConfig,
|
let left_symbol = &left_obj.symbols[left_symbol_idx];
|
||||||
) -> Result<(ObjSymbolDiff, ObjSymbolDiff)> {
|
let right_symbol = &right_obj.symbols[right_symbol_idx];
|
||||||
let mut left_diff = Vec::<ObjInsDiff>::new();
|
let left_section = left_symbol
|
||||||
let mut right_diff = Vec::<ObjInsDiff>::new();
|
.section
|
||||||
diff_instructions(&mut left_diff, &mut right_diff, left_out, right_out)?;
|
.and_then(|i| left_obj.sections.get(i))
|
||||||
|
.ok_or_else(|| anyhow!("Missing section for symbol"))?;
|
||||||
|
let right_section = right_symbol
|
||||||
|
.section
|
||||||
|
.and_then(|i| right_obj.sections.get(i))
|
||||||
|
.ok_or_else(|| anyhow!("Missing section for symbol"))?;
|
||||||
|
let left_data = left_section
|
||||||
|
.data_range(left_symbol.address, left_symbol.size as usize)
|
||||||
|
.ok_or_else(|| {
|
||||||
|
anyhow!(
|
||||||
|
"Symbol data out of bounds: {:#x}..{:#x}",
|
||||||
|
left_symbol.address,
|
||||||
|
left_symbol.address + left_symbol.size
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
let right_data = right_section
|
||||||
|
.data_range(right_symbol.address, right_symbol.size as usize)
|
||||||
|
.ok_or_else(|| {
|
||||||
|
anyhow!(
|
||||||
|
"Symbol data out of bounds: {:#x}..{:#x}",
|
||||||
|
right_symbol.address,
|
||||||
|
right_symbol.address + right_symbol.size
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
resolve_branches(&mut left_diff);
|
let left_section_idx = left_symbol.section.unwrap();
|
||||||
resolve_branches(&mut right_diff);
|
let right_section_idx = right_symbol.section.unwrap();
|
||||||
|
let left_ops = left_obj.arch.scan_instructions(
|
||||||
|
left_symbol.address,
|
||||||
|
left_data,
|
||||||
|
left_section_idx,
|
||||||
|
diff_config,
|
||||||
|
)?;
|
||||||
|
let right_ops = left_obj.arch.scan_instructions(
|
||||||
|
right_symbol.address,
|
||||||
|
right_data,
|
||||||
|
right_section_idx,
|
||||||
|
diff_config,
|
||||||
|
)?;
|
||||||
|
let (mut left_rows, mut right_rows) = diff_instructions(&left_ops, &right_ops)?;
|
||||||
|
resolve_branches(left_obj, left_section_idx, &left_ops, &mut left_rows);
|
||||||
|
resolve_branches(right_obj, right_section_idx, &right_ops, &mut right_rows);
|
||||||
|
|
||||||
let mut diff_state = InsDiffState::default();
|
let mut diff_state = InstructionDiffState::default();
|
||||||
for (left, right) in left_diff.iter_mut().zip(right_diff.iter_mut()) {
|
for (left_row, right_row) in left_rows.iter_mut().zip(right_rows.iter_mut()) {
|
||||||
let result = compare_ins(config, left_obj, right_obj, left, right, &mut diff_state)?;
|
let result = diff_instruction(
|
||||||
left.kind = result.kind;
|
left_obj,
|
||||||
right.kind = result.kind;
|
right_obj,
|
||||||
left.arg_diff = result.left_args_diff;
|
left_symbol_idx,
|
||||||
right.arg_diff = result.right_args_diff;
|
right_symbol_idx,
|
||||||
|
left_row.ins_ref.as_ref(),
|
||||||
|
right_row.ins_ref.as_ref(),
|
||||||
|
left_row,
|
||||||
|
right_row,
|
||||||
|
diff_config,
|
||||||
|
&mut diff_state,
|
||||||
|
)?;
|
||||||
|
left_row.kind = result.kind;
|
||||||
|
right_row.kind = result.kind;
|
||||||
|
left_row.arg_diff = result.left_args_diff;
|
||||||
|
right_row.arg_diff = result.right_args_diff;
|
||||||
}
|
}
|
||||||
|
|
||||||
let total = left_out.insts.len().max(right_out.insts.len());
|
let max_score = left_ops.len() as u64 * PENALTY_INSERT_DELETE;
|
||||||
let percent = if diff_state.diff_count >= total {
|
let diff_score = diff_state.diff_score.min(max_score);
|
||||||
0.0
|
let match_percent = if max_score == 0 {
|
||||||
|
100.0
|
||||||
} else {
|
} else {
|
||||||
((total - diff_state.diff_count) as f32 / total as f32) * 100.0
|
((1.0 - (diff_score as f64 / max_score as f64)) * 100.0) as f32
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
ObjSymbolDiff {
|
SymbolDiff {
|
||||||
symbol_ref: left_symbol_ref,
|
target_symbol: Some(right_symbol_idx),
|
||||||
target_symbol: Some(right_symbol_ref),
|
match_percent: Some(match_percent),
|
||||||
instructions: left_diff,
|
diff_score: Some((diff_score, max_score)),
|
||||||
match_percent: Some(percent),
|
instruction_rows: left_rows,
|
||||||
},
|
},
|
||||||
ObjSymbolDiff {
|
SymbolDiff {
|
||||||
symbol_ref: right_symbol_ref,
|
target_symbol: Some(left_symbol_idx),
|
||||||
target_symbol: Some(left_symbol_ref),
|
match_percent: Some(match_percent),
|
||||||
instructions: right_diff,
|
diff_score: Some((diff_score, max_score)),
|
||||||
match_percent: Some(percent),
|
instruction_rows: right_rows,
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn diff_instructions(
|
fn diff_instructions(
|
||||||
left_diff: &mut Vec<ObjInsDiff>,
|
left_insts: &[ScannedInstruction],
|
||||||
right_diff: &mut Vec<ObjInsDiff>,
|
right_insts: &[ScannedInstruction],
|
||||||
left_code: &ProcessCodeResult,
|
) -> Result<(Vec<InstructionDiffRow>, Vec<InstructionDiffRow>)> {
|
||||||
right_code: &ProcessCodeResult,
|
let left_ops = left_insts.iter().map(|i| i.ins_ref.opcode).collect::<Vec<_>>();
|
||||||
) -> Result<()> {
|
let right_ops = right_insts.iter().map(|i| i.ins_ref.opcode).collect::<Vec<_>>();
|
||||||
let ops = capture_diff_slices(Algorithm::Patience, &left_code.ops, &right_code.ops);
|
let ops = similar::capture_diff_slices(similar::Algorithm::Patience, &left_ops, &right_ops);
|
||||||
if ops.is_empty() {
|
if ops.is_empty() {
|
||||||
left_diff.extend(
|
ensure!(left_insts.len() == right_insts.len());
|
||||||
left_code
|
let left_diff = left_insts
|
||||||
.insts
|
|
||||||
.iter()
|
.iter()
|
||||||
.map(|i| ObjInsDiff { ins: Some(i.clone()), ..Default::default() }),
|
.map(|i| InstructionDiffRow { ins_ref: Some(i.ins_ref), ..Default::default() })
|
||||||
);
|
.collect();
|
||||||
right_diff.extend(
|
let right_diff = right_insts
|
||||||
right_code
|
|
||||||
.insts
|
|
||||||
.iter()
|
.iter()
|
||||||
.map(|i| ObjInsDiff { ins: Some(i.clone()), ..Default::default() }),
|
.map(|i| InstructionDiffRow { ins_ref: Some(i.ins_ref), ..Default::default() })
|
||||||
);
|
.collect();
|
||||||
return Ok(());
|
return Ok((left_diff, right_diff));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let row_count = ops
|
||||||
|
.iter()
|
||||||
|
.map(|op| match *op {
|
||||||
|
similar::DiffOp::Equal { len, .. } => len,
|
||||||
|
similar::DiffOp::Delete { old_len, .. } => old_len,
|
||||||
|
similar::DiffOp::Insert { new_len, .. } => new_len,
|
||||||
|
similar::DiffOp::Replace { old_len, new_len, .. } => old_len.max(new_len),
|
||||||
|
})
|
||||||
|
.sum();
|
||||||
|
let mut left_diff = Vec::<InstructionDiffRow>::with_capacity(row_count);
|
||||||
|
let mut right_diff = Vec::<InstructionDiffRow>::with_capacity(row_count);
|
||||||
for op in ops {
|
for op in ops {
|
||||||
let (_tag, left_range, right_range) = op.as_tag_tuple();
|
let (_tag, left_range, right_range) = op.as_tag_tuple();
|
||||||
let len = left_range.len().max(right_range.len());
|
let len = left_range.len().max(right_range.len());
|
||||||
left_diff.extend(
|
left_diff.extend(left_range.clone().map(|i| InstructionDiffRow {
|
||||||
left_code.insts[left_range.clone()]
|
ins_ref: Some(left_insts[i].ins_ref),
|
||||||
.iter()
|
..Default::default()
|
||||||
.map(|i| ObjInsDiff { ins: Some(i.clone()), ..Default::default() }),
|
}));
|
||||||
);
|
right_diff.extend(right_range.clone().map(|i| InstructionDiffRow {
|
||||||
right_diff.extend(
|
ins_ref: Some(right_insts[i].ins_ref),
|
||||||
right_code.insts[right_range.clone()]
|
..Default::default()
|
||||||
.iter()
|
}));
|
||||||
.map(|i| ObjInsDiff { ins: Some(i.clone()), ..Default::default() }),
|
|
||||||
);
|
|
||||||
if left_range.len() < len {
|
if left_range.len() < len {
|
||||||
left_diff.extend((left_range.len()..len).map(|_| ObjInsDiff::default()));
|
left_diff.extend((left_range.len()..len).map(|_| InstructionDiffRow::default()));
|
||||||
}
|
}
|
||||||
if right_range.len() < len {
|
if right_range.len() < len {
|
||||||
right_diff.extend((right_range.len()..len).map(|_| ObjInsDiff::default()));
|
right_diff.extend((right_range.len()..len).map(|_| InstructionDiffRow::default()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok((left_diff, right_diff))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn arg_to_string(arg: &InstructionArg, reloc: Option<&ResolvedRelocation>) -> String {
|
||||||
|
match arg {
|
||||||
|
InstructionArg::Value(arg) => arg.to_string(),
|
||||||
|
InstructionArg::Reloc => {
|
||||||
|
reloc.as_ref().map_or_else(|| "<unknown>".to_string(), |r| r.symbol.name.clone())
|
||||||
|
}
|
||||||
|
InstructionArg::BranchDest(arg) => arg.to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
fn resolve_branches(
|
||||||
}
|
obj: &Object,
|
||||||
|
section_index: usize,
|
||||||
fn resolve_branches(vec: &mut [ObjInsDiff]) {
|
ops: &[ScannedInstruction],
|
||||||
let mut branch_idx = 0usize;
|
rows: &mut [InstructionDiffRow],
|
||||||
|
) {
|
||||||
|
let section = &obj.sections[section_index];
|
||||||
|
let mut branch_idx = 0u32;
|
||||||
// Map addresses to indices
|
// Map addresses to indices
|
||||||
let mut addr_map = BTreeMap::<u64, usize>::new();
|
let mut addr_map = BTreeMap::<u64, u32>::new();
|
||||||
for (i, ins_diff) in vec.iter().enumerate() {
|
for (i, ins_diff) in rows.iter().enumerate() {
|
||||||
if let Some(ins) = &ins_diff.ins {
|
if let Some(ins) = ins_diff.ins_ref {
|
||||||
addr_map.insert(ins.address, i);
|
addr_map.insert(ins.address, i as u32);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Generate branches
|
// Generate branches
|
||||||
let mut branches = BTreeMap::<usize, ObjInsBranchFrom>::new();
|
let mut branches = BTreeMap::<u32, InstructionBranchFrom>::new();
|
||||||
for (i, ins_diff) in vec.iter_mut().enumerate() {
|
for ((i, ins_diff), ins) in
|
||||||
if let Some(ins) = &ins_diff.ins {
|
rows.iter_mut().enumerate().filter(|(_, row)| row.ins_ref.is_some()).zip(ops)
|
||||||
if let Some(ins_idx) = ins.branch_dest.and_then(|a| addr_map.get(&a)) {
|
{
|
||||||
if let Some(branch) = branches.get_mut(ins_idx) {
|
let branch_dest = if let Some(resolved) = section.relocation_at(ins.ins_ref.address, obj) {
|
||||||
ins_diff.branch_to =
|
if resolved.symbol.section == Some(section_index) {
|
||||||
Some(ObjInsBranchTo { ins_idx: *ins_idx, branch_idx: branch.branch_idx });
|
// If the relocation target is in the same section, use it as the branch destination
|
||||||
branch.ins_idx.push(i);
|
resolved.symbol.address.checked_add_signed(resolved.relocation.addend)
|
||||||
} else {
|
} else {
|
||||||
ins_diff.branch_to = Some(ObjInsBranchTo { ins_idx: *ins_idx, branch_idx });
|
None
|
||||||
branches.insert(*ins_idx, ObjInsBranchFrom { ins_idx: vec![i], branch_idx });
|
}
|
||||||
|
} else {
|
||||||
|
ins.branch_dest
|
||||||
|
};
|
||||||
|
if let Some(ins_idx) = branch_dest.and_then(|a| addr_map.get(&a).copied()) {
|
||||||
|
match branches.entry(ins_idx) {
|
||||||
|
btree_map::Entry::Vacant(e) => {
|
||||||
|
ins_diff.branch_to = Some(InstructionBranchTo { ins_idx, branch_idx });
|
||||||
|
e.insert(InstructionBranchFrom { ins_idx: vec![i as u32], branch_idx });
|
||||||
branch_idx += 1;
|
branch_idx += 1;
|
||||||
}
|
}
|
||||||
|
btree_map::Entry::Occupied(e) => {
|
||||||
|
let branch = e.into_mut();
|
||||||
|
ins_diff.branch_to =
|
||||||
|
Some(InstructionBranchTo { ins_idx, branch_idx: branch.branch_idx });
|
||||||
|
branch.ins_idx.push(i as u32);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Store branch from
|
// Store branch from
|
||||||
for (i, branch) in branches {
|
for (i, branch) in branches {
|
||||||
vec[i].branch_from = Some(branch);
|
rows[i as usize].branch_from = Some(branch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn address_eq(left: &ObjReloc, right: &ObjReloc) -> bool {
|
pub(crate) fn address_eq(left: &ResolvedRelocation, right: &ResolvedRelocation) -> bool {
|
||||||
if right.target.size == 0 && left.target.size != 0 {
|
if right.symbol.size == 0 && left.symbol.size != 0 {
|
||||||
// The base relocation is against a pool but the target relocation isn't.
|
// The base relocation is against a pool but the target relocation isn't.
|
||||||
// This can happen in rare cases where the compiler will generate a pool+addend relocation
|
// This can happen in rare cases where the compiler will generate a pool+addend relocation
|
||||||
// in the base's data, but the one detected in the target is direct with no addend.
|
// in the base's data, but the one detected in the target is direct with no addend.
|
||||||
// Just check that the final address is the same so these count as a match.
|
// Just check that the final address is the same so these count as a match.
|
||||||
left.target.address as i64 + left.addend == right.target.address as i64 + right.addend
|
left.symbol.address as i64 + left.relocation.addend
|
||||||
|
== right.symbol.address as i64 + right.relocation.addend
|
||||||
} else {
|
} else {
|
||||||
// But otherwise, if the compiler isn't using a pool, we're more strict and check that the
|
// But otherwise, if the compiler isn't using a pool, we're more strict and check that the
|
||||||
// target symbol address and relocation addend both match exactly.
|
// target symbol address and relocation addend both match exactly.
|
||||||
left.target.address == right.target.address && left.addend == right.addend
|
left.symbol.address == right.symbol.address
|
||||||
|
&& left.relocation.addend == right.relocation.addend
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn section_name_eq(
|
pub(crate) fn section_name_eq(
|
||||||
left_obj: &ObjInfo,
|
left_obj: &Object,
|
||||||
right_obj: &ObjInfo,
|
right_obj: &Object,
|
||||||
left_orig_section_index: usize,
|
left_section_index: usize,
|
||||||
right_orig_section_index: usize,
|
right_section_index: usize,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let Some(left_section) =
|
left_obj.sections.get(left_section_index).is_some_and(|left_section| {
|
||||||
left_obj.sections.iter().find(|s| s.orig_index == left_orig_section_index)
|
right_obj
|
||||||
else {
|
.sections
|
||||||
return false;
|
.get(right_section_index)
|
||||||
};
|
.is_some_and(|right_section| left_section.name == right_section.name)
|
||||||
let Some(right_section) =
|
})
|
||||||
right_obj.sections.iter().find(|s| s.orig_index == right_orig_section_index)
|
|
||||||
else {
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
left_section.name == right_section.name
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reloc_eq(
|
fn reloc_eq(
|
||||||
config: &DiffObjConfig,
|
left_obj: &Object,
|
||||||
left_obj: &ObjInfo,
|
right_obj: &Object,
|
||||||
right_obj: &ObjInfo,
|
left_reloc: Option<ResolvedRelocation>,
|
||||||
left_ins: Option<&ObjIns>,
|
right_reloc: Option<ResolvedRelocation>,
|
||||||
right_ins: Option<&ObjIns>,
|
diff_config: &DiffObjConfig,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let (Some(left_ins), Some(right_ins)) = (left_ins, right_ins) else {
|
let relax_reloc_diffs = diff_config.function_reloc_diffs == FunctionRelocDiffs::None;
|
||||||
return false;
|
let (left_reloc, right_reloc) = match (left_reloc, right_reloc) {
|
||||||
|
(Some(left_reloc), Some(right_reloc)) => (left_reloc, right_reloc),
|
||||||
|
// If relocations are relaxed, match if left is missing a reloc
|
||||||
|
(None, Some(_)) => return relax_reloc_diffs,
|
||||||
|
(None, None) => return true,
|
||||||
|
_ => return false,
|
||||||
};
|
};
|
||||||
let (Some(left), Some(right)) = (&left_ins.reloc, &right_ins.reloc) else {
|
if left_reloc.relocation.flags != right_reloc.relocation.flags {
|
||||||
return false;
|
|
||||||
};
|
|
||||||
if left.flags != right.flags {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if config.function_reloc_diffs == FunctionRelocDiffs::None {
|
if relax_reloc_diffs {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
let symbol_name_addend_matches =
|
let symbol_name_addend_matches = left_reloc.symbol.name == right_reloc.symbol.name
|
||||||
left.target.name == right.target.name && left.addend == right.addend;
|
&& left_reloc.relocation.addend == right_reloc.relocation.addend;
|
||||||
match (&left.target.orig_section_index, &right.target.orig_section_index) {
|
match (&left_reloc.symbol.section, &right_reloc.symbol.section) {
|
||||||
(Some(sl), Some(sr)) => {
|
(Some(sl), Some(sr)) => {
|
||||||
// Match if section and name+addend or address match
|
// Match if section and name or address match
|
||||||
section_name_eq(left_obj, right_obj, *sl, *sr)
|
section_name_eq(left_obj, right_obj, *sl, *sr)
|
||||||
&& (config.function_reloc_diffs == FunctionRelocDiffs::DataValue
|
&& (diff_config.function_reloc_diffs == FunctionRelocDiffs::DataValue
|
||||||
|| symbol_name_addend_matches
|
|| symbol_name_addend_matches
|
||||||
|| address_eq(left, right))
|
|| address_eq(&left_reloc, &right_reloc))
|
||||||
&& (config.function_reloc_diffs == FunctionRelocDiffs::NameAddress
|
&& (
|
||||||
|| left.target.kind != ObjSymbolKind::Object
|
diff_config.function_reloc_diffs == FunctionRelocDiffs::NameAddress
|
||||||
|| left_obj.arch.display_ins_data_labels(left_ins)
|
|| left_reloc.symbol.kind != SymbolKind::Object
|
||||||
== left_obj.arch.display_ins_data_labels(right_ins))
|
// TODO
|
||||||
|
// || left_obj.arch.display_ins_data_labels(left_ins)
|
||||||
|
// == left_obj.arch.display_ins_data_labels(right_ins))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
(Some(_), None) => false,
|
(Some(_), None) => false,
|
||||||
(None, Some(_)) => {
|
(None, Some(_)) => {
|
||||||
// Match if possibly stripped weak symbol
|
// Match if possibly stripped weak symbol
|
||||||
symbol_name_addend_matches && right.target.flags.0.contains(ObjSymbolFlags::Weak)
|
symbol_name_addend_matches && right_reloc.symbol.flags.contains(SymbolFlag::Weak)
|
||||||
}
|
}
|
||||||
(None, None) => symbol_name_addend_matches,
|
(None, None) => symbol_name_addend_matches,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn arg_eq(
|
fn arg_eq(
|
||||||
config: &DiffObjConfig,
|
left_obj: &Object,
|
||||||
left_obj: &ObjInfo,
|
right_obj: &Object,
|
||||||
right_obj: &ObjInfo,
|
left_row: &InstructionDiffRow,
|
||||||
left: &ObjInsArg,
|
right_row: &InstructionDiffRow,
|
||||||
right: &ObjInsArg,
|
left_arg: &InstructionArg,
|
||||||
left_diff: &ObjInsDiff,
|
right_arg: &InstructionArg,
|
||||||
right_diff: &ObjInsDiff,
|
left_reloc: Option<ResolvedRelocation>,
|
||||||
|
right_reloc: Option<ResolvedRelocation>,
|
||||||
|
diff_config: &DiffObjConfig,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
match left {
|
match left_arg {
|
||||||
ObjInsArg::PlainText(l) => match right {
|
InstructionArg::Value(l) => match right_arg {
|
||||||
ObjInsArg::PlainText(r) => l == r,
|
InstructionArg::Value(r) => l.loose_eq(r),
|
||||||
_ => false,
|
|
||||||
},
|
|
||||||
ObjInsArg::Arg(l) => match right {
|
|
||||||
ObjInsArg::Arg(r) => l.loose_eq(r),
|
|
||||||
// If relocations are relaxed, match if left is a constant and right is a reloc
|
// 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
|
// Useful for instances where the target object is created without relocations
|
||||||
ObjInsArg::Reloc => config.function_reloc_diffs == FunctionRelocDiffs::None,
|
InstructionArg::Reloc => diff_config.function_reloc_diffs == FunctionRelocDiffs::None,
|
||||||
_ => false,
|
_ => false,
|
||||||
},
|
},
|
||||||
ObjInsArg::Reloc => {
|
InstructionArg::Reloc => {
|
||||||
matches!(right, ObjInsArg::Reloc)
|
matches!(right_arg, InstructionArg::Reloc)
|
||||||
&& reloc_eq(
|
&& reloc_eq(left_obj, right_obj, left_reloc, right_reloc, diff_config)
|
||||||
config,
|
}
|
||||||
|
InstructionArg::BranchDest(_) => match right_arg {
|
||||||
|
// Compare dest instruction idx after diffing
|
||||||
|
InstructionArg::BranchDest(_) => {
|
||||||
|
left_row.branch_to.as_ref().map(|b| b.ins_idx)
|
||||||
|
== right_row.branch_to.as_ref().map(|b| b.ins_idx)
|
||||||
|
}
|
||||||
|
// If relocations are relaxed, match if left is a constant and right is a reloc
|
||||||
|
// Useful for instances where the target object is created without relocations
|
||||||
|
InstructionArg::Reloc => diff_config.function_reloc_diffs == FunctionRelocDiffs::None,
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct InstructionDiffState {
|
||||||
|
diff_score: u64,
|
||||||
|
left_arg_idx: u32,
|
||||||
|
right_arg_idx: u32,
|
||||||
|
left_args_idx: BTreeMap<String, u32>,
|
||||||
|
right_args_idx: BTreeMap<String, u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct InstructionDiffResult {
|
||||||
|
kind: InstructionDiffKind,
|
||||||
|
left_args_diff: Vec<InstructionArgDiffIndex>,
|
||||||
|
right_args_diff: Vec<InstructionArgDiffIndex>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InstructionDiffResult {
|
||||||
|
#[inline]
|
||||||
|
const fn new(kind: InstructionDiffKind) -> Self {
|
||||||
|
Self { kind, left_args_diff: Vec::new(), right_args_diff: Vec::new() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn diff_instruction(
|
||||||
|
left_obj: &Object,
|
||||||
|
right_obj: &Object,
|
||||||
|
left_symbol_idx: usize,
|
||||||
|
right_symbol_idx: usize,
|
||||||
|
l: Option<&InstructionRef>,
|
||||||
|
r: Option<&InstructionRef>,
|
||||||
|
left_row: &InstructionDiffRow,
|
||||||
|
right_row: &InstructionDiffRow,
|
||||||
|
diff_config: &DiffObjConfig,
|
||||||
|
state: &mut InstructionDiffState,
|
||||||
|
) -> Result<InstructionDiffResult> {
|
||||||
|
let (l, r) = match (l, r) {
|
||||||
|
(Some(l), Some(r)) => (l, r),
|
||||||
|
(Some(_), None) => {
|
||||||
|
state.diff_score += PENALTY_INSERT_DELETE;
|
||||||
|
return Ok(InstructionDiffResult::new(InstructionDiffKind::Delete));
|
||||||
|
}
|
||||||
|
(None, Some(_)) => {
|
||||||
|
state.diff_score += PENALTY_INSERT_DELETE;
|
||||||
|
return Ok(InstructionDiffResult::new(InstructionDiffKind::Insert));
|
||||||
|
}
|
||||||
|
(None, None) => return Ok(InstructionDiffResult::new(InstructionDiffKind::None)),
|
||||||
|
};
|
||||||
|
|
||||||
|
// If opcodes don't match, replace
|
||||||
|
if l.opcode != r.opcode {
|
||||||
|
state.diff_score += PENALTY_REPLACE;
|
||||||
|
return Ok(InstructionDiffResult::new(InstructionDiffKind::Replace));
|
||||||
|
}
|
||||||
|
|
||||||
|
let left_symbol = &left_obj.symbols[left_symbol_idx];
|
||||||
|
let right_symbol = &right_obj.symbols[right_symbol_idx];
|
||||||
|
let left_section = left_symbol
|
||||||
|
.section
|
||||||
|
.and_then(|i| left_obj.sections.get(i))
|
||||||
|
.ok_or_else(|| anyhow!("Missing section for symbol"))?;
|
||||||
|
let right_section = right_symbol
|
||||||
|
.section
|
||||||
|
.and_then(|i| right_obj.sections.get(i))
|
||||||
|
.ok_or_else(|| anyhow!("Missing section for symbol"))?;
|
||||||
|
|
||||||
|
// Resolve relocations
|
||||||
|
let left_reloc = left_section.relocation_at(l.address, left_obj);
|
||||||
|
let right_reloc = right_section.relocation_at(r.address, right_obj);
|
||||||
|
|
||||||
|
// Compare instruction data
|
||||||
|
let left_data = left_section.data_range(l.address, l.size as usize).ok_or_else(|| {
|
||||||
|
anyhow!(
|
||||||
|
"Instruction data out of bounds: {:#x}..{:#x}",
|
||||||
|
l.address,
|
||||||
|
l.address + l.size as u64
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
let right_data = right_section.data_range(r.address, r.size as usize).ok_or_else(|| {
|
||||||
|
anyhow!(
|
||||||
|
"Instruction data out of bounds: {:#x}..{:#x}",
|
||||||
|
r.address,
|
||||||
|
r.address + r.size as u64
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
if left_data != right_data {
|
||||||
|
// If data doesn't match, process instructions and compare args
|
||||||
|
let left_ins = left_obj.arch.process_instruction(
|
||||||
|
*l,
|
||||||
|
left_data,
|
||||||
|
left_reloc,
|
||||||
|
left_symbol.address..left_symbol.address + left_symbol.size,
|
||||||
|
left_symbol.section.unwrap(),
|
||||||
|
diff_config,
|
||||||
|
)?;
|
||||||
|
let right_ins = left_obj.arch.process_instruction(
|
||||||
|
*r,
|
||||||
|
right_data,
|
||||||
|
right_reloc,
|
||||||
|
right_symbol.address..right_symbol.address + right_symbol.size,
|
||||||
|
right_symbol.section.unwrap(),
|
||||||
|
diff_config,
|
||||||
|
)?;
|
||||||
|
if left_ins.args.len() != right_ins.args.len() {
|
||||||
|
state.diff_score += PENALTY_REPLACE;
|
||||||
|
return Ok(InstructionDiffResult::new(InstructionDiffKind::Replace));
|
||||||
|
}
|
||||||
|
let mut result = InstructionDiffResult::new(InstructionDiffKind::None);
|
||||||
|
if left_ins.mnemonic != right_ins.mnemonic {
|
||||||
|
state.diff_score += PENALTY_REG_DIFF;
|
||||||
|
result.kind = InstructionDiffKind::OpMismatch;
|
||||||
|
}
|
||||||
|
for (a, b) in left_ins.args.iter().zip(right_ins.args.iter()) {
|
||||||
|
if arg_eq(
|
||||||
left_obj,
|
left_obj,
|
||||||
right_obj,
|
right_obj,
|
||||||
left_diff.ins.as_ref(),
|
left_row,
|
||||||
right_diff.ins.as_ref(),
|
right_row,
|
||||||
)
|
a,
|
||||||
|
b,
|
||||||
|
left_reloc,
|
||||||
|
right_reloc,
|
||||||
|
diff_config,
|
||||||
|
) {
|
||||||
|
result.left_args_diff.push(InstructionArgDiffIndex::NONE);
|
||||||
|
result.right_args_diff.push(InstructionArgDiffIndex::NONE);
|
||||||
|
} else {
|
||||||
|
state.diff_score += if let InstructionArg::Value(
|
||||||
|
InstructionArgValue::Signed(_) | InstructionArgValue::Unsigned(_),
|
||||||
|
) = a
|
||||||
|
{
|
||||||
|
PENALTY_IMM_DIFF
|
||||||
|
} else {
|
||||||
|
PENALTY_REG_DIFF
|
||||||
|
};
|
||||||
|
if result.kind == InstructionDiffKind::None {
|
||||||
|
result.kind = InstructionDiffKind::ArgMismatch;
|
||||||
}
|
}
|
||||||
ObjInsArg::BranchDest(_) => match right {
|
let a_str = arg_to_string(a, left_reloc.as_ref());
|
||||||
// Compare dest instruction idx after diffing
|
let a_diff = match state.left_args_idx.entry(a_str) {
|
||||||
ObjInsArg::BranchDest(_) => {
|
btree_map::Entry::Vacant(e) => {
|
||||||
left_diff.branch_to.as_ref().map(|b| b.ins_idx)
|
let idx = state.left_arg_idx;
|
||||||
== right_diff.branch_to.as_ref().map(|b| b.ins_idx)
|
state.left_arg_idx = idx + 1;
|
||||||
|
e.insert(idx);
|
||||||
|
idx
|
||||||
}
|
}
|
||||||
// If relocations are relaxed, match if left is a constant and right is a reloc
|
btree_map::Entry::Occupied(e) => *e.get(),
|
||||||
// Useful for instances where the target object is created without relocations
|
};
|
||||||
ObjInsArg::Reloc => config.function_reloc_diffs == FunctionRelocDiffs::None,
|
let b_str = arg_to_string(b, right_reloc.as_ref());
|
||||||
_ => false,
|
let b_diff = match state.right_args_idx.entry(b_str) {
|
||||||
},
|
btree_map::Entry::Vacant(e) => {
|
||||||
|
let idx = state.right_arg_idx;
|
||||||
|
state.right_arg_idx = idx + 1;
|
||||||
|
e.insert(idx);
|
||||||
|
idx
|
||||||
|
}
|
||||||
|
btree_map::Entry::Occupied(e) => *e.get(),
|
||||||
|
};
|
||||||
|
result.left_args_diff.push(InstructionArgDiffIndex::new(a_diff));
|
||||||
|
result.right_args_diff.push(InstructionArgDiffIndex::new(b_diff));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
struct InsDiffState {
|
|
||||||
diff_count: usize,
|
|
||||||
left_arg_idx: usize,
|
|
||||||
right_arg_idx: usize,
|
|
||||||
left_args_idx: BTreeMap<String, usize>,
|
|
||||||
right_args_idx: BTreeMap<String, usize>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
struct InsDiffResult {
|
|
||||||
kind: ObjInsDiffKind,
|
|
||||||
left_args_diff: Vec<Option<ObjInsArgDiff>>,
|
|
||||||
right_args_diff: Vec<Option<ObjInsArgDiff>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn compare_ins(
|
|
||||||
config: &DiffObjConfig,
|
|
||||||
left_obj: &ObjInfo,
|
|
||||||
right_obj: &ObjInfo,
|
|
||||||
left: &ObjInsDiff,
|
|
||||||
right: &ObjInsDiff,
|
|
||||||
state: &mut InsDiffState,
|
|
||||||
) -> Result<InsDiffResult> {
|
|
||||||
let mut result = InsDiffResult::default();
|
|
||||||
if let (Some(left_ins), Some(right_ins)) = (&left.ins, &right.ins) {
|
|
||||||
// Count only non-PlainText args
|
|
||||||
let left_args_count = left_ins.iter_args().count();
|
|
||||||
let right_args_count = right_ins.iter_args().count();
|
|
||||||
if left_args_count != right_args_count || left_ins.op != right_ins.op {
|
|
||||||
// Totally different op
|
|
||||||
result.kind = ObjInsDiffKind::Replace;
|
|
||||||
state.diff_count += 1;
|
|
||||||
return Ok(result);
|
return Ok(result);
|
||||||
}
|
}
|
||||||
if left_ins.mnemonic != right_ins.mnemonic {
|
|
||||||
// Same op but different mnemonic, still cmp args
|
// Compare relocations
|
||||||
result.kind = ObjInsDiffKind::OpMismatch;
|
if !reloc_eq(left_obj, right_obj, left_reloc, right_reloc, diff_config) {
|
||||||
state.diff_count += 1;
|
state.diff_score += PENALTY_REG_DIFF;
|
||||||
}
|
return Ok(InstructionDiffResult::new(InstructionDiffKind::ArgMismatch));
|
||||||
for (a, b) in left_ins.iter_args().zip(right_ins.iter_args()) {
|
|
||||||
if arg_eq(config, left_obj, right_obj, a, b, left, right) {
|
|
||||||
result.left_args_diff.push(None);
|
|
||||||
result.right_args_diff.push(None);
|
|
||||||
} else {
|
|
||||||
if result.kind == ObjInsDiffKind::None {
|
|
||||||
result.kind = ObjInsDiffKind::ArgMismatch;
|
|
||||||
state.diff_count += 1;
|
|
||||||
}
|
|
||||||
let a_str = match a {
|
|
||||||
ObjInsArg::PlainText(arg) => arg.to_string(),
|
|
||||||
ObjInsArg::Arg(arg) => arg.to_string(),
|
|
||||||
ObjInsArg::Reloc => left_ins
|
|
||||||
.reloc
|
|
||||||
.as_ref()
|
|
||||||
.map_or_else(|| "<unknown>".to_string(), |r| r.target.name.clone()),
|
|
||||||
ObjInsArg::BranchDest(arg) => arg.to_string(),
|
|
||||||
};
|
|
||||||
let a_diff = if let Some(idx) = state.left_args_idx.get(&a_str) {
|
|
||||||
ObjInsArgDiff { idx: *idx }
|
|
||||||
} else {
|
|
||||||
let idx = state.left_arg_idx;
|
|
||||||
state.left_args_idx.insert(a_str, idx);
|
|
||||||
state.left_arg_idx += 1;
|
|
||||||
ObjInsArgDiff { idx }
|
|
||||||
};
|
|
||||||
let b_str = match b {
|
|
||||||
ObjInsArg::PlainText(arg) => arg.to_string(),
|
|
||||||
ObjInsArg::Arg(arg) => arg.to_string(),
|
|
||||||
ObjInsArg::Reloc => right_ins
|
|
||||||
.reloc
|
|
||||||
.as_ref()
|
|
||||||
.map_or_else(|| "<unknown>".to_string(), |r| r.target.name.clone()),
|
|
||||||
ObjInsArg::BranchDest(arg) => arg.to_string(),
|
|
||||||
};
|
|
||||||
let b_diff = if let Some(idx) = state.right_args_idx.get(&b_str) {
|
|
||||||
ObjInsArgDiff { idx: *idx }
|
|
||||||
} else {
|
|
||||||
let idx = state.right_arg_idx;
|
|
||||||
state.right_args_idx.insert(b_str, idx);
|
|
||||||
state.right_arg_idx += 1;
|
|
||||||
ObjInsArgDiff { idx }
|
|
||||||
};
|
|
||||||
result.left_args_diff.push(Some(a_diff));
|
|
||||||
result.right_args_diff.push(Some(b_diff));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if left.ins.is_some() {
|
|
||||||
result.kind = ObjInsDiffKind::Delete;
|
|
||||||
state.diff_count += 1;
|
|
||||||
} else {
|
|
||||||
result.kind = ObjInsDiffKind::Insert;
|
|
||||||
state.diff_count += 1;
|
|
||||||
}
|
|
||||||
Ok(result)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_symbol_matching_fake_symbol_in_sections(
|
Ok(InstructionDiffResult::new(InstructionDiffKind::None))
|
||||||
fake_symbol: &ObjSymbol,
|
|
||||||
sections: &[ObjSection],
|
|
||||||
) -> Option<ObjSymbol> {
|
|
||||||
let orig_section_index = fake_symbol.orig_section_index?;
|
|
||||||
let section = sections.iter().find(|s| s.orig_index == orig_section_index)?;
|
|
||||||
let real_symbol = section
|
|
||||||
.symbols
|
|
||||||
.iter()
|
|
||||||
.find(|s| s.size > 0 && (s.address..s.address + s.size).contains(&fake_symbol.address))?;
|
|
||||||
Some(real_symbol.clone())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
// fn find_symbol_matching_fake_symbol_in_sections(
|
||||||
|
// fake_symbol: &ObjSymbol,
|
||||||
|
// sections: &[ObjSection],
|
||||||
|
// ) -> Option<ObjSymbol> {
|
||||||
|
// let orig_section_index = fake_symbol.orig_section_index?;
|
||||||
|
// let section = sections.iter().find(|s| s.orig_index == orig_section_index)?;
|
||||||
|
// let real_symbol = section
|
||||||
|
// .symbols
|
||||||
|
// .iter()
|
||||||
|
// .find(|s| s.size > 0 && (s.address..s.address + s.size).contains(&fake_symbol.address))?;
|
||||||
|
// Some(real_symbol.clone())
|
||||||
|
// }
|
||||||
|
@ -4,119 +4,130 @@ use core::{cmp::Ordering, ops::Range};
|
|||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use similar::{capture_diff_slices, get_diff_ratio, Algorithm};
|
use similar::{capture_diff_slices, get_diff_ratio, Algorithm};
|
||||||
|
|
||||||
use super::code::{address_eq, section_name_eq};
|
use super::{
|
||||||
use crate::{
|
code::{address_eq, section_name_eq},
|
||||||
diff::{ObjDataDiff, ObjDataDiffKind, ObjDataRelocDiff, ObjSectionDiff, ObjSymbolDiff},
|
DataDiff, DataDiffKind, DataRelocationDiff, ObjectDiff, SectionDiff, SymbolDiff,
|
||||||
obj::{ObjInfo, ObjReloc, ObjSection, ObjSymbolFlags, SymbolRef},
|
|
||||||
};
|
};
|
||||||
|
use crate::obj::{Object, Relocation, ResolvedRelocation, SymbolFlag, SymbolKind};
|
||||||
|
|
||||||
pub fn diff_bss_symbol(
|
pub fn diff_bss_symbol(
|
||||||
left_obj: &ObjInfo,
|
left_obj: &Object,
|
||||||
right_obj: &ObjInfo,
|
right_obj: &Object,
|
||||||
left_symbol_ref: SymbolRef,
|
left_symbol_ref: usize,
|
||||||
right_symbol_ref: SymbolRef,
|
right_symbol_ref: usize,
|
||||||
) -> Result<(ObjSymbolDiff, ObjSymbolDiff)> {
|
) -> Result<(SymbolDiff, SymbolDiff)> {
|
||||||
let (_, left_symbol) = left_obj.section_symbol(left_symbol_ref);
|
let left_symbol = &left_obj.symbols[left_symbol_ref];
|
||||||
let (_, right_symbol) = right_obj.section_symbol(right_symbol_ref);
|
let right_symbol = &right_obj.symbols[right_symbol_ref];
|
||||||
let percent = if left_symbol.size == right_symbol.size { 100.0 } else { 50.0 };
|
let percent = if left_symbol.size == right_symbol.size { 100.0 } else { 50.0 };
|
||||||
Ok((
|
Ok((
|
||||||
ObjSymbolDiff {
|
SymbolDiff {
|
||||||
symbol_ref: left_symbol_ref,
|
|
||||||
target_symbol: Some(right_symbol_ref),
|
target_symbol: Some(right_symbol_ref),
|
||||||
instructions: vec![],
|
|
||||||
match_percent: Some(percent),
|
match_percent: Some(percent),
|
||||||
|
diff_score: None,
|
||||||
|
instruction_rows: vec![],
|
||||||
},
|
},
|
||||||
ObjSymbolDiff {
|
SymbolDiff {
|
||||||
symbol_ref: right_symbol_ref,
|
|
||||||
target_symbol: Some(left_symbol_ref),
|
target_symbol: Some(left_symbol_ref),
|
||||||
instructions: vec![],
|
|
||||||
match_percent: Some(percent),
|
match_percent: Some(percent),
|
||||||
|
diff_score: None,
|
||||||
|
instruction_rows: vec![],
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn no_diff_symbol(_obj: &ObjInfo, symbol_ref: SymbolRef) -> ObjSymbolDiff {
|
fn reloc_eq(
|
||||||
ObjSymbolDiff { symbol_ref, target_symbol: None, instructions: vec![], match_percent: None }
|
left_obj: &Object,
|
||||||
}
|
right_obj: &Object,
|
||||||
|
left: &ResolvedRelocation,
|
||||||
fn reloc_eq(left_obj: &ObjInfo, right_obj: &ObjInfo, left: &ObjReloc, right: &ObjReloc) -> bool {
|
right: &ResolvedRelocation,
|
||||||
if left.flags != right.flags {
|
) -> bool {
|
||||||
|
if left.relocation.flags != right.relocation.flags {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let symbol_name_addend_matches =
|
let symbol_name_addend_matches =
|
||||||
left.target.name == right.target.name && left.addend == right.addend;
|
left.symbol.name == right.symbol.name && left.relocation.addend == right.relocation.addend;
|
||||||
match (&left.target.orig_section_index, &right.target.orig_section_index) {
|
match (left.symbol.section, right.symbol.section) {
|
||||||
(Some(sl), Some(sr)) => {
|
(Some(sl), Some(sr)) => {
|
||||||
// Match if section and name+addend or address match
|
// Match if section and name+addend or address match
|
||||||
section_name_eq(left_obj, right_obj, *sl, *sr)
|
section_name_eq(left_obj, right_obj, sl, sr)
|
||||||
&& (symbol_name_addend_matches || address_eq(left, right))
|
&& (symbol_name_addend_matches || address_eq(left, right))
|
||||||
}
|
}
|
||||||
(Some(_), None) => false,
|
(Some(_), None) => false,
|
||||||
(None, Some(_)) => {
|
(None, Some(_)) => {
|
||||||
// Match if possibly stripped weak symbol
|
// Match if possibly stripped weak symbol
|
||||||
symbol_name_addend_matches && right.target.flags.0.contains(ObjSymbolFlags::Weak)
|
symbol_name_addend_matches && right.symbol.flags.contains(SymbolFlag::Weak)
|
||||||
}
|
}
|
||||||
(None, None) => symbol_name_addend_matches,
|
(None, None) => symbol_name_addend_matches,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn resolve_relocation<'obj>(
|
||||||
|
obj: &'obj Object,
|
||||||
|
reloc: &'obj Relocation,
|
||||||
|
) -> ResolvedRelocation<'obj> {
|
||||||
|
let symbol = &obj.symbols[reloc.target_symbol];
|
||||||
|
ResolvedRelocation { relocation: reloc, symbol }
|
||||||
|
}
|
||||||
|
|
||||||
/// Compares relocations contained with a certain data range.
|
/// Compares relocations contained with a certain data range.
|
||||||
/// The ObjDataDiffKind for each diff will either be `None`` (if the relocation matches),
|
/// The DataDiffKind for each diff will either be `None`` (if the relocation matches),
|
||||||
/// or `Replace` (if a relocation was changed, added, or removed).
|
/// or `Replace` (if a relocation was changed, added, or removed).
|
||||||
/// `Insert` and `Delete` are not used when a relocation is added or removed to avoid confusing diffs
|
/// `Insert` and `Delete` are not used when a relocation is added or removed to avoid confusing diffs
|
||||||
/// where it looks like the bytes themselves were changed but actually only the relocations changed.
|
/// where it looks like the bytes themselves were changed but actually only the relocations changed.
|
||||||
fn diff_data_relocs_for_range(
|
fn diff_data_relocs_for_range<'left, 'right>(
|
||||||
left_obj: &ObjInfo,
|
left_obj: &'left Object,
|
||||||
right_obj: &ObjInfo,
|
right_obj: &'right Object,
|
||||||
left: &ObjSection,
|
left_section_idx: usize,
|
||||||
right: &ObjSection,
|
right_section_idx: usize,
|
||||||
left_range: Range<usize>,
|
left_range: Range<usize>,
|
||||||
right_range: Range<usize>,
|
right_range: Range<usize>,
|
||||||
) -> Vec<(ObjDataDiffKind, Option<ObjReloc>, Option<ObjReloc>)> {
|
) -> Vec<(DataDiffKind, Option<ResolvedRelocation<'left>>, Option<ResolvedRelocation<'right>>)> {
|
||||||
|
let left_section = &left_obj.sections[left_section_idx];
|
||||||
|
let right_section = &right_obj.sections[right_section_idx];
|
||||||
let mut diffs = Vec::new();
|
let mut diffs = Vec::new();
|
||||||
for left_reloc in left.relocations.iter() {
|
for left_reloc in left_section.relocations.iter() {
|
||||||
if !left_range.contains(&(left_reloc.address as usize)) {
|
if !left_range.contains(&(left_reloc.address as usize)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let left_offset = left_reloc.address as usize - left_range.start;
|
let left_offset = left_reloc.address as usize - left_range.start;
|
||||||
let Some(right_reloc) = right.relocations.iter().find(|r| {
|
let left_reloc = resolve_relocation(left_obj, left_reloc);
|
||||||
|
let Some(right_reloc) = right_section.relocations.iter().find(|r| {
|
||||||
if !right_range.contains(&(r.address as usize)) {
|
if !right_range.contains(&(r.address as usize)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let right_offset = r.address as usize - right_range.start;
|
let right_offset = r.address as usize - right_range.start;
|
||||||
right_offset == left_offset
|
right_offset == left_offset
|
||||||
}) else {
|
}) else {
|
||||||
diffs.push((ObjDataDiffKind::Delete, Some(left_reloc.clone()), None));
|
diffs.push((DataDiffKind::Delete, Some(left_reloc), None));
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
if reloc_eq(left_obj, right_obj, left_reloc, right_reloc) {
|
let right_reloc = resolve_relocation(right_obj, right_reloc);
|
||||||
diffs.push((
|
if reloc_eq(left_obj, right_obj, &left_reloc, &right_reloc) {
|
||||||
ObjDataDiffKind::None,
|
diffs.push((DataDiffKind::None, Some(left_reloc), Some(right_reloc)));
|
||||||
Some(left_reloc.clone()),
|
|
||||||
Some(right_reloc.clone()),
|
|
||||||
));
|
|
||||||
} else {
|
} else {
|
||||||
diffs.push((
|
diffs.push((
|
||||||
ObjDataDiffKind::Replace,
|
DataDiffKind::Replace,
|
||||||
Some(left_reloc.clone()),
|
Some(left_reloc),
|
||||||
Some(right_reloc.clone()),
|
Some(right_reloc),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for right_reloc in right.relocations.iter() {
|
for right_reloc in right_section.relocations.iter() {
|
||||||
if !right_range.contains(&(right_reloc.address as usize)) {
|
if !right_range.contains(&(right_reloc.address as usize)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let right_offset = right_reloc.address as usize - right_range.start;
|
let right_offset = right_reloc.address as usize - right_range.start;
|
||||||
let Some(_) = left.relocations.iter().find(|r| {
|
let right_reloc = resolve_relocation(right_obj, right_reloc);
|
||||||
|
let Some(_) = left_section.relocations.iter().find(|r| {
|
||||||
if !left_range.contains(&(r.address as usize)) {
|
if !left_range.contains(&(r.address as usize)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let left_offset = r.address as usize - left_range.start;
|
let left_offset = r.address as usize - left_range.start;
|
||||||
left_offset == right_offset
|
left_offset == right_offset
|
||||||
}) else {
|
}) else {
|
||||||
diffs.push((ObjDataDiffKind::Insert, None, Some(right_reloc.clone())));
|
diffs.push((DataDiffKind::Insert, None, Some(right_reloc)));
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
// No need to check the cases for relocations being deleted or matching again.
|
// No need to check the cases for relocations being deleted or matching again.
|
||||||
@ -127,81 +138,103 @@ fn diff_data_relocs_for_range(
|
|||||||
|
|
||||||
/// Compare the data sections of two object files.
|
/// Compare the data sections of two object files.
|
||||||
pub fn diff_data_section(
|
pub fn diff_data_section(
|
||||||
left_obj: &ObjInfo,
|
left_obj: &Object,
|
||||||
right_obj: &ObjInfo,
|
right_obj: &Object,
|
||||||
left: &ObjSection,
|
left_diff: &ObjectDiff,
|
||||||
right: &ObjSection,
|
right_diff: &ObjectDiff,
|
||||||
left_section_diff: &ObjSectionDiff,
|
left_section_idx: usize,
|
||||||
right_section_diff: &ObjSectionDiff,
|
right_section_idx: usize,
|
||||||
) -> Result<(ObjSectionDiff, ObjSectionDiff)> {
|
) -> Result<(SectionDiff, SectionDiff)> {
|
||||||
let left_max =
|
let left_section = &left_obj.sections[left_section_idx];
|
||||||
left.symbols.iter().map(|s| s.section_address + s.size).max().unwrap_or(0).min(left.size);
|
let right_section = &right_obj.sections[right_section_idx];
|
||||||
let right_max =
|
let left_max = left_obj
|
||||||
right.symbols.iter().map(|s| s.section_address + s.size).max().unwrap_or(0).min(right.size);
|
.symbols
|
||||||
let left_data = &left.data[..left_max as usize];
|
.iter()
|
||||||
let right_data = &right.data[..right_max as usize];
|
.filter_map(|s| {
|
||||||
|
if s.section != Some(left_section_idx) || s.kind == SymbolKind::Section {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
s.address.checked_sub(left_section.address).map(|a| a + s.size)
|
||||||
|
})
|
||||||
|
.max()
|
||||||
|
.unwrap_or(0)
|
||||||
|
.min(left_section.size);
|
||||||
|
let right_max = right_obj
|
||||||
|
.symbols
|
||||||
|
.iter()
|
||||||
|
.filter_map(|s| {
|
||||||
|
if s.section != Some(right_section_idx) || s.kind == SymbolKind::Section {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
s.address.checked_sub(right_section.address).map(|a| a + s.size)
|
||||||
|
})
|
||||||
|
.max()
|
||||||
|
.unwrap_or(0)
|
||||||
|
.min(right_section.size);
|
||||||
|
let left_data = &left_section.data[..left_max as usize];
|
||||||
|
let right_data = &right_section.data[..right_max as usize];
|
||||||
let ops = capture_diff_slices(Algorithm::Patience, left_data, right_data);
|
let ops = capture_diff_slices(Algorithm::Patience, left_data, right_data);
|
||||||
let match_percent = get_diff_ratio(&ops, left_data.len(), right_data.len()) * 100.0;
|
let match_percent = get_diff_ratio(&ops, left_data.len(), right_data.len()) * 100.0;
|
||||||
|
|
||||||
let mut left_diff = Vec::<ObjDataDiff>::new();
|
let mut left_data_diff = Vec::<DataDiff>::new();
|
||||||
let mut right_diff = Vec::<ObjDataDiff>::new();
|
let mut right_data_diff = Vec::<DataDiff>::new();
|
||||||
for op in ops {
|
for op in ops {
|
||||||
let (tag, left_range, right_range) = op.as_tag_tuple();
|
let (tag, left_range, right_range) = op.as_tag_tuple();
|
||||||
let left_len = left_range.len();
|
let left_len = left_range.len();
|
||||||
let right_len = right_range.len();
|
let right_len = right_range.len();
|
||||||
let mut len = left_len.max(right_len);
|
let mut len = left_len.max(right_len);
|
||||||
let kind = match tag {
|
let kind = match tag {
|
||||||
similar::DiffTag::Equal => ObjDataDiffKind::None,
|
similar::DiffTag::Equal => DataDiffKind::None,
|
||||||
similar::DiffTag::Delete => ObjDataDiffKind::Delete,
|
similar::DiffTag::Delete => DataDiffKind::Delete,
|
||||||
similar::DiffTag::Insert => ObjDataDiffKind::Insert,
|
similar::DiffTag::Insert => DataDiffKind::Insert,
|
||||||
similar::DiffTag::Replace => {
|
similar::DiffTag::Replace => {
|
||||||
// Ensure replacements are equal length
|
// Ensure replacements are equal length
|
||||||
len = left_len.min(right_len);
|
len = left_len.min(right_len);
|
||||||
ObjDataDiffKind::Replace
|
DataDiffKind::Replace
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let left_data = &left.data[left_range];
|
let left_data = &left_section.data[left_range];
|
||||||
let right_data = &right.data[right_range];
|
let right_data = &right_section.data[right_range];
|
||||||
left_diff.push(ObjDataDiff {
|
left_data_diff.push(DataDiff {
|
||||||
data: left_data[..len.min(left_data.len())].to_vec(),
|
data: left_data[..len.min(left_data.len())].to_vec(),
|
||||||
kind,
|
kind,
|
||||||
len,
|
len,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
right_diff.push(ObjDataDiff {
|
right_data_diff.push(DataDiff {
|
||||||
data: right_data[..len.min(right_data.len())].to_vec(),
|
data: right_data[..len.min(right_data.len())].to_vec(),
|
||||||
kind,
|
kind,
|
||||||
len,
|
len,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
if kind == ObjDataDiffKind::Replace {
|
if kind == DataDiffKind::Replace {
|
||||||
match left_len.cmp(&right_len) {
|
match left_len.cmp(&right_len) {
|
||||||
Ordering::Less => {
|
Ordering::Less => {
|
||||||
let len = right_len - left_len;
|
let len = right_len - left_len;
|
||||||
left_diff.push(ObjDataDiff {
|
left_data_diff.push(DataDiff {
|
||||||
data: vec![],
|
data: vec![],
|
||||||
kind: ObjDataDiffKind::Insert,
|
kind: DataDiffKind::Insert,
|
||||||
len,
|
len,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
right_diff.push(ObjDataDiff {
|
right_data_diff.push(DataDiff {
|
||||||
data: right_data[left_len..right_len].to_vec(),
|
data: right_data[left_len..right_len].to_vec(),
|
||||||
kind: ObjDataDiffKind::Insert,
|
kind: DataDiffKind::Insert,
|
||||||
len,
|
len,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Ordering::Greater => {
|
Ordering::Greater => {
|
||||||
let len = left_len - right_len;
|
let len = left_len - right_len;
|
||||||
left_diff.push(ObjDataDiff {
|
left_data_diff.push(DataDiff {
|
||||||
data: left_data[right_len..left_len].to_vec(),
|
data: left_data[right_len..left_len].to_vec(),
|
||||||
kind: ObjDataDiffKind::Delete,
|
kind: DataDiffKind::Delete,
|
||||||
len,
|
len,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
right_diff.push(ObjDataDiff {
|
right_data_diff.push(DataDiff {
|
||||||
data: vec![],
|
data: vec![],
|
||||||
kind: ObjDataDiffKind::Delete,
|
kind: DataDiffKind::Delete,
|
||||||
len,
|
len,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
@ -216,28 +249,36 @@ pub fn diff_data_section(
|
|||||||
for (diff_kind, left_reloc, right_reloc) in diff_data_relocs_for_range(
|
for (diff_kind, left_reloc, right_reloc) in diff_data_relocs_for_range(
|
||||||
left_obj,
|
left_obj,
|
||||||
right_obj,
|
right_obj,
|
||||||
left,
|
left_section_idx,
|
||||||
right,
|
right_section_idx,
|
||||||
0..left_max as usize,
|
0..left_max as usize,
|
||||||
0..right_max as usize,
|
0..right_max as usize,
|
||||||
) {
|
) {
|
||||||
if let Some(left_reloc) = left_reloc {
|
if let Some(left_reloc) = left_reloc {
|
||||||
let len = left_obj.arch.get_reloc_byte_size(left_reloc.flags);
|
let len = left_obj.arch.get_reloc_byte_size(left_reloc.relocation.flags);
|
||||||
let range = left_reloc.address as usize..left_reloc.address as usize + len;
|
let range = left_reloc.relocation.address as usize
|
||||||
left_reloc_diffs.push(ObjDataRelocDiff { reloc: left_reloc, kind: diff_kind, range });
|
..left_reloc.relocation.address as usize + len;
|
||||||
|
left_reloc_diffs.push(DataRelocationDiff { kind: diff_kind, range });
|
||||||
}
|
}
|
||||||
if let Some(right_reloc) = right_reloc {
|
if let Some(right_reloc) = right_reloc {
|
||||||
let len = right_obj.arch.get_reloc_byte_size(right_reloc.flags);
|
let len = right_obj.arch.get_reloc_byte_size(right_reloc.relocation.flags);
|
||||||
let range = right_reloc.address as usize..right_reloc.address as usize + len;
|
let range = right_reloc.relocation.address as usize
|
||||||
right_reloc_diffs.push(ObjDataRelocDiff { reloc: right_reloc, kind: diff_kind, range });
|
..right_reloc.relocation.address as usize + len;
|
||||||
|
right_reloc_diffs.push(DataRelocationDiff { kind: diff_kind, range });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (mut left_section_diff, mut right_section_diff) =
|
let (mut left_section_diff, mut right_section_diff) = diff_generic_section(
|
||||||
diff_generic_section(left, right, left_section_diff, right_section_diff)?;
|
left_obj,
|
||||||
let all_left_relocs_match = left_reloc_diffs.iter().all(|d| d.kind == ObjDataDiffKind::None);
|
right_obj,
|
||||||
left_section_diff.data_diff = left_diff;
|
left_diff,
|
||||||
right_section_diff.data_diff = right_diff;
|
right_diff,
|
||||||
|
left_section_idx,
|
||||||
|
right_section_idx,
|
||||||
|
)?;
|
||||||
|
let all_left_relocs_match = left_reloc_diffs.iter().all(|d| d.kind == DataDiffKind::None);
|
||||||
|
left_section_diff.data_diff = left_data_diff;
|
||||||
|
right_section_diff.data_diff = right_data_diff;
|
||||||
left_section_diff.reloc_diff = left_reloc_diffs;
|
left_section_diff.reloc_diff = left_reloc_diffs;
|
||||||
right_section_diff.reloc_diff = right_reloc_diffs;
|
right_section_diff.reloc_diff = right_reloc_diffs;
|
||||||
if all_left_relocs_match {
|
if all_left_relocs_match {
|
||||||
@ -254,29 +295,58 @@ pub fn diff_data_section(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn diff_data_symbol(
|
pub fn diff_data_symbol(
|
||||||
left_obj: &ObjInfo,
|
left_obj: &Object,
|
||||||
right_obj: &ObjInfo,
|
right_obj: &Object,
|
||||||
left_symbol_ref: SymbolRef,
|
left_symbol_idx: usize,
|
||||||
right_symbol_ref: SymbolRef,
|
right_symbol_idx: usize,
|
||||||
) -> Result<(ObjSymbolDiff, ObjSymbolDiff)> {
|
) -> Result<(SymbolDiff, SymbolDiff)> {
|
||||||
let (left_section, left_symbol) = left_obj.section_symbol(left_symbol_ref);
|
let left_symbol = &left_obj.symbols[left_symbol_idx];
|
||||||
let (right_section, right_symbol) = right_obj.section_symbol(right_symbol_ref);
|
let right_symbol = &right_obj.symbols[right_symbol_idx];
|
||||||
|
|
||||||
let left_section = left_section.ok_or_else(|| anyhow!("Data symbol section not found"))?;
|
let left_section_idx =
|
||||||
let right_section = right_section.ok_or_else(|| anyhow!("Data symbol section not found"))?;
|
left_symbol.section.ok_or_else(|| anyhow!("Data symbol section not found"))?;
|
||||||
|
let right_section_idx =
|
||||||
|
right_symbol.section.ok_or_else(|| anyhow!("Data symbol section not found"))?;
|
||||||
|
|
||||||
let left_range = left_symbol.section_address as usize
|
let left_section = &left_obj.sections[left_section_idx];
|
||||||
..(left_symbol.section_address + left_symbol.size) as usize;
|
let right_section = &right_obj.sections[right_section_idx];
|
||||||
let right_range = right_symbol.section_address as usize
|
|
||||||
..(right_symbol.section_address + right_symbol.size) as usize;
|
let left_start = left_symbol
|
||||||
|
.address
|
||||||
|
.checked_sub(left_section.address)
|
||||||
|
.ok_or_else(|| anyhow!("Symbol address out of section bounds"))?;
|
||||||
|
let right_start = right_symbol
|
||||||
|
.address
|
||||||
|
.checked_sub(right_section.address)
|
||||||
|
.ok_or_else(|| anyhow!("Symbol address out of section bounds"))?;
|
||||||
|
let left_end = left_start + left_symbol.size;
|
||||||
|
if left_end > left_section.size {
|
||||||
|
return Err(anyhow!(
|
||||||
|
"Symbol {} size out of section bounds ({} > {})",
|
||||||
|
left_symbol.name,
|
||||||
|
left_end,
|
||||||
|
left_section.size
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let right_end = right_start + right_symbol.size;
|
||||||
|
if right_end > right_section.size {
|
||||||
|
return Err(anyhow!(
|
||||||
|
"Symbol {} size out of section bounds ({} > {})",
|
||||||
|
right_symbol.name,
|
||||||
|
right_end,
|
||||||
|
right_section.size
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let left_range = left_start as usize..left_end as usize;
|
||||||
|
let right_range = right_start as usize..right_end as usize;
|
||||||
let left_data = &left_section.data[left_range.clone()];
|
let left_data = &left_section.data[left_range.clone()];
|
||||||
let right_data = &right_section.data[right_range.clone()];
|
let right_data = &right_section.data[right_range.clone()];
|
||||||
|
|
||||||
let reloc_diffs = diff_data_relocs_for_range(
|
let reloc_diffs = diff_data_relocs_for_range(
|
||||||
left_obj,
|
left_obj,
|
||||||
right_obj,
|
right_obj,
|
||||||
left_section,
|
left_section_idx,
|
||||||
right_section,
|
right_section_idx,
|
||||||
left_range,
|
left_range,
|
||||||
right_range,
|
right_range,
|
||||||
);
|
);
|
||||||
@ -291,11 +361,15 @@ pub fn diff_data_symbol(
|
|||||||
for (diff_kind, left_reloc, right_reloc) in reloc_diffs {
|
for (diff_kind, left_reloc, right_reloc) in reloc_diffs {
|
||||||
let reloc_diff_len = match (left_reloc, right_reloc) {
|
let reloc_diff_len = match (left_reloc, right_reloc) {
|
||||||
(None, None) => unreachable!(),
|
(None, None) => unreachable!(),
|
||||||
(None, Some(right_reloc)) => right_obj.arch.get_reloc_byte_size(right_reloc.flags),
|
(None, Some(right_reloc)) => {
|
||||||
(Some(left_reloc), _) => left_obj.arch.get_reloc_byte_size(left_reloc.flags),
|
right_obj.arch.get_reloc_byte_size(right_reloc.relocation.flags)
|
||||||
|
}
|
||||||
|
(Some(left_reloc), _) => {
|
||||||
|
left_obj.arch.get_reloc_byte_size(left_reloc.relocation.flags)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
total_reloc_bytes += reloc_diff_len;
|
total_reloc_bytes += reloc_diff_len;
|
||||||
if diff_kind == ObjDataDiffKind::None {
|
if diff_kind == DataDiffKind::None {
|
||||||
matching_reloc_bytes += reloc_diff_len;
|
matching_reloc_bytes += reloc_diff_len;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -315,17 +389,17 @@ pub fn diff_data_symbol(
|
|||||||
let match_percent = match_ratio * 100.0;
|
let match_percent = match_ratio * 100.0;
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
ObjSymbolDiff {
|
SymbolDiff {
|
||||||
symbol_ref: left_symbol_ref,
|
target_symbol: Some(right_symbol_idx),
|
||||||
target_symbol: Some(right_symbol_ref),
|
|
||||||
instructions: vec![],
|
|
||||||
match_percent: Some(match_percent),
|
match_percent: Some(match_percent),
|
||||||
|
diff_score: None,
|
||||||
|
instruction_rows: vec![],
|
||||||
},
|
},
|
||||||
ObjSymbolDiff {
|
SymbolDiff {
|
||||||
symbol_ref: right_symbol_ref,
|
target_symbol: Some(left_symbol_idx),
|
||||||
target_symbol: Some(left_symbol_ref),
|
|
||||||
instructions: vec![],
|
|
||||||
match_percent: Some(match_percent),
|
match_percent: Some(match_percent),
|
||||||
|
diff_score: None,
|
||||||
|
instruction_rows: vec![],
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -333,69 +407,89 @@ pub fn diff_data_symbol(
|
|||||||
/// Compares a section of two object files.
|
/// Compares a section of two object files.
|
||||||
/// This essentially adds up the match percentage of each symbol in the section.
|
/// This essentially adds up the match percentage of each symbol in the section.
|
||||||
pub fn diff_generic_section(
|
pub fn diff_generic_section(
|
||||||
left: &ObjSection,
|
left_obj: &Object,
|
||||||
_right: &ObjSection,
|
_right_obj: &Object,
|
||||||
left_diff: &ObjSectionDiff,
|
left_diff: &ObjectDiff,
|
||||||
_right_diff: &ObjSectionDiff,
|
_right_diff: &ObjectDiff,
|
||||||
) -> Result<(ObjSectionDiff, ObjSectionDiff)> {
|
left_section_idx: usize,
|
||||||
let match_percent = if left_diff.symbols.iter().all(|d| d.match_percent == Some(100.0)) {
|
_right_section_idx: usize,
|
||||||
|
) -> Result<(SectionDiff, SectionDiff)> {
|
||||||
|
let match_percent = if left_obj
|
||||||
|
.symbols
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.filter(|(_, s)| s.section == Some(left_section_idx) && s.kind != SymbolKind::Section)
|
||||||
|
.map(|(i, _)| &left_diff.symbols[i])
|
||||||
|
.all(|d| d.match_percent == Some(100.0))
|
||||||
|
{
|
||||||
100.0 // Avoid fp precision issues
|
100.0 // Avoid fp precision issues
|
||||||
} else {
|
} else {
|
||||||
left.symbols
|
let (matched, total) = left_obj
|
||||||
|
.symbols
|
||||||
.iter()
|
.iter()
|
||||||
.zip(left_diff.symbols.iter())
|
.enumerate()
|
||||||
.map(|(s, d)| d.match_percent.unwrap_or(0.0) * s.size as f32)
|
.filter(|(_, s)| s.section == Some(left_section_idx) && s.kind != SymbolKind::Section)
|
||||||
.sum::<f32>()
|
.map(|(i, s)| (s, &left_diff.symbols[i]))
|
||||||
/ left.size as f32
|
.fold((0.0, 0.0), |(matched, total), (s, d)| {
|
||||||
|
(matched + d.match_percent.unwrap_or(0.0) * s.size as f32, total + s.size as f32)
|
||||||
|
});
|
||||||
|
if total == 0.0 {
|
||||||
|
100.0
|
||||||
|
} else {
|
||||||
|
matched / total
|
||||||
|
}
|
||||||
};
|
};
|
||||||
Ok((
|
Ok((
|
||||||
ObjSectionDiff {
|
SectionDiff { match_percent: Some(match_percent), data_diff: vec![], reloc_diff: vec![] },
|
||||||
symbols: vec![],
|
SectionDiff { match_percent: Some(match_percent), data_diff: vec![], reloc_diff: vec![] },
|
||||||
data_diff: vec![],
|
|
||||||
reloc_diff: vec![],
|
|
||||||
match_percent: Some(match_percent),
|
|
||||||
},
|
|
||||||
ObjSectionDiff {
|
|
||||||
symbols: vec![],
|
|
||||||
data_diff: vec![],
|
|
||||||
reloc_diff: vec![],
|
|
||||||
match_percent: Some(match_percent),
|
|
||||||
},
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compare the addresses and sizes of each symbol in the BSS sections.
|
/// Compare the addresses and sizes of each symbol in the BSS sections.
|
||||||
pub fn diff_bss_section(
|
pub fn diff_bss_section(
|
||||||
left: &ObjSection,
|
left_obj: &Object,
|
||||||
right: &ObjSection,
|
right_obj: &Object,
|
||||||
left_diff: &ObjSectionDiff,
|
left_diff: &ObjectDiff,
|
||||||
right_diff: &ObjSectionDiff,
|
right_diff: &ObjectDiff,
|
||||||
) -> Result<(ObjSectionDiff, ObjSectionDiff)> {
|
left_section_idx: usize,
|
||||||
let left_sizes = left.symbols.iter().map(|s| (s.section_address, s.size)).collect::<Vec<_>>();
|
right_section_idx: usize,
|
||||||
let right_sizes = right.symbols.iter().map(|s| (s.section_address, s.size)).collect::<Vec<_>>();
|
) -> Result<(SectionDiff, SectionDiff)> {
|
||||||
|
let left_section = &left_obj.sections[left_section_idx];
|
||||||
|
let left_sizes = left_obj
|
||||||
|
.symbols
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.filter(|(_, s)| s.section == Some(left_section_idx) && s.kind != SymbolKind::Section)
|
||||||
|
.filter_map(|(_, s)| s.address.checked_sub(left_section.address).map(|a| (a, s.size)))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let right_section = &right_obj.sections[right_section_idx];
|
||||||
|
let right_sizes = right_obj
|
||||||
|
.symbols
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.filter(|(_, s)| s.section == Some(right_section_idx) && s.kind != SymbolKind::Section)
|
||||||
|
.filter_map(|(_, s)| s.address.checked_sub(right_section.address).map(|a| (a, s.size)))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
let ops = capture_diff_slices(Algorithm::Patience, &left_sizes, &right_sizes);
|
let ops = capture_diff_slices(Algorithm::Patience, &left_sizes, &right_sizes);
|
||||||
let mut match_percent = get_diff_ratio(&ops, left_sizes.len(), right_sizes.len()) * 100.0;
|
let mut match_percent = get_diff_ratio(&ops, left_sizes.len(), right_sizes.len()) * 100.0;
|
||||||
|
|
||||||
// Use the highest match percent between two options:
|
// Use the highest match percent between two options:
|
||||||
// - Left symbols matching right symbols by name
|
// - Left symbols matching right symbols by name
|
||||||
// - Diff of the addresses and sizes of each symbol
|
// - Diff of the addresses and sizes of each symbol
|
||||||
let (generic_diff, _) = diff_generic_section(left, right, left_diff, right_diff)?;
|
let (generic_diff, _) = diff_generic_section(
|
||||||
|
left_obj,
|
||||||
|
right_obj,
|
||||||
|
left_diff,
|
||||||
|
right_diff,
|
||||||
|
left_section_idx,
|
||||||
|
right_section_idx,
|
||||||
|
)?;
|
||||||
if generic_diff.match_percent.unwrap_or(-1.0) > match_percent {
|
if generic_diff.match_percent.unwrap_or(-1.0) > match_percent {
|
||||||
match_percent = generic_diff.match_percent.unwrap();
|
match_percent = generic_diff.match_percent.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
ObjSectionDiff {
|
SectionDiff { match_percent: Some(match_percent), data_diff: vec![], reloc_diff: vec![] },
|
||||||
symbols: vec![],
|
SectionDiff { match_percent: Some(match_percent), data_diff: vec![], reloc_diff: vec![] },
|
||||||
data_diff: vec![],
|
|
||||||
reloc_diff: vec![],
|
|
||||||
match_percent: Some(match_percent),
|
|
||||||
},
|
|
||||||
ObjSectionDiff {
|
|
||||||
symbols: vec![],
|
|
||||||
data_diff: vec![],
|
|
||||||
reloc_diff: vec![],
|
|
||||||
match_percent: Some(match_percent),
|
|
||||||
},
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,28 @@
|
|||||||
use alloc::string::{String, ToString};
|
use alloc::{
|
||||||
|
borrow::Cow,
|
||||||
|
collections::BTreeSet,
|
||||||
|
format,
|
||||||
|
string::{String, ToString},
|
||||||
|
vec::Vec,
|
||||||
|
};
|
||||||
|
use core::cmp::Ordering;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use itertools::Itertools;
|
||||||
|
use regex::Regex;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
diff::{ObjInsArgDiff, ObjInsDiff},
|
diff::{DiffObjConfig, InstructionArgDiffIndex, InstructionDiffRow, ObjectDiff, SymbolDiff},
|
||||||
obj::{ObjInsArg, ObjInsArgValue, ObjReloc, ObjSymbol},
|
obj::{
|
||||||
|
InstructionArg, InstructionArgValue, Object, SectionFlag, SectionKind, Symbol, SymbolFlag,
|
||||||
|
SymbolKind,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub enum DiffText<'a> {
|
pub enum DiffText<'a> {
|
||||||
/// Basic text
|
/// Basic text
|
||||||
Basic(&'a str),
|
Basic(&'a str),
|
||||||
/// Colored text
|
|
||||||
BasicColor(&'a str, usize),
|
|
||||||
/// Line number
|
/// Line number
|
||||||
Line(u32),
|
Line(u32),
|
||||||
/// Instruction address
|
/// Instruction address
|
||||||
@ -18,13 +30,13 @@ pub enum DiffText<'a> {
|
|||||||
/// Instruction mnemonic
|
/// Instruction mnemonic
|
||||||
Opcode(&'a str, u16),
|
Opcode(&'a str, u16),
|
||||||
/// Instruction argument
|
/// Instruction argument
|
||||||
Argument(&'a ObjInsArgValue, Option<&'a ObjInsArgDiff>),
|
Argument(&'a InstructionArgValue),
|
||||||
/// Branch destination
|
/// Branch destination
|
||||||
BranchDest(u64, Option<&'a ObjInsArgDiff>),
|
BranchDest(u64),
|
||||||
/// Symbol name
|
/// Symbol name
|
||||||
Symbol(&'a ObjSymbol, Option<&'a ObjInsArgDiff>),
|
Symbol(&'a Symbol),
|
||||||
/// Relocation addend
|
/// Relocation addend
|
||||||
Addend(i64, Option<&'a ObjInsArgDiff>),
|
Addend(i64),
|
||||||
/// Number of spaces
|
/// Number of spaces
|
||||||
Spacing(usize),
|
Spacing(usize),
|
||||||
/// End of line
|
/// End of line
|
||||||
@ -36,83 +48,113 @@ pub enum HighlightKind {
|
|||||||
#[default]
|
#[default]
|
||||||
None,
|
None,
|
||||||
Opcode(u16),
|
Opcode(u16),
|
||||||
Arg(ObjInsArgValue),
|
Argument(InstructionArgValue),
|
||||||
Symbol(String),
|
Symbol(String),
|
||||||
Address(u64),
|
Address(u64),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn display_diff<E>(
|
pub enum InstructionPart {
|
||||||
ins_diff: &ObjInsDiff,
|
Basic(&'static str),
|
||||||
base_addr: u64,
|
Opcode(Cow<'static, str>, u16),
|
||||||
mut cb: impl FnMut(DiffText) -> Result<(), E>,
|
Arg(InstructionArg),
|
||||||
) -> Result<(), E> {
|
Separator,
|
||||||
let Some(ins) = &ins_diff.ins else {
|
|
||||||
cb(DiffText::Eol)?;
|
|
||||||
return Ok(());
|
|
||||||
};
|
|
||||||
if let Some(line) = ins.line {
|
|
||||||
cb(DiffText::Line(line))?;
|
|
||||||
}
|
|
||||||
cb(DiffText::Address(ins.address - base_addr))?;
|
|
||||||
if let Some(branch) = &ins_diff.branch_from {
|
|
||||||
cb(DiffText::BasicColor(" ~> ", branch.branch_idx))?;
|
|
||||||
} else {
|
|
||||||
cb(DiffText::Spacing(4))?;
|
|
||||||
}
|
|
||||||
cb(DiffText::Opcode(&ins.mnemonic, ins.op))?;
|
|
||||||
let mut arg_diff_idx = 0; // non-PlainText index
|
|
||||||
for (i, arg) in ins.args.iter().enumerate() {
|
|
||||||
if i == 0 {
|
|
||||||
cb(DiffText::Spacing(1))?;
|
|
||||||
}
|
|
||||||
let diff = ins_diff.arg_diff.get(arg_diff_idx).and_then(|o| o.as_ref());
|
|
||||||
match arg {
|
|
||||||
ObjInsArg::PlainText(s) => {
|
|
||||||
cb(DiffText::Basic(s))?;
|
|
||||||
}
|
|
||||||
ObjInsArg::Arg(v) => {
|
|
||||||
cb(DiffText::Argument(v, diff))?;
|
|
||||||
arg_diff_idx += 1;
|
|
||||||
}
|
|
||||||
ObjInsArg::Reloc => {
|
|
||||||
display_reloc_name(ins.reloc.as_ref().unwrap(), &mut cb, diff)?;
|
|
||||||
arg_diff_idx += 1;
|
|
||||||
}
|
|
||||||
ObjInsArg::BranchDest(dest) => {
|
|
||||||
if let Some(dest) = dest.checked_sub(base_addr) {
|
|
||||||
cb(DiffText::BranchDest(dest, diff))?;
|
|
||||||
} else {
|
|
||||||
cb(DiffText::Basic("<unknown>"))?;
|
|
||||||
}
|
|
||||||
arg_diff_idx += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(branch) = &ins_diff.branch_to {
|
|
||||||
cb(DiffText::BasicColor(" ~>", branch.branch_idx))?;
|
|
||||||
}
|
|
||||||
cb(DiffText::Eol)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn display_reloc_name<E>(
|
pub fn display_row(
|
||||||
reloc: &ObjReloc,
|
obj: &Object,
|
||||||
mut cb: impl FnMut(DiffText) -> Result<(), E>,
|
symbol_index: usize,
|
||||||
diff: Option<&ObjInsArgDiff>,
|
ins_row: &InstructionDiffRow,
|
||||||
) -> Result<(), E> {
|
diff_config: &DiffObjConfig,
|
||||||
cb(DiffText::Symbol(&reloc.target, diff))?;
|
mut cb: impl FnMut(DiffText, InstructionArgDiffIndex) -> Result<()>,
|
||||||
cb(DiffText::Addend(reloc.addend, diff))
|
) -> Result<()> {
|
||||||
|
let Some(ins_ref) = ins_row.ins_ref else {
|
||||||
|
cb(DiffText::Eol, InstructionArgDiffIndex::NONE)?;
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
let symbol = &obj.symbols[symbol_index];
|
||||||
|
let Some(section_index) = symbol.section else {
|
||||||
|
cb(DiffText::Eol, InstructionArgDiffIndex::NONE)?;
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
let section = &obj.sections[section_index];
|
||||||
|
let Some(data) = section.data_range(ins_ref.address, ins_ref.size as usize) else {
|
||||||
|
cb(DiffText::Eol, InstructionArgDiffIndex::NONE)?;
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
if let Some(line) = section.line_info.range(..=ins_ref.address).last().map(|(_, &b)| b) {
|
||||||
|
cb(DiffText::Line(line), InstructionArgDiffIndex::NONE)?;
|
||||||
|
}
|
||||||
|
cb(
|
||||||
|
DiffText::Address(ins_ref.address.saturating_sub(symbol.address)),
|
||||||
|
InstructionArgDiffIndex::NONE,
|
||||||
|
)?;
|
||||||
|
if let Some(branch) = &ins_row.branch_from {
|
||||||
|
cb(DiffText::Basic(" ~> "), InstructionArgDiffIndex::new(branch.branch_idx))?;
|
||||||
|
} else {
|
||||||
|
cb(DiffText::Spacing(4), InstructionArgDiffIndex::NONE)?;
|
||||||
|
}
|
||||||
|
let mut arg_idx = 0;
|
||||||
|
let relocation = section.relocation_at(ins_ref.address, obj);
|
||||||
|
obj.arch.display_instruction(
|
||||||
|
ins_ref,
|
||||||
|
data,
|
||||||
|
relocation,
|
||||||
|
symbol.address..symbol.address + symbol.size,
|
||||||
|
section_index,
|
||||||
|
diff_config,
|
||||||
|
&mut |part| match part {
|
||||||
|
InstructionPart::Basic(text) => {
|
||||||
|
cb(DiffText::Basic(text), InstructionArgDiffIndex::NONE)
|
||||||
|
}
|
||||||
|
InstructionPart::Opcode(mnemonic, opcode) => {
|
||||||
|
cb(DiffText::Opcode(mnemonic.as_ref(), opcode), InstructionArgDiffIndex::NONE)
|
||||||
|
}
|
||||||
|
InstructionPart::Arg(arg) => {
|
||||||
|
let diff_index = ins_row.arg_diff.get(arg_idx).copied().unwrap_or_default();
|
||||||
|
arg_idx += 1;
|
||||||
|
match arg {
|
||||||
|
InstructionArg::Value(ref value) => cb(DiffText::Argument(value), diff_index),
|
||||||
|
InstructionArg::Reloc => {
|
||||||
|
let resolved = relocation.unwrap();
|
||||||
|
cb(DiffText::Symbol(resolved.symbol), diff_index)?;
|
||||||
|
if resolved.relocation.addend != 0 {
|
||||||
|
cb(DiffText::Addend(resolved.relocation.addend), diff_index)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
InstructionArg::BranchDest(dest) => {
|
||||||
|
if let Some(addr) = dest.checked_sub(symbol.address) {
|
||||||
|
cb(DiffText::BranchDest(addr), diff_index)
|
||||||
|
} else {
|
||||||
|
cb(
|
||||||
|
DiffText::Argument(&InstructionArgValue::Opaque(Cow::Borrowed(
|
||||||
|
"<invalid>",
|
||||||
|
))),
|
||||||
|
diff_index,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
InstructionPart::Separator => {
|
||||||
|
cb(DiffText::Basic(diff_config.separator()), InstructionArgDiffIndex::NONE)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
if let Some(branch) = &ins_row.branch_to {
|
||||||
|
cb(DiffText::Basic(" ~>"), InstructionArgDiffIndex::new(branch.branch_idx))?;
|
||||||
|
}
|
||||||
|
cb(DiffText::Eol, InstructionArgDiffIndex::NONE)?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq<DiffText<'_>> for HighlightKind {
|
impl PartialEq<DiffText<'_>> for HighlightKind {
|
||||||
fn eq(&self, other: &DiffText) -> bool {
|
fn eq(&self, other: &DiffText) -> bool {
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
(HighlightKind::Opcode(a), DiffText::Opcode(_, b)) => a == b,
|
(HighlightKind::Opcode(a), DiffText::Opcode(_, b)) => a == b,
|
||||||
(HighlightKind::Arg(a), DiffText::Argument(b, _)) => a.loose_eq(b),
|
(HighlightKind::Argument(a), DiffText::Argument(b)) => a.loose_eq(b),
|
||||||
(HighlightKind::Symbol(a), DiffText::Symbol(b, _)) => a == &b.name,
|
(HighlightKind::Symbol(a), DiffText::Symbol(b)) => a == &b.name,
|
||||||
(HighlightKind::Address(a), DiffText::Address(b) | DiffText::BranchDest(b, _)) => {
|
(HighlightKind::Address(a), DiffText::Address(b) | DiffText::BranchDest(b)) => a == b,
|
||||||
a == b
|
|
||||||
}
|
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -126,10 +168,245 @@ impl From<DiffText<'_>> for HighlightKind {
|
|||||||
fn from(value: DiffText<'_>) -> Self {
|
fn from(value: DiffText<'_>) -> Self {
|
||||||
match value {
|
match value {
|
||||||
DiffText::Opcode(_, op) => HighlightKind::Opcode(op),
|
DiffText::Opcode(_, op) => HighlightKind::Opcode(op),
|
||||||
DiffText::Argument(arg, _) => HighlightKind::Arg(arg.clone()),
|
DiffText::Argument(arg) => HighlightKind::Argument(arg.clone()),
|
||||||
DiffText::Symbol(sym, _) => HighlightKind::Symbol(sym.name.to_string()),
|
DiffText::Symbol(sym) => HighlightKind::Symbol(sym.name.to_string()),
|
||||||
DiffText::Address(addr) | DiffText::BranchDest(addr, _) => HighlightKind::Address(addr),
|
DiffText::Address(addr) | DiffText::BranchDest(addr) => HighlightKind::Address(addr),
|
||||||
_ => HighlightKind::None,
|
_ => HighlightKind::None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum ContextMenuItem {
|
||||||
|
Copy { value: String, label: Option<String> },
|
||||||
|
Navigate { label: String },
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum HoverItemColor {
|
||||||
|
Normal, // Gray
|
||||||
|
Emphasized, // White
|
||||||
|
Special, // Blue
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct HoverItem {
|
||||||
|
pub text: String,
|
||||||
|
pub color: HoverItemColor,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn symbol_context(_obj: &Object, symbol: &Symbol) -> Vec<ContextMenuItem> {
|
||||||
|
let mut out = Vec::new();
|
||||||
|
if let Some(name) = &symbol.demangled_name {
|
||||||
|
out.push(ContextMenuItem::Copy { value: name.clone(), label: None });
|
||||||
|
}
|
||||||
|
out.push(ContextMenuItem::Copy { value: symbol.name.clone(), label: None });
|
||||||
|
if let Some(address) = symbol.virtual_address {
|
||||||
|
out.push(ContextMenuItem::Copy {
|
||||||
|
value: format!("{:#x}", address),
|
||||||
|
label: Some("virtual address".to_string()),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// if let Some(_extab) = obj.arch.ppc().and_then(|ppc| ppc.extab_for_symbol(symbol)) {
|
||||||
|
// out.push(ContextMenuItem::Navigate { label: "Decode exception table".to_string() });
|
||||||
|
// }
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn symbol_hover(_obj: &Object, symbol: &Symbol) -> Vec<HoverItem> {
|
||||||
|
let mut out = Vec::new();
|
||||||
|
out.push(HoverItem {
|
||||||
|
text: format!("Name: {}", symbol.name),
|
||||||
|
color: HoverItemColor::Emphasized,
|
||||||
|
});
|
||||||
|
out.push(HoverItem {
|
||||||
|
text: format!("Address: {:x}", symbol.address),
|
||||||
|
color: HoverItemColor::Emphasized,
|
||||||
|
});
|
||||||
|
if symbol.flags.contains(SymbolFlag::SizeInferred) {
|
||||||
|
out.push(HoverItem {
|
||||||
|
text: format!("Size: {:x} (inferred)", symbol.size),
|
||||||
|
color: HoverItemColor::Emphasized,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
out.push(HoverItem {
|
||||||
|
text: format!("Size: {:x}", symbol.size),
|
||||||
|
color: HoverItemColor::Emphasized,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if let Some(address) = symbol.virtual_address {
|
||||||
|
out.push(HoverItem {
|
||||||
|
text: format!("Virtual address: {:#x}", address),
|
||||||
|
color: HoverItemColor::Special,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// if let Some(extab) = obj.arch.ppc().and_then(|ppc| ppc.extab_for_symbol(symbol)) {
|
||||||
|
// out.push(HoverItem {
|
||||||
|
// text: format!("extab symbol: {}", extab.etb_symbol.name),
|
||||||
|
// color: HoverItemColor::Special,
|
||||||
|
// });
|
||||||
|
// out.push(HoverItem {
|
||||||
|
// text: format!("extabindex symbol: {}", extab.eti_symbol.name),
|
||||||
|
// color: HoverItemColor::Special,
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum SymbolFilter<'a> {
|
||||||
|
None,
|
||||||
|
Search(&'a Regex),
|
||||||
|
Mapping(usize, Option<&'a Regex>),
|
||||||
|
}
|
||||||
|
|
||||||
|
fn symbol_matches_filter(
|
||||||
|
symbol: &Symbol,
|
||||||
|
diff: &SymbolDiff,
|
||||||
|
filter: SymbolFilter<'_>,
|
||||||
|
show_hidden_symbols: bool,
|
||||||
|
) -> bool {
|
||||||
|
// Ignore absolute symbols
|
||||||
|
if symbol.section.is_none() && !symbol.flags.contains(SymbolFlag::Common) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if !show_hidden_symbols && symbol.flags.contains(SymbolFlag::Hidden) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
match filter {
|
||||||
|
SymbolFilter::None => true,
|
||||||
|
SymbolFilter::Search(regex) => {
|
||||||
|
regex.is_match(&symbol.name)
|
||||||
|
|| symbol.demangled_name.as_deref().is_some_and(|s| regex.is_match(s))
|
||||||
|
}
|
||||||
|
SymbolFilter::Mapping(symbol_ref, regex) => {
|
||||||
|
diff.target_symbol == Some(symbol_ref)
|
||||||
|
&& regex.is_none_or(|r| {
|
||||||
|
r.is_match(&symbol.name)
|
||||||
|
|| symbol.demangled_name.as_deref().is_some_and(|s| r.is_match(s))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||||
|
pub struct SectionDisplaySymbol {
|
||||||
|
pub symbol: usize,
|
||||||
|
pub is_mapping_symbol: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct SectionDisplay {
|
||||||
|
pub id: String,
|
||||||
|
pub name: String,
|
||||||
|
pub size: u64,
|
||||||
|
pub match_percent: Option<f32>,
|
||||||
|
pub symbols: Vec<SectionDisplaySymbol>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn display_sections(
|
||||||
|
obj: &Object,
|
||||||
|
diff: &ObjectDiff,
|
||||||
|
filter: SymbolFilter<'_>,
|
||||||
|
show_hidden_symbols: bool,
|
||||||
|
show_mapped_symbols: bool,
|
||||||
|
reverse_fn_order: bool,
|
||||||
|
) -> Vec<SectionDisplay> {
|
||||||
|
let mut mapping = BTreeSet::new();
|
||||||
|
let is_mapping_symbol = if let SymbolFilter::Mapping(_, _) = filter {
|
||||||
|
for mapping_diff in &diff.mapping_symbols {
|
||||||
|
let symbol = &obj.symbols[mapping_diff.symbol_index];
|
||||||
|
if !symbol_matches_filter(
|
||||||
|
symbol,
|
||||||
|
&mapping_diff.symbol_diff,
|
||||||
|
filter,
|
||||||
|
show_hidden_symbols,
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if !show_mapped_symbols {
|
||||||
|
let symbol_diff = &diff.symbols[mapping_diff.symbol_index];
|
||||||
|
if symbol_diff.target_symbol.is_some() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mapping.insert((symbol.section, mapping_diff.symbol_index));
|
||||||
|
}
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
for (symbol_idx, (symbol, symbol_diff)) in obj.symbols.iter().zip(&diff.symbols).enumerate()
|
||||||
|
{
|
||||||
|
if !symbol_matches_filter(symbol, symbol_diff, filter, show_hidden_symbols) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
mapping.insert((symbol.section, symbol_idx));
|
||||||
|
}
|
||||||
|
false
|
||||||
|
};
|
||||||
|
let num_sections = mapping.iter().map(|(section_idx, _)| *section_idx).dedup().count();
|
||||||
|
let mut sections = Vec::with_capacity(num_sections);
|
||||||
|
for (section_idx, group) in &mapping.iter().chunk_by(|(section_idx, _)| *section_idx) {
|
||||||
|
let mut symbols = group
|
||||||
|
.map(|&(_, symbol)| SectionDisplaySymbol { symbol, is_mapping_symbol })
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
if let Some(section_idx) = section_idx {
|
||||||
|
let section = &obj.sections[section_idx];
|
||||||
|
if section.kind == SectionKind::Unknown || section.flags.contains(SectionFlag::Hidden) {
|
||||||
|
// Skip unknown and hidden sections
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let section_diff = &diff.sections[section_idx];
|
||||||
|
if section.kind == SectionKind::Code && reverse_fn_order {
|
||||||
|
symbols.sort_by(|a, b| {
|
||||||
|
let a_symbol = &obj.symbols[a.symbol];
|
||||||
|
let b_symbol = &obj.symbols[b.symbol];
|
||||||
|
symbol_sort_reverse(a_symbol, b_symbol)
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
symbols.sort_by(|a, b| {
|
||||||
|
let a_symbol = &obj.symbols[a.symbol];
|
||||||
|
let b_symbol = &obj.symbols[b.symbol];
|
||||||
|
symbol_sort(a_symbol, b_symbol)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
sections.push(SectionDisplay {
|
||||||
|
id: section.id.clone(),
|
||||||
|
name: if section.flags.contains(SectionFlag::Combined) {
|
||||||
|
format!("{} [combined]", section.name)
|
||||||
|
} else {
|
||||||
|
section.name.clone()
|
||||||
|
},
|
||||||
|
size: section.size,
|
||||||
|
match_percent: section_diff.match_percent,
|
||||||
|
symbols,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Don't sort, preserve order of absolute symbols
|
||||||
|
sections.push(SectionDisplay {
|
||||||
|
id: ".comm".to_string(),
|
||||||
|
name: ".comm".to_string(),
|
||||||
|
size: 0,
|
||||||
|
match_percent: None,
|
||||||
|
symbols,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sections.sort_by(|a, b| a.id.cmp(&b.id));
|
||||||
|
sections
|
||||||
|
}
|
||||||
|
|
||||||
|
fn section_symbol_sort(a: &Symbol, b: &Symbol) -> Ordering {
|
||||||
|
if a.kind == SymbolKind::Section {
|
||||||
|
if b.kind != SymbolKind::Section {
|
||||||
|
return Ordering::Less;
|
||||||
|
}
|
||||||
|
} else if b.kind == SymbolKind::Section {
|
||||||
|
return Ordering::Greater;
|
||||||
|
}
|
||||||
|
Ordering::Equal
|
||||||
|
}
|
||||||
|
|
||||||
|
fn symbol_sort(a: &Symbol, b: &Symbol) -> Ordering {
|
||||||
|
section_symbol_sort(a, b).then(a.address.cmp(&b.address)).then(a.size.cmp(&b.size))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn symbol_sort_reverse(a: &Symbol, b: &Symbol) -> Ordering {
|
||||||
|
section_symbol_sort(a, b).then(b.address.cmp(&a.address)).then(b.size.cmp(&a.size))
|
||||||
|
}
|
||||||
|
@ -4,21 +4,19 @@ use alloc::{
|
|||||||
vec,
|
vec,
|
||||||
vec::Vec,
|
vec::Vec,
|
||||||
};
|
};
|
||||||
use core::ops::Range;
|
use core::{num::NonZeroU32, ops::Range};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
diff::{
|
diff::{
|
||||||
code::{diff_code, no_diff_code, process_code_symbol},
|
code::{diff_code, no_diff_code},
|
||||||
data::{
|
data::{
|
||||||
diff_bss_section, diff_bss_symbol, diff_data_section, diff_data_symbol,
|
diff_bss_section, diff_bss_symbol, diff_data_section, diff_data_symbol,
|
||||||
diff_generic_section, no_diff_symbol,
|
diff_generic_section,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
obj::{
|
obj::{InstructionRef, Object, SectionKind, Symbol, SymbolFlag},
|
||||||
ObjInfo, ObjIns, ObjReloc, ObjSection, ObjSectionKind, ObjSymbol, SymbolRef, SECTION_COMMON,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod code;
|
pub mod code;
|
||||||
@ -38,47 +36,44 @@ impl DiffObjConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ObjSectionDiff {
|
pub struct SectionDiff {
|
||||||
pub symbols: Vec<ObjSymbolDiff>,
|
// pub target_section: Option<usize>,
|
||||||
pub data_diff: Vec<ObjDataDiff>,
|
|
||||||
pub reloc_diff: Vec<ObjDataRelocDiff>,
|
|
||||||
pub match_percent: Option<f32>,
|
pub match_percent: Option<f32>,
|
||||||
}
|
pub data_diff: Vec<DataDiff>,
|
||||||
|
pub reloc_diff: Vec<DataRelocationDiff>,
|
||||||
impl ObjSectionDiff {
|
|
||||||
fn merge(&mut self, other: ObjSectionDiff) {
|
|
||||||
// symbols ignored
|
|
||||||
self.data_diff = other.data_diff;
|
|
||||||
self.reloc_diff = other.reloc_diff;
|
|
||||||
self.match_percent = other.match_percent;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct ObjSymbolDiff {
|
pub struct SymbolDiff {
|
||||||
/// The symbol ref this object
|
/// The symbol index in the _other_ object that this symbol was diffed against
|
||||||
pub symbol_ref: SymbolRef,
|
pub target_symbol: Option<usize>,
|
||||||
/// The symbol ref in the _other_ object that this symbol was diffed against
|
|
||||||
pub target_symbol: Option<SymbolRef>,
|
|
||||||
pub instructions: Vec<ObjInsDiff>,
|
|
||||||
pub match_percent: Option<f32>,
|
pub match_percent: Option<f32>,
|
||||||
|
pub diff_score: Option<(u64, u64)>,
|
||||||
|
pub instruction_rows: Vec<InstructionDiffRow>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct ObjInsDiff {
|
pub struct MappingSymbolDiff {
|
||||||
pub ins: Option<ObjIns>,
|
pub symbol_index: usize,
|
||||||
|
pub symbol_diff: SymbolDiff,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct InstructionDiffRow {
|
||||||
|
/// Instruction reference
|
||||||
|
pub ins_ref: Option<InstructionRef>,
|
||||||
/// Diff kind
|
/// Diff kind
|
||||||
pub kind: ObjInsDiffKind,
|
pub kind: InstructionDiffKind,
|
||||||
/// Branches from instruction
|
/// Branches from instruction(s)
|
||||||
pub branch_from: Option<ObjInsBranchFrom>,
|
pub branch_from: Option<InstructionBranchFrom>,
|
||||||
/// Branches to instruction
|
/// Branches to instruction
|
||||||
pub branch_to: Option<ObjInsBranchTo>,
|
pub branch_to: Option<InstructionBranchTo>,
|
||||||
/// Arg diffs (only contains non-PlainText args)
|
/// Arg diffs
|
||||||
pub arg_diff: Vec<Option<ObjInsArgDiff>>,
|
pub arg_diff: Vec<InstructionArgDiffIndex>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
|
||||||
pub enum ObjInsDiffKind {
|
pub enum InstructionDiffKind {
|
||||||
#[default]
|
#[default]
|
||||||
None,
|
None,
|
||||||
OpMismatch,
|
OpMismatch,
|
||||||
@ -89,22 +84,21 @@ pub enum ObjInsDiffKind {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct ObjDataDiff {
|
pub struct DataDiff {
|
||||||
pub data: Vec<u8>,
|
pub data: Vec<u8>,
|
||||||
pub kind: ObjDataDiffKind,
|
pub kind: DataDiffKind,
|
||||||
pub len: usize,
|
pub len: usize,
|
||||||
pub symbol: String,
|
pub symbol: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ObjDataRelocDiff {
|
pub struct DataRelocationDiff {
|
||||||
pub reloc: ObjReloc,
|
pub kind: DataDiffKind,
|
||||||
pub kind: ObjDataDiffKind,
|
|
||||||
pub range: Range<usize>,
|
pub range: Range<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
|
||||||
pub enum ObjDataDiffKind {
|
pub enum DataDiffKind {
|
||||||
#[default]
|
#[default]
|
||||||
None,
|
None,
|
||||||
Replace,
|
Replace,
|
||||||
@ -112,127 +106,102 @@ pub enum ObjDataDiffKind {
|
|||||||
Insert,
|
Insert,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
/// Index of the argument diff for coloring.
|
||||||
pub struct ObjInsArgDiff {
|
#[repr(transparent)]
|
||||||
/// Incrementing index for coloring
|
#[derive(Debug, Copy, Clone, Default)]
|
||||||
pub idx: usize,
|
pub struct InstructionArgDiffIndex(pub Option<NonZeroU32>);
|
||||||
|
|
||||||
|
impl InstructionArgDiffIndex {
|
||||||
|
pub const NONE: Self = Self(None);
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn new(idx: u32) -> Self {
|
||||||
|
Self(Some(unsafe { NonZeroU32::new_unchecked(idx.saturating_add(1)) }))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn get(&self) -> Option<u32> { self.0.map(|idx| idx.get() - 1) }
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn is_some(&self) -> bool { self.0.is_some() }
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn is_none(&self) -> bool { self.0.is_none() }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ObjInsBranchFrom {
|
pub struct InstructionBranchFrom {
|
||||||
/// Source instruction indices
|
/// Source instruction indices
|
||||||
pub ins_idx: Vec<usize>,
|
pub ins_idx: Vec<u32>,
|
||||||
/// Incrementing index for coloring
|
/// Incrementing index for coloring
|
||||||
pub branch_idx: usize,
|
pub branch_idx: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ObjInsBranchTo {
|
pub struct InstructionBranchTo {
|
||||||
/// Target instruction index
|
/// Target instruction index
|
||||||
pub ins_idx: usize,
|
pub ins_idx: u32,
|
||||||
/// Incrementing index for coloring
|
/// Incrementing index for coloring
|
||||||
pub branch_idx: usize,
|
pub branch_idx: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct ObjDiff {
|
pub struct ObjectDiff {
|
||||||
|
/// A list of all symbol diffs in the object.
|
||||||
|
pub symbols: Vec<SymbolDiff>,
|
||||||
/// A list of all section diffs in the object.
|
/// A list of all section diffs in the object.
|
||||||
pub sections: Vec<ObjSectionDiff>,
|
pub sections: Vec<SectionDiff>,
|
||||||
/// Common BSS symbols don't live in a section, so they're stored separately.
|
|
||||||
pub common: Vec<ObjSymbolDiff>,
|
|
||||||
/// If `selecting_left` or `selecting_right` is set, this is the list of symbols
|
/// If `selecting_left` or `selecting_right` is set, this is the list of symbols
|
||||||
/// that are being mapped to the other object.
|
/// that are being mapped to the other object.
|
||||||
pub mapping_symbols: Vec<ObjSymbolDiff>,
|
pub mapping_symbols: Vec<MappingSymbolDiff>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjDiff {
|
impl ObjectDiff {
|
||||||
pub fn new_from_obj(obj: &ObjInfo) -> Self {
|
pub fn new_from_obj(obj: &Object) -> Self {
|
||||||
let mut result = Self {
|
let mut result = Self {
|
||||||
|
symbols: Vec::with_capacity(obj.symbols.len()),
|
||||||
sections: Vec::with_capacity(obj.sections.len()),
|
sections: Vec::with_capacity(obj.sections.len()),
|
||||||
common: Vec::with_capacity(obj.common.len()),
|
|
||||||
mapping_symbols: vec![],
|
mapping_symbols: vec![],
|
||||||
};
|
};
|
||||||
for (section_idx, section) in obj.sections.iter().enumerate() {
|
for _ in obj.symbols.iter() {
|
||||||
let mut symbols = Vec::with_capacity(section.symbols.len());
|
result.symbols.push(SymbolDiff {
|
||||||
for (symbol_idx, _) in section.symbols.iter().enumerate() {
|
|
||||||
symbols.push(ObjSymbolDiff {
|
|
||||||
symbol_ref: SymbolRef { section_idx, symbol_idx },
|
|
||||||
target_symbol: None,
|
target_symbol: None,
|
||||||
instructions: vec![],
|
|
||||||
match_percent: None,
|
match_percent: None,
|
||||||
|
diff_score: None,
|
||||||
|
instruction_rows: vec![],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
result.sections.push(ObjSectionDiff {
|
for _ in obj.sections.iter() {
|
||||||
symbols,
|
result.sections.push(SectionDiff {
|
||||||
data_diff: vec![ObjDataDiff {
|
// target_section: None,
|
||||||
data: section.data.clone(),
|
match_percent: None,
|
||||||
kind: ObjDataDiffKind::None,
|
data_diff: vec![],
|
||||||
len: section.data.len(),
|
|
||||||
symbol: section.name.clone(),
|
|
||||||
}],
|
|
||||||
reloc_diff: vec![],
|
reloc_diff: vec![],
|
||||||
match_percent: None,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
for (symbol_idx, _) in obj.common.iter().enumerate() {
|
|
||||||
result.common.push(ObjSymbolDiff {
|
|
||||||
symbol_ref: SymbolRef { section_idx: SECTION_COMMON, symbol_idx },
|
|
||||||
target_symbol: None,
|
|
||||||
instructions: vec![],
|
|
||||||
match_percent: None,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn section_diff(&self, section_idx: usize) -> &ObjSectionDiff {
|
|
||||||
&self.sections[section_idx]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[derive(Debug, Default)]
|
||||||
pub fn section_diff_mut(&mut self, section_idx: usize) -> &mut ObjSectionDiff {
|
|
||||||
&mut self.sections[section_idx]
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn symbol_diff(&self, symbol_ref: SymbolRef) -> &ObjSymbolDiff {
|
|
||||||
if symbol_ref.section_idx == SECTION_COMMON {
|
|
||||||
&self.common[symbol_ref.symbol_idx]
|
|
||||||
} else {
|
|
||||||
&self.section_diff(symbol_ref.section_idx).symbols[symbol_ref.symbol_idx]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn symbol_diff_mut(&mut self, symbol_ref: SymbolRef) -> &mut ObjSymbolDiff {
|
|
||||||
if symbol_ref.section_idx == SECTION_COMMON {
|
|
||||||
&mut self.common[symbol_ref.symbol_idx]
|
|
||||||
} else {
|
|
||||||
&mut self.section_diff_mut(symbol_ref.section_idx).symbols[symbol_ref.symbol_idx]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct DiffObjsResult {
|
pub struct DiffObjsResult {
|
||||||
pub left: Option<ObjDiff>,
|
pub left: Option<ObjectDiff>,
|
||||||
pub right: Option<ObjDiff>,
|
pub right: Option<ObjectDiff>,
|
||||||
pub prev: Option<ObjDiff>,
|
pub prev: Option<ObjectDiff>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn diff_objs(
|
pub fn diff_objs(
|
||||||
|
left: Option<&Object>,
|
||||||
|
right: Option<&Object>,
|
||||||
|
prev: Option<&Object>,
|
||||||
diff_config: &DiffObjConfig,
|
diff_config: &DiffObjConfig,
|
||||||
mapping_config: &MappingConfig,
|
mapping_config: &MappingConfig,
|
||||||
left: Option<&ObjInfo>,
|
|
||||||
right: Option<&ObjInfo>,
|
|
||||||
prev: Option<&ObjInfo>,
|
|
||||||
) -> Result<DiffObjsResult> {
|
) -> Result<DiffObjsResult> {
|
||||||
let symbol_matches = matching_symbols(left, right, prev, mapping_config)?;
|
let symbol_matches = matching_symbols(left, right, prev, mapping_config)?;
|
||||||
let section_matches = matching_sections(left, right)?;
|
let section_matches = matching_sections(left, right)?;
|
||||||
let mut left = left.map(|p| (p, ObjDiff::new_from_obj(p)));
|
let mut left = left.map(|p| (p, ObjectDiff::new_from_obj(p)));
|
||||||
let mut right = right.map(|p| (p, ObjDiff::new_from_obj(p)));
|
let mut right = right.map(|p| (p, ObjectDiff::new_from_obj(p)));
|
||||||
let mut prev = prev.map(|p| (p, ObjDiff::new_from_obj(p)));
|
let mut prev = prev.map(|p| (p, ObjectDiff::new_from_obj(p)));
|
||||||
|
|
||||||
for symbol_match in symbol_matches {
|
for symbol_match in symbol_matches {
|
||||||
match symbol_match {
|
match symbol_match {
|
||||||
@ -245,87 +214,76 @@ pub fn diff_objs(
|
|||||||
let (left_obj, left_out) = left.as_mut().unwrap();
|
let (left_obj, left_out) = left.as_mut().unwrap();
|
||||||
let (right_obj, right_out) = right.as_mut().unwrap();
|
let (right_obj, right_out) = right.as_mut().unwrap();
|
||||||
match section_kind {
|
match section_kind {
|
||||||
ObjSectionKind::Code => {
|
SectionKind::Code => {
|
||||||
let left_code =
|
|
||||||
process_code_symbol(left_obj, left_symbol_ref, diff_config)?;
|
|
||||||
let right_code =
|
|
||||||
process_code_symbol(right_obj, right_symbol_ref, diff_config)?;
|
|
||||||
let (left_diff, right_diff) = diff_code(
|
let (left_diff, right_diff) = diff_code(
|
||||||
left_obj,
|
left_obj,
|
||||||
right_obj,
|
right_obj,
|
||||||
&left_code,
|
|
||||||
&right_code,
|
|
||||||
left_symbol_ref,
|
left_symbol_ref,
|
||||||
right_symbol_ref,
|
right_symbol_ref,
|
||||||
diff_config,
|
diff_config,
|
||||||
)?;
|
)?;
|
||||||
*left_out.symbol_diff_mut(left_symbol_ref) = left_diff;
|
left_out.symbols[left_symbol_ref] = left_diff;
|
||||||
*right_out.symbol_diff_mut(right_symbol_ref) = right_diff;
|
right_out.symbols[right_symbol_ref] = right_diff;
|
||||||
|
|
||||||
if let Some(prev_symbol_ref) = prev_symbol_ref {
|
if let Some(prev_symbol_ref) = prev_symbol_ref {
|
||||||
let (prev_obj, prev_out) = prev.as_mut().unwrap();
|
let (_prev_obj, prev_out) = prev.as_mut().unwrap();
|
||||||
let prev_code =
|
|
||||||
process_code_symbol(prev_obj, prev_symbol_ref, diff_config)?;
|
|
||||||
let (_, prev_diff) = diff_code(
|
let (_, prev_diff) = diff_code(
|
||||||
left_obj,
|
left_obj,
|
||||||
right_obj,
|
right_obj,
|
||||||
&right_code,
|
|
||||||
&prev_code,
|
|
||||||
right_symbol_ref,
|
right_symbol_ref,
|
||||||
prev_symbol_ref,
|
prev_symbol_ref,
|
||||||
diff_config,
|
diff_config,
|
||||||
)?;
|
)?;
|
||||||
*prev_out.symbol_diff_mut(prev_symbol_ref) = prev_diff;
|
prev_out.symbols[prev_symbol_ref] = prev_diff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ObjSectionKind::Data => {
|
SectionKind::Data => {
|
||||||
let (left_diff, right_diff) = diff_data_symbol(
|
let (left_diff, right_diff) = diff_data_symbol(
|
||||||
left_obj,
|
left_obj,
|
||||||
right_obj,
|
right_obj,
|
||||||
left_symbol_ref,
|
left_symbol_ref,
|
||||||
right_symbol_ref,
|
right_symbol_ref,
|
||||||
)?;
|
)?;
|
||||||
*left_out.symbol_diff_mut(left_symbol_ref) = left_diff;
|
left_out.symbols[left_symbol_ref] = left_diff;
|
||||||
*right_out.symbol_diff_mut(right_symbol_ref) = right_diff;
|
right_out.symbols[right_symbol_ref] = right_diff;
|
||||||
}
|
}
|
||||||
ObjSectionKind::Bss => {
|
SectionKind::Bss | SectionKind::Common => {
|
||||||
let (left_diff, right_diff) = diff_bss_symbol(
|
let (left_diff, right_diff) = diff_bss_symbol(
|
||||||
left_obj,
|
left_obj,
|
||||||
right_obj,
|
right_obj,
|
||||||
left_symbol_ref,
|
left_symbol_ref,
|
||||||
right_symbol_ref,
|
right_symbol_ref,
|
||||||
)?;
|
)?;
|
||||||
*left_out.symbol_diff_mut(left_symbol_ref) = left_diff;
|
left_out.symbols[left_symbol_ref] = left_diff;
|
||||||
*right_out.symbol_diff_mut(right_symbol_ref) = right_diff;
|
right_out.symbols[right_symbol_ref] = right_diff;
|
||||||
}
|
}
|
||||||
|
SectionKind::Unknown => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SymbolMatch { left: Some(left_symbol_ref), right: None, prev: _, section_kind } => {
|
SymbolMatch { left: Some(left_symbol_ref), right: None, prev: _, section_kind } => {
|
||||||
let (left_obj, left_out) = left.as_mut().unwrap();
|
let (left_obj, left_out) = left.as_mut().unwrap();
|
||||||
match section_kind {
|
match section_kind {
|
||||||
ObjSectionKind::Code => {
|
SectionKind::Code => {
|
||||||
let code = process_code_symbol(left_obj, left_symbol_ref, diff_config)?;
|
left_out.symbols[left_symbol_ref] =
|
||||||
*left_out.symbol_diff_mut(left_symbol_ref) =
|
no_diff_code(left_obj, left_symbol_ref, diff_config)?;
|
||||||
no_diff_code(&code, left_symbol_ref)?;
|
|
||||||
}
|
}
|
||||||
ObjSectionKind::Data | ObjSectionKind::Bss => {
|
SectionKind::Data | SectionKind::Bss | SectionKind::Common => {
|
||||||
*left_out.symbol_diff_mut(left_symbol_ref) =
|
// Nothing needs to be done
|
||||||
no_diff_symbol(left_obj, left_symbol_ref);
|
|
||||||
}
|
}
|
||||||
|
SectionKind::Unknown => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SymbolMatch { left: None, right: Some(right_symbol_ref), prev: _, section_kind } => {
|
SymbolMatch { left: None, right: Some(right_symbol_ref), prev: _, section_kind } => {
|
||||||
let (right_obj, right_out) = right.as_mut().unwrap();
|
let (right_obj, right_out) = right.as_mut().unwrap();
|
||||||
match section_kind {
|
match section_kind {
|
||||||
ObjSectionKind::Code => {
|
SectionKind::Code => {
|
||||||
let code = process_code_symbol(right_obj, right_symbol_ref, diff_config)?;
|
right_out.symbols[right_symbol_ref] =
|
||||||
*right_out.symbol_diff_mut(right_symbol_ref) =
|
no_diff_code(right_obj, right_symbol_ref, diff_config)?;
|
||||||
no_diff_code(&code, right_symbol_ref)?;
|
|
||||||
}
|
}
|
||||||
ObjSectionKind::Data | ObjSectionKind::Bss => {
|
SectionKind::Data | SectionKind::Bss | SectionKind::Common => {
|
||||||
*right_out.symbol_diff_mut(right_symbol_ref) =
|
// Nothing needs to be done
|
||||||
no_diff_symbol(right_obj, right_symbol_ref);
|
|
||||||
}
|
}
|
||||||
|
SectionKind::Unknown => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SymbolMatch { left: None, right: None, .. } => {
|
SymbolMatch { left: None, right: None, .. } => {
|
||||||
@ -343,47 +301,44 @@ pub fn diff_objs(
|
|||||||
{
|
{
|
||||||
let (left_obj, left_out) = left.as_mut().unwrap();
|
let (left_obj, left_out) = left.as_mut().unwrap();
|
||||||
let (right_obj, right_out) = right.as_mut().unwrap();
|
let (right_obj, right_out) = right.as_mut().unwrap();
|
||||||
let left_section = &left_obj.sections[left_section_idx];
|
|
||||||
let right_section = &right_obj.sections[right_section_idx];
|
|
||||||
match section_kind {
|
match section_kind {
|
||||||
ObjSectionKind::Code => {
|
SectionKind::Code => {
|
||||||
let left_section_diff = left_out.section_diff(left_section_idx);
|
|
||||||
let right_section_diff = right_out.section_diff(right_section_idx);
|
|
||||||
let (left_diff, right_diff) = diff_generic_section(
|
let (left_diff, right_diff) = diff_generic_section(
|
||||||
left_section,
|
left_obj,
|
||||||
right_section,
|
right_obj,
|
||||||
left_section_diff,
|
left_out,
|
||||||
right_section_diff,
|
right_out,
|
||||||
|
left_section_idx,
|
||||||
|
right_section_idx,
|
||||||
)?;
|
)?;
|
||||||
left_out.section_diff_mut(left_section_idx).merge(left_diff);
|
left_out.sections[left_section_idx] = left_diff;
|
||||||
right_out.section_diff_mut(right_section_idx).merge(right_diff);
|
right_out.sections[right_section_idx] = right_diff;
|
||||||
}
|
}
|
||||||
ObjSectionKind::Data => {
|
SectionKind::Data => {
|
||||||
let left_section_diff = left_out.section_diff(left_section_idx);
|
|
||||||
let right_section_diff = right_out.section_diff(right_section_idx);
|
|
||||||
let (left_diff, right_diff) = diff_data_section(
|
let (left_diff, right_diff) = diff_data_section(
|
||||||
left_obj,
|
left_obj,
|
||||||
right_obj,
|
right_obj,
|
||||||
left_section,
|
left_out,
|
||||||
right_section,
|
right_out,
|
||||||
left_section_diff,
|
left_section_idx,
|
||||||
right_section_diff,
|
right_section_idx,
|
||||||
)?;
|
)?;
|
||||||
left_out.section_diff_mut(left_section_idx).merge(left_diff);
|
left_out.sections[left_section_idx] = left_diff;
|
||||||
right_out.section_diff_mut(right_section_idx).merge(right_diff);
|
right_out.sections[right_section_idx] = right_diff;
|
||||||
}
|
}
|
||||||
ObjSectionKind::Bss => {
|
SectionKind::Bss | SectionKind::Common => {
|
||||||
let left_section_diff = left_out.section_diff(left_section_idx);
|
|
||||||
let right_section_diff = right_out.section_diff(right_section_idx);
|
|
||||||
let (left_diff, right_diff) = diff_bss_section(
|
let (left_diff, right_diff) = diff_bss_section(
|
||||||
left_section,
|
left_obj,
|
||||||
right_section,
|
right_obj,
|
||||||
left_section_diff,
|
left_out,
|
||||||
right_section_diff,
|
right_out,
|
||||||
|
left_section_idx,
|
||||||
|
right_section_idx,
|
||||||
)?;
|
)?;
|
||||||
left_out.section_diff_mut(left_section_idx).merge(left_diff);
|
left_out.sections[left_section_idx] = left_diff;
|
||||||
right_out.section_diff_mut(right_section_idx).merge(right_diff);
|
right_out.sections[right_section_idx] = right_diff;
|
||||||
}
|
}
|
||||||
|
SectionKind::Unknown => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -410,54 +365,46 @@ pub fn diff_objs(
|
|||||||
/// symbols in the other object that match the selected symbol's section and kind. This allows
|
/// symbols in the other object that match the selected symbol's section and kind. This allows
|
||||||
/// us to display match percentages for all symbols in the other object that could be selected.
|
/// us to display match percentages for all symbols in the other object that could be selected.
|
||||||
fn generate_mapping_symbols(
|
fn generate_mapping_symbols(
|
||||||
base_obj: &ObjInfo,
|
base_obj: &Object,
|
||||||
base_name: &str,
|
base_name: &str,
|
||||||
target_obj: &ObjInfo,
|
target_obj: &Object,
|
||||||
target_out: &mut ObjDiff,
|
target_out: &mut ObjectDiff,
|
||||||
config: &DiffObjConfig,
|
config: &DiffObjConfig,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let Some(base_symbol_ref) = symbol_ref_by_name(base_obj, base_name) else {
|
let Some(base_symbol_ref) = symbol_ref_by_name(base_obj, base_name) else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
let (base_section, _base_symbol) = base_obj.section_symbol(base_symbol_ref);
|
let base_section_kind = symbol_section_kind(base_obj, &base_obj.symbols[base_symbol_ref]);
|
||||||
let Some(base_section) = base_section else {
|
for (target_symbol_index, target_symbol) in target_obj.symbols.iter().enumerate() {
|
||||||
return Ok(());
|
if symbol_section_kind(target_obj, target_symbol) != base_section_kind {
|
||||||
};
|
continue;
|
||||||
let base_code = match base_section.kind {
|
|
||||||
ObjSectionKind::Code => Some(process_code_symbol(base_obj, base_symbol_ref, config)?),
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
for (target_section_index, target_section) in
|
|
||||||
target_obj.sections.iter().enumerate().filter(|(_, s)| s.kind == base_section.kind)
|
|
||||||
{
|
|
||||||
for (target_symbol_index, _target_symbol) in target_section.symbols.iter().enumerate() {
|
|
||||||
let target_symbol_ref =
|
|
||||||
SymbolRef { section_idx: target_section_index, symbol_idx: target_symbol_index };
|
|
||||||
match base_section.kind {
|
|
||||||
ObjSectionKind::Code => {
|
|
||||||
let target_code = process_code_symbol(target_obj, target_symbol_ref, config)?;
|
|
||||||
let (left_diff, _right_diff) = diff_code(
|
|
||||||
target_obj,
|
|
||||||
base_obj,
|
|
||||||
&target_code,
|
|
||||||
base_code.as_ref().unwrap(),
|
|
||||||
target_symbol_ref,
|
|
||||||
base_symbol_ref,
|
|
||||||
config,
|
|
||||||
)?;
|
|
||||||
target_out.mapping_symbols.push(left_diff);
|
|
||||||
}
|
}
|
||||||
ObjSectionKind::Data => {
|
match base_section_kind {
|
||||||
|
SectionKind::Code => {
|
||||||
let (left_diff, _right_diff) =
|
let (left_diff, _right_diff) =
|
||||||
diff_data_symbol(target_obj, base_obj, target_symbol_ref, base_symbol_ref)?;
|
diff_code(target_obj, base_obj, target_symbol_index, base_symbol_ref, config)?;
|
||||||
target_out.mapping_symbols.push(left_diff);
|
target_out.mapping_symbols.push(MappingSymbolDiff {
|
||||||
|
symbol_index: target_symbol_index,
|
||||||
|
symbol_diff: left_diff,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
ObjSectionKind::Bss => {
|
SectionKind::Data => {
|
||||||
let (left_diff, _right_diff) =
|
let (left_diff, _right_diff) =
|
||||||
diff_bss_symbol(target_obj, base_obj, target_symbol_ref, base_symbol_ref)?;
|
diff_data_symbol(target_obj, base_obj, target_symbol_index, base_symbol_ref)?;
|
||||||
target_out.mapping_symbols.push(left_diff);
|
target_out.mapping_symbols.push(MappingSymbolDiff {
|
||||||
|
symbol_index: target_symbol_index,
|
||||||
|
symbol_diff: left_diff,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
SectionKind::Bss | SectionKind::Common => {
|
||||||
|
let (left_diff, _right_diff) =
|
||||||
|
diff_bss_symbol(target_obj, base_obj, target_symbol_index, base_symbol_ref)?;
|
||||||
|
target_out.mapping_symbols.push(MappingSymbolDiff {
|
||||||
|
symbol_index: target_symbol_index,
|
||||||
|
symbol_diff: left_diff,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
SectionKind::Unknown => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -465,17 +412,17 @@ fn generate_mapping_symbols(
|
|||||||
|
|
||||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||||
struct SymbolMatch {
|
struct SymbolMatch {
|
||||||
left: Option<SymbolRef>,
|
left: Option<usize>,
|
||||||
right: Option<SymbolRef>,
|
right: Option<usize>,
|
||||||
prev: Option<SymbolRef>,
|
prev: Option<usize>,
|
||||||
section_kind: ObjSectionKind,
|
section_kind: SectionKind,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||||
struct SectionMatch {
|
struct SectionMatch {
|
||||||
left: Option<usize>,
|
left: Option<usize>,
|
||||||
right: Option<usize>,
|
right: Option<usize>,
|
||||||
section_kind: ObjSectionKind,
|
section_kind: SectionKind,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
@ -489,23 +436,16 @@ pub struct MappingConfig {
|
|||||||
pub selecting_right: Option<String>,
|
pub selecting_right: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn symbol_ref_by_name(obj: &ObjInfo, name: &str) -> Option<SymbolRef> {
|
fn symbol_ref_by_name(obj: &Object, name: &str) -> Option<usize> {
|
||||||
for (section_idx, section) in obj.sections.iter().enumerate() {
|
obj.symbols.iter().position(|s| s.name == name)
|
||||||
for (symbol_idx, symbol) in section.symbols.iter().enumerate() {
|
|
||||||
if symbol.name == name {
|
|
||||||
return Some(SymbolRef { section_idx, symbol_idx });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_symbol_mappings(
|
fn apply_symbol_mappings(
|
||||||
left: &ObjInfo,
|
left: &Object,
|
||||||
right: &ObjInfo,
|
right: &Object,
|
||||||
mapping_config: &MappingConfig,
|
mapping_config: &MappingConfig,
|
||||||
left_used: &mut BTreeSet<SymbolRef>,
|
left_used: &mut BTreeSet<usize>,
|
||||||
right_used: &mut BTreeSet<SymbolRef>,
|
right_used: &mut BTreeSet<usize>,
|
||||||
matches: &mut Vec<SymbolMatch>,
|
matches: &mut Vec<SymbolMatch>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
// If we're selecting a symbol to use as a comparison, mark it as used
|
// If we're selecting a symbol to use as a comparison, mark it as used
|
||||||
@ -523,47 +463,57 @@ fn apply_symbol_mappings(
|
|||||||
|
|
||||||
// Apply manual symbol mappings
|
// Apply manual symbol mappings
|
||||||
for (left_name, right_name) in &mapping_config.mappings {
|
for (left_name, right_name) in &mapping_config.mappings {
|
||||||
let Some(left_symbol) = symbol_ref_by_name(left, left_name) else {
|
let Some(left_symbol_index) = symbol_ref_by_name(left, left_name) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
if left_used.contains(&left_symbol) {
|
if left_used.contains(&left_symbol_index) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let Some(right_symbol) = symbol_ref_by_name(right, right_name) else {
|
let Some(right_symbol_index) = symbol_ref_by_name(right, right_name) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
if right_used.contains(&right_symbol) {
|
if right_used.contains(&right_symbol_index) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let left_section = &left.sections[left_symbol.section_idx];
|
let left_section_kind = left
|
||||||
let right_section = &right.sections[right_symbol.section_idx];
|
.symbols
|
||||||
if left_section.kind != right_section.kind {
|
.get(left_symbol_index)
|
||||||
|
.and_then(|s| s.section)
|
||||||
|
.and_then(|section_index| left.sections.get(section_index))
|
||||||
|
.map_or(SectionKind::Unknown, |s| s.kind);
|
||||||
|
let right_section_kind = right
|
||||||
|
.symbols
|
||||||
|
.get(right_symbol_index)
|
||||||
|
.and_then(|s| s.section)
|
||||||
|
.and_then(|section_index| right.sections.get(section_index))
|
||||||
|
.map_or(SectionKind::Unknown, |s| s.kind);
|
||||||
|
if left_section_kind != right_section_kind {
|
||||||
log::warn!(
|
log::warn!(
|
||||||
"Symbol section kind mismatch: {} ({:?}) vs {} ({:?})",
|
"Symbol section kind mismatch: {} ({:?}) vs {} ({:?})",
|
||||||
left_name,
|
left_name,
|
||||||
left_section.kind,
|
left_section_kind,
|
||||||
right_name,
|
right_name,
|
||||||
right_section.kind
|
right_section_kind
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
matches.push(SymbolMatch {
|
matches.push(SymbolMatch {
|
||||||
left: Some(left_symbol),
|
left: Some(left_symbol_index),
|
||||||
right: Some(right_symbol),
|
right: Some(right_symbol_index),
|
||||||
prev: None, // TODO
|
prev: None, // TODO
|
||||||
section_kind: left_section.kind,
|
section_kind: left_section_kind,
|
||||||
});
|
});
|
||||||
left_used.insert(left_symbol);
|
left_used.insert(left_symbol_index);
|
||||||
right_used.insert(right_symbol);
|
right_used.insert(right_symbol_index);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Find matching symbols between each object.
|
/// Find matching symbols between each object.
|
||||||
fn matching_symbols(
|
fn matching_symbols(
|
||||||
left: Option<&ObjInfo>,
|
left: Option<&Object>,
|
||||||
right: Option<&ObjInfo>,
|
right: Option<&Object>,
|
||||||
prev: Option<&ObjInfo>,
|
prev: Option<&Object>,
|
||||||
mappings: &MappingConfig,
|
mappings: &MappingConfig,
|
||||||
) -> Result<Vec<SymbolMatch>> {
|
) -> Result<Vec<SymbolMatch>> {
|
||||||
let mut matches = Vec::new();
|
let mut matches = Vec::new();
|
||||||
@ -580,34 +530,19 @@ fn matching_symbols(
|
|||||||
&mut matches,
|
&mut matches,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
for (section_idx, section) in left.sections.iter().enumerate() {
|
for (symbol_idx, symbol) in left.symbols.iter().enumerate() {
|
||||||
for (symbol_idx, symbol) in section.symbols.iter().enumerate() {
|
let section_kind = symbol_section_kind(left, symbol);
|
||||||
let symbol_ref = SymbolRef { section_idx, symbol_idx };
|
if section_kind == SectionKind::Unknown {
|
||||||
if left_used.contains(&symbol_ref) {
|
continue;
|
||||||
|
}
|
||||||
|
if left_used.contains(&symbol_idx) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let symbol_match = SymbolMatch {
|
let symbol_match = SymbolMatch {
|
||||||
left: Some(symbol_ref),
|
left: Some(symbol_idx),
|
||||||
right: find_symbol(right, symbol, section, Some(&right_used)),
|
right: find_symbol(right, left, symbol, Some(&right_used)),
|
||||||
prev: find_symbol(prev, symbol, section, None),
|
prev: find_symbol(prev, left, symbol, None),
|
||||||
section_kind: section.kind,
|
section_kind,
|
||||||
};
|
|
||||||
matches.push(symbol_match);
|
|
||||||
if let Some(right) = symbol_match.right {
|
|
||||||
right_used.insert(right);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (symbol_idx, symbol) in left.common.iter().enumerate() {
|
|
||||||
let symbol_ref = SymbolRef { section_idx: SECTION_COMMON, symbol_idx };
|
|
||||||
if left_used.contains(&symbol_ref) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let symbol_match = SymbolMatch {
|
|
||||||
left: Some(symbol_ref),
|
|
||||||
right: find_common_symbol(right, symbol),
|
|
||||||
prev: find_common_symbol(prev, symbol),
|
|
||||||
section_kind: ObjSectionKind::Bss,
|
|
||||||
};
|
};
|
||||||
matches.push(symbol_match);
|
matches.push(symbol_match);
|
||||||
if let Some(right) = symbol_match.right {
|
if let Some(right) = symbol_match.right {
|
||||||
@ -616,83 +551,84 @@ fn matching_symbols(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(right) = right {
|
if let Some(right) = right {
|
||||||
for (section_idx, section) in right.sections.iter().enumerate() {
|
for (symbol_idx, symbol) in right.symbols.iter().enumerate() {
|
||||||
for (symbol_idx, symbol) in section.symbols.iter().enumerate() {
|
let section_kind = symbol_section_kind(right, symbol);
|
||||||
let symbol_ref = SymbolRef { section_idx, symbol_idx };
|
if section_kind == SectionKind::Unknown {
|
||||||
if right_used.contains(&symbol_ref) {
|
continue;
|
||||||
|
}
|
||||||
|
if right_used.contains(&symbol_idx) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
matches.push(SymbolMatch {
|
matches.push(SymbolMatch {
|
||||||
left: None,
|
left: None,
|
||||||
right: Some(symbol_ref),
|
right: Some(symbol_idx),
|
||||||
prev: find_symbol(prev, symbol, section, None),
|
prev: find_symbol(prev, right, symbol, None),
|
||||||
section_kind: section.kind,
|
section_kind,
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (symbol_idx, symbol) in right.common.iter().enumerate() {
|
|
||||||
let symbol_ref = SymbolRef { section_idx: SECTION_COMMON, symbol_idx };
|
|
||||||
if right_used.contains(&symbol_ref) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
matches.push(SymbolMatch {
|
|
||||||
left: None,
|
|
||||||
right: Some(symbol_ref),
|
|
||||||
prev: find_common_symbol(prev, symbol),
|
|
||||||
section_kind: ObjSectionKind::Bss,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(matches)
|
Ok(matches)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unmatched_symbols<'section, 'used>(
|
fn unmatched_symbols<'obj, 'used>(
|
||||||
section: &'section ObjSection,
|
obj: &'obj Object,
|
||||||
section_idx: usize,
|
used: Option<&'used BTreeSet<usize>>,
|
||||||
used: Option<&'used BTreeSet<SymbolRef>>,
|
) -> impl Iterator<Item = (usize, &'obj Symbol)> + 'used
|
||||||
) -> impl Iterator<Item = (usize, &'section ObjSymbol)> + 'used
|
|
||||||
where
|
where
|
||||||
'section: 'used,
|
'obj: 'used,
|
||||||
{
|
{
|
||||||
section.symbols.iter().enumerate().filter(move |&(symbol_idx, _)| {
|
obj.symbols.iter().enumerate().filter(move |&(symbol_idx, _)| {
|
||||||
// Skip symbols that have already been matched
|
// Skip symbols that have already been matched
|
||||||
!used.map(|u| u.contains(&SymbolRef { section_idx, symbol_idx })).unwrap_or(false)
|
!used.is_some_and(|u| u.contains(&symbol_idx))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn symbol_section<'obj>(obj: &'obj Object, symbol: &Symbol) -> Option<(&'obj str, SectionKind)> {
|
||||||
|
if let Some(section) = symbol.section.and_then(|section_idx| obj.sections.get(section_idx)) {
|
||||||
|
Some((section.name.as_str(), section.kind))
|
||||||
|
} else if symbol.flags.contains(SymbolFlag::Common) {
|
||||||
|
Some((".comm", SectionKind::Common))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn symbol_section_kind(obj: &Object, symbol: &Symbol) -> SectionKind {
|
||||||
|
match symbol.section {
|
||||||
|
Some(section_index) => obj.sections[section_index].kind,
|
||||||
|
None if symbol.flags.contains(SymbolFlag::Common) => SectionKind::Common,
|
||||||
|
None => SectionKind::Unknown,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn find_symbol(
|
fn find_symbol(
|
||||||
obj: Option<&ObjInfo>,
|
obj: Option<&Object>,
|
||||||
in_symbol: &ObjSymbol,
|
in_obj: &Object,
|
||||||
in_section: &ObjSection,
|
in_symbol: &Symbol,
|
||||||
used: Option<&BTreeSet<SymbolRef>>,
|
used: Option<&BTreeSet<usize>>,
|
||||||
) -> Option<SymbolRef> {
|
) -> Option<usize> {
|
||||||
let obj = obj?;
|
let obj = obj?;
|
||||||
|
let (section_name, section_kind) = symbol_section(in_obj, in_symbol)?;
|
||||||
// Try to find an exact name match
|
// Try to find an exact name match
|
||||||
for (section_idx, section) in obj.sections.iter().enumerate() {
|
if let Some((symbol_idx, _)) = unmatched_symbols(obj, used).find(|(_, symbol)| {
|
||||||
if section.kind != in_section.kind {
|
symbol.name == in_symbol.name && symbol_section_kind(obj, symbol) == section_kind
|
||||||
continue;
|
}) {
|
||||||
}
|
return Some(symbol_idx);
|
||||||
if let Some((symbol_idx, _)) = unmatched_symbols(section, section_idx, used)
|
|
||||||
.find(|(_, symbol)| symbol.name == in_symbol.name)
|
|
||||||
{
|
|
||||||
return Some(SymbolRef { section_idx, symbol_idx });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Match compiler-generated symbols against each other (e.g. @251 -> @60)
|
// Match compiler-generated symbols against each other (e.g. @251 -> @60)
|
||||||
// If they are at the same address in the same section
|
// If they are at the same address in the same section
|
||||||
if in_symbol.name.starts_with('@')
|
if in_symbol.name.starts_with('@')
|
||||||
&& matches!(in_section.kind, ObjSectionKind::Data | ObjSectionKind::Bss)
|
&& matches!(section_kind, SectionKind::Data | SectionKind::Bss)
|
||||||
{
|
{
|
||||||
if let Some((section_idx, section)) =
|
if let Some((symbol_idx, _)) = unmatched_symbols(obj, used).find(|(_, symbol)| {
|
||||||
obj.sections.iter().enumerate().find(|(_, s)| s.name == in_section.name)
|
let Some(section_index) = symbol.section else {
|
||||||
{
|
return false;
|
||||||
if let Some((symbol_idx, _)) =
|
};
|
||||||
unmatched_symbols(section, section_idx, used).find(|(_, symbol)| {
|
symbol.name.starts_with('@')
|
||||||
symbol.address == in_symbol.address && symbol.name.starts_with('@')
|
&& symbol.address == in_symbol.address
|
||||||
})
|
&& obj.sections[section_index].name == section_name
|
||||||
{
|
}) {
|
||||||
return Some(SymbolRef { section_idx, symbol_idx });
|
return Some(symbol_idx);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Match Metrowerks symbol$1234 against symbol$2345
|
// Match Metrowerks symbol$1234 against symbol$2345
|
||||||
@ -700,41 +636,29 @@ fn find_symbol(
|
|||||||
if !suffix.chars().all(char::is_numeric) {
|
if !suffix.chars().all(char::is_numeric) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
for (section_idx, section) in obj.sections.iter().enumerate() {
|
if let Some((symbol_idx, _)) = unmatched_symbols(obj, used).find(|&(_, symbol)| {
|
||||||
if section.kind != in_section.kind {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if let Some((symbol_idx, _)) =
|
|
||||||
unmatched_symbols(section, section_idx, used).find(|&(_, symbol)| {
|
|
||||||
if let Some((p, s)) = symbol.name.split_once('$') {
|
if let Some((p, s)) = symbol.name.split_once('$') {
|
||||||
prefix == p && s.chars().all(char::is_numeric)
|
prefix == p
|
||||||
|
&& s.chars().all(char::is_numeric)
|
||||||
|
&& symbol_section_kind(obj, symbol) == section_kind
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
})
|
}) {
|
||||||
{
|
return Some(symbol_idx);
|
||||||
return Some(SymbolRef { section_idx, symbol_idx });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_common_symbol(obj: Option<&ObjInfo>, in_symbol: &ObjSymbol) -> Option<SymbolRef> {
|
|
||||||
let obj = obj?;
|
|
||||||
for (symbol_idx, symbol) in obj.common.iter().enumerate() {
|
|
||||||
if symbol.name == in_symbol.name {
|
|
||||||
return Some(SymbolRef { section_idx: SECTION_COMMON, symbol_idx });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Find matching sections between each object.
|
/// Find matching sections between each object.
|
||||||
fn matching_sections(left: Option<&ObjInfo>, right: Option<&ObjInfo>) -> Result<Vec<SectionMatch>> {
|
fn matching_sections(left: Option<&Object>, right: Option<&Object>) -> Result<Vec<SectionMatch>> {
|
||||||
let mut matches = Vec::new();
|
let mut matches = Vec::new();
|
||||||
if let Some(left) = left {
|
if let Some(left) = left {
|
||||||
for (section_idx, section) in left.sections.iter().enumerate() {
|
for (section_idx, section) in left.sections.iter().enumerate() {
|
||||||
|
if section.kind == SectionKind::Unknown {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
matches.push(SectionMatch {
|
matches.push(SectionMatch {
|
||||||
left: Some(section_idx),
|
left: Some(section_idx),
|
||||||
right: find_section(right, §ion.name, section.kind),
|
right: find_section(right, §ion.name, section.kind),
|
||||||
@ -744,6 +668,9 @@ fn matching_sections(left: Option<&ObjInfo>, right: Option<&ObjInfo>) -> Result<
|
|||||||
}
|
}
|
||||||
if let Some(right) = right {
|
if let Some(right) = right {
|
||||||
for (section_idx, section) in right.sections.iter().enumerate() {
|
for (section_idx, section) in right.sections.iter().enumerate() {
|
||||||
|
if section.kind == SectionKind::Unknown {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if matches.iter().any(|m| m.right == Some(section_idx)) {
|
if matches.iter().any(|m| m.right == Some(section_idx)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -757,14 +684,6 @@ fn matching_sections(left: Option<&ObjInfo>, right: Option<&ObjInfo>) -> Result<
|
|||||||
Ok(matches)
|
Ok(matches)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_section(obj: Option<&ObjInfo>, name: &str, section_kind: ObjSectionKind) -> Option<usize> {
|
fn find_section(obj: Option<&Object>, name: &str, section_kind: SectionKind) -> Option<usize> {
|
||||||
for (section_idx, section) in obj?.sections.iter().enumerate() {
|
obj?.sections.iter().position(|s| s.kind == section_kind && s.name == name)
|
||||||
if section.kind != section_kind {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if section.name == name {
|
|
||||||
return Some(section_idx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,9 @@ use typed_path::Utf8PlatformPathBuf;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
build::{run_make, BuildConfig, BuildStatus},
|
build::{run_make, BuildConfig, BuildStatus},
|
||||||
diff::{diff_objs, DiffObjConfig, MappingConfig, ObjDiff},
|
diff::{diff_objs, DiffObjConfig, MappingConfig, ObjectDiff},
|
||||||
jobs::{start_job, update_status, Job, JobContext, JobResult, JobState},
|
jobs::{start_job, update_status, Job, JobContext, JobResult, JobState},
|
||||||
obj::{read, ObjInfo},
|
obj::{read, Object},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct ObjDiffConfig {
|
pub struct ObjDiffConfig {
|
||||||
@ -24,8 +24,8 @@ pub struct 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, ObjDiff)>,
|
pub first_obj: Option<(Object, ObjectDiff)>,
|
||||||
pub second_obj: Option<(ObjInfo, ObjDiff)>,
|
pub second_obj: Option<(Object, ObjectDiff)>,
|
||||||
pub time: OffsetDateTime,
|
pub time: OffsetDateTime,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,11 +170,11 @@ fn run_build(
|
|||||||
update_status(context, "Performing diff".to_string(), step_idx, total, &cancel)?;
|
update_status(context, "Performing diff".to_string(), step_idx, total, &cancel)?;
|
||||||
step_idx += 1;
|
step_idx += 1;
|
||||||
let result = diff_objs(
|
let result = diff_objs(
|
||||||
&config.diff_obj_config,
|
|
||||||
&config.mapping_config,
|
|
||||||
first_obj.as_ref(),
|
first_obj.as_ref(),
|
||||||
second_obj.as_ref(),
|
second_obj.as_ref(),
|
||||||
None,
|
None,
|
||||||
|
&config.diff_obj_config,
|
||||||
|
&config.mapping_config,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
update_status(context, "Complete".to_string(), step_idx, total, &cancel)?;
|
update_status(context, "Complete".to_string(), step_idx, total, &cancel)?;
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
#![allow(clippy::too_many_arguments)]
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
@ -17,5 +18,3 @@ pub mod jobs;
|
|||||||
pub mod obj;
|
pub mod obj;
|
||||||
#[cfg(feature = "any-arch")]
|
#[cfg(feature = "any-arch")]
|
||||||
pub mod util;
|
pub mod util;
|
||||||
#[cfg(feature = "wasm")]
|
|
||||||
pub mod wasm;
|
|
||||||
|
@ -1,23 +1,30 @@
|
|||||||
pub mod read;
|
pub mod read;
|
||||||
pub mod split_meta;
|
pub mod split_meta;
|
||||||
|
|
||||||
use alloc::{borrow::Cow, boxed::Box, collections::BTreeMap, string::String, vec::Vec};
|
use alloc::{borrow::Cow, boxed::Box, collections::BTreeMap, string::String, vec, vec::Vec};
|
||||||
use core::fmt;
|
use core::{fmt, num::NonZeroU32};
|
||||||
|
|
||||||
use flagset::{flags, FlagSet};
|
use flagset::{flags, FlagSet};
|
||||||
use object::RelocationFlags;
|
|
||||||
use split_meta::SplitMeta;
|
|
||||||
|
|
||||||
use crate::{arch::ObjArch, util::ReallySigned};
|
use crate::{
|
||||||
|
arch::{Arch, ArchDummy},
|
||||||
|
obj::split_meta::SplitMeta,
|
||||||
|
util::ReallySigned,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
|
#[derive(Debug, Eq, PartialEq, Copy, Clone, Default)]
|
||||||
pub enum ObjSectionKind {
|
pub enum SectionKind {
|
||||||
|
#[default]
|
||||||
|
Unknown = -1,
|
||||||
Code,
|
Code,
|
||||||
Data,
|
Data,
|
||||||
Bss,
|
Bss,
|
||||||
|
Common,
|
||||||
}
|
}
|
||||||
|
|
||||||
flags! {
|
flags! {
|
||||||
pub enum ObjSymbolFlags: u8 {
|
#[derive(Hash)]
|
||||||
|
pub enum SymbolFlag: u8 {
|
||||||
Global,
|
Global,
|
||||||
Local,
|
Local,
|
||||||
Weak,
|
Weak,
|
||||||
@ -26,105 +33,152 @@ flags! {
|
|||||||
/// Has extra data associated with the symbol
|
/// Has extra data associated with the symbol
|
||||||
/// (e.g. exception table entry)
|
/// (e.g. exception table entry)
|
||||||
HasExtra,
|
HasExtra,
|
||||||
|
/// Symbol size was missing and was inferred
|
||||||
|
SizeInferred,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[derive(Debug, Copy, Clone, Default, PartialEq)]
|
|
||||||
pub struct ObjSymbolFlagSet(pub FlagSet<ObjSymbolFlags>);
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
pub type SymbolFlagSet = FlagSet<SymbolFlag>;
|
||||||
pub struct ObjSection {
|
|
||||||
|
flags! {
|
||||||
|
#[derive(Hash)]
|
||||||
|
pub enum SectionFlag: u8 {
|
||||||
|
/// Section combined from multiple input sections
|
||||||
|
Combined,
|
||||||
|
Hidden,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type SectionFlagSet = FlagSet<SectionFlag>;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct Section {
|
||||||
|
/// Unique section ID
|
||||||
|
pub id: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub kind: ObjSectionKind,
|
|
||||||
pub address: u64,
|
pub address: u64,
|
||||||
pub size: u64,
|
pub size: u64,
|
||||||
pub data: Vec<u8>,
|
pub kind: SectionKind,
|
||||||
pub orig_index: usize,
|
pub data: SectionData,
|
||||||
pub symbols: Vec<ObjSymbol>,
|
pub flags: SectionFlagSet,
|
||||||
pub relocations: Vec<ObjReloc>,
|
pub relocations: Vec<Relocation>,
|
||||||
pub virtual_address: Option<u64>,
|
|
||||||
/// Line number info (.line or .debug_line section)
|
/// Line number info (.line or .debug_line section)
|
||||||
pub line_info: BTreeMap<u64, u32>,
|
pub line_info: BTreeMap<u64, u32>,
|
||||||
|
/// Original virtual address (from .note.split section)
|
||||||
|
pub virtual_address: Option<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Default)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct SectionData(pub Vec<u8>);
|
||||||
|
|
||||||
|
impl core::ops::Deref for SectionData {
|
||||||
|
type Target = Vec<u8>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target { &self.0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for SectionData {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_tuple("SectionData").field(&self.0.len()).finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Section {
|
||||||
|
pub fn data_range(&self, address: u64, size: usize) -> Option<&[u8]> {
|
||||||
|
let start = self.address;
|
||||||
|
let end = start + self.size;
|
||||||
|
if address >= start && address + size as u64 <= end {
|
||||||
|
let offset = (address - start) as usize;
|
||||||
|
Some(&self.data[offset..offset + size])
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn relocation_at<'obj>(
|
||||||
|
&'obj self,
|
||||||
|
address: u64,
|
||||||
|
obj: &'obj Object,
|
||||||
|
) -> Option<ResolvedRelocation<'obj>> {
|
||||||
|
self.relocations.binary_search_by_key(&address, |r| r.address).ok().and_then(|i| {
|
||||||
|
let relocation = self.relocations.get(i)?;
|
||||||
|
let symbol = obj.symbols.get(relocation.target_symbol)?;
|
||||||
|
Some(ResolvedRelocation { relocation, symbol })
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
pub enum ObjInsArgValue {
|
pub enum InstructionArgValue {
|
||||||
Signed(i64),
|
Signed(i64),
|
||||||
Unsigned(u64),
|
Unsigned(u64),
|
||||||
Opaque(Cow<'static, str>),
|
Opaque(Cow<'static, str>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjInsArgValue {
|
impl InstructionArgValue {
|
||||||
pub fn loose_eq(&self, other: &ObjInsArgValue) -> bool {
|
pub fn loose_eq(&self, other: &InstructionArgValue) -> bool {
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
(ObjInsArgValue::Signed(a), ObjInsArgValue::Signed(b)) => a == b,
|
(InstructionArgValue::Signed(a), InstructionArgValue::Signed(b)) => a == b,
|
||||||
(ObjInsArgValue::Unsigned(a), ObjInsArgValue::Unsigned(b)) => a == b,
|
(InstructionArgValue::Unsigned(a), InstructionArgValue::Unsigned(b)) => a == b,
|
||||||
(ObjInsArgValue::Signed(a), ObjInsArgValue::Unsigned(b))
|
(InstructionArgValue::Signed(a), InstructionArgValue::Unsigned(b))
|
||||||
| (ObjInsArgValue::Unsigned(b), ObjInsArgValue::Signed(a)) => *a as u64 == *b,
|
| (InstructionArgValue::Unsigned(b), InstructionArgValue::Signed(a)) => *a as u64 == *b,
|
||||||
(ObjInsArgValue::Opaque(a), ObjInsArgValue::Opaque(b)) => a == b,
|
(InstructionArgValue::Opaque(a), InstructionArgValue::Opaque(b)) => a == b,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for ObjInsArgValue {
|
impl fmt::Display for InstructionArgValue {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
ObjInsArgValue::Signed(v) => write!(f, "{:#x}", ReallySigned(*v)),
|
InstructionArgValue::Signed(v) => write!(f, "{:#x}", ReallySigned(*v)),
|
||||||
ObjInsArgValue::Unsigned(v) => write!(f, "{:#x}", v),
|
InstructionArgValue::Unsigned(v) => write!(f, "{:#x}", v),
|
||||||
ObjInsArgValue::Opaque(v) => write!(f, "{}", v),
|
InstructionArgValue::Opaque(v) => write!(f, "{}", v),
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
|
||||||
pub enum ObjInsArg {
|
|
||||||
PlainText(Cow<'static, str>),
|
|
||||||
Arg(ObjInsArgValue),
|
|
||||||
Reloc,
|
|
||||||
BranchDest(u64),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ObjInsArg {
|
|
||||||
#[inline]
|
|
||||||
pub fn is_plain_text(&self) -> bool { matches!(self, ObjInsArg::PlainText(_)) }
|
|
||||||
|
|
||||||
pub fn loose_eq(&self, other: &ObjInsArg) -> bool {
|
|
||||||
match (self, other) {
|
|
||||||
(ObjInsArg::Arg(a), ObjInsArg::Arg(b)) => a.loose_eq(b),
|
|
||||||
(ObjInsArg::Reloc, ObjInsArg::Reloc) => true,
|
|
||||||
(ObjInsArg::BranchDest(a), ObjInsArg::BranchDest(b)) => a == b,
|
|
||||||
_ => false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ObjIns {
|
pub enum InstructionArg {
|
||||||
pub address: u64,
|
Value(InstructionArgValue),
|
||||||
pub size: u8,
|
Reloc,
|
||||||
pub op: u16,
|
BranchDest(u64),
|
||||||
pub mnemonic: Cow<'static, str>,
|
|
||||||
pub args: Vec<ObjInsArg>,
|
|
||||||
pub reloc: Option<ObjReloc>,
|
|
||||||
pub branch_dest: Option<u64>,
|
|
||||||
/// Line number
|
|
||||||
pub line: Option<u32>,
|
|
||||||
/// Formatted instruction
|
|
||||||
pub formatted: String,
|
|
||||||
/// Original (unsimplified) instruction
|
|
||||||
pub orig: Option<String>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjIns {
|
impl InstructionArg {
|
||||||
/// Iterate over non-PlainText arguments.
|
pub fn loose_eq(&self, other: &InstructionArg) -> bool {
|
||||||
#[inline]
|
match (self, other) {
|
||||||
pub fn iter_args(&self) -> impl DoubleEndedIterator<Item = &ObjInsArg> {
|
(InstructionArg::Value(a), InstructionArg::Value(b)) => a.loose_eq(b),
|
||||||
self.args.iter().filter(|a| !a.is_plain_text())
|
(InstructionArg::Reloc, InstructionArg::Reloc) => true,
|
||||||
|
(InstructionArg::BranchDest(a), InstructionArg::BranchDest(b)) => a == b,
|
||||||
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct InstructionRef {
|
||||||
|
pub address: u64,
|
||||||
|
pub size: u8,
|
||||||
|
pub opcode: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct ScannedInstruction {
|
||||||
|
pub ins_ref: InstructionRef,
|
||||||
|
pub branch_dest: Option<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ParsedInstruction {
|
||||||
|
pub ins_ref: InstructionRef,
|
||||||
|
pub mnemonic: Cow<'static, str>,
|
||||||
|
pub args: Vec<InstructionArg>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Default)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Default)]
|
||||||
pub enum ObjSymbolKind {
|
pub enum SymbolKind {
|
||||||
#[default]
|
#[default]
|
||||||
Unknown,
|
Unknown,
|
||||||
Function,
|
Function,
|
||||||
@ -132,60 +186,65 @@ pub enum ObjSymbolKind {
|
|||||||
Section,
|
Section,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, Eq, PartialEq, Hash, Default)]
|
||||||
pub struct ObjSymbol {
|
pub struct Symbol {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub demangled_name: Option<String>,
|
pub demangled_name: Option<String>,
|
||||||
pub address: u64,
|
pub address: u64,
|
||||||
pub section_address: u64,
|
|
||||||
pub size: u64,
|
pub size: u64,
|
||||||
pub size_known: bool,
|
pub kind: SymbolKind,
|
||||||
pub kind: ObjSymbolKind,
|
pub section: Option<usize>,
|
||||||
pub flags: ObjSymbolFlagSet,
|
pub flags: SymbolFlagSet,
|
||||||
pub orig_section_index: Option<usize>,
|
/// Alignment (from Metrowerks .comment section)
|
||||||
|
pub align: Option<NonZeroU32>,
|
||||||
/// Original virtual address (from .note.split section)
|
/// Original virtual address (from .note.split section)
|
||||||
pub virtual_address: Option<u64>,
|
pub virtual_address: Option<u64>,
|
||||||
/// Original index in object symbol table
|
|
||||||
pub original_index: Option<usize>,
|
|
||||||
pub bytes: Vec<u8>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ObjInfo {
|
#[derive(Debug)]
|
||||||
pub arch: Box<dyn ObjArch>,
|
pub struct Object {
|
||||||
pub path: Option<String>,
|
pub arch: Box<dyn Arch>,
|
||||||
#[cfg(feature = "std")]
|
pub symbols: Vec<Symbol>,
|
||||||
pub timestamp: Option<filetime::FileTime>,
|
pub sections: Vec<Section>,
|
||||||
pub sections: Vec<ObjSection>,
|
|
||||||
/// Common BSS symbols
|
|
||||||
pub common: Vec<ObjSymbol>,
|
|
||||||
/// Split object metadata (.note.split section)
|
/// Split object metadata (.note.split section)
|
||||||
pub split_meta: Option<SplitMeta>,
|
pub split_meta: Option<SplitMeta>,
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
pub path: Option<std::path::PathBuf>,
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
pub timestamp: Option<filetime::FileTime>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
impl Default for Object {
|
||||||
pub struct ObjReloc {
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
arch: ArchDummy::new(),
|
||||||
|
symbols: vec![],
|
||||||
|
sections: vec![],
|
||||||
|
split_meta: None,
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
path: None,
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
timestamp: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||||
|
pub struct Relocation {
|
||||||
pub flags: RelocationFlags,
|
pub flags: RelocationFlags,
|
||||||
pub address: u64,
|
pub address: u64,
|
||||||
pub target: ObjSymbol,
|
pub target_symbol: usize,
|
||||||
pub addend: i64,
|
pub addend: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
pub struct SymbolRef {
|
pub enum RelocationFlags {
|
||||||
pub section_idx: usize,
|
Elf(u32),
|
||||||
pub symbol_idx: usize,
|
Coff(u16),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const SECTION_COMMON: usize = usize::MAX - 1;
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct ResolvedRelocation<'a> {
|
||||||
impl ObjInfo {
|
pub relocation: &'a Relocation,
|
||||||
pub fn section_symbol(&self, symbol_ref: SymbolRef) -> (Option<&ObjSection>, &ObjSymbol) {
|
pub symbol: &'a Symbol,
|
||||||
if symbol_ref.section_idx == SECTION_COMMON {
|
|
||||||
let symbol = &self.common[symbol_ref.symbol_idx];
|
|
||||||
return (None, symbol);
|
|
||||||
}
|
|
||||||
let section = &self.sections[symbol_ref.section_idx];
|
|
||||||
let symbol = §ion.symbols[symbol_ref.symbol_idx];
|
|
||||||
(Some(section), symbol)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,161 @@
|
|||||||
|
---
|
||||||
|
source: objdiff-core/src/obj/read.rs
|
||||||
|
expression: "(sections, symbols)"
|
||||||
|
---
|
||||||
|
(
|
||||||
|
[
|
||||||
|
Section {
|
||||||
|
id: ".text-0",
|
||||||
|
name: ".text",
|
||||||
|
address: 0,
|
||||||
|
size: 8,
|
||||||
|
kind: Code,
|
||||||
|
data: SectionData(
|
||||||
|
8,
|
||||||
|
),
|
||||||
|
flags: FlagSet(),
|
||||||
|
relocations: [
|
||||||
|
Relocation {
|
||||||
|
flags: Elf(
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
address: 0,
|
||||||
|
target_symbol: 0,
|
||||||
|
addend: 4,
|
||||||
|
},
|
||||||
|
Relocation {
|
||||||
|
flags: Elf(
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
address: 2,
|
||||||
|
target_symbol: 1,
|
||||||
|
addend: 0,
|
||||||
|
},
|
||||||
|
Relocation {
|
||||||
|
flags: Elf(
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
address: 4,
|
||||||
|
target_symbol: 0,
|
||||||
|
addend: 10,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
line_info: {},
|
||||||
|
virtual_address: None,
|
||||||
|
},
|
||||||
|
Section {
|
||||||
|
id: ".data-0",
|
||||||
|
name: ".data",
|
||||||
|
address: 0,
|
||||||
|
size: 12,
|
||||||
|
kind: Data,
|
||||||
|
data: SectionData(
|
||||||
|
12,
|
||||||
|
),
|
||||||
|
flags: FlagSet(Combined),
|
||||||
|
relocations: [
|
||||||
|
Relocation {
|
||||||
|
flags: Elf(
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
address: 0,
|
||||||
|
target_symbol: 2,
|
||||||
|
addend: 0,
|
||||||
|
},
|
||||||
|
Relocation {
|
||||||
|
flags: Elf(
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
address: 4,
|
||||||
|
target_symbol: 2,
|
||||||
|
addend: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
line_info: {
|
||||||
|
0: 1,
|
||||||
|
8: 2,
|
||||||
|
},
|
||||||
|
virtual_address: None,
|
||||||
|
},
|
||||||
|
Section {
|
||||||
|
id: ".data-1",
|
||||||
|
name: ".data",
|
||||||
|
address: 0,
|
||||||
|
size: 0,
|
||||||
|
kind: Data,
|
||||||
|
data: SectionData(
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
flags: FlagSet(Hidden),
|
||||||
|
relocations: [],
|
||||||
|
line_info: {},
|
||||||
|
virtual_address: None,
|
||||||
|
},
|
||||||
|
Section {
|
||||||
|
id: ".data-2",
|
||||||
|
name: ".data",
|
||||||
|
address: 0,
|
||||||
|
size: 0,
|
||||||
|
kind: Data,
|
||||||
|
data: SectionData(
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
flags: FlagSet(Hidden),
|
||||||
|
relocations: [],
|
||||||
|
line_info: {},
|
||||||
|
virtual_address: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
Symbol {
|
||||||
|
name: ".data",
|
||||||
|
demangled_name: None,
|
||||||
|
address: 0,
|
||||||
|
size: 0,
|
||||||
|
kind: Section,
|
||||||
|
section: Some(
|
||||||
|
1,
|
||||||
|
),
|
||||||
|
flags: FlagSet(),
|
||||||
|
align: None,
|
||||||
|
virtual_address: None,
|
||||||
|
},
|
||||||
|
Symbol {
|
||||||
|
name: "symbol",
|
||||||
|
demangled_name: None,
|
||||||
|
address: 4,
|
||||||
|
size: 4,
|
||||||
|
kind: Object,
|
||||||
|
section: Some(
|
||||||
|
1,
|
||||||
|
),
|
||||||
|
flags: FlagSet(),
|
||||||
|
align: None,
|
||||||
|
virtual_address: None,
|
||||||
|
},
|
||||||
|
Symbol {
|
||||||
|
name: "function",
|
||||||
|
demangled_name: None,
|
||||||
|
address: 0,
|
||||||
|
size: 8,
|
||||||
|
kind: Function,
|
||||||
|
section: Some(
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
flags: FlagSet(),
|
||||||
|
align: None,
|
||||||
|
virtual_address: None,
|
||||||
|
},
|
||||||
|
Symbol {
|
||||||
|
name: ".data",
|
||||||
|
demangled_name: None,
|
||||||
|
address: 0,
|
||||||
|
size: 0,
|
||||||
|
kind: Unknown,
|
||||||
|
section: None,
|
||||||
|
flags: FlagSet(),
|
||||||
|
align: None,
|
||||||
|
virtual_address: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
)
|
@ -1,7 +1,7 @@
|
|||||||
use alloc::format;
|
use alloc::format;
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::{ensure, Result};
|
||||||
use num_traits::PrimInt;
|
use num_traits::PrimInt;
|
||||||
use object::{Endian, Object};
|
use object::{Endian, Object};
|
||||||
|
|
||||||
@ -27,17 +27,15 @@ impl<N: PrimInt> fmt::UpperHex for ReallySigned<N> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_u32(obj_file: &object::File, reader: &mut &[u8]) -> Result<u32> {
|
pub fn read_u32(obj_file: &object::File, reader: &mut &[u8]) -> Result<u32> {
|
||||||
if reader.len() < 4 {
|
ensure!(reader.len() >= 4, "Not enough bytes to read u32");
|
||||||
return Err(anyhow::anyhow!("Not enough bytes to read u32"));
|
|
||||||
}
|
|
||||||
let value = u32::from_ne_bytes(reader[..4].try_into()?);
|
let value = u32::from_ne_bytes(reader[..4].try_into()?);
|
||||||
|
*reader = &reader[4..];
|
||||||
Ok(obj_file.endianness().read_u32(value))
|
Ok(obj_file.endianness().read_u32(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_u16(obj_file: &object::File, reader: &mut &[u8]) -> Result<u16> {
|
pub fn read_u16(obj_file: &object::File, reader: &mut &[u8]) -> Result<u16> {
|
||||||
if reader.len() < 2 {
|
ensure!(reader.len() >= 2, "Not enough bytes to read u16");
|
||||||
return Err(anyhow::anyhow!("Not enough bytes to read u16"));
|
|
||||||
}
|
|
||||||
let value = u16::from_ne_bytes(reader[..2].try_into()?);
|
let value = u16::from_ne_bytes(reader[..2].try_into()?);
|
||||||
|
*reader = &reader[2..];
|
||||||
Ok(obj_file.endianness().read_u16(value))
|
Ok(obj_file.endianness().read_u16(value))
|
||||||
}
|
}
|
||||||
|
@ -1,99 +0,0 @@
|
|||||||
use alloc::{
|
|
||||||
format,
|
|
||||||
str::FromStr,
|
|
||||||
string::{String, ToString},
|
|
||||||
vec::Vec,
|
|
||||||
};
|
|
||||||
use core::cell::RefCell;
|
|
||||||
|
|
||||||
use prost::Message;
|
|
||||||
|
|
||||||
use crate::{bindings::diff::DiffResult, diff, obj};
|
|
||||||
|
|
||||||
wit_bindgen::generate!({
|
|
||||||
world: "api",
|
|
||||||
});
|
|
||||||
|
|
||||||
use exports::objdiff::core::diff::{
|
|
||||||
DiffConfigBorrow, Guest as GuestTypes, GuestDiffConfig, GuestObject, Object, ObjectBorrow,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Component;
|
|
||||||
|
|
||||||
impl Guest for Component {
|
|
||||||
fn init() -> Result<(), String> {
|
|
||||||
// console_error_panic_hook::set_once();
|
|
||||||
// #[cfg(debug_assertions)]
|
|
||||||
// console_log::init_with_level(log::Level::Debug).map_err(|e| e.to_string())?;
|
|
||||||
// #[cfg(not(debug_assertions))]
|
|
||||||
// console_log::init_with_level(log::Level::Info).map_err(|e| e.to_string())?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn version() -> String { env!("CARGO_PKG_VERSION").to_string() }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(transparent)]
|
|
||||||
struct ResourceDiffConfig(RefCell<diff::DiffObjConfig>);
|
|
||||||
|
|
||||||
impl GuestTypes for Component {
|
|
||||||
type DiffConfig = ResourceDiffConfig;
|
|
||||||
type Object = obj::ObjInfo;
|
|
||||||
|
|
||||||
fn run_diff(
|
|
||||||
left: Option<ObjectBorrow>,
|
|
||||||
right: Option<ObjectBorrow>,
|
|
||||||
diff_config: DiffConfigBorrow,
|
|
||||||
) -> Result<Vec<u8>, String> {
|
|
||||||
let diff_config = diff_config.get::<ResourceDiffConfig>().0.borrow();
|
|
||||||
let result = run_diff_internal(
|
|
||||||
left.as_ref().map(|o| o.get()),
|
|
||||||
right.as_ref().map(|o| o.get()),
|
|
||||||
&diff_config,
|
|
||||||
&diff::MappingConfig::default(),
|
|
||||||
)
|
|
||||||
.map_err(|e| e.to_string())?;
|
|
||||||
Ok(result.encode_to_vec())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GuestDiffConfig for ResourceDiffConfig {
|
|
||||||
fn new() -> Self { Self(RefCell::new(diff::DiffObjConfig::default())) }
|
|
||||||
|
|
||||||
fn set_property(&self, key: String, value: String) -> Result<(), String> {
|
|
||||||
let id = diff::ConfigPropertyId::from_str(&key)
|
|
||||||
.map_err(|_| format!("Invalid property key {:?}", key))?;
|
|
||||||
self.0
|
|
||||||
.borrow_mut()
|
|
||||||
.set_property_value_str(id, &value)
|
|
||||||
.map_err(|_| format!("Invalid property value {:?}", value))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_property(&self, key: String) -> Result<String, String> {
|
|
||||||
let id = diff::ConfigPropertyId::from_str(&key)
|
|
||||||
.map_err(|_| format!("Invalid property key {:?}", key))?;
|
|
||||||
Ok(self.0.borrow().get_property_value(id).to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GuestObject for obj::ObjInfo {
|
|
||||||
fn parse(data: Vec<u8>, diff_config: DiffConfigBorrow) -> Result<Object, String> {
|
|
||||||
let diff_config = diff_config.get::<ResourceDiffConfig>().0.borrow();
|
|
||||||
obj::read::parse(&data, &diff_config).map(|o| Object::new(o)).map_err(|e| e.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run_diff_internal(
|
|
||||||
left: Option<&obj::ObjInfo>,
|
|
||||||
right: Option<&obj::ObjInfo>,
|
|
||||||
diff_config: &diff::DiffObjConfig,
|
|
||||||
mapping_config: &diff::MappingConfig,
|
|
||||||
) -> anyhow::Result<DiffResult> {
|
|
||||||
log::debug!("Running diff with config: {:?}", diff_config);
|
|
||||||
let result = diff::diff_objs(diff_config, mapping_config, left, right, None)?;
|
|
||||||
let left = left.and_then(|o| result.left.as_ref().map(|d| (o, d)));
|
|
||||||
let right = right.and_then(|o| result.right.as_ref().map(|d| (o, d)));
|
|
||||||
Ok(DiffResult::new(left, right))
|
|
||||||
}
|
|
||||||
|
|
||||||
export!(Component);
|
|
@ -1,18 +0,0 @@
|
|||||||
mod api;
|
|
||||||
|
|
||||||
#[cfg(not(feature = "std"))]
|
|
||||||
mod cabi_realloc;
|
|
||||||
|
|
||||||
#[cfg(not(feature = "std"))]
|
|
||||||
static mut ARENA: [u8; 10000] = [0; 10000];
|
|
||||||
|
|
||||||
#[cfg(not(feature = "std"))]
|
|
||||||
#[global_allocator]
|
|
||||||
static ALLOCATOR: talc::Talck<spin::Mutex<()>, talc::ClaimOnOom> = talc::Talc::new(unsafe {
|
|
||||||
talc::ClaimOnOom::new(talc::Span::from_array(core::ptr::addr_of!(ARENA) as *mut [u8; 10000]))
|
|
||||||
})
|
|
||||||
.lock();
|
|
||||||
|
|
||||||
#[cfg(not(feature = "std"))]
|
|
||||||
#[panic_handler]
|
|
||||||
fn panic(_info: &core::panic::PanicInfo) -> ! { loop {} }
|
|
79
objdiff-core/tests/arch_ppc.rs
Normal file
79
objdiff-core/tests/arch_ppc.rs
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
use objdiff_core::{
|
||||||
|
diff::{self, display},
|
||||||
|
obj,
|
||||||
|
obj::SectionKind,
|
||||||
|
};
|
||||||
|
|
||||||
|
mod common;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "ppc")]
|
||||||
|
fn read_ppc() {
|
||||||
|
let diff_config = diff::DiffObjConfig::default();
|
||||||
|
let obj = obj::read::parse(include_object!("data/ppc/IObj.o"), &diff_config).unwrap();
|
||||||
|
insta::assert_debug_snapshot!(obj);
|
||||||
|
let symbol_idx =
|
||||||
|
obj.symbols.iter().position(|s| s.name == "Type2Text__10SObjectTagFUi").unwrap();
|
||||||
|
let diff = diff::code::no_diff_code(&obj, symbol_idx, &diff_config).unwrap();
|
||||||
|
insta::assert_debug_snapshot!(diff.instruction_rows);
|
||||||
|
let output = common::display_diff(&obj, &diff, symbol_idx, &diff_config);
|
||||||
|
insta::assert_snapshot!(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "ppc")]
|
||||||
|
fn read_dwarf1_line_info() {
|
||||||
|
let diff_config = diff::DiffObjConfig::default();
|
||||||
|
let obj = obj::read::parse(include_object!("data/ppc/m_Do_hostIO.o"), &diff_config).unwrap();
|
||||||
|
let line_infos = obj
|
||||||
|
.sections
|
||||||
|
.iter()
|
||||||
|
.filter(|s| s.kind == SectionKind::Code)
|
||||||
|
.map(|s| s.line_info.clone())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
insta::assert_debug_snapshot!(line_infos);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "ppc")]
|
||||||
|
fn diff_ppc() {
|
||||||
|
let diff_config = diff::DiffObjConfig::default();
|
||||||
|
let mapping_config = diff::MappingConfig::default();
|
||||||
|
let target_obj =
|
||||||
|
obj::read::parse(include_object!("data/ppc/CDamageVulnerability_target.o"), &diff_config)
|
||||||
|
.unwrap();
|
||||||
|
let base_obj =
|
||||||
|
obj::read::parse(include_object!("data/ppc/CDamageVulnerability_base.o"), &diff_config)
|
||||||
|
.unwrap();
|
||||||
|
let diff =
|
||||||
|
diff::diff_objs(Some(&target_obj), Some(&base_obj), None, &diff_config, &mapping_config)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let target_diff = diff.left.as_ref().unwrap();
|
||||||
|
let base_diff = diff.right.as_ref().unwrap();
|
||||||
|
let sections_display = display::display_sections(
|
||||||
|
&target_obj,
|
||||||
|
&target_diff,
|
||||||
|
display::SymbolFilter::None,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
insta::assert_debug_snapshot!(sections_display);
|
||||||
|
|
||||||
|
let target_symbol_idx = target_obj
|
||||||
|
.symbols
|
||||||
|
.iter()
|
||||||
|
.position(|s| s.name == "WeaponHurts__20CDamageVulnerabilityCFRC11CWeaponModei")
|
||||||
|
.unwrap();
|
||||||
|
let target_symbol_diff = &target_diff.symbols[target_symbol_idx];
|
||||||
|
let base_symbol_idx = base_obj
|
||||||
|
.symbols
|
||||||
|
.iter()
|
||||||
|
.position(|s| s.name == "WeaponHurts__20CDamageVulnerabilityCFRC11CWeaponModei")
|
||||||
|
.unwrap();
|
||||||
|
let base_symbol_diff = &base_diff.symbols[base_symbol_idx];
|
||||||
|
assert_eq!(target_symbol_diff.target_symbol, Some(base_symbol_idx));
|
||||||
|
assert_eq!(base_symbol_diff.target_symbol, Some(target_symbol_idx));
|
||||||
|
insta::assert_debug_snapshot!((target_symbol_diff, base_symbol_diff));
|
||||||
|
}
|
17
objdiff-core/tests/arch_x86.rs
Normal file
17
objdiff-core/tests/arch_x86.rs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
use objdiff_core::{diff, obj};
|
||||||
|
|
||||||
|
mod common;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "x86")]
|
||||||
|
fn read_x86() {
|
||||||
|
let diff_config = diff::DiffObjConfig::default();
|
||||||
|
let obj = obj::read::parse(include_object!("data/x86/rtest.obj"), &diff_config).unwrap();
|
||||||
|
insta::assert_debug_snapshot!(obj);
|
||||||
|
let symbol_idx =
|
||||||
|
obj.symbols.iter().position(|s| s.name == "Type2Text__10SObjectTagFUi").unwrap();
|
||||||
|
let diff = diff::code::no_diff_code(&obj, symbol_idx, &diff_config).unwrap();
|
||||||
|
insta::assert_debug_snapshot!(diff.instruction_rows);
|
||||||
|
let output = common::display_diff(&obj, &diff, symbol_idx, &diff_config);
|
||||||
|
insta::assert_snapshot!(output);
|
||||||
|
}
|
57
objdiff-core/tests/common.rs
Normal file
57
objdiff-core/tests/common.rs
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
use objdiff_core::{
|
||||||
|
diff::{DiffObjConfig, SymbolDiff},
|
||||||
|
obj::Object,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn display_diff(
|
||||||
|
obj: &Object,
|
||||||
|
diff: &SymbolDiff,
|
||||||
|
symbol_idx: usize,
|
||||||
|
diff_config: &DiffObjConfig,
|
||||||
|
) -> String {
|
||||||
|
let mut output = String::new();
|
||||||
|
for row in &diff.instruction_rows {
|
||||||
|
output.push('[');
|
||||||
|
let mut separator = false;
|
||||||
|
objdiff_core::diff::display::display_row(
|
||||||
|
&obj,
|
||||||
|
symbol_idx,
|
||||||
|
row,
|
||||||
|
&diff_config,
|
||||||
|
|text, diff_idx| {
|
||||||
|
if separator {
|
||||||
|
output.push_str(", ");
|
||||||
|
} else {
|
||||||
|
separator = true;
|
||||||
|
}
|
||||||
|
output.push_str(&format!("({:?}, {:?})", text, diff_idx.get()));
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
output.push_str("]\n");
|
||||||
|
}
|
||||||
|
output
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct AlignedAs<Align, Bytes: ?Sized> {
|
||||||
|
pub _align: [Align; 0],
|
||||||
|
pub bytes: Bytes,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! include_bytes_align_as {
|
||||||
|
($align_ty:ty, $path:literal) => {{
|
||||||
|
static ALIGNED: &common::AlignedAs<$align_ty, [u8]> =
|
||||||
|
&common::AlignedAs { _align: [], bytes: *include_bytes!($path) };
|
||||||
|
&ALIGNED.bytes
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! include_object {
|
||||||
|
($path:literal) => {
|
||||||
|
include_bytes_align_as!(u32, $path)
|
||||||
|
};
|
||||||
|
}
|
BIN
objdiff-core/tests/data/ppc/CDamageVulnerability_base.o
Normal file
BIN
objdiff-core/tests/data/ppc/CDamageVulnerability_base.o
Normal file
Binary file not shown.
BIN
objdiff-core/tests/data/ppc/CDamageVulnerability_target.o
Normal file
BIN
objdiff-core/tests/data/ppc/CDamageVulnerability_target.o
Normal file
Binary file not shown.
BIN
objdiff-core/tests/data/ppc/IObj.o
Normal file
BIN
objdiff-core/tests/data/ppc/IObj.o
Normal file
Binary file not shown.
BIN
objdiff-core/tests/data/ppc/m_Do_hostIO.o
Normal file
BIN
objdiff-core/tests/data/ppc/m_Do_hostIO.o
Normal file
Binary file not shown.
BIN
objdiff-core/tests/data/x86/rtest.obj
Normal file
BIN
objdiff-core/tests/data/x86/rtest.obj
Normal file
Binary file not shown.
BIN
objdiff-core/tests/data/x86/staticdebug.obj
Normal file
BIN
objdiff-core/tests/data/x86/staticdebug.obj
Normal file
Binary file not shown.
4870
objdiff-core/tests/snapshots/arch_ppc__diff_ppc-2.snap
Normal file
4870
objdiff-core/tests/snapshots/arch_ppc__diff_ppc-2.snap
Normal file
File diff suppressed because it is too large
Load Diff
90
objdiff-core/tests/snapshots/arch_ppc__diff_ppc.snap
Normal file
90
objdiff-core/tests/snapshots/arch_ppc__diff_ppc.snap
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
---
|
||||||
|
source: objdiff-core/tests/arch_ppc.rs
|
||||||
|
expression: sections_display
|
||||||
|
---
|
||||||
|
[
|
||||||
|
SectionDisplay {
|
||||||
|
id: ".comm",
|
||||||
|
name: ".comm",
|
||||||
|
size: 0,
|
||||||
|
match_percent: None,
|
||||||
|
symbols: [
|
||||||
|
SectionDisplaySymbol {
|
||||||
|
symbol: 11,
|
||||||
|
is_mapping_symbol: false,
|
||||||
|
},
|
||||||
|
SectionDisplaySymbol {
|
||||||
|
symbol: 12,
|
||||||
|
is_mapping_symbol: false,
|
||||||
|
},
|
||||||
|
SectionDisplaySymbol {
|
||||||
|
symbol: 13,
|
||||||
|
is_mapping_symbol: false,
|
||||||
|
},
|
||||||
|
SectionDisplaySymbol {
|
||||||
|
symbol: 14,
|
||||||
|
is_mapping_symbol: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
SectionDisplay {
|
||||||
|
id: ".ctors-0",
|
||||||
|
name: ".ctors",
|
||||||
|
size: 4,
|
||||||
|
match_percent: Some(
|
||||||
|
100.0,
|
||||||
|
),
|
||||||
|
symbols: [
|
||||||
|
SectionDisplaySymbol {
|
||||||
|
symbol: 2,
|
||||||
|
is_mapping_symbol: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
SectionDisplay {
|
||||||
|
id: ".text-0",
|
||||||
|
name: ".text",
|
||||||
|
size: 3060,
|
||||||
|
match_percent: Some(
|
||||||
|
59.02353,
|
||||||
|
),
|
||||||
|
symbols: [
|
||||||
|
SectionDisplaySymbol {
|
||||||
|
symbol: 1,
|
||||||
|
is_mapping_symbol: false,
|
||||||
|
},
|
||||||
|
SectionDisplaySymbol {
|
||||||
|
symbol: 3,
|
||||||
|
is_mapping_symbol: false,
|
||||||
|
},
|
||||||
|
SectionDisplaySymbol {
|
||||||
|
symbol: 10,
|
||||||
|
is_mapping_symbol: false,
|
||||||
|
},
|
||||||
|
SectionDisplaySymbol {
|
||||||
|
symbol: 9,
|
||||||
|
is_mapping_symbol: false,
|
||||||
|
},
|
||||||
|
SectionDisplaySymbol {
|
||||||
|
symbol: 8,
|
||||||
|
is_mapping_symbol: false,
|
||||||
|
},
|
||||||
|
SectionDisplaySymbol {
|
||||||
|
symbol: 7,
|
||||||
|
is_mapping_symbol: false,
|
||||||
|
},
|
||||||
|
SectionDisplaySymbol {
|
||||||
|
symbol: 6,
|
||||||
|
is_mapping_symbol: false,
|
||||||
|
},
|
||||||
|
SectionDisplaySymbol {
|
||||||
|
symbol: 5,
|
||||||
|
is_mapping_symbol: false,
|
||||||
|
},
|
||||||
|
SectionDisplaySymbol {
|
||||||
|
symbol: 4,
|
||||||
|
is_mapping_symbol: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
@ -0,0 +1,40 @@
|
|||||||
|
---
|
||||||
|
source: objdiff-core/tests/arch_ppc.rs
|
||||||
|
expression: line_infos
|
||||||
|
---
|
||||||
|
[
|
||||||
|
{
|
||||||
|
0: 13,
|
||||||
|
4: 16,
|
||||||
|
32: 17,
|
||||||
|
44: 18,
|
||||||
|
60: 20,
|
||||||
|
76: 21,
|
||||||
|
84: 23,
|
||||||
|
92: 25,
|
||||||
|
108: 26,
|
||||||
|
124: 27,
|
||||||
|
136: 28,
|
||||||
|
144: 29,
|
||||||
|
152: 31,
|
||||||
|
164: 34,
|
||||||
|
184: 35,
|
||||||
|
212: 39,
|
||||||
|
228: 40,
|
||||||
|
236: 41,
|
||||||
|
260: 43,
|
||||||
|
288: 44,
|
||||||
|
292: 45,
|
||||||
|
300: 48,
|
||||||
|
436: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
0: 48,
|
||||||
|
132: 35,
|
||||||
|
244: 26,
|
||||||
|
304: 22,
|
||||||
|
312: 23,
|
||||||
|
316: 24,
|
||||||
|
320: 0,
|
||||||
|
},
|
||||||
|
]
|
1008
objdiff-core/tests/snapshots/arch_ppc__read_ppc-2.snap
Normal file
1008
objdiff-core/tests/snapshots/arch_ppc__read_ppc-2.snap
Normal file
File diff suppressed because it is too large
Load Diff
70
objdiff-core/tests/snapshots/arch_ppc__read_ppc-3.snap
Normal file
70
objdiff-core/tests/snapshots/arch_ppc__read_ppc-3.snap
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
---
|
||||||
|
source: objdiff-core/tests/arch_ppc.rs
|
||||||
|
expression: output
|
||||||
|
---
|
||||||
|
[(Address(0), None), (Spacing(4), None), (Opcode("srwi", 60), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Opaque("24")), None), (Eol, None)]
|
||||||
|
[(Address(4), None), (Spacing(4), None), (Opcode("cmpwi", 38), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Argument(Signed(-1)), None), (Eol, None)]
|
||||||
|
[(Address(8), None), (Spacing(4), None), (Opcode("bne", 43), None), (BranchDest(20), None), (Basic(" ~>"), Some(0)), (Eol, None)]
|
||||||
|
[(Address(12), None), (Spacing(4), None), (Opcode("li", 41), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Argument(Signed(-1)), None), (Eol, None)]
|
||||||
|
[(Address(16), None), (Spacing(4), None), (Opcode("b", 45), None), (BranchDest(32), None), (Basic(" ~>"), Some(1)), (Eol, None)]
|
||||||
|
[(Address(20), None), (Basic(" ~> "), Some(0)), (Opcode("lis", 42), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Unsigned(0)), None), (Eol, None)]
|
||||||
|
[(Address(24), None), (Spacing(4), None), (Opcode("addi", 41), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Signed(0)), None), (Eol, None)]
|
||||||
|
[(Address(28), None), (Spacing(4), None), (Opcode("lbzx", 94), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Opaque("r0")), None), (Eol, None)]
|
||||||
|
[(Address(32), None), (Basic(" ~> "), Some(1)), (Opcode("extrwi", 60), None), (Argument(Opaque("r5")), None), (Basic(", "), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Opaque("8")), None), (Basic(", "), None), (Argument(Opaque("8")), None), (Eol, None)]
|
||||||
|
[(Address(36), None), (Spacing(4), None), (Opcode("stb", 166), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), None), (Basic("@sda21"), None), (Eol, None)]
|
||||||
|
[(Address(40), None), (Spacing(4), None), (Opcode("cmpwi", 38), None), (Argument(Opaque("r5")), None), (Basic(", "), None), (Argument(Signed(-1)), None), (Eol, None)]
|
||||||
|
[(Address(44), None), (Spacing(4), None), (Opcode("bne", 43), None), (BranchDest(56), None), (Basic(" ~>"), Some(2)), (Eol, None)]
|
||||||
|
[(Address(48), None), (Spacing(4), None), (Opcode("li", 41), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Argument(Signed(-1)), None), (Eol, None)]
|
||||||
|
[(Address(52), None), (Spacing(4), None), (Opcode("b", 45), None), (BranchDest(68), None), (Basic(" ~>"), Some(3)), (Eol, None)]
|
||||||
|
[(Address(56), None), (Basic(" ~> "), Some(2)), (Opcode("lis", 42), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Unsigned(0)), None), (Eol, None)]
|
||||||
|
[(Address(60), None), (Spacing(4), None), (Opcode("addi", 41), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Signed(0)), None), (Eol, None)]
|
||||||
|
[(Address(64), None), (Spacing(4), None), (Opcode("lbzx", 94), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Opaque("r5")), None), (Eol, None)]
|
||||||
|
[(Address(68), None), (Basic(" ~> "), Some(3)), (Opcode("extrwi", 60), None), (Argument(Opaque("r5")), None), (Basic(", "), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Opaque("8")), None), (Basic(", "), None), (Argument(Opaque("16")), None), (Eol, None)]
|
||||||
|
[(Address(72), None), (Spacing(4), None), (Opcode("li", 41), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), None), (Basic("@sda21"), None), (Eol, None)]
|
||||||
|
[(Address(76), None), (Spacing(4), None), (Opcode("cmpwi", 38), None), (Argument(Opaque("r5")), None), (Basic(", "), None), (Argument(Signed(-1)), None), (Eol, None)]
|
||||||
|
[(Address(80), None), (Spacing(4), None), (Opcode("stb", 166), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Argument(Signed(1)), None), (Basic("("), None), (Argument(Opaque("r4")), None), (Basic(")"), None), (Eol, None)]
|
||||||
|
[(Address(84), None), (Spacing(4), None), (Opcode("bne", 43), None), (BranchDest(96), None), (Basic(" ~>"), Some(4)), (Eol, None)]
|
||||||
|
[(Address(88), None), (Spacing(4), None), (Opcode("li", 41), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Argument(Signed(-1)), None), (Eol, None)]
|
||||||
|
[(Address(92), None), (Spacing(4), None), (Opcode("b", 45), None), (BranchDest(108), None), (Basic(" ~>"), Some(5)), (Eol, None)]
|
||||||
|
[(Address(96), None), (Basic(" ~> "), Some(4)), (Opcode("lis", 42), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Unsigned(0)), None), (Eol, None)]
|
||||||
|
[(Address(100), None), (Spacing(4), None), (Opcode("addi", 41), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Signed(0)), None), (Eol, None)]
|
||||||
|
[(Address(104), None), (Spacing(4), None), (Opcode("lbzx", 94), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Opaque("r5")), None), (Eol, None)]
|
||||||
|
[(Address(108), None), (Basic(" ~> "), Some(5)), (Opcode("clrlwi", 60), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Opaque("24")), None), (Eol, None)]
|
||||||
|
[(Address(112), None), (Spacing(4), None), (Opcode("li", 41), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), None), (Basic("@sda21"), None), (Eol, None)]
|
||||||
|
[(Address(116), None), (Spacing(4), None), (Opcode("cmpwi", 38), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Signed(-1)), None), (Eol, None)]
|
||||||
|
[(Address(120), None), (Spacing(4), None), (Opcode("stb", 166), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Argument(Signed(2)), None), (Basic("("), None), (Argument(Opaque("r3")), None), (Basic(")"), None), (Eol, None)]
|
||||||
|
[(Address(124), None), (Spacing(4), None), (Opcode("bne", 43), None), (BranchDest(136), None), (Basic(" ~>"), Some(6)), (Eol, None)]
|
||||||
|
[(Address(128), None), (Spacing(4), None), (Opcode("li", 41), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Signed(-1)), None), (Eol, None)]
|
||||||
|
[(Address(132), None), (Spacing(4), None), (Opcode("b", 45), None), (BranchDest(148), None), (Basic(" ~>"), Some(7)), (Eol, None)]
|
||||||
|
[(Address(136), None), (Basic(" ~> "), Some(6)), (Opcode("lis", 42), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Unsigned(0)), None), (Eol, None)]
|
||||||
|
[(Address(140), None), (Spacing(4), None), (Opcode("addi", 41), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Signed(0)), None), (Eol, None)]
|
||||||
|
[(Address(144), None), (Spacing(4), None), (Opcode("lbzx", 94), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Opaque("r4")), None), (Eol, None)]
|
||||||
|
[(Address(148), None), (Basic(" ~> "), Some(7)), (Opcode("li", 41), None), (Argument(Opaque("r5")), None), (Basic(", "), None), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), None), (Basic("@sda21"), None), (Eol, None)]
|
||||||
|
[(Address(152), None), (Spacing(4), None), (Opcode("li", 41), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Argument(Signed(0)), None), (Eol, None)]
|
||||||
|
[(Address(156), None), (Spacing(4), None), (Opcode("stb", 166), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Signed(3)), None), (Basic("("), None), (Argument(Opaque("r5")), None), (Basic(")"), None), (Eol, None)]
|
||||||
|
[(Address(160), None), (Spacing(4), None), (Opcode("lis", 42), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Unsigned(0)), None), (Eol, None)]
|
||||||
|
[(Address(164), None), (Spacing(4), None), (Opcode("addi", 41), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Signed(0)), None), (Eol, None)]
|
||||||
|
[(Address(168), None), (Spacing(4), None), (Opcode("stb", 166), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Argument(Signed(4)), None), (Basic("("), None), (Argument(Opaque("r5")), None), (Basic(")"), None), (Eol, None)]
|
||||||
|
[(Address(172), None), (Spacing(4), None), (Opcode("li", 41), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Argument(Signed(45)), None), (Eol, None)]
|
||||||
|
[(Address(176), None), (Spacing(4), None), (Opcode("lbz", 162), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), None), (Basic("@sda21"), None), (Eol, None)]
|
||||||
|
[(Address(180), None), (Spacing(4), None), (Opcode("lbzx", 94), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Opaque("r3")), None), (Eol, None)]
|
||||||
|
[(Address(184), None), (Spacing(4), None), (Opcode("andi.", 66), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Unsigned(220)), None), (Eol, None)]
|
||||||
|
[(Address(188), None), (Spacing(4), None), (Opcode("bne", 43), None), (BranchDest(196), None), (Basic(" ~>"), Some(8)), (Eol, None)]
|
||||||
|
[(Address(192), None), (Spacing(4), None), (Opcode("stb", 166), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Argument(Signed(0)), None), (Basic("("), None), (Argument(Opaque("r5")), None), (Basic(")"), None), (Eol, None)]
|
||||||
|
[(Address(196), None), (Basic(" ~> "), Some(8)), (Opcode("lbzu", 163), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Signed(1)), None), (Basic("("), None), (Argument(Opaque("r5")), None), (Basic(")"), None), (Eol, None)]
|
||||||
|
[(Address(200), None), (Spacing(4), None), (Opcode("lbzx", 94), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Opaque("r3")), None), (Eol, None)]
|
||||||
|
[(Address(204), None), (Spacing(4), None), (Opcode("andi.", 66), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Unsigned(220)), None), (Eol, None)]
|
||||||
|
[(Address(208), None), (Spacing(4), None), (Opcode("bne", 43), None), (BranchDest(216), None), (Basic(" ~>"), Some(9)), (Eol, None)]
|
||||||
|
[(Address(212), None), (Spacing(4), None), (Opcode("stb", 166), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Argument(Signed(0)), None), (Basic("("), None), (Argument(Opaque("r5")), None), (Basic(")"), None), (Eol, None)]
|
||||||
|
[(Address(216), None), (Basic(" ~> "), Some(9)), (Opcode("lbzu", 163), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Signed(1)), None), (Basic("("), None), (Argument(Opaque("r5")), None), (Basic(")"), None), (Eol, None)]
|
||||||
|
[(Address(220), None), (Spacing(4), None), (Opcode("lbzx", 94), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Opaque("r3")), None), (Eol, None)]
|
||||||
|
[(Address(224), None), (Spacing(4), None), (Opcode("andi.", 66), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Unsigned(220)), None), (Eol, None)]
|
||||||
|
[(Address(228), None), (Spacing(4), None), (Opcode("bne", 43), None), (BranchDest(236), None), (Basic(" ~>"), Some(10)), (Eol, None)]
|
||||||
|
[(Address(232), None), (Spacing(4), None), (Opcode("stb", 166), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Argument(Signed(0)), None), (Basic("("), None), (Argument(Opaque("r5")), None), (Basic(")"), None), (Eol, None)]
|
||||||
|
[(Address(236), None), (Basic(" ~> "), Some(10)), (Opcode("lbzu", 163), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Signed(1)), None), (Basic("("), None), (Argument(Opaque("r5")), None), (Basic(")"), None), (Eol, None)]
|
||||||
|
[(Address(240), None), (Spacing(4), None), (Opcode("lbzx", 94), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Opaque("r3")), None), (Eol, None)]
|
||||||
|
[(Address(244), None), (Spacing(4), None), (Opcode("andi.", 66), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Unsigned(220)), None), (Eol, None)]
|
||||||
|
[(Address(248), None), (Spacing(4), None), (Opcode("bne", 43), None), (BranchDest(256), None), (Basic(" ~>"), Some(11)), (Eol, None)]
|
||||||
|
[(Address(252), None), (Spacing(4), None), (Opcode("stb", 166), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Argument(Signed(0)), None), (Basic("("), None), (Argument(Opaque("r5")), None), (Basic(")"), None), (Eol, None)]
|
||||||
|
[(Address(256), None), (Basic(" ~> "), Some(11)), (Opcode("li", 41), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), None), (Basic("@sda21"), None), (Eol, None)]
|
||||||
|
[(Address(260), None), (Spacing(4), None), (Opcode("blr", 47), None), (Eol, None)]
|
489
objdiff-core/tests/snapshots/arch_ppc__read_ppc.snap
Normal file
489
objdiff-core/tests/snapshots/arch_ppc__read_ppc.snap
Normal file
@ -0,0 +1,489 @@
|
|||||||
|
---
|
||||||
|
source: objdiff-core/tests/arch_ppc.rs
|
||||||
|
expression: obj
|
||||||
|
---
|
||||||
|
Object {
|
||||||
|
arch: ArchPpc {
|
||||||
|
extab: None,
|
||||||
|
},
|
||||||
|
symbols: [
|
||||||
|
Symbol {
|
||||||
|
name: "IObj.cpp",
|
||||||
|
demangled_name: None,
|
||||||
|
address: 0,
|
||||||
|
size: 0,
|
||||||
|
kind: Unknown,
|
||||||
|
section: None,
|
||||||
|
flags: FlagSet(Local),
|
||||||
|
align: None,
|
||||||
|
virtual_address: Some(
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
Symbol {
|
||||||
|
name: "[.text]",
|
||||||
|
demangled_name: None,
|
||||||
|
address: 0,
|
||||||
|
size: 0,
|
||||||
|
kind: Section,
|
||||||
|
section: Some(
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
flags: FlagSet(Local),
|
||||||
|
align: None,
|
||||||
|
virtual_address: Some(
|
||||||
|
2150895620,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
Symbol {
|
||||||
|
name: "[.ctors]",
|
||||||
|
demangled_name: None,
|
||||||
|
address: 0,
|
||||||
|
size: 0,
|
||||||
|
kind: Section,
|
||||||
|
section: Some(
|
||||||
|
1,
|
||||||
|
),
|
||||||
|
flags: FlagSet(Local),
|
||||||
|
align: None,
|
||||||
|
virtual_address: Some(
|
||||||
|
2151461704,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
Symbol {
|
||||||
|
name: "[.sbss]",
|
||||||
|
demangled_name: None,
|
||||||
|
address: 0,
|
||||||
|
size: 0,
|
||||||
|
kind: Section,
|
||||||
|
section: Some(
|
||||||
|
2,
|
||||||
|
),
|
||||||
|
flags: FlagSet(Local),
|
||||||
|
align: None,
|
||||||
|
virtual_address: Some(
|
||||||
|
2153420048,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
Symbol {
|
||||||
|
name: "__sinit_IObj_cpp",
|
||||||
|
demangled_name: None,
|
||||||
|
address: 264,
|
||||||
|
size: 20,
|
||||||
|
kind: Function,
|
||||||
|
section: Some(
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
flags: FlagSet(Local),
|
||||||
|
align: None,
|
||||||
|
virtual_address: Some(
|
||||||
|
2150895884,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
Symbol {
|
||||||
|
name: "text$52",
|
||||||
|
demangled_name: None,
|
||||||
|
address: 8,
|
||||||
|
size: 5,
|
||||||
|
kind: Object,
|
||||||
|
section: Some(
|
||||||
|
2,
|
||||||
|
),
|
||||||
|
flags: FlagSet(Local),
|
||||||
|
align: None,
|
||||||
|
virtual_address: Some(
|
||||||
|
2153420056,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
Symbol {
|
||||||
|
name: "Type2Text__10SObjectTagFUi",
|
||||||
|
demangled_name: Some(
|
||||||
|
"SObjectTag::Type2Text(unsigned int)",
|
||||||
|
),
|
||||||
|
address: 0,
|
||||||
|
size: 264,
|
||||||
|
kind: Function,
|
||||||
|
section: Some(
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
flags: FlagSet(Global),
|
||||||
|
align: None,
|
||||||
|
virtual_address: Some(
|
||||||
|
2150895620,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
Symbol {
|
||||||
|
name: "gkInvalidObjectTag",
|
||||||
|
demangled_name: None,
|
||||||
|
address: 0,
|
||||||
|
size: 8,
|
||||||
|
kind: Object,
|
||||||
|
section: Some(
|
||||||
|
2,
|
||||||
|
),
|
||||||
|
flags: FlagSet(Global),
|
||||||
|
align: None,
|
||||||
|
virtual_address: Some(
|
||||||
|
2153420048,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
Symbol {
|
||||||
|
name: "__upper_map",
|
||||||
|
demangled_name: None,
|
||||||
|
address: 0,
|
||||||
|
size: 0,
|
||||||
|
kind: Unknown,
|
||||||
|
section: None,
|
||||||
|
flags: FlagSet(Global),
|
||||||
|
align: None,
|
||||||
|
virtual_address: Some(
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
Symbol {
|
||||||
|
name: "__ctype_map",
|
||||||
|
demangled_name: None,
|
||||||
|
address: 0,
|
||||||
|
size: 0,
|
||||||
|
kind: Unknown,
|
||||||
|
section: None,
|
||||||
|
flags: FlagSet(Global),
|
||||||
|
align: None,
|
||||||
|
virtual_address: Some(
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
sections: [
|
||||||
|
Section {
|
||||||
|
id: ".text-0",
|
||||||
|
name: ".text",
|
||||||
|
address: 0,
|
||||||
|
size: 284,
|
||||||
|
kind: Code,
|
||||||
|
data: SectionData(
|
||||||
|
284,
|
||||||
|
),
|
||||||
|
flags: FlagSet(),
|
||||||
|
relocations: [
|
||||||
|
Relocation {
|
||||||
|
flags: Elf(
|
||||||
|
6,
|
||||||
|
),
|
||||||
|
address: 22,
|
||||||
|
target_symbol: 8,
|
||||||
|
addend: 0,
|
||||||
|
},
|
||||||
|
Relocation {
|
||||||
|
flags: Elf(
|
||||||
|
4,
|
||||||
|
),
|
||||||
|
address: 26,
|
||||||
|
target_symbol: 8,
|
||||||
|
addend: 0,
|
||||||
|
},
|
||||||
|
Relocation {
|
||||||
|
flags: Elf(
|
||||||
|
109,
|
||||||
|
),
|
||||||
|
address: 36,
|
||||||
|
target_symbol: 5,
|
||||||
|
addend: 0,
|
||||||
|
},
|
||||||
|
Relocation {
|
||||||
|
flags: Elf(
|
||||||
|
6,
|
||||||
|
),
|
||||||
|
address: 58,
|
||||||
|
target_symbol: 8,
|
||||||
|
addend: 0,
|
||||||
|
},
|
||||||
|
Relocation {
|
||||||
|
flags: Elf(
|
||||||
|
4,
|
||||||
|
),
|
||||||
|
address: 62,
|
||||||
|
target_symbol: 8,
|
||||||
|
addend: 0,
|
||||||
|
},
|
||||||
|
Relocation {
|
||||||
|
flags: Elf(
|
||||||
|
109,
|
||||||
|
),
|
||||||
|
address: 72,
|
||||||
|
target_symbol: 5,
|
||||||
|
addend: 0,
|
||||||
|
},
|
||||||
|
Relocation {
|
||||||
|
flags: Elf(
|
||||||
|
6,
|
||||||
|
),
|
||||||
|
address: 98,
|
||||||
|
target_symbol: 8,
|
||||||
|
addend: 0,
|
||||||
|
},
|
||||||
|
Relocation {
|
||||||
|
flags: Elf(
|
||||||
|
4,
|
||||||
|
),
|
||||||
|
address: 102,
|
||||||
|
target_symbol: 8,
|
||||||
|
addend: 0,
|
||||||
|
},
|
||||||
|
Relocation {
|
||||||
|
flags: Elf(
|
||||||
|
109,
|
||||||
|
),
|
||||||
|
address: 112,
|
||||||
|
target_symbol: 5,
|
||||||
|
addend: 0,
|
||||||
|
},
|
||||||
|
Relocation {
|
||||||
|
flags: Elf(
|
||||||
|
6,
|
||||||
|
),
|
||||||
|
address: 138,
|
||||||
|
target_symbol: 8,
|
||||||
|
addend: 0,
|
||||||
|
},
|
||||||
|
Relocation {
|
||||||
|
flags: Elf(
|
||||||
|
4,
|
||||||
|
),
|
||||||
|
address: 142,
|
||||||
|
target_symbol: 8,
|
||||||
|
addend: 0,
|
||||||
|
},
|
||||||
|
Relocation {
|
||||||
|
flags: Elf(
|
||||||
|
109,
|
||||||
|
),
|
||||||
|
address: 148,
|
||||||
|
target_symbol: 5,
|
||||||
|
addend: 0,
|
||||||
|
},
|
||||||
|
Relocation {
|
||||||
|
flags: Elf(
|
||||||
|
6,
|
||||||
|
),
|
||||||
|
address: 162,
|
||||||
|
target_symbol: 9,
|
||||||
|
addend: 0,
|
||||||
|
},
|
||||||
|
Relocation {
|
||||||
|
flags: Elf(
|
||||||
|
4,
|
||||||
|
),
|
||||||
|
address: 166,
|
||||||
|
target_symbol: 9,
|
||||||
|
addend: 0,
|
||||||
|
},
|
||||||
|
Relocation {
|
||||||
|
flags: Elf(
|
||||||
|
109,
|
||||||
|
),
|
||||||
|
address: 176,
|
||||||
|
target_symbol: 5,
|
||||||
|
addend: 0,
|
||||||
|
},
|
||||||
|
Relocation {
|
||||||
|
flags: Elf(
|
||||||
|
109,
|
||||||
|
),
|
||||||
|
address: 256,
|
||||||
|
target_symbol: 5,
|
||||||
|
addend: 0,
|
||||||
|
},
|
||||||
|
Relocation {
|
||||||
|
flags: Elf(
|
||||||
|
109,
|
||||||
|
),
|
||||||
|
address: 268,
|
||||||
|
target_symbol: 7,
|
||||||
|
addend: 0,
|
||||||
|
},
|
||||||
|
Relocation {
|
||||||
|
flags: Elf(
|
||||||
|
109,
|
||||||
|
),
|
||||||
|
address: 272,
|
||||||
|
target_symbol: 7,
|
||||||
|
addend: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
line_info: {},
|
||||||
|
virtual_address: Some(
|
||||||
|
2150895620,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
Section {
|
||||||
|
id: ".ctors-0",
|
||||||
|
name: ".ctors",
|
||||||
|
address: 0,
|
||||||
|
size: 4,
|
||||||
|
kind: Data,
|
||||||
|
data: SectionData(
|
||||||
|
4,
|
||||||
|
),
|
||||||
|
flags: FlagSet(),
|
||||||
|
relocations: [
|
||||||
|
Relocation {
|
||||||
|
flags: Elf(
|
||||||
|
1,
|
||||||
|
),
|
||||||
|
address: 0,
|
||||||
|
target_symbol: 4,
|
||||||
|
addend: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
line_info: {},
|
||||||
|
virtual_address: Some(
|
||||||
|
2151461704,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
Section {
|
||||||
|
id: ".sbss-0",
|
||||||
|
name: ".sbss",
|
||||||
|
address: 0,
|
||||||
|
size: 16,
|
||||||
|
kind: Bss,
|
||||||
|
data: SectionData(
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
flags: FlagSet(),
|
||||||
|
relocations: [],
|
||||||
|
line_info: {},
|
||||||
|
virtual_address: Some(
|
||||||
|
2153420048,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
Section {
|
||||||
|
id: ".rela.text-0",
|
||||||
|
name: ".rela.text",
|
||||||
|
address: 0,
|
||||||
|
size: 216,
|
||||||
|
kind: Unknown,
|
||||||
|
data: SectionData(
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
flags: FlagSet(),
|
||||||
|
relocations: [],
|
||||||
|
line_info: {},
|
||||||
|
virtual_address: None,
|
||||||
|
},
|
||||||
|
Section {
|
||||||
|
id: ".rela.ctors-0",
|
||||||
|
name: ".rela.ctors",
|
||||||
|
address: 0,
|
||||||
|
size: 12,
|
||||||
|
kind: Unknown,
|
||||||
|
data: SectionData(
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
flags: FlagSet(),
|
||||||
|
relocations: [],
|
||||||
|
line_info: {},
|
||||||
|
virtual_address: None,
|
||||||
|
},
|
||||||
|
Section {
|
||||||
|
id: ".symtab-0",
|
||||||
|
name: ".symtab",
|
||||||
|
address: 0,
|
||||||
|
size: 176,
|
||||||
|
kind: Unknown,
|
||||||
|
data: SectionData(
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
flags: FlagSet(),
|
||||||
|
relocations: [],
|
||||||
|
line_info: {},
|
||||||
|
virtual_address: None,
|
||||||
|
},
|
||||||
|
Section {
|
||||||
|
id: ".strtab-0",
|
||||||
|
name: ".strtab",
|
||||||
|
address: 0,
|
||||||
|
size: 105,
|
||||||
|
kind: Unknown,
|
||||||
|
data: SectionData(
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
flags: FlagSet(),
|
||||||
|
relocations: [],
|
||||||
|
line_info: {},
|
||||||
|
virtual_address: None,
|
||||||
|
},
|
||||||
|
Section {
|
||||||
|
id: ".shstrtab-0",
|
||||||
|
name: ".shstrtab",
|
||||||
|
address: 0,
|
||||||
|
size: 77,
|
||||||
|
kind: Unknown,
|
||||||
|
data: SectionData(
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
flags: FlagSet(),
|
||||||
|
relocations: [],
|
||||||
|
line_info: {},
|
||||||
|
virtual_address: None,
|
||||||
|
},
|
||||||
|
Section {
|
||||||
|
id: ".comment-0",
|
||||||
|
name: ".comment",
|
||||||
|
address: 0,
|
||||||
|
size: 132,
|
||||||
|
kind: Unknown,
|
||||||
|
data: SectionData(
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
flags: FlagSet(),
|
||||||
|
relocations: [],
|
||||||
|
line_info: {},
|
||||||
|
virtual_address: None,
|
||||||
|
},
|
||||||
|
Section {
|
||||||
|
id: ".note.split-0",
|
||||||
|
name: ".note.split",
|
||||||
|
address: 0,
|
||||||
|
size: 152,
|
||||||
|
kind: Unknown,
|
||||||
|
data: SectionData(
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
flags: FlagSet(),
|
||||||
|
relocations: [],
|
||||||
|
line_info: {},
|
||||||
|
virtual_address: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
split_meta: Some(
|
||||||
|
SplitMeta {
|
||||||
|
generator: Some(
|
||||||
|
"decomp-toolkit 1.4.0",
|
||||||
|
),
|
||||||
|
module_name: Some(
|
||||||
|
"main",
|
||||||
|
),
|
||||||
|
module_id: Some(
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
virtual_addresses: Some(
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
2150895620,
|
||||||
|
2151461704,
|
||||||
|
2153420048,
|
||||||
|
2150895884,
|
||||||
|
2153420056,
|
||||||
|
2150895620,
|
||||||
|
2153420048,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
path: None,
|
||||||
|
timestamp: None,
|
||||||
|
}
|
@ -1,29 +0,0 @@
|
|||||||
package objdiff:core;
|
|
||||||
|
|
||||||
interface diff {
|
|
||||||
resource diff-config {
|
|
||||||
constructor();
|
|
||||||
set-property: func(id: string, value: string) -> result<_, string>;
|
|
||||||
get-property: func(id: string) -> result<string, string>;
|
|
||||||
}
|
|
||||||
|
|
||||||
resource object {
|
|
||||||
parse: static func(
|
|
||||||
data: list<u8>,
|
|
||||||
config: borrow<diff-config>,
|
|
||||||
) -> result<object, string>;
|
|
||||||
}
|
|
||||||
|
|
||||||
run-diff: func(
|
|
||||||
left: option<borrow<object>>,
|
|
||||||
right: option<borrow<object>>,
|
|
||||||
config: borrow<diff-config>,
|
|
||||||
) -> result<list<u8>, string>;
|
|
||||||
}
|
|
||||||
|
|
||||||
world api {
|
|
||||||
export diff;
|
|
||||||
|
|
||||||
export init: func() -> result<_, string>;
|
|
||||||
export version: func() -> string;
|
|
||||||
}
|
|
@ -38,7 +38,7 @@ float-ord = "0.3"
|
|||||||
font-kit = "0.14"
|
font-kit = "0.14"
|
||||||
globset = { version = "0.4", features = ["serde1"] }
|
globset = { version = "0.4", features = ["serde1"] }
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
objdiff-core = { path = "../objdiff-core", features = ["all"] }
|
objdiff-core = { path = "../objdiff-core", features = ["ppc", "arm", "arm64", "mips", "std", "config", "dwarf", "bindings", "serde", "build"] }
|
||||||
open = "5.3"
|
open = "5.3"
|
||||||
png = "0.17"
|
png = "0.17"
|
||||||
pollster = "0.4"
|
pollster = "0.4"
|
||||||
@ -52,6 +52,8 @@ shell-escape = "0.1"
|
|||||||
strum = { version = "0.26", features = ["derive"] }
|
strum = { version = "0.26", features = ["derive"] }
|
||||||
time = { version = "0.3", features = ["formatting", "local-offset"] }
|
time = { version = "0.3", features = ["formatting", "local-offset"] }
|
||||||
typed-path = "0.10"
|
typed-path = "0.10"
|
||||||
|
winit = { version = "0.30", features = ["wayland-csd-adwaita"] }
|
||||||
|
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||||
|
|
||||||
# Keep version in sync with egui
|
# Keep version in sync with egui
|
||||||
[dependencies.eframe]
|
[dependencies.eframe]
|
||||||
@ -81,15 +83,6 @@ winapi = "0.3"
|
|||||||
[target.'cfg(unix)'.dependencies]
|
[target.'cfg(unix)'.dependencies]
|
||||||
exec = "0.3"
|
exec = "0.3"
|
||||||
|
|
||||||
# native:
|
|
||||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
|
||||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
|
||||||
|
|
||||||
# web:
|
|
||||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
|
||||||
console_error_panic_hook = "0.1"
|
|
||||||
tracing-wasm = "0.2"
|
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
|
|
||||||
|
@ -783,7 +783,8 @@ impl eframe::App for App {
|
|||||||
|
|
||||||
let mut action = None;
|
let mut action = None;
|
||||||
egui::CentralPanel::default().show(ctx, |ui| {
|
egui::CentralPanel::default().show(ctx, |ui| {
|
||||||
action = diff_view_ui(ui, diff_state, appearance);
|
let state = state.read().unwrap();
|
||||||
|
action = diff_view_ui(ui, diff_state, appearance, &state.config.diff_obj_config);
|
||||||
});
|
});
|
||||||
|
|
||||||
project_window(ctx, state, show_project_config, config_state, appearance);
|
project_window(ctx, state, show_project_config, config_state, appearance);
|
||||||
|
@ -273,6 +273,7 @@ impl DiffObjConfigV1 {
|
|||||||
},
|
},
|
||||||
space_between_args: self.space_between_args,
|
space_between_args: self.space_between_args,
|
||||||
combine_data_sections: self.combine_data_sections,
|
combine_data_sections: self.combine_data_sections,
|
||||||
|
combine_text_sections: false,
|
||||||
x86_formatter: self.x86_formatter,
|
x86_formatter: self.x86_formatter,
|
||||||
mips_abi: self.mips_abi,
|
mips_abi: self.mips_abi,
|
||||||
mips_instr_category: self.mips_instr_category,
|
mips_instr_category: self.mips_instr_category,
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
#![allow(clippy::too_many_arguments)]
|
||||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release
|
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release
|
||||||
|
|
||||||
mod app;
|
mod app;
|
||||||
@ -37,8 +38,6 @@ fn load_icon() -> Result<egui::IconData> {
|
|||||||
|
|
||||||
const APP_NAME: &str = "objdiff";
|
const APP_NAME: &str = "objdiff";
|
||||||
|
|
||||||
// When compiling natively:
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
fn main() -> ExitCode {
|
fn main() -> ExitCode {
|
||||||
// Log to stdout (if you run with `RUST_LOG=debug`).
|
// Log to stdout (if you run with `RUST_LOG=debug`).
|
||||||
tracing_subscriber::fmt()
|
tracing_subscriber::fmt()
|
||||||
@ -229,21 +228,3 @@ fn run_eframe(
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// when compiling to web using trunk.
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
fn main() {
|
|
||||||
// Make sure panics are logged using `console.error`.
|
|
||||||
console_error_panic_hook::set_once();
|
|
||||||
|
|
||||||
// Redirect tracing to console.log and friends:
|
|
||||||
tracing_wasm::set_as_global_default();
|
|
||||||
|
|
||||||
let web_options = eframe::WebOptions::default();
|
|
||||||
eframe::start_web(
|
|
||||||
"the_canvas_id", // hardcode it
|
|
||||||
web_options,
|
|
||||||
Box::new(|cc| Box::new(eframe_template::TemplateApp::new(cc))),
|
|
||||||
)
|
|
||||||
.expect("failed to start eframe");
|
|
||||||
}
|
|
||||||
|
@ -1,13 +1,9 @@
|
|||||||
use std::{
|
use std::{cmp::min, default::Default, mem::take};
|
||||||
cmp::{min, Ordering},
|
|
||||||
default::Default,
|
|
||||||
mem::take,
|
|
||||||
};
|
|
||||||
|
|
||||||
use egui::{text::LayoutJob, Label, Sense, Widget};
|
use egui::{text::LayoutJob, Label, Sense, Widget};
|
||||||
use objdiff_core::{
|
use objdiff_core::{
|
||||||
diff::{ObjDataDiff, ObjDataDiffKind, ObjDataRelocDiff},
|
diff::{DataDiff, DataDiffKind, DataRelocationDiff},
|
||||||
obj::ObjInfo,
|
obj::Object,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::views::{appearance::Appearance, write_text};
|
use crate::views::{appearance::Appearance, write_text};
|
||||||
@ -16,108 +12,110 @@ pub(crate) const BYTES_PER_ROW: usize = 16;
|
|||||||
|
|
||||||
fn data_row_hover_ui(
|
fn data_row_hover_ui(
|
||||||
ui: &mut egui::Ui,
|
ui: &mut egui::Ui,
|
||||||
obj: &ObjInfo,
|
_obj: &Object,
|
||||||
diffs: &[(ObjDataDiff, Vec<ObjDataRelocDiff>)],
|
_diffs: &[(DataDiff, Vec<DataRelocationDiff>)],
|
||||||
appearance: &Appearance,
|
_appearance: &Appearance,
|
||||||
) {
|
) {
|
||||||
ui.scope(|ui| {
|
ui.scope(|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_mode = Some(egui::TextWrapMode::Extend);
|
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
|
||||||
|
|
||||||
let reloc_diffs = diffs.iter().flat_map(|(_, reloc_diffs)| reloc_diffs);
|
// TODO
|
||||||
let mut prev_reloc = None;
|
// let reloc_diffs = diffs.iter().flat_map(|(_, reloc_diffs)| reloc_diffs);
|
||||||
for reloc_diff in reloc_diffs {
|
// let mut prev_reloc = None;
|
||||||
let reloc = &reloc_diff.reloc;
|
// for reloc_diff in reloc_diffs {
|
||||||
if prev_reloc == Some(reloc) {
|
// let reloc = &reloc_diff.reloc;
|
||||||
// Avoid showing consecutive duplicate relocations.
|
// if prev_reloc == Some(reloc) {
|
||||||
// We do this because a single relocation can span across multiple diffs if the
|
// // Avoid showing consecutive duplicate relocations.
|
||||||
// bytes in the relocation changed (e.g. first byte is added, second is unchanged).
|
// // We do this because a single relocation can span across multiple diffs if the
|
||||||
continue;
|
// // bytes in the relocation changed (e.g. first byte is added, second is unchanged).
|
||||||
}
|
// continue;
|
||||||
prev_reloc = Some(reloc);
|
// }
|
||||||
|
// prev_reloc = Some(reloc);
|
||||||
let color = get_color_for_diff_kind(reloc_diff.kind, appearance);
|
//
|
||||||
|
// let color = get_color_for_diff_kind(reloc_diff.kind, appearance);
|
||||||
// TODO: Most of this code is copy-pasted from ins_hover_ui.
|
//
|
||||||
// Try to separate this out into a shared function.
|
// // TODO: Most of this code is copy-pasted from ins_hover_ui.
|
||||||
ui.label(format!("Relocation type: {}", obj.arch.display_reloc(reloc.flags)));
|
// // Try to separate this out into a shared function.
|
||||||
ui.label(format!("Relocation address: {:x}", reloc.address));
|
// ui.label(format!("Relocation type: {}", obj.arch.display_reloc(reloc.flags)));
|
||||||
let addend_str = match reloc.addend.cmp(&0i64) {
|
// ui.label(format!("Relocation address: {:x}", reloc.address));
|
||||||
Ordering::Greater => format!("+{:x}", reloc.addend),
|
// let addend_str = match reloc.addend.cmp(&0i64) {
|
||||||
Ordering::Less => format!("-{:x}", -reloc.addend),
|
// Ordering::Greater => format!("+{:x}", reloc.addend),
|
||||||
_ => "".to_string(),
|
// Ordering::Less => format!("-{:x}", -reloc.addend),
|
||||||
};
|
// _ => "".to_string(),
|
||||||
ui.colored_label(color, format!("Name: {}{}", reloc.target.name, addend_str));
|
// };
|
||||||
if let Some(orig_section_index) = reloc.target.orig_section_index {
|
// ui.colored_label(color, format!("Name: {}{}", reloc.target.name, addend_str));
|
||||||
if let Some(section) =
|
// if let Some(orig_section_index) = reloc.target.orig_section_index {
|
||||||
obj.sections.iter().find(|s| s.orig_index == orig_section_index)
|
// if let Some(section) =
|
||||||
{
|
// obj.sections.iter().find(|s| s.orig_index == orig_section_index)
|
||||||
ui.colored_label(color, format!("Section: {}", section.name));
|
// {
|
||||||
}
|
// ui.colored_label(color, format!("Section: {}", section.name));
|
||||||
ui.colored_label(
|
// }
|
||||||
color,
|
// ui.colored_label(
|
||||||
format!("Address: {:x}{}", reloc.target.address, addend_str),
|
// color,
|
||||||
);
|
// format!("Address: {:x}{}", reloc.target.address, addend_str),
|
||||||
ui.colored_label(color, format!("Size: {:x}", reloc.target.size));
|
// );
|
||||||
if reloc.addend >= 0 && reloc.target.bytes.len() > reloc.addend as usize {}
|
// ui.colored_label(color, format!("Size: {:x}", reloc.target.size));
|
||||||
} else {
|
// if reloc.addend >= 0 && reloc.target.bytes.len() > reloc.addend as usize {}
|
||||||
ui.colored_label(color, "Extern".to_string());
|
// } else {
|
||||||
}
|
// ui.colored_label(color, "Extern".to_string());
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn data_row_context_menu(ui: &mut egui::Ui, diffs: &[(ObjDataDiff, Vec<ObjDataRelocDiff>)]) {
|
fn data_row_context_menu(ui: &mut egui::Ui, _diffs: &[(DataDiff, Vec<DataRelocationDiff>)]) {
|
||||||
ui.scope(|ui| {
|
ui.scope(|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_mode = Some(egui::TextWrapMode::Extend);
|
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
|
||||||
|
|
||||||
let reloc_diffs = diffs.iter().flat_map(|(_, reloc_diffs)| reloc_diffs);
|
// TODO
|
||||||
let mut prev_reloc = None;
|
// let reloc_diffs = diffs.iter().flat_map(|(_, reloc_diffs)| reloc_diffs);
|
||||||
for reloc_diff in reloc_diffs {
|
// let mut prev_reloc = None;
|
||||||
let reloc = &reloc_diff.reloc;
|
// for reloc_diff in reloc_diffs {
|
||||||
if prev_reloc == Some(reloc) {
|
// let reloc = &reloc_diff.reloc;
|
||||||
// Avoid showing consecutive duplicate relocations.
|
// if prev_reloc == Some(reloc) {
|
||||||
// We do this because a single relocation can span across multiple diffs if the
|
// // Avoid showing consecutive duplicate relocations.
|
||||||
// bytes in the relocation changed (e.g. first byte is added, second is unchanged).
|
// // We do this because a single relocation can span across multiple diffs if the
|
||||||
continue;
|
// // bytes in the relocation changed (e.g. first byte is added, second is unchanged).
|
||||||
}
|
// continue;
|
||||||
prev_reloc = Some(reloc);
|
// }
|
||||||
|
// prev_reloc = Some(reloc);
|
||||||
// TODO: This code is copy-pasted from ins_context_menu.
|
//
|
||||||
// Try to separate this out into a shared function.
|
// // TODO: This code is copy-pasted from ins_context_menu.
|
||||||
if let Some(name) = &reloc.target.demangled_name {
|
// // Try to separate this out into a shared function.
|
||||||
if ui.button(format!("Copy \"{name}\"")).clicked() {
|
// if let Some(name) = &reloc.target.demangled_name {
|
||||||
ui.output_mut(|output| output.copied_text.clone_from(name));
|
// if ui.button(format!("Copy \"{name}\"")).clicked() {
|
||||||
ui.close_menu();
|
// ui.output_mut(|output| output.copied_text.clone_from(name));
|
||||||
}
|
// ui.close_menu();
|
||||||
}
|
// }
|
||||||
if ui.button(format!("Copy \"{}\"", reloc.target.name)).clicked() {
|
// }
|
||||||
ui.output_mut(|output| output.copied_text.clone_from(&reloc.target.name));
|
// if ui.button(format!("Copy \"{}\"", reloc.target.name)).clicked() {
|
||||||
ui.close_menu();
|
// ui.output_mut(|output| output.copied_text.clone_from(&reloc.target.name));
|
||||||
}
|
// ui.close_menu();
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_color_for_diff_kind(diff_kind: ObjDataDiffKind, appearance: &Appearance) -> egui::Color32 {
|
fn get_color_for_diff_kind(diff_kind: DataDiffKind, appearance: &Appearance) -> egui::Color32 {
|
||||||
match diff_kind {
|
match diff_kind {
|
||||||
ObjDataDiffKind::None => appearance.text_color,
|
DataDiffKind::None => appearance.text_color,
|
||||||
ObjDataDiffKind::Replace => appearance.replace_color,
|
DataDiffKind::Replace => appearance.replace_color,
|
||||||
ObjDataDiffKind::Delete => appearance.delete_color,
|
DataDiffKind::Delete => appearance.delete_color,
|
||||||
ObjDataDiffKind::Insert => appearance.insert_color,
|
DataDiffKind::Insert => appearance.insert_color,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn data_row_ui(
|
pub(crate) fn data_row_ui(
|
||||||
ui: &mut egui::Ui,
|
ui: &mut egui::Ui,
|
||||||
obj: Option<&ObjInfo>,
|
obj: Option<&Object>,
|
||||||
address: usize,
|
address: usize,
|
||||||
diffs: &[(ObjDataDiff, Vec<ObjDataRelocDiff>)],
|
diffs: &[(DataDiff, Vec<DataRelocationDiff>)],
|
||||||
appearance: &Appearance,
|
appearance: &Appearance,
|
||||||
) {
|
) {
|
||||||
if diffs.iter().any(|(dd, rds)| {
|
if diffs.iter().any(|(dd, rds)| {
|
||||||
dd.kind != ObjDataDiffKind::None || rds.iter().any(|rd| rd.kind != ObjDataDiffKind::None)
|
dd.kind != DataDiffKind::None || rds.iter().any(|rd| rd.kind != DataDiffKind::None)
|
||||||
}) {
|
}) {
|
||||||
ui.painter().rect_filled(ui.available_rect_before_wrap(), 0.0, ui.visuals().faint_bg_color);
|
ui.painter().rect_filled(ui.available_rect_before_wrap(), 0.0, ui.visuals().faint_bg_color);
|
||||||
}
|
}
|
||||||
@ -145,7 +143,7 @@ pub(crate) fn data_row_ui(
|
|||||||
for byte in &diff.data {
|
for byte in &diff.data {
|
||||||
let mut byte_color = base_color;
|
let mut byte_color = base_color;
|
||||||
if let Some(reloc_diff) = reloc_diffs.iter().find(|reloc_diff| {
|
if let Some(reloc_diff) = reloc_diffs.iter().find(|reloc_diff| {
|
||||||
reloc_diff.kind != ObjDataDiffKind::None
|
reloc_diff.kind != DataDiffKind::None
|
||||||
&& reloc_diff.range.contains(&cur_addr_actual)
|
&& reloc_diff.range.contains(&cur_addr_actual)
|
||||||
}) {
|
}) {
|
||||||
byte_color = get_color_for_diff_kind(reloc_diff.kind, appearance);
|
byte_color = get_color_for_diff_kind(reloc_diff.kind, appearance);
|
||||||
@ -200,11 +198,11 @@ pub(crate) fn data_row_ui(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn split_diffs(
|
pub(crate) fn split_diffs(
|
||||||
diffs: &[ObjDataDiff],
|
diffs: &[DataDiff],
|
||||||
reloc_diffs: &[ObjDataRelocDiff],
|
reloc_diffs: &[DataRelocationDiff],
|
||||||
) -> Vec<Vec<(ObjDataDiff, Vec<ObjDataRelocDiff>)>> {
|
) -> Vec<Vec<(DataDiff, Vec<DataRelocationDiff>)>> {
|
||||||
let mut split_diffs = Vec::<Vec<(ObjDataDiff, Vec<ObjDataRelocDiff>)>>::new();
|
let mut split_diffs = Vec::<Vec<(DataDiff, Vec<DataRelocationDiff>)>>::new();
|
||||||
let mut row_diffs = Vec::<(ObjDataDiff, Vec<ObjDataRelocDiff>)>::new();
|
let mut row_diffs = Vec::<(DataDiff, Vec<DataRelocationDiff>)>::new();
|
||||||
// The offset shown on the side of the GUI, shifted by insertions/deletions.
|
// The offset shown on the side of the GUI, shifted by insertions/deletions.
|
||||||
let mut cur_addr = 0usize;
|
let mut cur_addr = 0usize;
|
||||||
// The offset into the actual bytes of the section on this side, ignoring differences.
|
// The offset into the actual bytes of the section on this side, ignoring differences.
|
||||||
@ -216,7 +214,7 @@ pub(crate) fn split_diffs(
|
|||||||
let mut remaining_in_row = BYTES_PER_ROW - (cur_addr % BYTES_PER_ROW);
|
let mut remaining_in_row = BYTES_PER_ROW - (cur_addr % BYTES_PER_ROW);
|
||||||
let len = min(remaining_len, remaining_in_row);
|
let len = min(remaining_len, remaining_in_row);
|
||||||
|
|
||||||
let data_diff = ObjDataDiff {
|
let data_diff = DataDiff {
|
||||||
data: if diff.data.is_empty() {
|
data: if diff.data.is_empty() {
|
||||||
Vec::new()
|
Vec::new()
|
||||||
} else {
|
} else {
|
||||||
@ -226,7 +224,7 @@ pub(crate) fn split_diffs(
|
|||||||
len,
|
len,
|
||||||
symbol: String::new(), // TODO
|
symbol: String::new(), // TODO
|
||||||
};
|
};
|
||||||
let row_reloc_diffs: Vec<ObjDataRelocDiff> = if diff.data.is_empty() {
|
let row_reloc_diffs: Vec<DataRelocationDiff> = if diff.data.is_empty() {
|
||||||
Vec::new()
|
Vec::new()
|
||||||
} else {
|
} else {
|
||||||
let diff_range = cur_addr_actual + cur_len..cur_addr_actual + cur_len + len;
|
let diff_range = cur_addr_actual + cur_len..cur_addr_actual + cur_len + len;
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
use egui::{Id, Layout, RichText, ScrollArea, TextEdit, Ui, Widget};
|
use egui::{Id, Layout, RichText, ScrollArea, TextEdit, Ui, Widget};
|
||||||
use objdiff_core::{
|
use objdiff_core::{
|
||||||
build::BuildStatus,
|
build::BuildStatus,
|
||||||
diff::{ObjDiff, ObjSectionDiff, ObjSymbolDiff},
|
diff::{display::SymbolFilter, DiffObjConfig, ObjectDiff, SectionDiff, SymbolDiff},
|
||||||
obj::{ObjInfo, ObjSection, ObjSectionKind, ObjSymbol, SymbolRef},
|
obj::{Object, Section, SectionKind, Symbol},
|
||||||
};
|
};
|
||||||
use time::format_description;
|
use time::format_description;
|
||||||
|
|
||||||
@ -16,30 +16,30 @@ use crate::{
|
|||||||
function_diff::{asm_col_ui, FunctionDiffContext},
|
function_diff::{asm_col_ui, FunctionDiffContext},
|
||||||
symbol_diff::{
|
symbol_diff::{
|
||||||
match_color_for_symbol, symbol_list_ui, DiffViewAction, DiffViewNavigation,
|
match_color_for_symbol, symbol_list_ui, DiffViewAction, DiffViewNavigation,
|
||||||
DiffViewState, SymbolDiffContext, SymbolFilter, SymbolRefByName, View,
|
DiffViewState, SymbolDiffContext, SymbolRefByName, View,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
enum SelectedSymbol {
|
enum SelectedSymbol {
|
||||||
Symbol(SymbolRef),
|
Symbol(usize),
|
||||||
Section(usize),
|
Section(usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
struct DiffColumnContext<'a> {
|
struct DiffColumnContext<'a> {
|
||||||
status: &'a BuildStatus,
|
status: &'a BuildStatus,
|
||||||
obj: Option<&'a (ObjInfo, ObjDiff)>,
|
obj: Option<&'a (Object, ObjectDiff)>,
|
||||||
section: Option<(&'a ObjSection, &'a ObjSectionDiff)>,
|
section: Option<(&'a Section, &'a SectionDiff, usize)>,
|
||||||
symbol: Option<(&'a ObjSymbol, &'a ObjSymbolDiff)>,
|
symbol: Option<(&'a Symbol, &'a SymbolDiff, usize)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> DiffColumnContext<'a> {
|
impl<'a> DiffColumnContext<'a> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
view: View,
|
view: View,
|
||||||
status: &'a BuildStatus,
|
status: &'a BuildStatus,
|
||||||
obj: Option<&'a (ObjInfo, ObjDiff)>,
|
obj: Option<&'a (Object, ObjectDiff)>,
|
||||||
selected_symbol: Option<&SymbolRefByName>,
|
selected_symbol: Option<&SymbolRefByName>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let selected_symbol = match view {
|
let selected_symbol = match view {
|
||||||
@ -57,15 +57,18 @@ impl<'a> DiffColumnContext<'a> {
|
|||||||
};
|
};
|
||||||
let (section, symbol) = match (obj, selected_symbol) {
|
let (section, symbol) = match (obj, selected_symbol) {
|
||||||
(Some((obj, obj_diff)), Some(SelectedSymbol::Symbol(symbol_ref))) => {
|
(Some((obj, obj_diff)), Some(SelectedSymbol::Symbol(symbol_ref))) => {
|
||||||
let (section, symbol) = obj.section_symbol(symbol_ref);
|
let symbol = &obj.symbols[symbol_ref];
|
||||||
(
|
(
|
||||||
section.map(|s| (s, obj_diff.section_diff(symbol_ref.section_idx))),
|
symbol.section.map(|section_idx| {
|
||||||
Some((symbol, obj_diff.symbol_diff(symbol_ref))),
|
(&obj.sections[section_idx], &obj_diff.sections[section_idx], section_idx)
|
||||||
|
}),
|
||||||
|
Some((symbol, &obj_diff.symbols[symbol_ref], symbol_ref)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
(Some((obj, obj_diff)), Some(SelectedSymbol::Section(section_idx))) => {
|
(Some((obj, obj_diff)), Some(SelectedSymbol::Section(section_idx))) => (
|
||||||
(Some((&obj.sections[section_idx], obj_diff.section_diff(section_idx))), None)
|
Some((&obj.sections[section_idx], &obj_diff.sections[section_idx], section_idx)),
|
||||||
}
|
None,
|
||||||
|
),
|
||||||
_ => (None, None),
|
_ => (None, None),
|
||||||
};
|
};
|
||||||
Self { status, obj, section, symbol }
|
Self { status, obj, section, symbol }
|
||||||
@ -77,8 +80,8 @@ impl<'a> DiffColumnContext<'a> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn id(&self) -> Option<&str> {
|
pub fn id(&self) -> Option<&str> {
|
||||||
self.symbol
|
self.symbol
|
||||||
.map(|(symbol, _)| symbol.name.as_str())
|
.map(|(symbol, _, _)| symbol.name.as_str())
|
||||||
.or_else(|| self.section.map(|(section, _)| section.name.as_str()))
|
.or_else(|| self.section.map(|(section, _, _)| section.name.as_str()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,6 +90,7 @@ pub fn diff_view_ui(
|
|||||||
ui: &mut Ui,
|
ui: &mut Ui,
|
||||||
state: &DiffViewState,
|
state: &DiffViewState,
|
||||||
appearance: &Appearance,
|
appearance: &Appearance,
|
||||||
|
diff_config: &DiffObjConfig,
|
||||||
) -> Option<DiffViewAction> {
|
) -> Option<DiffViewAction> {
|
||||||
let mut ret = None;
|
let mut ret = None;
|
||||||
let Some(result) = &state.build else {
|
let Some(result) = &state.build else {
|
||||||
@ -113,12 +117,15 @@ pub fn diff_view_ui(
|
|||||||
right_symbol: state.symbol_state.right_symbol.clone(),
|
right_symbol: state.symbol_state.right_symbol.clone(),
|
||||||
};
|
};
|
||||||
let mut navigation = current_navigation.clone();
|
let mut navigation = current_navigation.clone();
|
||||||
if let Some((_symbol, symbol_diff)) = left_ctx.symbol {
|
if let Some((_symbol, symbol_diff, _symbol_idx)) = left_ctx.symbol {
|
||||||
// If a matching symbol appears, select it
|
// If a matching symbol appears, select it
|
||||||
if !right_ctx.has_symbol() {
|
if !right_ctx.has_symbol() {
|
||||||
if let Some(target_symbol_ref) = symbol_diff.target_symbol {
|
if let Some(target_symbol_ref) = symbol_diff.target_symbol {
|
||||||
let (target_section, target_symbol) =
|
let (right_obj, _) = right_ctx.obj.unwrap();
|
||||||
right_ctx.obj.unwrap().0.section_symbol(target_symbol_ref);
|
let target_symbol = &right_obj.symbols[target_symbol_ref];
|
||||||
|
let target_section = target_symbol
|
||||||
|
.section
|
||||||
|
.and_then(|section_idx| right_obj.sections.get(section_idx));
|
||||||
navigation.right_symbol = Some(SymbolRefByName::new(target_symbol, target_section));
|
navigation.right_symbol = Some(SymbolRefByName::new(target_symbol, target_section));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -129,12 +136,15 @@ pub fn diff_view_ui(
|
|||||||
// Clear selection if symbol goes missing
|
// Clear selection if symbol goes missing
|
||||||
navigation.left_symbol = None;
|
navigation.left_symbol = None;
|
||||||
}
|
}
|
||||||
if let Some((_symbol, symbol_diff)) = right_ctx.symbol {
|
if let Some((_symbol, symbol_diff, _symbol_idx)) = right_ctx.symbol {
|
||||||
// If a matching symbol appears, select it
|
// If a matching symbol appears, select it
|
||||||
if !left_ctx.has_symbol() {
|
if !left_ctx.has_symbol() {
|
||||||
if let Some(target_symbol_ref) = symbol_diff.target_symbol {
|
if let Some(target_symbol_ref) = symbol_diff.target_symbol {
|
||||||
let (target_section, target_symbol) =
|
let (left_obj, _) = left_ctx.obj.unwrap();
|
||||||
left_ctx.obj.unwrap().0.section_symbol(target_symbol_ref);
|
let target_symbol = &left_obj.symbols[target_symbol_ref];
|
||||||
|
let target_section = target_symbol
|
||||||
|
.section
|
||||||
|
.and_then(|section_idx| left_obj.sections.get(section_idx));
|
||||||
navigation.left_symbol = Some(SymbolRefByName::new(target_symbol, target_section));
|
navigation.left_symbol = Some(SymbolRefByName::new(target_symbol, target_section));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -170,7 +180,7 @@ pub fn diff_view_ui(
|
|||||||
ret = Some(DiffViewAction::Navigate(DiffViewNavigation::symbol_diff()));
|
ret = Some(DiffViewAction::Navigate(DiffViewNavigation::symbol_diff()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some((symbol, _)) = left_ctx.symbol {
|
if let Some((symbol, _, _)) = left_ctx.symbol {
|
||||||
ui.separator();
|
ui.separator();
|
||||||
if ui
|
if ui
|
||||||
.add_enabled(
|
.add_enabled(
|
||||||
@ -210,13 +220,13 @@ pub fn diff_view_ui(
|
|||||||
.color(appearance.replace_color),
|
.color(appearance.replace_color),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if let Some((symbol, _)) = left_ctx.symbol {
|
} else if let Some((symbol, _, _)) = left_ctx.symbol {
|
||||||
ui.label(
|
ui.label(
|
||||||
RichText::new(symbol.demangled_name.as_deref().unwrap_or(&symbol.name))
|
RichText::new(symbol.demangled_name.as_deref().unwrap_or(&symbol.name))
|
||||||
.font(appearance.code_font.clone())
|
.font(appearance.code_font.clone())
|
||||||
.color(appearance.highlight_color),
|
.color(appearance.highlight_color),
|
||||||
);
|
);
|
||||||
} else if let Some((section, _)) = left_ctx.section {
|
} else if let Some((section, _, _)) = left_ctx.section {
|
||||||
ui.label(
|
ui.label(
|
||||||
RichText::new(section.name.clone())
|
RichText::new(section.name.clone())
|
||||||
.font(appearance.code_font.clone())
|
.font(appearance.code_font.clone())
|
||||||
@ -331,13 +341,13 @@ pub fn diff_view_ui(
|
|||||||
.color(appearance.replace_color),
|
.color(appearance.replace_color),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if let Some((symbol, _)) = right_ctx.symbol {
|
} else if let Some((symbol, _, _)) = right_ctx.symbol {
|
||||||
ui.label(
|
ui.label(
|
||||||
RichText::new(symbol.demangled_name.as_deref().unwrap_or(&symbol.name))
|
RichText::new(symbol.demangled_name.as_deref().unwrap_or(&symbol.name))
|
||||||
.font(appearance.code_font.clone())
|
.font(appearance.code_font.clone())
|
||||||
.color(appearance.highlight_color),
|
.color(appearance.highlight_color),
|
||||||
);
|
);
|
||||||
} else if let Some((section, _)) = right_ctx.section {
|
} else if let Some((section, _, _)) = right_ctx.section {
|
||||||
ui.label(
|
ui.label(
|
||||||
RichText::new(section.name.clone())
|
RichText::new(section.name.clone())
|
||||||
.font(appearance.code_font.clone())
|
.font(appearance.code_font.clone())
|
||||||
@ -359,16 +369,29 @@ pub fn diff_view_ui(
|
|||||||
|
|
||||||
// Third row
|
// Third row
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
if let Some((_, symbol_diff)) = right_ctx.symbol {
|
if let Some((_, symbol_diff, _symbol_idx)) = right_ctx.symbol {
|
||||||
|
let mut needs_separator = false;
|
||||||
if let Some(match_percent) = symbol_diff.match_percent {
|
if let Some(match_percent) = symbol_diff.match_percent {
|
||||||
ui.label(
|
let response = ui.label(
|
||||||
RichText::new(format!("{:.0}%", match_percent.floor()))
|
RichText::new(format!("{:.2}%", match_percent))
|
||||||
.font(appearance.code_font.clone())
|
.font(appearance.code_font.clone())
|
||||||
.color(match_color_for_symbol(match_percent, appearance)),
|
.color(match_color_for_symbol(match_percent, appearance)),
|
||||||
);
|
);
|
||||||
|
if let Some((diff_score, max_score)) = symbol_diff.diff_score {
|
||||||
|
response.on_hover_ui_at_pointer(|ui| {
|
||||||
|
ui.label(
|
||||||
|
RichText::new(format!("Score: {}/{}", diff_score, max_score))
|
||||||
|
.font(appearance.code_font.clone())
|
||||||
|
.color(appearance.text_color),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
needs_separator = true;
|
||||||
}
|
}
|
||||||
if state.current_view == View::FunctionDiff && left_ctx.has_symbol() {
|
if state.current_view == View::FunctionDiff && left_ctx.has_symbol() {
|
||||||
|
if needs_separator {
|
||||||
ui.separator();
|
ui.separator();
|
||||||
|
}
|
||||||
if ui
|
if ui
|
||||||
.button("Change base")
|
.button("Change base")
|
||||||
.on_hover_text_at_pointer(
|
.on_hover_text_at_pointer(
|
||||||
@ -413,17 +436,17 @@ pub fn diff_view_ui(
|
|||||||
View::FunctionDiff,
|
View::FunctionDiff,
|
||||||
Some((left_obj, left_diff)),
|
Some((left_obj, left_diff)),
|
||||||
Some((right_obj, right_diff)),
|
Some((right_obj, right_diff)),
|
||||||
Some((_, left_symbol_diff)),
|
Some((_, left_symbol_diff, left_symbol_idx)),
|
||||||
Some((_, right_symbol_diff)),
|
Some((_, right_symbol_diff, right_symbol_idx)),
|
||||||
) = (state.current_view, left_ctx.obj, right_ctx.obj, left_ctx.symbol, right_ctx.symbol)
|
) = (state.current_view, left_ctx.obj, right_ctx.obj, left_ctx.symbol, right_ctx.symbol)
|
||||||
{
|
{
|
||||||
// Joint diff view
|
// Joint diff view
|
||||||
hotkeys::check_scroll_hotkeys(ui, true);
|
hotkeys::check_scroll_hotkeys(ui, true);
|
||||||
if left_symbol_diff.instructions.len() != right_symbol_diff.instructions.len() {
|
if left_symbol_diff.instruction_rows.len() != right_symbol_diff.instruction_rows.len() {
|
||||||
ui.label("Instruction count mismatch");
|
ui.label("Instruction count mismatch");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let instructions_len = left_symbol_diff.instructions.len();
|
let instructions_len = left_symbol_diff.instruction_rows.len();
|
||||||
render_table(
|
render_table(
|
||||||
ui,
|
ui,
|
||||||
available_width,
|
available_width,
|
||||||
@ -437,10 +460,11 @@ pub fn diff_view_ui(
|
|||||||
FunctionDiffContext {
|
FunctionDiffContext {
|
||||||
obj: left_obj,
|
obj: left_obj,
|
||||||
diff: left_diff,
|
diff: left_diff,
|
||||||
symbol_ref: Some(left_symbol_diff.symbol_ref),
|
symbol_ref: Some(left_symbol_idx),
|
||||||
},
|
},
|
||||||
appearance,
|
appearance,
|
||||||
&state.function_state,
|
&state.function_state,
|
||||||
|
diff_config,
|
||||||
column,
|
column,
|
||||||
) {
|
) {
|
||||||
ret = Some(action);
|
ret = Some(action);
|
||||||
@ -451,10 +475,11 @@ pub fn diff_view_ui(
|
|||||||
FunctionDiffContext {
|
FunctionDiffContext {
|
||||||
obj: right_obj,
|
obj: right_obj,
|
||||||
diff: right_diff,
|
diff: right_diff,
|
||||||
symbol_ref: Some(right_symbol_diff.symbol_ref),
|
symbol_ref: Some(right_symbol_idx),
|
||||||
},
|
},
|
||||||
appearance,
|
appearance,
|
||||||
&state.function_state,
|
&state.function_state,
|
||||||
|
diff_config,
|
||||||
column,
|
column,
|
||||||
) {
|
) {
|
||||||
ret = Some(action);
|
ret = Some(action);
|
||||||
@ -469,8 +494,8 @@ pub fn diff_view_ui(
|
|||||||
View::DataDiff,
|
View::DataDiff,
|
||||||
Some((left_obj, _left_diff)),
|
Some((left_obj, _left_diff)),
|
||||||
Some((right_obj, _right_diff)),
|
Some((right_obj, _right_diff)),
|
||||||
Some((_left_section, left_section_diff)),
|
Some((_left_section, left_section_diff, _left_symbol_idx)),
|
||||||
Some((_right_section, right_section_diff)),
|
Some((_right_section, right_section_diff, _right_symbol_idx)),
|
||||||
) =
|
) =
|
||||||
(state.current_view, left_ctx.obj, right_ctx.obj, left_ctx.section, right_ctx.section)
|
(state.current_view, left_ctx.obj, right_ctx.obj, left_ctx.section, right_ctx.section)
|
||||||
{
|
{
|
||||||
@ -522,6 +547,7 @@ pub fn diff_view_ui(
|
|||||||
right_ctx,
|
right_ctx,
|
||||||
available_width,
|
available_width,
|
||||||
open_sections.0,
|
open_sections.0,
|
||||||
|
diff_config,
|
||||||
) {
|
) {
|
||||||
ret = Some(action);
|
ret = Some(action);
|
||||||
}
|
}
|
||||||
@ -535,6 +561,7 @@ pub fn diff_view_ui(
|
|||||||
left_ctx,
|
left_ctx,
|
||||||
available_width,
|
available_width,
|
||||||
open_sections.1,
|
open_sections.1,
|
||||||
|
diff_config,
|
||||||
) {
|
) {
|
||||||
ret = Some(action);
|
ret = Some(action);
|
||||||
}
|
}
|
||||||
@ -547,7 +574,6 @@ pub fn diff_view_ui(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
fn diff_col_ui(
|
fn diff_col_ui(
|
||||||
ui: &mut Ui,
|
ui: &mut Ui,
|
||||||
state: &DiffViewState,
|
state: &DiffViewState,
|
||||||
@ -557,14 +583,15 @@ fn diff_col_ui(
|
|||||||
other_ctx: DiffColumnContext,
|
other_ctx: DiffColumnContext,
|
||||||
available_width: f32,
|
available_width: f32,
|
||||||
open_sections: Option<bool>,
|
open_sections: Option<bool>,
|
||||||
|
diff_config: &DiffObjConfig,
|
||||||
) -> Option<DiffViewAction> {
|
) -> Option<DiffViewAction> {
|
||||||
let mut ret = None;
|
let mut ret = None;
|
||||||
if !ctx.status.success {
|
if !ctx.status.success {
|
||||||
build_log_ui(ui, ctx.status, appearance);
|
build_log_ui(ui, ctx.status, appearance);
|
||||||
} else if let Some((obj, diff)) = ctx.obj {
|
} else if let Some((obj, diff)) = ctx.obj {
|
||||||
if let Some((_symbol, symbol_diff)) = ctx.symbol {
|
if let Some((_symbol, symbol_diff, symbol_idx)) = ctx.symbol {
|
||||||
hotkeys::check_scroll_hotkeys(ui, false);
|
hotkeys::check_scroll_hotkeys(ui, false);
|
||||||
let ctx = FunctionDiffContext { obj, diff, symbol_ref: Some(symbol_diff.symbol_ref) };
|
let ctx = FunctionDiffContext { obj, diff, symbol_ref: Some(symbol_idx) };
|
||||||
if state.current_view == View::ExtabDiff {
|
if state.current_view == View::ExtabDiff {
|
||||||
extab_ui(ui, ctx, appearance, column);
|
extab_ui(ui, ctx, appearance, column);
|
||||||
} else {
|
} else {
|
||||||
@ -573,11 +600,16 @@ fn diff_col_ui(
|
|||||||
available_width / 2.0,
|
available_width / 2.0,
|
||||||
1,
|
1,
|
||||||
appearance.code_font.size,
|
appearance.code_font.size,
|
||||||
symbol_diff.instructions.len(),
|
symbol_diff.instruction_rows.len(),
|
||||||
|row, column| {
|
|row, column| {
|
||||||
if let Some(action) =
|
if let Some(action) = asm_col_ui(
|
||||||
asm_col_ui(row, ctx, appearance, &state.function_state, column)
|
row,
|
||||||
{
|
ctx,
|
||||||
|
appearance,
|
||||||
|
&state.function_state,
|
||||||
|
diff_config,
|
||||||
|
column,
|
||||||
|
) {
|
||||||
ret = Some(action);
|
ret = Some(action);
|
||||||
}
|
}
|
||||||
if row.response().clicked() {
|
if row.response().clicked() {
|
||||||
@ -586,7 +618,7 @@ fn diff_col_ui(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if let Some((_section, section_diff)) = ctx.section {
|
} else if let Some((_section, section_diff, _section_idx)) = ctx.section {
|
||||||
hotkeys::check_scroll_hotkeys(ui, false);
|
hotkeys::check_scroll_hotkeys(ui, false);
|
||||||
let total_bytes =
|
let total_bytes =
|
||||||
section_diff.data_diff.iter().fold(0usize, |accum, item| accum + item.len);
|
section_diff.data_diff.iter().fold(0usize, |accum, item| accum + item.len);
|
||||||
@ -610,8 +642,8 @@ fn diff_col_ui(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
} else if let (
|
} else if let (
|
||||||
Some((other_section, _other_section_diff)),
|
Some((other_section, _other_section_diff, _other_section_idx)),
|
||||||
Some((other_symbol, other_symbol_diff)),
|
Some((other_symbol, _other_symbol_diff, other_symbol_idx)),
|
||||||
) = (other_ctx.section, other_ctx.symbol)
|
) = (other_ctx.section, other_ctx.symbol)
|
||||||
{
|
{
|
||||||
if let Some(action) = symbol_list_ui(
|
if let Some(action) = symbol_list_ui(
|
||||||
@ -619,7 +651,7 @@ fn diff_col_ui(
|
|||||||
SymbolDiffContext { obj, diff },
|
SymbolDiffContext { obj, diff },
|
||||||
None,
|
None,
|
||||||
&state.symbol_state,
|
&state.symbol_state,
|
||||||
SymbolFilter::Mapping(other_symbol_diff.symbol_ref, None),
|
SymbolFilter::Mapping(other_symbol_idx, None),
|
||||||
appearance,
|
appearance,
|
||||||
column,
|
column,
|
||||||
open_sections,
|
open_sections,
|
||||||
@ -634,7 +666,7 @@ fn diff_col_ui(
|
|||||||
) => {
|
) => {
|
||||||
ret = Some(DiffViewAction::SetMapping(
|
ret = Some(DiffViewAction::SetMapping(
|
||||||
match other_section.kind {
|
match other_section.kind {
|
||||||
ObjSectionKind::Code => View::FunctionDiff,
|
SectionKind::Code => View::FunctionDiff,
|
||||||
_ => View::SymbolDiff,
|
_ => View::SymbolDiff,
|
||||||
},
|
},
|
||||||
left_symbol_ref,
|
left_symbol_ref,
|
||||||
@ -650,7 +682,7 @@ fn diff_col_ui(
|
|||||||
) => {
|
) => {
|
||||||
ret = Some(DiffViewAction::SetMapping(
|
ret = Some(DiffViewAction::SetMapping(
|
||||||
match other_section.kind {
|
match other_section.kind {
|
||||||
ObjSectionKind::Code => View::FunctionDiff,
|
SectionKind::Code => View::FunctionDiff,
|
||||||
_ => View::SymbolDiff,
|
_ => View::SymbolDiff,
|
||||||
},
|
},
|
||||||
SymbolRefByName::new(other_symbol, Some(other_section)),
|
SymbolRefByName::new(other_symbol, Some(other_section)),
|
||||||
@ -725,17 +757,10 @@ fn missing_obj_ui(ui: &mut Ui, appearance: &Appearance) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_symbol(obj: &ObjInfo, selected_symbol: &SymbolRefByName) -> Option<SymbolRef> {
|
fn find_symbol(obj: &Object, selected_symbol: &SymbolRefByName) -> Option<usize> {
|
||||||
for (section_idx, section) in obj.sections.iter().enumerate() {
|
obj.symbols.iter().position(|symbol| symbol.name == selected_symbol.symbol_name)
|
||||||
for (symbol_idx, symbol) in section.symbols.iter().enumerate() {
|
|
||||||
if symbol.name == selected_symbol.symbol_name {
|
|
||||||
return Some(SymbolRef { section_idx, symbol_idx });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_section(obj: &ObjInfo, section_name: &str) -> Option<usize> {
|
fn find_section(obj: &Object, section_name: &str) -> Option<usize> {
|
||||||
obj.sections.iter().position(|section| section.name == section_name)
|
obj.sections.iter().position(|section| section.name == section_name)
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use egui::ScrollArea;
|
use egui::ScrollArea;
|
||||||
use objdiff_core::{
|
use objdiff_core::{
|
||||||
arch::ppc::ExceptionInfo,
|
arch::ppc::ExceptionInfo,
|
||||||
obj::{ObjInfo, ObjSymbol},
|
obj::{Object, Symbol},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::views::{appearance::Appearance, function_diff::FunctionDiffContext};
|
use crate::views::{appearance::Appearance, function_diff::FunctionDiffContext};
|
||||||
@ -26,14 +26,16 @@ fn decode_extab(extab: &ExceptionInfo) -> String {
|
|||||||
text
|
text
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_extab_entry<'a>(obj: &'a ObjInfo, symbol: &ObjSymbol) -> Option<&'a ExceptionInfo> {
|
fn find_extab_entry<'a>(_obj: &'a Object, _symbol: &Symbol) -> Option<&'a ExceptionInfo> {
|
||||||
obj.arch.ppc().and_then(|ppc| ppc.extab_for_symbol(symbol))
|
// TODO
|
||||||
|
// obj.arch.ppc().and_then(|ppc| ppc.extab_for_symbol(symbol))
|
||||||
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extab_text_ui(
|
fn extab_text_ui(
|
||||||
ui: &mut egui::Ui,
|
ui: &mut egui::Ui,
|
||||||
ctx: FunctionDiffContext<'_>,
|
ctx: FunctionDiffContext<'_>,
|
||||||
symbol: &ObjSymbol,
|
symbol: &Symbol,
|
||||||
appearance: &Appearance,
|
appearance: &Appearance,
|
||||||
) -> Option<()> {
|
) -> Option<()> {
|
||||||
if let Some(extab_entry) = find_extab_entry(ctx.obj, symbol) {
|
if let Some(extab_entry) = find_extab_entry(ctx.obj, symbol) {
|
||||||
@ -56,8 +58,8 @@ pub(crate) fn extab_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_mode = Some(egui::TextWrapMode::Extend);
|
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
|
||||||
|
|
||||||
if let Some((_section, symbol)) =
|
if let Some(symbol) =
|
||||||
ctx.symbol_ref.map(|symbol_ref| ctx.obj.section_symbol(symbol_ref))
|
ctx.symbol_ref.and_then(|symbol_ref| ctx.obj.symbols.get(symbol_ref))
|
||||||
{
|
{
|
||||||
extab_text_ui(ui, ctx, symbol, appearance);
|
extab_text_ui(ui, ctx, symbol, appearance);
|
||||||
}
|
}
|
||||||
|
@ -61,14 +61,9 @@ impl FrameHistory {
|
|||||||
);
|
);
|
||||||
egui::warn_if_debug_build(ui);
|
egui::warn_if_debug_build(ui);
|
||||||
|
|
||||||
if !cfg!(target_arch = "wasm32") {
|
egui::CollapsingHeader::new("📊 CPU usage history").default_open(false).show(ui, |ui| {
|
||||||
egui::CollapsingHeader::new("📊 CPU usage history").default_open(false).show(
|
|
||||||
ui,
|
|
||||||
|ui| {
|
|
||||||
self.graph(ui);
|
self.graph(ui);
|
||||||
},
|
});
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn graph(&mut self, ui: &mut egui::Ui) -> egui::Response {
|
fn graph(&mut self, ui: &mut egui::Ui) -> egui::Response {
|
||||||
|
@ -4,10 +4,14 @@ use egui::{text::LayoutJob, Label, Response, Sense, Widget};
|
|||||||
use egui_extras::TableRow;
|
use egui_extras::TableRow;
|
||||||
use objdiff_core::{
|
use objdiff_core::{
|
||||||
diff::{
|
diff::{
|
||||||
display::{display_diff, DiffText, HighlightKind},
|
display::{display_row, DiffText, HighlightKind},
|
||||||
ObjDiff, ObjInsDiff, ObjInsDiffKind,
|
DiffObjConfig, InstructionArgDiffIndex, InstructionDiffKind, InstructionDiffRow,
|
||||||
|
ObjectDiff,
|
||||||
|
},
|
||||||
|
obj::{
|
||||||
|
InstructionArg, InstructionArgValue, InstructionRef, Object, ParsedInstruction,
|
||||||
|
ResolvedRelocation, Section, Symbol,
|
||||||
},
|
},
|
||||||
obj::{ObjInfo, ObjIns, ObjInsArg, ObjInsArgValue, ObjSection, ObjSymbol, SymbolRef},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::views::{appearance::Appearance, symbol_diff::DiffViewAction};
|
use crate::views::{appearance::Appearance, symbol_diff::DiffViewAction};
|
||||||
@ -63,43 +67,94 @@ impl FunctionViewState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[expect(unused)]
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct ResolvedInstructionRef<'obj> {
|
||||||
|
pub symbol: &'obj Symbol,
|
||||||
|
pub section_idx: usize,
|
||||||
|
pub section: &'obj Section,
|
||||||
|
pub data: &'obj [u8],
|
||||||
|
pub relocation: Option<ResolvedRelocation<'obj>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_instruction_ref(
|
||||||
|
obj: &Object,
|
||||||
|
symbol_idx: usize,
|
||||||
|
ins_ref: InstructionRef,
|
||||||
|
) -> Option<ResolvedInstructionRef> {
|
||||||
|
let symbol = &obj.symbols[symbol_idx];
|
||||||
|
let section_idx = symbol.section?;
|
||||||
|
let section = &obj.sections[section_idx];
|
||||||
|
let offset = ins_ref.address.checked_sub(section.address)?;
|
||||||
|
let data = section.data.get(offset as usize..offset as usize + ins_ref.size as usize)?;
|
||||||
|
let relocation = section.relocation_at(ins_ref.address, obj);
|
||||||
|
Some(ResolvedInstructionRef { symbol, section, section_idx, data, relocation })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_instruction<'obj>(
|
||||||
|
obj: &'obj Object,
|
||||||
|
symbol_idx: usize,
|
||||||
|
ins_ref: InstructionRef,
|
||||||
|
diff_config: &DiffObjConfig,
|
||||||
|
) -> Option<(ResolvedInstructionRef<'obj>, ParsedInstruction)> {
|
||||||
|
let resolved = resolve_instruction_ref(obj, symbol_idx, ins_ref)?;
|
||||||
|
let ins = obj
|
||||||
|
.arch
|
||||||
|
.process_instruction(
|
||||||
|
ins_ref,
|
||||||
|
resolved.data,
|
||||||
|
resolved.relocation,
|
||||||
|
resolved.symbol.address..resolved.symbol.address + resolved.symbol.size,
|
||||||
|
resolved.section_idx,
|
||||||
|
diff_config,
|
||||||
|
)
|
||||||
|
.ok()?;
|
||||||
|
Some((resolved, ins))
|
||||||
|
}
|
||||||
|
|
||||||
fn ins_hover_ui(
|
fn ins_hover_ui(
|
||||||
ui: &mut egui::Ui,
|
ui: &mut egui::Ui,
|
||||||
obj: &ObjInfo,
|
obj: &Object,
|
||||||
section: &ObjSection,
|
symbol_idx: usize,
|
||||||
ins: &ObjIns,
|
ins_ref: InstructionRef,
|
||||||
symbol: &ObjSymbol,
|
diff_config: &DiffObjConfig,
|
||||||
appearance: &Appearance,
|
appearance: &Appearance,
|
||||||
) {
|
) {
|
||||||
|
let Some((
|
||||||
|
ResolvedInstructionRef { symbol, section_idx: _, section: _, data, relocation },
|
||||||
|
ins,
|
||||||
|
)) = resolve_instruction(obj, symbol_idx, ins_ref, diff_config)
|
||||||
|
else {
|
||||||
|
ui.colored_label(appearance.delete_color, "Failed to resolve instruction");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
ui.scope(|ui| {
|
ui.scope(|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_mode = Some(egui::TextWrapMode::Extend);
|
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
|
||||||
|
|
||||||
let offset = ins.address - section.address;
|
ui.label(format!("{:02x?}", data));
|
||||||
ui.label(format!(
|
|
||||||
"{:02x?}",
|
|
||||||
§ion.data[offset as usize..(offset + ins.size as u64) as usize]
|
|
||||||
));
|
|
||||||
|
|
||||||
if let Some(virtual_address) = symbol.virtual_address {
|
if let Some(virtual_address) = symbol.virtual_address {
|
||||||
let offset = ins.address - symbol.address;
|
let offset = ins_ref.address - symbol.address;
|
||||||
ui.colored_label(
|
ui.colored_label(
|
||||||
appearance.replace_color,
|
appearance.replace_color,
|
||||||
format!("Virtual address: {:#x}", virtual_address + offset),
|
format!("Virtual address: {:#x}", virtual_address + offset),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(orig) = &ins.orig {
|
// TODO
|
||||||
ui.label(format!("Original: {}", orig));
|
// if let Some(orig) = &ins.orig {
|
||||||
}
|
// ui.label(format!("Original: {}", orig));
|
||||||
|
// }
|
||||||
|
|
||||||
for arg in &ins.args {
|
for arg in &ins.args {
|
||||||
if let ObjInsArg::Arg(arg) = arg {
|
if let InstructionArg::Value(arg) = arg {
|
||||||
match arg {
|
match arg {
|
||||||
ObjInsArgValue::Signed(v) => {
|
InstructionArgValue::Signed(v) => {
|
||||||
ui.label(format!("{arg} == {v}"));
|
ui.label(format!("{arg} == {v}"));
|
||||||
}
|
}
|
||||||
ObjInsArgValue::Unsigned(v) => {
|
InstructionArgValue::Unsigned(v) => {
|
||||||
ui.label(format!("{arg} == {v}"));
|
ui.label(format!("{arg} == {v}"));
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
@ -107,66 +162,76 @@ fn ins_hover_ui(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(reloc) = &ins.reloc {
|
if let Some(resolved) = relocation {
|
||||||
ui.label(format!("Relocation type: {}", obj.arch.display_reloc(reloc.flags)));
|
ui.label(format!(
|
||||||
let addend_str = match reloc.addend.cmp(&0i64) {
|
"Relocation type: {}",
|
||||||
Ordering::Greater => format!("+{:x}", reloc.addend),
|
obj.arch.display_reloc(resolved.relocation.flags)
|
||||||
Ordering::Less => format!("-{:x}", -reloc.addend),
|
));
|
||||||
|
let addend_str = match resolved.relocation.addend.cmp(&0i64) {
|
||||||
|
Ordering::Greater => format!("+{:x}", resolved.relocation.addend),
|
||||||
|
Ordering::Less => format!("-{:x}", -resolved.relocation.addend),
|
||||||
_ => "".to_string(),
|
_ => "".to_string(),
|
||||||
};
|
};
|
||||||
ui.colored_label(
|
ui.colored_label(
|
||||||
appearance.highlight_color,
|
appearance.highlight_color,
|
||||||
format!("Name: {}{}", reloc.target.name, addend_str),
|
format!("Name: {}{}", resolved.symbol.name, addend_str),
|
||||||
);
|
);
|
||||||
if let Some(orig_section_index) = reloc.target.orig_section_index {
|
if let Some(orig_section_index) = resolved.symbol.section {
|
||||||
if let Some(section) =
|
let section = &obj.sections[orig_section_index];
|
||||||
obj.sections.iter().find(|s| s.orig_index == orig_section_index)
|
ui.colored_label(appearance.highlight_color, format!("Section: {}", section.name));
|
||||||
{
|
|
||||||
ui.colored_label(
|
ui.colored_label(
|
||||||
appearance.highlight_color,
|
appearance.highlight_color,
|
||||||
format!("Section: {}", section.name),
|
format!("Address: {:x}{}", resolved.symbol.address, addend_str),
|
||||||
);
|
|
||||||
}
|
|
||||||
ui.colored_label(
|
|
||||||
appearance.highlight_color,
|
|
||||||
format!("Address: {:x}{}", reloc.target.address, addend_str),
|
|
||||||
);
|
);
|
||||||
ui.colored_label(
|
ui.colored_label(
|
||||||
appearance.highlight_color,
|
appearance.highlight_color,
|
||||||
format!("Size: {:x}", reloc.target.size),
|
format!("Size: {:x}", resolved.symbol.size),
|
||||||
);
|
);
|
||||||
for label in obj.arch.display_ins_data_labels(ins) {
|
// TODO
|
||||||
ui.colored_label(appearance.highlight_color, label);
|
// for label in obj.arch.display_ins_data_labels(ins) {
|
||||||
}
|
// ui.colored_label(appearance.highlight_color, label);
|
||||||
|
// }
|
||||||
} else {
|
} else {
|
||||||
ui.colored_label(appearance.highlight_color, "Extern".to_string());
|
ui.colored_label(appearance.highlight_color, "Extern".to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(decoded) = rlwinmdec::decode(&ins.formatted) {
|
// TODO
|
||||||
ui.colored_label(appearance.highlight_color, decoded.trim());
|
// if let Some(decoded) = rlwinmdec::decode(&ins.formatted) {
|
||||||
}
|
// ui.colored_label(appearance.highlight_color, decoded.trim());
|
||||||
|
// }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ins_context_menu(
|
fn ins_context_menu(
|
||||||
ui: &mut egui::Ui,
|
ui: &mut egui::Ui,
|
||||||
obj: &ObjInfo,
|
obj: &Object,
|
||||||
section: &ObjSection,
|
symbol_idx: usize,
|
||||||
ins: &ObjIns,
|
ins_ref: InstructionRef,
|
||||||
symbol: &ObjSymbol,
|
diff_config: &DiffObjConfig,
|
||||||
|
appearance: &Appearance,
|
||||||
) {
|
) {
|
||||||
|
let Some((
|
||||||
|
ResolvedInstructionRef { symbol, section_idx: _, section: _, data, relocation },
|
||||||
|
ins,
|
||||||
|
)) = resolve_instruction(obj, symbol_idx, ins_ref, diff_config)
|
||||||
|
else {
|
||||||
|
ui.colored_label(appearance.delete_color, "Failed to resolve instruction");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
ui.scope(|ui| {
|
ui.scope(|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_mode = Some(egui::TextWrapMode::Extend);
|
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
|
||||||
|
|
||||||
if ui.button(format!("Copy \"{}\"", ins.formatted)).clicked() {
|
// TODO
|
||||||
ui.output_mut(|output| output.copied_text.clone_from(&ins.formatted));
|
// if ui.button(format!("Copy \"{}\"", ins.formatted)).clicked() {
|
||||||
ui.close_menu();
|
// ui.output_mut(|output| output.copied_text.clone_from(&ins.formatted));
|
||||||
}
|
// ui.close_menu();
|
||||||
|
// }
|
||||||
|
|
||||||
let mut hex_string = "0x".to_string();
|
let mut hex_string = "0x".to_string();
|
||||||
for byte in §ion.data[ins.address as usize..(ins.address + ins.size as u64) as usize] {
|
for byte in data {
|
||||||
hex_string.push_str(&format!("{:02x}", byte));
|
hex_string.push_str(&format!("{:02x}", byte));
|
||||||
}
|
}
|
||||||
if ui.button(format!("Copy \"{hex_string}\" (instruction bytes)")).clicked() {
|
if ui.button(format!("Copy \"{hex_string}\" (instruction bytes)")).clicked() {
|
||||||
@ -175,7 +240,7 @@ fn ins_context_menu(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(virtual_address) = symbol.virtual_address {
|
if let Some(virtual_address) = symbol.virtual_address {
|
||||||
let offset = ins.address - symbol.address;
|
let offset = ins_ref.address - symbol.address;
|
||||||
let offset_string = format!("{:#x}", virtual_address + offset);
|
let offset_string = format!("{:#x}", virtual_address + offset);
|
||||||
if ui.button(format!("Copy \"{offset_string}\" (virtual address)")).clicked() {
|
if ui.button(format!("Copy \"{offset_string}\" (virtual address)")).clicked() {
|
||||||
ui.output_mut(|output| output.copied_text = offset_string);
|
ui.output_mut(|output| output.copied_text = offset_string);
|
||||||
@ -184,9 +249,9 @@ fn ins_context_menu(
|
|||||||
}
|
}
|
||||||
|
|
||||||
for arg in &ins.args {
|
for arg in &ins.args {
|
||||||
if let ObjInsArg::Arg(arg) = arg {
|
if let InstructionArg::Value(arg) = arg {
|
||||||
match arg {
|
match arg {
|
||||||
ObjInsArgValue::Signed(v) => {
|
InstructionArgValue::Signed(v) => {
|
||||||
if ui.button(format!("Copy \"{arg}\"")).clicked() {
|
if ui.button(format!("Copy \"{arg}\"")).clicked() {
|
||||||
ui.output_mut(|output| output.copied_text = arg.to_string());
|
ui.output_mut(|output| output.copied_text = arg.to_string());
|
||||||
ui.close_menu();
|
ui.close_menu();
|
||||||
@ -196,7 +261,7 @@ fn ins_context_menu(
|
|||||||
ui.close_menu();
|
ui.close_menu();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ObjInsArgValue::Unsigned(v) => {
|
InstructionArgValue::Unsigned(v) => {
|
||||||
if ui.button(format!("Copy \"{arg}\"")).clicked() {
|
if ui.button(format!("Copy \"{arg}\"")).clicked() {
|
||||||
ui.output_mut(|output| output.copied_text = arg.to_string());
|
ui.output_mut(|output| output.copied_text = arg.to_string());
|
||||||
ui.close_menu();
|
ui.close_menu();
|
||||||
@ -210,21 +275,23 @@ fn ins_context_menu(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(reloc) = &ins.reloc {
|
|
||||||
for literal in obj.arch.display_ins_data_literals(ins) {
|
if let Some(resolved) = relocation {
|
||||||
if ui.button(format!("Copy \"{literal}\"")).clicked() {
|
// TODO
|
||||||
ui.output_mut(|output| output.copied_text.clone_from(&literal));
|
// for literal in obj.arch.display_ins_data_literals(ins) {
|
||||||
ui.close_menu();
|
// if ui.button(format!("Copy \"{literal}\"")).clicked() {
|
||||||
}
|
// ui.output_mut(|output| output.copied_text.clone_from(&literal));
|
||||||
}
|
// ui.close_menu();
|
||||||
if let Some(name) = &reloc.target.demangled_name {
|
// }
|
||||||
|
// }
|
||||||
|
if let Some(name) = &resolved.symbol.demangled_name {
|
||||||
if ui.button(format!("Copy \"{name}\"")).clicked() {
|
if ui.button(format!("Copy \"{name}\"")).clicked() {
|
||||||
ui.output_mut(|output| output.copied_text.clone_from(name));
|
ui.output_mut(|output| output.copied_text.clone_from(name));
|
||||||
ui.close_menu();
|
ui.close_menu();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ui.button(format!("Copy \"{}\"", reloc.target.name)).clicked() {
|
if ui.button(format!("Copy \"{}\"", resolved.symbol.name)).clicked() {
|
||||||
ui.output_mut(|output| output.copied_text.clone_from(&reloc.target.name));
|
ui.output_mut(|output| output.copied_text.clone_from(&resolved.symbol.name));
|
||||||
ui.close_menu();
|
ui.close_menu();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -232,11 +299,11 @@ fn ins_context_menu(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
#[expect(clippy::too_many_arguments)]
|
|
||||||
fn diff_text_ui(
|
fn diff_text_ui(
|
||||||
ui: &mut egui::Ui,
|
ui: &mut egui::Ui,
|
||||||
text: DiffText<'_>,
|
text: DiffText<'_>,
|
||||||
ins_diff: &ObjInsDiff,
|
diff: InstructionArgDiffIndex,
|
||||||
|
ins_diff: &InstructionDiffRow,
|
||||||
appearance: &Appearance,
|
appearance: &Appearance,
|
||||||
ins_view_state: &FunctionViewState,
|
ins_view_state: &FunctionViewState,
|
||||||
column: usize,
|
column: usize,
|
||||||
@ -246,22 +313,18 @@ fn diff_text_ui(
|
|||||||
let mut ret = None;
|
let mut ret = None;
|
||||||
let label_text;
|
let label_text;
|
||||||
let mut base_color = match ins_diff.kind {
|
let mut base_color = match ins_diff.kind {
|
||||||
ObjInsDiffKind::None | ObjInsDiffKind::OpMismatch | ObjInsDiffKind::ArgMismatch => {
|
InstructionDiffKind::None
|
||||||
appearance.text_color
|
| InstructionDiffKind::OpMismatch
|
||||||
}
|
| InstructionDiffKind::ArgMismatch => appearance.text_color,
|
||||||
ObjInsDiffKind::Replace => appearance.replace_color,
|
InstructionDiffKind::Replace => appearance.replace_color,
|
||||||
ObjInsDiffKind::Delete => appearance.delete_color,
|
InstructionDiffKind::Delete => appearance.delete_color,
|
||||||
ObjInsDiffKind::Insert => appearance.insert_color,
|
InstructionDiffKind::Insert => appearance.insert_color,
|
||||||
};
|
};
|
||||||
let mut pad_to = 0;
|
let mut pad_to = 0;
|
||||||
match text {
|
match text {
|
||||||
DiffText::Basic(text) => {
|
DiffText::Basic(text) => {
|
||||||
label_text = text.to_string();
|
label_text = text.to_string();
|
||||||
}
|
}
|
||||||
DiffText::BasicColor(s, idx) => {
|
|
||||||
label_text = s.to_string();
|
|
||||||
base_color = appearance.diff_colors[idx % appearance.diff_colors.len()];
|
|
||||||
}
|
|
||||||
DiffText::Line(num) => {
|
DiffText::Line(num) => {
|
||||||
label_text = num.to_string();
|
label_text = num.to_string();
|
||||||
base_color = appearance.deemphasized_text_color;
|
base_color = appearance.deemphasized_text_color;
|
||||||
@ -273,44 +336,30 @@ fn diff_text_ui(
|
|||||||
}
|
}
|
||||||
DiffText::Opcode(mnemonic, _op) => {
|
DiffText::Opcode(mnemonic, _op) => {
|
||||||
label_text = mnemonic.to_string();
|
label_text = mnemonic.to_string();
|
||||||
if ins_diff.kind == ObjInsDiffKind::OpMismatch {
|
if ins_diff.kind == InstructionDiffKind::OpMismatch {
|
||||||
base_color = appearance.replace_color;
|
base_color = appearance.replace_color;
|
||||||
}
|
}
|
||||||
pad_to = 8;
|
pad_to = 8;
|
||||||
}
|
}
|
||||||
DiffText::Argument(arg, diff) => {
|
DiffText::Argument(arg) => {
|
||||||
label_text = arg.to_string();
|
label_text = arg.to_string();
|
||||||
if let Some(diff) = diff {
|
|
||||||
base_color = appearance.diff_colors[diff.idx % appearance.diff_colors.len()]
|
|
||||||
}
|
}
|
||||||
}
|
DiffText::BranchDest(addr) => {
|
||||||
DiffText::BranchDest(addr, diff) => {
|
|
||||||
label_text = format!("{addr:x}");
|
label_text = format!("{addr:x}");
|
||||||
if let Some(diff) = diff {
|
|
||||||
base_color = appearance.diff_colors[diff.idx % appearance.diff_colors.len()]
|
|
||||||
}
|
}
|
||||||
}
|
DiffText::Symbol(sym) => {
|
||||||
DiffText::Symbol(sym, diff) => {
|
|
||||||
let name = sym.demangled_name.as_ref().unwrap_or(&sym.name);
|
let name = sym.demangled_name.as_ref().unwrap_or(&sym.name);
|
||||||
label_text = name.clone();
|
label_text = name.clone();
|
||||||
if let Some(diff) = diff {
|
|
||||||
base_color = appearance.diff_colors[diff.idx % appearance.diff_colors.len()]
|
|
||||||
} else {
|
|
||||||
base_color = appearance.emphasized_text_color;
|
base_color = appearance.emphasized_text_color;
|
||||||
}
|
}
|
||||||
}
|
DiffText::Addend(addend) => {
|
||||||
DiffText::Addend(addend, diff) => {
|
|
||||||
label_text = match addend.cmp(&0i64) {
|
label_text = match addend.cmp(&0i64) {
|
||||||
Ordering::Greater => format!("+{:#x}", addend),
|
Ordering::Greater => format!("+{:#x}", addend),
|
||||||
Ordering::Less => format!("-{:#x}", -addend),
|
Ordering::Less => format!("-{:#x}", -addend),
|
||||||
_ => "".to_string(),
|
_ => "".to_string(),
|
||||||
};
|
};
|
||||||
if let Some(diff) = diff {
|
|
||||||
base_color = appearance.diff_colors[diff.idx % appearance.diff_colors.len()]
|
|
||||||
} else {
|
|
||||||
base_color = appearance.emphasized_text_color;
|
base_color = appearance.emphasized_text_color;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
DiffText::Spacing(n) => {
|
DiffText::Spacing(n) => {
|
||||||
ui.add_space(n as f32 * space_width);
|
ui.add_space(n as f32 * space_width);
|
||||||
return ret;
|
return ret;
|
||||||
@ -319,6 +368,9 @@ fn diff_text_ui(
|
|||||||
label_text = "\n".to_string();
|
label_text = "\n".to_string();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if let Some(diff_idx) = diff.get() {
|
||||||
|
base_color = appearance.diff_colors[diff_idx as usize % appearance.diff_colors.len()];
|
||||||
|
}
|
||||||
|
|
||||||
let len = label_text.len();
|
let len = label_text.len();
|
||||||
let highlight = *ins_view_state.highlight(column) == text;
|
let highlight = *ins_view_state.highlight(column) == text;
|
||||||
@ -341,24 +393,27 @@ fn diff_text_ui(
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
fn asm_row_ui(
|
fn asm_row_ui(
|
||||||
ui: &mut egui::Ui,
|
ui: &mut egui::Ui,
|
||||||
ins_diff: &ObjInsDiff,
|
obj: &Object,
|
||||||
symbol: &ObjSymbol,
|
ins_diff: &InstructionDiffRow,
|
||||||
|
symbol_idx: usize,
|
||||||
appearance: &Appearance,
|
appearance: &Appearance,
|
||||||
ins_view_state: &FunctionViewState,
|
ins_view_state: &FunctionViewState,
|
||||||
|
diff_config: &DiffObjConfig,
|
||||||
column: usize,
|
column: usize,
|
||||||
response_cb: impl Fn(Response) -> Response,
|
response_cb: impl Fn(Response) -> Response,
|
||||||
) -> Option<DiffViewAction> {
|
) -> Option<DiffViewAction> {
|
||||||
let mut ret = None;
|
let mut ret = None;
|
||||||
ui.spacing_mut().item_spacing.x = 0.0;
|
ui.spacing_mut().item_spacing.x = 0.0;
|
||||||
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
|
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
|
||||||
if ins_diff.kind != ObjInsDiffKind::None {
|
if ins_diff.kind != InstructionDiffKind::None {
|
||||||
ui.painter().rect_filled(ui.available_rect_before_wrap(), 0.0, ui.visuals().faint_bg_color);
|
ui.painter().rect_filled(ui.available_rect_before_wrap(), 0.0, ui.visuals().faint_bg_color);
|
||||||
}
|
}
|
||||||
let space_width = ui.fonts(|f| f.glyph_width(&appearance.code_font, ' '));
|
let space_width = ui.fonts(|f| f.glyph_width(&appearance.code_font, ' '));
|
||||||
display_diff(ins_diff, symbol.address, |text| {
|
display_row(obj, symbol_idx, ins_diff, diff_config, |text, diff| {
|
||||||
if let Some(action) = diff_text_ui(
|
if let Some(action) = diff_text_ui(
|
||||||
ui,
|
ui,
|
||||||
text,
|
text,
|
||||||
|
diff,
|
||||||
ins_diff,
|
ins_diff,
|
||||||
appearance,
|
appearance,
|
||||||
ins_view_state,
|
ins_view_state,
|
||||||
@ -368,7 +423,7 @@ fn asm_row_ui(
|
|||||||
) {
|
) {
|
||||||
ret = Some(action);
|
ret = Some(action);
|
||||||
}
|
}
|
||||||
Ok::<_, ()>(())
|
Ok(())
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
ret
|
ret
|
||||||
@ -380,27 +435,36 @@ pub(crate) fn asm_col_ui(
|
|||||||
ctx: FunctionDiffContext<'_>,
|
ctx: FunctionDiffContext<'_>,
|
||||||
appearance: &Appearance,
|
appearance: &Appearance,
|
||||||
ins_view_state: &FunctionViewState,
|
ins_view_state: &FunctionViewState,
|
||||||
|
diff_config: &DiffObjConfig,
|
||||||
column: usize,
|
column: usize,
|
||||||
) -> Option<DiffViewAction> {
|
) -> Option<DiffViewAction> {
|
||||||
let mut ret = None;
|
let mut ret = None;
|
||||||
let symbol_ref = ctx.symbol_ref?;
|
let symbol_ref = ctx.symbol_ref?;
|
||||||
let (section, symbol) = ctx.obj.section_symbol(symbol_ref);
|
let ins_row = &ctx.diff.symbols[symbol_ref].instruction_rows[row.index()];
|
||||||
let section = section?;
|
|
||||||
let ins_diff = &ctx.diff.symbol_diff(symbol_ref).instructions[row.index()];
|
|
||||||
let response_cb = |response: Response| {
|
let response_cb = |response: Response| {
|
||||||
if let Some(ins) = &ins_diff.ins {
|
if let Some(ins_ref) = ins_row.ins_ref {
|
||||||
response.context_menu(|ui| ins_context_menu(ui, ctx.obj, section, ins, symbol));
|
response.context_menu(|ui| {
|
||||||
|
ins_context_menu(ui, ctx.obj, symbol_ref, ins_ref, diff_config, appearance)
|
||||||
|
});
|
||||||
response.on_hover_ui_at_pointer(|ui| {
|
response.on_hover_ui_at_pointer(|ui| {
|
||||||
ins_hover_ui(ui, ctx.obj, section, ins, symbol, appearance)
|
ins_hover_ui(ui, ctx.obj, symbol_ref, ins_ref, diff_config, appearance)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
response
|
response
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let (_, response) = row.col(|ui| {
|
let (_, response) = row.col(|ui| {
|
||||||
if let Some(action) =
|
if let Some(action) = asm_row_ui(
|
||||||
asm_row_ui(ui, ins_diff, symbol, appearance, ins_view_state, column, response_cb)
|
ui,
|
||||||
{
|
ctx.obj,
|
||||||
|
ins_row,
|
||||||
|
symbol_ref,
|
||||||
|
appearance,
|
||||||
|
ins_view_state,
|
||||||
|
diff_config,
|
||||||
|
column,
|
||||||
|
response_cb,
|
||||||
|
) {
|
||||||
ret = Some(action);
|
ret = Some(action);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -410,7 +474,7 @@ pub(crate) fn asm_col_ui(
|
|||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct FunctionDiffContext<'a> {
|
pub struct FunctionDiffContext<'a> {
|
||||||
pub obj: &'a ObjInfo,
|
pub obj: &'a Object,
|
||||||
pub diff: &'a ObjDiff,
|
pub diff: &'a ObjectDiff,
|
||||||
pub symbol_ref: Option<SymbolRef>,
|
pub symbol_ref: Option<usize>,
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,19 @@
|
|||||||
use std::{collections::BTreeMap, mem::take, ops::Bound};
|
use std::mem::take;
|
||||||
|
|
||||||
use egui::{
|
use egui::{
|
||||||
style::ScrollAnimation, text::LayoutJob, CollapsingHeader, Color32, Id, OpenUrl, ScrollArea,
|
style::ScrollAnimation, text::LayoutJob, CollapsingHeader, Color32, Id, OpenUrl, ScrollArea,
|
||||||
SelectableLabel, Ui, Widget,
|
SelectableLabel, Ui, Widget,
|
||||||
};
|
};
|
||||||
use objdiff_core::{
|
use objdiff_core::{
|
||||||
arch::ObjArch,
|
diff::{
|
||||||
diff::{display::HighlightKind, ObjDiff, ObjSymbolDiff},
|
display::{
|
||||||
jobs::{create_scratch::CreateScratchResult, objdiff::ObjDiffResult, Job, JobQueue, JobResult},
|
display_sections, symbol_context, symbol_hover, ContextMenuItem, HighlightKind,
|
||||||
obj::{
|
HoverItem, HoverItemColor, SectionDisplay, SymbolFilter,
|
||||||
ObjInfo, ObjSection, ObjSectionKind, ObjSymbol, ObjSymbolFlags, SymbolRef, SECTION_COMMON,
|
|
||||||
},
|
},
|
||||||
|
ObjectDiff, SymbolDiff,
|
||||||
|
},
|
||||||
|
jobs::{create_scratch::CreateScratchResult, objdiff::ObjDiffResult, Job, JobQueue, JobResult},
|
||||||
|
obj::{Object, Section, SectionKind, Symbol, SymbolFlag},
|
||||||
};
|
};
|
||||||
use regex::{Regex, RegexBuilder};
|
use regex::{Regex, RegexBuilder};
|
||||||
|
|
||||||
@ -28,7 +31,7 @@ pub struct SymbolRefByName {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SymbolRefByName {
|
impl SymbolRefByName {
|
||||||
pub fn new(symbol: &ObjSymbol, section: Option<&ObjSection>) -> Self {
|
pub fn new(symbol: &Symbol, section: Option<&Section>) -> Self {
|
||||||
Self { symbol_name: symbol.name.clone(), section_name: section.map(|s| s.name.clone()) }
|
Self { symbol_name: symbol.name.clone(), section_name: section.map(|s| s.name.clone()) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -50,7 +53,7 @@ pub enum DiffViewAction {
|
|||||||
/// Navigate to a new diff view
|
/// Navigate to a new diff view
|
||||||
Navigate(DiffViewNavigation),
|
Navigate(DiffViewNavigation),
|
||||||
/// Set the highlighted symbols in the symbols view, optionally scrolling them into view.
|
/// Set the highlighted symbols in the symbols view, optionally scrolling them into view.
|
||||||
SetSymbolHighlight(Option<SymbolRef>, Option<SymbolRef>, bool),
|
SetSymbolHighlight(Option<usize>, Option<usize>, bool),
|
||||||
/// Set the symbols view search filter
|
/// Set the symbols view search filter
|
||||||
SetSearch(String),
|
SetSearch(String),
|
||||||
/// Submit the current function to decomp.me
|
/// Submit the current function to decomp.me
|
||||||
@ -88,15 +91,17 @@ impl DiffViewNavigation {
|
|||||||
pub fn with_symbols(
|
pub fn with_symbols(
|
||||||
view: View,
|
view: View,
|
||||||
other_ctx: Option<SymbolDiffContext<'_>>,
|
other_ctx: Option<SymbolDiffContext<'_>>,
|
||||||
symbol: &ObjSymbol,
|
symbol: &Symbol,
|
||||||
section: &ObjSection,
|
section: &Section,
|
||||||
symbol_diff: &ObjSymbolDiff,
|
symbol_diff: &SymbolDiff,
|
||||||
column: usize,
|
column: usize,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let symbol1 = Some(SymbolRefByName::new(symbol, Some(section)));
|
let symbol1 = Some(SymbolRefByName::new(symbol, Some(section)));
|
||||||
let symbol2 = symbol_diff.target_symbol.and_then(|symbol_ref| {
|
let symbol2 = symbol_diff.target_symbol.and_then(|symbol_ref| {
|
||||||
other_ctx.map(|ctx| {
|
other_ctx.map(|ctx| {
|
||||||
let (section, symbol) = ctx.obj.section_symbol(symbol_ref);
|
let symbol = &ctx.obj.symbols[symbol_ref];
|
||||||
|
let section =
|
||||||
|
symbol.section.and_then(|section_idx| ctx.obj.sections.get(section_idx));
|
||||||
SymbolRefByName::new(symbol, section)
|
SymbolRefByName::new(symbol, section)
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
@ -107,7 +112,7 @@ impl DiffViewNavigation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn data_diff(section: &ObjSection, column: usize) -> Self {
|
pub fn data_diff(section: &Section, column: usize) -> Self {
|
||||||
let symbol = Some(SymbolRefByName {
|
let symbol = Some(SymbolRefByName {
|
||||||
symbol_name: "".to_string(),
|
symbol_name: "".to_string(),
|
||||||
section_name: Some(section.name.clone()),
|
section_name: Some(section.name.clone()),
|
||||||
@ -143,7 +148,7 @@ pub struct DiffViewState {
|
|||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct SymbolViewState {
|
pub struct SymbolViewState {
|
||||||
pub highlighted_symbol: (Option<SymbolRef>, Option<SymbolRef>),
|
pub highlighted_symbol: (Option<usize>, Option<usize>),
|
||||||
pub autoscroll_to_highlighted_symbols: bool,
|
pub autoscroll_to_highlighted_symbols: bool,
|
||||||
pub left_symbol: Option<SymbolRefByName>,
|
pub left_symbol: Option<SymbolRefByName>,
|
||||||
pub right_symbol: Option<SymbolRefByName>,
|
pub right_symbol: Option<SymbolRefByName>,
|
||||||
@ -365,9 +370,9 @@ fn symbol_context_menu_ui(
|
|||||||
ui: &mut Ui,
|
ui: &mut Ui,
|
||||||
ctx: SymbolDiffContext<'_>,
|
ctx: SymbolDiffContext<'_>,
|
||||||
other_ctx: Option<SymbolDiffContext<'_>>,
|
other_ctx: Option<SymbolDiffContext<'_>>,
|
||||||
symbol: &ObjSymbol,
|
symbol: &Symbol,
|
||||||
symbol_diff: &ObjSymbolDiff,
|
symbol_diff: &SymbolDiff,
|
||||||
section: Option<&ObjSection>,
|
section: Option<&Section>,
|
||||||
column: usize,
|
column: usize,
|
||||||
) -> Option<DiffViewNavigation> {
|
) -> Option<DiffViewNavigation> {
|
||||||
let mut ret = None;
|
let mut ret = None;
|
||||||
@ -375,37 +380,37 @@ fn symbol_context_menu_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_mode = Some(egui::TextWrapMode::Extend);
|
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
|
||||||
|
|
||||||
if let Some(name) = &symbol.demangled_name {
|
for item in symbol_context(ctx.obj, symbol) {
|
||||||
if ui.button(format!("Copy \"{name}\"")).clicked() {
|
match item {
|
||||||
ui.output_mut(|output| output.copied_text.clone_from(name));
|
ContextMenuItem::Copy { value, label } => {
|
||||||
|
let label = if let Some(extra) = label {
|
||||||
|
format!("Copy \"{value}\" ({extra})")
|
||||||
|
} else {
|
||||||
|
format!("Copy \"{value}\"")
|
||||||
|
};
|
||||||
|
if ui.button(label).clicked() {
|
||||||
|
ui.output_mut(|output| output.copied_text = value);
|
||||||
ui.close_menu();
|
ui.close_menu();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ui.button(format!("Copy \"{}\"", symbol.name)).clicked() {
|
ContextMenuItem::Navigate { label } => {
|
||||||
ui.output_mut(|output| output.copied_text.clone_from(&symbol.name));
|
if ui.button(label).clicked() {
|
||||||
ui.close_menu();
|
// TODO other navigation
|
||||||
}
|
|
||||||
if let Some(address) = symbol.virtual_address {
|
|
||||||
if ui.button(format!("Copy \"{:#x}\" (virtual address)", address)).clicked() {
|
|
||||||
ui.output_mut(|output| output.copied_text = format!("{:#x}", address));
|
|
||||||
ui.close_menu();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(section) = section {
|
|
||||||
let has_extab =
|
|
||||||
ctx.obj.arch.ppc().and_then(|ppc| ppc.extab_for_symbol(symbol)).is_some();
|
|
||||||
if has_extab && ui.button("Decode exception table").clicked() {
|
|
||||||
ret = Some(DiffViewNavigation::with_symbols(
|
ret = Some(DiffViewNavigation::with_symbols(
|
||||||
View::ExtabDiff,
|
View::ExtabDiff,
|
||||||
other_ctx,
|
other_ctx,
|
||||||
symbol,
|
symbol,
|
||||||
section,
|
section.unwrap(),
|
||||||
symbol_diff,
|
symbol_diff,
|
||||||
column,
|
column,
|
||||||
));
|
));
|
||||||
ui.close_menu();
|
ui.close_menu();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(section) = section {
|
||||||
if ui.button("Map symbol").clicked() {
|
if ui.button("Map symbol").clicked() {
|
||||||
let symbol_ref = SymbolRefByName::new(symbol, Some(section));
|
let symbol_ref = SymbolRefByName::new(symbol, Some(section));
|
||||||
if column == 0 {
|
if column == 0 {
|
||||||
@ -428,54 +433,41 @@ fn symbol_context_menu_ui(
|
|||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
fn symbol_hover_ui(ui: &mut Ui, arch: &dyn ObjArch, symbol: &ObjSymbol, appearance: &Appearance) {
|
fn symbol_hover_ui(
|
||||||
|
ui: &mut Ui,
|
||||||
|
ctx: SymbolDiffContext<'_>,
|
||||||
|
symbol: &Symbol,
|
||||||
|
appearance: &Appearance,
|
||||||
|
) {
|
||||||
ui.scope(|ui| {
|
ui.scope(|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_mode = Some(egui::TextWrapMode::Extend);
|
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
|
||||||
|
|
||||||
ui.colored_label(appearance.highlight_color, format!("Name: {}", symbol.name));
|
for HoverItem { text, color } in symbol_hover(ctx.obj, symbol) {
|
||||||
ui.colored_label(appearance.highlight_color, format!("Address: {:x}", symbol.address));
|
let color = match color {
|
||||||
if symbol.size_known {
|
HoverItemColor::Normal => appearance.text_color,
|
||||||
ui.colored_label(appearance.highlight_color, format!("Size: {:x}", symbol.size));
|
HoverItemColor::Emphasized => appearance.highlight_color,
|
||||||
} else {
|
HoverItemColor::Special => appearance.replace_color,
|
||||||
ui.colored_label(
|
};
|
||||||
appearance.highlight_color,
|
ui.colored_label(color, text);
|
||||||
format!("Size: {:x} (assumed)", symbol.size),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if let Some(address) = symbol.virtual_address {
|
|
||||||
ui.colored_label(appearance.replace_color, format!("Virtual address: {:#x}", address));
|
|
||||||
}
|
|
||||||
if let Some(extab) = arch.ppc().and_then(|ppc| ppc.extab_for_symbol(symbol)) {
|
|
||||||
ui.colored_label(
|
|
||||||
appearance.highlight_color,
|
|
||||||
format!("extab symbol: {}", &extab.etb_symbol.name),
|
|
||||||
);
|
|
||||||
ui.colored_label(
|
|
||||||
appearance.highlight_color,
|
|
||||||
format!("extabindex symbol: {}", &extab.eti_symbol.name),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
#[expect(clippy::too_many_arguments)]
|
|
||||||
fn symbol_ui(
|
fn symbol_ui(
|
||||||
ui: &mut Ui,
|
ui: &mut Ui,
|
||||||
ctx: SymbolDiffContext<'_>,
|
ctx: SymbolDiffContext<'_>,
|
||||||
other_ctx: Option<SymbolDiffContext<'_>>,
|
other_ctx: Option<SymbolDiffContext<'_>>,
|
||||||
symbol: &ObjSymbol,
|
symbol: &Symbol,
|
||||||
symbol_diff: &ObjSymbolDiff,
|
symbol_diff: &SymbolDiff,
|
||||||
section: Option<&ObjSection>,
|
symbol_idx: usize,
|
||||||
|
section: Option<&Section>,
|
||||||
state: &SymbolViewState,
|
state: &SymbolViewState,
|
||||||
appearance: &Appearance,
|
appearance: &Appearance,
|
||||||
column: usize,
|
column: usize,
|
||||||
) -> Option<DiffViewAction> {
|
) -> Option<DiffViewAction> {
|
||||||
let mut ret = None;
|
let mut ret = None;
|
||||||
if symbol.flags.0.contains(ObjSymbolFlags::Hidden) && !state.show_hidden_symbols {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
let mut job = LayoutJob::default();
|
let mut job = LayoutJob::default();
|
||||||
let name: &str =
|
let name: &str =
|
||||||
if let Some(demangled) = &symbol.demangled_name { demangled } else { &symbol.name };
|
if let Some(demangled) = &symbol.demangled_name { demangled } else { &symbol.name };
|
||||||
@ -483,24 +475,24 @@ fn symbol_ui(
|
|||||||
if let Some(sym_ref) =
|
if let Some(sym_ref) =
|
||||||
if column == 0 { state.highlighted_symbol.0 } else { state.highlighted_symbol.1 }
|
if column == 0 { state.highlighted_symbol.0 } else { state.highlighted_symbol.1 }
|
||||||
{
|
{
|
||||||
selected = symbol_diff.symbol_ref == sym_ref;
|
selected = symbol_idx == sym_ref;
|
||||||
}
|
}
|
||||||
if !symbol.flags.0.is_empty() {
|
if !symbol.flags.is_empty() {
|
||||||
write_text("[", appearance.text_color, &mut job, appearance.code_font.clone());
|
write_text("[", appearance.text_color, &mut job, appearance.code_font.clone());
|
||||||
if symbol.flags.0.contains(ObjSymbolFlags::Common) {
|
if symbol.flags.contains(SymbolFlag::Common) {
|
||||||
write_text("c", appearance.replace_color, &mut job, appearance.code_font.clone());
|
write_text("c", appearance.replace_color, &mut job, appearance.code_font.clone());
|
||||||
} else if symbol.flags.0.contains(ObjSymbolFlags::Global) {
|
} else if symbol.flags.contains(SymbolFlag::Global) {
|
||||||
write_text("g", appearance.insert_color, &mut job, appearance.code_font.clone());
|
write_text("g", appearance.insert_color, &mut job, appearance.code_font.clone());
|
||||||
} else if symbol.flags.0.contains(ObjSymbolFlags::Local) {
|
} else if symbol.flags.contains(SymbolFlag::Local) {
|
||||||
write_text("l", appearance.text_color, &mut job, appearance.code_font.clone());
|
write_text("l", appearance.text_color, &mut job, appearance.code_font.clone());
|
||||||
}
|
}
|
||||||
if symbol.flags.0.contains(ObjSymbolFlags::Weak) {
|
if symbol.flags.contains(SymbolFlag::Weak) {
|
||||||
write_text("w", appearance.text_color, &mut job, appearance.code_font.clone());
|
write_text("w", appearance.text_color, &mut job, appearance.code_font.clone());
|
||||||
}
|
}
|
||||||
if symbol.flags.0.contains(ObjSymbolFlags::HasExtra) {
|
if symbol.flags.contains(SymbolFlag::HasExtra) {
|
||||||
write_text("e", appearance.text_color, &mut job, appearance.code_font.clone());
|
write_text("e", appearance.text_color, &mut job, appearance.code_font.clone());
|
||||||
}
|
}
|
||||||
if symbol.flags.0.contains(ObjSymbolFlags::Hidden) {
|
if symbol.flags.contains(SymbolFlag::Hidden) {
|
||||||
write_text(
|
write_text(
|
||||||
"h",
|
"h",
|
||||||
appearance.deemphasized_text_color,
|
appearance.deemphasized_text_color,
|
||||||
@ -521,9 +513,9 @@ fn symbol_ui(
|
|||||||
write_text(") ", appearance.text_color, &mut job, appearance.code_font.clone());
|
write_text(") ", appearance.text_color, &mut job, appearance.code_font.clone());
|
||||||
}
|
}
|
||||||
write_text(name, appearance.highlight_color, &mut job, appearance.code_font.clone());
|
write_text(name, appearance.highlight_color, &mut job, appearance.code_font.clone());
|
||||||
let response = SelectableLabel::new(selected, job).ui(ui).on_hover_ui_at_pointer(|ui| {
|
let response = SelectableLabel::new(selected, job)
|
||||||
symbol_hover_ui(ui, ctx.obj.arch.as_ref(), symbol, appearance)
|
.ui(ui)
|
||||||
});
|
.on_hover_ui_at_pointer(|ui| symbol_hover_ui(ui, ctx, symbol, appearance));
|
||||||
response.context_menu(|ui| {
|
response.context_menu(|ui| {
|
||||||
if let Some(result) =
|
if let Some(result) =
|
||||||
symbol_context_menu_ui(ui, ctx, other_ctx, symbol, symbol_diff, section, column)
|
symbol_context_menu_ui(ui, ctx, other_ctx, symbol, symbol_diff, section, column)
|
||||||
@ -542,7 +534,7 @@ fn symbol_ui(
|
|||||||
if response.clicked() || (selected && hotkeys::enter_pressed(ui.ctx())) {
|
if response.clicked() || (selected && hotkeys::enter_pressed(ui.ctx())) {
|
||||||
if let Some(section) = section {
|
if let Some(section) = section {
|
||||||
match section.kind {
|
match section.kind {
|
||||||
ObjSectionKind::Code => {
|
SectionKind::Code => {
|
||||||
ret = Some(DiffViewAction::Navigate(DiffViewNavigation::with_symbols(
|
ret = Some(DiffViewAction::Navigate(DiffViewNavigation::with_symbols(
|
||||||
View::FunctionDiff,
|
View::FunctionDiff,
|
||||||
other_ctx,
|
other_ctx,
|
||||||
@ -552,62 +544,56 @@ fn symbol_ui(
|
|||||||
column,
|
column,
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
ObjSectionKind::Data => {
|
SectionKind::Data => {
|
||||||
ret = Some(DiffViewAction::Navigate(DiffViewNavigation::data_diff(
|
ret = Some(DiffViewAction::Navigate(DiffViewNavigation::data_diff(
|
||||||
section, column,
|
section, column,
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
ObjSectionKind::Bss => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if response.hovered() {
|
} else if response.hovered() {
|
||||||
ret = Some(if column == 0 {
|
ret = Some(if column == 0 {
|
||||||
DiffViewAction::SetSymbolHighlight(
|
DiffViewAction::SetSymbolHighlight(Some(symbol_idx), symbol_diff.target_symbol, false)
|
||||||
Some(symbol_diff.symbol_ref),
|
|
||||||
symbol_diff.target_symbol,
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
DiffViewAction::SetSymbolHighlight(
|
DiffViewAction::SetSymbolHighlight(symbol_diff.target_symbol, Some(symbol_idx), false)
|
||||||
symbol_diff.target_symbol,
|
|
||||||
Some(symbol_diff.symbol_ref),
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
fn symbol_matches_filter(
|
fn find_prev_symbol(section_display: &[SectionDisplay], current: usize) -> Option<usize> {
|
||||||
symbol: &ObjSymbol,
|
section_display
|
||||||
diff: &ObjSymbolDiff,
|
.iter()
|
||||||
filter: SymbolFilter<'_>,
|
.flat_map(|s| s.symbols.iter())
|
||||||
) -> bool {
|
.rev()
|
||||||
match filter {
|
.skip_while(|s| s.symbol != current)
|
||||||
SymbolFilter::None => true,
|
.nth(1)
|
||||||
SymbolFilter::Search(regex) => {
|
.map(|s| s.symbol)
|
||||||
regex.is_match(&symbol.name)
|
// Wrap around to the last symbol if we're at the beginning of the list
|
||||||
|| symbol.demangled_name.as_deref().is_some_and(|s| regex.is_match(s))
|
.or_else(|| find_last_symbol(section_display))
|
||||||
}
|
|
||||||
SymbolFilter::Mapping(symbol_ref, regex) => {
|
|
||||||
diff.target_symbol == Some(symbol_ref)
|
|
||||||
&& regex.is_none_or(|r| {
|
|
||||||
r.is_match(&symbol.name)
|
|
||||||
|| symbol.demangled_name.as_deref().is_some_and(|s| r.is_match(s))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
fn find_next_symbol(section_display: &[SectionDisplay], current: usize) -> Option<usize> {
|
||||||
pub enum SymbolFilter<'a> {
|
section_display
|
||||||
None,
|
.iter()
|
||||||
Search(&'a Regex),
|
.flat_map(|s| s.symbols.iter())
|
||||||
Mapping(SymbolRef, Option<&'a Regex>),
|
.skip_while(|s| s.symbol != current)
|
||||||
|
.nth(1)
|
||||||
|
.map(|s| s.symbol)
|
||||||
|
// Wrap around to the first symbol if we're at the end of the list
|
||||||
|
.or_else(|| find_first_symbol(section_display))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_first_symbol(section_display: &[SectionDisplay]) -> Option<usize> {
|
||||||
|
section_display.iter().flat_map(|s| s.symbols.iter()).next().map(|s| s.symbol)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_last_symbol(section_display: &[SectionDisplay]) -> Option<usize> {
|
||||||
|
section_display.iter().flat_map(|s| s.symbols.iter()).next_back().map(|s| s.symbol)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
#[expect(clippy::too_many_arguments)]
|
|
||||||
pub fn symbol_list_ui(
|
pub fn symbol_list_ui(
|
||||||
ui: &mut Ui,
|
ui: &mut Ui,
|
||||||
ctx: SymbolDiffContext<'_>,
|
ctx: SymbolDiffContext<'_>,
|
||||||
@ -620,41 +606,20 @@ pub fn symbol_list_ui(
|
|||||||
) -> Option<DiffViewAction> {
|
) -> Option<DiffViewAction> {
|
||||||
let mut ret = None;
|
let mut ret = None;
|
||||||
ScrollArea::both().auto_shrink([false, false]).show(ui, |ui| {
|
ScrollArea::both().auto_shrink([false, false]).show(ui, |ui| {
|
||||||
let mut mapping = BTreeMap::new();
|
|
||||||
if let SymbolFilter::Mapping(_, _) = filter {
|
|
||||||
let mut show_mapped_symbols = state.show_mapped_symbols;
|
let mut show_mapped_symbols = state.show_mapped_symbols;
|
||||||
|
if let SymbolFilter::Mapping(_, _) = filter {
|
||||||
if ui.checkbox(&mut show_mapped_symbols, "Show mapped symbols").changed() {
|
if ui.checkbox(&mut show_mapped_symbols, "Show mapped symbols").changed() {
|
||||||
ret = Some(DiffViewAction::SetShowMappedSymbols(show_mapped_symbols));
|
ret = Some(DiffViewAction::SetShowMappedSymbols(show_mapped_symbols));
|
||||||
}
|
}
|
||||||
for mapping_diff in &ctx.diff.mapping_symbols {
|
|
||||||
let symbol = ctx.obj.section_symbol(mapping_diff.symbol_ref).1;
|
|
||||||
if !symbol_matches_filter(symbol, mapping_diff, filter) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if !show_mapped_symbols {
|
|
||||||
let symbol_diff = ctx.diff.symbol_diff(mapping_diff.symbol_ref);
|
|
||||||
if symbol_diff.target_symbol.is_some() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mapping.insert(mapping_diff.symbol_ref, mapping_diff);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (symbol, diff) in ctx.obj.common.iter().zip(&ctx.diff.common) {
|
|
||||||
if !symbol_matches_filter(symbol, diff, filter) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
mapping.insert(diff.symbol_ref, diff);
|
|
||||||
}
|
|
||||||
for (section, section_diff) in ctx.obj.sections.iter().zip(&ctx.diff.sections) {
|
|
||||||
for (symbol, symbol_diff) in section.symbols.iter().zip(§ion_diff.symbols) {
|
|
||||||
if !symbol_matches_filter(symbol, symbol_diff, filter) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
mapping.insert(symbol_diff.symbol_ref, symbol_diff);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
let section_display = display_sections(
|
||||||
|
ctx.obj,
|
||||||
|
ctx.diff,
|
||||||
|
filter,
|
||||||
|
state.show_hidden_symbols,
|
||||||
|
show_mapped_symbols,
|
||||||
|
state.reverse_fn_order,
|
||||||
|
);
|
||||||
|
|
||||||
hotkeys::check_scroll_hotkeys(ui, false);
|
hotkeys::check_scroll_hotkeys(ui, false);
|
||||||
|
|
||||||
@ -669,14 +634,11 @@ pub fn symbol_list_ui(
|
|||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
if let Some(mut up) = up {
|
if let Some(up) = up {
|
||||||
if state.reverse_fn_order {
|
|
||||||
up = !up;
|
|
||||||
}
|
|
||||||
new_key_value_to_highlight = if up {
|
new_key_value_to_highlight = if up {
|
||||||
mapping.range(..sym_ref).next_back()
|
find_prev_symbol(§ion_display, sym_ref)
|
||||||
} else {
|
} else {
|
||||||
mapping.range((Bound::Excluded(sym_ref), Bound::Unbounded)).next()
|
find_next_symbol(§ion_display, sym_ref)
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
@ -685,26 +647,15 @@ pub fn symbol_list_ui(
|
|||||||
// we do when a symbol is highlighted. This is so that if only one column has a symbol
|
// we do when a symbol is highlighted. This is so that if only one column has a symbol
|
||||||
// highlighted, that one takes precedence over the one with nothing highlighted.
|
// highlighted, that one takes precedence over the one with nothing highlighted.
|
||||||
if hotkeys::up_pressed(ui.ctx()) || hotkeys::down_pressed(ui.ctx()) {
|
if hotkeys::up_pressed(ui.ctx()) || hotkeys::down_pressed(ui.ctx()) {
|
||||||
new_key_value_to_highlight = if state.reverse_fn_order {
|
new_key_value_to_highlight = find_first_symbol(§ion_display);
|
||||||
mapping.last_key_value()
|
|
||||||
} else {
|
|
||||||
mapping.first_key_value()
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some((new_sym_ref, new_symbol_diff)) = new_key_value_to_highlight {
|
if let Some(new_sym_ref) = new_key_value_to_highlight {
|
||||||
|
let target_symbol = ctx.diff.symbols[new_sym_ref].target_symbol;
|
||||||
ret = Some(if column == 0 {
|
ret = Some(if column == 0 {
|
||||||
DiffViewAction::SetSymbolHighlight(
|
DiffViewAction::SetSymbolHighlight(Some(new_sym_ref), target_symbol, true)
|
||||||
Some(*new_sym_ref),
|
|
||||||
new_symbol_diff.target_symbol,
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
DiffViewAction::SetSymbolHighlight(
|
DiffViewAction::SetSymbolHighlight(target_symbol, Some(new_sym_ref), true)
|
||||||
new_symbol_diff.target_symbol,
|
|
||||||
Some(*new_sym_ref),
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -712,44 +663,21 @@ pub 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_mode = Some(egui::TextWrapMode::Extend);
|
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
|
||||||
|
|
||||||
// Skip sections with all symbols filtered out
|
for section_display in section_display {
|
||||||
if mapping.keys().any(|symbol_ref| symbol_ref.section_idx == SECTION_COMMON) {
|
|
||||||
CollapsingHeader::new(".comm").default_open(true).show(ui, |ui| {
|
|
||||||
for (symbol_ref, symbol_diff) in mapping
|
|
||||||
.iter()
|
|
||||||
.filter(|(symbol_ref, _)| symbol_ref.section_idx == SECTION_COMMON)
|
|
||||||
{
|
|
||||||
let symbol = ctx.obj.section_symbol(*symbol_ref).1;
|
|
||||||
if let Some(result) = symbol_ui(
|
|
||||||
ui,
|
|
||||||
ctx,
|
|
||||||
other_ctx,
|
|
||||||
symbol,
|
|
||||||
symbol_diff,
|
|
||||||
None,
|
|
||||||
state,
|
|
||||||
appearance,
|
|
||||||
column,
|
|
||||||
) {
|
|
||||||
ret = Some(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
for ((section_index, section), section_diff) in
|
|
||||||
ctx.obj.sections.iter().enumerate().zip(&ctx.diff.sections)
|
|
||||||
{
|
|
||||||
// Skip sections with all symbols filtered out
|
|
||||||
if !mapping.keys().any(|symbol_ref| symbol_ref.section_idx == section_index) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let mut header = LayoutJob::simple_singleline(
|
let mut header = LayoutJob::simple_singleline(
|
||||||
format!("{} ({:x})", section.name, section.size),
|
section_display.name.clone(),
|
||||||
appearance.code_font.clone(),
|
appearance.code_font.clone(),
|
||||||
Color32::PLACEHOLDER,
|
Color32::PLACEHOLDER,
|
||||||
);
|
);
|
||||||
if let Some(match_percent) = section_diff.match_percent {
|
if section_display.size > 0 {
|
||||||
|
write_text(
|
||||||
|
&format!(" ({:x})", section_display.size),
|
||||||
|
Color32::PLACEHOLDER,
|
||||||
|
&mut header,
|
||||||
|
appearance.code_font.clone(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if let Some(match_percent) = section_display.match_percent {
|
||||||
write_text(
|
write_text(
|
||||||
" (",
|
" (",
|
||||||
Color32::PLACEHOLDER,
|
Color32::PLACEHOLDER,
|
||||||
@ -770,44 +698,33 @@ pub fn symbol_list_ui(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
CollapsingHeader::new(header)
|
CollapsingHeader::new(header)
|
||||||
.id_salt(Id::new(section.name.clone()).with(section.orig_index))
|
.id_salt(Id::new(§ion_display.id))
|
||||||
.default_open(true)
|
.default_open(true)
|
||||||
.open(open_sections)
|
.open(open_sections)
|
||||||
.show(ui, |ui| {
|
.show(ui, |ui| {
|
||||||
if section.kind == ObjSectionKind::Code && state.reverse_fn_order {
|
for symbol_display in §ion_display.symbols {
|
||||||
for (symbol, symbol_diff) in mapping
|
let symbol = &ctx.obj.symbols[symbol_display.symbol];
|
||||||
|
let section = symbol
|
||||||
|
.section
|
||||||
|
.and_then(|section_idx| ctx.obj.sections.get(section_idx));
|
||||||
|
let symbol_diff = if symbol_display.is_mapping_symbol {
|
||||||
|
ctx.diff
|
||||||
|
.mapping_symbols
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(symbol_ref, _)| symbol_ref.section_idx == section_index)
|
.find(|d| d.symbol_index == symbol_display.symbol)
|
||||||
.rev()
|
.map(|d| &d.symbol_diff)
|
||||||
{
|
.unwrap()
|
||||||
let symbol = ctx.obj.section_symbol(*symbol).1;
|
|
||||||
if let Some(result) = symbol_ui(
|
|
||||||
ui,
|
|
||||||
ctx,
|
|
||||||
other_ctx,
|
|
||||||
symbol,
|
|
||||||
symbol_diff,
|
|
||||||
Some(section),
|
|
||||||
state,
|
|
||||||
appearance,
|
|
||||||
column,
|
|
||||||
) {
|
|
||||||
ret = Some(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
for (symbol, symbol_diff) in mapping
|
&ctx.diff.symbols[symbol_display.symbol]
|
||||||
.iter()
|
};
|
||||||
.filter(|(symbol_ref, _)| symbol_ref.section_idx == section_index)
|
|
||||||
{
|
|
||||||
let symbol = ctx.obj.section_symbol(*symbol).1;
|
|
||||||
if let Some(result) = symbol_ui(
|
if let Some(result) = symbol_ui(
|
||||||
ui,
|
ui,
|
||||||
ctx,
|
ctx,
|
||||||
other_ctx,
|
other_ctx,
|
||||||
symbol,
|
symbol,
|
||||||
symbol_diff,
|
symbol_diff,
|
||||||
Some(section),
|
symbol_display.symbol,
|
||||||
|
section,
|
||||||
state,
|
state,
|
||||||
appearance,
|
appearance,
|
||||||
column,
|
column,
|
||||||
@ -815,7 +732,6 @@ pub fn symbol_list_ui(
|
|||||||
ret = Some(result);
|
ret = Some(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -825,6 +741,6 @@ pub fn symbol_list_ui(
|
|||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub struct SymbolDiffContext<'a> {
|
pub struct SymbolDiffContext<'a> {
|
||||||
pub obj: &'a ObjInfo,
|
pub obj: &'a Object,
|
||||||
pub diff: &'a ObjDiff,
|
pub diff: &'a ObjectDiff,
|
||||||
}
|
}
|
||||||
|
39
objdiff-wasm/Cargo.toml
Normal file
39
objdiff-wasm/Cargo.toml
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
[package]
|
||||||
|
name = "objdiff-wasm"
|
||||||
|
version.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
rust-version.workspace = true
|
||||||
|
authors.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
readme = "../README.md"
|
||||||
|
description = """
|
||||||
|
A local diffing tool for decompilation projects.
|
||||||
|
"""
|
||||||
|
publish = false
|
||||||
|
build = "build.rs"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["std"]
|
||||||
|
std = ["objdiff-core/std"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
log = { version = "0.4", default-features = false }
|
||||||
|
regex = { version = "1.11", default-features = false }
|
||||||
|
|
||||||
|
[dependencies.objdiff-core]
|
||||||
|
path = "../objdiff-core"
|
||||||
|
default-features = false
|
||||||
|
features = ["arm", "arm64", "mips", "ppc", "dwarf"]
|
||||||
|
|
||||||
|
[target.'cfg(target_family = "wasm")'.dependencies]
|
||||||
|
talc = { version = "4.4", default-features = false, features = ["lock_api"] }
|
||||||
|
|
||||||
|
[target.'cfg(target_os = "wasi")'.dependencies]
|
||||||
|
wit-bindgen = { version = "0.39", default-features = false, features = ["macros"] }
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
wit-deps = "0.5"
|
31
objdiff-wasm/biome.json
Normal file
31
objdiff-wasm/biome.json
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://biomejs.dev/schemas/1.8.0/schema.json",
|
||||||
|
"organizeImports": {
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
"vcs": {
|
||||||
|
"enabled": true,
|
||||||
|
"clientKind": "git",
|
||||||
|
"useIgnoreFile": true
|
||||||
|
},
|
||||||
|
"formatter": {
|
||||||
|
"indentStyle": "space"
|
||||||
|
},
|
||||||
|
"javascript": {
|
||||||
|
"formatter": {
|
||||||
|
"quoteStyle": "single"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"linter": {
|
||||||
|
"enabled": true,
|
||||||
|
"rules": {
|
||||||
|
"recommended": true,
|
||||||
|
"a11y": {
|
||||||
|
"all": false
|
||||||
|
},
|
||||||
|
"suspicious": {
|
||||||
|
"noExplicitAny": "off"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
4
objdiff-wasm/build.rs
Normal file
4
objdiff-wasm/build.rs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
wit_deps::lock_sync!()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
@ -1,28 +0,0 @@
|
|||||||
import globals from "globals";
|
|
||||||
import pluginJs from "@eslint/js";
|
|
||||||
import tseslint from "typescript-eslint";
|
|
||||||
|
|
||||||
export default [
|
|
||||||
{files: ["**/*.{js,mjs,cjs,ts}"]},
|
|
||||||
{languageOptions: {globals: globals.browser}},
|
|
||||||
pluginJs.configs.recommended,
|
|
||||||
...tseslint.configs.recommended,
|
|
||||||
{
|
|
||||||
rules: {
|
|
||||||
"semi": [2, "always"],
|
|
||||||
"@typescript-eslint/no-unused-vars": [
|
|
||||||
"error",
|
|
||||||
// https://typescript-eslint.io/rules/no-unused-vars/#benefits-over-typescript
|
|
||||||
{
|
|
||||||
"args": "all",
|
|
||||||
"argsIgnorePattern": "^_",
|
|
||||||
"caughtErrors": "all",
|
|
||||||
"caughtErrorsIgnorePattern": "^_",
|
|
||||||
"destructuredArrayIgnorePattern": "^_",
|
|
||||||
"varsIgnorePattern": "^_",
|
|
||||||
"ignoreRestSiblings": true
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
];
|
|
25
objdiff-wasm/lib/wasi-logging.ts
Normal file
25
objdiff-wasm/lib/wasi-logging.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import type { WasiLoggingLogging010Draft as logging } from '../pkg/objdiff';
|
||||||
|
|
||||||
|
export const log: typeof logging.log = (level, context, message) => {
|
||||||
|
const msg = `[${context}] ${message}`;
|
||||||
|
switch (level) {
|
||||||
|
case 'trace':
|
||||||
|
console.trace(msg);
|
||||||
|
break;
|
||||||
|
case 'debug':
|
||||||
|
console.debug(msg);
|
||||||
|
break;
|
||||||
|
case 'info':
|
||||||
|
console.info(msg);
|
||||||
|
break;
|
||||||
|
case 'warn':
|
||||||
|
console.warn(msg);
|
||||||
|
break;
|
||||||
|
case 'error':
|
||||||
|
console.error(msg);
|
||||||
|
break;
|
||||||
|
case 'critical':
|
||||||
|
console.error(msg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
4666
objdiff-wasm/package-lock.json
generated
4666
objdiff-wasm/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "objdiff-wasm",
|
"name": "objdiff-wasm",
|
||||||
"version": "2.7.1",
|
"version": "3.0.0-alpha.1",
|
||||||
"description": "A local diffing tool for decompilation projects.",
|
"description": "A local diffing tool for decompilation projects.",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Luke Street",
|
"name": "Luke Street",
|
||||||
@ -12,28 +12,19 @@
|
|||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/encounter/objdiff.git"
|
"url": "git+https://github.com/encounter/objdiff.git"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": ["dist/*"],
|
||||||
"dist/*"
|
"main": "dist/objdiff.js",
|
||||||
],
|
"types": "dist/objdiff.d.ts",
|
||||||
"main": "dist/main.js",
|
|
||||||
"types": "dist/main.d.ts",
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsup",
|
"build": "npm run build:wasm && npm run build:transpile && npm run build:lib",
|
||||||
"build:all": "npm run build:wasm && npm run build:proto && npm run build",
|
"build:wasm": "cargo +nightly -Zbuild-std=panic_abort,core,alloc -Zbuild-std-features=compiler-builtins-mem build --target wasm32-wasip2 --release --no-default-features",
|
||||||
"build:proto": "protoc --ts_out=gen --ts_opt add_pb_suffix,eslint_disable,ts_nocheck,use_proto_field_name --proto_path=../objdiff-core/protos ../objdiff-core/protos/*.proto",
|
"build:transpile": "jco transpile ../target/wasm32-wasip2/release/objdiff_wasm.wasm --no-nodejs-compat --no-wasi-shim --no-namespaced-exports --map wasi:logging/logging=./wasi-logging.js -o pkg --name objdiff",
|
||||||
"build:wasm": "cd ../objdiff-core && wasm-pack build --out-dir ../objdiff-wasm/pkg --target web -- --features arm,arm64,dwarf,config,ppc,x86,wasm"
|
"build:lib": "rslib build"
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@protobuf-ts/runtime": "^2.9.4"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.9.0",
|
"@biomejs/biome": "^1.9.3",
|
||||||
"@protobuf-ts/plugin": "^2.9.4",
|
"@bytecodealliance/jco": "^1.9.1",
|
||||||
"@types/node": "^22.4.1",
|
"@rslib/core": "^0.4.1",
|
||||||
"esbuild": "^0.23.1",
|
"typescript": "^5.7.2"
|
||||||
"eslint": "^9.9.0",
|
|
||||||
"globals": "^15.9.0",
|
|
||||||
"tsup": "^8.2.4",
|
|
||||||
"typescript-eslint": "^8.2.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
19
objdiff-wasm/rslib.config.ts
Normal file
19
objdiff-wasm/rslib.config.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { defineConfig } from '@rslib/core';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
source: {
|
||||||
|
entry: {
|
||||||
|
'wasi-logging': 'lib/wasi-logging.ts',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lib: [
|
||||||
|
{
|
||||||
|
format: 'esm',
|
||||||
|
syntax: 'es2022',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
output: {
|
||||||
|
target: 'web',
|
||||||
|
copy: [{ from: 'pkg' }, { from: '../objdiff-core/config-schema.json' }],
|
||||||
|
},
|
||||||
|
});
|
321
objdiff-wasm/src/api.rs
Normal file
321
objdiff-wasm/src/api.rs
Normal file
@ -0,0 +1,321 @@
|
|||||||
|
use alloc::{
|
||||||
|
format,
|
||||||
|
rc::Rc,
|
||||||
|
str::FromStr,
|
||||||
|
string::{String, ToString},
|
||||||
|
vec::Vec,
|
||||||
|
};
|
||||||
|
use core::cell::RefCell;
|
||||||
|
|
||||||
|
use objdiff_core::{diff, obj};
|
||||||
|
use regex::RegexBuilder;
|
||||||
|
|
||||||
|
use super::logging;
|
||||||
|
|
||||||
|
wit_bindgen::generate!({
|
||||||
|
world: "api",
|
||||||
|
with: {
|
||||||
|
"wasi:logging/logging@0.1.0-draft": logging::wasi_logging,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
use exports::objdiff::core::{
|
||||||
|
diff::Guest as GuestDiff,
|
||||||
|
diff_types::{
|
||||||
|
DiffConfigBorrow, DiffResult, Guest as GuestDiffTypes, GuestDiffConfig, GuestObject,
|
||||||
|
GuestObjectDiff, Object, ObjectBorrow, ObjectDiff, ObjectDiffBorrow,
|
||||||
|
},
|
||||||
|
display_types::{
|
||||||
|
ContextMenuItem, DiffText, DiffTextOpcode, DiffTextSegment, DiffTextSymbol, DisplayConfig,
|
||||||
|
HoverItem, InstructionDiffKind, InstructionDiffRow, SectionDisplay, SectionDisplaySymbol,
|
||||||
|
SymbolDisplay, SymbolFilter, SymbolFlags, SymbolKind, SymbolRef,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Component;
|
||||||
|
|
||||||
|
impl Guest for Component {
|
||||||
|
fn init(level: logging::wasi_logging::Level) { logging::init(level); }
|
||||||
|
|
||||||
|
fn version() -> String { env!("CARGO_PKG_VERSION").to_string() }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(transparent)]
|
||||||
|
struct ResourceObject(Rc<obj::Object>);
|
||||||
|
|
||||||
|
struct ResourceObjectDiff(Rc<obj::Object>, diff::ObjectDiff);
|
||||||
|
|
||||||
|
#[repr(transparent)]
|
||||||
|
struct ResourceDiffConfig(RefCell<diff::DiffObjConfig>);
|
||||||
|
|
||||||
|
impl GuestDiffTypes for Component {
|
||||||
|
type DiffConfig = ResourceDiffConfig;
|
||||||
|
type Object = ResourceObject;
|
||||||
|
type ObjectDiff = ResourceObjectDiff;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GuestDiff for Component {
|
||||||
|
fn run_diff(
|
||||||
|
left: Option<ObjectBorrow>,
|
||||||
|
right: Option<ObjectBorrow>,
|
||||||
|
diff_config: DiffConfigBorrow,
|
||||||
|
) -> Result<DiffResult, String> {
|
||||||
|
let diff_config = diff_config.get::<ResourceDiffConfig>().0.borrow();
|
||||||
|
log::debug!("Running diff with config: {:?}", diff_config);
|
||||||
|
let result = diff::diff_objs(
|
||||||
|
left.as_ref().map(|o| o.get::<ResourceObject>().0.as_ref()),
|
||||||
|
right.as_ref().map(|o| o.get::<ResourceObject>().0.as_ref()),
|
||||||
|
None,
|
||||||
|
&diff_config,
|
||||||
|
&diff::MappingConfig::default(),
|
||||||
|
)
|
||||||
|
.map_err(|e| e.to_string())?;
|
||||||
|
Ok(DiffResult {
|
||||||
|
left: result.left.map(|d| {
|
||||||
|
ObjectDiff::new(ResourceObjectDiff(
|
||||||
|
left.unwrap().get::<ResourceObject>().0.clone(),
|
||||||
|
d,
|
||||||
|
))
|
||||||
|
}),
|
||||||
|
right: result.right.map(|d| {
|
||||||
|
ObjectDiff::new(ResourceObjectDiff(
|
||||||
|
right.unwrap().get::<ResourceObject>().0.clone(),
|
||||||
|
d,
|
||||||
|
))
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn symbol_context(_obj: ObjectBorrow, _symbol: SymbolRef) -> Vec<ContextMenuItem> { todo!() }
|
||||||
|
|
||||||
|
fn symbol_hover(_obj: ObjectBorrow, _symbol: SymbolRef) -> Vec<HoverItem> { todo!() }
|
||||||
|
|
||||||
|
fn display_sections(
|
||||||
|
diff: ObjectDiffBorrow,
|
||||||
|
filter: SymbolFilter,
|
||||||
|
config: DisplayConfig,
|
||||||
|
) -> Vec<SectionDisplay> {
|
||||||
|
let regex = filter.regex.as_ref().and_then(|s| {
|
||||||
|
RegexBuilder::new(s).case_insensitive(true).build().ok().or_else(|| {
|
||||||
|
// Use the string as a literal if the regex fails to compile
|
||||||
|
let escaped = regex::escape(s);
|
||||||
|
RegexBuilder::new(&escaped).case_insensitive(true).build().ok()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
let filter = if let Some(mapping) = filter.mapping {
|
||||||
|
diff::display::SymbolFilter::Mapping(mapping as usize, regex.as_ref())
|
||||||
|
} else if let Some(regex) = ®ex {
|
||||||
|
diff::display::SymbolFilter::Search(regex)
|
||||||
|
} else {
|
||||||
|
diff::display::SymbolFilter::None
|
||||||
|
};
|
||||||
|
let obj_diff = diff.get::<ResourceObjectDiff>();
|
||||||
|
diff::display::display_sections(
|
||||||
|
obj_diff.0.as_ref(),
|
||||||
|
&obj_diff.1,
|
||||||
|
filter,
|
||||||
|
config.show_hidden_symbols,
|
||||||
|
config.show_mapped_symbols,
|
||||||
|
config.reverse_fn_order,
|
||||||
|
)
|
||||||
|
.into_iter()
|
||||||
|
.map(|d| SectionDisplay {
|
||||||
|
id: d.id,
|
||||||
|
name: d.name,
|
||||||
|
size: d.size,
|
||||||
|
match_percent: d.match_percent,
|
||||||
|
symbols: d
|
||||||
|
.symbols
|
||||||
|
.into_iter()
|
||||||
|
.map(|s| SectionDisplaySymbol {
|
||||||
|
symbol: s.symbol as SymbolRef,
|
||||||
|
is_mapping_symbol: s.is_mapping_symbol,
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn display_symbol(
|
||||||
|
diff: ObjectDiffBorrow,
|
||||||
|
symbol_display: SectionDisplaySymbol,
|
||||||
|
) -> SymbolDisplay {
|
||||||
|
let obj_diff = diff.get::<ResourceObjectDiff>();
|
||||||
|
let obj = obj_diff.0.as_ref();
|
||||||
|
let obj_diff = &obj_diff.1;
|
||||||
|
let symbol_idx = symbol_display.symbol as usize;
|
||||||
|
let symbol = &obj.symbols[symbol_idx];
|
||||||
|
let symbol_diff = if symbol_display.is_mapping_symbol {
|
||||||
|
obj_diff
|
||||||
|
.mapping_symbols
|
||||||
|
.iter()
|
||||||
|
.find(|s| s.symbol_index == symbol_idx)
|
||||||
|
.map(|s| &s.symbol_diff)
|
||||||
|
.unwrap()
|
||||||
|
} else {
|
||||||
|
&obj_diff.symbols[symbol_idx]
|
||||||
|
};
|
||||||
|
SymbolDisplay {
|
||||||
|
name: symbol.name.clone(),
|
||||||
|
demangled_name: symbol.demangled_name.clone(),
|
||||||
|
address: symbol.address,
|
||||||
|
size: symbol.size,
|
||||||
|
kind: SymbolKind::from(symbol.kind),
|
||||||
|
section: symbol.section.map(|s| s as u32),
|
||||||
|
flags: SymbolFlags::from(symbol.flags),
|
||||||
|
align: symbol.align.map(|a| a.get()),
|
||||||
|
virtual_address: symbol.virtual_address,
|
||||||
|
target_symbol: symbol_diff.target_symbol.map(|s| s as u32),
|
||||||
|
match_percent: symbol_diff.match_percent,
|
||||||
|
diff_score: symbol_diff.diff_score,
|
||||||
|
row_count: symbol_diff.instruction_rows.len() as u32,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn display_instruction_row(
|
||||||
|
diff: ObjectDiffBorrow,
|
||||||
|
symbol_display: SectionDisplaySymbol,
|
||||||
|
row_index: u32,
|
||||||
|
diff_config: DiffConfigBorrow,
|
||||||
|
) -> InstructionDiffRow {
|
||||||
|
let mut segments = Vec::with_capacity(16);
|
||||||
|
let obj_diff = diff.get::<ResourceObjectDiff>();
|
||||||
|
let obj = obj_diff.0.as_ref();
|
||||||
|
let obj_diff = &obj_diff.1;
|
||||||
|
let symbol_idx = symbol_display.symbol as usize;
|
||||||
|
let symbol_diff = if symbol_display.is_mapping_symbol {
|
||||||
|
obj_diff
|
||||||
|
.mapping_symbols
|
||||||
|
.iter()
|
||||||
|
.find(|s| s.symbol_index == symbol_idx)
|
||||||
|
.map(|s| &s.symbol_diff)
|
||||||
|
.unwrap()
|
||||||
|
} else {
|
||||||
|
&obj_diff.symbols[symbol_idx]
|
||||||
|
};
|
||||||
|
let row = &symbol_diff.instruction_rows[row_index as usize];
|
||||||
|
let diff_config = diff_config.get::<ResourceDiffConfig>().0.borrow();
|
||||||
|
diff::display::display_row(obj, symbol_idx, row, &diff_config, |text, idx| {
|
||||||
|
segments.push(DiffTextSegment { text: DiffText::from(text), diff_index: idx.get() });
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
InstructionDiffRow { segments, diff_kind: InstructionDiffKind::from(row.kind) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<obj::SymbolKind> for SymbolKind {
|
||||||
|
fn from(kind: obj::SymbolKind) -> Self {
|
||||||
|
match kind {
|
||||||
|
obj::SymbolKind::Unknown => SymbolKind::Unknown,
|
||||||
|
obj::SymbolKind::Function => SymbolKind::Function,
|
||||||
|
obj::SymbolKind::Object => SymbolKind::Object,
|
||||||
|
obj::SymbolKind::Section => SymbolKind::Section,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<obj::SymbolFlagSet> for SymbolFlags {
|
||||||
|
fn from(flags: obj::SymbolFlagSet) -> SymbolFlags {
|
||||||
|
let mut out = SymbolFlags::empty();
|
||||||
|
for flag in flags {
|
||||||
|
out |= match flag {
|
||||||
|
obj::SymbolFlag::Global => SymbolFlags::GLOBAL,
|
||||||
|
obj::SymbolFlag::Local => SymbolFlags::LOCAL,
|
||||||
|
obj::SymbolFlag::Weak => SymbolFlags::WEAK,
|
||||||
|
obj::SymbolFlag::Common => SymbolFlags::COMMON,
|
||||||
|
obj::SymbolFlag::Hidden => SymbolFlags::HIDDEN,
|
||||||
|
obj::SymbolFlag::HasExtra => SymbolFlags::HAS_EXTRA,
|
||||||
|
obj::SymbolFlag::SizeInferred => SymbolFlags::SIZE_INFERRED,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<diff::display::DiffText<'_>> for DiffText {
|
||||||
|
fn from(text: diff::display::DiffText) -> Self {
|
||||||
|
match text {
|
||||||
|
diff::display::DiffText::Basic(v) => DiffText::Basic(v.to_string()),
|
||||||
|
diff::display::DiffText::Line(v) => DiffText::Line(v),
|
||||||
|
diff::display::DiffText::Address(v) => DiffText::Address(v),
|
||||||
|
diff::display::DiffText::Opcode(n, op) => {
|
||||||
|
DiffText::Opcode(DiffTextOpcode { mnemonic: n.to_string(), opcode: op })
|
||||||
|
}
|
||||||
|
diff::display::DiffText::Argument(s) => match s {
|
||||||
|
obj::InstructionArgValue::Signed(v) => DiffText::Signed(*v),
|
||||||
|
obj::InstructionArgValue::Unsigned(v) => DiffText::Unsigned(*v),
|
||||||
|
obj::InstructionArgValue::Opaque(v) => DiffText::Opaque(v.to_string()),
|
||||||
|
},
|
||||||
|
diff::display::DiffText::BranchDest(v) => DiffText::BranchDest(v),
|
||||||
|
diff::display::DiffText::Symbol(s) => DiffText::Symbol(DiffTextSymbol {
|
||||||
|
name: s.name.clone(),
|
||||||
|
demangled_name: s.demangled_name.clone(),
|
||||||
|
}),
|
||||||
|
diff::display::DiffText::Addend(v) => DiffText::Addend(v),
|
||||||
|
diff::display::DiffText::Spacing(v) => DiffText::Spacing(v as u32),
|
||||||
|
diff::display::DiffText::Eol => DiffText::Eol,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<diff::InstructionDiffKind> for InstructionDiffKind {
|
||||||
|
fn from(kind: diff::InstructionDiffKind) -> Self {
|
||||||
|
match kind {
|
||||||
|
diff::InstructionDiffKind::None => InstructionDiffKind::None,
|
||||||
|
diff::InstructionDiffKind::OpMismatch => InstructionDiffKind::OpMismatch,
|
||||||
|
diff::InstructionDiffKind::ArgMismatch => InstructionDiffKind::ArgMismatch,
|
||||||
|
diff::InstructionDiffKind::Replace => InstructionDiffKind::Replace,
|
||||||
|
diff::InstructionDiffKind::Insert => InstructionDiffKind::Insert,
|
||||||
|
diff::InstructionDiffKind::Delete => InstructionDiffKind::Delete,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GuestDiffConfig for ResourceDiffConfig {
|
||||||
|
fn new() -> Self { Self(RefCell::new(diff::DiffObjConfig::default())) }
|
||||||
|
|
||||||
|
fn set_property(&self, key: String, value: String) -> Result<(), String> {
|
||||||
|
let id = diff::ConfigPropertyId::from_str(&key)
|
||||||
|
.map_err(|_| format!("Invalid property key {:?}", key))?;
|
||||||
|
self.0
|
||||||
|
.borrow_mut()
|
||||||
|
.set_property_value_str(id, &value)
|
||||||
|
.map_err(|_| format!("Invalid property value {:?}", value))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_property(&self, key: String) -> Result<String, String> {
|
||||||
|
let id = diff::ConfigPropertyId::from_str(&key)
|
||||||
|
.map_err(|_| format!("Invalid property key {:?}", key))?;
|
||||||
|
Ok(self.0.borrow().get_property_value(id).to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GuestObject for ResourceObject {
|
||||||
|
fn parse(data: Vec<u8>, diff_config: DiffConfigBorrow) -> Result<Object, String> {
|
||||||
|
let diff_config = diff_config.get::<ResourceDiffConfig>().0.borrow();
|
||||||
|
obj::read::parse(&data, &diff_config)
|
||||||
|
.map(|o| Object::new(ResourceObject(Rc::new(o))))
|
||||||
|
.map_err(|e| e.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GuestObjectDiff for ResourceObjectDiff {
|
||||||
|
fn find_symbol(&self, name: String, section_name: Option<String>) -> Option<SymbolRef> {
|
||||||
|
let obj = self.0.as_ref();
|
||||||
|
obj.symbols
|
||||||
|
.iter()
|
||||||
|
.position(|s| {
|
||||||
|
s.name == name
|
||||||
|
&& match section_name.as_deref() {
|
||||||
|
Some(section_name) => {
|
||||||
|
s.section.is_some_and(|n| obj.sections[n].name == section_name)
|
||||||
|
}
|
||||||
|
None => true,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map(|i| i as SymbolRef)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export!(Component);
|
@ -1,107 +0,0 @@
|
|||||||
import {ArgumentValue, InstructionDiff, RelocationTarget} from "../gen/diff_pb";
|
|
||||||
|
|
||||||
export type DiffText =
|
|
||||||
DiffTextBasic
|
|
||||||
| DiffTextBasicColor
|
|
||||||
| DiffTextAddress
|
|
||||||
| DiffTextLine
|
|
||||||
| DiffTextOpcode
|
|
||||||
| DiffTextArgument
|
|
||||||
| DiffTextSymbol
|
|
||||||
| DiffTextBranchDest
|
|
||||||
| DiffTextSpacing;
|
|
||||||
|
|
||||||
type DiffTextBase = {
|
|
||||||
diff_index?: number,
|
|
||||||
};
|
|
||||||
export type DiffTextBasic = DiffTextBase & {
|
|
||||||
type: 'basic',
|
|
||||||
text: string,
|
|
||||||
};
|
|
||||||
export type DiffTextBasicColor = DiffTextBase & {
|
|
||||||
type: 'basic_color',
|
|
||||||
text: string,
|
|
||||||
index: number,
|
|
||||||
};
|
|
||||||
export type DiffTextAddress = DiffTextBase & {
|
|
||||||
type: 'address',
|
|
||||||
address: bigint,
|
|
||||||
};
|
|
||||||
export type DiffTextLine = DiffTextBase & {
|
|
||||||
type: 'line',
|
|
||||||
line_number: number,
|
|
||||||
};
|
|
||||||
export type DiffTextOpcode = DiffTextBase & {
|
|
||||||
type: 'opcode',
|
|
||||||
mnemonic: string,
|
|
||||||
opcode: number,
|
|
||||||
};
|
|
||||||
export type DiffTextArgument = DiffTextBase & {
|
|
||||||
type: 'argument',
|
|
||||||
value: ArgumentValue,
|
|
||||||
};
|
|
||||||
export type DiffTextSymbol = DiffTextBase & {
|
|
||||||
type: 'symbol',
|
|
||||||
target: RelocationTarget,
|
|
||||||
};
|
|
||||||
export type DiffTextBranchDest = DiffTextBase & {
|
|
||||||
type: 'branch_dest',
|
|
||||||
address: bigint,
|
|
||||||
};
|
|
||||||
export type DiffTextSpacing = DiffTextBase & {
|
|
||||||
type: 'spacing',
|
|
||||||
count: number,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Native JavaScript implementation of objdiff_core::diff::display::display_diff
|
|
||||||
export function displayDiff(diff: InstructionDiff, baseAddr: bigint, cb: (text: DiffText) => void) {
|
|
||||||
const ins = diff.instruction;
|
|
||||||
if (!ins) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (ins.line_number != null) {
|
|
||||||
cb({type: 'line', line_number: ins.line_number});
|
|
||||||
}
|
|
||||||
cb({type: 'address', address: ins.address - baseAddr});
|
|
||||||
if (diff.branch_from) {
|
|
||||||
cb({type: 'basic_color', text: ' ~> ', index: diff.branch_from.branch_index});
|
|
||||||
} else {
|
|
||||||
cb({type: 'spacing', count: 4});
|
|
||||||
}
|
|
||||||
cb({type: 'opcode', mnemonic: ins.mnemonic, opcode: ins.opcode});
|
|
||||||
let arg_diff_idx = 0; // non-PlainText argument index
|
|
||||||
for (let i = 0; i < ins.arguments.length; i++) {
|
|
||||||
if (i === 0) {
|
|
||||||
cb({type: 'spacing', count: 1});
|
|
||||||
}
|
|
||||||
const arg = ins.arguments[i].value;
|
|
||||||
let diff_index: number | undefined;
|
|
||||||
if (arg.oneofKind !== 'plain_text') {
|
|
||||||
diff_index = diff.arg_diff[arg_diff_idx]?.diff_index;
|
|
||||||
arg_diff_idx++;
|
|
||||||
}
|
|
||||||
switch (arg.oneofKind) {
|
|
||||||
case "plain_text":
|
|
||||||
cb({type: 'basic', text: arg.plain_text, diff_index});
|
|
||||||
break;
|
|
||||||
case "argument":
|
|
||||||
cb({type: 'argument', value: arg.argument, diff_index});
|
|
||||||
break;
|
|
||||||
case "relocation": {
|
|
||||||
const reloc = ins.relocation!;
|
|
||||||
cb({type: 'symbol', target: reloc.target!, diff_index});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "branch_dest":
|
|
||||||
if (arg.branch_dest < baseAddr) {
|
|
||||||
cb({type: 'basic', text: '<unknown>', diff_index});
|
|
||||||
} else {
|
|
||||||
cb({type: 'branch_dest', address: arg.branch_dest - baseAddr, diff_index});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (diff.branch_to) {
|
|
||||||
cb({type: 'basic_color', text: ' ~> ', index: diff.branch_to.branch_index});
|
|
||||||
}
|
|
||||||
}
|
|
14
objdiff-wasm/src/lib.rs
Normal file
14
objdiff-wasm/src/lib.rs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
extern crate alloc;
|
||||||
|
|
||||||
|
#[cfg(target_os = "wasi")]
|
||||||
|
mod api;
|
||||||
|
#[cfg(target_os = "wasi")]
|
||||||
|
mod logging;
|
||||||
|
|
||||||
|
#[cfg(all(target_os = "wasi", not(feature = "std")))]
|
||||||
|
mod cabi_realloc;
|
||||||
|
|
||||||
|
#[cfg(all(target_family = "wasm", not(feature = "std")))]
|
||||||
|
#[global_allocator]
|
||||||
|
static ALLOCATOR: talc::TalckWasm = unsafe { talc::TalckWasm::new_global() };
|
52
objdiff-wasm/src/logging.rs
Normal file
52
objdiff-wasm/src/logging.rs
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
wit_bindgen::generate!({
|
||||||
|
world: "imports",
|
||||||
|
path: "wit/deps/logging",
|
||||||
|
});
|
||||||
|
|
||||||
|
use alloc::format;
|
||||||
|
|
||||||
|
pub use wasi::logging::logging as wasi_logging;
|
||||||
|
|
||||||
|
struct WasiLogger;
|
||||||
|
|
||||||
|
impl log::Log for WasiLogger {
|
||||||
|
fn enabled(&self, metadata: &log::Metadata) -> bool { metadata.level() <= log::max_level() }
|
||||||
|
|
||||||
|
fn log(&self, record: &log::Record) {
|
||||||
|
if !self.enabled(record.metadata()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let level = match record.level() {
|
||||||
|
log::Level::Error => wasi_logging::Level::Error,
|
||||||
|
log::Level::Warn => wasi_logging::Level::Warn,
|
||||||
|
log::Level::Info => wasi_logging::Level::Info,
|
||||||
|
log::Level::Debug => wasi_logging::Level::Debug,
|
||||||
|
log::Level::Trace => wasi_logging::Level::Trace,
|
||||||
|
};
|
||||||
|
wasi_logging::log(level, record.target(), &format!("{}", record.args()));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&self) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
static LOGGER: WasiLogger = WasiLogger;
|
||||||
|
|
||||||
|
pub fn init(level: wasi_logging::Level) {
|
||||||
|
let _ = log::set_logger(&LOGGER);
|
||||||
|
log::set_max_level(match level {
|
||||||
|
wasi_logging::Level::Error => log::LevelFilter::Error,
|
||||||
|
wasi_logging::Level::Warn => log::LevelFilter::Warn,
|
||||||
|
wasi_logging::Level::Info => log::LevelFilter::Info,
|
||||||
|
wasi_logging::Level::Debug => log::LevelFilter::Debug,
|
||||||
|
wasi_logging::Level::Trace => log::LevelFilter::Trace,
|
||||||
|
wasi_logging::Level::Critical => log::LevelFilter::Off,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "std"))]
|
||||||
|
#[panic_handler]
|
||||||
|
fn panic(info: &core::panic::PanicInfo) -> ! {
|
||||||
|
use alloc::string::ToString;
|
||||||
|
wasi_logging::log(wasi_logging::Level::Critical, "objdiff_core::panic", &info.to_string());
|
||||||
|
core::arch::wasm32::unreachable();
|
||||||
|
}
|
@ -1,132 +0,0 @@
|
|||||||
import {DiffResult} from "../gen/diff_pb";
|
|
||||||
import type {
|
|
||||||
ConfigProperty,
|
|
||||||
MappingConfig,
|
|
||||||
SymbolMappings,
|
|
||||||
} from '../pkg';
|
|
||||||
import {AnyHandlerData, InMessage, OutMessage} from './worker';
|
|
||||||
|
|
||||||
// Export wasm types
|
|
||||||
export {ConfigProperty, MappingConfig, SymbolMappings};
|
|
||||||
|
|
||||||
// Export protobuf types
|
|
||||||
export * from '../gen/diff_pb';
|
|
||||||
|
|
||||||
// Export display types
|
|
||||||
export * from './display';
|
|
||||||
|
|
||||||
interface PromiseCallbacks<T> {
|
|
||||||
start: number;
|
|
||||||
resolve: (value: T | PromiseLike<T>) => void;
|
|
||||||
reject: (reason?: string) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
let workerInit = false;
|
|
||||||
let workerCallbacks: PromiseCallbacks<Worker>;
|
|
||||||
const workerReady = new Promise<Worker>((resolve, reject) => {
|
|
||||||
workerCallbacks = {start: performance.now(), resolve, reject};
|
|
||||||
});
|
|
||||||
|
|
||||||
export async function initialize(data?: {
|
|
||||||
workerUrl?: string | URL,
|
|
||||||
wasmUrl?: string | URL, // Relative to worker URL
|
|
||||||
}): Promise<Worker> {
|
|
||||||
if (workerInit) {
|
|
||||||
return workerReady;
|
|
||||||
}
|
|
||||||
workerInit = true;
|
|
||||||
let {workerUrl, wasmUrl} = data || {};
|
|
||||||
if (!workerUrl) {
|
|
||||||
try {
|
|
||||||
// Bundlers will convert this into an asset URL
|
|
||||||
workerUrl = new URL('./worker.js', import.meta.url);
|
|
||||||
} catch (_) {
|
|
||||||
workerUrl = 'worker.js';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!wasmUrl) {
|
|
||||||
try {
|
|
||||||
// Bundlers will convert this into an asset URL
|
|
||||||
wasmUrl = new URL('./objdiff_core_bg.wasm', import.meta.url);
|
|
||||||
} catch (_) {
|
|
||||||
wasmUrl = 'objdiff_core_bg.js';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const worker = new Worker(workerUrl, {
|
|
||||||
name: 'objdiff',
|
|
||||||
type: 'module',
|
|
||||||
});
|
|
||||||
worker.onmessage = onMessage;
|
|
||||||
worker.onerror = (event) => {
|
|
||||||
console.error("Worker error", event);
|
|
||||||
workerCallbacks.reject("Worker failed to initialize, wrong URL?");
|
|
||||||
};
|
|
||||||
defer<void>({
|
|
||||||
type: 'init',
|
|
||||||
// URL can't be sent directly
|
|
||||||
wasmUrl: wasmUrl.toString(),
|
|
||||||
}, worker).then(() => {
|
|
||||||
workerCallbacks.resolve(worker);
|
|
||||||
}, (e) => {
|
|
||||||
workerCallbacks.reject(e);
|
|
||||||
});
|
|
||||||
return workerReady;
|
|
||||||
}
|
|
||||||
|
|
||||||
let globalMessageId = 0;
|
|
||||||
const messageCallbacks = new Map<number, PromiseCallbacks<never>>();
|
|
||||||
|
|
||||||
function onMessage(event: MessageEvent<OutMessage>) {
|
|
||||||
switch (event.data.type) {
|
|
||||||
case 'result': {
|
|
||||||
const {result, error, messageId} = event.data;
|
|
||||||
const callbacks = messageCallbacks.get(messageId);
|
|
||||||
if (callbacks) {
|
|
||||||
const end = performance.now();
|
|
||||||
console.debug(`Message ${messageId} took ${end - callbacks.start}ms`);
|
|
||||||
messageCallbacks.delete(messageId);
|
|
||||||
if (error != null) {
|
|
||||||
callbacks.reject(error);
|
|
||||||
} else {
|
|
||||||
callbacks.resolve(result as never);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.warn(`Unknown message ID ${messageId}`);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function defer<T>(message: AnyHandlerData, worker?: Worker): Promise<T> {
|
|
||||||
worker = worker || await initialize();
|
|
||||||
const messageId = globalMessageId++;
|
|
||||||
const promise = new Promise<T>((resolve, reject) => {
|
|
||||||
messageCallbacks.set(messageId, {start: performance.now(), resolve, reject});
|
|
||||||
});
|
|
||||||
worker.postMessage({
|
|
||||||
...message,
|
|
||||||
messageId
|
|
||||||
} as InMessage);
|
|
||||||
return promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function runDiff(
|
|
||||||
left: Uint8Array | null | undefined,
|
|
||||||
right: Uint8Array | null | undefined,
|
|
||||||
properties?: ConfigProperty[],
|
|
||||||
mappingConfig?: MappingConfig,
|
|
||||||
): Promise<DiffResult> {
|
|
||||||
const data = await defer<Uint8Array>({
|
|
||||||
type: 'run_diff_proto',
|
|
||||||
left,
|
|
||||||
right,
|
|
||||||
properties,
|
|
||||||
mappingConfig,
|
|
||||||
});
|
|
||||||
const parseStart = performance.now();
|
|
||||||
const result = DiffResult.fromBinary(data, {readUnknownField: false});
|
|
||||||
const end = performance.now();
|
|
||||||
console.debug(`Parsing message took ${end - parseStart}ms`);
|
|
||||||
return result;
|
|
||||||
}
|
|
@ -1,93 +0,0 @@
|
|||||||
import wasmInit, * as exports from '../pkg';
|
|
||||||
|
|
||||||
const handlers = {
|
|
||||||
init: init,
|
|
||||||
run_diff_proto: run_diff_proto,
|
|
||||||
} as const;
|
|
||||||
type ExtractData<T> = T extends (arg: infer U) => Promise<unknown> ? U : never;
|
|
||||||
type HandlerData = {
|
|
||||||
[K in keyof typeof handlers]: { type: K } & ExtractData<typeof handlers[K]>;
|
|
||||||
};
|
|
||||||
|
|
||||||
let wasmReady: Promise<void> | null = null;
|
|
||||||
|
|
||||||
async function init({wasmUrl}: { wasmUrl?: string }): Promise<void> {
|
|
||||||
if (wasmReady != null) {
|
|
||||||
throw new Error('Already initialized');
|
|
||||||
}
|
|
||||||
wasmReady = wasmInit({module_or_path: wasmUrl})
|
|
||||||
.then(() => {
|
|
||||||
});
|
|
||||||
return wasmReady;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function initIfNeeded() {
|
|
||||||
if (wasmReady == null) {
|
|
||||||
await init({});
|
|
||||||
}
|
|
||||||
return wasmReady;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function run_diff_proto({left, right, properties, mappingConfig}: {
|
|
||||||
left: Uint8Array | null | undefined,
|
|
||||||
right: Uint8Array | null | undefined,
|
|
||||||
properties?: exports.ConfigProperty[],
|
|
||||||
mappingConfig?: exports.MappingConfig,
|
|
||||||
}): Promise<Uint8Array> {
|
|
||||||
const diffConfig = exports.config_from_properties(properties || []);
|
|
||||||
const leftObj = left ? exports.parse_object(left, diffConfig) : null;
|
|
||||||
const rightObj = right ? exports.parse_object(right, diffConfig) : null;
|
|
||||||
return exports.run_diff(leftObj, rightObj, diffConfig, mappingConfig || {});
|
|
||||||
}
|
|
||||||
|
|
||||||
export type AnyHandlerData = HandlerData[keyof HandlerData];
|
|
||||||
export type InMessage = AnyHandlerData & { messageId: number };
|
|
||||||
|
|
||||||
export type OutMessage = {
|
|
||||||
type: 'result',
|
|
||||||
result: unknown | null,
|
|
||||||
error: string | null,
|
|
||||||
messageId: number,
|
|
||||||
};
|
|
||||||
|
|
||||||
self.onmessage = (event: MessageEvent<InMessage>) => {
|
|
||||||
const data = event.data;
|
|
||||||
const messageId = data?.messageId;
|
|
||||||
(async () => {
|
|
||||||
if (!data) {
|
|
||||||
throw new Error('No data');
|
|
||||||
}
|
|
||||||
const handler = handlers[data.type];
|
|
||||||
if (handler) {
|
|
||||||
if (data.type !== 'init') {
|
|
||||||
await initIfNeeded();
|
|
||||||
}
|
|
||||||
const start = performance.now();
|
|
||||||
const result = await handler(data as never);
|
|
||||||
const end = performance.now();
|
|
||||||
console.debug(`Worker message ${data.messageId} took ${end - start}ms`);
|
|
||||||
let transfer: Transferable[] = [];
|
|
||||||
if (result instanceof Uint8Array) {
|
|
||||||
console.log("Transferring!", result.byteLength);
|
|
||||||
transfer = [result.buffer];
|
|
||||||
} else {
|
|
||||||
console.log("Didn't transfer", typeof result);
|
|
||||||
}
|
|
||||||
self.postMessage({
|
|
||||||
type: 'result',
|
|
||||||
result: result,
|
|
||||||
error: null,
|
|
||||||
messageId,
|
|
||||||
} as OutMessage, {transfer});
|
|
||||||
} else {
|
|
||||||
throw new Error(`No handler for ${data.type}`);
|
|
||||||
}
|
|
||||||
})().catch(error => {
|
|
||||||
self.postMessage({
|
|
||||||
type: 'result',
|
|
||||||
result: null,
|
|
||||||
error: error.toString(),
|
|
||||||
messageId,
|
|
||||||
} as OutMessage);
|
|
||||||
});
|
|
||||||
};
|
|
@ -1,9 +1,17 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"esModuleInterop": true,
|
"lib": ["DOM", "ES2023"],
|
||||||
"module": "ES2022",
|
|
||||||
"moduleResolution": "Node",
|
|
||||||
"strict": true,
|
|
||||||
"target": "ES2022",
|
"target": "ES2022",
|
||||||
}
|
"noEmit": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"isolatedModules": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"moduleResolution": "Bundler",
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true
|
||||||
|
},
|
||||||
|
"include": ["lib"]
|
||||||
}
|
}
|
||||||
|
@ -1,34 +0,0 @@
|
|||||||
import {defineConfig} from 'tsup';
|
|
||||||
import fs from 'node:fs/promises';
|
|
||||||
|
|
||||||
export default defineConfig([
|
|
||||||
// Build main library
|
|
||||||
{
|
|
||||||
entry: ['src/main.ts'],
|
|
||||||
clean: true,
|
|
||||||
dts: true,
|
|
||||||
format: 'esm',
|
|
||||||
outDir: 'dist',
|
|
||||||
skipNodeModulesBundle: true,
|
|
||||||
sourcemap: true,
|
|
||||||
splitting: false,
|
|
||||||
target: 'es2022',
|
|
||||||
},
|
|
||||||
// Build web worker
|
|
||||||
{
|
|
||||||
entry: ['src/worker.ts'],
|
|
||||||
clean: true,
|
|
||||||
dts: true,
|
|
||||||
format: 'esm', // type: 'module'
|
|
||||||
minify: true,
|
|
||||||
outDir: 'dist',
|
|
||||||
sourcemap: true,
|
|
||||||
splitting: false,
|
|
||||||
target: 'es2022',
|
|
||||||
// https://github.com/egoist/tsup/issues/278
|
|
||||||
async onSuccess() {
|
|
||||||
await fs.copyFile('pkg/objdiff_core_bg.wasm', 'dist/objdiff_core_bg.wasm');
|
|
||||||
await fs.copyFile('../objdiff-core/config-schema.json', 'dist/config-schema.json');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]);
|
|
1
objdiff-wasm/wit/.gitignore
vendored
Normal file
1
objdiff-wasm/wit/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
deps/
|
4
objdiff-wasm/wit/deps.lock
Normal file
4
objdiff-wasm/wit/deps.lock
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
[logging]
|
||||||
|
url = "https://github.com/WebAssembly/wasi-logging/archive/d31c41d0d9eed81aabe02333d0025d42acf3fb75.tar.gz"
|
||||||
|
sha256 = "ad81d8b7f7a8ceb729cf551f1d24586f0de9560a43eea57a9bb031d2175804e1"
|
||||||
|
sha512 = "1687ad9a02ab3e689443e67d1a0605f58fc5dea828d2e4d2c7825c6002714fac9bd4289b1a68b61a37dcca6c3b421f4c8ed4b1e6cc29f6460e0913cf1bf11c04"
|
1
objdiff-wasm/wit/deps.toml
Normal file
1
objdiff-wasm/wit/deps.toml
Normal file
@ -0,0 +1 @@
|
|||||||
|
logging = "https://github.com/WebAssembly/wasi-logging/archive/d31c41d0d9eed81aabe02333d0025d42acf3fb75.tar.gz"
|
249
objdiff-wasm/wit/objdiff.wit
Normal file
249
objdiff-wasm/wit/objdiff.wit
Normal file
@ -0,0 +1,249 @@
|
|||||||
|
package objdiff:core;
|
||||||
|
|
||||||
|
use wasi:logging/logging@0.1.0-draft;
|
||||||
|
|
||||||
|
interface diff-types {
|
||||||
|
resource diff-config {
|
||||||
|
constructor();
|
||||||
|
set-property: func(id: string, value: string) -> result<_, string>;
|
||||||
|
get-property: func(id: string) -> result<string, string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
record mapping-config {
|
||||||
|
mappings: list<tuple<string, string>>,
|
||||||
|
selecting-left: option<string>,
|
||||||
|
selecting-right: option<string>,
|
||||||
|
}
|
||||||
|
|
||||||
|
resource object {
|
||||||
|
parse: static func(
|
||||||
|
data: list<u8>,
|
||||||
|
config: borrow<diff-config>,
|
||||||
|
) -> result<object, string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
resource object-diff {
|
||||||
|
find-symbol: func(
|
||||||
|
name: string,
|
||||||
|
section-name: option<string>
|
||||||
|
) -> option<u32>;
|
||||||
|
}
|
||||||
|
|
||||||
|
record diff-result {
|
||||||
|
left: option<object-diff>,
|
||||||
|
right: option<object-diff>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface display-types {
|
||||||
|
type symbol-ref = u32;
|
||||||
|
|
||||||
|
record display-config {
|
||||||
|
show-hidden-symbols: bool,
|
||||||
|
show-mapped-symbols: bool,
|
||||||
|
reverse-fn-order: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
record symbol-filter {
|
||||||
|
regex: option<string>,
|
||||||
|
mapping: option<symbol-ref>,
|
||||||
|
}
|
||||||
|
|
||||||
|
record section-display-symbol {
|
||||||
|
symbol: symbol-ref,
|
||||||
|
is-mapping-symbol: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
record section-display {
|
||||||
|
id: string,
|
||||||
|
name: string,
|
||||||
|
size: u64,
|
||||||
|
match-percent: option<f32>,
|
||||||
|
symbols: list<section-display-symbol>,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum symbol-kind {
|
||||||
|
unknown,
|
||||||
|
function,
|
||||||
|
object,
|
||||||
|
section,
|
||||||
|
}
|
||||||
|
|
||||||
|
flags symbol-flags {
|
||||||
|
global,
|
||||||
|
local,
|
||||||
|
weak,
|
||||||
|
common,
|
||||||
|
hidden,
|
||||||
|
has-extra,
|
||||||
|
size-inferred,
|
||||||
|
}
|
||||||
|
|
||||||
|
record symbol-display {
|
||||||
|
name: string,
|
||||||
|
demangled-name: option<string>,
|
||||||
|
address: u64,
|
||||||
|
size: u64,
|
||||||
|
kind: symbol-kind,
|
||||||
|
section: option<u32>,
|
||||||
|
%flags: symbol-flags,
|
||||||
|
align: option<u32>,
|
||||||
|
virtual-address: option<u64>,
|
||||||
|
|
||||||
|
target-symbol: option<symbol-ref>,
|
||||||
|
match-percent: option<f32>,
|
||||||
|
diff-score: option<tuple<u64, u64>>,
|
||||||
|
row-count: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
record context-menu-item-copy {
|
||||||
|
value: string,
|
||||||
|
label: option<string>,
|
||||||
|
}
|
||||||
|
|
||||||
|
record context-menu-item-navigate {
|
||||||
|
label: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
variant context-menu-item {
|
||||||
|
copy(context-menu-item-copy),
|
||||||
|
navigate(context-menu-item-navigate),
|
||||||
|
}
|
||||||
|
|
||||||
|
enum hover-item-color {
|
||||||
|
normal,
|
||||||
|
emphasized,
|
||||||
|
special,
|
||||||
|
}
|
||||||
|
|
||||||
|
record hover-item {
|
||||||
|
text: string,
|
||||||
|
color: hover-item-color,
|
||||||
|
}
|
||||||
|
|
||||||
|
record diff-text-opcode {
|
||||||
|
mnemonic: string,
|
||||||
|
opcode: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
record diff-text-symbol {
|
||||||
|
name: string,
|
||||||
|
demangled-name: option<string>,
|
||||||
|
}
|
||||||
|
|
||||||
|
variant diff-text {
|
||||||
|
// Basic text (not semantically meaningful)
|
||||||
|
basic(string),
|
||||||
|
// Line number
|
||||||
|
line(u32),
|
||||||
|
// Instruction address
|
||||||
|
address(u64),
|
||||||
|
// Instruction mnemonic
|
||||||
|
opcode(diff-text-opcode),
|
||||||
|
// Instruction argument (signed)
|
||||||
|
signed(s64),
|
||||||
|
// Instruction argument (unsigned)
|
||||||
|
unsigned(u64),
|
||||||
|
// Instruction argument (opaque)
|
||||||
|
opaque(string),
|
||||||
|
// Instruction argument (branch destination)
|
||||||
|
branch-dest(u64),
|
||||||
|
// Relocation target name
|
||||||
|
symbol(diff-text-symbol),
|
||||||
|
// Relocation addend
|
||||||
|
addend(s64),
|
||||||
|
// Number of spaces
|
||||||
|
spacing(u32),
|
||||||
|
// End of line
|
||||||
|
eol,
|
||||||
|
}
|
||||||
|
|
||||||
|
record diff-text-segment {
|
||||||
|
// Text to display
|
||||||
|
text: diff-text,
|
||||||
|
// Index for colorization
|
||||||
|
diff-index: option<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
record instruction-diff-row {
|
||||||
|
// Text segments
|
||||||
|
segments: list<diff-text-segment>,
|
||||||
|
// Diff kind
|
||||||
|
diff-kind: instruction-diff-kind,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum instruction-diff-kind {
|
||||||
|
none,
|
||||||
|
op-mismatch,
|
||||||
|
arg-mismatch,
|
||||||
|
replace,
|
||||||
|
insert,
|
||||||
|
delete,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface diff {
|
||||||
|
use diff-types.{
|
||||||
|
object,
|
||||||
|
object-diff,
|
||||||
|
diff-config,
|
||||||
|
diff-result
|
||||||
|
};
|
||||||
|
use display-types.{
|
||||||
|
section-display-symbol,
|
||||||
|
section-display,
|
||||||
|
symbol-ref,
|
||||||
|
symbol-filter,
|
||||||
|
symbol-display,
|
||||||
|
context-menu-item,
|
||||||
|
hover-item,
|
||||||
|
display-config,
|
||||||
|
instruction-diff-row
|
||||||
|
};
|
||||||
|
|
||||||
|
run-diff: func(
|
||||||
|
left: option<borrow<object>>,
|
||||||
|
right: option<borrow<object>>,
|
||||||
|
config: borrow<diff-config>,
|
||||||
|
) -> result<diff-result, string>;
|
||||||
|
|
||||||
|
display-sections: func(
|
||||||
|
diff: borrow<object-diff>,
|
||||||
|
filter: symbol-filter,
|
||||||
|
config: display-config,
|
||||||
|
) -> list<section-display>;
|
||||||
|
|
||||||
|
display-symbol: func(
|
||||||
|
diff: borrow<object-diff>,
|
||||||
|
symbol: section-display-symbol,
|
||||||
|
) -> symbol-display;
|
||||||
|
|
||||||
|
symbol-context: func(
|
||||||
|
object: borrow<object>,
|
||||||
|
symbol: symbol-ref,
|
||||||
|
) -> list<context-menu-item>;
|
||||||
|
|
||||||
|
symbol-hover: func(
|
||||||
|
object: borrow<object>,
|
||||||
|
symbol: symbol-ref,
|
||||||
|
) -> list<hover-item>;
|
||||||
|
|
||||||
|
display-instruction-row: func(
|
||||||
|
diff: borrow<object-diff>,
|
||||||
|
symbol: section-display-symbol,
|
||||||
|
row-index: u32,
|
||||||
|
config: borrow<diff-config>,
|
||||||
|
) -> instruction-diff-row;
|
||||||
|
}
|
||||||
|
|
||||||
|
world api {
|
||||||
|
import logging;
|
||||||
|
use logging.{level};
|
||||||
|
|
||||||
|
export diff;
|
||||||
|
export diff-types;
|
||||||
|
export display-types;
|
||||||
|
|
||||||
|
export init: func(level: level);
|
||||||
|
export version: func() -> string;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user