頭文件和.a(庫文件不匹配)導致虛函數錯位,進而導致的bug

頭文件和.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, 都是由於虛函數表錯位導致的.

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章