2424struct disas_context {
2525 struct objtool_file * file ;
2626 struct instruction * insn ;
27+ bool alt_applied ;
2728 char result [DISAS_RESULT_SIZE ];
2829 disassembler_ftype disassembler ;
2930 struct disassemble_info info ;
@@ -160,13 +161,53 @@ static void disas_print_addr_sym(struct section *sec, struct symbol *sym,
160161 }
161162}
162163
164+ static bool disas_print_addr_alt (bfd_vma addr , struct disassemble_info * dinfo )
165+ {
166+ struct disas_context * dctx = dinfo -> application_data ;
167+ struct instruction * orig_first_insn ;
168+ struct alt_group * alt_group ;
169+ unsigned long offset ;
170+ struct symbol * sym ;
171+
172+ /*
173+ * Check if we are processing an alternative at the original
174+ * instruction address (i.e. if alt_applied is true) and if
175+ * we are referencing an address inside the alternative.
176+ *
177+ * For example, this happens if there is a branch inside an
178+ * alternative. In that case, the address should be updated
179+ * to a reference inside the original instruction flow.
180+ */
181+ if (!dctx -> alt_applied )
182+ return false;
183+
184+ alt_group = dctx -> insn -> alt_group ;
185+ if (!alt_group || !alt_group -> orig_group ||
186+ addr < alt_group -> first_insn -> offset ||
187+ addr > alt_group -> last_insn -> offset )
188+ return false;
189+
190+ orig_first_insn = alt_group -> orig_group -> first_insn ;
191+ offset = addr - alt_group -> first_insn -> offset ;
192+
193+ addr = orig_first_insn -> offset + offset ;
194+ sym = orig_first_insn -> sym ;
195+
196+ disas_print_addr_sym (orig_first_insn -> sec , sym , addr , dinfo );
197+
198+ return true;
199+ }
200+
163201static void disas_print_addr_noreloc (bfd_vma addr ,
164202 struct disassemble_info * dinfo )
165203{
166204 struct disas_context * dctx = dinfo -> application_data ;
167205 struct instruction * insn = dctx -> insn ;
168206 struct symbol * sym = NULL ;
169207
208+ if (disas_print_addr_alt (addr , dinfo ))
209+ return ;
210+
170211 if (insn -> sym && addr >= insn -> sym -> offset &&
171212 addr < insn -> sym -> offset + insn -> sym -> len ) {
172213 sym = insn -> sym ;
@@ -232,8 +273,9 @@ static void disas_print_address(bfd_vma addr, struct disassemble_info *dinfo)
232273 */
233274 jump_dest = insn -> jump_dest ;
234275 if (jump_dest && jump_dest -> sym && jump_dest -> offset == addr ) {
235- disas_print_addr_sym (jump_dest -> sec , jump_dest -> sym ,
236- addr , dinfo );
276+ if (!disas_print_addr_alt (addr , dinfo ))
277+ disas_print_addr_sym (jump_dest -> sec , jump_dest -> sym ,
278+ addr , dinfo );
237279 return ;
238280 }
239281
@@ -490,13 +532,22 @@ void disas_print_insn(FILE *stream, struct disas_context *dctx,
490532
491533/*
492534 * Disassemble a single instruction. Return the size of the instruction.
535+ *
536+ * If alt_applied is true then insn should be an instruction from of an
537+ * alternative (i.e. insn->alt_group != NULL), and it is disassembled
538+ * at the location of the original code it is replacing. When the
539+ * instruction references any address inside the alternative then
540+ * these references will be re-adjusted to replace the original code.
493541 */
494- size_t disas_insn (struct disas_context * dctx , struct instruction * insn )
542+ static size_t disas_insn_common (struct disas_context * dctx ,
543+ struct instruction * insn ,
544+ bool alt_applied )
495545{
496546 disassembler_ftype disasm = dctx -> disassembler ;
497547 struct disassemble_info * dinfo = & dctx -> info ;
498548
499549 dctx -> insn = insn ;
550+ dctx -> alt_applied = alt_applied ;
500551 dctx -> result [0 ] = '\0' ;
501552
502553 if (insn -> type == INSN_NOP ) {
@@ -515,6 +566,17 @@ size_t disas_insn(struct disas_context *dctx, struct instruction *insn)
515566 return disasm (insn -> offset , & dctx -> info );
516567}
517568
569+ size_t disas_insn (struct disas_context * dctx , struct instruction * insn )
570+ {
571+ return disas_insn_common (dctx , insn , false);
572+ }
573+
574+ static size_t disas_insn_alt (struct disas_context * dctx ,
575+ struct instruction * insn )
576+ {
577+ return disas_insn_common (dctx , insn , true);
578+ }
579+
518580static struct instruction * next_insn_same_alt (struct objtool_file * file ,
519581 struct alt_group * alt_grp ,
520582 struct instruction * insn )
@@ -706,7 +768,7 @@ static int disas_alt_group(struct disas_context *dctx, struct disas_alt *dalt)
706768
707769 alt_for_each_insn (file , DALT_GROUP (dalt ), insn ) {
708770
709- disas_insn (dctx , insn );
771+ disas_insn_alt (dctx , insn );
710772 str = strdup (disas_result (dctx ));
711773 if (!str )
712774 return -1 ;
0 commit comments