我试图在gdb中看到一个简单C程序的分解二进制文件。
C程序:
int main(){
int i = 2;
if (i == 0){
printf("YES, it's 0!\n");
}else{
printf("NO");
}
return 0;
}拆卸指令:
0x0000000100401080 <+0>: push rbp
0x0000000100401081 <+1>: mov rbp,rsp
0x0000000100401084 <+4>: sub rsp,0x30
0x0000000100401088 <+8>: call 0x1004010e0 <__main>
0x000000010040108d <+13>: mov DWORD PTR [rbp-0x4],0x2
0x0000000100401094 <+20>: cmp DWORD PTR [rbp-0x4],0x0
0x0000000100401098 <+24>: jne 0x1004010ab <main+43>
0x000000010040109a <+26>: lea rax,[rip+0x1f5f] # 0x100403000
0x00000001004010a1 <+33>: mov rcx,rax
0x00000001004010a4 <+36>: call 0x100401100 <puts>
0x00000001004010a9 <+41>: jmp 0x1004010ba <main+58>
0x00000001004010ab <+43>: lea rax,[rip+0x1f5b] # 0x10040300d
0x00000001004010b2 <+50>: mov rcx,rax
0x00000001004010b5 <+53>: call 0x1004010f0 <printf>
0x00000001004010ba <+58>: mov eax,0x0
0x00000001004010bf <+63>: add rsp,0x30
0x00000001004010c3 <+67>: pop rbp
0x00000001004010c4 <+68>: ret
0x00000001004010c5 <+69>: nop我想这个指令,
0x00000001004010a4 <+36>: call 0x100401100 <puts>指点
printf("YES, it's 0!\n");现在让我们假设是的,那么我怀疑为什么在这里调用<push>,而在0x00000001004010b5 <+53>: call 0x1004010f0 <printf>中调用<printf>?
发布于 2022-02-18 15:26:33
使用C标准中定义的语义,printf("YES, it's 0!\n")产生与puts("YES, it's 0!")相同的输出,这可能更有效,因为不需要对字符串进行替换分析。
由于不使用返回值,编译器可以将printf调用替换为对puts的等效调用。
这种类型的优化很可能是为了减少经典K&R程序hello.c的可执行大小而引入的。将printf替换为puts避免了链接比puts大得多的printf代码。在您的例子中,这种优化是适得其反的,因为puts和printf都是链接的,但是现代系统使用动态链接,因此尝试以这种方式减少可执行文件的大小已经不再有意义了。
您可以在此戈德波特编译器浏览器页面上播放编译器设置,以观察编译器行为:
-O0,gcc也执行printf / puts替换,但是clang没有,这两个编译器都为两个调用生成代码,而不是优化测试if (i == 0),这在禁用优化时是可以的。我怀疑gcc团队甚至在没有优化的情况下也无法抵制对尺寸基准的偏见。-O1和更高版本,这两个编译器只为else分支生成代码,调用printf。"N",则printf将转换为对putchar的调用,这是另一个优化。发布于 2022-02-18 15:24:44
这是个优化。
使用没有格式说明符和尾随换行符的格式字符串调用printf,相当于使用删除尾随换行符的相同字符串调用puts。
由于printf有很多处理格式说明符的逻辑,但是puts只编写给定的字符串,后者将更快。因此,在第一次调用printf的情况下,编译器会看到这种等价性,并进行适当的替换。
https://stackoverflow.com/questions/71175676
复制相似问题