Logo Search packages:      
Sourcecode: lcrash version File versions  Download package

trace_arm.c

/*
 * This file is part of lcrash, an analysis tool for Linux memory dumps.
 * 
 * Ported from trace_i386.c 
 * by Fleming Feng (fleming.feng@intel.com).
 * 
 * Copyright 1999 Silicon Graphics, Inc. All rights reserved.
 * Copyright (C) 2003, Intel Corp. All rights reserved.
 * 
 * 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. See the file COPYING for more
 * information.
 */

/* Part of the code is copied from file tdep-arm.c of GDB. This includes 
 * function arm_scan_prologue. The license info for it is like the
 * following */

/* Common target dependent code for GDB on ARM systems.
   Copyright 1988, 1989, 1991, 1992, 1993, 1995, 1996, 1998, 1999, 2000,
   2001, 2002 Free Software Foundation, Inc.

   This file is part of GDB.

   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., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.  */

#include <lcrash.h>
#include <dis-asm.h>
#include <strings.h>
#include <lc_dis_arm.h>

#define CONST_FALSE     0
#define CONST_TRUE      1
#define SOURCE_OPERAND  2
#define DEST_OPERAND    4
#define DEST_REGLIST    8
#define DEST_ALT_OP     16
#define I_BIT_VARIANT   (1<<31)

#define BIT_25          0x02000000
#define BIT_4_7         0x00000090

/* The following constants is defined according to
 * kernel struct context_save_struct
 */
#define SP_INDEX_2_5          8
#define PC_INDEX_2_5          9

#ifdef CONFIG_CPU_XSCALE
#define PC_INDEX_2_4          11
#define CONTEXT_SIZE_2_4      12
#else
#define PC_INDEX_2_4          9
#define CONTEXT_SIZE_2_4      10
#endif
#define PC_INDEX_2_2          8
#define CONTEXT_SIZE_2_2      9

#define FP_SIZE         35

/* for 2.5 kernel */
struct kl_thread_info{
      unsigned long flags;
      unsigned long preempt_count;
      unsigned long addr_limit;
      void*       task;
      void*       exec_domain;
      unsigned long cpu;
      unsigned long cpu_domain;
      unsigned long cpu_context_save[12]; /* We only interest this field */
      void*       restart_fn;
      unsigned long restart_arg0;
      unsigned long restart_arg1;
      unsigned long restart_arg2;
      unsigned int  fp_state_save[FP_SIZE];     
};

/* for 2.4 kernel */
struct kl_thread_2_4{
      unsigned long refcount;
      unsigned long address;
      unsigned long trap_no;
      unsigned long error_code;
      unsigned long fp_state_save[FP_SIZE];
      int         dbg_info_nsaved;
      unsigned long dbg_info_addr0;
      unsigned long dbg_info_inst0;
      unsigned long dbg_info_addr1;
      unsigned long dbg_info_inst1;
      void*       saved_context;
      unsigned int  domain;   
};

/* for 2.2 kernel */
struct kl_thread_2_2{
      unsigned long address;
      unsigned long trap_no;
      unsigned long error_code;
      unsigned long fp_state_save[FP_SIZE];
      unsigned long debug[5];
      void*       saved_context;
      unsigned long memmap;
};

struct instr_info{
      unsigned long mask;
      unsigned long value;
      int flag;
};

struct instr_info branch_instr[ ] = {
      {0x0c500000, 0x04100000, DEST_OPERAND}, /* LDR, bit 12-15 */
      {0x0de00000, 0x01a00000, 
       DEST_OPERAND | I_BIT_VARIANT},     /* MOV, bit 12-15 */
      {0x0de00000, 0x01e00000, 
       DEST_OPERAND | I_BIT_VARIANT},     /* MVN, bit 12-15 */
      {0x0de00000, 0x01800000, 
       DEST_OPERAND | I_BIT_VARIANT},     /* ORR, bit 12-15 */
      {0x0de00000, 0x00600000, 
       DEST_OPERAND | I_BIT_VARIANT},           /* RSB, bit 12-15 */
      {0x0de00000, 0x00e00000, 
       DEST_OPERAND | I_BIT_VARIANT},           /* RSC, bit 12-15 */
      {0x0de00000, 0x00c00000, 
       DEST_OPERAND | I_BIT_VARIANT},     /* SBC, bit 12-15 */
      {0x0de00000, 0x00400000, 
       DEST_OPERAND | I_BIT_VARIANT},           /* SUB, bit 12-15 */
      {0x0de00000, 0x00a00000, 
       DEST_OPERAND | I_BIT_VARIANT},     /* ADC, bit 12-15 */
      {0x0de00000, 0x00800000, 
       DEST_OPERAND | I_BIT_VARIANT},     /* ADD, bit 12-15 */
      {0x0de00000, 0x00000000, 
       DEST_OPERAND | I_BIT_VARIANT},     /* AND, bit 12-15 */
      {0x0de00000, 0x01c00000, 
       DEST_OPERAND | I_BIT_VARIANT},     /* BIC, bit 12-15 */
      {0x0de00000, 0x00200000, 
       DEST_OPERAND | I_BIT_VARIANT},           /* EOR, bit 12-15 */
      {0x0e000000, 0x0a000000, CONST_TRUE},     /* B, BL */
      {0x0e500000, 0x08100000, DEST_REGLIST}, /* LDM(1), bit 0-15 */
      {0x0e508000, 0x08508000, CONST_TRUE},     /* LDM(3), pc always */
      {0x0f000000, 0x0f000000, CONST_TRUE},     /* SWI */
      {0x0fe000f0, 0x00200090, DEST_ALT_OP},    /* MLA, bit 19-16 */
      {0x0fe000f0, 0x00000090, DEST_ALT_OP},    /* MUL, bit 19-16 */
      {0x0fe000f0, 0x00e00090, 
       DEST_OPERAND | DEST_ALT_OP},             /* SMLAL, bit 19-12 */
      {0x0fe000f0, 0x00c00090,
       DEST_OPERAND | DEST_ALT_OP},       /* SMULL, bit 19-12 */
      {0x0fe000f0, 0x00a00090,
       DEST_OPERAND | DEST_ALT_OP},       /* UMLAL bit 19-12 */
      {0x0fe000f0, 0x00800090,
       DEST_OPERAND | DEST_ALT_OP},       /* UMULL bit 19-12 */
      {0x0ff000d0, 0x01200010, CONST_TRUE},     /* BLX(2), BX */
      {0x0ff000f0, 0x01000090, DEST_OPERAND}, /* SWP bit 12-15 */
      {0x0ff000f0, 0x01400090, DEST_OPERAND}, /* SWPB bit 12-15 */
      {0xfe000000, 0xfa000000, CONST_TRUE},     /* BLX(1) */
      {0xfff000f0, 0xe1200070, CONST_TRUE}      /* BKPT */
};

#define BRANCH_INSTR_SIZE (sizeof(branch_instr)/(sizeof(struct instr_info)))

int is_pc_dest(unsigned long opcode, int flag)
{

      int ret = 0;

      if (flag == CONST_TRUE)
            ret = 1;
      else if (flag == DEST_REGLIST) {
            if (opcode & 0x8000) {
                  /* pc in reglist is set */
                  ret = 1;
            }
      }
      else{
            if ((flag & DEST_OPERAND) == DEST_OPERAND) {
                  if ((opcode & 0xf000) == 0xf000) {
                        /* rd = 0xf(15) */
                        ret = 1;
                  }
            }
            if ((flag & DEST_ALT_OP) == DEST_ALT_OP) {
                  if ((opcode & 0xf0000) == 0xf0000) {
                        /* rd = 0xf(15) */
                        ret = 1;
                  }
            }
      }

      return ret;
      
}

/* If the instruction causes a branch then returns 1, otherwise 0
 */
int is_branch(unsigned long opcode)
{

      int i;

      for(i = 0; i < BRANCH_INSTR_SIZE; i ++){
            if ((opcode & branch_instr[i].mask) == 
                 branch_instr[i].value) {
                  if ((branch_instr[i].flag & I_BIT_VARIANT) != 0) {
                        if ((opcode & BIT_25) == 0 && 
                           (opcode & BIT_4_7) == BIT_4_7) {
                              /* not the assumed instruction */
                              continue;
                        }
                  }
                  return (is_pc_dest(opcode, branch_instr[i].flag));
            }
      }

      return 0;

}

/*
 * get_call_pc_arm()
 */
kaddr_t
get_call_pc_arm(kaddr_t ra)
{
      kaddr_t addr = 0;
      instr_rec_arm_t *irp;

      if (!(irp = get_instr_stream_arm(ra, 1, 0))) {
            return((kaddr_t)NULL);
      }

      /* Because get_instr_stream_arm returns the first instr info,
       * that is exactly the irp points to!
       */
      irp->opcode = KL_GET_UINT32(&(irp->opcode));
      if (is_branch(irp->opcode)) {
            addr = irp->addr;
      }
      free_instr_stream_arm(irp);
      return(addr);
}

/*
 * get_jmp_instr()
 */
int
get_jmp_instr(
kaddr_t addr, 
kaddr_t isp, 
kaddr_t *caddr, 
char *fname, 
char **cfname)
{
      instr_rec_arm_t *irp;

      if (!(irp = get_instr_stream_arm(addr, 1, 0))) {
            return(1);
      }
      if (!irp->prev) {
            free_instr_stream_arm(irp);
            return(1);
      }
      irp = irp->prev;
      irp->opcode = KL_GET_UINT32(&(irp->opcode));
      if (is_branch(irp->opcode) == 0) {
            free_instr_stream_arm(irp);
            return(1);
      }

      /* Check for the easiest case first...
       */
      free_instr_stream_arm(irp);
      return(0);
}

#define FRMSIZE_DBG 1
#define FRMSIZE2_DBG 1

/* The following function mostly is copied from the same function
 * from gdb's arm-tdep.c, just to make a little bit customize to 
 * make sure it can be embedded into lkcd. The funcion assumes
 * the global label before pc is a function entry point, 
 * and there is a function prologue starting from that address.
 * The code based on the above preassumption and stack moves once
 * condition with or without frame pointer. It supports APCS. For 
 * stack trace with none APCS support, it has not been tested and the
 * result can not be predicted at current stage.
 * Input:  pc = program counter to start stack  unwind from.
 *       fi = to hold the stack unwind result.
 * Return: stack frame size, start from the function entry to
 *         pc. if return -1, then stack unwind failed.
 */

/* This function decodes an ARM function prologue to determine:
 *  1) the size of the stack frame
 *  2) which registers are saved on it
 *  3) the offsets of saved regs
 *  4) the offset from the stack pointer to the frame pointer
 *  This information is stored in the "extra" fields of the frame_info.
 *
 *  There are two basic forms for the ARM prologue.  The fixed argument
 *  function call will look like:
 *
 *  mov    ip, sp
 *  stmfd  sp!, {fp, ip, lr, pc}
 *  sub    fp, ip, #4
 *  [sub sp, sp, #4]
 *
 *  Which would create this stack frame (offsets relative to FP):
 *  IP ->   4       (caller's stack)
 *  FP ->   0    PC (points to address of stmfd instruction + 8 in callee)
 *        -4   LR (return address in caller)
 *        -8   IP (copy of caller's SP)
 *        -12  FP (caller's FP)
 *  SP ->   -28     Local variables
 *
 *  The frame size would thus be 32 bytes, and the frame offset would be
 *  28 bytes.  The stmfd call can also save any of the vN registers it
 *  plans to use, which increases the frame size accordingly.
 *
 *  Note: The stored PC is 8 off of the STMFD instruction that stored it
 *  because the ARM Store instructions always store PC + 8 when you read
 *  the PC register.
 *
 *  A variable argument function call will look like:
 *
 *  mov    ip, sp
 *  stmfd  sp!, {a1, a2, a3, a4}
 *  stmfd  sp!, {fp, ip, lr, pc}
 *  sub    fp, ip, #20
 *
 *  Which would create this stack frame (offsets relative to FP):
 *  IP ->  20     (caller's stack)
 *       16  A4
 *       12  A3
 *       8   A2
 *       4   A1
 *  FP ->  0   PC (points to address of stmfd instruction + 8 in callee)
 *       -4  LR (return address in caller)
 *       -8  IP (copy of caller's SP)
 *       -12 FP (caller's FP)
 *  SP ->  -28    Local variables
 *
 *  The frame size would thus be 48 bytes, and the frame offset would be
 *  28 bytes.
 *
 *  There is another potential complication, which is that the optimizer
 *  will try to separate the store of fp in the "stmfd" instruction from
 *  the "sub fp, ip, #NN" instruction.  Almost anything can be there, so
 *  we just key on the stmfd, and then scan for the "sub fp, ip, #NN"...
 *
 *  Also, note, the original version of the ARM toolchain claimed that there
 *  should be an
 *
 *  instruction at the end of the prologue.  I have never seen GCC produce
 *  this, and the ARM docs don't mention it.  We still test for it below in
 *  case it happens...
 *
 */
int arm_scan_prologue(kaddr_t pc, struct frame_info* fi)
{

      syment_t *sp = NULL;
      kaddr_t entry_pc;
      kaddr_t prologue_start, prologue_end;
      kaddr_t current_pc;
      int sp_offset = 0;
      int fp_offset = 0;
      instr_rec_arm_t irp;
      int size, regno;

      if (fi == NULL) return -1;
      fi->framereg = ARM_SP_REGNUM;

      if ((sp = kl_lkup_symaddr(pc)) == NULL) {
            KL_ERROR = KLE_NO_SYMBOLS;
            return KL_ERROR;
      }
      entry_pc = sp->s_addr;
      prologue_start = entry_pc;
      prologue_end = entry_pc + 64;
      
      /* The pc maybe inside a prologue for an exception frame,
       * or pc is below entry_pc + 64 in this case, we only 
       * need to scan until pc. 
       */
      prologue_end = pc < prologue_end ? pc : prologue_end;
      
      if ((size = get_instr_info_arm(prologue_start, &irp)) != irp.size) {
            KL_ERROR = KLE_INVALID_VALUE;
            return KL_ERROR;
      }
      
      irp.opcode = KL_GET_UINT32(&(irp.opcode));

      if (irp.opcode == 0xe1a0c00d)       /* mov ip, sp */
            current_pc = prologue_start + 4;
      else
            current_pc = prologue_start;

      for(; current_pc < prologue_end; current_pc += 4){
            if ((size = get_instr_info_arm(current_pc, &irp)) != 
                irp.size) {
                  KL_ERROR = KLE_INVALID_VALUE;
                  return KL_ERROR;
            }
            irp.opcode = KL_GET_UINT32(&(irp.opcode));
            if ((irp.opcode & 0xffff0000) == 0xe92d0000) {
                  /* stmfd sp!, {..., fp, ip, lr, pc) or
                   * stmfd sp!, {a1, a2, a3, a4} 
                   */
                  int mask = irp.opcode & 0xffff;
                  for( regno = ARM_PC_REGNUM; regno >= 0; regno--){
                        if (mask & (1 << regno)) {
                              sp_offset -= 4;
                              fi->saved_regs[regno] = sp_offset;
                        }
                  }
            }
            else if ((irp.opcode & 0xfffff000) == 0xe24cb000) {
                  /* sub fp, ip #n */
                  unsigned imm = irp.opcode & 0xff; /* immediate value */
                  unsigned rot = (irp.opcode & 0xf00) >> 7; /* rotate amount */
                  imm = (imm >> rot) | (imm << (32 - rot));
                  fp_offset = -imm;
                  fi->framereg = ARM_FP_REGNUM;
            }
            else if ((irp.opcode & 0xfffff000) == 0xe24dd000) {
                  /* sub sp, sp #n */
                  unsigned imm = irp.opcode & 0xff; /* immediate value */
                  unsigned rot = (irp.opcode & 0xf00) >> 7; /* rotate amound */
                  imm = (imm >> rot) | (imm << (32 - rot));
                  sp_offset -= imm;
            }
            else if ((irp.opcode & 0xffff7fff) == 0xed6d0103) {
                  /* sfte f?, [sp, -#c]! */
                  sp_offset -= 12;
                  regno = ARM_F0_REGNUM + ((irp.opcode >> 12) & 0x07);
                  fi->saved_regs[regno] = sp_offset;
            }
            else if ((irp.opcode & 0xffbf0fff) == 0xec2d0200) {
                  /* stmfd f0, 4, [sp]! */
                  int n_saved_fp_regs;
                  unsigned int fp_start_reg, fp_bound_reg;
                  if ((irp.opcode & 0x800) == 0x800) {
                        /* N0 is set */
                        if ((irp.opcode & 0x40000) == 0x40000) { 
                              /* N1 is set */
                              n_saved_fp_regs = 3;
                        }
                        else
                              n_saved_fp_regs = 1;
                  }
                  else{
                        if ((irp.opcode & 0x40000) == 0x40000) {
                              /* N1 is set */
                              n_saved_fp_regs = 2;
                        }
                        else
                              n_saved_fp_regs = 4;
                  }
                  fp_start_reg = ARM_F0_REGNUM + ((irp.opcode >> 12) & 0x7);
                  fp_bound_reg = fp_start_reg + n_saved_fp_regs;
                  for(; fp_start_reg < fp_bound_reg; fp_start_reg ++){
                        sp_offset -=12;
                        fi->saved_regs[fp_start_reg++] = sp_offset;
                  }
            }
            else if ((irp.opcode & 0xf0000000) != 0xe0000000)
                  break; /* Condition not true, exit early */
            else if ((irp.opcode & 0xfe200000) == 0xe8200000) 
                  /* ldm? */
                  break; /* Currently, do not scan pass a block load,
                        * but in fact, this is subjeted to be
                        * changed in the future, since if the sp
                        * is the destination reg, every thing changed.
                        * But if the memory is loaded into sp, while after
                        * that the memory is changed, we have no means
                        * to determine the sp register thus where the
                        * stack locates.
                        * By Fleming Feng
                        */
            else /* The optimizer might shove anything into the prologue,
                  * we just skip what we don't recognize or what we don't 
                  * want to.
                  */
                  continue;
      }

      fi->framesize = -sp_offset;
      if (fi->framereg == ARM_FP_REGNUM)
            fi->frameoffset = fp_offset - sp_offset;
      else
            fi->frameoffset = 0;

      return (fi->framesize);

}

/*
 * print_pc_arm()
 */
void
print_pc_arm(kaddr_t addr, FILE *ofp)
{
      int offset = 0;
      syment_t *sp;

      if ((sp = kl_lkup_symaddr(addr))) {
            offset = addr - sp->s_addr;
      }

      /* Print out address
       */
      fprintf(ofp, "0x%x", (unsigned int)addr);

      /* Print out symbol name
       */
      if (sp) {
            if (offset) {
                  fprintf(ofp, " <%s+%d>", sp->s_name, offset);
            } else {
                  fprintf(ofp, " <%s>", sp->s_name);
            }
      }
}

/* 
 * setup_trace_rec_arm()
 */
int
setup_trace_rec_arm(kaddr_t saddr, kaddr_t task, int flag, trace_t *trace)
{
      int aflag = K_TEMP;
      kl_reset_error();

      if (flag & C_PERM) {
            aflag = K_PERM;
      }
      if (task) {
            trace->tsp = kl_alloc_block(TASK_STRUCT_SZ, aflag);
            if (kl_get_task_struct(task, 2, trace->tsp)) {
                  kl_free_block(trace->tsp);
                  trace->tsp = NULL;
                  return(1);
            }
      }
      trace->stack[0].type = S_KERNELSTACK;
      trace->stack[0].size = STACK_SIZE;

      /* Get the base address of the stack
       */
      trace->stack[0].addr = saddr - trace->stack[0].size;
      trace->stack[0].ptr = kl_alloc_block(STACK_SIZE, aflag);
      if (KL_ERROR) {
            clean_trace_rec(trace);
            return(1);
      }
      GET_BLOCK(trace->stack[0].addr, STACK_SIZE, trace->stack[0].ptr);
      if (KL_ERROR) {
            clean_trace_rec(trace);
            return(1);
      }
      return(0);
}

/*
 * valid_ra_arm()
 */
int
valid_ra_arm(kaddr_t ra)
{
      kaddr_t pc;

      if ((ra < KL_PAGE_OFFSET) || !kl_funcaddr(ra)) {
            if (ra < KL_HIGH_MEMORY) {
                  /* Fixme! At this stage, if the module symbol is not
                   * correctly generated in map.n file, maybe even
                   * ra fails the check kl_funcaddr() when 
                   * ra >= KL_HIGH_MEMORY, it can still be a function
                   * from kernel module, in this situation, do not
                   * return.
                   * By Fleming
                   */
                  return(0);
            }
      }
      if ((pc = get_call_pc_arm(ra))) {
            return(1);
      }
      return(0);
}

#define KERNEL_EFRAME         0
#define USER_EFRAME           1
#define KERNEL_EFRAME_SZ      13    /* no ss and esp */
#define USER_EFRAME_SZ        15

int is_trace_endpoint(char* func_name)
{

      if (strstr(func_name, "__sys_trace") ||
          strstr(func_name, "ret_fast_syscall") ||
          strstr(func_name, "asm_do_IRQ") ||
          strstr(func_name, "__sys_trace_return") ||
          strstr(func_name, "__irq_svc") ||
          strstr(func_name, "__und_svc") ||
          strstr(func_name, "ret_from_exeception") ||
          strstr(func_name, "__irq_usr") ||
          strstr(func_name, "fpundefinstr"))
            return 1;
      else
            return 0;
}

/*
 * find_trace_arm()
 *
 *   Given a starting pc (start_pc), starting stack pointer (start_sp), 
 *   and stack address, check to see if a valid trace is possible. A
 *   trace is considered valid if no errors are encountered (bad PC,
 *   bad SP, etc.) Certain errors are tolorated however. For example,
 *   if the current stack frame is an exception frame (e.g., VEC_*),
 *   go ahead and return success -- even if PC and SP obtained from
 *   the exception frame are bad (a partial trace is better than no
 *   trace)..
 *
 *   Return zero if no valid trace was found. Otherwise, return the
 *   number of frames found. If the C_ALL flag is passed in, then
 *   return a trace even if it is a subtrace of a trace that was
 *   previously found.
 *
 *   Parameters:
 *
 *   start_pc       starting program counter
 *   start_sp       starting stack pointer
 *   check_pc       if non-NULL, check to see if check_pc/check_sp
 *   check_sp       are a sub-trace of trace beginning with spc/ssp
 *   trace          structure containing all trace related info (frames,
 *                  pages, page/frame counts, etc.
 *   flags
 */
int
find_trace_arm(
      kaddr_t start_pc, 
      kaddr_t start_sp, 
      kaddr_t check_pc, 
      kaddr_t check_sp,
      trace_t *trace, 
      int flags)
{
      int curstkidx = 0, frame_size;
      kaddr_t sp, pc, ra, bp, sbase, saddr, func_addr;
      sframe_t *curframe;
      char *func_name;
      uaddr_t *sbp, *asp;
      struct frame_info fi;

      bzero(&fi, sizeof(struct frame_info));
      sbp = trace->stack[curstkidx].ptr;
      sbase = trace->stack[curstkidx].addr;
      saddr = sbase + trace->stack[curstkidx].size;
      if ((frame_size = arm_scan_prologue(start_pc, &fi)) >= 0)
            bp = start_sp + frame_size;
      else
            return 0;
      if (KL_ERROR || (bp < sbase) || (bp >= saddr)) {
            return(0);
      }
      pc = start_pc;
      sp = start_sp;
      func_name = kl_funcname(pc);

      while (pc) {

            /* LOOP TRAP! Make sure we are not just looping on the
             * same frame forever.
             */
            if ((trace->nframes > 1) &&
                (curframe->funcname == curframe->prev->funcname) &&
                (curframe->sp == curframe->prev->sp)) {
                  curframe->error = 1;
                  return(trace->nframes);
            } 

            /* Allocate space for a stack frame rec 
             */
            curframe = alloc_sframe(trace, flags);
            if (!(func_addr = kl_funcaddr(pc))) {
                  curframe->error = KLE_BAD_PC;
                  UPDATE_FRAME(0, pc, 0, 0, 0, 0, 0, 0, 0);
                  return(trace->nframes);
            }

            /* Check to see if check_pc/check_sp points to a sub-trace
             * of spc/ssp. If it does then don't return a trace (unless 
             * C_ALL). Make sure we free the curframe block since we 
             * wont be linking it in to the trace rec.
             */
            if (check_pc && ((pc == check_pc) && (sp == check_sp))) {
                  kl_free_block((void *)curframe);
                  if (flags & C_ALL) {
                        return(trace->nframes);
                  } else {
                        return(0);
                  }
            }
            asp = (uaddr_t*)((uaddr_t)sbp + (uaddr_t)(STACK_SIZE - (saddr - sp)));
            ra = kl_kaddr_to_ptr(bp + fi.saved_regs[ARM_LR_REGNUM]);
            UPDATE_FRAME(func_name, pc, ra, sp, sp + fi.framesize,
                       (unsigned int)asp, 0, 0, 0);
            /* Make sure that the ra we have is a valid one. If not
             * we may either met some special case that can not do 
             * stack roll-back or we have a corrupt stack.
             */
            if (!valid_ra_arm(ra)) {
                  func_name = kl_funcname(ra);
                  if (is_trace_endpoint(func_name)) {
                        bp = saddr - 4;
                        sp = curframe->fp + 4;
                        curframe = alloc_sframe(trace, flags);
                        asp = sbp + (STACK_SIZE - (saddr - sp));
                        UPDATE_FRAME(func_name, ra, 0, sp, bp,
                                       (unsigned int)asp, 0, 0, 0);
                  }
                  else{
                        curframe->error = KLE_BAD_RA;
                        UPDATE_FRAME(func_name, pc, ra, sp, 
                                     bp + 4, (unsigned int)asp, 0, 0, 0);
                  }
#ifdef FRMSIZE_DBG
                  printf("The return address 0x%x is not a valid "
                         "\"bl\" like instrunction\n!", (unsigned int)ra);
#endif
                  return(trace->nframes);
            }
            curframe->frame_size = fi.framesize;

            /* Gather starting information for the next frame
             */
            pc = get_call_pc_arm(ra);
            func_name = kl_funcname(pc);
            
            /* Execlude special cases for some situation that stack roll-back
             * will fail because of the implementation that did not comply with
             * ATPCS or do not have a well structured prologue information
             */
            if (func_name) {
                  if (is_trace_endpoint(func_name)) {
                        bp = saddr - 4;
                        sp = curframe->fp + 4;
                        curframe = alloc_sframe(trace, flags);
                        asp = sbp + (STACK_SIZE - (saddr - sp));
                        UPDATE_FRAME(func_name, pc, 0, sp, bp,
                                       (unsigned int)asp, 0, 0, 0);
                        return(trace->nframes);
                  }
            }

            bzero(&fi, sizeof(struct frame_info));
            if ((frame_size = arm_scan_prologue(pc, &fi)) >= 0) {
                  if ((curframe->fp + frame_size) >= saddr) {
                        bp = saddr - 4;
                  } 
                  else {
                        bp = curframe->fp + frame_size;
                  }
            }
            else{
                  /* Scan prologue failed */
                  curframe->error = 3;
#ifdef FRMSIZE_DBG
                  printf("The prologue decoding failed for pc = 0x%x!\n", (unsigned int)pc);
#endif
                  return(trace->nframes);
            }

            /* Make sure our next frame pointer is valid (in the stack).
             */
            if ((bp < sbase) || (bp >= saddr)) {
                  curframe->error = 3;
                  return(trace->nframes);
            }
            sp = curframe->fp;
      }
      return(trace->nframes);
}

/*
 * print_trace_arm()
 */
void
print_trace_arm(trace_t *trace, int flags, FILE *ofp)
{
      int offset;
      sframe_t *frmp;
      struct frame_info fi;
      int frame_size;

      if ((frmp = trace->frame)) {
            do {
                  fprintf(ofp, "%2d %s", frmp->level, frmp->funcname);
                  offset = pc_offset(frmp->pc);
                  if (offset > 0) {
                        fprintf(ofp, "+0x%x", offset);
                  } else if (offset < 0) {
                        fprintf(ofp, "+<ERROR>");
                  }
                  fprintf(ofp, " [0x%x]\n", (unsigned int)frmp->pc);
#if 0
                  if (frmp->flag & EX_FRAME)
                        print_eframe(ofp, (struct kl_pt_regs_arm *)frmp->asp);
#endif
                  if (flags & C_FULL) {
                        fprintf(ofp, "\n");
                        fprintf(ofp, "   RA=0x%x, SP=0x%x, FP=0x%x, "
                              "SIZE=%d\n\n", 
                              (unsigned int)frmp->ra, 
                                (unsigned int)frmp->sp, 
                              (unsigned int)frmp->fp,
                              frmp->frame_size);
#ifdef FRMSIZE_DBG
                        frame_size = arm_scan_prologue(frmp->pc, &fi);
                        fprintf(ofp, "\n  FRAMESIZE=%d\n\n", frame_size);
#endif
                        dump_stack_frame(trace, frmp, ofp);
                  }
                  if (frmp->error) {
                        fprintf(ofp, "TRACE ERROR 0x%llx\n", 
                              frmp->error);
                  }
                  frmp = frmp->next;
            } while (frmp != trace->frame);
      }
}

/*
 * task_trace_arm()
 */
int
task_trace_arm(kaddr_t task, int flags, FILE *ofp)
{
      void *tsp;
      kaddr_t saddr, eip, esp;
      trace_t *trace;
      kaddr_t saddr1;

        if (!(tsp = kl_alloc_block(TASK_STRUCT_SZ, K_TEMP))) {
            return(1);
      }
      if (kl_get_task_struct(task, 2, tsp)) {
            kl_free_block(tsp);
            return(1);
      }
      trace = (trace_t *)alloc_trace_rec(C_TEMP);
      if (!trace) {
            fprintf(KL_ERRORFP, "Could not alloc trace rec!\n");
            return(1);
      } else {
            saddr = kl_kernelstack_arm(task);
            if (kl_smp_dumptask_arm(task)) {
                  eip = kl_dumpeip_arm(task);
                  esp = kl_dumpesp_arm(task);
            } else {
                  if (LINUX_2_2_X(KL_LINUX_RELEASE)) {
                        struct kl_thread_2_2 temp;
                        unsigned long regs[CONTEXT_SIZE_2_2];
                        saddr1 = (kaddr_t)
                               ((unsigned long)
                                K_PTR(task, "task_struct", "tss"));
                        GET_BLOCK(saddr1, sizeof(struct kl_thread_2_2),
                               &temp);
                        if (KL_ERROR) {
                              kl_free_block(tsp);
                              free_trace_rec(trace);
                              fprintf(KL_ERRORFP, "Error reading "
                                              "thread structure!\n");
                              return 1;
                        }
                        saddr1 = KL_GET_UINT32(&(temp.saved_context));
                        GET_BLOCK(saddr1, 
                                sizeof(unsigned long) * CONTEXT_SIZE_2_2,
                                &regs[0]);
                        if (KL_ERROR) {
                              kl_free_block(tsp);
                              free_trace_rec(trace);
                              fprintf(KL_ERRORFP, "Error reading "
                                              "thread structure!\n");
                              return 1;
                        }
                        esp = saddr1 + CONTEXT_SIZE_2_2 * 
                                     sizeof(unsigned long);
                        eip = KL_GET_UINT32(&(regs[PC_INDEX_2_2]));                 
                  } 
                  else if (LINUX_2_4_X(KL_LINUX_RELEASE)) {
                        struct kl_thread_2_4 temp;
                        unsigned long regs[CONTEXT_SIZE_2_4];
                        saddr1 = (kaddr_t)(
                                       (unsigned long)
                                       K_PTR(task, "task_struct", 
                                               "thread"));
                        GET_BLOCK(saddr1, sizeof(struct kl_thread_2_4),
                               &temp);
                        if (KL_ERROR) {
                              kl_free_block(tsp);
                              free_trace_rec(trace);
                              fprintf(KL_ERRORFP, "Error reading "
                                              "thread structure!\n");
                              return 1;
                        }
                        saddr1 = KL_GET_UINT32(&(temp.saved_context));
                        GET_BLOCK(saddr1, 
                                sizeof(unsigned long) * CONTEXT_SIZE_2_4,
                                &regs[0]);
                        if (KL_ERROR) {
                              kl_free_block(tsp);
                              free_trace_rec(trace);
                              fprintf(KL_ERRORFP, "Error reading "
                                              "thread structure!\n");
                              return 1;
                        }
                        esp = saddr1 + CONTEXT_SIZE_2_4 * 
                                     sizeof(unsigned long);
                        eip = KL_GET_UINT32(&(regs[PC_INDEX_2_4]));
                  }
                  else {
                        /* Assum linux version is 2.5.x */
                        struct kl_thread_info temp;
                        saddr1 = (kaddr_t)(
                                       (unsigned long)
                                       K_PTR(task, "task_struct", 
                                             "thread_info"));
                        GET_BLOCK(saddr1, sizeof(unsigned long), &saddr1);
                        if (KL_ERROR) {
                              kl_free_block(tsp);
                              free_trace_rec(trace);
                              fprintf(KL_ERRORFP, "Error reading "
                                              "thread_info "
                                              "structure!\n");
                              return 1;
                        }
                        saddr1 = KL_GET_UINT32(&saddr1);
                        GET_BLOCK(saddr1, sizeof(struct kl_thread_info),
                                &temp);
                        if (KL_ERROR) {
                              kl_free_block(tsp);
                              free_trace_rec(trace);
                              fprintf(KL_ERRORFP, "Error reading "
                                              "thread_info "
                                              "structure!\n");
                              return 1;
                        }
                        eip = KL_GET_UINT32(&(temp.cpu_context_save[PC_INDEX_2_5]));
                        esp = KL_GET_UINT32(&(temp.cpu_context_save[SP_INDEX_2_5]));
                  }
            }
            if (esp < KL_PAGE_OFFSET || eip < KL_PAGE_OFFSET) {
                  fprintf(KL_ERRORFP, "Task in user space, No backtrace\n");
                  return 1;
            }
            setup_trace_rec_arm(saddr, 0, 0, trace);
            if (KL_ERROR) {
                  fprintf(KL_ERRORFP, "Error setting up trace rec!\n");
                  free_trace_rec(trace);
                  return(1);
            }
            find_trace_arm(eip, esp, 0, 0, trace, 0);
            trace_banner(ofp);
            fprintf(ofp, "STACK TRACE FOR TASK: 0x%x", 
                  (unsigned int)task);
            if (KL_TYPEINFO()) {
                  fprintf(ofp, "(%s)\n\n", 
                        (char *)K_PTR(tsp, "task_struct", "comm"));     
            } else {
                  fprintf(ofp, "(%s)\n\n", 
                        (char *)K_PTR(tsp, "task_struct", "comm"));
            }
            print_trace_arm(trace, flags, ofp);
      }
      kl_free_block(tsp);
      free_trace_rec(trace);
      return(0);
}

/*
 * print_traces_arm()
 *
 *   Output a list of all valid code addresses contained in a stack
 *   along with their function name and stack location.
 */
int
print_traces_arm(kaddr_t saddr, int level, int flags, FILE *ofp)
{
      int nfrms;
      char *fname, *cfname;
      uaddr_t *wordp, *stackp;
      trace_t *trace;
      kaddr_t addr, isp, caddr, sbase;
      
      stackp = (uaddr_t*)kl_alloc_block(STACK_SIZE, K_TEMP);
      sbase = saddr - STACK_SIZE;
      GET_BLOCK(sbase, STACK_SIZE, stackp);
      if (KL_ERROR) {
            kl_free_block(stackp);
            return(1);
      }

      if (!(trace = (trace_t *)alloc_trace_rec(K_TEMP))) {
            fprintf(KL_ERRORFP, "Could not alloc trace rec!\n");
            kl_free_block(stackp);
            return(1);
      }
      setup_trace_rec_arm(saddr, 0, 0, trace);

      wordp = stackp;
      while(wordp < (stackp + (STACK_SIZE / 4))) {
            if ((addr =  (kaddr_t)(*(uaddr_t*)wordp))) {

                  /* check to see if this is a valid code address
                   */
                  if ((fname = kl_funcname(addr))) {
                        /* Now use the instruction to back up and
                         * see if this RA was saved after a call.
                         * If it was, then try to determine what 
                         * function was called. At the very least,
                         * only print out info for true return
                         * addresses (coming right after a call
                         * instruction -- even if we can't tell
                         * what function was called).
                         */
                        isp = sbase + 
                              (((uaddr_t)wordp) - ((uaddr_t)stackp));

                        cfname = (char *)NULL;
                        caddr = 0;
                        if (get_jmp_instr(addr, isp, 
                                    &caddr, fname, &cfname)) {
                              wordp++;
                              continue;
                        }

                        /* We have found a valid jump address. Now, 
                         * try and get a backtrace.
                         */
                        nfrms = find_trace_arm(addr, isp, 0, 0, trace, 0);
                        if (nfrms) {
                              if ((nfrms >= level) &&
                                     (!trace->frame->prev->error ||
                                          (flags & C_ALL))) {
                                    fprintf(ofp, "\nPC=");
                                    print_kaddr(addr, ofp, 0);
                                    fprintf(ofp, "  SP=");
                                    print_kaddr(isp, ofp, 0);
                                    fprintf(ofp, "  SADDR=");
                                    print_kaddr(saddr, ofp, 0);
                                    fprintf(ofp, "\n");
                                    trace_banner(ofp);
                                    print_trace_arm(trace, flags, ofp);
                                    trace_banner(ofp);
                              }
                              free_sframes(trace);
                        }
                  }
                  wordp++;
            } else {
                  wordp++;
            }
      }
      kl_free_block(stackp);
      return(0);
}

/*
 * do_list_arm()
 *
 *   Output a list of all valid code addresses contained in a stack
 *   along with their function name and stack location.
 */
int
do_list_arm(kaddr_t saddr, int size, FILE *ofp)
{
      char *fname, *cfname;
      uaddr_t *wordp, *stackp;
      kaddr_t addr, isp, caddr, sbase;
      
      stackp = (uaddr_t*)kl_alloc_block(size, K_TEMP);
      sbase = saddr - size;
      GET_BLOCK(sbase, size, stackp);
      if (KL_ERROR) {
            kl_free_block(stackp);
            return(1);
      }

      wordp = stackp;
      while(wordp < (stackp + (size / 4))) {
            if ((addr =  (kaddr_t)(*(uaddr_t*)wordp))) {

                  /* check to see if this is a valid code address
                   */
                  if ((fname = kl_funcname(addr))) {
                        /* Now use the instruction to back up and
                         * see if this RA was saved after a call.
                         * If it was, then try to determine what 
                         * function was called. At the very least,
                         * only print out info for true return
                         * addresses (coming right after a call
                         * instruction -- even if we can't tell
                         * what function was called).
                         */
                        isp = sbase + 
                              (((uaddr_t)wordp) - ((uaddr_t)stackp));

                        cfname = (char *)NULL;
                        caddr = 0;
                        if (get_jmp_instr(addr, isp, 
                                    &caddr, fname, &cfname)) {
                              wordp++;
                              continue;
                        }
                        fprintf(ofp, "0x%x -- 0x%x (%s)",
                              (unsigned int)isp, 
                              (unsigned int)addr, 
                              fname);
                        if (cfname) {
                              fprintf(ofp, " --> 0x%x (%s)\n",
                                    (unsigned int)caddr, 
                                    cfname);
                        } else {
                              fprintf(ofp, "\n");
                        }
                  }
                  wordp++;
            } else {
                  wordp++;
            }
      }
      kl_free_block(stackp);
      return(0);
}

/*
 * add_frame_arm()
 */
int
add_frame_arm(trace_t *trace, kaddr_t fp, kaddr_t ra)
{
      sframe_t *cf, *sf;

      /* Check to make sure that sp is from the stack in the trace
       * record.
       *
       * XXX -- todo
       */
      sf = (sframe_t *)alloc_sframe(trace, C_PERM);
      sf->fp = fp;
      sf->ra = ra;
      if ((cf = trace->frame)) {
            do {
                  if (cf->fp && (sf->fp < cf->fp)) {
                        if (cf->next == cf) {
                              cf->prev = sf;
                              sf->next = cf;
                              cf->next = sf;
                              sf->prev = cf;
                              trace->frame = sf;
                        } else {
                              cf->prev->next = sf;
                              sf->prev = cf->prev;
                              cf->prev = sf;
                              sf->next = cf;
                        }
                        return(0);
                  }
                  cf = cf->next;
            } while (cf != trace->frame);
            cf = 0;
      } 
      if (!cf) {
            kl_enqueue((element_t **)&trace->frame, (element_t *)sf);
      }
      return(1);
}

/*
 * finish_trace_arm()
 */
void
finish_trace_arm(trace_t *trace)
{
      int level = 0, curstkidx = 0;
      uaddr_t *sbp;
      kaddr_t sbase, saddr;
      sframe_t *sf;

      sbp = trace->stack[curstkidx].ptr;
      sbase = trace->stack[curstkidx].addr;
      saddr = sbase + trace->stack[curstkidx].size;

      if ((sf = trace->frame)) {
            do {
                  if (!sf->pc) {
                        if (sf != trace->frame) {
                              sf->sp = sf->prev->fp + 4;
                              sf->pc = get_call_pc_arm(sf->prev->ra);
                        }
                        if (!sf->pc) {
                              sf = sf->next;
                              continue;
                        }
                  }
                  sf->level = level++;
                  sf->frame_size = sf->fp - sf->sp + 4;
                  sf->funcname = kl_funcname(sf->pc);
                  sf->asp = ((uaddr_t)sbp + 
                           (uaddr_t)(STACK_SIZE-(saddr-sf->sp)));
                  sf = sf->next;
            } while (sf != trace->frame);

            if (level > 0) {
                  sf = (sframe_t *)alloc_sframe(trace, C_PERM);
                  sf->level = level;
                  sf->sp = trace->frame->prev->fp + 4;
                  sf->pc = get_call_pc_arm(trace->frame->prev->ra);
                  sf->funcname = kl_funcname(sf->pc);
                  if (sf->funcname && 
                              strstr(sf->funcname, "kernel_thread")) {
                        sf->ra = 0;
                        sf->fp = saddr - 4;
                        sf->asp = ((uaddr_t)sbp + 
                                 (uaddr_t)(STACK_SIZE - 12));
                  } else {
                        sf->fp = saddr - 20;
                        sf->ra = kl_kaddr_to_ptr(sf->fp);
                        sf->asp = ((uaddr_t)sbp + 
                                 (uaddr_t)(STACK_SIZE-
                                         (saddr-sf->sp)));
                  }
                  sf->frame_size = sf->fp - sf->sp + 4;
                  kl_enqueue((element_t **)&trace->frame, 
                        (element_t *)sf);
            }
      }
}

/*
 * dumptask_trace_arm()
 */
int
dumptask_trace_arm(
      kaddr_t curtask, 
      int flags, 
      FILE *ofp)
{
      kaddr_t eip, esp, saddr;
      void *tsp;
      trace_t *trace;

      eip = kl_dumpeip_arm(curtask);
      esp = kl_dumpesp_arm(curtask);
      
      if (eip == 0 && esp == 0)
            return(1);

      tsp = kl_alloc_block(TASK_STRUCT_SZ, K_TEMP);
      if (!tsp) {
            return(1);
      }
      if (kl_get_task_struct(curtask, 2, tsp)) {
            kl_free_block(tsp);
            return(1);
      }
      if (!(trace = alloc_trace_rec(K_TEMP))) {
            fprintf(KL_ERRORFP, "Could not alloc trace rec!\n");
      } else {
            saddr = kl_kernelstack_arm(curtask);
            setup_trace_rec_arm(saddr, 0, 0, trace);
            find_trace_arm(eip, esp, 0, 0, trace, 0);
            trace_banner(ofp);
            fprintf(ofp, "STACK TRACE FOR TASK: 0x%"FMTPTR"x (%s)\n\n",
                  curtask, (char*)K_PTR(tsp, "task_struct", "comm"));
            print_trace_arm(trace, flags, ofp);
            trace_banner(ofp);
            free_trace_rec(trace);
      }
      kl_free_block(tsp);
      return(0);
}

/*
 * trace_init_arm()
 */
int
trace_init_arm(void)
{
      SETUP_TRACE_REC = setup_trace_rec_arm;
      PRINT_TRACE = print_trace_arm;
      TASK_TRACE  = task_trace_arm;
      PRINT_TRACES      = print_traces_arm;
      FIND_TRACE  = find_trace_arm;
      DUMPTASK_TRACE  = dumptask_trace_arm;
      DO_LIST           = do_list_arm;
      STACK_SEGMENTS  = STACK_SEGMENTS_ARM;
      STACK_SIZE  = STACK_SIZE_ARM;
      return(0);
}


Generated by  Doxygen 1.6.0   Back to index