Skip to content

Commit 0dc123b

Browse files
committed
Don't fail on line info parsing; use gimli::RelocateReader
Workaround for #228
1 parent 1e62d46 commit 0dc123b

File tree

5 files changed

+147
-54
lines changed

5 files changed

+147
-54
lines changed

Cargo.lock

Lines changed: 11 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

objdiff-core/Cargo.toml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,10 @@ config = [
6262
"dep:semver",
6363
"dep:typed-path",
6464
]
65-
dwarf = ["dep:gimli"]
65+
dwarf = [
66+
"dep:gimli",
67+
"dep:typed-arena",
68+
]
6669
serde = [
6770
"dep:pbjson",
6871
"dep:pbjson-build",
@@ -78,6 +81,7 @@ std = [
7881
"prost?/std",
7982
"serde?/std",
8083
"similar?/std",
84+
"typed-arena?/std",
8185
"typed-path?/std",
8286
"dep:filetime",
8387
"dep:memmap2",
@@ -143,6 +147,7 @@ serde_json = { version = "1.0", default-features = false, features = ["alloc"],
143147

144148
# dwarf
145149
gimli = { version = "0.32", default-features = false, features = ["read"], optional = true }
150+
typed-arena = { version = "2.0", default-features = false, optional = true }
146151

147152
# ppc
148153
cwdemangle = { version = "1.0", optional = true }

objdiff-core/src/obj/dwarf2.rs

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
use anyhow::{Context, Result};
2+
use object::{Object, ObjectSection};
3+
use typed_arena::Arena;
4+
5+
use crate::obj::{Section, SectionKind};
6+
7+
/// Parse line information from DWARF 2+ sections.
8+
pub(crate) fn parse_line_info_dwarf2(
9+
obj_file: &object::File,
10+
sections: &mut [Section],
11+
) -> Result<()> {
12+
let arena_data = Arena::new();
13+
let arena_relocations = Arena::new();
14+
let endian = match obj_file.endianness() {
15+
object::Endianness::Little => gimli::RunTimeEndian::Little,
16+
object::Endianness::Big => gimli::RunTimeEndian::Big,
17+
};
18+
let dwarf = gimli::Dwarf::load(|id: gimli::SectionId| -> Result<_> {
19+
load_file_section(id, obj_file, endian, &arena_data, &arena_relocations)
20+
})
21+
.context("loading DWARF sections")?;
22+
23+
let mut iter = dwarf.units();
24+
if let Some(header) = iter.next().map_err(|e| gimli_error(e, "iterating over DWARF units"))? {
25+
let unit = dwarf.unit(header).map_err(|e| gimli_error(e, "loading DWARF unit"))?;
26+
if let Some(program) = unit.line_program.clone() {
27+
let mut text_sections = sections.iter_mut().filter(|s| s.kind == SectionKind::Code);
28+
let mut lines = text_sections.next().map(|section| &mut section.line_info);
29+
30+
let mut rows = program.rows();
31+
while let Some((_header, row)) =
32+
rows.next_row().map_err(|e| gimli_error(e, "loading program row"))?
33+
{
34+
if let (Some(line), Some(lines)) = (row.line(), &mut lines) {
35+
lines.insert(row.address(), line.get() as u32);
36+
}
37+
if row.end_sequence() {
38+
// The next row is the start of a new sequence, which means we must
39+
// advance to the next .text section.
40+
lines = text_sections.next().map(|section| &mut section.line_info);
41+
}
42+
}
43+
}
44+
}
45+
if iter.next().map_err(|e| gimli_error(e, "checking for next unit"))?.is_some() {
46+
log::warn!("Multiple units found in DWARF data, only processing the first");
47+
}
48+
49+
Ok(())
50+
}
51+
52+
#[derive(Debug, Default)]
53+
struct RelocationMap(object::read::RelocationMap);
54+
55+
impl RelocationMap {
56+
fn add(&mut self, file: &object::File, section: &object::Section) {
57+
for (offset, relocation) in section.relocations() {
58+
if let Err(e) = self.0.add(file, offset, relocation) {
59+
log::error!(
60+
"Relocation error for section {} at offset 0x{:08x}: {}",
61+
section.name().unwrap(),
62+
offset,
63+
e
64+
);
65+
}
66+
}
67+
}
68+
}
69+
70+
impl gimli::read::Relocate for &'_ RelocationMap {
71+
fn relocate_address(&self, offset: usize, value: u64) -> gimli::Result<u64> {
72+
Ok(self.0.relocate(offset as u64, value))
73+
}
74+
75+
fn relocate_offset(&self, offset: usize, value: usize) -> gimli::Result<usize> {
76+
<usize as gimli::ReaderOffset>::from_u64(self.0.relocate(offset as u64, value as u64))
77+
}
78+
}
79+
80+
type Relocate<'a, R> = gimli::RelocateReader<R, &'a RelocationMap>;
81+
82+
fn load_file_section<'input, 'arena, Endian: gimli::Endianity>(
83+
id: gimli::SectionId,
84+
file: &object::File<'input>,
85+
endian: Endian,
86+
arena_data: &'arena Arena<alloc::borrow::Cow<'input, [u8]>>,
87+
arena_relocations: &'arena Arena<RelocationMap>,
88+
) -> Result<Relocate<'arena, gimli::EndianSlice<'arena, Endian>>> {
89+
let mut relocations = RelocationMap::default();
90+
let data = match file.section_by_name(id.name()) {
91+
Some(ref section) => {
92+
relocations.add(file, section);
93+
section.uncompressed_data()?
94+
}
95+
// Use a non-zero capacity so that `ReaderOffsetId`s are unique.
96+
None => alloc::borrow::Cow::Owned(Vec::with_capacity(1)),
97+
};
98+
let data_ref = arena_data.alloc(data);
99+
let section = gimli::EndianSlice::new(data_ref, endian);
100+
let relocations = arena_relocations.alloc(relocations);
101+
Ok(Relocate::new(section, relocations))
102+
}
103+
104+
fn gimli_error(e: gimli::Error, context: &str) -> anyhow::Error {
105+
anyhow::anyhow!("gimli error {context}: {e:?}")
106+
}

objdiff-core/src/obj/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#[cfg(feature = "dwarf")]
2+
mod dwarf2;
13
pub mod read;
24
pub mod split_meta;
35

objdiff-core/src/obj/read.rs

Lines changed: 22 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,28 @@ fn parse_line_info(
578578
obj_data: &[u8],
579579
) -> Result<()> {
580580
// DWARF 1.1
581+
if let Err(e) = parse_line_info_dwarf1(obj_file, sections) {
582+
log::warn!("Failed to parse DWARF 1.1 line info: {e}");
583+
}
584+
585+
// DWARF 2+
586+
#[cfg(feature = "dwarf")]
587+
if let Err(e) = super::dwarf2::parse_line_info_dwarf2(obj_file, sections) {
588+
log::warn!("Failed to parse DWARF 2+ line info: {e}");
589+
}
590+
591+
// COFF
592+
if let object::File::Coff(coff) = obj_file
593+
&& let Err(e) = parse_line_info_coff(coff, sections, section_indices, obj_data)
594+
{
595+
log::warn!("Failed to parse COFF line info: {e}");
596+
}
597+
598+
Ok(())
599+
}
600+
601+
/// Parse .line section from DWARF 1.1 format.
602+
fn parse_line_info_dwarf1(obj_file: &object::File, sections: &mut [Section]) -> Result<()> {
581603
if let Some(section) = obj_file.section_by_name(".line") {
582604
let data = section.uncompressed_data()?;
583605
let mut reader: &[u8] = data.as_ref();
@@ -605,55 +627,6 @@ fn parse_line_info(
605627
}
606628
}
607629
}
608-
609-
// DWARF 2+
610-
#[cfg(feature = "dwarf")]
611-
{
612-
fn gimli_error(e: gimli::Error) -> anyhow::Error { anyhow::anyhow!("DWARF error: {e:?}") }
613-
let dwarf_cow = gimli::DwarfSections::load(|id| {
614-
Ok::<_, gimli::Error>(
615-
obj_file
616-
.section_by_name(id.name())
617-
.and_then(|section| section.uncompressed_data().ok())
618-
.unwrap_or(alloc::borrow::Cow::Borrowed(&[][..])),
619-
)
620-
})
621-
.map_err(gimli_error)?;
622-
let endian = match obj_file.endianness() {
623-
object::Endianness::Little => gimli::RunTimeEndian::Little,
624-
object::Endianness::Big => gimli::RunTimeEndian::Big,
625-
};
626-
let dwarf = dwarf_cow.borrow(|section| gimli::EndianSlice::new(section, endian));
627-
let mut iter = dwarf.units();
628-
if let Some(header) = iter.next().map_err(gimli_error)? {
629-
let unit = dwarf.unit(header).map_err(gimli_error)?;
630-
if let Some(program) = unit.line_program.clone() {
631-
let mut text_sections = sections.iter_mut().filter(|s| s.kind == SectionKind::Code);
632-
let mut lines = text_sections.next().map(|section| &mut section.line_info);
633-
634-
let mut rows = program.rows();
635-
while let Some((_header, row)) = rows.next_row().map_err(gimli_error)? {
636-
if let (Some(line), Some(lines)) = (row.line(), &mut lines) {
637-
lines.insert(row.address(), line.get() as u32);
638-
}
639-
if row.end_sequence() {
640-
// The next row is the start of a new sequence, which means we must
641-
// advance to the next .text section.
642-
lines = text_sections.next().map(|section| &mut section.line_info);
643-
}
644-
}
645-
}
646-
}
647-
if iter.next().map_err(gimli_error)?.is_some() {
648-
log::warn!("Multiple units found in DWARF data, only processing the first");
649-
}
650-
}
651-
652-
// COFF
653-
if let object::File::Coff(coff) = obj_file {
654-
parse_line_info_coff(coff, sections, section_indices, obj_data)?;
655-
}
656-
657630
Ok(())
658631
}
659632

0 commit comments

Comments
 (0)