第1章 计算机工作原理
实验的具体过程如下:
一、实验的C语言代码如图所示:
二、将C语言代码转化为汇编语言代码如图所示:
三、堆栈空间示意图如图所示:
在这里,0~9代表堆栈空间的标号,压栈时标号加1,出栈时标号减1。右侧的数字表示内存地址,EBP和ESP寄存器都指向栈底,即指向一个4字节存储单元的下边缘2000的位置,表示2000~2003这4个字节,也就是标号为0的存储单元,以此类推,标号为1的存储单元为1996~1999这4个字节。
四、对汇编代码执行过程进行分析:
程序从从汇编代码第18行开始执行,也就是从“main”开始执行,执行过程如下:
-
第18行:这条指令的作用实际上就是把ebp寄存器的值压栈,pushl指令的功能是先把ESP寄存器指向标号1的位置,即标号加1或地址减4,然后将EBP寄存器的值标号0(地址2000)放到堆栈标号1的位置。
-
第19行:开始执行上一条指令时,EIP寄存器已经自动加1指向了第19行语句,将EBP寄存器也指向标号1的位置。第18行和第19行语句是建立main函数自己函数调用堆栈空间。
-
第20行:开始执行上一条指令时,EIP寄存器已经自动加1指向了第20行语句,把ESP寄存器减4,实际上是ESP寄存器向下移动和一个标号,指向标号2的位置。
-
第21行:开始执行上一条指令时,EIP寄存器已经自动加1指向了第21行语句,把立即数5放入ESP寄存器指向的标号2位置,也就是第20行代码预留出来的标号2位置。第20和21行语句实在为接下来调用f函数做准备,即压栈f函数所需的参数。
-
第22行:开始执行上一条指令时,EIP寄存器已经自动加1指向了第22行语句:“call f”。第22行语句开始执时,EIP寄存器已经自动加1指向了下一条指令,即的第23行语句,实际上把EIP寄存器的值(行号为23的指令地址,用行号23表示)放到了栈空间标号3的位置。因为压栈前ESP寄存器的值是标号2,压栈时ESP寄存器先减4个字节,即指向下一个位置标号3,然后将EIP寄存器的行号23入栈到栈空间标号3的位置。接着将f函数的第一条指令的行号9放入EIP寄存器,这样EIP寄存器就指向了f函数。接着开始执行f函数。
-
第9行和第10行的语句和上述第18行和第19行的语句相同,作用是初始化函数自己的函数调用堆栈空间。
-
第11行:将ESP寄存器减4,即指向下一个位置栈空间标号为5,实际上就是为了入栈预留出一个存储单元的空间。
-
第12行:这是一个变址寻址的语句:EBP寄存器的值加8,当前EBP寄存器指向标号4的位置,加8即再向上移动两个存储单元加两个标号的位置,实际所指向的位置就是堆栈空间中标号2的位置,标号2的位置存储的立即数是5,那么这条语句的作用就是把立即数5放到了EAX寄存器中。
-
第13行:把EAX寄存器中存储的立即数5放到ESP寄存器现在所指的位置,即第11行语句预留出来的栈空间标号5的位置。
-
第14行:与上文第22行的语句作用类似,将ESP寄存器指向堆栈空标号6的位置,把EIP寄存器的内容行号15放到堆栈空间标号6的位置,然后把EIP寄存器指向函数g的第一条指令。即第2行的位置。
-
第2行第3行的语句是为函数g建立一个逻辑上独立的函数调用堆栈空间。
-
第4行:是一个变址寻址语句。EBP寄存器加8,也就是在当前EBP寄存器指向的栈空间标号7的位置基础上向上移动两个存储单元指向标号5,然后把标号5的内容放到EAX寄存器中。实际上,这一步是将函数g的参数取出来。
-
第5行:把立即数2加到EAX寄存器中,就是5+2,EAX寄存器为7。这时EBP和ESP寄存器都指向标号7,EAX寄存器为7,EIP寄存器为代码行号6,函数调用空间如图所示。EBP或ESP+栈空间的标号表示存储的是某个时刻EBP或ESP寄存器的值,EIP+代码行号表示存储的是某个时刻的EIP寄存器的值。
-
第6行和第7行语句的作用是拆除g函数调用堆栈,并返回到调用函数g的位置。使EIP寄存器指向代码第15行的位置。
-
第15行和第16行:leav指令用于撤销堆栈。使EIP指向第23行的位置。
-
第23行:把EAX寄存器加立即数2,也就是7+2,此时EAX寄存器的值为9。
-
第24行和25行:撤销main函数的堆栈,使栈空间回到main函数开始执行之初的状态。
五、使用gdb调试C语言程序
-
启动gdb调试:
-
查看main函数信息:
从图中可见call指令自动把返回地址0x4004fc压入栈中。
-
查看f函数信息:
在栈上保存上层帧的帧指针0x4004fc,然后将新的栈帧赋给帧指针%rsp。在这里rsp相当于书上的esp,rbp相当于书上的ebp。call指令自动把返回地址0x4004ed压入栈中。
-
查看g函数信息:
在栈上保存上层帧的帧指针0x4004ed,然后将新的栈帧赋给帧指针%rsp。
遇到的问题
一、gdb调试问题
在用gdb调试该C语言程序时,使用run命令会出现如下的错误:
这样的话就不能查看该程序执行时,每一步的栈信息。在尝试了设置断点等方法后也没有解决该问题。接下来我会继续查阅相关资料,来解决该问题。