为什么GCC焊盘function与NOP?

我已经和C合作了一段时间,最近才开始进入ASM。 当我编译一个程序时:

int main(void) { int a = 0; a += 1; return 0; } 

objdump反汇编有代码,但在ret之后:

 ... 08048394 <main>: 8048394: 55 push %ebp 8048395: 89 e5 mov %esp,%ebp 8048397: 83 ec 10 sub $0x10,%esp 804839a: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%ebp) 80483a1: 83 45 fc 01 addl $0x1,-0x4(%ebp) 80483a5: b8 00 00 00 00 mov $0x0,%eax 80483aa: c9 leave 80483ab: c3 ret 80483ac: 90 nop 80483ad: 90 nop 80483ae: 90 nop 80483af: 90 nop ... 

从我所了解到的情况来看,nops什么都不做,而且之后也不会被执行。

我的问题是:为什么要麻烦? ELF(linux-x86)不能用任何大小的.text节(+ main)工作吗?

我会感谢任何帮助,只是想学习。

首先, gcc并不总是这样做。 填充由-falign-functions控制,由-O2-O3自动打开:

-falign-functions
-falign-functions=n

将函数的开始alignment到大于n的下一个2的幂,跳至n个字节。 例如, -falign-functions=32将函数与下一个32字节的边界alignment,但是-falign-functions=24将与下一个32字节的边界alignment, -falign-functions=24是这可以通过跳过23个字节或更less来完成。

-fno-align-functions-falign-functions=1是等价的,意味着函数不会alignment。

当n是2的幂时,一些汇编器只支持这个标志; 在这种情况下,它被取整了。

如果n未指定或为零,请使用与机器相关的默认值。

在-O2,-O3级别启用。

这样做可能有多种原因,但x86上的主要原因可能是这样的:

大多数处理器在alignment的16字节或32字节块中获取指令。 将关键循环条目和子例程条目alignment16以便最小化代码中的16字节边界的数量是有利的。 或者,在关键循环入口或子程序入口之后,确保前几个指令中没有16字节的边界。

(引自Agner Fog的“用汇编语言优化子程序”)

编辑:这是一个演示填充的例子:

 // align.c int f(void) { return 0; } int g(void) { return 0; } 

当使用默认设置的gcc 4.4.5编译时,我得到:

 align.o: file format elf64-x86-64 Disassembly of section .text: 0000000000000000 <f>: 0: 55 push %rbp 1: 48 89 e5 mov %rsp,%rbp 4: b8 00 00 00 00 mov $0x0,%eax 9: c9 leaveq a: c3 retq 000000000000000b <g>: b: 55 push %rbp c: 48 89 e5 mov %rsp,%rbp f: b8 00 00 00 00 mov $0x0,%eax 14: c9 leaveq 15: c3 retq 

指定-falign-functions给出:

 align.o: file format elf64-x86-64 Disassembly of section .text: 0000000000000000 <f>: 0: 55 push %rbp 1: 48 89 e5 mov %rsp,%rbp 4: b8 00 00 00 00 mov $0x0,%eax 9: c9 leaveq a: c3 retq b: eb 03 jmp 10 <g> d: 90 nop e: 90 nop f: 90 nop 0000000000000010 <g>: 10: 55 push %rbp 11: 48 89 e5 mov %rsp,%rbp 14: b8 00 00 00 00 mov $0x0,%eax 19: c9 leaveq 1a: c3 retq 

这样做是为了将下一个函数alignment8,16或32字节的边界。

从A.Fog的“用汇编语言优化子程序”

11.5代码alignment

大多数微处理器在alignment的16字节或32字节块中获取代码。 如果importantsubroutine入口或跳转标签碰巧接近16字节块的末尾,那么微处理器在获取该代码块时将只获得几个有用的代码字节。 在标签之后的第一个指令可以解码之前,它可能还要读取下一个16字节。 这可以通过将重要的子程序条目和循环条目alignment16来避免。

[…]

在子程序入口之前,根据需要调整子程序入口的次数,使得地址可以被8,16,32或64除尽。

据我所知,指令在cpu和不同的cpu模块(加载器,解码器等)中stream水线处理后续指令。 当RET指令正在执行时,很less有下一个指令已经加载到CPUpipe道中。 这是一个猜测,但你可以从这里开始挖掘,如果你发现(也许是安全的NOP的具体数量,请分享你的发现。