113 lines
3.5 KiB
C
113 lines
3.5 KiB
C
#include <stdint.h>
|
|
#include <errno.h>
|
|
#include <sys/ptrace.h>
|
|
#include <kernel/printf.h>
|
|
#include <kernel/process.h>
|
|
#include <kernel/string.h>
|
|
#include <kernel/signal.h>
|
|
#include <kernel/syscall.h>
|
|
#include <kernel/ptrace.h>
|
|
#include <kernel/args.h>
|
|
|
|
#include <kernel/arch/x86_64/regs.h>
|
|
#include <kernel/arch/x86_64/mmu.h>
|
|
|
|
long ptrace_attach(pid_t pid) {
|
|
process_t * tracee = process_from_pid(pid);
|
|
if (!tracee) return -ESRCH;
|
|
if (this_core->current_process->user != 0 && this_core->current_process->user != tracee->user) return -EPERM;
|
|
__sync_or_and_fetch(&tracee->flags, PROC_FLAG_TRACED);
|
|
tracee->tracer = this_core->current_process->id;
|
|
send_signal(pid, SIGSTOP, 1);
|
|
return 0;
|
|
}
|
|
|
|
long ptrace_self(void) {
|
|
process_t * parent = process_get_parent((process_t*)this_core->current_process);
|
|
if (!parent) return -EINVAL;
|
|
|
|
__sync_or_and_fetch(&this_core->current_process->flags, PROC_FLAG_TRACED);
|
|
this_core->current_process->tracer = parent->id;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Trigger a ptrace event on the currently executing thread.
|
|
*/
|
|
long ptrace_signal(int signal, int reason) {
|
|
__sync_or_and_fetch(&this_core->current_process->flags, PROC_FLAG_SUSPENDED);
|
|
this_core->current_process->status = 0x7F | (signal << 8) | (reason << 16);
|
|
|
|
process_t * parent = process_from_pid(this_core->current_process->tracer);
|
|
if (parent && !(parent->flags & PROC_FLAG_FINISHED)) {
|
|
wakeup_queue(parent->wait_queue);
|
|
}
|
|
switch_task(0);
|
|
|
|
int signum = (this_core->current_process->status >> 8);
|
|
this_core->current_process->status = 0;
|
|
return signum;
|
|
}
|
|
|
|
long ptrace_continue(pid_t pid, int sig) {
|
|
process_t * tracee = process_from_pid(pid);
|
|
if (!tracee || (tracee->tracer != this_core->current_process->id) || !(tracee->flags & PROC_FLAG_SUSPENDED)) return -ESRCH;
|
|
|
|
/* Unsuspend */
|
|
__sync_and_and_fetch(&tracee->flags, ~(PROC_FLAG_SUSPENDED));
|
|
tracee->status = (sig << 8);
|
|
make_process_ready(tracee);
|
|
|
|
return 0;
|
|
}
|
|
|
|
long ptrace_getregs(pid_t pid, void * data) {
|
|
if (!data || ptr_validate(data, "ptrace")) return -EFAULT;
|
|
process_t * tracee = process_from_pid(pid);
|
|
if (!tracee || (tracee->tracer != this_core->current_process->id) || !(tracee->flags & PROC_FLAG_SUSPENDED)) return -ESRCH;
|
|
|
|
/* Copy registers */
|
|
memcpy(data, tracee->interrupt_registers ? tracee->interrupt_registers : tracee->syscall_registers, sizeof(struct regs));
|
|
|
|
return 0;
|
|
}
|
|
|
|
long ptrace_peek(pid_t pid, void * addr, void * data) {
|
|
if (!data || ptr_validate(data, "ptrace")) return -EFAULT;
|
|
process_t * tracee = process_from_pid(pid);
|
|
if (!tracee || (tracee->tracer != this_core->current_process->id) || !(tracee->flags & PROC_FLAG_SUSPENDED)) return -ESRCH;
|
|
|
|
/* Figure out where *addr is...
|
|
* TODO: We don't ever really page things to disk, and we don't have file
|
|
* mappings that may be not-present, so this is fairly straightforward for now... */
|
|
uintptr_t mapped_address = mmu_map_to_physical(tracee->thread.page_directory->directory, (uintptr_t)addr);
|
|
|
|
if ((intptr_t)mapped_address < 0 && (intptr_t)mapped_address > -10) return -EFAULT;
|
|
|
|
uintptr_t blarg = (uintptr_t)mmu_map_from_physical(mapped_address);
|
|
|
|
/* Yeah, uh, one byte. That works. */
|
|
*(char*)data = *(char*)blarg;
|
|
|
|
return 0;
|
|
}
|
|
|
|
long ptrace_handle(long request, pid_t pid, void * addr, void * data) {
|
|
switch (request) {
|
|
case PTRACE_ATTACH:
|
|
return ptrace_attach(pid);
|
|
case PTRACE_TRACEME:
|
|
return ptrace_self();
|
|
case PTRACE_GETREGS:
|
|
return ptrace_getregs(pid,data);
|
|
case PTRACE_CONT:
|
|
return ptrace_continue(pid,(uintptr_t)data);
|
|
case PTRACE_PEEKDATA:
|
|
return ptrace_peek(pid,addr,data);
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|