GCC生成的汇编代码

来源:互联网 发布:java socket 客户端 编辑:程序博客网 时间:2024/05/19 23:11

假设我们写了一个C代码文件 code.c包含下面代码:

int accum = 0;

int sum(int x, int y)
{
    int t = x + y;
    accum += t;
    return t;
}

这是用echo命令输入源码的效果,简单的就是最好的:)
 


一、查看GCC生成的汇编代码
在命令行上用“-S”选项,就能看到C编译器产生的汇编代码:
 
gcc -S code.c         (注意:这里是大写的-S,如果用小写gcc会说找不到main函数)
 
这段汇编代码没有经过优化:
 
.file "code.c"
.globl _accum
 .bss
 .align 4
_accum:
 .space 4
 .text
.globl _sum
 .def _sum; .scl 2; .type 32; .endef
_sum:
 pushl %ebp
 movl %esp, %ebp
 subl $4, %esp                  # 为局部变量t在栈帧上分配空间
 movl 12(%ebp), %eax    # %eax <- y
 addl 8(%ebp), %eax       # %eax <- x + y
 movl %eax, -4(%ebp)     # t <- x +y
 movl -4(%ebp), %eax     # %eax <- t
 addl %eax, _accum        # _accum <- t + _accum
 movl -4(%ebp), %eax     # %eax <- t
 leave                                 # 平衡堆栈: %esp <- %ebp , popl %ebp
 ret                                      
 
 
下面是使用“-O2”选项开启二级优化的效果:
 
gcc -O2 -S code.c
 
 .file "code.c"
.globl _accum
 .bss
 .align 4
_accum:
 .space 4
 .text
 .p2align 4,,15                    # 使下一条指令的地址从16的倍数处开始,
.globl _sum                        # 最多浪费15个字节
 .def _sum; .scl 2; .type 32; .endef
_sum:
 pushl %ebp                       # 保存原%ebp   
 movl %esp, %ebp        
 movl 12(%ebp), %eax     # %eax <- y
 movl 8(%ebp), %edx       # %edx <- x
 popl %ebp                        # 恢复原%ebp 
 addl %edx, %eax             # %eax <- x + y
 addl %eax, _accum         # _accum <- _accum + x + y
 ret
 
 
GCC产生的汇编代码有点难读,它包含一些我们不关心的信息。所有以 "." 开头的行都是指导汇编器和链接器的命令,称为“汇编器命令”。
 
 
代码中已经除去了所有关于局部变量名或数据类型的信息,但我们还是看到了一个对全局变量_accum的引用,这是因为编译器还不能确定这个变量会放在存储中的哪个位置。
二、用GDB查看目标文件的字节表示
 
  首先,我们用反汇编器来确定函数sum的代码长度是19字节。然后我们在文件code.o上运行GNU调试工具GDB,输入命令:
(gdb) x/19xb sum
这条命令告诉GDB检查(简写为"x")19个以十六进制格式表示的字节。
三、反汇编目标文件
 
  在Linux系统中,带 "-d" 命令行选项调用OBJDUMP可以完成这个任务:
  objdump -d code.o
 
从这里可以看出函数sum的代码长度正好是19字节。
四、生成实际可执行的代码
  这需要对一组目标文件运行链接器,而这一组目标代码文件中必须包含有一个Main函数。在 main.c 中有这样的函数:
  int main()
  {
       return sum(1,2);
  }
然后,我们用如下方法生成可执行文件:
gcc -O2 -o prog code.o main.c
再反汇编:
objdump -d prog
00401050 <_sum>:
  401050: 55                              push   %ebp
  401051: 89 e5                        mov    %esp,%ebp
  401053: 8b 45 0c                   mov    0xc(%ebp),%eax
  401056: 8b 55 08                   mov    0x8(%ebp),%edx
  401059: 5d                              pop    %ebp
  40105a: 01 d0                        add    %edx,%eax
  40105c: 01 05 10 20 40 00  add    %eax,0x402010
  401062: c3                              ret 
  这段代码与code.c反汇编产生的代码几乎完全一样。一个主要的区别是左边列出的地址不同。第二个不同之处在于链接器终于确定了存贮全局变量accum的地址。地址由原来的0x0变成了现在的0x402010
 
 
 
 
