KBUILD_CFLAGS  += $(call cc-option,-fno-delete-null-pointer-checks,)
 KBUILD_CFLAGS  += $(call cc-disable-warning,maybe-uninitialized,)
 
+ifdef CONFIG_LD_DEAD_CODE_DATA_ELIMINATION
+KBUILD_CFLAGS  += $(call cc-option,-ffunction-sections,)
+KBUILD_CFLAGS  += $(call cc-option,-fdata-sections,)
+endif
+
 ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE
 KBUILD_CFLAGS  += -Os
 else
 KBUILD_LDFLAGS_MODULE += $(LDFLAGS_BUILD_ID)
 LDFLAGS_vmlinux += $(LDFLAGS_BUILD_ID)
 
+ifdef CONFIG_LD_DEAD_CODE_DATA_ELIMINATION
+LDFLAGS_vmlinux        += $(call ld-option, --gc-sections,)
+endif
+
 ifeq ($(CONFIG_STRIP_ASM_SYMS),y)
 LDFLAGS_vmlinux        += $(call ld-option, -X,)
 endif
 
          Select this if the architecture wants to use thin archives
          instead of ld -r to create the built-in.o files.
 
+config LD_DEAD_CODE_DATA_ELIMINATION
+       bool
+       help
+         Select this if the architecture wants to do dead code and
+         data elimination with the linker by compiling with
+         -ffunction-sections -fdata-sections and linking with
+         --gc-sections.
+
+         This requires that the arch annotates or otherwise protects
+         its external entry points from being discarded. Linker scripts
+         must also merge .text.*, .data.*, and .bss.* correctly into
+         output sections.
+
 config HAVE_CONTEXT_TRACKING
        bool
        help
 
        *(.dtb.init.rodata)                                             \
        VMLINUX_SYMBOL(__dtb_end) = .;
 
-/* .data section */
+/*
+ * .data section
+ * -fdata-sections generates .data.identifier which needs to be pulled in
+ * with .data, but don't want to pull in .data..stuff which has its own
+ * requirements. Same for bss.
+ */
 #define DATA_DATA                                                      \
