mirror of
https://github.com/torvalds/linux.git
synced 2025-12-07 20:06:24 +00:00
objtool: Disassemble group alternatives
When using the --disas option, disassemble all group alternatives. Jump tables and exception tables (which are handled as alternatives) are not disassembled at the moment. 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-21-alexandre.chartre@oracle.com
This commit is contained in:
committed by
Peter Zijlstra
parent
87343e6642
commit
a4f1599672
@@ -47,8 +47,14 @@ 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 */
|
||||
};
|
||||
|
||||
#define DALT_DEFAULT(dalt) (!(dalt)->alt)
|
||||
#define DALT_INSN(dalt) (DALT_DEFAULT(dalt) ? (dalt)->orig_insn : (dalt)->alt->insn)
|
||||
#define DALT_GROUP(dalt) (DALT_INSN(dalt)->alt_group)
|
||||
#define DALT_ALTID(dalt) ((dalt)->orig_insn->offset)
|
||||
|
||||
/*
|
||||
* Wrapper around asprintf() to allocate and format a string.
|
||||
* Return the allocated string or NULL on error.
|
||||
@@ -506,6 +512,21 @@ size_t disas_insn(struct disas_context *dctx, struct instruction *insn)
|
||||
return disasm(insn->offset, &dctx->info);
|
||||
}
|
||||
|
||||
static struct instruction *next_insn_same_alt(struct objtool_file *file,
|
||||
struct alt_group *alt_grp,
|
||||
struct instruction *insn)
|
||||
{
|
||||
if (alt_grp->last_insn == insn || alt_grp->nop == insn)
|
||||
return NULL;
|
||||
|
||||
return next_insn_same_sec(file, insn);
|
||||
}
|
||||
|
||||
#define alt_for_each_insn(file, alt_grp, insn) \
|
||||
for (insn = alt_grp->first_insn; \
|
||||
insn; \
|
||||
insn = next_insn_same_alt(file, alt_grp, insn))
|
||||
|
||||
/*
|
||||
* Provide a name for the type of alternatives present at the
|
||||
* specified instruction.
|
||||
@@ -594,23 +615,107 @@ 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)
|
||||
{
|
||||
int len;
|
||||
|
||||
if (index >= DISAS_ALT_INSN_MAX) {
|
||||
WARN("Alternative %lx.%s has more instructions than supported",
|
||||
DALT_ALTID(dalt), dalt->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
len = strlen(insn_str);
|
||||
dalt->insn[index] = insn_str;
|
||||
if (len > dalt->width)
|
||||
dalt->width = len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Disassemble an alternative and store instructions in the disas_alt
|
||||
* structure. Return the number of instructions in the alternative.
|
||||
*/
|
||||
static int disas_alt_group(struct disas_context *dctx, struct disas_alt *dalt)
|
||||
{
|
||||
struct objtool_file *file;
|
||||
struct instruction *insn;
|
||||
char *str;
|
||||
int count;
|
||||
int err;
|
||||
|
||||
file = dctx->file;
|
||||
count = 0;
|
||||
|
||||
alt_for_each_insn(file, DALT_GROUP(dalt), insn) {
|
||||
|
||||
disas_insn(dctx, insn);
|
||||
str = strdup(disas_result(dctx));
|
||||
if (!str)
|
||||
return -1;
|
||||
|
||||
err = disas_alt_add_insn(dalt, count, str);
|
||||
if (err)
|
||||
break;
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
* Disassemble the default alternative.
|
||||
*/
|
||||
static int disas_alt_default(struct disas_context *dctx, struct disas_alt *dalt)
|
||||
{
|
||||
char *str;
|
||||
int err;
|
||||
|
||||
if (DALT_GROUP(dalt))
|
||||
return disas_alt_group(dctx, dalt);
|
||||
|
||||
/*
|
||||
* Default alternative with no alt_group: this is the default
|
||||
* code associated with either a jump table or an exception
|
||||
* table and no other instruction alternatives. In that case
|
||||
* the default alternative is made of a single instruction.
|
||||
*/
|
||||
disas_insn(dctx, dalt->orig_insn);
|
||||
str = strdup(disas_result(dctx));
|
||||
if (!str)
|
||||
return -1;
|
||||
err = disas_alt_add_insn(dalt, 0, str);
|
||||
if (err)
|
||||
return -1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Print all alternatives one above the other.
|
||||
*/
|
||||
static void disas_alt_print_compact(char *alt_name, struct disas_alt *dalts,
|
||||
int alt_count)
|
||||
int alt_count, int insn_count)
|
||||
{
|
||||
struct instruction *orig_insn;
|
||||
int i, j;
|
||||
int len;
|
||||
int i;
|
||||
|
||||
orig_insn = dalts[0].orig_insn;
|
||||
|
||||
len = disas_print(stdout, orig_insn->sec, orig_insn->offset, 0, NULL);
|
||||
printf("%s\n", alt_name);
|
||||
|
||||
for (i = 0; i < alt_count; i++)
|
||||
for (i = 0; i < alt_count; i++) {
|
||||
printf("%*s= %s\n", len, "", dalts[i].name);
|
||||
for (j = 0; j < insn_count; j++) {
|
||||
if (!dalts[i].insn[j])
|
||||
break;
|
||||
printf("%*s| %s\n", len, "", dalts[i].insn[j]);
|
||||
}
|
||||
printf("%*s|\n", len, "");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -624,11 +729,15 @@ static void *disas_alt(struct disas_context *dctx,
|
||||
struct instruction *orig_insn)
|
||||
{
|
||||
struct disas_alt dalts[DISAS_ALT_MAX] = { 0 };
|
||||
struct instruction *last_insn = NULL;
|
||||
struct alternative *alt;
|
||||
struct disas_alt *dalt;
|
||||
int insn_count = 0;
|
||||
int alt_count = 0;
|
||||
char *alt_name;
|
||||
int count;
|
||||
int i, j;
|
||||
int err;
|
||||
int i;
|
||||
|
||||
alt_name = strfmt("<%s.%lx>", disas_alt_type_name(orig_insn),
|
||||
orig_insn->offset);
|
||||
@@ -639,7 +748,7 @@ static void *disas_alt(struct disas_context *dctx,
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the default alternative.
|
||||
* Initialize and disassemble the default alternative.
|
||||
*/
|
||||
err = disas_alt_init(&dalts[0], orig_insn, NULL);
|
||||
if (err) {
|
||||
@@ -647,8 +756,14 @@ static void *disas_alt(struct disas_context *dctx,
|
||||
goto done;
|
||||
}
|
||||
|
||||
insn_count = disas_alt_default(dctx, &dalts[0]);
|
||||
if (insn_count < 0) {
|
||||
WARN("%s: failed to disassemble default alternative", alt_name);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize all other alternatives.
|
||||
* Initialize and disassemble all other alternatives.
|
||||
*/
|
||||
i = 1;
|
||||
for (alt = orig_insn->alts; alt; alt = alt->next) {
|
||||
@@ -656,35 +771,52 @@ static void *disas_alt(struct disas_context *dctx,
|
||||
WARN("%s has more alternatives than supported", alt_name);
|
||||
break;
|
||||
}
|
||||
err = disas_alt_init(&dalts[i], orig_insn, alt);
|
||||
dalt = &dalts[i];
|
||||
err = disas_alt_init(dalt, orig_insn, alt);
|
||||
if (err) {
|
||||
WARN("%s: failed to disassemble alternative", alt_name);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*
|
||||
* Only group alternatives are supported at the moment.
|
||||
*/
|
||||
switch (dalt->alt->type) {
|
||||
case ALT_TYPE_INSTRUCTIONS:
|
||||
count = disas_alt_group(dctx, dalt);
|
||||
break;
|
||||
default:
|
||||
count = 0;
|
||||
}
|
||||
if (count < 0) {
|
||||
WARN("%s: failed to disassemble alternative %s",
|
||||
alt_name, dalt->name);
|
||||
goto done;
|
||||
}
|
||||
|
||||
insn_count = count > insn_count ? count : insn_count;
|
||||
i++;
|
||||
}
|
||||
alt_count = i;
|
||||
|
||||
/*
|
||||
* Print default and non-default alternatives.
|
||||
*
|
||||
* At the moment, this just prints an header for each alternative.
|
||||
*/
|
||||
disas_alt_print_compact(alt_name, dalts, alt_count);
|
||||
disas_alt_print_compact(alt_name, dalts, alt_count, insn_count);
|
||||
|
||||
last_insn = orig_insn->alt_group ? orig_insn->alt_group->last_insn :
|
||||
orig_insn;
|
||||
|
||||
done:
|
||||
for (i = 0; i < alt_count; i++)
|
||||
for (i = 0; i < alt_count; i++) {
|
||||
free(dalts[i].name);
|
||||
for (j = 0; j < insn_count; j++)
|
||||
free(dalts[i].insn[j]);
|
||||
}
|
||||
|
||||
free(alt_name);
|
||||
|
||||
/*
|
||||
* Currently we are not disassembling any alternative but just
|
||||
* printing alternative names. Return NULL to have disas_func()
|
||||
* resume the disassembly with the default alternative.
|
||||
*/
|
||||
return NULL;
|
||||
return last_insn;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user