Commit 78b4e992 authored by jD91mZM2's avatar jD91mZM2

Initial ptrace compatibility for Redox OS

parent 42a7d153
Pipeline #5275 failed with stages
in 7 minutes and 50 seconds
......@@ -152,6 +152,11 @@ name = "redox_syscall"
version = "0.1.54"
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#cceeae6ffe6967dc4bac0321de7bc739b2ae4da0"
[[package]]
name = "relibc"
version = "0.1.0"
......@@ -166,7 +171,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.54 (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)",
]
......@@ -271,6 +276,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"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.54 (registry+https://github.com/rust-lang/crates.io-index)" = "12229c14a0f65c4f1cb046a3b52047cdd9da1f4b30f8a39c5063c8bae515e252"
"checksum redox_syscall 0.1.56 (git+https://gitlab.redox-os.org/redox-os/syscall)" = "<none>"
"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"
"checksum sc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4ebbb026ba4a707c25caec2db5ef59ad8b41f7ad77cad06257e06229c891f376"
......
......@@ -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]
......
#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 */
......@@ -2,16 +2,10 @@ use c_str::CStr;
use core::ops::Deref;
use header::fcntl::O_CREAT;
use header::unistd::{SEEK_CUR, SEEK_END, SEEK_SET};
use io;
use platform;
use io::{self, last_os_error};
use platform::types::*;
use platform::{Pal, Sys};
fn last_os_error() -> io::Error {
let errno = unsafe { platform::errno };
io::Error::from_raw_os_error(errno)
}
pub struct File {
pub fd: c_int,
/// To avoid self referential FILE struct that needs both a reader and a writer,
......@@ -73,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(last_os_error()),
......@@ -82,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(last_os_error()),
......@@ -95,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),
......@@ -110,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;
......
......@@ -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]
......
......@@ -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]
......
pub use core_io::*;
pub fn last_os_error() -> Error {
use platform;
let errno = unsafe { platform::errno };
Error::from_raw_os_error(errno)
}
//! 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};
......@@ -909,14 +908,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 {
......
......@@ -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)
}
}
......@@ -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));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment