From nobody Sun Feb 8 19:55:21 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8747520011E; Tue, 3 Sep 2024 04:00:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1725336044; cv=none; b=GQltXHwzvB3yxKdNly/WljCjvYERPeh68+rXahc0sY2CXy8v2TosSqWCBi3fq4bWM6L6Vu7POfmqhd2bCo7Vfa3K4ikbaXqKg8pjysvaJM+jGVtFhkvG3YC6uzOrmbuzKSWgIZ9ALbUTwmmHMwF8Thmux+SMCODrD6spCNw8rSI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1725336044; c=relaxed/simple; bh=KhD/6JeYp0HzWzUQGCZwBMmcsHO1K5HE8VDDwOmizj8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=j7boxCK70In8JpY/AtcISOw3pxkoqkVD64C/f/e6SgSNiPmVYIsbA+nCON2PwSF3i+4kRzRMa3vPrgC5/geXVMjeWdzqlU5aVoG3Srr3vJuXErGFaWoWXQOYFnKv45V3hZl2kKSVT37/+mbUiMqgKjLhqyCOxtBtUS1qe6JkaY0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=iJNVwzJv; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="iJNVwzJv" Received: by smtp.kernel.org (Postfix) with ESMTPSA id C06F2C4CECA; Tue, 3 Sep 2024 04:00:43 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1725336044; bh=KhD/6JeYp0HzWzUQGCZwBMmcsHO1K5HE8VDDwOmizj8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=iJNVwzJvPzcxAR0nAQ2qiqUpjHFtFxBvFbS18VaJDuHX/Gl+36PQM9PBFWjxR+aOQ bvEe5zSrSZsLMtgntI3ufwCuEIG5FaBzrIYyTMu3BDKZwiMCAWPxuj3DIY3rPRnfED uZ4dhtzHCPBUYSOgTPrP6VuOw4DwHbKN7LU8L8LlbUkUOmQEvuCpjCWzPBFyI9EHDu 9GwV3fP7OHwXO/TgRgNgDnToYQz8vkEDt3fpC2uLlvXh0ZHHg5AFfm1Q4vlojz6MAE SDAxBEJ8HNOe00J1KjHW8HW+AP2105XmziciAXkase2eetCUxJV/sRcPN0pBIy0g86 FoxYWOwGvVGzA== From: Josh Poimboeuf To: live-patching@vger.kernel.org Cc: linux-kernel@vger.kernel.org, x86@kernel.org, Miroslav Benes , Petr Mladek , Joe Lawrence , Jiri Kosina , Peter Zijlstra , Marcos Paulo de Souza , Song Liu Subject: [RFC 29/31] objtool: Calculate function checksums Date: Mon, 2 Sep 2024 21:00:12 -0700 Message-ID: X-Mailer: git-send-email 2.45.2 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Calculate per-function checksums based on the functions' content and relocations. This will enable objtool to do binary diffs. Signed-off-by: Josh Poimboeuf --- scripts/Makefile.lib | 1 + tools/objtool/Makefile | 7 +- tools/objtool/builtin-check.c | 1 + tools/objtool/check.c | 137 +++++++++++++++++++++++- tools/objtool/elf.c | 31 ++++++ tools/objtool/include/objtool/builtin.h | 3 +- tools/objtool/include/objtool/check.h | 5 +- tools/objtool/include/objtool/elf.h | 11 +- tools/objtool/include/objtool/objtool.h | 2 + 9 files changed, 188 insertions(+), 10 deletions(-) diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index 8411e3d53938..9f4708702ef7 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -265,6 +265,7 @@ ifdef CONFIG_OBJTOOL =20 objtool :=3D $(objtree)/tools/objtool/objtool =20 +objtool-args-$(CONFIG_LIVEPATCH) +=3D --sym-checksum objtool-args-$(CONFIG_HAVE_JUMP_LABEL_HACK) +=3D --hacks=3Djump_label objtool-args-$(CONFIG_HAVE_NOINSTR_HACK) +=3D --hacks=3Dnoinstr objtool-args-$(CONFIG_MITIGATION_CALL_DEPTH_TRACKING) +=3D --hacks=3Dskyla= ke diff --git a/tools/objtool/Makefile b/tools/objtool/Makefile index bf7f7f84ac62..6833804ca419 100644 --- a/tools/objtool/Makefile +++ b/tools/objtool/Makefile @@ -21,6 +21,9 @@ OBJTOOL_IN :=3D $(OBJTOOL)-in.o LIBELF_FLAGS :=3D $(shell $(HOSTPKG_CONFIG) libelf --cflags 2>/dev/null) LIBELF_LIBS :=3D $(shell $(HOSTPKG_CONFIG) libelf --libs 2>/dev/null || e= cho -lelf) =20 +LIBXXHASH_FLAGS :=3D $(shell $(HOSTPKG_CONFIG) libxxhash --cflags 2>/dev/n= ull) +LIBXXHASH_LIBS :=3D $(shell $(HOSTPKG_CONFIG) libxxhash --libs 2>/dev/nul= l || echo -lxxhash) + all: $(OBJTOOL) =20 INCLUDES :=3D -I$(srctree)/tools/include \ @@ -32,8 +35,8 @@ INCLUDES :=3D -I$(srctree)/tools/include \ # Note, EXTRA_WARNINGS here was determined for CC and not HOSTCC, it # is passed here to match a legacy behavior. WARNINGS :=3D $(EXTRA_WARNINGS) -Wno-switch-default -Wno-switch-enum -Wno-= packed -Wno-nested-externs -OBJTOOL_CFLAGS :=3D -Werror $(WARNINGS) $(KBUILD_HOSTCFLAGS) -g $(INCLUDES= ) $(LIBELF_FLAGS) -OBJTOOL_LDFLAGS :=3D $(LIBELF_LIBS) $(LIBSUBCMD) $(KBUILD_HOSTLDFLAGS) +OBJTOOL_CFLAGS :=3D -Werror $(WARNINGS) $(KBUILD_HOSTCFLAGS) -g $(INCLUDES= ) $(LIBELF_FLAGS) $(LIBXXHASH_FLAGS) +OBJTOOL_LDFLAGS :=3D $(LIBELF_LIBS) $(LIBXXHASH_LIBS) $(LIBSUBCMD) $(KBUIL= D_HOSTLDFLAGS) =20 # Allow old libelf to be used: elfshdr :=3D $(shell echo '$(pound)include ' | $(HOSTCC) $(OBJTO= OL_CFLAGS) -x c -E - | grep elf_getshdr) diff --git a/tools/objtool/builtin-check.c b/tools/objtool/builtin-check.c index 6894ef68d125..f3473c046c86 100644 --- a/tools/objtool/builtin-check.c +++ b/tools/objtool/builtin-check.c @@ -75,6 +75,7 @@ static const struct option check_options[] =3D { OPT_BOOLEAN('l', "sls", &opts.sls, "validate straight-line-speculation mi= tigations"), OPT_BOOLEAN('s', "stackval", &opts.stackval, "validate frame pointer rule= s"), OPT_BOOLEAN('t', "static-call", &opts.static_call, "annotate static calls= "), + OPT_BOOLEAN(0, "sym-checksum", &opts.sym_checksum, "generate per-functi= on checksums"), OPT_BOOLEAN('u', "uaccess", &opts.uaccess, "validate uaccess rules for SM= AP"), OPT_BOOLEAN(0 , "cfi", &opts.cfi, "annotate kernel control flow integrit= y (kCFI) function preambles"), OPT_CALLBACK_OPTARG(0, "dump", NULL, NULL, "orc", "dump metadata", parse_= dump), diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 5dd78a7f75c3..0e9e485cd3b6 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -8,6 +8,8 @@ #include #include =20 +#include + #include #include #include @@ -951,6 +953,48 @@ static void create_direct_call_sections(struct objtool= _file *file) } } =20 +static void create_sym_checksum_section(struct objtool_file *file) +{ + struct section *sec; + struct symbol *sym; + unsigned int idx =3D 0; + struct sym_checksum *sym_checksum; + size_t entsize =3D sizeof(struct sym_checksum); + + sec =3D find_section_by_name(file->elf, SYM_CHECKSUM_SEC); + if (sec) { + WARN("file already has " SYM_CHECKSUM_SEC " section, skipping"); + return; + } + + for_each_sym(file->elf, sym) + if (sym->checksum) + idx++; + + if (!idx) + return; + + sec =3D elf_create_section_pair(file->elf, ".discard.sym_checksum", entsi= ze, + idx, idx); + + idx =3D 0; + for_each_sym(file->elf, sym) { + if (!sym->checksum) + continue; + + elf_init_reloc(file->elf, sec->rsec, idx, idx * entsize, sym, + 0, R_TEXT64); + + sym_checksum =3D (struct sym_checksum *)sec->data->d_buf + idx; + sym_checksum->addr =3D 0; /* reloc */ + sym_checksum->checksum =3D sym->checksum; + + mark_sec_changed(file->elf, sec, true); + + idx++; + } +} + /* * Warnings shouldn't be reported for ignored functions. */ @@ -1709,6 +1753,7 @@ static void handle_group_alt(struct objtool_file *fil= e, nop->sym =3D orig_insn->sym; nop->alt_group =3D new_alt_group; nop->ignore =3D orig_insn->ignore_alts; + nop->fake =3D 1; } =20 if (!special_alt->new_len) { @@ -3291,6 +3336,58 @@ static struct instruction *next_insn_to_validate(str= uct objtool_file *file, return next_insn_same_sec(file, alt_group->orig_group->last_insn); } =20 +static void update_sym_checksum(struct symbol *func, struct instruction *i= nsn, + const void *data, size_t size) +{ + XXH3_64bits_update(func->checksum_state, data, size); +} + +static void update_insn_sym_checksum(struct objtool_file *file, struct sym= bol *func, + struct instruction *insn) +{ + struct reloc *reloc =3D insn_reloc(file, insn); + struct symbol *dest =3D insn_call_dest(insn); + + if (dest && !reloc) { + update_sym_checksum(func, insn, insn->sec->data->d_buf + insn->offset, 1= ); + update_sym_checksum(func, insn, dest->name, strlen(dest->name)); + } else if (!insn->fake) { + update_sym_checksum(func, insn, insn->sec->data->d_buf + insn->offset, i= nsn->len); + } + + if (reloc) { + struct symbol *sym =3D reloc->sym; + + if (sym->sec && is_string_section(sym->sec)) { + s64 addend; + char *str; + + addend =3D arch_insn_adjusted_addend(insn, reloc); + + str =3D sym->sec->data->d_buf + sym->offset + addend; + + update_sym_checksum(func, insn, str, strlen(str)); + + } else { + u64 offset =3D arch_insn_adjusted_addend(insn, reloc); + + if (is_section_symbol(sym)) { + sym =3D find_symbol_containing(reloc->sym->sec, offset); + if (!sym) + return; + + offset -=3D sym->offset; + } + + update_sym_checksum(func, insn, sym->demangled_name, + strlen(sym->demangled_name)); + + update_sym_checksum(func, insn, &offset, sizeof(offset)); + } + } +} + + /* * Follow the branch starting at the given instruction, and recursively fo= llow * any other branches (jumps). Meanwhile, track the frame pointer state at @@ -3306,11 +3403,15 @@ static int validate_branch(struct objtool_file *fil= e, struct symbol *func, u8 visited; int ret; =20 - sec =3D insn->sec; - while (1) { next_insn =3D next_insn_to_validate(file, insn); =20 + // moved this because alt can continue to orig thanks to next_insn_same_= sec + sec =3D insn->sec; + + if (opts.sym_checksum && func && sec) + update_insn_sym_checksum(file, func, insn); + if (func && insn_func(insn) && func !=3D insn_func(insn)->pfunc) { /* Ignore KCFI type preambles, which always fall through */ if (!strncmp(func->name, "__cfi_", 6) || @@ -3549,7 +3650,15 @@ static int validate_unwind_hint(struct objtool_file = *file, struct insn_state *state) { if (insn->hint && !insn->visited && !insn->ignore) { - int ret =3D validate_branch(file, insn_func(insn), insn, *state); + struct symbol *func =3D insn_func(insn); + int ret; + + if (func && !func->checksum_state) { + func->checksum_state =3D XXH3_createState(); + XXH3_64bits_reset(func->checksum_state); + } + + ret =3D validate_branch(file, func, insn, *state); if (ret) BT_INSN(insn, "<=3D=3D=3D (hint)"); return ret; @@ -3941,7 +4050,9 @@ static void add_prefix_symbols(struct objtool_file *f= ile) static int validate_symbol(struct objtool_file *file, struct section *sec, struct symbol *sym, struct insn_state *state) { + static XXH3_state_t *checksum_state; struct instruction *insn; + struct symbol *func; int ret; =20 if (!sym->len) { @@ -3958,9 +4069,24 @@ static int validate_symbol(struct objtool_file *file= , struct section *sec, =20 state->uaccess =3D sym->uaccess_safe; =20 - ret =3D validate_branch(file, insn_func(insn), insn, *state); + func =3D insn_func(insn); + + if (func && !func->checksum_state) { + if (!checksum_state) + checksum_state =3D XXH3_createState(); + XXH3_64bits_reset(checksum_state); + func->checksum_state =3D checksum_state; + } + + ret =3D validate_branch(file, func, insn, *state); if (ret) BT_INSN(insn, "<=3D=3D=3D (sym)"); + + if (func) { + func->checksum =3D XXH3_64bits_digest(func->checksum_state); + func->checksum_state =3D NULL; + } + return ret; } =20 @@ -4509,6 +4635,9 @@ int check(struct objtool_file *file) if (opts.ibt) create_ibt_endbr_seal_sections(file); =20 + if (opts.sym_checksum) + create_sym_checksum_section(file); + if (opts.orc && nr_insns) { ret =3D orc_create(file); if (ret < 0) diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c index 3109277804cc..022873bf7064 100644 --- a/tools/objtool/elf.c +++ b/tools/objtool/elf.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -396,6 +397,34 @@ static void read_sections(struct elf *elf) ERROR("section entry mismatch"); } =20 +static const char *demangle_name(struct symbol *sym) +{ + char *str; + + if (!is_local_symbol(sym)) + return sym->name; + + if (!is_function_symbol(sym) && !is_object_symbol(sym)) + return sym->name; + + if (!strstarts(sym->name, "__UNIQUE_ID_") && !strchr(sym->name, '.')) + return sym->name; + + str =3D strdup(sym->name); + ERROR_ON(!str, "strdup"); + + for (int i =3D strlen(str) - 1; i >=3D 0; i--) { + char c =3D str[i]; + + if (!isdigit(c) && c !=3D '.') { + str[i + 1] =3D '\0'; + break; + } + }; + + return str; +} + static void elf_add_symbol(struct elf *elf, struct symbol *sym) { struct list_head *entry; @@ -440,6 +469,8 @@ static void elf_add_symbol(struct elf *elf, struct symb= ol *sym) if (sym->type =3D=3D STT_NOTYPE && !sym->len) __sym_remove(sym, &sym->sec->symbol_tree); #endif + + sym->demangled_name =3D demangle_name(sym); } =20 static void read_symbols(struct elf *elf) diff --git a/tools/objtool/include/objtool/builtin.h b/tools/objtool/includ= e/objtool/builtin.h index fcca6662c8b4..eab376169c1e 100644 --- a/tools/objtool/include/objtool/builtin.h +++ b/tools/objtool/include/objtool/builtin.h @@ -9,6 +9,7 @@ =20 struct opts { /* actions: */ + bool cfi; bool dump_orc; bool hack_jump_label; bool hack_noinstr; @@ -23,9 +24,9 @@ struct opts { bool sls; bool stackval; bool static_call; + bool sym_checksum; bool uaccess; int prefix; - bool cfi; =20 /* options: */ bool backtrace; diff --git a/tools/objtool/include/objtool/check.h b/tools/objtool/include/= objtool/check.h index daa46f1f0965..b546a31dc2a9 100644 --- a/tools/objtool/include/objtool/check.h +++ b/tools/objtool/include/objtool/check.h @@ -63,8 +63,9 @@ struct instruction { noendbr : 1, unret : 1, visited : 4, - no_reloc : 1; - /* 10 bit hole */ + no_reloc : 1, + fake : 1; + /* 9 bit hole */ =20 struct alt_group *alt_group; struct instruction *jump_dest; diff --git a/tools/objtool/include/objtool/elf.h b/tools/objtool/include/ob= jtool/elf.h index f759686d46d7..1f14f33d279e 100644 --- a/tools/objtool/include/objtool/elf.h +++ b/tools/objtool/include/objtool/elf.h @@ -13,6 +13,7 @@ #include #include #include +#include #include =20 #define SYM_NAME_LEN 512 @@ -29,6 +30,11 @@ #define ELF_C_READ_MMAP ELF_C_READ #endif =20 +struct sym_checksum { + u64 addr; + u64 checksum; +}; + struct elf_hash_node { struct elf_hash_node *next; }; @@ -56,7 +62,7 @@ struct symbol { struct elf_hash_node name_hash; GElf_Sym sym; struct section *sec; - const char *name; + const char *name, *demangled_name; unsigned int idx, len; unsigned long offset; unsigned long __subtree_last; @@ -73,6 +79,9 @@ struct symbol { u8 local_label : 1; struct list_head pv_target; struct reloc *relocs; + + XXH3_state_t *checksum_state; + XXH64_hash_t checksum; }; =20 struct reloc { diff --git a/tools/objtool/include/objtool/objtool.h b/tools/objtool/includ= e/objtool/objtool.h index ae30497e014b..3280abcce55e 100644 --- a/tools/objtool/include/objtool/objtool.h +++ b/tools/objtool/include/objtool/objtool.h @@ -14,6 +14,8 @@ =20 #define __weak __attribute__((weak)) =20 +#define SYM_CHECKSUM_SEC ".discard.sym_checksum" + struct pv_state { bool clean; struct list_head targets; --=20 2.45.2