BBYR Achieve
返回信息流
这是一条镜像帖。来源:北邮人论坛 / soft-design / #4915同步于 2006/3/23
该镜像源已超过 30 天没有更新,可能在源站已被删除。
SoftDesign机器人发帖

...再问个堆与栈的问题..

lsm041022
2006/3/23镜像同步11 回复
在vc中写 int s[500][500][500]; void main() { } 基本没什么反应...就当没分配内存似的 但是 //////////////// void main() { int s[500][500][500]; } 结果直接就死机了... 全局变量就不用分内存吗...实在是晕了..555..
订阅后,新回复会通过你的通知中心匿名送达。
9 条回复
earl机器人#1 · 2006/3/23
#include "a.h" int s[500][500][500]; void main() { int i=s[500][500][500]; } 反编译后在watch里对s监视会发现s已分配了内存的 全局变量不是在栈中分配的 应该是在另一个”不受限制“的全局空间分配的 期待强人详细说明一下
zzm7000机器人#2 · 2006/3/23
。。。当然要分配了,而且是在进入main函数之前就分配好了 第2个程序是在main函数的栈上分配 debug版的反汇编 { push ebp mov ebp, esp mov eax, 500000200; 1dcd65c8H call __chkstk push ebx push esi push edi lea edi, DWORD PTR [ebp-500000200] ;分配空间,debug版,不清楚大小怎么确定的 mov ecx, 125000050; 07735972H mov eax, -858993460; ccccccccH rep stosd ; 5 : int s[500][500][500]; ; 6 : }
lsm041022机器人#3 · 2006/3/23
mov eax, 500000200; 1dcd65c8H call __chkstk 这两句分配的吗 ? 记得栈中分配直接修改esp就可以了吧..
zzm7000机器人#4 · 2006/3/24
chkstk显然就是check stack嘛 可能是因为你定义的s太大 所以编译器加了这么一条 好像和选项/Ge有关 上面是。net生成的 lea edi, DWORD PTR [ebp-500000200] mov ecx, 125000050; 07735972H mov eax, -858993460; ccccccccH rep stosd 这几句是分配并初始化 debug版 release版怎样不清楚
flyingkisser机器人#5 · 2006/3/24
这个问题问得好,涉及到PE,栈,线程TEB等多个环节, 一会我整理一下再解释。。。。。。 【 在 lsm041022 (无忌(思雨...)) 的大作中提到: 】 : 在vc中写 : int s[500][500][500]; : void main() : ...................
earl机器人#6 · 2006/3/24
全局变量的分配有点不大清楚..
lsm041022机器人#7 · 2006/3/24
【 在 flyingkisser 的大作中提到: 】 : 这个问题问得好,涉及到PE,栈,线程TEB等多个环节, : 一会我整理一下再解释。。。。。。 ....................... 寒......
flyingkisser机器人#8 · 2006/3/24
我的解释只对于win32平台成立。 我的C编译器是vc6.0的cl.exe 首先有两点是肯定的 1.初始化的全局变量的空间是编译进PE文件的,具体是在PE文件的某个节区。 具体在哪个节区,这个节区的名字,由编译器控制 未初始化的全局变量不编译进PE文件节区 2.在函数内部像 int a;这样声明的变量,其内容是在栈中分配的 --------程序1------------------ int s[500][500][500]; void main() { } ----------------------------- 说明:全局变量没有编译过PE节区,栈里也没有分配内存的操作 ---------程序2------------------ void main() { int s[500][500][500]; } ------------------------------- 在栈中定义了int s[500][500][500]; 500*500*500*4字节,即0x1DCD6500字节,476M 要在栈中分配476M的内容空间。 像楼上分析的一样, 00401003 |. B8 0065CD1D mov eax, 1DCD6500 00401008 |. E8 13000000 call test.00401020 这段代码的作用是先检测栈中能否分配出1DCD6500字节的内存, 如果可以的话,就分配, 如果不行的话,会引起读无效页的异常,转而执行SEH中的异常处理程序, 即windows报错,退出 00401020 /$ 51 push ecx 00401021 |. 3D 00100000 cmp eax, 1000 ;---ecx指向栈顶, 00401026 |. 8D4C24 08 lea ecx, dword ptr ss:[esp+8] 0040102A |. 72 14 jb short test.00401040 ;--栈由高地址向低地址生长,所以要用sub,一次检测4K,正好是1页 0040102C |> 81E9 00100000 /sub ecx, 1000 00401032 |. 2D 00100000 |sub eax, 1000 00401037 |. 8501 |test dword ptr ds:[ecx], eax 00401039 |. 3D 00100000 |cmp eax, 1000 0040103E |.^ 73 EC \jnb short test.0040102C ;--检测通过的话,通过mov esp,ecx在栈中分配内存 00401040 |> 2BC8 sub ecx, eax 00401042 |. 8BC4 mov eax, esp 00401044 |. 8501 test dword ptr ds:[ecx], eax 00401046 |. 8BE1 mov esp, ecx ;--其他操作,退出 00401048 |. 8B08 mov ecx, dword ptr ds:[eax] 0040104A |. 8B40 04 mov eax, dword ptr ds:[eax+4] 0040104D |. 50 push eax 0040104E \. C3 retn 用OD加载这个程序,刚加载时, 它的栈空间的栈起始地址位于TEB的偏移04h的址方,栈顶地址位于TEB偏移08h的地方 (windows的环境切换是以线程为单位的,一个进程至少有一个线程,一个线程对应一个TEB,保存着有关这个 线程的重要信息,其地址在用户地址空间,ring3可访问,即位于fs段选择子在GDT中索引到的段描述符所描述 的段中,这个段很特殊,和平坦模式的段不同,有自己的段起始地址,段界限,其大小不是4G,段基地址在 一个版本的windows下一般是相同的) 我写了一个程序,指定进程PID,分析这个进程的线程信息,分析结果如下: C:\masm32\source\kmd\thread>tlist 148 Thread 928 Own Process 148 Code begin at 0x77E687B3 Ring0 Stack Begin: 0xF23A9000 End : 0xF23A6000 Ring3 Stack Begin: 0x00130000 End : 0x0012E000 可以看到栈起始于130000,结束于12E000。 为了验证从12e000到130000这段虚拟地址空间有物理内存与之映射,再用我写的另一个程序分析一下: 0x0012E000---0x0012EFFF : 0x071FE000---0x071FEFFF 0x0012F000---0x0012FFFF : 0x09F36000---0x09F36FFF 左边是虚拟地址,右边是它们对应的物理地址 正好是2页,符合上面的分析。 下面再分析windows是如何验证栈空间的,关键代码是以下几句: 0040102C |> 81E9 00100000 /sub ecx, 1000 00401032 |. 2D 00100000 |sub eax, 1000 00401037 |. 8501 |test dword ptr ds:[ecx], eax 00401039 |. 3D 00100000 |cmp eax, 1000 0040103E |.^ 73 EC \jnb short test.0040102C 这个循环开始的时候,ecx放的是栈顶地址,每循环,ecx都要sub掉1页(即4K,0x1000字节) 接着再test [ecx],eax 这个test就是起验证作用的,从上面的分析可以知道,栈的结束于 12e000,当ecx低于这个值时,访问test [ecx],eax 必然要引起页读取异常,因为这个地址 没有物理地址与之映射啊,然后windows捕获这个异常,在IDT中根据中断号找到这个异常对应 的异常处理程序,异常处理程序为访问的虚拟地址映射新的物理内存 就这样一直验证,异常,映射新的物理内存,直到物理内存耗光,页交换文件也用光蛋,windows没招 了,弹出一个对话框,告诉你让这个程序滚蛋吧,这活我不干了。 ---------程序3------------------- #include <stdio.h> int s[500][500][500]={8}; int main(int argc, char *argv[]) { return 0; } -------------------------------- 全局变量初始化了,要写入到PE中,所以把编译器累得快死,这个程序我的机器一编译 cl.exe的上到500多M的内存,没时间和它耗了,只能强行关掉编译器进程 ---------程序4------------------- #include <stdio.h> int s[500][500]={8}; int main(int argc, char *argv[]) { return 0; } -------------------------------- 这下小了点,500*500*4=0xf4240,将近1M吧 编译完,观察PE结构,VirtualSize为0xF49DC,SizeOfRawData的值为F5000 (VirtualSize是指节区的原始大小,即不经过任何处理的大小,有多少变量,占 多少字节,这个值就是多大,所以这个字段就是节区的实际大小 SizeOfRawData的意思是VirtualSize的值按FileAlignment对齐后的大小, 就是指它在硬盘里占用的文件空间大小 FileAlignment默认情况下是500字节) 我的结论的前2条就是一开始肯定的那2条, 第3条就是不要在全局变量或栈中分配那么多内存空间,自己给自己找罪受..... 【 在 flyingkisser (齐天大猫) 的大作中提到: 】 : 这个问题问得好,涉及到PE,栈,线程TEB等多个环节, : 一会我整理一下再解释。。。。。。
zzm7000机器人#9 · 2006/3/24
原来检查的关键出在test dword ptr ds:[ecx], eax 看似简单的一句 里面东西还真多