]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
objtool/klp: Add --debug-checksum=<funcs> to show per-instruction checksums
authorJosh Poimboeuf <jpoimboe@kernel.org>
Wed, 17 Sep 2025 16:03:58 +0000 (09:03 -0700)
committerJosh Poimboeuf <jpoimboe@kernel.org>
Tue, 14 Oct 2025 21:50:18 +0000 (14:50 -0700)
Add a --debug-checksum=<funcs> option to the check subcommand to print
the calculated checksum of each instruction in the given functions.

This is useful for determining where two versions of a function begin to
diverge.

Acked-by: Petr Mladek <pmladek@suse.com>
Tested-by: Joe Lawrence <joe.lawrence@redhat.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
tools/objtool/builtin-check.c
tools/objtool/check.c
tools/objtool/include/objtool/builtin.h
tools/objtool/include/objtool/checksum.h
tools/objtool/include/objtool/elf.h
tools/objtool/include/objtool/warn.h

index 14daa1357c8b98f46cf7b5473e230c3cf1f5f34f..7b6fd60b022bc4edf9b68b0f8326dda14fe4cfa5 100644 (file)
@@ -94,6 +94,7 @@ static const struct option check_options[] = {
        OPT_GROUP("Options:"),
        OPT_BOOLEAN(0,           "backtrace", &opts.backtrace, "unwind on error"),
        OPT_BOOLEAN(0,           "backup", &opts.backup, "create backup (.orig) file on warning/error"),
+       OPT_STRING(0,            "debug-checksum", &opts.debug_checksum,  "funcs", "enable checksum debug output"),
        OPT_BOOLEAN(0,           "dry-run", &opts.dryrun, "don't write modifications"),
        OPT_BOOLEAN(0,           "link", &opts.link, "object is a linked object"),
        OPT_BOOLEAN(0,           "module", &opts.module, "object is part of a kernel module"),
@@ -168,6 +169,11 @@ static bool opts_valid(void)
        }
 #endif
 
+       if (opts.debug_checksum && !opts.checksum) {
+               ERROR("--debug-checksum requires --checksum");
+               return false;
+       }
+
        if (opts.checksum               ||
            opts.hack_jump_label        ||
            opts.hack_noinstr           ||
index f5adbd23c42d7529d9a535d645c6bc49f66cc52e..0f5278127f37e0ef048acfe716cebe8ebef7fd3d 100644 (file)
@@ -3580,6 +3580,44 @@ static bool skip_alt_group(struct instruction *insn)
        return alt_insn->type == INSN_CLAC || alt_insn->type == INSN_STAC;
 }
 
+static int checksum_debug_init(struct objtool_file *file)
+{
+       char *dup, *s;
+
+       if (!opts.debug_checksum)
+               return 0;
+
+       dup = strdup(opts.debug_checksum);
+       if (!dup) {
+               ERROR_GLIBC("strdup");
+               return -1;
+       }
+
+       s = dup;
+       while (*s) {
+               struct symbol *func;
+               char *comma;
+
+               comma = strchr(s, ',');
+               if (comma)
+                       *comma = '\0';
+
+               func = find_symbol_by_name(file->elf, s);
+               if (!func || !is_func_sym(func))
+                       WARN("--debug-checksum: can't find '%s'", s);
+               else
+                       func->debug_checksum = 1;
+
+               if (!comma)
+                       break;
+
+               s = comma + 1;
+       }
+
+       free(dup);
+       return 0;
+}
+
 static void checksum_update_insn(struct objtool_file *file, struct symbol *func,
                                 struct instruction *insn)
 {
@@ -4818,6 +4856,10 @@ 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;
index 338bdab6b9ad8cf2b409e233b2a2310384a35172..cee9fc0318777c71c3bdc90defe580599c306dd5 100644 (file)
@@ -32,6 +32,7 @@ struct opts {
        /* options: */
        bool backtrace;
        bool backup;
+       const char *debug_checksum;
        bool dryrun;
        bool link;
        bool mnop;
index 927ca74b5c39e5463fd63ef79b3fd62bd84d38bd..7fe21608722ac659f420e30a4156df86a08e3582 100644 (file)
@@ -19,6 +19,7 @@ static inline void checksum_update(struct symbol *func,
                                   const void *data, size_t size)
 {
        XXH3_64bits_update(func->csum.state, data, size);
+       dbg_checksum(func, insn, XXH3_64bits_digest(func->csum.state));
 }
 
 static inline void checksum_finish(struct symbol *func)
index bc7d8a6167f8fd38b6a45f56601de9f5d336cf3a..a1f1762f89c4901cd5a502f16c09469cdf7a19cb 100644 (file)
@@ -82,6 +82,7 @@ struct symbol {
        u8 nocfi             : 1;
        u8 cold              : 1;
        u8 prefix            : 1;
+       u8 debug_checksum    : 1;
        struct list_head pv_target;
        struct reloc *relocs;
        struct section *group_sec;
index cb8fe846d9ddd8531714f03ffe102f1c341bcc89..29173a1368d79cc9f2d5896fc0ed9816c6304b19 100644 (file)
@@ -102,4 +102,23 @@ static inline char *offstr(struct section *sec, unsigned long offset)
 #define ERROR_FUNC(sec, offset, format, ...) __WARN_FUNC(ERROR_STR, sec, offset, format, ##__VA_ARGS__)
 #define ERROR_INSN(insn, format, ...) WARN_FUNC(insn->sec, insn->offset, format, ##__VA_ARGS__)
 
+
+#define __dbg(format, ...)                                             \
+       fprintf(stderr,                                                 \
+               "DEBUG: %s%s" format "\n",                              \
+               objname ?: "",                                          \
+               objname ? ": " : "",                                    \
+               ##__VA_ARGS__)
+
+#define dbg_checksum(func, insn, checksum)                             \
+({                                                                     \
+       if (unlikely(insn->sym && insn->sym->pfunc &&                   \
+                    insn->sym->pfunc->debug_checksum)) {               \
+               char *insn_off = offstr(insn->sec, insn->offset);       \
+               __dbg("checksum: %s %s %016lx",                         \
+                     func->name, insn_off, checksum);                  \
+               free(insn_off);                                         \
+       }                                                               \
+})
+
 #endif /* _WARN_H */