【问题标题】:Visual C++ 2015 /GL (whole program optimization) and /OPT:REF (optimize references) prevent static initializers being calledVisual C++ 2015 /GL(整个程序优化)和 /OPT:REF(优化引用)防止调用静态初始化程序
【发布时间】:2016-04-25 13:12:45
【问题描述】:

简介: 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++ 中有一些方法可以对它们施加一定的顺序。对于全局静态对象,可以在对象定义前使用#pragma init_seg(SECTIONNAME)来指定某个节名

但最终这只会导致编译器将(__cdecl *)(void) 指向某些链接器部分中的函数指针(它们都以.CRT$XC 开头)。 在链接器确定内存布局之前,节名称按字典顺序排列。默认部分名称为.CRT$XCU。 然后,C/C++ 初始化代码将.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

问题:如何仅针对单个符号避免这种情况?

【问题讨论】:

    标签: c linker visual-c++-2015


    【解决方案1】:

    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)
    

    【讨论】:

      猜你喜欢
      • 2012-12-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-09-17
      • 1970-01-01
      • 2015-05-24
      • 1970-01-01
      • 2021-07-12
      相关资源
      最近更新 更多