[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20251113164917.2563486-20-alexandre.chartre@oracle.com>
Date: Thu, 13 Nov 2025 17:49:08 +0100
From: Alexandre Chartre <alexandre.chartre@...cle.com>
To: linux-kernel@...r.kernel.org, mingo@...nel.org, jpoimboe@...nel.org,
peterz@...radead.org
Cc: alexandre.chartre@...cle.com
Subject: [PATCH v4 19/28] objtool: Print addresses with alternative instructions
All alternatives are disassemble side-by-side when using the --disas
option. However the address of each instruction is not printed because
instructions from different alternatives are not necessarily aligned.
Change this behavior to print the address of each instruction. Spaces
will appear between instructions from the same alternative when
instructions from different alternatives do not have the same alignment.
Signed-off-by: Alexandre Chartre <alexandre.chartre@...cle.com>
---
tools/objtool/disas.c | 152 +++++++++++++++++++++++++++++++++---------
1 file changed, 121 insertions(+), 31 deletions(-)
diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c
index 321256f746425..f4bd802e38474 100644
--- a/tools/objtool/disas.c
+++ b/tools/objtool/disas.c
@@ -47,7 +47,11 @@ struct disas_alt {
struct alternative *alt; /* alternative or NULL if default code */
char *name; /* name for this alternative */
int width; /* formatting width */
- char *insn[DISAS_ALT_INSN_MAX]; /* alternative instructions */
+ struct {
+ char *str; /* instruction string */
+ int offset; /* instruction offset */
+ } insn[DISAS_ALT_INSN_MAX]; /* alternative instructions */
+ int insn_idx; /* index of the next instruction to print */
};
#define DALT_DEFAULT(dalt) (!(dalt)->alt)
@@ -364,16 +368,14 @@ char *disas_result(struct disas_context *dctx)
disas_print_insn(stdout, dctx, insn, depth, "\n")
/*
- * Print a message in the instruction flow. If insn is not NULL then
- * the instruction address is printed in addition of the message,
- * otherwise only the message is printed. In all cases, the instruction
- * itself is not printed.
+ * Print a message in the instruction flow. If sec is not NULL then the
+ * address at the section offset is printed in addition of the message,
+ * otherwise only the message is printed.
*/
-void disas_print_info(FILE *stream, struct instruction *insn, int depth,
- const char *format, ...)
+static void disas_vprint(FILE *stream, struct section *sec, unsigned long offset,
+ int depth, const char *format, va_list ap)
{
const char *addr_str;
- va_list args;
int len;
int i;
@@ -383,9 +385,9 @@ void disas_print_info(FILE *stream, struct instruction *insn, int depth,
depth = 0;
}
- if (insn && insn->sec) {
- addr_str = offstr(insn->sec, insn->offset);
- fprintf(stream, "%6lx: %-*s ", insn->offset, len, addr_str);
+ if (sec) {
+ addr_str = offstr(sec, offset);
+ fprintf(stream, "%6lx: %-*s ", offset, len, addr_str);
free((char *)addr_str);
} else {
len += DISAS_INSN_OFFSET_SPACE + 1;
@@ -396,11 +398,44 @@ void disas_print_info(FILE *stream, struct instruction *insn, int depth,
for (i = 0; i < depth; i++)
fprintf(stream, "| ");
- if (format) {
- va_start(args, format);
- vfprintf(stream, format, args);
- va_end(args);
+ if (format)
+ vfprintf(stream, format, ap);
+}
+
+static void disas_print(FILE *stream, struct section *sec, unsigned long offset,
+ int depth, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ disas_vprint(stream, sec, offset, depth, format, args);
+ va_end(args);
+}
+
+/*
+ * Print a message in the instruction flow. If insn is not NULL then
+ * the instruction address is printed in addition of the message,
+ * otherwise only the message is printed. In all cases, the instruction
+ * itself is not printed.
+ */
+void disas_print_info(FILE *stream, struct instruction *insn, int depth,
+ const char *format, ...)
+{
+ struct section *sec;
+ unsigned long off;
+ va_list args;
+
+ if (insn) {
+ sec = insn->sec;
+ off = insn->offset;
+ } else {
+ sec = NULL;
+ off = 0;
}
+
+ va_start(args, format);
+ disas_vprint(stream, sec, off, depth, format, args);
+ va_end(args);
}
/*
@@ -534,6 +569,7 @@ static int disas_alt_init(struct disas_alt *dalt,
{
dalt->orig_insn = orig_insn;
dalt->alt = alt;
+ dalt->insn_idx = 0;
dalt->name = alt ? strfmt("ALTERNATIVE %d", alt_num) :
strfmt("<alternative.%lx>", orig_insn->offset);
if (!dalt->name)
@@ -543,7 +579,8 @@ static int disas_alt_init(struct disas_alt *dalt,
return 0;
}
-static int disas_alt_add_insn(struct disas_alt *dalt, int index, char *insn_str)
+static int disas_alt_add_insn(struct disas_alt *dalt, int index, char *insn_str,
+ int offset)
{
int len;
@@ -554,7 +591,8 @@ static int disas_alt_add_insn(struct disas_alt *dalt, int index, char *insn_str)
}
len = strlen(insn_str);
- dalt->insn[index] = insn_str;
+ dalt->insn[index].str = insn_str;
+ dalt->insn[index].offset = offset;
if (len > dalt->width)
dalt->width = len;
@@ -569,12 +607,14 @@ static int disas_alt_group(struct disas_context *dctx, struct disas_alt *dalt)
{
struct objtool_file *file;
struct instruction *insn;
+ int offset;
char *str;
int count;
int err;
file = dctx->file;
count = 0;
+ offset = 0;
alt_for_each_insn(file, DALT_GROUP(dalt), insn) {
@@ -583,9 +623,10 @@ static int disas_alt_group(struct disas_context *dctx, struct disas_alt *dalt)
if (!str)
return -1;
- err = disas_alt_add_insn(dalt, count, str);
+ err = disas_alt_add_insn(dalt, count, str, offset);
if (err)
break;
+ offset += insn->len;
count++;
}
@@ -613,13 +654,63 @@ static int disas_alt_default(struct disas_context *dctx, struct disas_alt *dalt)
str = strdup(disas_result(dctx));
if (!str)
return -1;
- err = disas_alt_add_insn(dalt, 0, str);
+ err = disas_alt_add_insn(dalt, 0, str, 0);
if (err)
return -1;
return 1;
}
+/*
+ * For each alternative, if there is an instruction at the specified
+ * offset then print this instruction, otherwise print a blank entry.
+ * The offset is an offset from the start of the alternative.
+ *
+ * Return the offset for the next instructions to print, or -1 if all
+ * instructions have been printed.
+ */
+static int disas_alt_print_insn(struct disas_alt *alts, int alt_count,
+ int insn_count, int offset)
+{
+ struct disas_alt *dalt;
+ int offset_next;
+ char *str;
+ int i, j;
+
+ offset_next = -1;
+
+ for (i = 0; i < alt_count; i++) {
+ dalt = &alts[i];
+ j = dalt->insn_idx;
+ if (j == -1) {
+ printf("| %-*s ", dalt->width, "");
+ continue;
+ }
+
+ if (dalt->insn[j].offset == offset) {
+ str = dalt->insn[j].str;
+ printf("| %-*s ", dalt->width, str ?: "");
+ free(str);
+ if (++j < insn_count) {
+ dalt->insn_idx = j;
+ } else {
+ dalt->insn_idx = -1;
+ continue;
+ }
+ } else {
+ printf("| %-*s ", dalt->width, "");
+ }
+
+ if (dalt->insn[j].offset > 0 &&
+ (offset_next == -1 ||
+ (dalt->insn[j].offset < offset_next)))
+ offset_next = dalt->insn[j].offset;
+ }
+ printf("\n");
+
+ return offset_next;
+}
+
/*
* Disassemble an alternative.
*
@@ -633,13 +724,14 @@ static void *disas_alt(struct disas_context *dctx,
struct disas_alt alts[DISAS_ALT_MAX] = { 0 };
struct alternative *alt;
struct disas_alt *dalt;
+ int offset_next;
int insn_count;
int alt_count;
int alt_id;
- char *str;
+ int offset;
int count;
- int i, j;
int err;
+ int i;
alt_id = orig_insn->offset;
@@ -700,16 +792,14 @@ static void *disas_alt(struct disas_context *dctx,
/*
* Print instructions for each alternative.
*/
- for (j = 0; j < insn_count; j++) {
- disas_print_info(stdout, NULL, -2, NULL);
- for (i = 0; i < alt_count; i++) {
- dalt = &alts[i];
- str = dalt->insn[j];
- printf("| %-*s ", dalt->width, str ?: "");
- free(str);
- }
- printf("\n");
- }
+ offset_next = 0;
+ do {
+ offset = offset_next;
+ disas_print(stdout, orig_insn->sec, orig_insn->offset + offset,
+ -2, NULL);
+ offset_next = disas_alt_print_insn(alts, alt_count, insn_count,
+ offset);
+ } while (offset_next > offset);
return orig_insn->alt_group ? orig_insn->alt_group->last_insn : orig_insn;
--
2.43.5
Powered by blists - more mailing lists