我正在玩弄可执行文件/编译器/程序集——全部。
void foo(void){
}
在编译器生成的 asm 中,我碰巧注意到函数序言和结尾(Godbolt 编译器资源管理器)之间没有操作:
foo:
push rbp
mov rbp, rsp
nop ## <--- this line
pop rbp
ret
我用gcc -S
(没有优化)编译它;编译时它消失了-O1
,所以我的理论是它是用于调试的。
但是我不清楚它的目的。即使对于调试,无操作(根据定义)对程序的行为方式没有任何影响。
那么为什么会有呢?
进一步的结果
NOP 在 GCC5 中是新的;GCC4.9.4 中不存在
NOP 在任何实际返回值的函数中都消失了。(从函数末尾掉下来
int
仍然会产生 anop
,包括main
在 C89 模式下没有隐式返回 0 的 for )。
int bar(int x){
return x;
}
bar:
push rbp
mov rbp, rsp
mov DWORD PTR [rbp-4], edi
mov eax, DWORD PTR [rbp-4]
pop rbp
ret
- 拥有非空函数体并不能避免 NOP:
int sink;
void baz(){
sink = 0;
}
baz:
push rbp
mov rbp, rsp
mov DWORD PTR sink[rip], 0
nop ## <--- Still present
pop rbp
ret
int main(void){
//return 0; // -std=gnu89 to not implicitly return 0; produces a nop
}
# GCC11 defaults to -std=gnu17 so return 0 is implicit
main:
push rbp
mov rbp, rsp
mov eax, 0 # replaced with a NOP with -std=gnu89
pop rbp
ret