Path: blob/main/crates/cranelift/src/debug/transform/unit.rs
3067 views
use super::address_transform::AddressTransform;1use super::attr::{EntryAttributesContext, clone_die_attributes};2use super::debug_transform_logging::{3dbi_log, log_begin_input_die, log_end_output_die, log_end_output_die_skipped,4log_get_cu_summary,5};6use super::expression::compile_expression;7use super::line_program::clone_line_program;8use super::range_info_builder::RangeInfoBuilder;9use super::synthetic::ModuleSyntheticUnit;10use super::utils::{append_vmctx_info, resolve_die_ref};11use crate::debug::{Compilation, Reader};12use cranelift_codegen::ir::Endianness;13use cranelift_codegen::isa::TargetIsa;14use gimli::AttributeValue;15use gimli::write;16use std::collections::HashSet;17use wasmtime_environ::StaticModuleIndex;18use wasmtime_environ::error::{Context, Error};19use wasmtime_versioned_export_macros::versioned_stringify_ident;2021#[derive(Debug)]22pub struct InheritedAttr<T> {23stack: Vec<(isize, T)>,24}2526impl<T> InheritedAttr<T> {27fn new() -> Self {28InheritedAttr { stack: Vec::new() }29}3031fn update(&mut self, depth: isize) {32while !self.stack.is_empty() && self.stack.last().unwrap().0 >= depth {33self.stack.pop();34}35}3637pub fn push(&mut self, depth: isize, value: T) {38self.stack.push((depth, value));39}4041pub fn top(&self) -> Option<&T> {42self.stack.last().map(|entry| &entry.1)43}4445pub fn top_with_depth_mut(&mut self, depth: isize) -> Option<&mut T> {46self.stack47.last_mut()48.filter(|entry| entry.0 == depth)49.map(|entry| &mut entry.1)50}5152fn is_empty(&self) -> bool {53self.stack.is_empty()54}55}5657fn get_base_type_name(type_entry: &write::ConvertUnitEntry<Reader<'_>>) -> Result<String, Error> {58// FIXME remove recursion.59if let Some(die_ref) = type_entry.attr_value(gimli::DW_AT_type) {60if let Some(ref die) = resolve_die_ref(type_entry.read_unit, &die_ref)? {61if let Some(value) = die.attr_value(gimli::DW_AT_name) {62return Ok(String::from(die.read_unit.attr_string(value)?.to_string()?));63}64match die.tag {65gimli::DW_TAG_const_type => {66return Ok(format!("const {}", get_base_type_name(die)?));67}68gimli::DW_TAG_pointer_type => {69return Ok(format!("{}*", get_base_type_name(die)?));70}71gimli::DW_TAG_reference_type => {72return Ok(format!("{}&", get_base_type_name(die)?));73}74gimli::DW_TAG_array_type => {75return Ok(format!("{}[]", get_base_type_name(die)?));76}77_ => (),78}79}80}81Ok(String::from("??"))82}8384enum WebAssemblyPtrKind {85Reference,86Pointer,87}8889/// Replaces WebAssembly pointer type DIE with the wrapper90/// which natively represented by offset in a Wasm memory.91///92/// `pointer_type_entry` is a DW_TAG_pointer_type entry (e.g. `T*`),93/// which refers its base type (e.g. `T`), or is a94/// DW_TAG_reference_type (e.g. `T&`).95///96/// The generated wrapper is a structure that contains only the97/// `__ptr` field. The utility operators overloads is added to98/// provide better debugging experience.99///100/// Wrappers of pointer and reference types are identical except for101/// their name -- they are formatted and accessed from a debugger102/// the same way.103///104/// Notice that "resolve_vmctx_memory_ptr" is external/builtin105/// subprogram that is not part of Wasm code.106fn replace_pointer_type<'a>(107wrapper_die_id: write::UnitEntryId,108parent_id: write::UnitEntryId,109kind: WebAssemblyPtrKind,110wasm_ptr_die_ref: write::DebugInfoRef,111pointer_type_entry: &write::ConvertUnitEntry<Reader<'a>>,112unit: &mut write::ConvertUnit<'_, Reader<'a>>,113) -> Result<(), Error> {114const WASM_PTR_LEN: u8 = 4;115116macro_rules! add_tag {117($parent_id:ident, $tag:expr => $die:ident as $die_id:ident { $($a:path = $v:expr),* }) => {118let $die_id = unit.unit.add($parent_id, $tag);119#[allow(unused_variables, reason = "sometimes not used below")]120let $die = unit.unit.get_mut($die_id);121$( $die.set($a, $v); )*122};123}124125// Build DW_TAG_structure_type for the wrapper:126// .. DW_AT_name = "WebAssemblyPtrWrapper<T>",127// .. DW_AT_byte_size = 4,128let name = match kind {129WebAssemblyPtrKind::Pointer => format!(130"WebAssemblyPtrWrapper<{}>",131get_base_type_name(pointer_type_entry)?132),133WebAssemblyPtrKind::Reference => format!(134"WebAssemblyRefWrapper<{}>",135get_base_type_name(pointer_type_entry)?136),137};138unit.unit139.add_reserved(wrapper_die_id, parent_id, gimli::DW_TAG_structure_type);140let wrapper_die = unit.unit.get_mut(wrapper_die_id);141wrapper_die.set(142gimli::DW_AT_name,143write::AttributeValue::StringRef(unit.strings.add(name.as_str())),144);145wrapper_die.set(146gimli::DW_AT_byte_size,147write::AttributeValue::Data1(WASM_PTR_LEN),148);149150// Build DW_TAG_pointer_type for `WebAssemblyPtrWrapper<T>*`:151// .. DW_AT_type = <wrapper_die>152add_tag!(parent_id, gimli::DW_TAG_pointer_type => wrapper_ptr_type as wrapper_ptr_type_id {153gimli::DW_AT_type = write::AttributeValue::UnitRef(wrapper_die_id)154});155156let base_type = pointer_type_entry.attr_value(gimli::DW_AT_type);157let base_type_id = if let Some(AttributeValue::UnitRef(offset)) = base_type {158unit.convert_unit_ref(offset).ok()159} else {160None161};162163// Build DW_TAG_reference_type for `T&`:164// .. DW_AT_type = <base_type>165add_tag!(parent_id, gimli::DW_TAG_reference_type => ref_type as ref_type_id {});166if let Some(base_type_id) = base_type_id {167ref_type.set(168gimli::DW_AT_type,169write::AttributeValue::UnitRef(base_type_id),170);171}172173// Build DW_TAG_pointer_type for `T*`:174// .. DW_AT_type = <base_type>175add_tag!(parent_id, gimli::DW_TAG_pointer_type => ptr_type as ptr_type_id {});176if let Some(base_type_id) = base_type_id {177ptr_type.set(178gimli::DW_AT_type,179write::AttributeValue::UnitRef(base_type_id),180);181}182183// Build wrapper_die's DW_TAG_template_type_parameter:184// .. DW_AT_name = "T"185// .. DW_AT_type = <base_type>186add_tag!(wrapper_die_id, gimli::DW_TAG_template_type_parameter => t_param_die as t_param_die_id {187gimli::DW_AT_name = write::AttributeValue::StringRef(unit.strings.add("T"))188});189if let Some(base_type_id) = base_type_id {190t_param_die.set(191gimli::DW_AT_type,192write::AttributeValue::UnitRef(base_type_id),193);194}195196// Build wrapper_die's DW_TAG_member for `__ptr`:197// .. DW_AT_name = "__ptr"198// .. DW_AT_type = <wp_die>199// .. DW_AT_location = 0200add_tag!(wrapper_die_id, gimli::DW_TAG_member => m_die as m_die_id {201gimli::DW_AT_name = write::AttributeValue::StringRef(unit.strings.add("__ptr")),202gimli::DW_AT_type = write::AttributeValue::DebugInfoRef(wasm_ptr_die_ref),203gimli::DW_AT_data_member_location = write::AttributeValue::Data1(0)204});205206// Build wrapper_die's DW_TAG_subprogram for `ptr()`:207// .. DW_AT_linkage_name = "wasmtime_resolve_vmctx_memory_ptr"208// .. DW_AT_name = "ptr"209// .. DW_AT_type = <ptr_type>210// .. DW_TAG_formal_parameter211// .. .. DW_AT_type = <wrapper_ptr_type>212// .. .. DW_AT_artificial = 1213add_tag!(wrapper_die_id, gimli::DW_TAG_subprogram => deref_op_die as deref_op_die_id {214gimli::DW_AT_linkage_name = write::AttributeValue::StringRef(unit.strings.add(versioned_stringify_ident!(wasmtime_resolve_vmctx_memory_ptr))),215gimli::DW_AT_name = write::AttributeValue::StringRef(unit.strings.add("ptr")),216gimli::DW_AT_type = write::AttributeValue::UnitRef(ptr_type_id)217});218add_tag!(deref_op_die_id, gimli::DW_TAG_formal_parameter => deref_op_this_param as deref_op_this_param_id {219gimli::DW_AT_type = write::AttributeValue::UnitRef(wrapper_ptr_type_id),220gimli::DW_AT_artificial = write::AttributeValue::Flag(true)221});222223// Build wrapper_die's DW_TAG_subprogram for `operator*`:224// .. DW_AT_linkage_name = "wasmtime_resolve_vmctx_memory_ptr"225// .. DW_AT_name = "operator*"226// .. DW_AT_type = <ref_type>227// .. DW_TAG_formal_parameter228// .. .. DW_AT_type = <wrapper_ptr_type>229// .. .. DW_AT_artificial = 1230add_tag!(wrapper_die_id, gimli::DW_TAG_subprogram => deref_op_die as deref_op_die_id {231gimli::DW_AT_linkage_name = write::AttributeValue::StringRef(unit.strings.add(versioned_stringify_ident!(wasmtime_resolve_vmctx_memory_ptr))),232gimli::DW_AT_name = write::AttributeValue::StringRef(unit.strings.add("operator*")),233gimli::DW_AT_type = write::AttributeValue::UnitRef(ref_type_id)234});235add_tag!(deref_op_die_id, gimli::DW_TAG_formal_parameter => deref_op_this_param as deref_op_this_param_id {236gimli::DW_AT_type = write::AttributeValue::UnitRef(wrapper_ptr_type_id),237gimli::DW_AT_artificial = write::AttributeValue::Flag(true)238});239240// Build wrapper_die's DW_TAG_subprogram for `operator->`:241// .. DW_AT_linkage_name = "wasmtime_resolve_vmctx_memory_ptr"242// .. DW_AT_name = "operator->"243// .. DW_AT_type = <ptr_type>244// .. DW_TAG_formal_parameter245// .. .. DW_AT_type = <wrapper_ptr_type>246// .. .. DW_AT_artificial = 1247add_tag!(wrapper_die_id, gimli::DW_TAG_subprogram => deref_op_die as deref_op_die_id {248gimli::DW_AT_linkage_name = write::AttributeValue::StringRef(unit.strings.add(versioned_stringify_ident!(wasmtime_resolve_vmctx_memory_ptr))),249gimli::DW_AT_name = write::AttributeValue::StringRef(unit.strings.add("operator->")),250gimli::DW_AT_type = write::AttributeValue::UnitRef(ptr_type_id)251});252add_tag!(deref_op_die_id, gimli::DW_TAG_formal_parameter => deref_op_this_param as deref_op_this_param_id {253gimli::DW_AT_type = write::AttributeValue::UnitRef(wrapper_ptr_type_id),254gimli::DW_AT_artificial = write::AttributeValue::Flag(true)255});256257Ok(())258}259260pub(crate) fn clone_unit<'a>(261compilation: &mut Compilation<'_>,262module: StaticModuleIndex,263unit: &mut write::ConvertUnit<'_, Reader<'a>>,264root_entry: &write::ConvertUnitEntry<Reader<'a>>,265skeleton_root_entry: Option<&write::ConvertUnitEntry<Reader<'a>>>,266addr_tr: &AddressTransform,267out_module_synthetic_unit: &ModuleSyntheticUnit,268translated: &mut HashSet<usize>,269isa: &dyn TargetIsa,270) -> Result<(), Error> {271let mut current_frame_base = InheritedAttr::new();272let mut current_value_range = InheritedAttr::new();273let mut current_scope_ranges = InheritedAttr::new();274let mut current_subprogram = InheritedAttr::new();275276dbi_log!("Cloning CU {:?}", log_get_cu_summary(unit.read_unit));277278if let Some(convert_program) = unit.read_line_program(Some(unit.unit.encoding()), None)? {279let (program, files) = clone_line_program(convert_program, addr_tr)?;280unit.set_line_program(program, files);281}282283if root_entry.tag == gimli::DW_TAG_compile_unit {284log_begin_input_die(root_entry);285286let out_root_id = unit.unit.root();287clone_die_attributes(288unit,289root_entry,290addr_tr,291None,292out_root_id,293None,294None,295EntryAttributesContext {296subprograms: &mut current_subprogram,297frame_base: None,298},299isa,300)?;301302if let Some(skeleton_root_entry) = skeleton_root_entry {303clone_die_attributes(304unit,305skeleton_root_entry,306addr_tr,307None,308out_root_id,309None,310None,311EntryAttributesContext {312subprograms: &mut current_subprogram,313frame_base: None,314},315isa,316)?;317}318319log_end_output_die(out_root_id, root_entry, unit);320} else {321// Can happen when the DWARF is split and we dont have the package/dwo files.322// This is a better user experience than errorring.323dbi_log!("... skipped: split DW_TAG_compile_unit entry missing");324return Ok(()); // empty:325}326327let mut entry = write::ConvertUnitEntry::null(unit.read_unit);328while let Some(entry_id) = unit.read_entry(&mut entry)? {329log_begin_input_die(&entry);330let (Some(out_die_id), Some(parent)) = (entry_id, entry.parent) else {331log_end_output_die_skipped(&entry, "unreachable");332continue;333};334335let new_stack_len = entry.depth;336current_frame_base.update(new_stack_len);337current_scope_ranges.update(new_stack_len);338current_value_range.update(new_stack_len);339current_subprogram.update(new_stack_len);340let range_builder = if entry.tag == gimli::DW_TAG_subprogram {341let range_builder = RangeInfoBuilder::from_subprogram_die(&entry, addr_tr)?;342if let RangeInfoBuilder::Function(func) = range_builder {343let frame_info = compilation.function_frame_info(module, func);344current_value_range.push(new_stack_len, frame_info);345let (symbol, _) = compilation.function(module, func);346translated.insert(symbol);347current_scope_ranges.push(new_stack_len, range_builder.get_ranges(addr_tr));348Some(range_builder)349} else {350// FIXME current_scope_ranges.push()351None352}353} else {354if entry.has_attr(gimli::DW_AT_high_pc) || entry.has_attr(gimli::DW_AT_ranges) {355let range_builder = RangeInfoBuilder::from(&entry)?;356current_scope_ranges.push(new_stack_len, range_builder.get_ranges(addr_tr));357Some(range_builder)358} else {359None360}361};362363if let Some(AttributeValue::Exprloc(expr)) = entry.attr_value(gimli::DW_AT_frame_base) {364if let Some(expr) = compile_expression(&expr, unit.unit.encoding(), None)? {365current_frame_base.push(new_stack_len, expr);366}367}368369if entry.tag == gimli::DW_TAG_pointer_type || entry.tag == gimli::DW_TAG_reference_type {370// Wrap pointer types.371let pointer_kind = match entry.tag {372gimli::DW_TAG_pointer_type => WebAssemblyPtrKind::Pointer,373gimli::DW_TAG_reference_type => WebAssemblyPtrKind::Reference,374_ => panic!(),375};376replace_pointer_type(377out_die_id,378parent,379pointer_kind,380out_module_synthetic_unit.wasm_ptr_die_ref(),381&entry,382unit,383)?;384log_end_output_die(out_die_id, &entry, unit);385continue;386}387388unit.unit.add_reserved(out_die_id, parent, entry.tag);389390clone_die_attributes(391unit,392&entry,393addr_tr,394current_value_range.top(),395out_die_id,396range_builder,397current_scope_ranges.top(),398EntryAttributesContext {399subprograms: &mut current_subprogram,400frame_base: current_frame_base.top(),401},402isa,403)?;404405// Data in WebAssembly memory always uses little-endian byte order.406// If the native architecture is big-endian, we need to mark all407// base types used to refer to WebAssembly memory as little-endian408// using the DW_AT_endianity attribute, so that the debugger will409// be able to correctly access them.410if entry.tag == gimli::DW_TAG_base_type && isa.endianness() == Endianness::Big {411let current_scope = unit.unit.get_mut(out_die_id);412current_scope.set(413gimli::DW_AT_endianity,414write::AttributeValue::Endianity(gimli::DW_END_little),415);416}417418if entry.tag == gimli::DW_TAG_subprogram && !current_scope_ranges.is_empty() {419append_vmctx_info(420unit.unit,421out_die_id,422out_module_synthetic_unit.vmctx_ptr_die_ref(),423addr_tr,424current_value_range.top(),425current_scope_ranges.top().context("range")?,426unit.strings,427isa,428)?;429}430431log_end_output_die(out_die_id, &entry, unit);432}433Ok(())434}435436437