// SPDX-FileCopyrightText: edef // SPDX-License-Identifier: OSL-3.0 #[cfg(test)] use std::fmt::{self, Debug}; #[cfg(test)] pub(crate) fn libc_check( item: &'static str, (our_name, our_value): (&'static str, T), (libc_name, libc_value): (&'static str, T), ) { match () { _ if libc_name.ends_with(our_name) => {} _ if libc_name.starts_with(&format!("{our_name}_")) => {} () => panic!("{libc_name} doesn't match {our_name}"), } assert!( our_value == libc_value, "{item}::{our_name} ({our_value:#x}) != libc::{libc_name} ({libc_value:#x})", ); } #[macro_export] macro_rules! syscall_bitflags { ( $( struct $BitFlags:ident: $T:ty { $( const $FLAG:ident = $value:expr => $LIBC_FLAG:ident; )* } )* ) => { #[test] fn verify_syscall_bitflags() { $( $BitFlags::verify(); )* } $( $crate::bitflags! { pub(crate) struct $BitFlags: $T { $( const $FLAG = $value; )* } } impl $BitFlags { #[cfg(test)] fn verify() { $( $crate::syscall_abi::macros::libc_check( stringify!($BitFlags), (stringify!($FLAG), Self::$FLAG.bits()), (stringify!($LIBC_FLAG), $crate::libc::$LIBC_FLAG) ); )* } } impl $crate::syscall_abi::SyscallArg for $BitFlags { fn try_from_reg(reg: u64) -> Option { $crate::syscall_abi::SyscallArg::try_from_reg(reg).and_then(Self::from_bits) } } )* }; } #[macro_export] macro_rules! syscall_enums { ( $( enum $Enum:ident: $T:ty { $( $VARIANT:ident = $value:literal $(=> $LIBC_VALUE:ident)?, )* } )* ) => { #[test] fn verify_syscall_enums() { $( $Enum::verify(); )* } $( #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[allow(non_camel_case_types)] pub(crate) enum $Enum { $($VARIANT = $value),* } impl $crate::syscall_abi::SyscallArg for $Enum { fn try_from_reg(reg: u64) -> Option { let reg = <$T as $crate::syscall_abi::SyscallArg>::try_from_reg(reg)?; Some(match reg { $( $value => $Enum::$VARIANT, )* _ => return None }) } } impl $Enum { #[cfg(test)] fn verify() { $( $( $crate::syscall_abi::macros::libc_check( stringify!($Enum), (stringify!($VARIANT), Self::$VARIANT as $T), (stringify!($LIBC_VALUE), $crate::libc::$LIBC_VALUE) ); )? )* } } )* }; } #[macro_export] macro_rules! define_syscalls { (enum $SyscallEntry:ident { $(fn $syscall:ident ( $($arg:ident : $Arg:ty),* ) -> $Ret:ty = $nr:literal ;)* }) => { #[derive(Debug, Clone)] #[allow(non_camel_case_types)] // TODO(edef): re-enable dead_code lint when we start fully interpreting syscall args #[allow(dead_code)] pub(crate) enum $SyscallEntry { $($syscall { $($arg : $Arg),* }),* } impl $SyscallEntry { pub(crate) fn from_regs(process: &Process, regs: $crate::libc::user_regs_struct) -> anyhow::Result<$SyscallEntry> { use anyhow::bail; Ok(match (regs.orig_rax, [regs.rdi, regs.rsi, regs.rdx, regs.r10, regs.r8, regs.r9]) { $( ($nr, [$($arg,)* ..]) => $SyscallEntry::$syscall { $($arg: match ProcessSyscallArg::try_from_process_reg(process, $arg) { Some(x) => x, None => bail!("couldn't parse {}(2) {}: {:#08x}", stringify!($syscall), stringify!($arg), $arg) }),* }, )* (n, _) => bail!("unknown syscall number {n}") }) } } } }