diff options
author | Ben Gamari <ben@smart-cactus.org> | 2021-10-08 09:13:29 -0400 |
---|---|---|
committer | Ben Gamari <ben@smart-cactus.org> | 2022-07-12 11:21:07 -0400 |
commit | f27af493adf6a21a5e5c6aa424712fc130156225 (patch) | |
tree | 8bb0a2e884e0ac545a130b166056196fc0ed35b4 | |
parent | 460505345e500eb902da9737c75c077d5fc5ef66 (diff) | |
download | haskell-f27af493adf6a21a5e5c6aa424712fc130156225.tar.gz |
rts/linker/Elf: Introduce support for invoking finalizers on unload
Addresses #20474.
-rw-r--r-- | rts/Linker.c | 5 | ||||
-rw-r--r-- | rts/LinkerInternals.h | 5 | ||||
-rw-r--r-- | rts/linker/Elf.c | 52 | ||||
-rw-r--r-- | rts/linker/Elf.h | 1 |
4 files changed, 60 insertions, 3 deletions
diff --git a/rts/Linker.c b/rts/Linker.c index 8c7cc2848a..a7bb2210ae 100644 --- a/rts/Linker.c +++ b/rts/Linker.c @@ -1136,6 +1136,11 @@ void freeObjectCode (ObjectCode *oc) { IF_DEBUG(linker, ocDebugBelch(oc, "start\n")); + // Run finalizers +#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 ae4721e6f3..b839bf0f82 100644 --- a/rts/linker/Elf.c +++ b/rts/linker/Elf.c @@ -1931,6 +1931,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 +1981,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 +1994,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 @@ -1986,6 +2008,32 @@ 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)) { + 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++) { + (*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 ); |