ghostscope_compiler/ebpf/
helper_functions.rs

1//! eBPF helper function management
2//!
3//! This module handles eBPF helper function calls, register mapping, and pt_regs
4//! access for different target architectures.
5
6use super::context::{CodeGenError, EbpfContext, Result};
7use aya_ebpf_bindings::bindings::bpf_func_id::{
8    BPF_FUNC_get_current_pid_tgid, BPF_FUNC_ktime_get_ns, BPF_FUNC_map_lookup_elem,
9    BPF_FUNC_perf_event_output, BPF_FUNC_probe_read_user, BPF_FUNC_probe_read_user_str,
10    BPF_FUNC_ringbuf_output,
11};
12use ghostscope_dwarf::MemoryAccessSize;
13use ghostscope_platform::register_mapping;
14use inkwell::types::{BasicType, BasicTypeEnum};
15use inkwell::values::{BasicMetadataValueEnum, BasicValueEnum, IntValue, PointerValue};
16use inkwell::AddressSpace;
17
18impl<'ctx> EbpfContext<'ctx> {
19    /// Get or create a static i8 buffer global of a given size, returning its ArrayType and pointer
20    pub fn get_or_create_i8_buffer(
21        &mut self,
22        size: u32,
23        name_prefix: &str,
24    ) -> (
25        inkwell::types::ArrayType<'ctx>,
26        inkwell::values::PointerValue<'ctx>,
27    ) {
28        let array_ty = self.context.i8_type().array_type(size);
29        let name = format!("{name_prefix}_{size}");
30        let global_ptr = match self.module.get_global(&name) {
31            Some(g) => g.as_pointer_value(),
32            None => {
33                let g = self
34                    .module
35                    .add_global(array_ty, Some(AddressSpace::default()), &name);
36                g.set_initializer(&array_ty.const_zero());
37                g.as_pointer_value()
38            }
39        };
40        (array_ty, global_ptr)
41    }
42
43    /// Read a user C-string into a static buffer using bpf_probe_read_user_str.
44    /// Returns (buffer_ptr, len_including_nul).
45    pub fn read_user_cstr_into_buffer(
46        &mut self,
47        src_addr: inkwell::values::IntValue<'ctx>,
48        size: u32,
49        name_prefix: &str,
50    ) -> Result<(
51        inkwell::values::PointerValue<'ctx>,
52        inkwell::values::IntValue<'ctx>,
53        inkwell::types::ArrayType<'ctx>,
54    )> {
55        let (arr_ty, buf_global) = self.get_or_create_i8_buffer(size, name_prefix);
56
57        let ptr_ty = self.context.ptr_type(AddressSpace::default());
58        // Cast addresses to void pointers
59        let dst_ptr = self
60            .builder
61            .build_bit_cast(buf_global, ptr_ty, "dst_ptr")
62            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
63        let src_ptr = self
64            .builder
65            .build_int_to_ptr(src_addr, ptr_ty, "src_ptr")
66            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
67
68        // Helper signature: long fn(void *dst, u32 size, const void *src)
69        let i64_ty = self.context.i64_type();
70        let i32_ty = self.context.i32_type();
71        let args: [inkwell::values::BasicValueEnum; 3] = [
72            dst_ptr,
73            i32_ty.const_int(size as u64, false).into(),
74            inkwell::values::BasicValueEnum::PointerValue(src_ptr),
75        ];
76        let ret = self.create_bpf_helper_call(
77            BPF_FUNC_probe_read_user_str as u64,
78            &args,
79            i64_ty.into(),
80            "probe_read_user_str",
81        )?;
82        let len = if let inkwell::values::BasicValueEnum::IntValue(iv) = ret {
83            iv
84        } else {
85            return Err(CodeGenError::LLVMError(
86                "probe_read_user_str did not return integer".to_string(),
87            ));
88        };
89        Ok((buf_global, len, arr_ty))
90    }
91
92    /// Read raw user bytes into a static buffer using bpf_probe_read_user.
93    /// Returns (buffer_ptr, status==0?).
94    pub fn read_user_bytes_into_buffer(
95        &mut self,
96        src_addr: inkwell::values::IntValue<'ctx>,
97        size: u32,
98        name_prefix: &str,
99    ) -> Result<(
100        inkwell::values::PointerValue<'ctx>,
101        inkwell::values::IntValue<'ctx>,
102        inkwell::types::ArrayType<'ctx>,
103    )> {
104        let (arr_ty, buf_global) = self.get_or_create_i8_buffer(size, name_prefix);
105        let ptr_ty = self.context.ptr_type(AddressSpace::default());
106        let i32_ty = self.context.i32_type();
107        let i64_ty = self.context.i64_type();
108        // Cast addresses to void pointers
109        let dst_ptr = self
110            .builder
111            .build_bit_cast(buf_global, ptr_ty, "dst_ptr")
112            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
113        let src_ptr = self
114            .builder
115            .build_int_to_ptr(src_addr, ptr_ty, "src_ptr")
116            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
117
118        let args: [inkwell::values::BasicValueEnum; 3] = [
119            dst_ptr,
120            i32_ty.const_int(size as u64, false).into(),
121            inkwell::values::BasicValueEnum::PointerValue(src_ptr),
122        ];
123        // Helper returns long (0 on success, -errno on failure)
124        let ret = self.create_bpf_helper_call(
125            BPF_FUNC_probe_read_user as u64,
126            &args,
127            i64_ty.into(),
128            "probe_read_user",
129        )?;
130        let status = if let inkwell::values::BasicValueEnum::IntValue(iv) = ret {
131            iv
132        } else {
133            return Err(CodeGenError::LLVMError(
134                "probe_read_user did not return integer".to_string(),
135            ));
136        };
137        Ok((buf_global, status, arr_ty))
138    }
139    /// Compute runtime address from link-time address using proc_module_offsets map
140    /// section_type: 0=text, 1=rodata, 2=data, 3=bss; other values fallback to data
141    pub fn generate_runtime_address_from_offsets(
142        &mut self,
143        link_addr: IntValue<'ctx>,
144        section_type: u8,
145        module_cookie: u64,
146    ) -> Result<(IntValue<'ctx>, IntValue<'ctx>)> {
147        let i64_type = self.context.i64_type();
148        let ptr_type = self.context.ptr_type(AddressSpace::default());
149
150        // Resolve map global pointer
151        let map_global = self
152            .module
153            .get_global("proc_module_offsets")
154            .ok_or_else(|| {
155                CodeGenError::LLVMError("proc_module_offsets map not found".to_string())
156            })?;
157        let map_ptr = map_global.as_pointer_value();
158        let map_ptr_cast = self
159            .builder
160            .build_bit_cast(map_ptr, ptr_type, "map_ptr")
161            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
162
163        // Use per-invocation key buffer [4 x u32] pre-allocated in entry block
164        // struct { u32 pid; u32 pad; u32 cookie_lo; u32 cookie_hi; }
165        let i32_type = self.context.i32_type();
166        let key_arr_ty = i32_type.array_type(4);
167        let key_alloca = self.pm_key_alloca.ok_or_else(|| {
168            CodeGenError::LLVMError("pm_key not allocated in entry block".to_string())
169        })?;
170        // Get i32* to the first element (&key[0])
171        let zero = i32_type.const_zero();
172        let base_i32_ptr = unsafe {
173            self.builder
174                .build_gep(key_arr_ty, key_alloca, &[zero, zero], "pm_key_i32_ptr")
175                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
176        };
177
178        // Write pid (u32)
179        let helper_id = i64_type.const_int(BPF_FUNC_get_current_pid_tgid as u64, false);
180        let helper_fn_type = i64_type.fn_type(&[], false);
181        let helper_fn_ptr = self
182            .builder
183            .build_int_to_ptr(helper_id, ptr_type, "get_pid_fn")
184            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
185        let pid_tgid = self
186            .builder
187            .build_indirect_call(helper_fn_type, helper_fn_ptr, &[], "pid_tgid")
188            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
189            .try_as_basic_value()
190            .left()
191            .ok_or_else(|| {
192                CodeGenError::LLVMError("get_current_pid_tgid returned void".to_string())
193            })?;
194        let pid = if let BasicValueEnum::IntValue(v) = pid_tgid {
195            // pid = upper 32 bits
196            let shifted = self
197                .builder
198                .build_right_shift(v, i64_type.const_int(32, false), false, "pid_shift")
199                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
200            self.builder
201                .build_int_truncate(shifted, i32_type, "pid32")
202                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
203        } else {
204            return Err(CodeGenError::LLVMError(
205                "pid_tgid is not IntValue".to_string(),
206            ));
207        };
208
209        // Store pid at key[0]
210        self.builder
211            .build_store(base_i32_ptr, pid)
212            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
213
214        // Zero padding at key[1] for deterministic key bytes
215        let idx1 = i32_type.const_int(1, false);
216        let pad_ptr = unsafe {
217            self.builder
218                .build_gep(self.context.i32_type(), base_i32_ptr, &[idx1], "pad_ptr")
219                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
220        };
221        self.builder
222            .build_store(pad_ptr, self.context.i32_type().const_zero())
223            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
224
225        // Store cookie_lo at key[2] and cookie_hi at key[3] (key[1] is padding for 8-byte alignment)
226        let cookie_lo = i32_type.const_int(module_cookie & 0xffff_ffff, false);
227        let cookie_hi = i32_type.const_int(module_cookie >> 32, false);
228        let idx2 = i32_type.const_int(2, false);
229        let idx3 = i32_type.const_int(3, false);
230        // key[1] left as padding = 0 by default
231        let cookie_lo_ptr = unsafe {
232            self.builder
233                .build_gep(
234                    self.context.i32_type(),
235                    base_i32_ptr,
236                    &[idx2],
237                    "cookie_lo_ptr",
238                )
239                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
240        };
241        let cookie_hi_ptr = unsafe {
242            self.builder
243                .build_gep(
244                    self.context.i32_type(),
245                    base_i32_ptr,
246                    &[idx3],
247                    "cookie_hi_ptr",
248                )
249                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
250        };
251        self.builder
252            .build_store(cookie_lo_ptr, cookie_lo)
253            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
254        self.builder
255            .build_store(cookie_hi_ptr, cookie_hi)
256            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
257
258        // Call bpf_map_lookup_elem(map, &key)
259        let lookup_id = i64_type.const_int(BPF_FUNC_map_lookup_elem as u64, false);
260        let lookup_fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
261        let lookup_fn_ptr = self
262            .builder
263            .build_int_to_ptr(lookup_id, ptr_type, "lookup_fn")
264            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
265        // Pass pointer to the beginning of the key buffer (void*)
266        let key_arg = self
267            .builder
268            .build_bit_cast(key_alloca, ptr_type, "key_arg")
269            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
270        let args: Vec<BasicMetadataValueEnum> = vec![map_ptr_cast.into(), key_arg.into()];
271        let val_ptr_any = self
272            .builder
273            .build_indirect_call(lookup_fn_type, lookup_fn_ptr, &args, "val_ptr_any")
274            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
275            .try_as_basic_value()
276            .left()
277            .ok_or_else(|| CodeGenError::LLVMError("map_lookup_elem returned void".to_string()))?;
278
279        let null_ptr = ptr_type.const_null();
280        let found_block = self.context.append_basic_block(
281            self.builder
282                .get_insert_block()
283                .unwrap()
284                .get_parent()
285                .unwrap(),
286            "found_offsets",
287        );
288        let miss_block = self.context.append_basic_block(
289            self.builder
290                .get_insert_block()
291                .unwrap()
292                .get_parent()
293                .unwrap(),
294            "miss_offsets",
295        );
296        let cont_block = self.context.append_basic_block(
297            self.builder
298                .get_insert_block()
299                .unwrap()
300                .get_parent()
301                .unwrap(),
302            "cont_offsets",
303        );
304
305        // Compare against NULL
306        let val_ptr = if let BasicValueEnum::PointerValue(p) = val_ptr_any {
307            p
308        } else {
309            null_ptr
310        };
311        let is_null = self
312            .builder
313            .build_int_compare(
314                inkwell::IntPredicate::EQ,
315                self.builder
316                    .build_ptr_to_int(val_ptr, i64_type, "val_ptr_i64")
317                    .map_err(|e| CodeGenError::LLVMError(e.to_string()))?,
318                i64_type.const_zero(),
319                "is_null_offsets",
320            )
321            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
322        self.builder
323            .build_conditional_branch(is_null, miss_block, found_block)
324            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
325
326        // Found: load appropriate offset based on section_type
327        self.builder.position_at_end(found_block);
328        // Cast value pointer (void*) to i64* for loading 64-bit offsets
329        // Use opaque pointer type (LLVM15+): model as generic pointer
330        let i64_ptr_ty = self.context.ptr_type(AddressSpace::default());
331        let val_u64_ptr = self
332            .builder
333            .build_pointer_cast(val_ptr, i64_ptr_ty, "val_u64_ptr")
334            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
335        let load_field = |idx: u64,
336                          ctx: &mut EbpfContext<'ctx>,
337                          base: PointerValue<'ctx>|
338         -> Result<IntValue<'ctx>> {
339            // GEP in i64 element space
340            let idx_i32 = ctx.context.i32_type().const_int(idx, false);
341            let field_ptr = unsafe {
342                ctx.builder
343                    .build_gep(ctx.context.i64_type(), base, &[idx_i32], "field_ptr_i64")
344                    .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
345            };
346            let loaded = ctx
347                .builder
348                .build_load(ctx.context.i64_type(), field_ptr, "loaded_offset")
349                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
350            if let BasicValueEnum::IntValue(iv) = loaded {
351                Ok(iv)
352            } else {
353                Err(CodeGenError::LLVMError("offset load failed".to_string()))
354            }
355        };
356        let st = section_type;
357        let off_text = load_field(0, self, val_u64_ptr)?;
358        let off_rodata = load_field(1, self, val_u64_ptr)?;
359        let off_data = load_field(2, self, val_u64_ptr)?;
360        let off_bss = load_field(3, self, val_u64_ptr)?;
361        // Build a bottom-up cascade to preserve earlier choices:
362        // tmp  = (section==data)   ? off_data   : off_bss
363        // tmp2 = (section==rodata) ? off_rodata : tmp
364        // off  = (section==text)   ? off_text  : tmp2
365        let st_val = i32_type.const_int(st as u64, false);
366        let eq_text = self
367            .builder
368            .build_int_compare(
369                inkwell::IntPredicate::EQ,
370                st_val,
371                i32_type.const_int(0, false),
372                "is_text",
373            )
374            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
375        let eq_ro = self
376            .builder
377            .build_int_compare(
378                inkwell::IntPredicate::EQ,
379                st_val,
380                i32_type.const_int(1, false),
381                "is_ro",
382            )
383            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
384        let eq_da = self
385            .builder
386            .build_int_compare(
387                inkwell::IntPredicate::EQ,
388                st_val,
389                i32_type.const_int(2, false),
390                "is_da",
391            )
392            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
393
394        let tmp_any = self
395            .builder
396            .build_select(eq_da, off_data, off_bss, "sel_data_bss")
397            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
398        let tmp = tmp_any.into_int_value();
399
400        let tmp2_any = self
401            .builder
402            .build_select(eq_ro, off_rodata, tmp, "sel_rodata_else")
403            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
404        let tmp2 = tmp2_any.into_int_value();
405
406        let off_final_any = self
407            .builder
408            .build_select(eq_text, off_text, tmp2, "sel_text_else")
409            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
410        let off_final = off_final_any.into_int_value();
411        let rt_addr = self
412            .builder
413            .build_int_add(link_addr, off_final, "runtime_addr")
414            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
415        self.builder
416            .build_unconditional_branch(cont_block)
417            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
418
419        // Miss: return link_addr as-is (will likely fault and set ReadError)
420        self.builder.position_at_end(miss_block);
421        self.builder
422            .build_unconditional_branch(cont_block)
423            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
424
425        // Phi to merge address
426        self.builder.position_at_end(cont_block);
427        let phi = self
428            .builder
429            .build_phi(i64_type, "addr_phi")
430            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
431        phi.add_incoming(&[(&rt_addr, found_block), (&link_addr, miss_block)]);
432        let final_addr = phi.as_basic_value().into_int_value();
433
434        // Phi to merge found-flag (i1): 1 on found, 0 on miss
435        let i1_type = self.context.bool_type();
436        let one = i1_type.const_int(1, false);
437        let zero = i1_type.const_int(0, false);
438        let flag_phi = self
439            .builder
440            .build_phi(i1_type, "off_found_phi")
441            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
442        flag_phi.add_incoming(&[(&one, found_block), (&zero, miss_block)]);
443        let found_flag = flag_phi.as_basic_value().into_int_value();
444
445        self.store_offsets_found_flag(found_flag)?;
446        Ok((final_addr, found_flag))
447    }
448    /// Load a register value from pt_regs
449    pub fn load_register_value(
450        &mut self,
451        reg_num: u16,
452        pt_regs_ptr: PointerValue<'ctx>,
453    ) -> Result<BasicValueEnum<'ctx>> {
454        // Check cache first
455        if let Some(cached_value) = self.register_cache.get(&reg_num) {
456            return Ok((*cached_value).into());
457        }
458
459        // Map DWARF register number to pt_regs offset
460        let pt_regs_offset = self.dwarf_reg_to_pt_regs_offset(reg_num)?;
461
462        // Calculate pointer to register in pt_regs structure
463        let i64_type = self.context.i64_type();
464        let offset_value = i64_type.const_int(pt_regs_offset as u64, false);
465
466        let reg_ptr = unsafe {
467            self.builder
468                .build_gep(
469                    i64_type,
470                    pt_regs_ptr,
471                    &[offset_value],
472                    &format!("reg_{reg_num}_ptr"),
473                )
474                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
475        };
476
477        // Load the register value
478        let reg_value = self
479            .builder
480            .build_load(i64_type, reg_ptr, &format!("reg_{reg_num}"))
481            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
482
483        if let BasicValueEnum::IntValue(int_val) = reg_value {
484            // Cache the value
485            self.register_cache.insert(reg_num, int_val);
486            Ok(reg_value)
487        } else {
488            Err(CodeGenError::RegisterMappingError(format!(
489                "Failed to load register {reg_num} as integer"
490            )))
491        }
492    }
493
494    /// Generate memory read using bpf_probe_read_user
495    pub fn generate_memory_read(
496        &mut self,
497        addr: IntValue<'ctx>,
498        size: MemoryAccessSize,
499    ) -> Result<BasicValueEnum<'ctx>> {
500        let i64_type = self.context.i64_type();
501        let ptr_type = self.context.ptr_type(AddressSpace::default());
502        let zero_const = i64_type.const_zero();
503        let offsets_found = self.load_offsets_found_flag()?;
504        let not_found = self
505            .builder
506            .build_not(offsets_found, "offsets_miss")
507            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
508
509        let result_size = size.bytes();
510        let buffer_name = format!("_temp_read_buffer_{result_size}");
511        let global_buffer = match self.module.get_global(&buffer_name) {
512            Some(existing) => existing.as_pointer_value(),
513            None => {
514                let array_type = self.context.i8_type().array_type(result_size as u32);
515                let global =
516                    self.module
517                        .add_global(array_type, Some(AddressSpace::default()), &buffer_name);
518                global.set_initializer(&array_type.const_zero());
519                global.as_pointer_value()
520            }
521        };
522
523        let stack_ptr = global_buffer;
524        let dst_ptr = self
525            .builder
526            .build_bit_cast(stack_ptr, ptr_type, "dst_ptr")
527            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
528        let base_src_ptr = self
529            .builder
530            .build_int_to_ptr(addr, ptr_type, "src_ptr")
531            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
532        let null_ptr = ptr_type.const_null();
533        let src_ptr = self
534            .builder
535            .build_select::<BasicValueEnum<'ctx>, _>(
536                offsets_found,
537                base_src_ptr.into(),
538                null_ptr.into(),
539                "src_or_null",
540            )
541            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
542            .into_pointer_value();
543
544        let i32_type = self.context.i32_type();
545        let helper_id = i64_type.const_int(BPF_FUNC_probe_read_user as u64, false);
546        let helper_fn_type =
547            i32_type.fn_type(&[ptr_type.into(), i32_type.into(), ptr_type.into()], false);
548        let helper_fn_ptr = self
549            .builder
550            .build_int_to_ptr(helper_id, ptr_type, "probe_read_user_fn")
551            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
552        let size_val = i32_type.const_int(result_size as u64, false);
553        let zero_i32 = i32_type.const_zero();
554        let effective_size = self
555            .builder
556            .build_select::<BasicValueEnum<'ctx>, _>(
557                offsets_found,
558                size_val.into(),
559                zero_i32.into(),
560                "size_or_zero",
561            )
562            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
563            .into_int_value();
564        let call_args: Vec<BasicMetadataValueEnum> =
565            vec![dst_ptr.into(), effective_size.into(), src_ptr.into()];
566
567        let _ = self
568            .builder
569            .build_indirect_call(
570                helper_fn_type,
571                helper_fn_ptr,
572                &call_args,
573                "probe_read_result",
574            )
575            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
576
577        let result_type: BasicTypeEnum = match size {
578            MemoryAccessSize::U8 => self.context.i8_type().into(),
579            MemoryAccessSize::U16 => self.context.i16_type().into(),
580            MemoryAccessSize::U32 => self.context.i32_type().into(),
581            MemoryAccessSize::U64 => self.context.i64_type().into(),
582        };
583        let typed_ptr = self
584            .builder
585            .build_bit_cast(
586                stack_ptr,
587                self.context.ptr_type(AddressSpace::default()),
588                "typed_ptr",
589            )
590            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
591        let loaded_value = if let BasicValueEnum::PointerValue(ptr) = typed_ptr {
592            self.builder
593                .build_load(result_type, ptr, "loaded_value")
594                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
595        } else {
596            return Err(CodeGenError::MemoryAccessError(
597                "Failed to cast result pointer".to_string(),
598            ));
599        };
600
601        let loaded_i64 = if let BasicValueEnum::IntValue(int_val) = loaded_value {
602            if int_val.get_type().get_bit_width() < 64 {
603                self.builder
604                    .build_int_z_extend(int_val, i64_type, "extended")
605                    .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
606            } else {
607                int_val
608            }
609        } else {
610            return Err(CodeGenError::MemoryAccessError(
611                "Expected integer value from memory read".to_string(),
612            ));
613        };
614
615        // Record failure if offsets were missing.
616        let i8_type = self.context.i8_type();
617        let miss_i8 = self
618            .builder
619            .build_int_z_extend(not_found, i8_type, "miss_i8")
620            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
621        let fail_ptr = self.get_or_create_flag_global("_gs_any_fail");
622        let cur_fail = self
623            .builder
624            .build_load(i8_type, fail_ptr, "cur_fail")
625            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
626            .into_int_value();
627        let new_fail = self
628            .builder
629            .build_or(cur_fail, miss_i8, "fail_or_miss")
630            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
631        self.builder
632            .build_store(fail_ptr, new_fail)
633            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
634
635        let zero_bv: BasicValueEnum = zero_const.into();
636        let val_bv: BasicValueEnum = loaded_i64.into();
637        let sel = self
638            .builder
639            .build_select::<BasicValueEnum<'ctx>, _>(
640                offsets_found,
641                val_bv,
642                zero_bv,
643                "offset_value_or_zero",
644            )
645            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
646        Ok(sel)
647    }
648
649    /// Generate memory read with runtime status capture (for control-flow conditions).
650    /// On helper failure, sets condition error code (if active) and returns zero value.
651    pub fn generate_memory_read_with_status(
652        &mut self,
653        addr: IntValue<'ctx>,
654        size: MemoryAccessSize,
655    ) -> Result<BasicValueEnum<'ctx>> {
656        let i64_type = self.context.i64_type();
657        let ptr_type = self.context.ptr_type(AddressSpace::default());
658        let zero_const = i64_type.const_zero();
659
660        let offsets_found = self.load_offsets_found_flag()?;
661        let not_found = self
662            .builder
663            .build_not(offsets_found, "offsets_miss_cf")
664            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
665
666        let result_size = size.bytes();
667        let buffer_name = format!("_temp_read_buffer_{result_size}");
668        let global_buffer = match self.module.get_global(&buffer_name) {
669            Some(existing) => existing.as_pointer_value(),
670            None => {
671                let array_type = self.context.i8_type().array_type(result_size as u32);
672                let global =
673                    self.module
674                        .add_global(array_type, Some(AddressSpace::default()), &buffer_name);
675                global.set_initializer(&array_type.const_zero());
676                global.as_pointer_value()
677            }
678        };
679
680        let dst_ptr = self
681            .builder
682            .build_bit_cast(global_buffer, ptr_type, "dst_ptr")
683            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
684        let base_src_ptr = self
685            .builder
686            .build_int_to_ptr(addr, ptr_type, "src_ptr")
687            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
688        let null_ptr = ptr_type.const_null();
689        let src_ptr = self
690            .builder
691            .build_select::<BasicValueEnum<'ctx>, _>(
692                offsets_found,
693                base_src_ptr.into(),
694                null_ptr.into(),
695                "src_or_null_cf",
696            )
697            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
698            .into_pointer_value();
699
700        let i32_type = self.context.i32_type();
701        let helper_id = i64_type.const_int(BPF_FUNC_probe_read_user as u64, false);
702        let helper_fn_type =
703            i32_type.fn_type(&[ptr_type.into(), i32_type.into(), ptr_type.into()], false);
704        let helper_fn_ptr = self
705            .builder
706            .build_int_to_ptr(helper_id, ptr_type, "probe_read_user_fn")
707            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
708        let size_val = i32_type.const_int(result_size as u64, false);
709        let zero_i32 = i32_type.const_zero();
710        let effective_size = self
711            .builder
712            .build_select::<BasicValueEnum<'ctx>, _>(
713                offsets_found,
714                size_val.into(),
715                zero_i32.into(),
716                "size_or_zero_cf",
717            )
718            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
719            .into_int_value();
720        let call_args: Vec<BasicMetadataValueEnum> =
721            vec![dst_ptr.into(), effective_size.into(), src_ptr.into()];
722
723        let call_site = self
724            .builder
725            .build_indirect_call(
726                helper_fn_type,
727                helper_fn_ptr,
728                &call_args,
729                "probe_read_result_cf",
730            )
731            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
732        let ret_iv = call_site.try_as_basic_value().left().ok_or_else(|| {
733            CodeGenError::LLVMError("Expected integer return from helper".to_string())
734        })?;
735        let ret_i32 = ret_iv.into_int_value();
736
737        let read_fail = self
738            .builder
739            .build_int_compare(
740                inkwell::IntPredicate::NE,
741                ret_i32,
742                i32_type.const_zero(),
743                "read_fail",
744            )
745            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
746        let combined_fail = self
747            .builder
748            .build_or(read_fail, not_found, "combined_fail")
749            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
750
751        let cur_block = self.builder.get_insert_block().unwrap();
752        let func = cur_block.get_parent().unwrap();
753        let set_block = self.context.append_basic_block(func, "set_cond_err");
754        let cont_block = self.context.append_basic_block(func, "read_cont");
755        self.builder
756            .build_conditional_branch(combined_fail, set_block, cont_block)
757            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
758        self.builder.position_at_end(set_block);
759        let _ = self.set_condition_error_if_unset(2u8);
760        let _ = self.set_condition_error_addr_if_unset(addr);
761        self.builder
762            .build_unconditional_branch(cont_block)
763            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
764        self.builder.position_at_end(cont_block);
765
766        let result_type: BasicTypeEnum = match size {
767            MemoryAccessSize::U8 => self.context.i8_type().into(),
768            MemoryAccessSize::U16 => self.context.i16_type().into(),
769            MemoryAccessSize::U32 => self.context.i32_type().into(),
770            MemoryAccessSize::U64 => self.context.i64_type().into(),
771        };
772        let typed_ptr = self
773            .builder
774            .build_bit_cast(
775                global_buffer,
776                self.context.ptr_type(AddressSpace::default()),
777                "typed_ptr",
778            )
779            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
780        let loaded_value = self
781            .builder
782            .build_load(result_type, typed_ptr.into_pointer_value(), "loaded_value")
783            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
784        let loaded_i64 = if let BasicValueEnum::IntValue(iv) = loaded_value {
785            if iv.get_type().get_bit_width() < 64 {
786                self.builder
787                    .build_int_z_extend(iv, i64_type, "ext_i64")
788                    .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
789            } else {
790                iv
791            }
792        } else {
793            return Err(CodeGenError::MemoryAccessError(
794                "Expected integer value from memory read".to_string(),
795            ));
796        };
797
798        let zero_bv: BasicValueEnum = zero_const.into();
799        let val_bv: BasicValueEnum = loaded_i64.into();
800        let sel_bv = self
801            .builder
802            .build_select::<BasicValueEnum<'ctx>, _>(combined_fail, zero_bv, val_bv, "val_or_zero")
803            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
804
805        let i8_type = self.context.i8_type();
806        let combined_fail_i8 = self
807            .builder
808            .build_int_z_extend(combined_fail, i8_type, "combined_fail_i8")
809            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
810        let fail_ptr = self.get_or_create_flag_global("_gs_any_fail");
811        let cur_fail = self
812            .builder
813            .build_load(i8_type, fail_ptr, "cur_fail_cf")
814            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
815            .into_int_value();
816        let new_fail = self
817            .builder
818            .build_or(cur_fail, combined_fail_i8, "fail_or_miss_cf")
819            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
820        self.builder
821            .build_store(fail_ptr, new_fail)
822            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
823
824        Ok(sel_bv)
825    }
826    /// Map DWARF register number to pt_regs offset (simplified)
827    pub fn dwarf_reg_to_pt_regs_offset(&self, dwarf_reg: u16) -> Result<usize> {
828        // Use platform-specific register mapping to get byte offset
829        let byte_offset = register_mapping::dwarf_reg_to_pt_regs_byte_offset(dwarf_reg)
830            .ok_or_else(|| {
831                CodeGenError::RegisterMappingError(format!(
832                    "Unsupported DWARF register: {dwarf_reg}"
833                ))
834            })?;
835
836        // Convert byte offset to u64 array index for pt_regs access
837        let u64_index = byte_offset / core::mem::size_of::<u64>();
838        Ok(u64_index)
839    }
840
841    /// Create eBPF helper call using the correct calling convention
842    /// This creates an indirect call through the eBPF helper mechanism
843    pub fn create_bpf_helper_call(
844        &mut self,
845        helper_id: u64,
846        args: &[BasicValueEnum<'ctx>],
847        return_type: BasicTypeEnum<'ctx>,
848        call_name: &str,
849    ) -> Result<BasicValueEnum<'ctx>> {
850        use inkwell::types::BasicMetadataTypeEnum;
851
852        // Create function type for the helper
853        let arg_types: Vec<BasicMetadataTypeEnum> =
854            args.iter().map(|arg| arg.get_type().into()).collect();
855        let fn_type = return_type.fn_type(&arg_types, false);
856
857        // Convert helper ID to function pointer for indirect call
858        let i64_type = self.context.i64_type();
859        let ptr_type = self.context.ptr_type(AddressSpace::default());
860
861        let helper_id_val = i64_type.const_int(helper_id, false);
862        let helper_fn_ptr = self
863            .builder
864            .build_int_to_ptr(helper_id_val, ptr_type, "helper_fn")
865            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
866
867        // Convert args to metadata values
868        let metadata_args: Vec<BasicMetadataValueEnum> =
869            args.iter().map(|arg| (*arg).into()).collect();
870
871        // Make the indirect call
872        let call_result = self
873            .builder
874            .build_indirect_call(fn_type, helper_fn_ptr, &metadata_args, call_name)
875            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
876
877        // Convert CallSiteValue to BasicValueEnum
878        Ok(call_result.try_as_basic_value().left().unwrap_or_else(|| {
879            // If it's void, return a null value of the expected type
880            return_type.const_zero()
881        }))
882    }
883
884    /// Get current timestamp using bpf_ktime_get_ns
885    pub fn get_current_timestamp(&mut self) -> Result<IntValue<'ctx>> {
886        let i64_type = self.context.i64_type();
887
888        // Call bpf_ktime_get_ns() - takes no arguments
889        let timestamp = self.create_bpf_helper_call(
890            BPF_FUNC_ktime_get_ns as u64,
891            &[],
892            i64_type.into(),
893            "timestamp",
894        )?;
895
896        if let BasicValueEnum::IntValue(int_val) = timestamp {
897            Ok(int_val)
898        } else {
899            Err(CodeGenError::LLVMError(
900                "bpf_ktime_get_ns did not return integer".to_string(),
901            ))
902        }
903    }
904
905    /// Get current PID/TID using bpf_get_current_pid_tgid
906    pub fn get_current_pid_tgid(&mut self) -> Result<IntValue<'ctx>> {
907        let i64_type = self.context.i64_type();
908
909        // Call bpf_get_current_pid_tgid() - returns combined PID/TID
910        let pid_tgid = self.create_bpf_helper_call(
911            BPF_FUNC_get_current_pid_tgid as u64,
912            &[],
913            i64_type.into(),
914            "pid_tgid",
915        )?;
916
917        if let BasicValueEnum::IntValue(int_val) = pid_tgid {
918            Ok(int_val)
919        } else {
920            Err(CodeGenError::LLVMError(
921                "bpf_get_current_pid_tgid did not return integer".to_string(),
922            ))
923        }
924    }
925
926    /// Create event output using either RingBuf or PerfEventArray based on compile options
927    /// This is the unified interface that should be used for all event output
928    pub fn create_event_output(&mut self, data: PointerValue<'ctx>, size: u64) -> Result<()> {
929        match self.compile_options.event_map_type {
930            crate::EventMapType::RingBuf => self.create_ringbuf_output_internal(data, size),
931            crate::EventMapType::PerfEventArray => {
932                self.create_perf_event_output_internal(data, size)
933            }
934        }
935    }
936
937    /// Create ringbuf output using bpf_ringbuf_output (internal implementation)
938    fn create_ringbuf_output_internal(
939        &mut self,
940        data: PointerValue<'ctx>,
941        size: u64,
942    ) -> Result<()> {
943        let i64_type = self.context.i64_type();
944
945        // Get ringbuf map
946        let ringbuf_global = self
947            .map_manager
948            .get_ringbuf_map(&self.module, "ringbuf")
949            .map_err(|e| {
950                CodeGenError::MemoryAccessError(format!("Failed to get ringbuf map: {e}"))
951            })?;
952
953        // Arguments: map, data, size, flags
954        let args = [
955            ringbuf_global.into(),
956            data.into(),
957            i64_type.const_int(size, false).into(),
958            i64_type.const_zero().into(), // flags = 0
959        ];
960
961        let _result = self.create_bpf_helper_call(
962            BPF_FUNC_ringbuf_output as u64,
963            &args,
964            i64_type.into(),
965            "ringbuf_output",
966        )?;
967
968        Ok(())
969    }
970
971    /// Create ringbuf output with dynamic size (IntValue)
972    pub fn create_ringbuf_output_dynamic(
973        &mut self,
974        data: PointerValue<'ctx>,
975        size: IntValue<'ctx>,
976    ) -> Result<()> {
977        let i64_type = self.context.i64_type();
978
979        // Get ringbuf map
980        let ringbuf_global = self
981            .map_manager
982            .get_ringbuf_map(&self.module, "ringbuf")
983            .map_err(|e| {
984                CodeGenError::MemoryAccessError(format!("Failed to get ringbuf map: {e}"))
985            })?;
986
987        // Arguments: map, data, size (dynamic), flags
988        let args = [
989            ringbuf_global.into(),
990            data.into(),
991            size.into(),
992            i64_type.const_zero().into(), // flags = 0
993        ];
994
995        let _result = self.create_bpf_helper_call(
996            BPF_FUNC_ringbuf_output as u64,
997            &args,
998            i64_type.into(),
999            "ringbuf_output",
1000        )?;
1001
1002        Ok(())
1003    }
1004
1005    /// Lookup per-CPU map value pointer for a given map name and u32 key constant
1006    pub fn lookup_percpu_value_ptr(
1007        &mut self,
1008        map_name: &str,
1009        key_const: u32,
1010    ) -> Result<PointerValue<'ctx>> {
1011        let ptr_ty = self.context.ptr_type(AddressSpace::default());
1012        let i32_ty = self.context.i32_type();
1013        let map_global = self
1014            .map_manager
1015            .get_map(&self.module, map_name)
1016            .map_err(|e| CodeGenError::LLVMError(format!("Map not found {map_name}: {e}")))?;
1017        let map_ptr = self
1018            .builder
1019            .build_bit_cast(map_global, ptr_ty, "map_ptr")
1020            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1021
1022        // Prepare stack key in the entry-block alloca (reuse pm_key's first i32 slot)
1023        let key_arr_ty = i32_ty.array_type(4);
1024        let key_alloca = self.pm_key_alloca.ok_or_else(|| {
1025            CodeGenError::LLVMError("pm_key not allocated in entry block".to_string())
1026        })?;
1027        let zero = i32_ty.const_zero();
1028        let base_i32_ptr = unsafe {
1029            self.builder
1030                .build_gep(key_arr_ty, key_alloca, &[zero, zero], "percpu_key_i32_ptr")
1031                .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
1032        };
1033        self.builder
1034            .build_store(base_i32_ptr, i32_ty.const_int(key_const as u64, false))
1035            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1036        let key_ptr = self
1037            .builder
1038            .build_bit_cast(base_i32_ptr, ptr_ty, "key_ptr")
1039            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1040
1041        // long bpf_map_lookup_elem(void *map, const void *key) -> void *
1042        let ret = self.create_bpf_helper_call(
1043            BPF_FUNC_map_lookup_elem as u64,
1044            &[map_ptr, key_ptr],
1045            ptr_ty.into(),
1046            "map_lookup_elem",
1047        )?;
1048        let val_ptr = if let BasicValueEnum::PointerValue(p) = ret {
1049            p
1050        } else {
1051            return Err(CodeGenError::LLVMError(
1052                "map_lookup_elem did not return pointer".to_string(),
1053            ));
1054        };
1055        Ok(val_ptr)
1056    }
1057
1058    /// Create perf event output using bpf_perf_event_output (internal implementation)
1059    fn create_perf_event_output_internal(
1060        &mut self,
1061        data: PointerValue<'ctx>,
1062        size: u64,
1063    ) -> Result<()> {
1064        let size_val = self.context.i64_type().const_int(size, false);
1065        self.create_perf_event_output_dynamic(data, size_val)
1066    }
1067
1068    /// Create perf event output with dynamic size (IntValue)
1069    pub fn create_perf_event_output_dynamic(
1070        &mut self,
1071        data: PointerValue<'ctx>,
1072        size: IntValue<'ctx>,
1073    ) -> Result<()> {
1074        let i64_type = self.context.i64_type();
1075
1076        // Get the current pt_regs pointer (first argument to eBPF program)
1077        let ctx_param = self
1078            .builder
1079            .get_insert_block()
1080            .and_then(|bb| bb.get_parent())
1081            .and_then(|func| func.get_first_param())
1082            .ok_or_else(|| {
1083                CodeGenError::LLVMError("Failed to get context parameter".to_string())
1084            })?;
1085
1086        // Get perf event array map
1087        let events_global = self
1088            .map_manager
1089            .get_perf_map(&self.module, "events")
1090            .map_err(|e| {
1091                CodeGenError::MemoryAccessError(format!("Failed to get perf event map: {e}"))
1092            })?;
1093
1094        // Arguments: ctx, map, flags, data, size
1095        // flags = BPF_F_CURRENT_CPU (0xFFFFFFFF) means use current CPU
1096        let args = [
1097            ctx_param,
1098            events_global.into(),
1099            i64_type.const_int(0xFFFFFFFF_u64, false).into(), // BPF_F_CURRENT_CPU
1100            data.into(),
1101            size.into(),
1102        ];
1103
1104        let _result = self.create_bpf_helper_call(
1105            BPF_FUNC_perf_event_output as u64,
1106            &args,
1107            i64_type.into(),
1108            "perf_event_output",
1109        )?;
1110
1111        Ok(())
1112    }
1113}