Merge pull request #127 from dave-tucker/ext

Support for BPF_PROG_TYPE_EXT
pull/157/head
Alessandro Decina 3 years ago committed by GitHub
commit c5a10f8fbe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -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 } tokio = { version = "1.2.0", features = ["macros", "rt", "rt-multi-thread", "net"], optional = true }
async-std = { version = "1.9.0", optional = true } async-std = { version = "1.9.0", optional = true }
async-io = { version = "1.3", optional = true } async-io = { version = "1.3", optional = true }
log = "0.4"
[dev-dependencies] [dev-dependencies]
matches = "0.1.8" matches = "0.1.8"

@ -1,6 +1,6 @@
use std::{ use std::{
borrow::Cow, borrow::Cow,
collections::HashMap, collections::{HashMap, HashSet},
error::Error, error::Error,
ffi::CString, ffi::CString,
fs, io, fs, io,
@ -8,6 +8,7 @@ use std::{
path::{Path, PathBuf}, path::{Path, PathBuf},
}; };
use log::debug;
use thiserror::Error; use thiserror::Error;
use crate::{ use crate::{
@ -21,12 +22,16 @@ use crate::{
MapKind, Object, ParseError, ProgramSection, MapKind, Object, ParseError, ProgramSection,
}, },
programs::{ programs::{
BtfTracePoint, CgroupSkb, CgroupSkbAttachType, FEntry, FExit, KProbe, LircMode2, Lsm, BtfTracePoint, CgroupSkb, CgroupSkbAttachType, Extension, FEntry, FExit, KProbe, LircMode2,
PerfEvent, ProbeKind, Program, ProgramData, ProgramError, RawTracePoint, SchedClassifier, Lsm, PerfEvent, ProbeKind, Program, ProgramData, ProgramError, RawTracePoint,
SkMsg, SkSkb, SkSkbKind, SockOps, SocketFilter, TracePoint, UProbe, Xdp, SchedClassifier, SkMsg, SkSkb, SkSkbKind, SockOps, SocketFilter, TracePoint, UProbe, Xdp,
}, },
sys::{bpf_map_freeze, bpf_map_update_elem_ptr}, sys::{
util::{bytes_of, possible_cpus, POSSIBLE_CPUS}, 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; 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. /// Builder style API for advanced loading of eBPF programs.
/// ///
/// Loading eBPF code involves a few steps, including loading maps and applying /// 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>>, btf: Option<Cow<'a, Btf>>,
map_pin_path: Option<PathBuf>, map_pin_path: Option<PathBuf>,
globals: HashMap<&'a str, &'a [u8]>, globals: HashMap<&'a str, &'a [u8]>,
features: Features,
extensions: HashSet<&'a str>,
} }
impl<'a> BpfLoader<'a> { impl<'a> BpfLoader<'a> {
/// Creates a new loader instance. /// Creates a new loader instance.
pub fn new() -> BpfLoader<'a> { pub fn new() -> BpfLoader<'a> {
let mut features = Features::default();
features.probe_features();
BpfLoader { BpfLoader {
btf: Btf::from_sys_fs().ok().map(Cow::Owned), btf: Btf::from_sys_fs().ok().map(Cow::Owned),
map_pin_path: None, map_pin_path: None,
globals: HashMap::new(), globals: HashMap::new(),
features,
extensions: HashSet::new(),
} }
} }
@ -187,6 +239,28 @@ impl<'a> BpfLoader<'a> {
self 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. /// Loads eBPF bytecode from a file.
/// ///
/// # Examples /// # Examples
@ -221,6 +295,39 @@ impl<'a> BpfLoader<'a> {
let mut obj = Object::parse(data)?; let mut obj = Object::parse(data)?;
obj.patch_map_data(self.globals.clone())?; 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 { if let Some(btf) = &self.btf {
obj.relocate_btf(btf)?; obj.relocate_btf(btf)?;
} }
@ -288,15 +395,26 @@ impl<'a> BpfLoader<'a> {
.programs .programs
.drain() .drain()
.map(|(name, obj)| { .map(|(name, obj)| {
let prog_name = if self.features.bpf_name {
Some(name.clone())
} else {
None
};
let data = ProgramData { let data = ProgramData {
name: prog_name,
obj, obj,
fd: None, fd: None,
links: Vec::new(), links: Vec::new(),
expected_attach_type: None, expected_attach_type: None,
attach_btf_obj_fd: None, attach_btf_obj_fd: None,
attach_btf_id: None, attach_btf_id: None,
attach_prog_fd: None,
btf_fd,
}; };
let program = match &data.obj.section { let program = if self.extensions.contains(name.as_str()) {
Program::Extension(Extension { data })
} else {
match &data.obj.section {
ProgramSection::KProbe { .. } => Program::KProbe(KProbe { ProgramSection::KProbe { .. } => Program::KProbe(KProbe {
data, data,
kind: ProbeKind::KProbe, kind: ProbeKind::KProbe,
@ -313,7 +431,9 @@ impl<'a> BpfLoader<'a> {
data, data,
kind: ProbeKind::URetProbe, kind: ProbeKind::URetProbe,
}), }),
ProgramSection::TracePoint { .. } => Program::TracePoint(TracePoint { data }), ProgramSection::TracePoint { .. } => {
Program::TracePoint(TracePoint { data })
}
ProgramSection::SocketFilter { .. } => { ProgramSection::SocketFilter { .. } => {
Program::SocketFilter(SocketFilter { data }) Program::SocketFilter(SocketFilter { data })
} }
@ -356,8 +476,9 @@ impl<'a> BpfLoader<'a> {
} }
ProgramSection::FEntry { .. } => Program::FEntry(FEntry { data }), ProgramSection::FEntry { .. } => Program::FEntry(FEntry { data }),
ProgramSection::FExit { .. } => Program::FExit(FExit { data }), ProgramSection::FExit { .. } => Program::FExit(FExit { data }),
ProgramSection::Extension { .. } => Program::Extension(Extension { data }),
}
}; };
(name, program) (name, program)
}) })
.collect(); .collect();

@ -1,18 +1,31 @@
use std::{ use std::{
borrow::Cow, borrow::Cow,
collections::HashMap,
convert::TryInto, convert::TryInto,
ffi::{c_void, CStr}, ffi::{c_void, CStr, CString},
fs, io, mem, fs, io, mem,
path::{Path, PathBuf}, path::{Path, PathBuf},
ptr, ptr,
}; };
use bytes::BufMut;
use log::debug;
use object::Endianness; use object::Endianness;
use thiserror::Error; use thiserror::Error;
use crate::{ 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}, 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; pub(crate) const MAX_RESOLVE_DEPTH: u8 = 32;
@ -71,6 +84,28 @@ pub enum BtfError {
#[error("maximum depth reached resolving BTF type")] #[error("maximum depth reached resolving BTF type")]
MaximumTypeDepthReached { type_id: u32 }, 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. /// Bpf Type Format metadata.
@ -90,6 +125,40 @@ pub struct Btf {
} }
impl 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`. /// Loads BTF metadata from `/sys/kernel/btf/vmlinux`.
pub fn from_sys_fs() -> Result<Btf, BtfError> { pub fn from_sys_fs() -> Result<Btf, BtfError> {
Btf::parse_file("/sys/kernel/btf/vmlinux", Endianness::default()) Btf::parse_file("/sys/kernel/btf/vmlinux", Endianness::default())
@ -291,6 +360,212 @@ impl Btf {
type_id: root_type_id, type_id: root_type_id,
}) })
} }
pub(crate) fn to_bytes(&self) -> Vec<u8> {
// Safety: btf_header is POD
let mut buf = unsafe { bytes_of::<btf_header>(&self.header).to_vec() };
for t in self.types() {
let b = t.to_bytes();
buf.put(b.as_slice())
}
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::Ptr(mut ty) => {
// Rust emits names for pointer types, which the kernel doesn't like
// While I figure out if this needs fixing in the Kernel or LLVM, we'll
// do a fixup here
ty.name_off = 0;
types.push(BtfType::Ptr(ty));
}
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 { unsafe fn read_btf_header(data: &[u8]) -> btf_header {
@ -304,13 +579,19 @@ pub struct BtfExt {
_endianness: Endianness, _endianness: Endianness,
relocations: Vec<(u32, Vec<Relocation>)>, relocations: Vec<(u32, Vec<Relocation>)>,
header: btf_ext_header, header: btf_ext_header,
_func_info_rec_size: usize, func_info_rec_size: usize,
_line_info_rec_size: usize, pub(crate) func_info: FuncInfo,
line_info_rec_size: usize,
pub(crate) line_info: LineInfo,
core_relo_rec_size: usize, core_relo_rec_size: usize,
} }
impl BtfExt { 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 // Safety: btf_ext_header is POD so read_unaligned is safe
let header = unsafe { let header = unsafe {
ptr::read_unaligned::<btf_ext_header>(data.as_ptr() as *const btf_ext_header) ptr::read_unaligned::<btf_ext_header>(data.as_ptr() as *const btf_ext_header)
@ -352,13 +633,57 @@ impl BtfExt {
let mut ext = BtfExt { let mut ext = BtfExt {
header, header,
relocations: Vec::new(), relocations: Vec::new(),
_func_info_rec_size: rec_size(func_info_off, func_info_len)?, func_info: FuncInfo::new(),
_line_info_rec_size: rec_size(line_info_off, line_info_len)?, 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)?, core_relo_rec_size: rec_size(core_relo_off, core_relo_len)?,
data: data.to_vec(), data: data.to_vec(),
_endianness: endianness, _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; let rec_size = ext.core_relo_rec_size;
ext.relocations.extend( ext.relocations.extend(
SecInfoIter::new(ext.core_relo_data(), ext.core_relo_rec_size, endianness) SecInfoIter::new(ext.core_relo_data(), ext.core_relo_rec_size, endianness)
@ -392,9 +717,25 @@ impl BtfExt {
self.info_data(self.header.core_relo_off, self.header.core_relo_len) 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>)> { pub(crate) fn relocations(&self) -> impl Iterator<Item = &(u32, Vec<Relocation>)> {
self.relocations.iter() 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> { pub(crate) struct SecInfoIter<'a> {
@ -439,7 +780,7 @@ impl<'a> Iterator for SecInfoIter<'a> {
Some(SecInfo { Some(SecInfo {
sec_name_off, sec_name_off,
_num_info: num_info, num_info,
data, data,
}) })
} }
@ -448,12 +789,14 @@ impl<'a> Iterator for SecInfoIter<'a> {
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct SecInfo<'a> { pub(crate) struct SecInfo<'a> {
sec_name_off: u32, sec_name_off: u32,
_num_info: u32, num_info: u32,
data: &'a [u8], data: &'a [u8],
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::generated::{btf_param, BTF_INT_SIGNED, BTF_VAR_STATIC};
use super::*; use super::*;
#[test] #[test]
@ -472,4 +815,239 @@ mod tests {
assert_eq!(header.str_off, 0x2a5464); assert_eq!(header.str_off, 0x2a5464);
assert_eq!(header.str_len, 0x1c6410); assert_eq!(header.str_len, 0x1c6410);
} }
#[test]
fn test_parse_btf() {
// this generated BTF data is from an XDP program that simply returns XDP_PASS
// compiled using clang
let data: &[u8] = &[
0x9f, 0xeb, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x01,
0x00, 0x00, 0x0c, 0x01, 0x00, 0x00, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00,
0x00, 0x04, 0x18, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00,
0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
0x20, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x30, 0x00,
0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x08, 0x04, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x0d, 0x06, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00,
0x00, 0x01, 0x69, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0c, 0x05, 0x00, 0x00, 0x00,
0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00,
0x08, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xbc, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
0xd0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x09, 0x00, 0x00, 0x00, 0x01, 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, 0x00, 0x78,
0x64, 0x70, 0x5f, 0x6d, 0x64, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x64, 0x61, 0x74,
0x61, 0x5f, 0x65, 0x6e, 0x64, 0x00, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x6d, 0x65, 0x74,
0x61, 0x00, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x69, 0x66, 0x69, 0x6e,
0x64, 0x65, 0x78, 0x00, 0x72, 0x78, 0x5f, 0x71, 0x75, 0x65, 0x75, 0x65, 0x5f, 0x69,
0x6e, 0x64, 0x65, 0x78, 0x00, 0x65, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x69, 0x66,
0x69, 0x6e, 0x64, 0x65, 0x78, 0x00, 0x5f, 0x5f, 0x75, 0x33, 0x32, 0x00, 0x75, 0x6e,
0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x74, 0x00, 0x63, 0x74, 0x78,
0x00, 0x69, 0x6e, 0x74, 0x00, 0x78, 0x64, 0x70, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x00,
0x78, 0x64, 0x70, 0x2f, 0x70, 0x61, 0x73, 0x73, 0x00, 0x2f, 0x68, 0x6f, 0x6d, 0x65,
0x2f, 0x64, 0x61, 0x76, 0x65, 0x2f, 0x64, 0x65, 0x76, 0x2f, 0x62, 0x70, 0x66, 0x64,
0x2f, 0x62, 0x70, 0x66, 0x2f, 0x78, 0x64, 0x70, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x2e,
0x62, 0x70, 0x66, 0x2e, 0x63, 0x00, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75,
0x72, 0x6e, 0x20, 0x58, 0x44, 0x50, 0x5f, 0x50, 0x41, 0x53, 0x53, 0x3b, 0x00, 0x63,
0x68, 0x61, 0x72, 0x00, 0x5f, 0x5f, 0x41, 0x52, 0x52, 0x41, 0x59, 0x5f, 0x53, 0x49,
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 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)] #[allow(clippy::module_inception)]
mod btf; mod btf;
mod info;
mod relocation; mod relocation;
mod types; mod types;
pub use btf::*; pub use btf::*;
pub(crate) use info::*;
pub use relocation::RelocationError; pub use relocation::RelocationError;
pub(crate) use types::*; pub(crate) use types::*;

@ -1,5 +1,6 @@
use std::{ use std::{
convert::{TryFrom, TryInto}, convert::{TryFrom, TryInto},
fmt::Display,
mem, ptr, mem, ptr,
}; };
@ -7,11 +8,11 @@ use object::Endianness;
use crate::{ use crate::{
generated::{ generated::{
btf_array, btf_enum, btf_member, btf_param, btf_type, btf_type__bindgen_ty_1, btf_var, btf_array, btf_enum, btf_func_linkage, btf_member, btf_param, btf_type,
btf_var_secinfo, BTF_KIND_ARRAY, BTF_KIND_CONST, BTF_KIND_DATASEC, BTF_KIND_ENUM, btf_type__bindgen_ty_1, btf_var, btf_var_secinfo, BTF_KIND_ARRAY, BTF_KIND_CONST,
BTF_KIND_FLOAT, BTF_KIND_FUNC, BTF_KIND_FUNC_PROTO, BTF_KIND_FWD, BTF_KIND_INT, BTF_KIND_DATASEC, BTF_KIND_ENUM, BTF_KIND_FLOAT, BTF_KIND_FUNC, BTF_KIND_FUNC_PROTO,
BTF_KIND_PTR, BTF_KIND_RESTRICT, BTF_KIND_STRUCT, BTF_KIND_TYPEDEF, BTF_KIND_UNION, BTF_KIND_FWD, BTF_KIND_INT, BTF_KIND_PTR, BTF_KIND_RESTRICT, BTF_KIND_STRUCT,
BTF_KIND_UNKN, BTF_KIND_VAR, BTF_KIND_VOLATILE, BTF_KIND_TYPEDEF, BTF_KIND_UNION, BTF_KIND_UNKN, BTF_KIND_VAR, BTF_KIND_VOLATILE,
}, },
obj::btf::{Btf, BtfError, MAX_RESOLVE_DEPTH}, 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> { unsafe fn read<T>(data: &[u8]) -> Result<T, BtfError> {
if mem::size_of::<T>() > data.len() { if mem::size_of::<T>() > data.len() {
return Err(BtfError::InvalidTypeInfo); return Err(BtfError::InvalidTypeInfo);
@ -149,6 +180,67 @@ impl BtfType {
}) })
} }
pub(crate) fn to_bytes(&self) -> Vec<u8> {
fn bytes_of<T>(val: &T) -> &[u8] {
// Safety: all btf types are POD
unsafe { crate::util::bytes_of(val) }
}
match self {
BtfType::Fwd(btf_type)
| BtfType::Const(btf_type)
| BtfType::Volatile(btf_type)
| BtfType::Restrict(btf_type)
| BtfType::Ptr(btf_type)
| BtfType::Typedef(btf_type)
| BtfType::Func(btf_type)
| BtfType::Float(btf_type) => bytes_of::<btf_type>(btf_type).to_vec(),
BtfType::Int(btf_type, len) => {
let mut buf = bytes_of::<btf_type>(btf_type).to_vec();
buf.append(&mut len.to_ne_bytes().to_vec());
buf
}
BtfType::Enum(btf_type, enums) => {
let mut buf = bytes_of::<btf_type>(btf_type).to_vec();
for en in enums {
buf.append(&mut bytes_of::<btf_enum>(en).to_vec());
}
buf
}
BtfType::Array(btf_type, btf_array) => {
let mut buf = bytes_of::<btf_type>(btf_type).to_vec();
buf.append(&mut bytes_of::<btf_array>(btf_array).to_vec());
buf
}
BtfType::Struct(btf_type, btf_members) | BtfType::Union(btf_type, btf_members) => {
let mut buf = bytes_of::<btf_type>(btf_type).to_vec();
for m in btf_members {
buf.append(&mut bytes_of::<btf_member>(m).to_vec());
}
buf
}
BtfType::FuncProto(btf_type, btf_params) => {
let mut buf = bytes_of::<btf_type>(btf_type).to_vec();
for p in btf_params {
buf.append(&mut bytes_of::<btf_param>(p).to_vec());
}
buf
}
BtfType::Var(btf_type, btf_var) => {
let mut buf = bytes_of::<btf_type>(btf_type).to_vec();
buf.append(&mut bytes_of::<btf_var>(btf_var).to_vec());
buf
}
BtfType::DataSec(btf_type, btf_var_secinfo) => {
let mut buf = bytes_of::<btf_type>(btf_type).to_vec();
for s in btf_var_secinfo {
buf.append(&mut bytes_of::<btf_var_secinfo>(s).to_vec());
}
buf
}
BtfType::Unknown => vec![],
}
}
pub(crate) fn type_info_size(&self) -> usize { pub(crate) fn type_info_size(&self) -> usize {
let ty_size = mem::size_of::<btf_type>(); let ty_size = mem::size_of::<btf_type>();
@ -206,13 +298,109 @@ impl BtfType {
pub(crate) fn is_composite(&self) -> bool { pub(crate) fn is_composite(&self) -> bool {
matches!(self, BtfType::Struct(_, _) | BtfType::Union(_, _)) 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> { fn type_kind(ty: &btf_type) -> Result<BtfKind, BtfError> {
((ty.info >> 24) & 0x1F).try_into() ((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 (ty.info & 0xFFFF) as usize
} }
@ -397,6 +585,8 @@ impl std::fmt::Debug for btf_type__bindgen_ty_1 {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::generated::BTF_INT_SIGNED;
use super::*; use super::*;
#[test] #[test]
@ -406,14 +596,48 @@ mod tests {
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x00, 0x40, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x00, 0x40, 0x00,
0x00, 0x00, 0x00, 0x00,
]; ];
match unsafe { BtfType::read(data, endianness) } { let got = unsafe { BtfType::read(data, endianness) };
match got {
Ok(BtfType::Int(ty, nr_bits)) => { Ok(BtfType::Int(ty, nr_bits)) => {
assert_eq!(ty.name_off, 1); assert_eq!(ty.name_off, 1);
assert_eq!(unsafe { ty.__bindgen_anon_1.size }, 8);
assert_eq!(nr_bits, 64); assert_eq!(nr_bits, 64);
} }
Ok(t) => panic!("expected int type, got {:#?}", t), Ok(t) => panic!("expected int type, got {:#?}", t),
Err(_) => panic!("unexpected error"), Err(_) => panic!("unexpected error"),
} }
let data2 = got.unwrap().to_bytes();
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] #[test]
@ -422,11 +646,14 @@ mod tests {
let data: &[u8] = &[ let data: &[u8] = &[
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x00, 0x00, 0x00,
]; ];
match unsafe { BtfType::read(data, endianness) } { let got = unsafe { BtfType::read(data, endianness) };
match got {
Ok(BtfType::Ptr(_)) => {} Ok(BtfType::Ptr(_)) => {}
Ok(t) => panic!("expected ptr type, got {:#?}", t), Ok(t) => panic!("expected ptr type, got {:#?}", t),
Err(_) => panic!("unexpected error"), Err(_) => panic!("unexpected error"),
} }
let data2 = got.unwrap().to_bytes();
assert_eq!(data, data2.as_slice())
} }
#[test] #[test]
@ -436,11 +663,14 @@ mod tests {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
]; ];
match unsafe { BtfType::read(data, endianness) } { let got = unsafe { BtfType::read(data, endianness) };
match got {
Ok(BtfType::Array(_, _)) => {} Ok(BtfType::Array(_, _)) => {}
Ok(t) => panic!("expected array type, got {:#?}", t), Ok(t) => panic!("expected array type, got {:#?}", t),
Err(_) => panic!("unexpected error"), Err(_) => panic!("unexpected error"),
} }
let data2 = got.unwrap().to_bytes();
assert_eq!(data, data2.as_slice())
} }
#[test] #[test]
@ -450,11 +680,14 @@ mod tests {
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x00, 0x47, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x00, 0x47, 0x02,
0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]; ];
match unsafe { BtfType::read(data, endianness) } { let got = unsafe { BtfType::read(data, endianness) };
match got {
Ok(BtfType::Struct(_, _)) => {} Ok(BtfType::Struct(_, _)) => {}
Ok(t) => panic!("expected struct type, got {:#?}", t), Ok(t) => panic!("expected struct type, got {:#?}", t),
Err(_) => panic!("unexpected error"), Err(_) => panic!("unexpected error"),
} }
let data2 = got.unwrap().to_bytes();
assert_eq!(data, data2.as_slice())
} }
#[test] #[test]
@ -464,11 +697,14 @@ mod tests {
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x05, 0x04, 0x00, 0x00, 0x00, 0x0d, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x05, 0x04, 0x00, 0x00, 0x00, 0x0d, 0x04,
0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]; ];
match unsafe { BtfType::read(data, endianness) } { let got = unsafe { BtfType::read(data, endianness) };
match got {
Ok(BtfType::Union(_, _)) => {} Ok(BtfType::Union(_, _)) => {}
Ok(t) => panic!("expected union type, got {:#?}", t), Ok(t) => panic!("expected union type, got {:#?}", t),
Err(_) => panic!("unexpected error"), Err(_) => panic!("unexpected error"),
} }
let data2 = got.unwrap().to_bytes();
assert_eq!(data, data2.as_slice())
} }
#[test] #[test]
@ -478,11 +714,14 @@ mod tests {
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x06, 0x04, 0x00, 0x00, 0x00, 0xc9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x06, 0x04, 0x00, 0x00, 0x00, 0xc9, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcf, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcf, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
]; ];
match unsafe { BtfType::read(data, endianness) } { let got = unsafe { BtfType::read(data, endianness) };
match got {
Ok(BtfType::Enum(_, _)) => {} Ok(BtfType::Enum(_, _)) => {}
Ok(t) => panic!("expected enum type, got {:#?}", t), Ok(t) => panic!("expected enum type, got {:#?}", t),
Err(_) => panic!("unexpected error"), Err(_) => panic!("unexpected error"),
} }
let data2 = got.unwrap().to_bytes();
assert_eq!(data, data2.as_slice())
} }
#[test] #[test]
@ -491,11 +730,14 @@ mod tests {
let data: &[u8] = &[ let data: &[u8] = &[
0x0b, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00,
]; ];
match unsafe { BtfType::read(data, endianness) } { let got = unsafe { BtfType::read(data, endianness) };
match got {
Ok(BtfType::Fwd(_)) => {} Ok(BtfType::Fwd(_)) => {}
Ok(t) => panic!("expected fwd type, got {:#?}", t), Ok(t) => panic!("expected fwd type, got {:#?}", t),
Err(_) => panic!("unexpected error"), Err(_) => panic!("unexpected error"),
} }
let data2 = got.unwrap().to_bytes();
assert_eq!(data, data2.as_slice())
} }
#[test] #[test]
@ -504,11 +746,14 @@ mod tests {
let data: &[u8] = &[ let data: &[u8] = &[
0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x0b, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x0b, 0x00, 0x00, 0x00,
]; ];
match unsafe { BtfType::read(data, endianness) } { let got = unsafe { BtfType::read(data, endianness) };
match got {
Ok(BtfType::Typedef(_)) => {} Ok(BtfType::Typedef(_)) => {}
Ok(t) => panic!("expected typedef type, got {:#?}", t), Ok(t) => panic!("expected typedef type, got {:#?}", t),
Err(_) => panic!("unexpected error"), Err(_) => panic!("unexpected error"),
} }
let data2 = got.unwrap().to_bytes();
assert_eq!(data, data2.as_slice())
} }
#[test] #[test]
@ -517,11 +762,14 @@ mod tests {
let data: &[u8] = &[ let data: &[u8] = &[
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x24, 0x00, 0x00, 0x00,
]; ];
match unsafe { BtfType::read(data, endianness) } { let got = unsafe { BtfType::read(data, endianness) };
match got {
Ok(BtfType::Volatile(_)) => {} Ok(BtfType::Volatile(_)) => {}
Ok(t) => panic!("expected volatile type, got {:#?}", t), Ok(t) => panic!("expected volatile type, got {:#?}", t),
Err(_) => panic!("unexpected error"), Err(_) => panic!("unexpected error"),
} }
let data2 = got.unwrap().to_bytes();
assert_eq!(data, data2.as_slice())
} }
#[test] #[test]
@ -530,11 +778,14 @@ mod tests {
let data: &[u8] = &[ let data: &[u8] = &[
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x01, 0x00, 0x00, 0x00,
]; ];
match unsafe { BtfType::read(data, endianness) } { let got = unsafe { BtfType::read(data, endianness) };
match got {
Ok(BtfType::Const(_)) => {} Ok(BtfType::Const(_)) => {}
Ok(t) => panic!("expected const type, got {:#?}", t), Ok(t) => panic!("expected const type, got {:#?}", t),
Err(_) => panic!("unexpected error"), Err(_) => panic!("unexpected error"),
} }
let data2 = got.unwrap().to_bytes();
assert_eq!(data, data2.as_slice())
} }
#[test] #[test]
@ -543,11 +794,14 @@ mod tests {
let data: &[u8] = &[ let data: &[u8] = &[
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x04, 0x00, 0x00, 0x00,
]; ];
match unsafe { BtfType::read(data, endianness) } { let got = unsafe { BtfType::read(data, endianness) };
match got {
Ok(BtfType::Restrict(_)) => {} Ok(BtfType::Restrict(_)) => {}
Ok(t) => panic!("expected restrict type gpt {:#?}", t), Ok(t) => panic!("expected restrict type gpt {:#?}", t),
Err(_) => panic!("unexpected error"), Err(_) => panic!("unexpected error"),
} }
let data2 = got.unwrap().to_bytes();
assert_eq!(data, data2.as_slice())
} }
#[test] #[test]
@ -556,11 +810,14 @@ mod tests {
let data: &[u8] = &[ let data: &[u8] = &[
0x17, 0x8b, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x0c, 0xf0, 0xe4, 0x00, 0x00, 0x17, 0x8b, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x0c, 0xf0, 0xe4, 0x00, 0x00,
]; ];
match unsafe { BtfType::read(data, endianness) } { let got = unsafe { BtfType::read(data, endianness) };
match got {
Ok(BtfType::Func(_)) => {} Ok(BtfType::Func(_)) => {}
Ok(t) => panic!("expected func type gpt {:#?}", t), Ok(t) => panic!("expected func type gpt {:#?}", t),
Err(_) => panic!("unexpected error"), Err(_) => panic!("unexpected error"),
} }
let data2 = got.unwrap().to_bytes();
assert_eq!(data, data2.as_slice())
} }
#[test] #[test]
@ -570,11 +827,14 @@ mod tests {
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00,
]; ];
match unsafe { BtfType::read(data, endianness) } { let got = unsafe { BtfType::read(data, endianness) };
match got {
Ok(BtfType::FuncProto(_, _)) => {} Ok(BtfType::FuncProto(_, _)) => {}
Ok(t) => panic!("expected func_proto type, got {:#?}", t), Ok(t) => panic!("expected func_proto type, got {:#?}", t),
Err(_) => panic!("unexpected error"), Err(_) => panic!("unexpected error"),
} }
let data2 = got.unwrap().to_bytes();
assert_eq!(data, data2.as_slice())
} }
#[test] #[test]
@ -585,26 +845,38 @@ mod tests {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
]; ];
match unsafe { BtfType::read(data, endianness) } { let got = unsafe { BtfType::read(data, endianness) };
match got {
Ok(BtfType::Var(_, _)) => {} Ok(BtfType::Var(_, _)) => {}
Ok(t) => panic!("expected var type, got {:#?}", t), Ok(t) => panic!("expected var type, got {:#?}", t),
Err(_) => panic!("unexpected error"), Err(_) => panic!("unexpected error"),
} };
let data2 = got.unwrap().to_bytes();
assert_eq!(data, data2.as_slice())
} }
#[test] #[test]
fn test_read_btf_type_func_datasec() { fn test_read_btf_type_func_datasec() {
let endianness = Endianness::default(); let endianness = Endianness::default();
// NOTE: There was no data in /sys/kernell/btf/vmlinux for this type
let data: &[u8] = &[ let data: &[u8] = &[
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0f, 0xf0, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
]; ];
match unsafe { BtfType::read(data, endianness) } { let got = unsafe { BtfType::read(data, endianness) };
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), Ok(t) => panic!("expected datasec type, got {:#?}", t),
Err(_) => panic!("unexpected error"), Err(_) => panic!("unexpected error"),
} }
let data2 = got.unwrap().to_bytes();
assert_eq!(data, data2.as_slice())
} }
#[test] #[test]
@ -613,10 +885,37 @@ mod tests {
let data: &[u8] = &[ let data: &[u8] = &[
0x78, 0xfd, 0x02, 0x00, 0x00, 0x00, 0x00, 0x10, 0x08, 0x00, 0x00, 0x00, 0x78, 0xfd, 0x02, 0x00, 0x00, 0x00, 0x00, 0x10, 0x08, 0x00, 0x00, 0x00,
]; ];
match unsafe { BtfType::read(data, endianness) } { let got = unsafe { BtfType::read(data, endianness) };
match got {
Ok(BtfType::Float(_)) => {} Ok(BtfType::Float(_)) => {}
Ok(t) => panic!("expected float type, got {:#?}", t), Ok(t) => panic!("expected float type, got {:#?}", t),
Err(_) => panic!("unexpected error"), Err(_) => panic!("unexpected error"),
} }
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 std::slice::from_raw_parts_mut;
use self::btf::{FuncSecInfo, LineSecInfo};
const KERNEL_VERSION_ANY: u32 = 0xFFFF_FFFE; const KERNEL_VERSION_ANY: u32 = 0xFFFF_FFFE;
/// The first five __u32 of `bpf_map_def` must be defined. /// The first five __u32 of `bpf_map_def` must be defined.
const MINIMUM_MAP_SIZE: usize = mem::size_of::<u32>() * 5; 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) functions: HashMap<u64, Function>,
pub(crate) relocations: HashMap<SectionIndex, HashMap<u64, Relocation>>, pub(crate) relocations: HashMap<SectionIndex, HashMap<u64, Relocation>>,
pub(crate) symbols_by_index: HashMap<usize, Symbol>, 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)] #[derive(Debug, Clone, PartialEq)]
@ -88,6 +94,10 @@ pub(crate) struct Function {
pub(crate) section_index: SectionIndex, pub(crate) section_index: SectionIndex,
pub(crate) section_offset: usize, pub(crate) section_offset: usize,
pub(crate) instructions: Vec<bpf_insn>, 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)] #[derive(Debug, Clone)]
@ -113,6 +123,7 @@ pub enum ProgramSection {
BtfTracePoint { name: String }, BtfTracePoint { name: String },
FEntry { name: String }, FEntry { name: String },
FExit { name: String }, FExit { name: String },
Extension { name: String },
} }
impl ProgramSection { impl ProgramSection {
@ -139,6 +150,7 @@ impl ProgramSection {
ProgramSection::BtfTracePoint { name } => name, ProgramSection::BtfTracePoint { name } => name,
ProgramSection::FEntry { name } => name, ProgramSection::FEntry { name } => name,
ProgramSection::FExit { name } => name, ProgramSection::FExit { name } => name,
ProgramSection::Extension { name } => name,
} }
} }
} }
@ -194,6 +206,7 @@ impl FromStr for ProgramSection {
"lsm" => Lsm { name }, "lsm" => Lsm { name },
"fentry" => FEntry { name }, "fentry" => FEntry { name },
"fexit" => FExit { name }, "fexit" => FExit { name },
"freplace" => Extension { name },
_ => { _ => {
return Err(ParseError::InvalidProgramSection { return Err(ParseError::InvalidProgramSection {
section: section.to_owned(), section: section.to_owned(),
@ -224,9 +237,14 @@ impl Object {
if let Some(symbol_table) = obj.symbol_table() { if let Some(symbol_table) = obj.symbol_table() {
for symbol in symbol_table.symbols() { for symbol in symbol_table.symbols() {
let name = symbol
.name()
.ok()
.map(String::from)
.ok_or(BtfError::InvalidSymbolName)?;
let sym = Symbol { let sym = Symbol {
index: symbol.index().0, index: symbol.index().0,
name: symbol.name().ok().map(String::from), name: Some(name.clone()),
section_index: symbol.section().index(), section_index: symbol.section().index(),
address: symbol.address(), address: symbol.address(),
size: symbol.size(), size: symbol.size(),
@ -236,10 +254,30 @@ impl Object {
bpf_obj bpf_obj
.symbols_by_index .symbols_by_index
.insert(symbol.index().0, sym.clone()); .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() { 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)?)?; bpf_obj.parse_section(Section::try_from(&s)?)?;
} }
@ -258,6 +296,8 @@ impl Object {
functions: HashMap::new(), functions: HashMap::new(),
relocations: HashMap::new(), relocations: HashMap::new(),
symbols_by_index: 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> { 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(()) Ok(())
} }
fn parse_program(&self, section: &Section) -> Result<Program, ParseError> { fn parse_program(&self, section: &Section) -> Result<Program, ParseError> {
let prog_sec = ProgramSection::from_str(section.name)?; let prog_sec = ProgramSection::from_str(section.name)?;
let name = prog_sec.name().to_owned(); 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 { Ok(Program {
license: self.license.clone(), license: self.license.clone(),
kernel_version: self.kernel_version, kernel_version: self.kernel_version,
@ -330,6 +389,10 @@ impl Object {
section_index: section.index, section_index: section.index,
section_offset: 0, section_offset: 0,
instructions: copy_instructions(section.data)?, 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( self.functions.insert(
sym.address, sym.address,
Function { Function {
@ -375,6 +470,10 @@ impl Object {
instructions: copy_instructions( instructions: copy_instructions(
&section.data[offset..offset + sym.size as usize], &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]); parts.push(parts[0]);
} }
self.section_sizes
.insert(section.name.to_owned(), section.size);
match section.kind { match section.kind {
BpfSectionKind::Data => { BpfSectionKind::Data => {
self.maps self.maps
@ -436,8 +536,10 @@ impl Object {
); );
} }
} }
BpfSectionKind::Undefined
_ => {} | BpfSectionKind::BtfMaps
| BpfSectionKind::License
| BpfSectionKind::Version => {}
} }
Ok(()) 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 { if data.len() % mem::size_of::<bpf_insn>() > 0 {
return Err(ParseError::InvalidProgramCode); return Err(ParseError::InvalidProgramCode);
} }
@ -978,9 +1080,8 @@ mod tests {
address: 0, address: 0,
section_index: SectionIndex(0), section_index: SectionIndex(0),
section_offset: 0, section_offset: 0,
instructions instructions,
} ..} }) if license.to_string_lossy() == "GPL" && name == "foo" && instructions.len() == 1
}) if license.to_string_lossy() == "GPL" && name == "foo" && instructions.len() == 1
); );
} }

@ -13,7 +13,7 @@ use crate::{
BpfError, BpfError,
}; };
const INS_SIZE: usize = mem::size_of::<bpf_insn>(); pub(crate) const INS_SIZE: usize = mem::size_of::<bpf_insn>();
#[derive(Debug, Error)] #[derive(Debug, Error)]
enum RelocationError { enum RelocationError {
@ -227,6 +227,11 @@ impl<'a> FunctionLinker<'a> {
// at `start_ins`. We'll use `start_ins` to do pc-relative calls. // at `start_ins`. We'll use `start_ins` to do pc-relative calls.
let start_ins = program.instructions.len(); let start_ins = program.instructions.len();
program.instructions.extend(&fun.instructions); 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); self.linked_functions.insert(fun.address, start_ins);
// relocate `fun`, recursively linking in all the callees // relocate `fun`, recursively linking in all the callees
@ -296,6 +301,33 @@ impl<'a> FunctionLinker<'a> {
Ok(()) 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 { fn is_call(ins: &bpf_insn) -> bool {

@ -88,13 +88,12 @@ impl CgroupSkb {
}; };
let k_ver = kernel_version().unwrap(); let k_ver = kernel_version().unwrap();
if k_ver >= (5, 7, 0) { if k_ver >= (5, 7, 0) {
let link_fd = let link_fd = bpf_link_create(prog_fd, cgroup_fd, attach_type, None, 0).map_err(
bpf_link_create(prog_fd, cgroup_fd, attach_type, 0).map_err(|(_, io_error)| { |(_, io_error)| ProgramError::SyscallError {
ProgramError::SyscallError {
call: "bpf_link_create".to_owned(), call: "bpf_link_create".to_owned(),
io_error, io_error,
} },
})? as RawFd; )? as RawFd;
Ok(self.data.link(FdLink { fd: Some(link_fd) })) Ok(self.data.link(FdLink { fd: Some(link_fd) }))
} else { } else {
bpf_prog_attach(prog_fd, cgroup_fd, attach_type).map_err(|(_, io_error)| { 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 //! [`Bpf::program_mut`]: crate::Bpf::program_mut
//! [`maps`]: crate::maps //! [`maps`]: crate::maps
mod cgroup_skb; mod cgroup_skb;
mod extension;
mod fentry; mod fentry;
mod fexit; mod fexit;
mod kprobe; mod kprobe;
@ -60,9 +61,8 @@ mod xdp;
use libc::{close, dup, ENOSPC}; use libc::{close, dup, ENOSPC};
use std::{ use std::{
cell::RefCell, cell::RefCell,
cmp,
convert::TryFrom, convert::TryFrom,
ffi::{CStr, CString}, ffi::CString,
io, io,
os::unix::io::{AsRawFd, RawFd}, os::unix::io::{AsRawFd, RawFd},
path::Path, path::Path,
@ -71,6 +71,7 @@ use std::{
use thiserror::Error; use thiserror::Error;
pub use cgroup_skb::{CgroupSkb, CgroupSkbAttachType}; pub use cgroup_skb::{CgroupSkb, CgroupSkbAttachType};
pub use extension::{Extension, ExtensionError};
pub use fentry::FEntry; pub use fentry::FEntry;
pub use fexit::FExit; pub use fexit::FExit;
pub use kprobe::{KProbe, KProbeError}; pub use kprobe::{KProbe, KProbeError};
@ -95,6 +96,7 @@ use crate::{
maps::MapError, maps::MapError,
obj::{self, btf::BtfError, Function, KernelVersion}, obj::{self, btf::BtfError, Function, KernelVersion},
sys::{bpf_load_program, bpf_pin_object, bpf_prog_detach, bpf_prog_query, BpfLoadProgramAttrs}, sys::{bpf_load_program, bpf_pin_object, bpf_prog_detach, bpf_prog_query, BpfLoadProgramAttrs},
util::VerifierLog,
}; };
/// Error type returned when working with programs. /// Error type returned when working with programs.
@ -175,9 +177,21 @@ pub enum ProgramError {
#[error(transparent)] #[error(transparent)]
TcError(#[from] TcError), TcError(#[from] TcError),
/// An error occurred while working with an [`Extension`] program.
#[error(transparent)]
ExtensionError(#[from] ExtensionError),
/// An error occurred while working with BTF. /// An error occurred while working with BTF.
#[error(transparent)] #[error(transparent)]
Btf(#[from] BtfError), 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 { pub trait ProgramFd {
@ -204,6 +218,7 @@ pub enum Program {
BtfTracePoint(BtfTracePoint), BtfTracePoint(BtfTracePoint),
FEntry(FEntry), FEntry(FEntry),
FExit(FExit), FExit(FExit),
Extension(Extension),
} }
impl Program { impl Program {
@ -242,6 +257,7 @@ impl Program {
Program::BtfTracePoint(_) => BPF_PROG_TYPE_TRACING, Program::BtfTracePoint(_) => BPF_PROG_TYPE_TRACING,
Program::FEntry(_) => BPF_PROG_TYPE_TRACING, Program::FEntry(_) => BPF_PROG_TYPE_TRACING,
Program::FExit(_) => 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::BtfTracePoint(p) => &p.data,
Program::FEntry(p) => &p.data, Program::FEntry(p) => &p.data,
Program::FExit(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::BtfTracePoint(p) => &mut p.data,
Program::FEntry(p) => &mut p.data, Program::FEntry(p) => &mut p.data,
Program::FExit(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) struct ProgramData {
pub(crate) name: Option<String>,
pub(crate) obj: obj::Program, pub(crate) obj: obj::Program,
pub(crate) fd: Option<RawFd>, pub(crate) fd: Option<RawFd>,
pub(crate) links: Vec<Rc<RefCell<dyn Link>>>, pub(crate) links: Vec<Rc<RefCell<dyn Link>>>,
pub(crate) expected_attach_type: Option<bpf_attach_type>, pub(crate) expected_attach_type: Option<bpf_attach_type>,
pub(crate) attach_btf_obj_fd: Option<u32>, pub(crate) attach_btf_obj_fd: Option<u32>,
pub(crate) attach_btf_id: Option<u32>, pub(crate) attach_btf_id: Option<u32>,
pub(crate) attach_prog_fd: Option<RawFd>,
pub(crate) btf_fd: Option<RawFd>,
} }
impl ProgramData { 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> { fn load_program(prog_type: bpf_prog_type, data: &mut ProgramData) -> Result<(), ProgramError> {
let ProgramData { obj, fd, .. } = data; let ProgramData { obj, fd, .. } = data;
if fd.is_some() { if fd.is_some() {
return Err(ProgramError::AlreadyLoaded); return Err(ProgramError::AlreadyLoaded);
} }
let crate::obj::Program { let crate::obj::Program {
function: Function { instructions, .. }, function:
Function {
instructions,
func_info,
line_info,
func_info_rec_size,
line_info_rec_size,
..
},
license, license,
kernel_version, 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 log_buf = VerifierLog::new();
let mut retries = 0; let mut retries = 0;
let mut ret; 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 { loop {
let attr = BpfLoadProgramAttrs { let attr = BpfLoadProgramAttrs {
name: prog_name.clone(),
ty: prog_type, ty: prog_type,
insns: instructions, insns: instructions,
license, license,
kernel_version: target_kernel_version, kernel_version: target_kernel_version,
expected_attach_type: data.expected_attach_type, expected_attach_type: data.expected_attach_type,
prog_btf_fd: data.btf_fd,
attach_btf_obj_fd: data.attach_btf_obj_fd, attach_btf_obj_fd: data.attach_btf_obj_fd,
attach_btf_id: data.attach_btf_id, attach_btf_id: data.attach_btf_id,
attach_prog_fd: data.attach_prog_fd,
log: &mut log_buf, 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); ret = bpf_load_program(attr);
match &ret { 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 { pub trait Link: std::fmt::Debug {
fn detach(&mut self) -> Result<(), ProgramError>; fn detach(&mut self) -> Result<(), ProgramError>;
} }
@ -599,6 +595,12 @@ macro_rules! impl_program_fd {
self.data.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, BtfTracePoint,
FEntry, FEntry,
FExit, FExit,
Extension,
); );
macro_rules! impl_try_from_program { macro_rules! impl_try_from_program {
@ -668,6 +671,7 @@ impl_try_from_program!(
BtfTracePoint, BtfTracePoint,
FEntry, FEntry,
FExit, FExit,
Extension,
); );
/// Provides information about a loaded program, like name, id and statistics /// Provides information about a loaded program, like name, id and statistics

@ -5,8 +5,10 @@ use thiserror::Error;
use crate::{ use crate::{
generated::{ generated::{
bpf_attach_type::BPF_XDP, bpf_prog_type::BPF_PROG_TYPE_XDP, XDP_FLAGS_DRV_MODE, bpf_attach_type::{self, BPF_XDP},
XDP_FLAGS_HW_MODE, XDP_FLAGS_REPLACE, XDP_FLAGS_SKB_MODE, XDP_FLAGS_UPDATE_IF_NOEXIST, 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}, programs::{load_program, FdLink, Link, LinkRef, ProgramData, ProgramError},
sys::{bpf_link_create, kernel_version, netlink_set_xdp_fd}, sys::{bpf_link_create, kernel_version, netlink_set_xdp_fd},
@ -72,6 +74,7 @@ impl Xdp {
/// ///
/// See also [`Program::load`](crate::programs::Program::load). /// See also [`Program::load`](crate::programs::Program::load).
pub fn load(&mut self) -> Result<(), ProgramError> { 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) load_program(BPF_PROG_TYPE_XDP, &mut self.data)
} }
@ -88,7 +91,6 @@ impl Xdp {
/// kernels. /// kernels.
pub fn attach(&mut self, interface: &str, flags: XdpFlags) -> Result<LinkRef, ProgramError> { pub fn attach(&mut self, interface: &str, flags: XdpFlags) -> Result<LinkRef, ProgramError> {
let prog_fd = self.data.fd_or_err()?; let prog_fd = self.data.fd_or_err()?;
let c_interface = CString::new(interface).unwrap(); let c_interface = CString::new(interface).unwrap();
let if_index = unsafe { if_nametoindex(c_interface.as_ptr()) } as RawFd; let if_index = unsafe { if_nametoindex(c_interface.as_ptr()) } as RawFd;
if if_index == 0 { if if_index == 0 {
@ -99,7 +101,7 @@ impl Xdp {
let k_ver = kernel_version().unwrap(); let k_ver = kernel_version().unwrap();
if k_ver >= (5, 9, 0) { 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 { |(_, io_error)| ProgramError::SyscallError {
call: "bpf_link_create".to_owned(), call: "bpf_link_create".to_owned(),
io_error, 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::{ use std::{
cmp, cmp::{self, min},
ffi::CStr, ffi::{CStr, CString},
io, io,
mem::{self, MaybeUninit}, mem::{self, MaybeUninit},
os::unix::io::RawFd, os::unix::io::RawFd,
slice, slice,
}; };
use libc::{c_long, ENOENT};
use crate::{ use crate::{
bpf_map_def, 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, maps::PerCpuValues,
programs::VerifierLog, obj::btf::{FuncSecInfo, LineSecInfo},
sys::{kernel_version, SysResult}, sys::{kernel_version, SysResult},
util::VerifierLog,
Pod, BPF_OBJ_NAME_LEN, 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) struct BpfLoadProgramAttrs<'a> {
pub(crate) name: Option<CString>,
pub(crate) ty: bpf_prog_type, pub(crate) ty: bpf_prog_type,
pub(crate) insns: &'a [bpf_insn], pub(crate) insns: &'a [bpf_insn],
pub(crate) license: &'a CStr, pub(crate) license: &'a CStr,
pub(crate) kernel_version: u32, pub(crate) kernel_version: u32,
pub(crate) expected_attach_type: Option<bpf_attach_type>, 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_obj_fd: Option<u32>,
pub(crate) attach_btf_id: Option<u32>, pub(crate) attach_btf_id: Option<u32>,
pub(crate) attach_prog_fd: Option<RawFd>,
pub(crate) log: &'a mut VerifierLog, 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 { pub(crate) fn bpf_load_program(aya_attr: BpfLoadProgramAttrs) -> SysResult {
let mut attr = unsafe { mem::zeroed::<bpf_attr>() }; let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
let u = unsafe { &mut attr.__bindgen_anon_3 }; 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; u.prog_type = aya_attr.ty as u32;
if let Some(v) = aya_attr.expected_attach_type { if let Some(v) = aya_attr.expected_attach_type {
u.expected_attach_type = v as u32; 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.insn_cnt = aya_attr.insns.len() as u32;
u.license = aya_attr.license.as_ptr() as u64; u.license = aya_attr.license.as_ptr() as u64;
u.kern_version = aya_attr.kernel_version; 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(); let log_buf = aya_attr.log.buf();
if log_buf.capacity() > 0 { if log_buf.capacity() > 0 {
u.log_level = 7; 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 { if let Some(v) = aya_attr.attach_btf_obj_fd {
u.__bindgen_anon_1.attach_btf_obj_fd = v; 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 { if let Some(v) = aya_attr.attach_btf_id {
u.attach_btf_id = v; u.attach_btf_id = v;
} }
@ -266,6 +312,7 @@ pub(crate) fn bpf_link_create(
prog_fd: RawFd, prog_fd: RawFd,
target_fd: RawFd, target_fd: RawFd,
attach_type: bpf_attach_type, attach_type: bpf_attach_type,
btf_id: Option<u32>,
flags: u32, flags: u32,
) -> SysResult { ) -> SysResult {
let mut attr = unsafe { mem::zeroed::<bpf_attr>() }; 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.__bindgen_anon_1.target_fd = target_fd as u32;
attr.link_create.attach_type = attach_type as u32; attr.link_create.attach_type = attach_type as u32;
attr.link_create.flags = flags; 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) 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 { pub(crate) fn bpf_raw_tracepoint_open(name: Option<&CStr>, prog_fd: RawFd) -> SysResult {
let mut attr = unsafe { mem::zeroed::<bpf_attr>() }; 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) 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 }) syscall(Syscall::Bpf { cmd, attr })
} }

@ -1,7 +1,8 @@
//! Utility functions. //! Utility functions.
use std::{ use std::{
cmp,
collections::BTreeMap, collections::BTreeMap,
ffi::CString, ffi::{CStr, CString},
fs::{self, File}, fs::{self, File},
io::{self, BufReader}, io::{self, BufReader},
mem, slice, 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) 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)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

Loading…
Cancel
Save