From 9bd43d55b6826fb51d233a160f5acb27666a5c75 Mon Sep 17 00:00:00 2001 From: dbalatoni13 <40299962+dbalatoni13@users.noreply.github.com> Date: Sun, 7 Jun 2026 22:09:34 +0200 Subject: [PATCH] Make the .mdebug line parser support EE-GCC (PS2) --- objdiff-core/src/obj/mdebug.rs | 98 +++++++++++++++--- objdiff-core/tests/arch_mips.rs | 25 +++++ .../tests/data/mips/ee_gcc_lines_example.o | Bin 0 -> 3348 bytes 3 files changed, 107 insertions(+), 16 deletions(-) create mode 100644 objdiff-core/tests/data/mips/ee_gcc_lines_example.o diff --git a/objdiff-core/src/obj/mdebug.rs b/objdiff-core/src/obj/mdebug.rs index 17b93c96..018d1700 100644 --- a/objdiff-core/src/obj/mdebug.rs +++ b/objdiff-core/src/obj/mdebug.rs @@ -10,6 +10,7 @@ const FDR_SIZE: usize = 0x48; const PDR_SIZE: usize = 0x34; const SYMR_SIZE: usize = 0x0c; +const ST_LABEL: u8 = 5; const ST_PROC: u8 = 6; const ST_STATICPROC: u8 = 14; const ST_END: u8 = 8; @@ -40,6 +41,8 @@ pub(super) fn parse_line_info_mdebug( )?; let symbols = parse_symbols(symbols_data, endianness)?; + let strings_data = slice_at(data, header.cb_ss_offset, header.iss_max, section_file_offset)?; + let fdr_data = slice_at( data, header.cb_fd_offset, @@ -63,11 +66,13 @@ pub(super) fn parse_line_info_mdebug( continue; } + assign_label_lines(sections, &symbols[sym_base..sym_end], strings_data, &fdr); + 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) + resolve_offset(line_file_offset, fdr.cb_line as usize, data.len(), section_file_offset) else { continue; }; @@ -168,6 +173,44 @@ fn assign_lines(sections: &mut [Section], base_address: u64, lines: &[i32]) { } } +fn assign_label_lines( + sections: &mut [Section], + symbols: &[SymbolEntry], + strings: &[u8], + fdr: &FileDescriptor, +) { + for sym in symbols { + if sym.st != ST_LABEL || sym.index == 0 { + continue; + } + let Some(name) = symbol_name(strings, fdr.iss_base, sym.iss) else { + continue; + }; + if !name.starts_with("$LM") { + continue; + } + let address = fdr.adr as u64 + sym.value as u64; + if let Some(section) = find_code_section(sections, address) { + section.line_info.insert(address, sym.index); + } + } +} + +fn symbol_name(strings: &[u8], file_string_base: u32, string_offset: u32) -> Option<&str> { + let offset = + string_offset.try_into().ok().filter(|&offset: &usize| offset < strings.len()).or_else( + || { + file_string_base + .checked_add(string_offset)? + .try_into() + .ok() + .filter(|&offset: &usize| offset < strings.len()) + }, + )?; + let end = offset + strings[offset..].iter().position(|&b| b == 0)?; + core::str::from_utf8(&strings[offset..end]).ok() +} + fn find_code_section(sections: &mut [Section], address: u64) -> Option<&mut Section> { sections.iter_mut().find(|section| { section.kind == SectionKind::Code @@ -209,12 +252,12 @@ fn slice_at( let size = size as usize; if size == 0 { ensure!( - resolve_offset(offset, data.len(), section_file_offset).is_some(), + resolve_offset(offset, 0, 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 { + let Some(offset) = resolve_offset(offset, size, data.len(), section_file_offset) else { bail!("offset outside of .mdebug section"); }; let end = offset.checked_add(size).context("range overflow")?; @@ -224,17 +267,18 @@ fn slice_at( fn resolve_offset( offset: u32, + size: usize, data_len: usize, section_file_offset: Option, ) -> Option { 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 + if let Some(file_offset) = section_file_offset + && let Some(rel) = offset.checked_sub(file_offset) + && rel.checked_add(size).is_some_and(|end| end <= data_len) + { + return Some(rel); } + offset.checked_add(size).filter(|&end| end <= data_len).map(|_| offset) } #[derive(Clone, Copy)] @@ -242,8 +286,10 @@ struct Header { cb_line_offset: u32, cb_pd_offset: u32, cb_sym_offset: u32, + cb_ss_offset: u32, cb_fd_offset: u32, isym_max: u32, + iss_max: u32, ifd_max: u32, } @@ -267,8 +313,8 @@ impl Header { 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_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)?; @@ -278,13 +324,23 @@ impl Header { 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 }) + Ok(Header { + cb_line_offset, + cb_pd_offset, + cb_sym_offset, + cb_ss_offset, + cb_fd_offset, + isym_max, + iss_max, + ifd_max, + }) } } #[derive(Clone, Copy)] struct FileDescriptor { adr: u32, + iss_base: u32, isym_base: u32, csym: u32, ipd_first: u16, @@ -299,7 +355,7 @@ impl FileDescriptor { 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 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)?; @@ -317,7 +373,16 @@ impl FileDescriptor { 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 }) + Ok(FileDescriptor { + adr, + iss_base, + isym_base, + csym, + ipd_first, + cpd, + cb_line_offset, + cb_line, + }) } } @@ -354,6 +419,7 @@ impl ProcDescriptor { #[derive(Clone, Copy)] struct SymbolEntry { + iss: u32, value: u32, st: u8, index: u32, @@ -364,14 +430,14 @@ fn parse_symbols(data: &[u8], endianness: Endianness) -> Result 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 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 }); + symbols.push(SymbolEntry { iss, value, st, index }); } Ok(symbols) } diff --git a/objdiff-core/tests/arch_mips.rs b/objdiff-core/tests/arch_mips.rs index dcc0ddd2..56a07467 100644 --- a/objdiff-core/tests/arch_mips.rs +++ b/objdiff-core/tests/arch_mips.rs @@ -80,3 +80,28 @@ fn ido_mdebug_line_numbers() { assert_eq!(text_section.line_info.get(&56), Some(&9)); assert_eq!(text_section.line_info.len(), 66); } + +#[test] +#[cfg(feature = "mips")] +fn ee_gcc_mdebug_line_numbers() { + let diff_config = diff::DiffObjConfig::default(); + let obj = obj::read::parse( + include_object!("data/mips/ee_gcc_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(&1)); + assert_eq!(text_section.line_info.get(&12), Some(&2)); + assert_eq!(text_section.line_info.get(&20), Some(&4)); + assert_eq!(text_section.line_info.get(&40), Some(&5)); + assert_eq!(text_section.line_info.get(&64), Some(&7)); + assert_eq!(text_section.line_info.get(&80), Some(&8)); + assert_eq!(text_section.line_info.get(&88), Some(&10)); + assert_eq!(text_section.line_info.get(&96), Some(&11)); + assert_eq!(text_section.line_info.get(&104), Some(&12)); + assert_eq!(text_section.line_info.get(&144), Some(&13)); + assert_eq!(text_section.line_info.len(), 10); +} diff --git a/objdiff-core/tests/data/mips/ee_gcc_lines_example.o b/objdiff-core/tests/data/mips/ee_gcc_lines_example.o new file mode 100644 index 0000000000000000000000000000000000000000..62519654360c2ff70fe3783d25c748b9c1697e79 GIT binary patch literal 3348 zcmb_fL2MgE6#b5q;uZuciVzYK7%5dF5xd@X?5wjjIBx7jBBdN!l!K*YZEqZ_w%77* zN;we_oH%TSIDk}Dk+}5M9N>fs76%TfQhVUS0VNkCBrZJ$2`Rk4JG&k?SOKYD+WBw( z{Q2+C-`Vl_!_sOw5{XbC5sJ~EM|ArPMhc(D=RAiQIz!KR=-}x7`APczDDikVI!BQs)$CzZt#2@`A{PyE?|9p%NqTXaQk1#(@KO6-y#=;o%obVaev5%KF zVmsyCLgXURM@z*Rl~3W3c@S@(xl$6>^F#+6FC-ShPj{ak6L3heaScH==)P2l3G_F{ ziDm#C=oI?5Pm@=x2hf3Fh^Dw^kXP^=L7)ncea&W1;%Hx`*SvAzLj~Ao@Q8l@$vMpV z-DFsJh?5UH~q2hjZ%-dpSk)Gcf3>v_td|c#ZKc(J#OojQ5Cs1>R!ZC)x+T z#rP4??-J&i?6ZOw_a|_HeUS4q@FS7)U%4$BkpmhM{_F!LJ5uH4*6uD({{&hHaceAXPX<%CQ(Rm6x+ZhJ>V1T8m7!VB}%EH z1ID0~gL-C1(>;}XdQ4t3yv+I0yn|ZFXhda4Ez{nzdXE1RwR}(06KQomtLfaL2>$4B zM!9@1hy9k#WYQUU_`AU0ib@LEru2lOYW{uMq21eOp1o$ZyDjskpShCB+gi^wS_{f-YBsIv&9>11POIF(ha#K< zMt+%DpE4KZ7&FZ$(v^sXBvuU^Azd(*@JINYNuU=MY)3O+-+@!gg<0ixQg|Y%%Eb>^f zCxzV`EaigTXmSTWE^F83W_&?IZ<@m5{!P(B;7Xq8;$3?%4-#wOTJOw;u z{H9RS{-BK$8`|mldMiMVdDEd}&uk@qRL7}L$*OI$vC-^oT9jH$&z$G`1~m7B>%{@EEIh`-_brZnl3x}+$gKj=10g|vto=9b56oh{yMP>$YsK*< zMPH8h0Dc!>4kZuUgak&2vrrla( literal 0 HcmV?d00001