From nobody Sat Jun 13 22:17:07 2026 Received: from galois.linutronix.de (Galois.linutronix.de [193.142.43.55]) (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 D8556426D26; Tue, 5 May 2026 10:56:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=193.142.43.55 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777978573; cv=none; b=hgwd/L3/Il/+o+0oibrA59cXdwz55Jc79HeepvWIlCDdkzwSUV8xZ0tqcLKE2vKK4QjKSeQXudrn6m02j9RDFWgg5f52HBawvk9l12Vaf5IEUHP/qLoTTEDPhHQlAYTPHQ647+2mB5SWBPhKqIxP6yPqw7iZbILotd+ufAQxawE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777978573; c=relaxed/simple; bh=xsqUy1oWcXNtKVQ4sNXUcTLgUxW1xi27hsyh7W4LTRo=; h=Date:From:To:Subject:Cc:MIME-Version:Message-ID:Content-Type; b=Uxredfy5VpK9TQjYr2ohrVPT/4WFE8jhAWTLv4/tXKjqqMbONVcSE8SwGmIQKWPuwijQuWNFH6URZaB3JD44u0X10NDmJbXLFR1t4JcUTqRnluUck+OPZscZcYUeshh/tNCUcY8Id1WRDoXGZFN24hNguZ4rd+greYt4cQ0fa2Y= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linutronix.de; spf=pass smtp.mailfrom=linutronix.de; dkim=pass (2048-bit key) header.d=linutronix.de header.i=@linutronix.de header.b=O0B7nkYx; dkim=permerror (0-bit key) header.d=linutronix.de header.i=@linutronix.de header.b=PMaXto+3; arc=none smtp.client-ip=193.142.43.55 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linutronix.de Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linutronix.de Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=linutronix.de header.i=@linutronix.de header.b="O0B7nkYx"; dkim=permerror (0-bit key) header.d=linutronix.de header.i=@linutronix.de header.b="PMaXto+3" Date: Tue, 05 May 2026 10:56:05 -0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linutronix.de; s=2020; t=1777978567; h=from:from:sender:sender:reply-to:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=zvfGWmAwtT+I3L3abd8MiSYJ89zCKxtnfWkNrd51eTQ=; b=O0B7nkYxwqzPUqv6myI43orrRBYxkJnMlvoyswEGqBJHZUb0BjDLFZulG87z5minEQFlVW 82XzfGXA5Mgzdw7D8Zaig+xSut4j6jsrJEF4h/kLW1VMeDATD2vy44Ih5UXpazA7DKC4cl LebKcZeAICeAX6/MbVbj9WbDvP0D92z8OCTI8V9ocXAEPsEWbNws7hTEIUpD90N+pmEFZK lAo8BkwZmeIs38IBrH/b4oAG02Tab97PkgvA1HwQ9viSFzz5A/h3nipGE/4HOmV7cCF+y7 e56QQn5+uItQEmwKF2Hx3TRSB+9cD7orNUeVUpoIgBYbTUYFpGmRg79qcD9JIQ== DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; d=linutronix.de; s=2020e; t=1777978567; h=from:from:sender:sender:reply-to:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=zvfGWmAwtT+I3L3abd8MiSYJ89zCKxtnfWkNrd51eTQ=; b=PMaXto+3HLvWngmYU/HF/dU9kTn1b9+PSJcE7RV/0JLCsY07aEqLBeqKO6AeHNHDKPjHzO wR2jel8zd2kggaAg== From: "tip-bot2 for Josh Poimboeuf" Sender: tip-bot2@linutronix.de Reply-to: linux-kernel@vger.kernel.org To: linux-tip-commits@vger.kernel.org Subject: [tip: objtool/core] objtool/klp: Add "objtool klp checksum" subcommand Cc: Song Liu , Josh Poimboeuf , x86@kernel.org, linux-kernel@vger.kernel.org Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-ID: <177797856581.424702.11281772510878253254.tip-bot2@tip-bot2> Robot-ID: Robot-Unsubscribe: Contact to get blacklisted from these emails Precedence: bulk Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable The following commit has been merged into the objtool/core branch of tip: Commit-ID: e10764614ad634071d3bc8cfbf8bce43285d458d Gitweb: https://git.kernel.org/tip/e10764614ad634071d3bc8cfbf8bce432= 85d458d Author: Josh Poimboeuf AuthorDate: Fri, 03 Apr 2026 13:08:32 -07:00 Committer: Josh Poimboeuf CommitterDate: Mon, 04 May 2026 21:16:05 -07:00 objtool/klp: Add "objtool klp checksum" subcommand Move the checksum functionality out of the main objtool command into a new "objtool klp checksum" subcommand. This has the benefit of making the code (and the patch generation process itself) more modular. For bisectability, both "objtool --checksum" and "objtool klp checksum" work for now. The former will be removed after klp-build has been converted to use the new subcommand. Acked-by: Song Liu Signed-off-by: Josh Poimboeuf --- tools/objtool/Build | 2 +- tools/objtool/builtin-klp.c | 1 +- tools/objtool/check.c | 205 +------------------ tools/objtool/include/objtool/check.h | 6 +- tools/objtool/include/objtool/checksum.h | 4 +- tools/objtool/include/objtool/klp.h | 1 +- tools/objtool/klp-checksum.c | 253 ++++++++++++++++++++++- tools/objtool/klp-diff.c | 2 +- 8 files changed, 269 insertions(+), 205 deletions(-) create mode 100644 tools/objtool/klp-checksum.c diff --git a/tools/objtool/Build b/tools/objtool/Build index 600da05..93a37b0 100644 --- a/tools/objtool/Build +++ b/tools/objtool/Build @@ -12,7 +12,7 @@ objtool-$(BUILD_DISAS) +=3D disas.o objtool-$(BUILD_DISAS) +=3D trace.o =20 objtool-$(BUILD_ORC) +=3D orc_gen.o orc_dump.o -objtool-$(BUILD_KLP) +=3D builtin-klp.o klp-diff.o klp-post-link.o +objtool-$(BUILD_KLP) +=3D builtin-klp.o klp-checksum.o klp-diff.o klp-post= -link.o =20 objtool-y +=3D libstring.o objtool-y +=3D libctype.o diff --git a/tools/objtool/builtin-klp.c b/tools/objtool/builtin-klp.c index 56d5a5b..58c3b9b 100644 --- a/tools/objtool/builtin-klp.c +++ b/tools/objtool/builtin-klp.c @@ -13,6 +13,7 @@ struct subcmd { }; =20 static struct subcmd subcmds[] =3D { + { "checksum", "Generate per-function checksums", cmd_klp_checksum, }, { "diff", "Generate binary diff of two object files", cmd_klp_diff, }, { "post-link", "Finalize klp symbols/relocs after module linking", cmd_k= lp_post_link, }, }; diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 49171dd..3e5d335 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -64,8 +64,8 @@ struct instruction *next_insn_same_sec(struct objtool_fil= e *file, return insn; } =20 -static struct instruction *next_insn_same_func(struct objtool_file *file, - struct instruction *insn) +struct instruction *next_insn_same_func(struct objtool_file *file, + struct instruction *insn) { struct instruction *next =3D next_insn_same_sec(file, insn); struct symbol *func =3D insn_func(insn); @@ -113,10 +113,6 @@ static struct instruction *prev_insn_same_sym(struct o= bjtool_file *file, for_each_sec(file->elf, __sec) \ sec_for_each_insn(file, __sec, insn) =20 -#define func_for_each_insn(file, func, insn) \ - for (insn =3D find_insn(file, func->sec, func->offset); \ - insn; \ - insn =3D next_insn_same_func(file, insn)) =20 #define sym_for_each_insn(file, sym, insn) \ for (insn =3D find_insn(file, sym->sec, sym->offset); \ @@ -1023,56 +1019,6 @@ static int create_direct_call_sections(struct objtoo= l_file *file) return 0; } =20 -#ifdef BUILD_KLP -static int create_sym_checksum_section(struct objtool_file *file) -{ - struct section *sec; - struct symbol *sym; - unsigned int idx =3D 0; - struct sym_checksum *checksum; - size_t entsize =3D sizeof(struct sym_checksum); - - sec =3D find_section_by_name(file->elf, ".discard.sym_checksum"); - if (sec) { - if (!opts.dryrun) - WARN("file already has .discard.sym_checksum section, skipping"); - - return 0; - } - - for_each_sym(file->elf, sym) - if (sym->csum.checksum) - idx++; - - sec =3D elf_create_section_pair(file->elf, ".discard.sym_checksum", entsi= ze, - idx, idx); - if (!sec) - return -1; - - idx =3D 0; - for_each_sym(file->elf, sym) { - if (!sym->csum.checksum) - continue; - - if (!elf_init_reloc(file->elf, sec->rsec, idx, idx * entsize, - sym, 0, R_TEXT64)) - return -1; - - checksum =3D (struct sym_checksum *)sec->data->d_buf + idx; - checksum->addr =3D 0; /* reloc */ - checksum->checksum =3D sym->csum.checksum; - - mark_sec_changed(file->elf, sec, true); - - idx++; - } - - return 0; -} -#else -static int create_sym_checksum_section(struct objtool_file *file) { return= -EINVAL; } -#endif - /* * Warnings shouldn't be reported for ignored functions. */ @@ -3672,153 +3618,6 @@ static bool skip_alt_group(struct instruction *insn) return alt_insn->type =3D=3D INSN_CLAC || alt_insn->type =3D=3D INSN_STAC; } =20 -#ifdef BUILD_KLP -static int checksum_debug_init(struct objtool_file *file) -{ - char *dup, *s; - - if (!opts.debug_checksum) - return 0; - - dup =3D strdup(opts.debug_checksum); - if (!dup) { - ERROR_GLIBC("strdup"); - return -1; - } - - s =3D dup; - while (*s) { - bool found =3D false; - struct symbol *sym; - char *comma; - - comma =3D strchr(s, ','); - if (comma) - *comma =3D '\0'; - - for_each_sym_by_name(file->elf, s, sym) { - if (!is_func_sym(sym)) - continue; - sym->debug_checksum =3D 1; - found =3D true; - } - - if (!found) - WARN("--debug-checksum: can't find '%s'", s); - - if (!comma) - break; - - s =3D comma + 1; - } - - free(dup); - return 0; -} - -static void checksum_update_insn(struct objtool_file *file, struct symbol = *func, - struct instruction *insn) -{ - struct reloc *reloc =3D insn_reloc(file, insn); - struct alternative *alt; - unsigned long offset; - struct symbol *sym; - static bool in_alt; - - if (insn->fake) - return; - - checksum_update(func, insn, insn->sec->data->d_buf + insn->offset, insn->= len); - - if (!reloc) { - struct symbol *call_dest =3D insn_call_dest(insn); - - if (call_dest) - checksum_update(func, insn, call_dest->demangled_name, - strlen(call_dest->demangled_name)); - goto alts; - } - - sym =3D reloc->sym; - offset =3D arch_insn_adjusted_addend(insn, reloc); - - if (is_string_sec(sym->sec)) { - char *str; - - str =3D sym->sec->data->d_buf + sym->offset + offset; - checksum_update(func, insn, str, strlen(str)); - goto alts; - } - - if (is_sec_sym(sym)) { - sym =3D find_symbol_containing(reloc->sym->sec, offset); - if (!sym) - goto alts; - - offset -=3D sym->offset; - } - - checksum_update(func, insn, sym->demangled_name, strlen(sym->demangled_na= me)); - checksum_update(func, insn, &offset, sizeof(offset)); - -alts: - for (alt =3D insn->alts; alt; alt =3D alt->next) { - struct alt_group *alt_group =3D alt->insn->alt_group; - - /* Prevent __ex_table recursion, e.g. LOAD_SEGMENT() */ - if (in_alt) - break; - in_alt =3D true; - - 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->feat= ure)); - - for (alt_insn =3D alt->insn; alt_insn; alt_insn =3D next_insn_same_sec(= file, alt_insn)) { - checksum_update_insn(file, func, alt_insn); - if (!alt_group->last_insn || alt_insn =3D=3D alt_group->last_insn) - break; - } - } else { - checksum_update_insn(file, func, alt->insn); - } - - in_alt =3D false; - } -} - -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) { - /* - * Skip cold subfunctions and aliases: they share the - * parent's checksum via func_for_each_insn() which - * follows func->cfunc into the cold subfunction. - */ - if (!is_func_sym(func) || is_cold_func(func) || - is_alias_sym(func) || !func->len) - 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 *fu= nc, diff --git a/tools/objtool/include/objtool/check.h b/tools/objtool/include/= objtool/check.h index 6489e52..eea6472 100644 --- a/tools/objtool/include/objtool/check.h +++ b/tools/objtool/include/objtool/check.h @@ -144,6 +144,12 @@ struct instruction *find_insn(struct objtool_file *fil= e, struct section *sec, unsigned long offset); =20 struct instruction *next_insn_same_sec(struct objtool_file *file, struct i= nstruction *insn); +struct instruction *next_insn_same_func(struct objtool_file *file, struct = instruction *insn); + +#define func_for_each_insn(file, func, insn) \ + for (insn =3D find_insn(file, func->sec, func->offset); \ + insn; \ + insn =3D next_insn_same_func(file, insn)) =20 #define sec_for_each_insn(file, _sec, insn) \ for (insn =3D find_insn(file, _sec, 0); \ diff --git a/tools/objtool/include/objtool/checksum.h b/tools/objtool/inclu= de/objtool/checksum.h index 3f25df9..be4eb7d 100644 --- a/tools/objtool/include/objtool/checksum.h +++ b/tools/objtool/include/objtool/checksum.h @@ -31,9 +31,13 @@ static inline void checksum_finish(struct symbol *func) } } =20 +int calculate_checksums(struct objtool_file *file); +int create_sym_checksum_section(struct objtool_file *file); + #else /* !BUILD_KLP */ =20 static inline int calculate_checksums(struct objtool_file *file) { return = -ENOSYS; } +static inline int create_sym_checksum_section(struct objtool_file *file) {= return -EINVAL; } =20 #endif /* !BUILD_KLP */ =20 diff --git a/tools/objtool/include/objtool/klp.h b/tools/objtool/include/ob= jtool/klp.h index e32e5e8..6f60cf0 100644 --- a/tools/objtool/include/objtool/klp.h +++ b/tools/objtool/include/objtool/klp.h @@ -29,6 +29,7 @@ struct klp_reloc { u32 type; }; =20 +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); =20 diff --git a/tools/objtool/klp-checksum.c b/tools/objtool/klp-checksum.c new file mode 100644 index 0000000..e4a910f --- /dev/null +++ b/tools/objtool/klp-checksum.c @@ -0,0 +1,253 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +static int checksum_debug_init(struct objtool_file *file) +{ + char *dup, *s; + + if (!opts.debug_checksum) + return 0; + + dup =3D strdup(opts.debug_checksum); + if (!dup) { + ERROR_GLIBC("strdup"); + return -1; + } + + s =3D dup; + while (*s) { + bool found =3D false; + struct symbol *sym; + char *comma; + + comma =3D strchr(s, ','); + if (comma) + *comma =3D '\0'; + + for_each_sym_by_name(file->elf, s, sym) { + if (!is_func_sym(sym)) + continue; + sym->debug_checksum =3D 1; + found =3D true; + } + + if (!found) + WARN("--debug-checksum: can't find '%s'", s); + + if (!comma) + break; + + s =3D comma + 1; + } + + free(dup); + return 0; +} + +static void checksum_update_insn(struct objtool_file *file, struct symbol = *func, + struct instruction *insn) +{ + struct reloc *reloc =3D insn_reloc(file, insn); + struct alternative *alt; + unsigned long offset; + struct symbol *sym; + static bool in_alt; + + if (insn->fake) + return; + + checksum_update(func, insn, insn->sec->data->d_buf + insn->offset, insn->= len); + + if (!reloc) { + struct symbol *call_dest =3D insn_call_dest(insn); + + if (call_dest) + checksum_update(func, insn, call_dest->demangled_name, + strlen(call_dest->demangled_name)); + goto alts; + } + + sym =3D reloc->sym; + offset =3D arch_insn_adjusted_addend(insn, reloc); + + if (is_string_sec(sym->sec)) { + char *str; + + str =3D sym->sec->data->d_buf + sym->offset + offset; + checksum_update(func, insn, str, strlen(str)); + goto alts; + } + + if (is_sec_sym(sym)) { + sym =3D find_symbol_containing(reloc->sym->sec, offset); + if (!sym) + goto alts; + + offset -=3D sym->offset; + } + + checksum_update(func, insn, sym->demangled_name, strlen(sym->demangled_na= me)); + checksum_update(func, insn, &offset, sizeof(offset)); + +alts: + for (alt =3D insn->alts; alt; alt =3D alt->next) { + struct alt_group *alt_group =3D alt->insn->alt_group; + + /* Prevent __ex_table recursion, e.g. LOAD_SEGMENT() */ + if (in_alt) + break; + in_alt =3D true; + + 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->feat= ure)); + + for (alt_insn =3D alt->insn; alt_insn; alt_insn =3D next_insn_same_sec(= file, alt_insn)) { + checksum_update_insn(file, func, alt_insn); + if (!alt_group->last_insn || alt_insn =3D=3D alt_group->last_insn) + break; + } + } else { + checksum_update_insn(file, func, alt->insn); + } + + in_alt =3D false; + } +} + +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) { + /* + * Skip cold subfunctions and aliases: they share the + * parent's checksum via func_for_each_insn() which + * follows func->cfunc into the cold subfunction. + */ + if (!is_func_sym(func) || is_cold_func(func) || + is_alias_sym(func) || !func->len) + continue; + + checksum_init(func); + + func_for_each_insn(file, func, insn) + checksum_update_insn(file, func, insn); + + checksum_finish(func); + } + return 0; +} + +int create_sym_checksum_section(struct objtool_file *file) +{ + struct section *sec; + struct symbol *sym; + unsigned int idx =3D 0; + struct sym_checksum *checksum; + size_t entsize =3D sizeof(struct sym_checksum); + + sec =3D find_section_by_name(file->elf, ".discard.sym_checksum"); + if (sec) { + if (!opts.dryrun) + WARN("file already has .discard.sym_checksum section, skipping"); + + return 0; + } + + for_each_sym(file->elf, sym) + if (sym->csum.checksum) + idx++; + + sec =3D elf_create_section_pair(file->elf, ".discard.sym_checksum", entsi= ze, + idx, idx); + if (!sec) + return -1; + + idx =3D 0; + for_each_sym(file->elf, sym) { + if (!sym->csum.checksum) + continue; + + if (!elf_init_reloc(file->elf, sec->rsec, idx, idx * entsize, + sym, 0, R_TEXT64)) + return -1; + + checksum =3D (struct sym_checksum *)sec->data->d_buf + idx; + checksum->addr =3D 0; /* reloc */ + checksum->checksum =3D sym->csum.checksum; + + mark_sec_changed(file->elf, sec, true); + + idx++; + } + + return 0; +} + +static const char * const klp_checksum_usage[] =3D { + "objtool klp checksum [] file.o", + NULL, +}; + +int cmd_klp_checksum(int argc, const char **argv) +{ + struct objtool_file *file; + int ret; + + const struct option options[] =3D { + OPT_STRING(0, "debug-checksum", &opts.debug_checksum, "funcs", "enable c= hecksum debug output"), + OPT_BOOLEAN(0, "dry-run", &opts.dryrun, "don't write modifications"), + OPT_END(), + }; + + argc =3D parse_options(argc, argv, options, klp_checksum_usage, 0); + if (argc !=3D 1) + usage_with_options(klp_checksum_usage, options); + + opts.checksum =3D true; + + objname =3D argv[0]; + + file =3D objtool_open_read(objname); + if (!file) + return 1; + + ret =3D decode_file(file); + if (ret) + goto out; + + ret =3D calculate_checksums(file); + if (ret) + goto out; + + ret =3D create_sym_checksum_section(file); + +out: + free_insns(file); + + if (ret) + return ret; + + if (!opts.dryrun && file->elf->changed && elf_write(file->elf)) + return 1; + + return elf_close(file->elf); +} diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c index 8728dda..95359ad 100644 --- a/tools/objtool/klp-diff.c +++ b/tools/objtool/klp-diff.c @@ -166,7 +166,7 @@ static int read_sym_checksums(struct elf *elf) =20 sec =3D find_section_by_name(elf, ".discard.sym_checksum"); if (!sec) { - ERROR("'%s' missing .discard.sym_checksum section, file not processed by= 'objtool --checksum'?", + ERROR("'%s' missing .discard.sym_checksum section, file not processed by= 'objtool klp checksum'?", elf->name); return -1; }