我目前正在阅读有关利用Linux下的内存漏洞的文章,我发现很难找到有关何时确定堆栈帧布局的任何信息。换句话说,它是在编译时,在程序执行之前确定的,还是在调用函数时构建的?操作系统之间的布局是否不同?
答案 0 :(得分:2)
有几个因素。在x86上,有calling convention定义了如何调用函数。我假设其他架构有类似的东西。系统库(例如glibc)可以定义其他约定。但最终编译器决定它如何使用堆栈 - 至少当它不需要连接外部库并且需要遵循它们的堆栈布局时。
答案 1 :(得分:2)
我怀疑您是否会一般或轻松找到有关如何设计堆栈帧的文档答案。正如其他人所观察到的那样,记录的内容是设计过程的结果,通常没有很多相关的理由,我同意这些理由会非常有趣。
堆栈帧布局的每个设计来自可能是人们为特定处理器架构设计编译器或一组可互操作的编译器,甚至可能针对特定OS。这将受到从调用者访问信息的子程序(参数?词法范围?),指令集表现良好(许多寄存器?容易推送参数?),编译器的优点或缺点等因素的影响。微软,例如,随着编译器和x86的发展,这几十年来设计了几次;他们对x86-32的约定与x86-64的约定完全不同。您可以从记录的结果中猜出基本原理,有时会有提示,但并非总是如此。
我可以为我公司在x86上运行的并行编程语言设计“堆栈帧”给你一些想法。
我的观点是堆栈框架设计的基本原理是由机器架构和它应该支持的编程语言的目标驱动的。如上所述的基本原理并没有出现在许多文件中,是的,这使得它很难找到。
给定堆栈帧设计,语言的编译器然后在帧内分配空间,用于编译的特定子例程。
答案 2 :(得分:0)
在编译器时,它是编译器的选择,如果在同一处理器/目标的不同操作系统上使用相同的构建选项,则可以获得相同类型的堆栈帧结果。
堆栈帧使编译器开发人员更容易调试代码以及其他人来读取代码,关于使用堆栈帧是否成本更高是值得商榷的。它也可能使调试器(软件)的生活更轻松,但您必须与编译器紧密同步才能工作。
它们通常不是必需的,不能想象为什么一个调用约定会关心,它只是一个实现的东西,我是否经常必须跟踪函数中的每个点,事物是相对于我改变栈顶,或者我是否想要预先计算整个函数所需的所有堆栈,并将其消耗一次,然后对于函数的其余部分,我可以硬编码所有相关的内容,使其更易于读取和调试代码有时以另一个寄存器为代价,有时不依赖于实现。
堆栈帧是编译器人员的设计选择,是编译时的事情,而不是运行时的事情。如果您使用相同的编译器和相同的选项,您可以在操作系统中获得相同的布局,在同一操作系统或不同的操作系统上使用不同的编译器,并且无法保证使用相同的布局或者两者都使用堆栈帧。