use super::address_transform::AddressTransform;
use super::{DebugInputContext, Reader};
use anyhow::Error;
use gimli::{write, AttributeValue, DebuggingInformationEntry, RangeListsOffset, Unit};
use wasmtime_environ::{DefinedFuncIndex, EntityRef};
pub(crate) enum RangeInfoBuilder {
Undefined,
Position(u64),
Ranges(Vec<(u64, u64)>),
Function(DefinedFuncIndex),
}
impl RangeInfoBuilder {
pub(crate) fn from<R>(
dwarf: &gimli::Dwarf<R>,
unit: &Unit<R, R::Offset>,
entry: &DebuggingInformationEntry<R>,
context: &DebugInputContext<R>,
cu_low_pc: u64,
) -> Result<Self, Error>
where
R: Reader,
{
if let Some(AttributeValue::RangeListsRef(r)) = entry.attr_value(gimli::DW_AT_ranges)? {
let r = dwarf.ranges_offset_from_raw(unit, r);
return RangeInfoBuilder::from_ranges_ref(unit, r, context, cu_low_pc);
};
let low_pc =
if let Some(AttributeValue::Addr(addr)) = entry.attr_value(gimli::DW_AT_low_pc)? {
addr
} else if let Some(AttributeValue::DebugAddrIndex(i)) =
entry.attr_value(gimli::DW_AT_low_pc)?
{
context.debug_addr.get_address(4, unit.addr_base, i)?
} else {
return Ok(RangeInfoBuilder::Undefined);
};
Ok(
if let Some(AttributeValue::Udata(u)) = entry.attr_value(gimli::DW_AT_high_pc)? {
RangeInfoBuilder::Ranges(vec![(low_pc, low_pc + u)])
} else {
RangeInfoBuilder::Position(low_pc)
},
)
}
pub(crate) fn from_ranges_ref<R>(
unit: &Unit<R, R::Offset>,
ranges: RangeListsOffset,
context: &DebugInputContext<R>,
cu_low_pc: u64,
) -> Result<Self, Error>
where
R: Reader,
{
let unit_encoding = unit.encoding();
let mut ranges = context.rnglists.ranges(
ranges,
unit_encoding,
cu_low_pc,
&context.debug_addr,
unit.addr_base,
)?;
let mut result = Vec::new();
while let Some(range) = ranges.next()? {
if range.begin >= range.end {
}
result.push((range.begin, range.end));
}
Ok(if result.is_empty() {
RangeInfoBuilder::Undefined
} else {
RangeInfoBuilder::Ranges(result)
})
}
pub(crate) fn from_subprogram_die<R>(
dwarf: &gimli::Dwarf<R>,
unit: &Unit<R, R::Offset>,
entry: &DebuggingInformationEntry<R>,
context: &DebugInputContext<R>,
addr_tr: &AddressTransform,
cu_low_pc: u64,
) -> Result<Self, Error>
where
R: Reader,
{
let unit_encoding = unit.encoding();
let addr =
if let Some(AttributeValue::Addr(addr)) = entry.attr_value(gimli::DW_AT_low_pc)? {
addr
} else if let Some(AttributeValue::DebugAddrIndex(i)) =
entry.attr_value(gimli::DW_AT_low_pc)?
{
context.debug_addr.get_address(4, unit.addr_base, i)?
} else if let Some(AttributeValue::RangeListsRef(r)) =
entry.attr_value(gimli::DW_AT_ranges)?
{
let r = dwarf.ranges_offset_from_raw(unit, r);
let mut ranges = context.rnglists.ranges(
r,
unit_encoding,
cu_low_pc,
&context.debug_addr,
unit.addr_base,
)?;
if let Some(range) = ranges.next()? {
range.begin
} else {
return Ok(RangeInfoBuilder::Undefined);
}
} else {
return Ok(RangeInfoBuilder::Undefined);
};
let index = addr_tr.find_func_index(addr);
if index.is_none() {
return Ok(RangeInfoBuilder::Undefined);
}
Ok(RangeInfoBuilder::Function(index.unwrap()))
}
pub(crate) fn build(
&self,
addr_tr: &AddressTransform,
out_unit: &mut write::Unit,
current_scope_id: write::UnitEntryId,
) {
match self {
RangeInfoBuilder::Undefined => (),
RangeInfoBuilder::Position(pc) => {
let addr = addr_tr
.translate(*pc)
.unwrap_or(write::Address::Constant(0));
let current_scope = out_unit.get_mut(current_scope_id);
current_scope.set(gimli::DW_AT_low_pc, write::AttributeValue::Address(addr));
}
RangeInfoBuilder::Ranges(ranges) => {
let mut result = Vec::new();
for (begin, end) in ranges {
result.extend(addr_tr.translate_ranges(*begin, *end));
}
if result.len() != 1 {
let range_list = result
.iter()
.map(|tr| write::Range::StartLength {
begin: tr.0,
length: tr.1,
})
.collect::<Vec<_>>();
let range_list_id = out_unit.ranges.add(write::RangeList(range_list));
let current_scope = out_unit.get_mut(current_scope_id);
current_scope.set(
gimli::DW_AT_ranges,
write::AttributeValue::RangeListRef(range_list_id),
);
} else {
let current_scope = out_unit.get_mut(current_scope_id);
current_scope.set(
gimli::DW_AT_low_pc,
write::AttributeValue::Address(result[0].0),
);
current_scope.set(
gimli::DW_AT_high_pc,
write::AttributeValue::Udata(result[0].1),
);
}
}
RangeInfoBuilder::Function(index) => {
let range = addr_tr.func_range(*index);
let symbol = index.index();
let addr = write::Address::Symbol {
symbol,
addend: range.0 as i64,
};
let len = (range.1 - range.0) as u64;
let current_scope = out_unit.get_mut(current_scope_id);
current_scope.set(gimli::DW_AT_low_pc, write::AttributeValue::Address(addr));
current_scope.set(gimli::DW_AT_high_pc, write::AttributeValue::Udata(len));
}
}
}
pub(crate) fn get_ranges(&self, addr_tr: &AddressTransform) -> Vec<(u64, u64)> {
match self {
RangeInfoBuilder::Undefined | RangeInfoBuilder::Position(_) => vec![],
RangeInfoBuilder::Ranges(ranges) => ranges.clone(),
RangeInfoBuilder::Function(index) => {
let range = addr_tr.func_source_range(*index);
vec![(range.0, range.1)]
}
}
}
pub(crate) fn build_ranges(
&self,
addr_tr: &AddressTransform,
out_range_lists: &mut write::RangeListTable,
) -> write::RangeListId {
if let RangeInfoBuilder::Ranges(ranges) = self {
let mut range_list = Vec::new();
for (begin, end) in ranges {
assert!(begin < end);
range_list.extend(addr_tr.translate_ranges(*begin, *end).map(|tr| {
write::Range::StartLength {
begin: tr.0,
length: tr.1,
}
}));
}
out_range_lists.add(write::RangeList(range_list))
} else {
unreachable!();
}
}
}