summary refs log tree commit diff
path: root/ripple/minitrace/src/syscall_abi
diff options
context:
space:
mode:
authoredef <edef@unfathomable.blue>2022-07-30 19:29:15 +0000
committeredef <edef@unfathomable.blue>2022-07-30 19:29:15 +0000
commit82652914c933f50931338e4bbc924013c358fe71 (patch)
treea77f0d48e59d497720de3f51a7ef4f928d361b15 /ripple/minitrace/src/syscall_abi
parentdd94473c5724f8215790a9195df96cfa7bd6a04b (diff)
downloadunf-legacy-82652914c933f50931338e4bbc924013c358fe71.tar.zst
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
Diffstat (limited to 'ripple/minitrace/src/syscall_abi')
-rw-r--r--ripple/minitrace/src/syscall_abi/arg.rs84
-rw-r--r--ripple/minitrace/src/syscall_abi/fd.rs62
-rw-r--r--ripple/minitrace/src/syscall_abi/macros.rs161
-rw-r--r--ripple/minitrace/src/syscall_abi/mod.rs127
4 files changed, 434 insertions, 0 deletions
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 <edef@unfathomable.blue>
+// 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<Self>;
+}
+
+impl ProcessSyscallArg for CString {
+	fn try_from_process_reg(process: &Process, reg: u64) -> Option<Self> {
+		process.read_mem_cstr(reg).ok()
+	}
+}
+
+impl<T: SyscallArg> ProcessSyscallArg for T {
+	fn try_from_process_reg(_process: &Process, reg: u64) -> Option<Self> {
+		SyscallArg::try_from_reg(reg)
+	}
+}
+
+pub(crate) trait SyscallArg: Sized {
+	fn try_from_reg(reg: u64) -> Option<Self>;
+}
+
+impl SyscallArg for u16 {
+	fn try_from_reg(reg: u64) -> Option<Self> {
+		reg.try_into().ok()
+	}
+}
+
+impl SyscallArg for u32 {
+	fn try_from_reg(reg: u64) -> Option<Self> {
+		reg.try_into().ok()
+	}
+}
+
+impl SyscallArg for u64 {
+	fn try_from_reg(reg: u64) -> Option<Self> {
+		Some(reg)
+	}
+}
+
+impl SyscallArg for i32 {
+	fn try_from_reg(reg: u64) -> Option<Self> {
+		Some(u32::try_from(reg).ok()? as i32)
+	}
+}
+
+impl SyscallArg for *mut i32 {
+	fn try_from_reg(reg: u64) -> Option<Self> {
+		Some(usize::try_from_reg(reg)? as *mut i32)
+	}
+}
+
+impl SyscallArg for usize {
+	fn try_from_reg(reg: u64) -> Option<Self> {
+		reg.try_into().ok()
+	}
+}
+
+impl SyscallArg for *const u8 {
+	fn try_from_reg(reg: u64) -> Option<Self> {
+		Some(usize::try_from_reg(reg)? as *const u8)
+	}
+}
+
+impl SyscallArg for *mut u8 {
+	fn try_from_reg(reg: u64) -> Option<Self> {
+		Some(usize::try_from_reg(reg)? as *mut u8)
+	}
+}
+
+impl SyscallArg for *mut () {
+	fn try_from_reg(reg: u64) -> Option<Self> {
+		Some(usize::try_from_reg(reg)? as *mut ())
+	}
+}
+
+impl SyscallArg for *const () {
+	fn try_from_reg(reg: u64) -> Option<Self> {
+		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 <edef@unfathomable.blue>
+// 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<Self> {
+		Some(match i32::try_from_reg(reg)? {
+			fd @ 0..=i32::MAX => FileDesc(fd),
+			_ => return None,
+		})
+	}
+}
+
+impl SyscallArg for Option<FileDesc> {
+	fn try_from_reg(reg: u64) -> Option<Self> {
+		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<Self> {
+		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 <edef@unfathomable.blue>
+// SPDX-License-Identifier: OSL-3.0
+
+#[cfg(test)]
+use std::fmt::{self, Debug};
+
+#[cfg(test)]
+pub(crate) fn libc_check<T: Debug + Eq + fmt::LowerHex>(
+	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<Self> {
+					$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<Self> {
+					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 <edef@unfathomable.blue>
+// SPDX-FileCopyrightText: V <v@unfathomable.blue>
+// 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<FileDesc>, 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,
+	}
+}