Skip to main content

ghostscope_compiler/ebpf/
context.rs

1//! eBPF LLVM context and core infrastructure
2//!
3//! This module provides the main code generation context and basic LLVM
4//! infrastructure for eBPF program generation.
5
6use super::maps::MapManager;
7use crate::script::{VarType, VariableContext};
8use ghostscope_dwarf::DwarfAnalyzer;
9use inkwell::builder::Builder;
10use inkwell::context::Context;
11use inkwell::debug_info::DebugInfoBuilder;
12use inkwell::module::Module;
13use inkwell::targets::{Target, TargetTriple};
14use inkwell::values::{FunctionValue, IntValue, PointerValue};
15use inkwell::AddressSpace;
16use inkwell::OptimizationLevel;
17use std::collections::HashMap;
18use thiserror::Error;
19use tracing::info;
20
21/// Compile-time context containing PC address and module information for DWARF queries
22#[derive(Debug, Clone)]
23pub struct CompileTimeContext {
24    pub pc_address: u64,
25    pub module_path: String,
26}
27
28#[derive(Error, Debug)]
29pub enum CodeGenError {
30    #[error("LLVM compilation error: {0}")]
31    LLVMError(String),
32    #[error("Unsupported evaluation result: {0}")]
33    UnsupportedEvaluation(String),
34    #[error("Register mapping error: {0}")]
35    RegisterMappingError(String),
36    #[error("Memory access error: {0}")]
37    MemoryAccessError(String),
38    #[error("Builder error: {0}")]
39    Builder(String),
40
41    // === Legacy variable management errors ===
42    #[error("Variable not found: {0}")]
43    VariableNotFound(String),
44    #[error("Variable not in scope: {0}")]
45    VariableNotInScope(String),
46    #[error("Type error: {0}")]
47    TypeError(String),
48    #[error("Not implemented: {0}")]
49    NotImplemented(String),
50    #[error("DWARF expression error: {0}")]
51    DwarfError(String),
52    #[error("Type size not available for variable: {0}")]
53    TypeSizeNotAvailable(String),
54}
55
56pub type Result<T> = std::result::Result<T, CodeGenError>;
57
58/// eBPF LLVM code generation context
59pub struct EbpfContext<'ctx, 'dw> {
60    pub context: &'ctx Context,
61    pub module: Module<'ctx>,
62    pub builder: Builder<'ctx>,
63
64    // eBPF-specific function declarations
65    pub trace_printk_fn: FunctionValue<'ctx>,
66
67    // Map manager for eBPF maps
68    pub map_manager: MapManager<'ctx>,
69
70    // Debug infrastructure
71    pub di_builder: DebugInfoBuilder<'ctx>,
72    pub compile_unit: inkwell::debug_info::DICompileUnit<'ctx>,
73
74    // Register cache for pt_regs access
75    pub register_cache: HashMap<u16, IntValue<'ctx>>,
76
77    // === Complete Variable Management System ===
78    pub variables: HashMap<String, PointerValue<'ctx>>, // Variable name -> LLVM pointer
79    pub var_types: HashMap<String, VarType>,            // Variable name -> type
80    pub optimized_out_vars: HashMap<String, bool>,      // Optimized out variables
81    pub var_pc_addresses: HashMap<String, u64>,         // Variable -> PC address
82    pub variable_context: Option<VariableContext>,      // Scope validation context
83    pub(super) process_analyzer: Option<&'dw DwarfAnalyzer>, // Multi-module DWARF analyzer
84    pub current_trace_id: Option<u32>,                  // Current trace_id being compiled
85    pub current_compile_time_context: Option<CompileTimeContext>, // PC address and module for DWARF queries
86
87    // === New instruction-based compilation system ===
88    pub trace_context: ghostscope_protocol::TraceContext, // Trace context for optimized transmission
89    pub current_resolved_var_module_path: Option<String>,
90
91    // Per-invocation stack key for proc_module_offsets lookups (allocated in entry block)
92    // Backed by `[4 x i32]`, so consumers may only assume i32 alignment.
93    pub pm_key_alloca: Option<inkwell::values::PointerValue<'ctx>>,
94    // Per-invocation event accumulation offset (u32) stored on stack (entry block)
95    pub event_offset_alloca: Option<inkwell::values::PointerValue<'ctx>>,
96    // Compile-time upper bound for bytes that may already be reserved in the current trace event.
97    // This is maintained across structured control flow so later instructions can budget against
98    // the worst-case path without double-counting sibling branches.
99    pub compile_time_event_bytes_upper_bound: usize,
100    // Tracks whether the last proc_module_offsets lookup succeeded (used to skip reads)
101    pub offsets_found_flag: Option<inkwell::values::PointerValue<'ctx>>,
102
103    // Compilation options (includes eBPF map configuration)
104    pub compile_options: crate::CompileOptions,
105
106    // === Control-flow expression error capture (soft abort) ===
107    pub condition_context_active: bool,
108
109    // === DWARF alias variables (script-level symbolic references) ===
110    // These variables do not store pointer values; instead they remember the RHS
111    // expression and are resolved to runtime addresses at use sites.
112    pub alias_vars: HashMap<String, crate::script::Expr>,
113
114    // === Script string variables (store literal bytes for content printing) ===
115    // When a variable is bound from a string literal (or copied from another string var),
116    // we keep its bytes (including optional NUL) here for content printing via ImmediateBytes.
117    pub string_vars: HashMap<String, Vec<u8>>,
118
119    // === Lexical scoping for immutable variables ===
120    // Each scope frame records names declared in that scope.
121    pub scope_stack: Vec<std::collections::HashSet<String>>,
122}
123
124// Temporary alias for backward compatibility during refactoring
125pub type NewCodeGen<'ctx, 'dw> = EbpfContext<'ctx, 'dw>;
126
127impl<'ctx, 'dw> EbpfContext<'ctx, 'dw> {
128    /// Create a new eBPF code generation context
129    pub fn new(
130        context: &'ctx Context,
131        module_name: &str,
132        trace_id: Option<u32>,
133        compile_options: &crate::CompileOptions,
134    ) -> Result<Self> {
135        let module = context.create_module(module_name);
136        let builder = context.create_builder();
137
138        // Initialize standard BPF target
139        Target::initialize_bpf(&Default::default());
140
141        // Create BPF target triple
142        let triple = TargetTriple::create("bpf-pc-linux");
143
144        // Get target and create target machine
145        let target = Target::from_triple(&triple).map_err(|e| {
146            CodeGenError::LLVMError(format!("Failed to get target from triple: {e}"))
147        })?;
148        let target_machine = target
149            .create_target_machine(
150                &triple,
151                "generic",
152                "+alu32",
153                OptimizationLevel::Default,
154                inkwell::targets::RelocMode::PIC,
155                inkwell::targets::CodeModel::Small,
156            )
157            .ok_or_else(|| {
158                CodeGenError::LLVMError("Failed to create target machine".to_string())
159            })?;
160
161        // Set module data layout and triple
162        let data_layout = target_machine.get_target_data().get_data_layout();
163        module.set_data_layout(&data_layout);
164        module.set_triple(&triple);
165
166        // Initialize debug info
167        let (di_builder, compile_unit) = module.create_debug_info_builder(
168            true,                                         // allow_unresolved
169            inkwell::debug_info::DWARFSourceLanguage::C,  // language
170            "ghostscope_generated.c",                     // filename
171            ".",                                          // directory
172            "ghostscope-compiler",                        // producer
173            false,                                        // is_optimized
174            "",                                           // flags
175            1,                                            // runtime_version
176            "",                                           // split_name
177            inkwell::debug_info::DWARFEmissionKind::Full, // kind
178            0,                                            // dwo_id
179            false,                                        // split_debug_inlining
180            false,                                        // debug_info_for_profiling
181            "",                                           // sysroot
182            "",                                           // sdk
183        );
184
185        let map_manager = MapManager::new(context);
186
187        // Declare eBPF helper functions
188        let trace_printk_fn = Self::declare_trace_printk(context, &module);
189
190        Ok(Self {
191            context,
192            module,
193            builder,
194            trace_printk_fn,
195            map_manager,
196            di_builder,
197            compile_unit,
198            register_cache: HashMap::new(),
199
200            // Initialize variable management system
201            variables: HashMap::new(),
202            var_types: HashMap::new(),
203            optimized_out_vars: HashMap::new(),
204            var_pc_addresses: HashMap::new(),
205            variable_context: None,
206            process_analyzer: None,
207            current_trace_id: trace_id,
208            current_compile_time_context: None,
209
210            // Initialize new instruction-based compilation system
211            trace_context: ghostscope_protocol::TraceContext::new(),
212            current_resolved_var_module_path: None,
213            pm_key_alloca: None,
214            event_offset_alloca: None,
215            compile_time_event_bytes_upper_bound: 0,
216            offsets_found_flag: None,
217            compile_options: compile_options.clone(),
218
219            // Control-flow expression context
220            condition_context_active: false,
221
222            // Alias variables
223            alias_vars: HashMap::new(),
224            // String variables
225            string_vars: HashMap::new(),
226
227            // Scopes
228            scope_stack: Vec::new(),
229        })
230    }
231
232    /// Enter a new lexical scope
233    pub fn enter_scope(&mut self) {
234        self.scope_stack.push(std::collections::HashSet::new());
235    }
236
237    /// Exit current lexical scope and drop all names declared within
238    pub fn exit_scope(&mut self) {
239        if let Some(names) = self.scope_stack.pop() {
240            for name in names {
241                self.variables.remove(&name);
242                self.var_types.remove(&name);
243                self.alias_vars.remove(&name);
244                self.string_vars.remove(&name);
245                self.optimized_out_vars.remove(&name);
246                self.var_pc_addresses.remove(&name);
247            }
248        }
249    }
250
251    /// Check if a name exists in any active scope
252    pub fn is_name_in_any_scope(&self, name: &str) -> bool {
253        self.scope_stack.iter().any(|s| s.contains(name))
254    }
255
256    /// Check if a name exists in current (top) scope
257    pub fn is_name_in_current_scope(&self, name: &str) -> bool {
258        match self.scope_stack.last() {
259            Some(top) => top.contains(name),
260            None => false,
261        }
262    }
263
264    /// Declare a name in the current scope. Disallow same-scope redeclaration and shadowing.
265    pub fn declare_name_in_current_scope(&mut self, name: &str) -> Result<()> {
266        if self.scope_stack.is_empty() {
267            // Initialize a root scope if not present
268            self.enter_scope();
269        }
270        if self.is_name_in_current_scope(name) {
271            return Err(CodeGenError::TypeError(format!(
272                "Redeclaration in the same scope is not allowed: '{name}'"
273            )));
274        }
275        if self.is_name_in_any_scope(name) {
276            return Err(CodeGenError::TypeError(format!(
277                "Shadowing is not allowed for immutable variables: '{name}'"
278            )));
279        }
280        if let Some(top) = self.scope_stack.last_mut() {
281            top.insert(name.to_string());
282        }
283        Ok(())
284    }
285
286    /// Create a new code generator with DWARF analyzer support
287    pub fn new_with_process_analyzer(
288        context: &'ctx Context,
289        module_name: &str,
290        process_analyzer: Option<&'dw DwarfAnalyzer>,
291        trace_id: Option<u32>,
292        compile_options: &crate::CompileOptions,
293    ) -> Result<Self> {
294        let mut codegen = Self::new(context, module_name, trace_id, compile_options)?;
295        codegen.process_analyzer = process_analyzer;
296        Ok(codegen)
297    }
298
299    /// Set compile-time context for DWARF queries
300    pub fn set_compile_time_context(&mut self, pc_address: u64, module_path: String) {
301        self.current_compile_time_context = Some(CompileTimeContext {
302            pc_address,
303            module_path,
304        });
305    }
306
307    /// Get compile-time context for DWARF queries
308    pub fn get_compile_time_context(&self) -> Result<&CompileTimeContext> {
309        self.current_compile_time_context
310            .as_ref()
311            .ok_or_else(|| CodeGenError::DwarfError("No compile-time context set".to_string()))
312    }
313
314    /// Take and clear the current module hint for offsets (if any)
315    pub fn take_module_hint(&mut self) -> Option<String> {
316        self.current_resolved_var_module_path.take()
317    }
318
319    /// Declare trace_printk eBPF helper function
320    fn declare_trace_printk(context: &'ctx Context, module: &Module<'ctx>) -> FunctionValue<'ctx> {
321        let i32_type = context.i32_type();
322        let ptr_type = context.ptr_type(AddressSpace::default());
323        let i64_type = context.i64_type();
324
325        // int bpf_trace_printk(const char *fmt, u32 fmt_size, ...)
326        let fn_type = i32_type.fn_type(&[ptr_type.into(), i64_type.into()], true);
327
328        module.add_function("bpf_trace_printk", fn_type, None)
329    }
330
331    /// Create basic eBPF function with proper signature
332    pub fn create_basic_ebpf_function(&mut self, function_name: &str) -> Result<()> {
333        let i32_type = self.context.i32_type();
334        let ptr_type = self.context.ptr_type(AddressSpace::default());
335
336        // eBPF function signature: int function(struct pt_regs *ctx)
337        let fn_type = i32_type.fn_type(&[ptr_type.into()], false);
338
339        let function = self.module.add_function(function_name, fn_type, None);
340
341        // Set section attribute for uprobe
342        function.add_attribute(
343            inkwell::attributes::AttributeLoc::Function,
344            self.context.create_string_attribute("section", "uprobe"),
345        );
346
347        // Create basic block
348        let basic_block = self.context.append_basic_block(function, "entry");
349        self.builder.position_at_end(basic_block);
350
351        info!("Created eBPF function: {}", function_name);
352        Ok(())
353    }
354
355    /// Test helper: ensure proc_module_offsets map exists in the module
356    #[cfg(test)]
357    pub fn __test_ensure_proc_offsets_map(&mut self) -> Result<()> {
358        self.map_manager
359            .create_proc_module_offsets_map(
360                &self.module,
361                &self.di_builder,
362                &self.compile_unit,
363                "proc_module_offsets",
364                self.compile_options.proc_module_offsets_max_entries,
365            )
366            .map_err(|e| {
367                CodeGenError::LLVMError(format!(
368                    "Failed to create proc_module_offsets map in test: {e}"
369                ))
370            })?;
371        self.map_manager
372            .create_pid_aliases_map(
373                &self.module,
374                &self.di_builder,
375                &self.compile_unit,
376                "pid_aliases",
377                self.compile_options.proc_module_offsets_max_entries,
378            )
379            .map_err(|e| {
380                CodeGenError::LLVMError(format!("Failed to create pid_aliases map in test: {e}"))
381            })
382    }
383
384    /// Test helper: allocate per-invocation pm_key on the entry block like create_main_function
385    #[cfg(test)]
386    pub fn __test_alloc_pm_key(&mut self) -> Result<()> {
387        let i32_type = self.context.i32_type();
388        let key_arr_ty = i32_type.array_type(4);
389        let key_alloca = self
390            .builder
391            .build_alloca(key_arr_ty, "pm_key")
392            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
393        self.pm_key_alloca = Some(key_alloca);
394        Ok(())
395    }
396
397    /// Get the LLVM module reference
398    pub fn get_module(&self) -> &Module<'ctx> {
399        &self.module
400    }
401
402    /// Get the string table after compilation
403    pub fn get_trace_context(&self) -> ghostscope_protocol::TraceContext {
404        self.trace_context.clone()
405    }
406
407    /// Get pt_regs parameter from current function
408    pub fn get_pt_regs_parameter(&self) -> Result<PointerValue<'ctx>> {
409        let current_function = self
410            .builder
411            .get_insert_block()
412            .ok_or_else(|| CodeGenError::Builder("No current basic block".to_string()))?
413            .get_parent()
414            .ok_or_else(|| CodeGenError::Builder("No parent function".to_string()))?;
415
416        let pt_regs_param = current_function
417            .get_first_param()
418            .ok_or_else(|| CodeGenError::Builder("Function has no parameters".to_string()))?
419            .into_pointer_value();
420
421        Ok(pt_regs_param)
422    }
423
424    /// Compile a complete program with statements
425    pub fn compile_program(
426        &mut self,
427        _program: &crate::script::Program,
428        function_name: &str,
429        trace_statements: &[crate::script::Statement],
430        target_pid: Option<u32>,
431        compile_time_pc: Option<u64>,
432        module_path: Option<&str>,
433    ) -> Result<(FunctionValue<'ctx>, ghostscope_protocol::TraceContext)> {
434        info!(
435            "Starting program compilation with function: {}",
436            function_name
437        );
438
439        // Set the current trace_id and compile-time context for code generation
440        self.current_compile_time_context =
441            if let (Some(pc), Some(path)) = (compile_time_pc, module_path) {
442                Some(CompileTimeContext {
443                    pc_address: pc,
444                    module_path: path.to_string(),
445                })
446            } else {
447                None
448            };
449
450        // Create required maps - critical for eBPF loader
451        // Create event output map based on compile options
452        match self.compile_options.event_map_type {
453            crate::EventMapType::RingBuf => {
454                self.map_manager
455                    .create_ringbuf_map(
456                        &self.module,
457                        &self.di_builder,
458                        &self.compile_unit,
459                        "ringbuf",
460                        self.compile_options.ringbuf_size,
461                    )
462                    .map_err(|e| {
463                        CodeGenError::LLVMError(format!("Failed to create ringbuf map: {e}"))
464                    })?;
465            }
466            crate::EventMapType::PerfEventArray => {
467                self.map_manager
468                    .create_perf_event_array_map(
469                        &self.module,
470                        &self.di_builder,
471                        &self.compile_unit,
472                        "events",
473                    )
474                    .map_err(|e| {
475                        CodeGenError::LLVMError(format!(
476                            "Failed to create perf event array map: {e}"
477                        ))
478                    })?;
479            }
480        }
481
482        // Create per-CPU accumulation maps for single-record event emission
483        //  - event_accum_buffer: value size = max_trace_event_size bytes, entries = 1
484        //  - event_accum_offset: value size = 4 bytes (u32), entries = 1
485        self.map_manager
486            .create_percpu_array_map(
487                &self.module,
488                &self.di_builder,
489                &self.compile_unit,
490                "event_accum_buffer",
491                1,
492                self.compile_options.max_trace_event_size as u64,
493            )
494            .map_err(|e| {
495                CodeGenError::LLVMError(format!("Failed to create event_accum_buffer: {e}"))
496            })?;
497
498        // Create ASLR offsets map for (pid,module) → section offsets
499        self.map_manager
500            .create_proc_module_offsets_map(
501                &self.module,
502                &self.di_builder,
503                &self.compile_unit,
504                "proc_module_offsets",
505                self.compile_options.proc_module_offsets_max_entries,
506            )
507            .map_err(|e| {
508                CodeGenError::LLVMError(format!("Failed to create proc_module_offsets map: {e}"))
509            })?;
510
511        self.map_manager
512            .create_pid_aliases_map(
513                &self.module,
514                &self.di_builder,
515                &self.compile_unit,
516                "pid_aliases",
517                self.compile_options.proc_module_offsets_max_entries,
518            )
519            .map_err(|e| {
520                CodeGenError::LLVMError(format!("Failed to create pid_aliases map: {e}"))
521            })?;
522
523        // Variables are now queried on-demand when accessed in expressions
524        // No need to pre-populate DWARF variables
525
526        // Create main function
527        let main_function = self.create_main_function(function_name)?;
528
529        // Add PID filtering:
530        // 1) explicit compile option override (namespace-aware)
531        // 2) fallback to legacy host TGID filter from target_pid
532        let pid_filter_spec = self
533            .compile_options
534            .pid_filter_spec
535            .or_else(|| target_pid.map(|pid| crate::PidFilterSpec::HostTgid { filter_pid: pid }));
536        if let Some(spec) = pid_filter_spec {
537            self.add_pid_filter(spec)?;
538        }
539
540        // Use new staged transmission system for all statements
541        let program = crate::script::ast::Program {
542            statements: trace_statements.to_vec(),
543        };
544
545        // Collect variable types from DWARF analysis
546        let variable_types = std::collections::HashMap::new(); // Empty for now, will be populated by codegen
547
548        // Generate staged transmission code using new architecture
549        let trace_context =
550            self.compile_program_with_staged_transmission(&program, variable_types)?;
551        info!(
552            "Generated TraceContext with {} strings",
553            trace_context.string_count()
554        );
555
556        // Return success
557        let i32_type = self.context.i32_type();
558        let return_value = i32_type.const_int(0, false);
559        self.builder
560            .build_return(Some(&return_value))
561            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
562
563        info!(
564            "Successfully compiled program with function: {} and TraceContext",
565            function_name
566        );
567        Ok((main_function, trace_context))
568    }
569
570    /// Create the main eBPF function
571    fn create_main_function(&mut self, function_name: &str) -> Result<FunctionValue<'ctx>> {
572        let i32_type = self.context.i32_type();
573        let ptr_type = self.context.ptr_type(AddressSpace::default());
574
575        // Create function type: int function_name(void *ctx)
576        let fn_type = i32_type.fn_type(&[ptr_type.into()], false);
577        let function = self.module.add_function(function_name, fn_type, None);
578
579        // CRITICAL: Set section name for eBPF loader to find the function
580        function.set_section(Some("uprobe"));
581
582        // Create basic block and position builder
583        let basic_block = self.context.append_basic_block(function, "entry");
584        self.builder.position_at_end(basic_block);
585
586        // Allocate fixed-size per-invocation key buffer on the eBPF stack (entry block)
587        // Layout: [ pid:u32, pad:u32, cookie_lo:u32, cookie_hi:u32 ] to match struct {u32; u64}
588        // This remains an i32-aligned slot; probe-read scratch reuse must stay
589        // limited to <=4-byte scalar loads.
590        let key_arr_ty = i32_type.array_type(4);
591        let key_alloca = self
592            .builder
593            .build_alloca(key_arr_ty, "pm_key")
594            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
595        self.pm_key_alloca = Some(key_alloca);
596
597        // Allocate per-invocation event_offset (u32) and initialize to 0
598        let event_off_alloca = self
599            .builder
600            .build_alloca(i32_type, "event_offset")
601            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
602        self.builder
603            .build_store(event_off_alloca, i32_type.const_zero())
604            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
605        self.event_offset_alloca = Some(event_off_alloca);
606
607        info!("Created main function: {}", function_name);
608        Ok(function)
609    }
610
611    /// Add PID filtering logic to the current function.
612    /// This generates LLVM IR to check PID and early-return if not matching.
613    fn add_pid_filter(&mut self, spec: crate::PidFilterSpec) -> Result<()> {
614        match spec {
615            crate::PidFilterSpec::HostTgid { filter_pid } => self.add_host_pid_filter(filter_pid),
616            crate::PidFilterSpec::NamespaceTgid { filter_pid, pid_ns } => {
617                let (pid_ns_dev, pid_ns_inode) = pid_ns.helper_dev_inode().ok_or_else(|| {
618                    CodeGenError::LLVMError(
619                        "Namespace TGID filter requires pid namespace device id".to_string(),
620                    )
621                })?;
622                self.add_namespace_pid_filter(filter_pid, pid_ns_dev, pid_ns_inode)
623            }
624        }
625    }
626
627    fn add_host_pid_filter(&mut self, filter_pid: u32) -> Result<()> {
628        info!("Adding host TGID filter for filter PID: {}", filter_pid);
629
630        // Get current function and entry block
631        let current_fn = self
632            .builder
633            .get_insert_block()
634            .unwrap()
635            .get_parent()
636            .unwrap();
637
638        // Create basic blocks for control flow
639        let continue_block = self
640            .context
641            .append_basic_block(current_fn, "continue_execution");
642        let early_return_block = self
643            .context
644            .append_basic_block(current_fn, "pid_mismatch_return");
645
646        // Get current PID/TID using bpf_get_current_pid_tgid helper
647        let pid_tgid_value = self.get_current_pid_tgid()?;
648
649        // Extract TGID (high 32 bits) by right shifting 32 bits
650        let shift_amount = self.context.i64_type().const_int(32, false);
651        let current_tgid = self
652            .builder
653            .build_right_shift(pid_tgid_value, shift_amount, false, "current_tgid")
654            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
655
656        // Convert filter_pid to i64 and compare
657        let target_pid_value = self.context.i64_type().const_int(filter_pid as u64, false);
658        let pid_matches = self
659            .builder
660            .build_int_compare(
661                inkwell::IntPredicate::EQ,
662                current_tgid,
663                target_pid_value,
664                "pid_matches",
665            )
666            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
667
668        // Conditional branch: if pid matches, continue; else early return
669        self.builder
670            .build_conditional_branch(pid_matches, continue_block, early_return_block)
671            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
672
673        // Early return block - just return 0
674        self.builder.position_at_end(early_return_block);
675        self.builder
676            .build_return(Some(&self.context.i32_type().const_int(0, false)))
677            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
678
679        // Position at continue block for the rest of the function
680        self.builder.position_at_end(continue_block);
681
682        info!(
683            "Host TGID filter added successfully for filter PID: {}",
684            filter_pid
685        );
686        Ok(())
687    }
688
689    fn add_namespace_pid_filter(
690        &mut self,
691        filter_pid: u32,
692        pid_ns_dev: u64,
693        pid_ns_inode: u64,
694    ) -> Result<()> {
695        const BPF_FUNC_GET_NS_CURRENT_PID_TGID: u64 = 120;
696        const BPF_PIDNS_INFO_SIZE: u64 = 8; // struct { u32 pid; u32 tgid; }
697
698        info!(
699            "Adding namespace TGID filter: filter_pid={} ns_dev={} ns_inode={}",
700            filter_pid, pid_ns_dev, pid_ns_inode
701        );
702
703        let current_fn = self
704            .builder
705            .get_insert_block()
706            .ok_or_else(|| CodeGenError::Builder("No current insert block".to_string()))?
707            .get_parent()
708            .ok_or_else(|| CodeGenError::Builder("No parent function".to_string()))?;
709
710        let helper_ok_block = self
711            .context
712            .append_basic_block(current_fn, "pidns_helper_ok");
713        let continue_block = self
714            .context
715            .append_basic_block(current_fn, "continue_execution");
716        let early_return_block = self
717            .context
718            .append_basic_block(current_fn, "pid_mismatch_return");
719
720        let i32_type = self.context.i32_type();
721        let i64_type = self.context.i64_type();
722        let ptr_type = self.context.ptr_type(AddressSpace::default());
723
724        // Stack-allocate bpf_pidns_info-compatible storage: [pid:u32, tgid:u32].
725        let pidns_info_ty = i32_type.array_type(2);
726        let pidns_info_alloca = self
727            .builder
728            .build_alloca(pidns_info_ty, "pidns_info")
729            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
730        self.builder
731            .build_store(pidns_info_alloca, pidns_info_ty.const_zero())
732            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
733
734        let pidns_info_ptr = self
735            .builder
736            .build_bit_cast(pidns_info_alloca, ptr_type, "pidns_info_ptr")
737            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
738
739        let helper_args = [
740            i64_type.const_int(pid_ns_dev, false).into(),
741            i64_type.const_int(pid_ns_inode, false).into(),
742            pidns_info_ptr,
743            i64_type.const_int(BPF_PIDNS_INFO_SIZE, false).into(),
744        ];
745        let helper_ret = self.create_bpf_helper_call(
746            BPF_FUNC_GET_NS_CURRENT_PID_TGID,
747            &helper_args,
748            i64_type.into(),
749            "ns_pid_tgid_ret",
750        )?;
751        let helper_ret = match helper_ret {
752            inkwell::values::BasicValueEnum::IntValue(v) => v,
753            _ => {
754                return Err(CodeGenError::LLVMError(
755                    "bpf_get_ns_current_pid_tgid did not return integer".to_string(),
756                ));
757            }
758        };
759
760        let helper_ok = self
761            .builder
762            .build_int_compare(
763                inkwell::IntPredicate::EQ,
764                helper_ret,
765                i64_type.const_zero(),
766                "pidns_helper_ok",
767            )
768            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
769        self.builder
770            .build_conditional_branch(helper_ok, helper_ok_block, early_return_block)
771            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
772
773        self.builder.position_at_end(helper_ok_block);
774        let tgid_ptr = unsafe {
775            self.builder.build_gep(
776                pidns_info_ty,
777                pidns_info_alloca,
778                &[i32_type.const_zero(), i32_type.const_int(1, false)],
779                "pidns_tgid_ptr",
780            )
781        }
782        .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
783        let ns_tgid = self
784            .builder
785            .build_load(i32_type, tgid_ptr, "ns_tgid")
786            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
787            .into_int_value();
788        let ns_tgid_i64 = self
789            .builder
790            .build_int_z_extend(ns_tgid, i64_type, "ns_tgid_i64")
791            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
792        let target_pid_value = i64_type.const_int(filter_pid as u64, false);
793        let pid_matches = self
794            .builder
795            .build_int_compare(
796                inkwell::IntPredicate::EQ,
797                ns_tgid_i64,
798                target_pid_value,
799                "pid_matches_ns",
800            )
801            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
802        self.builder
803            .build_conditional_branch(pid_matches, continue_block, early_return_block)
804            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
805
806        self.builder.position_at_end(early_return_block);
807        self.builder
808            .build_return(Some(&self.context.i32_type().const_int(0, false)))
809            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
810
811        self.builder.position_at_end(continue_block);
812        info!(
813            "Namespace TGID filter added successfully for filter PID: {}",
814            filter_pid
815        );
816        Ok(())
817    }
818
819    /// Get or create a global i8 flag by name, initialized to 0
820    pub fn get_or_create_flag_global(&mut self, name: &str) -> PointerValue<'ctx> {
821        if let Some(g) = self.module.get_global(name) {
822            return g.as_pointer_value();
823        }
824        let i8_type = self.context.i8_type();
825        let global = self
826            .module
827            .add_global(i8_type, Some(AddressSpace::default()), name);
828        global.set_initializer(&i8_type.const_zero());
829        global.as_pointer_value()
830    }
831
832    /// Set a flag global to a constant u8 value at runtime
833    pub fn store_flag_value(&mut self, name: &str, value: u8) -> Result<()> {
834        let ptr = self.get_or_create_flag_global(name);
835        self.builder
836            .build_store(ptr, self.context.i8_type().const_int(value as u64, false))
837            .map_err(|e| CodeGenError::LLVMError(format!("Failed to store flag {name}: {e}")))?;
838        Ok(())
839    }
840
841    /// Mark that at least one variable succeeded (status==0)
842    pub fn mark_any_success(&mut self) -> Result<()> {
843        self.store_flag_value("_gs_any_success", 1)
844    }
845
846    /// Mark that at least one variable failed (status!=0)
847    pub fn mark_any_fail(&mut self) -> Result<()> {
848        self.store_flag_value("_gs_any_fail", 1)
849    }
850
851    /// Get (and create if needed) the global flag tracking the last proc_module_offsets lookup.
852    /// Stored as i8 where 0 = miss, 1 = found.
853    pub fn get_or_create_offsets_found_flag(&mut self) -> inkwell::values::PointerValue<'ctx> {
854        if let Some(ptr) = self.offsets_found_flag {
855            return ptr;
856        }
857        let i8_type = self.context.i8_type();
858        // TODO: Replace this global flag with explicit control-flow checks when memory-read emission is refactored.
859        let global =
860            self.module
861                .add_global(i8_type, Some(AddressSpace::default()), "_gs_offsets_found");
862        global.set_initializer(&i8_type.const_int(1, false));
863        let ptr = global.as_pointer_value();
864        self.offsets_found_flag = Some(ptr);
865        ptr
866    }
867
868    /// Store a boolean into the offsets-found flag (true => found, false => miss).
869    pub fn store_offsets_found_flag(
870        &mut self,
871        flag: inkwell::values::IntValue<'ctx>,
872    ) -> Result<()> {
873        let ptr = self.get_or_create_offsets_found_flag();
874        let i8_type = self.context.i8_type();
875        let flag_i8 = self
876            .builder
877            .build_int_z_extend(flag, i8_type, "offset_flag_i8")
878            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
879        self.builder
880            .build_store(ptr, flag_i8)
881            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
882        Ok(())
883    }
884
885    /// Store a constant boolean value into the offsets-found flag.
886    pub fn store_offsets_found_const(&mut self, value: bool) -> Result<()> {
887        let ptr = self.get_or_create_offsets_found_flag();
888        let i8_type = self.context.i8_type();
889        self.builder
890            .build_store(ptr, i8_type.const_int(value as u64, false))
891            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
892        Ok(())
893    }
894
895    /// Load the current offsets-found flag as i1 (true => found, false => miss).
896    pub fn load_offsets_found_flag(&mut self) -> Result<inkwell::values::IntValue<'ctx>> {
897        let ptr = self.get_or_create_offsets_found_flag();
898        let i8_type = self.context.i8_type();
899        let raw = self
900            .builder
901            .build_load(i8_type, ptr, "offsets_found_raw")
902            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
903            .into_int_value();
904        let is_non_zero = self
905            .builder
906            .build_int_compare(
907                inkwell::IntPredicate::NE,
908                raw,
909                i8_type.const_zero(),
910                "offsets_found_bool",
911            )
912            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
913        Ok(is_non_zero)
914    }
915
916    /// Get or create global for condition error code (i8). Name: _gs_cond_error
917    pub fn get_or_create_cond_error_global(&mut self) -> PointerValue<'ctx> {
918        if let Some(g) = self.module.get_global("_gs_cond_error") {
919            return g.as_pointer_value();
920        }
921        let i8_type = self.context.i8_type();
922        let global =
923            self.module
924                .add_global(i8_type, Some(AddressSpace::default()), "_gs_cond_error");
925        global.set_initializer(&i8_type.const_zero());
926        global.as_pointer_value()
927    }
928
929    /// Reset condition error to 0 (only meaningful when condition_context_active=true)
930    pub fn reset_condition_error(&mut self) -> Result<()> {
931        let ptr = self.get_or_create_cond_error_global();
932        self.builder
933            .build_store(ptr, self.context.i8_type().const_zero())
934            .map_err(|e| CodeGenError::LLVMError(format!("Failed to reset _gs_cond_error: {e}")))?;
935        // Also reset error address
936        let aptr = self.get_or_create_cond_error_addr_global();
937        self.builder
938            .build_store(aptr, self.context.i64_type().const_zero())
939            .map_err(|e| {
940                CodeGenError::LLVMError(format!("Failed to reset _gs_cond_error_addr: {e}"))
941            })?;
942        // Also reset flags
943        let fptr = self.get_or_create_cond_error_flags_global();
944        self.builder
945            .build_store(fptr, self.context.i8_type().const_zero())
946            .map_err(|e| {
947                CodeGenError::LLVMError(format!("Failed to reset _gs_cond_error_flags: {e}"))
948            })?;
949        Ok(())
950    }
951
952    /// Get or create global for condition error address (i64). Name: _gs_cond_error_addr
953    pub fn get_or_create_cond_error_addr_global(&mut self) -> PointerValue<'ctx> {
954        if let Some(g) = self.module.get_global("_gs_cond_error_addr") {
955            return g.as_pointer_value();
956        }
957        let i64_type = self.context.i64_type();
958        let global = self.module.add_global(
959            i64_type,
960            Some(AddressSpace::default()),
961            "_gs_cond_error_addr",
962        );
963        global.set_initializer(&i64_type.const_zero());
964        global.as_pointer_value()
965    }
966
967    /// If in condition context, set error code when it's currently 0 (first error wins)
968    pub fn set_condition_error_if_unset(&mut self, code: u8) -> Result<()> {
969        if !self.condition_context_active {
970            return Ok(());
971        }
972        let ptr = self.get_or_create_cond_error_global();
973        let cur = self
974            .builder
975            .build_load(self.context.i8_type(), ptr, "cond_err_cur")
976            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
977            .into_int_value();
978        let is_zero = self
979            .builder
980            .build_int_compare(
981                inkwell::IntPredicate::EQ,
982                cur,
983                self.context.i8_type().const_zero(),
984                "cond_err_is_zero",
985            )
986            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
987        let newv_bv: inkwell::values::BasicValueEnum =
988            self.context.i8_type().const_int(code as u64, false).into();
989        let sel = self
990            .builder
991            .build_select::<inkwell::values::BasicValueEnum, _>(
992                is_zero,
993                newv_bv,
994                cur.into(),
995                "cond_err_new",
996            )
997            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
998        self.builder
999            .build_store(ptr, sel)
1000            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1001        Ok(())
1002    }
1003
1004    /// Read the current condition error as i1 predicate: (error != 0)
1005    pub fn build_condition_error_predicate(&mut self) -> Result<inkwell::values::IntValue<'ctx>> {
1006        let ptr = self.get_or_create_cond_error_global();
1007        let cur = self
1008            .builder
1009            .build_load(self.context.i8_type(), ptr, "cond_err_cur")
1010            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
1011            .into_int_value();
1012        self.builder
1013            .build_int_compare(
1014                inkwell::IntPredicate::NE,
1015                cur,
1016                self.context.i8_type().const_zero(),
1017                "cond_err_nonzero",
1018            )
1019            .map_err(|e| CodeGenError::LLVMError(e.to_string()))
1020    }
1021
1022    /// Get or create global for condition error flags (i8). Name: _gs_cond_error_flags
1023    pub fn get_or_create_cond_error_flags_global(&mut self) -> PointerValue<'ctx> {
1024        if let Some(g) = self.module.get_global("_gs_cond_error_flags") {
1025            return g.as_pointer_value();
1026        }
1027        let i8_type = self.context.i8_type();
1028        let global = self.module.add_global(
1029            i8_type,
1030            Some(AddressSpace::default()),
1031            "_gs_cond_error_flags",
1032        );
1033        global.set_initializer(&i8_type.const_zero());
1034        global.as_pointer_value()
1035    }
1036
1037    /// OR into condition error flags (BV must be i8)
1038    pub fn or_condition_error_flags(&mut self, flags: IntValue<'ctx>) -> Result<()> {
1039        if !self.condition_context_active {
1040            return Ok(());
1041        }
1042        let ptr = self.get_or_create_cond_error_flags_global();
1043        let cur = self
1044            .builder
1045            .build_load(self.context.i8_type(), ptr, "cond_err_flags_cur")
1046            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
1047            .into_int_value();
1048        let newv = self
1049            .builder
1050            .build_or(cur, flags, "cond_err_flags_or")
1051            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1052        self.builder
1053            .build_store(ptr, newv)
1054            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1055        Ok(())
1056    }
1057
1058    /// If in condition context, record failing address (first win). addr must be i64
1059    pub fn set_condition_error_addr_if_unset(&mut self, addr: IntValue<'ctx>) -> Result<()> {
1060        if !self.condition_context_active {
1061            return Ok(());
1062        }
1063        let ptr = self.get_or_create_cond_error_addr_global();
1064        let cur = self
1065            .builder
1066            .build_load(self.context.i64_type(), ptr, "cond_err_addr_cur")
1067            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
1068            .into_int_value();
1069        let is_zero = self
1070            .builder
1071            .build_int_compare(
1072                inkwell::IntPredicate::EQ,
1073                cur,
1074                self.context.i64_type().const_zero(),
1075                "cond_err_addr_is_zero",
1076            )
1077            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1078        let sel = self
1079            .builder
1080            .build_select::<IntValue<'ctx>, _>(is_zero, addr, cur, "cond_err_addr_new")
1081            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1082        self.builder
1083            .build_store(ptr, sel)
1084            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1085        Ok(())
1086    }
1087}