aya: Export BTF crate

Allows users to inspect program BTF for metadata.
Allows additional use cases for users who wish only to deal with BTF
parsing.

Signed-off-by: Dave Tucker <dave@dtucker.co.uk>
pull/283/head
Dave Tucker 3 years ago
parent 7d8365c351
commit 512d86bba0

@ -36,11 +36,11 @@ jobs:
- name: Run clippy - name: Run clippy
run: | run: |
cargo clippy -p aya -- --deny warnings cargo clippy --all-features -p aya -- --deny warnings
cargo clippy -p aya-gen -- --deny warnings cargo clippy --all-features -p aya-gen -- --deny warnings
cargo clippy -p xtask -- --deny warnings cargo clippy --all-features -p xtask -- --deny warnings
pushd bpf pushd bpf
cargo clippy -p aya-bpf -- --deny warnings cargo clippy --all-features -p aya-bpf -- --deny warnings
popd popd
- name: Run miri - name: Run miri

@ -1,3 +1,4 @@
{ {
"rust-analyzer.linkedProjects": ["Cargo.toml", "bpf/Cargo.toml"] "rust-analyzer.linkedProjects": ["Cargo.toml", "bpf/Cargo.toml"],
"rust-analyzer.cargo.allFeatures": true
} }

@ -1,3 +1,4 @@
{ {
"rust-analyzer.linkedProjects": ["Cargo.toml", "bpf/Cargo.toml"] "rust-analyzer.linkedProjects": ["Cargo.toml", "bpf/Cargo.toml"],
"rust-analyzer.cargo.allFeatures": true
} }

@ -1,2 +1,2 @@
[workspace] [workspace]
members = ["aya", "aya-gen", "xtask"] members = ["aya", "aya-gen", "btftool", "xtask"]

@ -32,3 +32,4 @@ default = []
async = ["futures"] async = ["futures"]
async_tokio = ["tokio", "async"] async_tokio = ["tokio", "async"]
async_std = ["async-std", "async-io", "async"] async_std = ["async-std", "async-io", "async"]
btf = []

@ -530,7 +530,11 @@ impl<'a> BpfLoader<'a> {
.drain() .drain()
.map(|(name, map)| (name, MapLock::new(map))) .map(|(name, map)| (name, MapLock::new(map)))
.collect(); .collect();
Ok(Bpf { maps, programs }) Ok(Bpf {
maps,
programs,
btf: obj.btf,
})
} }
} }
@ -543,6 +547,7 @@ impl<'a> Default for BpfLoader<'a> {
/// The main entry point into the library, used to work with eBPF programs and maps. /// The main entry point into the library, used to work with eBPF programs and maps.
#[derive(Debug)] #[derive(Debug)]
pub struct Bpf { pub struct Bpf {
btf: Option<Btf>,
maps: HashMap<String, MapLock>, maps: HashMap<String, MapLock>,
programs: HashMap<String, Program>, programs: HashMap<String, Program>,
} }
@ -744,6 +749,20 @@ impl Bpf {
pub fn programs_mut(&mut self) -> impl Iterator<Item = (&str, &mut Program)> { pub fn programs_mut(&mut self) -> impl Iterator<Item = (&str, &mut Program)> {
self.programs.iter_mut().map(|(s, p)| (s.as_str(), p)) self.programs.iter_mut().map(|(s, p)| (s.as_str(), p))
} }
/// Retrive the object BTF.
///
/// # Examples
/// ```no_run
/// # use std::path::Path;
/// # let mut bpf = aya::Bpf::load(&[])?;
/// let btf = bpf.btf().unwrap();
/// # Ok::<(), aya::BpfError>(())
/// ```
#[cfg(feature = "btf")]
pub fn btf(&mut self) -> Option<&Btf> {
self.btf.as_ref()
}
} }
/// The error type returned by [`Bpf::load_file`] and [`Bpf::load`]. /// The error type returned by [`Bpf::load_file`] and [`Bpf::load`].

@ -46,5 +46,10 @@ mod sys;
pub mod util; pub mod util;
pub use bpf::*; pub use bpf::*;
#[cfg(feature = "btf")]
pub use obj::btf;
pub use obj::btf::{Btf, BtfError}; pub use obj::btf::{Btf, BtfError};
pub use object::Endianness; pub use object::Endianness;

