2022年2022年函数调用机制C与汇编相互调用 .pdf
-
资源ID:34869576
资源大小:1.52MB
全文页数:6页
- 资源格式: PDF
下载积分:4.3金币
快捷下载
会员登录下载
微信登录下载
三方登录下载:
微信扫一扫登录
友情提示
2、PDF文件下载后,可能会被浏览器默认打开,此种情况可以点击浏览器菜单,保存网页到桌面,就可以正常下载了。
3、本站不支持迅雷下载,请使用电脑自带的IE浏览器,或者360浏览器、谷歌浏览器下载即可。
4、本站资源下载后的文档和图纸-无水印,预览文档经过压缩,下载后原文更清晰。
5、试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
|
2022年2022年函数调用机制C与汇编相互调用 .pdf
函数调用机制、 C 与汇编相互调用-2012 年 11 月 22 日 22:06:23为了提高代码执行效率,代码中有些地方直接使用汇编语言编制。这就会涉及到两种语言的程序间相互调用的问题。本文首先说明C 语言函数的相互调用机制,然后使用示例来说明 C 与汇编之间的调用方法。【C 函数相互调用机制】函数调用操作包括从一块代码到另一块代码之间的双向数据传递和执行控制转移。数据传递通过函数参数和返回值来进行。另外,还需要在进入函数时为函数的局部变量分配存储空间,并且在退出函数时收回这部分空间。Intel 80 x86 CPU 为控制传递提供了简单的指令,而数据的传递和局部变量存储空间的分配和回收则通过栈操作来实现。1、 栈帧结构和控制权转移方式大多数 CPU 上的程序实现使用栈来支持函数调用操作。栈被用来传递函数参数、存储返回信息、 临时保存寄存器原有值以备恢复以及用来存储局部数据。单个函数调用操作所使用的栈部分被称为栈帧(Stack frame )结构,如下图所示。栈帧结构的两端由两个指针来指定。 寄存器 ebp 通常用作帧指针 (frame pointer ) ,而 esp则用作栈指针 (stackpointer ) 。在函数执行过程中,栈指针esp 会随着数据的入栈和出栈而移动,因而函数中对大部分数据的访问都基于帧指针ebp 进行。对于函数 A 调用函数 B 的情况,传递给B 的参数包含在A 的栈帧中。当A 调用 B 时,函数 A 的返回地址(调用返回后继续执行的指令地址)被压入栈中,栈中该位置也明确指明了 A 栈帧的结束处。而B 的栈帧则从随后的栈部分开始。再随后则用于存放任何保存的寄存器值以及函数的临时值。B 函数同样也使用栈来保存不能存放在寄存器中的局部变量。例如由于CPU 寄存器数量有限而不能存放函数的所有局部数据,或者有些局部变量是数组或结构,因此必须使用名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 1 页,共 6 页 - - - - - - - - - 数组或结构引用来访问。还有就是C 语言的地址操作符“& ”被应用到一个局部变量上时,就需要为该变量生成一个地址,即为变量的地址指针分配一空间。最后,B 函数也会使用栈来保存调用任何其他函数的参数。栈是往低地址方向扩展的,而esp 指向当前栈顶处的元素。通过使用push 和 pop 指令可以把数据压入栈中或从栈中弹出。对于没有指定初始值的数据所需要的存储空间,可以通过把栈指针递减适当的值来做到。类似地,通过增加栈指针可以回收栈中已分配的空间。指令 CALL和 RET 用于处理函数调用和返回操作。调用指令CALL的作用是把返回地址压入栈中并且跳转到被调用函数开始处执行。返回地址是程序中紧随调用指令CALL后面一条指令的地址。因此当被调用函数返回时就会从该位置继续执行。返回指令RET 用于弹出栈顶处的地址并跳转到该地址处。在使用该指令之前,应该先正确处理栈中内容,使得当前栈指针所指位置的内容是先前CALL指令所保存的返回地址。另外,若返回值是一个整数或一个指针,那么寄存器eax 将被默认用来传递返回值。尽管某一时刻只有一个函数在执行,但还是需要确定在一个函数调用其它函数时,被调用者不会修改或覆盖掉调用者今后要用到的寄存器内容。因此 Intel CPU 采用了所有函数必须遵守的寄存器用法统一惯例。该惯例指明,寄存器eax、edx、ecx 的内容必须由调用者自己负责保存。寄存器ebx、esi、edi、ebp、esp的内容则必须由被调用者B 来保护。当被调用者需要使用这些寄存器中的任意一个时,必须首先在栈中保存其内容,并在退出时恢复这些寄存器内容。2、 函数调用举例上面函数调用时的栈结构如下:用 gcc 1.40 编译生成的汇编代码如下(汇编语法为AT&T语法,这里删除了几行与讨论无关的伪指令) :名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 2 页,共 6 页 - - - - - - - - - 从上面的分析可知,C语言在调用函数时是在堆栈上临时存放被调函数的值(压栈顺序为从右至左,即C调用约定( _cdecl) 。更多的关于函数调用时的入栈顺序可以参考:http:/ ) ,C语言是传值类语言,没有直接的方法可用来在被调用函数中修改调用者变量的值。因此为了达到修改的目的就需要向函数传递变量的指针(即变量的地址)。3、 main() 也是一个函数上面提到的 C程序的主程序 main() 也是一个函数。因为gcc在编译连接时它将被作为crt0.s 汇编程序的函数被调用。crt0.s 是一个桩 (stub)程序,名称中的“crt ”是“ C run-time ”的缩写。该程序的目标文件将被连接到每个用户执行程序的开始部分,主要用于设置一些初始化全局变量等。Linux-0.11 中crt0.s 汇编程序见如下所示。其中建立并初始化全局变量_environ 供程序中其他模块使用。名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 3 页,共 6 页 - - - - - - - - - 现在的 gcc编译器( 2.x)已经把这个crt0 扩展成几个模块:crt1.o 、crti.o 、crtbegin.o 、crtend.o 和crtn.o 。【在汇编中调用C函数】从汇编程序中调用C语言函数的方法实际上在上面已经给出。在上面C语言例子对应的汇编程序代码中,可以看出汇编程序语句是如何调用swap()函数的。 现在对调用方法作一总结。在汇编程序调用一个C函数时,程序需要按照从右至左的顺序将函数参数压栈,见下图。然后执行 CALL 指令去执行被调函数。在被调函数返回后,汇编程序需要再把先前压栈的函数参数清除掉。在执行 CALL 指令时, CPU会把 CALL 指令下一条指令的地址压入栈中(见图中EIP) 。如果调用还涉及到代码特权级变化,那么CPU 还会进行堆栈切换,并且把当前堆栈指针、段描述符和调用参数压入新堆栈中。也可以根本不用CALL 指令而采用 JMP 指令来同样达到调用函数的目的。方法是在参数入栈之后人工的把下一条要执行的指令地址压入栈中,然后直接使用JMP 指令跳转到被调函数开始处去执行。此后当函数执行完成时就会执行RET 指令把人工压栈的下一条指令地址弹出,作为函数返回地址。【在 C程序中调用汇编程序】从C程序中调用汇编程序函数的方法与汇编程序调用C函数的原理相同。调用方法的着重点仍然是对函数参数在栈中位置的确定。当然,如果调用的汇编语言程序比较短,那么可以直接在 C程序中使用内嵌汇编语句来实现。下面以一个示例来说明C调用汇编的方法。下面是包含两个函数的汇编程序:名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 4 页,共 6 页 - - - - - - - - - 调用这两个汇编函数的C程序如下:名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 5 页,共 6 页 - - - - - - - - - 若有问题,欢迎联系:本文参考了: Linux 内核完全注释-linux0.11版内核 -赵炯名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 6 页,共 6 页 - - - - - - - - -