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