mirror of https://github.com/aya-rs/aya
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
309 lines
11 KiB
Rust
309 lines
11 KiB
Rust
use crate::bindings::bpf_raw_tracepoint_args;
|
|
#[cfg(any(
|
|
bpf_target_arch = "arm",
|
|
bpf_target_arch = "mips",
|
|
bpf_target_arch = "powerpc64",
|
|
bpf_target_arch = "x86_64",
|
|
))]
|
|
use crate::bindings::pt_regs;
|
|
#[cfg(any(
|
|
bpf_target_arch = "aarch64",
|
|
bpf_target_arch = "loongarch64",
|
|
bpf_target_arch = "s390x",
|
|
))]
|
|
use crate::bindings::user_pt_regs as pt_regs;
|
|
#[cfg(bpf_target_arch = "riscv64")]
|
|
use crate::bindings::user_regs_struct as pt_regs;
|
|
|
|
mod sealed {
|
|
#[expect(clippy::missing_safety_doc)]
|
|
pub unsafe trait Argument {
|
|
fn from_register(value: u64) -> Self;
|
|
}
|
|
|
|
macro_rules! unsafe_impl_argument {
|
|
($($( { $($generics:tt)* } )? $ty:ty $( { where $($where:tt)* } )?),+ $(,)?) => {
|
|
$(
|
|
#[allow(clippy::cast_lossless, trivial_numeric_casts)]
|
|
unsafe impl$($($generics)*)? Argument for $ty $(where $($where)*)? {
|
|
fn from_register(value: u64) -> Self {
|
|
value as Self
|
|
}
|
|
}
|
|
)+
|
|
}
|
|
}
|
|
|
|
unsafe_impl_argument!(
|
|
i8,
|
|
u8,
|
|
i16,
|
|
u16,
|
|
i32,
|
|
u32,
|
|
i64,
|
|
u64,
|
|
i128,
|
|
u128,
|
|
isize,
|
|
usize,
|
|
{<T>} *const T {where T: 'static},
|
|
{<T>} *mut T {where T: 'static},
|
|
);
|
|
}
|
|
|
|
pub trait Argument: sealed::Argument {}
|
|
|
|
impl<T: sealed::Argument> Argument for T {}
|
|
|
|
/// Coerces a `T` from the `n`th argument from a BTF context where `n` starts
|
|
/// at 0 and increases by 1 for each successive argument.
|
|
pub(crate) fn btf_arg<T: Argument>(ctx: &impl crate::EbpfContext, n: usize) -> T {
|
|
// BTF arguments are exposed as an array of `usize` where `usize` can
|
|
// either be treated as a pointer or a primitive type
|
|
let ptr: *const usize = ctx.as_ptr().cast();
|
|
let ptr = unsafe { ptr.add(n) };
|
|
T::from_register(unsafe { *ptr as u64 })
|
|
}
|
|
|
|
trait PtRegsLayout {
|
|
type Reg: Copy;
|
|
|
|
fn arg_reg(&self, index: usize) -> Option<&Self::Reg>;
|
|
fn rc_reg(&self) -> &Self::Reg;
|
|
}
|
|
|
|
#[cfg(bpf_target_arch = "x86_64")]
|
|
impl PtRegsLayout for pt_regs {
|
|
type Reg = crate::cty::c_ulong;
|
|
|
|
fn arg_reg(&self, index: usize) -> Option<&Self::Reg> {
|
|
// x86-64 arguments mirror libbpf's __PT_PARM{1..6}_REG mapping (rdi, rsi, rdx, rcx, r8, r9).
|
|
// https://github.com/torvalds/linux/blob/v6.17/arch/x86/include/asm/ptrace.h#L103-L155
|
|
// https://github.com/torvalds/linux/blob/v6.17/tools/lib/bpf/bpf_tracing.h#L134-L152
|
|
match index {
|
|
0 => Some(&self.rdi),
|
|
1 => Some(&self.rsi),
|
|
2 => Some(&self.rdx),
|
|
3 => Some(&self.rcx),
|
|
4 => Some(&self.r8),
|
|
5 => Some(&self.r9),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
fn rc_reg(&self) -> &Self::Reg {
|
|
// Return codes use libbpf's __PT_RC_REG (rax).
|
|
// https://github.com/torvalds/linux/blob/v6.17/tools/lib/bpf/bpf_tracing.h#L148-L152
|
|
&self.rax
|
|
}
|
|
}
|
|
|
|
#[cfg(bpf_target_arch = "arm")]
|
|
impl PtRegsLayout for pt_regs {
|
|
type Reg = crate::cty::c_ulong;
|
|
|
|
fn arg_reg(&self, index: usize) -> Option<&Self::Reg> {
|
|
// ARM arguments follow libbpf's __PT_PARM{1..7}_REG mapping (uregs[0..6]).
|
|
// https://github.com/torvalds/linux/blob/v6.17/arch/arm/include/uapi/asm/ptrace.h#L124-L152
|
|
// https://github.com/torvalds/linux/blob/v6.17/tools/lib/bpf/bpf_tracing.h#L198-L210
|
|
match index {
|
|
0..=6 => Some(&self.uregs[index]),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
fn rc_reg(&self) -> &Self::Reg {
|
|
// Return codes use libbpf's __PT_RC_REG (uregs[0]).
|
|
// https://github.com/torvalds/linux/blob/v6.17/tools/lib/bpf/bpf_tracing.h#L211-L214
|
|
&self.uregs[0]
|
|
}
|
|
}
|
|
|
|
#[cfg(bpf_target_arch = "aarch64")]
|
|
impl PtRegsLayout for pt_regs {
|
|
type Reg = crate::bindings::__u64;
|
|
|
|
fn arg_reg(&self, index: usize) -> Option<&Self::Reg> {
|
|
// AArch64 arguments align with libbpf's __PT_PARM{1..8}_REG (regs[0..7]).
|
|
// https://github.com/torvalds/linux/blob/v6.17/arch/arm64/include/uapi/asm/ptrace.h#L88-L93
|
|
// https://github.com/torvalds/linux/blob/v6.17/tools/lib/bpf/bpf_tracing.h#L229-L244
|
|
match index {
|
|
0..=7 => Some(&self.regs[index]),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
fn rc_reg(&self) -> &Self::Reg {
|
|
// Return codes use libbpf's __PT_RC_REG (regs[0]/x0).
|
|
// https://github.com/torvalds/linux/blob/v6.17/tools/lib/bpf/bpf_tracing.h#L248-L251
|
|
&self.regs[0]
|
|
}
|
|
}
|
|
|
|
#[cfg(bpf_target_arch = "loongarch64")]
|
|
impl PtRegsLayout for pt_regs {
|
|
type Reg = crate::cty::c_ulong;
|
|
|
|
fn arg_reg(&self, index: usize) -> Option<&Self::Reg> {
|
|
// LoongArch arguments correspond to libbpf's __PT_PARM{1..8}_REG (regs[4..11]).
|
|
// https://github.com/torvalds/linux/blob/v6.17/arch/loongarch/include/asm/ptrace.h#L20-L33
|
|
// https://github.com/torvalds/linux/blob/v6.17/tools/lib/bpf/bpf_tracing.h#L427-L444
|
|
match index {
|
|
0..=7 => Some(&self.regs[4 + index]),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
fn rc_reg(&self) -> &Self::Reg {
|
|
// Return codes use libbpf's __PT_RC_REG (regs[4], a0).
|
|
// https://github.com/torvalds/linux/blob/v6.17/tools/lib/bpf/bpf_tracing.h#L445-L447
|
|
&self.regs[4]
|
|
}
|
|
}
|
|
|
|
#[cfg(bpf_target_arch = "riscv64")]
|
|
impl PtRegsLayout for pt_regs {
|
|
type Reg = crate::cty::c_ulong;
|
|
|
|
fn arg_reg(&self, index: usize) -> Option<&Self::Reg> {
|
|
// RISC-V arguments track libbpf's __PT_PARM{1..8}_REG (a0-a7).
|
|
// https://github.com/torvalds/linux/blob/v6.17/arch/riscv/include/asm/ptrace.h#L15-L55
|
|
// https://github.com/torvalds/linux/blob/v6.17/tools/lib/bpf/bpf_tracing.h#L360-L376
|
|
match index {
|
|
0 => Some(&self.a0),
|
|
1 => Some(&self.a1),
|
|
2 => Some(&self.a2),
|
|
3 => Some(&self.a3),
|
|
4 => Some(&self.a4),
|
|
5 => Some(&self.a5),
|
|
6 => Some(&self.a6),
|
|
7 => Some(&self.a7),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
fn rc_reg(&self) -> &Self::Reg {
|
|
// Return codes use libbpf's __PT_RC_REG (a0).
|
|
// https://github.com/torvalds/linux/blob/v6.17/tools/lib/bpf/bpf_tracing.h#L379-L382
|
|
&self.a0
|
|
}
|
|
}
|
|
|
|
#[cfg(bpf_target_arch = "powerpc64")]
|
|
impl PtRegsLayout for pt_regs {
|
|
type Reg = crate::cty::c_ulong;
|
|
|
|
fn arg_reg(&self, index: usize) -> Option<&Self::Reg> {
|
|
// PowerPC64 arguments follow libbpf's __PT_PARM{1..8}_REG (gpr[3..10]).
|
|
// https://github.com/torvalds/linux/blob/v6.17/arch/powerpc/include/asm/ptrace.h#L28-L56
|
|
// https://github.com/torvalds/linux/blob/v6.17/tools/lib/bpf/bpf_tracing.h#L290-L308
|
|
match index {
|
|
0..=7 => Some(&self.gpr[3 + index]),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
fn rc_reg(&self) -> &Self::Reg {
|
|
// Return codes use libbpf's __PT_RC_REG (gpr[3]).
|
|
// https://github.com/torvalds/linux/blob/v6.17/tools/lib/bpf/bpf_tracing.h#L311-L314
|
|
&self.gpr[3]
|
|
}
|
|
}
|
|
|
|
#[cfg(bpf_target_arch = "s390x")]
|
|
impl PtRegsLayout for pt_regs {
|
|
type Reg = crate::cty::c_ulong;
|
|
|
|
fn arg_reg(&self, index: usize) -> Option<&Self::Reg> {
|
|
// s390 arguments match libbpf's __PT_PARM{1..5}_REG (gprs[2..6]).
|
|
// https://github.com/torvalds/linux/blob/v6.17/arch/s390/include/asm/ptrace.h#L111-L131
|
|
// https://github.com/torvalds/linux/blob/v6.17/tools/lib/bpf/bpf_tracing.h#L170-L181
|
|
match index {
|
|
0..=4 => Some(&self.gprs[2 + index]),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
fn rc_reg(&self) -> &Self::Reg {
|
|
// Return codes use libbpf's __PT_RC_REG (gprs[2]).
|
|
// https://github.com/torvalds/linux/blob/v6.17/tools/lib/bpf/bpf_tracing.h#L186-L188
|
|
&self.gprs[2]
|
|
}
|
|
}
|
|
|
|
#[cfg(bpf_target_arch = "mips")]
|
|
impl PtRegsLayout for pt_regs {
|
|
type Reg = crate::bindings::__u64;
|
|
|
|
fn arg_reg(&self, index: usize) -> Option<&Self::Reg> {
|
|
// MIPS N64 arguments correspond to libbpf's __PT_PARM{1..8}_REG (regs[4..11]).
|
|
// https://github.com/torvalds/linux/blob/v6.17/arch/mips/include/asm/ptrace.h#L28-L52
|
|
// https://github.com/torvalds/linux/blob/v6.17/tools/lib/bpf/bpf_tracing.h#L261-L275
|
|
match index {
|
|
0..=7 => Some(&self.regs[4 + index]),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
fn rc_reg(&self) -> &Self::Reg {
|
|
// Return codes use libbpf's __PT_RC_REG (regs[2], $v0).
|
|
// https://github.com/torvalds/linux/blob/v6.17/tools/lib/bpf/bpf_tracing.h#L277-L279
|
|
&self.regs[2]
|
|
}
|
|
}
|
|
|
|
/// Coerces a `T` from the `n`th argument of a pt_regs context where `n` starts
|
|
/// at 0 and increases by 1 for each successive argument.
|
|
pub(crate) fn arg<T: Argument>(ctx: &pt_regs, n: usize) -> Option<T> {
|
|
let reg = ctx.arg_reg(n)?;
|
|
Some(T::from_register(*reg))
|
|
}
|
|
|
|
/// Coerces a `T` from the return value of a pt_regs context.
|
|
pub(crate) fn ret<T: Argument>(ctx: &pt_regs) -> T {
|
|
let reg = ctx.rc_reg();
|
|
T::from_register(*reg)
|
|
}
|
|
|
|
/// Returns the n-th argument of the raw tracepoint.
|
|
///
|
|
/// # Safety
|
|
///
|
|
/// This method is unsafe because it performs raw pointer conversion and makes assumptions
|
|
/// about the structure of the `bpf_raw_tracepoint_args` type. The tracepoint arguments are
|
|
/// represented as an array of `__u64` values. To be precise, the wrapped
|
|
/// `bpf_raw_tracepoint_args` binding defines it as `__IncompleteArrayField<__u64>` and the
|
|
/// original C type as `__u64 args[0]`. This method provides a way to access these arguments
|
|
/// conveniently in Rust using `__IncompleteArrayField<T>::as_slice` to represent that array
|
|
/// as a slice of length n and then retrieve the n-th element of it.
|
|
///
|
|
/// However, the method does not check the total number of available arguments for a given
|
|
/// tracepoint and assumes that the slice has at least `n` elements, leading to undefined
|
|
/// behavior if this condition is not met. Such check is impossible to do, because the
|
|
/// tracepoint context doesn't contain any information about number of arguments.
|
|
///
|
|
/// This method also cannot guarantee that the requested type matches the actual value type.
|
|
/// Wrong assumptions about types can lead to undefined behavior. The tracepoint context
|
|
/// doesn't provide any type information.
|
|
///
|
|
/// The caller is responsible for ensuring they have accurate knowledge of the arguments
|
|
/// and their respective types for the accessed tracepoint context.
|
|
pub(crate) fn raw_tracepoint_arg<T: Argument>(ctx: &bpf_raw_tracepoint_args, n: usize) -> T {
|
|
// Raw tracepoint arguments are exposed as `__u64 args[0]`.
|
|
// https://github.com/torvalds/linux/blob/v6.17/include/uapi/linux/bpf.h#L7231-L7233
|
|
// They are represented as `__IncompleteArrayField<T>` in the Rust
|
|
// wrapper.
|
|
//
|
|
// The most convenient way of accessing such type in Rust is to use
|
|
// `__IncompleteArrayField<T>::as_slice` to represent that array as a
|
|
// slice of length n and then retrieve the n-th element of it.
|
|
//
|
|
// We don't know how many arguments are there for the given tracepoint,
|
|
// so we just assume that the slice has at least n elements. The whole
|
|
// assumption and implementation is unsafe.
|
|
let ptr = ctx.args.as_ptr();
|
|
let ptr = unsafe { ptr.add(n) };
|
|
T::from_register(unsafe { *ptr })
|
|
}
|