GCC和Borland的分解C代码的差异? 最近我对DIS汇编C代码(非常简单的C代码)感兴趣,并遵循Borland C++编译器V 5.5(编译C代码很好)的教程,所有的工作都有效。然后我决定尝试自己的C代码,并在DEV C++(使用GCC)中编译它们。在IDA Pro中打开它时,我得到了一个惊喜,gcc的asm与Borland的真的不同。我希望有一些不同,但是C代码非常简单,所以是因为gcc没有进行太多优化,还是因为它们使用了不同的默认编译器设置

GCC和Borland的分解C代码的差异? 最近我对DIS汇编C代码(非常简单的C代码)感兴趣,并遵循Borland C++编译器V 5.5(编译C代码很好)的教程,所有的工作都有效。然后我决定尝试自己的C代码,并在DEV C++(使用GCC)中编译它们。在IDA Pro中打开它时,我得到了一个惊喜,gcc的asm与Borland的真的不同。我希望有一些不同,但是C代码非常简单,所以是因为gcc没有进行太多优化,还是因为它们使用了不同的默认编译器设置,c,assembly,disassembly,C,Assembly,Disassembly,C代码 int main(int argc, char **argv) { int a; a = 1; } 博尔兰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 .te

C代码

int main(int argc, char **argv)
{
   int a;
   a = 1;
}
博尔兰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(以下更新)

GCC更新 在遵循JimR的建议后,我去查看sub_401100是什么,然后我将该代码转到另一个代码,这似乎就是代码(我的假设正确吗?如果GCC的所有代码都在主函数中,那么为什么?)



这里最可能发生的情况是,Borland在使用运行时库中的代码初始化所有内容后,从其启动代码调用main


gcc代码在我看来不像main,而是像调用main的生成代码。在sub_401100处反汇编代码,看看它是否与主进程相似。

首先,确保至少已将-O2优化标志启用到gcc,否则根本不会得到优化


通过这个小例子,您并不是真正测试优化,而是看到程序初始化是如何工作的,例如,gcc调用通知windows应用程序类型以及其他初始化。e、 g.sub_401100为运行时注册atexit处理程序。Borland可能会事先调用运行时初始化,而gcc会在main()中调用。

Borland编译器似乎意识到您从来没有对
a
执行过任何操作,只是为空的主函数提供了等效的程序集。

编译器的输出应该是不同的,对于同一个来源,有时会有显著的不同。就像丰田和本田不同一样。当然有四个轮子和一些座椅,但当你看细节时,会发现它们有很多不同

同样地,具有不同编译器选项的同一个编译器可以而且经常会为同一源代码生成截然不同的输出。即使是看似简单的程序

对于您的简单程序,它实际上什么都不做(代码不影响输入、输出,也不影响函数之外的任何内容),一个好的优化编译器只会生成main:返回一些随机数,因为您没有指定返回值。实际上,它应该给出警告或错误。这是我在比较编译器输出时遇到的最大问题,它使一些事情变得足够简单,可以看到它们在做什么,但使一些事情变得足够复杂,编译器不只是预先计算答案并返回它

在x86的例子中,我想这就是你在这里所说的,现在被微代码化了,真的没有好代码和坏代码的答案,每一个处理器家族都改变了他们的胆量,过去快的是慢的,现在快的在旧处理器上是慢的。因此,对于像gcc这样的编译器来说,随着新内核的不断发展,优化既可以是所有x86e的通用优化,也可以是特定于特定系列的优化(尽管进行了最大优化,但仍会产生不同的代码)

随着您对反汇编的新兴趣,您将继续看到它们的相似性和差异,并了解同一代码可以采用多少种不同的编译方式。这些差异是意料之中的,即使是对于一些琐碎的程序也是如此。我鼓励您尝试尽可能多的编译器。即使是在gcc系列2.x、3.x、4.x以及构建它的不同方法中,也会产生不同的代码,这些代码可能被认为是同一个编译器

好的与坏的输出在旁观者的眼中。使用调试器的人希望他们的代码是可步进的,变量是可观察的(以书面代码顺序)。这使得代码非常大、笨重且速度慢(特别是对于x86)。当您为发布版编译时,最终会得到一个完全不同的程序,到目前为止,您没有花任何时间调试它。此外,为了性能优化,您还冒着编译器优化出您希望它执行的操作的风险(在上面的示例中,不会分配任何变量,也不会执行任何代码,即使是轻微的优化)。或者更糟糕的是,您暴露了编译器中的bug,而您的程序根本无法工作(这就是为什么gcc不推荐使用-O3)。这和/或您会发现C标准中有大量地方的解释是由实现定义的

未优化的代码更容易编译,因为它更明显。在您的示例中,期望是在堆栈上分配一个变量,设置某种堆栈指针排列,立即数1最终写入该位置,堆栈被清除,函数返回。编译器更难出错,程序更可能按预期运行。检测和删除死代码是优化和测试的任务 这就是风险所在。风险往往值得回报。但这取决于用户,美丽在于旁观者的眼睛

底线,简短的回答。预期会有差异(甚至是巨大的差异)。默认编译选项因编译器而异。尝试编译/优化选项和不同的编译器,并继续反汇编程序,以便更好地了解您使用的语言和编译器。到目前为止,你在正确的轨道上。在borland输出的情况下,它检测到您的程序什么也不做,没有使用输入变量,没有使用返回变量,也没有与局部变量相关,也没有全局变量或其他外部变量
.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
.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
(gdb) set disassembly-flavor intel
(gdb) disassemble
Dump of assembler code for function main:
   0x00401350 <+0>:     push   ebp
   0x00401351 <+1>:     mov    ebp,esp
   0x00401353 <+3>:     and    esp,0xfffffff0
   0x00401356 <+6>:     call   0x4018aa <__main>
=> 0x0040135b <+11>:    xor    eax,eax
   0x0040135d <+13>:    mov    esp,ebp
   0x0040135f <+15>:    pop    ebp
   0x00401360 <+16>:    ret
End of assembler dump.
(gdb) set disassembly-flavor intel
(gdb) disassemble
Dump of assembler code for function main:
   0x00401350 <+0>:     push   ebp
   0x00401351 <+1>:     mov    ebp,esp
   0x00401353 <+3>:     and    esp,0xfffffff0
   0x00401356 <+6>:     sub    esp,0x10
   0x00401359 <+9>:     call   0x4018aa <__main>
=> 0x0040135e <+14>:    mov    DWORD PTR [esp+0xc],0x1
   0x00401366 <+22>:    mov    eax,0x0
   0x0040136b <+27>:    leave
   0x0040136c <+28>:    ret
End of assembler dump.
void start()
{
    ... some initialization code here
    int result = main();
    ... some deinitialization code here
    ExitProcess(result);
}