summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Gamari <ben@well-typed.com>2022-07-12 00:07:04 -0400
committerBen Gamari <ben@smart-cactus.org>2022-07-14 19:49:10 -0400
commit43b7997b91db71ecfd62203047e539f189969a64 (patch)
treea6897beab9099d070b02e98f886865a64b7cd84e
parent2b7d4b00f1d00ed40df9a58044a13c541e8c7f3a (diff)
downloadhaskell-43b7997b91db71ecfd62203047e539f189969a64.tar.gz
rts/linker/PEi386: Ensure that all .ctors/.dtors sections are run
It turns out that PE objects may have multiple `.ctors`/`.dtors` sections but the RTS linker had assumed that there was only one. Fix this. Fixes #21618. (cherry picked from commit 8c8dfa3e9558c2a198195c8b9c2bf638d2ca6d05)
-rw-r--r--rts/linker/PEi386.c72
-rw-r--r--rts/linker/PEi386Types.h12
2 files changed, 63 insertions, 21 deletions
diff --git a/rts/linker/PEi386.c b/rts/linker/PEi386.c
index 5b93b57ece..38d10cf0eb 100644
--- a/rts/linker/PEi386.c
+++ b/rts/linker/PEi386.c
@@ -386,6 +386,15 @@ 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;
+static void freeSectionList(struct SectionList *slist)
+{
+ while (slist != NULL) {
+ struct SectionList *next = slist->next;
+ stgFree(slist);
+ slist = next;
+ }
+}
+
void initLinker_PEi386()
{
if (!ghciInsertSymbolTable(WSTR("(GHCi/Ld special symbols)"),
@@ -514,6 +523,8 @@ static void releaseOcInfo(ObjectCode* oc) {
if (!oc) return;
if (oc->info) {
+ freeSectionList(oc->info->init);
+ freeSectionList(oc->info->fini);
stgFree (oc->info->ch_info);
stgFree (oc->info->symbols);
stgFree (oc->info->str_tab);
@@ -1470,13 +1481,21 @@ ocGetNames_PEi386 ( ObjectCode* oc )
}
if (0==strncmp(".ctors", section->info->name, 6)) {
+ /* N.B. a compilation unit may have more than one .ctor section; we
+ * must run them all. See #21618 for a case where this happened */
+ struct SectionList *slist = stgMallocBytes(sizeof(struct SectionList), "ocGetNames_PEi386");
+ slist->section = &oc->sections[i];
+ slist->next = oc->info->init;
+ oc->info->init = slist;
kind = SECTIONKIND_INIT_ARRAY;
- oc->info->init = &oc->sections[i];
}
if (0==strncmp(".dtors", section->info->name, 6)) {
+ struct SectionList *slist = stgMallocBytes(sizeof(struct SectionList), "ocGetNames_PEi386");
+ slist->section = &oc->sections[i];
+ slist->next = oc->info->fini;
+ oc->info->fini = slist;
kind = SECTIONKIND_FINI_ARRAY;
- oc->info->fini = &oc->sections[i];
}
if ( 0 == strncmp(".stab" , section->info->name, 5 )
@@ -2146,16 +2165,23 @@ ocRunInit_PEi386 ( ObjectCode *oc )
getProgArgv(&argc, &argv);
getProgEnvv(&envc, &envv);
- Section section = *oc->info->init;
- CHECK(SECTIONKIND_INIT_ARRAY == section.kind);
-
- uint8_t *init_startC = section.start;
- init_t *init_start = (init_t*)init_startC;
- init_t *init_end = (init_t*)(init_startC + section.size);
+ for (struct SectionList *slist = oc->info->init;
+ slist != NULL;
+ slist = slist->next) {
+ Section *section = slist->section;
+ CHECK(SECTIONKIND_INIT_ARRAY == section->kind);
+ uint8_t *init_startC = section->start;
+ init_t *init_start = (init_t*)init_startC;
+ init_t *init_end = (init_t*)(init_startC + section->size);
+
+ // ctors are run *backwards*!
+ for (init_t *init = init_end - 1; init >= init_start; init--) {
+ (*init)(argc, argv, envv);
+ }
+ }
- // ctors are run in reverse order.
- for (init_t *init = init_end - 1; init >= init_start; init--)
- (*init)(argc, argv, envv);
+ freeSectionList(oc->info->init);
+ oc->info->init = NULL;
freeProgEnvv(envc, envv);
return true;
@@ -2170,18 +2196,26 @@ bool ocRunFini_PEi386( ObjectCode *oc )
return true;
}
- Section section = *oc->info->fini;
- CHECK(SECTIONKIND_FINIT_ARRAY == section.kind);
+ for (struct SectionList *slist = oc->info->fini;
+ slist != NULL;
+ slist = slist->next) {
+ Section section = *slist->section;
+ CHECK(SECTIONKIND_FINI_ARRAY == section.kind);
- uint8_t *fini_startC = section.start;
- fini_t *fini_start = (fini_t*)fini_startC;
- fini_t *fini_end = (fini_t*)(fini_startC + section.size);
+ uint8_t *fini_startC = section.start;
+ fini_t *fini_start = (fini_t*)fini_startC;
+ fini_t *fini_end = (fini_t*)(fini_startC + section.size);
- // dtors are run in forward order.
- for (fini_t *fini = fini_end - 1; fini >= fini_start; fini--)
+ // dtors are run in forward order.
+ for (fini_t *fini = fini_end - 1; fini >= fini_start; fini--) {
(*fini)();
+ }
+ }
- return true;
+ freeSectionList(oc->info->fini);
+ oc->info->fini = NULL;
+
+ return true;
}
SymbolAddr *lookupSymbol_PEi386(SymbolName *lbl, ObjectCode *dependent, SymType *type)
diff --git a/rts/linker/PEi386Types.h b/rts/linker/PEi386Types.h
index 747a083779..496db62357 100644
--- a/rts/linker/PEi386Types.h
+++ b/rts/linker/PEi386Types.h
@@ -16,9 +16,17 @@ struct SectionFormatInfo {
uint64_t virtualSize;
uint64_t virtualAddr;
};
+
+// A linked-list of Sections; used to represent the set of initializer/finalizer
+// list sections.
+struct SectionList {
+ Section *section;
+ struct SectionList *next;
+};
+
struct ObjectCodeFormatInfo {
- Section* init;
- Section* fini;
+ struct SectionList* init; // Freed by ocRunInit_PEi386
+ struct SectionList* fini; // Freed by ocRunFini_PEi386
Section* pdata;
Section* xdata;
COFF_HEADER_INFO* ch_info; // Freed by ocResolve_PEi386