在GCC和Borland的DIS组装C code的差别? [英] Differences in dis-assembled C code of GCC and Borland?

查看:216
本文介绍了在GCC和Borland的DIS组装C code的差别?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

最近我已经得到了兴趣进入DIS组装C code(很简单的C code)以及随后所使用的Borland C ++编译器V 5.5教程(编译C $ C $Ç就好了)和一切工作。然后,我决定尽我自己的C code和汇编他们开发的C ++(使用GCC)。当在IDA打开它临我得到了一个惊喜,相比之下,Borland的GCC的ASM真的很不同。我的预期差一些但C code是非常简单的,所以它只是说GCC不优化尽可能多或者是它们使用不同的默认编译器设置?

的C code

  INT主(INT ARGC,字符** argv的)
{
   int类型的;
   一个= 1;
}

Borland公司ASM

 的.text:00401150; INT __cdecl主(INT ARGC,为const char ** argv的,为const char * envp)
的.text:00401150 _main附近PROC; DATA XREF:。数据:004090D0
的.text:00401150
的.text:00401150 ARGC = DWORD PTR 8
的.text:00401150的argv = DWORD PTR 0CH
的.text:00401150 envp = DWORD PTR 10H
的.text:00401150
的.text:00401150推EBP
的.text:00401151 MOV EBP,ESP
的.text:00401153弹​​出EBP
的.text:00401154 RETN
的.text:00401154 _main ENDP

GCC ASM(修订波纹管)

 的.text:00401220; |||||||||||||||子程序|||||||||||||||||||||||||||||||||| |||||
的.text:00401220
的.text:00401220;属性:BP基于帧
的.text:00401220
的.text:00401220大众启动
的.text:00401220开始PROC附近
的.text:00401220
的.text:00401220 var_14 = DWORD PTR -14h
的.text:00401220 var_8 = DWORD PTR -8
的.text:00401220
的.text:00401220推EBP
的.text:00401221 MOV EBP,ESP
的.text:00401223子ESP,8
的.text:00401226 MOV [ESP + 8 + var_8] 1
的.text:0040122D通话DS:__ set_app_type
的.text:00401233电话sub_401100
的.text:00401238 NOP
的.text:00401239 LEA ESI,[ESI + 0]
的.text:00401240推EBP
的.text:00401241 MOV EBP,ESP
的.text:00401243子ESP,8
的.text:00401246 MOV [ESP + 14H + var_14],2
的.text:0040124D通话DS:__ set_app_type
的.text:00401253电话sub_401100
的.text:00401258 NOP
的.text:00401259 LEA ESI,[ESI + 0]
的.text:00401259开始ENDP

GCC更新
当以下JimR的建议我去看看是什么sub_401100,然后我跟着那个code到另一个,这似乎是code(我是正确的这一假设,如果sowhy确实GCC拥有其所有在主函数code):

 的.text:近00401100 sub_401100 PROC; code XREF:的.text:004010F1j
的.text:00401100;启动+ 13P ...
的.text:00401100
的.text:00401100 var_28 = DWORD PTR -28h
的.text:00401100 var_24 = DWORD PTR -24h
的.text:00401100 var_20 = DWORD PTR -20h
的.text:00401100 var_1C = DWORD PTR -1Ch
的.text:00401100 var_18 = DWORD PTR -18h
的.text:00401100 var_C = DWORD PTR -0Ch
的.text:00401100 var_8 = DWORD PTR -8
的.text:00401100
的.text:00401100推EBP
的.text:00401101 MOV EBP,ESP
的.text:00401103推EBX
的.text:00401104子ESP,24小时; lpTopLevelExceptionFilter
的.text:00401107 LEA EBX,[EBP + var_8]
的.text:0040110A MOV [ESP + 28H + var_28],抵消sub_401000
的.text:00401111呼叫SetUnhandledExceptionFilter
的.text:00401116子ESP,4; uExit code
的.text:00401119电话sub_4012E0
的.text:0040111E MOV [EBP + var_8],0
的.text:00401125 MOV EAX,抵消dword_404000
的.text:0040112A LEA EDX,[EBP + var_C]
的.text:0040112D MOV [ESP + 28H + var_18],EBX
的.text:00401131 MOV ECX,dword_402000
的.text:00401137 MOV [ESP + 28H + var_24],EAX
的.text:0040113B MOV [ESP + 28H + var_20],EDX
的.text:0040113F MOV [ESP + 28H + var_1C],ECX
的.text:00401143 MOV [ESP + 28H + var_28],抵消dword_404004
的.text:0040114A通话__getmainargs
的.text:0040114F MOV EAX,DS:dword_404010
的.text:00401154 TEST EAX,EAX
的.text:00401156 JZ短loc_4011B0
的.text:00401158 MOV dword_402010,EAX
的.text:0040115D MOV EDX,DS:_iob
的.text:00401163试验EDX,EDX
的.text:00401165 JNZ loc_4011F6


 的.text:近004012E0 sub_4012E0 PROC; code XREF:sub_401000 + C6P
