mirror of
https://github.com/encounter/objdiff.git
synced 2026-07-10 12:18:36 -07:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9f37d99fbd |
Generated
+730
-755
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -14,7 +14,7 @@ default-members = [
|
||||
resolver = "3"
|
||||
|
||||
[workspace.package]
|
||||
version = "3.4.1"
|
||||
version = "3.3.0"
|
||||
authors = ["Luke Street <luke@street.dev>"]
|
||||
edition = "2024"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
@@ -75,7 +75,6 @@ ignore = [
|
||||
#{ crate = "a-crate-that-is-yanked@0.1.1", reason = "you can specify why you are ignoring the yanked crate" },
|
||||
{ id = "RUSTSEC-2024-0436", reason = "Unmaintained paste crate is an indirect dependency" },
|
||||
{ id = "RUSTSEC-2025-0052", reason = "Unmaintained async-std crate is an indirect dependency" },
|
||||
{ id = "RUSTSEC-2025-0119", reason = "Unmaintained number_prefix crate is an indirect dependency" },
|
||||
]
|
||||
# If this is true, then cargo deny will use the git executable to fetch advisory database.
|
||||
# If this is false, then it uses a built-in git library.
|
||||
|
||||
@@ -16,19 +16,19 @@ publish = false
|
||||
anyhow = "1.0"
|
||||
argp = "0.4"
|
||||
crossterm = "0.29"
|
||||
enable-ansi-support = "0.3"
|
||||
enable-ansi-support = "0.2"
|
||||
memmap2 = "0.9"
|
||||
objdiff-core = { path = "../objdiff-core", features = ["all"] }
|
||||
prost = "0.14"
|
||||
ratatui = "0.29"
|
||||
rayon = "1.11"
|
||||
rayon = "1.10"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
supports-color = "3.0"
|
||||
time = { version = "0.3", features = ["formatting", "local-offset"] }
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
typed-path = "0.12"
|
||||
typed-path = "0.11"
|
||||
|
||||
[target.'cfg(target_env = "musl")'.dependencies]
|
||||
mimalloc = "0.1"
|
||||
|
||||
@@ -378,6 +378,7 @@ impl UiView for FunctionDiffUi {
|
||||
}
|
||||
// Reload
|
||||
KeyCode::Char('r') => {
|
||||
result.redraw = true;
|
||||
return EventControlFlow::Reload;
|
||||
}
|
||||
// Scroll right
|
||||
@@ -399,6 +400,7 @@ impl UiView for FunctionDiffUi {
|
||||
FunctionRelocDiffs::DataValue => FunctionRelocDiffs::All,
|
||||
FunctionRelocDiffs::All => FunctionRelocDiffs::None,
|
||||
};
|
||||
result.redraw = true;
|
||||
return EventControlFlow::Reload;
|
||||
}
|
||||
// Toggle three-way diff
|
||||
|
||||
+10
-10
@@ -132,13 +132,13 @@ itertools = { version = "0.14", default-features = false, features = ["use_alloc
|
||||
log = { version = "0.4", default-features = false, optional = true }
|
||||
memmap2 = { version = "0.9", optional = true }
|
||||
num-traits = { version = "0.2", default-features = false, optional = true }
|
||||
object = { version = "0.37", default-features = false, features = ["read_core", "elf", "coff"] }
|
||||
object = { git = "https://github.com/encounter/object", branch = "omf", default-features = false, features = ["read_core", "elf", "coff", "omf"] }
|
||||
pbjson = { version = "0.8", default-features = false, optional = true }
|
||||
prost = { version = "0.14", default-features = false, features = ["derive"], optional = true }
|
||||
regex = { version = "1.12", default-features = false, features = [], optional = true }
|
||||
regex = { version = "1.11", default-features = false, features = [], optional = true }
|
||||
serde = { version = "1.0", default-features = false, features = ["derive"], optional = true }
|
||||
similar = { git = "https://github.com/encounter/similar.git", branch = "no_std", default-features = false, features = ["hashbrown"], optional = true }
|
||||
typed-path = { version = "0.12", default-features = false, optional = true }
|
||||
typed-path = { version = "0.11", default-features = false, optional = true }
|
||||
|
||||
# config
|
||||
globset = { version = "0.4", default-features = false, optional = true }
|
||||
@@ -155,13 +155,13 @@ powerpc = { version = "0.4", optional = true }
|
||||
rlwinmdec = { version = "1.1", optional = true }
|
||||
|
||||
# mips
|
||||
rabbitizer = { version = "2.0.0-alpha.7", default-features = false, features = ["all_extensions"], optional = true }
|
||||
rabbitizer = { version = "2.0.0-alpha.4", default-features = false, features = ["all_extensions"], optional = true }
|
||||
|
||||
# x86
|
||||
iced-x86 = { version = "1.21", default-features = false, features = ["decoder", "intel", "gas", "masm", "nasm", "exhaustive_enums", "no_std"], optional = true }
|
||||
|
||||
# arm
|
||||
unarm = { version = "2.1", optional = true }
|
||||
unarm = { version = "1.9", optional = true }
|
||||
arm-attr = { version = "0.2", optional = true }
|
||||
|
||||
# arm64
|
||||
@@ -169,17 +169,17 @@ yaxpeax-arch = { version = "0.3", default-features = false, optional = true }
|
||||
yaxpeax-arm = { version = "0.3", default-features = false, optional = true }
|
||||
|
||||
# build
|
||||
notify = { version = "8.2.0", optional = true }
|
||||
notify-debouncer-full = { version = "0.6.0", optional = true }
|
||||
notify = { version = "8.1.0", optional = true }
|
||||
notify-debouncer-full = { version = "0.5.0", optional = true }
|
||||
shell-escape = { version = "0.1", optional = true }
|
||||
tempfile = { version = "3.23", optional = true }
|
||||
tempfile = { version = "3.20", optional = true }
|
||||
time = { version = "0.3", optional = true }
|
||||
encoding_rs = { version = "0.8.35", optional = true }
|
||||
|
||||
# demangler
|
||||
cpp_demangle = { version = "0.5", optional = true, default-features = false, features = ["alloc"] }
|
||||
cpp_demangle = { version = "0.4", optional = true, default-features = false, features = ["alloc"] }
|
||||
cwdemangle = { version = "1.0", optional = true }
|
||||
gnuv2_demangle = { version = "0.4", optional = true }
|
||||
gnuv2_demangle = { version = "0.1.0", optional = true }
|
||||
msvc-demangler = { version = "0.11", optional = true }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
|
||||
@@ -82,27 +82,6 @@
|
||||
"name": "Space between args",
|
||||
"description": "Adds a space between arguments in the diff output."
|
||||
},
|
||||
{
|
||||
"id": "showSymbolSizes",
|
||||
"type": "choice",
|
||||
"default": "off",
|
||||
"name": "Show symbol sizes",
|
||||
"description": "Shows symbol sizes in the symbol view.",
|
||||
"items": [
|
||||
{
|
||||
"value": "off",
|
||||
"name": "Off"
|
||||
},
|
||||
{
|
||||
"value": "hex",
|
||||
"name": "Hex"
|
||||
},
|
||||
{
|
||||
"value": "decimal",
|
||||
"name": "Decimal"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "combineDataSections",
|
||||
"type": "boolean",
|
||||
@@ -128,43 +107,20 @@
|
||||
"value": "auto",
|
||||
"name": "Auto"
|
||||
},
|
||||
{
|
||||
"value": "v4",
|
||||
"name": "ARMv4"
|
||||
},
|
||||
{
|
||||
"value": "v4t",
|
||||
"name": "ARMv4T (GBA)"
|
||||
},
|
||||
{
|
||||
"value": "v5t",
|
||||
"name": "ARMv5T"
|
||||
},
|
||||
{
|
||||
"value": "v5te",
|
||||
"name": "ARMv5TE (DS)"
|
||||
},
|
||||
{
|
||||
"value": "v5tej",
|
||||
"name": "ARMv5TEJ"
|
||||
},
|
||||
{
|
||||
"value": "v6",
|
||||
"name": "ARMv6"
|
||||
},
|
||||
{
|
||||
"value": "v6k",
|
||||
"name": "ARMv6K (3DS)"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "arm.vfpV2",
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"name": "VFPv2 instructions",
|
||||
"description": "Adds floating-point instructions from VFPv2."
|
||||
},
|
||||
{
|
||||
"id": "arm.unifiedSyntax",
|
||||
"type": "boolean",
|
||||
@@ -340,7 +296,6 @@
|
||||
"properties": [
|
||||
"functionRelocDiffs",
|
||||
"demangler",
|
||||
"showSymbolSizes",
|
||||
"spaceBetweenArgs",
|
||||
"combineDataSections",
|
||||
"combineTextSections"
|
||||
@@ -351,7 +306,6 @@
|
||||
"name": "ARM",
|
||||
"properties": [
|
||||
"arm.archVersion",
|
||||
"arm.vfpV2",
|
||||
"arm.unifiedSyntax",
|
||||
"arm.avRegisters",
|
||||
"arm.r9Usage",
|
||||
|
||||
+353
-260
File diff suppressed because it is too large
Load Diff
@@ -293,7 +293,6 @@ impl Arch for ArchMips {
|
||||
}
|
||||
elf::R_MIPS_PC16 => 0, // PC-relative relocation
|
||||
R_MIPS15_S3 => ((code & 0x001FFFC0) >> 3) as i64,
|
||||
elf::R_MIPS_GPREL32 => (code as i32 as i64) + self.ri_gp_value as i64,
|
||||
flags => bail!("Unsupported MIPS implicit relocation {flags:?}"),
|
||||
};
|
||||
Ok(Some(RelocationOverride { target: RelocationOverrideTarget::Keep, addend }))
|
||||
@@ -319,7 +318,6 @@ impl Arch for ArchMips {
|
||||
elf::R_MIPS_GOT16 => Some("R_MIPS_GOT16"),
|
||||
elf::R_MIPS_PC16 => Some("R_MIPS_PC16"),
|
||||
elf::R_MIPS_CALL16 => Some("R_MIPS_CALL16"),
|
||||
elf::R_MIPS_GPREL32 => Some("R_MIPS_GPREL32"),
|
||||
R_MIPS15_S3 => Some("R_MIPS15_S3"),
|
||||
_ => None,
|
||||
},
|
||||
|
||||
@@ -332,6 +332,7 @@ impl Arch for ArchPpc {
|
||||
pe::IMAGE_REL_PPC_PAIR => Some("IMAGE_REL_PPC_PAIR"),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -72,6 +72,7 @@ impl ArchX86 {
|
||||
elf::R_386_16 => Some(2),
|
||||
_ => None,
|
||||
},
|
||||
RelocationFlags::Omf() => Some(4), // TODO
|
||||
},
|
||||
Architecture::X86_64 => match flags {
|
||||
RelocationFlags::Coff(typ) => match typ {
|
||||
@@ -84,6 +85,7 @@ impl ArchX86 {
|
||||
elf::R_X86_64_64 => Some(8),
|
||||
_ => None,
|
||||
},
|
||||
RelocationFlags::Omf() => None, // TODO
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,13 +37,12 @@ pub fn diff_bss_symbol(
|
||||
|
||||
pub fn symbol_name_matches(left_name: &str, right_name: &str) -> bool {
|
||||
// Match Metrowerks symbol$1234 against symbol$2345
|
||||
// and GCC symbol.1234 against symbol.2345
|
||||
if let Some((prefix, suffix)) = left_name.split_once(['$', '.']) {
|
||||
if let Some((prefix, suffix)) = left_name.split_once('$') {
|
||||
if !suffix.chars().all(char::is_numeric) {
|
||||
return false;
|
||||
}
|
||||
right_name
|
||||
.split_once(['$', '.'])
|
||||
.split_once('$')
|
||||
.is_some_and(|(p, s)| p == prefix && s.chars().all(char::is_numeric))
|
||||
} else {
|
||||
left_name == right_name
|
||||
|
||||
@@ -34,12 +34,14 @@ impl Demangler {
|
||||
|
||||
fn demangle_itanium(name: &str) -> Option<String> {
|
||||
let name = name.trim_start_matches('.');
|
||||
cpp_demangle::Symbol::new(name).ok().and_then(|s| s.demangle().ok())
|
||||
cpp_demangle::Symbol::new(name)
|
||||
.ok()
|
||||
.and_then(|s| s.demangle(&cpp_demangle::DemangleOptions::default()).ok())
|
||||
}
|
||||
|
||||
fn demangle_gnu_legacy(name: &str) -> Option<String> {
|
||||
let name = name.trim_start_matches('.');
|
||||
gnuv2_demangle::demangle(name, &gnuv2_demangle::DemangleConfig::new()).ok()
|
||||
gnuv2_demangle::demangle(name, &gnuv2_demangle::DemangleConfig::new_no_cfilt_mimics()).ok()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,401 +0,0 @@
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use anyhow::{Context, Result, bail, ensure};
|
||||
use object::{Endianness, Object, ObjectSection};
|
||||
|
||||
use super::{Section, SectionKind};
|
||||
|
||||
const HDRR_SIZE: usize = 0x60;
|
||||
const FDR_SIZE: usize = 0x48;
|
||||
const PDR_SIZE: usize = 0x34;
|
||||
const SYMR_SIZE: usize = 0x0c;
|
||||
|
||||
const ST_PROC: u8 = 6;
|
||||
const ST_STATICPROC: u8 = 14;
|
||||
const ST_END: u8 = 8;
|
||||
|
||||
pub(super) fn parse_line_info_mdebug(
|
||||
obj_file: &object::File,
|
||||
sections: &mut [Section],
|
||||
) -> Result<()> {
|
||||
let Some(section) = obj_file.section_by_name(".mdebug") else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let data = section.data().context("failed to read .mdebug contents")?;
|
||||
if data.len() < HDRR_SIZE {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let section_file_offset = section.file_range().map(|(offset, _)| offset as usize);
|
||||
|
||||
let endianness = obj_file.endianness();
|
||||
let header = Header::parse(data, endianness)?;
|
||||
|
||||
let symbols_data = slice_at(
|
||||
data,
|
||||
header.cb_sym_offset,
|
||||
header.isym_max.checked_mul(SYMR_SIZE as u32).context("symbol table size overflow")?,
|
||||
section_file_offset,
|
||||
)?;
|
||||
let symbols = parse_symbols(symbols_data, endianness)?;
|
||||
|
||||
let fdr_data = slice_at(
|
||||
data,
|
||||
header.cb_fd_offset,
|
||||
header
|
||||
.ifd_max
|
||||
.checked_mul(FDR_SIZE as u32)
|
||||
.context("file descriptor table size overflow")?,
|
||||
section_file_offset,
|
||||
)?;
|
||||
|
||||
for fdr_index in 0..header.ifd_max as usize {
|
||||
let fdr_offset = fdr_index * FDR_SIZE;
|
||||
let fdr = FileDescriptor::parse(&fdr_data[fdr_offset..fdr_offset + FDR_SIZE], endianness)?;
|
||||
if fdr.cpd == 0 || fdr.csym == 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let sym_base = fdr.isym_base as usize;
|
||||
let sym_end = sym_base + fdr.csym as usize;
|
||||
if sym_end > symbols.len() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let Some(line_file_offset) = header.cb_line_offset.checked_add(fdr.cb_line_offset) else {
|
||||
continue;
|
||||
};
|
||||
let Some(line_file_base) =
|
||||
resolve_offset(line_file_offset, data.len(), section_file_offset)
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
let Some(line_file_end) = line_file_base.checked_add(fdr.cb_line as usize) else {
|
||||
continue;
|
||||
};
|
||||
if line_file_end > data.len() {
|
||||
continue;
|
||||
}
|
||||
|
||||
for local_proc_index in 0..fdr.cpd as usize {
|
||||
let pdr_index = fdr.ipd_first as usize + local_proc_index;
|
||||
let pdr_offset = header
|
||||
.cb_pd_offset
|
||||
.checked_add((pdr_index as u32) * PDR_SIZE as u32)
|
||||
.context("procedure descriptor offset overflow")?;
|
||||
let pdr_data = match slice_at(data, pdr_offset, PDR_SIZE as u32, section_file_offset) {
|
||||
Ok(data) => data,
|
||||
Err(_) => continue,
|
||||
};
|
||||
let pdr = ProcDescriptor::parse(pdr_data, endianness)?;
|
||||
if pdr.isym as usize >= fdr.csym as usize {
|
||||
continue;
|
||||
}
|
||||
let global_sym_index = sym_base + pdr.isym as usize;
|
||||
let Some(start_symbol) = symbols.get(global_sym_index) else {
|
||||
continue;
|
||||
};
|
||||
if start_symbol.st != ST_PROC && start_symbol.st != ST_STATICPROC {
|
||||
continue;
|
||||
}
|
||||
|
||||
let local_index = pdr.isym as u32;
|
||||
let mut end_address = None;
|
||||
for sym in &symbols[global_sym_index..sym_end] {
|
||||
if sym.st == ST_END && sym.index == local_index {
|
||||
end_address = Some(sym.value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
let Some(end_address) = end_address else {
|
||||
continue;
|
||||
};
|
||||
let Some(size) = end_address.checked_sub(start_symbol.value) else {
|
||||
continue;
|
||||
};
|
||||
if size == 0 || size % 4 != 0 {
|
||||
continue;
|
||||
}
|
||||
let word_count = (size / 4) as usize;
|
||||
if word_count == 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let Some(mut cursor) = line_file_base.checked_add(pdr.cb_line_offset as usize) else {
|
||||
continue;
|
||||
};
|
||||
if cursor >= line_file_end {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut line_number = pdr.ln_low as i32;
|
||||
let mut lines = Vec::with_capacity(word_count);
|
||||
while lines.len() < word_count && cursor < line_file_end {
|
||||
let b0 = data[cursor];
|
||||
cursor += 1;
|
||||
let count = (b0 & 0x0f) as usize + 1;
|
||||
let delta = decode_delta(endianness, b0 >> 4, data, &mut cursor, line_file_end)?;
|
||||
line_number = line_number.wrapping_add(delta as i32);
|
||||
for _ in 0..count {
|
||||
if lines.len() == word_count {
|
||||
break;
|
||||
}
|
||||
lines.push(line_number);
|
||||
}
|
||||
}
|
||||
|
||||
if lines.len() != word_count {
|
||||
continue;
|
||||
}
|
||||
|
||||
assign_lines(sections, fdr.adr as u64 + pdr.addr as u64, &lines);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn assign_lines(sections: &mut [Section], base_address: u64, lines: &[i32]) {
|
||||
let mut address = base_address;
|
||||
for &line in lines {
|
||||
if line >= 0
|
||||
&& let Some(section) = find_code_section(sections, address)
|
||||
{
|
||||
section.line_info.insert(address, line as u32);
|
||||
}
|
||||
address = address.wrapping_add(4);
|
||||
}
|
||||
}
|
||||
|
||||
fn find_code_section(sections: &mut [Section], address: u64) -> Option<&mut Section> {
|
||||
sections.iter_mut().find(|section| {
|
||||
section.kind == SectionKind::Code
|
||||
&& address >= section.address
|
||||
&& address < section.address + section.size
|
||||
})
|
||||
}
|
||||
|
||||
fn decode_delta(
|
||||
endianness: Endianness,
|
||||
nibble: u8,
|
||||
data: &[u8],
|
||||
cursor: &mut usize,
|
||||
end: usize,
|
||||
) -> Result<i32> {
|
||||
if nibble == 8 {
|
||||
ensure!(*cursor + 2 <= end, "extended delta out of range");
|
||||
let bytes: [u8; 2] = data[*cursor..*cursor + 2].try_into().unwrap();
|
||||
*cursor += 2;
|
||||
Ok(match endianness {
|
||||
Endianness::Big => i16::from_be_bytes(bytes) as i32,
|
||||
Endianness::Little => i16::from_le_bytes(bytes) as i32,
|
||||
})
|
||||
} else {
|
||||
let mut value = (nibble & 0x0f) as i32;
|
||||
if value & 0x8 != 0 {
|
||||
value -= 0x10;
|
||||
}
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
|
||||
fn slice_at(
|
||||
data: &[u8],
|
||||
offset: u32,
|
||||
size: u32,
|
||||
section_file_offset: Option<usize>,
|
||||
) -> Result<&[u8]> {
|
||||
let size = size as usize;
|
||||
if size == 0 {
|
||||
ensure!(
|
||||
resolve_offset(offset, data.len(), section_file_offset).is_some(),
|
||||
"offset outside of .mdebug section"
|
||||
);
|
||||
return Ok(&data[0..0]);
|
||||
}
|
||||
let Some(offset) = resolve_offset(offset, data.len(), section_file_offset) else {
|
||||
bail!("offset outside of .mdebug section");
|
||||
};
|
||||
let end = offset.checked_add(size).context("range overflow")?;
|
||||
ensure!(end <= data.len(), "range exceeds .mdebug size");
|
||||
Ok(&data[offset..end])
|
||||
}
|
||||
|
||||
fn resolve_offset(
|
||||
offset: u32,
|
||||
data_len: usize,
|
||||
section_file_offset: Option<usize>,
|
||||
) -> Option<usize> {
|
||||
let offset = offset as usize;
|
||||
if offset <= data_len {
|
||||
Some(offset)
|
||||
} else if let Some(file_offset) = section_file_offset {
|
||||
offset.checked_sub(file_offset).filter(|rel| *rel <= data_len)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct Header {
|
||||
cb_line_offset: u32,
|
||||
cb_pd_offset: u32,
|
||||
cb_sym_offset: u32,
|
||||
cb_fd_offset: u32,
|
||||
isym_max: u32,
|
||||
ifd_max: u32,
|
||||
}
|
||||
|
||||
impl Header {
|
||||
fn parse(data: &[u8], endianness: Endianness) -> Result<Self> {
|
||||
ensure!(HDRR_SIZE <= data.len(), ".mdebug header truncated");
|
||||
let mut cursor = 0;
|
||||
let magic = read_u16(data, &mut cursor, endianness)?;
|
||||
let _vstamp = read_u16(data, &mut cursor, endianness)?;
|
||||
ensure!(magic == 0x7009, "unexpected .mdebug magic: {magic:#x}");
|
||||
let _iline_max = read_u32(data, &mut cursor, endianness)?;
|
||||
let _cb_line = read_u32(data, &mut cursor, endianness)?;
|
||||
let cb_line_offset = read_u32(data, &mut cursor, endianness)?;
|
||||
let _idn_max = read_u32(data, &mut cursor, endianness)?;
|
||||
let _cb_dn_offset = read_u32(data, &mut cursor, endianness)?;
|
||||
let _ipd_max = read_u32(data, &mut cursor, endianness)?;
|
||||
let cb_pd_offset = read_u32(data, &mut cursor, endianness)?;
|
||||
let isym_max = read_u32(data, &mut cursor, endianness)?;
|
||||
let cb_sym_offset = read_u32(data, &mut cursor, endianness)?;
|
||||
let _iopt_max = read_u32(data, &mut cursor, endianness)?;
|
||||
let _cb_opt_offset = read_u32(data, &mut cursor, endianness)?;
|
||||
let _iaux_max = read_u32(data, &mut cursor, endianness)?;
|
||||
let _cb_aux_offset = read_u32(data, &mut cursor, endianness)?;
|
||||
let _iss_max = read_u32(data, &mut cursor, endianness)?;
|
||||
let _cb_ss_offset = read_u32(data, &mut cursor, endianness)?;
|
||||
let _iss_ext_max = read_u32(data, &mut cursor, endianness)?;
|
||||
let _cb_ss_ext_offset = read_u32(data, &mut cursor, endianness)?;
|
||||
let ifd_max = read_u32(data, &mut cursor, endianness)?;
|
||||
let cb_fd_offset = read_u32(data, &mut cursor, endianness)?;
|
||||
let _crfd = read_u32(data, &mut cursor, endianness)?;
|
||||
let _cb_rfd_offset = read_u32(data, &mut cursor, endianness)?;
|
||||
let _iext_max = read_u32(data, &mut cursor, endianness)?;
|
||||
let _cb_ext_offset = read_u32(data, &mut cursor, endianness)?;
|
||||
|
||||
Ok(Header { cb_line_offset, cb_pd_offset, cb_sym_offset, cb_fd_offset, isym_max, ifd_max })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct FileDescriptor {
|
||||
adr: u32,
|
||||
isym_base: u32,
|
||||
csym: u32,
|
||||
ipd_first: u16,
|
||||
cpd: u16,
|
||||
cb_line_offset: u32,
|
||||
cb_line: u32,
|
||||
}
|
||||
|
||||
impl FileDescriptor {
|
||||
fn parse(data: &[u8], endianness: Endianness) -> Result<Self> {
|
||||
ensure!(data.len() >= FDR_SIZE, "FDR truncated");
|
||||
let mut cursor = 0;
|
||||
let adr = read_u32(data, &mut cursor, endianness)?;
|
||||
let _rss = read_u32(data, &mut cursor, endianness)?;
|
||||
let _iss_base = read_u32(data, &mut cursor, endianness)?;
|
||||
let _cb_ss = read_u32(data, &mut cursor, endianness)?;
|
||||
let isym_base = read_u32(data, &mut cursor, endianness)?;
|
||||
let csym = read_u32(data, &mut cursor, endianness)?;
|
||||
let _iline_base = read_u32(data, &mut cursor, endianness)?;
|
||||
let _cline = read_u32(data, &mut cursor, endianness)?;
|
||||
let _iopt_base = read_u32(data, &mut cursor, endianness)?;
|
||||
let _copt = read_u32(data, &mut cursor, endianness)?;
|
||||
let ipd_first = read_u16(data, &mut cursor, endianness)?;
|
||||
let cpd = read_u16(data, &mut cursor, endianness)?;
|
||||
let _iaux_base = read_u32(data, &mut cursor, endianness)?;
|
||||
let _caux = read_u32(data, &mut cursor, endianness)?;
|
||||
let _rfd_base = read_u32(data, &mut cursor, endianness)?;
|
||||
let _crfd = read_u32(data, &mut cursor, endianness)?;
|
||||
let _bits = read_u32(data, &mut cursor, endianness)?;
|
||||
let cb_line_offset = read_u32(data, &mut cursor, endianness)?;
|
||||
let cb_line = read_u32(data, &mut cursor, endianness)?;
|
||||
|
||||
Ok(FileDescriptor { adr, isym_base, csym, ipd_first, cpd, cb_line_offset, cb_line })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct ProcDescriptor {
|
||||
addr: u32,
|
||||
isym: u32,
|
||||
ln_low: i32,
|
||||
cb_line_offset: u32,
|
||||
}
|
||||
|
||||
impl ProcDescriptor {
|
||||
fn parse(data: &[u8], endianness: Endianness) -> Result<Self> {
|
||||
ensure!(data.len() >= PDR_SIZE, "PDR truncated");
|
||||
let mut cursor = 0;
|
||||
let addr = read_u32(data, &mut cursor, endianness)?;
|
||||
let isym = read_u32(data, &mut cursor, endianness)?;
|
||||
let _iline = read_u32(data, &mut cursor, endianness)?;
|
||||
let _regmask = read_u32(data, &mut cursor, endianness)?;
|
||||
let _regoffset = read_u32(data, &mut cursor, endianness)?;
|
||||
let _iopt = read_u32(data, &mut cursor, endianness)?;
|
||||
let _fregmask = read_u32(data, &mut cursor, endianness)?;
|
||||
let _fregoffset = read_u32(data, &mut cursor, endianness)?;
|
||||
let _frameoffset = read_u32(data, &mut cursor, endianness)?;
|
||||
let _framereg = read_u16(data, &mut cursor, endianness)?;
|
||||
let _pcreg = read_u16(data, &mut cursor, endianness)?;
|
||||
let ln_low = read_i32(data, &mut cursor, endianness)?;
|
||||
let _ln_high = read_i32(data, &mut cursor, endianness)?;
|
||||
let cb_line_offset = read_u32(data, &mut cursor, endianness)?;
|
||||
|
||||
Ok(ProcDescriptor { addr, isym, ln_low, cb_line_offset })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct SymbolEntry {
|
||||
value: u32,
|
||||
st: u8,
|
||||
index: u32,
|
||||
}
|
||||
|
||||
fn parse_symbols(data: &[u8], endianness: Endianness) -> Result<Vec<SymbolEntry>> {
|
||||
ensure!(data.len().is_multiple_of(SYMR_SIZE), "symbol table misaligned");
|
||||
let mut symbols = Vec::with_capacity(data.len() / SYMR_SIZE);
|
||||
let mut cursor = 0;
|
||||
while cursor + SYMR_SIZE <= data.len() {
|
||||
let _iss = read_u32(data, &mut cursor, endianness)?;
|
||||
let value = read_u32(data, &mut cursor, endianness)?;
|
||||
let bits = read_u32(data, &mut cursor, endianness)?;
|
||||
let (st, index) = match endianness {
|
||||
Endianness::Big => (((bits >> 26) & 0x3f) as u8, bits & 0x000f_ffff),
|
||||
Endianness::Little => (((bits & 0x3f) as u8), (bits >> 12) & 0x000f_ffff),
|
||||
};
|
||||
symbols.push(SymbolEntry { value, st, index });
|
||||
}
|
||||
Ok(symbols)
|
||||
}
|
||||
|
||||
fn read_u16(data: &[u8], cursor: &mut usize, endianness: Endianness) -> Result<u16> {
|
||||
ensure!(*cursor + 2 <= data.len(), "unexpected EOF while reading u16");
|
||||
let bytes: [u8; 2] = data[*cursor..*cursor + 2].try_into().unwrap();
|
||||
*cursor += 2;
|
||||
Ok(match endianness {
|
||||
Endianness::Big => u16::from_be_bytes(bytes),
|
||||
Endianness::Little => u16::from_le_bytes(bytes),
|
||||
})
|
||||
}
|
||||
|
||||
fn read_u32(data: &[u8], cursor: &mut usize, endianness: Endianness) -> Result<u32> {
|
||||
ensure!(*cursor + 4 <= data.len(), "unexpected EOF while reading u32");
|
||||
let bytes: [u8; 4] = data[*cursor..*cursor + 4].try_into().unwrap();
|
||||
*cursor += 4;
|
||||
Ok(match endianness {
|
||||
Endianness::Big => u32::from_be_bytes(bytes),
|
||||
Endianness::Little => u32::from_le_bytes(bytes),
|
||||
})
|
||||
}
|
||||
|
||||
fn read_i32(data: &[u8], cursor: &mut usize, endianness: Endianness) -> Result<i32> {
|
||||
Ok(read_u32(data, cursor, endianness)? as i32)
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
#[cfg(feature = "dwarf")]
|
||||
mod dwarf2;
|
||||
mod mdebug;
|
||||
pub mod read;
|
||||
pub mod split_meta;
|
||||
|
||||
@@ -371,6 +370,7 @@ pub struct Relocation {
|
||||
pub enum RelocationFlags {
|
||||
Elf(u32),
|
||||
Coff(u16),
|
||||
Omf(/* TODO */),
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
|
||||
@@ -510,6 +510,7 @@ fn map_section_relocations(
|
||||
let flags = match reloc.flags() {
|
||||
object::RelocationFlags::Elf { r_type } => RelocationFlags::Elf(r_type),
|
||||
object::RelocationFlags::Coff { typ } => RelocationFlags::Coff(typ),
|
||||
object::RelocationFlags::Omf { .. } => RelocationFlags::Omf(/* TODO */),
|
||||
flags => bail!("Unhandled relocation flags: {:?}", flags),
|
||||
};
|
||||
let target_symbol = match symbol_indices.get(symbol_index.0).copied() {
|
||||
@@ -654,10 +655,6 @@ fn parse_line_info(
|
||||
log::warn!("Failed to parse COFF line info: {e}");
|
||||
}
|
||||
|
||||
if let Err(e) = super::mdebug::parse_line_info_mdebug(obj_file, sections) {
|
||||
log::warn!("Failed to parse MIPS mdebug line info: {e}");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -62,21 +62,3 @@ fn filter_non_matching() {
|
||||
.unwrap();
|
||||
insta::assert_debug_snapshot!(obj.symbols);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "mips")]
|
||||
fn ido_mdebug_line_numbers() {
|
||||
let diff_config = diff::DiffObjConfig::default();
|
||||
let obj = obj::read::parse(
|
||||
include_object!("data/mips/ido_lines_example.o"),
|
||||
&diff_config,
|
||||
diff::DiffSide::Base,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let text_section = obj.sections.iter().find(|s| s.name == ".text").unwrap();
|
||||
assert_eq!(text_section.line_info.get(&0), Some(&6));
|
||||
assert_eq!(text_section.line_info.get(&12), Some(&7));
|
||||
assert_eq!(text_section.line_info.get(&56), Some(&9));
|
||||
assert_eq!(text_section.line_info.len(), 66);
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -2,6 +2,6 @@
|
||||
source: objdiff-core/tests/arch_arm.rs
|
||||
expression: output
|
||||
---
|
||||
[(Line(90), Dim, 5), (Address(0), Dim, 5), (Spacing(4), Normal, 0), (Opcode("ldr", 32792), Normal, 10), (Argument(Opaque("r12")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("pc")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(0)), Normal, 0), (Basic("]"), Normal, 0), (Basic(" (->"), Normal, 0), (BranchDest(8), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(90), Dim, 5), (Address(4), Dim, 5), (Spacing(4), Normal, 0), (Opcode("bx", 32777), Normal, 10), (Argument(Opaque("r12")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(90), Dim, 5), (Address(0), Dim, 5), (Spacing(4), Normal, 0), (Opcode("ldr", 32799), Normal, 10), (Argument(Opaque("r12")), Normal, 0), (Basic(", "), Normal, 0), (Basic("["), Normal, 0), (Argument(Opaque("pc")), Normal, 0), (Basic(", "), Normal, 0), (Basic("#"), Normal, 0), (Argument(Signed(0)), Normal, 0), (Basic("]"), Normal, 0), (Basic(" (->"), Normal, 0), (BranchDest(8), Normal, 0), (Basic(")"), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(90), Dim, 5), (Address(4), Dim, 5), (Spacing(4), Normal, 0), (Opcode("bx", 32779), Normal, 10), (Argument(Opaque("r12")), Normal, 0), (Eol, Normal, 0)]
|
||||
[(Line(90), Dim, 5), (Address(8), Dim, 5), (Spacing(4), Normal, 0), (Opcode(".word", 65534), Normal, 10), (Symbol(Symbol { name: "esEnemyDraw", demangled_name: None, address: 0, size: 0, kind: Unknown, section: None, flags: FlagSet(Global), align: None, virtual_address: None }), Bright, 0), (Eol, Normal, 0)]
|
||||
|
||||
@@ -8,7 +8,7 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 76,
|
||||
size: 4,
|
||||
opcode: 32792,
|
||||
opcode: 32799,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
@@ -22,7 +22,7 @@ expression: diff.instruction_rows
|
||||
InstructionRef {
|
||||
address: 80,
|
||||
size: 4,
|
||||
opcode: 32777,
|
||||
opcode: 32779,
|
||||
branch_dest: None,
|
||||
},
|
||||
),
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user