@ -27,7 +27,6 @@ use super::{
}; };
pub(crate) const MAX_RESOLVE_DEPTH: u8 = 32; pub(crate) const MAX_RESOLVE_DEPTH: u8 = 32;
pub(crate) const MAX_SPEC_LEN: usize = 64;
/// The error type returned when `BTF` operations fail. /// The error type returned when `BTF` operations fail.
#[derive(Error, Debug)] #[derive(Error, Debug)]
@ -193,7 +192,8 @@ impl Btf {
} }
} }
pub(crate) fn types(&self) -> impl Iterator<Item = &BtfType> { /// Returns an iterator over the BTF types.
pub fn types(&self) -> impl Iterator<Item = &BtfType> {
self.types.types.iter() self.types.types.iter()
} }
@ -231,7 +231,8 @@ impl Btf {
) )
} }
pub(crate) fn parse(data: &[u8], endianness: Endianness) -> Result<Btf, BtfError> { /// Parses a byte slice into BTF.
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>() {
return Err(BtfError::InvalidHeader); return Err(BtfError::InvalidHeader);
} }
@ -282,7 +283,8 @@ impl Btf {
Ok(types) Ok(types)
} }
pub(crate) fn string_at(&self, offset: u32) -> Result<Cow<'_, str>, BtfError> { /// Returns the string at the provided offset.
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,
@ -308,15 +310,18 @@ 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 BTF type for a given 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)
} }
pub(crate) fn resolve_type(&self, root_type_id: u32) -> Result<u32, BtfError> { /// Resolves the base type id by traversing the type heirarchy starting from root_type_id.
pub fn resolve_type(&self, root_type_id: u32) -> Result<u32, BtfError> {
self.types.resolve_type(root_type_id) self.types.resolve_type(root_type_id)
} }
pub(crate) fn type_name(&self, ty: &BtfType) -> Result<Option<Cow<'_, str>>, BtfError> { /// Returns the name of the BTF type.
pub fn type_name(&self, ty: &BtfType) -> Result<Option<Cow<'_, str>>, BtfError> {
ty.name_offset().map(|off| self.string_at(off)).transpose() ty.name_offset().map(|off| self.string_at(off)).transpose()
} }
@ -325,7 +330,8 @@ impl Btf {
.and_then(|off| self.string_at(off).ok().map(String::from)) .and_then(|off| self.string_at(off).ok().map(String::from))
} }
pub(crate) fn id_by_type_name_kind(&self, name: &str, kind: BtfKind) -> Result<u32, BtfError> { /// Returns the BTF id of the type with the provided name and of the provided kind.
pub fn id_by_type_name_kind(&self, name: &str, kind: BtfKind) -> Result<u32, BtfError> {
for (type_id, ty) in self.types().enumerate() { for (type_id, ty) in self.types().enumerate() {
match ty.kind()? { match ty.kind()? {
Some(k) => { Some(k) => {
@ -399,7 +405,8 @@ impl Btf {
}) })
} }
pub(crate) fn to_bytes(&self) -> Vec<u8> { /// Writes the BTF to bytes.
pub fn to_bytes(&self) -> Vec<u8> {
// Safety: btf_header is POD // Safety: btf_header is POD
let mut buf = unsafe { bytes_of::<btf_header>(&self.header).to_vec() }; let mut buf = unsafe { bytes_of::<btf_header>(&self.header).to_vec() };
// Skip the first type since it's always BtfType::Unknown for type_by_id to work // Skip the first type since it's always BtfType::Unknown for type_by_id to work
@ -597,6 +604,7 @@ unsafe fn read_btf_header(data: &[u8]) -> btf_header {
ptr::read_unaligned(data.as_ptr() as *const btf_header) ptr::read_unaligned(data.as_ptr() as *const btf_header)
} }
/// Extended BTF Information
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct BtfExt { pub struct BtfExt {
data: Vec<u8>, data: Vec<u8>,
@ -611,11 +619,8 @@ pub struct BtfExt {
} }
impl BtfExt { impl BtfExt {
pub(crate) fn parse( /// Parses a byte slice to BtfExt using the provided BTF.
data: &[u8], pub fn parse(data: &[u8], endianness: Endianness, btf: &Btf) -> Result<BtfExt, BtfError> {
endianness: Endianness,
btf: &Btf,
) -> Result<BtfExt, BtfError> {
// Safety: btf_ext_header is POD so read_unaligned is safe // Safety: btf_ext_header is POD so read_unaligned is safe
let header = unsafe { let header = unsafe {
ptr::read_unaligned::<btf_ext_header>(data.as_ptr() as *const btf_ext_header) ptr::read_unaligned::<btf_ext_header>(data.as_ptr() as *const btf_ext_header)
@ -757,9 +762,19 @@ impl BtfExt {
self.func_info_rec_size self.func_info_rec_size
} }
/// Returns the function information
pub fn func_info(&self) -> &FuncInfo {
&self.func_info
}
pub(crate) fn line_info_rec_size(&self) -> usize { pub(crate) fn line_info_rec_size(&self) -> usize {
self.line_info_rec_size self.line_info_rec_size
} }
/// Returns the line information
pub fn line_info(&self) -> &LineInfo {
&self.line_info
}
} }
pub(crate) struct SecInfoIter<'a> { pub(crate) struct SecInfoIter<'a> {

@ -20,7 +20,7 @@ use crate::{
* ...... * ......
*/ */
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub(crate) struct FuncSecInfo { pub struct FuncSecInfo {
pub _sec_name_offset: u32, pub _sec_name_offset: u32,
pub num_info: u32, pub num_info: u32,
pub func_info: Vec<bpf_func_info>, pub func_info: Vec<bpf_func_info>,
@ -79,7 +79,7 @@ impl FuncSecInfo {
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct FuncInfo { pub struct FuncInfo {
pub data: HashMap<String, FuncSecInfo>, pub data: HashMap<String, FuncSecInfo>,
} }
@ -99,7 +99,7 @@ impl FuncInfo {
} }
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub(crate) struct LineSecInfo { pub struct LineSecInfo {
// each line info section has a header // each line info section has a header
pub _sec_name_offset: u32, pub _sec_name_offset: u32,
pub num_info: u32, pub num_info: u32,
@ -169,7 +169,7 @@ impl LineSecInfo {
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct LineInfo { pub struct LineInfo {
pub data: HashMap<String, LineSecInfo>, pub data: HashMap<String, LineSecInfo>,
} }

@ -1,3 +1,4 @@
//! BPF Type Format
#[allow(clippy::module_inception)] #[allow(clippy::module_inception)]
mod btf; mod btf;
mod info; mod info;
@ -6,5 +7,10 @@ mod types;
pub use btf::*; pub use btf::*;
pub(crate) use info::*; pub(crate) use info::*;
pub use relocation::RelocationError; pub(crate) use relocation::*;
#[cfg(feature = "btf")]
pub use types::*;
#[cfg(not(feature = "btf"))]
pub(crate) use types::*; pub(crate) use types::*;

@ -2,7 +2,6 @@ use std::{
collections::HashMap, collections::HashMap,
convert::{TryFrom, TryInto}, convert::{TryFrom, TryInto},
io, mem, ptr, io, mem, ptr,
str::FromStr,
}; };
use thiserror::Error; use thiserror::Error;
@ -15,21 +14,19 @@ use crate::{
obj::{ obj::{
btf::{ btf::{
fields_are_compatible, member_bit_field_size, member_bit_offset, types_are_compatible, fields_are_compatible, member_bit_field_size, member_bit_offset, types_are_compatible,
BtfType, MAX_SPEC_LEN, BtfType,
}, },
Btf, BtfError, Object, Program, ProgramSection, Btf, BtfError,
}, },
BpfError,
}; };
const MAX_SPEC_LEN: usize = 64;
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum RelocationError { pub enum BtfRelocationError {
#[error(transparent)] #[error(transparent)]
IOError(#[from] io::Error), IOError(#[from] io::Error),
#[error("program not found")]
ProgramNotFound,
#[error("invalid relocation access string {access_str}")] #[error("invalid relocation access string {access_str}")]
InvalidAccessString { access_str: String }, InvalidAccessString { access_str: String },
@ -151,59 +148,17 @@ impl Relocation {
} }
} }
impl Object { pub fn relocate_btf_program<'target>(
pub fn relocate_btf(&mut self, target_btf: &Btf) -> Result<(), BpfError> { instructions: &mut [bpf_insn],
let (local_btf, btf_ext) = match (&self.btf, &self.btf_ext) {
(Some(btf), Some(btf_ext)) => (btf, btf_ext),
_ => return Ok(()),
};
let mut candidates_cache = HashMap::<u32, Vec<Candidate>>::new();
for (sec_name_off, relos) in btf_ext.relocations() {
let section_name = local_btf.string_at(*sec_name_off)?;
let program_section = match ProgramSection::from_str(&section_name) {
Ok(program) => program,
Err(_) => continue,
};
let section_name = program_section.name();
let program = self
.programs
.get_mut(section_name)
.ok_or(BpfError::RelocationError {
function: section_name.to_owned(),
error: Box::new(RelocationError::ProgramNotFound),
})?;
match relocate_btf_program(program, relos, local_btf, target_btf, &mut candidates_cache)
{
Ok(_) => {}
Err(ErrorWrapper::BtfError(e)) => return Err(e.into()),
Err(ErrorWrapper::RelocationError(error)) => {
return Err(BpfError::RelocationError {
function: section_name.to_owned(),
error: Box::new(error),
})
}
}
}
Ok(())
}
}
fn relocate_btf_program<'target>(
program: &mut Program,
relos: &[Relocation], relos: &[Relocation],
local_btf: &Btf, local_btf: &Btf,
target_btf: &'target Btf, target_btf: &'target Btf,
candidates_cache: &mut HashMap<u32, Vec<Candidate<'target>>>, candidates_cache: &mut HashMap<u32, Vec<Candidate<'target>>>,
) -> Result<(), ErrorWrapper> { ) -> Result<(), ErrorWrapper> {
for rel in relos { for rel in relos {
let instructions = &mut program.function.instructions;
let ins_index = rel.ins_offset as usize / std::mem::size_of::<bpf_insn>(); let ins_index = rel.ins_offset as usize / std::mem::size_of::<bpf_insn>();
if ins_index >= instructions.len() { if ins_index >= instructions.len() {
return Err(RelocationError::InvalidInstructionIndex { return Err(BtfRelocationError::InvalidInstructionIndex {
index: ins_index, index: ins_index,
num_instructions: instructions.len(), num_instructions: instructions.len(),
relocation_number: rel.number, relocation_number: rel.number,
@ -261,7 +216,7 @@ fn relocate_btf_program<'target>(
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
if !conflicts.is_empty() { if !conflicts.is_empty() {
return Err(RelocationError::ConflictingCandidates { return Err(BtfRelocationError::ConflictingCandidates {
type_name: local_name.to_string(), type_name: local_name.to_string(),
candidates: conflicts, candidates: conflicts,
} }
@ -275,7 +230,7 @@ fn relocate_btf_program<'target>(
ComputedRelocation::new(rel, &local_spec, None)? ComputedRelocation::new(rel, &local_spec, None)?
}; };
comp_rel.apply(program, rel, local_btf, target_btf)?; comp_rel.apply(instructions, rel, local_btf, target_btf)?;
} }
Ok(()) Ok(())
@ -415,7 +370,7 @@ fn match_candidate<'target>(
} }
if target_spec.parts.len() == MAX_SPEC_LEN { if target_spec.parts.len() == MAX_SPEC_LEN {
return Err(RelocationError::MaximumNestingLevelReached { return Err(BtfRelocationError::MaximumNestingLevelReached {
type_name: Some(candidate.name.clone()), type_name: Some(candidate.name.clone()),
} }
.into()); .into());
@ -467,7 +422,7 @@ fn match_member<'local, 'target>(
for (index, target_member) in target_members.iter().enumerate() { for (index, target_member) in target_members.iter().enumerate() {
if target_spec.parts.len() == MAX_SPEC_LEN { if target_spec.parts.len() == MAX_SPEC_LEN {
let root_ty = target_spec.btf.type_by_id(target_spec.root_type_id)?; let root_ty = target_spec.btf.type_by_id(target_spec.root_type_id)?;
return Err(RelocationError::MaximumNestingLevelReached { return Err(BtfRelocationError::MaximumNestingLevelReached {
type_name: target_spec.btf.err_type_name(root_ty), type_name: target_spec.btf.err_type_name(root_ty),
} }
.into()); .into());
@ -535,7 +490,7 @@ impl<'a> AccessSpec<'a> {
.split(':') .split(':')
.map(|s| s.parse::<usize>()) .map(|s| s.parse::<usize>())
.collect::<Result<Vec<_>, _>>() .collect::<Result<Vec<_>, _>>()
.map_err(|_| RelocationError::InvalidAccessString { .map_err(|_| BtfRelocationError::InvalidAccessString {
access_str: spec.to_string(), access_str: spec.to_string(),
})?; })?;
@ -548,7 +503,7 @@ impl<'a> AccessSpec<'a> {
| RelocationKind::TypeExists | RelocationKind::TypeExists
| RelocationKind::TypeSize => { | RelocationKind::TypeSize => {
if parts != [0] { if parts != [0] {
return Err(RelocationError::InvalidAccessString { return Err(BtfRelocationError::InvalidAccessString {
access_str: spec.to_string(), access_str: spec.to_string(),
} }
.into()); .into());
@ -565,14 +520,14 @@ impl<'a> AccessSpec<'a> {
RelocationKind::EnumVariantExists | RelocationKind::EnumVariantValue => match ty { RelocationKind::EnumVariantExists | RelocationKind::EnumVariantValue => match ty {
BtfType::Enum(_, members) => { BtfType::Enum(_, members) => {
if parts.len() != 1 { if parts.len() != 1 {
return Err(RelocationError::InvalidAccessString { return Err(BtfRelocationError::InvalidAccessString {
access_str: spec.to_string(), access_str: spec.to_string(),
} }
.into()); .into());
} }
let index = parts[0]; let index = parts[0];
if index >= members.len() { if index >= members.len() {
return Err(RelocationError::InvalidAccessIndex { return Err(BtfRelocationError::InvalidAccessIndex {
type_name: btf.err_type_name(ty), type_name: btf.err_type_name(ty),
spec: spec.to_string(), spec: spec.to_string(),
index, index,
@ -597,7 +552,7 @@ impl<'a> AccessSpec<'a> {
} }
} }
_ => { _ => {
return Err(RelocationError::InvalidRelocationKindForType { return Err(BtfRelocationError::InvalidRelocationKindForType {
relocation_number: relocation.number, relocation_number: relocation.number,
relocation_kind: format!("{:?}", relocation.kind), relocation_kind: format!("{:?}", relocation.kind),
type_kind: format!("{:?}", ty.kind()?.unwrap()), type_kind: format!("{:?}", ty.kind()?.unwrap()),
@ -627,7 +582,7 @@ impl<'a> AccessSpec<'a> {
match ty { match ty {
Struct(t, members) | Union(t, members) => { Struct(t, members) | Union(t, members) => {
if index >= members.len() { if index >= members.len() {
return Err(RelocationError::InvalidAccessIndex { return Err(BtfRelocationError::InvalidAccessIndex {
type_name: btf.err_type_name(ty), type_name: btf.err_type_name(ty),
spec: spec.to_string(), spec: spec.to_string(),
index, index,
@ -664,7 +619,7 @@ impl<'a> AccessSpec<'a> {
} }
}; };
if !var_len && index >= array.nelems as usize { if !var_len && index >= array.nelems as usize {
return Err(RelocationError::InvalidAccessIndex { return Err(BtfRelocationError::InvalidAccessIndex {
type_name: btf.err_type_name(ty), type_name: btf.err_type_name(ty),
spec: spec.to_string(), spec: spec.to_string(),
index, index,
@ -682,7 +637,7 @@ impl<'a> AccessSpec<'a> {
bit_offset += index * size * 8; bit_offset += index * size * 8;
} }
rel_kind => { rel_kind => {
return Err(RelocationError::InvalidRelocationKindForType { return Err(BtfRelocationError::InvalidRelocationKindForType {
relocation_number: relocation.number, relocation_number: relocation.number,
relocation_kind: format!("{:?}", rel_kind), relocation_kind: format!("{:?}", rel_kind),
type_kind: format!("{:?}", ty.kind()), type_kind: format!("{:?}", ty.kind()),
@ -717,7 +672,7 @@ struct Accessor {
} }
#[derive(Debug)] #[derive(Debug)]
struct Candidate<'a> { pub struct Candidate<'a> {
name: String, name: String,
btf: &'a Btf, btf: &'a Btf,
_ty: &'a BtfType, _ty: &'a BtfType,
@ -765,18 +720,17 @@ impl ComputedRelocation {
fn apply( fn apply(
&self, &self,
program: &mut Program, instructions: &mut [bpf_insn],
rel: &Relocation, rel: &Relocation,
local_btf: &Btf, local_btf: &Btf,
target_btf: &Btf, target_btf: &Btf,
) -> Result<(), ErrorWrapper> { ) -> Result<(), ErrorWrapper> {
let instructions = &mut program.function.instructions;
let num_instructions = instructions.len(); let num_instructions = instructions.len();
let ins_index = rel.ins_offset as usize / std::mem::size_of::<bpf_insn>(); let ins_index = rel.ins_offset as usize / std::mem::size_of::<bpf_insn>();
let mut ins = let mut ins =
instructions instructions
.get_mut(ins_index) .get_mut(ins_index)
.ok_or(RelocationError::InvalidInstructionIndex { .ok_or(BtfRelocationError::InvalidInstructionIndex {
index: rel.ins_offset as usize, index: rel.ins_offset as usize,
num_instructions, num_instructions,
relocation_number: rel.number, relocation_number: rel.number,
@ -790,7 +744,7 @@ impl ComputedRelocation {
BPF_ALU | BPF_ALU64 => { BPF_ALU | BPF_ALU64 => {
let src_reg = ins.src_reg(); let src_reg = ins.src_reg();
if src_reg != BPF_K as u8 { if src_reg != BPF_K as u8 {
return Err(RelocationError::InvalidInstruction { return Err(BtfRelocationError::InvalidInstruction {
relocation_number: rel.number, relocation_number: rel.number,
index: ins_index, index: ins_index,
error: format!("invalid src_reg={:x} expected {:x}", src_reg, BPF_K), error: format!("invalid src_reg={:x} expected {:x}", src_reg, BPF_K),
@ -802,7 +756,7 @@ impl ComputedRelocation {
} }
BPF_LDX | BPF_ST | BPF_STX => { BPF_LDX | BPF_ST | BPF_STX => {
if target_value > std::i16::MAX as u32 { if target_value > std::i16::MAX as u32 {
return Err(RelocationError::InvalidInstruction { return Err(BtfRelocationError::InvalidInstruction {
relocation_number: rel.number, relocation_number: rel.number,
index: ins_index, index: ins_index,
error: format!("value `{}` overflows 16 bits offset field", target_value), error: format!("value `{}` overflows 16 bits offset field", target_value),
@ -822,7 +776,7 @@ impl ComputedRelocation {
(Int(_, local_info), Int(_, target_info)) (Int(_, local_info), Int(_, target_info))
if unsigned(*local_info) && unsigned(*target_info) => {} if unsigned(*local_info) && unsigned(*target_info) => {}
_ => { _ => {
return Err(RelocationError::InvalidInstruction { return Err(BtfRelocationError::InvalidInstruction {
relocation_number: rel.number, relocation_number: rel.number,
index: ins_index, index: ins_index,
error: format!( error: format!(
@ -843,7 +797,7 @@ impl ComputedRelocation {
2 => BPF_H, 2 => BPF_H,
1 => BPF_B, 1 => BPF_B,
size => { size => {
return Err(RelocationError::InvalidInstruction { return Err(BtfRelocationError::InvalidInstruction {
relocation_number: rel.number, relocation_number: rel.number,
index: ins_index, index: ins_index,
error: format!("invalid target size {}", size), error: format!("invalid target size {}", size),
@ -857,7 +811,7 @@ impl ComputedRelocation {
BPF_LD => { BPF_LD => {
ins.imm = target_value as i32; ins.imm = target_value as i32;
let mut next_ins = instructions.get_mut(ins_index + 1).ok_or( let mut next_ins = instructions.get_mut(ins_index + 1).ok_or(
RelocationError::InvalidInstructionIndex { BtfRelocationError::InvalidInstructionIndex {
index: ins_index + 1, index: ins_index + 1,
num_instructions, num_instructions,
relocation_number: rel.number, relocation_number: rel.number,
@ -867,7 +821,7 @@ impl ComputedRelocation {
next_ins.imm = 0; next_ins.imm = 0;
} }
class => { class => {
return Err(RelocationError::InvalidInstruction { return Err(BtfRelocationError::InvalidInstruction {
relocation_number: rel.number, relocation_number: rel.number,
index: ins_index, index: ins_index,
error: format!("invalid instruction class {:x}", class), error: format!("invalid instruction class {:x}", class),
@ -938,7 +892,7 @@ impl ComputedRelocation {
}), }),
rel_kind => { rel_kind => {
let ty = spec.btf.type_by_id(accessor.type_id)?; let ty = spec.btf.type_by_id(accessor.type_id)?;
return Err(RelocationError::InvalidRelocationKindForType { return Err(BtfRelocationError::InvalidRelocationKindForType {
relocation_number: rel.number, relocation_number: rel.number,
relocation_kind: format!("{:?}", rel_kind), relocation_kind: format!("{:?}", rel_kind),
type_kind: format!("{:?}", ty.kind()), type_kind: format!("{:?}", ty.kind()),
@ -955,7 +909,7 @@ impl ComputedRelocation {
(ty, members[accessor.index]) (ty, members[accessor.index])
} }
_ => { _ => {
return Err(RelocationError::InvalidRelocationKindForType { return Err(BtfRelocationError::InvalidRelocationKindForType {
relocation_number: rel.number, relocation_number: rel.number,
relocation_kind: format!("{:?}", rel.kind), relocation_kind: format!("{:?}", rel.kind),
type_kind: format!("{:?}", ty.kind()), type_kind: format!("{:?}", ty.kind()),
@ -1065,10 +1019,10 @@ impl ComputedRelocation {
// this exists only to simplify propagating errors from relocate_btf() and to associate // this exists only to simplify propagating errors from relocate_btf() and to associate
// RelocationError(s) with their respective program name // RelocationError(s) with their respective program name
#[derive(Error, Debug)] #[derive(Error, Debug)]
enum ErrorWrapper { pub enum ErrorWrapper {
#[error(transparent)] #[error(transparent)]
BtfError(#[from] BtfError), BtfError(#[from] BtfError),
#[error(transparent)] #[error(transparent)]
RelocationError(#[from] RelocationError), RelocationError(#[from] BtfRelocationError),
} }

@ -18,50 +18,91 @@ use crate::{
obj::btf::{Btf, BtfError, MAX_RESOLVE_DEPTH}, obj::btf::{Btf, BtfError, MAX_RESOLVE_DEPTH},
}; };
/// The BTF Type
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub(crate) enum BtfType {
pub enum BtfType {
/// An unknown type.
Unknown, Unknown,
/// Forward declaration.
Fwd(btf_type), Fwd(btf_type),
/// Constant.
Const(btf_type), Const(btf_type),
/// Volatile.
Volatile(btf_type), Volatile(btf_type),
/// Restrict.
Restrict(btf_type), Restrict(btf_type),
/// Pointer.
Ptr(btf_type), Ptr(btf_type),
/// Type definition.
Typedef(btf_type), Typedef(btf_type),
/// Function.
Func(btf_type), Func(btf_type),
/// Integer.
Int(btf_type, u32), Int(btf_type, u32),
/// Floating point.
Float(btf_type), Float(btf_type),
/// Enumeration.
Enum(btf_type, Vec<btf_enum>), Enum(btf_type, Vec<btf_enum>),
/// Array.
Array(btf_type, btf_array), Array(btf_type, btf_array),
/// Struct.
Struct(btf_type, Vec<btf_member>), Struct(btf_type, Vec<btf_member>),
/// Union.
Union(btf_type, Vec<btf_member>), Union(btf_type, Vec<btf_member>),
/// Function prototype
FuncProto(btf_type, Vec<btf_param>), FuncProto(btf_type, Vec<btf_param>),
/// Variable
Var(btf_type, btf_var), Var(btf_type, btf_var),
/// Data section
DataSec(btf_type, Vec<btf_var_secinfo>), DataSec(btf_type, Vec<btf_var_secinfo>),
/// Declaration Tag
DeclTag(btf_type, btf_decl_tag), DeclTag(btf_type, btf_decl_tag),
/// Type Tag
TypeTag(btf_type), TypeTag(btf_type),
} }
/// BTF Kind
#[derive(Copy, Clone, Debug, Eq, PartialEq)] #[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[repr(u32)] #[repr(u32)]
pub(crate) enum BtfKind { pub enum BtfKind {
/// BTF_KIND_UNKN
Unknown = BTF_KIND_UNKN, Unknown = BTF_KIND_UNKN,
/// BTF_KIND_INT
Int = BTF_KIND_INT, Int = BTF_KIND_INT,
/// BTF_KIND_FLOAT
Float = BTF_KIND_FLOAT, Float = BTF_KIND_FLOAT,
/// BTF_KIND_PTR
Ptr = BTF_KIND_PTR, Ptr = BTF_KIND_PTR,
/// BTF_KIND_ARRAY
Array = BTF_KIND_ARRAY, Array = BTF_KIND_ARRAY,
/// BTF_KIND_STRUCT
Struct = BTF_KIND_STRUCT, Struct = BTF_KIND_STRUCT,
/// BTF_KIND_UNION
Union = BTF_KIND_UNION, Union = BTF_KIND_UNION,
/// BTF_KIND_ENUM
Enum = BTF_KIND_ENUM, Enum = BTF_KIND_ENUM,
/// BTF_KIND_FWD
Fwd = BTF_KIND_FWD, Fwd = BTF_KIND_FWD,
/// BTF_KIND_TYPEDEF
Typedef = BTF_KIND_TYPEDEF, Typedef = BTF_KIND_TYPEDEF,
/// BTF_KIND_VOLATILE
Volatile = BTF_KIND_VOLATILE, Volatile = BTF_KIND_VOLATILE,
/// BTF_KIND_CONST
Const = BTF_KIND_CONST, Const = BTF_KIND_CONST,
/// BTF_KIND_RESTRICT
Restrict = BTF_KIND_RESTRICT, Restrict = BTF_KIND_RESTRICT,
/// BTF_KIND_FUNC
Func = BTF_KIND_FUNC, Func = BTF_KIND_FUNC,
/// BTF_KIND_FUNC_PROTO
FuncProto = BTF_KIND_FUNC_PROTO, FuncProto = BTF_KIND_FUNC_PROTO,
/// BTF_KIND_VAR
Var = BTF_KIND_VAR, Var = BTF_KIND_VAR,
/// BTF_KIND_DATASEC
DataSec = BTF_KIND_DATASEC, DataSec = BTF_KIND_DATASEC,
/// BTF_KIND_DECL_TAG
DeclTag = BTF_KIND_DECL_TAG, DeclTag = BTF_KIND_DECL_TAG,
/// BTF_KIND_TYPE_TAG
TypeTag = BTF_KIND_TYPE_TAG, TypeTag = BTF_KIND_TYPE_TAG,
} }
@ -98,25 +139,25 @@ impl TryFrom<u32> for BtfKind {
impl Display for BtfKind { impl Display for BtfKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
BtfKind::Unknown => write!(f, "[UNKNOWN]"), BtfKind::Unknown => write!(f, "UNKNOWN"),
BtfKind::Int => write!(f, "[INT]"), BtfKind::Int => write!(f, "INT"),
BtfKind::Float => write!(f, "[FLOAT]"), BtfKind::Float => write!(f, "FLOAT"),
BtfKind::Ptr => write!(f, "[PTR]"), BtfKind::Ptr => write!(f, "PTR"),
BtfKind::Array => write!(f, "[ARRAY]"), BtfKind::Array => write!(f, "ARRAY"),
BtfKind::Struct => write!(f, "[STRUCT]"), BtfKind::Struct => write!(f, "STRUCT"),
BtfKind::Union => write!(f, "[UNION]"), BtfKind::Union => write!(f, "UNION"),
BtfKind::Enum => write!(f, "[ENUM]"), BtfKind::Enum => write!(f, "ENUM"),
BtfKind::Fwd => write!(f, "[FWD]"), BtfKind::Fwd => write!(f, "FWD"),
BtfKind::Typedef => write!(f, "[TYPEDEF]"), BtfKind::Typedef => write!(f, "TYPEDEF"),
BtfKind::Volatile => write!(f, "[VOLATILE]"), BtfKind::Volatile => write!(f, "VOLATILE"),
BtfKind::Const => write!(f, "[CONST]"), BtfKind::Const => write!(f, "CONST"),
BtfKind::Restrict => write!(f, "[RESTRICT]"), BtfKind::Restrict => write!(f, "RESTRICT"),
BtfKind::Func => write!(f, "[FUNC]"), BtfKind::Func => write!(f, "FUNC"),
BtfKind::FuncProto => write!(f, "[FUNC_PROTO]"), BtfKind::FuncProto => write!(f, "FUNC_PROTO"),
BtfKind::Var => write!(f, "[VAR]"), BtfKind::Var => write!(f, "VAR"),
BtfKind::DataSec => write!(f, "[DATASEC]"), BtfKind::DataSec => write!(f, "DATASEC"),
BtfKind::DeclTag => write!(f, "[DECL_TAG]"), BtfKind::DeclTag => write!(f, "DECL_TAG"),
BtfKind::TypeTag => write!(f, "[TYPE_TAG]"), BtfKind::TypeTag => write!(f, "TYPE_TAG"),
} }
} }
} }
@ -307,11 +348,13 @@ impl BtfType {
self.btf_type().map(|ty| ty.info) self.btf_type().map(|ty| ty.info)
} }
pub(crate) fn name_offset(&self) -> Option<u32> { /// Returns the offset of the name of this type.
pub fn name_offset(&self) -> Option<u32> {
self.btf_type().map(|ty| ty.name_off) self.btf_type().map(|ty| ty.name_off)
} }
pub(crate) fn kind(&self) -> Result<Option<BtfKind>, BtfError> { /// Returns the BtfKind of this type.
pub fn kind(&self) -> Result<Option<BtfKind>, BtfError> {
self.btf_type().map(type_kind).transpose() self.btf_type().map(type_kind).transpose()
} }
@ -471,11 +514,13 @@ fn type_kind(ty: &btf_type) -> Result<BtfKind, BtfError> {
((ty.info >> 24) & 0x1F).try_into() ((ty.info >> 24) & 0x1F).try_into()
} }
pub(crate) fn type_vlen(ty: &btf_type) -> usize { /// Returns the vlen of the btf_type.
pub fn type_vlen(ty: &btf_type) -> usize {
(ty.info & 0xFFFF) as usize (ty.info & 0xFFFF) as usize
} }
pub(crate) fn member_bit_offset(info: u32, member: &btf_member) -> usize { /// The bit offset of a struct or union member
pub fn member_bit_offset(info: u32, member: &btf_member) -> usize {
let k_flag = info >> 31 == 1; let k_flag = info >> 31 == 1;
let bit_offset = if k_flag { let bit_offset = if k_flag {
member.offset & 0xFFFFFF member.offset & 0xFFFFFF

@ -1,4 +1,4 @@
pub(crate) mod btf; pub mod btf;
mod relocation; mod relocation;
use object::{ use object::{
@ -26,7 +26,7 @@ use crate::{
}; };
use std::slice::from_raw_parts_mut; use std::slice::from_raw_parts_mut;
use self::btf::{FuncSecInfo, LineSecInfo}; use crate::obj::btf::{FuncSecInfo, LineSecInfo};
const KERNEL_VERSION_ANY: u32 = 0xFFFF_FFFE; const KERNEL_VERSION_ANY: u32 = 0xFFFF_FFFE;
/// The first five __u32 of `bpf_map_def` must be defined. /// The first five __u32 of `bpf_map_def` must be defined.

@ -1,4 +1,5 @@
use std::{collections::HashMap, mem}; use crate::obj::{btf::relocate_btf_program, ProgramSection};
use std::{collections::HashMap, mem, str::FromStr};
use log::debug; use log::debug;
use object::{SectionIndex, SymbolKind}; use object::{SectionIndex, SymbolKind};
@ -11,13 +12,18 @@ use crate::{
}, },
maps::Map, maps::Map,
obj::{Function, Object, Program}, obj::{Function, Object, Program},
BpfError, BpfError, Btf,
}; };
use super::btf::{Candidate, ErrorWrapper};
pub(crate) const INS_SIZE: usize = mem::size_of::<bpf_insn>(); pub(crate) const INS_SIZE: usize = mem::size_of::<bpf_insn>();
#[derive(Debug, Error)] #[derive(Debug, Error)]
enum RelocationError { enum RelocationError {
#[error("program not found")]
ProgramNotFound,
#[error("unknown symbol, index `{index}`")] #[error("unknown symbol, index `{index}`")]
UnknownSymbol { index: usize }, UnknownSymbol { index: usize },
@ -117,6 +123,50 @@ impl Object {
Ok(()) Ok(())
} }
pub fn relocate_btf(&mut self, target_btf: &Btf) -> Result<(), BpfError> {
let (local_btf, btf_ext) = match (&self.btf, &self.btf_ext) {
(Some(btf), Some(btf_ext)) => (btf, btf_ext),
_ => return Ok(()),
};
let mut candidates_cache = HashMap::<u32, Vec<Candidate>>::new();
for (sec_name_off, relos) in btf_ext.relocations() {
let section_name = local_btf.string_at(*sec_name_off)?;
let program_section = match ProgramSection::from_str(&section_name) {
Ok(program) => program,
Err(_) => continue,
};
let section_name = program_section.name();
let program = self
.programs
.get_mut(section_name)
.ok_or(BpfError::RelocationError {
function: section_name.to_owned(),
error: Box::new(RelocationError::ProgramNotFound),
})?;
match relocate_btf_program(
&mut program.function.instructions,
relos,
local_btf,
target_btf,
&mut candidates_cache,
) {
Ok(_) => {}
Err(ErrorWrapper::BtfError(e)) => return Err(e.into()),
Err(ErrorWrapper::RelocationError(error)) => {
return Err(BpfError::RelocationError {
function: section_name.to_owned(),
error: Box::new(error),
})
}
}
}
Ok(())
}
} }
fn relocate_maps<'a, I: Iterator<Item = &'a Relocation>>( fn relocate_maps<'a, I: Iterator<Item = &'a Relocation>>(

@ -0,0 +1,12 @@
[package]
name = "btftool"
version = "0.1.0"
authors = ["The Aya Contributors"]
edition = "2018"
[dependencies]
clap = { version = "3", features=["derive"] }
aya = { path = "../aya", features = ["btf"] }
anyhow = "1"
object = "0.28"
thiserror = "1"

@ -0,0 +1,173 @@
use aya::{
btf::{member_bit_offset, type_vlen, BtfKind, BtfType},
Btf, BtfError, Endianness,
};
use clap::{Parser, Subcommand};
use object::{Object, ObjectSection};
use std::{
fmt::{self, Write},
fs, io,
path::Path,
};
use thiserror::Error;
#[derive(Parser)]
#[clap(author, version, about, long_about = None)]
struct Cli {
#[clap(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
/// Dumps the .BTF ELF Section
Dump { file: Option<String> },
}
#[derive(Error, Debug)]
pub enum Error {
#[error(".BTF section not found")]
NoBTF,
#[error("error parsing ELF data")]
ElfError(#[from] object::read::Error),
#[error(transparent)]
IOError(io::Error),
#[error(transparent)]
FmtError(fmt::Error),
#[error(transparent)]
BtfError(BtfError),
}
fn main() -> Result<(), anyhow::Error> {
let cli = Cli::parse();
match &cli.command {
Commands::Dump { file } => {
dump(file.as_ref().unwrap())?;
}
}
Ok(())
}
fn dump<P: AsRef<Path>>(input: P) -> Result<(), Error> {
let bin_data = fs::read(input).map_err(Error::IOError)?;
let obj_file = object::File::parse(&*bin_data).map_err(Error::ElfError)?;
if let Some(section) = obj_file.section_by_name(".BTF") {
let btf = Btf::parse(section.data()?, Endianness::default()).map_err(Error::BtfError)?;
for (i, t) in btf.types().enumerate().skip(1) {
let kind = t.kind().unwrap_or(None).unwrap_or(BtfKind::Unknown);
let name = if let Some(offset) = t.name_offset() {
if offset > 0 {
format!(
"'{}'",
btf.string_at(offset)
.unwrap_or(std::borrow::Cow::Borrowed(""))
)
} else {
"''".to_owned()
}
} else {
"''".to_owned()
};
let info = match t {
BtfType::Unknown => "".to_string(),
BtfType::Fwd(t)
| BtfType::Const(t)
| BtfType::Volatile(t)
| BtfType::Restrict(t)
| BtfType::Ptr(t)
| BtfType::Typedef(t)
| BtfType::Func(t) => {
format!("type_id={}", unsafe { t.__bindgen_anon_1.type_ })
}
BtfType::Int(t, size) => {
let encoding = match (t.info & 0x0f000000) >> 24 {
1 => "(signed)",
2 => "(char)",
4 => "(bool)",
_ => "(none)",
};
let offset = (t.info & 0x00ff0000) >> 16;
let bits = t.info & 0x000000ff;
format!(
"size={} bits_offset={} nr_bits={} encoding={}",
size, offset, bits, encoding,
)
}
BtfType::Float(_) => todo!(),
BtfType::Enum(_, _) => todo!(),
BtfType::Array(_, array) => {
format!(
"type_id={} index_type_id={} nr_elems={}",
array.type_, array.index_type, array.nelems
)
}
BtfType::Struct(ty, members) => {
let size = unsafe { ty.__bindgen_anon_1.size };
let vlen = type_vlen(ty);
let mut out = format!("size={} vlen={}", size, vlen,);
for m in members {
let name = btf
.string_at(m.name_off)
.unwrap_or(std::borrow::Cow::Borrowed(""));
let type_id = m.type_;
let offset = member_bit_offset(ty.info, m);
write!(out, "\n\t'{name}' type_id={type_id} bits_offset={offset}")
.map_err(Error::FmtError)?;
}
out
}
BtfType::Union(_, _) => todo!(),
BtfType::FuncProto(ty, params) => {
let ret_type_id = unsafe { ty.__bindgen_anon_1.type_ };
let vlen = type_vlen(ty);
let mut out = format!("ret_type_id={ret_type_id} vlen={vlen}");
for p in params {
let name = btf
.string_at(p.name_off)
.unwrap_or(std::borrow::Cow::Borrowed(""));
let type_id = p.type_;
write!(out, "\n\t'{name}' type_id={type_id}").map_err(Error::FmtError)?;
}
out
}
BtfType::Var(ty, var) => {
let type_id = unsafe { ty.__bindgen_anon_1.type_ };
let linkage = match var.linkage {
0 => "static".to_owned(),
1 => "global".to_owned(),
other => format!("{other}"),
};
format!("type_id={type_id} linkage={linkage}")
}
BtfType::DataSec(ty, secinfo) => {
let size = unsafe { ty.__bindgen_anon_1.size };
let vlen = type_vlen(ty);
let mut out = format!("size={size} vlen={vlen}");
for s in secinfo {
let points_to = btf.type_by_id(s.type_).unwrap();
let name = btf
.string_at(points_to.name_offset().unwrap_or(0))
.unwrap_or(std::borrow::Cow::Borrowed(""));
write!(
out,
"\n\ttype_id={} offset={} size={} ({} '{}')",
s.type_,
s.offset,
s.size,
points_to.kind().unwrap_or(None).unwrap_or(BtfKind::Unknown),
name
)
.map_err(Error::FmtError)?;
}
out
}
BtfType::DeclTag(_, _) => unimplemented!("decl tag formatting not implemented"),
BtfType::TypeTag(_) => unimplemented!("type tag formatting not implemented"),
};
println!("[{i}] {kind} {name} {info}");
}
Ok(())
} else {
Err(Error::NoBTF)
}
}
Loading…
Cancel
Save