青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

loop_in_codes

低調做技術__歡迎移步我的獨立博客 codemaro.com 微博 kevinlynx

記一次堆棧平衡錯誤

最近在一個使用Visual Studio開發的C++程序中,出現了如下錯誤:

Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention.

這個錯誤主要指的就是函數調用堆棧不平衡。在C/C++程序中,調用一個函數前會保存當前堆棧信息,目標函數返回后會把堆棧恢復到調用前的狀態。函數的參數、局部變量會影響堆棧。而函數堆棧不平衡,一般是因為函數調用方式和目標函數定義方式不一致導致,例如:

void __stdcall func(int a) {
}

int main(int argc, char* argv[]) {
    typedef void (*funcptr)(int);
    funcptr ptr = (funcptr) func;
    ptr(1); // 返回后導致堆棧不平衡
    return 0;
}

__stdcall修飾的函數,其函數參數的出棧由被調用者自己完成,而__cdecl,也就是C/C++函數的默認調用約定,則是調用者完成參數出棧。

Visual Studio在debug模式下會在我們的代碼中加入不少檢查代碼,例如以上代碼對應的匯編中,就會增加一個檢查堆棧是否平衡的函數調用,當出現問題時,就會出現提示Run-Time Check Failure...這樣的錯誤對話框:

call dword ptr [ptr]  ; ptr(1)
add  esp,4  ; cdecl方式,調用者清除參數
cmp  esi,esp  
call @ILT+1345(__RTC_CheckEsp) (0B01546h) ; 檢查堆棧是否平衡

但是我們的程序不是這種低級錯誤。我們調用的函數是放在dll中的,調用約定顯示定義為__stdcall,函數聲明和實現一致。大致的結構如下:

IParser *parser = CreateParser();
parser->Begin();
...
...
parser->End();
parser->Release(); // 返回后導致堆棧不平衡

IParser的實現在一個dll里,這反而是一個誤導人的信息。parser->Release返回后,堆棧不平衡,并且僅僅少了一個字節。一個字節怎么來的?

解決這個問題主要的手段就是跟反匯編,在關鍵位置查看寄存器和堆棧的內容。編譯器生成的代碼是正確的,而我們自己的代碼乍看上去也沒問題。最后甚至使用最傻逼的調試手段–逐行語句注釋查錯。

具體查錯過程就不細說了。解決問題往往需要更多的冷靜,和清晰的思路。最終我使用的方法是,在進入Release之前記錄堆棧指針的值,堆棧指針的值會被壓入堆棧,以在函數返回后從堆棧彈出,恢復堆棧指針。Release的實現很簡單,就是刪除一個Parser這個對象,但這個對象的析構會導致很多其他對象被析構。我就逐層地檢查,是在哪個函數里改變了堆棧里的內容。

理論上,函數本身是操作不到調用者的堆棧的。而現在看來,確實是被調用函數,也就是Release改寫了調用者的堆棧內容。要改變堆棧的內容,只有通過局部變量的地址才能做到。

最終,我發現在調用完以下函數后,我跟蹤的堆棧地址內容發生了改變:

call llvm::RefCountedBase<clang::TargetOptions>::Release (10331117h)

因為注意到TargetOptions這個字眼,想起了在parser->Begin里有涉及到這個類的使用,類似于:

TargetOptions TO;
...
TargetInfo *TI = TargetInfo::CreateTargetInfo(m_inst.getDiagnostics(), TO);

這部分初始化代碼,是直接從網上復制的,因為并不影響主要邏輯,所以從來沒對這塊代碼深究。查看CreateTargetInfo的源碼,發現這個函數將TO這個局部變量的地址保存了下來

而在Release中,則會對這個保存的臨時變量進行刪除操作,形如:

void Delete() const {
  assert (ref_cnt > 0 && "Reference count is already zero.");
  if (--ref_cnt == 0) delete static_cast<const Derived*>(this);
}

