fix PTRACE_PEEKUSER, PTRACE_POKEUSER, PTRACE_GETREGS.

support PTRACE_SETREGS.
  In struct process, add 'unsigned long *ptrace_debugreg', instead of 'struct user *userp'.
  debug registers are read/written from/to ptrace_debugreg, save/restore in schedule().
  most general registers are proc->uctx.
  fs_base is proc->thread.tlsblock_base.
  gs_base,ds,es,fs,gs and orig_rax are uncompleted.
  other members in 'struct user' are ignored, same as Linux implementation.

refs #257
refs #373
refs #263
This commit is contained in:
Susumu Komae
2015-01-29 14:08:38 +09:00
committed by postpeta
parent 9c35935671
commit c3ade864d9
5 changed files with 296 additions and 98 deletions

View File

@@ -16,8 +16,23 @@
#include <types.h> #include <types.h>
#define RFLAGS_CF (1 << 0)
#define RFLAGS_PF (1 << 2)
#define RFLAGS_AF (1 << 4)
#define RFLAGS_ZF (1 << 6)
#define RFLAGS_SF (1 << 7)
#define RFLAGS_TF (1 << 8) #define RFLAGS_TF (1 << 8)
#define RFLAGS_IF (1 << 9) #define RFLAGS_IF (1 << 9)
#define RFLAGS_DF (1 << 10)
#define RFLAGS_OF (1 << 11)
#define RFLAGS_IOPL (3 << 12)
#define RFLAGS_NT (1 << 14)
#define RFLAGS_RF (1 << 16)
#define RFLAGS_VM (1 << 17)
#define RFLAGS_AC (1 << 18)
#define RFLAGS_VIF (1 << 19)
#define RFLAGS_VIP (1 << 20)
#define RFLAGS_ID (1 << 21)
#define MSR_EFER 0xc0000080 #define MSR_EFER 0xc0000080
#define MSR_STAR 0xc0000081 #define MSR_STAR 0xc0000081

View File

