aboutsummaryrefslogtreecommitdiff
path: root/sysdeps
diff options
context:
space:
mode:
authorEdgar E. Iglesias <edgar@axis.com>2012-09-27 17:02:39 +0200
committerPetr Machata <pmachata@redhat.com>2012-09-27 17:46:10 +0200
commitaddae05da54ac16c55133e1c384d74e03346f634 (patch)
treed0866bff584930012c0592f00b6211d142bc70da /sysdeps
parent6ef7b2597cd3f4edf851a7b3516aef7449e554ad (diff)
downloadltrace-addae05da54ac16c55133e1c384d74e03346f634.tar.gz
mipsel: Singlestep over breakpoints
Signed-off-by: Edgar E. Iglesias <edgar@axis.com>
Diffstat (limited to 'sysdeps')
-rw-r--r--sysdeps/linux-gnu/mipsel/arch.h1
-rw-r--r--sysdeps/linux-gnu/mipsel/trace.c164
-rw-r--r--sysdeps/linux-gnu/trace.c2
3 files changed, 166 insertions, 1 deletions
diff --git a/sysdeps/linux-gnu/mipsel/arch.h b/sysdeps/linux-gnu/mipsel/arch.h
index ffb9b34..d46136b 100644
--- a/sysdeps/linux-gnu/mipsel/arch.h
+++ b/sysdeps/linux-gnu/mipsel/arch.h
@@ -42,6 +42,7 @@ struct arch_ltelf_data {
#define ARCH_HAVE_GET_SYMINFO
#define ARCH_HAVE_DYNLINK_DONE
#define ARCH_HAVE_ADD_PLT_ENTRY
+#define ARCH_HAVE_ATOMIC_SINGLESTEP
#define ARCH_HAVE_LIBRARY_SYMBOL_DATA
enum mips_plt_type
diff --git a/sysdeps/linux-gnu/mipsel/trace.c b/sysdeps/linux-gnu/mipsel/trace.c
index c55ff32..a8a680a 100644
--- a/sysdeps/linux-gnu/mipsel/trace.c
+++ b/sysdeps/linux-gnu/mipsel/trace.c
@@ -5,6 +5,7 @@
#include <signal.h>
#include <sys/ptrace.h>
#include <asm/ptrace.h>
+#include <assert.h>
#include "backend.h"
#include "common.h"
@@ -93,6 +94,169 @@ syscall_p(Process *proc, int status, int *sysnum) {
}
return 0;
}
+
+/* Based on GDB code. */
+#define mips32_op(x) (x >> 26)
+#define itype_op(x) (x >> 26)
+#define itype_rs(x) ((x >> 21) & 0x1f)
+#define itype_rt(x) ((x >> 16) & 0x1f)
+#define itype_immediate(x) (x & 0xffff)
+
+#define jtype_op(x) (x >> 26)
+#define jtype_target(x) (x & 0x03ffffff)
+
+#define rtype_op(x) (x >> 26)
+#define rtype_rs(x) ((x >> 21) & 0x1f)
+#define rtype_rt(x) ((x >> 16) & 0x1f)
+#define rtype_rd(x) ((x >> 11) & 0x1f)
+#define rtype_shamt(x) ((x >> 6) & 0x1f)
+#define rtype_funct(x) (x & 0x3f)
+
+static int32_t
+mips32_relative_offset (uint32_t inst)
+{
+ return ((itype_immediate (inst) ^ 0x8000) - 0x8000) << 2;
+}
+
+int mips_next_pcs(struct Process *proc, uint32_t pc, uint32_t *newpc)
+{
+ uint32_t inst, rx;
+ int op;
+ int rn;
+ int nr = 0;
+
+ inst = ptrace(PTRACE_PEEKTEXT, proc->pid, pc, 0);
+
+ if ((inst & 0xe0000000) != 0) {
+ /* Check for branches. */
+ if (itype_op (inst) >> 2 == 5) {
+ /* BEQL, BNEL, BLEZL, BGTZL: bits 0101xx */
+ op = (itype_op (inst) & 0x03);
+ switch (op)
+ {
+ case 0: /* BEQL */
+ case 1: /* BNEL */
+ case 2: /* BLEZL */
+ case 3: /* BGTZL */
+ newpc[nr++] = pc + 8;
+ newpc[nr++] = pc + 4 +
+ mips32_relative_offset (inst);
+ break;
+ default:
+ newpc[nr++] = pc + 4;
+ break;
+ }
+ } else if (itype_op (inst) == 17 && itype_rs (inst) == 8) {
+ /* Step over the branch. */
+ newpc[nr++] = pc + 8;
+ newpc[nr++] = pc + mips32_relative_offset (inst) + 4;
+ } else {
+ newpc[nr++] = pc + 4;
+ }
+ } else {
+ /* Further subdivide into SPECIAL, REGIMM and other. */
+ switch (op = itype_op (inst) & 0x07)
+ {
+ case 0:
+ op = rtype_funct (inst);
+ switch (op)
+ {
+ case 8: /* JR */
+ case 9: /* JALR */
+ rn = rtype_rs (inst);
+
+ rx = ptrace(PTRACE_PEEKUSER,proc->pid, rn, 0);
+ newpc[nr++] = rx;
+ break;
+ default:
+ case 12: /* SYSCALL */
+ newpc[nr++] = pc + 4;
+ break;
+ }
+ break;
+ case 1:
+ op = itype_rt (inst);
+ switch (op)
+ {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 16:
+ case 17:
+ case 18:
+ case 19:
+ newpc[nr++] = pc + 8;
+ newpc[nr++] = pc + 4 +
+ mips32_relative_offset(inst);
+ break;
+ default:
+ newpc[nr++] = pc + 4;
+ break;
+ }
+ break;
+ case 2: /* J */
+ case 3: /* JAL */
+ rx = jtype_target (inst) << 2;
+ /* Upper four bits get never changed... */
+ newpc[nr++] = rx + ((pc + 4) & ~0x0fffffff);
+ break;
+ default:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ /* Step over the branch. */
+ newpc[nr++] = pc + 8;
+ newpc[nr++] = pc + mips32_relative_offset (inst) + 4;
+ break;
+ }
+ }
+ if (nr <= 0 || nr > 2)
+ goto fail;
+ if (nr == 2) {
+ if (newpc[1] == 0)
+ goto fail;
+ }
+ if (newpc[0] == 0)
+ goto fail;
+
+ assert(nr == 1 || nr == 2);
+ return nr;
+
+fail:
+ printf("nr=%d pc=%x\n", nr, pc);
+ printf("pc=%x %x\n", newpc[0], newpc[1]);
+ return 0;
+}
+
+int
+arch_atomic_singlestep(struct Process *proc, struct breakpoint *sbp,
+ int (*add_cb)(void *addr, void *data),
+ void *add_cb_data)
+{
+ uint32_t pc = (uint32_t) get_instruction_pointer(proc);
+ uint32_t newpcs[2];
+ int nr;
+
+ nr = mips_next_pcs(proc, pc, newpcs);
+
+ while (nr-- > 0) {
+ arch_addr_t baddr = (arch_addr_t) newpcs[nr];
+ /* Not sure what to do here. We've already got a bp? */
+ if (dict_find_entry(proc->breakpoints, baddr) != NULL) {
+ fprintf(stderr, "skip %p %p\n", baddr, add_cb_data);
+ continue;
+ }
+
+ if (add_cb(baddr, add_cb_data) < 0)
+ return -1;
+ }
+
+ ptrace(PTRACE_SYSCALL, proc->pid, 0, 0);
+ return 0;
+}
+
/**
\param type Function/syscall call or return.
\param proc The process that had an event.
diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c
index 0829bdb..cdc4062 100644
--- a/sysdeps/linux-gnu/trace.c
+++ b/sysdeps/linux-gnu/trace.c
@@ -930,7 +930,7 @@ continue_after_breakpoint(Process *proc, struct breakpoint *sbp)
if (sbp->enabled == 0) {
continue_process(proc->pid);
} else {
-#if defined __sparc__ || defined __ia64___ || defined __mips__
+#if defined __sparc__ || defined __ia64___
/* we don't want to singlestep here */
continue_process(proc->pid);
#else