A piece code for backtrace in MIPS

时间:2022-10-06 00:14:33

一个相关的链接

 http://www.yosefk.com/blog/getting-the-call-stack-without-a-frame-pointer.html

 

#define _SYS_UCONTEXT_H
#define _BITS_SIGCONTEXT_H
#include <signal.h>
//#include <asm/ucontext.h>
#include <asm-mips/types.h>
#include <asm-mips/sigcontext.h>
#include <asm-mips/ucontext.h>
#include <stdio.h>
#include <time.h>

typedef struct pmap_line {
    unsigned long vm_start;     /* Our start address within vm_mm. */
    unsigned long vm_end;       /* The first byte after our
                                   end address within vm_mm. */
    char perm[5];               /* permission */
    unsigned long vm_pgoff;     /* Offset (within vm_file) in PAGE_SIZE units,
                                   *not* PAGE_CACHE_SIZE */
    char dev[6];                /* device name */
    unsigned long ino;              /* innode number */
    struct pmap_line *next;
} pmap_line_t;

static pmap_line_t *pmap_line_head=NULL;

static void free_pmap_line()
{
    pmap_line_t *line=NULL;
    while((line=pmap_line_head) != NULL)
    {
        pmap_line_head=pmap_line_head->next;
        free(line);
    }
}

static void getpmaps(size_t pid)
{

    FILE *f;
    char buf[4096+100]={0};
    pmap_line_t *pmap_line_tail=NULL;
    pmap_line_t *line=NULL;
    char fname [50]={0};

    sprintf(fname, "/proc/%ld/maps", (long)pid);
    f = fopen(fname, "r");
    if(!f)
    {
        return;
    }

    while(!feof(f))
    {
        if(fgets(buf, sizeof(buf), f) == 0)
            break;
        line=(pmap_line_t*)malloc(sizeof(pmap_line_t));
        memset(line,sizeof(pmap_line_t),0);

        sscanf(buf, "%lx-%lx %4s %lx %5s %lu", &line->vm_start, &line->vm_end,
                line->perm, &line->vm_pgoff, line->dev, &line->ino);
        line->next=NULL;

        if(!pmap_line_head)
        {
            pmap_line_head=line;
        }
        if(pmap_line_tail)
        {
            pmap_line_tail->next=line;
        }
        pmap_line_tail = line;
    }
    line=pmap_line_head;
    while(line)
    {
        printf("%08lx-%08lx %s %08lx %s %lu/n",line->vm_start,line->vm_end,
                line->perm,line->vm_pgoff,line->dev,line->ino);
        line=line->next;
    }
    fclose(f);
}

static int canReadAddr(unsigned long addr)
{
    pmap_line_t *line=pmap_line_head;
    if(!pmap_line_head)
    {
        return 0;
    }

    while(line)
    {
        if(line->perm[0] == 'r' &&
                addr >= line->vm_start && addr <=line->vm_end)
        {
            return 1;
        }
        line=line->next;
    }
    printf("cannot read address %lu/n",addr);
    return 0;

}

