<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                今天編譯一個C++程序時,報了一個奇怪的錯誤(之前是好好的): 1>LINK : fatal error LNK1123: failure during conversion to COFF: file invalid or corrupt Google上搜了一下解決方案: 把Project Properties -> Configuration Properties ?-> Linker (General) -> Enable Incremental Linking中的Yes (/INCREMENTAL)改成No (/INCREMENTAL:NO) 果然管用! 但又有個新的疑問出現了:Incremental Linking是什么?它有什么作用? 大概總結一下: Incremental Linking翻譯成中文就是“增量鏈接”,是一個鏈接的參數選項,作用就是為了提高鏈接速度的。什么意思呢?不選用增量鏈接時,每次修改或新增代碼后進行鏈接時會重新洗牌,把原來的.exe刪了,重新鏈接成一個新的.exe,這樣對于大型項目來說鏈接會比較慢。而選用增量鏈接時,在對代碼做小的改動時會把新成的函數或數據穿插到已有的.exe中,而不重新生成.exe,只有做了大量修改時才可能會重新編排,這樣就可以提高鏈接的速度。 一般VS的默認設置會把Debug版的Incremental Linking設置成Yes (/INCREMENTAL),而把Release版的設置成No (/INCREMENTAL:NO)。 關于Incremental Linking更詳細的介紹和分析請參考另一位博主的原文: [http://www.cnblogs.com/Dahaka/archive/2011/08/01/2124256.html](http://www.cnblogs.com/Dahaka/archive/2011/08/01/2124256.html) 為方便閱讀,直接將這文章拷貝了一份: 好的,文接上回,本文我就來講講微軟link.exe連接器的Incremental Liking這個特性。當然這個其實不是微軟linker獨有的特性,很多鏈接器都有這個特性,這個特性實際上是為了提高鏈接速度的。   想象一下這個場景,我寫了兩個函數foo()和bar(),其中foo()在0x400100處而bar()緊接著保存在0x400200處。現在我將foo()改寫了一下,添加了一些perfect的功能,然后編譯了新的代碼。不過現在的麻煩是foo()不可避免的變大了,他現在需要200h字節來保存了。那么鏈接器該怎么辦?   一般的思考是——重新洗牌,將現有的編譯好的exe刪除了,然后重新布局所有的函數,也即是說bar()函數向后挪動0x100h字節的位置,給foo()騰出空間來。然后之后所有的函數都需要重新定位……對于大型軟件來說這個處理時間開銷是痛苦的,但作為程序員我們卻不能避免需要不斷的調試改代碼,不斷地重復這個耗時的工作。   不過我們現在并不需要給客戶最終的發行代碼,我們只是想要盡快地將程序的bug改掉然后去休息而已!于是,Incremental Linking出現了!它的原理如下: ![](https://box.kancloud.cn/2016-01-19_569dd03ccdb33.gif)   現在連接器不會將所有函數緊挨著放在一塊兒了,他們會在函數之間加上padding,這個時候函數要想添幾句指令就有余地了。只要我們的改動不大,沒有超過padding的范圍連接器就不需要重新洗牌,這大大提高了鏈接的速度。   先別高興,加入我們的改動很大,以至于超過padding能夠搞定的范圍怎么辦?如上圖,我們還會在整個section末尾設置一個較大的padding(當然具體在哪里要看實現,比如我這圖是從GCC那里搞得,說的就是ld.exe的行為方式),這時候就可以將這個函數搬到這里來了。但有個毀滅性的問題——所有調用我這個函數的函數都必須重定位他們的call指令啊!   為了解決這個問題,我們引入了一個ILT表(Incremental Linking Table),這個表是放在.text區域中的(我在IDA中觀察得知)。它的原理是什么呢?我們來看: ![](https://box.kancloud.cn/2015-10-30_5632e1b8d3b57.gif) ~~~ ;之前我們都是直接調用函數 call foo ;現在我們來點小把戲 call foo_stub foo_stub: jmp foo ~~~ ![](https://box.kancloud.cn/2015-10-30_5632e1b8d3b57.gif)   我們現在不直接調用函數,而是call到一個包含jmp指令的地方,然后由這個指令將我們的程度帶往foo()函數的實現去。現在如果我們將foo()的實現改動過大后,linker直接將foo()移動了,然后只需要修改這個jmp指令就行了。可以看到,這種實現方式開銷是O(1)。然后當很多個函數都用這種方式時,就形成了一個有jmp指令構成的表——這就是ILT表啦。   有興趣的童鞋可以做下實驗,在VS2010編譯一次代碼,然后用IDA或者W32Dasm之類的軟件可以看到兩個函數之間間隔了不少距離,而這些間隔就是我們所謂padding。padding被填充以0xCCh的數據。熟悉win32匯編的朋友這時候該笑而不語了,是的,這個值就是指令INT 3。在WIndows下,執行這個指令會引發一個異常,然后程序會被終止或是回到調試器去,這當然是出于安全性考慮的。這之后如果你在前一個函數加幾句話,編譯后可以看到兩個函數位置不變,但函數間的padding變小了。 **和.textbss的關系**   嘛,之前有篇我討論了PE常見的section,里面提到了這個節,下面我就詳細介紹一下它的作用。   首先Incremental Linking作用不僅僅是在于減少我們重新連接程序所需要的時間,他還是我們調試時能夠動態改動代碼的前提。不知你還記得不,在那個炎熱的夏天,你正汗流浹背地在沒有空調的部屋里調試C代碼(咳,說遠了……)你直接修改了代碼,然后VS直接在調試的時候將你的改動反映到程序里去了。這就是VS在Debug模式時動態編譯代碼的功能。   實際上這個功能是基于Incremental Linking機制的,而且是使用的Incremental Linking的第二套方案——直接找個大的地兒把修改的函數挪過去。   但是和.textbss有啥關系?   首先我們看到,.textbss有關鍵字bss,這就說明實際上這個節沒有占據實際的硬盤空間。然后text關鍵字告訴我們這里段是包含代碼的,另外用工具查知這個段有可執行屬性更是印證了這個觀點。沒有代碼,那要這個節有啥用呢?   你想到了么?是的,在VS動態編譯的時候,他直接將被修改的函數放到了.textbss節里,然后修改了對應的ILT表項,是他指向這個位置。   說是簡單,但實際上這個過程還個細節需要注意——你把我的函數挪地兒了,要是我正在執行這個函數怎么辦?實際上,在改了ILT之后立刻會做的,就是檢查當前程序所有線程的TIB,如果他們的EIP指向老的函數(它們正在執行老版本函數),我們就修改EIP使其指向新版本函數的對應位置。當然,這實際上暗示了,這個工作非要在調試程序的幫助下不可了。注意動態編譯的功能只在Debug版本程序下有效,Release版本是不行的,因為Release版本默認禁用Incremental Linking。 **下面是小亡的實驗時間,以驗證我的觀點:**   我就用手頭上的程序來測試。有函數CheckValidPE(),它的RVA是0x12490h(你可理解為內存地址)。我的程序地址空間部分如下: ?Section       ?RVA      Size ?.textbss      1000h     10000h ?.text        11000h     7000h   可以看到這兩個函數實際上是位于.text節內的。我在CheckValidPE()上下個斷,可以看到: ![](https://box.kancloud.cn/2015-10-30_5632e1b8d3b57.gif) ~~~ bool PEAnalyser::checkValidPE() { 00D92490 push ebp 00D92491 mov ebp,esp 00D92493 sub esp,0FCh 00D92499 push ebx 00D9249A push esi 00D9249B push edi 00D9249C push ecx ...... ~~~ ![](https://box.kancloud.cn/2015-10-30_5632e1b8d3b57.gif)   從這里我發覺了VS貌似從來都是隨機裝載PE映像的ImageBase位置的,搞得我之前一次實驗滿心歡喜用常用的0x400000h為基址換算了所有的RVA~~T_T。。。   我之前說了CheckValidPE()的RVA是0x12490h,這里的絕對地址是0x00D92490h,我們用0x00D92490h-0x12490h = 0xD80000h,得到兩個的差值。哈哈,看來這次的映像基址被射到了0xD80000h。   我回到VS源代碼視圖上,對代碼稍作粉飾,嗯,然后它發生了!!! ![](https://box.kancloud.cn/2015-10-30_5632e1b8d3b57.gif) ~~~ bool PEAnalyser::checkValidPE() { 00D81000 push ebp 00D81001 mov ebp,esp 00D81003 sub esp,0FCh 00D81009 push ebx 00D8100A push esi 00D8100B push edi 00D8100C push ecx 00D8100D lea edi,[ebp-0FCh] 00D81013 mov ecx,3Fh 00D81018 mov eax,0CCCCCCCCh 00D8101D rep stos dword ptr es:[edi] ..... ~~~ ![](https://box.kancloud.cn/2015-10-30_5632e1b8d3b57.gif)   注意看函數變到這個地址來了!!而且VS的調試程序指針也確實說明EIP被更改了。   我們來算一下,看看這個地址在哪里?用這里新的函數起始地址減去我們之前計算的的基址得到RVA = 0xD81000h - 0xD80000h = 0x1000h   回去查一下我的內存地址空間,RVA為0x1000h正是位于.textbss節的起始位置。看來我的猜測是正確的。   小結一下,關于Incremental Linking,由于他的機制所致,勢必帶來程序體積的臃腫以及執行的低效,但是由于我們只是在Debug程序是使用,所以問題不大。另外VS默認是在Debug是開啟Incremental Linking而Release模式關閉這個特性的。這說明在Release時,我們不能夠動態的改動代碼了。   另外注意Incremental Linking是和**/LTCG**?選項不兼容的,你不能同時開啟Incremental Linking和Link Time Code Generation,從這個角度講,使用Incremental Linking進一步會造成程序執行效率下降。所以,我們應該在發布程序時,注意避免帶上這個特性。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看