// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2015-2017 Josh Poimboeuf <jpoimboe@redhat.com>
*/
#define _GNU_SOURCE
#include <fnmatch.h>
#include <objtool/arch.h>
#include <objtool/check.h>
#include <objtool/disas.h>
#include <objtool/special.h>
#include <objtool/warn.h>
#include <bfd.h>
#include <linux/string.h>
#include <tools/dis-asm-compat.h>
/*
* Size of the buffer for storing the result of disassembling
* a single instruction.
*/
#define DISAS_RESULT_SIZE 1024
struct disas_context {
struct objtool_file *file;
struct instruction *insn;
bool alt_applied;
char result[DISAS_RESULT_SIZE];
disassembler_ftype disassembler;
struct disassemble_info info;
};
/*
* Maximum number of alternatives
*/
#define DISAS_ALT_MAX 5
/*
* Maximum number of instructions per alternative
*/
#define DISAS_ALT_INSN_MAX 50
/*
* Information to disassemble an alternative
*/
struct disas_alt {
struct instruction *orig_insn; /* original instruction */
struct alternative *alt; /* alternative or NULL if default code */
char *name; /* name for this alternative */
int width; /* formatting width */
struct {
char *str; /* instruction string */
int offset; /* instruction offset */
int nops; /* number of nops */
} insn[DISAS_ALT_INSN_MAX]; /* alternative instructions */
int insn_idx; /* index of the next instruction to print */
};
#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)
#define ALT_FLAGS_SHIFT 16
#define ALT_FLAG_NOT (1 << 0)
#define ALT_FLAG_DIRECT_CALL (1 << 1)
#define ALT_FEATURE_MASK ((1 << ALT_FLAGS_SHIFT) - 1)
static int alt_feature(unsigned int ft_flags)
{
return (ft_flags & ALT_FEATURE_MASK);
}
static int alt_flags(unsigned int ft_flags)
{
return (ft_flags >> ALT_FLAGS_SHIFT);
}
/*
* Wrapper around asprintf() to allocate and format a string.
* Return the allocated string or NULL on error.
*/
static char *strfmt(const char *fmt, ...)
{
va_list ap;
char *str;
int rv;
va_start(ap, fmt);
rv = vasprintf(&str, fmt, ap);
va_end(ap);
return rv == -1 ? NULL : str;
}
static int sprint_name(char *str, const char *name, unsigned long offset)
{
int len;
if (offset)
len = sprintf(str, "%s+0x%lx", name, offset);
else
len = sprintf(str, "%s", name);
return len;
}
#define DINFO_FPRINTF(dinfo, ...) \
((*(dinfo)->fprintf_func)((dinfo)->stream, __VA_ARGS__))
static int disas_result_fprintf(struct disas_context *dctx,
const char *fmt, va_list ap)
{
char *buf = dctx->result;
int avail, len;
len =