1use super::context::{CodeGenError, EbpfContext, Result, RuntimeAddress};
7use aya_ebpf_bindings::bindings::bpf_func_id::{
8 BPF_FUNC_get_current_pid_tgid, BPF_FUNC_get_current_task, BPF_FUNC_ktime_get_ns,
9 BPF_FUNC_map_lookup_elem, BPF_FUNC_perf_event_output, BPF_FUNC_probe_read_kernel,
10 BPF_FUNC_probe_read_user, BPF_FUNC_probe_read_user_str, BPF_FUNC_ringbuf_output,
11};
12use ghostscope_dwarf::MemoryAccessSize;
13use ghostscope_platform::register_mapping;
14use ghostscope_protocol::trace_event::VariableStatus;
15use inkwell::types::{BasicType, BasicTypeEnum};
16use inkwell::values::{BasicMetadataValueEnum, BasicValueEnum, IntValue, PointerValue};
17use inkwell::AddressSpace;
18
19struct ProbeReadResult<'ctx> {
20 loaded_i64: IntValue<'ctx>,
21 combined_fail: IntValue<'ctx>,
22 not_found: IntValue<'ctx>,
23}
24
25pub(crate) struct ProcModuleOffsetsLookup<'ctx> {
26 pub(crate) found: IntValue<'ctx>,
27 pub(crate) text: IntValue<'ctx>,
28 pub(crate) rodata: IntValue<'ctx>,
29 pub(crate) data: IntValue<'ctx>,
30 pub(crate) bss: IntValue<'ctx>,
31}
32
33impl<'ctx, 'dw> EbpfContext<'ctx, 'dw> {
34 fn get_or_create_tls_scratch_buffer(&mut self) -> Result<PointerValue<'ctx>> {
35 if let Some(alloca) = self.tls_scratch_alloca {
36 return Ok(alloca);
37 }
38
39 let current_block = self.builder.get_insert_block().ok_or_else(|| {
40 CodeGenError::LLVMError("no current block for TLS scratch allocation".to_string())
41 })?;
42 let current_fn = current_block.get_parent().ok_or_else(|| {
43 CodeGenError::LLVMError("no current function for TLS scratch allocation".to_string())
44 })?;
45 let entry_block = current_fn.get_first_basic_block().ok_or_else(|| {
46 CodeGenError::LLVMError("no entry block for TLS scratch allocation".to_string())
47 })?;
48
49 if let Some(first_instruction) = entry_block.get_first_instruction() {
50 self.builder.position_before(&first_instruction);
51 } else {
52 self.builder.position_at_end(entry_block);
53 }
54 let scratch = self
55 .builder
56 .build_alloca(self.context.i64_type(), "tls_scratch")
57 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
58 self.builder.position_at_end(current_block);
59
60 self.tls_scratch_alloca = Some(scratch);
61 Ok(scratch)
62 }
63
64 fn get_probe_read_scratch_buffer(
65 &mut self,
66 result_size: usize,
67 name_prefix: &str,
68 ) -> Result<PointerValue<'ctx>> {
69 if result_size <= 4 {
70 if let Some(key_alloca) = self.pm_key_alloca {
71 let i32_type = self.context.i32_type();
80 let key_arr_ty = i32_type.array_type(4);
81 let zero = i32_type.const_zero();
82 return unsafe {
85 self.builder
86 .build_gep(
87 key_arr_ty,
88 key_alloca,
89 &[zero, zero],
90 &format!("{name_prefix}_scratch_i8"),
91 )
92 .map_err(|e| CodeGenError::LLVMError(e.to_string()))
93 };
94 }
95 }
96
97 let buffer_name = format!("_temp_read_buffer_{result_size}");
98 let global_buffer = match self.module.get_global(&buffer_name) {
99 Some(existing) => existing.as_pointer_value(),
100 None => {
101 let array_type = self.context.i8_type().array_type(result_size as u32);
102 let global =
103 self.module
104 .add_global(array_type, Some(AddressSpace::default()), &buffer_name);
105 global.set_initializer(&array_type.const_zero());
106 global.as_pointer_value()
107 }
108 };
109 Ok(global_buffer)
110 }
111
112 pub fn lookup_proc_pid_alias(
113 &mut self,
114 runtime_pid: IntValue<'ctx>,
115 name_prefix: &str,
116 ) -> Result<IntValue<'ctx>> {
117 let Some(map_global) = self.module.get_global("pid_aliases") else {
118 return Ok(runtime_pid);
119 };
120
121 let i32_type = self.context.i32_type();
122 let ptr_type = self.context.ptr_type(AddressSpace::default());
123 let map_ptr = map_global.as_pointer_value();
124 let map_ptr_cast = self
125 .builder
126 .build_bit_cast(map_ptr, ptr_type, &format!("{name_prefix}_map_ptr"))
127 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
128
129 let key_alloca = self.pm_key_alloca.ok_or_else(|| {
130 CodeGenError::LLVMError("pm_key not allocated in entry block".to_string())
131 })?;
132 let key_arr_ty = i32_type.array_type(4);
133 let zero = i32_type.const_zero();
134 let key_ptr = unsafe {
137 self.builder
138 .build_gep(
139 key_arr_ty,
140 key_alloca,
141 &[zero, zero],
142 &format!("{name_prefix}_alias_key_ptr"),
143 )
144 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
145 };
146 self.builder
147 .build_store(key_ptr, runtime_pid)
148 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
149 let key_arg = self
150 .builder
151 .build_bit_cast(key_ptr, ptr_type, &format!("{name_prefix}_alias_key_arg"))
152 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
153
154 let lookup_id = self
155 .context
156 .i64_type()
157 .const_int(BPF_FUNC_map_lookup_elem as u64, false);
158 let lookup_fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
159 let lookup_fn_ptr = self
160 .builder
161 .build_int_to_ptr(lookup_id, ptr_type, &format!("{name_prefix}_lookup_fn"))
162 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
163 let lookup_args: Vec<BasicMetadataValueEnum> = vec![map_ptr_cast.into(), key_arg.into()];
164 let value_ptr_any = self
165 .builder
166 .build_indirect_call(
167 lookup_fn_type,
168 lookup_fn_ptr,
169 &lookup_args,
170 &format!("{name_prefix}_alias_lookup"),
171 )
172 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
173 .try_as_basic_value()
174 .left()
175 .ok_or_else(|| {
176 CodeGenError::LLVMError("pid_aliases lookup returned void".to_string())
177 })?;
178
179 let value_ptr = match value_ptr_any {
180 BasicValueEnum::PointerValue(p) => p,
181 _ => {
182 return Err(CodeGenError::LLVMError(
183 "pid_aliases lookup did not return pointer".to_string(),
184 ));
185 }
186 };
187
188 let helper_fn = self.current_function("lookup proc pid alias")?;
189 let alias_hit_block = self
190 .context
191 .append_basic_block(helper_fn, &format!("{name_prefix}_alias_hit"));
192 let alias_miss_block = self
193 .context
194 .append_basic_block(helper_fn, &format!("{name_prefix}_alias_miss"));
195 let alias_cont_block = self
196 .context
197 .append_basic_block(helper_fn, &format!("{name_prefix}_alias_cont"));
198
199 let i64_type = self.context.i64_type();
200 let value_ptr_int = self
201 .builder
202 .build_ptr_to_int(
203 value_ptr,
204 i64_type,
205 &format!("{name_prefix}_alias_value_ptr_int"),
206 )
207 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
208 let is_hit = self
209 .builder
210 .build_int_compare(
211 inkwell::IntPredicate::NE,
212 value_ptr_int,
213 i64_type.const_zero(),
214 &format!("{name_prefix}_alias_found"),
215 )
216 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
217 self.builder
218 .build_conditional_branch(is_hit, alias_hit_block, alias_miss_block)
219 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
220
221 self.builder.position_at_end(alias_hit_block);
222 let alias_value = self
223 .builder
224 .build_load(i32_type, value_ptr, &format!("{name_prefix}_alias_value"))
225 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
226 .into_int_value();
227 self.builder
228 .build_unconditional_branch(alias_cont_block)
229 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
230 let alias_hit_end = self.current_insert_block("finish pid alias hit block")?;
231
232 self.builder.position_at_end(alias_miss_block);
233 self.builder
234 .build_unconditional_branch(alias_cont_block)
235 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
236 let alias_miss_end = self.current_insert_block("finish pid alias miss block")?;
237
238 self.builder.position_at_end(alias_cont_block);
239 let alias_phi = self
240 .builder
241 .build_phi(i32_type, &format!("{name_prefix}_alias_pid"))
242 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
243 alias_phi.add_incoming(&[
244 (&alias_value, alias_hit_end),
245 (&runtime_pid, alias_miss_end),
246 ]);
247
248 Ok(alias_phi.as_basic_value().into_int_value())
249 }
250
251 pub(crate) fn proc_module_pid_key(&mut self, name_prefix: &str) -> Result<IntValue<'ctx>> {
252 const BPF_FUNC_GET_NS_CURRENT_PID_TGID: u64 = 120;
253 const BPF_PIDNS_INFO_SIZE: u64 = 8; let i32_type = self.context.i32_type();
256 let i64_type = self.context.i64_type();
257 let ptr_type = self.context.ptr_type(AddressSpace::default());
258 let key_arr_ty = i32_type.array_type(4);
259 let key_alloca = self.pm_key_alloca.ok_or_else(|| {
260 CodeGenError::LLVMError("pm_key not allocated in entry block".to_string())
261 })?;
262
263 let helper_id = i64_type.const_int(BPF_FUNC_get_current_pid_tgid as u64, false);
264 let helper_fn_type = i64_type.fn_type(&[], false);
265 let helper_fn_ptr = self
266 .builder
267 .build_int_to_ptr(helper_id, ptr_type, &format!("{name_prefix}_get_pid_fn"))
268 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
269 let pid_tgid = self
270 .builder
271 .build_indirect_call(
272 helper_fn_type,
273 helper_fn_ptr,
274 &[],
275 &format!("{name_prefix}_pid_tgid"),
276 )
277 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
278 .try_as_basic_value()
279 .left()
280 .ok_or_else(|| {
281 CodeGenError::LLVMError("get_current_pid_tgid returned void".to_string())
282 })?;
283 let host_tgid = if let BasicValueEnum::IntValue(v) = pid_tgid {
284 let shifted = self
285 .builder
286 .build_right_shift(
287 v,
288 i64_type.const_int(32, false),
289 false,
290 &format!("{name_prefix}_pid_shift"),
291 )
292 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
293 self.builder
294 .build_int_truncate(shifted, i32_type, &format!("{name_prefix}_pid32"))
295 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
296 } else {
297 return Err(CodeGenError::LLVMError(
298 "pid_tgid is not IntValue".to_string(),
299 ));
300 };
301
302 let ns_spec = self
303 .compile_options
304 .proc_offsets_pid_ns
305 .and_then(|pid_ns| pid_ns.helper_dev_inode());
306
307 let runtime_pid = if let Some((pid_ns_dev, pid_ns_inode)) = ns_spec {
308 self.builder
309 .build_store(key_alloca, key_arr_ty.const_zero())
310 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
311 let pidns_info_ptr = self
312 .builder
313 .build_bit_cast(
314 key_alloca,
315 ptr_type,
316 &format!("{name_prefix}_pidns_info_ptr"),
317 )
318 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
319 let helper_args = [
320 i64_type.const_int(pid_ns_dev, false).into(),
321 i64_type.const_int(pid_ns_inode, false).into(),
322 pidns_info_ptr,
323 i64_type.const_int(BPF_PIDNS_INFO_SIZE, false).into(),
324 ];
325 let helper_ret = self.create_bpf_helper_call(
326 BPF_FUNC_GET_NS_CURRENT_PID_TGID,
327 &helper_args,
328 i64_type.into(),
329 &format!("{name_prefix}_ns_pid_tgid_ret"),
330 )?;
331 let helper_ret = match helper_ret {
332 BasicValueEnum::IntValue(v) => v,
333 _ => {
334 return Err(CodeGenError::LLVMError(
335 "bpf_get_ns_current_pid_tgid did not return integer".to_string(),
336 ));
337 }
338 };
339 let helper_ok = self
340 .builder
341 .build_int_compare(
342 inkwell::IntPredicate::EQ,
343 helper_ret,
344 i64_type.const_zero(),
345 &format!("{name_prefix}_ns_helper_ok"),
346 )
347 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
348 let ns_tgid_ptr = unsafe {
351 self.builder.build_gep(
352 key_arr_ty,
353 key_alloca,
354 &[i32_type.const_zero(), i32_type.const_int(1, false)],
355 &format!("{name_prefix}_ns_tgid_ptr"),
356 )
357 }
358 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
359 let ns_tgid = self
360 .builder
361 .build_load(i32_type, ns_tgid_ptr, &format!("{name_prefix}_ns_tgid"))
362 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
363 .into_int_value();
364 self.builder
368 .build_select(
369 helper_ok,
370 ns_tgid,
371 host_tgid,
372 &format!("{name_prefix}_pid_key"),
373 )
374 .map_err(|e| CodeGenError::Builder(e.to_string()))?
375 .into_int_value()
376 } else {
377 host_tgid
378 };
379
380 self.lookup_proc_pid_alias(runtime_pid, name_prefix)
381 }
382
383 pub fn get_or_create_i8_buffer(
385 &mut self,
386 size: u32,
387 name_prefix: &str,
388 ) -> (
389 inkwell::types::ArrayType<'ctx>,
390 inkwell::values::PointerValue<'ctx>,
391 ) {
392 let array_ty = self.context.i8_type().array_type(size);
393 let name = format!("{name_prefix}_{size}");
394 let global_ptr = match self.module.get_global(&name) {
395 Some(g) => g.as_pointer_value(),
396 None => {
397 let g = self
398 .module
399 .add_global(array_ty, Some(AddressSpace::default()), &name);
400 g.set_initializer(&array_ty.const_zero());
401 g.as_pointer_value()
402 }
403 };
404 (array_ty, global_ptr)
405 }
406
407 pub(crate) fn read_user_cstr_into_buffer(
410 &mut self,
411 src_addr: RuntimeAddress<'ctx>,
412 size: u32,
413 name_prefix: &str,
414 ) -> Result<(
415 inkwell::values::PointerValue<'ctx>,
416 inkwell::values::IntValue<'ctx>,
417 inkwell::types::ArrayType<'ctx>,
418 )> {
419 let (arr_ty, buf_global) = self.get_or_create_i8_buffer(size, name_prefix);
420
421 let ptr_ty = self.context.ptr_type(AddressSpace::default());
422 let dst_ptr = self
424 .builder
425 .build_bit_cast(buf_global, ptr_ty, "dst_ptr")
426 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
427 let src_ptr = self
428 .builder
429 .build_int_to_ptr(src_addr.value, ptr_ty, "src_ptr")
430 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
431 let src_ptr = self
432 .builder
433 .build_select::<BasicValueEnum<'ctx>, _>(
434 src_addr.offsets_found,
435 src_ptr.into(),
436 ptr_ty.const_null().into(),
437 "cstr_src_or_null",
438 )
439 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
440 .into_pointer_value();
441
442 let i64_ty = self.context.i64_type();
444 let i32_ty = self.context.i32_type();
445 let effective_size = self
446 .builder
447 .build_select::<BasicValueEnum<'ctx>, _>(
448 src_addr.offsets_found,
449 i32_ty.const_int(size as u64, false).into(),
450 i32_ty.const_zero().into(),
451 "cstr_size_or_zero",
452 )
453 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
454 .into_int_value();
455 let args: [inkwell::values::BasicValueEnum; 3] = [
456 dst_ptr,
457 effective_size.into(),
458 inkwell::values::BasicValueEnum::PointerValue(src_ptr),
459 ];
460 let ret = self.create_bpf_helper_call(
461 BPF_FUNC_probe_read_user_str as u64,
462 &args,
463 i64_ty.into(),
464 "probe_read_user_str",
465 )?;
466 let len = if let inkwell::values::BasicValueEnum::IntValue(iv) = ret {
467 iv
468 } else {
469 return Err(CodeGenError::LLVMError(
470 "probe_read_user_str did not return integer".to_string(),
471 ));
472 };
473 let len = self
474 .builder
475 .build_select::<BasicValueEnum<'ctx>, _>(
476 src_addr.offsets_found,
477 len.into(),
478 i64_ty.const_zero().into(),
479 "cstr_len_or_zero",
480 )
481 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
482 .into_int_value();
483 Ok((buf_global, len, arr_ty))
484 }
485
486 pub(crate) fn read_user_bytes_into_buffer(
489 &mut self,
490 src_addr: RuntimeAddress<'ctx>,
491 size: u32,
492 name_prefix: &str,
493 ) -> Result<(
494 inkwell::values::PointerValue<'ctx>,
495 inkwell::values::IntValue<'ctx>,
496 inkwell::types::ArrayType<'ctx>,
497 )> {
498 let (arr_ty, buf_global) = self.get_or_create_i8_buffer(size, name_prefix);
499 let ptr_ty = self.context.ptr_type(AddressSpace::default());
500 let i32_ty = self.context.i32_type();
501 let i64_ty = self.context.i64_type();
502 let dst_ptr = self
504 .builder
505 .build_bit_cast(buf_global, ptr_ty, "dst_ptr")
506 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
507 let src_ptr = self
508 .builder
509 .build_int_to_ptr(src_addr.value, ptr_ty, "src_ptr")
510 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
511 let src_ptr = self
512 .builder
513 .build_select::<BasicValueEnum<'ctx>, _>(
514 src_addr.offsets_found,
515 src_ptr.into(),
516 ptr_ty.const_null().into(),
517 "bytes_src_or_null",
518 )
519 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
520 .into_pointer_value();
521 let effective_size = self
522 .builder
523 .build_select::<BasicValueEnum<'ctx>, _>(
524 src_addr.offsets_found,
525 i32_ty.const_int(size as u64, false).into(),
526 i32_ty.const_zero().into(),
527 "bytes_size_or_zero",
528 )
529 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
530 .into_int_value();
531
532 let args: [inkwell::values::BasicValueEnum; 3] = [
533 dst_ptr,
534 effective_size.into(),
535 inkwell::values::BasicValueEnum::PointerValue(src_ptr),
536 ];
537 let ret = self.create_bpf_helper_call(
539 BPF_FUNC_probe_read_user as u64,
540 &args,
541 i64_ty.into(),
542 "probe_read_user",
543 )?;
544 let status = if let inkwell::values::BasicValueEnum::IntValue(iv) = ret {
545 iv
546 } else {
547 return Err(CodeGenError::LLVMError(
548 "probe_read_user did not return integer".to_string(),
549 ));
550 };
551 let status = self
552 .builder
553 .build_select::<BasicValueEnum<'ctx>, _>(
554 src_addr.offsets_found,
555 status.into(),
556 i64_ty.const_int(u64::MAX, true).into(),
557 "bytes_status_or_missing_offsets",
558 )
559 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
560 .into_int_value();
561 Ok((buf_global, status, arr_ty))
562 }
563 pub(crate) fn lookup_proc_module_offsets_value(
564 &mut self,
565 module_cookie: u64,
566 name_prefix: &str,
567 ) -> Result<ProcModuleOffsetsLookup<'ctx>> {
568 let i64_type = self.context.i64_type();
569 let ptr_type = self.context.ptr_type(AddressSpace::default());
570
571 let map_global = self
573 .module
574 .get_global("proc_module_offsets")
575 .ok_or_else(|| {
576 CodeGenError::LLVMError("proc_module_offsets map not found".to_string())
577 })?;
578 let map_ptr = map_global.as_pointer_value();
579 let map_ptr_cast = self
580 .builder
581 .build_bit_cast(map_ptr, ptr_type, &format!("{name_prefix}_map_ptr"))
582 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
583
584 let i32_type = self.context.i32_type();
587 let key_alloca = self.pm_key_alloca.ok_or_else(|| {
588 CodeGenError::LLVMError("pm_key not allocated in entry block".to_string())
589 })?;
590 let pid = self.proc_module_pid_key(name_prefix)?;
591
592 let store_key_u32 = |offset: usize,
593 value: IntValue<'ctx>,
594 name: &str,
595 ctx: &mut EbpfContext<'ctx, 'dw>|
596 -> Result<()> {
597 let offset_i32 = ctx.context.i32_type().const_int(offset as u64, false);
598 let ptr = unsafe {
601 ctx.builder
602 .build_gep(ctx.context.i8_type(), key_alloca, &[offset_i32], name)
603 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
604 };
605 ctx.builder
606 .build_store(ptr, value)
607 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
608 Ok(())
609 };
610
611 store_key_u32(
612 ghostscope_protocol::PROC_MODULE_KEY_PID_OFFSET,
613 pid,
614 &format!("{name_prefix}_pm_key_pid_ptr"),
615 self,
616 )?;
617 store_key_u32(
618 ghostscope_protocol::PROC_MODULE_KEY_PAD_OFFSET,
619 self.context.i32_type().const_zero(),
620 &format!("{name_prefix}_pm_key_pad_ptr"),
621 self,
622 )?;
623
624 let cookie_lo = i32_type.const_int(module_cookie & 0xffff_ffff, false);
625 let cookie_hi = i32_type.const_int(module_cookie >> 32, false);
626 store_key_u32(
627 ghostscope_protocol::PROC_MODULE_KEY_COOKIE_LO_OFFSET,
628 cookie_lo,
629 &format!("{name_prefix}_pm_key_cookie_lo_ptr"),
630 self,
631 )?;
632 store_key_u32(
633 ghostscope_protocol::PROC_MODULE_KEY_COOKIE_HI_OFFSET,
634 cookie_hi,
635 &format!("{name_prefix}_pm_key_cookie_hi_ptr"),
636 self,
637 )?;
638
639 let lookup_id = i64_type.const_int(BPF_FUNC_map_lookup_elem as u64, false);
641 let lookup_fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
642 let lookup_fn_ptr = self
643 .builder
644 .build_int_to_ptr(lookup_id, ptr_type, &format!("{name_prefix}_lookup_fn"))
645 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
646 let key_arg = self
648 .builder
649 .build_bit_cast(key_alloca, ptr_type, &format!("{name_prefix}_key_arg"))
650 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
651 let args: Vec<BasicMetadataValueEnum> = vec![map_ptr_cast.into(), key_arg.into()];
652 let val_ptr_any = self
653 .builder
654 .build_indirect_call(
655 lookup_fn_type,
656 lookup_fn_ptr,
657 &args,
658 &format!("{name_prefix}_val_ptr_any"),
659 )
660 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
661 .try_as_basic_value()
662 .left()
663 .ok_or_else(|| CodeGenError::LLVMError("map_lookup_elem returned void".to_string()))?;
664
665 let null_ptr = ptr_type.const_null();
666 let current_fn = self.current_function("generate module offset lookup")?;
667 let found_block = self
668 .context
669 .append_basic_block(current_fn, &format!("{name_prefix}_found_offsets"));
670 let miss_block = self
671 .context
672 .append_basic_block(current_fn, &format!("{name_prefix}_miss_offsets"));
673 let cont_block = self
674 .context
675 .append_basic_block(current_fn, &format!("{name_prefix}_cont_offsets"));
676
677 let val_ptr = if let BasicValueEnum::PointerValue(p) = val_ptr_any {
679 p
680 } else {
681 null_ptr
682 };
683 let is_null = self
684 .builder
685 .build_int_compare(
686 inkwell::IntPredicate::EQ,
687 self.builder
688 .build_ptr_to_int(val_ptr, i64_type, &format!("{name_prefix}_val_ptr_i64"))
689 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?,
690 i64_type.const_zero(),
691 &format!("{name_prefix}_is_null_offsets"),
692 )
693 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
694 self.builder
695 .build_conditional_branch(is_null, miss_block, found_block)
696 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
697
698 self.builder.position_at_end(found_block);
702 let load_field = |offset: usize,
703 field_name: &str,
704 ctx: &mut EbpfContext<'ctx, 'dw>,
705 base: PointerValue<'ctx>|
706 -> Result<IntValue<'ctx>> {
707 let offset_i32 = ctx.context.i32_type().const_int(offset as u64, false);
708 let field_ptr = unsafe {
711 ctx.builder
712 .build_gep(ctx.context.i8_type(), base, &[offset_i32], "field_ptr")
713 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
714 };
715 let loaded = ctx
716 .builder
717 .build_load(ctx.context.i64_type(), field_ptr, field_name)
718 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
719 if let BasicValueEnum::IntValue(iv) = loaded {
720 Ok(iv)
721 } else {
722 Err(CodeGenError::LLVMError("offset load failed".to_string()))
723 }
724 };
725 let off_text = load_field(
726 ghostscope_protocol::PROC_MODULE_OFFSETS_VALUE_TEXT_OFFSET,
727 &format!("{name_prefix}_text"),
728 self,
729 val_ptr,
730 )?;
731 let off_rodata = load_field(
732 ghostscope_protocol::PROC_MODULE_OFFSETS_VALUE_RODATA_OFFSET,
733 &format!("{name_prefix}_rodata"),
734 self,
735 val_ptr,
736 )?;
737 let off_data = load_field(
738 ghostscope_protocol::PROC_MODULE_OFFSETS_VALUE_DATA_OFFSET,
739 &format!("{name_prefix}_data"),
740 self,
741 val_ptr,
742 )?;
743 let off_bss = load_field(
744 ghostscope_protocol::PROC_MODULE_OFFSETS_VALUE_BSS_OFFSET,
745 &format!("{name_prefix}_bss"),
746 self,
747 val_ptr,
748 )?;
749 self.builder
750 .build_unconditional_branch(cont_block)
751 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
752 let found_end = self.current_insert_block("finish proc offsets found block")?;
753
754 self.builder.position_at_end(miss_block);
755 self.builder
756 .build_unconditional_branch(cont_block)
757 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
758 let miss_end = self.current_insert_block("finish proc offsets miss block")?;
759
760 self.builder.position_at_end(cont_block);
761 let zero_i64 = i64_type.const_zero();
762 let phi_i64 = |ctx: &mut EbpfContext<'ctx, 'dw>,
763 name: &str,
764 hit_value: IntValue<'ctx>|
765 -> Result<IntValue<'ctx>> {
766 let phi = ctx
767 .builder
768 .build_phi(i64_type, name)
769 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
770 phi.add_incoming(&[(&hit_value, found_end), (&zero_i64, miss_end)]);
771 Ok(phi.as_basic_value().into_int_value())
772 };
773 let found_type = self.context.bool_type();
774 let found_phi = self
775 .builder
776 .build_phi(found_type, &format!("{name_prefix}_found_phi"))
777 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
778 found_phi.add_incoming(&[
779 (&found_type.const_int(1, false), found_end),
780 (&found_type.const_zero(), miss_end),
781 ]);
782
783 Ok(ProcModuleOffsetsLookup {
784 found: found_phi.as_basic_value().into_int_value(),
785 text: phi_i64(self, &format!("{name_prefix}_text_phi"), off_text)?,
786 rodata: phi_i64(self, &format!("{name_prefix}_rodata_phi"), off_rodata)?,
787 data: phi_i64(self, &format!("{name_prefix}_data_phi"), off_data)?,
788 bss: phi_i64(self, &format!("{name_prefix}_bss_phi"), off_bss)?,
789 })
790 }
791
792 pub fn generate_runtime_address_from_offsets(
795 &mut self,
796 link_addr: IntValue<'ctx>,
797 section_type: u8,
798 module_cookie: u64,
799 ) -> Result<(IntValue<'ctx>, IntValue<'ctx>)> {
800 let i32_type = self.context.i32_type();
801 let offsets = self.lookup_proc_module_offsets_value(module_cookie, "offset")?;
802
803 let st_val = i32_type.const_int(section_type as u64, false);
808 let eq_text = self
809 .builder
810 .build_int_compare(
811 inkwell::IntPredicate::EQ,
812 st_val,
813 i32_type.const_int(0, false),
814 "is_text",
815 )
816 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
817 let eq_ro = self
818 .builder
819 .build_int_compare(
820 inkwell::IntPredicate::EQ,
821 st_val,
822 i32_type.const_int(1, false),
823 "is_ro",
824 )
825 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
826 let eq_da = self
827 .builder
828 .build_int_compare(
829 inkwell::IntPredicate::EQ,
830 st_val,
831 i32_type.const_int(2, false),
832 "is_da",
833 )
834 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
835
836 let tmp_any = self
837 .builder
838 .build_select(eq_da, offsets.data, offsets.bss, "sel_data_bss")
839 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
840 let tmp = tmp_any.into_int_value();
841
842 let tmp2_any = self
843 .builder
844 .build_select(eq_ro, offsets.rodata, tmp, "sel_rodata_else")
845 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
846 let tmp2 = tmp2_any.into_int_value();
847
848 let off_final_any = self
849 .builder
850 .build_select(eq_text, offsets.text, tmp2, "sel_text_else")
851 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
852 let off_final = off_final_any.into_int_value();
853 let rt_addr = self
854 .builder
855 .build_int_add(link_addr, off_final, "runtime_addr")
856 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
857 let final_addr = self
858 .builder
859 .build_select::<BasicValueEnum<'ctx>, _>(
860 offsets.found,
861 rt_addr.into(),
862 link_addr.into(),
863 "addr_or_link",
864 )
865 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
866 .into_int_value();
867
868 Ok((final_addr, offsets.found))
869 }
870 pub fn load_register_value(
872 &mut self,
873 reg_num: u16,
874 pt_regs_ptr: PointerValue<'ctx>,
875 ) -> Result<BasicValueEnum<'ctx>> {
876 let pt_regs_offset = self.dwarf_reg_to_pt_regs_offset(reg_num)?;
878
879 let i64_type = self.context.i64_type();
881 let offset_value = i64_type.const_int(pt_regs_offset as u64, false);
882
883 let reg_ptr = unsafe {
886 self.builder
887 .build_gep(
888 i64_type,
889 pt_regs_ptr,
890 &[offset_value],
891 &format!("reg_{reg_num}_ptr"),
892 )
893 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
894 };
895
896 let reg_value = self
898 .builder
899 .build_load(i64_type, reg_ptr, &format!("reg_{reg_num}"))
900 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
901
902 if let BasicValueEnum::IntValue(_) = reg_value {
903 Ok(reg_value)
904 } else {
905 Err(CodeGenError::RegisterMappingError(format!(
906 "Failed to load register {reg_num} as integer"
907 )))
908 }
909 }
910
911 fn probe_read_user_core(
912 &mut self,
913 address: RuntimeAddress<'ctx>,
914 size: MemoryAccessSize,
915 name_suffix: &str,
916 ) -> Result<ProbeReadResult<'ctx>> {
917 let i64_type = self.context.i64_type();
918 let ptr_type = self.context.ptr_type(AddressSpace::default());
919 let offsets_found = address.offsets_found;
920 let not_found = self
921 .builder
922 .build_not(offsets_found, "offsets_miss")
923 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
924
925 let result_size = size.bytes();
926 let scratch_buffer = self.get_probe_read_scratch_buffer(result_size, name_suffix)?;
927 let dst_ptr = self
928 .builder
929 .build_bit_cast(scratch_buffer, ptr_type, "dst_ptr")
930 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
931 let base_src_ptr = self
932 .builder
933 .build_int_to_ptr(address.value, ptr_type, "src_ptr")
934 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
935 let null_ptr = ptr_type.const_null();
936 let src_ptr = self
937 .builder
938 .build_select::<BasicValueEnum<'ctx>, _>(
939 offsets_found,
940 base_src_ptr.into(),
941 null_ptr.into(),
942 "src_or_null",
943 )
944 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
945 .into_pointer_value();
946
947 let i32_type = self.context.i32_type();
948 let helper_id = i64_type.const_int(BPF_FUNC_probe_read_user as u64, false);
949 let helper_fn_type =
950 i32_type.fn_type(&[ptr_type.into(), i32_type.into(), ptr_type.into()], false);
951 let helper_fn_ptr = self
952 .builder
953 .build_int_to_ptr(helper_id, ptr_type, "probe_read_user_fn")
954 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
955 let size_val = i32_type.const_int(result_size as u64, false);
956 let zero_i32 = i32_type.const_zero();
957 let effective_size = self
958 .builder
959 .build_select::<BasicValueEnum<'ctx>, _>(
960 offsets_found,
961 size_val.into(),
962 zero_i32.into(),
963 "size_or_zero",
964 )
965 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
966 .into_int_value();
967 let call_args: Vec<BasicMetadataValueEnum> =
968 vec![dst_ptr.into(), effective_size.into(), src_ptr.into()];
969
970 let call_site = self
971 .builder
972 .build_indirect_call(
973 helper_fn_type,
974 helper_fn_ptr,
975 &call_args,
976 "probe_read_result",
977 )
978 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
979 let ret_iv = call_site.try_as_basic_value().left().ok_or_else(|| {
980 CodeGenError::LLVMError("Expected integer return from helper".to_string())
981 })?;
982 let ret_i32 = ret_iv.into_int_value();
983 let read_fail = self
984 .builder
985 .build_int_compare(
986 inkwell::IntPredicate::NE,
987 ret_i32,
988 i32_type.const_zero(),
989 "read_fail",
990 )
991 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
992 let combined_fail = self
993 .builder
994 .build_or(read_fail, not_found, "combined_fail")
995 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
996
997 let result_type: BasicTypeEnum = match size {
998 MemoryAccessSize::U8 => self.context.i8_type().into(),
999 MemoryAccessSize::U16 => self.context.i16_type().into(),
1000 MemoryAccessSize::U32 => self.context.i32_type().into(),
1001 MemoryAccessSize::U64 => self.context.i64_type().into(),
1002 };
1003 let typed_ptr = self
1004 .builder
1005 .build_bit_cast(
1006 scratch_buffer,
1007 self.context.ptr_type(AddressSpace::default()),
1008 "typed_ptr",
1009 )
1010 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1011 let loaded_value = self
1012 .builder
1013 .build_load(result_type, typed_ptr.into_pointer_value(), "loaded_value")
1014 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1015 let loaded_i64 = if let BasicValueEnum::IntValue(int_val) = loaded_value {
1016 if int_val.get_type().get_bit_width() < 64 {
1017 self.builder
1018 .build_int_z_extend(int_val, i64_type, "extended")
1019 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
1020 } else {
1021 int_val
1022 }
1023 } else {
1024 return Err(CodeGenError::MemoryAccessError(
1025 "Expected integer value from memory read".to_string(),
1026 ));
1027 };
1028
1029 Ok(ProbeReadResult {
1030 loaded_i64,
1031 combined_fail,
1032 not_found,
1033 })
1034 }
1035
1036 fn update_any_fail_flag(
1037 &mut self,
1038 combined_fail: IntValue<'ctx>,
1039 name_suffix: &str,
1040 ) -> Result<()> {
1041 let i8_type = self.context.i8_type();
1042 let fail_i8 = self
1043 .builder
1044 .build_int_z_extend(combined_fail, i8_type, &format!("fail_i8_{name_suffix}"))
1045 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1046 let fail_ptr = self.get_or_create_flag_global("_gs_any_fail");
1047 let cur_fail = self
1048 .builder
1049 .build_load(i8_type, fail_ptr, &format!("cur_fail_{name_suffix}"))
1050 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
1051 .into_int_value();
1052 let new_fail = self
1053 .builder
1054 .build_or(cur_fail, fail_i8, &format!("fail_or_miss_{name_suffix}"))
1055 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1056 self.builder
1057 .build_store(fail_ptr, new_fail)
1058 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1059 Ok(())
1060 }
1061
1062 pub(crate) fn store_variable_read_status(
1063 &mut self,
1064 status_ptr: PointerValue<'ctx>,
1065 combined_fail: IntValue<'ctx>,
1066 not_found: IntValue<'ctx>,
1067 name_suffix: &str,
1068 ) -> Result<()> {
1069 let cur_status = self
1070 .builder
1071 .build_load(
1072 self.context.i8_type(),
1073 status_ptr,
1074 &format!("cur_status_{name_suffix}"),
1075 )
1076 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
1077 .into_int_value();
1078 let is_ok = self
1079 .builder
1080 .build_int_compare(
1081 inkwell::IntPredicate::EQ,
1082 cur_status,
1083 self.context.i8_type().const_zero(),
1084 &format!("status_is_ok_{name_suffix}"),
1085 )
1086 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1087 let desired_status = self
1088 .builder
1089 .build_select::<BasicValueEnum<'ctx>, _>(
1090 not_found,
1091 self.context
1092 .i8_type()
1093 .const_int(VariableStatus::OffsetsUnavailable as u64, false)
1094 .into(),
1095 self.context
1096 .i8_type()
1097 .const_int(VariableStatus::ReadError as u64, false)
1098 .into(),
1099 &format!("desired_read_status_{name_suffix}"),
1100 )
1101 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1102 let should_store = self
1103 .builder
1104 .build_and(
1105 is_ok,
1106 combined_fail,
1107 &format!("should_store_read_status_{name_suffix}"),
1108 )
1109 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1110 let new_status = self
1111 .builder
1112 .build_select::<BasicValueEnum<'ctx>, _>(
1113 should_store,
1114 desired_status,
1115 cur_status.into(),
1116 &format!("new_status_{name_suffix}"),
1117 )
1118 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1119 self.builder
1120 .build_store(status_ptr, new_status)
1121 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1122 Ok(())
1123 }
1124
1125 pub(crate) fn generate_memory_read(
1127 &mut self,
1128 address: RuntimeAddress<'ctx>,
1129 size: MemoryAccessSize,
1130 status_ptr: Option<PointerValue<'ctx>>,
1131 ) -> Result<BasicValueEnum<'ctx>> {
1132 let zero_const = self.context.i64_type().const_zero();
1133 let ProbeReadResult {
1134 loaded_i64,
1135 combined_fail,
1136 not_found,
1137 } = self.probe_read_user_core(address, size, "probe_read_user")?;
1138
1139 if let Some(status_ptr) = status_ptr {
1140 self.store_variable_read_status(
1141 status_ptr,
1142 combined_fail,
1143 not_found,
1144 "probe_read_user",
1145 )?;
1146 }
1147 self.update_any_fail_flag(combined_fail, "probe_read_user")?;
1148
1149 let zero_bv: BasicValueEnum = zero_const.into();
1150 let val_bv: BasicValueEnum = loaded_i64.into();
1151 self.builder
1152 .build_select::<BasicValueEnum<'ctx>, _>(
1153 combined_fail,
1154 zero_bv,
1155 val_bv,
1156 "value_or_zero",
1157 )
1158 .map_err(|e| CodeGenError::LLVMError(e.to_string()))
1159 }
1160
1161 pub(crate) fn generate_memory_read_with_fail_flag(
1163 &mut self,
1164 address: RuntimeAddress<'ctx>,
1165 size: MemoryAccessSize,
1166 name_suffix: &str,
1167 ) -> Result<(BasicValueEnum<'ctx>, IntValue<'ctx>)> {
1168 let zero_const = self.context.i64_type().const_zero();
1169 let ProbeReadResult {
1170 loaded_i64,
1171 combined_fail,
1172 ..
1173 } = self.probe_read_user_core(address, size, name_suffix)?;
1174
1175 self.update_any_fail_flag(combined_fail, name_suffix)?;
1176
1177 let zero_bv: BasicValueEnum = zero_const.into();
1178 let val_bv: BasicValueEnum = loaded_i64.into();
1179 let value = self
1180 .builder
1181 .build_select::<BasicValueEnum<'ctx>, _>(
1182 combined_fail,
1183 zero_bv,
1184 val_bv,
1185 &format!("{name_suffix}_value_or_zero"),
1186 )
1187 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1188 Ok((value, combined_fail))
1189 }
1190
1191 pub(crate) fn generate_memory_read_with_status(
1194 &mut self,
1195 address: RuntimeAddress<'ctx>,
1196 size: MemoryAccessSize,
1197 ) -> Result<BasicValueEnum<'ctx>> {
1198 let zero_const = self.context.i64_type().const_zero();
1199 let ProbeReadResult {
1200 loaded_i64,
1201 combined_fail,
1202 ..
1203 } = self.probe_read_user_core(address, size, "probe_read_user_cf")?;
1204
1205 let func = self.current_function("generate memory read with status")?;
1206 let set_block = self.context.append_basic_block(func, "set_cond_err");
1207 let cont_block = self.context.append_basic_block(func, "read_cont");
1208 self.builder
1209 .build_conditional_branch(combined_fail, set_block, cont_block)
1210 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1211 self.builder.position_at_end(set_block);
1212 let _ = self.set_condition_error_if_unset(2u8);
1213 let _ = self.set_condition_error_addr_if_unset(address.value);
1214 self.builder
1215 .build_unconditional_branch(cont_block)
1216 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1217 self.builder.position_at_end(cont_block);
1218
1219 let zero_bv: BasicValueEnum = zero_const.into();
1220 let val_bv: BasicValueEnum = loaded_i64.into();
1221 let sel_bv = self
1222 .builder
1223 .build_select::<BasicValueEnum<'ctx>, _>(combined_fail, zero_bv, val_bv, "val_or_zero")
1224 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1225
1226 self.update_any_fail_flag(combined_fail, "probe_read_user_cf")?;
1227 Ok(sel_bv)
1228 }
1229 pub fn dwarf_reg_to_pt_regs_offset(&self, dwarf_reg: u16) -> Result<usize> {
1231 let byte_offset = register_mapping::dwarf_reg_to_pt_regs_byte_offset(dwarf_reg)
1233 .ok_or_else(|| match register_mapping::dwarf_reg_to_name(dwarf_reg) {
1234 Some(reg_name) if reg_name.starts_with("XMM") => {
1235 CodeGenError::RegisterMappingError(format!(
1236 "Unsupported DWARF register: {dwarf_reg} ({reg_name}) is a SIMD/FP register; uprobe pt_regs does not expose XMM register values, so optimized float by-value parameters are unavailable unless the compiler spills them to memory"
1237 ))
1238 }
1239 Some(reg_name) => CodeGenError::RegisterMappingError(format!(
1240 "Unsupported DWARF register: {dwarf_reg} ({reg_name})"
1241 )),
1242 None => CodeGenError::RegisterMappingError(format!(
1243 "Unsupported DWARF register: {dwarf_reg}"
1244 )),
1245 })?;
1246
1247 let u64_index = byte_offset / core::mem::size_of::<u64>();
1249 Ok(u64_index)
1250 }
1251
1252 pub fn create_bpf_helper_call(
1255 &mut self,
1256 helper_id: u64,
1257 args: &[BasicValueEnum<'ctx>],
1258 return_type: BasicTypeEnum<'ctx>,
1259 call_name: &str,
1260 ) -> Result<BasicValueEnum<'ctx>> {
1261 use inkwell::types::BasicMetadataTypeEnum;
1262
1263 let arg_types: Vec<BasicMetadataTypeEnum> =
1265 args.iter().map(|arg| arg.get_type().into()).collect();
1266 let fn_type = return_type.fn_type(&arg_types, false);
1267
1268 let i64_type = self.context.i64_type();
1270 let ptr_type = self.context.ptr_type(AddressSpace::default());
1271
1272 let helper_id_val = i64_type.const_int(helper_id, false);
1273 let helper_fn_ptr = self
1274 .builder
1275 .build_int_to_ptr(helper_id_val, ptr_type, "helper_fn")
1276 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1277
1278 let metadata_args: Vec<BasicMetadataValueEnum> =
1280 args.iter().map(|arg| (*arg).into()).collect();
1281
1282 let call_result = self
1284 .builder
1285 .build_indirect_call(fn_type, helper_fn_ptr, &metadata_args, call_name)
1286 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1287
1288 Ok(call_result.try_as_basic_value().left().unwrap_or_else(|| {
1290 return_type.const_zero()
1292 }))
1293 }
1294
1295 pub fn get_current_timestamp(&mut self) -> Result<IntValue<'ctx>> {
1297 let i64_type = self.context.i64_type();
1298
1299 let timestamp = self.create_bpf_helper_call(
1301 BPF_FUNC_ktime_get_ns as u64,
1302 &[],
1303 i64_type.into(),
1304 "timestamp",
1305 )?;
1306
1307 if let BasicValueEnum::IntValue(int_val) = timestamp {
1308 Ok(int_val)
1309 } else {
1310 Err(CodeGenError::LLVMError(
1311 "bpf_ktime_get_ns did not return integer".to_string(),
1312 ))
1313 }
1314 }
1315
1316 pub fn get_current_pid_tgid(&mut self) -> Result<IntValue<'ctx>> {
1318 let i64_type = self.context.i64_type();
1319
1320 let pid_tgid = self.create_bpf_helper_call(
1322 BPF_FUNC_get_current_pid_tgid as u64,
1323 &[],
1324 i64_type.into(),
1325 "pid_tgid",
1326 )?;
1327
1328 if let BasicValueEnum::IntValue(int_val) = pid_tgid {
1329 Ok(int_val)
1330 } else {
1331 Err(CodeGenError::LLVMError(
1332 "bpf_get_current_pid_tgid did not return integer".to_string(),
1333 ))
1334 }
1335 }
1336
1337 pub(crate) fn generate_static_tls_address(
1338 &mut self,
1339 tls_offset: RuntimeAddress<'ctx>,
1340 module_hint: Option<&str>,
1341 ) -> Result<RuntimeAddress<'ctx>> {
1342 let module_path = match module_hint {
1343 Some(module_path) => module_path.to_string(),
1344 None => self.get_compile_time_context()?.module_path.clone(),
1345 };
1346 if ghostscope_process::is_shared_object(std::path::Path::new(&module_path)) {
1347 return Err(CodeGenError::NotImplemented(format!(
1348 "dynamic/shared-library TLS is not supported yet for DW_OP_form_tls_address in {module_path}; only x86_64 executable static TLS is currently supported"
1349 )));
1350 }
1351 let tls_bias =
1352 ghostscope_platform::static_tls_bias_for_elf(std::path::Path::new(&module_path))
1353 .map_err(|err| {
1354 CodeGenError::LLVMError(format!(
1355 "failed to read static TLS layout for {module_path}: {err}"
1356 ))
1357 })?
1358 .ok_or_else(|| {
1359 CodeGenError::LLVMError(format!(
1360 "module {module_path} does not contain a PT_TLS segment"
1361 ))
1362 })?;
1363 let fsbase_offset = ghostscope_platform::current_task_fsbase_offset().map_err(|err| {
1364 CodeGenError::LLVMError(format!(
1365 "failed to resolve task_struct.thread.fsbase offset from kernel BTF: {err}"
1366 ))
1367 })?;
1368
1369 let i64_type = self.context.i64_type();
1370 let i32_type = self.context.i32_type();
1371 let ptr_type = self.context.ptr_type(AddressSpace::default());
1372 let task_ptr_value = self.create_bpf_helper_call(
1373 BPF_FUNC_get_current_task as u64,
1374 &[],
1375 i64_type.into(),
1376 "current_task",
1377 )?;
1378 let BasicValueEnum::IntValue(task_ptr_int) = task_ptr_value else {
1379 return Err(CodeGenError::LLVMError(
1380 "bpf_get_current_task did not return integer".to_string(),
1381 ));
1382 };
1383 let fsbase_field_addr = self
1384 .builder
1385 .build_int_add(
1386 task_ptr_int,
1387 i64_type.const_int(fsbase_offset, false),
1388 "task_fsbase_addr",
1389 )
1390 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1391 let src_ptr = self
1392 .builder
1393 .build_int_to_ptr(fsbase_field_addr, ptr_type, "task_fsbase_ptr")
1394 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1395 let scratch_buffer = self.get_or_create_tls_scratch_buffer()?;
1396 let dst_ptr = self
1397 .builder
1398 .build_bit_cast(scratch_buffer, ptr_type, "tls_fsbase_dst")
1399 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1400
1401 let helper_id = i64_type.const_int(BPF_FUNC_probe_read_kernel as u64, false);
1402 let helper_fn_type =
1403 i32_type.fn_type(&[ptr_type.into(), i32_type.into(), ptr_type.into()], false);
1404 let helper_fn_ptr = self
1405 .builder
1406 .build_int_to_ptr(helper_id, ptr_type, "probe_read_kernel_fn")
1407 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1408 let call_args: Vec<BasicMetadataValueEnum> = vec![
1409 dst_ptr.into(),
1410 i32_type.const_int(8, false).into(),
1411 src_ptr.into(),
1412 ];
1413 let call_site = self
1414 .builder
1415 .build_indirect_call(
1416 helper_fn_type,
1417 helper_fn_ptr,
1418 &call_args,
1419 "probe_read_kernel_result",
1420 )
1421 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1422 let ret_i32 = call_site
1423 .try_as_basic_value()
1424 .left()
1425 .ok_or_else(|| {
1426 CodeGenError::LLVMError("Expected integer return from helper".to_string())
1427 })?
1428 .into_int_value();
1429 let read_ok = self
1430 .builder
1431 .build_int_compare(
1432 inkwell::IntPredicate::EQ,
1433 ret_i32,
1434 i32_type.const_zero(),
1435 "tls_fsbase_read_ok",
1436 )
1437 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1438 let read_fail = self
1439 .builder
1440 .build_not(read_ok, "tls_fsbase_read_fail")
1441 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1442 self.update_any_fail_flag(read_fail, "tls_fsbase")?;
1443
1444 let typed_ptr = self
1445 .builder
1446 .build_bit_cast(scratch_buffer, ptr_type, "tls_fsbase_typed_ptr")
1447 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
1448 .into_pointer_value();
1449 let fsbase = self
1450 .builder
1451 .build_load(i64_type, typed_ptr, "tls_fsbase")
1452 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
1453 .into_int_value();
1454 let tls_base = self
1455 .builder
1456 .build_int_add(
1457 fsbase,
1458 i64_type.const_int(tls_bias as u64, true),
1459 "tls_static_base",
1460 )
1461 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1462 let tls_addr = self
1463 .builder
1464 .build_int_add(tls_base, tls_offset.value, "tls_addr")
1465 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1466 let offsets_found = self
1467 .builder
1468 .build_and(tls_offset.offsets_found, read_ok, "tls_addr_available")
1469 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1470 let value = self
1471 .builder
1472 .build_select::<BasicValueEnum<'ctx>, _>(
1473 offsets_found,
1474 tls_addr.into(),
1475 i64_type.const_zero().into(),
1476 "tls_addr_or_zero",
1477 )
1478 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
1479 .into_int_value();
1480
1481 Ok(RuntimeAddress::with_offsets_found(value, offsets_found))
1482 }
1483
1484 pub fn create_event_output(&mut self, data: PointerValue<'ctx>, size: u64) -> Result<()> {
1487 match self.compile_options.event_map_type {
1488 crate::EventMapType::RingBuf => self.create_ringbuf_output_internal(data, size),
1489 crate::EventMapType::PerfEventArray => {
1490 self.create_perf_event_output_internal(data, size)
1491 }
1492 }
1493 }
1494
1495 fn increment_event_loss_counter(&mut self) -> Result<()> {
1496 let i64_type = self.context.i64_type();
1497
1498 let counter_ptr = self.lookup_percpu_value_ptr("event_loss_counters", 0)?;
1499 let current_fn = self
1500 .builder
1501 .get_insert_block()
1502 .and_then(|block| block.get_parent())
1503 .ok_or_else(|| {
1504 CodeGenError::LLVMError(
1505 "Cannot increment event loss counter outside a function".to_string(),
1506 )
1507 })?;
1508 let counter_hit_block = self
1509 .context
1510 .append_basic_block(current_fn, "event_loss_counter_hit");
1511 let counter_done_block = self
1512 .context
1513 .append_basic_block(current_fn, "event_loss_counter_done");
1514 let is_null = self
1515 .builder
1516 .build_is_null(counter_ptr, "event_loss_counter_is_null")
1517 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1518 self.builder
1519 .build_conditional_branch(is_null, counter_done_block, counter_hit_block)
1520 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1521
1522 self.builder.position_at_end(counter_hit_block);
1523 let current = self
1524 .builder
1525 .build_load(i64_type, counter_ptr, "event_loss_counter")
1526 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
1527 .into_int_value();
1528 let next = self
1529 .builder
1530 .build_int_add(
1531 current,
1532 i64_type.const_int(1, false),
1533 "event_loss_counter_next",
1534 )
1535 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1536 self.builder
1537 .build_store(counter_ptr, next)
1538 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1539 self.builder
1540 .build_unconditional_branch(counter_done_block)
1541 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1542
1543 self.builder.position_at_end(counter_done_block);
1544 Ok(())
1545 }
1546
1547 fn record_event_output_loss_on_error(&mut self, output_result: IntValue<'ctx>) -> Result<()> {
1548 let i64_type = self.context.i64_type();
1549 let current_fn = self
1550 .builder
1551 .get_insert_block()
1552 .and_then(|block| block.get_parent())
1553 .ok_or_else(|| {
1554 CodeGenError::LLVMError(
1555 "Cannot record event output loss outside a function".to_string(),
1556 )
1557 })?;
1558 let output_failed = self
1559 .builder
1560 .build_int_compare(
1561 inkwell::IntPredicate::SLT,
1562 output_result,
1563 i64_type.const_zero(),
1564 "event_output_failed",
1565 )
1566 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1567 let loss_block = self
1568 .context
1569 .append_basic_block(current_fn, "event_output_loss");
1570 let cont_block = self
1571 .context
1572 .append_basic_block(current_fn, "event_output_after_loss_check");
1573 self.builder
1574 .build_conditional_branch(output_failed, loss_block, cont_block)
1575 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1576
1577 self.builder.position_at_end(loss_block);
1578 self.increment_event_loss_counter()?;
1579 self.builder
1580 .build_unconditional_branch(cont_block)
1581 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1582
1583 self.builder.position_at_end(cont_block);
1584 Ok(())
1585 }
1586
1587 fn create_ringbuf_output_internal(
1589 &mut self,
1590 data: PointerValue<'ctx>,
1591 size: u64,
1592 ) -> Result<()> {
1593 let i64_type = self.context.i64_type();
1594
1595 let ringbuf_global = self
1597 .map_manager
1598 .get_ringbuf_map(&self.module, "ringbuf")
1599 .map_err(|e| {
1600 CodeGenError::MemoryAccessError(format!("Failed to get ringbuf map: {e}"))
1601 })?;
1602
1603 let args = [
1605 ringbuf_global.into(),
1606 data.into(),
1607 i64_type.const_int(size, false).into(),
1608 i64_type.const_zero().into(), ];
1610
1611 let result = self.create_bpf_helper_call(
1612 BPF_FUNC_ringbuf_output as u64,
1613 &args,
1614 i64_type.into(),
1615 "ringbuf_output",
1616 )?;
1617 let BasicValueEnum::IntValue(result) = result else {
1618 return Err(CodeGenError::LLVMError(
1619 "bpf_ringbuf_output did not return integer".to_string(),
1620 ));
1621 };
1622 self.record_event_output_loss_on_error(result)?;
1623
1624 Ok(())
1625 }
1626
1627 pub fn create_ringbuf_output_dynamic(
1629 &mut self,
1630 data: PointerValue<'ctx>,
1631 size: IntValue<'ctx>,
1632 ) -> Result<()> {
1633 let i64_type = self.context.i64_type();
1634
1635 let ringbuf_global = self
1637 .map_manager
1638 .get_ringbuf_map(&self.module, "ringbuf")
1639 .map_err(|e| {
1640 CodeGenError::MemoryAccessError(format!("Failed to get ringbuf map: {e}"))
1641 })?;
1642
1643 let args = [
1645 ringbuf_global.into(),
1646 data.into(),
1647 size.into(),
1648 i64_type.const_zero().into(), ];
1650
1651 let result = self.create_bpf_helper_call(
1652 BPF_FUNC_ringbuf_output as u64,
1653 &args,
1654 i64_type.into(),
1655 "ringbuf_output",
1656 )?;
1657 let BasicValueEnum::IntValue(result) = result else {
1658 return Err(CodeGenError::LLVMError(
1659 "bpf_ringbuf_output did not return integer".to_string(),
1660 ));
1661 };
1662 self.record_event_output_loss_on_error(result)?;
1663
1664 Ok(())
1665 }
1666
1667 pub fn lookup_percpu_value_ptr(
1669 &mut self,
1670 map_name: &str,
1671 key_const: u32,
1672 ) -> Result<PointerValue<'ctx>> {
1673 let ptr_ty = self.context.ptr_type(AddressSpace::default());
1674 let i32_ty = self.context.i32_type();
1675 let map_global = self
1676 .map_manager
1677 .get_map(&self.module, map_name)
1678 .map_err(|e| CodeGenError::LLVMError(format!("Map not found {map_name}: {e}")))?;
1679 let map_ptr = self
1680 .builder
1681 .build_bit_cast(map_global, ptr_ty, "map_ptr")
1682 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1683
1684 let key_arr_ty = i32_ty.array_type(4);
1686 let key_alloca = self.pm_key_alloca.ok_or_else(|| {
1687 CodeGenError::LLVMError("pm_key not allocated in entry block".to_string())
1688 })?;
1689 let zero = i32_ty.const_zero();
1690 let base_i32_ptr = unsafe {
1693 self.builder
1694 .build_gep(key_arr_ty, key_alloca, &[zero, zero], "percpu_key_i32_ptr")
1695 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
1696 };
1697 self.builder
1698 .build_store(base_i32_ptr, i32_ty.const_int(key_const as u64, false))
1699 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1700 let key_ptr = self
1701 .builder
1702 .build_bit_cast(base_i32_ptr, ptr_ty, "key_ptr")
1703 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1704
1705 let ret = self.create_bpf_helper_call(
1707 BPF_FUNC_map_lookup_elem as u64,
1708 &[map_ptr, key_ptr],
1709 ptr_ty.into(),
1710 "map_lookup_elem",
1711 )?;
1712 let val_ptr = if let BasicValueEnum::PointerValue(p) = ret {
1713 p
1714 } else {
1715 return Err(CodeGenError::LLVMError(
1716 "map_lookup_elem did not return pointer".to_string(),
1717 ));
1718 };
1719 Ok(val_ptr)
1720 }
1721
1722 fn create_perf_event_output_internal(
1724 &mut self,
1725 data: PointerValue<'ctx>,
1726 size: u64,
1727 ) -> Result<()> {
1728 let size_val = self.context.i64_type().const_int(size, false);
1729 self.create_perf_event_output_dynamic(data, size_val)
1730 }
1731
1732 pub fn create_perf_event_output_dynamic(
1734 &mut self,
1735 data: PointerValue<'ctx>,
1736 size: IntValue<'ctx>,
1737 ) -> Result<()> {
1738 let i64_type = self.context.i64_type();
1739
1740 let ctx_param = self
1742 .builder
1743 .get_insert_block()
1744 .and_then(|bb| bb.get_parent())
1745 .and_then(|func| func.get_first_param())
1746 .ok_or_else(|| {
1747 CodeGenError::LLVMError("Failed to get context parameter".to_string())
1748 })?;
1749
1750 let events_global = self
1752 .map_manager
1753 .get_perf_map(&self.module, "events")
1754 .map_err(|e| {
1755 CodeGenError::MemoryAccessError(format!("Failed to get perf event map: {e}"))
1756 })?;
1757
1758 let args = [
1761 ctx_param,
1762 events_global.into(),
1763 i64_type.const_int(0xFFFFFFFF_u64, false).into(), data.into(),
1765 size.into(),
1766 ];
1767
1768 let result = self.create_bpf_helper_call(
1769 BPF_FUNC_perf_event_output as u64,
1770 &args,
1771 i64_type.into(),
1772 "perf_event_output",
1773 )?;
1774 let BasicValueEnum::IntValue(result) = result else {
1775 return Err(CodeGenError::LLVMError(
1776 "bpf_perf_event_output did not return integer".to_string(),
1777 ));
1778 };
1779 self.record_event_output_loss_on_error(result)?;
1780
1781 Ok(())
1782 }
1783}