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, &regs);
-            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, &regs);
-                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