use alloc::{borrow::Cow, vec::Vec}; use anyhow::{Context, Result}; use object::{Object as _, ObjectSection as _}; use typed_arena::Arena; use crate::obj::{Section, SectionKind}; /// Parse line information from DWARF 2+ sections. pub(crate) fn parse_line_info_dwarf2( obj_file: &object::File, sections: &mut [Section], ) -> Result<()> { let arena_data = Arena::new(); let arena_relocations = Arena::new(); let endian = match obj_file.endianness() { object::Endianness::Little => gimli::RunTimeEndian::Little, object::Endianness::Big => gimli::RunTimeEndian::Big, }; let dwarf = gimli::Dwarf::load(|id: gimli::SectionId| -> Result<_> { load_file_section(id, obj_file, endian, &arena_data, &arena_relocations) }) .context("loading DWARF sections")?; let mut iter = dwarf.units(); if let Some(header) = iter.next().map_err(|e| gimli_error(e, "iterating over DWARF units"))? { let unit = dwarf.unit(header).map_err(|e| gimli_error(e, "loading DWARF unit"))?; if let Some(program) = unit.line_program.clone() { let mut text_sections = sections.iter_mut().filter(|s| s.kind == SectionKind::Code); let mut lines = text_sections.next().map(|section| &mut section.line_info); let mut rows = program.rows(); while let Some((_header, row)) = rows.next_row().map_err(|e| gimli_error(e, "loading program row"))? { if let (Some(line), Some(lines)) = (row.line(), &mut lines) { lines.insert(row.address(), line.get() as u32); } if row.end_sequence() { // The next row is the start of a new sequence, which means we must // advance to the next .text section. lines = text_sections.next().map(|section| &mut section.line_info); } } } } if iter.next().map_err(|e| gimli_error(e, "checking for next unit"))?.is_some() { log::warn!("Multiple units found in DWARF data, only processing the first"); } Ok(()) } #[derive(Debug, Default)] struct RelocationMap(object::read::RelocationMap); impl RelocationMap { fn add(&mut self, file: &object::File, section: &object::Section) { for (offset, relocation) in section.relocations() { if let Err(e) = self.0.add(file, offset, relocation) { log::error!( "Relocation error for section {} at offset 0x{:08x}: {}", section.name().unwrap(), offset, e ); } } } } impl gimli::read::Relocate for &'_ RelocationMap { fn relocate_address(&self, offset: usize, value: u64) -> gimli::Result { Ok(self.0.relocate(offset as u64, value)) } fn relocate_offset(&self, offset: usize, value: usize) -> gimli::Result { ::from_u64(self.0.relocate(offset as u64, value as u64)) } } type Relocate<'a, R> = gimli::RelocateReader; fn load_file_section<'input, 'arena, Endian: gimli::Endianity>( id: gimli::SectionId, file: &object::File<'input>, endian: Endian, arena_data: &'arena Arena>, arena_relocations: &'arena Arena, ) -> Result>> { let mut relocations = RelocationMap::default(); let data = match file.section_by_name(id.name()) { Some(ref section) => { relocations.add(file, section); section.uncompressed_data()? } // Use a non-zero capacity so that `ReaderOffsetId`s are unique. None => Cow::Owned(Vec::with_capacity(1)), }; let data_ref = arena_data.alloc(data); let section = gimli::EndianSlice::new(data_ref, endian); let relocations = arena_relocations.alloc(relocations); Ok(Relocate::new(section, relocations)) } #[inline] fn gimli_error(e: gimli::Error, context: &str) -> anyhow::Error { anyhow::anyhow!("gimli error {context}: {e:?}") }