@@ -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+
3269static 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 */
529693static 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