diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index bb5fdc0..885e278 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -29,9 +29,17 @@ jobs: uses: dtolnay/rust-toolchain@stable with: components: clippy + - name: Setup sccache + uses: mozilla-actions/sccache-action@v0.0.4 - name: Cargo check + env: + RUSTC_WRAPPER: sccache + SCCACHE_GHA_ENABLED: "true" run: cargo check - name: Cargo clippy + env: + RUSTC_WRAPPER: sccache + SCCACHE_GHA_ENABLED: "true" run: cargo clippy fmt: @@ -84,7 +92,12 @@ jobs: uses: actions/checkout@v4 - name: Setup Rust toolchain uses: dtolnay/rust-toolchain@stable + - name: Setup sccache + uses: mozilla-actions/sccache-action@v0.0.4 - name: Cargo test + env: + RUSTC_WRAPPER: sccache + SCCACHE_GHA_ENABLED: "true" run: cargo test --release build: @@ -117,18 +130,18 @@ jobs: run: | sudo apt-get update sudo apt-get -y install ${{ matrix.packages }} - - name: Run sccache-cache - uses: mozilla-actions/sccache-action@v0.0.4 - name: Checkout uses: actions/checkout@v4 - name: Setup Rust toolchain uses: dtolnay/rust-toolchain@stable with: targets: ${{ matrix.target }} + - name: Setup sccache + uses: mozilla-actions/sccache-action@v0.0.4 - name: Cargo build env: + RUSTC_WRAPPER: sccache SCCACHE_GHA_ENABLED: "true" - RUSTC_WRAPPER: "sccache" run: > cargo build --profile ${{ env.BUILD_PROFILE }} --target ${{ matrix.target }} --bin objdiff-cli --bin objdiff --features ${{ matrix.features }} diff --git a/Cargo.lock b/Cargo.lock index a0da823..6a1c37b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1054,6 +1054,15 @@ dependencies = [ "libc", ] +[[package]] +name = "cpp_demangle" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8227005286ec39567949b33df9896bcadfa6051bccca2488129f108ca23119" +dependencies = [ + "cfg-if", +] + [[package]] name = "cpufeatures" version = "0.2.12" @@ -3013,7 +3022,6 @@ dependencies = [ "argp", "crossterm", "enable-ansi-support", - "log", "objdiff-core", "ratatui", "rayon", @@ -3022,7 +3030,6 @@ dependencies = [ "supports-color", "time", "tracing", - "tracing-attributes", "tracing-subscriber", ] @@ -3032,6 +3039,7 @@ version = "1.0.0" dependencies = [ "anyhow", "byteorder", + "cpp_demangle", "cwdemangle", "filetime", "flagset", @@ -3081,15 +3089,11 @@ dependencies = [ "rfd", "ron", "self_update", - "semver", "serde", "serde_json", - "serde_yaml", "shell-escape", "tempfile", - "thiserror", "time", - "toml 0.8.11", "tracing-subscriber", "tracing-wasm", "vergen", @@ -3363,12 +3367,8 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppc750cl" -version = "0.2.0" -source = "git+https://github.com/encounter/ppc750cl?rev=4a2bbbc6f84dcb76255ab6f3595a8d4a0ce96618#4a2bbbc6f84dcb76255ab6f3595a8d4a0ce96618" -dependencies = [ - "num-traits", - "serde", -] +version = "0.3.0" +source = "git+https://github.com/encounter/ppc750cl?rev=d31bf75009e4efc102fc2b3b33fb7cd041859942#d31bf75009e4efc102fc2b3b33fb7cd041859942" [[package]] name = "ppv-lite86" @@ -3970,15 +3970,6 @@ dependencies = [ "syn 2.0.52", ] -[[package]] -name = "serde_spanned" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" -dependencies = [ - "serde", -] - [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -4466,26 +4457,11 @@ dependencies = [ "serde", ] -[[package]] -name = "toml" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af06656561d28735e9c1cd63dfd57132c8155426aa6af24f36a00a351f88c48e" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit 0.22.7", -] - [[package]] name = "toml_datetime" version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" -dependencies = [ - "serde", -] [[package]] name = "toml_edit" @@ -4495,7 +4471,7 @@ checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ "indexmap", "toml_datetime", - "winnow 0.5.40", + "winnow", ] [[package]] @@ -4506,20 +4482,7 @@ checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ "indexmap", "toml_datetime", - "winnow 0.5.40", -] - -[[package]] -name = "toml_edit" -version = "0.22.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18769cd1cec395d70860ceb4d932812a0b4d06b1a4bb336745a4d21b9496e992" -dependencies = [ - "indexmap", - "serde", - "serde_spanned", - "toml_datetime", - "winnow 0.6.2", + "winnow", ] [[package]] @@ -5460,15 +5423,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "winnow" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a4191c47f15cc3ec71fcb4913cb83d58def65dd3787610213c649283b5ce178" -dependencies = [ - "memchr", -] - [[package]] name = "winreg" version = "0.50.0" @@ -5485,7 +5439,7 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b68db261ef59e9e52806f688020631e987592bd83619edccda9c47d42cde4f6c" dependencies = [ - "toml 0.5.11", + "toml", ] [[package]] diff --git a/objdiff-cli/Cargo.toml b/objdiff-cli/Cargo.toml index f269fc5..3ac023d 100644 --- a/objdiff-cli/Cargo.toml +++ b/objdiff-cli/Cargo.toml @@ -18,7 +18,6 @@ anyhow = "1.0.81" argp = "0.3.0" crossterm = "0.27.0" enable-ansi-support = "0.2.1" -log = "0.4.21" objdiff-core = { path = "../objdiff-core", features = ["all"] } ratatui = "0.26.1" rayon = "1.9.0" @@ -27,5 +26,4 @@ serde_json = "1.0.114" supports-color = "3.0.0" time = { version = "0.3.34", features = ["formatting", "local-offset"] } tracing = "0.1.40" -tracing-attributes = "0.1.27" tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } diff --git a/objdiff-cli/src/cmd/report.rs b/objdiff-cli/src/cmd/report.rs index c7077a5..475cb58 100644 --- a/objdiff-cli/src/cmd/report.rs +++ b/objdiff-cli/src/cmd/report.rs @@ -14,6 +14,7 @@ use objdiff_core::{ obj::{ObjSectionKind, ObjSymbolFlags}, }; use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator}; +use tracing::{info, warn}; #[derive(FromArgs, PartialEq, Debug)] /// Commands for processing NVIDIA Shield TV alf files. @@ -114,13 +115,13 @@ pub fn run(args: Args) -> Result<()> { fn generate(args: GenerateArgs) -> Result<()> { let project_dir = args.project.as_deref().unwrap_or_else(|| Path::new(".")); - log::info!("Loading project {}", project_dir.display()); + info!("Loading project {}", project_dir.display()); let config = objdiff_core::config::try_project_config(project_dir); let Some((Ok(mut project), _)) = config else { bail!("No project configuration found"); }; - log::info!( + info!( "Generating report for {} units (using {} threads)", project.objects.len(), if args.deduplicate { 1 } else { rayon::current_num_threads() } @@ -181,9 +182,9 @@ fn generate(args: GenerateArgs) -> Result<()> { report.matched_functions as f32 / report.total_functions as f32 * 100.0 }; let duration = start.elapsed(); - log::info!("Report generated in {}.{:03}s", duration.as_secs(), duration.subsec_millis()); + info!("Report generated in {}.{:03}s", duration.as_secs(), duration.subsec_millis()); if let Some(output) = &args.output { - log::info!("Writing to {}", output.display()); + info!("Writing to {}", output.display()); let mut output = BufWriter::new( File::create(output) .with_context(|| format!("Failed to create file {}", output.display()))?, @@ -206,11 +207,11 @@ fn report_object( object.resolve_paths(project_dir, target_dir, base_dir); match (&object.target_path, &object.base_path) { (None, Some(_)) if object.complete != Some(true) => { - log::warn!("Skipping object without target: {}", object.name()); + warn!("Skipping object without target: {}", object.name()); return Ok(None); } (None, None) => { - log::warn!("Skipping object without target or base: {}", object.name()); + warn!("Skipping object without target or base: {}", object.name()); return Ok(None); } _ => {} @@ -452,7 +453,7 @@ fn changes(args: ChangesArgs) -> Result<()> { } } if let Some(output) = &args.output { - log::info!("Writing to {}", output.display()); + info!("Writing to {}", output.display()); let mut output = BufWriter::new( File::create(output) .with_context(|| format!("Failed to create file {}", output.display()))?, diff --git a/objdiff-core/Cargo.toml b/objdiff-core/Cargo.toml index 7885fda..03b8c21 100644 --- a/objdiff-core/Cargo.toml +++ b/objdiff-core/Cargo.toml @@ -18,7 +18,7 @@ config = ["globset", "semver", "serde_json", "serde_yaml"] dwarf = ["gimli"] mips = ["any-arch", "rabbitizer"] ppc = ["any-arch", "cwdemangle", "ppc750cl"] -x86 = ["any-arch", "iced-x86", "msvc-demangler"] +x86 = ["any-arch", "cpp_demangle", "iced-x86", "msvc-demangler"] [dependencies] anyhow = "1.0.81" @@ -43,15 +43,12 @@ gimli = { version = "0.28.1", default-features = false, features = ["read-all"], # ppc cwdemangle = { version = "1.0.0", optional = true } -ppc750cl = { git = "https://github.com/encounter/ppc750cl", rev = "4a2bbbc6f84dcb76255ab6f3595a8d4a0ce96618", optional = true } +ppc750cl = { git = "https://github.com/encounter/ppc750cl", rev = "d31bf75009e4efc102fc2b3b33fb7cd041859942", optional = true } # mips rabbitizer = { version = "1.9.2", optional = true } # x86 +cpp_demangle = { version = "0.4.3", optional = true } +iced-x86 = { version = "1.21.0", default-features = false, features = ["std", "decoder", "intel", "gas", "masm", "nasm", "exhaustive_enums"], optional = true } msvc-demangler = { version = "0.10.0", optional = true } -[dependencies.iced-x86] -version = "1.21.0" -default-features = false -features = ["std", "decoder", "intel", "gas", "masm", "nasm", "exhaustive_enums"] -optional = true diff --git a/objdiff-core/src/arch/mips.rs b/objdiff-core/src/arch/mips.rs index 2fb671d..7abce86 100644 --- a/objdiff-core/src/arch/mips.rs +++ b/objdiff-core/src/arch/mips.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, collections::BTreeMap}; +use std::borrow::Cow; use anyhow::{bail, Result}; use object::{elf, Endian, Endianness, File, Object, Relocation, RelocationFlags}; @@ -7,7 +7,7 @@ use rabbitizer::{config, Abi, InstrCategory, Instruction, OperandType}; use crate::{ arch::{ObjArch, ProcessCodeResult}, diff::DiffObjConfig, - obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection}, + obj::{ObjInfo, ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection, SymbolRef}, }; fn configure_rabbitizer() { @@ -21,27 +21,31 @@ pub struct ObjArchMips { } impl ObjArchMips { - pub fn new(object: &File) -> Result { Ok(Self { endianness: object.endianness() }) } + pub fn new(object: &File) -> Result { + configure_rabbitizer(); + Ok(Self { endianness: object.endianness() }) + } } impl ObjArch for ObjArchMips { fn process_code( &self, + obj: &ObjInfo, + symbol_ref: SymbolRef, config: &DiffObjConfig, - data: &[u8], - start_address: u64, - relocs: &[ObjReloc], - line_info: &Option>, ) -> Result { - configure_rabbitizer(); + let (section, symbol) = obj.section_symbol(symbol_ref); + let code = §ion.data + [symbol.section_address as usize..(symbol.section_address + symbol.size) as usize]; - let end_address = start_address + data.len() as u64; - let ins_count = data.len() / 4; + let start_address = symbol.address; + let end_address = symbol.address + symbol.size; + let ins_count = code.len() / 4; let mut ops = Vec::::with_capacity(ins_count); let mut insts = Vec::::with_capacity(ins_count); let mut cur_addr = start_address as u32; - for chunk in data.chunks_exact(4) { - let reloc = relocs.iter().find(|r| (r.address as u32 & !3) == cur_addr); + 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); @@ -61,11 +65,7 @@ impl ObjArch for ObjArchMips { let mut args = Vec::with_capacity(operands.len() + 1); for (idx, op) in operands.iter().enumerate() { if idx > 0 { - if config.space_between_args { - args.push(ObjInsArg::PlainText(", ".to_string())); - } else { - args.push(ObjInsArg::PlainText(",".to_string())); - } + args.push(ObjInsArg::PlainText(config.separator().into())); } match op { @@ -85,7 +85,7 @@ impl ObjArch for ObjArchMips { } } else { args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque( - op.disassemble(&instruction, None), + op.disassemble(&instruction, None).into(), ))); } } @@ -94,23 +94,24 @@ impl ObjArch for ObjArchMips { push_reloc(&mut args, reloc)?; } else { args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque( - OperandType::cpu_immediate.disassemble(&instruction, None), + OperandType::cpu_immediate.disassemble(&instruction, None).into(), ))); } - args.push(ObjInsArg::PlainText("(".to_string())); + args.push(ObjInsArg::PlainText("(".into())); args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque( - OperandType::cpu_rs.disassemble(&instruction, None), + OperandType::cpu_rs.disassemble(&instruction, None).into(), ))); - args.push(ObjInsArg::PlainText(")".to_string())); + args.push(ObjInsArg::PlainText(")".into())); } _ => { args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque( - op.disassemble(&instruction, None), + op.disassemble(&instruction, None).into(), ))); } } } - let line = line_info + let line = obj + .line_info .as_ref() .and_then(|map| map.range(..=cur_addr as u64).last().map(|(_, &b)| b)); insts.push(ObjIns { @@ -161,9 +162,9 @@ impl ObjArch for ObjArchMips { elf::R_MIPS_GPREL16 => Cow::Borrowed("R_MIPS_GPREL16"), elf::R_MIPS_32 => Cow::Borrowed("R_MIPS_32"), elf::R_MIPS_26 => Cow::Borrowed("R_MIPS_26"), - _ => Cow::Owned(format!("")), + _ => Cow::Owned(format!("<{flags:?}>")), }, - flags => Cow::Owned(format!("<{flags:?}>")), + _ => Cow::Owned(format!("<{flags:?}>")), } } } @@ -172,29 +173,29 @@ fn push_reloc(args: &mut Vec, reloc: &ObjReloc) -> Result<()> { match reloc.flags { RelocationFlags::Elf { r_type } => match r_type { elf::R_MIPS_HI16 => { - args.push(ObjInsArg::PlainText("%hi(".to_string())); + args.push(ObjInsArg::PlainText("%hi(".into())); args.push(ObjInsArg::Reloc); - args.push(ObjInsArg::PlainText(")".to_string())); + args.push(ObjInsArg::PlainText(")".into())); } elf::R_MIPS_LO16 => { - args.push(ObjInsArg::PlainText("%lo(".to_string())); + args.push(ObjInsArg::PlainText("%lo(".into())); args.push(ObjInsArg::Reloc); - args.push(ObjInsArg::PlainText(")".to_string())); + args.push(ObjInsArg::PlainText(")".into())); } elf::R_MIPS_GOT16 => { - args.push(ObjInsArg::PlainText("%got(".to_string())); + args.push(ObjInsArg::PlainText("%got(".into())); args.push(ObjInsArg::Reloc); - args.push(ObjInsArg::PlainText(")".to_string())); + args.push(ObjInsArg::PlainText(")".into())); } elf::R_MIPS_CALL16 => { - args.push(ObjInsArg::PlainText("%call16(".to_string())); + args.push(ObjInsArg::PlainText("%call16(".into())); args.push(ObjInsArg::Reloc); - args.push(ObjInsArg::PlainText(")".to_string())); + args.push(ObjInsArg::PlainText(")".into())); } elf::R_MIPS_GPREL16 => { - args.push(ObjInsArg::PlainText("%gp_rel(".to_string())); + args.push(ObjInsArg::PlainText("%gp_rel(".into())); args.push(ObjInsArg::Reloc); - args.push(ObjInsArg::PlainText(")".to_string())); + args.push(ObjInsArg::PlainText(")".into())); } elf::R_MIPS_32 | elf::R_MIPS_26 => { args.push(ObjInsArg::Reloc); diff --git a/objdiff-core/src/arch/mod.rs b/objdiff-core/src/arch/mod.rs index 03ce975..6e5057c 100644 --- a/objdiff-core/src/arch/mod.rs +++ b/objdiff-core/src/arch/mod.rs @@ -1,11 +1,11 @@ -use std::{borrow::Cow, collections::BTreeMap}; +use std::borrow::Cow; use anyhow::{bail, Result}; use object::{Architecture, Object, Relocation, RelocationFlags}; use crate::{ diff::DiffObjConfig, - obj::{ObjIns, ObjReloc, ObjSection}, + obj::{ObjInfo, ObjIns, ObjSection, SymbolRef}, }; #[cfg(feature = "mips")] @@ -18,11 +18,9 @@ mod x86; pub trait ObjArch: Send + Sync { fn process_code( &self, + obj: &ObjInfo, + symbol_ref: SymbolRef, config: &DiffObjConfig, - data: &[u8], - address: u64, - relocs: &[ObjReloc], - line_info: &Option>, ) -> Result; fn implcit_addend(&self, section: &ObjSection, address: u64, reloc: &Relocation) diff --git a/objdiff-core/src/arch/ppc.rs b/objdiff-core/src/arch/ppc.rs index e74fb39..db8c321 100644 --- a/objdiff-core/src/arch/ppc.rs +++ b/objdiff-core/src/arch/ppc.rs @@ -1,13 +1,13 @@ -use std::{borrow::Cow, collections::BTreeMap}; +use std::borrow::Cow; use anyhow::{bail, Result}; use object::{elf, File, Relocation, RelocationFlags}; -use ppc750cl::{disasm_iter, Argument, SimplifiedIns, GPR}; +use ppc750cl::{Argument, GPR}; use crate::{ arch::{ObjArch, ProcessCodeResult}, diff::DiffObjConfig, - obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection}, + obj::{ObjInfo, ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection, SymbolRef}, }; // Relative relocation, can be Simm, Offset or BranchDest @@ -31,30 +31,37 @@ impl ObjArchPpc { impl ObjArch for ObjArchPpc { fn process_code( &self, + obj: &ObjInfo, + symbol_ref: SymbolRef, config: &DiffObjConfig, - data: &[u8], - address: u64, - relocs: &[ObjReloc], - line_info: &Option>, ) -> Result { - let ins_count = data.len() / 4; + let (section, symbol) = obj.section_symbol(symbol_ref); + let code = §ion.data + [symbol.section_address as usize..(symbol.section_address + symbol.size) as usize]; + + let ins_count = code.len() / 4; let mut ops = Vec::::with_capacity(ins_count); let mut insts = Vec::::with_capacity(ins_count); - for mut ins in disasm_iter(data, address as u32) { - let reloc = relocs.iter().find(|r| (r.address as u32 & !3) == ins.addr); + let mut cur_addr = symbol.address as u32; + for chunk in code.chunks_exact(4) { + let mut code = u32::from_be_bytes(chunk.try_into()?); + let reloc = section.relocations.iter().find(|r| (r.address as u32 & !3) == cur_addr); if let Some(reloc) = reloc { // Zero out relocations - ins.code = match reloc.flags { - RelocationFlags::Elf { r_type: elf::R_PPC_EMB_SDA21 } => ins.code & !0x1FFFFF, - RelocationFlags::Elf { r_type: elf::R_PPC_REL24 } => ins.code & !0x3FFFFFC, - RelocationFlags::Elf { r_type: elf::R_PPC_REL14 } => ins.code & !0xFFFC, + code = match reloc.flags { + RelocationFlags::Elf { r_type: elf::R_PPC_EMB_SDA21 } => code & !0x1FFFFF, + RelocationFlags::Elf { r_type: elf::R_PPC_REL24 } => code & !0x3FFFFFC, + RelocationFlags::Elf { r_type: elf::R_PPC_REL14 } => code & !0xFFFC, RelocationFlags::Elf { r_type: elf::R_PPC_ADDR16_HI | elf::R_PPC_ADDR16_HA | elf::R_PPC_ADDR16_LO, - } => ins.code & !0xFFFF, - _ => ins.code, + } => code & !0xFFFF, + _ => code, }; } - let simplified = ins.clone().simplified(); + + let ins = ppc750cl::Ins::new(code); + let orig = ins.basic().to_string(); + let simplified = ins.simplified(); let mut reloc_arg = None; if let Some(reloc) = reloc { @@ -77,13 +84,9 @@ impl ObjArch for ObjArchPpc { let mut args = vec![]; let mut branch_dest = None; let mut writing_offset = false; - for (idx, arg) in simplified.args.iter().enumerate() { + for (idx, arg) in simplified.args_iter().enumerate() { if idx > 0 && !writing_offset { - if config.space_between_args { - args.push(ObjInsArg::PlainText(", ".to_string())); - } else { - args.push(ObjInsArg::PlainText(",".to_string())); - } + args.push(ObjInsArg::PlainText(config.separator().into())); } if reloc_arg == Some(idx) { @@ -108,41 +111,45 @@ impl ObjArch for ObjArchPpc { args.push(ObjInsArg::Arg(ObjInsArgValue::Signed(offset.0 as i64))); } Argument::BranchDest(dest) => { - let dest = ins.addr.wrapping_add_signed(dest.0) as u64; + let dest = cur_addr.wrapping_add_signed(dest.0) as u64; args.push(ObjInsArg::BranchDest(dest)); branch_dest = Some(dest); } _ => { - args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(arg.to_string()))); + args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque( + arg.to_string().into(), + ))); } }; } if writing_offset { - args.push(ObjInsArg::PlainText(")".to_string())); + args.push(ObjInsArg::PlainText(")".into())); writing_offset = false; } if is_offset_arg(arg) { - args.push(ObjInsArg::PlainText("(".to_string())); + args.push(ObjInsArg::PlainText("(".into())); writing_offset = true; } } - ops.push(simplified.ins.op as u16); - let line = line_info + ops.push(ins.op as u16); + let line = obj + .line_info .as_ref() - .and_then(|map| map.range(..=simplified.ins.addr as u64).last().map(|(_, &b)| b)); + .and_then(|map| map.range(..=cur_addr as u64).last().map(|(_, &b)| b)); insts.push(ObjIns { - address: simplified.ins.addr as u64, + address: cur_addr as u64, size: 4, - mnemonic: format!("{}{}", simplified.mnemonic, simplified.suffix), + mnemonic: simplified.mnemonic.to_string(), args, reloc: reloc.cloned(), op: ins.op as u16, branch_dest, line, - orig: Some(format!("{}", SimplifiedIns::basic_form(ins))), + orig: Some(orig), }); + cur_addr += 4; } Ok(ProcessCodeResult { ops, insts }) } @@ -157,7 +164,7 @@ impl ObjArch for ObjArchPpc { } fn demangle(&self, name: &str) -> Option { - cwdemangle::demangle(name, &Default::default()) + cwdemangle::demangle(name, &cwdemangle::DemangleOptions::default()) } fn display_reloc(&self, flags: RelocationFlags) -> Cow<'static, str> { @@ -171,9 +178,9 @@ impl ObjArch for ObjArchPpc { elf::R_PPC_UADDR32 => Cow::Borrowed("R_PPC_UADDR32"), elf::R_PPC_REL24 => Cow::Borrowed("R_PPC_REL24"), elf::R_PPC_REL14 => Cow::Borrowed("R_PPC_REL14"), - _ => Cow::Owned(format!("")), + _ => Cow::Owned(format!("<{flags:?}>")), }, - flags => Cow::Owned(format!("<{flags:?}>")), + _ => Cow::Owned(format!("<{flags:?}>")), } } } @@ -183,19 +190,19 @@ fn push_reloc(args: &mut Vec, reloc: &ObjReloc) -> Result<()> { RelocationFlags::Elf { r_type } => match r_type { elf::R_PPC_ADDR16_LO => { args.push(ObjInsArg::Reloc); - args.push(ObjInsArg::PlainText("@l".to_string())); + args.push(ObjInsArg::PlainText("@l".into())); } elf::R_PPC_ADDR16_HI => { args.push(ObjInsArg::Reloc); - args.push(ObjInsArg::PlainText("@h".to_string())); + args.push(ObjInsArg::PlainText("@h".into())); } elf::R_PPC_ADDR16_HA => { args.push(ObjInsArg::Reloc); - args.push(ObjInsArg::PlainText("@ha".to_string())); + args.push(ObjInsArg::PlainText("@ha".into())); } elf::R_PPC_EMB_SDA21 => { args.push(ObjInsArg::Reloc); - args.push(ObjInsArg::PlainText("@sda21".to_string())); + args.push(ObjInsArg::PlainText("@sda21".into())); } elf::R_PPC_ADDR32 | elf::R_PPC_UADDR32 | elf::R_PPC_REL24 | elf::R_PPC_REL14 => { args.push(ObjInsArg::Reloc); diff --git a/objdiff-core/src/arch/x86.rs b/objdiff-core/src/arch/x86.rs index 78f8d68..90501aa 100644 --- a/objdiff-core/src/arch/x86.rs +++ b/objdiff-core/src/arch/x86.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, collections::BTreeMap}; +use std::borrow::Cow; use anyhow::{anyhow, bail, ensure, Result}; use iced_x86::{ @@ -11,7 +11,7 @@ use object::{pe, Endian, Endianness, File, Object, Relocation, RelocationFlags}; use crate::{ arch::{ObjArch, ProcessCodeResult}, diff::{DiffObjConfig, X86Formatter}, - obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection}, + obj::{ObjInfo, ObjIns, ObjInsArg, ObjInsArgValue, ObjSection, SymbolRef}, }; pub struct ObjArchX86 { @@ -28,14 +28,16 @@ impl ObjArchX86 { impl ObjArch for ObjArchX86 { fn process_code( &self, + obj: &ObjInfo, + symbol_ref: SymbolRef, config: &DiffObjConfig, - data: &[u8], - start_address: u64, - relocs: &[ObjReloc], - line_info: &Option>, ) -> Result { + let (section, symbol) = obj.section_symbol(symbol_ref); + let code = §ion.data + [symbol.section_address as usize..(symbol.section_address + symbol.size) as usize]; + let mut result = ProcessCodeResult { ops: Vec::new(), insts: Vec::new() }; - let mut decoder = Decoder::with_ip(self.bits, data, start_address, DecoderOptions::NONE); + let mut decoder = Decoder::with_ip(self.bits, code, symbol.address, DecoderOptions::NONE); let mut formatter: Box = match config.x86_formatter { X86Formatter::Intel => Box::new(IntelFormatter::new()), X86Formatter::Gas => Box::new(GasFormatter::new()), @@ -66,7 +68,8 @@ impl ObjArch for ObjArchX86 { let address = instruction.ip(); let op = instruction.mnemonic() as u16; - let reloc = relocs + let reloc = section + .relocations .iter() .find(|r| r.address >= address && r.address < address + instruction.len() as u64); output.ins = ObjIns { @@ -77,7 +80,7 @@ impl ObjArch for ObjArchX86 { args: vec![], reloc: reloc.cloned(), branch_dest: None, - line: line_info.as_ref().and_then(|m| m.get(&address).cloned()), + line: obj.line_info.as_ref().and_then(|m| m.get(&address).cloned()), orig: None, }; // Run the formatter, which will populate output.ins @@ -141,7 +144,9 @@ impl ObjArch for ObjArchX86 { if name.starts_with('?') { msvc_demangler::demangle(name, msvc_demangler::DemangleFlags::llvm()).ok() } else { - None + cpp_demangle::Symbol::new(name) + .ok() + .and_then(|s| s.demangle(&cpp_demangle::DemangleOptions::default()).ok()) } } @@ -150,9 +155,9 @@ impl ObjArch for ObjArchX86 { 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!("")), + _ => Cow::Owned(format!("<{flags:?}>")), }, - flags => Cow::Owned(format!("<{flags:?}>")), + _ => Cow::Owned(format!("<{flags:?}>")), } } } @@ -214,10 +219,10 @@ impl FormatterOutput for InstructionFormatterOutput { self.ins_operands.push(None); match kind { FormatterTextKind::Text | FormatterTextKind::Punctuation => { - self.ins.args.push(ObjInsArg::PlainText(text.to_string())); + self.ins.args.push(ObjInsArg::PlainText(text.to_string().into())); } FormatterTextKind::Keyword | FormatterTextKind::Operator => { - self.ins.args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(text.to_string()))); + self.ins.args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(text.to_string().into()))); } _ => { if self.error.is_none() { @@ -230,7 +235,7 @@ 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()))); + self.ins.args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(text.to_string().into()))); } fn write_mnemonic(&mut self, _instruction: &Instruction, text: &str) { @@ -318,7 +323,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())); + self.ins.args.push(ObjInsArg::PlainText(text.to_string().into())); } fn write_register( @@ -331,6 +336,6 @@ 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()))); + self.ins.args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(text.to_string().into()))); } } diff --git a/objdiff-core/src/config/mod.rs b/objdiff-core/src/config/mod.rs index 33bd421..35d90b0 100644 --- a/objdiff-core/src/config/mod.rs +++ b/objdiff-core/src/config/mod.rs @@ -4,7 +4,7 @@ use std::{ path::{Path, PathBuf}, }; -use anyhow::Result; +use anyhow::{anyhow, Context, Result}; use filetime::FileTime; use globset::{Glob, GlobSet, GlobSetBuilder}; @@ -96,7 +96,7 @@ pub struct ScratchConfig { pub build_ctx: bool, } -pub const CONFIG_FILENAMES: [&str; 3] = ["objdiff.yml", "objdiff.yaml", "objdiff.json"]; +pub const CONFIG_FILENAMES: [&str; 3] = ["objdiff.json", "objdiff.yml", "objdiff.yaml"]; pub const DEFAULT_WATCH_PATTERNS: &[&str] = &[ "*.c", "*.cp", "*.cpp", "*.cxx", "*.h", "*.hp", "*.hpp", "*.hxx", "*.s", "*.S", "*.asm", @@ -121,16 +121,33 @@ pub fn try_project_config(dir: &Path) -> Option<(Result, ProjectC continue; } let ts = FileTime::from_last_modification_time(&metadata); - let config = match filename.contains("json") { + let mut result = match filename.contains("json") { true => read_json_config(&mut file), false => read_yml_config(&mut file), }; - return Some((config, ProjectConfigInfo { path: config_path, timestamp: ts })); + if let Ok(config) = &result { + // Validate min_version if present + if let Err(e) = validate_min_version(config) { + result = Err(e); + } + } + return Some((result, ProjectConfigInfo { path: config_path, timestamp: ts })); } } None } +fn validate_min_version(config: &ProjectConfig) -> Result<()> { + let Some(min_version) = &config.min_version else { return Ok(()) }; + let version = semver::Version::parse(env!("CARGO_PKG_VERSION")) + .context("Failed to parse package version")?; + match semver::VersionReq::parse(&format!(">={min_version}")) { + Ok(version_req) if version_req.matches(&version) => Ok(()), + Ok(_) => Err(anyhow!("Project requires objdiff version {min_version} or higher")), + Err(e) => Err(anyhow::Error::new(e).context("Failed to parse min_version")), + } +} + fn read_yml_config(reader: &mut R) -> Result { Ok(serde_yaml::from_reader(reader)?) } diff --git a/objdiff-core/src/diff/code.rs b/objdiff-core/src/diff/code.rs index 36baf27..00f064c 100644 --- a/objdiff-core/src/diff/code.rs +++ b/objdiff-core/src/diff/code.rs @@ -21,16 +21,7 @@ pub fn no_diff_code( symbol_ref: SymbolRef, config: &DiffObjConfig, ) -> Result { - let (section, symbol) = obj.section_symbol(symbol_ref); - let code = §ion.data - [symbol.section_address as usize..(symbol.section_address + symbol.size) as usize]; - let out = obj.arch.process_code( - config, - code, - symbol.address, - §ion.relocations, - &obj.line_info, - )?; + let out = obj.arch.process_code(obj, symbol_ref, config)?; let mut diff = Vec::::new(); for i in out.insts { @@ -47,26 +38,8 @@ pub fn diff_code( right_symbol_ref: SymbolRef, config: &DiffObjConfig, ) -> Result<(ObjSymbolDiff, ObjSymbolDiff)> { - let (left_section, left_symbol) = left_obj.section_symbol(left_symbol_ref); - let (right_section, right_symbol) = right_obj.section_symbol(right_symbol_ref); - let left_code = &left_section.data[left_symbol.section_address as usize - ..(left_symbol.section_address + left_symbol.size) as usize]; - let right_code = &right_section.data[right_symbol.section_address as usize - ..(right_symbol.section_address + right_symbol.size) as usize]; - let left_out = left_obj.arch.process_code( - config, - left_code, - left_symbol.address, - &left_section.relocations, - &left_obj.line_info, - )?; - let right_out = left_obj.arch.process_code( - config, - right_code, - right_symbol.address, - &right_section.relocations, - &right_obj.line_info, - )?; + let left_out = left_obj.arch.process_code(left_obj, left_symbol_ref, config)?; + let right_out = left_obj.arch.process_code(right_obj, right_symbol_ref, config)?; let mut left_diff = Vec::::new(); let mut right_diff = Vec::::new(); @@ -312,7 +285,7 @@ fn compare_ins( state.diff_count += 1; } let a_str = match a { - ObjInsArg::PlainText(arg) => arg.clone(), + ObjInsArg::PlainText(arg) => arg.to_string(), ObjInsArg::Arg(arg) => arg.to_string(), ObjInsArg::Reloc => String::new(), ObjInsArg::BranchDest(arg) => format!("{arg}"), @@ -326,7 +299,7 @@ fn compare_ins( ObjInsArgDiff { idx } }; let b_str = match b { - ObjInsArg::PlainText(arg) => arg.clone(), + ObjInsArg::PlainText(arg) => arg.to_string(), ObjInsArg::Arg(arg) => arg.to_string(), ObjInsArg::Reloc => String::new(), ObjInsArg::BranchDest(arg) => format!("{arg}"), diff --git a/objdiff-core/src/diff/mod.rs b/objdiff-core/src/diff/mod.rs index 243265c..20a9225 100644 --- a/objdiff-core/src/diff/mod.rs +++ b/objdiff-core/src/diff/mod.rs @@ -31,6 +31,16 @@ pub struct DiffObjConfig { pub x86_formatter: X86Formatter, } +impl DiffObjConfig { + pub fn separator(&self) -> &'static str { + if self.space_between_args { + ", " + } else { + "," + } + } +} + #[derive(Debug, Clone)] pub struct ObjSectionDiff { pub symbols: Vec, diff --git a/objdiff-core/src/obj/mod.rs b/objdiff-core/src/obj/mod.rs index 6264eeb..cfa1d20 100644 --- a/objdiff-core/src/obj/mod.rs +++ b/objdiff-core/src/obj/mod.rs @@ -1,7 +1,7 @@ pub mod read; pub mod split_meta; -use std::{collections::BTreeMap, fmt, path::PathBuf}; +use std::{borrow::Cow, collections::BTreeMap, fmt, path::PathBuf}; use filetime::FileTime; use flagset::{flags, FlagSet}; @@ -35,7 +35,7 @@ pub struct ObjSection { pub address: u64, pub size: u64, pub data: Vec, - pub index: usize, + pub orig_index: usize, pub symbols: Vec, pub relocations: Vec, pub virtual_address: Option, @@ -45,7 +45,7 @@ pub struct ObjSection { pub enum ObjInsArgValue { Signed(i64), Unsigned(u64), - Opaque(String), + Opaque(Cow<'static, str>), } impl ObjInsArgValue { @@ -73,7 +73,7 @@ impl fmt::Display for ObjInsArgValue { #[derive(Debug, Clone, Eq, PartialEq)] pub enum ObjInsArg { - PlainText(String), + PlainText(Cow<'static, str>), Arg(ObjInsArgValue), Reloc, BranchDest(u64), diff --git a/objdiff-core/src/obj/read.rs b/objdiff-core/src/obj/read.rs index e865aed..963c3a5 100644 --- a/objdiff-core/src/obj/read.rs +++ b/objdiff-core/src/obj/read.rs @@ -107,7 +107,7 @@ fn filter_sections(obj_file: &File<'_>, split_meta: Option<&SplitMeta>) -> Resul address: section.address(), size: section.size(), data: data.to_vec(), - index: section.index().0, + orig_index: section.index().0, symbols: Vec::new(), relocations: Vec::new(), virtual_address, @@ -129,7 +129,7 @@ fn symbols_by_section( continue; } if let Some(index) = symbol.section().index() { - if index.0 == section.index { + if index.0 == section.orig_index { if symbol.is_local() && section.kind == ObjSectionKind::Code { // TODO strip local syms in diff? let name = symbol.name().context("Failed to process symbol name")?; @@ -218,7 +218,7 @@ fn relocations_by_section( section: &ObjSection, split_meta: Option<&SplitMeta>, ) -> Result> { - let obj_section = obj_file.section_by_index(SectionIndex(section.index))?; + let obj_section = obj_file.section_by_index(SectionIndex(section.orig_index))?; let mut relocations = Vec::::new(); for (address, reloc) in obj_section.relocations() { let symbol = match reloc.target() { diff --git a/objdiff-gui/Cargo.toml b/objdiff-gui/Cargo.toml index ab3395e..66466bc 100644 --- a/objdiff-gui/Cargo.toml +++ b/objdiff-gui/Cargo.toml @@ -43,15 +43,11 @@ png = "0.17.13" pollster = "0.3.0" rfd = { version = "0.14.0" } #, default-features = false, features = ['xdg-portal'] ron = "0.8.1" -semver = "1.0.22" serde = { version = "1", features = ["derive"] } serde_json = "1.0.114" -serde_yaml = "0.9.32" shell-escape = "0.1.5" tempfile = "3.10.1" -thiserror = "1.0.58" time = { version = "0.3.34", features = ["formatting", "local-offset"] } -toml = "0.8.11" # For Linux static binaries, use rustls [target.'cfg(target_os = "linux")'.dependencies] diff --git a/objdiff-gui/src/config.rs b/objdiff-gui/src/config.rs index c1ef7d0..d6c3ef4 100644 --- a/objdiff-gui/src/config.rs +++ b/objdiff-gui/src/config.rs @@ -1,6 +1,6 @@ use std::path::{Component, Path}; -use anyhow::{ensure, Result}; +use anyhow::Result; use globset::Glob; use objdiff_core::config::{try_project_config, ProjectObject, DEFAULT_WATCH_PATTERNS}; @@ -70,15 +70,6 @@ pub fn load_project_config(config: &mut AppConfig) -> Result<()> { }; if let Some((result, info)) = try_project_config(project_dir) { let project_config = result?; - if let Some(min_version) = &project_config.min_version { - let version_str = env!("CARGO_PKG_VERSION"); - let version = semver::Version::parse(version_str).unwrap(); - let version_req = semver::VersionReq::parse(&format!(">={min_version}"))?; - ensure!( - version_req.matches(&version), - "Project requires objdiff version {min_version} or higher" - ); - } config.custom_make = project_config.custom_make; config.target_obj_dir = project_config.target_dir.map(|p| project_dir.join(p)); config.base_obj_dir = project_config.base_dir.map(|p| project_dir.join(p)); diff --git a/objdiff-gui/src/views/symbol_diff.rs b/objdiff-gui/src/views/symbol_diff.rs index 1ce9081..b751b1d 100644 --- a/objdiff-gui/src/views/symbol_diff.rs +++ b/objdiff-gui/src/views/symbol_diff.rs @@ -287,7 +287,7 @@ fn symbol_list_ui( for (section, section_diff) in obj.0.sections.iter().zip(&obj.1.sections) { CollapsingHeader::new(format!("{} ({:x})", section.name, section.size)) - .id_source(Id::new(section.name.clone()).with(section.index)) + .id_source(Id::new(section.name.clone()).with(section.orig_index)) .default_open(true) .show(ui, |ui| { if section.kind == ObjSectionKind::Code && state.reverse_fn_order {