diff --git a/aya-obj/src/btf/btf.rs b/aya-obj/src/btf/btf.rs index e08de4e9e..374e2f297 100644 --- a/aya-obj/src/btf/btf.rs +++ b/aya-obj/src/btf/btf.rs @@ -24,6 +24,7 @@ use crate::{ info::{FuncSecInfo, LineSecInfo}, relocation::Relocation, }, + extern_types::ExternCollection, generated::{btf_ext_header, btf_header}, util::{HashMap, bytes_of}, }; @@ -264,6 +265,8 @@ pub struct Btf { strings: Vec, types: BtfTypes, _endianness: Endianness, + /// Extern functions parsed from ksyms section + pub(crate) externs: ExternCollection, } fn add_type(header: &mut btf_header, types: &mut BtfTypes, btf_type: BtfType) -> u32 { @@ -292,6 +295,7 @@ impl Btf { strings: vec![0], types: BtfTypes::default(), _endianness: Endianness::default(), + externs: ExternCollection::new(), } } @@ -364,6 +368,7 @@ impl Btf { strings, types, _endianness: endianness, + externs: ExternCollection::new(), }) } @@ -500,6 +505,9 @@ impl Btf { symbol_offsets: &HashMap, features: &BtfFeatures, ) -> Result<(), BtfError> { + if !self.externs.is_empty() { + self.fixup_ksyms_datasec(self.externs.datasec_id, self.externs.dummy_ksym_var_id)?; + } let enum64_placeholder_id = OnceCell::new(); let filler_var_id = OnceCell::new(); let mut types = mem::take(&mut self.types); @@ -805,6 +813,128 @@ impl Btf { self.types = types; Ok(()) } + + /// Fixes up BTF for `.ksyms` datasec entries containing extern kernel symbol and + /// makes it acceptable by the kernel: + /// + /// * Changes linkage of extern functions to `GLOBAL`, fixes parameter names, injects + /// a dummy variable representing them in datasec. + /// * Changes linkage of extern variables to `GLOBAL_ALLOCATED`, replaces their type + /// with `int`. + pub(crate) fn fixup_ksyms_datasec( + &mut self, + datasec_id: Option, + dummy_var_id: Option, + ) -> Result<(), BtfError> { + // Extract dummy variable's name offset and type ID for patching func_proto names and datasec entries. + // If dummy var exists: use its name_offset (for string table patching) and btf_type (underlying int type). + // If no dummy var (fallback): search for a 4-byte int type directly, with no name_offset. + // Both paths provide an int_btf_id to ensure type consistency in datasec variable entries. + let (dummy_var_name_offset, int_btf_id) = if let Some(dummy_id) = dummy_var_id { + let dummy_type = &self.types.types[dummy_id as usize]; + if let BtfType::Var(v) = dummy_type { + (Some(v.name_offset), v.btf_type) + } else { + return Err(BtfError::InvalidDatasec); + } + } else { + let int_id = self + .types + .types + .iter() + .enumerate() + .find_map(|(idx, t)| { + if let BtfType::Int(int_type) = t { + (int_type.size == 4).then_some(idx as u32) + } else { + None + } + }) + .ok_or(BtfError::InvalidDatasec)?; + + (None, int_id) + }; + + let datasec_id = datasec_id.ok_or(BtfError::InvalidDatasec)?; + + let datasec_name = { + let datasec = &self.types.types[datasec_id as usize]; + let BtfType::DataSec(d) = datasec else { + return Err(BtfError::InvalidDatasec); + }; + self.string_at(d.name_offset)?.into_owned() + }; + + debug!("DATASEC {datasec_name}: fixing up extern ksyms"); + + let entry_type_ids: Vec = { + let BtfType::DataSec(d) = &self.types.types[datasec_id as usize] else { + return Err(BtfError::InvalidDatasec); + }; + d.entries.iter().map(|e| e.btf_type).collect() + }; + + let mut offset = 0u32; + let size = size_of::() as u32; + + for (i, &type_id) in entry_type_ids.iter().enumerate() { + match &self.types.types[type_id as usize] { + BtfType::Func(f) => { + let (func_name, proto_id) = + { (self.string_at(f.name_offset)?.into_owned(), f.btf_type) }; + + if let BtfType::Func(f) = &mut self.types.types[type_id as usize] { + f.set_linkage(FuncLinkage::Global); + } + + if let Some(dummy_name_off) = dummy_var_name_offset { + if let BtfType::FuncProto(func_proto) = + &mut self.types.types[proto_id as usize] + { + for param in &mut func_proto.params { + if param.btf_type != 0 && param.name_offset == 0 { + param.name_offset = dummy_name_off; + } + } + } + } + + if let (Some(dummy_id), BtfType::DataSec(d)) = + (dummy_var_id, &mut self.types.types[datasec_id as usize]) + { + d.entries[i].btf_type = dummy_id; + } + + debug!("DATASEC {datasec_name}: FUNC {func_name}: fixup offset {offset}"); + } + BtfType::Var(v) => { + let var_name = { self.string_at(v.name_offset)?.into_owned() }; + + if let BtfType::Var(v) = &mut self.types.types[type_id as usize] { + v.linkage = VarLinkage::Global; + v.btf_type = int_btf_id; + } + + debug!("DATASEC {datasec_name}: VAR {var_name}: fixup offset {offset}"); + } + _ => continue, + } + + if let BtfType::DataSec(d) = &mut self.types.types[datasec_id as usize] { + d.entries[i].offset = offset; + d.entries[i].size = size; + } + + offset += size; + } + + if let BtfType::DataSec(d) = &mut self.types.types[datasec_id as usize] { + d.size = offset; + debug!("DATASEC {datasec_name}: fixup size to {offset}"); + } + + Ok(()) + } } impl Default for Btf { diff --git a/aya-obj/src/btf/extern_types.rs b/aya-obj/src/btf/extern_types.rs new file mode 100644 index 000000000..c915a4a60 --- /dev/null +++ b/aya-obj/src/btf/extern_types.rs @@ -0,0 +1,186 @@ +use alloc::vec::Vec; + +use log::debug; + +use crate::{ + KsymsError, Object, + btf::{Btf, BtfError, BtfType, DataSec, DataSecEntry}, + extern_types::{ExternDesc, ExternType}, + relocation::Symbol, + util::HashMap, +}; +impl Btf { + /// Creates a dummy global variable named `dummy_ksym` with a 4-byte int type for unresolved kernel symbols. + pub(crate) fn create_dummy_ksym_var(&mut self) -> u32 { + let int_type_id = { + let mut found_id = None; + for (idx, t) in self.types().enumerate() { + if let BtfType::Int(int) = t { + if int.size == 4 { + found_id = Some((idx) as u32); + break; + } + } + } + found_id + }; + + let int_type_id = if let Some(id) = int_type_id { + id + } else { + let name_offset = self.add_string("int"); + self.add_type(BtfType::Int(crate::btf::Int::new( + name_offset, + 4, + crate::btf::IntEncoding::Signed, + 0, + ))) + }; + + debug!("Found/created int type_id: {int_type_id}"); + if let Ok(BtfType::Int(int)) = self.type_by_id(int_type_id) { + debug!( + "Int type size: {}, encoding: {:?}", + int.size, + int.encoding() + ); + } + + let name_offset = self.add_string("dummy_ksym"); + let dummy_var_id = self.add_type(BtfType::Var(crate::btf::Var::new( + name_offset, + int_type_id, + crate::btf::VarLinkage::Global, + ))); + + debug!("Created dummy_var type_id: {dummy_var_id}"); + if let Ok(BtfType::Var(var)) = self.type_by_id(dummy_var_id) { + debug!("Dummy var points to type_id: {}", var.btf_type); + } + + dummy_var_id + } + + /// Searches for the `.ksyms` datasec in BTF, returns it if found. + fn find_ksyms_datasec(&self) -> Result, BtfError> { + for (idx, btf_type) in self.types().enumerate() { + if let BtfType::DataSec(datasec) = btf_type { + let name = self.type_name(btf_type)?; + if name == ".ksyms" { + return Ok(Some((idx as u32, datasec.clone()))); + } + } + } + + Ok(None) + } + + /// Checks if datasec contains any functions. + pub(crate) fn datasec_has_functions(&self, datasec: &DataSec) -> bool { + datasec.entries.iter().any(|entry| { + self.type_by_id(entry.btf_type) + .is_ok_and(|t| matches!(t, BtfType::Func(_))) + }) + } + + /// Collects extern descriptors from datasec entries. + pub(crate) fn collect_extern_entries( + &self, + datasec: &DataSec, + symbol_table: &HashMap, + ) -> Result, BtfError> { + let mut result = Vec::new(); + + for entry in &datasec.entries { + let Some(extern_desc) = self.process_datasec_entry(entry, symbol_table)? else { + continue; + }; + + result.push(extern_desc); + } + + Ok(result) + } + + /// Processes a single datasec entry, returns [`ExternDesc`] if it's an extern. + fn process_datasec_entry( + &self, + entry: &DataSecEntry, + symbol_table: &HashMap, + ) -> Result, BtfError> { + let btf_type = self.type_by_id(entry.btf_type)?; + + let (name, is_func, var_btf_type) = match btf_type { + BtfType::Func(func) => { + let name = self.string_at(func.name_offset)?.into_owned(); + (name, true, func.btf_type) + } + BtfType::Var(var) => { + let name = self.string_at(var.name_offset)?.into_owned(); + (name, false, var.btf_type) + } + _ => return Ok(None), + }; + + let symbol = find_symbol_by_name(symbol_table, &name).ok_or(BtfError::InvalidSymbolName)?; + + // Resolve through modifiers (const, volatile, typedef, etc.) + // Type ID 0 represents void in BTF + let resolved_type_id = self.resolve_type(var_btf_type).unwrap_or(var_btf_type); + + // Typeless ksyms are declared as `extern const void symbol __ksym` + // They resolve to void (type_id 0) and are resolved via /proc/kallsyms + let is_typeless = !is_func && resolved_type_id == 0; + + let mut extern_desc = ExternDesc::new( + name, + ExternType::Ksym, + entry.btf_type, + symbol.is_weak, + is_func, + ); + + // For typeless ksyms, don't set type_id so they skip kernel BTF resolution + if !is_typeless { + extern_desc.type_id = Some(resolved_type_id); + } + + Ok(Some(extern_desc)) + } +} + +fn find_symbol_by_name<'a>( + symbol_table: &'a HashMap, + name: &str, +) -> Option<&'a Symbol> { + symbol_table + .values() + .find(|sym| sym.name.as_deref() == Some(name)) +} + +impl Object { + /// Collects extern kernel symbols from BTF datasec entries. + pub fn collect_ksyms_from_btf(&mut self) -> Result<(), KsymsError> { + let btf = self.btf.as_mut().ok_or(KsymsError::NoBtf)?; + let Some((datasec_id, datasec)) = btf.find_ksyms_datasec()? else { + return Ok(()); + }; + + if btf.datasec_has_functions(&datasec) { + let dummy_var_id = btf.create_dummy_ksym_var(); + btf.externs.set_dummy_var_id(dummy_var_id); + } + + let collected = btf.collect_extern_entries(&datasec, &self.symbol_table)?; + + for extern_desc in collected { + btf.externs.insert(extern_desc.name.clone(), extern_desc); + } + + if !btf.externs.is_empty() { + btf.externs.datasec_id = Some(datasec_id); + } + + Ok(()) + } +} diff --git a/aya-obj/src/btf/mod.rs b/aya-obj/src/btf/mod.rs index 81c727817..4f3344194 100644 --- a/aya-obj/src/btf/mod.rs +++ b/aya-obj/src/btf/mod.rs @@ -2,6 +2,7 @@ #[expect(clippy::module_inception, reason = "TODO")] mod btf; +mod extern_types; mod info; mod relocation; mod types; diff --git a/aya-obj/src/extern_types.rs b/aya-obj/src/extern_types.rs new file mode 100644 index 000000000..a60f5cee8 --- /dev/null +++ b/aya-obj/src/extern_types.rs @@ -0,0 +1,493 @@ +//! Extern type resolution, relocation +use alloc::{ + string::{String, ToString as _}, + vec::Vec, +}; + +use crate::{ + Object, + btf::{Btf, BtfError, BtfKind, BtfType}, + util::HashMap, +}; + +impl Object { + /// Resolves typed externs through kernel `BTF`. + pub fn resolve_typed_externs(&mut self, kernel_btf: &mut Btf) -> Result<(), KsymsError> { + let mut resolutions = Vec::new(); + { + let obj_btf = self.btf.as_ref().ok_or(KsymsError::NoBtf)?; + + for (name, extern_desc) in obj_btf.externs.iter() { + if extern_desc.type_id.is_none() { + continue; + } + + let btf_type = obj_btf.type_by_id(extern_desc.btf_id)?; + let kernel_btf_id = match btf_type { + BtfType::Func(_) => { + self.resolve_extern_function_internal(name, extern_desc, kernel_btf)? + } + BtfType::Var(_) => { + self.resolve_extern_variable_internal(name, extern_desc, kernel_btf)? + } + _ => { + return Err(KsymsError::InvalidExternType { name: name.clone() }); + } + }; + + if let Some(btf_id) = kernel_btf_id { + resolutions.push((name.clone(), btf_id)); + } + } + } + + let obj_mut = self.btf.as_mut().ok_or(KsymsError::NoBtf)?; + + for (name, kernel_btf_id) in resolutions { + if let Some(ext) = obj_mut.externs.get_mut(&name) { + ext.kernel_btf_id = Some(kernel_btf_id); + ext.is_resolved = true; + } + } + + Ok(()) + } + + /// Resolves typeless extern vars found in `.ksyms` section through `kallsyms`. + #[cfg(feature = "std")] + pub fn resolve_typeless_externs(&mut self) -> Result<(), KsymsError> { + use std::{fs::File, io::BufRead as _}; + let unresolved: Vec = self.get_unresolved_ksym_vars(); + + if !unresolved.is_empty() { + let file = File::open("/proc/kallsyms")?; + + let reader = std::io::BufReader::new(file); + let lines: Vec = reader.lines().collect::>()?; + + self.resolve_kallsyms_from_lines(&lines, &unresolved)?; + } + + // Finalize unresolved weak externs by setting ksym_addr = 0 + // This allows pointer checks like `if (weak_func)` to evaluate to false + self.finalize_weak_externs(); + + Ok(()) + } + + /// Sets `ksym_addr = Some(0)` for all unresolved weak ksym externs. + #[cfg(feature = "std")] + fn finalize_weak_externs(&mut self) { + let Some(obj_btf) = self.btf.as_mut() else { + return; + }; + + for ext in obj_btf.externs.externs.values_mut() { + if ext.extern_type == ExternType::Ksym && !ext.is_resolved && ext.is_weak { + ext.ksym_addr = Some(0); + } + } + } + + #[cfg(feature = "std")] + fn resolve_kallsyms_from_lines( + &mut self, + lines: &[String], + unresolved: &[String], + ) -> Result<(), KsymsError> { + let Some(obj_btf) = self.btf.as_mut() else { + return Err(KsymsError::NoBtf); + }; + + let mut resolved_count = 0; + + for line in lines { + // Parse: [] + let parts: Vec<&str> = line.split_whitespace().collect(); + if parts.len() < 3 { + continue; + } + + let addr_str = parts[0]; + let sym_name = parts[2]; + + if !unresolved.iter().any(|s| s.as_str() == sym_name) { + continue; + } + + let addr = u64::from_str_radix(addr_str, 16).map_err(|_err| { + KsymsError::KallsymsParseError(alloc::format!("invalid address: {addr_str}")) + })?; + + if let Some(ext) = obj_btf.externs.get_mut(sym_name) { + if ext.is_resolved { + if let Some(existing_addr) = ext.ksym_addr { + if existing_addr != addr { + return Err(KsymsError::AmbiguousResolution { + name: sym_name.to_string(), + first_addr: existing_addr, + second_addr: addr, + }); + } + } + } + + ext.ksym_addr = Some(addr); + ext.is_resolved = true; + resolved_count += 1; + } + + if resolved_count == unresolved.len() { + break; + } + } + + // Check for unresolved non-weak symbols + for (name, ext) in &obj_btf.externs.externs { + if ext.extern_type == ExternType::Ksym && !ext.is_resolved && !ext.is_weak { + return Err(KsymsError::UnresolvedExtern { name: name.clone() }); + } + } + + Ok(()) + } + + /// Gets unresolved ksyms variables from [`ExternCollection`]. + #[cfg(feature = "std")] + fn get_unresolved_ksym_vars(&self) -> Vec { + let Some(obj_btf) = self.btf.as_ref() else { + return Vec::new(); + }; + + obj_btf + .externs + .externs + .iter() + .filter(|(_, ext)| { + ext.extern_type == ExternType::Ksym && !ext.is_func && !ext.is_resolved + }) + .map(|(name, _)| name.clone()) + .collect() + } + + /// Resolves a single extern function. Returns BTF ID if found, otherwise + /// returns `None`. + fn resolve_extern_function_internal( + &self, + name: &str, + extern_desc: &ExternDesc, + kernel_btf: &Btf, + ) -> Result, KsymsError> { + let lookup_name = extern_desc.essent_name.as_deref().unwrap_or(name); + let Ok(kernel_func_id) = kernel_btf.id_by_type_name_kind(lookup_name, BtfKind::Func) else { + if extern_desc.is_weak { + return Ok(None); + } + + return Err(KsymsError::FunctionNotFound { + name: lookup_name.to_string(), + }); + }; + + let kernel_func_type = kernel_btf.type_by_id(kernel_func_id)?; + let kernel_proto_id = match kernel_func_type { + BtfType::Func(func) => func.btf_type, + _ => { + return Err(KsymsError::BtfError(BtfError::UnexpectedBtfType { + type_id: kernel_func_id, + })); + } + }; + + let local_proto_id = + extern_desc + .type_id + .ok_or(KsymsError::BtfError(BtfError::UnknownBtfType { + type_id: 0, + }))?; + + let obj_btf = self.btf.as_ref().ok_or(KsymsError::NoBtf)?; + let compatible = + crate::btf::types_are_compatible(obj_btf, local_proto_id, kernel_btf, kernel_proto_id)?; + + if !compatible { + if extern_desc.is_weak { + return Ok(None); + } + return Err(KsymsError::IncompatibleFunctionSignature { + name: lookup_name.to_string(), + }); + } + + Ok(Some(kernel_func_id)) + } + + /// Resolves a single extern variable. Returns BTF ID if found, otherwise + /// returns `None`. + fn resolve_extern_variable_internal( + &self, + name: &str, + extern_desc: &ExternDesc, + kernel_btf: &Btf, + ) -> Result, KsymsError> { + let Ok(kernel_var_id) = kernel_btf.id_by_type_name_kind(name, BtfKind::Var) else { + if extern_desc.is_weak { + return Ok(None); + } + return Err(KsymsError::VariableNotFound { + name: name.to_string(), + }); + }; + + let kernel_var_type = kernel_btf.type_by_id(kernel_var_id)?; + let kernel_type_id = match kernel_var_type { + BtfType::Var(var) => var.btf_type, + _ => { + return Err(KsymsError::BtfError(BtfError::UnexpectedBtfType { + type_id: kernel_var_id, + })); + } + }; + + let local_type_id = + extern_desc + .type_id + .ok_or(KsymsError::BtfError(BtfError::UnknownBtfType { + type_id: 0, + }))?; + + let obj_btf = self.btf.as_ref().ok_or(KsymsError::NoBtf)?; + let compatible = + crate::btf::types_are_compatible(obj_btf, local_type_id, kernel_btf, kernel_type_id)?; + + if !compatible { + return Err(KsymsError::IncompatibleVariableType { + name: name.to_string(), + }); + } + + Ok(Some(kernel_var_id)) + } +} + +/// Errors that can occur during ksyms operations. +#[derive(Debug, thiserror::Error)] +pub enum KsymsError { + /// A non-weak extern variable was not found in kernel BTF + #[error("kernel variable '{name}' not found in kernel BTF or kallsyms")] + VariableNotFound { + /// The name of the variable that was not found + name: String, + }, + + /// The extern function's signature is incompatible with the kernel function + #[error("kernel function '{name}' has incompatible signature")] + IncompatibleFunctionSignature { + /// The name of the function with incompatible signature + name: String, + }, + + /// The extern variable's type is incompatible with the kernel variable + #[error("kernel variable '{name}' has incompatible type")] + IncompatibleVariableType { + /// The name of the variable with incompatible type + name: String, + }, + + /// The extern symbol has an invalid BTF type (neither Func nor Var) + #[error("extern '{name}' has invalid BTF type (neither Func nor Var)")] + InvalidExternType { + /// The name of the extern with invalid type + name: String, + }, + + /// An error occurred while working with BTF data + #[error("BTF error: {0}")] + BtfError(#[from] BtfError), + + /// The object file has no BTF information + #[error("object has no BTF information")] + NoBtf, + + /// Resolved Extern's kallsyms address does not match agains the loaded kallsyms + #[error("extern (ksym) '{name}': resolution is ambiguous: {first_addr:#x} or {second_addr:#x}")] + AmbiguousResolution { + /// The name of the symbol with ambiguous resolution + name: String, + /// The first address found + first_addr: u64, + /// The second (conflicting) address found + second_addr: u64, + }, + + /// Could not read kallsyms entries + #[cfg(feature = "std")] + #[error("failed to read /proc/kallsyms: {0}")] + KallsymsReadError(#[from] std::io::Error), + + /// Failed to parse kallsyms data + #[error("failed to parse kallsyms: {0}")] + KallsymsParseError(String), + + /// An unresolved non-weak extern was encountered during patching + #[error( + "extern symbol '{name}' is not resolved (non-weak externs must be resolved before patching)" + )] + UnresolvedExtern { + /// The name of the unresolved extern + name: String, + }, + + /// Function not found when trying to patch its instructions + #[error("function '{name}' not found")] + FunctionNotFound { + /// The section index + name: String, + }, +} + +/// Type of extern symbol +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ExternType { + /// Kernel configuration variable (.kconfig section) + Kconfig, + /// Kernel symbol - variable or function (.ksyms section) + Ksym, +} + +/// Descriptor for an extern symbol +#[derive(Debug, Clone)] +pub(crate) struct ExternDesc { + /// Symbol name + pub(crate) name: String, + + /// Type of extern (Kconfig or Ksym) + #[cfg_attr( + not(feature = "std"), + expect(dead_code, reason = "only used with std feature") + )] + pub(crate) extern_type: ExternType, + + /// BTF type ID in local (program) BTF + pub(crate) btf_id: u32, + + /// Whether this is a weak symbol + pub(crate) is_weak: bool, + + /// Whether this is a function (vs variable) + #[cfg_attr( + not(feature = "std"), + expect(dead_code, reason = "only used with std feature") + )] + pub(crate) is_func: bool, + + /// Whether extern has been resolved + pub(crate) is_resolved: bool, + + /// For ksym: kernel BTF ID (after resolution) + pub(crate) kernel_btf_id: Option, + + /// For ksym variables: resolved kernel address + pub(crate) ksym_addr: Option, + + /// For ksym: resolved type ID (after skipping modifiers/typedefs) + pub(crate) type_id: Option, + + /// For names with flavors: stripped essential name + pub(crate) essent_name: Option, +} + +/// Given `some_struct_name___with_flavor` return the length of a name prefix +/// before last triple underscore. Struct name part after last triple +/// underscore is ignored by BPF CO-RE relocation during relocation matching. +fn essential_name_len(name: &str) -> usize { + let n = name.len(); + if n < 5 { + return n; + } + + for i in (0..=n - 5).rev() { + if is_flavor_sep(name, i) { + return i + 1; + } + } + + n +} + +fn is_flavor_sep(s: &str, pos: usize) -> bool { + let bytes = s.as_bytes(); + if pos + 4 >= bytes.len() { + return false; + } + bytes[pos] != b'_' + && bytes[pos + 1] == b'_' + && bytes[pos + 2] == b'_' + && bytes[pos + 3] == b'_' + && bytes[pos + 4] != b'_' +} + +impl ExternDesc { + pub(crate) fn new( + name: String, + extern_type: ExternType, + btf_id: u32, + is_weak: bool, + is_func: bool, + ) -> Self { + let essent_len = essential_name_len(&name); + let essent_name = (essent_len != name.len()).then(|| name[..essent_len].to_string()); + + Self { + name, + extern_type, + btf_id, + is_weak, + is_func, + is_resolved: false, + kernel_btf_id: None, + ksym_addr: None, + type_id: None, + essent_name, + } + } +} + +/// Collection of extern symbols +#[derive(Debug, Default, Clone)] +pub struct ExternCollection { + /// Map of extern descriptors by name + pub(crate) externs: HashMap, + + /// BTF ID of dummy ksym variable (if created) + pub(crate) dummy_ksym_var_id: Option, + + /// Index ID of `.ksyms`datasec entry in BTF types. + pub(crate) datasec_id: Option, +} + +impl ExternCollection { + pub(crate) fn new() -> Self { + Self::default() + } + + pub(crate) fn insert(&mut self, name: String, desc: ExternDesc) { + self.externs.insert(name, desc); + } + + pub(crate) fn get_mut(&mut self, name: &str) -> Option<&mut ExternDesc> { + self.externs.get_mut(name) + } + + pub(crate) fn iter(&self) -> impl Iterator { + self.externs.iter() + } + + pub(crate) const fn set_dummy_var_id(&mut self, id: u32) { + self.dummy_ksym_var_id = Some(id); + } + + pub(crate) fn is_empty(&self) -> bool { + self.externs.is_empty() + } +} diff --git a/aya-obj/src/lib.rs b/aya-obj/src/lib.rs index 73c0c1ac6..ff9b84789 100644 --- a/aya-obj/src/lib.rs +++ b/aya-obj/src/lib.rs @@ -76,6 +76,7 @@ extern crate alloc; extern crate std; pub mod btf; +pub mod extern_types; #[expect( clippy::all, clippy::as_pointer_underscore, @@ -107,6 +108,7 @@ pub mod programs; pub mod relocation; mod util; +pub use extern_types::KsymsError; pub use maps::Map; pub use obj::*; diff --git a/aya-obj/src/obj.rs b/aya-obj/src/obj.rs index ccb37527e..df7111292 100644 --- a/aya-obj/src/obj.rs +++ b/aya-obj/src/obj.rs @@ -23,6 +23,7 @@ use object::{ }; use crate::{ + KsymsError, btf::{ Array, Btf, BtfError, BtfExt, BtfFeatures, BtfType, DataSecEntry, FuncSecInfo, LineSecInfo, }, @@ -482,6 +483,7 @@ impl Object { size: symbol.size(), is_definition: symbol.is_definition(), kind: symbol.kind(), + is_weak: symbol.is_weak(), }; bpf_obj.symbol_table.insert(symbol.index().0, sym); if let Some(section_idx) = symbol.section().index() { @@ -505,6 +507,8 @@ impl Object { if let Some(s) = obj.section_by_name(".BTF.ext") { bpf_obj.parse_section(Section::try_from(&s)?)?; } + + bpf_obj.collect_ksyms_from_btf()?; } for s in obj.sections() { @@ -513,7 +517,6 @@ impl Object { continue; } } - bpf_obj.parse_section(Section::try_from(&s)?)?; } @@ -947,6 +950,9 @@ pub enum ParseError { #[error("error parsing ELF data")] ElfError(object::read::Error), + #[error("error collecting Externs: {0}")] + KsymsError(#[from] KsymsError), + /// Error parsing BTF object #[error("BTF error")] BtfError(#[from] BtfError), @@ -1009,6 +1015,23 @@ pub enum ParseError { /// No BTF parsed for object #[error("no BTF parsed for object")] NoBTF, + + /// Extern weak function not supported + #[error("extern weak function '{name}' is not supported in this implementation")] + WeakFuncNotSupported { name: String }, + + /// Extern function in kconfig section not supported + #[error( + "extern function '{name}' cannot be used in {section} section (only variables allowed)" + )] + FuncInKconfig { name: String, section: String }, + + #[error("duplicate extern symbol '{name}' found in extern section")] + DuplicateExtern { name: String }, + + /// Invalid kconfig variable size + #[error("extern kconfig variable '{name}' has invalid or zero size")] + InvalidKconfigSize { name: String }, } /// Invalid bindings to the bpf type from the parsed/received value. @@ -1095,6 +1118,7 @@ impl<'a> TryFrom<&'a ObjSection<'_, '_>> for Section<'a> { error, }; let name = section.name().map_err(map_err)?; + //println!("Section name altug got: {}", name); let kind = match EbpfSectionKind::from_name(name) { EbpfSectionKind::Undefined => { if section.kind() == SectionKind::Text && section.size() > 0 { @@ -1483,6 +1507,7 @@ mod tests { size, is_definition: false, kind: SymbolKind::Text, + is_weak: false, }, ); obj.symbols_by_section @@ -2645,6 +2670,7 @@ mod tests { size: 3, is_definition: true, kind: SymbolKind::Data, + is_weak: false, }, ); diff --git a/aya-obj/src/relocation.rs b/aya-obj/src/relocation.rs index 67a5d8307..13912b39b 100644 --- a/aya-obj/src/relocation.rs +++ b/aya-obj/src/relocation.rs @@ -7,6 +7,7 @@ use object::{SectionIndex, SymbolKind}; use crate::{ EbpfSectionKind, + extern_types::ExternDesc, generated::{ BPF_CALL, BPF_JMP, BPF_K, BPF_PSEUDO_CALL, BPF_PSEUDO_FUNC, BPF_PSEUDO_MAP_FD, BPF_PSEUDO_MAP_VALUE, bpf_insn, @@ -22,6 +23,8 @@ type RawFd = std::os::fd::RawFd; type RawFd = core::ffi::c_int; pub(crate) const INS_SIZE: usize = size_of::(); +pub(crate) const BPF_PSEUDO_KFUNC_CALL: u32 = 2; +pub(crate) const BPF_PSEUDO_BTF_ID: u32 = 3; /// The error type returned by [`Object::relocate_maps`] and [`Object::relocate_calls`] #[derive(thiserror::Error, Debug)] @@ -84,6 +87,33 @@ pub enum RelocationError { /// The relocation number relocation_number: usize, }, + /// Extern not found + #[error("extern `{name}` not found")] + ExternNotFound { + /// Name of the extern symbol + name: String, + }, + + /// Unresolved extern + #[error("extern `{name}` was not resolved against kernel BTF")] + UnresolvedExtern { + /// Name of the extern symbol + name: String, + }, + + /// Missing kernel BTF ID + #[error("extern `{name}` is missing kernel BTF ID")] + MissingKallsymsAddr { + /// Name of the extern symbol + name: String, + }, + + /// Strong symbol not found anywhere (neither BTF nor kallsyms) + #[error("strong extern `{name}` not resolvable (not in kernel BTF or kallsyms)")] + UnresolvableSymbol { + /// Name of the extern symbol + name: String, + }, } #[derive(Debug, Copy, Clone)] @@ -104,6 +134,17 @@ pub(crate) struct Symbol { pub(crate) size: u64, pub(crate) is_definition: bool, pub(crate) kind: SymbolKind, + pub(crate) is_weak: bool, +} + +impl Symbol { + /// Returns true if this symbol is an extern (undefined) symbol + pub(crate) fn is_extern(&self) -> bool { + self.section_index.is_none() + && self.name.is_some() + && !self.is_definition + && self.kind == SymbolKind::Unknown + } } impl Object { @@ -142,6 +183,35 @@ impl Object { Ok(()) } + /// Relocates extern ksym references after BTF resolution + pub fn relocate_externs(&mut self) -> Result<(), EbpfRelocationError> { + if let Some(obj_btf) = self.btf.as_mut() { + for (name, extern_desc) in &obj_btf.externs.externs { + debug!( + "[DEBUG] Extern '{}': resolved={}, btf_id={:?}", + name, extern_desc.is_resolved, extern_desc.kernel_btf_id + ); + } + + for function in self.functions.values_mut() { + if let Some(relocations) = self.relocations.get(&function.section_index) { + relocate_externs( + function, + relocations.values(), + &obj_btf.externs.externs, + &self.symbol_table, + ) + .map_err(|error| EbpfRelocationError { + function: function.name.clone(), + error, + })?; + } + } + } + + Ok(()) + } + /// Relocates function calls pub fn relocate_calls( &mut self, @@ -180,6 +250,107 @@ impl Object { } } +fn relocate_externs<'a, I: Iterator>( + fun: &mut Function, + relocations: I, + externs: &HashMap, + symbol_table: &HashMap, +) -> Result<(), RelocationError> { + let section_offset = fun.section_offset; + let instructions = &mut fun.instructions; + let function_size = instructions.len() * INS_SIZE; + + for (rel_n, rel) in relocations.enumerate() { + let rel_offset = rel.offset as usize; + if rel_offset < section_offset || rel_offset >= section_offset + function_size { + continue; + } + + let ins_offset = rel_offset - section_offset; + if !ins_offset.is_multiple_of(INS_SIZE) { + return Err(RelocationError::InvalidRelocationOffset { + offset: rel.offset, + relocation_number: rel_n, + }); + } + let ins_index = ins_offset / INS_SIZE; + + let sym = symbol_table + .get(&rel.symbol_index) + .ok_or(RelocationError::UnknownSymbol { + index: rel.symbol_index, + })?; + + // Only process extern symbols + if !sym.is_extern() { + continue; + } + + let extern_name = sym.name.as_ref().unwrap(); + let extern_desc = + externs + .get(extern_name) + .ok_or_else(|| RelocationError::ExternNotFound { + name: extern_name.clone(), + })?; + + let ins = &mut instructions[ins_index]; + let is_call = insn_is_call(*ins); + + if is_call { + ins.set_src_reg(BPF_PSEUDO_KFUNC_CALL as u8); + if extern_desc.is_resolved { + let kernel_btf_id = extern_desc.kernel_btf_id.unwrap(); + ins.imm = kernel_btf_id as i32; + ins.off = 0; // btf_fd_idx, typically 0 for vmlinux + } else { + // Unresolved weak kfunc call + poison_kfunc_call(ins, rel.symbol_index); + } + } else { + // Consistent Variable Reference Resolution + match (extern_desc.kernel_btf_id, extern_desc.ksym_addr) { + // SUCCESS: Symbol found in Kernel BTF + (Some(btf_id), _) => { + ins.set_src_reg(BPF_PSEUDO_BTF_ID as u8); + ins.imm = btf_id as i32; + instructions[ins_index + 1].imm = 0; // vmlinux + } + // SUCCESS: Fallback to Kallsyms (BTF missing but address found) + (None, Some(addr)) => { + ins.set_src_reg(0); // Standard 64-bit absolute load + ins.imm = (addr & 0xFFFFFFFF) as i32; + instructions[ins_index + 1].imm = (addr >> 32) as i32; + } + // SUCCESS: Weak symbol not found (Null-patch) + (None, None) if extern_desc.is_weak => { + ins.set_src_reg(0); + ins.imm = 0; + instructions[ins_index + 1].imm = 0; + } + // FAILURE: Strong symbol not found anywhere + _ => { + return Err(RelocationError::UnresolvableSymbol { + name: extern_name.clone(), + }); + } + } + } + } + + Ok(()) +} + +const POISON_CALL_KFUNC_BASE: i32 = 2002000000; + +fn poison_kfunc_call(ins: &mut bpf_insn, ext_idx: usize) { + ins.code = (BPF_JMP | BPF_CALL) as u8; + ins.set_dst_reg(0); + ins.set_src_reg(0); + ins.off = 0; + ins.imm = POISON_CALL_KFUNC_BASE + ext_idx as i32; +} + fn relocate_maps<'a, I: Iterator>( fun: &mut Function, relocations: I, @@ -368,8 +539,9 @@ impl<'a> FunctionLinker<'a> { .map(|sym| (rel, sym)) }) .filter(|(_rel, sym)| { - // only consider text relocations, data relocations are - // relocated in relocate_maps() + if sym.is_extern() { + return false; + } sym.kind == SymbolKind::Text || sym.section_index.is_some_and(|section_index| { self.text_sections.contains(§ion_index) @@ -509,6 +681,7 @@ mod test { size, is_definition: false, kind: SymbolKind::Data, + is_weak: false, } } diff --git a/aya/src/bpf.rs b/aya/src/bpf.rs index b9f771f33..77d00b7bf 100644 --- a/aya/src/bpf.rs +++ b/aya/src/bpf.rs @@ -8,7 +8,7 @@ use std::{ }; use aya_obj::{ - EbpfSectionKind, Features, Object, ParseError, ProgramSection, + EbpfSectionKind, Features, KsymsError, Object, ParseError, ProgramSection, btf::{Btf, BtfError, BtfFeatures, BtfRelocationError}, generated::{BPF_F_SLEEPABLE, BPF_F_XDP_HAS_FRAGS, bpf_map_type}, relocation::EbpfRelocationError, @@ -510,6 +510,12 @@ impl<'a> EbpfLoader<'a> { if let Some(btf) = &btf { obj.relocate_btf(btf)?; } + + if let Some(kernel_btf) = self.btf.as_mut() { + obj.resolve_typed_externs(kernel_btf.to_mut())?; + obj.resolve_typeless_externs()?; + } + let mut maps = HashMap::new(); for (name, mut obj) in obj.maps.drain() { if let (false, EbpfSectionKind::Bss | EbpfSectionKind::Data | EbpfSectionKind::Rodata) = @@ -569,6 +575,9 @@ impl<'a> EbpfLoader<'a> { .map(|(s, data)| (s.as_str(), data.fd().as_fd().as_raw_fd(), data.obj())), &text_sections, )?; + + obj.relocate_externs()?; + obj.relocate_calls(&text_sections)?; obj.sanitize_functions(&FEATURES); @@ -1206,6 +1215,10 @@ pub enum EbpfError { #[error("error relocating section")] BtfRelocationError(#[from] BtfRelocationError), + /// Error patching extern kernel symbol instructions + #[error("kernel symbol instruction patching error: {0}")] + KsymsError(#[from] KsymsError), + /// No BTF parsed for object #[error("no BTF parsed for object")] NoBTF, diff --git a/test-distro/src/init.rs b/test-distro/src/init.rs index 17352fa1c..f7e9c8446 100644 --- a/test-distro/src/init.rs +++ b/test-distro/src/init.rs @@ -135,7 +135,6 @@ fn run() -> anyhow::Result<()> { format!("mount({source}, {target}, {fstype}, {flags:?}, {data:?}) failed") })?; } - // By contract we run everything in /bin and assume they're rust test binaries. // // If the user requested command line arguments, they're named init.arg={}. diff --git a/test/implementation_plan.md.resolved b/test/implementation_plan.md.resolved new file mode 100644 index 000000000..d07d84a57 --- /dev/null +++ b/test/implementation_plan.md.resolved @@ -0,0 +1,115 @@ +# Ksyms Test Refactoring Plan + +## Goal +Separate tests by their BTF requirements so per-CPU helper tests don't fail on kernels that only have kallsyms. + +--- + +## Current State + +| BPF Program | Uses | Tests Using It | +|-------------|------|----------------| +| [ksyms_typed_weak](file:///Users/altugbozkurt/Desktop/ebpf-related-work/aya-altug/aya/test/integration-test/src/tests/ksyms.rs#196-263) | `bpf_this_cpu_ptr` | 5 tests | +| [ksyms_typeless](file:///Users/altugbozkurt/Desktop/ebpf-related-work/aya-altug/aya/test/integration-test/src/tests/ksyms.rs#488-547) | kallsyms only | 3 tests | +| [ksyms_typed_strong](file:///Users/altugbozkurt/Desktop/ebpf-related-work/aya-altug/aya/test/integration-test/src/tests/ksyms.rs#93-195) | `bpf_this_cpu_ptr` | 1 test | + +**Problem**: [ksyms_typed_weak](file:///Users/altugbozkurt/Desktop/ebpf-related-work/aya-altug/aya/test/integration-test/src/tests/ksyms.rs#196-263) bundles per-CPU helpers with kfunc tests - all fail together. + +--- + +## Proposed Structure + +### BPF Programs (4 total) + +| Program | Purpose | Requires | +|---------|---------|----------| +| [ksyms_typeless](file:///Users/altugbozkurt/Desktop/ebpf-related-work/aya-altug/aya/test/integration-test/src/tests/ksyms.rs#488-547) | Typeless kallsyms resolution | kallsyms | +| [ksyms_typed](file:///Users/altugbozkurt/Desktop/ebpf-related-work/aya-altug/aya/test/integration-test/src/tests/ksyms.rs#196-263) | Typed ksym address resolution (no helpers) | BTF for symbol | +| `ksyms_percpu` | Per-CPU helpers (`bpf_this_cpu_ptr`, `bpf_per_cpu_ptr`) | BTF + DATASEC | +| [ksyms_kfunc](file:///Users/altugbozkurt/Desktop/ebpf-related-work/aya-altug/aya/test/integration-test/src/tests/ksyms.rs#323-414) | Kfunc resolution and invocation | BTF for kfuncs | + +### Test Mapping + +| Test | New BPF Program | What It Checks | +|------|-----------------|----------------| +| [ksyms_typed_strong](file:///Users/altugbozkurt/Desktop/ebpf-related-work/aya-altug/aya/test/integration-test/src/tests/ksyms.rs#93-195) | [ksyms_typed](file:///Users/altugbozkurt/Desktop/ebpf-related-work/aya-altug/aya/test/integration-test/src/tests/ksyms.rs#196-263) | Strong typed errors if not in BTF | +| [ksyms_typed_weak](file:///Users/altugbozkurt/Desktop/ebpf-related-work/aya-altug/aya/test/integration-test/src/tests/ksyms.rs#196-263) | [ksyms_typed](file:///Users/altugbozkurt/Desktop/ebpf-related-work/aya-altug/aya/test/integration-test/src/tests/ksyms.rs#196-263) | Weak typed resolves or returns 0 | +| [ksyms_per_cpu_ptr](file:///Users/altugbozkurt/Desktop/ebpf-related-work/aya-altug/aya/test/integration-test/src/tests/ksyms.rs#415-487) | `ksyms_percpu` | Per-CPU helpers work | +| [ksyms_kfunc](file:///Users/altugbozkurt/Desktop/ebpf-related-work/aya-altug/aya/test/integration-test/src/tests/ksyms.rs#323-414) | [ksyms_kfunc](file:///Users/altugbozkurt/Desktop/ebpf-related-work/aya-altug/aya/test/integration-test/src/tests/ksyms.rs#323-414) | Kfunc call succeeds | +| [ksyms_typeless_variable](file:///Users/altugbozkurt/Desktop/ebpf-related-work/aya-altug/aya/test/integration-test/src/tests/ksyms.rs#488-547) | [ksyms_typeless](file:///Users/altugbozkurt/Desktop/ebpf-related-work/aya-altug/aya/test/integration-test/src/tests/ksyms.rs#488-547) | Kallsyms resolution | +| [ksyms_weak_typeless](file:///Users/altugbozkurt/Desktop/ebpf-related-work/aya-altug/aya/test/integration-test/src/tests/ksyms.rs#548-595) | [ksyms_typeless](file:///Users/altugbozkurt/Desktop/ebpf-related-work/aya-altug/aya/test/integration-test/src/tests/ksyms.rs#488-547) | Weak typeless = 0 | +| [ksyms_typeless_restricted](file:///Users/altugbozkurt/Desktop/ebpf-related-work/aya-altug/aya/test/integration-test/src/tests/ksyms.rs#596-648) | [ksyms_typeless](file:///Users/altugbozkurt/Desktop/ebpf-related-work/aya-altug/aya/test/integration-test/src/tests/ksyms.rs#488-547) | Loads without kallsyms | + +--- + +## Implementation Steps + +### 1. Create [ksyms_typed.bpf.c](file:///Users/altugbozkurt/Desktop/ebpf-related-work/aya-altug/aya/test/integration-test/bpf/ksyms_typed.bpf.c) (new) +```c +// Typed ksyms without per-CPU helpers +extern int bpf_prog_active __ksym __weak; + +SEC("tp_btf/sys_enter") +int ksyms_typed(ctx) { + // Just store address, no bpf_this_cpu_ptr + output[0] = (u64)&bpf_prog_active; +} +``` + +### 2. Create `ksyms_percpu.bpf.c` (new) +```c +// Per-CPU helpers (requires BTF + DATASEC) +extern int bpf_prog_active __ksym __weak; + +SEC("tp_btf/sys_enter") +int ksyms_percpu(ctx) { + if (&bpf_prog_active) { + int *p = bpf_this_cpu_ptr(&bpf_prog_active); + // ... + } +} +``` + +### 3. Create `ksyms_kfunc.bpf.c` (new) +```c +// Kfunc tests (separate from per-CPU) +void bpf_rcu_read_lock(void) __ksym __weak; +void bpf_rcu_read_unlock(void) __ksym __weak; + +SEC("tp_btf/sys_enter") +int ksyms_kfunc_test(ctx) { + if (bpf_rcu_read_lock) { + bpf_rcu_read_lock(); + bpf_rcu_read_unlock(); + output[0] = 1; + } +} +``` + +### 4. Update [build.rs](file:///Users/altugbozkurt/Desktop/ebpf-related-work/aya-altug/aya/test/integration-test/build.rs) +Add new BPF files to `C_BPF` list. + +### 5. Update [lib.rs](file:///Users/altugbozkurt/Desktop/ebpf-related-work/aya-altug/aya/aya-obj/src/lib.rs) +Add constants for new BPF object files. + +### 6. Refactor Rust tests +- Update test imports +- Remove redundant tests ([ksyms_weak_typed](file:///Users/altugbozkurt/Desktop/ebpf-related-work/aya-altug/aya/test/integration-test/src/tests/ksyms.rs#264-322) duplicates [ksyms_typed_weak](file:///Users/altugbozkurt/Desktop/ebpf-related-work/aya-altug/aya/test/integration-test/src/tests/ksyms.rs#196-263) logic) +- Simplify skip conditions + +--- + +## Benefits + +1. **Per-CPU tests** only run on kernels with full BTF support +2. **Kfunc tests** can run independently (many kernels have kfuncs but not percpu BTF) +3. **Typed tests** can work with just BTF lookup (no helpers needed) +4. **Typeless tests** remain unchanged (kallsyms only) + +--- + +## Questions + +1. Should [ksyms_weak_typed](file:///Users/altugbozkurt/Desktop/ebpf-related-work/aya-altug/aya/test/integration-test/src/tests/ksyms.rs#264-322) be removed as duplicate of [ksyms_typed_weak](file:///Users/altugbozkurt/Desktop/ebpf-related-work/aya-altug/aya/test/integration-test/src/tests/ksyms.rs#196-263)? +2. Should we keep [ksyms_typed_strong](file:///Users/altugbozkurt/Desktop/ebpf-related-work/aya-altug/aya/test/integration-test/src/tests/ksyms.rs#93-195) in its own file or merge into [ksyms_typed.bpf.c](file:///Users/altugbozkurt/Desktop/ebpf-related-work/aya-altug/aya/test/integration-test/bpf/ksyms_typed.bpf.c)? + diff --git a/test/integration-ebpf/Cargo.toml b/test/integration-ebpf/Cargo.toml index a9bcd75f7..dd4da5958 100644 --- a/test/integration-ebpf/Cargo.toml +++ b/test/integration-ebpf/Cargo.toml @@ -68,6 +68,10 @@ path = "src/raw_tracepoint.rs" name = "redirect" path = "src/redirect.rs" +[[bin]] +name = "ksyms" +path = "src/ksyms.rs" + [[bin]] name = "relocations" path = "src/relocations.rs" diff --git a/test/integration-ebpf/src/ksyms.rs b/test/integration-ebpf/src/ksyms.rs new file mode 100644 index 000000000..497e997cc --- /dev/null +++ b/test/integration-ebpf/src/ksyms.rs @@ -0,0 +1,76 @@ +#![no_std] +#![no_main] +#![expect(unused_crate_dependencies, reason = "used in other bins")] +use aya_ebpf::{ + macros::{map, tracepoint}, + maps::Array, + programs::TracePointContext, +}; + +#[cfg(not(test))] +extern crate ebpf_panic; + +#[repr(C)] +struct TestData { + counter: u64, + read_value: u64, +} + +#[map] +static DATA_MAP: Array = Array::with_max_entries(1, 0); + +// Opaque type for dynptr +#[repr(C)] +struct bpf_dynptr { + _opaque: [u64; 2], +} + +unsafe extern "C" { + // Kfunc with multiple arguments: (data, size, flags, dynptr) + fn bpf_dynptr_from_mem(data: *mut u8, size: u32, flags: u64, ptr: *mut bpf_dynptr) -> i32; + + // Kfunc with multiple arguments: (dynptr, offset, dst, len) + fn bpf_dynptr_read(ptr: *const bpf_dynptr, offset: u32, dst: *mut u8, len: u32) -> i32; +} + +#[tracepoint] +fn sys_enter(ctx: TracePointContext) -> u32 { + try_sys_enter(&ctx); + 0 +} + +fn try_sys_enter(_ctx: &TracePointContext) { + unsafe { + if let Some(data) = DATA_MAP.get_ptr_mut(0) { + // Test data + let mut test_val: u64 = 0x1234567890ABCDEF; + let mut dynptr: bpf_dynptr = core::mem::zeroed(); + + // Test kfunc with 4 arguments + let ret = bpf_dynptr_from_mem( + core::ptr::from_mut(&mut test_val).cast::(), + 8, // size + 0, // flags + &raw mut dynptr, + ); + + if ret == 0 { + let mut read_buf: u64 = 0; + + // Test another kfunc with 4 arguments + let ret = bpf_dynptr_read( + &raw const dynptr, + 0, // offset + core::ptr::from_mut(&mut read_buf).cast::(), + 8, // len + ); + + if ret == 0 { + (*data).read_value = read_buf; + } + } + + (*data).counter = (*data).counter.wrapping_add(1); + } + } +} diff --git a/test/integration-test/bpf/ksyms.bpf.c b/test/integration-test/bpf/ksyms.bpf.c new file mode 100644 index 000000000..4baaf5501 --- /dev/null +++ b/test/integration-test/bpf/ksyms.bpf.c @@ -0,0 +1,113 @@ +// clang-format off +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +#include +#include +#include +#include +// clang-format on + +#ifndef __ksym +#define __ksym __attribute__((section(".ksyms"))) +#endif + +#ifndef __weak +#define __weak __attribute__((weak)) +#endif + +char _license[] SEC("license") = "GPL"; + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 16); + __type(key, __u32); + __type(value, __u64); +} output SEC(".maps"); + +// Typed ksyms - declared weak for portability +extern const int nonexistent_typed_ksym __ksym __weak; + +// Typeless ksyms (kallsyms-based) +extern const void init_task __ksym __weak; +extern const void nonexistent_typeless_ksym __ksym __weak; + +// Kfuncs +extern void bpf_rcu_read_lock(void) __ksym __weak; +extern void bpf_rcu_read_unlock(void) __ksym __weak; +extern void bpf_rcu_read_lock_trace(void) __ksym __weak; +extern void bpf_rcu_read_unlock_trace(void) __ksym __weak; + +// Program using WEAK typed ksyms - tests weak symbol resolution and kfuncs +// No per-cpu helpers here - those are tested in ksyms_typed_strong +SEC("tp_btf/sys_enter") +int BPF_PROG(ksyms_typed_weak, struct pt_regs *regs, long id) { + __u32 key; + __u64 val; + + // Weak typed ksym (nonexistent) - should resolve to 0 + key = 2; + val = (&nonexistent_typed_ksym) ? 1 : 0; + bpf_map_update_elem(&output, &key, &val, BPF_ANY); + + // Kfunc address + key = 3; + val = (__u64)(unsigned long)bpf_rcu_read_lock; + bpf_map_update_elem(&output, &key, &val, BPF_ANY); + + // Kfunc call + key = 4; + if (bpf_rcu_read_lock) { + bpf_rcu_read_lock(); + val = 1; + bpf_rcu_read_unlock(); + } else { + val = 0; + } + bpf_map_update_elem(&output, &key, &val, BPF_ANY); + + // Marker + key = 5; + val = 0xDEADBEEF; + bpf_map_update_elem(&output, &key, &val, BPF_ANY); + + // Second kfunc address + key = 6; + val = (__u64)(unsigned long)bpf_rcu_read_lock_trace; + bpf_map_update_elem(&output, &key, &val, BPF_ANY); + + // Second kfunc call + key = 7; + if (bpf_rcu_read_lock_trace) { + bpf_rcu_read_lock_trace(); + val = 1; + bpf_rcu_read_unlock_trace(); + } else { + val = 0; + } + bpf_map_update_elem(&output, &key, &val, BPF_ANY); + + return 0; +} + +// Program for typeless ksyms (kallsyms-based) +SEC("tp_btf/sys_enter") +int BPF_PROG(ksyms_typeless, struct pt_regs *regs, long id) { + __u32 key; + __u64 val; + + // Typeless ksym address (init_task) + key = 8; + val = (__u64)(unsigned long)&init_task; + bpf_map_update_elem(&output, &key, &val, BPF_ANY); + + // Weak typeless ksym (nonexistent) + key = 9; + val = (__u64)(unsigned long)&nonexistent_typeless_ksym; + bpf_map_update_elem(&output, &key, &val, BPF_ANY); + + // Marker + key = 10; + val = 0xCAFEBABE; + bpf_map_update_elem(&output, &key, &val, BPF_ANY); + + return 0; +} \ No newline at end of file diff --git a/test/integration-test/bpf/ksyms_strong.bpf.c b/test/integration-test/bpf/ksyms_strong.bpf.c new file mode 100644 index 000000000..fb6453f47 --- /dev/null +++ b/test/integration-test/bpf/ksyms_strong.bpf.c @@ -0,0 +1,58 @@ +// clang-format off +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +#include +#include +#include +#include +// clang-format on + +#ifndef __ksym +#define __ksym __attribute__((section(".ksyms"))) +#endif + +char _license[] SEC("license") = "GPL"; + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 8); + __type(key, __u32); + __type(value, __u64); +} strong_output SEC(".maps"); + +// STRONG typed ksym - NO __weak +// If this symbol is not in kernel BTF, aya will fail at Ebpf::load() +extern const int bpf_prog_active __ksym; + +SEC("tp_btf/sys_enter") +int BPF_PROG(ksyms_typed_strong, struct pt_regs *regs, long id) { + __u32 key; + __u64 val; + + // bpf_this_cpu_ptr - uses strong ksym directly + key = 0; + int *p = bpf_this_cpu_ptr(&bpf_prog_active); + val = (__u64)(unsigned long)p; + bpf_map_update_elem(&strong_output, &key, &val, BPF_ANY); + + // Read value + key = 1; + val = p ? (__u64)*p : 0; + bpf_map_update_elem(&strong_output, &key, &val, BPF_ANY); + + // bpf_per_cpu_ptr for CPU 0 + key = 2; + int *p0 = bpf_per_cpu_ptr(&bpf_prog_active, 0); + val = (__u64)(unsigned long)p0; + bpf_map_update_elem(&strong_output, &key, &val, BPF_ANY); + + key = 3; + val = p0 ? (__u64)*p0 : 0xFFFFFFFF; + bpf_map_update_elem(&strong_output, &key, &val, BPF_ANY); + + // Marker + key = 4; + val = 0xBEEFCAFE; + bpf_map_update_elem(&strong_output, &key, &val, BPF_ANY); + + return 0; +} diff --git a/test/integration-test/build.rs b/test/integration-test/build.rs index e6390c017..8e6cce937 100644 --- a/test/integration-test/build.rs +++ b/test/integration-test/build.rs @@ -93,6 +93,8 @@ fn main() -> Result<()> { ("struct_flavors_reloc.bpf.c", true), ("text_64_64_reloc.c", false), ("variables_reloc.bpf.c", false), + ("ksyms.bpf.c", true), + ("ksyms_strong.bpf.c", true), ]; const C_BPF_HEADERS: &[&str] = &["reloc.h", "struct_with_scalars.h"]; diff --git a/test/integration-test/src/lib.rs b/test/integration-test/src/lib.rs index 4027c0146..836ad66b2 100644 --- a/test/integration-test/src/lib.rs +++ b/test/integration-test/src/lib.rs @@ -12,6 +12,8 @@ bpf_file!( MAIN => "main.bpf.o", MULTIMAP_BTF => "multimap-btf.bpf.o", RINGBUF_BTF => "ringbuf-btf.bpf.o", + KSYMS => "ksyms.bpf.o", + KSYMS_STRONG => "ksyms_strong.bpf.o", ENUM_SIGNED_32_RELOC_BPF => "enum_signed_32_reloc.bpf.o", ENUM_SIGNED_32_RELOC_BTF => "enum_signed_32_reloc.bpf.target.o", @@ -61,6 +63,7 @@ bpf_file!( TWO_PROGS => "two_progs", XDP_SEC => "xdp_sec", UPROBE_COOKIE => "uprobe_cookie", + KSYMS_RS => "ksyms", ); #[cfg(test)] diff --git a/test/integration-test/src/tests.rs b/test/integration-test/src/tests.rs index da995bfa0..f3769ff2e 100644 --- a/test/integration-test/src/tests.rs +++ b/test/integration-test/src/tests.rs @@ -16,6 +16,7 @@ mod elf; mod feature_probe; mod info; mod iter; +mod ksyms; mod linear_data_structures; mod load; mod log; diff --git a/test/integration-test/src/tests/ksyms.rs b/test/integration-test/src/tests/ksyms.rs new file mode 100644 index 000000000..07e71dce1 --- /dev/null +++ b/test/integration-test/src/tests/ksyms.rs @@ -0,0 +1,277 @@ +//! Integration tests for kernel symbol (ksym) resolution. + +use std::{ + fs::File, + io::{BufRead as _, BufReader}, +}; + +use aya::{Btf, Ebpf, maps::Array, programs::BtfTracePoint, util::KernelVersion}; +use test_log::test; + +const PROC_KALLSYMS: &str = "/proc/kallsyms"; +const SYS_ENTER: &str = "sys_enter"; + +fn kallsyms_available() -> bool { + let file = File::open(PROC_KALLSYMS) + .unwrap_or_else(|e| panic!("failed to open {PROC_KALLSYMS}: {e:?}")); + for line in BufReader::new(file).lines() { + let line = + line.unwrap_or_else(|e| panic!("failed to read the line from {PROC_KALLSYMS}: {e:?}")); + if let Some(addr) = line.split_whitespace().next() { + let addr = u64::from_str_radix(addr, 16) + .unwrap_or_else(|e| panic!("failed to parse the address: {addr}: {e:?}")); + if addr != 0 { + return true; + } + } + } + false +} + +fn kallsyms_find(symbol_name: &str) -> Option { + let file = File::open(PROC_KALLSYMS) + .unwrap_or_else(|e| panic!("failed to open {PROC_KALLSYMS}: {e:?}")); + for line in BufReader::new(file).lines() { + let line = + line.unwrap_or_else(|e| panic!("failed to read the line from {PROC_KALLSYMS}: {e:?}")); + let parts: Vec<&str> = line.split_whitespace().collect(); + if let [addr, _type, name, ..] = parts.as_slice() + && *name == symbol_name + { + let addr = u64::from_str_radix(addr, 16) + .unwrap_or_else(|e| panic!("failed to parse the address: {addr}: {e:?}")); + return Some(addr); + } + } + None +} + +/// Check if PERCPU DATASEC exists in kernel BTF. +/// Required for `bpf_this_cpu_ptr`/`bpf_per_cpu_ptr` to work. +fn btf_has_percpu_datasec(btf: &Btf) -> bool { + use aya_obj::btf::BtfKind; + btf.id_by_type_name_kind(".data..percpu", BtfKind::DataSec) + .is_ok() +} + +mod output_keys { + pub(super) const WEAK_TYPED: u32 = 2; + pub(super) const KFUNC_ADDR: u32 = 3; + pub(super) const KFUNC_CALLED: u32 = 4; + pub(super) const TYPED_MARKER: u32 = 5; + pub(super) const KFUNC2_ADDR: u32 = 6; + pub(super) const KFUNC2_CALLED: u32 = 7; + pub(super) const TYPELESS_ADDR: u32 = 8; + pub(super) const WEAK_TYPELESS: u32 = 9; + pub(super) const TYPELESS_MARKER: u32 = 10; +} + +mod output_keys_strong { + pub(super) const TYPED_ADDR: u32 = 0; + pub(super) const TYPED_VALUE: u32 = 1; + pub(super) const PER_CPU_PTR_ADDR: u32 = 2; + pub(super) const PER_CPU_PTR_VALUE: u32 = 3; + pub(super) const MARKER: u32 = 4; +} + +/// Test STRONG typed ksym resolution with per-cpu helpers. +/// Tests: strong ksym (`bpf_prog_active`), `bpf_this_cpu_ptr`, `bpf_per_cpu_ptr`. +#[test] +fn ksyms_typed_strong() { + let kernel_version = KernelVersion::current().unwrap(); + if kernel_version < KernelVersion::new(5, 10, 0) { + eprintln!("skipping test on kernel {kernel_version:?}, typed ksyms require kernel >= 5.10"); + return; + } + + let btf = Btf::from_sys_fs().unwrap(); + + if !btf_has_percpu_datasec(&btf) { + eprintln!("skipping test, no PERCPU DATASEC in kernel BTF"); + return; + } + + // Strong typed ksyms require the symbol to be in kallsyms. + // With CONFIG_KALLSYMS_ALL=n, only function symbols are exported, + // not variables like bpf_prog_active. The verifier uses + // bpf_kallsyms_lookup_name() internally and will reject the program + // if the symbol address cannot be resolved. + if !kallsyms_available() { + eprintln!("skipping test, kallsyms not available"); + return; + } + if kallsyms_find("bpf_prog_active").is_none() { + eprintln!( + "skipping test, bpf_prog_active kallsyms not available \ + (usually due to CONFIG_KALLSYMS_ALL kernel configuration disabled)" + ); + return; + } + + let mut bpf = Ebpf::load(crate::KSYMS_STRONG).unwrap(); + + let prog: &mut BtfTracePoint = bpf + .program_mut("ksyms_typed_strong") + .unwrap() + .try_into() + .unwrap(); + + if let Err(e) = prog.load(SYS_ENTER, &btf) { + panic!("failed to load program {SYS_ENTER}: {e:?}"); + } + prog.attach().unwrap(); + + // Trigger the tracepoint + drop(std::fs::metadata("/")); + + let output: Array<_, u64> = Array::try_from(bpf.map("strong_output").unwrap()).unwrap(); + + // Verify BPF program executed + let marker = output.get(&output_keys_strong::MARKER, 0).unwrap(); + assert_eq!(marker, 0xBEEFCAFE, "BPF program did not execute"); + + // bpf_this_cpu_ptr: address should be non-zero + let typed_addr = output.get(&output_keys_strong::TYPED_ADDR, 0).unwrap(); + assert!(typed_addr != 0, "strong ksym address should be non-zero"); + + // bpf_this_cpu_ptr: value should be >= 0 (like libbpf) + let typed_value = output.get(&output_keys_strong::TYPED_VALUE, 0).unwrap(); + let signed_value = typed_value as i32; + assert!( + signed_value >= 0, + "bpf_prog_active should be >= 0, got {signed_value}" + ); + + // bpf_per_cpu_ptr: address should be non-zero + let per_cpu_addr = output + .get(&output_keys_strong::PER_CPU_PTR_ADDR, 0) + .unwrap(); + assert!( + per_cpu_addr != 0, + "bpf_per_cpu_ptr address should be non-zero" + ); + + // bpf_per_cpu_ptr: value should be >= 0 + let per_cpu_value = output + .get(&output_keys_strong::PER_CPU_PTR_VALUE, 0) + .unwrap(); + let signed_per_cpu = per_cpu_value as i32; + assert!( + signed_per_cpu >= 0 || per_cpu_value != 0xFFFFFFFF, + "bpf_per_cpu_ptr value invalid, got {per_cpu_value:#x}" + ); +} + +/// Test WEAK typed ksym resolution and kfunc calls. +/// Tests: weak nonexistent typed ksym = 0, kfunc resolution. +#[test] +fn ksyms_typed_weak() { + let kernel_version = KernelVersion::current().unwrap(); + if kernel_version < KernelVersion::new(5, 10, 0) { + eprintln!("skipping test on kernel {kernel_version:?}, typed ksyms require kernel >= 5.10"); + return; + } + + let mut bpf = Ebpf::load(crate::KSYMS).unwrap(); + let prog: &mut BtfTracePoint = bpf + .program_mut("ksyms_typed_weak") + .unwrap() + .try_into() + .unwrap(); + + let btf = Btf::from_sys_fs().unwrap(); + if let Err(e) = prog.load(SYS_ENTER, &btf) { + panic!("failed to load program {SYS_ENTER}: {e:?}"); + } + prog.attach().unwrap(); + + // Trigger the tracepoint + drop(std::fs::metadata("/")); + + let output: Array<_, u64> = Array::try_from(bpf.map("output").unwrap()).unwrap(); + + // Verify BPF program executed + let marker = output.get(&output_keys::TYPED_MARKER, 0).unwrap(); + assert_eq!(marker, 0xDEADBEEF, "BPF program did not execute"); + + // Weak typed ksym (nonexistent) should resolve to 0 + let weak_typed = output.get(&output_keys::WEAK_TYPED, 0).unwrap(); + assert_eq!( + weak_typed, 0, + "weak nonexistent typed ksym should be 0, got {weak_typed}" + ); + + // Kfunc resolution - availability depends on kernel config, not just version + let kfunc_addr = output.get(&output_keys::KFUNC_ADDR, 0).unwrap(); + let kfunc_called = output.get(&output_keys::KFUNC_CALLED, 0).unwrap(); + let kfunc2_addr = output.get(&output_keys::KFUNC2_ADDR, 0).unwrap(); + let kfunc2_called = output.get(&output_keys::KFUNC2_CALLED, 0).unwrap(); + + // Consistency: if resolved, must be callable + if kfunc_addr != 0 { + assert_eq!(kfunc_called, 1, "kfunc resolved but not called"); + } + if kfunc2_addr != 0 { + assert_eq!(kfunc2_called, 1, "kfunc2 resolved but not called"); + } +} + +/// Test typeless ksym resolution (kallsyms-based). +/// Tests: `init_task` address + kallsyms cross-check, weak nonexistent = 0. +#[test] +fn ksyms_typeless() { + let kernel_version = KernelVersion::current().unwrap(); + if kernel_version < KernelVersion::new(5, 10, 0) { + eprintln!( + "skipping test on kernel {kernel_version:?}, typeless ksyms require kernel >= 5.10" + ); + return; + } + + let kallsyms_ok = kallsyms_available(); + let expected_addr = if kallsyms_ok { + kallsyms_find("init_task") + } else { + None + }; + + let mut bpf = Ebpf::load(crate::KSYMS).unwrap(); + let prog: &mut BtfTracePoint = bpf + .program_mut("ksyms_typeless") + .unwrap() + .try_into() + .unwrap(); + + let btf = Btf::from_sys_fs().unwrap(); + if let Err(e) = prog.load(SYS_ENTER, &btf) { + panic!("failed to load program {SYS_ENTER}: {e:?}"); + } + prog.attach().unwrap(); + + // Trigger the tracepoint + drop(std::fs::metadata("/")); + + let output: Array<_, u64> = Array::try_from(bpf.map("output").unwrap()).unwrap(); + + // Verify BPF program executed + let marker = output.get(&output_keys::TYPELESS_MARKER, 0).unwrap(); + assert_eq!(marker, 0xCAFEBABE, "BPF program did not execute"); + + // Typeless ksym: init_task - cross-verify with kallsyms when available + let typeless_addr = output.get(&output_keys::TYPELESS_ADDR, 0).unwrap(); + if typeless_addr != 0 { + if let Some(kallsyms_addr) = expected_addr { + assert_eq!( + typeless_addr, kallsyms_addr, + "BPF-resolved init_task ({typeless_addr:#x}) != kallsyms ({kallsyms_addr:#x})" + ); + } + } + + // Weak typeless ksym (nonexistent) should be 0 + let weak_typeless = output.get(&output_keys::WEAK_TYPELESS, 0).unwrap(); + assert_eq!( + weak_typeless, 0, + "weak nonexistent typeless ksym should be 0, got {weak_typeless:#x}" + ); +} diff --git a/xtask/public-api/aya-obj.txt b/xtask/public-api/aya-obj.txt index 0b34843f9..cb6265bbe 100644 --- a/xtask/public-api/aya-obj.txt +++ b/xtask/public-api/aya-obj.txt @@ -40,6 +40,8 @@ pub aya_obj::btf::BtfError::UnknownSectionSize pub aya_obj::btf::BtfError::UnknownSectionSize::section_name: alloc::string::String impl core::convert::From for aya_obj::ParseError pub fn aya_obj::ParseError::from(source: aya_obj::btf::BtfError) -> Self +impl core::convert::From for aya_obj::extern_types::KsymsError +pub fn aya_obj::extern_types::KsymsError::from(source: aya_obj::btf::BtfError) -> Self impl core::error::Error for aya_obj::btf::BtfError pub fn aya_obj::btf::BtfError::source(&self) -> core::option::Option<&(dyn core::error::Error + 'static)> impl core::fmt::Debug for aya_obj::btf::BtfError @@ -1373,6 +1375,139 @@ impl core::clone::CloneToUninit for aya_obj::btf::Volatile where T: core::clo pub unsafe fn aya_obj::btf::Volatile::clone_to_uninit(&self, dest: *mut u8) impl core::convert::From for aya_obj::btf::Volatile pub fn aya_obj::btf::Volatile::from(t: T) -> T +pub mod aya_obj::extern_types +pub enum aya_obj::extern_types::ExternType +pub aya_obj::extern_types::ExternType::Kconfig +pub aya_obj::extern_types::ExternType::Ksym +impl core::clone::Clone for aya_obj::extern_types::ExternType +pub fn aya_obj::extern_types::ExternType::clone(&self) -> aya_obj::extern_types::ExternType +impl core::cmp::Eq for aya_obj::extern_types::ExternType +impl core::cmp::PartialEq for aya_obj::extern_types::ExternType +pub fn aya_obj::extern_types::ExternType::eq(&self, other: &aya_obj::extern_types::ExternType) -> bool +impl core::fmt::Debug for aya_obj::extern_types::ExternType +pub fn aya_obj::extern_types::ExternType::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::marker::Copy for aya_obj::extern_types::ExternType +impl core::marker::StructuralPartialEq for aya_obj::extern_types::ExternType +impl core::marker::Freeze for aya_obj::extern_types::ExternType +impl core::marker::Send for aya_obj::extern_types::ExternType +impl core::marker::Sync for aya_obj::extern_types::ExternType +impl core::marker::Unpin for aya_obj::extern_types::ExternType +impl core::panic::unwind_safe::RefUnwindSafe for aya_obj::extern_types::ExternType +impl core::panic::unwind_safe::UnwindSafe for aya_obj::extern_types::ExternType +impl core::convert::Into for aya_obj::extern_types::ExternType where U: core::convert::From +pub fn aya_obj::extern_types::ExternType::into(self) -> U +impl core::convert::TryFrom for aya_obj::extern_types::ExternType where U: core::convert::Into +pub type aya_obj::extern_types::ExternType::Error = core::convert::Infallible +pub fn aya_obj::extern_types::ExternType::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya_obj::extern_types::ExternType where U: core::convert::TryFrom +pub type aya_obj::extern_types::ExternType::Error = >::Error +pub fn aya_obj::extern_types::ExternType::try_into(self) -> core::result::Result>::Error> +impl alloc::borrow::ToOwned for aya_obj::extern_types::ExternType where T: core::clone::Clone +pub type aya_obj::extern_types::ExternType::Owned = T +pub fn aya_obj::extern_types::ExternType::clone_into(&self, target: &mut T) +pub fn aya_obj::extern_types::ExternType::to_owned(&self) -> T +impl core::any::Any for aya_obj::extern_types::ExternType where T: 'static + ?core::marker::Sized +pub fn aya_obj::extern_types::ExternType::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya_obj::extern_types::ExternType where T: ?core::marker::Sized +pub fn aya_obj::extern_types::ExternType::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya_obj::extern_types::ExternType where T: ?core::marker::Sized +pub fn aya_obj::extern_types::ExternType::borrow_mut(&mut self) -> &mut T +impl core::clone::CloneToUninit for aya_obj::extern_types::ExternType where T: core::clone::Clone +pub unsafe fn aya_obj::extern_types::ExternType::clone_to_uninit(&self, dest: *mut u8) +impl core::convert::From for aya_obj::extern_types::ExternType +pub fn aya_obj::extern_types::ExternType::from(t: T) -> T +pub enum aya_obj::extern_types::KsymsError +pub aya_obj::extern_types::KsymsError::AmbiguousResolution +pub aya_obj::extern_types::KsymsError::AmbiguousResolution::first_addr: u64 +pub aya_obj::extern_types::KsymsError::AmbiguousResolution::name: alloc::string::String +pub aya_obj::extern_types::KsymsError::AmbiguousResolution::second_addr: u64 +pub aya_obj::extern_types::KsymsError::BtfError(aya_obj::btf::BtfError) +pub aya_obj::extern_types::KsymsError::FunctionNotFound +pub aya_obj::extern_types::KsymsError::FunctionNotFound::name: alloc::string::String +pub aya_obj::extern_types::KsymsError::IncompatibleFunctionSignature +pub aya_obj::extern_types::KsymsError::IncompatibleFunctionSignature::name: alloc::string::String +pub aya_obj::extern_types::KsymsError::IncompatibleVariableType +pub aya_obj::extern_types::KsymsError::IncompatibleVariableType::name: alloc::string::String +pub aya_obj::extern_types::KsymsError::InvalidExternType +pub aya_obj::extern_types::KsymsError::InvalidExternType::name: alloc::string::String +pub aya_obj::extern_types::KsymsError::KallsymsParseError(alloc::string::String) +pub aya_obj::extern_types::KsymsError::KallsymsReadError(std::io::error::Error) +pub aya_obj::extern_types::KsymsError::NoBtf +pub aya_obj::extern_types::KsymsError::UnresolvedExtern +pub aya_obj::extern_types::KsymsError::UnresolvedExtern::name: alloc::string::String +pub aya_obj::extern_types::KsymsError::VariableNotFound +pub aya_obj::extern_types::KsymsError::VariableNotFound::name: alloc::string::String +impl core::convert::From for aya_obj::extern_types::KsymsError +pub fn aya_obj::extern_types::KsymsError::from(source: aya_obj::btf::BtfError) -> Self +impl core::convert::From for aya_obj::ParseError +pub fn aya_obj::ParseError::from(source: aya_obj::extern_types::KsymsError) -> Self +impl core::convert::From for aya_obj::extern_types::KsymsError +pub fn aya_obj::extern_types::KsymsError::from(source: std::io::error::Error) -> Self +impl core::error::Error for aya_obj::extern_types::KsymsError +pub fn aya_obj::extern_types::KsymsError::source(&self) -> core::option::Option<&(dyn core::error::Error + 'static)> +impl core::fmt::Debug for aya_obj::extern_types::KsymsError +pub fn aya_obj::extern_types::KsymsError::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::fmt::Display for aya_obj::extern_types::KsymsError +pub fn aya_obj::extern_types::KsymsError::fmt(&self, __formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::marker::Freeze for aya_obj::extern_types::KsymsError +impl core::marker::Send for aya_obj::extern_types::KsymsError +impl core::marker::Sync for aya_obj::extern_types::KsymsError +impl core::marker::Unpin for aya_obj::extern_types::KsymsError +impl !core::panic::unwind_safe::RefUnwindSafe for aya_obj::extern_types::KsymsError +impl !core::panic::unwind_safe::UnwindSafe for aya_obj::extern_types::KsymsError +impl core::convert::Into for aya_obj::extern_types::KsymsError where U: core::convert::From +pub fn aya_obj::extern_types::KsymsError::into(self) -> U +impl core::convert::TryFrom for aya_obj::extern_types::KsymsError where U: core::convert::Into +pub type aya_obj::extern_types::KsymsError::Error = core::convert::Infallible +pub fn aya_obj::extern_types::KsymsError::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya_obj::extern_types::KsymsError where U: core::convert::TryFrom +pub type aya_obj::extern_types::KsymsError::Error = >::Error +pub fn aya_obj::extern_types::KsymsError::try_into(self) -> core::result::Result>::Error> +impl alloc::string::ToString for aya_obj::extern_types::KsymsError where T: core::fmt::Display + ?core::marker::Sized +pub fn aya_obj::extern_types::KsymsError::to_string(&self) -> alloc::string::String +impl core::any::Any for aya_obj::extern_types::KsymsError where T: 'static + ?core::marker::Sized +pub fn aya_obj::extern_types::KsymsError::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya_obj::extern_types::KsymsError where T: ?core::marker::Sized +pub fn aya_obj::extern_types::KsymsError::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya_obj::extern_types::KsymsError where T: ?core::marker::Sized +pub fn aya_obj::extern_types::KsymsError::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya_obj::extern_types::KsymsError +pub fn aya_obj::extern_types::KsymsError::from(t: T) -> T +pub struct aya_obj::extern_types::ExternCollection +impl core::clone::Clone for aya_obj::extern_types::ExternCollection +pub fn aya_obj::extern_types::ExternCollection::clone(&self) -> aya_obj::extern_types::ExternCollection +impl core::default::Default for aya_obj::extern_types::ExternCollection +pub fn aya_obj::extern_types::ExternCollection::default() -> aya_obj::extern_types::ExternCollection +impl core::fmt::Debug for aya_obj::extern_types::ExternCollection +pub fn aya_obj::extern_types::ExternCollection::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::marker::Freeze for aya_obj::extern_types::ExternCollection +impl core::marker::Send for aya_obj::extern_types::ExternCollection +impl core::marker::Sync for aya_obj::extern_types::ExternCollection +impl core::marker::Unpin for aya_obj::extern_types::ExternCollection +impl core::panic::unwind_safe::RefUnwindSafe for aya_obj::extern_types::ExternCollection +impl core::panic::unwind_safe::UnwindSafe for aya_obj::extern_types::ExternCollection +impl core::convert::Into for aya_obj::extern_types::ExternCollection where U: core::convert::From +pub fn aya_obj::extern_types::ExternCollection::into(self) -> U +impl core::convert::TryFrom for aya_obj::extern_types::ExternCollection where U: core::convert::Into +pub type aya_obj::extern_types::ExternCollection::Error = core::convert::Infallible +pub fn aya_obj::extern_types::ExternCollection::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya_obj::extern_types::ExternCollection where U: core::convert::TryFrom +pub type aya_obj::extern_types::ExternCollection::Error = >::Error +pub fn aya_obj::extern_types::ExternCollection::try_into(self) -> core::result::Result>::Error> +impl alloc::borrow::ToOwned for aya_obj::extern_types::ExternCollection where T: core::clone::Clone +pub type aya_obj::extern_types::ExternCollection::Owned = T +pub fn aya_obj::extern_types::ExternCollection::clone_into(&self, target: &mut T) +pub fn aya_obj::extern_types::ExternCollection::to_owned(&self) -> T +impl core::any::Any for aya_obj::extern_types::ExternCollection where T: 'static + ?core::marker::Sized +pub fn aya_obj::extern_types::ExternCollection::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya_obj::extern_types::ExternCollection where T: ?core::marker::Sized +pub fn aya_obj::extern_types::ExternCollection::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya_obj::extern_types::ExternCollection where T: ?core::marker::Sized +pub fn aya_obj::extern_types::ExternCollection::borrow_mut(&mut self) -> &mut T +impl core::clone::CloneToUninit for aya_obj::extern_types::ExternCollection where T: core::clone::Clone +pub unsafe fn aya_obj::extern_types::ExternCollection::clone_to_uninit(&self, dest: *mut u8) +impl core::convert::From for aya_obj::extern_types::ExternCollection +pub fn aya_obj::extern_types::ExternCollection::from(t: T) -> T pub mod aya_obj::generated pub mod aya_obj::generated::bpf_core_relo_kind pub const aya_obj::generated::bpf_core_relo_kind::BPF_CORE_ENUMVAL_EXISTS: aya_obj::generated::bpf_core_relo_kind::Type @@ -8538,11 +8673,18 @@ impl core::convert::From for aya_obj::EbpfSectionKind pub fn aya_obj::EbpfSectionKind::from(t: T) -> T pub enum aya_obj::obj::ParseError pub aya_obj::obj::ParseError::BtfError(aya_obj::btf::BtfError) +pub aya_obj::obj::ParseError::DuplicateExtern +pub aya_obj::obj::ParseError::DuplicateExtern::name: alloc::string::String pub aya_obj::obj::ParseError::ElfError(object::read::Error) +pub aya_obj::obj::ParseError::FuncInKconfig +pub aya_obj::obj::ParseError::FuncInKconfig::name: alloc::string::String +pub aya_obj::obj::ParseError::FuncInKconfig::section: alloc::string::String pub aya_obj::obj::ParseError::InvalidGlobalData pub aya_obj::obj::ParseError::InvalidGlobalData::data_size: usize pub aya_obj::obj::ParseError::InvalidGlobalData::name: alloc::string::String pub aya_obj::obj::ParseError::InvalidGlobalData::sym_size: u64 +pub aya_obj::obj::ParseError::InvalidKconfigSize +pub aya_obj::obj::ParseError::InvalidKconfigSize::name: alloc::string::String pub aya_obj::obj::ParseError::InvalidKernelVersion pub aya_obj::obj::ParseError::InvalidKernelVersion::data: alloc::vec::Vec pub aya_obj::obj::ParseError::InvalidLicense @@ -8555,6 +8697,7 @@ pub aya_obj::obj::ParseError::InvalidProgramSection::section: alloc::string::Str pub aya_obj::obj::ParseError::InvalidSymbol pub aya_obj::obj::ParseError::InvalidSymbol::index: usize pub aya_obj::obj::ParseError::InvalidSymbol::name: core::option::Option +pub aya_obj::obj::ParseError::KsymsError(aya_obj::extern_types::KsymsError) pub aya_obj::obj::ParseError::MapNotFound pub aya_obj::obj::ParseError::MapNotFound::index: usize pub aya_obj::obj::ParseError::MapSymbolNameNotFound @@ -8576,8 +8719,12 @@ pub aya_obj::obj::ParseError::UnknownSymbol pub aya_obj::obj::ParseError::UnknownSymbol::address: u64 pub aya_obj::obj::ParseError::UnknownSymbol::section_index: usize pub aya_obj::obj::ParseError::UnsupportedRelocationTarget +pub aya_obj::obj::ParseError::WeakFuncNotSupported +pub aya_obj::obj::ParseError::WeakFuncNotSupported::name: alloc::string::String impl core::convert::From for aya_obj::ParseError pub fn aya_obj::ParseError::from(source: aya_obj::btf::BtfError) -> Self +impl core::convert::From for aya_obj::ParseError +pub fn aya_obj::ParseError::from(source: aya_obj::extern_types::KsymsError) -> Self impl core::error::Error for aya_obj::ParseError pub fn aya_obj::ParseError::source(&self) -> core::option::Option<&(dyn core::error::Error + 'static)> impl core::fmt::Debug for aya_obj::ParseError @@ -8799,6 +8946,8 @@ pub aya_obj::obj::Object::license: alloc::ffi::c_str::CString pub aya_obj::obj::Object::maps: std::collections::hash::map::HashMap pub aya_obj::obj::Object::programs: std::collections::hash::map::HashMap impl aya_obj::Object +pub fn aya_obj::Object::collect_ksyms_from_btf(&mut self) -> core::result::Result<(), aya_obj::extern_types::KsymsError> +impl aya_obj::Object pub fn aya_obj::Object::fixup_and_sanitize_btf(&mut self, features: &aya_obj::btf::BtfFeatures) -> core::result::Result, aya_obj::btf::BtfError> impl aya_obj::Object pub fn aya_obj::Object::has_btf_relocations(&self) -> bool @@ -8809,7 +8958,11 @@ 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_externs(&mut self) -> 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 aya_obj::Object +pub fn aya_obj::Object::resolve_typed_externs(&mut self, kernel_btf: &mut aya_obj::btf::Btf) -> core::result::Result<(), aya_obj::extern_types::KsymsError> +pub fn aya_obj::Object::resolve_typeless_externs(&mut self) -> core::result::Result<(), aya_obj::extern_types::KsymsError> impl core::clone::Clone for aya_obj::Object pub fn aya_obj::Object::clone(&self) -> aya_obj::Object impl core::fmt::Debug for aya_obj::Object @@ -9225,9 +9378,13 @@ impl core::convert::From for aya_obj::programs::xdp::XdpAttachType pub fn aya_obj::programs::xdp::XdpAttachType::from(t: T) -> T pub mod aya_obj::relocation pub enum aya_obj::relocation::RelocationError +pub aya_obj::relocation::RelocationError::ExternNotFound +pub aya_obj::relocation::RelocationError::ExternNotFound::name: alloc::string::String pub aya_obj::relocation::RelocationError::InvalidRelocationOffset pub aya_obj::relocation::RelocationError::InvalidRelocationOffset::offset: u64 pub aya_obj::relocation::RelocationError::InvalidRelocationOffset::relocation_number: usize +pub aya_obj::relocation::RelocationError::MissingKallsymsAddr +pub aya_obj::relocation::RelocationError::MissingKallsymsAddr::name: alloc::string::String pub aya_obj::relocation::RelocationError::SectionNotFound pub aya_obj::relocation::RelocationError::SectionNotFound::section_index: usize pub aya_obj::relocation::RelocationError::SectionNotFound::symbol_index: usize @@ -9240,6 +9397,10 @@ pub aya_obj::relocation::RelocationError::UnknownProgram::address: u64 pub aya_obj::relocation::RelocationError::UnknownProgram::section_index: usize pub aya_obj::relocation::RelocationError::UnknownSymbol pub aya_obj::relocation::RelocationError::UnknownSymbol::index: usize +pub aya_obj::relocation::RelocationError::UnresolvableSymbol +pub aya_obj::relocation::RelocationError::UnresolvableSymbol::name: alloc::string::String +pub aya_obj::relocation::RelocationError::UnresolvedExtern +pub aya_obj::relocation::RelocationError::UnresolvedExtern::name: alloc::string::String impl core::error::Error for aya_obj::relocation::RelocationError impl core::fmt::Debug for aya_obj::relocation::RelocationError pub fn aya_obj::relocation::RelocationError::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result @@ -9350,6 +9511,63 @@ impl core::clone::CloneToUninit for aya_obj::EbpfSectionKind where T: core::c pub unsafe fn aya_obj::EbpfSectionKind::clone_to_uninit(&self, dest: *mut u8) impl core::convert::From for aya_obj::EbpfSectionKind pub fn aya_obj::EbpfSectionKind::from(t: T) -> T +pub enum aya_obj::KsymsError +pub aya_obj::KsymsError::AmbiguousResolution +pub aya_obj::KsymsError::AmbiguousResolution::first_addr: u64 +pub aya_obj::KsymsError::AmbiguousResolution::name: alloc::string::String +pub aya_obj::KsymsError::AmbiguousResolution::second_addr: u64 +pub aya_obj::KsymsError::BtfError(aya_obj::btf::BtfError) +pub aya_obj::KsymsError::FunctionNotFound +pub aya_obj::KsymsError::FunctionNotFound::name: alloc::string::String +pub aya_obj::KsymsError::IncompatibleFunctionSignature +pub aya_obj::KsymsError::IncompatibleFunctionSignature::name: alloc::string::String +pub aya_obj::KsymsError::IncompatibleVariableType +pub aya_obj::KsymsError::IncompatibleVariableType::name: alloc::string::String +pub aya_obj::KsymsError::InvalidExternType +pub aya_obj::KsymsError::InvalidExternType::name: alloc::string::String +pub aya_obj::KsymsError::KallsymsParseError(alloc::string::String) +pub aya_obj::KsymsError::KallsymsReadError(std::io::error::Error) +pub aya_obj::KsymsError::NoBtf +pub aya_obj::KsymsError::UnresolvedExtern +pub aya_obj::KsymsError::UnresolvedExtern::name: alloc::string::String +pub aya_obj::KsymsError::VariableNotFound +pub aya_obj::KsymsError::VariableNotFound::name: alloc::string::String +impl core::convert::From for aya_obj::extern_types::KsymsError +pub fn aya_obj::extern_types::KsymsError::from(source: aya_obj::btf::BtfError) -> Self +impl core::convert::From for aya_obj::ParseError +pub fn aya_obj::ParseError::from(source: aya_obj::extern_types::KsymsError) -> Self +impl core::convert::From for aya_obj::extern_types::KsymsError +pub fn aya_obj::extern_types::KsymsError::from(source: std::io::error::Error) -> Self +impl core::error::Error for aya_obj::extern_types::KsymsError +pub fn aya_obj::extern_types::KsymsError::source(&self) -> core::option::Option<&(dyn core::error::Error + 'static)> +impl core::fmt::Debug for aya_obj::extern_types::KsymsError +pub fn aya_obj::extern_types::KsymsError::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::fmt::Display for aya_obj::extern_types::KsymsError +pub fn aya_obj::extern_types::KsymsError::fmt(&self, __formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::marker::Freeze for aya_obj::extern_types::KsymsError +impl core::marker::Send for aya_obj::extern_types::KsymsError +impl core::marker::Sync for aya_obj::extern_types::KsymsError +impl core::marker::Unpin for aya_obj::extern_types::KsymsError +impl !core::panic::unwind_safe::RefUnwindSafe for aya_obj::extern_types::KsymsError +impl !core::panic::unwind_safe::UnwindSafe for aya_obj::extern_types::KsymsError +impl core::convert::Into for aya_obj::extern_types::KsymsError where U: core::convert::From +pub fn aya_obj::extern_types::KsymsError::into(self) -> U +impl core::convert::TryFrom for aya_obj::extern_types::KsymsError where U: core::convert::Into +pub type aya_obj::extern_types::KsymsError::Error = core::convert::Infallible +pub fn aya_obj::extern_types::KsymsError::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya_obj::extern_types::KsymsError where U: core::convert::TryFrom +pub type aya_obj::extern_types::KsymsError::Error = >::Error +pub fn aya_obj::extern_types::KsymsError::try_into(self) -> core::result::Result>::Error> +impl alloc::string::ToString for aya_obj::extern_types::KsymsError where T: core::fmt::Display + ?core::marker::Sized +pub fn aya_obj::extern_types::KsymsError::to_string(&self) -> alloc::string::String +impl core::any::Any for aya_obj::extern_types::KsymsError where T: 'static + ?core::marker::Sized +pub fn aya_obj::extern_types::KsymsError::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya_obj::extern_types::KsymsError where T: ?core::marker::Sized +pub fn aya_obj::extern_types::KsymsError::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya_obj::extern_types::KsymsError where T: ?core::marker::Sized +pub fn aya_obj::extern_types::KsymsError::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya_obj::extern_types::KsymsError +pub fn aya_obj::extern_types::KsymsError::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) @@ -9401,11 +9619,18 @@ impl core::convert::From for aya_obj::maps::Map pub fn aya_obj::maps::Map::from(t: T) -> T pub enum aya_obj::ParseError pub aya_obj::ParseError::BtfError(aya_obj::btf::BtfError) +pub aya_obj::ParseError::DuplicateExtern +pub aya_obj::ParseError::DuplicateExtern::name: alloc::string::String pub aya_obj::ParseError::ElfError(object::read::Error) +pub aya_obj::ParseError::FuncInKconfig +pub aya_obj::ParseError::FuncInKconfig::name: alloc::string::String +pub aya_obj::ParseError::FuncInKconfig::section: alloc::string::String pub aya_obj::ParseError::InvalidGlobalData pub aya_obj::ParseError::InvalidGlobalData::data_size: usize pub aya_obj::ParseError::InvalidGlobalData::name: alloc::string::String pub aya_obj::ParseError::InvalidGlobalData::sym_size: u64 +pub aya_obj::ParseError::InvalidKconfigSize +pub aya_obj::ParseError::InvalidKconfigSize::name: alloc::string::String pub aya_obj::ParseError::InvalidKernelVersion pub aya_obj::ParseError::InvalidKernelVersion::data: alloc::vec::Vec pub aya_obj::ParseError::InvalidLicense @@ -9418,6 +9643,7 @@ pub aya_obj::ParseError::InvalidProgramSection::section: alloc::string::String pub aya_obj::ParseError::InvalidSymbol pub aya_obj::ParseError::InvalidSymbol::index: usize pub aya_obj::ParseError::InvalidSymbol::name: core::option::Option +pub aya_obj::ParseError::KsymsError(aya_obj::extern_types::KsymsError) pub aya_obj::ParseError::MapNotFound pub aya_obj::ParseError::MapNotFound::index: usize pub aya_obj::ParseError::MapSymbolNameNotFound @@ -9439,8 +9665,12 @@ pub aya_obj::ParseError::UnknownSymbol pub aya_obj::ParseError::UnknownSymbol::address: u64 pub aya_obj::ParseError::UnknownSymbol::section_index: usize pub aya_obj::ParseError::UnsupportedRelocationTarget +pub aya_obj::ParseError::WeakFuncNotSupported +pub aya_obj::ParseError::WeakFuncNotSupported::name: alloc::string::String impl core::convert::From for aya_obj::ParseError pub fn aya_obj::ParseError::from(source: aya_obj::btf::BtfError) -> Self +impl core::convert::From for aya_obj::ParseError +pub fn aya_obj::ParseError::from(source: aya_obj::extern_types::KsymsError) -> Self impl core::error::Error for aya_obj::ParseError pub fn aya_obj::ParseError::source(&self) -> core::option::Option<&(dyn core::error::Error + 'static)> impl core::fmt::Debug for aya_obj::ParseError @@ -9662,6 +9892,8 @@ pub aya_obj::Object::license: alloc::ffi::c_str::CString pub aya_obj::Object::maps: std::collections::hash::map::HashMap pub aya_obj::Object::programs: std::collections::hash::map::HashMap impl aya_obj::Object +pub fn aya_obj::Object::collect_ksyms_from_btf(&mut self) -> core::result::Result<(), aya_obj::extern_types::KsymsError> +impl aya_obj::Object pub fn aya_obj::Object::fixup_and_sanitize_btf(&mut self, features: &aya_obj::btf::BtfFeatures) -> core::result::Result, aya_obj::btf::BtfError> impl aya_obj::Object pub fn aya_obj::Object::has_btf_relocations(&self) -> bool @@ -9672,7 +9904,11 @@ 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_externs(&mut self) -> 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 aya_obj::Object +pub fn aya_obj::Object::resolve_typed_externs(&mut self, kernel_btf: &mut aya_obj::btf::Btf) -> core::result::Result<(), aya_obj::extern_types::KsymsError> +pub fn aya_obj::Object::resolve_typeless_externs(&mut self) -> core::result::Result<(), aya_obj::extern_types::KsymsError> impl core::clone::Clone for aya_obj::Object pub fn aya_obj::Object::clone(&self) -> aya_obj::Object impl core::fmt::Debug for aya_obj::Object diff --git a/xtask/public-api/aya.txt b/xtask/public-api/aya.txt index 80e8ecfb0..811214b44 100644 --- a/xtask/public-api/aya.txt +++ b/xtask/public-api/aya.txt @@ -10708,6 +10708,7 @@ pub aya::EbpfError::BtfRelocationError(aya_obj::btf::relocation::BtfRelocationEr pub aya::EbpfError::FileError pub aya::EbpfError::FileError::error: std::io::error::Error pub aya::EbpfError::FileError::path: std::path::PathBuf +pub aya::EbpfError::KsymsError(aya_obj::extern_types::KsymsError) pub aya::EbpfError::MapError(aya::maps::MapError) pub aya::EbpfError::NoBTF pub aya::EbpfError::ParseError(aya_obj::obj::ParseError) @@ -10723,6 +10724,8 @@ impl core::convert::From for aya::EbpfError pub fn aya::EbpfError::from(source: aya_obj::btf::btf::BtfError) -> Self impl core::convert::From for aya::EbpfError pub fn aya::EbpfError::from(source: aya_obj::btf::relocation::BtfRelocationError) -> Self +impl core::convert::From for aya::EbpfError +pub fn aya::EbpfError::from(source: aya_obj::extern_types::KsymsError) -> Self impl core::convert::From for aya::EbpfError pub fn aya::EbpfError::from(source: aya_obj::obj::ParseError) -> Self impl core::convert::From for aya::EbpfError