1use super::maps::MapManager;
7use crate::script::{VarType, VariableContext};
8use ghostscope_dwarf::DwarfAnalyzer;
9use inkwell::basic_block::BasicBlock;
10use inkwell::builder::Builder;
11use inkwell::context::Context;
12use inkwell::debug_info::DebugInfoBuilder;
13use inkwell::module::Module;
14use inkwell::targets::{Target, TargetTriple};
15use inkwell::values::{FunctionValue, IntValue, PointerValue};
16use inkwell::AddressSpace;
17use inkwell::OptimizationLevel;
18use std::collections::HashMap;
19use thiserror::Error;
20use tracing::info;
21
22#[derive(Debug, Clone)]
24pub struct CompileTimeContext {
25 pub pc_address: u64,
26 pub module_path: String,
27}
28
29#[derive(Debug, Clone)]
30pub struct BacktraceTailCallProgram {
31 pub step_program_name: String,
32}
33
34#[derive(Debug, Clone)]
35pub(crate) struct PendingBacktraceTailCall {
36 pub step_program_name: String,
37 pub depth: u8,
38 pub instruction_size: usize,
39}
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq)]
42pub(crate) struct BacktraceModuleRowRangeEntry {
43 pub cookie: u64,
44 pub range: ghostscope_protocol::BacktraceModuleRowRange,
45}
46
47#[derive(Error, Debug)]
48pub enum CodeGenError {
49 #[error("LLVM compilation error: {0}")]
50 LLVMError(String),
51 #[error("Unsupported evaluation result: {0}")]
52 UnsupportedEvaluation(String),
53 #[error("Register mapping error: {0}")]
54 RegisterMappingError(String),
55 #[error("Memory access error: {0}")]
56 MemoryAccessError(String),
57 #[error("Builder error: {0}")]
58 Builder(String),
59
60 #[error("Variable not found: {0}")]
62 VariableNotFound(String),
63 #[error("Variable not in scope: {0}")]
64 VariableNotInScope(String),
65 #[error("Variable unavailable: {0}")]
66 VariableUnavailable(String),
67 #[error("Type error: {0}")]
68 TypeError(String),
69 #[error("Not implemented: {0}")]
70 NotImplemented(String),
71 #[error("DWARF expression error: {0}")]
72 DwarfError(String),
73 #[error("Type size not available for variable: {0}")]
74 TypeSizeNotAvailable(String),
75}
76
77pub type Result<T> = std::result::Result<T, CodeGenError>;
78
79#[derive(Debug, Clone, Copy)]
86pub(crate) struct RuntimeAddress<'ctx> {
87 pub value: IntValue<'ctx>,
88 pub offsets_found: IntValue<'ctx>,
89}
90
91impl<'ctx> RuntimeAddress<'ctx> {
92 pub(crate) fn available(value: IntValue<'ctx>, context: &'ctx Context) -> Self {
93 Self {
94 value,
95 offsets_found: context.bool_type().const_int(1, false),
96 }
97 }
98
99 pub(crate) fn with_offsets_found(value: IntValue<'ctx>, offsets_found: IntValue<'ctx>) -> Self {
100 Self {
101 value,
102 offsets_found,
103 }
104 }
105
106 pub(crate) fn with_value(self, value: IntValue<'ctx>) -> Self {
107 Self { value, ..self }
108 }
109}
110
111pub struct EbpfContext<'ctx, 'dw> {
113 pub context: &'ctx Context,
114 pub module: Module<'ctx>,
115 pub builder: Builder<'ctx>,
116
117 pub trace_printk_fn: FunctionValue<'ctx>,
119
120 pub map_manager: MapManager<'ctx>,
122
123 pub di_builder: DebugInfoBuilder<'ctx>,
125 pub compile_unit: inkwell::debug_info::DICompileUnit<'ctx>,
126
127 pub variables: HashMap<String, PointerValue<'ctx>>, pub var_types: HashMap<String, VarType>, pub optimized_out_vars: HashMap<String, bool>, pub var_pc_addresses: HashMap<String, u64>, pub variable_context: Option<VariableContext>, pub(super) process_analyzer: Option<&'dw DwarfAnalyzer>, pub current_trace_id: Option<u32>, pub current_compile_time_context: Option<CompileTimeContext>, pub trace_context: ghostscope_protocol::TraceContext, pub pm_key_alloca: Option<inkwell::values::PointerValue<'ctx>>,
143 pub(super) tls_scratch_alloca: Option<inkwell::values::PointerValue<'ctx>>,
146 pub event_offset_alloca: Option<inkwell::values::PointerValue<'ctx>>,
148 pub compile_time_event_bytes_upper_bound: usize,
152 pub compile_options: crate::CompileOptions,
154
155 pub condition_context_active: bool,
157
158 pub alias_vars: HashMap<String, crate::script::Expr>,
162
163 pub string_vars: HashMap<String, Vec<u8>>,
167
168 pub backtrace_unwind_rows: Vec<ghostscope_protocol::BacktraceUnwindRow>,
170 pub(crate) backtrace_module_row_ranges: Vec<BacktraceModuleRowRangeEntry>,
171 pub(crate) backtrace_tail_call_slots: u8,
172 pub(crate) next_backtrace_tail_call_slot: u8,
173 pub(crate) pending_backtrace_tail_call: Option<PendingBacktraceTailCall>,
174 pub(crate) backtrace_tail_enabled_alloca: Option<inkwell::values::PointerValue<'ctx>>,
175 pub(crate) backtrace_tail_last_slot_alloca: Option<inkwell::values::PointerValue<'ctx>>,
176
177 pub scope_stack: Vec<std::collections::HashSet<String>>,
180}
181
182impl<'ctx, 'dw> EbpfContext<'ctx, 'dw> {
183 pub(crate) fn backtrace_unwind_row_map_entries(&self) -> u64 {
184 (self.compile_options.backtrace_unwind_rows_max_entries as u64)
185 .max(self.backtrace_unwind_rows.len() as u64)
186 .max(1)
187 }
188
189 pub fn new(
191 context: &'ctx Context,
192 module_name: &str,
193 trace_id: Option<u32>,
194 compile_options: &crate::CompileOptions,
195 ) -> Result<Self> {
196 let module = context.create_module(module_name);
197 let builder = context.create_builder();
198
199 Target::initialize_bpf(&Default::default());
201
202 let triple = TargetTriple::create("bpf-pc-linux");
204
205 let target = Target::from_triple(&triple).map_err(|e| {
207 CodeGenError::LLVMError(format!("Failed to get target from triple: {e}"))
208 })?;
209 let target_machine = target
210 .create_target_machine(
211 &triple,
212 "generic",
213 "+alu32",
214 OptimizationLevel::Default,
215 inkwell::targets::RelocMode::PIC,
216 inkwell::targets::CodeModel::Small,
217 )
218 .ok_or_else(|| {
219 CodeGenError::LLVMError("Failed to create target machine".to_string())
220 })?;
221
222 let data_layout = target_machine.get_target_data().get_data_layout();
224 module.set_data_layout(&data_layout);
225 module.set_triple(&triple);
226
227 let (di_builder, compile_unit) = module.create_debug_info_builder(
229 true, inkwell::debug_info::DWARFSourceLanguage::C, "ghostscope_generated.c", ".", "ghostscope-compiler", false, "", 1, "", inkwell::debug_info::DWARFEmissionKind::Full, 0, false, false, "", "", );
245
246 let map_manager = MapManager::new(context);
247
248 let trace_printk_fn = Self::declare_trace_printk(context, &module);
250
251 Ok(Self {
252 context,
253 module,
254 builder,
255 trace_printk_fn,
256 map_manager,
257 di_builder,
258 compile_unit,
259
260 variables: HashMap::new(),
262 var_types: HashMap::new(),
263 optimized_out_vars: HashMap::new(),
264 var_pc_addresses: HashMap::new(),
265 variable_context: None,
266 process_analyzer: None,
267 current_trace_id: trace_id,
268 current_compile_time_context: None,
269
270 trace_context: ghostscope_protocol::TraceContext::new(),
272 pm_key_alloca: None,
273 tls_scratch_alloca: None,
274 event_offset_alloca: None,
275 compile_time_event_bytes_upper_bound: 0,
276 compile_options: compile_options.clone(),
277
278 condition_context_active: false,
280
281 alias_vars: HashMap::new(),
283 string_vars: HashMap::new(),
285 backtrace_unwind_rows: Vec::new(),
287 backtrace_module_row_ranges: Vec::new(),
288 backtrace_tail_call_slots: 1,
289 next_backtrace_tail_call_slot: 0,
290 pending_backtrace_tail_call: None,
291 backtrace_tail_enabled_alloca: None,
292 backtrace_tail_last_slot_alloca: None,
293
294 scope_stack: Vec::new(),
296 })
297 }
298
299 pub fn enter_scope(&mut self) {
301 self.scope_stack.push(std::collections::HashSet::new());
302 }
303
304 pub fn exit_scope(&mut self) {
306 if let Some(names) = self.scope_stack.pop() {
307 for name in names {
308 self.variables.remove(&name);
309 self.var_types.remove(&name);
310 self.alias_vars.remove(&name);
311 self.string_vars.remove(&name);
312 self.optimized_out_vars.remove(&name);
313 self.var_pc_addresses.remove(&name);
314 }
315 }
316 }
317
318 pub fn is_name_in_any_scope(&self, name: &str) -> bool {
320 self.scope_stack.iter().any(|s| s.contains(name))
321 }
322
323 pub fn is_name_in_current_scope(&self, name: &str) -> bool {
325 match self.scope_stack.last() {
326 Some(top) => top.contains(name),
327 None => false,
328 }
329 }
330
331 pub fn declare_name_in_current_scope(&mut self, name: &str) -> Result<()> {
333 if self.scope_stack.is_empty() {
334 self.enter_scope();
336 }
337 if self.is_name_in_current_scope(name) {
338 return Err(CodeGenError::TypeError(format!(
339 "Redeclaration in the same scope is not allowed: '{name}'"
340 )));
341 }
342 if self.is_name_in_any_scope(name) {
343 return Err(CodeGenError::TypeError(format!(
344 "Shadowing is not allowed for immutable variables: '{name}'"
345 )));
346 }
347 if let Some(top) = self.scope_stack.last_mut() {
348 top.insert(name.to_string());
349 }
350 Ok(())
351 }
352
353 pub fn new_with_process_analyzer(
355 context: &'ctx Context,
356 module_name: &str,
357 process_analyzer: Option<&'dw DwarfAnalyzer>,
358 trace_id: Option<u32>,
359 compile_options: &crate::CompileOptions,
360 ) -> Result<Self> {
361 let mut codegen = Self::new(context, module_name, trace_id, compile_options)?;
362 codegen.process_analyzer = process_analyzer;
363 Ok(codegen)
364 }
365
366 pub fn set_compile_time_context(&mut self, pc_address: u64, module_path: String) {
368 self.current_compile_time_context = Some(CompileTimeContext {
369 pc_address,
370 module_path,
371 });
372 }
373
374 pub fn get_compile_time_context(&self) -> Result<&CompileTimeContext> {
376 self.current_compile_time_context
377 .as_ref()
378 .ok_or_else(|| CodeGenError::DwarfError("No compile-time context set".to_string()))
379 }
380
381 fn declare_trace_printk(context: &'ctx Context, module: &Module<'ctx>) -> FunctionValue<'ctx> {
383 let i32_type = context.i32_type();
384 let ptr_type = context.ptr_type(AddressSpace::default());
385 let i64_type = context.i64_type();
386
387 let fn_type = i32_type.fn_type(&[ptr_type.into(), i64_type.into()], true);
389
390 module.add_function("bpf_trace_printk", fn_type, None)
391 }
392
393 pub fn create_basic_ebpf_function(&mut self, function_name: &str) -> Result<()> {
395 let i32_type = self.context.i32_type();
396 let ptr_type = self.context.ptr_type(AddressSpace::default());
397
398 let fn_type = i32_type.fn_type(&[ptr_type.into()], false);
400
401 let function = self.module.add_function(function_name, fn_type, None);
402
403 function.add_attribute(
405 inkwell::attributes::AttributeLoc::Function,
406 self.context.create_string_attribute("section", "uprobe"),
407 );
408
409 let basic_block = self.context.append_basic_block(function, "entry");
411 self.builder.position_at_end(basic_block);
412
413 info!("Created eBPF function: {}", function_name);
414 Ok(())
415 }
416
417 #[cfg(test)]
419 pub fn __test_ensure_proc_offsets_map(&mut self) -> Result<()> {
420 self.map_manager
421 .create_proc_module_offsets_map(
422 &self.module,
423 &self.di_builder,
424 &self.compile_unit,
425 "proc_module_offsets",
426 self.compile_options.proc_module_offsets_max_entries,
427 )
428 .map_err(|e| {
429 CodeGenError::LLVMError(format!(
430 "Failed to create proc_module_offsets map in test: {e}"
431 ))
432 })?;
433 self.map_manager
434 .create_pid_aliases_map(
435 &self.module,
436 &self.di_builder,
437 &self.compile_unit,
438 "pid_aliases",
439 self.compile_options.proc_module_offsets_max_entries,
440 )
441 .map_err(|e| {
442 CodeGenError::LLVMError(format!("Failed to create pid_aliases map in test: {e}"))
443 })?;
444 self.map_manager
445 .create_proc_module_range_meta_map(
446 &self.module,
447 &self.di_builder,
448 &self.compile_unit,
449 "proc_module_range_meta",
450 self.compile_options.proc_module_offsets_max_entries,
451 )
452 .map_err(|e| {
453 CodeGenError::LLVMError(format!(
454 "Failed to create proc_module_range_meta map in test: {e}"
455 ))
456 })?;
457 self.map_manager
458 .create_proc_module_ranges_map(
459 &self.module,
460 &self.di_builder,
461 &self.compile_unit,
462 "proc_module_ranges",
463 self.compile_options
464 .proc_module_offsets_max_entries
465 .saturating_mul(2)
466 .max(1),
467 )
468 .map_err(|e| {
469 CodeGenError::LLVMError(format!(
470 "Failed to create proc_module_ranges map in test: {e}"
471 ))
472 })
473 }
474
475 #[cfg(test)]
477 pub fn __test_alloc_pm_key(&mut self) -> Result<()> {
478 let i32_type = self.context.i32_type();
479 let key_arr_ty = i32_type.array_type(4);
480 let key_alloca = self
481 .builder
482 .build_alloca(key_arr_ty, "pm_key")
483 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
484 self.pm_key_alloca = Some(key_alloca);
485 Ok(())
486 }
487
488 pub fn get_module(&self) -> &Module<'ctx> {
490 &self.module
491 }
492
493 pub fn get_trace_context(&self) -> ghostscope_protocol::TraceContext {
495 self.trace_context.clone()
496 }
497
498 pub fn backtrace_tail_call_program(&self) -> Option<BacktraceTailCallProgram> {
499 self.pending_backtrace_tail_call
500 .as_ref()
501 .map(|plan| BacktraceTailCallProgram {
502 step_program_name: plan.step_program_name.clone(),
503 })
504 }
505
506 pub(crate) fn current_insert_block(&self, op: &str) -> Result<BasicBlock<'ctx>> {
507 self.builder
508 .get_insert_block()
509 .ok_or_else(|| CodeGenError::Builder(format!("{op} requires an active insert block")))
510 }
511
512 pub(crate) fn current_function(&self, op: &str) -> Result<FunctionValue<'ctx>> {
513 self.current_insert_block(op)?
514 .get_parent()
515 .ok_or_else(|| CodeGenError::Builder(format!("{op} requires a parent function")))
516 }
517
518 pub fn get_pt_regs_parameter(&self) -> Result<PointerValue<'ctx>> {
520 let current_function = self.current_function("get pt_regs parameter")?;
521
522 let pt_regs_param = current_function
523 .get_first_param()
524 .ok_or_else(|| CodeGenError::Builder("Function has no parameters".to_string()))?
525 .into_pointer_value();
526
527 Ok(pt_regs_param)
528 }
529
530 pub fn compile_program(
532 &mut self,
533 _program: &crate::script::Program,
534 function_name: &str,
535 trace_statements: &[crate::script::Statement],
536 target_pid: Option<u32>,
537 compile_time_pc: Option<u64>,
538 module_path: Option<&str>,
539 ) -> Result<(FunctionValue<'ctx>, ghostscope_protocol::TraceContext)> {
540 info!(
541 "Starting program compilation with function: {}",
542 function_name
543 );
544
545 self.current_compile_time_context =
547 if let (Some(pc), Some(path)) = (compile_time_pc, module_path) {
548 Some(CompileTimeContext {
549 pc_address: pc,
550 module_path: path.to_string(),
551 })
552 } else {
553 None
554 };
555 self.prepare_backtrace_unwind_rows(trace_statements);
556
557 match self.compile_options.event_map_type {
560 crate::EventMapType::RingBuf => {
561 self.map_manager
562 .create_ringbuf_map(
563 &self.module,
564 &self.di_builder,
565 &self.compile_unit,
566 "ringbuf",
567 self.compile_options.ringbuf_size,
568 )
569 .map_err(|e| {
570 CodeGenError::LLVMError(format!("Failed to create ringbuf map: {e}"))
571 })?;
572 }
573 crate::EventMapType::PerfEventArray => {
574 self.map_manager
575 .create_perf_event_array_map(
576 &self.module,
577 &self.di_builder,
578 &self.compile_unit,
579 "events",
580 )
581 .map_err(|e| {
582 CodeGenError::LLVMError(format!(
583 "Failed to create perf event array map: {e}"
584 ))
585 })?;
586 }
587 }
588
589 self.map_manager
590 .create_event_loss_counter_map(
591 &self.module,
592 &self.di_builder,
593 &self.compile_unit,
594 "event_loss_counters",
595 1,
596 )
597 .map_err(|e| {
598 CodeGenError::LLVMError(format!("Failed to create event_loss_counters map: {e}"))
599 })?;
600
601 self.map_manager
605 .create_percpu_array_map(
606 &self.module,
607 &self.di_builder,
608 &self.compile_unit,
609 "event_accum_buffer",
610 1,
611 self.compile_options.max_trace_event_size as u64,
612 )
613 .map_err(|e| {
614 CodeGenError::LLVMError(format!("Failed to create event_accum_buffer: {e}"))
615 })?;
616
617 self.map_manager
619 .create_proc_module_offsets_map(
620 &self.module,
621 &self.di_builder,
622 &self.compile_unit,
623 "proc_module_offsets",
624 self.compile_options.proc_module_offsets_max_entries,
625 )
626 .map_err(|e| {
627 CodeGenError::LLVMError(format!("Failed to create proc_module_offsets map: {e}"))
628 })?;
629
630 self.map_manager
631 .create_pid_aliases_map(
632 &self.module,
633 &self.di_builder,
634 &self.compile_unit,
635 "pid_aliases",
636 self.compile_options.proc_module_offsets_max_entries,
637 )
638 .map_err(|e| {
639 CodeGenError::LLVMError(format!("Failed to create pid_aliases map: {e}"))
640 })?;
641
642 if !self.backtrace_unwind_rows.is_empty() {
643 let share_backtrace_maps = !self.backtrace_module_row_ranges.is_empty();
644 if share_backtrace_maps {
645 self.map_manager.mark_pinned_map("bt_unwind_rows");
646 self.map_manager.mark_pinned_map("bt_module_row_ranges");
647 }
648 self.map_manager
649 .create_array_map(
650 &self.module,
651 &self.di_builder,
652 &self.compile_unit,
653 "bt_unwind_rows",
654 self.backtrace_unwind_row_map_entries(),
655 crate::BACKTRACE_UNWIND_ROW_SIZE as u64,
656 )
657 .map_err(|e| {
658 CodeGenError::LLVMError(format!("Failed to create bt_unwind_rows map: {e}"))
659 })?;
660 if share_backtrace_maps {
661 self.map_manager
662 .create_proc_module_range_meta_map(
663 &self.module,
664 &self.di_builder,
665 &self.compile_unit,
666 "proc_module_range_meta",
667 self.compile_options.proc_module_offsets_max_entries,
668 )
669 .map_err(|e| {
670 CodeGenError::LLVMError(format!(
671 "Failed to create proc_module_range_meta map: {e}"
672 ))
673 })?;
674 self.map_manager
675 .create_proc_module_ranges_map(
676 &self.module,
677 &self.di_builder,
678 &self.compile_unit,
679 "proc_module_ranges",
680 self.compile_options
681 .proc_module_offsets_max_entries
682 .saturating_mul(2)
683 .max(1),
684 )
685 .map_err(|e| {
686 CodeGenError::LLVMError(format!(
687 "Failed to create proc_module_ranges map: {e}"
688 ))
689 })?;
690 self.map_manager
691 .create_hash_map(
692 &self.module,
693 &self.di_builder,
694 &self.compile_unit,
695 "bt_module_row_ranges",
696 self.compile_options.proc_module_offsets_max_entries.max(1),
697 (
698 std::mem::size_of::<u64>() as u64,
699 ghostscope_protocol::BACKTRACE_MODULE_ROW_RANGE_SIZE as u64,
700 ),
701 )
702 .map_err(|e| {
703 CodeGenError::LLVMError(format!(
704 "Failed to create bt_module_row_ranges map: {e}"
705 ))
706 })?;
707 }
708 self.map_manager
709 .create_percpu_array_map(
710 &self.module,
711 &self.di_builder,
712 &self.compile_unit,
713 "bt_state",
714 self.backtrace_tail_call_slots.max(1) as u64,
715 crate::BACKTRACE_TAIL_STATE_SIZE as u64,
716 )
717 .map_err(|e| {
718 CodeGenError::LLVMError(format!("Failed to create bt_state map: {e}"))
719 })?;
720 self.map_manager
721 .create_program_array_map(
722 &self.module,
723 &self.di_builder,
724 &self.compile_unit,
725 "bt_prog_array",
726 1,
727 )
728 .map_err(|e| {
729 CodeGenError::LLVMError(format!("Failed to create bt_prog_array map: {e}"))
730 })?;
731 }
732
733 let main_function = self.create_main_function(function_name)?;
738
739 let pid_filter_spec = self
743 .compile_options
744 .pid_filter_spec
745 .or_else(|| target_pid.map(|pid| crate::PidFilterSpec::HostTgid { filter_pid: pid }));
746 if let Some(spec) = pid_filter_spec {
747 self.add_pid_filter(spec)?;
748 }
749
750 let program = crate::script::ast::Program {
752 statements: trace_statements.to_vec(),
753 };
754
755 let variable_types = std::collections::HashMap::new(); let trace_context =
760 self.compile_program_with_staged_transmission(&program, variable_types)?;
761 info!(
762 "Generated TraceContext with {} strings",
763 trace_context.string_count()
764 );
765
766 let i32_type = self.context.i32_type();
768 let return_value = i32_type.const_int(0, false);
769 self.builder
770 .build_return(Some(&return_value))
771 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
772
773 info!(
774 "Successfully compiled program with function: {} and TraceContext",
775 function_name
776 );
777 Ok((main_function, trace_context))
778 }
779
780 fn create_main_function(&mut self, function_name: &str) -> Result<FunctionValue<'ctx>> {
782 let i32_type = self.context.i32_type();
783 let ptr_type = self.context.ptr_type(AddressSpace::default());
784
785 let fn_type = i32_type.fn_type(&[ptr_type.into()], false);
787 let function = self.module.add_function(function_name, fn_type, None);
788
789 function.set_section(Some("uprobe"));
791
792 let basic_block = self.context.append_basic_block(function, "entry");
794 self.builder.position_at_end(basic_block);
795
796 let key_arr_ty = i32_type.array_type(4);
801 let key_alloca = self
802 .builder
803 .build_alloca(key_arr_ty, "pm_key")
804 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
805 self.pm_key_alloca = Some(key_alloca);
806
807 let event_off_alloca = self
809 .builder
810 .build_alloca(i32_type, "event_offset")
811 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
812 self.builder
813 .build_store(event_off_alloca, i32_type.const_zero())
814 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
815 self.event_offset_alloca = Some(event_off_alloca);
816
817 info!("Created main function: {}", function_name);
818 Ok(function)
819 }
820
821 pub(crate) fn create_tail_call_function(
822 &mut self,
823 function_name: &str,
824 ) -> Result<FunctionValue<'ctx>> {
825 let i32_type = self.context.i32_type();
826 let ptr_type = self.context.ptr_type(AddressSpace::default());
827 let fn_type = i32_type.fn_type(&[ptr_type.into()], false);
828 let function = self.module.add_function(function_name, fn_type, None);
829 function.set_section(Some("uprobe"));
830
831 let basic_block = self.context.append_basic_block(function, "entry");
832 self.builder.position_at_end(basic_block);
833
834 let key_arr_ty = i32_type.array_type(4);
835 let key_alloca = self
836 .builder
837 .build_alloca(key_arr_ty, "pm_key")
838 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
839 self.pm_key_alloca = Some(key_alloca);
840
841 info!("Created tail-call eBPF function: {}", function_name);
842 Ok(function)
843 }
844
845 fn add_pid_filter(&mut self, spec: crate::PidFilterSpec) -> Result<()> {
848 match spec {
849 crate::PidFilterSpec::HostTgid { filter_pid } => self.add_host_pid_filter(filter_pid),
850 crate::PidFilterSpec::NamespaceTgid { filter_pid, pid_ns } => {
851 let (pid_ns_dev, pid_ns_inode) = pid_ns.helper_dev_inode().ok_or_else(|| {
852 CodeGenError::LLVMError(
853 "Namespace TGID filter requires pid namespace device id".to_string(),
854 )
855 })?;
856 self.add_namespace_pid_filter(filter_pid, pid_ns_dev, pid_ns_inode)
857 }
858 }
859 }
860
861 fn add_host_pid_filter(&mut self, filter_pid: u32) -> Result<()> {
862 info!("Adding host TGID filter for filter PID: {}", filter_pid);
863
864 let current_fn = self.current_function("add host pid filter")?;
866
867 let continue_block = self
869 .context
870 .append_basic_block(current_fn, "continue_execution");
871 let early_return_block = self
872 .context
873 .append_basic_block(current_fn, "pid_mismatch_return");
874
875 let pid_tgid_value = self.get_current_pid_tgid()?;
877
878 let shift_amount = self.context.i64_type().const_int(32, false);
880 let current_tgid = self
881 .builder
882 .build_right_shift(pid_tgid_value, shift_amount, false, "current_tgid")
883 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
884
885 let target_pid_value = self.context.i64_type().const_int(filter_pid as u64, false);
887 let pid_matches = self
888 .builder
889 .build_int_compare(
890 inkwell::IntPredicate::EQ,
891 current_tgid,
892 target_pid_value,
893 "pid_matches",
894 )
895 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
896
897 self.builder
899 .build_conditional_branch(pid_matches, continue_block, early_return_block)
900 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
901
902 self.builder.position_at_end(early_return_block);
904 self.builder
905 .build_return(Some(&self.context.i32_type().const_int(0, false)))
906 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
907
908 self.builder.position_at_end(continue_block);
910
911 info!(
912 "Host TGID filter added successfully for filter PID: {}",
913 filter_pid
914 );
915 Ok(())
916 }
917
918 fn add_namespace_pid_filter(
919 &mut self,
920 filter_pid: u32,
921 pid_ns_dev: u64,
922 pid_ns_inode: u64,
923 ) -> Result<()> {
924 const BPF_FUNC_GET_NS_CURRENT_PID_TGID: u64 = 120;
925 const BPF_PIDNS_INFO_SIZE: u64 = 8; info!(
928 "Adding namespace TGID filter: filter_pid={} ns_dev={} ns_inode={}",
929 filter_pid, pid_ns_dev, pid_ns_inode
930 );
931
932 let current_fn = self
933 .builder
934 .get_insert_block()
935 .ok_or_else(|| CodeGenError::Builder("No current insert block".to_string()))?
936 .get_parent()
937 .ok_or_else(|| CodeGenError::Builder("No parent function".to_string()))?;
938
939 let helper_ok_block = self
940 .context
941 .append_basic_block(current_fn, "pidns_helper_ok");
942 let continue_block = self
943 .context
944 .append_basic_block(current_fn, "continue_execution");
945 let early_return_block = self
946 .context
947 .append_basic_block(current_fn, "pid_mismatch_return");
948
949 let i32_type = self.context.i32_type();
950 let i64_type = self.context.i64_type();
951 let ptr_type = self.context.ptr_type(AddressSpace::default());
952
953 let pidns_info_ty = i32_type.array_type(2);
955 let pidns_info_alloca = self
956 .builder
957 .build_alloca(pidns_info_ty, "pidns_info")
958 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
959 self.builder
960 .build_store(pidns_info_alloca, pidns_info_ty.const_zero())
961 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
962
963 let pidns_info_ptr = self
964 .builder
965 .build_bit_cast(pidns_info_alloca, ptr_type, "pidns_info_ptr")
966 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
967
968 let helper_args = [
969 i64_type.const_int(pid_ns_dev, false).into(),
970 i64_type.const_int(pid_ns_inode, false).into(),
971 pidns_info_ptr,
972 i64_type.const_int(BPF_PIDNS_INFO_SIZE, false).into(),
973 ];
974 let helper_ret = self.create_bpf_helper_call(
975 BPF_FUNC_GET_NS_CURRENT_PID_TGID,
976 &helper_args,
977 i64_type.into(),
978 "ns_pid_tgid_ret",
979 )?;
980 let helper_ret = match helper_ret {
981 inkwell::values::BasicValueEnum::IntValue(v) => v,
982 _ => {
983 return Err(CodeGenError::LLVMError(
984 "bpf_get_ns_current_pid_tgid did not return integer".to_string(),
985 ));
986 }
987 };
988
989 let helper_ok = self
990 .builder
991 .build_int_compare(
992 inkwell::IntPredicate::EQ,
993 helper_ret,
994 i64_type.const_zero(),
995 "pidns_helper_ok",
996 )
997 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
998 self.builder
999 .build_conditional_branch(helper_ok, helper_ok_block, early_return_block)
1000 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1001
1002 self.builder.position_at_end(helper_ok_block);
1003 let tgid_ptr = unsafe {
1006 self.builder.build_gep(
1007 pidns_info_ty,
1008 pidns_info_alloca,
1009 &[i32_type.const_zero(), i32_type.const_int(1, false)],
1010 "pidns_tgid_ptr",
1011 )
1012 }
1013 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1014 let ns_tgid = self
1015 .builder
1016 .build_load(i32_type, tgid_ptr, "ns_tgid")
1017 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
1018 .into_int_value();
1019 let ns_tgid_i64 = self
1020 .builder
1021 .build_int_z_extend(ns_tgid, i64_type, "ns_tgid_i64")
1022 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1023 let target_pid_value = i64_type.const_int(filter_pid as u64, false);
1024 let pid_matches = self
1025 .builder
1026 .build_int_compare(
1027 inkwell::IntPredicate::EQ,
1028 ns_tgid_i64,
1029 target_pid_value,
1030 "pid_matches_ns",
1031 )
1032 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1033 self.builder
1034 .build_conditional_branch(pid_matches, continue_block, early_return_block)
1035 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1036
1037 self.builder.position_at_end(early_return_block);
1038 self.builder
1039 .build_return(Some(&self.context.i32_type().const_int(0, false)))
1040 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1041
1042 self.builder.position_at_end(continue_block);
1043 info!(
1044 "Namespace TGID filter added successfully for filter PID: {}",
1045 filter_pid
1046 );
1047 Ok(())
1048 }
1049
1050 pub fn get_or_create_flag_global(&mut self, name: &str) -> PointerValue<'ctx> {
1052 if let Some(g) = self.module.get_global(name) {
1053 return g.as_pointer_value();
1054 }
1055 let i8_type = self.context.i8_type();
1056 let global = self
1057 .module
1058 .add_global(i8_type, Some(AddressSpace::default()), name);
1059 global.set_initializer(&i8_type.const_zero());
1060 global.as_pointer_value()
1061 }
1062
1063 pub fn store_flag_value(&mut self, name: &str, value: u8) -> Result<()> {
1065 let ptr = self.get_or_create_flag_global(name);
1066 self.builder
1067 .build_store(ptr, self.context.i8_type().const_int(value as u64, false))
1068 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store flag {name}: {e}")))?;
1069 Ok(())
1070 }
1071
1072 pub fn mark_any_success(&mut self) -> Result<()> {
1074 self.store_flag_value("_gs_any_success", 1)
1075 }
1076
1077 pub fn mark_any_fail(&mut self) -> Result<()> {
1079 self.store_flag_value("_gs_any_fail", 1)
1080 }
1081
1082 pub fn get_or_create_cond_error_global(&mut self) -> PointerValue<'ctx> {
1084 if let Some(g) = self.module.get_global("_gs_cond_error") {
1085 return g.as_pointer_value();
1086 }
1087 let i8_type = self.context.i8_type();
1088 let global =
1089 self.module
1090 .add_global(i8_type, Some(AddressSpace::default()), "_gs_cond_error");
1091 global.set_initializer(&i8_type.const_zero());
1092 global.as_pointer_value()
1093 }
1094
1095 pub fn reset_condition_error(&mut self) -> Result<()> {
1097 let ptr = self.get_or_create_cond_error_global();
1098 self.builder
1099 .build_store(ptr, self.context.i8_type().const_zero())
1100 .map_err(|e| CodeGenError::LLVMError(format!("Failed to reset _gs_cond_error: {e}")))?;
1101 let aptr = self.get_or_create_cond_error_addr_global();
1103 self.builder
1104 .build_store(aptr, self.context.i64_type().const_zero())
1105 .map_err(|e| {
1106 CodeGenError::LLVMError(format!("Failed to reset _gs_cond_error_addr: {e}"))
1107 })?;
1108 let fptr = self.get_or_create_cond_error_flags_global();
1110 self.builder
1111 .build_store(fptr, self.context.i8_type().const_zero())
1112 .map_err(|e| {
1113 CodeGenError::LLVMError(format!("Failed to reset _gs_cond_error_flags: {e}"))
1114 })?;
1115 Ok(())
1116 }
1117
1118 pub fn get_or_create_cond_error_addr_global(&mut self) -> PointerValue<'ctx> {
1120 if let Some(g) = self.module.get_global("_gs_cond_error_addr") {
1121 return g.as_pointer_value();
1122 }
1123 let i64_type = self.context.i64_type();
1124 let global = self.module.add_global(
1125 i64_type,
1126 Some(AddressSpace::default()),
1127 "_gs_cond_error_addr",
1128 );
1129 global.set_initializer(&i64_type.const_zero());
1130 global.as_pointer_value()
1131 }
1132
1133 pub fn set_condition_error_if_unset(&mut self, code: u8) -> Result<()> {
1135 if !self.condition_context_active {
1136 return Ok(());
1137 }
1138 let ptr = self.get_or_create_cond_error_global();
1139 let cur = self
1140 .builder
1141 .build_load(self.context.i8_type(), ptr, "cond_err_cur")
1142 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
1143 .into_int_value();
1144 let is_zero = self
1145 .builder
1146 .build_int_compare(
1147 inkwell::IntPredicate::EQ,
1148 cur,
1149 self.context.i8_type().const_zero(),
1150 "cond_err_is_zero",
1151 )
1152 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1153 let newv_bv: inkwell::values::BasicValueEnum =
1154 self.context.i8_type().const_int(code as u64, false).into();
1155 let sel = self
1156 .builder
1157 .build_select::<inkwell::values::BasicValueEnum, _>(
1158 is_zero,
1159 newv_bv,
1160 cur.into(),
1161 "cond_err_new",
1162 )
1163 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1164 self.builder
1165 .build_store(ptr, sel)
1166 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1167 Ok(())
1168 }
1169
1170 pub fn build_condition_error_predicate(&mut self) -> Result<inkwell::values::IntValue<'ctx>> {
1172 let ptr = self.get_or_create_cond_error_global();
1173 let cur = self
1174 .builder
1175 .build_load(self.context.i8_type(), ptr, "cond_err_cur")
1176 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
1177 .into_int_value();
1178 self.builder
1179 .build_int_compare(
1180 inkwell::IntPredicate::NE,
1181 cur,
1182 self.context.i8_type().const_zero(),
1183 "cond_err_nonzero",
1184 )
1185 .map_err(|e| CodeGenError::LLVMError(e.to_string()))
1186 }
1187
1188 pub fn get_or_create_cond_error_flags_global(&mut self) -> PointerValue<'ctx> {
1190 if let Some(g) = self.module.get_global("_gs_cond_error_flags") {
1191 return g.as_pointer_value();
1192 }
1193 let i8_type = self.context.i8_type();
1194 let global = self.module.add_global(
1195 i8_type,
1196 Some(AddressSpace::default()),
1197 "_gs_cond_error_flags",
1198 );
1199 global.set_initializer(&i8_type.const_zero());
1200 global.as_pointer_value()
1201 }
1202
1203 pub fn or_condition_error_flags(&mut self, flags: IntValue<'ctx>) -> Result<()> {
1205 if !self.condition_context_active {
1206 return Ok(());
1207 }
1208 let ptr = self.get_or_create_cond_error_flags_global();
1209 let cur = self
1210 .builder
1211 .build_load(self.context.i8_type(), ptr, "cond_err_flags_cur")
1212 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
1213 .into_int_value();
1214 let newv = self
1215 .builder
1216 .build_or(cur, flags, "cond_err_flags_or")
1217 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1218 self.builder
1219 .build_store(ptr, newv)
1220 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1221 Ok(())
1222 }
1223
1224 pub fn set_condition_error_addr_if_unset(&mut self, addr: IntValue<'ctx>) -> Result<()> {
1226 if !self.condition_context_active {
1227 return Ok(());
1228 }
1229 let ptr = self.get_or_create_cond_error_addr_global();
1230 let cur = self
1231 .builder
1232 .build_load(self.context.i64_type(), ptr, "cond_err_addr_cur")
1233 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
1234 .into_int_value();
1235 let is_zero = self
1236 .builder
1237 .build_int_compare(
1238 inkwell::IntPredicate::EQ,
1239 cur,
1240 self.context.i64_type().const_zero(),
1241 "cond_err_addr_is_zero",
1242 )
1243 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1244 let sel = self
1245 .builder
1246 .build_select::<IntValue<'ctx>, _>(is_zero, addr, cur, "cond_err_addr_new")
1247 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1248 self.builder
1249 .build_store(ptr, sel)
1250 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1251 Ok(())
1252 }
1253}