编译器可以生成无用的汇编代码吗?

时间:2017-11-21 19:50:18

标签: c gcc assembly x86

我试图找到从c程序生成的汇编代码的含义。这是C中的程序:

int* a = &argc;
int b = 8;
a = &b;

以下是使用说明生成的汇编代码。有一部分我不明白:

主要序言:

leal    4(%esp), %ecx
andl    $-16, %esp
pushl   -4(%ecx)
pushl   %ebp
movl    %esp, %ebp
pushl   %ecx
subl    $36, %esp

在%eax中加载argc的地址:

movl    %ecx, %eax

我没有得到的部分:

movl    4(%eax), %edx
movl    %edx, -28(%ebp)

Stack-Smashing Protector代码(设置):

movl    %gs:20, %ecx
movl    %ecx, -12(%ebp)
xorl    %ecx, %ecx

在a和b中加载值(参见main.c):

movl    %eax, -16(%ebp)
movl    $8, -20(%ebp)

修改a(a =& b)的值:

leal    -20(%ebp), %eax
movl    %eax, -16(%ebp)

Stack-Smashing Protector代码(验证堆栈是否正常):

movl    $0, %eax
movl    -12(%ebp), %edx
xorl    %gs:20, %edx
je  .L7
call    __stack_chk_fail

如果堆栈是Ok:

.L7:
    addl    $36, %esp
    popl    %ecx
    popl    %ebp
    leal    -4(%ecx), %esp
    ret

所以我不理解的部分是修改-28(%ebp)中的值,这是一个从未使用过的地址。有人知道为什么会生成这个部分吗?

1 个答案:

答案 0 :(得分:6)

查看编译器功能的好方法。我假设你有一个名为main.c的文件:

int main(int argc, char **argv) 
{
    int* a = &argc;
    int b = 8;
    a = &b;
}

使用调试信息编译到目标文件:

$ gcc -c -g main.c

查看程序集:

$ objdump -S main.o

main.o:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <main>:
int main(int argc, char **argv)
{
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   89 7d ec                mov    %edi,-0x14(%rbp)
   7:   48 89 75 e0             mov    %rsi,-0x20(%rbp)
    int* a = &argc;
   b:   48 8d 45 ec             lea    -0x14(%rbp),%rax
   f:   48 89 45 f8             mov    %rax,-0x8(%rbp)
    int b = 8;
  13:   c7 45 f4 08 00 00 00    movl   $0x8,-0xc(%rbp)
    a = &b;
  1a:   48 8d 45 f4             lea    -0xc(%rbp),%rax
  1e:   48 89 45 f8             mov    %rax,-0x8(%rbp)
  22:   b8 00 00 00 00          mov    $0x0,%eax
}
  27:   5d                      pop    %rbp
  28:   c3                      retq   

然后对完全优化做同样的事情:

$ gcc -c -g -O3 main.c 

再次查看程序集:

$ objdump -S main.o

main.o:     file format elf64-x86-64


Disassembly of section .text.startup:

0000000000000000 <main>:
int main(int argc, char **argv)
{
    int* a = &argc;
    int b = 8;
    a = &b;
}
   0:   31 c0                   xor    %eax,%eax
   2:   c3                      retq   

所以答案是肯定的。编译器可以生成不需要的指令。这就是你打开优化的原因。当它们被关闭时,编译器会以一种非常通用的方式完成它的工作而不用考虑。例如,它为未使用的变量保留空间。