Commit 304bddb4 authored by Linus Torvalds's avatar Linus Torvalds

x86: be a lot more careful about TF handling.

This should fix Wine for some games that otherwise
seem to think that they run under a debugger.
parent c25f215b
...@@ -42,6 +42,12 @@ ...@@ -42,6 +42,12 @@
*/ */
#define EFL_OFFSET ((EFL-2)*4-sizeof(struct pt_regs)) #define EFL_OFFSET ((EFL-2)*4-sizeof(struct pt_regs))
static inline struct pt_regs *get_child_regs(struct task_struct *task)
{
void *stack_top = (void *)task->thread.esp0;
return stack_top - sizeof(struct pt_regs);
}
/* /*
* this routine will get a word off of the processes privileged stack. * this routine will get a word off of the processes privileged stack.
* the offset is how far from the base addr as stored in the TSS. * the offset is how far from the base addr as stored in the TSS.
...@@ -140,22 +146,35 @@ static unsigned long getreg(struct task_struct *child, ...@@ -140,22 +146,35 @@ static unsigned long getreg(struct task_struct *child,
static void set_singlestep(struct task_struct *child) static void set_singlestep(struct task_struct *child)
{ {
long eflags; struct pt_regs *regs = get_child_regs(child);
/*
* Always set TIF_SINGLESTEP - this guarantees that
* we single-step system calls etc.. This will also
* cause us to set TF when returning to user mode.
*/
set_tsk_thread_flag(child, TIF_SINGLESTEP); set_tsk_thread_flag(child, TIF_SINGLESTEP);
eflags = get_stack_long(child, EFL_OFFSET);
put_stack_long(child, EFL_OFFSET, eflags | TRAP_FLAG); /*
* If TF was already set, don't do anything else
*/
if (regs->eflags & TRAP_FLAG)
return;
/* Set TF on the kernel stack, and set the flag to say so */
regs->eflags |= TRAP_FLAG;
child->ptrace |= PT_DTRACE; child->ptrace |= PT_DTRACE;
} }
static void clear_singlestep(struct task_struct *child) static void clear_singlestep(struct task_struct *child)
{ {
if (child->ptrace & PT_DTRACE) { /* Always clear TIF_SINGLESTEP... */
long eflags;
clear_tsk_thread_flag(child, TIF_SINGLESTEP); clear_tsk_thread_flag(child, TIF_SINGLESTEP);
eflags = get_stack_long(child, EFL_OFFSET);
put_stack_long(child, EFL_OFFSET, eflags & ~TRAP_FLAG); /* But touch TF only if it was set by us.. */
if (child->ptrace & PT_DTRACE) {
struct pt_regs *regs = get_child_regs(child);
regs->eflags &= ~TRAP_FLAG;
child->ptrace &= ~PT_DTRACE; child->ptrace &= ~PT_DTRACE;
} }
} }
......
...@@ -270,7 +270,6 @@ setup_sigcontext(struct sigcontext __user *sc, struct _fpstate __user *fpstate, ...@@ -270,7 +270,6 @@ setup_sigcontext(struct sigcontext __user *sc, struct _fpstate __user *fpstate,
struct pt_regs *regs, unsigned long mask) struct pt_regs *regs, unsigned long mask)
{ {
int tmp, err = 0; int tmp, err = 0;
unsigned long eflags;
tmp = 0; tmp = 0;
__asm__("movl %%gs,%0" : "=r"(tmp): "0"(tmp)); __asm__("movl %%gs,%0" : "=r"(tmp): "0"(tmp));
...@@ -292,16 +291,7 @@ setup_sigcontext(struct sigcontext __user *sc, struct _fpstate __user *fpstate, ...@@ -292,16 +291,7 @@ setup_sigcontext(struct sigcontext __user *sc, struct _fpstate __user *fpstate,
err |= __put_user(current->thread.error_code, &sc->err); err |= __put_user(current->thread.error_code, &sc->err);
err |= __put_user(regs->eip, &sc->eip); err |= __put_user(regs->eip, &sc->eip);
err |= __put_user(regs->xcs, (unsigned int __user *)&sc->cs); err |= __put_user(regs->xcs, (unsigned int __user *)&sc->cs);
err |= __put_user(regs->eflags, &sc->eflags);
/*
* Iff TF was set because the program is being single-stepped by a
* debugger, don't save that information on the signal stack.. We
* don't want debugging to change state.
*/
eflags = regs->eflags;
if (current->ptrace & PT_DTRACE)
eflags &= ~TF_MASK;
err |= __put_user(eflags, &sc->eflags);
err |= __put_user(regs->esp, &sc->esp_at_signal); err |= __put_user(regs->esp, &sc->esp_at_signal);
err |= __put_user(regs->xss, (unsigned int __user *)&sc->ss); err |= __put_user(regs->xss, (unsigned int __user *)&sc->ss);
...@@ -424,11 +414,9 @@ static void setup_frame(int sig, struct k_sigaction *ka, ...@@ -424,11 +414,9 @@ static void setup_frame(int sig, struct k_sigaction *ka,
* The tracer may want to single-step inside the * The tracer may want to single-step inside the
* handler too. * handler too.
*/ */
if (regs->eflags & TF_MASK) {
regs->eflags &= ~TF_MASK; regs->eflags &= ~TF_MASK;
if (current->ptrace & PT_DTRACE) if (test_thread_flag(TIF_SINGLESTEP))
ptrace_notify(SIGTRAP); ptrace_notify(SIGTRAP);
}
#if DEBUG_SIG #if DEBUG_SIG
printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n", printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n",
...@@ -519,11 +507,9 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, ...@@ -519,11 +507,9 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
* The tracer may want to single-step inside the * The tracer may want to single-step inside the
* handler too. * handler too.
*/ */
if (regs->eflags & TF_MASK) {
regs->eflags &= ~TF_MASK; regs->eflags &= ~TF_MASK;
if (current->ptrace & PT_DTRACE) if (test_thread_flag(TIF_SINGLESTEP))
ptrace_notify(SIGTRAP); ptrace_notify(SIGTRAP);
}
#if DEBUG_SIG #if DEBUG_SIG
printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n", printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n",
......
...@@ -717,9 +717,18 @@ fastcall void do_debug(struct pt_regs * regs, long error_code) ...@@ -717,9 +717,18 @@ fastcall void do_debug(struct pt_regs * regs, long error_code)
*/ */
if ((regs->xcs & 3) == 0) if ((regs->xcs & 3) == 0)
goto clear_TF_reenable; goto clear_TF_reenable;
if ((tsk->ptrace & (PT_DTRACE|PT_PTRACED)) == PT_DTRACE)
/*
* Was the TF flag set by a debugger? If so, clear it now,
* so that register information is correct.
*/
if (tsk->ptrace & PT_DTRACE) {
regs->eflags &= ~TF_MASK;
tsk->ptrace &= ~PT_DTRACE;
if (!tsk->ptrace & PT_DTRACE)
goto clear_TF; goto clear_TF;
} }
}
/* Ok, finally something we can handle */ /* Ok, finally something we can handle */
send_sigtrap(tsk, regs, error_code); send_sigtrap(tsk, regs, error_code);
......
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