assembly - 函数的序言可以写在它的框架之外吗?
问题描述
我目前正在尝试分析 N64 旧视频游戏的组装。为此,我使用了一些 N64 调试器来阅读和理解底层 MIPS 代码。
在我正在查看的其中一个电话中,序言定义如下:
ADDIR SP, SP, -0x030
SW RA, 0x0024 (SP)
SW S0, 0x0020 (SP)
SW A1, 0x0034 (SP)
稍后在这个函数中,我们还有两个堆栈推送:
SW V0, 0x002C (SP)
[...]
SW R0, 0x0010 (SP)
我不明白的是:
- 为什么序言subs 0x030,但没有使用所有空间:我们只存储5个寄存器,所以它最多应该subs 0x014
- 为什么,这对我来说是主要问题,A1 是在 SP 之外存储的?堆栈子 0x030,但 A1 保存到 SP[0x034]
我根本不是 ASM 专家,所以我可能会错过一些关于堆栈如何工作的信息,但对我来说,序言中保存的每个数据都应该保存 beetwen SP ans SP - 0x030(在我的情况下)。如果我理解正确,第四行写在另一个函数的堆栈帧上,这似乎很糟糕。
解决方案
一个 MIPS 调用约定让调用者为所有参数分配堆栈内存空间,尽管前 4 个参数(至少)实际上是在寄存器中传递的。
这意味着被调用的函数可以将$a0
, $a1
, $a2
,存储$a3
到堆栈中,并期望这些内存位置可用。
当一个函数调用另一个函数时,作为调用者,它也应该分配相同的 4 个字的堆栈空间。无论如何,调用另一个函数的函数至少需要一个最小的堆栈帧,因此让它分配这 4 个额外的单词在序言和尾声方面是免费的。
部分较旧的调用约定倾向于包括对 varargs(可变参数函数)的支持,这些函数甚至可能无法在 C 代码中正确声明。允许函数将其参数存储回调用者分配的内存允许所有参数刷新到内存并且是连续的,这对于可变参数函数很重要。对所有功能都这样做是矫枉过正,但简化了一些问题。
请参阅https://courses.cs.washington.edu/courses/cse410/09sp/examples/MIPSCallingConventionsSummary.pdf以获取包含寄存器参数的 4 个字的堆栈帧的图片。
在实践中,我认为在调用者的堆栈空间中为被调用者保留 4 个单词被高度高估了,并且有证据表明这已在 RISC V 中被删除,例如。
一些英特尔系统改为使用红色区域,这表示系统同意在堆栈指针下方的某个小距离内(即堆栈的未分配空间)不使用您的堆栈空间。这在 intel 上是有意义的,因为返回地址会自动写入内存,因此不需要像在 MIPS 上那样单独推送,但是这些系统还受益于一些预先分配的堆栈空间,以便使用简单的函数,而不必设置堆栈框架(即红色区域)。
推荐阅读
- vba - 如何在 VBA 中使用多个条件
- node.js - Node.js 错误:找不到模块 'ssl-root-cas/latest'
- prestashop - 如何根据模块的钩子名称制作不同的 tpl?
- qt - Ubuntu 18.04 apt-get qt5-default 搞砸了。变通?
- c# - 在当前目录中找不到 JSON 文件 - 构建中未添加文件夹
- python-3.x - 跟踪python中的悬空线程
- babeljs - 为什么 `yarn outdated` 不显示 babel 7 是最新的?
- c++ - 队列不返回正确的值
- sql - 如何正确实现双向休眠关系?
- ruby - 如何配置 Fedora 29 以使用 ruby 'number-theory' gem?