mirror of
https://github.com/encounter/objdiff.git
synced 2025-12-17 17:05:29 +00:00
Compare commits
13 Commits
v2.0.0-alp
...
v2.0.0-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a5a6a3928e | ||
| fc54e93681 | |||
| c9b11db2fa | |||
|
|
b991960080 | ||
| 425dc8546b | |||
| 9e04357d9f | |||
| 6037c12ad0 | |||
| b15f643713 | |||
| 3f82c1a50f | |||
| 0ea6242669 | |||
| 0c20a0d9cd | |||
| f30b3cfae2 | |||
| 9e57a66a05 |
19
Cargo.lock
generated
19
Cargo.lock
generated
@@ -3081,7 +3081,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "objdiff-cli"
|
name = "objdiff-cli"
|
||||||
version = "2.0.0-alpha.1"
|
version = "2.0.0-alpha.3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"argp",
|
"argp",
|
||||||
@@ -3100,7 +3100,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "objdiff-core"
|
name = "objdiff-core"
|
||||||
version = "2.0.0-alpha.1"
|
version = "2.0.0-alpha.3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
@@ -3123,11 +3123,13 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_yaml",
|
"serde_yaml",
|
||||||
"similar",
|
"similar",
|
||||||
|
"strum",
|
||||||
|
"unarm",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "objdiff-gui"
|
name = "objdiff-gui"
|
||||||
version = "2.0.0-alpha.1"
|
version = "2.0.0-alpha.3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
@@ -3157,6 +3159,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"shell-escape",
|
"shell-escape",
|
||||||
|
"strum",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"time",
|
"time",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
@@ -3543,9 +3546,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rabbitizer"
|
name = "rabbitizer"
|
||||||
version = "1.10.0"
|
version = "1.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f570a07717dcb6edf85e6652d92671c49e8728014c6ec1577a81b4d949725e27"
|
checksum = "305e6fb948a8a884ba996ac4efb1d7b0ee44d0bbfcd86b9c0f0fc0aa0aa0fc21"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"glob",
|
"glob",
|
||||||
@@ -4693,6 +4696,12 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unarm"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1c13fc9a9c95348bf7565e5c30688fc288239962958cac0ccdc7cd009141d850"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicase"
|
name = "unicase"
|
||||||
version = "2.7.0"
|
version = "2.7.0"
|
||||||
|
|||||||
@@ -8,5 +8,7 @@ resolver = "2"
|
|||||||
|
|
||||||
[profile.release-lto]
|
[profile.release-lto]
|
||||||
inherits = "release"
|
inherits = "release"
|
||||||
lto = "thin"
|
# Temporarily disabled to fix notify crash
|
||||||
|
# See https://github.com/encounter/objdiff/issues/66
|
||||||
|
#lto = "thin"
|
||||||
strip = "debuginfo"
|
strip = "debuginfo"
|
||||||
|
|||||||
@@ -14,9 +14,10 @@ Features:
|
|||||||
- Click to highlight all instances of values and registers.
|
- Click to highlight all instances of values and registers.
|
||||||
|
|
||||||
Supports:
|
Supports:
|
||||||
- PowerPC 750CL (GameCube & Wii)
|
- PowerPC 750CL (GameCube, Wii)
|
||||||
- MIPS (Nintendo 64 & PS2)
|
- MIPS (N64, PS1, PS2, PSP)
|
||||||
- x86 (COFF only at the moment)
|
- x86 (COFF only at the moment)
|
||||||
|
- ARMv5 (DS)
|
||||||
|
|
||||||
See [Usage](#usage) for more information.
|
See [Usage](#usage) for more information.
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "objdiff-cli"
|
name = "objdiff-cli"
|
||||||
version = "2.0.0-alpha.1"
|
version = "2.0.0-alpha.3"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.70"
|
rust-version = "1.70"
|
||||||
authors = ["Luke Street <luke@street.dev>"]
|
authors = ["Luke Street <luke@street.dev>"]
|
||||||
|
|||||||
@@ -823,6 +823,8 @@ impl FunctionDiffUi {
|
|||||||
relax_reloc_diffs: self.relax_reloc_diffs,
|
relax_reloc_diffs: self.relax_reloc_diffs,
|
||||||
space_between_args: true, // TODO
|
space_between_args: true, // TODO
|
||||||
x86_formatter: Default::default(), // 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())?;
|
let result = diff::diff_objs(&config, target.as_ref(), base.as_ref(), prev.as_ref())?;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "objdiff-core"
|
name = "objdiff-core"
|
||||||
version = "2.0.0-alpha.1"
|
version = "2.0.0-alpha.3"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.70"
|
rust-version = "1.70"
|
||||||
authors = ["Luke Street <luke@street.dev>"]
|
authors = ["Luke Street <luke@street.dev>"]
|
||||||
@@ -12,13 +12,14 @@ A local diffing tool for decompilation projects.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
all = ["config", "dwarf", "mips", "ppc", "x86"]
|
all = ["config", "dwarf", "mips", "ppc", "x86", "arm"]
|
||||||
any-arch = [] # Implicit, used to check if any arch is enabled
|
any-arch = [] # Implicit, used to check if any arch is enabled
|
||||||
config = ["globset", "semver", "serde_json", "serde_yaml"]
|
config = ["globset", "semver", "serde_json", "serde_yaml"]
|
||||||
dwarf = ["gimli"]
|
dwarf = ["gimli"]
|
||||||
mips = ["any-arch", "rabbitizer"]
|
mips = ["any-arch", "rabbitizer"]
|
||||||
ppc = ["any-arch", "cwdemangle", "ppc750cl"]
|
ppc = ["any-arch", "cwdemangle", "ppc750cl"]
|
||||||
x86 = ["any-arch", "cpp_demangle", "iced-x86", "msvc-demangler"]
|
x86 = ["any-arch", "cpp_demangle", "iced-x86", "msvc-demangler"]
|
||||||
|
arm = ["any-arch", "cpp_demangle", "unarm"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.82"
|
anyhow = "1.0.82"
|
||||||
@@ -31,6 +32,7 @@ num-traits = "0.2.18"
|
|||||||
object = { version = "0.35.0", features = ["read_core", "std", "elf", "pe"], default-features = false }
|
object = { version = "0.35.0", features = ["read_core", "std", "elf", "pe"], default-features = false }
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
similar = { version = "2.5.0", default-features = false }
|
similar = { version = "2.5.0", default-features = false }
|
||||||
|
strum = { version = "0.26.2", features = ["derive"] }
|
||||||
|
|
||||||
# config
|
# config
|
||||||
globset = { version = "0.4.14", features = ["serde1"], optional = true }
|
globset = { version = "0.4.14", features = ["serde1"], optional = true }
|
||||||
@@ -46,9 +48,12 @@ cwdemangle = { version = "1.0.0", optional = true }
|
|||||||
ppc750cl = { git = "https://github.com/encounter/ppc750cl", rev = "6cbd7d888c7082c2c860f66cbb9848d633f753ed", optional = true }
|
ppc750cl = { git = "https://github.com/encounter/ppc750cl", rev = "6cbd7d888c7082c2c860f66cbb9848d633f753ed", optional = true }
|
||||||
|
|
||||||
# mips
|
# mips
|
||||||
rabbitizer = { version = "1.10.0", optional = true }
|
rabbitizer = { version = "1.11.0", optional = true }
|
||||||
|
|
||||||
# x86
|
# x86
|
||||||
cpp_demangle = { version = "0.4.3", optional = true }
|
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 }
|
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 }
|
msvc-demangler = { version = "0.10.0", optional = true }
|
||||||
|
|
||||||
|
# arm
|
||||||
|
unarm = { version = "1.0.0", optional = true }
|
||||||
|
|||||||
296
objdiff-core/src/arch/arm.rs
Normal file
296
objdiff-core/src/arch/arm.rs
Normal file
@@ -0,0 +1,296 @@
|
|||||||
|
use std::{
|
||||||
|
borrow::Cow,
|
||||||
|
collections::{BTreeMap, HashMap},
|
||||||
|
};
|
||||||
|
|
||||||
|
use anyhow::{bail, Result};
|
||||||
|
use object::{
|
||||||
|
elf, File, Object, ObjectSection, ObjectSymbol, Relocation, RelocationFlags, SectionIndex,
|
||||||
|
SectionKind, Symbol,
|
||||||
|
};
|
||||||
|
use unarm::{
|
||||||
|
args::{Argument, OffsetImm, OffsetReg, Register},
|
||||||
|
parse::{ArmVersion, ParseMode, Parser},
|
||||||
|
ParsedIns,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
arch::{ObjArch, ProcessCodeResult},
|
||||||
|
diff::DiffObjConfig,
|
||||||
|
obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct ObjArchArm {
|
||||||
|
/// Maps section index, to list of disasm modes (arm, thumb or data) sorted by address
|
||||||
|
disasm_modes: HashMap<SectionIndex, Vec<DisasmMode>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ObjArchArm {
|
||||||
|
pub fn new(file: &File) -> Result<Self> {
|
||||||
|
match file {
|
||||||
|
File::Elf32(_) => {
|
||||||
|
let disasm_modes: HashMap<_, _> = file
|
||||||
|
.sections()
|
||||||
|
.filter(|s| s.kind() == SectionKind::Text)
|
||||||
|
.map(|s| {
|
||||||
|
let index = s.index();
|
||||||
|
let mut mapping_symbols: Vec<_> = file
|
||||||
|
.symbols()
|
||||||
|
.filter(|s| s.section_index().map(|i| i == index).unwrap_or(false))
|
||||||
|
.filter_map(|s| DisasmMode::from_symbol(&s))
|
||||||
|
.collect();
|
||||||
|
mapping_symbols.sort_unstable_by_key(|x| x.address);
|
||||||
|
(s.index(), mapping_symbols)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
Ok(Self { disasm_modes })
|
||||||
|
}
|
||||||
|
_ => bail!("Unsupported file format {:?}", file.format()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ObjArch for ObjArchArm {
|
||||||
|
fn process_code(
|
||||||
|
&self,
|
||||||
|
address: u64,
|
||||||
|
code: &[u8],
|
||||||
|
section_index: usize,
|
||||||
|
relocations: &[ObjReloc],
|
||||||
|
line_info: &BTreeMap<u64, u64>,
|
||||||
|
config: &DiffObjConfig,
|
||||||
|
) -> Result<ProcessCodeResult> {
|
||||||
|
let start_addr = address as u32;
|
||||||
|
let end_addr = start_addr + code.len() as u32;
|
||||||
|
|
||||||
|
// Mapping symbols decide what kind of data comes after it. $a for ARM code, $t for Thumb code and $d for data.
|
||||||
|
let fallback_mappings = [DisasmMode { address: start_addr, mapping: ParseMode::Arm }];
|
||||||
|
let mapping_symbols = self
|
||||||
|
.disasm_modes
|
||||||
|
.get(&SectionIndex(section_index))
|
||||||
|
.map(|x| x.as_slice())
|
||||||
|
.unwrap_or(&fallback_mappings);
|
||||||
|
let first_mapping_idx =
|
||||||
|
match mapping_symbols.binary_search_by_key(&start_addr, |x| x.address) {
|
||||||
|
Ok(idx) => idx,
|
||||||
|
Err(idx) => idx - 1,
|
||||||
|
};
|
||||||
|
let first_mapping = mapping_symbols[first_mapping_idx].mapping;
|
||||||
|
|
||||||
|
let mut mappings_iter =
|
||||||
|
mapping_symbols.iter().skip(first_mapping_idx + 1).take_while(|x| x.address < end_addr);
|
||||||
|
let mut next_mapping = mappings_iter.next();
|
||||||
|
|
||||||
|
let ins_count = code.len() / first_mapping.instruction_size();
|
||||||
|
let mut ops = Vec::<u16>::with_capacity(ins_count);
|
||||||
|
let mut insts = Vec::<ObjIns>::with_capacity(ins_count);
|
||||||
|
let mut parser = Parser::new(ArmVersion::V5Te, first_mapping, start_addr, code);
|
||||||
|
|
||||||
|
while let Some((address, op, ins)) = parser.next() {
|
||||||
|
if let Some(next) = next_mapping {
|
||||||
|
let next_address = parser.address;
|
||||||
|
if next_address >= next.address {
|
||||||
|
// Change mapping
|
||||||
|
parser.mode = next.mapping;
|
||||||
|
next_mapping = mappings_iter.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let line = line_info.range(..=address as u64).last().map(|(_, &b)| b);
|
||||||
|
|
||||||
|
let reloc = relocations.iter().find(|r| (r.address as u32 & !1) == address).cloned();
|
||||||
|
|
||||||
|
let mut reloc_arg = None;
|
||||||
|
if let Some(reloc) = &reloc {
|
||||||
|
match reloc.flags {
|
||||||
|
RelocationFlags::Elf { r_type: elf::R_ARM_THM_XPC22 }
|
||||||
|
| RelocationFlags::Elf { r_type: elf::R_ARM_PC24 } => {
|
||||||
|
reloc_arg =
|
||||||
|
ins.args.iter().rposition(|a| matches!(a, Argument::BranchDest(_)));
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let (args, branch_dest) = if reloc.is_some() && parser.mode == ParseMode::Data {
|
||||||
|
(vec![ObjInsArg::Reloc], None)
|
||||||
|
} else {
|
||||||
|
push_args(&ins, config, reloc_arg, address)?
|
||||||
|
};
|
||||||
|
|
||||||
|
ops.push(op.id());
|
||||||
|
insts.push(ObjIns {
|
||||||
|
address: address as u64,
|
||||||
|
size: (parser.address - address) as u8,
|
||||||
|
op: op.id(),
|
||||||
|
mnemonic: ins.mnemonic.to_string(),
|
||||||
|
args,
|
||||||
|
reloc,
|
||||||
|
branch_dest,
|
||||||
|
line,
|
||||||
|
formatted: ins.to_string(),
|
||||||
|
orig: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(ProcessCodeResult { ops, insts })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn implcit_addend(
|
||||||
|
&self,
|
||||||
|
_section: &ObjSection,
|
||||||
|
address: u64,
|
||||||
|
reloc: &Relocation,
|
||||||
|
) -> anyhow::Result<i64> {
|
||||||
|
bail!("Unsupported ARM implicit relocation {:#x}{:?}", address, reloc.flags())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn demangle(&self, name: &str) -> Option<String> {
|
||||||
|
cpp_demangle::Symbol::new(name)
|
||||||
|
.ok()
|
||||||
|
.and_then(|s| s.demangle(&cpp_demangle::DemangleOptions::default()).ok())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn display_reloc(&self, flags: RelocationFlags) -> Cow<'static, str> {
|
||||||
|
Cow::Owned(format!("<{flags:?}>"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
struct DisasmMode {
|
||||||
|
address: u32,
|
||||||
|
mapping: ParseMode,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DisasmMode {
|
||||||
|
fn from_symbol<'a>(sym: &Symbol<'a, '_, &'a [u8]>) -> Option<Self> {
|
||||||
|
if let Ok(name) = sym.name() {
|
||||||
|
ParseMode::from_mapping_symbol(name)
|
||||||
|
.map(|mapping| DisasmMode { address: sym.address() as u32, mapping })
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_args(
|
||||||
|
parsed_ins: &ParsedIns,
|
||||||
|
config: &DiffObjConfig,
|
||||||
|
reloc_arg: Option<usize>,
|
||||||
|
cur_addr: u32,
|
||||||
|
) -> Result<(Vec<ObjInsArg>, Option<u64>)> {
|
||||||
|
let mut args = vec![];
|
||||||
|
let mut branch_dest = None;
|
||||||
|
let mut writeback = false;
|
||||||
|
let mut deref = false;
|
||||||
|
for (i, arg) in parsed_ins.args_iter().enumerate() {
|
||||||
|
// Emit punctuation before separator
|
||||||
|
if deref {
|
||||||
|
match arg {
|
||||||
|
Argument::OffsetImm(OffsetImm { post_indexed: true, value: _ })
|
||||||
|
| Argument::OffsetReg(OffsetReg { add: _, post_indexed: true, reg: _ })
|
||||||
|
| Argument::CoOption(_) => {
|
||||||
|
deref = false;
|
||||||
|
args.push(ObjInsArg::PlainText("]".into()));
|
||||||
|
if writeback {
|
||||||
|
writeback = false;
|
||||||
|
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque("!".into())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if i > 0 {
|
||||||
|
args.push(ObjInsArg::PlainText(config.separator().into()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if reloc_arg == Some(i) {
|
||||||
|
args.push(ObjInsArg::Reloc);
|
||||||
|
} else {
|
||||||
|
match arg {
|
||||||
|
Argument::Reg(reg) => {
|
||||||
|
if reg.deref {
|
||||||
|
deref = true;
|
||||||
|
args.push(ObjInsArg::PlainText("[".into()));
|
||||||
|
}
|
||||||
|
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(reg.reg.to_string().into())));
|
||||||
|
if reg.writeback {
|
||||||
|
if reg.deref {
|
||||||
|
writeback = true;
|
||||||
|
} else {
|
||||||
|
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque("!".into())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Argument::RegList(reg_list) => {
|
||||||
|
args.push(ObjInsArg::PlainText("{".into()));
|
||||||
|
let mut first = true;
|
||||||
|
for i in 0..16 {
|
||||||
|
if (reg_list.regs & (1 << i)) != 0 {
|
||||||
|
if !first {
|
||||||
|
args.push(ObjInsArg::PlainText(config.separator().into()));
|
||||||
|
}
|
||||||
|
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(
|
||||||
|
Register::parse(i).to_string().into(),
|
||||||
|
)));
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
args.push(ObjInsArg::PlainText("}".into()));
|
||||||
|
if reg_list.user_mode {
|
||||||
|
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque("^".to_string().into())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Argument::UImm(value) | Argument::CoOpcode(value) => {
|
||||||
|
args.push(ObjInsArg::PlainText("#".into()));
|
||||||
|
args.push(ObjInsArg::Arg(ObjInsArgValue::Unsigned(*value as u64)));
|
||||||
|
}
|
||||||
|
Argument::SImm(value)
|
||||||
|
| Argument::OffsetImm(OffsetImm { post_indexed: _, value }) => {
|
||||||
|
args.push(ObjInsArg::PlainText("#".into()));
|
||||||
|
args.push(ObjInsArg::Arg(ObjInsArgValue::Signed(*value as i64)));
|
||||||
|
}
|
||||||
|
Argument::BranchDest(value) => {
|
||||||
|
let dest = cur_addr.wrapping_add_signed(*value) as u64;
|
||||||
|
args.push(ObjInsArg::BranchDest(dest));
|
||||||
|
branch_dest = Some(dest);
|
||||||
|
}
|
||||||
|
Argument::CoOption(value) => {
|
||||||
|
args.push(ObjInsArg::PlainText("{".into()));
|
||||||
|
args.push(ObjInsArg::Arg(ObjInsArgValue::Unsigned(*value as u64)));
|
||||||
|
args.push(ObjInsArg::PlainText("}".into()));
|
||||||
|
}
|
||||||
|
Argument::CoprocNum(value) => {
|
||||||
|
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(format!("p{}", value).into())));
|
||||||
|
}
|
||||||
|
Argument::ShiftImm(shift) => {
|
||||||
|
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(shift.op.to_string().into())));
|
||||||
|
args.push(ObjInsArg::PlainText(" #".into()));
|
||||||
|
args.push(ObjInsArg::Arg(ObjInsArgValue::Unsigned(shift.imm as u64)));
|
||||||
|
}
|
||||||
|
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())));
|
||||||
|
}
|
||||||
|
Argument::OffsetReg(offset) => {
|
||||||
|
if !offset.add {
|
||||||
|
args.push(ObjInsArg::PlainText("-".into()));
|
||||||
|
}
|
||||||
|
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(
|
||||||
|
offset.reg.to_string().into(),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
_ => args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(arg.to_string().into()))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if deref {
|
||||||
|
args.push(ObjInsArg::PlainText("]".into()));
|
||||||
|
if writeback {
|
||||||
|
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque("!".into())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok((args, branch_dest))
|
||||||
|
}
|
||||||
@@ -1,54 +1,95 @@
|
|||||||
use std::borrow::Cow;
|
use std::{borrow::Cow, collections::BTreeMap, sync::Mutex};
|
||||||
|
|
||||||
use anyhow::{anyhow, bail, Result};
|
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 rabbitizer::{config, Abi, InstrCategory, Instruction, OperandType};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
arch::{ObjArch, ProcessCodeResult},
|
arch::{ObjArch, ProcessCodeResult},
|
||||||
diff::DiffObjConfig,
|
diff::{DiffObjConfig, MipsAbi, MipsInstrCategory},
|
||||||
obj::{ObjInfo, ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection, SymbolRef},
|
obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection},
|
||||||
};
|
};
|
||||||
|
|
||||||
fn configure_rabbitizer() {
|
static RABBITIZER_MUTEX: Mutex<()> = Mutex::new(());
|
||||||
|
|
||||||
|
fn configure_rabbitizer(abi: Abi) {
|
||||||
unsafe {
|
unsafe {
|
||||||
config::RabbitizerConfig_Cfg.reg_names.fpr_abi_names = Abi::O32;
|
config::RabbitizerConfig_Cfg.reg_names.fpr_abi_names = abi;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ObjArchMips {
|
pub struct ObjArchMips {
|
||||||
pub endianness: Endianness,
|
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 {
|
impl ObjArchMips {
|
||||||
pub fn new(object: &File) -> Result<Self> {
|
pub fn new(object: &File) -> Result<Self> {
|
||||||
configure_rabbitizer();
|
let mut abi = Abi::NUMERIC;
|
||||||
Ok(Self { endianness: object.endianness() })
|
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 })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjArch for ObjArchMips {
|
impl ObjArch for ObjArchMips {
|
||||||
fn process_code(
|
fn process_code(
|
||||||
&self,
|
&self,
|
||||||
obj: &ObjInfo,
|
address: u64,
|
||||||
symbol_ref: SymbolRef,
|
code: &[u8],
|
||||||
|
_section_index: usize,
|
||||||
|
relocations: &[ObjReloc],
|
||||||
|
line_info: &BTreeMap<u64, u64>,
|
||||||
config: &DiffObjConfig,
|
config: &DiffObjConfig,
|
||||||
) -> Result<ProcessCodeResult> {
|
) -> Result<ProcessCodeResult> {
|
||||||
let (section, symbol) = obj.section_symbol(symbol_ref);
|
let _guard = RABBITIZER_MUTEX.lock().map_err(|e| anyhow!("Failed to lock mutex: {e}"))?;
|
||||||
let section = section.ok_or_else(|| anyhow!("Code symbol section not found"))?;
|
configure_rabbitizer(match config.mips_abi {
|
||||||
let code = §ion.data
|
MipsAbi::Auto => self.abi,
|
||||||
[symbol.section_address as usize..(symbol.section_address + symbol.size) as usize];
|
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 start_address = address;
|
||||||
let end_address = symbol.address + symbol.size;
|
let end_address = address + code.len() as u64;
|
||||||
let ins_count = code.len() / 4;
|
let ins_count = code.len() / 4;
|
||||||
let mut ops = Vec::<u16>::with_capacity(ins_count);
|
let mut ops = Vec::<u16>::with_capacity(ins_count);
|
||||||
let mut insts = Vec::<ObjIns>::with_capacity(ins_count);
|
let mut insts = Vec::<ObjIns>::with_capacity(ins_count);
|
||||||
let mut cur_addr = start_address as u32;
|
let mut cur_addr = start_address as u32;
|
||||||
for chunk in code.chunks_exact(4) {
|
for chunk in code.chunks_exact(4) {
|
||||||
let reloc = section.relocations.iter().find(|r| (r.address as u32 & !3) == cur_addr);
|
let reloc = relocations.iter().find(|r| (r.address as u32 & !3) == cur_addr);
|
||||||
let code = self.endianness.read_u32_bytes(chunk.try_into()?);
|
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 formatted = instruction.disassemble(None, 0);
|
||||||
let op = instruction.unique_id as u16;
|
let op = instruction.unique_id as u16;
|
||||||
@@ -112,7 +153,7 @@ impl ObjArch for ObjArchMips {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let line = section.line_info.range(..=cur_addr as u64).last().map(|(_, &b)| b);
|
let line = line_info.range(..=cur_addr as u64).last().map(|(_, &b)| b);
|
||||||
insts.push(ObjIns {
|
insts.push(ObjIns {
|
||||||
address: cur_addr as u64,
|
address: cur_addr as u64,
|
||||||
size: 4,
|
size: 4,
|
||||||
|
|||||||
@@ -1,25 +1,30 @@
|
|||||||
use std::borrow::Cow;
|
use std::{borrow::Cow, collections::BTreeMap};
|
||||||
|
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
use object::{Architecture, Object, Relocation, RelocationFlags};
|
use object::{Architecture, Object, Relocation, RelocationFlags};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
diff::DiffObjConfig,
|
diff::DiffObjConfig,
|
||||||
obj::{ObjInfo, ObjIns, ObjSection, SymbolRef},
|
obj::{ObjIns, ObjReloc, ObjSection},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "arm")]
|
||||||
|
mod arm;
|
||||||
#[cfg(feature = "mips")]
|
#[cfg(feature = "mips")]
|
||||||
mod mips;
|
pub mod mips;
|
||||||
#[cfg(feature = "ppc")]
|
#[cfg(feature = "ppc")]
|
||||||
mod ppc;
|
pub mod ppc;
|
||||||
#[cfg(feature = "x86")]
|
#[cfg(feature = "x86")]
|
||||||
mod x86;
|
pub mod x86;
|
||||||
|
|
||||||
pub trait ObjArch: Send + Sync {
|
pub trait ObjArch: Send + Sync {
|
||||||
fn process_code(
|
fn process_code(
|
||||||
&self,
|
&self,
|
||||||
obj: &ObjInfo,
|
address: u64,
|
||||||
symbol_ref: SymbolRef,
|
code: &[u8],
|
||||||
|
section_index: usize,
|
||||||
|
relocations: &[ObjReloc],
|
||||||
|
line_info: &BTreeMap<u64, u64>,
|
||||||
config: &DiffObjConfig,
|
config: &DiffObjConfig,
|
||||||
) -> Result<ProcessCodeResult>;
|
) -> Result<ProcessCodeResult>;
|
||||||
|
|
||||||
@@ -44,6 +49,8 @@ pub fn new_arch(object: &object::File) -> Result<Box<dyn ObjArch>> {
|
|||||||
Architecture::Mips => Box::new(mips::ObjArchMips::new(object)?),
|
Architecture::Mips => Box::new(mips::ObjArchMips::new(object)?),
|
||||||
#[cfg(feature = "x86")]
|
#[cfg(feature = "x86")]
|
||||||
Architecture::I386 | Architecture::X86_64 => Box::new(x86::ObjArchX86::new(object)?),
|
Architecture::I386 | Architecture::X86_64 => Box::new(x86::ObjArchX86::new(object)?),
|
||||||
|
#[cfg(feature = "arm")]
|
||||||
|
Architecture::Arm => Box::new(arm::ObjArchArm::new(object)?),
|
||||||
arch => bail!("Unsupported architecture: {arch:?}"),
|
arch => bail!("Unsupported architecture: {arch:?}"),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
use std::borrow::Cow;
|
use std::{borrow::Cow, collections::BTreeMap};
|
||||||
|
|
||||||
use anyhow::{anyhow, bail, Result};
|
use anyhow::{bail, Result};
|
||||||
use object::{elf, File, Relocation, RelocationFlags};
|
use object::{elf, File, Relocation, RelocationFlags};
|
||||||
use ppc750cl::{Argument, InsIter, GPR};
|
use ppc750cl::{Argument, InsIter, GPR};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
arch::{ObjArch, ProcessCodeResult},
|
arch::{ObjArch, ProcessCodeResult},
|
||||||
diff::DiffObjConfig,
|
diff::DiffObjConfig,
|
||||||
obj::{ObjInfo, ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection, SymbolRef},
|
obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Relative relocation, can be Simm, Offset or BranchDest
|
// Relative relocation, can be Simm, Offset or BranchDest
|
||||||
@@ -31,20 +31,18 @@ impl ObjArchPpc {
|
|||||||
impl ObjArch for ObjArchPpc {
|
impl ObjArch for ObjArchPpc {
|
||||||
fn process_code(
|
fn process_code(
|
||||||
&self,
|
&self,
|
||||||
obj: &ObjInfo,
|
address: u64,
|
||||||
symbol_ref: SymbolRef,
|
code: &[u8],
|
||||||
|
_section_index: usize,
|
||||||
|
relocations: &[ObjReloc],
|
||||||
|
line_info: &BTreeMap<u64, u64>,
|
||||||
config: &DiffObjConfig,
|
config: &DiffObjConfig,
|
||||||
) -> Result<ProcessCodeResult> {
|
) -> Result<ProcessCodeResult> {
|
||||||
let (section, symbol) = obj.section_symbol(symbol_ref);
|
|
||||||
let section = section.ok_or_else(|| anyhow!("Code symbol section not found"))?;
|
|
||||||
let code = §ion.data
|
|
||||||
[symbol.section_address as usize..(symbol.section_address + symbol.size) as usize];
|
|
||||||
|
|
||||||
let ins_count = code.len() / 4;
|
let ins_count = code.len() / 4;
|
||||||
let mut ops = Vec::<u16>::with_capacity(ins_count);
|
let mut ops = Vec::<u16>::with_capacity(ins_count);
|
||||||
let mut insts = Vec::<ObjIns>::with_capacity(ins_count);
|
let mut insts = Vec::<ObjIns>::with_capacity(ins_count);
|
||||||
for (cur_addr, mut ins) in InsIter::new(code, symbol.address as u32) {
|
for (cur_addr, mut ins) in InsIter::new(code, address as u32) {
|
||||||
let reloc = section.relocations.iter().find(|r| (r.address as u32 & !3) == cur_addr);
|
let reloc = relocations.iter().find(|r| (r.address as u32 & !3) == cur_addr);
|
||||||
if let Some(reloc) = reloc {
|
if let Some(reloc) = reloc {
|
||||||
// Zero out relocations
|
// Zero out relocations
|
||||||
ins.code = match reloc.flags {
|
ins.code = match reloc.flags {
|
||||||
@@ -133,7 +131,7 @@ impl ObjArch for ObjArchPpc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ops.push(ins.op as u16);
|
ops.push(ins.op as u16);
|
||||||
let line = section.line_info.range(..=cur_addr as u64).last().map(|(_, &b)| b);
|
let line = line_info.range(..=cur_addr as u64).last().map(|(_, &b)| b);
|
||||||
insts.push(ObjIns {
|
insts.push(ObjIns {
|
||||||
address: cur_addr as u64,
|
address: cur_addr as u64,
|
||||||
size: 4,
|
size: 4,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use std::borrow::Cow;
|
use std::{borrow::Cow, collections::BTreeMap};
|
||||||
|
|
||||||
use anyhow::{anyhow, bail, ensure, Result};
|
use anyhow::{anyhow, bail, ensure, Result};
|
||||||
use iced_x86::{
|
use iced_x86::{
|
||||||
@@ -11,7 +11,7 @@ use object::{pe, Endian, Endianness, File, Object, Relocation, RelocationFlags};
|
|||||||
use crate::{
|
use crate::{
|
||||||
arch::{ObjArch, ProcessCodeResult},
|
arch::{ObjArch, ProcessCodeResult},
|
||||||
diff::{DiffObjConfig, X86Formatter},
|
diff::{DiffObjConfig, X86Formatter},
|
||||||
obj::{ObjInfo, ObjIns, ObjInsArg, ObjInsArgValue, ObjSection, SymbolRef},
|
obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct ObjArchX86 {
|
pub struct ObjArchX86 {
|
||||||
@@ -28,17 +28,15 @@ impl ObjArchX86 {
|
|||||||
impl ObjArch for ObjArchX86 {
|
impl ObjArch for ObjArchX86 {
|
||||||
fn process_code(
|
fn process_code(
|
||||||
&self,
|
&self,
|
||||||
obj: &ObjInfo,
|
address: u64,
|
||||||
symbol_ref: SymbolRef,
|
code: &[u8],
|
||||||
|
_section_index: usize,
|
||||||
|
relocations: &[ObjReloc],
|
||||||
|
line_info: &BTreeMap<u64, u64>,
|
||||||
config: &DiffObjConfig,
|
config: &DiffObjConfig,
|
||||||
) -> Result<ProcessCodeResult> {
|
) -> Result<ProcessCodeResult> {
|
||||||
let (section, symbol) = obj.section_symbol(symbol_ref);
|
|
||||||
let section = section.ok_or_else(|| anyhow!("Code symbol section not found"))?;
|
|
||||||
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 result = ProcessCodeResult { ops: Vec::new(), insts: Vec::new() };
|
||||||
let mut decoder = Decoder::with_ip(self.bits, code, symbol.address, DecoderOptions::NONE);
|
let mut decoder = Decoder::with_ip(self.bits, code, address, DecoderOptions::NONE);
|
||||||
let mut formatter: Box<dyn Formatter> = match config.x86_formatter {
|
let mut formatter: Box<dyn Formatter> = match config.x86_formatter {
|
||||||
X86Formatter::Intel => Box::new(IntelFormatter::new()),
|
X86Formatter::Intel => Box::new(IntelFormatter::new()),
|
||||||
X86Formatter::Gas => Box::new(GasFormatter::new()),
|
X86Formatter::Gas => Box::new(GasFormatter::new()),
|
||||||
@@ -70,11 +68,10 @@ impl ObjArch for ObjArchX86 {
|
|||||||
|
|
||||||
let address = instruction.ip();
|
let address = instruction.ip();
|
||||||
let op = instruction.mnemonic() as u16;
|
let op = instruction.mnemonic() as u16;
|
||||||
let reloc = section
|
let reloc = relocations
|
||||||
.relocations
|
|
||||||
.iter()
|
.iter()
|
||||||
.find(|r| r.address >= address && r.address < address + instruction.len() as u64);
|
.find(|r| r.address >= address && r.address < address + instruction.len() as u64);
|
||||||
let line = section.line_info.range(..=address).last().map(|(_, &b)| b);
|
let line = line_info.range(..=address).last().map(|(_, &b)| b);
|
||||||
output.ins = ObjIns {
|
output.ins = ObjIns {
|
||||||
address,
|
address,
|
||||||
size: instruction.len() as u8,
|
size: instruction.len() as u8,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use std::{
|
|||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::{anyhow, Result};
|
||||||
use similar::{capture_diff_slices_deadline, Algorithm};
|
use similar::{capture_diff_slices_deadline, Algorithm};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -16,34 +16,48 @@ use crate::{
|
|||||||
obj::{ObjInfo, ObjInsArg, ObjReloc, ObjSymbol, ObjSymbolFlags, SymbolRef},
|
obj::{ObjInfo, ObjInsArg, ObjReloc, ObjSymbol, ObjSymbolFlags, SymbolRef},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn no_diff_code(
|
pub fn process_code_symbol(
|
||||||
obj: &ObjInfo,
|
obj: &ObjInfo,
|
||||||
symbol_ref: SymbolRef,
|
symbol_ref: SymbolRef,
|
||||||
config: &DiffObjConfig,
|
config: &DiffObjConfig,
|
||||||
) -> Result<ObjSymbolDiff> {
|
) -> Result<ProcessCodeResult> {
|
||||||
let out = obj.arch.process_code(obj, symbol_ref, config)?;
|
let (section, symbol) = obj.section_symbol(symbol_ref);
|
||||||
|
let section = section.ok_or_else(|| anyhow!("Code symbol section not found"))?;
|
||||||
|
let code = §ion.data
|
||||||
|
[symbol.section_address as usize..(symbol.section_address + symbol.size) as usize];
|
||||||
|
obj.arch.process_code(
|
||||||
|
symbol.address,
|
||||||
|
code,
|
||||||
|
section.orig_index,
|
||||||
|
§ion.relocations,
|
||||||
|
§ion.line_info,
|
||||||
|
config,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn no_diff_code(out: &ProcessCodeResult, symbol_ref: SymbolRef) -> Result<ObjSymbolDiff> {
|
||||||
let mut diff = Vec::<ObjInsDiff>::new();
|
let mut diff = Vec::<ObjInsDiff>::new();
|
||||||
for i in out.insts {
|
for i in &out.insts {
|
||||||
diff.push(ObjInsDiff { ins: Some(i), kind: ObjInsDiffKind::None, ..Default::default() });
|
diff.push(ObjInsDiff {
|
||||||
|
ins: Some(i.clone()),
|
||||||
|
kind: ObjInsDiffKind::None,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
}
|
}
|
||||||
resolve_branches(&mut diff);
|
resolve_branches(&mut diff);
|
||||||
Ok(ObjSymbolDiff { symbol_ref, diff_symbol: None, instructions: diff, match_percent: None })
|
Ok(ObjSymbolDiff { symbol_ref, diff_symbol: None, instructions: diff, match_percent: None })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn diff_code(
|
pub fn diff_code(
|
||||||
left_obj: &ObjInfo,
|
left_out: &ProcessCodeResult,
|
||||||
right_obj: &ObjInfo,
|
right_out: &ProcessCodeResult,
|
||||||
left_symbol_ref: SymbolRef,
|
left_symbol_ref: SymbolRef,
|
||||||
right_symbol_ref: SymbolRef,
|
right_symbol_ref: SymbolRef,
|
||||||
config: &DiffObjConfig,
|
config: &DiffObjConfig,
|
||||||
) -> Result<(ObjSymbolDiff, ObjSymbolDiff)> {
|
) -> Result<(ObjSymbolDiff, ObjSymbolDiff)> {
|
||||||
let left_out = left_obj.arch.process_code(left_obj, left_symbol_ref, config)?;
|
|
||||||
let right_out = right_obj.arch.process_code(right_obj, right_symbol_ref, config)?;
|
|
||||||
|
|
||||||
let mut left_diff = Vec::<ObjInsDiff>::new();
|
let mut left_diff = Vec::<ObjInsDiff>::new();
|
||||||
let mut right_diff = Vec::<ObjInsDiff>::new();
|
let mut right_diff = Vec::<ObjInsDiff>::new();
|
||||||
diff_instructions(&mut left_diff, &mut right_diff, &left_out, &right_out)?;
|
diff_instructions(&mut left_diff, &mut right_diff, left_out, right_out)?;
|
||||||
|
|
||||||
resolve_branches(&mut left_diff);
|
resolve_branches(&mut left_diff);
|
||||||
resolve_branches(&mut right_diff);
|
resolve_branches(&mut right_diff);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use anyhow::Result;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
diff::{
|
diff::{
|
||||||
code::{diff_code, no_diff_code},
|
code::{diff_code, no_diff_code, process_code_symbol},
|
||||||
data::{
|
data::{
|
||||||
diff_bss_section, diff_bss_symbol, diff_data_section, diff_data_symbol,
|
diff_bss_section, diff_bss_symbol, diff_data_section, diff_data_symbol,
|
||||||
diff_text_section, no_diff_symbol,
|
diff_text_section, no_diff_symbol,
|
||||||
@@ -13,29 +13,112 @@ use crate::{
|
|||||||
obj::{ObjInfo, ObjIns, ObjSection, ObjSectionKind, ObjSymbol, SymbolRef},
|
obj::{ObjInfo, ObjIns, ObjSection, ObjSectionKind, ObjSymbol, SymbolRef},
|
||||||
};
|
};
|
||||||
|
|
||||||
mod code;
|
pub mod code;
|
||||||
mod data;
|
pub mod data;
|
||||||
pub mod display;
|
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 {
|
pub enum X86Formatter {
|
||||||
#[default]
|
#[default]
|
||||||
|
#[strum(message = "Intel (default)")]
|
||||||
Intel,
|
Intel,
|
||||||
|
#[strum(message = "AT&T")]
|
||||||
Gas,
|
Gas,
|
||||||
|
#[strum(message = "NASM")]
|
||||||
Nasm,
|
Nasm,
|
||||||
|
#[strum(message = "MASM")]
|
||||||
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]
|
#[inline]
|
||||||
const fn default_true() -> bool { true }
|
const fn default_true() -> bool { true }
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
|
#[derive(Debug, Clone, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct DiffObjConfig {
|
pub struct DiffObjConfig {
|
||||||
pub relax_reloc_diffs: bool,
|
pub relax_reloc_diffs: bool,
|
||||||
#[serde(default = "default_true")]
|
#[serde(default = "default_true")]
|
||||||
pub space_between_args: bool,
|
pub space_between_args: bool,
|
||||||
|
// x86
|
||||||
pub x86_formatter: X86Formatter,
|
pub x86_formatter: X86Formatter,
|
||||||
|
// MIPS
|
||||||
|
pub mips_abi: MipsAbi,
|
||||||
|
pub mips_instr_category: MipsInstrCategory,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for DiffObjConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
relax_reloc_diffs: false,
|
||||||
|
space_between_args: true,
|
||||||
|
x86_formatter: Default::default(),
|
||||||
|
mips_abi: Default::default(),
|
||||||
|
mips_instr_category: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DiffObjConfig {
|
impl DiffObjConfig {
|
||||||
@@ -238,9 +321,11 @@ pub fn diff_objs(
|
|||||||
let (right_obj, right_out) = right.as_mut().unwrap();
|
let (right_obj, right_out) = right.as_mut().unwrap();
|
||||||
match section_kind {
|
match section_kind {
|
||||||
ObjSectionKind::Code => {
|
ObjSectionKind::Code => {
|
||||||
|
let left_code = process_code_symbol(left_obj, left_symbol_ref, config)?;
|
||||||
|
let right_code = process_code_symbol(right_obj, right_symbol_ref, config)?;
|
||||||
let (left_diff, right_diff) = diff_code(
|
let (left_diff, right_diff) = diff_code(
|
||||||
left_obj,
|
&left_code,
|
||||||
right_obj,
|
&right_code,
|
||||||
left_symbol_ref,
|
left_symbol_ref,
|
||||||
right_symbol_ref,
|
right_symbol_ref,
|
||||||
config,
|
config,
|
||||||
@@ -250,9 +335,10 @@ pub fn diff_objs(
|
|||||||
|
|
||||||
if let Some(prev_symbol_ref) = prev_symbol_ref {
|
if let Some(prev_symbol_ref) = prev_symbol_ref {
|
||||||
let (prev_obj, prev_out) = prev.as_mut().unwrap();
|
let (prev_obj, prev_out) = prev.as_mut().unwrap();
|
||||||
|
let prev_code = process_code_symbol(prev_obj, prev_symbol_ref, config)?;
|
||||||
let (_, prev_diff) = diff_code(
|
let (_, prev_diff) = diff_code(
|
||||||
right_obj,
|
&right_code,
|
||||||
prev_obj,
|
&prev_code,
|
||||||
right_symbol_ref,
|
right_symbol_ref,
|
||||||
prev_symbol_ref,
|
prev_symbol_ref,
|
||||||
config,
|
config,
|
||||||
@@ -286,8 +372,9 @@ pub fn diff_objs(
|
|||||||
let (left_obj, left_out) = left.as_mut().unwrap();
|
let (left_obj, left_out) = left.as_mut().unwrap();
|
||||||
match section_kind {
|
match section_kind {
|
||||||
ObjSectionKind::Code => {
|
ObjSectionKind::Code => {
|
||||||
|
let code = process_code_symbol(left_obj, left_symbol_ref, config)?;
|
||||||
*left_out.symbol_diff_mut(left_symbol_ref) =
|
*left_out.symbol_diff_mut(left_symbol_ref) =
|
||||||
no_diff_code(left_obj, left_symbol_ref, config)?;
|
no_diff_code(&code, left_symbol_ref)?;
|
||||||
}
|
}
|
||||||
ObjSectionKind::Data | ObjSectionKind::Bss => {
|
ObjSectionKind::Data | ObjSectionKind::Bss => {
|
||||||
*left_out.symbol_diff_mut(left_symbol_ref) =
|
*left_out.symbol_diff_mut(left_symbol_ref) =
|
||||||
@@ -299,8 +386,9 @@ pub fn diff_objs(
|
|||||||
let (right_obj, right_out) = right.as_mut().unwrap();
|
let (right_obj, right_out) = right.as_mut().unwrap();
|
||||||
match section_kind {
|
match section_kind {
|
||||||
ObjSectionKind::Code => {
|
ObjSectionKind::Code => {
|
||||||
|
let code = process_code_symbol(right_obj, right_symbol_ref, config)?;
|
||||||
*right_out.symbol_diff_mut(right_symbol_ref) =
|
*right_out.symbol_diff_mut(right_symbol_ref) =
|
||||||
no_diff_code(right_obj, right_symbol_ref, config)?;
|
no_diff_code(&code, right_symbol_ref)?;
|
||||||
}
|
}
|
||||||
ObjSectionKind::Data | ObjSectionKind::Bss => {
|
ObjSectionKind::Data | ObjSectionKind::Bss => {
|
||||||
*right_out.symbol_diff_mut(right_symbol_ref) =
|
*right_out.symbol_diff_mut(right_symbol_ref) =
|
||||||
|
|||||||
@@ -328,15 +328,10 @@ fn line_info(obj_file: &File<'_>, sections: &mut [ObjSection]) -> Result<()> {
|
|||||||
if let Some(program) = unit.line_program.clone() {
|
if let Some(program) = unit.line_program.clone() {
|
||||||
let mut text_sections =
|
let mut text_sections =
|
||||||
obj_file.sections().filter(|s| s.kind() == SectionKind::Text);
|
obj_file.sections().filter(|s| s.kind() == SectionKind::Text);
|
||||||
let section_index = text_sections
|
let section_index = text_sections.next().map(|s| s.index().0);
|
||||||
.next()
|
let mut lines = section_index.map(|index| {
|
||||||
.ok_or_else(|| anyhow!("Next text section not found for line info"))?
|
&mut sections.iter_mut().find(|s| s.orig_index == index).unwrap().line_info
|
||||||
.index()
|
});
|
||||||
.0;
|
|
||||||
let mut lines = sections
|
|
||||||
.iter_mut()
|
|
||||||
.find(|s| s.orig_index == section_index)
|
|
||||||
.map(|s| &mut s.line_info);
|
|
||||||
|
|
||||||
let mut rows = program.rows();
|
let mut rows = program.rows();
|
||||||
while let Some((_header, row)) = rows.next_row()? {
|
while let Some((_header, row)) = rows.next_row()? {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "objdiff-gui"
|
name = "objdiff-gui"
|
||||||
version = "2.0.0-alpha.1"
|
version = "2.0.0-alpha.3"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.70"
|
rust-version = "1.70"
|
||||||
authors = ["Luke Street <luke@street.dev>"]
|
authors = ["Luke Street <luke@street.dev>"]
|
||||||
@@ -46,6 +46,7 @@ ron = "0.8.1"
|
|||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = "1.0.116"
|
serde_json = "1.0.116"
|
||||||
shell-escape = "0.1.5"
|
shell-escape = "0.1.5"
|
||||||
|
strum = { version = "0.26.2", features = ["derive"] }
|
||||||
tempfile = "3.10.1"
|
tempfile = "3.10.1"
|
||||||
time = { version = "0.3.36", features = ["formatting", "local-offset"] }
|
time = { version = "0.3.36", features = ["formatting", "local-offset"] }
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ use crate::{
|
|||||||
views::{
|
views::{
|
||||||
appearance::{appearance_window, Appearance},
|
appearance::{appearance_window, Appearance},
|
||||||
config::{
|
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,
|
data_diff::data_diff_ui,
|
||||||
debug::debug_window,
|
debug::debug_window,
|
||||||
@@ -52,7 +52,7 @@ pub struct ViewState {
|
|||||||
pub show_appearance_config: bool,
|
pub show_appearance_config: bool,
|
||||||
pub show_demangle: bool,
|
pub show_demangle: bool,
|
||||||
pub show_project_config: bool,
|
pub show_project_config: bool,
|
||||||
pub show_diff_config: bool,
|
pub show_arch_config: bool,
|
||||||
pub show_debug: bool,
|
pub show_debug: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -414,7 +414,7 @@ impl eframe::App for App {
|
|||||||
show_appearance_config,
|
show_appearance_config,
|
||||||
show_demangle,
|
show_demangle,
|
||||||
show_project_config,
|
show_project_config,
|
||||||
show_diff_config,
|
show_arch_config,
|
||||||
show_debug,
|
show_debug,
|
||||||
} = view_state;
|
} = view_state;
|
||||||
|
|
||||||
@@ -468,8 +468,8 @@ impl eframe::App for App {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
ui.menu_button("Diff Options", |ui| {
|
ui.menu_button("Diff Options", |ui| {
|
||||||
if ui.button("More…").clicked() {
|
if ui.button("Arch Settings…").clicked() {
|
||||||
*show_diff_config = !*show_diff_config;
|
*show_arch_config = !*show_arch_config;
|
||||||
ui.close_menu();
|
ui.close_menu();
|
||||||
}
|
}
|
||||||
let mut config = config.write().unwrap();
|
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);
|
project_window(ctx, config, show_project_config, config_state, appearance);
|
||||||
appearance_window(ctx, show_appearance_config, appearance);
|
appearance_window(ctx, show_appearance_config, appearance);
|
||||||
demangle_window(ctx, show_demangle, demangle_state, 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);
|
debug_window(ctx, show_debug, frame_history, appearance);
|
||||||
|
|
||||||
self.post_update(ctx);
|
self.post_update(ctx);
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ fn run_make_cmd(config: &BuildConfig, cwd: &Path, arg: &Path) -> Result<BuildSta
|
|||||||
cmdline.push(' ');
|
cmdline.push(' ');
|
||||||
cmdline.push_str(shell_escape::escape(arg.to_string_lossy()).as_ref());
|
cmdline.push_str(shell_escape::escape(arg.to_string_lossy()).as_ref());
|
||||||
}
|
}
|
||||||
let output = command.output().context("Failed to execute build")?;
|
let output = command.output().map_err(|e| anyhow!("Failed to execute build: {e}"))?;
|
||||||
let stdout = from_utf8(&output.stdout).context("Failed to process stdout")?;
|
let stdout = from_utf8(&output.stdout).context("Failed to process stdout")?;
|
||||||
let stderr = from_utf8(&output.stderr).context("Failed to process stderr")?;
|
let stderr = from_utf8(&output.stderr).context("Failed to process stderr")?;
|
||||||
Ok(BuildStatus {
|
Ok(BuildStatus {
|
||||||
|
|||||||
@@ -119,6 +119,8 @@ impl Appearance {
|
|||||||
self.delete_color = Color32::from_rgb(200, 40, 41);
|
self.delete_color = Color32::from_rgb(200, 40, 41);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
style.spacing.scroll = egui::style::ScrollStyle::solid();
|
||||||
|
style.spacing.scroll.bar_width = 10.0;
|
||||||
ctx.set_style(style);
|
ctx.set_style(style);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,9 +16,10 @@ use egui::{
|
|||||||
use globset::Glob;
|
use globset::Glob;
|
||||||
use objdiff_core::{
|
use objdiff_core::{
|
||||||
config::{ProjectObject, DEFAULT_WATCH_PATTERNS},
|
config::{ProjectObject, DEFAULT_WATCH_PATTERNS},
|
||||||
diff::X86Formatter,
|
diff::{MipsAbi, MipsInstrCategory, X86Formatter},
|
||||||
};
|
};
|
||||||
use self_update::cargo_crate_version;
|
use self_update::cargo_crate_version;
|
||||||
|
use strum::{EnumMessage, VariantArray};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
app::{AppConfig, AppConfigRef, ObjectConfig},
|
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,
|
ctx: &egui::Context,
|
||||||
config: &AppConfigRef,
|
config: &AppConfigRef,
|
||||||
show: &mut bool,
|
show: &mut bool,
|
||||||
appearance: &Appearance,
|
appearance: &Appearance,
|
||||||
) {
|
) {
|
||||||
let mut config_guard = config.write().unwrap();
|
let mut config_guard = config.write().unwrap();
|
||||||
egui::Window::new("Diff Config").open(show).show(ctx, |ui| {
|
egui::Window::new("Arch Settings").open(show).show(ctx, |ui| {
|
||||||
diff_config_ui(ui, &mut config_guard, appearance);
|
arch_config_ui(ui, &mut config_guard, appearance);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn diff_config_ui(ui: &mut egui::Ui, config: &mut AppConfig, _appearance: &Appearance) {
|
fn arch_config_ui(ui: &mut egui::Ui, config: &mut AppConfig, _appearance: &Appearance) {
|
||||||
egui::ComboBox::new("x86_formatter", "X86 Format")
|
ui.heading("x86");
|
||||||
.selected_text(format!("{:?}", config.diff_obj_config.x86_formatter))
|
egui::ComboBox::new("x86_formatter", "Format")
|
||||||
|
.selected_text(config.diff_obj_config.x86_formatter.get_message().unwrap())
|
||||||
.show_ui(ui, |ui| {
|
.show_ui(ui, |ui| {
|
||||||
for &formatter in
|
for &formatter in X86Formatter::VARIANTS {
|
||||||
&[X86Formatter::Intel, X86Formatter::Gas, X86Formatter::Nasm, X86Formatter::Masm]
|
|
||||||
{
|
|
||||||
if ui
|
if ui
|
||||||
.selectable_label(
|
.selectable_label(
|
||||||
config.diff_obj_config.x86_formatter == formatter,
|
config.diff_obj_config.x86_formatter == formatter,
|
||||||
format!("{:?}", formatter),
|
formatter.get_message().unwrap(),
|
||||||
)
|
)
|
||||||
.clicked()
|
.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user