Annotation of tme/libtme/host/x86/recode-x86.c, revision 1.1.1.1

1.1       root        1: /* $Id: recode-x86.c,v 1.5 2010/02/07 17:06:28 fredette Exp $ */
                      2: 
                      3: /* libtme/host/x86/recode-x86.c - recode code file for x86 hosts: */
                      4: 
                      5: /*
                      6:  * Copyright (c) 2007 Matt Fredette
                      7:  * All rights reserved.
                      8:  *
                      9:  * Redistribution and use in source and binary forms, with or without
                     10:  * modification, are permitted provided that the following conditions
                     11:  * are met:
                     12:  * 1. Redistributions of source code must retain the above copyright
                     13:  *    notice, this list of conditions and the following disclaimer.
                     14:  * 2. Redistributions in binary form must reproduce the above copyright
                     15:  *    notice, this list of conditions and the following disclaimer in the
                     16:  *    documentation and/or other materials provided with the distribution.
                     17:  * 3. All advertising materials mentioning features or use of this software
                     18:  *    must display the following acknowledgement:
                     19:  *      This product includes software developed by Matt Fredette.
                     20:  * 4. The name of the author may not be used to endorse or promote products
                     21:  *    derived from this software without specific prior written permission.
                     22:  *
                     23:  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
                     24:  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
                     25:  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
                     26:  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
                     27:  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
                     28:  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
                     29:  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     30:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
                     31:  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
                     32:  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
                     33:  * POSSIBILITY OF SUCH DAMAGE.
                     34:  */
                     35: 
                     36: #include <tme/common.h>
                     37: _TME_RCSID("$Id: recode-x86.c,v 1.5 2010/02/07 17:06:28 fredette Exp $");
                     38: 
                     39: #if TME_HAVE_RECODE
                     40: 
                     41: /* includes: */
                     42: #include "recode-impl.h"
                     43: 
                     44: /* the maximum number of bytes in an x86 instruction: */
                     45: #define TME_RECODE_X86_INSN_BYTES_MAX  (15)
                     46: 
                     47: /* register encodings: */
                     48: #define TME_RECODE_X86_REG_A   (0)
                     49: #define TME_RECODE_X86_REG_C   (1)
                     50: #define TME_RECODE_X86_REG_D   (2)
                     51: #define TME_RECODE_X86_REG_B   (3)
                     52: #define TME_RECODE_X86_REG_SP  (4)
                     53: #define TME_RECODE_X86_REG_BP  (5)
                     54: #define TME_RECODE_X86_REG_SI  (6)
                     55: #define TME_RECODE_X86_REG_DI  (7)
                     56: #define TME_RECODE_X86_REG_N(n)        (n)
                     57: #define TME_RECODE_X86_REG_XMM(n) (n)
                     58: #define TME_RECODE_X86_REG_UNDEF (64)
                     59: 
                     60: /* flags: */
                     61: #define TME_RECODE_X86_FLAG_Z                          (1 << 6)
                     62: 
                     63: /* REX prefixes and register number masking: */
                     64: #if TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32
                     65: #define _TME_RECODE_X86_REX(size, reg, n)      \
                     66:   ((((size) == TME_RECODE_SIZE_8               \
                     67:      && (reg) >= TME_RECODE_X86_REG_SP         \
                     68:      && (reg) <= TME_RECODE_X86_REG_DI)                \
                     69:     ? 0x40                                     \
                     70:     : 0x00)                                    \
                     71:    | ((size) > TME_RECODE_SIZE_32              \
                     72:       ? 0x48                                   \
                     73:       : 0x00)                                  \
                     74:    | ((reg) >= TME_RECODE_X86_REG_N(8)         \
                     75:       ? (0x40 | (1 << (n)))                    \
                     76:       : 0x00))
                     77: #define TME_RECODE_X86_REG(x)                          ((x) & 7)
                     78: #else  /* TME_RECODE_SIZE_HOST == TME_RECODE_SIZE_32 */
                     79: #define _TME_RECODE_X86_REX(size, reg, n)              (0x00)
                     80: #define TME_RECODE_X86_REG(x)                          (x)
                     81: #endif /* TME_RECODE_SIZE_HOST == TME_RECODE_SIZE_32 */
                     82: #define TME_RECODE_X86_REX_B(size, reg)                        _TME_RECODE_X86_REX(size, reg, 0)
                     83: #define TME_RECODE_X86_REX_X(reg)                      _TME_RECODE_X86_REX(0, reg, 1)
                     84: #define TME_RECODE_X86_REX_R(size, reg)                        _TME_RECODE_X86_REX(size, reg, 2)
                     85: #define TME_RECODE_X86_REX_W(size)                     _TME_RECODE_X86_REX(size, TME_RECODE_X86_REG_A, 3)
                     86: 
                     87: /* conditions: */
                     88: #define TME_RECODE_X86_COND_NOT                                (1 << 0)
                     89: #define TME_RECODE_X86_COND_O                          (0x0)
                     90: #define TME_RECODE_X86_COND_C                          (0x2)
                     91: #define TME_RECODE_X86_COND_Z                          (0x4)
                     92: #define TME_RECODE_X86_COND_BE                         (0x6)
                     93: #define TME_RECODE_X86_COND_S                          (0x8)
                     94: #define TME_RECODE_X86_COND_PE                         (0xa)
                     95: #define TME_RECODE_X86_COND_L                          (0xc)
                     96: #define TME_RECODE_X86_COND_LE                         (0xe)
                     97: 
                     98: /* one-byte opcode table instructions: */
                     99: #define TME_RECODE_X86_OPCODE_BINOP_ADD                        (0x00)
                    100: #define TME_RECODE_X86_OPCODE_BINOP_OR                 (0x08)
                    101: #define TME_RECODE_X86_OPCODE_BINOP_ADC                        (0x10)
                    102: #define TME_RECODE_X86_OPCODE_BINOP_SBB                        (0x18)
                    103: #define TME_RECODE_X86_OPCODE_BINOP_AND                        (0x20)
                    104: #define TME_RECODE_X86_OPCODE_BINOP_SUB                        (0x28)
                    105: #define TME_RECODE_X86_OPCODE_BINOP_XOR                        (0x30)
                    106: #define TME_RECODE_X86_OPCODE_BINOP_CMP                        (0x38)
                    107: #define TME_RECODE_X86_OPCODE_PUSH_Gv(reg)             (0x50 + TME_RECODE_X86_REG(reg))
                    108: #define TME_RECODE_X86_OPCODE_POP_Gv(reg)              (0x58 + TME_RECODE_X86_REG(reg))
                    109: #define TME_RECODE_X86_OPCODE_MOVS_El_Gv               (0x63)
                    110: #define TME_RECODE_X86_PREFIX_OPSIZ                    (0x66)
                    111: #define TME_RECODE_X86_OPCODE_PUSH_Ib                  (0x6a)
                    112: #define TME_RECODE_X86_OPCODE_JCC(cond)                        (0x70 + (cond))
                    113: #define TME_RECODE_X86_OPCODE_GRP1_Ib_Eb               (0x80)
                    114: #define TME_RECODE_X86_OPCODE_GRP1_Iz_Ev               (0x81)
                    115: #define TME_RECODE_X86_OPCODE_GRP1_Ib_Ev               (0x83)
                    116: #define TME_RECODE_X86_OPCODE_BINOP_TEST               (0x84)
                    117: #define TME_RECODE_X86_OPCODE_BINOP_XCHG               (0x86)
                    118: #define TME_RECODE_X86_OPCODE_BINOP_MOV                        (0x88)
                    119: #define  TME_RECODE_X86_OPCODE_BINOP_Gb_Eb              (0x0)
                    120: #define  TME_RECODE_X86_OPCODE_BINOP_Gv_Ev              (0x1)
                    121: #define  TME_RECODE_X86_OPCODE_BINOP_Eb_Gb              (0x2)
                    122: #define  TME_RECODE_X86_OPCODE_BINOP_Ev_Gv              (0x3)
                    123: #define  TME_RECODE_X86_OPCODE_BINOP_Iz_A               (0x5)
                    124: #define  TME_RECODE_X86_OPCODE_GRP1_BINOP(binop)        ((binop) / 0x08)
                    125: #define TME_RECODE_X86_OPCODE_LEA                      (0x8d)
                    126: #define TME_RECODE_X86_OPCODE_PUSHF                    (0x9c)
                    127: #define TME_RECODE_X86_OPCODE_POPF                     (0x9d)
                    128: #define TME_RECODE_X86_OPCODE_MOV_Iv_Gv(reg)           (0xb8 + TME_RECODE_X86_REG(reg))
                    129: #define TME_RECODE_X86_OPCODE_GRP2_Ib_Ev               (0xc1)
                    130: #define  TME_RECODE_X86_OPCODE_GRP2_ROR                         (0x1)
                    131: #define  TME_RECODE_X86_OPCODE_GRP2_SHL                         (0x4)
                    132: #define  TME_RECODE_X86_OPCODE_GRP2_SHR                         (0x5)
                    133: #define  TME_RECODE_X86_OPCODE_GRP2_SAR                         (0x7)
                    134: #define TME_RECODE_X86_OPCODE_RET                      (0xc3)
                    135: #define TME_RECODE_X86_OPCODE_MOV_Ib_Eb                        (0xc6)
                    136: #define TME_RECODE_X86_OPCODE_MOV_Iz_Ev                        (0xc7)
                    137: #define TME_RECODE_X86_OPCODE_GRP2_1_Ev                        (0xd1)
                    138: #define TME_RECODE_X86_OPCODE_CALL_RELz                        (0xe8)
                    139: #define TME_RECODE_X86_OPCODE_JMP_RELz                 (0xe9)
                    140: #define TME_RECODE_X86_OPCODE_JMP_RELb                 (0xeb)
                    141: #define TME_RECODE_X86_PREFIX_REP                      (0xf3)
                    142: #define TME_RECODE_X86_OPCODE_GRP3_Eb                  (0xf6)
                    143: #define TME_RECODE_X86_OPCODE_GRP3_Ev                  (0xf7)
                    144: #define  TME_RECODE_X86_OPCODE_GRP3_TEST                (0x0)
                    145: #define  TME_RECODE_X86_OPCODE_GRP3_NOT                         (0x2)
                    146: #define  TME_RECODE_X86_OPCODE_GRP3_NEG                         (0x3)
                    147: #define TME_RECODE_X86_OPCODE_GRP5                     (0xff)
                    148: #define  TME_RECODE_X86_OPCODE_GRP5_CALL                (0x2)
                    149: #define  TME_RECODE_X86_OPCODE_GRP5_JMP                         (0x4)
                    150: #define  TME_RECODE_X86_OPCODE_GRP5_PUSH                (0x6)
                    151: 
                    152: /* two-byte opcode table instructions: */
                    153: #define TME_RECODE_X86_OPCODE_ESC_0F                   (0x0f)
                    154: #define TME_RECODE_X86_OPCODE0F_UD2                    (0x0b)
                    155: #define TME_RECODE_X86_OPCODE0F_JCC(cond)              (0x80 + (cond))
                    156: #define TME_RECODE_X86_OPCODE0F_SETCC(cond)            (0x90 + (cond))
                    157: #define TME_RECODE_X86_OPCODE0F_SHRD_Ib_Gv_Ev          (0xac)
                    158: #define TME_RECODE_X86_OPCODE0F_GRP15                  (0xae)
                    159: #define  TME_RECODE_X86_OPCODE0F_GRP15_MFENCE          TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(0), 6)
                    160: #define TME_RECODE_X86_OPCODE0F_MOVZ_Eb_Gv             (0xb6)
                    161: #define TME_RECODE_X86_OPCODE0F_MOVZ_Ew_Gv             (0xb7)
                    162: #define TME_RECODE_X86_OPCODE0F_MOVS_Eb_Gv             (0xbe)
                    163: #define TME_RECODE_X86_OPCODE0F_MOVS_Ew_Gv             (0xbf)
                    164: #define TME_RECODE_X86_OPCODE0F_BSWAP(reg)             (0xc8 + TME_RECODE_X86_REG(reg))
                    165: 
                    166: /* OPSIZ and REP two-byte opcode table instructions: */
                    167: #define TME_RECODE_X86_OPCODE660F_MOVDQA_Wdq_Vdq       (0x6f)
                    168: #define TME_RECODE_X86_OPCODEF30F_MOVDQU_Wdq_Vdq       (0x6f)
                    169: #define TME_RECODE_X86_OPCODE660F_MOVQ_Vq_Wq           (0xd6)
                    170: #define TME_RECODE_X86_OPCODEF30F_MOVQ_Wq_Vq           (0x7e)
                    171: #define TME_RECODE_X86_OPCODE660F_MOVDQA_Vdq_Wdq       (0x7f)
                    172: #define TME_RECODE_X86_OPCODEF30F_MOVDQU_Vdq_Wdq       (0x7f)
                    173: 
                    174: /* modR/M bytes: */
                    175: #define TME_RECODE_X86_MOD_OPREG_RM(mod_rm, opreg)     ((mod_rm) + ((opreg) << 3))
                    176: #define TME_RECODE_X86_MOD_RM_EA(reg)                  ((0x0 << 6) + TME_RECODE_X86_REG(reg))
                    177: #define TME_RECODE_X86_MOD_RM_EA_DISP8(reg)            ((0x1 << 6) + TME_RECODE_X86_REG(reg))
                    178: #define TME_RECODE_X86_MOD_RM_EA_DISP32(reg)           ((0x2 << 6) + TME_RECODE_X86_REG(reg))
                    179: #define TME_RECODE_X86_MOD_RM_REG(reg)                 ((0x3 << 6) + TME_RECODE_X86_REG(reg))
                    180: #define TME_RECODE_X86_EA_BASE_SIB                     TME_RECODE_X86_REG_SP
                    181: #define TME_RECODE_X86_EA_BASE_NONE                    TME_RECODE_X86_REG_BP
                    182: #define TME_RECODE_X86_EA_BASE_IP                      TME_RECODE_X86_EA_BASE_NONE
                    183: 
                    184: /* scale-index-base bytes: */
                    185: #define TME_RECODE_X86_SIB(base, index, scale)         \
                    186:   ((((((scale) - 1) - ((scale) == 4)) & 3) << 6)       \
                    187:    | (TME_RECODE_X86_REG(index) << 3)                  \
                    188:    | TME_RECODE_X86_REG(base))
                    189: #define TME_RECODE_X86_SIB_INDEX_NONE                  TME_RECODE_X86_REG_SP
                    190: #define TME_RECODE_X86_SIB_BASE_NONE                   TME_RECODE_X86_REG_BP
                    191: 
                    192: /* multibyte NOP instructions: */
                    193: #define _TME_RECODE_X86_NOP2                           \
                    194:   ((TME_RECODE_X86_OPCODE_BINOP_MOV                    \
                    195:     + TME_RECODE_X86_OPCODE_BINOP_Gv_Ev)               \
                    196:    + (TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(TME_RECODE_X86_REG_C), \
                    197:                                  TME_RECODE_X86_REG_C) \
                    198:       << 8))
                    199: #define _TME_RECODE_X86_NOP3                           \
                    200:   (TME_RECODE_X86_OPCODE_LEA                           \
                    201:    + (TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_EA_DISP8(TME_RECODE_X86_REG_C), \
                    202:                                  TME_RECODE_X86_REG_C) \
                    203:         << 8)                                          \
                    204:    + (0x00 << 16))
                    205: #define _TME_RECODE_X86_NOP4                           \
                    206:    (TME_RECODE_X86_OPCODE_LEA                          \
                    207:     + (TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_EA_DISP8(TME_RECODE_X86_EA_BASE_SIB), \
                    208:                                   TME_RECODE_X86_REG_C)\
                    209:        << 8)                                           \
                    210:     + (TME_RECODE_X86_SIB(TME_RECODE_X86_REG_C, TME_RECODE_X86_SIB_INDEX_NONE, 1) \
                    211:        << 16)                                          \
                    212:     + (0x00 << 24))
                    213: #define TME_RECODE_X86_NOP3                            \
                    214:   (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32           \
                    215:    ? (TME_RECODE_X86_REX_R(TME_RECODE_SIZE_HOST,       \
                    216:                           TME_RECODE_X86_REG_C)        \
                    217:       + (_TME_RECODE_X86_NOP2                          \
                    218:         << 8))                                         \
                    219:    : _TME_RECODE_X86_NOP3)
                    220: #define TME_RECODE_X86_NOP4                            \
                    221:   (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32           \
                    222:    ? (TME_RECODE_X86_REX_R(TME_RECODE_SIZE_HOST,       \
                    223:                           TME_RECODE_X86_REG_C)        \
                    224:       + (_TME_RECODE_X86_NOP3                          \
                    225:         << 8))                                         \
                    226:    : _TME_RECODE_X86_NOP4)
                    227: 
                    228: /* fixed registers: */
                    229: 
                    230: /* we always use the b register to hold the struct tme_ic *: */
                    231: #define TME_RECODE_X86_REG_IC                          TME_RECODE_X86_REG_B
                    232: 
                    233: /* we use the insn generic thunk offset to hold a subs thunk
                    234:    offset: */
                    235: #define tme_recode_x86_insn_subs_thunk_off tme_recode_insn_thunk_off
                    236: 
                    237: /* this returns the bit number of the first set bit in the value.  it
                    238:    returns zero if the value is zero.  this is slow, but it's only
                    239:    meant to be used at initialization time: */
                    240: static unsigned int
                    241: _tme_recode_x86_ffs(tme_recode_uguest_t value)
                    242: {
                    243:   unsigned int shift;
                    244: 
                    245:   shift = 0;
                    246:   if (value != 0) {
                    247:     for (; (value & 1) == 0; value >>= 1, shift++);
                    248:   }
                    249:   return (shift);
                    250: }
                    251: 
                    252: /* this returns the value with any first set bit in the value shifted
                    253:    down into the first byte.  only whole bytes of zero bits are
                    254:    shifted off.  this is slow, but it's only meant to be used at
                    255:    initialization time: */
                    256: static tme_recode_uguest_t
                    257: _tme_recode_x86_ffs_byte_shift(tme_recode_uguest_t value)
                    258: {
                    259:   return (value
                    260:          >> (_tme_recode_x86_ffs(value)
                    261:              & (0 - (unsigned int) 8)));
                    262: }
                    263: 
                    264: /* this starts more instructions: */
                    265: #define tme_recode_x86_insns_start(ic, thunk_bytes)                    \
                    266:   do {                                                                 \
                    267:     thunk_bytes = (ic)->tme_recode_ic_thunk_build_next;                        \
                    268:   } while (/* CONSTCOND */ 0)
                    269: 
                    270: /* this finishes instructions: */
                    271: #define tme_recode_x86_insns_finish(ic, thunk_bytes)                   \
                    272:   do {                                                                 \
                    273:     assert (thunk_bytes <= (ic)->tme_recode_ic_thunk_build_end);       \
                    274:     (ic)->tme_recode_ic_thunk_build_next = thunk_bytes;                        \
                    275:   } while (/* CONSTCOND */ 0)
                    276: 
                    277: /* this emits an instruction to adjust the stack pointer: */
                    278: static inline tme_uint8_t *
                    279: _tme_recode_x86_emit_adjust_sp(tme_uint8_t *thunk_bytes, int adjust)
                    280: {
                    281: 
                    282:   /* emit the add $imm, %sp: */
                    283:   assert (adjust <= 127 && adjust >= -128);
                    284:   if (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) {
                    285:     thunk_bytes[0] = TME_RECODE_X86_REX_B(TME_RECODE_SIZE_HOST, TME_RECODE_X86_REG_SP);
                    286:   }
                    287:   *((tme_uint16_t *) (thunk_bytes + (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32)))
                    288:     = (TME_RECODE_X86_OPCODE_GRP1_Ib_Ev
                    289:        + (TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(TME_RECODE_X86_REG_SP),
                    290:                                      TME_RECODE_X86_OPCODE_GRP1_BINOP(TME_RECODE_X86_OPCODE_BINOP_ADD))
                    291:          << 8));
                    292:   thunk_bytes[(TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 2] = (tme_int8_t) adjust;
                    293:   thunk_bytes += (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 3;
                    294:   return (thunk_bytes);
                    295: }
                    296: 
                    297: /* this gives the raw bytes for a binop instruction from one register into another: */
                    298: #define _tme_recode_x86_raw_reg_binop(binop, reg_x86_src, reg_x86_dst)         \
                    299:   ((TME_RECODE_X86_REX_B(TME_RECODE_SIZE_HOST, reg_x86_dst)                    \
                    300:     | TME_RECODE_X86_REX_R(TME_RECODE_SIZE_HOST, reg_x86_src))                 \
                    301:    + (((binop)                                                                 \
                    302:        + TME_RECODE_X86_OPCODE_BINOP_Gv_Ev)                                    \
                    303:       << (8 * (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32)))                    \
                    304:    + (TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(reg_x86_dst),      \
                    305:                                  TME_RECODE_X86_REG(reg_x86_src))              \
                    306:       << (8 + 8 * (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32))))
                    307: 
                    308: /* this emits an instruction that copies one register into another: */
                    309: #define _tme_recode_x86_emit_reg_copy(thunk_bytes, reg_x86_src, reg_x86_dst)           \
                    310:   _tme_recode_x86_emit_reg_binop(thunk_bytes, TME_RECODE_X86_OPCODE_BINOP_MOV, reg_x86_src, reg_x86_dst)
                    311: 
                    312: /* this emits a binop instruction from one register into another: */
                    313: #define _tme_recode_x86_emit_reg_binop(thunk_bytes, binop, reg_x86_src, reg_x86_dst)   \
                    314:   do {                                                                                 \
                    315:     if (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) {                                   \
                    316:       *((tme_uint32_t *) (thunk_bytes))                                                        \
                    317:         = _tme_recode_x86_raw_reg_binop(binop, reg_x86_src, reg_x86_dst);              \
                    318:       (thunk_bytes) += 3;                                                              \
                    319:     }                                                                                  \
                    320:     else {                                                                             \
                    321:       *((tme_uint16_t *) (thunk_bytes))                                                        \
                    322:         = (tme_uint16_t) _tme_recode_x86_raw_reg_binop(binop, reg_x86_src, reg_x86_dst);\
                    323:       (thunk_bytes) += 2;                                                              \
                    324:     }                                                                                  \
                    325:   } while (/* CONSTCOND */ 0)
                    326: 
                    327: /* this emits an instruction that pushes or pops a register: */
                    328: #define __tme_recode_x86_emit_reg_push_pop(thunk_bytes, reg_x86, opcode)               \
                    329:   do {                                                                                 \
                    330:     if (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32                                      \
                    331:        && (reg_x86) >= TME_RECODE_X86_REG_N(8)) {                                      \
                    332:       *((tme_uint16_t *) (thunk_bytes))                                                        \
                    333:        = (TME_RECODE_X86_REX_B(0, TME_RECODE_X86_REG_N(8))                             \
                    334:           + ((opcode)                                                                  \
                    335:              << 8));                                                                   \
                    336:       (thunk_bytes) += 2;                                                              \
                    337:     }                                                                                  \
                    338:     else {                                                                             \
                    339:       *(thunk_bytes) = (opcode);                                                       \
                    340:       (thunk_bytes) += 1;                                                              \
                    341:     }                                                                                  \
                    342:   } while (/* CONSTCOND */ 0)
                    343: 
                    344: /* this emits an instruction that pushes a register: */
                    345: #define _tme_recode_x86_emit_reg_push(thunk_bytes, reg_x86)                            \
                    346:   __tme_recode_x86_emit_reg_push_pop(thunk_bytes, reg_x86, TME_RECODE_X86_OPCODE_PUSH_Gv(reg_x86))
                    347: 
                    348: /* this emits an instruction that pops a register: */
                    349: #define _tme_recode_x86_emit_reg_pop(thunk_bytes, reg_x86)                             \
                    350:   __tme_recode_x86_emit_reg_push_pop(thunk_bytes, reg_x86, TME_RECODE_X86_OPCODE_POP_Gv(reg_x86))
                    351: 
                    352: /* this emits a ModR/M byte with a constant displacement to reference
                    353:    a struct tme_ic *: */
                    354: static inline tme_uint8_t *
                    355: _tme_recode_x86_emit_ic_modrm(tme_uint8_t *thunk_bytes,
                    356:                              unsigned long disp,
                    357:                              tme_uint8_t opreg)
                    358: {
                    359:   unsigned int disp_size;
                    360:   tme_uint8_t mod_rm;
                    361: 
                    362:   /* on x86-64 the displacement must fit into 31 bits, because it's
                    363:      sign-extended: */
                    364:   assert (TME_RECODE_SIZE_HOST == TME_RECODE_SIZE_32
                    365:          || disp <= 0x7fffffff);
                    366: 
                    367:   /* assume that this must be a 32-bit displacement: */
                    368:   *((tme_uint32_t *) (thunk_bytes + 1)) = disp;
                    369:   disp_size = sizeof(tme_uint32_t);
                    370:   mod_rm = TME_RECODE_X86_MOD_RM_EA_DISP32(TME_RECODE_X86_REG_IC);
                    371: 
                    372:   /* if this can be an eight-bit displacement, adjust the displacement
                    373:      size and ModR/M byte: */
                    374:   if (disp < 0x80) {
                    375:     disp_size = sizeof(tme_uint8_t);
                    376:   }
                    377:   if (disp < 0x80) {
                    378:     mod_rm = TME_RECODE_X86_MOD_RM_EA_DISP8(TME_RECODE_X86_REG_IC);
                    379:   }
                    380: 
                    381:   /* write the ModR/M byte and return the end of the instruction: */
                    382:   thunk_bytes[0] = TME_RECODE_X86_MOD_OPREG_RM(mod_rm, TME_RECODE_X86_REG(opreg));
                    383:   return (thunk_bytes + 1 + disp_size);
                    384: }
                    385: 
                    386: /* this emits a call or a jmp to a C function: */
                    387: static void
                    388: _tme_recode_x86_emit_transfer_func(struct tme_recode_ic *ic,
                    389:                                   unsigned int opcode_relz,
                    390:                                   void (*func) _TME_P((void)))
                    391: {
                    392:   tme_uint8_t *thunk_bytes;
                    393:   tme_recode_thunk_off_t thunk_off;
                    394:   tme_int32_t relv;
                    395: 
                    396:   /* start more instructions: */
                    397:   tme_recode_x86_insns_start(ic, thunk_bytes);
                    398: 
                    399:   /* assume that we can emit a relative call or jmp, and get the
                    400:      relative offset to the C function: */
                    401:   thunk_bytes += 1 + sizeof(tme_int32_t);
                    402:   thunk_off = tme_recode_build_to_thunk_off(ic, thunk_bytes);
                    403:   relv = tme_recode_function_to_thunk_off(ic, func) - thunk_off;
                    404: 
                    405:   /* if this relative offset will reach the C function: */
                    406:   if (tme_recode_thunk_off_to_pointer(ic,
                    407:                                      (thunk_off + relv),
                    408:                                      void (*) _TME_P((void)))
                    409:       == func) {
                    410: 
                    411:     /* emit the relative call or jmp: */
                    412:     thunk_bytes[-(1 + sizeof(tme_int32_t))] = opcode_relz;
                    413:     ((tme_int32_t *) thunk_bytes)[-1] = relv;
                    414:   }
                    415: 
                    416:   /* otherwise, we have to do an indirect call or jmp: */
                    417:   else {
                    418: 
                    419:     /* we must be on an x86-64 host: */
                    420:     assert (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32);
                    421: 
                    422:     /* abort the relative call or jmp: */
                    423:     thunk_bytes -= 1 + sizeof(tme_int32_t);
                    424: 
                    425:     /* load the a register with the address of the C function: */
                    426:     thunk_bytes[0] = TME_RECODE_X86_REX_B(TME_RECODE_SIZE_HOST, TME_RECODE_X86_REG_A);
                    427:     thunk_bytes[1] = TME_RECODE_X86_OPCODE_MOV_Iv_Gv(TME_RECODE_X86_REG_A);
                    428:     memset(&thunk_bytes[2], 0, TME_BIT(TME_RECODE_SIZE_HOST - TME_RECODE_SIZE_8));
                    429:     memcpy(&thunk_bytes[2], &func, sizeof(func));
                    430:     thunk_bytes += 2 + TME_BIT(TME_RECODE_SIZE_HOST - TME_RECODE_SIZE_8);
                    431: 
                    432:     /* emit the indirect call or jmp: */
                    433:     thunk_bytes[0] = TME_RECODE_X86_OPCODE_GRP5;
                    434:     thunk_bytes[1]
                    435:       = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(TME_RECODE_X86_REG_A),
                    436:                                    (opcode_relz == TME_RECODE_X86_OPCODE_CALL_RELz
                    437:                                     ? TME_RECODE_X86_OPCODE_GRP5_CALL
                    438:                                     : TME_RECODE_X86_OPCODE_GRP5_JMP));
                    439:     thunk_bytes += 2;
                    440:   }
                    441: 
                    442:   /* finish these instructions: */
                    443:   tme_recode_x86_insns_finish(ic, thunk_bytes);
                    444: }
                    445: 
                    446: /* this emits a multiplication by a constant: */
                    447: /* NB: since this only does shifts and adds, this shouldn't be used
                    448:    for large factors: */
                    449: static tme_uint8_t *
                    450: _tme_recode_x86_emit_mul_constant(tme_uint8_t *thunk_bytes,
                    451:                                  unsigned int reg_size,
                    452:                                  unsigned int reg_x86_factor,
                    453:                                  tme_recode_uguest_t constant_factor,
                    454:                                  unsigned int reg_x86_scratch)
                    455: {
                    456:   unsigned int shift_count;
                    457:   unsigned int rex;
                    458:   unsigned int scale_factor;
                    459:   int need_pop;
                    460: 
                    461:   assert (constant_factor > 0);
                    462: 
                    463:   /* if the constant has any two factors in it: */
                    464:   shift_count = _tme_recode_x86_ffs(constant_factor);
                    465:   if (shift_count > 0) {
                    466: 
                    467:     /* multiply by the constant with the two factors removed: */
                    468:     thunk_bytes = _tme_recode_x86_emit_mul_constant(thunk_bytes,
                    469:                                                    reg_size,
                    470:                                                    reg_x86_factor,
                    471:                                                    constant_factor >> shift_count,
                    472:                                                    reg_x86_scratch);
                    473: 
                    474:     /* emit a shift for the two factors: */
                    475:     rex = TME_RECODE_X86_REX_B(reg_size, reg_x86_factor);
                    476:     if (rex != 0) {
                    477:       *(thunk_bytes++) = rex;
                    478:     }
                    479:     thunk_bytes[0] = TME_RECODE_X86_OPCODE_GRP2_Ib_Ev;
                    480:     thunk_bytes[1]
                    481:       = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(reg_x86_factor),
                    482:                                    TME_RECODE_X86_OPCODE_GRP2_SHL);
                    483:     thunk_bytes[2] = shift_count;
                    484:     thunk_bytes += 3;
                    485: 
                    486:     /* done: */
                    487:     return (thunk_bytes);
                    488:   }
                    489: 
                    490:   /* handle all of the nine, five, and three factors: */
                    491:   scale_factor = 8;
                    492:   for (; scale_factor > 1; ) {
                    493: 
                    494:     /* if this constant doesn't have any more of this factor: */
                    495:     if ((constant_factor % (1 + scale_factor)) != 0) {
                    496: 
                    497:       /* advance to the next factor: */
                    498:       scale_factor /= 2;
                    499:       continue;
                    500:     }
                    501: 
                    502:     /* emit an lea to multiply by this factor: */
                    503:     rex
                    504:       = (TME_RECODE_X86_REX_W(reg_size)
                    505:         | TME_RECODE_X86_REX_R(reg_size, reg_x86_factor)
                    506:         | TME_RECODE_X86_REX_X(reg_x86_factor)
                    507:         | TME_RECODE_X86_REX_B(reg_size, reg_x86_factor));
                    508:     if (rex != 0) {
                    509:       *(thunk_bytes++) = rex;
                    510:     }
                    511:     thunk_bytes[0] = TME_RECODE_X86_OPCODE_LEA;
                    512:     thunk_bytes[1] = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_EA(TME_RECODE_X86_EA_BASE_SIB),
                    513:                                                 TME_RECODE_X86_REG(reg_x86_factor));
                    514:     thunk_bytes[2] = TME_RECODE_X86_SIB(reg_x86_factor, reg_x86_factor, scale_factor);
                    515:     thunk_bytes += 3;
                    516:     constant_factor /= (1 + scale_factor);
                    517:   }
                    518: 
                    519:   /* if we still have a constant: */
                    520:   if (constant_factor > 1) {
                    521: 
                    522:     /* if we need to, push the scratch register: */
                    523:     need_pop = (reg_x86_scratch > TME_RECODE_X86_REG_UNDEF);
                    524:     if (need_pop) {
                    525:       reg_x86_scratch -= TME_RECODE_X86_REG_UNDEF;
                    526:       _tme_recode_x86_emit_reg_push(thunk_bytes, reg_x86_scratch);
                    527:     }
                    528: 
                    529:     /* copy the register into the scratch register: */
                    530:     rex
                    531:       = (TME_RECODE_X86_REX_B(reg_size, reg_x86_factor)
                    532:         | TME_RECODE_X86_REX_R(reg_size, reg_x86_scratch));
                    533:     if (rex != 0) {
                    534:       *(thunk_bytes++) = rex;
                    535:     }
                    536:     thunk_bytes[0] = (TME_RECODE_X86_OPCODE_BINOP_MOV + TME_RECODE_X86_OPCODE_BINOP_Ev_Gv);
                    537:     thunk_bytes[1]
                    538:       = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(reg_x86_factor),
                    539:                                    reg_x86_scratch);
                    540:     thunk_bytes += 2;
                    541: 
                    542:     /* if the constant ends in 11 binary, we will multiply by the
                    543:        constant plus one and then subtract one, otherwise we will
                    544:        multiply by the constant minus one and add one: */
                    545:     thunk_bytes
                    546:       = _tme_recode_x86_emit_mul_constant(thunk_bytes,
                    547:                                          reg_size,
                    548:                                          reg_x86_factor,
                    549:                                          (constant_factor
                    550:                                           + (constant_factor & 2)
                    551:                                           - 1),
                    552:                                          (TME_RECODE_X86_REG_UNDEF
                    553:                                           + reg_x86_scratch));
                    554:     rex
                    555:       = (TME_RECODE_X86_REX_B(reg_size, reg_x86_scratch)
                    556:         | TME_RECODE_X86_REX_R(reg_size, reg_x86_factor));
                    557:     if (rex != 0) {
                    558:       *(thunk_bytes++) = rex;
                    559:     }
                    560:     thunk_bytes[0]
                    561:       = (TME_RECODE_X86_OPCODE_BINOP_Ev_Gv
                    562:         + ((constant_factor & 2)
                    563:            ? TME_RECODE_X86_OPCODE_BINOP_SUB
                    564:            : TME_RECODE_X86_OPCODE_BINOP_ADD));
                    565:     thunk_bytes[1]
                    566:       = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(reg_x86_scratch),
                    567:                                    reg_x86_factor);
                    568:     thunk_bytes += 2;
                    569: 
                    570:     /* if we need to, pop the scratch register: */
                    571:     if (need_pop) {
                    572:       _tme_recode_x86_emit_reg_pop(thunk_bytes, reg_x86_scratch);
                    573:     }
                    574:   }
                    575: 
                    576:   /* done: */
                    577:   return (thunk_bytes);
                    578: }
                    579: 
                    580: /* this emits a jmp or jcc: */
                    581: static tme_uint8_t *
                    582: _tme_recode_x86_emit_jmp(tme_uint8_t *thunk_bytes,
                    583:                         tme_uint32_t opcode,
                    584:                         const tme_uint8_t *thunk_bytes_target)
                    585: {
                    586:   int one_if_opcode0f_jcc;
                    587:   signed long disp;
                    588: 
                    589:   /* write the opcode: */
                    590:   *((tme_uint16_t *) thunk_bytes) = opcode;
                    591:   
                    592:   /* see if this is a six-byte jcc instruction: */
                    593:   one_if_opcode0f_jcc = ((opcode & 0xff) == TME_RECODE_X86_OPCODE_ESC_0F);
                    594: 
                    595:   /* the opcode must be for a jmp or jcc instruction: */
                    596:   assert (opcode == TME_RECODE_X86_OPCODE_JMP_RELb
                    597:          || opcode == TME_RECODE_X86_OPCODE_JMP_RELz
                    598:          || (opcode & 0xf0) == TME_RECODE_X86_OPCODE_JCC(0)
                    599:          || (one_if_opcode0f_jcc
                    600:              && (opcode >> 12) == (TME_RECODE_X86_OPCODE0F_JCC(0) >> 4)));
                    601: 
                    602:   /* if we know the jump target now: */
                    603:   if (thunk_bytes_target != NULL) {
                    604: 
                    605:     /* if the displacement can be a sign-extended eight bits: */
                    606:     disp = thunk_bytes_target - (thunk_bytes + 2);
                    607:     if (disp == (tme_int8_t) disp) {
                    608: 
                    609:       /* write the displacement: */
                    610:       thunk_bytes[1] = disp;
                    611: 
                    612:       /* if the given opcode uses a 32-bit displacement, convert it to
                    613:         one the uses an 8-bit displacement: */
                    614:       if (opcode == TME_RECODE_X86_OPCODE_JMP_RELz) {
                    615:        opcode = TME_RECODE_X86_OPCODE_JMP_RELb;
                    616:       }
                    617:       else if (one_if_opcode0f_jcc) {
                    618:        opcode
                    619:          = TME_RECODE_X86_OPCODE_JCC((opcode >> 8)
                    620:                                      - TME_RECODE_X86_OPCODE0F_JCC(0));
                    621:       }
                    622:       one_if_opcode0f_jcc = 0;
                    623: 
                    624:       /* rewrite the possibly changed opcode: */
                    625:       thunk_bytes[0] = opcode;
                    626:     }
                    627: 
                    628:     /* otherwise, the displacement can't be a sign-extended eight bits: */
                    629:     else {
                    630: 
                    631:       /* the opcode must use a 32-bit displacement: */
                    632:       assert (opcode == TME_RECODE_X86_OPCODE_JMP_RELz
                    633:              || one_if_opcode0f_jcc);
                    634: 
                    635:       /* the displacement must fit in a sign-extended 32 bits: */
                    636:       disp = (thunk_bytes_target
                    637:              - (thunk_bytes
                    638:                 + 1
                    639:                 + one_if_opcode0f_jcc
                    640:                 + sizeof(tme_int32_t)));
                    641:       assert (disp == (tme_int32_t) disp);
                    642: 
                    643:       /* write the displacement: */
                    644:       *((tme_int32_t *) &thunk_bytes[1 + one_if_opcode0f_jcc]) = disp;
                    645:     }
                    646:   }
                    647: 
                    648:   /* advance: */
                    649:   thunk_bytes += 1 + one_if_opcode0f_jcc + 1;
                    650:   if (opcode == TME_RECODE_X86_OPCODE_JMP_RELz
                    651:       || one_if_opcode0f_jcc) {
                    652:     thunk_bytes += sizeof(tme_uint32_t) - 1;
                    653:   }
                    654: 
                    655:   return (thunk_bytes);
                    656: }
                    657: 
                    658: /* this fixes up a jmp or jcc: */
                    659: static void
                    660: _tme_recode_x86_fixup_jmp(tme_uint8_t *thunk_bytes,
                    661:                          const tme_uint8_t *thunk_bytes_target)
                    662: {
                    663:   tme_uint8_t opcode;
                    664:   int one_if_opcode0f_jcc;
                    665:   signed long disp;
                    666: 
                    667:   /* get the first byte of the opcode: */
                    668:   opcode = thunk_bytes[0];
                    669: 
                    670:   /* see if this is a six-byte jcc instruction: */
                    671:   one_if_opcode0f_jcc = (opcode == TME_RECODE_X86_OPCODE_ESC_0F);
                    672: 
                    673:   /* if the opcode uses a 32-bit displacement: */
                    674:   if (opcode == TME_RECODE_X86_OPCODE_JMP_RELz
                    675:       || one_if_opcode0f_jcc) {
                    676: 
                    677:     /* the displacement must fit in a sign-extended 32 bits: */
                    678:     disp = (thunk_bytes_target
                    679:            - (thunk_bytes
                    680:               + 1
                    681:               + one_if_opcode0f_jcc
                    682:               + sizeof(tme_int32_t)));
                    683:     assert (disp == (tme_int32_t) disp);
                    684: 
                    685:     /* write the displacement: */
                    686:     *((tme_int32_t *) &thunk_bytes[1 + one_if_opcode0f_jcc]) = disp;
                    687:   }
                    688: 
                    689:   /* otherwise, the opcode uses an 8-bit displacement: */
                    690:   else {
                    691: 
                    692:     /* the displacement must fit in a sign-extended eight bits: */
                    693:     disp = thunk_bytes_target - (thunk_bytes + 2);
                    694:     assert (disp == (tme_int8_t) disp);
                    695: 
                    696:     /* write the displacement: */
                    697:     thunk_bytes[1] = disp;
                    698:   }
                    699: }
                    700: 
                    701: /* prototypes: */
                    702: 
                    703: /* this emits instructions for a chain in: */
                    704: static void _tme_recode_x86_chain_in _TME_P((struct tme_recode_ic *, const struct tme_recode_insns_group *));
                    705: 
                    706: /* this emits instructions for a chain out: */
                    707: static void _tme_recode_x86_chain_out _TME_P((struct tme_recode_ic *, const struct tme_recode_insns_group *));
                    708: 
                    709: /* include the other code files: */
                    710: #include "host/x86/rc-x86-subs.c"
                    711: #include "host/x86/rc-x86-regs.c"
                    712: #include "host/x86/rc-x86-conds.c"
                    713: #include "host/x86/rc-x86-insns.c"
                    714: #include "host/x86/rc-x86-flags.c"
                    715: #include "host/x86/rc-x86-tlb.c"
                    716: #include "host/x86/rc-x86-rws.c"
                    717: #include "host/x86/rc-x86-chain.c"
                    718: 
                    719: /* this host function starts a new IC: */
                    720: void
                    721: tme_recode_host_ic_new(struct tme_recode_ic *ic)
                    722: {
                    723:   tme_uint8_t *thunk_bytes;
                    724:   tme_recode_thunk_off_t thunk_off;
                    725: 
                    726:   /* make the chain epilogue: */
                    727:   _tme_recode_x86_chain_epilogue(ic);
                    728: 
                    729:   /* copy the hand-coded subs: */
                    730:   tme_recode_x86_insns_start(ic, thunk_bytes);
                    731:   thunk_off = tme_recode_build_to_thunk_off(ic, thunk_bytes);
                    732:   memcpy(thunk_bytes, _tme_recode_x86_subs, sizeof(_tme_recode_x86_subs));
                    733:   thunk_bytes += sizeof(_tme_recode_x86_subs);
                    734:   tme_recode_x86_insns_finish(ic, thunk_bytes);
                    735:   tme_recode_host_thunk_finish(ic);
                    736: 
                    737:   /* set the thunk offsets of the shift insn subs: */
                    738: #if (TME_RECODE_OPCODE_SHLL + 1) != TME_RECODE_OPCODE_SHRL || (TME_RECODE_OPCODE_SHRL + 1) != TME_RECODE_OPCODE_SHRA
                    739: #error "TME_RECODE_OPCODE_ values changed"
                    740: #endif
                    741: #define _tme_recode_x86_set_subs_shift(size, opcode, subs)             \
                    742:   ic->tme_recode_x86_ic_subs_shift                                     \
                    743:     [_TME_CONCAT(TME_RECODE_SIZE_,size) - TME_RECODE_SIZE_8]           \
                    744:     [_TME_CONCAT(TME_RECODE_OPCODE_,opcode) - TME_RECODE_OPCODE_SHLL]  \
                    745:     = (thunk_off + _TME_CONCAT3(tme_recode_x86_,subs,size))
                    746:   _tme_recode_x86_set_subs_shift(8, SHLL, shll);
                    747:   _tme_recode_x86_set_subs_shift(8, SHRL, shrl);
                    748:   _tme_recode_x86_set_subs_shift(8, SHRA, shra);
                    749:   _tme_recode_x86_set_subs_shift(16, SHLL, shll);
                    750:   _tme_recode_x86_set_subs_shift(16, SHRL, shrl);
                    751:   _tme_recode_x86_set_subs_shift(16, SHRA, shra);
                    752:   _tme_recode_x86_set_subs_shift(32, SHLL, shll);
                    753:   _tme_recode_x86_set_subs_shift(32, SHRL, shrl);
                    754:   _tme_recode_x86_set_subs_shift(32, SHRA, shra);
                    755: #if TME_RECODE_SIZE_GUEST_MAX > TME_RECODE_SIZE_32
                    756:   _tme_recode_x86_set_subs_shift(64, SHLL, shll);
                    757:   _tme_recode_x86_set_subs_shift(64, SHRL, shrl);
                    758:   _tme_recode_x86_set_subs_shift(64, SHRA, shra);
                    759: #endif /* TME_RECODE_SIZE_GUEST_MAX > TME_RECODE_SIZE_32 */
                    760: #if TME_RECODE_SIZE_GUEST_MAX > (TME_RECODE_SIZE_32 + 1)
                    761:   _tme_recode_x86_set_subs_shift(128, SHLL, shll);
                    762:   _tme_recode_x86_set_subs_shift(128, SHRL, shrl);
                    763:   _tme_recode_x86_set_subs_shift(128, SHRA, shra);
                    764: #endif /* TME_RECODE_SIZE_GUEST_MAX > (TME_RECODE_SIZE_32 + 1) */
                    765: #undef _tme_recode_x86_set_subs_shift
                    766: 
                    767:   /* check the value of TME_RECODE_HOST_INSN_SIZE_MAX: */
                    768:   _tme_recode_x86_insn_size_max_check();
                    769: }
                    770: 
                    771: #endif /* TME_HAVE_RECODE */

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.