mirror of
https://github.com/encounter/objdiff.git
synced 2026-03-30 11:32:16 -07:00
WIP objdiff 3.0 refactor
This commit is contained in:
@@ -71,7 +71,6 @@ jobs:
|
||||
|
||||
test:
|
||||
name: Test
|
||||
if: 'false' # No tests yet
|
||||
strategy:
|
||||
matrix:
|
||||
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
|
||||
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:
|
||||
name: Release
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
|
||||
Generated
+717
-115
File diff suppressed because it is too large
Load Diff
+2
-1
@@ -3,6 +3,7 @@ members = [
|
||||
"objdiff-cli",
|
||||
"objdiff-core",
|
||||
"objdiff-gui",
|
||||
"objdiff-wasm",
|
||||
]
|
||||
resolver = "2"
|
||||
|
||||
@@ -13,7 +14,7 @@ strip = "debuginfo"
|
||||
codegen-units = 1
|
||||
|
||||
[workspace.package]
|
||||
version = "2.7.1"
|
||||
version = "3.0.0-alpha.1"
|
||||
authors = ["Luke Street <luke@street.dev>"]
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
@@ -18,7 +18,7 @@ argp = "0.4"
|
||||
crossterm = "0.28"
|
||||
enable-ansi-support = "0.2"
|
||||
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"
|
||||
ratatui = "0.29"
|
||||
rayon = "1.10"
|
||||
|
||||
@@ -30,16 +30,15 @@ use objdiff_core::{
|
||||
path::{check_path_buf, platform_path, platform_path_serde_option},
|
||||
ProjectConfig, ProjectObject, ProjectObjectMetadata,
|
||||
},
|
||||
diff,
|
||||
diff::{
|
||||
ConfigEnum, ConfigPropertyId, ConfigPropertyKind, DiffObjConfig, MappingConfig, ObjDiff,
|
||||
self, ConfigEnum, ConfigPropertyId, ConfigPropertyKind, DiffObjConfig, MappingConfig,
|
||||
ObjectDiff,
|
||||
},
|
||||
jobs::{
|
||||
objdiff::{start_build, ObjDiffConfig},
|
||||
Job, JobQueue, JobResult,
|
||||
},
|
||||
obj,
|
||||
obj::ObjInfo,
|
||||
obj::{self, Object},
|
||||
};
|
||||
use ratatui::prelude::*;
|
||||
use typed_path::{Utf8PlatformPath, Utf8PlatformPathBuf};
|
||||
@@ -239,7 +238,7 @@ fn run_oneshot(
|
||||
})
|
||||
.transpose()?;
|
||||
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 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)?;
|
||||
@@ -253,9 +252,9 @@ pub struct AppState {
|
||||
pub project_config: Option<ProjectConfig>,
|
||||
pub target_path: Option<Utf8PlatformPathBuf>,
|
||||
pub base_path: Option<Utf8PlatformPathBuf>,
|
||||
pub left_obj: Option<(ObjInfo, ObjDiff)>,
|
||||
pub right_obj: Option<(ObjInfo, ObjDiff)>,
|
||||
pub prev_obj: Option<(ObjInfo, ObjDiff)>,
|
||||
pub left_obj: Option<(Object, ObjectDiff)>,
|
||||
pub right_obj: Option<(Object, ObjectDiff)>,
|
||||
pub prev_obj: Option<(Object, ObjectDiff)>,
|
||||
pub reload_time: Option<time::OffsetDateTime>,
|
||||
pub time_format: Vec<time::format_description::FormatItem<'static>>,
|
||||
pub watcher: Option<Watcher>,
|
||||
|
||||
@@ -10,7 +10,7 @@ use objdiff_core::{
|
||||
},
|
||||
config::path::platform_path,
|
||||
diff, obj,
|
||||
obj::{ObjSectionKind, ObjSymbolFlags},
|
||||
obj::{SectionKind, SymbolFlag},
|
||||
};
|
||||
use prost::Message;
|
||||
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
|
||||
@@ -181,7 +181,7 @@ fn report_object(
|
||||
})
|
||||
.transpose()?;
|
||||
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 {
|
||||
complete: object.metadata.complete,
|
||||
@@ -200,7 +200,9 @@ fn report_object(
|
||||
|
||||
let obj = target.as_ref().or(base.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(|| {
|
||||
// Support cases where we don't have a target object,
|
||||
// assume complete means 100% match
|
||||
@@ -221,23 +223,26 @@ fn report_object(
|
||||
});
|
||||
|
||||
match section.kind {
|
||||
ObjSectionKind::Data | ObjSectionKind::Bss => {
|
||||
SectionKind::Data | SectionKind::Bss => {
|
||||
measures.total_data += section.size;
|
||||
if section_match_percent == 100.0 {
|
||||
measures.matched_data += section.size;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
ObjSectionKind::Code => (),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
for (symbol, symbol_diff) in section.symbols.iter().zip(§ion_diff.symbols) {
|
||||
if symbol.size == 0 || symbol.flags.0.contains(ObjSymbolFlags::Hidden) {
|
||||
for (symbol, symbol_diff) in obj.symbols.iter().zip(&obj_diff.symbols) {
|
||||
if symbol.section != Some(section_idx)
|
||||
|| symbol.size == 0
|
||||
|| symbol.flags.contains(SymbolFlag::Hidden)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if let Some(existing_functions) = &mut existing_functions {
|
||||
if (symbol.flags.0.contains(ObjSymbolFlags::Global)
|
||||
|| symbol.flags.0.contains(ObjSymbolFlags::Weak))
|
||||
if (symbol.flags.contains(SymbolFlag::Global)
|
||||
|| symbol.flags.contains(SymbolFlag::Weak))
|
||||
&& !existing_functions.insert(symbol.name.clone())
|
||||
{
|
||||
continue;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![allow(clippy::too_many_arguments)]
|
||||
|
||||
mod argp_version;
|
||||
mod cmd;
|
||||
mod util;
|
||||
|
||||
@@ -4,10 +4,10 @@ use anyhow::{bail, Result};
|
||||
use crossterm::event::{Event, KeyCode, KeyEventKind, KeyModifiers, MouseButton, MouseEventKind};
|
||||
use objdiff_core::{
|
||||
diff::{
|
||||
display::{display_diff, DiffText, HighlightKind},
|
||||
FunctionRelocDiffs, ObjDiff, ObjInsDiffKind, ObjSymbolDiff,
|
||||
display::{display_row, DiffText, HighlightKind},
|
||||
DiffObjConfig, FunctionRelocDiffs, InstructionDiffKind, ObjectDiff, SymbolDiff,
|
||||
},
|
||||
obj::{ObjInfo, ObjSectionKind, ObjSymbol, SymbolRef},
|
||||
obj::Object,
|
||||
};
|
||||
use ratatui::{
|
||||
prelude::*,
|
||||
@@ -30,9 +30,9 @@ pub struct FunctionDiffUi {
|
||||
pub scroll_state_y: ScrollbarState,
|
||||
pub per_page: usize,
|
||||
pub num_rows: usize,
|
||||
pub left_sym: Option<SymbolRef>,
|
||||
pub right_sym: Option<SymbolRef>,
|
||||
pub prev_sym: Option<SymbolRef>,
|
||||
pub left_sym: Option<usize>,
|
||||
pub right_sym: Option<usize>,
|
||||
pub prev_sym: Option<usize>,
|
||||
pub open_options: bool,
|
||||
pub three_way: bool,
|
||||
}
|
||||
@@ -82,8 +82,8 @@ impl UiView for FunctionDiffUi {
|
||||
f.render_widget(line_l, header_chunks[0]);
|
||||
|
||||
let mut line_r = Line::default();
|
||||
if let Some(percent) =
|
||||
get_symbol(state.right_obj.as_ref(), self.right_sym).and_then(|(_, d)| d.match_percent)
|
||||
if let Some(percent) = get_symbol(state.right_obj.as_ref(), self.right_sym)
|
||||
.and_then(|(_, _, d)| d.match_percent)
|
||||
{
|
||||
line_r.spans.push(Span::styled(
|
||||
format!("{:.2}% ", percent),
|
||||
@@ -108,13 +108,17 @@ impl UiView for FunctionDiffUi {
|
||||
let mut left_text = None;
|
||||
let mut left_highlight = None;
|
||||
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 rect = content_chunks[0].inner(Margin::new(0, 1));
|
||||
left_highlight = self.print_sym(
|
||||
&mut text,
|
||||
symbol,
|
||||
obj,
|
||||
symbol_idx,
|
||||
symbol_diff,
|
||||
&state.diff_obj_config,
|
||||
rect,
|
||||
&self.left_highlight,
|
||||
result,
|
||||
@@ -127,13 +131,17 @@ impl UiView for FunctionDiffUi {
|
||||
let mut right_text = None;
|
||||
let mut right_highlight = 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 rect = content_chunks[2].inner(Margin::new(0, 1));
|
||||
right_highlight = self.print_sym(
|
||||
&mut text,
|
||||
symbol,
|
||||
obj,
|
||||
symbol_idx,
|
||||
symbol_diff,
|
||||
&state.diff_obj_config,
|
||||
rect,
|
||||
&self.right_highlight,
|
||||
result,
|
||||
@@ -152,14 +160,17 @@ impl UiView for FunctionDiffUi {
|
||||
let mut prev_text = None;
|
||||
let mut prev_margin_text = None;
|
||||
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 rect = content_chunks[4].inner(Margin::new(0, 1));
|
||||
self.print_sym(
|
||||
&mut text,
|
||||
symbol,
|
||||
obj,
|
||||
symbol_idx,
|
||||
symbol_diff,
|
||||
&state.diff_obj_config,
|
||||
rect,
|
||||
&self.right_highlight,
|
||||
result,
|
||||
@@ -437,9 +448,11 @@ impl UiView for FunctionDiffUi {
|
||||
get_symbol(state.left_obj.as_ref(), left_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, ld)), None) => ld.instructions.len(),
|
||||
(None, Some((_r, rd))) => rd.instructions.len(),
|
||||
(Some((_l, _ls, ld)), Some((_r, _rs, rd))) => {
|
||||
ld.instruction_rows.len().max(rd.instruction_rows.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),
|
||||
};
|
||||
self.left_sym = left_sym;
|
||||
@@ -482,52 +495,51 @@ impl FunctionDiffUi {
|
||||
self.scroll_y += self.per_page / if half { 2 } else { 1 };
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn print_sym(
|
||||
&self,
|
||||
out: &mut Text<'static>,
|
||||
symbol: &ObjSymbol,
|
||||
symbol_diff: &ObjSymbolDiff,
|
||||
obj: &Object,
|
||||
symbol_index: usize,
|
||||
symbol_diff: &SymbolDiff,
|
||||
diff_config: &DiffObjConfig,
|
||||
rect: Rect,
|
||||
highlight: &HighlightKind,
|
||||
result: &EventResult,
|
||||
only_changed: bool,
|
||||
) -> Option<HighlightKind> {
|
||||
let base_addr = symbol.address;
|
||||
let mut new_highlight = None;
|
||||
for (y, ins_diff) in symbol_diff
|
||||
.instructions
|
||||
for (y, ins_row) in symbol_diff
|
||||
.instruction_rows
|
||||
.iter()
|
||||
.skip(self.scroll_y)
|
||||
.take(rect.height as usize)
|
||||
.enumerate()
|
||||
{
|
||||
if only_changed && ins_diff.kind == ObjInsDiffKind::None {
|
||||
if only_changed && ins_row.kind == InstructionDiffKind::None {
|
||||
out.lines.push(Line::default());
|
||||
continue;
|
||||
}
|
||||
let mut sx = rect.x;
|
||||
let sy = rect.y + y as u16;
|
||||
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 mut base_color = match ins_diff.kind {
|
||||
ObjInsDiffKind::None
|
||||
| ObjInsDiffKind::OpMismatch
|
||||
| ObjInsDiffKind::ArgMismatch => Color::Gray,
|
||||
ObjInsDiffKind::Replace => Color::Cyan,
|
||||
ObjInsDiffKind::Delete => Color::Red,
|
||||
ObjInsDiffKind::Insert => Color::Green,
|
||||
let mut base_color = match ins_row.kind {
|
||||
InstructionDiffKind::None
|
||||
| InstructionDiffKind::OpMismatch
|
||||
| InstructionDiffKind::ArgMismatch => Color::Gray,
|
||||
InstructionDiffKind::Replace => Color::Cyan,
|
||||
InstructionDiffKind::Delete => Color::Red,
|
||||
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;
|
||||
match text {
|
||||
DiffText::Basic(text) => {
|
||||
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) => {
|
||||
label_text = format!("{num} ");
|
||||
base_color = Color::DarkGray;
|
||||
@@ -539,41 +551,31 @@ impl FunctionDiffUi {
|
||||
}
|
||||
DiffText::Opcode(mnemonic, _op) => {
|
||||
label_text = mnemonic.to_string();
|
||||
if ins_diff.kind == ObjInsDiffKind::OpMismatch {
|
||||
if ins_row.kind == InstructionDiffKind::OpMismatch {
|
||||
base_color = Color::Blue;
|
||||
}
|
||||
pad_to = 8;
|
||||
}
|
||||
DiffText::Argument(arg, diff) => {
|
||||
DiffText::Argument(arg) => {
|
||||
label_text = arg.to_string();
|
||||
if let Some(diff) = diff {
|
||||
base_color = COLOR_ROTATION[diff.idx % COLOR_ROTATION.len()]
|
||||
}
|
||||
}
|
||||
DiffText::BranchDest(addr, diff) => {
|
||||
DiffText::BranchDest(addr) => {
|
||||
label_text = format!("{addr:x}");
|
||||
if let Some(diff) = diff {
|
||||
base_color = COLOR_ROTATION[diff.idx % COLOR_ROTATION.len()]
|
||||
}
|
||||
}
|
||||
DiffText::Symbol(sym, diff) => {
|
||||
DiffText::Symbol(sym) => {
|
||||
let name = sym.demangled_name.as_ref().unwrap_or(&sym.name);
|
||||
label_text = name.clone();
|
||||
if let Some(diff) = diff {
|
||||
base_color = COLOR_ROTATION[diff.idx % COLOR_ROTATION.len()]
|
||||
} else {
|
||||
if diff_idx.is_none() {
|
||||
base_color = Color::White;
|
||||
}
|
||||
}
|
||||
DiffText::Addend(addend, diff) => {
|
||||
DiffText::Addend(addend) => {
|
||||
label_text = match addend.cmp(&0i64) {
|
||||
Ordering::Greater => format!("+{:#x}", addend),
|
||||
Ordering::Less => format!("-{:#x}", -addend),
|
||||
_ => "".to_string(),
|
||||
};
|
||||
if let Some(diff) = diff {
|
||||
base_color = COLOR_ROTATION[diff.idx % COLOR_ROTATION.len()]
|
||||
} else {
|
||||
if diff_idx.is_none() {
|
||||
base_color = Color::White;
|
||||
}
|
||||
}
|
||||
@@ -612,12 +614,13 @@ impl FunctionDiffUi {
|
||||
new_highlight
|
||||
}
|
||||
|
||||
fn print_margin(&self, out: &mut Text, symbol: &ObjSymbolDiff, rect: Rect) {
|
||||
for ins_diff in symbol.instructions.iter().skip(self.scroll_y).take(rect.height as usize) {
|
||||
if ins_diff.kind != ObjInsDiffKind::None {
|
||||
out.lines.push(Line::raw(match ins_diff.kind {
|
||||
ObjInsDiffKind::Delete => "<",
|
||||
ObjInsDiffKind::Insert => ">",
|
||||
fn print_margin(&self, out: &mut Text, symbol: &SymbolDiff, rect: Rect) {
|
||||
for ins_row in symbol.instruction_rows.iter().skip(self.scroll_y).take(rect.height as usize)
|
||||
{
|
||||
if ins_row.kind != InstructionDiffKind::None {
|
||||
out.lines.push(Line::raw(match ins_row.kind {
|
||||
InstructionDiffKind::Delete => "<",
|
||||
InstructionDiffKind::Insert => ">",
|
||||
_ => "|",
|
||||
}));
|
||||
} else {
|
||||
@@ -649,23 +652,18 @@ pub fn match_percent_color(match_percent: f32) -> Color {
|
||||
|
||||
#[inline]
|
||||
fn get_symbol(
|
||||
obj: Option<&(ObjInfo, ObjDiff)>,
|
||||
sym: Option<SymbolRef>,
|
||||
) -> Option<(&ObjSymbol, &ObjSymbolDiff)> {
|
||||
obj: Option<&(Object, ObjectDiff)>,
|
||||
sym: Option<usize>,
|
||||
) -> Option<(&Object, usize, &SymbolDiff)> {
|
||||
let (obj, diff) = obj?;
|
||||
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> {
|
||||
for (section_idx, section) in obj.sections.iter().enumerate() {
|
||||
if section.kind != ObjSectionKind::Code {
|
||||
continue;
|
||||
}
|
||||
for (symbol_idx, symbol) in section.symbols.iter().enumerate() {
|
||||
if symbol.name == name {
|
||||
return Some(SymbolRef { section_idx, symbol_idx });
|
||||
}
|
||||
fn find_function(obj: &Object, name: &str) -> Option<usize> {
|
||||
for (symbol_idx, symbol) in obj.symbols.iter().enumerate() {
|
||||
if symbol.name == name {
|
||||
return Some(symbol_idx);
|
||||
}
|
||||
}
|
||||
None
|
||||
|
||||
+23
-20
@@ -12,9 +12,6 @@ A local diffing tool for decompilation projects.
|
||||
"""
|
||||
documentation = "https://docs.rs/objdiff-core"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
all = [
|
||||
@@ -25,11 +22,11 @@ all = [
|
||||
"dwarf",
|
||||
"serde",
|
||||
# Architectures
|
||||
"arm",
|
||||
"arm64",
|
||||
"mips",
|
||||
"ppc",
|
||||
"x86",
|
||||
"arm",
|
||||
"arm64",
|
||||
]
|
||||
# Implicit, used to check if any arch is enabled
|
||||
any-arch = [
|
||||
@@ -41,6 +38,7 @@ any-arch = [
|
||||
"dep:prettyplease",
|
||||
"dep:proc-macro2",
|
||||
"dep:quote",
|
||||
"dep:regex",
|
||||
"dep:similar",
|
||||
"dep:strum",
|
||||
"dep:syn",
|
||||
@@ -113,14 +111,6 @@ arm64 = [
|
||||
"dep:yaxpeax-arch",
|
||||
"dep:yaxpeax-arm",
|
||||
]
|
||||
wasm = [
|
||||
"any-arch",
|
||||
"bindings",
|
||||
"dep:log",
|
||||
"dep:talc",
|
||||
"dep:spin",
|
||||
"dep:wit-bindgen",
|
||||
]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
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" }
|
||||
strum = { version = "0.26", default-features = false, features = ["derive"], 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
|
||||
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-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
|
||||
notify = { version = "8.0.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_json = { version = "1.0" }
|
||||
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")]
|
||||
mod config_gen;
|
||||
|
||||
fn main() {
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
#[cfg(feature = "bindings")]
|
||||
compile_protos();
|
||||
#[cfg(feature = "any-arch")]
|
||||
config_gen::generate_diff_config();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "bindings")]
|
||||
@@ -18,7 +19,7 @@ fn compile_protos() {
|
||||
.map(|m| m.modified().unwrap())
|
||||
.unwrap_or(std::time::SystemTime::UNIX_EPOCH);
|
||||
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 {
|
||||
println!("cargo:rerun-if-changed={}", proto_file.display());
|
||||
let mtime = match std::fs::metadata(proto_file) {
|
||||
|
||||
@@ -39,6 +39,13 @@
|
||||
"name": "Combine data sections",
|
||||
"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",
|
||||
"type": "choice",
|
||||
@@ -213,7 +220,8 @@
|
||||
"properties": [
|
||||
"functionRelocDiffs",
|
||||
"spaceBetweenArgs",
|
||||
"combineDataSections"
|
||||
"combineDataSections",
|
||||
"combineTextSections"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -87,11 +87,11 @@ message Relocation {
|
||||
}
|
||||
|
||||
message RelocationTarget {
|
||||
Symbol symbol = 1;
|
||||
uint32 symbol_index = 1;
|
||||
int64 addend = 2;
|
||||
}
|
||||
|
||||
message InstructionDiff {
|
||||
message InstructionDiffRow {
|
||||
DiffKind diff_kind = 1;
|
||||
optional Instruction instruction = 2;
|
||||
optional InstructionBranchFrom branch_from = 3;
|
||||
@@ -122,17 +122,12 @@ message InstructionBranchTo {
|
||||
uint32 branch_index = 2;
|
||||
}
|
||||
|
||||
message SymbolRef {
|
||||
optional uint32 section_index = 1;
|
||||
uint32 symbol_index = 2;
|
||||
}
|
||||
|
||||
message SymbolDiff {
|
||||
Symbol symbol = 1;
|
||||
repeated InstructionDiff instructions = 2;
|
||||
repeated InstructionDiffRow instruction_rows = 2;
|
||||
optional float match_percent = 3;
|
||||
// The symbol ref in the _other_ object that this symbol was diffed against
|
||||
optional SymbolRef target = 5;
|
||||
// The symbol index in the _other_ object that this symbol was diffed against
|
||||
optional uint32 target_symbol = 5;
|
||||
}
|
||||
|
||||
message DataDiff {
|
||||
@@ -147,7 +142,7 @@ message SectionDiff {
|
||||
SectionKind kind = 2;
|
||||
uint64 size = 3;
|
||||
uint64 address = 4;
|
||||
repeated SymbolDiff symbols = 5;
|
||||
reserved 5;
|
||||
repeated DataDiff data = 6;
|
||||
optional float match_percent = 7;
|
||||
}
|
||||
@@ -157,11 +152,11 @@ enum SectionKind {
|
||||
SECTION_TEXT = 1;
|
||||
SECTION_DATA = 2;
|
||||
SECTION_BSS = 3;
|
||||
SECTION_COMMON = 4;
|
||||
}
|
||||
|
||||
message ObjectDiff {
|
||||
repeated SectionDiff sections = 1;
|
||||
repeated SymbolDiff symbols = 2;
|
||||
}
|
||||
|
||||
message DiffResult {
|
||||
|
||||
Binary file not shown.
+345
-216
File diff suppressed because it is too large
Load Diff
+301
-167
File diff suppressed because it is too large
Load Diff
+218
-180
@@ -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 object::{
|
||||
elf, Endian, Endianness, File, FileFlags, Object, ObjectSection, ObjectSymbol, Relocation,
|
||||
RelocationFlags, RelocationTarget,
|
||||
};
|
||||
use object::{elf, Endian as _, Object as _, ObjectSection as _, ObjectSymbol as _};
|
||||
use rabbitizer::{
|
||||
abi::Abi,
|
||||
operands::{ValuedOperand, IU16},
|
||||
registers_meta::Register,
|
||||
Instruction, InstructionDisplayFlags, InstructionFlags, IsaExtension, IsaVersion, Vram,
|
||||
IsaExtension, IsaVersion, Vram,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
arch::{ObjArch, ProcessCodeResult},
|
||||
diff::{DiffObjConfig, MipsAbi, MipsInstrCategory},
|
||||
obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection},
|
||||
arch::Arch,
|
||||
diff::{display::InstructionPart, DiffObjConfig, MipsAbi, MipsInstrCategory},
|
||||
obj::{
|
||||
InstructionArg, InstructionArgValue, InstructionRef, Relocation, RelocationFlags,
|
||||
ResolvedRelocation, ScannedInstruction,
|
||||
},
|
||||
};
|
||||
|
||||
pub struct ObjArchMips {
|
||||
pub endianness: Endianness,
|
||||
#[derive(Debug)]
|
||||
pub struct ArchMips {
|
||||
pub endianness: object::Endianness,
|
||||
pub abi: Abi,
|
||||
pub isa_extension: Option<IsaExtension>,
|
||||
pub ri_gp_value: i32,
|
||||
@@ -33,13 +35,13 @@ const EF_MIPS_MACH_5900: u32 = 0x00920000;
|
||||
|
||||
const R_MIPS15_S3: u32 = 119;
|
||||
|
||||
impl ObjArchMips {
|
||||
pub fn new(object: &File) -> Result<Self> {
|
||||
impl ArchMips {
|
||||
pub fn new(object: &object::File) -> Result<Self> {
|
||||
let mut abi = Abi::O32;
|
||||
let mut isa_extension = None;
|
||||
match object.flags() {
|
||||
FileFlags::None => {}
|
||||
FileFlags::Elf { e_flags, .. } => {
|
||||
object::FileFlags::None => {}
|
||||
object::FileFlags::Elf { e_flags, .. } => {
|
||||
abi = match e_flags & EF_MIPS_ABI {
|
||||
elf::EF_MIPS_ABI_O32 | elf::EF_MIPS_ABI_O64 => Abi::O32,
|
||||
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 })
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjArch for ObjArchMips {
|
||||
fn process_code(
|
||||
&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 {
|
||||
fn instruction_flags(&self, diff_config: &DiffObjConfig) -> rabbitizer::InstructionFlags {
|
||||
let isa_extension = match diff_config.mips_instr_category {
|
||||
MipsInstrCategory::Auto => self.isa_extension,
|
||||
MipsInstrCategory::Cpu => None,
|
||||
MipsInstrCategory::Rsp => Some(IsaExtension::RSP),
|
||||
@@ -93,158 +85,105 @@ impl ObjArch for ObjArchMips {
|
||||
MipsInstrCategory::R4000allegrex => Some(IsaExtension::R4000ALLEGREX),
|
||||
MipsInstrCategory::R5900 => Some(IsaExtension::R5900),
|
||||
};
|
||||
let instruction_flags = match isa_extension {
|
||||
Some(extension) => InstructionFlags::new_extension(extension),
|
||||
None => InstructionFlags::new_isa(IsaVersion::MIPS_III, None),
|
||||
match isa_extension {
|
||||
Some(extension) => rabbitizer::InstructionFlags::new_extension(extension),
|
||||
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::O32 => Abi::O32,
|
||||
MipsAbi::N32 => Abi::N32,
|
||||
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 end_address = address + code.len() as u64;
|
||||
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 ops = Vec::<ScannedInstruction>::with_capacity(code.len() / 4);
|
||||
let mut cur_addr = start_address as u32;
|
||||
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 instruction = Instruction::new(code, Vram::new(cur_addr), instruction_flags);
|
||||
|
||||
let formatted = instruction.display(&display_flags, None::<&str>, 0).to_string();
|
||||
let op = instruction.opcode() as u16;
|
||||
ops.push(op);
|
||||
|
||||
let mnemonic = instruction.opcode().name();
|
||||
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(),
|
||||
let vram = Vram::new(cur_addr);
|
||||
let instruction = rabbitizer::Instruction::new(code, vram, instruction_flags);
|
||||
let opcode = instruction.opcode() as u16;
|
||||
let branch_dest =
|
||||
instruction.get_branch_offset_generic().map(|o| (vram + o).inner() as u64);
|
||||
ops.push(ScannedInstruction {
|
||||
ins_ref: InstructionRef { address, size: 4, opcode },
|
||||
branch_dest,
|
||||
line,
|
||||
formatted,
|
||||
orig: None,
|
||||
});
|
||||
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(
|
||||
&self,
|
||||
file: &File<'_>,
|
||||
section: &ObjSection,
|
||||
file: &object::File<'_>,
|
||||
section: &object::Section,
|
||||
address: u64,
|
||||
reloc: &Relocation,
|
||||
reloc: &object::Relocation,
|
||||
flags: RelocationFlags,
|
||||
) -> Result<i64> {
|
||||
let data = section.data[address as usize..address as usize + 4].try_into()?;
|
||||
let addend = self.endianness.read_u32_bytes(data);
|
||||
Ok(match reloc.flags() {
|
||||
RelocationFlags::Elf { r_type: elf::R_MIPS_32 } => addend as i64,
|
||||
RelocationFlags::Elf { r_type: elf::R_MIPS_26 } => ((addend & 0x03FFFFFF) << 2) as i64,
|
||||
RelocationFlags::Elf { r_type: elf::R_MIPS_HI16 } => {
|
||||
((addend & 0x0000FFFF) << 16) as i32 as i64
|
||||
let data = section.data()?;
|
||||
let code = data[address as usize..address as usize + 4].try_into()?;
|
||||
let addend = self.endianness.read_u32_bytes(code);
|
||||
Ok(match flags {
|
||||
RelocationFlags::Elf(elf::R_MIPS_32) => addend as i64,
|
||||
RelocationFlags::Elf(elf::R_MIPS_26) => ((addend & 0x03FFFFFF) << 2) 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 {
|
||||
r_type: elf::R_MIPS_LO16 | elf::R_MIPS_GOT16 | elf::R_MIPS_CALL16,
|
||||
} => (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 {
|
||||
RelocationFlags::Elf(elf::R_MIPS_GPREL16 | elf::R_MIPS_LITERAL) => {
|
||||
let object::RelocationTarget::Symbol(idx) = reloc.target() else {
|
||||
bail!("Unsupported R_MIPS_GPREL16 relocation against a non-symbol");
|
||||
};
|
||||
let sym = file.symbol_by_index(idx)?;
|
||||
@@ -257,15 +196,15 @@ impl ObjArch for ObjArchMips {
|
||||
(addend & 0x0000FFFF) as i16 as i64
|
||||
}
|
||||
}
|
||||
RelocationFlags::Elf { r_type: elf::R_MIPS_PC16 } => 0, // PC-relative relocation
|
||||
RelocationFlags::Elf { r_type: R_MIPS15_S3 } => ((addend & 0x001FFFC0) >> 3) as i64,
|
||||
RelocationFlags::Elf(elf::R_MIPS_PC16) => 0, // PC-relative relocation
|
||||
RelocationFlags::Elf(R_MIPS15_S3) => ((addend & 0x001FFFC0) >> 3) as i64,
|
||||
flags => bail!("Unsupported MIPS implicit relocation {flags:?}"),
|
||||
})
|
||||
}
|
||||
|
||||
fn display_reloc(&self, flags: RelocationFlags) -> Cow<'static, str> {
|
||||
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_26 => Cow::Borrowed("R_MIPS_26"),
|
||||
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 {
|
||||
match flags {
|
||||
RelocationFlags::Elf { r_type } => match r_type {
|
||||
RelocationFlags::Elf(r_type) => match r_type {
|
||||
elf::R_MIPS_16 => 2,
|
||||
elf::R_MIPS_32 => 4,
|
||||
_ => 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 {
|
||||
RelocationFlags::Elf { r_type } => match r_type {
|
||||
RelocationFlags::Elf(r_type) => match r_type {
|
||||
elf::R_MIPS_HI16 => {
|
||||
args.push(ObjInsArg::PlainText("%hi(".into()));
|
||||
args.push(ObjInsArg::Reloc);
|
||||
args.push(ObjInsArg::PlainText(")".into()));
|
||||
arg_cb(InstructionPart::Basic("%hi("))?;
|
||||
arg_cb(InstructionPart::Arg(InstructionArg::Reloc))?;
|
||||
arg_cb(InstructionPart::Basic(")"))?;
|
||||
}
|
||||
elf::R_MIPS_LO16 => {
|
||||
args.push(ObjInsArg::PlainText("%lo(".into()));
|
||||
args.push(ObjInsArg::Reloc);
|
||||
args.push(ObjInsArg::PlainText(")".into()));
|
||||
arg_cb(InstructionPart::Basic("%lo("))?;
|
||||
arg_cb(InstructionPart::Arg(InstructionArg::Reloc))?;
|
||||
arg_cb(InstructionPart::Basic(")"))?;
|
||||
}
|
||||
elf::R_MIPS_GOT16 => {
|
||||
args.push(ObjInsArg::PlainText("%got(".into()));
|
||||
args.push(ObjInsArg::Reloc);
|
||||
args.push(ObjInsArg::PlainText(")".into()));
|
||||
arg_cb(InstructionPart::Basic("%got("))?;
|
||||
arg_cb(InstructionPart::Arg(InstructionArg::Reloc))?;
|
||||
arg_cb(InstructionPart::Basic(")"))?;
|
||||
}
|
||||
elf::R_MIPS_CALL16 => {
|
||||
args.push(ObjInsArg::PlainText("%call16(".into()));
|
||||
args.push(ObjInsArg::Reloc);
|
||||
args.push(ObjInsArg::PlainText(")".into()));
|
||||
arg_cb(InstructionPart::Basic("%call16("))?;
|
||||
arg_cb(InstructionPart::Arg(InstructionArg::Reloc))?;
|
||||
arg_cb(InstructionPart::Basic(")"))?;
|
||||
}
|
||||
elf::R_MIPS_GPREL16 => {
|
||||
args.push(ObjInsArg::PlainText("%gp_rel(".into()));
|
||||
args.push(ObjInsArg::Reloc);
|
||||
args.push(ObjInsArg::PlainText(")".into()));
|
||||
arg_cb(InstructionPart::Basic("%gp_rel("))?;
|
||||
arg_cb(InstructionPart::Arg(InstructionArg::Reloc))?;
|
||||
arg_cb(InstructionPart::Basic(")"))?;
|
||||
}
|
||||
elf::R_MIPS_32
|
||||
| elf::R_MIPS_26
|
||||
| elf::R_MIPS_LITERAL
|
||||
| elf::R_MIPS_PC16
|
||||
| R_MIPS15_S3 => {
|
||||
args.push(ObjInsArg::Reloc);
|
||||
arg_cb(InstructionPart::Arg(InstructionArg::Reloc))?;
|
||||
}
|
||||
_ => bail!("Unsupported ELF MIPS relocation type {r_type}"),
|
||||
},
|
||||
|
||||
+179
-57
@@ -1,13 +1,16 @@
|
||||
use alloc::{borrow::Cow, boxed::Box, collections::BTreeMap, format, string::String, vec::Vec};
|
||||
use core::ffi::CStr;
|
||||
use alloc::{borrow::Cow, boxed::Box, format, string::String, vec, vec::Vec};
|
||||
use core::{ffi::CStr, fmt, fmt::Debug, ops::Range};
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use byteorder::ByteOrder;
|
||||
use object::{Architecture, File, Object, ObjectSymbol, Relocation, RelocationFlags, Symbol};
|
||||
use object::{File, Relocation, Section};
|
||||
|
||||
use crate::{
|
||||
diff::DiffObjConfig,
|
||||
obj::{ObjIns, ObjReloc, ObjSection},
|
||||
diff::{display::InstructionPart, DiffObjConfig},
|
||||
obj::{
|
||||
InstructionRef, ParsedInstruction, RelocationFlags, ResolvedRelocation, ScannedInstruction,
|
||||
SymbolFlagSet, SymbolKind,
|
||||
},
|
||||
util::ReallySigned,
|
||||
};
|
||||
|
||||
@@ -35,8 +38,8 @@ pub enum DataType {
|
||||
String,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for DataType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
impl fmt::Display for DataType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
DataType::Int8 => write!(f, "Int8"),
|
||||
DataType::Int16 => write!(f, "Int16"),
|
||||
@@ -154,23 +157,76 @@ impl DataType {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ObjArch: Send + Sync {
|
||||
fn process_code(
|
||||
pub trait Arch: Send + Sync + Debug {
|
||||
/// 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,
|
||||
address: u64,
|
||||
code: &[u8],
|
||||
section_index: usize,
|
||||
relocations: &[ObjReloc],
|
||||
line_info: &BTreeMap<u64, u32>,
|
||||
config: &DiffObjConfig,
|
||||
) -> Result<ProcessCodeResult>;
|
||||
diff_config: &DiffObjConfig,
|
||||
) -> Result<Vec<ScannedInstruction>>;
|
||||
|
||||
/// 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(
|
||||
&self,
|
||||
file: &File<'_>,
|
||||
section: &ObjSection,
|
||||
file: &object::File<'_>,
|
||||
section: &object::Section,
|
||||
address: u64,
|
||||
reloc: &Relocation,
|
||||
relocation: &object::Relocation,
|
||||
flags: RelocationFlags,
|
||||
) -> Result<i64>;
|
||||
|
||||
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 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> {
|
||||
vec![format!("Bytes: {:#x?}", bytes)]
|
||||
@@ -191,58 +258,113 @@ pub trait ObjArch: Send + Sync {
|
||||
vec![format!("{:#?}", bytes)]
|
||||
}
|
||||
|
||||
fn display_ins_data_labels(&self, ins: &ObjIns) -> Vec<String> {
|
||||
let Some(reloc) = ins.reloc.as_ref() else {
|
||||
return Vec::new();
|
||||
};
|
||||
if reloc.addend >= 0 && reloc.target.bytes.len() > reloc.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();
|
||||
}
|
||||
fn display_ins_data_labels(
|
||||
&self,
|
||||
_ins_ref: InstructionRef,
|
||||
_code: &[u8],
|
||||
_relocation: Option<ResolvedRelocation>,
|
||||
) -> Vec<String> {
|
||||
// TODO
|
||||
// let Some(reloc) = relocation else {
|
||||
// return Vec::new();
|
||||
// };
|
||||
// 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()
|
||||
}
|
||||
|
||||
fn display_ins_data_literals(&self, ins: &ObjIns) -> Vec<String> {
|
||||
let Some(reloc) = ins.reloc.as_ref() else {
|
||||
return Vec::new();
|
||||
};
|
||||
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();
|
||||
}
|
||||
fn display_ins_data_literals(
|
||||
&self,
|
||||
_ins_ref: InstructionRef,
|
||||
_code: &[u8],
|
||||
_relocation: Option<ResolvedRelocation>,
|
||||
) -> Vec<String> {
|
||||
// TODO
|
||||
// let Some(reloc) = ins.reloc.as_ref() else {
|
||||
// return Vec::new();
|
||||
// };
|
||||
// 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()
|
||||
}
|
||||
|
||||
// Downcast methods
|
||||
#[cfg(feature = "ppc")]
|
||||
fn ppc(&self) -> Option<&ppc::ObjArchPpc> { None }
|
||||
}
|
||||
|
||||
pub struct ProcessCodeResult {
|
||||
pub ops: Vec<u16>,
|
||||
pub insts: Vec<ObjIns>,
|
||||
}
|
||||
|
||||
pub fn new_arch(object: &File) -> Result<Box<dyn ObjArch>> {
|
||||
pub fn new_arch(object: &object::File) -> Result<Box<dyn Arch>> {
|
||||
use object::Object as _;
|
||||
Ok(match object.architecture() {
|
||||
#[cfg(feature = "ppc")]
|
||||
Architecture::PowerPc => Box::new(ppc::ObjArchPpc::new(object)?),
|
||||
object::Architecture::PowerPc => Box::new(ppc::ArchPpc::new(object)?),
|
||||
#[cfg(feature = "mips")]
|
||||
Architecture::Mips => Box::new(mips::ObjArchMips::new(object)?),
|
||||
object::Architecture::Mips => Box::new(mips::ArchMips::new(object)?),
|
||||
#[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")]
|
||||
Architecture::Arm => Box::new(arm::ObjArchArm::new(object)?),
|
||||
object::Architecture::Arm => Box::new(arm::ArchArm::new(object)?),
|
||||
#[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:?}"),
|
||||
})
|
||||
}
|
||||
|
||||
#[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 }
|
||||
}
|
||||
|
||||
+465
-426
File diff suppressed because it is too large
Load Diff
+118
-53
@@ -7,33 +7,93 @@ use alloc::{
|
||||
vec,
|
||||
vec::Vec,
|
||||
};
|
||||
use std::ops::Range;
|
||||
|
||||
use anyhow::{anyhow, bail, ensure, Result};
|
||||
use iced_x86::{
|
||||
Decoder, DecoderOptions, DecoratorKind, Formatter, FormatterOutput, FormatterTextKind,
|
||||
GasFormatter, Instruction, IntelFormatter, MasmFormatter, NasmFormatter, NumberKind, OpKind,
|
||||
PrefixKind, Register,
|
||||
Decoder, DecoderOptions, DecoratorKind, FormatterOutput, FormatterTextKind, GasFormatter,
|
||||
Instruction, IntelFormatter, MasmFormatter, NasmFormatter, NumberKind, OpKind, PrefixKind,
|
||||
Register,
|
||||
};
|
||||
use object::{pe, Endian, Endianness, File, Object, Relocation, RelocationFlags};
|
||||
use object::{pe, Endian as _, Object as _, ObjectSection as _};
|
||||
|
||||
use crate::{
|
||||
arch::{ObjArch, ProcessCodeResult},
|
||||
diff::{DiffObjConfig, X86Formatter},
|
||||
obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection},
|
||||
arch::Arch,
|
||||
diff::{display::InstructionPart, DiffObjConfig, X86Formatter},
|
||||
obj::{
|
||||
InstructionArg, InstructionArgValue, InstructionRef, ParsedInstruction, RelocationFlags,
|
||||
ResolvedRelocation, ScannedInstruction,
|
||||
},
|
||||
};
|
||||
|
||||
pub struct ObjArchX86 {
|
||||
#[derive(Debug)]
|
||||
pub struct ArchX86 {
|
||||
bits: u32,
|
||||
endianness: Endianness,
|
||||
endianness: object::Endianness,
|
||||
}
|
||||
|
||||
impl ObjArchX86 {
|
||||
pub fn new(object: &File) -> Result<Self> {
|
||||
impl ArchX86 {
|
||||
pub fn new(object: &object::File) -> Result<Self> {
|
||||
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 ObjArch for ObjArchX86 {
|
||||
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!()
|
||||
}
|
||||
|
||||
fn process_code(
|
||||
&self,
|
||||
address: u64,
|
||||
@@ -45,13 +105,7 @@ impl ObjArch for ObjArchX86 {
|
||||
) -> Result<ProcessCodeResult> {
|
||||
let mut result = ProcessCodeResult { ops: Vec::new(), insts: Vec::new() };
|
||||
let mut decoder = Decoder::with_ip(self.bits, code, address, DecoderOptions::NONE);
|
||||
let mut formatter: Box<dyn Formatter> = match 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(config.space_between_args);
|
||||
let mut formatter = self.formatter(config);
|
||||
|
||||
let mut output = InstructionFormatterOutput {
|
||||
formatted: String::new(),
|
||||
@@ -101,10 +155,12 @@ impl ObjArch for ObjArchX86 {
|
||||
output.ins.formatted.clone_from(&output.formatted);
|
||||
|
||||
// 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(
|
||||
OpKind::Memory,
|
||||
ObjInsArg::Reloc,
|
||||
InstructionArg::Reloc,
|
||||
&mut output.ins.args,
|
||||
&instruction,
|
||||
&output.ins_operands,
|
||||
@@ -112,7 +168,7 @@ impl ObjArch for ObjArchX86 {
|
||||
if !found {
|
||||
found = replace_arg(
|
||||
OpKind::Immediate32,
|
||||
ObjInsArg::Reloc,
|
||||
InstructionArg::Reloc,
|
||||
&mut output.ins.args,
|
||||
&instruction,
|
||||
&output.ins_operands,
|
||||
@@ -120,7 +176,9 @@ impl ObjArch for ObjArchX86 {
|
||||
}
|
||||
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");
|
||||
}
|
||||
|
||||
@@ -136,14 +194,15 @@ impl ObjArch for ObjArchX86 {
|
||||
|
||||
fn implcit_addend(
|
||||
&self,
|
||||
_file: &File<'_>,
|
||||
section: &ObjSection,
|
||||
_file: &object::File<'_>,
|
||||
section: &object::Section,
|
||||
address: u64,
|
||||
reloc: &Relocation,
|
||||
_relocation: &object::Relocation,
|
||||
flags: RelocationFlags,
|
||||
) -> Result<i64> {
|
||||
match reloc.flags() {
|
||||
RelocationFlags::Coff { typ: pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32 } => {
|
||||
let data = section.data[address as usize..address as usize + 4].try_into()?;
|
||||
match flags {
|
||||
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()?;
|
||||
Ok(self.endianness.read_i32_bytes(data) as i64)
|
||||
}
|
||||
flags => bail!("Unsupported x86 implicit relocation {flags:?}"),
|
||||
@@ -162,7 +221,7 @@ impl ObjArch for ObjArchX86 {
|
||||
|
||||
fn display_reloc(&self, flags: RelocationFlags) -> Cow<'static, str> {
|
||||
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_REL32 => Cow::Borrowed("IMAGE_REL_I386_REL32"),
|
||||
_ => Cow::Owned(format!("<{flags:?}>")),
|
||||
@@ -173,7 +232,7 @@ impl ObjArch for ObjArchX86 {
|
||||
|
||||
fn get_reloc_byte_size(&self, flags: RelocationFlags) -> usize {
|
||||
match flags {
|
||||
RelocationFlags::Coff { typ } => match typ {
|
||||
RelocationFlags::Coff(typ) => match typ {
|
||||
pe::IMAGE_REL_I386_DIR16 => 2,
|
||||
pe::IMAGE_REL_I386_REL16 => 2,
|
||||
pe::IMAGE_REL_I386_DIR32 => 4,
|
||||
@@ -187,8 +246,8 @@ impl ObjArch for ObjArchX86 {
|
||||
|
||||
fn replace_arg(
|
||||
from: OpKind,
|
||||
to: ObjInsArg,
|
||||
args: &mut [ObjInsArg],
|
||||
to: InstructionArg,
|
||||
args: &mut [InstructionArg],
|
||||
instruction: &Instruction,
|
||||
ins_operands: &[Option<u32>],
|
||||
) -> Result<bool> {
|
||||
@@ -213,7 +272,7 @@ fn replace_arg(
|
||||
|
||||
struct InstructionFormatterOutput {
|
||||
formatted: String,
|
||||
ins: ObjIns,
|
||||
ins: ParsedInstruction,
|
||||
error: Option<anyhow::Error>,
|
||||
ins_operands: Vec<Option<u32>>,
|
||||
}
|
||||
@@ -223,11 +282,13 @@ impl InstructionFormatterOutput {
|
||||
// The formatter writes the '-' operator and then gives us a negative value,
|
||||
// so convert it to a positive value to avoid double negatives
|
||||
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 {
|
||||
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);
|
||||
match kind {
|
||||
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 => {
|
||||
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() {
|
||||
@@ -258,12 +321,13 @@ impl FormatterOutput for InstructionFormatterOutput {
|
||||
fn write_prefix(&mut self, _instruction: &Instruction, text: &str, _prefix: PrefixKind) {
|
||||
self.formatted.push_str(text);
|
||||
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) {
|
||||
self.formatted.push_str(text);
|
||||
// TODO: can iced-x86 guarantee 'static here?
|
||||
self.ins.mnemonic = Cow::Owned(text.to_string());
|
||||
}
|
||||
|
||||
@@ -284,10 +348,11 @@ impl FormatterOutput for InstructionFormatterOutput {
|
||||
match kind {
|
||||
FormatterTextKind::LabelAddress => {
|
||||
if let Some(reloc) = self.ins.reloc.as_ref() {
|
||||
if matches!(reloc.flags, RelocationFlags::Coff {
|
||||
typ: pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32
|
||||
}) {
|
||||
self.ins.args.push(ObjInsArg::Reloc);
|
||||
if matches!(
|
||||
reloc.flags,
|
||||
RelocationFlags::Coff(pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32)
|
||||
) {
|
||||
self.ins.args.push(InstructionArg::Reloc);
|
||||
return;
|
||||
} else if self.error.is_none() {
|
||||
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);
|
||||
return;
|
||||
}
|
||||
FormatterTextKind::FunctionAddress => {
|
||||
if let Some(reloc) = self.ins.reloc.as_ref() {
|
||||
if matches!(reloc.flags, RelocationFlags::Coff {
|
||||
typ: pe::IMAGE_REL_I386_REL32
|
||||
}) {
|
||||
self.ins.args.push(ObjInsArg::Reloc);
|
||||
if matches!(reloc.flags, RelocationFlags::Coff(pe::IMAGE_REL_I386_REL32)) {
|
||||
self.ins.args.push(InstructionArg::Reloc);
|
||||
return;
|
||||
} else if self.error.is_none() {
|
||||
self.error = Some(anyhow!(
|
||||
@@ -332,7 +395,7 @@ impl FormatterOutput for InstructionFormatterOutput {
|
||||
self.push_signed(value as i64);
|
||||
}
|
||||
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.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(
|
||||
@@ -360,6 +423,8 @@ impl FormatterOutput for InstructionFormatterOutput {
|
||||
) {
|
||||
self.formatted.push_str(text);
|
||||
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())));
|
||||
}
|
||||
}
|
||||
|
||||
+228
-247
@@ -1,18 +1,6 @@
|
||||
#![allow(clippy::needless_lifetimes)] // Generated serde code
|
||||
|
||||
use alloc::string::ToString;
|
||||
|
||||
use crate::{
|
||||
diff::{
|
||||
ObjDataDiff, ObjDataDiffKind, ObjDiff, ObjInsArgDiff, ObjInsBranchFrom, ObjInsBranchTo,
|
||||
ObjInsDiff, ObjInsDiffKind, ObjSectionDiff, ObjSymbolDiff,
|
||||
},
|
||||
obj,
|
||||
obj::{
|
||||
ObjInfo, ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSectionKind, ObjSymbol,
|
||||
ObjSymbolFlagSet, ObjSymbolFlags,
|
||||
},
|
||||
};
|
||||
use crate::{diff, obj};
|
||||
|
||||
// Protobuf diff types
|
||||
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"));
|
||||
|
||||
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 {
|
||||
left: left.map(|(obj, diff)| ObjectDiff::new(obj, diff)),
|
||||
right: right.map(|(obj, diff)| ObjectDiff::new(obj, diff)),
|
||||
// TODO
|
||||
// left: left.map(|(obj, diff)| ObjectDiff::new(obj, diff)),
|
||||
// right: right.map(|(obj, diff)| ObjectDiff::new(obj, diff)),
|
||||
left: None,
|
||||
right: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectDiff {
|
||||
pub fn new(obj: &ObjInfo, diff: &ObjDiff) -> Self {
|
||||
Self {
|
||||
sections: diff
|
||||
.sections
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, d)| SectionDiff::new(obj, i, d))
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SectionDiff {
|
||||
pub fn new(obj: &ObjInfo, section_index: usize, section_diff: &ObjSectionDiff) -> Self {
|
||||
let section = &obj.sections[section_index];
|
||||
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();
|
||||
// TODO: section_diff.reloc_diff
|
||||
Self {
|
||||
name: section.name.to_string(),
|
||||
kind: SectionKind::from(section.kind) as i32,
|
||||
size: section.size,
|
||||
address: section.address,
|
||||
symbols,
|
||||
data,
|
||||
match_percent: section_diff.match_percent,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ObjSectionKind> for SectionKind {
|
||||
fn from(value: ObjSectionKind) -> Self {
|
||||
match value {
|
||||
ObjSectionKind::Code => SectionKind::SectionText,
|
||||
ObjSectionKind::Data => SectionKind::SectionData,
|
||||
ObjSectionKind::Bss => SectionKind::SectionBss,
|
||||
// TODO common
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<obj::SymbolRef> for SymbolRef {
|
||||
fn from(value: obj::SymbolRef) -> Self {
|
||||
Self {
|
||||
section_index: if value.section_idx == obj::SECTION_COMMON {
|
||||
None
|
||||
} else {
|
||||
Some(value.section_idx as u32)
|
||||
},
|
||||
symbol_index: value.symbol_idx as u32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
.iter()
|
||||
.map(|ins_diff| InstructionDiff::new(object, ins_diff))
|
||||
.collect();
|
||||
Self {
|
||||
symbol: Some(Symbol::new(symbol)),
|
||||
instructions,
|
||||
match_percent: symbol_diff.match_percent,
|
||||
target: symbol_diff.target_symbol.map(SymbolRef::from),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DataDiff {
|
||||
pub fn new(_object: &ObjInfo, data_diff: &ObjDataDiff) -> Self {
|
||||
Self {
|
||||
kind: DiffKind::from(data_diff.kind) as i32,
|
||||
data: data_diff.data.clone(),
|
||||
size: data_diff.len as u64,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Symbol {
|
||||
pub fn new(value: &ObjSymbol) -> Self {
|
||||
Self {
|
||||
name: value.name.to_string(),
|
||||
demangled_name: value.demangled_name.clone(),
|
||||
address: value.address,
|
||||
size: value.size,
|
||||
flags: symbol_flags(value.flags),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn symbol_flags(value: ObjSymbolFlagSet) -> u32 {
|
||||
let mut flags = 0u32;
|
||||
if value.0.contains(ObjSymbolFlags::Global) {
|
||||
flags |= SymbolFlag::SymbolGlobal as u32;
|
||||
}
|
||||
if value.0.contains(ObjSymbolFlags::Local) {
|
||||
flags |= SymbolFlag::SymbolLocal as u32;
|
||||
}
|
||||
if value.0.contains(ObjSymbolFlags::Weak) {
|
||||
flags |= SymbolFlag::SymbolWeak as u32;
|
||||
}
|
||||
if value.0.contains(ObjSymbolFlags::Common) {
|
||||
flags |= SymbolFlag::SymbolCommon as u32;
|
||||
}
|
||||
if value.0.contains(ObjSymbolFlags::Hidden) {
|
||||
flags |= SymbolFlag::SymbolHidden as u32;
|
||||
}
|
||||
flags
|
||||
}
|
||||
|
||||
impl Instruction {
|
||||
pub fn new(object: &ObjInfo, instruction: &ObjIns) -> Self {
|
||||
Self {
|
||||
address: instruction.address,
|
||||
size: instruction.size as u32,
|
||||
opcode: instruction.op as u32,
|
||||
mnemonic: instruction.mnemonic.to_string(),
|
||||
formatted: instruction.formatted.clone(),
|
||||
arguments: instruction.args.iter().map(Argument::new).collect(),
|
||||
relocation: instruction.reloc.as_ref().map(|reloc| Relocation::new(object, reloc)),
|
||||
branch_dest: instruction.branch_dest,
|
||||
line_number: instruction.line,
|
||||
original: instruction.orig.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Argument {
|
||||
pub fn new(value: &ObjInsArg) -> Self {
|
||||
Self {
|
||||
value: Some(match value {
|
||||
ObjInsArg::PlainText(s) => argument::Value::PlainText(s.to_string()),
|
||||
ObjInsArg::Arg(v) => argument::Value::Argument(ArgumentValue::new(v)),
|
||||
ObjInsArg::Reloc => argument::Value::Relocation(ArgumentRelocation {}),
|
||||
ObjInsArg::BranchDest(dest) => argument::Value::BranchDest(*dest),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ArgumentValue {
|
||||
pub fn new(value: &ObjInsArgValue) -> Self {
|
||||
Self {
|
||||
value: Some(match value {
|
||||
ObjInsArgValue::Signed(v) => argument_value::Value::Signed(*v),
|
||||
ObjInsArgValue::Unsigned(v) => argument_value::Value::Unsigned(*v),
|
||||
ObjInsArgValue::Opaque(v) => argument_value::Value::Opaque(v.to_string()),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Relocation {
|
||||
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,
|
||||
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)),
|
||||
addend: reloc.addend,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl InstructionDiff {
|
||||
pub fn new(object: &ObjInfo, instruction_diff: &ObjInsDiff) -> Self {
|
||||
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),
|
||||
branch_to: instruction_diff.branch_to.as_ref().map(InstructionBranchTo::new),
|
||||
arg_diff: instruction_diff.arg_diff.iter().map(ArgumentDiff::new).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ArgumentDiff {
|
||||
pub fn new(value: &Option<ObjInsArgDiff>) -> Self {
|
||||
Self { diff_index: value.as_ref().map(|v| v.idx as u32) }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ObjInsDiffKind> for DiffKind {
|
||||
fn from(value: ObjInsDiffKind) -> Self {
|
||||
match value {
|
||||
ObjInsDiffKind::None => DiffKind::DiffNone,
|
||||
ObjInsDiffKind::OpMismatch => DiffKind::DiffOpMismatch,
|
||||
ObjInsDiffKind::ArgMismatch => DiffKind::DiffArgMismatch,
|
||||
ObjInsDiffKind::Replace => DiffKind::DiffReplace,
|
||||
ObjInsDiffKind::Delete => DiffKind::DiffDelete,
|
||||
ObjInsDiffKind::Insert => DiffKind::DiffInsert,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ObjDataDiffKind> for DiffKind {
|
||||
fn from(value: ObjDataDiffKind) -> Self {
|
||||
match value {
|
||||
ObjDataDiffKind::None => DiffKind::DiffNone,
|
||||
ObjDataDiffKind::Replace => DiffKind::DiffReplace,
|
||||
ObjDataDiffKind::Delete => DiffKind::DiffDelete,
|
||||
ObjDataDiffKind::Insert => DiffKind::DiffInsert,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 }
|
||||
}
|
||||
}
|
||||
// impl ObjectDiff {
|
||||
// pub fn new(obj: &obj::Object, diff: &diff::ObjectDiff) -> Self {
|
||||
// Self {
|
||||
// sections: diff
|
||||
// .sections
|
||||
// .iter()
|
||||
// .enumerate()
|
||||
// .map(|(i, d)| SectionDiff::new(obj, i, d))
|
||||
// .collect(),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl SectionDiff {
|
||||
// pub fn new(obj: &obj::Object, section_index: usize, section_diff: &diff::SectionDiff) -> Self {
|
||||
// let section = &obj.sections[section_index];
|
||||
// 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();
|
||||
// // TODO: section_diff.reloc_diff
|
||||
// Self {
|
||||
// name: section.name.to_string(),
|
||||
// kind: SectionKind::from(section.kind) as i32,
|
||||
// size: section.size,
|
||||
// address: section.address,
|
||||
// symbols,
|
||||
// data,
|
||||
// match_percent: section_diff.match_percent,
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl From<obj::SectionKind> for SectionKind {
|
||||
// fn from(value: obj::SectionKind) -> Self {
|
||||
// match value {
|
||||
// obj::SectionKind::Code => SectionKind::SectionText,
|
||||
// obj::SectionKind::Data => SectionKind::SectionData,
|
||||
// obj::SectionKind::Bss => SectionKind::SectionBss,
|
||||
// // TODO common
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl SymbolDiff {
|
||||
// pub fn new(object: &obj::Object, symbol_diff: &diff::SymbolDiff) -> Self {
|
||||
// let symbol = object.symbols[symbol_diff.symbol_index];
|
||||
// let instructions = symbol_diff
|
||||
// .instruction_rows
|
||||
// .iter()
|
||||
// .map(|ins_diff| InstructionDiff::new(object, ins_diff))
|
||||
// .collect();
|
||||
// Self {
|
||||
// symbol: Some(Symbol::new(symbol)),
|
||||
// instructions,
|
||||
// match_percent: symbol_diff.match_percent,
|
||||
// target: symbol_diff.target_symbol.map(SymbolRef::from),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl DataDiff {
|
||||
// pub fn new(_object: &obj::Object, data_diff: &diff::DataDiff) -> Self {
|
||||
// Self {
|
||||
// kind: DiffKind::from(data_diff.kind) as i32,
|
||||
// data: data_diff.data.clone(),
|
||||
// size: data_diff.len as u64,
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl Symbol {
|
||||
// pub fn new(value: &ObjSymbol) -> Self {
|
||||
// Self {
|
||||
// name: value.name.to_string(),
|
||||
// demangled_name: value.demangled_name.clone(),
|
||||
// address: value.address,
|
||||
// size: value.size,
|
||||
// flags: symbol_flags(value.flags),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// fn symbol_flags(value: ObjSymbolFlagSet) -> u32 {
|
||||
// let mut flags = 0u32;
|
||||
// if value.0.contains(ObjSymbolFlags::Global) {
|
||||
// flags |= SymbolFlag::SymbolGlobal as u32;
|
||||
// }
|
||||
// if value.0.contains(ObjSymbolFlags::Local) {
|
||||
// flags |= SymbolFlag::SymbolLocal as u32;
|
||||
// }
|
||||
// if value.0.contains(ObjSymbolFlags::Weak) {
|
||||
// flags |= SymbolFlag::SymbolWeak as u32;
|
||||
// }
|
||||
// if value.0.contains(ObjSymbolFlags::Common) {
|
||||
// flags |= SymbolFlag::SymbolCommon as u32;
|
||||
// }
|
||||
// if value.0.contains(ObjSymbolFlags::Hidden) {
|
||||
// flags |= SymbolFlag::SymbolHidden as u32;
|
||||
// }
|
||||
// flags
|
||||
// }
|
||||
//
|
||||
// impl Instruction {
|
||||
// pub fn new(object: &obj::Object, instruction: &ObjIns) -> Self {
|
||||
// Self {
|
||||
// address: instruction.address,
|
||||
// size: instruction.size as u32,
|
||||
// opcode: instruction.op as u32,
|
||||
// mnemonic: instruction.mnemonic.to_string(),
|
||||
// formatted: instruction.formatted.clone(),
|
||||
// arguments: instruction.args.iter().map(Argument::new).collect(),
|
||||
// relocation: instruction.reloc.as_ref().map(|reloc| Relocation::new(object, reloc)),
|
||||
// branch_dest: instruction.branch_dest,
|
||||
// line_number: instruction.line,
|
||||
// original: instruction.orig.clone(),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl Argument {
|
||||
// pub fn new(value: &ObjInsArg) -> Self {
|
||||
// Self {
|
||||
// value: Some(match value {
|
||||
// ObjInsArg::PlainText(s) => argument::Value::PlainText(s.to_string()),
|
||||
// ObjInsArg::Arg(v) => argument::Value::Argument(ArgumentValue::new(v)),
|
||||
// ObjInsArg::Reloc => argument::Value::Relocation(ArgumentRelocation {}),
|
||||
// ObjInsArg::BranchDest(dest) => argument::Value::BranchDest(*dest),
|
||||
// }),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl ArgumentValue {
|
||||
// pub fn new(value: &ObjInsArgValue) -> Self {
|
||||
// Self {
|
||||
// value: Some(match value {
|
||||
// ObjInsArgValue::Signed(v) => argument_value::Value::Signed(*v),
|
||||
// ObjInsArgValue::Unsigned(v) => argument_value::Value::Unsigned(*v),
|
||||
// ObjInsArgValue::Opaque(v) => argument_value::Value::Opaque(v.to_string()),
|
||||
// }),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl Relocation {
|
||||
// pub fn new(object: &obj::Object, 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,
|
||||
// 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)),
|
||||
// addend: reloc.addend,
|
||||
// }),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl InstructionDiff {
|
||||
// pub fn new(object: &obj::Object, instruction_diff: &ObjInsDiff) -> Self {
|
||||
// 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),
|
||||
// branch_to: instruction_diff.branch_to.as_ref().map(InstructionBranchTo::new),
|
||||
// arg_diff: instruction_diff.arg_diff.iter().map(ArgumentDiff::new).collect(),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl ArgumentDiff {
|
||||
// pub fn new(value: &Option<ObjInsArgDiff>) -> Self {
|
||||
// Self { diff_index: value.as_ref().map(|v| v.idx as u32) }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl From<ObjInsDiffKind> for DiffKind {
|
||||
// fn from(value: ObjInsDiffKind) -> Self {
|
||||
// match value {
|
||||
// ObjInsDiffKind::None => DiffKind::DiffNone,
|
||||
// ObjInsDiffKind::OpMismatch => DiffKind::DiffOpMismatch,
|
||||
// ObjInsDiffKind::ArgMismatch => DiffKind::DiffArgMismatch,
|
||||
// ObjInsDiffKind::Replace => DiffKind::DiffReplace,
|
||||
// ObjInsDiffKind::Delete => DiffKind::DiffDelete,
|
||||
// ObjInsDiffKind::Insert => DiffKind::DiffInsert,
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl From<ObjDataDiffKind> for DiffKind {
|
||||
// fn from(value: ObjDataDiffKind) -> Self {
|
||||
// match value {
|
||||
// ObjDataDiffKind::None => DiffKind::DiffNone,
|
||||
// ObjDataDiffKind::Replace => DiffKind::DiffReplace,
|
||||
// ObjDataDiffKind::Delete => DiffKind::DiffDelete,
|
||||
// ObjDataDiffKind::Insert => DiffKind::DiffInsert,
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// 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 }
|
||||
// }
|
||||
// }
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user