反汇编一个简单的C

朱婷婷 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

计算机是怎么工作的?

我们知道计算机基本都是采用的冯诺依曼体系结构,它主要的核心思想就是存储程序计算机,指令和数据不加区别混合存储在同一个存储器中。CPU通过寄存器EIP指向内存从而获取内存中的数据。EIP总是指向内存的某一块区域(代码段),不断的指向下一条指令,每条指令的长度可能是不同的。我们可以用一个简单的图来描述下:




一、实验内容:通过反汇编一个简单的C程序,分析汇编代码理解计算机是如何工作的

main.c

int g(int x)
{
    return x+4;
}

int f(int y)
{
    return g(y);
}

int main(void)
{
    return f(2)+4;
}
保存后执行以下命令:

gcc -S -o main.s main.c -m32
其中gcc后面参数含义可以自行man以下,-S表示预处理后可直接对main.c文件编译生产汇编代码,如下所示:
shiyanlou:~/ $ cat main.s                                            [21:59:59]
	.file	"main.c"
	.text
	.globl	g
	.type	g, @function
g:
.LFB0:
	.cfi_startproc
	pushl	%ebp
	.cfi_def_cfa_offset 8
	.cfi_offset 5, -8
	movl	%esp, %ebp
	.cfi_def_cfa_register 5
	movl	8(%ebp), %eax
	addl	$4, %eax
	popl	%ebp
	.cfi_restore 5
	.cfi_def_cfa 4, 4
	ret
	.cfi_endproc
.LFE0:
	.size	g, .-g
	.globl	f
	.type	f, @function
f:
.LFB1:
	.cfi_startproc
	pushl	%ebp
	.cfi_def_cfa_offset 8
	.cfi_offset 5, -8
	movl	%esp, %ebp
	.cfi_def_cfa_register 5
	subl	$4, %esp
	movl	8(%ebp), %eax
	movl	%eax, (%esp)
	call	g
	leave
	.cfi_restore 5
	.cfi_def_cfa 4, 4
	ret
	.cfi_endproc
.LFE1:
	.size	f, .-f
	.globl	main
	.type	main, @function
main:
.LFB2:
	.cfi_startproc
	pushl	%ebp
	.cfi_def_cfa_offset 8
	.cfi_offset 5, -8
	movl	%esp, %ebp
	.cfi_def_cfa_register 5
	subl	$4, %esp
	movl	$2, (%esp)
	call	f
	addl	$4, %eax
	leave
	.cfi_restore 5
	.cfi_def_cfa 4, 4
	ret
	.cfi_endproc
.LFE2:
	.size	main, .-main
	.ident	"GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2"
	.section	.note.GNU-stack,"",@progbits
二、实验结果分析:

以上汇编代码中,以点(.)开头的都是用于链接的辅助信息,这些信息是不会被执行的。因此以下分析下除去辅助信息之外的汇编指令:

g:
	pushl	%ebp
	movl	%esp, %ebp
	movl	8(%ebp), %eax
	addl	$4, %eax
	popl	%ebp
	ret
f:
	pushl	%ebp
	movl	%esp, %ebp
	subl	$4, %esp
	movl	8(%ebp), %eax
	movl	%eax, (%esp)
	call	g
	leave
	ret
main:
	pushl	%ebp
	movl	%esp, %ebp
	subl	$4, %esp
	movl	$2, (%esp)
	call	f
	addl	$4, %eax
	leave
	ret


程序从main函数开始,假设一开始是一个空栈,如下图所示(在32位环境中,int型数据占4个zi)


pushl	%ebp



movl	%esp, %ebp


subl	$4, %esp

movl	$2, (%esp)


call	f
call f 的操作其实就相当于pushl %eip;  movl * f, %eip; 从这步开始进入到f函数中的操作


pushl	%ebp


movl	%esp, %ebp


subl	$4, %esp

movl	8(%ebp), %eax
以上操作相当于 eax = *(int32_t *) (ebx+4),因此eax = 2

movl	%eax, (%esp)


	call	g

pushl	%ebp


movl	%esp, %ebp

·

movl	8(%ebp), %eax


addl	$4, %eax

popl	%ebp


ret
ret的操作就相当于pop eip(*),所以esp指针向上移一格,然后代码回到第15行的那条指令



leave
leave指令相当于movl %ebp,%esp; popl %ebp;

ret
回到第23行指令继续执行


addl	$4, %eax


leave
ret



执行完以上所有代码,我们可以看到堆栈又回到了最初的状态。

三、总结

通过以上逐条指令的分析来理解C语言函数的执行过程,能够加深我们对计算机C语言等高级语言的理解,同时对以后学习用户空间虚拟地址空间打下了基础。

四、实验截图




阅读更多
文章标签: 汇编 c语言 计算机
个人分类: 计算机
下一篇实验二:完成一个简单的时间片轮转多道程序内核代码
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