diff options
author | Edgar E. Iglesias <edgar@axis.com> | 2012-09-27 17:02:39 +0200 |
---|---|---|
committer | Petr Machata <pmachata@redhat.com> | 2012-09-27 17:46:10 +0200 |
commit | addae05da54ac16c55133e1c384d74e03346f634 (patch) | |
tree | d0866bff584930012c0592f00b6211d142bc70da /sysdeps | |
parent | 6ef7b2597cd3f4edf851a7b3516aef7449e554ad (diff) | |
download | ltrace-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.h | 1 | ||||
-rw-r--r-- | sysdeps/linux-gnu/mipsel/trace.c | 164 | ||||
-rw-r--r-- | sysdeps/linux-gnu/trace.c | 2 |
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 |