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/cover.1725334260.git.jpoim...@kernel.org

  - 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:

diff --git a/arch/um/include/asm/Kbuild b/arch/um/include/asm/Kbuild
index 04ab3b653a48..1934fa0df888 100644
--- a/arch/um/include/asm/Kbuild
+++ b/arch/um/include/asm/Kbuild
@@ -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
diff --git a/arch/um/include/shared/common-offsets.h 
b/arch/um/include/shared/common-offsets.h
index 8ca66a1918c3..a6f77cb6aa7e 100644
--- a/arch/um/include/shared/common-offsets.h
+++ b/arch/um/include/shared/common-offsets.h
@@ -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));
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 62faa62b5959..448c6bfb71d6 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -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
diff --git a/arch/x86/include/asm/alternative.h 
b/arch/x86/include/asm/alternative.h
index e206e0f96568..eb24d9ba30d7 100644
--- a/arch/x86/include/asm/alternative.h
+++ b/arch/x86/include/asm/alternative.h
@@ -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" ;                       \
diff --git a/arch/x86/include/asm/asm.h b/arch/x86/include/asm/asm.h
index 62dff336f206..e9b6d2d006c6 100644
--- a/arch/x86/include/asm/asm.h
+++ b/arch/x86/include/asm/asm.h
@@ -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__
diff --git a/arch/x86/include/asm/jump_label.h 
b/arch/x86/include/asm/jump_label.h
index 6081c33e1566..7a6b0e5d85c1 100644
--- a/arch/x86/include/asm/jump_label.h
+++ b/arch/x86/include/asm/jump_label.h
@@ -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:
diff --git a/arch/x86/include/asm/static_call.h 
b/arch/x86/include/asm/static_call.h
index 41502bd2afd6..e03ad9bbbf59 100644
--- a/arch/x86/include/asm/static_call.h
+++ b/arch/x86/include/asm/static_call.h
@@ -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")
diff --git a/arch/x86/kernel/alternative.c b/arch/x86/kernel/alternative.c
index 499dc1ca2050..bacd6c157626 100644
--- a/arch/x86/kernel/alternative.c
+++ b/arch/x86/kernel/alternative.c
@@ -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);
 
        /*
diff --git a/arch/x86/kernel/asm-offsets.c b/arch/x86/kernel/asm-offsets.c
index 6259b474073b..3d3eef7fae32 100644
--- a/arch/x86/kernel/asm-offsets.c
+++ b/arch/x86/kernel/asm-offsets.c
@@ -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));
 }
diff --git a/arch/x86/kernel/unwind_orc.c b/arch/x86/kernel/unwind_orc.c
index 4624d6d916a2..977ee75e047c 100644
--- a/arch/x86/kernel/unwind_orc.c
+++ b/arch/x86/kernel/unwind_orc.c
@@ -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;
 
diff --git a/arch/x86/um/shared/sysdep/kernel-offsets.h 
b/arch/x86/um/shared/sysdep/kernel-offsets.h
index 6fd1ed400399..8215a0200ddd 100644
--- a/arch/x86/um/shared/sysdep/kernel-offsets.h
+++ b/arch/x86/um/shared/sysdep/kernel-offsets.h
@@ -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);
diff --git a/include/asm-generic/vmlinux.lds.h 
b/include/asm-generic/vmlinux.lds.h
index 3a0865e0a1a7..928e175254f6 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -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_]*
diff --git a/include/linux/compiler.h b/include/linux/compiler.h
index b90422671874..2039da81cf16 100644
--- a/include/linux/compiler.h
+++ b/include/linux/compiler.h
@@ -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"))
diff --git a/include/linux/jump_label.h b/include/linux/jump_label.h
index 9ff1ecc8e7a8..fdb79dd1ebd8 100644
--- a/include/linux/jump_label.h
+++ b/include/linux/jump_label.h
@@ -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__
 
diff --git a/include/linux/livepatch_helpers.h 
b/include/linux/livepatch_helpers.h
index 09f4a2d53fd7..337bee91d7da 100644
--- a/include/linux/livepatch_helpers.h
+++ b/include/linux/livepatch_helpers.h
@@ -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__)
diff --git a/include/linux/objtool.h b/include/linux/objtool.h
index d4137a46ee70..7d7bb7f1af69 100644
--- a/include/linux/objtool.h
+++ b/include/linux/objtool.h
@@ -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
 
diff --git a/include/linux/static_call.h b/include/linux/static_call.h
index 78a77a4ae0ea..5210612817f2 100644
--- a/include/linux/static_call.h
+++ b/include/linux/static_call.h
@@ -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);
diff --git a/include/linux/static_call_types.h 
b/include/linux/static_call_types.h
index 5a00b8b2cf9f..eb772df625d4 100644
--- a/include/linux/static_call_types.h
+++ b/include/linux/static_call_types.h
@@ -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);
diff --git a/kernel/bounds.c b/kernel/bounds.c
index 29b2cd00df2c..f9bc13727721 100644
--- a/kernel/bounds.c
+++ b/kernel/bounds.c
@@ -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 */
 
diff --git a/kernel/extable.c b/kernel/extable.c
index 0ae3ee2ef266..71f482581cab 100644
--- a/kernel/extable.c
+++ b/kernel/extable.c
@@ -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);
diff --git a/kernel/livepatch/Kconfig b/kernel/livepatch/Kconfig
index 53d51ed619a3..4c0a9c18d0b2 100644
--- a/kernel/livepatch/Kconfig
+++ b/kernel/livepatch/Kconfig
@@ -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
diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
index 41420d24e62e..28a1c08e3b22 100644
--- a/scripts/Makefile.lib
+++ b/scripts/Makefile.lib
@@ -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)
diff --git a/scripts/Makefile.modfinal b/scripts/Makefile.modfinal
index 878d0d25a461..7a888e1ff70f 100644
--- a/scripts/Makefile.modfinal
+++ b/scripts/Makefile.modfinal
@@ -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]  $@
diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh
index acffa3c935f2..59f875236292 100755
--- a/scripts/link-vmlinux.sh
+++ b/scripts/link-vmlinux.sh
@@ -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=
diff --git a/scripts/livepatch/fix-patch-lines 
b/scripts/livepatch/fix-patch-lines
index 73c5e3dea46e..fa7d4f6592e6 100755
--- a/scripts/livepatch/fix-patch-lines
+++ b/scripts/livepatch/fix-patch-lines
@@ -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
diff --git a/scripts/livepatch/klp-build b/scripts/livepatch/klp-build
index f7d88726ed4f..e47056f75475 100755
--- a/scripts/livepatch/klp-build
+++ b/scripts/livepatch/klp-build
@@ -2,12 +2,11 @@
 # SPDX-License-Identifier: GPL-2.0
 #
 # Build a livepatch module
-#
 
-# shellcheck disable=SC1090
+# shellcheck disable=SC1090,SC2155
 
