use crate::common::{ DebugLineStrOffset, DebugStrOffset, DebugStrOffsetsBase, DebugStrOffsetsIndex, DwarfFileType, Encoding, SectionId, }; use crate::endianity::Endianity; use crate::read::{EndianSlice, Reader, ReaderOffset, Result, Section}; use crate::Format; /// The `DebugStr` struct represents the DWARF strings /// found in the `.debug_str` section. #[derive(Debug, Default, Clone, Copy)] pub struct DebugStr { debug_str_section: R, } impl<'input, Endian> DebugStr> where Endian: Endianity, { /// Construct a new `DebugStr` instance from the data in the `.debug_str` /// section. /// /// It is the caller's responsibility to read the `.debug_str` section and /// present it as a `&[u8]` slice. That means using some ELF loader on /// Linux, a Mach-O loader on macOS, etc. /// /// ``` /// use gimli::{DebugStr, LittleEndian}; /// /// # let buf = [0x00, 0x01, 0x02, 0x03]; /// # let read_debug_str_section_somehow = || &buf; /// let debug_str = DebugStr::new(read_debug_str_section_somehow(), LittleEndian); /// ``` pub fn new(debug_str_section: &'input [u8], endian: Endian) -> Self { Self::from(EndianSlice::new(debug_str_section, endian)) } } impl DebugStr { /// Lookup a string from the `.debug_str` section by DebugStrOffset. /// /// ``` /// use gimli::{DebugStr, DebugStrOffset, LittleEndian}; /// /// # let buf = [0x01, 0x02, 0x00]; /// # let offset = DebugStrOffset(0); /// # let read_debug_str_section_somehow = || &buf; /// # let debug_str_offset_somehow = || offset; /// let debug_str = DebugStr::new(read_debug_str_section_somehow(), LittleEndian); /// println!("Found string {:?}", debug_str.get_str(debug_str_offset_somehow())); /// ``` pub fn get_str(&self, offset: DebugStrOffset) -> Result { let input = &mut self.debug_str_section.clone(); input.skip(offset.0)?; input.read_null_terminated_slice() } } impl DebugStr { /// Create a `DebugStr` section that references the data in `self`. /// /// This is useful when `R` implements `Reader` but `T` does not. /// /// ## Example Usage /// /// ```rust,no_run /// # let load_section = || unimplemented!(); /// // Read the DWARF section into a `Vec` with whatever object loader you're using. /// let owned_section: gimli::DebugStr> = load_section(); /// // Create a reference to the DWARF section. /// let section = owned_section.borrow(|section| { /// gimli::EndianSlice::new(§ion, gimli::LittleEndian) /// }); /// ``` pub fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugStr where F: FnMut(&'a T) -> R, { borrow(&self.debug_str_section).into() } } impl Section for DebugStr { fn id() -> SectionId { SectionId::DebugStr } fn reader(&self) -> &R { &self.debug_str_section } } impl From for DebugStr { fn from(debug_str_section: R) -> Self { DebugStr { debug_str_section } } } /// The raw contents of the `.debug_str_offsets` section. #[derive(Debug, Default, Clone, Copy)] pub struct DebugStrOffsets { section: R, } impl DebugStrOffsets { // TODO: add an iterator over the sets of entries in the section. // This is not needed for common usage of the section though. /// Returns the `.debug_str` offset at the given `base` and `index`. /// /// A set of entries in the `.debug_str_offsets` section consists of a header /// followed by a series of string table offsets. /// /// The `base` must be the `DW_AT_str_offsets_base` value from the compilation unit DIE. /// This is an offset that points to the first entry following the header. /// /// The `index` is the value of a `DW_FORM_strx` attribute. /// /// The `format` must be the DWARF format of the compilation unit. This format must /// match the header. However, note that we do not parse the header to validate this, /// since locating the header is unreliable, and the GNU extensions do not emit it. pub fn get_str_offset( &self, format: Format, base: DebugStrOffsetsBase, index: DebugStrOffsetsIndex, ) -> Result> { let input = &mut self.section.clone(); input.skip(base.0)?; input.skip(R::Offset::from_u64( index.0.into_u64() * u64::from(format.word_size()), )?)?; input.read_offset(format).map(DebugStrOffset) } } impl DebugStrOffsets { /// Create a `DebugStrOffsets` section that references the data in `self`. /// /// This is useful when `R` implements `Reader` but `T` does not. /// /// ## Example Usage /// /// ```rust,no_run /// # let load_section = || unimplemented!(); /// // Read the DWARF section into a `Vec` with whatever object loader you're using. /// let owned_section: gimli::DebugStrOffsets> = load_section(); /// // Create a reference to the DWARF section. /// let section = owned_section.borrow(|section| { /// gimli::EndianSlice::new(§ion, gimli::LittleEndian) /// }); /// ``` pub fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugStrOffsets where F: FnMut(&'a T) -> R, { borrow(&self.section).into() } } impl Section for DebugStrOffsets { fn id() -> SectionId { SectionId::DebugStrOffsets } fn reader(&self) -> &R { &self.section } } impl From for DebugStrOffsets { fn from(section: R) -> Self { DebugStrOffsets { section } } } impl DebugStrOffsetsBase where Offset: ReaderOffset, { /// Returns a `DebugStrOffsetsBase` with the default value of DW_AT_str_offsets_base /// for the given `Encoding` and `DwarfFileType`. pub fn default_for_encoding_and_file( encoding: Encoding, file_type: DwarfFileType, ) -> DebugStrOffsetsBase { if encoding.version >= 5 && file_type == DwarfFileType::Dwo { // In .dwo files, the compiler omits the DW_AT_str_offsets_base attribute (because there is // only a single unit in the file) but we must skip past the header, which the attribute // would normally do for us. // initial_length_size + version + 2 bytes of padding. DebugStrOffsetsBase(Offset::from_u8( encoding.format.initial_length_size() + 2 + 2, )) } else { DebugStrOffsetsBase(Offset::from_u8(0)) } } } /// The `DebugLineStr` struct represents the DWARF strings /// found in the `.debug_line_str` section. #[derive(Debug, Default, Clone, Copy)] pub struct DebugLineStr { section: R, } impl<'input, Endian> DebugLineStr> where Endian: Endianity, { /// Construct a new `DebugLineStr` instance from the data in the `.debug_line_str` /// section. /// /// It is the caller's responsibility to read the `.debug_line_str` section and /// present it as a `&[u8]` slice. That means using some ELF loader on /// Linux, a Mach-O loader on macOS, etc. /// /// ``` /// use gimli::{DebugLineStr, LittleEndian}; /// /// # let buf = [0x00, 0x01, 0x02, 0x03]; /// # let read_debug_line_str_section_somehow = || &buf; /// let debug_str = DebugLineStr::new(read_debug_line_str_section_somehow(), LittleEndian); /// ``` pub fn new(debug_line_str_section: &'input [u8], endian: Endian) -> Self { Self::from(EndianSlice::new(debug_line_str_section, endian)) } } impl DebugLineStr { /// Lookup a string from the `.debug_line_str` section by DebugLineStrOffset. pub fn get_str(&self, offset: DebugLineStrOffset) -> Result { let input = &mut self.section.clone(); input.skip(offset.0)?; input.read_null_terminated_slice() } } impl DebugLineStr { /// Create a `DebugLineStr` section that references the data in `self`. /// /// This is useful when `R` implements `Reader` but `T` does not. /// /// ## Example Usage /// /// ```rust,no_run /// # let load_section = || unimplemented!(); /// // Read the DWARF section into a `Vec` with whatever object loader you're using. /// let owned_section: gimli::DebugLineStr> = load_section(); /// // Create a reference to the DWARF section. /// let section = owned_section.borrow(|section| { /// gimli::EndianSlice::new(§ion, gimli::LittleEndian) /// }); /// ``` pub fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugLineStr where F: FnMut(&'a T) -> R, { borrow(&self.section).into() } } impl Section for DebugLineStr { fn id() -> SectionId { SectionId::DebugLineStr } fn reader(&self) -> &R { &self.section } } impl From for DebugLineStr { fn from(section: R) -> Self { DebugLineStr { section } } } #[cfg(test)] mod tests { use super::*; use crate::test_util::GimliSectionMethods; use crate::LittleEndian; use test_assembler::{Endian, Label, LabelMaker, Section}; #[test] fn test_get_str_offset() { for format in vec![Format::Dwarf32, Format::Dwarf64] { let zero = Label::new(); let length = Label::new(); let start = Label::new(); let first = Label::new(); let end = Label::new(); let mut section = Section::with_endian(Endian::Little) .mark(&zero) .initial_length(format, &length, &start) .D16(5) .D16(0) .mark(&first); for i in 0..20 { section = section.word(format.word_size(), 1000 + i); } section = section.mark(&end); length.set_const((&end - &start) as u64); let section = section.get_contents().unwrap(); let debug_str_offsets = DebugStrOffsets::from(EndianSlice::new(§ion, LittleEndian)); let base = DebugStrOffsetsBase((&first - &zero) as usize); assert_eq!( debug_str_offsets.get_str_offset(format, base, DebugStrOffsetsIndex(0)), Ok(DebugStrOffset(1000)) ); assert_eq!( debug_str_offsets.get_str_offset(format, base, DebugStrOffsetsIndex(19)), Ok(DebugStrOffset(1019)) ); } } }