Skip to content

Commit 4aae0d3

Browse files
achartrePeter Zijlstra
authored andcommitted
objtool: Fix address references in alternatives
When using the --disas option, alternatives are disassembled but address references in non-default alternatives can be incorrect. The problem is that alternatives are shown as if they were replacing the original code of the alternative. So if an alternative is referencing an address inside the alternative then the reference has to be adjusted to the location of the original code. 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-25-alexandre.chartre@oracle.com
1 parent 7e01772 commit 4aae0d3

1 file changed

Lines changed: 66 additions & 4 deletions

File tree

tools/objtool/disas.c

Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
struct 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+
163201
static 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+
518580
static 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

Comments
 (0)