-       *(.data)                                                        \
+       *(.data .data.[0-9a-zA-Z_]*)                                    \
        *(.ref.data)                                                    \
        *(.data..shared_aligned) /* percpu related */                   \
        MEM_KEEP(init.data)                                             \
        /* Kernel symbol table: Normal symbols */                       \
        __ksymtab         : AT(ADDR(__ksymtab) - LOAD_OFFSET) {         \
                VMLINUX_SYMBOL(__start___ksymtab) = .;                  \
-               *(SORT(___ksymtab+*))                                   \
+               KEEP(*(SORT(___ksymtab+*)))                             \
                VMLINUX_SYMBOL(__stop___ksymtab) = .;                   \
        }                                                               \
                                                                        \
        /* Kernel symbol table: GPL-only symbols */                     \
        __ksymtab_gpl     : AT(ADDR(__ksymtab_gpl) - LOAD_OFFSET) {     \
                VMLINUX_SYMBOL(__start___ksymtab_gpl) = .;              \
-               *(SORT(___ksymtab_gpl+*))                               \
+               KEEP(*(SORT(___ksymtab_gpl+*)))                         \
                VMLINUX_SYMBOL(__stop___ksymtab_gpl) = .;               \
        }                                                               \
                                                                        \
        /* Kernel symbol table: Normal unused symbols */                \
        __ksymtab_unused  : AT(ADDR(__ksymtab_unused) - LOAD_OFFSET) {  \
                VMLINUX_SYMBOL(__start___ksymtab_unused) = .;           \
-               *(SORT(___ksymtab_unused+*))                            \
+               KEEP(*(SORT(___ksymtab_unused+*)))                      \
                VMLINUX_SYMBOL(__stop___ksymtab_unused) = .;            \
        }                                                               \
                                                                        \
        /* Kernel symbol table: GPL-only unused symbols */              \
        __ksymtab_unused_gpl : AT(ADDR(__ksymtab_unused_gpl) - LOAD_OFFSET) { \
                VMLINUX_SYMBOL(__start___ksymtab_unused_gpl) = .;       \
-               *(SORT(___ksymtab_unused_gpl+*))                        \
+               KEEP(*(SORT(___ksymtab_unused_gpl+*)))                  \
                VMLINUX_SYMBOL(__stop___ksymtab_unused_gpl) = .;        \
        }                                                               \
                                                                        \
        /* Kernel symbol table: GPL-future-only symbols */              \
        __ksymtab_gpl_future : AT(ADDR(__ksymtab_gpl_future) - LOAD_OFFSET) { \
                VMLINUX_SYMBOL(__start___ksymtab_gpl_future) = .;       \
-               *(SORT(___ksymtab_gpl_future+*))                        \
+               KEEP(*(SORT(___ksymtab_gpl_future+*)))                  \
                VMLINUX_SYMBOL(__stop___ksymtab_gpl_future) = .;        \
        }                                                               \
                                                                        \
        /* Kernel symbol table: Normal symbols */                       \
        __kcrctab         : AT(ADDR(__kcrctab) - LOAD_OFFSET) {         \
                VMLINUX_SYMBOL(__start___kcrctab) = .;                  \
-               *(SORT(___kcrctab+*))                                   \
+               KEEP(*(SORT(___kcrctab+*)))                             \
                VMLINUX_SYMBOL(__stop___kcrctab) = .;                   \
        }                                                               \
                                                                        \
        /* Kernel symbol table: GPL-only symbols */                     \
        __kcrctab_gpl     : AT(ADDR(__kcrctab_gpl) - LOAD_OFFSET) {     \
                VMLINUX_SYMBOL(__start___kcrctab_gpl) = .;              \
-               *(SORT(___kcrctab_gpl+*))                               \
+               KEEP(*(SORT(___kcrctab_gpl+*)))                         \
                VMLINUX_SYMBOL(__stop___kcrctab_gpl) = .;               \
        }                                                               \
                                                                        \
        /* Kernel symbol table: Normal unused symbols */                \
        __kcrctab_unused  : AT(ADDR(__kcrctab_unused) - LOAD_OFFSET) {  \
                VMLINUX_SYMBOL(__start___kcrctab_unused) = .;           \
-               *(SORT(___kcrctab_unused+*))                            \
+               KEEP(*(SORT(___kcrctab_unused+*)))                      \
                VMLINUX_SYMBOL(__stop___kcrctab_unused) = .;            \
        }                                                               \
                                                                        \
        /* Kernel symbol table: GPL-only unused symbols */              \
        __kcrctab_unused_gpl : AT(ADDR(__kcrctab_unused_gpl) - LOAD_OFFSET) { \
                VMLINUX_SYMBOL(__start___kcrctab_unused_gpl) = .;       \
-               *(SORT(___kcrctab_unused_gpl+*))                        \
+               KEEP(*(SORT(___kcrctab_unused_gpl+*)))                  \
                VMLINUX_SYMBOL(__stop___kcrctab_unused_gpl) = .;        \
        }                                                               \
                                                                        \
        /* Kernel symbol table: GPL-future-only symbols */              \
        __kcrctab_gpl_future : AT(ADDR(__kcrctab_gpl_future) - LOAD_OFFSET) { \
                VMLINUX_SYMBOL(__start___kcrctab_gpl_future) = .;       \
-               *(SORT(___kcrctab_gpl_future+*))                        \
+               KEEP(*(SORT(___kcrctab_gpl_future+*)))                  \
                VMLINUX_SYMBOL(__stop___kcrctab_gpl_future) = .;        \
        }                                                               \
                                                                        \
        /* Kernel symbol table: strings */                              \
         __ksymtab_strings : AT(ADDR(__ksymtab_strings) - LOAD_OFFSET) {        \
-               *(__ksymtab_strings)                                    \
+               KEEP(*(__ksymtab_strings))                              \
        }                                                               \
                                                                        \
        /* __*init sections */                                          \
 #define SECURITY_INIT                                                  \
        .security_initcall.init : AT(ADDR(.security_initcall.init) - LOAD_OFFSET) { \
                VMLINUX_SYMBOL(__security_initcall_start) = .;          \
-               *(.security_initcall.init)                              \
+               KEEP(*(.security_initcall.init))                        \
                VMLINUX_SYMBOL(__security_initcall_end) = .;            \
        }
 
  * during second ld run in second ld pass when generating System.map */
 #define TEXT_TEXT                                                      \
                ALIGN_FUNCTION();                                       \
