step.c 3.4 KB
Newer Older
R
Roland McGrath 已提交
1 2 3 4 5 6 7
/*
 * x86 single-step support code, common to 32-bit and 64-bit.
 */
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/ptrace.h>

R
Roland McGrath 已提交
8 9 10
#ifdef CONFIG_X86_32
static
#endif
R
Roland McGrath 已提交
11 12 13 14
unsigned long convert_rip_to_linear(struct task_struct *child, struct pt_regs *regs)
{
	unsigned long addr, seg;

R
Roland McGrath 已提交
15
#ifdef CONFIG_X86_64
R
Roland McGrath 已提交
16 17
	addr = regs->rip;
	seg = regs->cs & 0xffff;
R
Roland McGrath 已提交
18 19 20 21 22 23 24 25
#else
	addr = regs->eip;
	seg = regs->xcs & 0xffff;
	if (regs->eflags & X86_EFLAGS_VM) {
		addr = (addr & 0xffff) + (seg << 4);
		return addr;
	}
#endif
R
Roland McGrath 已提交
26 27 28 29 30 31 32

	/*
	 * We'll assume that the code segments in the GDT
	 * are all zero-based. That is largely true: the
	 * TLS segments are used for data, and the PNPBIOS
	 * and APM bios ones we just ignore here.
	 */
R
Roland McGrath 已提交
33
	if ((seg & SEGMENT_TI_MASK) == SEGMENT_LDT) {
R
Roland McGrath 已提交
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
		u32 *desc;
		unsigned long base;

		seg &= ~7UL;

		mutex_lock(&child->mm->context.lock);
		if (unlikely((seg >> 3) >= child->mm->context.size))
			addr = -1L; /* bogus selector, access would fault */
		else {
			desc = child->mm->context.ldt + seg;
			base = ((desc[0] >> 16) |
				((desc[1] & 0xff) << 16) |
				(desc[1] & 0xff000000));

			/* 16-bit code segment? */
			if (!((desc[1] >> 22) & 1))
				addr &= 0xffff;
			addr += base;
		}
		mutex_unlock(&child->mm->context.lock);
	}

	return addr;
}

static int is_setting_trap_flag(struct task_struct *child, struct pt_regs *regs)
{
	int i, copied;
	unsigned char opcode[15];
	unsigned long addr = convert_rip_to_linear(child, regs);

	copied = access_process_vm(child, addr, opcode, sizeof(opcode), 0);
	for (i = 0; i < copied; i++) {
		switch (opcode[i]) {
		/* popf and iret */
		case 0x9d: case 0xcf:
			return 1;

			/* CHECKME: 64 65 */

		/* opcode and address size prefixes */
		case 0x66: case 0x67:
			continue;
		/* irrelevant prefixes (segment overrides and repeats) */
		case 0x26: case 0x2e:
		case 0x36: case 0x3e:
		case 0x64: case 0x65:
R
Roland McGrath 已提交
81
		case 0xf0: case 0xf2: case 0xf3:
R
Roland McGrath 已提交
82 83
			continue;

R
Roland McGrath 已提交
84
#ifdef CONFIG_X86_64
R
Roland McGrath 已提交
85 86 87 88 89 90
		case 0x40 ... 0x4f:
			if (regs->cs != __USER_CS)
				/* 32-bit mode: register increment */
				return 0;
			/* 64-bit mode: REX prefix */
			continue;
R
Roland McGrath 已提交
91
#endif
R
Roland McGrath 已提交
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137

			/* CHECKME: f2, f3 */

		/*
		 * pushf: NOTE! We should probably not let
		 * the user see the TF bit being set. But
		 * it's more pain than it's worth to avoid
		 * it, and a debugger could emulate this
		 * all in user space if it _really_ cares.
		 */
		case 0x9c:
		default:
			return 0;
		}
	}
	return 0;
}

void user_enable_single_step(struct task_struct *child)
{
	struct pt_regs *regs = task_pt_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);

	/*
	 * If TF was already set, don't do anything else
	 */
	if (regs->eflags & X86_EFLAGS_TF)
		return;

	/* Set TF on the kernel stack.. */
	regs->eflags |= X86_EFLAGS_TF;

	/*
	 * ..but if TF is changed by the instruction we will trace,
	 * don't mark it as being "us" that set it, so that we
	 * won't clear it by hand later.
	 */
	if (is_setting_trap_flag(child, regs))
		return;

R
Roland McGrath 已提交
138
	set_tsk_thread_flag(child, TIF_FORCED_TF);
R
Roland McGrath 已提交
139 140 141 142 143 144 145 146
}

void user_disable_single_step(struct task_struct *child)
{
	/* Always clear TIF_SINGLESTEP... */
	clear_tsk_thread_flag(child, TIF_SINGLESTEP);

	/* But touch TF only if it was set by us.. */
R
Roland McGrath 已提交
147 148
	if (test_and_clear_tsk_thread_flag(child, TIF_FORCED_TF))
		task_pt_regs(child)->eflags &= ~X86_EFLAGS_TF;
R
Roland McGrath 已提交
149
}