use std::cmp::{max, min};
use std::ffi::{c_char, CStr};
use std::fmt;
use std::mem::size_of;
use object::{Object, ObjectSection};
use scroll::{Endian, Pread};
use scroll_derive::{IOread, IOwrite, Pread as DerivePread, Pwrite, SizeWith};
use crate::{btf_error, BtfError, BtfResult};
pub const BTF_ELF_SEC: &str = ".BTF";
pub const BTF_EXT_ELF_SEC: &str = ".BTF.ext";
pub const BTF_MAGIC: u16 = 0xeb9f;
pub const BTF_VERSION: u8 = 1;
pub const BTF_KIND_UNKN: u32 = 0;
pub const BTF_KIND_INT: u32 = 1;
pub const BTF_KIND_PTR: u32 = 2;
pub const BTF_KIND_ARRAY: u32 = 3;
pub const BTF_KIND_STRUCT: u32 = 4;
pub const BTF_KIND_UNION: u32 = 5;
pub const BTF_KIND_ENUM: u32 = 6;
pub const BTF_KIND_FWD: u32 = 7;
pub const BTF_KIND_TYPEDEF: u32 = 8;
pub const BTF_KIND_VOLATILE: u32 = 9;
pub const BTF_KIND_CONST: u32 = 10;
pub const BTF_KIND_RESTRICT: u32 = 11;
pub const BTF_KIND_FUNC: u32 = 12;
pub const BTF_KIND_FUNC_PROTO: u32 = 13;
pub const BTF_KIND_VAR: u32 = 14;
pub const BTF_KIND_DATASEC: u32 = 15;
pub const BTF_KIND_FLOAT: u32 = 16;
pub const BTF_KIND_DECL_TAG: u32 = 17;
pub const BTF_KIND_TYPE_TAG: u32 = 18;
pub const BTF_KIND_ENUM64: u32 = 19;
pub const BTF_KIND_MAX: u32 = 19;
pub const NR_BTF_KINDS: u32 = BTF_KIND_MAX + 1;
pub const BTF_MAX_KIND: u32 = 0x0000007f;
pub const BTF_MAX_VLEN: u32 = 0x00ffffff;
pub const BTF_INT_SIGNED: u32 = 0b001;
pub const BTF_INT_CHAR: u32 = 0b010;
pub const BTF_INT_BOOL: u32 = 0b100;
pub const BTF_VAR_STATIC: u32 = 0;
pub const BTF_VAR_GLOBAL_ALLOCATED: u32 = 1;
pub const BTF_VAR_GLOBAL_EXTERNAL: u32 = 2;
pub const BTF_FUNC_STATIC: u32 = 0;
pub const BTF_FUNC_GLOBAL: u32 = 1;
pub const BTF_FUNC_EXTERN: u32 = 2;
#[repr(C)]
#[derive(Debug, Copy, Clone, DerivePread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct btf_header {
pub magic: u16,
pub version: u8,
pub flags: u8,
pub hdr_len: u32,
pub type_off: u32,
pub type_len: u32,
pub str_off: u32,
pub str_len: u32,
}
#[repr(C)]
#[derive(Debug, Copy, Clone, DerivePread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct btf_header_v2 {
pub magic: u16,
pub version: u8,
pub flags: u8,
pub hdr_len: u32,
pub type_off: u32,
pub type_len: u32,
pub str_off: u32,
pub str_len: u32,
pub layout_off: u32,
pub layout_len: u32,
}
#[repr(C)]
#[derive(Debug, Copy, Clone, Default, DerivePread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct btf_layout {
pub info_sz: u8,
pub elem_sz: u8,
pub flags: u16,
}
#[repr(C)]
#[derive(Debug, Copy, Clone, DerivePread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct btf_type {
pub name_off: u32,
pub info: u32,
pub type_id: u32,
}
#[repr(C)]
#[derive(Debug, Copy, Clone, DerivePread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct btf_enum {
pub name_off: u32,
pub val: i32,
}
#[repr(C)]
#[derive(Debug, Copy, Clone, DerivePread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct btf_array {
pub val_type_id: u32,
pub idx_type_id: u32,
pub nelems: u32,
}
#[repr(C)]
#[derive(Debug, Copy, Clone, DerivePread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct btf_member {
pub name_off: u32,
pub type_id: u32,
pub offset: u32,
}
#[repr(C)]
#[derive(Debug, Copy, Clone, DerivePread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct btf_param {
pub name_off: u32,
pub type_id: u32,
}
#[repr(C)]
#[derive(Debug, Copy, Clone, DerivePread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct btf_datasec_var {
pub type_id: u32,
pub offset: u32,
pub size: u32,
}
#[repr(C)]
#[derive(Debug, Copy, Clone, DerivePread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct btf_enum64 {
pub name_off: u32,
pub val_lo32: u32,
pub val_hi32: u32,
}
#[repr(C)]
#[derive(Debug, Copy, Clone, DerivePread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct btf_ext_min_header {
pub magic: u16,
pub version: u8,
pub flags: u8,
pub hdr_len: u32,
}
#[repr(C)]
#[derive(Debug, Copy, Clone, DerivePread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct btf_ext_header_v1 {
pub magic: u16,
pub version: u8,
pub flags: u8,
pub hdr_len: u32,
pub func_info_off: u32,
pub func_info_len: u32,
pub line_info_off: u32,
pub line_info_len: u32,
}
#[repr(C)]
#[derive(Debug, Copy, Clone, DerivePread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct btf_ext_header_v2 {
pub magic: u16,
pub version: u8,
pub flags: u8,
pub hdr_len: u32,
pub func_info_off: u32,
pub func_info_len: u32,
pub line_info_off: u32,
pub line_info_len: u32,
pub core_reloc_off: u32,
pub core_reloc_len: u32,
}
#[repr(C)]
#[derive(Debug, Copy, Clone, DerivePread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct btf_ext_info_sec {
pub sec_name_off: u32,
pub num_info: u32,
}
#[repr(C)]
#[derive(Debug, Copy, Clone, DerivePread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct btf_ext_func_info {
pub insn_off: u32,
pub type_id: u32,
}
#[repr(C)]
#[derive(Debug, Copy, Clone, DerivePread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct btf_ext_line_info {
pub insn_off: u32,
pub file_name_off: u32,
pub line_off: u32,
pub line_col: u32,
}
pub const BTF_FIELD_BYTE_OFFSET: u32 = 0;
pub const BTF_FIELD_BYTE_SIZE: u32 = 1;
pub const BTF_FIELD_EXISTS: u32 = 2;
pub const BTF_FIELD_SIGNED: u32 = 3;
pub const BTF_FIELD_LSHIFT_U64: u32 = 4;
pub const BTF_FIELD_RSHIFT_U64: u32 = 5;
pub const BTF_TYPE_LOCAL_ID: u32 = 6;
pub const BTF_TYPE_TARGET_ID: u32 = 7;
pub const BTF_TYPE_EXISTS: u32 = 8;
pub const BTF_TYPE_SIZE: u32 = 9;
pub const BTF_ENUMVAL_EXISTS: u32 = 10;
pub const BTF_ENUMVAL_VALUE: u32 = 11;
pub const BTF_TYPE_MATCHES: u32 = 12;
#[repr(C)]
#[derive(Debug, Copy, Clone, DerivePread, Pwrite, IOread, IOwrite, SizeWith)]
pub struct btf_ext_core_reloc {
pub insn_off: u32,
pub type_id: u32,
pub access_spec_off: u32,
pub kind: u32,
}
const EMPTY: &str = "";
const ANON_NAME: &str = "<anon>";
fn disp_name(s: &str) -> &str {
if s.is_empty() {
ANON_NAME
} else {
s
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum BtfIntEncoding {
None,
Signed,
Char,
Bool,
}
impl fmt::Display for BtfIntEncoding {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
BtfIntEncoding::None => write!(f, "none"),
BtfIntEncoding::Signed => write!(f, "signed"),
BtfIntEncoding::Char => write!(f, "char"),
BtfIntEncoding::Bool => write!(f, "bool"),
}
}
}
#[derive(Debug)]
pub struct BtfInt<'a> {
pub name: &'a str,
pub bits: u32,
pub offset: u32,
pub encoding: BtfIntEncoding,
}
impl fmt::Display for BtfInt<'_> {
#[expect(clippy::write_literal)]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"<{}> '{}' bits:{} off:{}",
"INT",
disp_name(self.name),
self.bits,
self.offset
)?;
match self.encoding {
BtfIntEncoding::None => (),
_ => write!(f, " enc:{}", self.encoding)?,
}
Ok(())
}
}
#[derive(Debug)]
pub struct BtfPtr {
pub type_id: u32,
}
impl fmt::Display for BtfPtr {
#[expect(clippy::write_literal)]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "<{}> --> [{}]", "PTR", self.type_id)
}
}
#[derive(Debug)]
pub struct BtfArray {
pub nelems: u32,
pub idx_type_id: u32,
pub val_type_id: u32,
}
impl fmt::Display for BtfArray {
#[expect(clippy::write_literal)]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"<{}> n:{} idx-->[{}] val-->[{}]",
"ARRAY", self.nelems, self.idx_type_id, self.val_type_id
)
}
}
#[derive(Debug)]
pub struct BtfMember<'a> {
pub name: &'a str,
pub type_id: u32,
pub bit_offset: u32,
pub bit_size: u8,
}
impl fmt::Display for BtfMember<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "'{}' off:{}", disp_name(self.name), self.bit_offset)?;
if self.bit_size != 0 {
write!(f, " sz:{}", self.bit_size)?;
}
write!(f, " --> [{}]", self.type_id)
}
}
#[derive(Debug)]
pub struct BtfComposite<'a> {
pub is_struct: bool,
pub name: &'a str,
pub sz: u32,
pub members: Vec<BtfMember<'a>>,
}
impl fmt::Display for BtfComposite<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"<{}> '{}' sz:{} n:{}",
if self.is_struct { "STRUCT" } else { "UNION" },
disp_name(self.name),
self.sz,
self.members.len()
)?;
for i in 0..self.members.len() {
write!(f, "\n\t#{:02} {}", i, self.members[i])?;
}
Ok(())
}
}
#[derive(Debug)]
pub struct BtfEnumValue<'a> {
pub name: &'a str,
pub value: i32,
}
impl BtfEnumValue<'_> {
pub fn value_str(&self, signed: bool) -> String {
if signed {
self.value.to_string()
} else {
(self.value as u32).to_string()
}
}
}
#[derive(Debug)]
pub struct BtfEnum<'a> {
pub name: &'a str,
pub sz: u32,
pub signed: bool,
pub values: Vec<BtfEnumValue<'a>>,
}
impl fmt::Display for BtfEnum<'_> {
#[expect(clippy::write_literal)]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"<{}> '{}' sz:{} n:{}",
"ENUM",
disp_name(self.name),
self.sz,
self.values.len()
)?;
if self.signed {
write!(f, " enc:signed")?;
}
for (i, v) in self.values.iter().enumerate() {
write!(
f,
"\n\t#{:02} {} = {}",
i,
disp_name(v.name),
v.value_str(self.signed)
)?;
}
Ok(())
}
}
#[derive(Debug)]
pub struct BtfEnum64Value<'a> {
pub name: &'a str,
pub value: i64,
}
impl BtfEnum64Value<'_> {
pub fn value_str(&self, signed: bool) -> String {
if signed {
self.value.to_string()
} else {
(self.value as u64).to_string()
}
}
}
#[derive(Debug)]
pub struct BtfEnum64<'a> {
pub name: &'a str,
pub sz: u32,
pub signed: bool,
pub values: Vec<BtfEnum64Value<'a>>,
}
impl fmt::Display for BtfEnum64<'_> {
#[expect(clippy::write_literal)]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"<{}> '{}' sz:{} n:{}",
"ENUM64",
disp_name(self.name),
self.sz,
self.values.len()
)?;
if self.signed {
write!(f, " enc:signed")?;
}
for (i, v) in self.values.iter().enumerate() {
write!(
f,
"\n\t#{:02} {} = {}",
i,
disp_name(v.name),
v.value_str(self.signed)
)?;
}
Ok(())
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum BtfFwdKind {
Struct,
Union,
}
impl fmt::Display for BtfFwdKind {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
BtfFwdKind::Struct => write!(f, "struct"),
BtfFwdKind::Union => write!(f, "union"),
}
}
}
#[derive(Debug)]
pub struct BtfFwd<'a> {
pub name: &'a str,
pub kind: BtfFwdKind,
}
impl fmt::Display for BtfFwd<'_> {
#[expect(clippy::write_literal)]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"<{}> '{}' kind:{}",
"FWD",
disp_name(self.name),
self.kind
)
}
}
#[derive(Debug)]
pub struct BtfTypedef<'a> {
pub name: &'a str,
pub type_id: u32,
}
impl fmt::Display for BtfTypedef<'_> {
#[expect(clippy::write_literal)]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"<{}> '{}' --> [{}]",
"TYPEDEF",
disp_name(self.name),
self.type_id
)
}
}
#[derive(Debug)]
pub struct BtfVolatile {
pub type_id: u32,
}
impl fmt::Display for BtfVolatile {
#[expect(clippy::write_literal)]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "<{}> --> [{}]", "VOLATILE", self.type_id)
}
}
#[derive(Debug)]
pub struct BtfConst {
pub type_id: u32,
}
impl fmt::Display for BtfConst {
#[expect(clippy::write_literal)]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "<{}> --> [{}]", "CONST", self.type_id)
}
}
#[derive(Debug)]
pub struct BtfRestrict {
pub type_id: u32,
}
impl fmt::Display for BtfRestrict {
#[expect(clippy::write_literal)]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "<{}> --> [{}]", "RESTRICT", self.type_id)
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum BtfFuncKind {
Unknown,
Static,
Global,
Extern,
}
impl fmt::Display for BtfFuncKind {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
BtfFuncKind::Unknown => write!(f, "<unknown>"),
BtfFuncKind::Static => write!(f, "static"),
BtfFuncKind::Global => write!(f, "global"),
BtfFuncKind::Extern => write!(f, "extern"),
}
}
}
#[derive(Debug)]
pub struct BtfFunc<'a> {
pub name: &'a str,
pub proto_type_id: u32,
pub kind: BtfFuncKind,
}
impl fmt::Display for BtfFunc<'_> {
#[expect(clippy::write_literal)]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"<{}> '{}' --> {} [{}]",
"FUNC",
disp_name(self.name),
self.kind,
self.proto_type_id
)
}
}
#[derive(Debug)]
pub struct BtfFuncParam<'a> {
pub name: &'a str,
pub type_id: u32,
}
impl fmt::Display for BtfFuncParam<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "'{}' --> [{}]", disp_name(self.name), self.type_id)
}
}
#[derive(Debug)]
pub struct BtfFuncProto<'a> {
pub res_type_id: u32,
pub params: Vec<BtfFuncParam<'a>>,
}
impl fmt::Display for BtfFuncProto<'_> {
#[expect(clippy::write_literal)]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"<{}> r-->[{}] n:{}",
"FUNC_PROTO",
self.res_type_id,
self.params.len()
)?;
for i in 0..self.params.len() {
write!(f, "\n\t#{:02} {}", i, self.params[i])?;
}
Ok(())
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum BtfVarKind {
Static,
GlobalAlloc,
GlobalExtern,
}
impl fmt::Display for BtfVarKind {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
BtfVarKind::Static => write!(f, "static"),
BtfVarKind::GlobalAlloc => write!(f, "global-alloc"),
BtfVarKind::GlobalExtern => write!(f, "global-extern"),
}
}
}
#[derive(Debug)]
pub struct BtfVar<'a> {
pub name: &'a str,
pub type_id: u32,
pub kind: BtfVarKind,
}
impl fmt::Display for BtfVar<'_> {
#[expect(clippy::write_literal)]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"<{}> '{}' kind:{} --> [{}]",
"VAR",
disp_name(self.name),
self.kind,
self.type_id
)
}
}
#[derive(Debug)]
pub struct BtfDatasecVar {
pub type_id: u32,
pub offset: u32,
pub sz: u32,
}
impl fmt::Display for BtfDatasecVar {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"off:{} sz:{} --> [{}]",
self.offset, self.sz, self.type_id
)
}
}
#[derive(Debug)]
pub struct BtfDatasec<'a> {
pub name: &'a str,
pub sz: u32,
pub vars: Vec<BtfDatasecVar>,
}
impl fmt::Display for BtfDatasec<'_> {
#[expect(clippy::write_literal)]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"<{}> '{}' sz:{} n:{}",
"DATASEC",
disp_name(self.name),
self.sz,
self.vars.len()
)?;
for i in 0..self.vars.len() {
write!(f, "\n\t#{:02} {}", i, self.vars[i])?;
}
Ok(())
}
}
#[derive(Debug)]
pub struct BtfFloat<'a> {
pub name: &'a str,
pub sz: u32,
}
impl fmt::Display for BtfFloat<'_> {
#[expect(clippy::write_literal)]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "<{}> '{}' sz:{}", "FLOAT", disp_name(self.name), self.sz)?;
Ok(())
}
}
#[derive(Debug)]
pub struct BtfDeclTag<'a> {
pub name: &'a str,
pub type_id: u32,
pub comp_idx: i32,
pub is_attr: bool,
}
impl fmt::Display for BtfDeclTag<'_> {
#[expect(clippy::write_literal)]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"<{}> '{}' --> [{}] comp_idx:{}",
"DECL_TAG",
disp_name(self.name),
self.type_id,
self.comp_idx,
)?;
if self.is_attr {
write!(f, " attr")?;
}
Ok(())
}
}
#[derive(Debug)]
pub struct BtfTypeTag<'a> {
pub name: &'a str,
pub type_id: u32,
pub is_attr: bool,
}
impl fmt::Display for BtfTypeTag<'_> {
#[expect(clippy::write_literal)]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"<{}> '{}' --> [{}]",
"TYPE_TAG",
disp_name(self.name),
self.type_id
)?;
if self.is_attr {
write!(f, " attr")?;
}
Ok(())
}
}
#[derive(Debug)]
pub struct BtfUnknown<'a> {
pub kind: u32,
pub name: &'a str,
pub vlen: u32,
pub size_or_type: u32,
pub raw_sz: usize,
}
impl fmt::Display for BtfUnknown<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"<UNKNOWN:{}> '{}' vlen:{} sz/type:{} raw_sz:{}",
self.kind,
disp_name(self.name),
self.vlen,
self.size_or_type,
self.raw_sz
)
}
}
#[derive(Debug)]
pub enum BtfType<'a> {
Void,
Int(BtfInt<'a>),
Ptr(BtfPtr),
Array(BtfArray),
Struct(BtfComposite<'a>),
Union(BtfComposite<'a>),
Enum(BtfEnum<'a>),
Fwd(BtfFwd<'a>),
Typedef(BtfTypedef<'a>),
Volatile(BtfVolatile),
Const(BtfConst),
Restrict(BtfRestrict),
Func(BtfFunc<'a>),
FuncProto(BtfFuncProto<'a>),
Var(BtfVar<'a>),
Datasec(BtfDatasec<'a>),
Float(BtfFloat<'a>),
DeclTag(BtfDeclTag<'a>),
TypeTag(BtfTypeTag<'a>),
Enum64(BtfEnum64<'a>),
Unknown(BtfUnknown<'a>),
}
impl fmt::Display for BtfType<'_> {
#[expect(clippy::write_literal)]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
BtfType::Void => write!(f, "<{}>", "VOID"),
BtfType::Int(t) => t.fmt(f),
BtfType::Ptr(t) => t.fmt(f),
BtfType::Array(t) => t.fmt(f),
BtfType::Struct(t) => t.fmt(f),
BtfType::Union(t) => t.fmt(f),
BtfType::Enum(t) => t.fmt(f),
BtfType::Fwd(t) => t.fmt(f),
BtfType::Typedef(t) => t.fmt(f),
BtfType::Volatile(t) => t.fmt(f),
BtfType::Const(t) => t.fmt(f),
BtfType::Restrict(t) => t.fmt(f),
BtfType::Func(t) => t.fmt(f),
BtfType::FuncProto(t) => t.fmt(f),
BtfType::Var(t) => t.fmt(f),
BtfType::Datasec(t) => t.fmt(f),
BtfType::Float(t) => t.fmt(f),
BtfType::DeclTag(t) => t.fmt(f),
BtfType::TypeTag(t) => t.fmt(f),
BtfType::Enum64(t) => t.fmt(f),
BtfType::Unknown(t) => t.fmt(f),
}
}
}
impl BtfType<'_> {
pub fn kind(&self) -> BtfKind {
match self {
BtfType::Void => BtfKind::Void,
BtfType::Int(_) => BtfKind::Int,
BtfType::Ptr(_) => BtfKind::Ptr,
BtfType::Array(_) => BtfKind::Array,
BtfType::Struct(_) => BtfKind::Struct,
BtfType::Union(_) => BtfKind::Union,
BtfType::Enum(_) => BtfKind::Enum,
BtfType::Fwd(_) => BtfKind::Fwd,
BtfType::Typedef(_) => BtfKind::Typedef,
BtfType::Volatile(_) => BtfKind::Volatile,
BtfType::Const(_) => BtfKind::Const,
BtfType::Restrict(_) => BtfKind::Restrict,
BtfType::Func(_) => BtfKind::Func,
BtfType::FuncProto(_) => BtfKind::FuncProto,
BtfType::Var(_) => BtfKind::Var,
BtfType::Datasec(_) => BtfKind::Datasec,
BtfType::Float(_) => BtfKind::Float,
BtfType::DeclTag(_) => BtfKind::DeclTag,
BtfType::TypeTag(_) => BtfKind::TypeTag,
BtfType::Enum64(_) => BtfKind::Enum64,
BtfType::Unknown(t) => BtfKind::Unknown(t.kind),
}
}
pub fn name(&self) -> &str {
match self {
BtfType::Void => EMPTY,
BtfType::Int(t) => t.name,
BtfType::Ptr(_) => EMPTY,
BtfType::Array(_) => EMPTY,
BtfType::Struct(t) => t.name,
BtfType::Union(t) => t.name,
BtfType::Enum(t) => t.name,
BtfType::Fwd(t) => t.name,
BtfType::Typedef(t) => t.name,
BtfType::Volatile(_) => EMPTY,
BtfType::Const(_) => EMPTY,
BtfType::Restrict(_) => EMPTY,
BtfType::Func(t) => t.name,
BtfType::FuncProto(_) => EMPTY,
BtfType::Var(t) => t.name,
BtfType::Datasec(t) => t.name,
BtfType::Float(t) => t.name,
BtfType::DeclTag(t) => t.name,
BtfType::TypeTag(t) => t.name,
BtfType::Enum64(t) => t.name,
BtfType::Unknown(t) => t.name,
}
}
}
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash)]
pub enum BtfKind {
Void,
Int,
Ptr,
Array,
Struct,
Union,
Enum,
Fwd,
Typedef,
Volatile,
Const,
Restrict,
Func,
FuncProto,
Var,
Datasec,
Float,
DeclTag,
TypeTag,
Enum64,
Unknown(u32),
}
impl std::str::FromStr for BtfKind {
type Err = BtfError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"void" => Ok(BtfKind::Void),
"int" | "i" => Ok(BtfKind::Int),
"ptr" | "p" => Ok(BtfKind::Ptr),
"array" | "arr" | "a" => Ok(BtfKind::Array),
"struct" | "s" => Ok(BtfKind::Struct),
"union" | "u" => Ok(BtfKind::Union),
"enum" | "e" => Ok(BtfKind::Enum),
"fwd" => Ok(BtfKind::Fwd),
"typedef" | "t" => Ok(BtfKind::Typedef),
"volatile" => Ok(BtfKind::Volatile),
"const" => Ok(BtfKind::Const),
"restrict" => Ok(BtfKind::Restrict),
"func_proto" | "funcproto" | "fnproto" | "fp" => Ok(BtfKind::FuncProto),
"func" | "fn" => Ok(BtfKind::Func),
"var" | "v" => Ok(BtfKind::Var),
"datasec" => Ok(BtfKind::Datasec),
"float" => Ok(BtfKind::Float),
"decl_tag" => Ok(BtfKind::DeclTag),
"type_tag" => Ok(BtfKind::TypeTag),
"enum64" | "e64" => Ok(BtfKind::Enum64),
_ => s
.strip_prefix("unknown:")
.and_then(|kind| kind.parse::<u32>().ok())
.map(BtfKind::Unknown)
.ok_or_else(|| BtfError::new_owned(format!("unrecognized btf kind: '{s}'"))),
}
}
}
#[derive(Debug)]
pub struct BtfExtSection<'a, T> {
pub name: &'a str,
pub rec_sz: usize,
pub recs: Vec<T>,
}
#[derive(Debug)]
pub struct BtfExtFunc {
pub insn_off: u32,
pub type_id: u32,
}
impl fmt::Display for BtfExtFunc {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"func: insn #{} --> [{}]",
self.insn_off / 8,
self.type_id
)
}
}
#[derive(Debug)]
pub struct BtfExtLine<'a> {
pub insn_off: u32,
pub file_name: &'a str,
pub src_line: &'a str,
pub line_num: u32,
pub col_num: u32,
}
impl fmt::Display for BtfExtLine<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"line: insn #{} --> {}:{} @ {}\n\t{}",
self.insn_off / 8,
self.line_num,
self.col_num,
self.file_name,
self.src_line
)
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum BtfCoreRelocKind {
ByteOff = 0,
ByteSz = 1,
FieldExists = 2,
Signed = 3,
LShiftU64 = 4,
RShiftU64 = 5,
LocalTypeId = 6,
TargetTypeId = 7,
TypeExists = 8,
TypeSize = 9,
EnumvalExists = 10,
EnumvalValue = 11,
TypeMatches = 12,
}
impl fmt::Display for BtfCoreRelocKind {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
BtfCoreRelocKind::ByteOff => write!(f, "byte_off"),
BtfCoreRelocKind::ByteSz => write!(f, "byte_sz"),
BtfCoreRelocKind::FieldExists => write!(f, "field_exists"),
BtfCoreRelocKind::Signed => write!(f, "signed"),
BtfCoreRelocKind::LShiftU64 => write!(f, "lshift_u64"),
BtfCoreRelocKind::RShiftU64 => write!(f, "rshift_u64"),
BtfCoreRelocKind::LocalTypeId => write!(f, "local_type_id"),
BtfCoreRelocKind::TargetTypeId => write!(f, "target_type_id"),
BtfCoreRelocKind::TypeExists => write!(f, "type_exists"),
BtfCoreRelocKind::TypeMatches => write!(f, "type_matches"),
BtfCoreRelocKind::TypeSize => write!(f, "type_size"),
BtfCoreRelocKind::EnumvalExists => write!(f, "enumval_exists"),
BtfCoreRelocKind::EnumvalValue => write!(f, "enumval_value"),
}
}
}
#[derive(Debug)]
pub struct BtfExtCoreReloc<'a> {
pub insn_off: u32,
pub type_id: u32,
pub access_spec_str: &'a str,
pub access_spec: Vec<usize>,
pub kind: BtfCoreRelocKind,
}
impl fmt::Display for BtfExtCoreReloc<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"core_reloc: insn #{} --> [{}] + {}: {}",
self.insn_off / 8,
self.type_id,
self.access_spec_str,
self.kind,
)
}
}
#[derive(Debug)]
pub struct Btf<'a> {
endian: scroll::Endian,
types: Vec<BtfType<'a>>,
ptr_sz: u32,
layout: Vec<btf_layout>,
has_ext: bool,
func_secs: Vec<BtfExtSection<'a, BtfExtFunc>>,
line_secs: Vec<BtfExtSection<'a, BtfExtLine<'a>>>,
core_reloc_secs: Vec<BtfExtSection<'a, BtfExtCoreReloc<'a>>>,
}
impl<'a> Btf<'a> {
pub fn ptr_sz(&self) -> u32 {
self.ptr_sz
}
pub fn types(&self) -> &[BtfType<'_>] {
&self.types
}
pub fn type_by_id(&self, type_id: u32) -> &BtfType<'_> {
&self.types[type_id as usize]
}
pub fn type_cnt(&self) -> u32 {
self.types.len() as u32
}
pub fn has_ext(&self) -> bool {
self.has_ext
}
pub fn func_secs(&self) -> &[BtfExtSection<'_, BtfExtFunc>] {
&self.func_secs
}
pub fn line_secs(&self) -> &[BtfExtSection<'_, BtfExtLine<'_>>] {
&self.line_secs
}
pub fn core_reloc_secs(&self) -> &[BtfExtSection<'_, BtfExtCoreReloc<'_>>] {
&self.core_reloc_secs
}
pub fn get_size_of(&self, type_id: u32) -> u32 {
match self.type_by_id(type_id) {
BtfType::Void => 0,
BtfType::Int(t) => t.bits.div_ceil(8),
BtfType::Volatile(t) => self.get_size_of(t.type_id),
BtfType::Const(t) => self.get_size_of(t.type_id),
BtfType::Restrict(t) => self.get_size_of(t.type_id),
BtfType::Ptr(_) => self.ptr_sz,
BtfType::Array(t) => t.nelems * self.get_size_of(t.val_type_id),
BtfType::FuncProto(_) => 0,
BtfType::Struct(t) => t.sz,
BtfType::Union(t) => t.sz,
BtfType::Enum(t) => t.sz,
BtfType::Fwd(_) => 0,
BtfType::Typedef(t) => self.get_size_of(t.type_id),
BtfType::Func(_) => 0,
BtfType::Var(_) => 0,
BtfType::Datasec(t) => t.sz,
BtfType::Float(t) => t.sz,
BtfType::DeclTag(t) => self.get_size_of(t.type_id),
BtfType::TypeTag(t) => self.get_size_of(t.type_id),
BtfType::Enum64(t) => t.sz,
BtfType::Unknown(_) => 0,
}
}
pub fn get_align_of(&self, type_id: u32) -> u32 {
match self.type_by_id(type_id) {
BtfType::Void => 0,
BtfType::Int(t) => min(self.ptr_sz, t.bits.div_ceil(8)),
BtfType::Volatile(t) => self.get_align_of(t.type_id),
BtfType::Const(t) => self.get_align_of(t.type_id),
BtfType::Restrict(t) => self.get_align_of(t.type_id),
BtfType::Ptr(_) => self.ptr_sz,
BtfType::Array(t) => self.get_align_of(t.val_type_id),
BtfType::FuncProto(_) => 0,
BtfType::Struct(t) => {
let mut align = 1;
for m in &t.members {
align = max(align, self.get_align_of(m.type_id));
}
align
}
BtfType::Union(t) => {
let mut align = 1;
for m in &t.members {
align = max(align, self.get_align_of(m.type_id));
}
align
}
BtfType::Enum(t) => min(self.ptr_sz, t.sz),
BtfType::Fwd(_) => 0,
BtfType::Typedef(t) => self.get_align_of(t.type_id),
BtfType::Func(_) => 0,
BtfType::Var(_) => 0,
BtfType::Datasec(_) => 0,
BtfType::Float(t) => min(self.ptr_sz, t.sz),
BtfType::DeclTag(_) => 0,
BtfType::TypeTag(t) => self.get_align_of(t.type_id),
BtfType::Enum64(t) => min(self.ptr_sz, t.sz),
BtfType::Unknown(_) => 0,
}
}
pub fn skip_mods(&self, mut type_id: u32) -> u32 {
loop {
match self.type_by_id(type_id) {
BtfType::Volatile(t) => type_id = t.type_id,
BtfType::Const(t) => type_id = t.type_id,
BtfType::Restrict(t) => type_id = t.type_id,
BtfType::TypeTag(t) => type_id = t.type_id,
_ => return type_id,
}
}
}
pub fn skip_mods_and_typedefs(&self, mut type_id: u32) -> u32 {
loop {
match self.type_by_id(type_id) {
BtfType::Volatile(t) => type_id = t.type_id,
BtfType::Const(t) => type_id = t.type_id,
BtfType::Restrict(t) => type_id = t.type_id,
BtfType::Typedef(t) => type_id = t.type_id,
BtfType::TypeTag(t) => type_id = t.type_id,
_ => return type_id,
}
}
}
fn load(endian: Endian, ptr_sz: u32, data: &'a [u8]) -> BtfResult<(Self, &'a [u8])> {
let mut btf = Self {
endian,
ptr_sz,
layout: Vec::new(),
types: vec![BtfType::Void],
has_ext: false,
func_secs: Vec::new(),
line_secs: Vec::new(),
core_reloc_secs: Vec::new(),
};
let hdr = data.pread_with::<btf_header>(0, endian)?;
if hdr.magic != BTF_MAGIC {
return btf_error(format!("Invalid BTF magic: {}", hdr.magic));
}
if hdr.version != BTF_VERSION {
return btf_error(format!(
"Unsupported BTF version: {}, expect: {}",
hdr.version, BTF_VERSION
));
}
if hdr.hdr_len as usize >= size_of::<btf_header_v2>() {
let hdr2 = data.pread_with::<btf_header_v2>(0, endian)?;
if hdr2.layout_len > 0 {
if !(hdr2.layout_len as usize).is_multiple_of(size_of::<btf_layout>()) {
return btf_error(format!(
"Invalid BTF kind layout section length: {}",
hdr2.layout_len
));
}
let off = (hdr2.hdr_len + hdr2.layout_off) as usize;
for i in 0..hdr2.layout_len as usize / size_of::<btf_layout>() {
btf.layout
.push(data.pread_with::<btf_layout>(off + i * size_of::<btf_layout>(), endian)?);
}
}
}
let str_off = (hdr.hdr_len + hdr.str_off) as usize;
let str_data = &data[str_off..str_off + hdr.str_len as usize];
let type_off = (hdr.hdr_len + hdr.type_off) as usize;
let type_data = &data[type_off..type_off + hdr.type_len as usize];
let mut off: usize = 0;
while off < hdr.type_len as usize {
let t = btf.load_type(&type_data[off..], str_data)?;
off += Btf::type_size(&t);
btf.types.push(t);
}
Ok((btf, str_data))
}
pub fn load_elf(elf: &object::File<'a>) -> BtfResult<Self> {
let endian = if elf.is_little_endian() {
scroll::LE
} else {
scroll::BE
};
let ptr_sz = if elf.is_64() { 8 } else { 4 };
let btf_section = elf
.section_by_name(BTF_ELF_SEC)
.ok_or_else(|| Box::new(BtfError::new("No .BTF section found!")))?;
let data = btf_section.data().expect("expected borrowed data");
let (mut btf, str_data) = Self::load(endian, ptr_sz, data)?;
if let Some(ext_section) = elf.section_by_name(BTF_EXT_ELF_SEC) {
btf.has_ext = true;
let ext_data = ext_section.data().expect("expected borrowed data");
let ext_hdr = ext_data.pread_with::<btf_ext_header_v1>(0, endian)?;
if ext_hdr.magic != BTF_MAGIC {
return btf_error(format!("Invalid .BTF.ext magic: {}", ext_hdr.magic));
}
if ext_hdr.version != BTF_VERSION {
return btf_error(format!(
"Unsupported .BTF.ext version: {}, expect: {}",
ext_hdr.version, BTF_VERSION
));
}
let ext_hdr2 = if ext_hdr.hdr_len >= size_of::<btf_ext_header_v2>() as u32 {
Some(ext_data.pread_with::<btf_ext_header_v2>(0, endian)?)
} else {
None
};
if ext_hdr.func_info_len > 0 {
let func_off = (ext_hdr.hdr_len + ext_hdr.func_info_off) as usize;
let func_data = &ext_data[func_off..func_off + ext_hdr.func_info_len as usize];
btf.func_secs = btf.load_func_secs(func_data, str_data)?;
}
if ext_hdr.line_info_len > 0 {
let line_off = (ext_hdr.hdr_len + ext_hdr.line_info_off) as usize;
let line_data = &ext_data[line_off..line_off + ext_hdr.line_info_len as usize];
btf.line_secs = btf.load_line_secs(line_data, str_data)?;
}
if let Some(h) = ext_hdr2 {
if h.core_reloc_len > 0 {
let reloc_off = (h.hdr_len + h.core_reloc_off) as usize;
let reloc_data = &ext_data[reloc_off..reloc_off + h.core_reloc_len as usize];
btf.core_reloc_secs = btf.load_core_reloc_secs(reloc_data, str_data)?;
}
}
}
Ok(btf)
}
pub fn raw_endianness(data: &[u8]) -> BtfResult<Endian> {
let magic = data.pread_with::<u16>(0, scroll::LE)?;
if magic == BTF_MAGIC {
Ok(scroll::LE)
} else if magic.swap_bytes() == BTF_MAGIC {
Ok(scroll::BE)
} else {
btf_error(format!("Invalid BTF magic: {magic:#x}"))
}
}
pub fn load_raw(data: &'a [u8]) -> BtfResult<Self> {
let endian = Self::raw_endianness(data)?;
#[cfg(target_pointer_width = "64")]
let ptr_sz = 8_u32;
#[cfg(target_pointer_width = "32")]
let ptr_sz = 4_u32;
let (btf, _) = Self::load(endian, ptr_sz, data)?;
Ok(btf)
}
pub fn type_size(t: &BtfType) -> usize {
let common = size_of::<btf_type>();
match t {
BtfType::Void => 0,
BtfType::Ptr(_)
| BtfType::Fwd(_)
| BtfType::Typedef(_)
| BtfType::Volatile(_)
| BtfType::Const(_)
| BtfType::Restrict(_)
| BtfType::Func(_)
| BtfType::Float(_)
| BtfType::TypeTag(_) => common,
BtfType::Int(_) | BtfType::Var(_) | BtfType::DeclTag(_) => common + size_of::<u32>(),
BtfType::Array(_) => common + size_of::<btf_array>(),
BtfType::Struct(t) => common + t.members.len() * size_of::<btf_member>(),
BtfType::Union(t) => common + t.members.len() * size_of::<btf_member>(),
BtfType::Enum(t) => common + t.values.len() * size_of::<btf_enum>(),
BtfType::Enum64(t) => common + t.values.len() * size_of::<btf_enum64>(),
BtfType::FuncProto(t) => common + t.params.len() * size_of::<btf_param>(),
BtfType::Datasec(t) => common + t.vars.len() * size_of::<btf_datasec_var>(),
BtfType::Unknown(t) => t.raw_sz,
}
}
fn load_type(&self, data: &'a [u8], strs: &'a [u8]) -> BtfResult<BtfType<'a>> {
let t = data.pread_with::<btf_type>(0, self.endian)?;
let extra = &data[size_of::<btf_type>()..];
let kind = Btf::get_kind(t.info);
match kind {
BTF_KIND_INT => self.load_int(&t, extra, strs),
BTF_KIND_PTR => Ok(BtfType::Ptr(BtfPtr { type_id: t.type_id })),
BTF_KIND_ARRAY => self.load_array(extra),
BTF_KIND_STRUCT => self.load_struct(&t, extra, strs),
BTF_KIND_UNION => self.load_union(&t, extra, strs),
BTF_KIND_ENUM => self.load_enum(&t, extra, strs),
BTF_KIND_FWD => self.load_fwd(&t, strs),
BTF_KIND_TYPEDEF => Ok(BtfType::Typedef(BtfTypedef {
name: Btf::get_btf_str(strs, t.name_off)?,
type_id: t.type_id,
})),
BTF_KIND_VOLATILE => Ok(BtfType::Volatile(BtfVolatile { type_id: t.type_id })),
BTF_KIND_CONST => Ok(BtfType::Const(BtfConst { type_id: t.type_id })),
BTF_KIND_RESTRICT => Ok(BtfType::Restrict(BtfRestrict { type_id: t.type_id })),
BTF_KIND_FUNC => Ok(BtfType::Func(BtfFunc {
name: Btf::get_btf_str(strs, t.name_off)?,
proto_type_id: t.type_id,
kind: match Btf::get_vlen(t.info) {
BTF_FUNC_STATIC => BtfFuncKind::Static,
BTF_FUNC_GLOBAL => BtfFuncKind::Global,
BTF_FUNC_EXTERN => BtfFuncKind::Extern,
_ => BtfFuncKind::Unknown,
},
})),
BTF_KIND_FUNC_PROTO => self.load_func_proto(&t, extra, strs),
BTF_KIND_VAR => self.load_var(&t, extra, strs),
BTF_KIND_DATASEC => self.load_datasec(&t, extra, strs),
BTF_KIND_FLOAT => Ok(BtfType::Float(BtfFloat {
name: Btf::get_btf_str(strs, t.name_off)?,
sz: t.type_id,
})),
BTF_KIND_DECL_TAG => self.load_decl_tag(&t, extra, strs),
BTF_KIND_TYPE_TAG => Ok(BtfType::TypeTag(BtfTypeTag {
name: Btf::get_btf_str(strs, t.name_off)?,
type_id: t.type_id,
is_attr: Btf::get_kind_flag(t.info),
})),
BTF_KIND_ENUM64 => self.load_enum64(&t, extra, strs),
_ => self.load_unknown(&t, kind, strs),
}
}
fn load_int(&self, t: &btf_type, extra: &'a [u8], strs: &'a [u8]) -> BtfResult<BtfType<'a>> {
let info = extra.pread_with::<u32>(0, self.endian)?;
let enc = (info >> 24) & 0xf;
let off = (info >> 16) & 0xff;
let bits = info & 0xff;
Ok(BtfType::Int(BtfInt {
name: Btf::get_btf_str(strs, t.name_off)?,
bits,
offset: off,
encoding: match enc {
0 => BtfIntEncoding::None,
BTF_INT_SIGNED => BtfIntEncoding::Signed,
BTF_INT_CHAR => BtfIntEncoding::Char,
BTF_INT_BOOL => BtfIntEncoding::Bool,
_ => {
return btf_error(format!("Unknown BTF int encoding: {enc}"));
}
},
}))
}
fn load_array(&self, extra: &'a [u8]) -> BtfResult<BtfType<'a>> {
let info = extra.pread_with::<btf_array>(0, self.endian)?;
Ok(BtfType::Array(BtfArray {
nelems: info.nelems,
idx_type_id: info.idx_type_id,
val_type_id: info.val_type_id,
}))
}
fn load_struct(&self, t: &btf_type, extra: &'a [u8], strs: &'a [u8]) -> BtfResult<BtfType<'a>> {
Ok(BtfType::Struct(BtfComposite {
is_struct: true,
name: Btf::get_btf_str(strs, t.name_off)?,
sz: t.type_id, members: self.load_members(t, extra, strs)?,
}))
}
fn load_union(&self, t: &btf_type, extra: &'a [u8], strs: &'a [u8]) -> BtfResult<BtfType<'a>> {
Ok(BtfType::Union(BtfComposite {
is_struct: false,
name: Btf::get_btf_str(strs, t.name_off)?,
sz: t.type_id, members: self.load_members(t, extra, strs)?,
}))
}
fn load_members(
&self,
t: &btf_type,
extra: &'a [u8],
strs: &'a [u8],
) -> BtfResult<Vec<BtfMember<'a>>> {
let mut res = Vec::new();
let mut off: usize = 0;
let bits = Btf::get_kind_flag(t.info);
for _ in 0..Btf::get_vlen(t.info) {
let m = extra.pread_with::<btf_member>(off, self.endian)?;
res.push(BtfMember {
name: Btf::get_btf_str(strs, m.name_off)?,
type_id: m.type_id,
bit_size: if bits { (m.offset >> 24) as u8 } else { 0 },
bit_offset: if bits { m.offset & 0xffffff } else { m.offset },
});
off += size_of::<btf_member>();
}
Ok(res)
}
fn load_enum(&self, t: &btf_type, extra: &'a [u8], strs: &'a [u8]) -> BtfResult<BtfType<'a>> {
let mut vals = Vec::new();
let mut off: usize = 0;
for _ in 0..Btf::get_vlen(t.info) {
let v = extra.pread_with::<btf_enum>(off, self.endian)?;
vals.push(BtfEnumValue {
name: Btf::get_btf_str(strs, v.name_off)?,
value: v.val,
});
off += size_of::<btf_enum>();
}
Ok(BtfType::Enum(BtfEnum {
name: Btf::get_btf_str(strs, t.name_off)?,
sz: t.type_id, signed: Btf::get_kind_flag(t.info),
values: vals,
}))
}
fn load_enum64(&self, t: &btf_type, extra: &'a [u8], strs: &'a [u8]) -> BtfResult<BtfType<'a>> {
let mut vals = Vec::new();
let mut off: usize = 0;
for _ in 0..Btf::get_vlen(t.info) {
let v = extra.pread_with::<btf_enum64>(off, self.endian)?;
vals.push(BtfEnum64Value {
name: Btf::get_btf_str(strs, v.name_off)?,
value: (i64::from(v.val_hi32) << 32) | i64::from(v.val_lo32),
});
off += size_of::<btf_enum64>();
}
Ok(BtfType::Enum64(BtfEnum64 {
name: Btf::get_btf_str(strs, t.name_off)?,
sz: t.type_id, signed: Btf::get_kind_flag(t.info),
values: vals,
}))
}
fn load_fwd(&self, t: &btf_type, strs: &'a [u8]) -> BtfResult<BtfType<'a>> {
Ok(BtfType::Fwd(BtfFwd {
name: Btf::get_btf_str(strs, t.name_off)?,
kind: if Btf::get_kind_flag(t.info) {
BtfFwdKind::Union
} else {
BtfFwdKind::Struct
},
}))
}
fn load_func_proto(
&self,
t: &btf_type,
extra: &'a [u8],
strs: &'a [u8],
) -> BtfResult<BtfType<'a>> {
let mut params = Vec::new();
let mut off: usize = 0;
for _ in 0..Btf::get_vlen(t.info) {
let p = extra.pread_with::<btf_param>(off, self.endian)?;
params.push(BtfFuncParam {
name: Btf::get_btf_str(strs, p.name_off)?,
type_id: p.type_id,
});
off += size_of::<btf_param>();
}
Ok(BtfType::FuncProto(BtfFuncProto {
res_type_id: t.type_id,
params,
}))
}
fn load_var(&self, t: &btf_type, extra: &'a [u8], strs: &'a [u8]) -> BtfResult<BtfType<'a>> {
let kind = extra.pread_with::<u32>(0, self.endian)?;
Ok(BtfType::Var(BtfVar {
name: Btf::get_btf_str(strs, t.name_off)?,
type_id: t.type_id,
kind: match kind {
BTF_VAR_STATIC => BtfVarKind::Static,
BTF_VAR_GLOBAL_ALLOCATED => BtfVarKind::GlobalAlloc,
BTF_VAR_GLOBAL_EXTERNAL => BtfVarKind::GlobalExtern,
_ => {
return btf_error(format!("Unknown BTF var kind: {kind}"));
}
},
}))
}
fn load_datasec(
&self,
t: &btf_type,
extra: &'a [u8],
strs: &'a [u8],
) -> BtfResult<BtfType<'a>> {
let mut vars = Vec::new();
let mut off: usize = 0;
for _ in 0..Btf::get_vlen(t.info) {
let v = extra.pread_with::<btf_datasec_var>(off, self.endian)?;
vars.push(BtfDatasecVar {
type_id: v.type_id,
offset: v.offset,
sz: v.size,
});
off += size_of::<btf_datasec_var>();
}
Ok(BtfType::Datasec(BtfDatasec {
name: Btf::get_btf_str(strs, t.name_off)?,
sz: t.type_id, vars,
}))
}
fn load_decl_tag(
&self,
t: &btf_type,
extra: &'a [u8],
strs: &'a [u8],
) -> BtfResult<BtfType<'a>> {
let comp_idx = extra.pread_with::<i32>(0, self.endian)?;
Ok(BtfType::DeclTag(BtfDeclTag {
name: Btf::get_btf_str(strs, t.name_off)?,
type_id: t.type_id,
comp_idx,
is_attr: Btf::get_kind_flag(t.info),
}))
}
fn load_unknown(&self, t: &btf_type, kind: u32, strs: &'a [u8]) -> BtfResult<BtfType<'a>> {
let Some(layout) = self.layout.get(kind as usize) else {
return btf_error(format!(
"Unknown BTF kind: {kind} (no kind layout information to skip it)"
));
};
let vlen = Btf::get_vlen(t.info);
Ok(BtfType::Unknown(BtfUnknown {
kind,
name: Btf::get_btf_str(strs, t.name_off)?,
vlen,
size_or_type: t.type_id,
raw_sz: size_of::<btf_type>()
+ layout.info_sz as usize
+ vlen as usize * layout.elem_sz as usize,
}))
}
fn get_vlen(info: u32) -> u32 {
info & BTF_MAX_VLEN
}
fn get_kind(info: u32) -> u32 {
(info >> 24) & BTF_MAX_KIND
}
fn get_kind_flag(info: u32) -> bool {
(info >> 31) == 1
}
fn load_func_secs(
&self,
mut data: &'a [u8],
strs: &'a [u8],
) -> BtfResult<Vec<BtfExtSection<'a, BtfExtFunc>>> {
let rec_sz = data.pread_with::<u32>(0, self.endian)?;
if rec_sz < size_of::<btf_ext_func_info>() as u32 {
return btf_error(format!(
"Too small func info record size: {}, expect at least: {}",
rec_sz,
size_of::<btf_ext_func_info>()
));
}
data = &data[size_of::<u32>()..];
let mut secs = Vec::new();
while !data.is_empty() {
let sec_hdr = data.pread_with::<btf_ext_info_sec>(0, self.endian)?;
data = &data[size_of::<btf_ext_info_sec>()..];
let mut recs = Vec::new();
for i in 0..sec_hdr.num_info {
let off = (i * rec_sz) as usize;
let rec = data.pread_with::<btf_ext_func_info>(off, self.endian)?;
recs.push(BtfExtFunc {
insn_off: rec.insn_off,
type_id: rec.type_id,
});
}
secs.push(BtfExtSection::<BtfExtFunc> {
name: Btf::get_btf_str(strs, sec_hdr.sec_name_off)?,
rec_sz: rec_sz as usize,
recs,
});
data = &data[(sec_hdr.num_info * rec_sz) as usize..];
}
Ok(secs)
}
fn load_line_secs(
&self,
mut data: &'a [u8],
strs: &'a [u8],
) -> BtfResult<Vec<BtfExtSection<'a, BtfExtLine<'a>>>> {
let rec_sz = data.pread_with::<u32>(0, self.endian)?;
if rec_sz < size_of::<btf_ext_line_info>() as u32 {
return btf_error(format!(
"Too small line info record size: {}, expect at least: {}",
rec_sz,
size_of::<btf_ext_line_info>()
));
}
data = &data[size_of::<u32>()..];
let mut secs = Vec::new();
while !data.is_empty() {
let sec_hdr = data.pread_with::<btf_ext_info_sec>(0, self.endian)?;
data = &data[size_of::<btf_ext_info_sec>()..];
let mut recs = Vec::new();
for i in 0..sec_hdr.num_info {
let off = (i * rec_sz) as usize;
let rec = data.pread_with::<btf_ext_line_info>(off, self.endian)?;
recs.push(BtfExtLine {
insn_off: rec.insn_off,
file_name: Btf::get_btf_str(strs, rec.file_name_off)?,
src_line: Btf::get_btf_str(strs, rec.line_off)?,
line_num: rec.line_col >> 10,
col_num: rec.line_col & 0x3ff,
});
}
secs.push(BtfExtSection::<BtfExtLine> {
name: Btf::get_btf_str(strs, sec_hdr.sec_name_off)?,
rec_sz: rec_sz as usize,
recs,
});
data = &data[(sec_hdr.num_info * rec_sz) as usize..];
}
Ok(secs)
}
fn load_core_reloc_secs(
&self,
mut data: &'a [u8],
strs: &'a [u8],
) -> BtfResult<Vec<BtfExtSection<'a, BtfExtCoreReloc<'a>>>> {
let rec_sz = data.pread_with::<u32>(0, self.endian)?;
if rec_sz < size_of::<btf_ext_core_reloc>() as u32 {
return btf_error(format!(
"Too small CO-RE reloc record size: {}, expect at least: {}",
rec_sz,
size_of::<btf_ext_core_reloc>()
));
}
data = &data[size_of::<u32>()..];
let mut secs = Vec::new();
while !data.is_empty() {
let sec_hdr = data.pread_with::<btf_ext_info_sec>(0, self.endian)?;
data = &data[size_of::<btf_ext_info_sec>()..];
let mut recs = Vec::new();
for i in 0..sec_hdr.num_info {
let off = (i * rec_sz) as usize;
let rec = data.pread_with::<btf_ext_core_reloc>(off, self.endian)?;
let kind = match rec.kind {
BTF_FIELD_BYTE_OFFSET => BtfCoreRelocKind::ByteOff,
BTF_FIELD_BYTE_SIZE => BtfCoreRelocKind::ByteSz,
BTF_FIELD_EXISTS => BtfCoreRelocKind::FieldExists,
BTF_FIELD_SIGNED => BtfCoreRelocKind::Signed,
BTF_FIELD_LSHIFT_U64 => BtfCoreRelocKind::LShiftU64,
BTF_FIELD_RSHIFT_U64 => BtfCoreRelocKind::RShiftU64,
BTF_TYPE_LOCAL_ID => BtfCoreRelocKind::LocalTypeId,
BTF_TYPE_TARGET_ID => BtfCoreRelocKind::TargetTypeId,
BTF_TYPE_EXISTS => BtfCoreRelocKind::TypeExists,
BTF_TYPE_MATCHES => BtfCoreRelocKind::TypeMatches,
BTF_TYPE_SIZE => BtfCoreRelocKind::TypeSize,
BTF_ENUMVAL_EXISTS => BtfCoreRelocKind::EnumvalExists,
BTF_ENUMVAL_VALUE => BtfCoreRelocKind::EnumvalValue,
_ => {
return btf_error(format!("Unknown BTF CO-RE reloc kind: {}", rec.kind));
}
};
let relo = {
let access_spec_str = Btf::get_btf_str(strs, rec.access_spec_off)?;
let access_spec = Btf::parse_reloc_access_spec(access_spec_str)?;
BtfExtCoreReloc {
insn_off: rec.insn_off,
type_id: rec.type_id,
access_spec_str,
access_spec,
kind,
}
};
recs.push(relo);
}
secs.push(BtfExtSection::<BtfExtCoreReloc> {
name: Btf::get_btf_str(strs, sec_hdr.sec_name_off)?,
rec_sz: rec_sz as usize,
recs,
});
data = &data[(sec_hdr.num_info * rec_sz) as usize..];
}
Ok(secs)
}
fn parse_reloc_access_spec(access_spec_str: &str) -> BtfResult<Vec<usize>> {
let mut spec = Vec::new();
for p in access_spec_str.split(':') {
spec.push(p.parse::<usize>()?);
}
Ok(spec)
}
fn get_btf_str(strs: &[u8], off: u32) -> BtfResult<&str> {
let c_str = unsafe { CStr::from_ptr(&strs[off as usize] as *const u8 as *const c_char) };
Ok(c_str.to_str()?)
}
}