但是,問題并不在于對一個局部變量地址進行deletedelete在調試模式下是做了內存檢測的,那會導致一種斷言。

TargetOptions包含了ref_cnt這個成員。當出了Begin作用域后,parser保存的TargetOptions的地址,指向的內容(堆棧)發生了改變,也就是ref_cnt這個成員變量的值不再正常。由于一些巧合,主要是代碼中各個局部變量、函數調用順序、函數參數個數(曾嘗試去除Begin的參數,可以避免錯誤提示),導致在調用Release前堆棧指針恰好等于之前保存的TargetOptions的地址。注意,之前保存的TargetOptions的地址,和調用Release前的堆棧指針值相同了。

而在TargetOptionsDelete函數中,進行了--ref_cnt,這個變量是TargetOptions的第一個成員,它的減1,也就導致了堆棧內容的改變。

至此,整個來龍去脈算是摸清。

posted on 2013-08-15 23:01 Kevin Lynx 閱讀(5802) 評論(1)  編輯 收藏 引用 所屬分類: c/c++

評論

# re: 記一次堆棧平衡錯誤 2013-08-16 12:05 小二郎健康燈

終于找到一個專業的C++博客了  回復  更多評論   

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            久久全国免费视频| 精品999在线观看| 午夜一区不卡| 欧美亚洲在线| 久久日韩精品| 欧美激情在线观看| 欧美区在线播放| 国产精品久久久久999| 国产精品成人一区二区| 国产嫩草一区二区三区在线观看| 国产欧亚日韩视频| 亚洲国产精品久久久久秋霞不卡| 亚洲国产精品一区二区www在线 | 亚洲视频在线免费观看| 亚洲午夜一区| 久久亚洲春色中文字幕久久久| 欧美成人一区二区三区片免费| 欧美高清在线| 国产性猛交xxxx免费看久久| 91久久久久久久久久久久久| 午夜精品成人在线| 欧美国产专区| 性刺激综合网| 欧美伦理视频网站| 国产一区成人| 在线亚洲欧美| 欧美国产日韩视频| 欧美在线免费播放| 国产精品免费在线| 这里只有精品丝袜| 欧美+亚洲+精品+三区| 亚洲中字在线| 欧美色视频在线| 亚洲日本在线观看| 久久久精品tv| 亚洲午夜黄色| 欧美日韩视频在线一区二区观看视频| 国产亚洲精品久久久久久| 一本色道久久综合狠狠躁篇怎么玩 | 亚洲精品乱码久久久久久日本蜜臀| 亚洲第一区色| 久久久久久69| 国产美女精品免费电影| 亚洲视频网在线直播| 亚洲国产日韩在线一区模特| 久久精品国产99国产精品澳门| 国产精品免费视频xxxx| 亚洲视频在线播放| 日韩视频三区| 欧美三级电影一区| 一区二区三区欧美成人| 亚洲伦理精品| 欧美片在线播放| 亚洲伦理在线免费看| 亚洲国产另类精品专区| 女人香蕉久久**毛片精品| 亚洲国产免费| 亚洲福利视频一区二区| 欧美不卡一区| 99在线热播精品免费| 亚洲精华国产欧美| 欧美日韩卡一卡二| 香蕉久久夜色精品国产| 亚洲欧美激情视频| 国产一区二区观看| 欧美激情第8页| 欧美日韩精品欧美日韩精品| 亚洲午夜视频在线| 亚洲视频免费观看| 国产欧美日韩在线观看| 久久精品国产亚洲一区二区| 久久国产精品久久w女人spa| 激情综合在线| 亚洲人成人一区二区在线观看| 欧美精品三级日韩久久| 妖精视频成人观看www| 夜夜嗨一区二区| 国产午夜精品一区二区三区视频| 久久看片网站| 欧美日韩成人综合天天影院| 亚洲欧美日韩精品久久| 欧美在线黄色| 99国产精品视频免费观看一公开| 在线亚洲精品福利网址导航| 国产欧美精品xxxx另类| 免费亚洲一区二区| 欧美日韩亚洲一区二区三区在线观看 | 欧美色欧美亚洲另类二区| 欧美一区二区在线| 麻豆9191精品国产| 亚洲欧美福利一区二区| 久久久www| 一本久久综合亚洲鲁鲁五月天| 最新国产成人av网站网址麻豆| 久久精品国产精品亚洲| 妖精视频成人观看www| 国产欧美日本在线| 亚洲高清不卡在线观看| 国产美女精品视频| 最新亚洲电影| 国内精品久久久| 亚洲毛片一区| 在线日韩中文| 亚洲综合第一页| 亚洲免费高清视频| 欧美伊人精品成人久久综合97| 99视频在线观看一区三区| 欧美在线免费观看| 香蕉久久一区二区不卡无毒影院| 欧美高清日韩| 免费av成人在线| 国产欧美激情| 亚洲素人在线| 亚洲影院高清在线| 欧美日韩99| 亚洲福利在线观看| 激情婷婷欧美| 久久av二区| 欧美影院视频| 国产精品美女久久久久av超清 | 久久久久久夜精品精品免费| 欧美日韩伦理在线| 亚洲精品亚洲人成人网| 亚洲国产女人aaa毛片在线| 久久久蜜桃精品| 久久久人成影片一区二区三区观看| 国产精品久久久久婷婷| 一本大道久久a久久精品综合 | 国产亚洲欧美一区| 亚洲综合导航| 欧美一区二区免费视频| 国产精品久久久久久久久久尿| 亚洲人久久久| 一本久久a久久免费精品不卡| 欧美精品xxxxbbbb| 99riav国产精品| 一区二区日韩欧美| 欧美午夜一区| 亚洲午夜91| 久久久99免费视频| 好吊成人免视频| 久久男女视频| 亚洲国产二区| 中文精品视频| 国产日本欧美一区二区| 久久av一区二区三区| 免费成人在线观看视频| 亚洲国产精品毛片| 欧美激情一区在线观看| 日韩一区二区免费高清| 欧美一级大片在线免费观看| 国产日韩高清一区二区三区在线| 欧美亚洲视频在线观看| 欧美jizz19性欧美| 在线亚洲成人| 久久精品91久久久久久再现| 伊人婷婷久久| 欧美激情1区2区| 中文成人激情娱乐网| 欧美专区中文字幕| 亚洲国产精品成人精品| 欧美日韩在线一区| 久久精品国产96久久久香蕉| 欧美高清视频在线观看| 一级日韩一区在线观看| 国产精品一区二区三区四区| 欧美一区二区视频观看视频| 亚洲国产精品va在线看黑人动漫| 亚洲一区二区三区免费视频| 国产日韩精品久久| 欧美成人精品在线| 香蕉国产精品偷在线观看不卡| 欧美国产三级| 亚洲一区二区三区在线看| 国产中文一区二区三区| 欧美日本亚洲| 久久大逼视频| 夜夜爽99久久国产综合精品女不卡| 久久精品久久综合| 日韩亚洲视频在线| 国产视频在线观看一区二区三区| 美女脱光内衣内裤视频久久影院 | 一区二区三区在线免费观看 | 国产一区二区中文字幕免费看| 久久亚洲精品一区二区| 中日韩高清电影网| 亚洲大胆美女视频| 欧美一区二区三区视频在线观看 | 久久精品国产久精国产思思 | 欧美暴力喷水在线| 午夜亚洲激情| 这里只有精品丝袜| 亚洲区第一页| 激情综合网激情| 国产精品自在线| 国产精品h在线观看| 欧美精品久久久久a| 欧美+亚洲+精品+三区| 久久av一区二区三区| 午夜精品剧场|