diff --git a/src/abbrev.rs b/src/abbrev.rs index f1c076ece..745c53696 100644 --- a/src/abbrev.rs +++ b/src/abbrev.rs @@ -259,15 +259,21 @@ impl Abbreviation { pub struct AttributeSpecification { name: constants::DwAt, form: constants::DwForm, + implicit_const_value: i64, } impl AttributeSpecification { - /// Construct a new `AttributeSpecification` from the given name and form. + /// Construct a new `AttributeSpecification` from the given name and form + /// and implicit const value. #[inline] - pub fn new(name: constants::DwAt, form: constants::DwForm) -> AttributeSpecification { + pub fn new(name: constants::DwAt, form: constants::DwForm, + implicit_const_value: Option) -> AttributeSpecification { + debug_assert!((form == constants::DW_FORM_implicit_const && implicit_const_value.is_some()) || + (form != constants::DW_FORM_implicit_const && implicit_const_value.is_none())); AttributeSpecification { name: name, form: form, + implicit_const_value: implicit_const_value.unwrap_or(0), } } @@ -283,6 +289,13 @@ impl AttributeSpecification { self.form } + /// Get the attribute's implicit const value. + #[inline] + pub fn implicit_const_value(&self) -> i64 { + assert!(self.form == constants::DW_FORM_implicit_const); + self.implicit_const_value + } + /// Return the size of the attribute, in bytes. /// /// Note that because some attributes are variably sized, the size cannot @@ -291,6 +304,8 @@ impl AttributeSpecification { match self.form { constants::DW_FORM_addr => Some(header.address_size() as usize), + constants::DW_FORM_implicit_const => Some(0), + constants::DW_FORM_flag | constants::DW_FORM_flag_present | constants::DW_FORM_data1 | @@ -358,7 +373,12 @@ impl AttributeSpecification { let name = constants::DwAt(name); let form = Self::parse_form(input)?; - let spec = AttributeSpecification::new(name, form); + let implicit_const_value = if form == constants::DW_FORM_implicit_const { + Some(input.read_sleb128()?) + } else { + None + }; + let spec = AttributeSpecification::new(name, form, implicit_const_value); Ok(Some(spec)) } } @@ -379,6 +399,7 @@ pub mod tests { fn abbrev(self, code: u64, tag: constants::DwTag, children: constants::DwChildren) -> Self; fn abbrev_null(self) -> Self; fn abbrev_attr(self, name: constants::DwAt, form: constants::DwForm) -> Self; + fn abbrev_attr_implicit_const(self, name: constants::DwAt, value: i64) -> Self; fn abbrev_attr_null(self) -> Self; } @@ -395,6 +416,10 @@ pub mod tests { self.uleb(name.0).uleb(form.0) } + fn abbrev_attr_implicit_const(self, name: constants::DwAt, value: i64) -> Self { + self.uleb(name.0).uleb(constants::DW_FORM_implicit_const.0).sleb(value) + } + fn abbrev_attr_null(self) -> Self { self.D8(0).D8(0) } @@ -427,8 +452,8 @@ pub mod tests { constants::DW_TAG_compile_unit, constants::DW_CHILDREN_yes, vec![ - AttributeSpecification::new(constants::DW_AT_producer, constants::DW_FORM_strp), - AttributeSpecification::new(constants::DW_AT_language, constants::DW_FORM_data2), + AttributeSpecification::new(constants::DW_AT_producer, constants::DW_FORM_strp, None), + AttributeSpecification::new(constants::DW_AT_language, constants::DW_FORM_data2, None), ], ); @@ -437,7 +462,7 @@ pub mod tests { constants::DW_TAG_subprogram, constants::DW_CHILDREN_no, vec![ - AttributeSpecification::new(constants::DW_AT_name, constants::DW_FORM_string), + AttributeSpecification::new(constants::DW_AT_name, constants::DW_FORM_string, None), ], ); @@ -571,8 +596,8 @@ pub mod tests { constants::DW_TAG_compile_unit, constants::DW_CHILDREN_yes, vec![ - AttributeSpecification::new(constants::DW_AT_producer, constants::DW_FORM_strp), - AttributeSpecification::new(constants::DW_AT_language, constants::DW_FORM_data2), + AttributeSpecification::new(constants::DW_AT_producer, constants::DW_FORM_strp, None), + AttributeSpecification::new(constants::DW_AT_language, constants::DW_FORM_data2, None), ], ); @@ -581,7 +606,7 @@ pub mod tests { constants::DW_TAG_subprogram, constants::DW_CHILDREN_no, vec![ - AttributeSpecification::new(constants::DW_AT_name, constants::DW_FORM_string), + AttributeSpecification::new(constants::DW_AT_name, constants::DW_FORM_string, None), ], ); @@ -668,7 +693,7 @@ pub mod tests { constants::DW_TAG_subprogram, constants::DW_CHILDREN_no, vec![ - AttributeSpecification::new(constants::DW_AT_name, constants::DW_FORM_string), + AttributeSpecification::new(constants::DW_AT_name, constants::DW_FORM_string, None), ], )); @@ -677,6 +702,46 @@ pub mod tests { assert_eq!(*rest, EndianBuf::new(&expected_rest, LittleEndian)); } + #[test] + fn test_parse_abbreviation_implicit_const_ok() { + let expected_rest = [0x01, 0x02, 0x03, 0x04]; + let buf = Section::new() + .abbrev(1, constants::DW_TAG_subprogram, constants::DW_CHILDREN_no) + .abbrev_attr_implicit_const(constants::DW_AT_name, -42) + .abbrev_attr_null() + .append_bytes(&expected_rest) + .get_contents() + .unwrap(); + let rest = &mut EndianBuf::new(&*buf, LittleEndian); + + let expect = Some(Abbreviation::new( + 1, + constants::DW_TAG_subprogram, + constants::DW_CHILDREN_no, + vec![ + AttributeSpecification::new(constants::DW_AT_name, constants::DW_FORM_implicit_const, Some(-42)), + ], + )); + + let abbrev = Abbreviation::parse(rest).expect("Should parse abbreviation"); + assert_eq!(abbrev, expect); + assert_eq!(*rest, EndianBuf::new(&expected_rest, LittleEndian)); + } + #[test] + fn test_parse_abbreviation_implicit_const_no_const() { + let buf = Section::new() + .abbrev(1, constants::DW_TAG_subprogram, constants::DW_CHILDREN_no) + .abbrev_attr(constants::DW_AT_name, constants::DW_FORM_implicit_const) + .get_contents() + .unwrap(); + let buf = &mut EndianBuf::new(&*buf, LittleEndian); + + match Abbreviation::parse(buf) { + Err(Error::UnexpectedEof) => {}, + otherwise => panic!("Unexpected result: {:?}", otherwise), + } + } + #[test] fn test_parse_null_abbreviation_ok() { let expected_rest = [0x01, 0x02, 0x03, 0x04]; diff --git a/src/parser.rs b/src/parser.rs index f29180351..b62405d2f 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -132,6 +132,8 @@ pub enum Error { /// The `.eh_frame_hdr` binary search table claims to be variable-length encoded, /// which makes binary search impossible. VariableLengthSearchTable, + /// The `DW_UT_*` value for this unit is not supported yet. + UnsupportedUnitType, } impl fmt::Display for Error { @@ -250,6 +252,9 @@ impl error::Error for Error { "The `.eh_frame_hdr` binary search table claims to be variable-length encoded, \ which makes binary search impossible." } + Error::UnsupportedUnitType => { + "The `DW_UT_*` value for this unit is not supported yet" + } } } } diff --git a/src/unit.rs b/src/unit.rs index 0f68a6e32..d53ed97e8 100644 --- a/src/unit.rs +++ b/src/unit.rs @@ -408,17 +408,10 @@ where } } -/// Parse the DWARF version from the compilation unit header. -fn parse_version(input: &mut R) -> Result { - let val = input.read_u16()?; - - // DWARF 1 was very different, and is obsolete, so isn't supported by this - // reader. - if 2 <= val && val <= 4 { - Ok(val) - } else { - Err(Error::UnknownVersion(val as u64)) - } +/// Parse the unit type from the compilation unit header. +fn parse_compilation_unit_type(input: &mut R) -> Result { + let val = input.read_u8()?; + Ok(constants::DwUt(val)) } /// Parse the `debug_abbrev_offset` in the compilation unit header. @@ -659,9 +652,24 @@ fn parse_unit_header(input: &mut R) -> Result(input: &mut R) -> Result { input.split(len) } -fn parse_attribute<'unit, R: Reader>( +fn parse_attribute<'unit, 'abbrev, R: Reader>( input: &mut R, unit: &'unit UnitHeader, - spec: AttributeSpecification, -) -> Result> { + mut specs: &'abbrev [AttributeSpecification], +) -> Result<(Attribute, &'abbrev [AttributeSpecification])> { + let spec = specs[0]; + specs = &specs[1..]; let mut form = spec.form(); loop { let value = match form { @@ -1733,6 +1743,9 @@ fn parse_attribute<'unit, R: Reader>( let offset = input.read_offset(unit.format())?; AttributeValue::DebugStrRef(DebugStrOffset(offset)) } + constants::DW_FORM_implicit_const => { + AttributeValue::Sdata(spec.implicit_const_value()) + } _ => { return Err(Error::UnknownForm); } @@ -1741,7 +1754,7 @@ fn parse_attribute<'unit, R: Reader>( name: spec.name(), value: value, }; - return Ok(attr); + return Ok((attr, specs)); } } @@ -1790,10 +1803,8 @@ impl<'abbrev, 'entry, 'unit, R: Reader> AttrsIter<'abbrev, 'entry, 'unit, R> { return Ok(None); } - let attr = self.attributes[0]; - let rest_attr = &self.attributes[1..]; - match parse_attribute(&mut self.input, self.entry.unit, attr) { - Ok(attr) => { + match parse_attribute(&mut self.input, self.entry.unit, &self.attributes[..]) { + Ok((attr, rest_attr)) => { self.attributes = rest_attr; Ok(Some(attr)) } @@ -2792,7 +2803,7 @@ mod tests { use super::*; use super::{parse_attribute, parse_debug_abbrev_offset, parse_type_offset, - parse_type_unit_header, parse_unit_header, parse_version}; + parse_type_unit_header, parse_unit_header}; use abbrev::{Abbreviation, AttributeSpecification, DebugAbbrev, DebugAbbrevOffset}; use abbrev::tests::AbbrevSectionMethods; use constants; @@ -2876,14 +2887,26 @@ mod tests { Format::Dwarf64 => self.L32(0xffffffff).L64(&length), }; - let section = section - .mark(&start) - .L16(unit.version) - .offset(unit.debug_abbrev_offset.0, unit.format) - .D8(unit.address_size) - .append_bytes(extra_header) - .append_bytes(unit.entries_buf.into()) - .mark(&end); + let section = match unit.version { + 2 | 3 | 4 => section + .mark(&start) + .L16(unit.version) + .offset(unit.debug_abbrev_offset.0, unit.format) + .D8(unit.address_size) + .append_bytes(extra_header) + .append_bytes(unit.entries_buf.into()) + .mark(&end), + 5 => section + .mark(&start) + .L16(unit.version) + .D8(constants::DW_UT_compile.0) + .D8(unit.address_size) + .offset(unit.debug_abbrev_offset.0, unit.format) + .append_bytes(extra_header) + .append_bytes(unit.entries_buf.into()) + .mark(&end), + _ => unreachable!(), + }; unit.unit_length = (&end - &start) as usize; length.set_const(unit.unit_length as u64); @@ -3052,35 +3075,20 @@ mod tests { assert_eq!(units.next(), Ok(None)); } - #[test] - fn test_unit_version_ok() { - // Version 4 and two extra bytes - let buf = [0x04, 0x00, 0xff, 0xff]; - let rest = &mut EndianBuf::new(&buf, LittleEndian); - - match parse_version(rest) { - Ok(val) => { - assert_eq!(val, 4); - assert_eq!(*rest, EndianBuf::new(&[0xff, 0xff], LittleEndian)); - } - otherwise => panic!("Unexpected result: {:?}", otherwise), - }; - } - #[test] fn test_unit_version_unknown_version() { - let buf = [0xab, 0xcd]; + let buf = [0x02, 0x00, 0x00, 0x00, 0xab, 0xcd]; let rest = &mut EndianBuf::new(&buf, LittleEndian); - match parse_version(rest) { + match parse_unit_header(rest) { Err(Error::UnknownVersion(0xcdab)) => assert!(true), otherwise => panic!("Unexpected result: {:?}", otherwise), }; - let buf = [0x1, 0x0]; + let buf = [0x02, 0x00, 0x00, 0x00, 0x1, 0x0]; let rest = &mut EndianBuf::new(&buf, LittleEndian); - match parse_version(rest) { + match parse_unit_header(rest) { Err(Error::UnknownVersion(1)) => assert!(true), otherwise => panic!("Unexpected result: {:?}", otherwise), }; @@ -3088,10 +3096,10 @@ mod tests { #[test] fn test_unit_version_incomplete() { - let buf = [0x04]; + let buf = [0x01, 0x00, 0x00, 0x00, 0x04]; let rest = &mut EndianBuf::new(&buf, LittleEndian); - match parse_version(rest) { + match parse_unit_header(rest) { Err(Error::UnexpectedEof) => assert!(true), otherwise => panic!("Unexpected result: {:?}", otherwise), }; @@ -3140,6 +3148,49 @@ mod tests { assert_eq!(*rest, EndianBuf::new(expected_rest, LittleEndian)); } + #[test] + fn test_parse_v5_unit_header_32_ok() { + let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; + let mut expected_unit = UnitHeader { + unit_length: 0, + version: 5, + debug_abbrev_offset: DebugAbbrevOffset(0x08070605), + address_size: 4, + format: Format::Dwarf32, + entries_buf: EndianBuf::new(expected_rest, LittleEndian), + }; + let section = Section::with_endian(Endian::Little) + .unit(&mut expected_unit, &[]) + .append_bytes(expected_rest); + let buf = section.get_contents().unwrap(); + let rest = &mut EndianBuf::new(&buf, LittleEndian); + + assert_eq!(parse_unit_header(rest), Ok(expected_unit)); + assert_eq!(*rest, EndianBuf::new(expected_rest, LittleEndian)); + } + + #[test] + #[cfg(target_pointer_width = "64")] + fn test_parse_v5_unit_header_64_ok() { + let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; + let mut expected_unit = UnitHeader { + unit_length: 0, + version: 5, + debug_abbrev_offset: DebugAbbrevOffset(0x0102030405060708), + address_size: 8, + format: Format::Dwarf64, + entries_buf: EndianBuf::new(expected_rest, LittleEndian), + }; + let section = Section::with_endian(Endian::Little) + .unit(&mut expected_unit, &[]) + .append_bytes(expected_rest); + let buf = section.get_contents().unwrap(); + let rest = &mut EndianBuf::new(&buf, LittleEndian); + + assert_eq!(parse_unit_header(rest), Ok(expected_unit)); + assert_eq!(*rest, EndianBuf::new(expected_rest, LittleEndian)); + } + #[test] fn test_parse_type_offset_32_ok() { let buf = [0x12, 0x34, 0x56, 0x78, 0x00]; @@ -3282,9 +3333,11 @@ mod tests { for test in tests.iter() { let (version, name, form, mut input, expect_raw, expect_value) = *test; unit.version = version; - let spec = AttributeSpecification::new(name, form); + let spec = vec![AttributeSpecification::new(name, form, None)]; let attribute = - parse_attribute(&mut input, &unit, spec).expect("Should parse attribute"); + parse_attribute(&mut input, &unit, &spec[..]) + .expect("Should parse attribute") + .0; assert_eq!(attribute.raw_value(), expect_raw); assert_eq!(attribute.value(), expect_value); } @@ -3371,7 +3424,7 @@ mod tests { ) where Endian: Endianity, { - let spec = AttributeSpecification::new(constants::DW_AT_low_pc, form); + let spec = vec![AttributeSpecification::new(constants::DW_AT_low_pc, form, None)]; let expect = Attribute { name: constants::DW_AT_low_pc, @@ -3379,8 +3432,8 @@ mod tests { }; let rest = &mut EndianBuf::new(buf, Endian::default()); - match parse_attribute(rest, unit, spec) { - Ok(attr) => { + match parse_attribute(rest, unit, &spec[..]) { + Ok((attr, _)) => { assert_eq!(attr, expect); assert_eq!(*rest, EndianBuf::new(&buf[len..], Endian::default())); } @@ -3737,9 +3790,9 @@ mod tests { constants::DW_TAG_subprogram, constants::DW_CHILDREN_yes, vec![ - AttributeSpecification::new(constants::DW_AT_name, constants::DW_FORM_string), - AttributeSpecification::new(constants::DW_AT_low_pc, constants::DW_FORM_addr), - AttributeSpecification::new(constants::DW_AT_high_pc, constants::DW_FORM_addr), + AttributeSpecification::new(constants::DW_AT_name, constants::DW_FORM_string, None), + AttributeSpecification::new(constants::DW_AT_low_pc, constants::DW_FORM_addr, None), + AttributeSpecification::new(constants::DW_AT_high_pc, constants::DW_FORM_addr, None), ], ); @@ -3855,9 +3908,9 @@ mod tests { constants::DW_TAG_subprogram, constants::DW_CHILDREN_yes, vec![ - AttributeSpecification::new(constants::DW_AT_name, constants::DW_FORM_string), - AttributeSpecification::new(constants::DW_AT_low_pc, constants::DW_FORM_addr), - AttributeSpecification::new(constants::DW_AT_high_pc, constants::DW_FORM_addr), + AttributeSpecification::new(constants::DW_AT_name, constants::DW_FORM_string, None), + AttributeSpecification::new(constants::DW_AT_low_pc, constants::DW_FORM_addr, None), + AttributeSpecification::new(constants::DW_AT_high_pc, constants::DW_FORM_addr, None), ], );