summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Gamari <ben@smart-cactus.org>2022-07-12 12:12:49 -0400
committerBen Gamari <ben@smart-cactus.org>2022-07-14 19:49:10 -0400
commit669d60cffb0bf206ab7fd8c493b2973ab4d29ea2 (patch)
tree2e0d115b51216f13659bfcd12f79d3cfa2b1207e
parent43b7997b91db71ecfd62203047e539f189969a64 (diff)
downloadhaskell-669d60cffb0bf206ab7fd8c493b2973ab4d29ea2.tar.gz
rts/linker/PEi386: Respect dtor/ctor priority
Previously we would run constructors and destructors in arbitrary order despite explicit priorities. Fixes #21847. (cherry picked from commit 6f2d9164d366a76f5ae2cf9a110836b5b629bee5)
-rw-r--r--rts/linker/PEi386.c46
-rw-r--r--rts/linker/PEi386Types.h1
2 files changed, 45 insertions, 2 deletions
diff --git a/rts/linker/PEi386.c b/rts/linker/PEi386.c
index 38d10cf0eb..697768aa82 100644
--- a/rts/linker/PEi386.c
+++ b/rts/linker/PEi386.c
@@ -386,6 +386,36 @@ const int default_alignment = 8;
the pointer as a redirect. Essentially it's a DATA DLL reference. */
const void* __rts_iob_func = (void*)&__acrt_iob_func;
+enum SortOrder { INCREASING, DECREASING };
+
+// Sort a SectionList by priority.
+static void sortSectionList(struct SectionList **slist, enum SortOrder order)
+{
+ // Bubble sort
+ bool done = false;
+ while (!done) {
+ struct SectionList **last = slist;
+ done = true;
+ while (*last != NULL && (*last)->next != NULL) {
+ struct SectionList *s0 = *last;
+ struct SectionList *s1 = s0->next;
+ bool flip;
+ switch (order) {
+ case INCREASING: flip = s0->priority > s1->priority; break;
+ case DECREASING: flip = s0->priority < s1->priority; break;
+ }
+ if (flip) {
+ s0->next = s1->next;
+ s1->next = s0;
+ *last = s1;
+ done = false;
+ } else {
+ last = &s0->next;
+ }
+ }
+ }
+}
+
static void freeSectionList(struct SectionList *slist)
{
while (slist != NULL) {
@@ -1486,6 +1516,10 @@ ocGetNames_PEi386 ( ObjectCode* oc )
struct SectionList *slist = stgMallocBytes(sizeof(struct SectionList), "ocGetNames_PEi386");
slist->section = &oc->sections[i];
slist->next = oc->info->init;
+ if (sscanf(section->info->name, ".ctors.%d", &slist->priority) != 1) {
+ // Sections without an explicit priority must be run last
+ slist->priority = 0;
+ }
oc->info->init = slist;
kind = SECTIONKIND_INIT_ARRAY;
}
@@ -1494,6 +1528,10 @@ ocGetNames_PEi386 ( ObjectCode* oc )
struct SectionList *slist = stgMallocBytes(sizeof(struct SectionList), "ocGetNames_PEi386");
slist->section = &oc->sections[i];
slist->next = oc->info->fini;
+ if (sscanf(section->info->name, ".dtors.%d", &slist->priority) != 1) {
+ // Sections without an explicit priority must be run last
+ slist->priority = INT_MAX;
+ }
oc->info->fini = slist;
kind = SECTIONKIND_FINI_ARRAY;
}
@@ -1594,6 +1632,10 @@ ocGetNames_PEi386 ( ObjectCode* oc )
addProddableBlock(oc, oc->sections[i].start, sz);
}
+ /* Sort the constructors and finalizers by priority */
+ sortSectionList(&oc->info->init, DECREASING);
+ sortSectionList(&oc->info->fini, INCREASING);
+
/* Copy exported symbols into the ObjectCode. */
oc->n_symbols = info->numberOfSymbols;
@@ -2135,9 +2177,9 @@ ocResolve_PEi386 ( ObjectCode* oc )
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* COFF/PE allows an object to define initializers and finalizers to be run
* at load/unload time, respectively. These are listed in the `.ctors` and
- * `.dtors` sections. Moreover, these section names may be sufficed with an
+ * `.dtors` sections. Moreover, these section names may be suffixed with an
* integer priority (e.g. `.ctors.1234`). Sections are run in order of
- * increasing priority. Sections without a priority (e.g. `.ctors`) are run
+ * high-to-low priority. Sections without a priority (e.g. `.ctors`) are run
* last.
*
* A `.ctors`/`.dtors` section contains an array of pointers to
diff --git a/rts/linker/PEi386Types.h b/rts/linker/PEi386Types.h
index 496db62357..edf3d419a6 100644
--- a/rts/linker/PEi386Types.h
+++ b/rts/linker/PEi386Types.h
@@ -21,6 +21,7 @@ struct SectionFormatInfo {
// list sections.
struct SectionList {
Section *section;
+ int priority;
struct SectionList *next;
};