mirror of
https://github.com/encounter/objdiff.git
synced 2025-12-19 18:05:35 +00:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2ad0898efa | |||
|
|
03f2bcb8b1 | ||
|
|
781071761a | ||
| 572afa8551 |
8
Cargo.lock
generated
8
Cargo.lock
generated
@@ -3484,7 +3484,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "objdiff-cli"
|
name = "objdiff-cli"
|
||||||
version = "3.3.0"
|
version = "3.3.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"argp",
|
"argp",
|
||||||
@@ -3507,7 +3507,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "objdiff-core"
|
name = "objdiff-core"
|
||||||
version = "3.3.0"
|
version = "3.3.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"arm-attr",
|
"arm-attr",
|
||||||
@@ -3563,7 +3563,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "objdiff-gui"
|
name = "objdiff-gui"
|
||||||
version = "3.3.0"
|
version = "3.3.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"argp",
|
"argp",
|
||||||
@@ -3601,7 +3601,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "objdiff-wasm"
|
name = "objdiff-wasm"
|
||||||
version = "3.3.0"
|
version = "3.3.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"objdiff-core",
|
"objdiff-core",
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ default-members = [
|
|||||||
resolver = "3"
|
resolver = "3"
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "3.3.0"
|
version = "3.3.1"
|
||||||
authors = ["Luke Street <luke@street.dev>"]
|
authors = ["Luke Street <luke@street.dev>"]
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
|
|||||||
@@ -82,6 +82,27 @@
|
|||||||
"name": "Space between args",
|
"name": "Space between args",
|
||||||
"description": "Adds a space between arguments in the diff output."
|
"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",
|
"id": "combineDataSections",
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
@@ -296,6 +317,7 @@
|
|||||||
"properties": [
|
"properties": [
|
||||||
"functionRelocDiffs",
|
"functionRelocDiffs",
|
||||||
"demangler",
|
"demangler",
|
||||||
|
"showSymbolSizes",
|
||||||
"spaceBetweenArgs",
|
"spaceBetweenArgs",
|
||||||
"combineDataSections",
|
"combineDataSections",
|
||||||
"combineTextSections"
|
"combineTextSections"
|
||||||
|
|||||||
@@ -293,6 +293,7 @@ impl Arch for ArchMips {
|
|||||||
}
|
}
|
||||||
elf::R_MIPS_PC16 => 0, // PC-relative relocation
|
elf::R_MIPS_PC16 => 0, // PC-relative relocation
|
||||||
R_MIPS15_S3 => ((code & 0x001FFFC0) >> 3) as i64,
|
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:?}"),
|
flags => bail!("Unsupported MIPS implicit relocation {flags:?}"),
|
||||||
};
|
};
|
||||||
Ok(Some(RelocationOverride { target: RelocationOverrideTarget::Keep, addend }))
|
Ok(Some(RelocationOverride { target: RelocationOverrideTarget::Keep, addend }))
|
||||||
@@ -318,6 +319,7 @@ impl Arch for ArchMips {
|
|||||||
elf::R_MIPS_GOT16 => Some("R_MIPS_GOT16"),
|
elf::R_MIPS_GOT16 => Some("R_MIPS_GOT16"),
|
||||||
elf::R_MIPS_PC16 => Some("R_MIPS_PC16"),
|
elf::R_MIPS_PC16 => Some("R_MIPS_PC16"),
|
||||||
elf::R_MIPS_CALL16 => Some("R_MIPS_CALL16"),
|
elf::R_MIPS_CALL16 => Some("R_MIPS_CALL16"),
|
||||||
|
elf::R_MIPS_GPREL32 => Some("R_MIPS_GPREL32"),
|
||||||
R_MIPS15_S3 => Some("R_MIPS15_S3"),
|
R_MIPS15_S3 => Some("R_MIPS15_S3"),
|
||||||
_ => None,
|
_ => None,
|
||||||
},
|
},
|
||||||
|
|||||||
401
objdiff-core/src/obj/mdebug.rs
Normal file
401
objdiff-core/src/obj/mdebug.rs
Normal file
@@ -0,0 +1,401 @@
|
|||||||
|
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,5 +1,6 @@
|
|||||||
#[cfg(feature = "dwarf")]
|
#[cfg(feature = "dwarf")]
|
||||||
mod dwarf2;
|
mod dwarf2;
|
||||||
|
mod mdebug;
|
||||||
pub mod read;
|
pub mod read;
|
||||||
pub mod split_meta;
|
pub mod split_meta;
|
||||||
|
|
||||||
|
|||||||
@@ -654,6 +654,10 @@ fn parse_line_info(
|
|||||||
log::warn!("Failed to parse COFF line info: {e}");
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -62,3 +62,21 @@ fn filter_non_matching() {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
insta::assert_debug_snapshot!(obj.symbols);
|
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);
|
||||||
|
}
|
||||||
|
|||||||
BIN
objdiff-core/tests/data/mips/ido_lines_example.o
Normal file
BIN
objdiff-core/tests/data/mips/ido_lines_example.o
Normal file
Binary file not shown.
@@ -6,7 +6,7 @@ use objdiff_core::{
|
|||||||
config::ScratchConfig,
|
config::ScratchConfig,
|
||||||
diff::{
|
diff::{
|
||||||
ArmArchVersion, ArmR9Usage, DiffObjConfig, FunctionRelocDiffs, MipsAbi, MipsInstrCategory,
|
ArmArchVersion, ArmR9Usage, DiffObjConfig, FunctionRelocDiffs, MipsAbi, MipsInstrCategory,
|
||||||
X86Formatter,
|
ShowSymbolSizes, X86Formatter,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use typed_path::{Utf8PlatformPathBuf, Utf8UnixPathBuf};
|
use typed_path::{Utf8PlatformPathBuf, Utf8UnixPathBuf};
|
||||||
@@ -227,6 +227,7 @@ pub struct DiffObjConfigV1 {
|
|||||||
pub relax_reloc_diffs: bool,
|
pub relax_reloc_diffs: bool,
|
||||||
#[serde(default = "bool_true")]
|
#[serde(default = "bool_true")]
|
||||||
pub space_between_args: bool,
|
pub space_between_args: bool,
|
||||||
|
pub show_symbol_sizes: ShowSymbolSizes,
|
||||||
pub combine_data_sections: bool,
|
pub combine_data_sections: bool,
|
||||||
// x86
|
// x86
|
||||||
pub x86_formatter: X86Formatter,
|
pub x86_formatter: X86Formatter,
|
||||||
@@ -248,6 +249,7 @@ impl Default for DiffObjConfigV1 {
|
|||||||
Self {
|
Self {
|
||||||
relax_reloc_diffs: false,
|
relax_reloc_diffs: false,
|
||||||
space_between_args: true,
|
space_between_args: true,
|
||||||
|
show_symbol_sizes: Default::default(),
|
||||||
combine_data_sections: false,
|
combine_data_sections: false,
|
||||||
x86_formatter: Default::default(),
|
x86_formatter: Default::default(),
|
||||||
mips_abi: Default::default(),
|
mips_abi: Default::default(),
|
||||||
@@ -272,6 +274,7 @@ impl DiffObjConfigV1 {
|
|||||||
FunctionRelocDiffs::default()
|
FunctionRelocDiffs::default()
|
||||||
},
|
},
|
||||||
space_between_args: self.space_between_args,
|
space_between_args: self.space_between_args,
|
||||||
|
show_symbol_sizes: self.show_symbol_sizes,
|
||||||
combine_data_sections: self.combine_data_sections,
|
combine_data_sections: self.combine_data_sections,
|
||||||
x86_formatter: self.x86_formatter,
|
x86_formatter: self.x86_formatter,
|
||||||
mips_abi: self.mips_abi,
|
mips_abi: self.mips_abi,
|
||||||
|
|||||||
@@ -665,6 +665,7 @@ fn diff_col_ui(
|
|||||||
appearance,
|
appearance,
|
||||||
column,
|
column,
|
||||||
open_sections,
|
open_sections,
|
||||||
|
diff_config,
|
||||||
) {
|
) {
|
||||||
match (column, action) {
|
match (column, action) {
|
||||||
(
|
(
|
||||||
@@ -703,6 +704,7 @@ fn diff_col_ui(
|
|||||||
appearance,
|
appearance,
|
||||||
column,
|
column,
|
||||||
open_sections,
|
open_sections,
|
||||||
|
diff_config,
|
||||||
) {
|
) {
|
||||||
ret = Some(result);
|
ret = Some(result);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use egui::{
|
|||||||
};
|
};
|
||||||
use objdiff_core::{
|
use objdiff_core::{
|
||||||
diff::{
|
diff::{
|
||||||
ObjectDiff, SymbolDiff,
|
DiffObjConfig, ObjectDiff, ShowSymbolSizes, SymbolDiff,
|
||||||
display::{
|
display::{
|
||||||
HighlightKind, SectionDisplay, SymbolFilter, SymbolNavigationKind, display_sections,
|
HighlightKind, SectionDisplay, SymbolFilter, SymbolNavigationKind, display_sections,
|
||||||
symbol_context, symbol_hover,
|
symbol_context, symbol_hover,
|
||||||
@@ -525,6 +525,7 @@ fn symbol_ui(
|
|||||||
state: &SymbolViewState,
|
state: &SymbolViewState,
|
||||||
appearance: &Appearance,
|
appearance: &Appearance,
|
||||||
column: usize,
|
column: usize,
|
||||||
|
diff_config: &DiffObjConfig,
|
||||||
) -> Option<DiffViewAction> {
|
) -> Option<DiffViewAction> {
|
||||||
let mut ret = None;
|
let mut ret = None;
|
||||||
let mut job = LayoutJob::default();
|
let mut job = LayoutJob::default();
|
||||||
@@ -572,6 +573,21 @@ fn symbol_ui(
|
|||||||
write_text(") ", appearance.text_color, &mut job, appearance.code_font.clone());
|
write_text(") ", appearance.text_color, &mut job, appearance.code_font.clone());
|
||||||
}
|
}
|
||||||
write_text(name, appearance.highlight_color, &mut job, appearance.code_font.clone());
|
write_text(name, appearance.highlight_color, &mut job, appearance.code_font.clone());
|
||||||
|
if diff_config.show_symbol_sizes == ShowSymbolSizes::Decimal {
|
||||||
|
write_text(
|
||||||
|
&format!(" (size={})", symbol.size),
|
||||||
|
appearance.deemphasized_text_color,
|
||||||
|
&mut job,
|
||||||
|
appearance.code_font.clone(),
|
||||||
|
);
|
||||||
|
} else if diff_config.show_symbol_sizes == ShowSymbolSizes::Hex {
|
||||||
|
write_text(
|
||||||
|
&format!(" (size={:x})", symbol.size),
|
||||||
|
appearance.deemphasized_text_color,
|
||||||
|
&mut job,
|
||||||
|
appearance.code_font.clone(),
|
||||||
|
);
|
||||||
|
}
|
||||||
let response = egui::Button::selectable(selected, job)
|
let response = egui::Button::selectable(selected, job)
|
||||||
.ui(ui)
|
.ui(ui)
|
||||||
.on_hover_ui_at_pointer(|ui| symbol_hover_ui(ui, ctx, symbol_idx, appearance));
|
.on_hover_ui_at_pointer(|ui| symbol_hover_ui(ui, ctx, symbol_idx, appearance));
|
||||||
@@ -646,6 +662,7 @@ pub fn symbol_list_ui(
|
|||||||
appearance: &Appearance,
|
appearance: &Appearance,
|
||||||
column: usize,
|
column: usize,
|
||||||
open_sections: Option<bool>,
|
open_sections: Option<bool>,
|
||||||
|
diff_config: &DiffObjConfig,
|
||||||
) -> Option<DiffViewAction> {
|
) -> Option<DiffViewAction> {
|
||||||
let mut ret = None;
|
let mut ret = None;
|
||||||
ScrollArea::both().auto_shrink([false, false]).show(ui, |ui| {
|
ScrollArea::both().auto_shrink([false, false]).show(ui, |ui| {
|
||||||
@@ -770,6 +787,7 @@ pub fn symbol_list_ui(
|
|||||||
state,
|
state,
|
||||||
appearance,
|
appearance,
|
||||||
column,
|
column,
|
||||||
|
diff_config,
|
||||||
) {
|
) {
|
||||||
ret = Some(result);
|
ret = Some(result);
|
||||||
}
|
}
|
||||||
|
|||||||
8
objdiff-wasm/package-lock.json
generated
8
objdiff-wasm/package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "objdiff-wasm",
|
"name": "objdiff-wasm",
|
||||||
"version": "3.3.0",
|
"version": "3.3.1",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "objdiff-wasm",
|
"name": "objdiff-wasm",
|
||||||
"version": "3.3.0",
|
"version": "3.3.1",
|
||||||
"license": "MIT OR Apache-2.0",
|
"license": "MIT OR Apache-2.0",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "^1.9.3",
|
"@biomejs/biome": "^1.9.3",
|
||||||
@@ -852,6 +852,7 @@
|
|||||||
"integrity": "sha512-Y3b4Y7lGIvNPywspP38deHkp/EEkTXrJEHeX1K5yz8U/94PkWvPlDebjjiSvmI6TT+9iVzsq22qDlBEdDuJZhA==",
|
"integrity": "sha512-Y3b4Y7lGIvNPywspP38deHkp/EEkTXrJEHeX1K5yz8U/94PkWvPlDebjjiSvmI6TT+9iVzsq22qDlBEdDuJZhA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@rspack/core": "1.2.3",
|
"@rspack/core": "1.2.3",
|
||||||
"@rspack/lite-tapable": "~1.0.1",
|
"@rspack/lite-tapable": "~1.0.1",
|
||||||
@@ -1083,6 +1084,7 @@
|
|||||||
"integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==",
|
"integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": "^2.8.0"
|
"tslib": "^2.8.0"
|
||||||
}
|
}
|
||||||
@@ -2139,6 +2141,7 @@
|
|||||||
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
|
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
@@ -2166,6 +2169,7 @@
|
|||||||
"integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==",
|
"integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "objdiff-wasm",
|
"name": "objdiff-wasm",
|
||||||
"version": "3.3.0",
|
"version": "3.3.1",
|
||||||
"description": "A local diffing tool for decompilation projects.",
|
"description": "A local diffing tool for decompilation projects.",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Luke Street",
|
"name": "Luke Street",
|
||||||
|
|||||||
Reference in New Issue
Block a user