summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Gamari <ben@smart-cactus.org>2021-10-08 09:13:29 -0400
committerBen Gamari <ben@smart-cactus.org>2022-07-12 11:21:07 -0400
commitf27af493adf6a21a5e5c6aa424712fc130156225 (patch)
tree8bb0a2e884e0ac545a130b166056196fc0ed35b4
parent460505345e500eb902da9737c75c077d5fc5ef66 (diff)
downloadhaskell-f27af493adf6a21a5e5c6aa424712fc130156225.tar.gz
rts/linker/Elf: Introduce support for invoking finalizers on unload
Addresses #20474.
-rw-r--r--rts/Linker.c5
-rw-r--r--rts/LinkerInternals.h5
-rw-r--r--rts/linker/Elf.c52
-rw-r--r--rts/linker/Elf.h1
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 );