`

C语言汇编代码分析(函数)

 
阅读更多

C语言中的函数(或称为方法或者过程)是通过进程的栈空间来进行管理的,一个个函数在栈空间的表现就像是一幅一幅的图片,称为栈帧(stack frame)。其中寄存器%ebp始终指向栈帧的开始,而寄存器%esp则像游标一样在相邻两个栈帧中滑动来存取值。

下面以一个实际例子来说明:



 由上图可知函数compare调用函数max, 不同颜色代表不同的栈帧,我们来开始分析汇编代码:

首先我们假设此时esp寄存器为某个值Vbase(上图中第二个箭头指向的位置);

 

pushl %ebp ;将%ebp寄存器的值入栈,此时Vesp = Vbase - 4;

 

movl %esp, %ebp; 将%esp的值赋值给%ebp,则Vebp = Vbase -4;

 

subl $8, %esp; 将%esp的值减去8,即分配栈空间,用于保存临时变量,这里是x和y,

                      ;此时Vesp = Vbase-4-8 = Vbase - 12

 

movl $7, 4(%esp); 这里将y的值7存到Vesp+4指定的地址

 

movl $5, %esp;这里将x的值5存储到Vesp指定的地址

 

call max;这里调用max函数,call指令会首先将函数执行完后的返回地址(即call后面第一条指令的虚拟地址)压入栈

            ;中,此时Vesp = Vbase - 16

 

//*************************************这里进入max函数********************************************************//

pushl %ebp;将%ebp寄存器的值入栈,此时Vesp = Vbase - 20;

 

movl %esp, %ebp; 将%esp的值赋值给ebp,则Vebp = Vbase -20;

 

movl 12(%ebp), %eax;将Vbase - 8地址即y的值存到%eax寄存器中

 

cmpl %eax, 8(%ebp);将y 与Vbase - 12地址即x的值作比较

 

setg %al;如果x大于y设置%al为1,相反设置为0

 

movzbl %al, %eax;用零扩展位方式将al的值扩展到%eax中,即清除%eax的高24位

 

popl %ebp;将进入函数时的%ebp值出栈存入%ebp寄存器中,此时Vesp = Vbase -16,指向“返回地址”

 

ret;将Vesp指向的地址的值转入%eip寄存器,然后让%esp+4,即出栈,此时Vesp = Vbase -12,指向x

//*************************************这里退出max函数********************************************************//

 

testl %eax, %eax;这里比较max的返回值是不是为1,函数的返回值一般存在eax寄存器中

 

mov $35, %edx; 这里编译器将x*y直接优化成35,将该值存到%edx寄存器中

 

cmovne %edx, %eax;这里用到了tesl的值,如果返回值为1,则将%edx的值赋值到%eax中

 

leave;将%ebp的值(即compare函数第二个语句中的%ebp值)赋值给%esp,即回收分配给x和y的空间,

        ;然后将%esp指向的栈顶地址中的值赋值给%ebp

 

ret;将Vesp指向的地址的值转入%eip寄存器,然后让Vesp+4,即出栈,此时Vesp = Vbase

 

 

扩展知识:由于芯片中寄存器是被所有方法共有的,为了不让被调用者破坏调用者的数据,必须遵循下面的规则:

    1.寄存器%eax,%edx和%ecx作为调用者保存寄存器,如果函数P调用函数Q,那么Q可以任意使用这些寄存器,不用

       担心会损坏P的数据

    

    2.寄存器%ebx,%esi和%edi为被调用者保存寄存器,如果函数P调用函数Q,如果Q需要用到这些寄存器的时候,必须先

     保存,当函数退出时恢复该寄存器的值。

 

 

 

 

 

 

 


 


 
 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics