blob: ea0ecb1cf75bb5c8ce72484df8ec3c5059d3040c [file] [log] [blame]
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);
}
}
}
}
}