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