diff options
author | Ben Gamari <ben@well-typed.com> | 2022-07-12 00:07:04 -0400 |
---|---|---|
committer | Ben Gamari <ben@smart-cactus.org> | 2022-07-14 19:49:10 -0400 |
commit | 43b7997b91db71ecfd62203047e539f189969a64 (patch) | |
tree | a6897beab9099d070b02e98f886865a64b7cd84e | |
parent | 2b7d4b00f1d00ed40df9a58044a13c541e8c7f3a (diff) | |
download | haskell-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.c | 72 | ||||
-rw-r--r-- | rts/linker/PEi386Types.h | 12 |
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 |