-               *(.text.hot .text .text.fixup .text.unlikely)           \
+               *(.text.hot .text .text.fixup .text.unlikely .text.*)   \
                *(.ref.text)                                            \
        MEM_KEEP(init.text)                                             \
        MEM_KEEP(exit.text)                                             \
 
 /* init and exit section handling */
 #define INIT_DATA                                                      \
+       KEEP(*(SORT(___kentry+*)))                                      \
        *(.init.data)                                                   \
        MEM_DISCARD(init.data)                                          \
        KERNEL_CTORS()                                                  \
                BSS_FIRST_SECTIONS                                      \
                *(.bss..page_aligned)                                   \
                *(.dynbss)                                              \
-               *(.bss)                                                 \
+               *(.bss .bss.[0-9a-zA-Z_]*)                              \
                *(COMMON)                                               \
        }
 
 
 #define INIT_CALLS_LEVEL(level)                                                \
                VMLINUX_SYMBOL(__initcall##level##_start) = .;          \
-               *(.initcall##level##.init)                              \
-               *(.initcall##level##s.init)                             \
+               KEEP(*(.initcall##level##.init))                        \
+               KEEP(*(.initcall##level##s.init))                       \
 
 #define INIT_CALLS                                                     \
                VMLINUX_SYMBOL(__initcall_start) = .;                   \
-               *(.initcallearly.init)                                  \
+               KEEP(*(.initcallearly.init))                            \
                INIT_CALLS_LEVEL(0)                                     \
                INIT_CALLS_LEVEL(1)                                     \
                INIT_CALLS_LEVEL(2)                                     \
 
 #define CON_INITCALL                                                   \
                VMLINUX_SYMBOL(__con_initcall_start) = .;               \
-               *(.con_initcall.init)                                   \
+               KEEP(*(.con_initcall.init))                             \
                VMLINUX_SYMBOL(__con_initcall_end) = .;
 
 #define SECURITY_INITCALL                                              \
                VMLINUX_SYMBOL(__security_initcall_start) = .;          \
-               *(.security_initcall.init)                              \
+               KEEP(*(.security_initcall.init))                        \
                VMLINUX_SYMBOL(__security_initcall_end) = .;
 
 #ifdef CONFIG_BLK_DEV_INITRD
 #define INIT_RAM_FS                                                    \
        . = ALIGN(4);                                                   \
        VMLINUX_SYMBOL(__initramfs_start) = .;                          \
-       *(.init.ramfs)                                                  \
+       KEEP(*(.init.ramfs))                                            \
        . = ALIGN(8);                                                   \
-       *(.init.ramfs.info)
+       KEEP(*(.init.ramfs.info))
 #else
 #define INIT_RAM_FS
 #endif
 
 # define unreachable() do { } while (1)
 #endif
 
+/*
+ * KENTRY - kernel entry point
+ * This can be used to annotate symbols (functions or data) that are used
+ * without their linker symbol being referenced explicitly. For example,
+ * interrupt vector handlers, or functions in the kernel image that are found
+ * programatically.
+ *
+ * Not required for symbols exported with EXPORT_SYMBOL, or initcalls. Those
+ * are handled in their own way (with KEEP() in linker scripts).
+ *
+ * KENTRY can be avoided if the symbols in question are marked as KEEP() in the
+ * linker script. For example an architecture could KEEP() its entire
+ * boot/exception vector code rather than annotate each function and data.
+ */
+#ifndef KENTRY
+# define KENTRY(sym)                                           \
+       extern typeof(sym) sym;                                 \
+       static const unsigned long __kentry_##sym               \
+       __used                                                  \
+       __attribute__((section("___kentry" "+" #sym ), used))   \
+       = (unsigned long)&sym;
+#endif
+
 #ifndef RELOC_HIDE
 # define RELOC_HIDE(ptr, off)                                  \
   ({ unsigned long __ptr;                                      \
 
 #ifndef _LINUX_EXPORT_H
 #define _LINUX_EXPORT_H
+
 /*
  * Export symbols from the kernel to modules.  Forked from module.h
  * to reduce the amount of pointless cruft we feed to gcc when only
 #ifdef CONFIG_MODVERSIONS
 /* Mark the CRC weak since genksyms apparently decides not to
  * generate a checksums for some symbols */
-#define __CRC_SYMBOL(sym, sec)                                 \
-       extern __visible void *__crc_##sym __attribute__((weak));               \
-       static const unsigned long __kcrctab_##sym              \
-       __used                                                  \
-       __attribute__((section("___kcrctab" sec "+" #sym), unused))     \
+#define __CRC_SYMBOL(sym, sec)                                         \
+       extern __visible void *__crc_##sym __attribute__((weak));       \
+       static const unsigned long __kcrctab_##sym                      \
+       __used                                                          \
+       __attribute__((section("___kcrctab" sec "+" #sym), used))       \
        = (unsigned long) &__crc_##sym;
 #else
 #define __CRC_SYMBOL(sym, sec)
 #endif
 
 /* For every exported symbol, place a struct in the __ksymtab section */
-#define ___EXPORT_SYMBOL(sym, sec)                             \
-       extern typeof(sym) sym;                                 \
-       __CRC_SYMBOL(sym, sec)                                  \
-       static const char __kstrtab_##sym[]                     \
-       __attribute__((section("__ksymtab_strings"), aligned(1))) \
-       = VMLINUX_SYMBOL_STR(sym);                              \
-       extern const struct kernel_symbol __ksymtab_##sym;      \
-       __visible const struct kernel_symbol __ksymtab_##sym    \
-       __used                                                  \
-       __attribute__((section("___ksymtab" sec "+" #sym), unused))     \
+#define ___EXPORT_SYMBOL(sym, sec)                                     \
+       extern typeof(sym) sym;                                         \
+       __CRC_SYMBOL(sym, sec)                                          \
+       static const char __kstrtab_##sym[]                             \
+       __attribute__((section("__ksymtab_strings"), aligned(1)))       \
+       = VMLINUX_SYMBOL_STR(sym);                                      \
+       static const struct kernel_symbol __ksymtab_##sym               \
+       __used                                                          \
+       __attribute__((section("___ksymtab" sec "+" #sym), used))       \
        = { (unsigned long)&sym, __kstrtab_##sym }
 
 #if defined(__KSYM_DEPS__)
 
 
 #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 
  *
  * 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) \
        static initcall_t __initcall_##fn##id __used \
-       __attribute__((__section__(".initcall" #id ".init"))) = fn; \
-       LTO_REFERENCE_INITCALL(__initcall_##fn##id)
+       __attribute__((__section__(".initcall" #id ".init"))) = fn;
 
 /*
  * Early initcalls run before initializing SMP.
 
 #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 \
+#define console_initcall(fn)                                   \
+       static initcall_t __initcall_##fn                       \
        __used __section(.con_initcall.init) = fn
 
-#define security_initcall(fn) \
-       static initcall_t __initcall_##fn \
+#define security_initcall(fn)                                  \
+       static initcall_t __initcall_##fn                       \
        __used __section(.security_initcall.init) = fn
 
 struct obs_kernel_param {
 
 # Makefile for the linux kernel.
 #
 
+ccflags-y := -fno-function-sections -fno-data-sections
+
 obj-y                          := main.o version.o mounts.o
 ifneq ($(CONFIG_BLK_DEV_INITRD),y)
 obj-y                          += noinitramfs.o