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

dis_arm.c

/* 
 * This file is part of lcrash, an analysis tool for Linux memory dumps.
 * 
 * Created by Fleming Feng(fleming.feng@intel.com).
 *
 * 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 GNU General Public 
 * License for more details.
 */

#include <string.h>
#include <lcrash.h>

void dis_memory_error(int status, bfd_vma pc,
                  struct disassemble_info* info);
void dis_print_addr(bfd_vma addr, struct disassemble_info* info);

static void printaddr(kaddr_t addr, int flag, FILE *ofp);

static struct disassemble_info dis_arm_info = {
      /* Function pointers */
      fprintf_func:           (fprintf_ftype)fprintf,
      read_memory_func:       getidmem,
      memory_error_func:      dis_memory_error,
      print_address_func:     dis_print_addr,

      /* Constants */
      flavour:          bfd_target_elf_flavour,
      arch:             bfd_arch_arm,
      mach:             bfd_mach_arm_XScale,
      endian:                 BFD_ENDIAN_LITTLE,
      display_endian:         BFD_ENDIAN_LITTLE
};

void dis_memory_error(int status, bfd_vma pc,
                  struct disassemble_info* info)
{
      fprintf(stderr, "Disassembler: Can not read memory "
                  "at location %#"FMTPTR"x!\n", (uint64_t) pc);

      return;
}

void dis_print_addr(bfd_vma addr, struct disassemble_info* info)
{
      printaddr(addr, 0, info->stream);
}

int get_instr_info_arm(kaddr_t pc, instr_rec_arm_t* irp)
{
      kaddr_t vma;
      int size;
      FILE* tmpfile = NULL;
      
      if (irp == NULL) {
            irp = (instr_rec_arm_t*)kl_alloc_block(sizeof(instr_rec_arm_t), K_TEMP);
            bzero(irp, sizeof(instr_rec_arm_t));
      }
      bzero(irp, sizeof(instr_rec_arm_t));

      vma = pc;
      GET_BLOCK(vma, 4, &irp->opcode);
      if (KL_ERROR) {
            return 0;
      }
      irp->size = 4;
      irp->addr = vma;
      if ((tmpfile = fopen("/tmp/fooCrashTmp", "w+")) == NULL) {
            fprintf(stderr, "Can not open temp files for disassebler!\n");
            return 0;
      }
      dis_arm_info.stream = (PTR)tmpfile;
      if (dis_arm_info.endian == BFD_ENDIAN_BIG)
            size = print_insn_big_arm(vma, &dis_arm_info);
      else if(dis_arm_info.endian == BFD_ENDIAN_LITTLE)
            size = print_insn_little_arm(vma, &dis_arm_info);
      fseek(tmpfile, 0, SEEK_SET);
      fread(&(irp->instr_string[0]), sizeof(char), INSTR_STRLEN_MAX - 1, tmpfile);
      fclose(tmpfile);
      
      return size;
}

static void
printaddr(kaddr_t addr, int flag, 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+0x%x>",
                        sp->s_name, offset);
            } else {
                  fprintf(ofp, " <%s>", sp->s_name);
            }
      }

      /* Line things up properly for current function
       */
      if (flag) {
            if (offset == 0) {
                  fprintf(ofp, ":         ");
            } else if (offset < 0x10) {
                  fprintf(ofp, ":     ");
            } else if (offset < 0x100) {
                  fprintf(ofp, ":    ");
            } else if (offset < 0x1000) {
                  fprintf(ofp, ":   ");
            } else if (offset < 0x10000) {
                  fprintf(ofp, ":  ");
            } else {
                  fprintf(ofp, ": ");
            }
      }
}

int
print_instr_arm(kaddr_t pc, FILE *ofp, int flag)
{
      instr_rec_arm_t irp;

      bzero(&irp, sizeof(irp));
      /* XXX -- For now, make aflag and dflag equal to one.  Should get
       * this from some sort of configuration struct (set via 
       * initialization)
       */
      get_instr_info_arm(pc, &irp);
      printaddr(pc, 1, ofp);
      if (flag) {
            fprintf(ofp, "0x%08lx  ", irp.opcode);
      }
      fprintf(ofp, "%s", irp.instr_string);
      fprintf(ofp, "\n");
      return(irp.size);
}

