@@ -517,6 +517,12 @@ struct module {
unsigned int num_ctors;
#endif
+#ifdef CONFIG_MODULE_DESTRUCTORS
+ /* Destructor functions. */
+ ctor_fn_t *dtors;
+ unsigned int num_dtors;
+#endif
+
#ifdef CONFIG_FUNCTION_ERROR_INJECTION
struct error_injection_entry *ei_funcs;
unsigned int num_ei_funcs;
@@ -853,4 +859,12 @@ int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *,
struct module *, unsigned long),
void *data);
+#ifdef CONFIG_MODULE_DESTRUCTORS
+void run_all_module_destructors(void);
+#else
+static inline void run_all_module_destructors(void)
+{
+}
+#endif
+
#endif /* _LINUX_MODULE_H */
@@ -2295,6 +2295,23 @@ config UNUSED_KSYMS_WHITELIST
endif # MODULES
+config WANT_MODULE_DESTRUCTORS
+ bool
+ help
+ Architectures may select this if they need atexit functions (such as
+ generated by the compiler for -ftest-coverage/gcov) to run in modules.
+ They're then responsible for calling run_all_module_destructors() at
+ shutdown so that module destructors are called for all still loaded
+ modules as well.
+
+ Note that CONFIG_GCOV_KERNEL does *not* require this since it keeps
+ all the coverage data in the kernel, notably CONFIG_GCOV in ARCH=um
+ requires this.
+
+config MODULE_DESTRUCTORS
+ def_bool y
+ depends on WANT_MODULE_DESTRUCTORS && MODULES
+
config MODULES_TREE_LOOKUP
def_bool y
depends on PERF_EVENTS || TRACING
@@ -904,6 +904,27 @@ EXPORT_SYMBOL(module_refcount);
/* This exists whether we can unload or not */
static void free_module(struct module *mod);
+#ifdef CONFIG_MODULE_DESTRUCTORS
+static void do_mod_dtors(struct module *mod)
+{
+ unsigned long i;
+
+ for (i = 0; i < mod->num_dtors; i++)
+ mod->dtors[i]();
+}
+
+void run_all_module_destructors(void)
+{
+ struct module *mod;
+
+ /* we no longer need to care about locking at this point */
+ list_for_each_entry(mod, &modules, list)
+ do_mod_dtors(mod);
+}
+#else
+static inline void do_mod_dtors(struct module *mod) {}
+#endif
+
SYSCALL_DEFINE2(delete_module, const char __user *, name_user,
unsigned int, flags)
{
@@ -966,6 +987,7 @@ SYSCALL_DEFINE2(delete_module, const char __user *, name_user,
MODULE_STATE_GOING, mod);
klp_module_going(mod);
ftrace_release_mod(mod);
+ do_mod_dtors(mod);
async_synchronize_full();
@@ -3263,6 +3285,23 @@ static int find_module_sections(struct module *mod, struct load_info *info)
}
#endif
+#ifdef CONFIG_MODULE_DESTRUCTORS
+ mod->dtors = section_objs(info, ".dtors",
+ sizeof(*mod->dtors), &mod->num_dtors);
+ if (!mod->dtors)
+ mod->dtors = section_objs(info, ".fini_array",
+ sizeof(*mod->dtors), &mod->num_dtors);
+ else if (find_sec(info, ".fini_array")) {
+ /*
+ * This shouldn't happen with same compiler and binutils
+ * building all parts of the module.
+ */
+ pr_warn("%s: has both .dtors and .fini_array.\n",
+ mod->name);
+ return -EINVAL;
+ }
+#endif
+
mod->noinstr_text_start = section_objs(info, ".noinstr.text", 1,
&mod->noinstr_text_size);