我已经和C一起工作了一段时间,最近才开始进入ASM.当我编译一个程序时:
int main(void)
{
int a = 0;
a += 1;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
objdump反汇编有代码,但在ret之后nops:
...
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
...
Run Code Online (Sandbox Code Playgroud)
从我学到的东西,什么都不做,因为ret之后甚至都不会被执行.
我的问题是:为什么要这么麻烦?ELF(linux-x86)无法使用任何大小的.text段(+ main)吗?
我很感激任何帮助,只是想学习.
NPE*_*NPE 86
首先,gcc
并不总是如此.填充由控制-falign-functions
,这是由自动开启-O2
和-O3
:
-falign-functions
-falign-functions=n
将函数的开头与下一个二次幂对齐大于
n
,跳过最多n
字节.例如,-falign-functions=32
将函数与下一个32字节边界-falign-functions=24
对齐,但只有在跳过23个字节或更少的情况下才能对齐下一个32字节边界.
-fno-align-functions
并且-falign-functions=1
是等价的并且意味着函数不会对齐.当n是2的幂时,一些汇编器仅支持该标志; 在这种情况下,它被四舍五入.
如果未指定n或为零,则使用与机器相关的默认值.
在-O2,-O3级别启用.
执行此操作可能有多种原因,但x86上的主要原因可能是:
大多数处理器在对齐的16字节或32字节块中获取指令.将关键循环条目和子例程条目对齐16可能是有利的,以便最小化代码中16字节边界的数量.或者,确保在关键循环条目或子例程条目之后的前几条指令中没有16字节边界.
(引自Agner Fog的"用汇编语言优化子程序".)
编辑:这是一个演示填充的示例:
// align.c
int f(void) { return 0; }
int g(void) { return 0; }
Run Code Online (Sandbox Code Playgroud)
使用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
Run Code Online (Sandbox Code Playgroud)
指定-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
Run Code Online (Sandbox Code Playgroud)
ham*_*ene 13
这样做是为了将下一个函数与8,16或32字节边界对齐.
来自A.Fog的"用汇编语言优化子程序":
11.5代码对齐
大多数微处理器在对齐的16字节或32字节块中获取代码.如果一个重要的子例程入口或跳转标签碰巧接近一个16字节块的末尾,则在获取该代码块时,微处理器将只获得一些有用的代码字节.在它可以解码标签之后的第一条指令之前,它也可能需要获取接下来的16个字节.通过将重要的子例程条目和循环条目对齐16可以避免这种情况.
[...]
对齐子例程条目就像在子例程输入之前根据需要放置尽可能多的NOP一样简单,以使地址可以根据需要被8,16,32或64整除.
据我所知,指令在cpu中流水线化,不同的cpu块(加载器,解码器等)处理后续指令.在RET
执行指令时,很少有下一条指令已加载到cpu管道中.这是一个猜测,但你可以开始在这里挖掘,如果你发现(也许NOP
是安全的具体数量,请分享你的发现.
归档时间: |
|
查看次数: |
7519 次 |
最近记录: |