In preparation for porting the checksum code to other arches, make its functionality independent from the CFG reverse engineering code.
Move it into a standalone calculate_checksums() function which iterates all functions and instructions directly, rather than being called inline from do_validate_branch(). Since checksum_update_insn() is no longer called during CFG traversal, it needs to manually iterate the alternatives. Signed-off-by: Josh Poimboeuf <[email protected]> --- tools/objtool/check.c | 106 +++++++++++++++++------ tools/objtool/include/objtool/checksum.h | 6 +- 2 files changed, 80 insertions(+), 32 deletions(-) diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 4ed27c53c718..c8208caa4b2c 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -1350,10 +1350,7 @@ static struct reloc *insn_reloc(struct objtool_file *file, struct instruction *i { struct reloc *reloc; - if (insn->no_reloc) - return NULL; - - if (!file) + if (!file || insn->no_reloc || insn->fake) return NULL; reloc = find_reloc_by_dest_range(file->elf, insn->sec, @@ -2622,9 +2619,17 @@ static void mark_holes(struct objtool_file *file) static bool validate_branch_enabled(void) { - return opts.stackval || - opts.orc || - opts.uaccess || + return opts.stackval || + opts.orc || + opts.uaccess; +} + +static bool alts_needed(void) +{ + return validate_branch_enabled() || + opts.noinstr || + opts.hack_jump_label || + opts.disas || opts.checksum; } @@ -2658,7 +2663,7 @@ static int decode_sections(struct objtool_file *file) * Must be before add_jump_destinations(), which depends on 'func' * being set for alternatives, to enable proper sibling call detection. */ - if (validate_branch_enabled() || opts.noinstr || opts.hack_jump_label || opts.disas) { + if (alts_needed()) { if (add_special_section_alts(file)) return -1; } @@ -3654,6 +3659,7 @@ static bool skip_alt_group(struct instruction *insn) return alt_insn->type == INSN_CLAC || alt_insn->type == INSN_STAC; } +#ifdef BUILD_KLP static void enable_debug_checksum_cb(struct symbol *sym, void *d) { bool *found = d; @@ -3705,8 +3711,10 @@ 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; @@ -3719,7 +3727,7 @@ static void checksum_update_insn(struct objtool_file *file, struct symbol *func, if (call_dest) checksum_update(func, insn, call_dest->demangled_name, strlen(call_dest->demangled_name)); - return; + goto alts; } sym = reloc->sym; @@ -3730,21 +3738,78 @@ static void checksum_update_insn(struct objtool_file *file, struct symbol *func, str = sym->sec->data->d_buf + sym->offset + offset; checksum_update(func, insn, str, strlen(str)); - return; + goto alts; } if (is_sec_sym(sym)) { sym = find_symbol_containing(reloc->sym->sec, offset); if (!sym) - return; + 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, @@ -4026,9 +4091,6 @@ static int do_validate_branch(struct objtool_file *file, struct symbol *func, insn->trace = 0; next_insn = next_insn_to_validate(file, insn); - if (opts.checksum && func && insn->sec) - checksum_update_insn(file, func, insn); - if (func && insn_func(insn) && func != insn_func(insn)->pfunc) { /* Ignore KCFI type preambles, which always fall through */ if (is_prefix_func(func)) @@ -4094,9 +4156,6 @@ static int validate_unwind_hint(struct objtool_file *file, struct symbol *func = insn_func(insn); int ret; - if (opts.checksum) - checksum_init(func); - ret = validate_branch(file, func, insn, *state); if (ret) BT_INSN(insn, "<=== (hint)"); @@ -4539,9 +4598,6 @@ static int validate_symbol(struct objtool_file *file, struct section *sec, func = insn_func(insn); - if (opts.checksum) - checksum_init(func); - if (opts.trace && !fnmatch(opts.trace, sym->name, 0)) { trace_enable(); TRACE("%s: validation begin\n", sym->name); @@ -4554,9 +4610,6 @@ static int validate_symbol(struct objtool_file *file, struct section *sec, TRACE("%s: validation %s\n\n", sym->name, ret ? "failed" : "end"); trace_disable(); - if (opts.checksum) - checksum_finish(func); - return ret; } @@ -5011,10 +5064,6 @@ 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; @@ -5105,6 +5154,9 @@ int check(struct objtool_file *file) warnings += check_abs_references(file); if (opts.checksum) { + ret = calculate_checksums(file); + if (ret) + goto out; ret = create_sym_checksum_section(file); if (ret) goto out; diff --git a/tools/objtool/include/objtool/checksum.h b/tools/objtool/include/objtool/checksum.h index 0bd16fe9168b..3f25df90305d 100644 --- a/tools/objtool/include/objtool/checksum.h +++ b/tools/objtool/include/objtool/checksum.h @@ -33,11 +33,7 @@ static inline void checksum_finish(struct symbol *func) #else /* !BUILD_KLP */ -static inline void checksum_init(struct symbol *func) {} -static inline void checksum_update(struct symbol *func, - struct instruction *insn, - const void *data, size_t size) {} -static inline void checksum_finish(struct symbol *func) {} +static inline int calculate_checksums(struct objtool_file *file) { return -ENOSYS; } #endif /* !BUILD_KLP */ -- 2.53.0

