Skip to content

Commit a4f1599

Browse files
achartrePeter Zijlstra
authored andcommitted
objtool: Disassemble group alternatives
When using the --disas option, disassemble all group alternatives. Jump tables and exception tables (which are handled as alternatives) are 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-21-alexandre.chartre@oracle.com
1 parent 87343e6 commit a4f1599

1 file changed

Lines changed: 149 additions & 17 deletions

File tree

tools/objtool/disas.c

Lines changed: 149 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,14 @@ struct disas_alt {
4747
struct alternative *alt; /* alternative or NULL if default code */
4848
char *name; /* name for this alternative */
4949
int width; /* formatting width */
50+
char *insn[DISAS_ALT_INSN_MAX]; /* alternative instructions */
5051
};
5152

53+
#define DALT_DEFAULT(dalt) (!(dalt)->alt)
54+
#define DALT_INSN(dalt) (DALT_DEFAULT(dalt) ? (dalt)->orig_insn : (dalt)->alt->insn)
55+
#define DALT_GROUP(dalt) (DALT_INSN(dalt)->alt_group)
56+
#define DALT_ALTID(dalt) ((dalt)->orig_insn->offset)
57+
5258
/*
5359
* Wrapper around asprintf() to allocate and format a string.
5460
* Return the allocated string or NULL on error.
@@ -506,6 +512,21 @@ size_t disas_insn(struct disas_context *dctx, struct instruction *insn)
506512
return disasm(insn->offset, &dctx->info);
507513
}
508514

515+
static struct instruction *next_insn_same_alt(struct objtool_file *file,
516+
struct alt_group *alt_grp,
517+
struct instruction *insn)
518+
{
519+
if (alt_grp->last_insn == insn || alt_grp->nop == insn)
520+
return NULL;
521+
522+
return next_insn_same_sec(file, insn);
523+
}
524+
525+
#define alt_for_each_insn(file, alt_grp, insn) \
526+
for (insn = alt_grp->first_insn; \
527+
insn; \
528+
insn = next_insn_same_alt(file, alt_grp, insn))
529+
509530
/*
510531
* Provide a name for the type of alternatives present at the
511532
* specified instruction.
@@ -594,23 +615,107 @@ static int disas_alt_init(struct disas_alt *dalt,
594615
return 0;
595616
}
596617

618+
static int disas_alt_add_insn(struct disas_alt *dalt, int index, char *insn_str)
619+
{
620+
int len;
621+
622+
if (index >= DISAS_ALT_INSN_MAX) {
623+
WARN("Alternative %lx.%s has more instructions than supported",
624+
DALT_ALTID(dalt), dalt->name);
625+
return -1;
626+
}
627+
628+
len = strlen(insn_str);
629+
dalt->insn[index] = insn_str;
630+
if (len > dalt->width)
631+
dalt->width = len;
632+
633+
return 0;
634+
}
635+
636+
/*
637+
* Disassemble an alternative and store instructions in the disas_alt
638+
* structure. Return the number of instructions in the alternative.
639+
*/
640+
static int disas_alt_group(struct disas_context *dctx, struct disas_alt *dalt)
641+
{
642+
struct objtool_file *file;
643+
struct instruction *insn;
644+
char *str;
645+
int count;
646+
int err;
647+
648+
file = dctx->file;
649+
count = 0;
650+
651+
alt_for_each_insn(file, DALT_GROUP(dalt), insn) {
652+
653+
disas_insn(dctx, insn);
654+
str = strdup(disas_result(dctx));
655+
if (!str)
656+
return -1;
657+
658+
err = disas_alt_add_insn(dalt, count, str);
659+
if (err)
660+
break;
661+
count++;
662+
}
663+
664+
return count;
665+
}
666+
667+
/*
668+
* Disassemble the default alternative.
669+
*/
670+
static int disas_alt_default(struct disas_context *dctx, struct disas_alt *dalt)
671+
{
672+
char *str;
673+
int err;
674+
675+
if (DALT_GROUP(dalt))
676+
return disas_alt_group(dctx, dalt);
677+
678+
/*
679+
* Default alternative with no alt_group: this is the default
680+
* code associated with either a jump table or an exception
681+
* table and no other instruction alternatives. In that case
682+
* the default alternative is made of a single instruction.
683+
*/
684+
disas_insn(dctx, dalt->orig_insn);
685+
str = strdup(disas_result(dctx));
686+
if (!str)
687+
return -1;
688+
err = disas_alt_add_insn(dalt, 0, str);
689+
if (err)
690+
return -1;
691+
692+
return 1;
693+
}
694+
597695
/*
598696
* Print all alternatives one above the other.
599697
*/
600698
static void disas_alt_print_compact(char *alt_name, struct disas_alt *dalts,
601-
int alt_count)
699+
int alt_count, int insn_count)
602700
{
603701
struct instruction *orig_insn;
702+
int i, j;
604703
int len;
605-
int i;
606704

607705
orig_insn = dalts[0].orig_insn;
608706

609707
len = disas_print(stdout, orig_insn->sec, orig_insn->offset, 0, NULL);
610708
printf("%s\n", alt_name);
611709

612-
for (i = 0; i < alt_count; i++)
710+
for (i = 0; i < alt_count; i++) {
613711
printf("%*s= %s\n", len, "", dalts[i].name);
712+
for (j = 0; j < insn_count; j++) {
713+
if (!dalts[i].insn[j])
714+
break;
715+
printf("%*s| %s\n", len, "", dalts[i].insn[j]);
716+
}
717+
printf("%*s|\n", len, "");
718+
}
614719
}
615720

