diff --git a/Cargo.lock b/Cargo.lock index 368a4d6..f7892fb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4330,9 +4330,9 @@ dependencies = [ [[package]] name = "unarm" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6bff109f0171a299559d82a7236e056093fc0dcd2a7da86aa745f82281e2d31" +checksum = "379762d9433a2e6e498cde97801fb238318b024a513d0843eeac98b9056b9f3c" [[package]] name = "unicase" diff --git a/objdiff-cli/src/cmd/diff.rs b/objdiff-cli/src/cmd/diff.rs index a7fc1ed..4c87666 100644 --- a/objdiff-cli/src/cmd/diff.rs +++ b/objdiff-cli/src/cmd/diff.rs @@ -817,6 +817,12 @@ impl FunctionDiffUi { mips_abi: Default::default(), // TODO mips_instr_category: Default::default(), // TODO arm_arch_version: Default::default(), // TODO + arm_unified_syntax: true, // TODO + arm_av_registers: false, // TODO + arm_r9_usage: Default::default(), // TODO + arm_sl_usage: false, // TODO + arm_fp_usage: false, // TODO + arm_ip_usage: false, // TODO }; let target = self .target_path diff --git a/objdiff-core/Cargo.toml b/objdiff-core/Cargo.toml index c2575c0..e6b2171 100644 --- a/objdiff-core/Cargo.toml +++ b/objdiff-core/Cargo.toml @@ -56,5 +56,5 @@ iced-x86 = { version = "1.21.0", default-features = false, features = ["std", "d msvc-demangler = { version = "0.10.0", optional = true } # arm -unarm = { version = "1.3.0", optional = true } +unarm = { version = "1.4.0", optional = true } arm-attr = { version = "0.1.1", optional = true } diff --git a/objdiff-core/src/arch/arm.rs b/objdiff-core/src/arch/arm.rs index a0121d9..9aabf43 100644 --- a/objdiff-core/src/arch/arm.rs +++ b/objdiff-core/src/arch/arm.rs @@ -13,12 +13,12 @@ use object::{ use unarm::{ args::{Argument, OffsetImm, OffsetReg, Register}, parse::{ArmVersion, ParseMode, Parser}, - ParsedIns, + DisplayOptions, ParseFlags, ParsedIns, RegNames, }; use crate::{ arch::{ObjArch, ProcessCodeResult}, - diff::{ArmArchVersion, DiffObjConfig}, + diff::{ArmArchVersion, ArmR9Usage, DiffObjConfig}, obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection}, }; @@ -149,7 +149,24 @@ impl ObjArch for ObjArchArm { object::Endianness::Little => unarm::Endian::Little, object::Endianness::Big => unarm::Endian::Big, }; - let mut parser = Parser::new(version, first_mapping, start_addr, endian, code); + + let parse_flags = ParseFlags { ual: config.arm_unified_syntax }; + + let mut parser = Parser::new(version, first_mapping, start_addr, endian, parse_flags, code); + + let display_options = DisplayOptions { + reg_names: RegNames { + av_registers: config.arm_av_registers, + r9_use: match config.arm_r9_usage { + ArmR9Usage::GeneralPurpose => unarm::R9Use::GeneralPurpose, + ArmR9Usage::Sb => unarm::R9Use::Pid, + ArmR9Usage::Tr => unarm::R9Use::Tls, + }, + explicit_stack_limit: config.arm_sl_usage, + frame_pointer: config.arm_fp_usage, + ip: config.arm_ip_usage, + }, + }; while let Some((address, op, ins)) = parser.next() { if let Some(next) = next_mapping { @@ -187,7 +204,7 @@ impl ObjArch for ObjArchArm { let (args, branch_dest) = if reloc.is_some() && parser.mode == ParseMode::Data { (vec![ObjInsArg::Reloc], None) } else { - push_args(&ins, config, reloc_arg, address)? + push_args(&ins, config, reloc_arg, address, display_options)? }; ops.push(op.id()); @@ -200,7 +217,7 @@ impl ObjArch for ObjArchArm { reloc, branch_dest, line, - formatted: ins.to_string(), + formatted: ins.display(display_options).to_string(), orig: None, }); } @@ -281,6 +298,7 @@ fn push_args( config: &DiffObjConfig, reloc_arg: Option, cur_addr: u32, + display_options: DisplayOptions, ) -> Result<(Vec, Option)> { let mut args = vec![]; let mut branch_dest = None; @@ -318,7 +336,9 @@ fn push_args( deref = true; args.push(ObjInsArg::PlainText("[".into())); } - args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(reg.reg.to_string().into()))); + args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque( + reg.reg.display(display_options.reg_names).to_string().into(), + ))); if reg.writeback { if reg.deref { writeback = true; @@ -336,7 +356,10 @@ fn push_args( args.push(ObjInsArg::PlainText(config.separator().into())); } args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque( - Register::parse(i).to_string().into(), + Register::parse(i) + .display(display_options.reg_names) + .to_string() + .into(), ))); first = false; } @@ -376,14 +399,16 @@ fn push_args( Argument::ShiftReg(shift) => { args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(shift.op.to_string().into()))); args.push(ObjInsArg::PlainText(" ".into())); - args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(shift.reg.to_string().into()))); + args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque( + shift.reg.display(display_options.reg_names).to_string().into(), + ))); } Argument::OffsetReg(offset) => { if !offset.add { args.push(ObjInsArg::PlainText("-".into())); } args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque( - offset.reg.to_string().into(), + offset.reg.display(display_options.reg_names).to_string().into(), ))); } Argument::CpsrMode(mode) => { @@ -398,9 +423,9 @@ fn push_args( | Argument::StatusMask(_) | Argument::Shift(_) | Argument::CpsrFlags(_) - | Argument::Endian(_) => { - args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(arg.to_string().into()))) - } + | Argument::Endian(_) => args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque( + arg.display(display_options).to_string().into(), + ))), } } } diff --git a/objdiff-core/src/diff/mod.rs b/objdiff-core/src/diff/mod.rs index e643a83..f1a6597 100644 --- a/objdiff-core/src/diff/mod.rs +++ b/objdiff-core/src/diff/mod.rs @@ -117,6 +117,34 @@ pub enum ArmArchVersion { V6K, } +#[derive( + Debug, + Copy, + Clone, + Default, + Eq, + PartialEq, + serde::Deserialize, + serde::Serialize, + strum::VariantArray, + strum::EnumMessage, +)] +pub enum ArmR9Usage { + #[default] + #[strum( + message = "R9 or V6 (default)", + detailed_message = "Use R9 as a general-purpose register." + )] + GeneralPurpose, + #[strum( + message = "SB (static base)", + detailed_message = "Used for position-independent data (PID)." + )] + Sb, + #[strum(message = "TR (TLS register)", detailed_message = "Used for thread-local storage.")] + Tr, +} + #[inline] const fn default_true() -> bool { true } @@ -134,6 +162,12 @@ pub struct DiffObjConfig { pub mips_instr_category: MipsInstrCategory, // ARM pub arm_arch_version: ArmArchVersion, + pub arm_unified_syntax: bool, + pub arm_av_registers: bool, + pub arm_r9_usage: ArmR9Usage, + pub arm_sl_usage: bool, + pub arm_fp_usage: bool, + pub arm_ip_usage: bool, } impl Default for DiffObjConfig { @@ -146,6 +180,12 @@ impl Default for DiffObjConfig { mips_abi: Default::default(), mips_instr_category: Default::default(), arm_arch_version: Default::default(), + arm_unified_syntax: true, + arm_av_registers: false, + arm_r9_usage: Default::default(), + arm_sl_usage: false, + arm_fp_usage: false, + arm_ip_usage: false, } } } diff --git a/objdiff-gui/src/views/config.rs b/objdiff-gui/src/views/config.rs index 56e1f53..f432f5a 100644 --- a/objdiff-gui/src/views/config.rs +++ b/objdiff-gui/src/views/config.rs @@ -16,7 +16,7 @@ use egui::{ use globset::Glob; use objdiff_core::{ config::{ProjectObject, DEFAULT_WATCH_PATTERNS}, - diff::{ArmArchVersion, MipsAbi, MipsInstrCategory, X86Formatter}, + diff::{ArmArchVersion, ArmR9Usage, MipsAbi, MipsInstrCategory, X86Formatter}, }; use self_update::cargo_crate_version; use strum::{EnumMessage, VariantArray}; @@ -925,4 +925,51 @@ fn arch_config_ui(ui: &mut egui::Ui, config: &mut AppConfig, _appearance: &Appea } } }); + let response = ui + .checkbox(&mut config.diff_obj_config.arm_unified_syntax, "Unified syntax") + .on_hover_text("Disassemble as unified assembly language (UAL)."); + if response.changed() { + config.queue_reload = true; + } + let response = ui + .checkbox(&mut config.diff_obj_config.arm_av_registers, "Use A/V registers") + .on_hover_text("Display R0-R3 as A1-A4 and R4-R11 as V1-V8"); + if response.changed() { + config.queue_reload = true; + } + egui::ComboBox::new("arm_r9_usage", "Display R9 as") + .selected_text(config.diff_obj_config.arm_r9_usage.get_message().unwrap()) + .show_ui(ui, |ui| { + for &usage in ArmR9Usage::VARIANTS { + if ui + .selectable_label( + config.diff_obj_config.arm_r9_usage == usage, + usage.get_message().unwrap(), + ) + .on_hover_text(usage.get_detailed_message().unwrap()) + .clicked() + { + config.diff_obj_config.arm_r9_usage = usage; + config.queue_reload = true; + } + } + }); + let response = ui + .checkbox(&mut config.diff_obj_config.arm_sl_usage, "Display R10 as SL") + .on_hover_text("Used for explicit stack limits."); + if response.changed() { + config.queue_reload = true; + } + let response = ui + .checkbox(&mut config.diff_obj_config.arm_fp_usage, "Display R11 as FP") + .on_hover_text("Used for frame pointers."); + if response.changed() { + config.queue_reload = true; + } + let response = ui + .checkbox(&mut config.diff_obj_config.arm_ip_usage, "Display R12 as IP") + .on_hover_text("Used for interworking and long branches."); + if response.changed() { + config.queue_reload = true; + } }