mod context;
mod line;
mod reader;
mod unit;
pub use reader::{DwarfError, DwarfResult};
use context::Context;
use line::{Builder, Range};
use reader::Reader;
use crate::diag::SourceLocation;
use crate::elf::read::{ElfFormat, ObjectFile};
use crate::error::Result;
#[derive(Debug, Default)]
pub struct LineLookup {
ranges: Vec<Range>,
files: Vec<String>,
errors: Vec<crate::Error>,
}
impl LineLookup {
pub fn parse<F: ElfFormat>(object: &ObjectFile<'_, F>) -> Result<Self> {
let ctx = Context::load(object)?;
let mut errors = Vec::new();
let mut units = Vec::new();
for &info in &ctx.info {
let (found, error) = unit::parse_units(&ctx, info);
units.extend(found);
if let Some(error) = error {
errors.push(ctx.error(info, error));
}
}
let mut builder = Builder::default();
for §ion in &ctx.line {
let Some(loaded) = ctx.section(section) else {
continue;
};
let mut r = Reader::new(&loaded.data, ctx.big_endian);
while !r.is_empty() {
let start = r.pos() as u64;
let unit = units.iter().find(|u| {
u.stmt_list.is_some_and(|(target, offset)| {
offset == start && target.unwrap_or(section) == section
})
});
let comp_dir = unit.and_then(|u| u.comp_dir.as_deref());
let name = unit.and_then(|u| u.name.as_deref());
let before = r.pos();
if let Err(error) =
line::parse_program(&ctx, section, &mut r, comp_dir, name, &mut builder)
{
errors.push(ctx.error(section, error));
if r.pos() == before {
break;
}
}
}
}
let mut ranges = builder.ranges;
ranges.sort_by_key(|r| (r.section, r.start, r.end));
Ok(Self {
ranges,
files: builder.files,
errors,
})
}
#[must_use]
pub fn find(&self, section: u32, offset: u64) -> Option<SourceLocation> {
let end = self
.ranges
.partition_point(|r| (r.section, r.start) <= (section, offset));
let range = self.ranges[..end]
.iter()
.rev()
.take(32)
.take_while(|r| r.section == section)
.find(|r| offset < r.end)?;
if range.line == 0 {
return None;
}
let file = self.files.get(usize::try_from(range.file).ok()?)?;
Some(SourceLocation {
file: file.clone(),
line: range.line,
})
}
#[must_use]
pub fn problems(&self) -> &[crate::Error] {
&self.errors
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.ranges.is_empty()
}
}
pub fn source_location<F: ElfFormat>(
object: &ObjectFile<'_, F>,
section: u32,
offset: u64,
) -> Result<Option<SourceLocation>> {
Ok(LineLookup::parse(object)?.find(section, offset))
}