-if (( BASH_VERSINFO[0] < 4 \
-       || (BASH_VERSINFO[0] == 4 && BASH_VERSINFO[1] < 4) )); then
+if (( BASH_VERSINFO[0]  < 4 || \
+     (BASH_VERSINFO[0] == 4 && BASH_VERSINFO[1] < 4) )); then
                echo "error: this script requires bash 4.4+" >&2
        exit 1
 fi
@@ -22,16 +21,16 @@ set -o nounset
 shopt -s lastpipe
 
 unset DEBUG_CLONE DIFF_CHECKSUM SKIP_CLEANUP XTRACE
+
 REPLACE=1
 SHORT_CIRCUIT=0
+JOBS="$(getconf _NPROCESSORS_ONLN)"
+VERBOSE="-s"
 shopt -o xtrace | grep -q 'on' && XTRACE=1
+
 # Avoid removing the previous $TMP_DIR until args have been fully processed.
 KEEP_TMP=1
 
-CPUS="$(getconf _NPROCESSORS_ONLN)"
-VERBOSE="-s"
-
-
 SCRIPT="$(basename "$0")"
 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
 FIX_PATCH_LINES="$SCRIPT_DIR/fix-patch-lines"
@@ -62,7 +61,7 @@ status() {
 }
 
 warn() {
-       echo "error: $(basename "$SCRIPT"): $*" >&2
+       echo "error: $SCRIPT: $*" >&2
 }
 
 die() {
@@ -99,7 +98,7 @@ cleanup() {
        revert_patches "--recount"
        restore_files
        [[ "$KEEP_TMP" -eq 0 ]] && rm -rf "$TMP_DIR"
-       true
+       return 0
 }
 
 trap_err() {
@@ -116,12 +115,13 @@ Generate a livepatch module.
 
 Options:
    -f, --show-first-changed    Show address of first changed instruction
-   -o, --output <file.ko>      Output file [default: livepatch-<patch-name>.ko]
+   -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
    -v, --verbose               Pass V=1 to kernel/module builds
 
 Advanced Options:
-   -D, --debug                 Show symbol/reloc cloning decisions
+   -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
@@ -142,8 +142,8 @@ process_args() {
        local long
        local args
 
-       short="hfo:vDS:T"
-       
long="help,show-first-changed,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
@@ -160,6 +160,10 @@ process_args() {
                                DIFF_CHECKSUM=1
                                shift
                                ;;
+                       -j | --jobs)
+                               JOBS="$2"
+                               shift 2
+                               ;;
                        -o | --output)
                                [[ "$2" != *.ko ]] && die "output filename 
should end with .ko"
                                OUTFILE="$2"
@@ -176,7 +180,7 @@ process_args() {
                                VERBOSE="V=1"
                                shift
                                ;;
-                       -D | --debug)
+                       -d | --debug)
                                DEBUG_CLONE=1
                                keep_tmp=1
                                shift
@@ -233,14 +237,17 @@ validate_config() {
        source "$CONFIG" || die "no .config file in $(dirname "$CONFIG")"
        xtrace_restore
 
-       [[ -v CONFIG_LIVEPATCH ]]                       \
-               || die "CONFIG_LIVEPATCH not enabled"
+       [[ -v CONFIG_LIVEPATCH ]] ||                    \
+               die "CONFIG_LIVEPATCH not enabled"
 
-       [[ -v CONFIG_GCC_PLUGIN_LATENT_ENTROPY ]]       \
-               && die "kernel option 'CONFIG_GCC_PLUGIN_LATENT_ENTROPY' not 
supported"
+       [[ -v CONFIG_KLP_BUILD ]] ||                    \
+               die "CONFIG_KLP_BUILD not enabled"
 
-       [[ -v CONFIG_GCC_PLUGIN_RANDSTRUCT ]]           \
-               && die "kernel option 'CONFIG_GCC_PLUGIN_RANDSTRUCT' not 
supported"
+       [[ -v CONFIG_GCC_PLUGIN_LATENT_ENTROPY ]] &&    \
+               die "kernel option 'CONFIG_GCC_PLUGIN_LATENT_ENTROPY' not 
supported"
+
+       [[ -v CONFIG_GCC_PLUGIN_RANDSTRUCT ]] &&        \
+               die "kernel option 'CONFIG_GCC_PLUGIN_RANDSTRUCT' not supported"
 
        return 0
 }
@@ -282,7 +289,7 @@ set_kernelversion() {
        localversion="$(cd "$SRC" && KERNELVERSION="$localversion" 
./scripts/setlocalversion)"
        [[ -z "$localversion" ]] && die "setlocalversion failed"
 
-       echo "echo $localversion" > "$file"
+       sed -i "2i echo $localversion; exit 0" scripts/setlocalversion
 }
 
 get_patch_files() {
@@ -299,7 +306,7 @@ git_refresh() {
        local patch="$1"
        local files=()
 
-       [[ ! -d "$SRC/.git" ]] && return
+       [[ ! -e "$SRC/.git" ]] && return
 
        get_patch_files "$patch" | mapfile -t files
 
@@ -334,7 +341,14 @@ apply_patch() {
 
        [[ ! -f "$patch" ]] && die "$patch doesn't exist"
 
-       ( cd "$SRC" && git apply "${extra_args[@]}" "$patch" )
+       (
+               cd "$SRC"
+
+               # The sed strips the version signature from 'git format-patch',
+               # otherwise 'git apply --recount' warns.
+               sed -n '/^-- /q;p' "$patch" |
+                       git apply "${extra_args[@]}"
+       )
 
        APPLIED_PATCHES+=("$patch")
 }
@@ -345,7 +359,12 @@ revert_patch() {
        local extra_args=("$@")
        local tmp=()
 
-       ( cd "$SRC" && git apply --reverse "${extra_args[@]}" "$patch" )
+       (
+               cd "$SRC"
+
+               sed -n '/^-- /q;p' "$patch" |
+                       git apply --reverse "${extra_args[@]}"
+       )
        git_refresh "$patch"
 
        for p in "${APPLIED_PATCHES[@]}"; do
@@ -373,9 +392,6 @@ revert_patches() {
        done
 
        APPLIED_PATCHES=()
-
-       # Make sure git actually sees the patches have been reverted.
-       [[ -d "$SRC/.git" ]] && (cd "$SRC" && git update-index -q --refresh)
 }
 
 validate_patches() {
@@ -412,10 +428,8 @@ refresh_patch() {
        mkdir -p "$tmpdir/a"
        mkdir -p "$tmpdir/b"
 
-       # Find all source files affected by the patch
-       grep0 -E '^(--- |\+\+\+ )[^ /]+' "$patch"       |
-               sed -E 's/(--- |\+\+\+ )[^ /]+\///'     |
-               sort | uniq | mapfile -t files
+       # Get all source files affected by the patch
+       get_patch_files "$patch" | mapfile -t files
 
        # Copy orig source files to 'a'
        ( cd "$SRC" && echo "${files[@]}" | xargs cp --parents 
--target-directory="$tmpdir/a" )
@@ -459,13 +473,26 @@ fix_patches() {
        done
 }
 
+clean_kernel() {
+       local cmd=()
+
+       cmd=("make")
+       cmd+=("--silent")
+       cmd+=("-j$JOBS")
+       cmd+=("clean")
+
+       (
+               cd "$SRC"
+               "${cmd[@]}"
+       )
+}
+
 build_kernel() {
        local log="$TMP_DIR/build.log"
        local objtool_args=()
        local cmd=()
 
        objtool_args=("--checksum")
-       [[ -v OBJTOOL_ARGS ]] && objtool_args+=("${OBJTOOL_ARGS}")
 
        cmd=("make")
 
@@ -487,7 +514,7 @@ build_kernel() {
        cmd+=("KBUILD_MODPOST_WARN=1")
 
        cmd+=("$VERBOSE")
-       cmd+=("-j$CPUS")
+       cmd+=("-j$JOBS")
        cmd+=("KCFLAGS=-ffunction-sections -fdata-sections")
        cmd+=("OBJTOOL_ARGS=${objtool_args[*]}")
        cmd+=("vmlinux")
@@ -496,7 +523,7 @@ build_kernel() {
        (
                cd "$SRC"
                "${cmd[@]}"                                                     
\
-                       > >(tee -a "$log")                                      
\
+                       1> >(tee -a "$log")                                     
\
                        2> >(tee -a "$log" | grep0 -v "modpost.*undefined!" >&2)
        )
 }
@@ -512,18 +539,31 @@ find_objects() {
                    -printf '%P\n'
 }
 
-# Copy all objects (.o archives) to $ORIG_DIR
+# Copy all .o archives to $ORIG_DIR
 copy_orig_objects() {
+       local files=()
 
        rm -rf "$ORIG_DIR"
        mkdir -p "$ORIG_DIR"
 
-       (
-               cd "$OBJ"
-               find_objects                                            \
-                       | sed 's/\.ko$/.o/'                             \
-                       | xargs cp --parents --target-directory="$ORIG_DIR"
-       )
+       find_objects | mapfile -t files
+
+       xtrace_save "copying orig objects"
+       for _file in "${files[@]}"; do
+               local rel_file="${_file/.ko/.o}"
+               local file="$OBJ/$rel_file"
+               local file_dir="$(dirname "$file")"
+               local orig_file="$ORIG_DIR/$rel_file"
+               local orig_dir="$(dirname "$orig_file")"
+               local cmd_file="$file_dir/.$(basename "$file").cmd"
+
+               [[ ! -f "$file" ]] && die "missing $(basename "$file") for 
$_file"
+
+               mkdir -p "$orig_dir"
+               cp -f "$file" "$orig_dir"
+               [[ -e "$cmd_file" ]] && cp -f "$cmd_file" "$orig_dir"
+       done
+       xtrace_restore
 
        mv -f "$TMP_DIR/build.log" "$ORIG_DIR"
        touch "$TIMESTAMP"
@@ -531,9 +571,9 @@ copy_orig_objects() {
 
 # Copy all changed objects to $PATCHED_DIR
 copy_patched_objects() {
-       local found
        local files=()
        local opts=()
+       local found=0
 
        rm -rf "$PATCHED_DIR"
        mkdir -p "$PATCHED_DIR"
@@ -544,24 +584,25 @@ copy_patched_objects() {
 
        find_objects "${opts[@]}" | mapfile -t files
 
-       xtrace_save "processing all objects"
+       xtrace_save "copying changed objects"
        for _file in "${files[@]}"; do
                local rel_file="${_file/.ko/.o}"
                local file="$OBJ/$rel_file"
                local orig_file="$ORIG_DIR/$rel_file"
                local patched_file="$PATCHED_DIR/$rel_file"
+               local patched_dir="$(dirname "$patched_file")"
 
                [[ ! -f "$file" ]] && die "missing $(basename "$file") for 
$_file"
 
                cmp -s "$orig_file" "$file" && continue
 
-               mkdir -p "$(dirname "$patched_file")"
-               cp -f "$file" "$patched_file"
+               mkdir -p "$patched_dir"
+               cp -f "$file" "$patched_dir"
                found=1
        done
        xtrace_restore
 
-       [[ -n "$found" ]] || die "no changes detected"
+       (( found == 0 )) && die "no changes detected"
 
        mv -f "$TMP_DIR/build.log" "$PATCHED_DIR"
 }
@@ -610,9 +651,9 @@ diff_objects() {
                (
                        cd "$ORIG_DIR"
                        "${cmd[@]}"                                             
        \
-                               > >(tee -a "$log")                              
        \
-                               2> >(tee -a "$log" | "${filter[@]}" >&2)        
        \
-                               || die "objtool klp diff failed"
+                               1> >(tee -a "$log")                             
        \
+                               2> >(tee -a "$log" | "${filter[@]}" >&2) ||     
        \
+                               die "objtool klp diff failed"
                )
        done
 }
@@ -653,12 +694,12 @@ diff_checksums() {
 
                (
                        cd "$ORIG_DIR"
-                       "${cmd[@]}" "$opt" "$file" &> "$orig_log"       \
-                               || ( cat "$orig_log" >&2; die "objtool 
--debug-checksum failed" )
+                       "${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" )
+                       "${cmd[@]}" "$opt" "$file" &> "$patched_log" || \
+                               ( cat "$patched_log" >&2; die "objtool 
--debug-checksum failed" )
                )
 
                for func in ${funcs[$file]}; do
@@ -677,6 +718,7 @@ diff_checksums() {
 build_patch_module() {
        local makefile="$KMOD_DIR/Kbuild"
        local log="$KMOD_DIR/build.log"
+       local kmod_file
        local cflags=()
        local files=()
        local cmd=()
@@ -694,19 +736,20 @@ build_patch_module() {
 
        for file in "${files[@]}"; do
                local rel_file="${file#"$DIFF_DIR"/}"
+               local orig_file="$ORIG_DIR/$rel_file"
+               local orig_dir="$(dirname "$orig_file")"
                local kmod_file="$KMOD_DIR/$rel_file"
-               local cmd_file
+               local kmod_dir="$(dirname "$kmod_file")"
+               local cmd_file="$orig_dir/.$(basename "$file").cmd"
 
-               mkdir -p "$(dirname "$kmod_file")"
-               cp -f "$file" "$kmod_file"
+               mkdir -p "$kmod_dir"
+               cp -f "$file" "$kmod_dir"
+               [[ -e "$cmd_file" ]] && cp -f "$cmd_file" "$kmod_dir"
 
                # Tell kbuild this is a prebuilt object
                cp -f "$file" "${kmod_file}_shipped"
 
                echo -n " $rel_file" >> "$makefile"
-
-               cmd_file="$ORIG_DIR/$(dirname "$rel_file")/.$(basename 
"$rel_file").cmd"
-               [[ -e "$cmd_file" ]] && cp -f "$cmd_file" "$(dirname 
"$kmod_file")"
        done
 
        echo >> "$makefile"
@@ -717,7 +760,7 @@ build_patch_module() {
 
        cmd=("make")
        cmd+=("$VERBOSE")
-       cmd+=("-j$CPUS")
+       cmd+=("-j$JOBS")
        cmd+=("--directory=.")
        cmd+=("M=$KMOD_DIR")
        cmd+=("KCFLAGS=${cflags[*]}")
@@ -726,17 +769,22 @@ build_patch_module() {
        (
                cd "$SRC"
                "${cmd[@]}"                                                     
\
-                       >  >(tee -a "$log")                                     
\
+                       1> >(tee -a "$log")                                     
\
                        2> >(tee -a "$log" >&2)
        )
 
+       kmod_file="$KMOD_DIR/$NAME.ko"
+
        # Save off the intermediate binary for debugging
-       cp -f "$KMOD_DIR/$NAME.ko" "$KMOD_DIR/$NAME.ko.orig"
+       cp -f "$kmod_file" "$kmod_file.orig"
+
+       # Work around issue where slight .config change makes corrupt BTF
+       objcopy --remove-section=.BTF "$kmod_file"
 
        # Fix (and work around) linker wreckage for klp syms / relocs
-       "$SRC/tools/objtool/objtool" klp post-link "$KMOD_DIR/$NAME.ko" || die 
"objtool klp post-link failed"
+       "$SRC/tools/objtool/objtool" klp post-link "$kmod_file" || die "objtool 
klp post-link failed"
 
-       cp -f "$KMOD_DIR/$NAME.ko" "$OUTFILE"
+       cp -f "$kmod_file" "$OUTFILE"
 }
 
 
@@ -746,8 +794,10 @@ process_args "$@"
 do_init
 
 if (( SHORT_CIRCUIT <= 1 )); then
-       status "Building original kernel"
+       status "Validating patches"
        validate_patches
+       status "Building original kernel"
+       clean_kernel
        build_kernel
        status "Copying original object files"
        copy_orig_objects
diff --git a/scripts/mod/devicetable-offsets.c 
b/scripts/mod/devicetable-offsets.c
index d3d00e85edf7..ef2ffb68f69d 100644
--- a/scripts/mod/devicetable-offsets.c
+++ b/scripts/mod/devicetable-offsets.c
@@ -1,4 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
+#define COMPILE_OFFSETS
 #include <linux/kbuild.h>
 #include <linux/mod_devicetable.h>
 
diff --git a/tools/include/linux/static_call_types.h 
b/tools/include/linux/static_call_types.h
index 5a00b8b2cf9f..eb772df625d4 100644
--- a/tools/include/linux/static_call_types.h
+++ b/tools/include/linux/static_call_types.h
@@ -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);
diff --git a/tools/include/linux/string.h b/tools/include/linux/string.h
index 8499f509f03e..51ad3cf4fa82 100644
--- a/tools/include/linux/string.h
+++ b/tools/include/linux/string.h
@@ -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 *);
diff --git a/tools/objtool/arch/x86/decode.c b/tools/objtool/arch/x86/decode.c
index 3b40afe144fe..c4d6b90b1134 100644
--- a/tools/objtool/arch/x86/decode.c
+++ b/tools/objtool/arch/x86/decode.c
@@ -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);
 }
diff --git a/tools/objtool/builtin-check.c b/tools/objtool/builtin-check.c
index 84918593d935..cd2c4a387100 100644
--- a/tools/objtool/builtin-check.c
+++ b/tools/objtool/builtin-check.c
@@ -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)
diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 9848ca612683..1eb6489ae459 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -187,20 +187,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.
  */
@@ -431,7 +417,6 @@ static int decode_instructions(struct objtool_file *file)
        struct symbol *func;
        unsigned long offset;
        struct instruction *insn;
-       int ret;
 
        for_each_sec(file->elf, sec) {
                struct instruction *insns = NULL;
@@ -480,11 +465,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_size(sec) - offset,
-                                                     insn);
-                       if (ret)
-                               return ret;
+                       if (arch_decode_instruction(file, sec, offset, 
sec_size(sec) - offset, insn))
+                               return -1;
 
                        prev_len = insn->len;
 
@@ -600,7 +582,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;
@@ -622,9 +604,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;
@@ -660,7 +641,7 @@ static int create_static_call_sections(struct objtool_file 
*file)
                 * site entries to take advantage of vmlinux static call
                 * privileges.
                 */
-               if (!!opts.dryrun || !file->klp)
+               if (!file->klp)
                        WARN("file already has .static_call_sites section, 
skipping");
 
                return 0;
@@ -678,8 +659,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) {
@@ -744,9 +732,7 @@ static int create_retpoline_sites_sections(struct 
objtool_file *file)
 
        sec = find_section_by_name(file->elf, ".retpoline_sites");
        if (sec) {
-               if (!opts.dryrun)
-                       WARN("file already has .retpoline_sites, skipping");
-
+               WARN("file already has .retpoline_sites, skipping");
                return 0;
        }
 
@@ -784,9 +770,7 @@ static int create_return_sites_sections(struct objtool_file 
*file)
 
        sec = find_section_by_name(file->elf, ".return_sites");
        if (sec) {
-               if (!opts.dryrun)
-                       WARN("file already has .return_sites, skipping");
-
+               WARN("file already has .return_sites, skipping");
                return 0;
        }
 
@@ -824,9 +808,7 @@ static int create_ibt_endbr_seal_sections(struct 
objtool_file *file)
 
        sec = find_section_by_name(file->elf, ".ibt_endbr_seal");
        if (sec) {
-               if (!opts.dryrun)
-                       WARN("file already has .ibt_endbr_seal, skipping");
-
+               WARN("file already has .ibt_endbr_seal, skipping");
                return 0;
        }
 
@@ -883,9 +865,7 @@ static int create_cfi_sections(struct objtool_file *file)
 
        sec = find_section_by_name(file->elf, ".cfi_sites");
        if (sec) {
-               if (!opts.dryrun)
-                       WARN("file already has .cfi_sites section, skipping");
-
+               WARN("file already has .cfi_sites section, skipping");
                return 0;
        }
 
@@ -937,7 +917,7 @@ static int create_mcount_loc_sections(struct objtool_file 
*file)
                 * Livepatch modules have already extracted their __mcount_loc
                 * entries to cover the !CONFIG_FTRACE_MCOUNT_USE_OBJTOOL case.
                 */
-               if (!opts.dryrun && !file->klp)
+               if (!file->klp)
                        WARN("file already has __mcount_loc section, skipping");
 
                return 0;
@@ -983,9 +963,7 @@ static int create_direct_call_sections(struct objtool_file 
*file)
 
        sec = find_section_by_name(file->elf, ".call_sites");
        if (sec) {
-               if (!opts.dryrun)
-                       WARN("file already has .call_sites section, skipping");
-
+               WARN("file already has .call_sites section, skipping");
                return 0;
        }
 
@@ -1548,7 +1526,6 @@ static int add_jump_destinations(struct objtool_file 
*file)
 {
        struct instruction *insn;
        struct reloc *reloc;
-       int ret;
 
        for_each_insn(file, insn) {
                struct symbol *func = insn_func(insn);
@@ -1556,7 +1533,6 @@ static int add_jump_destinations(struct objtool_file 
*file)
                struct section *dest_sec;
                struct symbol *dest_sym;
                unsigned long dest_off;
-               bool dest_undef = false;
 
                if (!is_static_jump(insn))
                        continue;
@@ -1573,78 +1549,85 @@ static int add_jump_destinations(struct objtool_file 
*file)
                if (!reloc) {
                        dest_sec = insn->sec;
                        dest_off = arch_jump_destination(insn);
-               } else if (is_undef_sym(reloc->sym)) {
-                       dest_sym = reloc->sym;
-                       dest_undef = true;
+                       dest_sym = dest_sec->sym;
                } else {
-                       dest_sec = reloc->sym->sec;
-                       dest_off = reloc->sym->sym.st_value +
-                                  arch_insn_adjusted_addend(insn, reloc);
-               }
-
-               if (!dest_undef) {
-                       dest_insn = find_insn(file, dest_sec, dest_off);
-                       if (!dest_insn) {
-                               struct symbol *sym = 
find_symbol_by_offset(dest_sec, dest_off);
-
-                               /*
-                                * 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);
+                       dest_sym = reloc->sym;
+                       if (is_undef_sym(dest_sym)) {
+                               if (dest_sym->retpoline_thunk) {
+                                       if (add_retpoline_call(file, insn))
+                                               return -1;
                                        continue;
                                }
 
-                               /*
-                                * 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)
+                               if (dest_sym->return_thunk) {
+                                       add_return_call(file, insn, true);
                                        continue;
+                               }
 
-                               ERROR_INSN(insn, "can't find jump dest 
instruction at %s+0x%lx",
-                                         dest_sec->name, dest_off);
-                               return -1;
+                               /* 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);
+               }
+
+               dest_insn = find_insn(file, dest_sec, dest_off);
+               if (!dest_insn) {
+                       struct symbol *sym = find_symbol_by_offset(dest_sec, 
dest_off);
+
+                       /*
+                        * 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);
+                               continue;
+                       }
+
+                       /*
+                        * 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",
+                                  offstr(dest_sec, dest_off));
+                       return -1;
+               }
+
+               if (!dest_sym || is_sec_sym(dest_sym)) {
                        dest_sym = dest_insn->sym;
                        if (!dest_sym)
                                goto set_jump_dest;
                }
 
-               if (dest_sym->retpoline_thunk) {
-                       ret = add_retpoline_call(file, insn);
-                       if (ret)
-                               return ret;
+               if (dest_sym->retpoline_thunk && dest_insn->offset == 
dest_sym->offset) {
+                       if (add_retpoline_call(file, insn))
+                               return -1;
                        continue;
                }
 
-               if (dest_sym->return_thunk) {
+               if (dest_sym->return_thunk && dest_insn->offset == 
dest_sym->offset) {
                        add_return_call(file, insn, true);
                        continue;
                }
 
-               if (dest_undef) {
-                       /* External symbol */
-                       if (func) {
-                               /* External sibling call */
-                               ret = add_call_dest(file, insn, dest_sym, true);
-                               if (ret)
-                                       return ret;
-                               continue;
-                       }
-
-                       /* Non-func asm code jumping to external symbol */
-                       continue;
-               }
-
                if (!insn->sym || insn->sym == dest_insn->sym)
                        goto set_jump_dest;
 
@@ -1675,9 +1658,8 @@ static int add_jump_destinations(struct objtool_file 
*file)
 
                if (is_first_func_insn(file, dest_insn)) {
                        /* Internal sibling call */
-                       ret = add_call_dest(file, insn, dest_sym, true);
-                       if (ret)
-                               return ret;
+                       if (add_call_dest(file, insn, dest_sym, true))
+                               return -1;
                        continue;
                }
 
@@ -1708,7 +1690,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);
@@ -1720,9 +1701,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;
@@ -1746,19 +1726,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;
                }
        }
 
@@ -1976,7 +1953,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;
@@ -2008,16 +1984,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));
@@ -2205,15 +2177,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;
@@ -2227,7 +2197,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;
@@ -2237,9 +2206,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;
@@ -2356,7 +2324,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)
@@ -2390,9 +2358,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;
@@ -2636,70 +2603,57 @@ static bool validate_branch_enabled(void)
 
 static int decode_sections(struct objtool_file *file)
 {
-       int ret;
-
        file->klp = is_livepatch_module(file);
 
        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 (validate_branch_enabled() || 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_jump_destinations() */
        mark_holes(file);
@@ -2708,9 +2662,8 @@ static int decode_sections(struct objtool_file *file)
         * 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;
 }
@@ -3464,7 +3417,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_insn_adjusted_addend(insn, reloc) / sizeof(void *));
+       idx = arch_insn_adjusted_addend(insn, reloc) / sizeof(void *);
 
        if (file->pv_ops[idx].clean)
                return true;
@@ -3692,45 +3645,44 @@ static void checksum_update_insn(struct objtool_file 
*file, struct symbol *func,
                                 struct instruction *insn)
 {
        struct reloc *reloc = insn_reloc(file, insn);
-       struct symbol *dest = insn_call_dest(insn);
+       unsigned long offset;
+       struct symbol *sym;
 
-       if (dest && !reloc) {
-               checksum_update(func, insn, insn->sec->data->d_buf + 
insn->offset, 1);
-               checksum_update(func, insn, dest->name, strlen(dest->name));
-       } else if (!insn->fake) {
-               checksum_update(func, insn, insn->sec->data->d_buf + 
insn->offset, insn->len);
+       if (insn->fake)
+               return;
+
+       checksum_update(func, insn, insn->sec->data->d_buf + insn->offset, 
insn->len);
+
+       if (!reloc) {
+               struct symbol *call_dest = insn_call_dest(insn);
+
+               if (call_dest)
+                       checksum_update(func, insn, call_dest->demangled_name,
+                                       strlen(call_dest->demangled_name));
+               return;
        }
 
-       if (reloc) {
-               struct symbol *sym = reloc->sym;
+       sym = reloc->sym;
+       offset = arch_insn_adjusted_addend(insn, reloc);
 
-               if (sym->sec && is_string_sec(sym->sec)) {
-                       s64 addend;
-                       char *str;
+       if (is_string_sec(sym->sec)) {
+               char *str;
 
-                       addend = arch_insn_adjusted_addend(insn, reloc);
-
-                       str = sym->sec->data->d_buf + sym->offset + addend;
-
-                       checksum_update(func, insn, str, strlen(str));
-
-               } else {
-                       u64 offset = arch_insn_adjusted_addend(insn, reloc);
-
-                       if (is_sec_sym(sym)) {
-                               sym = find_symbol_containing(reloc->sym->sec, 
offset);
-                               if (!sym)
-                                       return;
-
-                               offset -= sym->offset;
-                       }
-
-                       checksum_update(func, insn, sym->demangled_name,
-                                           strlen(sym->demangled_name));
-
-                       checksum_update(func, insn, &offset, sizeof(offset));
-               }
+               str = sym->sec->data->d_buf + sym->offset + offset;
+               checksum_update(func, insn, str, strlen(str));
+               return;
        }
+
+       if (is_sec_sym(sym)) {
+               sym = find_symbol_containing(reloc->sym->sec, offset);
+               if (!sym)
+                       return;
+
+               offset -= sym->offset;
+       }
+
+       checksum_update(func, insn, sym->demangled_name, 
strlen(sym->demangled_name));
+       checksum_update(func, insn, &offset, sizeof(offset));
 }
 
 /*
@@ -4992,9 +4944,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;
 }
diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c
index ae1c852ff8d8..f9eed5d50de5 100644
--- a/tools/objtool/elf.c
+++ b/tools/objtool/elf.c
@@ -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"))
diff --git a/tools/objtool/include/objtool/builtin.h 
b/tools/objtool/include/objtool/builtin.h
index e8eb3c54c373..e60438577000 100644
--- a/tools/objtool/include/objtool/builtin.h
+++ b/tools/objtool/include/objtool/builtin.h
@@ -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);
 
diff --git a/tools/objtool/include/objtool/elf.h 
b/tools/objtool/include/objtool/elf.h
index adfe508f96f5..1212f81f40e0 100644
--- a/tools/objtool/include/objtool/elf.h
+++ b/tools/objtool/include/objtool/elf.h
@@ -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
diff --git a/tools/objtool/include/objtool/util.h 
b/tools/objtool/include/objtool/util.h
new file mode 100644
index 000000000000..a0180b312f73
--- /dev/null
+++ b/tools/objtool/include/objtool/util.h
@@ -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 */
diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c
index 144525e74da3..15b554b53da6 100644
--- a/tools/objtool/klp-diff.c
+++ b/tools/objtool/klp-diff.c
@@ -11,6 +11,7 @@
 #include <objtool/warn.h>
 #include <objtool/arch.h>
 #include <objtool/klp.h>
+#include <objtool/util.h>
 #include <arch/special.h>
 
 #include <linux/livepatch_external.h>
@@ -80,42 +81,11 @@ static char *escape_str(const char *orig)
        return new;
 }
 
-/*
- * Do a sanity check to make sure the changed object was built with
- * -ffunction-sections and -fdata-sections.
- */
-static int validate_ffunction_fdata_sections(struct elf *elf)
-{
-       struct symbol *sym;
-       bool found_text = false, found_data = false;
-
-       for_each_sym(elf, sym) {
-               char sec_name[SEC_NAME_LEN];
-
-               if (!found_text && is_func_sym(sym)) {
-                       snprintf(sec_name, SEC_NAME_LEN, ".text.%s", sym->name);
-                       if (!strcmp(sym->sec->name, sec_name))
-                               found_text = true;
-               }
-
-               if (!found_data && is_object_sym(sym)) {
-                       snprintf(sec_name, SEC_NAME_LEN, ".data.%s", sym->name);
-                       if (!strcmp(sym->sec->name, sec_name))
-                               found_data = true;
-               }
-
-               if (found_text && found_data)
-                       return 0;
-       }
-
-       ERROR("changed object '%s' not built with -ffunction-sections and 
-fdata-sections", elf->name);
-       return -1;
-}
-
 static int read_exports(void)
 {
        const char *symvers = "Module.symvers";
        char line[1024], *path = NULL;
+       unsigned int line_num = 1;
        FILE *file;
 
        file = fopen(symvers, "r");
@@ -134,12 +104,12 @@ static int read_exports(void)
        }
 
        while (fgets(line, 1024, file)) {
-               char *sym, *mod, *exp;
+               char *sym, *mod, *type;
                struct export *export;
 
                sym = strchr(line, '\t');
                if (!sym) {
-                       ERROR("malformed Module.symvers");
+                       ERROR("malformed Module.symvers (sym) at line %d", 
line_num);
                        return -1;
                }
 
@@ -147,22 +117,22 @@ static int read_exports(void)
 
                mod = strchr(sym, '\t');
                if (!mod) {
-                       ERROR("malformed Module.symvers");
+                       ERROR("malformed Module.symvers (mod) at line %d", 
line_num);
                        return -1;
                }
 
                *mod++ = '\0';
 
-               exp = strchr(mod, '\t');
-               if (!exp) {
-                       ERROR("malformed Module.symvers");
+               type = strchr(mod, '\t');
+               if (!type) {
+                       ERROR("malformed Module.symvers (type) at line %d", 
line_num);
                        return -1;
                }
 
-               *exp++ = '\0';
+               *type++ = '\0';
 
                if (*sym == '\0' || *mod == '\0') {
-                       ERROR("malformed Module.symvers");
+                       ERROR("malformed Module.symvers at line %d", line_num);
                        return -1;
                }
 
@@ -177,6 +147,7 @@ static int read_exports(void)
                        ERROR_GLIBC("strdup");
                        return -1;
                }
+
                export->sym = strdup(sym);
                if (!export->sym) {
                        ERROR_GLIBC("strdup");
@@ -244,18 +215,20 @@ static struct symbol *first_file_symbol(struct elf *elf)
 {
        struct symbol *sym;
 
-       for_each_sym(elf, sym)
+       for_each_sym(elf, sym) {
                if (is_file_sym(sym))
                        return sym;
+       }
 
        return NULL;
 }
 
 static struct symbol *next_file_symbol(struct elf *elf, struct symbol *sym)
 {
-       for_each_sym_continue(elf, sym)
+       for_each_sym_continue(elf, sym) {
                if (is_file_sym(sym))
                        return sym;
+       }
 
        return NULL;
 }
@@ -267,11 +240,14 @@ 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.",
        };
 
@@ -324,14 +300,19 @@ static bool is_special_section(struct section *sec)
                SYM_CHECKSUM_SEC,
        };
 
-       for (int i = 0; i < ARRAY_SIZE(specials); i++)
+       if (is_text_sec(sec))
+               return false;
+
+       for (int i = 0; i < ARRAY_SIZE(specials); i++) {
                if (!strcmp(sec->name, specials[i]))
                        return true;
+       }
 
-       /* Most .discard sections are special */
-       for (int i = 0; i < ARRAY_SIZE(non_special_discards); i++)
+       /* Most .discard data sections are special */
+       for (int i = 0; i < ARRAY_SIZE(non_special_discards); i++) {
                if (!strcmp(sec->name, non_special_discards[i]))
                        return false;
+       }
 
        return strstarts(sec->name, ".discard.");
 }
@@ -347,9 +328,10 @@ static bool is_special_section_aux(struct section *sec)
                ".altinstr_aux",
        };
 
-       for (int i = 0; i < ARRAY_SIZE(specials_aux); i++)
+       for (int i = 0; i < ARRAY_SIZE(specials_aux); i++) {
                if (!strcmp(sec->name, specials_aux[i]))
                        return true;
+       }
 
        return false;
 }
@@ -460,6 +442,7 @@ static int correlate_symbols(struct elfs *e)
 /* "sympos" is used by livepatch to disambiguate duplicate symbol names */
 static unsigned long find_sympos(struct elf *elf, struct symbol *sym)
 {
+       bool vmlinux = str_ends_with(objname, "vmlinux.o");
        unsigned long sympos = 0, nr_matches = 0;
        bool has_dup = false;
        struct symbol *s;
@@ -467,13 +450,43 @@ static unsigned long find_sympos(struct elf *elf, struct 
symbol *sym)
        if (sym->bind != STB_LOCAL)
                return 0;
 
-       for_each_sym(elf, s) {
-               if (!strcmp(s->name, sym->name)) {
-                       nr_matches++;
-                       if (s == sym)
-                               sympos = nr_matches;
-                       else
-                               has_dup = true;
+       if (vmlinux && sym->type == STT_FUNC) {
+               /*
+                * HACK: Unfortunately, symbol ordering can differ between
+                * vmlinux.o and vmlinux due to the linker script emitting
+                * .text.unlikely* before .text*.  Count .text.unlikely* first.
+                *
+                * TODO: Disambiguate symbols more reliably (checksums?)
+                */
+               for_each_sym(elf, s) {
+                       if (strstarts(s->sec->name, ".text.unlikely") &&
+                           !strcmp(s->name, sym->name)) {
+                               nr_matches++;
+                               if (s == sym)
+                                       sympos = nr_matches;
+                               else
+                                       has_dup = true;
+                       }
+               }
+               for_each_sym(elf, s) {
+                       if (!strstarts(s->sec->name, ".text.unlikely") &&
+                           !strcmp(s->name, sym->name)) {
+                               nr_matches++;
+                               if (s == sym)
+                                       sympos = nr_matches;
+                               else
+                                       has_dup = true;
+                       }
+               }
+       } else {
+               for_each_sym(elf, s) {
+                       if (!strcmp(s->name, sym->name)) {
+                               nr_matches++;
+                               if (s == sym)
+                                       sympos = nr_matches;
+                               else
+                                       has_dup = true;
+                       }
                }
        }
 
@@ -565,7 +578,7 @@ static const char *sym_type(struct symbol *sym)
 static const char *sym_bind(struct symbol *sym)
 {
        switch (sym->bind) {
-       case STB_LOCAL :  return "LOCAL";
+       case STB_LOCAL:   return "LOCAL";
        case STB_GLOBAL:  return "GLOBAL";
        case STB_WEAK:    return "WEAK";
        default:          return "UNKNOWN";
@@ -727,7 +740,7 @@ static struct export *find_export(struct symbol *sym)
 
 static const char *__find_modname(struct elfs *e)
 {
-       struct section  *sec;
+       struct section *sec;
        char *name;
 
        sec = find_section_by_name(e->orig, ".modinfo");
@@ -795,7 +808,7 @@ static bool klp_reloc_needed(struct reloc *patched_reloc)
 
        /*
         * If exported by a module, it has to be a klp reloc.  Thanks to the
-        * clusterfoot that is late module patching, the patch module is
+        * clusterfunk that is late module patching, the patch module is
         * allowed to be loaded before any modules it depends on.
         *
         * If exported by vmlinux, a normal reloc will do.
@@ -942,8 +955,9 @@ static int clone_reloc_klp(struct elfs *e, struct reloc 
*patched_reloc,
        }
 
        /* symbol format: .klp.sym.modname.sym_name,sympos */
-       snprintf(sym_name, SYM_NAME_LEN, KLP_SYM_PREFIX "%s.%s,%ld",
-                sym_modname, sym_orig_name, sympos);
+       if (snprintf_check(sym_name, SYM_NAME_LEN, KLP_SYM_PREFIX "%s.%s,%ld",
+                     sym_modname, sym_orig_name, sympos))
+               return -1;
 
        klp_sym = find_symbol_by_name(e->out, sym_name);
        if (!klp_sym) {
@@ -1155,11 +1169,102 @@ static bool should_keep_special_sym(struct elf *elf, 
struct symbol *sym)
        return false;
 }
 
+/*
+ * Klp relocations aren't allowed for __jump_table and .static_call_sites if
+ * the referenced symbol lives in a kernel module, because such klp relocs may
+ * be applied after static branch/call init, resulting in code corruption.
+ *
+ * Validate a special section entry to avoid that.  Note that an inert
+ * tracepoint is harmless enough, in that case just skip the entry and print a
+ * warning.  Otherwise, return an error.
+ *
+ * This is only a temporary limitation which will be fixed when livepatch adds
+ * support for submodules: fully self-contained modules which are embedded in
+ * the top-level livepatch module's data and which can be loaded on demand when
+ * their corresponding to-be-patched module gets loaded.  Then klp relocs can
+ * be retired.
+ *
+ * Return:
+ *   -1: error: validation failed
+ *    1: warning: tracepoint skipped
+ *    0: success
+ */
+static int validate_special_section_klp_reloc(struct elfs *e, struct symbol 
*sym)
+{
+       bool static_branch = !strcmp(sym->sec->name, "__jump_table");
+       bool static_call   = !strcmp(sym->sec->name, ".static_call_sites");
+       struct symbol *code_sym = NULL;
+       unsigned long code_offset = 0;
+       struct reloc *reloc;
+       int ret = 0;
+
+       if (!static_branch && !static_call)
+               return 0;
+
+       sym_for_each_reloc(e->patched, sym, reloc) {
+               const char *sym_modname;
+               struct export *export;
+
+               /* Static branch/call keys are always STT_OBJECT */
+               if (reloc->sym->type != STT_OBJECT) {
+
+                       /* Save code location which can be printed below */
+                       if (reloc->sym->type == STT_FUNC && !code_sym) {
+                               code_sym = reloc->sym;
+                               code_offset = reloc_addend(reloc);
+                       }
+
+                       continue;
+               }
+
+               if (!klp_reloc_needed(reloc))
+                       continue;
+
+               export = find_export(reloc->sym);
+               if (export) {
+                       sym_modname = export->mod;
+               } else {
+                       sym_modname = find_modname(e);
+                       if (!sym_modname)
+                               return -1;
+               }
+
+               /* vmlinux keys are ok */
+               if (!strcmp(sym_modname, "vmlinux"))
+                       continue;
+
+               if (static_branch) {
+                       if (strstarts(reloc->sym->name, "__tracepoint_")) {
+                               WARN("%s: disabling unsupported tracepoint %s",
+                                    code_sym->name, reloc->sym->name + 13);
+                               ret = 1;
+                               continue;
+                       }
+
+                       ERROR("%s+0x%lx: unsupported static branch key %s.  Use 
static_key_enabled() instead",
+                             code_sym->name, code_offset, reloc->sym->name);
+                       return -1;
+               }
+
+               /* static call */
+               if (strstarts(reloc->sym->name, "__SCK__tp_func_")) {
+                       ret = 1;
+                       continue;
+               }
+
+               ERROR("%s()+0x%lx: unsupported static call key %s.  Use 
KLP_STATIC_CALL() instead",
+                     code_sym->name, code_offset, reloc->sym->name);
+               return -1;
+       }
+
+       return ret;
+}
+
 static int special_section_entry_size(struct section *sec)
 {
        unsigned int reloc_size;
 
-       if (sec->sh.sh_entsize)
+       if ((sec->sh.sh_flags & SHF_MERGE) && sec->sh.sh_entsize)
                return sec->sh.sh_entsize;
 
        if (!sec->rsec)
@@ -1176,12 +1281,13 @@ static int special_section_entry_size(struct section 
*sec)
 static int create_fake_symbol(struct elf *elf, struct section *sec,
                              unsigned long offset, size_t size)
 {
+       char name[SYM_NAME_LEN];
        unsigned int type;
-       char name[256];
        static int ctr;
        char *c;
 
-       snprintf(name, 256, "__DISCARD_%s_%d", sec->name, ctr++);
+       if (snprintf_check(name, SYM_NAME_LEN, "__DISCARD_%s_%d", sec->name, 
ctr++))
+               return -1;
 
        for (c = name; *c; c++)
                if (*c == '.')
@@ -1206,7 +1312,15 @@ static int clone_special_section(struct elfs *e, struct 
section *patched_sec)
 
        entry_size = special_section_entry_size(patched_sec);
        if (!entry_size) {
-               ERROR("%s: unknown entry size", patched_sec->name);
+               /*
+                * Any special section more complex than a simple array of
+                * pointers must have its entry size specified in sh_entsize
+                * (and the SHF_MERGE flag set so the linker preserves it).
+                *
+                * Clang older than version 20 doesn't properly preserve
+                * sh_entsize and will error out here.
+                */
+               ERROR("%s: buggy linker and/or missing sh_entsize", 
patched_sec->name);
                return -1;
        }
 
@@ -1242,12 +1356,20 @@ static int clone_special_section(struct elfs *e, struct 
section *patched_sec)
         * reference included functions.
         */
        sec_for_each_sym(patched_sec, patched_sym) {
+               int ret;
+
                if (!is_object_sym(patched_sym))
                        continue;
 
                if (!should_keep_special_sym(e->patched, patched_sym))
                        continue;
 
+               ret = validate_special_section_klp_reloc(e, patched_sym);
+               if (ret < 0)
+                       return -1;
+               if (ret > 0)
+                       continue;
+
                if (!clone_symbol(e, patched_sym, true))
                        return -1;
        }
@@ -1388,7 +1510,9 @@ static int create_klp_sections(struct elfs *e)
         * friends, and add them to the klp object.
         */
 
-       snprintf(sym_name, SYM_NAME_LEN, KLP_PRE_PATCH_PREFIX "%s", modname);
+       if (snprintf_check(sym_name, SYM_NAME_LEN, KLP_PRE_PATCH_PREFIX "%s", 
modname))
+               return -1;
+
        sym = find_symbol_by_name(e->out, sym_name);
        if (sym) {
                struct reloc *reloc;
@@ -1402,7 +1526,9 @@ static int create_klp_sections(struct elfs *e)
                        return -1;
        }
 
-       snprintf(sym_name, SYM_NAME_LEN, KLP_POST_PATCH_PREFIX "%s", modname);
+       if (snprintf_check(sym_name, SYM_NAME_LEN, KLP_POST_PATCH_PREFIX "%s", 
modname))
+               return -1;
+
        sym = find_symbol_by_name(e->out, sym_name);
        if (sym) {
                struct reloc *reloc;
@@ -1416,7 +1542,9 @@ static int create_klp_sections(struct elfs *e)
                        return -1;
        }
 
-       snprintf(sym_name, SYM_NAME_LEN, KLP_PRE_UNPATCH_PREFIX "%s", modname);
+       if (snprintf_check(sym_name, SYM_NAME_LEN, KLP_PRE_UNPATCH_PREFIX "%s", 
modname))
+               return -1;
+
        sym = find_symbol_by_name(e->out, sym_name);
        if (sym) {
                struct reloc *reloc;
@@ -1430,7 +1558,9 @@ static int create_klp_sections(struct elfs *e)
                        return -1;
        }
 
-       snprintf(sym_name, SYM_NAME_LEN, KLP_POST_UNPATCH_PREFIX "%s", modname);
+       if (snprintf_check(sym_name, SYM_NAME_LEN, KLP_POST_UNPATCH_PREFIX 
"%s", modname))
+               return -1;
+
        sym = find_symbol_by_name(e->out, sym_name);
        if (sym) {
                struct reloc *reloc;
@@ -1447,6 +1577,51 @@ static int create_klp_sections(struct elfs *e)
        return 0;
 }
 
+/*
+ * Copy all .modinfo import_ns= tags to ensure all namespaced exported symbols
+ * can be accessed via normal relocs.
+ */
+static int copy_import_ns(struct elfs *e)
+{
+       struct section *patched_sec, *out_sec = NULL;
+       char *import_ns, *data_end;
+
+       patched_sec = find_section_by_name(e->patched, ".modinfo");
+       if (!patched_sec)
+               return 0;
+
+       import_ns = patched_sec->data->d_buf;
+       if (!import_ns)
+               return 0;
+
+       for (data_end = import_ns + sec_size(patched_sec);
+            import_ns < data_end;
+            import_ns += strlen(import_ns) + 1) {
+
+               import_ns = memmem(import_ns, data_end - import_ns, 
"import_ns=", 10);
+               if (!import_ns)
+                       return 0;
+
+               if (!out_sec) {
+                       out_sec = find_section_by_name(e->out, ".modinfo");
+                       if (!out_sec) {
+                               out_sec = elf_create_section(e->out, 
".modinfo", 0,
+                                                            
patched_sec->sh.sh_entsize,
+                                                            
patched_sec->sh.sh_type,
+                                                            
patched_sec->sh.sh_addralign,
+                                                            
patched_sec->sh.sh_flags);
+                               if (!out_sec)
+                                       return -1;
+                       }
+               }
+
+               if (!elf_add_data(e->out, out_sec, import_ns, strlen(import_ns) 
+ 1))
+                       return -1;
+       }
+
+       return 0;
+}
+
 int cmd_klp_diff(int argc, const char **argv)
 {
        struct elfs e = {0};
@@ -1479,10 +1654,6 @@ int cmd_klp_diff(int argc, const char **argv)
        if (mark_changed_functions(&e))
                return 0;
 
-       if (validate_ffunction_fdata_sections(e.orig) ||
-           validate_ffunction_fdata_sections(e.patched))
-               return -1;
-
        e.out = elf_create_file(&e.orig->ehdr, argv[2]);
        if (!e.out)
                return -1;
@@ -1496,9 +1667,11 @@ int cmd_klp_diff(int argc, const char **argv)
        if (create_klp_sections(&e))
                return -1;
 
+       if (copy_import_ns(&e))
+               return -1;
+
        if  (elf_write(e.out))
                return -1;
 
        return elf_close(e.out);
 }
-
diff --git a/tools/objtool/klp-post-link.c b/tools/objtool/klp-post-link.c
index 05be6251e35f..c013e39957b1 100644
--- a/tools/objtool/klp-post-link.c
+++ b/tools/objtool/klp-post-link.c
@@ -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) {
diff --git a/tools/objtool/special.c b/tools/objtool/special.c
index 9cfdd424e173..fc2cf8dba1c0 100644
--- a/tools/objtool/special.c
+++ b/tools/objtool/special.c
@@ -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);
                }

Reply via email to