616721
/*
@@ -624,11 +729,15 @@ static void *disas_alt(struct disas_context *dctx,
624729
struct instruction *orig_insn)
625730
{
626731
struct disas_alt dalts[DISAS_ALT_MAX] = { 0 };
732+
struct instruction *last_insn = NULL;
627733
struct alternative *alt;
734+
struct disas_alt *dalt;
735+
int insn_count = 0;
628736
int alt_count = 0;
629737
char *alt_name;
738+
int count;
739+
int i, j;
630740
int err;
631-
int i;
632741

633742
alt_name = strfmt("<%s.%lx>", disas_alt_type_name(orig_insn),
634743
orig_insn->offset);
@@ -639,52 +748,75 @@ static void *disas_alt(struct disas_context *dctx,
639748
}
640749

641750
/*
642-
* Initialize the default alternative.
751+
* Initialize and disassemble the default alternative.
643752
*/
644753
err = disas_alt_init(&dalts[0], orig_insn, NULL);
645754
if (err) {
646755
WARN("%s: failed to initialize default alternative", alt_name);
647756
goto done;
648757
}
649758

759+
insn_count = disas_alt_default(dctx, &dalts[0]);
760+
if (insn_count < 0) {
761+
WARN("%s: failed to disassemble default alternative", alt_name);
762+
goto done;
763+
}
764+
650765
/*
651-
* Initialize all other alternatives.
766+
* Initialize and disassemble all other alternatives.
652767
*/
653768
i = 1;
654769
for (alt = orig_insn->alts; alt; alt = alt->next) {
655770
if (i >= DISAS_ALT_MAX) {
656771
WARN("%s has more alternatives than supported", alt_name);
657772
break;
658773
}
659-
err = disas_alt_init(&dalts[i], orig_insn, alt);
774+
dalt = &dalts[i];
775+
err = disas_alt_init(dalt, orig_insn, alt);
660776
if (err) {
661777
WARN("%s: failed to disassemble alternative", alt_name);
662778
goto done;
663779
}
664780

781+
/*
782+
* Only group alternatives are supported at the moment.
783+
*/
784+
switch (dalt->alt->type) {
785+
case ALT_TYPE_INSTRUCTIONS:
786+
count = disas_alt_group(dctx, dalt);
787+
break;
788+
default:
789+
count = 0;
790+
}
791+
if (count < 0) {
792+
WARN("%s: failed to disassemble alternative %s",
793+
alt_name, dalt->name);
794+
goto done;
795+
}
796+
797+
insn_count = count > insn_count ? count : insn_count;
665798
i++;
666799
}
667800
alt_count = i;
668801

669802
/*
670803
* Print default and non-default alternatives.
671-
*
672-
* At the moment, this just prints an header for each alternative.
673804
*/
674-
disas_alt_print_compact(alt_name, dalts, alt_count);
805+
disas_alt_print_compact(alt_name, dalts, alt_count, insn_count);
806+
807+
last_insn = orig_insn->alt_group ? orig_insn->alt_group->last_insn :
808+
orig_insn;
675809

676810
done:
677-
for (i = 0; i < alt_count; i++)
811+
for (i = 0; i < alt_count; i++) {
678812
free(dalts[i].name);
813+
for (j = 0; j < insn_count; j++)
814+
free(dalts[i].insn[j]);
815+
}
679816

680817
free(alt_name);
681818

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;
819+
return last_insn;
688820
}
689821

690822
/*

0 commit comments

Comments
 (0)