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

Reply via email to