如果有全局类数组,在main之前类数组会被初始化, 类的构造会被调用.
全局类的初始化由_cinit的第二个_initterm发起.
_cinit 在\VC98\CRT\SRC\CRT0DAT.C
void __cdecl _cinit (
void
)
{
/*
* initialize floating point package, if present
*/
#if defined (_M_MRX000) || defined (_M_ALPHA) || defined (_M_PPC)
/*
* MIPS compiler doesn't emit external reference to _fltused. Therefore,
* must always force in the floating point initialization.
*/
_fpmath();
#else /* defined (_M_MRX000) || defined (_M_ALPHA) || defined (_M_PPC) */
if ( _FPinit != NULL )
(*_FPinit)();
#endif /* defined (_M_MRX000) || defined (_M_ALPHA) || defined (_M_PPC) */
/*
* do initializations
*/
_initterm( __xi_a, __xi_z );
/*
* do C++ initializations
*/
// __xc_a(函数指针表开始地址) 和 __xc_z (函数指针表结束地址) 是编译器填写的
// 从__xc_a开始, 到 (__xc_z - 4)是函数指针表, 里面的内容是代理函数地址.
_initterm( __xc_a, __xc_z ); ///< 负责全局类的初始化, 调用类的构造
}
_initterm 也在\VC98\CRT\SRC\CRT0DAT.C
#ifdef CRTDLL
void __cdecl _initterm (
#else /* CRTDLL */
static void __cdecl _initterm (
#endif /* CRTDLL */
_PVFV * pfbegin,
_PVFV * pfend
)
{
/*
* walk the table of function pointers from the bottom up, until
* the end is encountered. Do not skip the first entry. The initial
* value of pfbegin points to the first valid entry. Do not try to
* execute what pfend points to. Only entries before pfend are valid.
*/
// cmp a, b
// a 和 b 都是无符号数,
// 当 a >= b 时, CF = 0, jnb成立
// 当 a < b 时, CF = 1, 会进入到循环内
// jnb xx // 跳到循环外
while ( pfbegin < pfend ) //< 这里的反汇编比较是 jnb
{
// jnb 不成立时, 进入到这里
/*
* if current table entry is non-NULL, call thru it.
*/
if ( *pfbegin != NULL )
(**pfbegin)();
++pfbegin;
}
}
185: * do initializations
186: */
187: _initterm( __xi_a, __xi_z );
00404282 68 20 B6 42 00 push offset ___xi_z (0042b620)
00404287 68 0C B3 42 00 push offset ___xi_a (0042b30c)
0040428C E8 7F 01 00 00 call _initterm (00404410)
00404291 83 C4 08 add esp,8
188:
189: /*
190: * do C++ initializations
191: */
192: _initterm( __xc_a, __xc_z );
00404294 68 08 B2 42 00 push offset ___xc_z (0042b208)
00404299 68 00 B0 42 00 push offset ___xc_a (0042b000)
0040429E E8 6D 01 00 00 call _initterm (00404410)
004042A3 83 C4 08 add esp,8
193:
194: }
0042b000 ~ 0042b204 是初始化函数指针表
debug版里面不一定都是有函数地址,可能有一大片是空的.
release版会做成有效函数地址连续摆放.
初始化函数指针表中放的是初始化代理函数,离真正用户写的初始化函数还有好远.
00404428 8B 45 08 mov eax,dword ptr [pfbegin]
0040442B 83 C0 04 add eax,4
0040442E 89 45 08 mov dword ptr [pfbegin],eax
函数 S3是代理函数 E2在初始化函数指针表中的2级指针
0042B104 A0 12 40 00 00 00 00 00 00 00 00 00 00 00 00 00 ..@………….
$E2是代理函数
$E2:
004012A0 55 push ebp
004012A1 8B EC mov ebp,esp
004012A3 83 EC 40 sub esp,40h
004012A6 53 push ebx
004012A7 56 push esi
004012A8 57 push edi
004012A9 8D 7D C0 lea edi,[ebp-40h]
004012AC B9 10 00 00 00 mov ecx,10h
004012B1 B8 CC CC CC CC mov eax,0CCCCCCCCh
004012B6 F3 AB rep stos dword ptr [edi]
004012B8 E8 23 00 00 00 call $E1 (004012e0)
004012BD 5F pop edi
004012BE 5E pop esi
004012BF 5B pop ebx
004012C0 83 C4 40 add esp,40h
004012C3 3B EC cmp ebp,esp
004012C5 E8 16 18 00 00 call __chkesp (00402ae0)
004012CA 8B E5 mov esp,ebp
004012CC 5D pop ebp
004012CD C3 ret
call $E1 (004012e0)
$E1 才是用户写的初始化代码
307: COffset g_AryMoveToNextStep[4] = {COffset(-1, 0), COffset(0, -1), COffset(1, 0), COffset(0, 1)};
004012E0 55 push ebp
004012E1 8B EC mov ebp,esp
004012E3 83 EC 40 sub esp,40h
004012E6 53 push ebx
004012E7 56 push esi
004012E8 57 push edi
004012E9 8D 7D C0 lea edi,[ebp-40h]
004012EC B9 10 00 00 00 mov ecx,10h
004012F1 B8 CC CC CC CC mov eax,0CCCCCCCCh
004012F6 F3 AB rep stos dword ptr [edi]
004012F8 6A 00 push 0
004012FA 6A FF push 0FFh
004012FC B9 28 F4 42 00 mov ecx,offset g_AryMoveToNextStep (0042f428)
00401301 E8 4F FD FF FF call @ILT+80(COffset::COffset) (00401055)
00401306 6A FF push 0FFh
00401308 6A 00 push 0
0040130A B9 30 F4 42 00 mov ecx,offset g_AryMoveToNextStep+8 (0042f430)
0040130F E8 41 FD FF FF call @ILT+80(COffset::COffset) (00401055)
00401314 6A 00 push 0
00401316 6A 01 push 1
00401318 B9 38 F4 42 00 mov ecx,offset g_AryMoveToNextStep+10h (0042f438)
0040131D E8 33 FD FF FF call @ILT+80(COffset::COffset) (00401055)
00401322 6A 01 push 1
00401324 6A 00 push 0
00401326 B9 40 F4 42 00 mov ecx,offset g_AryMoveToNextStep+18h (0042f440)
0040132B E8 25 FD FF FF call @ILT+80(COffset::COffset) (00401055)
00401330 5F pop edi
00401331 5E pop esi
00401332 5B pop ebx
00401333 83 C4 40 add esp,40h
00401336 3B EC cmp ebp,esp
00401338 E8 A3 17 00 00 call __chkesp (00402ae0)
0040133D 8B E5 mov esp,ebp
0040133F 5D pop ebp
00401340 C3 ret
在_cinit的第二个_initterm的入参上,能找到初始化函数的地址. 此时,是在main之前跑的代码.