的.text:004012E0; sub_401100 + 19P
的.text:004012E0推EBP
的.text:004012E1 MOV EBP,ESP
的.text:004012E3 FNINIT
的.text:004012E5弹出EBP
的.text:004012E6 RETN
的.text:004012E6 sub_4012E0 ENDP


解决方案

编译器产量预计有所不同,相同的源有时显着不同。以同样的方式,一个丰田和本田是不同的。四个轮子和一些座椅肯定,但比同更多的不同,当你看看细节。

同样,相同的编译器与不同的编译器选项可以并经常将产生相同的源$ C ​​$ C显着不同的输出。即使是看似简单的程序。

在您简单的程序,它实际上并没有做任何事情的情况下(code不影响输入,也没有输出,也没有任何函数外),一个好的优化的编译器将导致什么,但主:有一些随机数的回报,因为你没有指定返回值。其实它应该给一个警告或错误。这是我当我比较编译器的输出是使东西很简单,看看他们在做什么,但足够复杂,编译器做多的东西,最大的问题只是pre-计算答案并返回它。

在86的情况下,我以为是你在这里说的是什么,是微codeD,这些天实在没有答案好code VS坏code,处理器的每个家庭他们改变了胆量周围,曾经被认为是快是慢,什么是现在快是旧处理器慢。因此,对于像的gcc编译已继续与新核心演进,优化既可以是通用于所有x86es或特定于特定的家庭(导致不同code尽管最大优化)。

随着拆卸新的兴趣,你会继续看到的异同,找出相同的code究竟有多少不同的方式可以进行编译。差异预计,即使是微不足道的计划。我鼓励你去尝试尽可能多的编译器,你可以。即使在GCC家庭2.X,3.X,4.x和不同的方式来构建它会导致不同的code代表什么可能是,虽然作为相同的编译器的想法。

好不好VS输出在旁观者的眼中。使用调试器乡亲们都希望他们的code步进式及其变量有看头(书面code顺序)。这使得非常大,体积大,速度慢和code(特别是用于x86)。而当你编译你结束了一个完全不同的计划,到目前为止您已经花费了零时间的调试版本。同时优化性能,你需要编译器的优化风险了一些东西,你想要它做的事(你上面的例子,没有变量将被分配,没有code单步,即使是轻微优化)。或者更糟的是,你暴露了编译器的错误和你的程序根本不工作(这就是为什么-O3不鼓励为GCC)。这和/或你发现了大量的C标准的跨pretation是实现定义的地方。

未优化code更容易编译,因为它是更明显一点。在实施例的情况下,期望的是一个变量在栈上分配的,某种堆栈指针装置的设置,眼前1最终被写入到该位置,栈清理和函数返回。更难的编译器得到错误的,更可能是你的程序工作按预期。检测和删除死code为优化业务和
这是它得到的风险。通常情况下,风险是值得的奖励。但是,这取决于用户,美在旁观者的眼睛。

底线,简短的回答。差异预期(甚至有戏剧性的差异)。默认编译选项会有所不同,从编译器编译器。实验编译/优化选项和不同的编译器,并继续拆卸您的程序,以获取有关的语言和你使用的编译器更好的教育。你是在正确的轨道为止。在Borland输出的情况下,它检测到程序什么都不做,没有输入变量的使用,没有返回变量的使用,也没有涉及到局部变量,和不使用全局变量或其他外部的功能的资源。整数a和立即的分配都死了code,良好的优化器会去除基本上/忽略code两行。因此,它不屑于再设置堆栈帧清理其它没有需要做的,然后返回。 GCC看起来是建立一个异常处理程序是,即使它并不需要,开始优化或使用比主(其它函数名),你应该会看到不同的结果完全正常的。

