mirror of https://github.com/aya-rs/aya
aya: Support loading programs with ksyms
parent
03e8487177
commit
de90b7df1f
@ -0,0 +1,497 @@
|
|||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
format,
|
||||||
|
string::{String, ToString as _},
|
||||||
|
vec::Vec,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
Object,
|
||||||
|
btf::{Btf, BtfError, BtfKind, BtfType},
|
||||||
|
};
|
||||||
|
|
||||||
|
impl Object {
|
||||||
|
/// Resolve all extern kernel symbols (functions and variables) against kernel BTF
|
||||||
|
///
|
||||||
|
/// This is the main entry point for resolving extern symbols declared in .ksyms section.
|
||||||
|
/// It dispatches to separate handlers for functions and variables.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `kernel_btf` - Kernel BTF loaded from `/sys/kernel/btf/vmlinux`
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// Returns `Ok(())` if all non-weak extern symbols were successfully resolved.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use aya_obj::{Object, btf::Btf};
|
||||||
|
///
|
||||||
|
/// let mut obj = Object::parse(&data)?;
|
||||||
|
/// let kernel_btf = Btf::from_sys_fs()?;
|
||||||
|
///
|
||||||
|
/// // Resolve all extern kernel symbols (functions and variables)
|
||||||
|
/// obj.resolve_extern_ksyms(&kernel_btf)?;
|
||||||
|
/// ```
|
||||||
|
pub fn resolve_extern_ksyms(
|
||||||
|
&mut self,
|
||||||
|
kernel_btf: &Btf,
|
||||||
|
) -> std::result::Result<(), KsymResolveError> {
|
||||||
|
// Check if we have any externs to resolve
|
||||||
|
if self.externs.externs.is_empty() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let obj_btf = self.btf.as_ref().ok_or(KsymResolveError::NoBtf)?;
|
||||||
|
|
||||||
|
// Dispatch based on extern type (like libbpf does at line 8232-8236)
|
||||||
|
let mut resolutions = Vec::new();
|
||||||
|
|
||||||
|
for (name, extern_desc) in self.externs.iter() {
|
||||||
|
// Skip if extern has no type_id (typeless ksyms - not supported yet)
|
||||||
|
if extern_desc.type_id.is_none() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dispatch to appropriate resolver
|
||||||
|
let btf_type = obj_btf.type_by_id(extern_desc.btf_id)?;
|
||||||
|
let kernel_btf_id = match btf_type {
|
||||||
|
BtfType::Func(_) => {
|
||||||
|
if extern_desc.is_weak {
|
||||||
|
return Err(KsymResolveError::WeakExternFunctionUnsupported {
|
||||||
|
name: name.to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Resolve function
|
||||||
|
self.resolve_extern_function_internal(name, extern_desc, obj_btf, kernel_btf)?
|
||||||
|
}
|
||||||
|
BtfType::Var(_) => {
|
||||||
|
// Resolve variable
|
||||||
|
self.resolve_extern_variable_internal(name, extern_desc, obj_btf, kernel_btf)?
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err(KsymResolveError::InvalidExternType { name: name.clone() });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Collect resolution if not None (None means weak extern not found)
|
||||||
|
if let Some(btf_id) = kernel_btf_id {
|
||||||
|
resolutions.push((name.clone(), btf_id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply all resolutions
|
||||||
|
for (name, kernel_btf_id) in resolutions {
|
||||||
|
if let Some(ext) = self.externs.get_mut(&name) {
|
||||||
|
ext.kernel_btf_id = Some(kernel_btf_id);
|
||||||
|
ext.is_resolved = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.resolve_kallsyms()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_kallsyms(&mut self) -> std::result::Result<(), KsymResolveError> {
|
||||||
|
use std::{
|
||||||
|
fs::File,
|
||||||
|
io::{BufRead as _, BufReader},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Find all unresolved variable externs
|
||||||
|
let unresolved: Vec<String> = self
|
||||||
|
.externs
|
||||||
|
.externs
|
||||||
|
.iter()
|
||||||
|
.filter(|(_, ext)| {
|
||||||
|
ext.extern_type == ExternType::Ksym && !ext.is_func && !ext.is_resolved
|
||||||
|
})
|
||||||
|
.map(|(name, _)| name.clone())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if unresolved.is_empty() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read kallsyms
|
||||||
|
let file =
|
||||||
|
File::open("/proc/kallsyms").map_err(|e| KsymResolveError::KallsymsReadError {
|
||||||
|
error: e.to_string(),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let reader = BufReader::new(file);
|
||||||
|
let mut resolved_count = 0;
|
||||||
|
|
||||||
|
for line in reader.lines() {
|
||||||
|
let line = line.map_err(|e| KsymResolveError::KallsymsReadError {
|
||||||
|
error: e.to_string(),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// Parse: <addr> <type> <name> [<module>]
|
||||||
|
let parts: Vec<&str> = line.split_whitespace().collect();
|
||||||
|
if parts.len() < 3 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let addr_str = parts[0];
|
||||||
|
let sym_name = parts[2];
|
||||||
|
|
||||||
|
// Check if this symbol is one we need
|
||||||
|
if !unresolved.contains(&sym_name.to_string()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse address
|
||||||
|
let addr = u64::from_str_radix(addr_str, 16).map_err(|_| {
|
||||||
|
KsymResolveError::KallsymsReadError {
|
||||||
|
error: format!("invalid address: {}", addr_str),
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// Update extern descriptor
|
||||||
|
if let Some(ext) = self.externs.get_mut(sym_name) {
|
||||||
|
ext.ksym_addr = Some(addr);
|
||||||
|
ext.is_resolved = true;
|
||||||
|
resolved_count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Early exit if we've resolved everything
|
||||||
|
if resolved_count == unresolved.len() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for unresolved non-weak symbols
|
||||||
|
for (name, ext) in self.externs.externs.iter() {
|
||||||
|
if !ext.is_resolved && !ext.is_weak {
|
||||||
|
return Err(KsymResolveError::VariableNotFound { name: name.clone() });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_extern_function_internal(
|
||||||
|
&self,
|
||||||
|
name: &str,
|
||||||
|
extern_desc: &ExternDesc,
|
||||||
|
obj_btf: &Btf,
|
||||||
|
kernel_btf: &Btf,
|
||||||
|
) -> std::result::Result<Option<u32>, KsymResolveError> {
|
||||||
|
// Look up function in kernel BTF
|
||||||
|
let kernel_func_id = match kernel_btf.id_by_type_name_kind(name, BtfKind::Func) {
|
||||||
|
Ok(id) => id,
|
||||||
|
Err(_) => {
|
||||||
|
return Err(KsymResolveError::FunctionNotFound {
|
||||||
|
name: name.to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get kernel function prototype
|
||||||
|
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(KsymResolveError::BtfError(BtfError::UnexpectedBtfType {
|
||||||
|
type_id: kernel_func_id,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get local function prototype
|
||||||
|
let local_proto_id =
|
||||||
|
extern_desc
|
||||||
|
.type_id
|
||||||
|
.ok_or(KsymResolveError::BtfError(BtfError::UnknownBtfType {
|
||||||
|
type_id: 0,
|
||||||
|
}))?;
|
||||||
|
|
||||||
|
// Check compatibility
|
||||||
|
let compatible =
|
||||||
|
crate::btf::types_are_compatible(obj_btf, local_proto_id, kernel_btf, kernel_proto_id)?;
|
||||||
|
|
||||||
|
if !compatible {
|
||||||
|
return Err(KsymResolveError::IncompatibleFunctionSignature {
|
||||||
|
name: name.to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Some(kernel_func_id))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Internal: Resolve a single extern variable
|
||||||
|
/// Returns Some(btf_id) on success, None for weak externs not found
|
||||||
|
fn resolve_extern_variable_internal(
|
||||||
|
&self,
|
||||||
|
name: &str,
|
||||||
|
extern_desc: &ExternDesc,
|
||||||
|
obj_btf: &Btf,
|
||||||
|
kernel_btf: &Btf,
|
||||||
|
) -> std::result::Result<Option<u32>, KsymResolveError> {
|
||||||
|
// Look up variable in kernel BTF
|
||||||
|
let kernel_var_id = match kernel_btf.id_by_type_name_kind(name, BtfKind::Var) {
|
||||||
|
Ok(id) => id,
|
||||||
|
Err(_) => {
|
||||||
|
return Err(KsymResolveError::VariableNotFound {
|
||||||
|
name: name.to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get the variable's type (VAR points to actual type)
|
||||||
|
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(KsymResolveError::BtfError(BtfError::UnexpectedBtfType {
|
||||||
|
type_id: kernel_var_id,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get local variable's type
|
||||||
|
let local_type_id =
|
||||||
|
extern_desc
|
||||||
|
.type_id
|
||||||
|
.ok_or(KsymResolveError::BtfError(BtfError::UnknownBtfType {
|
||||||
|
type_id: 0,
|
||||||
|
}))?;
|
||||||
|
|
||||||
|
// Check type compatibility
|
||||||
|
let compatible =
|
||||||
|
crate::btf::types_are_compatible(obj_btf, local_type_id, kernel_btf, kernel_type_id)?;
|
||||||
|
|
||||||
|
if !compatible {
|
||||||
|
return Err(KsymResolveError::IncompatibleVariableType {
|
||||||
|
name: name.to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Some(kernel_var_id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Errors that can occur during ksym resolution
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum KsymResolveError {
|
||||||
|
/// A non-weak extern function was not found in kernel BTF
|
||||||
|
#[error("kernel function '{name}' not found in kernel BTF")]
|
||||||
|
FunctionNotFound {
|
||||||
|
/// The name of the function that was not found
|
||||||
|
name: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// A non-weak extern variable was not found in kernel BTF or kallsyms
|
||||||
|
#[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,
|
||||||
|
|
||||||
|
/// The object file has no BTF information
|
||||||
|
#[error("Weak extern functions are not supported")]
|
||||||
|
WeakExternFunctionUnsupported {
|
||||||
|
/// The name of the extern with weak type
|
||||||
|
name: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Could not read kallsyms entries
|
||||||
|
#[error("failed to read /proc/kallsyms: {error}")]
|
||||||
|
KallsymsReadError {
|
||||||
|
/// Could not read kallsyms entries
|
||||||
|
error: String,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Errors that can occur during extern symbol instruction patching
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum KsymPatchError {
|
||||||
|
/// An extern symbol was not found during instruction patching
|
||||||
|
#[error("extern symbol '{name}' not found in extern collection")]
|
||||||
|
ExternNotFound {
|
||||||
|
/// The name of the missing extern
|
||||||
|
name: 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,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Invalid instruction offset encountered
|
||||||
|
#[error("invalid instruction offset {offset} in function '{function_name}'")]
|
||||||
|
InvalidInstructionOffset {
|
||||||
|
/// The invalid offset
|
||||||
|
offset: usize,
|
||||||
|
/// The function name
|
||||||
|
function_name: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// An extern function call instruction was expected but not found
|
||||||
|
#[error("expected call instruction at offset {offset} in function '{function_name}'")]
|
||||||
|
ExpectedCallInstruction {
|
||||||
|
/// The instruction offset
|
||||||
|
offset: usize,
|
||||||
|
/// The function name
|
||||||
|
function_name: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// An ld_imm64 instruction was expected but not found
|
||||||
|
#[error("expected ld_imm64 instruction at offset {offset} in function '{function_name}'")]
|
||||||
|
ExpectedLdImm64Instruction {
|
||||||
|
/// The instruction offset
|
||||||
|
offset: usize,
|
||||||
|
/// The function name
|
||||||
|
function_name: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Program not found when trying to patch its instructions
|
||||||
|
#[error("program '{name}' not found")]
|
||||||
|
ProgramNotFound {
|
||||||
|
/// The program name
|
||||||
|
name: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Function not found when trying to patch its instructions
|
||||||
|
#[error("function not found for program at section {section_index}, address {address:#x}")]
|
||||||
|
FunctionNotFound {
|
||||||
|
/// The section index
|
||||||
|
section_index: usize,
|
||||||
|
/// The address
|
||||||
|
address: u64,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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)
|
||||||
|
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)
|
||||||
|
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<u32>,
|
||||||
|
|
||||||
|
/// For ksym variables: resolved kernel address
|
||||||
|
pub(crate) ksym_addr: Option<u64>,
|
||||||
|
|
||||||
|
/// For ksym: resolved type ID (after skipping modifiers/typedefs)
|
||||||
|
pub(crate) type_id: Option<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExternDesc {
|
||||||
|
pub(crate) fn new(
|
||||||
|
name: String,
|
||||||
|
extern_type: ExternType,
|
||||||
|
btf_id: u32,
|
||||||
|
is_weak: bool,
|
||||||
|
is_func: bool,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
name,
|
||||||
|
extern_type,
|
||||||
|
btf_id,
|
||||||
|
is_weak,
|
||||||
|
is_func,
|
||||||
|
is_resolved: false,
|
||||||
|
kernel_btf_id: None,
|
||||||
|
ksym_addr: None,
|
||||||
|
type_id: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Collection of extern symbols
|
||||||
|
#[derive(Debug, Default, Clone)]
|
||||||
|
pub struct ExternCollection {
|
||||||
|
/// Map of extern descriptors by name
|
||||||
|
pub(crate) externs: HashMap<String, ExternDesc>,
|
||||||
|
|
||||||
|
/// BTF ID of dummy ksym variable (if created)
|
||||||
|
pub(crate) dummy_ksym_var_id: Option<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<Item = (&String, &ExternDesc)> {
|
||||||
|
self.externs.iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
// clang-format off
|
||||||
|
#include <vmlinux.h>
|
||||||
|
#include <bpf/bpf_helpers.h>
|
||||||
|
#include <bpf/bpf_core_read.h>
|
||||||
|
#include <bpf/bpf_tracing.h>
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
char _license[] SEC("license") = "GPL";
|
||||||
|
|
||||||
|
struct {
|
||||||
|
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||||
|
__uint(max_entries, 1);
|
||||||
|
__type(key, __u32);
|
||||||
|
__type(value, __u64);
|
||||||
|
} counter_map SEC(".maps");
|
||||||
|
|
||||||
|
extern void bpf_rcu_read_lock(void) __attribute__((section(".ksyms")));
|
||||||
|
extern void bpf_rcu_read_unlock(void) __attribute__((section(".ksyms")));
|
||||||
|
|
||||||
|
SEC("tp_btf/sys_enter")
|
||||||
|
int BPF_PROG(sys_enter, struct pt_regs *regs, long id) {
|
||||||
|
__u32 key = 0;
|
||||||
|
__u64 *count;
|
||||||
|
|
||||||
|
bpf_rcu_read_lock();
|
||||||
|
|
||||||
|
count = bpf_map_lookup_elem(&counter_map, &key);
|
||||||
|
if (count) {
|
||||||
|
__sync_fetch_and_add(count, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bpf_rcu_read_unlock();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
use aya::{Btf, Ebpf, programs::BtfTracePoint};
|
||||||
|
use test_log::test;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ksym() {
|
||||||
|
let env = env!("OUT_DIR");
|
||||||
|
println!("out dir {}", env);
|
||||||
|
let mut ebpf = Ebpf::load(crate::KSYMS).unwrap();
|
||||||
|
|
||||||
|
let prog: &mut BtfTracePoint = ebpf.program_mut("sys_enter").unwrap().try_into().unwrap();
|
||||||
|
let btf = Btf::from_sys_fs().unwrap();
|
||||||
|
prog.load("sys_enter", &btf).unwrap();
|
||||||
|
prog.attach().unwrap();
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue