From 82652914c933f50931338e4bbc924013c358fe71 Mon Sep 17 00:00:00 2001 From: edef Date: Sat, 30 Jul 2022 19:29:15 +0000 Subject: ripple/minitrace/syscall_abi: init Factor out the rather sprawling syscall ABI definitions from the main program. The macros, argument parsing, and file descriptor code get some space to breathe too. Change-Id: I0aa01b6b94e4d4b770bb9ef59926e2236c50b258 --- ripple/minitrace/src/syscall_abi/arg.rs | 84 +++++++++++++++ ripple/minitrace/src/syscall_abi/fd.rs | 62 +++++++++++ ripple/minitrace/src/syscall_abi/macros.rs | 161 +++++++++++++++++++++++++++++ ripple/minitrace/src/syscall_abi/mod.rs | 127 +++++++++++++++++++++++ 4 files changed, 434 insertions(+) create mode 100644 ripple/minitrace/src/syscall_abi/arg.rs create mode 100644 ripple/minitrace/src/syscall_abi/fd.rs create mode 100644 ripple/minitrace/src/syscall_abi/macros.rs create mode 100644 ripple/minitrace/src/syscall_abi/mod.rs (limited to 'ripple/minitrace/src/syscall_abi') diff --git a/ripple/minitrace/src/syscall_abi/arg.rs b/ripple/minitrace/src/syscall_abi/arg.rs new file mode 100644 index 0000000..b25757c --- /dev/null +++ b/ripple/minitrace/src/syscall_abi/arg.rs @@ -0,0 +1,84 @@ +// SPDX-FileCopyrightText: edef +// SPDX-License-Identifier: OSL-3.0 + +use {crate::Process, std::ffi::CString}; + +pub(crate) trait ProcessSyscallArg: Sized { + fn try_from_process_reg(process: &Process, reg: u64) -> Option; +} + +impl ProcessSyscallArg for CString { + fn try_from_process_reg(process: &Process, reg: u64) -> Option { + process.read_mem_cstr(reg).ok() + } +} + +impl ProcessSyscallArg for T { + fn try_from_process_reg(_process: &Process, reg: u64) -> Option { + SyscallArg::try_from_reg(reg) + } +} + +pub(crate) trait SyscallArg: Sized { + fn try_from_reg(reg: u64) -> Option; +} + +impl SyscallArg for u16 { + fn try_from_reg(reg: u64) -> Option { + reg.try_into().ok() + } +} + +impl SyscallArg for u32 { + fn try_from_reg(reg: u64) -> Option { + reg.try_into().ok() + } +} + +impl SyscallArg for u64 { + fn try_from_reg(reg: u64) -> Option { + Some(reg) + } +} + +impl SyscallArg for i32 { + fn try_from_reg(reg: u64) -> Option { + Some(u32::try_from(reg).ok()? as i32) + } +} + +impl SyscallArg for *mut i32 { + fn try_from_reg(reg: u64) -> Option { + Some(usize::try_from_reg(reg)? as *mut i32) + } +} + +impl SyscallArg for usize { + fn try_from_reg(reg: u64) -> Option { + reg.try_into().ok() + } +} + +impl SyscallArg for *const u8 { + fn try_from_reg(reg: u64) -> Option { + Some(usize::try_from_reg(reg)? as *const u8) + } +} + +impl SyscallArg for *mut u8 { + fn try_from_reg(reg: u64) -> Option { + Some(usize::try_from_reg(reg)? as *mut u8) + } +} + +impl SyscallArg for *mut () { + fn try_from_reg(reg: u64) -> Option { + Some(usize::try_from_reg(reg)? as *mut ()) + } +} + +impl SyscallArg for *const () { + fn try_from_reg(reg: u64) -> Option { + Some(usize::try_from_reg(reg)? as *const ()) + } +} diff --git a/ripple/minitrace/src/syscall_abi/fd.rs b/ripple/minitrace/src/syscall_abi/fd.rs new file mode 100644 index 0000000..cd0c008 --- /dev/null +++ b/ripple/minitrace/src/syscall_abi/fd.rs @@ -0,0 +1,62 @@ +// SPDX-FileCopyrightText: edef +// SPDX-License-Identifier: OSL-3.0 + +use { + super::SyscallArg, + std::fmt::{self, Debug}, +}; + +#[derive(Clone, Copy, Eq, PartialEq)] +pub(crate) struct FileDesc(i32); + +impl Debug for FileDesc { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl SyscallArg for FileDesc { + fn try_from_reg(reg: u64) -> Option { + Some(match i32::try_from_reg(reg)? { + fd @ 0..=i32::MAX => FileDesc(fd), + _ => return None, + }) + } +} + +impl SyscallArg for Option { + fn try_from_reg(reg: u64) -> Option { + Some(match i32::try_from_reg(reg)? { + -1 => None, + fd @ 0..=i32::MAX => Some(FileDesc(fd)), + _ => return None, + }) + } +} + +#[derive(Clone, Copy, Eq, PartialEq)] +pub(crate) enum DirFd { + Cwd, + Fd(FileDesc), +} + +const AT_FDCWD: i32 = -100; + +impl Debug for DirFd { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + DirFd::Cwd => write!(f, "AT_FDCWD"), + DirFd::Fd(FileDesc(fd)) => write!(f, "{fd}"), + } + } +} + +impl SyscallArg for DirFd { + fn try_from_reg(reg: u64) -> Option { + Some(match i32::try_from_reg(reg)? { + AT_FDCWD => Self::Cwd, + fd @ 0..=i32::MAX => DirFd::Fd(FileDesc(fd)), + _ => return None, + }) + } +} diff --git a/ripple/minitrace/src/syscall_abi/macros.rs b/ripple/minitrace/src/syscall_abi/macros.rs new file mode 100644 index 0000000..07e6099 --- /dev/null +++ b/ripple/minitrace/src/syscall_abi/macros.rs @@ -0,0 +1,161 @@ +// 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}") + }) + } + } + } +} diff --git a/ripple/minitrace/src/syscall_abi/mod.rs b/ripple/minitrace/src/syscall_abi/mod.rs new file mode 100644 index 0000000..e0b35a9 --- /dev/null +++ b/ripple/minitrace/src/syscall_abi/mod.rs @@ -0,0 +1,127 @@ +// SPDX-FileCopyrightText: edef +// SPDX-FileCopyrightText: V +// SPDX-License-Identifier: OSL-3.0 + +use { + crate::{define_syscalls, syscall_bitflags, syscall_enums, Process}, + std::ffi::CString, +}; + +pub(crate) use arg::{ProcessSyscallArg, SyscallArg}; +pub(crate) use fd::{DirFd, FileDesc}; + +mod arg; +mod fd; +pub mod macros; + +type SigAction = (); +type SysInfo = (); +type Tms = (); +type Stat = (); +type RobustListHead = (); +type RLimit64 = (); + +define_syscalls! { + enum SyscallEntry { + fn read(fd: FileDesc, buf: *mut u8, count: usize) -> i64 = 0; + fn write(fd: FileDesc, buf: *const u8, count: usize) -> i64 = 1; + fn close(fd: FileDesc) -> i64 = 3; + fn mmap(addr: u64, len: u64, prot: ProtFlags, flags: MapFlags, fd: Option, off: u64) -> i64 = 9; + fn mprotect(addr: u64, len: usize, prot: ProtFlags) -> i64 = 10; + fn brk(brk: u64) -> i64 = 12; + fn rt_sigaction(sig: Signal, act: *const SigAction, oact: *mut SigAction, sigsetsize: usize) -> i64 = 13; + fn ioctl(fd: FileDesc, cmd: Ioctl, arg: u64) -> i64 = 16; + fn pread64(fd: FileDesc, buf: *mut u8, count: usize, pos: u64) -> i64 = 17; + fn access(filename: CString, mode: AccessMode) -> i64 = 21; + fn getcwd(buf: *mut u8, size: u64) -> i64 = 79; + fn readlink(path: CString, buf: *mut u8, bufsiz: i32) -> i64 = 89; + fn sysinfo(info: *mut SysInfo) -> i64 = 99; + fn times(tbuf: *mut Tms) -> i64 = 100; + fn arch_prctl(option: ArchOption, arg2: u64) -> i64 = 158; + fn set_tid_address(tidptr: *mut i32) -> i64 = 218; + fn exit_group(error_code: i32) -> i64 = 231; + fn openat(dfd: DirFd, filename: CString, flags: OpenFlags, mode: FileMode) -> i64 = 257; + fn newfstatat(dfd: DirFd, filename: CString, statbuf: *mut Stat, flags: AtFlags) -> i64 = 262; + fn set_robust_list(head: *mut RobustListHead, len: usize) -> i64 = 273; + fn prlimit64(pid: i32, resource: ResourceLimit, new_rlim: *const RLimit64, old_rlim: *mut RLimit64) -> i64 = 302; + fn getrandom(ubuf: *mut u8, len: usize, flags: GrndFlags) -> i64 = 318; + } +} + +syscall_bitflags! { + struct OpenFlags: i32 { + const WRONLY = 1 << 0 => O_WRONLY; + const CREAT = 1 << 6 => O_CREAT; + const NOCTTY = 1 << 8 => O_NOCTTY; + const TRUNC = 1 << 9 => O_TRUNC; + const CLOEXEC = 1 << 19 => O_CLOEXEC; + } + + struct GrndFlags: u32 { + const NONBLOCK = 1 << 0 => GRND_NONBLOCK; + const RANDOM = 1 << 1 => GRND_RANDOM; + } + + struct MapFlags: i32 { + const PRIVATE = 1 << 1 => MAP_PRIVATE; + const FIXED = 1 << 4 => MAP_FIXED; + const ANONYMOUS = 1 << 5 => MAP_ANONYMOUS; + const DENYWRITE = 1 << 11 => MAP_DENYWRITE; + } + + struct ProtFlags: i32 { + const READ = 1 << 0 => PROT_READ; + const WRITE = 1 << 1 => PROT_WRITE; + const EXEC = 1 << 2 => PROT_EXEC; + } + + struct AtFlags: i32 { + const EMPTY_PATH = 1 << 12 => AT_EMPTY_PATH; + } + + struct AccessMode: i32 { + const F = 0 => F_OK; + const X = 1 << 0 => X_OK; + const W = 1 << 1 => W_OK; + const R = 1 << 2 => R_OK; + } + + struct FileMode: u32 { + const IRUSR = 0o400 => S_IRUSR; + const IWUSR = 0o200 => S_IWUSR; + const IXUSR = 0o100 => S_IXUSR; + + const IRGRP = 0o040 => S_IRGRP; + const IWGRP = 0o020 => S_IWGRP; + const IXGRP = 0o010 => S_IXGRP; + + const IROTH = 0o004 => S_IROTH; + const IWOTH = 0o002 => S_IWOTH; + const IXOTH = 0o001 => S_IXOTH; + } +} + +syscall_enums! { + enum ResourceLimit: u32 { + STACK = 0x3 => RLIMIT_STACK, + RSS = 0x5 => RLIMIT_RSS, + AS = 0x9 => RLIMIT_AS, + } + + enum ArchOption: i32 { + SET_FS = 0x1002, + } + + enum Ioctl: u64 { + TCGETS = 0x5401 => TCGETS, + TIOCGWINSZ = 0x5413 => TIOCGWINSZ, + } + + enum Signal: i32 { + ILL = 4 => SIGILL, + ABRT = 6 => SIGABRT, + BUS = 7 => SIGBUS, + FPE = 8 => SIGFPE, + SEGV = 11 => SIGSEGV, + } +} -- cgit 1.4.1