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++

平台:视窗

4

4 回答 4

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

这是调用函数的典型顺序 - 参数被压入堆栈,然后控制权转移到该函数代码(call指令)。

在参与有关“编译后如何工作”的争论时,使用反汇编也非常有用 - 就像他对这个问题的回答中的caf点一样。

于 2009-10-14T10:59:13.020 回答
3

1 - 我们应该(我)将反汇编作为最后的手段进行调试。通常,优化编译器生成的代码对于人眼来说并不容易理解。指令被重新排序,一些死代码被消除,一些特定代码被内联等等等等。所以当需要理解反汇编代码时没有必要也不容易。例如,我有时会查看反汇编以查看常量是操作码的一部分还是存储在 const 变量中。

2 - 那段代码调用像 getRequiredFields(result, 0, arItem) 这样的函数。你必须为你想要的处理器学习汇编语言。对于 x86,请访问 www.intel.com 并获取 IA32 的手册。

于 2009-10-14T11:01:29.780 回答
2

何时应该涉及反汇编:当您确切想知道 CPU 在执行程序时正在做什么,或者当您没有使用任何更高级别的语言编写程序的源代码(在您的情况下为 C++)时。

如何解释汇编代码:学习汇编语言。您可以在Intel 的处理器手册中找到有关 Intel x86 CPU 指令的详尽参考。

您发布的这段代码为函数调用准备参数(通过在堆栈上获取和推送一些值并将值放入 register eax),然后调用 function getRequiredFields

于 2009-10-14T11:00:11.940 回答
1

I started out in 1982 with assembly debugging of PL/M programs on CP/M-80 and later Digital Research OSes. It was the same in the early days of MS-DOS until Microsoft introduced symdeb which was a command-line debugger where source and assembly were displayed simultaneously. Symdeb was a leap forward but not that great since the earlier debuggers had forced me to learn to recognize what assembly code belonged to which source code line. Before CodeView the best debugger was pfix86 from Phoenix Technologies. NuMegas SoftIce was the best debugger (apart from pure hardware ICEs) I've ever come across in that it not only debugged my application but effortlessly led me through the inner workings of Windows as well. But I digress.

Late in 1990 a consultant in a project I was working in approached me and said he had this (very early) C++ bug he'd been working on for days but couldn't understand what the problem was. He single-stepped through the source code (on a windowed non-graphic DOS debugger) for me while I got all impatient. Finally I interrupted him and looked through the debugger options and sure enough there was the mixed source/assembly mode with registers and everything. This made it easy to realize that the application was trying to free an internal pointer (for local variables) containing NULL. For this problem, the source code mode was of no help at all. Today's C++ compilers will probably no longer contain a bug such as this but there will be others.

Knowing assembly-level debugging allows you to understand the source-compiler-assembly relationship to the extent of being able to predict what code the compiler will generate. Many people here on stackoverflow say "profile-profile-profile" but this goes a step further in that you learn what source-code constructs (I write in C) to use when and which to avoid. I suspect this is even more important with C++ which can generate a lot of code without the developer suspecting anything. For example there is a standard class for handling lists of objects which appears to be without drawbacks - just a few lines of code and this fantastic functionality! - until you look at the scores of strange procedure calls it generates. I'm not saying it's wrong to use them, I'm just saying that the developer should be aware of the pros and cons of using them. Overloading operators may be great functionality (somewhat weird to a WYSIWYG programmer like me) but what is the price in execution speed? If you say "nothing" I say "prove it."

It is never wrong to use mixed or pure assembly mode when debugging. Difficult bugs will usually be easier to find and the developer will learn to write more efficient code. Developers from the interpreted camp (C# and Java) will say that their code is just as efficient as the compiled languages but if you know assembly you will also know why they are wrong, why they are dead wrong. You can smile and think "yeah, tell me about it!"

After you've worked with different compilers you will come across one with the most astonishing code-generation ability. One PowerPC compiler condensed three nested loops into one loop simply through the superior code interpretation of it's optimizer. Next to the guy who wrote that I'm ... well, let's just say in a different league.

Up until about ten years ago I wrote quite a bit of pure assembly but with multi-stage pipelines, multiple execution units and now multiple cores to contend with the C compiler beats me hands down. On the other hand I know what the compiler can do a good job with and what it shouldn't have to work with: Garbage In still equals Garbage Out. This is true for any compiler that produces assembly output.

于 2011-02-21T19:15:41.823 回答