diff --git a/aya-obj/src/btf/btf.rs b/aya-obj/src/btf/btf.rs index de9e54e4..f5c22d46 100644 --- a/aya-obj/src/btf/btf.rs +++ b/aya-obj/src/btf/btf.rs @@ -46,6 +46,11 @@ pub enum BtfError { #[error("error parsing BTF header")] InvalidHeader, + /// Invalid ELF data + #[cfg(feature = "std")] + #[error("error parsing ELF")] + Elf, + /// invalid BTF type info segment #[error("invalid BTF type info segment")] InvalidTypeInfo, @@ -276,7 +281,8 @@ impl Btf { self.types.types.len() < 2 } - pub(crate) fn types(&self) -> impl Iterator { + /// Iterates over the BTF types. + pub fn types(&self) -> impl Iterator { self.types.types.iter() } @@ -307,7 +313,7 @@ impl Btf { /// Loads BTF metadata from the given `path`. #[cfg(feature = "std")] - pub fn parse_file>( + pub(crate) fn parse_file>( path: P, endianness: Endianness, ) -> Result { @@ -322,6 +328,26 @@ impl Btf { ) } + /// Loads BTF metadata from an ELF section. + #[cfg(feature = "std")] + pub fn parse_elf_file>( + path: P, + endianness: Endianness, + ) -> Result { + use object::{Object, ObjectSection}; + let path = path.as_ref(); + let bin_data = std::fs::read(path).map_err(|e| BtfError::FileError { + path: path.to_owned(), + error: e, + })?; + let obj_file = object::File::parse(&*bin_data).map_err(|_| BtfError::Elf)?; + let section = obj_file + .section_by_name(".BTF") + .ok_or_else(|| BtfError::InvalidHeader)?; + let data = section.data().map_err(|_| BtfError::InvalidHeader)?; + Btf::parse(data, endianness) + } + /// Parses BTF from binary data of the given endianness pub fn parse(data: &[u8], endianness: Endianness) -> Result { if data.len() < mem::size_of::() { @@ -374,7 +400,8 @@ impl Btf { Ok(types) } - pub(crate) fn string_at(&self, offset: u32) -> Result, BtfError> { + /// Returns the string at the given offset within the BTF metadata. + pub fn string_at(&self, offset: u32) -> Result, BtfError> { let btf_header { hdr_len, mut str_off, @@ -400,7 +427,8 @@ impl Btf { Ok(s.to_string_lossy()) } - pub(crate) fn type_by_id(&self, type_id: u32) -> Result<&BtfType, BtfError> { + /// Returns the BtfType at the given type id. + pub fn type_by_id(&self, type_id: u32) -> Result<&BtfType, BtfError> { self.types.type_by_id(type_id) } diff --git a/aya-obj/src/btf/types.rs b/aya-obj/src/btf/types.rs index 89b1713b..47f47354 100644 --- a/aya-obj/src/btf/types.rs +++ b/aya-obj/src/btf/types.rs @@ -82,6 +82,10 @@ impl Const { btf_type, } } + /// The target type of the const. + pub fn btf_type(&self) -> u32 { + self.btf_type + } } #[repr(C)] @@ -104,6 +108,11 @@ impl Volatile { pub(crate) fn type_info_size(&self) -> usize { mem::size_of::() } + + /// The target type of the volatile. + pub fn btf_type(&self) -> u32 { + self.btf_type + } } #[derive(Clone, Debug)] @@ -125,6 +134,11 @@ impl Restrict { pub(crate) fn type_info_size(&self) -> usize { mem::size_of::() } + + /// The target type of the restrict. + pub fn btf_type(&self) -> u32 { + self.btf_type + } } #[repr(C)] @@ -156,6 +170,11 @@ impl Ptr { btf_type, } } + + /// The target type of the pointer. + pub fn btf_type(&self) -> u32 { + self.btf_type + } } #[repr(C)] @@ -187,6 +206,11 @@ impl Typedef { btf_type, } } + + /// The target type of the typedef. + pub fn btf_type(&self) -> u32 { + self.btf_type + } } #[repr(C)] @@ -276,6 +300,11 @@ impl Func { pub(crate) fn set_linkage(&mut self, linkage: FuncLinkage) { self.info = (self.info & 0xFFFF0000) | (linkage as u32) & 0xFFFF; } + + /// The target type of the func. + pub fn btf_type(&self) -> u32 { + self.btf_type + } } #[repr(C)] @@ -378,17 +407,23 @@ impl Int { } } - pub(crate) fn encoding(&self) -> IntEncoding { + /// The integer size. + pub fn size(&self) -> u32 { + self.size + } + + /// The integer encoding. + pub fn encoding(&self) -> IntEncoding { ((self.data & 0x0f000000) >> 24).into() } - pub(crate) fn offset(&self) -> u32 { + /// The offset of the integer. + pub fn offset(&self) -> u32 { (self.data & 0x00ff0000) >> 16 } - // TODO: Remove directive this when this crate is pub - #[cfg(test)] - pub(crate) fn bits(&self) -> u32 { + /// The size of the integer in bits. + pub fn bits(&self) -> u32 { self.data & 0x000000ff } } @@ -563,15 +598,27 @@ impl Enum64 { } } } - +/// A member of a struct or union. #[repr(C)] #[derive(Clone, Debug)] -pub(crate) struct BtfMember { +pub struct BtfMember { pub(crate) name_offset: u32, pub(crate) btf_type: u32, pub(crate) offset: u32, } +impl BtfMember { + /// The offset of the members name in the BTF string section. + pub fn name_offset(&self) -> u32 { + self.name_offset + } + + /// The type of the member. + pub fn btf_type(&self) -> u32 { + self.btf_type + } +} + #[repr(C)] #[derive(Clone, Debug)] pub struct Struct { @@ -632,7 +679,8 @@ impl Struct { } } - pub(crate) fn member_bit_offset(&self, member: &BtfMember) -> usize { + /// The offset of the member within the struct or union. + pub fn member_bit_offset(&self, member: &BtfMember) -> usize { let k_flag = self.info >> 31 == 1; let bit_offset = if k_flag { member.offset & 0xFFFFFF @@ -649,6 +697,21 @@ impl Struct { size as usize } + + /// The size of the struct. + pub fn size(&self) -> u32 { + self.size + } + + /// Vlen + pub fn vlen(&self) -> usize { + type_vlen(self.info) + } + + /// The members of the struct. + pub fn members(&self) -> &[BtfMember] { + &self.members + } } #[repr(C)] @@ -771,6 +834,14 @@ impl Array { mem::size_of::() } + pub fn index_type(&self) -> u32 { + self.array.index_type + } + + pub fn len(&self) -> u32 { + self.array.len + } + #[cfg(test)] pub(crate) fn new(name_offset: u32, element_type: u32, index_type: u32, len: u32) -> Self { let info = (BtfKind::Array as u32) << 24; @@ -790,8 +861,24 @@ impl Array { #[repr(C)] #[derive(Clone, Debug)] pub struct BtfParam { - pub name_offset: u32, - pub btf_type: u32, + pub(crate) name_offset: u32, + pub(crate) btf_type: u32, +} + +impl BtfParam { + pub fn new(name_offset: u32, btf_type: u32) -> Self { + Self { + name_offset, + btf_type, + } + } + + pub fn name_offset(&self) -> u32 { + self.name_offset + } + pub fn btf_type(&self) -> u32 { + self.btf_type + } } #[repr(C)] @@ -836,6 +923,18 @@ impl FuncProto { mem::size_of::() + mem::size_of::() * self.params.len() } + pub fn return_type(&self) -> u32 { + self.return_type + } + + pub fn vlen(&self) -> usize { + type_vlen(self.info) + } + + pub fn params(&self) -> &[BtfParam] { + &self.params + } + pub fn new(params: Vec, return_type: u32) -> Self { let mut info = (BtfKind::FuncProto as u32) << 24; info |= (params.len() as u32) & 0xFFFF; @@ -911,14 +1010,43 @@ impl Var { linkage, } } + + pub fn btf_type(&self) -> u32 { + self.btf_type + } + + pub fn linkage(&self) -> &VarLinkage { + &self.linkage + } } #[repr(C)] #[derive(Clone, Debug)] pub struct DataSecEntry { - pub btf_type: u32, - pub offset: u32, - pub size: u32, + pub(crate) btf_type: u32, + pub(crate) offset: u32, + pub(crate) size: u32, +} + +impl DataSecEntry { + pub fn new(btf_type: u32, offset: u32, size: u32) -> Self { + Self { + btf_type, + offset, + size, + } + } + pub fn btf_type(&self) -> u32 { + self.btf_type + } + + pub fn offset(&self) -> u32 { + self.offset + } + + pub fn size(&self) -> u32 { + self.size + } } #[repr(C)] @@ -980,6 +1108,18 @@ impl DataSec { entries, } } + + pub fn size(&self) -> u32 { + self.size + } + + pub fn vlen(&self) -> usize { + type_vlen(self.info) + } + + pub fn entries(&self) -> &[DataSecEntry] { + &self.entries + } } #[repr(C)] @@ -1337,7 +1477,8 @@ impl BtfType { } } - pub(crate) fn name_offset(&self) -> u32 { + /// Returns the name offset of the BTF type. + pub fn name_offset(&self) -> u32 { match self { BtfType::Unknown => 0, BtfType::Fwd(t) => t.name_offset, @@ -1362,7 +1503,8 @@ impl BtfType { } } - pub(crate) fn kind(&self) -> BtfKind { + /// Returns the kind of the BTF type. + pub fn kind(&self) -> BtfKind { match self { BtfType::Unknown => BtfKind::Unknown, BtfType::Fwd(t) => t.kind(), diff --git a/aya-tool/Cargo.toml b/aya-tool/Cargo.toml index 6780e2ba..ea5566c3 100644 --- a/aya-tool/Cargo.toml +++ b/aya-tool/Cargo.toml @@ -9,8 +9,10 @@ homepage.workspace = true edition.workspace = true [dependencies] +aya-obj = { version = "0.1.0", path = "../aya-obj", features = ["std"] } bindgen = { workspace = true, default-features = true } clap = { workspace = true, default-features = true, features = ["derive"] } anyhow = { workspace = true, default-features = true } thiserror = { workspace = true } tempfile = { workspace = true } +object = { workspace = true } diff --git a/aya-tool/src/bin/aya-tool.rs b/aya-tool/src/bin/aya-tool.rs index 759131d3..5e69b5ca 100644 --- a/aya-tool/src/bin/aya-tool.rs +++ b/aya-tool/src/bin/aya-tool.rs @@ -1,5 +1,6 @@ use std::{path::PathBuf, process::exit}; +use aya_tool::btf::print_btf; use aya_tool::generate::{generate, InputFile}; use clap::Parser; @@ -23,6 +24,12 @@ enum Command { #[clap(last = true, action)] bindgen_args: Vec, }, + /// Pretty print an ELF file's BTF + #[clap(name = "print-btf", action)] + PrintBtf { + /// The ELF file to print BTF for + target: PathBuf, + }, } fn main() { @@ -48,6 +55,9 @@ fn try_main() -> Result<(), anyhow::Error> { }; println!("{bindings}"); } + Command::PrintBtf { target } => { + print_btf(target)?; + } }; Ok(()) diff --git a/aya-tool/src/btf.rs b/aya-tool/src/btf.rs new file mode 100644 index 00000000..672bd773 --- /dev/null +++ b/aya-tool/src/btf.rs @@ -0,0 +1,119 @@ +use aya_obj::btf::{Btf, BtfType, IntEncoding, VarLinkage}; +use object::Endianness; + +use std::{fmt::Write, path::Path}; + +pub fn print_btf>(path: P) -> anyhow::Result<()> { + let btf = Btf::parse_elf_file(path, Endianness::default())?; + for (i, t) in btf.types().enumerate().skip(1) { + let kind = t.kind(); + let name = if t.name_offset() > 0 { + format!( + "'{}'", + btf.string_at(t.name_offset()) + .unwrap_or(std::borrow::Cow::Borrowed("")) + ) + } else { + "''".to_owned() + }; + let info = match t { + BtfType::Unknown => "".to_string(), + BtfType::Fwd(_) => "type_id=0".to_string(), + BtfType::Const(ty) => format!("type_id={}", ty.btf_type()), + BtfType::Volatile(ty) => format!("type_id={}", ty.btf_type()), + BtfType::Restrict(ty) => format!("type_id={}", ty.btf_type()), + BtfType::Ptr(ty) => format!("type_id={}", ty.btf_type()), + BtfType::Typedef(ty) => format!("type_id={}", ty.btf_type()), + BtfType::Func(ty) => format!("type_id={}", ty.btf_type()), + BtfType::Int(i) => { + let encoding = match i.encoding() { + IntEncoding::Signed => "(signed)", + IntEncoding::Char => "(char)", + IntEncoding::Bool => "(bool)", + IntEncoding::None => "(none)", + IntEncoding::Unknown => "(unknown)", + }; + format!( + "size={} bits_offset={} nr_bits={} encoding={}", + i.size(), + i.offset(), + i.bits(), + encoding, + ) + } + BtfType::Float(_) => todo!(), + BtfType::Enum(_) => todo!(), + BtfType::Array(ty) => { + format!( + "type_id=0 index_type_id={} nr_elems={}", + ty.index_type(), + ty.len() + ) + } + BtfType::Struct(ty) => { + let size = ty.size(); + let vlen = ty.vlen(); + let mut out = format!("size={} vlen={}", size, vlen,); + for m in ty.members() { + let name = btf + .string_at(m.name_offset()) + .unwrap_or(std::borrow::Cow::Borrowed("")); + let type_id = m.btf_type(); + let offset = ty.member_bit_offset(m); + write!(out, "\n\t'{name}' type_id={type_id} bits_offset={offset}")?; + } + out + } + BtfType::Union(_) => todo!(), + BtfType::FuncProto(ty) => { + let ret_type_id = ty.return_type(); + let vlen = ty.vlen(); + let mut out = format!("ret_type_id={ret_type_id} vlen={vlen}"); + for p in ty.params() { + let name = btf + .string_at(p.name_offset()) + .unwrap_or(std::borrow::Cow::Borrowed("")); + let type_id = p.btf_type(); + write!(out, "\n\t'{name}' type_id={type_id}")?; + } + out + } + BtfType::Var(ty) => { + let type_id = ty.btf_type(); + let linkage = match ty.linkage() { + VarLinkage::Static => "static".to_owned(), + VarLinkage::Global => "global".to_owned(), + VarLinkage::Extern => "extern".to_owned(), + VarLinkage::Unknown => "unknown".to_owned(), + }; + format!("type_id={type_id} linkage={linkage}") + } + BtfType::DataSec(ty) => { + let size = ty.size(); + let vlen = ty.vlen(); + let mut out = format!("size={size} vlen={vlen}"); + for entry in ty.entries() { + let points_to = btf.type_by_id(entry.btf_type()).unwrap(); + let name = btf + .string_at(points_to.name_offset()) + .unwrap_or(std::borrow::Cow::Borrowed("")); + write!( + out, + "\n\ttype_id={} offset={} size={} ({} '{}')", + entry.btf_type(), + entry.offset(), + entry.size(), + points_to.kind(), + name + )?; + } + out + } + BtfType::DeclTag(_) => unimplemented!("decl tag formatting not implemented"), + BtfType::TypeTag(_) => unimplemented!("type tag formatting not implemented"), + BtfType::Enum64(_) => unimplemented!("enum64 formatting not implemented"), + }; + println!("[{i}] {kind} {name} {info}"); + } + Ok(()) +} diff --git a/aya-tool/src/lib.rs b/aya-tool/src/lib.rs index c220e524..303dc68c 100644 --- a/aya-tool/src/lib.rs +++ b/aya-tool/src/lib.rs @@ -5,6 +5,7 @@ use std::{ }; pub mod bindgen; +pub mod btf; pub mod generate; pub mod rustfmt; diff --git a/aya/src/sys/bpf.rs b/aya/src/sys/bpf.rs index a971234f..87a5acc2 100644 --- a/aya/src/sys/bpf.rs +++ b/aya/src/sys/bpf.rs @@ -849,14 +849,8 @@ pub(crate) fn is_btf_func_supported() -> bool { let a_name = btf.add_string("a"); let b_name = btf.add_string("b"); let params = vec![ - BtfParam { - name_offset: a_name, - btf_type: int_type_id, - }, - BtfParam { - name_offset: b_name, - btf_type: int_type_id, - }, + BtfParam::new(a_name, int_type_id), + BtfParam::new(b_name, int_type_id), ]; let func_proto = BtfType::FuncProto(FuncProto::new(params, int_type_id)); let func_proto_type_id = btf.add_type(func_proto); @@ -879,14 +873,8 @@ pub(crate) fn is_btf_func_global_supported() -> bool { let a_name = btf.add_string("a"); let b_name = btf.add_string("b"); let params = vec![ - BtfParam { - name_offset: a_name, - btf_type: int_type_id, - }, - BtfParam { - name_offset: b_name, - btf_type: int_type_id, - }, + BtfParam::new(a_name, int_type_id), + BtfParam::new(b_name, int_type_id), ]; let func_proto = BtfType::FuncProto(FuncProto::new(params, int_type_id)); let func_proto_type_id = btf.add_type(func_proto); @@ -911,11 +899,7 @@ pub(crate) fn is_btf_datasec_supported() -> bool { let var_type_id = btf.add_type(var_type); let name_offset = btf.add_string(".data"); - let variables = vec![DataSecEntry { - btf_type: var_type_id, - offset: 0, - size: 4, - }]; + let variables = vec![DataSecEntry::new(var_type_id, 0, 4)]; let datasec_type = BtfType::DataSec(DataSec::new(name_offset, variables, 4)); btf.add_type(datasec_type);