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