aboutsummaryrefslogtreecommitdiff
path: root/vendor/gimli/src/write/loc.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gimli/src/write/loc.rs')
-rw-r--r--vendor/gimli/src/write/loc.rs550
1 files changed, 550 insertions, 0 deletions
diff --git a/vendor/gimli/src/write/loc.rs b/vendor/gimli/src/write/loc.rs
new file mode 100644
index 0000000..6dfe45a
--- /dev/null
+++ b/vendor/gimli/src/write/loc.rs
@@ -0,0 +1,550 @@
+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;
+ 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);
+ }
+ }
+ }
+ }
+}