/**PROC+**********************************************************************/
/* Name:      sigbacktrace_mips                                              */
/*                                                                           */
/* Purpose:   standard backtrace function provided by glic does NOT support  */
/*            MIPS architecture. This function provids same functionality for*/
/*            MIPS, retreive the calling function stack pointer address based*/
/*            on current PC.                                                 */
/*                                                                           */
/* Returns:   Nothing.                                                       */
/*                                                                           */
/* Params:    IN/OUT     buffer   - buffer to hold text address returned     */
/*            IN/OUT     buffer   - buffer to hold text function address     */
/*                                  returned                                 */
/*                                                                           */
/*            IN         size     - buffer size                              */
/*                                                                           */
/*            IN         uc       -MIPS ucontext structure returned from     */
/*                                 kernel, holding registers when signal     */
/*                                 occurs.                                   */
/*                                                                           */
/**PROC-**********************************************************************/
static int sigbacktrace_mips(void ** buffer,void ** func,
        int size, struct ucontext *uc)
{
    unsigned long *addr = NULL;
    unsigned long *pc=NULL;
    unsigned long *ra=NULL;
    unsigned long long *sp=NULL;
    size_t ra_offset=0;
    size_t stack_size=0;
    size_t depth=0;
    int first=0;

    if(size == 0){
        return 0;
    }

    if(!buffer || size<0 || !uc){
        return -1;
    }

    //get current $pc, $ra and $sp
    pc=(unsigned long*)(unsigned long)uc->uc_mcontext.sc_pc;
    ra=(unsigned long*)(uc->uc_mcontext.sc_regs[31]);
    sp=(unsigned long long*)(uc->uc_mcontext.sc_regs[29]);

    if(canReadAddr((unsigned long long)pc))
    {
        depth=1;
        buffer[0] = pc;
    }
    else
    {
        return 0;
    }

    //scanning to find the size of the current stack-frame
    ra_offset = stack_size = 0;

    addr=pc;
    first=1;

    while(1)
    {
//printf("addr:%08lx, pc:%08lx, ra content:%lx/n", addr, pc, *ra);
        if(!canReadAddr((unsigned long)addr))
        {
            return depth;
        }
       
        if(*addr == 0x1000ffff)
 {
  return depth;
 }

        switch(*addr & 0xffff0000)
        {
            case 0x23bd0000:
            /* 0x23bdxxxx: ADDI SP, SP, xxxx
             ADDIU -- Add immediate (with overflow)
              Description:
               Adds a register and a sign-extended immediate value and
               stores the result in a register
              
              Operation:
               $t = $s + imm; advance_pc (4);
              
              Syntax:
               addi $t, $s, imm
              
              Encoding:
               0010 00ss ssst tttt iiii iiii iiii iiii
             
              register : $29 sp
             
            */
            case 0x27bd0000:
            /* 0x27bdxxxx: ADDIU SP, SP, xxxx
             ADDIU -- Add immediate unsigned (no overflow)
              Description:
               Adds a register and a sign-extended immediate value and
               stores the result in a register
              
              Operation:
               $t = $s + imm; advance_pc (4);
              
              Syntax:
               addiu $t, $s, imm
              
              Encoding:
               0010 01ss ssst tttt iiii iiii iiii iiii
             
              register : $29 sp
             
            */
                stack_size = abs((short)(*addr & 0xffff));
//printf("----get addiu try to jump back---/n");
//printf("old ra:%08lx, ra_offset:%d, old sp:%08lx, sp_offset:%d /n", ra, ra_offset, sp, stack_size);
//printf("old ra content:%08lx/n", *ra);
                if(!first && (!ra_offset || !stack_size))
                {
                    //there's no return address or stack size,
                    //reach the calling stack top
                    return depth;
                }
                else{

                    //first level function, there might no ra/sp from text
                    //we might use those from regs
                    func[depth-1]=addr;

                    if(depth == size)
                    {
                        return depth;
                    }

                    if(ra_offset && !canReadAddr((unsigned long long)sp+ra_offset))
                    {
                        return depth;
                    }
                    if(ra_offset){
                        ra = (unsigned long*)(*(unsigned long long*)
    ((unsigned long long)sp+ra_offset));
                    }
                    if(stack_size){
                        sp = (unsigned long long*)
    ((unsigned long long)sp + stack_size);
                    }
//printf("new ra:%08lx, new sp:%08lx /n", ra, sp);
//printf("---jumping...---/n");
                    buffer[depth] = ra;
                    ++depth;
                    stack_size=0;
                    ra_offset=0;
                    addr=ra;
                }
                first=0;
//                --addr;
                break;

            case 0xafbf0000:
            /*0xafbfxxxx : sw ra ,xxxx(sp)
              SW -- Store word
              Description:
               The contents of $t is stored at the specified address.
              
              Operation:
               MEM[$s + offset] = $t; advance_pc (4);
              
              Syntax:
               sw $t, offset($s)
              
              Encoding:
               1010 11ss ssst tttt iiii iiii iiii iiii
             
               register $31 : ra ; $29 : sp
             
            */
                ra_offset = (short)(*addr & 0xffff);
                --addr;
                break;

            case 0xffbf0000:
            /*0xffbfxxxx : sd ra ,xxxx(sp)
              SD-- Store double word
              Description:
               The contents of $t is stored at the specified address.
              
              Operation:
               MEM[$s + offset] = $t; advance_pc (4);
              
              Syntax:
               sd $t, offset($s)
              
              Encoding:
               1010 11ss ssst tttt iiii iiii iiii iiii
             
               register $31 : ra ; $29 : sp
             
            */
                ra_offset = (short)(*addr & 0xffff);
                --addr;
                break;

//            case 0x3c1c0000:
//            /*  3c1cxxxx    lui gp xxxx
//                LUI -- Load upper immediate
//                Description:
//                 The immediate value is shifted left 16 bits and stored
//                 in the register. The lower 16 bits are zeroes.
//                
//                Operation:
//                 $t = (imm << 16); advance_pc (4);
//                
//                Syntax:
//                 lui $t, imm
//                
//                Encoding:
//                 0011 11-- ---t tttt iiii iiii iiii iiii
//               
//                registers: $28 : gp
//           */
//                if(!first && (!ra_offset || !stack_size))
//                {
//                    //there's no return address or stack size,
//                    //reach the calling stack top
//                    return depth;
//                }
//                else{
//
//                    //first level function, there might no ra/sp from text
//                    //we might use those from regs
//                    func[depth-1]=addr;
//
//                    if(depth == size)
//                    {
//                        return depth;
//                    }
//
//                    if(ra_offset && !canReadAddr((unsigned long long)sp+ra_offset))
//                    {
//                        return depth;
//                    }
//                    if(ra_offset){
//                        ra = (unsigned long*)(*(unsigned long long*)(
//                                    (unsigned long long)sp+ra_offset));
//                    }
//                    if(stack_size){
//                        sp = (unsigned long long*) (
//                                (unsigned long long)sp + stack_size);
//                    }
//                    buffer[depth] = ra;
//                    ++depth;
//                    stack_size=0;
//                    ra_offset=0;
//                    addr=ra;
//                }
//                first=0;
//                break;

            default:
                --addr;
        }
    }
    return depth;
}   
/**PROC+**********************************************************************/
/* Name:      nbb_mips_print_stack                                           */
/*                                                                           */
/* Purpose:   Print a stack back trace.                                      */
/*                                                                           */
/* Returns:   None.                                                          */
/*                                                                           */
/* Params:    IN   context ucontext structure from kernel                    */
/*                                                                           */
/* Operation:  crack out stack frames for MIPS.                              */
/*                                                                           */
/**PROC-**********************************************************************/

