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
run: |
cargo clippy -p aya -- --deny warnings
cargo clippy -p aya-gen -- --deny warnings
cargo clippy -p xtask -- --deny warnings
cargo clippy --all-features -p aya -- --deny warnings
cargo clippy --all-features -p aya-gen -- --deny warnings
cargo clippy --all-features -p xtask -- --deny warnings
pushd bpf
cargo clippy -p aya-bpf -- --deny warnings
cargo clippy --all-features -p aya-bpf -- --deny warnings
popd
- 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]
members = ["aya", "aya-gen", "xtask"]
members = ["aya", "aya-gen", "btftool", "xtask"]

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

@ -530,7 +530,11 @@ impl<'a> BpfLoader<'a> {
.drain()
.map(|(name, map)| (name, MapLock::new(map)))
.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.
#[derive(Debug)]
pub struct Bpf {
btf: Option<Btf>,
maps: HashMap<String, MapLock>,
programs: HashMap<String, Program>,
}
@ -744,6 +749,20 @@ impl Bpf {
pub fn programs_mut(&mut self) -> impl Iterator<Item = (&str, &mut Program)> {
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`].

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

@ -27,7 +27,6 @@ use super::{
};
pub(crate) const MAX_RESOLVE_DEPTH: u8 = 32;
pub(crate) const MAX_SPEC_LEN: usize = 64;
/// The error type returned when `BTF` operations fail.
#[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()
}
@ -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>() {
return Err(BtfError::InvalidHeader);
}
@ -282,7 +283,8 @@ impl Btf {
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 {
hdr_len,
mut str_off,
@ -308,15 +310,18 @@ impl Btf {
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)
}
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)
}
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()
}
@ -325,7 +330,8 @@ impl Btf {
.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() {
match ty.kind()? {
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
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
@ -597,6 +604,7 @@ unsafe fn read_btf_header(data: &[u8]) -> btf_header {
ptr::read_unaligned(data.as_ptr() as *const btf_header)
}
/// Extended BTF Information
#[derive(Debug, Clone)]
pub struct BtfExt {
data: Vec<u8>,
@ -611,11 +619,8 @@ pub struct BtfExt {
}
impl BtfExt {
pub(crate) fn parse(
data: &[u8],
endianness: Endianness,
btf: &Btf,
) -> Result<BtfExt, BtfError> {
/// Parses a byte slice to BtfExt using the provided BTF.
pub fn parse(data: &[u8], endianness: Endianness, btf: &Btf) -> Result<BtfExt, BtfError> {
// Safety: btf_ext_header is POD so read_unaligned is safe
let header = unsafe {
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
}
/// Returns the function information
pub fn func_info(&self) -> &FuncInfo {
&self.func_info
}
pub(crate) fn line_info_rec_size(&self) -> usize {
self.line_info_rec_size
}
/// Returns the line information
pub fn line_info(&self) -> &LineInfo {
&self.line_info
}
}
pub(crate) struct SecInfoIter<'a> {

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

@ -1,3 +1,4 @@
//! BPF Type Format
#[allow(clippy::module_inception)]
mod btf;
mod info;
@ -6,5 +7,10 @@ mod types;
pub use btf::*;
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::*;

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

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

@ -1,4 +1,4 @@
pub(crate) mod btf;
pub mod btf;
mod relocation;
use object::{
@ -26,7 +26,7 @@ use crate::{
};
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;
/// 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 object::{SectionIndex, SymbolKind};
@ -11,13 +12,18 @@ use crate::{
},
maps::Map,
obj::{Function, Object, Program},
BpfError,
BpfError, Btf,
};
use super::btf::{Candidate, ErrorWrapper};
pub(crate) const INS_SIZE: usize = mem::size_of::<bpf_insn>();
#[derive(Debug, Error)]
enum RelocationError {
#[error("program not found")]
ProgramNotFound,
#[error("unknown symbol, index `{index}`")]
UnknownSymbol { index: usize },
@ -117,6 +123,50 @@ impl Object {
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>>(

@ -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