1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (C) 2015-2017 Josh Poimboeuf <jpoimboe@redhat.com> 4 */ 5 6 #define _GNU_SOURCE 7 #include <fnmatch.h> 8 9 #include <objtool/arch.h> 10 #include <objtool/check.h> 11 #include <objtool/disas.h> 12 #include <objtool/warn.h> 13 14 #include <bfd.h> 15 #include <linux/string.h> 16 #include <tools/dis-asm-compat.h> 17 18 /* 19 * Size of the buffer for storing the result of disassembling 20 * a single instruction. 21 */ 22 #define DISAS_RESULT_SIZE 1024 23 24 struct disas_context { 25 struct objtool_file *file; 26 struct instruction *insn; 27 char result[DISAS_RESULT_SIZE]; 28 disassembler_ftype disassembler; 29 struct disassemble_info info; 30 }; 31 32 /* 33 * Maximum number of alternatives 34 */ 35 #define DISAS_ALT_MAX 5 36 37 /* 38 * Maximum number of instructions per alternative 39 */ 40 #define DISAS_ALT_INSN_MAX 50 41 42 /* 43 * Information to disassemble an alternative 44 */ 45 struct disas_alt { 46 struct instruction *orig_insn; /* original instruction */ 47 struct alternative *alt; /* alternative or NULL if default code */ 48 char *name; /* name for this alternative */ 49 int width; /* formatting width */ 50 }; 51 52 /* 53 * Wrapper around asprintf() to allocate and format a string. 54 * Return the allocated string or NULL on error. 55 */ 56 static char *strfmt(const char *fmt, ...) 57 { 58 va_list ap; 59 char *str; 60 int rv; 61 62 va_start(ap, fmt); 63 rv = vasprintf(&str, fmt, ap); 64 va_end(ap); 65 66 return rv == -1 ? NULL : str; 67 } 68 69 static int sprint_name(char *str, const char *name, unsigned long offset) 70 { 71 int len; 72 73 if (offset) 74 len = sprintf(str, "%s+0x%lx", name, offset); 75 else 76 len = sprintf(str, "%s", name); 77 78 return len; 79 } 80 81 #define DINFO_FPRINTF(dinfo, ...) \ 82 ((*(dinfo)->fprintf_func)((dinfo)->stream, __VA_ARGS__)) 83 84 static int disas_result_fprintf(struct disas_context *dctx, 85 const char *fmt, va_list ap) 86 { 87 char *buf = dctx->result; 88 int avail, len; 89 90 len = strlen(buf); 91 if (len >= DISAS_RESULT_SIZE - 1) { 92 WARN_FUNC(dctx->insn->sec, dctx->insn->offset, 93 "disassembly buffer is full"); 94 return -1; 95 } 96 avail = DISAS_RESULT_SIZE - len; 97 98 len = vsnprintf(buf + len, avail, fmt, ap); 99 if (len < 0 || len >= avail) { 100 WARN_FUNC(dctx->insn->sec, dctx->insn->offset, 101 "disassembly buffer is truncated"); 102 return -1; 103 } 104 105 return 0; 106 } 107 108 static int disas_fprintf(void *stream, const char *fmt, ...) 109 { 110 va_list arg; 111 int rv; 112 113 va_start(arg, fmt); 114 rv = disas_result_fprintf(stream, fmt, arg); 115 va_end(arg); 116 117 return rv; 118 } 119 120 /* 121 * For init_disassemble_info_compat(). 122 */ 123 static int disas_fprintf_styled(void *stream, 124 enum disassembler_style style, 125 const char *fmt, ...) 126 { 127 va_list arg; 128 int rv; 129 130 va_start(arg, fmt); 131 rv = disas_result_fprintf(stream, fmt, arg); 132 va_end(arg); 133 134 return rv; 135 } 136 137 static void disas_print_addr_sym(struct section *sec, struct symbol *sym, 138 bfd_vma addr, struct disassemble_info *dinfo) 139 { 140 char symstr[1024]; 141 char *str; 142 143 if (sym) { 144 sprint_name(symstr, sym->name, addr - sym->offset); 145 DINFO_FPRINTF(dinfo, "0x%lx <%s>", addr, symstr); 146 } else { 147 str = offstr(sec, addr); 148 DINFO_FPRINTF(dinfo, "0x%lx <%s>", addr, str); 149 free(str); 150 } 151 } 152 153 static void disas_print_addr_noreloc(bfd_vma addr, 154 struct disassemble_info *dinfo) 155 { 156 struct disas_context *dctx = dinfo->application_data; 157 struct instruction *insn = dctx->insn; 158 struct symbol *sym = NULL; 159 160 if (insn->sym && addr >= insn->sym->offset && 161 addr < insn->sym->offset + insn->sym->len) { 162 sym = insn->sym; 163 } 164 165 disas_print_addr_sym(insn->sec, sym, addr, dinfo); 166 } 167 168 static void disas_print_addr_reloc(bfd_vma addr, struct disassemble_info *dinfo) 169 { 170 struct disas_context *dctx = dinfo->application_data; 171 struct instruction *insn = dctx->insn; 172 unsigned long offset; 173 struct reloc *reloc; 174 char symstr[1024]; 175 char *str; 176 177 reloc = find_reloc_by_dest_range(dctx->file->elf, insn->sec, 178 insn->offset, insn->len); 179 if (!reloc) { 180 /* 181 * There is no relocation for this instruction although 182 * the address to resolve points to the next instruction. 183 * So this is an effective reference to the next IP, for 184 * example: "lea 0x0(%rip),%rdi". The kernel can reference 185 * the next IP with _THIS_IP_ macro. 186 */ 187 DINFO_FPRINTF(dinfo, "0x%lx <_THIS_IP_>", addr); 188 return; 189 } 190 191 offset = arch_insn_adjusted_addend(insn, reloc); 192 193 /* 194 * If the relocation symbol is a section name (for example ".bss") 195 * then we try to further resolve the name. 196 */ 197 if (reloc->sym->type == STT_SECTION) { 198 str = offstr(reloc->sym->sec, reloc->sym->offset + offset); 199 DINFO_FPRINTF(dinfo, "0x%lx <%s>", addr, str); 200 free(str); 201 } else { 202 sprint_name(symstr, reloc->sym->name, offset); 203 DINFO_FPRINTF(dinfo, "0x%lx <%s>", addr, symstr); 204 } 205 } 206 207 /* 208 * Resolve an address into a "<symbol>+<offset>" string. 209 */ 210 static void disas_print_address(bfd_vma addr, struct disassemble_info *dinfo) 211 { 212 struct disas_context *dctx = dinfo->application_data; 213 struct instruction *insn = dctx->insn; 214 struct instruction *jump_dest; 215 struct symbol *sym; 216 bool is_reloc; 217 218 /* 219 * If the instruction is a call/jump and it references a 220 * destination then this is likely the address we are looking 221 * up. So check it first. 222 */ 223 jump_dest = insn->jump_dest; 224 if (jump_dest && jump_dest->sym && jump_dest->offset == addr) { 225 disas_print_addr_sym(jump_dest->sec, jump_dest->sym, 226 addr, dinfo); 227 return; 228 } 229 230 /* 231 * If the address points to the next instruction then there is 232 * probably a relocation. It can be a false positive when the 233 * current instruction is referencing the address of the next 234 * instruction. This particular case will be handled in 235 * disas_print_addr_reloc(). 236 */ 237 is_reloc = (addr == insn->offset + insn->len); 238 239 /* 240 * The call destination offset can be the address we are looking 241 * up, or 0 if there is a relocation. 242 */ 243 sym = insn_call_dest(insn); 244 if (sym && (sym->offset == addr || (sym->offset == 0 && is_reloc))) { 245 DINFO_FPRINTF(dinfo, "0x%lx <%s>", addr, sym->name); 246 return; 247 } 248 249 if (!is_reloc) 250 disas_print_addr_noreloc(addr, dinfo); 251 else 252 disas_print_addr_reloc(addr, dinfo); 253 } 254 255 /* 256 * Initialize disassemble info arch, mach (32 or 64-bit) and options. 257 */ 258 int disas_info_init(struct disassemble_info *dinfo, 259 int arch, int mach32, int mach64, 260 const char *options) 261 { 262 struct disas_context *dctx = dinfo->application_data; 263 struct objtool_file *file = dctx->file; 264 265 dinfo->arch = arch; 266 267 switch (file->elf->ehdr.e_ident[EI_CLASS]) { 268 case ELFCLASS32: 269 dinfo->mach = mach32; 270 break; 271 case ELFCLASS64: 272 dinfo->mach = mach64; 273 break; 274 default: 275 return -1; 276 } 277 278 dinfo->disassembler_options = options; 279 280 return 0; 281 } 282 283 struct disas_context *disas_context_create(struct objtool_file *file) 284 { 285 struct disas_context *dctx; 286 struct disassemble_info *dinfo; 287 int err; 288 289 dctx = malloc(sizeof(*dctx)); 290 if (!dctx) { 291 WARN("failed to allocate disassembly context"); 292 return NULL; 293 } 294 295 dctx->file = file; 296 dinfo = &dctx->info; 297 298 init_disassemble_info_compat(dinfo, dctx, 299 disas_fprintf, disas_fprintf_styled); 300 301 dinfo->read_memory_func = buffer_read_memory; 302 dinfo->print_address_func = disas_print_address; 303 dinfo->application_data = dctx; 304 305 /* 306 * bfd_openr() is not used to avoid doing ELF data processing 307 * and caching that has already being done. Here, we just need 308 * to identify the target file so we call an arch specific 309 * function to fill some disassemble info (arch, mach). 310 */ 311 312 dinfo->arch = bfd_arch_unknown; 313 dinfo->mach = 0; 314 315 err = arch_disas_info_init(dinfo); 316 if (err || dinfo->arch == bfd_arch_unknown || dinfo->mach == 0) { 317 WARN("failed to init disassembly arch"); 318 goto error; 319 } 320 321 dinfo->endian = (file->elf->ehdr.e_ident[EI_DATA] == ELFDATA2MSB) ? 322 BFD_ENDIAN_BIG : BFD_ENDIAN_LITTLE; 323 324 disassemble_init_for_target(dinfo); 325 326 dctx->disassembler = disassembler(dinfo->arch, 327 dinfo->endian == BFD_ENDIAN_BIG, 328 dinfo->mach, NULL); 329 if (!dctx->disassembler) { 330 WARN("failed to create disassembler function"); 331 goto error; 332 } 333 334 return dctx; 335 336 error: 337 free(dctx); 338 return NULL; 339 } 340 341 void disas_context_destroy(struct disas_context *dctx) 342 { 343 free(dctx); 344 } 345 346 char *disas_result(struct disas_context *dctx) 347 { 348 return dctx->result; 349 } 350 351 #define DISAS_INSN_OFFSET_SPACE 10 352 #define DISAS_INSN_SPACE 60 353 354 #define DISAS_PRINSN(dctx, insn, depth) \ 355 disas_print_insn(stdout, dctx, insn, depth, "\n") 356 357 /* 358 * Print a message in the instruction flow. If insn is not NULL then 359 * the instruction address is printed in addition of the message, 360 * otherwise only the message is printed. In all cases, the instruction 361 * itself is not printed. 362 */ 363 static int disas_vprint(FILE *stream, struct section *sec, unsigned long offset, 364 int depth, const char *format, va_list ap) 365 { 366 const char *addr_str; 367 int i, n; 368 int len; 369 370 len = sym_name_max_len + DISAS_INSN_OFFSET_SPACE; 371 if (depth < 0) { 372 len += depth; 373 depth = 0; 374 } 375 376 n = 0; 377 378 if (sec) { 379 addr_str = offstr(sec, offset); 380 n += fprintf(stream, "%6lx: %-*s ", offset, len, addr_str); 381 free((char *)addr_str); 382 } else { 383 len += DISAS_INSN_OFFSET_SPACE + 1; 384 n += fprintf(stream, "%-*s", len, ""); 385 } 386 387 /* print vertical bars to show the code flow */ 388 for (i = 0; i < depth; i++) 389 n += fprintf(stream, "| "); 390 391 if (format) 392 n += vfprintf(stream, format, ap); 393 394 return n; 395 } 396 397 static int disas_print(FILE *stream, struct section *sec, unsigned long offset, 398 int depth, const char *format, ...) 399 { 400 va_list args; 401 int len; 402 403 va_start(args, format); 404 len = disas_vprint(stream, sec, offset, depth, format, args); 405 va_end(args); 406 407 return len; 408 } 409 410 /* 411 * Print a message in the instruction flow. If insn is not NULL then 412 * the instruction address is printed in addition of the message, 413 * otherwise only the message is printed. In all cases, the instruction 414 * itself is not printed. 415 */ 416 void disas_print_info(FILE *stream, struct instruction *insn, int depth, 417 const char *format, ...) 418 { 419 struct section *sec; 420 unsigned long off; 421 va_list args; 422 423 if (insn) { 424 sec = insn->sec; 425 off = insn->offset; 426 } else { 427 sec = NULL; 428 off = 0; 429 } 430 431 va_start(args, format); 432 disas_vprint(stream, sec, off, depth, format, args); 433 va_end(args); 434 } 435 436 /* 437 * Print an instruction address (offset and function), the instruction itself 438 * and an optional message. 439 */ 440 void disas_print_insn(FILE *stream, struct disas_context *dctx, 441 struct instruction *insn, int depth, 442 const char *format, ...) 443 { 444 char fake_nop_insn[32]; 445 const char *insn_str; 446 bool fake_nop; 447 va_list args; 448 int len; 449 450 /* 451 * Alternative can insert a fake nop, sometimes with no 452 * associated section so nothing to disassemble. 453 */ 454 fake_nop = (!insn->sec && insn->type == INSN_NOP); 455 if (fake_nop) { 456 snprintf(fake_nop_insn, 32, "<fake nop> (%d bytes)", insn->len); 457 insn_str = fake_nop_insn; 458 } else { 459 disas_insn(dctx, insn); 460 insn_str = disas_result(dctx); 461 } 462 463 /* print the instruction */ 464 len = (depth + 1) * 2 < DISAS_INSN_SPACE ? DISAS_INSN_SPACE - (depth+1) * 2 : 1; 465 disas_print_info(stream, insn, depth, "%-*s", len, insn_str); 466 467 /* print message if any */ 468 if (!format) 469 return; 470 471 if (strcmp(format, "\n") == 0) { 472 fprintf(stream, "\n"); 473 return; 474 } 475 476 fprintf(stream, " - "); 477 va_start(args, format); 478 vfprintf(stream, format, args); 479 va_end(args); 480 } 481 482 /* 483 * Disassemble a single instruction. Return the size of the instruction. 484 */ 485 size_t disas_insn(struct disas_context *dctx, struct instruction *insn) 486 { 487 disassembler_ftype disasm = dctx->disassembler; 488 struct disassemble_info *dinfo = &dctx->info; 489 490 dctx->insn = insn; 491 dctx->result[0] = '\0'; 492 493 if (insn->type == INSN_NOP) { 494 DINFO_FPRINTF(dinfo, "nop%d", insn->len); 495 return insn->len; 496 } 497 498 /* 499 * Set the disassembler buffer to read data from the section 500 * containing the instruction to disassemble. 501 */ 502 dinfo->buffer = insn->sec->data->d_buf; 503 dinfo->buffer_vma = 0; 504 dinfo->buffer_length = insn->sec->sh.sh_size; 505 506 return disasm(insn->offset, &dctx->info); 507 } 508 509 /* 510 * Provide a name for the type of alternatives present at the 511 * specified instruction. 512 * 513 * An instruction can have alternatives with different types, for 514 * example alternative instructions and an exception table. In that 515 * case the name for the alternative instructions type is used. 516 * 517 * Return NULL if the instruction as no alternative. 518 */ 519 const char *disas_alt_type_name(struct instruction *insn) 520 { 521 struct alternative *alt; 522 const char *name; 523 524 name = NULL; 525 for (alt = insn->alts; alt; alt = alt->next) { 526 if (alt->type == ALT_TYPE_INSTRUCTIONS) { 527 name = "alternative"; 528 break; 529 } 530 531 switch (alt->type) { 532 case ALT_TYPE_EX_TABLE: 533 name = "ex_table"; 534 break; 535 case ALT_TYPE_JUMP_TABLE: 536 name = "jump_table"; 537 break; 538 default: 539 name = "unknown"; 540 break; 541 } 542 } 543 544 return name; 545 } 546 547 /* 548 * Provide a name for an alternative. 549 */ 550 char *disas_alt_name(struct alternative *alt) 551 { 552 char *str = NULL; 553 554 switch (alt->type) { 555 556 case ALT_TYPE_EX_TABLE: 557 str = strdup("EXCEPTION"); 558 break; 559 560 case ALT_TYPE_JUMP_TABLE: 561 str = strdup("JUMP"); 562 break; 563 564 case ALT_TYPE_INSTRUCTIONS: 565 /* 566 * This is a non-default group alternative. Create a unique 567 * name using the offset of the first original and alternative 568 * instructions. 569 */ 570 asprintf(&str, "ALTERNATIVE %lx.%lx", 571 alt->insn->alt_group->orig_group->first_insn->offset, 572 alt->insn->alt_group->first_insn->offset); 573 break; 574 } 575 576 return str; 577 } 578 579 /* 580 * Initialize an alternative. The default alternative should be initialized 581 * with alt=NULL. 582 */ 583 static int disas_alt_init(struct disas_alt *dalt, 584 struct instruction *orig_insn, 585 struct alternative *alt) 586 { 587 dalt->orig_insn = orig_insn; 588 dalt->alt = alt; 589 dalt->name = alt ? disas_alt_name(alt) : strdup("DEFAULT"); 590 if (!dalt->name) 591 return -1; 592 dalt->width = strlen(dalt->name); 593 594 return 0; 595 } 596 597 /* 598 * Print all alternatives one above the other. 599 */ 600 static void disas_alt_print_compact(char *alt_name, struct disas_alt *dalts, 601 int alt_count) 602 { 603 struct instruction *orig_insn; 604 int len; 605 int i; 606 607 orig_insn = dalts[0].orig_insn; 608 609 len = disas_print(stdout, orig_insn->sec, orig_insn->offset, 0, NULL); 610 printf("%s\n", alt_name); 611 612 for (i = 0; i < alt_count; i++) 613 printf("%*s= %s\n", len, "", dalts[i].name); 614 } 615 616 /* 617 * Disassemble an alternative. 618 * 619 * Return the last instruction in the default alternative so that 620 * disassembly can continue with the next instruction. Return NULL 621 * on error. 622 */ 623 static void *disas_alt(struct disas_context *dctx, 624 struct instruction *orig_insn) 625 { 626 struct disas_alt dalts[DISAS_ALT_MAX] = { 0 }; 627 struct alternative *alt; 628 int alt_count = 0; 629 char *alt_name; 630 int err; 631 int i; 632 633 alt_name = strfmt("<%s.%lx>", disas_alt_type_name(orig_insn), 634 orig_insn->offset); 635 if (!alt_name) { 636 WARN("Failed to define name for alternative at instruction 0x%lx", 637 orig_insn->offset); 638 goto done; 639 } 640 641 /* 642 * Initialize the default alternative. 643 */ 644 err = disas_alt_init(&dalts[0], orig_insn, NULL); 645 if (err) { 646 WARN("%s: failed to initialize default alternative", alt_name); 647 goto done; 648 } 649 650 /* 651 * Initialize all other alternatives. 652 */ 653 i = 1; 654 for (alt = orig_insn->alts; alt; alt = alt->next) { 655 if (i >= DISAS_ALT_MAX) { 656 WARN("%s has more alternatives than supported", alt_name); 657 break; 658 } 659 err = disas_alt_init(&dalts[i], orig_insn, alt); 660 if (err) { 661 WARN("%s: failed to disassemble alternative", alt_name); 662 goto done; 663 } 664 665 i++; 666 } 667 alt_count = i; 668 669 /* 670 * Print default and non-default alternatives. 671 * 672 * At the moment, this just prints an header for each alternative. 673 */ 674 disas_alt_print_compact(alt_name, dalts, alt_count); 675 676 done: 677 for (i = 0; i < alt_count; i++) 678 free(dalts[i].name); 679 680 free(alt_name); 681 682 /* 683 * Currently we are not disassembling any alternative but just 684 * printing alternative names. Return NULL to have disas_func() 685 * resume the disassembly with the default alternative. 686 */ 687 return NULL; 688 } 689 690 /* 691 * Disassemble a function. 692 */ 693 static void disas_func(struct disas_context *dctx, struct symbol *func) 694 { 695 struct instruction *insn_start; 696 struct instruction *insn; 697 698 printf("%s:\n", func->name); 699 sym_for_each_insn(dctx->file, func, insn) { 700 if (insn->alts) { 701 insn_start = insn; 702 insn = disas_alt(dctx, insn); 703 if (insn) 704 continue; 705 /* 706 * There was an error with disassembling 707 * the alternative. Resume disassembling 708 * at the current instruction, this will 709 * disassemble the default alternative 710 * only and continue with the code after 711 * the alternative. 712 */ 713 insn = insn_start; 714 } 715 716 DISAS_PRINSN(dctx, insn, 0); 717 } 718 printf("\n"); 719 } 720 721 /* 722 * Disassemble all warned functions. 723 */ 724 void disas_warned_funcs(struct disas_context *dctx) 725 { 726 struct symbol *sym; 727 728 if (!dctx) 729 return; 730 731 for_each_sym(dctx->file->elf, sym) { 732 if (sym->warned) 733 disas_func(dctx, sym); 734 } 735 } 736 737 void disas_funcs(struct disas_context *dctx) 738 { 739 bool disas_all = !strcmp(opts.disas, "*"); 740 struct section *sec; 741 struct symbol *sym; 742 743 for_each_sec(dctx->file->elf, sec) { 744 745 if (!(sec->sh.sh_flags & SHF_EXECINSTR)) 746 continue; 747 748 sec_for_each_sym(sec, sym) { 749 /* 750 * If the function had a warning and the verbose 751 * option is used then the function was already 752 * disassemble. 753 */ 754 if (opts.verbose && sym->warned) 755 continue; 756 757 if (disas_all || fnmatch(opts.disas, sym->name, 0) == 0) 758 disas_func(dctx, sym); 759 } 760 } 761 } 762