我正在尝试理解这个简单的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对可执行文件进行反汇编是foobar反汇编。
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
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
将完成上述两条指令所要求的工作。为什么为rdi使用额外的内存位置rbp - 0x18
?
它与传递参考有关吗?
编辑:
我想问的另一个问题是为什么foobar函数正在访问不在foobar框架内的(rbp - 0x18)
。
我的gcc版本是gcc(Ubuntu 4.8.2-19ubuntu1)4.8.2
编辑: 在编译时使用-O1 -O2和-O3优化标志后,foobar组件变为
0x0000000000400670 <+0>: repz ret
并且在使用-O3标志时,main的一些反汇编是
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 <__read_chk@plt>
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 <__stack_chk_fail@plt>
我找不到任何关于foobar的电话。
答案 0 :(得分:1)
这是一个很好的问题。我赞赏你在&#34;偷看引擎盖&#34;,可以这么说。
大量研究已用于编写代码。有时您希望代码快速运行,有时您希望它很小,有时您希望它快速编译。由于编译器研究,编译器可以生成以上述任何方式运行的代码。为了允许用户选择他们想要的那些选项,gcc有command line options that control the level of optimization。
默认情况下,gcc使用-O0,它不会对代码进行太多优化,而是专注于最快的编译时间。因此,您有时会发现效率低下的指令序列。
当您打开-O3标志时,编译器内联 foobar的代码。如您所知,函数调用需要时间,因此,如果函数foobar足够短,编译器将只复制foobar的整个代码而不是调用它,从而无需调用和返回指令。这使得代码更快一点,但它也使代码更大。
考虑一个被称为100次的100指令函数。如果内联此函数,代码大小将急剧增加,因为没有太多额外的速度。如果您设置了较高的优化级别并且所讨论的函数非常小,则编译器仅内联代码。
您可能已经注意到没有任何东西代替foobar功能。它已被&#34;优化&#34;,这意味着编译器完全删除了它。这是因为编译器可以告诉foobar没有做任何有用的事情。也就是说,它没有side effects。在-O0,没有任何优化。在更高的优化级别,gcc开始优化功能,没有副作用,以节省空间。
我几年没有写过x86 assmembly(现在只是手臂),但如果我没记错的话,由于分支预测,repz ret
实际上是一种更有效的ret形式。可以找到更多信息here。
我现在必须去睡觉了。如果您仍有疑问,我会稍后回复:)。
答案 1 :(得分: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
的 observable 副作用。然后它删除了对foobar
的任何调用,因为它没用。
您可以尝试使用-fdump-tree-all
进行编译。您将获得数百个转储文件,对应于生成它们的许多GCC优化过程。
您还可以使用MELT(类似Lisp的域特定语言来扩展GCC)自定义gcc
,甚至可以使用 MELT findgimple模式(GCC内部的Gimple表示中的grep
种类。)