| use alloc::vec::Vec; |
| use indexmap::IndexSet; |
| use std::ops::{Deref, DerefMut}; |
| |
| use crate::common::{Encoding, LocationListsOffset, SectionId}; |
| use crate::write::{ |
| Address, BaseId, DebugInfoReference, Error, Expression, Result, Section, Sections, UnitOffsets, |
| Writer, |
| }; |
| |
| define_section!( |
| DebugLoc, |
| LocationListsOffset, |
| "A writable `.debug_loc` section." |
| ); |
| define_section!( |
| DebugLocLists, |
| LocationListsOffset, |
| "A writable `.debug_loclists` section." |
| ); |
| |
| define_offsets!( |
| LocationListOffsets: LocationListId => LocationListsOffset, |
| "The section offsets of a series of location lists within the `.debug_loc` or `.debug_loclists` sections." |
| ); |
| |
| define_id!( |
| LocationListId, |
| "An identifier for a location list in a `LocationListTable`." |
| ); |
| |
| /// A table of location lists that will be stored in a `.debug_loc` or `.debug_loclists` section. |
| #[derive(Debug, Default)] |
| pub struct LocationListTable { |
| base_id: BaseId, |
| locations: IndexSet<LocationList>, |
| } |
| |
| impl LocationListTable { |
| /// Add a location list to the table. |
| pub fn add(&mut self, loc_list: LocationList) -> LocationListId { |
| let (index, _) = self.locations.insert_full(loc_list); |
| LocationListId::new(self.base_id, index) |
| } |
| |
| /// Write the location list table to the appropriate section for the given DWARF version. |
| pub(crate) fn write<W: Writer>( |
| &self, |
| sections: &mut Sections<W>, |
| encoding: Encoding, |
| unit_offsets: Option<&UnitOffsets>, |
| ) -> Result<LocationListOffsets> { |
| if self.locations.is_empty() { |
| return Ok(LocationListOffsets::none()); |
| } |
| |
| match encoding.version { |
| 2..=4 => self.write_loc( |
| &mut sections.debug_loc, |
| &mut sections.debug_loc_refs, |
| encoding, |
| unit_offsets, |
| ), |
| 5 => self.write_loclists( |
| &mut sections.debug_loclists, |
| &mut sections.debug_loclists_refs, |
| encoding, |
| unit_offsets, |
| ), |
| _ => Err(Error::UnsupportedVersion(encoding.version)), |
| } |
| } |
| |
| /// Write the location list table to the `.debug_loc` section. |
| fn write_loc<W: Writer>( |
| &self, |
| w: &mut DebugLoc<W>, |
| refs: &mut Vec<DebugInfoReference>, |
| encoding: Encoding, |
| unit_offsets: Option<&UnitOffsets>, |
| ) -> Result<LocationListOffsets> { |
| let address_size = encoding.address_size; |
| let mut offsets = Vec::new(); |
| for loc_list in self.locations.iter() { |
| offsets.push(w.offset()); |
| for loc in &loc_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 *loc { |
| Location::BaseAddress { address } => { |
| let marker = !0 >> (64 - address_size * 8); |
| w.write_udata(marker, address_size)?; |
| w.write_address(address, address_size)?; |
| } |
| Location::OffsetPair { |
| begin, |
| end, |
| ref data, |
| } => { |
| if begin == end { |
| return Err(Error::InvalidRange); |
| } |
| w.write_udata(begin, address_size)?; |
| w.write_udata(end, address_size)?; |
| write_expression(&mut w.0, refs, encoding, unit_offsets, data)?; |
| } |
| Location::StartEnd { |
| begin, |
| end, |
| ref data, |
| } => { |
| if begin == end { |
| return Err(Error::InvalidRange); |
| } |
| w.write_address(begin, address_size)?; |
| w.write_address(end, address_size)?; |
| write_expression(&mut w.0, refs, encoding, unit_offsets, data)?; |
| } |
| Location::StartLength { |
| begin, |
| length, |
| ref data, |
| } => { |
| 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)?; |
| write_expression(&mut w.0, refs, encoding, unit_offsets, data)?; |
| } |
| Location::DefaultLocation { .. } => { |
| return Err(Error::InvalidRange); |
| } |
| } |
| } |
| w.write_udata(0, address_size)?; |
| w.write_udata(0, address_size)?; |
| } |
| Ok(LocationListOffsets { |
| base_id: self.base_id, |
| offsets, |
| }) |
| } |
| |
| /// Write the location list table to the `.debug_loclists` section. |
| fn write_loclists<W: Writer>( |
| &self, |
| w: &mut DebugLocLists<W>, |
| refs: &mut Vec<DebugInfoReference>, |
| encoding: Encoding, |
| unit_offsets: Option<&UnitOffsets>, |
| ) -> Result<LocationListOffsets> { |
| 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 loc_list in self.locations.iter() { |
| offsets.push(w.offset()); |
| for loc in &loc_list.0 { |
| match *loc { |
| Location::BaseAddress { address } => { |
| w.write_u8(crate::constants::DW_LLE_base_address.0)?; |
| w.write_address(address, encoding.address_size)?; |
| } |
| Location::OffsetPair { |
| begin, |
| end, |
| ref data, |
| } => { |
| w.write_u8(crate::constants::DW_LLE_offset_pair.0)?; |
| w.write_uleb128(begin)?; |
| w.write_uleb128(end)?; |
| write_expression(&mut w.0, refs, encoding, unit_offsets, data)?; |
| } |
| Location::StartEnd { |
| begin, |
| end, |
| ref data, |
| } => { |
| w.write_u8(crate::constants::DW_LLE_start_end.0)?; |
| w.write_address(begin, encoding.address_size)?; |
| w.write_address(end, encoding.address_size)?; |
| write_expression(&mut w.0, refs, encoding, unit_offsets, data)?; |
| } |
| Location::StartLength { |
| begin, |
| length, |
| ref data, |
| } => { |
| w.write_u8(crate::constants::DW_LLE_start_length.0)?; |
| w.write_address(begin, encoding.address_size)?; |
| w.write_uleb128(length)?; |
| write_expression(&mut w.0, refs, encoding, unit_offsets, data)?; |
| } |
| Location::DefaultLocation { ref data } => { |
| w.write_u8(crate::constants::DW_LLE_default_location.0)?; |
| write_expression(&mut w.0, refs, encoding, unit_offsets, data)?; |
| } |
| } |
| } |
| |
| w.write_u8(crate::constants::DW_LLE_end_of_list.0)?; |
| } |
| |
| let length = (w.len() - length_base) as u64; |
| w.write_initial_length_at(length_offset, length, encoding.format)?; |
| |
| Ok(LocationListOffsets { |
| base_id: self.base_id, |
| offsets, |
| }) |
| } |
| } |
| |
| /// A locations list that will be stored in a `.debug_loc` or `.debug_loclists` section. |
| #[derive(Clone, Debug, Eq, PartialEq, Hash)] |
| pub struct LocationList(pub Vec<Location>); |
| |
| /// A single location. |
| #[derive(Clone, Debug, Eq, PartialEq, Hash)] |
| pub enum Location { |
| /// DW_LLE_base_address |
| BaseAddress { |
| /// Base address. |
| address: Address, |
| }, |
| /// DW_LLE_offset_pair |
| OffsetPair { |
| /// Start of range relative to base address. |
| begin: u64, |
| /// End of range relative to base address. |
| end: u64, |
| /// Location description. |
| data: Expression, |
| }, |
| /// DW_LLE_start_end |
| StartEnd { |
| /// Start of range. |
| begin: Address, |
| /// End of range. |
| end: Address, |
| /// Location description. |
| data: Expression, |
| }, |
| /// DW_LLE_start_length |
| StartLength { |
| /// Start of range. |
| begin: Address, |
| /// Length of range. |
| length: u64, |
| /// Location description. |
| data: Expression, |
| }, |
| /// DW_LLE_default_location |
| DefaultLocation { |
| /// Location description. |
| data: Expression, |
| }, |
| } |
| |
| fn write_expression<W: Writer>( |
| w: &mut W, |
| refs: &mut Vec<DebugInfoReference>, |
| encoding: Encoding, |
| unit_offsets: Option<&UnitOffsets>, |
| val: &Expression, |
| ) -> Result<()> { |
| let size = val.size(encoding, unit_offsets) as u64; |
| if encoding.version <= 4 { |
| w.write_udata(size, 2)?; |
| } else { |
| w.write_uleb128(size)?; |
| } |
| val.write(w, Some(refs), encoding, unit_offsets)?; |
| Ok(()) |
| } |
| |
| #[cfg(feature = "read")] |
| mod convert { |
| use super::*; |
| |
| use crate::read::{self, Reader}; |
| use crate::write::{ConvertError, ConvertResult, ConvertUnitContext}; |
| |
| impl LocationList { |
| /// Create a location list by reading the data from the give location list iter. |
| pub(crate) fn from<R: Reader<Offset = usize>>( |
| mut from: read::RawLocListIter<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 convert_expression = |x| { |
| Expression::from( |
| x, |
| context.unit.encoding(), |
| Some(context.dwarf), |
| Some(context.unit), |
| Some(context.entry_ids), |
| context.convert_address, |
| ) |
| }; |
| let mut loc_list = Vec::new(); |
| while let Some(from_loc) = from.next()? { |
| let loc = match from_loc { |
| read::RawLocListEntry::AddressOrOffsetPair { begin, end, data } => { |
| // These were parsed as addresses, even if they are offsets. |
| let begin = convert_address(begin)?; |
| let end = convert_address(end)?; |
| let data = convert_expression(data)?; |
| match (begin, end) { |
| (Address::Constant(begin_offset), Address::Constant(end_offset)) => { |
| if have_base_address { |
| Location::OffsetPair { |
| begin: begin_offset, |
| end: end_offset, |
| data, |
| } |
| } else { |
| Location::StartEnd { begin, end, data } |
| } |
| } |
| _ => { |
| 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); |
| } |
| Location::StartEnd { begin, end, data } |
| } |
| } |
| } |
| read::RawLocListEntry::BaseAddress { addr } => { |
| have_base_address = true; |
| let address = convert_address(addr)?; |
| Location::BaseAddress { address } |
| } |
| read::RawLocListEntry::BaseAddressx { addr } => { |
| have_base_address = true; |
| let address = convert_address(context.dwarf.address(context.unit, addr)?)?; |
| Location::BaseAddress { address } |
| } |
| read::RawLocListEntry::StartxEndx { begin, end, data } => { |
| let begin = convert_address(context.dwarf.address(context.unit, begin)?)?; |
| let end = convert_address(context.dwarf.address(context.unit, end)?)?; |
| let data = convert_expression(data)?; |
| Location::StartEnd { begin, end, data } |
| } |
| read::RawLocListEntry::StartxLength { |
| begin, |
| length, |
| data, |
| } => { |
| let begin = convert_address(context.dwarf.address(context.unit, begin)?)?; |
| let data = convert_expression(data)?; |
| Location::StartLength { |
| begin, |
| length, |
| data, |
| } |
| } |
| read::RawLocListEntry::OffsetPair { begin, end, data } => { |
| let data = convert_expression(data)?; |
| Location::OffsetPair { begin, end, data } |
| } |
| read::RawLocListEntry::StartEnd { begin, end, data } => { |
| let begin = convert_address(begin)?; |
| let end = convert_address(end)?; |
| let data = convert_expression(data)?; |
| Location::StartEnd { begin, end, data } |
| } |
| read::RawLocListEntry::StartLength { |
| begin, |
| length, |
| data, |
| } => { |
| let begin = convert_address(begin)?; |
| let data = convert_expression(data)?; |
| Location::StartLength { |
| begin, |
| length, |
| data, |
| } |
| } |
| read::RawLocListEntry::DefaultLocation { data } => { |
| let data = convert_expression(data)?; |
| Location::DefaultLocation { data } |
| } |
| }; |
| // In some cases, existing data may contain begin == end, filtering |
| // these out. |
| match loc { |
| Location::StartLength { length, .. } if length == 0 => continue, |
| Location::StartEnd { begin, end, .. } if begin == end => continue, |
| Location::OffsetPair { begin, end, .. } if begin == end => continue, |
| _ => (), |
| } |
| loc_list.push(loc); |
| } |
| Ok(LocationList(loc_list)) |
| } |
| } |
| } |
| |
| #[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, RangeListTable, StringTable, |
| }; |
| use crate::LittleEndian; |
| use std::collections::HashMap; |
| |
| #[test] |
| fn test_loc_list() { |
| let mut line_strings = LineStringTable::default(); |
| let mut strings = StringTable::default(); |
| let mut expression = Expression::new(); |
| expression.op_constu(0); |
| |
| 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 loc_list = LocationList(vec![ |
| Location::StartLength { |
| begin: Address::Constant(6666), |
| length: 7777, |
| data: expression.clone(), |
| }, |
| Location::StartEnd { |
| begin: Address::Constant(4444), |
| end: Address::Constant(5555), |
| data: expression.clone(), |
| }, |
| Location::BaseAddress { |
| address: Address::Constant(1111), |
| }, |
| Location::OffsetPair { |
| begin: 2222, |
| end: 3333, |
| data: expression.clone(), |
| }, |
| ]); |
| if version >= 5 { |
| loc_list.0.push(Location::DefaultLocation { |
| data: expression.clone(), |
| }); |
| } |
| |
| let mut locations = LocationListTable::default(); |
| let loc_list_id = locations.add(loc_list.clone()); |
| |
| let mut sections = Sections::new(EndianVec::new(LittleEndian)); |
| let loc_list_offsets = locations.write(&mut sections, encoding, None).unwrap(); |
| assert!(sections.debug_loc_refs.is_empty()); |
| assert!(sections.debug_loclists_refs.is_empty()); |
| |
| let read_debug_loc = |
| read::DebugLoc::new(sections.debug_loc.slice(), LittleEndian); |
| let read_debug_loclists = |
| read::DebugLocLists::new(sections.debug_loclists.slice(), LittleEndian); |
| let read_loc = read::LocationLists::new(read_debug_loc, read_debug_loclists); |
| let offset = loc_list_offsets.get(loc_list_id); |
| let read_loc_list = read_loc.raw_locations(offset, encoding).unwrap(); |
| |
| let dwarf = read::Dwarf { |
| locations: read_loc, |
| ..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 RangeListTable::default(), |
| locations: &mut locations, |
| 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_loc_list = LocationList::from(read_loc_list, &context).unwrap(); |
| |
| if version <= 4 { |
| loc_list.0[0] = Location::StartEnd { |
| begin: Address::Constant(6666), |
| end: Address::Constant(6666 + 7777), |
| data: expression.clone(), |
| }; |
| } |
| assert_eq!(loc_list, convert_loc_list); |
| } |
| } |
| } |
| } |
| } |