Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

objtool/klp: Add --checksum option to generate per-function checksums

In preparation for the objtool klp diff subcommand, add a command-line
option to generate a unique checksum for each function. This will
enable detection of functions which have changed between two versions of
an object file.

Acked-by: Petr Mladek <pmladek@suse.com>
Tested-by: Joe Lawrence <joe.lawrence@redhat.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>

+289 -28
+24 -14
tools/objtool/Makefile
··· 2 2 include ../scripts/Makefile.include 3 3 include ../scripts/Makefile.arch 4 4 5 + ifeq ($(SRCARCH),x86) 6 + BUILD_ORC := y 7 + ARCH_HAS_KLP := y 8 + endif 9 + 10 + ifeq ($(SRCARCH),loongarch) 11 + BUILD_ORC := y 12 + endif 13 + 14 + ifeq ($(ARCH_HAS_KLP),y) 15 + HAVE_XXHASH = $(shell echo "int main() {}" | \ 16 + $(HOSTCC) -xc - -o /dev/null -lxxhash 2> /dev/null && echo y || echo n) 17 + ifeq ($(HAVE_XXHASH),y) 18 + LIBXXHASH_CFLAGS := $(shell $(HOSTPKG_CONFIG) libxxhash --cflags 2>/dev/null) \ 19 + -DBUILD_KLP 20 + LIBXXHASH_LIBS := $(shell $(HOSTPKG_CONFIG) libxxhash --libs 2>/dev/null || echo -lxxhash) 21 + endif 22 + endif 23 + 24 + export BUILD_ORC 25 + 5 26 ifeq ($(srctree),) 6 27 srctree := $(patsubst %/,%,$(dir $(CURDIR))) 7 28 srctree := $(patsubst %/,%,$(dir $(srctree))) ··· 57 36 -I$(srctree)/tools/objtool/arch/$(SRCARCH)/include \ 58 37 -I$(LIBSUBCMD_OUTPUT)/include 59 38 60 - OBJTOOL_CFLAGS := -std=gnu11 -fomit-frame-pointer -O2 -g \ 61 - $(WARNINGS) $(INCLUDES) $(LIBELF_FLAGS) $(HOSTCFLAGS) 39 + OBJTOOL_CFLAGS := -std=gnu11 -fomit-frame-pointer -O2 -g $(WARNINGS) \ 40 + $(INCLUDES) $(LIBELF_FLAGS) $(LIBXXHASH_CFLAGS) $(HOSTCFLAGS) 62 41 63 - OBJTOOL_LDFLAGS := $(LIBSUBCMD) $(LIBELF_LIBS) $(HOSTLDFLAGS) 42 + OBJTOOL_LDFLAGS := $(LIBSUBCMD) $(LIBELF_LIBS) $(LIBXXHASH_LIBS) $(HOSTLDFLAGS) 64 43 65 44 # Allow old libelf to be used: 66 45 elfshdr := $(shell echo '$(pound)include <libelf.h>' | $(HOSTCC) $(OBJTOOL_CFLAGS) -x c -E - 2>/dev/null | grep elf_getshdr) ··· 72 51 AWK = awk 73 52 MKDIR = mkdir 74 53 75 - BUILD_ORC := n 76 - 77 - ifeq ($(SRCARCH),x86) 78 - BUILD_ORC := y 79 - endif 80 - 81 - ifeq ($(SRCARCH),loongarch) 82 - BUILD_ORC := y 83 - endif 84 - 85 - export BUILD_ORC 86 54 export srctree OUTPUT CFLAGS SRCARCH AWK 87 55 include $(srctree)/tools/build/Makefile.include 88 56
+10 -1
tools/objtool/builtin-check.c
··· 73 73 74 74 static const struct option check_options[] = { 75 75 OPT_GROUP("Actions:"), 76 + OPT_BOOLEAN(0, "checksum", &opts.checksum, "generate per-function checksums"), 76 77 OPT_BOOLEAN(0, "cfi", &opts.cfi, "annotate kernel control flow integrity (kCFI) function preambles"), 77 78 OPT_CALLBACK_OPTARG('h', "hacks", NULL, NULL, "jump_label,noinstr,skylake", "patch toolchain bugs/limitations", parse_hacks), 78 79 OPT_BOOLEAN('i', "ibt", &opts.ibt, "validate and annotate IBT"), ··· 161 160 return false; 162 161 } 163 162 164 - if (opts.hack_jump_label || 163 + #ifndef BUILD_KLP 164 + if (opts.checksum) { 165 + ERROR("--checksum not supported; install xxhash-devel and recompile"); 166 + return false; 167 + } 168 + #endif 169 + 170 + if (opts.checksum || 171 + opts.hack_jump_label || 165 172 opts.hack_noinstr || 166 173 opts.ibt || 167 174 opts.mcount ||
+136 -5
tools/objtool/check.c
··· 14 14 #include <objtool/check.h> 15 15 #include <objtool/special.h> 16 16 #include <objtool/warn.h> 17 + #include <objtool/checksum.h> 17 18 18 19 #include <linux/objtool_types.h> 19 20 #include <linux/hashtable.h> ··· 972 971 return 0; 973 972 } 974 973 974 + #ifdef BUILD_KLP 975 + static int create_sym_checksum_section(struct objtool_file *file) 976 + { 977 + struct section *sec; 978 + struct symbol *sym; 979 + unsigned int idx = 0; 980 + struct sym_checksum *checksum; 981 + size_t entsize = sizeof(struct sym_checksum); 982 + 983 + sec = find_section_by_name(file->elf, ".discard.sym_checksum"); 984 + if (sec) { 985 + if (!opts.dryrun) 986 + WARN("file already has .discard.sym_checksum section, skipping"); 987 + 988 + return 0; 989 + } 990 + 991 + for_each_sym(file->elf, sym) 992 + if (sym->csum.checksum) 993 + idx++; 994 + 995 + if (!idx) 996 + return 0; 997 + 998 + sec = elf_create_section_pair(file->elf, ".discard.sym_checksum", entsize, 999 + idx, idx); 1000 + if (!sec) 1001 + return -1; 1002 + 1003 + idx = 0; 1004 + for_each_sym(file->elf, sym) { 1005 + if (!sym->csum.checksum) 1006 + continue; 1007 + 1008 + if (!elf_init_reloc(file->elf, sec->rsec, idx, idx * entsize, 1009 + sym, 0, R_TEXT64)) 1010 + return -1; 1011 + 1012 + checksum = (struct sym_checksum *)sec->data->d_buf + idx; 1013 + checksum->addr = 0; /* reloc */ 1014 + checksum->checksum = sym->csum.checksum; 1015 + 1016 + mark_sec_changed(file->elf, sec, true); 1017 + 1018 + idx++; 1019 + } 1020 + 1021 + return 0; 1022 + } 1023 + #else 1024 + static int create_sym_checksum_section(struct objtool_file *file) { return -EINVAL; } 1025 + #endif 1026 + 975 1027 /* 976 1028 * Warnings shouldn't be reported for ignored functions. 977 1029 */ ··· 1802 1748 nop->type = INSN_NOP; 1803 1749 nop->sym = orig_insn->sym; 1804 1750 nop->alt_group = new_alt_group; 1751 + nop->fake = 1; 1805 1752 } 1806 1753 1807 1754 if (!special_alt->new_len) { ··· 2572 2517 } 2573 2518 } 2574 2519 2520 + static bool validate_branch_enabled(void) 2521 + { 2522 + return opts.stackval || 2523 + opts.orc || 2524 + opts.uaccess || 2525 + opts.checksum; 2526 + } 2527 + 2575 2528 static int decode_sections(struct objtool_file *file) 2576 2529 { 2577 2530 mark_rodata(file); ··· 2608 2545 * Must be before add_jump_destinations(), which depends on 'func' 2609 2546 * being set for alternatives, to enable proper sibling call detection. 2610 2547 */ 2611 - if (opts.stackval || opts.orc || opts.uaccess || opts.noinstr || 2612 - opts.hack_jump_label) { 2548 + if (validate_branch_enabled() || opts.noinstr || opts.hack_jump_label) { 2613 2549 if (add_special_section_alts(file)) 2614 2550 return -1; 2615 2551 } ··· 3580 3518 return alt_insn->type == INSN_CLAC || alt_insn->type == INSN_STAC; 3581 3519 } 3582 3520 3521 + static void checksum_update_insn(struct objtool_file *file, struct symbol *func, 3522 + struct instruction *insn) 3523 + { 3524 + struct reloc *reloc = insn_reloc(file, insn); 3525 + unsigned long offset; 3526 + struct symbol *sym; 3527 + 3528 + if (insn->fake) 3529 + return; 3530 + 3531 + checksum_update(func, insn, insn->sec->data->d_buf + insn->offset, insn->len); 3532 + 3533 + if (!reloc) { 3534 + struct symbol *call_dest = insn_call_dest(insn); 3535 + 3536 + if (call_dest) 3537 + checksum_update(func, insn, call_dest->demangled_name, 3538 + strlen(call_dest->demangled_name)); 3539 + return; 3540 + } 3541 + 3542 + sym = reloc->sym; 3543 + offset = arch_insn_adjusted_addend(insn, reloc); 3544 + 3545 + if (is_string_sec(sym->sec)) { 3546 + char *str; 3547 + 3548 + str = sym->sec->data->d_buf + sym->offset + offset; 3549 + checksum_update(func, insn, str, strlen(str)); 3550 + return; 3551 + } 3552 + 3553 + if (is_sec_sym(sym)) { 3554 + sym = find_symbol_containing(reloc->sym->sec, offset); 3555 + if (!sym) 3556 + return; 3557 + 3558 + offset -= sym->offset; 3559 + } 3560 + 3561 + checksum_update(func, insn, sym->demangled_name, strlen(sym->demangled_name)); 3562 + checksum_update(func, insn, &offset, sizeof(offset)); 3563 + } 3564 + 3583 3565 /* 3584 3566 * Follow the branch starting at the given instruction, and recursively follow 3585 3567 * any other branches (jumps). Meanwhile, track the frame pointer state at ··· 3643 3537 3644 3538 while (1) { 3645 3539 next_insn = next_insn_to_validate(file, insn); 3540 + 3541 + if (opts.checksum && func && insn->sec) 3542 + checksum_update_insn(file, func, insn); 3646 3543 3647 3544 if (func && insn_func(insn) && func != insn_func(insn)->pfunc) { 3648 3545 /* Ignore KCFI type preambles, which always fall through */ ··· 3896 3787 struct insn_state *state) 3897 3788 { 3898 3789 if (insn->hint && !insn->visited) { 3899 - int ret = validate_branch(file, insn_func(insn), insn, *state); 3790 + struct symbol *func = insn_func(insn); 3791 + int ret; 3792 + 3793 + if (opts.checksum) 3794 + checksum_init(func); 3795 + 3796 + ret = validate_branch(file, func, insn, *state); 3900 3797 if (ret) 3901 3798 BT_INSN(insn, "<=== (hint)"); 3902 3799 return ret; ··· 4281 4166 struct symbol *sym, struct insn_state *state) 4282 4167 { 4283 4168 struct instruction *insn; 4169 + struct symbol *func; 4284 4170 int ret; 4285 4171 4286 4172 if (!sym->len) { ··· 4299 4183 if (opts.uaccess) 4300 4184 state->uaccess = sym->uaccess_safe; 4301 4185 4302 - ret = validate_branch(file, insn_func(insn), insn, *state); 4186 + func = insn_func(insn); 4187 + 4188 + if (opts.checksum) 4189 + checksum_init(func); 4190 + 4191 + ret = validate_branch(file, func, insn, *state); 4303 4192 if (ret) 4304 4193 BT_INSN(insn, "<=== (sym)"); 4194 + 4195 + if (opts.checksum) 4196 + checksum_finish(func); 4197 + 4305 4198 return ret; 4306 4199 } 4307 4200 ··· 4828 4703 if (opts.retpoline) 4829 4704 warnings += validate_retpoline(file); 4830 4705 4831 - if (opts.stackval || opts.orc || opts.uaccess) { 4706 + if (validate_branch_enabled()) { 4832 4707 int w = 0; 4833 4708 4834 4709 w += validate_functions(file); ··· 4906 4781 4907 4782 if (opts.noabs) 4908 4783 warnings += check_abs_references(file); 4784 + 4785 + if (opts.checksum) { 4786 + ret = create_sym_checksum_section(file); 4787 + if (ret) 4788 + goto out; 4789 + } 4909 4790 4910 4791 if (opts.orc && nr_insns) { 4911 4792 ret = orc_create(file);
+43 -3
tools/objtool/elf.c
··· 17 17 #include <unistd.h> 18 18 #include <errno.h> 19 19 #include <libgen.h> 20 + #include <ctype.h> 20 21 #include <linux/interval_tree_generic.h> 21 22 #include <objtool/builtin.h> 22 23 #include <objtool/elf.h> ··· 413 412 return 0; 414 413 } 415 414 416 - static void elf_add_symbol(struct elf *elf, struct symbol *sym) 415 + static const char *demangle_name(struct symbol *sym) 416 + { 417 + char *str; 418 + 419 + if (!is_local_sym(sym)) 420 + return sym->name; 421 + 422 + if (!is_func_sym(sym) && !is_object_sym(sym)) 423 + return sym->name; 424 + 425 + if (!strstarts(sym->name, "__UNIQUE_ID_") && !strchr(sym->name, '.')) 426 + return sym->name; 427 + 428 + str = strdup(sym->name); 429 + if (!str) { 430 + ERROR_GLIBC("strdup"); 431 + return NULL; 432 + } 433 + 434 + for (int i = strlen(str) - 1; i >= 0; i--) { 435 + char c = str[i]; 436 + 437 + if (!isdigit(c) && c != '.') { 438 + str[i + 1] = '\0'; 439 + break; 440 + } 441 + }; 442 + 443 + return str; 444 + } 445 + 446 + static int elf_add_symbol(struct elf *elf, struct symbol *sym) 417 447 { 418 448 struct list_head *entry; 419 449 struct rb_node *pnode; ··· 488 456 if (is_func_sym(sym) && strstr(sym->name, ".cold")) 489 457 sym->cold = 1; 490 458 sym->pfunc = sym->cfunc = sym; 459 + 460 + sym->demangled_name = demangle_name(sym); 461 + if (!sym->demangled_name) 462 + return -1; 463 + 464 + return 0; 491 465 } 492 466 493 467 static int read_symbols(struct elf *elf) ··· 567 529 } else 568 530 sym->sec = find_section_by_index(elf, 0); 569 531 570 - elf_add_symbol(elf, sym); 532 + if (elf_add_symbol(elf, sym)) 533 + return -1; 571 534 } 572 535 573 536 if (opts.stats) { ··· 906 867 mark_sec_changed(elf, symtab_shndx, true); 907 868 } 908 869 909 - elf_add_symbol(elf, sym); 870 + if (elf_add_symbol(elf, sym)) 871 + return NULL; 910 872 911 873 return sym; 912 874 }
+3 -2
tools/objtool/include/objtool/builtin.h
··· 9 9 10 10 struct opts { 11 11 /* actions: */ 12 + bool cfi; 13 + bool checksum; 12 14 bool dump_orc; 13 15 bool hack_jump_label; 14 16 bool hack_noinstr; 15 17 bool hack_skylake; 16 18 bool ibt; 17 19 bool mcount; 20 + bool noabs; 18 21 bool noinstr; 19 22 bool orc; 20 23 bool retpoline; ··· 28 25 bool static_call; 29 26 bool uaccess; 30 27 int prefix; 31 - bool cfi; 32 - bool noabs; 33 28 34 29 /* options: */ 35 30 bool backtrace;
+3 -2
tools/objtool/include/objtool/check.h
··· 65 65 unret : 1, 66 66 visited : 4, 67 67 no_reloc : 1, 68 - hole : 1; 69 - /* 10 bit hole */ 68 + hole : 1, 69 + fake : 1; 70 + /* 9 bit hole */ 70 71 71 72 struct alt_group *alt_group; 72 73 struct instruction *jump_dest;
+42
tools/objtool/include/objtool/checksum.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 + #ifndef _OBJTOOL_CHECKSUM_H 3 + #define _OBJTOOL_CHECKSUM_H 4 + 5 + #include <objtool/elf.h> 6 + 7 + #ifdef BUILD_KLP 8 + 9 + static inline void checksum_init(struct symbol *func) 10 + { 11 + if (func && !func->csum.state) { 12 + func->csum.state = XXH3_createState(); 13 + XXH3_64bits_reset(func->csum.state); 14 + } 15 + } 16 + 17 + static inline void checksum_update(struct symbol *func, 18 + struct instruction *insn, 19 + const void *data, size_t size) 20 + { 21 + XXH3_64bits_update(func->csum.state, data, size); 22 + } 23 + 24 + static inline void checksum_finish(struct symbol *func) 25 + { 26 + if (func && func->csum.state) { 27 + func->csum.checksum = XXH3_64bits_digest(func->csum.state); 28 + func->csum.state = NULL; 29 + } 30 + } 31 + 32 + #else /* !BUILD_KLP */ 33 + 34 + static inline void checksum_init(struct symbol *func) {} 35 + static inline void checksum_update(struct symbol *func, 36 + struct instruction *insn, 37 + const void *data, size_t size) {} 38 + static inline void checksum_finish(struct symbol *func) {} 39 + 40 + #endif /* !BUILD_KLP */ 41 + 42 + #endif /* _OBJTOOL_CHECKSUM_H */
+25
tools/objtool/include/objtool/checksum_types.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + #ifndef _OBJTOOL_CHECKSUM_TYPES_H 3 + #define _OBJTOOL_CHECKSUM_TYPES_H 4 + 5 + struct sym_checksum { 6 + u64 addr; 7 + u64 checksum; 8 + }; 9 + 10 + #ifdef BUILD_KLP 11 + 12 + #include <xxhash.h> 13 + 14 + struct checksum { 15 + XXH3_state_t *state; 16 + XXH64_hash_t checksum; 17 + }; 18 + 19 + #else /* !BUILD_KLP */ 20 + 21 + struct checksum {}; 22 + 23 + #endif /* !BUILD_KLP */ 24 + 25 + #endif /* _OBJTOOL_CHECKSUM_TYPES_H */
+3 -1
tools/objtool/include/objtool/elf.h
··· 15 15 #include <linux/jhash.h> 16 16 17 17 #include <objtool/endianness.h> 18 + #include <objtool/checksum_types.h> 18 19 #include <arch/elf.h> 19 20 20 21 #define SYM_NAME_LEN 512 ··· 62 61 struct elf_hash_node name_hash; 63 62 GElf_Sym sym; 64 63 struct section *sec; 65 - const char *name; 64 + const char *name, *demangled_name; 66 65 unsigned int idx, len; 67 66 unsigned long offset; 68 67 unsigned long __subtree_last; ··· 85 84 struct list_head pv_target; 86 85 struct reloc *relocs; 87 86 struct section *group_sec; 87 + struct checksum csum; 88 88 }; 89 89 90 90 struct reloc {