相关知识点:
 
原文出处:http://sourceware.org/binutils/docs-2.18/as/index.html 
 

7.3 .align abs-expr, abs-expr, abs-expr
Pad the location counter (in the current subsection) to a particular storage b
oundary. The first expression (which must be absolute) is the alignment requir
ed, as described below.
填充数据,使得位置计数器向后移动到达一个特殊的存储器边界。第一个表达式(其值必须为立既数)
是必须的,这会在下面描述。
The second expression (also absolute) gives the fill value to be stored in the
padding bytes. It (and the comma) may be omitted. If it is omitted, the paddi
ng bytes are normally zero. However, on some systems, if the section is marked
as containing code and the fill value is omitted, the space is filled with no
-op instructions.
第二个表达式(其值也必须为立既数)给出将被填充的值。此表达式(和逗号)可以省略。如果省略,
则通常用0填充。但是在某些系统中,如果一个段被标记为包含代码并且省略了填充的值,则使用no-op
指令进行填充。
The third expression is also absolute, and is also optional. If it is present,
it is the maximum number of bytes that should be skipped by this alignment di
rective. If doing the alignment would require skipping more bytes than the spe
cified maximum, then the alignment is not done at all. You can omit the fill v
alue (the second argument) entirely by simply using two commas after the requi
red alignment; this can be useful if you want the alignment to be filled with
no-op instructions when appropriate.
第三个表达式的值同样必须为立既数,并且也是可选项。此项表示这个对齐指令可以跳过的
最大的字节数。如果进行对齐操作需要跳过的字节数大于指定的数量,则对齐操作根本不会
被执行。通过在第一个参数后面简单的使用两个逗号,你完全可以省略填充值(第二个参数);
如果你希望对齐操作在适当的时侯用"no-op"指令进行填充,这个方法将非常有用。
The way the required alignment is specified varies from system to system. For
the a29k, hppa, m68k, m88k, w65, sparc, and Hitachi SH, and i386 using ELF for
mat, the first expression is the alignment request in bytes. For example ‘.al
ign 8’ advances the location counter until it is a multiple of 8. If the loca
tion counter is already a multiple of 8, no change is needed.
对于不同的系统,对齐的方式有所不同。a29k, hppa, m68k, m88k, w65, spar和Hitachi以及
使用ELF格式的i386系统,第一个表达式是需要对齐的字节数。例如".align 8"指令增加位置计数器
的值使其为8的倍数,如果已经是8的倍数则无需改变。
For other systems, including the i386 using a.out format, and the arm and stro
ngarm, it is the number of low-order zero bits the location counter must have
after advancement. For example ‘.align 3’ advances the location counter unti 
l it a multiple of 8. If the location counter is already a multiple of 8, no c
hange is needed.
其它某些系统,包括使用a.out格式的i386 以及arm和strongarm,第一个表达式是完成移动后,
位置计数器的值的低位中0的数量。例如,".align 3"指令增加计数器的值到8的倍数(译注:
数值的低3位总是三个0)。
This inconsistency is due to the different behaviors of the various native ass
emblers for these systems which GAS must emulate. GAS also provides .balign an
d .p2align directives, described later, which have a consistent behavior acros
s all architectures (but are specific to GAS).
因为GAS必须模仿这些系统中各种汇编器的不同行为,由此导致了这种差异。GAS 也提供了.balign
和.p2align 指令,这在后面描述,它们在所有的体系结构中拥有一致的行为。
 
