现在的位置: 首页 > 操作系统 > 正文

高级语言里的函数在汇编里的实现方式

2020年02月12日 操作系统 ⁄ 共 1866字 ⁄ 字号 评论关闭

一、学习过程

在高级语言中我们为什么要用变量呢?因为我们要存储数据,而且因为要使用循环等语法结构,存储的数据需要不断地变化,变量的特性可以很好地解决这个问题。在前面我已经讨论过了,变量的声明实际上就是在内存中开辟一个内存空间,我们在汇编语言里使用循环,主要是把数据存在si、di等寄存器中来进行操作,存储数据是把数据放在寄存器、内存空间(普通的和栈)里面。编写程序ur1.c,并编译连接:

用debug加载ur1.exe,用u命令查看编译后的机器码和汇编代码:

发现main函数中的代码没有出现,用t命令单步执行发现程序在一个死循环内,为什么这时显示的代码不是main函数里的代码呢?可能的原因有两个:(1)我以为main函数里的代码编译后会是这样:movax,1;movbx,1;movcx,1;movax,bx;addax,cx……但是c语言编译成的汇编代码是通过别的方式实现代码的功能的,与我从汇编的角度来实现功能的方式不一样。(2)main函数本身经过编译后会有许多汇编代码,或者是main函数前面还要加载其他程序,导致main函数里的代码被放在一个我们不知道的地方,所以找不到。那么main函数的代码在什么段中?用debug怎样找到ur1.exe中main函数的代码?我觉得应该在code段中,但是程序里没有标号,看不到main的地址。那么就写一个main到程序里,看看main的地址是多少。

编译程序,显示main函数的偏移地址:

运行发现,main函数的偏移地址为1fa:

那么,用debug运行ur1.exe,找到main函数的地址:

由图可知,ur1.c中对应的代码是movax,1;movbx,1;movcx,2;movax,bx;

addax,cx;movah,bl;addah,cl;moval,bh;addal,ch.这里还应注意到开头和结尾的三条指令:pushbp;movbp,sp;popbp.是对bp进行了保护,并且在程序中把sp赋给了bp,为什么要这样做呢?书本上的解释是:这是C编译器安排的为函数中可能使用到bp寄存器而设置的。那么为什么函数中可能使用到bp寄存器呢,也可能使用到别的寄存器啊,为什么只对bp寄存器进行了保护?

还有在代码结尾处用ret进行了处理,即对ip出栈,一般用到ret时会与call指令配合使程序段成为可调用的字程序段,那么main函数是以call-ret的方式来实现成为子程序段的吗?将以下程序编译连接:

用debug查看main函数中的内容:

发现果然f()的位置被call020b代替。而20b的位置上是f()函数的内容:

所以我们的想法是正确的,不仅main函数在汇编语言里是以call-ret程序段的方式实现的,甚至C语言是将所有函数都实现为汇编语言里的call-ret子程序段。

二、解决的问题

(1)为什么用debug查看ur1.exe文件找不到main函数里的内容?

答:因为main函数及其内容是作为子程序段被放在其他位置,所以找不到。

(2)用debug怎么查找函数的偏移地址?

答:写一个程序,用printf打印函数的地址。

(3)C语言里的函数在汇编语言里是怎样实现的?

答:用call-ret作为子程序段来实现。

三、未解决的问题

(1)为什么一个函数在汇编语言中实现要对并且只对bp寄存器进行保护?

(2)为什么用debug加载ur1.exe用u命令首先看到的不是main函数及其内容,而是一大堆无关的指令?

(3)其他高级语言函数的实现方式和C语言一样吗?

四、学习感想

你的想法到底正不正确呢?试一试就知道了,实践是检验真理的唯一标准。很多时候我们都会觉得尝试是一件很麻烦的事,所以面对还不太懂的问题,就想当然地给它下一个判断,或者根本不去想,等别人来告诉你,久而久之,我们对一件事物的整个认知就会出现偏差,从而犯错。函数名能够直接写在printf函数里把地址打印出来吗?c语言里的函数是用什么形式在汇编语言里实现的呢?这些都是需要亲自尝试才能解决的问题。在平时的学习和生活中,我们经常会遇到诸如此类的问题,但是有时候因为自身的惰性,想当然地把它忽视掉了,要用到的时候才感叹自己知道的真的太少。所以,保持一个积极强烈的求知心态是学习过程中很重要的。

本文永久更新链接地址:http://www.xuebuyuan.com/Linux/2016-12/138893.htm

以上就上有关高级语言里的函数在汇编里的实现方式的全部内容,学步园全面介绍编程技术、操作系统、数据库、web前端技术等内容。

抱歉!评论已关闭.