August 24th, 2021

New Compiler Warnings for Dynamic Initialization and User Defined Sections

Xiang Fan
PRINCIPAL SOFTWARE ENGINEER

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.

Author

Xiang Fan
PRINCIPAL SOFTWARE ENGINEER

Developer in Visual C++ team

6 comments

Discussion is closed. Login to edit/delete existing comments.

  • Adriano dos Santos Fernandes

    Can you please compare the .CRT$XCT/.CRT$XCV trick with init_seg pragma?

    • Xiang FanMicrosoft employee Author

      The init_seg pragma is a more advanced option with some restrictions.

      One major difference is that the init_seg pragma impacts in where the compiler puts the compiler generated C++ dynamic initializers after the pragma is seen and only one init_seg pragma is allowed in each translation unit. This means to avoid accidentally changing other C++ dynamic initializations, it is suggested to put the pragma in a separate translation unit.
      Something like (this is for illustration purpose...

      Read more
      • Adriano dos Santos Fernandes

        But I mean, is init_seg with compiler/lib/user (instead of section name) a high level and preferred way?

        For my case, a library which changes the global new/delete operator with a custom memory manager, so it needs to initialize variables before the high-level user code.

        So is it better to just use init_seg(lib) there and use custom section names only for more advanced and customized things?

      • Xiang FanMicrosoft employee Author

        Yes, using ‘#pramga init_seg(lib)’ is the better option for your case.

  • Tomislav Pericin

    It is a little unclear to me if the .CRT$X* section is going to appear in the resulting PE image or not. Will the linker try to interpret the data for dynamic initialization and omit the section for the final image? Thanks.

    • Xiang FanMicrosoft employee Author

      The linker behavior is mentioned in CRT Initialization: “Now, when the linker reads various .CRT subsections (the part after the $), it combines them in one section and orders them alphabetically.”
      Linker doesn’t interpret the data in these ‘.CRT$*’ sections from the object files, though.