Add BPF_PROG_TYPE_EXT

This requires loading the BTF to kernel when loading all programs as
well as implementing Extension program type

Signed-off-by: Dave Tucker <dave@dtucker.co.uk>
pull/127/head
Dave Tucker 3 years ago
parent 379bb313b1
commit 5c6131afba

@ -22,6 +22,7 @@ futures = { version = "0.3.12", optional = true, default-features = false, featu
tokio = { version = "1.2.0", features = ["macros", "rt", "rt-multi-thread", "net"], optional = true }
async-std = { version = "1.9.0", optional = true }
async-io = { version = "1.3", optional = true }
log = "0.4"
[dev-dependencies]
matches = "0.1.8"

@ -1,6 +1,6 @@
use std::{
borrow::Cow,
collections::HashMap,
collections::{HashMap, HashSet},
error::Error,
ffi::CString,
fs, io,
@ -8,6 +8,7 @@ use std::{
path::{Path, PathBuf},
};
use log::debug;
use thiserror::Error;
use crate::{
@ -21,12 +22,16 @@ use crate::{
MapKind, Object, ParseError, ProgramSection,
},
programs::{
BtfTracePoint, CgroupSkb, CgroupSkbAttachType, FEntry, FExit, KProbe, LircMode2, Lsm,
PerfEvent, ProbeKind, Program, ProgramData, ProgramError, RawTracePoint, SchedClassifier,
SkMsg, SkSkb, SkSkbKind, SockOps, SocketFilter, TracePoint, UProbe, Xdp,
BtfTracePoint, CgroupSkb, CgroupSkbAttachType, Extension, FEntry, FExit, KProbe, LircMode2,
Lsm, PerfEvent, ProbeKind, Program, ProgramData, ProgramError, RawTracePoint,
SchedClassifier, SkMsg, SkSkb, SkSkbKind, SockOps, SocketFilter, TracePoint, UProbe, Xdp,
},
sys::{bpf_map_freeze, bpf_map_update_elem_ptr},
util::{bytes_of, possible_cpus, POSSIBLE_CPUS},
sys::{
bpf_load_btf, bpf_map_freeze, bpf_map_update_elem_ptr, is_btf_datasec_supported,
is_btf_float_supported, is_btf_func_global_supported, is_btf_func_supported,
is_btf_supported, is_prog_name_supported,
},
util::{bytes_of, possible_cpus, VerifierLog, POSSIBLE_CPUS},
};
pub(crate) const BPF_OBJ_NAME_LEN: usize = 16;
@ -77,6 +82,47 @@ impl Default for PinningType {
}
}
// Features implements BPF and BTF feature detection
#[derive(Default, Debug)]
pub(crate) struct Features {
pub bpf_name: bool,
pub btf: bool,
pub btf_func: bool,
pub btf_func_global: bool,
pub btf_datasec: bool,
pub btf_float: bool,
}
impl Features {
fn probe_features(&mut self) {
self.bpf_name = is_prog_name_supported();
debug!("[FEAT PROBE] BPF program name support: {}", self.bpf_name);
self.btf = is_btf_supported();
debug!("[FEAT PROBE] BTF support: {}", self.btf);
if self.btf {
self.btf_func = is_btf_func_supported();
debug!("[FEAT PROBE] BTF func support: {}", self.btf_func);
self.btf_func_global = is_btf_func_global_supported();
debug!(
"[FEAT PROBE] BTF global func support: {}",
self.btf_func_global
);
self.btf_datasec = is_btf_datasec_supported();
debug!(
"[FEAT PROBE] BTF var and datasec support: {}",
self.btf_datasec
);
self.btf_float = is_btf_float_supported();
debug!("[FEAT PROBE] BTF float support: {}", self.btf_float);
}
}
}
/// Builder style API for advanced loading of eBPF programs.
///
/// Loading eBPF code involves a few steps, including loading maps and applying
@ -103,15 +149,21 @@ pub struct BpfLoader<'a> {
btf: Option<Cow<'a, Btf>>,
map_pin_path: Option<PathBuf>,
globals: HashMap<&'a str, &'a [u8]>,
features: Features,
extensions: HashSet<&'a str>,
}
impl<'a> BpfLoader<'a> {
/// Creates a new loader instance.
pub fn new() -> BpfLoader<'a> {
let mut features = Features::default();
features.probe_features();
BpfLoader {
btf: Btf::from_sys_fs().ok().map(Cow::Owned),
map_pin_path: None,
globals: HashMap::new(),
features,
extensions: HashSet::new(),
}
}
@ -187,6 +239,28 @@ impl<'a> BpfLoader<'a> {
self
}
/// Treat the provided program as an [`Extension`]
///
/// When attempting to load the program with the provided `name`
/// the program type is forced to be ] [`Extension`] and is not
/// inferred from the ELF section name.
///
/// # Example
///
/// ```no_run
/// use aya::BpfLoader;
///
/// let bpf = BpfLoader::new()
/// .extension("myfunc")
/// .load_file("file.o")?;
/// # Ok::<(), aya::BpfError>(())
/// ```
///
pub fn extension(&mut self, name: &'a str) -> &mut BpfLoader<'a> {
self.extensions.insert(name);
self
}
/// Loads eBPF bytecode from a file.
///
/// # Examples
@ -221,6 +295,39 @@ impl<'a> BpfLoader<'a> {
let mut obj = Object::parse(data)?;
obj.patch_map_data(self.globals.clone())?;
let btf_fd = if self.features.btf {
if let Some(ref mut obj_btf) = obj.btf {
// fixup btf
let section_data = obj.section_sizes.clone();
let symbol_offsets = obj.symbol_offset_by_name.clone();
obj_btf.fixup(&section_data, &symbol_offsets)?;
let btf = obj_btf.sanitize(&self.features)?;
// load btf to the kernel
let raw_btf = btf.to_bytes();
let mut log_buf = VerifierLog::new();
log_buf.grow();
let ret = bpf_load_btf(raw_btf.as_slice(), &mut log_buf);
match ret {
Ok(fd) => Some(fd),
Err(io_error) => {
log_buf.truncate();
return Err(BpfError::BtfError(BtfError::LoadError {
io_error,
verifier_log: log_buf
.as_c_str()
.map(|s| s.to_string_lossy().to_string())
.unwrap_or_else(|| "[none]".to_owned()),
}));
}
}
} else {
None
}
} else {
None
};
if let Some(btf) = &self.btf {
obj.relocate_btf(btf)?;
}
@ -288,76 +395,90 @@ impl<'a> BpfLoader<'a> {
.programs
.drain()
.map(|(name, obj)| {
let prog_name = if self.features.bpf_name {
Some(name.clone())
} else {
None
};
let data = ProgramData {
name: prog_name,
obj,
fd: None,
links: Vec::new(),
expected_attach_type: None,
attach_btf_obj_fd: None,
attach_btf_id: None,
attach_prog_fd: None,
btf_fd,
};
let program = match &data.obj.section {
ProgramSection::KProbe { .. } => Program::KProbe(KProbe {
data,
kind: ProbeKind::KProbe,
}),
ProgramSection::KRetProbe { .. } => Program::KProbe(KProbe {
data,
kind: ProbeKind::KRetProbe,
}),
ProgramSection::UProbe { .. } => Program::UProbe(UProbe {
data,
kind: ProbeKind::UProbe,
}),
ProgramSection::URetProbe { .. } => Program::UProbe(UProbe {
data,
kind: ProbeKind::URetProbe,
}),
ProgramSection::TracePoint { .. } => Program::TracePoint(TracePoint { data }),
ProgramSection::SocketFilter { .. } => {
Program::SocketFilter(SocketFilter { data })
}
ProgramSection::Xdp { .. } => Program::Xdp(Xdp { data }),
ProgramSection::SkMsg { .. } => Program::SkMsg(SkMsg { data }),
ProgramSection::SkSkbStreamParser { .. } => Program::SkSkb(SkSkb {
data,
kind: SkSkbKind::StreamParser,
}),
ProgramSection::SkSkbStreamVerdict { .. } => Program::SkSkb(SkSkb {
data,
kind: SkSkbKind::StreamVerdict,
}),
ProgramSection::SockOps { .. } => Program::SockOps(SockOps { data }),
ProgramSection::SchedClassifier { .. } => {
Program::SchedClassifier(SchedClassifier {
let program = if self.extensions.contains(name.as_str()) {
Program::Extension(Extension { data })
} else {
match &data.obj.section {
ProgramSection::KProbe { .. } => Program::KProbe(KProbe {
data,
name: unsafe {
CString::from_vec_unchecked(Vec::from(name.clone()))
.into_boxed_c_str()
},
})
}
ProgramSection::CgroupSkbIngress { .. } => Program::CgroupSkb(CgroupSkb {
data,
expected_attach_type: Some(CgroupSkbAttachType::Ingress),
}),
ProgramSection::CgroupSkbEgress { .. } => Program::CgroupSkb(CgroupSkb {
data,
expected_attach_type: Some(CgroupSkbAttachType::Egress),
}),
ProgramSection::LircMode2 { .. } => Program::LircMode2(LircMode2 { data }),
ProgramSection::PerfEvent { .. } => Program::PerfEvent(PerfEvent { data }),
ProgramSection::RawTracePoint { .. } => {
Program::RawTracePoint(RawTracePoint { data })
}
ProgramSection::Lsm { .. } => Program::Lsm(Lsm { data }),
ProgramSection::BtfTracePoint { .. } => {
Program::BtfTracePoint(BtfTracePoint { data })
kind: ProbeKind::KProbe,
}),
ProgramSection::KRetProbe { .. } => Program::KProbe(KProbe {
data,
kind: ProbeKind::KRetProbe,
}),
ProgramSection::UProbe { .. } => Program::UProbe(UProbe {
data,
kind: ProbeKind::UProbe,
}),
ProgramSection::URetProbe { .. } => Program::UProbe(UProbe {
data,
kind: ProbeKind::URetProbe,
}),
ProgramSection::TracePoint { .. } => {
Program::TracePoint(TracePoint { data })
}
ProgramSection::SocketFilter { .. } => {
Program::SocketFilter(SocketFilter { data })
}
ProgramSection::Xdp { .. } => Program::Xdp(Xdp { data }),
ProgramSection::SkMsg { .. } => Program::SkMsg(SkMsg { data }),
ProgramSection::SkSkbStreamParser { .. } => Program::SkSkb(SkSkb {
data,
kind: SkSkbKind::StreamParser,
}),
ProgramSection::SkSkbStreamVerdict { .. } => Program::SkSkb(SkSkb {
data,
kind: SkSkbKind::StreamVerdict,
}),
ProgramSection::SockOps { .. } => Program::SockOps(SockOps { data }),
ProgramSection::SchedClassifier { .. } => {
Program::SchedClassifier(SchedClassifier {
data,
name: unsafe {
CString::from_vec_unchecked(Vec::from(name.clone()))
.into_boxed_c_str()
},
})
}
ProgramSection::CgroupSkbIngress { .. } => Program::CgroupSkb(CgroupSkb {
data,
expected_attach_type: Some(CgroupSkbAttachType::Ingress),
}),
ProgramSection::CgroupSkbEgress { .. } => Program::CgroupSkb(CgroupSkb {
data,
expected_attach_type: Some(CgroupSkbAttachType::Egress),
}),
ProgramSection::LircMode2 { .. } => Program::LircMode2(LircMode2 { data }),
ProgramSection::PerfEvent { .. } => Program::PerfEvent(PerfEvent { data }),
ProgramSection::RawTracePoint { .. } => {
Program::RawTracePoint(RawTracePoint { data })
}
ProgramSection::Lsm { .. } => Program::Lsm(Lsm { data }),
ProgramSection::BtfTracePoint { .. } => {
Program::BtfTracePoint(BtfTracePoint { data })
}
ProgramSection::FEntry { .. } => Program::FEntry(FEntry { data }),
ProgramSection::FExit { .. } => Program::FExit(FExit { data }),
ProgramSection::Extension { .. } => Program::Extension(Extension { data }),
}
ProgramSection::FEntry { .. } => Program::FEntry(FEntry { data }),
ProgramSection::FExit { .. } => Program::FExit(FExit { data }),
};
(name, program)
})
.collect();

@ -1,7 +1,8 @@
use std::{
borrow::Cow,
collections::HashMap,
convert::TryInto,
ffi::{c_void, CStr},
ffi::{c_void, CStr, CString},
fs, io, mem,
path::{Path, PathBuf},
ptr,
@ -9,13 +10,22 @@ use std::{
use bytes::BufMut;
use log::debug;
use object::Endianness;
use thiserror::Error;
use crate::{
generated::{btf_ext_header, btf_header},
generated::{
btf_enum, btf_ext_header, btf_func_linkage, btf_header, btf_member, btf_var_secinfo,
},
obj::btf::{relocation::Relocation, BtfKind, BtfType},
util::bytes_of,
Features,
};
use super::{
info::{FuncSecInfo, LineSecInfo},
type_vlen, FuncInfo, LineInfo,
};
pub(crate) const MAX_RESOLVE_DEPTH: u8 = 32;
@ -74,6 +84,28 @@ pub enum BtfError {
#[error("maximum depth reached resolving BTF type")]
MaximumTypeDepthReached { type_id: u32 },
/// Loading the btf failed
#[error("the BPF_BTF_LOAD syscall failed. Verifier output: {verifier_log}")]
LoadError {
/// The [`io::Error`] returned by the `BPF_BTF_LOAD` syscall.
#[source]
io_error: io::Error,
/// The error log produced by the kernel verifier.
verifier_log: String,
},
#[error("Offset not found for symbol `{symbol_name}`")]
SymbolOffsetNotFound { symbol_name: String },
#[error("BTF type that is not VAR was found in DATASEC")]
InvalidDatasec,
#[error("Unable to determine the size of section `{section_name}`")]
UnknownSectionSize { section_name: String },
#[error("Unable to get symbol name")]
InvalidSymbolName,
}
/// Bpf Type Format metadata.
@ -93,6 +125,40 @@ pub struct Btf {
}
impl Btf {
pub(crate) fn new() -> Btf {
Btf {
header: btf_header {
magic: 0xeb9f,
version: 0x01,
flags: 0x00,
hdr_len: 0x18,
type_off: 0x00,
type_len: 0x00,
str_off: 0x00,
str_len: 0x00,
},
strings: vec![0],
types: vec![],
_endianness: Endianness::default(),
}
}
pub(crate) fn add_string(&mut self, name: String) -> u32 {
let str = CString::new(name).unwrap();
let name_off = self.strings.len();
self.strings.extend(str.as_c_str().to_bytes_with_nul());
self.header.str_len = self.strings.len() as u32;
name_off as u32
}
pub(crate) fn add_type(&mut self, type_: BtfType) -> u32 {
let size = type_.type_info_size() as u32;
self.types.push(type_);
self.header.type_len += size;
self.header.str_off += size;
self.types.len() as u32
}
/// Loads BTF metadata from `/sys/kernel/btf/vmlinux`.
pub fn from_sys_fs() -> Result<Btf, BtfError> {
Btf::parse_file("/sys/kernel/btf/vmlinux", Endianness::default())
@ -305,6 +371,194 @@ impl Btf {
buf.put(self.strings.as_slice());
buf
}
pub(crate) fn fixup(
&mut self,
section_sizes: &HashMap<String, u64>,
symbol_offsets: &HashMap<String, u64>,
) -> Result<(), BtfError> {
// FIXME: there is probably a more elegant way of doing operation in place
// for now, and to keep on the good side of the borrow checker, we'll create
// a new Vec and populate it as we go
let mut types = vec![];
for t in &self.types {
let kind = t.kind()?.unwrap_or_default();
// datasec sizes aren't set by llvm
// we need to fix them here before loading the btf to the kernel
match t {
BtfType::DataSec(mut ty, data) => {
// Start DataSec Fixups
let sec_name = self.type_name(t)?.ok_or(BtfError::InvalidTypeInfo)?;
let name = sec_name.to_string();
// There are cases when the compiler does indeed populate the
// size. If we hit this case, push to the types vector and
// continue
if unsafe { ty.__bindgen_anon_1.size > 0 } {
debug!("{} {}: fixup not required", kind, name);
types.push(BtfType::DataSec(ty, data.clone()));
continue;
}
// We need to get the size of the section from the ELF file
// Fortunately, we cached these when parsing it initially
// and we can this up by name in section_sizes
if let Some(size) = section_sizes.get(&name) {
debug!("{} {}: fixup size to {}", kind, name, size);
ty.__bindgen_anon_1.size = *size as u32;
} else {
return Err(BtfError::UnknownSectionSize { section_name: name });
}
// The Vec<btf_var_secinfo> contains BTF_KIND_VAR sections
// that need to have their offsets adjusted. To do this,
// we need to get the offset from the ELF file.
// This was also cached during initial parsing and
// we can query by name in symbol_offsets
let mut adjusted_data: Vec<btf_var_secinfo> = vec![];
for d in data {
let var_type = self.type_by_id(d.type_)?;
let var_kind = var_type.kind()?.unwrap();
if let BtfType::Var(vty, var) = var_type {
let var_name = self.string_at(vty.name_off)?.to_string();
if var.linkage == btf_func_linkage::BTF_FUNC_STATIC as u32 {
debug!(
"{} {}: {} {}: fixup not required",
kind, name, var_kind, var_name
);
adjusted_data.push(*d);
continue;
}
let offset = symbol_offsets.get(&var_name).ok_or(
BtfError::SymbolOffsetNotFound {
symbol_name: var_name.clone(),
},
)?;
adjusted_data.push(btf_var_secinfo {
type_: d.type_,
offset: *offset as u32,
size: d.size,
});
debug!(
"{} {}: {} {}: fixup offset {}",
kind, name, var_kind, var_name, offset
);
} else {
return Err(BtfError::InvalidDatasec);
}
}
types.push(BtfType::DataSec(ty, adjusted_data))
}
// The type does not need fixing up
// Push it to the new types vec unmodified
ty => types.push(ty.clone()),
}
}
self.types = types;
Ok(())
}
pub(crate) fn sanitize(&self, features: &Features) -> Result<Btf, BtfError> {
let mut btf = Btf::new();
btf.strings = self.strings.to_vec();
btf.header.str_len = btf.strings.len() as u32;
// Skip the first type as it's only there
// to make type_by_id work
for t in &self.types[1..] {
let kind = t.kind()?.unwrap_or_default();
match t {
BtfType::Var(ty, vars) => {
if !features.btf_datasec {
debug!("{}: not supported. replacing with INT", kind);
let int_type = BtfType::new_int(ty.name_off, 1, 0, 0);
btf.add_type(int_type);
} else {
btf.add_type(BtfType::Var(*ty, *vars));
}
}
BtfType::DataSec(ty, data) => {
if !features.btf_datasec {
debug!("{}: not supported. replacing with STRUCT", kind);
let members: Vec<btf_member> = data
.iter()
.map(|p| -> btf_member {
let mt = self.type_by_id(p.type_).unwrap();
btf_member {
name_off: mt.btf_type().unwrap().name_off,
type_: p.type_,
offset: p.offset * 8,
}
})
.collect();
let struct_type = BtfType::new_struct(ty.name_off, members, 0);
btf.add_type(struct_type);
} else {
btf.add_type(BtfType::DataSec(*ty, data.to_vec()));
}
}
BtfType::FuncProto(ty, vars) => {
if !features.btf_func {
debug!("{}: not supported. replacing with ENUM", kind);
let members: Vec<btf_enum> = vars
.iter()
.map(|p| -> btf_enum {
btf_enum {
name_off: p.name_off,
val: p.type_ as i32,
}
})
.collect();
let enum_type = BtfType::new_enum(ty.name_off, members);
btf.add_type(enum_type);
} else {
btf.add_type(BtfType::FuncProto(*ty, vars.to_vec()));
}
}
BtfType::Func(mut ty) => {
if !features.btf_func {
debug!("{}: not supported. replacing with TYPEDEF", kind);
let typedef_type =
BtfType::new_typedef(ty.name_off, unsafe { ty.__bindgen_anon_1.type_ });
btf.add_type(typedef_type);
} else if type_vlen(&ty) == btf_func_linkage::BTF_FUNC_GLOBAL as usize
&& !features.btf_func_global
{
debug!(
"{}: BTF_FUNC_GLOBAL not supported. replacing with BTF_FUNC_STATIC",
kind
);
ty.info |= (btf_func_linkage::BTF_FUNC_STATIC as u32) & 0xFFFF;
btf.add_type(BtfType::Func(ty));
} else {
btf.add_type(BtfType::Func(ty));
}
}
BtfType::Float(ty) => {
if !features.btf_float {
debug!("{}: not supported. replacing with STRUCT", kind);
let struct_ty =
BtfType::new_struct(0, vec![], unsafe { ty.__bindgen_anon_1.size });
btf.add_type(struct_ty);
} else {
btf.add_type(BtfType::Float(*ty));
}
}
// The type does not need sanitizing
ty => {
btf.add_type(ty.clone());
}
}
}
Ok(btf)
}
}
impl Default for Btf {
fn default() -> Self {
Self::new()
}
}
unsafe fn read_btf_header(data: &[u8]) -> btf_header {
@ -318,13 +572,19 @@ pub struct BtfExt {
_endianness: Endianness,
relocations: Vec<(u32, Vec<Relocation>)>,
header: btf_ext_header,
_func_info_rec_size: usize,
_line_info_rec_size: usize,
func_info_rec_size: usize,
pub(crate) func_info: FuncInfo,
line_info_rec_size: usize,
pub(crate) line_info: LineInfo,
core_relo_rec_size: usize,
}
impl BtfExt {
pub(crate) fn parse(data: &[u8], endianness: Endianness) -> Result<BtfExt, BtfError> {
pub(crate) 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)
@ -366,13 +626,57 @@ impl BtfExt {
let mut ext = BtfExt {
header,
relocations: Vec::new(),
_func_info_rec_size: rec_size(func_info_off, func_info_len)?,
_line_info_rec_size: rec_size(line_info_off, line_info_len)?,
func_info: FuncInfo::new(),
line_info: LineInfo::new(),
func_info_rec_size: rec_size(func_info_off, func_info_len)?,
line_info_rec_size: rec_size(line_info_off, line_info_len)?,
core_relo_rec_size: rec_size(core_relo_off, core_relo_len)?,
data: data.to_vec(),
_endianness: endianness,
};
let func_info_rec_size = ext.func_info_rec_size;
ext.func_info.data.extend(
SecInfoIter::new(ext.func_info_data(), ext.func_info_rec_size, endianness)
.map(move |sec| {
let name = btf
.string_at(sec.sec_name_off)
.ok()
.map(String::from)
.unwrap();
let info = FuncSecInfo::parse(
sec.sec_name_off,
sec.num_info,
func_info_rec_size,
sec.data,
endianness,
);
Ok((name, info))
})
.collect::<Result<HashMap<_, _>, _>>()?,
);
let line_info_rec_size = ext.line_info_rec_size;
ext.line_info.data.extend(
SecInfoIter::new(ext.line_info_data(), ext.line_info_rec_size, endianness)
.map(move |sec| {
let name = btf
.string_at(sec.sec_name_off)
.ok()
.map(String::from)
.unwrap();
let info = LineSecInfo::parse(
sec.sec_name_off,
sec.num_info,
line_info_rec_size,
sec.data,
endianness,
);
Ok((name, info))
})
.collect::<Result<HashMap<_, _>, _>>()?,
);
let rec_size = ext.core_relo_rec_size;
ext.relocations.extend(
SecInfoIter::new(ext.core_relo_data(), ext.core_relo_rec_size, endianness)
@ -406,9 +710,25 @@ impl BtfExt {
self.info_data(self.header.core_relo_off, self.header.core_relo_len)
}
fn func_info_data(&self) -> &[u8] {
self.info_data(self.header.func_info_off, self.header.func_info_len)
}
fn line_info_data(&self) -> &[u8] {
self.info_data(self.header.line_info_off, self.header.line_info_len)
}
pub(crate) fn relocations(&self) -> impl Iterator<Item = &(u32, Vec<Relocation>)> {
self.relocations.iter()
}
pub(crate) fn func_info_rec_size(&self) -> usize {
self.func_info_rec_size
}
pub(crate) fn line_info_rec_size(&self) -> usize {
self.line_info_rec_size
}
}
pub(crate) struct SecInfoIter<'a> {
@ -453,7 +773,7 @@ impl<'a> Iterator for SecInfoIter<'a> {
Some(SecInfo {
sec_name_off,
_num_info: num_info,
num_info,
data,
})
}
@ -462,12 +782,14 @@ impl<'a> Iterator for SecInfoIter<'a> {
#[derive(Debug)]
pub(crate) struct SecInfo<'a> {
sec_name_off: u32,
_num_info: u32,
num_info: u32,
data: &'a [u8],
}
#[cfg(test)]
mod tests {
use crate::generated::{btf_param, BTF_INT_SIGNED, BTF_VAR_STATIC};
use super::*;
#[test]
@ -530,12 +852,195 @@ mod tests {
0x5a, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x5f, 0x00, 0x5f, 0x6c, 0x69, 0x63,
0x65, 0x6e, 0x73, 0x65, 0x00, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x00,
];
assert_eq!(data.len(), 517);
let got = Btf::parse(data, Endianness::default());
match got {
Ok(_) => {}
Err(e) => panic!("{}", e),
}
let data2 = got.unwrap().to_bytes();
let btf = got.unwrap();
let data2 = btf.to_bytes();
assert_eq!(data2.len(), 517);
assert_eq!(data, data2);
let ext_data: &[u8] = &[
0x9f, 0xeb, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00,
0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
0x72, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x00,
0x00, 0x00, 0xa2, 0x00, 0x00, 0x00, 0x05, 0x2c, 0x00, 0x00,
];
assert_eq!(ext_data.len(), 80);
let got = BtfExt::parse(ext_data, Endianness::default(), &btf);
if let Err(e) = got {
panic!("{}", e)
}
}
#[test]
fn test_write_btf() {
let mut btf = Btf::new();
let name_offset = btf.add_string("int".to_string());
let int_type = BtfType::new_int(name_offset, 4, BTF_INT_SIGNED, 0);
btf.add_type(int_type);
let name_offset = btf.add_string("widget".to_string());
let int_type = BtfType::new_int(name_offset, 4, BTF_INT_SIGNED, 0);
btf.add_type(int_type);
let btf_bytes = btf.to_bytes();
let raw_btf = btf_bytes.as_slice();
let parsed = Btf::parse(raw_btf, Endianness::default());
match parsed {
Ok(btf) => {
assert_eq!(btf.string_at(1).unwrap(), "int");
assert_eq!(btf.string_at(5).unwrap(), "widget");
}
Err(e) => {
panic!("{}", e)
}
}
}
#[test]
fn test_sanitize_btf() {
let mut btf = Btf::new();
let name_offset = btf.add_string("int".to_string());
let int_type = BtfType::new_int(name_offset, 4, BTF_INT_SIGNED, 0);
let int_type_id = btf.add_type(int_type);
let name_offset = btf.add_string("foo".to_string());
let var_type = BtfType::new_var(name_offset, int_type_id, BTF_VAR_STATIC);
let var_type_id = btf.add_type(var_type);
let name_offset = btf.add_string(".data".to_string());
let variables = vec![btf_var_secinfo {
type_: var_type_id,
offset: 0,
size: 4,
}];
let datasec_type = BtfType::new_datasec(name_offset, variables, 4);
btf.add_type(datasec_type);
let name_offset = btf.add_string("float".to_string());
let float_type = BtfType::new_float(name_offset, 16);
btf.add_type(float_type);
let a_name = btf.add_string("a".to_string());
let b_name = btf.add_string("b".to_string());
let params = vec![
btf_param {
name_off: a_name,
type_: int_type_id,
},
btf_param {
name_off: b_name,
type_: int_type_id,
},
];
let func_proto = BtfType::new_func_proto(params, int_type_id);
let func_proto_type_id = btf.add_type(func_proto);
let add = btf.add_string("static".to_string());
let func = BtfType::new_func(add, func_proto_type_id, btf_func_linkage::BTF_FUNC_STATIC);
btf.add_type(func);
let c_name = btf.add_string("c".to_string());
let d_name = btf.add_string("d".to_string());
let params = vec![
btf_param {
name_off: c_name,
type_: int_type_id,
},
btf_param {
name_off: d_name,
type_: int_type_id,
},
];
let func_proto = BtfType::new_func_proto(params, int_type_id);
let func_proto_type_id = btf.add_type(func_proto);
let add = btf.add_string("global".to_string());
let func = BtfType::new_func(add, func_proto_type_id, btf_func_linkage::BTF_FUNC_GLOBAL);
btf.add_type(func);
let cases = HashMap::from([
(
"noop",
Features {
bpf_name: true,
btf: true,
btf_func: true,
btf_func_global: true,
btf_datasec: true,
btf_float: true,
},
),
(
"no datasec",
Features {
bpf_name: true,
btf: true,
btf_func: true,
btf_func_global: true,
btf_datasec: false,
btf_float: true,
},
),
(
"no float",
Features {
bpf_name: true,
btf: true,
btf_func: true,
btf_func_global: true,
btf_datasec: true,
btf_float: false,
},
),
(
"no func",
Features {
bpf_name: true,
btf: true,
btf_func: false,
btf_func_global: true,
btf_datasec: true,
btf_float: true,
},
),
(
"no global func",
Features {
bpf_name: true,
btf: true,
btf_func: true,
btf_func_global: false,
btf_datasec: true,
btf_float: true,
},
),
(
"all off",
Features {
bpf_name: true,
btf: true,
btf_func: false,
btf_func_global: false,
btf_datasec: false,
btf_float: false,
},
),
]);
for (name, features) in cases {
println!("[CASE] Sanitize {}", name);
let new_btf = btf.sanitize(&features).unwrap();
let raw_new_btf = new_btf.to_bytes();
Btf::parse(&raw_new_btf, Endianness::default()).unwrap();
}
}
}

@ -0,0 +1,189 @@
use std::{collections::HashMap, convert::TryInto};
use bytes::BufMut;
use object::Endianness;
use crate::{
generated::{bpf_func_info, bpf_line_info},
obj::relocation::INS_SIZE,
util::bytes_of,
};
/* The func_info subsection layout:
* record size for struct bpf_func_info in the func_info subsection
* struct btf_sec_func_info for section #1
* a list of bpf_func_info records for section #1
* where struct bpf_func_info mimics one in include/uapi/linux/bpf.h
* but may not be identical
* struct btf_sec_func_info for section #2
* a list of bpf_func_info records for section #2
* ......
*/
#[derive(Debug, Clone, Default)]
pub(crate) struct FuncSecInfo {
pub _sec_name_offset: u32,
pub num_info: u32,
pub func_info: Vec<bpf_func_info>,
}
impl FuncSecInfo {
pub(crate) fn parse(
sec_name_offset: u32,
num_info: u32,
rec_size: usize,
func_info_data: &[u8],
endianness: Endianness,
) -> FuncSecInfo {
let func_info = func_info_data
.chunks(rec_size)
.map(|data| {
let read_u32 = if endianness == Endianness::Little {
u32::from_le_bytes
} else {
u32::from_be_bytes
};
let mut offset = 0;
// ELF instruction offsets are in bytes
// Kernel instruction offsets are in instructions units
// We can convert by dividing the length in bytes by INS_SIZE
let insn_off =
read_u32(data[offset..offset + 4].try_into().unwrap()) / INS_SIZE as u32;
offset += 4;
let type_id = read_u32(data[offset..offset + 4].try_into().unwrap());
bpf_func_info { insn_off, type_id }
})
.collect();
FuncSecInfo {
_sec_name_offset: sec_name_offset,
num_info,
func_info,
}
}
pub(crate) fn func_info_bytes(&self) -> Vec<u8> {
let mut buf = vec![];
for l in &self.func_info {
// Safety: bpf_func_info is POD
buf.put(unsafe { bytes_of::<bpf_func_info>(l) })
}
buf
}
pub(crate) fn len(&self) -> usize {
self.func_info.len()
}
}
#[derive(Debug, Clone)]
pub(crate) struct FuncInfo {
pub data: HashMap<String, FuncSecInfo>,
}
impl FuncInfo {
pub(crate) fn new() -> FuncInfo {
FuncInfo {
data: HashMap::new(),
}
}
pub(crate) fn get(&self, name: &str) -> FuncSecInfo {
match self.data.get(name) {
Some(d) => d.clone(),
None => FuncSecInfo::default(),
}
}
}
#[derive(Debug, Clone, Default)]
pub(crate) struct LineSecInfo {
// each line info section has a header
pub _sec_name_offset: u32,
pub num_info: u32,
// followed by one or more bpf_line_info structs
pub line_info: Vec<bpf_line_info>,
}
impl LineSecInfo {
pub(crate) fn parse(
sec_name_offset: u32,
num_info: u32,
rec_size: usize,
func_info_data: &[u8],
endianness: Endianness,
) -> LineSecInfo {
let line_info = func_info_data
.chunks(rec_size)
.map(|data| {
let read_u32 = if endianness == Endianness::Little {
u32::from_le_bytes
} else {
u32::from_be_bytes
};
let mut offset = 0;
// ELF instruction offsets are in bytes
// Kernel instruction offsets are in instructions units
// We can convert by dividing the length in bytes by INS_SIZE
let insn_off =
read_u32(data[offset..offset + 4].try_into().unwrap()) / INS_SIZE as u32;
offset += 4;
let file_name_off = read_u32(data[offset..offset + 4].try_into().unwrap());
offset += 4;
let line_off = read_u32(data[offset..offset + 4].try_into().unwrap());
offset += 4;
let line_col = read_u32(data[offset..offset + 4].try_into().unwrap());
bpf_line_info {
insn_off,
file_name_off,
line_off,
line_col,
}
})
.collect();
LineSecInfo {
_sec_name_offset: sec_name_offset,
num_info,
line_info,
}
}
pub(crate) fn line_info_bytes(&self) -> Vec<u8> {
let mut buf = vec![];
for l in &self.line_info {
// Safety: bpf_func_info is POD
buf.put(unsafe { bytes_of::<bpf_line_info>(l) })
}
buf
}
pub(crate) fn len(&self) -> usize {
self.line_info.len()
}
}
#[derive(Debug, Clone)]
pub(crate) struct LineInfo {
pub data: HashMap<String, LineSecInfo>,
}
impl LineInfo {
pub(crate) fn new() -> LineInfo {
LineInfo {
data: HashMap::new(),
}
}
pub(crate) fn get(&self, name: &str) -> LineSecInfo {
match self.data.get(name) {
Some(d) => d.clone(),
None => LineSecInfo::default(),
}
}
}

@ -1,8 +1,10 @@
#[allow(clippy::module_inception)]
mod btf;
mod info;
mod relocation;
mod types;
pub use btf::*;
pub(crate) use info::*;
pub use relocation::RelocationError;
pub(crate) use types::*;

@ -1,5 +1,6 @@
use std::{
convert::{TryFrom, TryInto},
fmt::Display,
mem, ptr,
};
@ -7,11 +8,11 @@ use object::Endianness;
use crate::{
generated::{
btf_array, btf_enum, btf_member, btf_param, btf_type, btf_type__bindgen_ty_1, btf_var,
btf_var_secinfo, BTF_KIND_ARRAY, BTF_KIND_CONST, BTF_KIND_DATASEC, BTF_KIND_ENUM,
BTF_KIND_FLOAT, BTF_KIND_FUNC, BTF_KIND_FUNC_PROTO, BTF_KIND_FWD, BTF_KIND_INT,
BTF_KIND_PTR, BTF_KIND_RESTRICT, BTF_KIND_STRUCT, BTF_KIND_TYPEDEF, BTF_KIND_UNION,
BTF_KIND_UNKN, BTF_KIND_VAR, BTF_KIND_VOLATILE,
btf_array, btf_enum, btf_func_linkage, btf_member, btf_param, btf_type,
btf_type__bindgen_ty_1, btf_var, btf_var_secinfo, BTF_KIND_ARRAY, BTF_KIND_CONST,
BTF_KIND_DATASEC, BTF_KIND_ENUM, BTF_KIND_FLOAT, BTF_KIND_FUNC, BTF_KIND_FUNC_PROTO,
BTF_KIND_FWD, BTF_KIND_INT, BTF_KIND_PTR, BTF_KIND_RESTRICT, BTF_KIND_STRUCT,
BTF_KIND_TYPEDEF, BTF_KIND_UNION, BTF_KIND_UNKN, BTF_KIND_VAR, BTF_KIND_VOLATILE,
},
obj::btf::{Btf, BtfError, MAX_RESOLVE_DEPTH},
};
@ -87,6 +88,36 @@ 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]"),
}
}
}
impl Default for BtfKind {
fn default() -> Self {
BtfKind::Unknown
}
}
unsafe fn read<T>(data: &[u8]) -> Result<T, BtfError> {
if mem::size_of::<T>() > data.len() {
return Err(BtfError::InvalidTypeInfo);
@ -170,7 +201,7 @@ impl BtfType {
}
BtfType::Enum(btf_type, enums) => {
let mut buf = bytes_of::<btf_type>(btf_type).to_vec();
for en in enums {
for en in enums {
buf.append(&mut bytes_of::<btf_enum>(en).to_vec());
}
buf
@ -267,13 +298,109 @@ impl BtfType {
pub(crate) fn is_composite(&self) -> bool {
matches!(self, BtfType::Struct(_, _) | BtfType::Union(_, _))
}
pub(crate) fn new_int(name_off: u32, size: u32, encoding: u32, offset: u32) -> BtfType {
let info = (BTF_KIND_INT) << 24;
let mut btf_type = unsafe { std::mem::zeroed::<btf_type>() };
btf_type.name_off = name_off;
btf_type.info = info;
btf_type.__bindgen_anon_1.size = size;
let mut data = 0u32;
data |= (encoding & 0x0f) << 24;
data |= (offset & 0xff) << 16;
data |= (size * 8) & 0xff;
BtfType::Int(btf_type, data)
}
pub(crate) fn new_func(name_off: u32, proto: u32, linkage: btf_func_linkage) -> BtfType {
let mut info = (BTF_KIND_FUNC) << 24;
info |= (linkage as u32) & 0xFFFF;
let mut btf_type = unsafe { std::mem::zeroed::<btf_type>() };
btf_type.name_off = name_off;
btf_type.info = info;
btf_type.__bindgen_anon_1.type_ = proto;
BtfType::Func(btf_type)
}
pub(crate) fn new_func_proto(params: Vec<btf_param>, return_type: u32) -> BtfType {
let mut info = (BTF_KIND_FUNC_PROTO) << 24;
info |= (params.len() as u32) & 0xFFFF;
let mut btf_type = unsafe { std::mem::zeroed::<btf_type>() };
btf_type.name_off = 0;
btf_type.info = info;
btf_type.__bindgen_anon_1.type_ = return_type;
BtfType::FuncProto(btf_type, params)
}
pub(crate) fn new_var(name_off: u32, type_: u32, linkage: u32) -> BtfType {
let info = (BTF_KIND_VAR) << 24;
let mut btf_type = unsafe { std::mem::zeroed::<btf_type>() };
btf_type.name_off = name_off;
btf_type.info = info;
btf_type.__bindgen_anon_1.type_ = type_;
let var = btf_var { linkage };
BtfType::Var(btf_type, var)
}
pub(crate) fn new_datasec(
name_off: u32,
variables: Vec<btf_var_secinfo>,
size: u32,
) -> BtfType {
let mut info = (BTF_KIND_DATASEC) << 24;
info |= (variables.len() as u32) & 0xFFFF;
let mut btf_type = unsafe { std::mem::zeroed::<btf_type>() };
btf_type.name_off = name_off;
btf_type.info = info;
btf_type.__bindgen_anon_1.size = size;
BtfType::DataSec(btf_type, variables)
}
pub(crate) fn new_float(name_off: u32, size: u32) -> BtfType {
let info = (BTF_KIND_FLOAT) << 24;
let mut btf_type = unsafe { std::mem::zeroed::<btf_type>() };
btf_type.name_off = name_off;
btf_type.info = info;
btf_type.__bindgen_anon_1.size = size;
BtfType::Float(btf_type)
}
pub(crate) fn new_struct(name_off: u32, members: Vec<btf_member>, size: u32) -> BtfType {
let mut info = (BTF_KIND_STRUCT) << 24;
info |= (members.len() as u32) & 0xFFFF;
let mut btf_type = unsafe { std::mem::zeroed::<btf_type>() };
btf_type.name_off = name_off;
btf_type.info = info;
btf_type.__bindgen_anon_1.size = size;
BtfType::Struct(btf_type, members)
}
pub(crate) fn new_enum(name_off: u32, members: Vec<btf_enum>) -> BtfType {
let mut info = (BTF_KIND_ENUM) << 24;
info |= (members.len() as u32) & 0xFFFF;
let mut btf_type = unsafe { std::mem::zeroed::<btf_type>() };
btf_type.name_off = name_off;
btf_type.info = info;
btf_type.__bindgen_anon_1.size = 4;
BtfType::Enum(btf_type, members)
}
pub(crate) fn new_typedef(name_off: u32, type_: u32) -> BtfType {
let info = (BTF_KIND_TYPEDEF) << 24;
let mut btf_type = unsafe { std::mem::zeroed::<btf_type>() };
btf_type.name_off = name_off;
btf_type.info = info;
btf_type.__bindgen_anon_1.type_ = type_;
BtfType::Typedef(btf_type)
}
}
fn type_kind(ty: &btf_type) -> Result<BtfKind, BtfError> {
((ty.info >> 24) & 0x1F).try_into()
}
fn type_vlen(ty: &btf_type) -> usize {
pub(crate) fn type_vlen(ty: &btf_type) -> usize {
(ty.info & 0xFFFF) as usize
}
@ -458,6 +585,8 @@ impl std::fmt::Debug for btf_type__bindgen_ty_1 {
#[cfg(test)]
mod tests {
use crate::generated::BTF_INT_SIGNED;
use super::*;
#[test]
@ -471,13 +600,44 @@ mod tests {
match got {
Ok(BtfType::Int(ty, nr_bits)) => {
assert_eq!(ty.name_off, 1);
assert_eq!(unsafe { ty.__bindgen_anon_1.size }, 8);
assert_eq!(nr_bits, 64);
}
Ok(t) => panic!("expected int type, got {:#?}", t),
Err(_) => panic!("unexpected error"),
}
let data2 = got.unwrap().to_bytes();
assert_eq!(data, data2.as_slice())
assert_eq!(data, data2.as_slice());
}
#[test]
fn test_write_btf_long_unsigned_int() {
let data: &[u8] = &[
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x00, 0x40, 0x00,
0x00, 0x00,
];
let int = BtfType::new_int(1, 8, 0, 0);
assert_eq!(int.to_bytes(), data);
}
#[test]
fn test_write_btf_uchar() {
let data: &[u8] = &[
0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00,
0x00, 0x00,
];
let int = BtfType::new_int(0x13, 1, 0, 0);
assert_eq!(int.to_bytes(), data);
}
#[test]
fn test_write_btf_signed_short_int() {
let data: &[u8] = &[
0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00,
0x00, 0x01,
];
let int = BtfType::new_int(0x4a, 2, BTF_INT_SIGNED, 0);
assert_eq!(int.to_bytes(), data);
}
#[test]
@ -698,14 +858,20 @@ mod tests {
#[test]
fn test_read_btf_type_func_datasec() {
let endianness = Endianness::default();
// NOTE: There was no data in /sys/kernell/btf/vmlinux for this type
let data: &[u8] = &[
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xd9, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
];
let got = unsafe { BtfType::read(data, endianness) };
match got {
Ok(BtfType::DataSec(_, _)) => {}
match &got {
Ok(BtfType::DataSec(ty, info)) => {
assert_eq!(0, unsafe { ty.__bindgen_anon_1.size } as usize);
assert_eq!(1, type_vlen(ty) as usize);
assert_eq!(1, info.len());
assert_eq!(11, info[0].type_);
assert_eq!(0, info[0].offset);
assert_eq!(4, info[0].size);
}
Ok(t) => panic!("expected datasec type, got {:#?}", t),
Err(_) => panic!("unexpected error"),
}
@ -728,4 +894,28 @@ mod tests {
let data2 = got.unwrap().to_bytes();
assert_eq!(data, data2.as_slice())
}
#[test]
fn test_write_btf_func_proto() {
let params = vec![
btf_param {
name_off: 1,
type_: 1,
},
btf_param {
name_off: 3,
type_: 1,
},
];
let func_proto = BtfType::new_func_proto(params, 2);
let data = func_proto.to_bytes();
let got = unsafe { BtfType::read(&data, Endianness::default()) };
match got {
Ok(BtfType::FuncProto(btf_type, _params)) => {
assert_eq!(type_vlen(&btf_type), 2);
}
Ok(t) => panic!("expected func proto type, got {:#?}", t),
Err(_) => panic!("unexpected error"),
}
}
}

@ -25,6 +25,8 @@ use crate::{
};
use std::slice::from_raw_parts_mut;
use self::btf::{FuncSecInfo, LineSecInfo};
const KERNEL_VERSION_ANY: u32 = 0xFFFF_FFFE;
/// The first five __u32 of `bpf_map_def` must be defined.
const MINIMUM_MAP_SIZE: usize = mem::size_of::<u32>() * 5;
@ -41,6 +43,10 @@ pub struct Object {
pub(crate) functions: HashMap<u64, Function>,
pub(crate) relocations: HashMap<SectionIndex, HashMap<u64, Relocation>>,
pub(crate) symbols_by_index: HashMap<usize, Symbol>,
pub(crate) section_sizes: HashMap<String, u64>,
// symbol_offset_by_name caches symbols that could be referenced from a
// BTF VAR type so the offsets can be fixed up
pub(crate) symbol_offset_by_name: HashMap<String, u64>,
}
#[derive(Debug, Clone, PartialEq)]
@ -88,6 +94,10 @@ pub(crate) struct Function {
pub(crate) section_index: SectionIndex,
pub(crate) section_offset: usize,
pub(crate) instructions: Vec<bpf_insn>,
pub(crate) func_info: FuncSecInfo,
pub(crate) line_info: LineSecInfo,
pub(crate) func_info_rec_size: usize,
pub(crate) line_info_rec_size: usize,
}
#[derive(Debug, Clone)]
@ -113,6 +123,7 @@ pub enum ProgramSection {
BtfTracePoint { name: String },
FEntry { name: String },
FExit { name: String },
Extension { name: String },
}
impl ProgramSection {
@ -139,6 +150,7 @@ impl ProgramSection {
ProgramSection::BtfTracePoint { name } => name,
ProgramSection::FEntry { name } => name,
ProgramSection::FExit { name } => name,
ProgramSection::Extension { name } => name,
}
}
}
@ -194,6 +206,7 @@ impl FromStr for ProgramSection {
"lsm" => Lsm { name },
"fentry" => FEntry { name },
"fexit" => FExit { name },
"freplace" => Extension { name },
_ => {
return Err(ParseError::InvalidProgramSection {
section: section.to_owned(),
@ -224,9 +237,14 @@ impl Object {
if let Some(symbol_table) = obj.symbol_table() {
for symbol in symbol_table.symbols() {
let name = symbol
.name()
.ok()
.map(String::from)
.ok_or(BtfError::InvalidSymbolName)?;
let sym = Symbol {
index: symbol.index().0,
name: symbol.name().ok().map(String::from),
name: Some(name.clone()),
section_index: symbol.section().index(),
address: symbol.address(),
size: symbol.size(),
@ -236,10 +254,30 @@ impl Object {
bpf_obj
.symbols_by_index
.insert(symbol.index().0, sym.clone());
if symbol.is_global() || symbol.kind() == SymbolKind::Data {
bpf_obj.symbol_offset_by_name.insert(name, symbol.address());
}
}
}
// .BTF and .BTF.ext sections must be parsed first
// as they're required to prepare function and line information
// when parsing program sections
if let Some(s) = obj.section_by_name(".BTF") {
bpf_obj.parse_section(Section::try_from(&s)?)?;
if let Some(s) = obj.section_by_name(".BTF.ext") {
bpf_obj.parse_section(Section::try_from(&s)?)?;
}
}
for s in obj.sections() {
if let Ok(name) = s.name() {
if name == ".BTF" || name == ".BTF.ext" {
continue;
}
}
bpf_obj.parse_section(Section::try_from(&s)?)?;
}
@ -258,6 +296,8 @@ impl Object {
functions: HashMap::new(),
relocations: HashMap::new(),
symbols_by_index: HashMap::new(),
section_sizes: HashMap::new(),
symbol_offset_by_name: HashMap::new(),
}
}
@ -313,13 +353,32 @@ impl Object {
}
fn parse_btf_ext(&mut self, section: &Section) -> Result<(), BtfError> {
self.btf_ext = Some(BtfExt::parse(section.data, self.endianness)?);
self.btf_ext = Some(BtfExt::parse(
section.data,
self.endianness,
self.btf.as_ref().unwrap(),
)?);
Ok(())
}
fn parse_program(&self, section: &Section) -> Result<Program, ParseError> {
let prog_sec = ProgramSection::from_str(section.name)?;
let name = prog_sec.name().to_owned();
let (func_info, line_info, func_info_rec_size, line_info_rec_size) =
if let Some(btf_ext) = &self.btf_ext {
let func_info = btf_ext.func_info.get(section.name);
let line_info = btf_ext.line_info.get(section.name);
(
func_info,
line_info,
btf_ext.func_info_rec_size(),
btf_ext.line_info_rec_size(),
)
} else {
(FuncSecInfo::default(), LineSecInfo::default(), 0, 0)
};
Ok(Program {
license: self.license.clone(),
kernel_version: self.kernel_version,
@ -330,6 +389,10 @@ impl Object {
section_index: section.index,
section_offset: 0,
instructions: copy_instructions(section.data)?,
func_info,
line_info,
func_info_rec_size,
line_info_rec_size,
},
})
}
@ -365,6 +428,38 @@ impl Object {
});
}
let (func_info, line_info, func_info_rec_size, line_info_rec_size) =
if let Some(btf_ext) = &self.btf_ext {
let bytes_offset = offset as u32 / INS_SIZE as u32;
let section_size_bytes = sym.size as u32 / INS_SIZE as u32;
let mut func_info = btf_ext.func_info.get(section.name);
func_info.func_info = func_info
.func_info
.into_iter()
.filter(|f| f.insn_off == bytes_offset)
.collect();
let mut line_info = btf_ext.line_info.get(section.name);
line_info.line_info = line_info
.line_info
.into_iter()
.filter(|l| {
l.insn_off >= bytes_offset
&& l.insn_off < (bytes_offset + section_size_bytes) as u32
})
.collect();
(
func_info,
line_info,
btf_ext.func_info_rec_size(),
btf_ext.line_info_rec_size(),
)
} else {
(FuncSecInfo::default(), LineSecInfo::default(), 0, 0)
};
self.functions.insert(
sym.address,
Function {
@ -375,6 +470,10 @@ impl Object {
instructions: copy_instructions(
&section.data[offset..offset + sym.size as usize],
)?,
func_info,
line_info,
func_info_rec_size,
line_info_rec_size,
},
);
@ -407,7 +506,8 @@ impl Object {
{
parts.push(parts[0]);
}
self.section_sizes
.insert(section.name.to_owned(), section.size);
match section.kind {
BpfSectionKind::Data => {
self.maps
@ -436,8 +536,10 @@ impl Object {
);
}
}
_ => {}
BpfSectionKind::Undefined
| BpfSectionKind::BtfMaps
| BpfSectionKind::License
| BpfSectionKind::Version => {}
}
Ok(())
@ -700,7 +802,7 @@ fn parse_map_def(name: &str, data: &[u8]) -> Result<bpf_map_def, ParseError> {
}
}
fn copy_instructions(data: &[u8]) -> Result<Vec<bpf_insn>, ParseError> {
pub(crate) fn copy_instructions(data: &[u8]) -> Result<Vec<bpf_insn>, ParseError> {
if data.len() % mem::size_of::<bpf_insn>() > 0 {
return Err(ParseError::InvalidProgramCode);
}
@ -978,9 +1080,8 @@ mod tests {
address: 0,
section_index: SectionIndex(0),
section_offset: 0,
instructions
}
}) if license.to_string_lossy() == "GPL" && name == "foo" && instructions.len() == 1
instructions,
..} }) if license.to_string_lossy() == "GPL" && name == "foo" && instructions.len() == 1
);
}

@ -13,7 +13,7 @@ use crate::{
BpfError,
};
const INS_SIZE: usize = mem::size_of::<bpf_insn>();
pub(crate) const INS_SIZE: usize = mem::size_of::<bpf_insn>();
#[derive(Debug, Error)]
enum RelocationError {
@ -227,6 +227,11 @@ impl<'a> FunctionLinker<'a> {
// at `start_ins`. We'll use `start_ins` to do pc-relative calls.
let start_ins = program.instructions.len();
program.instructions.extend(&fun.instructions);
// link func and line info into the main program
// the offset needs to be adjusted
self.link_func_and_line_info(program, fun, start_ins)?;
self.linked_functions.insert(fun.address, start_ins);
// relocate `fun`, recursively linking in all the callees
@ -296,6 +301,33 @@ impl<'a> FunctionLinker<'a> {
Ok(())
}
fn link_func_and_line_info(
&mut self,
program: &mut Function,
fun: &Function,
start: usize,
) -> Result<(), RelocationError> {
let off_adj = start - (fun.section_offset as usize / INS_SIZE);
let func_info = &fun.func_info.func_info;
let func_info = func_info.iter().map(|f| {
let mut new = *f;
new.insn_off = f.insn_off + off_adj as u32;
new
});
program.func_info.func_info.extend(func_info);
program.func_info.num_info = program.func_info.func_info.len() as u32;
let line_info = &fun.line_info.line_info;
let line_info = line_info.iter().map(|l| {
let mut new = *l;
new.insn_off = l.insn_off + off_adj as u32;
new
});
program.line_info.line_info.extend(line_info);
program.line_info.num_info = program.func_info.func_info.len() as u32;
Ok(())
}
}
fn is_call(ins: &bpf_insn) -> bool {

@ -88,13 +88,12 @@ impl CgroupSkb {
};
let k_ver = kernel_version().unwrap();
if k_ver >= (5, 7, 0) {
let link_fd =
bpf_link_create(prog_fd, cgroup_fd, attach_type, 0).map_err(|(_, io_error)| {
ProgramError::SyscallError {
call: "bpf_link_create".to_owned(),
io_error,
}
})? as RawFd;
let link_fd = bpf_link_create(prog_fd, cgroup_fd, attach_type, None, 0).map_err(
|(_, io_error)| ProgramError::SyscallError {
call: "bpf_link_create".to_owned(),
io_error,
},
)? as RawFd;
Ok(self.data.link(FdLink { fd: Some(link_fd) }))
} else {
bpf_prog_attach(prog_fd, cgroup_fd, attach_type).map_err(|(_, io_error)| {

@ -0,0 +1,140 @@
use std::os::unix::prelude::{AsRawFd, RawFd};
use thiserror::Error;
use object::Endianness;
use crate::{
generated::{bpf_attach_type::BPF_CGROUP_INET_INGRESS, bpf_prog_type::BPF_PROG_TYPE_EXT},
obj::btf::BtfKind,
programs::{load_program, FdLink, LinkRef, ProgramData, ProgramError},
sys::{self, bpf_link_create},
Btf,
};
/// The type returned when loading or attaching an [`Extension`] fails
#[derive(Debug, Error)]
pub enum ExtensionError {
#[error("target BPF program does not have BTF loaded to the kernel")]
NoBTF,
}
/// A program used to extend existing BPF programs
///
/// [`Extension`] programs can be loaded to replace a global
/// function in a program that has already been loaded.
///
/// # Minimum kernel version
///
/// The minimum kernel version required to use this feature is 5.9
///
/// # Examples
///
/// ```no_run
/// use aya::{BpfLoader, programs::{Xdp, XdpFlags, Extension, ProgramFd}};
/// use std::convert::TryInto;
///
/// let mut bpf = BpfLoader::new().extension("extension").load_file("app.o")?;
/// let prog: &mut Xdp = bpf.program_mut("main").unwrap().try_into()?;
/// prog.load()?;
/// prog.attach("eth0", XdpFlags::default())?;
///
/// let prog_fd = prog.fd().unwrap();
/// let ext: &mut Extension = bpf.program_mut("extension").unwrap().try_into()?;
/// ext.load(prog_fd, "function_to_replace")?;
/// ext.attach()?;
/// Ok::<(), aya::BpfError>(())
/// ```
#[derive(Debug)]
#[doc(alias = "BPF_PROG_TYPE_EXT")]
pub struct Extension {
pub(crate) data: ProgramData,
}
impl Extension {
/// Loads the extension inside the kernel.
///
/// Prepares the code included in the extension to replace the code of the function
/// `func_name` within the eBPF program represented by the `program` file descriptor.
/// This requires that both the [`Extension`] and `program` have had their BTF
/// loaded into the kernel as the verifier must check that the function signatures
/// match.
///
/// The extension code will be loaded but inactive until it's attached.
/// There are no restrictions on what functions may be replaced, so you could replace
/// the main entry point of your program with an extension.
///
/// See also [`Program::load`](crate::programs::Program::load).
pub fn load<T: AsRawFd>(&mut self, program: T, func_name: &str) -> Result<(), ProgramError> {
let target_prog_fd = program.as_raw_fd();
let info = sys::bpf_obj_get_info_by_fd(target_prog_fd).map_err(|io_error| {
ProgramError::SyscallError {
call: "bpf_obj_get_info_by_fd".to_owned(),
io_error,
}
})?;
if info.btf_id == 0 {
return Err(ProgramError::ExtensionError(ExtensionError::NoBTF));
}
let btf_fd = sys::bpf_btf_get_fd_by_id(info.btf_id).map_err(|io_error| {
ProgramError::SyscallError {
call: "bpf_btf_get_fd_by_id".to_owned(),
io_error,
}
})?;
let mut buf = vec![0u8; 4096];
let btf_info = match sys::btf_obj_get_info_by_fd(btf_fd, &mut buf) {
Ok(info) => {
if info.btf_size > buf.len() as u32 {
buf.resize(info.btf_size as usize, 0u8);
let btf_info =
sys::btf_obj_get_info_by_fd(btf_fd, &mut buf).map_err(|io_error| {
ProgramError::SyscallError {
call: "bpf_obj_get_info_by_fd".to_owned(),
io_error,
}
})?;
Ok(btf_info)
} else {
Ok(info)
}
}
Err(io_error) => Err(ProgramError::SyscallError {
call: "bpf_obj_get_info_by_fd".to_owned(),
io_error,
}),
}?;
let btf = Btf::parse(&buf[0..btf_info.btf_size as usize], Endianness::default())
.map_err(ProgramError::Btf)?;
let btf_id = btf
.id_by_type_name_kind(func_name, BtfKind::Func)
.map_err(ProgramError::Btf)?;
self.data.attach_btf_obj_fd = Some(btf_fd as u32);
self.data.attach_prog_fd = Some(target_prog_fd);
self.data.attach_btf_id = Some(btf_id);
load_program(BPF_PROG_TYPE_EXT, &mut self.data)
}
/// Attaches the extension
///
/// Attaches the extension effectively replacing the original target function.
/// Detaching the returned link restores the original function.
pub fn attach(&mut self) -> Result<LinkRef, ProgramError> {
let prog_fd = self.data.fd_or_err()?;
let target_fd = self.data.attach_prog_fd.ok_or(ProgramError::NotLoaded)?;
let btf_id = self.data.attach_btf_id.ok_or(ProgramError::NotLoaded)?;
// the attach type must be set as 0, which is bpf_attach_type::BPF_CGROUP_INET_INGRESS
let link_fd = bpf_link_create(prog_fd, target_fd, BPF_CGROUP_INET_INGRESS, Some(btf_id), 0)
.map_err(|(_, io_error)| ProgramError::SyscallError {
call: "bpf_link_create".to_owned(),
io_error,
})? as RawFd;
Ok(self.data.link(FdLink { fd: Some(link_fd) }))
}
}

@ -37,6 +37,7 @@
//! [`Bpf::program_mut`]: crate::Bpf::program_mut
//! [`maps`]: crate::maps
mod cgroup_skb;
mod extension;
mod fentry;
mod fexit;
mod kprobe;
@ -60,9 +61,8 @@ mod xdp;
use libc::{close, dup, ENOSPC};
use std::{
cell::RefCell,
cmp,
convert::TryFrom,
ffi::{CStr, CString},
ffi::CString,
io,
os::unix::io::{AsRawFd, RawFd},
path::Path,
@ -71,6 +71,7 @@ use std::{
use thiserror::Error;
pub use cgroup_skb::{CgroupSkb, CgroupSkbAttachType};
pub use extension::{Extension, ExtensionError};
pub use fentry::FEntry;
pub use fexit::FExit;
pub use kprobe::{KProbe, KProbeError};
@ -95,6 +96,7 @@ use crate::{
maps::MapError,
obj::{self, btf::BtfError, Function, KernelVersion},
sys::{bpf_load_program, bpf_pin_object, bpf_prog_detach, bpf_prog_query, BpfLoadProgramAttrs},
util::VerifierLog,
};
/// Error type returned when working with programs.
@ -175,9 +177,21 @@ pub enum ProgramError {
#[error(transparent)]
TcError(#[from] TcError),
/// An error occurred while working with an [`Extension`] program.
#[error(transparent)]
ExtensionError(#[from] ExtensionError),
/// An error occurred while working with BTF.
#[error(transparent)]
Btf(#[from] BtfError),
/// The program is not attached.
#[error("the program name `{name}` is invalid")]
InvalidName { name: String },
/// The program is too long.
#[error("the program name `{name}` it longer than 16 characters")]
NameTooLong { name: String },
}
pub trait ProgramFd {
@ -204,6 +218,7 @@ pub enum Program {
BtfTracePoint(BtfTracePoint),
FEntry(FEntry),
FExit(FExit),
Extension(Extension),
}
impl Program {
@ -242,6 +257,7 @@ impl Program {
Program::BtfTracePoint(_) => BPF_PROG_TYPE_TRACING,
Program::FEntry(_) => BPF_PROG_TYPE_TRACING,
Program::FExit(_) => BPF_PROG_TYPE_TRACING,
Program::Extension(_) => BPF_PROG_TYPE_EXT,
}
}
@ -269,6 +285,7 @@ impl Program {
Program::BtfTracePoint(p) => &p.data,
Program::FEntry(p) => &p.data,
Program::FExit(p) => &p.data,
Program::Extension(p) => &p.data,
}
}
@ -291,18 +308,22 @@ impl Program {
Program::BtfTracePoint(p) => &mut p.data,
Program::FEntry(p) => &mut p.data,
Program::FExit(p) => &mut p.data,
Program::Extension(p) => &mut p.data,
}
}
}
#[derive(Debug)]
#[derive(Debug, Clone)]
pub(crate) struct ProgramData {
pub(crate) name: Option<String>,
pub(crate) obj: obj::Program,
pub(crate) fd: Option<RawFd>,
pub(crate) links: Vec<Rc<RefCell<dyn Link>>>,
pub(crate) expected_attach_type: Option<bpf_attach_type>,
pub(crate) attach_btf_obj_fd: Option<u32>,
pub(crate) attach_btf_id: Option<u32>,
pub(crate) attach_prog_fd: Option<RawFd>,
pub(crate) btf_fd: Option<RawFd>,
}
impl ProgramData {
@ -334,67 +355,21 @@ impl ProgramData {
}
}
const MIN_LOG_BUF_SIZE: usize = 1024 * 10;
const MAX_LOG_BUF_SIZE: usize = (std::u32::MAX >> 8) as usize;
pub(crate) struct VerifierLog {
buf: Vec<u8>,
}
impl VerifierLog {
fn new() -> VerifierLog {
VerifierLog { buf: Vec::new() }
}
pub(crate) fn buf(&mut self) -> &mut Vec<u8> {
&mut self.buf
}
fn grow(&mut self) {
let len = cmp::max(
MIN_LOG_BUF_SIZE,
cmp::min(MAX_LOG_BUF_SIZE, self.buf.capacity() * 10),
);
self.buf.resize(len, 0);
self.reset();
}
fn reset(&mut self) {
if !self.buf.is_empty() {
self.buf[0] = 0;
}
}
fn truncate(&mut self) {
if self.buf.is_empty() {
return;
}
let pos = self
.buf
.iter()
.position(|b| *b == 0)
.unwrap_or(self.buf.len() - 1);
self.buf[pos] = 0;
self.buf.truncate(pos + 1);
}
pub fn as_c_str(&self) -> Option<&CStr> {
if self.buf.is_empty() {
None
} else {
Some(CStr::from_bytes_with_nul(&self.buf).unwrap())
}
}
}
fn load_program(prog_type: bpf_prog_type, data: &mut ProgramData) -> Result<(), ProgramError> {
let ProgramData { obj, fd, .. } = data;
if fd.is_some() {
return Err(ProgramError::AlreadyLoaded);
}
let crate::obj::Program {
function: Function { instructions, .. },
function:
Function {
instructions,
func_info,
line_info,
func_info_rec_size,
line_info_rec_size,
..
},
license,
kernel_version,
..
@ -411,16 +386,37 @@ fn load_program(prog_type: bpf_prog_type, data: &mut ProgramData) -> Result<(),
let mut log_buf = VerifierLog::new();
let mut retries = 0;
let mut ret;
let prog_name = if let Some(name) = &data.name {
let name = name.clone();
let prog_name = CString::new(name.clone())
.map_err(|_| ProgramError::InvalidName { name: name.clone() })?;
if prog_name.to_bytes().len() > 16 {
return Err(ProgramError::NameTooLong { name });
}
Some(prog_name)
} else {
None
};
loop {
let attr = BpfLoadProgramAttrs {
name: prog_name.clone(),
ty: prog_type,
insns: instructions,
license,
kernel_version: target_kernel_version,
expected_attach_type: data.expected_attach_type,
prog_btf_fd: data.btf_fd,
attach_btf_obj_fd: data.attach_btf_obj_fd,
attach_btf_id: data.attach_btf_id,
attach_prog_fd: data.attach_prog_fd,
log: &mut log_buf,
func_info_rec_size: *func_info_rec_size,
func_info: func_info.clone(),
line_info_rec_size: *line_info_rec_size,
line_info: line_info.clone(),
};
ret = bpf_load_program(attr);
match &ret {
@ -491,7 +487,7 @@ pub(crate) fn query<T: AsRawFd>(
}
}
/// Detach an attached program.
/// Detach an attached program
pub trait Link: std::fmt::Debug {
fn detach(&mut self) -> Result<(), ProgramError>;
}
@ -599,6 +595,12 @@ macro_rules! impl_program_fd {
self.data.fd
}
}
impl ProgramFd for &mut $struct_name {
fn fd(&self) -> Option<RawFd> {
self.data.fd
}
}
)+
}
}
@ -620,6 +622,7 @@ impl_program_fd!(
BtfTracePoint,
FEntry,
FExit,
Extension,
);
macro_rules! impl_try_from_program {
@ -668,6 +671,7 @@ impl_try_from_program!(
BtfTracePoint,
FEntry,
FExit,
Extension,
);
/// Provides information about a loaded program, like name, id and statistics

@ -5,8 +5,10 @@ use thiserror::Error;
use crate::{
generated::{
bpf_attach_type::BPF_XDP, bpf_prog_type::BPF_PROG_TYPE_XDP, XDP_FLAGS_DRV_MODE,
XDP_FLAGS_HW_MODE, XDP_FLAGS_REPLACE, XDP_FLAGS_SKB_MODE, XDP_FLAGS_UPDATE_IF_NOEXIST,
bpf_attach_type::{self, BPF_XDP},
bpf_prog_type::BPF_PROG_TYPE_XDP,
XDP_FLAGS_DRV_MODE, XDP_FLAGS_HW_MODE, XDP_FLAGS_REPLACE, XDP_FLAGS_SKB_MODE,
XDP_FLAGS_UPDATE_IF_NOEXIST,
},
programs::{load_program, FdLink, Link, LinkRef, ProgramData, ProgramError},
sys::{bpf_link_create, kernel_version, netlink_set_xdp_fd},
@ -72,6 +74,7 @@ impl Xdp {
///
/// See also [`Program::load`](crate::programs::Program::load).
pub fn load(&mut self) -> Result<(), ProgramError> {
self.data.expected_attach_type = Some(bpf_attach_type::BPF_XDP);
load_program(BPF_PROG_TYPE_XDP, &mut self.data)
}
@ -88,7 +91,6 @@ impl Xdp {
/// kernels.
pub fn attach(&mut self, interface: &str, flags: XdpFlags) -> Result<LinkRef, ProgramError> {
let prog_fd = self.data.fd_or_err()?;
let c_interface = CString::new(interface).unwrap();
let if_index = unsafe { if_nametoindex(c_interface.as_ptr()) } as RawFd;
if if_index == 0 {
@ -99,7 +101,7 @@ impl Xdp {
let k_ver = kernel_version().unwrap();
if k_ver >= (5, 9, 0) {
let link_fd = bpf_link_create(prog_fd, if_index, BPF_XDP, flags.bits).map_err(
let link_fd = bpf_link_create(prog_fd, if_index, BPF_XDP, None, flags.bits).map_err(
|(_, io_error)| ProgramError::SyscallError {
call: "bpf_link_create".to_owned(),
io_error,

@ -1,20 +1,28 @@
use crate::{
generated::{btf_func_linkage, btf_param, btf_var_secinfo, BTF_INT_SIGNED, BTF_VAR_STATIC},
obj::{btf::BtfType, copy_instructions},
Btf,
};
use libc::{c_char, c_long, close, ENOENT};
use std::{
cmp,
ffi::CStr,
cmp::{self, min},
ffi::{CStr, CString},
io,
mem::{self, MaybeUninit},
os::unix::io::RawFd,
slice,
};
use libc::{c_long, ENOENT};
use crate::{
bpf_map_def,
generated::{bpf_attach_type, bpf_attr, bpf_cmd, bpf_insn, bpf_prog_info, bpf_prog_type},
generated::{
bpf_attach_type, bpf_attr, bpf_btf_info, bpf_cmd, bpf_insn, bpf_prog_info, bpf_prog_type,
},
maps::PerCpuValues,
programs::VerifierLog,
obj::btf::{FuncSecInfo, LineSecInfo},
sys::{kernel_version, SysResult},
util::VerifierLog,
Pod, BPF_OBJ_NAME_LEN,
};
@ -60,20 +68,38 @@ pub(crate) fn bpf_get_object(path: &CStr) -> SysResult {
}
pub(crate) struct BpfLoadProgramAttrs<'a> {
pub(crate) name: Option<CString>,
pub(crate) ty: bpf_prog_type,
pub(crate) insns: &'a [bpf_insn],
pub(crate) license: &'a CStr,
pub(crate) kernel_version: u32,
pub(crate) expected_attach_type: Option<bpf_attach_type>,
pub(crate) prog_btf_fd: Option<RawFd>,
pub(crate) attach_btf_obj_fd: Option<u32>,
pub(crate) attach_btf_id: Option<u32>,
pub(crate) attach_prog_fd: Option<RawFd>,
pub(crate) log: &'a mut VerifierLog,
pub(crate) func_info_rec_size: usize,
pub(crate) func_info: FuncSecInfo,
pub(crate) line_info_rec_size: usize,
pub(crate) line_info: LineSecInfo,
}
pub(crate) fn bpf_load_program(aya_attr: BpfLoadProgramAttrs) -> SysResult {
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
let u = unsafe { &mut attr.__bindgen_anon_3 };
if let Some(prog_name) = aya_attr.name {
let mut name: [c_char; 16] = [0; 16];
let name_bytes = prog_name.to_bytes();
let len = min(name.len(), name_bytes.len());
name[..len].copy_from_slice(unsafe {
slice::from_raw_parts(name_bytes.as_ptr() as *const c_char, len)
});
u.prog_name = name;
}
u.prog_type = aya_attr.ty as u32;
if let Some(v) = aya_attr.expected_attach_type {
u.expected_attach_type = v as u32;
@ -82,6 +108,22 @@ pub(crate) fn bpf_load_program(aya_attr: BpfLoadProgramAttrs) -> SysResult {
u.insn_cnt = aya_attr.insns.len() as u32;
u.license = aya_attr.license.as_ptr() as u64;
u.kern_version = aya_attr.kernel_version;
if let Some(btf_fd) = aya_attr.prog_btf_fd {
let line_info_buf = aya_attr.line_info.line_info_bytes();
let func_info_buf = aya_attr.func_info.func_info_bytes();
u.prog_btf_fd = btf_fd as u32;
if aya_attr.line_info_rec_size > 0 {
u.line_info = line_info_buf.as_ptr() as *const _ as u64;
u.line_info_cnt = aya_attr.line_info.len() as u32;
u.line_info_rec_size = aya_attr.line_info_rec_size as u32;
}
if aya_attr.func_info_rec_size > 0 {
u.func_info = func_info_buf.as_ptr() as *const _ as u64;
u.func_info_cnt = aya_attr.func_info.len() as u32;
u.func_info_rec_size = aya_attr.func_info_rec_size as u32;
}
}
let log_buf = aya_attr.log.buf();
if log_buf.capacity() > 0 {
u.log_level = 7;
@ -91,6 +133,10 @@ pub(crate) fn bpf_load_program(aya_attr: BpfLoadProgramAttrs) -> SysResult {
if let Some(v) = aya_attr.attach_btf_obj_fd {
u.__bindgen_anon_1.attach_btf_obj_fd = v;
}
if let Some(v) = aya_attr.attach_prog_fd {
u.__bindgen_anon_1.attach_prog_fd = v as u32;
}
if let Some(v) = aya_attr.attach_btf_id {
u.attach_btf_id = v;
}
@ -266,6 +312,7 @@ pub(crate) fn bpf_link_create(
prog_fd: RawFd,
target_fd: RawFd,
attach_type: bpf_attach_type,
btf_id: Option<u32>,
flags: u32,
) -> SysResult {
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
@ -274,6 +321,9 @@ pub(crate) fn bpf_link_create(
attr.link_create.__bindgen_anon_1.target_fd = target_fd as u32;
attr.link_create.attach_type = attach_type as u32;
attr.link_create.flags = flags;
if let Some(btf_id) = btf_id {
attr.link_create.__bindgen_anon_2.target_btf_id = btf_id;
}
sys_bpf(bpf_cmd::BPF_LINK_CREATE, &attr)
}
@ -359,6 +409,25 @@ pub(crate) fn bpf_obj_get_info_by_fd(prog_fd: RawFd) -> Result<bpf_prog_info, io
}
}
pub(crate) fn btf_obj_get_info_by_fd(
prog_fd: RawFd,
buf: &mut [u8],
) -> Result<bpf_btf_info, io::Error> {
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
let mut info = unsafe { mem::zeroed::<bpf_btf_info>() };
let buf_size = buf.len() as u32;
info.btf = buf.as_ptr() as u64;
info.btf_size = buf_size;
attr.info.bpf_fd = prog_fd as u32;
attr.info.info = &info as *const bpf_btf_info as u64;
attr.info.info_len = mem::size_of::<bpf_btf_info>() as u32;
match sys_bpf(bpf_cmd::BPF_OBJ_GET_INFO_BY_FD, &attr) {
Ok(_) => Ok(info),
Err((_, err)) => Err(err),
}
}
pub(crate) fn bpf_raw_tracepoint_open(name: Option<&CStr>, prog_fd: RawFd) -> SysResult {
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
@ -371,6 +440,233 @@ pub(crate) fn bpf_raw_tracepoint_open(name: Option<&CStr>, prog_fd: RawFd) -> Sy
sys_bpf(bpf_cmd::BPF_RAW_TRACEPOINT_OPEN, &attr)
}
fn sys_bpf(cmd: bpf_cmd, attr: &bpf_attr) -> SysResult {
pub(crate) fn bpf_load_btf(raw_btf: &[u8], log: &mut VerifierLog) -> Result<RawFd, io::Error> {
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
let u = unsafe { &mut attr.__bindgen_anon_7 };
u.btf = raw_btf.as_ptr() as *const _ as u64;
u.btf_size = mem::size_of_val(raw_btf) as u32;
let log_buf = log.buf();
if log_buf.capacity() > 0 {
u.btf_log_level = 1;
u.btf_log_buf = log_buf.as_mut_ptr() as u64;
u.btf_log_size = log_buf.capacity() as u32;
}
match sys_bpf(bpf_cmd::BPF_BTF_LOAD, &attr) {
Ok(v) => Ok(v as RawFd),
Err((_, err)) => Err(err),
}
}
pub(crate) fn bpf_btf_get_fd_by_id(id: u32) -> Result<RawFd, io::Error> {
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
attr.__bindgen_anon_6.__bindgen_anon_1.btf_id = id;
match sys_bpf(bpf_cmd::BPF_BTF_GET_FD_BY_ID, &attr) {
Ok(v) => Ok(v as RawFd),
Err((_, err)) => Err(err),
}
}
pub(crate) fn is_prog_name_supported() -> bool {
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
let u = unsafe { &mut attr.__bindgen_anon_3 };
let mut name: [c_char; 16] = [0; 16];
let cstring = CString::new("aya_name_check").unwrap();
let name_bytes = cstring.to_bytes();
let len = min(name.len(), name_bytes.len());
name[..len].copy_from_slice(unsafe {
slice::from_raw_parts(name_bytes.as_ptr() as *const c_char, len)
});
u.prog_name = name;
let prog: &[u8] = &[
0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov64 r0 = 0
0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exit
];
let gpl = b"GPL\0";
u.license = gpl.as_ptr() as u64;
let insns = copy_instructions(prog).unwrap();
u.insn_cnt = insns.len() as u32;
u.insns = insns.as_ptr() as u64;
u.prog_type = bpf_prog_type::BPF_PROG_TYPE_SOCKET_FILTER as u32;
match sys_bpf(bpf_cmd::BPF_PROG_LOAD, &attr) {
Ok(v) => {
let fd = v as RawFd;
unsafe { close(fd) };
true
}
Err(_) => false,
}
}
pub(crate) fn is_btf_supported() -> bool {
let mut btf = Btf::new();
let name_offset = btf.add_string("int".to_string());
let int_type = BtfType::new_int(name_offset, 4, BTF_INT_SIGNED, 0);
btf.add_type(int_type);
let btf_bytes = btf.to_bytes();
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
let u = unsafe { &mut attr.__bindgen_anon_7 };
u.btf = btf_bytes.as_ptr() as u64;
u.btf_size = btf_bytes.len() as u32;
match sys_bpf(bpf_cmd::BPF_BTF_LOAD, &attr) {
Ok(v) => {
let fd = v as RawFd;
unsafe { close(fd) };
true
}
Err(_) => false,
}
}
pub(crate) fn is_btf_func_supported() -> bool {
let mut btf = Btf::new();
let name_offset = btf.add_string("int".to_string());
let int_type = BtfType::new_int(name_offset, 4, BTF_INT_SIGNED, 0);
let int_type_id = btf.add_type(int_type);
let a_name = btf.add_string("a".to_string());
let b_name = btf.add_string("b".to_string());
let params = vec![
btf_param {
name_off: a_name,
type_: int_type_id,
},
btf_param {
name_off: b_name,
type_: int_type_id,
},
];
let func_proto = BtfType::new_func_proto(params, int_type_id);
let func_proto_type_id = btf.add_type(func_proto);
let add = btf.add_string("inc".to_string());
let func = BtfType::new_func(add, func_proto_type_id, btf_func_linkage::BTF_FUNC_STATIC);
btf.add_type(func);
let btf_bytes = btf.to_bytes();
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
let u = unsafe { &mut attr.__bindgen_anon_7 };
u.btf = btf_bytes.as_ptr() as u64;
u.btf_size = btf_bytes.len() as u32;
match sys_bpf(bpf_cmd::BPF_BTF_LOAD, &attr) {
Ok(v) => {
let fd = v as RawFd;
unsafe { close(fd) };
true
}
Err(_) => false,
}
}
pub(crate) fn is_btf_func_global_supported() -> bool {
let mut btf = Btf::new();
let name_offset = btf.add_string("int".to_string());
let int_type = BtfType::new_int(name_offset, 4, BTF_INT_SIGNED, 0);
let int_type_id = btf.add_type(int_type);
let a_name = btf.add_string("a".to_string());
let b_name = btf.add_string("b".to_string());
let params = vec![
btf_param {
name_off: a_name,
type_: int_type_id,
},
btf_param {
name_off: b_name,
type_: int_type_id,
},
];
let func_proto = BtfType::new_func_proto(params, int_type_id);
let func_proto_type_id = btf.add_type(func_proto);
let add = btf.add_string("inc".to_string());
let func = BtfType::new_func(add, func_proto_type_id, btf_func_linkage::BTF_FUNC_GLOBAL);
btf.add_type(func);
let btf_bytes = btf.to_bytes();
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
let u = unsafe { &mut attr.__bindgen_anon_7 };
u.btf = btf_bytes.as_ptr() as u64;
u.btf_size = btf_bytes.len() as u32;
match sys_bpf(bpf_cmd::BPF_BTF_LOAD, &attr) {
Ok(v) => {
let fd = v as RawFd;
unsafe { close(fd) };
true
}
Err(_) => false,
}
}
pub(crate) fn is_btf_datasec_supported() -> bool {
let mut btf = Btf::new();
let name_offset = btf.add_string("int".to_string());
let int_type = BtfType::new_int(name_offset, 4, BTF_INT_SIGNED, 0);
let int_type_id = btf.add_type(int_type);
let name_offset = btf.add_string("foo".to_string());
let var_type = BtfType::new_var(name_offset, int_type_id, BTF_VAR_STATIC);
let var_type_id = btf.add_type(var_type);
let name_offset = btf.add_string(".data".to_string());
let variables = vec![btf_var_secinfo {
type_: var_type_id,
offset: 0,
size: 4,
}];
let datasec_type = BtfType::new_datasec(name_offset, variables, 4);
btf.add_type(datasec_type);
let btf_bytes = btf.to_bytes();
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
let u = unsafe { &mut attr.__bindgen_anon_7 };
u.btf = btf_bytes.as_ptr() as u64;
u.btf_size = btf_bytes.len() as u32;
match sys_bpf(bpf_cmd::BPF_BTF_LOAD, &attr) {
Ok(v) => {
let fd = v as RawFd;
unsafe { close(fd) };
true
}
Err(_) => false,
}
}
pub(crate) fn is_btf_float_supported() -> bool {
let mut btf = Btf::new();
let name_offset = btf.add_string("float".to_string());
let float_type = BtfType::new_float(name_offset, 16);
btf.add_type(float_type);
let btf_bytes = btf.to_bytes();
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
let u = unsafe { &mut attr.__bindgen_anon_7 };
u.btf = btf_bytes.as_ptr() as u64;
u.btf_size = btf_bytes.len() as u32;
match sys_bpf(bpf_cmd::BPF_BTF_LOAD, &attr) {
Ok(v) => {
let fd = v as RawFd;
unsafe { close(fd) };
true
}
Err(_) => false,
}
}
pub fn sys_bpf(cmd: bpf_cmd, attr: &bpf_attr) -> SysResult {
syscall(Syscall::Bpf { cmd, attr })
}

@ -1,7 +1,8 @@
//! Utility functions.
use std::{
cmp,
collections::BTreeMap,
ffi::CString,
ffi::{CStr, CString},
fs::{self, File},
io::{self, BufReader},
mem, slice,
@ -155,6 +156,60 @@ pub(crate) unsafe fn bytes_of<T>(val: &T) -> &[u8] {
slice::from_raw_parts(slice::from_ref(val).as_ptr().cast(), size)
}
const MIN_LOG_BUF_SIZE: usize = 1024 * 10;
const MAX_LOG_BUF_SIZE: usize = (std::u32::MAX >> 8) as usize;
pub(crate) struct VerifierLog {
buf: Vec<u8>,
}
impl VerifierLog {
pub(crate) fn new() -> VerifierLog {
VerifierLog { buf: Vec::new() }
}
pub(crate) fn buf(&mut self) -> &mut Vec<u8> {
&mut self.buf
}
pub(crate) fn grow(&mut self) {
let len = cmp::max(
MIN_LOG_BUF_SIZE,
cmp::min(MAX_LOG_BUF_SIZE, self.buf.capacity() * 10),
);
self.buf.resize(len, 0);
self.reset();
}
pub(crate) fn reset(&mut self) {
if !self.buf.is_empty() {
self.buf[0] = 0;
}
}
pub(crate) fn truncate(&mut self) {
if self.buf.is_empty() {
return;
}
let pos = self
.buf
.iter()
.position(|b| *b == 0)
.unwrap_or(self.buf.len() - 1);
self.buf[pos] = 0;
self.buf.truncate(pos + 1);
}
pub(crate) fn as_c_str(&self) -> Option<&CStr> {
if self.buf.is_empty() {
None
} else {
Some(CStr::from_bytes_with_nul(&self.buf).unwrap())
}
}
}
#[cfg(test)]
mod tests {
use super::*;

Loading…
Cancel
Save