diff --git a/src/header/mod.rs b/src/header/mod.rs
index e11ffb4c14a15fda97e3233692f0f8dc65357ec5..25203a74c90bcc2e19162d18b95f7f2b7ef0f52a 100644
--- a/src/header/mod.rs
+++ b/src/header/mod.rs
@@ -37,6 +37,7 @@ pub mod sys_epoll;
 pub mod sys_file;
 pub mod sys_ioctl;
 pub mod sys_mman;
+pub mod sys_ptrace;
 //pub mod sys_resource;
 pub mod sys_select;
 pub mod sys_socket;
@@ -45,6 +46,7 @@ pub mod sys_statvfs;
 pub mod sys_time;
 pub mod sys_timeb;
 //pub mod sys_times;
+pub mod sys_user;
 pub mod _wctype;
 pub mod sys_uio;
 pub mod sys_un;
diff --git a/src/header/sys_ptrace/cbindgen.toml b/src/header/sys_ptrace/cbindgen.toml
new file mode 100644
index 0000000000000000000000000000000000000000..3ecacd053429f1e8ded5635cada8ba39c08e1089
--- /dev/null
+++ b/src/header/sys_ptrace/cbindgen.toml
@@ -0,0 +1,7 @@
+sys_includes = []
+include_guard = "_SYS_PTRACE_H"
+language = "C"
+style = "Tag"
+
+[enum]
+prefix_with_name = true
diff --git a/src/header/sys_ptrace/mod.rs b/src/header/sys_ptrace/mod.rs
new file mode 100644
index 0000000000000000000000000000000000000000..b9cef5b7ed3fa8178eedc3ceeb2ab12087b05b82
--- /dev/null
+++ b/src/header/sys_ptrace/mod.rs
@@ -0,0 +1,24 @@
+//! ptrace compatibility layer for Redox OS
+
+use platform::types::*;
+use platform::{Sys, PalPtrace};
+
+pub const PTRACE_PEEKTEXT: c_int = 1;
+pub const PTRACE_PEEKDATA: c_int = 2;
+pub const PTRACE_POKETEXT: c_int = 4;
+pub const PTRACE_POKEDATA: c_int = 5;
+pub const PTRACE_CONT: c_int = 7;
+pub const PTRACE_KILL: c_int = 8;
+pub const PTRACE_SINGLESTEP: c_int = 9;
+pub const PTRACE_GETREGS: c_int = 12;
+pub const PTRACE_SETREGS: c_int = 13;
+pub const PTRACE_GETFPREGS: c_int = 14;
+pub const PTRACE_SETFPREGS: c_int = 15;
+pub const PTRACE_ATTACH: c_int = 16;
+pub const PTRACE_DETACH: c_int = 17;
+
+#[no_mangle]
+pub unsafe extern "C" fn ptrace(request: c_int, mut params: ...) -> c_int {
+    // Musl also just grabs the arguments from the varargs...
+    Sys::ptrace(request, params.arg(), params.arg(), params.arg())
+}
diff --git a/src/header/sys_user/cbindgen.toml b/src/header/sys_user/cbindgen.toml
new file mode 100644
index 0000000000000000000000000000000000000000..63c05e2adcf029bd5a037f149dded486f4389060
--- /dev/null
+++ b/src/header/sys_user/cbindgen.toml
@@ -0,0 +1,7 @@
+sys_includes = []
+include_guard = "_SYS_USER_H"
+language = "C"
+style = "Tag"
+
+[enum]
+prefix_with_name = true
diff --git a/src/header/sys_user/mod.rs b/src/header/sys_user/mod.rs
new file mode 100644
index 0000000000000000000000000000000000000000..ca14bdeaef9f8528957f96ef4443b5618dd6e72a
--- /dev/null
+++ b/src/header/sys_user/mod.rs
@@ -0,0 +1,52 @@
+//! A part of the ptrace compatibility for Redox OS
+
+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]
+}
+
+#[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
+}
+
+#[no_mangle]
+pub extern "C" fn _cbindgen_only_generates_structs_if_they_are_mentioned_which_is_dumb(a: user_fpregs_struct, b: user_regs_struct) {}
diff --git a/src/lib.rs b/src/lib.rs
index 3ea4a1f0613d04d5567e28ee4d4b6073e3c5f839..268c6dcc8214bde17c503213e917be12e28c1157 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -2,7 +2,6 @@
 #![allow(non_camel_case_types)]
 #![allow(non_upper_case_globals)]
 #![allow(unused_variables)]
