zoukankan      html  css  js  c++  java
  • 嵌入式系统C编程之堆栈回溯(二)

    前言

         本文作为《嵌入式系统C编程之堆栈回溯》的补充版。文中涉及的代码运行环境如下:

     

     

     

    一  异常信号

         信号就是软件中断,用于向正在运行的程序(进程)发送有关异步事件发生的信息。Linux应用程序发生异常时,操作系统会产生相应的信号。硬件检测到异常(非法指令、对无效的内存引用等)时也会通知内核,内核将其转换为适当的信号并发给该异常发生时正在运行的进程。 此外,进程可将信号发送给另一进程或进程组(调用kill函数),或向自身发送信号(调用raise函数)。

         系统中可产生并发送多种类型的信号。在Linux环境中可通过kill -l命令查看完整的信号清单。

         若进程既不忽略也未指定信号处理函数,则操作系统将对信号执行默认动作(通常是终止该进程)。可能导致进程终止的常见信号如下:

    • SIGSEGV:段错误。当进程试图访问未分配给自己的内存,或对只读内存进行写操作时会产生该信号,常见编程错误有数组越界、空指针引用等。
    • SIGPIPE:管道破裂。当采用管道进行进程间通信时,若读进程未打开管道或已终止就往管道写,则写进程会收到该信号。若类型为SOCK_STREAM的套接字已不再连接,则进程写到该套接字时也产生此信号。
    • SIGFPE:致命的算术运算异常,如浮点或整数溢出、除0等。
    • SIGBUS:由实现定义的硬件故障。其原因有:1)无效的内存地址对齐(alignment),如某些非IA-32架构要求访问四字节整数时其地址必须为4的倍数;2)不存在的物理地址,如访问映射存储区中已不存在的某个部分;3)特定的硬件故障。
    • SIGILL:非法指令。当可执行文件本身出错、试图执行数据段、堆栈出错时可能产生此信号。

         应用程序异常终止时,常见的故障定位方法有:1)添加打印语句二分查找,效率低下;2)gdb调试,不适用于没有gdb的环境和软件发布后;3)分析core文件。

         对于导致进程终止的信号,系统默认处理是打印出错信息,并在进程当前工作目录下创建core或core.pid文件以复制该进程的存储映像。若未生成core文件,可查看ulimit -c命令结果。当结果为0时通过ulimit -c <Size>或ulimit -c unlimited命令设置core文件大小(不为0时才会产生core文件)。

         在编译程序时加上-g –rdynamic选项,当进程异常终止时即可运行gdb ./<Exec> core命令,定位到出错的C语句。若程序运行环境无法运行gdb,可将core文件、程序可执行文件拷贝到支持gdb的主机上调试。

         注意,某些异常信号(如SIGPIPE)不会产生core文件,而且有时core文件不能正常调试。

         更好的方法是捕获到异常信号后,在信号处理函数里进行堆栈回溯,即《嵌入式系统C编程之堆栈回溯》一文所述。

     

     

    二  特殊的堆栈回溯

         本节主要补充两种堆栈回溯的特殊情况。

    2.1 未开启-rdynamic -ldl选项

         未开启-rdynamic选项时(经测-ldl选项可有可无),堆栈回溯将无法显示函数名。

         例如,将Func1()函数稍作修改:

    1 VOID Func1(VOID){
    2     //SHOW_STACK();
    3     CHAR *p = NULL;
    4     *p = 0;
    5     return;
    6 }

         编译链接时开启-g选项,关闭-rdynamic和-ldl选项,执行结果如下:

     1 Start of Stack Trace>>>>>>>>>>>>>>>>>>>>>>>>>>
     2 Process (5663) receive signal 11
     3 <Signal Information>:
     4         SigNo:     11(SIGSEGV)
     5         ErrNo:     0 (Success)
     6         SigCode:   1 
     7         Raised at: (nil)[Unreliable]
     8 <Register Content>: 
     9         00000033 00000000 0000007b 0000007b 
    10         00000000 00535ca0 bfda20b8 bfda20a8 
    11         0067eff4 00000000 bfda205c 00000000 
    12         0000000e 00000006 0804a1c1 00000073 
    13         00010282 bfda20a8 0000007b 
    14 <Stack Trace(Customized)>:
    15         [ 1] (./OmciExec) [0x0804a1c1] (<STATIC>)+0x804a1c1
    16         [ 2] (./OmciExec) [0x0804a1d1] (<STATIC>)+0x804a1d1
    17         [ 3] (./OmciExec) [0x0804a1f2] (<STATIC>)+0x804a1fb
    18         [ 4] (./OmciExec) [0x0804e348] (<STATIC>)+0x804e35c
    19         [ 5] (/lib/libc.so.6) [0x00552e9c] (__libc_start_main)+0xdc
    20         [ 6] (./OmciExec) [0x08049001] (<STATIC>)+0x8049001
    21 End of Stack Trace<<<<<<<<<<<<<<<<<<<<<<<<<<<<

         其中,<STATIC>应作Unknown理解,而非静态函数。 

         由执行结果可知,错误发生在地址0x0804a1c1处。要定位出错代码的位置,需对该程序的可执行文件进行反汇编。反汇编命令为objdump -dS --start-address 0x804a1c1 OmciExec > dump,表示使用objdump工具反汇编可执行文件OmciExec,并将从出错地址开始的结果写入dump文件。指定参数-S(隐含-d参数)时,将尽可能地反汇编出源代码,通常需结合-g编译选项。

         打开dump文件,截取地址0x0804a1c1附近的指令片段如下:

     1 VOID Func1(VOID){
     2     //SHOW_STACK();
     3     CHAR *p = NULL;
     4     *p = 0;
     5  804a1c1:    c6 00 00                 movb   $0x0,(%eax)
     6     return;
     7 }
     8 VOID Func2(VOID){
     9     Func1();
    10  804a1cc:    e8 e0 ff ff ff           call   804a1b1 <Func1>
    11     printf("%s
    ", 0x123);
    12  804a1d1:    c7 44 24 04 23 01 00     movl   $0x123,0x4(%esp)
    13     return;
    14 }
    15 VOID BtrTest(VOID){
    16     Func2();
    17  804a1ed:    e8 d4 ff ff ff           call   804a1c6 <Func2>
    18     printf("%d
    ", 5/0);
    19  804a1f2:    ba 05 00 00 00           mov    $0x5,%edx
    20     return;
    21 }
    22 INT32S main(VOID)
    23 {
    24     BtrTest();
    25  804e343:    e8 9f be ff ff           call   804a1e7 <BtrTest>
    26     GlbOverrunTest();
    27  804e348:    e8 75 fe ff ff           call   804e1c2 <GlbOverrunTest>

         可见,函数调用顺序为Func1()->Func2()->BtrTest()->main()。 

         未开启-g选项时,dump结果只显示函数名和指令地址。但本例中发生的是段错误,通过重点排查出错函数内的空指针引用和数组越界代码,也可轻松定位出错语句。

    2.2 忽略帧基指针

         若忽略帧基指针(开启-fomit-frame-pointer),将无法正确回溯堆栈内容。此时,可借助/proc/pid/maps文件所提供的进程虚拟地址空间信息和栈顶指针SP对堆栈进行尝试性回溯。

         通过cat /proc/<pid>/maps 命令可查看指定进程的内存空间分布,如:

    1 004c0000-0057e000 rwxp 00000000 00:00 0          [heap]
    2 2aaa8000-2aaad000 r-xp 00000000 1f:06 75         /lib/ld-uClibc-0.9.29.so
    3 2aaad000-2aaaf000 rw-p 00000000 00:00 0
    4 2aabc000-2aabd000 r--p 00004000 1f:06 75         /lib/ld-uClibc-0.9.29.so
    5 2aabd000-2aabe000 rw-p 00005000 1f:06 75         /lib/ld-uClibc-0.9.29.so
    6 … … … …
    7 7ff9b000-7ffb0000 rwxp 00000000 00:00 0          [stack]

         显示内容共有6列,分别为: 

         1) 地址(address):虚拟内存段的起始和终止地址,即该文件所占用的地址空间。

         2) 权限(permission):虚拟内存段的访问权限(r为读,w为写,x为执行,s为共享,p为私有)。

         3) 偏移量:虚拟内存段在映像文件中的偏移量。

         4) 设备(device):映像文件的主设备号和次设备号(可通过cat /proc/devices查看详情)。

         5) 节点(inode):映像文件的节点号(0表示没有节点与内存相对应);

         6) 路径(name): 映像文件的路径名

         通常权限为r-xp时对应代码段(正文段),权限为rw-p时对应数据段。

         因此,忽略帧基指针时,函数堆栈回溯的步骤如下(以Intel x86架构为例):

         1) 读取/proc/<pid>/maps文件,记录映射到进程虚拟地址空间的可执行代码段的起止位置;

         2) 从当前栈顶指针SP出发,向高地址依次取出一个整型值。若该值位于上步所计算的某个地址区间中,则输出该值(可能是函数栈帧中的返回地址,即调用指令的下条地址);

         3) 循环步骤2,直至读取完指定数目的整型值。

         本实现首先需要增加几个宏定义,如下:

     1 #if defined(REG_RIP)
     2     #define REG_IP     REG_RIP   //指令指针(保存返回地址)
     3     #define REG_BP     REG_RBP   //帧基指针
     4     #define REG_SP     REG_RSP   //栈顶指针
     5     #define REG_FMT    "%016lx"
     6 #elif defined(REG_EIP)
     7     #define REG_IP     REG_EIP
     8     #define REG_BP     REG_EBP
     9     #define REG_SP     REG_ESP
    10     #define REG_FMT    "%08x"
    11 #else
    12     #warning "Neither REG_RIP nor REG_EIP is defined!"
    13     #define REG_FMT    "%08x"
    14 #endif
    15 
    16 #ifndef TASK_SIZE       //用户进程空间大小(基于该值可确定堆栈底部)
    17     #define TASK_SIZE        (0xbf000000UL)
    18 #endif
    19 
    20 #ifndef MAPS_SEG_NUM    //解析'/proc/pid/maps'结果时的最大段数(条目数)
    21     #define MAPS_SEG_NUM     30
    22 #endif

         此处TASK_SIZE定义为PAGE_OFFSET(0xc0000000)向低地址偏移16M处,即用户进程空间可用的虚拟内存范围为0~0xbf000000。

         然后定义ShowStackContent()函数如下:

     1 /******************************************************************************
     2 * 函数名称:  ShowStackContent
     3 * 功能说明:  显示堆栈内容
     4 ******************************************************************************/
     5 static VOID ShowStackContent(INT32U dwStkPtr)
     6 {
     7     fprintf(gpStraceFd, "<Current Thread Maps>:
    ");    
     8     CHAR szMapsCmd[sizeof("/proc/65535/maps")] = {0};
     9     sprintf(szMapsCmd, "/proc/%d/maps", getpid());
    10     FILE *pFile = fopen(szMapsCmd, "r");
    11     if(NULL == pFile)
    12     {
    13         fprintf(gpStraceFd, "Open File '%s' Error(%s)!
    ", szMapsCmd, strerror(errno));
    14         return;
    15     }
    16 
    17     INT32U dwSegIdx = 0, dwSegNum = 0;
    18     INT32U dwStartAddr = 0, dwEndAddr = 0;
    19     INT32U aAddrSeg[MAPS_SEG_NUM*2] = {0};
    20     CHAR szMapsBuf[256] = {0};
    21     while(fgets(szMapsBuf, sizeof(szMapsBuf)-1, pFile) != NULL)
    22     {  
    23         CHAR cAccess;
    24         CHAR szMisc[128]; //杂项,不必关注
    25         INT32S dwRet = sscanf(szMapsBuf, "%08x-%08x %*c%*c%c %[^
    ]%*c", &dwStartAddr, &dwEndAddr, &cAccess, szMisc);
    26         if(-1 == dwRet || 0 == dwRet)
    27             break;
    28 
    29         if(cAccess == 'x'/*Executable*/ &&  dwSegIdx < MAPS_SEG_NUM*2 && dwEndAddr != TASK_SIZE)
    30         {
    31             fprintf(gpStraceFd, "	%s", szMapsBuf);
    32             aAddrSeg[dwSegIdx++] = dwStartAddr;
    33             aAddrSeg[dwSegIdx++] = dwEndAddr;
    34         }
    35     }
    36     dwSegNum = dwSegIdx;
    37     fclose(pFile);
    38 
    39     //从当前ESP出发检查高地址处的dwDwordNum个堆栈单位(双字)
    40     INT32U dwDwordNum = ((TASK_SIZE-dwStkPtr) > 512) ? 512 : (TASK_SIZE-dwStkPtr);
    41     dwDwordNum >>= 2;
    42     fprintf(gpStraceFd, "<Possible Call Trace>:
    	");
    43 
    44     INT32U dwIdx = 0, dwIdx2 = 0;
    45     for(; dwIdx < dwDwordNum; dwIdx++)
    46     {
    47         INT32U dwStkCont = *((INT32U*)dwStkPtr + dwIdx);
    48         for(dwSegIdx = 0; dwSegIdx < dwSegNum; dwSegIdx+=2)
    49         {
    50             if(dwStkCont >= aAddrSeg[dwSegIdx] && dwStkCont <= aAddrSeg[dwSegIdx+1])
    51             {
    52                 fprintf(gpStraceFd, "[%8x]  ", dwStkCont);
    53                 if(0 == ((++dwIdx2)%4)) //每行输出4个堆栈内容
    54                    fprintf(gpStraceFd, "
    	");
    55                 break;
    56             }
    57         }
    58     }
    59     fprintf(gpStraceFd, "
    ");    
    60     return;
    61 }

         SigHandler()函数输出Customized堆栈回溯(仅首行有参考意义)后,再调用ShowStackContent()函数:

    ShowStackContent(ptContext->uc_mcontext.gregs[REG_SP]);

         以2.1节Func1()函数为例。编译链接时开启-g和-fomit-frame-pointer选项,可选关闭-rdynamic和-ldl选项,执行结果如下:

     1 Start of Stack Trace>>>>>>>>>>>>>>>>>>>>>>>>>>
     2 Process (15207) receive signal 11
     3 <Signal Information>:
     4         SigNo:     11(SIGSEGV)
     5         ErrNo:     0 (Success)
     6         SigCode:   1 
     7         Raised at: (nil)[Unreliable]
     8 <Register Content>: 
     9         00000033 00000000 0000007b 0000007b 
    10         00000000 00535ca0 bfe17428 bfe1735c 
    11         0067eff4 00000001 bfe173d0 00000000 
    12         0000000e 00000006 0804a373 00000073 
    13         00010286 bfe1735c 0000007b 
    14 <Stack Trace(Customized)>:
    15         [ 1] (./OmciExec) [0x0804a373] (<STATIC>)+0x804a373
    16         [ 2] (./OmciExec) [0x08049001] (<STATIC>)+0x8049001
    17 <Current Thread Maps>:
    18         0051b000-00535000 r-xp 00000000 fd:00 28871142   /lib/ld-2.5.so
    19         00535000-00536000 r-xp 00019000 fd:00 28871142   /lib/ld-2.5.so
    20         00536000-00537000 rwxp 0001a000 fd:00 28871142   /lib/ld-2.5.so
    21         0053d000-0067c000 r-xp 00000000 fd:00 28871143   /lib/libc-2.5.so
    22         0067c000-0067d000 --xp 0013f000 fd:00 28871143   /lib/libc-2.5.so
    23         0067d000-0067f000 r-xp 0013f000 fd:00 28871143   /lib/libc-2.5.so
    24         0067f000-00680000 rwxp 00141000 fd:00 28871143   /lib/libc-2.5.so
    25         00680000-00683000 rwxp 00680000 00:00 0 
    26         00685000-006aa000 r-xp 00000000 fd:00 28871150   /lib/libm-2.5.so
    27         006aa000-006ab000 r-xp 00024000 fd:00 28871150   /lib/libm-2.5.so
    28         006ab000-006ac000 rwxp 00025000 fd:00 28871150   /lib/libm-2.5.so
    29         006ae000-006b0000 r-xp 00000000 fd:00 28871144   /lib/libdl-2.5.so
    30         006b0000-006b1000 r-xp 00001000 fd:00 28871144   /lib/libdl-2.5.so
    31         006b1000-006b2000 rwxp 00002000 fd:00 28871144   /lib/libdl-2.5.so
    32         006b4000-006c8000 r-xp 00000000 fd:00 28871145   /lib/libpthread-2.5.so
    33         006c8000-006c9000 r-xp 00013000 fd:00 28871145   /lib/libpthread-2.5.so
    34         006c9000-006ca000 rwxp 00014000 fd:00 28871145   /lib/libpthread-2.5.so
    35         006ca000-006cc000 rwxp 006ca000 00:00 0 
    36         00a68000-00a6f000 r-xp 00000000 fd:00 28871146   /lib/librt-2.5.so
    37         00a6f000-00a70000 r-xp 00006000 fd:00 28871146   /lib/librt-2.5.so
    38         00a70000-00a71000 rwxp 00007000 fd:00 28871146   /lib/librt-2.5.so
    39         00b13000-00b42000 r-xp 00000000 fd:00 4096074    /usr/lib/libreadline.so.5.1
    40         00b42000-00b46000 rwxp 0002f000 fd:00 4096074    /usr/lib/libreadline.so.5.1
    41         00b46000-00b47000 rwxp 00b46000 00:00 0 
    42         00d10000-00d11000 r-xp 00d10000 00:00 0          [vdso]
    43         04e6a000-04eaa000 r-xp 00000000 fd:00 22226947   /usr/lib/libncurses.so.5.5
    44         04eaa000-04eb2000 rwxp 00040000 fd:00 22226947   /usr/lib/libncurses.so.5.5
    45         04eb2000-04eb3000 rwxp 04eb2000 00:00 0 
    46         08048000-08052000 r-xp 00000000 08:11 86278170   /sdb1/wangxiaoyuan/linux_test/DCLinkedList/OmciExec
    47 <Possible Call Trace>:
    48         [ 8052000]  [ 804a382]  [ 804a3a2]  [ 804eac1]  
    49         [  67eff4]  [  67d204]  [ 804f2e9]  [  568e25]  
    50         [  67eff4]  [  529600]  [ 804f2d0]  [  552e9c]  
    51         [  535ca0]  [ 804f2d0]  [  552e9c]  [  536810]  
    52         [  67eff4]  [  535ca0]  [  52e4f0]  [  552dcd]  
    53         [  535fc0]  [ 8048fe0]  [ 8049001]  [ 804eaae]  
    54         [ 804f2d0]  [ 804f2c0]  [  529600]  [  53202b]  
    55         [  d10400]  [  d10000]  [ 8048034]  [ 8048fe0]  
    56 
    57 End of Stack Trace<<<<<<<<<<<<<<<<<<<<<<<<<<<<

         通过objdump -dS OmciExec > dump命令反汇编可执行文件OmciExec。 

         打开dump文件,根据<Stack Trace(Customized)>首行的返回地址和<Possible Call Trace>的堆栈内容,分析和摘取位于OmciExec内存段的地址,匹配dump文件中的指令地址(若匹配极有可能为出错代码的下条指令)。

         截取部分指令片段如下:

     1 VOID Func1(VOID){
     2     //SHOW_STACK();
     3     CHAR *p = NULL;
     4     *p = 0;
     5  804a373:    c6 00 00                 movb   $0x0,(%eax)
     6     return;
     7 }
     8 VOID Func2(VOID){
     9     Func1();
    10  804a37d:    e8 e2 ff ff ff           call   804a364 <Func1>
    11     printf("%s
    ", 0x123);
    12  804a382:    c7 44 24 04 23 01 00     movl   $0x123,0x4(%esp)
    13     return;
    14 }
    15 VOID BtrTest(VOID){
    16     Func2();
    17  804a39d:    e8 d8 ff ff ff           call   804a37a <Func2>
    18     printf("%d
    ", 5/0);
    19  804a3a2:    ba 05 00 00 00           mov    $0x5,%edx
    20     return;
    21 }
    22 INT32S main(VOID)
    23 {
    24     BtrTest();
    25  804eabc:    e8 d9 b8 ff ff           call   804a39a <BtrTest>
    26     GlbOverrunTest();
    27  804eac1:    e8 8a fe ff ff           call   804e950 <GlbOverrunTest>

         可见,函数调用顺序为Func1()->Func2()->BtrTest()->main()。 

         注意,当出错语句调用库函数时,本实现很难有效地回溯。例如,删去Func1()函数中对*p的赋值语句,执行结果如下所示:

     1 Start of Stack Trace>>>>>>>>>>>>>>>>>>>>>>>>>>
     2 Process (28854) receive signal 11
     3 <Signal Information>:
     4         SigNo:     11(SIGSEGV)
     5         ErrNo:     0 (Success)
     6         SigCode:   1 
     7         Raised at: 0x123[Unreliable]
     8 <Register Content>: 
     9         00000033 00000000 0000007b 0000007b 
    10         00000123 bfff6004 bfff5fdc bfff59bc 
    11         0067eff4 00579999 00000003 00000123 
    12         0000000e 00000004 005ad1ab 00000073 
    13         00010206 bfff59bc 0000007b 
    14 <Stack Trace(Customized)>:
    15         [ 1] (/lib/libc.so.6) [0x005ad1ab] (strlen)+0x0b
    16         [ 2] (/lib/libc.so.6) [0x00582e83] (_IO_printf)+0x33
    17         [ 3] (./OmciExec) [0x0804a381] (<STATIC>)+0x804a381
    18         [ 4] (./OmciExec) [0x08049001] (<STATIC>)+0x8049001
    19 <Current Thread Maps>:
    20         003bb000-003bc000 r-xp 003bb000 00:00 0          [vdso]
    21         0051b000-00535000 r-xp 00000000 fd:00 28871142   /lib/ld-2.5.so
    22         00535000-00536000 r-xp 00019000 fd:00 28871142   /lib/ld-2.5.so
    23         00536000-00537000 rwxp 0001a000 fd:00 28871142   /lib/ld-2.5.so
    24         0053d000-0067c000 r-xp 00000000 fd:00 28871143   /lib/libc-2.5.so
    25         0067c000-0067d000 --xp 0013f000 fd:00 28871143   /lib/libc-2.5.so
    26         0067d000-0067f000 r-xp 0013f000 fd:00 28871143   /lib/libc-2.5.so
    27         0067f000-00680000 rwxp 00141000 fd:00 28871143   /lib/libc-2.5.so
    28         00680000-00683000 rwxp 00680000 00:00 0 
    29         00685000-006aa000 r-xp 00000000 fd:00 28871150   /lib/libm-2.5.so
    30         006aa000-006ab000 r-xp 00024000 fd:00 28871150   /lib/libm-2.5.so
    31         006ab000-006ac000 rwxp 00025000 fd:00 28871150   /lib/libm-2.5.so
    32         006ae000-006b0000 r-xp 00000000 fd:00 28871144   /lib/libdl-2.5.so
    33         006b0000-006b1000 r-xp 00001000 fd:00 28871144   /lib/libdl-2.5.so
    34         006b1000-006b2000 rwxp 00002000 fd:00 28871144   /lib/libdl-2.5.so
    35         006b4000-006c8000 r-xp 00000000 fd:00 28871145   /lib/libpthread-2.5.so
    36         006c8000-006c9000 r-xp 00013000 fd:00 28871145   /lib/libpthread-2.5.so
    37         006c9000-006ca000 rwxp 00014000 fd:00 28871145   /lib/libpthread-2.5.so
    38         006ca000-006cc000 rwxp 006ca000 00:00 0 
    39         00a68000-00a6f000 r-xp 00000000 fd:00 28871146   /lib/librt-2.5.so
    40         00a6f000-00a70000 r-xp 00006000 fd:00 28871146   /lib/librt-2.5.so
    41         00a70000-00a71000 rwxp 00007000 fd:00 28871146   /lib/librt-2.5.so
    42         00b13000-00b42000 r-xp 00000000 fd:00 4096074    /usr/lib/libreadline.so.5.1
    43         00b42000-00b46000 rwxp 0002f000 fd:00 4096074    /usr/lib/libreadline.so.5.1
    44         00b46000-00b47000 rwxp 00b46000 00:00 0 
    45         04e6a000-04eaa000 r-xp 00000000 fd:00 22226947   /usr/lib/libncurses.so.5.5
    46         04eaa000-04eb2000 rwxp 00040000 fd:00 22226947   /usr/lib/libncurses.so.5.5
    47         04eb2000-04eb3000 rwxp 04eb2000 00:00 0 
    48         08048000-08052000 r-xp 00000000 08:11 86278170   /sdb1/wangxiaoyuan/linux_test/DCLinkedList/OmciExec
    49 <Possible Call Trace>:
    50         [  57cc0e]  [ 804faba]  [  5245b5]  [  54ecd0]  
    51         [  51b5c6]  [ 80486fe]  [  578f5f]  [  52498d]  
    52         [  528e66]  [  53d1a4]  [  52f5d1]  [  67f554]  
    53         [  540c24]  [  540bf0]  [ 804fabb]  [ 804faba]  
    54         [  529a29]  [  6b7382]  [  53d120]  [  528e49]  
    55         [  540c24]  [  53fad8]  [  6b6c54]  [  549794]  
    56         [  6b4fa8]  [  535fc0]  [  524aa7]  [  6b4fa8]  
    57         [  5367b4]  [  549794]  [ 804faba]  [  535fc0]  
    58         [  5496c4]  [  5278b5]  [  535fc0]  [  6b6c54]  
    59         [  535000]  [  5245b5]  [  54ecd0]  [  6b73aa]  
    60         [  529a29]  [  6b7382]  [  535fc0]  
    61 End of Stack Trace<<<<<<<<<<<<<<<<<<<<<<<<<<<<

         可见,最内层的出错代码位于libc共享库内的strlen函数处。该库编译时未忽略帧基指针,故可正确回溯strlen和_IO_printf(printf别名)函数。但printf函数占用较大的堆栈空间,且<Possible Call Trace>显示的堆栈内容有限,因此无法进一步回溯。 

         由<Stack Trace(Customized)>第三行回溯信息可知,OmciExec内存段代码出错时返回地址为0x0804a381。反汇编可执行文件OmciExec后,截取部分指令片段如下:

    1 VOID Func2(VOID){
    2      Func1();
    3      printf("%s
    ", 0x123);
    4  804a375:    c7 04 24 ba fa 04 08     movl   $0x804faba,(%esp)
    5  804a37c:    e8 cf ea ff ff           call   8048e50 <printf@plt>
    6     return;
    7 }
    8  804a381:    83 c4 0c                 add    $0xc,%esp
    9  804a384:    c3                       ret

         可知,出错代码为printf("%s ", 0x123)语句,这也与<Signal Information>中的" Raised at: 0x123"相吻合。

  • 相关阅读:
    warning: ISO C++ forbids converting a string constant to 'char*' [-Wwrite-strings]
    Windows10+CLion+OpenCV4.5.2开发环境搭建
    Android解决部分机型WebView播放视频全屏按钮灰色无法点击、点击全屏白屏无法播放等问题
    MediaCodec.configure Picture Width(1080) or Height(2163) invalid, should N*2
    tesseract
    Caer -- a friendly API wrapper for OpenCV
    Integrating OpenCV python tool into one SKlearn MNIST example for supporting prediction
    Integrating Hub with one sklearn mnist example
    What is WSGI (Web Server Gateway Interface)?
    Hub --- 机器学习燃料(数据)的仓库
  • 原文地址:https://www.cnblogs.com/clover-toeic/p/3980721.html
Copyright © 2011-2022 走看看