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 * For each alternative, if there is an instruction at the specified 861 * offset then print this instruction, otherwise print a blank entry. 862 * The offset is an offset from the start of the alternative. 863 * 864 * Return the offset for the next instructions to print, or -1 if all 865 * instructions have been printed. 866 */ 867 static int disas_alt_print_insn(struct disas_alt *dalts, int alt_count, 868 int insn_count, int offset) 869 { 870 struct disas_alt *dalt; 871 int offset_next; 872 char *str; 873 int i, j; 874 875 offset_next = -1; 876 877 for (i = 0; i < alt_count; i++) { 878 dalt = &dalts[i]; 879 j = dalt->insn_idx; 880 if (j == -1) { 881 printf("| %-*s ", dalt->width, ""); 882 continue; 883 } 884 885 if (dalt->insn[j].offset == offset) { 886 str = dalt->insn[j].str; 887 printf("| %-*s ", dalt->width, str ?: ""); 888 if (++j < insn_count) { 889 dalt->insn_idx = j; 890 } else { 891 dalt->insn_idx = -1; 892 continue; 893 } 894 } else { 895 printf("| %-*s ", dalt->width, ""); 896 } 897 898 if (dalt->insn[j].offset > 0 && 899 (offset_next == -1 || 900 (dalt->insn[j].offset < offset_next))) 901 offset_next = dalt->insn[j].offset; 902 } 903 printf("\n"); 904 905 return offset_next; 906 } 907 908 /* 909 * Print all alternatives side-by-side. 910 */ 911 static void disas_alt_print_wide(char *alt_name, struct disas_alt *dalts, int alt_count, 912 int insn_count) 913 { 914 struct instruction *orig_insn; 915 int offset_next; 916 int offset; 917 int i; 918 919 orig_insn = dalts[0].orig_insn; 920 921 /* 922 * Print an header with the name of each alternative. 923 */ 924 disas_print_info(stdout, orig_insn, -2, NULL); 925 926 if (strlen(alt_name) > dalts[0].width) 927 dalts[0].width = strlen(alt_name); 928 printf("| %-*s ", dalts[0].width, alt_name); 929 930 for (i = 1; i < alt_count; i++) 931 printf("| %-*s ", dalts[i].width, dalts[i].name); 932 933 printf("\n"); 934 935 /* 936 * Print instructions for each alternative. 937 */ 938 offset_next = 0; 939 do { 940 offset = offset_next; 941 disas_print(stdout, orig_insn->sec, orig_insn->offset + offset, 942 -2, NULL); 943 offset_next = disas_alt_print_insn(dalts, alt_count, insn_count, 944 offset); 945 } while (offset_next > offset); 946 } 947 948 /* 949 * Print all alternatives one above the other. 950 */ 951 static void disas_alt_print_compact(char *alt_name, struct disas_alt *dalts, 952 int alt_count, int insn_count) 953 { 954 struct instruction *orig_insn; 955 int width; 956 int i, j; 957 int len; 958 959 orig_insn = dalts[0].orig_insn; 960 961 len = disas_print(stdout, orig_insn->sec, orig_insn->offset, 0, NULL); 962 printf("%s\n", alt_name); 963 964 /* 965 * If all alternatives have a single instruction then print each 966 * alternative on a single line. Otherwise, print alternatives 967 * one above the other with a clear separation. 968 */ 969 970 if (insn_count == 1) { 971 width = 0; 972 for (i = 0; i < alt_count; i++) { 973 if (dalts[i].width > width) 974 width = dalts[i].width; 975 } 976 977 for (i = 0; i < alt_count; i++) { 978 printf("%*s= %-*s (if %s)\n", len, "", width, 979 dalts[i].insn[0].str, dalts[i].name); 980 } 981 982 return; 983 } 984 985 for (i = 0; i < alt_count; i++) { 986 printf("%*s= %s\n", len, "", dalts[i].name); 987 for (j = 0; j < insn_count; j++) { 988 if (!dalts[i].insn[j].str) 989 break; 990 disas_print(stdout, orig_insn->sec, 991 orig_insn->offset + dalts[i].insn[j].offset, 0, 992 "| %s\n", dalts[i].insn[j].str); 993 } 994 printf("%*s|\n", len, ""); 995 } 996 } 997 998 /* 999 * Disassemble an alternative. 1000 * 1001 * Return the last instruction in the default alternative so that 1002 * disassembly can continue with the next instruction. Return NULL 1003 * on error. 1004 */ 1005 static void *disas_alt(struct disas_context *dctx, 1006 struct instruction *orig_insn) 1007 { 1008 struct disas_alt dalts[DISAS_ALT_MAX] = { 0 }; 1009 struct instruction *last_insn = NULL; 1010 struct alternative *alt; 1011 struct disas_alt *dalt; 1012 int insn_count = 0; 1013 int alt_count = 0; 1014 char *alt_name; 1015 int count; 1016 int i, j; 1017 int err; 1018 1019 alt_name = strfmt("<%s.%lx>", disas_alt_type_name(orig_insn), 1020 orig_insn->offset); 1021 if (!alt_name) { 1022 WARN("Failed to define name for alternative at instruction 0x%lx", 1023 orig_insn->offset); 1024 goto done; 1025 } 1026 1027 /* 1028 * Initialize and disassemble the default alternative. 1029 */ 1030 err = disas_alt_init(&dalts[0], orig_insn, NULL); 1031 if (err) { 1032 WARN("%s: failed to initialize default alternative", alt_name); 1033 goto done; 1034 } 1035 1036 insn_count = disas_alt_default(dctx, &dalts[0]); 1037 if (insn_count < 0) { 1038 WARN("%s: failed to disassemble default alternative", alt_name); 1039 goto done; 1040 } 1041 1042 /* 1043 * Initialize and disassemble all other alternatives. 1044 */ 1045 i = 1; 1046 for (alt = orig_insn->alts; alt; alt = alt->next) { 1047 if (i >= DISAS_ALT_MAX) { 1048 WARN("%s has more alternatives than supported", alt_name); 1049 break; 1050 } 1051 1052 dalt = &dalts[i]; 1053 err = disas_alt_init(dalt, orig_insn, alt); 1054 if (err) { 1055 WARN("%s: failed to disassemble alternative", alt_name); 1056 goto done; 1057 } 1058 1059 count = -1; 1060 switch (dalt->alt->type) { 1061 case ALT_TYPE_INSTRUCTIONS: 1062 count = disas_alt_group(dctx, dalt); 1063 break; 1064 case ALT_TYPE_EX_TABLE: 1065 count = disas_alt_extable(dalt); 1066 break; 1067 case ALT_TYPE_JUMP_TABLE: 1068 count = disas_alt_jump(dalt); 1069 break; 1070 } 1071 if (count < 0) { 1072 WARN("%s: failed to disassemble alternative %s", 1073 alt_name, dalt->name); 1074 goto done; 1075 } 1076 1077 insn_count = count > insn_count ? count : insn_count; 1078 i++; 1079 } 1080 alt_count = i; 1081 1082 /* 1083 * Print default and non-default alternatives. 1084 */ 1085 1086 if (opts.wide) 1087 disas_alt_print_wide(alt_name, dalts, alt_count, insn_count); 1088 else 1089 disas_alt_print_compact(alt_name, dalts, alt_count, insn_count); 1090 1091 last_insn = orig_insn->alt_group ? orig_insn->alt_group->last_insn : 1092 orig_insn; 1093 1094 done: 1095 for (i = 0; i < alt_count; i++) { 1096 free(dalts[i].name); 1097 for (j = 0; j < insn_count; j++) 1098 free(dalts[i].insn[j].str); 1099 } 1100 1101 free(alt_name); 1102 1103 return last_insn; 1104 } 1105 1106 /* 1107 * Disassemble a function. 1108 */ 1109 static void disas_func(struct disas_context *dctx, struct symbol *func) 1110 { 1111 struct instruction *insn_start; 1112 struct instruction *insn; 1113 1114 printf("%s:\n", func->name); 1115 sym_for_each_insn(dctx->file, func, insn) { 1116 if (insn->alts) { 1117 insn_start = insn; 1118 insn = disas_alt(dctx, insn); 1119 if (insn) 1120 continue; 1121 /* 1122 * There was an error with disassembling 1123 * the alternative. Resume disassembling 1124 * at the current instruction, this will 1125 * disassemble the default alternative 1126 * only and continue with the code after 1127 * the alternative. 1128 */ 1129 insn = insn_start; 1130 } 1131 1132 DISAS_PRINSN(dctx, insn, 0); 1133 } 1134 printf("\n"); 1135 } 1136 1137 /* 1138 * Disassemble all warned functions. 1139 */ 1140 void disas_warned_funcs(struct disas_context *dctx) 1141 { 1142 struct symbol *sym; 1143 1144 if (!dctx) 1145 return; 1146 1147 for_each_sym(dctx->file->elf, sym) { 1148 if (sym->warned) 1149 disas_func(dctx, sym); 1150 } 1151 } 1152 1153 void disas_funcs(struct disas_context *dctx) 1154 { 1155 bool disas_all = !strcmp(opts.disas, "*"); 1156 struct section *sec; 1157 struct symbol *sym; 1158 1159 for_each_sec(dctx->file->elf, sec) { 1160 1161 if (!(sec->sh.sh_flags & SHF_EXECINSTR)) 1162 continue; 1163 1164 sec_for_each_sym(sec, sym) { 1165 /* 1166 * If the function had a warning and the verbose 1167 * option is used then the function was already 1168 * disassemble. 1169 */ 1170 if (opts.verbose && sym->warned) 1171 continue; 1172 1173 if (disas_all || fnmatch(opts.disas, sym->name, 0) == 0) 1174 disas_func(dctx, sym); 1175 } 1176 } 1177 } 1178