0xb*_*7ed 8 c windows 64-bit assembly calling-convention
我一直在阅读Windows x64 ABI上很长篇的非常好的文章目录.这些文章的一个非常小的方面是帧指针的描述.一般要点是,因为Windows x64调用堆栈规则非常严格,所以通常不需要专用的帧指针,尽管它是可选的.
我一直注意到的一个例外是当alloca()用于在堆栈上动态分配内存时.这样做的功能显然需要一个帧指针.例如,引用微软关于"堆栈分配"的文档(斜体和粗体由我添加):
如果在函数中动态分配空间(alloca),则必须使用非易失性寄存器作为帧指针来标记堆栈固定部分的基础,并且必须在prolog中保存和初始化该寄存器.请注意,当使用alloca时,来自同一调用者的对同一被调用者的调用可能具有其寄存器参数的不同家庭地址.
对此,Microsoft的x64 ABI alloca()文档密码添加:
_alloca需要16字节对齐,并且还需要使用帧指针.
首先,为什么必须使用它?我假设在异常时调用堆栈展开,但我还没有找到令人满意的解释.
下一个问题:它必须指向哪里?在上述两个引文中的第一个中,它表示"必须"用于标记" 堆栈的固定部分 "的基础.什么是"堆栈的固定部分"?我得到的印象是,这个术语在给定的框架中表示包含的地址范围(较高地址到较低地址):
同样,我还没有找到这个"固定部分"的令人满意的定义.我上面链接的"堆栈分配"页面包含下面的图表以及"如果使用,则堆栈指针通常指向此处":

这个非常漂亮的博客文章同样含糊不清,包括一个图表,说明框架指针"点在这里的某处",其中"here"是保存的非易失性寄存器和本地的地址.
最后一点含糊不清,来自微软的MSDN文章"动态参数堆栈区域构建",其中仅包含以下内容:
如果使用帧指针,则存在动态创建参数堆栈区域的选项.目前在x64编译器中没有这样做.
"一般"是什么意思?"在这里的某个地方"在哪里?存在的选项是什么?有规则吗?谁在乎?
或者,tl;博士:标题要求什么.任何包含带注释的程序集的答案都会感激不尽.
该图清楚地表明帧指针指向本地堆栈帧的固定部分的底部."固定部分"是其大小不变并且其位置相对于初始堆栈指针固定的部分.在图中,它标记为"局部变量和保存的非易失性寄存器."[1]
帧指针的精确位置对操作系统无关紧要,因为从信息理论的角度来看,局部变量与alloca进入函数时立即分配的内存无法区分.
void function1()
{
int a;
int *b = (int*)alloca(sizeof(int));
...
}
void function2()
{
int& a = *(int*)alloca(sizeof(int));
int *b = (int*)alloca(sizeof(int));
...
}
Run Code Online (Sandbox Code Playgroud)
操作系统无法区分这两个功能.它们都存储a在非易失性寄存器正下方的堆栈中.
这种等价性是图表"一般"说的原因.在实践中,编译器将其指向指示的位置,但理论上它们可以将其指向本地帧内的任何位置,只要从帧指针到返回地址的距离是常数即可.
该函数需要通知操作系统帧指针所在的位置,以便在异常处理期间可以展开堆栈.没有这些信息,就不可能走栈,因为框架是可变大小的.
[1]你可以从文本说框架指针指向"堆栈固定部分的基础"的事实推断出这一点,图表说"框架指针通常指向这里",它指向的是局部变量的基础和保存的非易失性寄存器.假设文本和图表一致,这意味着堆栈的固定部分与局部变量相同并保存非易失性寄存器.这是你每天所做的同样的推断,甚至没有意识到.例如,如果一个故事说
莎莉叫她哥哥."比利,你在哪里?"
你可以推断比利是莎莉的兄弟.
| 归档时间: |
|
| 查看次数: |
2171 次 |
| 最近记录: |