先编写一个简单的C程序,main.c, 源码内容如下:
点击(此处)折叠或打开
-
#include <stdio.h>
-
-
int g(int a)
-
{
-
return a + 3;
-
}
-
int main(int argc, char *argv[])
-
{
-
return g(4);
- }
点击(此处)折叠或打开
-
g:
-
pushl %ebp
-
movl %esp, %ebp
-
movl 8(%ebp), %eax
-
addl $3, %eax
-
popl %ebp
-
ret
-
main:
-
pushl %ebp
-
movl %esp, %ebp
-
subl $4, %esp
-
movl $4, (%esp)
-
call g
-
leave
- ret
a.在此先将列出几个特殊指令的形态:(栈空间由高地址向低地址增长)
pushl %eax : subl $4, %esp ; movl %eax, %(%esp); #将eax寄存器的数进行压栈 ,esp下移4个字节,然后将eax的值赋给esp。
popl %eax : movl (%esp), %eax ; subl %esp; #将esp内的值赋给eax寄存器,同时将esp的指针上移四个字节。
call 0x1234 : pushl %eip(*) ; movl 0x1234 , %eip(*); #将当前eip的值压栈,然后将新的入口地址0x1234放入eip寄存器中。
ret : popl %eip(*); #将下一行要处理的指令行号送入eip寄存器中。
enter : pushl %ebp; movl %esp, %ebp ; #将栈基指针压栈,然后将栈顶指针赋给栈基指针。
leave : movl %ebp,%esp ; popl %ebp ; #这个跟enter正好相反。
b.程序的入口函数是main,在汇编代码中行号为 8. 第9/10行就是上面写的enter指令,这时重新构造了空栈,esp == ebp。
点击(此处)折叠或打开
-
subl $4, %esp
- movl $4, (%esp)
点击(此处)折叠或打开
- call g
第2/3 行进行构造栈。
下一步中第4/5/6行进行对eax赋值。
点击(此处)折叠或打开
-
movl 8(%ebp), %eax #将ebp 地址加8 ,也就是movl $4, (%esp) 指令的 4 取出,放入eax中
-
addl $3, %eax # 将3 加到eax中,此时eax值为7。
- popl %ebp #将当前栈指针内容赋给ebp。
下一步第7行:
点击(此处)折叠或打开
- ret #将下一行要处理的指令行号送入eip寄存器中。
由上面的分析可以看出,整个汇编指令都是以流水线方式逐条进行处理,这也说明了cpu主频其实跟计算机的处理速度是有很大关联的。
作者程大鹏, 转载请注明出处 http://blog.chinaunix.net/blog/post.html
Linux内核分析》MOOC课程 ”