wip: bpftool but with aya

Signed-off-by: Dave Tucker <dave@dtucker.co.uk>
pull/902/head
Dave Tucker 7 months ago
parent d6dfd678f0
commit 1d752136b9

@ -46,6 +46,11 @@ pub enum BtfError {
#[error("error parsing BTF header")] #[error("error parsing BTF header")]
InvalidHeader, InvalidHeader,
/// Invalid ELF data
#[cfg(feature = "std")]
#[error("error parsing ELF")]
Elf,
/// invalid BTF type info segment /// invalid BTF type info segment
#[error("invalid BTF type info segment")] #[error("invalid BTF type info segment")]
InvalidTypeInfo, InvalidTypeInfo,
@ -276,7 +281,8 @@ impl Btf {
self.types.types.len() < 2 self.types.types.len() < 2
} }
pub(crate) fn types(&self) -> impl Iterator<Item = &BtfType> { /// Iterates over the BTF types.
pub fn types(&self) -> impl Iterator<Item = &BtfType> {
self.types.types.iter() self.types.types.iter()
} }
@ -307,7 +313,7 @@ impl Btf {
/// Loads BTF metadata from the given `path`. /// Loads BTF metadata from the given `path`.
#[cfg(feature = "std")] #[cfg(feature = "std")]
pub fn parse_file<P: AsRef<std::path::Path>>( pub(crate) fn parse_file<P: AsRef<std::path::Path>>(
path: P, path: P,
endianness: Endianness, endianness: Endianness,
) -> Result<Btf, BtfError> { ) -> Result<Btf, BtfError> {
@ -322,6 +328,26 @@ impl Btf {
) )
} }
/// Loads BTF metadata from an ELF section.
#[cfg(feature = "std")]
pub fn parse_elf_file<P: AsRef<std::path::Path>>(
path: P,
endianness: Endianness,
) -> Result<Btf, BtfError> {
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 /// Parses BTF from binary data of the given endianness
pub fn parse(data: &[u8], endianness: Endianness) -> Result<Btf, BtfError> { pub fn parse(data: &[u8], endianness: Endianness) -> Result<Btf, BtfError> {
if data.len() < mem::size_of::<btf_header>() { if data.len() < mem::size_of::<btf_header>() {
@ -374,7 +400,8 @@ impl Btf {
Ok(types) Ok(types)
} }
pub(crate) fn string_at(&self, offset: u32) -> Result<Cow<'_, str>, BtfError> { /// Returns the string at the given offset within the BTF metadata.
pub fn string_at(&self, offset: u32) -> Result<Cow<'_, str>, BtfError> {
let btf_header { let btf_header {
hdr_len, hdr_len,
mut str_off, mut str_off,
@ -400,7 +427,8 @@ impl Btf {
Ok(s.to_string_lossy()) 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) self.types.type_by_id(type_id)
} }

@ -82,6 +82,10 @@ impl Const {
btf_type, btf_type,
} }
} }
/// The target type of the const.
pub fn btf_type(&self) -> u32 {
self.btf_type
}
} }
#[repr(C)] #[repr(C)]
@ -104,6 +108,11 @@ impl Volatile {
pub(crate) fn type_info_size(&self) -> usize { pub(crate) fn type_info_size(&self) -> usize {
mem::size_of::<Self>() mem::size_of::<Self>()
} }
/// The target type of the volatile.
pub fn btf_type(&self) -> u32 {
self.btf_type
}
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -125,6 +134,11 @@ impl Restrict {
pub(crate) fn type_info_size(&self) -> usize { pub(crate) fn type_info_size(&self) -> usize {
mem::size_of::<Self>() mem::size_of::<Self>()
} }
/// The target type of the restrict.
pub fn btf_type(&self) -> u32 {
self.btf_type
}
} }
#[repr(C)] #[repr(C)]
@ -156,6 +170,11 @@ impl Ptr {
btf_type, btf_type,
} }
} }
/// The target type of the pointer.
pub fn btf_type(&self) -> u32 {
self.btf_type
}
} }
#[repr(C)] #[repr(C)]
@ -187,6 +206,11 @@ impl Typedef {
btf_type, btf_type,
} }
} }
/// The target type of the typedef.
pub fn btf_type(&self) -> u32 {
self.btf_type
}
} }
#[repr(C)] #[repr(C)]
@ -276,6 +300,11 @@ impl Func {
pub(crate) fn set_linkage(&mut self, linkage: FuncLinkage) { pub(crate) fn set_linkage(&mut self, linkage: FuncLinkage) {
self.info = (self.info & 0xFFFF0000) | (linkage as u32) & 0xFFFF; 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)] #[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() ((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 (self.data & 0x00ff0000) >> 16
} }
// TODO: Remove directive this when this crate is pub /// The size of the integer in bits.
#[cfg(test)] pub fn bits(&self) -> u32 {
pub(crate) fn bits(&self) -> u32 {
self.data & 0x000000ff self.data & 0x000000ff
} }
} }
@ -563,15 +598,27 @@ impl Enum64 {
} }
} }
} }
/// A member of a struct or union.
#[repr(C)] #[repr(C)]
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub(crate) struct BtfMember { pub struct BtfMember {
pub(crate) name_offset: u32, pub(crate) name_offset: u32,
pub(crate) btf_type: u32, pub(crate) btf_type: u32,
pub(crate) offset: 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)] #[repr(C)]
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Struct { 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 k_flag = self.info >> 31 == 1;
let bit_offset = if k_flag { let bit_offset = if k_flag {
member.offset & 0xFFFFFF member.offset & 0xFFFFFF
@ -649,6 +697,21 @@ impl Struct {
size as usize 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)] #[repr(C)]
@ -771,6 +834,14 @@ impl Array {
mem::size_of::<Self>() mem::size_of::<Self>()
} }
pub fn index_type(&self) -> u32 {
self.array.index_type
}
pub fn len(&self) -> u32 {
self.array.len
}
#[cfg(test)] #[cfg(test)]
pub(crate) fn new(name_offset: u32, element_type: u32, index_type: u32, len: u32) -> Self { pub(crate) fn new(name_offset: u32, element_type: u32, index_type: u32, len: u32) -> Self {
let info = (BtfKind::Array as u32) << 24; let info = (BtfKind::Array as u32) << 24;
@ -790,8 +861,24 @@ impl Array {
#[repr(C)] #[repr(C)]
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct BtfParam { pub struct BtfParam {
pub name_offset: u32, pub(crate) name_offset: u32,
pub btf_type: 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)] #[repr(C)]
@ -836,6 +923,18 @@ impl FuncProto {
mem::size_of::<Fwd>() + mem::size_of::<BtfParam>() * self.params.len() mem::size_of::<Fwd>() + mem::size_of::<BtfParam>() * 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<BtfParam>, return_type: u32) -> Self { pub fn new(params: Vec<BtfParam>, return_type: u32) -> Self {
let mut info = (BtfKind::FuncProto as u32) << 24; let mut info = (BtfKind::FuncProto as u32) << 24;
info |= (params.len() as u32) & 0xFFFF; info |= (params.len() as u32) & 0xFFFF;
@ -911,14 +1010,43 @@ impl Var {
linkage, linkage,
} }
} }
pub fn btf_type(&self) -> u32 {
self.btf_type
}
pub fn linkage(&self) -> &VarLinkage {
&self.linkage
}
} }
#[repr(C)] #[repr(C)]
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct DataSecEntry { pub struct DataSecEntry {
pub btf_type: u32, pub(crate) btf_type: u32,
pub offset: u32, pub(crate) offset: u32,
pub size: 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)] #[repr(C)]
@ -980,6 +1108,18 @@ impl DataSec {
entries, 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)] #[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 { match self {
BtfType::Unknown => 0, BtfType::Unknown => 0,
BtfType::Fwd(t) => t.name_offset, 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 { match self {
BtfType::Unknown => BtfKind::Unknown, BtfType::Unknown => BtfKind::Unknown,
BtfType::Fwd(t) => t.kind(), BtfType::Fwd(t) => t.kind(),

@ -9,8 +9,10 @@ homepage.workspace = true
edition.workspace = true edition.workspace = true
[dependencies] [dependencies]
aya-obj = { version = "0.1.0", path = "../aya-obj", features = ["std"] }
bindgen = { workspace = true, default-features = true } bindgen = { workspace = true, default-features = true }
clap = { workspace = true, default-features = true, features = ["derive"] } clap = { workspace = true, default-features = true, features = ["derive"] }
anyhow = { workspace = true, default-features = true } anyhow = { workspace = true, default-features = true }
thiserror = { workspace = true } thiserror = { workspace = true }
tempfile = { workspace = true } tempfile = { workspace = true }
object = { workspace = true }

@ -1,5 +1,6 @@
use std::{path::PathBuf, process::exit}; use std::{path::PathBuf, process::exit};
use aya_tool::btf::print_btf;
use aya_tool::generate::{generate, InputFile}; use aya_tool::generate::{generate, InputFile};
use clap::Parser; use clap::Parser;
@ -23,6 +24,12 @@ enum Command {
#[clap(last = true, action)] #[clap(last = true, action)]
bindgen_args: Vec<String>, bindgen_args: Vec<String>,
}, },
/// Pretty print an ELF file's BTF
#[clap(name = "print-btf", action)]
PrintBtf {
/// The ELF file to print BTF for
target: PathBuf,
},
} }
fn main() { fn main() {
@ -48,6 +55,9 @@ fn try_main() -> Result<(), anyhow::Error> {
}; };
println!("{bindings}"); println!("{bindings}");
} }
Command::PrintBtf { target } => {
print_btf(target)?;
}
}; };
Ok(()) Ok(())

