Visual C++ 2015 /GL(整个程序优化)和/OPT:REF(优化引用)防止调用静态初始值设定项

Visual C++ 2015 /GL (whole program optimization) and /OPT:REF (optimize references) prevent static initializers being called

简介: C++中的全局静态对象在main()启动前初始化。考虑:

#include <stdio.h>
int calc_it() {
   return 1;
}
int glob = calc_it();
int main() {
   printf("glob = %d\n", glob);
   return 0;
}

输出是glob = 1,因为calc_it()和赋值是在main()开始之前执行的。代码顺序与此无关

现在想象一下,您有多个包含这样代码的源文件,并且进一步想象它们以某种方式相互依赖(无论出于何种原因,您都需要某种执行顺序。让我们先不讨论这是好设计还是坏设计.)

标准未定义执行顺序,但在 Visual C++ 中存在对它们施加特定顺序的方法。对于global static objects,可以在object定义前使用#pragma init_seg(SECTIONNAME)指定某个section name

但最后这只会导致编译器将 (__cdecl *)(void) 指针指向某些链接器部分中的函数(它们都以 .CRT$XC 开头)。 在链接器确定内存布局之前,节名称按字典顺序排序。默认的部分名称是 .CRT$XCU。 C/C++ init代码然后将.CRT$XCA.CRT$XCZ之间的这些段的内容视为指向函数的指针并逐一调用它们。

我们也可以使用 #pragma data_seg(SECTIONNAME) 指令手动完成此操作。所以这个:

#include <stdio.h>
void hi_there() {
   printf("hi there!\n");
}
int main() {
   printf("bye!\n");
   return 0;
}
#pragma data_seg(".CRT$XCM")
typedef void (__cdecl *atexit_func)(void);
atexit_func _init_ptr[] = { hi_there };

将输出:

hi there!
bye!

这有多好? :)

问题描述: AFAIK,自 Visual C++ 2015 起,如果您将 /GL 选项(全程序优化)与 /OPT:REF 链接器选项(删除未使用的函数和数据)一起使用,这将不再有效。原因可能是从链接器的角度来看 _init_ptr 从未使用过。在旧的 Visual Studio 版本中,这仍然有效,因为他们从未删除未使用的 data,仅使用了 code.

问题:如何避免仅对单个交易品种出现这种情况?

Visual Studio 链接器可以选择包含某个符号,无论它是否被引用:/INCLUDE:symbol。 Visual C++ 提供了一种将此链接器选项添加到已编译目标文件的方法:#pragma comment(linker, "/include:symbol")

即使使用 cl /O2 /GL x.cpp /link /OPT:REF(即对于 x86,32 位)编译,以下代码也会 运行:

#include <stdio.h>
void hi_there() {
   printf("hi there!\n");
}
int main() {
   printf("bye!\n");
   return 0;
}
#pragma data_seg(".CRT$XCM")
typedef void (__cdecl *atexit_func)(void);
extern "C" atexit_func _init_ptr[] = { hi_there };
#pragma comment(linker, "/include:__init_ptr")

请注意 /include:extern "C" 之后的前导额外下划线以防止名称混淆。

更新: 为了让代码也能编译为 x64,我们需要添加一些额外的东西:

#include <stdio.h>
void hi_there() {
   printf("hi there!\n");
}
int main() {
   printf("bye!\n");
   return 0;
}

#ifdef _M_X64
#define INCLUDE_SYM(s) comment(linker, "/include:" #s)
#else
#define INCLUDE_SYM(s) comment(linker, "/include:_" #s)
#endif

#pragma data_seg(".CRT$XCM")
#pragma section(".CRT$XCM", read)
typedef void (__cdecl *atexit_func)(void);
extern "C" atexit_func _init_ptr[] = { hi_there };
#pragma INCLUDE_SYM(_init_ptr)