Skip to content

Commit 87343e6

Browse files
achartrePeter Zijlstra
authored andcommitted
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
1 parent 7ad7a4a commit 87343e6

1 file changed

Lines changed: 182 additions & 6 deletions

File tree

tools/objtool/disas.c

Lines changed: 182 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,43 @@ struct disas_context {
2929
struct disassemble_info info;
3030
};
3131

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+
3269
static int sprint_name(char *str, const char *name, unsigned long offset)
3370
{
3471
int len;
@@ -314,6 +351,9 @@ char *disas_result(struct disas_context *dctx)
314351
#define DISAS_INSN_OFFSET_SPACE 10
315352
#define DISAS_INSN_SPACE 60
316353

354+
#define DISAS_PRINSN(dctx, insn, depth) \
355+
disas_print_insn(stdout, dctx, insn, depth, "\n")
356+
317357
/*
318358
* Print a message in the instruction flow. If insn is not NULL then
319359
* the instruction address is printed in addition of the message,
@@ -354,6 +394,19 @@ static int disas_vprint(FILE *stream, struct section *sec, unsigned long offset,
354394
return n;
355395
}
356396

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+
357410
/*
358411
* Print a message in the instruction flow. If insn is not NULL then
359412
* the instruction address is printed in addition of the message,
@@ -523,21 +576,144 @@ char *disas_alt_name(struct alternative *alt)
523576
return str;
524577
}
525578

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+
526690
/*
527691
* Disassemble a function.
528692
*/
529693
static void disas_func(struct disas_context *dctx, struct symbol *func)
530694
{
695+
struct instruction *insn_start;
531696
struct instruction *insn;
532-
size_t addr;
533697

534698
printf("%s:\n", func->name);
535699
sym_for_each_insn(dctx->file, func, insn) {
536-
addr = insn->offset;
537-
disas_insn(dctx, insn);
538-
printf(" %6lx: %s+0x%-6lx %s\n",
539-
addr, func->name, addr - func->offset,
540-
disas_result(dctx));
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);
541717
}
542718
printf("\n");
543719
}

0 commit comments

Comments
 (0)