-#![feature(alloc)]
 #![feature(allocator_api)]
 #![feature(asm)]
 #![feature(c_variadic)]
@@ -13,6 +12,7 @@
 #![feature(core_intrinsics)]
 #![feature(global_asm)]
 // FIXME: Stable on nightly, remove once redox fork is updated
+#![feature(alloc)]
 #![feature(iter_copied)]
 #![feature(lang_items)]
 #![feature(linkage)]
diff --git a/src/platform/linux/mod.rs b/src/platform/linux/mod.rs
index af4a1115ed5763b270d3527faf81788703cf3b30..edd7be4bc77497d06c2404dbd068a379cb26f356 100644
--- a/src/platform/linux/mod.rs
+++ b/src/platform/linux/mod.rs
@@ -15,6 +15,7 @@ use header::sys_utsname::utsname;
 use header::time::timespec;
 
 mod epoll;
+mod ptrace;
 mod signal;
 mod socket;
 
diff --git a/src/platform/linux/ptrace.rs b/src/platform/linux/ptrace.rs
new file mode 100644
index 0000000000000000000000000000000000000000..e2206ce60143bcd7f4f8c7c75189d7f1f7bd9825
--- /dev/null
+++ b/src/platform/linux/ptrace.rs
@@ -0,0 +1,9 @@
+use super::super::types::*;
+use super::super::PalPtrace;
+use super::{e, Sys};
+
+impl PalPtrace for Sys {
+    fn ptrace(request: c_int, pid: pid_t, addr: *mut c_void, data: *mut c_void) -> c_int {
+        unsafe { e(syscall!(PTRACE, request, pid, addr, data)) as c_int }
+    }
+}
diff --git a/src/platform/mod.rs b/src/platform/mod.rs
index 047aee80dbf97c434287d42140a5cd1030075575..979899e99cfeafa5fa5f5a497e0ca0af371b8290 100644
--- a/src/platform/mod.rs
+++ b/src/platform/mod.rs
@@ -12,7 +12,7 @@ mod allocator;
 #[path = "allocator/ralloc.rs"]
 mod allocator;
 
-pub use self::pal::{Pal, PalEpoll, PalSignal, PalSocket};
+pub use self::pal::{Pal, PalEpoll, PalPtrace, PalSignal, PalSocket};
 
 mod pal;
 
diff --git a/src/platform/pal/mod.rs b/src/platform/pal/mod.rs
index 8c542b3a14bdcf45385be22b9eb1cf3675ddf9b2..fde80078878882f63ba2105168b6ad18ac60e63c 100644
--- a/src/platform/pal/mod.rs
+++ b/src/platform/pal/mod.rs
@@ -10,6 +10,9 @@ use header::time::timespec;
 pub use self::epoll::PalEpoll;
 mod epoll;
 
+pub use self::ptrace::PalPtrace;
+mod ptrace;
+
 pub use self::signal::PalSignal;
 mod signal;
 
diff --git a/src/platform/pal/ptrace.rs b/src/platform/pal/ptrace.rs
new file mode 100644
index 0000000000000000000000000000000000000000..d26d84c05adfc5844431404e9be33f6bf03ae847
--- /dev/null
+++ b/src/platform/pal/ptrace.rs
@@ -0,0 +1,6 @@
+use super::super::types::*;
+use super::super::Pal;
+
+pub trait PalPtrace: Pal {
+    fn ptrace(request: c_int, pid: pid_t, addr: *mut c_void, data: *mut c_void) -> c_int;
+}
diff --git a/src/platform/redox/mod.rs b/src/platform/redox/mod.rs
index 317fbc44d9c8ec853f64d481ce9d50e015a41b84..9f854ef8d6cd4e95533cd12176029f0ebc35a2cf 100644
--- a/src/platform/redox/mod.rs
+++ b/src/platform/redox/mod.rs
@@ -28,6 +28,7 @@ use super::{errno, Pal, Read};
 
 mod epoll;
 mod extra;
