[英]Understanding assembly of a simple C program
我试图了解此简单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
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
将完成上述两个指令所需的工作。 为什么要为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标志时,主要的一些反汇编是
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>
我在main中找不到对foobar的任何调用。
这是一个很好的问题。 可以这么说,我赞扬您“在引擎盖下偷看”。
大量研究已进入编译代码。 有时您希望代码快速运行,有时希望代码很小,有时又希望快速编译。 多亏了编译器的研究,编译器可以生成以上述任何一种方式运行的代码。 为了允许用户选择他们想要的这些选项中的哪一个,gcc具有控制优化级别的命令行选项 。
默认情况下,gcc使用-O0,它不会对代码进行太多优化,而是专注于最快的编译时间。 因此,有时您会发现效率低下的指令序列。
当您打开-O3标志时,编译器将内联 foobar的代码。 如您所知,函数调用需要花费时间,因此,如果函数foobar足够短,则编译器将只复制foobar的整个代码而不是对其进行调用,从而消除了对调用和ret指令的需要。 这使代码的处理速度更快,但同时也使其变得更大。
考虑一个称为100次的100条指令的函数。 如果内联此函数,则代码大小将急剧增加,而不会增加太多速度。 仅当您设置了较高的优化级别并且所考虑的函数很小时,编译器才内联代码。
您可能已经注意到,没有什么可以代替foobar函数。 它已被“优化”,这意味着编译器将其完全删除。 这是因为编译器可以告诉foobar没有做任何有用的事情。 也就是说,它没有副作用 。 在-O0,没有任何优化。 在更高的优化级别上,gcc开始优化功能,而没有副作用以节省空间。
几年来我还没有写过x86程序集(如今只是武装起来),但是如果我repz ret
话,由于分支的repz ret
, repz ret
实际上是一种更有效的ret形式。 更多信息可以在这里找到。
我现在必须去睡觉。 如果您还有问题,我会在稍后回复:)。
正如一些人评论的那样,您应该进行一些优化,例如至少使用gcc -O1
(最好是gcc -O2
)进行gcc -O2
。
如果专门使用GCC进行编译,我建议也传递-fverbose-asm
因为这会在生成的汇编文件中发出有用的生成注释。
这是相关的清单,使用gcc-5 -O2 -fverbose-asm -S go.c
在Debian / Sid / amd64上使用GCC 5.1进行编译,然后使用go.s
器查看生成的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优化过程。
您还可以使用MELT (一种类似于Lisp的领域特定语言来扩展GCC)自定义gcc
,甚至可以使用MELT的findgimple模式(GCC内部的Gimple表示形式的grep
)搜索一些Gimple或Tree模式。 )。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.