~taiite/protodump

4fb1587013095429d896c05d2788c3fa4900eba0 — Hubert Hirtz a month ago 1e4c597 master
Replace panics with error returns

so that malformed input doesn't make protodump panic
2 files changed, 90 insertions(+), 37 deletions(-)

M src/dwarf.rs
M src/types.rs
M src/dwarf.rs => src/dwarf.rs +8 -7
@@ 1,4 1,5 @@
use crate::types;
use anyhow::Result;
use std::collections::HashMap;
use std::convert::TryFrom;
use std::hash::Hash;


@@ 100,7 101,7 @@ where
pub fn entry_int_attr<R>(
    entry: &gimli::DebuggingInformationEntry<R>,
    attr: gimli::DwAt,
) -> gimli::Result<Option<u64>>
) -> Result<Option<u64>>
where
    R: gimli::Reader,
{


@@ 116,11 117,11 @@ where
        gimli::AttributeValue::Data8(n) => n,
        gimli::AttributeValue::Sdata(n) => n as u64,
        gimli::AttributeValue::Udata(n) => n,
        av => panic!("unexpected attribute value for {}: {:?}", attr, av),
        av => anyhow::bail!("unexpected attribute value for {}: {:?}", attr, av),
    }))
}

pub fn entry_byte_size<R>(entry: &gimli::DebuggingInformationEntry<R>) -> gimli::Result<Option<u64>>
pub fn entry_byte_size<R>(entry: &gimli::DebuggingInformationEntry<R>) -> Result<Option<u64>>
where
    R: gimli::Reader,
{


@@ 129,7 130,7 @@ where

pub fn entry_encoding<R>(
    entry: &gimli::DebuggingInformationEntry<R>,
) -> gimli::Result<Option<crate::types::Encoding>>
) -> Result<Option<crate::types::Encoding>>
where
    R: gimli::Reader,
{


@@ 140,7 141,7 @@ where
    Ok(Some(match encoding_attr {
        gimli::AttributeValue::Encoding(encoding) => crate::types::Encoding::try_from(encoding)
            .map_err(|()| gimli::Error::Io /* TODO */)?,
        av => panic!("unexpected attribute value for DW_AT_encoding: {:?}", av),
        av => anyhow::bail!("unexpected attribute value for DW_AT_encoding: {:?}", av),
    }))
}



