Skip to contentSkip to conversation

objtool,livepatch: klp-build livepatch module generation

PATCH Superseded Josh Poimboeuf 3 participants 83 messages Jun 26, 2025 – Sep 16, 2025

Subsystems (11)

v3 of 4 Superseded Latest is v4 →

lore
Share & tools
lore

Josh Poimboeuf

author maintainer
#0 lore raw

v3 cover objtool,livepatch: klp-build livepatch module generation

This series introduces new objtool features and a klp-build script to generate livepatch modules using a source .patch as input.

This builds on concepts from the longstanding out-of-tree kpatch [1] project which began in 2012 and has been used for many years to generate livepatch modules for production kernels. However, this is a complete rewrite which incorporates hard-earned lessons from 12+ years of maintaining kpatch.

Key improvements compared to kpatch-build:

- Integrated with objtool: Leverages objtool's existing control-flow graph analysis to help detect changed functions.

- Works on vmlinux.o: Supports late-linked objects, making it compatible with LTO, IBT, and similar.

- Simplified code base: ~3k fewer lines of code.

- Upstream: No more out-of-tree #ifdef hacks, far less cruft.

- Cleaner internals: Vastly simplified logic for symbol/section/reloc inclusion and special section extraction.

- Robust __LINE__ macro handling: Avoids false positive binary diffs caused by the __LINE__ macro by introducing a fix-patch-lines script which injects #line directives into the source .patch to preserve the original line numbers at compile time.

The primary user interface is the klp-build script which does the following:

- Builds an original kernel with -function-sections and -fdata-sections, plus objtool function checksumming.

- Applies the .patch file and rebuilds the kernel using the same options.

- Runs 'objtool klp diff' to detect changed functions and generate intermediate binary diff objects.

- Builds a kernel module which links the diff objects with some livepatch module init code (scripts/livepatch/init.c).

- Finalizes the livepatch module (aka work around linker wreckage) using 'objtool klp post-link'.

I've tested with a variety of patches on defconfig and Fedora-config kernels with both GCC and Clang.

These patches can also be found at:

git://git.kernel.org/pub/scm/linux/kernel/git/jpoimboe/linux.git klp-build-v3

Please test!

[1] https://github.com/dynup/kpatch

Changes since v2: https://lore.kernel.org/[email protected]

- Fix bisection issue (Joe)
- Add CONFIG_KLP_BUILD to prevent powerpc from doing objtool-on-vmlinux (Joe)
- Use arch_pc_relative_reloc() in arch_insn_adjusted_addend() (Peter)
- Drop "objtool: Suppress section skipping warnings with --dryrun" (Peter)
- Resurrect --backup (Peter)
- Remove 16 byte prefix assumption (Peter, robot)
- Bump SEC_NAME_LEN to 1024 (Peter)
- Add sprintf error checking (Peter)
- Fix brace usage (Peter)
- Improve buggy linker sh_entsize warning (Peter)
- Refactor add_jump_destinations() some more to work with ITS retpolines
- Simplify error handling boilerplate
- Run "make clean" before original kernel build
- Fix .cmd file copying for CONFIG_MODVERSIONS (Joe)
- Add a line to setlocalversion rather than replacing the whole file
- Fix unset 'found' variable (Joe)
- Define section entry struct sizes in asm-offset.c and bounds.c (Brian)
- Fix STACK_FRAME_NON_STANDARD entry size unification (robot)
- Check for .rodata.*, .bss.* (Joe)
- Copy import_ns= entries from .modinfo (Joe)
- Strip signature from 'git format-patch' patches (Joe)
- Fix add_jump_destinations() (robot)
- ARCH=um fixes (robot)
- Fix vmlinux.o/vmlinux sympos mismatch
- Add klp-build --jobs option
- Allow .git to be a file (Dylan)
- Static branch/call fixes (Joe)
- Disable .printk_index support for now (Joe)
- Strip .BTF section (Joe)

Josh Poimboeuf (64):
s390/vmlinux.lds.S: Prevent thunk functions from getting placed with
normal text
vmlinux.lds: Unify TEXT_MAIN, DATA_MAIN, and related macros x86/module: Improve relocation error messages
x86/kprobes: Remove STACK_FRAME_NON_STANDARD annotation
compiler: Tweak __UNIQUE_ID() naming
compiler.h: Make addressable symbols less of an eyesore
elfnote: Change ELFNOTE() to use __UNIQUE_ID()
kbuild: Remove 'kmod_' prefix from __KBUILD_MODNAME
modpost: Ignore unresolved section bounds symbols x86/alternative: Refactor INT3 call emulation selftest
objtool: Make find_symbol_containing() less arbitrary
objtool: Fix broken error handling in read_symbols()
objtool: Propagate elf_truncate_section() error in elf_write()
objtool: Remove error handling boilerplate
objtool: Add empty symbols to the symbol tree again
objtool: Fix interval tree insertion for zero-length symbols
objtool: Fix weak symbol detection
objtool: Fix x86 addend calculation
objtool: Fix __pa_symbol() relocation handling
objtool: Fix "unexpected end of section" warning for alternatives
objtool: Check for missing annotation entries in read_annotate()
objtool: Const string cleanup
objtool: Clean up compiler flag usage
objtool: Remove .parainstructions reference
objtool: Convert elf iterator macros to use 'struct elf'
objtool: Add section/symbol type helpers
objtool: Mark .cold subfunctions
objtool: Fix weak symbol hole detection for .cold functions
objtool: Mark prefix functions
objtool: Simplify reloc offset calculation in unwind_read_hints()
objtool: Avoid emptying lists for duplicate sections
objtool: Rename --Werror to --werror
objtool: Resurrect --backup option
objtool: Reindent check_options[]
objtool: Refactor add_jump_destinations()
objtool: Simplify special symbol handling in elf_update_symbol()
objtool: Generalize elf_create_symbol()
objtool: Generalize elf_create_section()
objtool: Add elf_create_data()
objtool: Add elf_create_reloc() and elf_init_reloc()
objtool: Add elf_create_file()
kbuild,x86: Fix special section module permissions x86/alternative: Define ELF section entry size for alternatives x86/jump_label: Define ELF section entry size for jump labels x86/static_call: Define ELF section entry size of static calls x86/extable: Define ELF section entry size for exception table x86/bug: Define ELF section entry size for bug table x86/orc: Define ELF section entry size for unwind hints
objtool: Unify STACK_FRAME_NON_STANDARD entry sizes objtool/klp: Add --checksum option to generate per-function checksums objtool/klp: Add --debug-checksum=<funcs> to show per-instruction
checksums
objtool/klp: Introduce klp diff subcommand for diffing object files objtool/klp: Add --debug option to show cloning decisions objtool/klp: Add post-link subcommand to finalize livepatch modules
objtool: Disallow duplicate prefix symbols
objtool: Add base objtool support for livepatch modules
livepatch: Add CONFIG_KLP_BUILD
kbuild,objtool: Defer objtool validation step for CONFIG_KLP_BUILD livepatch/klp-build: Introduce fix-patch-lines script to avoid
__LINE__ diff noise
livepatch/klp-build: Add stub init code for livepatch modules livepatch/klp-build: Introduce klp-build script for generating
livepatch modules
livepatch/klp-build: Add --debug option to show cloning decisions livepatch/klp-build: Add --show-first-changed option to show function
divergence
livepatch: Introduce source code helpers for livepatch modules

 MAINTAINERS                                   |    3 +-
 arch/Kconfig                                  |    3 +
 arch/s390/include/asm/nospec-insn.h           |    2 +-
 arch/s390/kernel/vmlinux.lds.S                |    2 +-
 arch/um/include/asm/Kbuild                    |    1 -
 arch/um/include/shared/common-offsets.h       |    3 +
 arch/x86/Kconfig                              |    2 +
 arch/x86/include/asm/alternative.h            |    5 +-
 arch/x86/include/asm/asm.h                    |   22 +-
 arch/x86/include/asm/bug.h                    |   45 +-
 arch/x86/include/asm/jump_label.h             |   17 +-
 arch/x86/include/asm/static_call.h            |    3 +-
 arch/x86/kernel/alternative.c                 |   51 +-
 arch/x86/kernel/asm-offsets.c                 |    3 +
 arch/x86/kernel/kprobes/opt.c                 |    4 -
 arch/x86/kernel/module.c                      |   15 +-
 arch/x86/um/shared/sysdep/kernel-offsets.h    |    2 +
 include/asm-generic/vmlinux.lds.h             |   40 +-
 include/linux/compiler.h                      |    8 +-
 include/linux/elfnote.h                       |   13 +-
 include/linux/init.h                          |    3 +-
 include/linux/livepatch.h                     |   25 +-
 include/linux/livepatch_external.h            |   76 +
 include/linux/livepatch_helpers.h             |   79 +
 include/linux/objtool.h                       |   13 +-
 include/linux/static_call.h                   |    6 -
 include/linux/static_call_types.h             |    6 +
 kernel/bounds.c                               |   13 +
 kernel/livepatch/Kconfig                      |   12 +
 kernel/livepatch/core.c                       |    8 +-
 scripts/Makefile.lib                          |    6 +-
 scripts/Makefile.modfinal                     |   19 +-
 scripts/Makefile.vmlinux_o                    |    2 +-
 scripts/link-vmlinux.sh                       |    3 +-
 scripts/livepatch/fix-patch-lines             |   79 +
 scripts/livepatch/init.c                      |  108 ++
 scripts/livepatch/klp-build                   |  831 ++++++++
 scripts/mod/devicetable-offsets.c             |    1 +
 scripts/mod/modpost.c                         |    5 +
 scripts/module.lds.S                          |   26 +-
 tools/include/linux/interval_tree_generic.h   |    2 +-
 tools/include/linux/livepatch_external.h      |   76 +
 tools/include/linux/static_call_types.h       |    6 +
 tools/include/linux/string.h                  |   14 +
 tools/objtool/Build                           |    4 +-
 tools/objtool/Makefile                        |   48 +-
 tools/objtool/arch/loongarch/decode.c         |    6 +-
 tools/objtool/arch/powerpc/decode.c           |    6 +-
 tools/objtool/arch/x86/decode.c               |   62 +-
 tools/objtool/arch/x86/special.c              |    2 +-
 tools/objtool/builtin-check.c                 |   95 +-
 tools/objtool/builtin-klp.c                   |   53 +
 tools/objtool/check.c                         |  826 ++++----
 tools/objtool/elf.c                           |  765 ++++++--
 tools/objtool/include/objtool/arch.h          |    5 +-
 tools/objtool/include/objtool/builtin.h       |    9 +-
 tools/objtool/include/objtool/check.h         |    6 +-
 tools/objtool/include/objtool/checksum.h      |   43 +
 .../objtool/include/objtool/checksum_types.h  |   25 +
 tools/objtool/include/objtool/elf.h           |  185 +-
 tools/objtool/include/objtool/klp.h           |   35 +
 tools/objtool/include/objtool/objtool.h       |    6 +-
 tools/objtool/include/objtool/util.h          |   19 +
 tools/objtool/include/objtool/warn.h          |   40 +
 tools/objtool/klp-diff.c                      | 1677 +++++++++++++++++
 tools/objtool/klp-post-link.c                 |  168 ++
 tools/objtool/objtool.c                       |   42 +-
 tools/objtool/orc_gen.c                       |    8 +-
 tools/objtool/special.c                       |   13 +-
 tools/objtool/sync-check.sh                   |    1 +
 tools/objtool/weak.c                          |    7 +
 71 files changed, 5007 insertions(+), 812 deletions(-)
 create mode 100644 include/linux/livepatch_external.h
 create mode 100644 include/linux/livepatch_helpers.h
 create mode 100755 scripts/livepatch/fix-patch-lines
 create mode 100644 scripts/livepatch/init.c
 create mode 100755 scripts/livepatch/klp-build
 create mode 100644 tools/include/linux/livepatch_external.h
 create mode 100644 tools/objtool/builtin-klp.c
 create mode 100644 tools/objtool/include/objtool/checksum.h
 create mode 100644 tools/objtool/include/objtool/checksum_types.h
 create mode 100644 tools/objtool/include/objtool/klp.h
 create mode 100644 tools/objtool/include/objtool/util.h
 create mode 100644 tools/objtool/klp-diff.c
 create mode 100644 tools/objtool/klp-post-link.c

Diff between v2 and v3:

arch/um/include/asm/Kbuild +0 -1
index 04ab3b653a48..1934fa0df888 100644
@@ -5,7 +5,6 @@ generic-y += device.h
 generic-y += dma-mapping.h
 generic-y += emergency-restart.h
 generic-y += exec.h
-generic-y += extable.h
 generic-y += ftrace.h
 generic-y += hw_irq.h
 generic-y += irq_regs.h
arch/um/include/shared/common-offsets.h +3 -0
index 8ca66a1918c3..a6f77cb6aa7e 100644
@@ -18,3 +18,6 @@ DEFINE(UM_NSEC_PER_USEC, NSEC_PER_USEC);
 DEFINE(UM_KERN_GDT_ENTRY_TLS_ENTRIES, GDT_ENTRY_TLS_ENTRIES);
 
 DEFINE(UM_SECCOMP_ARCH_NATIVE, SECCOMP_ARCH_NATIVE);
+
+DEFINE(ALT_INSTR_SIZE, sizeof(struct alt_instr));
+DEFINE(EXTABLE_SIZE,   sizeof(struct exception_table_entry));
arch/x86/Kconfig +1 -0
index 62faa62b5959..448c6bfb71d6 100644
@@ -266,6 +266,7 @@ config X86
 	select HAVE_FUNCTION_ERROR_INJECTION
 	select HAVE_KRETPROBES
 	select HAVE_RETHOOK
+	select HAVE_KLP_BUILD			if X86_64
 	select HAVE_LIVEPATCH			if X86_64
 	select HAVE_MIXED_BREAKPOINTS_REGS
 	select HAVE_MOD_ARCH_SPECIFIC
arch/x86/include/asm/alternative.h +2 -4
index e206e0f96568..eb24d9ba30d7 100644
@@ -16,8 +16,6 @@
 #define ALT_DIRECT_CALL(feature) ((ALT_FLAG_DIRECT_CALL << ALT_FLAGS_SHIFT) | (feature))
 #define ALT_CALL_ALWAYS		ALT_DIRECT_CALL(X86_FEATURE_ALWAYS)
 
-#define ALTINSTR_SIZE		14
-
 #ifndef __ASSEMBLER__
 
 #include <linux/stddef.h>
@@ -200,7 +198,7 @@ static inline int alternatives_text_reserved(void *start, void *end)
 
 #define ALTINSTR_ENTRY(ft_flags)					      \
 	".pushsection .altinstructions, \"aM\", @progbits, "		      \
-		      __stringify(ALTINSTR_SIZE) "\n"			      \
+		      __stringify(ALT_INSTR_SIZE) "\n"			      \
 	" .long 771b - .\n"				/* label           */ \
 	" .long 774f - .\n"				/* new instruction */ \
 	" .4byte " __stringify(ft_flags) "\n"		/* feature + flags */ \
@@ -363,7 +361,7 @@ void nop_func(void);
 741:									\
 	.skip -(((744f-743f)-(741b-740b)) > 0) * ((744f-743f)-(741b-740b)),0x90	;\
 742:									\
-	.pushsection .altinstructions, "aM", @progbits, ALTINSTR_SIZE ;	\
+	.pushsection .altinstructions, "aM", @progbits, ALT_INSTR_SIZE ;\
 	altinstr_entry 740b,743f,flag,742b-740b,744f-743f ;		\
 	.popsection ;							\
 	.pushsection .altinstr_replacement,"ax"	;			\
arch/x86/include/asm/asm.h +4 -2
index 62dff336f206..e9b6d2d006c6 100644
@@ -136,9 +136,11 @@ static __always_inline __pure void *rip_rel_ptr(void *p)
 
 #ifdef __KERNEL__
 
-# include <asm/extable_fixup_types.h>
+#ifndef COMPILE_OFFSETS
+#include <asm/asm-offsets.h>
+#endif
 
-#define EXTABLE_SIZE 12
+# include <asm/extable_fixup_types.h>
 
 /* Exception table entry */
 #ifdef __ASSEMBLER__
arch/x86/include/asm/jump_label.h +19 -18
index 6081c33e1566..7a6b0e5d85c1 100644
@@ -12,31 +12,34 @@
 #include <linux/stringify.h>
 #include <linux/types.h>
 
-#define JUMP_TABLE_ENTRY(key, label, size)				\
-	".pushsection __jump_table, \"aM\", @progbits, " size "\n\t"	\
-	_ASM_ALIGN "\n\t"						\
-	".long 1b - . \n\t"						\
-	".long " label " - . \n\t"					\
-	_ASM_PTR " " key " - . \n\t"					\
+#ifndef COMPILE_OFFSETS
+#include <generated/bounds.h>
+#endif
+
+#define JUMP_TABLE_ENTRY(key, label)				\
+	".pushsection __jump_table,  \"aM\", @progbits, "	\
+	__stringify(JUMP_ENTRY_SIZE) "\n\t"			\
+	_ASM_ALIGN "\n\t"					\
+	".long 1b - . \n\t"					\
+	".long " label " - . \n\t"				\
+	_ASM_PTR " " key " - . \n\t"				\
 	".popsection \n\t"
 
 /* This macro is also expanded on the Rust side. */
 #ifdef CONFIG_HAVE_JUMP_LABEL_HACK
-#define ARCH_STATIC_BRANCH_ASM(key, label, size)	\
+#define ARCH_STATIC_BRANCH_ASM(key, label)		\
 	"1: jmp " label " # objtool NOPs this \n\t"	\
-	JUMP_TABLE_ENTRY(key " + 2", label, size)
+	JUMP_TABLE_ENTRY(key " + 2", label)
 #else /* !CONFIG_HAVE_JUMP_LABEL_HACK */
-#define ARCH_STATIC_BRANCH_ASM(key, label, size)	\
+#define ARCH_STATIC_BRANCH_ASM(key, label)		\
 	"1: .byte " __stringify(BYTES_NOP5) "\n\t"	\
-	JUMP_TABLE_ENTRY(key, label, size)
+	JUMP_TABLE_ENTRY(key, label)
 #endif /* CONFIG_HAVE_JUMP_LABEL_HACK */
 
 static __always_inline bool arch_static_branch(struct static_key * const key, const bool branch)
 {
-	asm goto(ARCH_STATIC_BRANCH_ASM("%c[key] + %c[branch]", "%l[l_yes]", "%c[size]")
-		 : : [key] "i" (key), [branch] "i" (branch),
-		     [size] "i" (sizeof(struct jump_entry))
-		 : : l_yes);
+	asm goto(ARCH_STATIC_BRANCH_ASM("%c0 + %c1", "%l[l_yes]")
+		: :  "i" (key), "i" (branch) : : l_yes);
 
 	return false;
 l_yes:
@@ -47,10 +50,8 @@ static __always_inline bool arch_static_branch_jump(struct static_key * const ke
 {
 	asm goto("1:"
 		"jmp %l[l_yes]\n\t"
-		JUMP_TABLE_ENTRY("%c[key] + %c[branch]", "%l[l_yes]", "%c[size]")
-		: : [key] "i" (key), [branch] "i" (branch),
-		    [size] "i" (sizeof(struct jump_entry))
-		: : l_yes);
+		JUMP_TABLE_ENTRY("%c0 + %c1", "%l[l_yes]")
+		: :  "i" (key), "i" (branch) : : l_yes);
 
 	return false;
 l_yes:
arch/x86/include/asm/static_call.h +2 -1
index 41502bd2afd6..e03ad9bbbf59 100644
@@ -58,7 +58,8 @@
 	ARCH_DEFINE_STATIC_CALL_TRAMP(name, __static_call_return0)
 
 #define ARCH_ADD_TRAMP_KEY(name)					\
-	asm(".pushsection .static_call_tramp_key, \"a\"		\n"	\
+	asm(".pushsection .static_call_tramp_key, \"aM\", @progbits, "	\
+	    __stringify(STATIC_CALL_TRAMP_KEY_SIZE) "\n"		\
 	    ".long " STATIC_CALL_TRAMP_STR(name) " - .		\n"	\
 	    ".long " STATIC_CALL_KEY_STR(name) " - .		\n"	\
 	    ".popsection					\n")
arch/x86/kernel/alternative.c +0 -2
index 499dc1ca2050..bacd6c157626 100644
@@ -622,8 +622,6 @@ void __init_or_module noinline apply_alternatives(struct alt_instr *start,
 	u8 *instr, *replacement;
 	struct alt_instr *a, *b;
 
-	BUILD_BUG_ON(ALTINSTR_SIZE != sizeof(struct alt_instr));
-
 	DPRINTK(ALT, "alt table %px, -> %px", start, end);
 
 	/*
arch/x86/kernel/asm-offsets.c +3 -0
index 6259b474073b..3d3eef7fae32 100644
@@ -123,4 +123,7 @@ static void __used common(void)
 	OFFSET(ARIA_CTX_rounds, aria_ctx, rounds);
 #endif
 
+	BLANK();
+	DEFINE(ALT_INSTR_SIZE,	 sizeof(struct alt_instr));
+	DEFINE(EXTABLE_SIZE,	 sizeof(struct exception_table_entry));
 }
arch/x86/kernel/unwind_orc.c +0 -2
index 4624d6d916a2..977ee75e047c 100644
@@ -199,8 +199,6 @@ static struct orc_entry *orc_find(unsigned long ip)
 {
 	static struct orc_entry *orc;
 
-	BUILD_BUG_ON(UNWIND_HINT_SIZE != sizeof(struct unwind_hint));
-
 	if (ip == 0)
 		return &null_orc_entry;
 
arch/x86/um/shared/sysdep/kernel-offsets.h +2 -0
index 6fd1ed400399..8215a0200ddd 100644
@@ -1,4 +1,5 @@
 /* SPDX-License-Identifier: GPL-2.0 */
+#define COMPILE_OFFSETS
 #include <linux/stddef.h>
 #include <linux/sched.h>
 #include <linux/elf.h>
@@ -7,6 +8,7 @@
 #include <linux/audit.h>
 #include <asm/mman.h>
 #include <asm/seccomp.h>
+#include <asm/extable.h>
 
 /* workaround for a warning with -Wmissing-prototypes */
 void foo(void);
include/asm-generic/vmlinux.lds.h +1 -1
index 3a0865e0a1a7..928e175254f6 100644
@@ -91,7 +91,7 @@
  * but exclude '.text..*'.
  *
  * Special .text.* sections that are typically grouped separately, such as
- * .text.unlikely or .text.hot, must be matched explicitly before invoking
+ * .text.unlikely or .text.hot, must be matched explicitly before using
  * TEXT_MAIN.
  */
 #define TEXT_MAIN .text .text.[0-9a-zA-Z_]*
include/linux/compiler.h +1 -1
index b90422671874..2039da81cf16 100644
@@ -287,7 +287,7 @@ static inline void *offset_to_ptr(const int *off)
  */
 #define ___ADDRESSABLE(sym, __attrs)						\
 	static void * __used __attrs						\
-	__UNIQUE_ID(__PASTE(addressable_,sym)) = (void *)(uintptr_t)&sym;
+	__UNIQUE_ID(__PASTE(addressable_, sym)) = (void *)(uintptr_t)&sym;
 
 #define __ADDRESSABLE(sym) \
 	___ADDRESSABLE(sym, __section(".discard.addressable"))
include/linux/jump_label.h +8 -12
index 9ff1ecc8e7a8..fdb79dd1ebd8 100644
@@ -110,20 +110,16 @@ struct static_key {
 #endif /* __ASSEMBLY__ */
 
 #ifdef CONFIG_JUMP_LABEL
+#include <asm/jump_label.h>
+
+#ifndef __ASSEMBLY__
+#ifdef CONFIG_HAVE_ARCH_JUMP_LABEL_RELATIVE
 
-#if defined(CONFIG_HAVE_ARCH_JUMP_LABEL_RELATIVE) && !defined(__ASSEMBLY__)
-/* Must be defined before including <asm/jump_label.h> */
 struct jump_entry {
 	s32 code;
 	s32 target;
 	long key;	// key may be far away from the core kernel under KASLR
 };
-#endif
-
-#include <asm/jump_label.h>
-
-#ifndef __ASSEMBLY__
-#ifdef CONFIG_HAVE_ARCH_JUMP_LABEL_RELATIVE
 
 static inline unsigned long jump_entry_code(const struct jump_entry *entry)
 {
@@ -142,7 +138,7 @@ static inline struct static_key *jump_entry_key(const struct jump_entry *entry)
 	return (struct static_key *)((unsigned long)&entry->key + offset);
 }
 
-#else /* !CONFIG_HAVE_ARCH_JUMP_LABEL_RELATIVE */
+#else
 
 static inline unsigned long jump_entry_code(const struct jump_entry *entry)
 {
@@ -159,7 +155,7 @@ static inline struct static_key *jump_entry_key(const struct jump_entry *entry)
 	return (struct static_key *)((unsigned long)entry->key & ~3UL);
 }
 
-#endif /* !CONFIG_HAVE_ARCH_JUMP_LABEL_RELATIVE */
+#endif
 
 static inline bool jump_entry_is_branch(const struct jump_entry *entry)
 {
@@ -188,8 +184,8 @@ static inline int jump_entry_size(struct jump_entry *entry)
 #endif
 }
 
-#endif /* !__ASSEMBLY__ */
-#endif /* CONFIG_JUMP_LABEL */
+#endif
+#endif
 
 #ifndef __ASSEMBLY__
 
include/linux/livepatch_helpers.h +11 -0
index 09f4a2d53fd7..337bee91d7da 100644
@@ -35,6 +35,17 @@
 	klp_post_unpatch_t __used __section(KLP_CALLBACK_PTRS)			\
 		__PASTE(__KLP_POST_UNPATCH_PREFIX, KLP_OBJNAME) = func
 
+/*
+ * KLP_STATIC_CALL
+ *
+ * Replace static_call() usage with this macro when create-diff-object
+ * recommends it due to the original static call key living in a module.
+ *
+ * This converts the static call to a regular indirect call.
+ */
+#define KLP_STATIC_CALL(name) \
+	((typeof(STATIC_CALL_TRAMP(name))*)(STATIC_CALL_KEY(name).func))
+
 /* Syscall patching */
 
 #define KLP_SYSCALL_DEFINE1(name, ...) KLP_SYSCALL_DEFINEx(1, _##name, __VA_ARGS__)
include/linux/objtool.h +8 -7
index d4137a46ee70..7d7bb7f1af69 100644
@@ -2,14 +2,16 @@
 #ifndef _LINUX_OBJTOOL_H
 #define _LINUX_OBJTOOL_H
 
+#ifndef COMPILE_OFFSETS
+#include <generated/bounds.h>
+#endif
+
 #include <linux/objtool_types.h>
 
 #ifdef CONFIG_OBJTOOL
 
 #include <asm/asm.h>
 
-#define UNWIND_HINT_SIZE 12
-
 #ifndef __ASSEMBLY__
 
 #define UNWIND_HINT(type, sp_reg, sp_offset, signal)		\
@@ -33,10 +35,9 @@
  *
  * For more information, see tools/objtool/Documentation/objtool.txt.
  */
-#define STACK_FRAME_NON_STANDARD(func)						\
-	asm(".pushsection .discard.func_stack_frame_non_standard, \"aw\"\n\t"	\
-	    ".long " __stringify(func) " - .\n\t"				\
-	    ".popsection")
+#define STACK_FRAME_NON_STANDARD(func) \
+	static void __used __section(".discard.func_stack_frame_non_standard") \
+		*__func_stack_frame_non_standard_##func = func
 
 /*
  * STACK_FRAME_NON_STANDARD_FP() is a frame-pointer-specific function ignore
@@ -105,7 +106,7 @@
 
 .macro STACK_FRAME_NON_STANDARD func:req
 	.pushsection .discard.func_stack_frame_non_standard, "aw"
-	.long \func - .
+	.quad \func
 	.popsection
 .endm
 
include/linux/static_call.h +0 -6
index 78a77a4ae0ea..5210612817f2 100644
@@ -172,12 +172,6 @@ struct static_call_mod {
 	struct static_call_site *sites;
 };
 
-/* For finding the key associated with a trampoline */
-struct static_call_tramp_key {
-	s32 tramp;
-	s32 key;
-};
-
 extern void __static_call_update(struct static_call_key *key, void *tramp, void *func);
 extern int static_call_mod_init(struct module *mod);
 extern int static_call_text_reserved(void *start, void *end);
include/linux/static_call_types.h +6 -0
index 5a00b8b2cf9f..eb772df625d4 100644
@@ -34,6 +34,12 @@ struct static_call_site {
 	s32 key;
 };
 
+/* For finding the key associated with a trampoline */
+struct static_call_tramp_key {
+	s32 tramp;
+	s32 key;
+};
+
 #define DECLARE_STATIC_CALL(name, func)					\
 	extern struct static_call_key STATIC_CALL_KEY(name);		\
 	extern typeof(func) STATIC_CALL_TRAMP(name);
kernel/bounds.c +13 -0
index 29b2cd00df2c..f9bc13727721 100644
@@ -6,12 +6,16 @@
  */
 
 #define __GENERATING_BOUNDS_H
+#define COMPILE_OFFSETS
 /* Include headers that define the enum constants of interest */
 #include <linux/page-flags.h>
 #include <linux/mmzone.h>
 #include <linux/kbuild.h>
 #include <linux/log2.h>
 #include <linux/spinlock_types.h>
+#include <linux/jump_label.h>
+#include <linux/static_call_types.h>
+#include <linux/objtool_types.h>
 
 int main(void)
 {
@@ -28,6 +32,15 @@ int main(void)
 #else
 	DEFINE(LRU_GEN_WIDTH, 0);
 	DEFINE(__LRU_REFS_WIDTH, 0);
+#endif
+#if defined(CONFIG_HAVE_ARCH_JUMP_LABEL_RELATIVE) && defined(CONFIG_JUMP_LABEL)
+	DEFINE(JUMP_ENTRY_SIZE, sizeof(struct jump_entry));
+#endif
+#ifdef CONFIG_HAVE_STATIC_CALL_INLINE
+	DEFINE(STATIC_CALL_TRAMP_KEY_SIZE, sizeof(struct static_call_tramp_key));
+#endif
+#ifdef CONFIG_OBJTOOL
+	DEFINE(UNWIND_HINT_SIZE, sizeof(struct unwind_hint));
 #endif
 	/* End of constants */
 
kernel/extable.c +0 -2
index 0ae3ee2ef266..71f482581cab 100644
@@ -55,8 +55,6 @@ const struct exception_table_entry *search_exception_tables(unsigned long addr)
 {
 	const struct exception_table_entry *e;
 
-	BUILD_BUG_ON(EXTABLE_SIZE != sizeof(struct exception_table_entry));
-
 	e = search_kernel_exception_table(addr);
 	if (!e)
 		e = search_module_extables(addr);
kernel/livepatch/Kconfig +12 -0
index 53d51ed619a3..4c0a9c18d0b2 100644
@@ -18,3 +18,15 @@ config LIVEPATCH
 	  module uses the interface provided by this option to register
 	  a patch, causing calls to patched functions to be redirected
 	  to new function code contained in the patch module.
+
+config HAVE_KLP_BUILD
+	bool
+	help
+	  Arch supports klp-build
+
+config KLP_BUILD
+	def_bool y
+	depends on LIVEPATCH && HAVE_KLP_BUILD
+	select OBJTOOL
+	help
+	  Enable klp-build support
scripts/Makefile.lib +1 -1
index 41420d24e62e..28a1c08e3b22 100644
@@ -197,7 +197,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),$(CONFIG_LIVEPATCH))
+delay-objtool := $(or $(CONFIG_LTO_CLANG),$(CONFIG_X86_KERNEL_IBT),$(CONFIG_KLP_BUILD))
 
 cmd_objtool = $(if $(objtool-enabled), ; $(objtool) $(objtool-args) $@)
 cmd_gen_objtooldep = $(if $(objtool-enabled), { echo ; echo '$@: $$(wildcard $(objtool))' ; } >> $(dot-target).cmd)
scripts/Makefile.modfinal +2 -1
index 878d0d25a461..7a888e1ff70f 100644
@@ -31,7 +31,8 @@ ccflags-remove-y := $(CC_FLAGS_CFI)
 ifdef CONFIG_NEED_MODULE_PERMISSIONS_FIX
 cmd_fix_mod_permissions =						\
 	$(OBJCOPY) --set-section-flags __jump_table=alloc,data		\
-		   --set-section-flags __bug_table=alloc,data $@
+		   --set-section-flags __bug_table=alloc,data $@	\
+		   --set-section-flags .static_call_sites=alloc,data $@
 endif
 
 quiet_cmd_ld_ko_o = LD [M]  $@
scripts/link-vmlinux.sh +1 -1
index acffa3c935f2..59f875236292 100755
@@ -61,7 +61,7 @@ vmlinux_link()
 	shift
 
 	if is_enabled CONFIG_LTO_CLANG || is_enabled CONFIG_X86_KERNEL_IBT ||
-	   is_enabled CONFIG_LIVEPATCH; then
+	   is_enabled CONFIG_KLP_BUILD; then
 		# Use vmlinux.o instead of performing the slow LTO link again.
 		objs=vmlinux.o
 		libs=
scripts/livepatch/fix-patch-lines +1 -1
index 73c5e3dea46e..fa7d4f6592e6 100755
@@ -23,7 +23,7 @@ BEGIN {
 
 	in_hunk = 1
 
-	# for @@ -1,3 +1,4 @@:
+	# @@ -1,3 +1,4 @@:
 	#   1: line number in old file
 	#   3: how many lines the hunk covers in old file
 	#   1: line number in new file
scripts/livepatch/klp-build +116 -66

408 lines not on this page · raw message

scripts/mod/devicetable-offsets.c +1 -0
index d3d00e85edf7..ef2ffb68f69d 100644
@@ -1,4 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
+#define COMPILE_OFFSETS
 #include <linux/kbuild.h>
 #include <linux/mod_devicetable.h>
 
tools/include/linux/static_call_types.h +6 -0
index 5a00b8b2cf9f..eb772df625d4 100644
@@ -34,6 +34,12 @@ struct static_call_site {
 	s32 key;
 };
 
+/* For finding the key associated with a trampoline */
+struct static_call_tramp_key {
+	s32 tramp;
+	s32 key;
+};
+
 #define DECLARE_STATIC_CALL(name, func)					\
 	extern struct static_call_key STATIC_CALL_KEY(name);		\
 	extern typeof(func) STATIC_CALL_TRAMP(name);
tools/include/linux/string.h +14 -0
index 8499f509f03e..51ad3cf4fa82 100644
@@ -44,6 +44,20 @@ static inline bool strstarts(const char *str, const char *prefix)
 	return strncmp(str, prefix, strlen(prefix)) == 0;
 }
 
+/*
+ * Checks if a string ends with another.
+ */
+static inline bool str_ends_with(const char *str, const char *substr)
+{
+	size_t len = strlen(str);
+	size_t sublen = strlen(substr);
+
+	if (sublen > len)
+		return false;
+
+	return !strcmp(str + len - sublen, substr);
+}
+
 extern char * __must_check skip_spaces(const char *);
 
 extern char *strim(char *);
tools/objtool/arch/x86/decode.c +1 -7
index 3b40afe144fe..c4d6b90b1134 100644
@@ -83,14 +83,8 @@ s64 arch_insn_adjusted_addend(struct instruction *insn, struct reloc *reloc)
 {
 	s64 addend = reloc_addend(reloc);
 
-	switch (reloc_type(reloc)) {
-	case R_X86_64_PC32:
-	case R_X86_64_PLT32:
+	if (arch_pc_relative_reloc(reloc))
 		addend += insn->offset + insn->len - reloc_offset(reloc);
-		break;
-	default:
-		break;
-	}
 
 	return phys_to_virt(addend);
 }
tools/objtool/builtin-check.c +11 -14
index 84918593d935..cd2c4a387100 100644
@@ -92,6 +92,7 @@ static const struct option check_options[] = {
 
 	OPT_GROUP("Options:"),
 	OPT_BOOLEAN(0,		 "backtrace", &opts.backtrace, "unwind on error"),
+	OPT_BOOLEAN(0,		 "backup", &opts.backup, "create backup (.orig) file on warning/error"),
 	OPT_STRING(0,		 "debug-checksum", &opts.debug_checksum,  "funcs", "enable checksum debug output"),
 	OPT_BOOLEAN(0,		 "dry-run", &opts.dryrun, "don't write modifications"),
 	OPT_BOOLEAN(0,		 "link", &opts.link, "object is a linked object"),
@@ -260,12 +261,9 @@ static void save_argv(int argc, const char **argv)
 	};
 }
 
-void print_args(void)
+int make_backup(void)
 {
-	char *backup = NULL;
-
-	if (opts.output || opts.dryrun)
-		goto print;
+	char *backup;
 
 	/*
 	 * Make a backup before kbuild deletes the file so the error
@@ -274,33 +272,32 @@ void print_args(void)
 	backup = malloc(strlen(objname) + strlen(ORIG_SUFFIX) + 1);
 	if (!backup) {
 		ERROR_GLIBC("malloc");
-		goto print;
+		return 1;
 	}
 
 	strcpy(backup, objname);
 	strcat(backup, ORIG_SUFFIX);
-	if (copy_file(objname, backup)) {
-		backup = NULL;
-		goto print;
-	}
+	if (copy_file(objname, backup))
+		return 1;
 
-print:
 	/*
-	 * Print the cmdline args to make it easier to recreate.  If '--output'
-	 * wasn't used, add it to the printed args with the backup as input.
+	 * Print the cmdline args to make it easier to recreate.
 	 */
+
 	fprintf(stderr, "%s", orig_argv[0]);
 
 	for (int i = 1; i < orig_argc; i++) {
 		char *arg = orig_argv[i];
 
-		if (backup && !strcmp(arg, objname))
+		/* Modify the printed args to use the backup */
+		if (!opts.output && !strcmp(arg, objname))
 			fprintf(stderr, " %s -o %s", backup, objname);
 		else
 			fprintf(stderr, " %s", arg);
 	}
 
 	fprintf(stderr, "\n");
+	return 0;
 }
 
 int objtool_run(int argc, const char **argv)
tools/objtool/check.c +161 -207

628 lines not on this page · raw message

tools/objtool/elf.c +2 -2
index ae1c852ff8d8..f9eed5d50de5 100644
@@ -492,8 +492,8 @@ static int elf_add_symbol(struct elf *elf, struct symbol *sym)
 	elf_hash_add(symbol, &sym->hash, sym->idx);
 	elf_hash_add(symbol_name, &sym->name_hash, str_hash(sym->name));
 
-	if (is_func_sym(sym) && sym->len == 16 &&
-	    (strstarts(sym->name, "__pfx") || strstarts(sym->name, "__cfi_")))
+	if (is_func_sym(sym) &&
+	    (strstarts(sym->name, "__pfx_") || strstarts(sym->name, "__cfi_")))
 		sym->prefix = 1;
 
 	if (strstarts(sym->name, ".klp.sym"))
tools/objtool/include/objtool/builtin.h +2 -1
index e8eb3c54c373..e60438577000 100644
@@ -30,6 +30,7 @@ struct opts {
 
 	/* options: */
 	bool backtrace;
+	bool backup;
 	const char *debug_checksum;
 	bool dryrun;
 	bool link;
@@ -49,7 +50,7 @@ int cmd_parse_options(int argc, const char **argv, const char * const usage[]);
 
 int objtool_run(int argc, const char **argv);
 
-void print_args(void);
+int make_backup(void);
 
 int cmd_klp(int argc, const char **argv);
 
tools/objtool/include/objtool/elf.h +1 -1
index adfe508f96f5..1212f81f40e0 100644
@@ -17,7 +17,7 @@
 #include <objtool/checksum_types.h>
 #include <arch/elf.h>
 
-#define SEC_NAME_LEN		512
+#define SEC_NAME_LEN		1024
 #define SYM_NAME_LEN		512
 
 #ifdef LIBELF_USE_DEPRECATED
tools/objtool/include/objtool/util.h new file +19 -0
new file mode 100644
index 000000000000..a0180b312f73
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef _UTIL_H
+#define _UTIL_H
+
+#include <objtool/warn.h>
+
+#define snprintf_check(str, size, format, args...)			\
+({									\
+	int __ret = snprintf(str, size, format, args);			\
+	if (__ret < 0)							\
+		ERROR_GLIBC("snprintf");				\
+	else if (__ret >= size)						\
+		ERROR("snprintf() failed for '" format "'", args);	\
+	else								\
+		__ret = 0;						\
+	__ret;								\
+})
+
+#endif /* _UTIL_H */
tools/objtool/klp-diff.c +246 -73

529 lines not on this page · raw message

tools/objtool/klp-post-link.c +5 -2
index 05be6251e35f..c013e39957b1 100644
@@ -16,6 +16,7 @@
 #include <objtool/objtool.h>
 #include <objtool/warn.h>
 #include <objtool/klp.h>
+#include <objtool/util.h>
 #include <linux/livepatch_external.h>
 
 static int fix_klp_relocs(struct elf *elf)
@@ -81,8 +82,10 @@ static int fix_klp_relocs(struct elf *elf)
 		 */
 
 		/* section format: .klp.rela.sec_objname.section_name */
-		snprintf(rsec_name, SEC_NAME_LEN, KLP_RELOC_SEC_PREFIX "%s.%s",
-			 sym_modname, sec->name);
+		if (snprintf_check(rsec_name, SEC_NAME_LEN,
+				   KLP_RELOC_SEC_PREFIX "%s.%s",
+				   sym_modname, sec->name))
+			return -1;
 
 		klp_rsec = find_section_by_name(elf, rsec_name);
 		if (!klp_rsec) {
tools/objtool/special.c +3 -6
index 9cfdd424e173..fc2cf8dba1c0 100644
@@ -133,7 +133,7 @@ int special_get_alts(struct elf *elf, struct list_head *alts)
 	struct section *sec;
 	unsigned int nr_entries;
 	struct special_alt *alt;
-	int idx, ret;
+	int idx;
 
 	INIT_LIST_HEAD(alts);
 
@@ -157,11 +157,8 @@ int special_get_alts(struct elf *elf, struct list_head *alts)
 			}
 			memset(alt, 0, sizeof(*alt));
 
-			ret = get_alt_entry(elf, entry, sec, idx, alt);
-			if (ret > 0)
-				continue;
-			if (ret < 0)
-				return ret;
+			if (get_alt_entry(elf, entry, sec, idx, alt))
+				return -1;
 
 			list_add_tail(&alt->list, alts);
 		}
author maintainer
#1 lore raw

PATCH v3 1/64 s390/vmlinux.lds.S: Prevent thunk functions from getting placed with normal text

The s390 indirect thunks are placed in the .text.__s390_indirect_jump_* sections.

Certain config options which enable -ffunction-sections have a custom version of the TEXT_TEXT macro:

.text.[0-9a-zA-Z_]*

That unintentionally matches the thunk sections, causing them to get grouped with normal text rather than being handled by their intended rule later in the script:

*(.text.*_indirect_*)

Fix that by adding another period to the thunk section names, following the kernel's general convention for distinguishing code-generated text sections from compiler-generated ones.

Cc Heiko Carstens <[email protected]>
Cc Vasily Gorbik <[email protected]>
Cc Alexander Gordeev <[email protected]>
---
 arch/s390/include/asm/nospec-insn.h | 2 +-
 arch/s390/kernel/vmlinux.lds.S      | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)
arch/s390/include/asm/nospec-insn.h +1 -1
index cb15dd25bf21..84372c3f313a 100644
@@ -18,7 +18,7 @@
 #ifdef CONFIG_EXPOLINE_EXTERN
 	SYM_CODE_START(\name)
 #else
-	.pushsection .text.\name,"axG",@progbits,\name,comdat
+	.pushsection .text..\name,"axG",@progbits,\name,comdat
 	.globl \name
 	.hidden \name
 	.type \name,@function
arch/s390/kernel/vmlinux.lds.S +1 -1
index ff1ddba96352..fbd6d1f83b67 100644
@@ -51,7 +51,7 @@ SECTIONS
 		IRQENTRY_TEXT
 		SOFTIRQENTRY_TEXT
 		FTRACE_HOTPATCH_TRAMPOLINES_TEXT
-		*(.text.*_indirect_*)
+		*(.text..*_indirect_*)
 		*(.gnu.warning)
 		. = ALIGN(PAGE_SIZE);
 		_etext = .;		/* End of text section */
Signature · 1 line
2.49.0
maintainer Acked-by on v3 1/64
#2 lore raw

Re: [PATCH v3 01/64] s390/vmlinux.lds.S: Prevent thunk functions from getting placed with normal text

Quoted 18 lines · Josh Poimboeuf

The s390 indirect thunks are placed in the .text.__s390_indirect_jump_* sections.

Certain config options which enable -ffunction-sections have a custom version of the TEXT_TEXT macro:

.text.[0-9a-zA-Z_]*

That unintentionally matches the thunk sections, causing them to get grouped with normal text rather than being handled by their intended rule later in the script:

*(.text.*_indirect_*)

Fix that by adding another period to the thunk section names, following the kernel's general convention for distinguishing code-generated text sections from compiler-generated ones.

Quoted 8 lines
Cc Heiko Carstens <[email protected]>
Cc Vasily Gorbik <[email protected]>
Cc Alexander Gordeev <[email protected]>
---
 arch/s390/include/asm/nospec-insn.h | 2 +-
 arch/s390/kernel/vmlinux.lds.S      | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)
author maintainer
#3 lore raw

PATCH v3 2/64 vmlinux.lds: Unify TEXT_MAIN, DATA_MAIN, and related macros

In reply to Josh Poimboeuf

TEXT_MAIN, DATA_MAIN and friends are defined differently depending on whether certain config options enable -ffunction-sections and/or -fdata-sections.

There's no technical reason for that beyond voodoo coding. Keeping the separate implementations adds unnecessary complexity, fragments the logic, and increases the risk of subtle bugs.

Unify the macros by using the same input section patterns across all configs.

This is a prerequisite for the upcoming livepatch klp-build tooling which will manually enable -ffunction-sections and -fdata-sections via KCFLAGS.

Cc Heiko Carstens <[email protected]>
Cc Vasily Gorbik <[email protected]>
Cc Alexander Gordeev <[email protected]>
---
 include/asm-generic/vmlinux.lds.h | 40 ++++++++++---------------------
 scripts/module.lds.S              | 16 ++++---------
 2 files changed, 17 insertions(+), 39 deletions(-)
include/asm-generic/vmlinux.lds.h +12 -28
index fa5f19b8d53a..928e175254f6 100644
@@ -87,39 +87,24 @@
 #define ALIGN_FUNCTION()  . = ALIGN(CONFIG_FUNCTION_ALIGNMENT)
 
 /*
- * LD_DEAD_CODE_DATA_ELIMINATION option enables -fdata-sections, which
- * generates .data.identifier sections, which need to be pulled in with
- * .data. We don't want to pull in .data..other sections, which Linux
- * has defined. Same for text and bss.
+ * Support -ffunction-sections by matching .text and .text.*,
+ * but exclude '.text..*'.
  *
- * With LTO_CLANG, the linker also splits sections by default, so we need
- * these macros to combine the sections during the final link.
- *
- * With AUTOFDO_CLANG and PROPELLER_CLANG, by default, the linker splits
- * text sections and regroups functions into subsections.
- *
- * RODATA_MAIN is not used because existing code already defines .rodata.x
- * sections to be brought in with rodata.
+ * Special .text.* sections that are typically grouped separately, such as
+ * .text.unlikely or .text.hot, must be matched explicitly before using
+ * TEXT_MAIN.
  */
-#if defined(CONFIG_LD_DEAD_CODE_DATA_ELIMINATION) || defined(CONFIG_LTO_CLANG) || \
-defined(CONFIG_AUTOFDO_CLANG) || defined(CONFIG_PROPELLER_CLANG)
 #define TEXT_MAIN .text .text.[0-9a-zA-Z_]*
-#else
-#define TEXT_MAIN .text
-#endif
-#if defined(CONFIG_LD_DEAD_CODE_DATA_ELIMINATION) || defined(CONFIG_LTO_CLANG)
+
+/*
+ * Support -fdata-sections by matching .data, .data.*, and others,
+ * but exclude '.data..*'.
+ */
 #define DATA_MAIN .data .data.[0-9a-zA-Z_]* .data.rel.* .data..L* .data..compoundliteral* .data.$__unnamed_* .data.$L*
 #define SDATA_MAIN .sdata .sdata.[0-9a-zA-Z_]*
 #define RODATA_MAIN .rodata .rodata.[0-9a-zA-Z_]* .rodata..L*
 #define BSS_MAIN .bss .bss.[0-9a-zA-Z_]* .bss..L* .bss..compoundliteral*
 #define SBSS_MAIN .sbss .sbss.[0-9a-zA-Z_]*
-#else
-#define DATA_MAIN .data .data.rel .data.rel.local
-#define SDATA_MAIN .sdata
-#define RODATA_MAIN .rodata
-#define BSS_MAIN .bss
-#define SBSS_MAIN .sbss
-#endif
 
 /*
  * GCC 4.5 and later have a 32 bytes section alignment for structures.
@@ -580,9 +565,8 @@ defined(CONFIG_AUTOFDO_CLANG) || defined(CONFIG_PROPELLER_CLANG)
  * during second ld run in second ld pass when generating System.map
  *
  * TEXT_MAIN here will match symbols with a fixed pattern (for example,
- * .text.hot or .text.unlikely) if dead code elimination or
- * function-section is enabled. Match these symbols first before
- * TEXT_MAIN to ensure they are grouped together.
+ * .text.hot or .text.unlikely).  Match those before TEXT_MAIN to ensure
+ * they get grouped together.
  *
  * Also placing .text.hot section at the beginning of a page, this
  * would help the TLB performance.
scripts/module.lds.S +5 -11
index 450f1088d5fd..0b5ea63d1c67 100644
@@ -38,12 +38,10 @@ SECTIONS {
 	__kcfi_traps 		: { KEEP(*(.kcfi_traps)) }
 #endif
 
-#ifdef CONFIG_LTO_CLANG
-	/*
-	 * 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.
-	 */
+	.text : {
+		*(.text .text.[0-9a-zA-Z_]*)
+	}
+
 	.bss : {
 		*(.bss .bss.[0-9a-zA-Z_]*)
 		*(.bss..L*)
@@ -59,11 +57,7 @@ SECTIONS {
 		*(.rodata .rodata.[0-9a-zA-Z_]*)
 		*(.rodata..L*)
 	}
-#else
-	.data : {
-		MOD_CODETAG_SECTIONS()
-	}
-#endif
+
 	MOD_SEPARATE_CODETAG_SECTIONS()
 }
 
Signature · 1 line
2.49.0
author maintainer
#4 lore raw

PATCH v3 3/64 x86/module: Improve relocation error messages

In reply to Josh Poimboeuf

Add the section number and reloc index to relocation error messages to help find the faulty relocation.

---
 arch/x86/kernel/module.c | 15 +++++++++------
 kernel/livepatch/core.c  |  4 ++--
 2 files changed, 11 insertions(+), 8 deletions(-)
arch/x86/kernel/module.c +9 -6
index 0ffbae902e2f..11c45ce42694 100644
@@ -97,6 +97,7 @@ static int __write_relocate_add(Elf64_Shdr *sechdrs,
 	DEBUGP("%s relocate section %u to %u\n",
 	       apply ? "Applying" : "Clearing",
 	       relsec, sechdrs[relsec].sh_info);
+
 	for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
 		size_t size;
 
@@ -162,15 +163,17 @@ static int __write_relocate_add(Elf64_Shdr *sechdrs,
 
 		if (apply) {
 			if (memcmp(loc, &zero, size)) {
-				pr_err("x86/modules: Invalid relocation target, existing value is nonzero for type %d, loc %p, val %Lx\n",
-				       (int)ELF64_R_TYPE(rel[i].r_info), loc, val);
+				pr_err("x86/modules: Invalid relocation target, existing value is nonzero for sec %u, idx %u, type %d, loc %lx, val %llx\n",
+				       relsec, i, (int)ELF64_R_TYPE(rel[i].r_info),
+				       (unsigned long)loc, val);
 				return -ENOEXEC;
 			}
 			write(loc, &val, size);
 		} else {
 			if (memcmp(loc, &val, size)) {
-				pr_warn("x86/modules: Invalid relocation target, existing value does not match expected value for type %d, loc %p, val %Lx\n",
-					(int)ELF64_R_TYPE(rel[i].r_info), loc, val);
+				pr_warn("x86/modules: Invalid relocation target, existing value does not match expected value for sec %u, idx %u, type %d, loc %lx, val %llx\n",
+					relsec, i, (int)ELF64_R_TYPE(rel[i].r_info),
+					(unsigned long)loc, val);
 				return -ENOEXEC;
 			}
 			write(loc, &zero, size);
@@ -179,8 +182,8 @@ static int __write_relocate_add(Elf64_Shdr *sechdrs,
 	return 0;
 
 overflow:
-	pr_err("overflow in relocation type %d val %Lx\n",
-	       (int)ELF64_R_TYPE(rel[i].r_info), val);
+	pr_err("overflow in relocation type %d val %llx sec %u idx %d\n",
+	       (int)ELF64_R_TYPE(rel[i].r_info), val, relsec, i);
 	pr_err("`%s' likely not compiled with -mcmodel=kernel\n",
 	       me->name);
 	return -ENOEXEC;
kernel/livepatch/core.c +2 -2
index 0e73fac55f8e..7e443c2cf7d4 100644
@@ -217,8 +217,8 @@ static int klp_resolve_symbols(Elf_Shdr *sechdrs, const char *strtab,
 	for (i = 0; i < relasec->sh_size / sizeof(Elf_Rela); i++) {
 		sym = (Elf_Sym *)sechdrs[symndx].sh_addr + ELF_R_SYM(relas[i].r_info);
 		if (sym->st_shndx != SHN_LIVEPATCH) {
-			pr_err("symbol %s is not marked as a livepatch symbol\n",
-			       strtab + sym->st_name);
+			pr_err("symbol %s at rela sec %u idx %d is not marked as a livepatch symbol\n",
+			       strtab + sym->st_name, symndx, i);
 			return -EINVAL;
 		}
 
Signature · 1 line
2.49.0
author maintainer
#5 lore raw

PATCH v3 4/64 x86/kprobes: Remove STACK_FRAME_NON_STANDARD annotation

In reply to Josh Poimboeuf

Since commit 877b145f0f47 ("x86/kprobes: Move trampoline code into RODATA"), the optprobe template code is no longer analyzed by objtool so it doesn't need to be ignored.

---
 arch/x86/kernel/kprobes/opt.c | 4 ----
 1 file changed, 4 deletions(-)
arch/x86/kernel/kprobes/opt.c +0 -4
index 0aabd4c4e2c4..6f826a00eca2 100644
@@ -103,7 +103,6 @@ static void synthesize_set_arg1(kprobe_opcode_t *addr, unsigned long val)
 
 asm (
 			".pushsection .rodata\n"
-			"optprobe_template_func:\n"
 			".global optprobe_template_entry\n"
 			"optprobe_template_entry:\n"
 #ifdef CONFIG_X86_64
@@ -160,9 +159,6 @@ asm (
 			"optprobe_template_end:\n"
 			".popsection\n");
 
-void optprobe_template_func(void);
-STACK_FRAME_NON_STANDARD(optprobe_template_func);
-
 #define TMPL_CLAC_IDX \
 	((long)optprobe_template_clac - (long)optprobe_template_entry)
 #define TMPL_MOVE_IDX \
Signature · 1 line
2.49.0
author maintainer
#6 lore raw

PATCH v3 5/64 compiler: Tweak __UNIQUE_ID() naming

In reply to Josh Poimboeuf

In preparation for the objtool klp diff subcommand, add an underscore between the name and the counter. This will make it possible for objtool to distinguish between the non-unique and unique parts of the symbol name so it can properly correlate the symbols.

---
 include/linux/compiler.h | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)
include/linux/compiler.h +5 -1
index 6f04a1d8c720..4406c1592d78 100644
@@ -163,7 +163,11 @@ void ftrace_likely_update(struct ftrace_likely_data *f, int val,
 	__asm__ ("" : "=r" (var) : "0" (var))
 #endif
 
-#define __UNIQUE_ID(prefix) __PASTE(__PASTE(__UNIQUE_ID_, prefix), __COUNTER__)
+/* Format: __UNIQUE_ID_<name>_<__COUNTER__> */
+#define __UNIQUE_ID(name)					\
+	__PASTE(__UNIQUE_ID_,					\
+	__PASTE(name,						\
+	__PASTE(_, __COUNTER__)))
 
 /**
  * data_race - mark an expression as containing intentional data races
Signature · 1 line
2.49.0
author maintainer
#7 lore raw

PATCH v3 6/64 compiler.h: Make addressable symbols less of an eyesore

In reply to Josh Poimboeuf

Avoid underscore overload by changing:

__UNIQUE_ID___addressable_loops_per_jiffy_868

to the following:

__UNIQUE_ID_addressable_loops_per_jiffy_868

This matches the format used by other __UNIQUE_ID()-generated symbols and improves readability for those who stare at ELF symbol table dumps.

---
 include/linux/compiler.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
include/linux/compiler.h +1 -1
index 4406c1592d78..2039da81cf16 100644
@@ -287,7 +287,7 @@ static inline void *offset_to_ptr(const int *off)
  */
 #define ___ADDRESSABLE(sym, __attrs)						\
 	static void * __used __attrs						\
-	__UNIQUE_ID(__PASTE(__addressable_,sym)) = (void *)(uintptr_t)&sym;
+	__UNIQUE_ID(__PASTE(addressable_, sym)) = (void *)(uintptr_t)&sym;
 
 #define __ADDRESSABLE(sym) \
 	___ADDRESSABLE(sym, __section(".discard.addressable"))
Signature · 1 line
2.49.0
author maintainer
#8 lore raw

PATCH v3 7/64 elfnote: Change ELFNOTE() to use __UNIQUE_ID()

In reply to Josh Poimboeuf

In preparation for the objtool klp diff subcommand, replace the custom unique symbol name generation in ELFNOTE() with __UNIQUE_ID().

This standardizes the naming format for all "unique" symbols, which will allow objtool to properly correlate them. Note this also removes the "one ELF note per line" limitation.

---
 include/linux/elfnote.h | 13 +++++--------
 1 file changed, 5 insertions(+), 8 deletions(-)
include/linux/elfnote.h +5 -8
index 69b136e4dd2b..bb3dcded055f 100644
@@ -60,23 +60,21 @@
 
 #else	/* !__ASSEMBLER__ */
 #include <uapi/linux/elf.h>
+#include <linux/compiler.h>
 /*
  * Use an anonymous structure which matches the shape of
  * Elf{32,64}_Nhdr, but includes the name and desc data.  The size and
  * type of name and desc depend on the macro arguments.  "name" must
- * be a literal string, and "desc" must be passed by value.  You may
- * only define one note per line, since __LINE__ is used to generate
- * unique symbols.
+ * be a literal string, and "desc" must be passed by value.
  */
-#define _ELFNOTE_PASTE(a,b)	a##b
-#define _ELFNOTE(size, name, unique, type, desc)			\
+#define ELFNOTE(size, name, type, desc)					\
 	static const struct {						\
 		struct elf##size##_note _nhdr;				\
 		unsigned char _name[sizeof(name)]			\
 		__attribute__((aligned(sizeof(Elf##size##_Word))));	\
 		typeof(desc) _desc					\
 			     __attribute__((aligned(sizeof(Elf##size##_Word)))); \
-	} _ELFNOTE_PASTE(_note_, unique)				\
+	} __UNIQUE_ID(note)						\
 		__used							\
 		__attribute__((section(".note." name),			\
 			       aligned(sizeof(Elf##size##_Word)),	\
@@ -89,11 +87,10 @@
 		name,							\
 		desc							\
 	}
-#define ELFNOTE(size, name, type, desc)		\
-	_ELFNOTE(size, name, __LINE__, type, desc)
 
 #define ELFNOTE32(name, type, desc) ELFNOTE(32, name, type, desc)
 #define ELFNOTE64(name, type, desc) ELFNOTE(64, name, type, desc)
+
 #endif	/* __ASSEMBLER__ */
 
 #endif /* _LINUX_ELFNOTE_H */
Signature · 1 line
2.49.0
author maintainer
#9 lore raw

PATCH v3 8/64 kbuild: Remove 'kmod_' prefix from __KBUILD_MODNAME

In reply to Josh Poimboeuf

In preparation for the objtool klp diff subcommand, remove the arbitrary 'kmod_' prefix from __KBUILD_MODNAME and instead add it explicitly in the __initcall_id() macro.

This change supports the standardization of "unique" symbol naming by ensuring the non-unique portion of the name comes before the unique part. That will enable objtool to properly correlate symbols across builds.

---
 include/linux/init.h | 3 ++-
 scripts/Makefile.lib | 2 +-
 2 files changed, 3 insertions(+), 2 deletions(-)
include/linux/init.h +2 -1
index ee1309473bc6..553a62f4cff5 100644
@@ -206,12 +206,13 @@ extern struct module __this_module;
 
 /* Format: <modname>__<counter>_<line>_<fn> */
 #define __initcall_id(fn)					\
+	__PASTE(kmod_,						\
 	__PASTE(__KBUILD_MODNAME,				\
 	__PASTE(__,						\
 	__PASTE(__COUNTER__,					\
 	__PASTE(_,						\
 	__PASTE(__LINE__,					\
-	__PASTE(_, fn))))))
+	__PASTE(_, fn)))))))
 
 /* Format: __<prefix>__<iid><id> */
 #define __initcall_name(prefix, __iid, id)			\
scripts/Makefile.lib +1 -1
index 1d581ba5df66..b95560266124 100644
@@ -20,7 +20,7 @@ name-fix-token = $(subst $(comma),_,$(subst -,_,$1))
 name-fix = $(call stringify,$(call name-fix-token,$1))
 basename_flags = -DKBUILD_BASENAME=$(call name-fix,$(basetarget))
 modname_flags  = -DKBUILD_MODNAME=$(call name-fix,$(modname)) \
-		 -D__KBUILD_MODNAME=kmod_$(call name-fix-token,$(modname))
+		 -D__KBUILD_MODNAME=$(call name-fix-token,$(modname))
 modfile_flags  = -DKBUILD_MODFILE=$(call stringify,$(modfile))
 
 _c_flags       = $(filter-out $(CFLAGS_REMOVE_$(target-stem).o), \
Signature · 1 line
2.49.0
author maintainer
#10 lore raw

PATCH v3 9/64 modpost: Ignore unresolved section bounds symbols

In reply to Josh Poimboeuf

In preparation for klp-build livepatch module creation tooling, suppress warnings for unresolved references to linker-generated __start_* and __stop_* section bounds symbols.

These symbols are expected to be undefined when modpost runs, as they're created later by the linker.

---
 scripts/mod/modpost.c | 5 +++++
 1 file changed, 5 insertions(+)
scripts/mod/modpost.c +5 -0
index 5ca7c268294e..c2b2c8fa6d25 100644
@@ -606,6 +606,11 @@ static int ignore_undef_symbol(struct elf_info *info, const char *symname)
 		    strstarts(symname, "_savevr_") ||
 		    strcmp(symname, ".TOC.") == 0)
 			return 1;
+
+	/* ignore linker-created section bounds variables */
+	if (strstarts(symname, "__start_") || strstarts(symname, "__stop_"))
+		return 1;
+
 	/* Do not ignore this symbol */
 	return 0;
 }
Signature · 1 line
2.49.0
author maintainer
#11 lore raw

PATCH v3 10/64 x86/alternative: Refactor INT3 call emulation selftest

In reply to Josh Poimboeuf

The INT3 call emulation selftest is a bit fragile as it relies on the compiler not inserting any extra instructions before the int3_selftest_ip() definition.

Also, the int3_selftest_ip() symbol overlaps with the int3_selftest symbol(), which can confuse objtool.

Fix those issues by slightly reworking the functionality and moving int3_selftest_ip() to a separate asm function. While at it, improve the naming.

---
 arch/x86/kernel/alternative.c | 51 +++++++++++++++++++----------------
 1 file changed, 28 insertions(+), 23 deletions(-)
arch/x86/kernel/alternative.c +28 -23
index ea1d984166cd..bacd6c157626 100644
@@ -2223,21 +2223,34 @@ int alternatives_text_reserved(void *start, void *end)
  * See entry_{32,64}.S for more details.
  */
 
-/*
- * We define the int3_magic() function in assembly to control the calling
- * convention such that we can 'call' it from assembly.
- */
-
-extern void int3_magic(unsigned int *ptr); /* defined in asm */
+extern void int3_selftest_asm(unsigned int *ptr);
 
 asm (
 "	.pushsection	.init.text, \"ax\", @progbits\n"
-"	.type		int3_magic, @function\n"
-"int3_magic:\n"
+"	.type		int3_selftest_asm, @function\n"
+"int3_selftest_asm:\n"
 	ANNOTATE_NOENDBR
-"	movl	$1, (%" _ASM_ARG1 ")\n"
+	/*
+	 * INT3 padded with NOP to CALL_INSN_SIZE. The INT3 triggers an
+	 * exception, then the int3_exception_nb notifier emulates a call to
+	 * int3_selftest_callee().
+	 */
+"	int3; nop; nop; nop; nop\n"
 	ASM_RET
-"	.size		int3_magic, .-int3_magic\n"
+"	.size		int3_selftest_asm, . - int3_selftest_asm\n"
+"	.popsection\n"
+);
+
+extern void int3_selftest_callee(unsigned int *ptr);
+
+asm (
+"	.pushsection	.init.text, \"ax\", @progbits\n"
+"	.type		int3_selftest_callee, @function\n"
+"int3_selftest_callee:\n"
+	ANNOTATE_NOENDBR
+"	movl	$0x1234, (%" _ASM_ARG1 ")\n"
+	ASM_RET
+"	.size		int3_selftest_callee, . - int3_selftest_callee\n"
 "	.popsection\n"
 );
 
@@ -2246,7 +2259,7 @@ extern void int3_selftest_ip(void); /* defined in asm below */
 static int __init
 int3_exception_notify(struct notifier_block *self, unsigned long val, void *data)
 {
-	unsigned long selftest = (unsigned long)&int3_selftest_ip;
+	unsigned long selftest = (unsigned long)&int3_selftest_asm;
 	struct die_args *args = data;
 	struct pt_regs *regs = args->regs;
 
@@ -2261,7 +2274,7 @@ int3_exception_notify(struct notifier_block *self, unsigned long val, void *data
 	if (regs->ip - INT3_INSN_SIZE != selftest)
 		return NOTIFY_DONE;
 
-	int3_emulate_call(regs, (unsigned long)&int3_magic);
+	int3_emulate_call(regs, (unsigned long)&int3_selftest_callee);
 	return NOTIFY_STOP;
 }
 
@@ -2277,19 +2290,11 @@ static noinline void __init int3_selftest(void)
 	BUG_ON(register_die_notifier(&int3_exception_nb));
 
 	/*
-	 * Basically: int3_magic(&val); but really complicated :-)
-	 *
-	 * INT3 padded with NOP to CALL_INSN_SIZE. The int3_exception_nb
-	 * notifier above will emulate CALL for us.
+	 * Basically: int3_selftest_callee(&val); but really complicated :-)
 	 */
-	asm volatile ("int3_selftest_ip:\n\t"
-		      ANNOTATE_NOENDBR
-		      "    int3; nop; nop; nop; nop\n\t"
-		      : ASM_CALL_CONSTRAINT
-		      : __ASM_SEL_RAW(a, D) (&val)
-		      : "memory");
+	int3_selftest_asm(&val);
 
-	BUG_ON(val != 1);
+	BUG_ON(val != 0x1234);
 
 	unregister_die_notifier(&int3_exception_nb);
 }
Signature · 1 line
2.49.0
author maintainer
#12 lore raw

PATCH v3 11/64 objtool: Make find_symbol_containing() less arbitrary

In reply to Josh Poimboeuf

In the rare case of overlapping symbols, find_symbol_containing() just returns the first one it finds. Make it slightly less arbitrary by returning the smallest symbol with size > 0.

---
 tools/objtool/elf.c | 25 ++++++++++++++++++++-----
 1 file changed, 20 insertions(+), 5 deletions(-)
tools/objtool/elf.c +20 -5
index ca5d77db692a..1c1bb2cb960d 100644
@@ -193,14 +193,29 @@ struct symbol *find_func_by_offset(struct section *sec, unsigned long offset)
 struct symbol *find_symbol_containing(const struct section *sec, unsigned long offset)
 {
 	struct rb_root_cached *tree = (struct rb_root_cached *)&sec->symbol_tree;
-	struct symbol *iter;
+	struct symbol *sym = NULL, *tmp;
 
-	__sym_for_each(iter, tree, offset, offset) {
-		if (iter->type != STT_SECTION)
-			return iter;
+	__sym_for_each(tmp, tree, offset, offset) {
+		if (tmp->len) {
+			if (!sym) {
+				sym = tmp;
+				continue;
+			}
+
+			if (sym->offset != tmp->offset || sym->len != tmp->len) {
+				/*
+				 * In the rare case of overlapping symbols,
+				 * pick the smaller one.
+				 *
+				 * TODO: outlaw overlapping symbols
+				 */
+				if (tmp->len < sym->len)
+					sym = tmp;
+			}
+		}
 	}
 
-	return NULL;
+	return sym;
 }
 
 /*
Signature · 1 line
2.49.0
author maintainer
#13 lore raw

PATCH v3 12/64 objtool: Fix broken error handling in read_symbols()

In reply to Josh Poimboeuf

The free(sym) call in the read_symbols() error path is fundamentally
broken: 'sym' doesn't point to any allocated block. If triggered, things would go from bad to worse.

Remove the free() and simplify the error paths. Freeing memory isn't necessary here anyway, these are fatal errors which lead to an immediate exit().

---
 tools/objtool/elf.c | 10 +++-------
 1 file changed, 3 insertions(+), 7 deletions(-)
tools/objtool/elf.c +3 -7
index 1c1bb2cb960d..b009d9feed76 100644
@@ -492,14 +492,14 @@ static int read_symbols(struct elf *elf)
 		if (!gelf_getsymshndx(symtab->data, shndx_data, i, &sym->sym,
 				      &shndx)) {
 			ERROR_ELF("gelf_getsymshndx");
-			goto err;
+			return -1;
 		}
 
 		sym->name = elf_strptr(elf->elf, symtab->sh.sh_link,
 				       sym->sym.st_name);
 		if (!sym->name) {
 			ERROR_ELF("elf_strptr");
-			goto err;
+			return -1;
 		}
 
 		if ((sym->sym.st_shndx > SHN_UNDEF &&
@@ -511,7 +511,7 @@ static int read_symbols(struct elf *elf)
 			sym->sec = find_section_by_index(elf, shndx);
 			if (!sym->sec) {
 				ERROR("couldn't find section for symbol %s", sym->name);
-				goto err;
+				return -1;
 			}
 			if (GELF_ST_TYPE(sym->sym.st_info) == STT_SECTION) {
 				sym->name = sym->sec->name;
@@ -581,10 +581,6 @@ static int read_symbols(struct elf *elf)
 	}
 
 	return 0;
-
-err:
-	free(sym);
-	return -1;
 }
 
 static int mark_group_syms(struct elf *elf)
Signature · 1 line
2.49.0
author maintainer
#14 lore raw

PATCH v3 13/64 objtool: Propagate elf_truncate_section() error in elf_write()

In reply to Josh Poimboeuf

Properly check and propagate the return value of elf_truncate_section() to avoid silent failures.

---
 tools/objtool/elf.c | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)
tools/objtool/elf.c +2 -3
index b009d9feed76..19e249f4783c 100644
@@ -1307,7 +1307,6 @@ static int elf_truncate_section(struct elf *elf, struct section *sec)
 	for (;;) {
 		/* get next data descriptor for the relevant section */
 		data = elf_getdata(s, data);
-
 		if (!data) {
 			if (size) {
 				ERROR("end of section data but non-zero size left\n");
@@ -1343,8 +1342,8 @@ int elf_write(struct elf *elf)
 
 	/* Update changed relocation sections and section headers: */
 	list_for_each_entry(sec, &elf->sections, list) {
-		if (sec->truncate)
-			elf_truncate_section(elf, sec);
+		if (sec->truncate && elf_truncate_section(elf, sec))
+			return -1;
 
 		if (sec_changed(sec)) {
 			s = elf_getscn(elf->elf, sec->idx);
Signature · 1 line
2.49.0
author maintainer
#15 lore raw

PATCH v3 14/64 objtool: Remove error handling boilerplate

In reply to Josh Poimboeuf

Up to a certain point in objtool's execution, all errors are fatal and return -1. When propagating such errors, just return -1 directly instead of trying to propagate the original return code. This helps make the code more compact and the behavior more explicit.

---
 tools/objtool/check.c   | 151 +++++++++++++++-------------------------
 tools/objtool/special.c |   9 +--
 2 files changed, 59 insertions(+), 101 deletions(-)
tools/objtool/check.c +56 -95
index f23bdda737aa..096bb603a67f 100644
@@ -429,7 +429,6 @@ static int decode_instructions(struct objtool_file *file)
 	struct symbol *func;
 	unsigned long offset;
 	struct instruction *insn;
-	int ret;
 
 	for_each_sec(file, sec) {
 		struct instruction *insns = NULL;
@@ -478,11 +477,8 @@ static int decode_instructions(struct objtool_file *file)
 			insn->offset = offset;
 			insn->prev_len = prev_len;
 
-			ret = arch_decode_instruction(file, sec, offset,
-						      sec->sh.sh_size - offset,
-						      insn);
-			if (ret)
-				return ret;
+			if (arch_decode_instruction(file, sec, offset, sec->sh.sh_size - offset, insn))
+				return -1;
 
 			prev_len = insn->len;
 
@@ -598,7 +594,7 @@ static int init_pv_ops(struct objtool_file *file)
 	};
 	const char *pv_ops;
 	struct symbol *sym;
-	int idx, nr, ret;
+	int idx, nr;
 
 	if (!opts.noinstr)
 		return 0;
@@ -620,9 +616,8 @@ static int init_pv_ops(struct objtool_file *file)
 		INIT_LIST_HEAD(&file->pv_ops[idx].targets);
 
 	for (idx = 0; (pv_ops = pv_ops_tables[idx]); idx++) {
-		ret = add_pv_ops(file, pv_ops);
-		if (ret)
-			return ret;
+		if (add_pv_ops(file, pv_ops))
+			return -1;
 	}
 
 	return 0;
@@ -1483,7 +1478,6 @@ static int add_jump_destinations(struct objtool_file *file)
 	struct reloc *reloc;
 	struct section *dest_sec;
 	unsigned long dest_off;
-	int ret;
 
 	for_each_insn(file, insn) {
 		struct symbol *func = insn_func(insn);
@@ -1506,9 +1500,8 @@ static int add_jump_destinations(struct objtool_file *file)
 			dest_sec = reloc->sym->sec;
 			dest_off = arch_dest_reloc_offset(reloc_addend(reloc));
 		} else if (reloc->sym->retpoline_thunk) {
-			ret = add_retpoline_call(file, insn);
-			if (ret)
-				return ret;
+			if (add_retpoline_call(file, insn))
+				return -1;
 			continue;
 		} else if (reloc->sym->return_thunk) {
 			add_return_call(file, insn, true);
@@ -1518,9 +1511,8 @@ static int add_jump_destinations(struct objtool_file *file)
 			 * External sibling call or internal sibling call with
 			 * STT_FUNC reloc.
 			 */
-			ret = add_call_dest(file, insn, reloc->sym, true);
-			if (ret)
-				return ret;
+			if (add_call_dest(file, insn, reloc->sym, true))
+				return -1;
 			continue;
 		} else if (reloc->sym->sec->idx) {
 			dest_sec = reloc->sym->sec;
@@ -1569,9 +1561,8 @@ static int add_jump_destinations(struct objtool_file *file)
 		 */
 		if (jump_dest->sym && jump_dest->offset == jump_dest->sym->offset) {
 			if (jump_dest->sym->retpoline_thunk) {
-				ret = add_retpoline_call(file, insn);
-				if (ret)
-					return ret;
+				if (add_retpoline_call(file, insn))
+					return -1;
 				continue;
 			}
 			if (jump_dest->sym->return_thunk) {
@@ -1612,9 +1603,8 @@ static int add_jump_destinations(struct objtool_file *file)
 			 * Internal sibling call without reloc or with
 			 * STT_SECTION reloc.
 			 */
-			ret = add_call_dest(file, insn, insn_func(jump_dest), true);
-			if (ret)
-				return ret;
+			if (add_call_dest(file, insn, insn_func(jump_dest), true))
+				return -1;
 			continue;
 		}
 
@@ -1644,7 +1634,6 @@ static int add_call_destinations(struct objtool_file *file)
 	unsigned long dest_off;
 	struct symbol *dest;
 	struct reloc *reloc;
-	int ret;
 
 	for_each_insn(file, insn) {
 		struct symbol *func = insn_func(insn);
@@ -1656,9 +1645,8 @@ static int add_call_destinations(struct objtool_file *file)
 			dest_off = arch_jump_destination(insn);
 			dest = find_call_destination(insn->sec, dest_off);
 
-			ret = add_call_dest(file, insn, dest, false);
-			if (ret)
-				return ret;
+			if (add_call_dest(file, insn, dest, false))
+				return -1;
 
 			if (func && func->ignore)
 				continue;
@@ -1682,19 +1670,16 @@ static int add_call_destinations(struct objtool_file *file)
 				return -1;
 			}
 
-			ret = add_call_dest(file, insn, dest, false);
-			if (ret)
-				return ret;
+			if (add_call_dest(file, insn, dest, false))
+				return -1;
 
 		} else if (reloc->sym->retpoline_thunk) {
-			ret = add_retpoline_call(file, insn);
-			if (ret)
-				return ret;
+			if (add_retpoline_call(file, insn))
+				return -1;
 
 		} else {
-			ret = add_call_dest(file, insn, reloc->sym, false);
-			if (ret)
-				return ret;
+			if (add_call_dest(file, insn, reloc->sym, false))
+				return -1;
 		}
 	}
 
@@ -1911,7 +1896,6 @@ static int add_special_section_alts(struct objtool_file *file)
 	struct instruction *orig_insn, *new_insn;
 	struct special_alt *special_alt, *tmp;
 	struct alternative *alt;
-	int ret;
 
 	if (special_get_alts(file->elf, &special_alts))
 		return -1;
@@ -1943,16 +1927,12 @@ static int add_special_section_alts(struct objtool_file *file)
 				continue;
 			}
 
-			ret = handle_group_alt(file, special_alt, orig_insn,
-					       &new_insn);
-			if (ret)
-				return ret;
+			if (handle_group_alt(file, special_alt, orig_insn, &new_insn))
+				return -1;
 
 		} else if (special_alt->jump_or_nop) {
-			ret = handle_jump_alt(file, special_alt, orig_insn,
-					      &new_insn);
-			if (ret)
-				return ret;
+			if (handle_jump_alt(file, special_alt, orig_insn, &new_insn))
+				return -1;
 		}
 
 		alt = calloc(1, sizeof(*alt));
@@ -2140,15 +2120,13 @@ static int add_func_jump_tables(struct objtool_file *file,
 				  struct symbol *func)
 {
 	struct instruction *insn;
-	int ret;
 
 	func_for_each_insn(file, func, insn) {
 		if (!insn_jump_table(insn))
 			continue;
 
-		ret = add_jump_table(file, insn);
-		if (ret)
-			return ret;
+		if (add_jump_table(file, insn))
+			return -1;
 	}
 
 	return 0;
@@ -2162,7 +2140,6 @@ static int add_func_jump_tables(struct objtool_file *file,
 static int add_jump_table_alts(struct objtool_file *file)
 {
 	struct symbol *func;
-	int ret;
 
 	if (!file->rodata)
 		return 0;
@@ -2172,9 +2149,8 @@ static int add_jump_table_alts(struct objtool_file *file)
 			continue;
 
 		mark_func_jump_tables(file, func);
-		ret = add_func_jump_tables(file, func);
-		if (ret)
-			return ret;
+		if (add_func_jump_tables(file, func))
+			return -1;
 	}
 
 	return 0;
@@ -2298,7 +2274,7 @@ static int read_annotate(struct objtool_file *file,
 	struct instruction *insn;
 	struct reloc *reloc;
 	uint64_t offset;
-	int type, ret;
+	int type;
 
 	sec = find_section_by_name(file->elf, ".discard.annotate_insn");
 	if (!sec)
@@ -2327,9 +2303,8 @@ static int read_annotate(struct objtool_file *file,
 			return -1;
 		}
 
-		ret = func(file, type, insn);
-		if (ret < 0)
-			return ret;
+		if (func(file, type, insn))
+			return -1;
 	}
 
 	return 0;
@@ -2527,76 +2502,62 @@ static void mark_rodata(struct objtool_file *file)
 
 static int decode_sections(struct objtool_file *file)
 {
-	int ret;
-
 	mark_rodata(file);
 
-	ret = init_pv_ops(file);
-	if (ret)
-		return ret;
+	if (init_pv_ops(file))
+		return -1;
 
 	/*
 	 * Must be before add_{jump_call}_destination.
 	 */
-	ret = classify_symbols(file);
-	if (ret)
-		return ret;
+	if (classify_symbols(file))
+		return -1;
 
-	ret = decode_instructions(file);
-	if (ret)
-		return ret;
+	if (decode_instructions(file))
+		return -1;
 
-	ret = add_ignores(file);
-	if (ret)
-		return ret;
+	if (add_ignores(file))
+		return -1;
 
 	add_uaccess_safe(file);
 
-	ret = read_annotate(file, __annotate_early);
-	if (ret)
-		return ret;
+	if (read_annotate(file, __annotate_early))
+		return -1;
 
 	/*
 	 * Must be before add_jump_destinations(), which depends on 'func'
 	 * being set for alternatives, to enable proper sibling call detection.
 	 */
 	if (opts.stackval || opts.orc || opts.uaccess || opts.noinstr) {
-		ret = add_special_section_alts(file);
-		if (ret)
-			return ret;
+		if (add_special_section_alts(file))
+			return -1;
 	}
 
-	ret = add_jump_destinations(file);
-	if (ret)
-		return ret;
+	if (add_jump_destinations(file))
+		return -1;
 
 	/*
 	 * Must be before add_call_destination(); it changes INSN_CALL to
 	 * INSN_JUMP.
 	 */
-	ret = read_annotate(file, __annotate_ifc);
-	if (ret)
-		return ret;
+	if (read_annotate(file, __annotate_ifc))
+		return -1;
 
-	ret = add_call_destinations(file);
-	if (ret)
-		return ret;
+	if (add_call_destinations(file))
+		return -1;
 
-	ret = add_jump_table_alts(file);
-	if (ret)
-		return ret;
+	if (add_jump_table_alts(file))
+		return -1;
 
-	ret = read_unwind_hints(file);
-	if (ret)
-		return ret;
+	if (read_unwind_hints(file))
+		return -1;
 
 	/*
 	 * Must be after add_call_destinations() such that it can override
 	 * dead_end_function() marks.
 	 */
-	ret = read_annotate(file, __annotate_late);
-	if (ret)
-		return ret;
+	if (read_annotate(file, __annotate_late))
+		return -1;
 
 	return 0;
 }
tools/objtool/special.c +3 -6
index c80fed8a840e..c0beefb93b62 100644
@@ -133,7 +133,7 @@ int special_get_alts(struct elf *elf, struct list_head *alts)
 	struct section *sec;
 	unsigned int nr_entries;
 	struct special_alt *alt;
-	int idx, ret;
+	int idx;
 
 	INIT_LIST_HEAD(alts);
 
@@ -157,11 +157,8 @@ int special_get_alts(struct elf *elf, struct list_head *alts)
 			}
 			memset(alt, 0, sizeof(*alt));
 
-			ret = get_alt_entry(elf, entry, sec, idx, alt);
-			if (ret > 0)
-				continue;
-			if (ret < 0)
-				return ret;
+			if (get_alt_entry(elf, entry, sec, idx, alt))
+				return -1;
 
 			list_add_tail(&alt->list, alts);
 		}
Signature · 1 line
2.49.0
author maintainer
#16 lore raw

PATCH v3 15/64 objtool: Add empty symbols to the symbol tree again

In reply to Josh Poimboeuf

The following commit

5da6aea375cd ("objtool: Fix find_{symbol,func}_containing()")

fixed the issue where overlapping symbols weren't getting sorted properly in the symbol tree. Therefore the workaround to skip adding empty symbols from the following commit

a2e38dffcd93 ("objtool: Don't add empty symbols to the rbtree")

is no longer needed.

---
 tools/objtool/elf.c | 10 ++--------
 1 file changed, 2 insertions(+), 8 deletions(-)
tools/objtool/elf.c +2 -8
index 19e249f4783c..a8a78b55d3ec 100644
@@ -96,7 +96,8 @@ static inline unsigned long __sym_last(struct symbol *s)
 }
 
 INTERVAL_TREE_DEFINE(struct symbol, node, unsigned long, __subtree_last,
-		     __sym_start, __sym_last, static, __sym)
+		     __sym_start, __sym_last, static inline __maybe_unused,
+		     __sym)
 
 #define __sym_for_each(_iter, _tree, _start, _end)			\
 	for (_iter = __sym_iter_first((_tree), (_start), (_end));	\
@@ -440,13 +441,6 @@ static void elf_add_symbol(struct elf *elf, struct symbol *sym)
 	list_add(&sym->list, entry);
 	elf_hash_add(symbol, &sym->hash, sym->idx);
 	elf_hash_add(symbol_name, &sym->name_hash, str_hash(sym->name));
-
-	/*
-	 * Don't store empty STT_NOTYPE symbols in the rbtree.  They
-	 * can exist within a function, confusing the sorting.
-	 */
-	if (!sym->len)
-		__sym_remove(sym, &sym->sec->symbol_tree);
 }
 
 static int read_symbols(struct elf *elf)
Signature · 1 line
2.49.0
author maintainer
#17 lore raw

PATCH v3 16/64 objtool: Fix interval tree insertion for zero-length symbols

In reply to Josh Poimboeuf

Zero-length symbols get inserted in the wrong spot. Fix that.

---
 tools/objtool/elf.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
tools/objtool/elf.c +1 -1
index a8a78b55d3ec..c024937eb12a 100644
@@ -92,7 +92,7 @@ static inline unsigned long __sym_start(struct symbol *s)
 
 static inline unsigned long __sym_last(struct symbol *s)
 {
-	return s->offset + s->len - 1;
+	return s->offset + (s->len ? s->len - 1 : 0);
 }
 
 INTERVAL_TREE_DEFINE(struct symbol, node, unsigned long, __subtree_last,
Signature · 1 line
2.49.0
author maintainer
#18 lore raw

PATCH v3 17/64 objtool: Fix weak symbol detection

In reply to Josh Poimboeuf

find_symbol_hole_containing() fails to find a symbol hole (aka stripped weak symbol) if its section has no symbols before the hole. This breaks weak symbol detection if -ffunction-sections is enabled.

Fix that by allowing the interval tree to contain section symbols, which are always at offset zero for a given section.

Fixes a bunch of (-ffunction-sections) warnings like:

vmlinux.o: warning: objtool: .text.__x64_sys_io_setup+0x10: unreachable instruction

Fixes 4adb23686795 ("objtool: Ignore extra-symbol code")
---
 tools/include/linux/interval_tree_generic.h | 2 +-
 tools/objtool/elf.c                         | 8 ++++----
 2 files changed, 5 insertions(+), 5 deletions(-)
tools/include/linux/interval_tree_generic.h +1 -1
index aaa8a0767aa3..c0ec9dbdfbaf 100644
@@ -77,7 +77,7 @@ ITSTATIC void ITPREFIX ## _remove(ITSTRUCT *node,			      \
  *   Cond2: start <= ITLAST(node)					      \
  */									      \
 									      \
-static ITSTRUCT *							      \
+ITSTATIC ITSTRUCT *							      \
 ITPREFIX ## _subtree_search(ITSTRUCT *node, ITTYPE start, ITTYPE last)	      \
 {									      \
 	while (true) {							      \
tools/objtool/elf.c +4 -4
index c024937eb12a..d7fb3d0b05cf 100644
@@ -109,7 +109,7 @@ struct symbol_hole {
 };
 
 /*
- * Find !section symbol where @offset is after it.
+ * Find the last symbol before @offset.
  */
 static int symbol_hole_by_offset(const void *key, const struct rb_node *node)
 {
@@ -120,8 +120,7 @@ static int symbol_hole_by_offset(const void *key, const struct rb_node *node)
 		return -1;
 
 	if (sh->key >= s->offset + s->len) {
-		if (s->type != STT_SECTION)
-			sh->sym = s;
+		sh->sym = s;
 		return 1;
 	}
 
@@ -428,7 +427,8 @@ static void elf_add_symbol(struct elf *elf, struct symbol *sym)
 	sym->len = sym->sym.st_size;
 
 	__sym_for_each(iter, &sym->sec->symbol_tree, sym->offset, sym->offset) {
-		if (iter->offset == sym->offset && iter->type == sym->type)
+		if (iter->offset == sym->offset && iter->type == sym->type &&
+		    iter->len == sym->len)
 			iter->alias = sym;
 	}
 
Signature · 1 line
2.49.0
maintainer
#19 lore raw

Re: [PATCH v3 17/64] objtool: Fix weak symbol detection

Quoted 23 lines · Josh Poimboeuf

find_symbol_hole_containing() fails to find a symbol hole (aka stripped weak symbol) if its section has no symbols before the hole. This breaks weak symbol detection if -ffunction-sections is enabled.

Fix that by allowing the interval tree to contain section symbols, which are always at offset zero for a given section.

Fixes a bunch of (-ffunction-sections) warnings like:

vmlinux.o: warning: objtool: .text.__x64_sys_io_setup+0x10: unreachable instruction

Fixes 4adb23686795 ("objtool: Ignore extra-symbol code")
---
 tools/include/linux/interval_tree_generic.h | 2 +-
 tools/objtool/elf.c                         | 8 ++++----
 2 files changed, 5 insertions(+), 5 deletions(-)
tools/include/linux/interval_tree_generic.h +0 -0
index aaa8a0767aa3..c0ec9dbdfbaf 100644

tools/include/linux/interval_tree_generic.h ITSTATIC void ITPREFIX ## _remove(ITSTRUCT *node, \ · line 83

Quoted 8 lines
tools/include/linux/interval_tree_generic.h +1 -1
@@ -77,7 +77,7 @@ ITSTATIC void ITPREFIX ## _remove(ITSTRUCT *node,			      \
  *   Cond2: start <= ITLAST(node)					      \
  */									      \
 									      \
-static ITSTRUCT *							      \
+ITSTATIC ITSTRUCT *							      \
 ITPREFIX ## _subtree_search(ITSTRUCT *node, ITTYPE start, ITTYPE last)	      \
 {									      \
 	while (true) {							      \

IIRC this file is a direct copy from the kernel; this should probably be changed in both?

author maintainer
#20 lore raw

Re: [PATCH v3 17/64] objtool: Fix weak symbol detection

Quoted 19 lines · Peter Zijlstra
Quoted 18 lines · Josh Poimboeuf

find_symbol_hole_containing() fails to find a symbol hole (aka stripped weak symbol) if its section has no symbols before the hole. This breaks weak symbol detection if -ffunction-sections is enabled.

Fix that by allowing the interval tree to contain section symbols, which are always at offset zero for a given section.

Fixes a bunch of (-ffunction-sections) warnings like:

vmlinux.o: warning: objtool: .text.__x64_sys_io_setup+0x10: unreachable instruction

Fixes 4adb23686795 ("objtool: Ignore extra-symbol code")
---
 tools/include/linux/interval_tree_generic.h | 2 +-
 tools/objtool/elf.c                         | 8 ++++----
 2 files changed, 5 insertions(+), 5 deletions(-)
Quoted 16 lines

tools/include/linux/interval_tree_generic.h ITSTATIC void ITPREFIX ## _remove(ITSTRUCT *node, \ · line 83

Quoted 13 lines
tools/include/linux/interval_tree_generic.h +1 -1
index aaa8a0767aa3..c0ec9dbdfbaf 100644
@@ -77,7 +77,7 @@ ITSTATIC void ITPREFIX ## _remove(ITSTRUCT *node,			      \
  *   Cond2: start <= ITLAST(node)					      \
  */									      \
 									      \
-static ITSTRUCT *							      \
+ITSTATIC ITSTRUCT *							      \
 ITPREFIX ## _subtree_search(ITSTRUCT *node, ITTYPE start, ITTYPE last)	      \
 {									      \
 	while (true) {							      \

IIRC this file is a direct copy from the kernel; this should probably be changed in both?

Ok. We should probably enforce that with the sync-check script.

Signature · 1 line
Josh
author maintainer
#21 lore raw

PATCH v3 18/64 objtool: Fix x86 addend calculation

In reply to Josh Poimboeuf

On x86, arch_dest_reloc_offset() hardcodes the addend adjustment to four, but the actual adjustment depends on the relocation type. Fix that.

---
 tools/objtool/arch/loongarch/decode.c |  4 ++--
 tools/objtool/arch/powerpc/decode.c   |  4 ++--
 tools/objtool/arch/x86/decode.c       |  9 +++++++--
 tools/objtool/check.c                 | 15 +++++----------
 tools/objtool/include/objtool/arch.h  |  2 +-
 5 files changed, 17 insertions(+), 17 deletions(-)
tools/objtool/arch/loongarch/decode.c +2 -2
index b6fdc68053cc..330671d88c59 100644
@@ -17,9 +17,9 @@ unsigned long arch_jump_destination(struct instruction *insn)
 	return insn->offset + (insn->immediate << 2);
 }
 
-unsigned long arch_dest_reloc_offset(int addend)
+s64 arch_insn_adjusted_addend(struct instruction *insn, struct reloc *reloc)
 {
-	return addend;
+	return reloc_addend(reloc);
 }
 
 bool arch_pc_relative_reloc(struct reloc *reloc)
tools/objtool/arch/powerpc/decode.c +2 -2
index c851c51d4bd3..9b17885e6cba 100644
@@ -14,9 +14,9 @@ int arch_ftrace_match(char *name)
 	return !strcmp(name, "_mcount");
 }
 
-unsigned long arch_dest_reloc_offset(int addend)
+s64 arch_insn_adjusted_addend(struct instruction *insn, struct reloc *reloc)
 {
-	return addend;
+	return reloc_addend(reloc);
 }
 
 bool arch_callee_saved_reg(unsigned char reg)
tools/objtool/arch/x86/decode.c +7 -2
index 98c4713c1b09..f29ab0f3d4a7 100644
@@ -68,9 +68,14 @@ bool arch_callee_saved_reg(unsigned char reg)
 	}
 }
 
-unsigned long arch_dest_reloc_offset(int addend)
+s64 arch_insn_adjusted_addend(struct instruction *insn, struct reloc *reloc)
 {
-	return addend + 4;
+	s64 addend = reloc_addend(reloc);
+
+	if (arch_pc_relative_reloc(reloc))
+		addend += insn->offset + insn->len - reloc_offset(reloc);
+
+	return addend;
 }
 
 unsigned long arch_jump_destination(struct instruction *insn)
tools/objtool/check.c +5 -10
index 096bb603a67f..fd93cae8b1b9 100644
@@ -1498,7 +1498,7 @@ static int add_jump_destinations(struct objtool_file *file)
 			dest_off = arch_jump_destination(insn);
 		} else if (reloc->sym->type == STT_SECTION) {
 			dest_sec = reloc->sym->sec;
-			dest_off = arch_dest_reloc_offset(reloc_addend(reloc));
+			dest_off = arch_insn_adjusted_addend(insn, reloc);
 		} else if (reloc->sym->retpoline_thunk) {
 			if (add_retpoline_call(file, insn))
 				return -1;
@@ -1517,7 +1517,7 @@ static int add_jump_destinations(struct objtool_file *file)
 		} else if (reloc->sym->sec->idx) {
 			dest_sec = reloc->sym->sec;
 			dest_off = reloc->sym->sym.st_value +
-				   arch_dest_reloc_offset(reloc_addend(reloc));
+				   arch_insn_adjusted_addend(insn, reloc);
 		} else {
 			/* non-func asm code jumping to another file */
 			continue;
@@ -1662,7 +1662,7 @@ static int add_call_destinations(struct objtool_file *file)
 			}
 
 		} else if (reloc->sym->type == STT_SECTION) {
-			dest_off = arch_dest_reloc_offset(reloc_addend(reloc));
+			dest_off = arch_insn_adjusted_addend(insn, reloc);
 			dest = find_call_destination(reloc->sym->sec, dest_off);
 			if (!dest) {
 				ERROR_INSN(insn, "can't find call dest symbol at %s+0x%lx",
@@ -3311,7 +3311,7 @@ static bool pv_call_dest(struct objtool_file *file, struct instruction *insn)
 	if (!reloc || strcmp(reloc->sym->name, "pv_ops"))
 		return false;
 
-	idx = (arch_dest_reloc_offset(reloc_addend(reloc)) / sizeof(void *));
+	idx = arch_insn_adjusted_addend(insn, reloc) / sizeof(void *);
 
 	if (file->pv_ops[idx].clean)
 		return true;
@@ -4359,12 +4359,7 @@ static int validate_ibt_insn(struct objtool_file *file, struct instruction *insn
 					      reloc_offset(reloc) + 1,
 					      (insn->offset + insn->len) - (reloc_offset(reloc) + 1))) {
 
-		off = reloc->sym->offset;
-		if (reloc_type(reloc) == R_X86_64_PC32 ||
-		    reloc_type(reloc) == R_X86_64_PLT32)
-			off += arch_dest_reloc_offset(reloc_addend(reloc));
-		else
-			off += reloc_addend(reloc);
+		off = reloc->sym->offset + arch_insn_adjusted_addend(insn, reloc);
 
 		dest = find_insn(file, reloc->sym->sec, off);
 		if (!dest)
tools/objtool/include/objtool/arch.h +1 -1
index 01ef6f415adf..cd1776c35b13 100644
@@ -83,7 +83,7 @@ bool arch_callee_saved_reg(unsigned char reg);
 
 unsigned long arch_jump_destination(struct instruction *insn);
 
-unsigned long arch_dest_reloc_offset(int addend);
+s64 arch_insn_adjusted_addend(struct instruction *insn, struct reloc *reloc);
 
 const char *arch_nop_insn(int len);
 const char *arch_ret_insn(int len);
Signature · 1 line
2.49.0
author maintainer
#22 lore raw

PATCH v3 19/64 objtool: Fix __pa_symbol() relocation handling

In reply to Josh Poimboeuf

__pa_symbol() generates a relocation which refers to a physical address. Convert it to back its virtual form before calculating the addend.

---
 tools/objtool/arch/x86/decode.c | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)
tools/objtool/arch/x86/decode.c +12 -1
index f29ab0f3d4a7..b10cfa9cd71e 100644
@@ -68,6 +68,17 @@ bool arch_callee_saved_reg(unsigned char reg)
 	}
 }
 
+/* Undo the effects of __pa_symbol() if necessary */
+static unsigned long phys_to_virt(unsigned long pa)
+{
+	s64 va = pa;
+
+	if (va > 0)
+		va &= ~(0x80000000);
+
+	return va;
+}
+
 s64 arch_insn_adjusted_addend(struct instruction *insn, struct reloc *reloc)
 {
 	s64 addend = reloc_addend(reloc);
@@ -75,7 +86,7 @@ s64 arch_insn_adjusted_addend(struct instruction *insn, struct reloc *reloc)
 	if (arch_pc_relative_reloc(reloc))
 		addend += insn->offset + insn->len - reloc_offset(reloc);
 
-	return addend;
+	return phys_to_virt(addend);
 }
 
 unsigned long arch_jump_destination(struct instruction *insn)
Signature · 1 line
2.49.0
author maintainer
#23 lore raw

PATCH v3 20/64 objtool: Fix "unexpected end of section" warning for alternatives

In reply to Josh Poimboeuf

Due to the short circuiting logic in next_insn_to_validate(), control flow may silently transition from .altinstr_replacement to .text without a corresponding nested call to validate_branch().

As a result the validate_branch() 'sec' variable doesn't get reinitialized, which can trigger a confusing "unexpected end of section" warning which blames .altinstr_replacement rather than the offending fallthrough function.

Fix that by not caching the section. There's no point in doing that anyway.

---
 tools/objtool/check.c | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)
tools/objtool/check.c +1 -4
index fd93cae8b1b9..b5257a959458 100644
@@ -3508,15 +3508,12 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
 {
 	struct alternative *alt;
 	struct instruction *next_insn, *prev_insn = NULL;
-	struct section *sec;
 	u8 visited;
 	int ret;
 
 	if (func && func->ignore)
 		return 0;
 
-	sec = insn->sec;
-
 	while (1) {
 		next_insn = next_insn_to_validate(file, insn);
 
@@ -3754,7 +3751,7 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
 
 			WARN("%s%sunexpected end of section %s",
 			     func ? func->name : "", func ? "(): " : "",
-			     sec->name);
+			     insn->sec->name);
 			return 1;
 		}
 
Signature · 1 line
2.49.0
author maintainer
#24 lore raw

PATCH v3 21/64 objtool: Check for missing annotation entries in read_annotate()

In reply to Josh Poimboeuf

Add a sanity check to make sure none of the relocations for the .discard.annotate_insn section have gone missing.

---
 tools/objtool/check.c | 5 +++++
 1 file changed, 5 insertions(+)
tools/objtool/check.c +5 -0
index b5257a959458..fe18f9f5dbef 100644
@@ -2292,6 +2292,11 @@ static int read_annotate(struct objtool_file *file,
 		sec->sh.sh_entsize = 8;
 	}
 
+	if (sec_num_entries(sec) != sec_num_entries(sec->rsec)) {
+		ERROR("bad .discard.annotate_insn section: missing relocs");
+		return -1;
+	}
+
 	for_each_reloc(sec->rsec, reloc) {
 		type = *(u32 *)(sec->data->d_buf + (reloc_idx(reloc) * sec->sh.sh_entsize) + 4);
 
Signature · 1 line
2.49.0
author maintainer
#25 lore raw

PATCH v3 22/64 objtool: Const string cleanup

In reply to Josh Poimboeuf

Use 'const char *' where applicable.

---
 tools/objtool/arch/loongarch/decode.c | 2 +-
 tools/objtool/arch/powerpc/decode.c   | 2 +-
 tools/objtool/arch/x86/decode.c       | 2 +-
 tools/objtool/elf.c                   | 6 +++---
 tools/objtool/include/objtool/arch.h  | 2 +-
 tools/objtool/include/objtool/elf.h   | 6 +++---
 6 files changed, 10 insertions(+), 10 deletions(-)
tools/objtool/arch/loongarch/decode.c +1 -1
index 330671d88c59..7b38718d782a 100644
@@ -7,7 +7,7 @@
 #include <linux/objtool_types.h>
 #include <arch/elf.h>
 
-int arch_ftrace_match(char *name)
+int arch_ftrace_match(const char *name)
 {
 	return !strcmp(name, "_mcount");
 }
tools/objtool/arch/powerpc/decode.c +1 -1
index 9b17885e6cba..d4cb02120a6b 100644
@@ -9,7 +9,7 @@
 #include <objtool/builtin.h>
 #include <objtool/endianness.h>
 
-int arch_ftrace_match(char *name)
+int arch_ftrace_match(const char *name)
 {
 	return !strcmp(name, "_mcount");
 }
tools/objtool/arch/x86/decode.c +1 -1
index b10cfa9cd71e..36a65cecada3 100644
@@ -23,7 +23,7 @@
 #include <objtool/builtin.h>
 #include <arch/elf.h>
 
-int arch_ftrace_match(char *name)
+int arch_ftrace_match(const char *name)
 {
 	return !strcmp(name, "__fentry__");
 }
tools/objtool/elf.c +3 -3
index d7fb3d0b05cf..2ea6d591c3c2 100644
@@ -853,7 +853,7 @@ elf_create_section_symbol(struct elf *elf, struct section *sec)
 	return sym;
 }
 
-static int elf_add_string(struct elf *elf, struct section *strtab, char *str);
+static int elf_add_string(struct elf *elf, struct section *strtab, const char *str);
 
 struct symbol *
 elf_create_prefix_symbol(struct elf *elf, struct symbol *orig, long size)
@@ -1086,7 +1086,7 @@ struct elf *elf_open_read(const char *name, int flags)
 	return NULL;
 }
 
-static int elf_add_string(struct elf *elf, struct section *strtab, char *str)
+static int elf_add_string(struct elf *elf, struct section *strtab, const char *str)
 {
 	Elf_Data *data;
 	Elf_Scn *s;
@@ -1111,7 +1111,7 @@ static int elf_add_string(struct elf *elf, struct section *strtab, char *str)
 		return -1;
 	}
 
-	data->d_buf = str;
+	data->d_buf = strdup(str);
 	data->d_size = strlen(str) + 1;
 	data->d_align = 1;
 
tools/objtool/include/objtool/arch.h +1 -1
index cd1776c35b13..07729a240159 100644
@@ -71,7 +71,7 @@ struct stack_op {
 
 struct instruction;
 
-int arch_ftrace_match(char *name);
+int arch_ftrace_match(const char *name);
 
 void arch_initial_func_cfi_state(struct cfi_init_state *state);
 
tools/objtool/include/objtool/elf.h +3 -3
index 0a2fa3ac0079..0f9adfe8e852 100644
@@ -40,7 +40,7 @@ struct section {
 	struct section *base, *rsec;
 	struct symbol *sym;
 	Elf_Data *data;
-	char *name;
+	const char *name;
 	int idx;
 	bool _changed, text, rodata, noinstr, init, truncate;
 	struct reloc *relocs;
@@ -53,7 +53,7 @@ struct symbol {
 	struct elf_hash_node name_hash;
 	GElf_Sym sym;
 	struct section *sec;
-	char *name;
+	const char *name;
 	unsigned int idx, len;
 	unsigned long offset;
 	unsigned long __subtree_last;
@@ -87,7 +87,7 @@ struct elf {
 	GElf_Ehdr ehdr;
 	int fd;
 	bool changed;
-	char *name;
+	const char *name;
 	unsigned int num_files;
 	struct list_head sections;
 	unsigned long num_relocs;
Signature · 1 line
2.49.0
author maintainer
#26 lore raw

PATCH v3 23/64 objtool: Clean up compiler flag usage

In reply to Josh Poimboeuf

KBUILD_HOSTCFLAGS and KBUILD_HOSTLDFLAGS aren't defined when objtool is built standalone. Also, the EXTRA_WARNINGS flags are rather arbitrary.

Make things simpler and more consistent by specifying compiler flags explicitly and tweaking the warnings. Also make a few code tweaks to make the new warnings happy.

---
 tools/objtool/Makefile | 15 ++++++++++-----
 tools/objtool/check.c  |  4 ++--
 tools/objtool/elf.c    |  2 +-
 3 files changed, 13 insertions(+), 8 deletions(-)
tools/objtool/Makefile +10 -5
index 8c20361dd100..fc82d47f2b9a 100644
@@ -23,6 +23,11 @@ LIBELF_LIBS  := $(shell $(HOSTPKG_CONFIG) libelf --libs 2>/dev/null || echo -lel
 
 all: $(OBJTOOL)
 
+WARNINGS := -Werror -Wall -Wextra -Wmissing-prototypes			\
+	    -Wmissing-declarations -Wwrite-strings			\
+	    -Wno-implicit-fallthrough -Wno-sign-compare			\
+	    -Wno-unused-parameter
+
 INCLUDES := -I$(srctree)/tools/include \
 	    -I$(srctree)/tools/include/uapi \
 	    -I$(srctree)/tools/arch/$(HOSTARCH)/include/uapi \
@@ -30,11 +35,11 @@ INCLUDES := -I$(srctree)/tools/include \
 	    -I$(srctree)/tools/objtool/include \
 	    -I$(srctree)/tools/objtool/arch/$(SRCARCH)/include \
 	    -I$(LIBSUBCMD_OUTPUT)/include
-# Note, EXTRA_WARNINGS here was determined for CC and not HOSTCC, it
-# is passed here to match a legacy behavior.
-WARNINGS := $(EXTRA_WARNINGS) -Wno-switch-default -Wno-switch-enum -Wno-packed -Wno-nested-externs
-OBJTOOL_CFLAGS := -Werror $(WARNINGS) $(KBUILD_HOSTCFLAGS) -g $(INCLUDES) $(LIBELF_FLAGS)
-OBJTOOL_LDFLAGS := $(LIBELF_LIBS) $(LIBSUBCMD) $(KBUILD_HOSTLDFLAGS)
+
+OBJTOOL_CFLAGS  := -std=gnu11 -fomit-frame-pointer -O2 -g \
+		   $(WARNINGS) $(INCLUDES) $(LIBELF_FLAGS) $(HOSTCFLAGS)
+
+OBJTOOL_LDFLAGS := $(LIBSUBCMD) $(LIBELF_LIBS) $(HOSTLDFLAGS)
 
 # Allow old libelf to be used:
 elfshdr := $(shell echo '$(pound)include <libelf.h>' | $(HOSTCC) $(OBJTOOL_CFLAGS) -x c -E - 2>/dev/null | grep elf_getshdr)
tools/objtool/check.c +2 -2
index fe18f9f5dbef..4e779bf8fcae 100644
@@ -460,7 +460,7 @@ static int decode_instructions(struct objtool_file *file)
 
 		for (offset = 0; offset < sec->sh.sh_size; offset += insn->len) {
 			if (!insns || idx == INSN_CHUNK_MAX) {
-				insns = calloc(sizeof(*insn), INSN_CHUNK_SIZE);
+				insns = calloc(INSN_CHUNK_SIZE, sizeof(*insn));
 				if (!insns) {
 					ERROR_GLIBC("calloc");
 					return -1;
@@ -606,7 +606,7 @@ static int init_pv_ops(struct objtool_file *file)
 		return 0;
 
 	nr = sym->len / sizeof(unsigned long);
-	file->pv_ops = calloc(sizeof(struct pv_state), nr);
+	file->pv_ops = calloc(nr, sizeof(struct pv_state));
 	if (!file->pv_ops) {
 		ERROR_GLIBC("calloc");
 		return -1;
tools/objtool/elf.c +1 -1
index 2ea6d591c3c2..c27edeed2dd0 100644
@@ -736,7 +736,7 @@ static int elf_update_symbol(struct elf *elf, struct section *symtab,
 	}
 
 	/* setup extended section index magic and write the symbol */
-	if ((shndx >= SHN_UNDEF && shndx < SHN_LORESERVE) || is_special_shndx) {
+	if (shndx < SHN_LORESERVE || is_special_shndx) {
 		sym->sym.st_shndx = shndx;
 		if (!shndx_data)
 			shndx = 0;
Signature · 1 line
2.49.0
author maintainer
#27 lore raw

PATCH v3 24/64 objtool: Remove .parainstructions reference

In reply to Josh Poimboeuf

The .parainstructions section no longer exists since the following commit:

60bc276b129e ("x86/paravirt: Switch mixed paravirt/alternative calls to alternatives").

Remove the reference to it.

---
 tools/objtool/check.c | 1 -
 1 file changed, 1 deletion(-)
tools/objtool/check.c +0 -1
index 4e779bf8fcae..61e44b927b99 100644
@@ -4431,7 +4431,6 @@ static int validate_ibt(struct objtool_file *file)
 		    !strcmp(sec->name, ".altinstructions")		||
 		    !strcmp(sec->name, ".ibt_endbr_seal")		||
 		    !strcmp(sec->name, ".orc_unwind_ip")		||
-		    !strcmp(sec->name, ".parainstructions")		||
 		    !strcmp(sec->name, ".retpoline_sites")		||
 		    !strcmp(sec->name, ".smp_locks")			||
 		    !strcmp(sec->name, ".static_call_sites")		||
Signature · 1 line
2.49.0
author maintainer
#28 lore raw

PATCH v3 25/64 objtool: Convert elf iterator macros to use 'struct elf'

In reply to Josh Poimboeuf

'struct objtool_file' is specific to the check code and doesn't belong in the elf code which is supposed to be objtool_file-agnostic. Convert the elf iterator macros to use 'struct elf' instead.

---
 tools/objtool/check.c               | 22 +++++++++++-----------
 tools/objtool/include/objtool/elf.h |  8 ++++----
 tools/objtool/orc_gen.c             |  2 +-
 3 files changed, 16 insertions(+), 16 deletions(-)
tools/objtool/check.c +11 -11
index 61e44b927b99..ec11fd29398b 100644
@@ -106,7 +106,7 @@ static struct instruction *prev_insn_same_sym(struct objtool_file *file,
 #define for_each_insn(file, insn)					\
 	for (struct section *__sec, *__fake = (struct section *)1;	\
 	     __fake; __fake = NULL)					\
-		for_each_sec(file, __sec)				\
+		for_each_sec(file->elf, __sec)				\
 			sec_for_each_insn(file, __sec, insn)
 
 #define func_for_each_insn(file, func, insn)				\
@@ -430,7 +430,7 @@ static int decode_instructions(struct objtool_file *file)
 	unsigned long offset;
 	struct instruction *insn;
 
-	for_each_sec(file, sec) {
+	for_each_sec(file->elf, sec) {
 		struct instruction *insns = NULL;
 		u8 prev_len = 0;
 		u8 idx = 0;
@@ -856,7 +856,7 @@ static int create_cfi_sections(struct objtool_file *file)
 	}
 
 	idx = 0;
-	for_each_sym(file, sym) {
+	for_each_sym(file->elf, sym) {
 		if (sym->type != STT_FUNC)
 			continue;
 
@@ -872,7 +872,7 @@ static int create_cfi_sections(struct objtool_file *file)
 		return -1;
 
 	idx = 0;
-	for_each_sym(file, sym) {
+	for_each_sym(file->elf, sym) {
 		if (sym->type != STT_FUNC)
 			continue;
 
@@ -2144,7 +2144,7 @@ static int add_jump_table_alts(struct objtool_file *file)
 	if (!file->rodata)
 		return 0;
 
-	for_each_sym(file, func) {
+	for_each_sym(file->elf, func) {
 		if (func->type != STT_FUNC)
 			continue;
 
@@ -2448,7 +2448,7 @@ static int classify_symbols(struct objtool_file *file)
 {
 	struct symbol *func;
 
-	for_each_sym(file, func) {
+	for_each_sym(file->elf, func) {
 		if (func->type == STT_NOTYPE && strstarts(func->name, ".L"))
 			func->local_label = true;
 
@@ -2493,7 +2493,7 @@ static void mark_rodata(struct objtool_file *file)
 	 *
 	 * .rodata.str1.* sections are ignored; they don't contain jump tables.
 	 */
-	for_each_sec(file, sec) {
+	for_each_sec(file->elf, sec) {
 		if ((!strncmp(sec->name, ".rodata", 7) &&
 		     !strstr(sec->name, ".str1.")) ||
 		    !strncmp(sec->name, ".data.rel.ro", 12)) {
@@ -4141,7 +4141,7 @@ static int add_prefix_symbols(struct objtool_file *file)
 	struct section *sec;
 	struct symbol *func;
 
-	for_each_sec(file, sec) {
+	for_each_sec(file->elf, sec) {
 		if (!(sec->sh.sh_flags & SHF_EXECINSTR))
 			continue;
 
@@ -4233,7 +4233,7 @@ static int validate_functions(struct objtool_file *file)
 	struct section *sec;
 	int warnings = 0;
 
-	for_each_sec(file, sec) {
+	for_each_sec(file->elf, sec) {
 		if (!(sec->sh.sh_flags & SHF_EXECINSTR))
 			continue;
 
@@ -4412,7 +4412,7 @@ static int validate_ibt(struct objtool_file *file)
 	for_each_insn(file, insn)
 		warnings += validate_ibt_insn(file, insn);
 
-	for_each_sec(file, sec) {
+	for_each_sec(file->elf, sec) {
 
 		/* Already done by validate_ibt_insn() */
 		if (sec->sh.sh_flags & SHF_EXECINSTR)
@@ -4573,7 +4573,7 @@ static void disas_warned_funcs(struct objtool_file *file)
 	struct symbol *sym;
 	char *funcs = NULL, *tmp;
 
-	for_each_sym(file, sym) {
+	for_each_sym(file->elf, sym) {
 		if (sym->warned) {
 			if (!funcs) {
 				funcs = malloc(strlen(sym->name) + 1);
tools/objtool/include/objtool/elf.h +4 -4
index 0f9adfe8e852..fcea9338c687 100644
@@ -324,16 +324,16 @@ static inline void set_sym_next_reloc(struct reloc *reloc, struct reloc *next)
 	reloc->_sym_next_reloc = (unsigned long)next | bit;
 }
 
-#define for_each_sec(file, sec)						\
-	list_for_each_entry(sec, &file->elf->sections, list)
+#define for_each_sec(elf, sec)						\
+	list_for_each_entry(sec, &elf->sections, list)
 
 #define sec_for_each_sym(sec, sym)					\
 	list_for_each_entry(sym, &sec->symbol_list, list)
 
-#define for_each_sym(file, sym)						\
+#define for_each_sym(elf, sym)						\
 	for (struct section *__sec, *__fake = (struct section *)1;	\
 	     __fake; __fake = NULL)					\
-		for_each_sec(file, __sec)				\
+		for_each_sec(elf, __sec)				\
 			sec_for_each_sym(__sec, sym)
 
 #define for_each_reloc(rsec, reloc)					\
tools/objtool/orc_gen.c +1 -1
index 922e6aac7cea..6eff3d6a125c 100644
@@ -57,7 +57,7 @@ int orc_create(struct objtool_file *file)
 
 	/* Build a deduplicated list of ORC entries: */
 	INIT_LIST_HEAD(&orc_list);
-	for_each_sec(file, sec) {
+	for_each_sec(file->elf, sec) {
 		struct orc_entry orc, prev_orc = {0};
 		struct instruction *insn;
 		bool empty = true;
Signature · 1 line
2.49.0
author maintainer
#29 lore raw

PATCH v3 26/64 objtool: Add section/symbol type helpers

In reply to Josh Poimboeuf

Add some helper macros to improve readability.

---
 tools/objtool/arch/x86/special.c    |  2 +-
 tools/objtool/check.c               | 58 ++++++++++++-------------
 tools/objtool/elf.c                 | 20 ++++-----
 tools/objtool/include/objtool/elf.h | 66 +++++++++++++++++++++++++++++
 tools/objtool/special.c             |  4 +-
 5 files changed, 108 insertions(+), 42 deletions(-)
tools/objtool/arch/x86/special.c +1 -1
index 06ca4a2659a4..09300761f108 100644
@@ -89,7 +89,7 @@ struct reloc *arch_find_switch_table(struct objtool_file *file,
 	/* look for a relocation which references .rodata */
 	text_reloc = find_reloc_by_dest_range(file->elf, insn->sec,
 					      insn->offset, insn->len);
-	if (!text_reloc || text_reloc->sym->type != STT_SECTION ||
+	if (!text_reloc || !is_sec_sym(text_reloc->sym) ||
 	    !text_reloc->sym->sec->rodata)
 		return NULL;
 
tools/objtool/check.c +29 -29

214 lines not on this page · raw message

tools/objtool/elf.c +10 -10
index c27edeed2dd0..d36c0d42fd7b 100644
@@ -170,7 +170,7 @@ struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset)
 	struct symbol *iter;
 
 	__sym_for_each(iter, tree, offset, offset) {
-		if (iter->offset == offset && iter->type != STT_SECTION)
+		if (iter->offset == offset && !is_sec_sym(iter))
 			return iter;
 	}
 
@@ -183,7 +183,7 @@ struct symbol *find_func_by_offset(struct section *sec, unsigned long offset)
 	struct symbol *iter;
 
 	__sym_for_each(iter, tree, offset, offset) {
-		if (iter->offset == offset && iter->type == STT_FUNC)
+		if (iter->offset == offset && is_func_sym(iter))
 			return iter;
 	}
 
@@ -264,7 +264,7 @@ struct symbol *find_func_containing(struct section *sec, unsigned long offset)
 	struct symbol *iter;
 
 	__sym_for_each(iter, tree, offset, offset) {
-		if (iter->type == STT_FUNC)
+		if (is_func_sym(iter))
 			return iter;
 	}
 
@@ -373,14 +373,14 @@ static int read_sections(struct elf *elf)
 			return -1;
 		}
 
-		if (sec->sh.sh_size != 0 && !is_dwarf_section(sec)) {
+		if (sec_size(sec) != 0 && !is_dwarf_section(sec)) {
 			sec->data = elf_getdata(s, NULL);
 			if (!sec->data) {
 				ERROR_ELF("elf_getdata");
 				return -1;
 			}
 			if (sec->data->d_off != 0 ||
-			    sec->data->d_size != sec->sh.sh_size) {
+			    sec->data->d_size != sec_size(sec)) {
 				ERROR("unexpected data attributes for %s", sec->name);
 				return -1;
 			}
@@ -420,7 +420,7 @@ static void elf_add_symbol(struct elf *elf, struct symbol *sym)
 	sym->type = GELF_ST_TYPE(sym->sym.st_info);
 	sym->bind = GELF_ST_BIND(sym->sym.st_info);
 
-	if (sym->type == STT_FILE)
+	if (is_file_sym(sym))
 		elf->num_files++;
 
 	sym->offset = sym->sym.st_value;
@@ -527,7 +527,7 @@ static int read_symbols(struct elf *elf)
 		sec_for_each_sym(sec, sym) {
 			char *pname;
 			size_t pnamelen;
-			if (sym->type != STT_FUNC)
+			if (!is_func_sym(sym))
 				continue;
 
 			if (sym->pfunc == NULL)
@@ -929,7 +929,7 @@ struct reloc *elf_init_reloc_text_sym(struct elf *elf, struct section *sec,
 	struct symbol *sym = insn_sec->sym;
 	int addend = insn_off;
 
-	if (!(insn_sec->sh.sh_flags & SHF_EXECINSTR)) {
+	if (!is_text_sec(insn_sec)) {
 		ERROR("bad call to %s() for data symbol %s", __func__, sym->name);
 		return NULL;
 	}
@@ -958,7 +958,7 @@ struct reloc *elf_init_reloc_data_sym(struct elf *elf, struct section *sec,
 				      struct symbol *sym,
 				      s64 addend)
 {
-	if (sym->sec && (sec->sh.sh_flags & SHF_EXECINSTR)) {
+	if (is_text_sec(sec)) {
 		ERROR("bad call to %s() for text symbol %s", __func__, sym->name);
 		return NULL;
 	}
@@ -1287,7 +1287,7 @@ int elf_write_insn(struct elf *elf, struct section *sec,
  */
 static int elf_truncate_section(struct elf *elf, struct section *sec)
 {
-	u64 size = sec->sh.sh_size;
+	u64 size = sec_size(sec);
 	bool truncated = false;
 	Elf_Data *data = NULL;
 	Elf_Scn *s;
tools/objtool/include/objtool/elf.h +66 -0

89 lines not on this page · raw message

tools/objtool/special.c +2 -2
index c0beefb93b62..fc2cf8dba1c0 100644
@@ -142,12 +142,12 @@ int special_get_alts(struct elf *elf, struct list_head *alts)
 		if (!sec)
 			continue;
 
-		if (sec->sh.sh_size % entry->size != 0) {
+		if (sec_size(sec) % entry->size != 0) {
 			ERROR("%s size not a multiple of %d", sec->name, entry->size);
 			return -1;
 		}
 
-		nr_entries = sec->sh.sh_size / entry->size;
+		nr_entries = sec_size(sec) / entry->size;
 
 		for (idx = 0; idx < nr_entries; idx++) {
 			alt = malloc(sizeof(*alt));
Signature · 1 line
2.49.0
maintainer
#30 lore raw

Re: [PATCH v3 26/64] objtool: Add section/symbol type helpers

Quoted 73 lines · Josh Poimboeuf
tools/objtool/include/objtool/elf.h +60 -0
@@ -177,11 +178,71 @@ static inline unsigned int elf_text_rela_type(struct elf *elf)
 	return elf_addr_size(elf) == 4 ? R_TEXT32 : R_TEXT64;
 }
 
+static inline bool sym_has_sec(struct symbol *sym)
+{
+	return sym->sec->idx;
+}
+
+static inline bool is_null_sym(struct symbol *sym)
+{
+	return !sym->idx;
+}
+
+static inline bool is_sec_sym(struct symbol *sym)
+{
+	return sym->type == STT_SECTION;
+}
+
+static inline bool is_object_sym(struct symbol *sym)
+{
+	return sym->type == STT_OBJECT;
+}
+
+static inline bool is_func_sym(struct symbol *sym)
+{
+	return sym->type == STT_FUNC;
+}
+
+static inline bool is_file_sym(struct symbol *sym)
+{
+	return sym->type == STT_FILE;
+}
+
+static inline bool is_notype_sym(struct symbol *sym)
+{
+	return sym->type == STT_NOTYPE;
+}
+
+static inline bool is_global_sym(struct symbol *sym)
+{
+	return sym->bind == STB_GLOBAL;
+}
+
+static inline bool is_weak_sym(struct symbol *sym)
+{
+	return sym->bind == STB_WEAK;
+}
+
+static inline bool is_local_sym(struct symbol *sym)
+{
+	return sym->bind == STB_LOCAL;
+}
+
 static inline bool is_reloc_sec(struct section *sec)
 {
 	return sec->sh.sh_type == SHT_RELA || sec->sh.sh_type == SHT_REL;
 }
 
+static inline bool is_string_sec(struct section *sec)
+{
+	return sec->sh.sh_flags & SHF_STRINGS;
+}
+
+static inline bool is_text_sec(struct section *sec)
+{
+	return sec->sh.sh_flags & SHF_EXECINSTR;
+}
+
 static inline bool sec_changed(struct section *sec)
 {
 	return sec->_changed;

tools/objtool/include/objtool/elf.h static inline bool is_32bit_reloc(struct reloc *reloc) · line 290

Quoted 8 lines
tools/objtool/include/objtool/elf.h +5 -0
@@ -222,6 +283,11 @@ static inline bool is_32bit_reloc(struct reloc *reloc)
 	return reloc->sec->sh.sh_entsize < 16;
 }
 
+static inline unsigned long sec_size(struct section *sec)
+{
+	return sec->sh.sh_size;
+}
+

Naming seems inconsistent, there are:

sym_has_sec(), sec_changed() and sec_size()

which have the object first, but then most new ones are:

is_foo_sym() and is_foo_sec()

which have the object last.

Can we make this consistent and do something like:

s/is_\([^_]*\)_\(sym\|sec\)/\2_is_\1/

?

author maintainer
#31 lore raw

Re: [PATCH v3 26/64] objtool: Add section/symbol type helpers

Quoted 9 lines · Peter Zijlstra

Naming seems inconsistent, there are:

sym_has_sec(), sec_changed() and sec_size()

which have the object first, but then most new ones are:

is_foo_sym() and is_foo_sec()

which have the object last.

For the "is_()" variants, I read them as:

"is a(n) <adjective> <noun>"

e.g.:

is_undef_sym(): "is an UNDEF symbol"
is_file_sym(): "is a FILE symbol"
is_string_sec() "is a STRING section"

Nerding out on English for a second, many of those adjectives can be read as noun adjuncts, e.g. "chicken soup", where a noun functions as an adjective.

If we changed those to:

"is <noun> <adjective>?"

or

"is <noun> a <noun>?"

then it doesn't always read correctly:

is_sym_file(): "is symbol a file?"
is_sec_string(): "is section a string?"

Signature · 1 line
Josh
maintainer
#32 lore raw

Re: [PATCH v3 26/64] objtool: Add section/symbol type helpers

Quoted 29 lines · Josh Poimboeuf
Quoted 9 lines · Peter Zijlstra

Naming seems inconsistent, there are:

sym_has_sec(), sec_changed() and sec_size()

which have the object first, but then most new ones are:

is_foo_sym() and is_foo_sec()

which have the object last.

For the "is_()" variants, I read them as:

"is a(n) <adjective> <noun>"

e.g.:

is_undef_sym(): "is an UNDEF symbol"
is_file_sym(): "is a FILE symbol"
is_string_sec() "is a STRING section"

Nerding out on English for a second, many of those adjectives can be read as noun adjuncts, e.g. "chicken soup", where a noun functions as an adjective.

If we changed those to:

"is <noun> <adjective>?"

Quoted 8 lines

or

"is <noun> a <noun>?"

then it doesn't always read correctly:

is_sym_file(): "is symbol a file?"
is_sec_string(): "is section a string?"

English aside; things like sym_*() create a clear namespace, and sym_is_file() can be easily read as sym::is_file().

author maintainer
#33 lore raw

Re: [PATCH v3 26/64] objtool: Add section/symbol type helpers

Quoted 34 lines · Peter Zijlstra
Quoted 33 lines · Josh Poimboeuf
Quoted 9 lines · Peter Zijlstra

Naming seems inconsistent, there are:

sym_has_sec(), sec_changed() and sec_size()

which have the object first, but then most new ones are:

is_foo_sym() and is_foo_sec()

which have the object last.

For the "is_()" variants, I read them as:

"is a(n) <adjective> <noun>"

e.g.:

is_undef_sym(): "is an UNDEF symbol"
is_file_sym(): "is a FILE symbol"
is_string_sec() "is a STRING section"

Nerding out on English for a second, many of those adjectives can be read as noun adjuncts, e.g. "chicken soup", where a noun functions as an adjective.

If we changed those to:

"is <noun> <adjective>?"

or

"is <noun> a <noun>?"

Quoted 7 lines
Quoted 4 lines

then it doesn't always read correctly:

is_sym_file(): "is symbol a file?"
is_sec_string(): "is section a string?"

English aside; things like sym_*() create a clear namespace, and sym_is_file() can be easily read as sym::is_file().

A namespace isn't necessarily a good thing if it hurts readability. A file symbol is not a file. A string section is not a string. A rela section is not a relocation.

It makes the code less readable because the natural English reading ("section is a string") doesn't make logical sense, so it takes longer for the brain to parse.

Signature · 1 line
Josh
author maintainer
#34 lore raw

PATCH v3 27/64 objtool: Mark .cold subfunctions

In reply to Josh Poimboeuf

Introduce a flag to identify .cold subfunctions so they can be detected easier and faster.

---
 tools/objtool/check.c               | 14 ++++++--------
 tools/objtool/elf.c                 | 19 ++++++++++---------
 tools/objtool/include/objtool/elf.h |  1 +
 3 files changed, 17 insertions(+), 17 deletions(-)
tools/objtool/check.c +6 -8
index b5b324bcc9fa..f76de990183d 100644
@@ -1574,7 +1574,9 @@ static int add_jump_destinations(struct objtool_file *file)
 		/*
 		 * Cross-function jump.
 		 */
-		if (func && insn_func(jump_dest) && func != insn_func(jump_dest)) {
+
+		if (func && insn_func(jump_dest) && !func->cold &&
+		    insn_func(jump_dest)->cold) {
 
 			/*
 			 * For GCC 8+, create parent/child links for any cold
@@ -1591,11 +1593,8 @@ static int add_jump_destinations(struct objtool_file *file)
 			 * case where the parent function's only reference to a
 			 * subfunction is through a jump table.
 			 */
-			if (!strstr(func->name, ".cold") &&
-			    strstr(insn_func(jump_dest)->name, ".cold")) {
-				func->cfunc = insn_func(jump_dest);
-				insn_func(jump_dest)->pfunc = func;
-			}
+			func->cfunc = insn_func(jump_dest);
+			insn_func(jump_dest)->pfunc = func;
 		}
 
 		if (jump_is_sibling_call(file, insn, jump_dest)) {
@@ -4029,9 +4028,8 @@ static bool ignore_unreachable_insn(struct objtool_file *file, struct instructio
 			 * If this hole jumps to a .cold function, mark it ignore too.
 			 */
 			if (insn->jump_dest && insn_func(insn->jump_dest) &&
-			    strstr(insn_func(insn->jump_dest)->name, ".cold")) {
+			    insn_func(insn->jump_dest)->cold)
 				insn_func(insn->jump_dest)->ignore = true;
-			}
 		}
 
 		return false;
tools/objtool/elf.c +10 -9
index d36c0d42fd7b..59568381486c 100644
@@ -441,6 +441,10 @@ static void elf_add_symbol(struct elf *elf, struct symbol *sym)
 	list_add(&sym->list, entry);
 	elf_hash_add(symbol, &sym->hash, sym->idx);
 	elf_hash_add(symbol_name, &sym->name_hash, str_hash(sym->name));
+
+	if (is_func_sym(sym) && strstr(sym->name, ".cold"))
+		sym->cold = 1;
+	sym->pfunc = sym->cfunc = sym;
 }
 
 static int read_symbols(struct elf *elf)
@@ -527,18 +531,15 @@ static int read_symbols(struct elf *elf)
 		sec_for_each_sym(sec, sym) {
 			char *pname;
 			size_t pnamelen;
-			if (!is_func_sym(sym))
+
+			if (!sym->cold)
 				continue;
 
-			if (sym->pfunc == NULL)
-				sym->pfunc = sym;
-
-			if (sym->cfunc == NULL)
-				sym->cfunc = sym;
-
 			coldstr = strstr(sym->name, ".cold");
-			if (!coldstr)
-				continue;
+			if (!coldstr) {
+				ERROR("%s(): cold subfunction without \".cold\"?", sym->name);
+				return -1;
+			}
 
 			pnamelen = coldstr - sym->name;
 			pname = strndup(sym->name, pnamelen);
tools/objtool/include/objtool/elf.h +1 -0
index 0914dadece0b..f41496b0ad8f 100644
@@ -71,6 +71,7 @@ struct symbol {
 	u8 local_label       : 1;
 	u8 frame_pointer     : 1;
 	u8 ignore	     : 1;
+	u8 cold		     : 1;
 	struct list_head pv_target;
 	struct reloc *relocs;
 	struct section *group_sec;
Signature · 1 line
2.49.0
author maintainer
#35 lore raw

PATCH v3 28/64 objtool: Fix weak symbol hole detection for .cold functions

In reply to Josh Poimboeuf

When ignore_unreachable_insn() looks for weak function holes which jump to their .cold functions, it assumes the parent function comes before the corresponding .cold function in the symbol table. That's not necessarily the case with -ffunction-sections.

Mark all the holes beforehand (including .cold functions) so the ordering of the discovery doesn't matter.

---
 tools/objtool/check.c                 | 84 ++++++++++++++-------------
 tools/objtool/include/objtool/check.h |  3 +-
 2 files changed, 45 insertions(+), 42 deletions(-)
tools/objtool/check.c +43 -41
index f76de990183d..80bafcdb42af 100644
@@ -2504,6 +2504,44 @@ static void mark_rodata(struct objtool_file *file)
 	file->rodata = found;
 }
 
+static void mark_holes(struct objtool_file *file)
+{
+	struct instruction *insn;
+	bool in_hole = false;
+
+	if (!opts.link)
+		return;
+
+	/*
+	 * Whole archive runs might encounter dead code from weak symbols.
+	 * This is where the linker will have dropped the weak symbol in
+	 * favour of a regular symbol, but leaves the code in place.
+	 */
+	for_each_insn(file, insn) {
+		if (insn->sym || !find_symbol_hole_containing(insn->sec, insn->offset)) {
+			in_hole = false;
+			continue;
+		}
+
+		/* Skip function padding and pfx code */
+		if (!in_hole && insn->type == INSN_NOP)
+			continue;
+
+		in_hole = true;
+		insn->hole = 1;
+
+		/*
+		 * If this hole jumps to a .cold function, mark it ignore.
+		 */
+		if (insn->jump_dest) {
+			struct symbol *dest_func = insn_func(insn->jump_dest);
+
+			if (dest_func && dest_func->cold)
+				dest_func->ignore = true;
+		}
+	}
+}
+
 static int decode_sections(struct objtool_file *file)
 {
 	mark_rodata(file);
@@ -2556,6 +2594,9 @@ static int decode_sections(struct objtool_file *file)
 	if (read_unwind_hints(file))
 		return -1;
 
+	/* Must be after add_jump_destinations() */
+	mark_holes(file);
+
 	/*
 	 * Must be after add_call_destinations() such that it can override
 	 * dead_end_function() marks.
@@ -3984,7 +4025,8 @@ static bool ignore_unreachable_insn(struct objtool_file *file, struct instructio
 	struct instruction *prev_insn;
 	int i;
 
-	if (insn->type == INSN_NOP || insn->type == INSN_TRAP || (func && func->ignore))
+	if (insn->type == INSN_NOP || insn->type == INSN_TRAP ||
+	    insn->hole || (func && func->ignore))
 		return true;
 
 	/*
@@ -3995,46 +4037,6 @@ static bool ignore_unreachable_insn(struct objtool_file *file, struct instructio
 	    !strcmp(insn->sec->name, ".altinstr_aux"))
 		return true;
 
-	/*
-	 * Whole archive runs might encounter dead code from weak symbols.
-	 * This is where the linker will have dropped the weak symbol in
-	 * favour of a regular symbol, but leaves the code in place.
-	 *
-	 * In this case we'll find a piece of code (whole function) that is not
-	 * covered by a !section symbol. Ignore them.
-	 */
-	if (opts.link && !func) {
-		int size = find_symbol_hole_containing(insn->sec, insn->offset);
-		unsigned long end = insn->offset + size;
-
-		if (!size) /* not a hole */
-			return false;
-
-		if (size < 0) /* hole until the end */
-			return true;
-
-		sec_for_each_insn_continue(file, insn) {
-			/*
-			 * If we reach a visited instruction at or before the
-			 * end of the hole, ignore the unreachable.
-			 */
-			if (insn->visited)
-				return true;
-
-			if (insn->offset >= end)
-				break;
-
-			/*
-			 * If this hole jumps to a .cold function, mark it ignore too.
-			 */
-			if (insn->jump_dest && insn_func(insn->jump_dest) &&
-			    insn_func(insn->jump_dest)->cold)
-				insn_func(insn->jump_dest)->ignore = true;
-		}
-
-		return false;
-	}
-
 	if (!func)
 		return false;
 
tools/objtool/include/objtool/check.h +2 -1
index 00fb745e7233..0f4e7ac929ef 100644
@@ -64,7 +64,8 @@ struct instruction {
 	    noendbr		: 1,
 	    unret		: 1,
 	    visited		: 4,
-	    no_reloc		: 1;
+	    no_reloc		: 1,
+	    hole		: 1;
 		/* 10 bit hole */
 
 	struct alt_group *alt_group;
Signature · 1 line
2.49.0
author maintainer
#36 lore raw

PATCH v3 29/64 objtool: Mark prefix functions

In reply to Josh Poimboeuf

In preparation for the objtool klp diff subcommand, introduce a flag to identify __pfx_*() and __cfi_*() functions in advance so they don't need to be manually identified every time a check is needed.

---
 tools/objtool/check.c               | 3 +--
 tools/objtool/elf.c                 | 4 ++++
 tools/objtool/include/objtool/elf.h | 6 ++++++
 3 files changed, 11 insertions(+), 2 deletions(-)
tools/objtool/check.c +1 -2
index 80bafcdb42af..55cc3a2a21c9 100644
@@ -3564,8 +3564,7 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
 
 		if (func && insn_func(insn) && func != insn_func(insn)->pfunc) {
 			/* Ignore KCFI type preambles, which always fall through */
-			if (!strncmp(func->name, "__cfi_", 6) ||
-			    !strncmp(func->name, "__pfx_", 6))
+			if (is_prefix_func(func))
 				return 0;
 
 			if (file->ignore_unreachables)
tools/objtool/elf.c +4 -0
index 59568381486c..1bb86151243a 100644
@@ -442,6 +442,10 @@ static void elf_add_symbol(struct elf *elf, struct symbol *sym)
 	elf_hash_add(symbol, &sym->hash, sym->idx);
 	elf_hash_add(symbol_name, &sym->name_hash, str_hash(sym->name));
 
+	if (is_func_sym(sym) &&
+	    (strstarts(sym->name, "__pfx_") || strstarts(sym->name, "__cfi_")))
+		sym->prefix = 1;
+
 	if (is_func_sym(sym) && strstr(sym->name, ".cold"))
 		sym->cold = 1;
 	sym->pfunc = sym->cfunc = sym;
tools/objtool/include/objtool/elf.h +6 -0
index f41496b0ad8f..842faec1b9a9 100644
@@ -72,6 +72,7 @@ struct symbol {
 	u8 frame_pointer     : 1;
 	u8 ignore	     : 1;
 	u8 cold		     : 1;
+	u8 prefix	     : 1;
 	struct list_head pv_target;
 	struct reloc *relocs;
 	struct section *group_sec;
@@ -229,6 +230,11 @@ static inline bool is_local_sym(struct symbol *sym)
 	return sym->bind == STB_LOCAL;
 }
 
+static inline bool is_prefix_func(struct symbol *sym)
+{
+	return is_func_sym(sym) && sym->prefix;
+}
+
 static inline bool is_reloc_sec(struct section *sec)
 {
 	return sec->sh.sh_type == SHT_RELA || sec->sh.sh_type == SHT_REL;
Signature · 1 line
2.49.0
maintainer
#37 lore raw

Re: [PATCH v3 29/64] objtool: Mark prefix functions

Quoted 53 lines · Josh Poimboeuf

In preparation for the objtool klp diff subcommand, introduce a flag to identify __pfx_*() and __cfi_*() functions in advance so they don't need to be manually identified every time a check is needed.

---
 tools/objtool/check.c               | 3 +--
 tools/objtool/elf.c                 | 4 ++++
 tools/objtool/include/objtool/elf.h | 6 ++++++
 3 files changed, 11 insertions(+), 2 deletions(-)
tools/objtool/check.c +1 -2
index 80bafcdb42af..55cc3a2a21c9 100644
@@ -3564,8 +3564,7 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
 
 		if (func && insn_func(insn) && func != insn_func(insn)->pfunc) {
 			/* Ignore KCFI type preambles, which always fall through */
-			if (!strncmp(func->name, "__cfi_", 6) ||
-			    !strncmp(func->name, "__pfx_", 6))
+			if (is_prefix_func(func))
 				return 0;
 
 			if (file->ignore_unreachables)
tools/objtool/elf.c +4 -0
index 59568381486c..1bb86151243a 100644
@@ -442,6 +442,10 @@ static void elf_add_symbol(struct elf *elf, struct symbol *sym)
 	elf_hash_add(symbol, &sym->hash, sym->idx);
 	elf_hash_add(symbol_name, &sym->name_hash, str_hash(sym->name));
 
+	if (is_func_sym(sym) &&
+	    (strstarts(sym->name, "__pfx_") || strstarts(sym->name, "__cfi_")))
+		sym->prefix = 1;
+
 	if (is_func_sym(sym) && strstr(sym->name, ".cold"))
 		sym->cold = 1;
 	sym->pfunc = sym->cfunc = sym;
tools/objtool/include/objtool/elf.h +1 -0
index f41496b0ad8f..842faec1b9a9 100644
@@ -72,6 +72,7 @@ struct symbol {
 	u8 frame_pointer     : 1;
 	u8 ignore	     : 1;
 	u8 cold		     : 1;
+	u8 prefix	     : 1;
 	struct list_head pv_target;
 	struct reloc *relocs;
 	struct section *group_sec;

tools/objtool/include/objtool/elf.h static inline bool is_local_sym(struct symbol *sym) · line 236

Quoted 7 lines
tools/objtool/include/objtool/elf.h +4 -0
@@ -229,6 +230,11 @@ static inline bool is_local_sym(struct symbol *sym)
 	return sym->bind == STB_LOCAL;
 }
 
+static inline bool is_prefix_func(struct symbol *sym)
+{
+	return is_func_sym(sym) && sym->prefix;
+}

func_is_prefix() ?

Also, since we only ever set sym->prefix when is_func_sym(), this helper could avoid checking that again.

author maintainer
#38 lore raw

Re: [PATCH v3 29/64] objtool: Mark prefix functions

Quoted 6 lines · Peter Zijlstra

tools/objtool/include/objtool/elf.h static inline bool is_local_sym(struct symbol *sym) · line 236

Quoted 4 lines
tools/objtool/include/objtool/elf.h +4 -0
+static inline bool is_prefix_func(struct symbol *sym)
+{
+	return is_func_sym(sym) && sym->prefix;
+}

func_is_prefix() ?

is the function a prefix though?

Quoted 2 lines

Also, since we only ever set sym->prefix when is_func_sym(), this helper could avoid checking that again.

Indeed...

Signature · 1 line
Josh
author maintainer
#39 lore raw

PATCH v3 30/64 objtool: Simplify reloc offset calculation in unwind_read_hints()

In reply to Josh Poimboeuf

Simplify the relocation offset calculation in unwind_read_hints(), similar to other conversions which have already been done.

---
 tools/objtool/check.c | 9 +--------
 1 file changed, 1 insertion(+), 8 deletions(-)
tools/objtool/check.c +1 -8
index 55cc3a2a21c9..9ffde9389c53 100644
@@ -2199,14 +2199,7 @@ static int read_unwind_hints(struct objtool_file *file)
 			return -1;
 		}
 
-		if (is_sec_sym(reloc->sym)) {
-			offset = reloc_addend(reloc);
-		} else if (reloc->sym->local_label) {
-			offset = reloc->sym->offset;
-		} else {
-			ERROR("unexpected relocation symbol type in %s", sec->rsec->name);
-			return -1;
-		}
+		offset = reloc->sym->offset + reloc_addend(reloc);
 
 		insn = find_insn(file, reloc->sym->sec, offset);
 		if (!insn) {
Signature · 1 line
2.49.0
author maintainer
#40 lore raw

PATCH v3 31/64 objtool: Avoid emptying lists for duplicate sections

In reply to Josh Poimboeuf

When a to-be-created section already exists, there's no point in emptying the various lists if their respective sections already exist. In fact it's better to leave them intact as they might get used later.

---
 tools/objtool/check.c | 4 ----
 1 file changed, 4 deletions(-)
tools/objtool/check.c +0 -4
index 9ffde9389c53..87d2ba7739d5 100644
@@ -634,7 +634,6 @@ static int create_static_call_sections(struct objtool_file *file)
 
 	sec = find_section_by_name(file->elf, ".static_call_sites");
 	if (sec) {
-		INIT_LIST_HEAD(&file->static_call_list);
 		WARN("file already has .static_call_sites section, skipping");
 		return 0;
 	}
@@ -850,7 +849,6 @@ static int create_cfi_sections(struct objtool_file *file)
 
 	sec = find_section_by_name(file->elf, ".cfi_sites");
 	if (sec) {
-		INIT_LIST_HEAD(&file->call_list);
 		WARN("file already has .cfi_sites section, skipping");
 		return 0;
 	}
@@ -899,7 +897,6 @@ static int create_mcount_loc_sections(struct objtool_file *file)
 
 	sec = find_section_by_name(file->elf, "__mcount_loc");
 	if (sec) {
-		INIT_LIST_HEAD(&file->mcount_loc_list);
 		WARN("file already has __mcount_loc section, skipping");
 		return 0;
 	}
@@ -944,7 +941,6 @@ static int create_direct_call_sections(struct objtool_file *file)
 
 	sec = find_section_by_name(file->elf, ".call_sites");
 	if (sec) {
-		INIT_LIST_HEAD(&file->call_list);
 		WARN("file already has .call_sites section, skipping");
 		return 0;
 	}
Signature · 1 line
2.49.0
author maintainer
#41 lore raw

PATCH v3 32/64 objtool: Rename --Werror to --werror

In reply to Josh Poimboeuf

The objtool --Werror option name is stylistically inconsistent: halfway between GCC's single-dash capitalized -Werror and objtool's double-dash --lowercase convention, making it unnecessarily hard to remember.

Make the 'W' lower case (--werror) for consistency with objtool's other options.

---
 scripts/Makefile.lib          | 2 +-
 scripts/Makefile.vmlinux_o    | 2 +-
 tools/objtool/builtin-check.c | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)
scripts/Makefile.lib +1 -1
index b95560266124..15fee73e9289 100644
@@ -191,7 +191,7 @@ objtool-args-$(CONFIG_HAVE_STATIC_CALL_INLINE)		+= --static-call
 objtool-args-$(CONFIG_HAVE_UACCESS_VALIDATION)		+= --uaccess
 objtool-args-$(or $(CONFIG_GCOV_KERNEL),$(CONFIG_KCOV))	+= --no-unreachable
 objtool-args-$(CONFIG_PREFIX_SYMBOLS)			+= --prefix=$(CONFIG_FUNCTION_PADDING_BYTES)
-objtool-args-$(CONFIG_OBJTOOL_WERROR)			+= --Werror
+objtool-args-$(CONFIG_OBJTOOL_WERROR)			+= --werror
 
 objtool-args = $(objtool-args-y)					\
 	$(if $(delay-objtool), --link)					\
scripts/Makefile.vmlinux_o +1 -1
index b024ffb3e201..fed5200195df 100644
@@ -41,7 +41,7 @@ objtool-enabled := $(or $(delay-objtool),$(CONFIG_NOINSTR_VALIDATION))
 ifeq ($(delay-objtool),y)
 vmlinux-objtool-args-y					+= $(objtool-args-y)
 else
-vmlinux-objtool-args-$(CONFIG_OBJTOOL_WERROR)		+= --Werror
+vmlinux-objtool-args-$(CONFIG_OBJTOOL_WERROR)		+= --werror
 endif
 
 vmlinux-objtool-args-$(CONFIG_NOINSTR_VALIDATION)	+= --noinstr \
tools/objtool/builtin-check.c +1 -1
index 80239843e9f0..43139143edf8 100644
@@ -100,7 +100,7 @@ static const struct option check_options[] = {
 	OPT_BOOLEAN(0,   "sec-address", &opts.sec_address, "print section addresses in warnings"),
 	OPT_BOOLEAN(0,   "stats", &opts.stats, "print statistics"),
 	OPT_BOOLEAN('v', "verbose", &opts.verbose, "verbose warnings"),
-	OPT_BOOLEAN(0,   "Werror", &opts.werror, "return error on warnings"),
+	OPT_BOOLEAN(0,   "werror", &opts.werror, "return error on warnings"),
 
 	OPT_END(),
 };
Signature · 1 line
2.49.0
author maintainer
#42 lore raw

PATCH v3 33/64 objtool: Resurrect --backup option

In reply to Josh Poimboeuf

The --backup option was removed with the following commit:

aa8b3e64fd39 ("objtool: Create backup on error and print args")

... which tied the backup functionality to --verbose, and only for warnings/errors.

It's a bit inelegant and out of scope to tie that to --verbose.

Bring back the old --backup option, but with the new behavior: only on warnings/errors, and print the args to make it easier to recreate.

---
 tools/objtool/builtin-check.c           | 25 +++++++++++--------------
 tools/objtool/check.c                   |  4 +++-
 tools/objtool/include/objtool/builtin.h |  3 ++-
 3 files changed, 16 insertions(+), 16 deletions(-)
tools/objtool/builtin-check.c +11 -14
index 43139143edf8..a8c349f2273d 100644
@@ -91,6 +91,7 @@ static const struct option check_options[] = {
 
 	OPT_GROUP("Options:"),
 	OPT_BOOLEAN(0,   "backtrace", &opts.backtrace, "unwind on error"),
+	OPT_BOOLEAN(0,   "backup", &opts.backup, "create backup (.orig) file on warning/error"),
 	OPT_BOOLEAN(0,   "dry-run", &opts.dryrun, "don't write modifications"),
 	OPT_BOOLEAN(0,   "link", &opts.link, "object is a linked object"),
 	OPT_BOOLEAN(0,   "module", &opts.module, "object is part of a kernel module"),
@@ -244,12 +245,9 @@ static void save_argv(int argc, const char **argv)
 	};
 }
 
-void print_args(void)
+int make_backup(void)
 {
-	char *backup = NULL;
-
-	if (opts.output || opts.dryrun)
-		goto print;
+	char *backup;
 
 	/*
 	 * Make a backup before kbuild deletes the file so the error
@@ -258,33 +256,32 @@ void print_args(void)
 	backup = malloc(strlen(objname) + strlen(ORIG_SUFFIX) + 1);
 	if (!backup) {
 		ERROR_GLIBC("malloc");
-		goto print;
+		return 1;
 	}
 
 	strcpy(backup, objname);
 	strcat(backup, ORIG_SUFFIX);
-	if (copy_file(objname, backup)) {
-		backup = NULL;
-		goto print;
-	}
+	if (copy_file(objname, backup))
+		return 1;
 
-print:
 	/*
-	 * Print the cmdline args to make it easier to recreate.  If '--output'
-	 * wasn't used, add it to the printed args with the backup as input.
+	 * Print the cmdline args to make it easier to recreate.
 	 */
+
 	fprintf(stderr, "%s", orig_argv[0]);
 
 	for (int i = 1; i < orig_argc; i++) {
 		char *arg = orig_argv[i];
 
-		if (backup && !strcmp(arg, objname))
+		/* Modify the printed args to use the backup */
+		if (!opts.output && !strcmp(arg, objname))
 			fprintf(stderr, " %s -o %s", backup, objname);
 		else
 			fprintf(stderr, " %s", arg);
 	}
 
 	fprintf(stderr, "\n");
+	return 0;
 }
 
 int objtool_run(int argc, const char **argv)
tools/objtool/check.c +3 -1
index 87d2ba7739d5..46ace2c80317 100644
@@ -4745,9 +4745,11 @@ int check(struct objtool_file *file)
 	if (opts.verbose) {
 		if (opts.werror && warnings)
 			WARN("%d warning(s) upgraded to errors", warnings);
-		print_args();
 		disas_warned_funcs(file);
 	}
 
+	if (opts.backup && make_backup())
+		return 1;
+
 	return ret;
 }
tools/objtool/include/objtool/builtin.h +2 -1
index 6b08666fa69d..de6c08f8e060 100644
@@ -29,6 +29,7 @@ struct opts {
 
 	/* options: */
 	bool backtrace;
+	bool backup;
 	bool dryrun;
 	bool link;
 	bool mnop;
@@ -47,6 +48,6 @@ int cmd_parse_options(int argc, const char **argv, const char * const usage[]);
 
 int objtool_run(int argc, const char **argv);
 
-void print_args(void);
+int make_backup(void);
 
 #endif /* _BUILTIN_H */
Signature · 1 line
2.49.0
author maintainer
#43 lore raw

PATCH v3 34/64 objtool: Reindent check_options[]

In reply to Josh Poimboeuf

Bring the cmdline check_options[] array back into vertical alignment for better readability.

---
 tools/objtool/builtin-check.c | 52 +++++++++++++++++------------------
 1 file changed, 26 insertions(+), 26 deletions(-)
tools/objtool/builtin-check.c +26 -26
index a8c349f2273d..86b1a05c9353 100644
@@ -73,35 +73,35 @@ static int parse_hacks(const struct option *opt, const char *str, int unset)
 
 static const struct option check_options[] = {
 	OPT_GROUP("Actions:"),
+	OPT_BOOLEAN(0  ,	 "cfi", &opts.cfi, "annotate kernel control flow integrity (kCFI) function preambles"),
 	OPT_CALLBACK_OPTARG('h', "hacks", NULL, NULL, "jump_label,noinstr,skylake", "patch toolchain bugs/limitations", parse_hacks),
-	OPT_BOOLEAN('i', "ibt", &opts.ibt, "validate and annotate IBT"),
-	OPT_BOOLEAN('m', "mcount", &opts.mcount, "annotate mcount/fentry calls for ftrace"),
-	OPT_BOOLEAN('n', "noinstr", &opts.noinstr, "validate noinstr rules"),
-	OPT_BOOLEAN(0,   "orc", &opts.orc, "generate ORC metadata"),
-	OPT_BOOLEAN('r', "retpoline", &opts.retpoline, "validate and annotate retpoline usage"),
-	OPT_BOOLEAN(0,   "rethunk", &opts.rethunk, "validate and annotate rethunk usage"),
-	OPT_BOOLEAN(0,   "unret", &opts.unret, "validate entry unret placement"),
-	OPT_INTEGER(0,   "prefix", &opts.prefix, "generate prefix symbols"),
-	OPT_BOOLEAN('l', "sls", &opts.sls, "validate straight-line-speculation mitigations"),
-	OPT_BOOLEAN('s', "stackval", &opts.stackval, "validate frame pointer rules"),
-	OPT_BOOLEAN('t', "static-call", &opts.static_call, "annotate static calls"),
-	OPT_BOOLEAN('u', "uaccess", &opts.uaccess, "validate uaccess rules for SMAP"),
-	OPT_BOOLEAN(0  , "cfi", &opts.cfi, "annotate kernel control flow integrity (kCFI) function preambles"),
-	OPT_CALLBACK_OPTARG(0, "dump", NULL, NULL, "orc", "dump metadata", parse_dump),
+	OPT_BOOLEAN('i',	 "ibt", &opts.ibt, "validate and annotate IBT"),
+	OPT_BOOLEAN('m',	 "mcount", &opts.mcount, "annotate mcount/fentry calls for ftrace"),
+	OPT_BOOLEAN('n',	 "noinstr", &opts.noinstr, "validate noinstr rules"),
+	OPT_BOOLEAN(0,		 "orc", &opts.orc, "generate ORC metadata"),
+	OPT_BOOLEAN('r',	 "retpoline", &opts.retpoline, "validate and annotate retpoline usage"),
+	OPT_BOOLEAN(0,		 "rethunk", &opts.rethunk, "validate and annotate rethunk usage"),
+	OPT_BOOLEAN(0,		 "unret", &opts.unret, "validate entry unret placement"),
+	OPT_INTEGER(0,		 "prefix", &opts.prefix, "generate prefix symbols"),
+	OPT_BOOLEAN('l',	 "sls", &opts.sls, "validate straight-line-speculation mitigations"),
+	OPT_BOOLEAN('s',	 "stackval", &opts.stackval, "validate frame pointer rules"),
+	OPT_BOOLEAN('t',	 "static-call", &opts.static_call, "annotate static calls"),
+	OPT_BOOLEAN('u',	 "uaccess", &opts.uaccess, "validate uaccess rules for SMAP"),
+	OPT_CALLBACK_OPTARG(0,	 "dump", NULL, NULL, "orc", "dump metadata", parse_dump),
 
 	OPT_GROUP("Options:"),
-	OPT_BOOLEAN(0,   "backtrace", &opts.backtrace, "unwind on error"),
-	OPT_BOOLEAN(0,   "backup", &opts.backup, "create backup (.orig) file on warning/error"),
-	OPT_BOOLEAN(0,   "dry-run", &opts.dryrun, "don't write modifications"),
-	OPT_BOOLEAN(0,   "link", &opts.link, "object is a linked object"),
-	OPT_BOOLEAN(0,   "module", &opts.module, "object is part of a kernel module"),
-	OPT_BOOLEAN(0,   "mnop", &opts.mnop, "nop out mcount call sites"),
-	OPT_BOOLEAN(0,   "no-unreachable", &opts.no_unreachable, "skip 'unreachable instruction' warnings"),
-	OPT_STRING('o',  "output", &opts.output, "file", "output file name"),
-	OPT_BOOLEAN(0,   "sec-address", &opts.sec_address, "print section addresses in warnings"),
-	OPT_BOOLEAN(0,   "stats", &opts.stats, "print statistics"),
-	OPT_BOOLEAN('v', "verbose", &opts.verbose, "verbose warnings"),
-	OPT_BOOLEAN(0,   "werror", &opts.werror, "return error on warnings"),
+	OPT_BOOLEAN(0,		 "backtrace", &opts.backtrace, "unwind on error"),
+	OPT_BOOLEAN(0,		 "backup", &opts.backup, "create backup (.orig) file on warning/error"),
+	OPT_BOOLEAN(0,		 "dry-run", &opts.dryrun, "don't write modifications"),
+	OPT_BOOLEAN(0,		 "link", &opts.link, "object is a linked object"),
+	OPT_BOOLEAN(0,		 "module", &opts.module, "object is part of a kernel module"),
+	OPT_BOOLEAN(0,		 "mnop", &opts.mnop, "nop out mcount call sites"),
+	OPT_BOOLEAN(0,		 "no-unreachable", &opts.no_unreachable, "skip 'unreachable instruction' warnings"),
+	OPT_STRING('o',		 "output", &opts.output, "file", "output file name"),
+	OPT_BOOLEAN(0,		 "sec-address", &opts.sec_address, "print section addresses in warnings"),
+	OPT_BOOLEAN(0,		 "stats", &opts.stats, "print statistics"),
+	OPT_BOOLEAN('v',	 "verbose", &opts.verbose, "verbose warnings"),
+	OPT_BOOLEAN(0,		 "werror", &opts.werror, "return error on warnings"),
 
 	OPT_END(),
 };
Signature · 1 line
2.49.0
author maintainer
#44 lore raw

PATCH v3 35/64 objtool: Refactor add_jump_destinations()

In reply to Josh Poimboeuf

The add_jump_destinations() logic is a bit weird and convoluted after being incrementally tweaked over the years. Refactor it to hopefully be more logical and straightforward.

---
 tools/objtool/check.c               | 222 +++++++++++++---------------
 tools/objtool/include/objtool/elf.h |   4 +-
 2 files changed, 106 insertions(+), 120 deletions(-)
tools/objtool/check.c +104 -118
index 46ace2c80317..3afc748ba516 100644
@@ -1422,9 +1422,14 @@ static void add_return_call(struct objtool_file *file, struct instruction *insn,
 }
 
 static bool is_first_func_insn(struct objtool_file *file,
-			       struct instruction *insn, struct symbol *sym)
+			       struct instruction *insn)
 {
-	if (insn->offset == sym->offset)
+	struct symbol *func = insn_func(insn);
+
+	if (!func)
+		return false;
+
+	if (insn->offset == func->offset)
 		return true;
 
 	/* Allow direct CALL/JMP past ENDBR */
@@ -1432,51 +1437,30 @@ static bool is_first_func_insn(struct objtool_file *file,
 		struct instruction *prev = prev_insn_same_sym(file, insn);
 
 		if (prev && prev->type == INSN_ENDBR &&
-		    insn->offset == sym->offset + prev->len)
+		    insn->offset == func->offset + prev->len)
 			return true;
 	}
 
 	return false;
 }
 
-/*
- * A sibling call is a tail-call to another symbol -- to differentiate from a
- * recursive tail-call which is to the same symbol.
- */
-static bool jump_is_sibling_call(struct objtool_file *file,
-				 struct instruction *from, struct instruction *to)
-{
-	struct symbol *fs = from->sym;
-	struct symbol *ts = to->sym;
-
-	/* Not a sibling call if from/to a symbol hole */
-	if (!fs || !ts)
-		return false;
-
-	/* Not a sibling call if not targeting the start of a symbol. */
-	if (!is_first_func_insn(file, to, ts))
-		return false;
-
-	/* Disallow sibling calls into STT_NOTYPE */
-	if (is_notype_sym(ts))
-		return false;
-
-	/* Must not be self to be a sibling */
-	return fs->pfunc != ts->pfunc;
-}
-
 /*
  * Find the destination instructions for all jumps.
  */
 static int add_jump_destinations(struct objtool_file *file)
 {
-	struct instruction *insn, *jump_dest;
+	struct instruction *insn;
 	struct reloc *reloc;
-	struct section *dest_sec;
-	unsigned long dest_off;
 
 	for_each_insn(file, insn) {
 		struct symbol *func = insn_func(insn);
+		struct instruction *dest_insn;
+		struct section *dest_sec;
+		struct symbol *dest_sym;
+		unsigned long dest_off;
+
+		if (!is_static_jump(insn))
+			continue;
 
 		if (insn->jump_dest) {
 			/*
@@ -1485,51 +1469,53 @@ static int add_jump_destinations(struct objtool_file *file)
 			 */
 			continue;
 		}
-		if (!is_static_jump(insn))
-			continue;
 
 		reloc = insn_reloc(file, insn);
 		if (!reloc) {
 			dest_sec = insn->sec;
 			dest_off = arch_jump_destination(insn);
-		} else if (is_sec_sym(reloc->sym)) {
-			dest_sec = reloc->sym->sec;
-			dest_off = arch_insn_adjusted_addend(insn, reloc);
-		} else if (reloc->sym->retpoline_thunk) {
-			if (add_retpoline_call(file, insn))
-				return -1;
-			continue;
-		} else if (reloc->sym->return_thunk) {
-			add_return_call(file, insn, true);
-			continue;
-		} else if (func) {
-			/*
-			 * External sibling call or internal sibling call with
-			 * STT_FUNC reloc.
-			 */
-			if (add_call_dest(file, insn, reloc->sym, true))
-				return -1;
-			continue;
-		} else if (reloc->sym->sec->idx) {
-			dest_sec = reloc->sym->sec;
-			dest_off = reloc->sym->sym.st_value +
-				   arch_insn_adjusted_addend(insn, reloc);
+			dest_sym = dest_sec->sym;
 		} else {
-			/* non-func asm code jumping to another file */
-			continue;
+			dest_sym = reloc->sym;
+			if (is_undef_sym(dest_sym)) {
+				if (dest_sym->retpoline_thunk) {
+					if (add_retpoline_call(file, insn))
+						return -1;
+					continue;
+				}
+
+				if (dest_sym->return_thunk) {
+					add_return_call(file, insn, true);
+					continue;
+				}
+
+				/* External symbol */
+				if (func) {
+					/* External sibling call */
+					if (add_call_dest(file, insn, dest_sym, true))
+						return -1;
+					continue;
+				}
+
+				/* Non-func asm code jumping to external symbol */
+				continue;
+			}
+
+			dest_sec = dest_sym->sec;
+			dest_off = dest_sym->offset + arch_insn_adjusted_addend(insn, reloc);
 		}
 
-		jump_dest = find_insn(file, dest_sec, dest_off);
-		if (!jump_dest) {
+		dest_insn = find_insn(file, dest_sec, dest_off);
+		if (!dest_insn) {
 			struct symbol *sym = find_symbol_by_offset(dest_sec, dest_off);
 
 			/*
-			 * This is a special case for retbleed_untrain_ret().
-			 * It jumps to __x86_return_thunk(), but objtool
-			 * can't find the thunk's starting RET
-			 * instruction, because the RET is also in the
-			 * middle of another instruction.  Objtool only
-			 * knows about the outer instruction.
+			 * retbleed_untrain_ret() jumps to
+			 * __x86_return_thunk(), but objtool can't find
+			 * the thunk's starting RET instruction,
+			 * because the RET is also in the middle of
+			 * another instruction.  Objtool only knows
+			 * about the outer instruction.
 			 */
 			if (sym && sym->embedded_insn) {
 				add_return_call(file, insn, false);
@@ -1537,73 +1523,73 @@ static int add_jump_destinations(struct objtool_file *file)
 			}
 
 			/*
-			 * GCOV/KCOV dead code can jump to the end of the
-			 * function/section.
+			 * GCOV/KCOV dead code can jump to the end of
+			 * the function/section.
 			 */
 			if (file->ignore_unreachables && func &&
 			    dest_sec == insn->sec &&
 			    dest_off == func->offset + func->len)
 				continue;
 
-			ERROR_INSN(insn, "can't find jump dest instruction at %s+0x%lx",
-				   dest_sec->name, dest_off);
+			ERROR_INSN(insn, "can't find jump dest instruction at %s",
+				   offstr(dest_sec, dest_off));
 			return -1;
 		}
 
-		/*
-		 * An intra-TU jump in retpoline.o might not have a relocation
-		 * for its jump dest, in which case the above
-		 * add_{retpoline,return}_call() didn't happen.
-		 */
-		if (jump_dest->sym && jump_dest->offset == jump_dest->sym->offset) {
-			if (jump_dest->sym->retpoline_thunk) {
-				if (add_retpoline_call(file, insn))
-					return -1;
-				continue;
-			}
-			if (jump_dest->sym->return_thunk) {
-				add_return_call(file, insn, true);
-				continue;
-			}
+		if (!dest_sym || is_sec_sym(dest_sym)) {
+			dest_sym = dest_insn->sym;
+			if (!dest_sym)
+				goto set_jump_dest;
 		}
 
-		/*
-		 * Cross-function jump.
-		 */
-
-		if (func && insn_func(jump_dest) && !func->cold &&
-		    insn_func(jump_dest)->cold) {
-
-			/*
-			 * For GCC 8+, create parent/child links for any cold
-			 * subfunctions.  This is _mostly_ redundant with a
-			 * similar initialization in read_symbols().
-			 *
-			 * If a function has aliases, we want the *first* such
-			 * function in the symbol table to be the subfunction's
-			 * parent.  In that case we overwrite the
-			 * initialization done in read_symbols().
-			 *
-			 * However this code can't completely replace the
-			 * read_symbols() code because this doesn't detect the
-			 * case where the parent function's only reference to a
-			 * subfunction is through a jump table.
-			 */
-			func->cfunc = insn_func(jump_dest);
-			insn_func(jump_dest)->pfunc = func;
-		}
-
-		if (jump_is_sibling_call(file, insn, jump_dest)) {
-			/*
-			 * Internal sibling call without reloc or with
-			 * STT_SECTION reloc.
-			 */
-			if (add_call_dest(file, insn, insn_func(jump_dest), true))
+		if (dest_sym->retpoline_thunk && dest_insn->offset == dest_sym->offset) {
+			if (add_retpoline_call(file, insn))
 				return -1;
 			continue;
 		}
 
-		insn->jump_dest = jump_dest;
+		if (dest_sym->return_thunk && dest_insn->offset == dest_sym->offset) {
+			add_return_call(file, insn, true);
+			continue;
+		}
+
+		if (!insn->sym || insn->sym == dest_insn->sym)
+			goto set_jump_dest;
+
+		/*
+		 * Internal cross-function jump.
+		 */
+
+		/*
+		 * For GCC 8+, create parent/child links for any cold
+		 * subfunctions.  This is _mostly_ redundant with a
+		 * similar initialization in read_symbols().
+		 *
+		 * If a function has aliases, we want the *first* such
+		 * function in the symbol table to be the subfunction's
+		 * parent.  In that case we overwrite the
+		 * initialization done in read_symbols().
+		 *
+		 * However this code can't completely replace the
+		 * read_symbols() code because this doesn't detect the
+		 * case where the parent function's only reference to a
+		 * subfunction is through a jump table.
+		 */
+		if (func && dest_sym->cold) {
+			func->cfunc = dest_sym;
+			dest_sym->pfunc = func;
+			goto set_jump_dest;
+		}
+
+		if (is_first_func_insn(file, dest_insn)) {
+			/* Internal sibling call */
+			if (add_call_dest(file, insn, dest_sym, true))
+				return -1;
+			continue;
+		}
+
+set_jump_dest:
+		insn->jump_dest = dest_insn;
 	}
 
 	return 0;
tools/objtool/include/objtool/elf.h +2 -2
index 842faec1b9a9..74c7b84b5310 100644
@@ -180,9 +180,9 @@ static inline unsigned int elf_text_rela_type(struct elf *elf)
 	return elf_addr_size(elf) == 4 ? R_TEXT32 : R_TEXT64;
 }
 
-static inline bool sym_has_sec(struct symbol *sym)
+static inline bool is_undef_sym(struct symbol *sym)
 {
-	return sym->sec->idx;
+	return !sym->sec->idx;
 }
 
 static inline bool is_null_sym(struct symbol *sym)
Signature · 1 line
2.49.0
author maintainer
#45 lore raw

PATCH v3 36/64 objtool: Simplify special symbol handling in elf_update_symbol()

In reply to Josh Poimboeuf

!sym->sec isn't actually a thing: even STT_UNDEF and other special symbol types belong to NULL section 0.

Simplify the initialization of 'shndx' accordingly.

---
 tools/objtool/elf.c | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)
tools/objtool/elf.c +2 -3
index 1bb86151243a..4e274197bcd6 100644
@@ -634,7 +634,7 @@ static int elf_update_sym_relocs(struct elf *elf, struct symbol *sym)
 static int elf_update_symbol(struct elf *elf, struct section *symtab,
 			     struct section *symtab_shndx, struct symbol *sym)
 {
-	Elf32_Word shndx = sym->sec ? sym->sec->idx : SHN_UNDEF;
+	Elf32_Word shndx;
 	Elf_Data *symtab_data = NULL, *shndx_data = NULL;
 	Elf64_Xword entsize = symtab->sh.sh_entsize;
 	int max_idx, idx = sym->idx;
@@ -642,8 +642,7 @@ static int elf_update_symbol(struct elf *elf, struct section *symtab,
 	bool is_special_shndx = sym->sym.st_shndx >= SHN_LORESERVE &&
 				sym->sym.st_shndx != SHN_XINDEX;
 
-	if (is_special_shndx)
-		shndx = sym->sym.st_shndx;
+	shndx = is_special_shndx ? sym->sym.st_shndx : sym->sec->idx;
 
 	s = elf_getscn(elf->elf, symtab->idx);
 	if (!s) {
Signature · 1 line
2.49.0
author maintainer
#46 lore raw

PATCH v3 37/64 objtool: Generalize elf_create_symbol()

In reply to Josh Poimboeuf

In preparation for the objtool klp diff subcommand, broaden the elf_create_symbol() interface to give callers more control and reduce duplication of some subtle setup logic.

While at it, make elf_create_symbol() and elf_create_section_symbol() public so sections can be created by the upcoming klp diff code.

---
 tools/objtool/elf.c                 | 111 +++++++++++++++-------------
 tools/objtool/include/objtool/elf.h |  11 ++-
 2 files changed, 69 insertions(+), 53 deletions(-)
tools/objtool/elf.c +59 -52
index 4e274197bcd6..127e39b05a86 100644
@@ -760,24 +760,60 @@ static int elf_update_symbol(struct elf *elf, struct section *symtab,
 	return 0;
 }
 
-static struct symbol *
-__elf_create_symbol(struct elf *elf, struct symbol *sym)
+static int elf_add_string(struct elf *elf, struct section *strtab, const char *str);
+
+struct symbol *elf_create_symbol(struct elf *elf, const char *name,
+				 struct section *sec, unsigned int bind,
+				 unsigned int type, unsigned long offset,
+				 size_t size)
 {
 	struct section *symtab, *symtab_shndx;
 	Elf32_Word first_non_local, new_idx;
-	struct symbol *old;
+	struct symbol *old, *sym;
+
+	sym = calloc(1, sizeof(*sym));
+	if (!sym) {
+		ERROR_GLIBC("calloc");
+		return NULL;
+	}
+
+	sym->name = strdup(name);
+	if (!sym->name) {
+		ERROR_GLIBC("strdup");
+		return NULL;
+	}
+
+	if (type != STT_SECTION) {
+		sym->sym.st_name = elf_add_string(elf, NULL, sym->name);
+		if (sym->sym.st_name == -1)
+			return NULL;
+	}
+
+	if (sec) {
+		sym->sec = sec;
+	} else {
+		sym->sec = find_section_by_index(elf, 0);
+		if (!sym->sec) {
+			ERROR("no NULL section");
+			return NULL;
+		}
+	}
+
+	sym->sym.st_info  = GELF_ST_INFO(bind, type);
+	sym->sym.st_value = offset;
+	sym->sym.st_size  = size;
 
 	symtab = find_section_by_name(elf, ".symtab");
-	if (symtab) {
-		symtab_shndx = find_section_by_name(elf, ".symtab_shndx");
-	} else {
+	if (!symtab) {
 		ERROR("no .symtab");
 		return NULL;
 	}
 
+	symtab_shndx = find_section_by_name(elf, ".symtab_shndx");
+
 	new_idx = sec_num_entries(symtab);
 
-	if (GELF_ST_BIND(sym->sym.st_info) != STB_LOCAL)
+	if (bind != STB_LOCAL)
 		goto non_local;
 
 	/*
@@ -815,10 +851,8 @@ __elf_create_symbol(struct elf *elf, struct symbol *sym)
 
 non_local:
 	sym->idx = new_idx;
-	if (elf_update_symbol(elf, symtab, symtab_shndx, sym)) {
-		ERROR("elf_update_symbol");
+	if (sym->idx && elf_update_symbol(elf, symtab, symtab_shndx, sym))
 		return NULL;
-	}
 
 	symtab->sh.sh_size += symtab->sh.sh_entsize;
 	mark_sec_changed(elf, symtab, true);
@@ -828,64 +862,39 @@ __elf_create_symbol(struct elf *elf, struct symbol *sym)
 		mark_sec_changed(elf, symtab_shndx, true);
 	}
 
+	elf_add_symbol(elf, sym);
+
 	return sym;
 }
 
-static struct symbol *
-elf_create_section_symbol(struct elf *elf, struct section *sec)
+struct symbol *elf_create_section_symbol(struct elf *elf, struct section *sec)
 {
 	struct symbol *sym = calloc(1, sizeof(*sym));
 
-	if (!sym) {
-		ERROR_GLIBC("malloc");
+	sym = elf_create_symbol(elf, sec->name, sec, STB_LOCAL, STT_SECTION, 0, 0);
+	if (!sym)
 		return NULL;
-	}
 
-	sym->name = sec->name;
-	sym->sec = sec;
-
-	// st_name 0
-	sym->sym.st_info = GELF_ST_INFO(STB_LOCAL, STT_SECTION);
-	// st_other 0
-	// st_value 0
-	// st_size 0
-
-	sym = __elf_create_symbol(elf, sym);
-	if (sym)
-		elf_add_symbol(elf, sym);
+	sec->sym = sym;
 
 	return sym;
 }
 
-static int elf_add_string(struct elf *elf, struct section *strtab, const char *str);
-
 struct symbol *
-elf_create_prefix_symbol(struct elf *elf, struct symbol *orig, long size)
+elf_create_prefix_symbol(struct elf *elf, struct symbol *orig, size_t size)
 {
-	struct symbol *sym = calloc(1, sizeof(*sym));
 	size_t namelen = strlen(orig->name) + sizeof("__pfx_");
-	char *name = malloc(namelen);
-
-	if (!sym || !name) {
-		ERROR_GLIBC("malloc");
-		return NULL;
-	}
+	char name[SYM_NAME_LEN];
+	unsigned long offset;
 
 	snprintf(name, namelen, "__pfx_%s", orig->name);
 
-	sym->name = name;
-	sym->sec = orig->sec;
+	offset = orig->sym.st_value - size;
 
-	sym->sym.st_name = elf_add_string(elf, NULL, name);
-	sym->sym.st_info = orig->sym.st_info;
-	sym->sym.st_value = orig->sym.st_value - size;
-	sym->sym.st_size = size;
-
-	sym = __elf_create_symbol(elf, sym);
-	if (sym)
-		elf_add_symbol(elf, sym);
-
-	return sym;
+	return elf_create_symbol(elf, name, orig->sec,
+				 GELF_ST_BIND(orig->sym.st_info),
+				 GELF_ST_TYPE(orig->sym.st_info),
+				 offset, size);
 }
 
 static struct reloc *elf_init_reloc(struct elf *elf, struct section *rsec,
@@ -931,7 +940,7 @@ struct reloc *elf_init_reloc_text_sym(struct elf *elf, struct section *sec,
 				      unsigned long insn_off)
 {
 	struct symbol *sym = insn_sec->sym;
-	int addend = insn_off;
+	s64 addend = insn_off;
 
 	if (!is_text_sec(insn_sec)) {
 		ERROR("bad call to %s() for data symbol %s", __func__, sym->name);
@@ -948,8 +957,6 @@ struct reloc *elf_init_reloc_text_sym(struct elf *elf, struct section *sec,
 		sym = elf_create_section_symbol(elf, insn_sec);
 		if (!sym)
 			return NULL;
-
-		insn_sec->sym = sym;
 	}
 
 	return elf_init_reloc(elf, sec->rsec, reloc_idx, offset, sym, addend,
tools/objtool/include/objtool/elf.h +10 -1
index 74c7b84b5310..ffdf9ec3882e 100644
@@ -15,6 +15,8 @@
 #include <linux/jhash.h>
 #include <arch/elf.h>
 
+#define SYM_NAME_LEN		512
+
 #ifdef LIBELF_USE_DEPRECATED
 # define elf_getshdrnum    elf_getshnum
 # define elf_getshdrstrndx elf_getshstrndx
@@ -119,7 +121,14 @@ struct section *elf_create_section_pair(struct elf *elf, const char *name,
 					size_t entsize, unsigned int nr,
 					unsigned int reloc_nr);
 
-struct symbol *elf_create_prefix_symbol(struct elf *elf, struct symbol *orig, long size);
+struct symbol *elf_create_symbol(struct elf *elf, const char *name,
+				 struct section *sec, unsigned int bind,
+				 unsigned int type, unsigned long offset,
+				 size_t size);
+struct symbol *elf_create_section_symbol(struct elf *elf, struct section *sec);
+struct symbol *elf_create_prefix_symbol(struct elf *elf, struct symbol *orig,
+					size_t size);
+
 
 struct reloc *elf_init_reloc_text_sym(struct elf *elf, struct section *sec,
 				      unsigned long offset,
Signature · 1 line
2.49.0
author maintainer
#47 lore raw

PATCH v3 38/64 objtool: Generalize elf_create_section()

In reply to Josh Poimboeuf

In preparation for the objtool klp diff subcommand, broaden the elf_create_section() interface to give callers more control and reduce duplication of some subtle setup logic.

While at it, make elf_create_rela_section() public so sections can be created by the upcoming klp diff code.

---
 tools/objtool/elf.c                 | 118 ++++++++++++++++------------
 tools/objtool/include/objtool/elf.h |   7 +-
 tools/objtool/orc_gen.c             |   6 +-
 3 files changed, 77 insertions(+), 54 deletions(-)
tools/objtool/elf.c +66 -52
index 127e39b05a86..4f56ea426683 100644
@@ -1135,51 +1135,53 @@ static int elf_add_string(struct elf *elf, struct section *strtab, const char *s
 }
 
 struct section *elf_create_section(struct elf *elf, const char *name,
-				   size_t entsize, unsigned int nr)
+				   size_t size, size_t entsize,
+				   unsigned int type, unsigned int align,
+				   unsigned int flags)
 {
 	struct section *sec, *shstrtab;
-	size_t size = entsize * nr;
 	Elf_Scn *s;
 
-	sec = malloc(sizeof(*sec));
-	if (!sec) {
-		ERROR_GLIBC("malloc");
+	if (name && find_section_by_name(elf, name)) {
+		ERROR("section '%s' already exists", name);
+		return NULL;
+	}
+
+	sec = calloc(1, sizeof(*sec));
+	if (!sec) {
+		ERROR_GLIBC("calloc");
 		return NULL;
 	}
-	memset(sec, 0, sizeof(*sec));
 
 	INIT_LIST_HEAD(&sec->symbol_list);
 
+	/* don't actually create the section, just the data structures */
+	if (type == SHT_NULL)
+		goto add;
+
 	s = elf_newscn(elf->elf);
 	if (!s) {
 		ERROR_ELF("elf_newscn");
 		return NULL;
 	}
 
-	sec->name = strdup(name);
-	if (!sec->name) {
-		ERROR_GLIBC("strdup");
-		return NULL;
-	}
-
 	sec->idx = elf_ndxscn(s);
 
-	sec->data = elf_newdata(s);
-	if (!sec->data) {
-		ERROR_ELF("elf_newdata");
-		return NULL;
-	}
-
-	sec->data->d_size = size;
-	sec->data->d_align = 1;
-
 	if (size) {
-		sec->data->d_buf = malloc(size);
-		if (!sec->data->d_buf) {
-			ERROR_GLIBC("malloc");
+		sec->data = elf_newdata(s);
+		if (!sec->data) {
+			ERROR_ELF("elf_newdata");
+			return NULL;
+		}
+
+		sec->data->d_size = size;
+		sec->data->d_align = 1;
+
+		sec->data->d_buf = calloc(1, size);
+		if (!sec->data->d_buf) {
+			ERROR_GLIBC("calloc");
 			return NULL;
 		}
-		memset(sec->data->d_buf, 0, size);
 	}
 
 	if (!gelf_getshdr(s, &sec->sh)) {
@@ -1189,34 +1191,44 @@ struct section *elf_create_section(struct elf *elf, const char *name,
 
 	sec->sh.sh_size = size;
 	sec->sh.sh_entsize = entsize;
-	sec->sh.sh_type = SHT_PROGBITS;
-	sec->sh.sh_addralign = 1;
-	sec->sh.sh_flags = SHF_ALLOC;
+	sec->sh.sh_type = type;
+	sec->sh.sh_addralign = align;
+	sec->sh.sh_flags = flags;
 
-	/* Add section name to .shstrtab (or .strtab for Clang) */
-	shstrtab = find_section_by_name(elf, ".shstrtab");
-	if (!shstrtab)
-		shstrtab = find_section_by_name(elf, ".strtab");
-	if (!shstrtab) {
-		ERROR("can't find .shstrtab or .strtab section");
-		return NULL;
+	if (name) {
+		sec->name = strdup(name);
+		if (!sec->name) {
+			ERROR("strdup");
+			return NULL;
+		}
+
+		/* Add section name to .shstrtab (or .strtab for Clang) */
+		shstrtab = find_section_by_name(elf, ".shstrtab");
+		if (!shstrtab) {
+			shstrtab = find_section_by_name(elf, ".strtab");
+			if (!shstrtab) {
+				ERROR("can't find .shstrtab or .strtab");
+				return NULL;
+			}
+		}
+		sec->sh.sh_name = elf_add_string(elf, shstrtab, sec->name);
+		if (sec->sh.sh_name == -1)
+			return NULL;
+
+		elf_hash_add(section_name, &sec->name_hash, str_hash(sec->name));
 	}
-	sec->sh.sh_name = elf_add_string(elf, shstrtab, sec->name);
-	if (sec->sh.sh_name == -1)
-		return NULL;
 
+add:
 	list_add_tail(&sec->list, &elf->sections);
 	elf_hash_add(section, &sec->hash, sec->idx);
-	elf_hash_add(section_name, &sec->name_hash, str_hash(sec->name));
 
 	mark_sec_changed(elf, sec, true);
 
 	return sec;
 }
 
-static struct section *elf_create_rela_section(struct elf *elf,
-					       struct section *sec,
-					       unsigned int reloc_nr)
+struct section *elf_create_rela_section(struct elf *elf, struct section *sec,
+					unsigned int reloc_nr)
 {
 	struct section *rsec;
 	char *rsec_name;
@@ -1229,22 +1241,23 @@ static struct section *elf_create_rela_section(struct elf *elf,
 	strcpy(rsec_name, ".rela");
 	strcat(rsec_name, sec->name);
 
-	rsec = elf_create_section(elf, rsec_name, elf_rela_size(elf), reloc_nr);
+	rsec = elf_create_section(elf, rsec_name, reloc_nr * elf_rela_size(elf),
+				  elf_rela_size(elf), SHT_RELA, elf_addr_size(elf),
+				  SHF_INFO_LINK);
 	free(rsec_name);
 	if (!rsec)
 		return NULL;
 
-	rsec->data->d_type = ELF_T_RELA;
-	rsec->sh.sh_type = SHT_RELA;
-	rsec->sh.sh_addralign = elf_addr_size(elf);
 	rsec->sh.sh_link = find_section_by_name(elf, ".symtab")->idx;
 	rsec->sh.sh_info = sec->idx;
-	rsec->sh.sh_flags = SHF_INFO_LINK;
 
-	rsec->relocs = calloc(sec_num_entries(rsec), sizeof(struct reloc));
-	if (!rsec->relocs) {
-		ERROR_GLIBC("calloc");
-		return NULL;
+	if (reloc_nr) {
+		rsec->data->d_type = ELF_T_RELA;
+		rsec->relocs = calloc(sec_num_entries(rsec), sizeof(struct reloc));
+		if (!rsec->relocs) {
+			ERROR_GLIBC("calloc");
+			return NULL;
+		}
 	}
 
 	sec->rsec = rsec;
@@ -1259,7 +1272,8 @@ struct section *elf_create_section_pair(struct elf *elf, const char *name,
 {
 	struct section *sec;
 
-	sec = elf_create_section(elf, name, entsize, nr);
+	sec = elf_create_section(elf, name, nr * entsize, entsize,
+				 SHT_PROGBITS, 1, SHF_ALLOC);
 	if (!sec)
 		return NULL;
 
tools/objtool/include/objtool/elf.h +6 -1
index ffdf9ec3882e..b366516b119d 100644
@@ -116,11 +116,16 @@ struct elf {
 struct elf *elf_open_read(const char *name, int flags);
 
 struct section *elf_create_section(struct elf *elf, const char *name,
-				   size_t entsize, unsigned int nr);
+				   size_t size, size_t entsize,
+				   unsigned int type, unsigned int align,
+				   unsigned int flags);
 struct section *elf_create_section_pair(struct elf *elf, const char *name,
 					size_t entsize, unsigned int nr,
 					unsigned int reloc_nr);
 
+struct section *elf_create_rela_section(struct elf *elf, struct section *sec,
+					unsigned int reloc_nr);
+
 struct symbol *elf_create_symbol(struct elf *elf, const char *name,
 				 struct section *sec, unsigned int bind,
 				 unsigned int type, unsigned long offset,
tools/objtool/orc_gen.c +5 -1
index 6eff3d6a125c..9d380abc2ed3 100644
@@ -127,7 +127,11 @@ int orc_create(struct objtool_file *file)
 		return -1;
 	}
 	orc_sec = elf_create_section(file->elf, ".orc_unwind",
-				     sizeof(struct orc_entry), nr);
+				     nr * sizeof(struct orc_entry),
+				     sizeof(struct orc_entry),
+				     SHT_PROGBITS,
+				     1,
+				     SHF_ALLOC);
 	if (!orc_sec)
 		return -1;
 
Signature · 1 line
2.49.0
author maintainer
#48 lore raw

PATCH v3 39/64 objtool: Add elf_create_data()

In reply to Josh Poimboeuf

In preparation for the objtool klp diff subcommand, refactor elf_add_string() by adding a new elf_add_data() helper which allows the adding of arbitrary data to a section.

Make both interfaces public so they can be used by the upcoming klp diff code.

---
 tools/objtool/elf.c                 | 66 ++++++++++++++++++++---------
 tools/objtool/include/objtool/elf.h | 10 +++--
 2 files changed, 54 insertions(+), 22 deletions(-)
tools/objtool/elf.c +47 -19
index 4f56ea426683..535bdcf077d0 100644
@@ -18,10 +18,11 @@
 #include <errno.h>
 #include <linux/interval_tree_generic.h>
 #include <objtool/builtin.h>
-
 #include <objtool/elf.h>
 #include <objtool/warn.h>
 
+#define ALIGN_UP(x, align_to) (((x) + ((align_to)-1)) & ~((align_to)-1))
+
 static inline u32 str_hash(const char *str)
 {
 	return jhash(str, strlen(str), 0);
@@ -760,8 +761,6 @@ static int elf_update_symbol(struct elf *elf, struct section *symtab,
 	return 0;
 }
 
-static int elf_add_string(struct elf *elf, struct section *strtab, const char *str);
-
 struct symbol *elf_create_symbol(struct elf *elf, const char *name,
 				 struct section *sec, unsigned int bind,
 				 unsigned int type, unsigned long offset,
@@ -1097,11 +1096,9 @@ struct elf *elf_open_read(const char *name, int flags)
 	return NULL;
 }
 
-static int elf_add_string(struct elf *elf, struct section *strtab, const char *str)
+unsigned int elf_add_string(struct elf *elf, struct section *strtab, const char *str)
 {
-	Elf_Data *data;
-	Elf_Scn *s;
-	int len;
+	unsigned int offset;
 
 	if (!strtab)
 		strtab = find_section_by_name(elf, ".strtab");
@@ -1110,28 +1107,59 @@ static int elf_add_string(struct elf *elf, struct section *strtab, const char *s
 		return -1;
 	}
 
-	s = elf_getscn(elf->elf, strtab->idx);
+	if (!strtab->sh.sh_addralign) {
+		ERROR("'%s': invalid sh_addralign", strtab->name);
+		return -1;
+	}
+
+	offset = ALIGN_UP(strtab->sh.sh_size, strtab->sh.sh_addralign);
+
+	if (!elf_add_data(elf, strtab, str, strlen(str) + 1))
+		return -1;
+
+	return offset;
+}
+
+void *elf_add_data(struct elf *elf, struct section *sec, const void *data, size_t size)
+{
+	unsigned long offset;
+	Elf_Scn *s;
+
+	if (!sec->sh.sh_addralign) {
+		ERROR("'%s': invalid sh_addralign", sec->name);
+		return NULL;
+	}
+
+	s = elf_getscn(elf->elf, sec->idx);
 	if (!s) {
 		ERROR_ELF("elf_getscn");
-		return -1;
+		return NULL;
 	}
 
-	data = elf_newdata(s);
-	if (!data) {
+	sec->data = elf_newdata(s);
+	if (!sec->data) {
 		ERROR_ELF("elf_newdata");
-		return -1;
+		return NULL;
 	}
 
-	data->d_buf = strdup(str);
-	data->d_size = strlen(str) + 1;
-	data->d_align = 1;
+	sec->data->d_buf = calloc(1, size);
+	if (!sec->data->d_buf) {
+		ERROR_GLIBC("calloc");
+		return NULL;
+	}
 
-	len = strtab->sh.sh_size;
-	strtab->sh.sh_size += data->d_size;
+	if (data)
+		memcpy(sec->data->d_buf, data, size);
 
-	mark_sec_changed(elf, strtab, true);
+	sec->data->d_size = size;
+	sec->data->d_align = 1;
 
-	return len;
+	offset = ALIGN_UP(sec->sh.sh_size, sec->sh.sh_addralign);
+	sec->sh.sh_size = offset + size;
+
+	mark_sec_changed(elf, sec, true);
+
+	return sec->data->d_buf;
 }
 
 struct section *elf_create_section(struct elf *elf, const char *name,
tools/objtool/include/objtool/elf.h +7 -3
index b366516b119d..fc00f86bedba 100644
@@ -134,6 +134,10 @@ struct symbol *elf_create_section_symbol(struct elf *elf, struct section *sec);
 struct symbol *elf_create_prefix_symbol(struct elf *elf, struct symbol *orig,
 					size_t size);
 
+void *elf_add_data(struct elf *elf, struct section *sec, const void *data,
+		   size_t size);
+
+unsigned int elf_add_string(struct elf *elf, struct section *strtab, const char *str);
 
 struct reloc *elf_init_reloc_text_sym(struct elf *elf, struct section *sec,
 				      unsigned long offset,
@@ -147,9 +151,9 @@ struct reloc *elf_init_reloc_data_sym(struct elf *elf, struct section *sec,
 				      struct symbol *sym,
 				      s64 addend);
 
-int elf_write_insn(struct elf *elf, struct section *sec,
-		   unsigned long offset, unsigned int len,
-		   const char *insn);
+int elf_write_insn(struct elf *elf, struct section *sec, unsigned long offset,
+		   unsigned int len, const char *insn);
+
 int elf_write(struct elf *elf);
 void elf_close(struct elf *elf);
 
Signature · 1 line
2.49.0
author maintainer
#49 lore raw

PATCH v3 40/64 objtool: Add elf_create_reloc() and elf_init_reloc()

In reply to Josh Poimboeuf

elf_create_rela_section() is quite limited in that it requires the caller to know how many relocations need to be allocated up front.

In preparation for the objtool klp diff subcommand, allow an arbitrary number of relocations to be created and initialized on demand after section creation.

---
 tools/objtool/elf.c                 | 151 +++++++++++++++++++++++++---
 tools/objtool/include/objtool/elf.h |   9 ++
 2 files changed, 145 insertions(+), 15 deletions(-)
tools/objtool/elf.c +136 -15
index 535bdcf077d0..0e98cf2ab533 100644
@@ -22,6 +22,8 @@
 #include <objtool/warn.h>
 
 #define ALIGN_UP(x, align_to) (((x) + ((align_to)-1)) & ~((align_to)-1))
+#define ALIGN_UP_POW2(x) (1U << ((8 * sizeof(x)) - __builtin_clz((x) - 1U)))
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
 
 static inline u32 str_hash(const char *str)
 {
@@ -896,10 +898,9 @@ elf_create_prefix_symbol(struct elf *elf, struct symbol *orig, size_t size)
 				 offset, size);
 }
 
-static struct reloc *elf_init_reloc(struct elf *elf, struct section *rsec,
-				    unsigned int reloc_idx,
-				    unsigned long offset, struct symbol *sym,
-				    s64 addend, unsigned int type)
+struct reloc *elf_init_reloc(struct elf *elf, struct section *rsec,
+			     unsigned int reloc_idx, unsigned long offset,
+			     struct symbol *sym, s64 addend, unsigned int type)
 {
 	struct reloc *reloc, empty = { 0 };
 
@@ -1001,12 +1002,14 @@ static int read_relocs(struct elf *elf)
 
 		rsec->base->rsec = rsec;
 
-		nr_reloc = 0;
-		rsec->relocs = calloc(sec_num_entries(rsec), sizeof(*reloc));
+		rsec->nr_alloc_relocs = sec_num_entries(rsec);
+		rsec->relocs = calloc(rsec->nr_alloc_relocs, sizeof(*reloc));
 		if (!rsec->relocs) {
 			ERROR_GLIBC("calloc");
 			return -1;
 		}
+
+		nr_reloc = 0;
 		for (i = 0; i < sec_num_entries(rsec); i++) {
 			reloc = &rsec->relocs[i];
 
@@ -1255,8 +1258,99 @@ struct section *elf_create_section(struct elf *elf, const char *name,
 	return sec;
 }
 
+static int elf_alloc_reloc(struct elf *elf, struct section *rsec)
+{
+	struct reloc *old_relocs, *old_relocs_end, *new_relocs;
+	unsigned int nr_relocs_old = sec_num_entries(rsec);
+	unsigned int nr_relocs_new = nr_relocs_old + 1;
+	unsigned long nr_alloc;
+	struct symbol *sym;
+
+	if (!rsec->data) {
+		rsec->data = elf_newdata(elf_getscn(elf->elf, rsec->idx));
+		if (!rsec->data) {
+			ERROR_ELF("elf_newdata");
+			return -1;
+		}
+
+		rsec->data->d_align = 1;
+		rsec->data->d_type = ELF_T_RELA;
+		rsec->data->d_buf = NULL;
+	}
+
+	rsec->data->d_size = nr_relocs_new * elf_rela_size(elf);
+	rsec->sh.sh_size   = rsec->data->d_size;
+
+	nr_alloc = MAX(64, ALIGN_UP_POW2(nr_relocs_new));
+	if (nr_alloc <= rsec->nr_alloc_relocs)
+		return 0;
+	rsec->nr_alloc_relocs = nr_alloc;
+
+	rsec->data->d_buf = realloc(rsec->data->d_buf,
+				    nr_alloc * elf_rela_size(elf));
+	if (!rsec->data->d_buf) {
+		ERROR_GLIBC("realloc");
+		return -1;
+	}
+
+	old_relocs = rsec->relocs;
+	new_relocs = calloc(nr_alloc, sizeof(struct reloc));
+	if (!new_relocs) {
+		ERROR_GLIBC("calloc");
+		return -1;
+	}
+
+	if (!old_relocs)
+		goto done;
+
+	/*
+	 * The struct reloc's address has changed.  Update all the symbols and
+	 * relocs which reference it.
+	 */
+
+	old_relocs_end = &old_relocs[nr_relocs_old];
+	for_each_sym(elf, sym) {
+		struct reloc *reloc;
+
+		reloc = sym->relocs;
+		if (!reloc)
+			continue;
+
+		if (reloc >= old_relocs && reloc < old_relocs_end)
+			sym->relocs = &new_relocs[reloc - old_relocs];
+
+		while (1) {
+			struct reloc *next_reloc = sym_next_reloc(reloc);
+
+			if (!next_reloc)
+				break;
+
+			if (next_reloc >= old_relocs && next_reloc < old_relocs_end)
+				set_sym_next_reloc(reloc, &new_relocs[next_reloc - old_relocs]);
+
+			reloc = next_reloc;
+		}
+	}
+
+	memcpy(new_relocs, old_relocs, nr_relocs_old * sizeof(struct reloc));
+
+	for (int i = 0; i < nr_relocs_old; i++) {
+		struct reloc *old = &old_relocs[i];
+		struct reloc *new = &new_relocs[i];
+		u32 key = reloc_hash(old);
+
+		elf_hash_del(reloc, &old->hash, key);
+		elf_hash_add(reloc, &new->hash, key);
+	}
+
+	free(old_relocs);
+done:
+	rsec->relocs = new_relocs;
+	return 0;
+}
+
 struct section *elf_create_rela_section(struct elf *elf, struct section *sec,
-					unsigned int reloc_nr)
+					unsigned int nr_relocs)
 {
 	struct section *rsec;
 	char *rsec_name;
@@ -1269,34 +1363,61 @@ struct section *elf_create_rela_section(struct elf *elf, struct section *sec,
 	strcpy(rsec_name, ".rela");
 	strcat(rsec_name, sec->name);
 
-	rsec = elf_create_section(elf, rsec_name, reloc_nr * elf_rela_size(elf),
+	rsec = elf_create_section(elf, rsec_name, nr_relocs * elf_rela_size(elf),
 				  elf_rela_size(elf), SHT_RELA, elf_addr_size(elf),
 				  SHF_INFO_LINK);
 	free(rsec_name);
 	if (!rsec)
 		return NULL;
 
-	rsec->sh.sh_link = find_section_by_name(elf, ".symtab")->idx;
-	rsec->sh.sh_info = sec->idx;
-
-	if (reloc_nr) {
+	if (nr_relocs) {
 		rsec->data->d_type = ELF_T_RELA;
-		rsec->relocs = calloc(sec_num_entries(rsec), sizeof(struct reloc));
+
+		rsec->nr_alloc_relocs = nr_relocs;
+		rsec->relocs = calloc(nr_relocs, sizeof(struct reloc));
 		if (!rsec->relocs) {
 			ERROR_GLIBC("calloc");
 			return NULL;
 		}
 	}
 
+	rsec->sh.sh_link = find_section_by_name(elf, ".symtab")->idx;
+	rsec->sh.sh_info = sec->idx;
+
 	sec->rsec = rsec;
 	rsec->base = sec;
 
 	return rsec;
 }
 
+struct reloc *elf_create_reloc(struct elf *elf, struct section *sec,
+			       unsigned long offset,
+			       struct symbol *sym, s64 addend,
+			       unsigned int type)
+{
+	struct section *rsec = sec->rsec;
+
+	if (!rsec) {
+		rsec = elf_create_rela_section(elf, sec, 0);
+		if (!rsec)
+			return NULL;
+	}
+
+	if (find_reloc_by_dest(elf, sec, offset)) {
+		ERROR_FUNC(sec, offset, "duplicate reloc");
+		return NULL;
+	}
+
+	if (elf_alloc_reloc(elf, rsec))
+		return NULL;
+
+	return elf_init_reloc(elf, rsec, sec_num_entries(rsec) - 1, offset, sym,
+			      addend, type);
+}
+
 struct section *elf_create_section_pair(struct elf *elf, const char *name,
 					size_t entsize, unsigned int nr,
-					unsigned int reloc_nr)
+					unsigned int nr_relocs)
 {
 	struct section *sec;
 
@@ -1305,7 +1426,7 @@ struct section *elf_create_section_pair(struct elf *elf, const char *name,
 	if (!sec)
 		return NULL;
 
-	if (!elf_create_rela_section(elf, sec, reloc_nr))
+	if (!elf_create_rela_section(elf, sec, nr_relocs))
 		return NULL;
 
 	return sec;
tools/objtool/include/objtool/elf.h +9 -0
index fc00f86bedba..5c663e475890 100644
@@ -47,6 +47,7 @@ struct section {
 	int idx;
 	bool _changed, text, rodata, noinstr, init, truncate;
 	struct reloc *relocs;
+	unsigned long nr_alloc_relocs;
 };
 
 struct symbol {
@@ -139,6 +140,14 @@ void *elf_add_data(struct elf *elf, struct section *sec, const void *data,
 
 unsigned int elf_add_string(struct elf *elf, struct section *strtab, const char *str);
 
+struct reloc *elf_create_reloc(struct elf *elf, struct section *sec,
+			       unsigned long offset, struct symbol *sym,
+			       s64 addend, unsigned int type);
+
+struct reloc *elf_init_reloc(struct elf *elf, struct section *rsec,
+			     unsigned int reloc_idx, unsigned long offset,
+			     struct symbol *sym, s64 addend, unsigned int type);
+
 struct reloc *elf_init_reloc_text_sym(struct elf *elf, struct section *sec,
 				      unsigned long offset,
 				      unsigned int reloc_idx,
Signature · 1 line
2.49.0
author maintainer
#50 lore raw

PATCH v3 41/64 objtool: Add elf_create_file()

In reply to Josh Poimboeuf

Add interface to enable the creation of a new ELF file.

---
 tools/objtool/builtin-check.c       |   2 +-
 tools/objtool/elf.c                 | 144 +++++++++++++++++++++++++++-
 tools/objtool/include/objtool/elf.h |   5 +-
 3 files changed, 147 insertions(+), 4 deletions(-)
tools/objtool/builtin-check.c +1 -1
index 86b1a05c9353..3172f7e98147 100644
@@ -327,5 +327,5 @@ int objtool_run(int argc, const char **argv)
 	if (!opts.dryrun && file->elf->changed && elf_write(file->elf))
 		return 1;
 
-	return 0;
+	return elf_close(file->elf);
 }
tools/objtool/elf.c +143 -1

176 lines not on this page · raw message

tools/objtool/include/objtool/elf.h +3 -2
index 5c663e475890..a0fc252e1993 100644
@@ -93,7 +93,7 @@ struct elf {
 	GElf_Ehdr ehdr;
 	int fd;
 	bool changed;
-	const char *name;
+	const char *name, *tmp_name;
 	unsigned int num_files;
 	struct list_head sections;
 	unsigned long num_relocs;
@@ -115,6 +115,7 @@ struct elf {
 };
 
 struct elf *elf_open_read(const char *name, int flags);
+struct elf *elf_create_file(GElf_Ehdr *ehdr, const char *name);
 
 struct section *elf_create_section(struct elf *elf, const char *name,
 				   size_t size, size_t entsize,
@@ -164,7 +165,7 @@ int elf_write_insn(struct elf *elf, struct section *sec, unsigned long offset,
 		   unsigned int len, const char *insn);
 
 int elf_write(struct elf *elf);
-void elf_close(struct elf *elf);
+int elf_close(struct elf *elf);
 
 struct section *find_section_by_name(const struct elf *elf, const char *name);
 struct symbol *find_func_by_offset(struct section *sec, unsigned long offset);
Signature · 1 line
2.49.0
author maintainer
#51 lore raw

PATCH v3 42/64 kbuild,x86: Fix special section module permissions

In reply to Josh Poimboeuf

An upcoming patch will add the SHF_MERGE flag to x86 __jump_table and __bug_table so their entry sizes can be defined in inline asm.

However, those sections have SHF_WRITE, which the Clang linker (lld) explicitly forbids combining with SHF_MERGE.

Those sections are modified at runtime and must remain writable. While SHF_WRITE is ignored by vmlinux, it's still needed for modules.

To work around the linker interference, remove SHF_WRITE during compilation and restore it after linking the module.

---
 arch/Kconfig                      |  3 +++
 arch/x86/Kconfig                  |  1 +
 arch/x86/include/asm/bug.h        |  4 ++--
 arch/x86/include/asm/jump_label.h |  2 +-
 scripts/Makefile.modfinal         | 19 ++++++++++++++-----
 5 files changed, 21 insertions(+), 8 deletions(-)
arch/Kconfig +3 -0
index a3308a220f86..350ea5df5e8d 100644
@@ -1314,6 +1314,9 @@ config HAVE_NOINSTR_HACK
 config HAVE_NOINSTR_VALIDATION
 	bool
 
+config NEED_MODULE_PERMISSIONS_FIX
+	bool
+
 config HAVE_UACCESS_VALIDATION
 	bool
 	select OBJTOOL
arch/x86/Kconfig +1 -0
index 71019b3b54ea..62faa62b5959 100644
@@ -310,6 +310,7 @@ config X86
 	select HOTPLUG_SPLIT_STARTUP		if SMP && X86_32
 	select IRQ_FORCED_THREADING
 	select LOCK_MM_AND_FIND_VMA
+	select NEED_MODULE_PERMISSIONS_FIX
 	select NEED_PER_CPU_EMBED_FIRST_CHUNK
 	select NEED_PER_CPU_PAGE_FIRST_CHUNK
 	select NEED_SG_DMA_LENGTH
arch/x86/include/asm/bug.h +2 -2
index 8593976b32cb..59e155ee3c76 100644
@@ -42,7 +42,7 @@
 #define _BUG_FLAGS(cond_str, ins, flags, extra)				\
 do {									\
 	asm_inline volatile("1:\t" ins "\n"				\
-		     ".pushsection __bug_table,\"aw\"\n"		\
+		     ".pushsection __bug_table,\"a\"\n"			\
 		     "2:\t" __BUG_REL(1b) "\t# bug_entry::bug_addr\n"	\
 		     "\t"  __BUG_REL(%c0) "\t# bug_entry::file\n"	\
 		     "\t.word %c1"        "\t# bug_entry::line\n"	\
@@ -60,7 +60,7 @@ do {									\
 #define _BUG_FLAGS(cond_str, ins, flags, extra)				\
 do {									\
 	asm_inline volatile("1:\t" ins "\n"				\
-		     ".pushsection __bug_table,\"aw\"\n"		\
+		     ".pushsection __bug_table,\"a\"\n"			\
 		     "2:\t" __BUG_REL(1b) "\t# bug_entry::bug_addr\n"	\
 		     "\t.word %c0"        "\t# bug_entry::flags\n"	\
 		     "\t.org 2b+%c1\n"					\
arch/x86/include/asm/jump_label.h +1 -1
index 61dd1dee7812..cd21554b3675 100644
@@ -13,7 +13,7 @@
 #include <linux/types.h>
 
 #define JUMP_TABLE_ENTRY(key, label)			\
-	".pushsection __jump_table,  \"aw\" \n\t"	\
+	".pushsection __jump_table,  \"a\"\n\t"		\
 	_ASM_ALIGN "\n\t"				\
 	".long 1b - . \n\t"				\
 	".long " label " - . \n\t"			\
scripts/Makefile.modfinal +14 -5
index 542ba462ed3e..7a888e1ff70f 100644
@@ -28,12 +28,24 @@ ccflags-remove-y := $(CC_FLAGS_CFI)
 .module-common.o: $(srctree)/scripts/module-common.c FORCE
 	$(call if_changed_rule,cc_o_c)
 
+ifdef CONFIG_NEED_MODULE_PERMISSIONS_FIX
+cmd_fix_mod_permissions =						\
+	$(OBJCOPY) --set-section-flags __jump_table=alloc,data		\
+		   --set-section-flags __bug_table=alloc,data $@	\
+		   --set-section-flags .static_call_sites=alloc,data $@
+endif
+
 quiet_cmd_ld_ko_o = LD [M]  $@
       cmd_ld_ko_o =							\
 	$(LD) -r $(KBUILD_LDFLAGS)					\
 		$(KBUILD_LDFLAGS_MODULE) $(LDFLAGS_MODULE)		\
 		-T $(objtree)/scripts/module.lds -o $@ $(filter %.o, $^)
 
+define rule_ld_ko_o
+	$(call cmd_and_savecmd,ld_ko_o)
+	$(call cmd,fix_mod_permissions)
+endef
+
 quiet_cmd_btf_ko = BTF [M] $@
       cmd_btf_ko = 							\
 	if [ ! -f $(objtree)/vmlinux ]; then				\
@@ -46,14 +58,11 @@ quiet_cmd_btf_ko = BTF [M] $@
 # Same as newer-prereqs, but allows to exclude specified extra dependencies
 newer_prereqs_except = $(filter-out $(PHONY) $(1),$?)
 
-# Same as if_changed, but allows to exclude specified extra dependencies
-if_changed_except = $(if $(call newer_prereqs_except,$(2))$(cmd-check),      \
-	$(cmd);                                                              \
-	printf '%s\n' 'savedcmd_$@ := $(make-cmd)' > $(dot-target).cmd, @:)
+if_changed_rule_except = $(if $(call newer_prereqs_except,$(2))$(cmd-check),$(rule_$(1)),@:)
 
 # Re-generate module BTFs if either module's .ko or vmlinux changed
 %.ko: %.o %.mod.o .module-common.o $(objtree)/scripts/module.lds $(and $(CONFIG_DEBUG_INFO_BTF_MODULES),$(KBUILD_BUILTIN),$(objtree)/vmlinux) FORCE
-	+$(call if_changed_except,ld_ko_o,$(objtree)/vmlinux)
+	+$(call if_changed_rule_except,ld_ko_o,$(objtree)/vmlinux)
 ifdef CONFIG_DEBUG_INFO_BTF_MODULES
 	+$(if $(newer-prereqs),$(call cmd,btf_ko))
 endif
Signature · 1 line
2.49.0
maintainer
#52 lore raw

Re: [PATCH v3 42/64] kbuild,x86: Fix special section module permissions

Quoted 11 lines · Josh Poimboeuf

An upcoming patch will add the SHF_MERGE flag to x86 __jump_table and __bug_table so their entry sizes can be defined in inline asm.

However, those sections have SHF_WRITE, which the Clang linker (lld) explicitly forbids combining with SHF_MERGE.

Those sections are modified at runtime and must remain writable. While SHF_WRITE is ignored by vmlinux, it's still needed for modules.

To work around the linker interference, remove SHF_WRITE during compilation and restore it after linking the module.

This is vile... but I'm not sure I have a better solution.

Eventually we should get the toolchains fixed, but we can't very well mandate clang-21+ to build x86 just yet.

author maintainer
#53 lore raw

Re: [PATCH v3 42/64] kbuild,x86: Fix special section module permissions

Quoted 10 lines · Peter Zijlstra
Quoted 9 lines · Josh Poimboeuf

An upcoming patch will add the SHF_MERGE flag to x86 __jump_table and __bug_table so their entry sizes can be defined in inline asm.

However, those sections have SHF_WRITE, which the Clang linker (lld) explicitly forbids combining with SHF_MERGE.

Those sections are modified at runtime and must remain writable. While SHF_WRITE is ignored by vmlinux, it's still needed for modules.

Quoted 7 lines
Quoted 2 lines

To work around the linker interference, remove SHF_WRITE during compilation and restore it after linking the module.

This is vile... but I'm not sure I have a better solution.

Eventually we should get the toolchains fixed, but we can't very well mandate clang-21+ to build x86 just yet.

Yeah, I really hate this too. I really tried to find something better, including mucking with the linker script, but this was unfortunately the only thing that worked.

Though, looking at it again, I realize we can localize the pain to Clang (and the makefile) by leaving the code untouched and instead strip SHF_WRITE before the link and re-add it afterwards. Then we can tie this horrible hack to specific Clang versions when it gets fixed.

Something like so:

arch/Kconfig +3 -0
index a3308a220f86..350ea5df5e8d 100644
@@ -1314,6 +1314,9 @@ config HAVE_NOINSTR_HACK
 config HAVE_NOINSTR_VALIDATION
 	bool
 
+config NEED_MODULE_PERMISSIONS_FIX
+	bool
+
 config HAVE_UACCESS_VALIDATION
 	bool
 	select OBJTOOL
arch/x86/Kconfig +1 -0
index 71019b3b54ea..0cac13c03a90 100644
@@ -310,6 +310,7 @@ config X86
 	select HOTPLUG_SPLIT_STARTUP		if SMP && X86_32
 	select IRQ_FORCED_THREADING
 	select LOCK_MM_AND_FIND_VMA
+	select NEED_MODULE_PERMISSIONS_FIX	if LD_IS_LLD
 	select NEED_PER_CPU_EMBED_FIRST_CHUNK
 	select NEED_PER_CPU_PAGE_FIRST_CHUNK
 	select NEED_SG_DMA_LENGTH
scripts/Makefile.modfinal +27 -5
index 542ba462ed3e..cbc3213427ba 100644
@@ -28,12 +28,37 @@ ccflags-remove-y := $(CC_FLAGS_CFI)
 .module-common.o: $(srctree)/scripts/module-common.c FORCE
 	$(call if_changed_rule,cc_o_c)
 
+
+ifdef CONFIG_NEED_MODULE_PERMISSIONS_FIX
+
+# The LLVM linker forbids SHF_MERGE+SHF_WRITE.  Hack around that by
+# temporarily removing SHF_WRITE from affected sections before linking.
+
+cmd_fix_mod_permissions_pre_link =					\
+	$(OBJCOPY) --set-section-flags __jump_table=alloc,readonly	\
+		   --set-section-flags __bug_table=alloc,readonly $@	\
+		   --set-section-flags .static_call_sites=alloc,readonly $@
+
+cmd_fix_mod_permissions_post_link =					\
+	$(OBJCOPY) --set-section-flags __jump_table=alloc,data		\
+		   --set-section-flags __bug_table=alloc,data $@	\
+		   --set-section-flags .static_call_sites=alloc,data $@
+
+endif # CONFIG_NEED_MODULE_PERMISSIONS_FIX
+
+
 quiet_cmd_ld_ko_o = LD [M]  $@
       cmd_ld_ko_o =							\
 	$(LD) -r $(KBUILD_LDFLAGS)					\
 		$(KBUILD_LDFLAGS_MODULE) $(LDFLAGS_MODULE)		\
 		-T $(objtree)/scripts/module.lds -o $@ $(filter %.o, $^)
 
+define rule_ld_ko_o
+	$(call cmd,fix_mod_permissions_pre_link)
+	$(call cmd_and_savecmd,ld_ko_o)
+	$(call cmd,fix_mod_permissions_post_link)
+endef
+
 quiet_cmd_btf_ko = BTF [M] $@
       cmd_btf_ko = 							\
 	if [ ! -f $(objtree)/vmlinux ]; then				\
@@ -46,14 +71,11 @@ quiet_cmd_btf_ko = BTF [M] $@
 # Same as newer-prereqs, but allows to exclude specified extra dependencies
 newer_prereqs_except = $(filter-out $(PHONY) $(1),$?)
 
-# Same as if_changed, but allows to exclude specified extra dependencies
-if_changed_except = $(if $(call newer_prereqs_except,$(2))$(cmd-check),      \
-	$(cmd);                                                              \
-	printf '%s\n' 'savedcmd_$@ := $(make-cmd)' > $(dot-target).cmd, @:)
+if_changed_rule_except = $(if $(call newer_prereqs_except,$(2))$(cmd-check),$(rule_$(1)),@:)
 
 # Re-generate module BTFs if either module's .ko or vmlinux changed
 %.ko: %.o %.mod.o .module-common.o $(objtree)/scripts/module.lds $(and $(CONFIG_DEBUG_INFO_BTF_MODULES),$(KBUILD_BUILTIN),$(objtree)/vmlinux) FORCE
-	+$(call if_changed_except,ld_ko_o,$(objtree)/vmlinux)
+	+$(call if_changed_rule_except,ld_ko_o,$(objtree)/vmlinux)
 ifdef CONFIG_DEBUG_INFO_BTF_MODULES
 	+$(if $(newer-prereqs),$(call cmd,btf_ko))
 endif
maintainer
#54 lore raw

Re: [PATCH v3 42/64] kbuild,x86: Fix special section module permissions

Quoted 19 lines · Josh Poimboeuf
Quoted 10 lines · Peter Zijlstra
Quoted 9 lines · Josh Poimboeuf

An upcoming patch will add the SHF_MERGE flag to x86 __jump_table and __bug_table so their entry sizes can be defined in inline asm.

However, those sections have SHF_WRITE, which the Clang linker (lld) explicitly forbids combining with SHF_MERGE.

Those sections are modified at runtime and must remain writable. While SHF_WRITE is ignored by vmlinux, it's still needed for modules.

Quoted 7 lines
Quoted 2 lines

To work around the linker interference, remove SHF_WRITE during compilation and restore it after linking the module.

This is vile... but I'm not sure I have a better solution.

Eventually we should get the toolchains fixed, but we can't very well mandate clang-21+ to build x86 just yet.

Quoted 8 lines

Yeah, I really hate this too. I really tried to find something better, including mucking with the linker script, but this was unfortunately the only thing that worked.

Though, looking at it again, I realize we can localize the pain to Clang (and the makefile) by leaving the code untouched and instead strip SHF_WRITE before the link and re-add it afterwards. Then we can tie this horrible hack to specific Clang versions when it gets fixed.

Oh yeah, that might be nicer indeed!

author maintainer
#55 lore raw

Re: [PATCH v3 42/64] kbuild,x86: Fix special section module permissions

Quoted 24 lines · Peter Zijlstra
Quoted 23 lines · Josh Poimboeuf
Quoted 10 lines · Peter Zijlstra
Quoted 9 lines · Josh Poimboeuf

An upcoming patch will add the SHF_MERGE flag to x86 __jump_table and __bug_table so their entry sizes can be defined in inline asm.

However, those sections have SHF_WRITE, which the Clang linker (lld) explicitly forbids combining with SHF_MERGE.

Those sections are modified at runtime and must remain writable. While SHF_WRITE is ignored by vmlinux, it's still needed for modules.

Quoted 7 lines
Quoted 2 lines

To work around the linker interference, remove SHF_WRITE during compilation and restore it after linking the module.

This is vile... but I'm not sure I have a better solution.

Eventually we should get the toolchains fixed, but we can't very well mandate clang-21+ to build x86 just yet.

Yeah, I really hate this too. I really tried to find something better, including mucking with the linker script, but this was unfortunately the only thing that worked.

Quoted 6 lines
Quoted 4 lines

Though, looking at it again, I realize we can localize the pain to Clang (and the makefile) by leaving the code untouched and instead strip SHF_WRITE before the link and re-add it afterwards. Then we can tie this horrible hack to specific Clang versions when it gets fixed.

Oh yeah, that might be nicer indeed!

I ended up giving up on this approach. It was just too painful trying to get the toolchain to cooperate. It does kind of make sense that SHF_WRITE and SHF_MERGE are incompatible.

What I really needed was a way to set entsize from assembly, but that doesn't seem possible without SHF_MERGE.

For now I'm just going with marking the beginning of each special section entry with an objtool annotation. Then objtool can deduce the sizes from the distance between the annotations.

Signature · 1 line
Josh
author maintainer
#56 lore raw

PATCH v3 43/64 x86/alternative: Define ELF section entry size for alternatives

In reply to Josh Poimboeuf

In preparation for the objtool klp diff subcommand, define the entry size for the .altinstructions section in its ELF header. This will allow tooling to extract individual entries.

---
 arch/um/include/shared/common-offsets.h    | 2 ++
 arch/x86/include/asm/alternative.h         | 5 +++--
 arch/x86/include/asm/asm.h                 | 4 ++++
 arch/x86/kernel/asm-offsets.c              | 2 ++
 arch/x86/um/shared/sysdep/kernel-offsets.h | 1 +
 kernel/bounds.c                            | 1 +
 scripts/mod/devicetable-offsets.c          | 1 +
 7 files changed, 14 insertions(+), 2 deletions(-)
arch/um/include/shared/common-offsets.h +2 -0
index 8ca66a1918c3..4e19103afd71 100644
@@ -18,3 +18,5 @@ DEFINE(UM_NSEC_PER_USEC, NSEC_PER_USEC);
 DEFINE(UM_KERN_GDT_ENTRY_TLS_ENTRIES, GDT_ENTRY_TLS_ENTRIES);
 
 DEFINE(UM_SECCOMP_ARCH_NATIVE, SECCOMP_ARCH_NATIVE);
+
+DEFINE(ALT_INSTR_SIZE, sizeof(struct alt_instr));
arch/x86/include/asm/alternative.h +3 -2
index 15bc07a5ebb3..eb24d9ba30d7 100644
@@ -197,7 +197,8 @@ static inline int alternatives_text_reserved(void *start, void *end)
 	"773:\n"
 
 #define ALTINSTR_ENTRY(ft_flags)					      \
-	".pushsection .altinstructions,\"a\"\n"				      \
+	".pushsection .altinstructions, \"aM\", @progbits, "		      \
+		      __stringify(ALT_INSTR_SIZE) "\n"			      \
 	" .long 771b - .\n"				/* label           */ \
 	" .long 774f - .\n"				/* new instruction */ \
 	" .4byte " __stringify(ft_flags) "\n"		/* feature + flags */ \
@@ -360,7 +361,7 @@ void nop_func(void);
 741:									\
 	.skip -(((744f-743f)-(741b-740b)) > 0) * ((744f-743f)-(741b-740b)),0x90	;\
 742:									\
-	.pushsection .altinstructions,"a" ;				\
+	.pushsection .altinstructions, "aM", @progbits, ALT_INSTR_SIZE ;\
 	altinstr_entry 740b,743f,flag,742b-740b,744f-743f ;		\
 	.popsection ;							\
 	.pushsection .altinstr_replacement,"ax"	;			\
arch/x86/include/asm/asm.h +4 -0
index f963848024a5..1f26f90a57ce 100644
@@ -136,6 +136,10 @@ static __always_inline __pure void *rip_rel_ptr(void *p)
 
 #ifdef __KERNEL__
 
+#ifndef COMPILE_OFFSETS
+#include <asm/asm-offsets.h>
+#endif
+
 # include <asm/extable_fixup_types.h>
 
 /* Exception table entry */
arch/x86/kernel/asm-offsets.c +2 -0
index 6259b474073b..b51625c3f64c 100644
@@ -123,4 +123,6 @@ static void __used common(void)
 	OFFSET(ARIA_CTX_rounds, aria_ctx, rounds);
 #endif
 
+	BLANK();
+	DEFINE(ALT_INSTR_SIZE,	 sizeof(struct alt_instr));
 }
arch/x86/um/shared/sysdep/kernel-offsets.h +1 -0
index 6fd1ed400399..9f6d3d1a248c 100644
@@ -1,4 +1,5 @@
 /* SPDX-License-Identifier: GPL-2.0 */
+#define COMPILE_OFFSETS
 #include <linux/stddef.h>
 #include <linux/sched.h>
 #include <linux/elf.h>
kernel/bounds.c +1 -0
index 29b2cd00df2c..02b619eb6106 100644
@@ -6,6 +6,7 @@
  */
 
 #define __GENERATING_BOUNDS_H
+#define COMPILE_OFFSETS
 /* Include headers that define the enum constants of interest */
 #include <linux/page-flags.h>
 #include <linux/mmzone.h>
scripts/mod/devicetable-offsets.c +1 -0
index d3d00e85edf7..ef2ffb68f69d 100644
@@ -1,4 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
+#define COMPILE_OFFSETS
 #include <linux/kbuild.h>
 #include <linux/mod_devicetable.h>
 
Signature · 1 line
2.49.0
author maintainer
#57 lore raw

PATCH v3 44/64 x86/jump_label: Define ELF section entry size for jump labels

In reply to Josh Poimboeuf

In preparation for the objtool klp diff subcommand, define the entry size for the __jump_table section in its ELF header. This will allow tooling to extract individual entries.

---
 arch/x86/include/asm/jump_label.h | 17 +++++++++++------
 kernel/bounds.c                   |  4 ++++
 2 files changed, 15 insertions(+), 6 deletions(-)
arch/x86/include/asm/jump_label.h +11 -6
index cd21554b3675..7a6b0e5d85c1 100644
@@ -12,12 +12,17 @@
 #include <linux/stringify.h>
 #include <linux/types.h>
 
-#define JUMP_TABLE_ENTRY(key, label)			\
-	".pushsection __jump_table,  \"a\"\n\t"		\
-	_ASM_ALIGN "\n\t"				\
-	".long 1b - . \n\t"				\
-	".long " label " - . \n\t"			\
-	_ASM_PTR " " key " - . \n\t"			\
+#ifndef COMPILE_OFFSETS
+#include <generated/bounds.h>
+#endif
+
+#define JUMP_TABLE_ENTRY(key, label)				\
+	".pushsection __jump_table,  \"aM\", @progbits, "	\
+	__stringify(JUMP_ENTRY_SIZE) "\n\t"			\
+	_ASM_ALIGN "\n\t"					\
+	".long 1b - . \n\t"					\
+	".long " label " - . \n\t"				\
+	_ASM_PTR " " key " - . \n\t"				\
 	".popsection \n\t"
 
 /* This macro is also expanded on the Rust side. */
kernel/bounds.c +4 -0
index 02b619eb6106..e4c7ded3dc48 100644
@@ -13,6 +13,7 @@
 #include <linux/kbuild.h>
 #include <linux/log2.h>
 #include <linux/spinlock_types.h>
+#include <linux/jump_label.h>
 
 int main(void)
 {
@@ -29,6 +30,9 @@ int main(void)
 #else
 	DEFINE(LRU_GEN_WIDTH, 0);
 	DEFINE(__LRU_REFS_WIDTH, 0);
+#endif
+#if defined(CONFIG_HAVE_ARCH_JUMP_LABEL_RELATIVE) && defined(CONFIG_JUMP_LABEL)
+	DEFINE(JUMP_ENTRY_SIZE, sizeof(struct jump_entry));
 #endif
 	/* End of constants */
 
Signature · 1 line
2.49.0
maintainer
#58 lore raw

Re: [PATCH v3 44/64] x86/jump_label: Define ELF section entry size for jump labels

Quoted 21 lines · Josh Poimboeuf

In preparation for the objtool klp diff subcommand, define the entry size for the __jump_table section in its ELF header. This will allow tooling to extract individual entries.

---
 arch/x86/include/asm/jump_label.h | 17 +++++++++++------
 kernel/bounds.c                   |  4 ++++
 2 files changed, 15 insertions(+), 6 deletions(-)
arch/x86/include/asm/jump_label.h +0 -3
index cd21554b3675..7a6b0e5d85c1 100644
@@ -12,12 +12,17 @@
 #include <linux/stringify.h>
 #include <linux/types.h>
 
-#define JUMP_TABLE_ENTRY(key, label)			\
-	".pushsection __jump_table,  \"a\"\n\t"		\
-	_ASM_ALIGN "\n\t"				\

arch/x86/include/asm/jump_label.h line 21

Quoted 10 lines
arch/x86/include/asm/jump_label.h +7 -3
-	".long 1b - . \n\t"				\
-	".long " label " - . \n\t"			\
-	_ASM_PTR " " key " - . \n\t"			\
+#ifndef COMPILE_OFFSETS
+#include <generated/bounds.h>
+#endif
+
+#define JUMP_TABLE_ENTRY(key, label)				\
+	".pushsection __jump_table,  \"aM\", @progbits, "	\
+	__stringify(JUMP_ENTRY_SIZE) "\n\t"			\

Argh, can you please not do this line-break. Yes it'll be long, but this is most confusing.

Quoted 20 lines
arch/x86/include/asm/jump_label.h +4 -0
+	_ASM_ALIGN "\n\t"					\
+	".long 1b - . \n\t"					\
+	".long " label " - . \n\t"				\
+	_ASM_PTR " " key " - . \n\t"				\
 	".popsection \n\t"
 
 /* This macro is also expanded on the Rust side. */
kernel/bounds.c +1 -0
index 02b619eb6106..e4c7ded3dc48 100644
@@ -13,6 +13,7 @@
 #include <linux/kbuild.h>
 #include <linux/log2.h>
 #include <linux/spinlock_types.h>
+#include <linux/jump_label.h>
 
 int main(void)
 {

kernel/bounds.c int main(void) · line 34

Quoted 5 lines
kernel/bounds.c +2 -0
@@ -29,6 +30,9 @@ int main(void)
 #else
 	DEFINE(LRU_GEN_WIDTH, 0);
 	DEFINE(__LRU_REFS_WIDTH, 0);
+#endif
+#if defined(CONFIG_HAVE_ARCH_JUMP_LABEL_RELATIVE) && defined(CONFIG_JUMP_LABEL)

How is HAVE_ARCH_JUMP_LABEL_RELATIVE relevant here?

Quoted 7 lines
kernel/bounds.c +1 -0
+	DEFINE(JUMP_ENTRY_SIZE, sizeof(struct jump_entry));
 #endif
 	/* End of constants */
 
Signature · 1 line
2.49.0
author maintainer
#59 lore raw

Re: [PATCH v3 44/64] x86/jump_label: Define ELF section entry size for jump labels

Quoted 6 lines · Peter Zijlstra

arch/x86/include/asm/jump_label.h line 21

Quoted 3 lines
arch/x86/include/asm/jump_label.h +3 -0
+#define JUMP_TABLE_ENTRY(key, label)				\
+	".pushsection __jump_table,  \"aM\", @progbits, "	\
+	__stringify(JUMP_ENTRY_SIZE) "\n\t"			\

Argh, can you please not do this line-break. Yes it'll be long, but this is most confusing.

Yeah, I'll go indent that like the extable one.

Quoted 8 lines

kernel/bounds.c int main(void) · line 34

Quoted 6 lines
kernel/bounds.c +2 -0
@@ -29,6 +30,9 @@ int main(void)
 #else
 	DEFINE(LRU_GEN_WIDTH, 0);
 	DEFINE(__LRU_REFS_WIDTH, 0);
+#endif
+#if defined(CONFIG_HAVE_ARCH_JUMP_LABEL_RELATIVE) && defined(CONFIG_JUMP_LABEL)

How is HAVE_ARCH_JUMP_LABEL_RELATIVE relevant here?

#ifdef CONFIG_HAVE_ARCH_JUMP_LABEL_RELATIVE

struct jump_entry {
	s32 code;
	s32 target;
	long key;	// key may be far away from the core kernel under KASLR
};
Signature · 1 line
Josh
maintainer
#60 lore raw

Re: [PATCH v3 44/64] x86/jump_label: Define ELF section entry size for jump labels

Quoted 19 lines · Josh Poimboeuf
Quoted 6 lines · Peter Zijlstra

arch/x86/include/asm/jump_label.h line 21

Quoted 3 lines
arch/x86/include/asm/jump_label.h +3 -0
+#define JUMP_TABLE_ENTRY(key, label)				\
+	".pushsection __jump_table,  \"aM\", @progbits, "	\
+	__stringify(JUMP_ENTRY_SIZE) "\n\t"			\

Argh, can you please not do this line-break. Yes it'll be long, but this is most confusing.

Yeah, I'll go indent that like the extable one.

Quoted 8 lines

kernel/bounds.c int main(void) · line 34

Quoted 6 lines
kernel/bounds.c +2 -0
@@ -29,6 +30,9 @@ int main(void)
 #else
 	DEFINE(LRU_GEN_WIDTH, 0);
 	DEFINE(__LRU_REFS_WIDTH, 0);
+#endif
+#if defined(CONFIG_HAVE_ARCH_JUMP_LABEL_RELATIVE) && defined(CONFIG_JUMP_LABEL)

How is HAVE_ARCH_JUMP_LABEL_RELATIVE relevant here?

Quoted 7 lines

#ifdef CONFIG_HAVE_ARCH_JUMP_LABEL_RELATIVE

struct jump_entry {
	s32 code;
	s32 target;
	long key;	// key may be far away from the core kernel under KASLR
};

Well,... that's odd. I was sure there was a !HAVE_ARCH_JUMP_LABEL_RELATIVE version somewhere... Oh well.

author maintainer
#61 lore raw

PATCH v3 45/64 x86/static_call: Define ELF section entry size of static calls

In reply to Josh Poimboeuf

In preparation for the objtool klp diff subcommand, define the entry size for the .static_call_sites section in its ELF header. This will allow tooling to extract individual entries.

---
 arch/x86/include/asm/static_call.h      |  3 ++-
 include/linux/static_call.h             |  6 ------
 include/linux/static_call_types.h       |  6 ++++++
 kernel/bounds.c                         |  4 ++++
 tools/include/linux/static_call_types.h |  6 ++++++
 tools/objtool/check.c                   | 11 +++++++++--
 6 files changed, 27 insertions(+), 9 deletions(-)
arch/x86/include/asm/static_call.h +2 -1
index 41502bd2afd6..e03ad9bbbf59 100644
@@ -58,7 +58,8 @@
 	ARCH_DEFINE_STATIC_CALL_TRAMP(name, __static_call_return0)
 
 #define ARCH_ADD_TRAMP_KEY(name)					\
-	asm(".pushsection .static_call_tramp_key, \"a\"		\n"	\
+	asm(".pushsection .static_call_tramp_key, \"aM\", @progbits, "	\
+	    __stringify(STATIC_CALL_TRAMP_KEY_SIZE) "\n"		\
 	    ".long " STATIC_CALL_TRAMP_STR(name) " - .		\n"	\
 	    ".long " STATIC_CALL_KEY_STR(name) " - .		\n"	\
 	    ".popsection					\n")
include/linux/static_call.h +0 -6
index 78a77a4ae0ea..5210612817f2 100644
@@ -172,12 +172,6 @@ struct static_call_mod {
 	struct static_call_site *sites;
 };
 
-/* For finding the key associated with a trampoline */
-struct static_call_tramp_key {
-	s32 tramp;
-	s32 key;
-};
-
 extern void __static_call_update(struct static_call_key *key, void *tramp, void *func);
 extern int static_call_mod_init(struct module *mod);
 extern int static_call_text_reserved(void *start, void *end);
include/linux/static_call_types.h +6 -0
index 5a00b8b2cf9f..eb772df625d4 100644
@@ -34,6 +34,12 @@ struct static_call_site {
 	s32 key;
 };
 
+/* For finding the key associated with a trampoline */
+struct static_call_tramp_key {
+	s32 tramp;
+	s32 key;
+};
+
 #define DECLARE_STATIC_CALL(name, func)					\
 	extern struct static_call_key STATIC_CALL_KEY(name);		\
 	extern typeof(func) STATIC_CALL_TRAMP(name);
kernel/bounds.c +4 -0
index e4c7ded3dc48..21c37e3ea629 100644
@@ -14,6 +14,7 @@
 #include <linux/log2.h>
 #include <linux/spinlock_types.h>
 #include <linux/jump_label.h>
+#include <linux/static_call_types.h>
 
 int main(void)
 {
@@ -33,6 +34,9 @@ int main(void)
 #endif
 #if defined(CONFIG_HAVE_ARCH_JUMP_LABEL_RELATIVE) && defined(CONFIG_JUMP_LABEL)
 	DEFINE(JUMP_ENTRY_SIZE, sizeof(struct jump_entry));
+#endif
+#ifdef CONFIG_HAVE_STATIC_CALL_INLINE
+	DEFINE(STATIC_CALL_TRAMP_KEY_SIZE, sizeof(struct static_call_tramp_key));
 #endif
 	/* End of constants */
 
tools/include/linux/static_call_types.h +6 -0
index 5a00b8b2cf9f..eb772df625d4 100644
@@ -34,6 +34,12 @@ struct static_call_site {
 	s32 key;
 };
 
+/* For finding the key associated with a trampoline */
+struct static_call_tramp_key {
+	s32 tramp;
+	s32 key;
+};
+
 #define DECLARE_STATIC_CALL(name, func)					\
 	extern struct static_call_key STATIC_CALL_KEY(name);		\
 	extern typeof(func) STATIC_CALL_TRAMP(name);
tools/objtool/check.c +9 -2
index 3afc748ba516..c3e1ca3dba06 100644
@@ -650,8 +650,15 @@ static int create_static_call_sections(struct objtool_file *file)
 	if (!sec)
 		return -1;
 
-	/* Allow modules to modify the low bits of static_call_site::key */
-	sec->sh.sh_flags |= SHF_WRITE;
+	/*
+	 * Set SHF_MERGE to prevent tooling from stripping entsize.
+	 *
+	 * SHF_WRITE would also get set here to allow modules to modify the low
+	 * bits of static_call_site::key, but the LLVM linker doesn't allow
+	 * SHF_MERGE+SHF_WRITE for whatever reason.  That gets fixed up by the
+	 * makefiles with CONFIG_NEED_MODULE_PERMISSIONS_FIX.
+	 */
+	sec->sh.sh_flags |= SHF_MERGE;
 
 	idx = 0;
 	list_for_each_entry(insn, &file->static_call_list, call_node) {
Signature · 1 line
2.49.0
maintainer
#62 lore raw

Re: [PATCH v3 45/64] x86/static_call: Define ELF section entry size of static calls

Quoted 19 lines · Josh Poimboeuf

In preparation for the objtool klp diff subcommand, define the entry size for the .static_call_sites section in its ELF header. This will allow tooling to extract individual entries.

---
 arch/x86/include/asm/static_call.h      |  3 ++-
 include/linux/static_call.h             |  6 ------
 include/linux/static_call_types.h       |  6 ++++++
 kernel/bounds.c                         |  4 ++++
 tools/include/linux/static_call_types.h |  6 ++++++
 tools/objtool/check.c                   | 11 +++++++++--
 6 files changed, 27 insertions(+), 9 deletions(-)
arch/x86/include/asm/static_call.h +0 -0
index 41502bd2afd6..e03ad9bbbf59 100644

arch/x86/include/asm/static_call.h line 62

Quoted 6 lines
arch/x86/include/asm/static_call.h +2 -1
@@ -58,7 +58,8 @@
 	ARCH_DEFINE_STATIC_CALL_TRAMP(name, __static_call_return0)
 
 #define ARCH_ADD_TRAMP_KEY(name)					\
-	asm(".pushsection .static_call_tramp_key, \"a\"		\n"	\
+	asm(".pushsection .static_call_tramp_key, \"aM\", @progbits, "	\
+	    __stringify(STATIC_CALL_TRAMP_KEY_SIZE) "\n"		\

More horrifically confusing indenting.

Quoted 3 lines
arch/x86/include/asm/static_call.h +0 -0
 	    ".long " STATIC_CALL_TRAMP_STR(name) " - .		\n"	\
 	    ".long " STATIC_CALL_KEY_STR(name) " - .		\n"	\
 	    ".popsection					\n")
author maintainer
#63 lore raw

PATCH v3 46/64 x86/extable: Define ELF section entry size for exception table

In reply to Josh Poimboeuf

In preparation for the objtool klp diff subcommand, define the entry size for the __ex_table section in its ELF header. This will allow tooling to extract individual entries.

---
 arch/um/include/asm/Kbuild                 |  1 -
 arch/um/include/shared/common-offsets.h    |  1 +
 arch/x86/include/asm/asm.h                 | 18 ++++++++++--------
 arch/x86/kernel/asm-offsets.c              |  1 +
 arch/x86/um/shared/sysdep/kernel-offsets.h |  1 +
 5 files changed, 13 insertions(+), 9 deletions(-)
arch/um/include/asm/Kbuild +0 -1
index 04ab3b653a48..1934fa0df888 100644
@@ -5,7 +5,6 @@ generic-y += device.h
 generic-y += dma-mapping.h
 generic-y += emergency-restart.h
 generic-y += exec.h
-generic-y += extable.h
 generic-y += ftrace.h
 generic-y += hw_irq.h
 generic-y += irq_regs.h
arch/um/include/shared/common-offsets.h +1 -0
index 4e19103afd71..a6f77cb6aa7e 100644
@@ -20,3 +20,4 @@ DEFINE(UM_KERN_GDT_ENTRY_TLS_ENTRIES, GDT_ENTRY_TLS_ENTRIES);
 DEFINE(UM_SECCOMP_ARCH_NATIVE, SECCOMP_ARCH_NATIVE);
 
 DEFINE(ALT_INSTR_SIZE, sizeof(struct alt_instr));
+DEFINE(EXTABLE_SIZE,   sizeof(struct exception_table_entry));
arch/x86/include/asm/asm.h +10 -8
index 1f26f90a57ce..e9b6d2d006c6 100644
@@ -145,12 +145,12 @@ static __always_inline __pure void *rip_rel_ptr(void *p)
 /* Exception table entry */
 #ifdef __ASSEMBLER__
 
-# define _ASM_EXTABLE_TYPE(from, to, type)			\
-	.pushsection "__ex_table","a" ;				\
-	.balign 4 ;						\
-	.long (from) - . ;					\
-	.long (to) - . ;					\
-	.long type ;						\
+# define _ASM_EXTABLE_TYPE(from, to, type)				\
+	.pushsection "__ex_table", "aM", @progbits, EXTABLE_SIZE;	\
+	.balign 4 ;							\
+	.long (from) - . ;						\
+	.long (to) - . ;						\
+	.long type ;							\
 	.popsection
 
 # ifdef CONFIG_KPROBES
@@ -193,7 +193,8 @@ static __always_inline __pure void *rip_rel_ptr(void *p)
 	".purgem extable_type_reg\n"
 
 # define _ASM_EXTABLE_TYPE(from, to, type)			\
-	" .pushsection \"__ex_table\",\"a\"\n"			\
+	" .pushsection __ex_table, \"aM\", @progbits, "		\
+		       __stringify(EXTABLE_SIZE) "\n"		\
 	" .balign 4\n"						\
 	" .long (" #from ") - .\n"				\
 	" .long (" #to ") - .\n"				\
@@ -201,7 +202,8 @@ static __always_inline __pure void *rip_rel_ptr(void *p)
 	" .popsection\n"
 
 # define _ASM_EXTABLE_TYPE_REG(from, to, type, reg)				\
-	" .pushsection \"__ex_table\",\"a\"\n"					\
+	" .pushsection __ex_table, \"aM\", @progbits, "				\
+		       __stringify(EXTABLE_SIZE) "\n"				\
 	" .balign 4\n"								\
 	" .long (" #from ") - .\n"						\
 	" .long (" #to ") - .\n"						\
arch/x86/kernel/asm-offsets.c +1 -0
index b51625c3f64c..3d3eef7fae32 100644
@@ -125,4 +125,5 @@ static void __used common(void)
 
 	BLANK();
 	DEFINE(ALT_INSTR_SIZE,	 sizeof(struct alt_instr));
+	DEFINE(EXTABLE_SIZE,	 sizeof(struct exception_table_entry));
 }
arch/x86/um/shared/sysdep/kernel-offsets.h +1 -0
index 9f6d3d1a248c..8215a0200ddd 100644
@@ -8,6 +8,7 @@
 #include <linux/audit.h>
 #include <asm/mman.h>
 #include <asm/seccomp.h>
+#include <asm/extable.h>
 
 /* workaround for a warning with -Wmissing-prototypes */
 void foo(void);
Signature · 1 line
2.49.0
maintainer
#64 lore raw

Re: [PATCH v3 46/64] x86/extable: Define ELF section entry size for exception table

Quoted 11 lines · Josh Poimboeuf
arch/x86/include/asm/asm.h +2 -1
@@ -193,7 +193,8 @@ static __always_inline __pure void *rip_rel_ptr(void *p)
 	".purgem extable_type_reg\n"
 
 # define _ASM_EXTABLE_TYPE(from, to, type)			\
-	" .pushsection \"__ex_table\",\"a\"\n"			\
+	" .pushsection __ex_table, \"aM\", @progbits, "		\
+		       __stringify(EXTABLE_SIZE) "\n"		\
 	" .balign 4\n"						\
 	" .long (" #from ") - .\n"				\
 	" .long (" #to ") - .\n"				\

arch/x86/include/asm/asm.h static __always_inline __pure void *rip_rel_ptr(void *p) · line 209

Quoted 9 lines
arch/x86/include/asm/asm.h +2 -1
@@ -201,7 +202,8 @@ static __always_inline __pure void *rip_rel_ptr(void *p)
 	" .popsection\n"
 
 # define _ASM_EXTABLE_TYPE_REG(from, to, type, reg)				\
-	" .pushsection \"__ex_table\",\"a\"\n"					\
+	" .pushsection __ex_table, \"aM\", @progbits, "				\
+		       __stringify(EXTABLE_SIZE) "\n"				\
 	" .balign 4\n"								\
 	" .long (" #from ") - .\n"						\
 	" .long (" #to ") - .\n"						\

This style is much better.

author maintainer
#65 lore raw

PATCH v3 47/64 x86/bug: Define ELF section entry size for bug table

In reply to Josh Poimboeuf

In preparation for the objtool klp diff subcommand, define the entry size for the __bug_table section in its ELF header. This will allow tooling to extract individual entries.

---
 arch/x86/include/asm/bug.h | 45 ++++++++++++++++++++------------------
 1 file changed, 24 insertions(+), 21 deletions(-)
arch/x86/include/asm/bug.h +24 -21
index 59e155ee3c76..3aa28c687696 100644
@@ -41,33 +41,36 @@
 
 #define _BUG_FLAGS(cond_str, ins, flags, extra)				\
 do {									\
-	asm_inline volatile("1:\t" ins "\n"				\
-		     ".pushsection __bug_table,\"a\"\n"			\
-		     "2:\t" __BUG_REL(1b) "\t# bug_entry::bug_addr\n"	\
-		     "\t"  __BUG_REL(%c0) "\t# bug_entry::file\n"	\
-		     "\t.word %c1"        "\t# bug_entry::line\n"	\
-		     "\t.word %c2"        "\t# bug_entry::flags\n"	\
-		     "\t.org 2b+%c3\n"					\
-		     ".popsection\n"					\
-		     extra						\
-		     : : "i" (WARN_CONDITION_STR(cond_str) __FILE__), "i" (__LINE__),		\
-			 "i" (flags),					\
-			 "i" (sizeof(struct bug_entry)));		\
+	asm_inline volatile(						\
+		"1:\t" ins "\n"						\
+		".pushsection __bug_table, \"aM\", @progbits, %c3\n"	\
+		"2:\t" __BUG_REL(1b) "\t# bug_entry::bug_addr\n"	\
+		"\t"  __BUG_REL(%c0) "\t# bug_entry::file\n"		\
+		"\t.word %c1"        "\t# bug_entry::line\n"		\
+		"\t.word %c2"        "\t# bug_entry::flags\n"		\
+		"\t.org 2b+%c3\n"					\
+		".popsection\n"						\
+		extra							\
+		: : "i" (WARN_CONDITION_STR(cond_str) __FILE__),	\
+		    "i" (__LINE__),					\
+		    "i" (flags),					\
+		    "i" (sizeof(struct bug_entry)));			\
 } while (0)
 
 #else /* !CONFIG_DEBUG_BUGVERBOSE */
 
 #define _BUG_FLAGS(cond_str, ins, flags, extra)				\
 do {									\
-	asm_inline volatile("1:\t" ins "\n"				\
-		     ".pushsection __bug_table,\"a\"\n"			\
-		     "2:\t" __BUG_REL(1b) "\t# bug_entry::bug_addr\n"	\
-		     "\t.word %c0"        "\t# bug_entry::flags\n"	\
-		     "\t.org 2b+%c1\n"					\
-		     ".popsection\n"					\
-		     extra						\
-		     : : "i" (flags),					\
-			 "i" (sizeof(struct bug_entry)));		\
+	asm_inline volatile(						\
+		"1:\t" ins "\n"						\
+		".pushsection __bug_table, \"aM\", @progbits, %c1\n"	\
+		"2:\t" __BUG_REL(1b) "\t# bug_entry::bug_addr\n"	\
+		"\t.word %c0"        "\t# bug_entry::flags\n"		\
+		"\t.org 2b+%c1\n"					\
+		".popsection\n"						\
+		extra							\
+		: : "i" (flags),					\
+		    "i" (sizeof(struct bug_entry)));			\
 } while (0)
 
 #endif /* CONFIG_DEBUG_BUGVERBOSE */
Signature · 1 line
2.49.0
author maintainer
#66 lore raw

PATCH v3 48/64 x86/orc: Define ELF section entry size for unwind hints

In reply to Josh Poimboeuf

In preparation for the objtool klp diff subcommand, define the entry size for the discard.unwind_hints section in its ELF header. This will allow tooling to extract individual entries.

---
 include/linux/objtool.h | 11 ++++++++---
 kernel/bounds.c         |  4 ++++
 2 files changed, 12 insertions(+), 3 deletions(-)
include/linux/objtool.h +8 -3
index 366ad004d794..c7a3851ae4ae 100644
@@ -2,6 +2,10 @@
 #ifndef _LINUX_OBJTOOL_H
 #define _LINUX_OBJTOOL_H
 
+#ifndef COMPILE_OFFSETS
+#include <generated/bounds.h>
+#endif
+
 #include <linux/objtool_types.h>
 
 #ifdef CONFIG_OBJTOOL
@@ -10,9 +14,10 @@
 
 #ifndef __ASSEMBLY__
 
-#define UNWIND_HINT(type, sp_reg, sp_offset, signal)	\
+#define UNWIND_HINT(type, sp_reg, sp_offset, signal)		\
 	"987: \n\t"						\
-	".pushsection .discard.unwind_hints\n\t"		\
+	".pushsection .discard.unwind_hints, \"M\", @progbits, "\
+		      __stringify(UNWIND_HINT_SIZE) "\n\t"	\
 	/* struct unwind_hint */				\
 	".long 987b - .\n\t"					\
 	".short " __stringify(sp_offset) "\n\t"			\
@@ -88,7 +93,7 @@
  */
 .macro UNWIND_HINT type:req sp_reg=0 sp_offset=0 signal=0
 .Lhere_\@:
-	.pushsection .discard.unwind_hints
+	.pushsection .discard.unwind_hints, "M", @progbits, UNWIND_HINT_SIZE
 		/* struct unwind_hint */
 		.long .Lhere_\@ - .
 		.short \sp_offset
kernel/bounds.c +4 -0
index 21c37e3ea629..f9bc13727721 100644
@@ -15,6 +15,7 @@
 #include <linux/spinlock_types.h>
 #include <linux/jump_label.h>
 #include <linux/static_call_types.h>
+#include <linux/objtool_types.h>
 
 int main(void)
 {
@@ -37,6 +38,9 @@ int main(void)
 #endif
 #ifdef CONFIG_HAVE_STATIC_CALL_INLINE
 	DEFINE(STATIC_CALL_TRAMP_KEY_SIZE, sizeof(struct static_call_tramp_key));
+#endif
+#ifdef CONFIG_OBJTOOL
+	DEFINE(UNWIND_HINT_SIZE, sizeof(struct unwind_hint));
 #endif
 	/* End of constants */
 
Signature · 1 line
2.49.0
author maintainer
#67 lore raw

PATCH v3 49/64 objtool: Unify STACK_FRAME_NON_STANDARD entry sizes

In reply to Josh Poimboeuf

The C implementation of STACK_FRAME_NON_STANDARD emits 8-byte entries, whereas the asm version's entries are only 4 bytes.

Make them consistent by converting the asm version to 8-byte entries.

This is much easier than converting the C version to 4-bytes, which would require awkwardly putting inline asm in a dummy function in order to pass the 'func' pointer to the asm.

---
 include/linux/objtool.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
include/linux/objtool.h +1 -1
index c7a3851ae4ae..7d7bb7f1af69 100644
@@ -106,7 +106,7 @@
 
 .macro STACK_FRAME_NON_STANDARD func:req
 	.pushsection .discard.func_stack_frame_non_standard, "aw"
-	.long \func - .
+	.quad \func
 	.popsection
 .endm
 
Signature · 1 line
2.49.0
author maintainer
#68 lore raw

PATCH v3 50/64 objtool/klp: Add --checksum option to generate per-function checksums

In reply to Josh Poimboeuf

In preparation for the objtool klp diff subcommand, add a command-line option to generate a unique checksum for each function. This will enable detection of functions which have changed between two versions of an object file.

---
 tools/objtool/Makefile                        |  38 +++--
 tools/objtool/builtin-check.c                 |  12 +-
 tools/objtool/check.c                         | 140 +++++++++++++++++-
 tools/objtool/elf.c                           |  46 +++++-
 tools/objtool/include/objtool/builtin.h       |   3 +-
 tools/objtool/include/objtool/check.h         |   5 +-
 tools/objtool/include/objtool/checksum.h      |  42 ++++++
 .../objtool/include/objtool/checksum_types.h  |  25 ++++
 tools/objtool/include/objtool/elf.h           |   5 +-
 tools/objtool/include/objtool/objtool.h       |   2 +
 10 files changed, 292 insertions(+), 26 deletions(-)
 create mode 100644 tools/objtool/include/objtool/checksum.h
 create mode 100644 tools/objtool/include/objtool/checksum_types.h
tools/objtool/Makefile +24 -14
index fc82d47f2b9a..958761c05b7c 100644
@@ -2,6 +2,27 @@
 include ../scripts/Makefile.include
 include ../scripts/Makefile.arch
 
+ifeq ($(SRCARCH),x86)
+	BUILD_ORC    := y
+	ARCH_HAS_KLP := y
+endif
+
+ifeq ($(SRCARCH),loongarch)
+	BUILD_ORC	   := y
+endif
+
+ifeq ($(ARCH_HAS_KLP),y)
+	HAVE_XXHASH = $(shell echo "int main() {}" | \
+		      $(HOSTCC) -xc - -o /dev/null -lxxhash 2> /dev/null && echo y || echo n)
+	ifeq ($(HAVE_XXHASH),y)
+		LIBXXHASH_CFLAGS := $(shell $(HOSTPKG_CONFIG) libxxhash --cflags 2>/dev/null) \
+				    -DBUILD_KLP
+		LIBXXHASH_LIBS   := $(shell $(HOSTPKG_CONFIG) libxxhash --libs 2>/dev/null || echo -lxxhash)
+	endif
+endif
+
+export BUILD_ORC
+
 ifeq ($(srctree),)
 srctree := $(patsubst %/,%,$(dir $(CURDIR)))
 srctree := $(patsubst %/,%,$(dir $(srctree)))
@@ -36,10 +57,10 @@ INCLUDES := -I$(srctree)/tools/include \
 	    -I$(srctree)/tools/objtool/arch/$(SRCARCH)/include \
 	    -I$(LIBSUBCMD_OUTPUT)/include
 
-OBJTOOL_CFLAGS  := -std=gnu11 -fomit-frame-pointer -O2 -g \
-		   $(WARNINGS) $(INCLUDES) $(LIBELF_FLAGS) $(HOSTCFLAGS)
+OBJTOOL_CFLAGS  := -std=gnu11 -fomit-frame-pointer -O2 -g $(WARNINGS)	\
+		   $(INCLUDES) $(LIBELF_FLAGS) $(LIBXXHASH_CFLAGS) $(HOSTCFLAGS)
 
-OBJTOOL_LDFLAGS := $(LIBSUBCMD) $(LIBELF_LIBS) $(HOSTLDFLAGS)
+OBJTOOL_LDFLAGS := $(LIBSUBCMD) $(LIBELF_LIBS) $(LIBXXHASH_LIBS) $(HOSTLDFLAGS)
 
 # Allow old libelf to be used:
 elfshdr := $(shell echo '$(pound)include <libelf.h>' | $(HOSTCC) $(OBJTOOL_CFLAGS) -x c -E - 2>/dev/null | grep elf_getshdr)
@@ -51,17 +72,6 @@ HOST_OVERRIDES := CC="$(HOSTCC)" LD="$(HOSTLD)" AR="$(HOSTAR)"
 AWK = awk
 MKDIR = mkdir
 
-BUILD_ORC := n
-
-ifeq ($(SRCARCH),x86)
-	BUILD_ORC := y
-endif
-
-ifeq ($(SRCARCH),loongarch)
-	BUILD_ORC := y
-endif
-
-export BUILD_ORC
 export srctree OUTPUT CFLAGS SRCARCH AWK
 include $(srctree)/tools/build/Makefile.include
 
tools/objtool/builtin-check.c +11 -1
index 3172f7e98147..331c6e1dc04c 100644
@@ -73,6 +73,7 @@ static int parse_hacks(const struct option *opt, const char *str, int unset)
 
 static const struct option check_options[] = {
 	OPT_GROUP("Actions:"),
+	OPT_BOOLEAN(0,		 "checksum", &opts.checksum, "generate per-function checksums"),
 	OPT_BOOLEAN(0  ,	 "cfi", &opts.cfi, "annotate kernel control flow integrity (kCFI) function preambles"),
 	OPT_CALLBACK_OPTARG('h', "hacks", NULL, NULL, "jump_label,noinstr,skylake", "patch toolchain bugs/limitations", parse_hacks),
 	OPT_BOOLEAN('i',	 "ibt", &opts.ibt, "validate and annotate IBT"),
@@ -159,7 +160,16 @@ static bool opts_valid(void)
 		return false;
 	}
 
-	if (opts.hack_jump_label	||
+
+#ifndef BUILD_KLP
+	if (opts.checksum) {
+		ERROR("--checksum not supported; install xxhash-devel and recompile");
+		return false;
+	}
+#endif
+
+	if (opts.checksum		||
+	    opts.hack_jump_label	||
 	    opts.hack_noinstr		||
 	    opts.ibt			||
 	    opts.mcount			||
tools/objtool/check.c +136 -4

214 lines not on this page · raw message

tools/objtool/elf.c +43 -3

76 lines not on this page · raw message

tools/objtool/include/objtool/builtin.h +2 -1
index de6c08f8e060..06083e8c5c91 100644
@@ -9,6 +9,7 @@
 
 struct opts {
 	/* actions: */
+	bool cfi;
 	bool dump_orc;
 	bool hack_jump_label;
 	bool hack_noinstr;
@@ -23,9 +24,9 @@ struct opts {
 	bool sls;
 	bool stackval;
 	bool static_call;
+	bool checksum;
 	bool uaccess;
 	int prefix;
-	bool cfi;
 
 	/* options: */
 	bool backtrace;
tools/objtool/include/objtool/check.h +3 -2
index 0f4e7ac929ef..d73b0c3ae1ee 100644
@@ -65,8 +65,9 @@ struct instruction {
 	    unret		: 1,
 	    visited		: 4,
 	    no_reloc		: 1,
-	    hole		: 1;
-		/* 10 bit hole */
+	    hole		: 1,
+	    fake		: 1;
+		/* 9 bit hole */
 
 	struct alt_group *alt_group;
 	struct instruction *jump_dest;
tools/objtool/include/objtool/checksum.h new file +42 -0

42 lines not on this page · raw message

tools/objtool/include/objtool/checksum_types.h new file +25 -0
new file mode 100644
index 000000000000..507efdd8ab5b
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _OBJTOOL_CHECKSUM_TYPES_H
+#define _OBJTOOL_CHECKSUM_TYPES_H
+
+struct sym_checksum {
+	u64 addr;
+	u64 checksum;
+};
+
+#ifdef BUILD_KLP
+
+#include <xxhash.h>
+
+struct checksum {
+	XXH3_state_t *state;
+	XXH64_hash_t checksum;
+};
+
+#else /* !BUILD_KLP */
+
+struct checksum {};
+
+#endif /* !BUILD_KLP */
+
+#endif /* _OBJTOOL_CHECKSUM_TYPES_H */
tools/objtool/include/objtool/elf.h +4 -1
index a0fc252e1993..4d1023fdb700 100644
@@ -13,6 +13,8 @@
 #include <linux/hashtable.h>
 #include <linux/rbtree.h>
 #include <linux/jhash.h>
+
+#include <objtool/checksum_types.h>
 #include <arch/elf.h>
 
 #define SYM_NAME_LEN		512
@@ -57,7 +59,7 @@ struct symbol {
 	struct elf_hash_node name_hash;
 	GElf_Sym sym;
 	struct section *sec;
-	const char *name;
+	const char *name, *demangled_name;
 	unsigned int idx, len;
 	unsigned long offset;
 	unsigned long __subtree_last;
@@ -79,6 +81,7 @@ struct symbol {
 	struct list_head pv_target;
 	struct reloc *relocs;
 	struct section *group_sec;
+	struct checksum csum;
 };
 
 struct reloc {
tools/objtool/include/objtool/objtool.h +2 -0
index c0dc86a78ff6..90c591b5bd68 100644
@@ -14,6 +14,8 @@
 
 #define __weak __attribute__((weak))
 
+#define SYM_CHECKSUM_SEC ".discard.sym_checksum"
+
 struct pv_state {
 	bool clean;
 	struct list_head targets;
Signature · 1 line
2.49.0
author maintainer
#69 lore raw

PATCH v3 51/64 objtool/klp: Add --debug-checksum=<funcs> to show per-instruction checksums

In reply to Josh Poimboeuf

Add a --debug-checksum=<funcs> option to the check subcommand to print the calculated checksum of each instruction in the given functions.

This is useful for determining where two versions of a function begin to diverge.

---
 tools/objtool/builtin-check.c            |  6 ++++
 tools/objtool/check.c                    | 42 ++++++++++++++++++++++++
 tools/objtool/include/objtool/builtin.h  |  1 +
 tools/objtool/include/objtool/checksum.h |  1 +
 tools/objtool/include/objtool/elf.h      |  1 +
 tools/objtool/include/objtool/warn.h     | 19 +++++++++++
 6 files changed, 70 insertions(+)
tools/objtool/builtin-check.c +6 -0
index 331c6e1dc04c..cd2c4a387100 100644
@@ -93,6 +93,7 @@ static const struct option check_options[] = {
 	OPT_GROUP("Options:"),
 	OPT_BOOLEAN(0,		 "backtrace", &opts.backtrace, "unwind on error"),
 	OPT_BOOLEAN(0,		 "backup", &opts.backup, "create backup (.orig) file on warning/error"),
+	OPT_STRING(0,		 "debug-checksum", &opts.debug_checksum,  "funcs", "enable checksum debug output"),
 	OPT_BOOLEAN(0,		 "dry-run", &opts.dryrun, "don't write modifications"),
 	OPT_BOOLEAN(0,		 "link", &opts.link, "object is a linked object"),
 	OPT_BOOLEAN(0,		 "module", &opts.module, "object is part of a kernel module"),
@@ -168,6 +169,11 @@ static bool opts_valid(void)
 	}
 #endif
 
+	if (opts.debug_checksum && !opts.checksum) {
+		ERROR("--debug-checksum requires --checksum");
+		return false;
+	}
+
 	if (opts.checksum		||
 	    opts.hack_jump_label	||
 	    opts.hack_noinstr		||
tools/objtool/check.c +42 -0

54 lines not on this page · raw message

tools/objtool/include/objtool/builtin.h +1 -0
index 06083e8c5c91..92bc7089cfc1 100644
@@ -31,6 +31,7 @@ struct opts {
 	/* options: */
 	bool backtrace;
 	bool backup;
+	const char *debug_checksum;
 	bool dryrun;
 	bool link;
 	bool mnop;
tools/objtool/include/objtool/checksum.h +1 -0
index 927ca74b5c39..7fe21608722a 100644
@@ -19,6 +19,7 @@ static inline void checksum_update(struct symbol *func,
 				   const void *data, size_t size)
 {
 	XXH3_64bits_update(func->csum.state, data, size);
+	dbg_checksum(func, insn, XXH3_64bits_digest(func->csum.state));
 }
 
 static inline void checksum_finish(struct symbol *func)
tools/objtool/include/objtool/elf.h +1 -0
index 4d1023fdb700..4cfd09e66cb5 100644
@@ -78,6 +78,7 @@ struct symbol {
 	u8 ignore	     : 1;
 	u8 cold		     : 1;
 	u8 prefix	     : 1;
+	u8 debug_checksum    : 1;
 	struct list_head pv_target;
 	struct reloc *relocs;
 	struct section *group_sec;
tools/objtool/include/objtool/warn.h +19 -0
index cb8fe846d9dd..29173a1368d7 100644
@@ -102,4 +102,23 @@ static inline char *offstr(struct section *sec, unsigned long offset)
 #define ERROR_FUNC(sec, offset, format, ...) __WARN_FUNC(ERROR_STR, sec, offset, format, ##__VA_ARGS__)
 #define ERROR_INSN(insn, format, ...) WARN_FUNC(insn->sec, insn->offset, format, ##__VA_ARGS__)
 
+
+#define __dbg(format, ...)						\
+	fprintf(stderr,							\
+		"DEBUG: %s%s" format "\n",				\
+		objname ?: "",						\
+		objname ? ": " : "",					\
+		##__VA_ARGS__)
+
+#define dbg_checksum(func, insn, checksum)				\
+({									\
+	if (unlikely(insn->sym && insn->sym->pfunc &&			\
+		     insn->sym->pfunc->debug_checksum)) {		\
+		char *insn_off = offstr(insn->sec, insn->offset);	\
+		__dbg("checksum: %s %s %016lx",				\
+		      func->name, insn_off, checksum);			\
+		free(insn_off);						\
+	}								\
+})
+
 #endif /* _WARN_H */
Signature · 1 line
2.49.0
author maintainer
#70 lore raw

PATCH v3 52/64 objtool/klp: Introduce klp diff subcommand for diffing object files

In reply to Josh Poimboeuf

Add a new klp diff subcommand which performs a binary diff between two object files and extracts changed functions into a new object which can then be linked into a livepatch module.

This builds on concepts from the longstanding out-of-tree kpatch [1] project which began in 2012 and has been used for many years to generate livepatch modules for production kernels. However, this is a complete rewrite which incorporates hard-earned lessons from 12+ years of maintaining kpatch.

Key improvements compared to kpatch-build:

- Integrated with objtool: Leverages objtool's existing control-flow graph analysis to help detect changed functions.

- Works on vmlinux.o: Supports late-linked objects, making it compatible with LTO, IBT, and similar.

- Simplified code base: ~3k fewer lines of code.

- Upstream: No more out-of-tree #ifdef hacks, far less cruft.

- Cleaner internals: Vastly simplified logic for symbol/section/reloc inclusion and special section extraction.

- Robust __LINE__ macro handling: Avoids false positive binary diffs caused by the __LINE__ macro by introducing a fix-patch-lines script (coming in a later patch) which injects #line directives into the source .patch to preserve the original line numbers at compile time.

Note the end result of this subcommand is not yet functionally complete. Livepatch needs some ELF magic which linkers don't like:

- Two relocation sections (.rela*, .klp.rela*) for the same text section.

- Use of SHN_LIVEPATCH to mark livepatch symbols.

Unfortunately linkers tend to mangle such things. To work around that, klp diff generates a linker-compliant intermediate binary which encodes the relevant KLP section/reloc/symbol metadata.

After module linking, a klp post-link step (coming soon) will clean up the mess and convert the linked .ko into a fully compliant livepatch module.

Note this subcommand requires the diffed binaries to have been compiled with -ffunction-sections and -fdata-sections, and processed with 'objtool --checksum'. Those constraints will be handled by a klp-build script introduced in a later patch.

Without '-ffunction-sections -fdata-sections', reliable object diffing would be infeasible due to toolchain limitations:

- For intra-file+intra-section references, the compiler might occasionally generated hard-coded instruction offsets instead of relocations.

- Section-symbol-based references can be ambiguous:

- Overlapping or zero-length symbols create ambiguity as to which symbol is being referenced.

- A reference to the end of a symbol (e.g., checking array bounds) can be misinterpreted as a reference to the next symbol, or vice versa.

A potential future alternative to '-ffunction-sections -fdata-sections' would be to introduce a toolchain option that forces symbol-based (non-section) relocations.

---
 MAINTAINERS                              |    2 +-
 include/linux/livepatch.h                |   25 +-
 include/linux/livepatch_external.h       |   76 +
 kernel/livepatch/core.c                  |    4 +-
 scripts/module.lds.S                     |   10 +-
 tools/include/linux/livepatch_external.h |   76 +
 tools/include/linux/string.h             |   14 +
 tools/objtool/Build                      |    4 +-
 tools/objtool/Makefile                   |    3 +-
 tools/objtool/arch/x86/decode.c          |   40 +
 tools/objtool/builtin-klp.c              |   52 +
 tools/objtool/check.c                    |   14 -
 tools/objtool/elf.c                      |   21 +-
 tools/objtool/include/objtool/arch.h     |    1 +
 tools/objtool/include/objtool/builtin.h  |    2 +
 tools/objtool/include/objtool/elf.h      |   52 +-
 tools/objtool/include/objtool/klp.h      |   31 +
 tools/objtool/include/objtool/objtool.h  |    2 +
 tools/objtool/include/objtool/util.h     |   19 +
 tools/objtool/klp-diff.c                 | 1600 ++++++++++++++++++++++
 tools/objtool/objtool.c                  |   41 +-
 tools/objtool/sync-check.sh              |    1 +
 tools/objtool/weak.c                     |    7 +
 23 files changed, 2038 insertions(+), 59 deletions(-)
 create mode 100644 include/linux/livepatch_external.h
 create mode 100644 tools/include/linux/livepatch_external.h
 create mode 100644 tools/objtool/builtin-klp.c
 create mode 100644 tools/objtool/include/objtool/klp.h
 create mode 100644 tools/objtool/include/objtool/util.h
 create mode 100644 tools/objtool/klp-diff.c
MAINTAINERS +1 -1
index efb51ee92683..0298d9570ca8 100644
@@ -14002,7 +14002,7 @@ T:	git git://git.kernel.org/pub/scm/linux/kernel/git/livepatching/livepatching.g
 F:	Documentation/ABI/testing/sysfs-kernel-livepatch
 F:	Documentation/livepatch/
 F:	arch/powerpc/include/asm/livepatch.h
-F:	include/linux/livepatch.h
+F:	include/linux/livepatch*.h
 F:	kernel/livepatch/
 F:	kernel/module/livepatch.c
 F:	samples/livepatch/
include/linux/livepatch.h +1 -24
index 51a258c24ff5..772919e8096a 100644
@@ -13,6 +13,7 @@
 #include <linux/ftrace.h>
 #include <linux/completion.h>
 #include <linux/list.h>
+#include <linux/livepatch_external.h>
 #include <linux/livepatch_sched.h>
 
 #if IS_ENABLED(CONFIG_LIVEPATCH)
@@ -77,30 +78,6 @@ struct klp_func {
 	bool transition;
 };
 
-struct klp_object;
-
-/**
- * struct klp_callbacks - pre/post live-(un)patch callback structure
- * @pre_patch:		executed before code patching
- * @post_patch:		executed after code patching
- * @pre_unpatch:	executed before code unpatching
- * @post_unpatch:	executed after code unpatching
- * @post_unpatch_enabled:	flag indicating if post-unpatch callback
- * 				should run
- *
- * All callbacks are optional.  Only the pre-patch callback, if provided,
- * will be unconditionally executed.  If the parent klp_object fails to
- * patch for any reason, including a non-zero error status returned from
- * the pre-patch callback, no further callbacks will be executed.
- */
-struct klp_callbacks {
-	int (*pre_patch)(struct klp_object *obj);
-	void (*post_patch)(struct klp_object *obj);
-	void (*pre_unpatch)(struct klp_object *obj);
-	void (*post_unpatch)(struct klp_object *obj);
-	bool post_unpatch_enabled;
-};
-
 /**
  * struct klp_object - kernel object structure for live patching
  * @name:	module name (or NULL for vmlinux)
include/linux/livepatch_external.h new file +76 -0

76 lines not on this page · raw message

kernel/livepatch/core.c +2 -2
index 7e443c2cf7d4..0044a8125013 100644
@@ -224,7 +224,7 @@ static int klp_resolve_symbols(Elf_Shdr *sechdrs, const char *strtab,
 
 		/* Format: .klp.sym.sym_objname.sym_name,sympos */
 		cnt = sscanf(strtab + sym->st_name,
-			     ".klp.sym.%55[^.].%511[^,],%lu",
+			     KLP_SYM_PREFIX "%55[^.].%511[^,],%lu",
 			     sym_objname, sym_name, &sympos);
 		if (cnt != 3) {
 			pr_err("symbol %s has an incorrectly formatted name\n",
@@ -303,7 +303,7 @@ static int klp_write_section_relocs(struct module *pmod, Elf_Shdr *sechdrs,
 	 * See comment in klp_resolve_symbols() for an explanation
 	 * of the selected field width value.
 	 */
-	cnt = sscanf(shstrtab + sec->sh_name, ".klp.rela.%55[^.]",
+	cnt = sscanf(shstrtab + sec->sh_name, KLP_RELOC_SEC_PREFIX "%55[^.]",
 		     sec_objname);
 	if (cnt != 1) {
 		pr_err("section %s has an incorrectly formatted name\n",
scripts/module.lds.S +9 -1
index 0b5ea63d1c67..05abb3ceabef 100644
@@ -34,8 +34,16 @@ SECTIONS {
 
 	__patchable_function_entries : { *(__patchable_function_entries) }
 
+	__klp_funcs		0: ALIGN(8) { KEEP(*(__klp_funcs)) }
+
+	__klp_objects		0: ALIGN(8) {
+		__start_klp_objects = .;
+		KEEP(*(__klp_objects))
+		__stop_klp_objects = .;
+	}
+
 #ifdef CONFIG_ARCH_USES_CFI_TRAPS
-	__kcfi_traps 		: { KEEP(*(.kcfi_traps)) }
+	__kcfi_traps		: { KEEP(*(.kcfi_traps)) }
 #endif
 
 	.text : {
tools/include/linux/livepatch_external.h new file +76 -0

76 lines not on this page · raw message

tools/include/linux/string.h +14 -0
index 8499f509f03e..51ad3cf4fa82 100644
@@ -44,6 +44,20 @@ static inline bool strstarts(const char *str, const char *prefix)
 	return strncmp(str, prefix, strlen(prefix)) == 0;
 }
 
+/*
+ * Checks if a string ends with another.
+ */
+static inline bool str_ends_with(const char *str, const char *substr)
+{
+	size_t len = strlen(str);
+	size_t sublen = strlen(substr);
+
+	if (sublen > len)
+		return false;
+
+	return !strcmp(str + len - sublen, substr);
+}
+
 extern char * __must_check skip_spaces(const char *);
 
 extern char *strim(char *);
tools/objtool/Build +2 -2
index a3cdf8af6635..0b01657671d7 100644
@@ -8,8 +8,8 @@ objtool-y += builtin-check.o
 objtool-y += elf.o
 objtool-y += objtool.o
 
-objtool-$(BUILD_ORC) += orc_gen.o
-objtool-$(BUILD_ORC) += orc_dump.o
+objtool-$(BUILD_ORC) += orc_gen.o orc_dump.o
+objtool-$(BUILD_KLP) += builtin-klp.o klp-diff.o
 
 objtool-y += libstring.o
 objtool-y += libctype.o
tools/objtool/Makefile +2 -1
index 958761c05b7c..48928c9bebef 100644
@@ -15,13 +15,14 @@ ifeq ($(ARCH_HAS_KLP),y)
 	HAVE_XXHASH = $(shell echo "int main() {}" | \
 		      $(HOSTCC) -xc - -o /dev/null -lxxhash 2> /dev/null && echo y || echo n)
 	ifeq ($(HAVE_XXHASH),y)
+		BUILD_KLP	 := y
 		LIBXXHASH_CFLAGS := $(shell $(HOSTPKG_CONFIG) libxxhash --cflags 2>/dev/null) \
 				    -DBUILD_KLP
 		LIBXXHASH_LIBS   := $(shell $(HOSTPKG_CONFIG) libxxhash --libs 2>/dev/null || echo -lxxhash)
 	endif
 endif
 
-export BUILD_ORC
+export BUILD_ORC BUILD_KLP
 
 ifeq ($(srctree),)
 srctree := $(patsubst %/,%,$(dir $(CURDIR)))
tools/objtool/arch/x86/decode.c +40 -0
index 36a65cecada3..c4d6b90b1134 100644
@@ -89,6 +89,46 @@ s64 arch_insn_adjusted_addend(struct instruction *insn, struct reloc *reloc)
 	return phys_to_virt(addend);
 }
 
+static void scan_for_insn(struct section *sec, unsigned long offset,
+			  unsigned long *insn_off, unsigned int *insn_len)
+{
+	unsigned long o = 0;
+	struct insn insn;
+
+	while (1) {
+
+		insn_decode(&insn, sec->data->d_buf + o, sec_size(sec) - o,
+			    INSN_MODE_64);
+
+		if (o + insn.length > offset) {
+			*insn_off = o;
+			*insn_len = insn.length;
+			return;
+		}
+
+		o += insn.length;
+	}
+}
+
+u64 arch_adjusted_addend(struct reloc *reloc)
+{
+	unsigned int type = reloc_type(reloc);
+	s64 addend = reloc_addend(reloc);
+	unsigned long insn_off;
+	unsigned int insn_len;
+
+	if (type == R_X86_64_PLT32)
+		return addend + 4;
+
+	if (type != R_X86_64_PC32 || !is_text_sec(reloc->sec->base))
+		return addend;
+
+	scan_for_insn(reloc->sec->base, reloc_offset(reloc),
+		      &insn_off, &insn_len);
+
+	return addend + insn_off + insn_len - reloc_offset(reloc);
+}
+
 unsigned long arch_jump_destination(struct instruction *insn)
 {
 	return insn->offset + insn->len + insn->immediate;
tools/objtool/builtin-klp.c new file +52 -0

52 lines not on this page · raw message

tools/objtool/check.c +0 -14
index 7e492945e5e5..9d1f545279a6 100644
@@ -186,20 +186,6 @@ static bool is_sibling_call(struct instruction *insn)
 	return (is_static_jump(insn) && insn_call_dest(insn));
 }
 
-/*
- * Checks if a string ends with another.
- */
-static bool str_ends_with(const char *s, const char *sub)
-{
-	const int slen = strlen(s);
-	const int sublen = strlen(sub);
-
-	if (sublen > slen)
-		return 0;
-
-	return !memcmp(s + slen - sublen, sub, sublen);
-}
-
 /*
  * Checks if a function is a Rust "noreturn" one.
  */
tools/objtool/elf.c +20 -1
index d2f418a34881..16c8d36afe4e 100644
@@ -288,6 +288,18 @@ struct symbol *find_symbol_by_name(const struct elf *elf, const char *name)
 	return NULL;
 }
 
+struct symbol *find_global_symbol_by_name(const struct elf *elf, const char *name)
+{
+	struct symbol *sym;
+
+	elf_hash_for_each_possible(symbol_name, sym, name_hash, str_hash(name)) {
+		if (!strcmp(sym->name, name) && !is_local_sym(sym))
+			return sym;
+	}
+
+	return NULL;
+}
+
 struct reloc *find_reloc_by_dest_range(const struct elf *elf, struct section *sec,
 				     unsigned long offset, unsigned int len)
 {
@@ -475,6 +487,8 @@ static int elf_add_symbol(struct elf *elf, struct symbol *sym)
 	else
 		entry = &sym->sec->symbol_list;
 	list_add(&sym->list, entry);
+
+	list_add_tail(&sym->global_list, &elf->symbols);
 	elf_hash_add(symbol, &sym->hash, sym->idx);
 	elf_hash_add(symbol_name, &sym->name_hash, str_hash(sym->name));
 
@@ -528,6 +542,9 @@ static int read_symbols(struct elf *elf)
 		ERROR_GLIBC("calloc");
 		return -1;
 	}
+
+	INIT_LIST_HEAD(&elf->symbols);
+
 	for (i = 0; i < symbols_nr; i++) {
 		sym = &elf->symbol_data[i];
 
@@ -636,7 +653,7 @@ static int mark_group_syms(struct elf *elf)
 		return -1;
 	}
 
-	list_for_each_entry(sec, &elf->sections, list) {
+	for_each_sec(elf, sec) {
 		if (sec->sh.sh_type == SHT_GROUP &&
 		    sec->sh.sh_link == symtab->idx) {
 			sym = find_symbol_by_index(elf, sec->sh.sh_info);
@@ -1219,6 +1236,8 @@ struct elf *elf_create_file(GElf_Ehdr *ehdr, const char *name)
 		return NULL;
 	}
 
+	INIT_LIST_HEAD(&elf->symbols);
+
 	if (!elf_alloc_hash(section,		1000) ||
 	    !elf_alloc_hash(section_name,	1000) ||
 	    !elf_alloc_hash(symbol,		10000) ||
tools/objtool/include/objtool/arch.h +1 -0
index 07729a240159..03c86caec4d6 100644
@@ -84,6 +84,7 @@ bool arch_callee_saved_reg(unsigned char reg);
 unsigned long arch_jump_destination(struct instruction *insn);
 
 s64 arch_insn_adjusted_addend(struct instruction *insn, struct reloc *reloc);
+u64 arch_adjusted_addend(struct reloc *reloc);
 
 const char *arch_nop_insn(int len);
 const char *arch_ret_insn(int len);
tools/objtool/include/objtool/builtin.h +2 -0
index 92bc7089cfc1..e60438577000 100644
@@ -52,4 +52,6 @@ int objtool_run(int argc, const char **argv);
 
 int make_backup(void);
 
+int cmd_klp(int argc, const char **argv);
+
 #endif /* _BUILTIN_H */
tools/objtool/include/objtool/elf.h +41 -11

101 lines not on this page · raw message

tools/objtool/include/objtool/klp.h new file +31 -0
new file mode 100644
index 000000000000..07928fac059b
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef _OBJTOOL_KLP_H
+#define _OBJTOOL_KLP_H
+
+/*
+ * __klp_objects and __klp_funcs are created by klp diff and used by the patch
+ * module init code to build the klp_patch, klp_object and klp_func structs
+ * needed by the livepatch API.
+ */
+#define KLP_OBJECTS_SEC	"__klp_objects"
+#define KLP_FUNCS_SEC	"__klp_funcs"
+
+/*
+ * __klp_relocs is an intermediate section which are created by klp diff and
+ * converted into KLP symbols/relas by "objtool klp post-link".  This is needed
+ * to work around the linker, which doesn't preserve SHN_LIVEPATCH or
+ * SHF_RELA_LIVEPATCH, nor does it support having two RELA sections for a
+ * single PROGBITS section.
+ */
+#define KLP_RELOCS_SEC	"__klp_relocs"
+#define KLP_STRINGS_SEC	".rodata.klp.str1.1"
+
+struct klp_reloc {
+	void *offset;
+	void *sym;
+	u32 type;
+};
+
+int cmd_klp_diff(int argc, const char **argv);
+
+#endif /* _OBJTOOL_KLP_H */
tools/objtool/include/objtool/objtool.h +2 -0
index 90c591b5bd68..37e9fe4492d6 100644
@@ -41,6 +41,8 @@ struct objtool_file {
 	struct pv_state *pv_ops;
 };
 
+char *top_level_dir(const char *file);
+
 struct objtool_file *objtool_open_read(const char *_objname);
 
 int objtool_pv_add(struct objtool_file *file, int idx, struct symbol *func);
tools/objtool/include/objtool/util.h new file +19 -0
new file mode 100644
index 000000000000..a0180b312f73
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef _UTIL_H
+#define _UTIL_H
+
+#include <objtool/warn.h>
+
+#define snprintf_check(str, size, format, args...)			\
+({									\
+	int __ret = snprintf(str, size, format, args);			\
+	if (__ret < 0)							\
+		ERROR_GLIBC("snprintf");				\
+	else if (__ret >= size)						\
+		ERROR("snprintf() failed for '" format "'", args);	\
+	else								\
+		__ret = 0;						\
+	__ret;								\
+})
+
+#endif /* _UTIL_H */
tools/objtool/klp-diff.c new file +1600 -0

1,600 lines not on this page · raw message

tools/objtool/objtool.c +39 -2

58 lines not on this page · raw message

tools/objtool/sync-check.sh +1 -0
index 81d120d05442..e1d98fb03157 100755
@@ -16,6 +16,7 @@ arch/x86/include/asm/orc_types.h
 arch/x86/include/asm/emulate_prefix.h
 arch/x86/lib/x86-opcode-map.txt
 arch/x86/tools/gen-insn-attr-x86.awk
+include/linux/livepatch_external.h
 include/linux/static_call_types.h
 "
 
tools/objtool/weak.c +7 -0
index d83f607733b0..d6562f292259 100644
@@ -8,6 +8,8 @@
 #include <stdbool.h>
 #include <errno.h>
 #include <objtool/objtool.h>
+#include <objtool/arch.h>
+#include <objtool/builtin.h>
 
 #define UNSUPPORTED(name)						\
 ({									\
@@ -24,3 +26,8 @@ int __weak orc_create(struct objtool_file *file)
 {
 	UNSUPPORTED("ORC");
 }
+
+int __weak cmd_klp(int argc, const char **argv)
+{
+	UNSUPPORTED("klp");
+}
Signature · 1 line
2.49.0
author maintainer
#71 lore raw

PATCH v3 53/64 objtool/klp: Add --debug option to show cloning decisions

In reply to Josh Poimboeuf

Add a --debug option to klp diff which prints cloning decisions and an indented dependency tree for all cloned symbols and relocations. This helps visualize which symbols and relocations were included and why.

---
 tools/objtool/include/objtool/warn.h | 21 ++++++++
 tools/objtool/klp-diff.c             | 75 ++++++++++++++++++++++++++++
 tools/objtool/objtool.c              |  3 ++
 3 files changed, 99 insertions(+)
tools/objtool/include/objtool/warn.h +21 -0
index 29173a1368d7..e88322d97573 100644
@@ -102,6 +102,10 @@ static inline char *offstr(struct section *sec, unsigned long offset)
 #define ERROR_FUNC(sec, offset, format, ...) __WARN_FUNC(ERROR_STR, sec, offset, format, ##__VA_ARGS__)
 #define ERROR_INSN(insn, format, ...) WARN_FUNC(insn->sec, insn->offset, format, ##__VA_ARGS__)
 
+extern bool debug;
+extern int indent;
+
+static inline void unindent(int *unused) { indent--; }
 
 #define __dbg(format, ...)						\
 	fprintf(stderr,							\
@@ -110,6 +114,23 @@ static inline char *offstr(struct section *sec, unsigned long offset)
 		objname ? ": " : "",					\
 		##__VA_ARGS__)
 
+#define dbg(args...)							\
+({									\
+	if (unlikely(debug))						\
+		__dbg(args);						\
+})
+
+#define __dbg_indent(format, ...)					\
+({									\
+	if (unlikely(debug))						\
+		__dbg("%*s" format, indent * 8, "", ##__VA_ARGS__);	\
+})
+
+#define dbg_indent(args...)						\
+	int __attribute__((cleanup(unindent))) __dummy_##__COUNTER__;	\
+	__dbg_indent(args);						\
+	indent++
+
 #define dbg_checksum(func, insn, checksum)				\
 ({									\
 	if (unlikely(insn->sym && insn->sym->pfunc &&			\
tools/objtool/klp-diff.c +75 -0

123 lines not on this page · raw message

tools/objtool/objtool.c +3 -0
index c8f611c1320d..3c26ed561c7e 100644
@@ -16,6 +16,9 @@
 #include <objtool/objtool.h>
 #include <objtool/warn.h>
 
+bool debug;
+int indent;
+
 static struct objtool_file file;
 
 struct objtool_file *objtool_open_read(const char *filename)
Signature · 1 line
2.49.0
author maintainer
#72 lore raw

PATCH v3 54/64 objtool/klp: Add post-link subcommand to finalize livepatch modules

In reply to Josh Poimboeuf

Livepatch needs some ELF magic which linkers don't like:

- Two relocation sections (.rela*, .klp.rela*) for the same text section.

- Use of SHN_LIVEPATCH to mark livepatch symbols.

Unfortunately linkers tend to mangle such things. To work around that, klp diff generates a linker-compliant intermediate binary which encodes the relevant KLP section/reloc/symbol metadata.

After module linking, the .ko then needs to be converted to an actual livepatch module. Introduce a new klp post-link subcommand to do so.

---
 tools/objtool/Build                 |   2 +-
 tools/objtool/builtin-klp.c         |   1 +
 tools/objtool/include/objtool/klp.h |   4 +
 tools/objtool/klp-post-link.c       | 168 ++++++++++++++++++++++++++++
 4 files changed, 174 insertions(+), 1 deletion(-)
 create mode 100644 tools/objtool/klp-post-link.c
tools/objtool/Build +1 -1
index 0b01657671d7..8cd71b9a5eef 100644
@@ -9,7 +9,7 @@ objtool-y += elf.o
 objtool-y += objtool.o
 
 objtool-$(BUILD_ORC) += orc_gen.o orc_dump.o
-objtool-$(BUILD_KLP) += builtin-klp.o klp-diff.o
+objtool-$(BUILD_KLP) += builtin-klp.o klp-diff.o klp-post-link.o
 
 objtool-y += libstring.o
 objtool-y += libctype.o
tools/objtool/builtin-klp.c +1 -0
index 9b13dd1182af..56d5a5b92f72 100644
@@ -14,6 +14,7 @@ struct subcmd {
 
 static struct subcmd subcmds[] = {
 	{ "diff",		"Generate binary diff of two object files",		cmd_klp_diff, },
+	{ "post-link",		"Finalize klp symbols/relocs after module linking",	cmd_klp_post_link, },
 };
 
 static void cmd_klp_usage(void)
tools/objtool/include/objtool/klp.h +4 -0
index 07928fac059b..ad830a7ce55b 100644
@@ -2,6 +2,9 @@
 #ifndef _OBJTOOL_KLP_H
 #define _OBJTOOL_KLP_H
 
+#define SHF_RELA_LIVEPATCH	0x00100000
+#define SHN_LIVEPATCH		0xff20
+
 /*
  * __klp_objects and __klp_funcs are created by klp diff and used by the patch
  * module init code to build the klp_patch, klp_object and klp_func structs
@@ -27,5 +30,6 @@ struct klp_reloc {
 };
 
 int cmd_klp_diff(int argc, const char **argv);
+int cmd_klp_post_link(int argc, const char **argv);
 
 #endif /* _OBJTOOL_KLP_H */
tools/objtool/klp-post-link.c new file +168 -0

168 lines not on this page · raw message

Signature · 1 line
2.49.0
author maintainer
#73 lore raw

PATCH v3 55/64 objtool: Disallow duplicate prefix symbols

In reply to Josh Poimboeuf

In preparation for adding objtool 'check' support for analyzing livepatch modules, error out if a duplicate prefix symbol is attempted.

---
 tools/objtool/check.c               | 12 +++++++++++-
 tools/objtool/elf.c                 |  9 +++++++++
 tools/objtool/include/objtool/elf.h |  3 +++
 3 files changed, 23 insertions(+), 1 deletion(-)
tools/objtool/check.c +11 -1
index 9d1f545279a6..c6c99e3fb76f 100644
@@ -4221,6 +4221,7 @@ static int add_prefix_symbol(struct objtool_file *file, struct symbol *func)
 	for (prev = prev_insn_same_sec(file, insn);
 	     prev;
 	     prev = prev_insn_same_sec(file, prev)) {
+		struct symbol *sym_pfx;
 		u64 offset;
 
 		if (prev->type != INSN_NOP)
@@ -4234,7 +4235,12 @@ static int add_prefix_symbol(struct objtool_file *file, struct symbol *func)
 		if (offset < opts.prefix)
 			continue;
 
-		elf_create_prefix_symbol(file->elf, func, opts.prefix);
+		sym_pfx = elf_create_prefix_symbol(file->elf, func, opts.prefix);
+		if (!sym_pfx) {
+			WARN("duplicate prefix symbol for %s\n", func->name);
+			return -1;
+		}
+
 		break;
 	}
 
@@ -4270,6 +4276,10 @@ static int add_prefix_symbols(struct objtool_file *file)
 			if (!is_func_sym(func))
 				continue;
 
+			/*
+			 * Ignore this error on purpose, there are valid
+			 * reasons for this to fail.
+			 */
 			add_prefix_symbol(file, func);
 		}
 	}
tools/objtool/elf.c +9 -0
index 16c8d36afe4e..383183e9cf2d 100644
@@ -945,11 +945,20 @@ elf_create_prefix_symbol(struct elf *elf, struct symbol *orig, size_t size)
 	size_t namelen = strlen(orig->name) + sizeof("__pfx_");
 	char name[SYM_NAME_LEN];
 	unsigned long offset;
+	struct symbol *sym;
 
 	snprintf(name, namelen, "__pfx_%s", orig->name);
 
+	sym = orig;
 	offset = orig->sym.st_value - size;
 
+	sec_for_each_sym_continue_reverse(orig->sec, sym) {
+		if (sym->offset < offset)
+			break;
+		if (sym->offset == offset && !strcmp(sym->name, name))
+			return NULL;
+	}
+
 	return elf_create_symbol(elf, name, orig->sec,
 				 GELF_ST_BIND(orig->sym.st_info),
 				 GELF_ST_TYPE(orig->sym.st_info),
tools/objtool/include/objtool/elf.h +3 -0
index 903cbcb84e6b..c2a744afd5d5 100644
@@ -443,6 +443,9 @@ static inline void set_sym_next_reloc(struct reloc *reloc, struct reloc *next)
 #define sec_for_each_sym(sec, sym)					\
 	list_for_each_entry(sym, &sec->symbol_list, list)
 
+#define sec_for_each_sym_continue_reverse(sec, sym)			\
+	list_for_each_entry_continue_reverse(sym, &sec->symbol_list, list)
+
 #define sec_prev_sym(sym)						\
 	sym->sec && sym->list.prev != &sym->sec->symbol_list ?		\
 	list_prev_entry(sym, list) : NULL
Signature · 1 line
2.49.0
author maintainer
#74 lore raw

PATCH v3 56/64 objtool: Add base objtool support for livepatch modules

In reply to Josh Poimboeuf

In preparation for klp-build, enable "classic" objtool to work on livepatch modules:

- Avoid duplicate symbol/section warnings for prefix symbols and the .static_call_sites and __mcount_loc sections which may have already been extracted by klp diff.

- Add __klp_funcs to the IBT function pointer section whitelist.

- Prevent KLP symbols from getting incorrectly classified as cold subfunctions.

---
 tools/objtool/check.c                   | 48 +++++++++++++++++++++----
 tools/objtool/elf.c                     |  5 ++-
 tools/objtool/include/objtool/elf.h     |  1 +
 tools/objtool/include/objtool/objtool.h |  2 +-
 4 files changed, 48 insertions(+), 8 deletions(-)
tools/objtool/check.c +42 -6
index c6c99e3fb76f..1eb6489ae459 100644
@@ -3,6 +3,7 @@
  * Copyright (C) 2015-2017 Josh Poimboeuf <[email protected]>
  */
 
+#define _GNU_SOURCE /* memmem() */
 #include <string.h>
 #include <stdlib.h>
 #include <inttypes.h>
@@ -610,6 +611,20 @@ static int init_pv_ops(struct objtool_file *file)
 	return 0;
 }
 
+static bool is_livepatch_module(struct objtool_file *file)
+{
+	struct section *sec;
+
+	if (!opts.module)
+		return false;
+
+	sec = find_section_by_name(file->elf, ".modinfo");
+	if (!sec)
+		return false;
+
+	return memmem(sec->data->d_buf, sec_size(sec), "\0livepatch=Y", 12);
+}
+
 static int create_static_call_sections(struct objtool_file *file)
 {
 	struct static_call_site *site;
@@ -621,7 +636,14 @@ static int create_static_call_sections(struct objtool_file *file)
 
 	sec = find_section_by_name(file->elf, ".static_call_sites");
 	if (sec) {
-		WARN("file already has .static_call_sites section, skipping");
+		/*
+		 * Livepatch modules may have already extracted the static call
+		 * site entries to take advantage of vmlinux static call
+		 * privileges.
+		 */
+		if (!file->klp)
+			WARN("file already has .static_call_sites section, skipping");
+
 		return 0;
 	}
 
@@ -672,7 +694,7 @@ static int create_static_call_sections(struct objtool_file *file)
 
 		key_sym = find_symbol_by_name(file->elf, tmp);
 		if (!key_sym) {
-			if (!opts.module) {
+			if (!opts.module || file->klp) {
 				ERROR("static_call: can't find static_call_key symbol: %s", tmp);
 				return -1;
 			}
@@ -891,7 +913,13 @@ static int create_mcount_loc_sections(struct objtool_file *file)
 
 	sec = find_section_by_name(file->elf, "__mcount_loc");
 	if (sec) {
-		WARN("file already has __mcount_loc section, skipping");
+		/*
+		 * Livepatch modules have already extracted their __mcount_loc
+		 * entries to cover the !CONFIG_FTRACE_MCOUNT_USE_OBJTOOL case.
+		 */
+		if (!file->klp)
+			WARN("file already has __mcount_loc section, skipping");
+
 		return 0;
 	}
 
@@ -2575,6 +2603,8 @@ static bool validate_branch_enabled(void)
 
 static int decode_sections(struct objtool_file *file)
 {
+	file->klp = is_livepatch_module(file);
+
 	mark_rodata(file);
 
 	if (init_pv_ops(file))
@@ -4235,8 +4265,13 @@ static int add_prefix_symbol(struct objtool_file *file, struct symbol *func)
 		if (offset < opts.prefix)
 			continue;
 
+		/*
+		 * Ignore attempts to make duplicate symbols in livepatch
+		 * modules.  They've already extracted the prefix symbols
+		 * except for the newly compiled init.c.
+		 */
 		sym_pfx = elf_create_prefix_symbol(file->elf, func, opts.prefix);
-		if (!sym_pfx) {
+		if (!sym_pfx && !file->klp) {
 			WARN("duplicate prefix symbol for %s\n", func->name);
 			return -1;
 		}
@@ -4571,6 +4606,7 @@ static int validate_ibt(struct objtool_file *file)
 		    !strncmp(sec->name, ".debug", 6)			||
 		    !strcmp(sec->name, ".altinstructions")		||
 		    !strcmp(sec->name, ".ibt_endbr_seal")		||
+		    !strcmp(sec->name, ".kcfi_traps")			||
 		    !strcmp(sec->name, ".orc_unwind_ip")		||
 		    !strcmp(sec->name, ".retpoline_sites")		||
 		    !strcmp(sec->name, ".smp_locks")			||
@@ -4580,12 +4616,12 @@ static int validate_ibt(struct objtool_file *file)
 		    !strcmp(sec->name, "__bug_table")			||
 		    !strcmp(sec->name, "__ex_table")			||
 		    !strcmp(sec->name, "__jump_table")			||
+		    !strcmp(sec->name, "__klp_funcs")			||
 		    !strcmp(sec->name, "__mcount_loc")			||
-		    !strcmp(sec->name, ".kcfi_traps")			||
 		    !strcmp(sec->name, ".llvm.call-graph-profile")	||
 		    !strcmp(sec->name, ".llvm_bb_addr_map")		||
 		    !strcmp(sec->name, "__tracepoints")			||
-		    strstr(sec->name, "__patchable_function_entries"))
+		    !strcmp(sec->name, "__patchable_function_entries"))
 			continue;
 
 		for_each_reloc(sec->rsec, reloc)
tools/objtool/elf.c +4 -1
index 383183e9cf2d..f9eed5d50de5 100644
@@ -496,7 +496,10 @@ static int elf_add_symbol(struct elf *elf, struct symbol *sym)
 	    (strstarts(sym->name, "__pfx_") || strstarts(sym->name, "__cfi_")))
 		sym->prefix = 1;
 
-	if (is_func_sym(sym) && strstr(sym->name, ".cold"))
+	if (strstarts(sym->name, ".klp.sym"))
+		sym->klp = 1;
+
+	if (!sym->klp && is_func_sym(sym) && strstr(sym->name, ".cold"))
 		sym->cold = 1;
 	sym->pfunc = sym->cfunc = sym;
 
tools/objtool/include/objtool/elf.h +1 -0
index c2a744afd5d5..1212f81f40e0 100644
@@ -84,6 +84,7 @@ struct symbol {
 	u8 debug_checksum    : 1;
 	u8 changed	     : 1;
 	u8 included	     : 1;
+	u8 klp		     : 1;
 	struct list_head pv_target;
 	struct reloc *relocs;
 	struct section *group_sec;
tools/objtool/include/objtool/objtool.h +1 -1
index 37e9fe4492d6..731965a742e9 100644
@@ -30,7 +30,7 @@ struct objtool_file {
 	struct list_head mcount_loc_list;
 	struct list_head endbr_list;
 	struct list_head call_list;
-	bool ignore_unreachables, hints, rodata;
+	bool ignore_unreachables, hints, rodata, klp;
 
 	unsigned int nr_endbr;
 	unsigned int nr_endbr_int;
Signature · 1 line
2.49.0
author maintainer
#75 lore raw

PATCH v3 57/64 livepatch: Add CONFIG_KLP_BUILD

In reply to Josh Poimboeuf

In preparation for introducing klp-build, add a new CONFIG_KLP_BUILD option. The initial version will only be supported on x86-64.

---
 arch/x86/Kconfig         |  1 +
 kernel/livepatch/Kconfig | 12 ++++++++++++
 2 files changed, 13 insertions(+)
arch/x86/Kconfig +1 -0
index 62faa62b5959..448c6bfb71d6 100644
@@ -266,6 +266,7 @@ config X86
 	select HAVE_FUNCTION_ERROR_INJECTION
 	select HAVE_KRETPROBES
 	select HAVE_RETHOOK
+	select HAVE_KLP_BUILD			if X86_64
 	select HAVE_LIVEPATCH			if X86_64
 	select HAVE_MIXED_BREAKPOINTS_REGS
 	select HAVE_MOD_ARCH_SPECIFIC
kernel/livepatch/Kconfig +12 -0
index 53d51ed619a3..4c0a9c18d0b2 100644
@@ -18,3 +18,15 @@ config LIVEPATCH
 	  module uses the interface provided by this option to register
 	  a patch, causing calls to patched functions to be redirected
 	  to new function code contained in the patch module.
+
+config HAVE_KLP_BUILD
+	bool
+	help
+	  Arch supports klp-build
+
+config KLP_BUILD
+	def_bool y
+	depends on LIVEPATCH && HAVE_KLP_BUILD
+	select OBJTOOL
+	help
+	  Enable klp-build support
Signature · 1 line
2.49.0
author maintainer
#76 lore raw

PATCH v3 58/64 kbuild,objtool: Defer objtool validation step for CONFIG_KLP_BUILD

In reply to Josh Poimboeuf

In preparation for klp-build, defer objtool validation for CONFIG_KLP_BUILD kernels until the final pre-link archive (e.g., vmlinux.o, module-foo.o) is built. This will simplify the process of generating livepatch modules.

Delayed objtool is generally preferred anyway, and is already standard for IBT and LTO. Eventually the per-translation-unit mode will be phased out.

---
 scripts/Makefile.lib    | 2 +-
 scripts/link-vmlinux.sh | 3 ++-
 2 files changed, 3 insertions(+), 2 deletions(-)
scripts/Makefile.lib +1 -1
index 15fee73e9289..28a1c08e3b22 100644
@@ -197,7 +197,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_KLP_BUILD))
 
 cmd_objtool = $(if $(objtool-enabled), ; $(objtool) $(objtool-args) $@)
 cmd_gen_objtooldep = $(if $(objtool-enabled), { echo ; echo '$@: $$(wildcard $(objtool))' ; } >> $(dot-target).cmd)
scripts/link-vmlinux.sh +2 -1
index 51367c2bfc21..59f875236292 100755
@@ -60,7 +60,8 @@ vmlinux_link()
 	# skip output file argument
 	shift
 
-	if is_enabled CONFIG_LTO_CLANG || is_enabled CONFIG_X86_KERNEL_IBT; then
+	if is_enabled CONFIG_LTO_CLANG || is_enabled CONFIG_X86_KERNEL_IBT ||
+	   is_enabled CONFIG_KLP_BUILD; then
 		# Use vmlinux.o instead of performing the slow LTO link again.
 		objs=vmlinux.o
 		libs=
Signature · 1 line
2.49.0
author maintainer
#77 lore raw

PATCH v3 59/64 livepatch/klp-build: Introduce fix-patch-lines script to avoid __LINE__ diff noise

In reply to Josh Poimboeuf

The __LINE__ macro creates challenges for binary diffing. When a .patch file adds or removes lines, it shifts the line numbers for all code below it.

This can cause the code generation of functions using __LINE__ to change due to the line number constant being embedded in a MOV instruction, despite there being no semantic difference.

Avoid such false positives by adding a fix-patch-lines script which can be used to insert a #line directive in each patch hunk affecting the line numbering. This script will be used by klp-build, which will be introduced in a subsequent patch.

---
 MAINTAINERS                       |  1 +
 scripts/livepatch/fix-patch-lines | 79 +++++++++++++++++++++++++++++++
 2 files changed, 80 insertions(+)
 create mode 100755 scripts/livepatch/fix-patch-lines
MAINTAINERS +1 -0
index 0298d9570ca8..dd622368d74b 100644
@@ -14006,6 +14006,7 @@ F:	include/linux/livepatch*.h
 F:	kernel/livepatch/
 F:	kernel/module/livepatch.c
 F:	samples/livepatch/
+F:	scripts/livepatch/
 F:	tools/testing/selftests/livepatch/
 
 LLC (802.2)
scripts/livepatch/fix-patch-lines new file +79 -0

79 lines not on this page · raw message

Signature · 1 line
2.49.0
author maintainer
#78 lore raw

PATCH v3 60/64 livepatch/klp-build: Add stub init code for livepatch modules

In reply to Josh Poimboeuf

Add a module initialization stub which can be linked with binary diff objects to produce a livepatch module.

---
 scripts/livepatch/init.c | 108 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 108 insertions(+)
 create mode 100644 scripts/livepatch/init.c
scripts/livepatch/init.c new file +108 -0
new file mode 100644
index 000000000000..2274d8f5a482
@@ -0,0 +1,108 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Init code for a livepatch kernel module
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/livepatch.h>
+
+extern struct klp_object_ext __start_klp_objects[];
+extern struct klp_object_ext __stop_klp_objects[];
+
+static struct klp_patch *patch;
+
+static int __init livepatch_mod_init(void)
+{
+	struct klp_object *objs;
+	unsigned int nr_objs;
+	int ret;
+
+	nr_objs = __stop_klp_objects - __start_klp_objects;
+
+	if (!nr_objs) {
+		pr_err("nothing to patch!\n");
+		ret = -EINVAL;
+		goto err;
+	}
+
+	patch = kzalloc(sizeof(*patch), GFP_KERNEL);
+	if (!patch) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	objs = kzalloc(sizeof(struct klp_object) * (nr_objs + 1),  GFP_KERNEL);
+	if (!objs) {
+		ret = -ENOMEM;
+		goto err_free_patch;
+	}
+
+	for (int i = 0; i < nr_objs; i++) {
+		struct klp_object_ext *obj_ext = __start_klp_objects + i;
+		struct klp_func_ext *funcs_ext = obj_ext->funcs;
+		unsigned int nr_funcs = obj_ext->nr_funcs;
+		struct klp_func *funcs = objs[i].funcs;
+		struct klp_object *obj = objs + i;
+
+		funcs = kzalloc(sizeof(struct klp_func) * (nr_funcs + 1), GFP_KERNEL);
+		if (!funcs) {
+			ret = -ENOMEM;
+			for (int j = 0; j < i; j++)
+				kfree(objs[i].funcs);
+			goto err_free_objs;
+		}
+
+		for (int j = 0; j < nr_funcs; j++) {
+			funcs[j].old_name   = funcs_ext[j].old_name;
+			funcs[j].new_func   = funcs_ext[j].new_func;
+			funcs[j].old_sympos = funcs_ext[j].sympos;
+		}
+
+		obj->name = obj_ext->name;
+		obj->funcs = funcs;
+
+		memcpy(&obj->callbacks, &obj_ext->callbacks, sizeof(struct klp_callbacks));
+	}
+
+	patch->mod = THIS_MODULE;
+	patch->objs = objs;
+
+	/* TODO patch->states */
+
+#ifdef KLP_NO_REPLACE
+	patch->replace = false;
+#else
+	patch->replace = true;
+#endif
+
+	return klp_enable_patch(patch);
+
+err_free_objs:
+	kfree(objs);
+err_free_patch:
+	kfree(patch);
+err:
+	return ret;
+}
+
+static void __exit livepatch_mod_exit(void)
+{
+	unsigned int nr_objs;
+
+	nr_objs = __stop_klp_objects - __start_klp_objects;
+
+	for (int i = 0; i < nr_objs; i++)
+		kfree(patch->objs[i].funcs);
+
+	kfree(patch->objs);
+	kfree(patch);
+}
+
+module_init(livepatch_mod_init);
+module_exit(livepatch_mod_exit);
+MODULE_LICENSE("GPL");
+MODULE_INFO(livepatch, "Y");
+MODULE_DESCRIPTION("Livepatch module");
Signature · 1 line
2.49.0
author maintainer
#79 lore raw

PATCH v3 61/64 livepatch/klp-build: Introduce klp-build script for generating livepatch modules

In reply to Josh Poimboeuf

Add a klp-build script which automates the generation of a livepatch module from a source .patch file by performing the following steps:

- Builds an original kernel with -function-sections and -fdata-sections, plus objtool function checksumming.

- Applies the .patch file and rebuilds the kernel using the same options.

- Runs 'objtool klp diff' to detect changed functions and generate intermediate binary diff objects.

- Builds a kernel module which links the diff objects with some livepatch module init code (scripts/livepatch/init.c).

- Finalizes the livepatch module (aka work around linker wreckage) using 'objtool klp post-link'.

---
 scripts/livepatch/fix-patch-lines |   2 +-
 scripts/livepatch/klp-build       | 747 ++++++++++++++++++++++++++++++
 tools/objtool/klp-diff.c          |   6 +-
 3 files changed, 752 insertions(+), 3 deletions(-)
 create mode 100755 scripts/livepatch/klp-build
scripts/livepatch/fix-patch-lines +1 -1
index 73c5e3dea46e..fa7d4f6592e6 100755
@@ -23,7 +23,7 @@ BEGIN {
 
 	in_hunk = 1
 
-	# for @@ -1,3 +1,4 @@:
+	# @@ -1,3 +1,4 @@:
 	#   1: line number in old file
 	#   3: how many lines the hunk covers in old file
 	#   1: line number in new file
scripts/livepatch/klp-build new file +747 -0

747 lines not on this page · raw message

tools/objtool/klp-diff.c +4 -2
index c9320ce39578..15b554b53da6 100644
@@ -240,10 +240,12 @@ static struct symbol *next_file_symbol(struct elf *elf, struct symbol *sym)
 static bool is_uncorrelated_static_local(struct symbol *sym)
 {
 	static const char * const vars[] = {
-		"__key.",
-		"__warned.",
 		"__already_done.",
 		"__func__.",
+		"__key.",
+		"__warned.",
+		"_entry.",
+		"_entry_ptr.",
 		"_rs.",
 		"descriptor.",
 		"CSWTCH.",
Signature · 1 line
2.49.0
author maintainer
#80 lore raw

PATCH v3 62/64 livepatch/klp-build: Add --debug option to show cloning decisions

In reply to Josh Poimboeuf

Add a --debug option which gets passed to "objtool klp diff" to enable debug output related to cloning decisions.

---
 scripts/livepatch/klp-build | 16 +++++++++++++---
 1 file changed, 13 insertions(+), 3 deletions(-)
scripts/livepatch/klp-build +13 -3
index b54776fe3161..fe9af53a8476 100755
@@ -20,7 +20,7 @@ set -o nounset
 # This helps keep execution in pipes so pipefail+errexit can catch errors.
 shopt -s lastpipe
 
-unset SKIP_CLEANUP XTRACE
+unset DEBUG_CLONE SKIP_CLEANUP XTRACE
 
 REPLACE=1
 SHORT_CIRCUIT=0
@@ -120,6 +120,7 @@ Options:
    -v, --verbose		Pass V=1 to kernel/module builds
 
 Advanced Options:
+   -d, --debug			Show symbol/reloc cloning decisions
    -S, --short-circuit=STEP	Start at build step (requires prior --keep-tmp)
 				   1|orig	Build original kernel (default)
 				   2|patched	Build patched kernel
@@ -140,8 +141,8 @@ process_args() {
 	local long
 	local args
 
-	short="hj:o:vS:T"
-	long="help,jobs:,output:,no-replace,verbose,short-circuit:,keep-tmp"
+	short="hj:o:vdS:T"
+	long="help,jobs:,output:,no-replace,verbose,debug,short-circuit:,keep-tmp"
 
 	args=$(getopt --options "$short" --longoptions "$long" -- "$@") || {
 		echo; usage; exit
@@ -174,6 +175,11 @@ process_args() {
 				VERBOSE="V=1"
 				shift
 				;;
+			-d | --debug)
+				DEBUG_CLONE=1
+				keep_tmp=1
+				shift
+				;;
 			-S | --short-circuit)
 				[[ ! -d "$TMP_DIR" ]] && die "--short-circuit requires preserved klp-tmp dir"
 				keep_tmp=1
@@ -600,6 +606,7 @@ copy_patched_objects() {
 diff_objects() {
 	local log="$KLP_DIFF_LOG"
 	local files=()
+	local opts=()
 
 	rm -rf "$DIFF_DIR"
 	mkdir -p "$DIFF_DIR"
@@ -607,6 +614,8 @@ diff_objects() {
 	find "$PATCHED_DIR" -type f -name "*.o" | mapfile -t files
 	[[ ${#files[@]} -eq 0 ]] && die "no changes detected"
 
+	[[ -v DEBUG_CLONE ]] && opts=("--debug")
+
 	# Diff all changed objects
 	for file in "${files[@]}"; do
 		local rel_file="${file#"$PATCHED_DIR"/}"
@@ -620,6 +629,7 @@ diff_objects() {
 		cmd=("$SRC/tools/objtool/objtool")
 		cmd+=("klp")
 		cmd+=("diff")
+		(( ${#opts[@]} > 0 )) && cmd+=("${opts[@]}")
 		cmd+=("$orig_file")
 		cmd+=("$patched_file")
 		cmd+=("$out_file")
Signature · 1 line
2.49.0
author maintainer
#81 lore raw

PATCH v3 63/64 livepatch/klp-build: Add --show-first-changed option to show function divergence

In reply to Josh Poimboeuf

Add a --show-first-changed option to identify where changed functions begin to diverge:

- Parse 'objtool klp diff' output to find changed functions.

- Run objtool again on each object with --debug-checksum=<funcs>.

- Diff the per-instruction checksum debug output to locate the first differing instruction.

This can be useful for quickly determining where and why a function changed.

---
 scripts/livepatch/klp-build | 82 +++++++++++++++++++++++++++++++++++--
 1 file changed, 78 insertions(+), 4 deletions(-)
scripts/livepatch/klp-build +78 -4
index fe9af53a8476..e47056f75475 100755
@@ -20,7 +20,7 @@ set -o nounset
 # This helps keep execution in pipes so pipefail+errexit can catch errors.
 shopt -s lastpipe
 
-unset DEBUG_CLONE SKIP_CLEANUP XTRACE
+unset DEBUG_CLONE DIFF_CHECKSUM SKIP_CLEANUP XTRACE
 
 REPLACE=1
 SHORT_CIRCUIT=0
@@ -114,6 +114,7 @@ Usage: $SCRIPT [OPTIONS] PATCH_FILE(s)
 Generate a livepatch module.
 
 Options:
+   -f, --show-first-changed	Show address of first changed instruction
    -j, --jobs=<jobs>		Build jobs to run simultaneously [default: $JOBS]
    -o, --output=<file.ko>	Output file [default: livepatch-<patch-name>.ko]
        --no-replace		Disable livepatch atomic replace
@@ -141,8 +142,8 @@ process_args() {
 	local long
 	local args
 
-	short="hj:o:vdS:T"
-	long="help,jobs:,output:,no-replace,verbose,debug,short-circuit:,keep-tmp"
+	short="hfj:o:vdS:T"
+	long="help,show-first-changed,jobs:,output:,no-replace,verbose,debug,short-circuit:,keep-tmp"
 
 	args=$(getopt --options "$short" --longoptions "$long" -- "$@") || {
 		echo; usage; exit
@@ -155,6 +156,10 @@ process_args() {
 				usage
 				exit 0
 				;;
+			-f | --show-first-changed)
+				DIFF_CHECKSUM=1
+				shift
+				;;
 			-j | --jobs)
 				JOBS="$2"
 				shift 2
@@ -622,6 +627,7 @@ diff_objects() {
 		local orig_file="$rel_file"
 		local patched_file="$PATCHED_DIR/$rel_file"
 		local out_file="$DIFF_DIR/$rel_file"
+		local filter=()
 		local cmd=()
 
 		mkdir -p "$(dirname "$out_file")"
@@ -634,16 +640,80 @@ diff_objects() {
 		cmd+=("$patched_file")
 		cmd+=("$out_file")
 
+		if [[ -v DIFF_CHECKSUM ]]; then
+			filter=("grep0")
+			filter+=("-Ev")
+			filter+=("DEBUG: .*checksum: ")
+		else
+			filter=("cat")
+		fi
+
 		(
 			cd "$ORIG_DIR"
 			"${cmd[@]}"							\
 				1> >(tee -a "$log")					\
-				2> >(tee -a "$log" >&2) ||				\
+				2> >(tee -a "$log" | "${filter[@]}" >&2) ||		\
 				die "objtool klp diff failed"
 		)
 	done
 }
 
+# For each changed object, run objtool with --debug-checksum to get the
+# per-instruction checksums, and then diff those to find the first changed
+# instruction for each function.
+diff_checksums() {
+	local orig_log="$ORIG_DIR/checksum.log"
+	local patched_log="$PATCHED_DIR/checksum.log"
+	local -A funcs
+	local cmd=()
+	local line
+	local file
+	local func
+
+	gawk '/\.o: changed function: / {
+		sub(/:$/, "", $1)
+		print $1, $NF
+	}' "$KLP_DIFF_LOG" | mapfile -t lines
+
+	for line in "${lines[@]}"; do
+		read -r file func <<< "$line"
+		if [[ ! -v funcs["$file"] ]]; then
+			funcs["$file"]="$func"
+		else
+			funcs["$file"]+=" $func"
+		fi
+	done
+
+	cmd=("$SRC/tools/objtool/objtool")
+	cmd+=("--checksum")
+	cmd+=("--link")
+	cmd+=("--dry-run")
+
+	for file in "${!funcs[@]}"; do
+		local opt="--debug-checksum=${funcs[$file]// /,}"
+
+		(
+			cd "$ORIG_DIR"
+			"${cmd[@]}" "$opt" "$file" &> "$orig_log" || \
+				( cat "$orig_log" >&2; die "objtool --debug-checksum failed" )
+
+			cd "$PATCHED_DIR"
+			"${cmd[@]}" "$opt" "$file" &> "$patched_log" ||	\
+				( cat "$patched_log" >&2; die "objtool --debug-checksum failed" )
+		)
+
+		for func in ${funcs[$file]}; do
+			diff <( grep0 -E "^DEBUG: .*checksum: $func " "$orig_log"    | sed "s|$ORIG_DIR/||")	\
+			     <( grep0 -E "^DEBUG: .*checksum: $func " "$patched_log" | sed "s|$PATCHED_DIR/||")	\
+				| gawk '/^< DEBUG: / {
+					gsub(/:/, "")
+					printf "%s: %s: %s\n", $3, $5, $6
+					exit
+			}' || true
+		done
+	done
+}
+
 # Build and post-process livepatch module in $KMOD_DIR
 build_patch_module() {
 	local makefile="$KMOD_DIR/Kbuild"
@@ -747,6 +817,10 @@ fi
 if (( SHORT_CIRCUIT <= 3 )); then
 	status "Diffing objects"
 	diff_objects
+	if [[ -v DIFF_CHECKSUM ]]; then
+		status "Finding first changed instructions"
+		diff_checksums
+	fi
 fi
 
 if (( SHORT_CIRCUIT <= 4 )); then
Signature · 1 line
2.49.0
author maintainer
#82 lore raw

PATCH v3 64/64 livepatch: Introduce source code helpers for livepatch modules

In reply to Josh Poimboeuf

Add some helper macros which can be used by livepatch source .patch files to register callbacks, convert static calls to regular calls where needed, and patch syscalls.

---
 include/linux/livepatch_helpers.h | 79 +++++++++++++++++++++++++++++++
 1 file changed, 79 insertions(+)
 create mode 100644 include/linux/livepatch_helpers.h
include/linux/livepatch_helpers.h new file +79 -0
new file mode 100644
index 000000000000..337bee91d7da
@@ -0,0 +1,79 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_LIVEPATCH_HELPERS_H
+#define _LINUX_LIVEPATCH_HELPERS_H
+
+/*
+ * Interfaces for use by livepatch patches
+ */
+
+#include <linux/syscalls.h>
+#include <linux/livepatch.h>
+
+#ifdef MODULE
+#define KLP_OBJNAME __KBUILD_MODNAME
+#else
+#define KLP_OBJNAME vmlinux
+#endif
+
+/* Livepatch callback registration */
+
+#define KLP_CALLBACK_PTRS ".discard.klp_callback_ptrs"
+
+#define KLP_PRE_PATCH_CALLBACK(func)						\
+	klp_pre_patch_t __used __section(KLP_CALLBACK_PTRS)			\
+		__PASTE(__KLP_PRE_PATCH_PREFIX, KLP_OBJNAME) = func
+
+#define KLP_POST_PATCH_CALLBACK(func)						\
+	klp_post_patch_t __used __section(KLP_CALLBACK_PTRS)			\
+		__PASTE(__KLP_POST_PATCH_PREFIX, KLP_OBJNAME) = func
+
+#define KLP_PRE_UNPATCH_CALLBACK(func)						\
+	klp_pre_unpatch_t __used __section(KLP_CALLBACK_PTRS)			\
+		__PASTE(__KLP_PRE_UNPATCH_PREFIX, KLP_OBJNAME) = func
+
+#define KLP_POST_UNPATCH_CALLBACK(func)						\
+	klp_post_unpatch_t __used __section(KLP_CALLBACK_PTRS)			\
+		__PASTE(__KLP_POST_UNPATCH_PREFIX, KLP_OBJNAME) = func
+
+/*
+ * KLP_STATIC_CALL
+ *
+ * Replace static_call() usage with this macro when create-diff-object
+ * recommends it due to the original static call key living in a module.
+ *
+ * This converts the static call to a regular indirect call.
+ */
+#define KLP_STATIC_CALL(name) \
+	((typeof(STATIC_CALL_TRAMP(name))*)(STATIC_CALL_KEY(name).func))
+
+/* Syscall patching */
+
+#define KLP_SYSCALL_DEFINE1(name, ...) KLP_SYSCALL_DEFINEx(1, _##name, __VA_ARGS__)
+#define KLP_SYSCALL_DEFINE2(name, ...) KLP_SYSCALL_DEFINEx(2, _##name, __VA_ARGS__)
+#define KLP_SYSCALL_DEFINE3(name, ...) KLP_SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)
+#define KLP_SYSCALL_DEFINE4(name, ...) KLP_SYSCALL_DEFINEx(4, _##name, __VA_ARGS__)
+#define KLP_SYSCALL_DEFINE5(name, ...) KLP_SYSCALL_DEFINEx(5, _##name, __VA_ARGS__)
+#define KLP_SYSCALL_DEFINE6(name, ...) KLP_SYSCALL_DEFINEx(6, _##name, __VA_ARGS__)
+
+#define KLP_SYSCALL_DEFINEx(x, sname, ...)				\
+	__KLP_SYSCALL_DEFINEx(x, sname, __VA_ARGS__)
+
+#ifdef CONFIG_X86_64
+// TODO move this to arch/x86/include/asm/syscall_wrapper.h and share code
+#define __KLP_SYSCALL_DEFINEx(x, name, ...)			\
+	static long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__));	\
+	static inline long __klp_do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));\
+	__X64_SYS_STUBx(x, name, __VA_ARGS__)				\
+	__IA32_SYS_STUBx(x, name, __VA_ARGS__)				\
+	static long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__))	\
+	{								\
+		long ret = __klp_do_sys##name(__MAP(x,__SC_CAST,__VA_ARGS__));\
+		__MAP(x,__SC_TEST,__VA_ARGS__);				\
+		__PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__));	\
+		return ret;						\
+	}								\
+	static inline long __klp_do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__))
+
+#endif
+
+#endif /* _LINUX_LIVEPATCH_HELPERS_H */
Signature · 1 line
2.49.0