1 use super::address_transform::AddressTransform; 2 use super::attr::{clone_die_attributes, FileAttributeContext}; 3 use super::expression::compile_expression; 4 use super::line_program::clone_line_program; 5 use super::range_info_builder::RangeInfoBuilder; 6 use super::refs::{PendingDebugInfoRefs, PendingUnitRefs, UnitRefsMap}; 7 use super::utils::{add_internal_types, append_vmctx_info, get_function_frame_info}; 8 use super::{DebugInputContext, Reader, TransformError}; 9 use crate::CompiledFunctions; 10 use anyhow::{Context, Error}; 11 use cranelift_codegen::ir::Endianness; 12 use cranelift_codegen::isa::TargetIsa; 13 use gimli::write; 14 use gimli::{AttributeValue, DebuggingInformationEntry, Unit}; 15 use std::collections::HashSet; 16 use wasmtime_environ::{DefinedFuncIndex, ModuleMemoryOffset}; 17 18 struct InheritedAttr<T> { 19 stack: Vec<(usize, T)>, 20 } 21 22 impl<T> InheritedAttr<T> { 23 fn new() -> Self { 24 InheritedAttr { stack: Vec::new() } 25 } 26 27 fn update(&mut self, depth: usize) { 28 while !self.stack.is_empty() && self.stack.last().unwrap().0 >= depth { 29 self.stack.pop(); 30 } 31 } 32 33 fn push(&mut self, depth: usize, value: T) { 34 self.stack.push((depth, value)); 35 } 36 37 fn top(&self) -> Option<&T> { 38 self.stack.last().map(|entry| &entry.1) 39 } 40 41 fn is_empty(&self) -> bool { 42 self.stack.is_empty() 43 } 44 } 45 46 fn get_base_type_name<R>( 47 type_entry: &DebuggingInformationEntry<R>, 48 unit: &Unit<R, R::Offset>, 49 context: &DebugInputContext<R>, 50 ) -> Result<String, Error> 51 where 52 R: Reader, 53 { 54 // FIXME remove recursion. 55 if let Some(AttributeValue::UnitRef(ref offset)) = type_entry.attr_value(gimli::DW_AT_type)? { 56 let mut entries = unit.entries_at_offset(*offset)?; 57 entries.next_entry()?; 58 if let Some(die) = entries.current() { 59 if let Some(AttributeValue::DebugStrRef(str_offset)) = 60 die.attr_value(gimli::DW_AT_name)? 61 { 62 return Ok(String::from( 63 context.debug_str.get_str(str_offset)?.to_string()?, 64 )); 65 } 66 match die.tag() { 67 gimli::DW_TAG_const_type => { 68 return Ok(format!("const {}", get_base_type_name(die, unit, context)?)); 69 } 70 gimli::DW_TAG_pointer_type => { 71 return Ok(format!("{}*", get_base_type_name(die, unit, context)?)); 72 } 73 gimli::DW_TAG_reference_type => { 74 return Ok(format!("{}&", get_base_type_name(die, unit, context)?)); 75 } 76 gimli::DW_TAG_array_type => { 77 return Ok(format!("{}[]", get_base_type_name(die, unit, context)?)); 78 } 79 _ => (), 80 } 81 } 82 } 83 Ok(String::from("??")) 84 } 85 86 enum WebAssemblyPtrKind { 87 Reference, 88 Pointer, 89 } 90 91 /// Replaces WebAssembly pointer type DIE with the wrapper 92 /// which natively represented by offset in a Wasm memory. 93 /// 94 /// `pointer_type_entry` is a DW_TAG_pointer_type entry (e.g. `T*`), 95 /// which refers its base type (e.g. `T`), or is a 96 /// DW_TAG_reference_type (e.g. `T&`). 97 /// 98 /// The generated wrapper is a structure that contains only the 99 /// `__ptr` field. The utility operators overloads is added to 100 /// provide better debugging experience. 101 /// 102 /// Wrappers of pointer and reference types are identical except for 103 /// their name -- they are formatted and accessed from a debugger 104 /// the same way. 105 /// 106 /// Notice that "resolve_vmctx_memory_ptr" is external/builtin 107 /// subprogram that is not part of Wasm code. 108 fn replace_pointer_type<R>( 109 parent_id: write::UnitEntryId, 110 kind: WebAssemblyPtrKind, 111 comp_unit: &mut write::Unit, 112 wp_die_id: write::UnitEntryId, 113 pointer_type_entry: &DebuggingInformationEntry<R>, 114 unit: &Unit<R, R::Offset>, 115 context: &DebugInputContext<R>, 116 out_strings: &mut write::StringTable, 117 pending_die_refs: &mut PendingUnitRefs, 118 ) -> Result<write::UnitEntryId, Error> 119 where 120 R: Reader, 121 { 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)] 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, context)? 140 ), 141 WebAssemblyPtrKind::Reference => format!( 142 "WebAssemblyRefWrapper<{}>", 143 get_base_type_name(pointer_type_entry, unit, context)? 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::UnitRef(wp_die_id), 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 = "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("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 = "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("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 = "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("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, R>( 247 dwarf: &gimli::Dwarf<R>, 248 unit: Unit<R, R::Offset>, 249 context: &DebugInputContext<R>, 250 addr_tr: &'a AddressTransform, 251 funcs: &'a CompiledFunctions, 252 memory_offset: &ModuleMemoryOffset, 253 out_encoding: gimli::Encoding, 254 out_units: &mut write::UnitTable, 255 out_strings: &mut write::StringTable, 256 translated: &mut HashSet<DefinedFuncIndex>, 257 isa: &dyn TargetIsa, 258 ) -> Result<Option<(write::UnitId, UnitRefsMap, PendingDebugInfoRefs)>, Error> 259 where 260 R: Reader, 261 { 262 let mut die_ref_map = UnitRefsMap::new(); 263 let mut pending_die_refs = PendingUnitRefs::new(); 264 let mut pending_di_refs = PendingDebugInfoRefs::new(); 265 let mut stack = Vec::new(); 266 267 // Iterate over all of this compilation unit's entries. 268 let mut entries = unit.entries(); 269 let (mut comp_unit, unit_id, file_map, file_index_base, cu_low_pc, wp_die_id, vmctx_die_id) = 270 if let Some((depth_delta, entry)) = entries.next_dfs()? { 271 assert_eq!(depth_delta, 0); 272 let (out_line_program, debug_line_offset, file_map, file_index_base) = 273 clone_line_program( 274 &unit, 275 entry, 276 addr_tr, 277 out_encoding, 278 context.debug_str, 279 context.debug_str_offsets, 280 context.debug_line_str, 281 context.debug_line, 282 out_strings, 283 )?; 284 285 if entry.tag() == gimli::DW_TAG_compile_unit { 286 let unit_id = out_units.add(write::Unit::new(out_encoding, out_line_program)); 287 let comp_unit = out_units.get_mut(unit_id); 288 289 let root_id = comp_unit.root(); 290 die_ref_map.insert(entry.offset(), root_id); 291 292 let cu_low_pc = if let Some(AttributeValue::Addr(addr)) = 293 entry.attr_value(gimli::DW_AT_low_pc)? 294 { 295 addr 296 } else if let Some(AttributeValue::DebugAddrIndex(i)) = 297 entry.attr_value(gimli::DW_AT_low_pc)? 298 { 299 context.debug_addr.get_address(4, unit.addr_base, i)? 300 } else { 301 // FIXME? return Err(TransformError("No low_pc for unit header").into()); 302 0 303 }; 304 305 clone_die_attributes( 306 dwarf, 307 &unit, 308 entry, 309 context, 310 addr_tr, 311 None, 312 comp_unit, 313 root_id, 314 None, 315 None, 316 cu_low_pc, 317 out_strings, 318 &mut pending_die_refs, 319 &mut pending_di_refs, 320 FileAttributeContext::Root(Some(debug_line_offset)), 321 isa, 322 )?; 323 324 let (wp_die_id, vmctx_die_id) = 325 add_internal_types(comp_unit, root_id, out_strings, memory_offset); 326 327 stack.push(root_id); 328 ( 329 comp_unit, 330 unit_id, 331 file_map, 332 file_index_base, 333 cu_low_pc, 334 wp_die_id, 335 vmctx_die_id, 336 ) 337 } else { 338 return Err(TransformError("Unexpected unit header").into()); 339 } 340 } else { 341 return Ok(None); // empty 342 }; 343 let mut skip_at_depth = None; 344 let mut current_frame_base = InheritedAttr::new(); 345 let mut current_value_range = InheritedAttr::new(); 346 let mut current_scope_ranges = InheritedAttr::new(); 347 while let Some((depth_delta, entry)) = entries.next_dfs()? { 348 let depth_delta = if let Some((depth, cached)) = skip_at_depth { 349 let new_depth = depth + depth_delta; 350 if new_depth > 0 { 351 skip_at_depth = Some((new_depth, cached)); 352 continue; 353 } 354 skip_at_depth = None; 355 new_depth + cached 356 } else { 357 depth_delta 358 }; 359 360 if !context 361 .reachable 362 .contains(&entry.offset().to_unit_section_offset(&unit)) 363 { 364 // entry is not reachable: discarding all its info. 365 skip_at_depth = Some((0, depth_delta)); 366 continue; 367 } 368 369 let new_stack_len = stack.len().wrapping_add(depth_delta as usize); 370 current_frame_base.update(new_stack_len); 371 current_scope_ranges.update(new_stack_len); 372 current_value_range.update(new_stack_len); 373 let range_builder = if entry.tag() == gimli::DW_TAG_subprogram { 374 let range_builder = RangeInfoBuilder::from_subprogram_die( 375 dwarf, &unit, entry, context, addr_tr, cu_low_pc, 376 )?; 377 if let RangeInfoBuilder::Function(func_index) = range_builder { 378 if let Some(frame_info) = get_function_frame_info(memory_offset, funcs, func_index) 379 { 380 current_value_range.push(new_stack_len, frame_info); 381 } 382 translated.insert(func_index); 383 current_scope_ranges.push(new_stack_len, range_builder.get_ranges(addr_tr)); 384 Some(range_builder) 385 } else { 386 // FIXME current_scope_ranges.push() 387 None 388 } 389 } else { 390 let high_pc = entry.attr_value(gimli::DW_AT_high_pc)?; 391 let ranges = entry.attr_value(gimli::DW_AT_ranges)?; 392 if high_pc.is_some() || ranges.is_some() { 393 let range_builder = 394 RangeInfoBuilder::from(dwarf, &unit, entry, context, cu_low_pc)?; 395 current_scope_ranges.push(new_stack_len, range_builder.get_ranges(addr_tr)); 396 Some(range_builder) 397 } else { 398 None 399 } 400 }; 401 402 if depth_delta <= 0 { 403 for _ in depth_delta..1 { 404 stack.pop(); 405 } 406 } else { 407 assert_eq!(depth_delta, 1); 408 } 409 410 if let Some(AttributeValue::Exprloc(expr)) = entry.attr_value(gimli::DW_AT_frame_base)? { 411 if let Some(expr) = compile_expression(&expr, unit.encoding(), None)? { 412 current_frame_base.push(new_stack_len, expr); 413 } 414 } 415 416 let parent = stack.last().unwrap(); 417 418 if entry.tag() == gimli::DW_TAG_pointer_type || entry.tag() == gimli::DW_TAG_reference_type 419 { 420 // Wrap pointer types. 421 let pointer_kind = match entry.tag() { 422 gimli::DW_TAG_pointer_type => WebAssemblyPtrKind::Pointer, 423 gimli::DW_TAG_reference_type => WebAssemblyPtrKind::Reference, 424 _ => panic!(), 425 }; 426 let die_id = replace_pointer_type( 427 *parent, 428 pointer_kind, 429 comp_unit, 430 wp_die_id, 431 entry, 432 &unit, 433 context, 434 out_strings, 435 &mut pending_die_refs, 436 )?; 437 stack.push(die_id); 438 assert_eq!(stack.len(), new_stack_len); 439 die_ref_map.insert(entry.offset(), die_id); 440 continue; 441 } 442 443 let die_id = comp_unit.add(*parent, entry.tag()); 444 445 stack.push(die_id); 446 assert_eq!(stack.len(), new_stack_len); 447 die_ref_map.insert(entry.offset(), die_id); 448 449 clone_die_attributes( 450 dwarf, 451 &unit, 452 entry, 453 context, 454 addr_tr, 455 current_value_range.top(), 456 &mut comp_unit, 457 die_id, 458 range_builder, 459 current_scope_ranges.top(), 460 cu_low_pc, 461 out_strings, 462 &mut pending_die_refs, 463 &mut pending_di_refs, 464 FileAttributeContext::Children { 465 file_map: &file_map, 466 file_index_base, 467 frame_base: current_frame_base.top(), 468 }, 469 isa, 470 )?; 471 472 // Data in WebAssembly memory always uses little-endian byte order. 473 // If the native architecture is big-endian, we need to mark all 474 // base types used to refer to WebAssembly memory as little-endian 475 // using the DW_AT_endianity attribute, so that the debugger will 476 // be able to correctly access them. 477 if entry.tag() == gimli::DW_TAG_base_type && isa.endianness() == Endianness::Big { 478 let current_scope = comp_unit.get_mut(die_id); 479 current_scope.set( 480 gimli::DW_AT_endianity, 481 write::AttributeValue::Endianity(gimli::DW_END_little), 482 ); 483 } 484 485 if entry.tag() == gimli::DW_TAG_subprogram && !current_scope_ranges.is_empty() { 486 append_vmctx_info( 487 comp_unit, 488 die_id, 489 vmctx_die_id, 490 addr_tr, 491 current_value_range.top(), 492 current_scope_ranges.top().context("range")?, 493 out_strings, 494 isa, 495 )?; 496 } 497 } 498 die_ref_map.patch(pending_die_refs, comp_unit); 499 Ok(Some((unit_id, die_ref_map, pending_di_refs))) 500 } 501