wasmtime_cranelift/debug/transform/
utils.rs

1use super::address_transform::AddressTransform;
2use super::expression::{CompiledExpression, FunctionFrameInfo};
3use crate::debug::ModuleMemoryOffset;
4use crate::CompiledFunctions;
5use anyhow::Error;
6use cranelift_codegen::isa::TargetIsa;
7use gimli::write;
8use wasmtime_environ::DefinedFuncIndex;
9
10/// Adds internal Wasm utility types DIEs such as WebAssemblyPtr and
11/// WasmtimeVMContext.
12///
13/// For unwrapping Wasm pointer, the WasmtimeVMContext has the `set()` method
14/// that allows to control current Wasm memory to inspect.
15/// Notice that "set_vmctx_memory" is an external/builtin subprogram that
16/// is not part of Wasm code.
17pub(crate) fn add_internal_types(
18    comp_unit: &mut write::Unit,
19    root_id: write::UnitEntryId,
20    out_strings: &mut write::StringTable,
21    memory_offset: &ModuleMemoryOffset,
22) -> (write::UnitEntryId, write::UnitEntryId) {
23    const WASM_PTR_LEN: u8 = 4;
24
25    macro_rules! add_tag {
26        ($parent_id:ident, $tag:expr => $die:ident as $die_id:ident { $($a:path = $v:expr),* }) => {
27            let $die_id = comp_unit.add($parent_id, $tag);
28            let $die = comp_unit.get_mut($die_id);
29            $( $die.set($a, $v); )*
30        };
31    }
32
33    // Build DW_TAG_base_type for generic `WebAssemblyPtr`.
34    //  .. DW_AT_name = "WebAssemblyPtr"
35    //  .. DW_AT_byte_size = 4
36    //  .. DW_AT_encoding = DW_ATE_unsigned
37    // let wp_die_id = comp_unit.add(root_id, gimli::DW_TAG_base_type);
38    // let wp_die = comp_unit.get_mut(wp_die_id);
39    add_tag!(root_id, gimli::DW_TAG_base_type => wp_die as wp_die_id {
40        gimli::DW_AT_name = write::AttributeValue::StringRef(out_strings.add("WebAssemblyPtr")),
41        gimli::DW_AT_byte_size = write::AttributeValue::Data1(WASM_PTR_LEN),
42        gimli::DW_AT_encoding = write::AttributeValue::Encoding(gimli::DW_ATE_unsigned)
43    });
44
45    // Build DW_TAG_base_type for Wasm byte:
46    //  .. DW_AT_name = u8
47    //  .. DW_AT_encoding = DW_ATE_unsigned
48    //  .. DW_AT_byte_size = 1
49    add_tag!(root_id, gimli::DW_TAG_base_type => memory_byte_die as memory_byte_die_id {
50        gimli::DW_AT_name = write::AttributeValue::StringRef(out_strings.add("u8")),
51        gimli::DW_AT_encoding = write::AttributeValue::Encoding(gimli::DW_ATE_unsigned),
52        gimli::DW_AT_byte_size = write::AttributeValue::Data1(1)
53    });
54
55    // Build DW_TAG_pointer_type that references Wasm bytes:
56    //  .. DW_AT_name = "u8*"
57    //  .. DW_AT_type = <memory_byte_die>
58    add_tag!(root_id, gimli::DW_TAG_pointer_type => memory_bytes_die as memory_bytes_die_id {
59        gimli::DW_AT_name = write::AttributeValue::StringRef(out_strings.add("u8*")),
60        gimli::DW_AT_type = write::AttributeValue::UnitRef(memory_byte_die_id)
61    });
62
63    // Create artificial VMContext type and its reference for convinience viewing
64    // its fields (such as memory ref) in a debugger. Build DW_TAG_structure_type:
65    //   .. DW_AT_name = "WasmtimeVMContext"
66    let vmctx_die_id = comp_unit.add(root_id, gimli::DW_TAG_structure_type);
67    let vmctx_die = comp_unit.get_mut(vmctx_die_id);
68    vmctx_die.set(
69        gimli::DW_AT_name,
70        write::AttributeValue::StringRef(out_strings.add("WasmtimeVMContext")),
71    );
72
73    // TODO multiple memories
74    match *memory_offset {
75        ModuleMemoryOffset::Defined(memory_offset) => {
76            // The context has defined memory: extend the WasmtimeVMContext size
77            // past the "memory" field.
78            const MEMORY_FIELD_SIZE_PLUS_PADDING: u32 = 8;
79            vmctx_die.set(
80                gimli::DW_AT_byte_size,
81                write::AttributeValue::Data4(memory_offset + MEMORY_FIELD_SIZE_PLUS_PADDING),
82            );
83
84            // Define the "memory" field which is a direct pointer to allocated Wasm memory.
85            // Build DW_TAG_member:
86            //  .. DW_AT_name = "memory"
87            //  .. DW_AT_type = <memory_bytes_die>
88            //  .. DW_AT_data_member_location = `memory_offset`
89            add_tag!(vmctx_die_id, gimli::DW_TAG_member => m_die as m_die_id {
90                gimli::DW_AT_name = write::AttributeValue::StringRef(out_strings.add("memory")),
91                gimli::DW_AT_type = write::AttributeValue::UnitRef(memory_bytes_die_id),
92                gimli::DW_AT_data_member_location = write::AttributeValue::Udata(memory_offset as u64)
93            });
94        }
95        ModuleMemoryOffset::Imported(_) => {
96            // TODO implement convinience pointer to and additional types for VMMemoryImport.
97        }
98        ModuleMemoryOffset::None => (),
99    }
100
101    // Build DW_TAG_pointer_type for `WasmtimeVMContext*`:
102    //  .. DW_AT_name = "WasmtimeVMContext*"
103    //  .. DW_AT_type = <vmctx_die>
104    add_tag!(root_id, gimli::DW_TAG_pointer_type => vmctx_ptr_die as vmctx_ptr_die_id {
105        gimli::DW_AT_name = write::AttributeValue::StringRef(out_strings.add("WasmtimeVMContext*")),
106        gimli::DW_AT_type = write::AttributeValue::UnitRef(vmctx_die_id)
107    });
108
109    // Build vmctx_die's DW_TAG_subprogram for `set` method:
110    //  .. DW_AT_linkage_name = "set_vmctx_memory"
111    //  .. DW_AT_name = "set"
112    //  .. DW_TAG_formal_parameter
113    //  ..  .. DW_AT_type = <vmctx_ptr_die>
114    //  ..  .. DW_AT_artificial = 1
115    add_tag!(vmctx_die_id, gimli::DW_TAG_subprogram => vmctx_set as vmctx_set_id {
116        gimli::DW_AT_linkage_name = write::AttributeValue::StringRef(out_strings.add("set_vmctx_memory")),
117        gimli::DW_AT_name = write::AttributeValue::StringRef(out_strings.add("set"))
118    });
119    add_tag!(vmctx_set_id, gimli::DW_TAG_formal_parameter => vmctx_set_this_param as vmctx_set_this_param_id {
120        gimli::DW_AT_type = write::AttributeValue::UnitRef(vmctx_ptr_die_id),
121        gimli::DW_AT_artificial = write::AttributeValue::Flag(true)
122    });
123
124    (wp_die_id, vmctx_ptr_die_id)
125}
126
127pub(crate) fn append_vmctx_info(
128    comp_unit: &mut write::Unit,
129    parent_id: write::UnitEntryId,
130    vmctx_die_id: write::UnitEntryId,
131    addr_tr: &AddressTransform,
132    frame_info: Option<&FunctionFrameInfo>,
133    scope_ranges: &[(u64, u64)],
134    out_strings: &mut write::StringTable,
135    isa: &dyn TargetIsa,
136) -> Result<(), Error> {
137    let loc = {
138        let expr = CompiledExpression::vmctx();
139        let locs = expr
140            .build_with_locals(scope_ranges, addr_tr, frame_info, isa)
141            .map(|i| {
142                i.map(|(begin, length, data)| write::Location::StartLength {
143                    begin,
144                    length,
145                    data,
146                })
147            })
148            .collect::<Result<Vec<_>, _>>()?;
149        let list_id = comp_unit.locations.add(write::LocationList(locs));
150        write::AttributeValue::LocationListRef(list_id)
151    };
152
153    let var_die_id = comp_unit.add(parent_id, gimli::DW_TAG_variable);
154    let var_die = comp_unit.get_mut(var_die_id);
155    var_die.set(
156        gimli::DW_AT_name,
157        write::AttributeValue::StringRef(out_strings.add("__vmctx")),
158    );
159    var_die.set(
160        gimli::DW_AT_type,
161        write::AttributeValue::UnitRef(vmctx_die_id),
162    );
163    var_die.set(gimli::DW_AT_location, loc);
164
165    Ok(())
166}
167
168pub(crate) fn get_function_frame_info<'a, 'b, 'c>(
169    memory_offset: &ModuleMemoryOffset,
170    funcs: &'b CompiledFunctions,
171    func_index: DefinedFuncIndex,
172) -> Option<FunctionFrameInfo<'a>>
173where
174    'b: 'a,
175    'c: 'a,
176{
177    if let Some(func) = funcs.get(func_index) {
178        let frame_info = FunctionFrameInfo {
179            value_ranges: &func.value_labels_ranges,
180            memory_offset: memory_offset.clone(),
181            sized_stack_slots: &func.sized_stack_slots,
182        };
183        Some(frame_info)
184    } else {
185        None
186    }
187}