From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id EF36A38F64C; Mon, 3 Aug 2026 03:24:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785727493; cv=none; b=YtPArBGBxNCuBZ5SIwTmMYaLO5zYwOrUgA67Xxze3yLo8BO4ZqQ01ebaDW7zIJTndW6/mVX2VGXJrXRxcCh6eZuinByKvo/+kCq7qBwYa+l7kk6RiWe3h96JDP3/xa4E8fqokulniHNM00tarAmhIpgFGg3Yb+FG4iFrszSQvuA= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785727493; c=relaxed/simple; bh=W4wO4L2SaAJEsHszjhStRI1k4X2ySnkaNcQsbJZ8ZW8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Mid95LAFCQwUl/3/9iTsTqyMIwLMYB1sQ2ocqWc7g/+RPw70glDS2NmGNEBoM1qldZx1kMz+YxwlNsjFCEs6KDFwea/5lnMzYW7o8fi6Rz3t6ow5mtqEPDe2GlvEuh1bRaS77EZEYtXke4KU4vHHY3d4uqp6Xq1ptHbpsnzI6jQ= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=V/zjbL8C; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="V/zjbL8C" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 615A31F00A3A; Mon, 3 Aug 2026 03:24:51 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1785727491; bh=U+FHei45N946Wh+aw6fEo/doxqF/olqKe92EL2KuFaU=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=V/zjbL8CDtcrxq8cf/+SRIFPsSdQi5XhNTs1Hax2caYAMoVlWzvbncQd9PdDjp/Gk eF49HZCpbDfhg8RIkyWp708Jz6bgzyqnaoRAjY/TqtA6x+tYhTLGuE1O9DtEsFmHsZ jxr+R1cQsanDw4mn8h5aGUEw3hkE9OboCJschfiiuznuyRRj1qchSsThdsic9Pk2Fr 5rPmUg4LAuqpfx9de2dNrYy6daichEwwsvi9xYrBnIPhFddd3MMEWWsHqP0Uo85Gtx FyRuYMXIunBGg9zNZ0ieFsZKRuUETLvEeD7nCNFm8zZ9HzCuUTwiQ0rx+a7IPmiUR3 nZMrHs+z59n4A== From: Josh Poimboeuf To: x86@kernel.org Cc: linux-kernel@vger.kernel.org, live-patching@vger.kernel.org, Peter Zijlstra , Joe Lawrence , Miroslav Benes , Petr Mladek , Song Liu , Luis Chamberlain , Petr Pavlu , Daniel Gomez , Sami Tolvanen , linux-modules@vger.kernel.org Subject: [PATCH 05/14] objtool/klp: Add .klp.symid for sympos disambiguation Date: Sun, 2 Aug 2026 20:24:27 -0700 Message-ID: <64d50f077b569f47883c015cdb7079edb068efe8.1785727106.git.jpoimboe@kernel.org> X-Mailer: git-send-email 2.54.0 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Livepatch identifies a duplicate-named symbol by its position (sympos) among same-named kallsyms entries, which for vmlinux are counted in ascending address order in the final linked kernel. That order can't be reliably derived from vmlinux.o: the final link reorders sub-sections (.text.unlikely*, .data..*, etc). Bridge the gap with a new .klp.symid section which can be used to correlate symbols between vmlinux.o and vmlinux so that klp-diff can reliably determine the sympos. The table can't survive --gc-sections: keeping it alive would keep every duplicate-named symbol's section alive, so the reference kernel would stop matching the one which ships. klp-build rejects CONFIG_LD_DEAD_CODE_DATA_ELIMINATION instead. Nothing is lost today: x86_64 is the only HAVE_KLP_BUILD arch and doesn't select HAVE_LD_DEAD_CODE_DATA_ELIMINATION, arm64 and s390 have never selected it either, and on powerpc, it's still EXPERIMENTAL and disabled by every distro kernel. This is the build-time half of reliable vmlinux sympos computation; "objtool klp diff" will consume the table in a subsequent commit. Signed-off-by: Josh Poimboeuf --- include/asm-generic/vmlinux.lds.h | 10 +- scripts/Makefile.vmlinux_o | 3 + scripts/livepatch/klp-build | 5 + scripts/mod/modpost.c | 1 + tools/objtool/Build | 1 + tools/objtool/builtin-check.c | 7 ++ tools/objtool/check.c | 7 ++ tools/objtool/include/objtool/builtin.h | 1 + tools/objtool/include/objtool/klp.h | 15 +++ tools/objtool/klp-symid.c | 117 ++++++++++++++++++++++++ 10 files changed, 166 insertions(+), 1 deletion(-) create mode 100644 tools/objtool/klp-symid.c diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index 5659f4b5a125..ee9c5d354a85 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -839,12 +839,20 @@ .stab.index 0 : { *(.stab.index) } \ .stab.indexstr 0 : { *(.stab.indexstr) } +#ifdef CONFIG_KLP_BUILD +#define KLP_SYMID \ + .klp.symid 0 : { *(.klp.symid) } +#else +#define KLP_SYMID +#endif + /* Required sections not related to debugging. */ #define ELF_DETAILS \ .comment 0 : { *(.comment) } \ .symtab 0 : { *(.symtab) } \ .strtab 0 : { *(.strtab) } \ - .shstrtab 0 : { *(.shstrtab) } + .shstrtab 0 : { *(.shstrtab) } \ + KLP_SYMID #define MODINFO \ .modinfo : { *(.modinfo) . = ALIGN(8); } diff --git a/scripts/Makefile.vmlinux_o b/scripts/Makefile.vmlinux_o index 527352c222ff..24a3a4fd271c 100644 --- a/scripts/Makefile.vmlinux_o +++ b/scripts/Makefile.vmlinux_o @@ -47,6 +47,9 @@ endif vmlinux-objtool-args-$(CONFIG_NOINSTR_VALIDATION) += --noinstr \ $(if $(or $(CONFIG_MITIGATION_UNRET_ENTRY),$(CONFIG_MITIGATION_SRSO)), --unret) +# Only used for builds initiated by klp-build +vmlinux-objtool-args-$(if $(KLP_SYMIDS),y) += --klp-symids + objtool-args = $(vmlinux-objtool-args-y) --link # Link of vmlinux.o used for section mismatch analysis diff --git a/scripts/livepatch/klp-build b/scripts/livepatch/klp-build index a8c103ce7763..f94e324ff53c 100755 --- a/scripts/livepatch/klp-build +++ b/scripts/livepatch/klp-build @@ -271,6 +271,9 @@ validate_config() { [[ -v CONFIG_GCC_PLUGIN_RANDSTRUCT ]] && \ die "kernel option 'CONFIG_GCC_PLUGIN_RANDSTRUCT' not supported" + [[ -v CONFIG_LD_DEAD_CODE_DATA_ELIMINATION ]] && \ + die "kernel option 'CONFIG_LD_DEAD_CODE_DATA_ELIMINATION' not supported" + [[ -v CONFIG_AS_IS_LLVM ]] && \ [[ "$CONFIG_AS_VERSION" -lt 200000 ]] && \ die "Clang assembler version < 20 not supported" @@ -555,6 +558,8 @@ build_kernel() { # cmd+=("KBUILD_MODPOST_WARN=1") + cmd+=("KLP_SYMIDS=1") + if [[ -v VERBOSE ]]; then cmd+=("V=1") else diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index a7b72a81d248..027944fe35b4 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -767,6 +767,7 @@ static const char *const section_white_list[] = ".llvm.call-graph-profile", /* call graph */ "__llvm_covfun", "__llvm_covmap", + ".klp.symid", /* objtool --klp-symids */ NULL }; diff --git a/tools/objtool/Build b/tools/objtool/Build index 93a37b0dfd31..506f89bed808 100644 --- a/tools/objtool/Build +++ b/tools/objtool/Build @@ -6,6 +6,7 @@ objtool-y += check.o objtool-y += special.o objtool-y += builtin-check.o objtool-y += elf.o +objtool-y += klp-symid.o objtool-y += objtool.o objtool-$(BUILD_DISAS) += disas.o diff --git a/tools/objtool/builtin-check.c b/tools/objtool/builtin-check.c index 118c3de2f293..75b11dc85010 100644 --- a/tools/objtool/builtin-check.c +++ b/tools/objtool/builtin-check.c @@ -76,6 +76,7 @@ static const struct option check_options[] = { OPT_STRING_OPTARG('d', "disas", &opts.disas, "function-pattern", "disassemble functions", "*"), OPT_CALLBACK_OPTARG('h', "hacks", NULL, NULL, "jump_label,noinstr,skylake", "patch toolchain bugs/limitations", parse_hacks), OPT_BOOLEAN('i', "ibt", &opts.ibt, "validate and annotate IBT"), + OPT_BOOLEAN(0, "klp-symids", &opts.klp_symids, "generate .klp.symids for duplicate symbol disambiguation"), OPT_BOOLEAN('m', "mcount", &opts.mcount, "annotate mcount/fentry calls for ftrace"), OPT_BOOLEAN(0, "noabs", &opts.noabs, "reject absolute references in allocatable sections"), OPT_BOOLEAN('n', "noinstr", &opts.noinstr, "validate noinstr rules"), @@ -174,10 +175,16 @@ static bool opts_valid(void) return false; } + if (opts.klp_symids && !opts.link) { + ERROR("--klp-symids requires --link"); + return false; + } + if (opts.disas || opts.hack_jump_label || opts.hack_noinstr || opts.ibt || + opts.klp_symids || opts.mcount || opts.noabs || opts.noinstr || diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 10b18cf9c360..4e6366663be1 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -4922,6 +4923,12 @@ int check(struct objtool_file *file) goto out; } + if (opts.klp_symids) { + ret = klp_create_symid_sections(file); + if (ret) + goto out; + } + if (opts.noabs) warnings += check_abs_references(file); diff --git a/tools/objtool/include/objtool/builtin.h b/tools/objtool/include/objtool/builtin.h index e844e9c82b7b..349690bb1c50 100644 --- a/tools/objtool/include/objtool/builtin.h +++ b/tools/objtool/include/objtool/builtin.h @@ -16,6 +16,7 @@ struct opts { bool hack_noinstr; bool hack_skylake; bool ibt; + bool klp_symids; bool mcount; bool noabs; bool noinstr; diff --git a/tools/objtool/include/objtool/klp.h b/tools/objtool/include/objtool/klp.h index aab6db42052d..4d3c3bd462aa 100644 --- a/tools/objtool/include/objtool/klp.h +++ b/tools/objtool/include/objtool/klp.h @@ -31,6 +31,21 @@ struct klp_reloc { u32 type; }; +/* + * .klp.symid is used to correlate symbols between vmlinux.o and vmlinux, for + * calculating sympos to disambiguate duplicately-named symbols. + */ +#define KLP_SYMID_SEC ".klp.symid" + +struct klp_symid { + u64 id; + u64 addr; +}; + +struct objtool_file; + +int klp_create_symid_sections(struct objtool_file *file); + int cmd_klp_checksum(int argc, const char **argv); int cmd_klp_diff(int argc, const char **argv); int cmd_klp_post_link(int argc, const char **argv); diff --git a/tools/objtool/klp-symid.c b/tools/objtool/klp-symid.c new file mode 100644 index 000000000000..cf188cdfa607 --- /dev/null +++ b/tools/objtool/klp-symid.c @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Emit the .klp.symid table which allows "objtool klp diff" to reliably + * disambiguate duplicate-named local symbols in vmlinux. + * + * Livepatch identifies a duplicate-named symbol by its position (sympos) + * among the same-named kallsyms entries, counted in ascending address order + * in the final linked vmlinux. That order can't be derived from vmlinux.o + * alone: the final link reorders sub-sections (.text.unlikely*, .data..*, + * etc). + * + * Bridge the gap with a table which survives the final link: a single + * non-alloc section containing an array of { id, addr } entries, where + * 'id' is a unique counter identifier and 'addr' has a relocation to the + * symbol. The linker copies 'id' verbatim and resolves 'addr' to the symbol's + * final address. + * + * The table is only emitted for vmlinux.o, and only when klp-build asks for it + * with KLP_SYMIDS=1, which adds --klp-symids to the vmlinux.o objtool run. + * + * It can't survive --gc-sections, which sweeps the whole section; klp-build + * rejects CONFIG_LD_DEAD_CODE_DATA_ELIMINATION. + */ +#include + +#include +#include +#include +#include + +static const char * const discarded_secs[] = { + ".discard", + ".modinfo", + "__tracepoint_check", +}; + +static bool discarded_sec(struct section *sec) +{ + if (!(sec->sh.sh_flags & SHF_ALLOC)) + return true; + + for (int i = 0; i < ARRAY_SIZE(discarded_secs); i++) + if (strstarts(sec->name, discarded_secs[i])) + return true; + + return false; +} + +static bool symid_needed(struct elf *elf, struct symbol *sym) +{ + struct symbol *s; + + if (!is_local_sym(sym) || is_undef_sym(sym)) + return false; + + if (!is_func_sym(sym) && !is_object_sym(sym)) + return false; + + if (is_prefix_func(sym)) + return false; + + if (discarded_sec(sym->sec)) + return false; + + for_each_sym_by_name(elf, sym->name, s) { + if (s == sym || is_sec_sym(s) || is_file_sym(s) || is_undef_sym(s)) + continue; + return true; + } + + return false; +} + +int klp_create_symid_sections(struct objtool_file *file) +{ + struct elf *elf = file->elf; + struct klp_symid *symids; + struct section *sec; + struct symbol *sym; + u64 nr = 0, i = 0; + + if (!str_ends_with(objname, "vmlinux.o")) + return 0; + + for_each_sym(elf, sym) + if (symid_needed(elf, sym)) + nr++; + + if (!nr) + return 0; + + sec = elf_create_section(elf, KLP_SYMID_SEC, 0, sizeof(struct klp_symid), + SHT_PROGBITS, 8, 0); + if (!sec) + return -1; + + symids = elf_add_data(elf, sec, NULL, nr * sizeof(struct klp_symid)); + if (!symids) + return -1; + + for_each_sym(elf, sym) { + if (!symid_needed(elf, sym)) + continue; + + symids[i].id = bswap_if_needed(elf, i); + + if (!elf_create_reloc(elf, sec, + i * sizeof(struct klp_symid) + + offsetof(struct klp_symid, addr), + sym, 0, R_ABS64)) + return -1; + + i++; + } + + return 0; +} -- 2.54.0