main函数之前执行代码

来源:互联网 发布:linux dns 编辑:程序博客网 时间:2024/06/11 13:11

main函数之前执行代码 

有的时候,需要会遇到这样的一个情况,“想要在main或者WinMain函数执行前初始化我们的系统框架,和在之后释放我们的系统框架”, 如果这样,我们该怎么做呢?笔者今天放假,来公司继续解决昨天剩余的问题,然后调试exit函数的时候,会发现它会调用

static void __cdecl doexit (int code, int quick,int retcaller)这样的一个内部函数,那么继续单步调试,那么你就会发现

[cpp] view plaincopyprint?
  1. /* cache the function to call. */  
  2. function_to_call = (_PVFV)_decode_pointer(*onexitend);  
  3.   
  4. /* mark the function pointer as visited. */  
  5. *onexitend = (_PVFV)_encoded_null();  
  6.   
  7. /* call the function, which can eventually change __onexitbegin and __onexitend */  
  8. (*function_to_call)();  
  9.   
  10. onexitbegin_new = (_PVFV *)_decode_pointer(__onexitbegin);  
  11. onexitend_new = (_PVFV *)_decode_pointer(__onexitend);  

这段代码,OK,我们移上去(function_to_call)看看,你会发现是那些全局类变量的析构调用,那么我们继续往下面走,_initterm(__xp_a, __xp_z); 这个函数, 我第一次看到这个函数的时候,莫名其妙想在里面做文章了。。。OK,我们去看看这个函数内部到底是干什么的

[cpp] view plaincopyprint?
  1. /* from the CRT */  
  2. typedef void (*_PVFV)(void);  
  3. void _initterm(_PVFV *begin, _PVFV *end)  
  4. {  
  5.    _PVFV *cur;  
  6.    for (cur = begin; cur < end; cur++)  
  7.    {  
  8.       /* skip NULL pointers */  
  9.       if (*cur)  
  10.       {  
  11.          (**cur)();  
  12.       }  
  13.    }  
  14. }  

很容易就可以看到,他就是一个循环遍历,执行每个元素,而仔细看看,那每个元素不就是一个函数指针吗? OK,我们想实现前面所说的功能,该怎么做呢?怎么能够自动往里面添加我们的函数指针呢? 如果能够自由添加我们的函数指针的话,就可以在main和WinMain之前,之后分别调用我们自己的 “初始化”“退出”函数了。那么我们继续在这个源文件中“crt0dat.c”中看看其他代码,或许你会幸运的发现如下代码:

[cpp] view plaincopyprint?
  1. extern _CRTALLOC(".CRT$XIA") _PIFV __xi_a[];  
  2. extern _CRTALLOC(".CRT$XIZ") _PIFV __xi_z[];    /* C initializers */  
  3. extern _CRTALLOC(".CRT$XCA") _PVFV __xc_a[];  
  4. extern _CRTALLOC(".CRT$XCZ") _PVFV __xc_z[];    /* C++ initializers */  
  5. extern _CRTALLOC(".CRT$XPA") _PVFV __xp_a[];  
  6. extern _CRTALLOC(".CRT$XPZ") _PVFV __xp_z[];    /* C pre-terminators */  
  7. extern _CRTALLOC(".CRT$XTA") _PVFV __xt_a[];  
  8. extern _CRTALLOC(".CRT$XTZ") _PVFV __xt_z[];    /* C terminators */  

MS也给我们写了一点注释, __xi_a[] 到 __xi_z[] 是用于 C语言初始化用的, __xc_a[] 到 __xc_z[] 适用于C++初始化用的,而

__xp_a[] 到 __xp_z[] 适用于C基本库预结束用的, __xt_a[] 到 __xt_z[] 适用于C基本库结束用的。通过这里,我们可以看到那个字符串中的英文字母".CRT$XIA" - ".CRT$XIZ", 我们可以分析出一些简单的规律,

I --> C initialize;

C --> C++ Initialize

P --> C PreUninitialize

T --> CUninitialize

这时候,我们需要查找 _CRTALLOC 宏定义, 通过查找它所引用的头文件,可以看到其中的sect_attribs.h:

[cpp] view plaincopyprint?
  1. ......................................  
  2.   
  3. #pragma section(".CRTMP$XCA",long,read)  
  4. #pragma section(".CRTMP$XCZ",long,read)   
  5. #pragma section(".CRTMP$XIA",long,read)  
  6. #pragma section(".CRTMP$XIZ",long,read)   
  7.   
  8. ......................................  
  9.   
  10. #define _CRTALLOC(x) __declspec(allocate(x))  

OK,得到上述的那些信息,我们可以开始尝试了,输入以下代码:

[cpp] view plaincopyprint?
  1. #include <stdio.h>   
  2. #include <stdlib.h>   
  3. #include <malloc.h>   
  4.   
  5. class A  
  6. {  
  7. public:  
  8.     A()  
  9.     {  
  10.         printf("A()\n");  
  11.     }  
  12.     ~A()  
  13.     {  
  14.         printf("~A()\n");  
  15.     }  
  16. };  
  17.   
  18. A a;  
  19.   
  20. bool b = false;  
  21. int foo()  
  22. {  
  23.     if (!b)  
  24.     {  
  25.         printf("init now!\n");  
  26.         b = true;  
  27.     }  
  28.     else  
  29.     {  
  30.         printf("uninit now\n");  
  31.     }  
  32.     return 0;  
  33. }  
  34.   
  35.   
  36. typedef int (__cdecl *_PVFV)();  
  37. #pragma section(".CRT$XIU", long, read)  
  38. #pragma section(".CRT$XPU", long, read)   
  39. __declspec(allocate(".CRT$XIU")) _PVFV mgt_startup[] = { foo };  
  40. __declspec(allocate(".CRT$XPU")) _PVFV mgt_exit[] = { foo };  
  41.   
  42. int main()  
  43. {  
  44.     printf("main.!\n");  
  45.     //exit(1);   
  46.     return 0;  
  47. }  

那么,你可以Ctrl+F5测试一下, 你可以看到 init  A() main() ~A() uninit 这样的顺序,呵呵,总之目标实现啦,其实原理也很简单的,

main函数执行之前主要是初始化系统资源

1、设置栈指针。

2、初始化static静态和global全局变量,即data段内容。

3、将未初始化部分的赋初值:数值型short,int,long等为0,bool为FALSE,指针为NULL,等等,即.bss段的内容。

4、运行全局构造器,估计是C++中构造函数之类的吧

5、将main函数的参数,argc,argv等传递给main函数,然后才真正运行main函数



原创粉丝点击