2015-06-09 79 views
1

我想了解这个简单C程序的汇编。了解简单C程序的汇编

#include<stdio.h> 
#include<unistd.h> 
#include<fcntl.h> 
#include<string.h> 
void foobar(char *a){ 
    char c = a[0]; 
} 
int main(){ 
    int fd = open("file.txt", O_RDONLY); 
     char buf1[100]="\0"; 
    char buf[100]; 
    int aa=0,b=1,c=2,d=3,f=2,g=3; 
    read(fd,buf1,104); 
    if(strlen(buf1) > 100){ 

    }else{ 
     strcpy(buf,buf1); 
    } 
    //strcpy(buf,buf1); 
    foobar(buf1); 
} 

使用gdb反汇编我得到的是可执行文件反汇编。

0x000000000040067d <+0>: push rbp 
    0x000000000040067e <+1>: mov rbp,rsp 
    0x0000000000400681 <+4>: mov QWORD PTR [rbp-0x18],rdi 
    0x0000000000400685 <+8>: mov rax,QWORD PTR [rbp-0x18] 
    0x0000000000400689 <+12>: movzx eax,BYTE PTR [rax] 
    0x000000000040068c <+15>: mov BYTE PTR [rbp-0x1],al 
    0x000000000040068f <+18>: pop rbp 

主要拆卸之前foobar的

0x0000000000400784 <+243>: lea rax,[rbp-0xf0] 
    0x000000000040078b <+250>: mov rdi,rax 
    0x000000000040078e <+253>: call 0x40067d <foobar> 
    0x0000000000400793 <+258>: mov rbx,QWORD PTR [rbp-0x18] 
    0x0000000000400797 <+262>: xor rbx,QWORD PTR fs:0x28 
    0x00000000004007a0 <+271>: je  0x4007a7 <main+278> 
    0x0000000000400690 <+19>: ret 

现在,我有一个关于Foobar的拆卸问题

0x0000000000400681 <+4>: mov QWORD PTR [rbp-0x18],rdi 
0x0000000000400685 <+8>: mov rax,QWORD PTR [rbp-0x18] 

岂不指令

mov rax, rdi 

会不要他的工作需要上述两条指令。为什么使用额外的内存位置rbp - 0x18 rdi? 它与通过引用相关吗?

编辑: 我想问另外一个问题是,为什么foobar的功能正在访问的东西(rbp - 0x18)这是不是在Foobar的框架?

我的gcc版本是gcc(Ubuntu的4.8.2-19ubuntu1)4.8.2

编辑: 使用-O1 -O2和-O3优化标志,而编译之后,foobar的装配变化

0x0000000000400670 <+0>: repz ret 

,并在使用-O3标志的一些主要的拆卸是

0x0000000000400551 <+81>: rep stos QWORD PTR es:[rdi],rax 
    0x0000000000400554 <+84>: mov DWORD PTR [rdi],0x0 
    0x000000000040055a <+90>: mov cl,0x64 
    0x000000000040055c <+92>: mov edi,r8d 
    0x000000000040055f <+95>: call 0x4004b0 <[email protected]> 
    0x0000000000400564 <+100>: mov rdx,QWORD PTR [rsp+0x68] 
    0x0000000000400569 <+105>: xor rdx,QWORD PTR fs:0x28 
    0x0000000000400572 <+114>: jne 0x400579 <main+121> 
    0x0000000000400574 <+116>: add rsp,0x78 
    0x0000000000400578 <+120>: ret  
    0x0000000000400579 <+121>: call 0x4004c0 <[email protected]> 

我无法找到主要以foobar的任何电话。

+0

你的编译器标志是什么? –

+0

我正在编译使用以下命令。 gcc a.c -o a -g – user2823667

+0

使用一些优化设置,否则代码不会被优化:)例如'-O3'。 – Jester

回答

1

这是一个很好的问题。可以这么说,我赞扬你“在引擎盖下窥视”。

大量的研究已经进入编译代码。有时你希望代码运行得很快,有时你希望代码很小,有时候你希望代码能够快速编译。感谢编译器研究,编译器可以生成以上述任何方式表现的代码。为了让用户选择他们想要的这些选项中的哪一个,gcc有command line options that control the level of optimization

默认情况下,gcc使用-O0,它不会优化代码,而是关注最快的编译时间。因此,您有时会发现无效的指令序列。


当您打开-O3标志,则编译内联为foobar的代码。如你所知,函数调用需要时间,因此,如果函数foobar足够短,编译器只需复制foobar的整个代码而不是调用它,从而不需要调用和ret指令。这使得代码更快一点,但它也使它更大。

