为什么GCC产生的机器码包含nop指令

16

每次我运行 objdump -d 命令时,我总是看到一堆 nop 指令(即不执行任何操作的指令)的汇编代码。

例如,看看这个程序:

#include <stdio.h>
#include <math.h>

int main()
{
    printf("Hello World!\n");
    printf("cos:  %f\n", cos(1));
    return 1;
}

例如,objdump在入口点末尾有两个nop。

0000000000400450 <_start>:
400450: 31 ed                   xor    %ebp,%ebp
400452: 49 89 d1                mov    %rdx,%r9
400455: 5e                      pop    %rsi
400456: 48 89 e2                mov    %rsp,%rdx
400459: 48 83 e4 f0             and    $0xfffffffffffffff0,%rsp
40045d: 50                      push   %rax
40045e: 54                      push   %rsp
40045f: 49 c7 c0 00 06 40 00    mov    $0x400600,%r8
400466: 48 c7 c1 70 05 40 00    mov    $0x400570,%rcx
40046d: 48 c7 c7 34 05 40 00    mov    $0x400534,%rdi
400474: e8 bf ff ff ff          callq  400438 <__libc_start_main@plt>
400479: f4                      hlt    
40047a: 90                      nop
40047b: 90                      nop 

这只是其中许多例子之一,但你已经了解到了。为什么C代码要以这种方式编译?谢谢您的帮助。

2个回答

19

为了使下一个函数对齐到4字节边界,添加了nop。 (请注意,最后一个nop之后的地址将是40047c,可被4整除)


在x86_64上,我也注意到一个函数中间有一个4字节的nop(在“call”之前):0f 1f 40 00 nop DWORD PTR [rax+0x0] 你知道这是做什么的吗?发生在“-O3”。 - Kerrek SB
1
@Kerrek 如果我没记错的话,这会告诉处理器清除其缓存并重新加载指向地址处的数据。在这种情况下,处理器将使用[rax]处的数据(我假设这是调用跳转到的地址)来清除其缓存。这使得处理器能够在跳转之前开始重新加载正确的数据到其缓存中。 - Cole Tobin

9

很多时候,这些只是用来填充的,以便后续的内容再次从单词或边界开始,因为访问未对齐的任意代码对于CPU来说更加昂贵。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接