@会网络的老鼠

涂飞平的博客空间

栈内存的分配 [原]

12 年前 0

栈内的内存分配虽然在delphi的使用并不多,但有时候为了快速访问,可能会有这个需要,下面截取的代码就是这方面的。
栈内存分配函数,注意这里采用的是register参数传递方式,所以这里size参数将放在eax中!

function StackAlloc(Size:integer): Pointer;register;
asm
pop ecx;//将返回地址弹出到ecx
mov edx,esp;//将弹出后esp指针存放在edx中
add eax,3
and eax,not 3;//这里主要是将eax(即size)取4的整数,因为栈的操作都是4字节的
cmp eax,4092;//比较取值后size与一个页的大小(4092是为后面留下的,因为压栈本身还有4字节)
@@1:
sub esp,4092;//如果size大于一页的话,先将esp减4092(即增加栈空间)
push eax//压入eax,这里将补上4字节空间,这样就是4096了,为什么要这一步呢?因为如果我们
//不是一步一步按页分配,那么我们可能会超出栈范围,如果一步一页,那么如果超出范围
//那么这个时候push操作就会发生异常。
sub eax,4096 //eax减去4096,剩下的再分析
jns @@1 //如果剩下的为正数,说明还要按整页来分配
add eax,4096 //如果为负数,就加上4096,这个时候eax的值就是零头的值了
@@2:
sub esp,eax //再按具体的零头分配栈空间
mov eax,esp //将这个时候的esp值返回到eax,就是返回值了
push edx //将原始的esp压入栈中
mov edx,esp
sub edx,4 //使edx的值存放它自己将压入到的栈地址
push edx //压入,便于释放时候来测试是否是此快内存
push ecx //压入此函数的返回地址,便于函数退出时候返回到断点或者呼叫函数中
end;
而栈内存的清除到是很简单了,因为栈内存中存放了最初的esp地址,所以很容易就可以恢复到初始状态:
procedure StackFree(P: Pointer);register;
asm
pop ecx //保存返回地址到ecx寄存器中
mov edx,dword ptr [esp] //将当前的esp内容放在edx中
sub eax,8 //将P的地址减8字节,其实这个时候应该指向测试的内存块
cmp edx,esp //比较,如果相等的话,表示此内存的确是栈分配函数分配的
jne @@1 //不是的话直接退出,不能强行释放,否则的话,呵呵!
cmp edx,eax //再次确认,如果通过的话就可以大胆释放了
jne @@1
mov esp,dword ptr [esp+4] //直接将原始的栈指针恢复就可以了达到释放栈空间的目的了
@@1:
push ecx //压入返回地址,便于退出到呼叫函数中!
end;
这段代码虽然简单,但技术含量极高,特别是多次分配,每次都有push操作,看似多此一举,其实是步步探测,主要栈空间一越界就会异常,防止此后的其他操作发生!

编写评论