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