2005-04-16 15:20:36 -07:00
/*
* linux / arch / arm / kernel / ptrace . c
*
* By Ross Biro 1 / 23 / 92
* edited by Linus Torvalds
* ARM modifications Copyright ( C ) 2000 Russell King
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/kernel.h>
# include <linux/sched.h>
# include <linux/mm.h>
# include <linux/smp.h>
# include <linux/ptrace.h>
# include <linux/user.h>
# include <linux/security.h>
# include <linux/init.h>
2005-05-01 08:59:14 -07:00
# include <linux/signal.h>
2005-04-16 15:20:36 -07:00
# include <asm/uaccess.h>
# include <asm/pgtable.h>
# include <asm/system.h>
# include <asm/traps.h>
# include "ptrace.h"
# define REG_PC 15
# define REG_PSR 16
/*
* does not yet catch signals sent when the child dies .
* in exit . c or in signal . c .
*/
#if 0
/*
* Breakpoint SWI instruction : SWI & 9F 0001
*/
# define BREAKINST_ARM 0xef9f0001
# define BREAKINST_THUMB 0xdf00 /* fill this in later */
# else
/*
* New breakpoints - use an undefined instruction . The ARM architecture
* reference manual guarantees that the following instruction space
* will produce an undefined instruction exception on all CPUs :
*
* ARM : xxxx 0111 1111 xxxx xxxx xxxx 1111 xxxx
* Thumb : 1101 1110 xxxx xxxx
*/
# define BREAKINST_ARM 0xe7f001f0
# define BREAKINST_THUMB 0xde01
# endif
/*
* 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 THREAD .
* this routine assumes that all the privileged stacks are in our
* data space .
*/
static inline long get_user_reg ( struct task_struct * task , int offset )
{
2006-01-12 01:05:57 -08:00
return task_pt_regs ( task ) - > uregs [ offset ] ;
2005-04-16 15:20:36 -07:00
}
/*
* this routine will put a word on the processes privileged stack .
* the offset is how far from the base addr as stored in the THREAD .
* this routine assumes that all the privileged stacks are in our
* data space .
*/
static inline int
put_user_reg ( struct task_struct * task , int offset , long data )
{
2006-01-12 01:05:57 -08:00
struct pt_regs newregs , * regs = task_pt_regs ( task ) ;
2005-04-16 15:20:36 -07:00
int ret = - EINVAL ;
newregs = * regs ;
newregs . uregs [ offset ] = data ;
if ( valid_user_regs ( & newregs ) ) {
regs - > uregs [ offset ] = data ;
ret = 0 ;
}
return ret ;
}
static inline int
read_u32 ( struct task_struct * task , unsigned long addr , u32 * res )
{
int ret ;
ret = access_process_vm ( task , addr , res , sizeof ( * res ) , 0 ) ;
return ret = = sizeof ( * res ) ? 0 : - EIO ;
}
static inline int
read_instr ( struct task_struct * task , unsigned long addr , u32 * res )
{
int ret ;
if ( addr & 1 ) {
u16 val ;
ret = access_process_vm ( task , addr & ~ 1 , & val , sizeof ( val ) , 0 ) ;
ret = ret = = sizeof ( val ) ? 0 : - EIO ;
* res = val ;
} else {
u32 val ;
ret = access_process_vm ( task , addr & ~ 3 , & val , sizeof ( val ) , 0 ) ;
ret = ret = = sizeof ( val ) ? 0 : - EIO ;
* res = val ;
}
return ret ;
}
/*
* Get value of register ` rn ' ( in the instruction )
*/
static unsigned long
ptrace_getrn ( struct task_struct * child , unsigned long insn )
{
unsigned int reg = ( insn > > 16 ) & 15 ;
unsigned long val ;
val = get_user_reg ( child , reg ) ;
if ( reg = = 15 )
val = pc_pointer ( val + 8 ) ;
return val ;
}
/*
* Get value of operand 2 ( in an ALU instruction )
*/
static unsigned long
ptrace_getaluop2 ( struct task_struct * child , unsigned long insn )
{
unsigned long val ;
int shift ;
int type ;
if ( insn & 1 < < 25 ) {
val = insn & 255 ;
shift = ( insn > > 8 ) & 15 ;
type = 3 ;
} else {
val = get_user_reg ( child , insn & 15 ) ;
if ( insn & ( 1 < < 4 ) )
shift = ( int ) get_user_reg ( child , ( insn > > 8 ) & 15 ) ;
else
shift = ( insn > > 7 ) & 31 ;
type = ( insn > > 5 ) & 3 ;
}
switch ( type ) {
case 0 : val < < = shift ; break ;
case 1 : val > > = shift ; break ;
case 2 :
val = ( ( ( signed long ) val ) > > shift ) ;
break ;
case 3 :
val = ( val > > shift ) | ( val < < ( 32 - shift ) ) ;
break ;
}
return val ;
}
/*
* Get value of operand 2 ( in a LDR instruction )
*/
static unsigned long
ptrace_getldrop2 ( struct task_struct * child , unsigned long insn )
{
unsigned long val ;
int shift ;
int type ;
val = get_user_reg ( child , insn & 15 ) ;
shift = ( insn > > 7 ) & 31 ;
type = ( insn > > 5 ) & 3 ;
switch ( type ) {
case 0 : val < < = shift ; break ;
case 1 : val > > = shift ; break ;
case 2 :
val = ( ( ( signed long ) val ) > > shift ) ;
break ;
case 3 :
val = ( val > > shift ) | ( val < < ( 32 - shift ) ) ;
break ;
}
return val ;
}
# define OP_MASK 0x01e00000
# define OP_AND 0x00000000
# define OP_EOR 0x00200000
# define OP_SUB 0x00400000
# define OP_RSB 0x00600000
# define OP_ADD 0x00800000
# define OP_ADC 0x00a00000
# define OP_SBC 0x00c00000
# define OP_RSC 0x00e00000
# define OP_ORR 0x01800000
# define OP_MOV 0x01a00000
# define OP_BIC 0x01c00000
# define OP_MVN 0x01e00000
static unsigned long
get_branch_address ( struct task_struct * child , unsigned long pc , unsigned long insn )
{
u32 alt = 0 ;
switch ( insn & 0x0e000000 ) {
case 0x00000000 :
case 0x02000000 : {
/*
* data processing
*/
long aluop1 , aluop2 , ccbit ;
2005-12-10 11:59:15 +00:00
if ( ( insn & 0x0fffffd0 ) = = 0x012fff10 ) {
/*
* bx or blx
*/
alt = get_user_reg ( child , insn & 15 ) ;
break ;
}
2005-04-16 15:20:36 -07:00
if ( ( insn & 0xf000 ) ! = 0xf000 )
break ;
aluop1 = ptrace_getrn ( child , insn ) ;
aluop2 = ptrace_getaluop2 ( child , insn ) ;
ccbit = get_user_reg ( child , REG_PSR ) & PSR_C_BIT ? 1 : 0 ;
switch ( insn & OP_MASK ) {
case OP_AND : alt = aluop1 & aluop2 ; break ;
case OP_EOR : alt = aluop1 ^ aluop2 ; break ;
case OP_SUB : alt = aluop1 - aluop2 ; break ;
case OP_RSB : alt = aluop2 - aluop1 ; break ;
case OP_ADD : alt = aluop1 + aluop2 ; break ;
case OP_ADC : alt = aluop1 + aluop2 + ccbit ; break ;
case OP_SBC : alt = aluop1 - aluop2 + ccbit ; break ;
case OP_RSC : alt = aluop2 - aluop1 + ccbit ; break ;
case OP_ORR : alt = aluop1 | aluop2 ; break ;
case OP_MOV : alt = aluop2 ; break ;
case OP_BIC : alt = aluop1 & ~ aluop2 ; break ;
case OP_MVN : alt = ~ aluop2 ; break ;
}
break ;
}
case 0x04000000 :
case 0x06000000 :
/*
* ldr
*/
if ( ( insn & 0x0010f000 ) = = 0x0010f000 ) {
unsigned long base ;
base = ptrace_getrn ( child , insn ) ;
if ( insn & 1 < < 24 ) {
long aluop2 ;
if ( insn & 0x02000000 )
aluop2 = ptrace_getldrop2 ( child , insn ) ;
else
aluop2 = insn & 0xfff ;
if ( insn & 1 < < 23 )
base + = aluop2 ;
else
base - = aluop2 ;
}
if ( read_u32 ( child , base , & alt ) = = 0 )
alt = pc_pointer ( alt ) ;
}
break ;
case 0x08000000 :
/*
* ldm
*/
if ( ( insn & 0x00108000 ) = = 0x00108000 ) {
unsigned long base ;
unsigned int nr_regs ;
if ( insn & ( 1 < < 23 ) ) {
nr_regs = hweight16 ( insn & 65535 ) < < 2 ;
if ( ! ( insn & ( 1 < < 24 ) ) )
nr_regs - = 4 ;
} else {
if ( insn & ( 1 < < 24 ) )
nr_regs = - 4 ;
else
nr_regs = 0 ;
}
base = ptrace_getrn ( child , insn ) ;
if ( read_u32 ( child , base + nr_regs , & alt ) = = 0 )
alt = pc_pointer ( alt ) ;
break ;
}
break ;
case 0x0a000000 : {
/*
* bl or b
*/
signed long displ ;
/* It's a branch/branch link: instead of trying to
* figure out whether the branch will be taken or not ,
* we ' ll put a breakpoint at both locations . This is
* simpler , more reliable , and probably not a whole lot
* slower than the alternative approach of emulating the
* branch .
*/
displ = ( insn & 0x00ffffff ) < < 8 ;
displ = ( displ > > 6 ) + 8 ;
if ( displ ! = 0 & & displ ! = 4 )
alt = pc + displ ;
}
break ;
}
return alt ;
}
static int
swap_insn ( struct task_struct * task , unsigned long addr ,
void * old_insn , void * new_insn , int size )
{
int ret ;
ret = access_process_vm ( task , addr , old_insn , size , 0 ) ;
if ( ret = = size )
ret = access_process_vm ( task , addr , new_insn , size , 1 ) ;
return ret ;
}
static void
add_breakpoint ( struct task_struct * task , struct debug_info * dbg , unsigned long addr )
{
int nr = dbg - > nsaved ;
if ( nr < 2 ) {
u32 new_insn = BREAKINST_ARM ;
int res ;
res = swap_insn ( task , addr , & dbg - > bp [ nr ] . insn , & new_insn , 4 ) ;
if ( res = = 4 ) {
dbg - > bp [ nr ] . address = addr ;
dbg - > nsaved + = 1 ;
}
} else
printk ( KERN_ERR " ptrace: too many breakpoints \n " ) ;
}
/*
* Clear one breakpoint in the user program . We copy what the hardware
* does and use bit 0 of the address to indicate whether this is a Thumb
* breakpoint or an ARM breakpoint .
*/
static void clear_breakpoint ( struct task_struct * task , struct debug_entry * bp )
{
unsigned long addr = bp - > address ;
union debug_insn old_insn ;
int ret ;
if ( addr & 1 ) {
ret = swap_insn ( task , addr & ~ 1 , & old_insn . thumb ,
& bp - > insn . thumb , 2 ) ;
if ( ret ! = 2 | | old_insn . thumb ! = BREAKINST_THUMB )
printk ( KERN_ERR " %s:%d: corrupted Thumb breakpoint at "
2007-10-18 23:40:41 -07:00
" 0x%08lx (0x%04x) \n " , task - > comm ,
task_pid_nr ( task ) , addr , old_insn . thumb ) ;
2005-04-16 15:20:36 -07:00
} else {
ret = swap_insn ( task , addr & ~ 3 , & old_insn . arm ,
& bp - > insn . arm , 4 ) ;
if ( ret ! = 4 | | old_insn . arm ! = BREAKINST_ARM )
printk ( KERN_ERR " %s:%d: corrupted ARM breakpoint at "
2007-10-18 23:40:41 -07:00
" 0x%08lx (0x%08x) \n " , task - > comm ,
task_pid_nr ( task ) , addr , old_insn . arm ) ;
2005-04-16 15:20:36 -07:00
}
}
void ptrace_set_bpt ( struct task_struct * child )
{
struct pt_regs * regs ;
unsigned long pc ;
u32 insn ;
int res ;
2006-01-12 01:05:57 -08:00
regs = task_pt_regs ( child ) ;
2005-04-16 15:20:36 -07:00
pc = instruction_pointer ( regs ) ;
if ( thumb_mode ( regs ) ) {
printk ( KERN_WARNING " ptrace: can't handle thumb mode \n " ) ;
return ;
}
res = read_instr ( child , pc , & insn ) ;
if ( ! res ) {
struct debug_info * dbg = & child - > thread . debug ;
unsigned long alt ;
dbg - > nsaved = 0 ;
alt = get_branch_address ( child , pc , insn ) ;
if ( alt )
add_breakpoint ( child , dbg , alt ) ;
/*
* Note that we ignore the result of setting the above
* breakpoint since it may fail . When it does , this is
* not so much an error , but a forewarning that we may
* be receiving a prefetch abort shortly .
*
* If we don ' t set this breakpoint here , then we can
* lose control of the thread during single stepping .
*/
if ( ! alt | | predicate ( insn ) ! = PREDICATE_ALWAYS )
add_breakpoint ( child , dbg , pc + 4 ) ;
}
}
/*
* Ensure no single - step breakpoint is pending . Returns non - zero
* value if child was being single - stepped .
*/
void ptrace_cancel_bpt ( struct task_struct * child )
{
int i , nsaved = child - > thread . debug . nsaved ;
child - > thread . debug . nsaved = 0 ;
if ( nsaved > 2 ) {
printk ( " ptrace_cancel_bpt: bogus nsaved: %d! \n " , nsaved ) ;
nsaved = 2 ;
}
for ( i = 0 ; i < nsaved ; i + + )
clear_breakpoint ( child , & child - > thread . debug . bp [ i ] ) ;
}
/*
* Called by kernel / ptrace . c when detaching . .
*/
void ptrace_disable ( struct task_struct * child )
{
2007-03-04 09:50:28 +00:00
single_step_disable ( child ) ;
2005-04-16 15:20:36 -07:00
}
/*
* Handle hitting a breakpoint .
*/
void ptrace_break ( struct task_struct * tsk , struct pt_regs * regs )
{
siginfo_t info ;
ptrace_cancel_bpt ( tsk ) ;
info . si_signo = SIGTRAP ;
info . si_errno = 0 ;
info . si_code = TRAP_BRKPT ;
info . si_addr = ( void __user * ) 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 .
*/
static int ptrace_read_user ( struct task_struct * tsk , unsigned long off ,
unsigned long __user * ret )
{
unsigned long tmp ;
if ( off & 3 | | off > = sizeof ( struct user ) )
return - EIO ;
tmp = 0 ;
if ( off < sizeof ( struct pt_regs ) )
tmp = get_user_reg ( tsk , off > > 2 ) ;
return put_user ( tmp , ret ) ;
}
/*
* Write the word at offset " off " into " struct user " . We
* actually access the pt_regs stored on the kernel stack .
*/
static int ptrace_write_user ( struct task_struct * tsk , unsigned long off ,
unsigned long val )
{
if ( off & 3 | | off > = sizeof ( struct user ) )
return - EIO ;
if ( off > = sizeof ( struct pt_regs ) )
return 0 ;
return put_user_reg ( tsk , off > > 2 , val ) ;
}
/*
* Get all user integer registers .
*/
static int ptrace_getregs ( struct task_struct * tsk , void __user * uregs )
{
2006-01-12 01:05:57 -08:00
struct pt_regs * regs = task_pt_regs ( tsk ) ;
2005-04-16 15:20:36 -07:00
return copy_to_user ( uregs , regs , sizeof ( struct pt_regs ) ) ? - EFAULT : 0 ;
}
/*
* Set all user integer registers .
*/
static int ptrace_setregs ( struct task_struct * tsk , void __user * uregs )
{
struct pt_regs newregs ;
int ret ;
ret = - EFAULT ;
if ( copy_from_user ( & newregs , uregs , sizeof ( struct pt_regs ) ) = = 0 ) {
2006-01-12 01:05:57 -08:00
struct pt_regs * regs = task_pt_regs ( tsk ) ;
2005-04-16 15:20:36 -07:00
ret = - EINVAL ;
if ( valid_user_regs ( & newregs ) ) {
* regs = newregs ;
ret = 0 ;
}
}
return ret ;
}
/*
* Get the child FPU state .
*/
static int ptrace_getfpregs ( struct task_struct * tsk , void __user * ufp )
{
2006-01-12 01:05:56 -08:00
return copy_to_user ( ufp , & task_thread_info ( tsk ) - > fpstate ,
2005-04-16 15:20:36 -07:00
sizeof ( struct user_fp ) ) ? - EFAULT : 0 ;
}
/*
* Set the child FPU state .
*/
static int ptrace_setfpregs ( struct task_struct * tsk , void __user * ufp )
{
2006-01-12 01:05:56 -08:00
struct thread_info * thread = task_thread_info ( tsk ) ;
2005-04-16 15:20:36 -07:00
thread - > used_cp [ 1 ] = thread - > used_cp [ 2 ] = 1 ;
return copy_from_user ( & thread - > fpstate , ufp ,
sizeof ( struct user_fp ) ) ? - EFAULT : 0 ;
}
# ifdef CONFIG_IWMMXT
/*
* Get the child iWMMXt state .
*/
static int ptrace_getwmmxregs ( struct task_struct * tsk , void __user * ufp )
{
2006-01-12 01:05:56 -08:00
struct thread_info * thread = task_thread_info ( tsk ) ;
2005-04-16 15:20:36 -07:00
if ( ! test_ti_thread_flag ( thread , TIF_USING_IWMMXT ) )
return - ENODATA ;
iwmmxt_task_disable ( thread ) ; /* force it to ram */
2006-03-12 22:36:06 +00:00
return copy_to_user ( ufp , & thread - > fpstate . iwmmxt , IWMMXT_SIZE )
? - EFAULT : 0 ;
2005-04-16 15:20:36 -07:00
}
/*
* Set the child iWMMXt state .
*/
static int ptrace_setwmmxregs ( struct task_struct * tsk , void __user * ufp )
{
2006-01-12 01:05:56 -08:00
struct thread_info * thread = task_thread_info ( tsk ) ;
2005-04-16 15:20:36 -07:00
if ( ! test_ti_thread_flag ( thread , TIF_USING_IWMMXT ) )
return - EACCES ;
iwmmxt_task_release ( thread ) ; /* force a reload */
2006-03-15 14:57:13 +00:00
return copy_from_user ( & thread - > fpstate . iwmmxt , ufp , IWMMXT_SIZE )
2006-03-12 22:36:06 +00:00
? - EFAULT : 0 ;
2005-04-16 15:20:36 -07:00
}
# endif
2006-06-27 22:56:19 +01:00
# ifdef CONFIG_CRUNCH
/*
* Get the child Crunch state .
*/
static int ptrace_getcrunchregs ( struct task_struct * tsk , void __user * ufp )
{
struct thread_info * thread = task_thread_info ( tsk ) ;
crunch_task_disable ( thread ) ; /* force it to ram */
return copy_to_user ( ufp , & thread - > crunchstate , CRUNCH_SIZE )
? - EFAULT : 0 ;
}
/*
* Set the child Crunch state .
*/
static int ptrace_setcrunchregs ( struct task_struct * tsk , void __user * ufp )
{
struct thread_info * thread = task_thread_info ( tsk ) ;
crunch_task_release ( thread ) ; /* force a reload */
return copy_from_user ( & thread - > crunchstate , ufp , CRUNCH_SIZE )
? - EFAULT : 0 ;
}
# endif
2005-11-07 00:59:47 -08:00
long arch_ptrace ( struct task_struct * child , long request , long addr , long data )
2005-04-16 15:20:36 -07:00
{
int ret ;
switch ( request ) {
/*
* read word at location " addr " in the child process .
*/
case PTRACE_PEEKTEXT :
case PTRACE_PEEKDATA :
2007-07-17 04:03:43 -07:00
ret = generic_ptrace_peekdata ( child , addr , data ) ;
2005-04-16 15:20:36 -07:00
break ;
case PTRACE_PEEKUSR :
ret = ptrace_read_user ( child , addr , ( unsigned long __user * ) data ) ;
break ;
/*
* write the word at location addr .
*/
case PTRACE_POKETEXT :
case PTRACE_POKEDATA :
2007-07-17 04:03:44 -07:00
ret = generic_ptrace_pokedata ( child , addr , data ) ;
2005-04-16 15:20:36 -07:00
break ;
case PTRACE_POKEUSR :
ret = ptrace_write_user ( child , addr , data ) ;
break ;
/*
* continue / restart and stop at next ( return from ) syscall
*/
case PTRACE_SYSCALL :
case PTRACE_CONT :
ret = - EIO ;
2005-05-01 08:59:14 -07:00
if ( ! valid_signal ( data ) )
2005-04-16 15:20:36 -07:00
break ;
if ( request = = PTRACE_SYSCALL )
set_tsk_thread_flag ( child , TIF_SYSCALL_TRACE ) ;
else
clear_tsk_thread_flag ( child , TIF_SYSCALL_TRACE ) ;
child - > exit_code = data ;
2007-03-04 09:50:28 +00:00
single_step_disable ( child ) ;
2005-04-16 15:20:36 -07:00
wake_up_process ( child ) ;
ret = 0 ;
break ;
/*
* make the child exit . Best I can do is send it a sigkill .
* perhaps it should be put in the status that it wants to
* exit .
*/
case PTRACE_KILL :
2007-03-04 09:50:28 +00:00
single_step_disable ( child ) ;
2005-04-16 15:20:36 -07:00
if ( child - > exit_state ! = EXIT_ZOMBIE ) {
child - > exit_code = SIGKILL ;
wake_up_process ( child ) ;
}
ret = 0 ;
break ;
/*
* execute single instruction .
*/
case PTRACE_SINGLESTEP :
ret = - EIO ;
2005-05-01 08:59:14 -07:00
if ( ! valid_signal ( data ) )
2005-04-16 15:20:36 -07:00
break ;
2007-03-04 09:50:28 +00:00
single_step_enable ( child ) ;
2005-04-16 15:20:36 -07:00
clear_tsk_thread_flag ( child , TIF_SYSCALL_TRACE ) ;
child - > exit_code = data ;
/* give it a chance to run. */
wake_up_process ( child ) ;
ret = 0 ;
break ;
case PTRACE_GETREGS :
ret = ptrace_getregs ( child , ( void __user * ) data ) ;
break ;
case PTRACE_SETREGS :
ret = ptrace_setregs ( child , ( void __user * ) data ) ;
break ;
case PTRACE_GETFPREGS :
ret = ptrace_getfpregs ( child , ( void __user * ) data ) ;
break ;
case PTRACE_SETFPREGS :
ret = ptrace_setfpregs ( child , ( void __user * ) data ) ;
break ;
# ifdef CONFIG_IWMMXT
case PTRACE_GETWMMXREGS :
ret = ptrace_getwmmxregs ( child , ( void __user * ) data ) ;
break ;
case PTRACE_SETWMMXREGS :
ret = ptrace_setwmmxregs ( child , ( void __user * ) data ) ;
break ;
# endif
case PTRACE_GET_THREAD_AREA :
2006-01-12 01:05:56 -08:00
ret = put_user ( task_thread_info ( child ) - > tp_value ,
2005-04-16 15:20:36 -07:00
( unsigned long __user * ) data ) ;
break ;
2006-01-14 19:30:04 +00:00
case PTRACE_SET_SYSCALL :
2007-05-06 13:56:26 +01:00
task_thread_info ( child ) - > syscall = data ;
2006-01-14 19:30:04 +00:00
ret = 0 ;
break ;
2006-06-27 22:56:19 +01:00
# ifdef CONFIG_CRUNCH
case PTRACE_GETCRUNCHREGS :
ret = ptrace_getcrunchregs ( child , ( void __user * ) data ) ;
break ;
case PTRACE_SETCRUNCHREGS :
ret = ptrace_setcrunchregs ( child , ( void __user * ) data ) ;
break ;
# endif
2005-04-16 15:20:36 -07:00
default :
ret = ptrace_request ( child , request , addr , data ) ;
break ;
}
return ret ;
}
2006-01-14 19:30:04 +00:00
asmlinkage int syscall_trace ( int why , struct pt_regs * regs , int scno )
2005-04-16 15:20:36 -07:00
{
unsigned long ip ;
if ( ! test_thread_flag ( TIF_SYSCALL_TRACE ) )
2006-01-14 19:30:04 +00:00
return scno ;
2005-04-16 15:20:36 -07:00
if ( ! ( current - > ptrace & PT_PTRACED ) )
2006-01-14 19:30:04 +00:00
return scno ;
2005-04-16 15:20:36 -07:00
/*
* Save IP . IP is used to denote syscall entry / exit :
* IP = 0 - > entry , = 1 - > exit
*/
ip = regs - > ARM_ip ;
regs - > ARM_ip = why ;
2007-05-06 13:56:26 +01:00
current_thread_info ( ) - > syscall = scno ;
2006-01-14 19:30:04 +00:00
2005-04-16 15:20:36 -07:00
/* the 0x80 provides a way for the tracing parent to distinguish
between a syscall stop and SIGTRAP delivery */
ptrace_notify ( SIGTRAP | ( ( current - > ptrace & PT_TRACESYSGOOD )
? 0x80 : 0 ) ) ;
/*
* this isn ' t the same as continuing with a signal , but it will do
* for normal use . strace only continues with a signal if the
* stopping signal is not SIGTRAP . - brl
*/
if ( current - > exit_code ) {
send_sig ( current - > exit_code , current , 1 ) ;
current - > exit_code = 0 ;
}
regs - > ARM_ip = ip ;
2006-01-14 19:30:04 +00:00
2007-05-06 13:56:26 +01:00
return current_thread_info ( ) - > syscall ;
2005-04-16 15:20:36 -07:00
}