Skip to content

Commit b67067f

Browse files
npigginMichal Marek
authored andcommitted
kbuild: allow archs to select link dead code/data elimination
Introduce LD_DEAD_CODE_DATA_ELIMINATION option for architectures to select to build with -ffunction-sections, -fdata-sections, and link with --gc-sections. It requires some work (documented) to ensure all unreferenced entrypoints are live, and requires toolchain and build verification, so it is made a per-arch option for now. On a random powerpc64le build, this yelds a significant size saving, it boots and runs fine, but there is a lot I haven't tested as yet, so these savings may be reduced if there are bugs in the link. text data bss dec filename 11169741 1180744 1923176 14273661 vmlinux 10445269 1004127 1919707 13369103 vmlinux.dce ~700K text, ~170K data, 6% removed from kernel image size. Signed-off-by: Nicholas Piggin <[email protected]> Signed-off-by: Michal Marek <[email protected]>
1 parent a5967db commit b67067f

File tree

7 files changed

+104
-63
lines changed

7 files changed

+104
-63
lines changed

Makefile

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -622,6 +622,11 @@ include arch/$(SRCARCH)/Makefile
622622
KBUILD_CFLAGS += $(call cc-option,-fno-delete-null-pointer-checks,)
623623
KBUILD_CFLAGS += $(call cc-disable-warning,maybe-uninitialized,)
624624

