2008年10月10日星期五

_chkstk() 分析

_chkstk() 分析 (2008-07-21 22:07:32)
标签:it 分类:技术

_chkstk()
作者: happylong (SXL)
1 什么是_chkstk() ?
“没有比源码还能说明问题的东西了” – 我说的

1.1 VC 6.0生成
_chkstk:

push ecx

cmp eax,1000h

lea ecx,[esp+8]

jb lastpage

probepages:

sub ecx,1000h

sub eax,1000h

test dword ptr [ecx],eax

cmp eax,1000h

jae probepages (0040818c)

lastpage:

sub ecx,eax

mov eax,esp

test dword ptr [ecx],eax

mov esp,ecx

mov ecx,dword ptr [eax]

mov eax,dword ptr [eax+4]

push eax

ret

1.2 分析:
大概流程

1) “chkstk Routine is a helper routine for the C compiler. For x86

compilers, _chkstk Routine is called when the local variables exceed

4096 bytes; for x64 compilers it is 4K and 8K respectively.”

即, 当一个函数局部变量超过一个页面大小4k的时候, 编译器会自动插入这个函数。插入这个函数的位置在:

push ebp

mov ebp,esp

mov eax,1000D4h

call _chkstk()

2) 这个函数做什么?当函数为堆栈分配的页面不够时, (堆栈默认大小为1M), 堆栈需要

更多的页面时调用这个函数。

当堆栈使用大于分配的大小(默认1M)时,产生_XCPT_UNABLE_TO_GROW_STACK.



3) 当调用这个函数时, 首先外面对eax进行赋值(已经分配的堆栈大小 + 即将分配给函数局部变量的堆栈大小)



4) 调用 _chkstk() 首先,保持当期esp到eax中,然后开始判断:如果分配的大小大于一个页面, 到第5)步(大多都先第5)步);否则到第6)步。



5) 当需要分配的大小大于一个页面, 则增加一个页面。“sub eax 1000h” 表示堆栈栈顶下移1000h,[由于堆栈是高地址(栈底部)->低地址(栈顶部)分布],所以堆栈扩大了1000h; “sub ecx 1000h”表示分配了1000h(1个页面)之后还需要多少空间;

“test dword ptr [ecx],eax” 表示分配空间,这个时候之前只不过是分配虚存,内存没有 commit ,这个时候对这个内存地址进行读写操作都会引发一个 page fault 异常(_XCPT_GUARD_PAGE_VIOLATION), OS捕获这个异常,检查一定的条件,适合的时候就把这个内存页 commit 了,即分配了实际的物理内存。然后再次比较需要多少内存,如果还是超过1页(1000h), 则重复第5)步,否则到第6)步。



6) 还需要分配的堆栈空间小于1页的时候,“sub ecx,eax” 堆栈继续扩大(扩大了剩余大小的空间);然后“mov esp,ecx”,保存到原来的esp;并且通过“test”为堆栈分配空间。



7) 最后,esp的值不再是原来的值,堆栈的大小变成: 堆栈原来大小 + 局部变量需要的堆栈大小 + xx (push 用的一点堆栈的零头).这个时候 ebp 和 esp如下 :(图搞不上去。。。。。)

没有评论: