gpt4 book ai didi

c - GCC和Borland在反汇编C代码方面的区别?

转载 作者:行者123 更新时间:2023-12-04 06:13:08 27 4
gpt4 key购买 nike

最近,我对分解C代码(非常简单的C代码)产生了兴趣,并遵循了使用Borland C ++ Compiler v 5.5(可以很好地编译C代码)的教程,并且一切正常。然后,我决定尝试自己的C代码,并在Dev C ++(使用gcc)中对其进行编译。在IDA Pro中打开它后,我感到很惊讶,与Borland的gcc相比,gcc的确与众不同。我期望有所不同,但是C代码非常简单,是否只是gcc没有进行太多优化,或者它们使用了不同的默认编译器设置?

C代码

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(更新贝洛)

.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更新
在遵循JimR的建议后,我去查看了sub_401100是什么,然后我又将该代码跟随到另一个代码中,这似乎是代码(我是在这个假设中正确的吗?如果这样,为什么GCC在主函数中拥有所有代码? ):

.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

最佳答案

编译器的输出可能会有所不同,对于同一源,有时会有很大不同。丰田和本田不同。当然,有四个轮子和一些座位,但是当您查看细节时,它们却有更多不同。

同样,具有不同编译器选项的同一编译器可以并且经常会为同一源代码产生截然不同的输出。即使对于看似简单的程序也是如此。

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

在x86的情况下,我假设这就是您在这里所说的,这些天被微编码了,对于好的代码还是不好的代码,实际上并没有答案,每个处理器家族都在改变他们的勇气,而以前很快的速度却很慢而现在的速度在旧处理器上却很慢。因此,对于像gcc这样的随着新内核而不断发展的编译器,优化既可以是所有x86通用的,也可以是特定于特定系列的(尽管进行了最大优化,但仍会产生不同的代码)。

随着您对反汇编的新兴趣,您将继续看到异同,并找出可以编译同一代码的几种不同方式。差异是可以预期的,即使对于琐碎的程序也是如此。我鼓励您尝试尽可能多的编译器。即使在gcc系列2.x,3.x,4.x中,以及构建它的不同方法也会导致产生不同的代码,尽管它们可能被认为是同一编译器。

仁者见仁,输出好坏。使用调试器的人们将希望它们的代码是可步进的,并且它们的变量是可观察的(以书面代码顺序)。这导致非常大,笨重且缓慢的代码(尤其是x86)。而且,当您进行发行时,最终得到的是一个完全不同的程序,到目前为止,您已经花费了零时间进行调试。此外,为了提高性能,您还冒着编译器优化自己想要做的事情的风险(在上面的示例中,即使进行了较小的优化,也不会分配任何变量,也不会执行任何代码)。或更糟糕的是,您暴露了编译器中的错误,而程序却根本无法正常工作(这就是为什么不建议将-O3用于gcc的原因)。您和/或您会发现C标准中有很多地方是其解释由实现定义。

未优化的代码更容易编译,因为它更加明显。在您的示例中,期望的是在栈上分配一个变量,设置了某种栈指针安排,立即数1最终写入该位置,清理了栈并返回了函数。编译器更难出错,程序更有可能按预期工作。检测和删除无效代码是优化和
那就是风险所在。通常,风险值得得到回报。但这取决于用户,情人眼中的情人。

底线是简短的答案。预期会有差异(甚至会有巨大差异)。默认编译选项因编译器而异。试用编译/优化选项和不同的编译器,然后继续反汇编程序,以便更好地了解所用的语言和编译器。到目前为止,您在正确的轨道上。对于borland输出,它检测到您的程序不执行任何操作,不使用输入变量,不使用返回变量,也不与局部变量相关,并且不使用全局变量或函数资源之外的其他变量。整数a和立即数的赋值是无效代码,一个好的优化程序实际上将删除/忽略这两行代码。因此,它麻烦设置一个堆栈框架,然后清理它不需要做的事情,然后返回。 gcc似乎正在建立一个异常处理程序,即使它不需要,它也很好,开始优化或使用main()以外的函数名,您应该会看到不同的结果。

关于c - GCC和Borland在反汇编C代码方面的区别?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4355610/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com