1use super::address_transform::AddressTransform;
2use super::attr::{clone_die_attributes, FileAttributeContext};
3use super::expression::compile_expression;
4use super::line_program::clone_line_program;
5use super::range_info_builder::RangeInfoBuilder;
6use super::refs::{PendingDebugInfoRefs, PendingUnitRefs, UnitRefsMap};
7use super::utils::{add_internal_types, append_vmctx_info, get_function_frame_info};
8use super::{DebugInputContext, Reader, TransformError};
9use crate::debug::ModuleMemoryOffset;
10use crate::CompiledFunctions;
11use anyhow::{Context, Error};
12use cranelift_codegen::ir::Endianness;
13use cranelift_codegen::isa::TargetIsa;
14use gimli::write;
15use gimli::{AttributeValue, DebuggingInformationEntry, Unit};
16use std::collections::HashSet;
17use wasmtime_environ::DefinedFuncIndex;
18
19struct InheritedAttr<T> {
20 stack: Vec<(usize, T)>,
21}
22
23impl<T> InheritedAttr<T> {
24 fn new() -> Self {
25 InheritedAttr { stack: Vec::new() }
26 }
27
28 fn update(&mut self, depth: usize) {
29 while !self.stack.is_empty() && self.stack.last().unwrap().0 >= depth {
30 self.stack.pop();
31 }
32 }
33
34 fn push(&mut self, depth: usize, value: T) {
35 self.stack.push((depth, value));
36 }
37
38 fn top(&self) -> Option<&T> {
39 self.stack.last().map(|entry| &entry.1)
40 }
41
42 fn is_empty(&self) -> bool {
43 self.stack.is_empty()
44 }
45}
46
47fn get_base_type_name<R>(
48 type_entry: &DebuggingInformationEntry<R>,
49 unit: &Unit<R, R::Offset>,
50 context: &DebugInputContext<R>,
51) -> Result<String, Error>
52where
53 R: Reader,
54{
55 if let Some(AttributeValue::UnitRef(ref offset)) = type_entry.attr_value(gimli::DW_AT_type)? {
57 let mut entries = unit.entries_at_offset(*offset)?;
58 entries.next_entry()?;
59 if let Some(die) = entries.current() {
60 if let Some(AttributeValue::DebugStrRef(str_offset)) =
61 die.attr_value(gimli::DW_AT_name)?
62 {
63 return Ok(String::from(
64 context.debug_str.get_str(str_offset)?.to_string()?,
65 ));
66 }
67 match die.tag() {
68 gimli::DW_TAG_const_type => {
69 return Ok(format!("const {}", get_base_type_name(die, unit, context)?));
70 }
71 gimli::DW_TAG_pointer_type => {
72 return Ok(format!("{}*", get_base_type_name(die, unit, context)?));
73 }
74 gimli::DW_TAG_reference_type => {
75 return Ok(format!("{}&", get_base_type_name(die, unit, context)?));
76 }
77 gimli::DW_TAG_array_type => {
78 return Ok(format!("{}[]", get_base_type_name(die, unit, context)?));
79 }
80 _ => (),
81 }
82 }
83 }
84 Ok(String::from("??"))
85}
86
87enum WebAssemblyPtrKind {
88 Reference,
89 Pointer,
90}
91
92fn replace_pointer_type<R>(
110 parent_id: write::UnitEntryId,
111 kind: WebAssemblyPtrKind,
112 comp_unit: &mut write::Unit,
113 wp_die_id: write::UnitEntryId,
114 pointer_type_entry: &DebuggingInformationEntry<R>,
115 unit: &Unit<R, R::Offset>,
116 context: &DebugInputContext<R>,
117 out_strings: &mut write::StringTable,
118 pending_die_refs: &mut PendingUnitRefs,
119) -> Result<write::UnitEntryId, Error>
120where
121 R: Reader,
122{
123 const WASM_PTR_LEN: u8 = 4;
124
125 macro_rules! add_tag {
126 ($parent_id:ident, $tag:expr => $die:ident as $die_id:ident { $($a:path = $v:expr),* }) => {
127 let $die_id = comp_unit.add($parent_id, $tag);
128 #[allow(unused_variables)]
129 let $die = comp_unit.get_mut($die_id);
130 $( $die.set($a, $v); )*
131 };
132 }
133
134 let name = match kind {
138 WebAssemblyPtrKind::Pointer => format!(
139 "WebAssemblyPtrWrapper<{}>",
140 get_base_type_name(pointer_type_entry, unit, context)?
141 ),
142 WebAssemblyPtrKind::Reference => format!(
143 "WebAssemblyRefWrapper<{}>",
144 get_base_type_name(pointer_type_entry, unit, context)?
145 ),
146 };
147 add_tag!(parent_id, gimli::DW_TAG_structure_type => wrapper_die as wrapper_die_id {
148 gimli::DW_AT_name = write::AttributeValue::StringRef(out_strings.add(name.as_str())),
149 gimli::DW_AT_byte_size = write::AttributeValue::Data1(WASM_PTR_LEN)
150 });
151
152 add_tag!(parent_id, gimli::DW_TAG_pointer_type => wrapper_ptr_type as wrapper_ptr_type_id {
155 gimli::DW_AT_type = write::AttributeValue::UnitRef(wrapper_die_id)
156 });
157
158 let base_type_id = pointer_type_entry.attr_value(gimli::DW_AT_type)?;
159 add_tag!(parent_id, gimli::DW_TAG_reference_type => ref_type as ref_type_id {});
162 if let Some(AttributeValue::UnitRef(ref offset)) = base_type_id {
163 pending_die_refs.insert(ref_type_id, gimli::DW_AT_type, *offset);
164 }
165
166 add_tag!(parent_id, gimli::DW_TAG_pointer_type => ptr_type as ptr_type_id {});
169 if let Some(AttributeValue::UnitRef(ref offset)) = base_type_id {
170 pending_die_refs.insert(ptr_type_id, gimli::DW_AT_type, *offset);
171 }
172
173 add_tag!(wrapper_die_id, gimli::DW_TAG_template_type_parameter => t_param_die as t_param_die_id {
177 gimli::DW_AT_name = write::AttributeValue::StringRef(out_strings.add("T"))
178 });
179 if let Some(AttributeValue::UnitRef(ref offset)) = base_type_id {
180 pending_die_refs.insert(t_param_die_id, gimli::DW_AT_type, *offset);
181 }
182
183 add_tag!(wrapper_die_id, gimli::DW_TAG_member => m_die as m_die_id {
188 gimli::DW_AT_name = write::AttributeValue::StringRef(out_strings.add("__ptr")),
189 gimli::DW_AT_type = write::AttributeValue::UnitRef(wp_die_id),
190 gimli::DW_AT_data_member_location = write::AttributeValue::Data1(0)
191 });
192
193 add_tag!(wrapper_die_id, gimli::DW_TAG_subprogram => deref_op_die as deref_op_die_id {
201 gimli::DW_AT_linkage_name = write::AttributeValue::StringRef(out_strings.add("resolve_vmctx_memory_ptr")),
202 gimli::DW_AT_name = write::AttributeValue::StringRef(out_strings.add("ptr")),
203 gimli::DW_AT_type = write::AttributeValue::UnitRef(ptr_type_id)
204 });
205 add_tag!(deref_op_die_id, gimli::DW_TAG_formal_parameter => deref_op_this_param as deref_op_this_param_id {
206 gimli::DW_AT_type = write::AttributeValue::UnitRef(wrapper_ptr_type_id),
207 gimli::DW_AT_artificial = write::AttributeValue::Flag(true)
208 });
209
210 add_tag!(wrapper_die_id, gimli::DW_TAG_subprogram => deref_op_die as deref_op_die_id {
218 gimli::DW_AT_linkage_name = write::AttributeValue::StringRef(out_strings.add("resolve_vmctx_memory_ptr")),
219 gimli::DW_AT_name = write::AttributeValue::StringRef(out_strings.add("operator*")),
220 gimli::DW_AT_type = write::AttributeValue::UnitRef(ref_type_id)
221 });
222 add_tag!(deref_op_die_id, gimli::DW_TAG_formal_parameter => deref_op_this_param as deref_op_this_param_id {
223 gimli::DW_AT_type = write::AttributeValue::UnitRef(wrapper_ptr_type_id),
224 gimli::DW_AT_artificial = write::AttributeValue::Flag(true)
225 });
226
227 add_tag!(wrapper_die_id, gimli::DW_TAG_subprogram => deref_op_die as deref_op_die_id {
235 gimli::DW_AT_linkage_name = write::AttributeValue::StringRef(out_strings.add("resolve_vmctx_memory_ptr")),
236 gimli::DW_AT_name = write::AttributeValue::StringRef(out_strings.add("operator->")),
237 gimli::DW_AT_type = write::AttributeValue::UnitRef(ptr_type_id)
238 });
239 add_tag!(deref_op_die_id, gimli::DW_TAG_formal_parameter => deref_op_this_param as deref_op_this_param_id {
240 gimli::DW_AT_type = write::AttributeValue::UnitRef(wrapper_ptr_type_id),
241 gimli::DW_AT_artificial = write::AttributeValue::Flag(true)
242 });
243
244 Ok(wrapper_die_id)
245}
246
247fn is_dead_code<R: Reader>(entry: &DebuggingInformationEntry<R>) -> bool {
248 const TOMBSTONE: u64 = u32::MAX as u64;
249
250 match entry.attr_value(gimli::DW_AT_low_pc) {
251 Ok(Some(AttributeValue::Addr(addr))) => addr == TOMBSTONE,
252 _ => false,
253 }
254}
255
256pub(crate) fn clone_unit<'a, R>(
257 dwarf: &gimli::Dwarf<R>,
258 unit: Unit<R, R::Offset>,
259 context: &DebugInputContext<R>,
260 addr_tr: &'a AddressTransform,
261 funcs: &'a CompiledFunctions,
262 memory_offset: &ModuleMemoryOffset,
263 out_encoding: gimli::Encoding,
264 out_units: &mut write::UnitTable,
265 out_strings: &mut write::StringTable,
266 translated: &mut HashSet<DefinedFuncIndex>,
267 isa: &dyn TargetIsa,
268) -> Result<Option<(write::UnitId, UnitRefsMap, PendingDebugInfoRefs)>, Error>
269where
270 R: Reader,
271{
272 let mut die_ref_map = UnitRefsMap::new();
273 let mut pending_die_refs = PendingUnitRefs::new();
274 let mut pending_di_refs = PendingDebugInfoRefs::new();
275 let mut stack = Vec::new();
276
277 let mut entries = unit.entries();
279 let (mut comp_unit, unit_id, file_map, file_index_base, cu_low_pc, wp_die_id, vmctx_die_id) =
280 if let Some((depth_delta, entry)) = entries.next_dfs()? {
281 assert_eq!(depth_delta, 0);
282 let (out_line_program, debug_line_offset, file_map, file_index_base) =
283 clone_line_program(
284 &unit,
285 entry,
286 addr_tr,
287 out_encoding,
288 context.debug_str,
289 context.debug_str_offsets,
290 context.debug_line_str,
291 context.debug_line,
292 out_strings,
293 )?;
294
295 if entry.tag() == gimli::DW_TAG_compile_unit {
296 let unit_id = out_units.add(write::Unit::new(out_encoding, out_line_program));
297 let comp_unit = out_units.get_mut(unit_id);
298
299 let root_id = comp_unit.root();
300 die_ref_map.insert(entry.offset(), root_id);
301
302 let cu_low_pc = if let Some(AttributeValue::Addr(addr)) =
303 entry.attr_value(gimli::DW_AT_low_pc)?
304 {
305 addr
306 } else if let Some(AttributeValue::DebugAddrIndex(i)) =
307 entry.attr_value(gimli::DW_AT_low_pc)?
308 {
309 context.debug_addr.get_address(4, unit.addr_base, i)?
310 } else {
311 0
313 };
314
315 clone_die_attributes(
316 dwarf,
317 &unit,
318 entry,
319 context,
320 addr_tr,
321 None,
322 comp_unit,
323 root_id,
324 None,
325 None,
326 cu_low_pc,
327 out_strings,
328 &mut pending_die_refs,
329 &mut pending_di_refs,
330 FileAttributeContext::Root(Some(debug_line_offset)),
331 isa,
332 )?;
333
334 let (wp_die_id, vmctx_die_id) =
335 add_internal_types(comp_unit, root_id, out_strings, memory_offset);
336
337 stack.push(root_id);
338 (
339 comp_unit,
340 unit_id,
341 file_map,
342 file_index_base,
343 cu_low_pc,
344 wp_die_id,
345 vmctx_die_id,
346 )
347 } else {
348 return Err(TransformError("Unexpected unit header").into());
349 }
350 } else {
351 return Ok(None); };
353 let mut skip_at_depth = None;
354 let mut current_frame_base = InheritedAttr::new();
355 let mut current_value_range = InheritedAttr::new();
356 let mut current_scope_ranges = InheritedAttr::new();
357 while let Some((depth_delta, entry)) = entries.next_dfs()? {
358 let depth_delta = if let Some((depth, cached)) = skip_at_depth {
364 let new_depth = depth + depth_delta;
366 if new_depth > 0 {
368 skip_at_depth = Some((new_depth, cached));
369 continue;
370 }
371 skip_at_depth = None;
373 new_depth + cached
374 } else {
375 depth_delta
376 };
377
378 if !context
379 .reachable
380 .contains(&entry.offset().to_unit_section_offset(&unit))
381 || is_dead_code(&entry)
382 {
383 skip_at_depth = Some((0, depth_delta));
387 continue;
388 }
389
390 let new_stack_len = stack.len().wrapping_add(depth_delta as usize);
391 current_frame_base.update(new_stack_len);
392 current_scope_ranges.update(new_stack_len);
393 current_value_range.update(new_stack_len);
394 let range_builder = if entry.tag() == gimli::DW_TAG_subprogram {
395 let range_builder = RangeInfoBuilder::from_subprogram_die(
396 dwarf, &unit, entry, context, addr_tr, cu_low_pc,
397 )?;
398 if let RangeInfoBuilder::Function(func_index) = range_builder {
399 if let Some(frame_info) = get_function_frame_info(memory_offset, funcs, func_index)
400 {
401 current_value_range.push(new_stack_len, frame_info);
402 }
403 translated.insert(func_index);
404 current_scope_ranges.push(new_stack_len, range_builder.get_ranges(addr_tr));
405 Some(range_builder)
406 } else {
407 None
409 }
410 } else {
411 let high_pc = entry.attr_value(gimli::DW_AT_high_pc)?;
412 let ranges = entry.attr_value(gimli::DW_AT_ranges)?;
413 if high_pc.is_some() || ranges.is_some() {
414 let range_builder =
415 RangeInfoBuilder::from(dwarf, &unit, entry, context, cu_low_pc)?;
416 current_scope_ranges.push(new_stack_len, range_builder.get_ranges(addr_tr));
417 Some(range_builder)
418 } else {
419 None
420 }
421 };
422
423 if depth_delta <= 0 {
424 for _ in depth_delta..1 {
425 stack.pop();
426 }
427 } else {
428 assert_eq!(depth_delta, 1);
429 }
430
431 if let Some(AttributeValue::Exprloc(expr)) = entry.attr_value(gimli::DW_AT_frame_base)? {
432 if let Some(expr) = compile_expression(&expr, unit.encoding(), None)? {
433 current_frame_base.push(new_stack_len, expr);
434 }
435 }
436
437 let parent = stack.last().unwrap();
438
439 if entry.tag() == gimli::DW_TAG_pointer_type || entry.tag() == gimli::DW_TAG_reference_type
440 {
441 let pointer_kind = match entry.tag() {
443 gimli::DW_TAG_pointer_type => WebAssemblyPtrKind::Pointer,
444 gimli::DW_TAG_reference_type => WebAssemblyPtrKind::Reference,
445 _ => panic!(),
446 };
447 let die_id = replace_pointer_type(
448 *parent,
449 pointer_kind,
450 comp_unit,
451 wp_die_id,
452 entry,
453 &unit,
454 context,
455 out_strings,
456 &mut pending_die_refs,
457 )?;
458 stack.push(die_id);
459 assert_eq!(stack.len(), new_stack_len);
460 die_ref_map.insert(entry.offset(), die_id);
461 continue;
462 }
463
464 let die_id = comp_unit.add(*parent, entry.tag());
465
466 stack.push(die_id);
467 assert_eq!(stack.len(), new_stack_len);
468 die_ref_map.insert(entry.offset(), die_id);
469
470 clone_die_attributes(
471 dwarf,
472 &unit,
473 entry,
474 context,
475 addr_tr,
476 current_value_range.top(),
477 &mut comp_unit,
478 die_id,
479 range_builder,
480 current_scope_ranges.top(),
481 cu_low_pc,
482 out_strings,
483 &mut pending_die_refs,
484 &mut pending_di_refs,
485 FileAttributeContext::Children {
486 file_map: &file_map,
487 file_index_base,
488 frame_base: current_frame_base.top(),
489 },
490 isa,
491 )?;
492
493 if entry.tag() == gimli::DW_TAG_base_type && isa.endianness() == Endianness::Big {
499 let current_scope = comp_unit.get_mut(die_id);
500 current_scope.set(
501 gimli::DW_AT_endianity,
502 write::AttributeValue::Endianity(gimli::DW_END_little),
503 );
504 }
505
506 if entry.tag() == gimli::DW_TAG_subprogram && !current_scope_ranges.is_empty() {
507 append_vmctx_info(
508 comp_unit,
509 die_id,
510 vmctx_die_id,
511 addr_tr,
512 current_value_range.top(),
513 current_scope_ranges.top().context("range")?,
514 out_strings,
515 isa,
516 )?;
517 }
518 }
519 die_ref_map.patch(pending_die_refs, comp_unit);
520 Ok(Some((unit_id, die_ref_map, pending_di_refs)))
521}