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

loop_in_codes

低調(diào)做技術(shù)__歡迎移步我的獨立博客 codemaro.com 微博 kevinlynx

記一次堆棧平衡錯誤

最近在一個使用Visual Studio開發(fā)的C++程序中,出現(xiàn)了如下錯誤:

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.

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

void __stdcall func(int a) {
}

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

__stdcall修飾的函數(shù),其函數(shù)參數(shù)的出棧由被調(diào)用者自己完成,而__cdecl,也就是C/C++函數(shù)的默認(rèn)調(diào)用約定,則是調(diào)用者完成參數(shù)出棧。

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

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

但是我們的程序不是這種低級錯誤。我們調(diào)用的函數(shù)是放在dll中的,調(diào)用約定顯示定義為__stdcall,函數(shù)聲明和實現(xiàn)一致。大致的結(jié)構(gòu)如下:

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

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

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

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

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

最終,我發(fā)現(xiàn)在調(diào)用完以下函數(shù)后,我跟蹤的堆棧地址內(nèi)容發(fā)生了改變:

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

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

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

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

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

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

但是,問題并不在于對一個局部變量地址進(jìn)行deletedelete在調(diào)試模式下是做了內(nèi)存檢測的,那會導(dǎo)致一種斷言。

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

而在TargetOptionsDelete函數(shù)中,進(jìn)行了--ref_cnt,這個變量是TargetOptions的第一個成員,它的減1,也就導(dǎo)致了堆棧內(nèi)容的改變。

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

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

評論

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

