1e1076461SJosh Poimboeuf // SPDX-License-Identifier: GPL-2.0-or-later 2e1076461SJosh Poimboeuf #include <string.h> 3e1076461SJosh Poimboeuf #include <subcmd/parse-options.h> 4e1076461SJosh Poimboeuf 5e1076461SJosh Poimboeuf #include <objtool/arch.h> 6e1076461SJosh Poimboeuf #include <objtool/builtin.h> 7e1076461SJosh Poimboeuf #include <objtool/check.h> 8e1076461SJosh Poimboeuf #include <objtool/elf.h> 9e1076461SJosh Poimboeuf #include <objtool/klp.h> 10e1076461SJosh Poimboeuf #include <objtool/objtool.h> 11e1076461SJosh Poimboeuf #include <objtool/warn.h> 12e1076461SJosh Poimboeuf #include <objtool/checksum.h> 13e1076461SJosh Poimboeuf 14e1076461SJosh Poimboeuf static int checksum_debug_init(struct objtool_file *file) 15e1076461SJosh Poimboeuf { 16e1076461SJosh Poimboeuf char *dup, *s; 17e1076461SJosh Poimboeuf 18e1076461SJosh Poimboeuf if (!opts.debug_checksum) 19e1076461SJosh Poimboeuf return 0; 20e1076461SJosh Poimboeuf 21e1076461SJosh Poimboeuf dup = strdup(opts.debug_checksum); 22e1076461SJosh Poimboeuf if (!dup) { 23e1076461SJosh Poimboeuf ERROR_GLIBC("strdup"); 24e1076461SJosh Poimboeuf return -1; 25e1076461SJosh Poimboeuf } 26e1076461SJosh Poimboeuf 27e1076461SJosh Poimboeuf s = dup; 28e1076461SJosh Poimboeuf while (*s) { 29e1076461SJosh Poimboeuf bool found = false; 30e1076461SJosh Poimboeuf struct symbol *sym; 31e1076461SJosh Poimboeuf char *comma; 32e1076461SJosh Poimboeuf 33e1076461SJosh Poimboeuf comma = strchr(s, ','); 34e1076461SJosh Poimboeuf if (comma) 35e1076461SJosh Poimboeuf *comma = '\0'; 36e1076461SJosh Poimboeuf 37e1076461SJosh Poimboeuf for_each_sym_by_name(file->elf, s, sym) { 38873a2208SJosh Poimboeuf if (!is_func_sym(sym) && !is_object_sym(sym)) 39e1076461SJosh Poimboeuf continue; 40e1076461SJosh Poimboeuf sym->debug_checksum = 1; 41e1076461SJosh Poimboeuf found = true; 42e1076461SJosh Poimboeuf } 43e1076461SJosh Poimboeuf 44e1076461SJosh Poimboeuf if (!found) 45e1076461SJosh Poimboeuf WARN("--debug-checksum: can't find '%s'", s); 46e1076461SJosh Poimboeuf 47e1076461SJosh Poimboeuf if (!comma) 48e1076461SJosh Poimboeuf break; 49e1076461SJosh Poimboeuf 50e1076461SJosh Poimboeuf s = comma + 1; 51e1076461SJosh Poimboeuf } 52e1076461SJosh Poimboeuf 53e1076461SJosh Poimboeuf free(dup); 54e1076461SJosh Poimboeuf return 0; 55e1076461SJosh Poimboeuf } 56e1076461SJosh Poimboeuf 57e1076461SJosh Poimboeuf static void checksum_update_insn(struct objtool_file *file, struct symbol *func, 58e1076461SJosh Poimboeuf struct instruction *insn) 59e1076461SJosh Poimboeuf { 60e1076461SJosh Poimboeuf struct reloc *reloc = insn_reloc(file, insn); 61e1076461SJosh Poimboeuf struct alternative *alt; 62e1076461SJosh Poimboeuf unsigned long offset; 63e1076461SJosh Poimboeuf struct symbol *sym; 64e1076461SJosh Poimboeuf static bool in_alt; 65e1076461SJosh Poimboeuf 66e1076461SJosh Poimboeuf if (insn->fake) 67e1076461SJosh Poimboeuf return; 68e1076461SJosh Poimboeuf 69e1076461SJosh Poimboeuf if (!reloc) { 70e1076461SJosh Poimboeuf struct symbol *call_dest = insn_call_dest(insn); 71*cca84cb1SJosh Poimboeuf struct instruction *jump_dest = insn->jump_dest; 72e1076461SJosh Poimboeuf 73*cca84cb1SJosh Poimboeuf /* 74*cca84cb1SJosh Poimboeuf * For a jump/call non-relocated dest offset embedded in the 75*cca84cb1SJosh Poimboeuf * instruction, the offset may vary due to changes in 76*cca84cb1SJosh Poimboeuf * surrounding code. Just hash the opcode and a 77*cca84cb1SJosh Poimboeuf * position-independent representation of the destination. 78*cca84cb1SJosh Poimboeuf */ 79*cca84cb1SJosh Poimboeuf 80*cca84cb1SJosh Poimboeuf if (call_dest || jump_dest) { 81*cca84cb1SJosh Poimboeuf unsigned char buf[16]; 82*cca84cb1SJosh Poimboeuf size_t len; 83*cca84cb1SJosh Poimboeuf 84*cca84cb1SJosh Poimboeuf len = arch_jump_opcode_bytes(file, insn, buf); 85*cca84cb1SJosh Poimboeuf __checksum_update_insn(func, insn, buf, len); 86*cca84cb1SJosh Poimboeuf 87*cca84cb1SJosh Poimboeuf if (call_dest) { 88873a2208SJosh Poimboeuf __checksum_update_insn(func, insn, call_dest->demangled_name, 89e1076461SJosh Poimboeuf strlen(call_dest->demangled_name)); 90*cca84cb1SJosh Poimboeuf 91*cca84cb1SJosh Poimboeuf } else if (jump_dest) { 92*cca84cb1SJosh Poimboeuf struct symbol *dest_sym; 93*cca84cb1SJosh Poimboeuf unsigned long offset; 94*cca84cb1SJosh Poimboeuf 95*cca84cb1SJosh Poimboeuf /* 96*cca84cb1SJosh Poimboeuf * use insn->_sym instead of insn_sym() here. 97*cca84cb1SJosh Poimboeuf * For alternative replacements, the latter 98*cca84cb1SJosh Poimboeuf * would give the function of the code being 99*cca84cb1SJosh Poimboeuf * replaced. 100*cca84cb1SJosh Poimboeuf */ 101*cca84cb1SJosh Poimboeuf dest_sym = jump_dest->_sym; 102*cca84cb1SJosh Poimboeuf if (!dest_sym) 103*cca84cb1SJosh Poimboeuf goto alts; 104*cca84cb1SJosh Poimboeuf 105*cca84cb1SJosh Poimboeuf __checksum_update_insn(func, insn, dest_sym->demangled_name, 106*cca84cb1SJosh Poimboeuf strlen(dest_sym->demangled_name)); 107*cca84cb1SJosh Poimboeuf 108*cca84cb1SJosh Poimboeuf offset = jump_dest->offset - dest_sym->offset; 109*cca84cb1SJosh Poimboeuf __checksum_update_insn(func, insn, &offset, sizeof(offset)); 110*cca84cb1SJosh Poimboeuf } 111*cca84cb1SJosh Poimboeuf 112e1076461SJosh Poimboeuf goto alts; 113e1076461SJosh Poimboeuf } 114*cca84cb1SJosh Poimboeuf } 115*cca84cb1SJosh Poimboeuf 116*cca84cb1SJosh Poimboeuf __checksum_update_insn(func, insn, insn->sec->data->d_buf + insn->offset, insn->len); 117*cca84cb1SJosh Poimboeuf 118*cca84cb1SJosh Poimboeuf if (!reloc) 119*cca84cb1SJosh Poimboeuf goto alts; 120e1076461SJosh Poimboeuf 121e1076461SJosh Poimboeuf sym = reloc->sym; 122e1076461SJosh Poimboeuf offset = arch_insn_adjusted_addend(insn, reloc); 123e1076461SJosh Poimboeuf 124e1076461SJosh Poimboeuf if (is_string_sec(sym->sec)) { 125e1076461SJosh Poimboeuf char *str; 126e1076461SJosh Poimboeuf 127e1076461SJosh Poimboeuf str = sym->sec->data->d_buf + sym->offset + offset; 128873a2208SJosh Poimboeuf __checksum_update_insn(func, insn, str, strlen(str)); 129e1076461SJosh Poimboeuf goto alts; 130e1076461SJosh Poimboeuf } 131e1076461SJosh Poimboeuf 132e1076461SJosh Poimboeuf if (is_sec_sym(sym)) { 133e1076461SJosh Poimboeuf sym = find_symbol_containing(reloc->sym->sec, offset); 134e1076461SJosh Poimboeuf if (!sym) 135e1076461SJosh Poimboeuf goto alts; 136e1076461SJosh Poimboeuf 137e1076461SJosh Poimboeuf offset -= sym->offset; 138e1076461SJosh Poimboeuf } 139e1076461SJosh Poimboeuf 140873a2208SJosh Poimboeuf __checksum_update_insn(func, insn, sym->demangled_name, 141873a2208SJosh Poimboeuf strlen(sym->demangled_name)); 142873a2208SJosh Poimboeuf __checksum_update_insn(func, insn, &offset, sizeof(offset)); 143e1076461SJosh Poimboeuf 144e1076461SJosh Poimboeuf alts: 145e1076461SJosh Poimboeuf for (alt = insn->alts; alt; alt = alt->next) { 146e1076461SJosh Poimboeuf struct alt_group *alt_group = alt->insn->alt_group; 147e1076461SJosh Poimboeuf 148e1076461SJosh Poimboeuf /* Prevent __ex_table recursion, e.g. LOAD_SEGMENT() */ 149e1076461SJosh Poimboeuf if (in_alt) 150e1076461SJosh Poimboeuf break; 151e1076461SJosh Poimboeuf in_alt = true; 152e1076461SJosh Poimboeuf 153873a2208SJosh Poimboeuf __checksum_update_insn(func, insn, &alt->type, 154873a2208SJosh Poimboeuf sizeof(alt->type)); 155e1076461SJosh Poimboeuf 156e1076461SJosh Poimboeuf if (alt_group && alt_group->orig_group) { 157e1076461SJosh Poimboeuf struct instruction *alt_insn; 158e1076461SJosh Poimboeuf 159873a2208SJosh Poimboeuf __checksum_update_insn(func, insn, &alt_group->feature,sizeof(alt_group->feature)); 160e1076461SJosh Poimboeuf 161e1076461SJosh Poimboeuf for (alt_insn = alt->insn; alt_insn; alt_insn = next_insn_same_sec(file, alt_insn)) { 162e1076461SJosh Poimboeuf checksum_update_insn(file, func, alt_insn); 163e1076461SJosh Poimboeuf if (!alt_group->last_insn || alt_insn == alt_group->last_insn) 164e1076461SJosh Poimboeuf break; 165e1076461SJosh Poimboeuf } 166e1076461SJosh Poimboeuf } else { 167e1076461SJosh Poimboeuf checksum_update_insn(file, func, alt->insn); 168e1076461SJosh Poimboeuf } 169e1076461SJosh Poimboeuf 170e1076461SJosh Poimboeuf in_alt = false; 171e1076461SJosh Poimboeuf } 172e1076461SJosh Poimboeuf } 173e1076461SJosh Poimboeuf 174873a2208SJosh Poimboeuf static void checksum_update_object(struct objtool_file *file, struct symbol *sym) 175873a2208SJosh Poimboeuf { 176873a2208SJosh Poimboeuf struct reloc *reloc; 177873a2208SJosh Poimboeuf 178873a2208SJosh Poimboeuf __checksum_update_object(sym, 0, "len", &sym->len, sizeof(sym->len)); 179873a2208SJosh Poimboeuf 180873a2208SJosh Poimboeuf if (sym->sec->data->d_buf) 181873a2208SJosh Poimboeuf __checksum_update_object(sym, 0, "data", 182873a2208SJosh Poimboeuf sym->sec->data->d_buf + sym->offset, 183873a2208SJosh Poimboeuf sym->len); 184873a2208SJosh Poimboeuf 185873a2208SJosh Poimboeuf sym_for_each_reloc(file->elf, sym, reloc) { 186873a2208SJosh Poimboeuf unsigned long sym_offset = reloc_offset(reloc) - sym->offset; 187873a2208SJosh Poimboeuf struct symbol *target = reloc->sym; 188873a2208SJosh Poimboeuf s64 offset; 189873a2208SJosh Poimboeuf 190873a2208SJosh Poimboeuf offset = reloc_addend(reloc); 191873a2208SJosh Poimboeuf 192873a2208SJosh Poimboeuf if (is_string_sec(target->sec)) { 193873a2208SJosh Poimboeuf char *str; 194873a2208SJosh Poimboeuf 195873a2208SJosh Poimboeuf str = target->sec->data->d_buf + target->offset + offset; 196873a2208SJosh Poimboeuf __checksum_update_object(sym, sym_offset, 197873a2208SJosh Poimboeuf "reloc string", str, strlen(str)); 198873a2208SJosh Poimboeuf continue; 199873a2208SJosh Poimboeuf } 200873a2208SJosh Poimboeuf 201873a2208SJosh Poimboeuf if (is_sec_sym(target)) { 202873a2208SJosh Poimboeuf target = find_symbol_containing(reloc->sym->sec, offset); 203873a2208SJosh Poimboeuf if (!target) 204873a2208SJosh Poimboeuf continue; 205873a2208SJosh Poimboeuf 206873a2208SJosh Poimboeuf offset -= target->offset; 207873a2208SJosh Poimboeuf } 208873a2208SJosh Poimboeuf 209873a2208SJosh Poimboeuf __checksum_update_object(sym, sym_offset, "reloc name", 210873a2208SJosh Poimboeuf target->demangled_name, 211873a2208SJosh Poimboeuf strlen(target->demangled_name)); 212873a2208SJosh Poimboeuf __checksum_update_object(sym, sym_offset, "reloc addend", 213873a2208SJosh Poimboeuf &offset, sizeof(offset)); 214873a2208SJosh Poimboeuf } 215873a2208SJosh Poimboeuf } 216873a2208SJosh Poimboeuf 217e1076461SJosh Poimboeuf int calculate_checksums(struct objtool_file *file) 218e1076461SJosh Poimboeuf { 219e1076461SJosh Poimboeuf struct instruction *insn; 220873a2208SJosh Poimboeuf struct symbol *sym; 221e1076461SJosh Poimboeuf 222e1076461SJosh Poimboeuf if (checksum_debug_init(file)) 223e1076461SJosh Poimboeuf return -1; 224e1076461SJosh Poimboeuf 225873a2208SJosh Poimboeuf for_each_sym(file->elf, sym) { 226873a2208SJosh Poimboeuf 227e1076461SJosh Poimboeuf /* 228e1076461SJosh Poimboeuf * Skip cold subfunctions and aliases: they share the 229e1076461SJosh Poimboeuf * parent's checksum via func_for_each_insn() which 230e1076461SJosh Poimboeuf * follows func->cfunc into the cold subfunction. 231e1076461SJosh Poimboeuf */ 232873a2208SJosh Poimboeuf if (is_cold_func(sym) || is_alias_sym(sym) || !sym->len || 233873a2208SJosh Poimboeuf !sym->sec || !sym->sec->data) 234e1076461SJosh Poimboeuf continue; 235e1076461SJosh Poimboeuf 236873a2208SJosh Poimboeuf if (is_func_sym(sym)) { 237873a2208SJosh Poimboeuf checksum_init(sym); 238873a2208SJosh Poimboeuf func_for_each_insn(file, sym, insn) 239873a2208SJosh Poimboeuf checksum_update_insn(file, sym, insn); 240873a2208SJosh Poimboeuf checksum_finish(sym); 241e1076461SJosh Poimboeuf 242873a2208SJosh Poimboeuf } else if (is_object_sym(sym)) { 243873a2208SJosh Poimboeuf checksum_init(sym); 244873a2208SJosh Poimboeuf checksum_update_object(file, sym); 245873a2208SJosh Poimboeuf checksum_finish(sym); 246e1076461SJosh Poimboeuf } 247873a2208SJosh Poimboeuf 248873a2208SJosh Poimboeuf } 249873a2208SJosh Poimboeuf 250e1076461SJosh Poimboeuf return 0; 251e1076461SJosh Poimboeuf } 252e1076461SJosh Poimboeuf 253e1076461SJosh Poimboeuf int create_sym_checksum_section(struct objtool_file *file) 254e1076461SJosh Poimboeuf { 255e1076461SJosh Poimboeuf struct section *sec; 256e1076461SJosh Poimboeuf struct symbol *sym; 257e1076461SJosh Poimboeuf unsigned int idx = 0; 258e1076461SJosh Poimboeuf struct sym_checksum *checksum; 259e1076461SJosh Poimboeuf size_t entsize = sizeof(struct sym_checksum); 260e1076461SJosh Poimboeuf 261e1076461SJosh Poimboeuf sec = find_section_by_name(file->elf, ".discard.sym_checksum"); 262e1076461SJosh Poimboeuf if (sec) { 263e1076461SJosh Poimboeuf if (!opts.dryrun) 264e1076461SJosh Poimboeuf WARN("file already has .discard.sym_checksum section, skipping"); 265e1076461SJosh Poimboeuf 266e1076461SJosh Poimboeuf return 0; 267e1076461SJosh Poimboeuf } 268e1076461SJosh Poimboeuf 269e1076461SJosh Poimboeuf for_each_sym(file->elf, sym) 270e1076461SJosh Poimboeuf if (sym->csum.checksum) 271e1076461SJosh Poimboeuf idx++; 272e1076461SJosh Poimboeuf 273e1076461SJosh Poimboeuf sec = elf_create_section_pair(file->elf, ".discard.sym_checksum", entsize, 274e1076461SJosh Poimboeuf idx, idx); 275e1076461SJosh Poimboeuf if (!sec) 276e1076461SJosh Poimboeuf return -1; 277e1076461SJosh Poimboeuf 278e1076461SJosh Poimboeuf idx = 0; 279e1076461SJosh Poimboeuf for_each_sym(file->elf, sym) { 280e1076461SJosh Poimboeuf if (!sym->csum.checksum) 281e1076461SJosh Poimboeuf continue; 282e1076461SJosh Poimboeuf 283e1076461SJosh Poimboeuf if (!elf_init_reloc(file->elf, sec->rsec, idx, idx * entsize, 284e1076461SJosh Poimboeuf sym, 0, R_TEXT64)) 285e1076461SJosh Poimboeuf return -1; 286e1076461SJosh Poimboeuf 287e1076461SJosh Poimboeuf checksum = (struct sym_checksum *)sec->data->d_buf + idx; 288e1076461SJosh Poimboeuf checksum->addr = 0; /* reloc */ 289e1076461SJosh Poimboeuf checksum->checksum = sym->csum.checksum; 290e1076461SJosh Poimboeuf 291e1076461SJosh Poimboeuf mark_sec_changed(file->elf, sec, true); 292e1076461SJosh Poimboeuf 293e1076461SJosh Poimboeuf idx++; 294e1076461SJosh Poimboeuf } 295e1076461SJosh Poimboeuf 296e1076461SJosh Poimboeuf return 0; 297e1076461SJosh Poimboeuf } 298e1076461SJosh Poimboeuf 299e1076461SJosh Poimboeuf static const char * const klp_checksum_usage[] = { 300e1076461SJosh Poimboeuf "objtool klp checksum [<options>] file.o", 301e1076461SJosh Poimboeuf NULL, 302e1076461SJosh Poimboeuf }; 303e1076461SJosh Poimboeuf 304e1076461SJosh Poimboeuf int cmd_klp_checksum(int argc, const char **argv) 305e1076461SJosh Poimboeuf { 306e1076461SJosh Poimboeuf struct objtool_file *file; 307e1076461SJosh Poimboeuf int ret; 308e1076461SJosh Poimboeuf 309e1076461SJosh Poimboeuf const struct option options[] = { 310873a2208SJosh Poimboeuf OPT_STRING(0, "debug-checksum", &opts.debug_checksum, "syms", "enable checksum debug output"), 311e1076461SJosh Poimboeuf OPT_BOOLEAN(0, "dry-run", &opts.dryrun, "don't write modifications"), 312e1076461SJosh Poimboeuf OPT_END(), 313e1076461SJosh Poimboeuf }; 314e1076461SJosh Poimboeuf 315e1076461SJosh Poimboeuf argc = parse_options(argc, argv, options, klp_checksum_usage, 0); 316e1076461SJosh Poimboeuf if (argc != 1) 317e1076461SJosh Poimboeuf usage_with_options(klp_checksum_usage, options); 318e1076461SJosh Poimboeuf 319e1076461SJosh Poimboeuf opts.checksum = true; 320e1076461SJosh Poimboeuf 321e1076461SJosh Poimboeuf objname = argv[0]; 322e1076461SJosh Poimboeuf 323e1076461SJosh Poimboeuf file = objtool_open_read(objname); 324e1076461SJosh Poimboeuf if (!file) 325e1076461SJosh Poimboeuf return 1; 326e1076461SJosh Poimboeuf 327e1076461SJosh Poimboeuf ret = decode_file(file); 328e1076461SJosh Poimboeuf if (ret) 329e1076461SJosh Poimboeuf goto out; 330e1076461SJosh Poimboeuf 331e1076461SJosh Poimboeuf ret = calculate_checksums(file); 332e1076461SJosh Poimboeuf if (ret) 333e1076461SJosh Poimboeuf goto out; 334e1076461SJosh Poimboeuf 335e1076461SJosh Poimboeuf ret = create_sym_checksum_section(file); 336e1076461SJosh Poimboeuf 337e1076461SJosh Poimboeuf out: 338e1076461SJosh Poimboeuf free_insns(file); 339e1076461SJosh Poimboeuf 340e1076461SJosh Poimboeuf if (ret) 341e1076461SJosh Poimboeuf return ret; 342e1076461SJosh Poimboeuf 343e1076461SJosh Poimboeuf if (!opts.dryrun && file->elf->changed && elf_write(file->elf)) 344e1076461SJosh Poimboeuf return 1; 345e1076461SJosh Poimboeuf 346e1076461SJosh Poimboeuf return elf_close(file->elf); 347e1076461SJosh Poimboeuf } 348