use std::borrow::Cow;
use std::fmt;
use std::marker::PhantomData;
use std::ops::Deref;
use failure::Fail;
use fallible_iterator::FallibleIterator;
use gimli::read::{AttributeValue, Range};
use gimli::{constants, UnitSectionOffset};
use lazycell::LazyCell;
use symbolic_common::{derive_failure, AsSelf, Language, Name, SelfCell};
use crate::base::*;
use crate::private::FunctionStack;
#[doc(hidden)]
pub use gimli;
pub use gimli::RunTimeEndian as Endian;
type Slice<'a> = gimli::read::EndianSlice<'a, Endian>;
type RangeLists<'a> = gimli::read::RangeLists<Slice<'a>>;
type Unit<'a> = gimli::read::Unit<Slice<'a>>;
type DwarfInner<'a> = gimli::read::Dwarf<Slice<'a>>;
type Die<'d, 'u> = gimli::read::DebuggingInformationEntry<'u, 'u, Slice<'d>, usize>;
type Attribute<'a> = gimli::read::Attribute<Slice<'a>>;
type UnitOffset = gimli::read::UnitOffset<usize>;
type DebugInfoOffset = gimli::DebugInfoOffset<usize>;
type CompilationUnitHeader<'a> = gimli::read::CompilationUnitHeader<Slice<'a>>;
type IncompleteLineNumberProgram<'a> = gimli::read::IncompleteLineProgram<Slice<'a>>;
type LineNumberProgramHeader<'a> = gimli::read::LineProgramHeader<Slice<'a>>;
type LineProgramFileEntry<'a> = gimli::read::FileEntry<Slice<'a>>;
#[derive(Clone, Copy, Debug, Eq, Fail, PartialEq)]
pub enum DwarfErrorKind {
#[fail(display = "compilation unit for offset {} does not exist", _0)]
InvalidUnitRef(usize),
#[fail(display = "referenced file {} does not exist", _0)]
InvalidFileRef(u64),
#[fail(display = "unexpected inline function without parent")]
UnexpectedInline,
#[fail(display = "function with inverted address range")]
InvertedFunctionRange,
#[fail(display = "corrupted dwarf debug data")]
CorruptedData,
}
derive_failure!(
DwarfError,
DwarfErrorKind,
doc = "An error handling [`DWARF`](trait.Dwarf.html) debugging information.",
);
impl From<gimli::read::Error> for DwarfError {
fn from(error: gimli::read::Error) -> Self {
error.context(DwarfErrorKind::CorruptedData).into()
}
}
#[derive(Clone)]
pub struct DwarfSection<'data> {
pub address: u64,
pub offset: u64,
pub align: u64,
pub data: Cow<'data, [u8]>,
}
impl fmt::Debug for DwarfSection<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("DwarfSection")
.field("address", &format_args!("{:#x}", self.address))
.field("offset", &format_args!("{:#x}", self.offset))
.field("align", &format_args!("{:#x}", self.align))
.field("len()", &self.data.len())
.finish()
}
}
pub trait Dwarf<'data> {
fn endianity(&self) -> Endian;
#[doc(hidden)]
#[deprecated(note = "use raw_section instead")]
fn raw_data(&self, name: &str) -> Option<(u64, &'data [u8])> {
let section = self.raw_section(name)?;
match section.data {
Cow::Borrowed(data) => Some((section.offset, data)),
Cow::Owned(_) => None,
}
}
#[doc(hidden)]
#[deprecated(note = "use section instead")]
fn section_data(&self, name: &str) -> Option<(u64, Cow<'data, [u8]>)> {
let section = self.section(name)?;
Some((section.offset, section.data))
}
fn raw_section(&self, name: &str) -> Option<DwarfSection<'data>>;
fn section(&self, name: &str) -> Option<DwarfSection<'data>> {
self.raw_section(name)
}
fn has_section(&self, name: &str) -> bool {
self.raw_section(name).is_some()
}
}
#[derive(Debug)]
struct DwarfRow {
address: u64,
file_index: u64,
line: Option<u64>,
size: Option<u64>,
}
#[derive(Debug)]
struct DwarfSequence {
start: u64,
end: u64,
rows: Vec<DwarfRow>,
}
#[derive(Debug)]
struct DwarfLineProgram<'d> {
header: LineNumberProgramHeader<'d>,
sequences: Vec<DwarfSequence>,
}
impl<'d, 'a> DwarfLineProgram<'d> {
fn prepare(program: IncompleteLineNumberProgram<'d>) -> Result<Self, DwarfError> {
let mut sequences = Vec::new();
let mut sequence_rows = Vec::<DwarfRow>::new();
let mut prev_address = 0;
let mut state_machine = program.rows();
while let Ok(Some((_, &program_row))) = state_machine.next_row() {
let address = program_row.address();
if let Some(last_row) = sequence_rows.last_mut() {
if address >= last_row.address {
last_row.size = Some(address - last_row.address);
}
}
if program_row.end_sequence() {
if !sequence_rows.is_empty() {
sequences.push(DwarfSequence {
start: sequence_rows[0].address,
end: if address < prev_address {
prev_address + 1
} else {
address
},
rows: sequence_rows.drain(..).collect(),
});
}
prev_address = 0;
} else if address < prev_address {
} else {
let file_index = program_row.file_index();
let line = program_row.line();
let mut duplicate = false;
if let Some(last_row) = sequence_rows.last_mut() {
if last_row.address == address {
last_row.file_index = file_index;
last_row.line = line;
duplicate = true;
}
}
if !duplicate {
sequence_rows.push(DwarfRow {
address,
file_index,
line,
size: None,
});
}
prev_address = address;
}
}
if !sequence_rows.is_empty() {
let start = sequence_rows[0].address;
let end = prev_address + 1;
sequences.push(DwarfSequence {
start,
end,
rows: sequence_rows,
});
}
dmsort::sort_by_key(&mut sequences, |x| x.start);
Ok(DwarfLineProgram {
header: state_machine.header().clone(),
sequences,
})
}
pub fn get_rows(&self, range: &Range) -> &[DwarfRow] {
for seq in &self.sequences {
if seq.end <= range.begin || seq.start > range.end {
continue;
}
let from = match seq.rows.binary_search_by_key(&range.begin, |x| x.address) {
Ok(idx) => idx,
Err(0) => continue,
Err(next_idx) => next_idx - 1,
};
let len = seq.rows[from..]
.binary_search_by_key(&range.end, |x| x.address)
.unwrap_or_else(|e| e);
return &seq.rows[from..from + len];
}
&[]
}
}
#[derive(Clone, Copy, Debug)]
struct UnitRef<'d, 'a> {
info: &'a DwarfInfo<'d>,
unit: &'a Unit<'d>,
}
impl<'d, 'a> UnitRef<'d, 'a> {
#[inline(always)]
fn slice_value(&self, value: AttributeValue<Slice<'d>>) -> Option<&'d [u8]> {
self.info
.attr_string(self.unit, value)
.map(|reader| reader.slice())
.ok()
}
#[inline(always)]
fn string_value(&self, value: AttributeValue<Slice<'d>>) -> Option<Cow<'d, str>> {
let slice = self.slice_value(value)?;
Some(String::from_utf8_lossy(slice))
}
fn resolve_reference<T, F>(&self, attr: Attribute<'d>, f: F) -> Result<Option<T>, DwarfError>
where
F: FnOnce(UnitRef<'d, '_>, &Die<'d, '_>) -> Result<Option<T>, DwarfError>,
{
let (unit, offset) = match attr.value() {
AttributeValue::UnitRef(offset) => (*self, offset),
AttributeValue::DebugInfoRef(offset) => self.info.find_unit_offset(offset)?,
_ => return Ok(None),
};
let mut entries = unit.unit.entries_at_offset(offset)?;
entries.next_entry()?;
if let Some(entry) = entries.current() {
f(unit, entry)
} else {
Ok(None)
}
}
fn resolve_function_name(
&self,
entry: &Die<'d, '_>,
) -> Result<Option<Cow<'d, str>>, DwarfError> {
let mut attrs = entry.attrs();
let mut fallback_name = None;
let mut reference_target = None;
while let Some(attr) = attrs.next()? {
match attr.name() {
constants::DW_AT_linkage_name | constants::DW_AT_MIPS_linkage_name => {
return Ok(self.string_value(attr.value()));
}
constants::DW_AT_name => {
fallback_name = Some(attr);
}
constants::DW_AT_abstract_origin | constants::DW_AT_specification => {
reference_target = Some(attr);
}
_ => {}
}
}
if let Some(attr) = fallback_name {
return Ok(self.string_value(attr.value()));
}
if let Some(attr) = reference_target {
let resolved = self.resolve_reference(attr, |ref_unit, ref_entry| {
if self.unit.offset != ref_unit.unit.offset || entry.offset() != ref_entry.offset()
{
ref_unit.resolve_function_name(ref_entry)
} else {
Ok(None)
}
})?;
if let Some(name) = resolved {
return Ok(Some(name));
}
}
Ok(None)
}
}
#[derive(Debug)]
struct DwarfUnit<'d, 'a> {
inner: UnitRef<'d, 'a>,
language: Language,
line_program: Option<DwarfLineProgram<'d>>,
}
impl<'d, 'a> DwarfUnit<'d, 'a> {
fn from_unit(unit: &'a Unit<'d>, info: &'a DwarfInfo<'d>) -> Result<Option<Self>, DwarfError> {
let mut entries = unit.entries();
let entry = match entries.next_dfs()? {
Some((_, entry)) => entry,
None => return Err(gimli::read::Error::MissingUnitDie.into()),
};
if info.kind != ObjectKind::Relocatable
&& unit.low_pc == 0
&& entry.attr(constants::DW_AT_ranges)?.is_none()
{
return Ok(None);
}
let language = match entry.attr_value(constants::DW_AT_language)? {
Some(AttributeValue::Language(lang)) => language_from_dwarf(lang),
_ => Language::Unknown,
};
let line_program = match unit.line_program {
Some(ref program) => Some(DwarfLineProgram::prepare(program.clone())?),
None => None,
};
Ok(Some(DwarfUnit {
inner: UnitRef { info, unit },
language,
line_program,
}))
}
fn compilation_dir(&self) -> &'d [u8] {
match self.inner.unit.comp_dir {
Some(ref dir) => dir.slice(),
None => &[],
}
}
fn parse_ranges(
&self,
entry: &Die<'d, '_>,
range_buf: &mut Vec<Range>,
) -> Result<(Option<u64>, Option<u64>), DwarfError> {
let mut tuple = (None, None);
let mut low_pc = None;
let mut high_pc = None;
let mut high_pc_rel = None;
let mut attrs = entry.attrs();
while let Some(attr) = attrs.next()? {
match attr.name() {
constants::DW_AT_low_pc => match attr.value() {
AttributeValue::Addr(addr) => low_pc = Some(addr),
_ => unreachable!(),
},
constants::DW_AT_high_pc => match attr.value() {
AttributeValue::Addr(addr) => high_pc = Some(addr),
AttributeValue::Udata(size) => high_pc_rel = Some(size),
_ => unreachable!(),
},
constants::DW_AT_call_line => match attr.value() {
AttributeValue::Udata(line) => tuple.0 = Some(line),
_ => unreachable!(),
},
constants::DW_AT_call_file => match attr.value() {
AttributeValue::FileIndex(file) => tuple.1 = Some(file),
_ => unreachable!(),
},
constants::DW_AT_ranges
| constants::DW_AT_rnglists_base
| constants::DW_AT_start_scope => {
match self.inner.info.attr_ranges(self.inner.unit, attr.value())? {
Some(mut ranges) => {
while let Some(range) = ranges.next()? {
range_buf.push(range);
}
}
None => continue,
}
}
_ => continue,
}
}
if !range_buf.is_empty() {
return Ok(tuple);
}
let kind = self.inner.info.kind;
let low_pc = match low_pc {
Some(low_pc) if low_pc != 0 || kind == ObjectKind::Relocatable => low_pc,
_ => return Ok(tuple),
};
let high_pc = match (high_pc, high_pc_rel) {
(Some(high_pc), _) => high_pc,
(_, Some(high_pc_rel)) => low_pc.wrapping_add(high_pc_rel),
_ => return Ok(tuple),
};
if low_pc == high_pc {
return Ok(tuple);
}
if low_pc > high_pc {
return Err(DwarfErrorKind::InvertedFunctionRange.into());
}
range_buf.push(Range {
begin: low_pc,
end: high_pc,
});
Ok(tuple)
}
fn resolve_lines(&self, ranges: &[Range]) -> Vec<LineInfo<'d>> {
let line_program = match self.line_program {
Some(ref program) => program,
None => return Vec::new(),
};
let mut lines = Vec::new();
for range in ranges {
let rows = line_program.get_rows(range);
lines.reserve(rows.len());
if let Some((first, rows)) = rows.split_first() {
let mut last_file = first.file_index;
let mut last_info = LineInfo {
address: range.begin - self.inner.info.load_address,
size: first.size.map(|s| s + first.address - range.begin),
file: self.resolve_file(first.file_index).unwrap_or_default(),
line: first.line.unwrap_or(0),
};
for row in rows {
let line = row.line.unwrap_or(0);
if (last_file, last_info.line) == (row.file_index, line) {
if let Some(size) = last_info.size.as_mut() {
*size += row.size.unwrap_or(0);
}
continue;
}
lines.push(last_info);
last_file = row.file_index;
last_info = LineInfo {
address: row.address - self.inner.info.load_address,
size: row.size,
file: self.resolve_file(row.file_index).unwrap_or_default(),
line,
};
}
if let Some(size) = last_info.size.as_mut() {
*size = range.end - self.inner.info.load_address - last_info.address;
}
lines.push(last_info);
}
}
lines
}
fn file_info(
&self,
line_program: &LineNumberProgramHeader<'d>,
file: &LineProgramFileEntry<'d>,
) -> FileInfo<'d> {
FileInfo {
dir: file
.directory(line_program)
.and_then(|attr| self.inner.slice_value(attr))
.unwrap_or_default(),
name: self.inner.slice_value(file.path_name()).unwrap_or_default(),
}
}
fn resolve_file(&self, file_id: u64) -> Option<FileInfo<'d>> {
let line_program = match self.line_program {
Some(ref program) => &program.header,
None => return None,
};
line_program
.file(file_id)
.map(|file| self.file_info(line_program, file))
}
fn functions(&self, range_buf: &mut Vec<Range>) -> Result<Vec<Function<'d>>, DwarfError> {
let mut depth = 0;
let mut skipped_depth = None;
let mut functions = Vec::new();
let mut stack = FunctionStack::new();
let mut entries = self.inner.unit.entries();
while let Some((movement, entry)) = entries.next_dfs()? {
depth += movement;
match skipped_depth {
Some(skipped) if depth > skipped => continue,
_ => skipped_depth = None,
}
stack.flush(depth, &mut functions);
let inline = match entry.tag() {
constants::DW_TAG_subprogram => false,
constants::DW_TAG_inlined_subroutine => true,
_ => continue,
};
range_buf.clear();
let (call_line, call_file) = self.parse_ranges(entry, range_buf)?;
if range_buf.is_empty() {
skipped_depth = Some(depth);
continue;
}
if !inline && range_buf.len() != 1 {
skipped_depth = Some(depth);
continue;
}
let function_address = range_buf[0].begin - self.inner.info.load_address;
let function_size = range_buf[range_buf.len() - 1].end - range_buf[0].begin;
let function_end = function_address + function_size;
let symbol_name = if !inline {
self.inner
.info
.symbol_map
.lookup_range(function_address..function_end)
.and_then(|symbol| symbol.name.clone())
} else {
None
};
let name = match symbol_name {
Some(name) => Some(name),
None => self.inner.resolve_function_name(entry).ok().flatten(),
};
let lines = self.resolve_lines(&range_buf);
if inline {
let parent = match stack.peek_mut() {
Some(parent) => parent,
None => return Err(DwarfErrorKind::UnexpectedInline.into()),
};
if let (Some(line), Some(file_id)) = (call_line, call_file) {
let file = self.resolve_file(file_id).unwrap_or_default();
let lines = &mut parent.lines;
let mut index = 0;
for range in range_buf.iter() {
let range_begin = range.begin - self.inner.info.load_address;
let range_end = range.end - self.inner.info.load_address;
if let Some(next) = lines.get(index) {
if next.address > range_begin {
let line_info = LineInfo {
address: range_begin,
size: Some(range_end.min(next.address) - range_begin),
file: file.clone(),
line,
};
lines.insert(index, line_info);
index += 1;
}
}
while index < lines.len() {
let record = &mut lines[index];
if record.address >= range_end {
break;
}
index += 1;
let record_end = record.address + record.size.unwrap_or(0);
if record_end <= range_begin {
continue;
}
let split = if record_end > range_end {
record.size = Some(range_end - record.address);
Some(LineInfo {
address: range_end,
size: Some(record_end - range_end),
file: record.file.clone(),
line: record.line,
})
} else {
None
};
if record.address < range_begin {
let max_size = range_begin - record.address;
if record.size.map_or(true, |prev_size| prev_size > max_size) {
record.size = Some(max_size);
}
let size = record_end.min(range_end) - range_begin;
let line_info = LineInfo {
address: range_begin,
size: Some(size),
file: file.clone(),
line,
};
lines.insert(index, line_info);
index += 1;
} else {
record.file = file.clone();
record.line = line;
};
if let Some(split) = split {
lines.insert(index, split);
}
}
if let Some(prev) = index.checked_sub(1).and_then(|i| lines.get(i)) {
let record_end = prev.address + prev.size.unwrap_or(0);
if record_end < range_end {
let line_info = LineInfo {
address: record_end,
size: Some(range_end - record_end),
file: file.clone(),
line,
};
lines.insert(index, line_info);
index += 1;
}
}
}
}
}
let function = Function {
address: function_address,
size: function_size,
name: Name::with_language(name.unwrap_or_default(), self.language),
compilation_dir: self.compilation_dir(),
lines,
inlinees: Vec::new(),
inline,
};
stack.push(depth, function)
}
stack.flush(0, &mut functions);
Ok(functions)
}
}
fn language_from_dwarf(language: gimli::DwLang) -> Language {
match language {
constants::DW_LANG_C => Language::C,
constants::DW_LANG_C11 => Language::C,
constants::DW_LANG_C89 => Language::C,
constants::DW_LANG_C99 => Language::C,
constants::DW_LANG_C_plus_plus => Language::Cpp,
constants::DW_LANG_C_plus_plus_03 => Language::Cpp,
constants::DW_LANG_C_plus_plus_11 => Language::Cpp,
constants::DW_LANG_C_plus_plus_14 => Language::Cpp,
constants::DW_LANG_D => Language::D,
constants::DW_LANG_Go => Language::Go,
constants::DW_LANG_ObjC => Language::ObjC,
constants::DW_LANG_ObjC_plus_plus => Language::ObjCpp,
constants::DW_LANG_Rust => Language::Rust,
constants::DW_LANG_Swift => Language::Swift,
_ => Language::Unknown,
}
}
struct DwarfSectionData<'data, S> {
data: Cow<'data, [u8]>,
endianity: Endian,
_ph: PhantomData<S>,
}
impl<'data, S> DwarfSectionData<'data, S>
where
S: gimli::read::Section<Slice<'data>>,
{
fn load<D>(dwarf: &D) -> Self
where
D: Dwarf<'data>,
{
DwarfSectionData {
data: dwarf
.section(&S::section_name()[1..])
.map(|section| section.data)
.unwrap_or_default(),
endianity: dwarf.endianity(),
_ph: PhantomData,
}
}
fn to_gimli(&'data self) -> S {
S::from(Slice::new(&self.data, self.endianity))
}
}
impl<'d, S> fmt::Debug for DwarfSectionData<'d, S>
where
S: gimli::read::Section<Slice<'d>>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let owned = match self.data {
Cow::Owned(_) => true,
Cow::Borrowed(_) => false,
};
f.debug_struct("DwarfSectionData")
.field("type", &S::section_name())
.field("endianity", &self.endianity)
.field("len()", &self.data.len())
.field("owned()", &owned)
.finish()
}
}
struct DwarfSections<'data> {
debug_abbrev: DwarfSectionData<'data, gimli::read::DebugAbbrev<Slice<'data>>>,
debug_info: DwarfSectionData<'data, gimli::read::DebugInfo<Slice<'data>>>,
debug_line: DwarfSectionData<'data, gimli::read::DebugLine<Slice<'data>>>,
debug_line_str: DwarfSectionData<'data, gimli::read::DebugLineStr<Slice<'data>>>,
debug_str: DwarfSectionData<'data, gimli::read::DebugStr<Slice<'data>>>,
debug_str_offsets: DwarfSectionData<'data, gimli::read::DebugStrOffsets<Slice<'data>>>,
debug_ranges: DwarfSectionData<'data, gimli::read::DebugRanges<Slice<'data>>>,
debug_rnglists: DwarfSectionData<'data, gimli::read::DebugRngLists<Slice<'data>>>,
}
impl<'data> DwarfSections<'data> {
fn from_dwarf<D>(dwarf: &D) -> Result<Self, DwarfError>
where
D: Dwarf<'data>,
{
Ok(DwarfSections {
debug_abbrev: DwarfSectionData::load(dwarf),
debug_info: DwarfSectionData::load(dwarf),
debug_line: DwarfSectionData::load(dwarf),
debug_line_str: DwarfSectionData::load(dwarf),
debug_str: DwarfSectionData::load(dwarf),
debug_str_offsets: DwarfSectionData::load(dwarf),
debug_ranges: DwarfSectionData::load(dwarf),
debug_rnglists: DwarfSectionData::load(dwarf),
})
}
}
struct DwarfInfo<'data> {
inner: DwarfInner<'data>,
headers: Vec<CompilationUnitHeader<'data>>,
units: Vec<LazyCell<Option<Unit<'data>>>>,
symbol_map: SymbolMap<'data>,
load_address: u64,
kind: ObjectKind,
}
impl<'d> Deref for DwarfInfo<'d> {
type Target = DwarfInner<'d>;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<'d> DwarfInfo<'d> {
pub fn parse(
sections: &'d DwarfSections<'d>,
symbol_map: SymbolMap<'d>,
load_address: u64,
kind: ObjectKind,
) -> Result<Self, DwarfError> {
let inner = gimli::read::Dwarf {
debug_abbrev: sections.debug_abbrev.to_gimli(),
debug_addr: Default::default(),
debug_info: sections.debug_info.to_gimli(),
debug_line: sections.debug_line.to_gimli(),
debug_line_str: sections.debug_line_str.to_gimli(),
debug_str: sections.debug_str.to_gimli(),
debug_str_offsets: sections.debug_str_offsets.to_gimli(),
debug_str_sup: Default::default(),
debug_types: Default::default(),
locations: Default::default(),
ranges: RangeLists::new(
sections.debug_ranges.to_gimli(),
sections.debug_rnglists.to_gimli(),
),
};
let headers = inner.units().collect::<Vec<_>>()?;
let units = headers.iter().map(|_| LazyCell::new()).collect();
Ok(DwarfInfo {
inner,
headers,
units,
symbol_map,
load_address,
kind,
})
}
fn get_unit(&self, index: usize) -> Result<Option<&Unit<'d>>, DwarfError> {
let cell = match self.units.get(index) {
Some(cell) => cell,
None => return Ok(None),
};
let unit_opt = cell.try_borrow_with(|| {
let header = self.headers[index];
match self.inner.unit(header) {
Ok(unit) => Ok(Some(unit)),
Err(gimli::read::Error::MissingUnitDie) => Ok(None),
Err(error) => Err(DwarfError::from(error)),
}
})?;
Ok(unit_opt.as_ref())
}
fn find_unit_offset(
&self,
offset: DebugInfoOffset,
) -> Result<(UnitRef<'d, '_>, UnitOffset), DwarfError> {
let search_result = self
.headers
.binary_search_by_key(&offset, CompilationUnitHeader::offset);
let index = match search_result {
Ok(index) => index,
Err(0) => return Err(DwarfErrorKind::InvalidUnitRef(offset.0).into()),
Err(next_index) => next_index - 1,
};
if let Some(unit) = self.get_unit(index)? {
let offset = UnitSectionOffset::DebugInfoOffset(offset);
if let Some(unit_offset) = offset.to_unit_offset(unit) {
return Ok((UnitRef { unit, info: self }, unit_offset));
}
}
Err(DwarfErrorKind::InvalidUnitRef(offset.0).into())
}
fn units(&'d self) -> DwarfUnitIterator<'_> {
DwarfUnitIterator {
info: self,
index: 0,
}
}
}
impl<'slf, 'd: 'slf> AsSelf<'slf> for DwarfInfo<'d> {
type Ref = DwarfInfo<'slf>;
fn as_self(&'slf self) -> &Self::Ref {
unsafe { std::mem::transmute(self) }
}
}
impl fmt::Debug for DwarfInfo<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("DwarfInfo")
.field("headers", &self.headers)
.field("symbol_map", &self.symbol_map)
.field("load_address", &self.load_address)
.finish()
}
}
struct DwarfUnitIterator<'s> {
info: &'s DwarfInfo<'s>,
index: usize,
}
impl<'s> Iterator for DwarfUnitIterator<'s> {
type Item = Result<DwarfUnit<'s, 's>, DwarfError>;
fn next(&mut self) -> Option<Self::Item> {
while self.index < self.info.headers.len() {
let result = self.info.get_unit(self.index);
self.index += 1;
let unit = match result {
Ok(Some(unit)) => unit,
Ok(None) => continue,
Err(error) => return Some(Err(error)),
};
match DwarfUnit::from_unit(unit, self.info) {
Ok(Some(unit)) => return Some(Ok(unit)),
Ok(None) => continue,
Err(error) => return Some(Err(error)),
}
}
None
}
}
impl std::iter::FusedIterator for DwarfUnitIterator<'_> {}
pub struct DwarfDebugSession<'data> {
cell: SelfCell<Box<DwarfSections<'data>>, DwarfInfo<'data>>,
}
impl<'d> DwarfDebugSession<'d> {
pub fn parse<D>(
dwarf: &D,
symbol_map: SymbolMap<'d>,
load_address: u64,
kind: ObjectKind,
) -> Result<Self, DwarfError>
where
D: Dwarf<'d>,
{
let sections = DwarfSections::from_dwarf(dwarf)?;
let cell = SelfCell::try_new(Box::new(sections), |sections| {
DwarfInfo::parse(unsafe { &*sections }, symbol_map, load_address, kind)
})?;
Ok(DwarfDebugSession { cell })
}
pub fn files(&self) -> DwarfFileIterator<'_> {
DwarfFileIterator {
units: self.cell.get().units(),
files: DwarfUnitFileIterator::default(),
finished: false,
}
}
pub fn functions(&self) -> DwarfFunctionIterator<'_> {
DwarfFunctionIterator {
units: self.cell.get().units(),
functions: Vec::new().into_iter(),
range_buf: Vec::new(),
finished: false,
}
}
pub fn source_by_path(&self, _path: &str) -> Result<Option<Cow<'_, str>>, DwarfError> {
Ok(None)
}
}
impl<'d> DebugSession for DwarfDebugSession<'d> {
type Error = DwarfError;
fn functions(&self) -> DynIterator<'_, Result<Function<'_>, Self::Error>> {
Box::new(self.functions())
}
fn files(&self) -> DynIterator<'_, Result<FileEntry<'_>, Self::Error>> {
Box::new(self.files())
}
fn source_by_path(&self, path: &str) -> Result<Option<Cow<'_, str>>, Self::Error> {
self.source_by_path(path)
}
}
#[derive(Debug, Default)]
struct DwarfUnitFileIterator<'s> {
unit: Option<DwarfUnit<'s, 's>>,
index: usize,
}
impl<'s> Iterator for DwarfUnitFileIterator<'s> {
type Item = FileEntry<'s>;
fn next(&mut self) -> Option<Self::Item> {
let unit = self.unit.as_ref()?;
let line_program = unit.line_program.as_ref().map(|p| &p.header)?;
let file = line_program.file_names().get(self.index)?;
self.index += 1;
Some(FileEntry {
compilation_dir: unit.compilation_dir(),
info: unit.file_info(line_program, file),
})
}
}
pub struct DwarfFileIterator<'s> {
units: DwarfUnitIterator<'s>,
files: DwarfUnitFileIterator<'s>,
finished: bool,
}
impl<'s> Iterator for DwarfFileIterator<'s> {
type Item = Result<FileEntry<'s>, DwarfError>;
fn next(&mut self) -> Option<Self::Item> {
if self.finished {
return None;
}
loop {
if let Some(file_entry) = self.files.next() {
return Some(Ok(file_entry));
}
let unit = match self.units.next() {
Some(Ok(unit)) => unit,
Some(Err(error)) => return Some(Err(error)),
None => break,
};
self.files = DwarfUnitFileIterator {
unit: Some(unit),
index: 0,
};
}
self.finished = true;
None
}
}
pub struct DwarfFunctionIterator<'s> {
units: DwarfUnitIterator<'s>,
functions: std::vec::IntoIter<Function<'s>>,
range_buf: Vec<Range>,
finished: bool,
}
impl<'s> Iterator for DwarfFunctionIterator<'s> {
type Item = Result<Function<'s>, DwarfError>;
fn next(&mut self) -> Option<Self::Item> {
if self.finished {
return None;
}
loop {
if let Some(func) = self.functions.next() {
return Some(Ok(func));
}
let unit = match self.units.next() {
Some(Ok(unit)) => unit,
Some(Err(error)) => return Some(Err(error)),
None => break,
};
self.functions = match unit.functions(&mut self.range_buf) {
Ok(functions) => functions.into_iter(),
Err(error) => return Some(Err(error)),
};
}
self.finished = true;
None
}
}
impl std::iter::FusedIterator for DwarfFunctionIterator<'_> {}