@@ -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 */
600698static 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
676810done :
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