Recently I have gotten interested into dis-assembling C code (very simple C code) and followed a tutorial that used Borland C++ Compiler v 5.5 (compiles C code just fine) and everything worked. Then I decided to try my own c code and compiled them in Dev C++ (which uses gcc). Upon opening it in IDA Pro I got a surprise, the asm of gcc was really different compared to Borland's. I expected some difference but the C code was EXTREMELY simple, so is it just that gcc doesn't optimize as much or is it that they use different default compiler settings?

The C Code

int main(int argc, char **argv)
{
   int a;
   a = 1;
}

Borland ASM

.text:00401150 ; int __cdecl main(int argc,const char **argv,const char *envp)
.text:00401150 _main           proc near               ; DATA XREF: .data:004090D0
.text:00401150
.text:00401150 argc            = dword ptr  8
.text:00401150 argv            = dword ptr  0Ch
.text:00401150 envp            = dword ptr  10h
.text:00401150
.text:00401150                 push    ebp
.text:00401151                 mov     ebp, esp
.text:00401153                 pop     ebp
.text:00401154                 retn
.text:00401154 _main           endp

GCC ASM (UPDATED BELLOW)

.text:00401220 ; ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ S U B R O U T I N E ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦
.text:00401220
.text:00401220 ; Attributes: bp-based frame
.text:00401220
.text:00401220                 public start
.text:00401220 start           proc near
.text:00401220
.text:00401220 var_14          = dword ptr -14h
.text:00401220 var_8           = dword ptr -8
.text:00401220
.text:00401220                 push    ebp
.text:00401221                 mov     ebp, esp
.text:00401223                 sub     esp, 8
.text:00401226                 mov     [esp+8+var_8], 1
.text:0040122D                 call    ds:__set_app_type
.text:00401233                 call    sub_401100
.text:00401238                 nop
.text:00401239                 lea     esi, [esi+0]
.text:00401240                 push    ebp
.text:00401241                 mov     ebp, esp
.text:00401243                 sub     esp, 8
.text:00401246                 mov     [esp+14h+var_14], 2
.text:0040124D                 call    ds:__set_app_type
.text:00401253                 call    sub_401100
.text:00401258                 nop
.text:00401259                 lea     esi, [esi+0]
.text:00401259 start           endp

GCC Update Upon following the suggestion of JimR I went to see what sub_401100 is and then I followed that code to another and this seems to be the code (Am I correct in that assumption and if sowhy does GCC have all of its code in the main function?):

.text:00401100 sub_401100      proc near               ; CODE XREF: .text:004010F1j
.text:00401100                                         ; start+13p ...
.text:00401100
.text:00401100 var_28          = dword ptr -28h
.text:00401100 var_24          = dword ptr -24h
.text:00401100 var_20          = dword ptr -20h
.text:00401100 var_1C          = dword ptr -1Ch
.text:00401100 var_18          = dword ptr -18h
.text:00401100 var_C           = dword ptr -0Ch
.text:00401100 var_8           = dword ptr -8
.text:00401100
.text:00401100                 push    ebp
.text:00401101                 mov     ebp, esp
.text:00401103                 push    ebx
.text:00401104                 sub     esp, 24h        ; lpTopLevelExceptionFilter
.text:00401107                 lea     ebx, [ebp+var_8]
.text:0040110A                 mov     [esp+28h+var_28], offset sub_401000
.text:00401111                 call    SetUnhandledExceptionFilter
.text:00401116                 sub     esp, 4          ; uExitCode
.text:00401119                 call    sub_4012E0
.text:0040111E                 mov     [ebp+var_8], 0
.text:00401125                 mov     eax, offset dword_404000
.text:0040112A                 lea     edx, [ebp+var_C]
.text:0040112D                 mov     [esp+28h+var_18], ebx
.text:00401131                 mov     ecx, dword_402000
.text:00401137                 mov     [esp+28h+var_24], eax
.text:0040113B                 mov     [esp+28h+var_20], edx
.text:0040113F                 mov     [esp+28h+var_1C], ecx
.text:00401143                 mov     [esp+28h+var_28], offset dword_404004
.text:0040114A                 call    __getmainargs
.text:0040114F                 mov     eax, ds:dword_404010
.text:00401154                 test    eax, eax
.text:00401156                 jz      short loc_4011B0
.text:00401158                 mov     dword_402010, eax
.text:0040115D                 mov     edx, ds:_iob
.text:00401163                 test    edx, edx
.text:00401165                 jnz     loc_4011F6


