MSVC CRT的全局构造和析构
来源:互联网 发布:javascript格式化输出 编辑:程序博客网 时间:2024/05/19 19:30
在了解了Glibc/GCC的全局构造析构之后,让我们趁热打铁来看看MSVC在这方面是如何实现的,有了前面的经验,在介绍MSVC CRT的全局构造和析构的时候使用相对简洁的方式,因为很多地方它们是相通的。
首先很自然想到在MSVC的入口函数mainCRTStartup里是否有全局构造的相关内容。我们可以看到它调用了一个函数为:
mainCRTStartup:
mainCRTStartup()
{
…
_initterm( __xc_a, __xc_z );
…
}
其中__xc_a和__xc_z是两个函数指针,而initterm的内容则是:
mainCRTStartup -> _initterm:
// file: crt\src\crt0dat.c
static void __cdecl _initterm (_PVFV * pfbegin,_PVFV * pfend)
{
while ( pfbegin < pfend )
{
if ( *pfbegin != NULL )
(**pfbegin)();
++pfbegin;
}
}
其中_PVFV的定义是:
typedef void (__cdecl *_PVFV)();
从_PVFV的定义可以看出,它是一个函数指针类型,__xc_a和__xc_z则都是函数指针的指针。不过第一眼看到_initterm这个函数是不是看着很眼熟呢?对照Glibc/GCC的实现,_initterm长得可谓与__do_global_ctors_aux一模一样,它依次遍历所有的函数指针并且调用它们, __xc_a就是这个指针数组的开始地址,相当于__CTOR_LIST__;而__xc_z则是结束地址,相当于__CTOR_END__。
__xc_a和__xc_z不是mainCRTStartup的参数或局部变量,而是两个全局变量,它们的值在mainCRTStartup调用之前就已经正确地设置好了。我们知道mainCRTStartup作为入口函数是真正第一个执行的函数,那么MSVC是如何在此之前就将这两个指针正确设置的呢?让我们来看看__xc_a和__xc_z的定义:
// file: crt\src\cinitexe.c
_CRTALLOC(".CRT$XCA") _PVFV __xc_a[] = { NULL };
_CRTALLOC(".CRT$XCZ") _PVFV __xc_z[] = { NULL };
其中宏_CRTALLOC 定义于crt\src\sect_attribs.h:
……
#pragma section(".CRT$XCA",long,read)
#pragma section(".CRT$XCZ",long,read)
……
#define _CRTALLOC(x) __declspec(allocate(x))
在这个头文件里,须要注意的是两条pragma指令。形如#pragma section的指令语法如下:
#pragma section( "section-name" [, attributes] )
作用是在生成的obj文件里创建名为section-name的段,并具有attributes属性。因此这两条pragma指令实际在obj文件里生成了名为.CRT$XCA和.CRT$XCZ的两个段。下面再来看看_CRTALLOC这个宏,该宏的定义为__declspec(allocate(x)),这个指示字表明其后的变量将被分配在段x里。所以__xc_a被分配在段.CRT$XCA里,而__xc_z被分配在段.CRT$XCZ里。
现在我们知道__xc_a和__xc_z分别处于两个特殊的段里,那么它是如何形成一个存储了初始化函数的数组呢?当编译的时候,每一个编译单元都会生成名为.CRT$XCU(U是User的意思)的段,在这个段中编译单元会加入自身的全局初始化函数。当链接的时候,链接器会将所有相同属性的段合并,值得注意的是:在这个合并过程中,所有输入的段在被合并到输出段时,是据字母表顺序依次排列。于是在本例中,各个段链接之后的状态可能如图11-11所示。
由于.CRT$XT*这些段的属性都是只读的,且它们的名字很相近,所以它们会被按顺序合并到一起,最后往往被放到只读段中,成为.rdata段的一部分。这样就自然地形成了存储所有全局初始化函数的数组,以供_initterm函数遍历。我们不得不再次惊叹!MSVC CRT的全局构造实现在机制上与Glibc基本是一样的,只不过它们的名字略有不同,MSVC CRT采用这种段合并的模式与.ctor的合并及__CTOR_LIST__和__CTOR_END__的地址确定何其相似!这再一次证明了虽然各个操作系统、运行库、编译器在细节上大相径庭,但是在基本实现的机制上其实是完全相通的。
【小实验】
自己添加初始化函数:
#include <iostream>
#define SECNAME ".CRT$XCG"
#pragma section(SECNAME,long,read)
void foo()
{
std::cout << "hello" << std::endl;
}
typedef void (__cdecl *_PVFV)();
__declspec(allocate(SECNAME)) _PVFV dummy[] = { foo };
int main()
{
return 0;
}
- MSVC CRT的全局构造和析构(1)
- MSVC CRT的全局构造和析构
- MSVC與CRT的恩怨情仇
- MSVC與CRT的恩怨情仇
- C++ 全局对象构造和析构
- MSVC 与 CRT 之间的恩怨情仇
- 深度剖析C++全局构造函数和析构函数的调用机制
- C++带有虚函数的单继承类的构造过程探索,msvc和gcc编译器
- 建立全局和局部对象时,不同的构造函数和析构函数的调用顺序
- 关于全局全局静态局部局部静态的构造与析构顺序
- 全局类对象、静态全局类对象、静态成员类对象、静态局部类对象 的构造和析构过程
- MSVC CRT运行库启动代码分析
- 全局构造函数与析构函数
- glibc与MSVC CRT,crt编译错误及解决
- 程序入口函数和glibc及C++全局构造和析构
- 程序入口函数和glibc及C++全局构造和析构
- MSVC和MinGW的DLL工具
- 全局对象和函数内静态对象调用构造析构函数差异
- LeetCode_18---4SUM
- VC利用console调试和记录日志
- JdbcTemplate学习笔记
- Running Oracle ADF application on High availability (HA) architecture by Vinay Kumar
- Linux 用dd生成指定大小的文件
- MSVC CRT的全局构造和析构
- Android HttpURLConnection 下载xml文件时候 出现ioexception
- 提取VS的Win32SDk用C/C++编译器 (续)——使用方法
- jquery 动态设置图片居中显示
- 我的MYSQL学习心得(1) 简单语法
- JavaScript实现图片上标记多点区域
- AVFoundation的使用
- 登陆页面的用户信息保存实现
- 2- Linux系统入门