_chkstk() 函数的目的是啥?

Posted

技术标签:

【中文标题】_chkstk() 函数的目的是啥?【英文标题】:What is the purpose of the _chkstk() function?_chkstk() 函数的目的是什么? 【发布时间】:2012-01-14 01:36:50 【问题描述】:

我最近使用/FAsu Visual C++ compiler option 输出了一个特别长的成员函数定义的源+程序集。在汇编输出中,堆栈帧建立后,有一个神秘的_chkstk()函数调用。

_chkstk() 上的 MSDN 页面并没有解释调用这个函数的原因。我也看过 Stack Overflow 问题Allocating a buffer of more a page size on stack will corrupt memory?,但我不明白 OP 和接受的答案在说什么。

_chkstk() CRT 函数的用途是什么?它有什么作用?

【问题讨论】:

我的猜测(在阅读了您的链接之后)是该函数设置了保护,因此函数堆栈外的写入将被 CRT 捕获,并且可以像在 C++ 或其他东西中抛出异常一样进行处理。 【参考方案1】:

Windows 页面在使用时为您的线程提供额外堆栈。在堆栈的末尾,有一个保护页面被映射为不可访问的内存——如果程序访问它(因为它试图使用比当前映射的更多的堆栈),就会出现访问冲突。操作系统捕捉到错误,将堆栈的另一页映射到与旧保护页相同的地址,在旧保护页之外创建一个新保护页,并从导致违规的指令恢复。

如果一个函数有不止一页的局部变量,那么它访问的第一个地址可能比当前堆栈末尾多一页。因此,它会错过保护页面并触发操作系统没有意识到的访问冲突,因为需要更多的堆栈。如果所需的总堆栈特别大,它甚至可能超出保护页,超出分配给堆栈的虚拟地址空间的末尾,并进入实际用于其他用途的内存。

所以,_chkstk 确保有足够的空间用于局部变量。您可以想象它通过按页面大小的间隔按递增顺序触摸局部变量的内存来做到这一点,以确保它不会错过保护页面(所谓的“堆栈探测器”)。我不知道它是否真的这样做,但可能它需要更直接的路线并指示操作系统映射到一定数量的堆栈。无论哪种方式,如果所需的总数大于堆栈可用的虚拟地址空间,那么操作系统可以抱怨它而不是做一些未定义的事情。

【讨论】:

阅读您的回答后,我偶然发现了KB100775: Description of the stack checking for Windows NT-based applications。实际上,“[_chkstk()] 只是从当前堆栈指针位置到请求的分配每 4K 触摸一次内存地址。”感谢您详细解释的回答。 如果您遇到访问冲突 - 检查您是否有一个对于堆栈来说太大的变量。巨型数组或数组数组往往是罪魁祸首。 @DanielTrebbien 看起来那个页面不见了。这是它的存档。 archive.is/J01oT【参考方案2】:

我查看了__chkstk 的代码,它确实以一页为间隔进行了重复的堆栈探测。所以这样,它不需要对操作系统进行任何调用。 rax 中的参数是要添加的数据的大小。它确保目标地址(当前rsp - rax)是可访问的。如果rax > rsp,它对地址0执行此操作。作为一个有趣的快捷方式,它首先将地址与gs:[10h]进行比较,这是当前映射的最低页面;如果目标地址 >= this,那么它什么也不做。

顺便说一句,至少对于 64 位代码,它的拼写是两个下划线:__chkstk__

【讨论】:

对于 64 位 Windows,只有一个 gs:[10h] stack-bottom 值。所以 __chkstk-function 总是触及堆栈中的页面。

以上是关于_chkstk() 函数的目的是啥?的主要内容,如果未能解决你的问题,请参考以下文章

mingw 构建错误:未定义对 `__chkstk_ms' 的引用

HexRays - “__OFSUB__()”的目的是啥?

dumpdecrypted Symbol not found: ___chkstk_darwin

dumpdecrypted Symbol not found: ___chkstk_darwin

__cxa_pure_virtual 的目的是啥?

分段错误 chkstk_ms C++