7.54 .p2align[wl] abs-expr, abs-expr, abs-expr
Pad the location counter (in the current subsection) to a particular storage b
oundary. The first expression (which must be absolute) is the number of low-or
der zero bits the location counter must have after advancement. For example ‘
.p2align 3’ advances the location counter until it a multiple of 8. If the lo
cation counter is already a multiple of 8, no change is needed.
填充数据,使得位置计数器向后移动到达一个特殊的存储器边界。第一个表达式(其值必须为立既数)
是计数器值的低位中0的数量。例如:".p2align 3"将使得计数器移动到地址为8的倍数的位置,
如果地址已经是8的倍数则无需移动。
The second expression (also absolute) gives the fill value to be stored in the
padding bytes. It (and the comma) may be omitted. If it is omitted, the paddi
ng bytes are normally zero. However, on some systems, if the section is marked
as containing code and the fill value is omitted, the space is filled with no
-op instructions.
第二个表达式(其值也必须为立既数)给出将被填充的值。此表达式(和逗号)可以省略。如果省略,
则通常用0填充。但是在某些系统中,如果一个段被标记为包含代码并且省略了填充的值,则使用no-op
指令进行填充。

The third expression is also absolute, and is also optional. If it is present,
it is the maximum number of bytes that should be skipped by this alignment di
rective. If doing the alignment would require skipping more bytes than the spe
cified maximum, then the alignment is not done at all. You can omit the fill v
alue (the second argument) entirely by simply using two commas after the requi
red alignment; this can be useful if you want the alignment to be filled with
no-op instructions when appropriate.
第三个表达式的值同样必须为立既数,并且也是可选项。此项表示这个对齐指令可以跳过的
最大的字节数。如果进行对齐操作需要跳过的字节数大于指定的数量,则对齐操作根本不会
被执行。通过在第一个参数后面简单的使用两个逗号,你完全可以省略填充值(第二个参数)。
如果你希望对齐操作在适当的时侯用"no-op"指令进行填充,这个方法将非常有用。
The .p2alignw and .p2alignl directives are variants of the .p2align directive.
The .p2alignw directive treats the fill pattern as a two byte word value. The
.p2alignl directives treats the fill pattern as a four byte longword value. F
or example, .p2alignw 2,0x368d will align to a multiple of 4. If it skips two
bytes, they will be filled in with the value 0x368d (the exact placement of th
e bytes depends upon the endianness of the processor). If it skips 1 or 3 byte
s, the fill value is undefined.
.p2alignw 和 .p2alignl是.p2align指令的两种变形。.p2alignw以一个2字节大小的数据进行
填充。.p2alignl指令以一个4字节大小的数据填充。例如:指令.p2alignw 2,0x368d 将地址对
齐到4的倍数。如果此操作跳过了两字节,则这两个字节是以数值0x368d 进行填充的(这些字节的
准确布局依赖于处理器的字节顺序)。如果跳过的字节数是1或3,那么填充的值是未定义的。
 
7.65 .scl class 
Set the storage-class value for a symbol. This directive may only be used insi
de a .def/.endef pair. Storage class may flag whether a symbol is static or ex
ternal, or it may record further symbolic debugging information.
The ‘.scl’ directive is primarily associated with COFF output; when configur
ed to generate b.out output format, as accepts this directive but ignores it.
设置一个符号的存储类型值。这条指令只能被用在一对.def/.endef里面。存储类型可以表明符号是
静态(static)或外部(external)类型,或者可能进一步记录符号的调试信息。
".scl"指令主要是和COFF输出有关,当设置成产生b.out输出格式时,汇编器可以接受这条指令,
但它将被忽略。

7.84 .type int (COFF version)
This directive, permitted only within .def/.endef pairs, records the integer i
nt as the type attribute of a symbol table entry.
‘.type’ is associated only with COFF format output; when as is configured fo
r b.out output, it accepts this directive but ignores it.
这条命令仅允许用在一对.def/.endef之内。记录整数int 作为一个符号表条目的类型属性。
".type"命令仅与COFF输出格式相关,当汇编器被设置成输出 a.out文件时,汇编器可以接受
这条命令,但它将会被忽略。
 
原创粉丝点击