Commit 70ba5e7c authored by Russell King's avatar Russell King

[ARM] switch ptrace to use an undefined instruction

This avoids a problem with the original ptrace code using a system
call (SWI) to implement single stepping; programs such as ltrace
do not expect to receive system call trace traps when breakpoints
are hit.
parent 9a8185da
......@@ -18,10 +18,12 @@
#include <linux/ptrace.h>
#include <linux/user.h>
#include <linux/security.h>
#include <linux/init.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h>
#include <asm/system.h>
#include <asm/traps.h>
#include "ptrace.h"
......@@ -32,7 +34,7 @@
* in exit.c or in signal.c.
*/
#if 1
#if 0
/*
* Breakpoint SWI instruction: SWI &9F0001
*/
......@@ -479,25 +481,47 @@ void ptrace_break(struct task_struct *tsk, struct pt_regs *regs)
{
siginfo_t info;
/*
* The PC is always left pointing at the next instruction. Fix this.
*/
regs->ARM_pc -= 4;
if (tsk->thread.debug.nsaved == 0)
printk(KERN_ERR "ptrace: bogus breakpoint trap\n");
ptrace_cancel_bpt(tsk);
info.si_signo = SIGTRAP;
info.si_errno = 0;
info.si_code = TRAP_BRKPT;
info.si_addr = (void *)instruction_pointer(regs) -
(thumb_mode(regs) ? 2 : 4);
info.si_addr = (void *)instruction_pointer(regs);
force_sig_info(SIGTRAP, &info, tsk);
}
static int break_trap(struct pt_regs *regs, unsigned int instr)
{
ptrace_break(current, regs);
return 0;
}
static struct undef_hook arm_break_hook = {
.instr_mask = 0x0fffffff,
.instr_val = 0x07f001f0,
.cpsr_mask = PSR_T_BIT,
.cpsr_val = 0,
.fn = break_trap,
};
static struct undef_hook thumb_break_hook = {
.instr_mask = 0xffff,
.instr_val = 0xde01,
.cpsr_mask = PSR_T_BIT,
.cpsr_val = PSR_T_BIT,
.fn = break_trap,
};
static int __init ptrace_break_init(void)
{
register_undef_hook(&arm_break_hook);
register_undef_hook(&thumb_break_hook);
return 0;
}
core_initcall(ptrace_break_init);
/*
* Read the word at offset "off" into the "struct user". We
* actually access the pt_regs stored on the kernel stack.
......
......@@ -34,7 +34,7 @@
#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/unistd.h>
#include <asm/semaphore.h>
#include <asm/traps.h>
#include "ptrace.h"
......@@ -240,17 +240,56 @@ void die_if_kernel(const char *str, struct pt_regs *regs, int err)
die(str, regs, err);
}
static LIST_HEAD(undef_hook);
static spinlock_t undef_lock = SPIN_LOCK_UNLOCKED;
void register_undef_hook(struct undef_hook *hook)
{
spin_lock_irq(&undef_lock);
list_add(&hook->node, &undef_hook);
spin_unlock_irq(&undef_lock);
}
void unregister_undef_hook(struct undef_hook *hook)
{
spin_lock_irq(&undef_lock);
list_del(&hook->node);
spin_unlock_irq(&undef_lock);
}
asmlinkage void do_undefinstr(struct pt_regs *regs)
{
unsigned long *pc;
unsigned int correction = thumb_mode(regs) ? 2 : 4;
unsigned int instr;
struct undef_hook *hook;
siginfo_t info;
void *pc;
/*
* According to the ARM ARM, PC is 2 or 4 bytes ahead, depending
* whether we're in Thumb mode or not.
* According to the ARM ARM, PC is 2 or 4 bytes ahead,
* depending whether we're in Thumb mode or not.
* Correct this offset.
*/
regs->ARM_pc -= thumb_mode(regs) ? 2 : 4;
pc = (unsigned long *)instruction_pointer(regs);
regs->ARM_pc -= correction;
pc = (void *)instruction_pointer(regs);
if (thumb_mode(regs)) {
get_user(instr, (u16 *)pc);
} else {
get_user(instr, (u32 *)pc);
}
spin_lock_irq(&undef_lock);
list_for_each_entry(hook, &undef_hook, node) {
if ((instr & hook->instr_mask) == hook->instr_val &&
(regs->ARM_cpsr & hook->cpsr_mask) == hook->cpsr_val) {
if (hook->fn(regs, instr) == 0) {
spin_unlock_irq(&undef_lock);
return;
}
}
}
spin_unlock_irq(&undef_lock);
#ifdef CONFIG_DEBUG_USER
printk(KERN_INFO "%s (%d): undefined instruction: pc=%p\n",
......@@ -377,6 +416,7 @@ asmlinkage int arm_syscall(int no, struct pt_regs *regs)
return 0;
case NR(breakpoint): /* SWI BREAK_POINT */
regs->ARM_pc -= thumb_mode(regs) ? 2 : 4;
ptrace_break(current, regs);
return regs->ARM_r0;
......
#ifndef _ASMARM_TRAP_H
#define _ASMARM_TRAP_H
#include <linux/list.h>
struct undef_hook {
struct list_head node;
u32 instr_mask;
u32 instr_val;
u32 cpsr_mask;
u32 cpsr_val;
int (*fn)(struct pt_regs *regs, unsigned int instr);
};
void register_undef_hook(struct undef_hook *hook);
void unregister_undef_hook(struct undef_hook *hook);
#endif
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