mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 09:36:22 +02: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 <song@kernel.org> Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
This commit is contained in:
parent
a5b6612332
commit
e10764614a
|
|
@ -12,7 +12,7 @@ objtool-$(BUILD_DISAS) += disas.o
|
|||
objtool-$(BUILD_DISAS) += trace.o
|
||||
|
||||
objtool-$(BUILD_ORC) += orc_gen.o orc_dump.o
|
||||
objtool-$(BUILD_KLP) += builtin-klp.o klp-diff.o klp-post-link.o
|
||||
objtool-$(BUILD_KLP) += builtin-klp.o klp-checksum.o klp-diff.o klp-post-link.o
|
||||
|
||||
objtool-y += libstring.o
|
||||
objtool-y += libctype.o
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ struct subcmd {
|
|||
};
|
||||
|
||||
static struct subcmd subcmds[] = {
|
||||
{ "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_klp_post_link, },
|
||||
};
|
||||
|
|
|
|||
|
|
@ -64,8 +64,8 @@ struct instruction *next_insn_same_sec(struct objtool_file *file,
|
|||
return insn;
|
||||
}
|
||||
|
||||
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 = next_insn_same_sec(file, insn);
|
||||
struct symbol *func = insn_func(insn);
|
||||
|
|
@ -113,10 +113,6 @@ static struct instruction *prev_insn_same_sym(struct objtool_file *file,
|
|||
for_each_sec(file->elf, __sec) \
|
||||
sec_for_each_insn(file, __sec, insn)
|
||||
|
||||
#define func_for_each_insn(file, func, insn) \
|
||||
for (insn = find_insn(file, func->sec, func->offset); \
|
||||
insn; \
|
||||
insn = next_insn_same_func(file, insn))
|
||||
|
||||
#define sym_for_each_insn(file, sym, insn) \
|
||||
for (insn = find_insn(file, sym->sec, sym->offset); \
|
||||
|
|
@ -1023,56 +1019,6 @@ static int create_direct_call_sections(struct objtool_file *file)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef BUILD_KLP
|
||||
static int create_sym_checksum_section(struct objtool_file *file)
|
||||
{
|
||||
struct section *sec;
|
||||
struct symbol *sym;
|
||||
unsigned int idx = 0;
|
||||
struct sym_checksum *checksum;
|
||||
size_t entsize = sizeof(struct sym_checksum);
|
||||
|
||||
sec = 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 = elf_create_section_pair(file->elf, ".discard.sym_checksum", entsize,
|
||||
idx, idx);
|
||||
if (!sec)
|
||||
return -1;
|
||||
|
||||
idx = 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 = (struct sym_checksum *)sec->data->d_buf + idx;
|
||||
checksum->addr = 0; /* reloc */
|
||||
checksum->checksum = 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 == INSN_CLAC || alt_insn->type == INSN_STAC;
|
||||
}
|
||||
|
||||
#ifdef BUILD_KLP
|
||||
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) {
|
||||
bool found = false;
|
||||
struct symbol *sym;
|
||||
char *comma;
|
||||
|
||||
comma = strchr(s, ',');
|
||||
if (comma)
|
||||
*comma = '\0';
|
||||
|
||||
for_each_sym_by_name(file->elf, s, sym) {
|
||||
if (!is_func_sym(sym))
|
||||
continue;
|
||||
sym->debug_checksum = 1;
|
||||
found = true;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
WARN("--debug-checksum: can't find '%s'", s);
|
||||
|
||||
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)
|
||||
{
|
||||
struct reloc *reloc = 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 = insn_call_dest(insn);
|
||||
|
||||
if (call_dest)
|
||||
checksum_update(func, insn, call_dest->demangled_name,
|
||||
strlen(call_dest->demangled_name));
|
||||
goto alts;
|
||||
}
|
||||
|
||||
sym = reloc->sym;
|
||||
offset = arch_insn_adjusted_addend(insn, reloc);
|
||||
|
||||
if (is_string_sec(sym->sec)) {
|
||||
char *str;
|
||||
|
||||
str = sym->sec->data->d_buf + sym->offset + offset;
|
||||
checksum_update(func, insn, str, strlen(str));
|
||||
goto alts;
|
||||
}
|
||||
|
||||
if (is_sec_sym(sym)) {
|
||||
sym = find_symbol_containing(reloc->sym->sec, offset);
|
||||
if (!sym)
|
||||
goto alts;
|
||||
|
||||
offset -= sym->offset;
|
||||
}
|
||||
|
||||
checksum_update(func, insn, sym->demangled_name, strlen(sym->demangled_name));
|
||||
checksum_update(func, insn, &offset, sizeof(offset));
|
||||
|
||||
alts:
|
||||
for (alt = insn->alts; alt; alt = alt->next) {
|
||||
struct alt_group *alt_group = alt->insn->alt_group;
|
||||
|
||||
/* Prevent __ex_table recursion, e.g. LOAD_SEGMENT() */
|
||||
if (in_alt)
|
||||
break;
|
||||
in_alt = 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->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_group->last_insn || alt_insn == alt_group->last_insn)
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
checksum_update_insn(file, func, alt->insn);
|
||||
}
|
||||
|
||||
in_alt = 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 *func,
|
||||
|
|
|
|||
|
|
@ -144,6 +144,12 @@ struct instruction *find_insn(struct objtool_file *file,
|
|||
struct section *sec, unsigned long offset);
|
||||
|
||||
struct instruction *next_insn_same_sec(struct objtool_file *file, struct instruction *insn);
|
||||
struct instruction *next_insn_same_func(struct objtool_file *file, struct instruction *insn);
|
||||
|
||||
#define func_for_each_insn(file, func, insn) \
|
||||
for (insn = find_insn(file, func->sec, func->offset); \
|
||||
insn; \
|
||||
insn = next_insn_same_func(file, insn))
|
||||
|
||||
#define sec_for_each_insn(file, _sec, insn) \
|
||||
for (insn = find_insn(file, _sec, 0); \
|
||||
|
|
|
|||
|
|
@ -31,9 +31,13 @@ static inline void checksum_finish(struct symbol *func)
|
|||
}
|
||||
}
|
||||
|
||||
int calculate_checksums(struct objtool_file *file);
|
||||
int create_sym_checksum_section(struct objtool_file *file);
|
||||
|
||||
#else /* !BUILD_KLP */
|
||||
|
||||
static inline int calculate_checksums(struct objtool_file *file) { return -ENOSYS; }
|
||||
static inline int create_sym_checksum_section(struct objtool_file *file) { return -EINVAL; }
|
||||
|
||||
#endif /* !BUILD_KLP */
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ struct klp_reloc {
|
|||
u32 type;
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
|
|
|
|||
253
tools/objtool/klp-checksum.c
Normal file
253
tools/objtool/klp-checksum.c
Normal file
|
|
@ -0,0 +1,253 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#include <string.h>
|
||||
#include <subcmd/parse-options.h>
|
||||
|
||||
#include <objtool/arch.h>
|
||||
#include <objtool/builtin.h>
|
||||
#include <objtool/check.h>
|
||||
#include <objtool/elf.h>
|
||||
#include <objtool/klp.h>
|
||||
#include <objtool/objtool.h>
|
||||
#include <objtool/warn.h>
|
||||
#include <objtool/checksum.h>
|
||||
|
||||
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) {
|
||||
bool found = false;
|
||||
struct symbol *sym;
|
||||
char *comma;
|
||||
|
||||
comma = strchr(s, ',');
|
||||
if (comma)
|
||||
*comma = '\0';
|
||||
|
||||
for_each_sym_by_name(file->elf, s, sym) {
|
||||
if (!is_func_sym(sym))
|
||||
continue;
|
||||
sym->debug_checksum = 1;
|
||||
found = true;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
WARN("--debug-checksum: can't find '%s'", s);
|
||||
|
||||
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)
|
||||
{
|
||||
struct reloc *reloc = 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 = insn_call_dest(insn);
|
||||
|
||||
if (call_dest)
|
||||
checksum_update(func, insn, call_dest->demangled_name,
|
||||
strlen(call_dest->demangled_name));
|
||||
goto alts;
|
||||
}
|
||||
|
||||
sym = reloc->sym;
|
||||
offset = arch_insn_adjusted_addend(insn, reloc);
|
||||
|
||||
if (is_string_sec(sym->sec)) {
|
||||
char *str;
|
||||
|
||||
str = sym->sec->data->d_buf + sym->offset + offset;
|
||||
checksum_update(func, insn, str, strlen(str));
|
||||
goto alts;
|
||||
}
|
||||
|
||||
if (is_sec_sym(sym)) {
|
||||
sym = find_symbol_containing(reloc->sym->sec, offset);
|
||||
if (!sym)
|
||||
goto alts;
|
||||
|
||||
offset -= sym->offset;
|
||||
}
|
||||
|
||||
checksum_update(func, insn, sym->demangled_name, strlen(sym->demangled_name));
|
||||
checksum_update(func, insn, &offset, sizeof(offset));
|
||||
|
||||
alts:
|
||||
for (alt = insn->alts; alt; alt = alt->next) {
|
||||
struct alt_group *alt_group = alt->insn->alt_group;
|
||||
|
||||
/* Prevent __ex_table recursion, e.g. LOAD_SEGMENT() */
|
||||
if (in_alt)
|
||||
break;
|
||||
in_alt = 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->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_group->last_insn || alt_insn == alt_group->last_insn)
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
checksum_update_insn(file, func, alt->insn);
|
||||
}
|
||||
|
||||
in_alt = 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 = 0;
|
||||
struct sym_checksum *checksum;
|
||||
size_t entsize = sizeof(struct sym_checksum);
|
||||
|
||||
sec = 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 = elf_create_section_pair(file->elf, ".discard.sym_checksum", entsize,
|
||||
idx, idx);
|
||||
if (!sec)
|
||||
return -1;
|
||||
|
||||
idx = 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 = (struct sym_checksum *)sec->data->d_buf + idx;
|
||||
checksum->addr = 0; /* reloc */
|
||||
checksum->checksum = sym->csum.checksum;
|
||||
|
||||
mark_sec_changed(file->elf, sec, true);
|
||||
|
||||
idx++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char * const klp_checksum_usage[] = {
|
||||
"objtool klp checksum [<options>] file.o",
|
||||
NULL,
|
||||
};
|
||||
|
||||
int cmd_klp_checksum(int argc, const char **argv)
|
||||
{
|
||||
struct objtool_file *file;
|
||||
int ret;
|
||||
|
||||
const struct option options[] = {
|
||||
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_END(),
|
||||
};
|
||||
|
||||
argc = parse_options(argc, argv, options, klp_checksum_usage, 0);
|
||||
if (argc != 1)
|
||||
usage_with_options(klp_checksum_usage, options);
|
||||
|
||||
opts.checksum = true;
|
||||
|
||||
objname = argv[0];
|
||||
|
||||
file = objtool_open_read(objname);
|
||||
if (!file)
|
||||
return 1;
|
||||
|
||||
ret = decode_file(file);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = calculate_checksums(file);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = 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);
|
||||
}
|
||||
|
|
@ -166,7 +166,7 @@ static int read_sym_checksums(struct elf *elf)
|
|||
|
||||
sec = 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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user