• 嵌入式系统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"相吻合。

  • 相关阅读:
    C++ set简介及简单应用
    windows下安装scrapy报错:building 'twisted.test.raiser' extension error: Microsoft Visual C++ 14.0 is required.
    jsp调用Python脚本存取文件
    mysql触发器问题
    javascript调用alert()
    jsp调用Python
    注意细节,注意细节,注意细节
    pandas读取csv文件报错
    [kuangbin带你飞]专题四 最短路练习
    计算机网络之网络应用(应用层)上
  • 原文地址:https://www.cnblogs.com/clover-toeic/p/3980721.html
Copyright © 2020-2023  润新知