diff options
Diffstat (limited to 'include/linux/init.h')
| -rw-r--r-- | include/linux/init.h | 66 |
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; |
