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, Masami Hiramatsu wrote: > Hi Jim, > > Jim Keniston wrote: >> On Fri, 2009-02-27 at 16:20 -0500, Masami Hiramatsu wrote: >> ... >>> Here are a patch against your code and an example code for >>> instruction length decoder. >>> Curiously, KVM's instruction decoder does not completely >>> cover all instructions(especially, Jcc/test...). >>> I had to refer Intel manuals. >>> >>> Moreover, even with this patch, the decoder is incomplete. >>> - this doesn't cover 3bytes opcode yet. >>> - this doesn't decode sib, displacement and immediate. >>> - might have some bugs :-( >>> >>> >>> Thank you, >> Thanks for your work on this. Comments below. > > Thank you very much for review! > > Actually, that code was based on KVM code, so I also found many > opcodes were not supported. > >> As I mentioned in private email, you or I should probably refactor this >> into: >> - insn_get_sib() >> - insn_get_displacement() >> - insn_get_immediate() >> - insn_get_length() > > Agreed, these should be supported. > > I also would like to change struct insn as below; > > struct insn { > struct insn_field prefixes; /* prefixes.value is a bitmap */ > struct insn_field opcode; /* opcode.bytes[n] == opcode_n */ > 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; > }; > > opcode2 and opcode3 will be stored in opcode.value with opcode1. > > Now, I'm updating my code. Would anyone also be working on it? > > Thank you, > >> Jim >> >>> plain text document attachment (insn_x86.patch) >>> Index: insn_x86.h >>> =================================================================== >>> --- insn_x86.h (revision 1510) >>> +++ insn_x86.h (working copy) >>> @@ -66,6 +66,10 @@ >>> struct insn_field displacement; >>> struct insn_field immediate; >>> >>> + u8 op_bytes; >> I'd probably use opnd_bytes and addr_bytes here, for clarity. (When I >> first saw "op", I thought "opcode".) Also, we should clarify that these >> are the EFFECTIVE lengths, not the lengths of the immediate and >> displacement fields in the instruction. >> >>> + u8 ad_bytes; >>> + u8 length; >>> + >>> const u8 *kaddr; /* kernel address of insn (copy) to analyze */ >>> const u8 *next_byte; >>> bool x86_64; >>> @@ -75,6 +79,7 @@ >>> 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_length(struct insn *insn); >>> >>> #ifdef CONFIG_X86_64 >>> extern bool insn_rip_relative(struct insn *insn); >>> Index: insn_x86.c >>> =================================================================== >>> --- insn_x86.c (revision 1510) >>> +++ insn_x86.c (working copy) >>> @@ -17,7 +17,7 @@ >>> * >>> * Copyright (C) IBM Corporation, 2002, 2004, 2009 >>> */ >>> - >>> +#include <linux/module.h> >>> #include <linux/string.h> >>> // #include <asm/insn.h> >>> #include "insn_x86.h" >>> @@ -34,6 +34,11 @@ >>> insn->kaddr = kaddr; >>> insn->next_byte = kaddr; >>> insn->x86_64 = x86_64; >>> + insn->op_bytes = 4; >>> + if (x86_64) >>> + insn->ad_bytes = 8; >>> + else >>> + insn->ad_bytes = 4; >>> } >>> EXPORT_SYMBOL_GPL(insn_init); >>> >>> @@ -79,10 +84,51 @@ >>> break; >>> prefixes->value |= pfx; >>> } >>> + if (prefixes->value & X86_PFX_OPNDSZ) { >>> + /* oprand size switches 2/4 */ >>> + insn->op_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->op_bytes ^= 12; >>> + else >>> +#endif >>> + insn->op_bytes ^= 6; >> This seems wrong. You're checking the address-size prefix, but >> adjusting the operand size. >> >>> + } >>> +#ifdef CONFIG_X86_64 >>> + if (prefixes->value & X86_PFX_REXW) >>> + insn->op_bytes = 8; >>> +#endif >>> prefixes->got = true; >>> } >>> EXPORT_SYMBOL_GPL(insn_get_prefixes); >>> >>> +static bool __insn_is_stack(struct insn *insn) >> It's not entirely clear to me what this function checks. (A more >> precise name might help.) You have pushes, pops, and calls here, but >> you also have some instructions that don't appear to affect the stack at >> all. And other push and pop instructions are missing. >> >>> +{ >>> + u8 reg; >>> + if (insn->opcode.nbytes == 2) >>> + return 0; >> The following are 2-byte pushes or pops: 0f-a0, 0f-a1, 0f-a8, and 0f-a9. >> >> Also, since the return value is bool, I'd prefer to see true/false >> rather than 1/0. >> >>> + >>> + switch(insn->opcode1) { >>> + case 0x68: >>> + case 0x6a: >>> + case 0x9c: >>> + case 0x9d: >>> + case 0xc5: >> 0xc5 = lds. Why lds? >> >> In general, it'd be nice to add a comment showing the mnemonic next to >> each hex value -- e.g., >> case 0x68: /* push */ >> >>> + case 0xe8: >>> + return 1; >>> + } >> Other related instructions: 9a, 1f, 07, 17, 8f. >> >>> + reg = ((*insn->next_byte) >> 3) & 7; >>> + if ((insn->opcode1 & 0xf0) == 0x50 || >>> + (insn->opcode1 == 0x1a && reg == 0) || >> The above line doesn't seem right. It catches things like >> sbb (%rax),%al . >> >>> + (insn->opcode1 == 0xff && (reg & 1) == 0 && reg != 0)) { >> Looks like the interesting reg values are 2 (call), 3 (call), and 6 >> (push). >> >>> + return 1; >>> + } >>> + return 0; >>> +} >>> + >>> /** >>> * insn_get_opcode - collect opcode(s) >>> * @insn: &struct insn containing instruction >>> @@ -108,6 +154,8 @@ >>> opcode->nbytes = 1; >>> opcode->value = insn->opcode1; >>> opcode->got = true; >>> + if (insn->x86_64 && __insn_is_stack(insn)) >>> + insn->op_bytes = 8; >>> } >>> EXPORT_SYMBOL_GPL(insn_get_opcode); >>> >>> @@ -208,3 +256,115 @@ >>> } >>> EXPORT_SYMBOL_GPL(insn_rip_relative); >>> #endif >>> + >>> +/** >>> + * >>> + * insn_get_length() - Get the length of instruction >>> + * @insn: &struct insn containing instruction >>> + * >>> + * If necessary, first collects the instruction up to and including the >>> + * ModRM byte. >>> + */ >> As I mentioned in private email, you or I should probably refactor this >> into: >> - insn_get_sib() >> - insn_get_displacement() >> - insn_get_immediate() >> - insn_get_length() >> >> BTW, I'm going to have to change my definition of insn_field to >> accommodate the 8-byte fields that can be found in instructions like >> a0-a3 (8-byte displacement) and b8-bf (8-byte immediate). >> >>> +void insn_get_length(struct insn *insn) >>> +{ >>> + u8 modrm; >>> + u8 mod = 0, reg = 0, rm = 0, sib; >>> + const u8 *next_byte; >>> + if (insn->length) >>> + return; >>> + if (!insn->modrm.got) >>> + insn_get_modrm(insn); >>> + next_byte = insn->next_byte; >> This of course assumes that no fields have been fetched beyond the modrm >> field. >> >>> + >>> + if (insn->modrm.nbytes) { >>> + modrm = insn->modrm.value; >>> + mod = (modrm & 0xc0) >> 6; >>> + reg = (modrm & 0x38) >> 3; >>> + rm = (modrm & 0x07); >> Some comments here would really help -- e.g... >> /* >> 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 field = 101 - displacement field is 4 bytes >> mod = 00, r/m = 101 - rip-relative addressing, displacement >> field is 4 bytes >> */ >> >>> + if (mod == 3) >>> + goto decode_src; >>> + if (insn->ad_bytes == 2) { >>> + if (mod == 1) >>> + next_byte++; >>> + else if (mod == 2) >>> + next_byte += 2; >>> + else if (rm == 6) >>> + next_byte += 2; >>> + } else { >>> + if (rm == 4) { >>> + sib = *(next_byte++); >>> + insn->sib.value = sib; >>> + insn->sib.nbytes = 1; >>> + insn->sib.got = 1; >>> + if ((sib & 7) == 5 && mod == 0) >>> + next_byte += 4; >>> + } >>> + if (mod == 1) >>> + next_byte++; >>> + else if (mod == 2) >>> + next_byte += 4; >>> + else if (rm == 5) >>> + next_byte += 4; >>> + } >>> + } else if (insn->opcode.nbytes == 1) >>> + if (0xa0 <= insn->opcode1 && insn->opcode1 < 0xa4) >> Add comment: >> /* Displacement = entire address - up to 8 bytes */ >> >>> + next_byte += insn->ad_bytes; >>> +decode_src: >> decode_src is a misnomer. Here we're decoding the immediate operand >> (which is always a source operand, but not the only kind). >> >>> + if (insn->opcode.nbytes == 1) { >>> + switch (insn->opcode1) { >>> + case 0x05: >>> + case 0x25: >> What about (hex) 15, 35, 01, 0d, 2d? >> >>> + case 0x3d: >>> + case 0x68: // pushl >>> + case 0x69: // imul >>> + case 0x9a: /* long call */ >> 0x9a (lcall) seems to have 6 bytes following the opcode, disassembled as >> 2 immediate operands. >> >>> + case 0xa9: // test >>> + case 0xc7: >>> + case 0xe8: >>> + case 0xe9: >>> + case 0xea: /* long jump */ >> Similarly, 0xea (ljmp) seems to have 6 bytes following the opcode, >> disassembled as 2 immediate operands. >> >>> + case 0x82: /* Group */ >> s/82/81/ here. >> >>> + goto imm_common; >>> + case 0x04: >>> + case 0x24: >> What about (hex) 14, 34, 0c, 1c, 2c? >> >>> + case 0x3c: >>> + case 0x6a: //pushb >>> + case 0x6b: //imul >>> + case 0xa8: //testb >>> + case 0xeb: >>> + case 0xc0: >>> + case 0xc1: >>> + case 0xc6: >>> + case 0x80: /* Group */ >>> + case 0x81: /* Group */ >> s/81/82/ here. >> >>> + case 0x83: /* Group */ >>> + goto immbyte_common; >>> + } >>> + if ((insn->opcode1 & 0xf8) == 0xb8 || >> I don't think this is right. b8-bf can have 8-byte immediate fields >> (with 0x48 prefix). >> >>> + (insn->opcode1 == 0xf7 && reg == 0 >> or reg == 1 >> >>> ) ) { >>> +imm_common: >> Jumping into the middle of an if block is ugly, and not necessary here. >> >>> + next_byte += (insn->op_bytes == 8) ? 4 : insn->op_bytes; >>> + } else if ((insn->opcode1 & 0xf8) == 0xb0 || // >>> + (insn->opcode1 & 0xf0) == 0x70 || // Jcc >>> + (insn->opcode1 & 0xf8) == 0xe0 || // loop/in/out >>> + (insn->opcode1 == 0xf6 && reg == 0)) { >>> +immbyte_common: >> Jumping into the middle of an if block is ugly, and not necessary here. >> >>> + next_byte++; >>> + } >> 0xc8 and 0xcd are weird cases that we should handle . >> >>> + } else { >>> + switch (insn->opcode2) { >> Add 0x70. >> >>> + case 0xa4: >>> + case 0xac: >>> + case 0xba: >>> + case 0x0f: // 3dnow >>> + case 0x3a: // ssse3 >>> + next_byte++; >>> + break; >>> + default: >>> + if ((insn->opcode2 & 0xf0) == 0x80) >>> + next_byte += (insn->op_bytes == 8) ? 4 : >>> insn->op_bytes; >>> + } >>> + } >>> + insn->length = (u8)(next_byte - insn->kaddr); >>> +} >>> +EXPORT_SYMBOL_GPL(insn_get_length); >>> > -- Masami Hiramatsu Software Engineer Hitachi Computer Products (America) Inc. Software Solutions Division e-mail: mhira...@redhat.com
Index: insn_x86.h =================================================================== --- insn_x86.h +++ insn_x86.h @@ -52,29 +52,69 @@ #define X86_PFX_REXW 0x8000 /* 0x48 bit */ struct insn_field { - s32 value; + union { + s32 value; + u8 bytes[4]; + }; bool got; /* true if we've run insn_get_xxx() for this field */ u8 nbytes; }; struct insn { - u8 opcode1, opcode2; struct insn_field prefixes; /* prefixes.value is a bitmap */ - struct insn_field opcode; /* opcode.value == opcode1 */ + 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; - struct insn_field immediate; + 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; - bool x86_64; }; +#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); Index: insn_x86.c =================================================================== --- insn_x86.c +++ insn_x86.c @@ -17,11 +17,13 @@ * * Copyright (C) IBM Corporation, 2002, 2004, 2009 */ - +#include <linux/module.h> #include <linux/string.h> // #include <asm/insn.h> #include "insn_x86.h" +MODULE_LICENSE("GPL"); // for test + /** * insn_init() - initialize struct insn * @insn: &struct insn to be initialized @@ -34,6 +36,11 @@ 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); @@ -79,6 +86,23 @@ 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); @@ -100,13 +124,16 @@ return; if (!insn->prefixes.got) insn_get_prefixes(insn); - insn->opcode1 = *insn->next_byte++; - if (insn->opcode1 == 0x0f) { - insn->opcode2 = *insn->next_byte++; - opcode->nbytes = 2; + 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->value = insn->opcode1; opcode->got = true; } EXPORT_SYMBOL_GPL(insn_get_opcode); @@ -114,22 +141,22 @@ 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) | /* 00 */ - W(0x10, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0) , /* 10 */ - W(0x20, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0) | /* 20 */ - W(0x30, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0) , /* 30 */ - W(0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) | /* 40 */ - W(0x50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) , /* 50 */ - W(0x60, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0) | /* 60 */ - W(0x70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) , /* 70 */ - W(0x80, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* 80 */ - W(0x90, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) , /* 90 */ - W(0xa0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) | /* a0 */ - W(0xb0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) , /* b0 */ - W(0xc0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0) | /* c0 */ - W(0xd0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1) , /* d0 */ - W(0xe0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) | /* e0 */ - W(0xf0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1) /* f0 */ + 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 */ }; @@ -157,6 +184,45 @@ /* 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 @@ -172,14 +238,27 @@ return; if (!insn->opcode.got) insn_get_opcode(insn); - if (insn->opcode.nbytes == 2) - modrm->nbytes = test_bit(insn->opcode2, + 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); - else - modrm->nbytes = test_bit(insn->opcode1, - (const unsigned long*) onebyte_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); @@ -208,3 +287,307 @@ } 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 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);
#include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> //#include <asm/insn_x86.h> #include "insn_x86.h" MODULE_LICENSE("GPL"); static unsigned long addr = 0; module_param(addr, ulong, 0444); static unsigned long num = 10; module_param(num, ulong, 0444); int __gen_init(void) { int i; struct insn insn; if (num > 256) num = 256; if (addr) { printk("decode instruction start from %lx\n", addr); for (i = 0; i < num; i++) { insn_init(&insn, (u8 *)addr, (sizeof(long) == 8)); insn_get_immediate(&insn); insn_get_length(&insn); printk("0x%lx: %d [op:", addr, insn.length); printk("%02x", OPCODE1(&insn)); if (insn.opcode.nbytes > 1) printk(" %02x", OPCODE2(&insn)); if (insn.opcode.nbytes > 2) printk(" %02x", OPCODE3(&insn)); printk("]"); if (insn.modrm.nbytes) printk("[modrm:%02x]", (u8)insn.modrm.value); if (insn.sib.nbytes) printk("[sib:%02x]", (u8)insn.sib.value); if (insn.displacement.nbytes) printk("[displacement:%08x]", insn.displacement.value); if (insn.immediate.nbytes) { if (insn.immediate2.nbytes) printk("[imm64:%016llx]", IMMEDIATE64(&insn)); else printk("[imm:%08x]", insn.immediate.value); } printk(" next_bytep=%p\n", insn.next_byte); addr += insn.length; } printk("decode instruction end\n"); } return 0; } void __gen_exit(void) { printk("test1 unloaded\n"); } module_init(__gen_init); module_exit(__gen_exit);