#![allow(non_camel_case_types)]
use std::ffi::c_long;
use std::ffi::c_uint;
use std::io;
use std::mem::size_of;
use std::mem::size_of_val;
use std::os::fd::FromRawFd as _;
use std::os::fd::OwnedFd;
use std::os::fd::RawFd;
use libc::syscall;
use libc::SYS_bpf;
type bpf_cmd = c_uint;
const BPF_PROG_GET_NEXT_ID: bpf_cmd = 11;
const BPF_PROG_GET_FD_BY_ID: bpf_cmd = 13;
const BPF_OBJ_GET_INFO_BY_FD: bpf_cmd = 15;
const BPF_BTF_GET_FD_BY_ID: bpf_cmd = 19;
#[repr(C)]
#[derive(Copy, Clone, Debug, Default)]
pub struct bpf_line_info {
pub insn_off: u32,
pub file_name_off: u32,
pub line_off: u32,
pub line_col: u32,
}
impl bpf_line_info {
pub fn line(&self) -> u32 {
self.line_col >> 10
}
pub fn column(&self) -> u16 {
(self.line_col & 0x3ff) as _
}
}
#[repr(C)]
#[derive(Copy, Clone, Debug, Default)]
pub struct bpf_prog_info {
pub type_: u32,
pub id: u32,
pub tag: [u8; 8usize],
pub jited_prog_len: u32,
pub xlated_prog_len: u32,
pub jited_prog_insns: u64,
pub xlated_prog_insns: u64,
pub load_time: u64,
pub created_by_uid: u32,
pub nr_map_ids: u32,
pub map_ids: u64,
pub name: [u8; 16usize],
pub ifindex: u32,
pub _bitfield_align_1: [u8; 0],
pub _bitfield_1: [u8; 4usize],
pub netns_dev: u64,
pub netns_ino: u64,
pub nr_jited_ksyms: u32,
pub nr_jited_func_lens: u32,
pub jited_ksyms: u64,
pub jited_func_lens: u64,
pub btf_id: u32,
pub func_info_rec_size: u32,
pub func_info: u64,
pub nr_func_info: u32,
pub nr_line_info: u32,
pub line_info: u64,
pub jited_line_info: u64,
pub nr_jited_line_info: u32,
pub line_info_rec_size: u32,
pub jited_line_info_rec_size: u32,
pub nr_prog_tags: u32,
pub prog_tags: u64,
pub run_time_ns: u64,
pub run_cnt: u64,
pub recursion_misses: u64,
pub verified_insns: u32,
pub attach_btf_obj_id: u32,
pub attach_btf_id: u32,
pub __bindgen_padding_0: [u8; 4usize],
}
#[repr(C)]
#[derive(Copy, Clone, Debug, Default)]
pub struct bpf_btf_info {
pub btf: u64,
pub btf_size: u32,
pub id: u32,
pub name: u64,
pub name_len: u32,
pub kernel_btf: u32,
}
#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct btf_header {
pub magic: u16,
pub version: u8,
pub flags: u8,
pub hdr_len: u32,
pub type_off: u32,
pub type_len: u32,
pub str_off: u32,
pub str_len: u32,
}
#[repr(C)]
#[derive(Copy, Clone)]
union bpf_attr {
pub __bindgen_anon_6: bpf_attr__bindgen_ty_8,
pub info: bpf_attr__bindgen_ty_9,
}
#[repr(C)]
#[derive(Copy, Clone)]
struct bpf_attr__bindgen_ty_8 {
pub __bindgen_anon_1: bpf_attr__bindgen_ty_8__bindgen_ty_1,
pub next_id: u32,
pub open_flags: u32,
}
#[repr(C)]
#[derive(Copy, Clone)]
union bpf_attr__bindgen_ty_8__bindgen_ty_1 {
pub start_id: u32,
pub prog_id: u32,
pub map_id: u32,
pub btf_id: u32,
pub link_id: u32,
}
#[repr(C)]
#[derive(Copy, Clone, Debug)]
struct bpf_attr__bindgen_ty_9 {
pub bpf_fd: u32,
pub info_len: u32,
pub info: u64,
}
fn sys_bpf(cmd: bpf_cmd, attr: *mut bpf_attr, attr_size: usize) -> io::Result<c_long> {
let rc = unsafe { syscall(SYS_bpf, cmd, attr, attr_size) };
if rc < 0 {
return Err(io::Error::last_os_error())
}
Ok(rc)
}
pub fn bpf_prog_get_next_id(start_id: u32) -> io::Result<u32> {
let mut attr = bpf_attr {
__bindgen_anon_6: bpf_attr__bindgen_ty_8 {
__bindgen_anon_1: bpf_attr__bindgen_ty_8__bindgen_ty_1 { start_id },
next_id: 0,
open_flags: 0,
},
};
let attr_size = unsafe { size_of_val(&attr.__bindgen_anon_6) };
let _rc = sys_bpf(BPF_PROG_GET_NEXT_ID, &mut attr, attr_size)?;
Ok(unsafe { attr.__bindgen_anon_6.next_id })
}
pub fn bpf_prog_get_fd_from_id(prog_id: u32) -> io::Result<OwnedFd> {
let mut attr = bpf_attr {
__bindgen_anon_6: bpf_attr__bindgen_ty_8 {
__bindgen_anon_1: bpf_attr__bindgen_ty_8__bindgen_ty_1 { prog_id },
next_id: 0,
open_flags: 0,
},
};
let attr_size = unsafe { size_of_val(&attr.__bindgen_anon_6) };
let fd = sys_bpf(BPF_PROG_GET_FD_BY_ID, &mut attr, attr_size)?;
let fd = unsafe { OwnedFd::from_raw_fd(fd.try_into().unwrap()) };
Ok(fd)
}
fn bpf_obj_get_info_from_fd<I>(bpf_fd: RawFd, info: &mut I) -> io::Result<()> {
let mut attr = bpf_attr {
info: bpf_attr__bindgen_ty_9 {
bpf_fd: bpf_fd as _,
info_len: size_of::<I>() as _,
info: info as *mut _ as usize as _,
},
};
let attr_size = unsafe { size_of_val(&attr.info) };
let _rc = sys_bpf(BPF_OBJ_GET_INFO_BY_FD, &mut attr, attr_size)?;
Ok(())
}
pub fn bpf_prog_get_info_from_fd(bpf_fd: RawFd, info: &mut bpf_prog_info) -> io::Result<()> {
bpf_obj_get_info_from_fd::<bpf_prog_info>(bpf_fd, info)
}
pub fn bpf_btf_get_fd_from_id(btf_id: u32) -> io::Result<OwnedFd> {
let mut attr = bpf_attr {
__bindgen_anon_6: bpf_attr__bindgen_ty_8 {
__bindgen_anon_1: bpf_attr__bindgen_ty_8__bindgen_ty_1 { btf_id },
next_id: 0,
open_flags: 0,
},
};
let attr_size = unsafe { size_of_val(&attr.__bindgen_anon_6) };
let fd = sys_bpf(BPF_BTF_GET_FD_BY_ID, &mut attr, attr_size)?;
let fd = unsafe { OwnedFd::from_raw_fd(fd.try_into().unwrap()) };
Ok(fd)
}
pub fn bpf_btf_get_info_from_fd(btf_fd: RawFd, info: &mut bpf_btf_info) -> io::Result<()> {
bpf_obj_get_info_from_fd(btf_fd, info)
}
#[cfg(test)]
mod tests {
use super::*;
use std::ffi::CStr;
use std::os::fd::AsRawFd as _;
use blazesym_dev::prog_mut;
use blazesym_dev::test_object;
use blazesym_dev::TracepointCategory;
use tempfile::tempfile;
#[test]
fn invalid_prog_info_retrieval() {
let file = tempfile().unwrap();
let mut info = bpf_prog_info::default();
let err = bpf_prog_get_info_from_fd(file.as_raw_fd(), &mut info).unwrap_err();
assert_eq!(err.kind(), io::ErrorKind::InvalidInput);
}
#[test]
fn prog_discovery() {
let mut obj = test_object("getpid.bpf.o");
let prog = prog_mut(&mut obj, "handle__getpid");
let _link = prog
.attach_tracepoint(TracepointCategory::Syscalls, "sys_enter_getpid")
.expect("failed to attach prog");
let prog_id = 0;
let prog_id = bpf_prog_get_next_id(prog_id).unwrap();
assert_ne!(prog_id, 0);
let fd = bpf_prog_get_fd_from_id(prog_id).unwrap();
assert_ne!(fd.as_raw_fd(), 0);
}
#[test]
fn prog_iteration() {
let mut obj = test_object("getpid.bpf.o");
let prog = prog_mut(&mut obj, "handle__getpid");
let _link = prog
.attach_tracepoint(TracepointCategory::Syscalls, "sys_enter_getpid")
.expect("failed to attach prog");
let mut next_prog_id = 0;
while let Ok(prog_id) = bpf_prog_get_next_id(next_prog_id) {
let fd = bpf_prog_get_fd_from_id(prog_id).unwrap();
let mut info = bpf_prog_info::default();
let () = bpf_prog_get_info_from_fd(fd.as_raw_fd(), &mut info).unwrap();
println!(
"found BPF program: {}",
CStr::from_bytes_until_nul(info.name.as_slice())
.unwrap()
.to_string_lossy()
);
next_prog_id = prog_id;
}
}
}