summaryrefslogtreecommitdiffstats
path: root/tools/objtool/klp-checksum.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-08-19 10:09:22 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-08-19 10:09:22 -0700
commitf4cdf7ca9a1fdcca413157df19753f388a5a224e (patch)
tree60a31895b2446cd7fa27514e8c2843534a0de0da /tools/objtool/klp-checksum.c
downloadlinux-stable-f4cdf7ca9a1fdcca413157df19753f388a5a224e.tar.gz
linux-stable-f4cdf7ca9a1fdcca413157df19753f388a5a224e.zip
Merge tag 'media/v7.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-mediagrafted
Pull media updates from Mauro Carvalho Chehab: - v4l2-core: added ISP statistics support and per-block validation - v4l2-core: Allow unknown HDR10 white point and luminance - New camera sensors: Sony IMX678 and IMX471m, Himax HM1092 IR sensor - New codec: Milos: VPU v2.0 codec support - isp driver: gained support for Dreamchip RPPX1 ISP framework - vsp1 driver: gained support for RZ/T2H and RZ/N2H - Novalake driver: gained CVS support for new NVL hardware - dvb-core: fix feed leak on failed DMX_ADD_PID - several driver fixes, cleanups and minor improvements * tag 'media/v7.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (308 commits) media: ipu-bridge: check all DMI entries when overriding sensor rotation media: v4l2-async: avoid deleting unlinked ASC entry on link error media: rzg2l-cru: Align bytesperline to hardware DMA stride requirement media: intel/ipu6: fix async notifier cleanup leak on parse error media: staging/ipu7: fix async notifier UAF on probe error path media: amd: isp4: fix self-deadlock in isp4sd_pwron_and_init() error path media: amd: isp4: release partial allocations in isp4if_alloc_fw_gpumem() media: rcar-isp: Fix VSPX reference leaks media: rcar-isp: Release ISPCORE resources media: i2c: imx415: Release runtime PM reference on VBLANK error media: i2c: imx415: Return test pattern write errors media: renesas: vsp1: Declare index variables in for loop statement media: renesas: vsp1: Make reset control optional to support platforms without a reset line media: dt-bindings: media: renesas,vsp1: Document RZ/T2H and RZ/N2H SoCs media: dt-bindings: media: renesas,fcp: Document RZ/T2H and RZ/N2H SoCs media: nxp: imx8-isi: Add additional 32-bit RGB format support media: nxp: imx8-isi: Add 16-bit raw Bayer format support media: nxp: imx8-isi: Implement per-stream reference counting for multiplexed streams media: nxp: imx8-isi: Use BIT_ULL() for 64-bit stream masks media: nxp: imx8-isi: Correct color map between V4L2 and ISI ...
Diffstat (limited to 'tools/objtool/klp-checksum.c')
-rw-r--r--tools/objtool/klp-checksum.c347
1 files changed, 347 insertions, 0 deletions
diff --git a/tools/objtool/klp-checksum.c b/tools/objtool/klp-checksum.c
new file mode 100644
index 000000000..b8e47f289
--- /dev/null
+++ b/tools/objtool/klp-checksum.c
@@ -0,0 +1,347 @@
+// 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) && !is_object_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;
+
+ if (!reloc) {
+ struct symbol *call_dest = insn_call_dest(insn);
+ struct instruction *jump_dest = insn->jump_dest;
+
+ /*
+ * For a jump/call non-relocated dest offset embedded in the
+ * instruction, the offset may vary due to changes in
+ * surrounding code. Just hash the opcode and a
+ * position-independent representation of the destination.
+ */
+
+ if (call_dest || jump_dest) {
+ unsigned char buf[16];
+ size_t len;
+
+ len = arch_jump_opcode_bytes(file, insn, buf);
+ __checksum_update_insn(func, insn, buf, len);
+
+ if (call_dest) {
+ __checksum_update_insn(func, insn, call_dest->demangled_name,
+ strlen(call_dest->demangled_name));
+
+ } else if (jump_dest) {
+ struct symbol *dest_sym;
+ unsigned long offset;
+
+ /*
+ * use insn->_sym instead of insn_sym() here.
+ * For alternative replacements, the latter
+ * would give the function of the code being
+ * replaced.
+ */
+ dest_sym = jump_dest->_sym;
+ if (!dest_sym)
+ goto alts;
+
+ __checksum_update_insn(func, insn, dest_sym->demangled_name,
+ strlen(dest_sym->demangled_name));
+
+ offset = jump_dest->offset - dest_sym->offset;
+ __checksum_update_insn(func, insn, &offset, sizeof(offset));
+ }
+
+ goto alts;
+ }
+ }
+
+ __checksum_update_insn(func, insn, insn->sec->data->d_buf + insn->offset, insn->len);
+
+ if (!reloc)
+ 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_insn(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_insn(func, insn, sym->demangled_name,
+ strlen(sym->demangled_name));
+ __checksum_update_insn(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_insn(func, insn, &alt->type,
+ sizeof(alt->type));
+
+ if (alt_group && alt_group->orig_group) {
+ struct instruction *alt_insn;
+
+ __checksum_update_insn(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 void checksum_update_object(struct objtool_file *file, struct symbol *sym)
+{
+ struct reloc *reloc;
+
+ __checksum_update_object(sym, 0, "len", &sym->len, sizeof(sym->len));
+
+ if (sym->sec->data->d_buf)
+ __checksum_update_object(sym, 0, "data",
+ sym->sec->data->d_buf + sym->offset,
+ sym->len);
+
+ sym_for_each_reloc(file->elf, sym, reloc) {
+ unsigned long sym_offset = reloc_offset(reloc) - sym->offset;
+ struct symbol *target = reloc->sym;
+ s64 offset;
+
+ offset = reloc_addend(reloc);
+
+ if (is_string_sec(target->sec)) {
+ char *str;
+
+ str = target->sec->data->d_buf + target->offset + offset;
+ __checksum_update_object(sym, sym_offset,
+ "reloc string", str, strlen(str));
+ continue;
+ }
+
+ if (is_sec_sym(target)) {
+ target = find_symbol_containing(reloc->sym->sec, offset);
+ if (!target)
+ continue;
+
+ offset -= target->offset;
+ }
+
+ __checksum_update_object(sym, sym_offset, "reloc name",
+ target->demangled_name,
+ strlen(target->demangled_name));
+ __checksum_update_object(sym, sym_offset, "reloc addend",
+ &offset, sizeof(offset));
+ }
+}
+
+int calculate_checksums(struct objtool_file *file)
+{
+ struct instruction *insn;
+ struct symbol *sym;
+
+ if (checksum_debug_init(file))
+ return -1;
+
+ for_each_sym(file->elf, sym) {
+
+ /*
+ * 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_cold_func(sym) || is_alias_sym(sym) || !sym->len ||
+ !sym->sec || !sym->sec->data)
+ continue;
+
+ if (is_func_sym(sym)) {
+ checksum_init(sym);
+ func_for_each_insn(file, sym, insn)
+ checksum_update_insn(file, sym, insn);
+ checksum_finish(sym);
+
+ } else if (is_object_sym(sym)) {
+ checksum_init(sym);
+ checksum_update_object(file, sym);
+ checksum_finish(sym);
+ }
+
+ }
+
+ 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, "syms", "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);
+}