0
点赞
收藏
分享

微信扫一扫

1618_x86汇编以及函数调用堆栈


​​GreyZhang/g_unix: some basic learning about unix operating system. (github.com)​​

         之前已经简单了解过x86的汇编语言了,这一次主要是结合部分汇编的知识来看一下x86架构下C语言函数的参数处理以及堆栈。由于C在起作用的过程中中间还是借助了一次汇编的转换,因此这里也就有了汇编相关内容的关联性。

1618_x86汇编以及函数调用堆栈_unix

         这一页的知识其实不多,主要是介绍了一个数的几进制以及表达方式,另外介绍了高级语言的编译链接过程。

1618_x86汇编以及函数调用堆栈_服务器_02

1. 这里的这个方格矩阵有必要来简单看一下,因为很多时候大家在讨论大端或者小端的时候少了这样的一个设定。因此,同样的单行方格不同人理解起来不一样。如果有这样的一个约定那么理解起来会好很多,那就是:这种方格图从矩阵角度看,左下角是0,右上角是最大。

2. 关于分区,不同的环境可能有不同的拆分方式。这里提出来了代码区、static区、heap、stack等。在其他的资料中,这些分区信息可能会更多也会更加详细。

1618_x86汇编以及函数调用堆栈_unix_03

         在整个计算机系统中,寄存器有一个比较特殊的地方在于:没地址但是有名字,访问按照名字来。

1618_x86汇编以及函数调用堆栈_寄存器_04

1. 从ebp和esp的介绍来看,可以看得出来这两个信息可以决定一次调用堆栈的空间范围。

2. eip在很多资料中其实是就是PC寄存器。

3. 在RISC-V中,调用函数的时候,参数进入寄存器,而在x86中则是进入stack。

4. 函数调用过程一般来说有11个步骤,这里介绍了第一步:参数传递。需要注意的是,文档的排版需要我们看得时候关注文字后面的一张图。

1618_x86汇编以及函数调用堆栈_存储栈_05

         第二步,存储PC寄存器的信息,在这里叫做EIP。

1618_x86汇编以及函数调用堆栈_寄存器_06

         第三步:修改eip,也就是PC寄存器,这样可以指定下一条的执行指令地址。

1618_x86汇编以及函数调用堆栈_unix_07

         第四步,ebp的信息压栈。如果这个信息压栈了,后续可以在恢复的时候知道该恢复哪一部分堆栈区间。

1618_x86汇编以及函数调用堆栈_服务器_08

         第五步,现在之前的堆栈空间信息已经保存了。接下来直接修改ebp,用来划定最新的堆栈空间的开始位置。这里得再次强调一下ebp寄存器用来存储栈顶空间的,而esp则是用来存储栈底空间的。因此,到此为止两个寄存器划定的新的堆栈空间还没有开辟真正的空间大小。

1618_x86汇编以及函数调用堆栈_存储栈_09

         第六步,有了第五步时候的分析也就可以知道接下来的这步骤中必然有这样的操作了。这一步的过程中,其实是为新的函数执行准备了堆栈空间。

1618_x86汇编以及函数调用堆栈_存储栈_10

         第七步,为函数功能开辟新的堆栈空间并且执行函数。

1618_x86汇编以及函数调用堆栈_服务器_11

         第八步,esp移动擦除现在的堆栈空间。这里我写了一个疑问,因为这里给出来的是最简单的一个场景。实际的场景中,可能并不是这么简单的额单层调用。因此,可能在这一步之前会进行整个动作流程的嵌套。如果是抹除当前的堆栈区,比较简单的方式就是esp直接移动到ebp。

1618_x86汇编以及函数调用堆栈_寄存器_12

         第九步,恢复ebp,这样之前的堆栈空间很大程度上恢复了。

1618_x86汇编以及函数调用堆栈_堆栈_13

         第十步,恢复PC寄存器,也就是eip。

1618_x86汇编以及函数调用堆栈_服务器_14

         第十一步,移除传递参数。

1618_x86汇编以及函数调用堆栈_服务器_15

         在整个过程中,esp是没有保存的。其实,esp的设定是一直指向栈底用来划定空间。而整个调用过程,里面的信息空间其实是固定的偏移处理,因此其实esp不需要存储是可以计算出来的。

1618_x86汇编以及函数调用堆栈_unix_16

         这里通过一个例子进行了简单的过程展示,其实有些步骤是合二为一的。比如说,call指令其实是可以自动处理eip寄存器的信息备份的。而这整个过程,开头以及结束的内容在函数调用中都是很固定的形式内容,因此也称之为函数头尾,可以直接套用。

举报

相关推荐

0 条评论