use std::borrow::Cow;
use std::cell::Cell;
use std::collections::{BTreeMap, HashMap};
use std::mem;
use std::rc::Rc;
use std::u32;
use gimli;
use gimli::Reader as GimliReader;
use object::{self, ObjectSection, ObjectSymbol};
use crate::cfi::{Cfi, CfiDirective};
use crate::file::{Architecture, DebugInfo, FileHash, StringCache};
use crate::function::{
Function, FunctionDetails, FunctionOffset, InlinedFunction, Parameter, ParameterOffset,
};
use crate::location::{Location, Piece, Register};
use crate::namespace::{Namespace, NamespaceKind};
use crate::range::Range;
use crate::source::Source;
use crate::types::{
ArrayType, BaseType, BaseTypeEncoding, Endianity, EnumerationType, Enumerator, FunctionType,
Inherit, Member, MemberOffset, ParameterType, PointerToMemberType, StructType, SubrangeType,
Type, TypeDef, TypeKind, TypeModifier, TypeModifierKind, TypeOffset, UnionType,
UnspecifiedType, Variant, VariantPart,
};
use crate::unit::Unit;
use crate::variable::{LocalVariable, Variable, VariableOffset};
use crate::{Address, Result, Size};
type RelocationMap = HashMap<usize, object::Relocation>;
fn add_relocations<'input, 'file, Object>(
relocations: &mut RelocationMap,
file: &'file Object,
section: &Object::Section,
) where
Object: object::Object<'input, 'file>,
{
for (offset64, mut relocation) in section.relocations() {
let offset = offset64 as usize;
if offset as u64 != offset64 {
continue;
}
let offset = offset as usize;
let target = match relocation.target() {
object::RelocationTarget::Symbol(index) => {
if let Ok(symbol) = file.symbol_by_index(index) {
symbol.address()
} else {
println!(
"Relocation with invalid symbol index {} for section {} at offset 0x{:08x}",
index.0,
section.name().unwrap(),
offset
);
continue;
}
}
object::RelocationTarget::Section(index) => {
if let Ok(section) = file.section_by_index(index) {
section.address()
} else {
println!(
"Relocation with invalid section index {} for section {} at offset 0x{:08x}",
index.0,
section.name().unwrap(),
offset
);
continue;
}
}
object::RelocationTarget::Absolute => {
continue;
}
};
match relocation.kind() {
object::RelocationKind::Absolute => {
let addend = target.wrapping_add(relocation.addend() as u64);
relocation.set_addend(addend as i64);
if relocations.insert(offset, relocation).is_some() {
println!(
"Multiple relocations for section {} at offset 0x{:08x}",
section.name().unwrap(),
offset
);
}
}
object::RelocationKind::Relative => {
let addend = target
.wrapping_add(relocation.addend() as u64)
.wrapping_sub(section.address())
.wrapping_sub(offset as u64);
relocation.set_addend(addend as i64);
if relocations.insert(offset, relocation).is_some() {
println!(
"Multiple relocations for section {} at offset 0x{:08x}",
section.name().unwrap(),
offset
);
}
}
_ => {
println!(
"Unsupported relocation for section {} at offset 0x{:08x}",
section.name().unwrap(),
offset
);
}
}
}
}
#[derive(Debug, Clone, Copy)]
struct Relocate<'a, R: gimli::Reader<Offset = usize>> {
relocations: &'a RelocationMap,
section: R,
reader: R,
}
impl<'a, R: gimli::Reader<Offset = usize>> Relocate<'a, R> {
fn relocate(&self, offset: usize, value: u64) -> u64 {
if let Some(relocation) = self.relocations.get(&offset) {
match relocation.kind() {
object::RelocationKind::Absolute | object::RelocationKind::Relative => {
if relocation.has_implicit_addend() {
return value.wrapping_add(relocation.addend() as u64);
} else {
return relocation.addend() as u64;
}
}
_ => {}
}
};
value
}
}
impl<'a, Endian> Relocate<'a, gimli::EndianSlice<'a, Endian>>
where
Endian: gimli::Endianity,
{
fn slice(&self) -> &'a [u8] {
self.reader.slice()
}
}
impl<'a, R: gimli::Reader<Offset = usize>> gimli::Reader for Relocate<'a, R> {
type Endian = R::Endian;
type Offset = R::Offset;
fn read_address(&mut self, address_size: u8) -> gimli::Result<u64> {
let offset = self.reader.offset_from(&self.section);
let value = self.reader.read_address(address_size)?;
Ok(self.relocate(offset, value))
}
fn read_length(&mut self, format: gimli::Format) -> gimli::Result<usize> {
let offset = self.reader.offset_from(&self.section);
let value = self.reader.read_length(format)?;
<usize as gimli::ReaderOffset>::from_u64(self.relocate(offset, value as u64))
}
fn read_offset(&mut self, format: gimli::Format) -> gimli::Result<usize> {
let offset = self.reader.offset_from(&self.section);
let value = self.reader.read_offset(format)?;
<usize as gimli::ReaderOffset>::from_u64(self.relocate(offset, value as u64))
}
fn read_sized_offset(&mut self, size: u8) -> gimli::Result<usize> {
let offset = self.reader.offset_from(&self.section);
let value = self.reader.read_sized_offset(size)?;
<usize as gimli::ReaderOffset>::from_u64(self.relocate(offset, value as u64))
}
#[inline]
fn split(&mut self, len: Self::Offset) -> gimli::Result<Self> {
let mut other = self.clone();
other.reader.truncate(len)?;
self.reader.skip(len)?;
Ok(other)
}
#[inline]
fn endian(&self) -> Self::Endian {
self.reader.endian()
}
#[inline]
fn len(&self) -> Self::Offset {
self.reader.len()
}
#[inline]
fn empty(&mut self) {
self.reader.empty()
}
#[inline]
fn truncate(&mut self, len: Self::Offset) -> gimli::Result<()> {
self.reader.truncate(len)
}
#[inline]
fn offset_from(&self, base: &Self) -> Self::Offset {
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::Offset> {
self.reader.lookup_offset_id(id)
}
#[inline]
fn find(&self, byte: u8) -> gimli::Result<Self::Offset> {
self.reader.find(byte)
}
#[inline]
fn skip(&mut self, len: Self::Offset) -> gimli::Result<()> {
self.reader.skip(len)
}
#[inline]
fn to_slice(&self) -> gimli::Result<Cow<[u8]>> {
self.reader.to_slice()
}
#[inline]
fn to_string(&self) -> gimli::Result<Cow<str>> {
self.reader.to_string()
}
#[inline]
fn to_string_lossy(&self) -> gimli::Result<Cow<str>> {
self.reader.to_string_lossy()
}
#[inline]
fn read_slice(&mut self, buf: &mut [u8]) -> gimli::Result<()> {
self.reader.read_slice(buf)
}
}
type Reader<'input, Endian> = Relocate<'input, gimli::EndianSlice<'input, Endian>>;
pub(crate) struct DwarfDebugInfo<'input, Endian>
where
Endian: gimli::Endianity,
{
endian: Endian,
read: gimli::Dwarf<Reader<'input, Endian>>,
frame: DwarfFrame<Reader<'input, Endian>>,
strings: &'input StringCache,
units: Vec<gimli::Unit<Reader<'input, Endian>, usize>>,
}
impl<'input, Endian> DwarfDebugInfo<'input, Endian>
where
Endian: gimli::Endianity,
{
fn string(
&self,
dwarf_unit: &DwarfUnit<'input, Endian>,
value: gimli::AttributeValue<Reader<'input, Endian>>,
) -> Option<&'input str> {
self.read
.attr_string(dwarf_unit, value)
.map(|r| self.strings.get(r.slice()))
.ok()
}
fn tree(
&self,
offset: gimli::DebugInfoOffset,
) -> Option<(
&DwarfUnit<'input, Endian>,
gimli::EntriesTree<Reader<'input, Endian>>,
)> {
let offset = gimli::UnitSectionOffset::DebugInfoOffset(offset);
for unit in &self.units {
if let Some(offset) = offset.to_unit_offset(unit) {
let tree = unit.entries_tree(Some(offset)).ok()?;
return Some((unit, tree));
}
}
None
}
fn type_tree(
&self,
offset: TypeOffset,
) -> Option<(
&DwarfUnit<'input, Endian>,
gimli::EntriesTree<Reader<'input, Endian>>,
)> {
offset
.get()
.and_then(|offset| self.tree(gimli::DebugInfoOffset(offset)))
}
fn function_tree(
&self,
offset: FunctionOffset,
) -> Option<(
&DwarfUnit<'input, Endian>,
gimli::EntriesTree<Reader<'input, Endian>>,
)> {
offset
.get()
.and_then(|offset| self.tree(gimli::DebugInfoOffset(offset)))
}
pub(crate) fn get_type(&self, offset: TypeOffset) -> Option<Type<'input>> {
self.type_tree(offset).and_then(|(unit, mut tree)| {
let node = tree.root().ok()?;
parse_unnamed_type(self, unit, node).ok()?
})
}
pub(crate) fn get_enumerators(&self, offset: TypeOffset) -> Vec<Enumerator<'input>> {
self.type_tree(offset)
.and_then(|(unit, mut tree)| {
let node = tree.root().ok()?;
parse_enumerators(self, unit, node).ok()
})
.unwrap_or_default()
}
pub(crate) fn get_function_details(
&self,
offset: FunctionOffset,
hash: &FileHash<'input>,
) -> Option<FunctionDetails<'input>> {
self.function_tree(offset).and_then(|(unit, mut tree)| {
let node = tree.root().ok()?;
parse_subprogram_details(hash, self, unit, node).ok()
})
}
pub(crate) fn get_cfi(&self, address: Address, size: Size) -> Vec<Cfi> {
self.frame.get_cfi(address, size).unwrap_or_default()
}
pub(crate) fn get_register_name(
&self,
machine: Architecture,
register: Register,
) -> Option<&'static str> {
let register_name = match machine {
Architecture::Arm => gimli::Arm::register_name,
Architecture::I386 => gimli::X86::register_name,
Architecture::X86_64 => gimli::X86_64::register_name,
_ => return None,
};
register_name(gimli::Register(register.0))
}
}
type DwarfUnit<'input, Endian> = gimli::Unit<Reader<'input, Endian>>;
struct DwarfSubprogram<'input> {
offset: gimli::UnitOffset,
specification: FunctionOffset,
abstract_origin: bool,
function: Function<'input>,
}
struct DwarfVariable<'input> {
offset: gimli::UnitOffset,
specification: Option<VariableOffset>,
variable: Variable<'input>,
}
pub(crate) fn parse<'input: 'file, 'file, Endian, Object, Cb>(
endian: Endian,
object: &'file Object,
strings: &'input StringCache,
cb: Cb,
) -> Result<()>
where
Endian: gimli::Endianity,
Object: object::Object<'input, 'file>,
Cb: FnOnce(Vec<Unit>, DebugInfo<Endian>) -> Result<()>,
{
let no_section = |_| Ok((Cow::Borrowed(&[][..]), RelocationMap::default()));
let get_section = |id: gimli::SectionId| -> Result<_> {
let mut relocations = RelocationMap::default();
let data = match object.section_by_name(id.name()) {
Some(ref section) => {
add_relocations(&mut relocations, object, section);
section.uncompressed_data()?
}
None => Cow::Borrowed(&[][..]),
};
Ok((data, relocations))
};
let get_reader: &dyn for<'a> Fn(&'a (Cow<[u8]>, RelocationMap)) -> Reader<'a, Endian> =
&|section| {
let relocations = §ion.1;
let reader = gimli::EndianSlice::new(§ion.0, endian);
Relocate {
relocations,
section: reader,
reader,
}
};
let sections = gimli::Dwarf::load(get_section, no_section)?;
let read = sections.borrow(get_reader);
let debug_frame = get_section(gimli::SectionId::DebugFrame)?;
let eh_frame = get_section(gimli::SectionId::EhFrame)?;
let mut bases = gimli::BaseAddresses::default();
if let Some(section) = object.section_by_name(".eh_frame") {
bases = bases.set_eh_frame(section.address());
}
if let Some(section) = object.section_by_name(".text") {
bases = bases.set_text(section.address());
}
if let Some(section) = object.section_by_name(".got") {
bases = bases.set_got(section.address());
}
let frame = DwarfFrame::new(
get_reader(&debug_frame).into(),
get_reader(&eh_frame).into(),
bases,
);
let mut dwarf = DwarfDebugInfo {
endian,
read,
frame,
strings,
units: Vec::new(),
};
let mut units = Vec::new();
let mut unit_headers = dwarf.read.units();
while let Some(unit_header) = unit_headers.next()? {
let dwarf_unit = dwarf.read.unit(unit_header)?;
units.push(parse_unit(&mut dwarf, dwarf_unit)?);
}
cb(units, DebugInfo::Dwarf(&dwarf))
}
fn parse_unit<'input, Endian>(
dwarf: &mut DwarfDebugInfo<'input, Endian>,
dwarf_unit: DwarfUnit<'input, Endian>,
) -> Result<Unit<'input>>
where
Endian: gimli::Endianity,
{
let mut unit = Unit::default();
unit.address_size = Some(u64::from(dwarf_unit.header.address_size()));
let mut subprograms = Vec::new();
let mut variables = Vec::new();
let mut tree = dwarf_unit.entries_tree(None)?;
let root = tree.root()?;
let entry = root.entry();
if entry.tag() != gimli::DW_TAG_compile_unit {
return Err(format!("unknown CU tag: {}", entry.tag()).into());
}
let mut ranges = None;
let mut high_pc = None;
let mut size = None;
let mut attrs = entry.attrs();
while let Some(attr) = attrs.next()? {
match attr.name() {
gimli::DW_AT_name => {
unit.name = dwarf.string(&dwarf_unit, attr.value()).map(Cow::Borrowed);
}
gimli::DW_AT_comp_dir => {
unit.dir = dwarf.string(&dwarf_unit, attr.value()).map(Cow::Borrowed);
}
gimli::DW_AT_language => {
if let gimli::AttributeValue::Language(language) = attr.value() {
unit.language = Some(language);
}
}
gimli::DW_AT_low_pc => {
if let gimli::AttributeValue::Addr(addr) = attr.value() {
unit.low_pc = Some(addr);
}
}
gimli::DW_AT_high_pc => match attr.value() {
gimli::AttributeValue::Addr(val) => high_pc = Some(val),
gimli::AttributeValue::Udata(val) => size = Some(val),
val => debug!("unknown CU DW_AT_high_pc: {:?}", val),
},
gimli::DW_AT_ranges => {
if let gimli::AttributeValue::RangeListsRef(val) = attr.value() {
ranges = Some(val);
}
}
gimli::DW_AT_stmt_list
| gimli::DW_AT_producer
| gimli::DW_AT_entry_pc
| gimli::DW_AT_APPLE_optimized
| gimli::DW_AT_macro_info
| gimli::DW_AT_GNU_macros
| gimli::DW_AT_GNU_pubnames
| gimli::DW_AT_sibling => {}
_ => debug!("unknown CU attribute: {} {:?}", attr.name(), attr.value()),
}
}
if let Some(program) = dwarf_unit.line_program.clone() {
let mut rows = program.rows();
let mut seq_addr = None;
while let Some((_, row)) = rows.next_row()? {
let addr = row.address();
if row.end_sequence() {
if let Some(seq_addr) = seq_addr {
if seq_addr != 0 {
unit.ranges.push(Range {
begin: seq_addr,
end: addr,
});
}
}
seq_addr = None;
} else if seq_addr.is_none() {
seq_addr = Some(addr);
}
}
} else if let Some(offset) = ranges {
let mut ranges = dwarf.read.ranges(&dwarf_unit, offset)?;
while let Some(range) = ranges.next()? {
if range.begin != 0 {
unit.ranges.push(Range {
begin: range.begin,
end: range.end,
});
}
}
} else if let Some(low_pc) = unit.low_pc {
if let Some(size) = size {
if high_pc.is_none() {
high_pc = low_pc.checked_add(size);
}
}
if let Some(high_pc) = high_pc {
unit.ranges.push(Range {
begin: low_pc,
end: high_pc,
});
}
}
unit.ranges.sort();
let namespace = None;
parse_namespace_children(
&mut unit,
dwarf,
&dwarf_unit,
&mut subprograms,
&mut variables,
&namespace,
root.children(),
)?;
fixup_subprogram_specifications(
&mut unit,
dwarf,
&dwarf_unit,
&mut subprograms,
&mut variables,
)?;
fixup_variable_specifications(&mut unit, dwarf, &dwarf_unit, &mut variables)?;
dwarf.units.push(dwarf_unit);
Ok(unit)
}
#[inline(never)]
fn fixup_subprogram_specifications<'input, Endian>(
unit: &mut Unit<'input>,
dwarf: &DwarfDebugInfo<'input, Endian>,
dwarf_unit: &DwarfUnit<'input, Endian>,
subprograms: &mut Vec<DwarfSubprogram<'input>>,
variables: &mut Vec<DwarfVariable<'input>>,
) -> Result<()>
where
Endian: gimli::Endianity,
{
let mut functions = BTreeMap::new();
for function in unit.functions.drain(..) {
functions.insert(function.offset, function);
}
let mut defer = Vec::new();
while !subprograms.is_empty() {
let mut progress = false;
mem::swap(&mut defer, subprograms);
for mut subprogram in defer.drain(..) {
if inherit_subprogram(
&functions,
&mut subprogram.function,
subprogram.specification,
subprogram.abstract_origin,
) {
let mut tree = dwarf_unit.entries_tree(Some(subprogram.offset))?;
parse_subprogram_children(
unit,
dwarf,
dwarf_unit,
subprograms,
variables,
&mut subprogram.function,
tree.root()?.children(),
)?;
let offset = subprogram.offset.to_unit_section_offset(dwarf_unit);
functions.insert(offset.into(), subprogram.function);
for function in unit.functions.drain(..) {
functions.insert(function.offset, function);
}
progress = true;
} else {
subprograms.push(subprogram);
}
}
if !progress {
debug!(
"invalid specification for {} subprograms",
subprograms.len()
);
mem::swap(&mut defer, subprograms);
for mut subprogram in defer.drain(..) {
let mut tree = dwarf_unit.entries_tree(Some(subprogram.offset))?;
parse_subprogram_children(
unit,
dwarf,
dwarf_unit,
subprograms,
variables,
&mut subprogram.function,
tree.root()?.children(),
)?;
let offset = subprogram.offset.to_unit_section_offset(dwarf_unit);
functions.insert(offset.into(), subprogram.function);
for function in unit.functions.drain(..) {
functions.insert(function.offset, function);
}
}
}
}
unit.functions = functions.into_iter().map(|(_, x)| x).collect();
Ok(())
}
#[inline(never)]
fn fixup_variable_specifications<'input, Endian>(
unit: &mut Unit<'input>,
_dwarf: &DwarfDebugInfo<'input, Endian>,
dwarf_unit: &DwarfUnit<'input, Endian>,
variables: &mut Vec<DwarfVariable<'input>>,
) -> Result<()>
where
Endian: gimli::Endianity,
{
let mut variable_map = BTreeMap::new();
for variable in unit.variables.drain(..) {
variable_map.insert(variable.offset, variable);
}
loop {
let mut progress = false;
let mut defer = Vec::new();
for mut variable in variables.drain(..) {
match variable.specification.and_then(|v| variable_map.get(&v)) {
Some(specification) => {
let variable = &mut variable.variable;
variable.namespace = specification.namespace.clone();
if variable.name.is_none() {
variable.name = specification.name;
}
if variable.linkage_name.is_none() {
variable.linkage_name = specification.linkage_name;
}
if variable.ty.is_none() {
variable.ty = specification.ty;
}
}
None => {
defer.push(variable);
continue;
}
}
let offset = variable.offset.to_unit_section_offset(dwarf_unit);
variable_map.insert(offset.into(), variable.variable);
progress = true;
}
if defer.is_empty() {
break;
}
if !progress {
debug!("invalid specification for {} variables", defer.len());
for variable in variables.drain(..) {
let offset = variable.offset.to_unit_section_offset(dwarf_unit);
variable_map.insert(offset.into(), variable.variable);
}
break;
}
*variables = defer;
}
unit.variables = variable_map.into_iter().map(|(_, x)| x).collect();
Ok(())
}
fn parse_namespace_children<'input, 'abbrev, 'unit, 'tree, Endian>(
unit: &mut Unit<'input>,
dwarf: &DwarfDebugInfo<'input, Endian>,
dwarf_unit: &DwarfUnit<'input, Endian>,
subprograms: &mut Vec<DwarfSubprogram<'input>>,
variables: &mut Vec<DwarfVariable<'input>>,
namespace: &Option<Rc<Namespace<'input>>>,
mut iter: gimli::EntriesTreeIter<'abbrev, 'unit, 'tree, Reader<'input, Endian>>,
) -> Result<()>
where
Endian: gimli::Endianity,
{
while let Some(child) = iter.next()? {
match child.entry().tag() {
gimli::DW_TAG_namespace => {
parse_namespace(
unit,
dwarf,
dwarf_unit,
subprograms,
variables,
namespace,
child,
)?;
}
gimli::DW_TAG_subprogram => {
parse_subprogram(
unit,
dwarf,
dwarf_unit,
subprograms,
variables,
namespace,
child,
)?;
}
gimli::DW_TAG_variable => {
let variable = parse_variable(unit, dwarf, dwarf_unit, namespace.clone(), child)?;
if variable.specification.is_some() {
variables.push(variable);
} else {
unit.variables.push(variable.variable);
}
}
gimli::DW_TAG_dwarf_procedure
| gimli::DW_TAG_imported_declaration
| gimli::DW_TAG_imported_module => {}
tag => {
if !parse_type(
unit,
dwarf,
dwarf_unit,
subprograms,
variables,
namespace,
child,
)? {
debug!("unknown namespace child tag: {}", tag);
}
}
}
}
Ok(())
}
fn parse_namespace<'input, 'abbrev, 'unit, 'tree, Endian>(
unit: &mut Unit<'input>,
dwarf: &DwarfDebugInfo<'input, Endian>,
dwarf_unit: &DwarfUnit<'input, Endian>,
subprograms: &mut Vec<DwarfSubprogram<'input>>,
variables: &mut Vec<DwarfVariable<'input>>,
namespace: &Option<Rc<Namespace<'input>>>,
node: gimli::EntriesTreeNode<'abbrev, 'unit, 'tree, Reader<'input, Endian>>,
) -> Result<()>
where
Endian: gimli::Endianity,
{
let mut name = None;
let entry = node.entry();
let mut attrs = entry.attrs();
while let Some(attr) = attrs.next()? {
match attr.name() {
gimli::DW_AT_name => {
name = dwarf.string(dwarf_unit, attr.value());
}
gimli::DW_AT_decl_file | gimli::DW_AT_decl_line | gimli::DW_AT_decl_column => {}
_ => debug!(
"unknown namespace attribute: {} {:?}",
attr.name(),
attr.value()
),
}
}
let namespace = Some(Namespace::new(namespace, name, NamespaceKind::Namespace));
parse_namespace_children(
unit,
dwarf,
dwarf_unit,
subprograms,
variables,
&namespace,
node.children(),
)
}
fn parse_type<'input, 'abbrev, 'unit, 'tree, Endian>(
unit: &mut Unit<'input>,
dwarf: &DwarfDebugInfo<'input, Endian>,
dwarf_unit: &DwarfUnit<'input, Endian>,
subprograms: &mut Vec<DwarfSubprogram<'input>>,
variables: &mut Vec<DwarfVariable<'input>>,
namespace: &Option<Rc<Namespace<'input>>>,
node: gimli::EntriesTreeNode<'abbrev, 'unit, 'tree, Reader<'input, Endian>>,
) -> Result<bool>
where
Endian: gimli::Endianity,
{
let tag = node.entry().tag();
let mut ty = Type::default();
let offset = node.entry().offset();
let offset = offset.to_unit_section_offset(dwarf_unit);
ty.offset = offset.into();
ty.kind = match tag {
gimli::DW_TAG_base_type => TypeKind::Base(parse_base_type(dwarf, dwarf_unit, node)?),
gimli::DW_TAG_typedef => TypeKind::Def(parse_typedef(dwarf, dwarf_unit, namespace, node)?),
gimli::DW_TAG_class_type | gimli::DW_TAG_structure_type => {
TypeKind::Struct(parse_structure_type(
unit,
dwarf,
dwarf_unit,
subprograms,
variables,
namespace,
node,
)?)
}
gimli::DW_TAG_union_type => TypeKind::Union(parse_union_type(
unit,
dwarf,
dwarf_unit,
subprograms,
variables,
namespace,
node,
)?),
gimli::DW_TAG_enumeration_type => TypeKind::Enumeration(parse_enumeration_type(
ty.offset,
unit,
dwarf,
dwarf_unit,
subprograms,
variables,
namespace,
node,
)?),
gimli::DW_TAG_unspecified_type => {
TypeKind::Unspecified(parse_unspecified_type(dwarf, dwarf_unit, namespace, node)?)
}
_ => return parse_unnamed_type(dwarf, dwarf_unit, node).map(|x| x.is_some()),
};
unit.types.push(ty);
Ok(true)
}
fn parse_unnamed_type<'input, 'abbrev, 'unit, 'tree, Endian>(
dwarf: &DwarfDebugInfo<'input, Endian>,
dwarf_unit: &DwarfUnit<'input, Endian>,
node: gimli::EntriesTreeNode<'abbrev, 'unit, 'tree, Reader<'input, Endian>>,
) -> Result<Option<Type<'input>>>
where
Endian: gimli::Endianity,
{
let tag = node.entry().tag();
let mut ty = Type::default();
let offset = node.entry().offset();
let offset = offset.to_unit_section_offset(dwarf_unit);
ty.offset = offset.into();
ty.kind = match tag {
gimli::DW_TAG_array_type => TypeKind::Array(parse_array_type(dwarf, dwarf_unit, node)?),
gimli::DW_TAG_subrange_type => {
TypeKind::Subrange(parse_subrange_type(dwarf, dwarf_unit, node)?)
}
gimli::DW_TAG_subroutine_type => {
TypeKind::Function(parse_subroutine_type(dwarf, dwarf_unit, node)?)
}
gimli::DW_TAG_ptr_to_member_type => {
TypeKind::PointerToMember(parse_pointer_to_member_type(dwarf, dwarf_unit, node)?)
}
gimli::DW_TAG_pointer_type => TypeKind::Modifier(parse_type_modifier(
dwarf,
dwarf_unit,
node,
TypeModifierKind::Pointer,
)?),
gimli::DW_TAG_reference_type => TypeKind::Modifier(parse_type_modifier(
dwarf,
dwarf_unit,
node,
TypeModifierKind::Reference,
)?),
gimli::DW_TAG_const_type => TypeKind::Modifier(parse_type_modifier(
dwarf,
dwarf_unit,
node,
TypeModifierKind::Const,
)?),
gimli::DW_TAG_packed_type => TypeKind::Modifier(parse_type_modifier(
dwarf,
dwarf_unit,
node,
TypeModifierKind::Packed,
)?),
gimli::DW_TAG_volatile_type => TypeKind::Modifier(parse_type_modifier(
dwarf,
dwarf_unit,
node,
TypeModifierKind::Volatile,
)?),
gimli::DW_TAG_restrict_type => TypeKind::Modifier(parse_type_modifier(
dwarf,
dwarf_unit,
node,
TypeModifierKind::Restrict,
)?),
gimli::DW_TAG_shared_type => TypeKind::Modifier(parse_type_modifier(
dwarf,
dwarf_unit,
node,
TypeModifierKind::Shared,
)?),
gimli::DW_TAG_rvalue_reference_type => TypeKind::Modifier(parse_type_modifier(
dwarf,
dwarf_unit,
node,
TypeModifierKind::RvalueReference,
)?),
gimli::DW_TAG_atomic_type => TypeKind::Modifier(parse_type_modifier(
dwarf,
dwarf_unit,
node,
TypeModifierKind::Atomic,
)?),
_ => return Ok(None),
};
Ok(Some(ty))
}
fn parse_type_modifier<'input, 'abbrev, 'unit, 'tree, Endian>(
dwarf: &DwarfDebugInfo<'input, Endian>,
dwarf_unit: &DwarfUnit<'input, Endian>,
node: gimli::EntriesTreeNode<'abbrev, 'unit, 'tree, Reader<'input, Endian>>,
kind: TypeModifierKind,
) -> Result<TypeModifier<'input>>
where
Endian: gimli::Endianity,
{
let mut modifier = TypeModifier {
kind,
ty: TypeOffset::none(),
name: None,
byte_size: Size::none(),
address_size: Some(u64::from(dwarf_unit.header.address_size())),
};
let mut attrs = node.entry().attrs();
while let Some(attr) = attrs.next()? {
match attr.name() {
gimli::DW_AT_name => {
modifier.name = dwarf.string(dwarf_unit, attr.value());
}
gimli::DW_AT_type => {
if let Some(offset) = parse_type_offset(dwarf_unit, &attr) {
modifier.ty = offset;
}
}
gimli::DW_AT_byte_size => {
if let Some(byte_size) = attr.udata_value() {
modifier.byte_size = Size::new(byte_size);
}
}
gimli::DW_AT_artificial => {}
_ => debug!(
"unknown type modifier attribute: {} {:?}",
attr.name(),
attr.value()
),
}
}
let mut iter = node.children();
while let Some(child) = iter.next()? {
match child.entry().tag() {
tag => {
debug!("unknown type modifier child tag: {}", tag);
}
}
}
Ok(modifier)
}
fn parse_base_type<'input, 'abbrev, 'unit, 'tree, Endian>(
dwarf: &DwarfDebugInfo<'input, Endian>,
dwarf_unit: &DwarfUnit<'input, Endian>,
node: gimli::EntriesTreeNode<'abbrev, 'unit, 'tree, Reader<'input, Endian>>,
) -> Result<BaseType<'input>>
where
Endian: gimli::Endianity,
{
let mut ty = BaseType::default();
let mut attrs = node.entry().attrs();
while let Some(attr) = attrs.next()? {
match attr.name() {
gimli::DW_AT_name => {
ty.name = dwarf.string(dwarf_unit, attr.value());
}
gimli::DW_AT_byte_size => {
if let Some(byte_size) = attr.udata_value() {
ty.byte_size = Size::new(byte_size);
}
}
gimli::DW_AT_encoding => {
if let gimli::AttributeValue::Encoding(val) = attr.value() {
ty.encoding = match val {
gimli::DW_ATE_boolean => BaseTypeEncoding::Boolean,
gimli::DW_ATE_address => BaseTypeEncoding::Address,
gimli::DW_ATE_signed => BaseTypeEncoding::Signed,
gimli::DW_ATE_signed_char => BaseTypeEncoding::SignedChar,
gimli::DW_ATE_unsigned => BaseTypeEncoding::Unsigned,
gimli::DW_ATE_unsigned_char => BaseTypeEncoding::UnsignedChar,
gimli::DW_ATE_float => BaseTypeEncoding::Float,
_ => {
debug!("unknown base type encoding: {} {:?}", attr.name(), val);
BaseTypeEncoding::Other
}
}
}
}
gimli::DW_AT_endianity => {
if let gimli::AttributeValue::Endianity(val) = attr.value() {
ty.endianity = match val {
gimli::DW_END_default => Endianity::Default,
gimli::DW_END_big => Endianity::Big,
gimli::DW_END_little => Endianity::Little,
_ => {
debug!("unknown base type endianity: {} {:?}", attr.name(), val);
Endianity::Default
}
}
}
}
gimli::DW_AT_artificial | gimli::DW_AT_decimal_scale => {}
_ => debug!(
"unknown base type attribute: {} {:?}",
attr.name(),
attr.value()
),
}
}
let mut iter = node.children();
while let Some(child) = iter.next()? {
match child.entry().tag() {
tag => {
debug!("unknown base type child tag: {}", tag);
}
}
}
Ok(ty)
}
fn parse_typedef<'input, 'abbrev, 'unit, 'tree, Endian>(
dwarf: &DwarfDebugInfo<'input, Endian>,
dwarf_unit: &DwarfUnit<'input, Endian>,
namespace: &Option<Rc<Namespace<'input>>>,
node: gimli::EntriesTreeNode<'abbrev, 'unit, 'tree, Reader<'input, Endian>>,
) -> Result<TypeDef<'input>>
where
Endian: gimli::Endianity,
{
let mut typedef = TypeDef::default();
typedef.namespace = namespace.clone();
let mut attrs = node.entry().attrs();
while let Some(attr) = attrs.next()? {
match attr.name() {
gimli::DW_AT_name => {
typedef.name = dwarf.string(dwarf_unit, attr.value());
}
gimli::DW_AT_type => {
if let Some(offset) = parse_type_offset(dwarf_unit, &attr) {
typedef.ty = offset;
}
}
gimli::DW_AT_decl_file => {
parse_source_file(dwarf, dwarf_unit, &attr, &mut typedef.source)
}
gimli::DW_AT_decl_line => parse_source_line(&attr, &mut typedef.source),
gimli::DW_AT_decl_column => parse_source_column(&attr, &mut typedef.source),
_ => debug!(
"unknown typedef attribute: {} {:?}",
attr.name(),
attr.value()
),
}
}
let mut iter = node.children();
while let Some(child) = iter.next()? {
match child.entry().tag() {
tag => {
debug!("unknown typedef child tag: {}", tag);
}
}
}
Ok(typedef)
}
fn parse_structure_type<'input, 'abbrev, 'unit, 'tree, Endian>(
unit: &mut Unit<'input>,
dwarf: &DwarfDebugInfo<'input, Endian>,
dwarf_unit: &DwarfUnit<'input, Endian>,
subprograms: &mut Vec<DwarfSubprogram<'input>>,
variables: &mut Vec<DwarfVariable<'input>>,
namespace: &Option<Rc<Namespace<'input>>>,
node: gimli::EntriesTreeNode<'abbrev, 'unit, 'tree, Reader<'input, Endian>>,
) -> Result<StructType<'input>>
where
Endian: gimli::Endianity,
{
let mut ty = StructType::default();
ty.namespace = namespace.clone();
let mut attrs = node.entry().attrs();
while let Some(attr) = attrs.next()? {
match attr.name() {
gimli::DW_AT_name => {
ty.name = dwarf.string(dwarf_unit, attr.value());
}
gimli::DW_AT_byte_size => {
if let Some(byte_size) = attr.udata_value() {
ty.byte_size = Size::new(byte_size);
}
}
gimli::DW_AT_declaration => {
if let gimli::AttributeValue::Flag(flag) = attr.value() {
ty.declaration = flag;
}
}
gimli::DW_AT_decl_file => parse_source_file(dwarf, dwarf_unit, &attr, &mut ty.source),
gimli::DW_AT_decl_line => parse_source_line(&attr, &mut ty.source),
gimli::DW_AT_decl_column => parse_source_column(&attr, &mut ty.source),
gimli::DW_AT_containing_type | gimli::DW_AT_alignment | gimli::DW_AT_sibling => {}
_ => debug!(
"unknown struct attribute: {} {:?}",
attr.name(),
attr.value()
),
}
}
let namespace = Some(Namespace::new(&ty.namespace, ty.name, NamespaceKind::Type));
let mut iter = node.children();
while let Some(child) = iter.next()? {
match child.entry().tag() {
gimli::DW_TAG_subprogram => {
parse_subprogram(
unit,
dwarf,
dwarf_unit,
subprograms,
variables,
&namespace,
child,
)?;
}
gimli::DW_TAG_member => {
parse_member(&mut ty.members, unit, dwarf, dwarf_unit, &namespace, child)?;
}
gimli::DW_TAG_inheritance => {
parse_inheritance(&mut ty.inherits, dwarf_unit, child)?;
}
gimli::DW_TAG_variant_part => {
parse_variant_part(
&mut ty.members,
&mut ty.variant_parts,
unit,
dwarf,
dwarf_unit,
&namespace,
child,
)?;
}
gimli::DW_TAG_template_type_parameter
| gimli::DW_TAG_template_value_parameter
| gimli::DW_TAG_GNU_template_parameter_pack => {}
tag => {
if !parse_type(
unit,
dwarf,
dwarf_unit,
subprograms,
variables,
&namespace,
child,
)? {
debug!("unknown struct child tag: {}", tag);
}
}
}
}
Ok(ty)
}
fn parse_union_type<'input, 'abbrev, 'unit, 'tree, Endian>(
unit: &mut Unit<'input>,
dwarf: &DwarfDebugInfo<'input, Endian>,
dwarf_unit: &DwarfUnit<'input, Endian>,
subprograms: &mut Vec<DwarfSubprogram<'input>>,
variables: &mut Vec<DwarfVariable<'input>>,
namespace: &Option<Rc<Namespace<'input>>>,
node: gimli::EntriesTreeNode<'abbrev, 'unit, 'tree, Reader<'input, Endian>>,
) -> Result<UnionType<'input>>
where
Endian: gimli::Endianity,
{
let mut ty = UnionType::default();
ty.namespace = namespace.clone();
let mut attrs = node.entry().attrs();
while let Some(attr) = attrs.next()? {
match attr.name() {
gimli::DW_AT_name => {
ty.name = dwarf.string(dwarf_unit, attr.value());
}
gimli::DW_AT_byte_size => {
if let Some(byte_size) = attr.udata_value() {
ty.byte_size = Size::new(byte_size);
}
}
gimli::DW_AT_declaration => {
if let gimli::AttributeValue::Flag(flag) = attr.value() {
ty.declaration = flag;
}
}
gimli::DW_AT_decl_file => parse_source_file(dwarf, dwarf_unit, &attr, &mut ty.source),
gimli::DW_AT_decl_line => parse_source_line(&attr, &mut ty.source),
gimli::DW_AT_decl_column => parse_source_column(&attr, &mut ty.source),
gimli::DW_AT_alignment | gimli::DW_AT_sibling => {}
_ => debug!(
"unknown union attribute: {} {:?}",
attr.name(),
attr.value()
),
}
}
let namespace = Some(Namespace::new(&ty.namespace, ty.name, NamespaceKind::Type));
let mut iter = node.children();
while let Some(child) = iter.next()? {
match child.entry().tag() {
gimli::DW_TAG_subprogram => {
parse_subprogram(
unit,
dwarf,
dwarf_unit,
subprograms,
variables,
&namespace,
child,
)?;
}
gimli::DW_TAG_member => {
parse_member(&mut ty.members, unit, dwarf, dwarf_unit, &namespace, child)?;
}
gimli::DW_TAG_template_type_parameter => {}
tag => {
if !parse_type(
unit,
dwarf,
dwarf_unit,
subprograms,
variables,
&namespace,
child,
)? {
debug!("unknown union child tag: {}", tag);
}
}
}
}
Ok(ty)
}
fn parse_variant_part<'input, 'abbrev, 'unit, 'tree, Endian>(
members: &mut Vec<Member<'input>>,
variant_parts: &mut Vec<VariantPart<'input>>,
unit: &mut Unit<'input>,
dwarf: &DwarfDebugInfo<'input, Endian>,
dwarf_unit: &DwarfUnit<'input, Endian>,
namespace: &Option<Rc<Namespace<'input>>>,
node: gimli::EntriesTreeNode<'abbrev, 'unit, 'tree, Reader<'input, Endian>>,
) -> Result<()>
where
Endian: gimli::Endianity,
{
let mut variant_part = VariantPart::default();
let mut attrs = node.entry().attrs();
while let Some(attr) = attrs.next()? {
match attr.name() {
gimli::DW_AT_discr => {
if let Some(offset) = parse_member_offset(dwarf_unit, &attr) {
variant_part.discr = offset;
}
}
gimli::DW_AT_sibling => {}
_ => debug!(
"unknown variant_part attribute: {} {:?}",
attr.name(),
attr.value()
),
}
}
let mut iter = node.children();
while let Some(child) = iter.next()? {
match child.entry().tag() {
gimli::DW_TAG_member => {
parse_member(members, unit, dwarf, dwarf_unit, namespace, child)?;
}
gimli::DW_TAG_variant => {
parse_variant(
&mut variant_part.variants,
unit,
dwarf,
dwarf_unit,
namespace,
child,
)?;
}
tag => {
debug!("unknown variant_part child tag: {}", tag);
}
}
}
variant_parts.push(variant_part);
Ok(())
}
fn parse_variant<'input, 'abbrev, 'unit, 'tree, Endian>(
variants: &mut Vec<Variant<'input>>,
unit: &mut Unit<'input>,
dwarf: &DwarfDebugInfo<'input, Endian>,
dwarf_unit: &DwarfUnit<'input, Endian>,
namespace: &Option<Rc<Namespace<'input>>>,
node: gimli::EntriesTreeNode<'abbrev, 'unit, 'tree, Reader<'input, Endian>>,
) -> Result<()>
where
Endian: gimli::Endianity,
{
let mut variant = Variant::default();
let mut attrs = node.entry().attrs();
while let Some(attr) = attrs.next()? {
match attr.name() {
gimli::DW_AT_discr_value => {
if let Some(value) = attr.udata_value() {
variant.discr_value = Some(value);
}
}
gimli::DW_AT_sibling => {}
_ => debug!(
"unknown variant attribute: {} {:?}",
attr.name(),
attr.value()
),
}
}
let mut iter = node.children();
while let Some(child) = iter.next()? {
match child.entry().tag() {
gimli::DW_TAG_member => {
parse_member(
&mut variant.members,
unit,
dwarf,
dwarf_unit,
namespace,
child,
)?;
}
gimli::DW_TAG_subrange_type => {
parse_subrange_type(dwarf, dwarf_unit, child)?;
}
tag => {
debug!("unknown variant child tag: {}", tag);
}
}
}
if unit.language == Some(gimli::DW_LANG_Rust) && variant.members.len() == 1 {
if let Some(offset) = variant.members[0].ty.get() {
let offset = gimli::UnitSectionOffset::DebugInfoOffset(gimli::DebugInfoOffset(offset));
if let Some(offset) = offset.to_unit_offset(dwarf_unit) {
let mut tree = dwarf_unit.entries_tree(Some(offset))?;
let node = tree.root()?;
if node.entry().tag() == gimli::DW_TAG_structure_type {
if let Some(attr) = node.entry().attr_value(gimli::DW_AT_name)? {
variant.name = dwarf.string(dwarf_unit, attr);
}
variant.members.clear();
let mut iter = node.children();
while let Some(child) = iter.next()? {
if child.entry().tag() == gimli::DW_TAG_member {
parse_member(
&mut variant.members,
unit,
dwarf,
dwarf_unit,
namespace,
child,
)?;
}
}
}
}
}
}
variants.push(variant);
Ok(())
}
fn parse_member<'input, 'abbrev, 'unit, 'tree, Endian>(
members: &mut Vec<Member<'input>>,
unit: &mut Unit<'input>,
dwarf: &DwarfDebugInfo<'input, Endian>,
dwarf_unit: &DwarfUnit<'input, Endian>,
namespace: &Option<Rc<Namespace<'input>>>,
node: gimli::EntriesTreeNode<'abbrev, 'unit, 'tree, Reader<'input, Endian>>,
) -> Result<()>
where
Endian: gimli::Endianity,
{
let mut member = Member::default();
let offset = node.entry().offset();
let offset = offset.to_unit_section_offset(dwarf_unit);
member.offset = offset.into();
let mut bit_offset = None;
let mut byte_size = None;
let mut declaration = false;
let mut attrs = node.entry().attrs();
while let Some(attr) = attrs.next()? {
match attr.name() {
gimli::DW_AT_name => {
member.name = dwarf.string(dwarf_unit, attr.value());
}
gimli::DW_AT_type => {
if let Some(offset) = parse_type_offset(dwarf_unit, &attr) {
member.ty = offset;
}
}
gimli::DW_AT_data_member_location => {
if let Some(offset) = parse_data_member_location(dwarf_unit, &attr) {
member.bit_offset = offset;
}
}
gimli::DW_AT_data_bit_offset => {
if let Some(bit_offset) = attr.udata_value() {
member.bit_offset = bit_offset;
}
}
gimli::DW_AT_bit_offset => {
bit_offset = attr.udata_value();
}
gimli::DW_AT_byte_size => {
byte_size = attr.udata_value();
}
gimli::DW_AT_bit_size => {
if let Some(bit_size) = attr.udata_value() {
member.bit_size = Size::new(bit_size);
}
}
gimli::DW_AT_declaration => {
declaration = true;
}
gimli::DW_AT_decl_file
| gimli::DW_AT_decl_line
| gimli::DW_AT_decl_column
| gimli::DW_AT_external
| gimli::DW_AT_accessibility
| gimli::DW_AT_artificial
| gimli::DW_AT_const_value
| gimli::DW_AT_alignment
| gimli::DW_AT_sibling => {}
_ => {
debug!(
"unknown member attribute: {} {:?}",
attr.name(),
attr.value()
);
}
}
}
if declaration {
let variable = parse_variable(unit, dwarf, dwarf_unit, namespace.clone(), node)?;
if variable.specification.is_some() {
debug!(
"specification on variable declaration at offset 0x{:x}",
variable.offset.0
);
}
unit.variables.push(variable.variable);
return Ok(());
}
if let (Some(bit_offset), Some(bit_size)) = (bit_offset, member.bit_size.get()) {
if dwarf.endian.is_big_endian() {
member.bit_offset = member.bit_offset.wrapping_add(bit_offset);
} else {
if let Some(byte_size) = byte_size {
member.bit_offset = member.bit_offset.wrapping_add(byte_size * 8);
member.bit_offset = member
.bit_offset
.wrapping_sub(bit_offset.wrapping_add(bit_size));
} else {
debug!("missing byte_size for bit field offset");
}
}
} else if byte_size.is_some() {
debug!("ignored member byte_size");
}
let mut iter = node.children();
while let Some(child) = iter.next()? {
match child.entry().tag() {
tag => {
debug!("unknown member child tag: {}", tag);
}
}
}
members.push(member);
Ok(())
}
fn parse_inheritance<'input, 'abbrev, 'unit, 'tree, Endian>(
inherits: &mut Vec<Inherit>,
dwarf_unit: &DwarfUnit<'input, Endian>,
node: gimli::EntriesTreeNode<'abbrev, 'unit, 'tree, Reader<'input, Endian>>,
) -> Result<()>
where
Endian: gimli::Endianity,
{
let mut inherit = Inherit::default();
let mut attrs = node.entry().attrs();
while let Some(attr) = attrs.next()? {
match attr.name() {
gimli::DW_AT_type => {
if let Some(offset) = parse_type_offset(dwarf_unit, &attr) {
inherit.ty = offset;
}
}
gimli::DW_AT_data_member_location => {
if let Some(offset) = parse_data_member_location(dwarf_unit, &attr) {
inherit.bit_offset = offset;
}
}
gimli::DW_AT_accessibility | gimli::DW_AT_virtuality | gimli::DW_AT_sibling => {}
_ => {
debug!(
"unknown inheritance attribute: {} {:?}",
attr.name(),
attr.value()
);
}
}
}
let mut iter = node.children();
while let Some(child) = iter.next()? {
match child.entry().tag() {
tag => {
debug!("unknown inheritance child tag: {}", tag);
}
}
}
inherits.push(inherit);
Ok(())
}
fn parse_data_member_location<Endian>(
dwarf_unit: &DwarfUnit<Endian>,
attr: &gimli::Attribute<Reader<Endian>>,
) -> Option<u64>
where
Endian: gimli::Endianity,
{
match attr.value() {
gimli::AttributeValue::Udata(v) => return Some(v * 8),
gimli::AttributeValue::Sdata(v) => {
if v >= 0 {
return Some((v as u64) * 8);
} else {
debug!("DW_AT_data_member_location is negative: {}", v)
}
}
gimli::AttributeValue::Exprloc(expr) => {
if let Some(offset) = evaluate_member_location(&dwarf_unit.header, expr) {
return Some(offset);
}
}
gimli::AttributeValue::LocationListsRef(offset) => {
if dwarf_unit.header.version() == 3 {
return Some(offset.0 as u64 * 8);
} else {
debug!("loclist for member: {:?}", attr.value());
}
}
_ => {
debug!("unknown DW_AT_data_member_location: {:?}", attr.value());
}
}
None
}
fn parse_enumeration_type<'input, 'abbrev, 'unit, 'tree, Endian>(
offset: TypeOffset,
unit: &mut Unit<'input>,
dwarf: &DwarfDebugInfo<'input, Endian>,
dwarf_unit: &DwarfUnit<'input, Endian>,
subprograms: &mut Vec<DwarfSubprogram<'input>>,
variables: &mut Vec<DwarfVariable<'input>>,
namespace: &Option<Rc<Namespace<'input>>>,
node: gimli::EntriesTreeNode<'abbrev, 'unit, 'tree, Reader<'input, Endian>>,
) -> Result<EnumerationType<'input>>
where
Endian: gimli::Endianity,
{
let mut ty = EnumerationType {
offset,
namespace: namespace.clone(),
..Default::default()
};
let mut attrs = node.entry().attrs();
while let Some(attr) = attrs.next()? {
match attr.name() {
gimli::DW_AT_name => {
ty.name = dwarf.string(dwarf_unit, attr.value());
}
gimli::DW_AT_byte_size => {
if let Some(byte_size) = attr.udata_value() {
ty.byte_size = Size::new(byte_size);
}
}
gimli::DW_AT_declaration => {
if let gimli::AttributeValue::Flag(flag) = attr.value() {
ty.declaration = flag;
}
}
gimli::DW_AT_decl_file => parse_source_file(dwarf, dwarf_unit, &attr, &mut ty.source),
gimli::DW_AT_decl_line => parse_source_line(&attr, &mut ty.source),
gimli::DW_AT_decl_column => parse_source_column(&attr, &mut ty.source),
gimli::DW_AT_sibling
| gimli::DW_AT_encoding
| gimli::DW_AT_type
| gimli::DW_AT_alignment
| gimli::DW_AT_enum_class => {}
_ => debug!(
"unknown enumeration attribute: {} {:?}",
attr.name(),
attr.value()
),
}
}
let namespace = Some(Namespace::new(&ty.namespace, ty.name, NamespaceKind::Type));
let mut iter = node.children();
while let Some(child) = iter.next()? {
match child.entry().tag() {
gimli::DW_TAG_subprogram => {
parse_subprogram(
unit,
dwarf,
dwarf_unit,
subprograms,
variables,
&namespace,
child,
)?;
}
gimli::DW_TAG_enumerator => {}
tag => {
debug!("unknown enumeration child tag: {}", tag);
}
}
}
Ok(ty)
}
fn parse_enumerators<'input, 'abbrev, 'unit, 'tree, Endian>(
dwarf: &DwarfDebugInfo<'input, Endian>,
dwarf_unit: &DwarfUnit<'input, Endian>,
node: gimli::EntriesTreeNode<'abbrev, 'unit, 'tree, Reader<'input, Endian>>,
) -> Result<Vec<Enumerator<'input>>>
where
Endian: gimli::Endianity,
{
let mut enumerators = Vec::new();
let mut iter = node.children();
while let Some(child) = iter.next()? {
match child.entry().tag() {
gimli::DW_TAG_enumerator => {
enumerators.push(parse_enumerator(dwarf, dwarf_unit, child)?);
}
_ => {}
}
}
Ok(enumerators)
}
fn parse_enumerator<'input, 'abbrev, 'unit, 'tree, Endian>(
dwarf: &DwarfDebugInfo<'input, Endian>,
dwarf_unit: &DwarfUnit<'input, Endian>,
node: gimli::EntriesTreeNode<'abbrev, 'unit, 'tree, Reader<'input, Endian>>,
) -> Result<Enumerator<'input>>
where
Endian: gimli::Endianity,
{
let mut enumerator = Enumerator::default();
let mut attrs = node.entry().attrs();
while let Some(ref attr) = attrs.next()? {
match attr.name() {
gimli::DW_AT_name => {
enumerator.name = dwarf.string(dwarf_unit, attr.value());
}
gimli::DW_AT_const_value => {
if let Some(value) = attr.sdata_value() {
enumerator.value = Some(value);
} else {
debug!("unknown enumerator const_value: {:?}", attr.value());
}
}
_ => debug!(
"unknown enumerator attribute: {} {:?}",
attr.name(),
attr.value()
),
}
}
let mut iter = node.children();
while let Some(child) = iter.next()? {
match child.entry().tag() {
tag => {
debug!("unknown enumerator child tag: {}", tag);
}
}
}
Ok(enumerator)
}
fn parse_array_type<'input, 'abbrev, 'unit, 'tree, Endian>(
_dwarf: &DwarfDebugInfo<'input, Endian>,
dwarf_unit: &DwarfUnit<'input, Endian>,
node: gimli::EntriesTreeNode<'abbrev, 'unit, 'tree, Reader<'input, Endian>>,
) -> Result<ArrayType<'input>>
where
Endian: gimli::Endianity,
{
let mut array = ArrayType::default();
let mut attrs = node.entry().attrs();
while let Some(attr) = attrs.next()? {
match attr.name() {
gimli::DW_AT_type => {
if let Some(offset) = parse_type_offset(dwarf_unit, &attr) {
array.ty = offset;
}
}
gimli::DW_AT_byte_size => {
if let Some(byte_size) = attr.udata_value() {
array.byte_size = Size::new(byte_size);
}
}
gimli::DW_AT_name | gimli::DW_AT_GNU_vector | gimli::DW_AT_sibling => {}
_ => debug!(
"unknown array attribute: {} {:?}",
attr.name(),
attr.value()
),
}
}
let mut iter = node.children();
while let Some(child) = iter.next()? {
match child.entry().tag() {
gimli::DW_TAG_subrange_type => {
let mut attrs = child.entry().attrs();
while let Some(attr) = attrs.next()? {
match attr.name() {
gimli::DW_AT_count => {
if let Some(count) = attr.udata_value() {
array.count = Size::new(count);
}
}
gimli::DW_AT_upper_bound => {
if array.byte_size.is_none() {
if let Some(upper_bound) = attr.udata_value() {
if let Some(count) = u64::checked_add(upper_bound, 1) {
array.count = Size::new(count);
} else {
debug!("overflow for array upper bound: {}", upper_bound);
}
}
}
}
gimli::DW_AT_type | gimli::DW_AT_lower_bound => {}
_ => debug!(
"unknown array subrange attribute: {} {:?}",
attr.name(),
attr.value()
),
}
}
}
tag => {
debug!("unknown array child tag: {}", tag);
}
}
}
Ok(array)
}
fn parse_subrange_type<'input, 'abbrev, 'unit, 'tree, Endian>(
dwarf: &DwarfDebugInfo<'input, Endian>,
dwarf_unit: &DwarfUnit<'input, Endian>,
node: gimli::EntriesTreeNode<'abbrev, 'unit, 'tree, Reader<'input, Endian>>,
) -> Result<SubrangeType<'input>>
where
Endian: gimli::Endianity,
{
let mut subrange = SubrangeType::default();
let mut count = None;
let mut attrs = node.entry().attrs();
while let Some(attr) = attrs.next()? {
match attr.name() {
gimli::DW_AT_name => {
subrange.name = dwarf.string(dwarf_unit, attr.value());
}
gimli::DW_AT_type => {
if let Some(offset) = parse_type_offset(dwarf_unit, &attr) {
subrange.ty = offset;
}
}
gimli::DW_AT_lower_bound => {
if let Some(lower) = attr.udata_value() {
subrange.lower = Some(lower);
}
}
gimli::DW_AT_upper_bound => {
if let Some(upper) = attr.udata_value() {
subrange.upper = Some(upper);
}
}
gimli::DW_AT_count => {
if let Some(v) = attr.udata_value() {
count = Some(v);
}
}
gimli::DW_AT_byte_size => {
if let Some(byte_size) = attr.udata_value() {
subrange.byte_size = Size::new(byte_size);
}
}
gimli::DW_AT_artificial => {}
_ => debug!(
"unknown subrange attribute: {} {:?}",
attr.name(),
attr.value()
),
}
}
if let (Some(lower), Some(count)) = (subrange.lower, count) {
subrange.upper = Some(lower + count);
}
let mut iter = node.children();
while let Some(child) = iter.next()? {
match child.entry().tag() {
tag => {
debug!("unknown subrange child tag: {}", tag);
}
}
}
Ok(subrange)
}
fn parse_subroutine_type<'input, 'abbrev, 'unit, 'tree, Endian>(
dwarf: &DwarfDebugInfo<'input, Endian>,
dwarf_unit: &DwarfUnit<'input, Endian>,
node: gimli::EntriesTreeNode<'abbrev, 'unit, 'tree, Reader<'input, Endian>>,
) -> Result<FunctionType<'input>>
where
Endian: gimli::Endianity,
{
let mut function = FunctionType {
byte_size: Size::new(u64::from(dwarf_unit.header.address_size())),
..Default::default()
};
let mut attrs = node.entry().attrs();
while let Some(attr) = attrs.next()? {
match attr.name() {
gimli::DW_AT_type => {
if let Some(offset) = parse_type_offset(dwarf_unit, &attr) {
function.return_type = offset;
}
}
gimli::DW_AT_name | gimli::DW_AT_prototyped | gimli::DW_AT_sibling => {}
_ => debug!(
"unknown subroutine attribute: {} {:?}",
attr.name(),
attr.value()
),
}
}
let mut iter = node.children();
while let Some(child) = iter.next()? {
match child.entry().tag() {
gimli::DW_TAG_formal_parameter => {
parse_parameter_type(&mut function.parameters, dwarf, dwarf_unit, child)?;
}
tag => {
debug!("unknown subroutine child tag: {}", tag);
}
}
}
Ok(function)
}
fn parse_unspecified_type<'input, 'abbrev, 'unit, 'tree, Endian>(
dwarf: &DwarfDebugInfo<'input, Endian>,
dwarf_unit: &DwarfUnit<'input, Endian>,
namespace: &Option<Rc<Namespace<'input>>>,
node: gimli::EntriesTreeNode<'abbrev, 'unit, 'tree, Reader<'input, Endian>>,
) -> Result<UnspecifiedType<'input>>
where
Endian: gimli::Endianity,
{
let mut ty = UnspecifiedType::default();
ty.namespace = namespace.clone();
let mut attrs = node.entry().attrs();
while let Some(attr) = attrs.next()? {
match attr.name() {
gimli::DW_AT_name => {
ty.name = dwarf.string(dwarf_unit, attr.value());
}
_ => debug!(
"unknown unspecified type attribute: {} {:?}",
attr.name(),
attr.value()
),
}
}
let mut iter = node.children();
while let Some(child) = iter.next()? {
match child.entry().tag() {
tag => {
debug!("unknown unspecified type child tag: {}", tag);
}
}
}
Ok(ty)
}
fn parse_pointer_to_member_type<'input, 'abbrev, 'unit, 'tree, Endian>(
_dwarf: &DwarfDebugInfo<'input, Endian>,
dwarf_unit: &DwarfUnit<'input, Endian>,
node: gimli::EntriesTreeNode<'abbrev, 'unit, 'tree, Reader<'input, Endian>>,
) -> Result<PointerToMemberType>
where
Endian: gimli::Endianity,
{
let mut ty = PointerToMemberType {
address_size: Some(u64::from(dwarf_unit.header.address_size())),
..Default::default()
};
let mut attrs = node.entry().attrs();
while let Some(attr) = attrs.next()? {
match attr.name() {
gimli::DW_AT_type => {
if let Some(offset) = parse_type_offset(dwarf_unit, &attr) {
ty.ty = offset;
}
}
gimli::DW_AT_containing_type => {
if let Some(offset) = parse_type_offset(dwarf_unit, &attr) {
ty.containing_ty = offset;
}
}
gimli::DW_AT_byte_size => {
if let Some(byte_size) = attr.udata_value() {
ty.byte_size = Size::new(byte_size);
}
}
_ => debug!(
"unknown ptr_to_member type attribute: {} {:?}",
attr.name(),
attr.value()
),
}
}
let mut iter = node.children();
while let Some(child) = iter.next()? {
match child.entry().tag() {
tag => {
debug!("unknown ptr_to_member type child tag: {}", tag);
}
}
}
Ok(ty)
}
fn parse_subprogram<'input, 'abbrev, 'unit, 'tree, Endian>(
unit: &mut Unit<'input>,
dwarf: &DwarfDebugInfo<'input, Endian>,
dwarf_unit: &DwarfUnit<'input, Endian>,
subprograms: &mut Vec<DwarfSubprogram<'input>>,
variables: &mut Vec<DwarfVariable<'input>>,
namespace: &Option<Rc<Namespace<'input>>>,
node: gimli::EntriesTreeNode<'abbrev, 'unit, 'tree, Reader<'input, Endian>>,
) -> Result<()>
where
Endian: gimli::Endianity,
{
let offset = node.entry().offset();
let mut function = Function {
id: Cell::new(0),
offset: offset.to_unit_section_offset(dwarf_unit).into(),
namespace: namespace.clone(),
name: None,
symbol_name: None,
linkage_name: None,
source: Source::default(),
address: Address::none(),
size: Size::none(),
inline: false,
declaration: false,
parameters: Vec::new(),
return_type: TypeOffset::none(),
};
let mut specification = None;
let mut abstract_origin = false;
let mut high_pc = None;
let entry = node.entry();
let mut attrs = entry.attrs();
while let Some(attr) = attrs.next()? {
match attr.name() {
gimli::DW_AT_name => {
function.name = dwarf.string(dwarf_unit, attr.value());
}
gimli::DW_AT_linkage_name | gimli::DW_AT_MIPS_linkage_name => {
function.linkage_name = dwarf.string(dwarf_unit, attr.value());
}
gimli::DW_AT_decl_file => {
parse_source_file(dwarf, dwarf_unit, &attr, &mut function.source)
}
gimli::DW_AT_decl_line => parse_source_line(&attr, &mut function.source),
gimli::DW_AT_decl_column => parse_source_column(&attr, &mut function.source),
gimli::DW_AT_inline => {
if let gimli::AttributeValue::Inline(val) = attr.value() {
match val {
gimli::DW_INL_inlined | gimli::DW_INL_declared_inlined => {
function.inline = true
}
_ => function.inline = false,
}
}
}
gimli::DW_AT_low_pc => {
if let gimli::AttributeValue::Addr(addr) = attr.value() {
function.address = Address::new(addr);
}
}
gimli::DW_AT_high_pc => match attr.value() {
gimli::AttributeValue::Addr(addr) => high_pc = Some(addr),
gimli::AttributeValue::Udata(val) => {
if val != 0 {
function.size = Size::new(val);
}
}
_ => {}
},
gimli::DW_AT_type => {
if let Some(offset) = parse_type_offset(dwarf_unit, &attr) {
function.return_type = offset;
}
}
gimli::DW_AT_specification | gimli::DW_AT_abstract_origin => {
if let Some(offset) = parse_function_offset(dwarf_unit, &attr) {
specification = Some(offset);
abstract_origin = attr.name() == gimli::DW_AT_abstract_origin;
}
}
gimli::DW_AT_declaration => {
if let gimli::AttributeValue::Flag(flag) = attr.value() {
function.declaration = flag;
}
}
gimli::DW_AT_frame_base => {
}
gimli::DW_AT_external
| gimli::DW_AT_GNU_all_call_sites
| gimli::DW_AT_GNU_all_tail_call_sites
| gimli::DW_AT_prototyped
| gimli::DW_AT_accessibility
| gimli::DW_AT_explicit
| gimli::DW_AT_artificial
| gimli::DW_AT_object_pointer
| gimli::DW_AT_virtuality
| gimli::DW_AT_vtable_elem_location
| gimli::DW_AT_containing_type
| gimli::DW_AT_main_subprogram
| gimli::DW_AT_noreturn
| gimli::DW_AT_APPLE_optimized
| gimli::DW_AT_APPLE_omit_frame_ptr
| gimli::DW_AT_sibling => {}
_ => debug!(
"unknown subprogram attribute: {} {:?}",
attr.name(),
attr.value()
),
}
}
if let (Some(address), Some(high_pc)) = (function.address(), high_pc) {
if high_pc > address {
function.size = Size::new(high_pc - address);
}
}
if let Some(specification) = specification {
subprograms.push(DwarfSubprogram {
offset,
specification,
abstract_origin,
function,
});
return Ok(());
}
parse_subprogram_children(
unit,
dwarf,
dwarf_unit,
subprograms,
variables,
&mut function,
node.children(),
)?;
unit.functions.push(function);
Ok(())
}
fn inherit_subprogram<'input>(
functions: &BTreeMap<FunctionOffset, Function<'input>>,
function: &mut Function<'input>,
specification: FunctionOffset,
abstract_origin: bool,
) -> bool {
let specification = match functions.get(&specification) {
Some(val) => val,
None => return false,
};
function.namespace = specification.namespace.clone();
if function.name.is_none() {
function.name = specification.name;
}
if function.linkage_name.is_none() {
function.linkage_name = specification.linkage_name;
}
if function.source.is_none() {
function.source = specification.source.clone();
}
if function.return_type.is_none() {
function.return_type = specification.return_type;
}
if specification.inline {
function.inline = true;
}
if abstract_origin {
function.parameters = specification.parameters.clone();
} else {
}
true
}
fn parse_subprogram_children<'input, 'abbrev, 'unit, 'tree, Endian>(
unit: &mut Unit<'input>,
dwarf: &DwarfDebugInfo<'input, Endian>,
dwarf_unit: &DwarfUnit<'input, Endian>,
subprograms: &mut Vec<DwarfSubprogram<'input>>,
variables: &mut Vec<DwarfVariable<'input>>,
function: &mut Function<'input>,
mut iter: gimli::EntriesTreeIter<'abbrev, 'unit, 'tree, Reader<'input, Endian>>,
) -> Result<()>
where
Endian: gimli::Endianity,
{
let namespace = Some(Namespace::new(
&function.namespace,
function.name,
NamespaceKind::Function,
));
while let Some(child) = iter.next()? {
match child.entry().tag() {
gimli::DW_TAG_formal_parameter => {
parse_parameter_type(&mut function.parameters, dwarf, dwarf_unit, child)?;
}
gimli::DW_TAG_variable => {
}
gimli::DW_TAG_inlined_subroutine => {
parse_inlined_subroutine(child)?;
}
gimli::DW_TAG_lexical_block => {
parse_lexical_block(
unit,
dwarf,
dwarf_unit,
subprograms,
variables,
&namespace,
child,
)?;
}
gimli::DW_TAG_subprogram => {
parse_subprogram(
unit,
dwarf,
dwarf_unit,
subprograms,
variables,
&namespace,
child,
)?;
}
gimli::DW_TAG_unspecified_parameters
| gimli::DW_TAG_template_type_parameter
| gimli::DW_TAG_template_value_parameter
| gimli::DW_TAG_GNU_template_parameter_pack
| gimli::DW_TAG_label
| gimli::DW_TAG_imported_declaration
| gimli::DW_TAG_imported_module
| gimli::DW_TAG_GNU_call_site => {}
tag => {
if !parse_type(
unit,
dwarf,
dwarf_unit,
subprograms,
variables,
&namespace,
child,
)? {
debug!("unknown subprogram child tag: {}", tag);
}
}
}
}
Ok(())
}
fn parse_parameter_type<'input, 'abbrev, 'unit, 'tree, Endian>(
parameters: &mut Vec<ParameterType<'input>>,
dwarf: &DwarfDebugInfo<'input, Endian>,
dwarf_unit: &DwarfUnit<'input, Endian>,
node: gimli::EntriesTreeNode<'abbrev, 'unit, 'tree, Reader<'input, Endian>>,
) -> Result<()>
where
Endian: gimli::Endianity,
{
let mut parameter = ParameterType::default();
let offset = node.entry().offset();
let offset = offset.to_unit_section_offset(dwarf_unit);
parameter.offset = offset.into();
let mut abstract_origin = None;
let mut attrs = node.entry().attrs();
while let Some(attr) = attrs.next()? {
match attr.name() {
gimli::DW_AT_abstract_origin => {
if let Some(offset) = parse_parameter_offset(dwarf_unit, &attr) {
abstract_origin = Some(offset);
}
}
gimli::DW_AT_name => {
parameter.name = dwarf.string(dwarf_unit, attr.value());
}
gimli::DW_AT_type => {
if let Some(offset) = parse_type_offset(dwarf_unit, &attr) {
parameter.ty = offset;
}
}
gimli::DW_AT_location
| gimli::DW_AT_decl_file
| gimli::DW_AT_decl_line
| gimli::DW_AT_decl_column
| gimli::DW_AT_artificial
| gimli::DW_AT_const_value
| gimli::DW_AT_sibling => {}
_ => debug!(
"unknown parameter attribute: {} {:?}",
attr.name(),
attr.value()
),
}
}
let mut iter = node.children();
while let Some(child) = iter.next()? {
match child.entry().tag() {
tag => {
debug!("unknown parameter child tag: {}", tag);
}
}
}
if let Some(abstract_origin) = abstract_origin {
if let Some(index) = parameters.iter().position(|x| x.offset == abstract_origin) {
let p = &mut parameters[index];
if parameter.name.is_some() {
p.name = parameter.name;
}
if parameter.ty.is_some() {
p.ty = parameter.ty;
}
return Ok(());
} else {
let unit_offset = offset
.to_unit_offset(dwarf_unit)
.unwrap_or(gimli::UnitOffset(0));
let offset = match offset {
gimli::UnitSectionOffset::DebugInfoOffset(offset) => offset.0,
_ => panic!("unexpected offset"),
};
let header_offset = match dwarf_unit.header.offset() {
gimli::UnitSectionOffset::DebugInfoOffset(offset) => offset.0,
_ => panic!("unexpected offset"),
};
debug!(
"missing parameter abstract origin: 0x{:08x}(0x{:08x}+0x{:08x})",
offset, header_offset, unit_offset.0
);
}
}
parameters.push(parameter);
Ok(())
}
fn parse_parameter<'input, 'abbrev, 'unit, 'tree, Endian>(
parameters: &mut Vec<Parameter<'input>>,
dwarf: &DwarfDebugInfo<'input, Endian>,
dwarf_unit: &DwarfUnit<'input, Endian>,
node: gimli::EntriesTreeNode<'abbrev, 'unit, 'tree, Reader<'input, Endian>>,
) -> Result<()>
where
Endian: gimli::Endianity,
{
let mut parameter = Parameter::default();
let offset = node.entry().offset();
let offset = offset.to_unit_section_offset(dwarf_unit);
parameter.offset = offset.into();
let mut abstract_origin = None;
let mut attrs = node.entry().attrs();
while let Some(attr) = attrs.next()? {
match attr.name() {
gimli::DW_AT_abstract_origin => {
if let Some(offset) = parse_parameter_offset(dwarf_unit, &attr) {
abstract_origin = Some(offset);
}
}
gimli::DW_AT_name => {
parameter.name = dwarf.string(dwarf_unit, attr.value());
}
gimli::DW_AT_type => {
if let Some(offset) = parse_type_offset(dwarf_unit, &attr) {
parameter.ty = offset;
}
}
gimli::DW_AT_location => {
match attr.value() {
gimli::AttributeValue::Exprloc(expr) => {
evaluate_parameter_location(
&dwarf_unit.header,
Range::all(),
expr,
&mut parameter,
);
}
gimli::AttributeValue::LocationListsRef(offset) => {
let mut locations = dwarf.read.locations(dwarf_unit, offset)?;
while let Some(location) = locations.next()? {
evaluate_parameter_location(
&dwarf_unit.header,
location.range.into(),
location.data,
&mut parameter,
);
}
}
_ => {
debug!("unknown parameter DW_AT_location: {:?}", attr.value());
}
}
}
gimli::DW_AT_decl_file
| gimli::DW_AT_decl_line
| gimli::DW_AT_decl_column
| gimli::DW_AT_artificial
| gimli::DW_AT_const_value
| gimli::DW_AT_sibling => {}
_ => debug!(
"unknown parameter attribute: {} {:?}",
attr.name(),
attr.value()
),
}
}
let mut iter = node.children();
while let Some(child) = iter.next()? {
match child.entry().tag() {
tag => {
debug!("unknown parameter child tag: {}", tag);
}
}
}
if let Some(abstract_origin) = abstract_origin {
if let Some(index) = parameters.iter().position(|x| x.offset == abstract_origin) {
let p = &mut parameters[index];
if parameter.name.is_some() {
p.name = parameter.name;
}
if parameter.ty.is_some() {
p.ty = parameter.ty;
}
if !parameter.locations.is_empty() {
p.locations.extend(¶meter.locations);
}
return Ok(());
} else {
let unit_offset = offset
.to_unit_offset(dwarf_unit)
.unwrap_or(gimli::UnitOffset(0));
let offset = match offset {
gimli::UnitSectionOffset::DebugInfoOffset(offset) => offset.0,
_ => panic!("unexpected offset"),
};
let header_offset = match dwarf_unit.header.offset() {
gimli::UnitSectionOffset::DebugInfoOffset(offset) => offset.0,
_ => panic!("unexpected offset"),
};
debug!(
"missing parameter abstract origin: 0x{:08x}(0x{:08x}+0x{:08x})",
offset, header_offset, unit_offset.0
);
}
}
parameters.push(parameter);
Ok(())
}
fn parse_lexical_block<'input, 'abbrev, 'unit, 'tree, Endian>(
unit: &mut Unit<'input>,
dwarf: &DwarfDebugInfo<'input, Endian>,
dwarf_unit: &DwarfUnit<'input, Endian>,
subprograms: &mut Vec<DwarfSubprogram<'input>>,
variables: &mut Vec<DwarfVariable<'input>>,
namespace: &Option<Rc<Namespace<'input>>>,
node: gimli::EntriesTreeNode<'abbrev, 'unit, 'tree, Reader<'input, Endian>>,
) -> Result<()>
where
Endian: gimli::Endianity,
{
let mut attrs = node.entry().attrs();
while let Some(attr) = attrs.next()? {
match attr.name() {
gimli::DW_AT_low_pc
| gimli::DW_AT_high_pc
| gimli::DW_AT_ranges
| gimli::DW_AT_abstract_origin
| gimli::DW_AT_sibling => {}
_ => debug!(
"unknown lexical_block attribute: {} {:?}",
attr.name(),
attr.value()
),
}
}
let mut iter = node.children();
while let Some(child) = iter.next()? {
match child.entry().tag() {
gimli::DW_TAG_variable => {
}
gimli::DW_TAG_inlined_subroutine => {
parse_inlined_subroutine(child)?;
}
gimli::DW_TAG_lexical_block => {
parse_lexical_block(
unit,
dwarf,
dwarf_unit,
subprograms,
variables,
namespace,
child,
)?;
}
gimli::DW_TAG_formal_parameter
| gimli::DW_TAG_label
| gimli::DW_TAG_imported_declaration
| gimli::DW_TAG_imported_module
| gimli::DW_TAG_GNU_call_site => {}
tag => {
if !parse_type(
unit,
dwarf,
dwarf_unit,
subprograms,
variables,
namespace,
child,
)? {
debug!("unknown lexical_block child tag: {}", tag);
}
}
}
}
Ok(())
}
fn parse_inlined_subroutine<'input, 'abbrev, 'unit, 'tree, Endian>(
node: gimli::EntriesTreeNode<'abbrev, 'unit, 'tree, Reader<'input, Endian>>,
) -> Result<()>
where
Endian: gimli::Endianity,
{
let mut iter = node.children();
while let Some(child) = iter.next()? {
match child.entry().tag() {
gimli::DW_TAG_formal_parameter | gimli::DW_TAG_variable => {
}
gimli::DW_TAG_inlined_subroutine => {
parse_inlined_subroutine(child)?;
}
gimli::DW_TAG_lexical_block => {
parse_inlined_lexical_block(child)?;
}
gimli::DW_TAG_GNU_call_site => {}
tag => {
debug!("unknown inlined_subroutine child tag: {}", tag);
}
}
}
Ok(())
}
fn parse_inlined_lexical_block<'input, 'abbrev, 'unit, 'tree, Endian>(
node: gimli::EntriesTreeNode<'abbrev, 'unit, 'tree, Reader<'input, Endian>>,
) -> Result<()>
where
Endian: gimli::Endianity,
{
let mut attrs = node.entry().attrs();
while let Some(attr) = attrs.next()? {
match attr.name() {
gimli::DW_AT_low_pc
| gimli::DW_AT_high_pc
| gimli::DW_AT_ranges
| gimli::DW_AT_abstract_origin
| gimli::DW_AT_sibling => {}
_ => debug!(
"unknown inlined lexical_block attribute: {} {:?}",
attr.name(),
attr.value()
),
}
}
let mut iter = node.children();
while let Some(child) = iter.next()? {
match child.entry().tag() {
gimli::DW_TAG_inlined_subroutine => {
parse_inlined_subroutine(child)?;
}
gimli::DW_TAG_lexical_block => {
parse_inlined_lexical_block(child)?;
}
gimli::DW_TAG_formal_parameter
| gimli::DW_TAG_variable
| gimli::DW_TAG_label
| gimli::DW_TAG_GNU_call_site
| gimli::DW_TAG_imported_module => {}
tag => {
debug!("unknown inlined lexical_block child tag: {}", tag);
}
}
}
Ok(())
}
fn parse_subprogram_details<'input, 'abbrev, 'unit, 'tree, Endian>(
hash: &FileHash<'input>,
dwarf: &DwarfDebugInfo<'input, Endian>,
dwarf_unit: &DwarfUnit<'input, Endian>,
node: gimli::EntriesTreeNode<'abbrev, 'unit, 'tree, Reader<'input, Endian>>,
) -> Result<FunctionDetails<'input>>
where
Endian: gimli::Endianity,
{
let mut abstract_origin = None;
let entry = node.entry();
let mut attrs = entry.attrs();
while let Some(attr) = attrs.next()? {
match attr.name() {
gimli::DW_AT_abstract_origin => {
if let Some(offset) = parse_function_offset(dwarf_unit, &attr) {
abstract_origin = Some(offset);
}
}
_ => {}
}
}
let mut details = abstract_origin
.and_then(|offset| dwarf.get_function_details(offset, hash))
.unwrap_or_else(|| FunctionDetails {
parameters: Vec::new(),
variables: Vec::new(),
inlined_functions: Vec::new(),
});
parse_subprogram_children_details(hash, dwarf, dwarf_unit, &mut details, node.children())?;
Ok(details)
}
fn parse_subprogram_children_details<'input, 'abbrev, 'unit, 'tree, Endian>(
hash: &FileHash<'input>,
dwarf: &DwarfDebugInfo<'input, Endian>,
dwarf_unit: &DwarfUnit<'input, Endian>,
function: &mut FunctionDetails<'input>,
mut iter: gimli::EntriesTreeIter<'abbrev, 'unit, 'tree, Reader<'input, Endian>>,
) -> Result<()>
where
Endian: gimli::Endianity,
{
while let Some(child) = iter.next()? {
match child.entry().tag() {
gimli::DW_TAG_formal_parameter => {
parse_parameter(&mut function.parameters, dwarf, dwarf_unit, child)?;
}
gimli::DW_TAG_variable => {
parse_local_variable(&mut function.variables, dwarf, dwarf_unit, child)?;
}
gimli::DW_TAG_inlined_subroutine => {
function
.inlined_functions
.push(parse_inlined_subroutine_details(
hash, dwarf, dwarf_unit, child,
)?);
}
gimli::DW_TAG_lexical_block => {
parse_lexical_block_details(
&mut function.inlined_functions,
&mut function.variables,
hash,
dwarf,
dwarf_unit,
child,
)?;
}
_ => {}
}
}
Ok(())
}
fn parse_lexical_block_details<'input, 'abbrev, 'unit, 'tree, Endian>(
inlined_functions: &mut Vec<InlinedFunction<'input>>,
local_variables: &mut Vec<LocalVariable<'input>>,
hash: &FileHash<'input>,
dwarf: &DwarfDebugInfo<'input, Endian>,
dwarf_unit: &DwarfUnit<'input, Endian>,
node: gimli::EntriesTreeNode<'abbrev, 'unit, 'tree, Reader<'input, Endian>>,
) -> Result<()>
where
Endian: gimli::Endianity,
{
let mut iter = node.children();
while let Some(child) = iter.next()? {
match child.entry().tag() {
gimli::DW_TAG_variable => {
parse_local_variable(local_variables, dwarf, dwarf_unit, child)?;
}
gimli::DW_TAG_inlined_subroutine => {
inlined_functions.push(parse_inlined_subroutine_details(
hash, dwarf, dwarf_unit, child,
)?);
}
gimli::DW_TAG_lexical_block => {
parse_lexical_block_details(
inlined_functions,
local_variables,
hash,
dwarf,
dwarf_unit,
child,
)?;
}
_ => {}
}
}
Ok(())
}
fn parse_inlined_subroutine_details<'input, 'abbrev, 'unit, 'tree, Endian>(
hash: &FileHash<'input>,
dwarf: &DwarfDebugInfo<'input, Endian>,
dwarf_unit: &DwarfUnit<'input, Endian>,
node: gimli::EntriesTreeNode<'abbrev, 'unit, 'tree, Reader<'input, Endian>>,
) -> Result<InlinedFunction<'input>>
where
Endian: gimli::Endianity,
{
let mut function = InlinedFunction::default();
let mut low_pc = None;
let mut high_pc = None;
let mut size = None;
let mut ranges = None;
let mut attrs = node.entry().attrs();
while let Some(attr) = attrs.next()? {
match attr.name() {
gimli::DW_AT_abstract_origin => {
if let Some(offset) = parse_function_offset(dwarf_unit, &attr) {
function.abstract_origin = offset;
}
}
gimli::DW_AT_low_pc => {
if let gimli::AttributeValue::Addr(addr) = attr.value() {
low_pc = Some(addr);
}
}
gimli::DW_AT_high_pc => match attr.value() {
gimli::AttributeValue::Addr(addr) => high_pc = Some(addr),
gimli::AttributeValue::Udata(val) => size = Some(val),
_ => {}
},
gimli::DW_AT_ranges => {
if let gimli::AttributeValue::RangeListsRef(val) = attr.value() {
ranges = Some(val);
}
}
gimli::DW_AT_call_file => {
parse_source_file(dwarf, dwarf_unit, &attr, &mut function.call_source)
}
gimli::DW_AT_call_line => parse_source_line(&attr, &mut function.call_source),
gimli::DW_AT_call_column => parse_source_column(&attr, &mut function.call_source),
gimli::DW_AT_entry_pc | gimli::DW_AT_sibling => {}
_ => debug!(
"unknown inlined_subroutine attribute: {} {:?}",
attr.name(),
attr.value()
),
}
}
if function.abstract_origin.is_some() {
if let Some(details) = dwarf.get_function_details(function.abstract_origin, hash) {
function.parameters = details.parameters;
function.variables = details.variables;
if !function.inlined_functions.is_empty() {
debug!("abstract origin with inlined functions");
}
} else {
debug!("inlined_subroutine with invalid abstract origin");
}
} else {
debug!("inlined_subroutine with no abstract origin");
}
if let Some(offset) = ranges {
let mut size = 0;
let mut ranges = dwarf.read.ranges(dwarf_unit, offset)?;
while let Some(range) = ranges.next()? {
size += range.end.wrapping_sub(range.begin);
}
function.size = Size::new(size);
} else if let Some(size) = size {
function.size = Size::new(size);
} else if let (Some(low_pc), Some(high_pc)) = (low_pc, high_pc) {
function.size = Size::new(high_pc.wrapping_sub(low_pc));
} else {
debug!("unknown inlined_subroutine size");
}
let mut iter = node.children();
while let Some(child) = iter.next()? {
match child.entry().tag() {
gimli::DW_TAG_formal_parameter => {
parse_parameter(&mut function.parameters, dwarf, dwarf_unit, child)?;
}
gimli::DW_TAG_variable => {
parse_local_variable(&mut function.variables, dwarf, dwarf_unit, child)?;
}
gimli::DW_TAG_inlined_subroutine => {
function
.inlined_functions
.push(parse_inlined_subroutine_details(
hash, dwarf, dwarf_unit, child,
)?);
}
gimli::DW_TAG_lexical_block => {
parse_lexical_block_details(
&mut function.inlined_functions,
&mut function.variables,
hash,
dwarf,
dwarf_unit,
child,
)?;
}
gimli::DW_TAG_GNU_call_site => {}
tag => {
debug!("unknown inlined_subroutine child tag: {}", tag);
}
}
}
Ok(function)
}
fn parse_variable<'input, 'abbrev, 'unit, 'tree, Endian>(
_unit: &mut Unit<'input>,
dwarf: &DwarfDebugInfo<'input, Endian>,
dwarf_unit: &DwarfUnit<'input, Endian>,
namespace: Option<Rc<Namespace<'input>>>,
node: gimli::EntriesTreeNode<'abbrev, 'unit, 'tree, Reader<'input, Endian>>,
) -> Result<DwarfVariable<'input>>
where
Endian: gimli::Endianity,
{
let offset = node.entry().offset();
let mut specification = None;
let mut variable = Variable {
offset: offset.to_unit_section_offset(dwarf_unit).into(),
namespace,
..Default::default()
};
let mut attrs = node.entry().attrs();
while let Some(attr) = attrs.next()? {
match attr.name() {
gimli::DW_AT_name => {
variable.name = dwarf.string(dwarf_unit, attr.value());
}
gimli::DW_AT_linkage_name | gimli::DW_AT_MIPS_linkage_name => {
variable.linkage_name = dwarf.string(dwarf_unit, attr.value());
}
gimli::DW_AT_type => {
if let Some(offset) = parse_type_offset(dwarf_unit, &attr) {
variable.ty = offset;
}
}
gimli::DW_AT_specification => {
if let Some(offset) = parse_variable_offset(dwarf_unit, &attr) {
specification = Some(offset);
}
}
gimli::DW_AT_declaration => {
if let gimli::AttributeValue::Flag(flag) = attr.value() {
variable.declaration = flag;
}
}
gimli::DW_AT_decl_file => {
parse_source_file(dwarf, dwarf_unit, &attr, &mut variable.source)
}
gimli::DW_AT_decl_line => parse_source_line(&attr, &mut variable.source),
gimli::DW_AT_decl_column => parse_source_column(&attr, &mut variable.source),
gimli::DW_AT_location => match attr.value() {
gimli::AttributeValue::Exprloc(expr) => {
if let Some((address, size)) =
evaluate_variable_location(&dwarf_unit.header, expr)
{
variable.address = address;
if size.is_some() {
variable.size = size;
}
}
}
gimli::AttributeValue::LocationListsRef(..) => {
debug!("loclist for variable: {:?}", attr.value());
}
_ => {
debug!("unknown variable DW_AT_location: {:?}", attr.value());
}
},
gimli::DW_AT_abstract_origin
| gimli::DW_AT_artificial
| gimli::DW_AT_const_value
| gimli::DW_AT_external
| gimli::DW_AT_accessibility
| gimli::DW_AT_alignment => {}
_ => debug!(
"unknown variable attribute: {} {:?}",
attr.name(),
attr.value()
),
}
}
let mut iter = node.children();
while let Some(child) = iter.next()? {
match child.entry().tag() {
tag => {
debug!("unknown variable child tag: {}", tag);
}
}
}
Ok(DwarfVariable {
offset,
specification,
variable,
})
}
fn parse_local_variable<'input, 'abbrev, 'unit, 'tree, Endian>(
variables: &mut Vec<LocalVariable<'input>>,
dwarf: &DwarfDebugInfo<'input, Endian>,
dwarf_unit: &DwarfUnit<'input, Endian>,
node: gimli::EntriesTreeNode<'abbrev, 'unit, 'tree, Reader<'input, Endian>>,
) -> Result<()>
where
Endian: gimli::Endianity,
{
let mut variable = LocalVariable::default();
let offset = node.entry().offset();
let offset = offset.to_unit_section_offset(dwarf_unit);
variable.offset = offset.into();
let mut abstract_origin = None;
let mut attrs = node.entry().attrs();
while let Some(attr) = attrs.next()? {
match attr.name() {
gimli::DW_AT_abstract_origin => {
if let Some(offset) = parse_variable_offset(dwarf_unit, &attr) {
abstract_origin = Some(offset);
}
}
gimli::DW_AT_name => {
variable.name = dwarf.string(dwarf_unit, attr.value());
}
gimli::DW_AT_type => {
if let Some(offset) = parse_type_offset(dwarf_unit, &attr) {
variable.ty = offset;
}
}
gimli::DW_AT_decl_file => {
parse_source_file(dwarf, dwarf_unit, &attr, &mut variable.source)
}
gimli::DW_AT_decl_line => parse_source_line(&attr, &mut variable.source),
gimli::DW_AT_decl_column => parse_source_column(&attr, &mut variable.source),
gimli::DW_AT_location => {
match attr.value() {
gimli::AttributeValue::Exprloc(expr) => {
evaluate_local_variable_location(
&dwarf_unit.header,
Range::all(),
expr,
&mut variable,
);
}
gimli::AttributeValue::LocationListsRef(offset) => {
let mut locations = dwarf.read.locations(dwarf_unit, offset)?;
while let Some(location) = locations.next()? {
evaluate_local_variable_location(
&dwarf_unit.header,
location.range.into(),
location.data,
&mut variable,
);
}
}
_ => {
debug!("unknown local variable DW_AT_location: {:?}", attr.value());
}
}
}
gimli::DW_AT_alignment
| gimli::DW_AT_artificial
| gimli::DW_AT_const_value
| gimli::DW_AT_external => {}
_ => debug!(
"unknown local variable attribute: {} {:?}",
attr.name(),
attr.value()
),
}
}
let mut iter = node.children();
while let Some(child) = iter.next()? {
match child.entry().tag() {
tag => {
debug!("unknown variable child tag: {}", tag);
}
}
}
if let Some(abstract_origin) = abstract_origin {
if let Some(index) = variables.iter().position(|x| x.offset == abstract_origin) {
let v = &mut variables[index];
if variable.name.is_some() {
v.name = variable.name;
}
if variable.ty.is_some() {
v.ty = variable.ty;
}
if variable.source.is_some() {
v.source = variable.source;
}
if variable.address.is_some() {
v.address = variable.address;
}
if variable.size.is_some() {
v.size = variable.size;
}
if !variable.locations.is_empty() {
v.locations.extend(&variable.locations);
}
return Ok(());
} else {
let unit_offset = offset
.to_unit_offset(dwarf_unit)
.unwrap_or(gimli::UnitOffset(0));
let offset = match offset {
gimli::UnitSectionOffset::DebugInfoOffset(offset) => offset.0,
_ => panic!("unexpected offset"),
};
let header_offset = match dwarf_unit.header.offset() {
gimli::UnitSectionOffset::DebugInfoOffset(offset) => offset.0,
_ => panic!("unexpected offset"),
};
debug!(
"missing variable abstract origin: 0x{:08x}(0x{:08x}+0x{:08x})",
offset, header_offset, unit_offset.0
);
}
}
variables.push(variable);
Ok(())
}
fn evaluate_member_location<'input, Endian>(
unit: &gimli::UnitHeader<Reader<'input, Endian>>,
expression: gimli::Expression<Reader<'input, Endian>>,
) -> Option<u64>
where
Endian: gimli::Endianity,
{
let pieces = evaluate(unit, expression, true);
if pieces.len() != 1 {
debug!("unsupported number of evaluation pieces: {:?}", pieces);
return None;
}
match pieces[0].location {
gimli::Location::Address { address } => Some(address * 8),
gimli::Location::Register { .. } => None,
_ => {
debug!("unknown DW_AT_data_member_location result: {:?}", pieces);
None
}
}
}
fn evaluate_variable_location<'input, Endian>(
unit: &gimli::UnitHeader<Reader<'input, Endian>>,
expression: gimli::Expression<Reader<'input, Endian>>,
) -> Option<(Address, Size)>
where
Endian: gimli::Endianity,
{
let pieces = evaluate(unit, expression, false);
let mut result = None;
for piece in &*pieces {
match piece.location {
gimli::Location::Address { address } => {
if result.is_some() {
debug!(
"unsupported DW_AT_location with multiple addresses: {:?}",
pieces
);
} else {
let address = Address::new(address);
let size = match piece.size_in_bits.map(|x| (x + 7) / 8) {
Some(size) => Size::new(size),
None => Size::none(),
};
result = Some((address, size));
}
}
gimli::Location::Empty
| gimli::Location::Register { .. }
| gimli::Location::Value { .. }
| gimli::Location::ImplicitPointer { .. } => {}
_ => debug!("unknown DW_AT_location piece: {:?}", piece),
}
}
result
}
fn evaluate_local_variable_location<'input, Endian>(
unit: &gimli::UnitHeader<Reader<'input, Endian>>,
range: Range,
expression: gimli::Expression<Reader<'input, Endian>>,
variable: &mut LocalVariable<'input>,
) where
Endian: gimli::Endianity,
{
let pieces = match evaluate_simple(unit, expression, false) {
Ok(locations) => locations,
Err(_e) => {
return;
}
};
for piece in &pieces {
if piece.is_value {
continue;
}
if let Location::Address { address } = piece.location {
if variable.address.is_some() {
if address != variable.address {
debug!(
"unsupported DW_AT_location with multiple addresses: {:?}",
pieces
);
}
} else {
variable.address = address;
if let Some(bit_size) = piece.bit_size.get() {
variable.size = Size::new((bit_size + 7) / 8);
}
}
}
}
variable
.locations
.extend(pieces.into_iter().map(|piece| (range, piece)));
}
fn evaluate_parameter_location<'input, Endian>(
unit: &gimli::UnitHeader<Reader<'input, Endian>>,
range: Range,
expression: gimli::Expression<Reader<'input, Endian>>,
parameter: &mut Parameter<'input>,
) where
Endian: gimli::Endianity,
{
let pieces = match evaluate_simple(unit, expression, false) {
Ok(locations) => locations,
Err(_e) => {
return;
}
};
parameter
.locations
.extend(pieces.into_iter().map(|piece| (range, piece)));
}
fn evaluate_simple<'input, Endian>(
unit: &gimli::UnitHeader<Reader<'input, Endian>>,
expression: gimli::Expression<Reader<'input, Endian>>,
_object_address: bool,
) -> Result<Vec<Piece>>
where
Endian: gimli::Endianity + 'input,
{
let encoding = unit.encoding();
let addr_mask = if encoding.address_size == 8 {
!0u64
} else {
(1 << (8 * u64::from(encoding.address_size))) - 1
};
let mut bytes = expression.0;
let mut pieces = Vec::new();
let mut next_bit_offset = 0;
let mut add_piece = |pieces: &mut Vec<Piece>,
location: Location,
location_offset: u64,
is_value: bool,
bit_size: Size| {
let bit_offset = next_bit_offset;
next_bit_offset += bit_size.get().unwrap_or(0);
pieces.push(Piece {
bit_offset,
bit_size,
location,
location_offset,
is_value,
});
};
let mut stack = Vec::new();
let pop = |stack: &mut Vec<Location>| match stack.pop() {
Some(value) => Ok(value),
None => Err(gimli::Error::NotEnoughStackItems),
};
let mut location = None;
while !bytes.is_empty() {
match gimli::Operation::parse(&mut bytes, encoding)? {
gimli::Operation::Nop => {}
gimli::Operation::Register { register } => {
location = Some((
Location::Register {
register: register.into(),
},
false,
));
}
gimli::Operation::ImplicitValue { .. } => {
location = Some((Location::Other, true));
}
gimli::Operation::ImplicitPointer { .. } => {
location = Some((Location::Other, false));
}
gimli::Operation::StackValue => {
location = Some((pop(&mut stack)?, true));
}
gimli::Operation::EntryValue { .. }
| gimli::Operation::ParameterRef { .. }
| gimli::Operation::TypedLiteral { .. }
| gimli::Operation::PushObjectAddress => {
stack.push(Location::Other);
}
gimli::Operation::UnsignedConstant { value } => {
stack.push(Location::Literal { value });
}
gimli::Operation::SignedConstant { value } => {
stack.push(Location::Literal {
value: value as u64,
});
}
gimli::Operation::RegisterOffset {
register, offset, ..
} => {
stack.push(Location::RegisterOffset {
register: register.into(),
offset,
});
}
gimli::Operation::FrameOffset { offset } => {
stack.push(Location::FrameOffset { offset });
}
gimli::Operation::CallFrameCFA => {
stack.push(Location::CfaOffset { offset: 0 });
}
gimli::Operation::Address { address } => {
stack.push(Location::Address {
address: Address::new(address),
});
}
gimli::Operation::AddressIndex { .. } | gimli::Operation::ConstantIndex { .. } => {
stack.push(Location::Other);
}
gimli::Operation::TLS => {
let location = match pop(&mut stack)? {
Location::Literal { value } => Location::TlsOffset { offset: value },
Location::Other => Location::Other,
location => {
debug!("unsupported TLS: {:?}", location);
Location::Other
}
};
stack.push(location);
}
gimli::Operation::Piece {
size_in_bits,
bit_offset,
} => {
let location = stack.pop().unwrap_or(Location::Empty);
add_piece(
&mut pieces,
location,
bit_offset.unwrap_or(0),
false,
Size::new(size_in_bits),
);
}
gimli::Operation::Drop => {
pop(&mut stack)?;
}
gimli::Operation::Swap => {
let one = pop(&mut stack)?;
let two = pop(&mut stack)?;
stack.push(one);
stack.push(two);
}
gimli::Operation::Rot => {
let one = pop(&mut stack)?;
let two = pop(&mut stack)?;
let three = pop(&mut stack)?;
stack.push(one);
stack.push(three);
stack.push(two);
}
gimli::Operation::Pick { index } => {
let index = index as usize;
if index >= stack.len() {
return Err(gimli::Error::NotEnoughStackItems.into());
}
let location = stack[stack.len() - index - 1];
stack.push(location);
}
gimli::Operation::PlusConstant { value: constant } => {
let location = match pop(&mut stack)? {
Location::Literal { value } => {
let value = value.wrapping_add(constant) & addr_mask;
Location::Literal { value }
}
Location::RegisterOffset { register, offset } => {
let offset = ((offset as u64).wrapping_add(constant) & addr_mask) as i64;
Location::RegisterOffset { register, offset }
}
Location::FrameOffset { offset } => {
let offset = ((offset as u64).wrapping_add(constant) & addr_mask) as i64;
Location::FrameOffset { offset }
}
Location::CfaOffset { offset } => {
let offset = ((offset as u64).wrapping_add(constant) & addr_mask) as i64;
Location::CfaOffset { offset }
}
Location::Other => Location::Other,
location => {
debug!("unsupported PlusConstant: {:?}", location);
Location::Other
}
};
stack.push(location);
}
gimli::Operation::Plus => {
let one = pop(&mut stack)?;
let two = pop(&mut stack)?;
match (one, two) {
(Location::Other, _) | (_, Location::Other) => Location::Other,
(Location::RegisterOffset { .. }, Location::RegisterOffset { .. }) => {
Location::Other
}
location => {
debug!("unsupported Plus: {:?}", location);
Location::Other
}
};
}
gimli::Operation::Minus => {
let one = pop(&mut stack)?;
let two = pop(&mut stack)?;
match (one, two) {
(Location::Other, _) | (_, Location::Other) => Location::Other,
(Location::RegisterOffset { .. }, Location::RegisterOffset { .. }) => {
Location::Other
}
(Location::Literal { value }, Location::FrameOffset { offset }) => {
Location::FrameOffset {
offset: offset - value as i64,
}
}
location => {
debug!("unsupported Minus: {:?}", location);
Location::Other
}
};
}
gimli::Operation::Neg
| gimli::Operation::Not
| gimli::Operation::Abs
| gimli::Operation::Convert { .. }
| gimli::Operation::Reinterpret { .. } => {
pop(&mut stack)?;
stack.push(Location::Other);
}
gimli::Operation::Mul
| gimli::Operation::Div
| gimli::Operation::Mod
| gimli::Operation::Shl
| gimli::Operation::Shr
| gimli::Operation::Shra
| gimli::Operation::And
| gimli::Operation::Or
| gimli::Operation::Xor
| gimli::Operation::Eq
| gimli::Operation::Ne
| gimli::Operation::Gt
| gimli::Operation::Ge
| gimli::Operation::Lt
| gimli::Operation::Le => {
pop(&mut stack)?;
pop(&mut stack)?;
stack.push(Location::Other);
}
gimli::Operation::Deref { space, .. } => {
pop(&mut stack)?;
if space {
pop(&mut stack)?;
}
stack.push(Location::Other);
}
gimli::Operation::Bra { .. }
| gimli::Operation::Skip { .. }
| gimli::Operation::Call { .. } => {
return Ok(pieces);
}
}
if let Some((location, is_value)) = location {
if bytes.is_empty() {
if !pieces.is_empty() {
return Err(gimli::Error::InvalidPiece.into());
}
add_piece(&mut pieces, location, 0, is_value, Size::none());
} else {
match gimli::Operation::parse(&mut bytes, encoding)? {
gimli::Operation::Piece {
size_in_bits,
bit_offset,
} => {
add_piece(
&mut pieces,
location,
bit_offset.unwrap_or(0),
is_value,
Size::new(size_in_bits),
);
}
_ => {
return Err(gimli::Error::InvalidPiece.into());
}
}
}
}
location = None;
}
if pieces.is_empty() {
if let Some(location) = stack.pop() {
add_piece(&mut pieces, location, 0, false, Size::none());
}
}
Ok(pieces)
}
fn evaluate<'input, Endian>(
unit: &gimli::UnitHeader<Reader<'input, Endian>>,
expression: gimli::Expression<Reader<'input, Endian>>,
object_address: bool,
) -> Vec<gimli::Piece<Reader<'input, Endian>>>
where
Endian: gimli::Endianity + 'input,
{
let mut evaluation = expression.evaluation(unit.encoding());
if object_address {
evaluation.set_object_address(0);
evaluation.set_initial_value(0);
}
let mut result = evaluation.evaluate();
loop {
match result {
Ok(gimli::EvaluationResult::Complete) => {
return evaluation.result();
}
Ok(gimli::EvaluationResult::RequiresRelocatedAddress(address)) => {
result = evaluation.resume_with_relocated_address(address);
}
Ok(_x) => {
debug!("incomplete evaluation: {:?}", _x);
return Vec::new();
}
Err(e) => {
debug!("evaluation failed: {}", e);
return Vec::new();
}
}
}
}
impl From<gimli::Range> for Range {
#[inline]
fn from(range: gimli::Range) -> Range {
Range {
begin: range.begin,
end: range.end,
}
}
}
impl From<gimli::Register> for Register {
#[inline]
fn from(register: gimli::Register) -> Register {
Register(register.0)
}
}
impl From<gimli::UnitSectionOffset> for FunctionOffset {
#[inline]
fn from(o: gimli::UnitSectionOffset) -> FunctionOffset {
let o = match o {
gimli::UnitSectionOffset::DebugInfoOffset(o) => o,
_ => panic!("unexpected offset {:?}", o),
};
FunctionOffset::new(o.0)
}
}
impl From<gimli::UnitSectionOffset> for ParameterOffset {
#[inline]
fn from(o: gimli::UnitSectionOffset) -> ParameterOffset {
let o = match o {
gimli::UnitSectionOffset::DebugInfoOffset(o) => o,
_ => panic!("unexpected offset {:?}", o),
};
ParameterOffset::new(o.0)
}
}
impl From<gimli::UnitSectionOffset> for MemberOffset {
#[inline]
fn from(o: gimli::UnitSectionOffset) -> MemberOffset {
let o = match o {
gimli::UnitSectionOffset::DebugInfoOffset(o) => o,
_ => panic!("unexpected offset {:?}", o),
};
MemberOffset::new(o.0)
}
}
impl From<gimli::UnitSectionOffset> for TypeOffset {
#[inline]
fn from(o: gimli::UnitSectionOffset) -> TypeOffset {
let o = match o {
gimli::UnitSectionOffset::DebugInfoOffset(o) => o,
_ => panic!("unexpected offset {:?}", o),
};
TypeOffset::new(o.0)
}
}
impl From<gimli::UnitSectionOffset> for VariableOffset {
#[inline]
fn from(o: gimli::UnitSectionOffset) -> VariableOffset {
let o = match o {
gimli::UnitSectionOffset::DebugInfoOffset(o) => o,
_ => panic!("unexpected offset {:?}", o),
};
VariableOffset::new(o.0)
}
}
fn parse_debug_info_offset<'input, Endian>(
dwarf_unit: &DwarfUnit<'input, Endian>,
attr: &gimli::Attribute<Reader<'input, Endian>>,
) -> Option<gimli::UnitSectionOffset>
where
Endian: gimli::Endianity,
{
match attr.value() {
gimli::AttributeValue::UnitRef(offset) => Some(offset.to_unit_section_offset(dwarf_unit)),
gimli::AttributeValue::DebugInfoRef(offset) => {
Some(gimli::UnitSectionOffset::DebugInfoOffset(offset))
}
other => {
debug!("unknown offset: {:?}", other);
None
}
}
}
fn parse_function_offset<'input, Endian>(
dwarf_unit: &DwarfUnit<'input, Endian>,
attr: &gimli::Attribute<Reader<'input, Endian>>,
) -> Option<FunctionOffset>
where
Endian: gimli::Endianity,
{
parse_debug_info_offset(dwarf_unit, attr).map(|x| x.into())
}
fn parse_parameter_offset<'input, Endian>(
dwarf_unit: &DwarfUnit<'input, Endian>,
attr: &gimli::Attribute<Reader<'input, Endian>>,
) -> Option<ParameterOffset>
where
Endian: gimli::Endianity,
{
parse_debug_info_offset(dwarf_unit, attr).map(|x| x.into())
}
fn parse_member_offset<'input, Endian>(
dwarf_unit: &DwarfUnit<'input, Endian>,
attr: &gimli::Attribute<Reader<'input, Endian>>,
) -> Option<MemberOffset>
where
Endian: gimli::Endianity,
{
parse_debug_info_offset(dwarf_unit, attr).map(|x| x.into())
}
fn parse_type_offset<'input, Endian>(
dwarf_unit: &DwarfUnit<'input, Endian>,
attr: &gimli::Attribute<Reader<'input, Endian>>,
) -> Option<TypeOffset>
where
Endian: gimli::Endianity,
{
parse_debug_info_offset(dwarf_unit, attr).map(|x| x.into())
}
fn parse_variable_offset<'input, Endian>(
dwarf_unit: &DwarfUnit<'input, Endian>,
attr: &gimli::Attribute<Reader<'input, Endian>>,
) -> Option<VariableOffset>
where
Endian: gimli::Endianity,
{
parse_debug_info_offset(dwarf_unit, attr).map(|x| x.into())
}
fn parse_source_file<'input, Endian>(
dwarf: &DwarfDebugInfo<'input, Endian>,
dwarf_unit: &DwarfUnit<'input, Endian>,
attr: &gimli::Attribute<Reader<'input, Endian>>,
source: &mut Source<'input>,
) where
Endian: gimli::Endianity,
{
match attr.value() {
gimli::AttributeValue::FileIndex(val) => {
if val != 0 {
if let Some(ref line) = dwarf_unit.line_program {
if let Some(entry) = line.header().file(val) {
source.file = dwarf.string(dwarf_unit, entry.path_name());
if let Some(directory) = entry.directory(line.header()) {
source.directory = dwarf.string(dwarf_unit, directory);
} else {
debug!("invalid directory index {}", entry.directory_index());
}
} else {
debug!("invalid file index {}", val);
}
}
}
}
val => {
debug!("unknown DW_AT_decl_file attribute value: {:?}", val);
}
}
}
fn parse_source_line<Endian>(attr: &gimli::Attribute<Reader<Endian>>, source: &mut Source)
where
Endian: gimli::Endianity,
{
match attr.value() {
gimli::AttributeValue::Udata(val) => {
if val != 0 {
if val <= u64::from(u32::MAX) {
source.line = val as u32;
} else {
debug!("large source line: {}", val);
}
}
}
val => {
debug!("unknown DW_AT_decl_line attribute value: {:?}", val);
}
}
}
fn parse_source_column<Endian>(attr: &gimli::Attribute<Reader<Endian>>, source: &mut Source)
where
Endian: gimli::Endianity,
{
match attr.value() {
gimli::AttributeValue::Udata(val) => {
if val != 0 {
if val <= u64::from(u32::MAX) {
source.column = val as u32;
} else {
debug!("large source column: {}", val);
}
}
}
val => {
debug!("unknown DW_AT_decl_column attribute value: {:?}", val);
}
}
}
struct DwarfFrame<R: gimli::Reader<Offset = usize>> {
debug_frame: DebugFrameTable<R>,
eh_frame: EhFrameTable<R>,
}
impl<R: gimli::Reader<Offset = usize>> DwarfFrame<R> {
fn new(
debug_frame: gimli::DebugFrame<R>,
eh_frame: gimli::EhFrame<R>,
bases: gimli::BaseAddresses,
) -> Self {
DwarfFrame {
debug_frame: DebugFrameTable::new(debug_frame),
eh_frame: EhFrameTable::new(eh_frame, bases),
}
}
fn get_cfi(&self, address: Address, size: Size) -> Option<Vec<Cfi>> {
let cfi = self
.eh_frame
.get_cfi(address, size)
.or_else(|| self.debug_frame.get_cfi(address, size));
if cfi.is_none() {
debug!(
"no FDE for 0x{:x}[0x{:x}]",
address.get().unwrap_or(0),
size.get().unwrap_or(0)
);
}
cfi
}
}
struct DebugFrameTable<R: gimli::Reader<Offset = usize>> {
debug_frame: gimli::DebugFrame<R>,
bases: gimli::BaseAddresses,
fdes: FdeOffsetTable,
}
impl<R: gimli::Reader<Offset = usize>> DebugFrameTable<R> {
fn new(debug_frame: gimli::DebugFrame<R>) -> Self {
let bases = gimli::BaseAddresses::default();
let fdes = FdeOffsetTable::new(&debug_frame, &bases);
DebugFrameTable {
debug_frame,
bases,
fdes,
}
}
fn get_cfi(&self, address: Address, size: Size) -> Option<Vec<Cfi>> {
get_cfi(&self.debug_frame, &self.bases, &self.fdes, address, size)
}
}
struct EhFrameTable<R: gimli::Reader<Offset = usize>> {
eh_frame: gimli::EhFrame<R>,
bases: gimli::BaseAddresses,
fdes: FdeOffsetTable,
}
impl<R: gimli::Reader<Offset = usize>> EhFrameTable<R> {
fn new(eh_frame: gimli::EhFrame<R>, bases: gimli::BaseAddresses) -> Self {
let fdes = FdeOffsetTable::new(&eh_frame, &bases);
EhFrameTable {
eh_frame,
bases,
fdes,
}
}
fn get_cfi(&self, address: Address, size: Size) -> Option<Vec<Cfi>> {
get_cfi(&self.eh_frame, &self.bases, &self.fdes, address, size)
}
}
struct FdeOffsetTable {
offsets: Vec<(Range, usize)>,
}
impl FdeOffsetTable {
fn new<R: gimli::Reader<Offset = usize>, S: gimli::UnwindSection<R>>(
section: &S,
bases: &gimli::BaseAddresses,
) -> Self
where
S::Offset: gimli::UnwindOffset,
{
let mut offsets = Vec::new();
let mut entries = section.entries(bases);
while let Ok(Some(entry)) = entries.next() {
match entry {
gimli::CieOrFde::Cie(_) => {}
gimli::CieOrFde::Fde(partial) => {
if let Ok(fde) = partial.parse(S::cie_from_offset) {
let range = Range {
begin: fde.initial_address(),
end: fde.initial_address() + fde.len(),
};
offsets.push((range, fde.offset()));
}
}
}
}
offsets.sort_by_key(|x| x.0);
FdeOffsetTable { offsets }
}
fn find(&self, address: u64) -> Option<usize> {
let index = match self.offsets.binary_search_by_key(&address, |x| x.0.begin) {
Ok(x) => Some(x),
Err(x) => {
if x > 0 {
Some(x - 1)
} else {
None
}
}
};
if let Some(index) = index {
let (range, offset) = self.offsets[index];
if range.begin <= address && range.end > address {
return Some(offset);
}
}
None
}
}
fn get_cfi<R: gimli::Reader, S: gimli::UnwindSection<R>>(
section: &S,
bases: &gimli::BaseAddresses,
fdes: &FdeOffsetTable,
address: Address,
size: Size,
) -> Option<Vec<Cfi>>
where
S::Offset: gimli::UnwindOffset,
{
let address = address.get()?;
let size = size.get()?;
let fde_offset = S::Offset::from(fdes.find(address)?);
let fde = section
.fde_from_offset(bases, fde_offset, S::cie_from_offset)
.ok()?;
if (address, size) != (fde.initial_address(), fde.len()) {
debug!(
"FDE address mismatch: want function 0x{:x}[0x{:x}], found FDE 0x{:x}[0x{:x}]",
address,
size,
fde.initial_address(),
fde.len(),
);
}
let mut cfi = Vec::new();
cfi.push((Address::none(), CfiDirective::StartProc));
if let Some(personality) = fde.personality() {
let address = match personality {
gimli::Pointer::Direct(x) => Address::new(x),
gimli::Pointer::Indirect(x) => Address::new(x),
};
cfi.push((Address::none(), CfiDirective::Personality(address)));
}
if let Some(lsda) = fde.lsda() {
let address = match lsda {
gimli::Pointer::Direct(x) => Address::new(x),
gimli::Pointer::Indirect(x) => Address::new(x),
};
cfi.push((Address::none(), CfiDirective::Lsda(address)));
}
if fde.is_signal_trampoline() {
cfi.push((Address::none(), CfiDirective::SignalFrame));
}
let cie = fde.cie();
let mut address = 0;
let mut instructions = cie.instructions(section, bases);
while let Ok(Some(instruction)) = instructions.next() {
if let Some(directive) = convert_cfi(cie, instruction, &mut address) {
cfi.push((Address::none(), directive))
}
}
let mut address = fde.initial_address();
let mut instructions = fde.instructions(section, bases);
while let Ok(Some(instruction)) = instructions.next() {
if let Some(directive) = convert_cfi(cie, instruction, &mut address) {
cfi.push((Address::new(address), directive))
}
}
cfi.push((
Address::new(fde.initial_address() + fde.len()),
CfiDirective::EndProc,
));
Some(cfi)
}
fn convert_cfi<R: gimli::Reader>(
cie: &gimli::CommonInformationEntry<R>,
instruction: gimli::CallFrameInstruction<R>,
loc: &mut u64,
) -> Option<CfiDirective> {
match instruction {
gimli::CallFrameInstruction::SetLoc { address } => {
*loc = address;
None
}
gimli::CallFrameInstruction::AdvanceLoc { delta } => {
*loc += delta as u64 * cie.code_alignment_factor();
None
}
gimli::CallFrameInstruction::DefCfa { register, offset } => {
Some(CfiDirective::DefCfa(register.into(), offset as i64))
}
gimli::CallFrameInstruction::DefCfaSf {
register,
factored_offset,
} => {
let offset = factored_offset * cie.data_alignment_factor();
Some(CfiDirective::DefCfa(register.into(), offset as i64))
}
gimli::CallFrameInstruction::DefCfaRegister { register } => {
Some(CfiDirective::DefCfaRegister(register.into()))
}
gimli::CallFrameInstruction::DefCfaOffset { offset } => {
Some(CfiDirective::DefCfaOffset(offset as i64))
}
gimli::CallFrameInstruction::DefCfaOffsetSf { factored_offset } => {
let offset = factored_offset * cie.data_alignment_factor();
Some(CfiDirective::DefCfaOffset(offset as i64))
}
gimli::CallFrameInstruction::Offset {
register,
factored_offset,
} => {
let offset = factored_offset as i64 * cie.data_alignment_factor();
Some(CfiDirective::Offset(register.into(), offset))
}
gimli::CallFrameInstruction::OffsetExtendedSf {
register,
factored_offset,
} => {
let offset = factored_offset * cie.data_alignment_factor();
Some(CfiDirective::Offset(register.into(), offset))
}
gimli::CallFrameInstruction::ValOffset {
register,
factored_offset,
} => {
let offset = factored_offset as i64 * cie.data_alignment_factor();
Some(CfiDirective::ValOffset(register.into(), offset))
}
gimli::CallFrameInstruction::ValOffsetSf {
register,
factored_offset,
} => {
let offset = factored_offset * cie.data_alignment_factor();
Some(CfiDirective::ValOffset(register.into(), offset))
}
gimli::CallFrameInstruction::Register {
dest_register,
src_register,
} => Some(CfiDirective::Register(
dest_register.into(),
src_register.into(),
)),
gimli::CallFrameInstruction::Undefined { register } => {
Some(CfiDirective::Undefined(register.into()))
}
gimli::CallFrameInstruction::SameValue { register } => {
Some(CfiDirective::SameValue(register.into()))
}
gimli::CallFrameInstruction::Restore { register } => {
Some(CfiDirective::Restore(register.into()))
}
gimli::CallFrameInstruction::RememberState => Some(CfiDirective::RememberState),
gimli::CallFrameInstruction::RestoreState => Some(CfiDirective::RestoreState),
gimli::CallFrameInstruction::ArgsSize { .. }
| gimli::CallFrameInstruction::DefCfaExpression { .. }
| gimli::CallFrameInstruction::Expression { .. }
| gimli::CallFrameInstruction::ValExpression { .. } => {
debug!("Unhandled CFI: {:?}", instruction);
Some(CfiDirective::Other)
}
gimli::CallFrameInstruction::Nop => None,
}
}