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_event_loss_counter_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 info!(
288 "Creating event loss counter map: {} with {} max entries",
289 name, max_entries
290 );
291 self.create_map_definition(
295 module,
296 di_builder,
297 compile_unit,
298 name,
299 BpfMapType::Array,
300 max_entries,
301 SizedType::integer(64), SizedType::integer(64), )
304 }
305
306 #[allow(clippy::too_many_arguments)]
309 fn create_map_btf_info(
310 &self,
311 di_builder: &DebugInfoBuilder<'ctx>,
312 compile_unit: &inkwell::debug_info::DICompileUnit<'ctx>,
313 map_name: &str,
314 map_type: BpfMapType,
315 max_entries: u64,
316 key_type: SizedType,
317 value_type: SizedType,
318 ) -> Result<inkwell::debug_info::DIType<'ctx>> {
319 info!(
320 "Creating BTF info for map: {} (type: {:?})",
321 map_name, map_type
322 );
323
324 let i32_type = di_builder.create_basic_type("int", 32, 0x05, 0)?; let file = compile_unit.get_file();
328 let scope = compile_unit.as_debug_info_scope();
329
330 let map_type_id = map_type.to_aya_map_type();
333
334 let mk_ptr_to_array = |name: &str, nr_elems: i64| {
336 let range = 0..nr_elems;
337 let arr = di_builder.create_array_type(
338 i32_type.as_type(),
339 64,
340 32,
341 std::slice::from_ref(&range),
342 );
343 di_builder.create_pointer_type(name, arr.as_type(), 64, 64, AddressSpace::default())
344 };
345
346 let type_ptr = mk_ptr_to_array("type", map_type_id as i64);
347
348 let members = match map_type {
349 BpfMapType::Ringbuf => {
350 info!("Creating ringbuf BTF with 2 fields (type, max_entries) as pointer-to-array");
351 let max_entries_ptr = mk_ptr_to_array("max_entries", max_entries as i64);
352 vec![
353 di_builder.create_member_type(
354 scope,
355 "type",
356 file,
357 0,
358 64,
359 64,
360 0,
361 0,
362 type_ptr.as_type(),
363 ),
364 di_builder.create_member_type(
365 scope,
366 "max_entries",
367 file,
368 0,
369 64,
370 64,
371 64,
372 0,
373 max_entries_ptr.as_type(),
374 ),
375 ]
376 }
377 _ => {
378 info!("Creating array/hash BTF with pointer-to-array fields for aya compatibility");
379 let key_size_val = if key_type.is_none {
380 0
381 } else {
382 (key_type.size / 8) as i64
383 };
384 let value_size_val = if value_type.is_none {
385 0
386 } else {
387 (value_type.size / 8) as i64
388 };
389 let key_size_ptr = mk_ptr_to_array("key_size", key_size_val);
390 let value_size_ptr = mk_ptr_to_array("value_size", value_size_val);
391 let max_entries_ptr = mk_ptr_to_array("max_entries", max_entries as i64);
392 let mut v = vec![
393 di_builder.create_member_type(
394 scope,
395 "type",
396 file,
397 0,
398 64,
399 64,
400 0,
401 0,
402 type_ptr.as_type(),
403 ),
404 di_builder.create_member_type(
405 scope,
406 "key_size",
407 file,
408 0,
409 64,
410 64,
411 64,
412 0,
413 key_size_ptr.as_type(),
414 ),
415 di_builder.create_member_type(
416 scope,
417 "value_size",
418 file,
419 0,
420 64,
421 64,
422 128,
423 0,
424 value_size_ptr.as_type(),
425 ),
426 di_builder.create_member_type(
427 scope,
428 "max_entries",
429 file,
430 0,
431 64,
432 64,
433 192,
434 0,
435 max_entries_ptr.as_type(),
436 ),
437 ];
438 if map_name == "proc_module_offsets" {
440 let pinning_ptr = mk_ptr_to_array("pinning", 1);
442 v.push(di_builder.create_member_type(
443 scope,
444 "pinning",
445 file,
446 0,
447 64,
448 64,
449 256,
450 0,
451 pinning_ptr.as_type(),
452 ));
453 }
454 v
455 }
456 };
457
458 let member_types: Vec<_> = members.iter().map(|m| m.as_type()).collect();
460
461 let (total_size_bits, field_count) = match map_type {
463 BpfMapType::Ringbuf => (128, 2), _ => {
465 if map_name == "proc_module_offsets" {
466 (320, 5) } else {
468 (256, 4)
469 }
470 }
471 };
472
473 let map_struct_type = di_builder.create_struct_type(
475 scope, "", file, 0, total_size_bits, 32, 0, None, &member_types, 0, None, "", );
488
489 info!(
490 "Created BTF struct type for map: {} with {} fields, {} total bits",
491 map_name, field_count, total_size_bits
492 );
493 Ok(map_struct_type.as_type())
494 }
495
496 pub fn get_ringbuf_map(&self, module: &Module<'ctx>, name: &str) -> Result<PointerValue<'ctx>> {
498 self.get_map(module, name)
499 }
500
501 pub fn get_perf_map(&self, module: &Module<'ctx>, name: &str) -> Result<PointerValue<'ctx>> {
503 self.get_map(module, name)
504 }
505
506 pub fn create_percpu_array_map(
508 &mut self,
509 module: &Module<'ctx>,
510 di_builder: &DebugInfoBuilder<'ctx>,
511 compile_unit: &inkwell::debug_info::DICompileUnit<'ctx>,
512 name: &str,
513 max_entries: u64,
514 value_size_bytes: u64,
515 ) -> Result<()> {
516 self.create_map_definition(
517 module,
518 di_builder,
519 compile_unit,
520 name,
521 BpfMapType::PerCpuArray,
522 max_entries,
523 SizedType::integer(32),
524 SizedType::integer(value_size_bytes * 8),
525 )
526 }
527}