diff --git a/aya-ebpf-macros/src/lib.rs b/aya-ebpf-macros/src/lib.rs index 5c9878b0e..66051d3cf 100644 --- a/aya-ebpf-macros/src/lib.rs +++ b/aya-ebpf-macros/src/lib.rs @@ -23,6 +23,7 @@ mod sk_msg; mod sk_skb; mod sock_ops; mod socket_filter; +mod struct_ops; mod tc; mod tracepoint; mod uprobe; @@ -51,6 +52,7 @@ use sk_msg::SkMsg; use sk_skb::{SkSkb, SkSkbKind}; use sock_ops::SockOps; use socket_filter::SocketFilter; +use struct_ops::StructOps; use tc::SchedClassifier; use tracepoint::TracePoint; use uprobe::{UProbe, UProbeKind}; @@ -601,6 +603,40 @@ pub fn fexit(attrs: TokenStream, item: TokenStream) -> TokenStream { .into() } +/// Marks a function as an eBPF struct_ops program. +/// +/// Struct ops programs implement kernel interfaces like `hid_bpf_ops` for +/// HID device handling or `sched_ext_ops` for custom schedulers. +/// +/// # Arguments +/// +/// * `name` - Optional name for the struct_ops callback. If not specified, +/// the function name is used. +/// * `sleepable` - Mark the program as sleepable. +/// +/// # Minimum kernel version +/// +/// The minimum kernel version required to use this feature is 5.6. +/// +/// # Example +/// +/// ```no_run +/// use aya_ebpf::{macros::struct_ops, programs::StructOpsContext}; +/// +/// #[struct_ops] +/// fn my_callback(ctx: StructOpsContext) -> i32 { +/// 0 +/// } +/// ``` +#[proc_macro_attribute] +pub fn struct_ops(attrs: TokenStream, item: TokenStream) -> TokenStream { + match StructOps::parse(attrs.into(), item.into()) { + Ok(prog) => prog.expand(), + Err(err) => err.into_compile_error(), + } + .into() +} + /// Marks a function as an eBPF Flow Dissector program. /// /// Flow dissector is a program type that parses metadata out of the packets. diff --git a/aya-ebpf-macros/src/struct_ops.rs b/aya-ebpf-macros/src/struct_ops.rs new file mode 100644 index 000000000..02ae6acdb --- /dev/null +++ b/aya-ebpf-macros/src/struct_ops.rs @@ -0,0 +1,151 @@ +use std::borrow::Cow; + +use proc_macro2::TokenStream; +use quote::quote; +use syn::{ItemFn, Result}; + +use crate::args::{err_on_unknown_args, pop_bool_arg, pop_string_arg}; + +pub(crate) struct StructOps { + item: ItemFn, + name: Option, + sleepable: bool, +} + +impl StructOps { + pub(crate) fn parse(attrs: TokenStream, item: TokenStream) -> Result { + let item = syn::parse2(item)?; + let mut args = syn::parse2(attrs)?; + let name = pop_string_arg(&mut args, "name"); + let sleepable = pop_bool_arg(&mut args, "sleepable"); + err_on_unknown_args(&args)?; + Ok(Self { + item, + name, + sleepable, + }) + } + + pub(crate) fn expand(&self) -> TokenStream { + let Self { + item, + name, + sleepable, + } = self; + let ItemFn { + attrs: _, + vis, + sig, + block: _, + } = item; + let section_prefix = if *sleepable { + "struct_ops.s" + } else { + "struct_ops" + }; + let fn_name = &sig.ident; + let section_name: Cow<'_, _> = if let Some(name) = name { + format!("{section_prefix}/{name}").into() + } else { + format!("{section_prefix}/{fn_name}").into() + }; + quote! { + #[unsafe(no_mangle)] + #[unsafe(link_section = #section_name)] + #vis fn #fn_name(ctx: *mut ::core::ffi::c_void) -> i32 { + return #fn_name(::aya_ebpf::programs::StructOpsContext::new(ctx)); + + #item + } + } + } +} + +#[cfg(test)] +mod tests { + use syn::parse_quote; + + use super::*; + + #[test] + fn test_struct_ops() { + let prog = StructOps::parse( + parse_quote! {}, + parse_quote! { + fn my_callback(ctx: &mut aya_ebpf::programs::StructOpsContext) -> i32 { + 0 + } + }, + ) + .unwrap(); + let expanded = prog.expand(); + let expected = quote! { + #[unsafe(no_mangle)] + #[unsafe(link_section = "struct_ops/my_callback")] + fn my_callback(ctx: *mut ::core::ffi::c_void) -> i32 { + return my_callback(::aya_ebpf::programs::StructOpsContext::new(ctx)); + + fn my_callback(ctx: &mut aya_ebpf::programs::StructOpsContext) -> i32 { + 0 + } + } + }; + assert_eq!(expected.to_string(), expanded.to_string()); + } + + #[test] + fn test_struct_ops_with_name() { + let prog = StructOps::parse( + parse_quote! { + name = "hid_device_event" + }, + parse_quote! { + fn my_handler(ctx: &mut aya_ebpf::programs::StructOpsContext) -> i32 { + 0 + } + }, + ) + .unwrap(); + let expanded = prog.expand(); + let expected = quote! { + #[unsafe(no_mangle)] + #[unsafe(link_section = "struct_ops/hid_device_event")] + fn my_handler(ctx: *mut ::core::ffi::c_void) -> i32 { + return my_handler(::aya_ebpf::programs::StructOpsContext::new(ctx)); + + fn my_handler(ctx: &mut aya_ebpf::programs::StructOpsContext) -> i32 { + 0 + } + } + }; + assert_eq!(expected.to_string(), expanded.to_string()); + } + + #[test] + fn test_struct_ops_sleepable() { + let prog = StructOps::parse( + parse_quote! { + sleepable + }, + parse_quote! { + fn my_callback(ctx: &mut aya_ebpf::programs::StructOpsContext) -> i32 { + 0 + } + }, + ) + .unwrap(); + let expanded = prog.expand(); + let expected = quote! { + #[unsafe(no_mangle)] + #[unsafe(link_section = "struct_ops.s/my_callback")] + fn my_callback(ctx: *mut ::core::ffi::c_void) -> i32 { + return my_callback(::aya_ebpf::programs::StructOpsContext::new(ctx)); + + fn my_callback(ctx: &mut aya_ebpf::programs::StructOpsContext) -> i32 { + 0 + } + } + }; + assert_eq!(expected.to_string(), expanded.to_string()); + } +} diff --git a/aya-obj/src/btf/btf.rs b/aya-obj/src/btf/btf.rs index ff28b1648..8650e8c77 100644 --- a/aya-obj/src/btf/btf.rs +++ b/aya-obj/src/btf/btf.rs @@ -448,7 +448,67 @@ impl Btf { }) } - pub(crate) fn type_size(&self, root_type_id: u32) -> Result { + /// Returns the index of a struct member by name. + /// + /// This is useful for struct_ops programs where `expected_attach_type` needs + /// to be set to the member index in the kernel struct. + pub fn struct_member_index( + &self, + struct_type_id: u32, + member_name: &str, + ) -> Result { + let ty = self.types.type_by_id(struct_type_id)?; + let members = match ty { + BtfType::Struct(s) => &s.members, + _ => { + return Err(BtfError::UnexpectedBtfType { + type_id: struct_type_id, + }); + } + }; + for (index, member) in members.iter().enumerate() { + let name = self.string_at(member.name_offset)?; + if name == member_name { + return Ok(index as u32); + } + } + Err(BtfError::UnknownBtfTypeName { + type_name: member_name.to_owned(), + }) + } + + /// Returns the byte offset of a struct member by name. + /// + /// This is useful for struct_ops where we need to find the offset of the + /// `data` field within the kernel wrapper struct (e.g., `bpf_struct_ops_hid_bpf_ops`). + pub fn struct_member_byte_offset( + &self, + struct_type_id: u32, + member_name: &str, + ) -> Result { + let ty = self.types.type_by_id(struct_type_id)?; + let members = match ty { + BtfType::Struct(s) => &s.members, + _ => { + return Err(BtfError::UnexpectedBtfType { + type_id: struct_type_id, + }); + } + }; + for member in members.iter() { + let name = self.string_at(member.name_offset)?; + if name == member_name { + // offset is in bits, convert to bytes + return Ok(member.offset / 8); + } + } + Err(BtfError::UnknownBtfTypeName { + type_name: member_name.to_owned(), + }) + } + + /// Returns the size of a BTF type in bytes. + pub fn type_size(&self, root_type_id: u32) -> Result { let mut type_id = root_type_id; let mut n_elems = 1; for () in core::iter::repeat_n((), MAX_RESOLVE_DEPTH) { @@ -569,8 +629,23 @@ impl Btf { d.name_offset = self.add_string(&fixed_name); } - // There are some cases when the compiler does indeed populate the size. - if d.size > 0 { + // .ksyms is a pseudo-section for extern kernel symbols (see EbpfSectionKind::Ksyms). + // It has no real ELF section and the kernel rejects DATASEC with + // size=0 and non-empty entries. Replace with INT to remove it. + const KSYMS_SECTION: &str = ".ksyms"; + if name == KSYMS_SECTION { + debug!("{kind} {name}: pseudo-section, replacing with INT"); + let old_size = d.type_info_size(); + let new_type = + BtfType::Int(Int::new(d.name_offset, 0, IntEncoding::None, 0)); + let new_size = new_type.type_info_size(); + // Update header to reflect the size change + self.header.type_len = + self.header.type_len - old_size as u32 + new_size as u32; + self.header.str_off = self.header.type_len; + *t = new_type; + } else if d.size > 0 { + // There are some cases when the compiler does indeed populate the size. debug!("{kind} {name}: size fixup not required"); } else { // We need to get the size of the section from the ELF file. @@ -700,6 +775,13 @@ impl Btf { if !features.btf_func { debug!("{kind}: not supported. replacing with TYPEDEF"); *t = BtfType::Typedef(Typedef::new(ty.name_offset, ty.btf_type)); + } else if ty.linkage() == FuncLinkage::Extern { + // BTF_FUNC_EXTERN is used for kfuncs (kernel functions). + // These are resolved from kernel BTF at load time, so we + // should not include them in the program BTF. Replace with + // TYPEDEF to preserve type info without the problematic linkage. + debug!("{kind} {name}: extern kfunc, replacing with TYPEDEF"); + *t = BtfType::Typedef(Typedef::new(ty.name_offset, ty.btf_type)); } else if !features.btf_func_global || name == "memset" || name == "memcpy" diff --git a/aya-obj/src/lib.rs b/aya-obj/src/lib.rs index 6a6f44794..7fc39e4e4 100644 --- a/aya-obj/src/lib.rs +++ b/aya-obj/src/lib.rs @@ -41,7 +41,7 @@ //! let text_sections = std::collections::HashSet::new(); //! #[cfg(not(feature = "std"))] //! let text_sections = hashbrown::HashSet::new(); -//! object.relocate_calls(&text_sections).unwrap(); +//! object.relocate_calls(&text_sections, None).unwrap(); //! object.relocate_maps(std::iter::empty(), &text_sections).unwrap(); //! //! // Run with rbpf diff --git a/aya-obj/src/maps.rs b/aya-obj/src/maps.rs index 217b401b5..3e5b189d9 100644 --- a/aya-obj/src/maps.rs +++ b/aya-obj/src/maps.rs @@ -3,7 +3,9 @@ use alloc::vec::Vec; use core::mem; -use crate::{EbpfSectionKind, InvalidTypeBinding}; +use crate::{ + EbpfSectionKind, InvalidTypeBinding, generated::bpf_map_type::BPF_MAP_TYPE_STRUCT_OPS, +}; impl TryFrom for crate::generated::bpf_map_type { type Error = InvalidTypeBinding; @@ -138,6 +140,8 @@ pub enum Map { Legacy(LegacyMap), /// A map defined in the `.maps` section Btf(BtfMap), + /// A struct_ops map defined in `.struct_ops` or `.struct_ops.link` sections + StructOps(StructOpsMap), } impl Map { @@ -146,6 +150,7 @@ impl Map { match self { Self::Legacy(m) => m.def.map_type, Self::Btf(m) => m.def.map_type, + Self::StructOps(_) => BPF_MAP_TYPE_STRUCT_OPS as u32, } } @@ -154,6 +159,8 @@ impl Map { match self { Self::Legacy(m) => m.def.key_size, Self::Btf(m) => m.def.key_size, + // struct_ops maps always have key_size of 4 + Self::StructOps(_) => 4, } } @@ -162,6 +169,8 @@ impl Map { match self { Self::Legacy(m) => m.def.value_size, Self::Btf(m) => m.def.value_size, + // For struct_ops, value_size is the size of the struct data + Self::StructOps(m) => m.data.len() as u32, } } @@ -170,6 +179,8 @@ impl Map { match self { Self::Legacy(m) => m.def.value_size = size, Self::Btf(m) => m.def.value_size = size, + // struct_ops value_size is determined by the data, cannot be set + Self::StructOps(_) => {} } } @@ -178,6 +189,8 @@ impl Map { match self { Self::Legacy(m) => m.def.max_entries, Self::Btf(m) => m.def.max_entries, + // struct_ops maps always have max_entries of 1 + Self::StructOps(_) => 1, } } @@ -186,6 +199,8 @@ impl Map { match self { Self::Legacy(m) => m.def.max_entries = v, Self::Btf(m) => m.def.max_entries = v, + // struct_ops max_entries is always 1, cannot be changed + Self::StructOps(_) => {} } } @@ -194,6 +209,14 @@ impl Map { match self { Self::Legacy(m) => m.def.map_flags, Self::Btf(m) => m.def.map_flags, + // BPF_F_LINK (0x2000) is used for .struct_ops.link sections + Self::StructOps(m) => { + if m.is_link { + 0x2000 // BPF_F_LINK + } else { + 0 + } + } } } @@ -202,6 +225,7 @@ impl Map { match self { Self::Legacy(m) => m.def.pinning, Self::Btf(m) => m.def.pinning, + Self::StructOps(_) => PinningType::None, } } @@ -210,6 +234,7 @@ impl Map { match self { Self::Legacy(m) => &m.data, Self::Btf(m) => &m.data, + Self::StructOps(m) => &m.data, } } @@ -218,6 +243,7 @@ impl Map { match self { Self::Legacy(m) => m.data.as_mut(), Self::Btf(m) => m.data.as_mut(), + Self::StructOps(m) => m.data.as_mut(), } } @@ -226,6 +252,7 @@ impl Map { match self { Self::Legacy(m) => m.section_index, Self::Btf(m) => m.section_index, + Self::StructOps(m) => m.section_index, } } @@ -234,6 +261,13 @@ impl Map { match self { Self::Legacy(m) => m.section_kind, Self::Btf(_) => EbpfSectionKind::BtfMaps, + Self::StructOps(m) => { + if m.is_link { + EbpfSectionKind::StructOpsLink + } else { + EbpfSectionKind::StructOps + } + } } } @@ -245,6 +279,18 @@ impl Map { match self { Self::Legacy(m) => m.symbol_index, Self::Btf(m) => Some(m.symbol_index), + Self::StructOps(m) => Some(m.symbol_index), + } + } + + /// Returns the BTF value type ID for struct_ops maps. + /// + /// This is the type ID of the struct_ops type in the program's BTF. + pub fn btf_value_type_id(&self) -> Option { + match self { + Self::Legacy(_) => None, + Self::Btf(m) => Some(m.def.btf_value_type_id), + Self::StructOps(m) => Some(m.btf_type_id), } } } @@ -280,3 +326,40 @@ pub struct BtfMap { pub(crate) symbol_index: usize, pub(crate) data: Vec, } + +/// Information about a function pointer field in a struct_ops struct. +#[derive(Debug, Clone)] +pub struct StructOpsFuncInfo { + /// The name of the struct member (function pointer field) + pub member_name: alloc::string::String, + /// The offset of this member within the struct (in bytes) + pub member_offset: u32, + /// The name of the BPF program that implements this callback + pub prog_name: alloc::string::String, +} + +/// A struct_ops map, defined in `.struct_ops` or `.struct_ops.link` sections. +/// +/// Struct_ops maps are special BPF maps that allow implementing kernel subsystem +/// callbacks (like tcp_congestion_ops, hid_bpf_ops, sched_ext_ops) in BPF programs. +#[derive(Debug, Clone)] +pub struct StructOpsMap { + /// The name of the struct_ops type (e.g., "hid_bpf_ops", "tcp_congestion_ops") + pub type_name: alloc::string::String, + /// BTF type ID of the struct_ops type + pub btf_type_id: u32, + /// The section index where this struct_ops was defined + pub section_index: usize, + /// The symbol index for this struct_ops variable + pub symbol_index: usize, + /// The struct instance data (will have program FDs filled in during loading) + pub data: Vec, + /// Whether this is a link-based struct_ops (from `.struct_ops.link` section) + pub is_link: bool, + /// Information about function pointer fields that need BPF programs + pub func_info: Vec, + /// Byte offset of the `data` field within the kernel wrapper struct + /// (e.g., offset of `data` in `bpf_struct_ops_hid_bpf_ops`) + /// This offset must be added to all member offsets when writing to the struct. + pub data_offset: u32, +} diff --git a/aya-obj/src/obj.rs b/aya-obj/src/obj.rs index c3d0d107f..4b065edcc 100644 --- a/aya-obj/src/obj.rs +++ b/aya-obj/src/obj.rs @@ -217,7 +217,6 @@ pub struct Function { /// - `action` /// - `sk_reuseport/migrate`, `sk_reuseport` /// - `syscall` -/// - `struct_ops+` /// - `fmod_ret+`, `fmod_ret.s+` /// - `iter+`, `iter.s+` #[derive(Debug, Clone)] @@ -276,6 +275,13 @@ pub enum ProgramSection { Iter { sleepable: bool, }, + StructOps { + sleepable: bool, + /// The struct member name this program implements (from section name) + member_name: String, + }, + /// BPF_PROG_TYPE_SYSCALL - invoked via bpf() syscall + Syscall, } impl FromStr for ProgramSection { @@ -429,6 +435,15 @@ impl FromStr for ProgramSection { "sk_lookup" => Self::SkLookup, "iter" => Self::Iter { sleepable: false }, "iter.s" => Self::Iter { sleepable: true }, + "struct_ops" => Self::StructOps { + sleepable: false, + member_name: next()?.to_string(), + }, + "struct_ops.s" => Self::StructOps { + sleepable: true, + member_name: next()?.to_string(), + }, + "syscall" => Self::Syscall, _ => { return Err(ParseError::InvalidProgramSection { section: section.to_owned(), @@ -831,6 +846,120 @@ impl Object { Ok(()) } + fn parse_struct_ops_section(&mut self, section: &Section<'_>) -> Result<(), ParseError> { + let btf = self.btf.as_ref().ok_or(ParseError::NoBTF)?; + let is_link = section.kind == EbpfSectionKind::StructOpsLink; + + // Get symbols for this section to find the variable names + let symbols = + self.symbols_by_section + .get(§ion.index) + .ok_or(ParseError::NoSymbolsForSection { + section_name: section.name.to_owned(), + })?; + + // Look for the DATASEC type for this section in BTF + let sec_name = section.name; + let datasec = btf.types().enumerate().find_map(|(i, t)| { + if let crate::btf::BtfType::DataSec(ds) = t { + let name = btf.string_at(ds.name_offset).ok()?; + if name == sec_name { + return Some((i as u32, ds)); + } + } + None + }); + + // If no DATASEC found in BTF, we can still create maps from symbols + // but won't have BTF type information + if let Some((_sec_type_id, datasec)) = datasec { + // Parse variables from BTF DATASEC + for var_sec_info in datasec.entries.iter() { + let var_type = btf.type_by_id(var_sec_info.btf_type)?; + + let var = match var_type { + crate::btf::BtfType::Var(v) => v, + _ => continue, // Skip non-VAR types + }; + + let var_name = btf.string_at(var.name_offset)?.to_string(); + + // Get the actual struct type that the VAR points to + let struct_type_id = var.btf_type; + let struct_type = btf.type_by_id(struct_type_id)?; + + let type_name = match struct_type { + crate::btf::BtfType::Struct(s) => btf.string_at(s.name_offset)?.to_string(), + _ => continue, // Skip if not a struct type + }; + + // Find the symbol index for this variable + let symbol_index = symbols + .iter() + .find(|&&sym_idx| { + if let Some(sym) = self.symbol_table.get(&sym_idx) { + if let Some(name) = sym.name.as_ref() { + return name == &var_name; + } + } + false + }) + .copied() + .unwrap_or(0); + + // Extract the data for this variable from the section + let offset = var_sec_info.offset as usize; + let size = var_sec_info.size as usize; + let data = if offset + size <= section.data.len() { + section.data[offset..offset + size].to_vec() + } else { + alloc::vec![0u8; size] + }; + + // Process relocations to find function pointer fields + // Relocations point to program symbols at specific offsets in the struct + let mut func_info = alloc::vec::Vec::new(); + for rel in section.relocations.iter() { + // Check if this relocation is within this variable's data + let rel_offset = rel.offset as usize; + if rel_offset >= offset && rel_offset < offset + size { + // Get the symbol for this relocation + if let Some(sym) = self.symbol_table.get(&rel.symbol_index) { + // Check if this is a text symbol (program) + if sym.kind == object::SymbolKind::Text { + if let Some(name) = sym.name.as_ref() { + // The member_offset is relative to the start of this struct + let member_offset = (rel_offset - offset) as u32; + func_info.push(crate::maps::StructOpsFuncInfo { + member_name: alloc::string::String::new(), // Could look up from BTF + member_offset, + prog_name: name.clone(), + }); + } + } + } + } + } + + // Create the StructOpsMap entry + let map = crate::maps::StructOpsMap { + type_name, + btf_type_id: struct_type_id, + section_index: section.index.0, + symbol_index, + data, + is_link, + func_info, + data_offset: 0, // Will be set from kernel BTF during loading + }; + + self.maps.insert(var_name, crate::maps::Map::StructOps(map)); + } + } + + Ok(()) + } + fn parse_section(&mut self, section: Section<'_>) -> Result<(), ParseError> { self.section_infos .insert(section.name.to_owned(), (section.index, section.size)); @@ -878,7 +1007,25 @@ impl Object { ); } } - EbpfSectionKind::Undefined | EbpfSectionKind::License | EbpfSectionKind::Version => {} + EbpfSectionKind::Undefined + | EbpfSectionKind::License + | EbpfSectionKind::Version + | EbpfSectionKind::Ksyms => {} + EbpfSectionKind::StructOps | EbpfSectionKind::StructOpsLink => { + self.parse_struct_ops_section(§ion)?; + // Store relocations for struct_ops sections - they'll be used to resolve + // function pointer fields to BPF programs + if !section.relocations.is_empty() { + self.relocations.insert( + section.index, + section + .relocations + .into_iter() + .map(|rel| (rel.offset, rel)) + .collect(), + ); + } + } } Ok(()) @@ -1033,6 +1180,12 @@ pub enum EbpfSectionKind { License, /// `version` Version, + /// `.struct_ops` + StructOps, + /// `.struct_ops.link` + StructOpsLink, + /// `.ksyms` - kernel symbol references resolved at load time + Ksyms, } impl EbpfSectionKind { @@ -1045,6 +1198,10 @@ impl EbpfSectionKind { Self::Maps } else if name.starts_with(".maps") { Self::BtfMaps + } else if name.starts_with(".struct_ops.link") { + Self::StructOpsLink + } else if name.starts_with(".struct_ops") { + Self::StructOps } else if name.starts_with(".text") { Self::Text } else if name.starts_with(".bss") { @@ -1057,6 +1214,8 @@ impl EbpfSectionKind { Self::Btf } else if name == ".BTF.ext" { Self::BtfExt + } else if name == ".ksyms" { + Self::Ksyms } else { Self::Undefined } @@ -2635,6 +2794,52 @@ mod tests { ); } + #[test] + fn test_parse_section_struct_ops() { + let mut obj = fake_obj(); + fake_sym(&mut obj, 0, 0, "foo", FAKE_INS_LEN); + + assert_matches!( + obj.parse_section(fake_section( + EbpfSectionKind::Program, + "struct_ops/foo", + bytes_of(&fake_ins()), + None + )), + Ok(()) + ); + assert_matches!( + obj.programs.get("foo"), + Some(Program { + section: ProgramSection::StructOps { sleepable: false, member_name }, + .. + }) if member_name == "foo" + ); + } + + #[test] + fn test_parse_section_struct_ops_sleepable() { + let mut obj = fake_obj(); + fake_sym(&mut obj, 0, 0, "foo", FAKE_INS_LEN); + + assert_matches!( + obj.parse_section(fake_section( + EbpfSectionKind::Program, + "struct_ops.s/foo", + bytes_of(&fake_ins()), + None + )), + Ok(()) + ); + assert_matches!( + obj.programs.get("foo"), + Some(Program { + section: ProgramSection::StructOps { sleepable: true, member_name }, + .. + }) if member_name == "foo" + ); + } + #[test] fn test_patch_map_data() { let mut obj = fake_obj(); diff --git a/aya-obj/src/relocation.rs b/aya-obj/src/relocation.rs index d035339b4..3eb7a22a0 100644 --- a/aya-obj/src/relocation.rs +++ b/aya-obj/src/relocation.rs @@ -8,9 +8,10 @@ use object::{SectionIndex, SymbolKind}; use crate::{ EbpfSectionKind, + btf::{Btf, BtfKind}, generated::{ - BPF_CALL, BPF_JMP, BPF_K, BPF_PSEUDO_CALL, BPF_PSEUDO_FUNC, BPF_PSEUDO_MAP_FD, - BPF_PSEUDO_MAP_VALUE, bpf_insn, + BPF_CALL, BPF_JMP, BPF_K, BPF_PSEUDO_CALL, BPF_PSEUDO_FUNC, BPF_PSEUDO_KFUNC_CALL, + BPF_PSEUDO_MAP_FD, BPF_PSEUDO_MAP_VALUE, bpf_insn, }, maps::Map, obj::{Function, Object}, @@ -144,9 +145,14 @@ impl Object { } /// Relocates function calls + /// + /// If `kernel_btf` is provided, kfunc calls will be resolved by looking up + /// the function name in the kernel BTF and setting the instruction's `imm` + /// to the BTF type ID. pub fn relocate_calls( &mut self, text_sections: &HashSet, + kernel_btf: Option<&Btf>, ) -> Result<(), EbpfRelocationError> { for (name, program) in self.programs.iter() { let linker = FunctionLinker::new( @@ -154,6 +160,7 @@ impl Object { &self.relocations, &self.symbol_table, text_sections, + kernel_btf, ); let func_orig = @@ -281,6 +288,7 @@ struct FunctionLinker<'a> { relocations: &'a HashMap>, symbol_table: &'a HashMap, text_sections: &'a HashSet, + kernel_btf: Option<&'a Btf>, } impl<'a> FunctionLinker<'a> { @@ -289,6 +297,7 @@ impl<'a> FunctionLinker<'a> { relocations: &'a HashMap>, symbol_table: &'a HashMap, text_sections: &'a HashSet, + kernel_btf: Option<&'a Btf>, ) -> Self { Self { functions, @@ -296,6 +305,7 @@ impl<'a> FunctionLinker<'a> { relocations, symbol_table, text_sections, + kernel_btf, } } @@ -367,17 +377,54 @@ impl<'a> FunctionLinker<'a> { self.symbol_table .get(&rel.symbol_index) .map(|sym| (rel, sym)) - }) - .filter(|(_rel, sym)| { - // only consider text relocations, data relocations are - // relocated in relocate_maps() - sym.kind == SymbolKind::Text - || sym - .section_index - .map(|section_index| self.text_sections.contains(§ion_index)) - .unwrap_or(false) }); + // Check if this is a kfunc (external kernel function) relocation. + // Kfuncs are represented as undefined/external symbols in the ELF file, + // which means they have no section_index (sym.section_index.is_none()). + // These need to be resolved at load time by looking up the function + // in the kernel's BTF and setting the instruction's imm to the BTF ID. + if let Some((_rel, sym)) = &rel { + if sym.section_index.is_none() { + let kfunc_name = sym.name.as_deref().unwrap_or("?"); + + // Look up the kfunc in kernel BTF + if let Some(btf) = self.kernel_btf { + match btf.id_by_type_name_kind(kfunc_name, BtfKind::Func) { + Ok(btf_id) => { + // Set the instruction to call the kfunc via BTF + let ins = &mut program.instructions[ins_index]; + ins.imm = btf_id as i32; + ins.set_src_reg(BPF_PSEUDO_KFUNC_CALL as u8); + debug!( + "resolved kfunc `{kfunc_name}` to BTF id {btf_id} at instruction {ins_index}" + ); + } + Err(e) => { + debug!( + "kfunc `{kfunc_name}` not found in kernel BTF: {e}, leaving unresolved" + ); + } + } + } else { + debug!( + "no kernel BTF available, skipping kfunc `{kfunc_name}` at instruction {ins_index}" + ); + } + continue; + } + } + + // Filter to only consider text relocations - data relocations are + // handled in relocate_maps() + let rel = rel.filter(|(_rel, sym)| { + sym.kind == SymbolKind::Text + || sym + .section_index + .map(|section_index| self.text_sections.contains(§ion_index)) + .unwrap_or(false) + }); + // not a call and not a text relocation, we don't need to do anything if !is_call && rel.is_none() { continue; diff --git a/aya/src/bpf.rs b/aya/src/bpf.rs index 93f58c3e9..ef5beeee4 100644 --- a/aya/src/bpf.rs +++ b/aya/src/bpf.rs @@ -26,7 +26,7 @@ use crate::{ CgroupSockopt, CgroupSysctl, Extension, FEntry, FExit, FlowDissector, Iter, KProbe, LircMode2, Lsm, LsmCgroup, PerfEvent, ProbeKind, Program, ProgramData, ProgramError, RawTracePoint, SchedClassifier, SkLookup, SkMsg, SkSkb, SkSkbKind, SockOps, SocketFilter, - TracePoint, UProbe, Xdp, + StructOps, Syscall, TracePoint, UProbe, Xdp, }, sys::{ bpf_load_btf, is_bpf_cookie_supported, is_bpf_global_data_supported, @@ -458,7 +458,11 @@ impl<'a> EbpfLoader<'a> { | ProgramSection::Lsm { sleepable: _ } | ProgramSection::LsmCgroup | ProgramSection::BtfTracePoint - | ProgramSection::Iter { sleepable: _ } => { + | ProgramSection::Iter { sleepable: _ } + | ProgramSection::StructOps { + sleepable: _, + member_name: _, + } => { return Err(EbpfError::BtfError(err)); } ProgramSection::KRetProbe @@ -488,7 +492,8 @@ impl<'a> EbpfLoader<'a> { | ProgramSection::SkLookup | ProgramSection::FlowDissector | ProgramSection::CgroupSock { attach_type: _ } - | ProgramSection::CgroupDevice => {} + | ProgramSection::CgroupDevice + | ProgramSection::Syscall => {} } } @@ -544,7 +549,38 @@ impl<'a> EbpfLoader<'a> { _ => (), } let btf_fd = btf_fd.as_deref().map(|fd| fd.as_fd()); - let mut map = if let Some(pin_path) = map_pin_path_by_name.get(name.as_str()) { + + // Handle struct_ops maps specially - they need btf_vmlinux_value_type_id + let mut map = if let aya_obj::Map::StructOps(ref struct_ops_map) = obj { + // Look up the kernel BTF type for bpf_struct_ops_ + let vmlinux_btf = btf.as_deref().ok_or_else(|| { + EbpfError::BtfError(aya_obj::btf::BtfError::UnknownBtfTypeName { + type_name: format!( + "bpf_struct_ops_{} (kernel BTF not available)", + struct_ops_map.type_name + ), + }) + })?; + let kernel_type_name = format!("bpf_struct_ops_{}", struct_ops_map.type_name); + let vmlinux_type_id = vmlinux_btf + .id_by_type_name_kind(&kernel_type_name, aya_obj::btf::BtfKind::Struct)?; + + // Get the kernel struct size - the kernel rejects maps with wrong value_size + let kernel_value_size = vmlinux_btf.type_size(vmlinux_type_id)? as u32; + + // Get the offset of the 'data' field within the wrapper struct + // The actual struct_ops data is stored at this offset + let data_offset = vmlinux_btf.struct_member_byte_offset(vmlinux_type_id, "data")?; + + MapData::create_struct_ops( + obj, + &name, + btf_fd, + vmlinux_type_id, + kernel_value_size, + data_offset, + )? + } else if let Some(pin_path) = map_pin_path_by_name.get(name.as_str()) { MapData::create_pinned_by_name(pin_path, obj, &name, btf_fd)? } else { match obj.pinning() { @@ -561,7 +597,11 @@ impl<'a> EbpfLoader<'a> { } } }; - map.finalize()?; + // Don't finalize struct_ops maps - they need program FDs filled in first + // and will be registered later via StructOpsMap::register() + if !matches!(map.obj(), aya_obj::Map::StructOps(_)) { + map.finalize()?; + } maps.insert(name, map); } @@ -576,7 +616,7 @@ impl<'a> EbpfLoader<'a> { .map(|(s, data)| (s.as_str(), data.fd().as_fd().as_raw_fd(), data.obj())), &text_sections, )?; - obj.relocate_calls(&text_sections)?; + obj.relocate_calls(&text_sections, btf.as_deref())?; obj.sanitize_functions(&FEATURES); let programs = obj @@ -756,6 +796,23 @@ impl<'a> EbpfLoader<'a> { } Program::Iter(Iter { data }) } + ProgramSection::StructOps { + sleepable, + member_name, + } => { + let mut data = + ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level); + if *sleepable { + data.flags = BPF_F_SLEEPABLE; + } + Program::StructOps(StructOps { + data, + member_name: member_name.clone(), + }) + } + ProgramSection::Syscall => Program::Syscall(Syscall { + data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level), + }), } }; (name, program) @@ -798,6 +855,7 @@ fn parse_map( BPF_MAP_TYPE_DEVMAP_HASH => Map::DevMapHash(map), BPF_MAP_TYPE_XSKMAP => Map::XskMap(map), BPF_MAP_TYPE_SK_STORAGE => Map::SkStorage(map), + BPF_MAP_TYPE_STRUCT_OPS => Map::StructOps(map), m_type => { if allow_unsupported_maps { Map::Unsupported(map) @@ -1164,6 +1222,172 @@ impl Ebpf { pub fn programs_mut(&mut self) -> impl Iterator { self.programs.iter_mut().map(|(s, p)| (s.as_str(), p)) } + + /// Loads struct_ops programs and fills their file descriptors into the corresponding maps. + /// + /// This method must be called before registering struct_ops maps. It: + /// 1. Loads all struct_ops programs + /// 2. Gets their file descriptors + /// 3. Writes the FDs to the correct offsets in the struct_ops map data + /// + /// After calling this method, you can register the struct_ops map using + /// [`StructOpsMap::register`](crate::maps::StructOpsMap::register). + /// + /// # Arguments + /// + /// * `btf` - The kernel BTF information, typically from `Btf::from_sys_fs()` + /// + /// # Example + /// + /// ```no_run + /// use aya::{Ebpf, Btf, maps::StructOpsMap}; + /// + /// let mut bpf = Ebpf::load_file("my_struct_ops.o")?; + /// let btf = Btf::from_sys_fs()?; + /// bpf.load_struct_ops(&btf)?; + /// + /// let mut struct_ops: StructOpsMap<_> = bpf.take_map("my_ops").unwrap().try_into()?; + /// struct_ops.register()?; + /// # Ok::<(), Box>(()) + /// ``` + pub fn load_struct_ops(&mut self, btf: &Btf) -> Result<(), EbpfError> { + use std::os::fd::{AsFd as _, AsRawFd as _}; + + use aya_obj::btf::BtfKind; + + // Collect (map_name, struct_name, program_names) for each struct_ops map + let mut struct_ops_info: Vec<(String, String, Vec)> = Vec::new(); + for (map_name, map) in self.maps.iter() { + if let Map::StructOps(map_data) = map { + if let aya_obj::Map::StructOps(m) = map_data.obj() { + let prog_names: Vec = + m.func_info.iter().map(|i| i.prog_name.clone()).collect(); + debug!( + "struct_ops map '{map_name}' type='{}' prog_names={prog_names:?}", + m.type_name + ); + struct_ops_info.push((map_name.clone(), m.type_name.clone(), prog_names)); + } + } + } + + // If func_info is empty (no relocations), fall back to matching by member_name + // This happens with Rust BPF objects where function pointer fields can't create relocations + let mut fallback_matches: Vec<(String, String, String, u32)> = Vec::new(); // (map_name, struct_name, prog_name, member_offset) + for (map_name, struct_name, prog_names) in &struct_ops_info { + if prog_names.is_empty() { + debug!( + "struct_ops map '{map_name}' has no func_info, using fallback matching by member_name" + ); + // Get struct BTF type ID for member offset lookup + if let Ok(struct_type_id) = btf.id_by_type_name_kind(struct_name, BtfKind::Struct) { + // Find all StructOps programs and match by member_name + for (prog_name, prog) in self.programs.iter() { + if let Program::StructOps(struct_ops) = prog { + let member_name = struct_ops.member_name(); + if let Ok(member_offset) = + btf.struct_member_byte_offset(struct_type_id, member_name) + { + debug!( + "fallback: matched program '{prog_name}' to member '{member_name}' at offset {member_offset}" + ); + fallback_matches.push(( + map_name.clone(), + struct_name.clone(), + prog_name.clone(), + member_offset, + )); + } + } + } + } + } + } + + // Load all struct_ops programs (from func_info) + for (_, struct_name, prog_names) in &struct_ops_info { + for prog_name in prog_names { + debug!("looking for struct_ops program '{prog_name}'"); + if let Some(Program::StructOps(struct_ops)) = self.programs.get_mut(prog_name) { + debug!("loading struct_ops program '{prog_name}' for struct '{struct_name}'"); + struct_ops.load(struct_name, btf)?; + } else { + warn!("struct_ops program '{prog_name}' not found or not StructOps type"); + } + } + } + + // Load fallback-matched programs + for (_, struct_name, prog_name, _) in &fallback_matches { + if let Some(Program::StructOps(struct_ops)) = self.programs.get_mut(prog_name) { + debug!( + "loading fallback struct_ops program '{prog_name}' for struct '{struct_name}'" + ); + struct_ops.load(struct_name, btf)?; + } + } + + // Fill program FDs into struct_ops maps + for (map_name, map) in self.maps.iter_mut() { + if let Map::StructOps(map_data) = map { + // Get the func_info and data_offset from the map's object + let (func_info, data_offset): (Vec<_>, u32) = { + if let aya_obj::Map::StructOps(m) = map_data.obj() { + (m.func_info.clone(), m.data_offset) + } else { + continue; + } + }; + + debug!("struct_ops map '{map_name}' data_offset={data_offset}"); + + // Fill FDs from func_info (relocation-based) + for info in &func_info { + let actual_offset = (data_offset + info.member_offset) as usize; + if let Some(Program::StructOps(prog)) = self.programs.get(&info.prog_name) { + let fd = prog.fd()?; + let raw_fd = fd.as_fd().as_raw_fd(); + let member_offset = info.member_offset; + let prog_name = &info.prog_name; + debug!( + "filling FD {raw_fd} for program '{prog_name}' at offset {actual_offset} (data_offset {data_offset} + member_offset {member_offset})" + ); + let data = map_data.obj_mut().data_mut(); + if actual_offset + 4 <= data.len() { + data[actual_offset..actual_offset + 4] + .copy_from_slice(&raw_fd.to_ne_bytes()); + } + } else { + let prog_name = &info.prog_name; + warn!("struct_ops program '{prog_name}' not found"); + } + } + + // Fill FDs from fallback matches (section-name based) + if func_info.is_empty() { + for (fallback_map, _, prog_name, member_offset) in &fallback_matches { + if fallback_map == map_name { + let actual_offset = (data_offset + member_offset) as usize; + if let Some(Program::StructOps(prog)) = self.programs.get(prog_name) { + let fd = prog.fd()?; + let raw_fd = fd.as_fd().as_raw_fd(); + debug!( + "filling FD {raw_fd} for fallback program '{prog_name}' at offset {actual_offset} (data_offset {data_offset} + member_offset {member_offset})" + ); + let data = map_data.obj_mut().data_mut(); + if actual_offset + 4 <= data.len() { + data[actual_offset..actual_offset + 4] + .copy_from_slice(&raw_fd.to_ne_bytes()); + } + } + } + } + } + } + } + + Ok(()) + } } /// The error type returned by [`Ebpf::load_file`] and [`Ebpf::load`]. diff --git a/aya/src/maps/mod.rs b/aya/src/maps/mod.rs index 9e65c0cce..64ed777fc 100644 --- a/aya/src/maps/mod.rs +++ b/aya/src/maps/mod.rs @@ -66,8 +66,9 @@ use crate::{ PinningType, Pod, pin::PinError, sys::{ - SyscallError, bpf_create_map, bpf_get_object, bpf_map_freeze, bpf_map_get_fd_by_id, - bpf_map_get_next_key, bpf_map_update_elem_ptr, bpf_pin_object, + SyscallError, bpf_create_map, bpf_create_map_with_vmlinux_btf, bpf_get_object, + bpf_map_freeze, bpf_map_get_fd_by_id, bpf_map_get_next_key, bpf_map_update_elem_ptr, + bpf_pin_object, }, util::nr_cpus, }; @@ -84,6 +85,7 @@ pub mod sk_storage; pub mod sock; pub mod stack; pub mod stack_trace; +pub mod struct_ops; pub mod xdp; pub use array::{Array, PerCpuArray, ProgramArray}; @@ -98,6 +100,7 @@ pub use sk_storage::SkStorage; pub use sock::{SockHash, SockMap}; pub use stack::Stack; pub use stack_trace::StackTraceMap; +pub use struct_ops::StructOpsMap; pub use xdp::{CpuMap, DevMap, DevMapHash, XskMap}; #[derive(Error, Debug)] @@ -275,6 +278,8 @@ pub enum Map { Stack(MapData), /// A [`StackTraceMap`] map. StackTraceMap(MapData), + /// A [`StructOpsMap`] map. + StructOps(MapData), /// An unsupported map type. Unsupported(MapData), /// A [`XskMap`] map. @@ -305,6 +310,7 @@ impl Map { Self::SkStorage(map) => map.obj.map_type(), Self::Stack(map) => map.obj.map_type(), Self::StackTraceMap(map) => map.obj.map_type(), + Self::StructOps(map) => map.obj.map_type(), Self::Unsupported(map) => map.obj.map_type(), Self::XskMap(map) => map.obj.map_type(), } @@ -336,6 +342,7 @@ impl Map { Self::SkStorage(map) => map.pin(path), Self::Stack(map) => map.pin(path), Self::StackTraceMap(map) => map.pin(path), + Self::StructOps(map) => map.pin(path), Self::Unsupported(map) => map.pin(path), Self::XskMap(map) => map.pin(path), } @@ -380,7 +387,7 @@ impl Map { bpf_map_type::BPF_MAP_TYPE_CGROUP_STORAGE_DEPRECATED => Self::Unsupported(map_data), bpf_map_type::BPF_MAP_TYPE_REUSEPORT_SOCKARRAY => Self::Unsupported(map_data), bpf_map_type::BPF_MAP_TYPE_SK_STORAGE => Self::SkStorage(map_data), - bpf_map_type::BPF_MAP_TYPE_STRUCT_OPS => Self::Unsupported(map_data), + bpf_map_type::BPF_MAP_TYPE_STRUCT_OPS => Self::StructOps(map_data), bpf_map_type::BPF_MAP_TYPE_INODE_STORAGE => Self::Unsupported(map_data), bpf_map_type::BPF_MAP_TYPE_TASK_STORAGE => Self::Unsupported(map_data), bpf_map_type::BPF_MAP_TYPE_USER_RINGBUF => Self::Unsupported(map_data), @@ -426,6 +433,7 @@ impl_map_pin!(() { ProgramArray, SockMap, StackTraceMap, + StructOpsMap, CpuMap, DevMap, DevMapHash, @@ -508,6 +516,7 @@ impl_try_from_map!(() { RingBuf, SockMap, StackTraceMap, + StructOpsMap from StructOps, XskMap, }); @@ -607,6 +616,48 @@ impl MapData { }) } + /// Creates a struct_ops map with the btf_vmlinux_value_type_id. + /// + /// This is used for BPF_MAP_TYPE_STRUCT_OPS maps which require the kernel BTF + /// type ID for the struct_ops type. + pub(crate) fn create_struct_ops( + mut obj: aya_obj::Map, + name: &str, + btf_fd: Option>, + btf_vmlinux_value_type_id: u32, + kernel_value_size: u32, + data_offset: u32, + ) -> Result { + let c_name = CString::new(name) + .map_err(|std::ffi::NulError { .. }| MapError::InvalidName { name: name.into() })?; + + // For struct_ops maps, value_size must match kernel wrapper size (bpf_struct_ops_) + // The .struct_ops section data represents the ops struct, which must be placed at + // data_offset within the kernel wrapper struct (bpf_struct_ops_). + if let aya_obj::Map::StructOps(ref mut m) = obj { + let original_data = std::mem::take(&mut m.data); + let mut new_data = vec![0u8; kernel_value_size as usize]; + let offset = data_offset as usize; + // Copy original data to the correct offset within the wrapper struct + if offset + original_data.len() <= new_data.len() { + new_data[offset..offset + original_data.len()].copy_from_slice(&original_data); + } + m.data = new_data; + m.data_offset = data_offset; + } + + let fd = + bpf_create_map_with_vmlinux_btf(&c_name, &obj, btf_fd, Some(btf_vmlinux_value_type_id)) + .map_err(|io_error| MapError::CreateError { + name: name.into(), + io_error, + })?; + Ok(Self { + obj, + fd: MapFd::from_fd(fd), + }) + } + pub(crate) fn create_pinned_by_name>( path: P, obj: aya_obj::Map, @@ -768,6 +819,11 @@ impl MapData { obj } + pub(crate) fn obj_mut(&mut self) -> &mut aya_obj::Map { + let Self { obj, fd: _ } = self; + obj + } + /// Returns the kernel's information about the loaded map. pub fn info(&self) -> Result { MapInfo::new_from_fd(self.fd.as_fd()) diff --git a/aya/src/maps/struct_ops.rs b/aya/src/maps/struct_ops.rs new file mode 100644 index 000000000..31b784210 --- /dev/null +++ b/aya/src/maps/struct_ops.rs @@ -0,0 +1,302 @@ +//! A struct_ops map for implementing kernel callbacks. +//! +//! Struct_ops maps are special maps that allow implementing kernel subsystem callbacks +//! (like tcp_congestion_ops, hid_bpf_ops, sched_ext_ops) in BPF programs. +//! +//! Unlike regular maps that store key-value pairs, struct_ops maps represent a registration +//! mechanism for kernel callbacks. The "value" is the struct_ops structure with program FDs +//! filled in at the appropriate offsets. + +use std::{ + borrow::{Borrow, BorrowMut}, + os::fd::AsFd as _, +}; + +use aya_obj::maps::StructOpsFuncInfo; +use log::debug; + +use crate::{ + maps::{MapData, MapError, MapFd}, + programs::links::FdLink, + sys::{SyscallError, bpf_map_update_elem_ptr, bpf_struct_ops_link_create}, +}; + +/// A struct_ops map that implements kernel callbacks. +/// +/// Struct_ops maps are used to register BPF programs as implementations of kernel +/// callbacks like `hid_bpf_ops` for HID device handling or `sched_ext_ops` for +/// custom schedulers. +/// +/// # Example +/// +/// ```no_run +/// # #[derive(thiserror::Error, Debug)] +/// # enum Error { +/// # #[error(transparent)] +/// # BtfError(#[from] aya::BtfError), +/// # #[error(transparent)] +/// # Map(#[from] aya::maps::MapError), +/// # #[error(transparent)] +/// # Program(#[from] aya::programs::ProgramError), +/// # #[error(transparent)] +/// # Ebpf(#[from] aya::EbpfError), +/// # } +/// # let mut bpf = aya::Ebpf::load_file("ebpf_programs.o")?; +/// use aya::maps::StructOpsMap; +/// use aya::Btf; +/// +/// // Get the struct_ops map +/// let struct_ops: StructOpsMap<_> = bpf.map("my_struct_ops").unwrap().try_into()?; +/// +/// # Ok::<(), Error>(()) +/// ``` +#[derive(Debug)] +#[doc(alias = "BPF_MAP_TYPE_STRUCT_OPS")] +pub struct StructOpsMap { + pub(crate) inner: T, +} + +impl> StructOpsMap { + pub(crate) fn new(map: T) -> Result { + let data = map.borrow(); + let _map_type = data.obj.map_type(); + + Ok(Self { inner: map }) + } + + /// Returns the file descriptor of the map. + pub fn fd(&self) -> &MapFd { + self.inner.borrow().fd() + } + + /// Returns information about the function pointer fields in this struct_ops. + /// + /// Each entry contains: + /// - `member_name`: The name of the function pointer field in the struct + /// - `member_offset`: The byte offset where the program FD should be written + /// - `prog_name`: The name of the BPF program that implements this callback + pub fn func_info(&self) -> Option<&[StructOpsFuncInfo]> { + let data = self.inner.borrow(); + if let aya_obj::Map::StructOps(m) = data.obj() { + Some(&m.func_info) + } else { + None + } + } + + /// Returns the type name of this struct_ops (e.g., "hid_bpf_ops"). + pub fn type_name(&self) -> Option<&str> { + let data = self.inner.borrow(); + if let aya_obj::Map::StructOps(m) = data.obj() { + Some(&m.type_name) + } else { + None + } + } + + /// Returns whether this is a link-based struct_ops. + pub fn is_link(&self) -> bool { + let data = self.inner.borrow(); + if let aya_obj::Map::StructOps(m) = data.obj() { + m.is_link + } else { + false + } + } +} + +impl> StructOpsMap { + /// Sets a program FD at the specified offset in the struct data. + /// + /// This is an internal method used by [`Ebpf::load_struct_ops`] to fill in + /// program FDs for function pointer fields. Users should call + /// `load_struct_ops()` instead of this method directly. + /// + /// [`Ebpf::load_struct_ops`]: crate::Ebpf::load_struct_ops + #[allow(dead_code)] + pub(crate) fn set_prog_fd(&mut self, offset: u32, fd: i32) -> Result<(), MapError> { + self.set_field_i32(offset, fd) + } + + /// Sets an i32 field at the specified byte offset in the struct data. + /// + /// This is useful for setting fields like `hid_id` in HID-BPF struct_ops + /// before registration. + /// + /// # Arguments + /// + /// * `offset` - The byte offset where the value should be written + /// * `value` - The i32 value to write + /// + /// # Example + /// + /// ```no_run + /// # use aya::maps::StructOpsMap; + /// # fn example(struct_ops: &mut StructOpsMap) -> Result<(), aya::maps::MapError> { + /// // Set hid_id at offset 0 before registration + /// struct_ops.set_field_i32(0, 189)?; // hid_id = 189 + /// # Ok(()) + /// # } + /// ``` + pub fn set_field_i32(&mut self, offset: u32, value: i32) -> Result<(), MapError> { + let map_data = self.inner.borrow_mut(); + let obj = map_data.obj_mut(); + + // Get the data_offset from the struct_ops map - this is the offset of the + // actual ops struct within the kernel wrapper struct + let data_offset = if let aya_obj::Map::StructOps(m) = obj { + m.data_offset + } else { + 0 + }; + + let data = obj.data_mut(); + // Add data_offset to the user-provided offset + let actual_offset = (data_offset + offset) as usize; + if actual_offset + 4 > data.len() { + return Err(MapError::OutOfBounds { + index: actual_offset as u32, + max_entries: data.len() as u32, + }); + } + + data[actual_offset..actual_offset + 4].copy_from_slice(&value.to_ne_bytes()); + Ok(()) + } + + /// Sets a u32 field at the specified byte offset in the struct data. + /// + /// # Arguments + /// + /// * `offset` - The byte offset where the value should be written + /// * `value` - The u32 value to write + /// + /// # Example + /// + /// ```no_run + /// # use aya::maps::StructOpsMap; + /// # fn example(struct_ops: &mut StructOpsMap) -> Result<(), aya::maps::MapError> { + /// // Set flags at offset 4 + /// struct_ops.set_field_u32(4, 0)?; + /// # Ok(()) + /// # } + /// ``` + pub fn set_field_u32(&mut self, offset: u32, value: u32) -> Result<(), MapError> { + let map_data = self.inner.borrow_mut(); + let obj = map_data.obj_mut(); + + // Get the data_offset from the struct_ops map - this is the offset of the + // actual ops struct within the kernel wrapper struct + let data_offset = if let aya_obj::Map::StructOps(m) = obj { + m.data_offset + } else { + 0 + }; + + let data = obj.data_mut(); + // Add data_offset to the user-provided offset + let actual_offset = (data_offset + offset) as usize; + if actual_offset + 4 > data.len() { + return Err(MapError::OutOfBounds { + index: actual_offset as u32, + max_entries: data.len() as u32, + }); + } + + data[actual_offset..actual_offset + 4].copy_from_slice(&value.to_ne_bytes()); + Ok(()) + } +} + +impl> StructOpsMap { + /// Registers the struct_ops by calling bpf_map_update_elem. + /// + /// This should be called after [`Ebpf::load_struct_ops`] has been called to load + /// all associated BPF programs and fill their file descriptors into the struct data. + /// + /// [`Ebpf::load_struct_ops`]: crate::Ebpf::load_struct_ops + pub fn register(&self) -> Result<(), MapError> { + let data = self.inner.borrow(); + let key: u32 = 0; + let fd = data.fd().as_fd(); + + let data_offset = if let aya_obj::Map::StructOps(m) = data.obj() { + m.data_offset + } else { + 0 + }; + + debug!( + "registering struct_ops map: data_len={} data_offset={}", + data.obj().data().len(), + data_offset + ); + + // For struct_ops, we need to update the map with the struct data. + // The struct data should already have program FDs filled in. + // Use a mutable copy to avoid casting away const from immutable data. + let mut value = data.obj().data().to_vec(); + bpf_map_update_elem_ptr(fd, &key, value.as_mut_ptr(), 0) + .map_err(|io_error| SyscallError { + call: "bpf_map_update_elem", + io_error, + }) + .map_err(MapError::from)?; + + Ok(()) + } + + /// Attaches a link-based struct_ops by creating a BPF link. + /// + /// For struct_ops maps created with the `BPF_F_LINK` flag (from `.struct_ops.link` section), + /// this method creates a BPF link that activates the struct_ops. This must be called + /// after [`register()`](Self::register) has been called to update the map data. + /// + /// For non-link struct_ops (from `.struct_ops` section), calling [`register()`](Self::register) + /// alone is sufficient - no link is needed. + /// + /// # Returns + /// + /// An `FdLink` that keeps the struct_ops active. When the link is dropped (or its + /// file descriptor is closed), the struct_ops will be detached. + /// + /// # Example + /// + /// ```no_run + /// # use aya::maps::StructOpsMap; + /// # fn example(struct_ops: &StructOpsMap) -> Result<(), aya::maps::MapError> { + /// // First register the struct_ops data + /// struct_ops.register()?; + /// + /// // For link-based struct_ops, create the link to activate + /// if struct_ops.is_link() { + /// let _link = struct_ops.attach()?; + /// // Keep the link alive to maintain attachment + /// } + /// # Ok(()) + /// # } + /// ``` + pub fn attach(&self) -> Result { + let data = self.inner.borrow(); + let fd = data.fd().as_fd(); + + debug!("creating struct_ops link for map"); + + let link_fd = bpf_struct_ops_link_create(fd) + .map_err(|io_error| SyscallError { + call: "bpf_link_create", + io_error, + }) + .map_err(MapError::from)?; + + Ok(FdLink::new(link_fd)) + } +} + +impl TryFrom for StructOpsMap { + type Error = MapError; + + fn try_from(map: MapData) -> Result { + Self::new(map) + } +} diff --git a/aya/src/programs/mod.rs b/aya/src/programs/mod.rs index 9e151ad8c..669683d5d 100644 --- a/aya/src/programs/mod.rs +++ b/aya/src/programs/mod.rs @@ -66,6 +66,8 @@ pub mod sk_msg; pub mod sk_skb; pub mod sock_ops; pub mod socket_filter; +pub mod struct_ops; +pub mod syscall; pub mod tc; pub mod tp_btf; pub mod trace_point; @@ -119,6 +121,8 @@ pub use crate::programs::{ sk_skb::{SkSkb, SkSkbKind}, sock_ops::SockOps, socket_filter::{SocketFilter, SocketFilterError}, + struct_ops::StructOps, + syscall::Syscall, tc::{SchedClassifier, TcAttachType, TcError}, tp_btf::BtfTracePoint, trace_point::{TracePoint, TracePointError}, @@ -329,6 +333,10 @@ pub enum Program { CgroupDevice(CgroupDevice), /// An [`Iter`] program Iter(Iter), + /// A [`StructOps`] program + StructOps(StructOps), + /// A [`Syscall`] program + Syscall(Syscall), } impl Program { @@ -369,6 +377,8 @@ impl Program { Self::CgroupSock(_) => ProgramType::CgroupSock, Self::CgroupDevice(_) => ProgramType::CgroupDevice, Self::FlowDissector(_) => ProgramType::FlowDissector, + Self::StructOps(_) => ProgramType::StructOps, + Self::Syscall(_) => ProgramType::Syscall, } } @@ -402,6 +412,8 @@ impl Program { Self::CgroupSock(p) => p.pin(path), Self::CgroupDevice(p) => p.pin(path), Self::Iter(p) => p.pin(path), + Self::StructOps(p) => p.pin(path), + Self::Syscall(p) => p.pin(path), } } @@ -435,6 +447,8 @@ impl Program { Self::CgroupSock(mut p) => p.unload(), Self::CgroupDevice(mut p) => p.unload(), Self::Iter(mut p) => p.unload(), + Self::StructOps(mut p) => p.unload(), + Self::Syscall(mut p) => p.unload(), } } @@ -470,6 +484,8 @@ impl Program { Self::CgroupSock(p) => p.fd(), Self::CgroupDevice(p) => p.fd(), Self::Iter(p) => p.fd(), + Self::StructOps(p) => p.fd(), + Self::Syscall(p) => p.fd(), } } @@ -506,6 +522,8 @@ impl Program { Self::CgroupSock(p) => p.info(), Self::CgroupDevice(p) => p.info(), Self::Iter(p) => p.info(), + Self::StructOps(p) => p.info(), + Self::Syscall(p) => p.info(), } } } @@ -823,6 +841,8 @@ impl_program_unload!( CgroupSock, CgroupDevice, Iter, + StructOps, + Syscall, ); macro_rules! impl_fd { @@ -866,6 +886,8 @@ impl_fd!( CgroupSock, CgroupDevice, Iter, + StructOps, + Syscall, ); /// Trait implemented by the [`Program`] types which support the kernel's @@ -974,6 +996,8 @@ impl_program_pin!( CgroupSock, CgroupDevice, Iter, + StructOps, + Syscall, ); macro_rules! impl_from_pin { @@ -1016,6 +1040,25 @@ impl_from_pin!( Iter, ); +// StructOps needs special handling for from_pin since it has member_name field +impl StructOps { + /// Creates a program from a pinned entry on a bpffs. + /// + /// Existing links will not be populated. To work with existing links you should use [`crate::programs::links::PinnedLink`]. + /// + /// On drop, any managed links are detached and the program is unloaded. This will not result in + /// the program being unloaded from the kernel if it is still pinned. + /// + /// Note: The member name cannot be determined from a pinned program. + pub fn from_pin>(path: P) -> Result { + let data = ProgramData::from_pinned_path(path, VerifierLogLevel::default())?; + Ok(Self { + data, + member_name: String::new(), + }) + } +} + macro_rules! impl_from_prog_info { ( $(#[$doc:meta])* @@ -1180,6 +1223,8 @@ impl_try_from_program!( CgroupSock, CgroupDevice, Iter, + StructOps, + Syscall, ); impl_info!( @@ -1210,6 +1255,8 @@ impl_info!( CgroupSock, CgroupDevice, Iter, + StructOps, + Syscall, ); /// Returns an iterator over all loaded links. diff --git a/aya/src/programs/struct_ops.rs b/aya/src/programs/struct_ops.rs new file mode 100644 index 000000000..c55627cf3 --- /dev/null +++ b/aya/src/programs/struct_ops.rs @@ -0,0 +1,153 @@ +//! struct_ops programs. + +use aya_obj::{ + btf::{Btf, BtfKind}, + generated::{bpf_attach_type, bpf_prog_type::BPF_PROG_TYPE_STRUCT_OPS}, +}; +use log::debug; + +use crate::programs::{ + FdLink, FdLinkId, Link, ProgramData, ProgramError, ProgramType, links::id_as_key, load_program, +}; + +/// A program that implements a kernel struct_ops interface. +/// +/// Struct ops programs are used to implement kernel interfaces like `sched_ext_ops` for +/// custom schedulers or `hid_bpf_ops` for HID device handling. Unlike other BPF program +/// types, struct_ops programs are callbacks that the kernel invokes when specific +/// operations are needed. +/// +/// # Minimum kernel version +/// +/// The minimum kernel version required to use this feature is 5.6. +/// +/// # Examples +/// +/// ```no_run +/// # #[derive(thiserror::Error, Debug)] +/// # enum Error { +/// # #[error(transparent)] +/// # BtfError(#[from] aya::BtfError), +/// # #[error(transparent)] +/// # Program(#[from] aya::programs::ProgramError), +/// # #[error(transparent)] +/// # Ebpf(#[from] aya::EbpfError), +/// # } +/// # let mut bpf = Ebpf::load_file("ebpf_programs.o")?; +/// use aya::{Ebpf, programs::StructOps, Btf}; +/// +/// let btf = Btf::from_sys_fs()?; +/// let program: &mut StructOps = bpf.program_mut("my_struct_ops").unwrap().try_into()?; +/// program.load("hid_bpf_ops", &btf)?; +/// # Ok::<(), Error>(()) +/// ``` +#[derive(Debug)] +#[doc(alias = "BPF_PROG_TYPE_STRUCT_OPS")] +pub struct StructOps { + pub(crate) data: ProgramData, + /// The struct member name this program implements (from section name) + pub(crate) member_name: String, +} + +impl StructOps { + /// The type of the program according to the kernel. + pub const PROGRAM_TYPE: ProgramType = ProgramType::StructOps; + + /// Loads the program inside the kernel. + /// + /// # Arguments + /// + /// * `struct_name` - the name of the struct_ops type (e.g., "hid_bpf_ops", + /// "sched_ext_ops") + /// * `btf` - the BTF information for the running kernel + /// + /// The member name is automatically determined from the program's section name + /// (e.g., `struct_ops/hid_device_event` -> `hid_device_event`). + pub fn load(&mut self, struct_name: &str, btf: &Btf) -> Result<(), ProgramError> { + let struct_type_id = btf.id_by_type_name_kind(struct_name, BtfKind::Struct)?; + let member_index = btf.struct_member_index(struct_type_id, &self.member_name)?; + + debug!( + "loading struct_ops program member='{}' struct='{struct_name}' member_index={member_index}", + self.member_name + ); + + // For struct_ops, expected_attach_type stores the member index (not a bpf_attach_type). + // SAFETY: While this creates a technically invalid enum value, it is safe because: + // 1. bpf_attach_type is #[repr(u32)] so it has the same memory layout as u32 + // 2. The value is only used by casting back to u32 in bpf_load_program() + // 3. The kernel interprets this field as a raw u32 for struct_ops programs + // A cleaner solution would require adding a separate field to ProgramData. + self.data.expected_attach_type = + Some(unsafe { std::mem::transmute::(member_index) }); + self.data.attach_btf_id = Some(struct_type_id); + load_program(BPF_PROG_TYPE_STRUCT_OPS, &mut self.data) + } + + /// Returns the struct member name this program implements. + pub fn member_name(&self) -> &str { + &self.member_name + } +} + +/// The identifier for a [`StructOpsLink`]. +/// +/// This is returned by [`StructOpsMap::attach`](crate::maps::StructOpsMap::attach). +#[derive(Debug, Hash, Eq, PartialEq)] +pub struct StructOpsLinkId(FdLinkId); + +/// The link used by [`StructOps`] programs. +/// +/// This is created by [`StructOpsMap::attach`](crate::maps::StructOpsMap::attach) +/// after the struct_ops map has been registered. +#[derive(Debug)] +pub struct StructOpsLink(Option); + +#[allow(dead_code)] +impl StructOpsLink { + pub(crate) fn new(base: FdLink) -> Self { + Self(Some(base)) + } + + fn inner(&self) -> &FdLink { + self.0.as_ref().unwrap() + } + + fn into_inner(mut self) -> FdLink { + self.0.take().unwrap() + } +} + +impl Drop for StructOpsLink { + fn drop(&mut self) { + if let Some(base) = self.0.take() { + let _: Result<(), ProgramError> = base.detach(); + } + } +} + +impl Link for StructOpsLink { + type Id = StructOpsLinkId; + + fn id(&self) -> Self::Id { + StructOpsLinkId(self.0.as_ref().unwrap().id()) + } + + fn detach(mut self) -> Result<(), ProgramError> { + self.0.take().unwrap().detach() + } +} + +id_as_key!(StructOpsLink, StructOpsLinkId); + +impl From for StructOpsLink { + fn from(b: FdLink) -> Self { + Self(Some(b)) + } +} + +impl From for FdLink { + fn from(mut w: StructOpsLink) -> Self { + w.0.take().unwrap() + } +} diff --git a/aya/src/programs/syscall.rs b/aya/src/programs/syscall.rs new file mode 100644 index 000000000..e0c054bd1 --- /dev/null +++ b/aya/src/programs/syscall.rs @@ -0,0 +1,82 @@ +//! Syscall programs. +//! +//! BPF_PROG_TYPE_SYSCALL programs can be invoked directly via the bpf() syscall. +//! They are used for various purposes including HID-BPF probe functions. + +use aya_obj::generated::bpf_prog_type::BPF_PROG_TYPE_SYSCALL; + +use crate::programs::{ProgramData, ProgramError, ProgramType, load_program}; + +/// A BPF program that can be invoked directly via the bpf() syscall. +/// +/// Syscall programs are typically used for: +/// - HID-BPF probe functions that determine whether to attach to a device +/// - General-purpose programs invoked via BPF_PROG_RUN +/// +/// # Minimum kernel version +/// +/// The minimum kernel version required to use this feature is 5.14. +/// +/// # Examples +/// +/// ```no_run +/// # #[derive(Debug, thiserror::Error)] +/// # enum Error { +/// # #[error(transparent)] +/// # Program(#[from] aya::programs::ProgramError), +/// # #[error(transparent)] +/// # Ebpf(#[from] aya::EbpfError) +/// # } +/// # let mut bpf = aya::Ebpf::load(&[])?; +/// use aya::programs::Syscall; +/// +/// let prog: &mut Syscall = bpf.program_mut("probe").unwrap().try_into()?; +/// prog.load()?; +/// // The program can now be invoked via bpf(BPF_PROG_RUN, ...) +/// // For HID-BPF, the kernel invokes the probe automatically during struct_ops attachment +/// # Ok::<(), Error>(()) +/// ``` +#[derive(Debug)] +#[doc(alias = "BPF_PROG_TYPE_SYSCALL")] +pub struct Syscall { + pub(crate) data: ProgramData, +} + +impl Syscall { + /// The type of the program according to the kernel. + pub const PROGRAM_TYPE: ProgramType = ProgramType::Syscall; + + /// Loads the program inside the kernel. + pub fn load(&mut self) -> Result<(), ProgramError> { + load_program(BPF_PROG_TYPE_SYSCALL, &mut self.data) + } +} + +/// A link for syscall programs. +/// +/// Syscall programs don't attach to anything in the traditional sense, +/// they are invoked directly. This is a placeholder for API consistency. +#[derive(Debug)] +pub struct SyscallLink { + _private: (), +} + +/// The type returned when detaching syscall links. Syscall programs don't +/// actually attach, so this is a no-op placeholder. +#[derive(Debug, Hash, Eq, PartialEq)] +pub struct SyscallLinkId(()); + +impl crate::programs::Link for SyscallLink { + type Id = SyscallLinkId; + + fn id(&self) -> Self::Id { + SyscallLinkId(()) + } + + fn detach(self) -> Result<(), ProgramError> { + // Syscall programs don't attach to anything + Ok(()) + } +} + +crate::programs::id_as_key!(SyscallLink, SyscallLinkId); diff --git a/aya/src/sys/bpf.rs b/aya/src/sys/bpf.rs index 7a6a0ef28..2319af2e5 100644 --- a/aya/src/sys/bpf.rs +++ b/aya/src/sys/bpf.rs @@ -27,7 +27,7 @@ use libc::{ EBADF, ENOENT, ENOSPC, EPERM, RLIM_INFINITY, RLIMIT_MEMLOCK, getrlimit, rlim_t, rlimit, setrlimit, }; -use log::warn; +use log::{debug, warn}; use crate::{ Btf, Pod, VerifierLogLevel, @@ -51,6 +51,15 @@ pub(crate) fn bpf_create_map( name: &CStr, def: &aya_obj::Map, btf_fd: Option>, +) -> io::Result { + bpf_create_map_with_vmlinux_btf(name, def, btf_fd, None) +} + +pub(crate) fn bpf_create_map_with_vmlinux_btf( + name: &CStr, + def: &aya_obj::Map, + btf_fd: Option>, + btf_vmlinux_value_type_id: Option, ) -> io::Result { let mut attr = unsafe { mem::zeroed::() }; @@ -61,7 +70,18 @@ pub(crate) fn bpf_create_map( u.max_entries = def.max_entries(); u.map_flags = def.map_flags(); - if let aya_obj::Map::Btf(m) = def { + // Handle struct_ops maps - they use btf_vmlinux_value_type_id exclusively. + // When btf_vmlinux_value_type_id is set, btf_value_type_id MUST be 0 (kernel rejects otherwise). + // See kernel/bpf/syscall.c map_create() validation. + // However, btf_fd IS required to resolve struct/function types in the BPF object's BTF. + if let aya_obj::Map::StructOps(_) = def { + if let Some(vmlinux_type_id) = btf_vmlinux_value_type_id { + u.btf_vmlinux_value_type_id = vmlinux_type_id; + } + // btf_value_type_id must be 0 (already zeroed via mem::zeroed) + // btf_fd IS needed for struct_ops to resolve struct/function references + u.btf_fd = btf_fd.map(|fd| fd.as_raw_fd()).unwrap_or_default() as u32; + } else if let aya_obj::Map::Btf(m) = def { use bpf_map_type::*; // Mimic https://github.com/libbpf/libbpf/issues/355 @@ -165,6 +185,13 @@ pub(crate) fn bpf_load_program( } u.insns = aya_attr.insns.as_ptr() as u64; u.insn_cnt = aya_attr.insns.len() as u32; + debug!( + "bpf_load_program: prog_type={} insn_cnt={} expected_attach_type={:?} attach_btf_id={:?}", + u.prog_type, + aya_attr.insns.len(), + aya_attr.expected_attach_type, + aya_attr.attach_btf_id + ); u.license = aya_attr.license.as_ptr() as u64; u.kern_version = aya_attr.kernel_version; @@ -454,6 +481,33 @@ pub(crate) fn bpf_link_create( unsafe { fd_sys_bpf(bpf_cmd::BPF_LINK_CREATE, &mut attr) } } +/// Creates a BPF link for a struct_ops map. +/// +/// For link-based struct_ops (maps created with BPF_F_LINK flag), +/// this activates the struct_ops after bpf_map_update_elem has been called. +/// +/// # Arguments +/// +/// * `map_fd` - The file descriptor of the struct_ops map +/// +/// # Returns +/// +/// A new file descriptor for the BPF link on success. +// since kernel 5.13 (for struct_ops links) +pub(crate) fn bpf_struct_ops_link_create(map_fd: BorrowedFd<'_>) -> io::Result { + let mut attr = unsafe { mem::zeroed::() }; + + // For struct_ops, the kernel reuses prog_fd field for the map_fd + attr.link_create.__bindgen_anon_1.prog_fd = map_fd.as_raw_fd() as u32; + // target_fd is 0 for struct_ops + attr.link_create.__bindgen_anon_2.target_fd = 0; + // BPF_STRUCT_OPS = 44 + attr.link_create.attach_type = bpf_attach_type::BPF_STRUCT_OPS as u32; + + // SAFETY: BPF_LINK_CREATE returns a new file descriptor. + unsafe { fd_sys_bpf(bpf_cmd::BPF_LINK_CREATE, &mut attr) } +} + // since kernel 5.7 pub(crate) fn bpf_link_update( link_fd: BorrowedFd<'_>, diff --git a/ebpf/aya-ebpf/src/programs/mod.rs b/ebpf/aya-ebpf/src/programs/mod.rs index f95b38dee..e50c8b68d 100644 --- a/ebpf/aya-ebpf/src/programs/mod.rs +++ b/ebpf/aya-ebpf/src/programs/mod.rs @@ -14,6 +14,7 @@ pub mod sock; pub mod sock_addr; pub mod sock_ops; pub mod sockopt; +pub mod struct_ops; pub mod sysctl; pub mod tc; pub mod tp_btf; @@ -36,6 +37,7 @@ pub use sock::SockContext; pub use sock_addr::SockAddrContext; pub use sock_ops::SockOpsContext; pub use sockopt::SockoptContext; +pub use struct_ops::StructOpsContext; pub use sysctl::SysctlContext; pub use tc::TcContext; pub use tp_btf::BtfTracePointContext; diff --git a/ebpf/aya-ebpf/src/programs/struct_ops.rs b/ebpf/aya-ebpf/src/programs/struct_ops.rs new file mode 100644 index 000000000..b0bb9c662 --- /dev/null +++ b/ebpf/aya-ebpf/src/programs/struct_ops.rs @@ -0,0 +1,40 @@ +use core::ffi::c_void; + +use crate::{Argument, EbpfContext, args::btf_arg}; + +/// Context provided to struct_ops programs. +/// +/// Struct ops programs implement kernel interfaces and receive context +/// appropriate to the specific callback being invoked. +pub struct StructOpsContext { + ctx: *mut c_void, +} + +impl StructOpsContext { + /// Creates a new context from a raw pointer. + pub fn new(ctx: *mut c_void) -> Self { + Self { ctx } + } + + /// Returns the `n`th argument passed to the struct_ops callback, starting from 0. + /// + /// # Examples + /// + /// ```no_run + /// # use aya_ebpf::programs::StructOpsContext; + /// unsafe fn my_struct_ops_callback(ctx: StructOpsContext) -> i32 { + /// let arg0: u64 = ctx.arg(0); + /// // Process the argument... + /// 0 + /// } + /// ``` + pub fn arg(&self, n: usize) -> T { + btf_arg(self, n) + } +} + +impl EbpfContext for StructOpsContext { + fn as_ptr(&self) -> *mut c_void { + self.ctx + } +} diff --git a/test/integration-ebpf/Cargo.toml b/test/integration-ebpf/Cargo.toml index 33715cfb8..caa153a20 100644 --- a/test/integration-ebpf/Cargo.toml +++ b/test/integration-ebpf/Cargo.toml @@ -107,3 +107,7 @@ path = "src/uprobe_cookie.rs" [[bin]] name = "perf_event_bp" path = "src/perf_event_bp.rs" + +[[bin]] +name = "struct_ops_test" +path = "src/struct_ops_test.rs" diff --git a/test/integration-ebpf/src/struct_ops_test.rs b/test/integration-ebpf/src/struct_ops_test.rs new file mode 100644 index 000000000..3f0aebb2d --- /dev/null +++ b/test/integration-ebpf/src/struct_ops_test.rs @@ -0,0 +1,14 @@ +#![no_std] +#![no_main] +#![expect(unused_crate_dependencies, reason = "used in other bins")] + +use aya_ebpf::{macros::struct_ops, programs::StructOpsContext}; +#[cfg(not(test))] +extern crate ebpf_panic; + +// A simple struct_ops callback that does nothing but return 0. +// This is used to test that struct_ops section parsing works. +#[struct_ops] +pub(crate) fn struct_ops_test_callback(_ctx: StructOpsContext) -> i32 { + 0 +} diff --git a/test/integration-test/src/lib.rs b/test/integration-test/src/lib.rs index 01e26f03a..306ba2e31 100644 --- a/test/integration-test/src/lib.rs +++ b/test/integration-test/src/lib.rs @@ -59,6 +59,7 @@ bpf_file!( TWO_PROGS => "two_progs", XDP_SEC => "xdp_sec", UPROBE_COOKIE => "uprobe_cookie", + STRUCT_OPS_TEST => "struct_ops_test", ); #[cfg(test)] diff --git a/test/integration-test/src/tests.rs b/test/integration-test/src/tests.rs index 4de831aad..6c6544fa5 100644 --- a/test/integration-test/src/tests.rs +++ b/test/integration-test/src/tests.rs @@ -19,6 +19,7 @@ mod ring_buf; mod sk_storage; mod smoke; mod strncmp; +mod struct_ops; mod tcx; mod uprobe_cookie; mod xdp; diff --git a/test/integration-test/src/tests/rbpf.rs b/test/integration-test/src/tests/rbpf.rs index faa7a0349..93cfe2b8f 100644 --- a/test/integration-test/src/tests/rbpf.rs +++ b/test/integration-test/src/tests/rbpf.rs @@ -91,7 +91,7 @@ fn use_map_with_rbpf() { ) .expect("Relocation failed"); // Actually there is no local function call involved. - object.relocate_calls(&text_sections).unwrap(); + object.relocate_calls(&text_sections, None).unwrap(); // Executes the program assert_eq!(object.programs.len(), 1); diff --git a/test/integration-test/src/tests/struct_ops.rs b/test/integration-test/src/tests/struct_ops.rs new file mode 100644 index 000000000..04c546d09 --- /dev/null +++ b/test/integration-test/src/tests/struct_ops.rs @@ -0,0 +1,43 @@ +use aya::{Ebpf, programs::StructOps}; + +/// Test that struct_ops section parsing works correctly. +/// +/// This test verifies that: +/// 1. The struct_ops section is recognized and parsed +/// 2. The program can be retrieved as a StructOps type +/// +/// Note: Actually loading and attaching struct_ops programs requires +/// additional kernel support and a properly defined struct_ops map, +/// which is more complex to test. +#[test_log::test] +fn struct_ops_parse() { + let mut ebpf = Ebpf::load(crate::STRUCT_OPS_TEST).unwrap(); + + // Verify we can get the program as a StructOps type + let prog: &mut StructOps = ebpf + .program_mut("struct_ops_test_callback") + .expect("program not found") + .try_into() + .expect("wrong program type"); + + // The program should exist but we can't easily load it without + // a proper struct_ops map definition. + // This test at least verifies the parsing works. + let _ = prog; +} + +/// Test that struct_ops program info is correct. +#[test_log::test] +fn struct_ops_program_type() { + let ebpf = Ebpf::load(crate::STRUCT_OPS_TEST).unwrap(); + + let prog = ebpf + .program("struct_ops_test_callback") + .expect("program not found"); + + // Verify the program type is StructOps + assert!( + matches!(prog.prog_type(), aya::programs::ProgramType::StructOps), + "expected StructOps program type" + ); +} diff --git a/xtask/public-api/aya-ebpf-macros.txt b/xtask/public-api/aya-ebpf-macros.txt index c0e002411..b4fbb546b 100644 --- a/xtask/public-api/aya-ebpf-macros.txt +++ b/xtask/public-api/aya-ebpf-macros.txt @@ -24,6 +24,7 @@ pub proc macro aya_ebpf_macros::#[sock_ops] pub proc macro aya_ebpf_macros::#[socket_filter] pub proc macro aya_ebpf_macros::#[stream_parser] pub proc macro aya_ebpf_macros::#[stream_verdict] +pub proc macro aya_ebpf_macros::#[struct_ops] pub proc macro aya_ebpf_macros::#[tracepoint] pub proc macro aya_ebpf_macros::#[uprobe] pub proc macro aya_ebpf_macros::#[uretprobe] diff --git a/xtask/public-api/aya-ebpf.txt b/xtask/public-api/aya-ebpf.txt index e14196ca5..20f109760 100644 --- a/xtask/public-api/aya-ebpf.txt +++ b/xtask/public-api/aya-ebpf.txt @@ -2208,6 +2208,35 @@ impl core::borrow::BorrowMut for aya_ebpf::programs::sockopt::SockoptConte pub fn aya_ebpf::programs::sockopt::SockoptContext::borrow_mut(&mut self) -> &mut T impl core::convert::From for aya_ebpf::programs::sockopt::SockoptContext pub fn aya_ebpf::programs::sockopt::SockoptContext::from(t: T) -> T +pub mod aya_ebpf::programs::struct_ops +pub struct aya_ebpf::programs::struct_ops::StructOpsContext +impl aya_ebpf::programs::struct_ops::StructOpsContext +pub fn aya_ebpf::programs::struct_ops::StructOpsContext::arg(&self, n: usize) -> T +pub fn aya_ebpf::programs::struct_ops::StructOpsContext::new(ctx: *mut core::ffi::c_void) -> Self +impl aya_ebpf::EbpfContext for aya_ebpf::programs::struct_ops::StructOpsContext +pub fn aya_ebpf::programs::struct_ops::StructOpsContext::as_ptr(&self) -> *mut core::ffi::c_void +impl core::marker::Freeze for aya_ebpf::programs::struct_ops::StructOpsContext +impl !core::marker::Send for aya_ebpf::programs::struct_ops::StructOpsContext +impl !core::marker::Sync for aya_ebpf::programs::struct_ops::StructOpsContext +impl core::marker::Unpin for aya_ebpf::programs::struct_ops::StructOpsContext +impl core::panic::unwind_safe::RefUnwindSafe for aya_ebpf::programs::struct_ops::StructOpsContext +impl core::panic::unwind_safe::UnwindSafe for aya_ebpf::programs::struct_ops::StructOpsContext +impl core::convert::Into for aya_ebpf::programs::struct_ops::StructOpsContext where U: core::convert::From +pub fn aya_ebpf::programs::struct_ops::StructOpsContext::into(self) -> U +impl core::convert::TryFrom for aya_ebpf::programs::struct_ops::StructOpsContext where U: core::convert::Into +pub type aya_ebpf::programs::struct_ops::StructOpsContext::Error = core::convert::Infallible +pub fn aya_ebpf::programs::struct_ops::StructOpsContext::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya_ebpf::programs::struct_ops::StructOpsContext where U: core::convert::TryFrom +pub type aya_ebpf::programs::struct_ops::StructOpsContext::Error = >::Error +pub fn aya_ebpf::programs::struct_ops::StructOpsContext::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya_ebpf::programs::struct_ops::StructOpsContext where T: 'static + ?core::marker::Sized +pub fn aya_ebpf::programs::struct_ops::StructOpsContext::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya_ebpf::programs::struct_ops::StructOpsContext where T: ?core::marker::Sized +pub fn aya_ebpf::programs::struct_ops::StructOpsContext::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya_ebpf::programs::struct_ops::StructOpsContext where T: ?core::marker::Sized +pub fn aya_ebpf::programs::struct_ops::StructOpsContext::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya_ebpf::programs::struct_ops::StructOpsContext +pub fn aya_ebpf::programs::struct_ops::StructOpsContext::from(t: T) -> T pub mod aya_ebpf::programs::sysctl pub struct aya_ebpf::programs::sysctl::SysctlContext pub aya_ebpf::programs::sysctl::SysctlContext::sysctl: *mut aya_ebpf_bindings::x86_64::bindings::bpf_sysctl @@ -2888,6 +2917,34 @@ impl core::borrow::BorrowMut for aya_ebpf::programs::sockopt::SockoptConte pub fn aya_ebpf::programs::sockopt::SockoptContext::borrow_mut(&mut self) -> &mut T impl core::convert::From for aya_ebpf::programs::sockopt::SockoptContext pub fn aya_ebpf::programs::sockopt::SockoptContext::from(t: T) -> T +pub struct aya_ebpf::programs::StructOpsContext +impl aya_ebpf::programs::struct_ops::StructOpsContext +pub fn aya_ebpf::programs::struct_ops::StructOpsContext::arg(&self, n: usize) -> T +pub fn aya_ebpf::programs::struct_ops::StructOpsContext::new(ctx: *mut core::ffi::c_void) -> Self +impl aya_ebpf::EbpfContext for aya_ebpf::programs::struct_ops::StructOpsContext +pub fn aya_ebpf::programs::struct_ops::StructOpsContext::as_ptr(&self) -> *mut core::ffi::c_void +impl core::marker::Freeze for aya_ebpf::programs::struct_ops::StructOpsContext +impl !core::marker::Send for aya_ebpf::programs::struct_ops::StructOpsContext +impl !core::marker::Sync for aya_ebpf::programs::struct_ops::StructOpsContext +impl core::marker::Unpin for aya_ebpf::programs::struct_ops::StructOpsContext +impl core::panic::unwind_safe::RefUnwindSafe for aya_ebpf::programs::struct_ops::StructOpsContext +impl core::panic::unwind_safe::UnwindSafe for aya_ebpf::programs::struct_ops::StructOpsContext +impl core::convert::Into for aya_ebpf::programs::struct_ops::StructOpsContext where U: core::convert::From +pub fn aya_ebpf::programs::struct_ops::StructOpsContext::into(self) -> U +impl core::convert::TryFrom for aya_ebpf::programs::struct_ops::StructOpsContext where U: core::convert::Into +pub type aya_ebpf::programs::struct_ops::StructOpsContext::Error = core::convert::Infallible +pub fn aya_ebpf::programs::struct_ops::StructOpsContext::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya_ebpf::programs::struct_ops::StructOpsContext where U: core::convert::TryFrom +pub type aya_ebpf::programs::struct_ops::StructOpsContext::Error = >::Error +pub fn aya_ebpf::programs::struct_ops::StructOpsContext::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya_ebpf::programs::struct_ops::StructOpsContext where T: 'static + ?core::marker::Sized +pub fn aya_ebpf::programs::struct_ops::StructOpsContext::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya_ebpf::programs::struct_ops::StructOpsContext where T: ?core::marker::Sized +pub fn aya_ebpf::programs::struct_ops::StructOpsContext::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya_ebpf::programs::struct_ops::StructOpsContext where T: ?core::marker::Sized +pub fn aya_ebpf::programs::struct_ops::StructOpsContext::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya_ebpf::programs::struct_ops::StructOpsContext +pub fn aya_ebpf::programs::struct_ops::StructOpsContext::from(t: T) -> T pub struct aya_ebpf::programs::SysctlContext pub aya_ebpf::programs::SysctlContext::sysctl: *mut aya_ebpf_bindings::x86_64::bindings::bpf_sysctl impl aya_ebpf::programs::sysctl::SysctlContext @@ -3067,6 +3124,8 @@ impl aya_ebpf::EbpfContext for aya_ebpf::programs::sock_ops::SockOpsContext pub fn aya_ebpf::programs::sock_ops::SockOpsContext::as_ptr(&self) -> *mut core::ffi::c_void impl aya_ebpf::EbpfContext for aya_ebpf::programs::sockopt::SockoptContext pub fn aya_ebpf::programs::sockopt::SockoptContext::as_ptr(&self) -> *mut core::ffi::c_void +impl aya_ebpf::EbpfContext for aya_ebpf::programs::struct_ops::StructOpsContext +pub fn aya_ebpf::programs::struct_ops::StructOpsContext::as_ptr(&self) -> *mut core::ffi::c_void impl aya_ebpf::EbpfContext for aya_ebpf::programs::sysctl::SysctlContext pub fn aya_ebpf::programs::sysctl::SysctlContext::as_ptr(&self) -> *mut core::ffi::c_void impl aya_ebpf::EbpfContext for aya_ebpf::programs::tc::TcContext diff --git a/xtask/public-api/aya-obj.txt b/xtask/public-api/aya-obj.txt index 5e59c5ea2..bb98d50da 100644 --- a/xtask/public-api/aya-obj.txt +++ b/xtask/public-api/aya-obj.txt @@ -363,7 +363,10 @@ pub fn aya_obj::btf::Btf::id_by_type_name_kind(&self, name: &str, kind: aya_obj: pub fn aya_obj::btf::Btf::new() -> Self pub fn aya_obj::btf::Btf::parse(data: &[u8], endianness: object::endian::Endianness) -> core::result::Result pub fn aya_obj::btf::Btf::parse_file>(path: P, endianness: object::endian::Endianness) -> core::result::Result +pub fn aya_obj::btf::Btf::struct_member_byte_offset(&self, struct_type_id: u32, member_name: &str) -> core::result::Result +pub fn aya_obj::btf::Btf::struct_member_index(&self, struct_type_id: u32, member_name: &str) -> core::result::Result pub fn aya_obj::btf::Btf::to_bytes(&self) -> alloc::vec::Vec +pub fn aya_obj::btf::Btf::type_size(&self, root_type_id: u32) -> core::result::Result impl core::clone::Clone for aya_obj::btf::Btf pub fn aya_obj::btf::Btf::clone(&self) -> aya_obj::btf::Btf impl core::default::Default for aya_obj::btf::Btf @@ -8202,7 +8205,9 @@ pub mod aya_obj::maps pub enum aya_obj::maps::Map pub aya_obj::maps::Map::Btf(aya_obj::maps::BtfMap) pub aya_obj::maps::Map::Legacy(aya_obj::maps::LegacyMap) +pub aya_obj::maps::Map::StructOps(aya_obj::maps::StructOpsMap) impl aya_obj::maps::Map +pub fn aya_obj::maps::Map::btf_value_type_id(&self) -> core::option::Option pub fn aya_obj::maps::Map::data(&self) -> &[u8] pub fn aya_obj::maps::Map::data_mut(&mut self) -> &mut alloc::vec::Vec pub fn aya_obj::maps::Map::key_size(&self) -> u32 @@ -8439,6 +8444,83 @@ impl core::clone::CloneToUninit for aya_obj::maps::LegacyMap where T: core::c pub unsafe fn aya_obj::maps::LegacyMap::clone_to_uninit(&self, dest: *mut u8) impl core::convert::From for aya_obj::maps::LegacyMap pub fn aya_obj::maps::LegacyMap::from(t: T) -> T +pub struct aya_obj::maps::StructOpsFuncInfo +pub aya_obj::maps::StructOpsFuncInfo::member_name: alloc::string::String +pub aya_obj::maps::StructOpsFuncInfo::member_offset: u32 +pub aya_obj::maps::StructOpsFuncInfo::prog_name: alloc::string::String +impl core::clone::Clone for aya_obj::maps::StructOpsFuncInfo +pub fn aya_obj::maps::StructOpsFuncInfo::clone(&self) -> aya_obj::maps::StructOpsFuncInfo +impl core::fmt::Debug for aya_obj::maps::StructOpsFuncInfo +pub fn aya_obj::maps::StructOpsFuncInfo::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::marker::Freeze for aya_obj::maps::StructOpsFuncInfo +impl core::marker::Send for aya_obj::maps::StructOpsFuncInfo +impl core::marker::Sync for aya_obj::maps::StructOpsFuncInfo +impl core::marker::Unpin for aya_obj::maps::StructOpsFuncInfo +impl core::panic::unwind_safe::RefUnwindSafe for aya_obj::maps::StructOpsFuncInfo +impl core::panic::unwind_safe::UnwindSafe for aya_obj::maps::StructOpsFuncInfo +impl core::convert::Into for aya_obj::maps::StructOpsFuncInfo where U: core::convert::From +pub fn aya_obj::maps::StructOpsFuncInfo::into(self) -> U +impl core::convert::TryFrom for aya_obj::maps::StructOpsFuncInfo where U: core::convert::Into +pub type aya_obj::maps::StructOpsFuncInfo::Error = core::convert::Infallible +pub fn aya_obj::maps::StructOpsFuncInfo::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya_obj::maps::StructOpsFuncInfo where U: core::convert::TryFrom +pub type aya_obj::maps::StructOpsFuncInfo::Error = >::Error +pub fn aya_obj::maps::StructOpsFuncInfo::try_into(self) -> core::result::Result>::Error> +impl alloc::borrow::ToOwned for aya_obj::maps::StructOpsFuncInfo where T: core::clone::Clone +pub type aya_obj::maps::StructOpsFuncInfo::Owned = T +pub fn aya_obj::maps::StructOpsFuncInfo::clone_into(&self, target: &mut T) +pub fn aya_obj::maps::StructOpsFuncInfo::to_owned(&self) -> T +impl core::any::Any for aya_obj::maps::StructOpsFuncInfo where T: 'static + ?core::marker::Sized +pub fn aya_obj::maps::StructOpsFuncInfo::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya_obj::maps::StructOpsFuncInfo where T: ?core::marker::Sized +pub fn aya_obj::maps::StructOpsFuncInfo::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya_obj::maps::StructOpsFuncInfo where T: ?core::marker::Sized +pub fn aya_obj::maps::StructOpsFuncInfo::borrow_mut(&mut self) -> &mut T +impl core::clone::CloneToUninit for aya_obj::maps::StructOpsFuncInfo where T: core::clone::Clone +pub unsafe fn aya_obj::maps::StructOpsFuncInfo::clone_to_uninit(&self, dest: *mut u8) +impl core::convert::From for aya_obj::maps::StructOpsFuncInfo +pub fn aya_obj::maps::StructOpsFuncInfo::from(t: T) -> T +pub struct aya_obj::maps::StructOpsMap +pub aya_obj::maps::StructOpsMap::btf_type_id: u32 +pub aya_obj::maps::StructOpsMap::data: alloc::vec::Vec +pub aya_obj::maps::StructOpsMap::data_offset: u32 +pub aya_obj::maps::StructOpsMap::func_info: alloc::vec::Vec +pub aya_obj::maps::StructOpsMap::is_link: bool +pub aya_obj::maps::StructOpsMap::section_index: usize +pub aya_obj::maps::StructOpsMap::symbol_index: usize +pub aya_obj::maps::StructOpsMap::type_name: alloc::string::String +impl core::clone::Clone for aya_obj::maps::StructOpsMap +pub fn aya_obj::maps::StructOpsMap::clone(&self) -> aya_obj::maps::StructOpsMap +impl core::fmt::Debug for aya_obj::maps::StructOpsMap +pub fn aya_obj::maps::StructOpsMap::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::marker::Freeze for aya_obj::maps::StructOpsMap +impl core::marker::Send for aya_obj::maps::StructOpsMap +impl core::marker::Sync for aya_obj::maps::StructOpsMap +impl core::marker::Unpin for aya_obj::maps::StructOpsMap +impl core::panic::unwind_safe::RefUnwindSafe for aya_obj::maps::StructOpsMap +impl core::panic::unwind_safe::UnwindSafe for aya_obj::maps::StructOpsMap +impl core::convert::Into for aya_obj::maps::StructOpsMap where U: core::convert::From +pub fn aya_obj::maps::StructOpsMap::into(self) -> U +impl core::convert::TryFrom for aya_obj::maps::StructOpsMap where U: core::convert::Into +pub type aya_obj::maps::StructOpsMap::Error = core::convert::Infallible +pub fn aya_obj::maps::StructOpsMap::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya_obj::maps::StructOpsMap where U: core::convert::TryFrom +pub type aya_obj::maps::StructOpsMap::Error = >::Error +pub fn aya_obj::maps::StructOpsMap::try_into(self) -> core::result::Result>::Error> +impl alloc::borrow::ToOwned for aya_obj::maps::StructOpsMap where T: core::clone::Clone +pub type aya_obj::maps::StructOpsMap::Owned = T +pub fn aya_obj::maps::StructOpsMap::clone_into(&self, target: &mut T) +pub fn aya_obj::maps::StructOpsMap::to_owned(&self) -> T +impl core::any::Any for aya_obj::maps::StructOpsMap where T: 'static + ?core::marker::Sized +pub fn aya_obj::maps::StructOpsMap::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya_obj::maps::StructOpsMap where T: ?core::marker::Sized +pub fn aya_obj::maps::StructOpsMap::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya_obj::maps::StructOpsMap where T: ?core::marker::Sized +pub fn aya_obj::maps::StructOpsMap::borrow_mut(&mut self) -> &mut T +impl core::clone::CloneToUninit for aya_obj::maps::StructOpsMap where T: core::clone::Clone +pub unsafe fn aya_obj::maps::StructOpsMap::clone_to_uninit(&self, dest: *mut u8) +impl core::convert::From for aya_obj::maps::StructOpsMap +pub fn aya_obj::maps::StructOpsMap::from(t: T) -> T #[repr(C)] pub struct aya_obj::maps::bpf_map_def pub aya_obj::maps::bpf_map_def::id: u32 pub aya_obj::maps::bpf_map_def::key_size: u32 @@ -8493,10 +8575,13 @@ pub aya_obj::obj::EbpfSectionKind::Btf pub aya_obj::obj::EbpfSectionKind::BtfExt pub aya_obj::obj::EbpfSectionKind::BtfMaps pub aya_obj::obj::EbpfSectionKind::Data +pub aya_obj::obj::EbpfSectionKind::Ksyms pub aya_obj::obj::EbpfSectionKind::License pub aya_obj::obj::EbpfSectionKind::Maps pub aya_obj::obj::EbpfSectionKind::Program pub aya_obj::obj::EbpfSectionKind::Rodata +pub aya_obj::obj::EbpfSectionKind::StructOps +pub aya_obj::obj::EbpfSectionKind::StructOpsLink pub aya_obj::obj::EbpfSectionKind::Text pub aya_obj::obj::EbpfSectionKind::Undefined pub aya_obj::obj::EbpfSectionKind::Version @@ -8645,6 +8730,10 @@ pub aya_obj::obj::ProgramSection::SkSkbStreamParser pub aya_obj::obj::ProgramSection::SkSkbStreamVerdict pub aya_obj::obj::ProgramSection::SockOps pub aya_obj::obj::ProgramSection::SocketFilter +pub aya_obj::obj::ProgramSection::StructOps +pub aya_obj::obj::ProgramSection::StructOps::member_name: alloc::string::String +pub aya_obj::obj::ProgramSection::StructOps::sleepable: bool +pub aya_obj::obj::ProgramSection::Syscall pub aya_obj::obj::ProgramSection::TracePoint pub aya_obj::obj::ProgramSection::UProbe pub aya_obj::obj::ProgramSection::UProbe::sleepable: bool @@ -8809,7 +8898,7 @@ pub fn aya_obj::Object::sanitize_functions(&mut self, features: &aya_obj::Featur impl aya_obj::Object pub fn aya_obj::Object::relocate_btf(&mut self, target_btf: &aya_obj::btf::Btf) -> core::result::Result<(), aya_obj::btf::BtfRelocationError> impl aya_obj::Object -pub fn aya_obj::Object::relocate_calls(&mut self, text_sections: &std::collections::hash::set::HashSet) -> core::result::Result<(), aya_obj::relocation::EbpfRelocationError> +pub fn aya_obj::Object::relocate_calls(&mut self, text_sections: &std::collections::hash::set::HashSet, kernel_btf: core::option::Option<&aya_obj::btf::Btf>) -> core::result::Result<(), aya_obj::relocation::EbpfRelocationError> pub fn aya_obj::Object::relocate_maps<'a, I: core::iter::traits::iterator::Iterator>(&mut self, maps: I, text_sections: &std::collections::hash::set::HashSet) -> core::result::Result<(), aya_obj::relocation::EbpfRelocationError> impl core::clone::Clone for aya_obj::Object pub fn aya_obj::Object::clone(&self) -> aya_obj::Object @@ -9307,10 +9396,13 @@ pub aya_obj::EbpfSectionKind::Btf pub aya_obj::EbpfSectionKind::BtfExt pub aya_obj::EbpfSectionKind::BtfMaps pub aya_obj::EbpfSectionKind::Data +pub aya_obj::EbpfSectionKind::Ksyms pub aya_obj::EbpfSectionKind::License pub aya_obj::EbpfSectionKind::Maps pub aya_obj::EbpfSectionKind::Program pub aya_obj::EbpfSectionKind::Rodata +pub aya_obj::EbpfSectionKind::StructOps +pub aya_obj::EbpfSectionKind::StructOpsLink pub aya_obj::EbpfSectionKind::Text pub aya_obj::EbpfSectionKind::Undefined pub aya_obj::EbpfSectionKind::Version @@ -9354,7 +9446,9 @@ pub fn aya_obj::EbpfSectionKind::from(t: T) -> T pub enum aya_obj::Map pub aya_obj::Map::Btf(aya_obj::maps::BtfMap) pub aya_obj::Map::Legacy(aya_obj::maps::LegacyMap) +pub aya_obj::Map::StructOps(aya_obj::maps::StructOpsMap) impl aya_obj::maps::Map +pub fn aya_obj::maps::Map::btf_value_type_id(&self) -> core::option::Option pub fn aya_obj::maps::Map::data(&self) -> &[u8] pub fn aya_obj::maps::Map::data_mut(&mut self) -> &mut alloc::vec::Vec pub fn aya_obj::maps::Map::key_size(&self) -> u32 @@ -9508,6 +9602,10 @@ pub aya_obj::ProgramSection::SkSkbStreamParser pub aya_obj::ProgramSection::SkSkbStreamVerdict pub aya_obj::ProgramSection::SockOps pub aya_obj::ProgramSection::SocketFilter +pub aya_obj::ProgramSection::StructOps +pub aya_obj::ProgramSection::StructOps::member_name: alloc::string::String +pub aya_obj::ProgramSection::StructOps::sleepable: bool +pub aya_obj::ProgramSection::Syscall pub aya_obj::ProgramSection::TracePoint pub aya_obj::ProgramSection::UProbe pub aya_obj::ProgramSection::UProbe::sleepable: bool @@ -9672,7 +9770,7 @@ pub fn aya_obj::Object::sanitize_functions(&mut self, features: &aya_obj::Featur impl aya_obj::Object pub fn aya_obj::Object::relocate_btf(&mut self, target_btf: &aya_obj::btf::Btf) -> core::result::Result<(), aya_obj::btf::BtfRelocationError> impl aya_obj::Object -pub fn aya_obj::Object::relocate_calls(&mut self, text_sections: &std::collections::hash::set::HashSet) -> core::result::Result<(), aya_obj::relocation::EbpfRelocationError> +pub fn aya_obj::Object::relocate_calls(&mut self, text_sections: &std::collections::hash::set::HashSet, kernel_btf: core::option::Option<&aya_obj::btf::Btf>) -> core::result::Result<(), aya_obj::relocation::EbpfRelocationError> pub fn aya_obj::Object::relocate_maps<'a, I: core::iter::traits::iterator::Iterator>(&mut self, maps: I, text_sections: &std::collections::hash::set::HashSet) -> core::result::Result<(), aya_obj::relocation::EbpfRelocationError> impl core::clone::Clone for aya_obj::Object pub fn aya_obj::Object::clone(&self) -> aya_obj::Object diff --git a/xtask/public-api/aya.txt b/xtask/public-api/aya.txt index 0ddcea1be..90608724b 100644 --- a/xtask/public-api/aya.txt +++ b/xtask/public-api/aya.txt @@ -904,6 +904,57 @@ impl core::borrow::BorrowMut for aya::maps::stack_trace::StackTraceMap pub fn aya::maps::stack_trace::StackTraceMap::borrow_mut(&mut self) -> &mut T impl core::convert::From for aya::maps::stack_trace::StackTraceMap pub fn aya::maps::stack_trace::StackTraceMap::from(t: T) -> T +pub mod aya::maps::struct_ops +pub struct aya::maps::struct_ops::StructOpsMap +impl> aya::maps::struct_ops::StructOpsMap +pub fn aya::maps::struct_ops::StructOpsMap::attach(&self) -> core::result::Result +pub fn aya::maps::struct_ops::StructOpsMap::register(&self) -> core::result::Result<(), aya::maps::MapError> +impl> aya::maps::struct_ops::StructOpsMap +pub fn aya::maps::struct_ops::StructOpsMap::fd(&self) -> &aya::maps::MapFd +pub fn aya::maps::struct_ops::StructOpsMap::func_info(&self) -> core::option::Option<&[aya_obj::maps::StructOpsFuncInfo]> +pub fn aya::maps::struct_ops::StructOpsMap::is_link(&self) -> bool +pub fn aya::maps::struct_ops::StructOpsMap::type_name(&self) -> core::option::Option<&str> +impl> aya::maps::struct_ops::StructOpsMap +pub fn aya::maps::struct_ops::StructOpsMap::pin>(self, path: P) -> core::result::Result<(), aya::pin::PinError> +impl> aya::maps::struct_ops::StructOpsMap +pub fn aya::maps::struct_ops::StructOpsMap::set_field_i32(&mut self, offset: u32, value: i32) -> core::result::Result<(), aya::maps::MapError> +pub fn aya::maps::struct_ops::StructOpsMap::set_field_u32(&mut self, offset: u32, value: u32) -> core::result::Result<(), aya::maps::MapError> +impl core::convert::TryFrom for aya::maps::struct_ops::StructOpsMap +pub type aya::maps::struct_ops::StructOpsMap::Error = aya::maps::MapError +pub fn aya::maps::struct_ops::StructOpsMap::try_from(map: aya::maps::Map) -> core::result::Result +impl core::convert::TryFrom for aya::maps::struct_ops::StructOpsMap +pub type aya::maps::struct_ops::StructOpsMap::Error = aya::maps::MapError +pub fn aya::maps::struct_ops::StructOpsMap::try_from(map: aya::maps::MapData) -> core::result::Result +impl<'a> core::convert::TryFrom<&'a aya::maps::Map> for aya::maps::struct_ops::StructOpsMap<&'a aya::maps::MapData> +pub type aya::maps::struct_ops::StructOpsMap<&'a aya::maps::MapData>::Error = aya::maps::MapError +pub fn aya::maps::struct_ops::StructOpsMap<&'a aya::maps::MapData>::try_from(map: &'a aya::maps::Map) -> core::result::Result +impl<'a> core::convert::TryFrom<&'a mut aya::maps::Map> for aya::maps::struct_ops::StructOpsMap<&'a mut aya::maps::MapData> +pub type aya::maps::struct_ops::StructOpsMap<&'a mut aya::maps::MapData>::Error = aya::maps::MapError +pub fn aya::maps::struct_ops::StructOpsMap<&'a mut aya::maps::MapData>::try_from(map: &'a mut aya::maps::Map) -> core::result::Result +impl core::fmt::Debug for aya::maps::struct_ops::StructOpsMap +pub fn aya::maps::struct_ops::StructOpsMap::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::marker::Freeze for aya::maps::struct_ops::StructOpsMap where T: core::marker::Freeze +impl core::marker::Send for aya::maps::struct_ops::StructOpsMap where T: core::marker::Send +impl core::marker::Sync for aya::maps::struct_ops::StructOpsMap where T: core::marker::Sync +impl core::marker::Unpin for aya::maps::struct_ops::StructOpsMap where T: core::marker::Unpin +impl core::panic::unwind_safe::RefUnwindSafe for aya::maps::struct_ops::StructOpsMap where T: core::panic::unwind_safe::RefUnwindSafe +impl core::panic::unwind_safe::UnwindSafe for aya::maps::struct_ops::StructOpsMap where T: core::panic::unwind_safe::UnwindSafe +impl core::convert::Into for aya::maps::struct_ops::StructOpsMap where U: core::convert::From +pub fn aya::maps::struct_ops::StructOpsMap::into(self) -> U +impl core::convert::TryFrom for aya::maps::struct_ops::StructOpsMap where U: core::convert::Into +pub type aya::maps::struct_ops::StructOpsMap::Error = core::convert::Infallible +pub fn aya::maps::struct_ops::StructOpsMap::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya::maps::struct_ops::StructOpsMap where U: core::convert::TryFrom +pub type aya::maps::struct_ops::StructOpsMap::Error = >::Error +pub fn aya::maps::struct_ops::StructOpsMap::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya::maps::struct_ops::StructOpsMap where T: 'static + ?core::marker::Sized +pub fn aya::maps::struct_ops::StructOpsMap::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya::maps::struct_ops::StructOpsMap where T: ?core::marker::Sized +pub fn aya::maps::struct_ops::StructOpsMap::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya::maps::struct_ops::StructOpsMap where T: ?core::marker::Sized +pub fn aya::maps::struct_ops::StructOpsMap::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya::maps::struct_ops::StructOpsMap +pub fn aya::maps::struct_ops::StructOpsMap::from(t: T) -> T pub mod aya::maps::xdp pub enum aya::maps::xdp::XdpMapError pub aya::maps::xdp::XdpMapError::ChainedProgramNotSupported @@ -1121,6 +1172,7 @@ pub aya::maps::Map::SockHash(aya::maps::MapData) pub aya::maps::Map::SockMap(aya::maps::MapData) pub aya::maps::Map::Stack(aya::maps::MapData) pub aya::maps::Map::StackTraceMap(aya::maps::MapData) +pub aya::maps::Map::StructOps(aya::maps::MapData) pub aya::maps::Map::Unsupported(aya::maps::MapData) pub aya::maps::Map::XskMap(aya::maps::MapData) impl aya::maps::Map @@ -1153,6 +1205,9 @@ pub fn aya::maps::ring_buf::RingBuf::try_from(map: aya::maps impl core::convert::TryFrom for aya::maps::stack_trace::StackTraceMap pub type aya::maps::stack_trace::StackTraceMap::Error = aya::maps::MapError pub fn aya::maps::stack_trace::StackTraceMap::try_from(map: aya::maps::Map) -> core::result::Result +impl core::convert::TryFrom for aya::maps::struct_ops::StructOpsMap +pub type aya::maps::struct_ops::StructOpsMap::Error = aya::maps::MapError +pub fn aya::maps::struct_ops::StructOpsMap::try_from(map: aya::maps::Map) -> core::result::Result impl core::fmt::Debug for aya::maps::Map pub fn aya::maps::Map::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result impl<'a, K: aya::Pod, V: aya::Pod> core::convert::TryFrom<&'a aya::maps::Map> for aya::maps::hash_map::HashMap<&'a aya::maps::MapData, K, V> @@ -1242,6 +1297,9 @@ pub fn aya::maps::ring_buf::RingBuf<&'a aya::maps::MapData>::try_from(map: &'a a impl<'a> core::convert::TryFrom<&'a aya::maps::Map> for aya::maps::stack_trace::StackTraceMap<&'a aya::maps::MapData> pub type aya::maps::stack_trace::StackTraceMap<&'a aya::maps::MapData>::Error = aya::maps::MapError pub fn aya::maps::stack_trace::StackTraceMap<&'a aya::maps::MapData>::try_from(map: &'a aya::maps::Map) -> core::result::Result +impl<'a> core::convert::TryFrom<&'a aya::maps::Map> for aya::maps::struct_ops::StructOpsMap<&'a aya::maps::MapData> +pub type aya::maps::struct_ops::StructOpsMap<&'a aya::maps::MapData>::Error = aya::maps::MapError +pub fn aya::maps::struct_ops::StructOpsMap<&'a aya::maps::MapData>::try_from(map: &'a aya::maps::Map) -> core::result::Result impl<'a> core::convert::TryFrom<&'a mut aya::maps::Map> for aya::maps::CpuMap<&'a mut aya::maps::MapData> pub type aya::maps::CpuMap<&'a mut aya::maps::MapData>::Error = aya::maps::MapError pub fn aya::maps::CpuMap<&'a mut aya::maps::MapData>::try_from(map: &'a mut aya::maps::Map) -> core::result::Result @@ -1269,6 +1327,9 @@ pub fn aya::maps::ring_buf::RingBuf<&'a mut aya::maps::MapData>::try_from(map: & impl<'a> core::convert::TryFrom<&'a mut aya::maps::Map> for aya::maps::stack_trace::StackTraceMap<&'a mut aya::maps::MapData> pub type aya::maps::stack_trace::StackTraceMap<&'a mut aya::maps::MapData>::Error = aya::maps::MapError pub fn aya::maps::stack_trace::StackTraceMap<&'a mut aya::maps::MapData>::try_from(map: &'a mut aya::maps::Map) -> core::result::Result +impl<'a> core::convert::TryFrom<&'a mut aya::maps::Map> for aya::maps::struct_ops::StructOpsMap<&'a mut aya::maps::MapData> +pub type aya::maps::struct_ops::StructOpsMap<&'a mut aya::maps::MapData>::Error = aya::maps::MapError +pub fn aya::maps::struct_ops::StructOpsMap<&'a mut aya::maps::MapData>::try_from(map: &'a mut aya::maps::Map) -> core::result::Result impl core::convert::TryFrom for aya::maps::hash_map::HashMap pub type aya::maps::hash_map::HashMap::Error = aya::maps::MapError pub fn aya::maps::hash_map::HashMap::try_from(map: aya::maps::Map) -> core::result::Result @@ -1771,6 +1832,9 @@ pub fn aya::maps::MapData::from_id(id: u32) -> core::result::Result>(path: P) -> core::result::Result pub fn aya::maps::MapData::info(&self) -> core::result::Result pub fn aya::maps::MapData::pin>(&self, path: P) -> core::result::Result<(), aya::pin::PinError> +impl core::convert::TryFrom for aya::maps::struct_ops::StructOpsMap +pub type aya::maps::struct_ops::StructOpsMap::Error = aya::maps::MapError +pub fn aya::maps::struct_ops::StructOpsMap::try_from(map: aya::maps::MapData) -> core::result::Result impl core::fmt::Debug for aya::maps::MapData pub fn aya::maps::MapData::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result impl core::marker::Freeze for aya::maps::MapData @@ -2411,6 +2475,56 @@ impl core::borrow::BorrowMut for aya::maps::stack_trace::StackTraceMap pub fn aya::maps::stack_trace::StackTraceMap::borrow_mut(&mut self) -> &mut T impl core::convert::From for aya::maps::stack_trace::StackTraceMap pub fn aya::maps::stack_trace::StackTraceMap::from(t: T) -> T +pub struct aya::maps::StructOpsMap +impl> aya::maps::struct_ops::StructOpsMap +pub fn aya::maps::struct_ops::StructOpsMap::attach(&self) -> core::result::Result +pub fn aya::maps::struct_ops::StructOpsMap::register(&self) -> core::result::Result<(), aya::maps::MapError> +impl> aya::maps::struct_ops::StructOpsMap +pub fn aya::maps::struct_ops::StructOpsMap::fd(&self) -> &aya::maps::MapFd +pub fn aya::maps::struct_ops::StructOpsMap::func_info(&self) -> core::option::Option<&[aya_obj::maps::StructOpsFuncInfo]> +pub fn aya::maps::struct_ops::StructOpsMap::is_link(&self) -> bool +pub fn aya::maps::struct_ops::StructOpsMap::type_name(&self) -> core::option::Option<&str> +impl> aya::maps::struct_ops::StructOpsMap +pub fn aya::maps::struct_ops::StructOpsMap::pin>(self, path: P) -> core::result::Result<(), aya::pin::PinError> +impl> aya::maps::struct_ops::StructOpsMap +pub fn aya::maps::struct_ops::StructOpsMap::set_field_i32(&mut self, offset: u32, value: i32) -> core::result::Result<(), aya::maps::MapError> +pub fn aya::maps::struct_ops::StructOpsMap::set_field_u32(&mut self, offset: u32, value: u32) -> core::result::Result<(), aya::maps::MapError> +impl core::convert::TryFrom for aya::maps::struct_ops::StructOpsMap +pub type aya::maps::struct_ops::StructOpsMap::Error = aya::maps::MapError +pub fn aya::maps::struct_ops::StructOpsMap::try_from(map: aya::maps::Map) -> core::result::Result +impl core::convert::TryFrom for aya::maps::struct_ops::StructOpsMap +pub type aya::maps::struct_ops::StructOpsMap::Error = aya::maps::MapError +pub fn aya::maps::struct_ops::StructOpsMap::try_from(map: aya::maps::MapData) -> core::result::Result +impl<'a> core::convert::TryFrom<&'a aya::maps::Map> for aya::maps::struct_ops::StructOpsMap<&'a aya::maps::MapData> +pub type aya::maps::struct_ops::StructOpsMap<&'a aya::maps::MapData>::Error = aya::maps::MapError +pub fn aya::maps::struct_ops::StructOpsMap<&'a aya::maps::MapData>::try_from(map: &'a aya::maps::Map) -> core::result::Result +impl<'a> core::convert::TryFrom<&'a mut aya::maps::Map> for aya::maps::struct_ops::StructOpsMap<&'a mut aya::maps::MapData> +pub type aya::maps::struct_ops::StructOpsMap<&'a mut aya::maps::MapData>::Error = aya::maps::MapError +pub fn aya::maps::struct_ops::StructOpsMap<&'a mut aya::maps::MapData>::try_from(map: &'a mut aya::maps::Map) -> core::result::Result +impl core::fmt::Debug for aya::maps::struct_ops::StructOpsMap +pub fn aya::maps::struct_ops::StructOpsMap::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::marker::Freeze for aya::maps::struct_ops::StructOpsMap where T: core::marker::Freeze +impl core::marker::Send for aya::maps::struct_ops::StructOpsMap where T: core::marker::Send +impl core::marker::Sync for aya::maps::struct_ops::StructOpsMap where T: core::marker::Sync +impl core::marker::Unpin for aya::maps::struct_ops::StructOpsMap where T: core::marker::Unpin +impl core::panic::unwind_safe::RefUnwindSafe for aya::maps::struct_ops::StructOpsMap where T: core::panic::unwind_safe::RefUnwindSafe +impl core::panic::unwind_safe::UnwindSafe for aya::maps::struct_ops::StructOpsMap where T: core::panic::unwind_safe::UnwindSafe +impl core::convert::Into for aya::maps::struct_ops::StructOpsMap where U: core::convert::From +pub fn aya::maps::struct_ops::StructOpsMap::into(self) -> U +impl core::convert::TryFrom for aya::maps::struct_ops::StructOpsMap where U: core::convert::Into +pub type aya::maps::struct_ops::StructOpsMap::Error = core::convert::Infallible +pub fn aya::maps::struct_ops::StructOpsMap::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya::maps::struct_ops::StructOpsMap where U: core::convert::TryFrom +pub type aya::maps::struct_ops::StructOpsMap::Error = >::Error +pub fn aya::maps::struct_ops::StructOpsMap::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya::maps::struct_ops::StructOpsMap where T: 'static + ?core::marker::Sized +pub fn aya::maps::struct_ops::StructOpsMap::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya::maps::struct_ops::StructOpsMap where T: ?core::marker::Sized +pub fn aya::maps::struct_ops::StructOpsMap::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya::maps::struct_ops::StructOpsMap where T: ?core::marker::Sized +pub fn aya::maps::struct_ops::StructOpsMap::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya::maps::struct_ops::StructOpsMap +pub fn aya::maps::struct_ops::StructOpsMap::from(t: T) -> T pub struct aya::maps::XskMap impl> aya::maps::XskMap pub fn aya::maps::XskMap::len(&self) -> u32 @@ -4396,6 +4510,8 @@ impl core::convert::From for aya::programs::raw_tr pub fn aya::programs::raw_trace_point::RawTracePointLink::from(b: aya::programs::links::FdLink) -> aya::programs::raw_trace_point::RawTracePointLink impl core::convert::From for aya::programs::sk_lookup::SkLookupLink pub fn aya::programs::sk_lookup::SkLookupLink::from(b: aya::programs::links::FdLink) -> aya::programs::sk_lookup::SkLookupLink +impl core::convert::From for aya::programs::struct_ops::StructOpsLink +pub fn aya::programs::struct_ops::StructOpsLink::from(b: aya::programs::links::FdLink) -> Self impl core::convert::From for aya::programs::tp_btf::BtfTracePointLink pub fn aya::programs::tp_btf::BtfTracePointLink::from(b: aya::programs::links::FdLink) -> aya::programs::tp_btf::BtfTracePointLink impl core::convert::From for aya::programs::links::FdLink @@ -4408,6 +4524,8 @@ impl core::convert::From for pub fn aya::programs::links::FdLink::from(w: aya::programs::raw_trace_point::RawTracePointLink) -> aya::programs::links::FdLink impl core::convert::From for aya::programs::links::FdLink pub fn aya::programs::links::FdLink::from(w: aya::programs::sk_lookup::SkLookupLink) -> aya::programs::links::FdLink +impl core::convert::From for aya::programs::links::FdLink +pub fn aya::programs::links::FdLink::from(w: aya::programs::struct_ops::StructOpsLink) -> Self impl core::convert::From for aya::programs::links::FdLink pub fn aya::programs::links::FdLink::from(w: aya::programs::tp_btf::BtfTracePointLink) -> aya::programs::links::FdLink impl core::convert::TryFrom for aya::programs::links::FdLink @@ -4809,6 +4927,14 @@ impl aya::programs::links::Link for aya::programs::socket_filter::SocketFilterLi pub type aya::programs::socket_filter::SocketFilterLink::Id = aya::programs::socket_filter::SocketFilterLinkId pub fn aya::programs::socket_filter::SocketFilterLink::detach(self) -> core::result::Result<(), aya::programs::ProgramError> pub fn aya::programs::socket_filter::SocketFilterLink::id(&self) -> Self::Id +impl aya::programs::links::Link for aya::programs::struct_ops::StructOpsLink +pub type aya::programs::struct_ops::StructOpsLink::Id = aya::programs::struct_ops::StructOpsLinkId +pub fn aya::programs::struct_ops::StructOpsLink::detach(self) -> core::result::Result<(), aya::programs::ProgramError> +pub fn aya::programs::struct_ops::StructOpsLink::id(&self) -> Self::Id +impl aya::programs::links::Link for aya::programs::syscall::SyscallLink +pub type aya::programs::syscall::SyscallLink::Id = aya::programs::syscall::SyscallLinkId +pub fn aya::programs::syscall::SyscallLink::detach(self) -> core::result::Result<(), aya::programs::ProgramError> +pub fn aya::programs::syscall::SyscallLink::id(&self) -> Self::Id impl aya::programs::links::Link for aya::programs::tc::SchedClassifierLink pub type aya::programs::tc::SchedClassifierLink::Id = aya::programs::tc::SchedClassifierLinkId pub fn aya::programs::tc::SchedClassifierLink::detach(self) -> core::result::Result<(), aya::programs::ProgramError> @@ -6691,6 +6817,253 @@ impl core::borrow::BorrowMut for aya::programs::socket_filter::SocketFilte pub fn aya::programs::socket_filter::SocketFilterLinkId::borrow_mut(&mut self) -> &mut T impl core::convert::From for aya::programs::socket_filter::SocketFilterLinkId pub fn aya::programs::socket_filter::SocketFilterLinkId::from(t: T) -> T +pub mod aya::programs::struct_ops +pub struct aya::programs::struct_ops::StructOps +impl aya::programs::struct_ops::StructOps +pub const aya::programs::struct_ops::StructOps::PROGRAM_TYPE: aya::programs::ProgramType +pub fn aya::programs::struct_ops::StructOps::load(&mut self, struct_name: &str, btf: &aya_obj::btf::btf::Btf) -> core::result::Result<(), aya::programs::ProgramError> +pub fn aya::programs::struct_ops::StructOps::member_name(&self) -> &str +impl aya::programs::struct_ops::StructOps +pub fn aya::programs::struct_ops::StructOps::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError> +impl aya::programs::struct_ops::StructOps +pub fn aya::programs::struct_ops::StructOps::from_pin>(path: P) -> core::result::Result +impl aya::programs::struct_ops::StructOps +pub fn aya::programs::struct_ops::StructOps::info(&self) -> core::result::Result +impl aya::programs::struct_ops::StructOps +pub fn aya::programs::struct_ops::StructOps::pin>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError> +pub fn aya::programs::struct_ops::StructOps::unpin(&mut self) -> core::result::Result<(), std::io::error::Error> +impl aya::programs::struct_ops::StructOps +pub fn aya::programs::struct_ops::StructOps::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError> +impl core::fmt::Debug for aya::programs::struct_ops::StructOps +pub fn aya::programs::struct_ops::StructOps::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::ops::drop::Drop for aya::programs::struct_ops::StructOps +pub fn aya::programs::struct_ops::StructOps::drop(&mut self) +impl<'a> core::convert::TryFrom<&'a aya::programs::Program> for &'a aya::programs::struct_ops::StructOps +pub type &'a aya::programs::struct_ops::StructOps::Error = aya::programs::ProgramError +pub fn &'a aya::programs::struct_ops::StructOps::try_from(program: &'a aya::programs::Program) -> core::result::Result<&'a aya::programs::struct_ops::StructOps, aya::programs::ProgramError> +impl<'a> core::convert::TryFrom<&'a mut aya::programs::Program> for &'a mut aya::programs::struct_ops::StructOps +pub type &'a mut aya::programs::struct_ops::StructOps::Error = aya::programs::ProgramError +pub fn &'a mut aya::programs::struct_ops::StructOps::try_from(program: &'a mut aya::programs::Program) -> core::result::Result<&'a mut aya::programs::struct_ops::StructOps, aya::programs::ProgramError> +impl core::marker::Freeze for aya::programs::struct_ops::StructOps +impl core::marker::Send for aya::programs::struct_ops::StructOps +impl core::marker::Sync for aya::programs::struct_ops::StructOps +impl core::marker::Unpin for aya::programs::struct_ops::StructOps +impl core::panic::unwind_safe::RefUnwindSafe for aya::programs::struct_ops::StructOps +impl core::panic::unwind_safe::UnwindSafe for aya::programs::struct_ops::StructOps +impl core::convert::Into for aya::programs::struct_ops::StructOps where U: core::convert::From +pub fn aya::programs::struct_ops::StructOps::into(self) -> U +impl core::convert::TryFrom for aya::programs::struct_ops::StructOps where U: core::convert::Into +pub type aya::programs::struct_ops::StructOps::Error = core::convert::Infallible +pub fn aya::programs::struct_ops::StructOps::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya::programs::struct_ops::StructOps where U: core::convert::TryFrom +pub type aya::programs::struct_ops::StructOps::Error = >::Error +pub fn aya::programs::struct_ops::StructOps::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya::programs::struct_ops::StructOps where T: 'static + ?core::marker::Sized +pub fn aya::programs::struct_ops::StructOps::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya::programs::struct_ops::StructOps where T: ?core::marker::Sized +pub fn aya::programs::struct_ops::StructOps::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya::programs::struct_ops::StructOps where T: ?core::marker::Sized +pub fn aya::programs::struct_ops::StructOps::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya::programs::struct_ops::StructOps +pub fn aya::programs::struct_ops::StructOps::from(t: T) -> T +pub struct aya::programs::struct_ops::StructOpsLink(_) +impl aya::programs::links::Link for aya::programs::struct_ops::StructOpsLink +pub type aya::programs::struct_ops::StructOpsLink::Id = aya::programs::struct_ops::StructOpsLinkId +pub fn aya::programs::struct_ops::StructOpsLink::detach(self) -> core::result::Result<(), aya::programs::ProgramError> +pub fn aya::programs::struct_ops::StructOpsLink::id(&self) -> Self::Id +impl core::cmp::Eq for aya::programs::struct_ops::StructOpsLink +impl core::cmp::PartialEq for aya::programs::struct_ops::StructOpsLink +pub fn aya::programs::struct_ops::StructOpsLink::eq(&self, other: &Self) -> bool +impl core::convert::From for aya::programs::struct_ops::StructOpsLink +pub fn aya::programs::struct_ops::StructOpsLink::from(b: aya::programs::links::FdLink) -> Self +impl core::convert::From for aya::programs::links::FdLink +pub fn aya::programs::links::FdLink::from(w: aya::programs::struct_ops::StructOpsLink) -> Self +impl core::fmt::Debug for aya::programs::struct_ops::StructOpsLink +pub fn aya::programs::struct_ops::StructOpsLink::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::hash::Hash for aya::programs::struct_ops::StructOpsLink +pub fn aya::programs::struct_ops::StructOpsLink::hash(&self, state: &mut H) +impl core::ops::drop::Drop for aya::programs::struct_ops::StructOpsLink +pub fn aya::programs::struct_ops::StructOpsLink::drop(&mut self) +impl equivalent::Equivalent for aya::programs::struct_ops::StructOpsLinkId +pub fn aya::programs::struct_ops::StructOpsLinkId::equivalent(&self, key: &aya::programs::struct_ops::StructOpsLink) -> bool +impl core::marker::Freeze for aya::programs::struct_ops::StructOpsLink +impl core::marker::Send for aya::programs::struct_ops::StructOpsLink +impl core::marker::Sync for aya::programs::struct_ops::StructOpsLink +impl core::marker::Unpin for aya::programs::struct_ops::StructOpsLink +impl core::panic::unwind_safe::RefUnwindSafe for aya::programs::struct_ops::StructOpsLink +impl core::panic::unwind_safe::UnwindSafe for aya::programs::struct_ops::StructOpsLink +impl equivalent::Equivalent for aya::programs::struct_ops::StructOpsLink where Q: core::cmp::Eq + ?core::marker::Sized, K: core::borrow::Borrow + ?core::marker::Sized +pub fn aya::programs::struct_ops::StructOpsLink::equivalent(&self, key: &K) -> bool +impl core::convert::Into for aya::programs::struct_ops::StructOpsLink where U: core::convert::From +pub fn aya::programs::struct_ops::StructOpsLink::into(self) -> U +impl core::convert::TryFrom for aya::programs::struct_ops::StructOpsLink where U: core::convert::Into +pub type aya::programs::struct_ops::StructOpsLink::Error = core::convert::Infallible +pub fn aya::programs::struct_ops::StructOpsLink::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya::programs::struct_ops::StructOpsLink where U: core::convert::TryFrom +pub type aya::programs::struct_ops::StructOpsLink::Error = >::Error +pub fn aya::programs::struct_ops::StructOpsLink::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya::programs::struct_ops::StructOpsLink where T: 'static + ?core::marker::Sized +pub fn aya::programs::struct_ops::StructOpsLink::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya::programs::struct_ops::StructOpsLink where T: ?core::marker::Sized +pub fn aya::programs::struct_ops::StructOpsLink::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya::programs::struct_ops::StructOpsLink where T: ?core::marker::Sized +pub fn aya::programs::struct_ops::StructOpsLink::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya::programs::struct_ops::StructOpsLink +pub fn aya::programs::struct_ops::StructOpsLink::from(t: T) -> T +pub struct aya::programs::struct_ops::StructOpsLinkId(_) +impl core::cmp::Eq for aya::programs::struct_ops::StructOpsLinkId +impl core::cmp::PartialEq for aya::programs::struct_ops::StructOpsLinkId +pub fn aya::programs::struct_ops::StructOpsLinkId::eq(&self, other: &aya::programs::struct_ops::StructOpsLinkId) -> bool +impl core::fmt::Debug for aya::programs::struct_ops::StructOpsLinkId +pub fn aya::programs::struct_ops::StructOpsLinkId::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::hash::Hash for aya::programs::struct_ops::StructOpsLinkId +pub fn aya::programs::struct_ops::StructOpsLinkId::hash<__H: core::hash::Hasher>(&self, state: &mut __H) +impl core::marker::StructuralPartialEq for aya::programs::struct_ops::StructOpsLinkId +impl equivalent::Equivalent for aya::programs::struct_ops::StructOpsLinkId +pub fn aya::programs::struct_ops::StructOpsLinkId::equivalent(&self, key: &aya::programs::struct_ops::StructOpsLink) -> bool +impl core::marker::Freeze for aya::programs::struct_ops::StructOpsLinkId +impl core::marker::Send for aya::programs::struct_ops::StructOpsLinkId +impl core::marker::Sync for aya::programs::struct_ops::StructOpsLinkId +impl core::marker::Unpin for aya::programs::struct_ops::StructOpsLinkId +impl core::panic::unwind_safe::RefUnwindSafe for aya::programs::struct_ops::StructOpsLinkId +impl core::panic::unwind_safe::UnwindSafe for aya::programs::struct_ops::StructOpsLinkId +impl equivalent::Equivalent for aya::programs::struct_ops::StructOpsLinkId where Q: core::cmp::Eq + ?core::marker::Sized, K: core::borrow::Borrow + ?core::marker::Sized +pub fn aya::programs::struct_ops::StructOpsLinkId::equivalent(&self, key: &K) -> bool +impl core::convert::Into for aya::programs::struct_ops::StructOpsLinkId where U: core::convert::From +pub fn aya::programs::struct_ops::StructOpsLinkId::into(self) -> U +impl core::convert::TryFrom for aya::programs::struct_ops::StructOpsLinkId where U: core::convert::Into +pub type aya::programs::struct_ops::StructOpsLinkId::Error = core::convert::Infallible +pub fn aya::programs::struct_ops::StructOpsLinkId::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya::programs::struct_ops::StructOpsLinkId where U: core::convert::TryFrom +pub type aya::programs::struct_ops::StructOpsLinkId::Error = >::Error +pub fn aya::programs::struct_ops::StructOpsLinkId::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya::programs::struct_ops::StructOpsLinkId where T: 'static + ?core::marker::Sized +pub fn aya::programs::struct_ops::StructOpsLinkId::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya::programs::struct_ops::StructOpsLinkId where T: ?core::marker::Sized +pub fn aya::programs::struct_ops::StructOpsLinkId::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya::programs::struct_ops::StructOpsLinkId where T: ?core::marker::Sized +pub fn aya::programs::struct_ops::StructOpsLinkId::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya::programs::struct_ops::StructOpsLinkId +pub fn aya::programs::struct_ops::StructOpsLinkId::from(t: T) -> T +pub mod aya::programs::syscall +pub struct aya::programs::syscall::Syscall +impl aya::programs::syscall::Syscall +pub const aya::programs::syscall::Syscall::PROGRAM_TYPE: aya::programs::ProgramType +pub fn aya::programs::syscall::Syscall::load(&mut self) -> core::result::Result<(), aya::programs::ProgramError> +impl aya::programs::syscall::Syscall +pub fn aya::programs::syscall::Syscall::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError> +impl aya::programs::syscall::Syscall +pub fn aya::programs::syscall::Syscall::info(&self) -> core::result::Result +impl aya::programs::syscall::Syscall +pub fn aya::programs::syscall::Syscall::pin>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError> +pub fn aya::programs::syscall::Syscall::unpin(&mut self) -> core::result::Result<(), std::io::error::Error> +impl aya::programs::syscall::Syscall +pub fn aya::programs::syscall::Syscall::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError> +impl core::fmt::Debug for aya::programs::syscall::Syscall +pub fn aya::programs::syscall::Syscall::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::ops::drop::Drop for aya::programs::syscall::Syscall +pub fn aya::programs::syscall::Syscall::drop(&mut self) +impl<'a> core::convert::TryFrom<&'a aya::programs::Program> for &'a aya::programs::syscall::Syscall +pub type &'a aya::programs::syscall::Syscall::Error = aya::programs::ProgramError +pub fn &'a aya::programs::syscall::Syscall::try_from(program: &'a aya::programs::Program) -> core::result::Result<&'a aya::programs::syscall::Syscall, aya::programs::ProgramError> +impl<'a> core::convert::TryFrom<&'a mut aya::programs::Program> for &'a mut aya::programs::syscall::Syscall +pub type &'a mut aya::programs::syscall::Syscall::Error = aya::programs::ProgramError +pub fn &'a mut aya::programs::syscall::Syscall::try_from(program: &'a mut aya::programs::Program) -> core::result::Result<&'a mut aya::programs::syscall::Syscall, aya::programs::ProgramError> +impl core::marker::Freeze for aya::programs::syscall::Syscall +impl core::marker::Send for aya::programs::syscall::Syscall +impl core::marker::Sync for aya::programs::syscall::Syscall +impl core::marker::Unpin for aya::programs::syscall::Syscall +impl core::panic::unwind_safe::RefUnwindSafe for aya::programs::syscall::Syscall +impl core::panic::unwind_safe::UnwindSafe for aya::programs::syscall::Syscall +impl core::convert::Into for aya::programs::syscall::Syscall where U: core::convert::From +pub fn aya::programs::syscall::Syscall::into(self) -> U +impl core::convert::TryFrom for aya::programs::syscall::Syscall where U: core::convert::Into +pub type aya::programs::syscall::Syscall::Error = core::convert::Infallible +pub fn aya::programs::syscall::Syscall::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya::programs::syscall::Syscall where U: core::convert::TryFrom +pub type aya::programs::syscall::Syscall::Error = >::Error +pub fn aya::programs::syscall::Syscall::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya::programs::syscall::Syscall where T: 'static + ?core::marker::Sized +pub fn aya::programs::syscall::Syscall::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya::programs::syscall::Syscall where T: ?core::marker::Sized +pub fn aya::programs::syscall::Syscall::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya::programs::syscall::Syscall where T: ?core::marker::Sized +pub fn aya::programs::syscall::Syscall::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya::programs::syscall::Syscall +pub fn aya::programs::syscall::Syscall::from(t: T) -> T +pub struct aya::programs::syscall::SyscallLink +impl aya::programs::links::Link for aya::programs::syscall::SyscallLink +pub type aya::programs::syscall::SyscallLink::Id = aya::programs::syscall::SyscallLinkId +pub fn aya::programs::syscall::SyscallLink::detach(self) -> core::result::Result<(), aya::programs::ProgramError> +pub fn aya::programs::syscall::SyscallLink::id(&self) -> Self::Id +impl core::cmp::Eq for aya::programs::syscall::SyscallLink +impl core::cmp::PartialEq for aya::programs::syscall::SyscallLink +pub fn aya::programs::syscall::SyscallLink::eq(&self, other: &Self) -> bool +impl core::fmt::Debug for aya::programs::syscall::SyscallLink +pub fn aya::programs::syscall::SyscallLink::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::hash::Hash for aya::programs::syscall::SyscallLink +pub fn aya::programs::syscall::SyscallLink::hash(&self, state: &mut H) +impl equivalent::Equivalent for aya::programs::syscall::SyscallLinkId +pub fn aya::programs::syscall::SyscallLinkId::equivalent(&self, key: &aya::programs::syscall::SyscallLink) -> bool +impl core::marker::Freeze for aya::programs::syscall::SyscallLink +impl core::marker::Send for aya::programs::syscall::SyscallLink +impl core::marker::Sync for aya::programs::syscall::SyscallLink +impl core::marker::Unpin for aya::programs::syscall::SyscallLink +impl core::panic::unwind_safe::RefUnwindSafe for aya::programs::syscall::SyscallLink +impl core::panic::unwind_safe::UnwindSafe for aya::programs::syscall::SyscallLink +impl equivalent::Equivalent for aya::programs::syscall::SyscallLink where Q: core::cmp::Eq + ?core::marker::Sized, K: core::borrow::Borrow + ?core::marker::Sized +pub fn aya::programs::syscall::SyscallLink::equivalent(&self, key: &K) -> bool +impl core::convert::Into for aya::programs::syscall::SyscallLink where U: core::convert::From +pub fn aya::programs::syscall::SyscallLink::into(self) -> U +impl core::convert::TryFrom for aya::programs::syscall::SyscallLink where U: core::convert::Into +pub type aya::programs::syscall::SyscallLink::Error = core::convert::Infallible +pub fn aya::programs::syscall::SyscallLink::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya::programs::syscall::SyscallLink where U: core::convert::TryFrom +pub type aya::programs::syscall::SyscallLink::Error = >::Error +pub fn aya::programs::syscall::SyscallLink::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya::programs::syscall::SyscallLink where T: 'static + ?core::marker::Sized +pub fn aya::programs::syscall::SyscallLink::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya::programs::syscall::SyscallLink where T: ?core::marker::Sized +pub fn aya::programs::syscall::SyscallLink::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya::programs::syscall::SyscallLink where T: ?core::marker::Sized +pub fn aya::programs::syscall::SyscallLink::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya::programs::syscall::SyscallLink +pub fn aya::programs::syscall::SyscallLink::from(t: T) -> T +pub struct aya::programs::syscall::SyscallLinkId(_) +impl core::cmp::Eq for aya::programs::syscall::SyscallLinkId +impl core::cmp::PartialEq for aya::programs::syscall::SyscallLinkId +pub fn aya::programs::syscall::SyscallLinkId::eq(&self, other: &aya::programs::syscall::SyscallLinkId) -> bool +impl core::fmt::Debug for aya::programs::syscall::SyscallLinkId +pub fn aya::programs::syscall::SyscallLinkId::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::hash::Hash for aya::programs::syscall::SyscallLinkId +pub fn aya::programs::syscall::SyscallLinkId::hash<__H: core::hash::Hasher>(&self, state: &mut __H) +impl core::marker::StructuralPartialEq for aya::programs::syscall::SyscallLinkId +impl equivalent::Equivalent for aya::programs::syscall::SyscallLinkId +pub fn aya::programs::syscall::SyscallLinkId::equivalent(&self, key: &aya::programs::syscall::SyscallLink) -> bool +impl core::marker::Freeze for aya::programs::syscall::SyscallLinkId +impl core::marker::Send for aya::programs::syscall::SyscallLinkId +impl core::marker::Sync for aya::programs::syscall::SyscallLinkId +impl core::marker::Unpin for aya::programs::syscall::SyscallLinkId +impl core::panic::unwind_safe::RefUnwindSafe for aya::programs::syscall::SyscallLinkId +impl core::panic::unwind_safe::UnwindSafe for aya::programs::syscall::SyscallLinkId +impl equivalent::Equivalent for aya::programs::syscall::SyscallLinkId where Q: core::cmp::Eq + ?core::marker::Sized, K: core::borrow::Borrow + ?core::marker::Sized +pub fn aya::programs::syscall::SyscallLinkId::equivalent(&self, key: &K) -> bool +impl core::convert::Into for aya::programs::syscall::SyscallLinkId where U: core::convert::From +pub fn aya::programs::syscall::SyscallLinkId::into(self) -> U +impl core::convert::TryFrom for aya::programs::syscall::SyscallLinkId where U: core::convert::Into +pub type aya::programs::syscall::SyscallLinkId::Error = core::convert::Infallible +pub fn aya::programs::syscall::SyscallLinkId::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya::programs::syscall::SyscallLinkId where U: core::convert::TryFrom +pub type aya::programs::syscall::SyscallLinkId::Error = >::Error +pub fn aya::programs::syscall::SyscallLinkId::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya::programs::syscall::SyscallLinkId where T: 'static + ?core::marker::Sized +pub fn aya::programs::syscall::SyscallLinkId::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya::programs::syscall::SyscallLinkId where T: ?core::marker::Sized +pub fn aya::programs::syscall::SyscallLinkId::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya::programs::syscall::SyscallLinkId where T: ?core::marker::Sized +pub fn aya::programs::syscall::SyscallLinkId::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya::programs::syscall::SyscallLinkId +pub fn aya::programs::syscall::SyscallLinkId::from(t: T) -> T pub mod aya::programs::tc pub enum aya::programs::tc::TcAttachOptions pub aya::programs::tc::TcAttachOptions::Netlink(aya::programs::tc::NlOptions) @@ -8101,6 +8474,8 @@ pub aya::programs::Program::SkMsg(aya::programs::sk_msg::SkMsg) pub aya::programs::Program::SkSkb(aya::programs::sk_skb::SkSkb) pub aya::programs::Program::SockOps(aya::programs::sock_ops::SockOps) pub aya::programs::Program::SocketFilter(aya::programs::socket_filter::SocketFilter) +pub aya::programs::Program::StructOps(aya::programs::struct_ops::StructOps) +pub aya::programs::Program::Syscall(aya::programs::syscall::Syscall) pub aya::programs::Program::TracePoint(aya::programs::trace_point::TracePoint) pub aya::programs::Program::UProbe(aya::programs::uprobe::UProbe) pub aya::programs::Program::Xdp(aya::programs::xdp::Xdp) @@ -8178,6 +8553,12 @@ pub fn &'a aya::programs::sock_ops::SockOps::try_from(program: &'a aya::programs impl<'a> core::convert::TryFrom<&'a aya::programs::Program> for &'a aya::programs::socket_filter::SocketFilter pub type &'a aya::programs::socket_filter::SocketFilter::Error = aya::programs::ProgramError pub fn &'a aya::programs::socket_filter::SocketFilter::try_from(program: &'a aya::programs::Program) -> core::result::Result<&'a aya::programs::socket_filter::SocketFilter, aya::programs::ProgramError> +impl<'a> core::convert::TryFrom<&'a aya::programs::Program> for &'a aya::programs::struct_ops::StructOps +pub type &'a aya::programs::struct_ops::StructOps::Error = aya::programs::ProgramError +pub fn &'a aya::programs::struct_ops::StructOps::try_from(program: &'a aya::programs::Program) -> core::result::Result<&'a aya::programs::struct_ops::StructOps, aya::programs::ProgramError> +impl<'a> core::convert::TryFrom<&'a aya::programs::Program> for &'a aya::programs::syscall::Syscall +pub type &'a aya::programs::syscall::Syscall::Error = aya::programs::ProgramError +pub fn &'a aya::programs::syscall::Syscall::try_from(program: &'a aya::programs::Program) -> core::result::Result<&'a aya::programs::syscall::Syscall, aya::programs::ProgramError> impl<'a> core::convert::TryFrom<&'a aya::programs::Program> for &'a aya::programs::tc::SchedClassifier pub type &'a aya::programs::tc::SchedClassifier::Error = aya::programs::ProgramError pub fn &'a aya::programs::tc::SchedClassifier::try_from(program: &'a aya::programs::Program) -> core::result::Result<&'a aya::programs::tc::SchedClassifier, aya::programs::ProgramError> @@ -8259,6 +8640,12 @@ pub fn &'a mut aya::programs::sock_ops::SockOps::try_from(program: &'a mut aya:: impl<'a> core::convert::TryFrom<&'a mut aya::programs::Program> for &'a mut aya::programs::socket_filter::SocketFilter pub type &'a mut aya::programs::socket_filter::SocketFilter::Error = aya::programs::ProgramError pub fn &'a mut aya::programs::socket_filter::SocketFilter::try_from(program: &'a mut aya::programs::Program) -> core::result::Result<&'a mut aya::programs::socket_filter::SocketFilter, aya::programs::ProgramError> +impl<'a> core::convert::TryFrom<&'a mut aya::programs::Program> for &'a mut aya::programs::struct_ops::StructOps +pub type &'a mut aya::programs::struct_ops::StructOps::Error = aya::programs::ProgramError +pub fn &'a mut aya::programs::struct_ops::StructOps::try_from(program: &'a mut aya::programs::Program) -> core::result::Result<&'a mut aya::programs::struct_ops::StructOps, aya::programs::ProgramError> +impl<'a> core::convert::TryFrom<&'a mut aya::programs::Program> for &'a mut aya::programs::syscall::Syscall +pub type &'a mut aya::programs::syscall::Syscall::Error = aya::programs::ProgramError +pub fn &'a mut aya::programs::syscall::Syscall::try_from(program: &'a mut aya::programs::Program) -> core::result::Result<&'a mut aya::programs::syscall::Syscall, aya::programs::ProgramError> impl<'a> core::convert::TryFrom<&'a mut aya::programs::Program> for &'a mut aya::programs::tc::SchedClassifier pub type &'a mut aya::programs::tc::SchedClassifier::Error = aya::programs::ProgramError pub fn &'a mut aya::programs::tc::SchedClassifier::try_from(program: &'a mut aya::programs::Program) -> core::result::Result<&'a mut aya::programs::tc::SchedClassifier, aya::programs::ProgramError> @@ -10118,6 +10505,99 @@ impl core::borrow::BorrowMut for aya::programs::socket_filter::SocketFilte pub fn aya::programs::socket_filter::SocketFilter::borrow_mut(&mut self) -> &mut T impl core::convert::From for aya::programs::socket_filter::SocketFilter pub fn aya::programs::socket_filter::SocketFilter::from(t: T) -> T +pub struct aya::programs::StructOps +impl aya::programs::struct_ops::StructOps +pub const aya::programs::struct_ops::StructOps::PROGRAM_TYPE: aya::programs::ProgramType +pub fn aya::programs::struct_ops::StructOps::load(&mut self, struct_name: &str, btf: &aya_obj::btf::btf::Btf) -> core::result::Result<(), aya::programs::ProgramError> +pub fn aya::programs::struct_ops::StructOps::member_name(&self) -> &str +impl aya::programs::struct_ops::StructOps +pub fn aya::programs::struct_ops::StructOps::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError> +impl aya::programs::struct_ops::StructOps +pub fn aya::programs::struct_ops::StructOps::from_pin>(path: P) -> core::result::Result +impl aya::programs::struct_ops::StructOps +pub fn aya::programs::struct_ops::StructOps::info(&self) -> core::result::Result +impl aya::programs::struct_ops::StructOps +pub fn aya::programs::struct_ops::StructOps::pin>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError> +pub fn aya::programs::struct_ops::StructOps::unpin(&mut self) -> core::result::Result<(), std::io::error::Error> +impl aya::programs::struct_ops::StructOps +pub fn aya::programs::struct_ops::StructOps::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError> +impl core::fmt::Debug for aya::programs::struct_ops::StructOps +pub fn aya::programs::struct_ops::StructOps::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::ops::drop::Drop for aya::programs::struct_ops::StructOps +pub fn aya::programs::struct_ops::StructOps::drop(&mut self) +impl<'a> core::convert::TryFrom<&'a aya::programs::Program> for &'a aya::programs::struct_ops::StructOps +pub type &'a aya::programs::struct_ops::StructOps::Error = aya::programs::ProgramError +pub fn &'a aya::programs::struct_ops::StructOps::try_from(program: &'a aya::programs::Program) -> core::result::Result<&'a aya::programs::struct_ops::StructOps, aya::programs::ProgramError> +impl<'a> core::convert::TryFrom<&'a mut aya::programs::Program> for &'a mut aya::programs::struct_ops::StructOps +pub type &'a mut aya::programs::struct_ops::StructOps::Error = aya::programs::ProgramError +pub fn &'a mut aya::programs::struct_ops::StructOps::try_from(program: &'a mut aya::programs::Program) -> core::result::Result<&'a mut aya::programs::struct_ops::StructOps, aya::programs::ProgramError> +impl core::marker::Freeze for aya::programs::struct_ops::StructOps +impl core::marker::Send for aya::programs::struct_ops::StructOps +impl core::marker::Sync for aya::programs::struct_ops::StructOps +impl core::marker::Unpin for aya::programs::struct_ops::StructOps +impl core::panic::unwind_safe::RefUnwindSafe for aya::programs::struct_ops::StructOps +impl core::panic::unwind_safe::UnwindSafe for aya::programs::struct_ops::StructOps +impl core::convert::Into for aya::programs::struct_ops::StructOps where U: core::convert::From +pub fn aya::programs::struct_ops::StructOps::into(self) -> U +impl core::convert::TryFrom for aya::programs::struct_ops::StructOps where U: core::convert::Into +pub type aya::programs::struct_ops::StructOps::Error = core::convert::Infallible +pub fn aya::programs::struct_ops::StructOps::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya::programs::struct_ops::StructOps where U: core::convert::TryFrom +pub type aya::programs::struct_ops::StructOps::Error = >::Error +pub fn aya::programs::struct_ops::StructOps::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya::programs::struct_ops::StructOps where T: 'static + ?core::marker::Sized +pub fn aya::programs::struct_ops::StructOps::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya::programs::struct_ops::StructOps where T: ?core::marker::Sized +pub fn aya::programs::struct_ops::StructOps::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya::programs::struct_ops::StructOps where T: ?core::marker::Sized +pub fn aya::programs::struct_ops::StructOps::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya::programs::struct_ops::StructOps +pub fn aya::programs::struct_ops::StructOps::from(t: T) -> T +pub struct aya::programs::Syscall +impl aya::programs::syscall::Syscall +pub const aya::programs::syscall::Syscall::PROGRAM_TYPE: aya::programs::ProgramType +pub fn aya::programs::syscall::Syscall::load(&mut self) -> core::result::Result<(), aya::programs::ProgramError> +impl aya::programs::syscall::Syscall +pub fn aya::programs::syscall::Syscall::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError> +impl aya::programs::syscall::Syscall +pub fn aya::programs::syscall::Syscall::info(&self) -> core::result::Result +impl aya::programs::syscall::Syscall +pub fn aya::programs::syscall::Syscall::pin>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError> +pub fn aya::programs::syscall::Syscall::unpin(&mut self) -> core::result::Result<(), std::io::error::Error> +impl aya::programs::syscall::Syscall +pub fn aya::programs::syscall::Syscall::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError> +impl core::fmt::Debug for aya::programs::syscall::Syscall +pub fn aya::programs::syscall::Syscall::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::ops::drop::Drop for aya::programs::syscall::Syscall +pub fn aya::programs::syscall::Syscall::drop(&mut self) +impl<'a> core::convert::TryFrom<&'a aya::programs::Program> for &'a aya::programs::syscall::Syscall +pub type &'a aya::programs::syscall::Syscall::Error = aya::programs::ProgramError +pub fn &'a aya::programs::syscall::Syscall::try_from(program: &'a aya::programs::Program) -> core::result::Result<&'a aya::programs::syscall::Syscall, aya::programs::ProgramError> +impl<'a> core::convert::TryFrom<&'a mut aya::programs::Program> for &'a mut aya::programs::syscall::Syscall +pub type &'a mut aya::programs::syscall::Syscall::Error = aya::programs::ProgramError +pub fn &'a mut aya::programs::syscall::Syscall::try_from(program: &'a mut aya::programs::Program) -> core::result::Result<&'a mut aya::programs::syscall::Syscall, aya::programs::ProgramError> +impl core::marker::Freeze for aya::programs::syscall::Syscall +impl core::marker::Send for aya::programs::syscall::Syscall +impl core::marker::Sync for aya::programs::syscall::Syscall +impl core::marker::Unpin for aya::programs::syscall::Syscall +impl core::panic::unwind_safe::RefUnwindSafe for aya::programs::syscall::Syscall +impl core::panic::unwind_safe::UnwindSafe for aya::programs::syscall::Syscall +impl core::convert::Into for aya::programs::syscall::Syscall where U: core::convert::From +pub fn aya::programs::syscall::Syscall::into(self) -> U +impl core::convert::TryFrom for aya::programs::syscall::Syscall where U: core::convert::Into +pub type aya::programs::syscall::Syscall::Error = core::convert::Infallible +pub fn aya::programs::syscall::Syscall::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya::programs::syscall::Syscall where U: core::convert::TryFrom +pub type aya::programs::syscall::Syscall::Error = >::Error +pub fn aya::programs::syscall::Syscall::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya::programs::syscall::Syscall where T: 'static + ?core::marker::Sized +pub fn aya::programs::syscall::Syscall::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya::programs::syscall::Syscall where T: ?core::marker::Sized +pub fn aya::programs::syscall::Syscall::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya::programs::syscall::Syscall where T: ?core::marker::Sized +pub fn aya::programs::syscall::Syscall::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya::programs::syscall::Syscall +pub fn aya::programs::syscall::Syscall::from(t: T) -> T pub struct aya::programs::TracePoint impl aya::programs::trace_point::TracePoint pub const aya::programs::trace_point::TracePoint::PROGRAM_TYPE: aya::programs::ProgramType @@ -10491,6 +10971,14 @@ impl aya::programs::links::Link for aya::programs::socket_filter::SocketFilterLi pub type aya::programs::socket_filter::SocketFilterLink::Id = aya::programs::socket_filter::SocketFilterLinkId pub fn aya::programs::socket_filter::SocketFilterLink::detach(self) -> core::result::Result<(), aya::programs::ProgramError> pub fn aya::programs::socket_filter::SocketFilterLink::id(&self) -> Self::Id +impl aya::programs::links::Link for aya::programs::struct_ops::StructOpsLink +pub type aya::programs::struct_ops::StructOpsLink::Id = aya::programs::struct_ops::StructOpsLinkId +pub fn aya::programs::struct_ops::StructOpsLink::detach(self) -> core::result::Result<(), aya::programs::ProgramError> +pub fn aya::programs::struct_ops::StructOpsLink::id(&self) -> Self::Id +impl aya::programs::links::Link for aya::programs::syscall::SyscallLink +pub type aya::programs::syscall::SyscallLink::Id = aya::programs::syscall::SyscallLinkId +pub fn aya::programs::syscall::SyscallLink::detach(self) -> core::result::Result<(), aya::programs::ProgramError> +pub fn aya::programs::syscall::SyscallLink::id(&self) -> Self::Id impl aya::programs::links::Link for aya::programs::tc::SchedClassifierLink pub type aya::programs::tc::SchedClassifierLink::Id = aya::programs::tc::SchedClassifierLinkId pub fn aya::programs::tc::SchedClassifierLink::detach(self) -> core::result::Result<(), aya::programs::ProgramError> @@ -10718,6 +11206,7 @@ pub struct aya::Ebpf impl aya::Ebpf pub fn aya::Ebpf::load(data: &[u8]) -> core::result::Result pub fn aya::Ebpf::load_file>(path: P) -> core::result::Result +pub fn aya::Ebpf::load_struct_ops(&mut self, btf: &aya_obj::btf::btf::Btf) -> core::result::Result<(), aya::EbpfError> pub fn aya::Ebpf::map(&self, name: &str) -> core::option::Option<&aya::maps::Map> pub fn aya::Ebpf::map_mut(&mut self, name: &str) -> core::option::Option<&mut aya::maps::Map> pub fn aya::Ebpf::maps(&self) -> impl core::iter::traits::iterator::Iterator