编译选项 -fomit-frame-pointer
与 -fno-omit-frame-pointer
对汇编及调试影响
栈
- 栈是向下生长的。所谓向下生长是指从 内存高地址 -> 低地址 的路径延伸
- 有两个重要的指针用于维护栈信息
- ESP:栈指针寄存器(extended stack pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的栈顶。 由于栈的地址大小是从上到下从大到小,所以ESP指在栈的最底端
- EBP:基址指针寄存器(extended base pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的底部。指在栈的最顶端
man
手册中对 -fomit-frame-pointer
描述如下
Don’t keep the frame pointer in a register for functions that don’t need one. This avoids the instructions to save, set up and restore frame pointers; it also makes an extra register available in many functions. It also makes debugging impossible on some machines.
Enabled at levels -O, -O2, -O3, -Os.
当使用 -O0
时不会开启上述选项,编译器会在栈空间中保存额外信息用于调试,即 frame pointer
Example
int add(int a, int b)
{
return a + b ;
}
汇编对比如下
$ gcc -fno-omit-frame-pointer test.c
$ csky-elf-objdump -dS a.out
00008934 <add>:
8934: 2470 subi r0, r0, 8
8936: 9800 st r8, (r0, 0)
8938: 2470 subi r0, r0, 8
893a: 1208 mov r8, r0
893c: 9208 st r2, (r8, 0)
893e: 9318 st r3, (r8, 4)
8940: 8608 ld r6, (r8, 0)
8942: 8718 ld r7, (r8, 4)
8944: 1c67 addu r7, r7, r6
8946: 1272 mov r2, r7
8948: 1280 mov r0, r8
894a: 2070 addi r0, r0, 8
894c: 8640 ld r6, (r0, 16)
894e: 8800 ld r8, (r0, 0)
8950: 2070 addi r0, r0, 8
8952: 00cf jmp r15
$ gcc -fomit-frame-pointer test.c
$ objdump -dS a.out
00008934 <add>:
8934: 2470 subi r0, r0, 8
8936: 9200 st r2, (r0, 0)
8938: 9310 st r3, (r0, 4)
893a: 8600 ld r6, (r0, 0)
893c: 8710 ld r7, (r0, 4)
893e: 1c67 addu r7, r7, r6
8940: 1272 mov r2, r7
8942: 2070 addi r0, r0, 8
8944: 8640 ld r6, (r0, 16)
8946: 00cf jmp r15
可以看到在栈上多开辟一些空间用于存储 frame pointer
不开启优化时更能反映出压栈及出栈的操作,在这个过程中不会有 fp
操作
00008934 <add>:
# 开辟栈空间 8B
8934: 2470 subi r0, r0, 8
# 第一个参数 r2 入栈
8936: 9200 st r2, (r0, 0)
# 第二个参数 r3 入栈
8938: 9310 st r3, (r0, 4)
# 加载并执行子程序 a + b
893a: 8600 ld r6, (r0, 0)
893c: 8710 ld r7, (r0, 4)
893e: 1c67 addu r7, r7, r6
# 赋值给 r2 作为函数返回值
8940: 1272 mov r2, r7
# 释放栈
8942: 2070 addi r0, r0, 8
8944: 8640 ld r6, (r0, 16)
# 返回调用函数栈
8946: 00cf jmp r15