1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (C) 2015-2017 Josh Poimboeuf <jpoimboe@redhat.com> 4 */ 5 6 #define _GNU_SOURCE 7 #include <fnmatch.h> 8 9 #include <objtool/arch.h> 10 #include <objtool/check.h> 11 #include <objtool/disas.h> 12 #include <objtool/special.h> 13 #include <objtool/warn.h> 14 15 #include <bfd.h> 16 #include <linux/string.h> 17 #include <tools/dis-asm-compat.h> 18 19 /* 20 * Size of the buffer for storing the result of disassembling 21 * a single instruction. 22 */ 23 #define DISAS_RESULT_SIZE 1024 24 25 struct disas_context { 26 struct objtool_file *file; 27 struct instruction *insn; 28 bool alt_applied; 29 char result[DISAS_RESULT_SIZE]; 30 disassembler_ftype disassembler; 31 struct disassemble_info info; 32 }; 33 34 /* 35 * Maximum number of alternatives 36 */ 37 #define DISAS_ALT_MAX 5 38 39 /* 40 * Maximum number of instructions per alternative 41 */ 42 #define DISAS_ALT_INSN_MAX 50 43 44 /* 45 * Information to disassemble an alternative 46 */ 47 struct disas_alt { 48 struct instruction *orig_insn; /* original instruction */ 49 struct alternative *alt; /* alternative or NULL if default code */ 50 char *name; /* name for this alternative */ 51 int width; /* formatting width */ 52 struct { 53 char *str; /* instruction string */ 54 int offset; /* instruction offset */ 55 int nops; /* number of nops */ 56 } insn[DISAS_ALT_INSN_MAX]; /* alternative instructions */ 57 int insn_idx; /* index of the next instruction to print */ 58 }; 59 60 #define DALT_DEFAULT(dalt) (!(dalt)->alt) 61 #define DALT_INSN(dalt) (DALT_DEFAULT(dalt) ? (dalt)->orig_insn : (dalt)->alt->insn) 62 #define DALT_GROUP(dalt) (DALT_INSN(dalt)->alt_group) 63 #define DALT_ALTID(dalt) ((dalt)->orig_insn->offset) 64 65 #define ALT_FLAGS_SHIFT 16 66 #define ALT_FLAG_NOT (1 << 0) 67 #define ALT_FLAG_DIRECT_CALL (1 << 1) 68 #define ALT_FEATURE_MASK ((1 << ALT_FLAGS_SHIFT) - 1) 69 70 static int alt_feature(unsigned int ft_flags) 71 { 72 return (ft_flags & ALT_FEATURE_MASK); 73 } 74 75 static int alt_flags(unsigned int ft_flags) 76 { 77 return (ft_flags >> ALT_FLAGS_SHIFT); 78 } 79 80 /* 81 * Wrapper around asprintf() to allocate and format a string. 82 * Return the allocated string or NULL on error. 83 */ 84 static char *strfmt(const char *fmt, ...) 85 { 86 va_list ap; 87 char *str; 88 int rv; 89 90 va_start(ap, fmt); 91 rv = vasprintf(&str, fmt, ap); 92 va_end(ap); 93 94 return rv == -1 ? NULL : str; 95 } 96 97 static int sprint_name(char *str, const char *name, unsigned long offset) 98 { 99 int len; 100 101 if (offset) 102 len = sprintf(str, "%s+0x%lx", name, offset); 103 else 104 len = sprintf(str, "%s", name); 105 106 return len; 107 } 108 109 #define DINFO_FPRINTF(dinfo, ...) \ 110 ((*(dinfo)->fprintf_func)((dinfo)->stream, __VA_ARGS__)) 111 112 static int disas_result_fprintf(struct disas_context *dctx, 113 const char *fmt, va_list ap) 114 { 115 char *buf = dctx->result; 116 int avail, len; 117 118 len = strlen(buf); 119 if (len >= DISAS_RESULT_SIZE - 1) { 120 WARN_FUNC(dctx->insn->sec, dctx->insn->offset, 121 "disassembly buffer is full"); 122 return -1; 123 } 124 avail = DISAS_RESULT_SIZE - len; 125 126 len = vsnprintf(buf + len, avail, fmt, ap); 127 if (len < 0 || len >= avail) { 128 WARN_FUNC(dctx->insn->sec, dctx->insn->offset, 129 "disassembly buffer is truncated"); 130 return -1; 131 } 132 133 return 0; 134 } 135 136 static int disas_fprintf(void *stream, const char *fmt, ...) 137 { 138 va_list arg; 139 int rv; 140 141 va_start(arg, fmt); 142 rv = disas_result_fprintf(stream, fmt, arg); 143 va_end(arg); 144 145 return rv; 146 } 147 148 /* 149 * For init_disassemble_info_compat(). 150 */ 151 static int disas_fprintf_styled(void *stream, 152 enum disassembler_style style, 153 const char *fmt, ...) 154 { 155 va_list arg; 156 int rv; 157 158 va_start(arg, fmt); 159 rv = disas_result_fprintf(stream, fmt, arg); 160 va_end(arg); 161 162 return rv; 163 } 164 165 static void disas_print_addr_sym(struct section *sec, struct symbol *sym, 166 bfd_vma addr, struct disassemble_info *dinfo) 167 { 168 char symstr[1024]; 169 char *str; 170 171 if (sym) { 172 sprint_name(symstr, sym->name, addr - sym->offset); 173 DINFO_FPRINTF(dinfo, "0x%lx <%s>", addr, symstr); 174 } else { 175 str = offstr(sec, addr); 176 DINFO_FPRINTF(dinfo, "0x%lx <%s>", addr, str); 177 free(str); 178 } 179 } 180 181 static bool disas_print_addr_alt(bfd_vma addr, struct disassemble_info *dinfo) 182 { 183 struct disas_context *dctx = dinfo->application_data; 184 struct instruction *orig_first_insn; 185 struct alt_group *alt_group; 186 unsigned long offset; 187 struct symbol *sym; 188 189 /* 190 * Check if we are processing an alternative at the original 191 * instruction address (i.e. if alt_applied is true) and if 192 * we are referencing an address inside the alternative. 193 * 194 * For example, this happens if there is a branch inside an 195 * alternative. In that case, the address should be updated 196 * to a reference inside the original instruction flow. 197 */ 198 if (!dctx->alt_applied) 199 return false; 200 201 alt_group = dctx->insn->alt_group; 202 if (!alt_group || !alt_group->orig_group || 203 addr < alt_group->first_insn->offset || 204 addr > alt_group->last_insn->offset) 205 return false; 206 207 orig_first_insn = alt_group->orig_group->first_insn; 208 offset = addr - alt_group->first_insn->offset; 209 210 addr = orig_first_insn->offset + offset; 211 sym = orig_first_insn->sym; 212 213 disas_print_addr_sym(orig_first_insn->sec, sym, addr, dinfo); 214 215 return true; 216 } 217 218 static void disas_print_addr_noreloc(bfd_vma addr, 219 struct disassemble_info *dinfo) 220 { 221 struct disas_context *dctx = dinfo->application_data; 222 struct instruction *insn = dctx->insn; 223 struct symbol *sym = NULL; 224 225 if (disas_print_addr_alt(addr, dinfo)) 226 return; 227 228 if (insn->sym && addr >= insn->sym->offset && 229 addr < insn->sym->offset + insn->sym->len) { 230 sym = insn->sym; 231 } 232 233 disas_print_addr_sym(insn->sec, sym, addr, dinfo); 234 } 235 236 static void disas_print_addr_reloc(bfd_vma addr, struct disassemble_info *dinfo) 237 { 238 struct disas_context *dctx = dinfo->application_data; 239 struct instruction *insn = dctx->insn; 240 unsigned long offset; 241 struct reloc *reloc; 242 char symstr[1024]; 243 char *str; 244 245 reloc = find_reloc_by_dest_range(dctx->file->elf, insn->sec, 246 insn->offset, insn->len); 247 if (!reloc) { 248 /* 249 * There is no relocation for this instruction although 250 * the address to resolve points to the next instruction. 251 * So this is an effective reference to the next IP, for 252 * example: "lea 0x0(%rip),%rdi". The kernel can reference 253 * the next IP with _THIS_IP_ macro. 254 */ 255 DINFO_FPRINTF(dinfo, "0x%lx <_THIS_IP_>", addr); 256 return; 257 } 258 259 offset = arch_insn_adjusted_addend(insn, reloc); 260 261 /* 262 * If the relocation symbol is a section name (for example ".bss") 263 * then we try to further resolve the name. 264 */ 265 if (reloc->sym->type == STT_SECTION) { 266 str = offstr(reloc->sym->sec, reloc->sym->offset + offset); 267 DINFO_FPRINTF(dinfo, "0x%lx <%s>", addr, str); 268 free(str); 269 } else { 270 sprint_name(symstr, reloc->sym->name, offset); 271 DINFO_FPRINTF(dinfo, "0x%lx <%s>", addr, symstr); 272 } 273 } 274 275 /* 276 * Resolve an address into a "<symbol>+<offset>" string. 277 */ 278 static void disas_print_address(bfd_vma addr, struct disassemble_info *dinfo) 279 { 280 struct disas_context *dctx = dinfo->application_data; 281 struct instruction *insn = dctx->insn; 282 struct instruction *jump_dest; 283 struct symbol *sym; 284 bool is_reloc; 285 286 /* 287 * If the instruction is a call/jump and it references a 288 * destination then this is likely the address we are looking 289 * up. So check it first. 290 */ 291 jump_dest = insn->jump_dest; 292 if (jump_dest && jump_dest->sym && jump_dest->offset == addr) { 293 if (!disas_print_addr_alt(addr, dinfo)) 294 disas_print_addr_sym(jump_dest->sec, jump_dest->sym, 295 addr, dinfo); 296 return; 297 } 298 299 /* 300 * If the address points to the next instruction then there is 301 * probably a relocation. It can be a false positive when the 302 * current instruction is referencing the address of the next 303 * instruction. This particular case will be handled in 304 * disas_print_addr_reloc(). 305 */ 306 is_reloc = (addr == insn->offset + insn->len); 307 308 /* 309 * The call destination offset can be the address we are looking 310 * up, or 0 if there is a relocation. 311 */ 312 sym = insn_call_dest(insn); 313 if (sym && (sym->offset == addr || (sym->offset == 0 && is_reloc))) { 314 DINFO_FPRINTF(dinfo, "0x%lx <%s>", addr, sym->name); 315 return; 316 } 317 318 if (!is_reloc) 319 disas_print_addr_noreloc(addr, dinfo); 320 else 321 disas_print_addr_reloc(addr, dinfo); 322 } 323 324 /* 325 * Initialize disassemble info arch, mach (32 or 64-bit) and options. 326 */ 327 int disas_info_init(struct disassemble_info *dinfo, 328 int arch, int mach32, int mach64, 329 const char *options) 330 { 331 struct disas_context *dctx = dinfo->application_data; 332 struct objtool_file *file = dctx->file; 333 334 dinfo->arch = arch; 335 336 switch (file->elf->ehdr.e_ident[EI_CLASS]) { 337 case ELFCLASS32: 338 dinfo->mach = mach32; 339 break; 340 case ELFCLASS64: 341 dinfo->mach = mach64; 342 break; 343 default: 344 return -1; 345 } 346 347 dinfo->disassembler_options = options; 348 349 return 0; 350 } 351 352 struct disas_context *disas_context_create(struct objtool_file *file) 353 { 354 struct disas_context *dctx; 355 struct disassemble_info *dinfo; 356 int err; 357 358 dctx = malloc(sizeof(*dctx)); 359 if (!dctx) { 360 WARN("failed to allocate disassembly context"); 361 return NULL; 362 } 363 364 dctx->file = file; 365 dinfo = &dctx->info; 366 367 init_disassemble_info_compat(dinfo, dctx, 368 disas_fprintf, disas_fprintf_styled); 369 370 dinfo->read_memory_func = buffer_read_memory; 371 dinfo->print_address_func = disas_print_address; 372 dinfo->application_data = dctx; 373 374 /* 375 * bfd_openr() is not used to avoid doing ELF data processing 376 * and caching that has already being done. Here, we just need 377 * to identify the target file so we call an arch specific 378 * function to fill some disassemble info (arch, mach). 379 */ 380 381 dinfo->arch = bfd_arch_unknown; 382 dinfo->mach = 0; 383 384 err = arch_disas_info_init(dinfo); 385 if (err || dinfo->arch == bfd_arch_unknown || dinfo->mach == 0) { 386 WARN("failed to init disassembly arch"); 387 goto error; 388 } 389 390 dinfo->endian = (file->elf->ehdr.e_ident[EI_DATA] == ELFDATA2MSB) ? 391 BFD_ENDIAN_BIG : BFD_ENDIAN_LITTLE; 392 393 disassemble_init_for_target(dinfo); 394 395 dctx->disassembler = disassembler(dinfo->arch, 396 dinfo->endian == BFD_ENDIAN_BIG, 397 dinfo->mach, NULL); 398 if (!dctx->disassembler) { 399 WARN("failed to create disassembler function"); 400 goto error; 401 } 402 403 return dctx; 404 405 error: 406 free(dctx); 407 return NULL; 408 } 409 410 void disas_context_destroy(struct disas_context *dctx) 411 { 412 free(dctx); 413 } 414 415 char *disas_result(struct disas_context *dctx) 416 { 417 return dctx->result; 418 } 419 420 #define DISAS_INSN_OFFSET_SPACE 10 421 #define DISAS_INSN_SPACE 60 422 423 #define DISAS_PRINSN(dctx, insn, depth) \ 424 disas_print_insn(stdout, dctx, insn, depth, "\n") 425 426 /* 427 * Print a message in the instruction flow. If sec is not NULL then the 428 * address at the section offset is printed in addition of the message, 429 * otherwise only the message is printed. 430 */ 431 static int disas_vprint(FILE *stream, struct section *sec, unsigned long offset, 432 int depth, const char *format, va_list ap) 433 { 434 const char *addr_str; 435 int i, n; 436 int len; 437 438 len = sym_name_max_len + DISAS_INSN_OFFSET_SPACE; 439 if (depth < 0) { 440 len += depth; 441 depth = 0; 442 } 443 444 n = 0; 445 446 if (sec) { 447 addr_str = offstr(sec, offset); 448 n += fprintf(stream, "%6lx: %-*s ", offset, len, addr_str); 449 free((char *)addr_str); 450 } else { 451 len += DISAS_INSN_OFFSET_SPACE + 1; 452 n += fprintf(stream, "%-*s", len, ""); 453 } 454 455 /* print vertical bars to show the code flow */ 456 for (i = 0; i < depth; i++) 457 n += fprintf(stream, "| "); 458 459 if (format) 460 n += vfprintf(stream, format, ap); 461 462 return n; 463 } 464 465 static int disas_print(FILE *stream, struct section *sec, unsigned long offset, 466 int depth, const char *format, ...) 467 { 468 va_list args; 469 int len; 470 471 va_start(args, format); 472 len = disas_vprint(stream, sec, offset, depth, format, args); 473 va_end(args); 474 475 return len; 476 } 477 478 /* 479 * Print a message in the instruction flow. If insn is not NULL then 480 * the instruction address is printed in addition of the message, 481 * otherwise only the message is printed. In all cases, the instruction 482 * itself is not printed. 483 */ 484 void disas_print_info(FILE *stream, struct instruction *insn, int depth, 485 const char *format, ...) 486 { 487 struct section *sec; 488 unsigned long off; 489 va_list args; 490 491 if (insn) { 492 sec = insn->sec; 493 off = insn->offset; 494 } else { 495 sec = NULL; 496 off = 0; 497 } 498 499 va_start(args, format); 500 disas_vprint(stream, sec, off, depth, format, args); 501 va_end(args); 502 } 503 504 /* 505 * Print an instruction address (offset and function), the instruction itself 506 * and an optional message. 507 */ 508 void disas_print_insn(FILE *stream, struct disas_context *dctx, 509 struct instruction *insn, int depth, 510 const char *format, ...) 511 { 512 char fake_nop_insn[32]; 513 const char *insn_str; 514 bool fake_nop; 515 va_list args; 516 int len; 517 518 /* 519 * Alternative can insert a fake nop, sometimes with no 520 * associated section so nothing to disassemble. 521 */ 522 fake_nop = (!insn->sec && insn->type == INSN_NOP); 523 if (fake_nop) { 524 snprintf(fake_nop_insn, 32, "<fake nop> (%d bytes)", insn->len); 525 insn_str = fake_nop_insn; 526 } else { 527 disas_insn(dctx, insn); 528 insn_str = disas_result(dctx); 529 } 530 531 /* print the instruction */ 532 len = (depth + 1) * 2 < DISAS_INSN_SPACE ? DISAS_INSN_SPACE - (depth+1) * 2 : 1; 533 disas_print_info(stream, insn, depth, "%-*s", len, insn_str); 534 535 /* print message if any */ 536 if (!format) 537 return; 538 539 if (strcmp(format, "\n") == 0) { 540 fprintf(stream, "\n"); 541 return; 542 } 543 544 fprintf(stream, " - "); 545 va_start(args, format); 546 vfprintf(stream, format, args); 547 va_end(args); 548 } 549 550 /* 551 * Disassemble a single instruction. Return the size of the instruction. 552 * 553 * If alt_applied is true then insn should be an instruction from of an 554 * alternative (i.e. insn->alt_group != NULL), and it is disassembled 555 * at the location of the original code it is replacing. When the 556 * instruction references any address inside the alternative then 557 * these references will be re-adjusted to replace the original code. 558 */ 559 static size_t disas_insn_common(struct disas_context *dctx, 560 struct instruction *insn, 561 bool alt_applied) 562 { 563 disassembler_ftype disasm = dctx->disassembler; 564 struct disassemble_info *dinfo = &dctx->info; 565 566 dctx->insn = insn; 567 dctx->alt_applied = alt_applied; 568 dctx->result[0] = '\0'; 569 570 if (insn->type == INSN_NOP) { 571 DINFO_FPRINTF(dinfo, "nop%d", insn->len); 572 return insn->len; 573 } 574 575 /* 576 * Set the disassembler buffer to read data from the section 577 * containing the instruction to disassemble. 578 */ 579 dinfo->buffer = insn->sec->data->d_buf; 580 dinfo->buffer_vma = 0; 581 dinfo->buffer_length = insn->sec->sh.sh_size; 582 583 return disasm(insn->offset, &dctx->info); 584 } 585 586 size_t disas_insn(struct disas_context *dctx, struct instruction *insn) 587 { 588 return disas_insn_common(dctx, insn, false); 589 } 590 591 static size_t disas_insn_alt(struct disas_context *dctx, 592 struct instruction *insn) 593 { 594 return disas_insn_common(dctx, insn, true); 595 } 596 597 static struct instruction *next_insn_same_alt(struct objtool_file *file, 598 struct alt_group *alt_grp, 599 struct instruction *insn) 600 { 601 if (alt_grp->last_insn == insn || alt_grp->nop == insn) 602 return NULL; 603 604 return next_insn_same_sec(file, insn); 605 } 606 607 #define alt_for_each_insn(file, alt_grp, insn) \ 608 for (insn = alt_grp->first_insn; \ 609 insn; \ 610 insn = next_insn_same_alt(file, alt_grp, insn)) 611 612 /* 613 * Provide a name for the type of alternatives present at the 614 * specified instruction. 615 * 616 * An instruction can have alternatives with different types, for 617 * example alternative instructions and an exception table. In that 618 * case the name for the alternative instructions type is used. 619 * 620 * Return NULL if the instruction as no alternative. 621 */ 622 const char *disas_alt_type_name(struct instruction *insn) 623 { 624 struct alternative *alt; 625 const char *name; 626 627 name = NULL; 628 for (alt = insn->alts; alt; alt = alt->next) { 629 if (alt->type == ALT_TYPE_INSTRUCTIONS) { 630 name = "alternative"; 631 break; 632 } 633 634 switch (alt->type) { 635 case ALT_TYPE_EX_TABLE: 636 name = "ex_table"; 637 break; 638 case ALT_TYPE_JUMP_TABLE: 639 name = "jump_table"; 640 break; 641 default: 642 name = "unknown"; 643 break; 644 } 645 } 646 647 return name; 648 } 649 650 /* 651 * Provide a name for an alternative. 652 */ 653 char *disas_alt_name(struct alternative *alt) 654 { 655 char pfx[4] = { 0 }; 656 char *str = NULL; 657 const char *name; 658 int feature; 659 int flags; 660 int num; 661 662 switch (alt->type) { 663 664 case ALT_TYPE_EX_TABLE: 665 str = strdup("EXCEPTION"); 666 break; 667 668 case ALT_TYPE_JUMP_TABLE: 669 str = strdup("JUMP"); 670 break; 671 672 case ALT_TYPE_INSTRUCTIONS: 673 /* 674 * This is a non-default group alternative. Create a name 675 * based on the feature and flags associated with this 676 * alternative. Use either the feature name (it is available) 677 * or the feature number. And add a prefix to show the flags 678 * used. 679 * 680 * Prefix flags characters: 681 * 682 * '!' alternative used when feature not enabled 683 * '+' direct call alternative 684 * '?' unknown flag 685 */ 686 687 if (!alt->insn->alt_group) 688 return NULL; 689 690 feature = alt->insn->alt_group->feature; 691 num = alt_feature(feature); 692 flags = alt_flags(feature); 693 str = pfx; 694 695 if (flags & ~(ALT_FLAG_NOT | ALT_FLAG_DIRECT_CALL)) 696 *str++ = '?'; 697 if (flags & ALT_FLAG_DIRECT_CALL) 698 *str++ = '+'; 699 if (flags & ALT_FLAG_NOT) 700 *str++ = '!'; 701 702 name = arch_cpu_feature_name(num); 703 if (!name) 704 str = strfmt("%sFEATURE 0x%X", pfx, num); 705 else 706 str = strfmt("%s%s", pfx, name); 707 708 break; 709 } 710 711 return str; 712 } 713 714 /* 715 * Initialize an alternative. The default alternative should be initialized 716 * with alt=NULL. 717 */ 718 static int disas_alt_init(struct disas_alt *dalt, 719 struct instruction *orig_insn, 720 struct alternative *alt) 721 { 722 dalt->orig_insn = orig_insn; 723 dalt->alt = alt; 724 dalt->insn_idx = 0; 725 dalt->name = alt ? disas_alt_name(alt) : strdup("DEFAULT"); 726 if (!dalt->name) 727 return -1; 728 dalt->width = strlen(dalt->name); 729 730 return 0; 731 } 732 733 static int disas_alt_add_insn(struct disas_alt *dalt, int index, char *insn_str, 734 int offset, int nops) 735 { 736 int len; 737 738 if (index >= DISAS_ALT_INSN_MAX) { 739 WARN("Alternative %lx.%s has more instructions than supported", 740 DALT_ALTID(dalt), dalt->name); 741 return -1; 742 } 743 744 len = strlen(insn_str); 745 dalt->insn[index].str = insn_str; 746 dalt->insn[index].offset = offset; 747 dalt->insn[index].nops = nops; 748 if (len > dalt->width) 749 dalt->width = len; 750 751 return 0; 752 } 753 754 static int disas_alt_jump(struct disas_alt *dalt) 755 { 756 struct instruction *orig_insn; 757 struct instruction *dest_insn; 758 char suffix[2] = { 0 }; 759 char *str; 760 int nops; 761 762 orig_insn = dalt->orig_insn; 763 dest_insn = dalt->alt->insn; 764 765 if (orig_insn->type == INSN_NOP) { 766 if (orig_insn->len == 5) 767 suffix[0] = 'q'; 768 str = strfmt("jmp%-3s %lx <%s+0x%lx>", suffix, 769 dest_insn->offset, dest_insn->sym->name, 770 dest_insn->offset - dest_insn->sym->offset); 771 nops = 0; 772 } else { 773 str = strfmt("nop%d", orig_insn->len); 774 nops = orig_insn->len; 775 } 776 777 if (!str) 778 return -1; 779 780 disas_alt_add_insn(dalt, 0, str, 0, nops); 781 782 return 1; 783 } 784 785 /* 786 * Disassemble an exception table alternative. 787 */ 788 static int disas_alt_extable(struct disas_alt *dalt) 789 { 790 struct instruction *alt_insn; 791 char *str; 792 793 alt_insn = dalt->alt->insn; 794 str = strfmt("resume at 0x%lx <%s+0x%lx>", 795 alt_insn->offset, alt_insn->sym->name, 796 alt_insn->offset - alt_insn->sym->offset); 797 if (!str) 798 return -1; 799 800 disas_alt_add_insn(dalt, 0, str, 0, 0); 801 802 return 1; 803 } 804 805 /* 806 * Disassemble an alternative and store instructions in the disas_alt 807 * structure. Return the number of instructions in the alternative. 808 */ 809 static int disas_alt_group(struct disas_context *dctx, struct disas_alt *dalt) 810 { 811 struct objtool_file *file; 812 struct instruction *insn; 813 int offset; 814 char *str; 815 int count; 816 int nops; 817 int err; 818 819 file = dctx->file; 820 count = 0; 821 offset = 0; 822 nops = 0; 823 824 alt_for_each_insn(file, DALT_GROUP(dalt), insn) { 825 826 disas_insn_alt(dctx, insn); 827 str = strdup(disas_result(dctx)); 828 if (!str) 829 return -1; 830 831 nops = insn->type == INSN_NOP ? insn->len : 0; 832 err = disas_alt_add_insn(dalt, count, str, offset, nops); 833 if (err) 834 break; 835 offset += insn->len; 836 count++; 837 } 838 839 return count; 840 } 841 842 /* 843 * Disassemble the default alternative. 844 */ 845 static int disas_alt_default(struct disas_context *dctx, struct disas_alt *dalt) 846 { 847 char *str; 848 int nops; 849 int err; 850 851 if (DALT_GROUP(dalt)) 852 return disas_alt_group(dctx, dalt); 853 854 /* 855 * Default alternative with no alt_group: this is the default 856 * code associated with either a jump table or an exception 857 * table and no other instruction alternatives. In that case 858 * the default alternative is made of a single instruction. 859 */ 860 disas_insn(dctx, dalt->orig_insn); 861 str = strdup(disas_result(dctx)); 862 if (!str) 863 return -1; 864 nops = dalt->orig_insn->type == INSN_NOP ? dalt->orig_insn->len : 0; 865 err = disas_alt_add_insn(dalt, 0, str, 0, nops); 866 if (err) 867 return -1; 868 869 return 1; 870 } 871 872 /* 873 * For each alternative, if there is an instruction at the specified 874 * offset then print this instruction, otherwise print a blank entry. 875 * The offset is an offset from the start of the alternative. 876 * 877 * Return the offset for the next instructions to print, or -1 if all 878 * instructions have been printed. 879 */ 880 static int disas_alt_print_insn(struct disas_alt *dalts, int alt_count, 881 int insn_count, int offset) 882 { 883 struct disas_alt *dalt; 884 int offset_next; 885 char *str; 886 int i, j; 887 888 offset_next = -1; 889 890 for (i = 0; i < alt_count; i++) { 891 dalt = &dalts[i]; 892 j = dalt->insn_idx; 893 if (j == -1) { 894 printf("| %-*s ", dalt->width, ""); 895 continue; 896 } 897 898 if (dalt->insn[j].offset == offset) { 899 str = dalt->insn[j].str; 900 printf("| %-*s ", dalt->width, str ?: ""); 901 if (++j < insn_count) { 902 dalt->insn_idx = j; 903 } else { 904 dalt->insn_idx = -1; 905 continue; 906 } 907 } else { 908 printf("| %-*s ", dalt->width, ""); 909 } 910 911 if (dalt->insn[j].offset > 0 && 912 (offset_next == -1 || 913 (dalt->insn[j].offset < offset_next))) 914 offset_next = dalt->insn[j].offset; 915 } 916 printf("\n"); 917 918 return offset_next; 919 } 920 921 /* 922 * Print all alternatives side-by-side. 923 */ 924 static void disas_alt_print_wide(char *alt_name, struct disas_alt *dalts, int alt_count, 925 int insn_count) 926 { 927 struct instruction *orig_insn; 928 int offset_next; 929 int offset; 930 int i; 931 932 orig_insn = dalts[0].orig_insn; 933 934 /* 935 * Print an header with the name of each alternative. 936 */ 937 disas_print_info(stdout, orig_insn, -2, NULL); 938 939 if (strlen(alt_name) > dalts[0].width) 940 dalts[0].width = strlen(alt_name); 941 printf("| %-*s ", dalts[0].width, alt_name); 942 943 for (i = 1; i < alt_count; i++) 944 printf("| %-*s ", dalts[i].width, dalts[i].name); 945 946 printf("\n"); 947 948 /* 949 * Print instructions for each alternative. 950 */ 951 offset_next = 0; 952 do { 953 offset = offset_next; 954 disas_print(stdout, orig_insn->sec, orig_insn->offset + offset, 955 -2, NULL); 956 offset_next = disas_alt_print_insn(dalts, alt_count, insn_count, 957 offset); 958 } while (offset_next > offset); 959 } 960 961 /* 962 * Print all alternatives one above the other. 963 */ 964 static void disas_alt_print_compact(char *alt_name, struct disas_alt *dalts, 965 int alt_count, int insn_count) 966 { 967 struct instruction *orig_insn; 968 int width; 969 int i, j; 970 int len; 971 972 orig_insn = dalts[0].orig_insn; 973 974 len = disas_print(stdout, orig_insn->sec, orig_insn->offset, 0, NULL); 975 printf("%s\n", alt_name); 976 977 /* 978 * If all alternatives have a single instruction then print each 979 * alternative on a single line. Otherwise, print alternatives 980 * one above the other with a clear separation. 981 */ 982 983 if (insn_count == 1) { 984 width = 0; 985 for (i = 0; i < alt_count; i++) { 986 if (dalts[i].width > width) 987 width = dalts[i].width; 988 } 989 990 for (i = 0; i < alt_count; i++) { 991 printf("%*s= %-*s (if %s)\n", len, "", width, 992 dalts[i].insn[0].str, dalts[i].name); 993 } 994 995 return; 996 } 997 998 for (i = 0; i < alt_count; i++) { 999 printf("%*s= %s\n", len, "", dalts[i].name); 1000 for (j = 0; j < insn_count; j++) { 1001 if (!dalts[i].insn[j].str) 1002 break; 1003 disas_print(stdout, orig_insn->sec, 1004 orig_insn->offset + dalts[i].insn[j].offset, 0, 1005 "| %s\n", dalts[i].insn[j].str); 1006 } 1007 printf("%*s|\n", len, ""); 1008 } 1009 } 1010 1011 /* 1012 * Trim NOPs in alternatives. This replaces trailing NOPs in alternatives 1013 * with a single indication of the number of bytes covered with NOPs. 1014 * 1015 * Return the maximum numbers of instructions in all alternatives after 1016 * trailing NOPs have been trimmed. 1017 */ 1018 static int disas_alt_trim_nops(struct disas_alt *dalts, int alt_count, 1019 int insn_count) 1020 { 1021 struct disas_alt *dalt; 1022 int nops_count; 1023 const char *s; 1024 int offset; 1025 int count; 1026 int nops; 1027 int i, j; 1028 1029 count = 0; 1030 for (i = 0; i < alt_count; i++) { 1031 offset = 0; 1032 nops = 0; 1033 nops_count = 0; 1034 dalt = &dalts[i]; 1035 for (j = insn_count - 1; j >= 0; j--) { 1036 if (!dalt->insn[j].str || !dalt->insn[j].nops) 1037 break; 1038 offset = dalt->insn[j].offset; 1039 free(dalt->insn[j].str); 1040 dalt->insn[j].offset = 0; 1041 dalt->insn[j].str = NULL; 1042 nops += dalt->insn[j].nops; 1043 nops_count++; 1044 } 1045 1046 /* 1047 * All trailing NOPs have been removed. If there was a single 1048 * NOP instruction then re-add it. If there was a block of 1049 * NOPs then indicate the number of bytes than the block 1050 * covers (nop*<number-of-bytes>). 1051 */ 1052 if (nops_count) { 1053 s = nops_count == 1 ? "" : "*"; 1054 dalt->insn[j + 1].str = strfmt("nop%s%d", s, nops); 1055 dalt->insn[j + 1].offset = offset; 1056 dalt->insn[j + 1].nops = nops; 1057 j++; 1058 } 1059 1060 if (j > count) 1061 count = j; 1062 } 1063 1064 return count + 1; 1065 } 1066 1067 /* 1068 * Disassemble an alternative. 1069 * 1070 * Return the last instruction in the default alternative so that 1071 * disassembly can continue with the next instruction. Return NULL 1072 * on error. 1073 */ 1074 static void *disas_alt(struct disas_context *dctx, 1075 struct instruction *orig_insn) 1076 { 1077 struct disas_alt dalts[DISAS_ALT_MAX] = { 0 }; 1078 struct instruction *last_insn = NULL; 1079 struct alternative *alt; 1080 struct disas_alt *dalt; 1081 int insn_count = 0; 1082 int alt_count = 0; 1083 char *alt_name; 1084 int count; 1085 int i, j; 1086 int err; 1087 1088 alt_name = strfmt("<%s.%lx>", disas_alt_type_name(orig_insn), 1089 orig_insn->offset); 1090 if (!alt_name) { 1091 WARN("Failed to define name for alternative at instruction 0x%lx", 1092 orig_insn->offset); 1093 goto done; 1094 } 1095 1096 /* 1097 * Initialize and disassemble the default alternative. 1098 */ 1099 err = disas_alt_init(&dalts[0], orig_insn, NULL); 1100 if (err) { 1101 WARN("%s: failed to initialize default alternative", alt_name); 1102 goto done; 1103 } 1104 1105 insn_count = disas_alt_default(dctx, &dalts[0]); 1106 if (insn_count < 0) { 1107 WARN("%s: failed to disassemble default alternative", alt_name); 1108 goto done; 1109 } 1110 1111 /* 1112 * Initialize and disassemble all other alternatives. 1113 */ 1114 i = 1; 1115 for (alt = orig_insn->alts; alt; alt = alt->next) { 1116 if (i >= DISAS_ALT_MAX) { 1117 WARN("%s has more alternatives than supported", alt_name); 1118 break; 1119 } 1120 1121 dalt = &dalts[i]; 1122 err = disas_alt_init(dalt, orig_insn, alt); 1123 if (err) { 1124 WARN("%s: failed to disassemble alternative", alt_name); 1125 goto done; 1126 } 1127 1128 count = -1; 1129 switch (dalt->alt->type) { 1130 case ALT_TYPE_INSTRUCTIONS: 1131 count = disas_alt_group(dctx, dalt); 1132 break; 1133 case ALT_TYPE_EX_TABLE: 1134 count = disas_alt_extable(dalt); 1135 break; 1136 case ALT_TYPE_JUMP_TABLE: 1137 count = disas_alt_jump(dalt); 1138 break; 1139 } 1140 if (count < 0) { 1141 WARN("%s: failed to disassemble alternative %s", 1142 alt_name, dalt->name); 1143 goto done; 1144 } 1145 1146 insn_count = count > insn_count ? count : insn_count; 1147 i++; 1148 } 1149 alt_count = i; 1150 1151 /* 1152 * Print default and non-default alternatives. 1153 */ 1154 1155 insn_count = disas_alt_trim_nops(dalts, alt_count, insn_count); 1156 1157 if (opts.wide) 1158 disas_alt_print_wide(alt_name, dalts, alt_count, insn_count); 1159 else 1160 disas_alt_print_compact(alt_name, dalts, alt_count, insn_count); 1161 1162 last_insn = orig_insn->alt_group ? orig_insn->alt_group->last_insn : 1163 orig_insn; 1164 1165 done: 1166 for (i = 0; i < alt_count; i++) { 1167 free(dalts[i].name); 1168 for (j = 0; j < insn_count; j++) 1169 free(dalts[i].insn[j].str); 1170 } 1171 1172 free(alt_name); 1173 1174 return last_insn; 1175 } 1176 1177 /* 1178 * Disassemble a function. 1179 */ 1180 static void disas_func(struct disas_context *dctx, struct symbol *func) 1181 { 1182 struct instruction *insn_start; 1183 struct instruction *insn; 1184 1185 printf("%s:\n", func->name); 1186 sym_for_each_insn(dctx->file, func, insn) { 1187 if (insn->alts) { 1188 insn_start = insn; 1189 insn = disas_alt(dctx, insn); 1190 if (insn) 1191 continue; 1192 /* 1193 * There was an error with disassembling 1194 * the alternative. Resume disassembling 1195 * at the current instruction, this will 1196 * disassemble the default alternative 1197 * only and continue with the code after 1198 * the alternative. 1199 */ 1200 insn = insn_start; 1201 } 1202 1203 DISAS_PRINSN(dctx, insn, 0); 1204 } 1205 printf("\n"); 1206 } 1207 1208 /* 1209 * Disassemble all warned functions. 1210 */ 1211 void disas_warned_funcs(struct disas_context *dctx) 1212 { 1213 struct symbol *sym; 1214 1215 if (!dctx) 1216 return; 1217 1218 for_each_sym(dctx->file->elf, sym) { 1219 if (sym->warned) 1220 disas_func(dctx, sym); 1221 } 1222 } 1223 1224 void disas_funcs(struct disas_context *dctx) 1225 { 1226 bool disas_all = !strcmp(opts.disas, "*"); 1227 struct section *sec; 1228 struct symbol *sym; 1229 1230 for_each_sec(dctx->file->elf, sec) { 1231 1232 if (!(sec->sh.sh_flags & SHF_EXECINSTR)) 1233 continue; 1234 1235 sec_for_each_sym(sec, sym) { 1236 /* 1237 * If the function had a warning and the verbose 1238 * option is used then the function was already 1239 * disassemble. 1240 */ 1241 if (opts.verbose && sym->warned) 1242 continue; 1243 1244 if (disas_all || fnmatch(opts.disas, sym->name, 0) == 0) 1245 disas_func(dctx, sym); 1246 } 1247 } 1248 } 1249