From f49dfeb8420f703c1b725f850003f7370679b164 Mon Sep 17 00:00:00 2001 From: Philip Craig Date: Sun, 11 Aug 2024 15:37:56 +1000 Subject: [PATCH] read: handle tombstones in .debug_aranges (#743) --- crates/examples/src/bin/dwarfdump.rs | 10 +- src/read/aranges.rs | 211 +++++++++++++++++++++++++-- 2 files changed, 203 insertions(+), 18 deletions(-) diff --git a/crates/examples/src/bin/dwarfdump.rs b/crates/examples/src/bin/dwarfdump.rs index a837e190..96a913c9 100644 --- a/crates/examples/src/bin/dwarfdump.rs +++ b/crates/examples/src/bin/dwarfdump.rs @@ -2217,9 +2217,13 @@ fn dump_aranges( 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(()) diff --git a/src/read/aranges.rs b/src/read/aranges.rs index fd9f8353..c2aac914 100644 --- a/src/read/aranges.rs +++ b/src/read/aranges.rs @@ -249,6 +249,24 @@ impl ArangeEntryIter { /// 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> { + 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> { if self.input.is_empty() { return Ok(None); } @@ -265,6 +283,23 @@ impl ArangeEntryIter { } } } + + /// 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> { + // 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")] @@ -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 @@ -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 { @@ -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 { @@ -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)); } @@ -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, + }) + ); + } }