patch-1.3.22 linux/arch/alpha/kernel/ptrace.c

Next file: linux/arch/alpha/kernel/setup.c
Previous file: linux/arch/alpha/kernel/process.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v1.3.21/linux/arch/alpha/kernel/ptrace.c linux/arch/alpha/kernel/ptrace.c
@@ -2,6 +2,7 @@
 /* By Ross Biro 1/23/92 */
 /* edited by Linus Torvalds */
 /* mangled further by Bob Manson ([email protected]) */
+/* more mutilation by David Mosberger ([email protected]) */
 
 #include <linux/head.h>
 #include <linux/kernel.h>
@@ -21,8 +22,9 @@
 #ifdef DEBUG
 
   enum {
-      DBG_MEM = (1<<0),
-      DBG_BPT = (1<<1)
+      DBG_MEM		= (1<<0),
+      DBG_BPT		= (1<<1),
+      DBG_MEM_ALL	= (1<<2)
   };
 
   int debug_mask = DBG_BPT;
@@ -35,49 +37,74 @@
 
 #define BREAKINST	0x00000080	/* call_pal bpt */
 
-/* This was determined via brute force. */
-#define MAGICNUM 496
-
 /*
  * does not yet catch signals sent when the child dies.
  * in exit.c or in signal.c.
  */
 
-/* A mapping between register number and its offset on the kernel stack.
- * You also need to add MAGICNUM to get past the kernel stack frame
- * to the actual saved user info.
- * The first portion is the switch_stack, then comes the pt_regs.
- * 320 is the size of the switch_stack area.
+/*
+ * Processes always block with the following stack-layout:
+ *
+ *  +================================+ --------------------------
+ *  | PALcode saved frame (ps, pc,   | ^		      ^
+ *  | gp, a0, a1, a2)		     | |		      |
+ *  +================================+ | struct pt_regs	      |
+ *  |	        		     | |		      |
+ *  | frame generated by SAVE_ALL    | |		      |
+ *  |	        		     | v		      | P
+ *  +================================+			      |	A
+ *  |	        		     | ^		      |	G
+ *  | frame saved by do_switch_stack | | struct switch_stack  |	E
+ *  |	        		     | v		      |	_
+ *  +================================+			      |	S
+ *  |	        		     |			      |	I
+ *  |	        		     |			      |	Z
+ *  /	        		     /			      |	E
+ *  /	                             /			      |
+ *  |				     |			      |
+ *  |				     |			      |
+ *  |				     |			      v
+ *  +================================+ <-------------------------
+ *					task->kernel_stack_page
+ */
+#define PT_REG(reg)	(PAGE_SIZE - sizeof(struct pt_regs)	\
+			 + (long)&((struct pt_regs *)0)->reg)
+#define SW_REG(reg)	(PAGE_SIZE - sizeof(struct pt_regs)	\
+			 - sizeof(struct switch_stack)		\
+			 + (long)&((struct switch_stack *)0)->reg)
+/* 
+ * The following table maps a register index into the stack offset at
+ * which the register is saved.  Register indices are 0-31 for integer
+ * regs, 32-63 for fp regs, and 64 for the pc.  Notice that sp and
+ * zero have no stack-slot and need to be treated specially (see
+ * get_reg/put_reg below).
  */
-
 enum {
-	REG_R0 =  0,
-	REG_F0 = 32,
-	REG_PC = 64
+	REG_R0 = 0, REG_F0 = 32, REG_PC = 64
 };
 
