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);

Reply via email to