Skip to content

Commit

Permalink
read: handle tombstones in .debug_aranges (#743)
Browse files Browse the repository at this point in the history
  • Loading branch information
philipc authored Aug 11, 2024
1 parent dd3c98c commit f49dfeb
Show file tree
Hide file tree
Showing 2 changed files with 203 additions and 18 deletions.
10 changes: 7 additions & 3 deletions crates/examples/src/bin/dwarfdump.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2217,9 +2217,13 @@ fn dump_aranges<R: Reader, W: Write>(
header.encoding().address_size,
)?;
let mut aranges = header.entries();
while let Some(arange) = aranges.next()? {
let range = arange.range();
writeln!(w, "[{:#x}, {:#x})", range.begin, range.end)?;
while let Some(raw) = aranges.next_raw()? {
if let Some(arange) = aranges.convert_raw(raw.clone())? {
let range = arange.range();
writeln!(w, "[{:#x}, {:#x})", range.begin, range.end)?;
} else {
writeln!(w, "[{:#x}, {:#x}) (ignored)", raw.address(), raw.length())?;
}
}
}
Ok(())
Expand Down
211 changes: 196 additions & 15 deletions src/read/aranges.rs
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,24 @@ impl<R: Reader> ArangeEntryIter<R> {
/// yielded. If an error occurs while parsing the next arange, then this error
/// is returned as `Err(e)`, and all subsequent calls return `Ok(None)`.
pub fn next(&mut self) -> Result<Option<ArangeEntry>> {
loop {
let raw_entry = match self.next_raw()? {
Some(entry) => entry,
None => return Ok(None),
};

let entry = self.convert_raw(raw_entry)?;
if entry.is_some() {
return Ok(entry);
}
}
}

/// Advance the iterator and return the next arange without validating it.
///
/// The returned entry will have `range.end` set to 0.
/// This will return tombstone entries as well.
pub fn next_raw(&mut self) -> Result<Option<ArangeEntry>> {
if self.input.is_empty() {
return Ok(None);
}
Expand All @@ -265,6 +283,23 @@ impl<R: Reader> ArangeEntryIter<R> {
}
}
}

/// Convert a raw range into a range.
///
/// The raw range should have been obtained from `next_raw`.
#[doc(hidden)]
pub fn convert_raw(&self, mut entry: ArangeEntry) -> Result<Option<ArangeEntry>> {
// Skip tombstone entries.
let address_size = self.encoding.address_size;
let tombstone_address = !0 >> (64 - self.encoding.address_size * 8);
if entry.range.begin == tombstone_address {
return Ok(None);
}

// Calculate end now so that we can handle overflow.
entry.range.end = entry.range.begin.add_sized(entry.length, address_size)?;
Ok(Some(entry))
}
}