@ -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<P: AsRef<Path>>(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(())
}

@ -5,6 +5,7 @@ use std::{
}; };
pub mod bindgen; pub mod bindgen;
pub mod btf;
pub mod generate; pub mod generate;
pub mod rustfmt; pub mod rustfmt;

@ -849,14 +849,8 @@ pub(crate) fn is_btf_func_supported() -> bool {
let a_name = btf.add_string("a"); let a_name = btf.add_string("a");
let b_name = btf.add_string("b"); let b_name = btf.add_string("b");
let params = vec![ let params = vec![
BtfParam { BtfParam::new(a_name, int_type_id),
name_offset: a_name, BtfParam::new(b_name, int_type_id),
btf_type: int_type_id,
},
BtfParam {
name_offset: b_name,
btf_type: int_type_id,
},
]; ];
let func_proto = BtfType::FuncProto(FuncProto::new(params, int_type_id)); let func_proto = BtfType::FuncProto(FuncProto::new(params, int_type_id));
let func_proto_type_id = btf.add_type(func_proto); 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 a_name = btf.add_string("a");
let b_name = btf.add_string("b"); let b_name = btf.add_string("b");
let params = vec![ let params = vec![
BtfParam { BtfParam::new(a_name, int_type_id),
name_offset: a_name, BtfParam::new(b_name, int_type_id),
btf_type: int_type_id,
},
BtfParam {
name_offset: b_name,
btf_type: int_type_id,
},
]; ];
let func_proto = BtfType::FuncProto(FuncProto::new(params, int_type_id)); let func_proto = BtfType::FuncProto(FuncProto::new(params, int_type_id));
let func_proto_type_id = btf.add_type(func_proto); 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 var_type_id = btf.add_type(var_type);
let name_offset = btf.add_string(".data"); let name_offset = btf.add_string(".data");
let variables = vec![DataSecEntry { let variables = vec![DataSecEntry::new(var_type_id, 0, 4)];
btf_type: var_type_id,
offset: 0,
size: 4,
}];
let datasec_type = BtfType::DataSec(DataSec::new(name_offset, variables, 4)); let datasec_type = BtfType::DataSec(DataSec::new(name_offset, variables, 4));
btf.add_type(datasec_type); btf.add_type(datasec_type);

Loading…
Cancel
Save