@@ -190,89 +190,198 @@ do_setpgid(int pid, int pgid)
} }
} }
void static unsigned long *ptrace_get_regaddr(struct process *proc, long addr)
pokeuser(struct process *proc, void *regs0)
{ {
struct x86_regs *regs = regs0; #define PTRACE_GET_REGADDR(regname) case offsetof(struct user_regs_struct, regname): return &(proc->uctx->regname)
switch (addr) {
if(regs == NULL){ PTRACE_GET_REGADDR(r15);
asm("movq %%gs:132, %0" : "=r" (regs)); PTRACE_GET_REGADDR(r14);
--regs; PTRACE_GET_REGADDR(r13);
PTRACE_GET_REGADDR(r12);
PTRACE_GET_REGADDR(rbp);
PTRACE_GET_REGADDR(rbx);
PTRACE_GET_REGADDR(r11);
PTRACE_GET_REGADDR(r10);
PTRACE_GET_REGADDR(r9);
PTRACE_GET_REGADDR(r8);
PTRACE_GET_REGADDR(rax);
PTRACE_GET_REGADDR(rcx);
PTRACE_GET_REGADDR(rdx);
PTRACE_GET_REGADDR(rsi);
PTRACE_GET_REGADDR(rdi);
/* skip orig_rax */
PTRACE_GET_REGADDR(rip);
PTRACE_GET_REGADDR(cs);
/* skip eflags */
PTRACE_GET_REGADDR(rsp);
PTRACE_GET_REGADDR(ss);
/* skip fs_base gs_base ds es fs gs */
default:
break;
} }
asm("mov %0, %%db0" ::"r" (proc->userp->u_debugreg[0])); return NULL;
asm("mov %0, %%db1" ::"r" (proc->userp->u_debugreg[1])); #undef PTRACE_GET_REGADDR
asm("mov %0, %%db2" ::"r" (proc->userp->u_debugreg[2])); }
asm("mov %0, %%db3" ::"r" (proc->userp->u_debugreg[3]));
// asm("mov %0, %%db4" ::"r" (proc->userp->u_debugreg[4])); #define RFLAGS_MASK (RFLAGS_CF | RFLAGS_PF | RFLAGS_AF | RFLAGS_ZF | \
// asm("mov %0, %%db5" ::"r" (proc->userp->u_debugreg[5])); RFLAGS_SF | RFLAGS_TF | RFLAGS_DF | RFLAGS_OF | \
asm("mov %0, %%db6" ::"r" (proc->userp->u_debugreg[6])); RFLAGS_NT | RFLAGS_RF | RFLAGS_AC)
asm("mov %0, %%db7" ::"r" (proc->userp->u_debugreg[7])); #define DB6_RESERVED_MASK (0xffffffffffff1ff0UL)
regs->r15 = proc->userp->regs.r15; #define DB6_RESERVED_SET (0xffff0ff0UL)
regs->r14 = proc->userp->regs.r14; #define DB7_RESERVED_MASK (0xffffffff0000dc00UL)
regs->r13 = proc->userp->regs.r13; #define DB7_RESERVED_SET (0x400UL)
regs->r12 = proc->userp->regs.r12;
regs->rbp = proc->userp->regs.rbp; long
regs->rbx = proc->userp->regs.rbx; ptrace_read_user(struct process *proc, long addr, unsigned long *value)
regs->r11 = proc->userp->regs.r11; {
regs->r10 = proc->userp->regs.r10; unsigned long *p;
regs->r9 = proc->userp->regs.r9;
regs->r8 = proc->userp->regs.r8; if (addr < sizeof(struct user_regs_struct)) {
regs->rax = proc->userp->regs.rax; if (addr & (sizeof(*value) - 1)) return -EIO;
regs->rcx = proc->userp->regs.rcx; if (addr == offsetof(struct user_regs_struct, eflags)) {
regs->rdx = proc->userp->regs.rdx; *value = proc->uctx->rflags;
regs->rsi = proc->userp->regs.rsi; return 0;
regs->rdi = proc->userp->regs.rdi; }
regs->rip = proc->userp->regs.rip; if (addr == offsetof(struct user_regs_struct, fs_base)) {
regs->cs = proc->userp->regs.cs; *value = proc->thread.tlsblock_base;
regs->rflags = proc->userp->regs.eflags; return 0;
regs->rsp = proc->userp->regs.rsp; }
regs->ss = proc->userp->regs.ss; p = ptrace_get_regaddr(proc, addr);
if (p) {
*value = *p;
} else {
dkprintf("ptrace_read_user,addr=%d\n", addr);
*value = 0;
}
return 0;
}
if (offsetof(struct user, u_debugreg[0]) <= addr &&
addr < offsetof(struct user, u_debugreg[8])) {
if (addr & (sizeof(*value) - 1)) return -EIO;
if (proc->ptrace_debugreg == NULL) {
kprintf("ptrace_read_user: missing ptrace_debugreg\n");
return -EFAULT;
}
p = &proc->ptrace_debugreg[(addr - offsetof(struct user, u_debugreg[0])) / sizeof(*value)];
*value = *p;
return 0;
}
/* SUCCESS others */
dkprintf("ptrace_read_user,addr=%d\n", addr);
*value = 0;
return 0;
}
long
ptrace_write_user(struct process *proc, long addr, unsigned long value)
{
unsigned long *p;
if (addr < sizeof(struct user_regs_struct)) {
if (addr & (sizeof(value) - 1)) return -EIO;
if (addr == offsetof(struct user_regs_struct, eflags)) {
proc->uctx->rflags &= ~RFLAGS_MASK;
proc->uctx->rflags |= (value & RFLAGS_MASK);
return 0;
}
if (addr == offsetof(struct user_regs_struct, fs_base)) {
proc->thread.tlsblock_base = value;
return 0;
}
p = ptrace_get_regaddr(proc, addr);
if (p) {
*p = value;
} else {
dkprintf("ptrace_write_user,addr=%d\n", addr);
}
return 0;
}
if (offsetof(struct user, u_debugreg[0]) <= addr &&
addr < offsetof(struct user, u_debugreg[8])) {
if (addr & (sizeof(value) - 1)) return -EIO;
if (proc->ptrace_debugreg == NULL) {
kprintf("ptrace_write_user: missing ptrace_debugreg\n");
return -EFAULT;
}
p = &proc->ptrace_debugreg[(addr - offsetof(struct user, u_debugreg[0])) / sizeof(value)];
if (addr == offsetof(struct user, u_debugreg[6])) {
value &= ~DB6_RESERVED_MASK;
value |= DB6_RESERVED_SET;
}
if (addr == offsetof(struct user, u_debugreg[7])) {
value &= ~DB7_RESERVED_MASK;
value |= DB7_RESERVED_SET;
}
*p = value;
return 0;
}
/* SUCCESS others */
dkprintf("ptrace_write_user,addr=%d\n", addr);
return 0;
}
long
alloc_debugreg(struct process *proc)
{
proc->ptrace_debugreg = kmalloc(sizeof(*proc->ptrace_debugreg) * 8, IHK_MC_AP_NOWAIT);
if (proc->ptrace_debugreg == NULL) {
kprintf("alloc_debugreg: no memory.\n");
return -ENOMEM;
}
memset(proc->ptrace_debugreg, '\0', sizeof(*proc->ptrace_debugreg) * 8);
proc->ptrace_debugreg[6] = DB6_RESERVED_SET;
proc->ptrace_debugreg[7] = DB7_RESERVED_SET;
return 0;
} }
void void
peekuser(struct process *proc, void *regs0) save_debugreg(unsigned long *debugreg)
{ {
struct x86_regs *regs = regs0; asm("mov %%db0, %0" :"=r" (debugreg[0]));
asm("mov %%db1, %0" :"=r" (debugreg[1]));
asm("mov %%db2, %0" :"=r" (debugreg[2]));
asm("mov %%db3, %0" :"=r" (debugreg[3]));
// asm("mov %%db4, %0" :"=r" (debugreg[4]));
// asm("mov %%db5, %0" :"=r" (debugreg[5]));
debugreg[4] = debugreg[5] = 0;
asm("mov %%db6, %0" :"=r" (debugreg[6]));
asm("mov %%db7, %0" :"=r" (debugreg[7]));
}
if(regs == NULL){ void
asm("movq %%gs:132, %0" : "=r" (regs)); restore_debugreg(unsigned long *debugreg)
--regs; {
} asm("mov %0, %%db0" ::"r" (debugreg[0]));
if(proc->userp == NULL){ asm("mov %0, %%db1" ::"r" (debugreg[1]));
proc->userp = kmalloc(sizeof(struct user), IHK_MC_AP_NOWAIT); asm("mov %0, %%db2" ::"r" (debugreg[2]));
memset(proc->userp, '\0', sizeof(struct user)); asm("mov %0, %%db3" ::"r" (debugreg[3]));
} // asm("mov %0, %%db4" ::"r" (debugreg[4]));
asm("mov %%db0, %0" :"=r" (proc->userp->u_debugreg[0])); // asm("mov %0, %%db5" ::"r" (debugreg[5]));
asm("mov %%db1, %0" :"=r" (proc->userp->u_debugreg[1])); asm("mov %0, %%db6" ::"r" (debugreg[6]));
asm("mov %%db2, %0" :"=r" (proc->userp->u_debugreg[2])); asm("mov %0, %%db7" ::"r" (debugreg[7]));
asm("mov %%db3, %0" :"=r" (proc->userp->u_debugreg[3])); }
// asm("mov %%db4, %0" :"=r" (proc->userp->u_debugreg[4]));
// asm("mov %%db5, %0" :"=r" (proc->userp->u_debugreg[5])); void
asm("mov %%db6, %0" :"=r" (proc->userp->u_debugreg[6])); clear_debugreg(void)
asm("mov %%db7, %0" :"=r" (proc->userp->u_debugreg[7])); {
proc->userp->regs.r15 = regs->r15; unsigned long r = 0;
proc->userp->regs.r14 = regs->r14; asm("mov %0, %%db0" ::"r" (r));
proc->userp->regs.r13 = regs->r13; asm("mov %0, %%db1" ::"r" (r));
proc->userp->regs.r12 = regs->r12; asm("mov %0, %%db2" ::"r" (r));
proc->userp->regs.rbp = regs->rbp; asm("mov %0, %%db3" ::"r" (r));
proc->userp->regs.rbx = regs->rbx; // asm("mov %0, %%db4" ::"r" (r));
proc->userp->regs.r11 = regs->r11; // asm("mov %0, %%db5" ::"r" (r));
proc->userp->regs.r10 = regs->r10; r = DB6_RESERVED_SET;
proc->userp->regs.r9 = regs->r9; asm("mov %0, %%db6" ::"r" (r));
proc->userp->regs.r8 = regs->r8; r = DB7_RESERVED_SET;
proc->userp->regs.rax = regs->rax; asm("mov %0, %%db7" ::"r" (r));
proc->userp->regs.rcx = regs->rcx; }
proc->userp->regs.rdx = regs->rdx;
proc->userp->regs.rsi = regs->rsi; void clear_single_step(struct process *proc)
proc->userp->regs.rdi = regs->rdi; {
proc->userp->regs.rip = regs->rip; proc->uctx->rflags &= ~RFLAGS_TF;
proc->userp->regs.cs = regs->cs;
proc->userp->regs.eflags = regs->rflags;
proc->userp->regs.rsp = regs->rsp;
proc->userp->regs.ss = regs->ss;
asm("mov %%es, %0" :"=r" (proc->userp->regs.es));
asm("mov %%fs, %0" :"=r" (proc->userp->regs.fs));
asm("mov %%gs, %0" :"=r" (proc->userp->regs.gs));
} }
extern void coredump(struct process *proc, void *regs); extern void coredump(struct process *proc, void *regs);
@@ -283,7 +392,6 @@ static void ptrace_report_signal(struct process *proc, struct x86_regs *regs, in
dkprintf("ptrace_report_signal,pid=%d\n", proc->ftn->pid); dkprintf("ptrace_report_signal,pid=%d\n", proc->ftn->pid);
peekuser(proc, regs);
ihk_mc_spinlock_lock_noirq(&proc->ftn->lock); ihk_mc_spinlock_lock_noirq(&proc->ftn->lock);
proc->ftn->exit_status = sig; proc->ftn->exit_status = sig;
/* Transition process state */ /* Transition process state */

View File

@@ -352,7 +352,7 @@ struct process {
cpu_set_t cpu_set; cpu_set_t cpu_set;
unsigned long saved_auxv[AUXV_LEN]; unsigned long saved_auxv[AUXV_LEN];
struct user *userp; unsigned long *ptrace_debugreg; /* debug registers for ptrace */
}; };
struct process_vm { struct process_vm {

View File

@@ -43,6 +43,11 @@
#endif #endif
extern long do_arch_prctl(unsigned long code, unsigned long address); extern long do_arch_prctl(unsigned long code, unsigned long address);
extern long alloc_debugreg(struct process *proc);
extern void save_debugreg(unsigned long *debugreg);
extern void restore_debugreg(unsigned long *debugreg);
extern void clear_debugreg(void);
extern void clear_single_step(struct process *proc);
static void insert_vm_range_list(struct process_vm *vm, static void insert_vm_range_list(struct process_vm *vm,
struct vm_range *newrange); struct vm_range *newrange);
static int copy_user_ranges(struct process *proc, struct process *org); static int copy_user_ranges(struct process *proc, struct process *org);
@@ -378,6 +383,13 @@ int ptrace_traceme(void){
ihk_mc_spinlock_unlock_noirq(&proc->ftn->lock); ihk_mc_spinlock_unlock_noirq(&proc->ftn->lock);
if (proc->ptrace_debugreg == NULL) {
error = alloc_debugreg(proc);
}
clear_single_step(proc);
/* TODO: other flags may reset */
out: out:
dkprintf("ptrace_traceme,returning,error=%d\n", error); dkprintf("ptrace_traceme,returning,error=%d\n", error);
return error; return error;
@@ -1803,6 +1815,9 @@ void destroy_process(struct process *proc)
list_del(&pending->list); list_del(&pending->list);
kfree(pending); kfree(pending);
} }
if (proc->ptrace_debugreg) {
kfree(proc->ptrace_debugreg);
}
ihk_mc_free_pages(proc, KERNEL_STACK_NR_PAGES); ihk_mc_free_pages(proc, KERNEL_STACK_NR_PAGES);
} }
@@ -2043,6 +2058,16 @@ redo:
ihk_mc_get_processor_id(), ihk_mc_get_processor_id(),
prev ? prev->ftn->tid : 0, next ? next->ftn->tid : 0); prev ? prev->ftn->tid : 0, next ? next->ftn->tid : 0);
if (prev && prev->ptrace_debugreg) {
save_debugreg(prev->ptrace_debugreg);
if (next->ptrace_debugreg == NULL) {
clear_debugreg();
}
}
if (next->ptrace_debugreg) {
restore_debugreg(next->ptrace_debugreg);
}
ihk_mc_load_page_table(next->vm->page_table); ihk_mc_load_page_table(next->vm->page_table);
dkprintf("[%d] schedule: tlsblock_base: 0x%lX\n", dkprintf("[%d] schedule: tlsblock_base: 0x%lX\n",

View File

@@ -1433,8 +1433,6 @@ SYSCALL_DECLARE(arch_prctl)
ihk_mc_syscall_arg1(ctx)); ihk_mc_syscall_arg1(ctx));
} }
extern void peekuser(struct process *proc, void *regs);
static int ptrace_report_exec(struct process *proc) static int ptrace_report_exec(struct process *proc)
{ {
dkprintf("ptrace_report_exec,enter\n"); dkprintf("ptrace_report_exec,enter\n");
@@ -1474,7 +1472,6 @@ static int ptrace_report_exec(struct process *proc)
waitq_wakeup(&proc->ftn->parent->waitpid_q); waitq_wakeup(&proc->ftn->parent->waitpid_q);
} }
peekuser(proc, NULL);
/* Sleep */ /* Sleep */
dkprintf("ptrace_report_exec,sleeping\n"); dkprintf("ptrace_report_exec,sleeping\n");
@@ -1583,8 +1580,6 @@ found:
ihk_mc_spinlock_unlock_noirq(&new->ftn->lock); ihk_mc_spinlock_unlock_noirq(&new->ftn->lock);
} }
peekuser(proc, NULL);
return error; return error;
} }
@@ -2618,6 +2613,9 @@ out:
return error; return error;
} }
extern long ptrace_read_user(struct process *proc, long addr, unsigned long *value);
extern long ptrace_write_user(struct process *proc, long addr, unsigned long value);
static long ptrace_pokeuser(int pid, long addr, long data) static long ptrace_pokeuser(int pid, long addr, long data)
{ {
long rc = -EIO; long rc = -EIO;
@@ -2631,8 +2629,7 @@ static long ptrace_pokeuser(int pid, long addr, long data)
if (!child) if (!child)
return -ESRCH; return -ESRCH;
if(child->ftn->status == PS_TRACED){ if(child->ftn->status == PS_TRACED){
memcpy((char *)child->userp + addr, &data, 8); rc = ptrace_write_user(child, addr, (unsigned long)data);
rc = 0;
} }
ihk_mc_spinlock_unlock(savelock, irqstate); ihk_mc_spinlock_unlock(savelock, irqstate);
@@ -2643,6 +2640,7 @@ static long ptrace_peekuser(int pid, long addr, long data)
{ {
long rc = -EIO; long rc = -EIO;
struct process *child; struct process *child;
struct process *proc = cpu_local_var(current);
ihk_spinlock_t *savelock; ihk_spinlock_t *savelock;
unsigned long irqstate; unsigned long irqstate;
unsigned long *p = (unsigned long *)data; unsigned long *p = (unsigned long *)data;
@@ -2653,10 +2651,11 @@ static long ptrace_peekuser(int pid, long addr, long data)
if (!child) if (!child)
return -ESRCH; return -ESRCH;
if(child->ftn->status == PS_TRACED){ if(child->ftn->status == PS_TRACED){
if(copy_to_user(child, p, (char *)child->userp + addr, 8)) unsigned long value;
rc = -EFAULT; rc = ptrace_read_user(child, addr, &value);
else if (rc == 0) {
rc = 0; rc = copy_to_user(proc, p, (char *)&value, sizeof(value));
}
} }
ihk_mc_spinlock_unlock(savelock, irqstate); ihk_mc_spinlock_unlock(savelock, irqstate);
@@ -2668,6 +2667,7 @@ static long ptrace_getregs(int pid, long data)
struct user_regs_struct *regs = (struct user_regs_struct *)data; struct user_regs_struct *regs = (struct user_regs_struct *)data;
long rc = -EIO; long rc = -EIO;
struct process *child; struct process *child;
struct process *proc = cpu_local_var(current);
ihk_spinlock_t *savelock; ihk_spinlock_t *savelock;
unsigned long irqstate; unsigned long irqstate;
@@ -2675,16 +2675,57 @@ static long ptrace_getregs(int pid, long data)
if (!child) if (!child)
return -ESRCH; return -ESRCH;
if(child->ftn->status == PS_TRACED){ if(child->ftn->status == PS_TRACED){
if(copy_to_user(child, regs, child->userp, sizeof(struct user_regs_struct))) struct user_regs_struct user_regs;
rc = -EFAULT; long addr;
else unsigned long *p;
rc = 0; memset(&user_regs, '\0', sizeof(struct user_regs_struct));
for (addr = 0, p = (unsigned long *)&user_regs;
addr < sizeof(struct user_regs_struct);
addr += sizeof(*p), p++) {
rc = ptrace_read_user(child, addr, p);
if (rc) break;
}
if (rc == 0) {
rc = copy_to_user(proc, regs, &user_regs, sizeof(struct user_regs_struct));
}
} }
ihk_mc_spinlock_unlock(savelock, irqstate); ihk_mc_spinlock_unlock(savelock, irqstate);
return rc; return rc;
} }
static long ptrace_setregs(int pid, long data)
{
struct user_regs_struct *regs = (struct user_regs_struct *)data;
long rc = -EIO;
struct process *child;
struct process *proc = cpu_local_var(current);
ihk_spinlock_t *savelock;
unsigned long irqstate;
child = findthread_and_lock(pid, -1, &savelock, &irqstate);
if (!child)
return -ESRCH;
if(child->ftn->status == PS_TRACED){
struct user_regs_struct user_regs;
rc = copy_from_user(proc, &user_regs, regs, sizeof(struct user_regs_struct));
if (rc == 0) {
long addr;
unsigned long *p;
for (addr = 0, p = (unsigned long *)&user_regs;
addr < sizeof(struct user_regs_struct);
addr += sizeof(*p), p++) {
rc = ptrace_write_user(child, addr, *p);
if (rc) {
break;
}
}
}
}
ihk_mc_spinlock_unlock(savelock, irqstate);
return rc;
}
static int ptrace_setoptions(int pid, int flags) static int ptrace_setoptions(int pid, int flags)
{ {
int ret; int ret;
@@ -2724,6 +2765,8 @@ out:
return ret; return ret;
} }
extern void clear_single_step(struct process *proc);
static int ptrace_detach(int pid, int data) static int ptrace_detach(int pid, int data)
{ {
int error; int error;
@@ -2775,11 +2818,16 @@ found:
ihk_mc_spinlock_unlock_noirq(&proc->ftn->parent->lock); ihk_mc_spinlock_unlock_noirq(&proc->ftn->parent->lock);
} }
proc->uctx->rflags &= ~RFLAGS_TF; /* SingleStep clear */
/* TODO: other flags may clear */
ihk_mc_spinlock_unlock_noirq(&proc->ftn->lock); ihk_mc_spinlock_unlock_noirq(&proc->ftn->lock);
if (proc->ptrace_debugreg) {
kfree(proc->ptrace_debugreg);
proc->ptrace_debugreg = NULL;
}
clear_single_step(proc);
/* TODO: other flags may clear */
if (data != 0) { if (data != 0) {
struct process *cur; struct process *cur;
@@ -2811,6 +2859,7 @@ static long ptrace_geteventmsg(int pid, long data)
unsigned long *msg_p = (unsigned long *)data; unsigned long *msg_p = (unsigned long *)data;
long rc = -ESRCH; long rc = -ESRCH;
struct process *child; struct process *child;
struct process *proc = cpu_local_var(current);
ihk_spinlock_t *savelock; ihk_spinlock_t *savelock;
unsigned long irqstate; unsigned long irqstate;
@@ -2819,7 +2868,7 @@ static long ptrace_geteventmsg(int pid, long data)
return -ESRCH; return -ESRCH;
} }
if (child->ftn->status == PS_TRACED) { if (child->ftn->status == PS_TRACED) {
if (copy_to_user(child, msg_p, &child->ftn->ptrace_eventmsg, sizeof(*msg_p))) { if (copy_to_user(proc, msg_p, &child->ftn->ptrace_eventmsg, sizeof(*msg_p))) {
rc = -EFAULT; rc = -EFAULT;
} else { } else {
rc = 0; rc = 0;
@@ -2889,7 +2938,8 @@ SYSCALL_DECLARE(ptrace)
dkprintf("ptrace: unimplemented ptrace(PTRACE_SETFPREGS) called.\n"); dkprintf("ptrace: unimplemented ptrace(PTRACE_SETFPREGS) called.\n");
break; break;
case PTRACE_SETREGS: case PTRACE_SETREGS:
dkprintf("ptrace: unimplemented ptrace(PTRACE_SETREGS) called.\n"); error = ptrace_setregs(pid, data);
dkprintf("PTRACE_SETREGS: data=%p return=%p\n", data, error);
break; break;
case PTRACE_ATTACH: case PTRACE_ATTACH:
dkprintf("ptrace: unimplemented ptrace(PTRACE_ATTACH) called.\n"); dkprintf("ptrace: unimplemented ptrace(PTRACE_ATTACH) called.\n");