|
|
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 */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.