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/warn.h> 13 14 #include <bfd.h> 15 #include <linux/string.h> 16 #include <tools/dis-asm-compat.h> 17 18 /* 19 * Size of the buffer for storing the result of disassembling 20 * a single instruction. 21 */ 22 #define DISAS_RESULT_SIZE 1024 23 24 struct disas_context { 25 struct objtool_file *file; 26 struct instruction *insn; 27 char result[DISAS_RESULT_SIZE]; 28 disassembler_ftype disassembler; 29 struct disassemble_info info; 30 }; 31 32 /* 33 * Maximum number of alternatives 34 */ 35 #define DISAS_ALT_MAX 5 36 37 /* 38 * Maximum number of instructions per alternative 39 */ 40 #define DISAS_ALT_INSN_MAX 50 41 42 /* 43 * Information to disassemble an alternative 44 */ 45 struct disas_alt { 46 struct instruction *orig_insn; /* original instruction */ 47 struct alternative *alt; /* alternative or NULL if default code */ 48 char *name; /* name for this alternative */ 49 int width; /* formatting width */ 50 struct { 51 char *str; /* instruction string */ 52 int offset; /* instruction offset */ 53 } insn[DISAS_ALT_INSN_MAX]; /* alternative instructions */ 54 int insn_idx; /* index of the next instruction to print */ 55 }; 56 57 #define DALT_DEFAULT(dalt) (!(dalt)->alt) 58 #define DALT_INSN(dalt) (DALT_DEFAULT(dalt) ? (dalt)->orig_insn : (dalt)->alt->insn) 59 #define DALT_GROUP(dalt) (DALT_INSN(dalt)->alt_group) 60 #define DALT_ALTID(dalt) ((dalt)->orig_insn->offset) 61 62 /* 63 * Wrapper around asprintf() to allocate and format a string. 64 * Return the allocated string or NULL on error. 65 */ 66 static char *strfmt(const char *fmt, ...) 67 { 68 va_list ap; 69 char *str; 70 int rv; 71 72 va_start(ap, fmt); 73 rv = vasprintf(&str, fmt, ap); 74 va_end(ap); 75 76 return rv == -1 ? NULL : str; 77 } 78 79 static int sprint_name(char *str, const char *name, unsigned long offset) 80 { 81 int len; 82 83 if (offset) 84 len = sprintf(str, "%s+0x%lx", name, offset); 85 else 86 len = sprintf(str, "%s", name); 87 88 return len; 89 } 90 91 #define DINFO_FPRINTF(dinfo, ...) \ 92 ((*(dinfo)->fprintf_func)((dinfo)->stream, __VA_ARGS__)) 93 94 static int disas_result_fprintf(struct disas_context *dctx, 95 const char *fmt, va_list ap) 96 { 97 char *buf = dctx->result; 98 int avail, len; 99 100 len = strlen(buf); 101 if (len >= DISAS_RESULT_SIZE - 1) { 102 WARN_FUNC(dctx->insn->sec, dctx->insn->offset, 103 "disassembly buffer is full"); 104 return -1; 105 } 106 avail = DISAS_RESULT_SIZE - len; 107 108 len = vsnprintf(buf + len, avail, fmt, ap); 109 if (len < 0 || len >= avail) { 110 WARN_FUNC(dctx->insn->sec, dctx->insn->offset, 111 "disassembly buffer is truncated"); 112 return -1; 113 } 114 115 return 0; 116 } 117 118 static int disas_fprintf(void *stream, const char *fmt, ...) 119 { 120 va_list arg; 121 int rv; 122 123 va_start(arg, fmt); 124 rv = disas_result_fprintf(stream, fmt, arg); 125 va_end(arg); 126 127 return rv; 128 } 129 130 /* 131 * For init_disassemble_info_compat(). 132 */ 133 static int disas_fprintf_styled(void *stream, 134 enum disassembler_style style, 135 const char *fmt, ...) 136 { 137 va_list arg; 138 int rv; 139 140 va_start(arg, fmt); 141 rv = disas_result_fprintf(stream, fmt, arg); 142 va_end(arg); 143 144 return rv; 145 } 146 147 static void disas_print_addr_sym(struct section *sec, struct symbol *sym, 148 bfd_vma addr, struct disassemble_info *dinfo) 149 { 150 char symstr[1024]; 151 char *str; 152 153 if (sym) { 154 sprint_name(symstr, sym->name, addr - sym->offset); 155 DINFO_FPRINTF(dinfo, "0x%lx <%s>", addr, symstr); 156 } else { 157 str = offstr(sec, addr); 158 DINFO_FPRINTF(dinfo, "0x%lx <%s>", addr, str); 159 free(str); 160 } 161 } 162 163 static void disas_print_addr_noreloc(bfd_vma addr, 164 struct disassemble_info *dinfo) 165 { 166 struct disas_context *dctx = dinfo->application_data; 167 struct instruction *insn = dctx->insn; 168 struct symbol *sym = NULL; 169 170 if (insn->sym && addr >= insn->sym->offset && 171 addr < insn->sym->offset + insn->sym->len) { 172 sym = insn->sym; 173 } 174 175 disas_print_addr_sym(insn->sec, sym, addr, dinfo); 176 } 177 178 static void disas_print_addr_reloc(bfd_vma addr, struct disassemble_info *dinfo) 179 { 180 struct disas_context *dctx = dinfo->application_data; 181 struct instruction *insn = dctx->insn; 182 unsigned long offset; 183 struct reloc *reloc; 184 char symstr[1024]; 185 char *str; 186 187 reloc = find_reloc_by_dest_range(dctx->file->elf, insn->sec, 188 insn->offset, insn->len); 189 if (!reloc) { 190 /* 191 * There is no relocation for this instruction although 192 * the address to resolve points to the next instruction. 193 * So this is an effective reference to the next IP, for 194 * example: "lea 0x0(%rip),%rdi". The kernel can reference 195 * the next IP with _THIS_IP_ macro. 196 */ 197 DINFO_FPRINTF(dinfo, "0x%lx <_THIS_IP_>", addr); 198 return; 199 } 200 201 offset = arch_insn_adjusted_addend(insn, reloc); 202 203 /* 204 * If the relocation symbol is a section name (for example ".bss") 205 * then we try to further resolve the name. 206 */ 207 if (reloc->sym->type == STT_SECTION) { 208 str = offstr(reloc->sym->sec, reloc->sym->offset + offset); 209 DINFO_FPRINTF(dinfo, "0x%lx <%s>", addr, str); 210 free(str); 211 } else { 212 sprint_name(symstr, reloc->sym->name, offset); 213 DINFO_FPRINTF(dinfo, "0x%lx <%s>", addr, symstr); 214 } 215 } 216 217 /* 218 * Resolve an address into a "<symbol>+<offset>" string. 219 */ 220 static void disas_print_address(bfd_vma addr, struct disassemble_info *dinfo) 221 { 222 struct disas_context *dctx = dinfo->application_data; 223 struct instruction *insn = dctx->insn; 224 struct instruction *jump_dest; 225 struct symbol *sym; 226 bool is_reloc; 227 228 /* 229 * If the instruction is a call/jump and it references a 230 * destination then this is likely the address we are looking 231 * up. So check it first. 232 */ 233 jump_dest = insn->jump_dest; 234 if (jump_dest && jump_dest->sym && jump_dest->offset == addr) { 235 disas_print_addr_sym(jump_dest->sec, jump_dest->sym, 236 addr, dinfo); 237 return; 238 } 239 240 /* 241 * If the address points to the next instruction then there is 242 * probably a relocation. It can be a false positive when the 243 * current instruction is referencing the address of the next 244 * instruction. This particular case will be handled in 245 * disas_print_addr_reloc(). 246 */ 247 is_reloc = (addr == insn->offset + insn->len); 248 249 /* 250 * The call destination offset can be the address we are looking 251 * up, or 0 if there is a relocation. 252 */ 253 sym = insn_call_dest(insn); 254 if (sym && (sym->offset == addr || (sym->offset == 0 && is_reloc))) { 255 DINFO_FPRINTF(dinfo, "0x%lx <%s>", addr, sym->name); 256 return; 257 } 258 259 if (!is_reloc) 260 disas_print_addr_noreloc(addr, dinfo); 261 else 262 disas_print_addr_reloc(addr, dinfo); 263 } 264 265 /* 266 * Initialize disassemble info arch, mach (32 or 64-bit) and options. 267 */ 268 int disas_info_init(struct disassemble_info *dinfo, 269 int arch, int mach32, int mach64, 270 const char *options) 271 { 272 struct disas_context *dctx = dinfo->application_data; 273 struct objtool_file *file = dctx->file; 274 275 dinfo->arch = arch; 276 277 switch (file->elf->ehdr.e_ident[EI_CLASS]) { 278 case ELFCLASS32: 279 dinfo->mach = mach32; 280 break; 281 case ELFCLASS64: 282 dinfo->mach = mach64; 283 break; 284 default: 285 return -1; 286 } 287 288 dinfo->disassembler_options = options; 289 290 return 0; 291 } 292 293 struct disas_context *disas_context_create(struct objtool_file *file) 294 { 295 struct disas_context *dctx; 296 struct disassemble_info *dinfo; 297 int err; 298 299 dctx = malloc(sizeof(*dctx)); 300 if (!dctx) { 301 WARN("failed to allocate disassembly context"); 302 return NULL; 303 } 304 305 dctx->file = file; 306 dinfo = &dctx->info; 307 308 init_disassemble_info_compat(dinfo, dctx, 309 disas_fprintf, disas_fprintf_styled); 310 311 dinfo->read_memory_func = buffer_read_memory; 312 dinfo->print_address_func = disas_print_address; 313 dinfo->application_data = dctx; 314 315 /* 316 * bfd_openr() is not used to avoid doing ELF data processing 317 * and caching that has already being done. Here, we just need 318 * to identify the target file so we call an arch specific 319 * function to fill some disassemble info (arch, mach). 320 */ 321 322 dinfo->arch = bfd_arch_unknown; 323 dinfo->mach = 0; 324 325 err = arch_disas_info_init(dinfo); 326 if (err || dinfo->arch == bfd_arch_unknown || dinfo->mach == 0) { 327 WARN("failed to init disassembly arch"); 328 goto error; 329 } 330 331 dinfo->endian = (file->elf->ehdr.e_ident[EI_DATA] == ELFDATA2MSB) ? 332 BFD_ENDIAN_BIG : BFD_ENDIAN_LITTLE; 333 334 disassemble_init_for_target(dinfo); 335 336 dctx->disassembler = disassembler(dinfo->arch, 337 dinfo->endian == BFD_ENDIAN_BIG, 338 dinfo->mach, NULL); 339 if (!dctx->disassembler) { 340 WARN("failed to create disassembler function"); 341 goto error; 342 } 343 344 return dctx; 345 346 error: 347 free(dctx); 348 return NULL; 349 } 350 351 void disas_context_destroy(struct disas_context *dctx) 352 { 353 free(dctx); 354 } 355 356 char *disas_result(struct disas_context *dctx) 357 { 358 return dctx->result; 359 } 360 361 #define DISAS_INSN_OFFSET_SPACE 10 362 #define DISAS_INSN_SPACE 60 363 364 #define DISAS_PRINSN(dctx, insn, depth) \ 365 disas_print_insn(stdout, dctx, insn, depth, "\n") 366 367 /* 368 * Print a message in the instruction flow. If sec is not NULL then the 369 * address at the section offset is printed in addition of the message, 370 * otherwise only the message is printed. 371 */ 372 static int disas_vprint(FILE *stream, struct section *sec, unsigned long offset, 373 int depth, const char *format, va_list ap) 374 { 375 const char *addr_str; 376 int i, n; 377 int len; 378 379 len = sym_name_max_len + DISAS_INSN_OFFSET_SPACE; 380 if (depth < 0) { 381 len += depth; 382 depth = 0; 383 } 384 385 n = 0; 386 387 if (sec) { 388 addr_str = offstr(sec, offset); 389 n += fprintf(stream, "%6lx: %-*s ", offset, len, addr_str); 390 free((char *)addr_str); 391 } else { 392 len += DISAS_INSN_OFFSET_SPACE + 1; 393 n += fprintf(stream, "%-*s", len, ""); 394 } 395 396 /* print vertical bars to show the code flow */ 397 for (i = 0; i < depth; i++) 398 n += fprintf(stream, "| "); 399 400 if (format) 401 n += vfprintf(stream, format, ap); 402 403 return n; 404 } 405 406 static int disas_print(FILE *stream, struct section *sec, unsigned long offset, 407 int depth, const char *format, ...) 408 { 409 va_list args; 410 int len; 411 412 va_start(args, format); 413 len = disas_vprint(stream, sec, offset, depth, format, args); 414 va_end(args); 415 416 return len; 417 } 418 419 /* 420 * Print a message in the instruction flow. If insn is not NULL then 421 * the instruction address is printed in addition of the message, 422 * otherwise only the message is printed. In all cases, the instruction 423 * itself is not printed. 424 */ 425 void disas_print_info(FILE *stream, struct instruction *insn, int depth, 426 const char *format, ...) 427 { 428 struct section *sec; 429 unsigned long off; 430 va_list args; 431 432 if (insn) { 433 sec = insn->sec; 434 off = insn->offset; 435 } else { 436 sec = NULL; 437 off = 0; 438 } 439 440 va_start(args, format); 441 disas_vprint(stream, sec, off, depth, format, args); 442 va_end(args); 443 } 444 445 /* 446 * Print an instruction address (offset and function), the instruction itself 447 * and an optional message. 448 */ 449 void disas_print_insn(FILE *stream, struct disas_context *dctx, 450 struct instruction *insn, int depth, 451 const char *format, ...) 452 { 453 char fake_nop_insn[32]; 454 const char *insn_str; 455 bool fake_nop; 456 va_list args; 457 int len; 458 459 /* 460 * Alternative can insert a fake nop, sometimes with no 461 * associated section so nothing to disassemble. 462 */ 463 fake_nop = (!insn->sec && insn->type == INSN_NOP); 464 if (fake_nop) { 465 snprintf(fake_nop_insn, 32, "<fake nop> (%d bytes)", insn->len); 466 insn_str = fake_nop_insn; 467 } else { 468 disas_insn(dctx, insn); 469 insn_str = disas_result(dctx); 470 } 471 472 /* print the instruction */ 473 len = (depth + 1) * 2 < DISAS_INSN_SPACE ? DISAS_INSN_SPACE - (depth+1) * 2 : 1; 474 disas_print_info(stream, insn, depth, "%-*s", len, insn_str); 475 476 /* print message if any */ 477 if (!format) 478 return; 479 480 if (strcmp(format, "\n") == 0) { 481 fprintf(stream, "\n"); 482 return; 483 } 484 485 fprintf(stream, " - "); 486 va_start(args, format); 487 vfprintf(stream, format, args); 488 va_end(args); 489 } 490 491 /* 492 * Disassemble a single instruction. Return the size of the instruction. 493 */ 494 size_t disas_insn(struct disas_context *dctx, struct instruction *insn) 495 { 496 disassembler_ftype disasm = dctx->disassembler; 497 struct disassemble_info *dinfo = &dctx->info; 498 499 dctx->insn = insn; 500 dctx->result[0] = '\0'; 501 502 if (insn->type == INSN_NOP) { 503 DINFO_FPRINTF(dinfo, "nop%d", insn->len); 504 return insn->len; 505 } 506 507 /* 508 * Set the disassembler buffer to read data from the section 509 * containing the instruction to disassemble. 510 */ 511 dinfo->buffer = insn->sec->data->d_buf; 512 dinfo->buffer_vma = 0; 513 dinfo->buffer_length = insn->sec->sh.sh_size; 514 515 return disasm(insn->offset, &dctx->info); 516 } 517 518 static struct instruction *next_insn_same_alt(struct objtool_file *file, 519 struct alt_group *alt_grp, 520 struct instruction *insn) 521 { 522 if (alt_grp->last_insn == insn || alt_grp->nop == insn) 523 return NULL; 524 525 return next_insn_same_sec(file, insn); 526 } 527 528 #define alt_for_each_insn(file, alt_grp, insn) \ 529 for (insn = alt_grp->first_insn; \ 530 insn; \ 531 insn = next_insn_same_alt(file, alt_grp, insn)) 532 533 /* 534 * Provide a name for the type of alternatives present at the 535 * specified instruction. 536 * 537 * An instruction can have alternatives with different types, for 538 * example alternative instructions and an exception table. In that 539 * case the name for the alternative instructions type is used. 540 * 541 * Return NULL if the instruction as no alternative. 542 */ 543 const char *disas_alt_type_name(struct instruction *insn) 544 { 545 struct alternative *alt; 546 const char *name; 547 548 name = NULL; 549 for (alt = insn->alts; alt; alt = alt->next) { 550 if (alt->type == ALT_TYPE_INSTRUCTIONS) { 551 name = "alternative"; 552 break; 553 } 554 555 switch (alt->type) { 556 case ALT_TYPE_EX_TABLE: 557 name = "ex_table"; 558 break; 559 case ALT_TYPE_JUMP_TABLE: 560 name = "jump_table"; 561 break; 562 default: 563 name = "unknown"; 564 break; 565 } 566 } 567 568 return name; 569 } 570 571 /* 572 * Provide a name for an alternative. 573 */ 574 char *disas_alt_name(struct alternative *alt) 575 { 576 char *str = NULL; 577 578 switch (alt->type) { 579 580 case ALT_TYPE_EX_TABLE: 581 str = strdup("EXCEPTION"); 582 break; 583 584 case ALT_TYPE_JUMP_TABLE: 585 str = strdup("JUMP"); 586 break; 587 588 case ALT_TYPE_INSTRUCTIONS: 589 /* 590 * This is a non-default group alternative. Create a unique 591 * name using the offset of the first original and alternative 592 * instructions. 593 */ 594 asprintf(&str, "ALTERNATIVE %lx.%lx", 595 alt->insn->alt_group->orig_group->first_insn->offset, 596 alt->insn->alt_group->first_insn->offset); 597 break; 598 } 599 600 return str; 601 } 602 603 /* 604 * Initialize an alternative. The default alternative should be initialized 605 * with alt=NULL. 606 */ 607 static int disas_alt_init(struct disas_alt *dalt, 608 struct instruction *orig_insn, 609 struct alternative *alt) 610 { 611 dalt->orig_insn = orig_insn; 612 dalt->alt = alt; 613 dalt->insn_idx = 0; 614 dalt->name = alt ? disas_alt_name(alt) : strdup("DEFAULT"); 615 if (!dalt->name) 616 return -1; 617 dalt->width = strlen(dalt->name); 618 619 return 0; 620 } 621 622 static int disas_alt_add_insn(struct disas_alt *dalt, int index, char *insn_str, 623 int offset) 624 { 625 int len; 626 627 if (index >= DISAS_ALT_INSN_MAX) { 628 WARN("Alternative %lx.%s has more instructions than supported", 629 DALT_ALTID(dalt), dalt->name); 630 return -1; 631 } 632 633 len = strlen(insn_str); 634 dalt->insn[index].str = insn_str; 635 dalt->insn[index].offset = offset; 636 if (len > dalt->width) 637 dalt->width = len; 638 639 return 0; 640 } 641 642 /* 643 * Disassemble an exception table alternative. 644 */ 645 static int disas_alt_extable(struct disas_alt *dalt) 646 { 647 struct instruction *alt_insn; 648 char *str; 649 650 alt_insn = dalt->alt->insn; 651 str = strfmt("resume at 0x%lx <%s+0x%lx>", 652 alt_insn->offset, alt_insn->sym->name, 653 alt_insn->offset - alt_insn->sym->offset); 654 if (!str) 655 return -1; 656 657 disas_alt_add_insn(dalt, 0, str, 0); 658 659 return 1; 660 } 661 662 /* 663 * Disassemble an alternative and store instructions in the disas_alt 664 * structure. Return the number of instructions in the alternative. 665 */ 666 static int disas_alt_group(struct disas_context *dctx, struct disas_alt *dalt) 667 { 668 struct objtool_file *file; 669 struct instruction *insn; 670 int offset; 671 char *str; 672 int count; 673 int err; 674 675 file = dctx->file; 676 count = 0; 677 offset = 0; 678 679 alt_for_each_insn(file, DALT_GROUP(dalt), insn) { 680 681 disas_insn(dctx, insn); 682 str = strdup(disas_result(dctx)); 683 if (!str) 684 return -1; 685 686 err = disas_alt_add_insn(dalt, count, str, offset); 687 if (err) 688 break; 689 offset += insn->len; 690 count++; 691 } 692 693 return count; 694 } 695 696 /* 697 * Disassemble the default alternative. 698 */ 699 static int disas_alt_default(struct disas_context *dctx, struct disas_alt *dalt) 700 { 701 char *str; 702 int err; 703 704 if (DALT_GROUP(dalt)) 705 return disas_alt_group(dctx, dalt); 706 707 /* 708 * Default alternative with no alt_group: this is the default 709 * code associated with either a jump table or an exception 710 * table and no other instruction alternatives. In that case 711 * the default alternative is made of a single instruction. 712 */ 713 disas_insn(dctx, dalt->orig_insn); 714 str = strdup(disas_result(dctx)); 715 if (!str) 716 return -1; 717 err = disas_alt_add_insn(dalt, 0, str, 0); 718 if (err) 719 return -1; 720 721 return 1; 722 } 723 724 /* 725 * Print all alternatives one above the other. 726 */ 727 static void disas_alt_print_compact(char *alt_name, struct disas_alt *dalts, 728 int alt_count, int insn_count) 729 { 730 struct instruction *orig_insn; 731 int i, j; 732 int len; 733 734 orig_insn = dalts[0].orig_insn; 735 736 len = disas_print(stdout, orig_insn->sec, orig_insn->offset, 0, NULL); 737 printf("%s\n", alt_name); 738 739 for (i = 0; i < alt_count; i++) { 740 printf("%*s= %s\n", len, "", dalts[i].name); 741 for (j = 0; j < insn_count; j++) { 742 if (!dalts[i].insn[j].str) 743 break; 744 disas_print(stdout, orig_insn->sec, 745 orig_insn->offset + dalts[i].insn[j].offset, 0, 746 "| %s\n", dalts[i].insn[j].str); 747 } 748 printf("%*s|\n", len, ""); 749 } 750 } 751 752 /* 753 * Disassemble an alternative. 754 * 755 * Return the last instruction in the default alternative so that 756 * disassembly can continue with the next instruction. Return NULL 757 * on error. 758 */ 759 static void *disas_alt(struct disas_context *dctx, 760 struct instruction *orig_insn) 761 { 762 struct disas_alt dalts[DISAS_ALT_MAX] = { 0 }; 763 struct instruction *last_insn = NULL; 764 struct alternative *alt; 765 struct disas_alt *dalt; 766 int insn_count = 0; 767 int alt_count = 0; 768 char *alt_name; 769 int count; 770 int i, j; 771 int err; 772 773 alt_name = strfmt("<%s.%lx>", disas_alt_type_name(orig_insn), 774 orig_insn->offset); 775 if (!alt_name) { 776 WARN("Failed to define name for alternative at instruction 0x%lx", 777 orig_insn->offset); 778 goto done; 779 } 780 781 /* 782 * Initialize and disassemble the default alternative. 783 */ 784 err = disas_alt_init(&dalts[0], orig_insn, NULL); 785 if (err) { 786 WARN("%s: failed to initialize default alternative", alt_name); 787 goto done; 788 } 789 790 insn_count = disas_alt_default(dctx, &dalts[0]); 791 if (insn_count < 0) { 792 WARN("%s: failed to disassemble default alternative", alt_name); 793 goto done; 794 } 795 796 /* 797 * Initialize and disassemble all other alternatives. 798 */ 799 i = 1; 800 for (alt = orig_insn->alts; alt; alt = alt->next) { 801 if (i >= DISAS_ALT_MAX) { 802 WARN("%s has more alternatives than supported", alt_name); 803 break; 804 } 805 dalt = &dalts[i]; 806 err = disas_alt_init(dalt, orig_insn, alt); 807 if (err) { 808 WARN("%s: failed to disassemble alternative", alt_name); 809 goto done; 810 } 811 812 /* 813 * Only group alternatives and exception tables are 814 * supported at the moment. 815 */ 816 switch (dalt->alt->type) { 817 case ALT_TYPE_INSTRUCTIONS: 818 count = disas_alt_group(dctx, dalt); 819 break; 820 case ALT_TYPE_EX_TABLE: 821 count = disas_alt_extable(dalt); 822 break; 823 default: 824 count = 0; 825 } 826 if (count < 0) { 827 WARN("%s: failed to disassemble alternative %s", 828 alt_name, dalt->name); 829 goto done; 830 } 831 832 insn_count = count > insn_count ? count : insn_count; 833 i++; 834 } 835 alt_count = i; 836 837 /* 838 * Print default and non-default alternatives. 839 */ 840 disas_alt_print_compact(alt_name, dalts, alt_count, insn_count); 841 842 last_insn = orig_insn->alt_group ? orig_insn->alt_group->last_insn : 843 orig_insn; 844 845 done: 846 for (i = 0; i < alt_count; i++) { 847 free(dalts[i].name); 848 for (j = 0; j < insn_count; j++) 849 free(dalts[i].insn[j].str); 850 } 851 852 free(alt_name); 853 854 return last_insn; 855 } 856 857 /* 858 * Disassemble a function. 859 */ 860 static void disas_func(struct disas_context *dctx, struct symbol *func) 861 { 862 struct instruction *insn_start; 863 struct instruction *insn; 864 865 printf("%s:\n", func->name); 866 sym_for_each_insn(dctx->file, func, insn) { 867 if (insn->alts) { 868 insn_start = insn; 869 insn = disas_alt(dctx, insn); 870 if (insn) 871 continue; 872 /* 873 * There was an error with disassembling 874 * the alternative. Resume disassembling 875 * at the current instruction, this will 876 * disassemble the default alternative 877 * only and continue with the code after 878 * the alternative. 879 */ 880 insn = insn_start; 881 } 882 883 DISAS_PRINSN(dctx, insn, 0); 884 } 885 printf("\n"); 886 } 887 888 /* 889 * Disassemble all warned functions. 890 */ 891 void disas_warned_funcs(struct disas_context *dctx) 892 { 893 struct symbol *sym; 894 895 if (!dctx) 896 return; 897 898 for_each_sym(dctx->file->elf, sym) { 899 if (sym->warned) 900 disas_func(dctx, sym); 901 } 902 } 903 904 void disas_funcs(struct disas_context *dctx) 905 { 906 bool disas_all = !strcmp(opts.disas, "*"); 907 struct section *sec; 908 struct symbol *sym; 909 910 for_each_sec(dctx->file->elf, sec) { 911 912 if (!(sec->sh.sh_flags & SHF_EXECINSTR)) 913 continue; 914 915 sec_for_each_sym(sec, sym) { 916 /* 917 * If the function had a warning and the verbose 918 * option is used then the function was already 919 * disassemble. 920 */ 921 if (opts.verbose && sym->warned) 922 continue; 923 924 if (disas_all || fnmatch(opts.disas, sym->name, 0) == 0) 925 disas_func(dctx, sym); 926 } 927 } 928 } 929