@@ 161,7 162,7 @@ pub fn entry_type<R>(
    cache: &mut types::Cache<R>,
    unit: &gimli::Unit<R>,
    entry: &gimli::DebuggingInformationEntry<R>,
) -> gimli::Result<Option<types::InstancePtr<R>>>
) -> Result<Option<types::InstancePtr<R>>>
where
    R: gimli::Reader,
{


@@ 189,7 190,7 @@ impl<'a, R> Sources<'a, R>
where
    R: gimli::Reader + Eq + Hash,
{
    pub fn parse(dwarf: &'a gimli::Dwarf<R>) -> gimli::Result<Sources<'a, R>> {
    pub fn parse(dwarf: &'a gimli::Dwarf<R>) -> Result<Sources<'a, R>> {
        let mut subprograms = HashMap::new();
        let mut type_cache = types::Cache::new(dwarf);


M src/types.rs => src/types.rs +82 -30
@@ 1,3 1,5 @@
use anyhow::Context;
use anyhow::Result;
use std::borrow::Cow;
use std::cell::RefCell;
use std::collections::hash_map;


@@ 7,6 9,23 @@ use std::fmt;
use std::rc::Rc;
use std::rc::Weak;

fn r<R>(val: &R) -> String
where
    R: gimli::Reader,
{
    val.to_string_lossy().unwrap().to_string()
}

fn or<R>(val: &Option<R>) -> String
where
    R: gimli::Reader,
{
    match val {
        Some(val) => r(val),
        None => String::new(),
    }
}

/// Description of how base types are encoded.
///
/// Enumeration version of `DW_AT_encoding` (see section 7.8).


@@ 306,10 325,12 @@ where
    fn resolve_base_definition(
        &mut self,
        entry: &gimli::DebuggingInformationEntry<R>,
    ) -> gimli::Result<Definition<R>> {
    ) -> Result<Definition<R>> {
        Ok(Definition::Base {
            byte_size: crate::dwarf::entry_byte_size(entry)?.unwrap(),
            encoding: crate::dwarf::entry_encoding(entry)?.unwrap(),
            byte_size: crate::dwarf::entry_byte_size(entry)?
                .context("expected base type to have a byte size")?,
            encoding: crate::dwarf::entry_encoding(entry)?
                .context("expected base type to have an encoding")?,
        })
    }



@@ 317,12 338,16 @@ where
        &mut self,
        unit: &gimli::Unit<R>,
        entry: &gimli::DebuggingInformationEntry<R>,
    ) -> gimli::Result<Definition<R>> {
        let byte_size = crate::dwarf::entry_byte_size(entry)?.unwrap();
    ) -> Result<Definition<R>> {
        let byte_size =
            crate::dwarf::entry_byte_size(entry)?.context("expected enum to have a byte size")?;
        let encoding = match crate::dwarf::entry_encoding(entry)? {
            None => match crate::dwarf::entry_type(self, unit, entry)? {
                None => {
                    assert_eq!(byte_size, 1);
                    anyhow::ensure!(
                        byte_size == 1,
                        "expected type with a byte size greater than one to have an encoding",
                    );
                    Encoding::Unsigned
                }
                Some(type_) => match &type_


@@ 334,7 359,9 @@ where
                    .definition
                {
                    Definition::Base { encoding, .. } => *encoding,
                    unexpected => panic!("unexpected underlying type for enum: {:?}", unexpected),
                    unexpected => {
                        anyhow::bail!("unexpected underlying type for enum: {:?}", unexpected)
                    }
                },
            },
            Some(encoding) => encoding,


@@ 346,8 373,10 @@ where
            if child.tag() != gimli::DW_TAG_enumerator {
                continue;
            }
            let name = crate::dwarf::entry_name(self.dwarf, unit, child)?.unwrap();
            let value = crate::dwarf::entry_int_attr(child, gimli::DW_AT_const_value)?.unwrap();
            let name = crate::dwarf::entry_name(self.dwarf, unit, child)?
                .context("expected enum variant to have a name")?;
            let value = crate::dwarf::entry_int_attr(child, gimli::DW_AT_const_value)?
                .with_context(|| format!("expected enum variant {:?} to have a value", r(&name)))?;
            enumerators.push(Enumerator { name, value });
        }



@@ 362,7 391,7 @@ where
        &mut self,
        unit: &gimli::Unit<R>,
        entry: &gimli::DebuggingInformationEntry<R>,
    ) -> gimli::Result<Definition<R>> {
    ) -> Result<Definition<R>> {
        let mut members = Vec::new();
        let mut children = crate::dwarf::entry_children(unit, entry)?;
        while let Some(child) = children.next()? {


@@ 370,9 399,16 @@ where
                continue;
            }
            let name = crate::dwarf::entry_name(self.dwarf, unit, child)?;
            let location =
                crate::dwarf::entry_int_attr(child, gimli::DW_AT_data_member_location)?.unwrap();
            let type_ = crate::dwarf::entry_type(self, unit, child)?.unwrap();
            let location = crate::dwarf::entry_int_attr(child, gimli::DW_AT_data_member_location)?
                .with_context(|| {
                    format!(
                        "expected DW_AT_data_member_location on structure member {:?}",
                        or(&name),
                    )
                })?;
            let type_ = crate::dwarf::entry_type(self, unit, child)?.with_context(|| {
                format!("expected structure member {:?} to have a type", or(&name))
            })?;
            members.push(Member {
                name,
                location,


@@ 386,7 422,7 @@ where
        &mut self,
        unit: &gimli::Unit<R>,
        entry: &gimli::DebuggingInformationEntry<R>,
    ) -> gimli::Result<Definition<R>> {
    ) -> Result<Definition<R>> {
        let return_type = crate::dwarf::entry_type(self, unit, entry)?;
        let mut parameters = Vec::new();
        let mut children = crate::dwarf::entry_children(unit, entry)?;


@@ 394,7 430,8 @@ where
            if child.tag() != gimli::DW_TAG_formal_parameter {
                continue;
            }
            let type_ = crate::dwarf::entry_type(self, unit, child)?.unwrap();
            let type_ = crate::dwarf::entry_type(self, unit, child)?
                .context("expected subroutine parameter to have a type")?;
            parameters.push(type_);
        }
        Ok(Definition::Subroutine {


@@ 407,7 444,7 @@ where
        &mut self,
        unit: &gimli::Unit<R>,
        entry: &gimli::DebuggingInformationEntry<R>,
    ) -> gimli::Result<Definition<R>> {
    ) -> Result<Definition<R>> {
        let mut members = Vec::new();
        let mut children = crate::dwarf::entry_children(unit, entry)?;
        while let Some(child) = children.next()? {


@@ 415,7 452,9 @@ where
                continue;
            }
            let name = crate::dwarf::entry_name(self.dwarf, unit, child)?;
            let type_ = crate::dwarf::entry_type(self, unit, child)?.unwrap();
            let type_ = crate::dwarf::entry_type(self, unit, child)?.with_context(|| {
                format!("expected union variant {:?} to have a type", or(&name))
            })?;
            members.push(Member {
                name,
                location: 0,


@@ 429,8 468,11 @@ where
        &mut self,
        unit: &gimli::Unit<R>,
        entry: &gimli::DebuggingInformationEntry<R>,
    ) -> gimli::Result<TypePtr<R>> {
        let entry_offset = entry.offset().to_debug_info_offset(&unit.header).unwrap();
    ) -> Result<TypePtr<R>> {
        let entry_offset = entry
            .offset()
            .to_debug_info_offset(&unit.header)
            .context("unit offset couldn't be converted to a .debug_info offset")?;

        // Allocate the definition and store it in the map in case the type is
        // recursive.


@@ 448,13 490,21 @@ where

        let name = crate::dwarf::entry_name(self.dwarf, unit, entry)?;
        let definition = match entry.tag() {
            gimli::DW_TAG_base_type => self.resolve_base_definition(entry)?,
            gimli::DW_TAG_enumeration_type => self.resolve_enumeration_definition(unit, entry)?,
            gimli::DW_TAG_structure_type | gimli::DW_TAG_class_type => {
                self.resolve_structure_definition(unit, entry)?
            }
            gimli::DW_TAG_subroutine_type => self.resolve_subroutine_definition(unit, entry)?,
            gimli::DW_TAG_union_type => self.resolve_union_definition(unit, entry)?,
            gimli::DW_TAG_base_type => self
                .resolve_base_definition(entry)
                .with_context(|| format!("failed to parse base type {:?}", or(&name)))?,
            gimli::DW_TAG_enumeration_type => self
                .resolve_enumeration_definition(unit, entry)
                .with_context(|| format!("failed to parse enum {:?}", or(&name)))?,
            gimli::DW_TAG_structure_type | gimli::DW_TAG_class_type => self
                .resolve_structure_definition(unit, entry)
                .with_context(|| format!("failed to parse structure {:?}", or(&name)))?,
            gimli::DW_TAG_subroutine_type => self
                .resolve_subroutine_definition(unit, entry)
                .with_context(|| format!("failed to parse subroutine {:?}", or(&name)))?,
            gimli::DW_TAG_union_type => self
                .resolve_union_definition(unit, entry)
                .with_context(|| format!("failed to union base type {:?}", or(&name)))?,
            _ => unreachable!(),
        };



@@ 467,8 517,10 @@ where
        &mut self,
        unit: &gimli::Unit<R>,
        mut entry_offset: gimli::UnitOffset<R::Offset>,
    ) -> gimli::Result<InstancePtr<R>> {
        let absolute_offset = entry_offset.to_debug_info_offset(&unit.header).unwrap();
    ) -> Result<InstancePtr<R>> {
        let absolute_offset = entry_offset
            .to_debug_info_offset(&unit.header)
            .context("unit offset couldn't be converted to a .debug_info offset")?;
        if let Some(instance) = self.instances.get(&absolute_offset) {
            return Ok(Rc::clone(instance));
        }


@@ 494,13 546,13 @@ where
                gimli::DW_TAG_restrict_type => modifiers.push(Modifier::Restrict),
                gimli::DW_TAG_volatile_type => modifiers.push(Modifier::Volatile),
                gimli::DW_TAG_typedef => {}
                tag => panic!("unexpected tag: {}", tag),
                tag => anyhow::bail!("unexpected tag for type: {}", tag),
            }

            if let Some(next_attr) = entry.attr_value(gimli::DW_AT_type)? {
                entry_offset = match next_attr {
                    gimli::AttributeValue::UnitRef(offset) => offset,
                    av => panic!("unexpected attribute value: {:?}", av),
                    av => anyhow::bail!("unexpected attribute value for type: {:?}", av),
                };
                continue;
            }