| use alloc::vec::Vec; |
| use indexmap::IndexSet; |
| use std::ops::{Deref, DerefMut}; |
| |
| use crate::common::{Encoding, RangeListsOffset, SectionId}; |
| use crate::write::{Address, BaseId, Error, Result, Section, Sections, Writer}; |
| |
| define_section!( |
| DebugRanges, |
| RangeListsOffset, |
| "A writable `.debug_ranges` section." |
| ); |
| define_section!( |
| DebugRngLists, |
| RangeListsOffset, |
| "A writable `.debug_rnglists` section." |
| ); |
| |
| define_offsets!( |
| RangeListOffsets: RangeListId => RangeListsOffset, |
| "The section offsets of a series of range lists within the `.debug_ranges` or `.debug_rnglists` sections." |
| ); |
| |
| define_id!( |
| RangeListId, |
| "An identifier for a range list in a `RangeListTable`." |
| ); |
| |
| /// A table of range lists that will be stored in a `.debug_ranges` or `.debug_rnglists` section. |
| #[derive(Debug, Default)] |
| pub struct RangeListTable { |
| base_id: BaseId, |
| ranges: IndexSet<RangeList>, |
| } |
| |
| impl RangeListTable { |
| /// Add a range list to the table. |
| pub fn add(&mut self, range_list: RangeList) -> RangeListId { |
| let (index, _) = self.ranges.insert_full(range_list); |
| RangeListId::new(self.base_id, index) |
| } |
| |
| /// Write the range list table to the appropriate section for the given DWARF version. |
| pub(crate) fn write<W: Writer>( |
| &self, |
| sections: &mut Sections<W>, |
| encoding: Encoding, |
| ) -> Result<RangeListOffsets> { |
| if self.ranges.is_empty() { |
| return Ok(RangeListOffsets::none()); |
| } |
| |
| match encoding.version { |
| 2..=4 => self.write_ranges(&mut sections.debug_ranges, encoding.address_size), |
| 5 => self.write_rnglists(&mut sections.debug_rnglists, encoding), |
| _ => Err(Error::UnsupportedVersion(encoding.version)), |
| } |
| } |
| |
| /// Write the range list table to the `.debug_ranges` section. |
| fn write_ranges<W: Writer>( |
| &self, |
| w: &mut DebugRanges<W>, |
| address_size: u8, |
| ) -> Result<RangeListOffsets> { |
| let mut offsets = Vec::new(); |
| for range_list in self.ranges.iter() { |
| offsets.push(w.offset()); |
| for range in &range_list.0 { |
| // Note that we must ensure none of the ranges have both begin == 0 and end == 0. |
| // We do this by ensuring that begin != end, which is a bit more restrictive |
| // than required, but still seems reasonable. |
| match *range { |
| Range::BaseAddress { address } => { |
| let marker = !0 >> (64 - address_size * 8); |
| w.write_udata(marker, address_size)?; |
| w.write_address(address, address_size)?; |
| } |
| Range::OffsetPair { begin, end } => { |
| if begin == end { |
| return Err(Error::InvalidRange); |
| } |
| w.write_udata(begin, address_size)?; |
| w.write_udata(end, address_size)?; |
| } |
| Range::StartEnd { begin, end } => { |
| if begin == end { |
| return Err(Error::InvalidRange); |
| } |
| w.write_address(begin, address_size)?; |
| w.write_address(end, address_size)?; |
| } |
| Range::StartLength { begin, length } => { |
| let end = match begin { |
| Address::Constant(begin) => Address::Constant(begin + length), |
| Address::Symbol { symbol, addend } => Address::Symbol { |
| symbol, |
| addend: addend + length as i64, |
| }, |
| }; |
| if begin == end { |
| return Err(Error::InvalidRange); |
| } |
| w.write_address(begin, address_size)?; |
| w.write_address(end, address_size)?; |
| } |
| } |
| } |
| w.write_udata(0, address_size)?; |
| w.write_udata(0, address_size)?; |
| } |
| Ok(RangeListOffsets { |
| base_id: self.base_id, |
| offsets, |
| }) |
| } |
| |
| /// Write the range list table to the `.debug_rnglists` section. |
| fn write_rnglists<W: Writer>( |
| &self, |
| w: &mut DebugRngLists<W>, |
| encoding: Encoding, |
| ) -> Result<RangeListOffsets> { |
| let mut offsets = Vec::new(); |
| |
| if encoding.version != 5 { |
| return Err(Error::NeedVersion(5)); |
| } |
| |
| let length_offset = w.write_initial_length(encoding.format)?; |
| let length_base = w.len(); |
| |
| w.write_u16(encoding.version)?; |
| w.write_u8(encoding.address_size)?; |
| w.write_u8(0)?; // segment_selector_size |
| w.write_u32(0)?; // offset_entry_count (when set to zero DW_FORM_rnglistx can't be used, see section 7.28) |
| // FIXME implement DW_FORM_rnglistx writing and implement the offset entry list |
| |
| for range_list in self.ranges.iter() { |
| offsets.push(w.offset()); |
| for range in &range_list.0 { |
| match *range { |
| Range::BaseAddress { address } => { |
| w.write_u8(crate::constants::DW_RLE_base_address.0)?; |
| w.write_address(address, encoding.address_size)?; |
| } |
| Range::OffsetPair { begin, end } => { |
| w.write_u8(crate::constants::DW_RLE_offset_pair.0)?; |
| w.write_uleb128(begin)?; |
| w.write_uleb128(end)?; |
| } |
| Range::StartEnd { begin, end } => { |
| w.write_u8(crate::constants::DW_RLE_start_end.0)?; |
| w.write_address(begin, encoding.address_size)?; |
| w.write_address(end, encoding.address_size)?; |
| } |
| Range::StartLength { begin, length } => { |
| w.write_u8(crate::constants::DW_RLE_start_length.0)?; |
| w.write_address(begin, encoding.address_size)?; |
| w.write_uleb128(length)?; |
| } |
| } |
| } |
| |
| w.write_u8(crate::constants::DW_RLE_end_of_list.0)?; |
| } |
| |
| let length = (w.len() - length_base) as u64; |
| w.write_initial_length_at(length_offset, length, encoding.format)?; |
| |
| Ok(RangeListOffsets { |
| base_id: self.base_id, |
| offsets, |
| }) |
| } |
| } |
| |
| /// A range list that will be stored in a `.debug_ranges` or `.debug_rnglists` section. |
| #[derive(Clone, Debug, Eq, PartialEq, Hash)] |
| pub struct RangeList(pub Vec<Range>); |
| |
| /// A single range. |
| #[derive(Clone, Debug, Eq, PartialEq, Hash)] |
| pub enum Range { |
| /// DW_RLE_base_address |
| BaseAddress { |
| /// Base address. |
| address: Address, |
| }, |
| /// DW_RLE_offset_pair |
| OffsetPair { |
| /// Start of range relative to base address. |
| begin: u64, |
| /// End of range relative to base address. |
| end: u64, |
| }, |
| /// DW_RLE_start_end |
| StartEnd { |
| /// Start of range. |
| begin: Address, |
| /// End of range. |
| end: Address, |
| }, |
| /// DW_RLE_start_length |
| StartLength { |
| /// Start of range. |
| begin: Address, |
| /// Length of range. |
| length: u64, |
| }, |
| } |
| |
| #[cfg(feature = "read")] |
| mod convert { |
| use super::*; |
| |
| use crate::read::{self, Reader}; |
| use crate::write::{ConvertError, ConvertResult, ConvertUnitContext}; |
| |
| impl RangeList { |
| /// Create a range list by reading the data from the give range list iter. |
| pub(crate) fn from<R: Reader<Offset = usize>>( |
| mut from: read::RawRngListIter<R>, |
| context: &ConvertUnitContext<R>, |
| ) -> ConvertResult<Self> { |
| let mut have_base_address = context.base_address != Address::Constant(0); |
| let convert_address = |
| |x| (context.convert_address)(x).ok_or(ConvertError::InvalidAddress); |
| let mut ranges = Vec::new(); |
| while let Some(from_range) = from.next()? { |
| let range = match from_range { |
| read::RawRngListEntry::AddressOrOffsetPair { begin, end } => { |
| // These were parsed as addresses, even if they are offsets. |
| let begin = convert_address(begin)?; |
| let end = convert_address(end)?; |
| match (begin, end) { |
| (Address::Constant(begin_offset), Address::Constant(end_offset)) => { |
| if have_base_address { |
| Range::OffsetPair { |
| begin: begin_offset, |
| end: end_offset, |
| } |
| } else { |
| Range::StartEnd { begin, end } |
| } |
| } |
| _ => { |
| if have_base_address { |
| // At least one of begin/end is an address, but we also have |
| // a base address. Adding addresses is undefined. |
| return Err(ConvertError::InvalidRangeRelativeAddress); |
| } |
| Range::StartEnd { begin, end } |
| } |
| } |
| } |
| read::RawRngListEntry::BaseAddress { addr } => { |
| have_base_address = true; |
| let address = convert_address(addr)?; |
| Range::BaseAddress { address } |
| } |
| read::RawRngListEntry::BaseAddressx { addr } => { |
| have_base_address = true; |
| let address = convert_address(context.dwarf.address(context.unit, addr)?)?; |
| Range::BaseAddress { address } |
| } |
| read::RawRngListEntry::StartxEndx { begin, end } => { |
| let begin = convert_address(context.dwarf.address(context.unit, begin)?)?; |
| let end = convert_address(context.dwarf.address(context.unit, end)?)?; |
| Range::StartEnd { begin, end } |
| } |
| read::RawRngListEntry::StartxLength { begin, length } => { |
| let begin = convert_address(context.dwarf.address(context.unit, begin)?)?; |
| Range::StartLength { begin, length } |
| } |
| read::RawRngListEntry::OffsetPair { begin, end } => { |
| Range::OffsetPair { begin, end } |
| } |
| read::RawRngListEntry::StartEnd { begin, end } => { |
| let begin = convert_address(begin)?; |
| let end = convert_address(end)?; |
| Range::StartEnd { begin, end } |
| } |
| read::RawRngListEntry::StartLength { begin, length } => { |
| let begin = convert_address(begin)?; |
| Range::StartLength { begin, length } |
| } |
| }; |
| // Filtering empty ranges out. |
| match range { |
| Range::StartLength { length, .. } if length == 0 => continue, |
| Range::StartEnd { begin, end, .. } if begin == end => continue, |
| Range::OffsetPair { begin, end, .. } if begin == end => continue, |
| _ => (), |
| } |
| ranges.push(range); |
| } |
| Ok(RangeList(ranges)) |
| } |
| } |
| } |
| |
| #[cfg(test)] |
| #[cfg(feature = "read")] |
| mod tests { |
| use super::*; |
| use crate::common::{ |
| DebugAbbrevOffset, DebugAddrBase, DebugInfoOffset, DebugLocListsBase, DebugRngListsBase, |
| DebugStrOffsetsBase, Format, |
| }; |
| use crate::read; |
| use crate::write::{ |
| ConvertUnitContext, EndianVec, LineStringTable, LocationListTable, Range, RangeListTable, |
| StringTable, |
| }; |
| use crate::LittleEndian; |
| use std::collections::HashMap; |
| |
| #[test] |
| fn test_range() { |
| let mut line_strings = LineStringTable::default(); |
| let mut strings = StringTable::default(); |
| |
| for &version in &[2, 3, 4, 5] { |
| for &address_size in &[4, 8] { |
| for &format in &[Format::Dwarf32, Format::Dwarf64] { |
| let encoding = Encoding { |
| format, |
| version, |
| address_size, |
| }; |
| |
| let mut range_list = RangeList(vec![ |
| Range::StartLength { |
| begin: Address::Constant(6666), |
| length: 7777, |
| }, |
| Range::StartEnd { |
| begin: Address::Constant(4444), |
| end: Address::Constant(5555), |
| }, |
| Range::BaseAddress { |
| address: Address::Constant(1111), |
| }, |
| Range::OffsetPair { |
| begin: 2222, |
| end: 3333, |
| }, |
| ]); |
| |
| let mut ranges = RangeListTable::default(); |
| let range_list_id = ranges.add(range_list.clone()); |
| |
| let mut sections = Sections::new(EndianVec::new(LittleEndian)); |
| let range_list_offsets = ranges.write(&mut sections, encoding).unwrap(); |
| |
| let read_debug_ranges = |
| read::DebugRanges::new(sections.debug_ranges.slice(), LittleEndian); |
| let read_debug_rnglists = |
| read::DebugRngLists::new(sections.debug_rnglists.slice(), LittleEndian); |
| let read_ranges = read::RangeLists::new(read_debug_ranges, read_debug_rnglists); |
| let offset = range_list_offsets.get(range_list_id); |
| let read_range_list = read_ranges.raw_ranges(offset, encoding).unwrap(); |
| |
| let dwarf = read::Dwarf { |
| ranges: read_ranges, |
| ..Default::default() |
| }; |
| let unit = read::Unit { |
| header: read::UnitHeader::new( |
| encoding, |
| 0, |
| read::UnitType::Compilation, |
| DebugAbbrevOffset(0), |
| DebugInfoOffset(0).into(), |
| read::EndianSlice::default(), |
| ), |
| abbreviations: read::Abbreviations::default(), |
| name: None, |
| comp_dir: None, |
| low_pc: 0, |
| str_offsets_base: DebugStrOffsetsBase(0), |
| addr_base: DebugAddrBase(0), |
| loclists_base: DebugLocListsBase(0), |
| rnglists_base: DebugRngListsBase(0), |
| line_program: None, |
| dwo_id: None, |
| }; |
| let context = ConvertUnitContext { |
| dwarf: &dwarf, |
| unit: &unit, |
| line_strings: &mut line_strings, |
| strings: &mut strings, |
| ranges: &mut ranges, |
| locations: &mut LocationListTable::default(), |
| convert_address: &|address| Some(Address::Constant(address)), |
| base_address: Address::Constant(0), |
| line_program_offset: None, |
| line_program_files: Vec::new(), |
| entry_ids: &HashMap::new(), |
| }; |
| let convert_range_list = RangeList::from(read_range_list, &context).unwrap(); |
| |
| if version <= 4 { |
| range_list.0[0] = Range::StartEnd { |
| begin: Address::Constant(6666), |
| end: Address::Constant(6666 + 7777), |
| }; |
| } |
| assert_eq!(range_list, convert_range_list); |
| } |
| } |
| } |
| } |
| } |