| /* |
| * This file is part of ltrace. |
| * Copyright (C) 2013 Petr Machata, Red Hat Inc. |
| * Copyright (C) 2012 Edgar E. Iglesias, Axis Communications |
| * Copyright (C) 2010 Arnaud Patard, Mandriva SA |
| * Copyright (C) 2008,2009 Juan Cespedes |
| * Copyright (C) 2006 Eric Vaitl, Cisco Systems, Inc. |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License as |
| * published by the Free Software Foundation; either version 2 of the |
| * License, or (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, but |
| * WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA |
| * 02110-1301 USA |
| */ |
| |
| #include "config.h" |
| |
| #include <sys/types.h> |
| #include <sys/wait.h> |
| #include <signal.h> |
| #include <sys/ptrace.h> |
| #include <asm/ptrace.h> |
| #include <assert.h> |
| |
| #include "backend.h" |
| #include "common.h" |
| #include "debug.h" |
| #include "mips.h" |
| #include "proc.h" |
| #include "type.h" |
| |
| #if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) |
| # define PTRACE_PEEKUSER PTRACE_PEEKUSR |
| #endif |
| |
| #if (!defined(PTRACE_POKEUSER) && defined(PTRACE_POKEUSR)) |
| # define PTRACE_POKEUSER PTRACE_POKEUSR |
| #endif |
| |
| |
| /** |
| \addtogroup mips Mips specific functions. |
| |
| These are the functions that it looks like I need to implement in |
| order to get ltrace to work on our target. |
| |
| @{ |
| */ |
| |
| /** |
| \param proc The process that had an event. |
| |
| Called by \c next_event() right after the return from wait. |
| |
| Most targets just return here. A couple use proc->arch_ptr for a |
| private data area. |
| */ |
| void |
| get_arch_dep(struct process *proc) |
| { |
| } |
| |
| /** |
| \param proc Process that had event. |
| \param status From \c wait() |
| \param sysnum 0-based syscall number. |
| \return 1 if syscall, 2 if sysret, 0 otherwise. |
| |
| Called by \c next_event() after the call to get_arch_dep() |
| |
| It seems that the ptrace call trips twice on a system call, once |
| just before the system call and once when it returns. Both times, |
| the pc points at the instruction just after the mips "syscall" |
| instruction. |
| |
| There are several possiblities for system call sets, each is offset |
| by a base from the others. On our system, it looks like the base |
| for the system calls is 4000. |
| */ |
| int |
| syscall_p(struct process *proc, int status, int *sysnum) |
| { |
| if (WIFSTOPPED(status) |
| && WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) { |
| /* get the user's pc (plus 8) */ |
| long pc = (long)get_instruction_pointer(proc); |
| /* fetch the SWI instruction */ |
| int insn = ptrace(PTRACE_PEEKTEXT, proc->pid, pc - 4, 0); |
| int num = ptrace(PTRACE_PEEKTEXT, proc->pid, pc - 8, 0); |
| |
| /* |
| On a mips, syscall looks like: |
| 24040fa1 li v0, 0x0fa1 # 4001 --> _exit syscall |
| 0000000c syscall |
| */ |
| if(insn!=0x0000000c){ |
| return 0; |
| } |
| |
| *sysnum = (num & 0xFFFF) - 4000; |
| /* if it is a syscall, return 1 or 2 */ |
| if (proc->callstack_depth > 0 && |
| proc->callstack[proc->callstack_depth - 1].is_syscall && |
| proc->callstack[proc->callstack_depth - 1].c_un.syscall == *sysnum) { |
| return 2; |
| } |
| |
| if (*sysnum >= 0) { |
| return 1; |
| } |
| } |
| 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; |
| case 4: /* BEQ */ |
| if (itype_rs(inst) == itype_rt(inst)) { |
| /* Compare the same reg for equality, always |
| * follow the branch. */ |
| newpc[nr++] = pc + 4 + |
| mips32_relative_offset(inst); |
| break; |
| } |
| /* Fall through. */ |
| default: |
| 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; |
| } |
| |
| enum sw_singlestep_status |
| arch_sw_singlestep(struct process *proc, struct breakpoint *bp, |
| int (*add_cb)(arch_addr_t, struct sw_singlestep_data *), |
| struct sw_singlestep_data *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_HAS_KEY(proc->leader->breakpoints, &baddr)) { |
| fprintf(stderr, "skip %p %p\n", baddr, add_cb_data); |
| continue; |
| } |
| |
| if (add_cb(baddr, add_cb_data) < 0) |
| return SWS_FAIL; |
| } |
| |
| ptrace(PTRACE_SYSCALL, proc->pid, 0, 0); |
| return SWS_OK; |
| } |
| |
| /** |
| \param type Function/syscall call or return. |
| \param proc The process that had an event. |
| \param arg_num -1 for return value, |
| \return The argument to fetch. |
| |
| A couple of assumptions. |
| |
| - Type is LT_TOF_FUNCTIONR or LT_TOF_SYSCALLR if arg_num==-1. These |
| types are only used in calls for output_right(), which only uses -1 |
| for arg_num. |
| - Type is LT_TOF_FUNCTION or LT_TOF_SYSCALL for args 0...4. |
| - I'm only displaying the first 4 args (Registers a0..a3). Good |
| enough for now. |
| |
| Mips conventions seem to be: |
| - syscall parameters: r4...r9 |
| - syscall return: if(!a3){ return v0;} else{ errno=v0;return -1;} |
| - function call: r4..r7. Not sure how to get arg number 5. |
| - function return: v0 |
| |
| The argument registers are wiped by a call, so it is a mistake to ask |
| for arguments on a return. If ltrace does this, we will need to cache |
| arguments somewhere on the call. |
| |
| I'm not doing any floating point support here. |
| |
| */ |
| long |
| gimme_arg(enum tof type, struct process *proc, int arg_num, |
| struct arg_type_info *info) |
| { |
| long ret; |
| long addr; |
| debug(2,"type %d arg %d",type,arg_num); |
| if (arg_num == -1) { |
| if(type == LT_TOF_FUNCTIONR) { |
| return ptrace(PTRACE_PEEKUSER,proc->pid,off_v0,0); |
| } |
| if (type == LT_TOF_SYSCALLR) { |
| unsigned a3=ptrace(PTRACE_PEEKUSER, proc->pid,off_a3,0); |
| unsigned v0=ptrace(PTRACE_PEEKUSER, proc->pid,off_v0,0); |
| if(!a3){ |
| return v0; |
| } |
| return -1; |
| } |
| } |
| if (type == LT_TOF_FUNCTION || type == LT_TOF_SYSCALL) { |
| /* o32: float args are in f12 and f14 */ |
| if ((info->type == ARGTYPE_FLOAT) && (arg_num < 2)) { |
| ret=ptrace(PTRACE_PEEKUSER,proc->pid,off_fpr0+12+arg_num*2,0); |
| debug(2,"ret = %#lx",ret); |
| return ret; |
| } |
| if(arg_num <4){ |
| ret=ptrace(PTRACE_PEEKUSER,proc->pid,off_a0+arg_num,0); |
| debug(2,"ret = %#lx",ret); |
| return ret; |
| } else { |
| /* not sure it's going to work for something else than syscall */ |
| addr=ptrace(PTRACE_PEEKUSER,proc->pid,off_sp,0); |
| if (addr == -1) { |
| debug(2,"ret = %#lx",addr); |
| return addr; |
| } |
| ret = addr + 4*arg_num; |
| ret=ptrace(PTRACE_PEEKTEXT,proc->pid,addr,0); |
| debug(2,"ret = %#lx",ret); |
| return ret; |
| } |
| } |
| if (type == LT_TOF_FUNCTIONR || type == LT_TOF_SYSCALLR){ |
| addr=ptrace(PTRACE_PEEKUSER,proc->pid,off_sp,0); |
| if (addr == -1) { |
| debug(2,"ret = %#lx",addr); |
| return addr; |
| } |
| ret = addr + 4*arg_num; |
| ret=ptrace(PTRACE_PEEKTEXT,proc->pid,addr,0); |
| debug(2,"ret = %#lx",ret); |
| return ret; |
| } |
| fprintf(stderr, "gimme_arg called with wrong arguments\n"); |
| return 0; |
| } |
| |
| /**@}*/ |