WIP objdiff 3.0 refactor

This commit is contained in:
Luke Street
2025-02-20 17:48:00 -07:00
parent 6d3c63ccd8
commit f3c157ff06
79 changed files with 14886 additions and 6820 deletions
+19 -1
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+2 -1
View File
@@ -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"
+1 -1
View File
@@ -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"
+7 -8
View File
@@ -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>,
+14 -9
View File
@@ -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(&section_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;
+2
View File
@@ -1,3 +1,5 @@
#![allow(clippy::too_many_arguments)]
mod argp_version;
mod cmd;
mod util;
+70 -72
View File
@@ -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
View File
@@ -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"
+3 -2
View File
@@ -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) {
+9 -1
View 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"
]
},
{
+7 -12
View File
@@ -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.
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+218 -180
View File
@@ -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
View File
@@ -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 }
}
File diff suppressed because it is too large Load Diff
+118 -53
View File
@@ -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
View File
@@ -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