終于找到一個專業(yè)的C++博客了  回復(fù)  更多評論   

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            宅男精品视频| 久久精品国产一区二区电影 | 欧美日韩视频专区在线播放| 国产一区在线观看视频| 亚洲制服少妇| 亚洲色无码播放| 欧美绝品在线观看成人午夜影视| 在线观看日韩av先锋影音电影院| 性欧美长视频| 亚洲自拍偷拍网址| 国产日韩精品一区二区| 欧美一区二区免费| 亚洲一区二区影院| 国产欧美日韩精品在线| 久久国产免费看| 久久激情视频久久| 精品动漫3d一区二区三区| 久久人人爽人人爽| 久久视频一区二区| 亚洲国产一区二区精品专区| 欧美aaa级| 欧美第一黄色网| 亚洲伦理自拍| 日韩一级网站| 国产精品欧美日韩一区| 欧美在线视频观看| 久久国产综合精品| 亚洲欧洲日产国产综合网| 亚洲人精品午夜| 欧美裸体一区二区三区| 亚洲欧美国产日韩天堂区| 午夜精品久久久久久久蜜桃app| 国产精品美女www爽爽爽| 久久高清福利视频| 美女脱光内衣内裤视频久久网站| 亚洲精品一区二区三| 亚洲毛片av在线| 国产美女在线精品免费观看| 久久婷婷久久| 欧美高清一区| 欧美亚洲一区二区三区| 久久久综合香蕉尹人综合网| 亚洲品质自拍| 亚洲欧美日本日韩| 亚洲高清不卡一区| 亚洲无亚洲人成网站77777 | 亚洲在线免费观看| 久久琪琪电影院| 亚洲欧美99| 亚洲免费一级电影| 国产精品黄色| 亚洲欧美国产精品专区久久| 一区二区三区导航| 国产精品午夜在线| 久久精品视频99| 亚洲激情成人网| 亚洲嫩草精品久久| 国产一区二区中文| 免费在线观看日韩欧美| 一区二区三区精品视频| 久久久久久久久岛国免费| 亚洲国产精品成人久久综合一区| 欧美gay视频| 午夜精品网站| 亚洲精品国偷自产在线99热| 欧美在线视频一区二区三区| 亚洲高清自拍| 国产网站欧美日韩免费精品在线观看| 麻豆freexxxx性91精品| 一区二区精品在线| 欧美成人免费在线| 美女诱惑一区| 久久久久久久久久久成人| 99精品国产在热久久下载| 亚洲第一伊人| 又紧又大又爽精品一区二区| 国产精品成人一区二区三区吃奶 | 久久人人97超碰精品888| 亚洲男女毛片无遮挡| 一本色道久久综合亚洲精品高清| 亚洲高清中文字幕| 亚洲娇小video精品| 亚洲一级黄色| 亚洲自啪免费| 久久久91精品国产| 久久久国产精品亚洲一区| 久久成人这里只有精品| 久久九九精品99国产精品| 久久婷婷成人综合色| 欧美成人午夜剧场免费观看| 欧美第一黄色网| 国产精品久久久久9999高清 | 国产在线观看一区| 精品成人乱色一区二区| 91久久精品国产| 亚洲欧美国产日韩天堂区| 欧美一区精品| 亚洲国内欧美| 欧美在线网址| 欧美日韩亚洲国产精品| 国产一区再线| 亚洲欧美在线视频观看| 亚洲国产成人av好男人在线观看| 日韩午夜av电影| 久久久久一区| 国产日韩欧美| 亚洲免费综合| 一区二区三区日韩在线观看 | 欧美性做爰猛烈叫床潮| 韩曰欧美视频免费观看| 亚洲在线不卡| 一区二区三区福利| 欧美激情精品久久久久久黑人 | 国产伦精品一区二区三区高清| 国语精品一区| 久久久噜噜噜久久久| 亚洲男人的天堂在线aⅴ视频| 欧美成人精品h版在线观看| 欧美影院在线| 含羞草久久爱69一区| 久久电影一区| 久久精品二区| 影音先锋国产精品| 欧美顶级大胆免费视频| 久久一区二区三区四区五区| 玉米视频成人免费看| 免费久久久一本精品久久区| 久久精品国产一区二区电影 | 亚洲欧洲日夜超级视频| 欧美二区在线看| 亚洲欧美欧美一区二区三区| 亚洲欧美激情视频在线观看一区二区三区 | 亚洲国产国产亚洲一二三| 美女日韩在线中文字幕| 99视频精品在线| 亚洲一区www| 亚洲国产一区二区三区a毛片| 亚洲国产成人一区| 欧美视频中文字幕| 久久综合成人精品亚洲另类欧美 | 亚洲淫片在线视频| 久久国产精品久久久久久| 亚洲黄一区二区三区| 一本在线高清不卡dvd| 极品少妇一区二区| 中文成人激情娱乐网| 亚洲国产美国国产综合一区二区| 亚洲精品日韩在线| 在线播放精品| 欧美一区二区三区免费观看视频| 日韩亚洲在线| 男人插女人欧美| 国外精品视频| 亚洲综合三区| 亚洲视频专区在线| 欧美成年人视频网站| 久久综合色综合88| 国产日韩欧美中文| 久久av一区二区三区| 久久精品国产在热久久| 国产精品一卡二| 亚洲制服av| 久久精品国产一区二区三| 国产精品视频区| 欧美一区二区成人6969| 老牛影视一区二区三区| 国内一区二区在线视频观看| 亚洲综合精品四区| 久久久国产一区二区| 激情六月婷婷综合| 男女精品网站| 在线一区二区三区四区| 久久久999| 亚洲伦理在线| 国产欧美欧洲在线观看| 久久久999精品| 亚洲欧洲日本在线| 亚洲欧美一区二区三区极速播放 | 亚洲一区二区三区影院| 国产精品亚洲综合| 嫩草成人www欧美| 亚洲一区二区久久| 欧美插天视频在线播放| 亚洲视频在线观看一区| 极品中文字幕一区| 国产精品国码视频| 免费成人在线观看视频| 亚洲尤物在线视频观看| 亚洲清纯自拍| 美女国产一区| 久久精品欧美日韩| 亚洲免费在线观看| 亚洲免费av片| 在线视频国产日韩| 国产一区二区三区久久精品| 欧美日本高清视频| 欧美久久久久久蜜桃| 欧美成人一区二区三区| 久久这里只精品最新地址| 欧美精品久久99久久在免费线|