2009-10-14 15 views
2

我有以下幾個基本問​​題:在這情況下它使用反彙編語言是非常有用的,而調試

  • 當我們應該包括在調試拆裝

  • 如何解釋拆卸,例如下面哪些呢每段代表

00637CE3 8B 55 08    mov   edx,dword ptr [arItem] 
00637CE6 52     push  edx 
00637CE7 6A 00    push  0 
00637CE9 8B 45 EC    mov   eax,dword ptr [result] 
00637CEC 50     push  eax 
00637CED E8 3E E3 FF FF  call  getRequiredFields (00636030) 
00637CF2 83 C4 0C    add 

語言:C++

平臺:視窗

+0

對於它的價值,我從來沒有聽說過它叫做「反彙編」語言,但我知道你的意思。 – 2009-10-15 05:19:50

回答

4

這是相當有用的估計是多麼有效編譯器產生的代碼。例如,如果在沒有反彙編的循環中使用std::vector::operator[],很難猜測每次調用operator[]實際上都需要兩次內存訪問,但使用迭代器來訪問內存時需要一次內存訪問。

在您的例子:

mov   edx,dword ptr [arItem] // value stored at address "arItem" is loaded onto the register 
push  edx // that register is pushes into stack 
push  0 // zero is pushed into stack 
mov   eax,dword ptr [result] // value stored at "result" address us loaded onto the register 
push  eax // that register is pushed into stack 
call  getRequiredFields (00636030) // getRequiredFields function is called 

這是調用的功能的典型的序列 - PARAMATERS被推入堆棧,然後將控制轉移到函數代碼(call指令)。

當參與關於「編譯後如何工作」的參數時,使用反彙編也非常有用 - 例如cafhis answer to this question

+0

爲什麼'vector :: operator []'在循環中需要兩次內存訪問?將基址T *加載到寄存器中,並使用單個索引存儲器訪問。 – MSalters 2009-10-14 11:15:27

+0

如果在循環中調用operator [],編譯器可能很難看出緩衝區的開始沒有改變,並且在每次迭代時都沒有重新加載基本T *。 – sharptooth 2009-10-14 11:41:53

2

當你應該涉及反彙編時:當你確切地想知道CPU在執行你的程序時正在做什麼,或者當你沒有用任何更高級語言編寫程序的源代碼時(C++中你的情況)。

如何解釋彙編代碼:學習彙編語言。您可以在Intel's processor manuals的Intel x86 CPU指令中找到詳盡的參考資料。

您發佈的一段代碼爲函數調用準備了參數(通過獲取並推送堆棧上的某些值並將值存入寄存器eax),然後調用函數getRequiredFields

3

1 - 我們應該(I)在調試中包含反彙編作爲最後的手段。通常,優化編譯器生成的代碼對於人眼來說不是微不足道的。指令被重新排序,一些死代碼被刪除,一些特定的代碼被內聯等等。所以當需要理解反彙編代碼時,沒有必要也不容易。例如,我有時會查看反彙編,以查看常量是否是操作碼的一部分,或者是否存儲在常量變量中。

2 - 這段代碼調用了像getRequiredFields(result,0,arItem)這樣的函數。你必須爲你想要的處理器學習彙編語言。對於x86,請訪問www.intel.com並獲取IA32的手冊。

+1

當您想要使用反彙編時,在調試優化代碼時恰好*。由於所有內聯和重新排序,源代碼的執行將不會有任何意義。唯一有意義的代碼是反彙編。 – 2009-10-14 13:53:27

+0

@贊恩Lynx:再次想到,你是對的。我的觀點是,反彙編列表與「看到」的C++源代碼/代碼流非常不同,所以它會讓他困惑,而不是幫助。 – Malkocoglu 2009-10-14 19:57:11

1

我從1982年開始在CP/M-80和後來的數字研究操作系統上調試PL/M程序。在微軟推出的symdeb是一個命令行調試器,其源代碼和程序集同時顯示之前,它在MS-DOS的早期是一樣的。 Symdeb是一個飛躍,但並不是那麼好,因爲先前的調試器迫使我學會識別哪些彙編代碼屬於哪個源代碼行。在CodeView之前,最好的調試器是Phoenix Technologies的pfix86。 NuMegas SoftIce是我見過的最好的調試器(除了純硬件ICE),它不僅調試了我的應用程序,而且還毫不費力地引導我完成了Windows的內部工作。但我離題了。

1990年晚些時候,我在一個項目的顧問找到我,說他有這個(很早)的C++ bug,他一直在努力好幾天,但不明白是什麼問題。在我全無耐心的時候,他單步通過源代碼(在窗口化的非圖形DOS調試器上)。最後,我打斷了他,並查看了調試器選項,並確定存在與寄存器和所有內容混合的源/彙編模式。這使得應用程序試圖釋放一個包含NULL的內部指針(用於局部變量)變得容易。對於這個問題,源代碼模式根本沒有任何幫助。今天的C++編譯器可能不再包含像這樣的錯誤,但會有其他錯誤。

瞭解彙編級別的調試功能可以讓您瞭解源編譯器彙編關係,以便能夠預測編譯器將生成的代碼。很多人在這裏在stackoverflow上說「profile-profile-profile」,但是這更進一步,因爲你學習了什麼源代碼結構(我用C編寫)來使用什麼時候以及如何避免。我懷疑這對C++來說更重要,它可以在開發人員不懷疑任何事情的情況下生成大量代碼。例如,有一個處理對象列表的標準類,它看起來沒有缺點 - 只需幾行代碼和這個奇妙的功能! - 直到你看到它產生的奇怪過程調用的分數。我並不是說使用它們是錯誤的,我只是說開發者應該意識到使用它們的優點和缺點。重載操作符可能是很好的功能(對於像我這樣的所見即所得程序員來說有些奇怪),但執行速度的價格是多少?如果你說「沒有」,我會說「證明它」。

在調試時使用混合或純粹的彙編模式絕不是錯誤。通常更容易找到困難的錯誤,開發人員將學習編寫更高效的代碼。來自解釋陣營(C#和Java)的開發人員會說他們的代碼和編譯語言一樣高效,但如果你知道程序集,你也會知道他們爲什麼錯了,爲什麼他們錯了。你可以微笑並思考「是的,請告訴我吧!」

在與不同的編譯器一起工作之後,您會遇到一個具有最驚人代碼生成能力的代碼。一個PowerPC編譯器只需通過其優化器的高級代碼解釋就可以將三個嵌套循環壓縮成一個循環。在寫這篇文章的那個人旁邊......好吧,讓我們來談談另一個聯盟。

直到大約十年前,我寫了相當多的純彙編,但是使用多級流水線,多個執行單元,現在有多個與C編譯器競爭的內核,這些都讓我倍感壓力。另一方面,我知道編譯器可以做些什麼以及不應該使用哪些東西:垃圾回收仍然等於垃圾回收。任何生成彙編輸出的編譯器都是如此。