diff options
author | Ben Gamari <ben@smart-cactus.org> | 2021-10-08 09:13:29 -0400 |
---|---|---|
committer | Marge Bot <ben+marge-bot@smart-cactus.org> | 2022-07-16 23:50:36 -0400 |
commit | 616365b01ea6552c39e211a42df4cb33762f481e (patch) | |
tree | ab04f2e26bde63305ee9fec55e69e3f7a8d58e13 | |
parent | e38a268491347c3aceba359d47034e272afcdf6b (diff) | |
download | haskell-616365b01ea6552c39e211a42df4cb33762f481e.tar.gz |
rts/linker/Elf: Introduce support for invoking finalizers on unload
Addresses #20494.
-rw-r--r-- | rts/Linker.c | 10 | ||||
-rw-r--r-- | rts/LinkerInternals.h | 5 | ||||
-rw-r--r-- | rts/linker/Elf.c | 77 | ||||
-rw-r--r-- | rts/linker/Elf.h | 1 |
4 files changed, 90 insertions, 3 deletions
diff --git a/rts/Linker.c b/rts/Linker.c index 8c7cc2848a..cd469b4f2d 100644 --- a/rts/Linker.c +++ b/rts/Linker.c @@ -1136,6 +1136,16 @@ void freeObjectCode (ObjectCode *oc) { IF_DEBUG(linker, ocDebugBelch(oc, "start\n")); + // Run finalizers + if (oc->type == STATIC_OBJECT && + (oc->status == OBJECT_READY || oc->status == OBJECT_UNLOADED)) { + // Only run finalizers if the initializers have also been run, which + // happens when we resolve the object. +#if defined(OBJFORMAT_ELF) + ocRunFini_ELF(oc); +#endif + } + if (oc->type == DYNAMIC_OBJECT) { #if defined(OBJFORMAT_ELF) ACQUIRE_LOCK(&dl_mutex); diff --git a/rts/LinkerInternals.h b/rts/LinkerInternals.h index 5711b16526..503d281a71 100644 --- a/rts/LinkerInternals.h +++ b/rts/LinkerInternals.h @@ -373,9 +373,12 @@ extern Mutex dl_mutex; #endif #endif /* THREADED_RTS */ -/* Type of the initializer */ +/* Type of an initializer */ typedef void (*init_t) (int argc, char **argv, char **env); +/* Type of a finalizer */ +typedef void (*fini_t) (void); + typedef enum _SymStrength { STRENGTH_NORMAL, STRENGTH_WEAK, diff --git a/rts/linker/Elf.c b/rts/linker/Elf.c index 4650aea8b1..dd4b8fcd0d 100644 --- a/rts/linker/Elf.c +++ b/rts/linker/Elf.c @@ -628,6 +628,13 @@ static SectionKind getSectionKind_ELF( Elf_Shdr *hdr, int *is_bss ) return SECTIONKIND_INIT_ARRAY; } #endif /* not SHT_INIT_ARRAY */ +#if defined(SHT_FINI_ARRAY) + if (hdr->sh_type == SHT_FINI_ARRAY + && (hdr->sh_flags & SHF_ALLOC) && (hdr->sh_flags & SHF_WRITE)) { + /* .fini_array section */ + return SECTIONKIND_FINI_ARRAY; + } +#endif /* not SHT_INIT_ARRAY */ if (hdr->sh_type == SHT_NOBITS && (hdr->sh_flags & SHF_ALLOC) && (hdr->sh_flags & SHF_WRITE)) { /* .bss-style section */ @@ -1931,6 +1938,28 @@ ocResolve_ELF ( ObjectCode* oc ) return ocMprotect_Elf(oc); } +/* + * Note [Initializers and finalizers (ELF)] + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * The System V ABI describes a facility for allowing object code to mark + * functions to be run at load time. These functions are known as + * "initializers" (or "constructors"). Initializers are recorded in a section + * marked with the DT_INIT tag (often with the name `.init`). + * + * There is also a similar mechanism for code to be run at unload time (e.g. + * during program termination). These are known as finalizers and are collected + * in `.fini` section. + * + * For more about how the code generator emits initializers and finalizers see + * Note [Initializers and finalizers in Cmm] in GHC.Cmm.InitFini. + * + * See also: the "Initialization and Termination Functions" section of the + * System V ABI. + */ + +// Run the constructors/initializers of an ObjectCode. +// Returns 1 on success. +// See Note [Initializers and finalizers (ELF)]. int ocRunInit_ELF( ObjectCode *oc ) { Elf_Word i; @@ -1959,7 +1988,7 @@ int ocRunInit_ELF( ObjectCode *oc ) } if (kind == SECTIONKIND_INIT_ARRAY) { - char *init_startC = oc->sections[i].start; + char *init_startC = oc->sections[i].start; init_start = (init_t*)init_startC; init_end = (init_t*)(init_startC + shdr[i].sh_size); for (init = init_start; init < init_end; init++) { @@ -1972,7 +2001,7 @@ int ocRunInit_ELF( ObjectCode *oc ) // SECTIONKIND_RWDATA; but allowing RODATA seems harmless enough. if ((kind == SECTIONKIND_RWDATA || kind == SECTIONKIND_CODE_OR_RODATA) && 0 == memcmp(".ctors", sh_strtab + shdr[i].sh_name, 6)) { - char *init_startC = oc->sections[i].start; + char *init_startC = oc->sections[i].start; init_start = (init_t*)init_startC; init_end = (init_t*)(init_startC + shdr[i].sh_size); // ctors run in reverse @@ -1987,6 +2016,50 @@ int ocRunInit_ELF( ObjectCode *oc ) return 1; } +// Run the finalizers of an ObjectCode. +// Returns 1 on success. +// See Note [Initializers and finalizers (ELF)]. +int ocRunFini_ELF( ObjectCode *oc ) +{ + char* ehdrC = (char*)(oc->image); + Elf_Ehdr* ehdr = (Elf_Ehdr*) ehdrC; + Elf_Shdr* shdr = (Elf_Shdr*) (ehdrC + ehdr->e_shoff); + char* sh_strtab = ehdrC + shdr[elf_shstrndx(ehdr)].sh_offset; + + for (Elf_Word i = 0; i < elf_shnum(ehdr); i++) { + int is_bss = false; + SectionKind kind = getSectionKind_ELF(&shdr[i], &is_bss); + + if (kind == SECTIONKIND_CODE_OR_RODATA && 0 == memcmp(".fini", sh_strtab + shdr[i].sh_name, 5)) { + fini_t fini_f = (fini_t)(oc->sections[i].start); + fini_f(); + } + + if (kind == SECTIONKIND_FINI_ARRAY) { + fini_t *fini_start, *fini_end, *fini; + char *fini_startC = oc->sections[i].start; + fini_start = (fini_t*)fini_startC; + fini_end = (fini_t*)(fini_startC + shdr[i].sh_size); + for (fini = fini_start; fini < fini_end; fini++) { + CHECK(0x0 != *fini); + (*fini)(); + } + } + + if (kind == SECTIONKIND_CODE_OR_RODATA && 0 == memcmp(".dtors", sh_strtab + shdr[i].sh_name, 6)) { + char *fini_startC = oc->sections[i].start; + fini_t *fini_start = (fini_t*)fini_startC; + fini_t *fini_end = (fini_t*)(fini_startC + shdr[i].sh_size); + for (fini_t *fini = fini_start; fini < fini_end; fini++) { + CHECK(0x0 != *fini); + (*fini)(); + } + } + } + + return 1; +} + /* * Shared object loading */ diff --git a/rts/linker/Elf.h b/rts/linker/Elf.h index 12bf1772f7..2b9ad87aee 100644 --- a/rts/linker/Elf.h +++ b/rts/linker/Elf.h @@ -12,6 +12,7 @@ int ocVerifyImage_ELF ( ObjectCode* oc ); int ocGetNames_ELF ( ObjectCode* oc ); int ocResolve_ELF ( ObjectCode* oc ); int ocRunInit_ELF ( ObjectCode* oc ); +int ocRunFini_ELF ( ObjectCode* oc ); int ocAllocateExtras_ELF ( ObjectCode *oc ); void freeNativeCode_ELF ( ObjectCode *nc ); void *loadNativeObj_ELF ( pathchar *path, char **errmsg ); |