summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Gamari <ben@smart-cactus.org>2021-10-08 09:13:29 -0400
committerMarge Bot <ben+marge-bot@smart-cactus.org>2022-07-16 23:50:36 -0400
commit616365b01ea6552c39e211a42df4cb33762f481e (patch)
treeab04f2e26bde63305ee9fec55e69e3f7a8d58e13
parente38a268491347c3aceba359d47034e272afcdf6b (diff)
downloadhaskell-616365b01ea6552c39e211a42df4cb33762f481e.tar.gz
rts/linker/Elf: Introduce support for invoking finalizers on unload
Addresses #20494.
-rw-r--r--rts/Linker.c10
-rw-r--r--rts/LinkerInternals.h5
-rw-r--r--rts/linker/Elf.c77
-rw-r--r--rts/linker/Elf.h1
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 );