+mod ptrace;
 mod signal;
 mod socket;
 
diff --git a/src/platform/redox/ptrace.rs b/src/platform/redox/ptrace.rs
new file mode 100644
index 0000000000000000000000000000000000000000..9c3c71803515fccdd570ea052541e0ba3282ae6c
--- /dev/null
+++ b/src/platform/redox/ptrace.rs
@@ -0,0 +1,10 @@
+use super::super::types::*;
+use super::super::PalPtrace;
+use super::{e, Sys};
+
+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!()
+    }
+}
diff --git a/tests/Makefile b/tests/Makefile
index da4d243bbfa5a08a5dc9631d15a5afbb5d7fca70..388720a45847f850f683b207592bc23c6336320f 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -15,6 +15,7 @@ EXPECT_NAMES=\
 	libgen \
 	locale \
 	math \
+    ptrace \
 	regex \
 	select \
 	setjmp \
diff --git a/tests/expected/ptrace.stderr b/tests/expected/ptrace.stderr
new file mode 100644
index 0000000000000000000000000000000000000000..289c82a876f1984bb4f573c71f1142c498a3731e
--- /dev/null
+++ b/tests/expected/ptrace.stderr
@@ -0,0 +1,4 @@
+This is printed to STDOUT.
+Or, at least, that's what I thought.
+But all write(...) syscalls are actually redirected to STDERR by the tracer.
+Big surprise, right!
diff --git a/tests/expected/ptrace.stdout b/tests/expected/ptrace.stdout
new file mode 100644
index 0000000000000000000000000000000000000000..8cc46e622268f5ce701809404e129b6a40fa7db3
--- /dev/null
+++ b/tests/expected/ptrace.stdout
@@ -0,0 +1 @@
+Child exited with status 0
diff --git a/tests/ptrace.c b/tests/ptrace.c
new file mode 100644
index 0000000000000000000000000000000000000000..6b4dc936c03fb625c5887fa90db5271eee6f484c
--- /dev/null
+++ b/tests/ptrace.c
@@ -0,0 +1,64 @@
+#include <signal.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <sys/ptrace.h>
+#include <sys/user.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "test_helpers.h"
+
+int main() {
+    int pid = fork();
+    ERROR_IF(fork, pid, == -1);
+
+    if (pid == 0) {
+        int result = ptrace(PTRACE_TRACEME, 0, NULL, NULL);
+        ERROR_IF(ptrace, result, == -1);
+        UNEXP_IF(ptrace, result, != 0);
+
+        // Alert parent: I'm ready
+        result = raise(SIGSTOP);
+        ERROR_IF(close, result, == -1);
+        UNEXP_IF(close, result, != 0);
+
+        puts("This is printed to STDOUT.");
+        puts("Or, at least, that's what I thought.");
+        puts("But all write(...) syscalls are actually redirected to STDERR by the tracer.");
+        puts("Big surprise, right!");
+    } else {
+        // Wait for child process to be ready
+        int result = waitpid(pid, NULL, 0);
+        ERROR_IF(close, 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);
+            result = waitpid(pid, &status, 0);
+            ERROR_IF(close, result, == -1);
+            if (WIFEXITED(status)) { break; }
+
+            struct user_regs_struct regs;
+            result = ptrace(PTRACE_GETREGS, pid, NULL, &regs);
+            ERROR_IF(close, 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);
+            }
+
+            // Post-syscall:
+            result = ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
+            ERROR_IF(close, result, == -1);
+            UNEXP_IF(close, result, != 0);
+            result = waitpid(pid, &status, 0);
+            ERROR_IF(close, result, == -1);
+            if (WIFEXITED(status)) { break; }
+        }
+        printf("Child exited with status %d\n", WEXITSTATUS(status));
+    }
+}