考虑一个被称为100次的100指令函数。如果此功能内联,代码大小将大幅增加,因为速度不会太高。如果您的优化级别设置较高且所涉及的函数非常小,则编译器只会内联代码。

您可能已经注意到,foobar函数没有任何内容。它已经被“优化了”,这意味着编译器完全删除了它。这是因为编译器可以告诉foobar没有做任何有用的事情。也就是说,它没有side effects。在-O0处,没有任何优化。在更高的优化级别下,gcc开始优化功能,无副作用以节省空间。

我几年没有写x86组件(现在只是arm),但是如果我没有记错,repz ret实际上是由于分支预测而更有效的ret形式。更多的信息可以发现here。我不得不睡觉了。如果您仍然有问题,我会稍后回复:)。

+0

当我使用优化标志运行时,我在foobar反汇编中只得到一条指令。为什么是这样? 0x0000000000400670 <+0>:\t repz ret – user2823667

+0

你可以添加新的反汇编到你的问题的结尾吗?确保包含您使用的优化标志。 –

0

正如几位人士所评论的那样,您应该进行一些优化编译,例如,至少用gcc -O1(并且最好是gcc -O2)。

如果具体编译为GCC,我建议也通过-fverbose-asm,因为这会在生成的汇编程序文件中发出有用的生成注释。

下面是相关上市后,在Debian/SID/AMD64使用GCC 5.1编译,使用gcc-5 -O2 -fverbose-asm -S go.c再看看与寻呼机的生产go.s汇编文件:

 .section  .text.unlikely,"ax",@progbits 
.LCOLDB0: 
     .text 
.LHOTB0: 
     .p2align 4,,15 
     .globl foobar 
     .type foobar, @function 
foobar: 
.LFB25: 
     .cfi_startproc 
     rep ret 
     .cfi_endproc 
.LFE25: 
     .size foobar, .-foobar 
     .section  .text.unlikely 
.LCOLDE0: 
     .text 
.LHOTE0: 
     .section  .rodata.str1.1,"aMS",@progbits,1 
.LC1: 
     .string "file.txt" 
     .section  .rodata 
.LC2: 
     .string "" 
     .string "" 
     .zero 98 
     .section  .text.unlikely 
.LCOLDB3: 
     .section  .text.startup,"ax",@progbits 
.LHOTB3: 
     .p2align 4,,15 
     .globl main 
     .type main, @function 
main: 
.LFB26: 
     .cfi_startproc 
     subq $120, %rsp  #, 
     .cfi_def_cfa_offset 128 
     xorl %esi, %esi  # 
     movl $.LC1, %edi  #, 
     xorl %eax, %eax  # 
     call open # 
     movl %eax, %r8d  #, fd 
     movzwl .LC2(%rip), %eax  #, tmp92 
     leaq 8(%rsp), %rdi #, tmp93 
     movl $11, %ecx  #, tmp95 
     movl $104, %edx  #, 
     movq %rsp, %rsi  #, 
     movl $0, 4(%rsp)  #, buf1 
     movw %ax, (%rsp)  # tmp92, buf1 
     xorl %eax, %eax  # 
     movw %ax, 2(%rsp) #, buf1 
     xorl %eax, %eax  # tmp94 
     rep stosq 
     movl $0, (%rdi)  #, buf1 
     movl %r8d, %edi  # fd, 
     call read # 
     movq %rsp, %rax  #, D.3346 
.L3: 
     movl (%rax), %edx #* D.3346, tmp100 
     addq $4, %rax  #, D.3346 
     leal -16843009(%rdx), %ecx #, tmp99 
     notl %edx # tmp100 
     andl %edx, %ecx  # tmp100, tmp99 
     andl $-2139062144, %ecx  #, tmp99 
     je  .L3  #, 
     xorl %eax, %eax  # 
     addq $120, %rsp  #, 
     .cfi_def_cfa_offset 8 
     ret 
     .cfi_endproc 
.LFE26: 
     .size main, .-main 

内联编译器来电foobar和优化其主体为空(因为您的源代码没有可观察副作用foobar)。然后它删除了对foobar的任何呼叫,因为它没用。

您可以尝试使用-fdump-tree-all进行编译。您将获得数百个转储文件,对应于生成它们的许多GCC优化过程。

你也可以自定义您的gccMELT(一类Lisp领域特定语言扩展GCC),你甚至可以搜索使用MELTfindgimple模式一些GIMPLE或树的图案(在排序的grep GCC内部的Gimple表示)。