mirror of
https://github.com/encounter/objdiff.git
synced 2025-12-14 23:56:19 +00:00
Compare commits
5 Commits
v2.0.0-alp
...
v2.0.0-alp
| Author | SHA1 | Date | |
|---|---|---|---|
| 233839346a | |||
| 95615c2ec5 | |||
|
|
97981160f4 | ||
|
|
1fd901a863 | ||
| 759d55994a |
30
Cargo.lock
generated
30
Cargo.lock
generated
@@ -150,6 +150,15 @@ dependencies = [
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arm-attr"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d0cabd3a7d2dfa96ab3faa7b532a83c5e090061bf6d83197ca2bc91f5afac6c"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.7.4"
|
||||
@@ -968,7 +977,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e3d747f100290a1ca24b752186f61f6637e1deffe3bf6320de6fcb29510a307"
|
||||
dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
"libloading 0.7.4",
|
||||
"libloading 0.8.3",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
@@ -2780,7 +2789,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "objdiff-cli"
|
||||
version = "2.0.0-alpha.4"
|
||||
version = "2.0.0-alpha.5"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"argp",
|
||||
@@ -2799,9 +2808,10 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "objdiff-core"
|
||||
version = "2.0.0-alpha.4"
|
||||
version = "2.0.0-alpha.5"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arm-attr",
|
||||
"byteorder",
|
||||
"cpp_demangle",
|
||||
"cwdemangle",
|
||||
@@ -2828,7 +2838,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "objdiff-gui"
|
||||
version = "2.0.0-alpha.4"
|
||||
version = "2.0.0-alpha.5"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@@ -4029,18 +4039,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.59"
|
||||
version = "1.0.61"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa"
|
||||
checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.59"
|
||||
version = "1.0.61"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66"
|
||||
checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -4320,9 +4330,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "unarm"
|
||||
version = "1.0.0"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c13fc9a9c95348bf7565e5c30688fc288239962958cac0ccdc7cd009141d850"
|
||||
checksum = "c6bff109f0171a299559d82a7236e056093fc0dcd2a7da86aa745f82281e2d31"
|
||||
|
||||
[[package]]
|
||||
name = "unicase"
|
||||
|
||||
@@ -17,7 +17,7 @@ Supports:
|
||||
- PowerPC 750CL (GameCube, Wii)
|
||||
- MIPS (N64, PS1, PS2, PSP)
|
||||
- x86 (COFF only at the moment)
|
||||
- ARMv5 (DS)
|
||||
- ARM (GBA, DS, 3DS)
|
||||
|
||||
See [Usage](#usage) for more information.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "objdiff-cli"
|
||||
version = "2.0.0-alpha.4"
|
||||
version = "2.0.0-alpha.5"
|
||||
edition = "2021"
|
||||
rust-version = "1.70"
|
||||
authors = ["Luke Street <luke@street.dev>"]
|
||||
|
||||
@@ -809,23 +809,29 @@ impl FunctionDiffUi {
|
||||
|
||||
fn reload(&mut self) -> Result<()> {
|
||||
let prev = self.right_obj.take();
|
||||
let config = diff::DiffObjConfig {
|
||||
relax_reloc_diffs: self.relax_reloc_diffs,
|
||||
space_between_args: true, // TODO
|
||||
combine_data_sections: false, // TODO
|
||||
x86_formatter: Default::default(), // TODO
|
||||
mips_abi: Default::default(), // TODO
|
||||
mips_instr_category: Default::default(), // TODO
|
||||
arm_arch_version: Default::default(), // TODO
|
||||
};
|
||||
let target = self
|
||||
.target_path
|
||||
.as_deref()
|
||||
.map(|p| obj::read::read(p).with_context(|| format!("Loading {}", p.display())))
|
||||
.map(|p| {
|
||||
obj::read::read(p, &config).with_context(|| format!("Loading {}", p.display()))
|
||||
})
|
||||
.transpose()?;
|
||||
let base = self
|
||||
.base_path
|
||||
.as_deref()
|
||||
.map(|p| obj::read::read(p).with_context(|| format!("Loading {}", p.display())))
|
||||
.map(|p| {
|
||||
obj::read::read(p, &config).with_context(|| format!("Loading {}", p.display()))
|
||||
})
|
||||
.transpose()?;
|
||||
let config = diff::DiffObjConfig {
|
||||
relax_reloc_diffs: self.relax_reloc_diffs,
|
||||
space_between_args: true, // TODO
|
||||
x86_formatter: Default::default(), // TODO
|
||||
mips_abi: Default::default(), // TODO
|
||||
mips_instr_category: Default::default(), // TODO
|
||||
};
|
||||
let result = diff::diff_objs(&config, target.as_ref(), base.as_ref(), prev.as_ref())?;
|
||||
|
||||
let left_sym = target.as_ref().and_then(|o| find_function(o, &self.symbol_name));
|
||||
|
||||
@@ -230,17 +230,21 @@ fn report_object(
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
let config = diff::DiffObjConfig { relax_reloc_diffs: true, ..Default::default() };
|
||||
let target = object
|
||||
.target_path
|
||||
.as_ref()
|
||||
.map(|p| obj::read::read(p).with_context(|| format!("Failed to open {}", p.display())))
|
||||
.map(|p| {
|
||||
obj::read::read(p, &config).with_context(|| format!("Failed to open {}", p.display()))
|
||||
})
|
||||
.transpose()?;
|
||||
let base = object
|
||||
.base_path
|
||||
.as_ref()
|
||||
.map(|p| obj::read::read(p).with_context(|| format!("Failed to open {}", p.display())))
|
||||
.map(|p| {
|
||||
obj::read::read(p, &config).with_context(|| format!("Failed to open {}", p.display()))
|
||||
})
|
||||
.transpose()?;
|
||||
let config = diff::DiffObjConfig { relax_reloc_diffs: true, ..Default::default() };
|
||||
let result = diff::diff_objs(&config, target.as_ref(), base.as_ref(), None)?;
|
||||
let mut unit = ReportUnit {
|
||||
name: object.name().to_string(),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "objdiff-core"
|
||||
version = "2.0.0-alpha.4"
|
||||
version = "2.0.0-alpha.5"
|
||||
edition = "2021"
|
||||
rust-version = "1.70"
|
||||
authors = ["Luke Street <luke@street.dev>"]
|
||||
@@ -19,7 +19,7 @@ dwarf = ["gimli"]
|
||||
mips = ["any-arch", "rabbitizer"]
|
||||
ppc = ["any-arch", "cwdemangle", "ppc750cl"]
|
||||
x86 = ["any-arch", "cpp_demangle", "iced-x86", "msvc-demangler"]
|
||||
arm = ["any-arch", "cpp_demangle", "unarm"]
|
||||
arm = ["any-arch", "cpp_demangle", "unarm", "arm-attr"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.82"
|
||||
@@ -56,4 +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.0.0", optional = true }
|
||||
unarm = { version = "1.3.0", optional = true }
|
||||
arm-attr = { version = "0.1.1", optional = true }
|
||||
|
||||
@@ -4,9 +4,11 @@ use std::{
|
||||
};
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use arm_attr::{enums::CpuArch, tag::Tag, BuildAttrs};
|
||||
use object::{
|
||||
elf, File, Object, ObjectSection, ObjectSymbol, Relocation, RelocationFlags, SectionIndex,
|
||||
SectionKind, Symbol,
|
||||
elf::{self, SHT_ARM_ATTRIBUTES},
|
||||
Endian, File, Object, ObjectSection, ObjectSymbol, Relocation, RelocationFlags, SectionIndex,
|
||||
SectionKind, Symbol, SymbolKind,
|
||||
};
|
||||
use unarm::{
|
||||
args::{Argument, OffsetImm, OffsetReg, Register},
|
||||
@@ -16,41 +18,93 @@ use unarm::{
|
||||
|
||||
use crate::{
|
||||
arch::{ObjArch, ProcessCodeResult},
|
||||
diff::DiffObjConfig,
|
||||
diff::{ArmArchVersion, 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>>,
|
||||
detected_version: Option<ArmVersion>,
|
||||
endianness: object::Endianness,
|
||||
}
|
||||
|
||||
impl ObjArchArm {
|
||||
pub fn new(file: &File) -> Result<Self> {
|
||||
let endianness = file.endianness();
|
||||
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 })
|
||||
let disasm_modes = Self::elf_get_mapping_symbols(file);
|
||||
let detected_version = Self::elf_detect_arm_version(file)?;
|
||||
Ok(Self { disasm_modes, detected_version, endianness })
|
||||
}
|
||||
_ => bail!("Unsupported file format {:?}", file.format()),
|
||||
}
|
||||
}
|
||||
|
||||
fn elf_detect_arm_version(file: &File) -> Result<Option<ArmVersion>> {
|
||||
// Check ARM attributes
|
||||
if let Some(arm_attrs) = file.sections().find(|s| {
|
||||
s.kind() == SectionKind::Elf(SHT_ARM_ATTRIBUTES) && s.name() == Ok(".ARM.attributes")
|
||||
}) {
|
||||
let attr_data = arm_attrs.uncompressed_data()?;
|
||||
let build_attrs = BuildAttrs::new(&attr_data, match file.endianness() {
|
||||
object::Endianness::Little => arm_attr::Endian::Little,
|
||||
object::Endianness::Big => arm_attr::Endian::Big,
|
||||
})?;
|
||||
for subsection in build_attrs.subsections() {
|
||||
let subsection = subsection?;
|
||||
if !subsection.is_aeabi() {
|
||||
continue;
|
||||
}
|
||||
// Only checking first CpuArch tag. Others may exist, but that's very unlikely.
|
||||
let cpu_arch = subsection.into_public_tag_iter()?.find_map(|(_, tag)| {
|
||||
if let Tag::CpuArch(cpu_arch) = tag {
|
||||
Some(cpu_arch)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
match cpu_arch {
|
||||
Some(CpuArch::V4T) => return Ok(Some(ArmVersion::V4T)),
|
||||
Some(CpuArch::V5TE) => return Ok(Some(ArmVersion::V5Te)),
|
||||
Some(CpuArch::V6K) => return Ok(Some(ArmVersion::V6K)),
|
||||
Some(arch) => bail!("ARM arch {} not supported", arch),
|
||||
None => {}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn elf_get_mapping_symbols(file: &File) -> HashMap<SectionIndex, Vec<DisasmMode>> {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjArch for ObjArchArm {
|
||||
fn symbol_address(&self, symbol: &Symbol) -> u64 {
|
||||
let address = symbol.address();
|
||||
if symbol.kind() == SymbolKind::Text {
|
||||
address & !1
|
||||
} else {
|
||||
address
|
||||
}
|
||||
}
|
||||
|
||||
fn process_code(
|
||||
&self,
|
||||
address: u64,
|
||||
@@ -81,10 +135,21 @@ impl ObjArch for ObjArchArm {
|
||||
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 ins_count = code.len() / first_mapping.instruction_size(start_addr);
|
||||
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);
|
||||
|
||||
let version = match config.arm_arch_version {
|
||||
ArmArchVersion::Auto => self.detected_version.unwrap_or(ArmVersion::V5Te),
|
||||
ArmArchVersion::V4T => ArmVersion::V4T,
|
||||
ArmArchVersion::V5TE => ArmVersion::V5Te,
|
||||
ArmArchVersion::V6K => ArmVersion::V6K,
|
||||
};
|
||||
let endian = match self.endianness {
|
||||
object::Endianness::Little => unarm::Endian::Little,
|
||||
object::Endianness::Big => unarm::Endian::Big,
|
||||
};
|
||||
let mut parser = Parser::new(version, first_mapping, start_addr, endian, code);
|
||||
|
||||
while let Some((address, op, ins)) = parser.next() {
|
||||
if let Some(next) = next_mapping {
|
||||
@@ -95,7 +160,6 @@ impl ObjArch for ObjArchArm {
|
||||
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();
|
||||
@@ -103,11 +167,19 @@ impl ObjArch for ObjArchArm {
|
||||
let mut reloc_arg = None;
|
||||
if let Some(reloc) = &reloc {
|
||||
match reloc.flags {
|
||||
// Calls
|
||||
RelocationFlags::Elf { r_type: elf::R_ARM_THM_XPC22 }
|
||||
| RelocationFlags::Elf { r_type: elf::R_ARM_PC24 } => {
|
||||
| RelocationFlags::Elf { r_type: elf::R_ARM_THM_PC22 }
|
||||
| RelocationFlags::Elf { r_type: elf::R_ARM_PC24 }
|
||||
| RelocationFlags::Elf { r_type: elf::R_ARM_XPC25 }
|
||||
| RelocationFlags::Elf { r_type: elf::R_ARM_CALL } => {
|
||||
reloc_arg =
|
||||
ins.args.iter().rposition(|a| matches!(a, Argument::BranchDest(_)));
|
||||
}
|
||||
// Data
|
||||
RelocationFlags::Elf { r_type: elf::R_ARM_ABS32 } => {
|
||||
reloc_arg = ins.args.iter().rposition(|a| matches!(a, Argument::UImm(_)));
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
};
|
||||
@@ -138,11 +210,42 @@ impl ObjArch for ObjArchArm {
|
||||
|
||||
fn implcit_addend(
|
||||
&self,
|
||||
_section: &ObjSection,
|
||||
section: &ObjSection,
|
||||
address: u64,
|
||||
reloc: &Relocation,
|
||||
) -> anyhow::Result<i64> {
|
||||
bail!("Unsupported ARM implicit relocation {:#x}{:?}", address, reloc.flags())
|
||||
let address = address as usize;
|
||||
Ok(match reloc.flags() {
|
||||
// ARM calls
|
||||
RelocationFlags::Elf { r_type: elf::R_ARM_PC24 }
|
||||
| RelocationFlags::Elf { r_type: elf::R_ARM_XPC25 }
|
||||
| RelocationFlags::Elf { r_type: elf::R_ARM_CALL } => {
|
||||
let data = section.data[address..address + 4].try_into()?;
|
||||
let addend = self.endianness.read_i32_bytes(data);
|
||||
let imm24 = addend & 0xffffff;
|
||||
(imm24 << 2) << 8 >> 8
|
||||
}
|
||||
|
||||
// Thumb calls
|
||||
RelocationFlags::Elf { r_type: elf::R_ARM_THM_PC22 }
|
||||
| RelocationFlags::Elf { r_type: elf::R_ARM_THM_XPC22 } => {
|
||||
let data = section.data[address..address + 2].try_into()?;
|
||||
let high = self.endianness.read_i16_bytes(data) as i32;
|
||||
let data = section.data[address + 2..address + 4].try_into()?;
|
||||
let low = self.endianness.read_i16_bytes(data) as i32;
|
||||
|
||||
let imm22 = ((high & 0x7ff) << 11) | (low & 0x7ff);
|
||||
(imm22 << 1) << 9 >> 9
|
||||
}
|
||||
|
||||
// Data
|
||||
RelocationFlags::Elf { r_type: elf::R_ARM_ABS32 } => {
|
||||
let data = section.data[address..address + 4].try_into()?;
|
||||
self.endianness.read_i32_bytes(data)
|
||||
}
|
||||
|
||||
flags => bail!("Unsupported ARM implicit relocation {flags:?}"),
|
||||
} as i64)
|
||||
}
|
||||
|
||||
fn demangle(&self, name: &str) -> Option<String> {
|
||||
@@ -209,6 +312,7 @@ fn push_args(
|
||||
args.push(ObjInsArg::Reloc);
|
||||
} else {
|
||||
match arg {
|
||||
Argument::None => {}
|
||||
Argument::Reg(reg) => {
|
||||
if reg.deref {
|
||||
deref = true;
|
||||
@@ -242,7 +346,7 @@ fn push_args(
|
||||
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque("^".to_string().into())));
|
||||
}
|
||||
}
|
||||
Argument::UImm(value) | Argument::CoOpcode(value) => {
|
||||
Argument::UImm(value) | Argument::CoOpcode(value) | Argument::SatImm(value) => {
|
||||
args.push(ObjInsArg::PlainText("#".into()));
|
||||
args.push(ObjInsArg::Arg(ObjInsArgValue::Unsigned(*value as u64)));
|
||||
}
|
||||
@@ -282,7 +386,21 @@ fn push_args(
|
||||
offset.reg.to_string().into(),
|
||||
)));
|
||||
}
|
||||
_ => args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(arg.to_string().into()))),
|
||||
Argument::CpsrMode(mode) => {
|
||||
args.push(ObjInsArg::PlainText("#".into()));
|
||||
args.push(ObjInsArg::Arg(ObjInsArgValue::Unsigned(mode.mode as u64)));
|
||||
if mode.writeback {
|
||||
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque("!".into())));
|
||||
}
|
||||
}
|
||||
Argument::CoReg(_)
|
||||
| Argument::StatusReg(_)
|
||||
| Argument::StatusMask(_)
|
||||
| Argument::Shift(_)
|
||||
| Argument::CpsrFlags(_)
|
||||
| Argument::Endian(_) => {
|
||||
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(arg.to_string().into())))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,8 +27,8 @@ pub struct ObjArchMips {
|
||||
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;
|
||||
const EF_MIPS_MACH_ALLEGREX: u32 = 0x00840000;
|
||||
const EF_MIPS_MACH_5900: u32 = 0x00920000;
|
||||
|
||||
impl ObjArchMips {
|
||||
pub fn new(object: &File) -> Result<Self> {
|
||||
@@ -38,13 +38,19 @@ impl ObjArchMips {
|
||||
FileFlags::None => {}
|
||||
FileFlags::Elf { e_flags, .. } => {
|
||||
abi = match e_flags & EF_MIPS_ABI {
|
||||
elf::EF_MIPS_ABI_O32 => Abi::O32,
|
||||
elf::EF_MIPS_ABI_O32 | elf::EF_MIPS_ABI_O64 => Abi::O32,
|
||||
elf::EF_MIPS_ABI_EABI32 | elf::EF_MIPS_ABI_EABI64 => Abi::N32,
|
||||
_ => Abi::NUMERIC,
|
||||
_ => {
|
||||
if e_flags & elf::EF_MIPS_ABI2 != 0 {
|
||||
Abi::N32
|
||||
} else {
|
||||
Abi::NUMERIC
|
||||
}
|
||||
}
|
||||
};
|
||||
instr_category = match e_flags & EF_MIPS_MACH {
|
||||
E_MIPS_MACH_ALLEGREX => InstrCategory::R4000ALLEGREX,
|
||||
E_MIPS_MACH_5900 => InstrCategory::R5900,
|
||||
EF_MIPS_MACH_ALLEGREX => InstrCategory::R4000ALLEGREX,
|
||||
EF_MIPS_MACH_5900 => InstrCategory::R5900,
|
||||
_ => InstrCategory::CPU,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::{borrow::Cow, collections::BTreeMap};
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use object::{Architecture, Object, Relocation, RelocationFlags};
|
||||
use object::{Architecture, Object, ObjectSymbol, Relocation, RelocationFlags, Symbol};
|
||||
|
||||
use crate::{
|
||||
diff::DiffObjConfig,
|
||||
@@ -34,6 +34,8 @@ pub trait ObjArch: Send + Sync {
|
||||
fn demangle(&self, _name: &str) -> Option<String> { None }
|
||||
|
||||
fn display_reloc(&self, flags: RelocationFlags) -> Cow<'static, str>;
|
||||
|
||||
fn symbol_address(&self, symbol: &Symbol) -> u64 { symbol.address() }
|
||||
}
|
||||
|
||||
pub struct ProcessCodeResult {
|
||||
|
||||
@@ -93,6 +93,30 @@ pub enum MipsInstrCategory {
|
||||
R5900,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Debug,
|
||||
Copy,
|
||||
Clone,
|
||||
Default,
|
||||
Eq,
|
||||
PartialEq,
|
||||
serde::Deserialize,
|
||||
serde::Serialize,
|
||||
strum::VariantArray,
|
||||
strum::EnumMessage,
|
||||
)]
|
||||
pub enum ArmArchVersion {
|
||||
#[default]
|
||||
#[strum(message = "Auto (default)")]
|
||||
Auto,
|
||||
#[strum(message = "ARMv4T (GBA)")]
|
||||
V4T,
|
||||
#[strum(message = "ARMv5TE (DS)")]
|
||||
V5TE,
|
||||
#[strum(message = "ARMv6K (3DS)")]
|
||||
V6K,
|
||||
}
|
||||
|
||||
#[inline]
|
||||
const fn default_true() -> bool { true }
|
||||
|
||||
@@ -102,11 +126,14 @@ pub struct DiffObjConfig {
|
||||
pub relax_reloc_diffs: bool,
|
||||
#[serde(default = "default_true")]
|
||||
pub space_between_args: bool,
|
||||
pub combine_data_sections: bool,
|
||||
// x86
|
||||
pub x86_formatter: X86Formatter,
|
||||
// MIPS
|
||||
pub mips_abi: MipsAbi,
|
||||
pub mips_instr_category: MipsInstrCategory,
|
||||
// ARM
|
||||
pub arm_arch_version: ArmArchVersion,
|
||||
}
|
||||
|
||||
impl Default for DiffObjConfig {
|
||||
@@ -114,9 +141,11 @@ impl Default for DiffObjConfig {
|
||||
Self {
|
||||
relax_reloc_diffs: false,
|
||||
space_between_args: true,
|
||||
combine_data_sections: false,
|
||||
x86_formatter: Default::default(),
|
||||
mips_abi: Default::default(),
|
||||
mips_instr_category: Default::default(),
|
||||
arm_arch_version: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::{fs, io::Cursor, path::Path};
|
||||
use std::{collections::HashSet, fs, io::Cursor, path::Path};
|
||||
|
||||
use anyhow::{anyhow, bail, ensure, Context, Result};
|
||||
use byteorder::{BigEndian, ReadBytesExt};
|
||||
@@ -11,6 +11,7 @@ use object::{
|
||||
|
||||
use crate::{
|
||||
arch::{new_arch, ObjArch},
|
||||
diff::DiffObjConfig,
|
||||
obj::{
|
||||
split_meta::{SplitMeta, SPLITMETA_SECTION},
|
||||
ObjInfo, ObjReloc, ObjSection, ObjSectionKind, ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags,
|
||||
@@ -54,12 +55,13 @@ fn to_obj_symbol(
|
||||
if obj_file.format() == BinaryFormat::Elf && symbol.scope() == SymbolScope::Linkage {
|
||||
flags = ObjSymbolFlagSet(flags.0 | ObjSymbolFlags::Hidden);
|
||||
}
|
||||
let address = arch.symbol_address(symbol);
|
||||
let section_address = if let Some(section) =
|
||||
symbol.section_index().and_then(|idx| obj_file.section_by_index(idx).ok())
|
||||
{
|
||||
symbol.address() - section.address()
|
||||
address - section.address()
|
||||
} else {
|
||||
symbol.address()
|
||||
address
|
||||
};
|
||||
let demangled_name = arch.demangle(name);
|
||||
// Find the virtual address for the symbol if available
|
||||
@@ -69,7 +71,7 @@ fn to_obj_symbol(
|
||||
Ok(ObjSymbol {
|
||||
name: name.to_string(),
|
||||
demangled_name,
|
||||
address: symbol.address(),
|
||||
address,
|
||||
section_address,
|
||||
size: symbol.size(),
|
||||
size_known: symbol.size() != 0,
|
||||
@@ -361,7 +363,105 @@ fn line_info(obj_file: &File<'_>, sections: &mut [ObjSection]) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn read(obj_path: &Path) -> Result<ObjInfo> {
|
||||
fn update_combined_symbol(symbol: ObjSymbol, address_change: i64) -> Result<ObjSymbol> {
|
||||
Ok(ObjSymbol {
|
||||
name: symbol.name,
|
||||
demangled_name: symbol.demangled_name,
|
||||
address: (symbol.address as i64 + address_change).try_into()?,
|
||||
section_address: (symbol.section_address as i64 + address_change).try_into()?,
|
||||
size: symbol.size,
|
||||
size_known: symbol.size_known,
|
||||
flags: symbol.flags,
|
||||
addend: symbol.addend,
|
||||
virtual_address: if let Some(virtual_address) = symbol.virtual_address {
|
||||
Some((virtual_address as i64 + address_change).try_into()?)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
fn combine_sections(section: ObjSection, combine: ObjSection) -> Result<ObjSection> {
|
||||
let mut data = section.data;
|
||||
data.extend(combine.data);
|
||||
|
||||
let address_change: i64 = (section.address + section.size) as i64 - combine.address as i64;
|
||||
let mut symbols = section.symbols;
|
||||
for symbol in combine.symbols {
|
||||
symbols.push(update_combined_symbol(symbol, address_change)?);
|
||||
}
|
||||
|
||||
let mut relocations = section.relocations;
|
||||
for reloc in combine.relocations {
|
||||
relocations.push(ObjReloc {
|
||||
flags: reloc.flags,
|
||||
address: (reloc.address as i64 + address_change).try_into()?,
|
||||
target: reloc.target, // TODO: Should be updated?
|
||||
target_section: reloc.target_section, // TODO: Same as above
|
||||
});
|
||||
}
|
||||
|
||||
let mut line_info = section.line_info;
|
||||
for (addr, line) in combine.line_info {
|
||||
let key = (addr as i64 + address_change).try_into()?;
|
||||
line_info.insert(key, line);
|
||||
}
|
||||
|
||||
Ok(ObjSection {
|
||||
name: section.name,
|
||||
kind: section.kind,
|
||||
address: section.address,
|
||||
size: section.size + combine.size,
|
||||
data,
|
||||
orig_index: section.orig_index,
|
||||
symbols,
|
||||
relocations,
|
||||
virtual_address: section.virtual_address,
|
||||
line_info,
|
||||
})
|
||||
}
|
||||
|
||||
fn combine_data_sections(sections: &mut Vec<ObjSection>) -> Result<()> {
|
||||
let names_to_combine: HashSet<_> = sections
|
||||
.iter()
|
||||
.filter(|s| s.kind == ObjSectionKind::Data)
|
||||
.map(|s| s.name.clone())
|
||||
.collect();
|
||||
|
||||
for name in names_to_combine {
|
||||
// Take section with lowest index
|
||||
let (mut section_index, _) = sections
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, s)| s.name == name)
|
||||
.min_by_key(|(_, s)| s.orig_index)
|
||||
// Should not happen
|
||||
.context("No combine section found with name")?;
|
||||
let mut section = sections.remove(section_index);
|
||||
|
||||
// Remove equally named sections
|
||||
let mut combines = vec![];
|
||||
for i in (0..sections.len()).rev() {
|
||||
if sections[i].name != name || sections[i].orig_index == section.orig_index {
|
||||
continue;
|
||||
}
|
||||
combines.push(sections.remove(i));
|
||||
if i < section_index {
|
||||
section_index -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Combine sections ordered by index
|
||||
combines.sort_unstable_by_key(|c| c.orig_index);
|
||||
for combine in combines {
|
||||
section = combine_sections(section, combine)?;
|
||||
}
|
||||
sections.insert(section_index, section);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn read(obj_path: &Path, config: &DiffObjConfig) -> Result<ObjInfo> {
|
||||
let (data, timestamp) = {
|
||||
let file = fs::File::open(obj_path)?;
|
||||
let timestamp = FileTime::from_last_modification_time(&file.metadata()?);
|
||||
@@ -377,6 +477,9 @@ pub fn read(obj_path: &Path) -> Result<ObjInfo> {
|
||||
section.relocations =
|
||||
relocations_by_section(arch.as_ref(), &obj_file, section, split_meta.as_ref())?;
|
||||
}
|
||||
if config.combine_data_sections {
|
||||
combine_data_sections(&mut sections)?;
|
||||
}
|
||||
line_info(&obj_file, &mut sections)?;
|
||||
let common = common_symbols(arch.as_ref(), &obj_file, split_meta.as_ref())?;
|
||||
Ok(ObjInfo { arch, path: obj_path.to_owned(), timestamp, sections, common, split_meta })
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "objdiff-gui"
|
||||
version = "2.0.0-alpha.4"
|
||||
version = "2.0.0-alpha.5"
|
||||
edition = "2021"
|
||||
rust-version = "1.70"
|
||||
authors = ["Luke Street <luke@street.dev>"]
|
||||
|
||||
@@ -559,6 +559,16 @@ impl eframe::App for App {
|
||||
{
|
||||
config.queue_reload = true;
|
||||
}
|
||||
if ui
|
||||
.checkbox(
|
||||
&mut config.diff_obj_config.combine_data_sections,
|
||||
"Combine data sections",
|
||||
)
|
||||
.on_hover_text("Combines data sections with equal names.")
|
||||
.changed()
|
||||
{
|
||||
config.queue_reload = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -40,6 +40,7 @@ pub struct BuildConfig {
|
||||
pub project_dir: Option<PathBuf>,
|
||||
pub custom_make: Option<String>,
|
||||
pub custom_args: Option<Vec<String>>,
|
||||
#[allow(unused)]
|
||||
pub selected_wsl_distro: Option<String>,
|
||||
}
|
||||
|
||||
@@ -235,7 +236,7 @@ fn run_build(
|
||||
total,
|
||||
&cancel,
|
||||
)?;
|
||||
Some(read::read(target_path).with_context(|| {
|
||||
Some(read::read(target_path, &config.diff_obj_config).with_context(|| {
|
||||
format!("Failed to read object '{}'", target_path.display())
|
||||
})?)
|
||||
}
|
||||
@@ -252,7 +253,7 @@ fn run_build(
|
||||
&cancel,
|
||||
)?;
|
||||
Some(
|
||||
read::read(base_path)
|
||||
read::read(base_path, &config.diff_obj_config)
|
||||
.with_context(|| format!("Failed to read object '{}'", base_path.display()))?,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ use egui::{
|
||||
use globset::Glob;
|
||||
use objdiff_core::{
|
||||
config::{ProjectObject, DEFAULT_WATCH_PATTERNS},
|
||||
diff::{MipsAbi, MipsInstrCategory, X86Formatter},
|
||||
diff::{ArmArchVersion, MipsAbi, MipsInstrCategory, X86Formatter},
|
||||
};
|
||||
use self_update::cargo_crate_version;
|
||||
use strum::{EnumMessage, VariantArray};
|
||||
@@ -907,4 +907,22 @@ fn arch_config_ui(ui: &mut egui::Ui, config: &mut AppConfig, _appearance: &Appea
|
||||
}
|
||||
}
|
||||
});
|
||||
ui.separator();
|
||||
ui.heading("ARM");
|
||||
egui::ComboBox::new("arm_arch_version", "Architecture Version")
|
||||
.selected_text(config.diff_obj_config.arm_arch_version.get_message().unwrap())
|
||||
.show_ui(ui, |ui| {
|
||||
for &version in ArmArchVersion::VARIANTS {
|
||||
if ui
|
||||
.selectable_label(
|
||||
config.diff_obj_config.arm_arch_version == version,
|
||||
version.get_message().unwrap(),
|
||||
)
|
||||
.clicked()
|
||||
{
|
||||
config.diff_obj_config.arm_arch_version = version;
|
||||
config.queue_reload = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user