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