记录两种程序崩溃时查找方法

堆栈回溯实践

总结

目前堆栈回溯存在两套代码:

  • 代码1使用系统库函数backtrace()自动进行堆栈回溯,在x86,mips,arm架构上实践证明有效.
  • 代码2手动进行堆栈回溯,通过fp,ra等指针,一层一层找到调用的函数地址,并通过函数dladdr()得到函数名,使用此函数时链接选项添加-ldl,头文件为dlfcn.h。这种方案在x86,arm架构上有效,mips的堆栈帧太过复杂,目前没有手动回溯成功。
    通过工具链自定义的宏控,可以将两套代码合并,使用在我们的平台下。

编译时用到的小知识

  • 使用gcc -E -dM - </dev/null 查看工具链预定义的宏,如x86工具链预定义了__x86_64__,类似可以知道arm,mips预定义的类似宏(在方案二的代码中用到)。
  • 使用gcc -E -v - </dev/null查看交叉工具链的头文件与库文件搜索路径,方便我们使用交叉工具链进行编译。

编译注意事项

    • 使用-g -dynamic 产生必要的调试信息       < -g -dynamic -ldl> 个人觉得直接这样加上比较省事
    • 若手动进行符号解析,需要使用dladdr函数,链接选项添加-ldl
    • arm下编译选项需要带有 -mapcs-frame:对所有函数都生成一个遵从ARM程序调用标准的堆栈帧,即使在正确执行代码无需严格这么做时。使用此开关时指定“-fomit-frame-pointer”将不产生叶函数的堆栈帧。缺省情况下是“-mno-apcs-frame”。使用此编译选项,方案2即可有效。参考https://blog.csdn.net/weixin_30270561/article/details/94854486
    • arm下编译选项需要带有-mapcs-frame -fexceptions方案1才有效。
    • mips下可能需要编译选项-fexceptions -fno-omit-frame-pointer -O0(没有严格验证,只是为防止意外)

      代码1(mips,arm,x86下有效)

      #include <stdio.h>
      #include <execinfo.h>
      #include <signal.h>
      #include <stdlib.h>
      #include <unistd.h>
      
      
      void handler(int sig) {
        void *array[10];
        size_t size;
      
        // get void*'s for all entries on the stack
        size = backtrace(array, 10);
      
        // print out all the frames to stderr
        fprintf(stderr, "Error: signal %d:\n", sig);
        backtrace_symbols_fd(array, size, STDERR_FILENO);
        exit(1);
      }
      
      void baz() {
       //int *foo = (int*)-1; // make a bad pointer
       int *foo =(int*)123;
        printf("%d\n", *foo);       // causes segfault
      }
      
      void bar() { baz(); }
      void foo() { bar(); }
      
      
      int main(int argc, char **argv) {
        signal(SIGSEGV, handler);   // install our handler
        foo(); // this will call foo, bar, and baz.  baz segfaults.
      }

      代码2(x86,arm下有效,mips下无效)

      #ifndef _GNU_SOURCE
          #define _GNU_SOURCE
      #endif
      #include <stdio.h>
      #include <dlfcn.h>
      #include <stdlib.h>
      #include <signal.h>
      #include <unistd.h>
      #include <string.h>
      #include <ucontext.h>
       
       
      /* 纯C环境下,不定义宏NO_CPP_DEMANGLE */
      #if (!defined(__cplusplus)) && (!defined(NO_CPP_DEMANGLE))
      #define NO_CPP_DEMANGLE
      #endif
       
      #ifndef NO_CPP_DEMANGLE
          #include <cxxabi.h>
          #ifdef __cplusplus
              using __cxxabiv1::__cxa_demangle;
          #endif
      #endif
       
      #ifdef HAS_ULSLIB
          #include <uls/logger.h>
          #define sigsegv_outp(x)    sigsegv_outp(, gx)
      #else
          #define sigsegv_outp(x, ...)     fprintf(stderr, x"\n", ##__VA_ARGS__)
      #endif
       
      #if (defined __x86_64__)
          #define REGFORMAT   "%016lx"    
      #elif (defined __i386__)
          #define REGFORMAT   "%08x"
      #elif (defined __arm__)
          #define REGFORMAT   "%lx"
      #endif
       
      static void print_reg(ucontext_t *uc) 
      {
      #if (defined __x86_64__) || (defined __i386__)
          int i;
          for (i = 0; i < NGREG; i++) {
              sigsegv_outp("reg[%02d]: 0x"REGFORMAT, i, uc->uc_mcontext.gregs[i]);
          }
      #elif (defined __arm__)
          sigsegv_outp("reg[%02d]        = 0x"REGFORMAT, 0, uc->uc_mcontext.arm_r0);
          sigsegv_outp("reg[%02d]        = 0x"REGFORMAT, 1, uc->uc_mcontext.arm_r1);
          sigsegv_outp("reg[%02d]        = 0x"REGFORMAT, 2, uc->uc_mcontext.arm_r2);
          sigsegv_outp("reg[%02d]        = 0x"REGFORMAT, 3, uc->uc_mcontext.arm_r3);
          sigsegv_outp("reg[%02d]        = 0x"REGFORMAT, 4, uc->uc_mcontext.arm_r4);
          sigsegv_outp("reg[%02d]        = 0x"REGFORMAT, 5, uc->uc_mcontext.arm_r5);
          sigsegv_outp("reg[%02d]        = 0x"REGFORMAT, 6, uc->uc_mcontext.arm_r6);
          sigsegv_outp("reg[%02d]        = 0x"REGFORMAT, 7, uc->uc_mcontext.arm_r7);
          sigsegv_outp("reg[%02d]        = 0x"REGFORMAT, 8, uc->uc_mcontext.arm_r8);
          sigsegv_outp("reg[%02d]        = 0x"REGFORMAT, 9, uc->uc_mcontext.arm_r9);
          sigsegv_outp("reg[%02d]        = 0x"REGFORMAT, 10, uc->uc_mcontext.arm_r10);
          sigsegv_outp("FP        = 0x"REGFORMAT, uc->uc_mcontext.arm_fp);
          sigsegv_outp("IP        = 0x"REGFORMAT, uc->uc_mcontext.arm_ip);
          sigsegv_outp("SP        = 0x"REGFORMAT, uc->uc_mcontext.arm_sp);
          sigsegv_outp("LR        = 0x"REGFORMAT, uc->uc_mcontext.arm_lr);
          sigsegv_outp("PC        = 0x"REGFORMAT, uc->uc_mcontext.arm_pc);
          sigsegv_outp("CPSR        = 0x"REGFORMAT, uc->uc_mcontext.arm_cpsr);
          sigsegv_outp("Fault Address    = 0x"REGFORMAT, uc->uc_mcontext.fault_address);
          sigsegv_outp("Trap no        = 0x"REGFORMAT, uc->uc_mcontext.trap_no);
          sigsegv_outp("Err Code    = 0x"REGFORMAT, uc->uc_mcontext.error_code);
          sigsegv_outp("Old Mask    = 0x"REGFORMAT, uc->uc_mcontext.oldmask);
      #endif
      }
       
      static void print_call_link(ucontext_t *uc) 
      {
          int i = 0;
          void **frame_pointer = (void **)NULL;
          void *return_address = NULL;
          Dl_info    dl_info = { 0 };
       
      #if (defined __i386__)
          frame_pointer = (void **)uc->uc_mcontext.gregs[REG_EBP];
          return_address = (void *)uc->uc_mcontext.gregs[REG_EIP];
      #elif (defined __x86_64__)
          frame_pointer = (void **)uc->uc_mcontext.gregs[REG_RBP];
          return_address = (void *)uc->uc_mcontext.gregs[REG_RIP];
      #elif (defined __arm__)
      /* sigcontext_t on ARM:
              unsigned long trap_no;
              unsigned long error_code;
              unsigned long oldmask;
              unsigned long arm_r0;
              ...
              unsigned long arm_r10;
              unsigned long arm_fp;
              unsigned long arm_ip;
              unsigned long arm_sp;
              unsigned long arm_lr;
              unsigned long arm_pc;
              unsigned long arm_cpsr;
              unsigned long fault_address;
      */
          frame_pointer = (void **)uc->uc_mcontext.arm_fp;
          return_address = (void *)uc->uc_mcontext.arm_pc;
      #endif
       
          sigsegv_outp("\nStack trace:");
          while (frame_pointer && return_address) {
              if (!dladdr(return_address, &dl_info))    break;
              const char *sname = dl_info.dli_sname;    
      #ifndef NO_CPP_DEMANGLE
              int status;
              char *tmp = __cxa_demangle(sname, NULL, 0, &status);
              if (status == 0 && tmp) {
                  sname = tmp;
              }
      #endif
              /* No: return address <sym-name + offset> (filename) */
              sigsegv_outp("%02d: %p <%s + %lu> (%s)", ++i, return_address, sname, 
                  (unsigned long)return_address - (unsigned long)dl_info.dli_saddr, 
                                                          dl_info.dli_fname);
      #ifndef NO_CPP_DEMANGLE
              if (tmp)    free(tmp);
      #endif
              if (dl_info.dli_sname && !strcmp(dl_info.dli_sname, "main")) {
                  break;
              }
       
      #if (defined __x86_64__) || (defined __i386__)
              return_address = frame_pointer[1];
              frame_pointer = frame_pointer[0];
      #elif (defined __arm__)
              return_address = frame_pointer[-1];    
              frame_pointer = (void **)frame_pointer[-3];
      #endif
          }
          sigsegv_outp("Stack trace end.");
      }
       
      static void sigsegv_handler(int signo, siginfo_t *info, void *context)
      {
          if (context) {
              ucontext_t *uc = (ucontext_t *)context;
       
                 sigsegv_outp("Segmentation Fault!");
              sigsegv_outp("info.si_signo = %d", signo);
              sigsegv_outp("info.si_errno = %d", info->si_errno);
              sigsegv_outp("info.si_code  = %d (%s)", info->si_code, 
                  (info->si_code == SEGV_MAPERR) ? "SEGV_MAPERR" : "SEGV_ACCERR");
              sigsegv_outp("info.si_addr  = %p\n", info->si_addr);
       
              print_reg(uc);
              print_call_link(uc);
          }
       
          _exit(0);
      }
       
      #define SETSIG(sa, sig, fun, flags)     \
              do {                            \
                  sa.sa_sigaction = fun;      \
                  sa.sa_flags = flags;        \
                  sigemptyset(&sa.sa_mask);   \
                  sigaction(sig, &sa, NULL);  \
              } while(0)
       
      static void __attribute((constructor)) setup_sigsegv(void) 
      {
          struct sigaction sa;
       
          SETSIG(sa, SIGSEGV, sigsegv_handler, SA_SIGINFO); 
      #if 0
          memset(&sa, 0, sizeof(struct sigaction));
          sa.sa_sigaction = sigsegv_handler;
          sa.sa_flags = SA_SIGINFO;
          if (sigaction(SIGSEGV, &sa, NULL) < 0) {
              perror("sigaction: ");
          }
      #endif
      }
       
      #if 1
      void func3(void)
      {
          char *p = (char *)0x12345678;
          *p = 10;
      }
       
      void func2(void)
      {
          func3();    
      }
       
      void func1(void)
      {
          func2();
      }
       
      int main(int argc, const char *argv[])
      {
          func1();    
          exit(EXIT_SUCCESS);
      }
      #endif 

      参考链接:https://www.cnblogs.com/followthemaster/p/14171282.html

posted @ 2022-02-12 14:31  层楼  阅读(229)  评论(0)    收藏  举报