个人初步学习操作系统,为了解汇编程序的工作过程,在x86 32位机器上对如下图所示的C代码进行了反汇编,以此来分析程序工作内部情况。
反汇编后的代码如下图:
下面对程序工作过程中的堆栈及相关寄存器情况进行分析:
假设初始时,堆栈指针ebp、esp均指向地址x
main:
pushl %ebp esp的值减4,将栈底地址压栈。esp=x-4,x~x-4的内存单元内容为ebp的值x
movl %esp,%ebp 将esp的值赋给ebp,esp与ebp指向同一位置。esp=x-4,ebp=x-4
subl $4,%esp esp的值减4,即栈顶地址向下扩充四个字节。esp=x-8
movl $4,(%esp) 将esp指向内存单元的内容赋值为4。x-4~x-8的内存单元内容为4
call f 调用f函数,相当于pushl eip movl f,eip ,esp的值减4,将寄存器eip的值(此时为main中的add $5,%eax指令的地址,假设其值为a)压栈,将标号f下第一条语句的地址赋值给寄存器eip(假设其值为b)。esp=x-12,x-8~x-12内存单元内容为a,eip=b
pushl %ebp esp的值减4,将ebp的值压栈。esp=x-16,ebp=x-4,x-12~x-16的内存单元内容为x-4
movl %esp,%ebp 将esp的值赋值给ebp,esp与ebp指向同一位置。esp=x-16,ebp=x-16
subl $4,%esp 将esp的值减4。esp=x-20
movl 8(%ebp),%eax 将ebp指向内存单元中的值加上8后的值为x-8,其所指向的内存单元值为4,赋值给eax寄存器。eax=4
movl %eax,(%esp) 将eax的值赋值给esp指向的内存单元。x-16~x-20内存单元内容为4
call g 调用g函数,相当于pushl eip movl g,eip,esp的值减4,将寄存器eip的值(此时为f中的leave指令的地址,假设其值为c)压栈,将标号g下第一条语句的地址赋值给寄存器eip(假设其值为d)。esp=x-24,x-20~x-24内存单元内容为c,eip=d
pushl %ebp esp的值减4,将ebp的值压栈。esp=x-28,ebp=x-16,x-24~x-28的内存单元内容为x-16
movl %esp,%ebp 将esp的值赋值给ebp,esp与ebp指向同一位置。esp=x-28,ebp=x-28
movl 8(%ebp),%eax将ebp指向内存单元中的值加上8后的值为x-20,其所指向的内存单元值为4,赋值给eax寄存器。eax=4
addl $6,%eax 将eax的值加上6。eax=10
popl %ebp esp的值加4,将栈顶内容出栈,并将其赋值给ebp。esp=x-24,ebp=x-16
ret 相当于popl %eip,esp的值加4,栈顶内容出栈,将其赋值给eip。esp=x-20,eip=c
c指向的位置为f中的leave指令,所以下一条指令为leave
leave 相当于movl %ebp,%esp popl%ebp 。esp=x-16,ebp=x-16,然后,esp=esp+4=x-12,将栈顶内容出栈,值付给ebp,ebp=x-4
ret 相当于popl %eip,esp的值加4,栈顶内容出栈,将其赋值给eip。esp=x-8,eip=a
a指向的位置为main中的add $5,%eax指令,所以下一条指令为add $5,%eax
add $5,%eax 将eax中的值加上5.。eax=15
leave 相当于movl%ebp,%esp popl%ebp 。esp=x-4,ebp=x-4,然后,esp=esp+4=x,将栈顶内容出栈,值付给ebp,ebp=x
ret 相当于popl %eip,esp的值加4,栈顶内容出栈,将其赋值给eip。esp=x+4,eip=调用main函数语句的下一条指令地址
在以上的分析中,计算机大量使用了堆栈,进行了指令的跳转、顺序执行。虽然只是一个很小的程序的示例,但是还是能够说明,计算机内部的工作机理简单,
但是执行步骤繁琐且数目巨大。我觉得,这也是相当于封装的思想,计算机封装了简单、基本、但是大量的、机械的操作,屏蔽了硬件的复杂细节,
给上层程序提供相对简单、清晰的接口。