-static int map_reg_to_offset[] = {
-   320+0,320+8,320+16,320+24,320+32,320+40,320+48,320+56,320+64, /* 0-8 */
-   0,8,16,24,32,40,48, /* 9-15 */
-   320+184,320+192,320+200, /* 16-18 */
-   320+72,320+80,320+88,320+96,320+104,320+112,320+120, /* 19-25 */
-   320+128,320+136,320+144,320+176,320+160,-1, /* 26-31*/
-
-   /* fp registers below */
-   64,72,80,88,96,104,112,120,128,136,144,152,160,168,176,184,192,
-   200,208,216,224,232,240,248,256,264,272,280,288,296,304,312,
-
-   /* 64 = pc */
-   320+168
+static unsigned short regoff[] = {
+	PT_REG(	   r0), PT_REG(	   r1), PT_REG(	   r2), PT_REG(	  r3),
+	PT_REG(	   r4), PT_REG(	   r5), PT_REG(	   r6), PT_REG(	  r7),
+	PT_REG(	   r8), SW_REG(	   r9), SW_REG(	  r10), SW_REG(	 r11),
+	SW_REG(	  r12), SW_REG(	  r13), SW_REG(	  r14), SW_REG(	 r15),
+	PT_REG(	  r16), PT_REG(	  r17), PT_REG(	  r18), PT_REG(	 r19),
+	PT_REG(	  r20), PT_REG(	  r21), PT_REG(	  r22), PT_REG(	 r23),
+	PT_REG(	  r24), PT_REG(	  r25), PT_REG(	  r26), PT_REG(	 r27),
+	PT_REG(	  r28), PT_REG(	   gp),		   -1,		   -1,
+	SW_REG(fp[ 0]), SW_REG(fp[ 1]), SW_REG(fp[ 2]), SW_REG(fp[ 3]),
+	SW_REG(fp[ 4]), SW_REG(fp[ 5]), SW_REG(fp[ 6]), SW_REG(fp[ 7]),
+	SW_REG(fp[ 8]), SW_REG(fp[ 9]), SW_REG(fp[10]), SW_REG(fp[11]),
+	SW_REG(fp[12]), SW_REG(fp[13]), SW_REG(fp[14]), SW_REG(fp[15]),
+	SW_REG(fp[16]), SW_REG(fp[17]), SW_REG(fp[18]), SW_REG(fp[19]),
+	SW_REG(fp[20]), SW_REG(fp[21]), SW_REG(fp[22]), SW_REG(fp[23]),
+	SW_REG(fp[24]), SW_REG(fp[25]), SW_REG(fp[26]), SW_REG(fp[27]),
+	SW_REG(fp[28]), SW_REG(fp[29]), SW_REG(fp[30]), SW_REG(fp[31]),
+	PT_REG(	   pc)
 };
 
-static int offset_of_register(int reg_num)
-{
-	if (reg_num < 0 || reg_num > 64) {
-		return -1;
-	}
-	return map_reg_to_offset[reg_num];
-}
+static long zero;
+
 
 /* change a pid into a task struct. */
 static inline struct task_struct * get_task(int pid)
@@ -92,36 +119,38 @@
 }
 
 /*
- * 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.  
- * this routine assumes that all the privileged stacks are in our
- * data space.
- * MAGICNUM is the amount to skip to get to the actual user regs. It
- * was determined by brute force & asking BufElves.
- */   
-static inline long get_stack_long(struct task_struct *task, unsigned long offset)
+ * Get contents of register REGNO in task TASK.
+ */
+static inline long get_reg(struct task_struct * task, long regno)
 {
-	unsigned char *stack;
+	long *addr;
 
-	stack = (unsigned char *)task->tss.ksp;
-	stack += offset+MAGICNUM;
-	return (*((long *)stack));
+	if (regno == 30) {
+		addr = &task->tss.usp;
+	} else if (regno == 31) {
+		zero = 0;
+		addr = &zero;
+	} else {
+		addr = (long *) (task->kernel_stack_page + regoff[regno]);
+	}
+	return *addr;
 }
 
 /*
- * this routine will put a word on the processes privileged stack. 
- * the offset is how far from the base addr as stored in the TSS.  
- * this routine assumes that all the privileged stacks are in our
- * data space.
+ * Write contents of register REGNO in task TASK.
  */
-static inline int put_stack_long(struct task_struct *task, unsigned long offset,
-	unsigned long data)
+static inline int put_reg(struct task_struct *task, long regno, long data)
 {
-	unsigned char * stack;
+	long *addr, zero;
 
-	stack = (unsigned char *) task->tss.ksp;
-	stack += offset+MAGICNUM;
-	*(unsigned long *) stack = data;
+	if (regno == 30) {
+		addr = &task->tss.usp;
+	} else if (regno == 31) {
+		addr = &zero;
+	} else {
+		addr = (long *) (task->kernel_stack_page + regoff[regno]);
+	}
+	*addr = data;
 	return 0;
 }
 
@@ -138,7 +167,7 @@
 	pte_t * pgtable;
 	unsigned long page;
 
-	DBG(DBG_MEM, ("Getting long at 0x%lx\n", addr));
+	DBG(DBG_MEM_ALL, ("getting long at 0x%lx\n", addr));
 repeat:
 	pgdir = pgd_offset(vma->vm_task, addr);
 	if (pgd_none(*pgdir)) {
@@ -226,11 +255,12 @@
 		*(unsigned long *) (page + (addr & ~PAGE_MASK)) = data;
 /* we're bypassing pagetables, so we have to set the dirty bit ourselves */
 /* this should also re-instate whatever read-only mode there was before */
-	*pgtable = pte_mkdirty(mk_pte(page, vma->vm_page_prot));
+	set_pte(pgtable, pte_mkdirty(mk_pte(page, vma->vm_page_prot)));
 	invalidate();
 }
 
