1use padlock_core::arch::ArchConfig;
9use padlock_core::ir::{AccessPattern, Field, StructLayout, TypeInfo};
10
11const BTF_MAGIC: u16 = 0xEB9F;
14const BTF_KIND_INT: u32 = 1;
15const BTF_KIND_PTR: u32 = 2;
16const BTF_KIND_ARRAY: u32 = 3;
17const BTF_KIND_STRUCT: u32 = 4;
18const BTF_KIND_UNION: u32 = 5;
19const BTF_KIND_ENUM: u32 = 6;
20const BTF_KIND_FWD: u32 = 7;
21const BTF_KIND_TYPEDEF: u32 = 8;
22const BTF_KIND_VOLATILE: u32 = 9;
23const BTF_KIND_CONST: u32 = 10;
24const BTF_KIND_RESTRICT: u32 = 11;
25const BTF_KIND_FUNC: u32 = 12;
26const BTF_KIND_FUNC_PROTO: u32 = 13;
27const BTF_KIND_VAR: u32 = 14;
28const BTF_KIND_DATASEC: u32 = 15;
29const BTF_KIND_FLOAT: u32 = 16;
30const BTF_KIND_DECL_TAG: u32 = 17;
31const BTF_KIND_TYPE_TAG: u32 = 18;
32const BTF_KIND_ENUM64: u32 = 19;
33
34#[derive(Debug, Clone)]
37struct BtfHeader {
38 hdr_len: u32,
39 type_off: u32,
40 type_len: u32,
41 str_off: u32,
42 str_len: u32,
43}
44
45#[derive(Debug, Clone)]
46struct RawBtfType {
47 info: u32,
48}
49
50impl RawBtfType {
51 fn kind(&self) -> u32 {
52 (self.info >> 24) & 0x1f
53 }
54 fn vlen(&self) -> u32 {
55 self.info & 0xffff
56 }
57 fn kind_flag(&self) -> bool {
58 (self.info >> 31) & 1 == 1
59 }
60}
61
62#[derive(Debug, Clone)]
65enum BtfType {
66 Int {
67 name: String,
68 size: u32,
69 },
70 Ptr,
71 Array {
72 elem_type: u32,
73 nelems: u32,
74 },
75 Struct {
76 name: String,
77 size: u32,
78 members: Vec<BtfMember>,
79 is_union: bool,
80 },
81 Enum {
82 size: u32,
83 },
84 Typedef {
85 type_id: u32,
86 },
87 Qualifier {
88 type_id: u32,
89 }, Float {
91 size: u32,
92 },
93 Unknown,
94}
95
96#[derive(Debug, Clone)]
97struct BtfMember {
98 name: String,
99 type_id: u32,
100 bit_offset: u32,
101 bitfield_size: u32, }
103
104pub struct BtfParser<'a> {
107 data: &'a [u8],
108 types: Vec<BtfType>,
110 arch: &'static ArchConfig,
111}
112
113impl<'a> BtfParser<'a> {
114 pub fn new(data: &'a [u8], arch: &'static ArchConfig) -> anyhow::Result<Self> {
115 let mut p = BtfParser {
116 data,
117 types: Vec::new(),
118 arch,
119 };
120 p.parse()?;
121 Ok(p)
122 }
123
124 fn parse(&mut self) -> anyhow::Result<()> {
125 if self.data.len() < 24 {
126 anyhow::bail!("BTF data too short");
127 }
128
129 let magic = u16::from_le_bytes([self.data[0], self.data[1]]);
130 if magic != BTF_MAGIC {
131 anyhow::bail!("invalid BTF magic: 0x{:04x}", magic);
132 }
133
134 let hdr = BtfHeader {
135 hdr_len: u32::from_le_bytes(self.data[4..8].try_into()?),
136 type_off: u32::from_le_bytes(self.data[8..12].try_into()?),
137 type_len: u32::from_le_bytes(self.data[12..16].try_into()?),
138 str_off: u32::from_le_bytes(self.data[16..20].try_into()?),
139 str_len: u32::from_le_bytes(self.data[20..24].try_into()?),
140 };
141
142 let type_base = hdr.hdr_len as usize + hdr.type_off as usize;
143 let type_end = type_base + hdr.type_len as usize;
144 let str_base = hdr.hdr_len as usize + hdr.str_off as usize;
145 let str_end = str_base + hdr.str_len as usize;
146
147 if type_end > self.data.len() || str_end > self.data.len() {
148 anyhow::bail!("BTF sections extend beyond data");
149 }
150
151 let type_data = &self.data[type_base..type_end];
152 let str_data = &self.data[str_base..str_end];
153
154 self.types.push(BtfType::Unknown);
156
157 let mut off = 0usize;
158 while off + 12 <= type_data.len() {
159 let name_off = u32::from_le_bytes(type_data[off..off + 4].try_into()?);
160 let info = u32::from_le_bytes(type_data[off + 4..off + 8].try_into()?);
161 let size_or_type = u32::from_le_bytes(type_data[off + 8..off + 12].try_into()?);
162 off += 12;
163
164 let raw = RawBtfType { info };
165 let name = read_btf_str(str_data, name_off as usize);
166 let kind = raw.kind();
167 let vlen = raw.vlen() as usize;
168
169 let btf_type = match kind {
170 BTF_KIND_INT => {
171 off += 4; BtfType::Int {
173 name,
174 size: size_or_type,
175 }
176 }
177 BTF_KIND_PTR => BtfType::Ptr,
178 BTF_KIND_ARRAY => {
179 if off + 12 > type_data.len() {
180 break;
181 }
182 let elem_type = u32::from_le_bytes(type_data[off..off + 4].try_into()?);
183 let nelems = u32::from_le_bytes(type_data[off + 8..off + 12].try_into()?);
185 off += 12;
186 BtfType::Array { elem_type, nelems }
187 }
188 BTF_KIND_STRUCT | BTF_KIND_UNION => {
189 let mut members = Vec::with_capacity(vlen);
190 for _ in 0..vlen {
191 if off + 12 > type_data.len() {
192 break;
193 }
194 let m_name_off = u32::from_le_bytes(type_data[off..off + 4].try_into()?);
195 let m_type = u32::from_le_bytes(type_data[off + 4..off + 8].try_into()?);
196 let m_offset = u32::from_le_bytes(type_data[off + 8..off + 12].try_into()?);
197 off += 12;
198
199 let (bit_offset, bitfield_size) = if raw.kind_flag() {
200 (m_offset & 0xffffff, (m_offset >> 24) & 0xff)
201 } else {
202 (m_offset, 0)
203 };
204
205 members.push(BtfMember {
206 name: read_btf_str(str_data, m_name_off as usize),
207 type_id: m_type,
208 bit_offset,
209 bitfield_size,
210 });
211 }
212 BtfType::Struct {
213 name,
214 size: size_or_type,
215 members,
216 is_union: kind == BTF_KIND_UNION,
217 }
218 }
219 BTF_KIND_ENUM => {
220 off += vlen * 8; BtfType::Enum { size: size_or_type }
222 }
223 BTF_KIND_TYPEDEF => BtfType::Typedef {
224 type_id: size_or_type,
225 },
226 BTF_KIND_VOLATILE | BTF_KIND_CONST | BTF_KIND_RESTRICT => BtfType::Qualifier {
227 type_id: size_or_type,
228 },
229 BTF_KIND_FLOAT => BtfType::Float { size: size_or_type },
230 BTF_KIND_ENUM64 => {
231 off += vlen * 12; BtfType::Enum { size: size_or_type }
233 }
234 BTF_KIND_FWD | BTF_KIND_FUNC | BTF_KIND_TYPE_TAG => BtfType::Unknown,
236 BTF_KIND_FUNC_PROTO => {
238 off += vlen * 8;
239 BtfType::Unknown
240 }
241 BTF_KIND_VAR => {
243 off += 4;
244 BtfType::Unknown
245 }
246 BTF_KIND_DATASEC => {
248 off += vlen * 12;
249 BtfType::Unknown
250 }
251 BTF_KIND_DECL_TAG => {
253 off += 4;
254 BtfType::Unknown
255 }
256 _ => {
257 break;
259 }
260 };
261
262 self.types.push(btf_type);
263 }
264
265 Ok(())
266 }
267
268 fn type_size(&self, type_id: u32) -> usize {
270 if type_id == 0 {
271 return 0; }
273 let idx = type_id as usize;
274 if idx >= self.types.len() {
275 return self.arch.pointer_size;
276 }
277 match &self.types[idx] {
278 BtfType::Int { size, .. } | BtfType::Float { size } | BtfType::Enum { size } => {
279 *size as usize
280 }
281 BtfType::Ptr => self.arch.pointer_size,
282 BtfType::Array { elem_type, nelems } => self.type_size(*elem_type) * (*nelems as usize),
283 BtfType::Struct { size, .. } => *size as usize,
284 BtfType::Typedef { type_id } | BtfType::Qualifier { type_id } => {
285 self.type_size(*type_id)
286 }
287 BtfType::Unknown => self.arch.pointer_size,
288 }
289 }
290
291 fn type_align(&self, type_id: u32) -> usize {
293 if type_id == 0 {
294 return 1;
295 }
296 let idx = type_id as usize;
297 if idx >= self.types.len() {
298 return self.arch.pointer_size;
299 }
300 match &self.types[idx] {
301 BtfType::Int { size, .. } | BtfType::Float { size } | BtfType::Enum { size } => {
302 (*size as usize).min(self.arch.max_align)
303 }
304 BtfType::Ptr => self.arch.pointer_size,
305 BtfType::Array { elem_type, .. } => self.type_align(*elem_type),
306 BtfType::Struct { members, .. } => members
307 .iter()
308 .map(|m| self.type_align(m.type_id))
309 .max()
310 .unwrap_or(1),
311 BtfType::Typedef { type_id } | BtfType::Qualifier { type_id } => {
312 self.type_align(*type_id)
313 }
314 BtfType::Unknown => self.arch.pointer_size,
315 }
316 }
317
318 fn type_name(&self, type_id: u32) -> String {
320 if type_id == 0 {
321 return "void".to_string();
322 }
323 let idx = type_id as usize;
324 if idx >= self.types.len() {
325 return format!("type_{}", type_id);
326 }
327 match &self.types[idx] {
328 BtfType::Int { name, .. } | BtfType::Struct { name, .. } => {
329 if name.is_empty() {
330 format!("type_{}", type_id)
331 } else {
332 name.clone()
333 }
334 }
335 BtfType::Float { size } => format!("f{}", size * 8),
336 BtfType::Ptr => "*".to_string(),
337 BtfType::Array { elem_type, nelems } => {
338 format!("[{}]{}", nelems, self.type_name(*elem_type))
339 }
340 BtfType::Enum { .. } => format!("enum_{}", type_id),
341 BtfType::Typedef { type_id } => self.type_name(*type_id),
342 BtfType::Qualifier { type_id } => self.type_name(*type_id),
343 BtfType::Unknown => format!("unknown_{}", type_id),
344 }
345 }
346
347 pub fn extract_structs(&self) -> Vec<StructLayout> {
349 let mut layouts = Vec::new();
350
351 for (idx, ty) in self.types.iter().enumerate() {
352 if let BtfType::Struct {
353 name,
354 size,
355 members,
356 is_union,
357 } = ty
358 {
359 if name.is_empty() {
361 continue;
362 }
363
364 let mut fields: Vec<Field> = Vec::new();
365 let mut covered_until: usize = 0;
368
369 for member in members {
370 let is_bitfield = member.bitfield_size != 0 || member.bit_offset % 8 != 0;
371
372 if is_bitfield {
373 let storage_size = self.type_size(member.type_id).max(1);
377 let storage_bits = (storage_size * 8) as u32;
379 let unit_start_bit = (member.bit_offset / storage_bits) * storage_bits;
380 let unit_byte_offset = (unit_start_bit / 8) as usize;
381 let unit_end = unit_byte_offset + storage_size;
382
383 if unit_byte_offset >= covered_until {
386 let fname = format!("{}__bits", member.name);
387 let falign = storage_size.min(self.arch.max_align);
388 fields.push(Field {
389 name: fname.clone(),
390 ty: TypeInfo::Primitive {
391 name: format!("u{}", storage_size * 8),
392 size: storage_size,
393 align: falign,
394 },
395 offset: unit_byte_offset,
396 size: storage_size,
397 align: falign,
398 source_file: None,
399 source_line: None,
400 access: AccessPattern::Unknown,
401 });
402 covered_until = unit_end;
403 }
404 continue;
405 }
406
407 let byte_offset = (member.bit_offset / 8) as usize;
408 let fsize = self.type_size(member.type_id);
409 let falign = self.type_align(member.type_id);
410 let fname = if member.name.is_empty() {
411 format!("field_{}", fields.len())
412 } else {
413 member.name.clone()
414 };
415
416 covered_until = covered_until.max(byte_offset + fsize);
417 fields.push(Field {
418 name: fname.clone(),
419 ty: TypeInfo::Primitive {
420 name: self.type_name(member.type_id),
421 size: fsize,
422 align: falign,
423 },
424 offset: byte_offset,
425 size: fsize,
426 align: falign,
427 source_file: None,
428 source_line: None,
429 access: AccessPattern::Unknown,
430 });
431 }
432
433 if fields.is_empty() {
434 continue;
435 }
436
437 let max_align = fields.iter().map(|f| f.align).max().unwrap_or(1);
438
439 let natural_size = {
443 let mut off2 = 0usize;
444 for f in &fields {
445 if max_align > 0 {
446 off2 = off2.next_multiple_of(f.align.max(1));
447 }
448 off2 += f.size;
449 }
450 if max_align > 0 {
451 off2 = off2.next_multiple_of(max_align.max(1));
452 }
453 off2
454 };
455 let is_packed = !*is_union && (*size as usize) < natural_size;
456
457 layouts.push(StructLayout {
458 name: name.clone(),
459 total_size: *size as usize,
460 align: max_align,
461 fields,
462 source_file: None,
463 source_line: None,
464 arch: self.arch,
465 is_packed,
466 is_union: *is_union,
467 });
468
469 let _ = idx;
470 }
471 }
472
473 layouts
474 }
475}
476
477fn read_btf_str(str_data: &[u8], off: usize) -> String {
478 if off >= str_data.len() {
479 return String::new();
480 }
481 let end = str_data[off..]
482 .iter()
483 .position(|&b| b == 0)
484 .map(|p| off + p)
485 .unwrap_or(str_data.len());
486 String::from_utf8_lossy(&str_data[off..end]).into_owned()
487}
488
489pub fn extract_from_btf(
493 btf_data: &[u8],
494 arch: &'static ArchConfig,
495) -> anyhow::Result<Vec<StructLayout>> {
496 let parser = BtfParser::new(btf_data, arch)?;
497 Ok(parser.extract_structs())
498}
499
500#[cfg(test)]
503mod tests {
504 use super::*;
505 use padlock_core::arch::X86_64_SYSV;
506
507 fn build_test_btf() -> Vec<u8> {
509 let strings: &[u8] = b"\0point\0x\0y\0";
511 let str_len = strings.len() as u32;
512
513 let mut type_data: Vec<u8> = Vec::new();
526
527 let x_name_off: u32 = 7;
529 type_data.extend_from_slice(&x_name_off.to_le_bytes());
530 type_data.extend_from_slice(&(BTF_KIND_INT << 24).to_le_bytes());
531 type_data.extend_from_slice(&4u32.to_le_bytes());
532 type_data.extend_from_slice(&0u32.to_le_bytes()); let y_name_off: u32 = 9;
536 type_data.extend_from_slice(&y_name_off.to_le_bytes());
537 type_data.extend_from_slice(&(BTF_KIND_INT << 24).to_le_bytes());
538 type_data.extend_from_slice(&4u32.to_le_bytes());
539 type_data.extend_from_slice(&0u32.to_le_bytes());
540
541 let point_name_off: u32 = 1;
543 type_data.extend_from_slice(&point_name_off.to_le_bytes());
544 type_data.extend_from_slice(&((BTF_KIND_STRUCT << 24) | 2u32).to_le_bytes()); type_data.extend_from_slice(&8u32.to_le_bytes()); type_data.extend_from_slice(&x_name_off.to_le_bytes()); type_data.extend_from_slice(&1u32.to_le_bytes()); type_data.extend_from_slice(&0u32.to_le_bytes()); type_data.extend_from_slice(&y_name_off.to_le_bytes()); type_data.extend_from_slice(&2u32.to_le_bytes()); type_data.extend_from_slice(&32u32.to_le_bytes()); let type_len = type_data.len() as u32;
556
557 let hdr_len: u32 = 24;
559 let mut btf = Vec::new();
560 btf.extend_from_slice(&BTF_MAGIC.to_le_bytes()); btf.push(1); btf.push(0); btf.extend_from_slice(&hdr_len.to_le_bytes()); btf.extend_from_slice(&0u32.to_le_bytes()); btf.extend_from_slice(&type_len.to_le_bytes()); btf.extend_from_slice(&type_len.to_le_bytes()); btf.extend_from_slice(&str_len.to_le_bytes()); btf.extend_from_slice(&type_data);
569 btf.extend_from_slice(strings);
570 btf
571 }
572
573 #[test]
574 fn btf_parse_simple_struct() {
575 let btf = build_test_btf();
576 let layouts = extract_from_btf(&btf, &X86_64_SYSV).unwrap();
577 assert_eq!(layouts.len(), 1);
578 assert_eq!(layouts[0].name, "point");
579 assert_eq!(layouts[0].total_size, 8);
580 assert_eq!(layouts[0].fields.len(), 2);
581 }
582
583 #[test]
584 fn btf_field_offsets_correct() {
585 let btf = build_test_btf();
586 let layouts = extract_from_btf(&btf, &X86_64_SYSV).unwrap();
587 let l = &layouts[0];
588 assert_eq!(l.fields[0].name, "x");
589 assert_eq!(l.fields[0].offset, 0);
590 assert_eq!(l.fields[1].name, "y");
591 assert_eq!(l.fields[1].offset, 4);
592 }
593
594 #[test]
595 fn btf_invalid_magic_errors() {
596 let mut btf = build_test_btf();
597 btf[0] = 0xff;
598 btf[1] = 0xff;
599 assert!(extract_from_btf(&btf, &X86_64_SYSV).is_err());
600 }
601
602 #[test]
603 fn btf_bitfield_members_become_synthetic_storage_unit_fields() {
604 let strings: &[u8] = b"\0mystruct\0flags\0";
607 let str_len = strings.len() as u32;
609 let mut type_data: Vec<u8> = Vec::new();
610
611 let flags_name_off: u32 = 10;
613 type_data.extend_from_slice(&flags_name_off.to_le_bytes());
614 type_data.extend_from_slice(&(BTF_KIND_INT << 24).to_le_bytes());
615 type_data.extend_from_slice(&4u32.to_le_bytes());
616 type_data.extend_from_slice(&0u32.to_le_bytes()); let struct_name_off: u32 = 1;
620 let struct_info: u32 = (1u32 << 31) | (BTF_KIND_STRUCT << 24) | 1u32;
622 type_data.extend_from_slice(&struct_name_off.to_le_bytes());
623 type_data.extend_from_slice(&struct_info.to_le_bytes());
624 type_data.extend_from_slice(&4u32.to_le_bytes()); let m_offset: u32 = (3u32 << 24) | 0u32; type_data.extend_from_slice(&flags_name_off.to_le_bytes());
628 type_data.extend_from_slice(&1u32.to_le_bytes()); type_data.extend_from_slice(&m_offset.to_le_bytes());
630
631 let type_len = type_data.len() as u32;
632 let hdr_len: u32 = 24;
633 let mut btf = Vec::new();
634 btf.extend_from_slice(&BTF_MAGIC.to_le_bytes());
635 btf.push(1);
636 btf.push(0);
637 btf.extend_from_slice(&hdr_len.to_le_bytes());
638 btf.extend_from_slice(&0u32.to_le_bytes());
639 btf.extend_from_slice(&type_len.to_le_bytes());
640 btf.extend_from_slice(&type_len.to_le_bytes());
641 btf.extend_from_slice(&str_len.to_le_bytes());
642 btf.extend_from_slice(&type_data);
643 btf.extend_from_slice(strings);
644
645 let layouts = extract_from_btf(&btf, &X86_64_SYSV).unwrap();
646 assert_eq!(layouts.len(), 1);
647 let l = &layouts[0];
648 assert_eq!(l.name, "mystruct");
649 assert_eq!(l.fields.len(), 1);
651 assert_eq!(l.fields[0].offset, 0);
652 assert_eq!(l.fields[0].size, 4); assert!(l.fields[0].name.ends_with("__bits"));
654 }
655
656 #[test]
657 fn btf_skips_unknown_kinds_gracefully() {
658 let strings: &[u8] = b"\0foo\0x\0myfunc\0";
661 let str_len = strings.len() as u32;
662 let mut type_data: Vec<u8> = Vec::new();
664
665 type_data.extend_from_slice(&5u32.to_le_bytes());
667 type_data.extend_from_slice(&(BTF_KIND_INT << 24).to_le_bytes());
668 type_data.extend_from_slice(&4u32.to_le_bytes());
669 type_data.extend_from_slice(&0u32.to_le_bytes());
670
671 type_data.extend_from_slice(&7u32.to_le_bytes());
673 type_data.extend_from_slice(&(BTF_KIND_FUNC << 24).to_le_bytes());
674 type_data.extend_from_slice(&1u32.to_le_bytes()); type_data.extend_from_slice(&1u32.to_le_bytes());
678 type_data.extend_from_slice(&((BTF_KIND_STRUCT << 24) | 1u32).to_le_bytes());
679 type_data.extend_from_slice(&4u32.to_le_bytes());
680 type_data.extend_from_slice(&5u32.to_le_bytes());
682 type_data.extend_from_slice(&1u32.to_le_bytes());
683 type_data.extend_from_slice(&0u32.to_le_bytes());
684
685 let type_len = type_data.len() as u32;
686 let hdr_len: u32 = 24;
687 let mut btf = Vec::new();
688 btf.extend_from_slice(&BTF_MAGIC.to_le_bytes());
689 btf.push(1);
690 btf.push(0);
691 btf.extend_from_slice(&hdr_len.to_le_bytes());
692 btf.extend_from_slice(&0u32.to_le_bytes());
693 btf.extend_from_slice(&type_len.to_le_bytes());
694 btf.extend_from_slice(&type_len.to_le_bytes());
695 btf.extend_from_slice(&str_len.to_le_bytes());
696 btf.extend_from_slice(&type_data);
697 btf.extend_from_slice(strings);
698
699 let layouts = extract_from_btf(&btf, &X86_64_SYSV).unwrap();
700 assert_eq!(layouts.len(), 1);
701 assert_eq!(layouts[0].name, "foo");
702 assert_eq!(layouts[0].fields[0].name, "x");
703 }
704}