objtool/klp: Add --debug-checksum=<funcs> to show per-instruction checksums

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>
This commit is contained in:
Josh Poimboeuf 2025-09-17 09:03:58 -07:00
parent 0d83da43b1
commit a3493b3338
6 changed files with 70 additions and 0 deletions

View 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 ||

View 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;

View File

@ -32,6 +32,7 @@ struct opts {
/* options: */
bool backtrace;
bool backup;
const char *debug_checksum;
bool dryrun;
bool link;
bool mnop;

View 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)

View 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;

View 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 */