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