aya: Make Features part of the public API

This commit adds a new probe for bpf_attach_cookie, which would be used
to implement USDT probes. Since USDT probes aren't currently supported,
we this triggers a dead_code warning in clippy.

There are cases where exposing FEATURES - our lazy static - is actually
helpful to users of the library. For example, they may wish to choose to
load a different version of their bytecode based on current features.
Or, in the case of an orchestrator like bpfd, we might want to allow
users to describe which features their program needs and return nice
error message is one or more nodes in their cluster doesn't support the
necessary feature set.

To do this without breaking the API, we make all the internal members of
the `Features` and `BtfFeatures` structs private, and add accessors for
them. We then add a `features()` API to avoid leaking the
lazy_static.

Signed-off-by: Dave Tucker <dave@dtucker.co.uk>
pull/531/head
Dave Tucker 2 years ago
parent 76d35d10ce
commit 47f764c191

@ -166,12 +166,68 @@ pub enum BtfError {
#[derive(Default, Debug)] #[derive(Default, Debug)]
#[allow(missing_docs)] #[allow(missing_docs)]
pub struct BtfFeatures { pub struct BtfFeatures {
pub btf_func: bool, btf_func: bool,
pub btf_func_global: bool, btf_func_global: bool,
pub btf_datasec: bool, btf_datasec: bool,
pub btf_float: bool, btf_float: bool,
pub btf_decl_tag: bool, btf_decl_tag: bool,
pub btf_type_tag: bool, btf_type_tag: bool,
}
impl BtfFeatures {
#[doc(hidden)]
pub fn new(
btf_func: bool,
btf_func_global: bool,
btf_datasec: bool,
btf_float: bool,
btf_decl_tag: bool,
btf_type_tag: bool,
) -> Self {
BtfFeatures {
btf_func,
btf_func_global,
btf_datasec,
btf_float,
btf_decl_tag,
btf_type_tag,
}
}
/// Returns true if the BTF_TYPE_FUNC is supported.
pub fn btf_func(&self) -> bool {
self.btf_func
}
/// Returns true if the BTF_TYPE_FUNC_GLOBAL is supported.
pub fn btf_func_global(&self) -> bool {
self.btf_func_global
}
/// Returns true if the BTF_TYPE_DATASEC is supported.
pub fn btf_datasec(&self) -> bool {
self.btf_datasec
}
/// Returns true if the BTF_FLOAT is supported.
pub fn btf_float(&self) -> bool {
self.btf_float
}
/// Returns true if the BTF_DECL_TAG is supported.
pub fn btf_decl_tag(&self) -> bool {
self.btf_decl_tag
}
/// Returns true if the BTF_TYPE_TAG is supported.
pub fn btf_type_tag(&self) -> bool {
self.btf_type_tag
}
/// Returns true if the BTF_KIND_FUNC_PROTO is supported.
pub fn btf_kind_func_proto(&self) -> bool {
self.btf_func && self.btf_decl_tag
}
} }
/// Bpf Type Format metadata. /// Bpf Type Format metadata.

@ -42,11 +42,63 @@ const KERNEL_VERSION_ANY: u32 = 0xFFFF_FFFE;
#[derive(Default, Debug)] #[derive(Default, Debug)]
#[allow(missing_docs)] #[allow(missing_docs)]
pub struct Features { pub struct Features {
pub bpf_name: bool, bpf_name: bool,
pub bpf_probe_read_kernel: bool, bpf_probe_read_kernel: bool,
pub bpf_perf_link: bool, bpf_perf_link: bool,
pub bpf_global_data: bool, bpf_global_data: bool,
pub btf: Option<BtfFeatures>, bpf_cookie: bool,
btf: Option<BtfFeatures>,
}
impl Features {
#[doc(hidden)]
pub fn new(
bpf_name: bool,
bpf_probe_read_kernel: bool,
bpf_perf_link: bool,
bpf_global_data: bool,
bpf_cookie: bool,
btf: Option<BtfFeatures>,
) -> Self {
Self {
bpf_name,
bpf_probe_read_kernel,
bpf_perf_link,
bpf_global_data,
bpf_cookie,
btf,
}
}
/// Returns whether BPF program names are supported.
pub fn bpf_name(&self) -> bool {
self.bpf_name
}
/// Returns whether the bpf_probe_read_kernel helper is supported.
pub fn bpf_probe_read_kernel(&self) -> bool {
self.bpf_probe_read_kernel
}
/// Returns whether bpf_links are supported for Kprobes/Uprobes/Tracepoints.
pub fn bpf_perf_link(&self) -> bool {
self.bpf_perf_link
}
/// Returns whether BPF program global data is supported.
pub fn bpf_global_data(&self) -> bool {
self.bpf_global_data
}
/// Returns whether BPF program cookie is supported.
pub fn bpf_cookie(&self) -> bool {
self.bpf_cookie
}
/// If BTF is supported, returns which BTF features are supported.
pub fn btf(&self) -> Option<&BtfFeatures> {
self.btf.as_ref()
}
} }
/// The loaded object file representation /// The loaded object file representation

@ -242,10 +242,7 @@ fn relocate_maps<'a, I: Iterator<Item = &'a Relocation>>(
m m
} else { } else {
let Some(m) = maps_by_section.get(&section_index) else { let Some(m) = maps_by_section.get(&section_index) else {
debug!( debug!("failed relocating map by section index {}", section_index);
"failed relocating map by section index {}",
section_index
);
return Err(RelocationError::SectionNotFound { return Err(RelocationError::SectionNotFound {
symbol_index: rel.symbol_index, symbol_index: rel.symbol_index,
symbol_name: sym.name.clone(), symbol_name: sym.name.clone(),

@ -33,11 +33,11 @@ use crate::{
SkMsg, SkSkb, SkSkbKind, SockOps, SocketFilter, TracePoint, UProbe, Xdp, SkMsg, SkSkb, SkSkbKind, SockOps, SocketFilter, TracePoint, UProbe, Xdp,
}, },
sys::{ sys::{
bpf_load_btf, bpf_map_freeze, bpf_map_update_elem_ptr, is_bpf_global_data_supported, bpf_load_btf, bpf_map_freeze, bpf_map_update_elem_ptr, is_bpf_cookie_supported,
is_btf_datasec_supported, is_btf_decl_tag_supported, is_btf_float_supported, is_bpf_global_data_supported, is_btf_datasec_supported, is_btf_decl_tag_supported,
is_btf_func_global_supported, is_btf_func_supported, is_btf_supported, is_btf_float_supported, is_btf_func_global_supported, is_btf_func_supported,
is_btf_type_tag_supported, is_perf_link_supported, is_probe_read_kernel_supported, is_btf_supported, is_btf_type_tag_supported, is_perf_link_supported,
is_prog_name_supported, retry_with_verifier_logs, is_probe_read_kernel_supported, is_prog_name_supported, retry_with_verifier_logs,
}, },
util::{bytes_of, bytes_of_slice, possible_cpus, VerifierLog, POSSIBLE_CPUS}, util::{bytes_of, bytes_of_slice, possible_cpus, VerifierLog, POSSIBLE_CPUS},
}; };
@ -72,28 +72,34 @@ lazy_static! {
fn detect_features() -> Features { fn detect_features() -> Features {
let btf = if is_btf_supported() { let btf = if is_btf_supported() {
Some(BtfFeatures { Some(BtfFeatures::new(
btf_func: is_btf_func_supported(), is_btf_func_supported(),
btf_func_global: is_btf_func_global_supported(), is_btf_func_global_supported(),
btf_datasec: is_btf_datasec_supported(), is_btf_datasec_supported(),
btf_float: is_btf_float_supported(), is_btf_float_supported(),
btf_decl_tag: is_btf_decl_tag_supported(), is_btf_decl_tag_supported(),
btf_type_tag: is_btf_type_tag_supported(), is_btf_type_tag_supported(),
}) ))
} else { } else {
None None
}; };
let f = Features { let f = Features::new(
bpf_name: is_prog_name_supported(), is_prog_name_supported(),
bpf_probe_read_kernel: is_probe_read_kernel_supported(), is_probe_read_kernel_supported(),
bpf_perf_link: is_perf_link_supported(), is_perf_link_supported(),
bpf_global_data: is_bpf_global_data_supported(), is_bpf_global_data_supported(),
is_bpf_cookie_supported(),
btf, btf,
}; );
debug!("BPF Feature Detection: {:#?}", f); debug!("BPF Feature Detection: {:#?}", f);
f f
} }
/// Returns a reference to the detected BPF features.
pub fn features() -> &'static Features {
&FEATURES
}
/// 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
@ -347,7 +353,7 @@ 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 let Some(features) = &FEATURES.btf { let btf_fd = if let Some(features) = &FEATURES.btf() {
if let Some(btf) = obj.fixup_and_sanitize_btf(features)? { if let Some(btf) = obj.fixup_and_sanitize_btf(features)? {
// load btf to the kernel // load btf to the kernel
Some(load_btf(btf.to_bytes())?) Some(load_btf(btf.to_bytes())?)
@ -364,7 +370,7 @@ impl<'a> BpfLoader<'a> {
let mut maps = HashMap::new(); let mut maps = HashMap::new();
for (name, mut obj) in obj.maps.drain() { for (name, mut obj) in obj.maps.drain() {
if let (false, BpfSectionKind::Bss | BpfSectionKind::Data | BpfSectionKind::Rodata) = if let (false, BpfSectionKind::Bss | BpfSectionKind::Data | BpfSectionKind::Rodata) =
(FEATURES.bpf_global_data, obj.section_kind()) (FEATURES.bpf_global_data(), obj.section_kind())
{ {
continue; continue;
} }
@ -452,7 +458,7 @@ impl<'a> BpfLoader<'a> {
.map(|(name, prog_obj)| { .map(|(name, prog_obj)| {
let function_obj = obj.functions.get(&prog_obj.function_key()).unwrap().clone(); let function_obj = obj.functions.get(&prog_obj.function_key()).unwrap().clone();
let prog_name = if FEATURES.bpf_name { let prog_name = if FEATURES.bpf_name() {
Some(name.clone()) Some(name.clone())
} else { } else {
None None

@ -73,7 +73,7 @@ impl Link for PerfLink {
} }
pub(crate) fn perf_attach(prog_fd: RawFd, fd: RawFd) -> Result<PerfLinkInner, ProgramError> { pub(crate) fn perf_attach(prog_fd: RawFd, fd: RawFd) -> Result<PerfLinkInner, ProgramError> {
if FEATURES.bpf_perf_link { if FEATURES.bpf_perf_link() {
let link_fd = let link_fd =
bpf_link_create(prog_fd, fd, BPF_PERF_EVENT, None, 0).map_err(|(_, io_error)| { bpf_link_create(prog_fd, fd, BPF_PERF_EVENT, None, 0).map_err(|(_, io_error)| {
ProgramError::SyscallError { ProgramError::SyscallError {

@ -719,6 +719,33 @@ pub(crate) fn is_bpf_global_data_supported() -> bool {
false false
} }
pub(crate) fn is_bpf_cookie_supported() -> bool {
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
let u = unsafe { &mut attr.__bindgen_anon_3 };
let prog: &[u8] = &[
0x85, 0x00, 0x00, 0x00, 0xae, 0x00, 0x00, 0x00, // call bpf_get_attach_cookie
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_KPROBE 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 { pub(crate) fn is_btf_supported() -> bool {
let mut btf = Btf::new(); let mut btf = Btf::new();
let name_offset = btf.add_string("int".to_string()); let name_offset = btf.add_string("int".to_string());

Loading…
Cancel
Save