#[cfg(feature = "fallible-iterator")]
Expand Down Expand Up @@ -297,9 +332,7 @@ impl ArangeEntry {

let begin = input.read_address(address_size)?;
let length = input.read_address(address_size)?;
// Calculate end now so that we can handle overflow.
let end = begin.add_sized(length, address_size)?;
let range = Range { begin, end };
let range = Range { begin, end: 0 };

match (begin, length) {
// This is meant to be a null terminator, but in practice it can occur
Expand Down Expand Up @@ -533,9 +566,15 @@ mod tests {
address_size: 4,
};
let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09];
let rest = &mut EndianSlice::new(&buf, LittleEndian);
let entry = ArangeEntry::parse(rest, encoding).expect("should parse entry ok");
assert_eq!(*rest, EndianSlice::new(&buf[buf.len() - 1..], LittleEndian));
let mut iter = ArangeEntryIter {
input: EndianSlice::new(&buf, LittleEndian),
encoding,
};
let entry = iter.next().expect("should parse entry ok");
assert_eq!(
iter.input,
EndianSlice::new(&buf[buf.len() - 1..], LittleEndian)
);
assert_eq!(
entry,
Some(ArangeEntry {
Expand Down Expand Up @@ -566,9 +605,15 @@ mod tests {
// Next tuple.
0x09
];
let rest = &mut EndianSlice::new(&buf, LittleEndian);
let entry = ArangeEntry::parse(rest, encoding).expect("should parse entry ok");
assert_eq!(*rest, EndianSlice::new(&buf[buf.len() - 1..], LittleEndian));
let mut iter = ArangeEntryIter {
input: EndianSlice::new(&buf, LittleEndian),
encoding,
};
let entry = iter.next().expect("should parse entry ok");
assert_eq!(
iter.input,
EndianSlice::new(&buf[buf.len() - 1..], LittleEndian)
);
assert_eq!(
entry,
Some(ArangeEntry {
Expand Down Expand Up @@ -597,9 +642,15 @@ mod tests {
// Next tuple.
0x09
];
let rest = &mut EndianSlice::new(&buf, LittleEndian);
let entry = ArangeEntry::parse(rest, encoding);
assert_eq!(*rest, EndianSlice::new(&buf[buf.len() - 1..], LittleEndian));
let mut iter = ArangeEntryIter {
input: EndianSlice::new(&buf, LittleEndian),
encoding,
};
let entry = iter.next();
assert_eq!(
iter.input,
EndianSlice::new(&buf[buf.len() - 1..], LittleEndian)
);
assert_eq!(entry, Err(Error::AddressOverflow));
}

Expand All @@ -619,9 +670,139 @@ mod tests {
// Next tuple.
0x09
];
let rest = &mut EndianSlice::new(&buf, LittleEndian);
let entry = ArangeEntry::parse(rest, encoding);
assert_eq!(*rest, EndianSlice::new(&buf[buf.len() - 1..], LittleEndian));
let mut iter = ArangeEntryIter {
input: EndianSlice::new(&buf, LittleEndian),
encoding,
};
let entry = iter.next();
assert_eq!(
iter.input,
EndianSlice::new(&buf[buf.len() - 1..], LittleEndian)
);
assert_eq!(entry, Err(Error::AddressOverflow));
}

#[test]
fn test_parse_entry_tombstone_32() {
let encoding = Encoding {
format: Format::Dwarf32,
version: 2,
address_size: 4,
};
#[rustfmt::skip]
let buf = [
// Address.
0xff, 0xff, 0xff, 0xff,
// Length.
0x05, 0x06, 0x07, 0x08,
// Address.
0x01, 0x02, 0x03, 0x04,
// Length.
0x05, 0x06, 0x07, 0x08,
// Next tuple.
0x09
];

let mut iter = ArangeEntryIter {
input: EndianSlice::new(&buf, LittleEndian),
encoding,
};
let entry = iter.next_raw().unwrap();
assert_eq!(
iter.input,
EndianSlice::new(&buf[buf.len() - 9..], LittleEndian)
);
assert_eq!(
entry,
Some(ArangeEntry {
range: Range {
begin: 0xffff_ffff,
end: 0,
},
length: 0x0807_0605,
})
);

let mut iter = ArangeEntryIter {
input: EndianSlice::new(&buf, LittleEndian),
encoding,
};
let entry = iter.next().unwrap();
assert_eq!(
iter.input,
EndianSlice::new(&buf[buf.len() - 1..], LittleEndian)
);
assert_eq!(
entry,
Some(ArangeEntry {
range: Range {
begin: 0x0403_0201,
end: 0x0403_0201 + 0x0807_0605,
},
length: 0x0807_0605,
})
);
}

#[test]
fn test_parse_entry_tombstone_64() {
let encoding = Encoding {
format: Format::Dwarf32,
version: 2,
address_size: 8,
};
#[rustfmt::skip]
let buf = [
// Address.
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
// Length.
0x05, 0x06, 0x07, 0x08, 0x00, 0x00, 0x00, 0x00,
// Address.
0x01, 0x02, 0x03, 0x04, 0x00, 0x00, 0x00, 0x00,
// Length.
0x05, 0x06, 0x07, 0x08, 0x00, 0x00, 0x00, 0x00,
// Next tuple.
0x09
];

let mut iter = ArangeEntryIter {
input: EndianSlice::new(&buf, LittleEndian),
encoding,
};
let entry = iter.next_raw().unwrap();
assert_eq!(
iter.input,
EndianSlice::new(&buf[buf.len() - 17..], LittleEndian)
);
assert_eq!(
entry,
Some(ArangeEntry {
range: Range {
begin: 0xffff_ffff_ffff_ffff,
end: 0,
},
length: 0x0807_0605,
})
);

let mut iter = ArangeEntryIter {
input: EndianSlice::new(&buf, LittleEndian),
encoding,
};
let entry = iter.next().unwrap();
assert_eq!(
iter.input,
EndianSlice::new(&buf[buf.len() - 1..], LittleEndian)
);
assert_eq!(
entry,
Some(ArangeEntry {
range: Range {
begin: 0x0403_0201,
end: 0x0403_0201 + 0x0807_0605,
},
length: 0x0807_0605,
})
);
}
}

0 comments on commit f49dfeb

Please sign in to comment.