头文件和.a(库文件不匹配)导致虚函数错位,进而导致的bug

来源:互联网 发布:01年总决赛艾弗森数据 编辑:程序博客网 时间:2024/03/29 19:10

头文件和.a(库文件不匹配)导致虚函数错位,进而导致的bug

该bug的快速定位技巧
    bt查看函数调用堆栈. 如果堆栈的顺序错乱, 则可能是虚函数表错乱导致.
    本例中, GetTPLContent 的上层很明显不应该是 DealInput, 因为DealInput没有调用GetTPLContent

故障回顾:

    某日遇到一个奇葩bug, 进程core, 查看堆栈, core的位置没有任何异常, 在一个不可能出问题的地方出错了. 只根据core文件在生产环境无法排查.
    在开发机生把cgi编译成可执行文件, 用gdb跟踪, 发现在 单步跟进 BeforeProcess() 函数时, 却意外的跳到了 GetTPLContent()函数接下来就是core了.

中断现场如下:

    rogram received signal SIGSEGV, Segmentation fault.
    [Switching to Thread -1211028832 (LWP 10060)]
    0xb7ef2422 in std::string::compare () from /usr/lib/libstdc++.so.6
    (gdb) bt
    #0  0xb7ef2422 in std::string::compare () from /usr/lib/libstdc++.so.6
    #1  0x08055e5c in std::operator==<char, std::char_traits<char>, std::allocator<char> > (__lhs=@0xbfcd60ec, __rhs=0x82a080e "json")
        at /usr/include/c++/4.1.0/bits/basic_string.h:2163
    #2  0x0804eea0 in CMyCGI::GetTPLContent (this=0xbfcd505c) at src/mb_get_app_friends.cpp:165
    #3  0x08260eb0 in COpenAPIBase::DealInput (this=0xbfcd4128, params=@0xbfcd505c) at src/openapi_base.cpp:239
    #4  0x0821b9af in CCGIEx::Run (this=0xbfcd4128, pszTemplateFile=0x0) at cgiex.cpp:248
    #5  0x08051205 in main () at src/mb_get_app_friends.cpp:386

把栈帧定位到COpenAPIBase::DealInput上

    (gdb) frame 3
    #3  0x08260eb0 in COpenAPIBase::DealInput (this=0xbfcd4128, params=@0xbfcd505c) at src/openapi_base.cpp:239
    239     BeforeProcess(params);
    (gdb) l
    234     //把参数转化成值
    235     TransParams(params);
    236     //填充出authinfo
    237     FillOpenAPIInfo();
    238 
    239     BeforeProcess(params);
    240     m_mod_ret = Process(params);
    241     AfterProcess(params);
    242     return true;
    243 }