625+
ifdef CONFIG_LD_DEAD_CODE_DATA_ELIMINATION
626+
KBUILD_CFLAGS += $(call cc-option,-ffunction-sections,)
627+
KBUILD_CFLAGS += $(call cc-option,-fdata-sections,)
628+
endif
629+
625630
ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE
626631
KBUILD_CFLAGS += -Os
627632
else
@@ -809,6 +814,10 @@ LDFLAGS_BUILD_ID = $(patsubst -Wl$(comma)%,%,\
809814
KBUILD_LDFLAGS_MODULE += $(LDFLAGS_BUILD_ID)
810815
LDFLAGS_vmlinux += $(LDFLAGS_BUILD_ID)
811816

817+
ifdef CONFIG_LD_DEAD_CODE_DATA_ELIMINATION
818+
LDFLAGS_vmlinux += $(call ld-option, --gc-sections,)
819+
endif
820+
812821
ifeq ($(CONFIG_STRIP_ASM_SYMS),y)
813822
LDFLAGS_vmlinux += $(call ld-option, -X,)
814823
endif

arch/Kconfig

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,19 @@ config THIN_ARCHIVES
467467
Select this if the architecture wants to use thin archives
468468
instead of ld -r to create the built-in.o files.
469469

470+
config LD_DEAD_CODE_DATA_ELIMINATION
471+
bool
472+
help
473+
Select this if the architecture wants to do dead code and
474+
data elimination with the linker by compiling with
475+
-ffunction-sections -fdata-sections and linking with
476+
--gc-sections.
477+
478+
This requires that the arch annotates or otherwise protects
479+
its external entry points from being discarded. Linker scripts
480+
must also merge .text.*, .data.*, and .bss.* correctly into
481+
output sections.
482+
470483
config HAVE_CONTEXT_TRACKING
471484
bool
472485
help

include/asm-generic/vmlinux.lds.h

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -196,9 +196,14 @@
196196
*(.dtb.init.rodata) \
197197
VMLINUX_SYMBOL(__dtb_end) = .;
198198

199-
/* .data section */
199+
/*
200+
* .data section
201+
* -fdata-sections generates .data.identifier which needs to be pulled in
202+
* with .data, but don't want to pull in .data..stuff which has its own
203+
* requirements. Same for bss.
204+
*/
200205
#define DATA_DATA \
201-
*(.data) \
206+
*(.data .data.[0-9a-zA-Z_]*) \
202207
*(.ref.data) \
203208
*(.data..shared_aligned) /* percpu related */ \
204209
MEM_KEEP(init.data) \
@@ -320,76 +325,76 @@
320325
/* Kernel symbol table: Normal symbols */ \
321326
__ksymtab : AT(ADDR(__ksymtab) - LOAD_OFFSET) { \
322327
VMLINUX_SYMBOL(__start___ksymtab) = .; \
323-
*(SORT(___ksymtab+*)) \
328+
KEEP(*(SORT(___ksymtab+*))) \
324329
VMLINUX_SYMBOL(__stop___ksymtab) = .; \
325330
} \
326331
\
327332
/* Kernel symbol table: GPL-only symbols */ \
328333
__ksymtab_gpl : AT(ADDR(__ksymtab_gpl) - LOAD_OFFSET) { \
329334
VMLINUX_SYMBOL(__start___ksymtab_gpl) = .; \
330-
*(SORT(___ksymtab_gpl+*)) \
335+
KEEP(*(SORT(___ksymtab_gpl+*))) \
331336
VMLINUX_SYMBOL(__stop___ksymtab_gpl) = .; \
332337
} \
333338
\
334339
/* Kernel symbol table: Normal unused symbols */ \
335340
__ksymtab_unused : AT(ADDR(__ksymtab_unused) - LOAD_OFFSET) { \
336341
VMLINUX_SYMBOL(__start___ksymtab_unused) = .; \
337-
*(SORT(___ksymtab_unused+*)) \
342+
KEEP(*(SORT(___ksymtab_unused+*))) \
338343
VMLINUX_SYMBOL(__stop___ksymtab_unused) = .; \
339344
} \
340345
\
341346
/* Kernel symbol table: GPL-only unused symbols */ \
342347
__ksymtab_unused_gpl : AT(ADDR(__ksymtab_unused_gpl) - LOAD_OFFSET) { \
343348
VMLINUX_SYMBOL(__start___ksymtab_unused_gpl) = .; \
344-
*(SORT(___ksymtab_unused_gpl+*)) \
349+
KEEP(*(SORT(___ksymtab_unused_gpl+*))) \
345350
VMLINUX_SYMBOL(__stop___ksymtab_unused_gpl) = .; \
346351
} \
347352
\
348353
/* Kernel symbol table: GPL-future-only symbols */ \
349354
__ksymtab_gpl_future : AT(ADDR(__ksymtab_gpl_future) - LOAD_OFFSET) { \
350355
VMLINUX_SYMBOL(__start___ksymtab_gpl_future) = .; \
351-
*(SORT(___ksymtab_gpl_future+*)) \
356+
KEEP(*(SORT(___ksymtab_gpl_future+*))) \
352357
VMLINUX_SYMBOL(__stop___ksymtab_gpl_future) = .; \
353358
} \
354359
\
355360
/* Kernel symbol table: Normal symbols */ \
356361
__kcrctab : AT(ADDR(__kcrctab) - LOAD_OFFSET) { \
357362
VMLINUX_SYMBOL(__start___kcrctab) = .; \
358-
*(SORT(___kcrctab+*)) \
363+
KEEP(*(SORT(___kcrctab+*))) \
359364
VMLINUX_SYMBOL(__stop___kcrctab) = .; \
360365
} \
361366
\
362367
/* Kernel symbol table: GPL-only symbols */ \
363368
__kcrctab_gpl : AT(ADDR(__kcrctab_gpl) - LOAD_OFFSET) { \
364369
VMLINUX_SYMBOL(__start___kcrctab_gpl) = .; \
365-
*(SORT(___kcrctab_gpl+*)) \
370+
KEEP(*(SORT(___kcrctab_gpl+*))) \
366371
VMLINUX_SYMBOL(__stop___kcrctab_gpl) = .; \
367372
} \
368373
\
369374
/* Kernel symbol table: Normal unused symbols */ \
370375
__kcrctab_unused : AT(ADDR(__kcrctab_unused) - LOAD_OFFSET) { \
371376
VMLINUX_SYMBOL(__start___kcrctab_unused) = .; \
372-
*(SORT(___kcrctab_unused+*)) \
377+
KEEP(*(SORT(___kcrctab_unused+*))) \
373378
VMLINUX_SYMBOL(__stop___kcrctab_unused) = .; \
374379
} \
375380
\
376381
/* Kernel symbol table: GPL-only unused symbols */ \
377382
__kcrctab_unused_gpl : AT(ADDR(__kcrctab_unused_gpl) - LOAD_OFFSET) { \
378383
VMLINUX_SYMBOL(__start___kcrctab_unused_gpl) = .; \
379-
*(SORT(___kcrctab_unused_gpl+*)) \
384+
KEEP(*(SORT(___kcrctab_unused_gpl+*))) \
380385
VMLINUX_SYMBOL(__stop___kcrctab_unused_gpl) = .; \
381386
} \
382387
\
383388
/* Kernel symbol table: GPL-future-only symbols */ \
384389
__kcrctab_gpl_future : AT(ADDR(__kcrctab_gpl_future) - LOAD_OFFSET) { \
385390
VMLINUX_SYMBOL(__start___kcrctab_gpl_future) = .; \
386-
*(SORT(___kcrctab_gpl_future+*)) \
391+
KEEP(*(SORT(___kcrctab_gpl_future+*))) \
387392
VMLINUX_SYMBOL(__stop___kcrctab_gpl_future) = .; \
388393
} \
389394
\
390395
/* Kernel symbol table: strings */ \
391396
__ksymtab_strings : AT(ADDR(__ksymtab_strings) - LOAD_OFFSET) { \
392-
*(__ksymtab_strings) \
397+
KEEP(*(__ksymtab_strings)) \
393398
} \
394399
\
395400
/* __*init sections */ \
@@ -424,15 +429,15 @@
424429
#define SECURITY_INIT \
425430
.security_initcall.init : AT(ADDR(.security_initcall.init) - LOAD_OFFSET) { \
426431
VMLINUX_SYMBOL(__security_initcall_start) = .; \
427-
*(.security_initcall.init) \
432+
KEEP(*(.security_initcall.init)) \
428433
VMLINUX_SYMBOL(__security_initcall_end) = .; \
429434
}
430435

431436
/* .text section. Map to function alignment to avoid address changes
432437
* during second ld run in second ld pass when generating System.map */
433438
#define TEXT_TEXT \
434439
ALIGN_FUNCTION(); \
435-
*(.text.hot .text .text.fixup .text.unlikely) \
440+
*(.text.hot .text .text.fixup .text.unlikely .text.*) \
436441
*(.ref.text) \
437442
MEM_KEEP(init.text) \
438443
MEM_KEEP(exit.text) \
@@ -527,6 +532,7 @@
527532

528533
/* init and exit section handling */
529534
#define INIT_DATA \
535+
KEEP(*(SORT(___kentry+*))) \
530536
*(.init.data) \
531537
MEM_DISCARD(init.data) \
532538
KERNEL_CTORS() \
@@ -593,7 +599,7 @@
593599
BSS_FIRST_SECTIONS \
594600
*(.bss..page_aligned) \
595601
*(.dynbss) \
596-
*(.bss) \
602+
*(.bss .bss.[0-9a-zA-Z_]*) \
597603
*(COMMON) \
598604
}
599605

