为什么为我的C程序生成的机器代码与本书中给出的机器代码不同?

时间:2016-06-25 18:12:06

标签: c gcc assembly x86

我在Richard Blum的专业汇编语言一书中找到了这个文本。

  

编译步骤转换文本编程语言语句   进入执行申请所需的指令代码   功能。每个HLL代码行都与一个或多个匹配   更多与特定处理器有关的指令代码   应用程序将运行。例如,简单的HLL代码

int main()
{
    int i = 1;
    exit(0);
}
     

编译为以下IA-32指令代码:

55
89 E5
83 EC 08
C7 45 FC 01 00 00 00
83 EC 0C
6A 00
E8 D1 FE FF FF

但是当我自己尝试这个程序时,我无法重现这些结果。

首先是关于我的系统和编译器的一些细节。

$ cat /etc/debian_version 
8.3
$ uname -a
Linux debian1 3.16.0-4-amd64 #1 SMP Debian 3.16.7-ckt20-1+deb8u2 (2016-01-02) x86_64 GNU/Linux
$ gcc --version
gcc (Debian 4.9.2-10) 4.9.2
Copyright (C) 2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$ dpkg -l gcc-multilib
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend
|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
||/ Name                                      Version                   Architecture              Description
+++-=========================================-=========================-=========================-========================================================================================
ii  gcc-multilib                              4:4.9.2-2                 amd64                     GNU C compiler (multilib files)

这是我写的程序。

$ cat foo.c
#include <stdlib.h>

int main()
{
    int i = 1;
    exit(0);
}

以下是仅在编译后得到的结果。

$ gcc -m32 -c foo.c
$ objdump -d foo.o 

foo.o:     file format elf32-i386


Disassembly of section .text:

00000000 <main>:
   0:   8d 4c 24 04             lea    0x4(%esp),%ecx
   4:   83 e4 f0                and    $0xfffffff0,%esp
   7:   ff 71 fc                pushl  -0x4(%ecx)
   a:   55                      push   %ebp
   b:   89 e5                   mov    %esp,%ebp
   d:   51                      push   %ecx
   e:   83 ec 14                sub    $0x14,%esp
  11:   c7 45 f4 01 00 00 00    movl   $0x1,-0xc(%ebp)
  18:   83 ec 0c                sub    $0xc,%esp
  1b:   6a 00                   push   $0x0
  1d:   e8 fc ff ff ff          call   1e <main+0x1e>

以下是我在编译和链接后得到的结果。

$ gcc -c foo.c
$ objdump -d a.out | grep -A15 "<main>"
080483fb <main>:
 80483fb:   8d 4c 24 04             lea    0x4(%esp),%ecx
 80483ff:   83 e4 f0                and    $0xfffffff0,%esp
 8048402:   ff 71 fc                pushl  -0x4(%ecx)
 8048405:   55                      push   %ebp
 8048406:   89 e5                   mov    %esp,%ebp
 8048408:   51                      push   %ecx
 8048409:   83 ec 14                sub    $0x14,%esp
 804840c:   c7 45 f4 01 00 00 00    movl   $0x1,-0xc(%ebp)
 8048413:   83 ec 0c                sub    $0xc,%esp
 8048416:   6a 00                   push   $0x0
 8048418:   e8 c3 fe ff ff          call   80482e0 <exit@plt>
 804841d:   66 90                   xchg   %ax,%ax
 804841f:   90                      nop

08048420 <__libc_csu_init>:

如何重现作者在书中提供的结果?

1 个答案:

答案 0 :(得分:4)

书中没有的额外说明是:

80483fb:   8d 4c 24 04             lea    0x4(%esp),%ecx
80483ff:   83 e4 f0                and    $0xfffffff0,%esp
8048402:   ff 71 fc                pushl  -0x4(%ecx)
8048408:   51                      push   %ecx
8048409:   83 ec 14                sub    $0x14,%esp

...

804841d:   66 90                   xchg   ax,ax
804841f:   90                      nop

第一对线将堆栈对齐到16字节边界。这样可以提高性能(参数不能跨越缓存行边界)并允许使用仅在16位对齐地址上运行的SIMD指令。

最后的xchg %ax, %ax是一个2字节的NOP。 nop的3个字节无关紧要,因为它们无论如何都无法访问。它们用于将__libc_csu_init函数填充到合适的对齐位置。

至于装配的不同之处,装配是一种编程语言,通常有多种方法可以做。您不能指望C程序在编译器,相同编译器的版本或相同版本的配置之间提供相同的输出。

在您的特定情况下,16位堆栈对齐归因于-mpreferred-stack-boundary=4和归因于-falign-functions的3字节nop

当您调用gcc时,这些参数被配置为默认参数。直接或-O2或类似的要求。