use alloc::vec::Vec;
use core::ops::{Deref, DerefMut};
use crate::common::{Encoding, LocationListsOffset, SectionId};
use crate::write::{
Address, BaseId, DebugInfoFixup, Error, Expression, FnvIndexSet, 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`."
);
#[derive(Debug, Default)]
pub struct LocationListTable {
base_id: BaseId,
locations: FnvIndexSet<LocationList>,
}
impl LocationListTable {
pub fn add(&mut self, loc_list: LocationList) -> LocationListId {
let (index, _) = self.locations.insert_full(loc_list);
LocationListId::new(self.base_id, index)
}
#[inline]
pub fn get(&self, id: LocationListId) -> &LocationList {
debug_assert_eq!(self.base_id, id.base_id);
&self.locations[id.index]
}
pub(crate) fn write<W: Writer>(
&self,
sections: &mut Sections<W>,
encoding: Encoding,
have_base_address: bool,
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_fixups,
encoding,
have_base_address,
unit_offsets,
),
5 => self.write_loclists(
&mut sections.debug_loclists,
&mut sections.debug_loclists_fixups,
encoding,
unit_offsets,
),
_ => Err(Error::UnsupportedVersion(encoding.version)),
}
}
fn write_loc<W: Writer>(
&self,
w: &mut DebugLoc<W>,
refs: &mut Vec<DebugInfoFixup>,
encoding: Encoding,
have_unit_base_address: bool,
unit_offsets: Option<&UnitOffsets>,
) -> Result<LocationListOffsets> {
let address_size = encoding.address_size;
let mut offsets = Vec::new();
for loc_list in self.locations.iter() {
let mut have_base_address = have_unit_base_address;
offsets.push(w.offset());
for loc in &loc_list.0 {
match *loc {
Location::BaseAddress { address } => {
let marker = !0 >> (64 - address_size * 8);
w.write_udata(marker, address_size)?;
w.write_address(address, address_size)?;
have_base_address = true;
}
Location::OffsetPair {
begin,
end,
ref data,
} => {
if begin == end {
return Err(Error::InvalidRange);
}
if !have_base_address {
return Err(Error::MissingBaseAddress);
}
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);
}
if have_base_address {
return Err(Error::UnexpectedBaseAddress);
}
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);
}
if have_base_address {
return Err(Error::UnexpectedBaseAddress);
}
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,
})
}
fn write_loclists<W: Writer>(
&self,
w: &mut DebugLocLists<W>,
refs: &mut Vec<DebugInfoFixup>,
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)?; w.write_u32(0)?;
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,
})
}
}
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct LocationList(pub Vec<Location>);
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum Location {
BaseAddress {
address: Address,
},
OffsetPair {
begin: u64,
end: u64,
data: Expression,
},
StartEnd {
begin: Address,
end: Address,
data: Expression,
},
StartLength {
begin: Address,
length: u64,
data: Expression,
},
DefaultLocation {
data: Expression,
},
}
fn write_expression<W: Writer>(
w: &mut W,
refs: &mut Vec<DebugInfoFixup>,
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::{ConvertDebugInfoRef, ConvertError, ConvertResult};
impl LocationList {
pub(crate) fn from<R: Reader<Offset = usize>>(
mut from: read::RawLocListIter<R>,
from_unit: read::UnitRef<'_, R>,
convert_address: &dyn Fn(u64) -> Option<Address>,
convert_debug_info_ref: &dyn ConvertDebugInfoRef,
) -> ConvertResult<Self> {
let convert_expression = |x| {
Expression::from(
x,
from_unit.encoding(),
Some(from_unit),
convert_address,
convert_debug_info_ref,
)
};
let convert_address = |x| convert_address(x).ok_or(ConvertError::InvalidAddress);
let mut have_base_address = from_unit.low_pc != 0;
let mut loc_list = Vec::new();
while let Some(from_loc) = from.next()? {
let loc = match from_loc {
read::RawLocListEntry::AddressOrOffsetPair { begin, end, data } => {
let begin = convert_address(begin)?;
let end = convert_address(end)?;
let data = convert_expression(data)?;
if have_base_address {
let (Address::Constant(begin_offset), Address::Constant(end_offset)) =
(begin, end)
else {
return Err(ConvertError::InvalidRangeRelativeAddress);
};
Location::OffsetPair {
begin: begin_offset,
end: end_offset,
data,
}
} else {
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(from_unit.address(addr)?)?;
Location::BaseAddress { address }
}
read::RawLocListEntry::StartxEndx { begin, end, data } => {
let begin = convert_address(from_unit.address(begin)?)?;
let end = convert_address(from_unit.address(end)?)?;
let data = convert_expression(data)?;
Location::StartEnd { begin, end, data }
}
read::RawLocListEntry::StartxLength {
begin,
length,
data,
} => {
let begin = convert_address(from_unit.address(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 }
}
};
match loc {
Location::StartLength { 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::LittleEndian;
use crate::common::{
DebugAbbrevOffset, DebugAddrBase, DebugLocListsBase, DebugRngListsBase,
DebugStrOffsetsBase, Format, UnitSectionOffset,
};
use crate::write::{AttributeValue, DwarfUnit, EndianVec, NoConvertDebugInfoRef};
use crate::{constants, read};
use alloc::sync::Arc;
#[test]
fn test_loc_list() {
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, false, None)
.unwrap();
assert!(sections.debug_loc_fixups.is_empty());
assert!(sections.debug_loclists_fixups.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),
SectionId::DebugInfo,
UnitSectionOffset(0),
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 convert_loc_list = LocationList::from(
read_loc_list,
unit.unit_ref(&dwarf),
&|address| Some(Address::Constant(address)),
&NoConvertDebugInfoRef,
)
.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);
}
}
}
}
#[test]
fn test_loc_base_address_v4() {
let encoding = Encoding {
format: Format::Dwarf32,
version: 4,
address_size: 8,
};
let location = [
Location::OffsetPair {
begin: 0x1234,
end: 0x2345,
data: Expression::new(),
},
Location::StartEnd {
begin: Address::Constant(0x1234),
end: Address::Constant(0x2345),
data: Expression::new(),
},
Location::StartLength {
begin: Address::Constant(0x1234),
length: 1,
data: Expression::new(),
},
];
for (l, low_pc, err) in [
(0, None, Err(Error::MissingBaseAddress)),
(0, Some(0), Err(Error::MissingBaseAddress)),
(0, Some(1), Ok(())),
(1, None, Ok(())),
(1, Some(0), Ok(())),
(1, Some(1), Err(Error::UnexpectedBaseAddress)),
(2, None, Ok(())),
(2, Some(0), Ok(())),
(2, Some(1), Err(Error::UnexpectedBaseAddress)),
] {
let mut dwarf = DwarfUnit::new(encoding);
let location = dwarf
.unit
.locations
.add(LocationList(vec![location[l].clone()]));
let root = dwarf.unit.get_mut(dwarf.unit.root());
if let Some(low_pc) = low_pc {
root.set(
constants::DW_AT_low_pc,
AttributeValue::Address(Address::Constant(low_pc)),
);
}
root.set(
constants::DW_AT_location,
AttributeValue::LocationListRef(location),
);
let mut sections = Sections::new(EndianVec::new(LittleEndian));
assert_eq!(dwarf.write(&mut sections), err);
}
}
}