1use aya_ebpf_bindings::bindings::bpf_map_type;
2use inkwell::context::Context;
3use inkwell::debug_info::{AsDIScope, DebugInfoBuilder};
4use inkwell::module::Linkage;
5use inkwell::module::Module;
6use inkwell::values::PointerValue;
7use inkwell::AddressSpace;
8use std::collections::HashMap;
10use tracing::{error, info};
11
12#[derive(Debug, Clone, Copy)]
13pub enum BpfMapType {
14 Ringbuf,
15 Array,
16 PerCpuArray,
17 Hash,
18 PerfEventArray,
19}
20
21impl BpfMapType {
22 fn to_aya_map_type(self) -> u32 {
23 match self {
24 BpfMapType::Ringbuf => bpf_map_type::BPF_MAP_TYPE_RINGBUF,
25 BpfMapType::PerCpuArray => bpf_map_type::BPF_MAP_TYPE_PERCPU_ARRAY,
26 BpfMapType::Array => bpf_map_type::BPF_MAP_TYPE_ARRAY,
27 BpfMapType::Hash => bpf_map_type::BPF_MAP_TYPE_HASH,
28 BpfMapType::PerfEventArray => bpf_map_type::BPF_MAP_TYPE_PERF_EVENT_ARRAY,
29 }
30 }
31}
32
33#[derive(Debug, Clone)]
34pub struct SizedType {
35 pub size: u64, pub is_none: bool,
37}
38
39impl SizedType {
40 pub fn none() -> Self {
41 SizedType {
42 size: 0,
43 is_none: true,
44 }
45 }
46
47 pub fn integer(size: u64) -> Self {
48 SizedType {
49 size,
50 is_none: false,
51 }
52 }
53}
54
55pub struct MapManager<'ctx> {
56 context: &'ctx Context,
57 map_types: HashMap<String, BpfMapType>,
58}
59
60#[derive(Debug, thiserror::Error)]
61pub enum MapError {
62 #[error("Map not found: {0}")]
63 MapNotFound(String),
64
65 #[error("Builder error: {0}")]
66 Builder(String),
67
68 #[error("Debug info error: {0}")]
69 DebugInfo(String),
70}
71
72impl From<&str> for MapError {
73 fn from(err: &str) -> Self {
74 MapError::DebugInfo(err.to_string())
75 }
76}
77
78pub type Result<T> = std::result::Result<T, MapError>;
79
80impl<'ctx> MapManager<'ctx> {
81 pub fn new(context: &'ctx Context) -> Self {
82 MapManager {
83 context,
84 map_types: HashMap::new(),
85 }
86 }
87
88 #[allow(clippy::too_many_arguments)]
89 pub fn create_map_definition(
90 &mut self,
91 module: &Module<'ctx>,
92 di_builder: &DebugInfoBuilder<'ctx>,
93 compile_unit: &inkwell::debug_info::DICompileUnit<'ctx>,
94 name: &str,
95 map_type: BpfMapType,
96 max_entries: u64,
97 key_type: SizedType,
98 value_type: SizedType,
99 ) -> Result<()> {
100 info!(
101 "Creating map definition: {} (type: {:?}, max_entries: {}, key_type: {:?}, value_type: {:?})",
102 name, map_type, max_entries, key_type, value_type
103 );
104
105 self.map_types.insert(name.to_string(), map_type);
107
108 let var_name = name.to_string();
110 info!("Map variable name: {}", var_name);
111
112 let ptr_ty = self.context.ptr_type(inkwell::AddressSpace::default());
116
117 let (elements, initializer_values): (Vec<_>, Vec<_>) = match map_type {
122 BpfMapType::Ringbuf => (
123 vec![ptr_ty.into(), ptr_ty.into()],
124 vec![ptr_ty.const_null().into(), ptr_ty.const_null().into()],
125 ),
126 _ => (
127 vec![ptr_ty.into(), ptr_ty.into(), ptr_ty.into(), ptr_ty.into()],
128 vec![
129 ptr_ty.const_null().into(),
130 ptr_ty.const_null().into(),
131 ptr_ty.const_null().into(),
132 ptr_ty.const_null().into(),
133 ],
134 ),
135 };
136 let struct_type = self.context.struct_type(&elements, false);
137 let initializer = struct_type.const_named_struct(&initializer_values);
138
139 let map_di_type = self.create_map_btf_info(
142 di_builder,
143 compile_unit,
144 &var_name,
145 map_type,
146 max_entries,
147 key_type,
148 value_type,
149 )?;
150
151 let map_var = module.add_global(struct_type, None, &var_name);
153
154 map_var.set_initializer(&initializer);
156
157 map_var.set_section(Some(".maps"));
159
160 map_var.set_linkage(Linkage::External);
163
164 let file = compile_unit.get_file();
167 let di_global_variable = di_builder.create_global_variable_expression(
168 compile_unit.as_debug_info_scope(), &var_name, &var_name, file, 1, map_di_type, false, None, None, map_var.get_alignment(), );
179
180 map_var.set_metadata(di_global_variable.as_metadata_value(self.context), 0);
183
184 let field_count = match map_type {
185 BpfMapType::Ringbuf => 2,
186 _ => 4,
187 };
188 info!(
189 "Successfully created map: {} with {} fields",
190 var_name, field_count
191 );
192 Ok(())
193 }
194
195 pub fn get_map(&self, module: &Module<'ctx>, name: &str) -> Result<PointerValue<'ctx>> {
196 let var_name = name.to_string(); info!("Looking up map: {}", var_name);
198
199 if let Some(map_var) = module.get_global(&var_name) {
200 info!("Found map: {}", var_name);
201 Ok(map_var.as_pointer_value())
202 } else {
203 error!("Map not found: {}", var_name);
204 Err(MapError::MapNotFound(var_name))
205 }
206 }
207
208 pub fn create_ringbuf_map(
209 &mut self,
210 module: &Module<'ctx>,
211 di_builder: &DebugInfoBuilder<'ctx>,
212 compile_unit: &inkwell::debug_info::DICompileUnit<'ctx>,
213 name: &str,
214 ringbuf_size: u64,
215 ) -> Result<()> {
216 let max_entries = ringbuf_size;
220 info!("Creating ringbuf map: {} with {} bytes", name, max_entries);
221 self.create_map_definition(
222 module,
223 di_builder,
224 compile_unit,
225 name,
226 BpfMapType::Ringbuf,
227 max_entries,
228 SizedType::none(),
230 SizedType::none(),
231 )
232 }
233
234 pub fn create_perf_event_array_map(
236 &mut self,
237 module: &Module<'ctx>,
238 di_builder: &DebugInfoBuilder<'ctx>,
239 compile_unit: &inkwell::debug_info::DICompileUnit<'ctx>,
240 name: &str,
241 ) -> Result<()> {
242 info!("Creating PerfEventArray map: {}", name);
243 self.create_map_definition(
244 module,
245 di_builder,
246 compile_unit,
247 name,
248 BpfMapType::PerfEventArray,
249 0, SizedType::integer(32),
252 SizedType::integer(32),
253 )
254 }
255
256 pub fn create_proc_module_offsets_map(
258 &mut self,
259 module: &Module<'ctx>,
260 di_builder: &DebugInfoBuilder<'ctx>,
261 compile_unit: &inkwell::debug_info::DICompileUnit<'ctx>,
262 name: &str,
263 max_entries: u64,
264 ) -> Result<()> {
265 self.create_map_definition(
268 module,
269 di_builder,
270 compile_unit,
271 name,
272 BpfMapType::Hash,
273 max_entries,
274 SizedType::integer(128),
275 SizedType::integer(256),
276 )
277 }
278
279 pub fn create_pid_aliases_map(
280 &mut self,
281 module: &Module<'ctx>,
282 di_builder: &DebugInfoBuilder<'ctx>,
283 compile_unit: &inkwell::debug_info::DICompileUnit<'ctx>,
284 name: &str,
285 max_entries: u64,
286 ) -> Result<()> {
287 self.create_map_definition(
288 module,
289 di_builder,
290 compile_unit,
291 name,
292 BpfMapType::Hash,
293 max_entries,
294 SizedType::integer(32),
295 SizedType::integer(32),
296 )
297 }
298
299 pub fn create_event_loss_counter_map(
300 &mut self,
301 module: &Module<'ctx>,
302 di_builder: &DebugInfoBuilder<'ctx>,
303 compile_unit: &inkwell::debug_info::DICompileUnit<'ctx>,
304 name: &str,
305 max_entries: u64,
306 ) -> Result<()> {
307 info!(
308 "Creating event loss counter map: {} with {} max entries",
309 name, max_entries
310 );
311 self.create_map_definition(
315 module,
316 di_builder,
317 compile_unit,
318 name,
319 BpfMapType::Array,
320 max_entries,
321 SizedType::integer(64), SizedType::integer(64), )
324 }
325
326 #[allow(clippy::too_many_arguments)]
329 fn create_map_btf_info(
330 &self,
331 di_builder: &DebugInfoBuilder<'ctx>,
332 compile_unit: &inkwell::debug_info::DICompileUnit<'ctx>,
333 map_name: &str,
334 map_type: BpfMapType,
335 max_entries: u64,
336 key_type: SizedType,
337 value_type: SizedType,
338 ) -> Result<inkwell::debug_info::DIType<'ctx>> {
339 info!(
340 "Creating BTF info for map: {} (type: {:?})",
341 map_name, map_type
342 );
343
344 let i32_type = di_builder.create_basic_type("int", 32, 0x05, 0)?; let file = compile_unit.get_file();
348 let scope = compile_unit.as_debug_info_scope();
349
350 let map_type_id = map_type.to_aya_map_type();
353
354 let mk_ptr_to_array = |name: &str, nr_elems: i64| {
356 let range = 0..nr_elems;
357 let arr = di_builder.create_array_type(
358 i32_type.as_type(),
359 64,
360 32,
361 std::slice::from_ref(&range),
362 );
363 di_builder.create_pointer_type(name, arr.as_type(), 64, 64, AddressSpace::default())
364 };
365
366 let type_ptr = mk_ptr_to_array("type", map_type_id as i64);
367
368 let members = match map_type {
369 BpfMapType::Ringbuf => {
370 info!("Creating ringbuf BTF with 2 fields (type, max_entries) as pointer-to-array");
371 let max_entries_ptr = mk_ptr_to_array("max_entries", max_entries as i64);
372 vec![
373 di_builder.create_member_type(
374 scope,
375 "type",
376 file,
377 0,
378 64,
379 64,
380 0,
381 0,
382 type_ptr.as_type(),
383 ),
384 di_builder.create_member_type(
385 scope,
386 "max_entries",
387 file,
388 0,
389 64,
390 64,
391 64,
392 0,
393 max_entries_ptr.as_type(),
394 ),
395 ]
396 }
397 _ => {
398 info!("Creating array/hash BTF with pointer-to-array fields for aya compatibility");
399 let key_size_val = if key_type.is_none {
400 0
401 } else {
402 (key_type.size / 8) as i64
403 };
404 let value_size_val = if value_type.is_none {
405 0
406 } else {
407 (value_type.size / 8) as i64
408 };
409 let key_size_ptr = mk_ptr_to_array("key_size", key_size_val);
410 let value_size_ptr = mk_ptr_to_array("value_size", value_size_val);
411 let max_entries_ptr = mk_ptr_to_array("max_entries", max_entries as i64);
412 let mut v = vec![
413 di_builder.create_member_type(
414 scope,
415 "type",
416 file,
417 0,
418 64,
419 64,
420 0,
421 0,
422 type_ptr.as_type(),
423 ),
424 di_builder.create_member_type(
425 scope,
426 "key_size",
427 file,
428 0,
429 64,
430 64,
431 64,
432 0,
433 key_size_ptr.as_type(),
434 ),
435 di_builder.create_member_type(
436 scope,
437 "value_size",
438 file,
439 0,
440 64,
441 64,
442 128,
443 0,
444 value_size_ptr.as_type(),
445 ),
446 di_builder.create_member_type(
447 scope,
448 "max_entries",
449 file,
450 0,
451 64,
452 64,
453 192,
454 0,
455 max_entries_ptr.as_type(),
456 ),
457 ];
458 if matches!(map_name, "proc_module_offsets" | "pid_aliases") {
460 let pinning_ptr = mk_ptr_to_array("pinning", 1);
462 v.push(di_builder.create_member_type(
463 scope,
464 "pinning",
465 file,
466 0,
467 64,
468 64,
469 256,
470 0,
471 pinning_ptr.as_type(),
472 ));
473 }
474 v
475 }
476 };
477
478 let member_types: Vec<_> = members.iter().map(|m| m.as_type()).collect();
480
481 let (total_size_bits, field_count) = match map_type {
483 BpfMapType::Ringbuf => (128, 2), _ => {
485 if matches!(map_name, "proc_module_offsets" | "pid_aliases") {
486 (320, 5) } else {
488 (256, 4)
489 }
490 }
491 };
492
493 let map_struct_type = di_builder.create_struct_type(
495 scope, "", file, 0, total_size_bits, 32, 0, None, &member_types, 0, None, "", );
508
509 info!(
510 "Created BTF struct type for map: {} with {} fields, {} total bits",
511 map_name, field_count, total_size_bits
512 );
513 Ok(map_struct_type.as_type())
514 }
515
516 pub fn get_ringbuf_map(&self, module: &Module<'ctx>, name: &str) -> Result<PointerValue<'ctx>> {
518 self.get_map(module, name)
519 }
520
521 pub fn get_perf_map(&self, module: &Module<'ctx>, name: &str) -> Result<PointerValue<'ctx>> {
523 self.get_map(module, name)
524 }
525
526 pub fn create_percpu_array_map(
528 &mut self,
529 module: &Module<'ctx>,
530 di_builder: &DebugInfoBuilder<'ctx>,
531 compile_unit: &inkwell::debug_info::DICompileUnit<'ctx>,
532 name: &str,
533 max_entries: u64,
534 value_size_bytes: u64,
535 ) -> Result<()> {
536 self.create_map_definition(
537 module,
538 di_builder,
539 compile_unit,
540 name,
541 BpfMapType::PerCpuArray,
542 max_entries,
543 SizedType::integer(32),
544 SizedType::integer(value_size_bytes * 8),
545 )
546 }
547}