Live Patching
 help / color / mirror / Atom feed
* [PATCH v2 12/12] klp-build: Support cross-compilation
From: Josh Poimboeuf @ 2026-03-17 22:51 UTC (permalink / raw)
  To: x86
  Cc: linux-kernel, live-patching, Peter Zijlstra, Joe Lawrence,
	Song Liu, Catalin Marinas, Will Deacon, linux-arm-kernel,
	Mark Rutland, Nathan Chancellor, Nicolas Schier, Herbert Xu
In-Reply-To: <cover.1773787568.git.jpoimboe@kernel.org>

Add support for cross-compilation.  The user must export ARCH, and
either CROSS_COMPILE or LLVM as needed.

Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
---
 scripts/livepatch/klp-build | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/scripts/livepatch/klp-build b/scripts/livepatch/klp-build
index 809e198a561d..b6c057e2120f 100755
--- a/scripts/livepatch/klp-build
+++ b/scripts/livepatch/klp-build
@@ -404,6 +404,14 @@ validate_patches() {
 	revert_patches
 }
 
+cross_compile_init() {
+	if [[ -v LLVM ]]; then
+		OBJCOPY=llvm-objcopy
+	else
+		OBJCOPY="${CROSS_COMPILE:-}objcopy"
+	fi
+}
+
 do_init() {
 	# We're not yet smart enough to handle anything other than in-tree
 	# builds in pwd.
@@ -420,6 +428,7 @@ do_init() {
 	validate_config
 	set_module_name
 	set_kernelversion
+	cross_compile_init
 }
 
 # Refresh the patch hunk headers, specifically the line numbers and counts.
@@ -783,7 +792,7 @@ build_patch_module() {
 	cp -f "$kmod_file" "$kmod_file.orig"
 
 	# Work around issue where slight .config change makes corrupt BTF
-	objcopy --remove-section=.BTF "$kmod_file"
+	"$OBJCOPY" --remove-section=.BTF "$kmod_file"
 
 	# Fix (and work around) linker wreckage for klp syms / relocs
 	"$SRC/tools/objtool/objtool" klp post-link "$kmod_file" || die "objtool klp post-link failed"
-- 
2.53.0


^ permalink raw reply related

* [PATCH v2 11/12] objtool: Introduce objtool for arm64
From: Josh Poimboeuf @ 2026-03-17 22:51 UTC (permalink / raw)
  To: x86
  Cc: linux-kernel, live-patching, Peter Zijlstra, Joe Lawrence,
	Song Liu, Catalin Marinas, Will Deacon, linux-arm-kernel,
	Mark Rutland, Nathan Chancellor, Nicolas Schier, Herbert Xu
In-Reply-To: <cover.1773787568.git.jpoimboe@kernel.org>

Add basic support for arm64 on objtool.  Only --checksum is currently
supported.

Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
---
 arch/arm64/Kconfig                            |   2 +
 tools/objtool/Makefile                        |   4 +
 tools/objtool/arch/arm64/Build                |   2 +
 tools/objtool/arch/arm64/decode.c             | 116 ++++++++++++++++++
 .../arch/arm64/include/arch/cfi_regs.h        |  11 ++
 tools/objtool/arch/arm64/include/arch/elf.h   |  13 ++
 .../objtool/arch/arm64/include/arch/special.h |  21 ++++
 tools/objtool/arch/arm64/special.c            |  21 ++++
 8 files changed, 190 insertions(+)
 create mode 100644 tools/objtool/arch/arm64/Build
 create mode 100644 tools/objtool/arch/arm64/decode.c
 create mode 100644 tools/objtool/arch/arm64/include/arch/cfi_regs.h
 create mode 100644 tools/objtool/arch/arm64/include/arch/elf.h
 create mode 100644 tools/objtool/arch/arm64/include/arch/special.h
 create mode 100644 tools/objtool/arch/arm64/special.c

diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 38dba5f7e4d2..354aa80c5b4b 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -236,9 +236,11 @@ config ARM64
 	select HAVE_HW_BREAKPOINT if PERF_EVENTS
 	select HAVE_IOREMAP_PROT
 	select HAVE_IRQ_TIME_ACCOUNTING
+	select HAVE_KLP_BUILD
 	select HAVE_LIVEPATCH
 	select HAVE_MOD_ARCH_SPECIFIC
 	select HAVE_NMI
+	select HAVE_OBJTOOL
 	select HAVE_PERF_EVENTS
 	select HAVE_PERF_EVENTS_NMI if ARM64_PSEUDO_NMI
 	select HAVE_PERF_REGS
diff --git a/tools/objtool/Makefile b/tools/objtool/Makefile
index b71d1886022e..66b04172b79a 100644
--- a/tools/objtool/Makefile
+++ b/tools/objtool/Makefile
@@ -11,6 +11,10 @@ ifeq ($(SRCARCH),loongarch)
 	BUILD_ORC	   := y
 endif
 
+ifeq ($(SRCARCH),arm64)
+	ARCH_HAS_KLP := y
+endif
+
 ifeq ($(ARCH_HAS_KLP),y)
 	HAVE_XXHASH = $(shell printf "$(pound)include <xxhash.h>\nXXH3_state_t *state;int main() {}" | \
 		      $(HOSTCC) $(HOSTCFLAGS) -xc - -o /dev/null -lxxhash 2> /dev/null && echo y || echo n)
diff --git a/tools/objtool/arch/arm64/Build b/tools/objtool/arch/arm64/Build
new file mode 100644
index 000000000000..d24d5636a5b8
--- /dev/null
+++ b/tools/objtool/arch/arm64/Build
@@ -0,0 +1,2 @@
+objtool-y += decode.o
+objtool-y += special.o
diff --git a/tools/objtool/arch/arm64/decode.c b/tools/objtool/arch/arm64/decode.c
new file mode 100644
index 000000000000..ee93c096243f
--- /dev/null
+++ b/tools/objtool/arch/arm64/decode.c
@@ -0,0 +1,116 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <objtool/check.h>
+#include <objtool/disas.h>
+#include <objtool/elf.h>
+#include <objtool/arch.h>
+#include <objtool/warn.h>
+#include <objtool/builtin.h>
+
+const char *arch_reg_name[CFI_NUM_REGS] = {};
+
+int arch_ftrace_match(const char *name)
+{
+	return 0;
+}
+
+s64 arch_insn_adjusted_addend(struct instruction *insn, struct reloc *reloc)
+{
+	return reloc_addend(reloc);
+}
+
+bool arch_callee_saved_reg(unsigned char reg)
+{
+	return false;
+}
+
+int arch_decode_hint_reg(u8 sp_reg, int *base)
+{
+	exit(-1);
+}
+
+const char *arch_nop_insn(int len)
+{
+	exit(-1);
+}
+
+const char *arch_ret_insn(int len)
+{
+	exit(-1);
+}
+
+int arch_decode_instruction(struct objtool_file *file, const struct section *sec,
+			    unsigned long offset, unsigned int maxlen,
+			    struct instruction *insn)
+{
+	u32 i = *((u32 *)(sec->data->d_buf + offset));
+
+	insn->len = 4;
+
+	/*
+	 * These are the bare minimum needed for static branch detection and
+	 * checksum calculations.
+	 */
+	if (i == 0xd503201f || i == 0x2a1f03f7) {
+		/* For static branches */
+		insn->type = INSN_NOP;
+	} else if ((i & 0xfc000000) == 0x14000000) {
+		/* For static branches and intra-TU sibling calls */
+		insn->type = INSN_JUMP_UNCONDITIONAL;
+		insn->immediate = sign_extend64(i & 0x03ffffff, 25);
+	} else if ((i & 0xfc000000) == 0x94000000) {
+		/* For intra-TU calls */
+		insn->type = INSN_CALL;
+		insn->immediate = sign_extend64(i & 0x03ffffff, 25);
+	} else if ((i & 0xff000000) == 0x54000000) {
+		/* For intra-TU conditional sibling calls */
+		insn->type = INSN_JUMP_CONDITIONAL;
+		insn->immediate = sign_extend64((i & 0xffffe0) >> 5, 18);
+	} else {
+		insn->type = INSN_OTHER;
+	}
+
+	return 0;
+}
+
+u64 arch_adjusted_addend(struct reloc *reloc)
+{
+	return reloc_addend(reloc);
+}
+
+unsigned long arch_jump_destination(struct instruction *insn)
+{
+	return insn->offset + (insn->immediate << 2);
+}
+
+bool arch_pc_relative_reloc(struct reloc *reloc)
+{
+	return false;
+}
+
+void arch_initial_func_cfi_state(struct cfi_init_state *state)
+{
+	state->cfa.base = CFI_UNDEFINED;
+}
+
+unsigned int arch_reloc_size(struct reloc *reloc)
+{
+	switch (reloc_type(reloc)) {
+	case R_AARCH64_ABS64:
+	case R_AARCH64_PREL64:
+		return 8;
+	default:
+		return 4;
+	}
+}
+
+#ifdef DISAS
+int arch_disas_info_init(struct disassemble_info *dinfo)
+{
+	return disas_info_init(dinfo, bfd_arch_aarch64,
+			       bfd_mach_arm_unknown, bfd_mach_aarch64,
+			       NULL);
+}
+#endif /* DISAS */
diff --git a/tools/objtool/arch/arm64/include/arch/cfi_regs.h b/tools/objtool/arch/arm64/include/arch/cfi_regs.h
new file mode 100644
index 000000000000..49c81cbb6646
--- /dev/null
+++ b/tools/objtool/arch/arm64/include/arch/cfi_regs.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef _OBJTOOL_ARCH_CFI_REGS_H
+#define _OBJTOOL_ARCH_CFI_REGS_H
+
+/* These aren't actually used for arm64 */
+#define CFI_BP 0
+#define CFI_SP 0
+#define CFI_RA 0
+#define CFI_NUM_REGS 2
+
+#endif /* _OBJTOOL_ARCH_CFI_REGS_H */
diff --git a/tools/objtool/arch/arm64/include/arch/elf.h b/tools/objtool/arch/arm64/include/arch/elf.h
new file mode 100644
index 000000000000..80a1bc479930
--- /dev/null
+++ b/tools/objtool/arch/arm64/include/arch/elf.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef _OBJTOOL_ARCH_ELF_H
+#define _OBJTOOL_ARCH_ELF_H
+
+#define R_NONE		R_AARCH64_NONE
+#define R_ABS64		R_AARCH64_ABS64
+#define R_ABS32		R_AARCH64_ABS32
+#define R_DATA32	R_AARCH64_PREL32
+#define R_DATA64	R_AARCH64_PREL64
+#define R_TEXT32	R_AARCH64_PREL32
+#define R_TEXT64	R_AARCH64_PREL64
+
+#endif /* _OBJTOOL_ARCH_ELF_H */
diff --git a/tools/objtool/arch/arm64/include/arch/special.h b/tools/objtool/arch/arm64/include/arch/special.h
new file mode 100644
index 000000000000..8ae804a83ea4
--- /dev/null
+++ b/tools/objtool/arch/arm64/include/arch/special.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef _OBJTOOL_ARCH_SPECIAL_H
+#define _OBJTOOL_ARCH_SPECIAL_H
+
+#define EX_ENTRY_SIZE 12
+#define EX_ORIG_OFFSET 0
+#define EX_NEW_OFFSET 4
+
+#define JUMP_ENTRY_SIZE 16
+#define JUMP_ORIG_OFFSET 0
+#define JUMP_NEW_OFFSET 4
+#define JUMP_KEY_OFFSET 8
+
+#define ALT_ENTRY_SIZE 12
+#define ALT_ORIG_OFFSET 0
+#define ALT_NEW_OFFSET 4
+#define ALT_FEATURE_OFFSET 8
+#define ALT_ORIG_LEN_OFFSET 10
+#define ALT_NEW_LEN_OFFSET 11
+
+#endif /* _OBJTOOL_ARCH_SPECIAL_H */
diff --git a/tools/objtool/arch/arm64/special.c b/tools/objtool/arch/arm64/special.c
new file mode 100644
index 000000000000..3c2b83d94939
--- /dev/null
+++ b/tools/objtool/arch/arm64/special.c
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include <objtool/special.h>
+
+bool arch_support_alt_relocation(struct special_alt *special_alt,
+				 struct instruction *insn,
+				 struct reloc *reloc)
+{
+	return false;
+}
+
+struct reloc *arch_find_switch_table(struct objtool_file *file,
+				     struct instruction *insn,
+				     unsigned long *table_size)
+{
+	return NULL;
+}
+
+const char *arch_cpu_feature_name(int feature_number)
+{
+	return NULL;
+}
-- 
2.53.0


^ permalink raw reply related

* [PATCH v2 10/12] objtool: Reuse consecutive string references
From: Josh Poimboeuf @ 2026-03-17 22:51 UTC (permalink / raw)
  To: x86
  Cc: linux-kernel, live-patching, Peter Zijlstra, Joe Lawrence,
	Song Liu, Catalin Marinas, Will Deacon, linux-arm-kernel,
	Mark Rutland, Nathan Chancellor, Nicolas Schier, Herbert Xu
In-Reply-To: <cover.1773787568.git.jpoimboe@kernel.org>

For duplicate strings, elf_add_string() just blindly adds duplicates.

That can be a problem for arm64 which often uses two consecutive
instructions (and corresponding relocations) to put an address into a
register, like:

  d8:   90000001        adrp    x1, 0 <meminfo_proc_show>       d8: R_AARCH64_ADR_PREL_PG_HI21  .rodata.meminfo_proc_show.str1.8
  dc:   91000021        add     x1, x1, #0x0    dc: R_AARCH64_ADD_ABS_LO12_NC   .rodata.meminfo_proc_show.str1.8

Referencing two different string addresses in the adrp+add pair can
result in a corrupt string addresses.  Detect such consecutive reuses
and force them to use the same string.

Acked-by: Song Liu <song@kernel.org>
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
---
 tools/objtool/elf.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c
index b911abe3a15e..5ccae2c937dc 100644
--- a/tools/objtool/elf.c
+++ b/tools/objtool/elf.c
@@ -1374,6 +1374,8 @@ struct elf *elf_create_file(GElf_Ehdr *ehdr, const char *name)
 
 unsigned int elf_add_string(struct elf *elf, struct section *strtab, const char *str)
 {
+	static unsigned int last_off;
+	static const char *last_str;
 	unsigned int offset;
 
 	if (!strtab)
@@ -1382,17 +1384,22 @@ unsigned int elf_add_string(struct elf *elf, struct section *strtab, const char
 		ERROR("can't find .strtab section");
 		return -1;
 	}
-
 	if (!strtab->sh.sh_addralign) {
 		ERROR("'%s': invalid sh_addralign", strtab->name);
 		return -1;
 	}
 
+	if (last_str && !strcmp(last_str, str))
+		return last_off;
+
 	offset = ALIGN(sec_size(strtab), strtab->sh.sh_addralign);
 
 	if (!elf_add_data(elf, strtab, str, strlen(str) + 1))
 		return -1;
 
+	last_str = str;
+	last_off = offset;
+
 	return offset;
 }
 
-- 
2.53.0


^ permalink raw reply related

* [PATCH v2 09/12] objtool: Allow empty alternatives
From: Josh Poimboeuf @ 2026-03-17 22:51 UTC (permalink / raw)
  To: x86
  Cc: linux-kernel, live-patching, Peter Zijlstra, Joe Lawrence,
	Song Liu, Catalin Marinas, Will Deacon, linux-arm-kernel,
	Mark Rutland, Nathan Chancellor, Nicolas Schier, Herbert Xu
In-Reply-To: <cover.1773787568.git.jpoimboe@kernel.org>

arm64 can have empty alternatives, which are effectively no-ops.  Ignore
them.

Acked-by: Song Liu <song@kernel.org>
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
---
 tools/objtool/check.c | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 6e33a29bd3ed..7297fb87f876 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -1994,10 +1994,8 @@ static int add_special_section_alts(struct objtool_file *file)
 		}
 
 		if (special_alt->group) {
-			if (!special_alt->orig_len) {
-				ERROR_INSN(orig_insn, "empty alternative entry");
+			if (!special_alt->orig_len)
 				continue;
-			}
 
 			if (handle_group_alt(file, special_alt, orig_insn, &new_insn))
 				return -1;
-- 
2.53.0


^ permalink raw reply related

* [PATCH v2 08/12] objtool: Ignore jumps to the end of the function for non-CFG arches
From: Josh Poimboeuf @ 2026-03-17 22:51 UTC (permalink / raw)
  To: x86
  Cc: linux-kernel, live-patching, Peter Zijlstra, Joe Lawrence,
	Song Liu, Catalin Marinas, Will Deacon, linux-arm-kernel,
	Mark Rutland, Nathan Chancellor, Nicolas Schier, Herbert Xu
In-Reply-To: <cover.1773787568.git.jpoimboe@kernel.org>

Sometimes Clang arm64 code jumps to the end of the function for UB.
No need to make that an error, arm64 doesn't reverse engineer the CFG
anyway.

Acked-by: Song Liu <song@kernel.org>
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
---
 tools/objtool/check.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index d428d63b29c6..6e33a29bd3ed 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -1630,10 +1630,12 @@ static int add_jump_destinations(struct objtool_file *file)
 			/*
 			 * GCOV/KCOV dead code can jump to the end of
 			 * the function/section.
+			 *
+			 * Clang on arm64 also does this sometimes for
+			 * undefined behavior.
 			 */
-			if (file->ignore_unreachables && func &&
-			    dest_sec == insn->sec &&
-			    dest_off == func->offset + func->len)
+			if ((file->ignore_unreachables || (!opts.stackval && !opts.orc)) &&
+			    func && dest_sec == insn->sec && dest_off == func->offset + func->len)
 				continue;
 
 			ERROR_INSN(insn, "can't find jump dest instruction at %s",
-- 
2.53.0


^ permalink raw reply related

* [PATCH v2 07/12] kbuild: Only run objtool if there is at least one command
From: Josh Poimboeuf @ 2026-03-17 22:51 UTC (permalink / raw)
  To: x86
  Cc: linux-kernel, live-patching, Peter Zijlstra, Joe Lawrence,
	Song Liu, Catalin Marinas, Will Deacon, linux-arm-kernel,
	Mark Rutland, Nathan Chancellor, Nicolas Schier, Herbert Xu
In-Reply-To: <cover.1773787568.git.jpoimboe@kernel.org>

Split the objtool args into commands and options, such that if no
commands have been enabled, objtool doesn't run.

This is in preparation in enabling objtool and klp-build for arm64.

Reviewed-by: Nathan Chancellor <nathan@kernel.org>
Tested-by: Nathan Chancellor <nathan@kernel.org>
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
---
 arch/x86/boot/startup/Makefile |  2 +-
 scripts/Makefile.build         |  4 +--
 scripts/Makefile.lib           | 46 ++++++++++++++++++----------------
 scripts/Makefile.vmlinux_o     | 15 ++++-------
 4 files changed, 33 insertions(+), 34 deletions(-)

diff --git a/arch/x86/boot/startup/Makefile b/arch/x86/boot/startup/Makefile
index 5e499cfb29b5..a08297829fc6 100644
--- a/arch/x86/boot/startup/Makefile
+++ b/arch/x86/boot/startup/Makefile
@@ -36,7 +36,7 @@ $(patsubst %.o,$(obj)/%.o,$(lib-y)): OBJECT_FILES_NON_STANDARD := y
 # relocations, even if other objtool actions are being deferred.
 #
 $(pi-objs): objtool-enabled	= 1
-$(pi-objs): objtool-args	= $(if $(delay-objtool),--dry-run,$(objtool-args-y)) --noabs
+$(pi-objs): objtool-args	= $(if $(delay-objtool),--dry-run,$(objtool-cmds-y) $(objtool-opts-y)) --noabs
 
 #
 # Confine the startup code by prefixing all symbols with __pi_ (for position
diff --git a/scripts/Makefile.build b/scripts/Makefile.build
index 3652b85be545..8a1bdfdb2fdb 100644
--- a/scripts/Makefile.build
+++ b/scripts/Makefile.build
@@ -277,7 +277,7 @@ endif # CONFIG_FTRACE_MCOUNT_USE_RECORDMCOUNT
 is-standard-object = $(if $(filter-out y%, $(OBJECT_FILES_NON_STANDARD_$(target-stem).o)$(OBJECT_FILES_NON_STANDARD)n),$(is-kernel-object))
 
 ifdef CONFIG_OBJTOOL
-$(obj)/%.o: private objtool-enabled = $(if $(is-standard-object),$(if $(delay-objtool),$(is-single-obj-m),y))
+$(obj)/%.o: private objtool-enabled = $(if $(is-standard-object),$(if $(objtool-cmds-y),$(if $(delay-objtool),$(is-single-obj-m),y)))
 endif
 
 ifneq ($(findstring 1, $(KBUILD_EXTRA_WARN)),)
@@ -501,7 +501,7 @@ define rule_ld_multi_m
 	$(call cmd,gen_objtooldep)
 endef
 
-$(multi-obj-m): private objtool-enabled := $(delay-objtool)
+$(multi-obj-m): private objtool-enabled := $(if $(objtool-cmds-y),$(delay-objtool))
 $(multi-obj-m): private part-of-module := y
 $(multi-obj-m): %.o: %.mod FORCE
 	$(call if_changed_rule,ld_multi_m)
diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
index 0718e39cedda..40a462581666 100644
--- a/scripts/Makefile.lib
+++ b/scripts/Makefile.lib
@@ -183,27 +183,31 @@ ifdef CONFIG_OBJTOOL
 
 objtool := $(objtree)/tools/objtool/objtool
 
-objtool-args-$(CONFIG_HAVE_JUMP_LABEL_HACK)		+= --hacks=jump_label
-objtool-args-$(CONFIG_HAVE_NOINSTR_HACK)		+= --hacks=noinstr
-objtool-args-$(CONFIG_MITIGATION_CALL_DEPTH_TRACKING)	+= --hacks=skylake
-objtool-args-$(CONFIG_X86_KERNEL_IBT)			+= --ibt
-objtool-args-$(CONFIG_FINEIBT)				+= --cfi
-objtool-args-$(CONFIG_FTRACE_MCOUNT_USE_OBJTOOL)	+= --mcount
-ifdef CONFIG_FTRACE_MCOUNT_USE_OBJTOOL
-objtool-args-$(CONFIG_HAVE_OBJTOOL_NOP_MCOUNT)		+= --mnop
-endif
-objtool-args-$(CONFIG_UNWINDER_ORC)			+= --orc
-objtool-args-$(CONFIG_MITIGATION_RETPOLINE)		+= --retpoline
-objtool-args-$(CONFIG_MITIGATION_RETHUNK)		+= --rethunk
-objtool-args-$(CONFIG_MITIGATION_SLS)			+= --sls
-objtool-args-$(CONFIG_STACK_VALIDATION)			+= --stackval
-objtool-args-$(CONFIG_HAVE_STATIC_CALL_INLINE)		+= --static-call
-objtool-args-$(CONFIG_HAVE_UACCESS_VALIDATION)		+= --uaccess
-objtool-args-$(or $(CONFIG_GCOV_KERNEL),$(CONFIG_KCOV))	+= --no-unreachable
-objtool-args-$(CONFIG_PREFIX_SYMBOLS)			+= --prefix=$(CONFIG_FUNCTION_PADDING_BYTES)
-objtool-args-$(CONFIG_OBJTOOL_WERROR)			+= --werror
+# objtool commands
+objtool-cmds-$(CONFIG_HAVE_JUMP_LABEL_HACK)		+= --hacks=jump_label
+objtool-cmds-$(CONFIG_HAVE_NOINSTR_HACK)		+= --hacks=noinstr
+objtool-cmds-$(CONFIG_MITIGATION_CALL_DEPTH_TRACKING)	+= --hacks=skylake
+objtool-cmds-$(CONFIG_X86_KERNEL_IBT)			+= --ibt
+objtool-cmds-$(CONFIG_FINEIBT)				+= --cfi
+objtool-cmds-$(CONFIG_FTRACE_MCOUNT_USE_OBJTOOL)	+= --mcount
+objtool-cmds-$(CONFIG_UNWINDER_ORC)			+= --orc
+objtool-cmds-$(CONFIG_MITIGATION_RETPOLINE)		+= --retpoline
+objtool-cmds-$(CONFIG_MITIGATION_RETHUNK)		+= --rethunk
+objtool-cmds-$(CONFIG_MITIGATION_SLS)			+= --sls
+objtool-cmds-$(CONFIG_STACK_VALIDATION)			+= --stackval
+objtool-cmds-$(CONFIG_HAVE_STATIC_CALL_INLINE)		+= --static-call
+objtool-cmds-$(CONFIG_HAVE_UACCESS_VALIDATION)		+= --uaccess
+objtool-cmds-$(CONFIG_PREFIX_SYMBOLS)			+= --prefix=$(CONFIG_FUNCTION_PADDING_BYTES)
+objtool-cmds-y						+= $(OBJTOOL_ARGS)
 
-objtool-args = $(objtool-args-y)					\
+# objtool options
+ifdef CONFIG_FTRACE_MCOUNT_USE_OBJTOOL
+objtool-opts-$(CONFIG_HAVE_OBJTOOL_NOP_MCOUNT)		+= --mnop
+endif
+objtool-opts-$(or $(CONFIG_GCOV_KERNEL),$(CONFIG_KCOV))	+= --no-unreachable
+objtool-opts-$(CONFIG_OBJTOOL_WERROR)			+= --werror
+
+objtool-args = $(objtool-cmds-y) $(objtool-opts-y)			\
 	$(if $(delay-objtool), --link)					\
 	$(if $(part-of-module), --module)
 
@@ -212,7 +216,7 @@ delay-objtool := $(or $(CONFIG_LTO_CLANG),$(CONFIG_X86_KERNEL_IBT),$(CONFIG_KLP_
 cmd_objtool = $(if $(objtool-enabled), ; $(objtool) $(objtool-args) $@)
 cmd_gen_objtooldep = $(if $(objtool-enabled), { echo ; echo '$@: $$(wildcard $(objtool))' ; } >> $(dot-target).cmd)
 
-objtool-enabled := y
+objtool-enabled = $(if $(objtool-cmds-y),y)
 
 endif # CONFIG_OBJTOOL
 
diff --git a/scripts/Makefile.vmlinux_o b/scripts/Makefile.vmlinux_o
index 527352c222ff..09af33203bd8 100644
--- a/scripts/Makefile.vmlinux_o
+++ b/scripts/Makefile.vmlinux_o
@@ -36,18 +36,13 @@ endif
 # For !delay-objtool + CONFIG_NOINSTR_VALIDATION, it runs on both translation
 # units and vmlinux.o, with the latter only used for noinstr/unret validation.
 
-objtool-enabled := $(or $(delay-objtool),$(CONFIG_NOINSTR_VALIDATION))
-
-ifeq ($(delay-objtool),y)
-vmlinux-objtool-args-y					+= $(objtool-args-y)
-else
-vmlinux-objtool-args-$(CONFIG_OBJTOOL_WERROR)		+= --werror
+ifneq ($(delay-objtool),y)
+objtool-cmds-y					 =
+objtool-opts-y					+= --link
 endif
 
-vmlinux-objtool-args-$(CONFIG_NOINSTR_VALIDATION)	+= --noinstr \
-							   $(if $(or $(CONFIG_MITIGATION_UNRET_ENTRY),$(CONFIG_MITIGATION_SRSO)), --unret)
-
-objtool-args = $(vmlinux-objtool-args-y) --link
+objtool-cmds-$(CONFIG_NOINSTR_VALIDATION)	+= --noinstr \
+						   $(if $(or $(CONFIG_MITIGATION_UNRET_ENTRY),$(CONFIG_MITIGATION_SRSO)), --unret)
 
 # Link of vmlinux.o used for section mismatch analysis
 # ---------------------------------------------------------------------------
-- 
2.53.0


^ permalink raw reply related

* [PATCH v2 06/12] objtool: Allow setting --mnop without --mcount
From: Josh Poimboeuf @ 2026-03-17 22:51 UTC (permalink / raw)
  To: x86
  Cc: linux-kernel, live-patching, Peter Zijlstra, Joe Lawrence,
	Song Liu, Catalin Marinas, Will Deacon, linux-arm-kernel,
	Mark Rutland, Nathan Chancellor, Nicolas Schier, Herbert Xu
In-Reply-To: <cover.1773787568.git.jpoimboe@kernel.org>

Instead of returning an error for --mnop without --mcount, just silently
ignore it.  This will help simplify kbuild's handling of objtool args.

Acked-by: Song Liu <song@kernel.org>
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
---
 tools/objtool/builtin-check.c | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/tools/objtool/builtin-check.c b/tools/objtool/builtin-check.c
index b780df513715..f528a58aca74 100644
--- a/tools/objtool/builtin-check.c
+++ b/tools/objtool/builtin-check.c
@@ -145,11 +145,6 @@ int cmd_parse_options(int argc, const char **argv, const char * const usage[])
 
 static bool opts_valid(void)
 {
-	if (opts.mnop && !opts.mcount) {
-		ERROR("--mnop requires --mcount");
-		return false;
-	}
-
 	if (opts.noinstr && !opts.link) {
 		ERROR("--noinstr requires --link");
 		return false;
-- 
2.53.0


^ permalink raw reply related

* [PATCH v2 05/12] objtool: Extricate checksum calculation from validate_branch()
From: Josh Poimboeuf @ 2026-03-17 22:51 UTC (permalink / raw)
  To: x86
  Cc: linux-kernel, live-patching, Peter Zijlstra, Joe Lawrence,
	Song Liu, Catalin Marinas, Will Deacon, linux-arm-kernel,
	Mark Rutland, Nathan Chancellor, Nicolas Schier, Herbert Xu
In-Reply-To: <cover.1773787568.git.jpoimboe@kernel.org>

In preparation for porting the checksum code to other arches, make its
functionality independent from the CFG reverse engineering code.

Now that it's no longer called by validate_insn(),
checksum_update_insn() has to manually iterate the alternatives.

Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
---
 tools/objtool/check.c                    | 71 +++++++++++++++++-------
 tools/objtool/include/objtool/checksum.h |  6 +-
 2 files changed, 53 insertions(+), 24 deletions(-)

diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 2301fb7ff3c8..d428d63b29c6 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -2633,8 +2633,7 @@ static bool validate_branch_enabled(void)
 {
 	return opts.stackval ||
 	       opts.orc ||
-	       opts.uaccess ||
-	       opts.checksum;
+	       opts.uaccess;
 }
 
 static int decode_sections(struct objtool_file *file)
@@ -3663,6 +3662,7 @@ static bool skip_alt_group(struct instruction *insn)
 	return alt_insn->type == INSN_CLAC || alt_insn->type == INSN_STAC;
 }
 
+#ifdef BUILD_KLP
 static int checksum_debug_init(struct objtool_file *file)
 {
 	char *dup, *s;
@@ -3705,9 +3705,30 @@ static void checksum_update_insn(struct objtool_file *file, struct symbol *func,
 				 struct instruction *insn)
 {
 	struct reloc *reloc = insn_reloc(file, insn);
+	struct alternative *alt;
 	unsigned long offset;
 	struct symbol *sym;
 
+	for (alt = insn->alts; alt; alt = alt->next) {
+		struct alt_group *alt_group = alt->insn->alt_group;
+
+		checksum_update(func, insn, &alt->type, sizeof(alt->type));
+
+		if (alt_group && alt_group->orig_group) {
+			struct instruction *alt_insn;
+
+			checksum_update(func, insn, &alt_group->feature, sizeof(alt_group->feature));
+
+			for (alt_insn = alt->insn; alt_insn; alt_insn = next_insn_same_sec(file, alt_insn)) {
+				checksum_update_insn(file, func, alt_insn);
+				if (alt_insn == alt_group->last_insn)
+					break;
+			}
+		} else {
+			checksum_update(func, insn, &alt->insn->offset, sizeof(alt->insn->offset));
+		}
+	}
+
 	if (insn->fake)
 		return;
 
@@ -3716,9 +3737,11 @@ static void checksum_update_insn(struct objtool_file *file, struct symbol *func,
 	if (!reloc) {
 		struct symbol *call_dest = insn_call_dest(insn);
 
-		if (call_dest)
+		if (call_dest) {
+			/* intra-TU call without reloc */
 			checksum_update(func, insn, call_dest->demangled_name,
 					strlen(call_dest->demangled_name));
+		}
 		return;
 	}
 
@@ -3745,6 +3768,29 @@ static void checksum_update_insn(struct objtool_file *file, struct symbol *func,
 	checksum_update(func, insn, &offset, sizeof(offset));
 }
 
+static int calculate_checksums(struct objtool_file *file)
+{
+	struct instruction *insn;
+	struct symbol *func;
+
+	if (checksum_debug_init(file))
+		return -1;
+
+	for_each_sym(file->elf, func) {
+		if (!is_func_sym(func))
+			continue;
+
+		checksum_init(func);
+
+		func_for_each_insn(file, func, insn)
+			checksum_update_insn(file, func, insn);
+
+		checksum_finish(func);
+	}
+	return 0;
+}
+#endif /* BUILD_KLP */
+
 static int validate_branch(struct objtool_file *file, struct symbol *func,
 			   struct instruction *insn, struct insn_state state);
 static int do_validate_branch(struct objtool_file *file, struct symbol *func,
@@ -4026,9 +4072,6 @@ static int do_validate_branch(struct objtool_file *file, struct symbol *func,
 		insn->trace = 0;
 		next_insn = next_insn_to_validate(file, insn);
 
-		if (opts.checksum && func && insn->sec)
-			checksum_update_insn(file, func, insn);
-
 		if (func && insn_func(insn) && func != insn_func(insn)->pfunc) {
 			/* Ignore KCFI type preambles, which always fall through */
 			if (is_prefix_func(func))
@@ -4094,9 +4137,6 @@ static int validate_unwind_hint(struct objtool_file *file,
 		struct symbol *func = insn_func(insn);
 		int ret;
 
-		if (opts.checksum)
-			checksum_init(func);
-
 		ret = validate_branch(file, func, insn, *state);
 		if (ret)
 			BT_INSN(insn, "<=== (hint)");
@@ -4539,9 +4579,6 @@ static int validate_symbol(struct objtool_file *file, struct section *sec,
 
 	func = insn_func(insn);
 
-	if (opts.checksum)
-		checksum_init(func);
-
 	if (opts.trace && !fnmatch(opts.trace, sym->name, 0)) {
 		trace_enable();
 		TRACE("%s: validation begin\n", sym->name);
@@ -4554,9 +4591,6 @@ static int validate_symbol(struct objtool_file *file, struct section *sec,
 	TRACE("%s: validation %s\n\n", sym->name, ret ? "failed" : "end");
 	trace_disable();
 
-	if (opts.checksum)
-		checksum_finish(func);
-
 	return ret;
 }
 
@@ -5011,10 +5045,6 @@ int check(struct objtool_file *file)
 	cfi_hash_add(&init_cfi);
 	cfi_hash_add(&func_cfi);
 
-	ret = checksum_debug_init(file);
-	if (ret)
-		goto out;
-
 	ret = decode_sections(file);
 	if (ret)
 		goto out;
@@ -5105,6 +5135,9 @@ int check(struct objtool_file *file)
 		warnings += check_abs_references(file);
 
 	if (opts.checksum) {
+		ret = calculate_checksums(file);
+		if (ret)
+			goto out;
 		ret = create_sym_checksum_section(file);
 		if (ret)
 			goto out;
diff --git a/tools/objtool/include/objtool/checksum.h b/tools/objtool/include/objtool/checksum.h
index 7fe21608722a..36a17688c716 100644
--- a/tools/objtool/include/objtool/checksum.h
+++ b/tools/objtool/include/objtool/checksum.h
@@ -32,11 +32,7 @@ static inline void checksum_finish(struct symbol *func)
 
 #else /* !BUILD_KLP */
 
-static inline void checksum_init(struct symbol *func) {}
-static inline void checksum_update(struct symbol *func,
-				   struct instruction *insn,
-				   const void *data, size_t size) {}
-static inline void checksum_finish(struct symbol *func) {}
+static inline int calculate_checksums(struct objtool_file *file) { return -ENOSYS; }
 
 #endif /* !BUILD_KLP */
 
-- 
2.53.0


^ permalink raw reply related

* [PATCH v2 04/12] crypto: arm64: Move data to .rodata
From: Josh Poimboeuf @ 2026-03-17 22:51 UTC (permalink / raw)
  To: x86
  Cc: linux-kernel, live-patching, Peter Zijlstra, Joe Lawrence,
	Song Liu, Catalin Marinas, Will Deacon, linux-arm-kernel,
	Mark Rutland, Nathan Chancellor, Nicolas Schier, Herbert Xu
In-Reply-To: <cover.1773787568.git.jpoimboe@kernel.org>

Data embedded in .text pollutes i-cache and confuses objtool and other
tools that try to disassemble it.  Move it to .rodata.

Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
---
 lib/crypto/arm64/sha2-armv8.pl | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/lib/crypto/arm64/sha2-armv8.pl b/lib/crypto/arm64/sha2-armv8.pl
index 35ec9ae99fe1..8cd86768bf5c 100644
--- a/lib/crypto/arm64/sha2-armv8.pl
+++ b/lib/crypto/arm64/sha2-armv8.pl
@@ -237,7 +237,8 @@ $code.=<<___;
 	ldp	$E,$F,[$ctx,#4*$SZ]
 	add	$num,$inp,$num,lsl#`log(16*$SZ)/log(2)`	// end of input
 	ldp	$G,$H,[$ctx,#6*$SZ]
-	adr	$Ktbl,.LK$BITS
+	adrp	$Ktbl,.LK$BITS
+	add	$Ktbl,$Ktbl,:lo12:.LK$BITS
 	stp	$ctx,$num,[x29,#96]
 
 .Loop:
@@ -286,6 +287,7 @@ $code.=<<___;
 	ret
 .size	$func,.-$func
 
+.pushsection .rodata
 .align	6
 .type	.LK$BITS,%object
 .LK$BITS:
@@ -365,6 +367,7 @@ $code.=<<___;
 #endif
 .asciz	"SHA$BITS block transform for ARMv8, CRYPTOGAMS by <appro\@openssl.org>"
 .align	2
+.popsection
 ___
 
 if ($SZ==4) {
@@ -385,7 +388,8 @@ sha256_block_armv8:
 	add		x29,sp,#0
 
 	ld1.32		{$ABCD,$EFGH},[$ctx]
-	adr		$Ktbl,.LK256
+	adrp		$Ktbl,.LK256
+	add		$Ktbl,$Ktbl,:lo12:.LK256
 
 .Loop_hw:
 	ld1		{@MSG[0]-@MSG[3]},[$inp],#64
@@ -648,7 +652,8 @@ sha256_block_neon:
 	mov	x29, sp
 	sub	sp,sp,#16*4
 
-	adr	$Ktbl,.LK256
+	adrp	$Ktbl,.LK256
+	add	$Ktbl,$Ktbl,:lo12:.LK256
 	add	$num,$inp,$num,lsl#6	// len to point at the end of inp
 
 	ld1.8	{@X[0]},[$inp], #16
-- 
2.53.0


^ permalink raw reply related

* [PATCH v2 03/12] arm64: Fix EFI linking with -fdata-sections
From: Josh Poimboeuf @ 2026-03-17 22:51 UTC (permalink / raw)
  To: x86
  Cc: linux-kernel, live-patching, Peter Zijlstra, Joe Lawrence,
	Song Liu, Catalin Marinas, Will Deacon, linux-arm-kernel,
	Mark Rutland, Nathan Chancellor, Nicolas Schier, Herbert Xu
In-Reply-To: <cover.1773787568.git.jpoimboe@kernel.org>

When building with func-fdata-sections, the .init.bss section gets split
up into a bunch of .init.bss.<var> sections.  Make sure they get linked
into .init.data.

Acked-by: Song Liu <song@kernel.org>
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
---
 arch/arm64/kernel/vmlinux.lds.S | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/arm64/kernel/vmlinux.lds.S b/arch/arm64/kernel/vmlinux.lds.S
index 2964aad0362e..f825e6a26918 100644
--- a/arch/arm64/kernel/vmlinux.lds.S
+++ b/arch/arm64/kernel/vmlinux.lds.S
@@ -269,7 +269,7 @@ SECTIONS
 		INIT_CALLS
 		CON_INITCALL
 		INIT_RAM_FS
-		*(.init.altinstructions .init.bss)	/* from the EFI stub */
+		*(.init.altinstructions .init.bss .init.bss.*)	/* from the EFI stub */
 	}
 	.exit.data : {
 		EXIT_DATA
-- 
2.53.0


^ permalink raw reply related

* [PATCH v2 02/12] arm64: head: Move boot header to .head.data
From: Josh Poimboeuf @ 2026-03-17 22:51 UTC (permalink / raw)
  To: x86
  Cc: linux-kernel, live-patching, Peter Zijlstra, Joe Lawrence,
	Song Liu, Catalin Marinas, Will Deacon, linux-arm-kernel,
	Mark Rutland, Nathan Chancellor, Nicolas Schier, Herbert Xu
In-Reply-To: <cover.1773787568.git.jpoimboe@kernel.org>

The arm64 boot header is mostly data.  Move it to a data section to
prevent objtool and other tools from trying to disassemble it.  The
final linked result is the same.

Acked-by: Song Liu <song@kernel.or
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
---
 arch/arm64/kernel/head.S          | 2 +-
 include/asm-generic/vmlinux.lds.h | 2 +-
 include/linux/init.h              | 1 +
 3 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S
index 87a822e5c4ca..0681c6e50859 100644
--- a/arch/arm64/kernel/head.S
+++ b/arch/arm64/kernel/head.S
@@ -54,7 +54,7 @@
  * that are useful before the MMU is enabled. The allocations are described
  * in the entry routines.
  */
-	__HEAD
+	__HEADDATA
 	/*
 	 * DO NOT MODIFY. Image header expected by Linux boot-loaders.
 	 */
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 1e1580febe4b..f82f170a97eb 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -663,7 +663,7 @@
 		__static_call_text_end = .;
 
 /* Section used for early init (in .S files) */
-#define HEAD_TEXT  KEEP(*(.head.text))
+#define HEAD_TEXT  KEEP(*(.head.data .head.text))
 
 #define HEAD_TEXT_SECTION							\
 	.head.text : AT(ADDR(.head.text) - LOAD_OFFSET) {		\
diff --git a/include/linux/init.h b/include/linux/init.h
index 40331923b9f4..91e16f3205e2 100644
--- a/include/linux/init.h
+++ b/include/linux/init.h
@@ -90,6 +90,7 @@
 
 /* For assembly routines */
 #define __HEAD		.section	".head.text","ax"
+#define __HEADDATA	.section	".head.data","aw"
 #define __INIT		.section	".init.text","ax"
 #define __FINIT		.previous
 
-- 
2.53.0


^ permalink raw reply related

* [PATCH v2 01/12] arm64: Annotate intra-function calls
From: Josh Poimboeuf @ 2026-03-17 22:51 UTC (permalink / raw)
  To: x86
  Cc: linux-kernel, live-patching, Peter Zijlstra, Joe Lawrence,
	Song Liu, Catalin Marinas, Will Deacon, linux-arm-kernel,
	Mark Rutland, Nathan Chancellor, Nicolas Schier, Herbert Xu
In-Reply-To: <cover.1773787568.git.jpoimboe@kernel.org>

In preparation for enabling objtool on arm64, annotate intra-function
calls.

Acked-by: Song Liu <song@kernel.org>
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
---
 arch/arm64/kernel/entry.S       |  2 ++
 arch/arm64/kernel/proton-pack.c | 12 +++++++-----
 2 files changed, 9 insertions(+), 5 deletions(-)

diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S
index f8018b5c1f9a..cf808bb2abc0 100644
--- a/arch/arm64/kernel/entry.S
+++ b/arch/arm64/kernel/entry.S
@@ -10,6 +10,7 @@
 #include <linux/arm-smccc.h>
 #include <linux/init.h>
 #include <linux/linkage.h>
+#include <linux/annotate.h>
 
 #include <asm/alternative.h>
 #include <asm/assembler.h>
@@ -707,6 +708,7 @@ alternative_else_nop_endif
 	 * entry onto the return stack and using a RET instruction to
 	 * enter the full-fat kernel vectors.
 	 */
+	ANNOTATE_INTRA_FUNCTION_CALL
 	bl	2f
 	b	.
 2:
diff --git a/arch/arm64/kernel/proton-pack.c b/arch/arm64/kernel/proton-pack.c
index b3801f532b10..b63887a1b823 100644
--- a/arch/arm64/kernel/proton-pack.c
+++ b/arch/arm64/kernel/proton-pack.c
@@ -24,6 +24,7 @@
 #include <linux/nospec.h>
 #include <linux/prctl.h>
 #include <linux/sched/task_stack.h>
+#include <linux/annotate.h>
 
 #include <asm/debug-monitors.h>
 #include <asm/insn.h>
@@ -245,11 +246,12 @@ static noinstr void qcom_link_stack_sanitisation(void)
 {
 	u64 tmp;
 
-	asm volatile("mov	%0, x30		\n"
-		     ".rept	16		\n"
-		     "bl	. + 4		\n"
-		     ".endr			\n"
-		     "mov	x30, %0		\n"
+	asm volatile("mov	%0, x30			\n"
+		     ".rept	16			\n"
+		     ANNOTATE_INTRA_FUNCTION_CALL "	\n"
+		     "bl	. + 4			\n"
+		     ".endr				\n"
+		     "mov	x30, %0			\n"
 		     : "=&r" (tmp));
 }
 
-- 
2.53.0


^ permalink raw reply related

* [PATCH v2 00/12] objtool/arm64: Port klp-build to arm64
From: Josh Poimboeuf @ 2026-03-17 22:51 UTC (permalink / raw)
  To: x86
  Cc: linux-kernel, live-patching, Peter Zijlstra, Joe Lawrence,
	Song Liu, Catalin Marinas, Will Deacon, linux-arm-kernel,
	Mark Rutland, Nathan Chancellor, Nicolas Schier, Herbert Xu

v2:
- patches 1-2 were merged, rebase on tip/master
- improve commit message for "objtool: Extricate checksum calculation from validate_branch()"
- add review tags

v1: https://lore.kernel.org/cover.1772681234.git.jpoimboe@kernel.org

Port objtool and the klp-build tooling (for building livepatch modules)
to arm64.

Note this doesn't bring all the objtool bells and whistles to arm64, nor
any of the CFG reverse engineering.  This only adds the bare minimum
needed for 'objtool --checksum'.

And note that objtool still doesn't get enabled at all for normal arm64
kernel builds, so this doesn't affect any users other than those running
klp-build directly.

Josh Poimboeuf (12):
  arm64: Annotate intra-function calls
  arm64: head: Move boot header to .head.data
  arm64: Fix EFI linking with -fdata-sections
  crypto: arm64: Move data to .rodata
  objtool: Extricate checksum calculation from validate_branch()
  objtool: Allow setting --mnop without --mcount
  kbuild: Only run objtool if there is at least one command
  objtool: Ignore jumps to the end of the function for non-CFG arches
  objtool: Allow empty alternatives
  objtool: Reuse consecutive string references
  objtool: Introduce objtool for arm64
  klp-build: Support cross-compilation

 arch/arm64/Kconfig                            |   2 +
 arch/arm64/kernel/entry.S                     |   2 +
 arch/arm64/kernel/head.S                      |   2 +-
 arch/arm64/kernel/proton-pack.c               |  12 +-
 arch/arm64/kernel/vmlinux.lds.S               |   2 +-
 arch/x86/boot/startup/Makefile                |   2 +-
 include/asm-generic/vmlinux.lds.h             |   2 +-
 include/linux/init.h                          |   1 +
 lib/crypto/arm64/sha2-armv8.pl                |  11 +-
 scripts/Makefile.build                        |   4 +-
 scripts/Makefile.lib                          |  46 +++----
 scripts/Makefile.vmlinux_o                    |  15 +--
 scripts/livepatch/klp-build                   |  11 +-
 tools/objtool/Makefile                        |   4 +
 tools/objtool/arch/arm64/Build                |   2 +
 tools/objtool/arch/arm64/decode.c             | 116 ++++++++++++++++++
 .../arch/arm64/include/arch/cfi_regs.h        |  11 ++
 tools/objtool/arch/arm64/include/arch/elf.h   |  13 ++
 .../objtool/arch/arm64/include/arch/special.h |  21 ++++
 tools/objtool/arch/arm64/special.c            |  21 ++++
 tools/objtool/builtin-check.c                 |   5 -
 tools/objtool/check.c                         |  83 +++++++++----
 tools/objtool/elf.c                           |   9 +-
 tools/objtool/include/objtool/checksum.h      |   6 +-
 24 files changed, 321 insertions(+), 82 deletions(-)
 create mode 100644 tools/objtool/arch/arm64/Build
 create mode 100644 tools/objtool/arch/arm64/decode.c
 create mode 100644 tools/objtool/arch/arm64/include/arch/cfi_regs.h
 create mode 100644 tools/objtool/arch/arm64/include/arch/elf.h
 create mode 100644 tools/objtool/arch/arm64/include/arch/special.h
 create mode 100644 tools/objtool/arch/arm64/special.c

-- 
2.53.0


^ permalink raw reply

* Re: [PATCH 14/14] klp-build: Support cross-compilation
From: Song Liu @ 2026-03-17 18:58 UTC (permalink / raw)
  To: Josh Poimboeuf
  Cc: x86, linux-kernel, live-patching, Peter Zijlstra, Joe Lawrence,
	Catalin Marinas, Will Deacon, linux-arm-kernel, Mark Rutland,
	Nathan Chancellor, Nicolas Schier, Herbert Xu
In-Reply-To: <74fwwcyggggcnh7pj2i7nhglhagltkqeth4ykhtqs4izx4dtig@lzyai66d2vn6>

On Tue, Mar 17, 2026 at 11:53 AM Josh Poimboeuf <jpoimboe@kernel.org> wrote:
[...]
> >
> > Do we need ARCH when CROSS_COMPILE is set? I was
> > under the impression that CROSS_COMPILE doesn't require
> > ARCH.
>
> If CROSS_COMPILE is used without ARCH, it will just try to use the host
> arch.  I'm not sure if that's considered cross-compiling?   I suppose it
> should use the CROSS_COMPILE version of objcopy in that case?  Though in
> practice it probably doesn't matter.
>
> I guess the original version of the function is probably fine and we
> don't need to complicate matters.

Agreed that the original version works fine. Thanks for bearing with my
nitpick on this.

Song

^ permalink raw reply

* Re: [PATCH 14/14] klp-build: Support cross-compilation
From: Josh Poimboeuf @ 2026-03-17 18:53 UTC (permalink / raw)
  To: Song Liu
  Cc: x86, linux-kernel, live-patching, Peter Zijlstra, Joe Lawrence,
	Catalin Marinas, Will Deacon, linux-arm-kernel, Mark Rutland,
	Nathan Chancellor, Nicolas Schier, Herbert Xu
In-Reply-To: <CAPhsuW6SsDTDCJ8-w9OP3FeS2d0Rj6jgP7gYbzD3pZhAmK5nAg@mail.gmail.com>

On Tue, Mar 17, 2026 at 11:21:43AM -0700, Song Liu wrote:
> On Tue, Mar 17, 2026 at 10:52 AM Josh Poimboeuf <jpoimboe@kernel.org> wrote:
> [...]
> > >
> > > Yeah, I think that would be a good idea.  Will do that for v2.
> >
> > So, in general ARCH is complicated.  For example:
> >
> >  - ARCH=arm64 would never match "uname -m" (aarch64)
> >  - ARCH=i386 would use the same gcc binary (no cross-compiler needed)
> >  - I'm sure there are many other edge cases...
> 
> Agreed. I haven't worked with i386 for a long time, but I did notice
> the arm64 vs. aarch64 difference.
> 
> > Instead of a manual error, it may be simpler to just let the build fail
> > naturally if the user doesn't set the right ARCH.
> >
> > Though, I think the check can be improved slightly, as ARCH is a
> > reasonably good indicator that cross-compiling is happening.  So I can
> > at least add an ARCH check at the beginning like so?
> >
> > cross_compile_init() {
> >         if [[ ! -v ARCH ]]; then
> >                 OBJCOPY=objcopy
> >                 return 0
> >         fi
> >
> >         if [[ -v LLVM ]]; then
> >                 OBJCOPY=llvm-objcopy
> >         else
> >                 OBJCOPY="${CROSS_COMPILE:-}objcopy"
> >         fi
> > }
> 
> Do we need ARCH when CROSS_COMPILE is set? I was
> under the impression that CROSS_COMPILE doesn't require
> ARCH.

If CROSS_COMPILE is used without ARCH, it will just try to use the host
arch.  I'm not sure if that's considered cross-compiling?   I suppose it
should use the CROSS_COMPILE version of objcopy in that case?  Though in
practice it probably doesn't matter.

I guess the original version of the function is probably fine and we
don't need to complicate matters.

-- 
Josh

^ permalink raw reply

* Re: [PATCH 14/14] klp-build: Support cross-compilation
From: Song Liu @ 2026-03-17 18:21 UTC (permalink / raw)
  To: Josh Poimboeuf
  Cc: x86, linux-kernel, live-patching, Peter Zijlstra, Joe Lawrence,
	Catalin Marinas, Will Deacon, linux-arm-kernel, Mark Rutland,
	Nathan Chancellor, Nicolas Schier, Herbert Xu
In-Reply-To: <e2yxamlxwif5kxur7thr4x7yp7ppbde6awzm6vomdfkg6auxeq@aaahh3aclf2e>

On Tue, Mar 17, 2026 at 10:52 AM Josh Poimboeuf <jpoimboe@kernel.org> wrote:
[...]
> >
> > Yeah, I think that would be a good idea.  Will do that for v2.
>
> So, in general ARCH is complicated.  For example:
>
>  - ARCH=arm64 would never match "uname -m" (aarch64)
>  - ARCH=i386 would use the same gcc binary (no cross-compiler needed)
>  - I'm sure there are many other edge cases...

Agreed. I haven't worked with i386 for a long time, but I did notice
the arm64 vs. aarch64 difference.

> Instead of a manual error, it may be simpler to just let the build fail
> naturally if the user doesn't set the right ARCH.
>
> Though, I think the check can be improved slightly, as ARCH is a
> reasonably good indicator that cross-compiling is happening.  So I can
> at least add an ARCH check at the beginning like so?
>
> cross_compile_init() {
>         if [[ ! -v ARCH ]]; then
>                 OBJCOPY=objcopy
>                 return 0
>         fi
>
>         if [[ -v LLVM ]]; then
>                 OBJCOPY=llvm-objcopy
>         else
>                 OBJCOPY="${CROSS_COMPILE:-}objcopy"
>         fi
> }

Do we need ARCH when CROSS_COMPILE is set? I was
under the impression that CROSS_COMPILE doesn't require
ARCH.

Thanks,
Song

^ permalink raw reply

* Re: [PATCH 14/14] klp-build: Support cross-compilation
From: Josh Poimboeuf @ 2026-03-17 17:52 UTC (permalink / raw)
  To: Song Liu
  Cc: x86, linux-kernel, live-patching, Peter Zijlstra, Joe Lawrence,
	Catalin Marinas, Will Deacon, linux-arm-kernel, Mark Rutland,
	Nathan Chancellor, Nicolas Schier, Herbert Xu
In-Reply-To: <xzezfjfb5uttvmg2divzk3toym3qqvkh5c4w2enamsrku342m3@bogfmdj65wql>

On Mon, Mar 16, 2026 at 12:15:28PM -0700, Josh Poimboeuf wrote:
> On Wed, Mar 11, 2026 at 04:18:40PM -0700, Song Liu wrote:
> > On Wed, Mar 4, 2026 at 7:32 PM Josh Poimboeuf <jpoimboe@kernel.org> wrote:
> > >
> > > Add support for cross-compilation.  The user must export ARCH, and
> > > either CROSS_COMPILE or LLVM.
> > >
> > > Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
> > > ---
> > >  scripts/livepatch/klp-build | 11 ++++++++++-
> > >  1 file changed, 10 insertions(+), 1 deletion(-)
> > >
> > > diff --git a/scripts/livepatch/klp-build b/scripts/livepatch/klp-build
> > > index 809e198a561d..b6c057e2120f 100755
> > > --- a/scripts/livepatch/klp-build
> > > +++ b/scripts/livepatch/klp-build
> > > @@ -404,6 +404,14 @@ validate_patches() {
> > >         revert_patches
> > >  }
> > >
> > > +cross_compile_init() {
> > > +       if [[ -v LLVM ]]; then
> > > +               OBJCOPY=llvm-objcopy
> > > +       else
> > > +               OBJCOPY="${CROSS_COMPILE:-}objcopy"
> > > +       fi
> > > +}
> > 
> > Shall we show a specific warning if
> >   - ARCH is set; and
> >   - ARCH is not the same as (uname -m); and
> >   - neither LLVM nor CROSS_COMPILE is set.
> 
> Yeah, I think that would be a good idea.  Will do that for v2.

So, in general ARCH is complicated.  For example:

 - ARCH=arm64 would never match "uname -m" (aarch64)
 - ARCH=i386 would use the same gcc binary (no cross-compiler needed)
 - I'm sure there are many other edge cases...

Instead of a manual error, it may be simpler to just let the build fail
naturally if the user doesn't set the right ARCH.

Though, I think the check can be improved slightly, as ARCH is a
reasonably good indicator that cross-compiling is happening.  So I can
at least add an ARCH check at the beginning like so?

cross_compile_init() {
	if [[ ! -v ARCH ]]; then
		OBJCOPY=objcopy
		return 0
	fi

	if [[ -v LLVM ]]; then
		OBJCOPY=llvm-objcopy
	else
		OBJCOPY="${CROSS_COMPILE:-}objcopy"
	fi
}

-- 
Josh

^ permalink raw reply

* Re: [PATCH 07/14] objtool: Extricate checksum calculation from validate_branch()
From: Josh Poimboeuf @ 2026-03-17 17:21 UTC (permalink / raw)
  To: Miroslav Benes
  Cc: x86, linux-kernel, live-patching, Peter Zijlstra, Joe Lawrence,
	Song Liu, Catalin Marinas, Will Deacon, linux-arm-kernel,
	Mark Rutland, Nathan Chancellor, Nicolas Schier, Herbert Xu
In-Reply-To: <alpine.LSU.2.21.2603110916110.19933@pobox.suse.cz>

On Wed, Mar 11, 2026 at 09:24:22AM +0100, Miroslav Benes wrote:
> On Tue, 10 Mar 2026, Josh Poimboeuf wrote:
> 
> > On Tue, Mar 10, 2026 at 11:47:41AM +0100, Miroslav Benes wrote:
> > > Hi,
> > > 
> > > > @@ -3691,9 +3691,30 @@ static void checksum_update_insn(struct objtool_file *file, struct symbol *func,
> > > >  				 struct instruction *insn)
> > > >  {
> > > >  	struct reloc *reloc = insn_reloc(file, insn);
> > > > +	struct alternative *alt;
> > > >  	unsigned long offset;
> > > >  	struct symbol *sym;
> > > >  
> > > > +	for (alt = insn->alts; alt; alt = alt->next) {
> > > > +		struct alt_group *alt_group = alt->insn->alt_group;
> > > > +
> > > > +		checksum_update(func, insn, &alt->type, sizeof(alt->type));
> > > > +
> > > > +		if (alt_group && alt_group->orig_group) {
> > > > +			struct instruction *alt_insn;
> > > > +
> > > > +			checksum_update(func, insn, &alt_group->feature, sizeof(alt_group->feature));
> > > > +
> > > > +			for (alt_insn = alt->insn; alt_insn; alt_insn = next_insn_same_sec(file, alt_insn)) {
> > > > +				checksum_update_insn(file, func, alt_insn);
> > > > +				if (alt_insn == alt_group->last_insn)
> > > > +					break;
> > > > +			}
> > > > +		} else {
> > > > +			checksum_update(func, insn, &alt->insn->offset, sizeof(alt->insn->offset));
> > > > +		}
> > > > +	}
> > > > +
> > > 
> > > does this hunk belong to the patch? Unless I am missing something, it 
> > > might be worth a separate one.
> > 
> > It belongs, but I should have clarified that in the patch description.
> > 
> > This hunk wasn't needed before because validate_branch() already
> > iterates all the alternatives, so it was calling checksum_update_insn()
> > for every instruction in the function, including the alternatives.
> > 
> > Now that it's no longer called by validate_branch(),
> > checksum_update_insn() has to manually iterate the alternatives.
> 
> After writing the email I had a suspicion it must have been something like 
> above but failed to find it. Now I see that next_insn_to_validate() called 
> in do_validate_branch() handles exactly that. Thanks for the pointer. The 
> patch looks good to me then (and the rest as well as far as I can judge).

Actually, next_insn_to_validate() helps with an edge case for directing
code flow from the end of an alternative back to the original code.

The code which traverses the alternatives is in validate_insn():

	if (insn->alts) {
		for (alt = insn->alts; alt; alt = alt->next) {
			TRACE_ALT_BEGIN(insn, alt, alt_name);
			ret = validate_branch(file, func, alt->insn, *statep);
			TRACE_ALT_END(insn, alt, alt_name);
			if (ret) {
				BT_INSN(insn, "(alt)");
				return ret;
			}
		}
		TRACE_ALT_INFO_NOADDR(insn, "/ ", "DEFAULT");
	}

> I must admit that objtool has gotten so complex that I have a hard time to 
> track everything in the code :).

The code hasn't changed *too* much, it's just that validate_branch() got
split up more when the tracing code went in, so things are organized a
bit differently.  Most of that code is now in validate_insn().

-- 
Josh

^ permalink raw reply

* [PATCH] module/kallsyms: sort function symbols and use binary search
From: Stanislaw Gruszka @ 2026-03-17 11:04 UTC (permalink / raw)
  To: linux-modules, Sami Tolvanen, Luis Chamberlain
  Cc: linux-kernel, linux-trace-kernel, live-patching, Petr Pavlu,
	Daniel Gomez, Aaron Tomlin, Steven Rostedt, Masami Hiramatsu,
	Jordan Rome, Viktor Malik

Module symbol lookup via find_kallsyms_symbol() performs a linear scan
over the entire symtab when resolving an address. The number of symbols
in module symtabs has grown over the years, largely due to additional
metadata in non-standard sections, making this lookup very slow.

Improve this by separating function symbols during module load, placing
them at the beginning of the symtab, sorting them by address, and using
binary search when resolving addresses in module text.

This also should improve times for linear symbol name lookups, as valid
function symbols are now located at the beginning of the symtab.

The cost of sorting is small relative to module load time. In repeated
module load tests [1], depending on .config options, this change
increases load time between 2% and 4%. With cold caches, the difference
is not measurable, as memory access latency dominates.

The sorting theoretically could be done in compile time, but much more
complicated as we would have to simulate kernel addresses resolution
for symbols, and then correct relocation entries. That would be risky
if get out of sync.

The improvement can be observed when listing ftrace filter functions:

root@nano:~# time cat /sys/kernel/tracing/available_filter_functions | wc -l
74908

real	0m1.315s
user	0m0.000s
sys	0m1.312s

After:

root@nano:~# time cat /sys/kernel/tracing/available_filter_functions | wc -l
74911

real	0m0.167s
user	0m0.004s
sys	0m0.175s

(there are three more symbols introduced by the patch)

For livepatch modules, the symtab layout is preserved and the existing
linear search is used. For this case, it should be possible to keep
the original ELF symtab instead of copying it 1:1, but that is outside
the scope of this patch.

Link: https://gist.github.com/sgruszka/09f3fb1dad53a97b1aad96e1927ab117 [1]
Signed-off-by: Stanislaw Gruszka <stf_xl@wp.pl>
---
 include/linux/module.h   |   6 ++
 kernel/module/internal.h |   1 +
 kernel/module/kallsyms.c | 169 ++++++++++++++++++++++++++++-----------
 3 files changed, 131 insertions(+), 45 deletions(-)

diff --git a/include/linux/module.h b/include/linux/module.h
index ac254525014c..4da0289fba02 100644
--- a/include/linux/module.h
+++ b/include/linux/module.h
@@ -379,8 +379,14 @@ struct module_memory {
 struct mod_kallsyms {
 	Elf_Sym *symtab;
 	unsigned int num_symtab;
+	unsigned int num_func_syms;
 	char *strtab;
 	char *typetab;
+
+	unsigned int (*search)(struct mod_kallsyms *kallsyms,
+			       struct module_memory *mod_mem,
+			       unsigned long addr, unsigned long *bestval,
+			       unsigned long *nextval);
 };
 
 #ifdef CONFIG_LIVEPATCH
diff --git a/kernel/module/internal.h b/kernel/module/internal.h
index 618202578b42..6a4d498619b1 100644
--- a/kernel/module/internal.h
+++ b/kernel/module/internal.h
@@ -73,6 +73,7 @@ struct load_info {
 	bool sig_ok;
 #ifdef CONFIG_KALLSYMS
 	unsigned long mod_kallsyms_init_off;
+	unsigned long num_func_syms;
 #endif
 #ifdef CONFIG_MODULE_DECOMPRESS
 #ifdef CONFIG_MODULE_STATS
diff --git a/kernel/module/kallsyms.c b/kernel/module/kallsyms.c
index 0fc11e45df9b..9e131baae441 100644
--- a/kernel/module/kallsyms.c
+++ b/kernel/module/kallsyms.c
@@ -10,6 +10,7 @@
 #include <linux/kallsyms.h>
 #include <linux/buildid.h>
 #include <linux/bsearch.h>
+#include <linux/sort.h>
 #include "internal.h"
 
 /* Lookup exported symbol in given range of kernel_symbols */
@@ -103,6 +104,95 @@ static bool is_core_symbol(const Elf_Sym *src, const Elf_Shdr *sechdrs,
 	return true;
 }
 
+static inline bool is_func_symbol(const Elf_Sym *sym)
+{
+	return sym->st_shndx != SHN_UNDEF && sym->st_size != 0 &&
+	       ELF_ST_TYPE(sym->st_info) == STT_FUNC;
+}
+
+static unsigned int bsearch_kallsyms_symbol(struct mod_kallsyms *kallsyms,
+					    struct module_memory *mod_mem,
+					    unsigned long addr,
+					    unsigned long *bestval,
+					    unsigned long *nextval)
+
+{
+	unsigned int mid, low = 1, high = kallsyms->num_func_syms + 1;
+	unsigned int best = 0;
+	unsigned long thisval;
+
+	while (low < high) {
+		mid = low + (high - low) / 2;
+		thisval = kallsyms_symbol_value(&kallsyms->symtab[mid]);
+
+		if (thisval <= addr) {
+			*bestval = thisval;
+			best = mid;
+			low = mid + 1;
+		} else {
+			*nextval = thisval;
+			high = mid;
+		}
+	}
+
+	return best;
+}
+
+static const char *kallsyms_symbol_name(struct mod_kallsyms *kallsyms,
+					unsigned int symnum)
+{
+	return kallsyms->strtab + kallsyms->symtab[symnum].st_name;
+}
+
+static unsigned int search_kallsyms_symbol(struct mod_kallsyms *kallsyms,
+					   struct module_memory *mod_mem,
+					   unsigned long addr,
+					   unsigned long *bestval,
+					   unsigned long *nextval)
+{
+	unsigned int i, best = 0;
+
+	/*
+	 * Scan for closest preceding symbol, and next symbol. (ELF
+	 * starts real symbols at 1).
+	 */
+	for (i = 1; i < kallsyms->num_symtab; i++) {
+		const Elf_Sym *sym = &kallsyms->symtab[i];
+		unsigned long thisval = kallsyms_symbol_value(sym);
+
+		if (sym->st_shndx == SHN_UNDEF)
+			continue;
+
+		/*
+		 * We ignore unnamed symbols: they're uninformative
+		 * and inserted at a whim.
+		 */
+		if (*kallsyms_symbol_name(kallsyms, i) == '\0' ||
+		    is_mapping_symbol(kallsyms_symbol_name(kallsyms, i)))
+			continue;
+
+		if (thisval <= addr && thisval > *bestval) {
+			best = i;
+			*bestval = thisval;
+		}
+		if (thisval > addr && thisval < *nextval)
+			*nextval = thisval;
+	}
+
+	return best;
+}
+
+static int elf_sym_cmp(const void *a, const void *b)
+{
+	const Elf_Sym *sym_a = a;
+	const Elf_Sym *sym_b = b;
+
+	if (sym_a->st_value < sym_b->st_value)
+		return -1;
+
+	return sym_a->st_value > sym_b->st_value;
+}
+
 /*
  * We only allocate and copy the strings needed by the parts of symtab
  * we keep.  This is simple, but has the effect of making multiple
@@ -115,9 +205,10 @@ void layout_symtab(struct module *mod, struct load_info *info)
 	Elf_Shdr *symsect = info->sechdrs + info->index.sym;
 	Elf_Shdr *strsect = info->sechdrs + info->index.str;
 	const Elf_Sym *src;
-	unsigned int i, nsrc, ndst, strtab_size = 0;
+	unsigned int i, nsrc, ndst, nfunc, strtab_size = 0;
 	struct module_memory *mod_mem_data = &mod->mem[MOD_DATA];
 	struct module_memory *mod_mem_init_data = &mod->mem[MOD_INIT_DATA];
+	bool is_lp_mod = is_livepatch_module(mod);
 
 	/* Put symbol section at end of init part of module. */
 	symsect->sh_flags |= SHF_ALLOC;
@@ -129,12 +220,14 @@ void layout_symtab(struct module *mod, struct load_info *info)
 	nsrc = symsect->sh_size / sizeof(*src);
 
 	/* Compute total space required for the core symbols' strtab. */
-	for (ndst = i = 0; i < nsrc; i++) {
-		if (i == 0 || is_livepatch_module(mod) ||
+	for (ndst = nfunc = i = 0; i < nsrc; i++) {
+		if (i == 0 || is_lp_mod ||
 		    is_core_symbol(src + i, info->sechdrs, info->hdr->e_shnum,
 				   info->index.pcpu)) {
 			strtab_size += strlen(&info->strtab[src[i].st_name]) + 1;
 			ndst++;
+			if (!is_lp_mod && is_func_symbol(src + i))
+				nfunc++;
 		}
 	}
 
@@ -156,6 +249,7 @@ void layout_symtab(struct module *mod, struct load_info *info)
 	mod_mem_init_data->size = ALIGN(mod_mem_init_data->size,
 					__alignof__(struct mod_kallsyms));
 	info->mod_kallsyms_init_off = mod_mem_init_data->size;
+	info->num_func_syms = nfunc;
 
 	mod_mem_init_data->size += sizeof(struct mod_kallsyms);
 	info->init_typeoffs = mod_mem_init_data->size;
@@ -169,7 +263,7 @@ void layout_symtab(struct module *mod, struct load_info *info)
  */
 void add_kallsyms(struct module *mod, const struct load_info *info)
 {
-	unsigned int i, ndst;
+	unsigned int i, di, nfunc, ndst;
 	const Elf_Sym *src;
 	Elf_Sym *dst;
 	char *s;
@@ -178,6 +272,7 @@ void add_kallsyms(struct module *mod, const struct load_info *info)
 	void *data_base = mod->mem[MOD_DATA].base;
 	void *init_data_base = mod->mem[MOD_INIT_DATA].base;
 	struct mod_kallsyms *kallsyms;
+	bool is_lp_mod = is_livepatch_module(mod);
 
 	kallsyms = init_data_base + info->mod_kallsyms_init_off;
 
@@ -186,6 +281,7 @@ void add_kallsyms(struct module *mod, const struct load_info *info)
 	/* Make sure we get permanent strtab: don't use info->strtab. */
 	kallsyms->strtab = (void *)info->sechdrs[info->index.str].sh_addr;
 	kallsyms->typetab = init_data_base + info->init_typeoffs;
+	kallsyms->search = search_kallsyms_symbol;
 
 	/*
 	 * Now populate the cut down core kallsyms for after init
@@ -194,19 +290,30 @@ void add_kallsyms(struct module *mod, const struct load_info *info)
 	mod->core_kallsyms.symtab = dst = data_base + info->symoffs;
 	mod->core_kallsyms.strtab = s = data_base + info->stroffs;
 	mod->core_kallsyms.typetab = data_base + info->core_typeoffs;
+	mod->core_kallsyms.search = is_lp_mod ? search_kallsyms_symbol :
+						bsearch_kallsyms_symbol;
+
 	strtab_size = info->core_typeoffs - info->stroffs;
 	src = kallsyms->symtab;
-	for (ndst = i = 0; i < kallsyms->num_symtab; i++) {
+	ndst = info->num_func_syms + 1;
+
+	for (nfunc = i = 0; i < kallsyms->num_symtab; i++) {
 		kallsyms->typetab[i] = elf_type(src + i, info);
-		if (i == 0 || is_livepatch_module(mod) ||
+		if (i == 0 || is_lp_mod ||
 		    is_core_symbol(src + i, info->sechdrs, info->hdr->e_shnum,
 				   info->index.pcpu)) {
 			ssize_t ret;
 
-			mod->core_kallsyms.typetab[ndst] =
-				kallsyms->typetab[i];
-			dst[ndst] = src[i];
-			dst[ndst++].st_name = s - mod->core_kallsyms.strtab;
+			if (i == 0)
+				di = 0;
+			else if (!is_lp_mod && is_func_symbol(src + i))
+				di = 1 + nfunc++;
+			else
+				di = ndst++;
+
+			mod->core_kallsyms.typetab[di] = kallsyms->typetab[i];
+			dst[di] = src[i];
+			dst[di].st_name = s - mod->core_kallsyms.strtab;
 			ret = strscpy(s, &kallsyms->strtab[src[i].st_name],
 				      strtab_size);
 			if (ret < 0)
@@ -216,9 +323,13 @@ void add_kallsyms(struct module *mod, const struct load_info *info)
 		}
 	}
 
+	WARN_ON_ONCE(nfunc != info->num_func_syms);
+	sort(dst + 1, nfunc, sizeof(Elf_Sym), elf_sym_cmp, NULL);
+
 	/* Set up to point into init section. */
 	rcu_assign_pointer(mod->kallsyms, kallsyms);
 	mod->core_kallsyms.num_symtab = ndst;
+	mod->core_kallsyms.num_func_syms = nfunc;
 }
 
 #if IS_ENABLED(CONFIG_STACKTRACE_BUILD_ID)
@@ -241,11 +352,6 @@ void init_build_id(struct module *mod, const struct load_info *info)
 }
 #endif
 
-static const char *kallsyms_symbol_name(struct mod_kallsyms *kallsyms, unsigned int symnum)
-{
-	return kallsyms->strtab + kallsyms->symtab[symnum].st_name;
-}
-
 /*
  * Given a module and address, find the corresponding symbol and return its name
  * while providing its size and offset if needed.
@@ -255,7 +361,7 @@ static const char *find_kallsyms_symbol(struct module *mod,
 					unsigned long *size,
 					unsigned long *offset)
 {
-	unsigned int i, best = 0;
+	unsigned int best;
 	unsigned long nextval, bestval;
 	struct mod_kallsyms *kallsyms = rcu_dereference(mod->kallsyms);
 	struct module_memory *mod_mem;
@@ -267,36 +373,9 @@ static const char *find_kallsyms_symbol(struct module *mod,
 		mod_mem = &mod->mem[MOD_TEXT];
 
 	nextval = (unsigned long)mod_mem->base + mod_mem->size;
+	bestval = kallsyms_symbol_value(&kallsyms->symtab[0]);
 
-	bestval = kallsyms_symbol_value(&kallsyms->symtab[best]);
-
-	/*
-	 * Scan for closest preceding symbol, and next symbol. (ELF
-	 * starts real symbols at 1).
-	 */
-	for (i = 1; i < kallsyms->num_symtab; i++) {
-		const Elf_Sym *sym = &kallsyms->symtab[i];
-		unsigned long thisval = kallsyms_symbol_value(sym);
-
-		if (sym->st_shndx == SHN_UNDEF)
-			continue;
-
-		/*
-		 * We ignore unnamed symbols: they're uninformative
-		 * and inserted at a whim.
-		 */
-		if (*kallsyms_symbol_name(kallsyms, i) == '\0' ||
-		    is_mapping_symbol(kallsyms_symbol_name(kallsyms, i)))
-			continue;
-
-		if (thisval <= addr && thisval > bestval) {
-			best = i;
-			bestval = thisval;
-		}
-		if (thisval > addr && thisval < nextval)
-			nextval = thisval;
-	}
-
+	best = kallsyms->search(kallsyms, mod_mem, addr, &bestval, &nextval);
 	if (!best)
 		return NULL;
 
-- 
2.50.1


^ permalink raw reply related

* Re: [PATCH v4 00/12] livepatch-klp-build: small fixups and enhancements
From: Josh Poimboeuf @ 2026-03-16 22:23 UTC (permalink / raw)
  To: Joe Lawrence
  Cc: live-patching, Song Liu, Jiri Kosina, Miroslav Benes, Petr Mladek
In-Reply-To: <20260310203751.1479229-1-joe.lawrence@redhat.com>

On Tue, Mar 10, 2026 at 04:37:39PM -0400, Joe Lawrence wrote:
> v4:
> 
> - Rebased on 9a73f085dc91 (tip/objtool/urgent)
> - Dropped elf_add_data() fix, added __clone_symbol() align fix [Josh]
> - Adopted Josh's kernel version fix [Josh]
> - Updated friendly msg, "foo.patch: unsupported patch to bar.c" [Josh]
> - s/warn:/warning:/, trap_err -> die, and commit msg clarification on
>   colorization commit [Josh]
> - Use "fuzz" instead of "drift", adjust output ordering, and send patch
>   cmd errors to stderr [Josh]
> - Dropped ("livepatch/klp-build: don't look for changed objects in
>   tools/") for now
> - Added Song's Acks [Song]

If there are no objections, I will go ahead and queue these up in the
-tip tree.

-- 
Josh

^ permalink raw reply

* Re: [PATCH 4/8] selftests: livepatch: functions: Introduce check_sysfs_exists
From: Joe Lawrence @ 2026-03-16 20:47 UTC (permalink / raw)
  To: Marcos Paulo de Souza
  Cc: Josh Poimboeuf, Jiri Kosina, Miroslav Benes, Petr Mladek,
	Shuah Khan, live-patching, linux-kselftest, linux-kernel
In-Reply-To: <20260313-lp-tests-old-fixes-v1-4-71ac6dfb3253@suse.com>

On Fri, Mar 13, 2026 at 05:58:35PM -0300, Marcos Paulo de Souza wrote:
> Return 0 if the livepatch sysfs attribute don't exists, and 1 otherwise.
> This new function will be used in the next patches.
> 
> Signed-off-by: Marcos Paulo de Souza <mpdesouza@suse.com>
> ---
>  tools/testing/selftests/livepatch/functions.sh | 14 ++++++++++++++
>  1 file changed, 14 insertions(+)
> 
> diff --git a/tools/testing/selftests/livepatch/functions.sh b/tools/testing/selftests/livepatch/functions.sh
> index 8ec0cb64ad94a..781346d6e94e0 100644
> --- a/tools/testing/selftests/livepatch/functions.sh
> +++ b/tools/testing/selftests/livepatch/functions.sh
> @@ -339,6 +339,20 @@ function check_result {
>  	fi
>  }
>  
> +# check_sysfs_exists(modname, attr) - check sysfs attribute existence
> +#	modname - livepatch module creating the sysfs interface
> +#	attr - attribute name to be checked
> +function check_sysfs_exists() {
> +	local mod="$1"; shift
> +	local attr="$1"; shift
> +
> +	if [[ ! -f "$SYSFS_KLP_DIR/$mod/$attr" ]]; then
> +		return 0
> +	fi
> +
> +	return 1
> +}
> +

I don't have my shell coding hat on, but a few questions:

1. I thought shell functions usually returned 1 for a failed result and
   0 on success?

2. Could this be reduced to (assuming inverting the return as well):

  function check_sysfs_exists() {
  	local mod="$1"; shift
  	local attr="$1"; shift
  
  	[[ -f "$SYSFS_KLP_DIR/$mod/$attr" ]]
  }

3. A higher level question, but the other check_* functions will die
   "reason" on failure.  Would it be better to name this one with
   does_sysfs_exist() to indicate that subtle difference?  (Or "has" or
   some other kind of prefix.)

Regards,
--
Joe


^ permalink raw reply

* Re: [PATCH 3/8] selftests: livepatch: test-kprobe: Check if kprobes can work with livepatches
From: Joe Lawrence @ 2026-03-16 20:38 UTC (permalink / raw)
  To: Marcos Paulo de Souza
  Cc: Josh Poimboeuf, Jiri Kosina, Miroslav Benes, Petr Mladek,
	Shuah Khan, live-patching, linux-kselftest, linux-kernel
In-Reply-To: <20260313-lp-tests-old-fixes-v1-3-71ac6dfb3253@suse.com>

On Fri, Mar 13, 2026 at 05:58:34PM -0300, Marcos Paulo de Souza wrote:
> Running the upstream selftests on older kernels can presente some issues
> regarding features being not present. One of such issues if the missing
> capability of having both kprobes and livepatches on the same function.
>

nit picking, but slightly reworded for clarity and spelling:

Running upstream selftests on older kernels can be problematic when
features or fixes from newer versions are not present. For example,
older kernels may lack the capability to support kprobes and livepatches
on the same function simultaneously.

> The support was introduced in commit 0bc11ed5ab60c
> ("kprobes: Allow kprobes coexist with livepatch"), which means that older
> kernels may lack this change.
> 
> The lack of this feature can be checked when a kprobe without a
> post_handler is loaded and checking that the enabled_function's file
> shows the flag "I". A kernel with the proper support for kprobes and
> livepatches would presente the flag only when a post_handler is

nit: s/presente/present 

> registered.
> 
> No functional changes.
> 
> Signed-off-by: Marcos Paulo de Souza <mpdesouza@suse.com>
> ---
>  tools/testing/selftests/livepatch/test-kprobe.sh | 52 ++++++++++++++----------
>  1 file changed, 31 insertions(+), 21 deletions(-)
> 
> diff --git a/tools/testing/selftests/livepatch/test-kprobe.sh b/tools/testing/selftests/livepatch/test-kprobe.sh
> index cdf31d0e51955..44cd16156dbd4 100755
> --- a/tools/testing/selftests/livepatch/test-kprobe.sh
> +++ b/tools/testing/selftests/livepatch/test-kprobe.sh
> @@ -16,30 +16,19 @@ setup_config
>  # when it uses a post_handler since only one IPMODIFY maybe be registered
>  # to any given function at a time.
>  
> -start_test "livepatch interaction with kprobed function with post_handler"
> -
> -echo 1 > "$SYSFS_KPROBES_DIR/enabled"
> -
> -load_mod $MOD_KPROBE has_post_handler=1
> -load_failing_mod $MOD_LIVEPATCH
> -unload_mod $MOD_KPROBE
> -
> -check_result "% insmod test_modules/test_klp_kprobe.ko has_post_handler=1
> -% insmod test_modules/$MOD_LIVEPATCH.ko
> -livepatch: enabling patch '$MOD_LIVEPATCH'
> -livepatch: '$MOD_LIVEPATCH': initializing patching transition
> -livepatch: failed to register ftrace handler for function 'cmdline_proc_show' (-16)
> -livepatch: failed to patch object 'vmlinux'
> -livepatch: failed to enable patch '$MOD_LIVEPATCH'
> -livepatch: '$MOD_LIVEPATCH': canceling patching transition, going to unpatch
> -livepatch: '$MOD_LIVEPATCH': completing unpatching transition
> -livepatch: '$MOD_LIVEPATCH': unpatching complete
> -insmod: ERROR: could not insert module test_modules/$MOD_LIVEPATCH.ko: Device or resource busy
> -% rmmod test_klp_kprobe"
> -
>  start_test "livepatch interaction with kprobed function without post_handler"
>  
>  load_mod $MOD_KPROBE has_post_handler=0
> +
> +# Check if commit 0bc11ed5ab60c ("kprobes: Allow kprobes coexist with livepatch")
> +# is missing, meaning that livepatches and kprobes can't be used together.
> +# When the commit is missing, kprobes always set IPMODIFY (the I flag), even
> +# when the post handler is missing.
> +if grep --quiet ") R I" "$SYSFS_DEBUG_DIR/tracing/enabled_functions"; then

Will flags R I always be in this order?

--
Joe


^ permalink raw reply

* Re: [PATCH 1/8] selftests: livepatch: test-syscall: Check for ARCH_HAS_SYSCALL_WRAPPER
From: Joe Lawrence @ 2026-03-16 20:12 UTC (permalink / raw)
  To: Marcos Paulo de Souza
  Cc: Josh Poimboeuf, Jiri Kosina, Miroslav Benes, Petr Mladek,
	Shuah Khan, live-patching, linux-kselftest, linux-kernel
In-Reply-To: <20260313-lp-tests-old-fixes-v1-1-71ac6dfb3253@suse.com>

On Fri, Mar 13, 2026 at 05:58:32PM -0300, Marcos Paulo de Souza wrote:
> Instead of checking if the architecture running the test was powerpc,
> check if CONF_ARCH_HAS_SYSCALL_WRAPPER is defined or not.
> 
> No functional changes.
> 
> Signed-off-by: Marcos Paulo de Souza <mpdesouza@suse.com>
> ---
>  tools/testing/selftests/livepatch/test_modules/test_klp_syscall.c | 7 +++----
>  1 file changed, 3 insertions(+), 4 deletions(-)
> 
> diff --git a/tools/testing/selftests/livepatch/test_modules/test_klp_syscall.c b/tools/testing/selftests/livepatch/test_modules/test_klp_syscall.c
> index dd802783ea849..c01a586866304 100644
> --- a/tools/testing/selftests/livepatch/test_modules/test_klp_syscall.c
> +++ b/tools/testing/selftests/livepatch/test_modules/test_klp_syscall.c
> @@ -12,15 +12,14 @@
>  #include <linux/slab.h>
>  #include <linux/livepatch.h>
>  
> -#if defined(__x86_64__)
> +#if !defined(CONFIG_ARCH_HAS_SYSCALL_WRAPPER)
> +#define FN_PREFIX
> +#elif defined(__x86_64__)
>  #define FN_PREFIX __x64_
>  #elif defined(__s390x__)
>  #define FN_PREFIX __s390x_
>  #elif defined(__aarch64__)
>  #define FN_PREFIX __arm64_
> -#else
> -/* powerpc does not select ARCH_HAS_SYSCALL_WRAPPER */
> -#define FN_PREFIX

The patch does maintain the previous behavior, but I'm wondering if the
original assertion about ARCH_HAS_SYSCALL_WRAPPER on Power was correct:

  $ grep ARCH_HAS_SYSCALL_WRAPPER arch/powerpc/Kconfig
          select ARCH_HAS_SYSCALL_WRAPPER         if !SPU_BASE && !COMPAT
          depends on PPC64 && ARCH_HAS_SYSCALL_WRAPPER

Perhaps I just forgot what that additional piece of information that
explains the comment (highly probable these days), and if so, might be
nice to add to this commit since I don't see it in 6a71770442b5
("selftests: livepatch: Test livepatching a heavily called syscall").

Thanks,
--
Joe


^ permalink raw reply

* Re: [PATCH 14/14] klp-build: Support cross-compilation
From: Josh Poimboeuf @ 2026-03-16 19:15 UTC (permalink / raw)
  To: Song Liu
  Cc: x86, linux-kernel, live-patching, Peter Zijlstra, Joe Lawrence,
	Catalin Marinas, Will Deacon, linux-arm-kernel, Mark Rutland,
	Nathan Chancellor, Nicolas Schier, Herbert Xu
In-Reply-To: <CAPhsuW6Cyw_z+9sWt5G1XOp94z8BbwNmsoVE9=iM8WQfkuNDBA@mail.gmail.com>

On Wed, Mar 11, 2026 at 04:18:40PM -0700, Song Liu wrote:
> On Wed, Mar 4, 2026 at 7:32 PM Josh Poimboeuf <jpoimboe@kernel.org> wrote:
> >
> > Add support for cross-compilation.  The user must export ARCH, and
> > either CROSS_COMPILE or LLVM.
> >
> > Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
> > ---
> >  scripts/livepatch/klp-build | 11 ++++++++++-
> >  1 file changed, 10 insertions(+), 1 deletion(-)
> >
> > diff --git a/scripts/livepatch/klp-build b/scripts/livepatch/klp-build
> > index 809e198a561d..b6c057e2120f 100755
> > --- a/scripts/livepatch/klp-build
> > +++ b/scripts/livepatch/klp-build
> > @@ -404,6 +404,14 @@ validate_patches() {
> >         revert_patches
> >  }
> >
> > +cross_compile_init() {
> > +       if [[ -v LLVM ]]; then
> > +               OBJCOPY=llvm-objcopy
> > +       else
> > +               OBJCOPY="${CROSS_COMPILE:-}objcopy"
> > +       fi
> > +}
> 
> Shall we show a specific warning if
>   - ARCH is set; and
>   - ARCH is not the same as (uname -m); and
>   - neither LLVM nor CROSS_COMPILE is set.

Yeah, I think that would be a good idea.  Will do that for v2.

-- 
Josh

^ permalink raw reply

* Re: [PATCH kbuild v2] kbuild: Reduce the number of compiler-generated suffixes for clang thin-lto build
From: Nicolas Schier @ 2026-03-16  7:07 UTC (permalink / raw)
  To: Yonghong Song
  Cc: linux-kbuild, live-patching, Josh Poimboeuf, kernel-team,
	Nathan Chancellor, Song Liu
In-Reply-To: <044bebc0-d996-4be3-9330-a64195c19a84@linux.dev>

On Sun, Mar 15, 2026 at 09:37:22AM -0700, Yonghong Song wrote:
> 
> 
> On 3/12/26 7:12 AM, Nicolas Schier wrote:
> > On Fri, 06 Mar 2026 21:02:50 -0800, Yonghong Song wrote:
> > > The current clang thin-lto build often produces lots of symbols with
> > > suffix. The following is a partial list of such function call symbols:
> > >      ...
> > >      ethnl_module_fw_flash_ntf.llvm.7631589765585346066
> > >      __nf_conntrack_alloc.llvm.6438426151906658917
> > >      tcp_can_early_drop.llvm.11937612064648250727
> > >      tcp_print_conntrack.llvm.11937612064648250727
> > >      ...
> > > 
> > > [...]
> > Note: Due to application of [1] to kbuild-next-unstable, I had to update the
> >        patch context.
> > 
> > [1]: https://lore.kernel.org/linux-kbuild/20251028182822.3210436-1-xur@google.com/
> > 
> > 
> > 
> > Applied to kbuild/kbuild-next.git (kbuild-next-unstable), thanks!
> > 
> > [1/1] kbuild: Reduce the number of compiler-generated suffixes for clang thin-lto build
> >        https://git.kernel.org/kbuild/c/b7a7ce34
> > 
> > Please look out for regression or issue reports or other follow up
> > comments, as they may result in the patch/series getting dropped,
> > reverted or modified (e.g. trailers). Patches applied to the
> > kbuild-next-unstable branch are accepted pending wider testing in
> > linux-next and any post-commit review; they will generally be moved
> > to the kbuild-next branch in about a week if no issues are found.
> 
> Thanks, Nicolas,
> 
> I looked at the patch [1] and find that my patch needs some change.
> The current change is
> 
> @@ -1047,6 +1047,7 @@ CC_FLAGS_LTO := -flto
> else
> CC_FLAGS_LTO := -flto=thin -fsplit-lto-unit
> +KBUILD_LDFLAGS += $(call ld-option,--lto-whole-program-visibility -mllvm
> -always-rename-promoted-locals=false)
> endif
> CC_FLAGS_LTO += -fvisibility=hidden Due to [1], the above change should be
> @@ -1047,6 +1047,7 @@ CC_FLAGS_LTO := -flto
> else
> CC_FLAGS_LTO := -flto=thin -fsplit-lto-unit
> +if CONFIG_LTO_CLANG_THIN
> +KBUILD_LDFLAGS += $(call ld-option,--lto-whole-program-visibility -mllvm
> -always-rename-promoted-locals=false)
> +endif
> endif
> CC_FLAGS_LTO += -fvisibility=hidden
> 
> The reason likes below:
> 
> The patch [1] introduced CONFOG_LTO_CLANG_THIN_DIST and in Makefile, for the following change:
> 
>  ifdef CONFIG_LTO_CLANG
> -ifdef CONFIG_LTO_CLANG_THIN -CC_FLAGS_LTO := -flto=thin -fsplit-lto-unit
> -else +ifdef CONFIG_LTO_CLANG_FULL  CC_FLAGS_LTO	:= -flto
> +else +CC_FLAGS_LTO := -flto=thin -fsplit-lto-unit  endif
>  CC_FLAGS_LTO	+= -fvisibility=hidden
> 
> The else branch 'CC_FLAGS_LTO := -flto=thin -fsplit-lto-unit' will support both CONFIG_LTO_CLANG_THIN and CONFIG_LTO_CLANG_THIN_DIST.
> 
> My patch commit message mentioned that the new flag won't support
> thinlto distributed mode yet. So The new ldflags
>   $(call ld-option,--lto-whole-program-visibility -mllvm
> -always-rename-promoted-locals=false) needs under LTO_CLANG_THIN but not
> LTO_CLANG_THIN_DIST. There will be some effort in llvm to support
> distributed thin-lto as well for suffix reduction. But it may take a little
> bit time as llvm needs some infrastructure change before supporting
> distributed thin-lto. Thanks!

Thanks a lot!  I changed the commit as instructed:

$ git range-diff b7a7ce345daa...6a76b3c06a1d
1:  b7a7ce345daa ! 1:  6a76b3c06a1d kbuild: Reduce the number of compiler-generated suffixes for clang thin-lto build
    @@ Makefile: ifdef CONFIG_LTO_CLANG_FULL
      CC_FLAGS_LTO      := -flto
      else
      CC_FLAGS_LTO      := -flto=thin -fsplit-lto-unit
    ++if CONFIG_LTO_CLANG_THIN
     +KBUILD_LDFLAGS += $(call ld-option,--lto-whole-program-visibility -mllvm -always-rename-promoted-locals=false)
    ++endif
      endif
      CC_FLAGS_LTO      += -fvisibility=hidden


and it is pushed to kbuild/kbuild-next-unstable
https://git.kernel.org/kbuild/c/6a76b3c06a1d359a3e84c37bc0fead370f6ccfc0


-- 
Nicolas

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox