From f389010fff3398f65ea8d07cd556d56c4241f27a Mon Sep 17 00:00:00 2001 From: jD91mZM2 <me@krake.one> Date: Wed, 10 Jul 2019 11:03:31 +0200 Subject: [PATCH] Initial ptrace compatibility for Redox OS --- Cargo.lock | 8 +- Cargo.toml | 2 +- include/bits/sys/wait.h | 12 +-- src/fs.rs | 28 ++++++- src/header/sys_ptrace/mod.rs | 2 + src/header/sys_user/mod.rs | 76 +++++++++--------- src/platform/redox/mod.rs | 31 ++++++-- src/platform/redox/ptrace.rs | 147 ++++++++++++++++++++++++++++++++--- tests/ptrace.c | 24 +++--- 9 files changed, 254 insertions(+), 76 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 508a1a2f9..c3930bbe3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -155,6 +155,11 @@ name = "rand_core" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "redox_syscall" +version = "0.1.56" +source = "git+https://gitlab.redox-os.org/redox-os/syscall#9e9f47d2a570c55dd96cd80c83bc818d63cab8af" + [[package]] name = "redox_syscall" version = "0.1.56" @@ -174,7 +179,7 @@ dependencies = [ "posix-regex 0.1.0", "ralloc 1.0.0", "rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (git+https://gitlab.redox-os.org/redox-os/syscall)", "sc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "spin 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -279,6 +284,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9" "checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" "checksum rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0e7a549d590831370895ab7ba4ea0c1b6b011d106b5ff2da6eee112615e6dc0" +"checksum redox_syscall 0.1.56 (git+https://gitlab.redox-os.org/redox-os/syscall)" = "<none>" "checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" "checksum rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" diff --git a/Cargo.toml b/Cargo.toml index cf8a58e98..3d9804d91 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,7 +37,7 @@ optional = true sc = "0.2.2" [target.'cfg(target_os = "redox")'.dependencies] -redox_syscall = "0.1.52" +redox_syscall = { git = "https://gitlab.redox-os.org/redox-os/syscall" } spin = "0.4.10" [features] diff --git a/include/bits/sys/wait.h b/include/bits/sys/wait.h index 94d5a2ba0..7af6a82bb 100644 --- a/include/bits/sys/wait.h +++ b/include/bits/sys/wait.h @@ -1,13 +1,13 @@ #ifndef _BITS_SYS_WAIT_H #define _BITS_SYS_WAIT_H -#define WEXITSTATUS(s) (((s) & 0xff00) >> 8) -#define WTERMSIG(s) ((s) & 0x7f) +#define WEXITSTATUS(s) (((s) >> 8) & 0xff) +#define WTERMSIG(s) (((s) & 0x7f) != 0) #define WSTOPSIG(s) WEXITSTATUS(s) -#define WCOREDUMP(s) ((s) & 0x80) -#define WIFEXITED(s) (!WTERMSIG(s)) -#define WIFSTOPPED(s) ((short)((((s)&0xffff)*0x10001)>>8) > 0x7f00) -#define WIFSIGNALED(s) (((s)&0xffff)-1U < 0xffu) +#define WCOREDUMP(s) (((s) & 0x80) != 0) +#define WIFEXITED(s) (((s) & 0x7f) == 0) +#define WIFSTOPPED(s) (((s) & 0xff) == 0x7f) +#define WIFSIGNALED(s) (((((s) & 0x7f) + 1U) & 0x7f) >= 2) // Ends with 1111111 or 10000000 #define WIFCONTINUED(s) ((s) == 0xffff) #endif /* _BITS_SYS_WAIT_H */ diff --git a/src/fs.rs b/src/fs.rs index 39c952d71..103dc8ec5 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -67,7 +67,7 @@ impl File { } } -impl io::Read for File { +impl io::Read for &File { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { match Sys::read(self.fd, buf) { -1 => Err(io::last_os_error()), @@ -76,7 +76,7 @@ impl io::Read for File { } } -impl io::Write for File { +impl io::Write for &File { fn write(&mut self, buf: &[u8]) -> io::Result<usize> { match Sys::write(self.fd, buf) { -1 => Err(io::last_os_error()), @@ -89,7 +89,7 @@ impl io::Write for File { } } -impl io::Seek for File { +impl io::Seek for &File { fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> { let (offset, whence) = match pos { io::SeekFrom::Start(start) => (start as off_t, SEEK_SET), @@ -104,6 +104,28 @@ impl io::Seek for File { } } +impl io::Read for File { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + (&mut &*self).read(buf) + } +} + +impl io::Write for File { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + (&mut &*self).write(buf) + } + + fn flush(&mut self) -> io::Result<()> { + (&mut &*self).flush() + } +} + +impl io::Seek for File { + fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> { + (&mut &*self).seek(pos) + } +} + impl Deref for File { type Target = c_int; diff --git a/src/header/sys_ptrace/mod.rs b/src/header/sys_ptrace/mod.rs index 8b40fa8b4..9a54c0725 100644 --- a/src/header/sys_ptrace/mod.rs +++ b/src/header/sys_ptrace/mod.rs @@ -19,6 +19,8 @@ pub const PTRACE_SETFPREGS: c_int = 15; pub const PTRACE_ATTACH: c_int = 16; pub const PTRACE_DETACH: c_int = 17; pub const PTRACE_SYSCALL: c_int = 24; +pub const PTRACE_SYSEMU: c_int = 31; +pub const PTRACE_SYSEMU_SINGLESTEP: c_int = 32; // Can't use "params: ..." syntax, because... guess what? Cbingen again :( #[no_mangle] diff --git a/src/header/sys_user/mod.rs b/src/header/sys_user/mod.rs index 826130ee8..2af4f4927 100644 --- a/src/header/sys_user/mod.rs +++ b/src/header/sys_user/mod.rs @@ -4,48 +4,48 @@ use platform::types::*; #[repr(C)] pub struct user_fpregs_struct { - cwd: u16, - swd: u16, - ftw: u16, - fop: u16, - rip: u64, - rdp: u64, - mxcsr: u32, - mxcr_mask: u32, - st_space: [u32; 32], - xmm_space: [u32; 64], - padding: [u32; 24], + pub cwd: u16, + pub swd: u16, + pub ftw: u16, + pub fop: u16, + pub rip: u64, + pub rdp: u64, + pub mxcsr: u32, + pub mxcr_mask: u32, + pub st_space: [u32; 32], + pub xmm_space: [u32; 64], + pub padding: [u32; 24], } #[repr(C)] pub struct user_regs_struct { - r15: c_ulong, - r14: c_ulong, - r13: c_ulong, - r12: c_ulong, - rbp: c_ulong, - rbx: c_ulong, - r11: c_ulong, - r10: c_ulong, - r9: c_ulong, - r8: c_ulong, - rax: c_ulong, - rcx: c_ulong, - rdx: c_ulong, - rsi: c_ulong, - rdi: c_ulong, - orig_rax: c_ulong, - rip: c_ulong, - cs: c_ulong, - eflags: c_ulong, - rsp: c_ulong, - ss: c_ulong, - fs_base: c_ulong, - gs_base: c_ulong, - ds: c_ulong, - es: c_ulong, - fs: c_ulong, - gs: c_ulong, + pub r15: c_ulong, + pub r14: c_ulong, + pub r13: c_ulong, + pub r12: c_ulong, + pub rbp: c_ulong, + pub rbx: c_ulong, + pub r11: c_ulong, + pub r10: c_ulong, + pub r9: c_ulong, + pub r8: c_ulong, + pub rax: c_ulong, + pub rcx: c_ulong, + pub rdx: c_ulong, + pub rsi: c_ulong, + pub rdi: c_ulong, + pub orig_rax: c_ulong, + pub rip: c_ulong, + pub cs: c_ulong, + pub eflags: c_ulong, + pub rsp: c_ulong, + pub ss: c_ulong, + pub fs_base: c_ulong, + pub gs_base: c_ulong, + pub ds: c_ulong, + pub es: c_ulong, + pub fs: c_ulong, + pub gs: c_ulong, } #[no_mangle] diff --git a/src/platform/redox/mod.rs b/src/platform/redox/mod.rs index 9f854ef8d..7d2e0e926 100644 --- a/src/platform/redox/mod.rs +++ b/src/platform/redox/mod.rs @@ -1,5 +1,3 @@ -//! sys/socket implementation, following http://pubs.opengroup.org/onlinepubs/009696699/basedefs/sys/socket.h.html - use core::result::Result as CoreResult; use core::{mem, ptr, slice}; use syscall::data::Map; @@ -17,6 +15,7 @@ use header::sys_mman::MAP_ANON; use header::sys_stat::stat; use header::sys_statvfs::statvfs; use header::sys_time::{timeval, timezone}; +use header::sys_wait; use header::sys_utsname::{utsname, UTSLENGTH}; use header::time::timespec; use header::unistd::{F_OK, R_OK, W_OK, X_OK}; @@ -922,14 +921,34 @@ impl Pal for Sys { if pid == !0 { pid = 0; } + let mut res = None; + let mut status = 0; + + // First, allow ptrace to handle waitpid + let state = ptrace::init_state(); + let sessions = state.sessions.lock(); + if let Some(session) = sessions.get(&pid) { + if options & sys_wait::WNOHANG != sys_wait::WNOHANG { + let _ = (&mut &session.tracer).write(&[syscall::PTRACE_WAIT]); + res = Some(e(syscall::waitpid(pid as usize, &mut status, (options | sys_wait::WNOHANG) as usize))); + if res == Some(0) { + // WNOHANG, just pretend ptrace SIGSTOP:ped this + status = (syscall::SIGSTOP << 8) | 0x7f; + res = Some(pid as usize); + } + } + } + + // If ptrace didn't impact this waitpid, proceed as normal + let res = res.unwrap_or_else(|| e(syscall::waitpid(pid as usize, &mut status, options as usize))); + + // If stat_loc is non-null, set that and the return unsafe { - let mut temp: usize = 0; - let res = e(syscall::waitpid(pid as usize, &mut temp, options as usize)); if !stat_loc.is_null() { - *stat_loc = temp as c_int; + *stat_loc = status as c_int; } - res as pid_t } + res as pid_t } fn write(fd: c_int, buf: &[u8]) -> ssize_t { diff --git a/src/platform/redox/ptrace.rs b/src/platform/redox/ptrace.rs index 8a539c2dc..da82cd76f 100644 --- a/src/platform/redox/ptrace.rs +++ b/src/platform/redox/ptrace.rs @@ -5,23 +5,152 @@ //! compatibility. So, this module will be a hellhole. use super::super::types::*; -use super::super::PalPtrace; -use super::{e, Sys}; +use super::super::{errno, Pal, PalPtrace, PalSignal, Sys}; +use crate::c_str::CString; +use crate::fs::File; +use crate::header::sys_user::user_regs_struct; +use crate::header::{errno as errnoh, fcntl, signal, sys_ptrace}; +use crate::io::{self, prelude::*}; use crate::sync::{Mutex, Once}; use alloc::collections::BTreeMap; +use alloc::collections::btree_map::Entry; +use syscall; -#[derive(Default)] -struct State {} +pub struct Session { + pub tracer: File, + pub mem: File, + pub regs: File, + pub fpregs: File +} +pub struct State { + pub sessions: Mutex<BTreeMap<pid_t, Session>> +} +impl State { + fn new() -> Self { + Self { + sessions: Mutex::new(BTreeMap::new()) + } + } +} + +static STATE: Once<State> = Once::new(); + +pub fn init_state() -> &'static State { + STATE.call_once(|| State::new()) +} + +fn inner_ptrace(request: c_int, pid: pid_t, addr: *mut c_void, data: *mut c_void) -> io::Result<c_int> { + let state = init_state(); + + if request == sys_ptrace::PTRACE_TRACEME { + // let pid = Sys::getpid(); + // todo: only auto-open session on host if this happens + return Ok(0); + } + + const NEW_FLAGS: c_int = fcntl::O_RDWR | fcntl::O_CLOEXEC; -static STATE: Once<Mutex<State>> = Once::new(); + let mut sessions = state.sessions.lock(); + let session = match sessions.entry(pid) { + Entry::Vacant(entry) => entry.insert(Session { + tracer: File::open(&CString::new(format!("proc:{}/trace", pid)).unwrap(), NEW_FLAGS | fcntl::O_NONBLOCK)?, + mem: File::open(&CString::new(format!("proc:{}/mem", pid)).unwrap(), NEW_FLAGS)?, + regs: File::open(&CString::new(format!("proc:{}/regs/int", pid)).unwrap(), NEW_FLAGS)?, + fpregs: File::open(&CString::new(format!("proc:{}/regs/float", pid)).unwrap(), NEW_FLAGS)?, + }), + Entry::Occupied(entry) => entry.into_mut() + }; + match request { + sys_ptrace::PTRACE_CONT | sys_ptrace::PTRACE_SINGLESTEP | + sys_ptrace::PTRACE_SYSCALL | sys_ptrace::PTRACE_SYSEMU | + sys_ptrace::PTRACE_SYSEMU_SINGLESTEP => { + Sys::kill(pid, signal::SIGCONT as _); -fn state() -> &'static Mutex<State> { - STATE.call_once(|| Mutex::new(State::default())) + (&mut &session.tracer).write(&[match request { + sys_ptrace::PTRACE_CONT => syscall::PTRACE_CONT, + sys_ptrace::PTRACE_SINGLESTEP => syscall::PTRACE_SINGLESTEP, + sys_ptrace::PTRACE_SYSCALL => syscall::PTRACE_SYSCALL, + sys_ptrace::PTRACE_SYSEMU => syscall::PTRACE_SYSEMU | syscall::PTRACE_SYSCALL, + sys_ptrace::PTRACE_SYSEMU_SINGLESTEP => syscall::PTRACE_SYSEMU | syscall::PTRACE_SINGLESTEP, + _ => unreachable!("unhandled ptrace request type {}", request) + }])?; + Ok(0) + }, + sys_ptrace::PTRACE_GETREGS => { + let c_regs = unsafe { &mut *(data as *mut user_regs_struct) }; + let mut redox_regs = syscall::IntRegisters::default(); + (&mut &session.regs).read(&mut redox_regs)?; + *c_regs = user_regs_struct { + r15: redox_regs.r15 as _, + r14: redox_regs.r14 as _, + r13: redox_regs.r13 as _, + r12: redox_regs.r12 as _, + rbp: redox_regs.rbp as _, + rbx: redox_regs.rbx as _, + r11: redox_regs.r11 as _, + r10: redox_regs.r10 as _, + r9: redox_regs.r9 as _, + r8: redox_regs.r8 as _, + rax: redox_regs.rax as _, + rcx: redox_regs.rcx as _, + rdx: redox_regs.rdx as _, + rsi: redox_regs.rsi as _, + rdi: redox_regs.rdi as _, + orig_rax: redox_regs.rax as _, // redox_regs.orig_rax as _, + rip: redox_regs.rip as _, + cs: redox_regs.cs as _, + eflags: redox_regs.rflags as _, + rsp: redox_regs.rsp as _, + ss: redox_regs.ss as _, + fs_base: 0, // fs_base: redox_regs.fs_base as _, + gs_base: 0, // gs_base: redox_regs.gs_base as _, + ds: 0, // ds: redox_regs.ds as _, + es: 0, // es: redox_regs.es as _, + fs: redox_regs.fs as _, + gs: 0, // gs: redox_regs.gs as _, + }; + Ok(0) + }, + sys_ptrace::PTRACE_SETREGS => { + let c_regs = unsafe { &*(data as *mut user_regs_struct) }; + let redox_regs = syscall::IntRegisters { + r15: c_regs.r15 as _, + r14: c_regs.r14 as _, + r13: c_regs.r13 as _, + r12: c_regs.r12 as _, + rbp: c_regs.rbp as _, + rbx: c_regs.rbx as _, + r11: c_regs.r11 as _, + r10: c_regs.r10 as _, + r9: c_regs.r9 as _, + r8: c_regs.r8 as _, + rax: c_regs.orig_rax as _, // c_regs.rax as _, + rcx: c_regs.rcx as _, + rdx: c_regs.rdx as _, + rsi: c_regs.rsi as _, + rdi: c_regs.rdi as _, + // orig_rax: c_regs.orig_rax as _, + rip: c_regs.rip as _, + cs: c_regs.cs as _, + rflags: c_regs.eflags as _, + rsp: c_regs.rsp as _, + ss: c_regs.ss as _, + // fs_base: c_regs.fs_base as _, + // gs_base: c_regs.gs_base as _, + // ds: c_regs.ds as _, + // es: c_regs.es as _, + fs: c_regs.fs as _, + // gs: c_regs.gs as _, + }; + (&mut &session.regs).write(&redox_regs)?; + Ok(0) + }, + _ => unimplemented!() + } } impl PalPtrace for Sys { fn ptrace(request: c_int, pid: pid_t, addr: *mut c_void, data: *mut c_void) -> c_int { - // Oh boy, this is not gonna be fun......... - unimplemented!() + inner_ptrace(request, pid, addr, data).unwrap_or(-1) } } diff --git a/tests/ptrace.c b/tests/ptrace.c index 6b4dc936c..c2a1760b3 100644 --- a/tests/ptrace.c +++ b/tests/ptrace.c @@ -19,8 +19,8 @@ int main() { // Alert parent: I'm ready result = raise(SIGSTOP); - ERROR_IF(close, result, == -1); - UNEXP_IF(close, result, != 0); + ERROR_IF(raise, result, == -1); + UNEXP_IF(raise, result, != 0); puts("This is printed to STDOUT."); puts("Or, at least, that's what I thought."); @@ -28,35 +28,35 @@ int main() { puts("Big surprise, right!"); } else { // Wait for child process to be ready - int result = waitpid(pid, NULL, 0); - ERROR_IF(close, result, == -1); + int result = waitpid(pid, NULL, WUNTRACED); + ERROR_IF(waitpid, result, == -1); int status; while (true) { // Pre-syscall: result = ptrace(PTRACE_SYSCALL, pid, NULL, NULL); - ERROR_IF(close, result, == -1); - UNEXP_IF(close, result, != 0); + ERROR_IF(ptrace, result, == -1); + UNEXP_IF(ptrace, result, != 0); result = waitpid(pid, &status, 0); - ERROR_IF(close, result, == -1); + ERROR_IF(waitpid, result, == -1); if (WIFEXITED(status)) { break; } struct user_regs_struct regs; result = ptrace(PTRACE_GETREGS, pid, NULL, ®s); - ERROR_IF(close, result, == -1); + ERROR_IF(ptrace, result, == -1); if (regs.orig_rax == 1 || regs.orig_rax == 0x21000004) { // SYS_write on Redox and Linux regs.rdi = 2; result = ptrace(PTRACE_SETREGS, pid, NULL, ®s); - ERROR_IF(close, result, == -1); + ERROR_IF(ptrace, result, == -1); } // Post-syscall: result = ptrace(PTRACE_SYSCALL, pid, NULL, NULL); - ERROR_IF(close, result, == -1); - UNEXP_IF(close, result, != 0); + ERROR_IF(ptrace, result, == -1); + UNEXP_IF(ptrace, result, != 0); result = waitpid(pid, &status, 0); - ERROR_IF(close, result, == -1); + ERROR_IF(waitpid, result, == -1); if (WIFEXITED(status)) { break; } } printf("Child exited with status %d\n", WEXITSTATUS(status)); -- GitLab