patch-2.3.11 linux/arch/arm/kernel/ptrace.c
Next file: linux/arch/arm/kernel/setup.c
Previous file: linux/arch/arm/kernel/process.c
Back to the patch index
Back to the overall index
- Lines: 397
- Date:
Mon Jul 19 09:52:57 1999
- Orig file:
v2.3.10/linux/arch/arm/kernel/ptrace.c
- Orig date:
Thu Jun 17 01:11:35 1999
diff -u --recursive --new-file v2.3.10/linux/arch/arm/kernel/ptrace.c linux/arch/arm/kernel/ptrace.c
@@ -59,209 +59,24 @@
return 0;
}
-/*
- * This routine gets a long from any process space by following the page
- * tables. NOTE! You should check that the long isn't on a page boundary,
- * and that it is in the task area before calling this: this routine does
- * no checking.
- */
-static unsigned long get_long(struct task_struct * tsk,
- struct vm_area_struct * vma, unsigned long addr)
+static int
+read_long(struct task_struct *child, unsigned long addr, unsigned long *res)
{
- pgd_t *pgdir;
- pmd_t *pgmiddle;
- pte_t *pgtable;
- unsigned long page;
-
-repeat:
- pgdir = pgd_offset(vma->vm_mm, addr);
- if (pgd_none(*pgdir)) {
- handle_mm_fault(tsk, vma, addr, 0);
- goto repeat;
- }
- if (pgd_bad(*pgdir)) {
- printk("ptrace: bad page directory %08lx\n", pgd_val(*pgdir));
- pgd_clear(pgdir);
- return 0;
- }
- pgmiddle = pmd_offset(pgdir, addr);
- if (pmd_none(*pgmiddle)) {
- handle_mm_fault(tsk, vma, addr, 0);
- goto repeat;
- }
- if (pmd_bad(*pgmiddle)) {
- printk("ptrace: bad page middle %08lx\n", pmd_val(*pgmiddle));
- pmd_clear(pgmiddle);
- return 0;
- }
- pgtable = pte_offset(pgmiddle, addr);
- if (!pte_present(*pgtable)) {
- handle_mm_fault(tsk, vma, addr, 0);
- goto repeat;
- }
- page = pte_page(*pgtable);
-
- if(MAP_NR(page) >= max_mapnr)
- return 0;
- page += addr & ~PAGE_MASK;
- return *(unsigned long *)page;
-}
+ int copied;
-/*
- * This routine puts a long into any process space by following the page
- * tables. NOTE! You should check that the long isn't on a page boundary,
- * and that it is in the task area before calling this: this routine does
- * no checking.
- *
- * Now keeps R/W state of the page so that a text page stays readonly
- * even if a debugger scribbles breakpoints into it. -M.U-
- */
-static void put_long(struct task_struct * tsk, struct vm_area_struct * vma, unsigned long addr,
- unsigned long data)
-{
- pgd_t *pgdir;
- pmd_t *pgmiddle;
- pte_t *pgtable;
- unsigned long page;
-
-repeat:
- pgdir = pgd_offset(vma->vm_mm, addr);
- if (!pgd_present(*pgdir)) {
- handle_mm_fault(tsk, vma, addr, 1);
- goto repeat;
- }
- if (pgd_bad(*pgdir)) {
- printk("ptrace: bad page directory %08lx\n", pgd_val(*pgdir));
- pgd_clear(pgdir);
- return;
- }
- pgmiddle = pmd_offset(pgdir, addr);
- if (pmd_none(*pgmiddle)) {
- handle_mm_fault(tsk, vma, addr, 1);
- goto repeat;
- }
- if (pmd_bad(*pgmiddle)) {
- printk("ptrace: bad page middle %08lx\n", pmd_val(*pgmiddle));
- pmd_clear(pgmiddle);
- return;
- }
- pgtable = pte_offset(pgmiddle, addr);
- if (!pte_present(*pgtable)) {
- handle_mm_fault(tsk, vma, addr, 1);
- goto repeat;
- }
- page = pte_page(*pgtable);
- if (!pte_write(*pgtable)) {
- handle_mm_fault(tsk, vma, addr, 1);
- goto repeat;
- }
-
- if (MAP_NR(page) < max_mapnr) {
- page += addr & ~PAGE_MASK;
-
- flush_cache_range(vma->vm_mm, addr, addr + sizeof(unsigned long));
-
- *(unsigned long *)page = data;
-
- clean_cache_area(page, sizeof(unsigned long));
+ copied = access_process_vm(child, addr, res, sizeof(*res), 0);
- set_pte(pgtable, pte_mkdirty(mk_pte(page, vma->vm_page_prot)));
- flush_tlb_page(vma, addr & PAGE_MASK);
- }
+ return copied != sizeof(*res) ? -EIO : 0;
}
-/*
- * This routine checks the page boundaries, and that the offset is
- * within the task area. It then calls get_long() to read a long.
- */
-static int read_long(struct task_struct * tsk, unsigned long addr,
- unsigned long * result)
+static int
+write_long(struct task_struct *child, unsigned long addr, unsigned long val)
{
- struct vm_area_struct * vma = find_extend_vma(tsk, addr);
+ int copied;
- if (!vma)
- return -EIO;
- if ((addr & ~PAGE_MASK) > PAGE_SIZE-sizeof(long)) {
- unsigned long low,high;
- struct vm_area_struct * vma_high = vma;
-
- 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(tsk, vma, addr & ~(sizeof(long)-1));
- high = get_long(tsk, vma_high, (addr+sizeof(long)) & ~(sizeof(long)-1));
- switch (addr & (sizeof(long)-1)) {
- case 1:
- low >>= 8;
- low |= high << 24;
- break;
- case 2:
- low >>= 16;
- low |= high << 16;
- break;
- case 3:
- low >>= 24;
- low |= high << 8;
- break;
- }
- *result = low;
- } else
- *result = get_long(tsk, vma, addr);
- return 0;
-}
-
-/*
- * This routine checks the page boundaries, and that the offset is
- * within the task area. It then calls put_long() to write a long.
- */
-static int write_long(struct task_struct * tsk, unsigned long addr,
- unsigned long data)
-{
- struct vm_area_struct * vma = find_extend_vma(tsk, addr);
+ copied = access_process_vm(child, addr, &val, sizeof(val), 1);
- if (!vma)
- return -EIO;
- if ((addr & ~PAGE_MASK) > PAGE_SIZE-sizeof(long)) {
- unsigned long low,high;
- struct vm_area_struct * vma_high = vma;
-
- 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(tsk, vma, addr & ~(sizeof(long)-1));
- high = get_long(tsk, 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 &= 0x000000ff;
- low |= data << 8;
- high &= ~0xff;
- high |= data >> 24;
- break;
- case 2:
- low &= 0x0000ffff;
- low |= data << 16;
- high &= ~0xffff;
- high |= data >> 16;
- break;
- case 3:
- low &= 0x00ffffff;
- low |= data << 24;
- high &= ~0xffffff;
- high |= data >> 8;
- break;
- }
- put_long(tsk, vma, addr & ~(sizeof(long)-1),low);
- put_long(tsk, vma_high, (addr+sizeof(long)) & ~(sizeof(long)-1),high);
- } else
- put_long(tsk, vma, addr, data);
- return 0;
+ return copied != sizeof(val) ? -EIO : 0;
}
/*
@@ -350,19 +165,12 @@
return val;
}
-int ptrace_set_bpt (struct task_struct *child)
+static unsigned long
+get_branch_address(struct task_struct *child, unsigned long pc, unsigned long insn)
{
- unsigned long insn, pc, alt;
- int i, nsaved = 0, res;
-
- pc = pc_pointer (get_stack_long (child, 15/*REG_PC*/));
-
- res = read_long (child, pc, &insn);
- if (res < 0)
- return res;
+ unsigned long alt = 0;
- child->tss.debug[nsaved++] = alt = pc + 4;
-printk ("ptrace_set_bpt: insn=%08lX pc=%08lX ", insn, pc);
+printk(KERN_DEBUG "ptrace_set_bpt: insn=%08lX pc=%08lX ", insn, pc);
switch (insn & 0x0e100000) {
case 0x00000000:
case 0x00100000:
@@ -423,7 +231,7 @@
alt -= ptrace_getldrop2 (child, insn);
}
if (read_long (child, alt, &alt) < 0)
- alt = pc + 4; /* not valid */
+ alt = 0; /* not valid */
else
alt = pc_pointer (alt);
}
@@ -440,7 +248,7 @@
alt -= insn & 0xfff;
}
if (read_long (child, alt, &alt) < 0)
- alt = pc + 4; /* not valid */
+ alt = 0; /* not valid */
else
alt = pc_pointer (alt);
}
@@ -473,7 +281,7 @@
base = ptrace_getrn (child, insn);
if (read_long (child, base + nr_regs, &alt) < 0)
- alt = pc + 4; /* not valid */
+ alt = 0; /* not valid */
else
alt = pc_pointer (alt);
break;
@@ -499,22 +307,55 @@
break;
}
printk ("=%08lX\n", alt);
- if (alt != pc + 4)
- child->tss.debug[nsaved++] = alt;
- for (i = 0; i < nsaved; i++) {
- res = read_long (child, child->tss.debug[i], &insn);
- if (res >= 0) {
- child->tss.debug[i + 2] = insn;
- res = write_long (child, child->tss.debug[i], BREAKINST);
+ return alt;
+}
+
+static int
+add_breakpoint(struct task_struct *child, struct debug_info *dbg, unsigned long addr)
+{
+ int nr = dbg->nsaved;
+ int res = -EINVAL;
+
+ if (nr < 2) {
+ res = read_long(child, addr, &dbg->bp[nr].insn);
+ if (res == 0)
+ res = write_long(child, addr, BREAKINST);
+
+ if (res == 0) {
+ dbg->bp[nr].address = addr;
+ dbg->nsaved += 1;
}
- if (res < 0) {
- child->tss.debug[4] = 0;
- return res;
+ } else
+ printk(KERN_DEBUG "add_breakpoint: too many breakpoints\n");
+
+ return res;
+}
+
+int ptrace_set_bpt (struct task_struct *child)
+{
+ struct debug_info *dbg = &child->tss.debug;
+ unsigned long insn, pc, alt;
+ int res;
+
+ pc = pc_pointer (get_stack_long (child, 15/*REG_PC*/));
+
+ res = read_long(child, pc, &insn);
+ if (res >= 0) {
+ res = 0;
+
+ dbg->nsaved = 0;
+
+ res = add_breakpoint(child, dbg, pc + 4);
+
+ if (res == 0) {
+ alt = get_branch_address(child, pc, insn);
+ if (alt)
+ res = add_breakpoint(child, dbg, alt);
}
}
- child->tss.debug[4] = nsaved;
- return 0;
+
+ return res;
}
/* Ensure no single-step breakpoint is pending. Returns non-zero
@@ -522,16 +363,24 @@
*/
int ptrace_cancel_bpt (struct task_struct *child)
{
- int i, nsaved = child->tss.debug[4];
+ struct debug_info *dbg = &child->tss.debug;
+ unsigned long tmp;
+ int i, nsaved = dbg->nsaved;
- child->tss.debug[4] = 0;
+ dbg->nsaved = 0;
if (nsaved > 2) {
printk ("ptrace_cancel_bpt: bogus nsaved: %d!\n", nsaved);
nsaved = 2;
}
- for (i = 0; i < nsaved; i++)
- write_long (child, child->tss.debug[i], child->tss.debug[i + 2]);
+
+ for (i = 0; i < nsaved; i++) {
+ read_long(child, dbg->bp[i].address, &tmp);
+ if (tmp != BREAKINST)
+ printk(KERN_ERR "ptrace_cancel_bpt: weirdness\n");
+ write_long(child, dbg->bp[i].address, dbg->bp[i].insn);
+ }
+
return nsaved != 0;
}
@@ -598,8 +447,8 @@
unsigned long tmp;
ret = read_long(child, addr, &tmp);
- if (ret >= 0)
- ret = put_user(tmp, (unsigned long *)data);
+ if (ret)
+ put_user(tmp, (unsigned long *) data);
goto out;
}
@@ -619,7 +468,7 @@
case PTRACE_POKETEXT: /* write the word at location addr. */
case PTRACE_POKEDATA:
- ret = write_long(child,addr,data);
+ ret = write_long(child, addr, data);
goto out;
case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
@@ -665,7 +514,7 @@
ret = -EIO;
if ((unsigned long) data > _NSIG)
goto out;
- child->tss.debug[4] = -1;
+ child->tss.debug.nsaved = -1;
child->flags &= ~PF_TRACESYS;
wake_up_process(child);
child->exit_code = data;
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)