-static struct vm_area_struct * find_extend_vma(struct task_struct * tsk, unsigned long addr)
+static struct vm_area_struct * find_extend_vma(struct task_struct * tsk,
+					       unsigned long addr)
 {
 	struct vm_area_struct * vma;
 
@@ -258,57 +288,35 @@
 {
 	struct vm_area_struct * vma = find_extend_vma(tsk, addr);
 
-	DBG(DBG_MEM, ("in read_long\n"));
+	DBG(DBG_MEM_ALL, ("in read_long\n"));
 	if (!vma) {
 	        printk("Unable to find vma for addr 0x%lx\n",addr);
 		return -EIO;
 	}
-	if ((addr & ~PAGE_MASK) > (PAGE_SIZE-sizeof(long))) {
-		unsigned long low,high;
+	if ((addr & ~PAGE_MASK) > (PAGE_SIZE - sizeof(long))) {
 		struct vm_area_struct * vma_high = vma;
+		unsigned long low, align;
 
 		if (addr + sizeof(long) >= vma->vm_end) {
 			vma_high = vma->vm_next;
 			if (!vma_high || vma_high->vm_start != vma->vm_end)
 				return -EIO;
 		}
-		low = get_long(vma, addr & ~(sizeof(long)-1));
-		high = get_long(vma_high, (addr+sizeof(long)) & ~(sizeof(long)-1));
-		switch (addr & (sizeof(long)-1)) {
-			case 1:
-				low >>= 8;
-				low |= high << 56;
-				break;
-			case 2:
-				low >>= 16;
-				low |= high << 48;
-				break;
-			case 3:
-				low >>= 24;
-				low |= high << 40;
-				break;
-			case 4:
-				low >>= 32;
-				low |= high << 32;
-				break;
-			case 5:
-				low >>= 40;
-				low |= high << 24;
-				break;
-			case 6:
-				low >>= 48;
-				low |= high << 16;
-				break;
-			case 7:
-				low >>= 56;
-				low |= high << 8;
-				break;
+		align = addr & (sizeof(long) - 1);
+		addr -= align;
+		low = get_long(vma, addr);
+		if (align) {
+		    unsigned long high;
+
+		    high = get_long(vma_high, addr + sizeof(long));
+		    low >>= align * 8;
+		    low  |= high << (64 - align * 8);
 		}
 		*result = low;
 	} else {
-	        long l =get_long(vma, addr);
+	        long l = get_long(vma, addr);
 
-		DBG(DBG_MEM, ("value is 0x%lx\n",l));
+		DBG(DBG_MEM_ALL, ("value is 0x%lx\n", l));
 		*result = l;
 	}
 	return 0;
@@ -326,7 +334,7 @@
 	if (!vma)
 		return -EIO;
 	if ((addr & ~PAGE_MASK) > PAGE_SIZE-sizeof(long)) {
-		unsigned long low,high;
+		unsigned long low, high, align;
 		struct vm_area_struct * vma_high = vma;
 
 		if (addr + sizeof(long) >= vma->vm_end) {
@@ -334,58 +342,16 @@
 			if (!vma_high || vma_high->vm_start != vma->vm_end)
 				return -EIO;
 		}
-		low = get_long(vma, addr & ~(sizeof(long)-1));
-		high = get_long(vma_high, (addr+sizeof(long)) & ~(sizeof(long)-1));
-		switch (addr & (sizeof(long)-1)) {
-			case 0: /* shouldn't happen, but safety first */
-				low = data;
-				break;
-			case 1:
-				low &= 0x00000000000000ffL;
-				low |= data << 8;
-				high &= ~0x000000000000ffL;
-				high |= data >> 56;
-				break;
-			case 2:
-				low &= 0x000000000000ffffL;
-				low |= data << 16;
-				high &= ~0x0000000000ffffL;
-				high |= data >> 48;
-				break;
-			case 3:
-				low &= 0x0000000000ffffffL;
-				low |= data << 24;
-				high &= ~0x00000000ffffffL;
-				high |= data >> 40;
-				break;
-		        case 4:
-				low &= 0x00000000ffffffffL;
-				low |= data << 32;
-				high &= ~0x000000ffffffffL;
-				high |= data >> 32;
-				break;
-
-			case 5:
-				low &= 0x000000ffffffffffL;
-				low |= data << 40;
-				high &= ~0x0000ffffffffffL;
-				high |= data >> 24;
-				break;
-			case 6:
-				low &= 0x0000ffffffffffffL;
-				low |= data << 48;
-				high &= ~0x00ffffffffffffL;
-				high |= data >> 16;
-				break;
-			case 7:
-				low &= 0x00ffffffffffffffL;
-				low |= data << 56;
-				high &= ~0xffffffffffffffL;
-				high |= data >> 8;
-				break;
-		}
-		put_long(vma, addr & ~(sizeof(long)-1),low);
-		put_long(vma_high, (addr+sizeof(long)) & ~(sizeof(long)-1),high);
+		align = addr & (sizeof(long) - 1);
+		addr -= align;
+		low  = get_long(vma, addr);
+		high = get_long(vma_high, addr + sizeof(long));
+		low  &= ~0UL >> (64 - align * 8);
+		high &= ~0UL << (align * 8);
+		low  |= data << (align * 8);
+		high |= data >> (64 - align * 8);
+		put_long(vma, addr, low);
+		put_long(vma_high, addr + sizeof(long), high);
 	} else
 		put_long(vma, addr, data);
 	return 0;
@@ -394,7 +360,8 @@
 /*
  * Read a 32bit int from address space TSK.
  */
-static int read_int(struct task_struct * tsk, unsigned long addr, unsigned int *data)
+static int read_int(struct task_struct * tsk, unsigned long addr,
+		    unsigned int *data)
 {
 	unsigned long l, align;
 	int res;
@@ -420,7 +387,8 @@
  * For simplicity, do a read-modify-write of the 64bit word that
  * contains the 32bit word that we are about to write.
  */
-static int write_int(struct task_struct * tsk, unsigned long addr, unsigned int data)
+static int write_int(struct task_struct * tsk, unsigned long addr,
+		     unsigned int data)
 {
 	unsigned long l, align;
 	int res;
@@ -441,36 +409,15 @@
 }
 
 /*
- * Uh, this does ugly stuff. It stores the specified value in the a3
- * register. entry.S will swap a3 and the returned value from
- * sys_ptrace() before returning to the user.
- */
-
-static inline void set_success(struct pt_regs *regs,long resval)
-{
-	regs->r19 = resval;
-}
-
-/*
- * This doesn't do diddly, actually--if the value returned from 
- * sys_ptrace() is != 0, it sets things up properly.
- */
-
-static inline void set_failure(struct pt_regs *regs, long errcode)
-{
-	regs->r19 = 0;
-}
-
-/*
  * Set breakpoint.
  */
-static int set_bpt(struct task_struct *child)
+int ptrace_set_bpt(struct task_struct * child)
 {
-	int displ, i, res, reg_b, off, nsaved = 0;
+	int displ, i, res, reg_b, nsaved = 0;
 	u32 insn, op_code;
 	unsigned long pc;
 
-	pc  = get_stack_long(child, map_reg_to_offset[REG_PC]);
+	pc  = get_reg(child, REG_PC);
 	res = read_int(child, pc, &insn);
 	if (res < 0)
 	  return res;
@@ -487,22 +434,12 @@
 		 */
 		displ = ((s32)(insn << 11)) >> 9;
 		child->debugreg[nsaved++] = pc + 4;
-		if (displ)			/* guard against unoptimized code */
+		if (displ)		/* guard against unoptimized code */
 		  child->debugreg[nsaved++] = pc + 4 + displ;
 		DBG(DBG_BPT, ("execing branch\n"));
 	} else if (op_code == 0x1a) {
 		reg_b = (insn >> 16) & 0x1f;
-		off = offset_of_register(reg_b);
-		if (off >= 0) {
-			child->debugreg[nsaved++] = get_stack_long(child, off);
-		} else {
-			/* $31 (aka zero) doesn't have a stack-slot */
-			if (reg_b == 31) {
-				child->debugreg[nsaved++] = 0;
-			} else {
-				return -EIO;
-			}
-		}
+		child->debugreg[nsaved++] = get_reg(child, reg_b);
 		DBG(DBG_BPT, ("execing jump\n"));
 	} else {
 		child->debugreg[nsaved++] = pc + 4;
@@ -524,7 +461,11 @@
 	return 0;
 }
 
-int ptrace_cancel_bpt(struct task_struct *child)
+/*
+ * Ensure no single-step breakpoint is pending.  Returns non-zero
+ * value if child was being single-stepped.
+ */
+int ptrace_cancel_bpt(struct task_struct * child)
 {
 	int i, nsaved = child->debugreg[4];
 
@@ -538,56 +479,43 @@
 	for (i = 0; i < nsaved; ++i) {
 		write_int(child, child->debugreg[i], child->debugreg[i + 2]);
 	}
-	return nsaved;
+	return (nsaved != 0);
 }
 
-asmlinkage long sys_ptrace(long request, long pid, long addr, long data, int a4, int a5,
-			   struct pt_regs regs)
+asmlinkage long sys_ptrace(long request, long pid, long addr, long data,
+			   int a4, int a5, struct pt_regs regs)
 {
 	struct task_struct *child;
 	struct user * dummy;
-	int res;
 
 	dummy = NULL;
 
-	DBG(DBG_MEM, ("request=%ld pid=%ld addr=0x%lx data=0x%lx\n",request,pid,addr,data));
-	set_success(&regs,0);
+	DBG(DBG_MEM, ("request=%ld pid=%ld addr=0x%lx data=0x%lx\n",
+		      request, pid, addr, data));
 	if (request == PTRACE_TRACEME) {
 		/* are we already being traced? */
-		if (current->flags & PF_PTRACED) {
-			set_failure(&regs,-EPERM);
+		if (current->flags & PF_PTRACED)
 			return -EPERM;
-		}
 		/* set the ptrace bit in the process flags. */
 		current->flags |= PF_PTRACED;
 		return 0;
 	}
-	if (pid == 1) {		/* you may not mess with init */
-		set_failure(&regs,-EPERM);
+	if (pid == 1)		/* you may not mess with init */
 		return -EPERM;
-	}
-	if (!(child = get_task(pid))) {
-		set_failure(&regs,-ESRCH);
+	if (!(child = get_task(pid)))
 		return -ESRCH;
-	}
 	if (request == PTRACE_ATTACH) {
-		if (child == current) {
-			set_failure(&regs,-EPERM);
+		if (child == current)
 			return -EPERM;
-		}
 		if ((!child->dumpable ||
 		     (current->uid != child->euid) ||
 		     (current->uid != child->uid) ||
 		     (current->gid != child->egid) ||
-		     (current->gid != child->gid)) && !suser()) {
-			set_failure(&regs,-EPERM);
+		     (current->gid != child->gid)) && !suser())
 			return -EPERM;
-		}
 		/* the same process cannot be attached many times */
-		if (child->flags & PF_PTRACED) {
-			set_failure(&regs,-EPERM);
+		if (child->flags & PF_PTRACED)
 			return -EPERM;
-		}
 		child->flags |= PF_PTRACED;
 		if (child->p_pptr != current) {
 			REMOVE_LINKS(child);
@@ -599,97 +527,54 @@
 	}
 	if (!(child->flags & PF_PTRACED)) {
 		DBG(DBG_MEM, ("child not traced\n"));
-		set_failure(&regs,-ESRCH);
 		return -ESRCH;
 	}
 	if (child->state != TASK_STOPPED) {
 		DBG(DBG_MEM, ("child process not stopped\n"));
-		if (request != PTRACE_KILL) {
-			set_failure(&regs,-ESRCH);
+		if (request != PTRACE_KILL)
 			return -ESRCH;
-		}
 	}
 	if (child->p_pptr != current) {
 		DBG(DBG_MEM, ("child not parent of this process\n"));
-		set_failure(&regs,-ESRCH);
 		return -ESRCH;
 	}
 
 	switch (request) {
 	/* when I and D space are separate, these will need to be fixed. */
-		case PTRACE_PEEKTEXT: /* read word at location addr. */ 
+		case PTRACE_PEEKTEXT: /* read word at location addr. */
 		case PTRACE_PEEKDATA: {
 			unsigned long tmp;
 			int res;
 
-			DBG(DBG_MEM, ("doing request at addr 0x%lx\n",addr));
 			res = read_long(child, addr, &tmp);
-			if (res < 0) {
-				set_failure(&regs,res);
+			DBG(DBG_MEM, ("peek %#lx->%#lx\n", addr, tmp));
+			if (res < 0)
 				return res;
-			} else {
-				set_success(&regs,tmp);
-				return 0;
-			}
+			regs.r0 = tmp;	/* special return */
+			return -255;
 		}
 
-	/* read the word at location addr in the USER area. */
-		case PTRACE_PEEKUSR: {
-		        /* We only allow access to registers. */
-			unsigned long tmp;
+	/* read register number ADDR. */
+		case PTRACE_PEEKUSR:
+			regs.r0 = get_reg(child, addr);
+			DBG(DBG_MEM, ("peek $%ld=%#lx\n", addr, regs.r0));
+			return -255;		/* special return */
 
-			tmp = 0;  /* Default return condition */
-			if (addr == 30) {
-				/* stack pointer */
-				tmp=child->tss.usp;
-			} else {
-#ifdef DEBUG
-				int reg = addr;
-#endif
-				addr = offset_of_register(addr);
-				if (addr < 0) {
-					set_failure(&regs, -EIO);
-					return -EIO;
-				}
-				tmp = get_stack_long(child, addr);
-				DBG(DBG_MEM, ("%d = reg 0x%lx=tmp\n",reg,tmp));
-			}
-			set_success(&regs,tmp);
-			return 0;
-		}
-
-      /* when I and D space are separate, this will have to be fixed. */
+	/* when I and D space are separate, this will have to be fixed. */
 		case PTRACE_POKETEXT: /* write the word at location addr. */
-		case PTRACE_POKEDATA: {
-			long res = write_long(child,addr,data);
-			if (res) {
-				set_failure(&regs,res);
-			}
-			return res;
-		}
+		case PTRACE_POKEDATA:
+			DBG(DBG_MEM, ("poke %#lx<-%#lx\n", addr, data));
+			return write_long(child, addr, data);
 
 		case PTRACE_POKEUSR: /* write the specified register */
-		  {
-			  long res;
-			  addr = offset_of_register(addr);
-			  if(addr < 0) {
-				  set_failure(&regs,-EIO);
-				  return -EIO;
-			  }
-			  res = put_stack_long(child, addr, data);
-			  if (res) {
-				  set_failure(&regs,res);
-			  }
-			  return res;
-		  }
+			DBG(DBG_MEM, ("poke $%ld<-%#lx\n", addr, data));
+			return put_reg(child, addr, data);
 
-		case PTRACE_SYSCALL: /* continue and stop at next 
+		case PTRACE_SYSCALL: /* continue and stop at next
 					(return from) syscall */
 		case PTRACE_CONT: { /* restart after signal. */
-			if ((unsigned long) data > NSIG) {
-				set_failure(&regs,-EIO);
+			if ((unsigned long) data > NSIG)
 				return -EIO;
-			}
 			if (request == PTRACE_SYSCALL)
 				child->flags |= PF_TRACESYS;
 			else
@@ -698,32 +583,28 @@
 			wake_up_process(child);
         /* make sure single-step breakpoint is gone. */
 			ptrace_cancel_bpt(child);
-			set_success(&regs,data);
-			return 0;
+			return data;
 		}
 
 /*
- * 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 
+ * 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: {
-			wake_up_process(child);
-			child->exit_code = SIGKILL;
+			if (child->state != TASK_ZOMBIE) {
+				wake_up_process(child);
+				child->exit_code = SIGKILL;
+			}
         /* make sure single-step breakpoint is gone. */
 			ptrace_cancel_bpt(child);
 			return 0;
 		}
 
-		case PTRACE_SINGLESTEP: {  /* execute signle instruction. */
-			if ((unsigned long) data > NSIG) {
-				set_failure(&regs,-EIO);
+		case PTRACE_SINGLESTEP: {  /* execute single instruction. */
+			if ((unsigned long) data > NSIG)
 				return -EIO;
-			}
-			res = set_bpt(child);
-			if (res < 0) {
-				return res;
-			}
+			child->debugreg[4] = -1;	/* mark single-stepping */
 			child->flags &= ~PF_TRACESYS;
 			wake_up_process(child);
 			child->exit_code = data;
@@ -732,10 +613,8 @@
 		}
 
 		case PTRACE_DETACH: { /* detach a process that was attached. */
-			if ((unsigned long) data > NSIG) {
-				set_failure(&regs,-EIO);
+			if ((unsigned long) data > NSIG)
 				return -EIO;
-			}
 			child->flags &= ~(PF_PTRACED|PF_TRACESYS);
 			wake_up_process(child);
 			child->exit_code = data;
@@ -748,7 +627,6 @@
 		}
 
 		default:
-		  set_failure(&regs,-EIO);
 		  return -EIO;
 	  }
 }

FUNET's LINUX-ADM group, [email protected]
TCL-scripts by Sam Shen, [email protected] with Sam's (original) version
of this