(patch by Benoit Rousseau)
This patch fixes a bug where the global variable initializers were sometimes not invoked in the correct order when it involved a C++ template instantiation.
The compiler generates an array of function pointers to the global variable initializers in the .CRT$XCU and .CRT$XIU sub-sections, and then the CRT calls those functions one by one to initialize the global variables. The sections that contain the initializer for a C++ template instantiation have the COMDAT bit set because the linker needs to do some additional work to delete the duplicate definitions.
The initial bug was that the linker puts the COMDAT sections at the end of the non-COMDAT sections, even though they are intended to be called in the same order that they appear in their OBJ.
We had the bug in the Microsoft Cloud SDK. Looking at the disassembly, the code of the library looks a bit like this.
#include <vector> class State { public: static std::vector<State*> states; State(const char* name) { s_states.push_back(this); // ... } // // ... // }; std::vector<State*> State::states(); State idle("Idle"); State connection_pending("Connection pending"); State connected("Connected"); // ...
Because of the bug, State::states was initialized after the globals idle, connected pushed themselves onto it, which caused State::states to drop all those states. And later on the code accesses them with a std::vector::operator[] and it crashes.
The fix simply sorts the .CRT by section number, after the COMDAT sections have been resolved. I expect the other sections to be independent of the order they appear in the executable (although there could be caching effects in the resulting executable if code that is related is separated by a wide gap in the executable’s code pages)