Quoting Masami Hiramatsu <mhira...@redhat.com>:
Hi Jim and Sriker,
Here, I almost rewrote my patch.
Changelog:
- rewrite decoding logic based on Intel' manual.
- supoort insn_get_sib(),insn_get_displacement()
and insn_get_immediate() too.
- support 3 bytes opcode and 64bit immediate.
- introduce some bitmaps.
Thank you,
Well, I didn't do much of a code review -- it looks like you addressed
all my concerns -- but as I mentioned on IRC, I hacked together a test
rig whereby you can disassemble a designated elf file (e.g., vmlinux,
libc, libm) and then compare insn_get_length()'s results with
objdump's results. The comment in distill.awk shows how to use
objdump, awk, and test_get_len together.
I also hacked up insn_x86.h and insn_x86.c to work in user space.
Most of that is accomplished via insn_x86_user.h, but it certainly
isn't necessary to do it that way. In particular, __u8, __s8, __u16,
etc. are versions of u8, s8, u16, etc. that can be used in both kernel
and user code, so maybe we should switch to those.
I tested with vmlinux, libc, and libm on both an i686 system and an
x86_64 system. I found and fixed a few bugs. Here are the ones that
come to mind (all fixed):
- shrd/shld, which we discussed
- missing support for weird nops with modrm bytes (0f 1f ...).
- neglected to include the REX prefix in prefixes.nbytes
- missing static decl in an inline function in insn_x86.h
There are some other cases where insn_get_length() doesn't match up
with the disassembly, but I don't consider them bugs:
- 0x9b is an instruction (fwait), but the disassembler treats it as a
prefix. For example 9b df ... can be disassembled as
fstsw ... // wait, then store status word
or
fwait // wait
fnstsw ... // store status word without waiting
Perhaps it's relevant to investigate whether a single-step of 9b df
... would execute just the fwait or the whole fstsw. Anyway, this
explains the "failures" of finit and fstsw that I mentioned to you. I
also saw this with fstcw and fclex.
- Illegal instruction sequences, such as an x86_64 instruction that
starts with 0x40, or a misplaced 0x65 prefix. Typically, we see these
when disassembling data. I just filtered out (via egrep) instructions
whose disassembly starts with "rex" or includes "(bad)".
We could address the above by filtering them out in distill.awk or
test_get_len.c. I think we're clean otherwise.
There's a little more housecleaning to do -- e.g., adding Hitachi (?)
copyright to IBM copyright, discarding insn_field_exists() and
insn_extract_reg(), putting this all in git somewhere. But not tonight.
Pull all the attached files into a directory and have a go -- e.g.,
$ make
$ objdump -d vmlinux | awk -f distill.awk | ./test_get_len [x86_64]
Jim
# Usage: objdump -d a.out | awk -f distill.awk | ./test_get_len
# Distills the disassembly as follows:
# - Removes all lines except the disassembled instructions.
# - For instructions that exceed 1 line (7 bytes), crams all the hex bytes
# into a single line.
BEGIN {
prev_addr = ""
prev_hex = ""
prev_mnemonic = ""
}
/^ *[0-9a-f]+:/ {
if (split($0, field, "\t") < 3) {
# This is a continuation of the same insn.
prev_hex = prev_hex field[2]
} else {
if (prev_addr != "")
printf "%s\t%s\t%s\n", prev_addr, prev_hex,
prev_mnemonic
prev_addr = field[1]
prev_hex = field[2]
prev_mnemonic = field[3]
}
}
END {
if (prev_addr != "")
printf "%s\t%s\t%s\n", prev_addr, prev_hex, prev_mnemonic
}
/*
* x86 instruction analysis
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* Copyright (C) IBM Corporation, 2002, 2004, 2009
*/
#ifdef KERNEL
#include <linux/module.h>
#include <linux/string.h>
#else
#include <string.h>
#endif
// #include <asm/insn.h>
#include "insn_x86.h"
MODULE_LICENSE("GPL"); // for test
/**
* insn_init() - initialize struct insn
* @insn: &struct insn to be initialized
* @kaddr: address (in kernel memory) of instruction (or copy thereof)
* @x86_64: true for 64-bit kernel or 64-bit app
*/
void insn_init(struct insn *insn, const u8 *kaddr, bool x86_64)
{
memset(insn, 0, sizeof(*insn));
insn->kaddr = kaddr;
insn->next_byte = kaddr;
insn->x86_64 = x86_64;
insn->opnd_bytes = 4;
if (x86_64)
insn->addr_bytes = 8;
else
insn->addr_bytes = 4;
}
EXPORT_SYMBOL_GPL(insn_init);
/**
* 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 true.
*/
void insn_get_prefixes(struct insn *insn)
{
u32 pfx;
struct insn_field *prefixes = &insn->prefixes;
if (prefixes->got)
return;
for (;; insn->next_byte++, prefixes->nbytes++) {
u8 b = *(insn->next_byte);
#ifdef CONFIG_X86_64
if ((b & 0xf0) == 0x40 && insn->x86_64) {
prefixes->value |= X86_PFX_REX;
prefixes->value |= (b & 0x0f) * X86_PFX_REX_BASE;
/* REX prefix is always last. */
insn->next_byte++;
prefixes->nbytes++;
break;
}
#endif
switch (b) {
case 0x26: pfx = X86_PFX_ES; break;
case 0x2E: pfx = X86_PFX_CS; break;
case 0x36: pfx = X86_PFX_SS; break;
case 0x3E: pfx = X86_PFX_DS; break;
case 0x64: pfx = X86_PFX_FS; break;
case 0x65: pfx = X86_PFX_GS; break;
case 0x66: pfx = X86_PFX_OPNDSZ; break;
case 0x67: pfx = X86_PFX_ADDRSZ; break;
case 0xF0: pfx = X86_PFX_LOCK; break;
case 0xF2: pfx = X86_PFX_REPNE; break;
case 0xF3: pfx = X86_PFX_REPE; break;
default: pfx = 0x0; break;
}
if (!pfx)
break;
prefixes->value |= pfx;
}
if (prefixes->value & X86_PFX_OPNDSZ) {
/* oprand size switches 2/4 */
insn->opnd_bytes ^= 6;
}
if (prefixes->value & X86_PFX_ADDRSZ) {
/* address size switches 2/4 or 4/8 */
#ifdef CONFIG_X86_64
if (insn->x86_64)
insn->addr_bytes ^= 12;
else
#endif
insn->addr_bytes ^= 6;
}
#ifdef CONFIG_X86_64
if (prefixes->value & X86_PFX_REXW)
insn->opnd_bytes = 8;
#endif
prefixes->got = true;
}
EXPORT_SYMBOL_GPL(insn_get_prefixes);
/**
* insn_get_opcode - collect opcode(s)
* @insn: &struct insn containing instruction
*
* Populates @insn->opcode1 (and @insn->opcode2, if it's a 2-byte opcode)
* and updates @insn->next_byte to point past the opcode byte(s).
* If necessary, first collects any preceding (prefix) bytes.
* Sets @insn->opcode.value = opcode1. No effect if @insn->opcode.got
* is already true.
*/
void insn_get_opcode(struct insn *insn)
{
struct insn_field *opcode = &insn->opcode;
if (opcode->got)
return;
if (!insn->prefixes.got)
insn_get_prefixes(insn);
OPCODE1(insn) = *insn->next_byte++;
if (OPCODE1(insn) == 0x0f) {
OPCODE2(insn) = *insn->next_byte++;
if (OPCODE2(insn) == 0x38 || OPCODE2(insn) == 0x3a) {
OPCODE3(insn) = *insn->next_byte++;
opcode->nbytes = 3;
} else
opcode->nbytes = 2;
} else
opcode->nbytes = 1;
opcode->got = true;
}
EXPORT_SYMBOL_GPL(insn_get_opcode);
const u32 onebyte_has_modrm[256 / 32] = {
/* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
/* ----------------------------------------------- */
W(0x00, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0) | /* 0f */
W(0x10, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0) , /* 1f */
W(0x20, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0) | /* 2f */
W(0x30, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0) , /* 3f */
W(0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) | /* 4f */
W(0x50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) , /* 5f */
W(0x60, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0) | /* 6f */
W(0x70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) , /* 7f */
W(0x80, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* 8f */
W(0x90, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) , /* 9f */
W(0xa0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) | /* af */
W(0xb0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) , /* bf */
W(0xc0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0) | /* cf */
W(0xd0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1) , /* df */
W(0xe0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) | /* ef */
W(0xf0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1) /* ff */
/* ----------------------------------------------- */
/* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
};
const u32 twobyte_has_modrm[256 / 32] = {
/* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
/* ----------------------------------------------- */
W(0x00, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1) | /* 0f */
W(0x10, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* 1f */
W(0x20, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1) | /* 2f */
W(0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) , /* 3f */
W(0x40, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* 4f */
W(0x50, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* 5f */
W(0x60, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* 6f */
W(0x70, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1) , /* 7f */
W(0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) | /* 8f */
W(0x90, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* 9f */
W(0xa0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1) | /* af */
W(0xb0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1) , /* bf */
W(0xc0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0) | /* cf */
W(0xd0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* df */
W(0xe0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* ef */
W(0xf0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0) /* ff */
/* ----------------------------------------------- */
/* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
};
#ifdef CONFIG_X86_64
const u32 onebyte_force_64[256 / 32] = {
/* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
/* ----------------------------------------------- */
W(0x00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) | /* 0f */
W(0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) , /* 1f */
W(0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) | /* 2f */
W(0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) , /* 3f */
W(0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) | /* 4f */
W(0x50, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* 5f */
W(0x60, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0) | /* 6f */
W(0x70, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* 7f */
W(0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1) | /* 8f */
W(0x90, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) , /* 9f */
W(0xa0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) | /* af */
W(0xb0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) , /* bf */
W(0xc0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0) | /* cf */
W(0xd0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) , /* df */
W(0xe0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0) | /* ef */
W(0xf0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) /* ff */
/* ----------------------------------------------- */
/* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
};
/* force 64 or default 64 bits operand opcodes */
static bool __operand_64(struct insn *insn)
{
u8 reg = MODRM_REG(insn);
if (insn->opcode.nbytes == 1) {
if (test_bit(OPCODE1(insn),
(const unsigned long*) onebyte_force_64) ||
(OPCODE1(insn) == 0xff &&
(reg == 2 || reg == 4 || reg == 6)))
return true;
}
return false;
}
#endif
/**
* 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 true.
*/
void insn_get_modrm(struct insn *insn)
{
struct insn_field *modrm = &insn->modrm;
if (modrm->got)
return;
if (!insn->opcode.got)
insn_get_opcode(insn);
switch (insn->opcode.nbytes) {
case 1:
modrm->nbytes = test_bit(OPCODE1(insn),
(const unsigned long*) onebyte_has_modrm);
break;
case 2:
modrm->nbytes = test_bit(OPCODE2(insn),
(const unsigned long*) twobyte_has_modrm);
break;
case 3:
/* Three bytes opcodes always have modrm */
modrm->nbytes = 1;
break;
}
if (modrm->nbytes)
modrm->value = *(insn->next_byte++);
#ifdef CONFIG_X86_64
if (insn->x86_64 && __operand_64(insn))
insn->opnd_bytes = 8;
#endif
modrm->got = true;
}
EXPORT_SYMBOL_GPL(insn_get_modrm);
#ifdef CONFIG_X86_64
/**
* 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 false.
*/
bool insn_rip_relative(struct insn *insn)
{
struct insn_field *modrm = &insn->modrm;
if (!insn->x86_64)
return false;
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 (insn_field_exists(modrm) && (modrm->value & 0xc7) == 0x5);
}
EXPORT_SYMBOL_GPL(insn_rip_relative);
#endif
/**
*
* 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)
{
if (insn->sib.got)
return;
if (!insn->modrm.got)
insn_get_modrm(insn);
if (insn->modrm.nbytes)
if (insn->addr_bytes != 2 &&
MODRM_MOD(insn) != 3 && MODRM_RM(insn) == 4) {
insn->sib.value = *(insn->next_byte++);
insn->sib.nbytes = 1;
}
insn->sib.got = true;
}
EXPORT_SYMBOL_GPL(insn_get_sib);
#define get_next(t, insn) \
({t r; r = *(t *)insn->next_byte; insn->next_byte += sizeof(t); r;})
/**
*
* 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)
{
u8 mod;
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 = MODRM_MOD(insn);
if (mod == 3)
goto out;
if (mod == 1) {
insn->displacement.value = *((s8 *)insn->next_byte++);
insn->displacement.nbytes = 1;
} else if (insn->addr_bytes == 2) {
if ((mod == 0 && MODRM_RM(insn) == 6) || mod == 2) {
insn->displacement.value = get_next(s16, insn);
insn->displacement.nbytes = 2;
}
} else {
if ((mod == 0 && MODRM_RM(insn) == 5) || mod == 2 ||
(mod == 0 && SIB_BASE(insn) == 5)) {
insn->displacement.value = get_next(s32, insn);
insn->displacement.nbytes = 4;
}
}
}
out:
insn->displacement.got = true;
}
EXPORT_SYMBOL_GPL(insn_get_displacement);
const u32 onebyte_has_immb[256 / 32] = {
/* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
/* ----------------------------------------------- */
W(0x00, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0) | /* 0f */
W(0x10, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0) , /* 1f */
W(0x20, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0) | /* 2f */
W(0x30, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0) , /* 3f */
W(0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) | /* 4f */
W(0x50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) , /* 5f */
W(0x60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0) | /* 6f */
W(0x70, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* 7f */
W(0x80, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) | /* 8f */
W(0x90, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) , /* 9f */
W(0xa0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0) | /* af */
W(0xb0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0) , /* bf */
W(0xc0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0) | /* cf */
W(0xd0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) , /* df */
W(0xe0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0) | /* ef */
W(0xf0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) /* ff */
/* ----------------------------------------------- */
/* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
};
const u32 onebyte_has_imm[256 / 32] = {
/* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
/* ----------------------------------------------- */
W(0x00, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0) | /* 0f */
W(0x10, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0) , /* 1f */
W(0x20, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0) | /* 2f */
W(0x30, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0) , /* 3f */
W(0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) | /* 4f */
W(0x50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) , /* 5f */
W(0x60, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0) | /* 6f */
W(0x70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) , /* 7f */
W(0x80, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) | /* 8f */
W(0x90, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) , /* 9f */
W(0xa0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0) | /* af */
W(0xb0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) , /* bf */
W(0xc0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0) | /* cf */
W(0xd0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) , /* df */
W(0xe0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0) | /* ef */
W(0xf0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) /* ff */
/* ----------------------------------------------- */
/* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
};
/* Decode moffset16/32/64 */
static void __get_moffset(struct insn *insn)
{
switch (insn->addr_bytes) {
case 2:
insn->moffset1.value = get_next(s16, insn);
insn->moffset1.nbytes = 2;
break;
case 4:
insn->moffset1.value = get_next(s32, insn);
insn->moffset1.nbytes = 4;
break;
case 8:
insn->moffset1.value = get_next(s32, insn);
insn->moffset1.nbytes = 4;
insn->moffset2.value = get_next(s32, insn);
insn->moffset2.nbytes = 4;
break;
}
insn->moffset1.got = insn->moffset2.got = true;
}
/* Decode imm(Iz) */
static void __get_imm(struct insn *insn)
{
switch (insn->opnd_bytes) {
case 2:
insn->immediate.value = get_next(s16, insn);
insn->immediate.nbytes = 2;
break;
case 4:
case 8:
insn->immediate.value = get_next(s32, insn);
insn->immediate.nbytes = 4;
break;
}
}
/* Decode imm64(Iv) */
static void __get_imm64(struct insn *insn)
{
switch (insn->opnd_bytes) {
case 2:
insn->immediate1.value = get_next(s16, insn);
insn->immediate1.nbytes = 2;
break;
case 4:
insn->immediate1.value = get_next(s32, insn);
insn->immediate1.nbytes = 4;
break;
case 8:
insn->immediate1.value = get_next(s32, insn);
insn->immediate1.nbytes = 4;
insn->immediate2.value = get_next(s32, insn);
insn->immediate2.nbytes = 4;
break;
}
insn->immediate1.got = insn->immediate2.got = true;
}
/* Decode ptr16:16/32(AP) */
static void __get_immptr(struct insn *insn)
{
switch (insn->opnd_bytes) {
case 2:
insn->immediate1.value = get_next(s16, insn);
insn->immediate1.nbytes = 2;
break;
case 4:
insn->immediate1.value = get_next(s32, insn);
insn->immediate1.nbytes = 4;
break;
case 8:
/* ptr16:64 is not supported (no segment) */
WARN_ON(1);
return;
}
insn->immediate2.value = get_next(u16, insn);
insn->immediate2.nbytes = 2;
insn->immediate1.got = insn->immediate2.got = true;
}
/**
*
* 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)
{
u8 opcode;
if (insn->immediate.got)
return;
if (!insn->displacement.got)
insn_get_displacement(insn);
if (insn->opcode.nbytes == 1) {
opcode = OPCODE1(insn);
if (opcode >= 0xa0 && opcode <= 0xa3) { /* direct moffset mov */
__get_moffset(insn);
} else if (test_bit(opcode,
(const unsigned long *)onebyte_has_immb) ||
(opcode == 0xf6 && MODRM_REG(insn) == 0)) {
insn->immediate.value = get_next(s8, insn);
insn->immediate.nbytes = 1;
} else if (test_bit(opcode,
(const unsigned long *)onebyte_has_imm) ||
(opcode == 0xf7 && MODRM_REG(insn) == 0)) {
__get_imm(insn);
} else if (0xb8 <= opcode && opcode <= 0xbf /* mov immv */) {
__get_imm64(insn);
} else if (opcode == 0xea /* jmp far seg:offs */) {
__get_immptr(insn);
} else if (opcode == 0xc2 /* retn immw */ ||
opcode == 0xca /* retf immw */) {
insn->immediate.value = get_next(u16, insn);
insn->immediate.nbytes = 2;
} else if (opcode == 0xc8 /* enter immw, immb */) {
insn->immediate1.value = get_next(u16, insn);
insn->immediate1.nbytes = 2;
insn->immediate2.value = get_next(u8, insn);
insn->immediate2.nbytes = 1;
}
} else if (insn->opcode.nbytes == 2) {
opcode = OPCODE2(insn);
if ((opcode & 0xf0) == 0x80 /* Jcc imm32 */) {
__get_imm(insn);
} else
switch(opcode) {
case 0x70: /* pshuf* %1, %2, immb */
case 0x71: /* Group12 %1, immb */
case 0x72: /* Group13 %1, immb */
case 0x73: /* Group14 %1, immb */
case 0xa4: /* shld %1, %2, immb */
case 0xac: /* shrd %1, %2, immb */
case 0xba: /* Group8 %1, immb */
case 0xc2: /* cmpps %1, %2, immb */
case 0xc4: /* pinsw %1, %2, immb */
case 0xc5: /* pextrw %1, %2, immb */
case 0xc6: /* shufps/d %1, %2, immb */
insn->immediate.value = get_next(u8, insn);
insn->immediate.nbytes = 1;
default:
break;
}
} else if (OPCODE3(insn) == 0x0f /* pailgnr %1, %2, immb */) {
insn->immediate.value = get_next(u8, insn);
insn->immediate.nbytes = 1;
}
insn->immediate.got = true;
}
EXPORT_SYMBOL_GPL(insn_get_immediate);
/**
*
* 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 = (u8)((unsigned long)insn->next_byte
- (unsigned long)insn->kaddr);
}
EXPORT_SYMBOL_GPL(insn_get_length);
#ifndef _ASM_X86_INSN_H
#define _ASM_X86_INSN_H
/*
* x86 instruction analysis
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* Copyright (C) IBM Corporation, 2009
*/
#ifdef KERNEL
#include <linux/types.h>
#else
#include "insn_x86_user.h"
#endif
#undef W
#define W(row, b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, ba, bb, bc, bd, be, bf)\
(((b0##UL << 0x0)|(b1##UL << 0x1)|(b2##UL << 0x2)|(b3##UL << 0x3) | \
(b4##UL << 0x4)|(b5##UL << 0x5)|(b6##UL << 0x6)|(b7##UL << 0x7) | \
(b8##UL << 0x8)|(b9##UL << 0x9)|(ba##UL << 0xa)|(bb##UL << 0xb) | \
(bc##UL << 0xc)|(bd##UL << 0xd)|(be##UL << 0xe)|(bf##UL << 0xf)) \
<< (row % 32))
/* legacy instruction prefixes */
#define X86_PFX_OPNDSZ 0x1 /* 0x66 */
#define X86_PFX_ADDRSZ 0x2 /* 0x67 */
#define X86_PFX_CS 0x4 /* 0x2E */
#define X86_PFX_DS 0x8 /* 0x3E */
#define X86_PFX_ES 0x10 /* 0x26 */
#define X86_PFX_FS 0x20 /* 0x64 */
#define X86_PFX_GS 0x40 /* 0x65 */
#define X86_PFX_SS 0x80 /* 0x36 */
#define X86_PFX_LOCK 0x100 /* 0xF0 */
#define X86_PFX_REPE 0x200 /* 0xF3 */
#define X86_PFX_REPNE 0x400 /* 0xF2 */
/* REX prefix */
#define X86_PFX_REX 0x800 /* 0x4X */
/* REX prefix dissected */
#define X86_PFX_REX_BASE 0x1000
#define X86_PFX_REXB 0x1000 /* 0x41 bit */
#define X86_PFX_REXX 0x2000 /* 0x42 bit */
#define X86_PFX_REXR 0x4000 /* 0x44 bit */
#define X86_PFX_REXW 0x8000 /* 0x48 bit */
struct insn_field {
union {
s32 value;
u8 bytes[4];
};
bool got; /* true if we've run insn_get_xxx() for this field */
u8 nbytes;
};
struct insn {
struct insn_field prefixes; /* prefixes.value is a bitmap */
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 */
};
u8 opnd_bytes;
u8 addr_bytes;
u8 length;
bool x86_64;
const u8 *kaddr; /* kernel address of insn (copy) to analyze */
const u8 *next_byte;
};
#define OPCODE1(insn) ((insn)->opcode.bytes[0])
#define OPCODE2(insn) ((insn)->opcode.bytes[1])
#define OPCODE3(insn) ((insn)->opcode.bytes[2])
#define MODRM_MOD(insn) (((insn)->modrm.value & 0xc0) >> 6)
#define MODRM_REG(insn) (((insn)->modrm.value & 0x38) >> 3)
#define MODRM_RM(insn) ((insn)->modrm.value & 0x07)
#define SIB_SCALE(insn) (((insn)->sib.value & 0xc0) >> 6)
#define SIB_INDEX(insn) (((insn)->sib.value & 0x38) >> 3)
#define SIB_BASE(insn) ((insn)->sib.value & 0x07)
#define MOFFSET64(insn) (((u64)((insn)->moffset2.value) << 32) | \
(u32)((insn)->moffset1.value))
#define IMMEDIATE64(insn) (((u64)((insn)->immediate2.value) << 32) | \
(u32)((insn)->immediate1.value))
extern void insn_init(struct insn *insn, const u8 *kaddr, bool 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);
#ifdef CONFIG_X86_64
extern bool insn_rip_relative(struct insn *insn);
#else
static bool insn_rip_relative(struct insn *insn)
{
return false;
}
#endif
static inline bool insn_field_exists(const struct insn_field *field)
{
return (field->nbytes > 0);
}
static inline u8 insn_extract_reg(int modrm)
{
return (modrm >> 3) & 0x7;
}
#endif /* _ASM_X86_INSN_H */
#ifndef __INSN_X86_USER_H
#define __INSN_X86_USER_H
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* Copyright (C) IBM Corporation, 2009
*/
#ifdef __x86_64__
#define CONFIG_X86_64
#else
#define CONFIG_X86_32
#endif
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef unsigned long long u64;
typedef signed char s8;
typedef short s16;
typedef int s32;
typedef long long s64;
typedef enum bool { false, true } bool;
/* any harmless file-scope decl */
#define NOP_DECL struct __nop
#define EXPORT_SYMBOL_GPL(symbol) NOP_DECL
#define MODULE_LICENSE(gpl) NOP_DECL
#define WARN_ON(cond) do{}while(0)
#define BITS_PER_LONG (8*sizeof(long))
/* from arch/x86/include/asm/bitops.h */
static inline int test_bit(int nr, const volatile unsigned long *addr)
{
return ((1UL << (nr % BITS_PER_LONG)) &
(((unsigned long *)addr)[nr / BITS_PER_LONG])) != 0;
}
#endif /* __INSN_X86_USER_H */
test_get_len: test_get_len.c insn_x86.c insn_x86.h insn_x86_user.h
$(CC) -g test_get_len.c insn_x86.c -o test_get_len
clean:
rm -f *.o
clobber: clean
rm -f test_get_len
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "insn_x86.h"
/*
* Test of instruction analysis in general and insn_get_length() in
* particular. See if insn_get_length() and the disassembler agree
* on the length of each instruction in an elf disassembly.
*
* usage: test_get_len [x86_64] < distilled_disassembly
*/
const char *prog;
static void usage()
{
fprintf(stderr, "usage: %s [x86_64] < distilled_disassembly\n", prog);
exit(1);
}
static void malformed_line(const char *line, int line_nr)
{
fprintf(stderr, "%s: malformed line %d:\n%s", prog, line_nr, line);
exit(3);
}
main(int argc, char **argv)
{
char line[200];
unsigned char insn_buf[16];
struct insn insn;
bool x86_64 = false;
int errors = 0, insns = 0;
#define MAX_ERRORS 10
prog = argv[0];
if (argc == 2) {
if (!strcmp(argv[1], "x86_64"))
x86_64 = true;
else
usage();
} else if (argc > 2)
usage();
while (fgets(line, 200, stdin)) {
char copy[200], *s, *tab1, *tab2;
int nb = 0;
unsigned b;
insns++;
memset(insn_buf, 0, 16);
strcpy(copy, line);
tab1 = strchr(copy, '\t');
if (!tab1)
malformed_line(line, insns);
s = tab1 + 1;
s += strspn(s, " ");
tab2 = strchr(s, '\t');
if (!tab2)
malformed_line(line, insns);
*tab2 = '\0'; // so characters beyond tab2 aren't examined
while (s < tab2) {
if (sscanf(s, "%x", &b) == 1) {
insn_buf[nb++] = (unsigned char) b;
s += 3;
} else
break;
}
insn_init(&insn, insn_buf, x86_64);
insn_get_length(&insn);
if (insn.length != nb) {
fprintf(stderr, "%s", line);
fprintf(stderr, "objdump says %d bytes, but "
"insn_get_length() says %d\n", nb, insn.length);
if (++errors > MAX_ERRORS) {
fprintf(stderr, "Stopping after %d errors "
"and %d instructions.\n",
MAX_ERRORS, insns);
exit(2);
}
}
}
exit(0);
}