[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-ID: <cover.1750980516.git.jpoimboe@kernel.org>
Date: Thu, 26 Jun 2025 16:54:47 -0700
From: Josh Poimboeuf <jpoimboe@...nel.org>
To: x86@...nel.org
Cc: linux-kernel@...r.kernel.org,
Petr Mladek <pmladek@...e.com>,
Miroslav Benes <mbenes@...e.cz>,
Joe Lawrence <joe.lawrence@...hat.com>,
live-patching@...r.kernel.org,
Song Liu <song@...nel.org>,
laokz <laokz@...mail.com>,
Jiri Kosina <jikos@...nel.org>,
Marcos Paulo de Souza <mpdesouza@...e.com>,
Weinan Liu <wnliu@...gle.com>,
Fazla Mehrab <a.mehrab@...edance.com>,
Chen Zhongjin <chenzhongjin@...wei.com>,
Puranjay Mohan <puranjay@...nel.org>,
Dylan Hatch <dylanbhatch@...gle.com>
Subject: [PATCH v3 00/64] objtool,livepatch: klp-build livepatch module generation
This series introduces new objtool features and a klp-build script to
generate livepatch modules using a source .patch as input.
This builds on concepts from the longstanding out-of-tree kpatch [1]
project which began in 2012 and has been used for many years to generate
livepatch modules for production kernels. However, this is a complete
rewrite which incorporates hard-earned lessons from 12+ years of
maintaining kpatch.
Key improvements compared to kpatch-build:
- Integrated with objtool: Leverages objtool's existing control-flow
graph analysis to help detect changed functions.
- Works on vmlinux.o: Supports late-linked objects, making it
compatible with LTO, IBT, and similar.
- Simplified code base: ~3k fewer lines of code.
- Upstream: No more out-of-tree #ifdef hacks, far less cruft.
- Cleaner internals: Vastly simplified logic for symbol/section/reloc
inclusion and special section extraction.
- Robust __LINE__ macro handling: Avoids false positive binary diffs
caused by the __LINE__ macro by introducing a fix-patch-lines script
which injects #line directives into the source .patch to preserve
the original line numbers at compile time.
The primary user interface is the klp-build script which does the
following:
- Builds an original kernel with -function-sections and
-fdata-sections, plus objtool function checksumming.
- Applies the .patch file and rebuilds the kernel using the same
options.
- Runs 'objtool klp diff' to detect changed functions and generate
intermediate binary diff objects.
- Builds a kernel module which links the diff objects with some
livepatch module init code (scripts/livepatch/init.c).
- Finalizes the livepatch module (aka work around linker wreckage)
using 'objtool klp post-link'.
I've tested with a variety of patches on defconfig and Fedora-config
kernels with both GCC and Clang.
These patches can also be found at:
git://git.kernel.org/pub/scm/linux/kernel/git/jpoimboe/linux.git klp-build-v3
Please test!
[1] https://github.com/dynup/kpatch
Changes since v2: https://lore.kernel.org/cover.1725334260.git.jpoimboe@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);
}
Powered by blists - more mailing lists