分配一个新的调用堆栈(Allocating a new call stack)

2019-08-17 07:12发布

(我觉得有这个问题的机会很高要么是重复或以其他方式已经在这里回答,但搜索的答案是很难得益于“堆栈分配”和相关术语的干扰。)

我有一个玩具的编译器,我一直在研究一种脚本语言。 为了能够同时它在进步暂停脚本的执行并返回到宿主程序,它有它自己的堆栈:存储简单的块与被使用正常的C代码的操作增加了“堆栈指针”变量对于诸如此类的事情等等,等等。 不感兴趣为止。

目前,我编译成C.但我感兴趣的调查编译成机器代码,以及 - 同时保持次要堆栈,并在预定义的控制点返回到宿主程序的能力。

所以......我想,这是不太可能是用我自己的代码中常规堆栈寄存器的一个问题,我想会发生什么变化寄存器有我自己的事,只要当它所做的一切,恢复(不纠正我,如果我“错了这一点)。 但是 ......如果我想脚本代码调用了其他一些库代码,它是安全离开使用这个“虚拟堆叠”的节目,或者是至关重要的是,它被赋予回用于此目的的原始堆?

像回答这一个和这一个表明堆栈是不是内存传统的块,但是它依赖于特殊的,系统特定的行为与页面错误和诸如此类的东西做的。

所以:

  • 它是安全的堆栈指针移动到内存的一些其他地区? 栈内存是不是“特殊”? 我想,线程库必须做这样的事情,为他们创造更多的堆栈...
  • 假设内存中的任何区域是安全的使用堆栈寄存器和指令来操作,只要我能想到的任何理由将调用与已知的调用深度的任何功能(即没有递归,没有函数指针)一个问题,即量是可用的虚拟堆栈上。 对?
  • 堆栈溢出显然是正常的代码中的问题,无论如何,但会有这样的系统中溢出的任何额外灾难性的后果?

这显然不是确有必要,因为简单地返回指针到真正的堆栈将是完全维修,或为此事在第一时间没有虐待他们,只是忍受用更少的寄存器,我可能不应该这样做在所有的(至少不是由于是明显的从我的深度)。 但我还是好奇的两种方式。 想知道这些事情是如何工作的。

编辑:对不起,当然,应该说。 我工作在x86(32位对我自己的机器),Windows和Ubuntu Linux系统。 没有异国情调。

Answer 1:

所有这些答案都是基于“共同的处理器架构”,并且由于它涉及生成汇编代码,它必须是“针对特定的” - 如果你决定这样做的处理器X,它有堆栈的一些怪异的操作,下面是显然不值得它写在[替代皮] screensurface。 对于x86一般来说,下面的持有,除非另有说明。

 is it safe to move the stack pointers into some other area of memory? 

栈内存是不是“特殊”? 我想,线程库必须做这样的事情,为他们创造更多的堆栈...

这样的内存是不是特别的。 但这不过认为它不是在x86架构下的堆栈段用于限制堆栈使用。 虽然这是可能的,这是相当罕见的实施情况,看看。 我知道,几年前诺基亚不得不使用段在32位模式下的特殊的操作系统。 至于我现在能想到的,这是唯一一个我有任何的接触与使用用于为x86的分割模式描述堆栈段。

假设内存中的任何区域是安全的使用堆栈寄存器和指令来操作,只要我能想到的任何理由将调用与已知的调用深度的任何功能(即没有递归,没有函数指针)一个问题,即量是可用的虚拟堆栈上。 对?

正确。 只是,只要你别指望能要回一些其他的功能,而无需切换回原来的堆栈。 递归的水平有限也是可以接受的,只要栈足够深[有一些肯定很难不递归来解决某些类型的问题 - 二叉树搜索示例]。

堆栈溢出显然是正常的代码中的问题,无论如何,但会有这样的系统中溢出的任何额外灾难性的后果?

事实上,这将是一个艰难的错误,如果你是一个有点凶破解。

我会建议你使用一个呼叫VirtualProtect() (Windows)或mprotect() (Linux的等),所以,如果你的代码不小心走开栈,它崩溃适当地标记为不可读和不可写的“栈底”而不是其他更微妙的未定义行为[因为它不能保证内存的正下方(较低地址)是不可用的,所以你可能会覆盖其他一些有用的东西,如果它不熄灭堆栈,这将导致一些很难调试错误。

加一点的代码,偶尔检查堆栈深度(你知道你的堆栈开始和结束,所以它不应该是很难检查特定堆栈值为[如果你给自己一些“额外缓冲区“范围之外”空间我们就死‘你保护区 - 一个‘”堆栈的顶部和之间的’崩溃区’,因为他们会打电话,如果它是在发生碰撞车]您也可以填写与整个堆栈识别图案,检查有多少是“不变”。



Answer 2:

通常情况下,在x86上,你可以只要使用现有的堆栈没有任何问题:

  • 你不溢出它
  • 你不递增堆栈指针寄存器(与popadd esp, positive_value / sub esp, negative_value )超出了你的代码开始(如果你这样做,中断或异步回调(信号)或使用堆栈将垃圾任何其他活动它的内容)
  • 你不会引起任何CPU异常(如果你这样做,异常处理的代码可能无法展开栈到异常可处理最近的点)

这同样适用于使用的存储器的不同块临时堆栈和指点esp到其端部。

与异常处理和堆栈展开的问题与你的编译的C和C ++代码中包含类似的范围内的一些异常处理相关的数据结构的事实做eip与链接到他们各自的异常处理(这告诉其中最接近的例外处理器是每一段代码),也有相关的调用函数的标识的一些信息(即返回地址是堆栈等)上,这样你就可以泡了异常。 如果你只需要插上原始机器代码到这个“框架”,您将无法正常扩展这些异常处理的数据结构来覆盖它,如果出了问题,他们很可能会走的很错的(整个过程可能会崩溃或被损坏,尽管你有周围生成的代码异常处理程序)。

所以,是的,如果你很小心,你可以打堆。



Answer 3:

你可以用你喜欢该处理器的堆栈中的任何区域(模内存保护)。

从本质上讲,你只需用一个指针到新的区域加载ESP寄存器(“MOV ESP,......”),然而你设法分配它。

你必须有足够的程序 ,不管它可能调用(如Windows操作系统API),以及任何有趣的行为操作系统了。 你也许可以找出你的代码需要多大的空间; 一个好的编译器可以很容易地做到这一点。 搞清楚有多少是由Windows需要的是更硬; 你总是可以分配“的方式太多了”,这是什么样的Windows程序往往做。

如果您决定严格管理这个空间,你可能不得不切换书库调用Windows函数。 这是不够的; 你可能会得到通过各种Windows惊喜烧毁。 我在这里介绍其中的一个窗口:避免推栈完整的x86背景 。 我有平庸的解决方案,但对于这个并不好解决方案。



文章来源: Allocating a new call stack