[RFC 30/31] livepatch: Enable -ffunction-sections -fdata-sections

Josh Poimboeuf
2024-09-03T04:00:44Z

Doing a binary diff of two objects can be problematic:

- For intra-section references, the compiler might use hard-coded
offsets rather than relocations.

- Data references can be ambiguous if the compiler uses section symbol
references:

- Symbol overlap and zero-length symbols create ambiguity as to
which symbol is being referenced.

- An access to the end of a symbol (e.g., array bounds) could be
incorrectly interpreted as an access to the beginning of the next
symbol.

Remove those ambiguities by turning on -ffunction-sections and
-fdata-sections for CONFIG_LIVEPATCH.

They could alternatively be turned on only during the comparison builds,
rather than also in the production kernel, but that might theoretically
leave the possibility open for unexpected differences between the
kernels. The compared kernels should be identical to the original
kernel as much as possible.

The performance impact should be small. Intra-TU references are quite
rare in practice. And the resulting final code layout is similar.

Distros are likely going to start using these options soon anyway for
things like LTO and fgKASLR.

A potential alternative approach would be to add a toolchain option to
always use (symbol) relocations.

Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
---
Makefile | 9 +++++++++
include/asm-generic/vmlinux.lds.h | 2 +-
scripts/Makefile.lib | 2 +-
scripts/module.lds.S | 13 +++++++++----
4 files changed, 20 insertions(+), 6 deletions(-)

diff --git a/Makefile b/Makefile
index 3d10e3aadeda..2a15b560ecc0 100644
--- a/Makefile
+++ b/Makefile
@@ -931,10 +931,19 @@ ifdef CONFIG_DEBUG_SECTION_MISMATCH
KBUILD_CFLAGS += -fno-inline-functions-called-once
endif

+ifdef CONFIG_LIVEPATCH
+KBUILD_CFLAGS += -ffunction-sections -fdata-sections
# `rustc`'s `-Zfunction-sections` applies to data too (as of 1.59.0).
+KBUILD_RUSTFLAGS += -Zfunction-sections=y
+else
ifdef CONFIG_LD_DEAD_CODE_DATA_ELIMINATION
KBUILD_CFLAGS_KERNEL += -ffunction-sections -fdata-sections
+# `rustc`'s `-Zfunction-sections` applies to data too (as of 1.59.0).
KBUILD_RUSTFLAGS_KERNEL += -Zfunction-sections=y
+endif
+endif
+
+ifdef CONFIG_LD_DEAD_CODE_DATA_ELIMINATION
LDFLAGS_vmlinux += --gc-sections
endif

diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 5703526d6ebf..a7ac3ca596ad 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -98,7 +98,7 @@
* RODATA_MAIN is not used because existing code already defines .rodata.x
* sections to be brought in with rodata.
*/
-#if defined(CONFIG_LD_DEAD_CODE_DATA_ELIMINATION) || defined(CONFIG_LTO_CLANG)
+#if defined(CONFIG_LD_DEAD_CODE_DATA_ELIMINATION) || defined(CONFIG_LTO_CLANG) || defined(CONFIG_LIVEPATCH)
#define TEXT_MAIN .text .text.[0-9a-zA-Z_]*
#define DATA_MAIN .data .data.[0-9a-zA-Z_]* .data..L* .data..compoundliteral* .data.$__unnamed_* .data.$L*
#define SDATA_MAIN .sdata .sdata.[0-9a-zA-Z_]*
diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
index 9f4708702ef7..ca7497f74247 100644
--- a/scripts/Makefile.lib
+++ b/scripts/Makefile.lib
@@ -289,7 +289,7 @@ objtool-args = $(objtool-args-y) \
$(if $(delay-objtool), --link) \
$(if $(part-of-module), --module)

-delay-objtool := $(or $(CONFIG_LTO_CLANG),$(CONFIG_X86_KERNEL_IBT))
+delay-objtool := $(or $(CONFIG_LTO_CLANG),$(CONFIG_X86_KERNEL_IBT),$(CONFIG_LIVEPATCH))

cmd_objtool = $(if $(objtool-enabled), ; $(objtool) $(objtool-args) $@)
cmd_gen_objtooldep = $(if $(objtool-enabled), { echo ; echo '$@: $$(wildcard $(objtool))' ; } >> $(dot-target).cmd)
diff --git a/scripts/module.lds.S b/scripts/module.lds.S
index 3f43edef813c..5cbae820bca0 100644
--- a/scripts/module.lds.S
+++ b/scripts/module.lds.S
@@ -36,12 +36,17 @@ SECTIONS {
__kcfi_traps : { KEEP(*(.kcfi_traps)) }
#endif

-#ifdef CONFIG_LTO_CLANG
+#if defined(CONFIG_LTO_CLANG) || defined(CONFIG_LIVEPATCH)
+
/*
- * With CONFIG_LTO_CLANG, LLD always enables -fdata-sections and
- * -ffunction-sections, which increases the size of the final module.
- * Merge the split sections in the final binary.
+ * Merge -ffunction-sections and -fdata-sections sections to decrease
+ * module size.
*/
+
+ .text : {
+ *(.text .text.[0-9a-zA-Z_]*)
+ }
+
.bss : {
*(.bss .bss.[0-9a-zA-Z_]*)
*(.bss..L*)
--
2.45.2