@@ -676,12 +682,12 @@
676682

677683
#define INIT_CALLS_LEVEL(level) \
678684
VMLINUX_SYMBOL(__initcall##level##_start) = .; \
679-
*(.initcall##level##.init) \
680-
*(.initcall##level##s.init) \
685+
KEEP(*(.initcall##level##.init)) \
686+
KEEP(*(.initcall##level##s.init)) \
681687

682688
#define INIT_CALLS \
683689
VMLINUX_SYMBOL(__initcall_start) = .; \
684-
*(.initcallearly.init) \
690+
KEEP(*(.initcallearly.init)) \
685691
INIT_CALLS_LEVEL(0) \
686692
INIT_CALLS_LEVEL(1) \
687693
INIT_CALLS_LEVEL(2) \
@@ -695,21 +701,21 @@
695701

696702
#define CON_INITCALL \
697703
VMLINUX_SYMBOL(__con_initcall_start) = .; \
698-
*(.con_initcall.init) \
704+
KEEP(*(.con_initcall.init)) \
699705
VMLINUX_SYMBOL(__con_initcall_end) = .;
700706

701707
#define SECURITY_INITCALL \
702708
VMLINUX_SYMBOL(__security_initcall_start) = .; \
703-
*(.security_initcall.init) \
709+
KEEP(*(.security_initcall.init)) \
704710
VMLINUX_SYMBOL(__security_initcall_end) = .;
705711

706712
#ifdef CONFIG_BLK_DEV_INITRD
707713
#define INIT_RAM_FS \
708714
. = ALIGN(4); \
709715
VMLINUX_SYMBOL(__initramfs_start) = .; \
710-
*(.init.ramfs) \
716+
KEEP(*(.init.ramfs)) \
711717
. = ALIGN(8); \
712-
*(.init.ramfs.info)
718+
KEEP(*(.init.ramfs.info))
713719
#else
714720
#define INIT_RAM_FS
715721
#endif

include/linux/compiler.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,29 @@ void ftrace_likely_update(struct ftrace_branch_data *f, int val, int expect);
182182
# define unreachable() do { } while (1)
183183
#endif
184184

185+
/*
186+
* KENTRY - kernel entry point
187+
* This can be used to annotate symbols (functions or data) that are used
188+
* without their linker symbol being referenced explicitly. For example,
189+
* interrupt vector handlers, or functions in the kernel image that are found
190+
* programatically.
191+
*
192+
* Not required for symbols exported with EXPORT_SYMBOL, or initcalls. Those
193+
* are handled in their own way (with KEEP() in linker scripts).
194+
*
195+
* KENTRY can be avoided if the symbols in question are marked as KEEP() in the
196+
* linker script. For example an architecture could KEEP() its entire
197+
* boot/exception vector code rather than annotate each function and data.
198+
*/
199+
#ifndef KENTRY
200+
# define KENTRY(sym) \
201+
extern typeof(sym) sym; \
202+
static const unsigned long __kentry_##sym \
203+
__used \
204+
__attribute__((section("___kentry" "+" #sym ), used)) \
205+
= (unsigned long)&sym;
206+
#endif
207+
185208
#ifndef RELOC_HIDE
186209
# define RELOC_HIDE(ptr, off) \
187210
({ unsigned long __ptr; \

include/linux/export.h

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#ifndef _LINUX_EXPORT_H
22
#define _LINUX_EXPORT_H
3+
34
/*
45
* Export symbols from the kernel to modules. Forked from module.h
56
* to reduce the amount of pointless cruft we feed to gcc when only
@@ -42,27 +43,26 @@ extern struct module __this_module;
4243
#ifdef CONFIG_MODVERSIONS
4344
/* Mark the CRC weak since genksyms apparently decides not to
4445
* generate a checksums for some symbols */
45-
#define __CRC_SYMBOL(sym, sec) \
46-
extern __visible void *__crc_##sym __attribute__((weak)); \
47-
static const unsigned long __kcrctab_##sym \
48-
__used \
49-
__attribute__((section("___kcrctab" sec "+" #sym), unused)) \
46+
#define __CRC_SYMBOL(sym, sec) \
47+
extern __visible void *__crc_##sym __attribute__((weak)); \
48+
static const unsigned long __kcrctab_##sym \
49+
__used \
50+
__attribute__((section("___kcrctab" sec "+" #sym), used)) \
5051
= (unsigned long) &__crc_##sym;
5152
#else
5253
#define __CRC_SYMBOL(sym, sec)
5354
#endif
5455

5556
/* For every exported symbol, place a struct in the __ksymtab section */
56-
#define ___EXPORT_SYMBOL(sym, sec) \
57-
extern typeof(sym) sym; \
58-
__CRC_SYMBOL(sym, sec) \
59-
static const char __kstrtab_##sym[] \
60-
__attribute__((section("__ksymtab_strings"), aligned(1))) \
61-
= VMLINUX_SYMBOL_STR(sym); \
62-
extern const struct kernel_symbol __ksymtab_##sym; \
63-
__visible const struct kernel_symbol __ksymtab_##sym \
64-
__used \
65-
__attribute__((section("___ksymtab" sec "+" #sym), unused)) \
57+
#define ___EXPORT_SYMBOL(sym, sec) \
58+
extern typeof(sym) sym; \
59+
__CRC_SYMBOL(sym, sec) \
60+
static const char __kstrtab_##sym[] \
61+
__attribute__((section("__ksymtab_strings"), aligned(1))) \
62+
= VMLINUX_SYMBOL_STR(sym); \
63+
static const struct kernel_symbol __ksymtab_##sym \
64+
__used \
65+
__attribute__((section("___ksymtab" sec "+" #sym), used)) \
6666
= { (unsigned long)&sym, __kstrtab_##sym }
6767

6868
#if defined(__KSYM_DEPS__)

include/linux/init.h

Lines changed: 13 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -150,37 +150,25 @@ extern bool initcall_debug;
150150

151151
#ifndef __ASSEMBLY__
152152

153-
#ifdef CONFIG_LTO
154-
/* Work around a LTO gcc problem: when there is no reference to a variable
155-
* in a module it will be moved to the end of the program. This causes
156-
* reordering of initcalls which the kernel does not like.
157-
* Add a dummy reference function to avoid this. The function is
158-
* deleted by the linker.
159-
*/
160-
#define LTO_REFERENCE_INITCALL(x) \
161-
; /* yes this is needed */ \
162-
static __used __exit void *reference_##x(void) \
163-
{ \
164-
return &x; \
165-
}
166-
#else
167-
#define LTO_REFERENCE_INITCALL(x)
168-
#endif
169-
170-
/* initcalls are now grouped by functionality into separate
153+
/*
154+
* initcalls are now grouped by functionality into separate
171155
* subsections. Ordering inside the subsections is determined
172156
* by link order.
173157
* For backwards compatibility, initcall() puts the call in
174158
* the device init subsection.
175159
*
176160
* The `id' arg to __define_initcall() is needed so that multiple initcalls
177161
* can point at the same handler without causing duplicate-symbol build errors.
162+
*
163+
* Initcalls are run by placing pointers in initcall sections that the
164+
* kernel iterates at runtime. The linker can do dead code / data elimination
165+
* and remove that completely, so the initcall sections have to be marked
166+
* as KEEP() in the linker script.
178167
*/
179168

180169
#define __define_initcall(fn, id) \
181170
static initcall_t __initcall_##fn##id __used \
182-
__attribute__((__section__(".initcall" #id ".init"))) = fn; \
183-
LTO_REFERENCE_INITCALL(__initcall_##fn##id)
171+
__attribute__((__section__(".initcall" #id ".init"))) = fn;
184172

185173
/*
186174
* Early initcalls run before initializing SMP.
@@ -216,15 +204,15 @@ extern bool initcall_debug;
216204

217205
#define __initcall(fn) device_initcall(fn)
218206

219-
#define __exitcall(fn) \
207+
#define __exitcall(fn) \
220208
static exitcall_t __exitcall_##fn __exit_call = fn
221209

222-
#define console_initcall(fn) \
223-
static initcall_t __initcall_##fn \
210+
#define console_initcall(fn) \
211+
static initcall_t __initcall_##fn \
224212
__used __section(.con_initcall.init) = fn
225213

226-
#define security_initcall(fn) \
227-
static initcall_t __initcall_##fn \
214+
#define security_initcall(fn) \
215+
static initcall_t __initcall_##fn \
228216
__used __section(.security_initcall.init) = fn
229217

230218
struct obs_kernel_param {

init/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
# Makefile for the linux kernel.
33
#
44

5+
ccflags-y := -fno-function-sections -fno-data-sections
6+
57
obj-y := main.o version.o mounts.o
68
ifneq ($(CONFIG_BLK_DEV_INITRD),y)
79
obj-y += noinitramfs.o

0 commit comments

Comments
 (0)