You can declare a section and then start generating data into it.
#pragma section("mydata$a", read, write) __declspec(allocate("mydata$a")) int i = 0; #pragma section("mydata$b", read, write) __declspec(allocate("mydata$b")) int j = 0;
The #pragma section
directive lets you define a new section and assign attributes. You can then place data into that section with the __declspec(allocate(...))
attribute.
When the linker combines all the little bits and pieces of data, it does the following:
- It takes the section names and splits them at the first dollar sign. (If there is no dollar sign in the section name, then the entire string is treated as the “before the first dollar sign” portion.)
- The portion before the dollar sign is the name of the section in the generated module.
- The portion after the dollar sign, if any, is used to sort the fragments within a section.
It is common to take advantage of the “sorts the data fragments alphabetically” step by generating data into a carefully-named sequence of sections so that they can iterate over all the objects in the middle section:
typedef void (*INITIALIZER)(); #pragma section("mydata$a", read) __declspec(allocate("mydata$a")) const INITIALIZER firstInitializer = nullptr; #define ADD_INITIALIZER_TO_SECTION(fn, s) \ __declspec(allocate("mydata$" s)) \ const INITIALIZER initializer##fn = fn #pragma section("mydata$g", read) #pragma section("mydata$m", read) #pragma section("mydata$t", read) #define ADD_EARLY_INITIALIZER(fn) ADD_INITIALIZER_TO_SECTION(fn, "g") #define ADD_INITIALIZER(fn) ADD_INITIALIZER_TO_SECTION(fn, "m") #define ADD_LATE_INITIALIZER(fn) ADD_INITIALIZER_TO_SECTION(fn, "t") #pragma section("mydata$z", read) __declspec(allocate("mydata$z")) INITIALIZER lastInitializer = nullptr; // In various files // file1.cpp ADD_INITIALIZER(Function1); // file2.cpp ADD_INITIALIZER(Function2); ADD_LATE_INITIALIZER(DoThisLater2); // file3.cpp ADD_INITIALIZER(Function3); ADD_EARLY_INITIALIZER(DoThisSooner3); // file4.cpp ADD_EARLY_INITIALIZER(DoThisSooner4); ADD_LATE_INITIALIZER(DoThisLater4);
The idea is that anybody who needs to add an initializer declares a function pointer in the mydata$g
, mydata$m
, or mydata$t
section. The linker will collect all of those function pointers from same-named sections together, and then sort the sections, so that the final order of fragments in the mydata
section is
mydata$a |
firstInitializer |
main.obj |
 |
mydata$g |
DoThisSooner3 |
file3.obj |
unspecified order |
DoThisSooner4 |
file4.obj |
 | |
mydata$m |
Function2 |
file2.obj |
unspecified order |
Function1 |
file1.obj |
||
Function3 |
file3.obj |
||
mydata$t |
DoThisLater2 |
file2.obj |
unspecified order |
DoThisLater4 |
file4.obj |
||
mydata$z |
lastInitializer |
main.obj |
The InitializeÂAllÂTheÂThings
function then walks through all the function pointers between firstÂInitializer
and lastÂInitializer
and calls each one.
The alphabetical ordering rule ensures that the mydata$a
fragment comes first, so that firstÂInitializer
has the lowest address. Next comes the mydata$g
fragments, which contain the early initializers. Following that are the mydata$m
fragments, which are the regular initializers. Next are the mydata$t
fragments, which contain the late initializers. And finally the mydata$z
fragment, which contains lastÂInitializer
.
Now that we understand the principle behind section grouping and sorting, we can look at the gotchas next time.
0 comments