b2科目四模拟试题多少题驾考考爆了怎么补救
b2科目四模拟试题多少题 驾考考爆了怎么补救

tls现代的应用程序都运行在一个内存空间里,在32位的系统里(2)

电脑杂谈  发布时间:2018-02-13 20:45:13  来源:网络整理

3)pop ebp: 从栈中恢复保存的ebp的值

4)ret:从栈中取得返回地址,并跳转到该位置

例如:

反汇编的结果:

在第五步,函数将0x7B(即123)赋值给eax,作为返回值传出。在函数返回之后,调用方可以通过读取eax寄存器来获取返回值。接下来的几步是函数的资源清理阶段,从栈中恢复保存的寄存器、ebp等。最后使用ret指令从函数返回。

它们的基本形式:

其中x为栈上开辟出来的临时空间的字节数,reg1...regn分别代表需要保存的n个寄存器。

1.2.2 调用惯例

一般,函数的调用方和被调用方对函数如何调用有着统一的理解。如果不这样的话,函数将无法正确运行。例如:

如果函数的调用方在传递参数时先压入参数n,再压入参数m,而foo函数却认为其调用的方式应该是压入参数m,后压入参数n,那么不难想象foo内部的m和n的值将会被交换。

因此,毫无疑问函数的调用方和被调用方对于函数如何调用必须有一个明确的约定,只有双方都遵守通用的约定,函数才能被正确地调用,这样的约定就称为调用惯例。一个调用惯例一般会规定如下几个方面的内容:

1)函数参数的传递顺序和方式

函数参数的传递有很多种方式,最常见的一种是通过栈传递。函数的调用方将参数压入栈中,函数自己再从栈中将参数取出。对于有多个参数的函数,调用惯例要规定函数调用方将参数压栈的顺序。

2)栈的维护方式

在函数将参数压栈之后,函数体会被调用,此时需要将被压入栈中的参数全部弹出,以使得函数调用前后保持一致。这个弹出的工作可以由函数的调用方完成,也可以由函数本身完成。

3)名字修饰的策略

为了连接的时候对调用惯例进行区分,调用管理要对函数本身的名字进行修饰。不同的调用管理有不同的名字修饰策略。

事实上,在C语言里,存在着多个调用惯例,而默认的调用惯例是cdecl。任何一个没有显示指定调用惯例的函数都是cdecl惯例。对于函数foo的声明,它的完整形式:

cdecl这个调用惯例是C语言默认的调用惯例,如图所示:

按照cdecl的参数传递方式,具体的堆栈操作如下:

1)将m压入栈

2)将n压入栈

3)调用_foo,此步骤有分两步:

a 将返回地址压入栈

b 跳转到_foo执行

当前函数返回之后:sp=sp+8(参数出栈,由于不需要得到出栈的数据,所以直接调整栈顶位置就可以了)。因此进入foo函数之后,栈上大致如图所示:

然后在foo里面要保存一系列的寄存器,包括函数调用方的ebp寄存器,以及要为a和b两个局部变量分配空间。最终栈如图所示:

对于不同的编译器,由于分配局部变量和保存寄存器的策略不同,这个结果可以不同。 以上布局中,如果想访问变量n,实际的地址是使用ebp+8.当foo返回时,程序首先会使用pop恢复保存在栈里的寄存器,然后从栈里取得返回地址,返 回到调用方。调用方再通知esp将堆栈恢复。因此有如下代码:

其中虚线指向该指令执行后的栈状态,实线表示程序的跳转状况。同样,对于多级调用,如果我们有如下代码:

箭头代表指向关系,而待下划线的代码代表当前执行的函数。除了cdecl调用惯例外,还存在很多别的调用惯例,如图所示:

1.2.3函数返回值传递

除了参数的传递之外,函数与调用方的交互还有一个就是返回值。通过反汇编可以看出,发现eax是传递返回值的通道。函数将返回值存储在eax中,返回后函数的调用方再读取eax。

相对于栈而言,堆这片内存面临一个稍微复杂的行为模式:在任意时刻,程序可能发出请求,要么申请一段内存,要么释放一段已申请过的内存,而且申请的大小从几个字节到数GB都是可能的。


本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-76057-2.html

相关阅读
    发表评论  请自觉遵守互联网相关的政策法规,严禁发布、暴力、反动的言论

    热点图片
    拼命载入中...