@ -1,3 +1,4 @@
use crate ::bindings ::bpf_raw_tracepoint_args ;
#[ cfg(any(
#[ cfg(any(
bpf_target_arch = "arm" ,
bpf_target_arch = "arm" ,
bpf_target_arch = "mips" ,
bpf_target_arch = "mips" ,
@ -13,619 +14,305 @@ use crate::bindings::pt_regs;
use crate ::bindings ::user_pt_regs as pt_regs ;
use crate ::bindings ::user_pt_regs as pt_regs ;
#[ cfg(bpf_target_arch = " riscv64 " ) ]
#[ cfg(bpf_target_arch = " riscv64 " ) ]
use crate ::bindings ::user_regs_struct as pt_regs ;
use crate ::bindings ::user_regs_struct as pt_regs ;
use crate ::{ bindings ::bpf_raw_tracepoint_args , cty ::c_void , helpers ::bpf_probe_read } ;
/// A trait that indicates a valid type for an argument which can be coerced from a BTF
mod sealed {
/// context.
#[ expect(clippy::missing_safety_doc) ]
///
pub unsafe trait Argument {
/// Users should not implement this trait.
fn from_register ( value : u64 ) -> Self ;
///
/// # Safety
///
/// This trait is _only_ safe to implement on primitive types that can fit into
/// a `u64`. For example, integers and raw pointers may be coerced from a BTF context.
pub unsafe trait FromBtfArgument : Sized {
/// 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.
///
/// # Safety
///
/// This function is deeply unsafe, as we are reading raw pointers into kernel
/// memory. In particular, the value of `n` must not exceed the number of function
/// arguments. Moreover, `ctx` must be a valid pointer to a BTF context, and `T` must
/// be the right type for the given argument.
unsafe fn from_argument ( ctx : * const c_void , n : usize ) -> Self ;
}
unsafe impl < T > FromBtfArgument for * const T {
unsafe fn from_argument ( ctx : * const c_void , n : usize ) -> * const T {
// BTF arguments are exposed as an array of `usize` where `usize` can
// either be treated as a pointer or a primitive type
let ctx : * const usize = ctx . cast ( ) ;
( unsafe { * ctx . add ( n ) } ) as _
}
}
}
/// Helper macro to implement [`FromBtfArgument`] for a primitive type.
macro_rules! unsafe_impl_argument {
macro_rules! unsafe_impl_from_btf_argument {
( $( $( { $( $generics :tt ) * } ) ? $ty :ty $( { where $( $where :tt ) * } ) ? ) , + $(, ) ? ) = > {
( $type :ident ) = > {
$(
unsafe impl FromBtfArgument for $type {
#[ allow(clippy::cast_lossless, trivial_numeric_casts) ]
#[ allow(trivial_numeric_casts) ]
unsafe impl $( $( $generics ) * ) ? Argument for $ty $( where $( $where ) * ) ? {
unsafe fn from_argument ( ctx : * const c_void , n : usize ) -> Self {
fn from_register ( value : u64 ) -> Self {
// BTF arguments are exposed as an array of `usize` where `usize` can
value as Self
// either be treated as a pointer or a primitive type
}
let ctx : * const usize = ctx . cast ( ) ;
}
( unsafe { * ctx . add ( n ) } ) as _
) +
}
}
}
} ;
}
unsafe_impl_from_btf_argument ! ( u8 ) ;
unsafe_impl_from_btf_argument ! ( u16 ) ;
unsafe_impl_from_btf_argument ! ( u32 ) ;
unsafe_impl_from_btf_argument ! ( u64 ) ;
unsafe_impl_from_btf_argument ! ( i8 ) ;
unsafe_impl_from_btf_argument ! ( i16 ) ;
unsafe_impl_from_btf_argument ! ( i32 ) ;
unsafe_impl_from_btf_argument ! ( i64 ) ;
unsafe_impl_from_btf_argument ! ( usize ) ;
unsafe_impl_from_btf_argument ! ( isize ) ;
pub struct PtRegs {
regs : * mut pt_regs ,
}
/// A portable wrapper around pt_regs, user_pt_regs and user_regs_struct.
impl PtRegs {
pub fn new ( regs : * mut pt_regs ) -> Self {
Self { regs }
}
/// Returns the value of the register used to pass arg `n`.
pub fn arg < T : FromPtRegs > ( & self , n : usize ) -> Option < T > {
T ::from_argument ( unsafe { & * self . regs } , n )
}
/// Returns the value of the register used to pass the return value.
pub fn ret < T : FromPtRegs > ( & self ) -> Option < T > {
T ::from_retval ( unsafe { & * self . regs } )
}
}
/// Returns a pointer to the wrapped value.
unsafe_impl_argument ! (
pub fn as_ptr ( & self ) -> * mut pt_regs {
i8 ,
self . regs
u8 ,
}
i16 ,
u16 ,
i32 ,
u32 ,
i64 ,
u64 ,
i128 ,
u128 ,
isize ,
usize ,
{ < T > } * const T { where T : ' static } ,
{ < T > } * mut T { where T : ' static } ,
) ;
}
}
/// A trait that indicates a valid type for an argument which can be coerced from
pub trait Argument : sealed ::Argument { }
/// a pt_regs context.
///
/// Any implementation of this trait is strictly architecture-specific and depends on the
/// layout of the underlying pt_regs struct and the target processor's calling
/// conventions. Users should not implement this trait.
pub trait FromPtRegs : Sized {
/// 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.
fn from_argument ( ctx : & pt_regs , n : usize ) -> Option < Self > ;
/// Coerces a `T` from the return value of a pt_regs context.
fn from_retval ( ctx : & pt_regs ) -> Option < Self > ;
}
#[ cfg(bpf_target_arch = " x86_64 " ) ]
impl < T : sealed ::Argument > Argument for T { }
impl < T > FromPtRegs for * const T {
fn from_argument ( ctx : & pt_regs , n : usize ) -> Option < Self > {
match n {
0 = > unsafe { bpf_probe_read ( & ctx . rdi ) . map ( | v | v as * const _ ) . ok ( ) } ,
1 = > unsafe { bpf_probe_read ( & ctx . rsi ) . map ( | v | v as * const _ ) . ok ( ) } ,
2 = > unsafe { bpf_probe_read ( & ctx . rdx ) . map ( | v | v as * const _ ) . ok ( ) } ,
3 = > unsafe { bpf_probe_read ( & ctx . rcx ) . map ( | v | v as * const _ ) . ok ( ) } ,
4 = > unsafe { bpf_probe_read ( & ctx . r8 ) . map ( | v | v as * const _ ) . ok ( ) } ,
5 = > unsafe { bpf_probe_read ( & ctx . r9 ) . map ( | v | v as * const _ ) . ok ( ) } ,
_ = > None ,
}
}
fn from_retval ( ctx : & pt_regs ) -> Option < Self > {
/// Coerces a `T` from the `n`th argument from a BTF context where `n` starts
unsafe { bpf_probe_read ( & ctx . rax ) . map ( | v | v as * const _ ) . ok ( ) }
/// 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 } )
}
}
#[ cfg(bpf_target_arch = " arm " ) ]
trait PtRegsLayout {
impl < T > FromPtRegs for * const T {
type Reg ;
fn from_argument ( ctx : & pt_regs , n : usize ) -> Option < Self > {
if n < = 6 {
unsafe { bpf_probe_read ( & ctx . uregs [ n ] ) . map ( | v | v as * const _ ) . ok ( ) }
} else {
None
}
}
fn from_retval ( ctx : & pt_regs ) -> Option < Self > {
fn arg_reg ( & self , index : usize ) -> Option < & Self ::Reg > ;
unsafe { bpf_probe_read ( & ctx . uregs [ 0 ] ) . map ( | v | v as * const _ ) . ok ( ) }
fn rc_reg ( & self ) -> & Self ::Reg ;
}
}
}
#[ cfg(bpf_target_arch = " aarch64 " ) ]
#[ cfg(bpf_target_arch = " aarch64 " ) ]
impl < T > FromPtRegs for * const T {
impl PtRegsLayout for pt_regs {
fn from_argument ( ctx : & pt_regs , n : usize ) -> Option < Self > {
type Reg = crate ::bindings ::__u64 ;
if n < = 7 {
unsafe { bpf_probe_read ( & ctx . regs [ n ] ) . map ( | v | v as * const _ ) . ok ( ) }
fn arg_reg ( & self , index : usize ) -> Option < & Self ::Reg > {
} else {
// AArch64 arguments align with libbpf's __PT_PARM{1..8}_REG (regs[0..7]).
None
// 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 ] ) ,
fn from_retval ( ctx : & pt_regs ) -> Option < Self > {
unsafe { bpf_probe_read ( & ctx . regs [ 0 ] ) . map ( | v | v as * const _ ) . ok ( ) }
}
}
#[ cfg(bpf_target_arch = " loongarch64 " ) ]
impl < T > FromPtRegs for * const T {
fn from_argument ( ctx : & pt_regs , n : usize ) -> Option < Self > {
if n < = 7 {
unsafe { bpf_probe_read ( & ctx . regs [ 4 + n ] ) . map ( | v | v as * const _ ) . ok ( ) }
} else {
None
}
}
fn from_retval ( ctx : & pt_regs ) -> Option < Self > {
unsafe { bpf_probe_read ( & ctx . regs [ 4 ] ) . map ( | v | v as * const _ ) . ok ( ) }
}
}
#[ cfg(bpf_target_arch = " riscv64 " ) ]
impl < T > FromPtRegs for * const T {
fn from_argument ( ctx : & pt_regs , n : usize ) -> Option < Self > {
match n {
0 = > unsafe { bpf_probe_read ( & ctx . a0 ) . map ( | v | v as * const _ ) . ok ( ) } ,
1 = > unsafe { bpf_probe_read ( & ctx . a1 ) . map ( | v | v as * const _ ) . ok ( ) } ,
2 = > unsafe { bpf_probe_read ( & ctx . a2 ) . map ( | v | v as * const _ ) . ok ( ) } ,
3 = > unsafe { bpf_probe_read ( & ctx . a3 ) . map ( | v | v as * const _ ) . ok ( ) } ,
4 = > unsafe { bpf_probe_read ( & ctx . a4 ) . map ( | v | v as * const _ ) . ok ( ) } ,
5 = > unsafe { bpf_probe_read ( & ctx . a5 ) . map ( | v | v as * const _ ) . ok ( ) } ,
6 = > unsafe { bpf_probe_read ( & ctx . a6 ) . map ( | v | v as * const _ ) . ok ( ) } ,
7 = > unsafe { bpf_probe_read ( & ctx . a7 ) . map ( | v | v as * const _ ) . ok ( ) } ,
_ = > None ,
_ = > None ,
}
}
}
}
fn from_retval ( ctx : & pt_regs ) -> Option < Self > {
fn rc_reg ( & self ) -> & Self ::Reg {
unsafe { bpf_probe_read ( & ctx . ra ) . map ( | v | v as * const _ ) . ok ( ) }
// 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 = " powerpc64 " ) ]
#[ cfg(bpf_target_arch = " arm " ) ]
impl < T > FromPtRegs for * const T {
impl PtRegsLayout for pt_regs {
fn from_argument ( ctx : & pt_regs , n : usize ) -> Option < Self > {
type Reg = crate ::cty ::c_long ;
if n < = 7 {
unsafe { bpf_probe_read ( & ctx . gpr [ 3 + n ] ) . map ( | v | v as * const _ ) . ok ( ) }
fn arg_reg ( & self , index : usize ) -> Option < & Self ::Reg > {
} else {
// ARM arguments follow libbpf's __PT_PARM{1..7}_REG mapping (uregs[0..6]).
None
// 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 from_retval ( ctx : & pt_regs ) -> Option < Self > {
fn rc_reg ( & self ) -> & Self ::Reg {
unsafe { bpf_probe_read ( & ctx . gpr [ 3 ] ) . map ( | v | v as * const _ ) . ok ( ) }
// 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 = " s390x " ) ]
#[ cfg(bpf_target_arch = " loongarch64 " ) ]
impl < T > FromPtRegs for * const T {
impl PtRegsLayout for pt_regs {
fn from_argument ( ctx : & pt_regs , n : usize ) -> Option < Self > {
type Reg = crate ::cty ::c_ulong ;
if n < = 4 {
unsafe { bpf_probe_read ( & ctx . gprs [ 2 + n ] ) . map ( | v | v as * const _ ) . ok ( ) }
fn arg_reg ( & self , index : usize ) -> Option < & Self ::Reg > {
} else {
// LoongArch arguments correspond to libbpf's __PT_PARM{1..8}_REG (regs[4..11]).
None
// 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 from_retval ( ctx : & pt_regs ) -> Option < Self > {
fn rc_reg ( & self ) -> & Self ::Reg {
unsafe { bpf_probe_read ( & ctx . gprs [ 2 ] ) . map ( | v | v as * const _ ) . ok ( ) }
// 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 = " mips " ) ]
#[ cfg(bpf_target_arch = " mips " ) ]
impl < T > FromPtRegs for * const T {
impl PtRegsLayout for pt_regs {
fn from_argument ( ctx : & pt_regs , n : usize ) -> Option < Self > {
type Reg = crate ::bindings ::__u64 ;
// Assume N64 ABI like libbpf does.
if n < = 7 {
fn arg_reg ( & self , index : usize ) -> Option < & Self ::Reg > {
unsafe { bpf_probe_read ( & ctx . regs [ n + 4 ] ) . map ( | v | v as * const _ ) . ok ( ) }
// MIPS N64 arguments correspond to libbpf's __PT_PARM{1..8}_REG (regs[4..11]).
} else {
// https://github.com/torvalds/linux/blob/v6.17/arch/mips/include/asm/ptrace.h#L28-L52
None
// 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 ] ) ,
fn from_retval ( ctx : & pt_regs ) -> Option < Self > {
unsafe { bpf_probe_read ( & ctx . regs [ 31 ] ) . map ( | v | v as * const _ ) . ok ( ) }
}
}
#[ cfg(bpf_target_arch = " x86_64 " ) ]
impl < T > FromPtRegs for * mut T {
fn from_argument ( ctx : & pt_regs , n : usize ) -> Option < Self > {
match n {
0 = > unsafe { bpf_probe_read ( & ctx . rdi ) . map ( | v | v as * mut _ ) . ok ( ) } ,
1 = > unsafe { bpf_probe_read ( & ctx . rsi ) . map ( | v | v as * mut _ ) . ok ( ) } ,
2 = > unsafe { bpf_probe_read ( & ctx . rdx ) . map ( | v | v as * mut _ ) . ok ( ) } ,
3 = > unsafe { bpf_probe_read ( & ctx . rcx ) . map ( | v | v as * mut _ ) . ok ( ) } ,
4 = > unsafe { bpf_probe_read ( & ctx . r8 ) . map ( | v | v as * mut _ ) . ok ( ) } ,
5 = > unsafe { bpf_probe_read ( & ctx . r9 ) . map ( | v | v as * mut _ ) . ok ( ) } ,
_ = > None ,
_ = > None ,
}
}
}
}
fn from_retval ( ctx : & pt_regs ) -> Option < Self > {
fn rc_reg ( & self ) -> & Self ::Reg {
unsafe { bpf_probe_read ( & ctx . rax ) . map ( | v | v as * mut _ ) . ok ( ) }
// Return codes use libbpf's __PT_RC_REG (regs[2], which aliases MIPS $v0).
}
// https://github.com/torvalds/linux/blob/v6.17/tools/lib/bpf/bpf_tracing.h#L277-L279
}
& self . regs [ 2 ]
#[ cfg(bpf_target_arch = " arm " ) ]
impl < T > FromPtRegs for * mut T {
fn from_argument ( ctx : & pt_regs , n : usize ) -> Option < Self > {
if n < = 6 {
unsafe { bpf_probe_read ( & ctx . uregs [ n ] ) . map ( | v | v as * mut _ ) . ok ( ) }
} else {
None
}
}
fn from_retval ( ctx : & pt_regs ) -> Option < Self > {
unsafe { bpf_probe_read ( & ctx . uregs [ 0 ] ) . map ( | v | v as * mut _ ) . ok ( ) }
}
}
#[ cfg(bpf_target_arch = " aarch64 " ) ]
impl < T > FromPtRegs for * mut T {
fn from_argument ( ctx : & pt_regs , n : usize ) -> Option < Self > {
if n < = 7 {
unsafe { bpf_probe_read ( & ctx . regs [ n ] ) . map ( | v | v as * mut _ ) . ok ( ) }
} else {
None
}
}
fn from_retval ( ctx : & pt_regs ) -> Option < Self > {
unsafe { bpf_probe_read ( & ctx . regs [ 0 ] ) . map ( | v | v as * mut _ ) . ok ( ) }
}
}
}
}
#[ cfg(bpf_target_arch = " loongarch64 " ) ]
#[ cfg(bpf_target_arch = " powerpc64 " ) ]
impl < T > FromPtRegs for * mut T {
impl PtRegsLayout for pt_regs {
fn from_argument ( ctx : & pt_regs , n : usize ) -> Option < Self > {
type Reg = crate ::cty ::c_ulong ;
if n < = 7 {
unsafe { bpf_probe_read ( & ctx . regs [ 4 + n ] ) . map ( | v | v as * mut _ ) . ok ( ) }
fn arg_reg ( & self , index : usize ) -> Option < & Self ::Reg > {
} else {
// PowerPC64 arguments follow libbpf's __PT_PARM{1..8}_REG (gpr[3..10]).
None
// 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 from_retval ( ctx : & pt_regs ) -> Option < Self > {
fn rc_reg ( & self ) -> & Self ::Reg {
unsafe { bpf_probe_read ( & ctx . regs [ 4 ] ) . map ( | v | v as * mut _ ) . ok ( ) }
// 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 = " riscv64 " ) ]
#[ cfg(bpf_target_arch = " riscv64 " ) ]
impl < T > FromPtRegs for * mut T {
impl PtRegsLayout for pt_regs {
fn from_argument ( ctx : & pt_regs , n : usize ) -> Option < Self > {
type Reg = crate ::cty ::c_ulong ;
match n {
0 = > unsafe { bpf_probe_read ( & ctx . a0 ) . map ( | v | v as * mut _ ) . ok ( ) } ,
fn arg_reg ( & self , index : usize ) -> Option < & Self ::Reg > {
1 = > unsafe { bpf_probe_read ( & ctx . a1 ) . map ( | v | v as * mut _ ) . ok ( ) } ,
// RISC-V arguments track libbpf's __PT_PARM{1..8}_REG (a0-a7).
2 = > unsafe { bpf_probe_read ( & ctx . a2 ) . map ( | v | v as * mut _ ) . ok ( ) } ,
// https://github.com/torvalds/linux/blob/v6.17/arch/riscv/include/asm/ptrace.h#L15-L55
3 = > unsafe { bpf_probe_read ( & ctx . a3 ) . map ( | v | v as * mut _ ) . ok ( ) } ,
// https://github.com/torvalds/linux/blob/v6.17/tools/lib/bpf/bpf_tracing.h#L360-L376
4 = > unsafe { bpf_probe_read ( & ctx . a4 ) . map ( | v | v as * mut _ ) . ok ( ) } ,
match index {
5 = > unsafe { bpf_probe_read ( & ctx . a5 ) . map ( | v | v as * mut _ ) . ok ( ) } ,
0 = > Some ( & self . a0 ) ,
6 = > unsafe { bpf_probe_read ( & ctx . a6 ) . map ( | v | v as * mut _ ) . ok ( ) } ,
1 = > Some ( & self . a1 ) ,
7 = > unsafe { bpf_probe_read ( & ctx . a7 ) . map ( | v | v as * mut _ ) . ok ( ) } ,
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 ,
_ = > None ,
}
}
}
}
fn from_retval ( ctx : & pt_regs ) -> Option < Self > {
fn rc_reg ( & self ) -> & Self ::Reg {
unsafe { bpf_probe_read ( & ctx . ra ) . map ( | v | v as * mut _ ) . ok ( ) }
// 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 < T > FromPtRegs for * mut T {
fn from_argument ( ctx : & pt_regs , n : usize ) -> Option < Self > {
if n < = 7 {
unsafe { bpf_probe_read ( & ctx . gpr [ 3 + n ] ) . map ( | v | v as * mut _ ) . ok ( ) }
} else {
None
}
}
fn from_retval ( ctx : & pt_regs ) -> Option < Self > {
unsafe { bpf_probe_read ( & ctx . gpr [ 3 ] ) . map ( | v | v as * mut _ ) . ok ( ) }
}
}
}
}
#[ cfg(bpf_target_arch = " s390x " ) ]
#[ cfg(bpf_target_arch = " s390x " ) ]
impl < T > FromPtRegs for * mut T {
impl PtRegsLayout for pt_regs {
fn from_argument ( ctx : & pt_regs , n : usize ) -> Option < Self > {
type Reg = crate ::cty ::c_ulong ;
if n < = 4 {
unsafe { bpf_probe_read ( & ctx . gprs [ 2 + n ] ) . map ( | v | v as * mut _ ) . ok ( ) }
fn arg_reg ( & self , index : usize ) -> Option < & Self ::Reg > {
} else {
// s390 arguments match libbpf's __PT_PARM{1..5}_REG (gprs[2..6]).
None
// 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 ] ) ,
fn from_retval ( ctx : & pt_regs ) -> Option < Self > {
_ = > None ,
unsafe { bpf_probe_read ( & ctx . gprs [ 2 ] ) . map ( | v | v as * mut _ ) . ok ( ) }
}
}
#[ cfg(bpf_target_arch = " mips " ) ]
impl < T > FromPtRegs for * mut T {
fn from_argument ( ctx : & pt_regs , n : usize ) -> Option < Self > {
// Assume N64 ABI like libbpf does.
if n < = 7 {
unsafe { bpf_probe_read ( & ctx . regs [ n + 4 ] ) . map ( | v | v as * mut _ ) . ok ( ) }
} else {
None
}
}
}
}
fn from_retval ( ctx : & pt_regs ) -> Option < Self > {
fn rc_reg ( & self ) -> & Self ::Reg {
unsafe { bpf_probe_read ( & ctx . regs [ 31 ] ) . map ( | v | v as * mut _ ) . ok ( ) }
// 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 ]
}
}
}
}
/// Helper macro to implement [`FromPtRegs`] for a primitive type.
#[ cfg(bpf_target_arch = " x86_64 " ) ]
macro_rules! impl_from_pt_regs {
impl PtRegsLayout for pt_regs {
( $type :ident ) = > {
type Reg = crate ::cty ::c_ulong ;
#[ cfg(bpf_target_arch = " x86_64 " ) ]
impl FromPtRegs for $type {
fn arg_reg ( & self , index : usize ) -> Option < & Self ::Reg > {
fn from_argument ( ctx : & pt_regs , n : usize ) -> Option < Self > {
// x86-64 arguments mirror libbpf's __PT_PARM{1..6}_REG mapping (rdi, rsi, rdx, rcx, r8, r9).
match n {
// https://github.com/torvalds/linux/blob/v6.17/arch/x86/include/asm/ptrace.h#L103-L155
0 = > Some ( ctx . rdi as * const $type as _ ) ,
// https://github.com/torvalds/linux/blob/v6.17/tools/lib/bpf/bpf_tracing.h#L134-L152
1 = > Some ( ctx . rsi as * const $type as _ ) ,
match index {
2 = > Some ( ctx . rdx as * const $type as _ ) ,
0 = > Some ( & self . rdi ) ,
3 = > Some ( ctx . rcx as * const $type as _ ) ,
1 = > Some ( & self . rsi ) ,
4 = > Some ( ctx . r8 as * const $type as _ ) ,
2 = > Some ( & self . rdx ) ,
5 = > Some ( ctx . r9 as * const $type as _ ) ,
3 = > Some ( & self . rcx ) ,
_ = > None ,
4 = > Some ( & self . r8 ) ,
}
5 = > Some ( & self . r9 ) ,
}
_ = > None ,
fn from_retval ( ctx : & pt_regs ) -> Option < Self > {
Some ( ctx . rax as * const $type as _ )
}
}
#[ cfg(bpf_target_arch = " arm " ) ]
impl FromPtRegs for $type {
fn from_argument ( ctx : & pt_regs , n : usize ) -> Option < Self > {
if n < = 6 {
Some ( ctx . uregs [ n ] as * const $type as _ )
} else {
None
}
}
fn from_retval ( ctx : & pt_regs ) -> Option < Self > {
Some ( ctx . uregs [ 0 ] as * const $type as _ )
}
}
#[ cfg(bpf_target_arch = " aarch64 " ) ]
impl FromPtRegs for $type {
fn from_argument ( ctx : & pt_regs , n : usize ) -> Option < Self > {
if n < = 7 {
Some ( ctx . regs [ n ] as * const $type as _ )
} else {
None
}
}
fn from_retval ( ctx : & pt_regs ) -> Option < Self > {
Some ( ctx . regs [ 0 ] as * const $type as _ )
}
}
#[ cfg(bpf_target_arch = " loongarch64 " ) ]
impl FromPtRegs for $type {
fn from_argument ( ctx : & pt_regs , n : usize ) -> Option < Self > {
if n < = 7 {
Some ( ctx . regs [ 4 + n ] as * const $type as _ )
} else {
None
}
}
fn from_retval ( ctx : & pt_regs ) -> Option < Self > {
Some ( ctx . regs [ 4 ] as * const $type as _ )
}
}
#[ cfg(bpf_target_arch = " riscv64 " ) ]
impl FromPtRegs for $type {
fn from_argument ( ctx : & pt_regs , n : usize ) -> Option < Self > {
match n {
0 = > Some ( ctx . a0 as * const $type as _ ) ,
1 = > Some ( ctx . a1 as * const $type as _ ) ,
2 = > Some ( ctx . a2 as * const $type as _ ) ,
3 = > Some ( ctx . a3 as * const $type as _ ) ,
4 = > Some ( ctx . a4 as * const $type as _ ) ,
5 = > Some ( ctx . a5 as * const $type as _ ) ,
6 = > Some ( ctx . a6 as * const $type as _ ) ,
7 = > Some ( ctx . a7 as * const $type as _ ) ,
_ = > None ,
}
}
fn from_retval ( ctx : & pt_regs ) -> Option < Self > {
Some ( ctx . ra as * const $type as _ )
}
}
#[ cfg(bpf_target_arch = " powerpc64 " ) ]
impl FromPtRegs for $type {
fn from_argument ( ctx : & pt_regs , n : usize ) -> Option < Self > {
if n < = 7 {
Some ( ctx . gpr [ 3 + n ] as * const $type as _ )
} else {
None
}
}
fn from_retval ( ctx : & pt_regs ) -> Option < Self > {
Some ( ctx . gpr [ 3 ] as * const $type as _ )
}
}
#[ cfg(bpf_target_arch = " s390x " ) ]
impl FromPtRegs for $type {
fn from_argument ( ctx : & pt_regs , n : usize ) -> Option < Self > {
if n < = 4 {
Some ( ctx . gprs [ 2 + n ] as * const $type as _ )
} else {
None
}
}
fn from_retval ( ctx : & pt_regs ) -> Option < Self > {
Some ( ctx . gprs [ 2 ] as * const $type as _ )
}
}
#[ cfg(bpf_target_arch = " mips " ) ]
impl FromPtRegs for $type {
fn from_argument ( ctx : & pt_regs , n : usize ) -> Option < Self > {
if n < = 7 {
Some ( ctx . regs [ n + 4 ] as * const $type as _ )
} else {
None
}
}
fn from_retval ( ctx : & pt_regs ) -> Option < Self > {
Some ( ctx . regs [ 31 ] as * const $type as _ )
}
}
}
} ;
}
impl_from_pt_regs ! ( u8 ) ;
impl_from_pt_regs ! ( u16 ) ;
impl_from_pt_regs ! ( u32 ) ;
impl_from_pt_regs ! ( u64 ) ;
impl_from_pt_regs ! ( i8 ) ;
impl_from_pt_regs ! ( i16 ) ;
impl_from_pt_regs ! ( i32 ) ;
impl_from_pt_regs ! ( i64 ) ;
impl_from_pt_regs ! ( usize ) ;
impl_from_pt_regs ! ( isize ) ;
/// A Rust wrapper on `bpf_raw_tracepoint_args`.
pub struct RawTracepointArgs {
args : * mut bpf_raw_tracepoint_args ,
}
impl RawTracepointArgs {
/// Creates a new instance of `RawTracepointArgs` from the given
/// `bpf_raw_tracepoint_args` raw pointer to allow easier access
/// to raw tracepoint argumetns.
pub fn new ( args : * mut bpf_raw_tracepoint_args ) -> Self {
Self { args }
}
}
/// Returns the n-th argument of the raw tracepoint.
fn rc_reg ( & self ) -> & Self ::Reg {
///
// Return codes use libbpf's __PT_RC_REG (rax).
/// # Safety
// https://github.com/torvalds/linux/blob/v6.17/tools/lib/bpf/bpf_tracing.h#L148-L152
///
& self . rax
/// 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 unsafe fn arg < T : FromRawTracepointArgs > ( & self , n : usize ) -> T {
unsafe { T ::from_argument ( & * self . args , n ) }
}
}
}
}
#[ expect(clippy::missing_safety_doc) ]
/// Coerces a `T` from the `n`th argument of a pt_regs context where `n` starts
pub unsafe trait FromRawTracepointArgs : Sized {
/// at 0 and increases by 1 for each successive argument.
/// Returns the n-th argument of the raw tracepoint.
pub ( crate ) fn arg < T : Argument > ( ctx : & pt_regs , n : usize ) -> Option < T > {
///
let reg = ctx . arg_reg ( n ) ? ;
/// # Safety
#[ allow(
///
clippy ::cast_sign_loss ,
/// This method is unsafe because it performs raw pointer conversion and makes assumptions
clippy ::unnecessary_cast ,
/// about the structure of the `bpf_raw_tracepoint_args` type. The tracepoint arguments are
trivial_numeric_casts
/// represented as an array of `__u64` values. To be precise, the wrapped
) ]
/// `bpf_raw_tracepoint_args` binding defines it as `__IncompleteArrayField<__u64>` and the
Some ( T ::from_register ( ( * reg ) as u64 ) )
/// 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.
unsafe fn from_argument ( ctx : & bpf_raw_tracepoint_args , n : usize ) -> Self ;
}
}
unsafe impl < T > FromRawTracepointArgs for * const T {
/// Coerces a `T` from the return value of a pt_regs context.
unsafe fn from_argument ( ctx : & bpf_raw_tracepoint_args , n : usize ) -> * const T {
pub ( crate ) fn ret < T : Argument > ( ctx : & pt_regs ) -> T {
// Raw tracepoint arguments are exposed as `__u64 args[0]`.
let reg = ctx . rc_reg ( ) ;
// https://elixir.bootlin.com/linux/v6.5.5/source/include/uapi/linux/bpf.h#L6829
#[ allow(
// They are represented as `__IncompleteArrayField<T>` in the Rust
clippy ::cast_sign_loss ,
// wraapper.
clippy ::unnecessary_cast ,
//
trivial_numeric_casts
// The most convenient way of accessing such type in Rust is to use
) ]
// `__IncompleteArrayField<T>::as_slice` to represent that array as a
T ::from_register ( ( * reg ) as u64 )
// 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
// assumntion and implementation is unsafe.
( unsafe { ctx . args . as_slice ( n + 1 ) } ) [ n ] as _
}
}
}
macro_rules! unsafe_impl_from_raw_tracepoint_args {
/// Returns the n-th argument of the raw tracepoint.
( $type :ident ) = > {
///
unsafe impl FromRawTracepointArgs for $type {
/// # Safety
#[ allow(trivial_numeric_casts) ]
///
unsafe fn from_argument ( ctx : & bpf_raw_tracepoint_args , n : usize ) -> Self {
/// This method is unsafe because it performs raw pointer conversion and makes assumptions
( unsafe { ctx . args . as_slice ( n + 1 ) } ) [ n ] as _
/// 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 } )
}
}
unsafe_impl_from_raw_tracepoint_args ! ( u8 ) ;
unsafe_impl_from_raw_tracepoint_args ! ( u16 ) ;
unsafe_impl_from_raw_tracepoint_args ! ( u32 ) ;
unsafe_impl_from_raw_tracepoint_args ! ( u64 ) ;
unsafe_impl_from_raw_tracepoint_args ! ( i8 ) ;
unsafe_impl_from_raw_tracepoint_args ! ( i16 ) ;
unsafe_impl_from_raw_tracepoint_args ! ( i32 ) ;
unsafe_impl_from_raw_tracepoint_args ! ( i64 ) ;
unsafe_impl_from_raw_tracepoint_args ! ( usize ) ;
unsafe_impl_from_raw_tracepoint_args ! ( isize ) ;