1use std::collections::BTreeMap;
4
5use anyhow::{Context, Result};
6use rudy_dwarf::{Die, types::DieTypeDefinition};
7use rudy_types::{
8 ArrayLayout, BTreeNodeLayout, CEnumLayout, EnumLayout, Layout, MapLayout, MapVariant,
9 OptionLayout, PointerLayout, PrimitiveLayout, ReferenceLayout, SliceLayout, SmartPtrVariant,
10 StdLayout, StrSliceLayout, VecLayout,
11};
12
13use crate::{Value, database::Db, outputs::TypedPointer};
14
15pub trait DataResolver {
23 fn read_memory(&self, address: u64, size: usize) -> Result<Vec<u8>> {
34 Err(anyhow::anyhow!(
35 "read_memory({address:#x}, {size}) not implemented for this DataResolver",
36 ))
37 }
38
39 fn read_address(&self, address: u64) -> Result<u64> {
51 let data = self.read_memory(address, std::mem::size_of::<u64>())?;
52 if data.len() != std::mem::size_of::<u64>() {
53 return Err(anyhow::anyhow!("Failed to read address"));
54 }
55 let addr = u64::from_le_bytes(data.try_into().unwrap());
56 tracing::trace!("read raw address: {addr:#x}");
57 Ok(addr)
58 }
59 fn get_register(&self, idx: usize) -> Result<u64> {
69 Err(anyhow::anyhow!(
70 "get_register({idx}) not implemented for this DataResolver"
71 ))
72 }
73
74 fn get_stack_pointer(&self) -> Result<u64> {
75 Err(anyhow::anyhow!("get_stack_pointer() not implemented"))
76 }
77
78 fn allocate_memory(&self, size: usize) -> Result<u64> {
88 Err(anyhow::anyhow!(
89 "allocate_memory({size:#x}) not implemented"
90 ))
91 }
92
93 fn write_memory(&self, address: u64, data: &[u8]) -> Result<()> {
100 let _ = data;
101 Err(anyhow::anyhow!(
102 "write_memory({address:#x}, &[..]) not implemented"
103 ))
104 }
105}
106
107pub(crate) struct DataResolverExpressionContext<'a, T: ?Sized>(pub &'a T);
108
109impl<'a, R: DataResolver + ?Sized> rudy_dwarf::expressions::ExpressionContext
110 for DataResolverExpressionContext<'a, R>
111{
112 fn get_register(&self, register: u16) -> Result<u64> {
113 self.0.get_register(register as usize)
114 }
115
116 fn get_stack_pointer(&self) -> Result<u64> {
117 self.0.get_stack_pointer()
118 }
119}
120
121pub fn read_map_entries(
123 address: u64,
124 def: &MapLayout<Die>,
125 data_resolver: &dyn crate::DataResolver,
126) -> Result<Vec<(TypedPointer, TypedPointer)>> {
127 tracing::trace!("read_map_entries {address:#x} {}", def.display_name());
128
129 match def.variant.clone() {
130 MapVariant::HashMap {
131 bucket_mask_offset,
132 ctrl_offset,
133 items_offset,
134 pair_size,
135 key_offset,
136 value_offset,
137 } => {
138 let bucket_mask_address = address + bucket_mask_offset as u64;
139 let ctrl_address = address + ctrl_offset as u64;
140 let items_address = address + items_offset as u64;
141 let items = data_resolver.read_memory(items_address, 8)?;
143 let items = usize::from_le_bytes(items.try_into().unwrap());
144
145 if items == 0 {
146 return Ok(vec![]);
147 }
148
149 tracing::trace!(
150 "reading HashMap at {address:#x}, items: {items}, bucket_mask_addr: {bucket_mask_address:#x}, ctrl_addr: {ctrl_address:#x}"
151 );
152
153 let bucket_mask = data_resolver.read_memory(bucket_mask_address, 8)?;
155 let capacity = usize::from_le_bytes(bucket_mask.try_into().unwrap()) + 1;
156
157 let ctrl_ptr = data_resolver.read_address(ctrl_address)?;
159
160 tracing::trace!(
161 "HashMap capacity: {capacity}, ctrl_ptr: {ctrl_ptr:#x}, items: {items}"
162 );
163
164 let mut slot_addr = ctrl_ptr - pair_size as u64;
167
168 let ctrl_bytes = data_resolver.read_memory(ctrl_ptr, capacity)?;
170
171 let mut entries = Vec::new();
172
173 for &ctrl in &ctrl_bytes {
174 if ctrl < 0x80 {
175 let key = TypedPointer {
177 address: slot_addr + key_offset as u64,
178 type_def: def.key_type.clone(),
179 };
180 let value = TypedPointer {
181 address: slot_addr + value_offset as u64,
182 type_def: def.value_type.clone(),
183 };
184
185 entries.push((key, value));
186
187 if entries.len() >= items {
189 break;
190 }
191 }
192 slot_addr -= pair_size as u64;
194 }
195 Ok(entries)
196 }
197 MapVariant::BTreeMap {
198 length_offset,
199 root_offset,
200 root_layout,
201 node_layout,
202 } => {
203 let length_addr = address + length_offset as u64;
205 let length_bytes = data_resolver.read_memory(length_addr, 8)?;
206 let length = usize::from_le_bytes(length_bytes.try_into().unwrap());
207
208 tracing::trace!("BTreeMap at {address:#x}, length: {length}");
209
210 if length == 0 {
211 return Ok(vec![]);
212 }
213
214 let root_addr = address + root_offset as u64;
216
217 let discriminant_bytes = data_resolver.read_memory(root_addr, 8)?;
219 let discriminant = u64::from_le_bytes(discriminant_bytes.try_into().unwrap());
220
221 if discriminant == 0 {
222 return Ok(vec![]);
224 }
225
226 let node_ptr_addr = root_addr + root_layout.node_offset as u64;
228 let height_addr = root_addr + root_layout.height_offset as u64;
229
230 let node_ptr = data_resolver.read_address(node_ptr_addr)?;
231 let height_bytes = data_resolver.read_memory(height_addr, 8)?;
232 let height = usize::from_le_bytes(height_bytes.try_into().unwrap());
233
234 tracing::trace!("BTreeMap root node: {node_ptr:#x}, height: {height}");
235
236 let mut entries = Vec::new();
238 read_btree_node_entries(
239 node_ptr,
240 height,
241 &def.key_type,
242 &def.value_type,
243 &node_layout,
244 data_resolver,
245 &mut entries,
246 )?;
247
248 Ok(entries)
249 }
250 MapVariant::IndexMap => {
251 unimplemented!(
252 "read_std_from_memory: MapVariant::IndexMap not implemented yet: {def:#?}"
253 )
254 }
255 }
256}
257
258fn read_enum(
259 db: &dyn Db,
260 address: u64,
261 enum_def: &EnumLayout<Die>,
262 data_resolver: &dyn crate::DataResolver,
263) -> Result<Value> {
264 tracing::trace!("read_enum {address:#x} {enum_def:#?}");
265
266 let EnumLayout {
267 name,
268 variants,
269 discriminant,
270 ..
271 } = enum_def;
272
273 let disc_offset = discriminant.offset as u64;
274 let disc_address = address + disc_offset;
275
276 let explicit_disc = !matches!(discriminant.ty, rudy_types::DiscriminantType::Implicit);
277
278 let disc_value = match &discriminant.ty {
279 rudy_types::DiscriminantType::Int(int_def) => {
280 let memory = data_resolver.read_memory(disc_address, int_def.size)?;
281
282 match int_def.size {
283 1 => i8::from_le_bytes(memory.try_into().unwrap()) as i128,
284 2 => i16::from_le_bytes(memory.try_into().unwrap()) as i128,
285 4 => i32::from_le_bytes(memory.try_into().unwrap()) as i128,
286 8 => i64::from_le_bytes(memory.try_into().unwrap()) as i128,
287 _ => {
288 anyhow::bail!(
289 "read_primitive_from_memory: unsupported IntDef size {} at address {address:#x}",
290 int_def.size
291 )
292 }
293 }
294 }
295 rudy_types::DiscriminantType::UnsignedInt(unsigned_int_def) => {
296 let memory = data_resolver.read_memory(disc_address, unsigned_int_def.size)?;
297 match unsigned_int_def.size {
298 1 => u8::from_le_bytes(memory.try_into().unwrap()) as i128,
299 2 => u16::from_le_bytes(memory.try_into().unwrap()) as i128,
300 4 => u32::from_le_bytes(memory.try_into().unwrap()) as i128,
301 8 => u64::from_le_bytes(memory.try_into().unwrap()) as i128,
302 _ => {
303 anyhow::bail!(
304 "read_primitive_from_memory: unsupported UnsignedIntDef size {} at address {address:#x}",
305 unsigned_int_def.size
306 )
307 }
308 }
309 }
310 rudy_types::DiscriminantType::Implicit => {
311 let memory = data_resolver.read_memory(disc_address + disc_offset, 4)?;
313 i32::from_le_bytes(memory.try_into().unwrap()) as i128
314 }
315 };
316
317 tracing::trace!("read_enum: at {disc_address:#x}, discriminant value: {disc_value}");
318
319 let maybe_variant = variants.iter().enumerate().find(|(i, v)| {
320 if explicit_disc {
321 v.discriminant == Some(disc_value)
324 } else {
325 *i as i128 == disc_value
327 }
328 });
329
330 let matching_variant = match (maybe_variant, explicit_disc) {
331 (Some((_, v)), _) => v,
332 (None, true) => {
333 variants.iter().find(|v| v.discriminant.is_none()).with_context(|| {
336 format!(
337 "read_enum: No matching variant found for discriminant value {disc_value} in {name} with explicit discriminant"
338 )
339 })?
340 }
341 (None, false) => {
342 anyhow::bail!(
343 "read_enum: No matching variant found for discriminant value {disc_value} in {name} with implicit discriminant"
344 )
345 }
346 };
347
348 tracing::trace!("found matching variant: {matching_variant:#?}");
349
350 let inner = read_from_memory(db, address, &matching_variant.layout, data_resolver)?;
352
353 Ok(match inner {
355 Value::Struct { fields, .. } => {
356 if fields.is_empty() {
357 Value::Scalar {
359 ty: format!("{name}::{}", matching_variant.name),
360 value: matching_variant.name.to_string(),
361 }
362 } else if fields.keys().all(|k| k.starts_with("__")) {
363 let values: Vec<_> = fields.into_values().collect();
365 Value::Tuple {
366 ty: format!("{name}::{}", matching_variant.name),
367 entries: values,
368 }
369 } else {
370 Value::Struct {
372 ty: format!("{name}::{}", matching_variant.name),
373 fields,
374 }
375 }
376 }
377 v => {
378 tracing::error!("read_enum: Expected a struct for enum variant, got: {v:#?}");
380 v
381 }
382 })
383}
384
385fn read_c_enum(
386 address: u64,
387 c_enum_def: &CEnumLayout,
388 data_resolver: &dyn crate::DataResolver,
389) -> Result<Value> {
390 let CEnumLayout {
391 name,
392 discriminant_type,
393 variants,
394 ..
395 } = c_enum_def;
396
397 let disc_value = match discriminant_type {
398 rudy_types::DiscriminantType::Int(int_def) => {
399 let memory = data_resolver.read_memory(address, int_def.size)?;
400
401 match int_def.size {
402 1 => i8::from_le_bytes(memory.try_into().unwrap()) as i128,
403 2 => i16::from_le_bytes(memory.try_into().unwrap()) as i128,
404 4 => i32::from_le_bytes(memory.try_into().unwrap()) as i128,
405 8 => i64::from_le_bytes(memory.try_into().unwrap()) as i128,
406 _ => {
407 anyhow::bail!(
408 "read_primitive_from_memory: unsupported IntDef size {} at address {address:#x}",
409 int_def.size
410 )
411 }
412 }
413 }
414 rudy_types::DiscriminantType::UnsignedInt(unsigned_int_def) => {
415 let memory = data_resolver.read_memory(address, unsigned_int_def.size)?;
416 match unsigned_int_def.size {
417 1 => u8::from_le_bytes(memory.try_into().unwrap()) as i128,
418 2 => u16::from_le_bytes(memory.try_into().unwrap()) as i128,
419 4 => u32::from_le_bytes(memory.try_into().unwrap()) as i128,
420 8 => u64::from_le_bytes(memory.try_into().unwrap()) as i128,
421 _ => {
422 anyhow::bail!(
423 "read_primitive_from_memory: unsupported UnsignedIntDef size {} at address {address:#x}",
424 unsigned_int_def.size
425 )
426 }
427 }
428 }
429 rudy_types::DiscriminantType::Implicit => {
430 anyhow::bail!("read_c_enum: Implicit discriminant type is not supported yet")
431 }
432 };
433
434 let matching_variant = variants
435 .iter()
436 .find_map(|v| (v.value == disc_value).then_some(&v.name))
437 .ok_or_else(|| {
438 anyhow::anyhow!(
439 "read_c_enum: No matching variant found for discriminant value {disc_value} in {name}"
440 )
441 })?;
442
443 Ok(Value::Scalar {
444 ty: format!("{name}::{matching_variant}"),
445 value: disc_value.to_string(),
446 })
447}
448
449pub fn read_from_memory(
450 db: &dyn Db,
451 address: u64,
452 ty: &DieTypeDefinition,
453 data_resolver: &dyn crate::DataResolver,
454) -> Result<Value> {
455 tracing::trace!("read_from_memory {address:#x} {}", ty.display_name());
456 match ty.layout.as_ref() {
457 Layout::Primitive(primitive_def) => {
458 read_primitive_from_memory(db, address, primitive_def, data_resolver)
459 }
460 Layout::Struct(struct_def) => {
461 let mut fields = BTreeMap::new();
462 for field in &struct_def.fields {
463 let field_name = &field.name;
464 let field_ty = &field.ty;
465 let field_address = address + (field.offset as u64);
466 let field_value = Value::Pointer(TypedPointer {
468 address: field_address,
469 type_def: field_ty.clone(),
470 });
471 fields.insert(field_name.to_string(), field_value);
472 }
473 Ok(Value::Struct {
474 ty: struct_def.name.clone(),
475 fields,
476 })
477 }
478 Layout::Std(std_def) => read_std_from_memory(db, address, std_def, data_resolver),
479 Layout::Enum(enum_def) => read_enum(db, address, enum_def, data_resolver),
480 Layout::CEnum(c_enum_def) => read_c_enum(address, c_enum_def, data_resolver),
481 Layout::Alias { .. } => {
482 let underlying_type = rudy_dwarf::types::resolve_type_offset(db, ty.location)?;
484 read_from_memory(db, address, &underlying_type, data_resolver)
486 }
487 }
488}
489
490pub fn extract_vec_info(
492 base_address: u64,
493 def: &VecLayout<Die>,
494 data_resolver: &dyn crate::DataResolver,
495) -> Result<(u64, usize)> {
496 let VecLayout {
497 length_offset,
498 data_ptr_offset,
499 ..
500 } = def;
501 let length = data_resolver
502 .read_memory(base_address + *length_offset as u64, 8)?
503 .try_into()
504 .map(usize::from_le_bytes)
505 .map_err(|_| {
506 anyhow::anyhow!("Failed to read length for Vec at address {base_address:#x}")
507 })?;
508 tracing::trace!("Vec length: {length}");
509 let address = data_resolver
510 .read_address(base_address + *data_ptr_offset as u64)
511 .with_context(|| {
512 format!(
513 "Failed to read Vec data pointer at {:#x}",
514 base_address + *data_ptr_offset as u64
515 )
516 })?;
517 Ok((address, length))
518}
519
520fn read_primitive_from_memory(
521 db: &dyn Db,
522 address: u64,
523 def: &PrimitiveLayout<Die>,
524 data_resolver: &dyn crate::DataResolver,
525) -> Result<Value> {
526 let value = match def {
527 PrimitiveLayout::Bool(_) => {
528 let memory = data_resolver.read_memory(address, 1)?;
529 let bool_value = memory[0] != 0;
530 Value::Scalar {
531 ty: "bool".to_string(),
532 value: bool_value.to_string(),
533 }
534 }
535 PrimitiveLayout::Char(()) => {
536 let memory = data_resolver.read_memory(address, 4)?;
537 let char_value = char::from_u32(u32::from_le_bytes(memory.try_into().unwrap()))
538 .ok_or_else(|| anyhow::anyhow!("Invalid char value at address {address:#x}"))?;
539 Value::Scalar {
540 ty: "char".to_string(),
541 value: format!("'{char_value}'"),
542 }
543 }
544 PrimitiveLayout::Function(function_def) => Value::Scalar {
545 ty: function_def.display_name(),
546 value: format!("fn at {address:#x}"),
547 },
548 PrimitiveLayout::Array(ArrayLayout {
549 element_type,
550 length,
551 }) => {
552 let element_type = resolve_alias(db, element_type)?;
553 let element_size = element_type.size().with_context(|| {
554 format!(
555 "inner type: {} has unknown size",
556 element_type.display_name()
557 )
558 })? as u64;
559
560 let mut address = address;
561 let mut values = Vec::with_capacity(*length);
562 for _ in 0..*length {
563 let value = Value::Pointer(TypedPointer {
565 address,
566 type_def: element_type.clone(),
567 });
568 values.push(value);
569 address += element_size;
570 }
571 Value::Array {
572 ty: format!("[{}; {length}]", element_type.display_name()),
573 items: values,
574 }
575 }
576 PrimitiveLayout::Pointer(PointerLayout { pointed_type, .. }) => {
577 let address = data_resolver.read_address(address)?;
578 read_from_memory(db, address, pointed_type, data_resolver)?.prefix_type("*")
579 }
580 PrimitiveLayout::Reference(ReferenceLayout { pointed_type, .. }) => {
581 let address = data_resolver.read_address(address)?;
582 read_from_memory(db, address, pointed_type, data_resolver)?.prefix_type("&")
583 }
584 PrimitiveLayout::Slice(SliceLayout {
585 element_type,
586 data_ptr_offset,
587 length_offset,
588 }) => {
589 let length = address + *length_offset as u64;
590 let length_bytes = data_resolver.read_memory(length, 8)?;
591 let length = u64::from_le_bytes(length_bytes.try_into().unwrap());
592 tracing::trace!("length: {length}");
593
594 let element_type = resolve_alias(db, element_type)?;
595
596 let element_size = element_type.size().with_context(|| {
597 format!(
598 "inner type: {} has unknown size",
599 element_type.display_name()
600 )
601 })? as u64;
602 let data_ptr_address = data_resolver.read_address(address + *data_ptr_offset as u64)?;
603 let mut current_addr = data_ptr_address;
604 let mut values = Vec::with_capacity(length as usize);
605 for _ in 0..length {
606 let value = Value::Pointer(TypedPointer {
608 address: current_addr,
609 type_def: element_type.clone(),
610 });
611 values.push(value);
612 current_addr += element_size;
613 }
614 Value::Array {
615 ty: format!("&[{}]", element_type.display_name()),
616 items: values,
617 }
618 }
619 PrimitiveLayout::StrSlice(StrSliceLayout {
620 data_ptr_offset,
621 length_offset,
622 }) => {
623 let data_ptr = address + *data_ptr_offset as u64;
624 let length = address + *length_offset as u64;
625
626 let data_address = data_resolver.read_address(data_ptr)?;
627 let memory = data_resolver.read_memory(length, 8)?;
628 let length = u64::from_le_bytes(memory.try_into().unwrap());
629 tracing::trace!("length: {length}");
630
631 let memory = data_resolver.read_memory(data_address, length as usize)?;
632 let string_value = String::from_utf8_lossy(&memory).to_string();
633 Value::Scalar {
634 ty: "str".to_string(),
635 value: format!("\"{string_value}\""),
636 }
637 }
638 PrimitiveLayout::UnsignedInt(unsigned_int_def) => {
639 let memory = data_resolver.read_memory(address, unsigned_int_def.size)?;
640
641 let num_string = match unsigned_int_def.size {
642 1 => u8::from_le_bytes(memory.try_into().unwrap()).to_string(),
643 2 => u16::from_le_bytes(memory.try_into().unwrap()).to_string(),
644 4 => u32::from_le_bytes(memory.try_into().unwrap()).to_string(),
645 8 => u64::from_le_bytes(memory.try_into().unwrap()).to_string(),
646 16 => u128::from_le_bytes(memory.try_into().unwrap()).to_string(),
647 _ => {
648 anyhow::bail!(
649 "read_primitive_from_memory: unsupported UnsignedIntDef size {} at address {address:#x}",
650 unsigned_int_def.size
651 )
652 }
653 };
654 Value::Scalar {
655 ty: unsigned_int_def.display_name(),
656 value: num_string,
657 }
658 }
659 PrimitiveLayout::Float(float_def) => {
660 let memory = data_resolver.read_memory(address, float_def.size)?;
661
662 let num_string = match float_def.size {
663 4 => f32::from_le_bytes(memory.try_into().unwrap()).to_string(),
664 8 => f64::from_le_bytes(memory.try_into().unwrap()).to_string(),
665 16 => {
666 anyhow::bail!("f128 is not supported yet, found at address {address:#x}");
667 }
668 _ => {
669 anyhow::bail!(
670 "read_primitive_from_memory: unsupported FloatDef size {} at address {address:#x}",
671 float_def.size
672 )
673 }
674 };
675 Value::Scalar {
676 ty: format!("f{}", float_def.size * 8),
677 value: num_string,
678 }
679 }
680 PrimitiveLayout::Int(int_def) => {
681 let memory = data_resolver.read_memory(address, int_def.size)?;
682
683 let num_string = match int_def.size {
684 1 => i8::from_le_bytes(memory.try_into().unwrap()).to_string(),
685 2 => i16::from_le_bytes(memory.try_into().unwrap()).to_string(),
686 4 => i32::from_le_bytes(memory.try_into().unwrap()).to_string(),
687 8 => i64::from_le_bytes(memory.try_into().unwrap()).to_string(),
688 16 => i128::from_le_bytes(memory.try_into().unwrap()).to_string(),
689 _ => {
690 anyhow::bail!(
691 "read_primitive_from_memory: unsupported IntDef size {} at address {address:#x}",
692 int_def.size
693 )
694 }
695 };
696 Value::Scalar {
697 ty: int_def.display_name(),
698 value: num_string,
699 }
700 }
701 PrimitiveLayout::Never(_) => {
702 Value::Scalar {
704 ty: "Never".to_string(),
705 value: "unreachable".to_string(),
706 }
707 }
708 PrimitiveLayout::Str(()) => {
709 unimplemented!("read_primitive_from_memory: bare `str` is not supported yet");
710 }
711 PrimitiveLayout::Tuple(tuple_def) => {
712 unimplemented!(
713 "read_primitive_from_memory: TupleDef not implemented yet: {tuple_def:#?}"
714 );
715 }
716 PrimitiveLayout::Unit(_) => {
717 Value::Scalar {
719 ty: "()".to_string(),
720 value: "()".to_string(),
721 }
722 }
723 };
724
725 Ok(value)
726}
727
728fn resolve_alias(db: &dyn Db, def: &DieTypeDefinition) -> Result<DieTypeDefinition> {
729 if let Layout::Alias { name } = def.layout.as_ref() {
730 rudy_dwarf::types::resolve_type_offset(db, def.location)
731 .with_context(|| format!("Failed to resolve alias for {name}"))
732 } else {
733 Ok(def.clone())
734 }
735}
736
737fn read_option_from_memory(
738 db: &dyn Db,
739 address: u64,
740 opt_def: &OptionLayout<Die>,
741 data_resolver: &dyn crate::DataResolver,
742) -> Result<Value> {
743 let OptionLayout {
744 discriminant,
745 some_offset,
746 some_type,
747 ..
748 } = opt_def;
749
750 let first_byte = data_resolver.read_memory(
751 address + opt_def.discriminant.offset as u64,
752 discriminant.size(),
753 )?;
754 Ok(if first_byte[0] == 0 {
755 Value::Scalar {
756 ty: some_type.display_name(),
757 value: "None".to_string(),
758 }
759 } else {
760 tracing::debug!("Found Some variant at {address:#x}: {some_type:#?}");
761 read_from_memory(db, address + *some_offset as u64, some_type, data_resolver)?
764 }
765 .wrap_type("Option"))
766}
767
768fn read_std_from_memory(
769 db: &dyn Db,
770 address: u64,
771 def: &StdLayout<Die>,
772 data_resolver: &dyn crate::DataResolver,
773) -> Result<Value> {
774 let value = match def {
775 StdLayout::Option(enum_def) => {
776 tracing::trace!("reading Option at {address:#x}");
777 read_option_from_memory(db, address, enum_def, data_resolver)?
778 }
779 StdLayout::Vec(
780 v @ VecLayout {
781 length_offset,
782 data_ptr_offset,
783 inner_type,
784 ..
785 },
786 ) => {
787 tracing::trace!(
788 "reading Vec at {address:#x}, length_offset: {length_offset:#x}, data_ptr_offset: {data_ptr_offset:#x}",
789 );
790 let element_type = resolve_alias(db, inner_type)?;
791
792 let element_size = element_type.size().with_context(|| {
793 format!(
794 "inner type: {} has unknown size",
795 element_type.display_name()
796 )
797 })? as u64;
798
799 let (mut address, length) = extract_vec_info(address, v, data_resolver)?;
800 let mut values = Vec::with_capacity(length);
801 tracing::trace!("reading Vec data at {address:#016x}");
802 for _ in 0..length {
803 let value = Value::Pointer(TypedPointer {
805 address,
806 type_def: element_type.clone(),
807 });
808 values.push(value);
809 address += element_size;
810 }
811 Value::Array {
812 ty: format!("Vec<{}>", element_type.display_name()),
813 items: values,
814 }
815 }
816 StdLayout::String(s) => {
817 let v = &s.0;
818 tracing::trace!(
819 "reading String length at {:#x}",
820 address + v.length_offset as u64
821 );
822 let length = data_resolver
823 .read_memory(address + v.length_offset as u64, 8)?
824 .try_into()
825 .map(usize::from_le_bytes)
826 .map_err(|_| {
827 anyhow::anyhow!("Failed to read length for Vec at address {address:#x}")
828 })?;
829 tracing::trace!("String length: {length}");
830 let data_address = address + s.0.data_ptr_offset as u64;
832 let data = data_resolver.read_address(data_address).with_context(|| {
833 format!("Failed to read String data pointer at {data_address:#x}")
834 })?;
835 tracing::trace!("reading String data at {data:#016x}");
836 let bytes = data_resolver.read_memory(data, length)?;
837 let value = String::from_utf8_lossy(&bytes).to_string();
838 Value::Scalar {
839 ty: "String".to_string(),
840 value: format!("\"{value}\""),
841 }
842 }
843 StdLayout::Map(def) => {
844 let entries = read_map_entries(address, def, data_resolver)?
845 .into_iter()
846 .map(|(key, value)| Ok((Value::Pointer(key), Value::Pointer(value))))
847 .collect::<Result<Vec<_>>>()?;
848 Value::Map {
849 ty: def.display_name(),
850 entries,
851 }
852 }
853 StdLayout::SmartPtr(s) => match s.variant {
854 SmartPtrVariant::Mutex | SmartPtrVariant::RefCell => {
855 let inner_type = s.inner_type.clone();
856 let address = address + s.inner_ptr_offset as u64;
857 read_from_memory(db, address, &inner_type, data_resolver)?
858 .wrap_type(s.variant.name())
859 }
860 SmartPtrVariant::Box => {
861 let inner_type = s.inner_type.clone();
862 let address = data_resolver.read_address(address)?;
863 read_from_memory(db, address, &inner_type, data_resolver)?
864 .wrap_type(s.variant.name())
865 }
866 SmartPtrVariant::Rc | SmartPtrVariant::Arc => {
867 let inner_type = s.inner_type.clone();
868 let inner_address =
869 data_resolver.read_address(address + s.inner_ptr_offset as u64)?;
870 let data_address = inner_address + s.data_ptr_offset as u64;
871 read_from_memory(db, data_address, &inner_type, data_resolver)?
872 .wrap_type(s.variant.name())
873 }
874 _ => {
875 unimplemented!("read_std_from_memory: SmartPtrVariant not implemented yet: {s:#?}")
876 }
877 },
878 StdLayout::Result(result_def) => {
879 unimplemented!("read_std_from_memory: ResultDef not implemented yet: {result_def:#?}")
880 }
881 };
882
883 Ok(value)
884}
885
886#[allow(clippy::too_many_arguments)]
888fn read_btree_node_entries(
889 node_ptr: u64,
890 height: usize,
891 key_type: &DieTypeDefinition,
892 value_type: &DieTypeDefinition,
893 node_layout: &BTreeNodeLayout,
894 data_resolver: &dyn crate::DataResolver,
895 entries: &mut Vec<(TypedPointer, TypedPointer)>,
896) -> Result<()> {
897 tracing::trace!("read_btree_node_entries at {node_ptr:#x}, height: {height}");
898
899 let len_addr = node_ptr + node_layout.len_offset as u64;
901 let len_bytes = data_resolver.read_memory(len_addr, 2)?; let len = u16::from_le_bytes(len_bytes.try_into().unwrap()) as usize;
903
904 tracing::trace!("Node at {node_ptr:#x} has {len} entries");
905
906 let key_size = key_type
908 .size()
909 .ok_or_else(|| anyhow::anyhow!("Cannot determine key size for BTreeMap"))?;
910 let value_size = value_type
911 .size()
912 .ok_or_else(|| anyhow::anyhow!("Cannot determine value size for BTreeMap"))?;
913
914 let keys_addr = node_ptr + node_layout.keys_offset as u64;
916 let vals_addr = node_ptr + node_layout.vals_offset as u64;
917
918 let edges_addr = if height > 0 {
920 Some(node_ptr + node_layout.edges_offset as u64)
921 } else {
922 None
923 };
924
925 for i in 0..=len {
927 if let Some(edges_base) = edges_addr
929 && i <= len
930 {
931 let edge_addr = edges_base + (i * 8) as u64; let edge_ptr = data_resolver.read_address(edge_addr)?;
935
936 if edge_ptr != 0 {
937 read_btree_node_entries(
939 edge_ptr,
940 height - 1,
941 key_type,
942 value_type,
943 node_layout,
944 data_resolver,
945 entries,
946 )?;
947 }
948 }
949
950 if i < len {
952 if key_size > 0 && value_size > 0 {
955 let key_addr = keys_addr + (i * key_size) as u64;
956 let value_addr = vals_addr + (i * value_size) as u64;
957
958 let key_ptr = TypedPointer {
959 address: key_addr,
960 type_def: key_type.clone(),
961 };
962
963 let value_ptr = TypedPointer {
964 address: value_addr,
965 type_def: value_type.clone(),
966 };
967
968 entries.push((key_ptr, value_ptr));
969 }
970 }
971 }
972
973 Ok(())
974}