From 736c202a1a0d748c95ded0f62c7ccef0803e06a6 Mon Sep 17 00:00:00 2001 From: Philip Craig Date: Wed, 13 Mar 2019 17:55:20 +1000 Subject: [PATCH] read: improved UnexpectedEof errors Stores a (hopefully) unique `ReaderOffsetId` in the error, which can be converted back to a section+offset for display. --- examples/dwarfdump.rs | 103 +++++++++++++------------------------- src/leb128.rs | 28 +++++++++-- src/read/abbrev.rs | 6 ++- src/read/addr.rs | 4 ++ src/read/aranges.rs | 4 ++ src/read/cfi.rs | 80 +++++++++++++++++++++++------ src/read/dwarf.rs | 76 +++++++++++++++++++++++++++- src/read/endian_reader.rs | 43 ++++++++++++---- src/read/endian_slice.rs | 28 +++++++++-- src/read/line.rs | 8 ++- src/read/loclists.rs | 21 ++++++-- src/read/lookup.rs | 4 ++ src/read/mod.rs | 23 +++++++-- src/read/op.rs | 16 +++++- src/read/pubnames.rs | 4 ++ src/read/pubtypes.rs | 4 ++ src/read/reader.rs | 15 ++++++ src/read/rnglists.rs | 21 +++++++- src/read/str.rs | 12 +++++ src/read/unit.rs | 20 +++++--- 20 files changed, 397 insertions(+), 123 deletions(-) diff --git a/examples/dwarfdump.rs b/examples/dwarfdump.rs index 0c1e46f5d..92b2fe2f5 100644 --- a/examples/dwarfdump.rs +++ b/examples/dwarfdump.rs @@ -9,7 +9,6 @@ use std::borrow::{Borrow, Cow}; use std::cmp::min; use std::collections::HashMap; use std::env; -use std::error; use std::fmt::{self, Debug}; use std::fs; use std::io; @@ -25,7 +24,6 @@ use typed_arena::Arena; pub enum Error { GimliError(gimli::Error), IoError, - MissingDIE, } impl fmt::Display for Error { @@ -35,21 +33,21 @@ impl fmt::Display for Error { } } -impl error::Error for Error { - fn description(&self) -> &str { - match *self { - Error::GimliError(ref err) => err.description(), - Error::IoError => "An I/O error occurred while writing.", - Error::MissingDIE => "Expected a DIE but none was found", - } - } - - fn cause(&self) -> Option<&error::Error> { - match *self { - Error::GimliError(ref err) => Some(err), - _ => None, +fn writeln_error( + w: &mut W, + dwarf: &gimli::Dwarf, + err: Error, + msg: &str, +) -> io::Result<()> { + writeln!( + w, + "{}: {}", + msg, + match err { + Error::GimliError(err) => dwarf.format_error(err), + Error::IoError => "An I/O error occurred while writing.".to_string(), } - } + ) } impl From for Error { @@ -289,6 +287,16 @@ impl<'a, R: gimli::Reader> gimli::Reader for Relocate<'a, R> { self.reader.offset_from(&base.reader) } + #[inline] + fn offset_id(&self) -> gimli::ReaderOffsetId { + self.reader.offset_id() + } + + #[inline] + fn lookup_offset_id(&self, id: gimli::ReaderOffsetId) -> Option { + self.reader.lookup_offset_id(id) + } + #[inline] fn find(&self, byte: u8) -> gimli::Result { self.reader.find(byte) @@ -429,22 +437,14 @@ fn main() { let file = match fs::File::open(&file_path) { Ok(file) => file, Err(err) => { - println!( - "Failed to open file '{}': {}", - file_path, - error::Error::description(&err) - ); + println!("Failed to open file '{}': {}", file_path, err); continue; } }; let file = match unsafe { memmap::Mmap::map(&file) } { Ok(mmap) => mmap, Err(err) => { - println!( - "Failed to map file '{}': {}", - file_path, - error::Error::description(&err) - ); + println!("Failed to map file '{}': {}", file_path, err); continue; } }; @@ -464,11 +464,7 @@ fn main() { let ret = dump_file(&file, endian, &flags); match ret { Ok(_) => (), - Err(err) => println!( - "Failed to dump '{}': {}", - file_path, - error::Error::description(&err) - ), + Err(err) => println!("Failed to dump '{}': {}", file_path, err,), } } } @@ -486,7 +482,8 @@ where add_relocations(&mut relocations, file, section); section.uncompressed_data() } - None => Cow::Borrowed(&[][..]), + // Use a non-zero capacity so that `ReaderOffsetId`s are unique. + None => Cow::Owned(Vec::with_capacity(1)), }; let data_ref = (*arena.0.alloc(data)).borrow(); let reader = gimli::EndianSlice::new(data_ref, endian); @@ -840,22 +837,14 @@ where let unit = match dwarf.unit(header) { Ok(unit) => unit, Err(err) => { - writeln!( - buf, - "Failed to parse unit root entry: {}", - error::Error::description(&err) - )?; + writeln_error(buf, dwarf, err.into(), "Failed to parse unit root entry")?; return Ok(()); } }; let entries_result = dump_entries(buf, unit, dwarf, flags); if let Err(err) = entries_result { - writeln!( - buf, - "Failed to dump entries: {}", - error::Error::description(&err) - )?; + writeln_error(buf, dwarf, err, "Failed to dump entries")?; } if !flags .match_units @@ -894,21 +883,13 @@ fn dump_types( let unit = match dwarf.type_unit(header) { Ok(unit) => unit, Err(err) => { - writeln!( - w, - "Failed to parse unit root entry: {}", - error::Error::description(&err) - )?; + writeln_error(w, dwarf, err.into(), "Failed to parse unit root entry")?; continue; } }; let entries_result = dump_entries(w, unit, dwarf, flags); if let Err(err) = entries_result { - writeln!( - w, - "Failed to dump entries: {}", - error::Error::description(&err) - )?; + writeln_error(w, dwarf, err, "Failed to dump entries")?; } } Ok(()) @@ -959,11 +940,7 @@ fn dump_entries( } else { match dump_attr_value(w, &attr, &unit, dwarf) { Ok(_) => (), - Err(ref err) => writeln!( - w, - "Failed to dump attribute value: {}", - error::Error::description(err) - )?, + Err(err) => writeln_error(w, dwarf, err, "Failed to dump attribute value")?, }; } } @@ -1682,22 +1659,14 @@ fn dump_line(w: &mut W, dwarf: &gimli::Dwarf) -> Result< let unit = match dwarf.unit(header) { Ok(unit) => unit, Err(err) => { - writeln!( - w, - "Failed to parse unit root entry: {}", - error::Error::description(&err) - )?; + writeln_error(w, dwarf, err.into(), "Failed to parse unit root entry")?; continue; } }; match dump_line_program(w, &unit, dwarf) { Ok(_) => (), Err(Error::IoError) => return Err(Error::IoError), - Err(err) => writeln!( - w, - "Failed to dump line program: {}", - error::Error::description(&err) - )?, + Err(err) => writeln_error(w, dwarf, err, "Failed to dump line program")?, } } Ok(()) diff --git a/src/leb128.rs b/src/leb128.rs index 7a2e08324..aef21eb25 100644 --- a/src/leb128.rs +++ b/src/leb128.rs @@ -188,10 +188,26 @@ pub mod write { mod tests { use super::{low_bits_of_byte, low_bits_of_u64, read, write, CONTINUATION_BIT}; use crate::endianity::NativeEndian; - use crate::read::{EndianSlice, Error}; + use crate::read::{EndianSlice, Error, ReaderOffsetId}; use std; use std::io; + trait ResultExt { + fn map_eof(self, input: &[u8]) -> Self; + } + + impl ResultExt for Result { + fn map_eof(self, input: &[u8]) -> Self { + match self { + Err(Error::UnexpectedEof(id)) => { + let id = ReaderOffsetId(id.0 - input.as_ptr() as u64); + Err(Error::UnexpectedEof(id)) + } + r => r, + } + } + } + #[test] fn test_low_bits_of_byte() { for i in 0..127 { @@ -335,14 +351,20 @@ mod tests { fn test_read_unsigned_not_enough_data() { let buf = [CONTINUATION_BIT]; let mut readable = EndianSlice::new(&buf[..], NativeEndian); - assert_eq!(read::unsigned(&mut readable), Err(Error::UnexpectedEof)); + assert_eq!( + read::unsigned(&mut readable).map_eof(&buf), + Err(Error::UnexpectedEof(ReaderOffsetId(1))) + ); } #[test] fn test_read_signed_not_enough_data() { let buf = [CONTINUATION_BIT]; let mut readable = EndianSlice::new(&buf[..], NativeEndian); - assert_eq!(read::signed(&mut readable), Err(Error::UnexpectedEof)); + assert_eq!( + read::signed(&mut readable).map_eof(&buf), + Err(Error::UnexpectedEof(ReaderOffsetId(1))) + ); } #[test] diff --git a/src/read/abbrev.rs b/src/read/abbrev.rs index 31c3d3130..00dac9163 100644 --- a/src/read/abbrev.rs +++ b/src/read/abbrev.rs @@ -82,6 +82,10 @@ impl Section for DebugAbbrev { fn id() -> SectionId { SectionId::DebugAbbrev } + + fn reader(&self) -> &R { + &self.debug_abbrev_section + } } impl From for DebugAbbrev { @@ -774,7 +778,7 @@ pub mod tests { let buf = &mut EndianSlice::new(&*buf, LittleEndian); match Abbreviation::parse(buf) { - Err(Error::UnexpectedEof) => {} + Err(Error::UnexpectedEof(_)) => {} otherwise => panic!("Unexpected result: {:?}", otherwise), } } diff --git a/src/read/addr.rs b/src/read/addr.rs index 84fb5db47..593f9fe3c 100644 --- a/src/read/addr.rs +++ b/src/read/addr.rs @@ -68,6 +68,10 @@ impl Section for DebugAddr { fn id() -> SectionId { SectionId::DebugAddr } + + fn reader(&self) -> &R { + &self.section + } } impl From for DebugAddr { diff --git a/src/read/aranges.rs b/src/read/aranges.rs index 011867fbb..1b0bba922 100644 --- a/src/read/aranges.rs +++ b/src/read/aranges.rs @@ -224,6 +224,10 @@ impl Section for DebugAranges { fn id() -> SectionId { SectionId::DebugAranges } + + fn reader(&self) -> &R { + self.0.reader() + } } impl From for DebugAranges { diff --git a/src/read/cfi.rs b/src/read/cfi.rs index 1efb7d228..f701061f7 100644 --- a/src/read/cfi.rs +++ b/src/read/cfi.rs @@ -78,6 +78,10 @@ impl Section for DebugFrame { fn id() -> SectionId { SectionId::DebugFrame } + + fn reader(&self) -> &R { + &self.section + } } impl From for DebugFrame { @@ -173,6 +177,10 @@ impl Section for EhFrameHdr { fn id() -> SectionId { SectionId::EhFrameHdr } + + fn reader(&self) -> &R { + &self.0 + } } impl From for EhFrameHdr { @@ -434,6 +442,10 @@ impl Section for EhFrame { fn id() -> SectionId { SectionId::EhFrame } + + fn reader(&self) -> &R { + &self.section + } } impl From for EhFrame { @@ -3250,7 +3262,9 @@ mod tests { use crate::common::Format; use crate::constants; use crate::endianity::{BigEndian, Endianity, LittleEndian, NativeEndian}; - use crate::read::{EndianSlice, Error, Expression, Pointer, Result, Section as ReadSection}; + use crate::read::{ + EndianSlice, Error, Expression, Pointer, ReaderOffsetId, Result, Section as ReadSection, + }; use crate::test_util::GimliSectionMethods; use crate::vec::Vec; use std::marker::PhantomData; @@ -3491,6 +3505,22 @@ mod tests { } } + trait ResultExt { + fn map_eof(self, input: &[u8]) -> Self; + } + + impl ResultExt for Result { + fn map_eof(self, input: &[u8]) -> Self { + match self { + Err(Error::UnexpectedEof(id)) => { + let id = ReaderOffsetId(id.0 - input.as_ptr() as u64); + Err(Error::UnexpectedEof(id)) + } + r => r, + } + } + } + #[allow(clippy::type_complexity)] #[allow(clippy::needless_pass_by_value)] fn assert_parse_cie<'input, E>( @@ -3510,7 +3540,7 @@ mod tests { let input = &mut EndianSlice::new(§ion, E::default()); let bases = Default::default(); let result = CommonInformationEntry::parse(&bases, &debug_frame, input); - let result = result.map(|cie| (*input, cie)); + let result = result.map(|cie| (*input, cie)).map_eof(§ion); assert_eq!(result, expected); } @@ -3518,7 +3548,12 @@ mod tests { fn test_parse_cie_incomplete_length_32() { let kind = debug_frame_le(); let section = Section::with_endian(kind.endian()).L16(5); - assert_parse_cie(kind, section, 8, Err(Error::UnexpectedEof)); + assert_parse_cie( + kind, + section, + 8, + Err(Error::UnexpectedEof(ReaderOffsetId(0))), + ); } #[test] @@ -3527,7 +3562,12 @@ mod tests { let section = Section::with_endian(kind.endian()) .L32(0xffff_ffff) .L32(12345); - assert_parse_cie(kind, section, 8, Err(Error::UnexpectedEof)); + assert_parse_cie( + kind, + section, + 8, + Err(Error::UnexpectedEof(ReaderOffsetId(4))), + ); } #[test] @@ -3537,7 +3577,12 @@ mod tests { // The length is not large enough to contain the ID. .B32(3) .B32(0xffff_ffff); - assert_parse_cie(kind, section, 8, Err(Error::UnexpectedEof)); + assert_parse_cie( + kind, + section, + 8, + Err(Error::UnexpectedEof(ReaderOffsetId(4))), + ); } #[test] @@ -3686,14 +3731,16 @@ mod tests { contents[2] = 0; contents[3] = 255; + let debug_frame = DebugFrame::new(&contents, LittleEndian); let bases = Default::default(); assert_eq!( CommonInformationEntry::parse( &bases, - &DebugFrame::new(&contents, LittleEndian), + &debug_frame, &mut EndianSlice::new(&contents, LittleEndian) - ), - Err(Error::UnexpectedEof) + ) + .map_eof(&contents), + Err(Error::UnexpectedEof(ReaderOffsetId(4))) ); } @@ -3705,8 +3752,8 @@ mod tests { let debug_frame = kind.section(§ion); let rest = &mut EndianSlice::new(§ion, LittleEndian); assert_eq!( - parse_fde(debug_frame, rest, UnwindSection::cie_from_offset), - Err(Error::UnexpectedEof) + parse_fde(debug_frame, rest, UnwindSection::cie_from_offset).map_eof(§ion), + Err(Error::UnexpectedEof(ReaderOffsetId(0))) ); } @@ -3720,8 +3767,8 @@ mod tests { let debug_frame = kind.section(§ion); let rest = &mut EndianSlice::new(§ion, LittleEndian); assert_eq!( - parse_fde(debug_frame, rest, UnwindSection::cie_from_offset), - Err(Error::UnexpectedEof) + parse_fde(debug_frame, rest, UnwindSection::cie_from_offset).map_eof(§ion), + Err(Error::UnexpectedEof(ReaderOffsetId(4))) ); } @@ -3736,8 +3783,8 @@ mod tests { let debug_frame = kind.section(§ion); let rest = &mut EndianSlice::new(§ion, BigEndian); assert_eq!( - parse_fde(debug_frame, rest, UnwindSection::cie_from_offset), - Err(Error::UnexpectedEof) + parse_fde(debug_frame, rest, UnwindSection::cie_from_offset).map_eof(§ion), + Err(Error::UnexpectedEof(ReaderOffsetId(4))) ); } @@ -4823,7 +4870,10 @@ mod tests { parameters, }; - assert_eq!(iter.next(), Err(Error::UnexpectedEof)); + assert_eq!( + iter.next().map_eof(&contents), + Err(Error::UnexpectedEof(ReaderOffsetId(1))) + ); assert_eq!(iter.next(), Ok(None)); } diff --git a/src/read/dwarf.rs b/src/read/dwarf.rs index 5e2366a77..ece22996e 100644 --- a/src/read/dwarf.rs +++ b/src/read/dwarf.rs @@ -9,9 +9,10 @@ use crate::read::{ Abbreviations, AttributeValue, CompilationUnitHeader, CompilationUnitHeadersIter, DebugAbbrev, DebugAddr, DebugInfo, DebugLine, DebugLineStr, DebugStr, DebugStrOffsets, DebugTypes, EntriesCursor, EntriesTree, Error, IncompleteLineProgram, LocListIter, LocationLists, - RangeLists, Reader, ReaderOffset, Result, RngListIter, Section, TypeUnitHeader, + RangeLists, Reader, ReaderOffset, ReaderOffsetId, Result, RngListIter, Section, TypeUnitHeader, TypeUnitHeadersIter, UnitHeader, UnitOffset, }; +use crate::string::String; /// All of the commonly used DWARF sections, and other common information. #[derive(Debug, Default)] @@ -367,6 +368,50 @@ impl Dwarf { None => Ok(None), } } + + /// Call `Reader::lookup_offset_id` for each section, and return the first match. + /// + /// The first element of the tuple is `true` for supplementary sections. + pub fn lookup_offset_id(&self, id: ReaderOffsetId) -> Option<(bool, SectionId, R::Offset)> { + None.or_else(|| self.debug_abbrev.lookup_offset_id(id)) + .or_else(|| self.debug_addr.lookup_offset_id(id)) + .or_else(|| self.debug_info.lookup_offset_id(id)) + .or_else(|| self.debug_line.lookup_offset_id(id)) + .or_else(|| self.debug_line_str.lookup_offset_id(id)) + .or_else(|| self.debug_str.lookup_offset_id(id)) + .or_else(|| self.debug_str_offsets.lookup_offset_id(id)) + .or_else(|| self.debug_types.lookup_offset_id(id)) + .or_else(|| self.locations.lookup_offset_id(id)) + .or_else(|| self.ranges.lookup_offset_id(id)) + .map(|(id, offset)| (false, id, offset)) + .or_else(|| { + self.debug_str_sup + .lookup_offset_id(id) + .map(|(id, offset)| (true, id, offset)) + }) + } + + /// Returns a string representation of the given error. + /// + /// This uses information from the DWARF sections to provide more information in some cases. + pub fn format_error(&self, err: Error) -> String { + match err { + Error::UnexpectedEof(id) => match self.lookup_offset_id(id) { + Some((sup, section, offset)) => { + return format!( + "{} at {}{}+0x{:x}", + err.description(), + section.name(), + if sup { "(sup)" } else { "" }, + offset.into_u64(), + ); + } + None => {} + }, + _ => {} + } + format!("{}", err.description()) + } } /// All of the commonly used information for a unit in the `.debug_info` or `.debug_types` @@ -603,7 +648,7 @@ impl UnitOffset { mod tests { use super::*; use crate::read::EndianSlice; - use crate::Endianity; + use crate::{Endianity, LittleEndian}; /// Ensure that `Dwarf` is covariant wrt R. #[test] @@ -622,4 +667,31 @@ mod tests { x } } + + #[test] + fn test_format_error() { + let owned_dwarf = + Dwarf::load(|_| -> Result<_> { Ok(vec![1, 2]) }, |_| Ok(vec![1, 2])).unwrap(); + let dwarf = owned_dwarf.borrow(|section| EndianSlice::new(§ion, LittleEndian)); + + match dwarf.debug_str.get_str(DebugStrOffset(1)) { + Ok(r) => panic!("Unexpected str {:?}", r), + Err(e) => { + assert_eq!( + dwarf.format_error(e), + "Hit the end of input before it was expected at .debug_str+0x1" + ); + } + } + match dwarf.debug_str_sup.get_str(DebugStrOffset(1)) { + Ok(r) => panic!("Unexpected str {:?}", r), + Err(e) => { + assert_eq!( + dwarf.format_error(e), + "Hit the end of input before it was expected at .debug_str(sup)+0x1" + ); + } + } + assert_eq!(dwarf.format_error(Error::Io), Error::Io.description()); + } } diff --git a/src/read/endian_reader.rs b/src/read/endian_reader.rs index 51ffd4bba..01ca39560 100644 --- a/src/read/endian_reader.rs +++ b/src/read/endian_reader.rs @@ -11,7 +11,7 @@ use std::slice; use std::str; use crate::endianity::Endianity; -use crate::read::{Error, Reader, Result}; +use crate::read::{Error, Reader, ReaderOffsetId, Result}; /// A reference counted, non-thread-safe slice of bytes and associated /// endianity. @@ -203,14 +203,14 @@ where } #[inline] - fn read_slice(&mut self, len: usize) -> Result<&[u8]> { + fn read_slice(&mut self, len: usize) -> Option<&[u8]> { if self.len() < len { - Err(Error::UnexpectedEof) + None } else { // Same as for `bytes()`. let bytes = unsafe { slice::from_raw_parts(self.ptr, len) }; self.skip(len); - Ok(bytes) + Some(bytes) } } } @@ -374,7 +374,7 @@ where #[inline] fn truncate(&mut self, len: usize) -> Result<()> { if self.len() < len { - Err(Error::UnexpectedEof) + Err(Error::UnexpectedEof(self.offset_id())) } else { self.range.truncate(len); Ok(()) @@ -390,18 +390,35 @@ where ptr - base_ptr } + #[inline] + fn offset_id(&self) -> ReaderOffsetId { + ReaderOffsetId(self.bytes().as_ptr() as u64) + } + + #[inline] + fn lookup_offset_id(&self, id: ReaderOffsetId) -> Option { + let id = id.0; + let self_id = self.bytes().as_ptr() as u64; + let self_len = self.bytes().len() as u64; + if id >= self_id && id <= self_id + self_len { + Some((id - self_id) as usize) + } else { + None + } + } + #[inline] fn find(&self, byte: u8) -> Result { self.bytes() .iter() .position(|x| *x == byte) - .ok_or(Error::UnexpectedEof) + .ok_or(Error::UnexpectedEof(self.offset_id())) } #[inline] fn skip(&mut self, len: usize) -> Result<()> { if self.len() < len { - Err(Error::UnexpectedEof) + Err(Error::UnexpectedEof(self.offset_id())) } else { self.range.skip(len); Ok(()) @@ -411,7 +428,7 @@ where #[inline] fn split(&mut self, len: usize) -> Result { if self.len() < len { - Err(Error::UnexpectedEof) + Err(Error::UnexpectedEof(self.offset_id())) } else { let mut r = self.clone(); r.range.truncate(len); @@ -440,9 +457,13 @@ where #[inline] fn read_slice(&mut self, buf: &mut [u8]) -> Result<()> { - let slice = self.range.read_slice(buf.len())?; - buf.clone_from_slice(slice); - Ok(()) + match self.range.read_slice(buf.len()) { + Some(slice) => { + buf.clone_from_slice(slice); + Ok(()) + } + None => Err(Error::UnexpectedEof(self.offset_id())), + } } } diff --git a/src/read/endian_slice.rs b/src/read/endian_slice.rs index ab10171af..74f7f7365 100644 --- a/src/read/endian_slice.rs +++ b/src/read/endian_slice.rs @@ -6,7 +6,7 @@ use std::ops::{Deref, Index, Range, RangeFrom, RangeTo}; use std::str; use crate::endianity::Endianity; -use crate::read::{Error, Reader, Result}; +use crate::read::{Error, Reader, ReaderOffsetId, Result}; /// A `&[u8]` slice with endianity metadata. /// @@ -90,7 +90,7 @@ where #[inline] fn read_slice(&mut self, len: usize) -> Result<&'input [u8]> { if self.slice.len() < len { - Err(Error::UnexpectedEof) + Err(Error::UnexpectedEof(self.offset_id())) } else { let val = &self.slice[..len]; self.slice = &self.slice[len..]; @@ -233,7 +233,7 @@ where #[inline] fn truncate(&mut self, len: usize) -> Result<()> { if self.slice.len() < len { - Err(Error::UnexpectedEof) + Err(Error::UnexpectedEof(self.offset_id())) } else { self.slice = &self.slice[..len]; Ok(()) @@ -245,15 +245,33 @@ where self.offset_from(*base) } + #[inline] + fn offset_id(&self) -> ReaderOffsetId { + ReaderOffsetId(self.slice.as_ptr() as u64) + } + + #[inline] + fn lookup_offset_id(&self, id: ReaderOffsetId) -> Option { + let id = id.0; + let self_id = self.slice.as_ptr() as u64; + let self_len = self.slice.len() as u64; + if id >= self_id && id <= self_id + self_len { + Some((id - self_id) as usize) + } else { + None + } + } + #[inline] fn find(&self, byte: u8) -> Result { - self.find(byte).ok_or(Error::UnexpectedEof) + self.find(byte) + .ok_or(Error::UnexpectedEof(self.offset_id())) } #[inline] fn skip(&mut self, len: usize) -> Result<()> { if self.slice.len() < len { - Err(Error::UnexpectedEof) + Err(Error::UnexpectedEof(self.offset_id())) } else { self.slice = &self.slice[len..]; Ok(()) diff --git a/src/read/line.rs b/src/read/line.rs index 027c00532..d5852134f 100644 --- a/src/read/line.rs +++ b/src/read/line.rs @@ -108,6 +108,10 @@ impl Section for DebugLine { fn id() -> SectionId { SectionId::DebugLine } + + fn reader(&self) -> &R { + &self.debug_line_section + } } impl From for DebugLine { @@ -2049,7 +2053,7 @@ mod tests { let input = &mut EndianSlice::new(&buf, LittleEndian); match LineProgramHeader::parse(input, DebugLineOffset(0), 4, None, None) { - Err(Error::UnexpectedEof) => return, + Err(Error::UnexpectedEof(_)) => return, otherwise => panic!("Unexpected result: {:?}", otherwise), } } @@ -2110,7 +2114,7 @@ mod tests { let input = &mut EndianSlice::new(&buf, LittleEndian); match LineProgramHeader::parse(input, DebugLineOffset(0), 4, None, None) { - Err(Error::UnexpectedEof) => return, + Err(Error::UnexpectedEof(_)) => return, otherwise => panic!("Unexpected result: {:?}", otherwise), } } diff --git a/src/read/loclists.rs b/src/read/loclists.rs index c5ad0234f..4e4d0822a 100644 --- a/src/read/loclists.rs +++ b/src/read/loclists.rs @@ -7,8 +7,8 @@ use crate::common::{ use crate::constants; use crate::endianity::Endianity; use crate::read::{ - DebugAddr, EndianSlice, Error, Expression, Range, RawRange, Reader, ReaderOffset, Result, - Section, + DebugAddr, EndianSlice, Error, Expression, Range, RawRange, Reader, ReaderOffset, + ReaderOffsetId, Result, Section, }; /// The raw contents of the `.debug_loc` section. @@ -44,6 +44,10 @@ impl Section for DebugLoc { fn id() -> SectionId { SectionId::DebugLoc } + + fn reader(&self) -> &R { + &self.section + } } impl From for DebugLoc { @@ -86,6 +90,10 @@ impl Section for DebugLocLists { fn id() -> SectionId { SectionId::DebugLocLists } + + fn reader(&self) -> &R { + &self.section + } } impl From for DebugLocLists { @@ -271,6 +279,13 @@ impl LocationLists { .read_offset(format) .map(|x| LocationListsOffset(base.0 + x)) } + + /// Call `Reader::lookup_offset_id` for each section, and return the first match. + pub fn lookup_offset_id(&self, id: ReaderOffsetId) -> Option<(SectionId, R::Offset)> { + self.debug_loc + .lookup_offset_id(id) + .or_else(|| self.debug_loclists.lookup_offset_id(id)) + } } /// A raw iterator over a location list. @@ -1352,7 +1367,7 @@ mod tests { debug_addr, debug_addr_base, ) { - Err(Error::UnexpectedEof) => {} + Err(Error::UnexpectedEof(_)) => {} otherwise => panic!("Unexpected result: {:?}", otherwise), } } diff --git a/src/read/lookup.rs b/src/read/lookup.rs index 2b23aa763..ab45c2412 100644 --- a/src/read/lookup.rs +++ b/src/read/lookup.rs @@ -62,6 +62,10 @@ where remaining_input: self.input_buffer.clone(), } } + + pub fn reader(&self) -> &R { + &self.input_buffer + } } #[derive(Clone, Debug)] diff --git a/src/read/mod.rs b/src/read/mod.rs index 741c4d211..1b59a915c 100644 --- a/src/read/mod.rs +++ b/src/read/mod.rs @@ -267,7 +267,7 @@ pub enum Error { /// Found a record with an unknown abbreviation code. UnknownAbbreviation, /// Hit the end of input before it was expected. - UnexpectedEof, + UnexpectedEof(ReaderOffsetId), /// Read a null entry before it was expected. UnexpectedNull, /// Found an unknown standard opcode. @@ -427,7 +427,7 @@ impl Error { Error::UnknownReservedLength => "Found an unknown reserved length value", Error::UnknownVersion(_) => "Found an unknown DWARF version", Error::UnknownAbbreviation => "Found a record with an unknown abbreviation code", - Error::UnexpectedEof => "Hit the end of input before it was expected", + Error::UnexpectedEof(_) => "Hit the end of input before it was expected", Error::UnexpectedNull => "Read a null entry before it was expected.", Error::UnknownStandardOpcode(_) => "Found an unknown standard opcode", Error::UnknownExtendedOpcode(_) => "Found an unknown extended opcode", @@ -566,6 +566,21 @@ pub trait Section: From { { f(Self::id()).map(From::from) } + + /// Returns the `Reader` for this section. + fn reader(&self) -> &R + where + R: Reader; + + /// Returns the `Reader` for this section. + fn lookup_offset_id(&self, id: ReaderOffsetId) -> Option<(SectionId, R::Offset)> + where + R: Reader, + { + self.reader() + .lookup_offset_id(id) + .map(|offset| (Self::id(), offset)) + } } impl Register { @@ -647,7 +662,7 @@ mod tests { let input = &mut EndianSlice::new(&buf, LittleEndian); match input.read_initial_length() { - Err(Error::UnexpectedEof) => assert!(true), + Err(Error::UnexpectedEof(_)) => assert!(true), otherwise => panic!("Unexpected result: {:?}", otherwise), }; } @@ -663,7 +678,7 @@ mod tests { let input = &mut EndianSlice::new(&buf, LittleEndian); match input.read_initial_length() { - Err(Error::UnexpectedEof) => assert!(true), + Err(Error::UnexpectedEof(_)) => assert!(true), otherwise => panic!("Unexpected result: {:?}", otherwise), }; } diff --git a/src/read/op.rs b/src/read/op.rs index 9f7ccf15a..c104887ce 100644 --- a/src/read/op.rs +++ b/src/read/op.rs @@ -1862,6 +1862,18 @@ mod tests { } } + fn check_op_parse_eof(input: &[u8], encoding: Encoding) { + let buf = EndianSlice::new(input, LittleEndian); + let mut pc = buf; + match Operation::parse(&mut pc, &buf, encoding) { + Err(Error::UnexpectedEof(id)) => { + assert!(buf.lookup_offset_id(id).is_some()); + } + + _ => panic!("Unexpected result"), + } + } + fn check_op_parse( input: F, expect: &Operation>, @@ -1873,7 +1885,7 @@ mod tests { .get_contents() .unwrap(); for i in 1..input.len() { - check_op_parse_failure(&input[..i], Error::UnexpectedEof, encoding); + check_op_parse_eof(&input[..i], encoding); } check_op_parse_simple(&input, expect, encoding); } @@ -2000,7 +2012,7 @@ mod tests { ]; let input = []; - check_op_parse_failure(&input[..], Error::UnexpectedEof, encoding); + check_op_parse_eof(&input[..], encoding); for item in inputs.iter() { let (opcode, ref result) = *item; diff --git a/src/read/pubnames.rs b/src/read/pubnames.rs index 51f363655..931bb8cd0 100644 --- a/src/read/pubnames.rs +++ b/src/read/pubnames.rs @@ -100,6 +100,10 @@ impl Section for DebugPubNames { fn id() -> SectionId { SectionId::DebugPubNames } + + fn reader(&self) -> &R { + self.0.reader() + } } impl From for DebugPubNames { diff --git a/src/read/pubtypes.rs b/src/read/pubtypes.rs index be1a9a2d0..303d79e42 100644 --- a/src/read/pubtypes.rs +++ b/src/read/pubtypes.rs @@ -100,6 +100,10 @@ impl Section for DebugPubTypes { fn id() -> SectionId { SectionId::DebugPubTypes } + + fn reader(&self) -> &R { + self.0.reader() + } } impl From for DebugPubTypes { diff --git a/src/read/reader.rs b/src/read/reader.rs index eb1549008..4694caed1 100644 --- a/src/read/reader.rs +++ b/src/read/reader.rs @@ -8,6 +8,14 @@ use crate::endianity::Endianity; use crate::leb128; use crate::read::{Error, Result}; +/// An identifier for an offset within a section reader. +/// +/// This is used for error reporting. The meaning of this value is specific to +/// each reader implementation. The values should be chosen to be unique amongst +/// all readers. If values are not unique then errors may point to the wrong reader. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct ReaderOffsetId(pub u64); + /// A trait for offsets with a DWARF section. /// /// This allows consumers to choose a size that is appropriate for their address space. @@ -222,6 +230,13 @@ pub trait Reader: Debug + Clone { /// base reader's data. fn offset_from(&self, base: &Self) -> Self::Offset; + /// Return an identifier for the current reader offset. + fn offset_id(&self) -> ReaderOffsetId; + + /// Return the offset corresponding to the given `id` if + /// it is associated with this reader. + fn lookup_offset_id(&self, id: ReaderOffsetId) -> Option; + /// Find the index of the first occurence of the given byte. /// The offset of the reader is not changed. fn find(&self, byte: u8) -> Result; diff --git a/src/read/rnglists.rs b/src/read/rnglists.rs index ea1dd9589..5576a45d7 100644 --- a/src/read/rnglists.rs +++ b/src/read/rnglists.rs @@ -6,7 +6,9 @@ use crate::common::{ }; use crate::constants; use crate::endianity::Endianity; -use crate::read::{DebugAddr, EndianSlice, Error, Reader, ReaderOffset, Result, Section}; +use crate::read::{ + DebugAddr, EndianSlice, Error, Reader, ReaderOffset, ReaderOffsetId, Result, Section, +}; /// The raw contents of the `.debug_ranges` section. #[derive(Debug, Default, Clone, Copy)] @@ -41,6 +43,10 @@ impl Section for DebugRanges { fn id() -> SectionId { SectionId::DebugRanges } + + fn reader(&self) -> &R { + &self.section + } } impl From for DebugRanges { @@ -84,6 +90,10 @@ impl Section for DebugRngLists { fn id() -> SectionId { SectionId::DebugRngLists } + + fn reader(&self) -> &R { + &self.section + } } impl From for DebugRngLists { @@ -271,6 +281,13 @@ impl RangeLists { .read_offset(format) .map(|x| RangeListsOffset(base.0 + x)) } + + /// Call `Reader::lookup_offset_id` for each section, and return the first match. + pub fn lookup_offset_id(&self, id: ReaderOffsetId) -> Option<(SectionId, R::Offset)> { + self.debug_ranges + .lookup_offset_id(id) + .or_else(|| self.debug_rnglists.lookup_offset_id(id)) + } } /// A raw iterator over an address range list. @@ -1281,7 +1298,7 @@ mod tests { debug_addr, debug_addr_base, ) { - Err(Error::UnexpectedEof) => {} + Err(Error::UnexpectedEof(_)) => {} otherwise => panic!("Unexpected result: {:?}", otherwise), } } diff --git a/src/read/str.rs b/src/read/str.rs index e273e416e..d55ff1f28 100644 --- a/src/read/str.rs +++ b/src/read/str.rs @@ -83,6 +83,10 @@ impl Section for DebugStr { fn id() -> SectionId { SectionId::DebugStr } + + fn reader(&self) -> &R { + &self.debug_str_section + } } impl From for DebugStr { @@ -157,6 +161,10 @@ impl Section for DebugStrOffsets { fn id() -> SectionId { SectionId::DebugStrOffsets } + + fn reader(&self) -> &R { + &self.section + } } impl From for DebugStrOffsets { @@ -209,6 +217,10 @@ impl Section for DebugLineStr { fn id() -> SectionId { SectionId::DebugLineStr } + + fn reader(&self) -> &R { + &self.section + } } impl From for DebugLineStr { diff --git a/src/read/unit.rs b/src/read/unit.rs index 920b9f6bb..2fbba307a 100644 --- a/src/read/unit.rs +++ b/src/read/unit.rs @@ -178,6 +178,10 @@ impl Section for DebugInfo { fn id() -> SectionId { SectionId::DebugInfo } + + fn reader(&self) -> &R { + &self.debug_info_section + } } impl From for DebugInfo { @@ -2814,6 +2818,10 @@ impl Section for DebugTypes { fn id() -> SectionId { SectionId::DebugTypes } + + fn reader(&self) -> &R { + &self.debug_types_section + } } impl From for DebugTypes { @@ -3281,7 +3289,7 @@ mod tests { let buf = &mut EndianSlice::new(&buf, LittleEndian); match parse_debug_abbrev_offset(buf, Format::Dwarf32) { - Err(Error::UnexpectedEof) => assert!(true), + Err(Error::UnexpectedEof(_)) => assert!(true), otherwise => panic!("Unexpected result: {:?}", otherwise), }; } @@ -3305,7 +3313,7 @@ mod tests { let buf = &mut EndianSlice::new(&buf, LittleEndian); match parse_debug_abbrev_offset(buf, Format::Dwarf64) { - Err(Error::UnexpectedEof) => assert!(true), + Err(Error::UnexpectedEof(_)) => assert!(true), otherwise => panic!("Unexpected result: {:?}", otherwise), }; } @@ -3328,7 +3336,7 @@ mod tests { let buf = &mut EndianSlice::new(&buf, LittleEndian); match parse_debug_info_offset(buf, Format::Dwarf32) { - Err(Error::UnexpectedEof) => assert!(true), + Err(Error::UnexpectedEof(_)) => assert!(true), otherwise => panic!("Unexpected result: {:?}", otherwise), }; } @@ -3352,7 +3360,7 @@ mod tests { let buf = &mut EndianSlice::new(&buf, LittleEndian); match parse_debug_info_offset(buf, Format::Dwarf64) { - Err(Error::UnexpectedEof) => assert!(true), + Err(Error::UnexpectedEof(_)) => assert!(true), otherwise => panic!("Unexpected result: {:?}", otherwise), }; } @@ -3425,7 +3433,7 @@ mod tests { let rest = &mut EndianSlice::new(&buf, LittleEndian); match parse_unit_header(rest) { - Err(Error::UnexpectedEof) => assert!(true), + Err(Error::UnexpectedEof(_)) => assert!(true), otherwise => panic!("Unexpected result: {:?}", otherwise), }; } @@ -3564,7 +3572,7 @@ mod tests { let rest = &mut EndianSlice::new(&buf, LittleEndian); match parse_type_offset(rest, Format::Dwarf32) { - Err(Error::UnexpectedEof) => assert!(true), + Err(Error::UnexpectedEof(_)) => assert!(true), otherwise => panic!("Unexpected result: {:?}", otherwise), }; }