#![allow(non_camel_case_types, dead_code)]
use std::{
io::{Read, Seek, SeekFrom},
mem,
};
use btf_rs_derive::cbtf_type;
use byteorder::{BigEndian, ByteOrder, LittleEndian, ReadBytesExt};
use crate::{Error, Result};
pub(super) enum Endianness {
Little,
Big,
}
impl Endianness {
fn u16_from_bytes(&self, buf: &[u8]) -> Result<u16> {
if buf.len() < 2 {
return Err(Error::OpNotSupp("Not enough bytes in buffer".to_string()));
}
Ok(match &self {
Endianness::Little => LittleEndian::read_u16(buf),
Endianness::Big => BigEndian::read_u16(buf),
})
}
fn u32_from_bytes(&self, buf: &[u8]) -> Result<u32> {
if buf.len() < 4 {
return Err(Error::OpNotSupp("Not enough bytes in buffer".to_string()));
}
Ok(match &self {
Endianness::Little => LittleEndian::read_u32(buf),
Endianness::Big => BigEndian::read_u32(buf),
})
}
fn i32_from_bytes(&self, buf: &[u8]) -> Result<i32> {
if buf.len() < 4 {
return Err(Error::OpNotSupp("Not enough bytes in buffer".to_string()));
}
Ok(match &self {
Endianness::Little => LittleEndian::read_i32(buf),
Endianness::Big => BigEndian::read_i32(buf),
})
}
fn u16_from_reader<R: Read>(&self, reader: &mut R) -> Result<u16> {
Ok(match &self {
Endianness::Little => reader.read_u16::<LittleEndian>()?,
Endianness::Big => reader.read_u16::<BigEndian>()?,
})
}
fn u32_from_reader<R: Read>(&self, reader: &mut R) -> Result<u32> {
Ok(match &self {
Endianness::Little => reader.read_u32::<LittleEndian>()?,
Endianness::Big => reader.read_u32::<BigEndian>()?,
})
}
fn i32_from_reader<R: Read>(&self, reader: &mut R) -> Result<i32> {
Ok(match &self {
Endianness::Little => reader.read_i32::<LittleEndian>()?,
Endianness::Big => reader.read_i32::<BigEndian>()?,
})
}
}
pub(super) enum BtfKind {
Int = 1,
Ptr = 2,
Array = 3,
Struct = 4,
Union = 5,
Enum = 6,
Fwd = 7,
Typedef = 8,
Volatile = 9,
Const = 10,
Restrict = 11,
Func = 12,
FuncProto = 13,
Var = 14,
Datasec = 15,
Float = 16,
DeclTag = 17,
TypeTag = 18,
Enum64 = 19,
}
impl BtfKind {
pub(super) fn from_id(id: u32) -> Result<Self> {
use BtfKind::*;
Ok(match id {
1 => Int,
2 => Ptr,
3 => Array,
4 => Struct,
5 => Union,
6 => Enum,
7 => Fwd,
8 => Typedef,
9 => Volatile,
10 => Const,
11 => Restrict,
12 => Func,
13 => FuncProto,
14 => Var,
15 => Datasec,
16 => Float,
17 => DeclTag,
18 => TypeTag,
19 => Enum64,
x => return Err(Error::Format(format!("Unsupported BTF type {x}"))),
})
}
fn size(&self, vlen: usize) -> usize {
use BtfKind::*;
mem::size_of::<btf_type>()
+ match self {
Ptr | Fwd | Typedef | Volatile | Const | Restrict | Func | Float | TypeTag => 0,
Int => mem::size_of::<btf_int>(),
Array => mem::size_of::<btf_array>(),
Struct | Union => vlen * mem::size_of::<btf_member>(),
Enum => vlen * mem::size_of::<btf_enum>(),
FuncProto => vlen * mem::size_of::<btf_param>(),
Var => mem::size_of::<btf_var>(),
Datasec => vlen * mem::size_of::<btf_var_secinfo>(),
DeclTag => mem::size_of::<btf_decl_tag>(),
Enum64 => vlen * mem::size_of::<btf_enum64>(),
}
}
fn has_anon_name(&self) -> bool {
use BtfKind::*;
matches!(self, Struct | Union | Enum | Enum64)
}
fn has_size(&self) -> bool {
use BtfKind::*;
matches!(self, Int | Struct | Union | Enum | Datasec | Float | Enum64)
}
fn has_type(&self) -> bool {
use BtfKind::*;
matches!(
self,
Ptr | Typedef
| Volatile
| Const
| Restrict
| Func
| FuncProto
| Var
| DeclTag
| TypeTag
)
}
}
#[derive(Clone, Copy, Debug)]
#[repr(C, packed)]
pub(super) struct btf_header {
pub(super) magic: u16,
pub(super) version: u8,
pub(super) flags: u8,
pub(super) hdr_len: u32,
pub(super) type_off: u32,
pub(super) type_len: u32,
pub(super) str_off: u32,
pub(super) str_len: u32,
}
impl btf_header {
pub(super) fn from_reader<R: Read>(reader: &mut R) -> Result<(btf_header, Endianness)> {
let magic = reader.read_u16::<LittleEndian>()?;
#[allow(clippy::mixed_case_hex_literals)]
let endianness = match magic {
0xeB9F => Endianness::Little,
0x9FeB => Endianness::Big,
magic => return Err(Error::Format(format!("Invalid BTF magic: {magic:#x}"))),
};
Ok((
btf_header {
magic,
version: reader.read_u8()?,
flags: reader.read_u8()?,
hdr_len: endianness.u32_from_reader(reader)?,
type_off: endianness.u32_from_reader(reader)?,
type_len: endianness.u32_from_reader(reader)?,
str_off: endianness.u32_from_reader(reader)?,
str_len: endianness.u32_from_reader(reader)?,
},
endianness,
))
}
}
pub(super) fn btf_skip_type<R: Read + Seek>(reader: &mut R, endianness: &Endianness) -> Result<()> {
reader.seek(SeekFrom::Current(4))?;
let info = endianness.u32_from_reader(reader)?;
let id = (info >> 24) & 0x1f;
let vlen = (info & 0xffff) as usize;
reader.seek(SeekFrom::Current(
(BtfKind::from_id(id)?.size(vlen) - 2 * mem::size_of::<u32>()) as i64,
))?;
Ok(())
}
#[cbtf_type]
pub(super) struct btf_type {
name_off: u32,
info: u32,
size_type: u32,
}
impl btf_type {
pub(super) fn name_offset(&self) -> Option<u32> {
let offset = self.name_off;
if offset > 0 {
return Some(offset);
}
if let Ok(kind) = BtfKind::from_id(self.kind()) {
if kind.has_anon_name() {
return Some(0);
}
}
None
}
pub(super) fn vlen(&self) -> u32 {
self.info & 0xffff
}
pub(super) fn kind(&self) -> u32 {
(self.info >> 24) & 0x1f
}
pub(super) fn kind_flag(&self) -> u32 {
(self.info >> 31) & 0x1
}
pub(super) fn size(&self) -> Option<usize> {
if let Ok(kind) = BtfKind::from_id(self.kind()) {
if kind.has_size() {
return Some(self.size_type as usize);
}
}
None
}
pub(super) fn r#type(&self) -> Option<u32> {
if let Ok(kind) = BtfKind::from_id(self.kind()) {
if kind.has_type() {
return Some(self.size_type);
}
}
None
}
}
#[cbtf_type]
pub(super) struct btf_int {
data: u32,
}
impl btf_int {
pub(super) fn encoding(&self) -> u32 {
(self.data & 0x0f000000) >> 24
}
pub(super) fn offset(&self) -> u32 {
(self.data & 0x00ff0000) >> 16
}
pub(super) fn bits(&self) -> u32 {
self.data & 0x000000ff
}
}
pub(super) const BTF_INT_SIGNED: u32 = 1 << 0;
pub(super) const BTF_INT_CHAR: u32 = 1 << 1;
pub(super) const BTF_INT_BOOL: u32 = 1 << 2;
#[cbtf_type]
pub(super) struct btf_array {
pub(super) r#type: u32,
pub(super) index_type: u32,
pub(super) nelems: u32,
}
#[cbtf_type]
pub(super) struct btf_member {
pub(super) name_off: u32,
pub(super) r#type: u32,
pub(super) offset: u32,
}
#[cbtf_type]
pub(super) struct btf_enum {
pub(super) name_off: u32,
pub(super) val: u32,
}
pub(super) const BTF_FUNC_STATIC: u32 = 0;
pub(super) const BTF_FUNC_GLOBAL: u32 = 1;
pub(super) const BTF_FUNC_EXTERN: u32 = 2;
#[cbtf_type]
pub(super) struct btf_param {
pub(super) name_off: u32,
pub(super) r#type: u32,
}
#[cbtf_type]
pub(super) struct btf_var {
pub(super) linkage: u32,
}
pub(super) const BTF_VAR_STATIC: u32 = 0;
pub(super) const BTF_VAR_GLOBAL_ALLOCATED: u32 = 1;
pub(super) const BTF_VAR_GLOBAL_EXTERN: u32 = 2;
#[cbtf_type]
pub(super) struct btf_var_secinfo {
pub(super) r#type: u32,
pub(super) offset: u32,
pub(super) size: u32,
}
#[cbtf_type]
pub(super) struct btf_decl_tag {
pub(super) component_idx: i32,
}
#[cbtf_type]
pub(super) struct btf_enum64 {
pub(super) name_off: u32,
pub(super) val_lo32: u32,
pub(super) val_hi32: u32,
}