New Compiler Warnings for Dynamic Initialization and User Defined Sections

Xiang Fan

We’ve added two new warnings to MSVC for misuses of binary sections reserved for the compiler. This short post will explain the issues which they warn against, and potential fixes for your code.

The MSVC C++ compiler provides several ways to create sections and place data in them. The linker will convert them into the ‘sections’ in the PE (Portable Executable) format.

The compiler also creates some sections itself. One example is the section .CRT$XCU, which is used to implement C++ dynamic initialization (there are other sections for this purpose, too). The name of the section is mentioned in articles like CRT Initialization.

Some code bases try to emulate what the compiler does, but the syntax they use doesn’t do the same thing as what the compiler does internally. This often leads to undefined behavior. In Visual Studio 2019 version 16.11, we introduced two off-by-default warnings C5247 and C5248 to notify the user when this happens.

For example, we find that the following pattern is used in C++ source in some code bases:

void f();
typedef void (*type)();

#pragma section(".CRT$XCU", read)
__declspec(allocate(".CRT$XCU")) type i = f;

There are two major differences.

  1. The section created by #pragma section is a regular section and it has different properties from the one created by the compiler for dynamic initialization.
  2. The variable i placed in the section is a regular variable and isn’t considered as an initializer by the compiler.

These differences can lead to many subtle semantics differences. For example,

  1. i may be optimized out.
  2. The order when f is called relative to other C++ dynamic initializers is unspecified.
  3. The user defined section .CRT$XCU will interfere with the C++ dynamic initialization. This can lead to undefined behavior which includes missing initialization.

If the order is not important, it is recommended to use the following instead:

void f();
struct init_helper {
    init_helper() { f(); }
};
init_helper i;

If you need a specific order (like before or after any compiler generated C++ dynamic initializer), there is no standard conformant way to do that. However, you can take advantage of a feature in the linker which sorts sections by the name and avoid the name of any section created by the compiler or used by the CRT library (note that the variable may still be optimized out and there is no guarantee that the linker behavior will remain the same in the future):

#pragma section(".CRT$XCT", read)
// 'f1' is guaranteed to be called before any compiler generated C++ dynamic initializer
void f1();
__declspec(allocate(".CRT$XCT")) type i1 = f1;

#pragma section(".CRT$XCV", read)
// 'f2' is guaranteed to be called after any compiler generated C++ dynamic initializer
void f2();
__declspec(allocate(".CRT$XCV")) type i2 = f2;

Note, while the names .CRT$XCT and .CRT$XCV are not used by either the compiler or the CRT library right now, there is no guarantee that they will remain unused in the future.

If you have any semantics requirements which can’t be implemented using the above, feel free to let us know in the comment section.