查看汇编

    (gdb) disassemble
    Dump of assembler code for function _ZN12COpenAPIBase9DealInputERSt3mapISsSsSt4lessISsESaISt4pairIKSsSsEEE:
    0x08260e76 <_ZN12COpenAPIBase9DealInputERSt3mapISsSsSt4lessISsESaISt4pairIKSsSsEEE+0>: push   %ebp
    0x08260e77 <_ZN12COpenAPIBase9DealInputERSt3mapISsSsSt4lessISsESaISt4pairIKSsSsEEE+1>: mov    %esp,%ebp
    0x08260e79 <_ZN12COpenAPIBase9DealInputERSt3mapISsSsSt4lessISsESaISt4pairIKSsSsEEE+3>: push   %esi
    0x08260e7a <_ZN12COpenAPIBase9DealInputERSt3mapISsSsSt4lessISsESaISt4pairIKSsSsEEE+4>: push   %ebx
    0x08260e7b <_ZN12COpenAPIBase9DealInputERSt3mapISsSsSt4lessISsESaISt4pairIKSsSsEEE+5>: sub    $0x10,%esp
    0x08260e7e <_ZN12COpenAPIBase9DealInputERSt3mapISsSsSt4lessISsESaISt4pairIKSsSsEEE+8>: mov    0x8(%ebp),%ebx
    0x08260e81 <_ZN12COpenAPIBase9DealInputERSt3mapISsSsSt4lessISsESaISt4pairIKSsSsEEE+11>: mov    0xc(%ebp),%esi
    0x08260e84 <_ZN12COpenAPIBase9DealInputERSt3mapISsSsSt4lessISsESaISt4pairIKSsSsEEE+14>: mov    (%ebx),%eax
    0x08260e86 <_ZN12COpenAPIBase9DealInputERSt3mapISsSsSt4lessISsESaISt4pairIKSsSsEEE+16>: mov    %esi,0x4(%esp)
    0x08260e8a <_ZN12COpenAPIBase9DealInputERSt3mapISsSsSt4lessISsESaISt4pairIKSsSsEEE+20>: mov    %ebx,(%esp)
    0x08260e8d <_ZN12COpenAPIBase9DealInputERSt3mapISsSsSt4lessISsESaISt4pairIKSsSsEEE+23>: call   *0x10(%eax) // 虚函数 GetDftParams
    0x08260e90 <_ZN12COpenAPIBase9DealInputERSt3mapISsSsSt4lessISsESaISt4pairIKSsSsEEE+26>: mov    %esi,0x4(%esp)
    0x08260e94 <_ZN12COpenAPIBase9DealInputERSt3mapISsSsSt4lessISsESaISt4pairIKSsSsEEE+30>: mov    %ebx,(%esp)
    0x08260e97 <_ZN12COpenAPIBase9DealInputERSt3mapISsSsSt4lessISsESaISt4pairIKSsSsEEE+33>: call   0x8260684 <_ZN12COpenAPIBase11TransParamsERSt3mapISsSsSt4lessISsESaISt4pairIKSsSsEEE>
    0x08260e9c <_ZN12COpenAPIBase9DealInputERSt3mapISsSsSt4lessISsESaISt4pairIKSsSsEEE+38>: mov    %ebx,(%esp)
    0x08260e9f <_ZN12COpenAPIBase9DealInputERSt3mapISsSsSt4lessISsESaISt4pairIKSsSsEEE+41>: call   0x825fbcc <_ZN12COpenAPIBase15FillOpenAPIInfoEv>
    0x08260ea4 <_ZN12COpenAPIBase9DealInputERSt3mapISsSsSt4lessISsESaISt4pairIKSsSsEEE+46>: mov    (%ebx),%eax
    0x08260ea6 <_ZN12COpenAPIBase9DealInputERSt3mapISsSsSt4lessISsESaISt4pairIKSsSsEEE+48>: mov    %esi,0x4(%esp)
    0x08260eaa <_ZN12COpenAPIBase9DealInputERSt3mapISsSsSt4lessISsESaISt4pairIKSsSsEEE+52>: mov    %ebx,(%esp)
    0x08260ead <_ZN12COpenAPIBase9DealInputERSt3mapISsSsSt4lessISsESaISt4pairIKSsSsEEE+55>: call   *0x18(%eax)  // 虚函数 BeforeProcess
    0x08260eb0 <_ZN12COpenAPIBase9DealInputERSt3mapISsSsSt4lessISsESaISt4pairIKSsSsEEE+58>: mov    (%ebx),%eax
    0x08260eb2 <_ZN12COpenAPIBase9DealInputERSt3mapISsSsSt4lessISsESaISt4pairIKSsSsEEE+60>: mov    %esi,0x4(%esp)
    0x08260eb6 <_ZN12COpenAPIBase9DealInputERSt3mapISsSsSt4lessISsESaISt4pairIKSsSsEEE+64>: mov    %ebx,(%esp)
    0x08260eb9 <_ZN12COpenAPIBase9DealInputERSt3mapISsSsSt4lessISsESaISt4pairIKSsSsEEE+67>: call   *0x20(%eax)   // 虚函数 Process
    0x08260ebc <_ZN12COpenAPIBase9DealInputERSt3mapISsSsSt4lessISsESaISt4pairIKSsSsEEE+70>: mov    %eax,0xf94(%ebx)
    0x08260ec2 <_ZN12COpenAPIBase9DealInputERSt3mapISsSsSt4lessISsESaISt4pairIKSsSsEEE+76>: mov    (%ebx),%eax
    0x08260ec4 <_ZN12COpenAPIBase9DealInputERSt3mapISsSsSt4lessISsESaISt4pairIKSsSsEEE+78>: mov    %esi,0x4(%esp)
    0x08260ec8 <_ZN12COpenAPIBase9DealInputERSt3mapISsSsSt4lessISsESaISt4pairIKSsSsEEE+82>: mov    %ebx,(%esp)
    0x08260ecb <_ZN12COpenAPIBase9DealInputERSt3mapISsSsSt4lessISsESaISt4pairIKSsSsEEE+85>: call   *0x1c(%eax)  // 虚函数 AfterProcess
    0x08260ece <_ZN12COpenAPIBase9DealInputERSt3mapISsSsSt4lessISsESaISt4pairIKSsSsEEE+88>: mov    $0x1,%eax
    0x08260ed3 <_ZN12COpenAPIBase9DealInputERSt3mapISsSsSt4lessISsESaISt4pairIKSsSsEEE+93>: add    $0x10,%esp
    0x08260ed6 <_ZN12COpenAPIBase9DealInputERSt3mapISsSsSt4lessISsESaISt4pairIKSsSsEEE+96>: pop    %ebx
    0x08260ed7 <_ZN12COpenAPIBase9DealInputERSt3mapISsSsSt4lessISsESaISt4pairIKSsSsEEE+97>: pop    %esi
    0x08260ed8 <_ZN12COpenAPIBase9DealInputERSt3mapISsSsSt4lessISsESaISt4pairIKSsSsEEE+98>: pop    %ebp
    0x08260ed9 <_ZN12COpenAPIBase9DealInputERSt3mapISsSsSt4lessISsESaISt4pairIKSsSsEEE+99>: ret

    这个时候, 如果嗅觉敏锐就已经可以看到问题了, TransParams 在头文件里是个虚函数, 理论上是无法在反汇编里直接看到函数符号的 (因为虚函数的具体的地址, 只有在运行时才能决定)

进一步验证
   单步执行时, 当执行到BeforeProcess时, s跳进去, 代码却跳到 GetTPLContent, 因此严重怀疑是 我使用的头文件 和 .a库不匹配导致.
   头文件更新了, .a库却没有更新, 如果头文件中新增虚函数, 将导致虚函数表中各个虚函数的位置发生改变, 而.a却使用旧的虚函数表!

   重新编译.a库后bug消失.

 

这个bug让我想起了以前在windows环境下遭遇的著名的"dll地狱"bug, 都是由于虚函数表错位导致的.

 

原创粉丝点击