1use super::context::{CodeGenError, EbpfContext, Result};
7use ghostscope_dwarf::{
8 ComputeStep, DirectValueResult, EvaluationResult, LocationResult, MemoryAccessSize, TypeInfo,
9 VariableWithEvaluation,
10};
11use ghostscope_process::module_probe;
12use inkwell::values::{BasicValueEnum, IntValue, PointerValue};
13use tracing::{debug, warn};
14
15impl<'ctx, 'dw> EbpfContext<'ctx, 'dw> {
16 fn fallback_cookie_from_module_path(&self, module_path: &str) -> u64 {
18 module_probe::cookie_for_path(module_path)
19 }
20
21 fn section_code_for_address(&mut self, module_path: &str, link_addr: u64) -> u8 {
23 if let Some(analyzer) = self.process_analyzer {
24 if let Some(st) = analyzer.classify_section_for_address(module_path, link_addr) {
25 return match st {
26 ghostscope_dwarf::core::SectionType::Text => 0,
27 ghostscope_dwarf::core::SectionType::Rodata => 1,
28 ghostscope_dwarf::core::SectionType::Data => 2,
29 ghostscope_dwarf::core::SectionType::Bss => 3,
30 _ => 2,
31 };
32 }
33 }
34 2
35 }
36
37 fn cookie_for_module_or_fallback(&mut self, module_path: &str) -> u64 {
39 self.fallback_cookie_from_module_path(module_path)
40 }
41 fn unwrap_type_aliases(mut t: &TypeInfo) -> &TypeInfo {
43 loop {
44 match t {
45 TypeInfo::TypedefType {
46 underlying_type, ..
47 } => t = underlying_type.as_ref(),
48 TypeInfo::QualifiedType {
49 underlying_type, ..
50 } => t = underlying_type.as_ref(),
51 _ => break,
52 }
53 }
54 t
55 }
56
57 fn is_aggregate_type(&self, t: &TypeInfo) -> bool {
59 matches!(
60 Self::unwrap_type_aliases(t),
61 TypeInfo::StructType { .. } | TypeInfo::UnionType { .. } | TypeInfo::ArrayType { .. }
62 )
63 }
64 pub fn evaluate_result_to_llvm_value(
66 &mut self,
67 evaluation_result: &EvaluationResult,
68 dwarf_type: &TypeInfo,
69 var_name: &str,
70 pc_address: u64,
71 status_ptr: Option<PointerValue<'ctx>>,
72 ) -> Result<BasicValueEnum<'ctx>> {
73 debug!(
74 "Converting EvaluationResult to LLVM value for variable: {}",
75 var_name
76 );
77 debug!("Evaluation context PC address: 0x{:x}", pc_address);
78
79 let pt_regs_ptr = self.get_pt_regs_parameter()?;
81
82 match evaluation_result {
83 EvaluationResult::DirectValue(direct) => {
84 self.generate_direct_value(direct, pt_regs_ptr)
85 }
86 EvaluationResult::MemoryLocation(location) => {
87 self.generate_memory_location(location, pt_regs_ptr, dwarf_type, status_ptr)
88 }
89 EvaluationResult::Optimized => {
90 debug!("Variable {} is optimized out", var_name);
91 Ok(self.context.i64_type().const_zero().into())
93 }
94 EvaluationResult::Composite(members) => {
95 debug!(
96 "Variable {} is composite with {} members",
97 var_name,
98 members.len()
99 );
100 if let Some(first_member) = members.first() {
102 self.evaluate_result_to_llvm_value(
103 &first_member.location,
104 dwarf_type,
105 var_name,
106 pc_address,
107 status_ptr,
108 )
109 } else {
110 Ok(self.context.i64_type().const_zero().into())
111 }
112 }
113 }
114 }
115
116 pub fn evaluation_result_to_address_with_hint(
118 &mut self,
119 evaluation_result: &EvaluationResult,
120 status_ptr: Option<PointerValue<'ctx>>,
121 module_hint: Option<&str>,
122 ) -> Result<IntValue<'ctx>> {
123 let pt_regs_ptr = self.get_pt_regs_parameter()?;
130 self.store_offsets_found_const(true)?;
132
133 match evaluation_result {
134 EvaluationResult::MemoryLocation(LocationResult::Address(addr)) => {
135 let ctx = self.get_compile_time_context()?;
137 let module_for_offsets = module_hint
138 .map(|s| s.to_string())
139 .or_else(|| self.current_resolved_var_module_path.clone())
140 .unwrap_or_else(|| ctx.module_path.clone());
141 let st_code = self.section_code_for_address(&module_for_offsets, *addr);
142 let cookie = self.cookie_for_module_or_fallback(&module_for_offsets);
143 let link_val = self.context.i64_type().const_int(*addr, false);
144 let (rt_addr, found_flag) =
145 self.generate_runtime_address_from_offsets(link_val, st_code, cookie)?;
146 if let Some(sp) = status_ptr {
147 let is_miss = self
148 .builder
149 .build_int_compare(
150 inkwell::IntPredicate::EQ,
151 found_flag,
152 self.context.bool_type().const_zero(),
153 "is_off_miss",
154 )
155 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
156 let cur_status = self
157 .builder
158 .build_load(self.context.i8_type(), sp, "cur_status")
159 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
160 let is_ok = self
161 .builder
162 .build_int_compare(
163 inkwell::IntPredicate::EQ,
164 cur_status.into_int_value(),
165 self.context.i8_type().const_zero(),
166 "status_is_ok",
167 )
168 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
169 let should_store = self
170 .builder
171 .build_and(is_miss, is_ok, "store_offsets_unavail")
172 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
173 let new_status = self
174 .builder
175 .build_select(
176 should_store,
177 self.context
178 .i8_type()
179 .const_int(
180 ghostscope_protocol::VariableStatus::OffsetsUnavailable as u64,
181 false,
182 )
183 .into(),
184 cur_status,
185 "new_status",
186 )
187 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
188 self.builder
189 .build_store(sp, new_status)
190 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
191 }
192 self.store_offsets_found_flag(found_flag)?;
193 self.current_resolved_var_module_path = None;
194 Ok(rt_addr)
195 }
196 EvaluationResult::MemoryLocation(LocationResult::RegisterAddress {
197 register,
198 offset,
199 ..
200 }) => {
201 let reg_val = self.load_register_value(*register, pt_regs_ptr)?;
202 if let BasicValueEnum::IntValue(reg_i) = reg_val {
203 if let Some(ofs) = offset {
204 let ofs_val = self.context.i64_type().const_int(*ofs as u64, true);
205 let sum = self
206 .builder
207 .build_int_add(reg_i, ofs_val, "addr_with_offset")
208 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
209 Ok(sum)
210 } else {
211 Ok(reg_i)
212 }
213 } else {
214 Err(CodeGenError::RegisterMappingError(
215 "Register value is not integer".to_string(),
216 ))
217 }
218 }
219 EvaluationResult::MemoryLocation(LocationResult::ComputedLocation { steps }) => {
220 let mut const_stack: Vec<i64> = Vec::new();
223 let mut foldable = true;
224 for s in steps.iter() {
225 match s {
226 ComputeStep::PushConstant(v) => const_stack.push(*v),
227 ComputeStep::Add => {
228 if const_stack.len() >= 2 {
229 let b = const_stack.pop().unwrap();
230 let a = const_stack.pop().unwrap();
231 const_stack.push(a.saturating_add(b));
232 } else {
233 foldable = false;
234 break;
235 }
236 }
237 ComputeStep::LoadRegister(_) | ComputeStep::Dereference { .. } => {
239 foldable = false;
240 break;
241 }
242 _ => {
243 foldable = false;
245 break;
246 }
247 }
248 }
249
250 if foldable && const_stack.len() == 1 {
251 let link_addr_u = const_stack[0] as u64;
252 let ctx = self.get_compile_time_context()?;
253 let module_for_offsets = module_hint
254 .map(|s| s.to_string())
255 .or_else(|| self.current_resolved_var_module_path.clone())
256 .unwrap_or_else(|| ctx.module_path.clone());
257 let st_code = self.section_code_for_address(&module_for_offsets, link_addr_u);
258 let cookie = self.cookie_for_module_or_fallback(&module_for_offsets);
259 let link_val = self.context.i64_type().const_int(link_addr_u, false);
260 let (rt_addr, found_flag) =
261 self.generate_runtime_address_from_offsets(link_val, st_code, cookie)?;
262 if let Some(sp) = status_ptr {
263 let is_miss = self
264 .builder
265 .build_int_compare(
266 inkwell::IntPredicate::EQ,
267 found_flag,
268 self.context.bool_type().const_zero(),
269 "is_off_miss",
270 )
271 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
272 let cur_status = self
273 .builder
274 .build_load(self.context.i8_type(), sp, "cur_status")
275 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
276 let is_ok = self
277 .builder
278 .build_int_compare(
279 inkwell::IntPredicate::EQ,
280 cur_status.into_int_value(),
281 self.context.i8_type().const_zero(),
282 "status_is_ok",
283 )
284 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
285 let should_store = self
286 .builder
287 .build_and(is_miss, is_ok, "store_offsets_unavail")
288 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
289 let new_status = self
290 .builder
291 .build_select(
292 should_store,
293 self.context
294 .i8_type()
295 .const_int(
296 ghostscope_protocol::VariableStatus::OffsetsUnavailable
297 as u64,
298 false,
299 )
300 .into(),
301 cur_status,
302 "new_status",
303 )
304 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
305 self.builder
306 .build_store(sp, new_status)
307 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
308 }
309 self.current_resolved_var_module_path = None;
310 return Ok(rt_addr);
311 }
312
313 if let Some(ComputeStep::PushConstant(base_const)) = steps.first() {
316 let mut saw_reg = false;
318 let mut saw_deref = false;
319 for s in &steps[1..] {
320 match s {
321 ComputeStep::LoadRegister(_) => {
322 saw_reg = true;
323 break;
324 }
325 ComputeStep::Dereference { .. } => {
326 saw_deref = true;
327 break;
328 }
329 _ => {}
330 }
331 }
332 if saw_deref && !saw_reg {
333 let link_addr_u = *base_const as u64;
334 let ctx = self.get_compile_time_context()?;
335 let module_for_offsets = module_hint
336 .map(|s| s.to_string())
337 .or_else(|| self.current_resolved_var_module_path.clone())
338 .unwrap_or_else(|| ctx.module_path.clone());
339 let st_code =
340 self.section_code_for_address(&module_for_offsets, link_addr_u);
341 let cookie = self.cookie_for_module_or_fallback(&module_for_offsets);
342 let link_val = self.context.i64_type().const_int(link_addr_u, false);
343 let (rt, found_flag) =
344 self.generate_runtime_address_from_offsets(link_val, st_code, cookie)?;
345 if let Some(sp) = status_ptr {
346 let is_miss = self
347 .builder
348 .build_int_compare(
349 inkwell::IntPredicate::EQ,
350 found_flag,
351 self.context.bool_type().const_zero(),
352 "is_off_miss",
353 )
354 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
355 let cur_status = self
356 .builder
357 .build_load(self.context.i8_type(), sp, "cur_status")
358 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
359 let is_ok = self
360 .builder
361 .build_int_compare(
362 inkwell::IntPredicate::EQ,
363 cur_status.into_int_value(),
364 self.context.i8_type().const_zero(),
365 "status_is_ok",
366 )
367 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
368 let should_store = self
369 .builder
370 .build_and(is_miss, is_ok, "store_offsets_unavail")
371 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
372 let new_status = self
373 .builder
374 .build_select(
375 should_store,
376 self.context
377 .i8_type()
378 .const_int(
379 ghostscope_protocol::VariableStatus::OffsetsUnavailable
380 as u64,
381 false,
382 )
383 .into(),
384 cur_status,
385 "new_status",
386 )
387 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
388 self.builder
389 .build_store(sp, new_status)
390 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
391 }
392 let rest = &steps[1..];
394 let val = self.generate_compute_steps(
395 rest,
396 pt_regs_ptr,
397 None,
398 status_ptr,
399 Some(rt),
400 )?;
401 if let BasicValueEnum::IntValue(i) = val {
402 return Ok(i);
403 } else {
404 return Err(CodeGenError::LLVMError(
405 "Computed location did not produce integer".to_string(),
406 ));
407 }
408 }
409 }
410
411 let val =
413 self.generate_compute_steps(steps, pt_regs_ptr, None, status_ptr, None)?;
414 if let BasicValueEnum::IntValue(i) = val {
415 Ok(i)
416 } else {
417 Err(CodeGenError::LLVMError(
418 "Computed location did not produce integer".to_string(),
419 ))
420 }
421 }
422 _ => Err(CodeGenError::NotImplemented(
423 "Unable to compute address from evaluation result".to_string(),
424 )),
425 }
426 }
427
428 fn dwarf_type_to_memory_access_size(&self, dwarf_type: &TypeInfo) -> MemoryAccessSize {
430 let size = Self::get_dwarf_type_size(dwarf_type);
431 match size {
432 1 => MemoryAccessSize::U8,
433 2 => MemoryAccessSize::U16,
434 4 => MemoryAccessSize::U32,
435 8 => MemoryAccessSize::U64,
436 _ => MemoryAccessSize::U64, }
438 }
439
440 fn generate_direct_value(
442 &mut self,
443 direct: &DirectValueResult,
444 pt_regs_ptr: PointerValue<'ctx>,
445 ) -> Result<BasicValueEnum<'ctx>> {
446 match direct {
447 DirectValueResult::Constant(value) => {
448 debug!("Generating constant: {}", value);
449 Ok(self
450 .context
451 .i64_type()
452 .const_int(*value as u64, true)
453 .into())
454 }
455
456 DirectValueResult::AbsoluteAddress(value) => {
457 debug!("Generating rebased absolute address: 0x{value:x}");
458 let module_hint = self.current_resolved_var_module_path.clone();
459 let status_ptr = if self.condition_context_active {
460 Some(self.get_or_create_cond_error_global())
461 } else {
462 None
463 };
464 let eval = ghostscope_dwarf::EvaluationResult::MemoryLocation(
465 ghostscope_dwarf::LocationResult::Address(*value),
466 );
467 self.evaluation_result_to_address_with_hint(
468 &eval,
469 status_ptr,
470 module_hint.as_deref(),
471 )
472 .map(Into::into)
473 }
474
475 DirectValueResult::ImplicitValue(bytes) => {
476 debug!("Generating implicit value: {} bytes", bytes.len());
477 let mut value: u64 = 0;
479 for (i, &byte) in bytes.iter().enumerate().take(8) {
480 value |= (byte as u64) << (i * 8);
481 }
482 Ok(self.context.i64_type().const_int(value, false).into())
483 }
484
485 DirectValueResult::RegisterValue(reg_num) => {
486 debug!("Generating register value: {}", reg_num);
487 let reg_value = self.load_register_value(*reg_num, pt_regs_ptr)?;
488 Ok(reg_value)
489 }
490
491 DirectValueResult::ComputedValue { steps, result_size } => {
492 debug!("Generating computed value: {} steps", steps.len());
493 let status_ptr = if self.condition_context_active {
494 Some(self.get_or_create_cond_error_global())
495 } else {
496 None
497 };
498 self.generate_compute_steps(
499 steps,
500 pt_regs_ptr,
501 Some(*result_size),
502 status_ptr,
503 None,
504 )
505 }
506 }
507 }
508
509 fn generate_memory_location(
511 &mut self,
512 location: &LocationResult,
513 pt_regs_ptr: PointerValue<'ctx>,
514 dwarf_type: &TypeInfo,
515 status_ptr: Option<PointerValue<'ctx>>,
516 ) -> Result<BasicValueEnum<'ctx>> {
517 match location {
518 LocationResult::Address(addr) => {
527 debug!("Generating absolute address: 0x{:x}", addr);
528 let module_hint = self.current_resolved_var_module_path.clone();
530 let runtime_status_ptr = if self.condition_context_active {
531 Some(self.get_or_create_cond_error_global())
532 } else {
533 status_ptr
534 };
535 let eval = ghostscope_dwarf::EvaluationResult::MemoryLocation(
536 ghostscope_dwarf::LocationResult::Address(*addr),
537 );
538 let rt_addr = self.evaluation_result_to_address_with_hint(
539 &eval,
540 runtime_status_ptr,
541 module_hint.as_deref(),
542 )?;
543 if self.is_aggregate_type(dwarf_type) {
545 let ptr_ty = self.context.ptr_type(inkwell::AddressSpace::default());
546 let as_ptr = self
547 .builder
548 .build_int_to_ptr(rt_addr, ptr_ty, "aggregate_addr_as_ptr")
549 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
550 return Ok(as_ptr.into());
551 }
552 let access_size = self.dwarf_type_to_memory_access_size(dwarf_type);
554 if self.condition_context_active {
555 self.generate_memory_read_with_status(rt_addr, access_size)
556 } else {
557 self.generate_memory_read(rt_addr, access_size, status_ptr)
558 }
559 }
560
561 LocationResult::RegisterAddress {
562 register,
563 offset,
564 size,
565 } => {
566 debug!(
567 "Generating register address: reg{} {:+}",
568 register,
569 offset.unwrap_or(0)
570 );
571
572 let reg_value = self.load_register_value(*register, pt_regs_ptr)?;
574
575 let final_addr = if let Some(offset) = offset {
577 let offset_value = self.context.i64_type().const_int(*offset as u64, true);
578 if let BasicValueEnum::IntValue(reg_int) = reg_value {
579 self.builder
580 .build_int_add(reg_int, offset_value, "addr_with_offset")
581 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
582 } else {
583 return Err(CodeGenError::RegisterMappingError(
584 "Register value is not integer".to_string(),
585 ));
586 }
587 } else if let BasicValueEnum::IntValue(reg_int) = reg_value {
588 reg_int
589 } else {
590 return Err(CodeGenError::RegisterMappingError(
591 "Register value is not integer".to_string(),
592 ));
593 };
594 if self.is_aggregate_type(dwarf_type) {
596 let ptr_ty = self.context.ptr_type(inkwell::AddressSpace::default());
597 let as_ptr = self
598 .builder
599 .build_int_to_ptr(final_addr, ptr_ty, "aggregate_addr_as_ptr")
600 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
601 return Ok(as_ptr.into());
602 }
603 let access_size = size
605 .map(|s| match s {
606 1 => MemoryAccessSize::U8,
607 2 => MemoryAccessSize::U16,
608 4 => MemoryAccessSize::U32,
609 _ => MemoryAccessSize::U64,
610 })
611 .unwrap_or_else(|| self.dwarf_type_to_memory_access_size(dwarf_type));
612
613 if self.condition_context_active {
614 self.generate_memory_read_with_status(final_addr, access_size)
615 } else {
616 self.generate_memory_read(final_addr, access_size, status_ptr)
617 }
618 }
619
620 LocationResult::ComputedLocation { steps } => {
621 debug!("Generating computed location: {} steps", steps.len());
622 let runtime_status_ptr = if self.condition_context_active {
624 Some(self.get_or_create_cond_error_global())
625 } else {
626 status_ptr
627 };
628 let addr_value = self.generate_compute_steps(
629 steps,
630 pt_regs_ptr,
631 None,
632 runtime_status_ptr,
633 None,
634 )?;
635 if let BasicValueEnum::IntValue(addr) = addr_value {
636 if self.is_aggregate_type(dwarf_type) {
638 let ptr_ty = self.context.ptr_type(inkwell::AddressSpace::default());
639 let as_ptr = self
640 .builder
641 .build_int_to_ptr(addr, ptr_ty, "aggregate_addr_as_ptr")
642 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
643 return Ok(as_ptr.into());
644 }
645 let access_size = self.dwarf_type_to_memory_access_size(dwarf_type);
647 if self.condition_context_active {
648 self.generate_memory_read_with_status(addr, access_size)
649 } else {
650 self.generate_memory_read(addr, access_size, status_ptr)
651 }
652 } else {
653 Err(CodeGenError::LLVMError(
654 "Address computation must return integer".to_string(),
655 ))
656 }
657 }
658 }
659 }
660
661 fn generate_compute_steps(
663 &mut self,
664 steps: &[ComputeStep],
665 pt_regs_ptr: PointerValue<'ctx>,
666 _result_size: Option<MemoryAccessSize>,
667 status_ptr: Option<PointerValue<'ctx>>,
668 initial_top: Option<IntValue<'ctx>>,
669 ) -> Result<BasicValueEnum<'ctx>> {
670 let mut stack: Vec<IntValue<'ctx>> = Vec::new();
672 let mut deref_null_flag: Option<inkwell::values::IntValue> = None;
675 if let Some(top) = initial_top {
676 stack.push(top);
677 }
678
679 for step in steps {
680 match step {
681 ComputeStep::LoadRegister(reg_num) => {
682 let reg_value = self.load_register_value(*reg_num, pt_regs_ptr)?;
683 if let BasicValueEnum::IntValue(int_val) = reg_value {
684 stack.push(int_val);
685 } else {
686 return Err(CodeGenError::RegisterMappingError(format!(
687 "Register {reg_num} did not return integer value"
688 )));
689 }
690 }
691
692 ComputeStep::PushConstant(value) => {
693 let const_val = self.context.i64_type().const_int(*value as u64, true);
694 stack.push(const_val);
695 }
696
697 ComputeStep::Add => {
698 if let (Some(b), Some(a)) = (stack.pop(), stack.pop()) {
699 let sum_val = self
700 .builder
701 .build_int_add(a, b, "add")
702 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
703 if let Some(nf) = deref_null_flag {
704 let masked_bv = self
705 .builder
706 .build_select::<inkwell::values::BasicValueEnum<'ctx>, _>(
707 nf,
708 self.context.i64_type().const_zero().into(),
709 sum_val.into(),
710 "add_masked",
711 )
712 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
713 stack.push(masked_bv.into_int_value());
714 } else {
715 stack.push(sum_val);
716 }
717 } else {
718 return Err(CodeGenError::LLVMError(
719 "Stack underflow in Add".to_string(),
720 ));
721 }
722 }
723
724 ComputeStep::Sub => {
725 if let (Some(b), Some(a)) = (stack.pop(), stack.pop()) {
726 let result = self
727 .builder
728 .build_int_sub(a, b, "sub")
729 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
730 stack.push(result);
731 } else {
732 return Err(CodeGenError::LLVMError(
733 "Stack underflow in Sub".to_string(),
734 ));
735 }
736 }
737
738 ComputeStep::Mul => {
739 if let (Some(b), Some(a)) = (stack.pop(), stack.pop()) {
740 let result = self
741 .builder
742 .build_int_mul(a, b, "mul")
743 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
744 stack.push(result);
745 } else {
746 return Err(CodeGenError::LLVMError(
747 "Stack underflow in Mul".to_string(),
748 ));
749 }
750 }
751
752 ComputeStep::Div => {
753 if let (Some(b), Some(a)) = (stack.pop(), stack.pop()) {
754 let result = self
755 .builder
756 .build_int_signed_div(a, b, "div")
757 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
758 stack.push(result);
759 } else {
760 return Err(CodeGenError::LLVMError(
761 "Stack underflow in Div".to_string(),
762 ));
763 }
764 }
765
766 ComputeStep::And => {
767 if let (Some(b), Some(a)) = (stack.pop(), stack.pop()) {
768 let result = self
769 .builder
770 .build_and(a, b, "and")
771 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
772 stack.push(result);
773 } else {
774 return Err(CodeGenError::LLVMError(
775 "Stack underflow in BitwiseAnd".to_string(),
776 ));
777 }
778 }
779
780 ComputeStep::Or => {
781 if let (Some(b), Some(a)) = (stack.pop(), stack.pop()) {
782 let result = self
783 .builder
784 .build_or(a, b, "or")
785 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
786 stack.push(result);
787 } else {
788 return Err(CodeGenError::LLVMError(
789 "Stack underflow in BitwiseOr".to_string(),
790 ));
791 }
792 }
793
794 ComputeStep::Xor => {
795 if let (Some(b), Some(a)) = (stack.pop(), stack.pop()) {
796 let result = self
797 .builder
798 .build_xor(a, b, "xor")
799 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
800 stack.push(result);
801 } else {
802 return Err(CodeGenError::LLVMError(
803 "Stack underflow in BitwiseXor".to_string(),
804 ));
805 }
806 }
807
808 ComputeStep::Shl => {
809 if let (Some(b), Some(a)) = (stack.pop(), stack.pop()) {
810 let result = self
811 .builder
812 .build_left_shift(a, b, "shl")
813 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
814 stack.push(result);
815 } else {
816 return Err(CodeGenError::LLVMError(
817 "Stack underflow in ShiftLeft".to_string(),
818 ));
819 }
820 }
821
822 ComputeStep::Shr => {
823 if let (Some(b), Some(a)) = (stack.pop(), stack.pop()) {
824 let result = self
825 .builder
826 .build_right_shift(a, b, false, "shr")
827 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
828 stack.push(result);
829 } else {
830 return Err(CodeGenError::LLVMError(
831 "Stack underflow in ShiftRight".to_string(),
832 ));
833 }
834 }
835
836 ComputeStep::Dereference { size } => {
837 if let Some(addr) = stack.pop() {
838 let zero64 = self.context.i64_type().const_zero();
840 let is_null = self
841 .builder
842 .build_int_compare(
843 inkwell::IntPredicate::EQ,
844 addr,
845 zero64,
846 "is_null_deref",
847 )
848 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
849
850 let cur_fn = self
851 .builder
852 .get_insert_block()
853 .unwrap()
854 .get_parent()
855 .unwrap();
856 let null_bb = self.context.append_basic_block(cur_fn, "deref_null");
857 let read_bb = self.context.append_basic_block(cur_fn, "deref_read");
858 let cont_bb = self.context.append_basic_block(cur_fn, "deref_cont");
859 self.builder
860 .build_conditional_branch(is_null, null_bb, read_bb)
861 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
862
863 self.builder.position_at_end(null_bb);
865 let null_val = self.context.i64_type().const_zero();
866 if let Some(sp) = status_ptr {
867 let cur_status = self
868 .builder
869 .build_load(self.context.i8_type(), sp, "cur_status")
870 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
871 .into_int_value();
872 let is_ok = self
873 .builder
874 .build_int_compare(
875 inkwell::IntPredicate::EQ,
876 cur_status,
877 self.context.i8_type().const_zero(),
878 "status_is_ok",
879 )
880 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
881 let then_val = self.context.i8_type().const_int(
882 ghostscope_protocol::VariableStatus::NullDeref as u64,
883 false,
884 );
885 let new_status_bv = self
886 .builder
887 .build_select::<inkwell::values::BasicValueEnum<'ctx>, _>(
888 is_ok,
889 then_val.into(),
890 cur_status.into(),
891 "new_status",
892 )
893 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
894 self.builder
895 .build_store(sp, new_status_bv)
896 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
897 }
898 self.builder
899 .build_unconditional_branch(cont_bb)
900 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
901
902 self.builder.position_at_end(read_bb);
904 let access_size = *size;
905 let loaded_bv = if self.condition_context_active {
906 self.generate_memory_read_with_status(addr, access_size)?
907 } else {
908 self.generate_memory_read(addr, access_size, status_ptr)?
909 };
910 let loaded_int = if let BasicValueEnum::IntValue(int_val) = loaded_bv {
911 int_val
912 } else {
913 return Err(CodeGenError::LLVMError(
914 "Memory load did not return integer".to_string(),
915 ));
916 };
917 let value_block = self.builder.get_insert_block().ok_or_else(|| {
918 CodeGenError::LLVMError(
919 "No insertion block after dereference read".to_string(),
920 )
921 })?;
922 self.builder
923 .build_unconditional_branch(cont_bb)
924 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
925
926 self.builder.position_at_end(cont_bb);
928 let phi = self
929 .builder
930 .build_phi(self.context.i64_type(), "deref_phi")
931 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
932 phi.add_incoming(&[(&null_val, null_bb), (&loaded_int, value_block)]);
933 let merged = phi.as_basic_value().into_int_value();
934 let is_zero_ptr = self
936 .builder
937 .build_int_compare(
938 inkwell::IntPredicate::EQ,
939 merged,
940 self.context.i64_type().const_zero(),
941 "is_zero_ptr",
942 )
943 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
944 deref_null_flag = Some(match deref_null_flag {
945 Some(prev) => self
946 .builder
947 .build_or(prev, is_zero_ptr, "null_or")
948 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?,
949 None => is_zero_ptr,
950 });
951 if let (Some(sp), Some(nf)) = (status_ptr, deref_null_flag) {
952 let cur_status = self
954 .builder
955 .build_load(self.context.i8_type(), sp, "cur_status")
956 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
957 .into_int_value();
958 let is_ok = self
959 .builder
960 .build_int_compare(
961 inkwell::IntPredicate::EQ,
962 cur_status,
963 self.context.i8_type().const_zero(),
964 "status_is_ok2",
965 )
966 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
967 let should_store = self
968 .builder
969 .build_and(is_ok, nf, "store_null_deref_from_ptr")
970 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
971 let then_val = self.context.i8_type().const_int(
972 ghostscope_protocol::VariableStatus::NullDeref as u64,
973 false,
974 );
975 let new_status_bv = self
976 .builder
977 .build_select::<inkwell::values::BasicValueEnum<'ctx>, _>(
978 should_store,
979 then_val.into(),
980 cur_status.into(),
981 "new_status2",
982 )
983 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
984 self.builder
985 .build_store(sp, new_status_bv)
986 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
987 }
988 stack.push(merged);
989 } else {
990 return Err(CodeGenError::LLVMError(
991 "Stack underflow in LoadMemory".to_string(),
992 ));
993 }
994 }
995
996 ComputeStep::EntryValueLookup {
997 caller_pc_steps,
998 cases,
999 } => {
1000 let value = self.generate_entry_value_lookup(
1001 caller_pc_steps,
1002 cases,
1003 pt_regs_ptr,
1004 _result_size,
1005 status_ptr,
1006 )?;
1007 stack.push(value);
1008 }
1009
1010 _ => {
1012 warn!("Unimplemented ComputeStep: {:?}", step);
1013 return Err(CodeGenError::NotImplemented(format!(
1014 "ComputeStep {step:?} not yet implemented"
1015 )));
1016 }
1017 }
1018 }
1019
1020 if stack.len() == 1 {
1021 Ok(stack.pop().unwrap().into())
1022 } else {
1023 Err(CodeGenError::LLVMError(format!(
1024 "Invalid stack state after computation: {} elements remaining",
1025 stack.len()
1026 )))
1027 }
1028 }
1029
1030 fn generate_entry_value_lookup(
1031 &mut self,
1032 caller_pc_steps: &[ComputeStep],
1033 cases: &[ghostscope_dwarf::core::EntryValueCase],
1034 pt_regs_ptr: PointerValue<'ctx>,
1035 result_size: Option<MemoryAccessSize>,
1036 status_ptr: Option<PointerValue<'ctx>>,
1037 ) -> Result<IntValue<'ctx>> {
1038 if cases.is_empty() {
1039 return Err(CodeGenError::LLVMError(
1040 "EntryValueLookup requires at least one case".to_string(),
1041 ));
1042 }
1043
1044 let caller_pc = self
1045 .generate_compute_steps(
1046 caller_pc_steps,
1047 pt_regs_ptr,
1048 Some(MemoryAccessSize::U64),
1049 status_ptr,
1050 None,
1051 )?
1052 .into_int_value();
1053
1054 let current_block = self.builder.get_insert_block().ok_or_else(|| {
1055 CodeGenError::LLVMError("No insertion block for EntryValueLookup".to_string())
1056 })?;
1057 let current_fn = current_block.get_parent().ok_or_else(|| {
1058 CodeGenError::LLVMError("No parent function for EntryValueLookup".to_string())
1059 })?;
1060 let merge_bb = self
1061 .context
1062 .append_basic_block(current_fn, "entry_value_merge");
1063 let default_bb = self
1064 .context
1065 .append_basic_block(current_fn, "entry_value_default");
1066
1067 let module_for_offsets = {
1068 let ctx = self.get_compile_time_context()?;
1069 self.current_resolved_var_module_path
1070 .clone()
1071 .unwrap_or_else(|| ctx.module_path.clone())
1072 };
1073 let module_cookie = self.cookie_for_module_or_fallback(&module_for_offsets);
1074 let mut incoming_values = Vec::with_capacity(cases.len() + 1);
1075 let mut any_missing_offsets = None;
1076
1077 for (index, case) in cases.iter().enumerate() {
1078 let st_code = self.section_code_for_address(&module_for_offsets, case.caller_return_pc);
1079 let link_pc = self
1080 .context
1081 .i64_type()
1082 .const_int(case.caller_return_pc, false);
1083 let (runtime_return_pc, found_flag) =
1084 self.generate_runtime_address_from_offsets(link_pc, st_code, module_cookie)?;
1085 let missing_offsets = self
1086 .builder
1087 .build_not(found_flag, &format!("entry_value_missing_{index}"))
1088 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1089 any_missing_offsets = Some(match any_missing_offsets {
1090 Some(prev) => self
1091 .builder
1092 .build_or(
1093 prev,
1094 missing_offsets,
1095 &format!("entry_value_missing_or_{index}"),
1096 )
1097 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?,
1098 None => missing_offsets,
1099 });
1100
1101 let is_match = self
1102 .builder
1103 .build_int_compare(
1104 inkwell::IntPredicate::EQ,
1105 caller_pc,
1106 runtime_return_pc,
1107 &format!("entry_value_match_{index}"),
1108 )
1109 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1110 let case_bb = self
1111 .context
1112 .append_basic_block(current_fn, &format!("entry_value_case_{index}"));
1113 let next_bb = if index + 1 == cases.len() {
1114 default_bb
1115 } else {
1116 self.context
1117 .append_basic_block(current_fn, &format!("entry_value_check_{}", index + 1))
1118 };
1119 self.builder
1120 .build_conditional_branch(is_match, case_bb, next_bb)
1121 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1122
1123 self.builder.position_at_end(case_bb);
1124 let case_value = self
1125 .generate_compute_steps(
1126 &case.value_steps,
1127 pt_regs_ptr,
1128 result_size,
1129 status_ptr,
1130 None,
1131 )?
1132 .into_int_value();
1133 let case_value_block = self.builder.get_insert_block().ok_or_else(|| {
1134 CodeGenError::LLVMError(
1135 "No insertion block after EntryValueLookup case".to_string(),
1136 )
1137 })?;
1138 self.builder
1139 .build_unconditional_branch(merge_bb)
1140 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1141 incoming_values.push((case_value, case_value_block));
1142
1143 self.builder.position_at_end(next_bb);
1144 }
1145
1146 self.builder.position_at_end(default_bb);
1147 if let Some(sp) = status_ptr {
1148 self.store_variable_read_status(
1149 sp,
1150 self.context.bool_type().const_int(1, false),
1151 any_missing_offsets.unwrap_or_else(|| self.context.bool_type().const_zero()),
1152 "entry_value_default",
1153 )?;
1154 }
1155 let default_value = self.context.i64_type().const_zero();
1156 let default_value_block = self.builder.get_insert_block().ok_or_else(|| {
1157 CodeGenError::LLVMError("No default block for EntryValueLookup".to_string())
1158 })?;
1159 self.builder
1160 .build_unconditional_branch(merge_bb)
1161 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1162 incoming_values.push((default_value, default_value_block));
1163
1164 self.builder.position_at_end(merge_bb);
1165 let phi = self
1166 .builder
1167 .build_phi(self.context.i64_type(), "entry_value_phi")
1168 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1169 let incoming_refs: Vec<(&dyn inkwell::values::BasicValue<'ctx>, _)> = incoming_values
1170 .iter()
1171 .map(|(value, block)| (value as &dyn inkwell::values::BasicValue<'ctx>, *block))
1172 .collect();
1173 phi.add_incoming(&incoming_refs);
1174
1175 Ok(phi.as_basic_value().into_int_value())
1176 }
1177
1178 pub fn query_dwarf_for_complex_expr(
1180 &mut self,
1181 expr: &crate::script::Expr,
1182 ) -> Result<Option<VariableWithEvaluation>> {
1183 use crate::script::Expr;
1184
1185 fn expand_aliases(
1189 ctx: &crate::ebpf::context::EbpfContext<'_, '_>,
1190 e: &crate::script::Expr,
1191 visited: &mut std::collections::HashSet<String>,
1192 depth: usize,
1193 ) -> std::result::Result<crate::script::Expr, super::context::CodeGenError> {
1194 use crate::script::Expr as E;
1195 const MAX_DEPTH: usize = 64;
1196 if depth > MAX_DEPTH {
1197 return Err(super::context::CodeGenError::TypeError(
1198 "alias expansion depth exceeded (cycle?)".to_string(),
1199 ));
1200 }
1201 Ok(match e {
1202 E::Variable(name) => {
1203 if ctx.alias_variable_exists(name) {
1204 if !visited.insert(name.clone()) {
1205 return Err(super::context::CodeGenError::TypeError(format!(
1206 "alias cycle detected for '{name}'"
1207 )));
1208 }
1209 if let Some(t) = ctx.get_alias_variable(name) {
1210 let res = expand_aliases(ctx, &t, visited, depth + 1)?;
1211 visited.remove(name);
1212 res
1213 } else {
1214 e.clone()
1215 }
1216 } else {
1217 e.clone()
1218 }
1219 }
1220 E::MemberAccess(obj, field) => {
1221 let base = expand_aliases(ctx, obj, visited, depth + 1)?;
1222 E::MemberAccess(Box::new(base), field.clone())
1223 }
1224 E::ArrayAccess(arr, idx) => {
1225 let base = expand_aliases(ctx, arr, visited, depth + 1)?;
1226 let idx2 = expand_aliases(ctx, idx, visited, depth + 1)?;
1227 E::ArrayAccess(Box::new(base), Box::new(idx2))
1228 }
1229 E::PointerDeref(inner) => {
1230 let in2 = expand_aliases(ctx, inner, visited, depth + 1)?;
1231 E::PointerDeref(Box::new(in2))
1232 }
1233 E::AddressOf(inner) => {
1234 let in2 = expand_aliases(ctx, inner, visited, depth + 1)?;
1235 E::AddressOf(Box::new(in2))
1236 }
1237 E::ChainAccess(chain) => {
1238 if chain.is_empty() {
1239 return Ok(e.clone());
1240 }
1241 let head = &chain[0];
1242 if ctx.alias_variable_exists(head) {
1243 if !visited.insert(head.clone()) {
1244 return Err(super::context::CodeGenError::TypeError(format!(
1245 "alias cycle detected for '{head}'"
1246 )));
1247 }
1248 if let Some(alias_expr) = ctx.get_alias_variable(head) {
1249 let mut acc = expand_aliases(ctx, &alias_expr, visited, depth + 1)?;
1251 for seg in &chain[1..] {
1252 acc = E::MemberAccess(Box::new(acc), seg.clone());
1253 }
1254 visited.remove(head);
1255 acc
1256 } else {
1257 e.clone()
1258 }
1259 } else {
1260 e.clone()
1261 }
1262 }
1263 E::BuiltinCall { name, args } => E::BuiltinCall {
1264 name: name.clone(),
1265 args: args
1266 .iter()
1267 .map(|a| expand_aliases(ctx, a, visited, depth + 1))
1268 .collect::<std::result::Result<Vec<_>, _>>()?,
1269 },
1270 E::BinaryOp { left, op, right } => E::BinaryOp {
1271 left: Box::new(expand_aliases(ctx, left, visited, depth + 1)?),
1272 op: op.clone(),
1273 right: Box::new(expand_aliases(ctx, right, visited, depth + 1)?),
1274 },
1275 _ => e.clone(),
1276 })
1277 }
1278
1279 let mut visited = std::collections::HashSet::new();
1280 let expanded = expand_aliases(self, expr, &mut visited, 0)?;
1281
1282 match &expanded {
1283 Expr::Variable(var_name) => self.query_dwarf_for_variable(var_name),
1285
1286 Expr::MemberAccess(obj_expr, field_name) => {
1288 self.query_dwarf_for_member_access(obj_expr, field_name)
1289 }
1290
1291 Expr::ArrayAccess(array_expr, index_expr) => {
1293 self.query_dwarf_for_array_access(array_expr, index_expr)
1294 }
1295
1296 Expr::ChainAccess(chain) => self.query_dwarf_for_chain_access(chain),
1298
1299 Expr::PointerDeref(expr) => self.query_dwarf_for_pointer_deref(expr),
1301
1302 _ => Ok(None),
1304 }
1305 }
1306
1307 pub fn query_dwarf_for_variable(
1309 &mut self,
1310 var_name: &str,
1311 ) -> Result<Option<VariableWithEvaluation>> {
1312 let context = self.get_compile_time_context()?;
1313 let pc_address = context.pc_address;
1314 let module_path = context.module_path.clone();
1315
1316 debug!(
1317 "Querying DWARF for variable '{}' at PC 0x{:x} in module '{}'",
1318 var_name, pc_address, module_path
1319 );
1320
1321 let analyzer = self
1322 .process_analyzer
1323 .ok_or_else(|| CodeGenError::DwarfError("No DWARF analyzer available".to_string()))?;
1324
1325 let module_address = ghostscope_dwarf::ModuleAddress::new(
1326 std::path::PathBuf::from(module_path.clone()),
1327 pc_address,
1328 );
1329
1330 let module_path_owned = module_path;
1331 let lookup_globals = |analyzer: &ghostscope_dwarf::DwarfAnalyzer| -> Result<
1332 Option<(std::path::PathBuf, VariableWithEvaluation)>,
1333 > {
1334 debug!(
1335 "Variable '{}' not found in locals; attempting global lookup",
1336 var_name
1337 );
1338 let matches = analyzer.find_global_variables_by_name(var_name);
1339 if matches.is_empty() {
1340 return Ok(None);
1341 }
1342
1343 let preferred: Vec<(
1349 std::path::PathBuf,
1350 ghostscope_dwarf::core::GlobalVariableInfo,
1351 )> = matches
1352 .iter()
1353 .filter(|(p, _)| p.to_string_lossy() == module_path_owned.as_str())
1354 .cloned()
1355 .collect();
1356 let preferred_with_addr: Vec<(
1357 std::path::PathBuf,
1358 ghostscope_dwarf::core::GlobalVariableInfo,
1359 )> = preferred
1360 .iter()
1361 .filter(|(_, info)| info.link_address.is_some())
1362 .cloned()
1363 .collect();
1364 let with_addr: Vec<(
1365 std::path::PathBuf,
1366 ghostscope_dwarf::core::GlobalVariableInfo,
1367 )> = matches
1368 .iter()
1369 .filter(|(_, info)| info.link_address.is_some())
1370 .cloned()
1371 .collect();
1372
1373 let candidates: Vec<(
1374 std::path::PathBuf,
1375 ghostscope_dwarf::core::GlobalVariableInfo,
1376 )> = if !preferred_with_addr.is_empty() {
1377 preferred_with_addr
1378 } else if !with_addr.is_empty() {
1379 with_addr
1380 } else if !preferred.is_empty() {
1381 preferred
1382 } else {
1383 matches
1384 };
1385
1386 if candidates.len() == 1 {
1387 let (mpath, info) = &candidates[0];
1388 let gv = analyzer
1389 .resolve_variable_by_offsets_in_module(
1390 mpath,
1391 info.unit_offset,
1392 info.die_offset,
1393 )
1394 .map_err(|err| CodeGenError::DwarfError(err.to_string()))?;
1395 return Ok(Some((mpath.clone(), gv)));
1396 }
1397
1398 let mut resolved: Vec<(std::path::PathBuf, VariableWithEvaluation)> = Vec::new();
1400 let mut resolved_with_size: Vec<(std::path::PathBuf, VariableWithEvaluation)> =
1401 Vec::new();
1402 for (mpath, info) in candidates.iter() {
1403 let gv = match analyzer.resolve_variable_by_offsets_in_module(
1404 mpath,
1405 info.unit_offset,
1406 info.die_offset,
1407 ) {
1408 Ok(v) => v,
1409 Err(err) => {
1410 debug!(
1411 "Skipping unresolved global candidate '{}' in '{}': {}",
1412 var_name,
1413 mpath.display(),
1414 err
1415 );
1416 continue;
1417 }
1418 };
1419 let ty_size = gv.dwarf_type.as_ref().map(|t| t.size()).unwrap_or(0);
1420 if ty_size > 0 {
1421 resolved_with_size.push((mpath.clone(), gv.clone()));
1422 }
1423 resolved.push((mpath.clone(), gv));
1424 }
1425
1426 if resolved_with_size.len() == 1 {
1427 return Ok(resolved_with_size.into_iter().next());
1428 }
1429
1430 if resolved.len() == 1 {
1431 return Ok(resolved.into_iter().next());
1432 }
1433
1434 let ambiguous_count = if resolved_with_size.len() > 1 {
1435 resolved_with_size.len()
1436 } else if !resolved.is_empty() {
1437 resolved.len()
1438 } else {
1439 candidates.len()
1440 };
1441 debug!("Global '{var_name}' is ambiguous across modules ({ambiguous_count} candidates)");
1442 Err(CodeGenError::DwarfError(format!(
1443 "Ambiguous global '{var_name}': {ambiguous_count} matches"
1444 )))
1445 };
1446
1447 match analyzer.get_all_variables_at_address(&module_address) {
1448 Ok(vars) => {
1449 if let Some(var_result) = vars.iter().find(|v| v.name == var_name).or_else(|| {
1450 let prefix = format!("{var_name}@");
1451 vars.iter().find(|v| v.name.starts_with(&prefix))
1452 }) {
1453 debug!("Found DWARF variable '{}' in locals/params", var_name);
1454 Ok(Some(var_result.clone()))
1455 } else if let Some((mpath, gv)) = lookup_globals(analyzer)? {
1456 self.current_resolved_var_module_path =
1457 Some(mpath.to_string_lossy().to_string());
1458 Ok(Some(gv))
1459 } else {
1460 Ok(None)
1461 }
1462 }
1463 Err(e) => {
1464 debug!(
1465 "DWARF local lookup error for '{}': {e}; falling back to globals",
1466 var_name
1467 );
1468 if let Some((mpath, gv)) = lookup_globals(analyzer)? {
1469 self.current_resolved_var_module_path =
1470 Some(mpath.to_string_lossy().to_string());
1471 Ok(Some(gv))
1472 } else {
1473 Ok(None)
1474 }
1475 }
1476 }
1477 }
1478
1479 pub fn get_dwarf_type_size(dwarf_type: &TypeInfo) -> u64 {
1481 match dwarf_type {
1482 TypeInfo::BaseType { size, .. } => *size,
1483 TypeInfo::PointerType { size, .. } => *size,
1484 TypeInfo::ArrayType { total_size, .. } => total_size.unwrap_or(0),
1485 TypeInfo::StructType { size, .. } => *size,
1486 TypeInfo::UnionType { size, .. } => *size,
1487 TypeInfo::EnumType { size, .. } => *size,
1488 TypeInfo::BitfieldType {
1489 underlying_type, ..
1490 } => {
1491 Self::get_dwarf_type_size(underlying_type)
1493 }
1494 TypeInfo::TypedefType {
1495 underlying_type, ..
1496 } => Self::get_dwarf_type_size(underlying_type),
1497 TypeInfo::QualifiedType {
1498 underlying_type, ..
1499 } => Self::get_dwarf_type_size(underlying_type),
1500 TypeInfo::FunctionType { .. } => 8, TypeInfo::UnknownType { .. } => 0,
1502 TypeInfo::OptimizedOut { .. } => 0, }
1504 }
1505
1506 pub fn query_dwarf_for_member_access(
1508 &mut self,
1509 obj_expr: &crate::script::Expr,
1510 field_name: &str,
1511 ) -> Result<Option<VariableWithEvaluation>> {
1512 if !matches!(obj_expr, crate::script::Expr::Variable(_)) {
1514 if let Some(base_var) = self.query_dwarf_for_complex_expr(obj_expr)? {
1515 if let Some(base_ty) = base_var.dwarf_type.as_ref() {
1516 fn find_member_offset_and_type(
1517 t: &ghostscope_dwarf::TypeInfo,
1518 field: &str,
1519 ) -> Option<(u64, ghostscope_dwarf::TypeInfo)> {
1520 match t {
1521 ghostscope_dwarf::TypeInfo::StructType { members, .. }
1522 | ghostscope_dwarf::TypeInfo::UnionType { members, .. } => {
1523 for m in members {
1524 if m.name == field {
1525 return Some((m.offset, m.member_type.clone()));
1526 }
1527 }
1528 None
1529 }
1530 ghostscope_dwarf::TypeInfo::TypedefType {
1531 underlying_type, ..
1532 }
1533 | ghostscope_dwarf::TypeInfo::QualifiedType {
1534 underlying_type, ..
1535 } => find_member_offset_and_type(underlying_type, field),
1536 _ => None,
1537 }
1538 }
1539 let mut effective_ty = base_ty.clone();
1541 let mut effective_eval = base_var.evaluation_result.clone();
1542 fn unwrap_typedef(
1544 mut t: &ghostscope_dwarf::TypeInfo,
1545 ) -> &ghostscope_dwarf::TypeInfo {
1546 while let ghostscope_dwarf::TypeInfo::TypedefType {
1547 underlying_type, ..
1548 }
1549 | ghostscope_dwarf::TypeInfo::QualifiedType {
1550 underlying_type,
1551 ..
1552 } = t
1553 {
1554 t = underlying_type.as_ref();
1555 }
1556 t
1557 }
1558 let unwrapped = unwrap_typedef(&effective_ty);
1559 if let ghostscope_dwarf::TypeInfo::PointerType { target_type, .. } = unwrapped {
1560 effective_eval = self.compute_pointer_dereference(&effective_eval)?;
1562 effective_ty = *target_type.clone();
1563 }
1564
1565 if let Some((member_off, member_ty)) =
1566 find_member_offset_and_type(&effective_ty, field_name)
1567 {
1568 use ghostscope_dwarf::{
1569 ComputeStep as CS, EvaluationResult as ER, LocationResult as LR,
1570 };
1571 let new_eval = match &effective_eval {
1572 ER::MemoryLocation(LR::Address(a)) => {
1573 ER::MemoryLocation(LR::Address(a + member_off))
1574 }
1575 ER::MemoryLocation(LR::ComputedLocation { steps }) => {
1576 let mut s = steps.clone();
1577 s.push(CS::PushConstant(member_off as i64));
1578 s.push(CS::Add);
1579 ER::MemoryLocation(LR::ComputedLocation { steps: s })
1580 }
1581 ER::MemoryLocation(LR::RegisterAddress {
1582 register,
1583 offset,
1584 size,
1585 }) => {
1586 let new_off = offset.unwrap_or(0).saturating_add(member_off as i64);
1587 ER::MemoryLocation(LR::RegisterAddress {
1588 register: *register,
1589 offset: Some(new_off),
1590 size: *size,
1591 })
1592 }
1593 _ => {
1594 return Err(CodeGenError::NotImplemented(
1595 "Member access on non-addressable expression".to_string(),
1596 ))
1597 }
1598 };
1599 let name = format!("{}.{}", base_var.name, field_name);
1600 let v = VariableWithEvaluation {
1601 name,
1602 type_name: member_ty.type_name(),
1603 dwarf_type: Some(member_ty),
1604 evaluation_result: new_eval,
1605 scope_depth: base_var.scope_depth,
1606 is_parameter: base_var.is_parameter,
1607 is_artificial: base_var.is_artificial,
1608 };
1609 return Ok(Some(v));
1610 }
1611 }
1612 }
1613 {
1615 fn flatten_ident_chain<'a>(
1616 e: &'a crate::script::Expr,
1617 out: &mut Vec<&'a str>,
1618 ) -> bool {
1619 match e {
1620 crate::script::Expr::Variable(name) => {
1621 out.push(name.as_str());
1622 true
1623 }
1624 crate::script::Expr::MemberAccess(obj, field) => {
1625 if flatten_ident_chain(obj, out) {
1626 out.push(field.as_str());
1627 true
1628 } else {
1629 false
1630 }
1631 }
1632 _ => false,
1633 }
1634 }
1635 let mut segs: Vec<&str> = Vec::new();
1636 if flatten_ident_chain(obj_expr, &mut segs) && !segs.is_empty() {
1637 let mut chain: Vec<String> = segs.into_iter().map(|s| s.to_string()).collect();
1638 chain.push(field_name.to_string());
1639 return self.query_dwarf_for_chain_access(&chain);
1640 }
1641 }
1642 }
1644 if let crate::script::Expr::Variable(base_name) = obj_expr {
1646 let ctx = self.get_compile_time_context()?;
1647 let module_path = ctx.module_path.clone();
1648 let pc_address = ctx.pc_address;
1649 let analyzer = self.process_analyzer.ok_or_else(|| {
1650 CodeGenError::DwarfError("No DWARF analyzer available".to_string())
1651 })?;
1652 let module_address = ghostscope_dwarf::ModuleAddress::new(
1653 std::path::PathBuf::from(module_path.clone()),
1654 pc_address,
1655 );
1656 match analyzer.plan_chain_access(&module_address, base_name, &[field_name.to_string()])
1658 {
1659 Ok(Some(var)) => return Ok(Some(var)),
1660 Ok(None) => {}
1661 Err(e) => {
1662 tracing::debug!("member planner miss at current module: {}", e);
1663 }
1664 }
1665
1666 match analyzer
1668 .plan_global_chain_access(
1669 &std::path::PathBuf::from(module_path.clone()),
1670 base_name,
1671 &[field_name.to_string()],
1672 )
1673 .map_err(|e| CodeGenError::DwarfError(e.to_string()))?
1674 {
1675 Some((mpath, v)) => {
1676 self.current_resolved_var_module_path =
1677 Some(mpath.to_string_lossy().to_string());
1678 Ok(Some(v))
1679 }
1680 None => {
1681 let mut matches = analyzer.find_global_variables_by_name(base_name);
1685 if !matches.is_empty() {
1686 let preferred: Vec<(
1688 std::path::PathBuf,
1689 ghostscope_dwarf::core::GlobalVariableInfo,
1690 )> = matches
1691 .iter()
1692 .filter(|(p, _)| p.to_string_lossy() == module_path.as_str())
1693 .cloned()
1694 .collect();
1695 let chosen = if preferred.len() == 1 {
1696 Some(preferred[0].clone())
1697 } else if preferred.is_empty() && matches.len() == 1 {
1698 Some(matches.remove(0))
1699 } else {
1700 None
1701 };
1702 if let Some((mp, info)) = chosen {
1703 if let Ok(var) = analyzer.resolve_variable_by_offsets_in_module(
1704 &mp,
1705 info.unit_offset,
1706 info.die_offset,
1707 ) {
1708 if let Some(ty) = var.dwarf_type.as_ref() {
1709 let mut t = ty;
1711 loop {
1712 match t {
1713 ghostscope_dwarf::TypeInfo::TypedefType {
1714 underlying_type,
1715 ..
1716 } => t = underlying_type.as_ref(),
1717 ghostscope_dwarf::TypeInfo::QualifiedType {
1718 underlying_type,
1719 ..
1720 } => t = underlying_type.as_ref(),
1721 _ => break,
1722 }
1723 }
1724 let mut kind: Option<&'static str> = None;
1725 let mut member_names: Vec<String> = Vec::new();
1726 match t {
1727 ghostscope_dwarf::TypeInfo::StructType {
1728 members, ..
1729 } => {
1730 kind = Some("struct");
1731 member_names =
1732 members.iter().map(|m| m.name.clone()).collect();
1733 }
1734 ghostscope_dwarf::TypeInfo::UnionType {
1735 members, ..
1736 } => {
1737 kind = Some("union");
1738 member_names =
1739 members.iter().map(|m| m.name.clone()).collect();
1740 }
1741 _ => {}
1742 }
1743 if let Some(k) = kind {
1744 member_names.sort();
1747 member_names.dedup();
1748 let list = if member_names.is_empty() {
1749 "<none>".to_string()
1750 } else {
1751 member_names.join(", ")
1752 };
1753 let msg = format!(
1754 "Unknown member '{field_name}' in {k} '{base_name}' (known members: {list})"
1755 );
1756 return Err(CodeGenError::TypeError(msg));
1757 }
1758 }
1759 }
1760 }
1761 }
1762 Ok(None)
1763 }
1764 }
1765 } else {
1766 Err(CodeGenError::NotImplemented(
1767 "MemberAccess base must be a simple variable (use chain access)".to_string(),
1768 ))
1769 }
1770 }
1771
1772 pub fn query_dwarf_for_array_access(
1774 &mut self,
1775 array_expr: &crate::script::Expr,
1776 index_expr: &crate::script::Expr,
1777 ) -> Result<Option<VariableWithEvaluation>> {
1778 if let crate::script::Expr::MemberAccess(_, _) = array_expr {
1780 fn flatten_chain<'a>(e: &'a crate::script::Expr, out: &mut Vec<&'a str>) -> bool {
1782 match e {
1783 crate::script::Expr::Variable(name) => {
1784 out.push(name.as_str());
1785 true
1786 }
1787 crate::script::Expr::MemberAccess(obj, field) => {
1788 if flatten_chain(obj, out) {
1789 out.push(field.as_str());
1790 true
1791 } else {
1792 false
1793 }
1794 }
1795 _ => false,
1796 }
1797 }
1798 let mut segs: Vec<&str> = Vec::new();
1799 if flatten_chain(array_expr, &mut segs) && !segs.is_empty() {
1800 let ctx = self.get_compile_time_context()?;
1801 let module_path = ctx.module_path.clone();
1802 let pc_address = ctx.pc_address;
1803 let analyzer = self.process_analyzer.ok_or_else(|| {
1804 CodeGenError::DwarfError("No DWARF analyzer available".to_string())
1805 })?;
1806 let module_address = ghostscope_dwarf::ModuleAddress::new(
1807 std::path::PathBuf::from(module_path),
1808 pc_address,
1809 );
1810 let base = segs[0].to_string();
1811 let rest: Vec<String> = segs[1..].iter().map(|s| s.to_string()).collect();
1812 if let Ok(Some(var)) = analyzer.plan_chain_access(&module_address, &base, &rest) {
1813 let base_var = var;
1815 return self.finish_array_access_from_base(base_var, index_expr);
1816 }
1817 }
1818 }
1819
1820 let base_var = match self.query_dwarf_for_complex_expr(array_expr)? {
1822 Some(var) => var,
1823 None => return Ok(None),
1824 };
1825
1826 self.finish_array_access_from_base(base_var, index_expr)
1827 }
1828
1829 fn finish_array_access_from_base(
1830 &mut self,
1831 base_var: VariableWithEvaluation,
1832 index_expr: &crate::script::Expr,
1833 ) -> Result<Option<VariableWithEvaluation>> {
1834 let array_type = match &base_var.dwarf_type {
1836 Some(type_info) => type_info,
1837 None => return Ok(None),
1838 };
1839
1840 let element_type = match array_type {
1842 TypeInfo::ArrayType { element_type, .. } => element_type.as_ref().clone(),
1843 _ => return Ok(None), };
1845
1846 let element_size = element_type.size();
1848
1849 let index_value: i64 = match index_expr {
1852 crate::script::Expr::Int(v) => *v,
1853 _ => {
1854 return Err(CodeGenError::NotImplemented(
1855 "Only literal integer array indices are supported (TODO)".to_string(),
1856 ))
1857 }
1858 };
1859 let element_evaluation_result = match &base_var.evaluation_result {
1860 EvaluationResult::DirectValue(_) => {
1861 return Ok(None);
1863 }
1864 EvaluationResult::MemoryLocation(location) => {
1865 match location {
1866 LocationResult::Address(addr) => {
1868 let offs = (index_value as i128) * (element_size as i128);
1869 let new_addr = (*addr as i128).saturating_add(offs);
1870 if new_addr < 0 {
1871 return Err(CodeGenError::LLVMError(
1872 "negative address after indexing".to_string(),
1873 ));
1874 }
1875 EvaluationResult::MemoryLocation(LocationResult::Address(new_addr as u64))
1876 }
1877 _ => {
1879 let array_access_steps =
1880 self.create_array_access_steps(location, element_size, index_value);
1881 EvaluationResult::MemoryLocation(LocationResult::ComputedLocation {
1882 steps: array_access_steps,
1883 })
1884 }
1885 }
1886 }
1887 EvaluationResult::Optimized => {
1888 return Ok(None);
1889 }
1890 EvaluationResult::Composite(_) => {
1891 return Ok(None);
1893 }
1894 };
1895
1896 let elem_name = format!("{}[{}]", base_var.name, index_value);
1898 let element_var = VariableWithEvaluation {
1899 name: elem_name,
1900 type_name: Self::type_info_to_name(&element_type),
1901 dwarf_type: Some(element_type),
1902 evaluation_result: element_evaluation_result,
1903 scope_depth: base_var.scope_depth,
1904 is_parameter: false,
1905 is_artificial: false,
1906 };
1907
1908 Ok(Some(element_var))
1909 }
1910
1911 pub fn query_dwarf_for_chain_access(
1913 &mut self,
1914 chain: &[String],
1915 ) -> Result<Option<VariableWithEvaluation>> {
1916 if chain.is_empty() {
1917 return Ok(None);
1918 }
1919 if chain.len() == 1 {
1921 return self.query_dwarf_for_variable(&chain[0]);
1922 }
1923 let ctx = self.get_compile_time_context()?;
1925 let module_path = ctx.module_path.clone();
1926 let pc_address = ctx.pc_address;
1927 let analyzer = self
1928 .process_analyzer
1929 .ok_or_else(|| CodeGenError::DwarfError("No DWARF analyzer available".to_string()))?;
1930 let module_address = ghostscope_dwarf::ModuleAddress::new(
1932 std::path::PathBuf::from(module_path.clone()),
1933 pc_address,
1934 );
1935 match analyzer.plan_chain_access(&module_address, &chain[0], &chain[1..]) {
1936 Ok(Some(var)) => return Ok(Some(var)),
1937 Ok(None) => {}
1938 Err(e) => {
1939 tracing::debug!("chain planner miss at current module: {}", e);
1941 }
1942 }
1943
1944 let base = &chain[0];
1945 let rest = &chain[1..];
1946 match analyzer
1947 .plan_global_chain_access(&std::path::PathBuf::from(module_path.clone()), base, rest)
1948 .map_err(|e| CodeGenError::DwarfError(e.to_string()))?
1949 {
1950 Some((mpath, v)) => {
1951 self.current_resolved_var_module_path = Some(mpath.to_string_lossy().to_string());
1952 Ok(Some(v))
1953 }
1954 None => {
1955 if chain.len() == 2 {
1957 let field_name = &chain[1];
1958 let mut matches = analyzer.find_global_variables_by_name(base);
1959 if !matches.is_empty() {
1960 let preferred: Vec<(
1961 std::path::PathBuf,
1962 ghostscope_dwarf::core::GlobalVariableInfo,
1963 )> = matches
1964 .iter()
1965 .filter(|(p, _)| p.to_string_lossy() == module_path.as_str())
1966 .cloned()
1967 .collect();
1968 let chosen = if preferred.len() == 1 {
1969 Some(preferred[0].clone())
1970 } else if preferred.is_empty() && matches.len() == 1 {
1971 Some(matches.remove(0))
1972 } else {
1973 None
1974 };
1975 if let Some((mp, info)) = chosen {
1976 if let Ok(var) = analyzer.resolve_variable_by_offsets_in_module(
1977 &mp,
1978 info.unit_offset,
1979 info.die_offset,
1980 ) {
1981 if let Some(ty) = var.dwarf_type.as_ref() {
1982 let mut t = ty;
1984 loop {
1985 match t {
1986 ghostscope_dwarf::TypeInfo::TypedefType {
1987 underlying_type,
1988 ..
1989 } => t = underlying_type.as_ref(),
1990 ghostscope_dwarf::TypeInfo::QualifiedType {
1991 underlying_type,
1992 ..
1993 } => t = underlying_type.as_ref(),
1994 _ => break,
1995 }
1996 }
1997 let mut kind: Option<&'static str> = None;
1998 let mut member_names: Vec<String> = Vec::new();
1999 match t {
2000 ghostscope_dwarf::TypeInfo::StructType {
2001 members, ..
2002 } => {
2003 kind = Some("struct");
2004 member_names =
2005 members.iter().map(|m| m.name.clone()).collect();
2006 }
2007 ghostscope_dwarf::TypeInfo::UnionType {
2008 members, ..
2009 } => {
2010 kind = Some("union");
2011 member_names =
2012 members.iter().map(|m| m.name.clone()).collect();
2013 }
2014 _ => {}
2015 }
2016 if let Some(k) = kind {
2017 member_names.sort();
2018 member_names.dedup();
2019 let list = if member_names.is_empty() {
2020 "<none>".to_string()
2021 } else {
2022 member_names.join(", ")
2023 };
2024 let msg = format!(
2025 "Unknown member '{field_name}' in {k} '{base}' (known members: {list})"
2026 );
2027 return Err(CodeGenError::TypeError(msg));
2028 }
2029 }
2030 }
2031 }
2032 }
2033 }
2034 Ok(None)
2035 }
2036 }
2037 }
2039
2040 pub fn query_dwarf_for_pointer_deref(
2042 &mut self,
2043 expr: &crate::script::Expr,
2044 ) -> Result<Option<VariableWithEvaluation>> {
2045 let ptr_var = match self.query_dwarf_for_complex_expr(expr)? {
2047 Some(var) => var,
2048 None => return Ok(None),
2049 };
2050
2051 let ptr_type = match &ptr_var.dwarf_type {
2053 Some(type_info) => type_info,
2054 None => return Ok(None),
2055 };
2056
2057 let mut pointed_type = match ptr_type {
2059 TypeInfo::PointerType { target_type, .. } => target_type.as_ref().clone(),
2060 _ => return Ok(None), };
2062
2063 if let TypeInfo::UnknownType { name } = &pointed_type {
2067 let mut candidate_names: Vec<String> = Vec::new();
2068 if !name.is_empty() && name != "void" {
2069 candidate_names.push(name.clone());
2070 }
2071 if candidate_names.is_empty() {
2073 let tn = ptr_var.type_name.trim().to_string();
2074 if let Some(idx) = tn.find('*') {
2075 let mut base = tn[..idx].trim().to_string();
2076 for prefix in [
2078 "const ",
2079 "volatile ",
2080 "restrict ",
2081 "struct ",
2082 "class ",
2083 "union ",
2084 ] {
2085 if base.starts_with(prefix) {
2086 base = base[prefix.len()..].trim().to_string();
2087 }
2088 }
2089 if !base.is_empty() && base != "void" {
2090 candidate_names.push(base);
2091 }
2092 }
2093 }
2094 let ctx = self.get_compile_time_context()?;
2095 let module_path = ctx.module_path.clone();
2096 if let Some(analyzer) = self.process_analyzer {
2097 let mut alias_used: Option<String> = None;
2098 for n in candidate_names {
2099 let mut upgraded: Option<TypeInfo> = None;
2101 if let Some(ti) = analyzer.resolve_struct_type_shallow_by_name(&n) {
2103 if ti.size() > 0 {
2104 upgraded = Some(ti);
2105 }
2106 }
2107 if upgraded.is_none() {
2108 if let Some(ti) =
2109 analyzer.resolve_struct_type_shallow_by_name_in_module(&module_path, &n)
2110 {
2111 if ti.size() > 0 {
2112 upgraded = Some(ti);
2113 }
2114 }
2115 }
2116 if upgraded.is_none() {
2118 if let Some(ti) = analyzer.resolve_union_type_shallow_by_name(&n) {
2119 if ti.size() > 0 {
2120 upgraded = Some(ti);
2121 }
2122 }
2123 }
2124 if upgraded.is_none() {
2125 if let Some(ti) =
2126 analyzer.resolve_union_type_shallow_by_name_in_module(&module_path, &n)
2127 {
2128 if ti.size() > 0 {
2129 upgraded = Some(ti);
2130 }
2131 }
2132 }
2133 if upgraded.is_none() {
2135 if let Some(ti) = analyzer.resolve_enum_type_shallow_by_name(&n) {
2136 if ti.size() > 0 {
2137 upgraded = Some(ti);
2138 }
2139 }
2140 }
2141 if upgraded.is_none() {
2142 if let Some(ti) =
2143 analyzer.resolve_enum_type_shallow_by_name_in_module(&module_path, &n)
2144 {
2145 if ti.size() > 0 {
2146 upgraded = Some(ti);
2147 }
2148 }
2149 }
2150 if let Some(ti) = upgraded {
2151 pointed_type = ti;
2152 alias_used = Some(n.clone());
2153 break;
2154 }
2155 }
2156
2157 if let Some(alias) = alias_used {
2159 match &pointed_type {
2160 TypeInfo::StructType { .. }
2161 | TypeInfo::UnionType { .. }
2162 | TypeInfo::EnumType { .. } => {
2163 pointed_type = TypeInfo::TypedefType {
2164 name: alias,
2165 underlying_type: Box::new(pointed_type.clone()),
2166 };
2167 }
2168 _ => {}
2169 }
2170 }
2171 }
2172 }
2173
2174 let deref_var = VariableWithEvaluation {
2176 name: format!("*{}", Self::expr_to_string(expr)),
2177 type_name: Self::type_info_to_name(&pointed_type),
2178 dwarf_type: Some(pointed_type),
2179 evaluation_result: self.compute_pointer_dereference(&ptr_var.evaluation_result)?,
2180 scope_depth: ptr_var.scope_depth,
2181 is_parameter: false,
2182 is_artificial: false,
2183 };
2184
2185 Ok(Some(deref_var))
2186 }
2187
2188 fn compute_pointer_dereference(
2190 &self,
2191 ptr_result: &EvaluationResult,
2192 ) -> Result<EvaluationResult> {
2193 use ghostscope_dwarf::{ComputeStep, LocationResult, MemoryAccessSize};
2194
2195 match ptr_result {
2196 EvaluationResult::MemoryLocation(location) => {
2199 let steps = [
2200 self.location_to_compute_steps(location),
2201 vec![ComputeStep::Dereference {
2203 size: MemoryAccessSize::U64,
2204 }],
2205 ]
2206 .concat();
2207
2208 Ok(EvaluationResult::MemoryLocation(
2209 LocationResult::ComputedLocation { steps },
2210 ))
2211 }
2212 EvaluationResult::DirectValue(dv) => {
2215 use ghostscope_dwarf::DirectValueResult as DV;
2216 match dv {
2217 DV::RegisterValue(reg) => Ok(EvaluationResult::MemoryLocation(
2218 LocationResult::RegisterAddress {
2219 register: *reg,
2220 offset: None,
2221 size: None,
2222 },
2223 )),
2224 DV::Constant(val) => Ok(EvaluationResult::MemoryLocation(
2225 LocationResult::Address(*val as u64),
2226 )),
2227 DV::AbsoluteAddress(val) => Ok(EvaluationResult::MemoryLocation(
2228 LocationResult::Address(*val),
2229 )),
2230 DV::ImplicitValue(bytes) => {
2231 let mut v: u64 = 0;
2233 for (i, b) in bytes.iter().take(8).enumerate() {
2234 v |= (*b as u64) << (8 * i);
2235 }
2236 Ok(EvaluationResult::MemoryLocation(LocationResult::Address(v)))
2237 }
2238 DV::ComputedValue { steps, .. } => Ok(EvaluationResult::MemoryLocation(
2239 LocationResult::ComputedLocation {
2240 steps: steps.clone(),
2241 },
2242 )),
2243 }
2244 }
2245 _ => Err(CodeGenError::NotImplemented(
2246 "Unsupported pointer dereference scenario".to_string(),
2247 )),
2248 }
2249 }
2250
2251 fn location_to_compute_steps(&self, location: &LocationResult) -> Vec<ComputeStep> {
2253 use ghostscope_dwarf::{ComputeStep, LocationResult};
2254
2255 match location {
2256 LocationResult::Address(addr) => {
2257 vec![ComputeStep::PushConstant(*addr as i64)]
2258 }
2259 LocationResult::RegisterAddress {
2260 register, offset, ..
2261 } => {
2262 let mut steps = vec![ComputeStep::LoadRegister(*register)];
2263 if let Some(offset) = offset {
2264 steps.push(ComputeStep::PushConstant(*offset));
2265 steps.push(ComputeStep::Add);
2266 }
2267 steps
2268 }
2269 LocationResult::ComputedLocation { steps } => steps.clone(),
2270 }
2271 }
2272
2273 fn expr_to_string(expr: &crate::script::Expr) -> String {
2275 use crate::script::Expr;
2276
2277 match expr {
2278 Expr::Variable(name) => name.clone(),
2279 Expr::MemberAccess(obj, field) => format!("{}.{}", Self::expr_to_string(obj), field),
2280 Expr::ArrayAccess(arr, _) => format!("{}[index]", Self::expr_to_string(arr)),
2281 Expr::ChainAccess(chain) => chain.join("."),
2282 Expr::PointerDeref(expr) => format!("*{}", Self::expr_to_string(expr)),
2283 _ => "expr".to_string(),
2284 }
2285 }
2286
2287 fn type_info_to_name(type_info: &TypeInfo) -> String {
2289 match type_info {
2290 TypeInfo::BaseType { name, .. } => name.clone(),
2291 TypeInfo::PointerType { target_type, .. } => {
2292 format!("{}*", Self::type_info_to_name(target_type))
2293 }
2294 TypeInfo::ArrayType {
2295 element_type,
2296 element_count,
2297 ..
2298 } => {
2299 if let Some(count) = element_count {
2300 format!("{}[{}]", Self::type_info_to_name(element_type), count)
2301 } else {
2302 format!("{}[]", Self::type_info_to_name(element_type))
2303 }
2304 }
2305 TypeInfo::StructType { name, .. } => format!("struct {name}"),
2306 TypeInfo::UnionType { name, .. } => format!("union {name}"),
2307 TypeInfo::EnumType { name, .. } => format!("enum {name}"),
2308 TypeInfo::BitfieldType {
2309 underlying_type,
2310 bit_offset,
2311 bit_size,
2312 } => {
2313 format!(
2314 "bitfield<{}:{}> {}",
2315 bit_offset,
2316 bit_size,
2317 Self::type_info_to_name(underlying_type)
2318 )
2319 }
2320 TypeInfo::TypedefType { name, .. } => name.clone(),
2321 TypeInfo::QualifiedType {
2322 underlying_type, ..
2323 } => Self::type_info_to_name(underlying_type),
2324 TypeInfo::FunctionType { .. } => "function".to_string(),
2325 TypeInfo::UnknownType { name } => name.clone(),
2326 TypeInfo::OptimizedOut { name } => format!("<optimized_out> {name}"),
2327 }
2328 }
2329
2330 fn create_array_access_steps(
2332 &self,
2333 base_location: &LocationResult,
2334 element_size: u64,
2335 index: i64,
2336 ) -> Vec<ComputeStep> {
2337 let mut steps = Vec::new();
2338
2339 match base_location {
2341 LocationResult::Address(addr) => {
2342 steps.push(ComputeStep::PushConstant(*addr as i64));
2343 }
2344 LocationResult::RegisterAddress {
2345 register, offset, ..
2346 } => {
2347 steps.push(ComputeStep::LoadRegister(*register));
2348 if let Some(offset) = offset {
2349 if *offset != 0 {
2350 steps.push(ComputeStep::PushConstant(*offset));
2351 steps.push(ComputeStep::Add);
2352 }
2353 }
2354 }
2355 LocationResult::ComputedLocation { steps: base_steps } => {
2356 steps.extend(base_steps.clone());
2357 }
2358 }
2359
2360 steps.push(ComputeStep::PushConstant(index)); steps.push(ComputeStep::PushConstant(element_size as i64)); steps.push(ComputeStep::Mul); steps.push(ComputeStep::Add); steps
2367 }
2368
2369 pub fn compute_pointed_location_with_index(
2373 &mut self,
2374 ptr_expr: &crate::script::Expr,
2375 index: i64,
2376 ) -> Result<(EvaluationResult, TypeInfo)> {
2377 use ghostscope_dwarf::{
2378 ComputeStep, EvaluationResult as ER, LocationResult as LR, TypeInfo,
2379 };
2380
2381 let ptr_var = self
2383 .query_dwarf_for_complex_expr(ptr_expr)?
2384 .ok_or_else(|| CodeGenError::VariableNotFound(format!("{ptr_expr:?}")))?;
2385
2386 let ptr_ty = ptr_var.dwarf_type.as_ref().ok_or_else(|| {
2387 CodeGenError::DwarfError("Expression has no DWARF type information".to_string())
2388 })?;
2389
2390 let mut ty = ptr_ty;
2392 loop {
2393 match ty {
2394 TypeInfo::TypedefType {
2395 underlying_type, ..
2396 } => ty = underlying_type.as_ref(),
2397 TypeInfo::QualifiedType {
2398 underlying_type, ..
2399 } => ty = underlying_type.as_ref(),
2400 _ => break,
2401 }
2402 }
2403
2404 let (elem_ty, elem_size) = match ty {
2406 TypeInfo::PointerType { target_type, .. } => {
2407 let et = target_type.as_ref().clone();
2408 let es = et.size();
2409 let es = if es == 0 { 1 } else { es };
2410 (et, es)
2411 }
2412 TypeInfo::ArrayType { element_type, .. } => {
2413 let et = element_type.as_ref().clone();
2414 let es = et.size();
2415 let es = if es == 0 { 1 } else { es };
2416 (et, es)
2417 }
2418 TypeInfo::FunctionType { .. } => {
2419 return Err(CodeGenError::TypeError(
2420 "Pointer arithmetic is not supported on function pointers".to_string(),
2421 ))
2422 }
2423 _ => {
2424 return Err(CodeGenError::TypeError(
2425 "Pointer arithmetic requires a pointer or array expression".to_string(),
2426 ))
2427 }
2428 };
2429
2430 let base_loc_eval = self.compute_pointer_dereference(&ptr_var.evaluation_result)?;
2432 let base_loc = match &base_loc_eval {
2433 ER::MemoryLocation(loc) => loc,
2434 _ => {
2435 return Err(CodeGenError::DwarfError(
2436 "Failed to compute base location for pointer arithmetic".to_string(),
2437 ))
2438 }
2439 };
2440
2441 let steps = {
2443 let mut s = self.location_to_compute_steps(base_loc);
2444 s.push(ComputeStep::PushConstant(index));
2446 s.push(ComputeStep::PushConstant(elem_size as i64));
2447 s.push(ComputeStep::Mul);
2448 s.push(ComputeStep::Add);
2449 s
2450 };
2451
2452 Ok((ER::MemoryLocation(LR::ComputedLocation { steps }), elem_ty))
2453 }
2454}
2455
2456#[cfg(test)]
2457mod tests {
2458 use super::*;
2459 use inkwell::context::Context as LlvmContext;
2460
2461 #[test]
2462 fn aggregate_address_returns_pointer_for_struct_and_array() {
2463 let llctx = LlvmContext::create();
2464 let opts = crate::CompileOptions::default();
2465 let mut ctx = EbpfContext::new(&llctx, "agg_ptr", Some(0), &opts).expect("ctx");
2466 ctx.create_basic_ebpf_function("f").expect("fn");
2468 ctx.__test_ensure_proc_offsets_map().expect("map");
2470 ctx.__test_alloc_pm_key().expect("pm_key");
2472 ctx.set_compile_time_context(0, "/nonexistent/module".to_string());
2474
2475 let st = ghostscope_protocol::TypeInfo::StructType {
2477 name: "S".to_string(),
2478 size: 80,
2479 members: vec![],
2480 };
2481 let eval = EvaluationResult::MemoryLocation(LocationResult::Address(0x1000));
2482 let v = ctx
2483 .evaluate_result_to_llvm_value(&eval, &st, "S", 0, None)
2484 .expect("eval");
2485 match v {
2486 BasicValueEnum::PointerValue(_) => {}
2487 other => panic!("expected PointerValue for struct, got {other:?}"),
2488 }
2489
2490 let arr = ghostscope_protocol::TypeInfo::ArrayType {
2492 element_type: Box::new(ghostscope_protocol::TypeInfo::BaseType {
2493 name: "int".to_string(),
2494 size: 4,
2495 encoding: ghostscope_dwarf::constants::DW_ATE_signed.0 as u16,
2496 }),
2497 element_count: Some(4),
2498 total_size: Some(16),
2499 };
2500 let v2 = ctx
2501 .evaluate_result_to_llvm_value(&eval, &arr, "A", 0, None)
2502 .expect("eval2");
2503 match v2 {
2504 BasicValueEnum::PointerValue(_) => {}
2505 other => panic!("expected PointerValue for array, got {other:?}"),
2506 }
2507 }
2508
2509 #[test]
2510 fn scalar_address_reads_value() {
2511 let llctx = LlvmContext::create();
2512 let opts = crate::CompileOptions::default();
2513 let mut ctx = EbpfContext::new(&llctx, "scalar_val", Some(0), &opts).expect("ctx");
2514 ctx.create_basic_ebpf_function("f").expect("fn");
2515 ctx.__test_ensure_proc_offsets_map().expect("map");
2517 ctx.__test_alloc_pm_key().expect("pm_key");
2519 ctx.set_compile_time_context(0, "/nonexistent/module".to_string());
2521
2522 let bt = ghostscope_protocol::TypeInfo::BaseType {
2524 name: "int".to_string(),
2525 size: 4,
2526 encoding: ghostscope_dwarf::constants::DW_ATE_signed.0 as u16,
2527 };
2528 let eval = EvaluationResult::MemoryLocation(LocationResult::Address(0x2000));
2529 let v = ctx
2530 .evaluate_result_to_llvm_value(&eval, &bt, "x", 0, None)
2531 .expect("eval");
2532 match v {
2533 BasicValueEnum::IntValue(_) => {}
2534 other => panic!("expected IntValue for scalar, got {other:?}"),
2535 }
2536 assert!(
2537 ctx.module.get_global("_temp_read_buffer_4").is_none(),
2538 "scalar reads should use per-invocation scratch, not shared temp globals"
2539 );
2540 }
2541
2542 #[test]
2543 fn computed_location_supports_dereference_before_trailing_arithmetic() {
2544 let llctx = LlvmContext::create();
2545 let opts = crate::CompileOptions::default();
2546 let mut ctx = EbpfContext::new(&llctx, "computed_addr", Some(0), &opts).expect("ctx");
2547 ctx.create_basic_ebpf_function("f").expect("fn");
2548 ctx.__test_ensure_proc_offsets_map().expect("map");
2549 ctx.__test_alloc_pm_key().expect("pm_key");
2550 ctx.set_compile_time_context(0, "/nonexistent/module".to_string());
2551
2552 let eval = EvaluationResult::MemoryLocation(LocationResult::ComputedLocation {
2553 steps: vec![
2554 ComputeStep::PushConstant(0x3000),
2555 ComputeStep::Dereference {
2556 size: MemoryAccessSize::U64,
2557 },
2558 ComputeStep::PushConstant(16),
2559 ComputeStep::Add,
2560 ],
2561 });
2562
2563 let addr = ctx
2564 .evaluation_result_to_address_with_hint(&eval, None, None)
2565 .expect("computed address with mid-stream dereference should compile");
2566 assert_eq!(addr.get_type().get_bit_width(), 64);
2567 }
2568}