Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

objtool: Print headers for alternatives

When using the --disas option, objtool doesn't currently disassemble
any alternative. Print an header for each alternative. This identifies
places where alternatives are present but alternative code is still
not disassembled at the moment.

Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: Josh Poimboeuf <jpoimboe@kernel.org>
Link: https://patch.msgid.link/20251121095340.464045-20-alexandre.chartre@oracle.com

authored by

Alexandre Chartre and committed by
Peter Zijlstra
87343e66 7ad7a4a7

+182 -6
+182 -6
tools/objtool/disas.c
··· 29 29 struct disassemble_info info; 30 30 }; 31 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 + 32 69 static int sprint_name(char *str, const char *name, unsigned long offset) 33 70 { 34 71 int len; ··· 351 314 #define DISAS_INSN_OFFSET_SPACE 10 352 315 #define DISAS_INSN_SPACE 60 353 316 317 + #define DISAS_PRINSN(dctx, insn, depth) \ 318 + disas_print_insn(stdout, dctx, insn, depth, "\n") 319 + 354 320 /* 355 321 * Print a message in the instruction flow. If insn is not NULL then 356 322 * the instruction address is printed in addition of the message, ··· 392 352 n += vfprintf(stream, format, ap); 393 353 394 354 return n; 355 + } 356 + 357 + static int disas_print(FILE *stream, struct section *sec, unsigned long offset, 358 + int depth, const char *format, ...) 359 + { 360 + va_list args; 361 + int len; 362 + 363 + va_start(args, format); 364 + len = disas_vprint(stream, sec, offset, depth, format, args); 365 + va_end(args); 366 + 367 + return len; 395 368 } 396 369 397 370 /* ··· 577 524 } 578 525 579 526 /* 527 + * Initialize an alternative. The default alternative should be initialized 528 + * with alt=NULL. 529 + */ 530 + static int disas_alt_init(struct disas_alt *dalt, 531 + struct instruction *orig_insn, 532 + struct alternative *alt) 533 + { 534 + dalt->orig_insn = orig_insn; 535 + dalt->alt = alt; 536 + dalt->name = alt ? disas_alt_name(alt) : strdup("DEFAULT"); 537 + if (!dalt->name) 538 + return -1; 539 + dalt->width = strlen(dalt->name); 540 + 541 + return 0; 542 + } 543 + 544 + /* 545 + * Print all alternatives one above the other. 546 + */ 547 + static void disas_alt_print_compact(char *alt_name, struct disas_alt *dalts, 548 + int alt_count) 549 + { 550 + struct instruction *orig_insn; 551 + int len; 552 + int i; 553 + 554 + orig_insn = dalts[0].orig_insn; 555 + 556 + len = disas_print(stdout, orig_insn->sec, orig_insn->offset, 0, NULL); 557 + printf("%s\n", alt_name); 558 + 559 + for (i = 0; i < alt_count; i++) 560 + printf("%*s= %s\n", len, "", dalts[i].name); 561 + } 562 + 563 + /* 564 + * Disassemble an alternative. 565 + * 566 + * Return the last instruction in the default alternative so that 567 + * disassembly can continue with the next instruction. Return NULL 568 + * on error. 569 + */ 570 + static void *disas_alt(struct disas_context *dctx, 571 + struct instruction *orig_insn) 572 + { 573 + struct disas_alt dalts[DISAS_ALT_MAX] = { 0 }; 574 + struct alternative *alt; 575 + int alt_count = 0; 576 + char *alt_name; 577 + int err; 578 + int i; 579 + 580 + alt_name = strfmt("<%s.%lx>", disas_alt_type_name(orig_insn), 581 + orig_insn->offset); 582 + if (!alt_name) { 583 + WARN("Failed to define name for alternative at instruction 0x%lx", 584 + orig_insn->offset); 585 + goto done; 586 + } 587 + 588 + /* 589 + * Initialize the default alternative. 590 + */ 591 + err = disas_alt_init(&dalts[0], orig_insn, NULL); 592 + if (err) { 593 + WARN("%s: failed to initialize default alternative", alt_name); 594 + goto done; 595 + } 596 + 597 + /* 598 + * Initialize all other alternatives. 599 + */ 600 + i = 1; 601 + for (alt = orig_insn->alts; alt; alt = alt->next) { 602 + if (i >= DISAS_ALT_MAX) { 603 + WARN("%s has more alternatives than supported", alt_name); 604 + break; 605 + } 606 + err = disas_alt_init(&dalts[i], orig_insn, alt); 607 + if (err) { 608 + WARN("%s: failed to disassemble alternative", alt_name); 609 + goto done; 610 + } 611 + 612 + i++; 613 + } 614 + alt_count = i; 615 + 616 + /* 617 + * Print default and non-default alternatives. 618 + * 619 + * At the moment, this just prints an header for each alternative. 620 + */ 621 + disas_alt_print_compact(alt_name, dalts, alt_count); 622 + 623 + done: 624 + for (i = 0; i < alt_count; i++) 625 + free(dalts[i].name); 626 + 627 + free(alt_name); 628 + 629 + /* 630 + * Currently we are not disassembling any alternative but just 631 + * printing alternative names. Return NULL to have disas_func() 632 + * resume the disassembly with the default alternative. 633 + */ 634 + return NULL; 635 + } 636 + 637 + /* 580 638 * Disassemble a function. 581 639 */ 582 640 static void disas_func(struct disas_context *dctx, struct symbol *func) 583 641 { 642 + struct instruction *insn_start; 584 643 struct instruction *insn; 585 - size_t addr; 586 644 587 645 printf("%s:\n", func->name); 588 646 sym_for_each_insn(dctx->file, func, insn) { 589 - addr = insn->offset; 590 - disas_insn(dctx, insn); 591 - printf(" %6lx: %s+0x%-6lx %s\n", 592 - addr, func->name, addr - func->offset, 593 - disas_result(dctx)); 647 + if (insn->alts) { 648 + insn_start = insn; 649 + insn = disas_alt(dctx, insn); 650 + if (insn) 651 + continue; 652 + /* 653 + * There was an error with disassembling 654 + * the alternative. Resume disassembling 655 + * at the current instruction, this will 656 + * disassemble the default alternative 657 + * only and continue with the code after 658 + * the alternative. 659 + */ 660 + insn = insn_start; 661 + } 662 + 663 + DISAS_PRINSN(dctx, insn, 0); 594 664 } 595 665 printf("\n"); 596 666 }