|
|
1.1 root 1: /* $Id: rc-x86-rws.c,v 1.5 2010/06/05 19:14:40 fredette Exp $ */
2:
3: /* libtme/host/x86/rc-x86-rws.c - x86 host recode reads and writes support: */
4:
5: /*
6: * Copyright (c) 2008 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: _TME_RCSID("$Id: rc-x86-rws.c,v 1.5 2010/06/05 19:14:40 fredette Exp $");
37:
38: /* this emits instructions to do a byte swap in a host register: */
39: static tme_uint8_t *
40: _tme_recode_x86_rw_bswap(tme_uint8_t *thunk_bytes,
41: unsigned int size,
42: unsigned int reg_x86)
43: {
44: unsigned int rex;
45:
46: /* NB: we don't have to worry about zero-truncation on an x86-64
47: host; if this register is supposed to be sign-extended, we do
48: that after all byte swapping: */
49:
50: /* if this is an eight-bit byte swap: */
51: if (size == TME_RECODE_SIZE_8) {
52: /* nothing to do */
53: }
54:
55: /* otherwise, if this is a 16-bit byte swap: */
56: else if (size == TME_RECODE_SIZE_16) {
57:
58: /* if this register has a high 8-bit encoding: */
59: if (reg_x86 < TME_RECODE_X86_REG_SP) {
60:
61: /* emit an xchgb %regh, %regl: */
62: thunk_bytes[0]
63: = (TME_RECODE_X86_OPCODE_BINOP_XCHG
64: + TME_RECODE_X86_OPCODE_BINOP_Gb_Eb);
65: thunk_bytes[1]
66: = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(reg_x86),
67: TME_RECODE_X86_REG((TME_RECODE_X86_REG_SP + reg_x86)));
68: thunk_bytes += 2;
69: }
70:
71: /* otherwise, this register doesn't have a high 8-bit encoding: */
72: else {
73:
74: /* emit a rorw $8, %reg: */
75: thunk_bytes[0] = TME_RECODE_X86_PREFIX_OPSIZ;
76: thunk_bytes[1] = TME_RECODE_X86_OPCODE_GRP2_Ib_Ev;
77: thunk_bytes[2]
78: = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(reg_x86),
79: TME_RECODE_X86_OPCODE_GRP2_ROR);
80: thunk_bytes[3] = 8;
81: thunk_bytes += 4;
82: }
83: }
84:
85: /* otherwise, this is a 32-bit swap, and/or a host-sized byte
86: swap: */
87: else {
88:
89: /* if the bswap instruction is available: */
90: if (1) {
91:
92: /* emit a bswap %reg: */
93: rex = TME_RECODE_X86_REX_R(size, reg_x86);
94: if (rex != 0) {
95: *(thunk_bytes++) = rex;
96: }
97: thunk_bytes[0] = TME_RECODE_X86_OPCODE_ESC_0F;
98: thunk_bytes[1] = TME_RECODE_X86_OPCODE0F_BSWAP(reg_x86);
99: thunk_bytes += 2;
100: }
101:
102: /* otherwise, the bswap instruction is not available: */
103: else {
104:
105: /* this must be a 32-bit swap: */
106: assert (size == TME_RECODE_SIZE_32);
107:
108: /* emit:
109: rorw $8, %reg
110: rorl $16, %reg
111: rorw $8, %reg
112: */
113: thunk_bytes[0] = TME_RECODE_X86_PREFIX_OPSIZ;
114: thunk_bytes[1] = TME_RECODE_X86_OPCODE_GRP2_Ib_Ev;
115: thunk_bytes[2]
116: = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(reg_x86),
117: TME_RECODE_X86_OPCODE_GRP2_ROR);
118: thunk_bytes[3] = 8;
119: thunk_bytes += 4;
120: thunk_bytes[0] = TME_RECODE_X86_OPCODE_GRP2_Ib_Ev;
121: thunk_bytes[1]
122: = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(reg_x86),
123: TME_RECODE_X86_OPCODE_GRP2_ROR);
124: thunk_bytes[2] = 16;
125: thunk_bytes += 3;
126: thunk_bytes[0] = TME_RECODE_X86_PREFIX_OPSIZ;
127: thunk_bytes[1] = TME_RECODE_X86_OPCODE_GRP2_Ib_Ev;
128: thunk_bytes[2]
129: = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(reg_x86),
130: TME_RECODE_X86_OPCODE_GRP2_ROR);
131: thunk_bytes[3] = 8;
132: thunk_bytes += 4;
133: }
134: }
135:
136: return (thunk_bytes);
137: }
138:
139: /* this host function returns a new read/write thunk: */
140: struct tme_recode_rw_thunk *
141: tme_recode_host_rw_thunk_new(struct tme_recode_ic *ic,
142: const struct tme_recode_rw *rw)
143: {
144: struct tme_recode_rw_thunk *rw_thunk;
145: unsigned int max_boundaries_guest;
146: unsigned int max_boundaries_host;
147: struct tme_recode_x86_tlb_type x86_tlb_type;
148: unsigned int reg_x86_address;
149: unsigned int reg_host_value_0;
150: unsigned int reg_host_value_1;
151: struct tme_recode_insn insn_buffer;
152: tme_uint8_t *thunk_bytes;
153: unsigned int rex;
154: unsigned int reg_host_value_orig;
155: int stack_adjust;
156: unsigned int reg_size;
157: int memory_signed;
158:
159: /* start the new read/write thunk: */
160: if (!tme_recode_host_thunk_start(ic)) {
161: abort();
162: }
163: rw_thunk = tme_new(struct tme_recode_rw_thunk, 1);
164: rw_thunk->tme_recode_x86_rw_thunk_subs
165: = tme_recode_build_to_thunk_off(ic, ic->tme_recode_ic_thunk_build_next);
166:
167: /* assume that we will always need to assist: */
168: x86_tlb_type.tme_recode_x86_tlb_type_assist_jmp_address_ok = (tme_uint8_t *) NULL;
169: x86_tlb_type.tme_recode_x86_tlb_type_assist_jmp = (tme_uint8_t *) NULL;
170:
171: /* get the worst-case maximum number of guest bus boundaries this
172: read/write could cross: */
173: max_boundaries_guest
174: = (rw->tme_recode_rw_bus_boundary == 0
175: ? 0
176: : (((TME_MAX(rw->tme_recode_rw_bus_boundary,
177: rw->tme_recode_rw_address_type.tme_recode_address_type_align_min)
178: - rw->tme_recode_rw_address_type.tme_recode_address_type_align_min)
179: + (TME_BIT(rw->tme_recode_rw_memory_size - TME_RECODE_SIZE_8)
180: - 1))
181: / rw->tme_recode_rw_bus_boundary));
182:
183: /* get the worst-case maximum number of host bus boundaries this
184: read/write could cross: */
185: max_boundaries_host
186: = (((TME_MAX(TME_MEMORY_BUS_BOUNDARY,
187: rw->tme_recode_rw_address_type.tme_recode_address_type_align_min)
188: - rw->tme_recode_rw_address_type.tme_recode_address_type_align_min)
189: + (TME_BIT(rw->tme_recode_rw_memory_size - TME_RECODE_SIZE_8)
190: - 1))
191: / TME_MEMORY_BUS_BOUNDARY);
192:
193: /* NB: as of 20080906, Intel's "Intel 64 Architecture Memory
194: Ordering White Paper" (Order number 318147-001, from August 2007)
195: only guarantees that size-aligned reads and writes up to 64 bits
196: are atomic; we assume that non-Intel processors are similar.
197: until this guarantee is extended to cover size-aligned 128-bit
198: reads and writes, TME_MEMORY_BUS_BOUNDARY shouldn't be more than
199: sizeof(tme_uint64_t).
200:
201: this means that we will always assist guest 128-bit reads
202: and writes that might be atomic for the guest (since we will
203: detect above that we might cross more bus boundaries than the
204: guest might): */
205:
206: /* if threads are cooperative, or if common atomic operations aren't
207: being done under software lock and we can't cross more boundaries
208: than the guest would: */
209: if (TME_THREADS_COOPERATIVE
210: || (TME_MEMORY_ALIGNMENT_ATOMIC(TME_MEMORY_TYPE_COMMON) != 0
211: && max_boundaries_host <= max_boundaries_guest)) {
212:
213: /* get the TLB type for the address type: */
214: tme_recode_address_type_tlb_type(ic,
215: &rw->tme_recode_rw_address_type,
216: &x86_tlb_type.tme_recode_tlb_type);
217:
218: /* XXX FIXME - document read/write thunk calling convention, how
219: it differs from normal subs */
220:
221: /* for a double-host-size guest, the guest address is in the a:bp
222: register pair, otherwise it's in the a register. NB that we
223: primarily deal with only a host-sized part: */
224: reg_x86_address
225: = tme_recode_x86_reg_from_host[_tme_recode_x86_tlb_reg_host_address(ic)];
226:
227: /* for a write, the value to write is in the first host register
228: (pair). for a read, the value read is returned in the same
229: host register (pair) that was used for the address: */
230: if (rw->tme_recode_rw_write) {
231: reg_host_value_0 = TME_RECODE_REG_HOST(0);
232: }
233: else {
234: reg_host_value_0
235: = _tme_recode_x86_tlb_reg_host_address(ic);
236: assert (tme_recode_x86_reg_from_host[reg_host_value_0] == reg_x86_address);
237: }
238: reg_host_value_1 = reg_host_value_0 + 1;
239:
240: /* find, busy, and check a data TLB entry: */
241: _tme_recode_x86_tlb_busy(ic,
242: &rw->tme_recode_rw_address_type,
243: &x86_tlb_type);
244:
245: /* start more instructions: */
246: tme_recode_x86_insns_start(ic, thunk_bytes);
247:
248: /* if this is a write, and we need to byte-swap the value to
249: write: */
250: if (rw->tme_recode_rw_write
251: && rw->tme_recode_rw_memory_size > TME_RECODE_SIZE_8
252: && rw->tme_recode_rw_memory_endian != TME_ENDIAN_NATIVE) {
253:
254: /* we will byte-swap the value to write into (at least) the
255: TLB scratch register: */
256: reg_host_value_orig = reg_host_value_0;
257: assert (reg_host_value_1 == reg_host_value_0 + 1);
258: reg_host_value_0 = TME_RECODE_REG_HOST_UNDEF;
259: assert (tme_recode_x86_reg_from_host[reg_host_value_0] == TME_RECODE_X86_REG_TLB_SCRATCH);
260:
261: /* if this is a double-host-size write: */
262: if (TME_RECODE_SIZE_IS_DOUBLE_HOST(rw->tme_recode_rw_memory_size)) {
263:
264: /* we will use the most-significant half of the guest register
265: that held the address for the higher-in-memory (i.e., the
266: guest least-significant) part of the value. NB that we are
267: swapping guest register halves here, too: */
268: reg_host_value_1 = TME_RECODE_X86_REG_HOST_SUBS_SRC1 + 1;
269: assert (tme_recode_x86_reg_from_host[reg_host_value_1 - 1] == reg_x86_address);
270: _tme_recode_x86_emit_reg_copy(thunk_bytes,
271: tme_recode_x86_reg_from_host[reg_host_value_orig + 0],
272: tme_recode_x86_reg_from_host[reg_host_value_1]);
273: thunk_bytes
274: = _tme_recode_x86_rw_bswap(thunk_bytes,
275: TME_RECODE_SIZE_HOST,
276: tme_recode_x86_reg_from_host[reg_host_value_1]);
277: reg_host_value_orig += 1;
278: }
279:
280: /* copy and swap the lower-in-memory (i.e., the guest
281: most-significant) part of the value: */
282: _tme_recode_x86_emit_reg_copy(thunk_bytes,
283: tme_recode_x86_reg_from_host[reg_host_value_orig],
284: tme_recode_x86_reg_from_host[reg_host_value_0]);
285: thunk_bytes
286: = _tme_recode_x86_rw_bswap(thunk_bytes,
287: TME_MIN(rw->tme_recode_rw_memory_size,
288: TME_RECODE_SIZE_HOST),
289: tme_recode_x86_reg_from_host[reg_host_value_0]);
290: }
291:
292: /* if threads aren't cooperative, and this is a double-host-size
293: access: */
294: if (!TME_THREADS_COOPERATIVE
295: && TME_RECODE_SIZE_IS_DOUBLE_HOST(rw->tme_recode_rw_memory_size)) {
296:
297: /* NB: in this case, we assume that the host is capable of SSE2
298: instructions. this seems reasonable: */
299:
300: /* the x86-64 ABI requires that the stack pointer be 16-byte
301: aligned immediately before a call instruction. inside an
302: insn thunk, the stack pointer is 16-byte aligned immediately
303: before a call to a read/write thunk, which means at the
304: beginning of a read/write thunk it is only 8-byte aligned
305: (because of the return address for the read/write thunk).
306:
307: on x86-64, we want to use at least one movdqa, which requires
308: us to align the stack pointer: */
309: stack_adjust
310: = (TME_RECODE_SIZE_HOST == TME_RECODE_SIZE_32
311: ? 0
312: : TME_BIT(TME_RECODE_SIZE_HOST - TME_RECODE_SIZE_8));
313:
314: /* if this is a write: */
315: if (rw->tme_recode_rw_write) {
316:
317: /* do any stack pointer alignment: */
318: if (stack_adjust) {
319: thunk_bytes = _tme_recode_x86_emit_adjust_sp(thunk_bytes, -stack_adjust);
320: }
321:
322: /* push the double-host-size value to write: */
323: _tme_recode_x86_emit_reg_push(thunk_bytes, tme_recode_x86_reg_from_host[reg_host_value_1]);
324: _tme_recode_x86_emit_reg_push(thunk_bytes, tme_recode_x86_reg_from_host[reg_host_value_0]);
325:
326: /* after the write, we will need to discard the value: */
327: stack_adjust += TME_BIT(rw->tme_recode_rw_memory_size - TME_RECODE_SIZE_8);
328: }
329:
330: /* otherwise, this is a read: */
331: else {
332:
333: /* do any stack pointer alignment, and make space for the
334: double-host-size value to read: */
335: thunk_bytes
336: = _tme_recode_x86_emit_adjust_sp(thunk_bytes,
337: -(stack_adjust
338: + TME_BIT(rw->tme_recode_rw_memory_size
339: - TME_RECODE_SIZE_8)));
340: }
341:
342: /* emit one of:
343: movq (%esp), %xmm0
344: movdqa (%esp), %xmm0
345: movq (%address), %xmm0
346: movdqa (%address), %xmm0
347:
348: to read the value to read or write into %xmm0:
349: */
350: thunk_bytes[0]
351: = ((TME_RECODE_SIZE_HOST == TME_RECODE_SIZE_32
352: || (!rw->tme_recode_rw_write
353: && (rw->tme_recode_rw_address_type.tme_recode_address_type_align_min
354: < TME_BIT(rw->tme_recode_rw_memory_size - TME_RECODE_SIZE_8))))
355: ? TME_RECODE_X86_PREFIX_REP
356: : TME_RECODE_X86_PREFIX_OPSIZ);
357: thunk_bytes[1] = TME_RECODE_X86_OPCODE_ESC_0F;
358: thunk_bytes[2]
359: = (TME_RECODE_SIZE_HOST == TME_RECODE_SIZE_32
360: ? TME_RECODE_X86_OPCODEF30F_MOVQ_Wq_Vq
361: : thunk_bytes[0] == TME_RECODE_X86_PREFIX_REP
362: ? TME_RECODE_X86_OPCODEF30F_MOVDQU_Wdq_Vdq
363: : TME_RECODE_X86_OPCODE660F_MOVDQA_Wdq_Vdq);
364: if (rw->tme_recode_rw_write) {
365: thunk_bytes[3]
366: = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_EA(TME_RECODE_X86_EA_BASE_SIB),
367: TME_RECODE_X86_REG(TME_RECODE_X86_REG_XMM(0)));
368: thunk_bytes[4]
369: = TME_RECODE_X86_SIB(TME_RECODE_X86_REG_SP, TME_RECODE_X86_SIB_INDEX_NONE, 1);
370: thunk_bytes += 5;
371: }
372: else {
373: thunk_bytes[3]
374: = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_EA(reg_x86_address),
375: TME_RECODE_X86_REG(TME_RECODE_X86_REG_XMM(0)));
376: thunk_bytes += 4;
377: }
378:
379: /* emit one of:
380: movq %xmm0, (%address)
381: movdqu %xmm0, (%address)
382: movdqa %xmm0, (%address)
383: movq %xmm0, (%esp)
384: movdqa %xmm0, (%esp)
385:
386: to write the value to read or write in %xmm0:
387: */
388: thunk_bytes[0]
389: = ((TME_RECODE_SIZE_HOST == TME_RECODE_SIZE_32
390: || (rw->tme_recode_rw_write
391: && (rw->tme_recode_rw_address_type.tme_recode_address_type_align_min
392: >= TME_BIT(rw->tme_recode_rw_memory_size - TME_RECODE_SIZE_8))))
393: ? TME_RECODE_X86_PREFIX_OPSIZ
394: : TME_RECODE_X86_PREFIX_REP);
395: thunk_bytes[1] = TME_RECODE_X86_OPCODE_ESC_0F;
396: thunk_bytes[2]
397: = (TME_RECODE_SIZE_HOST == TME_RECODE_SIZE_32
398: ? TME_RECODE_X86_OPCODE660F_MOVQ_Vq_Wq
399: : thunk_bytes[0] == TME_RECODE_X86_PREFIX_REP
400: ? TME_RECODE_X86_OPCODEF30F_MOVDQU_Vdq_Wdq
401: : TME_RECODE_X86_OPCODE660F_MOVDQA_Vdq_Wdq);
402: if (rw->tme_recode_rw_write) {
403: thunk_bytes[3]
404: = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_EA(reg_x86_address),
405: TME_RECODE_X86_REG(TME_RECODE_X86_REG_XMM(0)));
406: thunk_bytes += 4;
407: }
408: else {
409: thunk_bytes[3]
410: = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_EA(TME_RECODE_X86_EA_BASE_SIB),
411: TME_RECODE_X86_REG(TME_RECODE_X86_REG_XMM(0)));
412: thunk_bytes[4]
413: = TME_RECODE_X86_SIB(TME_RECODE_X86_REG_SP, TME_RECODE_X86_SIB_INDEX_NONE, 1);
414: thunk_bytes += 5;
415: }
416:
417: /* if this was a read: */
418: if (!rw->tme_recode_rw_write) {
419:
420: /* pop the double-host-size value we read: */
421: _tme_recode_x86_emit_reg_pop(thunk_bytes, tme_recode_x86_reg_from_host[reg_host_value_0]);
422: _tme_recode_x86_emit_reg_pop(thunk_bytes, tme_recode_x86_reg_from_host[reg_host_value_1]);
423: }
424:
425: /* discard any double-host-size value we wrote, and any stack
426: pointer alignment: */
427: if (stack_adjust) {
428: thunk_bytes = _tme_recode_x86_emit_adjust_sp(thunk_bytes, -stack_adjust);
429: }
430: }
431:
432: /* otherwise, either threads are cooperative, or this isn't a
433: double-host-size access: */
434:
435: /* if this is a write: */
436: else if (rw->tme_recode_rw_write) {
437:
438: /* if this is an ia32 host, and an 8-bit store of a register
439: that doesn't have an 8-bit encoding: */
440: if (TME_RECODE_SIZE_HOST <= TME_RECODE_SIZE_32
441: && rw->tme_recode_rw_memory_size == TME_RECODE_SIZE_8
442: && tme_recode_x86_reg_from_host[reg_host_value_0] >= TME_RECODE_X86_REG_SP) {
443:
444: /* we will copy the value to write into the TLB scratch
445: register, which has an 8-bit encoding: */
446: reg_host_value_orig = reg_host_value_0;
447: reg_host_value_0 = TME_RECODE_REG_HOST_UNDEF;
448: assert (tme_recode_x86_reg_from_host[reg_host_value_0] < TME_RECODE_X86_REG_SP);
449: assert (tme_recode_x86_reg_from_host[reg_host_value_0] == TME_RECODE_X86_REG_TLB_SCRATCH);
450: _tme_recode_x86_emit_reg_copy(thunk_bytes,
451: tme_recode_x86_reg_from_host[reg_host_value_orig],
452: tme_recode_x86_reg_from_host[reg_host_value_0]);
453: }
454:
455: /* emit one of:
456: movb %reg, (%address)
457: movw %reg, (%address)
458: movl %reg, (%address)
459: movq %reg, (%address)
460: */
461: if (rw->tme_recode_rw_memory_size == TME_RECODE_SIZE_16) {
462: *(thunk_bytes++) = TME_RECODE_X86_PREFIX_OPSIZ;
463: }
464: rex
465: = (TME_RECODE_X86_REX_R(TME_MIN(rw->tme_recode_rw_memory_size,
466: TME_RECODE_SIZE_HOST),
467: tme_recode_x86_reg_from_host[reg_host_value_0])
468: | TME_RECODE_X86_REX_B(0, reg_x86_address));
469: if (rex != 0) {
470: *(thunk_bytes++) = rex;
471: }
472: thunk_bytes[0]
473: = (rw->tme_recode_rw_memory_size >= TME_RECODE_SIZE_16
474: ? (TME_RECODE_X86_OPCODE_BINOP_MOV + TME_RECODE_X86_OPCODE_BINOP_Gv_Ev)
475: : (TME_RECODE_X86_OPCODE_BINOP_MOV + TME_RECODE_X86_OPCODE_BINOP_Gb_Eb));
476: /* NB: a disp8 EA must be used when the base register is bp or r13: */
477: if (TME_RECODE_X86_REG(reg_x86_address) == TME_RECODE_X86_REG_BP) {
478: thunk_bytes[1]
479: = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_EA_DISP8(reg_x86_address),
480: TME_RECODE_X86_REG(tme_recode_x86_reg_from_host[reg_host_value_0]));
481: thunk_bytes[2] = 0;
482: thunk_bytes += 3;
483: }
484: else {
485: thunk_bytes[1]
486: = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_EA(reg_x86_address),
487: TME_RECODE_X86_REG(tme_recode_x86_reg_from_host[reg_host_value_0]));
488: thunk_bytes += 2;
489: }
490:
491: /* if this is a double-host-size write: */
492: if (TME_RECODE_SIZE_IS_DOUBLE_HOST(rw->tme_recode_rw_memory_size)) {
493:
494: /* emit one of:
495: movl %reg, 4(%address)
496: movq %reg, 8(%address)
497: */
498: rex
499: = (TME_RECODE_X86_REX_R(TME_RECODE_SIZE_HOST,
500: tme_recode_x86_reg_from_host[reg_host_value_1])
501: | TME_RECODE_X86_REX_B(0, reg_x86_address));
502: if (rex != 0) {
503: *(thunk_bytes++) = rex;
504: }
505: thunk_bytes[0] = TME_RECODE_X86_OPCODE_BINOP_MOV + TME_RECODE_X86_OPCODE_BINOP_Gv_Ev;
506: thunk_bytes[1]
507: = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_EA_DISP8(reg_x86_address),
508: TME_RECODE_X86_REG(tme_recode_x86_reg_from_host[reg_host_value_1]));
509: thunk_bytes[2] = TME_BIT(TME_RECODE_SIZE_HOST - TME_RECODE_SIZE_8);
510: thunk_bytes += 3;
511: }
512: }
513:
514: /* otherwise, this is a read: */
515: else {
516:
517: /* if this is a double-host-size read: */
518: if (TME_RECODE_SIZE_IS_DOUBLE_HOST(rw->tme_recode_rw_memory_size)) {
519:
520: /* emit one of:
521: movl 4(%address), %reg
522: movq 8(%address), %reg
523: */
524: rex
525: = (TME_RECODE_X86_REX_R(TME_RECODE_SIZE_HOST,
526: tme_recode_x86_reg_from_host[reg_host_value_1])
527: | TME_RECODE_X86_REX_B(0, reg_x86_address));
528: if (rex != 0) {
529: *(thunk_bytes++) = rex;
530: }
531: thunk_bytes[0] = TME_RECODE_X86_OPCODE_BINOP_MOV + TME_RECODE_X86_OPCODE_BINOP_Ev_Gv;
532: thunk_bytes[1]
533: = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_EA_DISP8(reg_x86_address),
534: TME_RECODE_X86_REG(tme_recode_x86_reg_from_host[reg_host_value_1]));
535: thunk_bytes[2] = TME_BIT(TME_RECODE_SIZE_HOST - TME_RECODE_SIZE_8);
536: thunk_bytes += 3;
537: }
538:
539: /* assume that we we will read into all of a host register: */
540: reg_size = TME_MAX(rw->tme_recode_rw_reg_size, TME_RECODE_SIZE_HOST);
541:
542: /* by default, in the read instruction itself we zero-extend the
543: value into all of a host register. the only time we will
544: sign-extend in the read instruction itself is when the
545: register size is greater than the memory size, memory is
546: signed, and we're only reading a byte or the guest's byte
547: order matches the host. if the first two are true, but we're
548: reading more than a byte and the guest's byte order doesn't
549: match the host, we have to wait to do the sign extension
550: after we've byte swapped the value read: */
551: memory_signed
552: = (rw->tme_recode_rw_reg_size > rw->tme_recode_rw_memory_size
553: && rw->tme_recode_rw_memory_signed
554: && (rw->tme_recode_rw_memory_size == TME_RECODE_SIZE_8
555: || rw->tme_recode_rw_memory_endian == TME_ENDIAN_NATIVE));
556:
557: /* if this is an x86-64 host and a 32-bit sign- or zero-extended load: */
558: if (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32
559: && rw->tme_recode_rw_memory_size == TME_RECODE_SIZE_32
560: && rw->tme_recode_rw_reg_size > rw->tme_recode_rw_memory_size) {
561:
562: /* if this is a zero-extended load, or if the guest's byte
563: order doesn't match the host: */
564: if (!memory_signed) {
565:
566: /* read into only the least-significant 32 bits of the
567: register. this will zero-extend the read to all 64
568: bits. this should prevent a rex prefix: */
569: reg_size = TME_RECODE_SIZE_32;
570: }
571: }
572:
573: /* emit any rex prefix: */
574: rex
575: = (TME_RECODE_X86_REX_B(0, reg_x86_address)
576: | TME_RECODE_X86_REX_R(TME_MIN(reg_size,
577: TME_RECODE_SIZE_HOST),
578: tme_recode_x86_reg_from_host[reg_host_value_0]));
579: if (rex != 0) {
580: *(thunk_bytes++) = rex;
581: }
582:
583: /* if this is an x86-64 host and a 32-bit sign-extended load: */
584: if (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32
585: && rw->tme_recode_rw_memory_size == TME_RECODE_SIZE_32
586: && reg_size > TME_RECODE_SIZE_32) {
587:
588: /* emit the opcode part of a movslq (%address), %reg: */
589: thunk_bytes[0] = TME_RECODE_X86_OPCODE_MOVS_El_Gv;
590: }
591:
592: /* otherwise, if this is an 8- or 16-bit load: */
593: else if (rw->tme_recode_rw_memory_size <= TME_RECODE_SIZE_16) {
594:
595: /* emit the opcode part of one of:
596: movsb (%address), %reg
597: movzb (%address), %reg
598: movsw (%address), %reg
599: movzw (%address), %reg
600: */
601: *(thunk_bytes++) = TME_RECODE_X86_OPCODE_ESC_0F;
602: thunk_bytes[0]
603: = (rw->tme_recode_rw_memory_size == TME_RECODE_SIZE_8
604: ? (memory_signed
605: ? TME_RECODE_X86_OPCODE0F_MOVS_Eb_Gv
606: : TME_RECODE_X86_OPCODE0F_MOVZ_Eb_Gv)
607: : (memory_signed
608: ? TME_RECODE_X86_OPCODE0F_MOVS_Ew_Gv
609: : TME_RECODE_X86_OPCODE0F_MOVZ_Ew_Gv));
610: }
611:
612: /* otherwise, this load is double-host-size, or host-size, or a
613: 32-bit zero-extended load on an x86-64 host: */
614: else {
615:
616: /* emit the opcode part of a movl (%address), %reg or a movq (%address), %reg: */
617: thunk_bytes[0]
618: = (TME_RECODE_X86_OPCODE_BINOP_MOV
619: + TME_RECODE_X86_OPCODE_BINOP_Ev_Gv);
620: }
621:
622: /* emit the modR/M byte for this instruction: */
623: /* NB: a disp8 EA must be used when the base register is bp or r13: */
624: if (TME_RECODE_X86_REG(reg_x86_address) == TME_RECODE_X86_REG_BP) {
625: thunk_bytes[1]
626: = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_EA_DISP8(reg_x86_address),
627: TME_RECODE_X86_REG(tme_recode_x86_reg_from_host[reg_host_value_0]));
628: thunk_bytes[2] = 0;
629: thunk_bytes += 3;
630: }
631: else {
632: thunk_bytes[1]
633: = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_EA(reg_x86_address),
634: TME_RECODE_X86_REG(tme_recode_x86_reg_from_host[reg_host_value_0]));
635: thunk_bytes += 2;
636: }
637: }
638:
639: /* if this is a read: */
640: if (!rw->tme_recode_rw_write) {
641:
642: /* if we need to byte-swap the value read: */
643: if (rw->tme_recode_rw_memory_size > TME_RECODE_SIZE_8
644: && rw->tme_recode_rw_memory_endian != TME_ENDIAN_NATIVE) {
645:
646: /* if this is a double-host-size read: */
647: if (TME_RECODE_SIZE_IS_DOUBLE_HOST(rw->tme_recode_rw_memory_size)) {
648:
649: /* swap the guest register halves: */
650: _tme_recode_x86_emit_reg_binop(thunk_bytes,
651: TME_RECODE_X86_OPCODE_BINOP_XCHG,
652: tme_recode_x86_reg_from_host[reg_host_value_0],
653: tme_recode_x86_reg_from_host[reg_host_value_1]);
654:
655: /* byte-swap the most-significant half of the guest register: */
656: thunk_bytes
657: = _tme_recode_x86_rw_bswap(thunk_bytes,
658: TME_RECODE_SIZE_HOST,
659: tme_recode_x86_reg_from_host[reg_host_value_1]);
660: }
661:
662: /* byte-swap the (least-significant half of the) guest register: */
663: thunk_bytes
664: = _tme_recode_x86_rw_bswap(thunk_bytes,
665: TME_MIN(rw->tme_recode_rw_memory_size,
666: TME_RECODE_SIZE_HOST),
667: tme_recode_x86_reg_from_host[reg_host_value_0]);
668:
669: /* if the read is sign-extended from smaller than host-sized: */
670: if (rw->tme_recode_rw_reg_size > rw->tme_recode_rw_memory_size
671: && rw->tme_recode_rw_memory_signed
672: && rw->tme_recode_rw_memory_size < TME_RECODE_SIZE_HOST) {
673:
674: /* sign-extend the value read to host size: */
675: tme_recode_x86_insns_finish(ic, thunk_bytes);
676: insn_buffer.tme_recode_insn_opcode = TME_RECODE_OPCODE_EXTS;
677: insn_buffer.tme_recode_insn_operand_src[0] = reg_host_value_0;
678: insn_buffer.tme_recode_insn_operand_src[1] = rw->tme_recode_rw_memory_size;
679: insn_buffer.tme_recode_insn_operand_dst = reg_host_value_0;
680: _tme_recode_x86_insn_ext(ic, &insn_buffer);
681: tme_recode_x86_insns_start(ic, thunk_bytes);
682: }
683: }
684:
685: /* if this is a double-host-size read that needs zero- or sign-extension: */
686: if (TME_RECODE_SIZE_IS_DOUBLE_HOST(rw->tme_recode_rw_reg_size)
687: && rw->tme_recode_rw_reg_size > rw->tme_recode_rw_memory_size) {
688:
689: /* if memory is signed: */
690: if (rw->tme_recode_rw_memory_signed) {
691:
692: /* sign-extend the value read: */
693: _tme_recode_x86_emit_reg_binop(thunk_bytes,
694: TME_RECODE_X86_OPCODE_BINOP_MOV,
695: tme_recode_x86_reg_from_host[reg_host_value_0],
696: tme_recode_x86_reg_from_host[reg_host_value_1]);
697: _tme_recode_x86_emit_reg_binop(thunk_bytes,
698: TME_RECODE_X86_OPCODE_BINOP_ADD,
699: tme_recode_x86_reg_from_host[reg_host_value_0],
700: tme_recode_x86_reg_from_host[reg_host_value_1]);
701: _tme_recode_x86_emit_reg_binop(thunk_bytes,
702: TME_RECODE_X86_OPCODE_BINOP_SBB,
703: tme_recode_x86_reg_from_host[reg_host_value_1],
704: tme_recode_x86_reg_from_host[reg_host_value_1]);
705: }
706:
707: /* otherwise, memory is unsigned: */
708: else {
709:
710: /* zero-extend the value read: */
711: /* NB: we always make this a 32-bit operation, to try to
712: prevent a rex prefix: */
713: rex = (TME_RECODE_X86_REX_R(TME_RECODE_SIZE_32,
714: tme_recode_x86_reg_from_host[reg_host_value_1])
715: | TME_RECODE_X86_REX_B(TME_RECODE_SIZE_32,
716: tme_recode_x86_reg_from_host[reg_host_value_1]));
717: if (rex != 0) {
718: *(thunk_bytes++) = rex;
719: }
720: thunk_bytes[0]
721: = (TME_RECODE_X86_OPCODE_BINOP_XOR
722: + TME_RECODE_X86_OPCODE_BINOP_Gv_Ev);
723: thunk_bytes[1]
724: = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(tme_recode_x86_reg_from_host[reg_host_value_1]),
725: TME_RECODE_X86_REG(tme_recode_x86_reg_from_host[reg_host_value_1]));
726: thunk_bytes += 2;
727: }
728: }
729: }
730:
731: /* finish these instructions: */
732: tme_recode_x86_insns_finish(ic, thunk_bytes);
733:
734: /* unbusy the TLB entry: */
735: _tme_recode_x86_tlb_unbusy(ic,
736: x86_tlb_type.tme_recode_tlb_type.tme_recode_tlb_type_offset_token);
737:
738: /* start more instructions: */
739: tme_recode_x86_insns_start(ic, thunk_bytes);
740:
741: /* return to the instructions thunk: */
742: *(thunk_bytes++) = TME_RECODE_X86_OPCODE_RET;
743:
744: /* finish these instructions: */
745: tme_recode_x86_insns_finish(ic, thunk_bytes);
746: }
747:
748: if (x86_tlb_type.tme_recode_x86_tlb_type_assist_jmp != NULL) {
749:
750: /* finish the assist conditional jump above, now
751: that we are at the target: */
752: _tme_recode_x86_fixup_jmp(x86_tlb_type.tme_recode_x86_tlb_type_assist_jmp,
753: ic->tme_recode_ic_thunk_build_next);
754:
755: /* start more instructions: */
756: tme_recode_x86_insns_start(ic, thunk_bytes);
757:
758: /* exclusive-or the TLB entry page offset with the
759: (least-significant half of the) TLB entry page, to convert the
760: TLB entry page offset back into the (least-significant half of
761: the) guest address: */
762: thunk_bytes
763: = _tme_recode_x86_tlb_ref(thunk_bytes,
764: TME_RECODE_SIZE_HOST,
765: (TME_RECODE_X86_OPCODE_BINOP_XOR
766: + TME_RECODE_X86_OPCODE_BINOP_Ev_Gv),
767: TME_RECODE_X86_REG_TLB,
768: x86_tlb_type.tme_recode_tlb_type.tme_recode_tlb_type_offset_page,
769: TME_RECODE_X86_REG(reg_x86_address));
770:
771: /* finish these instructions: */
772: tme_recode_x86_insns_finish(ic, thunk_bytes);
773: }
774:
775: /* fix up any double-host-size address assist: */
776: if (x86_tlb_type.tme_recode_x86_tlb_type_assist_jmp_address_ok != NULL) {
777: _tme_recode_x86_fixup_jmp(x86_tlb_type.tme_recode_x86_tlb_type_assist_jmp_address_ok,
778: ic->tme_recode_ic_thunk_build_next);
779: }
780:
781: if (x86_tlb_type.tme_recode_x86_tlb_type_assist_jmp_address_ok != NULL
782: || x86_tlb_type.tme_recode_x86_tlb_type_assist_jmp != NULL) {
783:
784: /* unbusy the TLB entry: */
785: _tme_recode_x86_tlb_unbusy(ic,
786: x86_tlb_type.tme_recode_tlb_type.tme_recode_tlb_type_offset_token);
787: }
788:
789: /* start more instructions: */
790: tme_recode_x86_insns_start(ic, thunk_bytes);
791:
792: /* if this is an ia32 host: */
793: if (TME_RECODE_SIZE_HOST == TME_RECODE_SIZE_32) {
794:
795: /* if this is a write: */
796: if (rw->tme_recode_rw_write) {
797:
798: /* push the value argument for the guest function. NB that if
799: double-host-size guests are supported, but this isn't a
800: double-host-size guest, we use a garbage word on the stack as
801: the most-significant half of this argument (which is okay
802: since the guest functions are supposed to truncate their
803: arguments to the expected size): */
804: if (TME_RECODE_SIZE_GUEST_MAX > TME_RECODE_SIZE_HOST) {
805: _tme_recode_x86_emit_reg_push(thunk_bytes, tme_recode_x86_reg_from_host[TME_RECODE_REG_HOST(0) + 1]);
806: }
807: _tme_recode_x86_emit_reg_push(thunk_bytes, tme_recode_x86_reg_from_host[TME_RECODE_REG_HOST(0)]);
808: }
809:
810: /* push the address argument for the guest function. NB that if
811: double-host-size guests are supported, but this isn't a
812: double-host-size guest, we use a garbage word on the stack as
813: the most-significant half of this argument (which is okay since
814: the guest functions are supposed to truncate their arguments to
815: the expected size): */
816: if (TME_RECODE_SIZE_GUEST_MAX > TME_RECODE_SIZE_HOST) {
817: _tme_recode_x86_emit_reg_push(thunk_bytes, TME_RECODE_X86_REG_A);
818: }
819: _tme_recode_x86_emit_reg_push(thunk_bytes, reg_x86_address);
820:
821: /* emit the instruction to push the struct tme_ic * argument for
822: the guest function, and then the call instruction to the guest
823: function: */
824: *((tme_uint16_t *) thunk_bytes)
825: = (TME_RECODE_X86_OPCODE_PUSH_Gv(TME_RECODE_X86_REG_IC)
826: + (TME_RECODE_X86_OPCODE_CALL_RELz << 8));
827: thunk_bytes += 2 + sizeof(tme_uint32_t);
828: ((tme_int32_t *) thunk_bytes)[-1]
829: = (tme_recode_function_to_thunk_off(ic, rw->tme_recode_rw_guest_func_read)
830: - tme_recode_build_to_thunk_off(ic, thunk_bytes));
831:
832: /* remove the guest function arguments from the stack: */
833: thunk_bytes
834: = _tme_recode_x86_emit_adjust_sp(thunk_bytes,
835: (sizeof(struct tme_ic *)
836: + sizeof(tme_recode_uguest_t)
837: + (sizeof(tme_recode_uguest_t)
838: * !!rw->tme_recode_rw_write)));
839: }
840:
841: /* otherwise, this is an x86-64 host: */
842: else {
843:
844: /* push the caller-saved registers that aren't normally destroyed
845: by a read/write thunk: */
846: _tme_recode_x86_emit_reg_push(thunk_bytes, TME_RECODE_X86_REG_N(10));
847: _tme_recode_x86_emit_reg_push(thunk_bytes, TME_RECODE_X86_REG_N(11));
848: _tme_recode_x86_emit_reg_push(thunk_bytes, TME_RECODE_X86_REG_DI);
849: _tme_recode_x86_emit_reg_push(thunk_bytes, TME_RECODE_X86_REG_SI);
850: _tme_recode_x86_emit_reg_push(thunk_bytes, TME_RECODE_X86_REG_N(8));
851: _tme_recode_x86_emit_reg_push(thunk_bytes, TME_RECODE_X86_REG_N(9));
852:
853: /* make the struct tme_ic * argument for the guest function: */
854: _tme_recode_x86_emit_reg_copy(thunk_bytes, TME_RECODE_X86_REG_IC, TME_RECODE_X86_REG_DI);
855:
856: /* make the address argument for the guest function. NB that if
857: double-host-size guests are supported, but this isn't a
858: double-host-size guest, we use a garbage word as the
859: most-significant half of this argument (which is okay since the
860: guest functions are supposed to truncate their arguments to the
861: expected size): */
862: _tme_recode_x86_emit_reg_copy(thunk_bytes, reg_x86_address, TME_RECODE_X86_REG_SI);
863: if (TME_RECODE_SIZE_IS_DOUBLE_HOST(ic->tme_recode_ic_reg_size)) {
864: _tme_recode_x86_emit_reg_copy(thunk_bytes, TME_RECODE_X86_REG_A, TME_RECODE_X86_REG_D);
865: }
866:
867: /* if this is a write: */
868: if (rw->tme_recode_rw_write) {
869:
870: /* make the value argument for the guest function. NB that if
871: double-host-size guests are supported, but this isn't a
872: double-host-size guest, we use a garbage word as the
873: most-significant half of this argument (which is okay since
874: the guest functions are supposed to truncate their arguments
875: to the expected size): */
876: _tme_recode_x86_emit_reg_copy(thunk_bytes,
877: tme_recode_x86_reg_from_host[TME_RECODE_REG_HOST(0)],
878: (TME_RECODE_SIZE_GUEST_MAX <= TME_RECODE_SIZE_HOST
879: ? TME_RECODE_X86_REG_D
880: : TME_RECODE_X86_REG_C));
881: if (TME_RECODE_SIZE_IS_DOUBLE_HOST(ic->tme_recode_ic_reg_size)) {
882: _tme_recode_x86_emit_reg_copy(thunk_bytes,
883: tme_recode_x86_reg_from_host[TME_RECODE_REG_HOST(0)],
884: TME_RECODE_X86_REG_N(8));
885: }
886: }
887:
888: /* we must assume that we can't reach the guest function from the
889: instruction thunk with a 32-bit displacement. emit a direct
890: call to the guest function using %rax: */
891: *((tme_uint16_t *) thunk_bytes)
892: = (TME_RECODE_X86_REX_B(TME_RECODE_SIZE_HOST, TME_RECODE_X86_REG_A)
893: + (TME_RECODE_X86_OPCODE_MOV_Iv_Gv(TME_RECODE_X86_REG_A)
894: << 8));
895: memcpy(thunk_bytes + 2,
896: &rw->tme_recode_rw_guest_func_write,
897: TME_BIT(TME_RECODE_SIZE_HOST - TME_RECODE_SIZE_8));
898: thunk_bytes += 2 + TME_BIT(TME_RECODE_SIZE_HOST - TME_RECODE_SIZE_8);
899: *((tme_uint16_t *) thunk_bytes)
900: = (TME_RECODE_X86_OPCODE_GRP5
901: + (TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(TME_RECODE_X86_REG_A),
902: TME_RECODE_X86_OPCODE_GRP5_CALL)
903: << 8));
904: thunk_bytes += 2;
905:
906: /* pop the caller-saved registers that aren't normally destroyed
907: by a read/write thunk: */
908: _tme_recode_x86_emit_reg_pop(thunk_bytes, TME_RECODE_X86_REG_N(9));
909: _tme_recode_x86_emit_reg_pop(thunk_bytes, TME_RECODE_X86_REG_N(8));
910: _tme_recode_x86_emit_reg_pop(thunk_bytes, TME_RECODE_X86_REG_SI);
911: _tme_recode_x86_emit_reg_pop(thunk_bytes, TME_RECODE_X86_REG_DI);
912: _tme_recode_x86_emit_reg_pop(thunk_bytes, TME_RECODE_X86_REG_N(11));
913: _tme_recode_x86_emit_reg_pop(thunk_bytes, TME_RECODE_X86_REG_N(10));
914: }
915:
916: /* if this is a read: */
917: if (!rw->tme_recode_rw_write) {
918:
919: /* if this is a double-host-size guest: */
920: if (TME_RECODE_SIZE_IS_DOUBLE_HOST(ic->tme_recode_ic_reg_size)) {
921:
922: /* move the value read into the expected return registers: */
923: _tme_recode_x86_emit_reg_copy(thunk_bytes,
924: TME_RECODE_X86_REG_A,
925: tme_recode_x86_reg_from_host[reg_host_value_0]);
926: _tme_recode_x86_emit_reg_copy(thunk_bytes,
927: TME_RECODE_X86_REG_D,
928: tme_recode_x86_reg_from_host[reg_host_value_1]);
929: }
930:
931: /* otherwise, this is not a double-host-size guest: */
932: else {
933:
934: /* the value read should already be in the expected register: */
935: assert (tme_recode_x86_reg_from_host[reg_host_value_0] == TME_RECODE_X86_REG_A);
936: }
937: }
938:
939: /* return to the instructions thunk: */
940: *(thunk_bytes++) = TME_RECODE_X86_OPCODE_RET;
941:
942: /* finish these instructions: */
943: tme_recode_x86_insns_finish(ic, thunk_bytes);
944:
945: /* finish this read/write thunk: */
946: tme_recode_host_thunk_finish(ic);
947:
948: /* no further extension is needed for this read/write thunk: */
949: rw_thunk->tme_recode_x86_rw_thunk_extend_size = 0;
950:
951: return (rw_thunk);
952: }
953:
954: /* this host function tries to duplicate a read/write thunk: */
955: struct tme_recode_rw_thunk *
956: tme_recode_host_rw_thunk_dup(struct tme_recode_ic *ic,
957: const struct tme_recode_rw *rw,
958: const struct tme_recode_rw *rw_other)
959: {
960: tme_uint8_t *thunk_bytes_0;
961: struct tme_recode_insn insn_buffer;
962: tme_uint8_t *thunk_bytes_1;
963: struct tme_recode_rw_thunk *rw_thunk;
964:
965: /* start more instructions, so if we use _tme_recode_x86_insn_ext()
966: to emit an extension instruction, we can discard it from the
967: thunk build memory: */
968: tme_recode_x86_insns_start(ic, thunk_bytes_0);
969:
970: /* if our register size is the same as the memory size, or
971: if the existing read/write thunk will do the extension that
972: we need to at least our register size: */
973: /* NB: a read/write thunk always extends to at least host size, and
974: when no particular extension is explicitly required,
975: zero-extension is the default: */
976: if ((rw->tme_recode_rw_reg_size
977: == rw_other->tme_recode_rw_memory_size)
978: || ((((rw_other->tme_recode_rw_reg_size
979: > rw_other->tme_recode_rw_memory_size)
980: && rw_other->tme_recode_rw_memory_signed)
981: == !!rw->tme_recode_rw_memory_signed)
982: && (TME_MAX(rw_other->tme_recode_rw_reg_size, TME_RECODE_SIZE_HOST)
983: >= rw->tme_recode_rw_reg_size))) {
984:
985: /* we can reuse the existing read/write thunk, and we don't need
986: to do any extension: */
987: /* nothing to do */
988: }
989:
990: /* otherwise, if this is a double-host-size read: */
991: else if (TME_RECODE_SIZE_IS_DOUBLE_HOST(rw->tme_recode_rw_reg_size)) {
992:
993: /* we won't reuse the existing read/write thunk: */
994: return (NULL);
995: }
996:
997: /* otherwise, we will reuse this read/write thunk, with a single
998: zero- or sign-extension instruction after each call: */
999: else {
1000:
1001: /* use _tme_recode_x86_insn_ext() to emit the extension
1002: instruction: */
1003: insn_buffer.tme_recode_insn_opcode
1004: = (rw->tme_recode_rw_memory_signed
1005: ? TME_RECODE_OPCODE_EXTS
1006: : TME_RECODE_OPCODE_EXTZ);
1007: insn_buffer.tme_recode_insn_operand_src[0] = TME_RECODE_X86_REG_HOST_FREE_CALL;
1008: insn_buffer.tme_recode_insn_operand_src[1] = rw->tme_recode_rw_memory_size;
1009: insn_buffer.tme_recode_insn_operand_dst = insn_buffer.tme_recode_insn_operand_src[0];
1010: _tme_recode_x86_insn_ext(ic, &insn_buffer);
1011: }
1012:
1013: /* duplicate the read/write thunk: */
1014: rw_thunk = tme_dup(struct tme_recode_rw_thunk, rw_other->tme_recode_rw_thunk, 1);
1015:
1016: /* get any extension instruction from the thunk build memory, and
1017: then discard it: */
1018: tme_recode_x86_insns_start(ic, thunk_bytes_1);
1019: rw_thunk->tme_recode_x86_rw_thunk_extend = *((tme_uint32_t *) thunk_bytes_0);
1020: rw_thunk->tme_recode_x86_rw_thunk_extend_size = (thunk_bytes_1 - thunk_bytes_0);
1021: assert (rw_thunk->tme_recode_x86_rw_thunk_extend_size <= sizeof(tme_uint32_t));
1022: tme_recode_x86_insns_finish(ic, thunk_bytes_0);
1023:
1024: /* return the duplicated read/write thunk: */
1025: return (rw_thunk);
1026: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.