.text:004012E0 sub_4012E0      proc near               ; CODE XREF: sub_401000+C6p
.text:004012E0                                         ; sub_401100+19p
.text:004012E0                 push    ebp
.text:004012E1                 mov     ebp, esp
.text:004012E3                 fninit
.text:004012E5                 pop     ebp
.text:004012E6                 retn
.text:004012E6 sub_4012E0      endp

解决方案

Compiler output is expected to be different, sometimes dramatically different for the same source. In the same way that a toyota and a honda are different. Four wheels and some seats sure, but more different than the same when you look at the details.

Likewise the same compiler with different compiler options can and often will produce dramatically different output for the same source code. Even for what appears to be simple programs.

In the case of your simple program, which actually does not do anything (code does not affect the input, nor output, nor anything outside the function), a good optimized compiler will result in nothing but main: with a return of some random number since you didnt specify the return value. Actually it should give a warning or error. This is the biggest problem I have when I compare compiler output is making something simple enough to see what they are doing but something complicated enough that the compiler does more than just pre-compute the answer and return it.

In the case of x86, which I assume is what you are talking about here, being microcoded these days there is really no answer for good code vs bad code, each family of processor they change the guts around and what used to be fast is slow and what is now fast is slow on the old processor. So for compilers like gcc that have continued to evolve with the new cores, the optimization can be both generic to all x86es or specific to a particular family (resulting in different code despite max optimization).

With your new interest in disassembling, you will continue to see the similarities and differences and find out just how many different ways the same code can be compiled. the differences are expected, even for trivial programs. And I encourage you to try as many compilers as you can. Even in the gcc family 2.x, 3.x, 4.x and the different ways to build it will result in different code for what might be though thought of as the same compiler.

Good vs bad output is in the eyes of the beholder. Folks that use debuggers will want their code steppable and their variables watchable (in written code order). This makes for very big, bulky, and slow code (particularly for x86). And when you compile for release you end up with a completely different program which you have so far spent zero time debugging. Also optimizing for performance you take a risk of the compiler optimizing out something you wanted it to do (your example above, no variable will be allocated, no code to step through, even with minor optimization). Or worse, you expose the bugs in the compiler and your program simply doesnt work (this is why -O3 is discouraged for gcc). That and/or you find out the large number of places in the C standard whose interpretation is implementation defined.

Unoptimized code is easier to compile, as it is a bit more obvious. In the case of your example the expectation is a variable is allocated on the stack, some sort of stack pointer arrangement set up, the immediate 1 is eventually written to that location, stack cleaned up and function returns. Harder for compilers to get wrong and more likely that your program works as you intended. Detecting and removing dead code is the business of optimization and that is where it gets risky. Often the risk is worth the reward. But that depends on the user, beauty is in the eye of the beholder.

Bottom line, short answer. Differences are expected (even dramatic differences). Default compile options vary from compiler to compiler. Experiment with the compile/optimization options and different compilers and continue to disassemble your programs in order to gain a better education about the language and the compilers you use. You are on the right track so far. In the case of the borland output, it detected that your program does nothing, no input variables are used, no return variables are used, nor related to the local variables, and no global variables or other external to the function resources are used. The integer a and the assignment of an immediate are dead code, a good optimizer will essentially remove/ignore both lines of code. So it bothered to setup a stack frame then clean it up which it didnt need to do, then returned. gcc looks to be setting up an exception handler which is perfectly fine even though it doesnt need to, start optimizing or use a function name other than main() and you should see different results.

这篇关于在GCC和Borland的DIS组装C code的差别?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