aboutsummaryrefslogtreecommitdiff
path: root/include/linux/init.h
diff options
context:
space:
mode:
Diffstat (limited to 'include/linux/init.h')
-rw-r--r--include/linux/init.h66
1 files changed, 36 insertions, 30 deletions
diff --git a/include/linux/init.h b/include/linux/init.h
index 3561ea30ed8c..0e274bb51709 100644
--- a/include/linux/init.h
+++ b/include/linux/init.h
@@ -163,24 +163,8 @@ extern bool initcall_debug;
#ifndef __ASSEMBLY__
-#ifdef CONFIG_LTO
-/* Work around a LTO gcc problem: when there is no reference to a variable
- * in a module it will be moved to the end of the program. This causes
- * reordering of initcalls which the kernel does not like.
- * Add a dummy reference function to avoid this. The function is
- * deleted by the linker.
- */
-#define LTO_REFERENCE_INITCALL(x) \
- ; /* yes this is needed */ \
- static __used __exit void *reference_##x(void) \
- { \
- return &x; \
- }
-#else
-#define LTO_REFERENCE_INITCALL(x)
-#endif
-
-/* initcalls are now grouped by functionality into separate
+/*
+ * initcalls are now grouped by functionality into separate
* subsections. Ordering inside the subsections is determined
* by link order.
* For backwards compatibility, initcall() puts the call in
@@ -188,12 +172,39 @@ extern bool initcall_debug;
*
* The `id' arg to __define_initcall() is needed so that multiple initcalls
* can point at the same handler without causing duplicate-symbol build errors.
+ *
+ * Initcalls are run by placing pointers in initcall sections that the
+ * kernel iterates at runtime. The linker can do dead code / data elimination
+ * and remove that completely, so the initcall sections have to be marked
+ * as KEEP() in the linker script.
*/
-
-#define __define_initcall(fn, id) \
+#ifdef CONFIG_LTO_CLANG
+ /*
+ * With LTO, the compiler doesn't necessarily obey link order for
+ * initcalls, and the initcall variable needs to be globally unique
+ * to avoid naming collisions. In order to preserve the correct
+ * order, we add each variable into its own section and generate a
+ * linker script (in scripts/link-vmlinux.sh) to ensure the order
+ * remains correct. We also add a __COUNTER__ prefix to the name,
+ * so we can retain the order of initcalls within each compilation
+ * unit, and __LINE__ to make the names more unique.
+ */
+ #define ___lto_initcall(c, l, fn, id, __sec) \
+ static initcall_t __initcall_##c##_##l##_##fn##id __used \
+ __attribute__((__section__( #__sec \
+ __stringify(.init..##c##_##l##_##fn)))) = fn;
+ #define __lto_initcall(c, l, fn, id, __sec) \
+ ___lto_initcall(c, l, fn, id, __sec)
+
+ #define ___define_initcall(fn, id, __sec) \
+ __lto_initcall(__COUNTER__, __LINE__, fn, id, __sec)
+#else
+ #define ___define_initcall(fn, id, __sec) \
static initcall_t __initcall_##fn##id __used \
- __attribute__((__section__(".initcall" #id ".init"))) = fn; \
- LTO_REFERENCE_INITCALL(__initcall_##fn##id)
+ __attribute__((__section__(#__sec ".init"))) = fn;
+#endif
+
+#define __define_initcall(fn, id) ___define_initcall(fn, id, .initcall##id)
/*
* Early initcalls run before initializing SMP.
@@ -229,16 +240,11 @@ extern bool initcall_debug;
#define __initcall(fn) device_initcall(fn)
-#define __exitcall(fn) \
+#define __exitcall(fn) \
static exitcall_t __exitcall_##fn __exit_call = fn
-#define console_initcall(fn) \
- static initcall_t __initcall_##fn \
- __used __section(.con_initcall.init) = fn
-
-#define security_initcall(fn) \
- static initcall_t __initcall_##fn \
- __used __section(.security_initcall.init) = fn
+#define console_initcall(fn) ___define_initcall(fn, con, .con_initcall)
+#define security_initcall(fn) ___define_initcall(fn, security, .security_initcall)
struct obs_kernel_param {
const char *str;