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