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, } 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( &self, sections: &mut Sections, encoding: Encoding, unit_offsets: Option<&UnitOffsets>, ) -> Result { 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( &self, w: &mut DebugLoc, refs: &mut Vec, encoding: Encoding, unit_offsets: Option<&UnitOffsets>, ) -> Result { 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( &self, w: &mut DebugLocLists, refs: &mut Vec, encoding: Encoding, unit_offsets: Option<&UnitOffsets>, ) -> Result { 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); /// 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: &mut W, refs: &mut Vec, 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>( mut from: read::RawLocListIter, context: &ConvertUnitContext, ) -> ConvertResult { 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; use std::sync::Arc; #[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: Arc::new(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); } } } } }