Merge branch 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull perf updates from Ingo Molnar: "Kernel side changes: - Improved kbprobes robustness - Intel PEBS support for PT hardware tracing - Other Intel PT improvements: high order pages memory footprint reduction and various related cleanups - Misc cleanups The perf tooling side has been very busy in this cycle, with over 300 commits. This is an incomplete high-level summary of the many improvements done by over 30 developers: - Lots of updates to the following tools: 'perf c2c' 'perf config' 'perf record' 'perf report' 'perf script' 'perf test' 'perf top' 'perf trace' - Updates to libperf and libtraceevent, and a consolidation of the proliferation of x86 instruction decoder libraries. - Vendor event updates for Intel and PowerPC CPUs, - Updates to hardware tracing tooling for ARM and Intel CPUs, - ... and lots of other changes and cleanups - see the shortlog and Git log for details" * 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (322 commits) kprobes: Prohibit probing on BUG() and WARN() address perf/x86: Make more stuff static x86, perf: Fix the dependency of the x86 insn decoder selftest objtool: Ignore intentional differences for the x86 insn decoder objtool: Update sync-check.sh from perf's check-headers.sh perf build: Ignore intentional differences for the x86 insn decoder perf intel-pt: Use shared x86 insn decoder perf intel-pt: Remove inat.c from build dependency list perf: Update .gitignore file objtool: Move x86 insn decoder to a common location perf metricgroup: Support multiple events for metricgroup perf metricgroup: Scale the metric result perf pmu: Change convert_scale from static to global perf symbols: Move mem_info and branch_info out of symbol.h perf auxtrace: Uninline functions that touch perf_session perf tools: Remove needless evlist.h include directives perf tools: Remove needless evlist.h include directives perf tools: Remove needless thread_map.h include directives perf tools: Remove needless thread.h include directives perf tools: Remove needless map.h include directives ...
This commit is contained in:
@@ -281,6 +281,8 @@
|
||||
#define X86_FEATURE_CQM_OCCUP_LLC (11*32+ 1) /* LLC occupancy monitoring */
|
||||
#define X86_FEATURE_CQM_MBM_TOTAL (11*32+ 2) /* LLC Total MBM monitoring */
|
||||
#define X86_FEATURE_CQM_MBM_LOCAL (11*32+ 3) /* LLC Local MBM monitoring */
|
||||
#define X86_FEATURE_FENCE_SWAPGS_USER (11*32+ 4) /* "" LFENCE in user entry SWAPGS path */
|
||||
#define X86_FEATURE_FENCE_SWAPGS_KERNEL (11*32+ 5) /* "" LFENCE in kernel entry SWAPGS path */
|
||||
|
||||
/* Intel-defined CPU features, CPUID level 0x00000007:1 (EAX), word 12 */
|
||||
#define X86_FEATURE_AVX512_BF16 (12*32+ 5) /* AVX512 BFLOAT16 instructions */
|
||||
@@ -394,5 +396,6 @@
|
||||
#define X86_BUG_L1TF X86_BUG(18) /* CPU is affected by L1 Terminal Fault */
|
||||
#define X86_BUG_MDS X86_BUG(19) /* CPU is affected by Microarchitectural data sampling */
|
||||
#define X86_BUG_MSBDS_ONLY X86_BUG(20) /* CPU is only affected by the MSDBS variant of BUG_MDS */
|
||||
#define X86_BUG_SWAPGS X86_BUG(21) /* CPU is affected by speculation through SWAPGS */
|
||||
|
||||
#endif /* _ASM_X86_CPUFEATURES_H */
|
||||
|
230
tools/arch/x86/include/asm/inat.h
Normal file
230
tools/arch/x86/include/asm/inat.h
Normal file
@@ -0,0 +1,230 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
#ifndef _ASM_X86_INAT_H
|
||||
#define _ASM_X86_INAT_H
|
||||
/*
|
||||
* x86 instruction attributes
|
||||
*
|
||||
* Written by Masami Hiramatsu <mhiramat@redhat.com>
|
||||
*/
|
||||
#include "inat_types.h"
|
||||
|
||||
/*
|
||||
* Internal bits. Don't use bitmasks directly, because these bits are
|
||||
* unstable. You should use checking functions.
|
||||
*/
|
||||
|
||||
#define INAT_OPCODE_TABLE_SIZE 256
|
||||
#define INAT_GROUP_TABLE_SIZE 8
|
||||
|
||||
/* Legacy last prefixes */
|
||||
#define INAT_PFX_OPNDSZ 1 /* 0x66 */ /* LPFX1 */
|
||||
#define INAT_PFX_REPE 2 /* 0xF3 */ /* LPFX2 */
|
||||
#define INAT_PFX_REPNE 3 /* 0xF2 */ /* LPFX3 */
|
||||
/* Other Legacy prefixes */
|
||||
#define INAT_PFX_LOCK 4 /* 0xF0 */
|
||||
#define INAT_PFX_CS 5 /* 0x2E */
|
||||
#define INAT_PFX_DS 6 /* 0x3E */
|
||||
#define INAT_PFX_ES 7 /* 0x26 */
|
||||
#define INAT_PFX_FS 8 /* 0x64 */
|
||||
#define INAT_PFX_GS 9 /* 0x65 */
|
||||
#define INAT_PFX_SS 10 /* 0x36 */
|
||||
#define INAT_PFX_ADDRSZ 11 /* 0x67 */
|
||||
/* x86-64 REX prefix */
|
||||
#define INAT_PFX_REX 12 /* 0x4X */
|
||||
/* AVX VEX prefixes */
|
||||
#define INAT_PFX_VEX2 13 /* 2-bytes VEX prefix */
|
||||
#define INAT_PFX_VEX3 14 /* 3-bytes VEX prefix */
|
||||
#define INAT_PFX_EVEX 15 /* EVEX prefix */
|
||||
|
||||
#define INAT_LSTPFX_MAX 3
|
||||
#define INAT_LGCPFX_MAX 11
|
||||
|
||||
/* Immediate size */
|
||||
#define INAT_IMM_BYTE 1
|
||||
#define INAT_IMM_WORD 2
|
||||
#define INAT_IMM_DWORD 3
|
||||
#define INAT_IMM_QWORD 4
|
||||
#define INAT_IMM_PTR 5
|
||||
#define INAT_IMM_VWORD32 6
|
||||
#define INAT_IMM_VWORD 7
|
||||
|
||||
/* Legacy prefix */
|
||||
#define INAT_PFX_OFFS 0
|
||||
#define INAT_PFX_BITS 4
|
||||
#define INAT_PFX_MAX ((1 << INAT_PFX_BITS) - 1)
|
||||
#define INAT_PFX_MASK (INAT_PFX_MAX << INAT_PFX_OFFS)
|
||||
/* Escape opcodes */
|
||||
#define INAT_ESC_OFFS (INAT_PFX_OFFS + INAT_PFX_BITS)
|
||||
#define INAT_ESC_BITS 2
|
||||
#define INAT_ESC_MAX ((1 << INAT_ESC_BITS) - 1)
|
||||
#define INAT_ESC_MASK (INAT_ESC_MAX << INAT_ESC_OFFS)
|
||||
/* Group opcodes (1-16) */
|
||||
#define INAT_GRP_OFFS (INAT_ESC_OFFS + INAT_ESC_BITS)
|
||||
#define INAT_GRP_BITS 5
|
||||
#define INAT_GRP_MAX ((1 << INAT_GRP_BITS) - 1)
|
||||
#define INAT_GRP_MASK (INAT_GRP_MAX << INAT_GRP_OFFS)
|
||||
/* Immediates */
|
||||
#define INAT_IMM_OFFS (INAT_GRP_OFFS + INAT_GRP_BITS)
|
||||
#define INAT_IMM_BITS 3
|
||||
#define INAT_IMM_MASK (((1 << INAT_IMM_BITS) - 1) << INAT_IMM_OFFS)
|
||||
/* Flags */
|
||||
#define INAT_FLAG_OFFS (INAT_IMM_OFFS + INAT_IMM_BITS)
|
||||
#define INAT_MODRM (1 << (INAT_FLAG_OFFS))
|
||||
#define INAT_FORCE64 (1 << (INAT_FLAG_OFFS + 1))
|
||||
#define INAT_SCNDIMM (1 << (INAT_FLAG_OFFS + 2))
|
||||
#define INAT_MOFFSET (1 << (INAT_FLAG_OFFS + 3))
|
||||
#define INAT_VARIANT (1 << (INAT_FLAG_OFFS + 4))
|
||||
#define INAT_VEXOK (1 << (INAT_FLAG_OFFS + 5))
|
||||
#define INAT_VEXONLY (1 << (INAT_FLAG_OFFS + 6))
|
||||
#define INAT_EVEXONLY (1 << (INAT_FLAG_OFFS + 7))
|
||||
/* Attribute making macros for attribute tables */
|
||||
#define INAT_MAKE_PREFIX(pfx) (pfx << INAT_PFX_OFFS)
|
||||
#define INAT_MAKE_ESCAPE(esc) (esc << INAT_ESC_OFFS)
|
||||
#define INAT_MAKE_GROUP(grp) ((grp << INAT_GRP_OFFS) | INAT_MODRM)
|
||||
#define INAT_MAKE_IMM(imm) (imm << INAT_IMM_OFFS)
|
||||
|
||||
/* Identifiers for segment registers */
|
||||
#define INAT_SEG_REG_IGNORE 0
|
||||
#define INAT_SEG_REG_DEFAULT 1
|
||||
#define INAT_SEG_REG_CS 2
|
||||
#define INAT_SEG_REG_SS 3
|
||||
#define INAT_SEG_REG_DS 4
|
||||
#define INAT_SEG_REG_ES 5
|
||||
#define INAT_SEG_REG_FS 6
|
||||
#define INAT_SEG_REG_GS 7
|
||||
|
||||
/* Attribute search APIs */
|
||||
extern insn_attr_t inat_get_opcode_attribute(insn_byte_t opcode);
|
||||
extern int inat_get_last_prefix_id(insn_byte_t last_pfx);
|
||||
extern insn_attr_t inat_get_escape_attribute(insn_byte_t opcode,
|
||||
int lpfx_id,
|
||||
insn_attr_t esc_attr);
|
||||
extern insn_attr_t inat_get_group_attribute(insn_byte_t modrm,
|
||||
int lpfx_id,
|
||||
insn_attr_t esc_attr);
|
||||
extern insn_attr_t inat_get_avx_attribute(insn_byte_t opcode,
|
||||
insn_byte_t vex_m,
|
||||
insn_byte_t vex_pp);
|
||||
|
||||
/* Attribute checking functions */
|
||||
static inline int inat_is_legacy_prefix(insn_attr_t attr)
|
||||
{
|
||||
attr &= INAT_PFX_MASK;
|
||||
return attr && attr <= INAT_LGCPFX_MAX;
|
||||
}
|
||||
|
||||
static inline int inat_is_address_size_prefix(insn_attr_t attr)
|
||||
{
|
||||
return (attr & INAT_PFX_MASK) == INAT_PFX_ADDRSZ;
|
||||
}
|
||||
|
||||
static inline int inat_is_operand_size_prefix(insn_attr_t attr)
|
||||
{
|
||||
return (attr & INAT_PFX_MASK) == INAT_PFX_OPNDSZ;
|
||||
}
|
||||
|
||||
static inline int inat_is_rex_prefix(insn_attr_t attr)
|
||||
{
|
||||
return (attr & INAT_PFX_MASK) == INAT_PFX_REX;
|
||||
}
|
||||
|
||||
static inline int inat_last_prefix_id(insn_attr_t attr)
|
||||
{
|
||||
if ((attr & INAT_PFX_MASK) > INAT_LSTPFX_MAX)
|
||||
return 0;
|
||||
else
|
||||
return attr & INAT_PFX_MASK;
|
||||
}
|
||||
|
||||
static inline int inat_is_vex_prefix(insn_attr_t attr)
|
||||
{
|
||||
attr &= INAT_PFX_MASK;
|
||||
return attr == INAT_PFX_VEX2 || attr == INAT_PFX_VEX3 ||
|
||||
attr == INAT_PFX_EVEX;
|
||||
}
|
||||
|
||||
static inline int inat_is_evex_prefix(insn_attr_t attr)
|
||||
{
|
||||
return (attr & INAT_PFX_MASK) == INAT_PFX_EVEX;
|
||||
}
|
||||
|
||||
static inline int inat_is_vex3_prefix(insn_attr_t attr)
|
||||
{
|
||||
return (attr & INAT_PFX_MASK) == INAT_PFX_VEX3;
|
||||
}
|
||||
|
||||
static inline int inat_is_escape(insn_attr_t attr)
|
||||
{
|
||||
return attr & INAT_ESC_MASK;
|
||||
}
|
||||
|
||||
static inline int inat_escape_id(insn_attr_t attr)
|
||||
{
|
||||
return (attr & INAT_ESC_MASK) >> INAT_ESC_OFFS;
|
||||
}
|
||||
|
||||
static inline int inat_is_group(insn_attr_t attr)
|
||||
{
|
||||
return attr & INAT_GRP_MASK;
|
||||
}
|
||||
|
||||
static inline int inat_group_id(insn_attr_t attr)
|
||||
{
|
||||
return (attr & INAT_GRP_MASK) >> INAT_GRP_OFFS;
|
||||
}
|
||||
|
||||
static inline int inat_group_common_attribute(insn_attr_t attr)
|
||||
{
|
||||
return attr & ~INAT_GRP_MASK;
|
||||
}
|
||||
|
||||
static inline int inat_has_immediate(insn_attr_t attr)
|
||||
{
|
||||
return attr & INAT_IMM_MASK;
|
||||
}
|
||||
|
||||
static inline int inat_immediate_size(insn_attr_t attr)
|
||||
{
|
||||
return (attr & INAT_IMM_MASK) >> INAT_IMM_OFFS;
|
||||
}
|
||||
|
||||
static inline int inat_has_modrm(insn_attr_t attr)
|
||||
{
|
||||
return attr & INAT_MODRM;
|
||||
}
|
||||
|
||||
static inline int inat_is_force64(insn_attr_t attr)
|
||||
{
|
||||
return attr & INAT_FORCE64;
|
||||
}
|
||||
|
||||
static inline int inat_has_second_immediate(insn_attr_t attr)
|
||||
{
|
||||
return attr & INAT_SCNDIMM;
|
||||
}
|
||||
|
||||
static inline int inat_has_moffset(insn_attr_t attr)
|
||||
{
|
||||
return attr & INAT_MOFFSET;
|
||||
}
|
||||
|
||||
static inline int inat_has_variant(insn_attr_t attr)
|
||||
{
|
||||
return attr & INAT_VARIANT;
|
||||
}
|
||||
|
||||
static inline int inat_accept_vex(insn_attr_t attr)
|
||||
{
|
||||
return attr & INAT_VEXOK;
|
||||
}
|
||||
|
||||
static inline int inat_must_vex(insn_attr_t attr)
|
||||
{
|
||||
return attr & (INAT_VEXONLY | INAT_EVEXONLY);
|
||||
}
|
||||
|
||||
static inline int inat_must_evex(insn_attr_t attr)
|
||||
{
|
||||
return attr & INAT_EVEXONLY;
|
||||
}
|
||||
#endif
|
15
tools/arch/x86/include/asm/inat_types.h
Normal file
15
tools/arch/x86/include/asm/inat_types.h
Normal file
@@ -0,0 +1,15 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
#ifndef _ASM_X86_INAT_TYPES_H
|
||||
#define _ASM_X86_INAT_TYPES_H
|
||||
/*
|
||||
* x86 instruction attributes
|
||||
*
|
||||
* Written by Masami Hiramatsu <mhiramat@redhat.com>
|
||||
*/
|
||||
|
||||
/* Instruction attributes */
|
||||
typedef unsigned int insn_attr_t;
|
||||
typedef unsigned char insn_byte_t;
|
||||
typedef signed int insn_value_t;
|
||||
|
||||
#endif
|
216
tools/arch/x86/include/asm/insn.h
Normal file
216
tools/arch/x86/include/asm/insn.h
Normal file
@@ -0,0 +1,216 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
#ifndef _ASM_X86_INSN_H
|
||||
#define _ASM_X86_INSN_H
|
||||
/*
|
||||
* x86 instruction analysis
|
||||
*
|
||||
* Copyright (C) IBM Corporation, 2009
|
||||
*/
|
||||
|
||||
/* insn_attr_t is defined in inat.h */
|
||||
#include "inat.h"
|
||||
|
||||
struct insn_field {
|
||||
union {
|
||||
insn_value_t value;
|
||||
insn_byte_t bytes[4];
|
||||
};
|
||||
/* !0 if we've run insn_get_xxx() for this field */
|
||||
unsigned char got;
|
||||
unsigned char nbytes;
|
||||
};
|
||||
|
||||
struct insn {
|
||||
struct insn_field prefixes; /*
|
||||
* Prefixes
|
||||
* prefixes.bytes[3]: last prefix
|
||||
*/
|
||||
struct insn_field rex_prefix; /* REX prefix */
|
||||
struct insn_field vex_prefix; /* VEX prefix */
|
||||
struct insn_field opcode; /*
|
||||
* opcode.bytes[0]: opcode1
|
||||
* opcode.bytes[1]: opcode2
|
||||
* opcode.bytes[2]: opcode3
|
||||
*/
|
||||
struct insn_field modrm;
|
||||
struct insn_field sib;
|
||||
struct insn_field displacement;
|
||||
union {
|
||||
struct insn_field immediate;
|
||||
struct insn_field moffset1; /* for 64bit MOV */
|
||||
struct insn_field immediate1; /* for 64bit imm or off16/32 */
|
||||
};
|
||||
union {
|
||||
struct insn_field moffset2; /* for 64bit MOV */
|
||||
struct insn_field immediate2; /* for 64bit imm or seg16 */
|
||||
};
|
||||
|
||||
insn_attr_t attr;
|
||||
unsigned char opnd_bytes;
|
||||
unsigned char addr_bytes;
|
||||
unsigned char length;
|
||||
unsigned char x86_64;
|
||||
|
||||
const insn_byte_t *kaddr; /* kernel address of insn to analyze */
|
||||
const insn_byte_t *end_kaddr; /* kernel address of last insn in buffer */
|
||||
const insn_byte_t *next_byte;
|
||||
};
|
||||
|
||||
#define MAX_INSN_SIZE 15
|
||||
|
||||
#define X86_MODRM_MOD(modrm) (((modrm) & 0xc0) >> 6)
|
||||
#define X86_MODRM_REG(modrm) (((modrm) & 0x38) >> 3)
|
||||
#define X86_MODRM_RM(modrm) ((modrm) & 0x07)
|
||||
|
||||
#define X86_SIB_SCALE(sib) (((sib) & 0xc0) >> 6)
|
||||
#define X86_SIB_INDEX(sib) (((sib) & 0x38) >> 3)
|
||||
#define X86_SIB_BASE(sib) ((sib) & 0x07)
|
||||
|
||||
#define X86_REX_W(rex) ((rex) & 8)
|
||||
#define X86_REX_R(rex) ((rex) & 4)
|
||||
#define X86_REX_X(rex) ((rex) & 2)
|
||||
#define X86_REX_B(rex) ((rex) & 1)
|
||||
|
||||
/* VEX bit flags */
|
||||
#define X86_VEX_W(vex) ((vex) & 0x80) /* VEX3 Byte2 */
|
||||
#define X86_VEX_R(vex) ((vex) & 0x80) /* VEX2/3 Byte1 */
|
||||
#define X86_VEX_X(vex) ((vex) & 0x40) /* VEX3 Byte1 */
|
||||
#define X86_VEX_B(vex) ((vex) & 0x20) /* VEX3 Byte1 */
|
||||
#define X86_VEX_L(vex) ((vex) & 0x04) /* VEX3 Byte2, VEX2 Byte1 */
|
||||
/* VEX bit fields */
|
||||
#define X86_EVEX_M(vex) ((vex) & 0x03) /* EVEX Byte1 */
|
||||
#define X86_VEX3_M(vex) ((vex) & 0x1f) /* VEX3 Byte1 */
|
||||
#define X86_VEX2_M 1 /* VEX2.M always 1 */
|
||||
#define X86_VEX_V(vex) (((vex) & 0x78) >> 3) /* VEX3 Byte2, VEX2 Byte1 */
|
||||
#define X86_VEX_P(vex) ((vex) & 0x03) /* VEX3 Byte2, VEX2 Byte1 */
|
||||
#define X86_VEX_M_MAX 0x1f /* VEX3.M Maximum value */
|
||||
|
||||
extern void insn_init(struct insn *insn, const void *kaddr, int buf_len, int x86_64);
|
||||
extern void insn_get_prefixes(struct insn *insn);
|
||||
extern void insn_get_opcode(struct insn *insn);
|
||||
extern void insn_get_modrm(struct insn *insn);
|
||||
extern void insn_get_sib(struct insn *insn);
|
||||
extern void insn_get_displacement(struct insn *insn);
|
||||
extern void insn_get_immediate(struct insn *insn);
|
||||
extern void insn_get_length(struct insn *insn);
|
||||
|
||||
/* Attribute will be determined after getting ModRM (for opcode groups) */
|
||||
static inline void insn_get_attribute(struct insn *insn)
|
||||
{
|
||||
insn_get_modrm(insn);
|
||||
}
|
||||
|
||||
/* Instruction uses RIP-relative addressing */
|
||||
extern int insn_rip_relative(struct insn *insn);
|
||||
|
||||
/* Init insn for kernel text */
|
||||
static inline void kernel_insn_init(struct insn *insn,
|
||||
const void *kaddr, int buf_len)
|
||||
{
|
||||
#ifdef CONFIG_X86_64
|
||||
insn_init(insn, kaddr, buf_len, 1);
|
||||
#else /* CONFIG_X86_32 */
|
||||
insn_init(insn, kaddr, buf_len, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int insn_is_avx(struct insn *insn)
|
||||
{
|
||||
if (!insn->prefixes.got)
|
||||
insn_get_prefixes(insn);
|
||||
return (insn->vex_prefix.value != 0);
|
||||
}
|
||||
|
||||
static inline int insn_is_evex(struct insn *insn)
|
||||
{
|
||||
if (!insn->prefixes.got)
|
||||
insn_get_prefixes(insn);
|
||||
return (insn->vex_prefix.nbytes == 4);
|
||||
}
|
||||
|
||||
/* Ensure this instruction is decoded completely */
|
||||
static inline int insn_complete(struct insn *insn)
|
||||
{
|
||||
return insn->opcode.got && insn->modrm.got && insn->sib.got &&
|
||||
insn->displacement.got && insn->immediate.got;
|
||||
}
|
||||
|
||||
static inline insn_byte_t insn_vex_m_bits(struct insn *insn)
|
||||
{
|
||||
if (insn->vex_prefix.nbytes == 2) /* 2 bytes VEX */
|
||||
return X86_VEX2_M;
|
||||
else if (insn->vex_prefix.nbytes == 3) /* 3 bytes VEX */
|
||||
return X86_VEX3_M(insn->vex_prefix.bytes[1]);
|
||||
else /* EVEX */
|
||||
return X86_EVEX_M(insn->vex_prefix.bytes[1]);
|
||||
}
|
||||
|
||||
static inline insn_byte_t insn_vex_p_bits(struct insn *insn)
|
||||
{
|
||||
if (insn->vex_prefix.nbytes == 2) /* 2 bytes VEX */
|
||||
return X86_VEX_P(insn->vex_prefix.bytes[1]);
|
||||
else
|
||||
return X86_VEX_P(insn->vex_prefix.bytes[2]);
|
||||
}
|
||||
|
||||
/* Get the last prefix id from last prefix or VEX prefix */
|
||||
static inline int insn_last_prefix_id(struct insn *insn)
|
||||
{
|
||||
if (insn_is_avx(insn))
|
||||
return insn_vex_p_bits(insn); /* VEX_p is a SIMD prefix id */
|
||||
|
||||
if (insn->prefixes.bytes[3])
|
||||
return inat_get_last_prefix_id(insn->prefixes.bytes[3]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Offset of each field from kaddr */
|
||||
static inline int insn_offset_rex_prefix(struct insn *insn)
|
||||
{
|
||||
return insn->prefixes.nbytes;
|
||||
}
|
||||
static inline int insn_offset_vex_prefix(struct insn *insn)
|
||||
{
|
||||
return insn_offset_rex_prefix(insn) + insn->rex_prefix.nbytes;
|
||||
}
|
||||
static inline int insn_offset_opcode(struct insn *insn)
|
||||
{
|
||||
return insn_offset_vex_prefix(insn) + insn->vex_prefix.nbytes;
|
||||
}
|
||||
static inline int insn_offset_modrm(struct insn *insn)
|
||||
{
|
||||
return insn_offset_opcode(insn) + insn->opcode.nbytes;
|
||||
}
|
||||
static inline int insn_offset_sib(struct insn *insn)
|
||||
{
|
||||
return insn_offset_modrm(insn) + insn->modrm.nbytes;
|
||||
}
|
||||
static inline int insn_offset_displacement(struct insn *insn)
|
||||
{
|
||||
return insn_offset_sib(insn) + insn->sib.nbytes;
|
||||
}
|
||||
static inline int insn_offset_immediate(struct insn *insn)
|
||||
{
|
||||
return insn_offset_displacement(insn) + insn->displacement.nbytes;
|
||||
}
|
||||
|
||||
#define POP_SS_OPCODE 0x1f
|
||||
#define MOV_SREG_OPCODE 0x8e
|
||||
|
||||
/*
|
||||
* Intel SDM Vol.3A 6.8.3 states;
|
||||
* "Any single-step trap that would be delivered following the MOV to SS
|
||||
* instruction or POP to SS instruction (because EFLAGS.TF is 1) is
|
||||
* suppressed."
|
||||
* This function returns true if @insn is MOV SS or POP SS. On these
|
||||
* instructions, single stepping is suppressed.
|
||||
*/
|
||||
static inline int insn_masking_exception(struct insn *insn)
|
||||
{
|
||||
return insn->opcode.bytes[0] == POP_SS_OPCODE ||
|
||||
(insn->opcode.bytes[0] == MOV_SREG_OPCODE &&
|
||||
X86_MODRM_REG(insn->modrm.bytes[0]) == 2);
|
||||
}
|
||||
|
||||
#endif /* _ASM_X86_INSN_H */
|
97
tools/arch/x86/include/asm/orc_types.h
Normal file
97
tools/arch/x86/include/asm/orc_types.h
Normal file
@@ -0,0 +1,97 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com>
|
||||
*/
|
||||
|
||||
#ifndef _ORC_TYPES_H
|
||||
#define _ORC_TYPES_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/compiler.h>
|
||||
|
||||
/*
|
||||
* The ORC_REG_* registers are base registers which are used to find other
|
||||
* registers on the stack.
|
||||
*
|
||||
* ORC_REG_PREV_SP, also known as DWARF Call Frame Address (CFA), is the
|
||||
* address of the previous frame: the caller's SP before it called the current
|
||||
* function.
|
||||
*
|
||||
* ORC_REG_UNDEFINED means the corresponding register's value didn't change in
|
||||
* the current frame.
|
||||
*
|
||||
* The most commonly used base registers are SP and BP -- which the previous SP
|
||||
* is usually based on -- and PREV_SP and UNDEFINED -- which the previous BP is
|
||||
* usually based on.
|
||||
*
|
||||
* The rest of the base registers are needed for special cases like entry code
|
||||
* and GCC realigned stacks.
|
||||
*/
|
||||
#define ORC_REG_UNDEFINED 0
|
||||
#define ORC_REG_PREV_SP 1
|
||||
#define ORC_REG_DX 2
|
||||
#define ORC_REG_DI 3
|
||||
#define ORC_REG_BP 4
|
||||
#define ORC_REG_SP 5
|
||||
#define ORC_REG_R10 6
|
||||
#define ORC_REG_R13 7
|
||||
#define ORC_REG_BP_INDIRECT 8
|
||||
#define ORC_REG_SP_INDIRECT 9
|
||||
#define ORC_REG_MAX 15
|
||||
|
||||
/*
|
||||
* ORC_TYPE_CALL: Indicates that sp_reg+sp_offset resolves to PREV_SP (the
|
||||
* caller's SP right before it made the call). Used for all callable
|
||||
* functions, i.e. all C code and all callable asm functions.
|
||||
*
|
||||
* ORC_TYPE_REGS: Used in entry code to indicate that sp_reg+sp_offset points
|
||||
* to a fully populated pt_regs from a syscall, interrupt, or exception.
|
||||
*
|
||||
* ORC_TYPE_REGS_IRET: Used in entry code to indicate that sp_reg+sp_offset
|
||||
* points to the iret return frame.
|
||||
*
|
||||
* The UNWIND_HINT macros are used only for the unwind_hint struct. They
|
||||
* aren't used in struct orc_entry due to size and complexity constraints.
|
||||
* Objtool converts them to real types when it converts the hints to orc
|
||||
* entries.
|
||||
*/
|
||||
#define ORC_TYPE_CALL 0
|
||||
#define ORC_TYPE_REGS 1
|
||||
#define ORC_TYPE_REGS_IRET 2
|
||||
#define UNWIND_HINT_TYPE_SAVE 3
|
||||
#define UNWIND_HINT_TYPE_RESTORE 4
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
/*
|
||||
* This struct is more or less a vastly simplified version of the DWARF Call
|
||||
* Frame Information standard. It contains only the necessary parts of DWARF
|
||||
* CFI, simplified for ease of access by the in-kernel unwinder. It tells the
|
||||
* unwinder how to find the previous SP and BP (and sometimes entry regs) on
|
||||
* the stack for a given code address. Each instance of the struct corresponds
|
||||
* to one or more code locations.
|
||||
*/
|
||||
struct orc_entry {
|
||||
s16 sp_offset;
|
||||
s16 bp_offset;
|
||||
unsigned sp_reg:4;
|
||||
unsigned bp_reg:4;
|
||||
unsigned type:2;
|
||||
unsigned end:1;
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* This struct is used by asm and inline asm code to manually annotate the
|
||||
* location of registers on the stack for the ORC unwinder.
|
||||
*
|
||||
* Type can be either ORC_TYPE_* or UNWIND_HINT_TYPE_*.
|
||||
*/
|
||||
struct unwind_hint {
|
||||
u32 ip;
|
||||
s16 sp_offset;
|
||||
u8 sp_reg;
|
||||
u8 type;
|
||||
u8 end;
|
||||
};
|
||||
#endif /* __ASSEMBLY__ */
|
||||
|
||||
#endif /* _ORC_TYPES_H */
|
83
tools/arch/x86/lib/inat.c
Normal file
83
tools/arch/x86/lib/inat.c
Normal file
@@ -0,0 +1,83 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* x86 instruction attribute tables
|
||||
*
|
||||
* Written by Masami Hiramatsu <mhiramat@redhat.com>
|
||||
*/
|
||||
#include "../include/asm/insn.h"
|
||||
|
||||
/* Attribute tables are generated from opcode map */
|
||||
#include "inat-tables.c"
|
||||
|
||||
/* Attribute search APIs */
|
||||
insn_attr_t inat_get_opcode_attribute(insn_byte_t opcode)
|
||||
{
|
||||
return inat_primary_table[opcode];
|
||||
}
|
||||
|
||||
int inat_get_last_prefix_id(insn_byte_t last_pfx)
|
||||
{
|
||||
insn_attr_t lpfx_attr;
|
||||
|
||||
lpfx_attr = inat_get_opcode_attribute(last_pfx);
|
||||
return inat_last_prefix_id(lpfx_attr);
|
||||
}
|
||||
|
||||
insn_attr_t inat_get_escape_attribute(insn_byte_t opcode, int lpfx_id,
|
||||
insn_attr_t esc_attr)
|
||||
{
|
||||
const insn_attr_t *table;
|
||||
int n;
|
||||
|
||||
n = inat_escape_id(esc_attr);
|
||||
|
||||
table = inat_escape_tables[n][0];
|
||||
if (!table)
|
||||
return 0;
|
||||
if (inat_has_variant(table[opcode]) && lpfx_id) {
|
||||
table = inat_escape_tables[n][lpfx_id];
|
||||
if (!table)
|
||||
return 0;
|
||||
}
|
||||
return table[opcode];
|
||||
}
|
||||
|
||||
insn_attr_t inat_get_group_attribute(insn_byte_t modrm, int lpfx_id,
|
||||
insn_attr_t grp_attr)
|
||||
{
|
||||
const insn_attr_t *table;
|
||||
int n;
|
||||
|
||||
n = inat_group_id(grp_attr);
|
||||
|
||||
table = inat_group_tables[n][0];
|
||||
if (!table)
|
||||
return inat_group_common_attribute(grp_attr);
|
||||
if (inat_has_variant(table[X86_MODRM_REG(modrm)]) && lpfx_id) {
|
||||
table = inat_group_tables[n][lpfx_id];
|
||||
if (!table)
|
||||
return inat_group_common_attribute(grp_attr);
|
||||
}
|
||||
return table[X86_MODRM_REG(modrm)] |
|
||||
inat_group_common_attribute(grp_attr);
|
||||
}
|
||||
|
||||
insn_attr_t inat_get_avx_attribute(insn_byte_t opcode, insn_byte_t vex_m,
|
||||
insn_byte_t vex_p)
|
||||
{
|
||||
const insn_attr_t *table;
|
||||
if (vex_m > X86_VEX_M_MAX || vex_p > INAT_LSTPFX_MAX)
|
||||
return 0;
|
||||
/* At first, this checks the master table */
|
||||
table = inat_avx_tables[vex_m][0];
|
||||
if (!table)
|
||||
return 0;
|
||||
if (!inat_is_group(table[opcode]) && vex_p) {
|
||||
/* If this is not a group, get attribute directly */
|
||||
table = inat_avx_tables[vex_m][vex_p];
|
||||
if (!table)
|
||||
return 0;
|
||||
}
|
||||
return table[opcode];
|
||||
}
|
||||
|
593
tools/arch/x86/lib/insn.c
Normal file
593
tools/arch/x86/lib/insn.c
Normal file
@@ -0,0 +1,593 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* x86 instruction analysis
|
||||
*
|
||||
* Copyright (C) IBM Corporation, 2002, 2004, 2009
|
||||
*/
|
||||
|
||||
#ifdef __KERNEL__
|
||||
#include <linux/string.h>
|
||||
#else
|
||||
#include <string.h>
|
||||
#endif
|
||||
#include "../include/asm/inat.h"
|
||||
#include "../include/asm/insn.h"
|
||||
|
||||
/* Verify next sizeof(t) bytes can be on the same instruction */
|
||||
#define validate_next(t, insn, n) \
|
||||
((insn)->next_byte + sizeof(t) + n <= (insn)->end_kaddr)
|
||||
|
||||
#define __get_next(t, insn) \
|
||||
({ t r = *(t*)insn->next_byte; insn->next_byte += sizeof(t); r; })
|
||||
|
||||
#define __peek_nbyte_next(t, insn, n) \
|
||||
({ t r = *(t*)((insn)->next_byte + n); r; })
|
||||
|
||||
#define get_next(t, insn) \
|
||||
({ if (unlikely(!validate_next(t, insn, 0))) goto err_out; __get_next(t, insn); })
|
||||
|
||||
#define peek_nbyte_next(t, insn, n) \
|
||||
({ if (unlikely(!validate_next(t, insn, n))) goto err_out; __peek_nbyte_next(t, insn, n); })
|
||||
|
||||
#define peek_next(t, insn) peek_nbyte_next(t, insn, 0)
|
||||
|
||||
/**
|
||||
* insn_init() - initialize struct insn
|
||||
* @insn: &struct insn to be initialized
|
||||
* @kaddr: address (in kernel memory) of instruction (or copy thereof)
|
||||
* @x86_64: !0 for 64-bit kernel or 64-bit app
|
||||
*/
|
||||
void insn_init(struct insn *insn, const void *kaddr, int buf_len, int x86_64)
|
||||
{
|
||||
/*
|
||||
* Instructions longer than MAX_INSN_SIZE (15 bytes) are invalid
|
||||
* even if the input buffer is long enough to hold them.
|
||||
*/
|
||||
if (buf_len > MAX_INSN_SIZE)
|
||||
buf_len = MAX_INSN_SIZE;
|
||||
|
||||
memset(insn, 0, sizeof(*insn));
|
||||
insn->kaddr = kaddr;
|
||||
insn->end_kaddr = kaddr + buf_len;
|
||||
insn->next_byte = kaddr;
|
||||
insn->x86_64 = x86_64 ? 1 : 0;
|
||||
insn->opnd_bytes = 4;
|
||||
if (x86_64)
|
||||
insn->addr_bytes = 8;
|
||||
else
|
||||
insn->addr_bytes = 4;
|
||||
}
|
||||
|
||||
/**
|
||||
* insn_get_prefixes - scan x86 instruction prefix bytes
|
||||
* @insn: &struct insn containing instruction
|
||||
*
|
||||
* Populates the @insn->prefixes bitmap, and updates @insn->next_byte
|
||||
* to point to the (first) opcode. No effect if @insn->prefixes.got
|
||||
* is already set.
|
||||
*/
|
||||
void insn_get_prefixes(struct insn *insn)
|
||||
{
|
||||
struct insn_field *prefixes = &insn->prefixes;
|
||||
insn_attr_t attr;
|
||||
insn_byte_t b, lb;
|
||||
int i, nb;
|
||||
|
||||
if (prefixes->got)
|
||||
return;
|
||||
|
||||
nb = 0;
|
||||
lb = 0;
|
||||
b = peek_next(insn_byte_t, insn);
|
||||
attr = inat_get_opcode_attribute(b);
|
||||
while (inat_is_legacy_prefix(attr)) {
|
||||
/* Skip if same prefix */
|
||||
for (i = 0; i < nb; i++)
|
||||
if (prefixes->bytes[i] == b)
|
||||
goto found;
|
||||
if (nb == 4)
|
||||
/* Invalid instruction */
|
||||
break;
|
||||
prefixes->bytes[nb++] = b;
|
||||
if (inat_is_address_size_prefix(attr)) {
|
||||
/* address size switches 2/4 or 4/8 */
|
||||
if (insn->x86_64)
|
||||
insn->addr_bytes ^= 12;
|
||||
else
|
||||
insn->addr_bytes ^= 6;
|
||||
} else if (inat_is_operand_size_prefix(attr)) {
|
||||
/* oprand size switches 2/4 */
|
||||
insn->opnd_bytes ^= 6;
|
||||
}
|
||||
found:
|
||||
prefixes->nbytes++;
|
||||
insn->next_byte++;
|
||||
lb = b;
|
||||
b = peek_next(insn_byte_t, insn);
|
||||
attr = inat_get_opcode_attribute(b);
|
||||
}
|
||||
/* Set the last prefix */
|
||||
if (lb && lb != insn->prefixes.bytes[3]) {
|
||||
if (unlikely(insn->prefixes.bytes[3])) {
|
||||
/* Swap the last prefix */
|
||||
b = insn->prefixes.bytes[3];
|
||||
for (i = 0; i < nb; i++)
|
||||
if (prefixes->bytes[i] == lb)
|
||||
prefixes->bytes[i] = b;
|
||||
}
|
||||
insn->prefixes.bytes[3] = lb;
|
||||
}
|
||||
|
||||
/* Decode REX prefix */
|
||||
if (insn->x86_64) {
|
||||
b = peek_next(insn_byte_t, insn);
|
||||
attr = inat_get_opcode_attribute(b);
|
||||
if (inat_is_rex_prefix(attr)) {
|
||||
insn->rex_prefix.value = b;
|
||||
insn->rex_prefix.nbytes = 1;
|
||||
insn->next_byte++;
|
||||
if (X86_REX_W(b))
|
||||
/* REX.W overrides opnd_size */
|
||||
insn->opnd_bytes = 8;
|
||||
}
|
||||
}
|
||||
insn->rex_prefix.got = 1;
|
||||
|
||||
/* Decode VEX prefix */
|
||||
b = peek_next(insn_byte_t, insn);
|
||||
attr = inat_get_opcode_attribute(b);
|
||||
if (inat_is_vex_prefix(attr)) {
|
||||
insn_byte_t b2 = peek_nbyte_next(insn_byte_t, insn, 1);
|
||||
if (!insn->x86_64) {
|
||||
/*
|
||||
* In 32-bits mode, if the [7:6] bits (mod bits of
|
||||
* ModRM) on the second byte are not 11b, it is
|
||||
* LDS or LES or BOUND.
|
||||
*/
|
||||
if (X86_MODRM_MOD(b2) != 3)
|
||||
goto vex_end;
|
||||
}
|
||||
insn->vex_prefix.bytes[0] = b;
|
||||
insn->vex_prefix.bytes[1] = b2;
|
||||
if (inat_is_evex_prefix(attr)) {
|
||||
b2 = peek_nbyte_next(insn_byte_t, insn, 2);
|
||||
insn->vex_prefix.bytes[2] = b2;
|
||||
b2 = peek_nbyte_next(insn_byte_t, insn, 3);
|
||||
insn->vex_prefix.bytes[3] = b2;
|
||||
insn->vex_prefix.nbytes = 4;
|
||||
insn->next_byte += 4;
|
||||
if (insn->x86_64 && X86_VEX_W(b2))
|
||||
/* VEX.W overrides opnd_size */
|
||||
insn->opnd_bytes = 8;
|
||||
} else if (inat_is_vex3_prefix(attr)) {
|
||||
b2 = peek_nbyte_next(insn_byte_t, insn, 2);
|
||||
insn->vex_prefix.bytes[2] = b2;
|
||||
insn->vex_prefix.nbytes = 3;
|
||||
insn->next_byte += 3;
|
||||
if (insn->x86_64 && X86_VEX_W(b2))
|
||||
/* VEX.W overrides opnd_size */
|
||||
insn->opnd_bytes = 8;
|
||||
} else {
|
||||
/*
|
||||
* For VEX2, fake VEX3-like byte#2.
|
||||
* Makes it easier to decode vex.W, vex.vvvv,
|
||||
* vex.L and vex.pp. Masking with 0x7f sets vex.W == 0.
|
||||
*/
|
||||
insn->vex_prefix.bytes[2] = b2 & 0x7f;
|
||||
insn->vex_prefix.nbytes = 2;
|
||||
insn->next_byte += 2;
|
||||
}
|
||||
}
|
||||
vex_end:
|
||||
insn->vex_prefix.got = 1;
|
||||
|
||||
prefixes->got = 1;
|
||||
|
||||
err_out:
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* insn_get_opcode - collect opcode(s)
|
||||
* @insn: &struct insn containing instruction
|
||||
*
|
||||
* Populates @insn->opcode, updates @insn->next_byte to point past the
|
||||
* opcode byte(s), and set @insn->attr (except for groups).
|
||||
* If necessary, first collects any preceding (prefix) bytes.
|
||||
* Sets @insn->opcode.value = opcode1. No effect if @insn->opcode.got
|
||||
* is already 1.
|
||||
*/
|
||||
void insn_get_opcode(struct insn *insn)
|
||||
{
|
||||
struct insn_field *opcode = &insn->opcode;
|
||||
insn_byte_t op;
|
||||
int pfx_id;
|
||||
if (opcode->got)
|
||||
return;
|
||||
if (!insn->prefixes.got)
|
||||
insn_get_prefixes(insn);
|
||||
|
||||
/* Get first opcode */
|
||||
op = get_next(insn_byte_t, insn);
|
||||
opcode->bytes[0] = op;
|
||||
opcode->nbytes = 1;
|
||||
|
||||
/* Check if there is VEX prefix or not */
|
||||
if (insn_is_avx(insn)) {
|
||||
insn_byte_t m, p;
|
||||
m = insn_vex_m_bits(insn);
|
||||
p = insn_vex_p_bits(insn);
|
||||
insn->attr = inat_get_avx_attribute(op, m, p);
|
||||
if ((inat_must_evex(insn->attr) && !insn_is_evex(insn)) ||
|
||||
(!inat_accept_vex(insn->attr) &&
|
||||
!inat_is_group(insn->attr)))
|
||||
insn->attr = 0; /* This instruction is bad */
|
||||
goto end; /* VEX has only 1 byte for opcode */
|
||||
}
|
||||
|
||||
insn->attr = inat_get_opcode_attribute(op);
|
||||
while (inat_is_escape(insn->attr)) {
|
||||
/* Get escaped opcode */
|
||||
op = get_next(insn_byte_t, insn);
|
||||
opcode->bytes[opcode->nbytes++] = op;
|
||||
pfx_id = insn_last_prefix_id(insn);
|
||||
insn->attr = inat_get_escape_attribute(op, pfx_id, insn->attr);
|
||||
}
|
||||
if (inat_must_vex(insn->attr))
|
||||
insn->attr = 0; /* This instruction is bad */
|
||||
end:
|
||||
opcode->got = 1;
|
||||
|
||||
err_out:
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* insn_get_modrm - collect ModRM byte, if any
|
||||
* @insn: &struct insn containing instruction
|
||||
*
|
||||
* Populates @insn->modrm and updates @insn->next_byte to point past the
|
||||
* ModRM byte, if any. If necessary, first collects the preceding bytes
|
||||
* (prefixes and opcode(s)). No effect if @insn->modrm.got is already 1.
|
||||
*/
|
||||
void insn_get_modrm(struct insn *insn)
|
||||
{
|
||||
struct insn_field *modrm = &insn->modrm;
|
||||
insn_byte_t pfx_id, mod;
|
||||
if (modrm->got)
|
||||
return;
|
||||
if (!insn->opcode.got)
|
||||
insn_get_opcode(insn);
|
||||
|
||||
if (inat_has_modrm(insn->attr)) {
|
||||
mod = get_next(insn_byte_t, insn);
|
||||
modrm->value = mod;
|
||||
modrm->nbytes = 1;
|
||||
if (inat_is_group(insn->attr)) {
|
||||
pfx_id = insn_last_prefix_id(insn);
|
||||
insn->attr = inat_get_group_attribute(mod, pfx_id,
|
||||
insn->attr);
|
||||
if (insn_is_avx(insn) && !inat_accept_vex(insn->attr))
|
||||
insn->attr = 0; /* This is bad */
|
||||
}
|
||||
}
|
||||
|
||||
if (insn->x86_64 && inat_is_force64(insn->attr))
|
||||
insn->opnd_bytes = 8;
|
||||
modrm->got = 1;
|
||||
|
||||
err_out:
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* insn_rip_relative() - Does instruction use RIP-relative addressing mode?
|
||||
* @insn: &struct insn containing instruction
|
||||
*
|
||||
* If necessary, first collects the instruction up to and including the
|
||||
* ModRM byte. No effect if @insn->x86_64 is 0.
|
||||
*/
|
||||
int insn_rip_relative(struct insn *insn)
|
||||
{
|
||||
struct insn_field *modrm = &insn->modrm;
|
||||
|
||||
if (!insn->x86_64)
|
||||
return 0;
|
||||
if (!modrm->got)
|
||||
insn_get_modrm(insn);
|
||||
/*
|
||||
* For rip-relative instructions, the mod field (top 2 bits)
|
||||
* is zero and the r/m field (bottom 3 bits) is 0x5.
|
||||
*/
|
||||
return (modrm->nbytes && (modrm->value & 0xc7) == 0x5);
|
||||
}
|
||||
|
||||
/**
|
||||
* insn_get_sib() - Get the SIB byte of instruction
|
||||
* @insn: &struct insn containing instruction
|
||||
*
|
||||
* If necessary, first collects the instruction up to and including the
|
||||
* ModRM byte.
|
||||
*/
|
||||
void insn_get_sib(struct insn *insn)
|
||||
{
|
||||
insn_byte_t modrm;
|
||||
|
||||
if (insn->sib.got)
|
||||
return;
|
||||
if (!insn->modrm.got)
|
||||
insn_get_modrm(insn);
|
||||
if (insn->modrm.nbytes) {
|
||||
modrm = (insn_byte_t)insn->modrm.value;
|
||||
if (insn->addr_bytes != 2 &&
|
||||
X86_MODRM_MOD(modrm) != 3 && X86_MODRM_RM(modrm) == 4) {
|
||||
insn->sib.value = get_next(insn_byte_t, insn);
|
||||
insn->sib.nbytes = 1;
|
||||
}
|
||||
}
|
||||
insn->sib.got = 1;
|
||||
|
||||
err_out:
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* insn_get_displacement() - Get the displacement of instruction
|
||||
* @insn: &struct insn containing instruction
|
||||
*
|
||||
* If necessary, first collects the instruction up to and including the
|
||||
* SIB byte.
|
||||
* Displacement value is sign-expanded.
|
||||
*/
|
||||
void insn_get_displacement(struct insn *insn)
|
||||
{
|
||||
insn_byte_t mod, rm, base;
|
||||
|
||||
if (insn->displacement.got)
|
||||
return;
|
||||
if (!insn->sib.got)
|
||||
insn_get_sib(insn);
|
||||
if (insn->modrm.nbytes) {
|
||||
/*
|
||||
* Interpreting the modrm byte:
|
||||
* mod = 00 - no displacement fields (exceptions below)
|
||||
* mod = 01 - 1-byte displacement field
|
||||
* mod = 10 - displacement field is 4 bytes, or 2 bytes if
|
||||
* address size = 2 (0x67 prefix in 32-bit mode)
|
||||
* mod = 11 - no memory operand
|
||||
*
|
||||
* If address size = 2...
|
||||
* mod = 00, r/m = 110 - displacement field is 2 bytes
|
||||
*
|
||||
* If address size != 2...
|
||||
* mod != 11, r/m = 100 - SIB byte exists
|
||||
* mod = 00, SIB base = 101 - displacement field is 4 bytes
|
||||
* mod = 00, r/m = 101 - rip-relative addressing, displacement
|
||||
* field is 4 bytes
|
||||
*/
|
||||
mod = X86_MODRM_MOD(insn->modrm.value);
|
||||
rm = X86_MODRM_RM(insn->modrm.value);
|
||||
base = X86_SIB_BASE(insn->sib.value);
|
||||
if (mod == 3)
|
||||
goto out;
|
||||
if (mod == 1) {
|
||||
insn->displacement.value = get_next(signed char, insn);
|
||||
insn->displacement.nbytes = 1;
|
||||
} else if (insn->addr_bytes == 2) {
|
||||
if ((mod == 0 && rm == 6) || mod == 2) {
|
||||
insn->displacement.value =
|
||||
get_next(short, insn);
|
||||
insn->displacement.nbytes = 2;
|
||||
}
|
||||
} else {
|
||||
if ((mod == 0 && rm == 5) || mod == 2 ||
|
||||
(mod == 0 && base == 5)) {
|
||||
insn->displacement.value = get_next(int, insn);
|
||||
insn->displacement.nbytes = 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
out:
|
||||
insn->displacement.got = 1;
|
||||
|
||||
err_out:
|
||||
return;
|
||||
}
|
||||
|
||||
/* Decode moffset16/32/64. Return 0 if failed */
|
||||
static int __get_moffset(struct insn *insn)
|
||||
{
|
||||
switch (insn->addr_bytes) {
|
||||
case 2:
|
||||
insn->moffset1.value = get_next(short, insn);
|
||||
insn->moffset1.nbytes = 2;
|
||||
break;
|
||||
case 4:
|
||||
insn->moffset1.value = get_next(int, insn);
|
||||
insn->moffset1.nbytes = 4;
|
||||
break;
|
||||
case 8:
|
||||
insn->moffset1.value = get_next(int, insn);
|
||||
insn->moffset1.nbytes = 4;
|
||||
insn->moffset2.value = get_next(int, insn);
|
||||
insn->moffset2.nbytes = 4;
|
||||
break;
|
||||
default: /* opnd_bytes must be modified manually */
|
||||
goto err_out;
|
||||
}
|
||||
insn->moffset1.got = insn->moffset2.got = 1;
|
||||
|
||||
return 1;
|
||||
|
||||
err_out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Decode imm v32(Iz). Return 0 if failed */
|
||||
static int __get_immv32(struct insn *insn)
|
||||
{
|
||||
switch (insn->opnd_bytes) {
|
||||
case 2:
|
||||
insn->immediate.value = get_next(short, insn);
|
||||
insn->immediate.nbytes = 2;
|
||||
break;
|
||||
case 4:
|
||||
case 8:
|
||||
insn->immediate.value = get_next(int, insn);
|
||||
insn->immediate.nbytes = 4;
|
||||
break;
|
||||
default: /* opnd_bytes must be modified manually */
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
err_out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Decode imm v64(Iv/Ov), Return 0 if failed */
|
||||
static int __get_immv(struct insn *insn)
|
||||
{
|
||||
switch (insn->opnd_bytes) {
|
||||
case 2:
|
||||
insn->immediate1.value = get_next(short, insn);
|
||||
insn->immediate1.nbytes = 2;
|
||||
break;
|
||||
case 4:
|
||||
insn->immediate1.value = get_next(int, insn);
|
||||
insn->immediate1.nbytes = 4;
|
||||
break;
|
||||
case 8:
|
||||
insn->immediate1.value = get_next(int, insn);
|
||||
insn->immediate1.nbytes = 4;
|
||||
insn->immediate2.value = get_next(int, insn);
|
||||
insn->immediate2.nbytes = 4;
|
||||
break;
|
||||
default: /* opnd_bytes must be modified manually */
|
||||
goto err_out;
|
||||
}
|
||||
insn->immediate1.got = insn->immediate2.got = 1;
|
||||
|
||||
return 1;
|
||||
err_out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Decode ptr16:16/32(Ap) */
|
||||
static int __get_immptr(struct insn *insn)
|
||||
{
|
||||
switch (insn->opnd_bytes) {
|
||||
case 2:
|
||||
insn->immediate1.value = get_next(short, insn);
|
||||
insn->immediate1.nbytes = 2;
|
||||
break;
|
||||
case 4:
|
||||
insn->immediate1.value = get_next(int, insn);
|
||||
insn->immediate1.nbytes = 4;
|
||||
break;
|
||||
case 8:
|
||||
/* ptr16:64 is not exist (no segment) */
|
||||
return 0;
|
||||
default: /* opnd_bytes must be modified manually */
|
||||
goto err_out;
|
||||
}
|
||||
insn->immediate2.value = get_next(unsigned short, insn);
|
||||
insn->immediate2.nbytes = 2;
|
||||
insn->immediate1.got = insn->immediate2.got = 1;
|
||||
|
||||
return 1;
|
||||
err_out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* insn_get_immediate() - Get the immediates of instruction
|
||||
* @insn: &struct insn containing instruction
|
||||
*
|
||||
* If necessary, first collects the instruction up to and including the
|
||||
* displacement bytes.
|
||||
* Basically, most of immediates are sign-expanded. Unsigned-value can be
|
||||
* get by bit masking with ((1 << (nbytes * 8)) - 1)
|
||||
*/
|
||||
void insn_get_immediate(struct insn *insn)
|
||||
{
|
||||
if (insn->immediate.got)
|
||||
return;
|
||||
if (!insn->displacement.got)
|
||||
insn_get_displacement(insn);
|
||||
|
||||
if (inat_has_moffset(insn->attr)) {
|
||||
if (!__get_moffset(insn))
|
||||
goto err_out;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!inat_has_immediate(insn->attr))
|
||||
/* no immediates */
|
||||
goto done;
|
||||
|
||||
switch (inat_immediate_size(insn->attr)) {
|
||||
case INAT_IMM_BYTE:
|
||||
insn->immediate.value = get_next(signed char, insn);
|
||||
insn->immediate.nbytes = 1;
|
||||
break;
|
||||
case INAT_IMM_WORD:
|
||||
insn->immediate.value = get_next(short, insn);
|
||||
insn->immediate.nbytes = 2;
|
||||
break;
|
||||
case INAT_IMM_DWORD:
|
||||
insn->immediate.value = get_next(int, insn);
|
||||
insn->immediate.nbytes = 4;
|
||||
break;
|
||||
case INAT_IMM_QWORD:
|
||||
insn->immediate1.value = get_next(int, insn);
|
||||
insn->immediate1.nbytes = 4;
|
||||
insn->immediate2.value = get_next(int, insn);
|
||||
insn->immediate2.nbytes = 4;
|
||||
break;
|
||||
case INAT_IMM_PTR:
|
||||
if (!__get_immptr(insn))
|
||||
goto err_out;
|
||||
break;
|
||||
case INAT_IMM_VWORD32:
|
||||
if (!__get_immv32(insn))
|
||||
goto err_out;
|
||||
break;
|
||||
case INAT_IMM_VWORD:
|
||||
if (!__get_immv(insn))
|
||||
goto err_out;
|
||||
break;
|
||||
default:
|
||||
/* Here, insn must have an immediate, but failed */
|
||||
goto err_out;
|
||||
}
|
||||
if (inat_has_second_immediate(insn->attr)) {
|
||||
insn->immediate2.value = get_next(signed char, insn);
|
||||
insn->immediate2.nbytes = 1;
|
||||
}
|
||||
done:
|
||||
insn->immediate.got = 1;
|
||||
|
||||
err_out:
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* insn_get_length() - Get the length of instruction
|
||||
* @insn: &struct insn containing instruction
|
||||
*
|
||||
* If necessary, first collects the instruction up to and including the
|
||||
* immediates bytes.
|
||||
*/
|
||||
void insn_get_length(struct insn *insn)
|
||||
{
|
||||
if (insn->length)
|
||||
return;
|
||||
if (!insn->immediate.got)
|
||||
insn_get_immediate(insn);
|
||||
insn->length = (unsigned char)((unsigned long)insn->next_byte
|
||||
- (unsigned long)insn->kaddr);
|
||||
}
|
1072
tools/arch/x86/lib/x86-opcode-map.txt
Normal file
1072
tools/arch/x86/lib/x86-opcode-map.txt
Normal file
File diff suppressed because it is too large
Load Diff
393
tools/arch/x86/tools/gen-insn-attr-x86.awk
Normal file
393
tools/arch/x86/tools/gen-insn-attr-x86.awk
Normal file
@@ -0,0 +1,393 @@
|
||||
#!/bin/awk -f
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
# gen-insn-attr-x86.awk: Instruction attribute table generator
|
||||
# Written by Masami Hiramatsu <mhiramat@redhat.com>
|
||||
#
|
||||
# Usage: awk -f gen-insn-attr-x86.awk x86-opcode-map.txt > inat-tables.c
|
||||
|
||||
# Awk implementation sanity check
|
||||
function check_awk_implement() {
|
||||
if (sprintf("%x", 0) != "0")
|
||||
return "Your awk has a printf-format problem."
|
||||
return ""
|
||||
}
|
||||
|
||||
# Clear working vars
|
||||
function clear_vars() {
|
||||
delete table
|
||||
delete lptable2
|
||||
delete lptable1
|
||||
delete lptable3
|
||||
eid = -1 # escape id
|
||||
gid = -1 # group id
|
||||
aid = -1 # AVX id
|
||||
tname = ""
|
||||
}
|
||||
|
||||
BEGIN {
|
||||
# Implementation error checking
|
||||
awkchecked = check_awk_implement()
|
||||
if (awkchecked != "") {
|
||||
print "Error: " awkchecked > "/dev/stderr"
|
||||
print "Please try to use gawk." > "/dev/stderr"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Setup generating tables
|
||||
print "/* x86 opcode map generated from x86-opcode-map.txt */"
|
||||
print "/* Do not change this code. */\n"
|
||||
ggid = 1
|
||||
geid = 1
|
||||
gaid = 0
|
||||
delete etable
|
||||
delete gtable
|
||||
delete atable
|
||||
|
||||
opnd_expr = "^[A-Za-z/]"
|
||||
ext_expr = "^\\("
|
||||
sep_expr = "^\\|$"
|
||||
group_expr = "^Grp[0-9A-Za-z]+"
|
||||
|
||||
imm_expr = "^[IJAOL][a-z]"
|
||||
imm_flag["Ib"] = "INAT_MAKE_IMM(INAT_IMM_BYTE)"
|
||||
imm_flag["Jb"] = "INAT_MAKE_IMM(INAT_IMM_BYTE)"
|
||||
imm_flag["Iw"] = "INAT_MAKE_IMM(INAT_IMM_WORD)"
|
||||
imm_flag["Id"] = "INAT_MAKE_IMM(INAT_IMM_DWORD)"
|
||||
imm_flag["Iq"] = "INAT_MAKE_IMM(INAT_IMM_QWORD)"
|
||||
imm_flag["Ap"] = "INAT_MAKE_IMM(INAT_IMM_PTR)"
|
||||
imm_flag["Iz"] = "INAT_MAKE_IMM(INAT_IMM_VWORD32)"
|
||||
imm_flag["Jz"] = "INAT_MAKE_IMM(INAT_IMM_VWORD32)"
|
||||
imm_flag["Iv"] = "INAT_MAKE_IMM(INAT_IMM_VWORD)"
|
||||
imm_flag["Ob"] = "INAT_MOFFSET"
|
||||
imm_flag["Ov"] = "INAT_MOFFSET"
|
||||
imm_flag["Lx"] = "INAT_MAKE_IMM(INAT_IMM_BYTE)"
|
||||
|
||||
modrm_expr = "^([CDEGMNPQRSUVW/][a-z]+|NTA|T[012])"
|
||||
force64_expr = "\\([df]64\\)"
|
||||
rex_expr = "^REX(\\.[XRWB]+)*"
|
||||
fpu_expr = "^ESC" # TODO
|
||||
|
||||
lprefix1_expr = "\\((66|!F3)\\)"
|
||||
lprefix2_expr = "\\(F3\\)"
|
||||
lprefix3_expr = "\\((F2|!F3|66\\&F2)\\)"
|
||||
lprefix_expr = "\\((66|F2|F3)\\)"
|
||||
max_lprefix = 4
|
||||
|
||||
# All opcodes starting with lower-case 'v', 'k' or with (v1) superscript
|
||||
# accepts VEX prefix
|
||||
vexok_opcode_expr = "^[vk].*"
|
||||
vexok_expr = "\\(v1\\)"
|
||||
# All opcodes with (v) superscript supports *only* VEX prefix
|
||||
vexonly_expr = "\\(v\\)"
|
||||
# All opcodes with (ev) superscript supports *only* EVEX prefix
|
||||
evexonly_expr = "\\(ev\\)"
|
||||
|
||||
prefix_expr = "\\(Prefix\\)"
|
||||
prefix_num["Operand-Size"] = "INAT_PFX_OPNDSZ"
|
||||
prefix_num["REPNE"] = "INAT_PFX_REPNE"
|
||||
prefix_num["REP/REPE"] = "INAT_PFX_REPE"
|
||||
prefix_num["XACQUIRE"] = "INAT_PFX_REPNE"
|
||||
prefix_num["XRELEASE"] = "INAT_PFX_REPE"
|
||||
prefix_num["LOCK"] = "INAT_PFX_LOCK"
|
||||
prefix_num["SEG=CS"] = "INAT_PFX_CS"
|
||||
prefix_num["SEG=DS"] = "INAT_PFX_DS"
|
||||
prefix_num["SEG=ES"] = "INAT_PFX_ES"
|
||||
prefix_num["SEG=FS"] = "INAT_PFX_FS"
|
||||
prefix_num["SEG=GS"] = "INAT_PFX_GS"
|
||||
prefix_num["SEG=SS"] = "INAT_PFX_SS"
|
||||
prefix_num["Address-Size"] = "INAT_PFX_ADDRSZ"
|
||||
prefix_num["VEX+1byte"] = "INAT_PFX_VEX2"
|
||||
prefix_num["VEX+2byte"] = "INAT_PFX_VEX3"
|
||||
prefix_num["EVEX"] = "INAT_PFX_EVEX"
|
||||
|
||||
clear_vars()
|
||||
}
|
||||
|
||||
function semantic_error(msg) {
|
||||
print "Semantic error at " NR ": " msg > "/dev/stderr"
|
||||
exit 1
|
||||
}
|
||||
|
||||
function debug(msg) {
|
||||
print "DEBUG: " msg
|
||||
}
|
||||
|
||||
function array_size(arr, i,c) {
|
||||
c = 0
|
||||
for (i in arr)
|
||||
c++
|
||||
return c
|
||||
}
|
||||
|
||||
/^Table:/ {
|
||||
print "/* " $0 " */"
|
||||
if (tname != "")
|
||||
semantic_error("Hit Table: before EndTable:.");
|
||||
}
|
||||
|
||||
/^Referrer:/ {
|
||||
if (NF != 1) {
|
||||
# escape opcode table
|
||||
ref = ""
|
||||
for (i = 2; i <= NF; i++)
|
||||
ref = ref $i
|
||||
eid = escape[ref]
|
||||
tname = sprintf("inat_escape_table_%d", eid)
|
||||
}
|
||||
}
|
||||
|
||||
/^AVXcode:/ {
|
||||
if (NF != 1) {
|
||||
# AVX/escape opcode table
|
||||
aid = $2
|
||||
if (gaid <= aid)
|
||||
gaid = aid + 1
|
||||
if (tname == "") # AVX only opcode table
|
||||
tname = sprintf("inat_avx_table_%d", $2)
|
||||
}
|
||||
if (aid == -1 && eid == -1) # primary opcode table
|
||||
tname = "inat_primary_table"
|
||||
}
|
||||
|
||||
/^GrpTable:/ {
|
||||
print "/* " $0 " */"
|
||||
if (!($2 in group))
|
||||
semantic_error("No group: " $2 )
|
||||
gid = group[$2]
|
||||
tname = "inat_group_table_" gid
|
||||
}
|
||||
|
||||
function print_table(tbl,name,fmt,n)
|
||||
{
|
||||
print "const insn_attr_t " name " = {"
|
||||
for (i = 0; i < n; i++) {
|
||||
id = sprintf(fmt, i)
|
||||
if (tbl[id])
|
||||
print " [" id "] = " tbl[id] ","
|
||||
}
|
||||
print "};"
|
||||
}
|
||||
|
||||
/^EndTable/ {
|
||||
if (gid != -1) {
|
||||
# print group tables
|
||||
if (array_size(table) != 0) {
|
||||
print_table(table, tname "[INAT_GROUP_TABLE_SIZE]",
|
||||
"0x%x", 8)
|
||||
gtable[gid,0] = tname
|
||||
}
|
||||
if (array_size(lptable1) != 0) {
|
||||
print_table(lptable1, tname "_1[INAT_GROUP_TABLE_SIZE]",
|
||||
"0x%x", 8)
|
||||
gtable[gid,1] = tname "_1"
|
||||
}
|
||||
if (array_size(lptable2) != 0) {
|
||||
print_table(lptable2, tname "_2[INAT_GROUP_TABLE_SIZE]",
|
||||
"0x%x", 8)
|
||||
gtable[gid,2] = tname "_2"
|
||||
}
|
||||
if (array_size(lptable3) != 0) {
|
||||
print_table(lptable3, tname "_3[INAT_GROUP_TABLE_SIZE]",
|
||||
"0x%x", 8)
|
||||
gtable[gid,3] = tname "_3"
|
||||
}
|
||||
} else {
|
||||
# print primary/escaped tables
|
||||
if (array_size(table) != 0) {
|
||||
print_table(table, tname "[INAT_OPCODE_TABLE_SIZE]",
|
||||
"0x%02x", 256)
|
||||
etable[eid,0] = tname
|
||||
if (aid >= 0)
|
||||
atable[aid,0] = tname
|
||||
}
|
||||
if (array_size(lptable1) != 0) {
|
||||
print_table(lptable1,tname "_1[INAT_OPCODE_TABLE_SIZE]",
|
||||
"0x%02x", 256)
|
||||
etable[eid,1] = tname "_1"
|
||||
if (aid >= 0)
|
||||
atable[aid,1] = tname "_1"
|
||||
}
|
||||
if (array_size(lptable2) != 0) {
|
||||
print_table(lptable2,tname "_2[INAT_OPCODE_TABLE_SIZE]",
|
||||
"0x%02x", 256)
|
||||
etable[eid,2] = tname "_2"
|
||||
if (aid >= 0)
|
||||
atable[aid,2] = tname "_2"
|
||||
}
|
||||
if (array_size(lptable3) != 0) {
|
||||
print_table(lptable3,tname "_3[INAT_OPCODE_TABLE_SIZE]",
|
||||
"0x%02x", 256)
|
||||
etable[eid,3] = tname "_3"
|
||||
if (aid >= 0)
|
||||
atable[aid,3] = tname "_3"
|
||||
}
|
||||
}
|
||||
print ""
|
||||
clear_vars()
|
||||
}
|
||||
|
||||
function add_flags(old,new) {
|
||||
if (old && new)
|
||||
return old " | " new
|
||||
else if (old)
|
||||
return old
|
||||
else
|
||||
return new
|
||||
}
|
||||
|
||||
# convert operands to flags.
|
||||
function convert_operands(count,opnd, i,j,imm,mod)
|
||||
{
|
||||
imm = null
|
||||
mod = null
|
||||
for (j = 1; j <= count; j++) {
|
||||
i = opnd[j]
|
||||
if (match(i, imm_expr) == 1) {
|
||||
if (!imm_flag[i])
|
||||
semantic_error("Unknown imm opnd: " i)
|
||||
if (imm) {
|
||||
if (i != "Ib")
|
||||
semantic_error("Second IMM error")
|
||||
imm = add_flags(imm, "INAT_SCNDIMM")
|
||||
} else
|
||||
imm = imm_flag[i]
|
||||
} else if (match(i, modrm_expr))
|
||||
mod = "INAT_MODRM"
|
||||
}
|
||||
return add_flags(imm, mod)
|
||||
}
|
||||
|
||||
/^[0-9a-f]+\:/ {
|
||||
if (NR == 1)
|
||||
next
|
||||
# get index
|
||||
idx = "0x" substr($1, 1, index($1,":") - 1)
|
||||
if (idx in table)
|
||||
semantic_error("Redefine " idx " in " tname)
|
||||
|
||||
# check if escaped opcode
|
||||
if ("escape" == $2) {
|
||||
if ($3 != "#")
|
||||
semantic_error("No escaped name")
|
||||
ref = ""
|
||||
for (i = 4; i <= NF; i++)
|
||||
ref = ref $i
|
||||
if (ref in escape)
|
||||
semantic_error("Redefine escape (" ref ")")
|
||||
escape[ref] = geid
|
||||
geid++
|
||||
table[idx] = "INAT_MAKE_ESCAPE(" escape[ref] ")"
|
||||
next
|
||||
}
|
||||
|
||||
variant = null
|
||||
# converts
|
||||
i = 2
|
||||
while (i <= NF) {
|
||||
opcode = $(i++)
|
||||
delete opnds
|
||||
ext = null
|
||||
flags = null
|
||||
opnd = null
|
||||
# parse one opcode
|
||||
if (match($i, opnd_expr)) {
|
||||
opnd = $i
|
||||
count = split($(i++), opnds, ",")
|
||||
flags = convert_operands(count, opnds)
|
||||
}
|
||||
if (match($i, ext_expr))
|
||||
ext = $(i++)
|
||||
if (match($i, sep_expr))
|
||||
i++
|
||||
else if (i < NF)
|
||||
semantic_error($i " is not a separator")
|
||||
|
||||
# check if group opcode
|
||||
if (match(opcode, group_expr)) {
|
||||
if (!(opcode in group)) {
|
||||
group[opcode] = ggid
|
||||
ggid++
|
||||
}
|
||||
flags = add_flags(flags, "INAT_MAKE_GROUP(" group[opcode] ")")
|
||||
}
|
||||
# check force(or default) 64bit
|
||||
if (match(ext, force64_expr))
|
||||
flags = add_flags(flags, "INAT_FORCE64")
|
||||
|
||||
# check REX prefix
|
||||
if (match(opcode, rex_expr))
|
||||
flags = add_flags(flags, "INAT_MAKE_PREFIX(INAT_PFX_REX)")
|
||||
|
||||
# check coprocessor escape : TODO
|
||||
if (match(opcode, fpu_expr))
|
||||
flags = add_flags(flags, "INAT_MODRM")
|
||||
|
||||
# check VEX codes
|
||||
if (match(ext, evexonly_expr))
|
||||
flags = add_flags(flags, "INAT_VEXOK | INAT_EVEXONLY")
|
||||
else if (match(ext, vexonly_expr))
|
||||
flags = add_flags(flags, "INAT_VEXOK | INAT_VEXONLY")
|
||||
else if (match(ext, vexok_expr) || match(opcode, vexok_opcode_expr))
|
||||
flags = add_flags(flags, "INAT_VEXOK")
|
||||
|
||||
# check prefixes
|
||||
if (match(ext, prefix_expr)) {
|
||||
if (!prefix_num[opcode])
|
||||
semantic_error("Unknown prefix: " opcode)
|
||||
flags = add_flags(flags, "INAT_MAKE_PREFIX(" prefix_num[opcode] ")")
|
||||
}
|
||||
if (length(flags) == 0)
|
||||
continue
|
||||
# check if last prefix
|
||||
if (match(ext, lprefix1_expr)) {
|
||||
lptable1[idx] = add_flags(lptable1[idx],flags)
|
||||
variant = "INAT_VARIANT"
|
||||
}
|
||||
if (match(ext, lprefix2_expr)) {
|
||||
lptable2[idx] = add_flags(lptable2[idx],flags)
|
||||
variant = "INAT_VARIANT"
|
||||
}
|
||||
if (match(ext, lprefix3_expr)) {
|
||||
lptable3[idx] = add_flags(lptable3[idx],flags)
|
||||
variant = "INAT_VARIANT"
|
||||
}
|
||||
if (!match(ext, lprefix_expr)){
|
||||
table[idx] = add_flags(table[idx],flags)
|
||||
}
|
||||
}
|
||||
if (variant)
|
||||
table[idx] = add_flags(table[idx],variant)
|
||||
}
|
||||
|
||||
END {
|
||||
if (awkchecked != "")
|
||||
exit 1
|
||||
# print escape opcode map's array
|
||||
print "/* Escape opcode map array */"
|
||||
print "const insn_attr_t * const inat_escape_tables[INAT_ESC_MAX + 1]" \
|
||||
"[INAT_LSTPFX_MAX + 1] = {"
|
||||
for (i = 0; i < geid; i++)
|
||||
for (j = 0; j < max_lprefix; j++)
|
||||
if (etable[i,j])
|
||||
print " ["i"]["j"] = "etable[i,j]","
|
||||
print "};\n"
|
||||
# print group opcode map's array
|
||||
print "/* Group opcode map array */"
|
||||
print "const insn_attr_t * const inat_group_tables[INAT_GRP_MAX + 1]"\
|
||||
"[INAT_LSTPFX_MAX + 1] = {"
|
||||
for (i = 0; i < ggid; i++)
|
||||
for (j = 0; j < max_lprefix; j++)
|
||||
if (gtable[i,j])
|
||||
print " ["i"]["j"] = "gtable[i,j]","
|
||||
print "};\n"
|
||||
# print AVX opcode map's array
|
||||
print "/* AVX opcode map array */"
|
||||
print "const insn_attr_t * const inat_avx_tables[X86_VEX_M_MAX + 1]"\
|
||||
"[INAT_LSTPFX_MAX + 1] = {"
|
||||
for (i = 0; i < gaid; i++)
|
||||
for (j = 0; j < max_lprefix; j++)
|
||||
if (atable[i,j])
|
||||
print " ["i"]["j"] = "atable[i,j]","
|
||||
print "};"
|
||||
}
|
||||
|
Reference in New Issue
Block a user