support a few DWARF-5 only features (#1410)
Support a few DWARF-5 only features: * read .debug_addr * read .debug_rnglists * read .debug_loclists when present * add dwarf-5 test * read .debug_addr * read .debug_rnglists * read .debug_loclists when present * support .debug_line_str and .debug_str_offsets Co-authored-by: Yury Delendik <ydelendik@mozilla.com>
This commit is contained in:
@@ -170,6 +170,9 @@ fn has_valid_code_range<R: Reader<Offset = usize>>(
|
|||||||
} else if let Some(low_pc) = die.attr_value(constants::DW_AT_low_pc)? {
|
} else if let Some(low_pc) = die.attr_value(constants::DW_AT_low_pc)? {
|
||||||
if let read::AttributeValue::Addr(a) = low_pc {
|
if let read::AttributeValue::Addr(a) = low_pc {
|
||||||
return Ok(at.can_translate_address(a));
|
return Ok(at.can_translate_address(a));
|
||||||
|
} else if let read::AttributeValue::DebugAddrIndex(i) = low_pc {
|
||||||
|
let a = dwarf.debug_addr.get_address(4, unit.addr_base, i)?;
|
||||||
|
return Ok(at.can_translate_address(a));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,47 +61,41 @@ fn convert_sections<'a>(sections: HashMap<&str, &'a [u8]>) -> Result<Dwarf<'a>>
|
|||||||
sections.get(".debug_line").unwrap_or(&EMPTY_SECTION),
|
sections.get(".debug_line").unwrap_or(&EMPTY_SECTION),
|
||||||
endian,
|
endian,
|
||||||
);
|
);
|
||||||
|
let debug_addr = DebugAddr::from(EndianSlice::new(
|
||||||
|
sections.get(".debug_addr").unwrap_or(&EMPTY_SECTION),
|
||||||
|
endian,
|
||||||
|
));
|
||||||
|
|
||||||
if sections.contains_key(".debug_addr") {
|
let debug_line_str = DebugLineStr::from(EndianSlice::new(
|
||||||
bail!("Unexpected .debug_addr");
|
sections.get(".debug_line_str").unwrap_or(&EMPTY_SECTION),
|
||||||
}
|
endian,
|
||||||
|
));
|
||||||
let debug_addr = DebugAddr::from(EndianSlice::new(EMPTY_SECTION, endian));
|
|
||||||
|
|
||||||
if sections.contains_key(".debug_line_str") {
|
|
||||||
bail!("Unexpected .debug_line_str");
|
|
||||||
}
|
|
||||||
|
|
||||||
let debug_line_str = DebugLineStr::from(EndianSlice::new(EMPTY_SECTION, endian));
|
|
||||||
let debug_str_sup = DebugStr::from(EndianSlice::new(EMPTY_SECTION, endian));
|
let debug_str_sup = DebugStr::from(EndianSlice::new(EMPTY_SECTION, endian));
|
||||||
|
|
||||||
if sections.contains_key(".debug_rnglists") {
|
|
||||||
bail!("Unexpected .debug_rnglists");
|
|
||||||
}
|
|
||||||
|
|
||||||
let debug_ranges = match sections.get(".debug_ranges") {
|
let debug_ranges = match sections.get(".debug_ranges") {
|
||||||
Some(section) => DebugRanges::new(section, endian),
|
Some(section) => DebugRanges::new(section, endian),
|
||||||
None => DebugRanges::new(EMPTY_SECTION, endian),
|
None => DebugRanges::new(EMPTY_SECTION, endian),
|
||||||
};
|
};
|
||||||
let debug_rnglists = DebugRngLists::new(EMPTY_SECTION, endian);
|
let debug_rnglists = match sections.get(".debug_rnglists") {
|
||||||
|
Some(section) => DebugRngLists::new(section, endian),
|
||||||
|
None => DebugRngLists::new(EMPTY_SECTION, endian),
|
||||||
|
};
|
||||||
let ranges = RangeLists::new(debug_ranges, debug_rnglists);
|
let ranges = RangeLists::new(debug_ranges, debug_rnglists);
|
||||||
|
|
||||||
if sections.contains_key(".debug_loclists") {
|
|
||||||
bail!("Unexpected .debug_loclists");
|
|
||||||
}
|
|
||||||
|
|
||||||
let debug_loc = match sections.get(".debug_loc") {
|
let debug_loc = match sections.get(".debug_loc") {
|
||||||
Some(section) => DebugLoc::new(section, endian),
|
Some(section) => DebugLoc::new(section, endian),
|
||||||
None => DebugLoc::new(EMPTY_SECTION, endian),
|
None => DebugLoc::new(EMPTY_SECTION, endian),
|
||||||
};
|
};
|
||||||
let debug_loclists = DebugLocLists::new(EMPTY_SECTION, endian);
|
let debug_loclists = match sections.get(".debug_loclists") {
|
||||||
|
Some(section) => DebugLocLists::new(section, endian),
|
||||||
|
None => DebugLocLists::new(EMPTY_SECTION, endian),
|
||||||
|
};
|
||||||
let locations = LocationLists::new(debug_loc, debug_loclists);
|
let locations = LocationLists::new(debug_loc, debug_loclists);
|
||||||
|
|
||||||
if sections.contains_key(".debug_str_offsets") {
|
let debug_str_offsets = DebugStrOffsets::from(EndianSlice::new(
|
||||||
bail!("Unexpected .debug_str_offsets");
|
sections.get(".debug_str_offsets").unwrap_or(&EMPTY_SECTION),
|
||||||
}
|
endian,
|
||||||
|
));
|
||||||
let debug_str_offsets = DebugStrOffsets::from(EndianSlice::new(EMPTY_SECTION, endian));
|
|
||||||
|
|
||||||
if sections.contains_key(".debug_types") {
|
if sections.contains_key(".debug_types") {
|
||||||
bail!("Unexpected .debug_types");
|
bail!("Unexpected .debug_types");
|
||||||
|
|||||||
@@ -4,12 +4,20 @@ use super::range_info_builder::RangeInfoBuilder;
|
|||||||
use super::refs::{PendingDebugInfoRefs, PendingUnitRefs};
|
use super::refs::{PendingDebugInfoRefs, PendingUnitRefs};
|
||||||
use super::{DebugInputContext, Reader, TransformError};
|
use super::{DebugInputContext, Reader, TransformError};
|
||||||
use anyhow::{bail, Error};
|
use anyhow::{bail, Error};
|
||||||
use gimli::{write, AttributeValue, DebugLineOffset, DebugStr, DebuggingInformationEntry};
|
use gimli::{
|
||||||
|
write, AttributeValue, DebugLineOffset, DebugLineStr, DebugStr, DebugStrOffsets,
|
||||||
|
DebuggingInformationEntry, Unit,
|
||||||
|
};
|
||||||
use wasmtime_environ::isa::TargetIsa;
|
use wasmtime_environ::isa::TargetIsa;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub(crate) enum FileAttributeContext<'a> {
|
pub(crate) enum FileAttributeContext<'a> {
|
||||||
Root(Option<DebugLineOffset>),
|
Root(Option<DebugLineOffset>),
|
||||||
Children(&'a Vec<write::FileId>, Option<&'a CompiledExpression<'a>>),
|
Children(
|
||||||
|
&'a Vec<write::FileId>,
|
||||||
|
u64,
|
||||||
|
Option<&'a CompiledExpression<'a>>,
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_exprloc_to_loclist_allowed(attr_name: gimli::constants::DwAt) -> bool {
|
fn is_exprloc_to_loclist_allowed(attr_name: gimli::constants::DwAt) -> bool {
|
||||||
@@ -28,11 +36,11 @@ fn is_exprloc_to_loclist_allowed(attr_name: gimli::constants::DwAt) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn clone_die_attributes<'a, R>(
|
pub(crate) fn clone_die_attributes<'a, R>(
|
||||||
|
unit: &Unit<R, R::Offset>,
|
||||||
entry: &DebuggingInformationEntry<R>,
|
entry: &DebuggingInformationEntry<R>,
|
||||||
context: &DebugInputContext<R>,
|
context: &DebugInputContext<R>,
|
||||||
addr_tr: &'a AddressTransform,
|
addr_tr: &'a AddressTransform,
|
||||||
frame_info: Option<&FunctionFrameInfo>,
|
frame_info: Option<&FunctionFrameInfo>,
|
||||||
unit_encoding: gimli::Encoding,
|
|
||||||
out_unit: &mut write::Unit,
|
out_unit: &mut write::Unit,
|
||||||
current_scope_id: write::UnitEntryId,
|
current_scope_id: write::UnitEntryId,
|
||||||
subprogram_range_builder: Option<RangeInfoBuilder>,
|
subprogram_range_builder: Option<RangeInfoBuilder>,
|
||||||
@@ -49,23 +57,24 @@ where
|
|||||||
{
|
{
|
||||||
let _tag = &entry.tag();
|
let _tag = &entry.tag();
|
||||||
let endian = gimli::RunTimeEndian::Little;
|
let endian = gimli::RunTimeEndian::Little;
|
||||||
|
let unit_encoding = unit.encoding();
|
||||||
|
|
||||||
let range_info = if let Some(subprogram_range_builder) = subprogram_range_builder {
|
let range_info = if let Some(subprogram_range_builder) = subprogram_range_builder {
|
||||||
subprogram_range_builder
|
subprogram_range_builder
|
||||||
} else if entry.tag() == gimli::DW_TAG_compile_unit {
|
|
||||||
// FIXME currently address_transform operate on a single func range,
|
|
||||||
// once it is fixed we can properly set DW_AT_ranges attribute.
|
|
||||||
// Using for now DW_AT_low_pc = 0.
|
|
||||||
RangeInfoBuilder::Position(0)
|
|
||||||
} else {
|
} else {
|
||||||
RangeInfoBuilder::from(entry, context, unit_encoding, cu_low_pc)?
|
// FIXME for CU: currently address_transform operate on a single
|
||||||
|
// function range, and when CU spans multiple ranges the
|
||||||
|
// transformation may be incomplete.
|
||||||
|
RangeInfoBuilder::from(unit, entry, context, cu_low_pc)?
|
||||||
};
|
};
|
||||||
range_info.build(addr_tr, out_unit, current_scope_id);
|
range_info.build(addr_tr, out_unit, current_scope_id);
|
||||||
|
|
||||||
let mut attrs = entry.attrs();
|
let mut attrs = entry.attrs();
|
||||||
while let Some(attr) = attrs.next()? {
|
while let Some(attr) = attrs.next()? {
|
||||||
let attr_value = match attr.value() {
|
let attr_value = match attr.value() {
|
||||||
AttributeValue::Addr(_) if attr.name() == gimli::DW_AT_low_pc => {
|
AttributeValue::Addr(_) | AttributeValue::DebugAddrIndex(_)
|
||||||
|
if attr.name() == gimli::DW_AT_low_pc =>
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
AttributeValue::Udata(_) if attr.name() == gimli::DW_AT_high_pc => {
|
AttributeValue::Udata(_) if attr.name() == gimli::DW_AT_high_pc => {
|
||||||
@@ -77,11 +86,19 @@ where
|
|||||||
AttributeValue::Exprloc(_) if attr.name() == gimli::DW_AT_frame_base => {
|
AttributeValue::Exprloc(_) if attr.name() == gimli::DW_AT_frame_base => {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
AttributeValue::DebugAddrBase(_) | AttributeValue::DebugStrOffsetsBase(_) => {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
AttributeValue::Addr(u) => {
|
AttributeValue::Addr(u) => {
|
||||||
let addr = addr_tr.translate(u).unwrap_or(write::Address::Constant(0));
|
let addr = addr_tr.translate(u).unwrap_or(write::Address::Constant(0));
|
||||||
write::AttributeValue::Address(addr)
|
write::AttributeValue::Address(addr)
|
||||||
}
|
}
|
||||||
|
AttributeValue::DebugAddrIndex(i) => {
|
||||||
|
let u = context.debug_addr.get_address(4, unit.addr_base, i)?;
|
||||||
|
let addr = addr_tr.translate(u).unwrap_or(write::Address::Constant(0));
|
||||||
|
write::AttributeValue::Address(addr)
|
||||||
|
}
|
||||||
AttributeValue::Udata(u) => write::AttributeValue::Udata(u),
|
AttributeValue::Udata(u) => write::AttributeValue::Udata(u),
|
||||||
AttributeValue::Data1(d) => write::AttributeValue::Data1(d),
|
AttributeValue::Data1(d) => write::AttributeValue::Data1(d),
|
||||||
AttributeValue::Data2(d) => write::AttributeValue::Data2(d),
|
AttributeValue::Data2(d) => write::AttributeValue::Data2(d),
|
||||||
@@ -99,8 +116,8 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
AttributeValue::FileIndex(i) => {
|
AttributeValue::FileIndex(i) => {
|
||||||
if let FileAttributeContext::Children(file_map, _) = file_context {
|
if let FileAttributeContext::Children(file_map, file_index_base, _) = file_context {
|
||||||
write::AttributeValue::FileIndex(Some(file_map[(i - 1) as usize]))
|
write::AttributeValue::FileIndex(Some(file_map[(i - file_index_base) as usize]))
|
||||||
} else {
|
} else {
|
||||||
return Err(TransformError("unexpected file index attribute").into());
|
return Err(TransformError("unexpected file index attribute").into());
|
||||||
}
|
}
|
||||||
@@ -109,9 +126,17 @@ where
|
|||||||
let s = context.debug_str.get_str(str_offset)?.to_slice()?.to_vec();
|
let s = context.debug_str.get_str(str_offset)?.to_slice()?.to_vec();
|
||||||
write::AttributeValue::StringRef(out_strings.add(s))
|
write::AttributeValue::StringRef(out_strings.add(s))
|
||||||
}
|
}
|
||||||
|
AttributeValue::DebugStrOffsetsIndex(i) => {
|
||||||
|
let str_offset = context.debug_str_offsets.get_str_offset(
|
||||||
|
gimli::Format::Dwarf32,
|
||||||
|
unit.str_offsets_base,
|
||||||
|
i,
|
||||||
|
)?;
|
||||||
|
let s = context.debug_str.get_str(str_offset)?.to_slice()?.to_vec();
|
||||||
|
write::AttributeValue::StringRef(out_strings.add(s))
|
||||||
|
}
|
||||||
AttributeValue::RangeListsRef(r) => {
|
AttributeValue::RangeListsRef(r) => {
|
||||||
let range_info =
|
let range_info = RangeInfoBuilder::from_ranges_ref(unit, r, context, cu_low_pc)?;
|
||||||
RangeInfoBuilder::from_ranges_ref(r, context, unit_encoding, cu_low_pc)?;
|
|
||||||
let range_list_id = range_info.build_ranges(addr_tr, &mut out_unit.ranges);
|
let range_list_id = range_info.build_ranges(addr_tr, &mut out_unit.ranges);
|
||||||
write::AttributeValue::RangeListRef(range_list_id)
|
write::AttributeValue::RangeListRef(range_list_id)
|
||||||
}
|
}
|
||||||
@@ -122,14 +147,14 @@ where
|
|||||||
unit_encoding,
|
unit_encoding,
|
||||||
low_pc,
|
low_pc,
|
||||||
&context.debug_addr,
|
&context.debug_addr,
|
||||||
context.debug_addr_base,
|
unit.addr_base,
|
||||||
)?;
|
)?;
|
||||||
let frame_base = if let FileAttributeContext::Children(_, frame_base) = file_context
|
let frame_base =
|
||||||
{
|
if let FileAttributeContext::Children(_, _, frame_base) = file_context {
|
||||||
frame_base
|
frame_base
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
let mut result = None;
|
let mut result = None;
|
||||||
while let Some(loc) = locs.next()? {
|
while let Some(loc) = locs.next()? {
|
||||||
if let Some(expr) =
|
if let Some(expr) =
|
||||||
@@ -166,12 +191,12 @@ where
|
|||||||
write::AttributeValue::LocationListRef(list_id)
|
write::AttributeValue::LocationListRef(list_id)
|
||||||
}
|
}
|
||||||
AttributeValue::Exprloc(ref expr) => {
|
AttributeValue::Exprloc(ref expr) => {
|
||||||
let frame_base = if let FileAttributeContext::Children(_, frame_base) = file_context
|
let frame_base =
|
||||||
{
|
if let FileAttributeContext::Children(_, _, frame_base) = file_context {
|
||||||
frame_base
|
frame_base
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
if let Some(expr) = compile_expression(expr, unit_encoding, frame_base, isa)? {
|
if let Some(expr) = compile_expression(expr, unit_encoding, frame_base, isa)? {
|
||||||
if expr.is_simple() {
|
if expr.is_simple() {
|
||||||
if let Some(expr) = expr.build() {
|
if let Some(expr) = expr.build() {
|
||||||
@@ -263,7 +288,10 @@ where
|
|||||||
pub(crate) fn clone_attr_string<R>(
|
pub(crate) fn clone_attr_string<R>(
|
||||||
attr_value: &AttributeValue<R>,
|
attr_value: &AttributeValue<R>,
|
||||||
form: gimli::DwForm,
|
form: gimli::DwForm,
|
||||||
|
unit: &Unit<R, R::Offset>,
|
||||||
debug_str: &DebugStr<R>,
|
debug_str: &DebugStr<R>,
|
||||||
|
debug_str_offsets: &DebugStrOffsets<R>,
|
||||||
|
debug_line_str: &DebugLineStr<R>,
|
||||||
out_strings: &mut write::StringTable,
|
out_strings: &mut write::StringTable,
|
||||||
) -> Result<write::LineString, Error>
|
) -> Result<write::LineString, Error>
|
||||||
where
|
where
|
||||||
@@ -273,6 +301,17 @@ where
|
|||||||
AttributeValue::DebugStrRef(str_offset) => {
|
AttributeValue::DebugStrRef(str_offset) => {
|
||||||
debug_str.get_str(*str_offset)?.to_slice()?.to_vec()
|
debug_str.get_str(*str_offset)?.to_slice()?.to_vec()
|
||||||
}
|
}
|
||||||
|
AttributeValue::DebugStrOffsetsIndex(i) => {
|
||||||
|
let str_offset = debug_str_offsets.get_str_offset(
|
||||||
|
gimli::Format::Dwarf32,
|
||||||
|
unit.str_offsets_base,
|
||||||
|
*i,
|
||||||
|
)?;
|
||||||
|
debug_str.get_str(str_offset)?.to_slice()?.to_vec()
|
||||||
|
}
|
||||||
|
AttributeValue::DebugLineStrRef(str_offset) => {
|
||||||
|
debug_line_str.get_str(*str_offset)?.to_slice()?.to_vec()
|
||||||
|
}
|
||||||
AttributeValue::String(b) => b.to_slice()?.to_vec(),
|
AttributeValue::String(b) => b.to_slice()?.to_vec(),
|
||||||
v => bail!("Unexpected attribute value: {:?}", v),
|
v => bail!("Unexpected attribute value: {:?}", v),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ use super::attr::clone_attr_string;
|
|||||||
use super::{Reader, TransformError};
|
use super::{Reader, TransformError};
|
||||||
use anyhow::{Context, Error};
|
use anyhow::{Context, Error};
|
||||||
use gimli::{
|
use gimli::{
|
||||||
write, DebugLine, DebugLineOffset, DebugStr, DebuggingInformationEntry, LineEncoding, Unit,
|
write, DebugLine, DebugLineOffset, DebugLineStr, DebugStr, DebugStrOffsets,
|
||||||
|
DebuggingInformationEntry, LineEncoding, Unit,
|
||||||
};
|
};
|
||||||
use more_asserts::assert_le;
|
use more_asserts::assert_le;
|
||||||
use wasmtime_environ::entity::EntityRef;
|
use wasmtime_environ::entity::EntityRef;
|
||||||
@@ -46,9 +47,11 @@ pub(crate) fn clone_line_program<R>(
|
|||||||
addr_tr: &AddressTransform,
|
addr_tr: &AddressTransform,
|
||||||
out_encoding: gimli::Encoding,
|
out_encoding: gimli::Encoding,
|
||||||
debug_str: &DebugStr<R>,
|
debug_str: &DebugStr<R>,
|
||||||
|
debug_str_offsets: &DebugStrOffsets<R>,
|
||||||
|
debug_line_str: &DebugLineStr<R>,
|
||||||
debug_line: &DebugLine<R>,
|
debug_line: &DebugLine<R>,
|
||||||
out_strings: &mut write::StringTable,
|
out_strings: &mut write::StringTable,
|
||||||
) -> Result<(write::LineProgram, DebugLineOffset, Vec<write::FileId>), Error>
|
) -> Result<(write::LineProgram, DebugLineOffset, Vec<write::FileId>, u64), Error>
|
||||||
where
|
where
|
||||||
R: Reader,
|
R: Reader,
|
||||||
{
|
{
|
||||||
@@ -63,13 +66,19 @@ where
|
|||||||
let out_comp_dir = clone_attr_string(
|
let out_comp_dir = clone_attr_string(
|
||||||
comp_dir.as_ref().context("comp_dir")?,
|
comp_dir.as_ref().context("comp_dir")?,
|
||||||
gimli::DW_FORM_strp,
|
gimli::DW_FORM_strp,
|
||||||
|
unit,
|
||||||
debug_str,
|
debug_str,
|
||||||
|
debug_str_offsets,
|
||||||
|
debug_line_str,
|
||||||
out_strings,
|
out_strings,
|
||||||
)?;
|
)?;
|
||||||
let out_comp_name = clone_attr_string(
|
let out_comp_name = clone_attr_string(
|
||||||
comp_name.as_ref().context("comp_name")?,
|
comp_name.as_ref().context("comp_name")?,
|
||||||
gimli::DW_FORM_strp,
|
gimli::DW_FORM_strp,
|
||||||
|
unit,
|
||||||
debug_str,
|
debug_str,
|
||||||
|
debug_str_offsets,
|
||||||
|
debug_line_str,
|
||||||
out_strings,
|
out_strings,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@@ -81,7 +90,8 @@ where
|
|||||||
);
|
);
|
||||||
if let Ok(program) = program {
|
if let Ok(program) = program {
|
||||||
let header = program.header();
|
let header = program.header();
|
||||||
assert_le!(header.version(), 4, "not supported 5");
|
let file_index_base = if header.version() < 5 { 1 } else { 0 };
|
||||||
|
assert_le!(header.version(), 5, "not supported 6");
|
||||||
let line_encoding = LineEncoding {
|
let line_encoding = LineEncoding {
|
||||||
minimum_instruction_length: header.minimum_instruction_length(),
|
minimum_instruction_length: header.minimum_instruction_length(),
|
||||||
maximum_operations_per_instruction: header.maximum_operations_per_instruction(),
|
maximum_operations_per_instruction: header.maximum_operations_per_instruction(),
|
||||||
@@ -102,7 +112,10 @@ where
|
|||||||
let dir_id = out_program.add_directory(clone_attr_string(
|
let dir_id = out_program.add_directory(clone_attr_string(
|
||||||
dir_attr,
|
dir_attr,
|
||||||
gimli::DW_FORM_string,
|
gimli::DW_FORM_string,
|
||||||
|
unit,
|
||||||
debug_str,
|
debug_str,
|
||||||
|
debug_str_offsets,
|
||||||
|
debug_line_str,
|
||||||
out_strings,
|
out_strings,
|
||||||
)?);
|
)?);
|
||||||
dirs.push(dir_id);
|
dirs.push(dir_id);
|
||||||
@@ -114,7 +127,10 @@ where
|
|||||||
clone_attr_string(
|
clone_attr_string(
|
||||||
&file_entry.path_name(),
|
&file_entry.path_name(),
|
||||||
gimli::DW_FORM_string,
|
gimli::DW_FORM_string,
|
||||||
|
unit,
|
||||||
debug_str,
|
debug_str,
|
||||||
|
debug_str_offsets,
|
||||||
|
debug_line_str,
|
||||||
out_strings,
|
out_strings,
|
||||||
)?,
|
)?,
|
||||||
dir_id,
|
dir_id,
|
||||||
@@ -238,7 +254,7 @@ where
|
|||||||
};
|
};
|
||||||
out_program.row().address_offset = address_offset;
|
out_program.row().address_offset = address_offset;
|
||||||
out_program.row().op_index = *op_index;
|
out_program.row().op_index = *op_index;
|
||||||
out_program.row().file = files[(file_index - 1) as usize];
|
out_program.row().file = files[(file_index - file_index_base) as usize];
|
||||||
out_program.row().line = *line;
|
out_program.row().line = *line;
|
||||||
out_program.row().column = *column;
|
out_program.row().column = *column;
|
||||||
out_program.row().discriminator = *discriminator;
|
out_program.row().discriminator = *discriminator;
|
||||||
@@ -255,7 +271,7 @@ where
|
|||||||
let end_addr = (map.offset + map.len - 1) as u64;
|
let end_addr = (map.offset + map.len - 1) as u64;
|
||||||
out_program.end_sequence(end_addr);
|
out_program.end_sequence(end_addr);
|
||||||
}
|
}
|
||||||
Ok((out_program, offset, files))
|
Ok((out_program, offset, files, file_index_base))
|
||||||
} else {
|
} else {
|
||||||
Err(TransformError("Valid line program not found").into())
|
Err(TransformError("Valid line program not found").into())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ use crate::gc::build_dependencies;
|
|||||||
use crate::DebugInfoData;
|
use crate::DebugInfoData;
|
||||||
use anyhow::Error;
|
use anyhow::Error;
|
||||||
use gimli::{
|
use gimli::{
|
||||||
write, DebugAddr, DebugAddrBase, DebugLine, DebugStr, LocationLists, RangeLists,
|
write, DebugAddr, DebugLine, DebugLineStr, DebugStr, DebugStrOffsets, LocationLists,
|
||||||
UnitSectionOffset,
|
RangeLists, UnitSectionOffset,
|
||||||
};
|
};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
@@ -38,9 +38,10 @@ where
|
|||||||
R: Reader,
|
R: Reader,
|
||||||
{
|
{
|
||||||
debug_str: &'a DebugStr<R>,
|
debug_str: &'a DebugStr<R>,
|
||||||
|
debug_str_offsets: &'a DebugStrOffsets<R>,
|
||||||
|
debug_line_str: &'a DebugLineStr<R>,
|
||||||
debug_line: &'a DebugLine<R>,
|
debug_line: &'a DebugLine<R>,
|
||||||
debug_addr: &'a DebugAddr<R>,
|
debug_addr: &'a DebugAddr<R>,
|
||||||
debug_addr_base: DebugAddrBase<R::Offset>,
|
|
||||||
rnglists: &'a RangeLists<R>,
|
rnglists: &'a RangeLists<R>,
|
||||||
loclists: &'a LocationLists<R>,
|
loclists: &'a LocationLists<R>,
|
||||||
reachable: &'a HashSet<UnitSectionOffset>,
|
reachable: &'a HashSet<UnitSectionOffset>,
|
||||||
@@ -58,9 +59,10 @@ pub fn transform_dwarf(
|
|||||||
|
|
||||||
let context = DebugInputContext {
|
let context = DebugInputContext {
|
||||||
debug_str: &di.dwarf.debug_str,
|
debug_str: &di.dwarf.debug_str,
|
||||||
|
debug_str_offsets: &di.dwarf.debug_str_offsets,
|
||||||
|
debug_line_str: &di.dwarf.debug_line_str,
|
||||||
debug_line: &di.dwarf.debug_line,
|
debug_line: &di.dwarf.debug_line,
|
||||||
debug_addr: &di.dwarf.debug_addr,
|
debug_addr: &di.dwarf.debug_addr,
|
||||||
debug_addr_base: DebugAddrBase(0),
|
|
||||||
rnglists: &di.dwarf.ranges,
|
rnglists: &di.dwarf.ranges,
|
||||||
loclists: &di.dwarf.locations,
|
loclists: &di.dwarf.locations,
|
||||||
reachable: &reachable,
|
reachable: &reachable,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use super::address_transform::AddressTransform;
|
use super::address_transform::AddressTransform;
|
||||||
use super::{DebugInputContext, Reader};
|
use super::{DebugInputContext, Reader};
|
||||||
use anyhow::Error;
|
use anyhow::Error;
|
||||||
use gimli::{write, AttributeValue, DebuggingInformationEntry, RangeListsOffset};
|
use gimli::{write, AttributeValue, DebuggingInformationEntry, RangeListsOffset, Unit};
|
||||||
use more_asserts::assert_lt;
|
use more_asserts::assert_lt;
|
||||||
use wasmtime_environ::entity::EntityRef;
|
use wasmtime_environ::entity::EntityRef;
|
||||||
use wasmtime_environ::wasm::DefinedFuncIndex;
|
use wasmtime_environ::wasm::DefinedFuncIndex;
|
||||||
@@ -15,21 +15,25 @@ pub(crate) enum RangeInfoBuilder {
|
|||||||
|
|
||||||
impl RangeInfoBuilder {
|
impl RangeInfoBuilder {
|
||||||
pub(crate) fn from<R>(
|
pub(crate) fn from<R>(
|
||||||
|
unit: &Unit<R, R::Offset>,
|
||||||
entry: &DebuggingInformationEntry<R>,
|
entry: &DebuggingInformationEntry<R>,
|
||||||
context: &DebugInputContext<R>,
|
context: &DebugInputContext<R>,
|
||||||
unit_encoding: gimli::Encoding,
|
|
||||||
cu_low_pc: u64,
|
cu_low_pc: u64,
|
||||||
) -> Result<Self, Error>
|
) -> Result<Self, Error>
|
||||||
where
|
where
|
||||||
R: Reader,
|
R: Reader,
|
||||||
{
|
{
|
||||||
if let Some(AttributeValue::RangeListsRef(r)) = entry.attr_value(gimli::DW_AT_ranges)? {
|
if let Some(AttributeValue::RangeListsRef(r)) = entry.attr_value(gimli::DW_AT_ranges)? {
|
||||||
return RangeInfoBuilder::from_ranges_ref(r, context, unit_encoding, cu_low_pc);
|
return RangeInfoBuilder::from_ranges_ref(unit, r, context, cu_low_pc);
|
||||||
};
|
};
|
||||||
|
|
||||||
let low_pc =
|
let low_pc =
|
||||||
if let Some(AttributeValue::Addr(addr)) = entry.attr_value(gimli::DW_AT_low_pc)? {
|
if let Some(AttributeValue::Addr(addr)) = entry.attr_value(gimli::DW_AT_low_pc)? {
|
||||||
addr
|
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 {
|
} else {
|
||||||
return Ok(RangeInfoBuilder::Undefined);
|
return Ok(RangeInfoBuilder::Undefined);
|
||||||
};
|
};
|
||||||
@@ -44,20 +48,21 @@ impl RangeInfoBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn from_ranges_ref<R>(
|
pub(crate) fn from_ranges_ref<R>(
|
||||||
|
unit: &Unit<R, R::Offset>,
|
||||||
ranges: RangeListsOffset,
|
ranges: RangeListsOffset,
|
||||||
context: &DebugInputContext<R>,
|
context: &DebugInputContext<R>,
|
||||||
unit_encoding: gimli::Encoding,
|
|
||||||
cu_low_pc: u64,
|
cu_low_pc: u64,
|
||||||
) -> Result<Self, Error>
|
) -> Result<Self, Error>
|
||||||
where
|
where
|
||||||
R: Reader,
|
R: Reader,
|
||||||
{
|
{
|
||||||
|
let unit_encoding = unit.encoding();
|
||||||
let mut ranges = context.rnglists.ranges(
|
let mut ranges = context.rnglists.ranges(
|
||||||
ranges,
|
ranges,
|
||||||
unit_encoding,
|
unit_encoding,
|
||||||
cu_low_pc,
|
cu_low_pc,
|
||||||
&context.debug_addr,
|
&context.debug_addr,
|
||||||
context.debug_addr_base,
|
unit.addr_base,
|
||||||
)?;
|
)?;
|
||||||
let mut result = Vec::new();
|
let mut result = Vec::new();
|
||||||
while let Some(range) = ranges.next()? {
|
while let Some(range) = ranges.next()? {
|
||||||
@@ -75,18 +80,23 @@ impl RangeInfoBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn from_subprogram_die<R>(
|
pub(crate) fn from_subprogram_die<R>(
|
||||||
|
unit: &Unit<R, R::Offset>,
|
||||||
entry: &DebuggingInformationEntry<R>,
|
entry: &DebuggingInformationEntry<R>,
|
||||||
context: &DebugInputContext<R>,
|
context: &DebugInputContext<R>,
|
||||||
unit_encoding: gimli::Encoding,
|
|
||||||
addr_tr: &AddressTransform,
|
addr_tr: &AddressTransform,
|
||||||
cu_low_pc: u64,
|
cu_low_pc: u64,
|
||||||
) -> Result<Self, Error>
|
) -> Result<Self, Error>
|
||||||
where
|
where
|
||||||
R: Reader,
|
R: Reader,
|
||||||
{
|
{
|
||||||
|
let unit_encoding = unit.encoding();
|
||||||
let addr =
|
let addr =
|
||||||
if let Some(AttributeValue::Addr(addr)) = entry.attr_value(gimli::DW_AT_low_pc)? {
|
if let Some(AttributeValue::Addr(addr)) = entry.attr_value(gimli::DW_AT_low_pc)? {
|
||||||
addr
|
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)) =
|
} else if let Some(AttributeValue::RangeListsRef(r)) =
|
||||||
entry.attr_value(gimli::DW_AT_ranges)?
|
entry.attr_value(gimli::DW_AT_ranges)?
|
||||||
{
|
{
|
||||||
@@ -95,7 +105,7 @@ impl RangeInfoBuilder {
|
|||||||
unit_encoding,
|
unit_encoding,
|
||||||
cu_low_pc,
|
cu_low_pc,
|
||||||
&context.debug_addr,
|
&context.debug_addr,
|
||||||
context.debug_addr_base,
|
unit.addr_base,
|
||||||
)?;
|
)?;
|
||||||
if let Some(range) = ranges.next()? {
|
if let Some(range) = ranges.next()? {
|
||||||
range.begin
|
range.begin
|
||||||
|
|||||||
@@ -246,18 +246,21 @@ where
|
|||||||
|
|
||||||
// Iterate over all of this compilation unit's entries.
|
// Iterate over all of this compilation unit's entries.
|
||||||
let mut entries = unit.entries();
|
let mut entries = unit.entries();
|
||||||
let (mut comp_unit, unit_id, file_map, cu_low_pc, wp_die_id, vmctx_die_id) =
|
let (mut comp_unit, unit_id, file_map, file_index_base, cu_low_pc, wp_die_id, vmctx_die_id) =
|
||||||
if let Some((depth_delta, entry)) = entries.next_dfs()? {
|
if let Some((depth_delta, entry)) = entries.next_dfs()? {
|
||||||
assert_eq!(depth_delta, 0);
|
assert_eq!(depth_delta, 0);
|
||||||
let (out_line_program, debug_line_offset, file_map) = clone_line_program(
|
let (out_line_program, debug_line_offset, file_map, file_index_base) =
|
||||||
&unit,
|
clone_line_program(
|
||||||
entry,
|
&unit,
|
||||||
addr_tr,
|
entry,
|
||||||
out_encoding,
|
addr_tr,
|
||||||
context.debug_str,
|
out_encoding,
|
||||||
context.debug_line,
|
context.debug_str,
|
||||||
out_strings,
|
context.debug_str_offsets,
|
||||||
)?;
|
context.debug_line_str,
|
||||||
|
context.debug_line,
|
||||||
|
out_strings,
|
||||||
|
)?;
|
||||||
|
|
||||||
if entry.tag() == gimli::DW_TAG_compile_unit {
|
if entry.tag() == gimli::DW_TAG_compile_unit {
|
||||||
let unit_id = out_units.add(write::Unit::new(out_encoding, out_line_program));
|
let unit_id = out_units.add(write::Unit::new(out_encoding, out_line_program));
|
||||||
@@ -270,17 +273,21 @@ where
|
|||||||
entry.attr_value(gimli::DW_AT_low_pc)?
|
entry.attr_value(gimli::DW_AT_low_pc)?
|
||||||
{
|
{
|
||||||
addr
|
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 {
|
} else {
|
||||||
// FIXME? return Err(TransformError("No low_pc for unit header").into());
|
// FIXME? return Err(TransformError("No low_pc for unit header").into());
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
|
||||||
clone_die_attributes(
|
clone_die_attributes(
|
||||||
|
&unit,
|
||||||
entry,
|
entry,
|
||||||
context,
|
context,
|
||||||
addr_tr,
|
addr_tr,
|
||||||
None,
|
None,
|
||||||
unit.encoding(),
|
|
||||||
comp_unit,
|
comp_unit,
|
||||||
root_id,
|
root_id,
|
||||||
None,
|
None,
|
||||||
@@ -301,6 +308,7 @@ where
|
|||||||
comp_unit,
|
comp_unit,
|
||||||
unit_id,
|
unit_id,
|
||||||
file_map,
|
file_map,
|
||||||
|
file_index_base,
|
||||||
cu_low_pc,
|
cu_low_pc,
|
||||||
wp_die_id,
|
wp_die_id,
|
||||||
vmctx_die_id,
|
vmctx_die_id,
|
||||||
@@ -342,13 +350,8 @@ where
|
|||||||
current_scope_ranges.update(new_stack_len);
|
current_scope_ranges.update(new_stack_len);
|
||||||
current_value_range.update(new_stack_len);
|
current_value_range.update(new_stack_len);
|
||||||
let range_builder = if entry.tag() == gimli::DW_TAG_subprogram {
|
let range_builder = if entry.tag() == gimli::DW_TAG_subprogram {
|
||||||
let range_builder = RangeInfoBuilder::from_subprogram_die(
|
let range_builder =
|
||||||
entry,
|
RangeInfoBuilder::from_subprogram_die(&unit, entry, context, addr_tr, cu_low_pc)?;
|
||||||
context,
|
|
||||||
unit.encoding(),
|
|
||||||
addr_tr,
|
|
||||||
cu_low_pc,
|
|
||||||
)?;
|
|
||||||
if let RangeInfoBuilder::Function(func_index) = range_builder {
|
if let RangeInfoBuilder::Function(func_index) = range_builder {
|
||||||
if let Some(frame_info) =
|
if let Some(frame_info) =
|
||||||
get_function_frame_info(module_info, func_index, value_ranges)
|
get_function_frame_info(module_info, func_index, value_ranges)
|
||||||
@@ -366,8 +369,7 @@ where
|
|||||||
let high_pc = entry.attr_value(gimli::DW_AT_high_pc)?;
|
let high_pc = entry.attr_value(gimli::DW_AT_high_pc)?;
|
||||||
let ranges = entry.attr_value(gimli::DW_AT_ranges)?;
|
let ranges = entry.attr_value(gimli::DW_AT_ranges)?;
|
||||||
if high_pc.is_some() || ranges.is_some() {
|
if high_pc.is_some() || ranges.is_some() {
|
||||||
let range_builder =
|
let range_builder = RangeInfoBuilder::from(&unit, entry, context, cu_low_pc)?;
|
||||||
RangeInfoBuilder::from(entry, context, unit.encoding(), cu_low_pc)?;
|
|
||||||
current_scope_ranges.push(new_stack_len, range_builder.get_ranges(addr_tr));
|
current_scope_ranges.push(new_stack_len, range_builder.get_ranges(addr_tr));
|
||||||
Some(range_builder)
|
Some(range_builder)
|
||||||
} else {
|
} else {
|
||||||
@@ -417,11 +419,11 @@ where
|
|||||||
die_ref_map.insert(entry.offset(), die_id);
|
die_ref_map.insert(entry.offset(), die_id);
|
||||||
|
|
||||||
clone_die_attributes(
|
clone_die_attributes(
|
||||||
|
&unit,
|
||||||
entry,
|
entry,
|
||||||
context,
|
context,
|
||||||
addr_tr,
|
addr_tr,
|
||||||
current_value_range.top(),
|
current_value_range.top(),
|
||||||
unit.encoding(),
|
|
||||||
&mut comp_unit,
|
&mut comp_unit,
|
||||||
die_id,
|
die_id,
|
||||||
range_builder,
|
range_builder,
|
||||||
@@ -430,7 +432,7 @@ where
|
|||||||
out_strings,
|
out_strings,
|
||||||
&mut pending_die_refs,
|
&mut pending_die_refs,
|
||||||
&mut pending_di_refs,
|
&mut pending_di_refs,
|
||||||
FileAttributeContext::Children(&file_map, current_frame_base.top()),
|
FileAttributeContext::Children(&file_map, file_index_base, current_frame_base.top()),
|
||||||
isa,
|
isa,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
|||||||
BIN
tests/all/debug/testsuite/fib-wasm-dwarf5.wasm
Executable file
BIN
tests/all/debug/testsuite/fib-wasm-dwarf5.wasm
Executable file
Binary file not shown.
@@ -1,10 +1,13 @@
|
|||||||
// Compile with:
|
// Compile with:
|
||||||
// clang --target=wasm32 fib-wasm.c -o fib-wasm.wasm -g \
|
// clang --target=wasm32 fib-wasm.c -o fib-wasm.wasm -gdwarf-4 \
|
||||||
|
// -Wl,--no-entry,--export=fib -nostdlib -fdebug-prefix-map=$PWD=.
|
||||||
|
//
|
||||||
|
// clang --target=wasm32 fib-wasm.c -o fib-wasm-dwarf5.wasm -gdwarf-5 \
|
||||||
// -Wl,--no-entry,--export=fib -nostdlib -fdebug-prefix-map=$PWD=.
|
// -Wl,--no-entry,--export=fib -nostdlib -fdebug-prefix-map=$PWD=.
|
||||||
|
|
||||||
int fib(int n) {
|
int fib(int n) {
|
||||||
int i, t, a = 0, b = 1;
|
int t, a = 0, b = 1;
|
||||||
for (i = 0; i < n; i++) {
|
for (int i = 0; i < n; i++) {
|
||||||
t = a;
|
t = a;
|
||||||
a = b;
|
a = b;
|
||||||
b += t;
|
b += t;
|
||||||
|
|||||||
Binary file not shown.
@@ -41,17 +41,51 @@ check: DW_AT_name ("fib")
|
|||||||
# Accepts one parameter
|
# Accepts one parameter
|
||||||
check: DW_TAG_formal_parameter
|
check: DW_TAG_formal_parameter
|
||||||
check: DW_AT_name ("n")
|
check: DW_AT_name ("n")
|
||||||
check: DW_AT_decl_line (5)
|
check: DW_AT_decl_line (8)
|
||||||
# Has four locals: i, t, a, b
|
# Has four locals: t, a, b, i
|
||||||
check: DW_TAG_variable
|
|
||||||
check: DW_AT_name ("i")
|
|
||||||
check: DW_AT_decl_line (6)
|
|
||||||
check: DW_TAG_variable
|
check: DW_TAG_variable
|
||||||
check: DW_AT_name ("t")
|
check: DW_AT_name ("t")
|
||||||
|
check: DW_AT_decl_line (9)
|
||||||
check: DW_TAG_variable
|
check: DW_TAG_variable
|
||||||
check: DW_AT_name ("a")
|
check: DW_AT_name ("a")
|
||||||
check: DW_TAG_variable
|
check: DW_TAG_variable
|
||||||
check: DW_AT_name ("b")
|
check: DW_AT_name ("b")
|
||||||
|
check: DW_TAG_variable
|
||||||
|
check: DW_AT_name ("i")
|
||||||
|
check: DW_AT_decl_line (10)
|
||||||
|
"##,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
#[cfg(all(
|
||||||
|
any(target_os = "linux", target_os = "macos"),
|
||||||
|
target_pointer_width = "64"
|
||||||
|
))]
|
||||||
|
fn test_debug_dwarf5_translate() -> Result<()> {
|
||||||
|
check_wasm(
|
||||||
|
"tests/debug/testsuite/fib-wasm-dwarf5.wasm",
|
||||||
|
r##"
|
||||||
|
check: DW_TAG_compile_unit
|
||||||
|
# We have "fib" function
|
||||||
|
check: DW_TAG_subprogram
|
||||||
|
check: DW_AT_name ("fib")
|
||||||
|
# Accepts one parameter
|
||||||
|
check: DW_TAG_formal_parameter
|
||||||
|
check: DW_AT_name ("n")
|
||||||
|
check: DW_AT_decl_line (8)
|
||||||
|
# Has four locals: t, a, b, i
|
||||||
|
check: DW_TAG_variable
|
||||||
|
check: DW_AT_name ("t")
|
||||||
|
check: DW_AT_decl_line (9)
|
||||||
|
check: DW_TAG_variable
|
||||||
|
check: DW_AT_name ("a")
|
||||||
|
check: DW_TAG_variable
|
||||||
|
check: DW_AT_name ("b")
|
||||||
|
check: DW_TAG_variable
|
||||||
|
check: DW_AT_name ("i")
|
||||||
|
check: DW_AT_decl_line (10)
|
||||||
"##,
|
"##,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
tests/debug/testsuite/fib-wasm-dwarf5.wasm
Executable file
BIN
tests/debug/testsuite/fib-wasm-dwarf5.wasm
Executable file
Binary file not shown.
Reference in New Issue
Block a user