static void  mips_print_stack(void *context)
{
  /***************************************************************************/
  /* Local Variables                                                         */
  /***************************************************************************/
  void *array[50]={0};
  void *func[50]={0};
  char stack_log[50] = {0}; /* log file to hold the core dump stack*/
  size_t size;
  sprintf(stack_log,"backtrace_log.%d",getpid());
  FILE* stackLog = fopen(stack_log,"w");
  time_t currenttime;
  int i=0;
  char **strings = NULL;


  if(!stackLog)
  {
      printf("Cannot open stack log file./n");
      goto EXIT_LABEL;
  }

  getpmaps(getpid());
  size = sigbacktrace_mips(array, func,50,(struct ucontext*)context);
  if (size == 0)
  {
    /*************************************************************************/
    /* NBB_TRC_FLOW((NBB_FORMAT "No frames"));                               */
    /*************************************************************************/
    printf("Failed to find any stack frames./n");
    goto EXIT_LABEL;
  }

  if(time(&currenttime))
  {
      fprintf(stackLog,"/n%s/n",ctime(&currenttime));
  }

  strings = backtrace_symbols(func, size);

  if(!strings)
  {
      printf("Cannot get symbol with backtrace_symbols./n");
      goto EXIT_LABEL;
  }

  fprintf(stackLog,"Stack trace:/n");
  for(i=0;i<size;i++){
      fprintf(stackLog,"%d/t%s ; address: 0x%08llx ; offset: %lld/n",
              (i+1),strings[i],(unsigned long )array[i],
              (func[i]?((unsigned long)array[i]-(unsigned long)func[i]):0L));
  }

EXIT_LABEL:

  if(stackLog)
  {
      fclose(stackLog);
  }
  if(strings)
  {
      free(strings);
  }

  free_pmap_line();
} /* nbb_mips_print_stack */

void test(int n,struct siginfo *siginfo,void *myact)
{
         printf("signal number:%d/n",n);/** ´òÓ¡³öÐźÅÖµ **/
         printf("siginfo signo:%d/n",siginfo->si_signo); /** siginfo½á¹¹Àï±£´æµÄÐźÅÖµ **/
         printf("siginfo errno:%d/n",siginfo->si_errno); /** ´òÓ¡³ö´íÎó´úÂë **/
         printf("siginfo code:%d/n",siginfo->si_code);   /**¡¡´òÓ¡³ö³ö´íÔ­Òò **/
  mips_print_stack(myact);
}

void b()
{
         while(1)
         {
                 sleep(1);
                 printf("wait for the signal/n");
         }
}

void a()
{
  int mk;
  b(); 
}

void do_test()
{
  a();
}

int main(void)
{
         /** install signal use sigaction **/
         struct sigaction act;
         sigemptyset(&act.sa_mask);  
         act.sa_flags=SA_SIGINFO;   
         act.sa_sigaction=test;
         if(sigaction(SIGINT,&act,NULL) < 0)
         {
                 printf("install signal error/n");
         }
  do_test();
  return 0;
}

 

 

原理描述:

1. 先从sigaction函数中得到ucontext结构体,其中包含了

pc 指向当前要执行的指令

ra :return address 即上一个函数调用本函数的下一个指令

sp : 进程堆栈指针

 

2. 沿着pc所指的地方,网上找指令

 

3. 找到 保存 ra的指令

    知道ra 保存在堆栈的 哪个地方

4. 找到 改变 sp的指令

    知道本函数新开辟了多少大的堆栈

 

这样就可以从堆栈中找出上一个函数的地址,和上一个函数使用的堆栈的地方。