From 9e57a66a05d9b3410db09f539fa3e3fd797df397 Mon Sep 17 00:00:00 2001 From: Luke Street Date: Tue, 21 May 2024 18:06:14 -0600 Subject: [PATCH] Auto-detect MIPS ABI/category & add config Under Diff Options -> Arch Settings, one can override the ABI/instruction category --- Cargo.lock | 8 ++-- objdiff-cli/Cargo.toml | 2 +- objdiff-cli/src/cmd/diff.rs | 6 ++- objdiff-core/Cargo.toml | 3 +- objdiff-core/src/arch/mips.rs | 59 ++++++++++++++++++++++---- objdiff-core/src/diff/mod.rs | 73 ++++++++++++++++++++++++++++++++- objdiff-gui/Cargo.toml | 3 +- objdiff-gui/src/app.rs | 12 +++--- objdiff-gui/src/views/config.rs | 56 ++++++++++++++++++++----- 9 files changed, 188 insertions(+), 34 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 94ddee6..2fd304a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3081,7 +3081,7 @@ dependencies = [ [[package]] name = "objdiff-cli" -version = "2.0.0-alpha.1" +version = "2.0.0-alpha.2" dependencies = [ "anyhow", "argp", @@ -3100,7 +3100,7 @@ dependencies = [ [[package]] name = "objdiff-core" -version = "2.0.0-alpha.1" +version = "2.0.0-alpha.2" dependencies = [ "anyhow", "byteorder", @@ -3123,11 +3123,12 @@ dependencies = [ "serde_json", "serde_yaml", "similar", + "strum", ] [[package]] name = "objdiff-gui" -version = "2.0.0-alpha.1" +version = "2.0.0-alpha.2" dependencies = [ "anyhow", "bytes", @@ -3157,6 +3158,7 @@ dependencies = [ "serde", "serde_json", "shell-escape", + "strum", "tempfile", "time", "tracing-subscriber", diff --git a/objdiff-cli/Cargo.toml b/objdiff-cli/Cargo.toml index 396643d..b3f4a05 100644 --- a/objdiff-cli/Cargo.toml +++ b/objdiff-cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "objdiff-cli" -version = "2.0.0-alpha.1" +version = "2.0.0-alpha.2" edition = "2021" rust-version = "1.70" authors = ["Luke Street "] diff --git a/objdiff-cli/src/cmd/diff.rs b/objdiff-cli/src/cmd/diff.rs index f0e0a3d..d0992a1 100644 --- a/objdiff-cli/src/cmd/diff.rs +++ b/objdiff-cli/src/cmd/diff.rs @@ -821,8 +821,10 @@ impl FunctionDiffUi { .transpose()?; let config = diff::DiffObjConfig { relax_reloc_diffs: self.relax_reloc_diffs, - space_between_args: true, // TODO - x86_formatter: Default::default(), // TODO + space_between_args: true, // TODO + x86_formatter: Default::default(), // TODO + mips_abi: Default::default(), // TODO + mips_instr_category: Default::default(), // TODO }; let result = diff::diff_objs(&config, target.as_ref(), base.as_ref(), prev.as_ref())?; diff --git a/objdiff-core/Cargo.toml b/objdiff-core/Cargo.toml index fd5d820..7bad507 100644 --- a/objdiff-core/Cargo.toml +++ b/objdiff-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "objdiff-core" -version = "2.0.0-alpha.1" +version = "2.0.0-alpha.2" edition = "2021" rust-version = "1.70" authors = ["Luke Street "] @@ -31,6 +31,7 @@ num-traits = "0.2.18" object = { version = "0.35.0", features = ["read_core", "std", "elf", "pe"], default-features = false } serde = { version = "1", features = ["derive"] } similar = { version = "2.5.0", default-features = false } +strum = { version = "0.26.2", features = ["derive"] } # config globset = { version = "0.4.14", features = ["serde1"], optional = true } diff --git a/objdiff-core/src/arch/mips.rs b/objdiff-core/src/arch/mips.rs index ea556dc..406f0ad 100644 --- a/objdiff-core/src/arch/mips.rs +++ b/objdiff-core/src/arch/mips.rs @@ -1,29 +1,56 @@ -use std::borrow::Cow; +use std::{borrow::Cow, sync::Mutex}; use anyhow::{anyhow, bail, Result}; -use object::{elf, Endian, Endianness, File, Object, Relocation, RelocationFlags}; +use object::{elf, Endian, Endianness, File, FileFlags, Object, Relocation, RelocationFlags}; use rabbitizer::{config, Abi, InstrCategory, Instruction, OperandType}; use crate::{ arch::{ObjArch, ProcessCodeResult}, - diff::DiffObjConfig, + diff::{DiffObjConfig, MipsAbi, MipsInstrCategory}, obj::{ObjInfo, ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection, SymbolRef}, }; -fn configure_rabbitizer() { +static RABBITIZER_MUTEX: Mutex<()> = Mutex::new(()); + +fn configure_rabbitizer(abi: Abi) { unsafe { - config::RabbitizerConfig_Cfg.reg_names.fpr_abi_names = Abi::O32; + config::RabbitizerConfig_Cfg.reg_names.fpr_abi_names = abi; } } pub struct ObjArchMips { pub endianness: Endianness, + pub abi: Abi, + pub instr_category: InstrCategory, } +const EF_MIPS_ABI: u32 = 0x0000F000; +const EF_MIPS_MACH: u32 = 0x00FF0000; + +const E_MIPS_MACH_ALLEGREX: u32 = 0x00840000; +const E_MIPS_MACH_5900: u32 = 0x00920000; + impl ObjArchMips { pub fn new(object: &File) -> Result { - configure_rabbitizer(); - Ok(Self { endianness: object.endianness() }) + let mut abi = Abi::NUMERIC; + let mut instr_category = InstrCategory::CPU; + match object.flags() { + FileFlags::None => {} + FileFlags::Elf { e_flags, .. } => { + abi = match e_flags & EF_MIPS_ABI { + elf::EF_MIPS_ABI_O32 => Abi::O32, + elf::EF_MIPS_ABI_EABI32 | elf::EF_MIPS_ABI_EABI64 => Abi::N32, + _ => Abi::NUMERIC, + }; + instr_category = match e_flags & EF_MIPS_MACH { + E_MIPS_MACH_ALLEGREX => InstrCategory::R4000ALLEGREX, + E_MIPS_MACH_5900 => InstrCategory::R5900, + _ => InstrCategory::CPU, + }; + } + _ => bail!("Unsupported MIPS file flags"), + } + Ok(Self { endianness: object.endianness(), abi, instr_category }) } } @@ -39,6 +66,22 @@ impl ObjArch for ObjArchMips { let code = §ion.data [symbol.section_address as usize..(symbol.section_address + symbol.size) as usize]; + let _guard = RABBITIZER_MUTEX.lock().map_err(|e| anyhow!("Failed to lock mutex: {e}"))?; + configure_rabbitizer(match config.mips_abi { + MipsAbi::Auto => self.abi, + MipsAbi::O32 => Abi::O32, + MipsAbi::N32 => Abi::N32, + MipsAbi::N64 => Abi::N64, + }); + let instr_category = match config.mips_instr_category { + MipsInstrCategory::Auto => self.instr_category, + MipsInstrCategory::Cpu => InstrCategory::CPU, + MipsInstrCategory::Rsp => InstrCategory::RSP, + MipsInstrCategory::R3000Gte => InstrCategory::R3000GTE, + MipsInstrCategory::R4000Allegrex => InstrCategory::R4000ALLEGREX, + MipsInstrCategory::R5900 => InstrCategory::R5900, + }; + let start_address = symbol.address; let end_address = symbol.address + symbol.size; let ins_count = code.len() / 4; @@ -48,7 +91,7 @@ impl ObjArch for ObjArchMips { for chunk in code.chunks_exact(4) { let reloc = section.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, cur_addr, InstrCategory::CPU); + let instruction = Instruction::new(code, cur_addr, instr_category); let formatted = instruction.disassemble(None, 0); let op = instruction.unique_id as u16; diff --git a/objdiff-core/src/diff/mod.rs b/objdiff-core/src/diff/mod.rs index a194d5f..3388efc 100644 --- a/objdiff-core/src/diff/mod.rs +++ b/objdiff-core/src/diff/mod.rs @@ -17,15 +17,82 @@ mod code; mod data; pub mod display; -#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[derive( + Debug, + Copy, + Clone, + Default, + Eq, + PartialEq, + serde::Deserialize, + serde::Serialize, + strum::VariantArray, + strum::EnumMessage, +)] pub enum X86Formatter { #[default] + #[strum(message = "Intel (default)")] Intel, + #[strum(message = "AT&T")] Gas, + #[strum(message = "NASM")] Nasm, + #[strum(message = "MASM")] Masm, } +#[derive( + Debug, + Copy, + Clone, + Default, + Eq, + PartialEq, + serde::Deserialize, + serde::Serialize, + strum::VariantArray, + strum::EnumMessage, +)] +pub enum MipsAbi { + #[default] + #[strum(message = "Auto (default)")] + Auto, + #[strum(message = "O32")] + O32, + #[strum(message = "N32")] + N32, + #[strum(message = "N64")] + N64, +} + +#[derive( + Debug, + Copy, + Clone, + Default, + Eq, + PartialEq, + serde::Deserialize, + serde::Serialize, + strum::VariantArray, + strum::EnumMessage, +)] +pub enum MipsInstrCategory { + #[default] + #[strum(message = "Auto (default)")] + Auto, + #[strum(message = "CPU")] + Cpu, + #[strum(message = "RSP (N64)")] + Rsp, + #[strum(message = "R3000 GTE (PS1)")] + R3000Gte, + #[strum(message = "R4000 ALLEGREX (PSP)")] + R4000Allegrex, + #[strum(message = "R5900 EE (PS2)")] + R5900, +} + #[inline] const fn default_true() -> bool { true } @@ -35,7 +102,11 @@ pub struct DiffObjConfig { pub relax_reloc_diffs: bool, #[serde(default = "default_true")] pub space_between_args: bool, + // x86 pub x86_formatter: X86Formatter, + // MIPS + pub mips_abi: MipsAbi, + pub mips_instr_category: MipsInstrCategory, } impl DiffObjConfig { diff --git a/objdiff-gui/Cargo.toml b/objdiff-gui/Cargo.toml index 63b21a8..9c9eaa8 100644 --- a/objdiff-gui/Cargo.toml +++ b/objdiff-gui/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "objdiff-gui" -version = "2.0.0-alpha.1" +version = "2.0.0-alpha.2" edition = "2021" rust-version = "1.70" authors = ["Luke Street "] @@ -46,6 +46,7 @@ ron = "0.8.1" serde = { version = "1", features = ["derive"] } serde_json = "1.0.116" shell-escape = "0.1.5" +strum = { version = "0.26.2", features = ["derive"] } tempfile = "3.10.1" time = { version = "0.3.36", features = ["formatting", "local-offset"] } diff --git a/objdiff-gui/src/app.rs b/objdiff-gui/src/app.rs index bcd125c..5f6a6f9 100644 --- a/objdiff-gui/src/app.rs +++ b/objdiff-gui/src/app.rs @@ -30,7 +30,7 @@ use crate::{ views::{ appearance::{appearance_window, Appearance}, config::{ - config_ui, diff_config_window, project_window, ConfigViewState, CONFIG_DISABLED_TEXT, + arch_config_window, config_ui, project_window, ConfigViewState, CONFIG_DISABLED_TEXT, }, data_diff::data_diff_ui, debug::debug_window, @@ -52,7 +52,7 @@ pub struct ViewState { pub show_appearance_config: bool, pub show_demangle: bool, pub show_project_config: bool, - pub show_diff_config: bool, + pub show_arch_config: bool, pub show_debug: bool, } @@ -414,7 +414,7 @@ impl eframe::App for App { show_appearance_config, show_demangle, show_project_config, - show_diff_config, + show_arch_config, show_debug, } = view_state; @@ -468,8 +468,8 @@ impl eframe::App for App { } }); ui.menu_button("Diff Options", |ui| { - if ui.button("More…").clicked() { - *show_diff_config = !*show_diff_config; + if ui.button("Arch Settings…").clicked() { + *show_arch_config = !*show_arch_config; ui.close_menu(); } let mut config = config.write().unwrap(); @@ -541,7 +541,7 @@ impl eframe::App for App { project_window(ctx, config, show_project_config, config_state, appearance); appearance_window(ctx, show_appearance_config, appearance); demangle_window(ctx, show_demangle, demangle_state, appearance); - diff_config_window(ctx, config, show_diff_config, appearance); + arch_config_window(ctx, config, show_arch_config, appearance); debug_window(ctx, show_debug, frame_history, appearance); self.post_update(ctx); diff --git a/objdiff-gui/src/views/config.rs b/objdiff-gui/src/views/config.rs index 81357f9..99edf75 100644 --- a/objdiff-gui/src/views/config.rs +++ b/objdiff-gui/src/views/config.rs @@ -16,9 +16,10 @@ use egui::{ use globset::Glob; use objdiff_core::{ config::{ProjectObject, DEFAULT_WATCH_PATTERNS}, - diff::X86Formatter, + diff::{MipsAbi, MipsInstrCategory, X86Formatter}, }; use self_update::cargo_crate_version; +use strum::{EnumMessage, VariantArray}; use crate::{ app::{AppConfig, AppConfigRef, ObjectConfig}, @@ -842,29 +843,28 @@ fn split_obj_config_ui( }); } -pub fn diff_config_window( +pub fn arch_config_window( ctx: &egui::Context, config: &AppConfigRef, show: &mut bool, appearance: &Appearance, ) { let mut config_guard = config.write().unwrap(); - egui::Window::new("Diff Config").open(show).show(ctx, |ui| { - diff_config_ui(ui, &mut config_guard, appearance); + egui::Window::new("Arch Settings").open(show).show(ctx, |ui| { + arch_config_ui(ui, &mut config_guard, appearance); }); } -fn diff_config_ui(ui: &mut egui::Ui, config: &mut AppConfig, _appearance: &Appearance) { - egui::ComboBox::new("x86_formatter", "X86 Format") - .selected_text(format!("{:?}", config.diff_obj_config.x86_formatter)) +fn arch_config_ui(ui: &mut egui::Ui, config: &mut AppConfig, _appearance: &Appearance) { + ui.heading("x86"); + egui::ComboBox::new("x86_formatter", "Format") + .selected_text(config.diff_obj_config.x86_formatter.get_message().unwrap()) .show_ui(ui, |ui| { - for &formatter in - &[X86Formatter::Intel, X86Formatter::Gas, X86Formatter::Nasm, X86Formatter::Masm] - { + for &formatter in X86Formatter::VARIANTS { if ui .selectable_label( config.diff_obj_config.x86_formatter == formatter, - format!("{:?}", formatter), + formatter.get_message().unwrap(), ) .clicked() { @@ -873,4 +873,38 @@ fn diff_config_ui(ui: &mut egui::Ui, config: &mut AppConfig, _appearance: &Appea } } }); + ui.separator(); + ui.heading("MIPS"); + egui::ComboBox::new("mips_abi", "ABI") + .selected_text(config.diff_obj_config.mips_abi.get_message().unwrap()) + .show_ui(ui, |ui| { + for &abi in MipsAbi::VARIANTS { + if ui + .selectable_label( + config.diff_obj_config.mips_abi == abi, + abi.get_message().unwrap(), + ) + .clicked() + { + config.diff_obj_config.mips_abi = abi; + config.queue_reload = true; + } + } + }); + egui::ComboBox::new("mips_instr_category", "Instruction Category") + .selected_text(config.diff_obj_config.mips_instr_category.get_message().unwrap()) + .show_ui(ui, |ui| { + for &category in MipsInstrCategory::VARIANTS { + if ui + .selectable_label( + config.diff_obj_config.mips_instr_category == category, + category.get_message().unwrap(), + ) + .clicked() + { + config.diff_obj_config.mips_instr_category = category; + config.queue_reload = true; + } + } + }); }