void
list_instructions_arm(FILE *ofp)
{
      fprintf(ofp, "This operation(list instruction) not supported for ARM/Scale architecture.\n");
}

void
free_instr_stream_arm(instr_rec_arm_t *irp)
{
      instr_rec_arm_t *ptr;

      if (irp) {
            while (irp->prev) {
                  irp = irp->prev;
            }
            while (irp) {
                  ptr = irp;
                  irp = irp->next;
                  kl_free_block(ptr);
            }
      }
}

instr_rec_arm_t *
get_instr_stream_arm(kaddr_t pc, int bcount, int acount)
{
      int size, count = 0;
      kaddr_t addr, start_addr, end_addr;
        syment_t *sp1;
      instr_rec_arm_t *fst = (instr_rec_arm_t *)NULL, *ptr, *cur;

      if (!(sp1 = kl_lkup_symaddr(pc))) {
            /* make sure that the pc is in the range of a code segment */
            return((instr_rec_arm_t *)NULL);
      }

      /* The following based on the pre-assumption that all ARM/XScale
       * instructions are 4 bytes long. 
       */
      addr = pc & 0xfffffffc;
      start_addr = addr - bcount * 4;
      end_addr = addr + acount * 4;
      addr = start_addr;
      while (addr <= end_addr){
            cur = (instr_rec_arm_t *)kl_alloc_block(sizeof(instr_rec_arm_t), K_TEMP);
            bzero(cur, sizeof(instr_rec_arm_t));
            count ++;
            if ((ptr = fst)) {
                  while(ptr->next){
                        ptr = ptr->next;
                  }
                  ptr->next = cur;
                  cur->prev = ptr;
            }
            else{
                  fst = cur;
            }
            size = get_instr_info_arm(addr, cur);
            if (size == 0) {
                  free_instr_stream_arm(cur);
                  return((instr_rec_arm_t *)NULL);
            }
            addr += size;
      }

      return(fst);
}

/*
 * print_instr_stream_arm()
 */
kaddr_t
print_instr_stream_arm(kaddr_t value, int bcount, int acount, int flags, FILE *ofp)
{
      kaddr_t v = value;
      instr_rec_arm_t *cur_irp, *irp;

      if ((cur_irp = get_instr_stream_arm(v, bcount, acount))) {
            irp = cur_irp;

            /* Walk back to the start of the stream and then
             * print out all instructions in the stream.
             */
            while (irp->prev) {
                  irp = irp->prev;
            }
            while (irp) {
                  if (flags & C_FULL) {
                        print_instr_arm(irp->addr, ofp, 1);
                  } else {
                        print_instr_arm(irp->addr, ofp, 0);
                  }
                  if (irp->addr >= value) {
                        v += irp->size;
                  }
                  irp = irp->next;
            }
            free_instr_stream_arm(cur_irp);
      }
      return(v);
}

/*
 * dump_instr() -- architecture specific instruction dump routine
 */
void
dump_instr(kaddr_t addr, uint64_t count, int flags, FILE *ofp)
{
      fprintf(ofp, "This operation not supported for ARM/Scale architecture.\n");
}

/*
 * dis_init_arm() - init arch specific stuff for disassembling
 */
int
dis_init_arm(FILE* ofp, int dumparch)
{
      LIST_INSTRUCTIONS           = list_instructions_arm;
      PRINT_INSTR             = print_instr_arm;
      PRINT_INSTR_STREAM          = print_instr_stream_arm;
      if (KL_BYTE_ORDER == KL_BIG_ENDIAN){
            dis_arm_info.endian         = BFD_ENDIAN_BIG;
            dis_arm_info.display_endian = BFD_ENDIAN_BIG;
      }
      else if(KL_BYTE_ORDER == KL_LITTLE_ENDIAN){
            dis_arm_info.endian         = BFD_ENDIAN_LITTLE;
            dis_arm_info.display_endian = BFD_ENDIAN_LITTLE;
      }
      
      return (0);
}



Generated by  Doxygen 1.6.0   Back to index