|
|
1.1 ! root 1: /* $Id: rc-x86-chain.c,v 1.3 2010/02/15 22:15:59 fredette Exp $ */ ! 2: ! 3: /* libtme/host/x86/rc-x86-chain.c - x86 host recode chain support: */ ! 4: ! 5: /* ! 6: * Copyright (c) 2009 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-chain.c,v 1.3 2010/02/15 22:15:59 fredette Exp $"); ! 37: ! 38: /* macros: */ ! 39: ! 40: /* the x86 register for the pointer to the token for the current ! 41: instruction TLB entry: */ ! 42: #define TME_RECODE_X86_REG_CHAIN_ITLB_CURRENT_TOKEN (TME_RECODE_X86_REG_TLB) ! 43: ! 44: /* the x86 register for the chain return address: */ ! 45: #define TME_RECODE_X86_REG_CHAIN_RETURN_ADDRESS (TME_RECODE_X86_REG_TLB) ! 46: ! 47: /* the x86 register for the chain return address stack pointer: */ ! 48: #define TME_RECODE_X86_REG_CHAIN_RAS_POINTER (TME_RECODE_X86_REG_TLB_SCRATCH) ! 49: ! 50: /* the x86 register for the fixup address: */ ! 51: #define TME_RECODE_X86_REG_CHAIN_FIXUP_ADDRESS (TME_RECODE_X86_REG_TLB_SCRATCH) ! 52: ! 53: /* the x86 register for the guest instructions source address: */ ! 54: /* NB: we use the a register to take advantage of the fact that there ! 55: is a special cmp $imm, %a form that will save a byte in each ! 56: instruction thunk: */ ! 57: #define TME_RECODE_X86_REG_CHAIN_GUEST_SRC (TME_RECODE_X86_REG_A) ! 58: ! 59: /* this gives the one or two opcode bytes for a cmp $imm32, ! 60: %TME_RECODE_X86_REG_CHAIN_GUEST_SRC: */ ! 61: #if TME_RECODE_X86_REG_CHAIN_GUEST_SRC != TME_RECODE_X86_REG_A ! 62: #error "TME_RECODE_X86_REG_CHAIN_GUEST_SRC changed" ! 63: #endif ! 64: #define TME_RECODE_X86_INSN_CMP_Iz_REG_CHAIN_GUEST_SRC \ ! 65: (TME_RECODE_X86_REX_B(TME_RECODE_SIZE_HOST, \ ! 66: TME_RECODE_X86_REG_CHAIN_GUEST_SRC) \ ! 67: + ((TME_RECODE_X86_OPCODE_BINOP_CMP \ ! 68: + TME_RECODE_X86_OPCODE_BINOP_Iz_A) \ ! 69: << (8 * (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32)))) ! 70: ! 71: /* this gives the one opcode byte for a movl $imm32, %TME_RECODE_X86_REG_TLB_SCRATCH: */ ! 72: #if TME_RECODE_X86_REG(TME_RECODE_X86_REG_TLB_SCRATCH) != TME_RECODE_X86_REG_TLB_SCRATCH ! 73: #error "TME_RECODE_X86_REG_TLB_SCRATCH changed" ! 74: #endif ! 75: #define TME_RECODE_X86_INSN_MOVL_IMM32_REG_TLB_SCRATCH \ ! 76: TME_RECODE_X86_OPCODE_MOV_Iv_Gv(TME_RECODE_X86_REG_TLB_SCRATCH) ! 77: ! 78: /* this gives the three opcode bytes for a lea disp32(%ip), ! 79: %TME_RECODE_X86_REG_TLB_SCRATCH: */ ! 80: #define TME_RECODE_X86_INSN_LEA_DISP32_IP_REG_TLB_SCRATCH \ ! 81: (TME_RECODE_X86_REX_R(TME_RECODE_SIZE_HOST, \ ! 82: TME_RECODE_X86_REG_TLB_SCRATCH) \ ! 83: + (TME_RECODE_X86_OPCODE_LEA \ ! 84: << 8) \ ! 85: + (TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_EA(TME_RECODE_X86_EA_BASE_IP),\ ! 86: TME_RECODE_X86_REG(TME_RECODE_X86_REG_TLB_SCRATCH))\ ! 87: << 16)) ! 88: ! 89: /* this gives the two opcode bytes for a movq $imm64, ! 90: %TME_RECODE_X86_REG_TLB_SCRATCH: */ ! 91: #define TME_RECODE_X86_INSN_MOVQ_IMM64_REG_TLB_SCRATCH \ ! 92: (TME_RECODE_X86_REX_R(TME_RECODE_SIZE_HOST, \ ! 93: TME_RECODE_X86_REG_TLB_SCRATCH) \ ! 94: + (TME_RECODE_X86_OPCODE_MOV_Iv_Gv(TME_RECODE_X86_REG_TLB_SCRATCH)\ ! 95: << 8)) ! 96: ! 97: /* this gives the three opcode bytes for a testb $imm8, addr32: */ ! 98: #define TME_RECODE_X86_INSN_TESTB_IMM8_ADDR32 \ ! 99: (TME_RECODE_X86_OPCODE_GRP3_Eb \ ! 100: + (TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_EA(TME_RECODE_X86_EA_BASE_SIB),\ ! 101: TME_RECODE_X86_OPCODE_GRP3_TEST)\ ! 102: << 8) \ ! 103: + (TME_RECODE_X86_SIB(TME_RECODE_X86_SIB_BASE_NONE, \ ! 104: TME_RECODE_X86_SIB_INDEX_NONE, \ ! 105: 1) \ ! 106: << 16)) ! 107: ! 108: /* this gives the two opcode bytes for a testb $imm8, disp32(%ip): */ ! 109: #define TME_RECODE_X86_INSN_TESTB_IMM8_DISP32_IP \ ! 110: (TME_RECODE_X86_OPCODE_GRP3_Eb \ ! 111: + (TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_EA(TME_RECODE_X86_EA_BASE_IP),\ ! 112: TME_RECODE_X86_OPCODE_GRP3_TEST)\ ! 113: << 8)) ! 114: ! 115: /* this gives the two opcode bytes for a testb $imm8, ! 116: disp32(%TME_RECODE_X86_REG_CHAIN_GUEST_SRC): */ ! 117: #if TME_RECODE_X86_REG(TME_RECODE_X86_REG_CHAIN_GUEST_SRC) != TME_RECODE_X86_REG_CHAIN_GUEST_SRC ! 118: #error "TME_RECODE_X86_REG_CHAIN_GUEST_SRC changed" ! 119: #endif ! 120: #define TME_RECODE_X86_INSN_TESTB_IMM8_DISP32_REG_CHAIN_GUEST_SRC \ ! 121: (TME_RECODE_X86_OPCODE_GRP3_Eb \ ! 122: + (TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_EA_DISP32(TME_RECODE_X86_REG_CHAIN_GUEST_SRC),\ ! 123: TME_RECODE_X86_OPCODE_GRP3_TEST)\ ! 124: << 8)) ! 125: ! 126: /* this is the size of a jcc epilogue instruction: */ ! 127: #define TME_RECODE_X86_CHAIN_IN_SIZE_JCC_EPILOGUE \ ! 128: (6 /* jcc epilogue */) ! 129: ! 130: /* this is the size of the chain in far instructions on an ia32 host: */ ! 131: #define TME_RECODE_IA32_CHAIN_IN_SIZE_FAR \ ! 132: (5 /* cmpl $imm32, %eax */ \ ! 133: + TME_RECODE_X86_CHAIN_IN_SIZE_JCC_EPILOGUE \ ! 134: + 7 /* testb $imm8, addr32 */ \ ! 135: + TME_RECODE_X86_CHAIN_IN_SIZE_JCC_EPILOGUE) ! 136: ! 137: /* the size of cmpq $imm32, %TME_RECODE_X86_REG_CHAIN_GUEST_SRC ; jnz ! 138: epilogue */ ! 139: #define TME_RECODE_X86_64_CHAIN_IN_SIZE_CMPQ_IMM32_JNZ \ ! 140: (6 /* cmpq $imm32, %rax */ \ ! 141: + TME_RECODE_X86_CHAIN_IN_SIZE_JCC_EPILOGUE) ! 142: ! 143: /* the size of cmpq %reg, %reg ; jnz epilogue */ ! 144: #define TME_RECODE_X86_64_CHAIN_IN_SIZE_CMP_JNZ \ ! 145: (3 /* cmpq %reg, %reg */ \ ! 146: + TME_RECODE_X86_CHAIN_IN_SIZE_JCC_EPILOGUE) ! 147: ! 148: /* the size of movl $imm32, %reg ; cmpq %reg, %reg ; jnz epilogue */ ! 149: #define TME_RECODE_X86_64_CHAIN_IN_SIZE_MOVL_CMP_JNZ \ ! 150: (5 /* movl $imm32, %reg */ \ ! 151: + TME_RECODE_X86_64_CHAIN_IN_SIZE_CMP_JNZ) ! 152: ! 153: /* the size of lea disp32(%ip), %reg ; cmpq %reg, %reg ; jnz epilogue */ ! 154: #define TME_RECODE_X86_64_CHAIN_IN_SIZE_LEA_IP_CMP_JNZ \ ! 155: (7 /* leaq disp32(%ip), %reg */ \ ! 156: + TME_RECODE_X86_64_CHAIN_IN_SIZE_CMP_JNZ) ! 157: ! 158: /* the size of movq $imm64, %reg ; cmpq %reg, %reg ; jnz epilogue */ ! 159: #define TME_RECODE_X86_64_CHAIN_IN_SIZE_MOVQ_CMP_JNZ \ ! 160: (10 /* movq $imm64, %reg */ \ ! 161: + TME_RECODE_X86_64_CHAIN_IN_SIZE_CMP_JNZ) ! 162: ! 163: /* the size of testb $imm8, addr32 ; jz epilogue */ ! 164: #define TME_RECODE_X86_64_CHAIN_IN_SIZE_TESTB_ADDR32_JZ \ ! 165: (8 /* testb $imm8, addr32 */ \ ! 166: + TME_RECODE_X86_CHAIN_IN_SIZE_JCC_EPILOGUE) ! 167: ! 168: /* the size of testb $imm8, base32(%reg) ; jz epilogue */ ! 169: #define TME_RECODE_X86_64_CHAIN_IN_SIZE_TESTB_BASE_JZ \ ! 170: (7 /* testb $imm8, addr32 */ \ ! 171: + TME_RECODE_X86_CHAIN_IN_SIZE_JCC_EPILOGUE) ! 172: ! 173: /* the size of movq $imm64, %reg ; testb $imm32, (%reg) ; jz epilogue */ ! 174: #define TME_RECODE_X86_64_CHAIN_IN_SIZE_MOVQ_TESTB_JZ \ ! 175: (10 /* movq $imm64, %reg */ \ ! 176: + 3 /* testb $imm32, (%reg) */ \ ! 177: + TME_RECODE_X86_CHAIN_IN_SIZE_JCC_EPILOGUE) ! 178: ! 179: /* this gives a chain subs: */ ! 180: #if (TME_RECODE_CHAIN_INFO_CONDITIONAL | TME_RECODE_CHAIN_INFO_FAR) != 3 ! 181: #error "TME_RECODE_CHAIN_INFO_ values changed" ! 182: #endif ! 183: #if (TME_RECODE_CHAIN_INFO_JUMP != 4 || TME_RECODE_CHAIN_INFO_RETURN != 8) ! 184: #error "TME_RECODE_CHAIN_INFO_ values changed" ! 185: #endif ! 186: #define TME_RECODE_X86_CHAIN_SUBS(chain_thunk, chain_info)\ ! 187: ((chain_thunk)->tme_recode_x86_chain_thunk_subs \ ! 188: [(chain_info) \ ! 189: & (TME_RECODE_CHAIN_INFO_CONDITIONAL \ ! 190: | TME_RECODE_CHAIN_INFO_FAR \ ! 191: | TME_RECODE_CHAIN_INFO_JUMP \ ! 192: | TME_RECODE_CHAIN_INFO_RETURN)]) ! 193: ! 194: /* these give a chain fixup target for the regular target or alternate ! 195: target: */ ! 196: #if TME_RECODE_CHAIN_INFO_FAR != 2 || TME_RECODE_CHAIN_INFO_ALTERNATE_FAR == 0 ! 197: #error "TME_RECODE_CHAIN_INFO_ values changed" ! 198: #endif ! 199: #define TME_RECODE_X86_CHAIN_FIXUP_TARGET(ic, chain_info) \ ! 200: ((ic)->tme_recode_x86_ic_chain_fixup_target \ ! 201: [((chain_info) & TME_RECODE_CHAIN_INFO_FAR) \ ! 202: + (((chain_info) / TME_RECODE_CHAIN_INFO_CALL) & 1)]) ! 203: #define TME_RECODE_X86_CHAIN_FIXUP_TARGET_ALTERNATE(ic, chain_info) \ ! 204: ((ic)->tme_recode_x86_ic_chain_fixup_target \ ! 205: [4 \ ! 206: + (((chain_info) / TME_RECODE_CHAIN_INFO_ALTERNATE_FAR) & 1)]) ! 207: ! 208: /* on an ia32 host, an entry on the return address stack is the ! 209: absolute address of either an instructions thunk or the chain ! 210: epilogue. on an x86-64 host, an entry on the return address stack ! 211: is the instructions thunk offset of either an instructions thunk or ! 212: the chain epilogue: */ ! 213: #if TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32 ! 214: #define TME_RECODE_X86_CHAIN_RETURN_ADDRESS(ic, insns_thunk)\ ! 215: ((tme_recode_ras_entry_t) (insns_thunk)) ! 216: #else /* TME_RECODE_SIZE_HOST == TME_RECODE_SIZE_32 */ ! 217: #define TME_RECODE_X86_CHAIN_RETURN_ADDRESS(ic, insns_thunk)\ ! 218: tme_recode_thunk_off_to_pointer(ic, \ ! 219: insns_thunk, \ ! 220: tme_recode_ras_entry_t) ! 221: #endif /* TME_RECODE_SIZE_HOST == TME_RECODE_SIZE_32 */ ! 222: ! 223: /* this emits instructions for a chain epilogue: */ ! 224: static void ! 225: _tme_recode_x86_chain_epilogue(struct tme_recode_ic *ic) ! 226: { ! 227: tme_uint8_t *thunk_bytes; ! 228: ! 229: /* start more instructions: */ ! 230: tme_recode_x86_insns_start(ic, thunk_bytes); ! 231: ! 232: /* set the instructions thunk offset of the chain epilogue: */ ! 233: ic->tme_recode_x86_ic_chain_epilogue = tme_recode_build_to_thunk_off(ic, thunk_bytes); ! 234: ! 235: /* remove the recode carry flag stack word: */ ! 236: thunk_bytes ! 237: = _tme_recode_x86_emit_adjust_sp(thunk_bytes, ! 238: TME_BIT(TME_RECODE_SIZE_HOST ! 239: - TME_RECODE_SIZE_8)); ! 240: ! 241: /* pop all callee-saved registers: */ ! 242: if (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) { ! 243: _tme_recode_x86_emit_reg_pop(thunk_bytes, TME_RECODE_X86_REG_N(15)); ! 244: _tme_recode_x86_emit_reg_pop(thunk_bytes, TME_RECODE_X86_REG_N(14)); ! 245: _tme_recode_x86_emit_reg_pop(thunk_bytes, TME_RECODE_X86_REG_N(13)); ! 246: _tme_recode_x86_emit_reg_pop(thunk_bytes, TME_RECODE_X86_REG_N(12)); ! 247: } ! 248: else { ! 249: _tme_recode_x86_emit_reg_pop(thunk_bytes, TME_RECODE_X86_REG_DI); ! 250: _tme_recode_x86_emit_reg_pop(thunk_bytes, TME_RECODE_X86_REG_SI); ! 251: } ! 252: _tme_recode_x86_emit_reg_pop(thunk_bytes, TME_RECODE_X86_REG_B); ! 253: _tme_recode_x86_emit_reg_pop(thunk_bytes, TME_RECODE_X86_REG_BP); ! 254: ! 255: /* emit the return: */ ! 256: thunk_bytes[0] = TME_RECODE_X86_OPCODE_RET; ! 257: thunk_bytes++; ! 258: ! 259: /* finish these instructions: */ ! 260: tme_recode_x86_insns_finish(ic, thunk_bytes); ! 261: ! 262: /* finish the chain epilogue: */ ! 263: tme_recode_host_thunk_finish(ic); ! 264: } ! 265: ! 266: /* this emits instructions to advance the fixup address register ! 267: past a chain conditional six-byte jcc instruction: */ ! 268: static tme_uint8_t * ! 269: _tme_recode_x86_chain_fixup_alternate(tme_uint8_t *thunk_bytes) ! 270: { ! 271: ! 272: /* NB: this is always a 32-bit instruction, even on an x86-64 host, ! 273: because we end up converting the fixup address into an ! 274: instructions thunk offset, which is only 32 bits: */ ! 275: assert (sizeof(tme_recode_thunk_off_t) == sizeof(tme_int32_t)); ! 276: thunk_bytes[0] = TME_RECODE_X86_OPCODE_GRP1_Ib_Ev; ! 277: thunk_bytes[1] ! 278: = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(TME_RECODE_X86_REG_CHAIN_FIXUP_ADDRESS), ! 279: TME_RECODE_X86_OPCODE_GRP1_BINOP(TME_RECODE_X86_OPCODE_BINOP_ADD)); ! 280: thunk_bytes[2] = 2 + sizeof(tme_int32_t); ! 281: thunk_bytes += 3; ! 282: return (thunk_bytes); ! 283: } ! 284: ! 285: /* this emits instructions to make a constant for the third argument ! 286: to the chain fixup function: */ ! 287: static tme_uint8_t * ! 288: _tme_recode_x86_chain_fixup_arg2(tme_uint8_t *thunk_bytes, ! 289: tme_uint32_t chain_info) ! 290: { ! 291: ! 292: /* if this is an ia32 host: */ ! 293: if (TME_RECODE_SIZE_HOST == TME_RECODE_SIZE_32) { ! 294: ! 295: /* push the argument: */ ! 296: assert (chain_info < 0x80); ! 297: thunk_bytes[0] = TME_RECODE_X86_OPCODE_PUSH_Ib; ! 298: thunk_bytes[1] = chain_info; ! 299: thunk_bytes += 2; ! 300: } ! 301: ! 302: /* otherwise, this is an x86-64 host: */ ! 303: else { ! 304: ! 305: /* load the third argument register: */ ! 306: thunk_bytes[0] = TME_RECODE_X86_OPCODE_MOV_Iv_Gv(TME_RECODE_X86_REG_D); ! 307: *((tme_uint32_t *) &thunk_bytes[1]) = chain_info; ! 308: thunk_bytes += 1 + sizeof(tme_uint32_t); ! 309: } ! 310: ! 311: return (thunk_bytes); ! 312: } ! 313: ! 314: /* this emits instructions for the chain fixup targets for chain jump ! 315: far, chain call far, and chain call near: */ ! 316: static void ! 317: _tme_recode_x86_chain_fixup_target(struct tme_recode_ic *ic, ! 318: tme_uint32_t chain_info, ! 319: const tme_uint8_t *thunk_bytes_fixup_call) ! 320: { ! 321: tme_uint8_t *thunk_bytes; ! 322: ! 323: /* start more instructions: */ ! 324: tme_recode_x86_insns_start(ic, thunk_bytes); ! 325: ! 326: /* set next fixup target: */ ! 327: TME_RECODE_X86_CHAIN_FIXUP_TARGET(ic, ! 328: chain_info) ! 329: = tme_recode_build_to_thunk_off(ic, thunk_bytes); ! 330: ! 331: /* make the third argument for the chain fixup function: */ ! 332: thunk_bytes = _tme_recode_x86_chain_fixup_arg2(thunk_bytes, chain_info); ! 333: ! 334: /* jmp to the common chain fixup call point: */ ! 335: thunk_bytes ! 336: = _tme_recode_x86_emit_jmp(thunk_bytes, ! 337: TME_RECODE_X86_OPCODE_JMP_RELz, ! 338: thunk_bytes_fixup_call); ! 339: ! 340: /* finish these instructions: */ ! 341: tme_recode_x86_insns_finish(ic, thunk_bytes); ! 342: } ! 343: ! 344: /* this makes the chain fixup targets for a new IC: */ ! 345: static void ! 346: _tme_recode_x86_chain_fixup_targets(struct tme_recode_ic *ic) ! 347: { ! 348: tme_uint8_t *thunk_bytes; ! 349: const tme_uint8_t *thunk_bytes_fixup_call; ! 350: unsigned long thunk_address0; ! 351: unsigned int stack_adjust; ! 352: ! 353: /* start more instructions: */ ! 354: tme_recode_x86_insns_start(ic, thunk_bytes); ! 355: ! 356: /* the first fixup target is for chain jump alternate near: */ ! 357: TME_RECODE_X86_CHAIN_FIXUP_TARGET_ALTERNATE(ic, ! 358: (TME_RECODE_CHAIN_INFO_JUMP ! 359: + TME_RECODE_CHAIN_INFO_ALTERNATE_NEAR)) ! 360: = tme_recode_build_to_thunk_off(ic, thunk_bytes); ! 361: ! 362: /* advance the fixup address register past the chain jump ! 363: conditional six-byte jcc instruction: */ ! 364: thunk_bytes = _tme_recode_x86_chain_fixup_alternate(thunk_bytes); ! 365: ! 366: /* the next fixup target is for chain jump near: */ ! 367: /* NB: this fixup target is also for chain return alternate near, ! 368: which is treated like a chain jump near: */ ! 369: TME_RECODE_X86_CHAIN_FIXUP_TARGET(ic, ! 370: (TME_RECODE_CHAIN_INFO_JUMP ! 371: + TME_RECODE_CHAIN_INFO_NEAR)) ! 372: = tme_recode_build_to_thunk_off(ic, thunk_bytes); ! 373: ! 374: /* make the third argument for the chain fixup function: */ ! 375: thunk_bytes ! 376: = _tme_recode_x86_chain_fixup_arg2(thunk_bytes, ! 377: TME_RECODE_CHAIN_INFO_NEAR); ! 378: ! 379: /* this is the common chain fixup call point: */ ! 380: thunk_bytes_fixup_call = thunk_bytes; ! 381: ! 382: /* get the near address of the thunk at offset zero: */ ! 383: thunk_address0 = tme_recode_thunk_off_to_pointer(ic, 0, char *) - (char *) 0; ! 384: ! 385: /* convert the fixup address (which has been already truncated to 32 ! 386: bits) into the instructions thunk offset: */ ! 387: /* NB: this is always a 32-bit instruction, because a ! 388: tme_recode_thunk_off_t is a tme_int32_t: */ ! 389: assert (sizeof(tme_recode_thunk_off_t) == sizeof(tme_int32_t)); ! 390: thunk_bytes[0] = TME_RECODE_X86_OPCODE_GRP1_Iz_Ev; ! 391: thunk_bytes[1] = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(TME_RECODE_X86_REG_CHAIN_FIXUP_ADDRESS), ! 392: TME_RECODE_X86_OPCODE_GRP1_BINOP(TME_RECODE_X86_OPCODE_BINOP_SUB)); ! 393: *((tme_uint32_t *) &thunk_bytes[2]) = thunk_address0; ! 394: thunk_bytes += 2 + sizeof(tme_uint32_t); ! 395: ! 396: /* if this is an ia32 host: */ ! 397: if (TME_RECODE_SIZE_HOST == TME_RECODE_SIZE_32) { ! 398: ! 399: /* push the arguments for the chain fixup function: */ ! 400: _tme_recode_x86_emit_reg_push(thunk_bytes, TME_RECODE_X86_REG_CHAIN_FIXUP_ADDRESS); ! 401: _tme_recode_x86_emit_reg_push(thunk_bytes, TME_RECODE_X86_REG_IC); ! 402: ! 403: /* we will need to remove the arguments from the stack: */ ! 404: stack_adjust = sizeof(struct tme_ic *) + sizeof(tme_recode_thunk_off_t) + sizeof(tme_uint32_t); ! 405: } ! 406: ! 407: /* otherwise, this is an x86-64 host: */ ! 408: else { ! 409: ! 410: /* copy the arguments for the guest jump chain function: */ ! 411: _tme_recode_x86_emit_reg_copy(thunk_bytes, TME_RECODE_X86_REG_IC, TME_RECODE_X86_REG_DI); ! 412: _tme_recode_x86_emit_reg_copy(thunk_bytes, TME_RECODE_X86_REG_CHAIN_FIXUP_ADDRESS, TME_RECODE_X86_REG_SI); ! 413: ! 414: /* we don't need to adjust the stack. NB that the instructions ! 415: thunk, where the stack pointer is 16-byte aligned, jumped to ! 416: us, so we don't have to align it for the x86-64 ABI: */ ! 417: stack_adjust = 0; ! 418: } ! 419: ! 420: /* call the chain fixup function: */ ! 421: tme_recode_x86_insns_finish(ic, thunk_bytes); ! 422: _tme_recode_x86_emit_transfer_func(ic, ! 423: TME_RECODE_X86_OPCODE_CALL_RELz, ! 424: ((void (*) _TME_P((void))) ! 425: ic->tme_recode_ic_chain_fixup)); ! 426: tme_recode_x86_insns_start(ic, thunk_bytes); ! 427: ! 428: /* do any stack adjust: */ ! 429: if (stack_adjust) { ! 430: thunk_bytes = _tme_recode_x86_emit_adjust_sp(thunk_bytes, stack_adjust); ! 431: } ! 432: ! 433: /* jmp to the chain epilogue: */ ! 434: thunk_bytes ! 435: = _tme_recode_x86_emit_jmp(thunk_bytes, ! 436: TME_RECODE_X86_OPCODE_JMP_RELz, ! 437: tme_recode_thunk_off_to_pointer(ic, ! 438: ic->tme_recode_x86_ic_chain_epilogue, ! 439: tme_uint8_t *)); ! 440: ! 441: /* the next fixup target is for chain jump alternate far: */ ! 442: TME_RECODE_X86_CHAIN_FIXUP_TARGET_ALTERNATE(ic, ! 443: (TME_RECODE_CHAIN_INFO_JUMP ! 444: + TME_RECODE_CHAIN_INFO_ALTERNATE_FAR)) ! 445: = tme_recode_build_to_thunk_off(ic, thunk_bytes); ! 446: ! 447: /* advance the fixup address register past the chain jump ! 448: conditional six-byte jcc instruction: */ ! 449: thunk_bytes = _tme_recode_x86_chain_fixup_alternate(thunk_bytes); ! 450: ! 451: /* finish these instructions: */ ! 452: tme_recode_x86_insns_finish(ic, thunk_bytes); ! 453: ! 454: /* the next fixup target is for chain jump far: */ ! 455: _tme_recode_x86_chain_fixup_target(ic, ! 456: TME_RECODE_CHAIN_INFO_FAR, ! 457: thunk_bytes_fixup_call); ! 458: ! 459: /* the next fixup target is for chain call far: */ ! 460: _tme_recode_x86_chain_fixup_target(ic, ! 461: (TME_RECODE_CHAIN_INFO_CALL ! 462: + TME_RECODE_CHAIN_INFO_FAR), ! 463: thunk_bytes_fixup_call); ! 464: ! 465: /* the next fixup target is for chain call near: */ ! 466: _tme_recode_x86_chain_fixup_target(ic, ! 467: (TME_RECODE_CHAIN_INFO_CALL ! 468: + TME_RECODE_CHAIN_INFO_NEAR), ! 469: thunk_bytes_fixup_call); ! 470: ! 471: /* finish the fixup targets thunk: */ ! 472: tme_recode_host_thunk_finish(ic); ! 473: } ! 474: ! 475: /* this emits a jmp to a chain subs: */ ! 476: static tme_uint8_t * ! 477: _tme_recode_x86_chain_subs_jmp(struct tme_recode_ic *ic, ! 478: tme_uint32_t opcode, ! 479: struct tme_recode_chain_thunk *chain_thunk, ! 480: tme_uint32_t chain_info) ! 481: { ! 482: tme_uint8_t *thunk_bytes; ! 483: tme_uint8_t *thunk_bytes_jc; ! 484: tme_recode_thunk_off_t chain_subs; ! 485: tme_uint8_t *thunk_bytes_target; ! 486: ! 487: /* if this is a conditional jmp: */ ! 488: if (opcode != TME_RECODE_X86_OPCODE_JMP_RELb ! 489: && opcode != TME_RECODE_X86_OPCODE_JMP_RELz) { ! 490: ! 491: /* define CF with the recode jump flag. NB that the recode flags ! 492: are now above the return address to the instructions thunk: */ ! 493: _tme_recode_x86_conds_testc(ic, TME_BIT(TME_RECODE_SIZE_HOST - TME_RECODE_SIZE_8) + TME_RECODE_FLAG_JUMP); ! 494: } ! 495: ! 496: /* start more instructions: */ ! 497: tme_recode_x86_insns_start(ic, thunk_bytes); ! 498: ! 499: /* emit the jmp: */ ! 500: chain_subs = TME_RECODE_X86_CHAIN_SUBS(chain_thunk, chain_info); ! 501: if (chain_subs == 0) { ! 502: thunk_bytes_jc = thunk_bytes; ! 503: thunk_bytes_target = (tme_uint8_t *) NULL; ! 504: } ! 505: else { ! 506: thunk_bytes_jc = (tme_uint8_t *) NULL; ! 507: thunk_bytes_target = tme_recode_thunk_off_to_pointer(ic, chain_subs, tme_uint8_t *); ! 508: } ! 509: thunk_bytes = _tme_recode_x86_emit_jmp(thunk_bytes, opcode, thunk_bytes_target); ! 510: ! 511: /* finish these instructions: */ ! 512: tme_recode_x86_insns_finish(ic, thunk_bytes); ! 513: ! 514: return (thunk_bytes_jc); ! 515: } ! 516: ! 517: /* this emits instructions to add a sign-extended byte to the chain ! 518: return address stack pointer, mask it, and then store it: */ ! 519: static void ! 520: _tme_recode_x86_chain_ras_pointer_update(struct tme_recode_ic *ic, ! 521: tme_int8_t addend) ! 522: { ! 523: tme_uint8_t *thunk_bytes; ! 524: tme_uint8_t ras_size; ! 525: ! 526: /* start these instructions: */ ! 527: tme_recode_x86_insns_start(ic, thunk_bytes); ! 528: ! 529: /* emit the add $imm8: */ ! 530: thunk_bytes[0] = TME_RECODE_X86_OPCODE_GRP1_Ib_Ev; ! 531: thunk_bytes[1] = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(TME_RECODE_X86_REG_CHAIN_RAS_POINTER), ! 532: TME_RECODE_X86_OPCODE_GRP1_BINOP(TME_RECODE_X86_OPCODE_BINOP_ADD)); ! 533: thunk_bytes[2] = addend; ! 534: thunk_bytes += 3; ! 535: ! 536: /* emit the and $imm8: */ ! 537: thunk_bytes[0] = TME_RECODE_X86_OPCODE_GRP1_Ib_Ev; ! 538: thunk_bytes[1] = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(TME_RECODE_X86_REG_CHAIN_RAS_POINTER), ! 539: TME_RECODE_X86_OPCODE_GRP1_BINOP(TME_RECODE_X86_OPCODE_BINOP_AND)); ! 540: ras_size = ic->tme_recode_ic_chain_ras_size; ! 541: assert (ras_size > 0 && (ras_size & (ras_size - 1)) == 0); ! 542: thunk_bytes[2] = ras_size - 1; ! 543: thunk_bytes += 3; ! 544: ! 545: /* store the chain return address stack pointer: */ ! 546: thunk_bytes[0] = (TME_RECODE_X86_OPCODE_BINOP_MOV + TME_RECODE_X86_OPCODE_BINOP_Gv_Ev); ! 547: thunk_bytes = _tme_recode_x86_emit_ic_modrm(thunk_bytes + 1, ! 548: ic->tme_recode_ic_chain_ras_pointer_offset, ! 549: TME_RECODE_X86_REG_CHAIN_RAS_POINTER); ! 550: ! 551: /* finish these instructions: */ ! 552: tme_recode_x86_insns_finish(ic, thunk_bytes); ! 553: } ! 554: ! 555: /* this emits an instruction to load or store an entry on the chain ! 556: return address stack: */ ! 557: static void ! 558: _tme_recode_x86_chain_ras_ls(struct tme_recode_ic *ic, ! 559: int store) ! 560: { ! 561: tme_uint8_t *thunk_bytes; ! 562: tme_int32_t ras_offset; ! 563: ! 564: /* start more instructions: */ ! 565: tme_recode_x86_insns_start(ic, thunk_bytes); ! 566: ! 567: /* emit the movl: */ ! 568: thunk_bytes[0] ! 569: = (TME_RECODE_X86_OPCODE_BINOP_MOV ! 570: + (store ! 571: ? TME_RECODE_X86_OPCODE_BINOP_Gv_Ev ! 572: : TME_RECODE_X86_OPCODE_BINOP_Ev_Gv)); ! 573: ras_offset = ic->tme_recode_ic_chain_ras_offset; ! 574: thunk_bytes[1] ! 575: = TME_RECODE_X86_MOD_OPREG_RM((ras_offset < 0x80 ! 576: ? TME_RECODE_X86_MOD_RM_EA_DISP8(TME_RECODE_X86_EA_BASE_SIB) ! 577: : TME_RECODE_X86_MOD_RM_EA_DISP32(TME_RECODE_X86_EA_BASE_SIB)), ! 578: TME_RECODE_X86_REG(TME_RECODE_X86_REG_CHAIN_RETURN_ADDRESS)); ! 579: thunk_bytes[2] ! 580: = TME_RECODE_X86_SIB(TME_RECODE_X86_REG_IC, ! 581: TME_RECODE_X86_REG_CHAIN_RAS_POINTER, ! 582: sizeof(tme_recode_ras_entry_t)); ! 583: *((tme_uint32_t *) (thunk_bytes + 3)) = ras_offset; ! 584: thunk_bytes += 3 + (ras_offset < 0x80 ? sizeof(tme_uint8_t) : sizeof(tme_uint32_t)); ! 585: ! 586: /* finish these instructions: */ ! 587: tme_recode_x86_insns_finish(ic, thunk_bytes); ! 588: } ! 589: ! 590: /* this emits instructions to push the chain return address stack: */ ! 591: static void ! 592: _tme_recode_x86_chain_ras_push(struct tme_recode_ic *ic) ! 593: { ! 594: tme_uint8_t *thunk_bytes; ! 595: ! 596: /* start more instructions: */ ! 597: tme_recode_x86_insns_start(ic, thunk_bytes); ! 598: ! 599: /* load the return address stack pointer: */ ! 600: thunk_bytes[0] = (TME_RECODE_X86_OPCODE_BINOP_MOV + TME_RECODE_X86_OPCODE_BINOP_Ev_Gv); ! 601: thunk_bytes = _tme_recode_x86_emit_ic_modrm(thunk_bytes + 1, ! 602: ic->tme_recode_ic_chain_ras_pointer_offset, ! 603: TME_RECODE_X86_REG_CHAIN_RAS_POINTER); ! 604: ! 605: /* finish these instructions: */ ! 606: tme_recode_x86_insns_finish(ic, thunk_bytes); ! 607: ! 608: /* subtract one from the return address stack pointer, and store it: */ ! 609: _tme_recode_x86_chain_ras_pointer_update(ic, -1); ! 610: ! 611: /* store the return address: */ ! 612: _tme_recode_x86_chain_ras_ls(ic, TRUE); ! 613: } ! 614: ! 615: /* this emits instructions to load or store the pointer to the token ! 616: for the current recode instruction TLB entry: */ ! 617: static void ! 618: _tme_recode_x86_chain_itlb_current_token_ls(struct tme_recode_ic *ic, ! 619: int store) ! 620: { ! 621: tme_uint8_t *thunk_bytes; ! 622: ! 623: /* start more instructions: */ ! 624: tme_recode_x86_insns_start(ic, thunk_bytes); ! 625: ! 626: /* load or store the pointer to the token for the current recode ! 627: instruction TLB entry: */ ! 628: if (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) { ! 629: *(thunk_bytes++) ! 630: = TME_RECODE_X86_REX_B(TME_RECODE_SIZE_HOST, ! 631: TME_RECODE_X86_REG_CHAIN_ITLB_CURRENT_TOKEN); ! 632: } ! 633: thunk_bytes[0] ! 634: = (TME_RECODE_X86_OPCODE_BINOP_MOV ! 635: + (store ! 636: ? TME_RECODE_X86_OPCODE_BINOP_Gv_Ev ! 637: : TME_RECODE_X86_OPCODE_BINOP_Ev_Gv)); ! 638: thunk_bytes = _tme_recode_x86_emit_ic_modrm(thunk_bytes + 1, ! 639: ic->tme_recode_ic_itlb_current_token_offset, ! 640: TME_RECODE_X86_REG_CHAIN_ITLB_CURRENT_TOKEN); ! 641: ! 642: /* finish these instructions: */ ! 643: tme_recode_x86_insns_finish(ic, thunk_bytes); ! 644: } ! 645: #define _tme_recode_x86_chain_itlb_current_token_load(ic) _tme_recode_x86_chain_itlb_current_token_ls(ic, FALSE) ! 646: ! 647: /* this stores the pointer to the current recode instruction TLB entry: */ ! 648: static void ! 649: _tme_recode_x86_chain_itlb_current_store(struct tme_recode_ic *ic, ! 650: const struct tme_recode_x86_tlb_type *x86_tlb_type) ! 651: { ! 652: tme_uint8_t *thunk_bytes; ! 653: ! 654: /* start more instructions: */ ! 655: tme_recode_x86_insns_start(ic, thunk_bytes); ! 656: ! 657: /* convert the pointer to the recode instruction TLB entry in ! 658: TME_RECODE_X86_REG_TLB into a pointer to the token for that entry ! 659: in TME_RECODE_X86_REG_CHAIN_ITLB_CURRENT_TOKEN: */ ! 660: #if TME_RECODE_X86_REG_CHAIN_ITLB_CURRENT_TOKEN != TME_RECODE_X86_REG_TLB ! 661: #error "TME_RECODE_X86_REG_CHAIN_ITLB_CURRENT_TOKEN changed" ! 662: #endif ! 663: thunk_bytes[0] = TME_RECODE_X86_OPCODE_GRP1_Ib_Ev; ! 664: thunk_bytes[1] = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(TME_RECODE_X86_REG_TLB), ! 665: TME_RECODE_X86_OPCODE_GRP1_BINOP(TME_RECODE_X86_OPCODE_BINOP_ADD)); ! 666: assert (x86_tlb_type->tme_recode_tlb_type.tme_recode_tlb_type_offset_token < 0x80); ! 667: thunk_bytes[2] = x86_tlb_type->tme_recode_tlb_type.tme_recode_tlb_type_offset_token; ! 668: thunk_bytes += 3; ! 669: ! 670: /* finish these instructions: */ ! 671: tme_recode_x86_insns_finish(ic, thunk_bytes); ! 672: ! 673: /* store the pointer to the token for the current recode instruction ! 674: TLB entry: */ ! 675: _tme_recode_x86_chain_itlb_current_token_ls(ic, TRUE); ! 676: } ! 677: ! 678: /* this emits instructions for the call and jump chain subs, or for ! 679: the return chain subs: */ ! 680: static void ! 681: _tme_recode_x86_chain_subs(struct tme_recode_ic *ic, ! 682: const struct tme_recode_chain *chain, ! 683: struct tme_recode_chain_thunk *chain_thunk, ! 684: tme_uint32_t chain_info_call_or_return) ! 685: { ! 686: tme_uint8_t *thunk_bytes_jmp_to_jump_far_unconditional; ! 687: tme_uint8_t *thunk_bytes; ! 688: struct tme_recode_x86_tlb_type x86_tlb_type; ! 689: union tme_recode_reginfo reginfo; ! 690: unsigned int rex; ! 691: unsigned int reg_x86_address; ! 692: tme_uint8_t *thunk_bytes_jcc_chain_counter; ! 693: const tme_uint8_t *thunk_bytes_jmp_to_epilogue; ! 694: ! 695: /* the first chain subs is for a chain call far conditional or chain ! 696: return far conditional: */ ! 697: TME_RECODE_X86_CHAIN_SUBS(chain_thunk, ! 698: (chain_info_call_or_return ! 699: + TME_RECODE_CHAIN_INFO_FAR ! 700: + TME_RECODE_CHAIN_INFO_CONDITIONAL)) ! 701: = tme_recode_build_to_thunk_off(ic, ic->tme_recode_ic_thunk_build_next); ! 702: ! 703: /* if the recode jump flag is false, jmp to the chain subs for a ! 704: chain jump far unconditional: */ ! 705: thunk_bytes_jmp_to_jump_far_unconditional ! 706: = _tme_recode_x86_chain_subs_jmp(ic, ! 707: (TME_RECODE_X86_OPCODE_ESC_0F ! 708: + (TME_RECODE_X86_OPCODE0F_JCC(TME_RECODE_X86_COND_NOT ! 709: | TME_RECODE_X86_COND_C) ! 710: << 8)), ! 711: chain_thunk, ! 712: (TME_RECODE_CHAIN_INFO_JUMP ! 713: + TME_RECODE_CHAIN_INFO_FAR ! 714: + TME_RECODE_CHAIN_INFO_UNCONDITIONAL)); ! 715: ! 716: /* if this the chain subs for a chain return far conditional: */ ! 717: if (chain_info_call_or_return == TME_RECODE_CHAIN_INFO_RETURN) { ! 718: ! 719: /* throw away the return address to the instructions thunk: */ ! 720: tme_recode_x86_insns_start(ic, thunk_bytes); ! 721: thunk_bytes = _tme_recode_x86_emit_adjust_sp(thunk_bytes, ! 722: TME_BIT(TME_RECODE_SIZE_HOST - TME_RECODE_SIZE_8)); ! 723: tme_recode_x86_insns_finish(ic, thunk_bytes); ! 724: } ! 725: ! 726: /* the next chain subs is for a chain call far unconditional or a ! 727: chain return far unconditional: */ ! 728: TME_RECODE_X86_CHAIN_SUBS(chain_thunk, ! 729: (chain_info_call_or_return ! 730: + TME_RECODE_CHAIN_INFO_FAR ! 731: + TME_RECODE_CHAIN_INFO_UNCONDITIONAL)) ! 732: = tme_recode_build_to_thunk_off(ic, ic->tme_recode_ic_thunk_build_next); ! 733: ! 734: /* if this is the chain subs for a chain call far unconditional: */ ! 735: if (chain_info_call_or_return == TME_RECODE_CHAIN_INFO_CALL) { ! 736: ! 737: /* push the chain return address stack: */ ! 738: _tme_recode_x86_chain_ras_push(ic); ! 739: ! 740: /* the next chain subs is for both a chain jump far conditional ! 741: and a chain jump far unconditional: */ ! 742: TME_RECODE_X86_CHAIN_SUBS(chain_thunk, ! 743: (TME_RECODE_CHAIN_INFO_JUMP ! 744: + TME_RECODE_CHAIN_INFO_FAR ! 745: + TME_RECODE_CHAIN_INFO_CONDITIONAL)) ! 746: = tme_recode_build_to_thunk_off(ic, ic->tme_recode_ic_thunk_build_next); ! 747: TME_RECODE_X86_CHAIN_SUBS(chain_thunk, ! 748: (TME_RECODE_CHAIN_INFO_JUMP ! 749: + TME_RECODE_CHAIN_INFO_FAR ! 750: + TME_RECODE_CHAIN_INFO_UNCONDITIONAL)) ! 751: = tme_recode_build_to_thunk_off(ic, ic->tme_recode_ic_thunk_build_next); ! 752: ! 753: /* fix up the jmp to the chain subs for a chain jump far ! 754: unconditional: */ ! 755: _tme_recode_x86_fixup_jmp(thunk_bytes_jmp_to_jump_far_unconditional, ! 756: ic->tme_recode_ic_thunk_build_next); ! 757: } ! 758: ! 759: /* load the pointer to the token for the current instruction TLB ! 760: entry: */ ! 761: _tme_recode_x86_chain_itlb_current_token_load(ic); ! 762: ! 763: /* get the TLB type for the address type: */ ! 764: tme_recode_address_type_tlb_type(ic, ! 765: &chain->tme_recode_chain_address_type, ! 766: &x86_tlb_type.tme_recode_tlb_type); ! 767: ! 768: /* unbusy the token for the current instruction TLB entry: */ ! 769: #if TME_RECODE_X86_REG_CHAIN_ITLB_CURRENT_TOKEN != TME_RECODE_X86_REG_TLB ! 770: #error "TME_RECODE_X86_REG_CHAIN_ITLB_CURRENT_TOKEN changed" ! 771: #endif ! 772: _tme_recode_x86_tlb_unbusy(ic, 0); ! 773: ! 774: /* load the PC to chain to: */ ! 775: reginfo = ic->tme_recode_ic_reginfo[chain->tme_recode_chain_reg_guest]; ! 776: reginfo.tme_recode_reginfo_tags_ruses ! 777: = (TME_RECODE_REGINFO_TAGS_REG_HOST(_tme_recode_x86_tlb_reg_host_address(ic)) ! 778: + TME_RECODE_REGINFO_TAGS_VALID_SIZE(ic->tme_recode_ic_reg_size) ! 779: + TME_RECODE_REGINFO_TAGS_CLEAN); ! 780: ic->tme_recode_x86_ic_thunks_reg_guest_window_c = TME_RECODE_REG_GUEST_WINDOW_UNDEF; ! 781: tme_recode_host_reg_move(ic, ! 782: chain->tme_recode_chain_reg_guest, ! 783: reginfo.tme_recode_reginfo_all); ! 784: ic->tme_recode_x86_ic_thunks_reg_guest_window_c = TME_RECODE_REG_GUEST_WINDOW_UNDEF; ! 785: ! 786: /* find, busy, and check an instruction TLB entry: */ ! 787: _tme_recode_x86_tlb_busy(ic, ! 788: &chain->tme_recode_chain_address_type, ! 789: &x86_tlb_type); ! 790: ! 791: /* store the pointer to the current recode instruction TLB entry: */ ! 792: _tme_recode_x86_chain_itlb_current_store(ic, &x86_tlb_type); ! 793: ! 794: /* if this is the chain subs for a chain jump far conditional and ! 795: a chain jump far unconditional: */ ! 796: if (chain_info_call_or_return == TME_RECODE_CHAIN_INFO_CALL) { ! 797: ! 798: /* the next chain subs is for both a chain jump near conditional ! 799: and a chain jump near unconditional: */ ! 800: TME_RECODE_X86_CHAIN_SUBS(chain_thunk, ! 801: (TME_RECODE_CHAIN_INFO_JUMP ! 802: + TME_RECODE_CHAIN_INFO_NEAR ! 803: + TME_RECODE_CHAIN_INFO_CONDITIONAL)) ! 804: = tme_recode_build_to_thunk_off(ic, ic->tme_recode_ic_thunk_build_next); ! 805: TME_RECODE_X86_CHAIN_SUBS(chain_thunk, ! 806: (TME_RECODE_CHAIN_INFO_JUMP ! 807: + TME_RECODE_CHAIN_INFO_NEAR ! 808: + TME_RECODE_CHAIN_INFO_UNCONDITIONAL)) ! 809: = tme_recode_build_to_thunk_off(ic, ic->tme_recode_ic_thunk_build_next); ! 810: } ! 811: ! 812: /* start more instructions: */ ! 813: tme_recode_x86_insns_start(ic, thunk_bytes); ! 814: ! 815: /* subtract one from the guest chain counter: */ ! 816: thunk_bytes[0] = TME_RECODE_X86_OPCODE_GRP1_Ib_Ev; ! 817: thunk_bytes[1] = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_EA_DISP32(TME_RECODE_X86_REG_IC), ! 818: TME_RECODE_X86_OPCODE_GRP1_BINOP(TME_RECODE_X86_OPCODE_BINOP_SUB)); ! 819: *((tme_int32_t *) &thunk_bytes[2]) = ic->tme_recode_ic_chain_counter_offset; ! 820: thunk_bytes[2 + sizeof(tme_int32_t)] = 1; ! 821: thunk_bytes += 2 + sizeof(tme_int32_t) + 1; ! 822: ! 823: /* if this is the chain subs for a chain jump near conditional and a ! 824: chain jump near unconditional: */ ! 825: if (chain_info_call_or_return == TME_RECODE_CHAIN_INFO_CALL) { ! 826: ! 827: /* load the return address to the instructions thunk into the ! 828: fixup address register: */ ! 829: /* NB: this is always a 32-bit instruction, even on an x86-64 ! 830: host, because we end up converting the return address into an ! 831: instructions thunk offset, which is only 32 bits: */ ! 832: assert (sizeof(tme_recode_thunk_off_t) == sizeof(tme_int32_t)); ! 833: rex = TME_RECODE_X86_REX_B(TME_RECODE_SIZE_32, TME_RECODE_X86_REG_CHAIN_FIXUP_ADDRESS); ! 834: if (rex != 0) { ! 835: *(thunk_bytes++) = rex; ! 836: } ! 837: thunk_bytes[0] = TME_RECODE_X86_OPCODE_BINOP_MOV + TME_RECODE_X86_OPCODE_BINOP_Ev_Gv; ! 838: thunk_bytes[1] = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_EA(TME_RECODE_X86_EA_BASE_SIB), ! 839: TME_RECODE_X86_REG_CHAIN_FIXUP_ADDRESS); ! 840: thunk_bytes[2] = TME_RECODE_X86_SIB(TME_RECODE_X86_REG_SP, TME_RECODE_X86_SIB_INDEX_NONE, 1); ! 841: thunk_bytes += 3; ! 842: } ! 843: ! 844: /* otherwise, this is the chain subs for a return far unconditional: */ ! 845: else { ! 846: ! 847: /* load the chain return address stack pointer: */ ! 848: thunk_bytes[0] = (TME_RECODE_X86_OPCODE_BINOP_MOV + TME_RECODE_X86_OPCODE_BINOP_Ev_Gv); ! 849: thunk_bytes = _tme_recode_x86_emit_ic_modrm(thunk_bytes + 1, ! 850: ic->tme_recode_ic_chain_ras_pointer_offset, ! 851: TME_RECODE_X86_REG_CHAIN_RAS_POINTER); ! 852: } ! 853: ! 854: /* if needed, copy the guest instructions source address into the ! 855: correct register: */ ! 856: reg_x86_address ! 857: = tme_recode_x86_reg_from_host[_tme_recode_x86_tlb_reg_host_address(ic)]; ! 858: if (reg_x86_address != TME_RECODE_X86_REG_CHAIN_GUEST_SRC) { ! 859: _tme_recode_x86_emit_reg_copy(thunk_bytes, reg_x86_address, TME_RECODE_X86_REG_CHAIN_GUEST_SRC); ! 860: } ! 861: ! 862: /* if the guest chain counter was zero or one, jmp to the code that ! 863: handles a guest chain counter underflow: */ ! 864: thunk_bytes_jcc_chain_counter = thunk_bytes; ! 865: thunk_bytes ! 866: = _tme_recode_x86_emit_jmp(thunk_bytes, ! 867: TME_RECODE_X86_OPCODE_JCC(TME_RECODE_X86_COND_BE), ! 868: (tme_uint8_t *) NULL); ! 869: ! 870: /* finish these instructions: */ ! 871: tme_recode_x86_insns_finish(ic, thunk_bytes); ! 872: ! 873: /* if this is the chain subs for a chain jump near conditional and a ! 874: chain jump near unconditional: */ ! 875: if (chain_info_call_or_return == TME_RECODE_CHAIN_INFO_CALL) { ! 876: ! 877: /* define CF with the recode jump flag. NB that the recode flags ! 878: are now above the return address to the instructions thunk: */ ! 879: _tme_recode_x86_conds_testc(ic, TME_BIT(TME_RECODE_SIZE_HOST - TME_RECODE_SIZE_8) + TME_RECODE_FLAG_JUMP); ! 880: ! 881: /* emit a return back to the instructions thunk: */ ! 882: tme_recode_x86_insns_start(ic, thunk_bytes); ! 883: thunk_bytes[0] = TME_RECODE_X86_OPCODE_RET; ! 884: thunk_bytes++; ! 885: tme_recode_x86_insns_finish(ic, thunk_bytes); ! 886: ! 887: /* the next chain subs is for a chain call near conditional: */ ! 888: TME_RECODE_X86_CHAIN_SUBS(chain_thunk, ! 889: (TME_RECODE_CHAIN_INFO_CALL ! 890: + TME_RECODE_CHAIN_INFO_NEAR ! 891: + TME_RECODE_CHAIN_INFO_CONDITIONAL)) ! 892: = tme_recode_build_to_thunk_off(ic, ic->tme_recode_ic_thunk_build_next); ! 893: ! 894: /* if the recode jump flag is false, jmp to the chain subs for a ! 895: chain jump near conditional: */ ! 896: _tme_recode_x86_chain_subs_jmp(ic, ! 897: TME_RECODE_X86_OPCODE_JCC(TME_RECODE_X86_COND_NOT ! 898: | TME_RECODE_X86_COND_C), ! 899: chain_thunk, ! 900: (TME_RECODE_CHAIN_INFO_JUMP ! 901: + TME_RECODE_CHAIN_INFO_NEAR ! 902: + TME_RECODE_CHAIN_INFO_CONDITIONAL)); ! 903: ! 904: /* the next chain subs is for a chain call near unconditional: */ ! 905: TME_RECODE_X86_CHAIN_SUBS(chain_thunk, ! 906: (TME_RECODE_CHAIN_INFO_CALL ! 907: + TME_RECODE_CHAIN_INFO_NEAR ! 908: + TME_RECODE_CHAIN_INFO_UNCONDITIONAL)) ! 909: = tme_recode_build_to_thunk_off(ic, ic->tme_recode_ic_thunk_build_next); ! 910: ! 911: /* push the chain return address stack: */ ! 912: _tme_recode_x86_chain_ras_push(ic); ! 913: ! 914: /* jmp to the chain subs for a chain jump near unconditional: */ ! 915: _tme_recode_x86_chain_subs_jmp(ic, ! 916: TME_RECODE_X86_OPCODE_JMP_RELb, ! 917: chain_thunk, ! 918: (TME_RECODE_CHAIN_INFO_JUMP ! 919: + TME_RECODE_CHAIN_INFO_NEAR ! 920: + TME_RECODE_CHAIN_INFO_UNCONDITIONAL)); ! 921: } ! 922: ! 923: /* otherwise, this is the chain subs for a chain return far ! 924: unconditional: */ ! 925: else { ! 926: ! 927: /* load the return address: */ ! 928: _tme_recode_x86_chain_ras_ls(ic, FALSE); ! 929: ! 930: /* add one to the return address stack pointer, and store it: */ ! 931: _tme_recode_x86_chain_ras_pointer_update(ic, 1); ! 932: ! 933: /* start more instructions: */ ! 934: tme_recode_x86_insns_start(ic, thunk_bytes); ! 935: ! 936: /* if this is an x86-64 host: */ ! 937: if (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) { ! 938: ! 939: /* emit an %ip-relative lea instruction, to load the address of ! 940: thunk offset zero into the TLB scratch register: */ ! 941: thunk_bytes[0] = TME_RECODE_X86_REX_R(TME_RECODE_SIZE_HOST, TME_RECODE_X86_REG_TLB_SCRATCH); ! 942: thunk_bytes[1] = TME_RECODE_X86_OPCODE_LEA; ! 943: thunk_bytes[2] = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_EA(TME_RECODE_X86_EA_BASE_IP), ! 944: TME_RECODE_X86_REG(TME_RECODE_X86_REG_TLB_SCRATCH)); ! 945: thunk_bytes += 3 + sizeof(tme_int32_t); ! 946: ((tme_int32_t *) thunk_bytes)[-1] ! 947: = (0 - (tme_int32_t) tme_recode_build_to_thunk_off(ic, thunk_bytes)); ! 948: ! 949: /* add the address of thunk offset zero into the return address register: */ ! 950: _tme_recode_x86_emit_reg_binop(thunk_bytes, ! 951: TME_RECODE_X86_OPCODE_BINOP_ADD, ! 952: TME_RECODE_X86_REG_TLB_SCRATCH, ! 953: TME_RECODE_X86_REG_CHAIN_RETURN_ADDRESS); ! 954: } ! 955: ! 956: /* emit the indirect jmp instruction with the return address ! 957: register: */ ! 958: thunk_bytes[0] = TME_RECODE_X86_OPCODE_GRP5; ! 959: thunk_bytes[1] ! 960: = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(TME_RECODE_X86_REG_CHAIN_RETURN_ADDRESS), ! 961: TME_RECODE_X86_OPCODE_GRP5_JMP); ! 962: thunk_bytes += 2; ! 963: ! 964: /* unknown indirect jmps are predicted to fallthrough; placing a UD2 ! 965: instruction after an indirect jmp can stop a processor from ! 966: speculatively executing garbage fallthrough instructions: */ ! 967: thunk_bytes[0] = TME_RECODE_X86_OPCODE_ESC_0F; ! 968: thunk_bytes[1] = TME_RECODE_X86_OPCODE0F_UD2; ! 969: thunk_bytes += 2; ! 970: ! 971: /* finish these instructions: */ ! 972: tme_recode_x86_insns_finish(ic, thunk_bytes); ! 973: } ! 974: ! 975: /* this is the code that handles an instruction TLB miss: */ ! 976: ! 977: /* fix up the assist jcc(s) in the instruction TLB busy: */ ! 978: if (x86_tlb_type.tme_recode_x86_tlb_type_assist_jmp_address_ok != NULL) { ! 979: _tme_recode_x86_fixup_jmp(x86_tlb_type.tme_recode_x86_tlb_type_assist_jmp_address_ok, ! 980: ic->tme_recode_ic_thunk_build_next); ! 981: } ! 982: _tme_recode_x86_fixup_jmp(x86_tlb_type.tme_recode_x86_tlb_type_assist_jmp, ! 983: ic->tme_recode_ic_thunk_build_next); ! 984: ! 985: /* store the pointer to the current recode instruction TLB entry: */ ! 986: /* NB: this is necessary because the instruction TLB that missed is ! 987: still busy: */ ! 988: _tme_recode_x86_chain_itlb_current_store(ic, &x86_tlb_type); ! 989: ! 990: /* start more instructions: */ ! 991: tme_recode_x86_insns_start(ic, thunk_bytes); ! 992: ! 993: /* this is the code that jmps to the chain epilogue: */ ! 994: thunk_bytes_jmp_to_epilogue = thunk_bytes; ! 995: ! 996: /* if this is not for the chain return far subs: */ ! 997: if (chain_info_call_or_return != TME_RECODE_CHAIN_INFO_RETURN) { ! 998: ! 999: /* throw away the return address to the instructions thunk: */ ! 1000: thunk_bytes = _tme_recode_x86_emit_adjust_sp(thunk_bytes, ! 1001: TME_BIT(TME_RECODE_SIZE_HOST - TME_RECODE_SIZE_8)); ! 1002: } ! 1003: ! 1004: /* jmp to the chain epilogue: */ ! 1005: thunk_bytes ! 1006: = _tme_recode_x86_emit_jmp(thunk_bytes, ! 1007: TME_RECODE_X86_OPCODE_JMP_RELz, ! 1008: tme_recode_thunk_off_to_pointer(ic, ! 1009: ic->tme_recode_x86_ic_chain_epilogue, ! 1010: tme_uint8_t *)); ! 1011: ! 1012: /* this is the code that handles a guest chain counter underflow: */ ! 1013: _tme_recode_x86_fixup_jmp(thunk_bytes_jcc_chain_counter, thunk_bytes); ! 1014: ! 1015: /* clear the guest chain counter, since we may have wrapped it past ! 1016: zero: */ ! 1017: thunk_bytes[0] = TME_RECODE_X86_OPCODE_MOV_Iz_Ev; ! 1018: thunk_bytes[1] = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_EA_DISP32(TME_RECODE_X86_REG_IC), ! 1019: 0 /* undefined */); ! 1020: *((tme_int32_t *) &thunk_bytes[2]) = ic->tme_recode_ic_chain_counter_offset; ! 1021: *((tme_uint32_t *) &thunk_bytes[6]) = 0; ! 1022: thunk_bytes += 2 + sizeof(tme_int32_t) + sizeof(tme_uint32_t); ! 1023: ! 1024: /* jmp to the code that jmps to the chain epilogue: */ ! 1025: thunk_bytes ! 1026: = _tme_recode_x86_emit_jmp(thunk_bytes, ! 1027: TME_RECODE_X86_OPCODE_JMP_RELb, ! 1028: thunk_bytes_jmp_to_epilogue); ! 1029: ! 1030: /* finish these instructions: */ ! 1031: tme_recode_x86_insns_finish(ic, thunk_bytes); ! 1032: } ! 1033: ! 1034: /* this emits instructions for a chain out: */ ! 1035: static void ! 1036: _tme_recode_x86_chain_out(struct tme_recode_ic *ic, ! 1037: const struct tme_recode_insns_group *insns_group) ! 1038: { ! 1039: tme_uint8_t *thunk_bytes; ! 1040: tme_uint8_t *thunk_bytes_start; ! 1041: tme_uint32_t chain_info; ! 1042: ! 1043: /* the chain subs, and a chain call instruction itself, destroy the ! 1044: c register, so it won't hold the base offset of a guest register ! 1045: window any more: */ ! 1046: ic->tme_recode_x86_ic_thunks_reg_guest_window_c = TME_RECODE_REG_GUEST_WINDOW_UNDEF; ! 1047: ! 1048: /* start more instructions: */ ! 1049: tme_recode_x86_insns_start(ic, thunk_bytes); ! 1050: ! 1051: /* remember where these instructions started: */ ! 1052: thunk_bytes_start = thunk_bytes; ! 1053: ! 1054: /* get the chain information: */ ! 1055: chain_info = insns_group->tme_recode_insns_group_chain_info; ! 1056: ! 1057: /* exactly one of jump, call, and return may be set: */ ! 1058: assert ((chain_info ! 1059: & (TME_RECODE_CHAIN_INFO_JUMP ! 1060: | TME_RECODE_CHAIN_INFO_CALL ! 1061: | TME_RECODE_CHAIN_INFO_RETURN)) != 0 ! 1062: && ((chain_info ! 1063: & (TME_RECODE_CHAIN_INFO_JUMP ! 1064: | TME_RECODE_CHAIN_INFO_CALL ! 1065: | TME_RECODE_CHAIN_INFO_RETURN)) ! 1066: & ((chain_info ! 1067: & (TME_RECODE_CHAIN_INFO_JUMP ! 1068: | TME_RECODE_CHAIN_INFO_CALL ! 1069: | TME_RECODE_CHAIN_INFO_RETURN)) ! 1070: - 1)) == 0); ! 1071: ! 1072: /* there is no such thing as a chain return near: */ ! 1073: assert ((chain_info & TME_RECODE_CHAIN_INFO_RETURN) == 0 ! 1074: || (chain_info ! 1075: & (TME_RECODE_CHAIN_INFO_NEAR ! 1076: | TME_RECODE_CHAIN_INFO_FAR)) != TME_RECODE_CHAIN_INFO_NEAR); ! 1077: ! 1078: /* if this is a chain conditional, and the alternate target is far, ! 1079: force the main target to be far, since the alternate target needs ! 1080: the information generated by a chain far subs: */ ! 1081: assert (((chain_info ! 1082: & (TME_RECODE_CHAIN_INFO_ALTERNATE_NEAR ! 1083: | TME_RECODE_CHAIN_INFO_ALTERNATE_FAR)) ! 1084: == TME_RECODE_CHAIN_INFO_ALTERNATE_NEAR) ! 1085: || ((chain_info ! 1086: & (TME_RECODE_CHAIN_INFO_UNCONDITIONAL ! 1087: | TME_RECODE_CHAIN_INFO_CONDITIONAL)) ! 1088: != TME_RECODE_CHAIN_INFO_UNCONDITIONAL)); ! 1089: #if TME_RECODE_CHAIN_INFO_FAR == 0 || TME_RECODE_CHAIN_INFO_ALTERNATE_FAR <= TME_RECODE_CHAIN_INFO_FAR ! 1090: #error "TME_RECODE_CHAIN_INFO_ values changed" ! 1091: #endif ! 1092: chain_info ! 1093: |= ((chain_info ! 1094: / (TME_RECODE_CHAIN_INFO_ALTERNATE_FAR ! 1095: / TME_RECODE_CHAIN_INFO_FAR)) ! 1096: & TME_RECODE_CHAIN_INFO_FAR); ! 1097: ! 1098: /* if this is a chain call: */ ! 1099: if (chain_info & TME_RECODE_CHAIN_INFO_CALL) { ! 1100: ! 1101: /* load the return address register with a value that indicates ! 1102: the chain epilogue. this will later get fixed up to indicate ! 1103: the instructions thunk to return to: */ ! 1104: thunk_bytes[0] = TME_RECODE_X86_OPCODE_MOV_Iv_Gv(TME_RECODE_X86_REG_CHAIN_RETURN_ADDRESS); ! 1105: *((tme_uint32_t *) (thunk_bytes + 1)) ! 1106: = TME_RECODE_X86_CHAIN_RETURN_ADDRESS(ic, ic->tme_recode_x86_ic_chain_epilogue); ! 1107: thunk_bytes += 1 + sizeof(tme_uint32_t); ! 1108: } ! 1109: ! 1110: /* if this is a chain return unconditional, jump to the chain subs, ! 1111: otherwise call the chain subs: */ ! 1112: thunk_bytes[0] ! 1113: = (((chain_info ! 1114: & (TME_RECODE_CHAIN_INFO_RETURN ! 1115: + (TME_RECODE_CHAIN_INFO_UNCONDITIONAL ! 1116: | TME_RECODE_CHAIN_INFO_CONDITIONAL))) ! 1117: == (TME_RECODE_CHAIN_INFO_RETURN ! 1118: + TME_RECODE_CHAIN_INFO_UNCONDITIONAL)) ! 1119: ? TME_RECODE_X86_OPCODE_JMP_RELz ! 1120: : TME_RECODE_X86_OPCODE_CALL_RELz); ! 1121: thunk_bytes += 1 + sizeof(tme_int32_t); ! 1122: ((tme_int32_t *) thunk_bytes)[-1] ! 1123: = (TME_RECODE_X86_CHAIN_SUBS(insns_group->tme_recode_insns_group_chain_thunk, ! 1124: chain_info) ! 1125: - tme_recode_build_to_thunk_off(ic, thunk_bytes)); ! 1126: ! 1127: /* for the regular target, emit the unconditional jmp instruction, ! 1128: or the conditional jc instruction, to the chain fixup target: */ ! 1129: if ((chain_info ! 1130: & (TME_RECODE_CHAIN_INFO_UNCONDITIONAL ! 1131: | TME_RECODE_CHAIN_INFO_CONDITIONAL)) ! 1132: == TME_RECODE_CHAIN_INFO_UNCONDITIONAL) { ! 1133: thunk_bytes[0] = TME_RECODE_X86_OPCODE_JMP_RELz; ! 1134: } ! 1135: else { ! 1136: *((tme_uint16_t *) thunk_bytes) ! 1137: = (TME_RECODE_X86_OPCODE_ESC_0F ! 1138: + (TME_RECODE_X86_OPCODE0F_JCC(TME_RECODE_X86_COND_C) ! 1139: << 8)); ! 1140: thunk_bytes++; ! 1141: } ! 1142: thunk_bytes += 1 + sizeof(tme_int32_t); ! 1143: ((tme_int32_t *) thunk_bytes)[-1] ! 1144: = (TME_RECODE_X86_CHAIN_FIXUP_TARGET(ic, chain_info) ! 1145: - tme_recode_build_to_thunk_off(ic, thunk_bytes)); ! 1146: ! 1147: /* if this is a chain conditional instruction: */ ! 1148: if ((chain_info ! 1149: & (TME_RECODE_CHAIN_INFO_UNCONDITIONAL ! 1150: | TME_RECODE_CHAIN_INFO_CONDITIONAL)) ! 1151: != TME_RECODE_CHAIN_INFO_UNCONDITIONAL) { ! 1152: ! 1153: /* for the alternate target, emit the unconditional jmp ! 1154: instruction to the chain fixup target alternate: */ ! 1155: thunk_bytes[0] = TME_RECODE_X86_OPCODE_JMP_RELz; ! 1156: thunk_bytes += 1 + sizeof(tme_int32_t); ! 1157: ((tme_int32_t *) thunk_bytes)[-1] ! 1158: = (TME_RECODE_X86_CHAIN_FIXUP_TARGET_ALTERNATE(ic, chain_info) ! 1159: - tme_recode_build_to_thunk_off(ic, thunk_bytes)); ! 1160: } ! 1161: ! 1162: /* check the size of the chain out: */ ! 1163: assert ((thunk_bytes - thunk_bytes_start) ! 1164: <= TME_RECODE_X86_CHAIN_OUT_SIZE_MAX); ! 1165: ! 1166: /* finish these instructions: */ ! 1167: tme_recode_x86_insns_finish(ic, thunk_bytes); ! 1168: } ! 1169: ! 1170: /* if the given address is reachable with an effective address using ! 1171: an address in another register, this returns the signed 32-bit ! 1172: displacement, otherwise this returns zero: */ ! 1173: static inline tme_int32_t ! 1174: _tme_recode_x86_chain_base_disp32(const tme_shared tme_uint8_t *base, ! 1175: const tme_shared tme_uint8_t *pointer) ! 1176: { ! 1177: signed long disp; ! 1178: ! 1179: disp = pointer - base; ! 1180: return (disp == (tme_int32_t) disp ! 1181: ? disp ! 1182: : 0); ! 1183: } ! 1184: ! 1185: /* if the given address is reachable with a PC-relative effective ! 1186: address, this returns the signed 32-bit displacement, otherwise ! 1187: this returns zero: */ ! 1188: static inline tme_int32_t ! 1189: _tme_recode_x86_chain_ip_disp32(const struct tme_recode_ic *ic, ! 1190: const tme_uint8_t *thunk_bytes, ! 1191: const tme_shared tme_uint8_t *pointer) ! 1192: { ! 1193: tme_recode_thunk_off_t thunk_off; ! 1194: const tme_uint8_t *ip; ! 1195: ! 1196: thunk_off = tme_recode_build_to_thunk_off(ic, thunk_bytes); ! 1197: ip = tme_recode_thunk_off_to_pointer(ic, thunk_off, const tme_uint8_t *); ! 1198: return (_tme_recode_x86_chain_base_disp32(ip, pointer)); ! 1199: } ! 1200: ! 1201: /* this emits instructions for a chain in: */ ! 1202: static void ! 1203: _tme_recode_x86_chain_in(struct tme_recode_ic *ic, ! 1204: const struct tme_recode_insns_group *insns_group) ! 1205: { ! 1206: tme_uint8_t *thunk_bytes; ! 1207: tme_uint8_t *thunk_bytes_start; ! 1208: const tme_shared tme_uint8_t *insns_group_src; ! 1209: unsigned int size_insns_group_src; ! 1210: tme_int32_t disp32; ! 1211: unsigned int rex; ! 1212: const tme_shared tme_uint8_t *insns_group_valid_byte; ! 1213: unsigned int size_insns_group_valid_byte; ! 1214: ! 1215: /* start more instructions: */ ! 1216: tme_recode_x86_insns_start(ic, thunk_bytes); ! 1217: ! 1218: /* remember where these instructions started: */ ! 1219: thunk_bytes_start = thunk_bytes; ! 1220: ! 1221: /* get the guest instructions source address: */ ! 1222: insns_group_src = insns_group->tme_recode_insns_group_src; ! 1223: ! 1224: /* if this is an ia32 host, or if this is an x86-64 host and the ! 1225: guest instructions source address fits in a sign-extended 32 ! 1226: bits: */ ! 1227: if (TME_RECODE_SIZE_HOST == TME_RECODE_SIZE_32 ! 1228: || (((tme_int32_t) ! 1229: (signed long) ! 1230: insns_group_src) ! 1231: == (signed long) insns_group_src)) { ! 1232: ! 1233: /* emit a cmpl $imm32, %reg or cmpq $imm32, %reg: */ ! 1234: if (TME_RECODE_SIZE_HOST == TME_RECODE_SIZE_32) { ! 1235: thunk_bytes[0] = (tme_uint8_t) TME_RECODE_X86_INSN_CMP_Iz_REG_CHAIN_GUEST_SRC; ! 1236: } ! 1237: else { ! 1238: *((tme_uint16_t *) thunk_bytes) = TME_RECODE_X86_INSN_CMP_Iz_REG_CHAIN_GUEST_SRC; ! 1239: } ! 1240: *((tme_int32_t *) &thunk_bytes[(TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 1]) ! 1241: = (signed long) insns_group_src; ! 1242: thunk_bytes += (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 1 + sizeof(tme_int32_t); ! 1243: ! 1244: /* assume that this is an x86-64 host, and set the size of the ! 1245: instructions that check the guest instruction source address ! 1246: matches the instructions thunk: */ ! 1247: size_insns_group_src = TME_RECODE_X86_64_CHAIN_IN_SIZE_CMPQ_IMM32_JNZ; ! 1248: } ! 1249: ! 1250: /* otherwise, this is an x86-64 host and the guest instructions ! 1251: source address doesn't fit in a sign-extended 32 bits: */ ! 1252: else { ! 1253: ! 1254: /* if the guest instructions source address fits in 32 bits: */ ! 1255: if (TME_RECODE_SIZE_FITS((unsigned long) insns_group_src, ! 1256: TME_RECODE_SIZE_32)) { ! 1257: ! 1258: /* emit a movl $imm32, %reg: */ ! 1259: thunk_bytes[0] = TME_RECODE_X86_INSN_MOVL_IMM32_REG_TLB_SCRATCH; ! 1260: *((tme_uint32_t *) &thunk_bytes[1]) = (unsigned long) insns_group_src; ! 1261: thunk_bytes += 1 + sizeof(tme_uint32_t); ! 1262: ! 1263: /* set the size of the instructions that check the guest ! 1264: instruction source address matches the instructions thunk: */ ! 1265: size_insns_group_src = TME_RECODE_X86_64_CHAIN_IN_SIZE_MOVL_CMP_JNZ; ! 1266: } ! 1267: ! 1268: /* otherwise, if the guest instructions source address is within a ! 1269: signed 32-bit displacement of the %ip of an lea ! 1270: instruction: */ ! 1271: else if ((disp32 ! 1272: = _tme_recode_x86_chain_ip_disp32(ic, ! 1273: (thunk_bytes ! 1274: + 1 /* rex */ ! 1275: + 1 /* lea */ ! 1276: + 1 /* modR/M */ ! 1277: + sizeof(disp32)), ! 1278: insns_group_src))) { ! 1279: ! 1280: /* emit an lea disp32(%ip), %reg: */ ! 1281: thunk_bytes[0] = (tme_uint8_t) TME_RECODE_X86_INSN_LEA_DISP32_IP_REG_TLB_SCRATCH; ! 1282: *((tme_uint16_t *) &thunk_bytes[1]) = (TME_RECODE_X86_INSN_LEA_DISP32_IP_REG_TLB_SCRATCH >> 8); ! 1283: *((tme_int32_t *) &thunk_bytes[3]) = disp32; ! 1284: thunk_bytes += 1 + 2 + sizeof(disp32); ! 1285: ! 1286: /* set the size of the instructions that check the guest ! 1287: instruction source address matches the instructions thunk: */ ! 1288: size_insns_group_src = TME_RECODE_X86_64_CHAIN_IN_SIZE_LEA_IP_CMP_JNZ; ! 1289: } ! 1290: ! 1291: /* otherwise, the guest instructions source address can't be ! 1292: generated: */ ! 1293: else { ! 1294: ! 1295: /* emit a movq $imm64, %reg: */ ! 1296: *((tme_uint16_t *) thunk_bytes) = TME_RECODE_X86_INSN_MOVQ_IMM64_REG_TLB_SCRATCH; ! 1297: *((unsigned long *) &thunk_bytes[2]) = (unsigned long) insns_group_src; ! 1298: thunk_bytes += 1 + 1 + TME_BIT(TME_RECODE_SIZE_HOST - TME_RECODE_SIZE_8); ! 1299: ! 1300: /* set the size of the instructions that check the guest ! 1301: instruction source address matches the instructions thunk: */ ! 1302: size_insns_group_src = TME_RECODE_X86_64_CHAIN_IN_SIZE_MOVQ_CMP_JNZ; ! 1303: } ! 1304: ! 1305: /* emit a cmpq %reg, %reg: */ ! 1306: rex = (TME_RECODE_X86_REX_B(TME_RECODE_SIZE_HOST, ! 1307: TME_RECODE_X86_REG_CHAIN_GUEST_SRC) ! 1308: | TME_RECODE_X86_REX_R(TME_RECODE_SIZE_HOST, ! 1309: TME_RECODE_X86_REG_TLB_SCRATCH)); ! 1310: assert (rex != 0); ! 1311: thunk_bytes[0] = rex; ! 1312: *((tme_uint16_t *) &thunk_bytes[1]) ! 1313: = ((TME_RECODE_X86_OPCODE_BINOP_CMP ! 1314: + TME_RECODE_X86_OPCODE_BINOP_Ev_Gv) ! 1315: + (TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(TME_RECODE_X86_REG_CHAIN_GUEST_SRC), ! 1316: TME_RECODE_X86_REG(TME_RECODE_X86_REG_TLB_SCRATCH)) ! 1317: << 8)); ! 1318: thunk_bytes += 3; ! 1319: } ! 1320: ! 1321: /* if the guest instructions source address doesn't match this ! 1322: instructions thunk, jump to the chain epilogue: */ ! 1323: *((tme_uint16_t *) thunk_bytes) ! 1324: = (TME_RECODE_X86_OPCODE_ESC_0F ! 1325: + (TME_RECODE_X86_OPCODE0F_JCC(TME_RECODE_X86_COND_NOT | TME_RECODE_X86_COND_Z) ! 1326: << 8)); ! 1327: thunk_bytes += 2 + sizeof(tme_int32_t); ! 1328: ((tme_int32_t *) thunk_bytes)[-1] ! 1329: = (ic->tme_recode_x86_ic_chain_epilogue ! 1330: - tme_recode_build_to_thunk_off(ic, thunk_bytes)); ! 1331: ! 1332: /* get the guest instructions valid byte address: */ ! 1333: insns_group_valid_byte = insns_group->tme_recode_insns_group_valid_byte; ! 1334: ! 1335: /* if this is an ia32 host: */ ! 1336: if (TME_RECODE_SIZE_HOST == TME_RECODE_SIZE_32) { ! 1337: ! 1338: /* emit the opcode and addr32 for a testb $imm8, addr32: */ ! 1339: *((tme_uint16_t *) thunk_bytes) ! 1340: = (TME_RECODE_X86_OPCODE_GRP3_Eb ! 1341: + (TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_EA(TME_RECODE_X86_EA_BASE_NONE), ! 1342: TME_RECODE_X86_OPCODE_GRP3_TEST) ! 1343: << 8)); ! 1344: *((tme_uint32_t *) &thunk_bytes[2]) = (unsigned long) insns_group_valid_byte; ! 1345: thunk_bytes += 2 + sizeof(tme_uint32_t); ! 1346: ! 1347: /* silence uninitialized variable warnings: */ ! 1348: size_insns_group_valid_byte = 0; ! 1349: } ! 1350: ! 1351: /* otherwise, this is an x86-64 host: */ ! 1352: else { ! 1353: ! 1354: /* assume that we can emit a testb followed by a jz, and emit the ! 1355: first opcode byte for the testb: */ ! 1356: thunk_bytes[0] = TME_RECODE_X86_OPCODE_GRP3_Eb; ! 1357: thunk_bytes++; ! 1358: ! 1359: /* if the guest instructions valid byte address fits in a ! 1360: sign-extended 32 bits: */ ! 1361: if (((tme_int32_t) ! 1362: (signed long) ! 1363: insns_group_valid_byte) ! 1364: == (signed long) insns_group_valid_byte) { ! 1365: ! 1366: /* emit the modR/M, SIB, and disp32 for a testb $imm8, addr32: */ ! 1367: *((tme_uint16_t *) thunk_bytes) = TME_RECODE_X86_INSN_TESTB_IMM8_ADDR32 >> 8; ! 1368: *((tme_int32_t *) &thunk_bytes[2]) = (signed long) insns_group_valid_byte; ! 1369: thunk_bytes += 2 + sizeof(tme_int32_t); ! 1370: ! 1371: /* set the size of the instructions that check that the guest ! 1372: instructions are still valid: */ ! 1373: size_insns_group_valid_byte = TME_RECODE_X86_64_CHAIN_IN_SIZE_TESTB_ADDR32_JZ; ! 1374: } ! 1375: ! 1376: /* otherwise, if the guest instructions valid byte address is ! 1377: within a signed 32-bit displacement of the %ip of the testb ! 1378: instruction: */ ! 1379: else if ((disp32 ! 1380: = _tme_recode_x86_chain_ip_disp32(ic, ! 1381: (thunk_bytes ! 1382: + 1 /* testb */ ! 1383: + 1 /* modR/M */ ! 1384: + sizeof(disp32) ! 1385: + 1 /* imm8 */), ! 1386: insns_group_valid_byte))) { ! 1387: ! 1388: /* emit the modR/M and disp32 for a testb $imm8, disp32(%ip): */ ! 1389: thunk_bytes[0] = TME_RECODE_X86_INSN_TESTB_IMM8_DISP32_IP >> 8; ! 1390: *((tme_int32_t *) &thunk_bytes[1]) = disp32; ! 1391: thunk_bytes += 1 + sizeof(disp32); ! 1392: ! 1393: /* set the size of the instructions that check that the guest ! 1394: instructions are still valid: */ ! 1395: size_insns_group_valid_byte = TME_RECODE_X86_64_CHAIN_IN_SIZE_TESTB_BASE_JZ; ! 1396: } ! 1397: ! 1398: /* otherwise, if the guest instructions valid byte address is ! 1399: within a signed 32-bit displacement of the guest instructions ! 1400: source address: */ ! 1401: else if ((disp32 ! 1402: = _tme_recode_x86_chain_base_disp32(insns_group_src, ! 1403: insns_group_valid_byte))) { ! 1404: ! 1405: /* emit the modR/M and disp32 for a testb $imm8, disp32(%reg): */ ! 1406: thunk_bytes[0] = TME_RECODE_X86_INSN_TESTB_IMM8_DISP32_REG_CHAIN_GUEST_SRC >> 8; ! 1407: *((tme_int32_t *) &thunk_bytes[1]) = disp32; ! 1408: thunk_bytes += 1 + sizeof(disp32); ! 1409: ! 1410: /* set the size of the instructions that check that the guest ! 1411: instructions are still valid: */ ! 1412: size_insns_group_valid_byte = TME_RECODE_X86_64_CHAIN_IN_SIZE_TESTB_BASE_JZ; ! 1413: } ! 1414: ! 1415: /* otherwise, the guest instructions valid byte address can't be ! 1416: generated: */ ! 1417: else { ! 1418: ! 1419: /* emit a movq $imm64, %reg: */ ! 1420: /* NB: thunk_bytes has already been advanced by one: */ ! 1421: *((tme_uint16_t *) &thunk_bytes[0 - 1]) = TME_RECODE_X86_INSN_MOVQ_IMM64_REG_TLB_SCRATCH; ! 1422: *((unsigned long *) &thunk_bytes[2 - 1]) = (unsigned long) insns_group_valid_byte; ! 1423: thunk_bytes += -1 + 1 + 1 + TME_BIT(TME_RECODE_SIZE_HOST - TME_RECODE_SIZE_8); ! 1424: ! 1425: /* emit the opcode and modR/M for a testb $imm8, (%reg): */ ! 1426: *((tme_uint16_t *) thunk_bytes) ! 1427: = (TME_RECODE_X86_OPCODE_GRP3_Eb ! 1428: + (TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_EA(TME_RECODE_X86_REG_TLB_SCRATCH), ! 1429: TME_RECODE_X86_OPCODE_GRP3_TEST) ! 1430: << 8)); ! 1431: thunk_bytes += 2; ! 1432: ! 1433: /* set the size of the instructions that check that the guest ! 1434: instructions are still valid: */ ! 1435: size_insns_group_valid_byte = TME_RECODE_X86_64_CHAIN_IN_SIZE_MOVQ_TESTB_JZ; ! 1436: } ! 1437: } ! 1438: ! 1439: /* emit the $imm8, and if the guest instructions aren't valid, jump ! 1440: to the chain epilogue: */ ! 1441: thunk_bytes[0] = insns_group->tme_recode_insns_group_valid_mask; ! 1442: *((tme_uint16_t *) &thunk_bytes[1]) ! 1443: = (TME_RECODE_X86_OPCODE_ESC_0F ! 1444: + (TME_RECODE_X86_OPCODE0F_JCC(TME_RECODE_X86_COND_Z) ! 1445: << 8)); ! 1446: thunk_bytes += 1 + 2 + sizeof(tme_int32_t); ! 1447: ((tme_int32_t *) thunk_bytes)[-1] ! 1448: = (ic->tme_recode_x86_ic_chain_epilogue ! 1449: - tme_recode_build_to_thunk_off(ic, thunk_bytes)); ! 1450: ! 1451: /* check the size of the chain in: */ ! 1452: assert ((thunk_bytes - thunk_bytes_start) ! 1453: <= TME_RECODE_X86_CHAIN_IN_SIZE_MAX); ! 1454: ! 1455: /* check the sizes of the instructions that we need to skip for a ! 1456: chain in near: */ ! 1457: assert ((thunk_bytes - thunk_bytes_start) ! 1458: == (TME_RECODE_SIZE_HOST == TME_RECODE_SIZE_32 ! 1459: ? TME_RECODE_IA32_CHAIN_IN_SIZE_FAR ! 1460: : (size_insns_group_src ! 1461: + size_insns_group_valid_byte))); ! 1462: ! 1463: /* finish these instructions: */ ! 1464: tme_recode_x86_insns_finish(ic, thunk_bytes); ! 1465: } ! 1466: ! 1467: /* this makes the chain prologue for a new IC: */ ! 1468: static void ! 1469: _tme_recode_x86_chain_prologue(struct tme_recode_ic *ic, ! 1470: const struct tme_recode_chain *chain, ! 1471: struct tme_recode_chain_thunk *chain_thunk) ! 1472: { ! 1473: tme_uint8_t *thunk_bytes; ! 1474: unsigned int reg_x86_insns_thunk; ! 1475: unsigned long thunk_address0; ! 1476: ! 1477: /* start more instructions: */ ! 1478: tme_recode_x86_insns_start(ic, thunk_bytes); ! 1479: ! 1480: /* set the chain prologue: */ ! 1481: chain_thunk->tme_recode_x86_chain_thunk_prologue ! 1482: = tme_recode_thunk_off_to_pointer(ic, ! 1483: tme_recode_build_to_thunk_off(ic, thunk_bytes), ! 1484: void (*) _TME_P((struct tme_ic *, tme_recode_thunk_off_t))); ! 1485: ! 1486: /* push all callee-saved registers: */ ! 1487: _tme_recode_x86_emit_reg_push(thunk_bytes, TME_RECODE_X86_REG_BP); ! 1488: _tme_recode_x86_emit_reg_push(thunk_bytes, TME_RECODE_X86_REG_B); ! 1489: if (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) { ! 1490: _tme_recode_x86_emit_reg_push(thunk_bytes, TME_RECODE_X86_REG_N(12)); ! 1491: _tme_recode_x86_emit_reg_push(thunk_bytes, TME_RECODE_X86_REG_N(13)); ! 1492: _tme_recode_x86_emit_reg_push(thunk_bytes, TME_RECODE_X86_REG_N(14)); ! 1493: _tme_recode_x86_emit_reg_push(thunk_bytes, TME_RECODE_X86_REG_N(15)); ! 1494: } ! 1495: else { ! 1496: _tme_recode_x86_emit_reg_push(thunk_bytes, TME_RECODE_X86_REG_SI); ! 1497: _tme_recode_x86_emit_reg_push(thunk_bytes, TME_RECODE_X86_REG_DI); ! 1498: } ! 1499: ! 1500: /* the instructions thunk offset and address will be in the si register: */ ! 1501: reg_x86_insns_thunk = TME_RECODE_X86_REG_SI; ! 1502: ! 1503: /* if this is an x86-64 host: */ ! 1504: if (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) { ! 1505: ! 1506: /* copy the struct tme_ic * argument into the ic register: */ ! 1507: _tme_recode_x86_emit_reg_copy(thunk_bytes, TME_RECODE_X86_REG_DI, TME_RECODE_X86_REG_IC); ! 1508: ! 1509: /* the instructions thunk offset is already in the second argument register: */ ! 1510: assert (reg_x86_insns_thunk == TME_RECODE_X86_REG_SI); ! 1511: } ! 1512: ! 1513: /* otherwise, this is an ia32 host: */ ! 1514: else { ! 1515: ! 1516: /* load the struct tme_ic * argument from the stack: */ ! 1517: /* NB: the magic 5 below is for the four callee-saved registers ! 1518: that we pushed above, plus the return address: */ ! 1519: thunk_bytes[0] = (TME_RECODE_X86_OPCODE_BINOP_MOV + TME_RECODE_X86_OPCODE_BINOP_Ev_Gv); ! 1520: thunk_bytes[1] = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_EA_DISP8(TME_RECODE_X86_EA_BASE_SIB), ! 1521: TME_RECODE_X86_REG_IC); ! 1522: thunk_bytes[2] = TME_RECODE_X86_SIB(TME_RECODE_X86_REG_SP, TME_RECODE_X86_SIB_INDEX_NONE, 1); ! 1523: thunk_bytes[3] = (TME_BIT(TME_RECODE_SIZE_HOST - TME_RECODE_SIZE_8) * 5); ! 1524: thunk_bytes += 4; ! 1525: ! 1526: /* load the instructions thunk offset from the stack: */ ! 1527: /* NB: the magic 6 below is for the four callee-saved registers ! 1528: that we pushed above, plus the return address, plus the struct ! 1529: tme_ic * argument: */ ! 1530: thunk_bytes[0] = (TME_RECODE_X86_OPCODE_BINOP_MOV + TME_RECODE_X86_OPCODE_BINOP_Ev_Gv); ! 1531: thunk_bytes[1] = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_EA_DISP8(TME_RECODE_X86_EA_BASE_SIB), ! 1532: reg_x86_insns_thunk); ! 1533: thunk_bytes[2] = TME_RECODE_X86_SIB(TME_RECODE_X86_REG_SP, TME_RECODE_X86_SIB_INDEX_NONE, 1); ! 1534: thunk_bytes[3] = (TME_BIT(TME_RECODE_SIZE_HOST - TME_RECODE_SIZE_8) * 6); ! 1535: thunk_bytes += 4; ! 1536: } ! 1537: ! 1538: /* get the near address of the thunk at offset zero: */ ! 1539: thunk_address0 = tme_recode_thunk_off_to_pointer(ic, 0, char *) - (char *) 0; ! 1540: ! 1541: /* if this is an ia32 host, or if this is an x86-64 host and the ! 1542: near address of the thunk at offset zero fits in 32 bits: */ ! 1543: if (TME_RECODE_SIZE_HOST == TME_RECODE_SIZE_32 ! 1544: || thunk_address0 == (tme_uint32_t) thunk_address0) { ! 1545: ! 1546: /* add the near address of the thunk at offset zero to the ! 1547: instructions thunk offset: */ ! 1548: thunk_bytes[0] = TME_RECODE_X86_OPCODE_GRP1_Iz_Ev; ! 1549: thunk_bytes[1] = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(reg_x86_insns_thunk), ! 1550: TME_RECODE_X86_OPCODE_GRP1_BINOP(TME_RECODE_X86_OPCODE_BINOP_ADD)); ! 1551: *((tme_uint32_t *) &thunk_bytes[2]) = thunk_address0; ! 1552: thunk_bytes += 2 + sizeof(tme_uint32_t); ! 1553: } ! 1554: ! 1555: /* otherwise, this is an x86-64 host and the near address of the ! 1556: thunk at offset zero doesn't fit in 32 bits: */ ! 1557: else { ! 1558: ! 1559: /* zero-extend the instructions thunk offset to 64 bits: */ ! 1560: thunk_bytes[0] ! 1561: = (TME_RECODE_X86_OPCODE_BINOP_MOV ! 1562: + TME_RECODE_X86_OPCODE_BINOP_Gv_Ev); ! 1563: thunk_bytes[1] ! 1564: = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(reg_x86_insns_thunk), ! 1565: TME_RECODE_X86_REG(reg_x86_insns_thunk)); ! 1566: thunk_bytes += 2; ! 1567: ! 1568: /* load the near address of the thunk at offset zero into the TLB ! 1569: scratch register: */ ! 1570: *((tme_uint16_t *) &thunk_bytes[0]) = TME_RECODE_X86_INSN_MOVQ_IMM64_REG_TLB_SCRATCH; ! 1571: *((unsigned long *) &thunk_bytes[2]) = thunk_address0; ! 1572: thunk_bytes += 2 + TME_BIT(TME_RECODE_SIZE_HOST - TME_RECODE_SIZE_8); ! 1573: ! 1574: /* add the TLB scratch register to the instructions thunk ! 1575: offset: */ ! 1576: _tme_recode_x86_emit_reg_binop(thunk_bytes, ! 1577: TME_RECODE_X86_OPCODE_BINOP_ADD, ! 1578: TME_RECODE_X86_REG_TLB_SCRATCH, ! 1579: reg_x86_insns_thunk); ! 1580: } ! 1581: ! 1582: /* allocate one word on the stack for the recode flag bytes. NB ! 1583: that on x86-64, this returns the stack pointer to 16-byte ! 1584: alignment as required by the ABI: */ ! 1585: thunk_bytes ! 1586: = _tme_recode_x86_emit_adjust_sp(thunk_bytes, ! 1587: (0 - TME_BIT(TME_RECODE_SIZE_HOST ! 1588: - TME_RECODE_SIZE_8))); ! 1589: ! 1590: /* call the chain jump far unconditional subs: */ ! 1591: thunk_bytes[0] = TME_RECODE_X86_OPCODE_CALL_RELz; ! 1592: thunk_bytes += 1 + sizeof(tme_int32_t); ! 1593: ((tme_int32_t *) thunk_bytes)[-1] ! 1594: = (TME_RECODE_X86_CHAIN_SUBS(chain_thunk, ! 1595: (TME_RECODE_CHAIN_INFO_JUMP ! 1596: + TME_RECODE_CHAIN_INFO_FAR ! 1597: + TME_RECODE_CHAIN_INFO_UNCONDITIONAL)) ! 1598: - tme_recode_build_to_thunk_off(ic, thunk_bytes)); ! 1599: ! 1600: /* do the indirect jmp into the instructions thunk: */ ! 1601: assert (TME_RECODE_X86_REX_B(0, reg_x86_insns_thunk) == 0); ! 1602: thunk_bytes[0] = TME_RECODE_X86_OPCODE_GRP5; ! 1603: thunk_bytes[1] = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(reg_x86_insns_thunk), ! 1604: TME_RECODE_X86_OPCODE_GRP5_JMP); ! 1605: thunk_bytes += 2; ! 1606: ! 1607: /* unknown indirect jmps are predicted to fallthrough; placing a UD2 ! 1608: instruction after an indirect jmp can stop a processor from ! 1609: speculatively executing garbage fallthrough instructions: */ ! 1610: thunk_bytes[0] = TME_RECODE_X86_OPCODE_ESC_0F; ! 1611: thunk_bytes[1] = TME_RECODE_X86_OPCODE0F_UD2; ! 1612: thunk_bytes += 2; ! 1613: ! 1614: /* finish these instructions: */ ! 1615: tme_recode_x86_insns_finish(ic, thunk_bytes); ! 1616: ! 1617: /* finish the chain prologue: */ ! 1618: tme_recode_host_thunk_finish(ic); ! 1619: } ! 1620: ! 1621: /* this returns a chain thunk: */ ! 1622: const struct tme_recode_chain_thunk * ! 1623: tme_recode_chain_thunk(struct tme_recode_ic *ic, ! 1624: const struct tme_recode_chain *chain) ! 1625: { ! 1626: struct tme_recode_chain_thunk *chain_thunk; ! 1627: ! 1628: /* if the chain fixup targets haven't been made yet: */ ! 1629: if (TME_RECODE_X86_CHAIN_FIXUP_TARGET_ALTERNATE(ic, ! 1630: (TME_RECODE_CHAIN_INFO_JUMP ! 1631: + TME_RECODE_CHAIN_INFO_ALTERNATE_NEAR)) ! 1632: == 0) { ! 1633: ! 1634: /* make the chain fixup targets: */ ! 1635: _tme_recode_x86_chain_fixup_targets(ic); ! 1636: } ! 1637: ! 1638: /* start the thunk for the chain subs: */ ! 1639: if (!tme_recode_host_thunk_start(ic)) { ! 1640: abort(); ! 1641: } ! 1642: chain_thunk = tme_new0(struct tme_recode_chain_thunk, 1); ! 1643: ! 1644: /* make the chain subs for chain calls and chain jumps: */ ! 1645: _tme_recode_x86_chain_subs(ic, ! 1646: chain, ! 1647: chain_thunk, ! 1648: TME_RECODE_CHAIN_INFO_CALL); ! 1649: ! 1650: /* make the chain subs for chain returns: */ ! 1651: _tme_recode_x86_chain_subs(ic, ! 1652: chain, ! 1653: chain_thunk, ! 1654: TME_RECODE_CHAIN_INFO_RETURN); ! 1655: ! 1656: /* finish the thunk for the chain subs: */ ! 1657: tme_recode_host_thunk_finish(ic); ! 1658: ! 1659: /* start the thunk for the chain prologue: */ ! 1660: if (!tme_recode_host_thunk_start(ic)) { ! 1661: abort(); ! 1662: } ! 1663: ! 1664: /* make the chain prologue: */ ! 1665: _tme_recode_x86_chain_prologue(ic, ! 1666: chain, ! 1667: chain_thunk); ! 1668: ! 1669: /* finish the thunk for the chain prologue: */ ! 1670: tme_recode_host_thunk_finish(ic); ! 1671: ! 1672: return (chain_thunk); ! 1673: } ! 1674: ! 1675: /* this clears the return address stack: */ ! 1676: void ! 1677: tme_recode_chain_ras_clear(const struct tme_recode_ic *recode_ic, ! 1678: struct tme_ic *ic) ! 1679: { ! 1680: tme_recode_ras_entry_t *_ras_entry; ! 1681: tme_uint32_t ras_size; ! 1682: tme_recode_ras_entry_t ras_entry; ! 1683: ! 1684: /* clear the return address stack: */ ! 1685: ras_entry ! 1686: = TME_RECODE_X86_CHAIN_RETURN_ADDRESS(recode_ic, ! 1687: recode_ic->tme_recode_x86_ic_chain_epilogue); ! 1688: _ras_entry ! 1689: = ((tme_recode_ras_entry_t *) ! 1690: (((tme_uint8_t *) ic) ! 1691: + recode_ic->tme_recode_ic_chain_ras_offset)); ! 1692: ras_size = recode_ic->tme_recode_ic_chain_ras_size; ! 1693: do { ! 1694: _ras_entry[ras_size - 1] = ras_entry; ! 1695: } while (--ras_size); ! 1696: ! 1697: /* make sure that the return address stack pointer is valid: */ ! 1698: assert (*((tme_uint32_t *) ! 1699: (((tme_uint8_t *) ic) ! 1700: + recode_ic->tme_recode_ic_chain_ras_pointer_offset)) ! 1701: < recode_ic->tme_recode_ic_chain_ras_size); ! 1702: } ! 1703: ! 1704: /* this fixes up a chain: */ ! 1705: tme_recode_thunk_off_t ! 1706: tme_recode_chain_fixup(struct tme_recode_ic *ic, ! 1707: tme_recode_thunk_off_t chain_fixup, ! 1708: tme_uint32_t chain_info, ! 1709: tme_recode_thunk_off_t insns_thunk_next, ! 1710: tme_recode_thunk_off_t insns_thunk_return) ! 1711: { ! 1712: tme_uint32_t return_imm32; ! 1713: tme_uint16_t insn_0_15_buffer; ! 1714: tme_uint32_t insn_0_15; ! 1715: const tme_uint8_t *thunk_bytes; ! 1716: tme_uint8_t opcode_buffer; ! 1717: tme_int32_t displacement; ! 1718: ! 1719: /* if this is a chain call: */ ! 1720: if (chain_info & TME_RECODE_CHAIN_INFO_CALL) { ! 1721: ! 1722: /* there must be a return instructions thunk: */ ! 1723: assert (insns_thunk_return != 0); ! 1724: ! 1725: /* a chain call emits: ! 1726: ! 1727: movl $imm32, %TME_RECODE_X86_REG_CHAIN_RETURN_ADDRESS ! 1728: call chain_subs_call_{near|far}_{unconditional|conditional} ! 1729: jmp/jnc chain_fixup_call_{near|far} ! 1730: ! 1731: on an ia32 host, the $imm32 is the absolute address of the ! 1732: instructions thunk to chain to. on an x86-64 host, the $imm32 ! 1733: is the offset of the instructions thunk to chain to. on both ! 1734: hosts, before fixup, the $imm32 indicates the chain epilogue. ! 1735: fix up the $imm32: */ ! 1736: /* NB: chain_fixup points to the jump instruction. the call ! 1737: instruction is a call rel32, which is five bytes long, and the ! 1738: $imm32 is immediately before that: */ ! 1739: return_imm32 = TME_RECODE_X86_CHAIN_RETURN_ADDRESS(ic, insns_thunk_return); ! 1740: tme_recode_thunk_off_write(ic, ! 1741: (chain_fixup ! 1742: - (5 ! 1743: + sizeof(tme_uint32_t))), ! 1744: tme_uint32_t, ! 1745: return_imm32); ! 1746: } ! 1747: ! 1748: /* if this is a chain near: */ ! 1749: if ((chain_info ! 1750: & (TME_RECODE_CHAIN_INFO_NEAR ! 1751: | TME_RECODE_CHAIN_INFO_FAR)) ! 1752: == TME_RECODE_CHAIN_INFO_NEAR) { ! 1753: ! 1754: /* if this is an ia32 host: */ ! 1755: if (TME_RECODE_SIZE_HOST == TME_RECODE_SIZE_32) { ! 1756: ! 1757: /* advance the next instructions thunk offset past the chain ! 1758: in far checks: */ ! 1759: insns_thunk_next += TME_RECODE_IA32_CHAIN_IN_SIZE_FAR; ! 1760: } ! 1761: ! 1762: /* otherwise, this is an x86-64 host: */ ! 1763: else { ! 1764: ! 1765: /* read the first two bytes of the first instruction of the ! 1766: chain in. this instruction starts checking that the guest ! 1767: instruction source address matches the instructions thunk: */ ! 1768: insn_0_15 = *tme_recode_thunk_off_read(ic, insns_thunk_next, tme_uint16_t, insn_0_15_buffer); ! 1769: ! 1770: /* if this instruction is a cmpq $imm32, %reg: */ ! 1771: if (insn_0_15 == (tme_uint16_t) TME_RECODE_X86_INSN_CMP_Iz_REG_CHAIN_GUEST_SRC) { ! 1772: ! 1773: /* advance the next instructions thunk offset past the cmpq ! 1774: $imm32, %reg; jnz epilogue */ ! 1775: insns_thunk_next += TME_RECODE_X86_64_CHAIN_IN_SIZE_CMPQ_IMM32_JNZ; ! 1776: } ! 1777: ! 1778: /* if this instruction is an lea disp32(%ip), %reg: */ ! 1779: if (insn_0_15 == (tme_uint16_t) TME_RECODE_X86_INSN_LEA_DISP32_IP_REG_TLB_SCRATCH) { ! 1780: ! 1781: /* advance the next instructions thunk offset past the lea ! 1782: disp32(%ip), %reg ; cmpq %reg, %reg ; jnz epilogue */ ! 1783: insns_thunk_next += TME_RECODE_X86_64_CHAIN_IN_SIZE_LEA_IP_CMP_JNZ; ! 1784: } ! 1785: ! 1786: /* if this instruction is a movq $imm64, %reg: */ ! 1787: if (insn_0_15 == (tme_uint16_t) TME_RECODE_X86_INSN_MOVQ_IMM64_REG_TLB_SCRATCH) { ! 1788: ! 1789: /* advance the next instructions thunk offset past the movq ! 1790: $imm64, %reg ; cmpq %reg, %reg ; jnz epilogue */ ! 1791: insns_thunk_next += TME_RECODE_X86_64_CHAIN_IN_SIZE_MOVQ_CMP_JNZ; ! 1792: } ! 1793: ! 1794: /* if this instruction is a movl $imm32, %reg: */ ! 1795: if (((tme_uint8_t) insn_0_15) == TME_RECODE_X86_INSN_MOVL_IMM32_REG_TLB_SCRATCH) { ! 1796: ! 1797: /* advance the next instructions thunk offset past the movl ! 1798: $imm32, %reg ; cmpq %reg, %reg ; jnz epilogue */ ! 1799: insns_thunk_next += TME_RECODE_X86_64_CHAIN_IN_SIZE_MOVL_CMP_JNZ; ! 1800: } ! 1801: ! 1802: /* read the first two bytes of the next instruction of the chain ! 1803: in. this instruction starts checking that the guest ! 1804: instructions are still valid: */ ! 1805: insn_0_15 = *tme_recode_thunk_off_read(ic, insns_thunk_next, tme_uint16_t, insn_0_15_buffer); ! 1806: ! 1807: /* if this instruction is a testb $imm8, addr32: */ ! 1808: if (insn_0_15 == (tme_uint16_t) TME_RECODE_X86_INSN_TESTB_IMM8_ADDR32) { ! 1809: ! 1810: /* advance the next instructions thunk offset past the testb ! 1811: $imm8, addr32 ; jz epilogue */ ! 1812: insns_thunk_next += TME_RECODE_X86_64_CHAIN_IN_SIZE_TESTB_ADDR32_JZ; ! 1813: } ! 1814: ! 1815: /* if this instruction is a testb $imm8, disp32(%ip): */ ! 1816: if (insn_0_15 == (tme_uint16_t) TME_RECODE_X86_INSN_TESTB_IMM8_DISP32_IP) { ! 1817: ! 1818: /* advance the next instructions thunk offset past the testb ! 1819: $imm8, disp32(%ip) ; jz epilogue */ ! 1820: insns_thunk_next += TME_RECODE_X86_64_CHAIN_IN_SIZE_TESTB_BASE_JZ; ! 1821: } ! 1822: ! 1823: /* if this instruction is a testb $imm8, disp32(%reg): */ ! 1824: if (insn_0_15 == (tme_uint16_t) TME_RECODE_X86_INSN_TESTB_IMM8_DISP32_REG_CHAIN_GUEST_SRC) { ! 1825: ! 1826: /* advance the next instructions thunk offset past the testb ! 1827: $imm8, disp32(%reg) ; jz epilogue */ ! 1828: insns_thunk_next += TME_RECODE_X86_64_CHAIN_IN_SIZE_TESTB_BASE_JZ; ! 1829: } ! 1830: ! 1831: /* if this instruction is a movq $imm64, %reg: */ ! 1832: if (insn_0_15 == TME_RECODE_X86_INSN_MOVQ_IMM64_REG_TLB_SCRATCH) { ! 1833: ! 1834: /* advance the next instructions thunk offset past the movq ! 1835: $imm64, %reg ; testb $imm8, (%reg) ; jz epilogue */ ! 1836: insns_thunk_next += TME_RECODE_X86_64_CHAIN_IN_SIZE_MOVQ_TESTB_JZ; ! 1837: } ! 1838: } ! 1839: } ! 1840: ! 1841: /* all chain fixup instructions are unconditional or conditional ! 1842: jump instructions with 32-bit displacements at their ends. ! 1843: unconditional jump instructions are five bytes, and conditional ! 1844: jump instructions are six bytes because they are escaped: */ ! 1845: ! 1846: /* read the first opcode byte of the jump instruction: */ ! 1847: thunk_bytes = tme_recode_thunk_off_read(ic, chain_fixup, tme_uint8_t, opcode_buffer); ! 1848: ! 1849: /* advance the chain fixup offset to the jump instruction displacement: */ ! 1850: chain_fixup += (*thunk_bytes == TME_RECODE_X86_OPCODE_ESC_0F) + 1; ! 1851: ! 1852: /* rewrite the displacement for the instructions thunk: */ ! 1853: displacement = insns_thunk_next - (chain_fixup + sizeof(tme_int32_t)); ! 1854: tme_recode_thunk_off_write(ic, chain_fixup, tme_int32_t, displacement); ! 1855: ! 1856: /* return the next instructions thunk offset: */ ! 1857: return (insns_thunk_next); ! 1858: } ! 1859: ! 1860: #ifdef TME_RECODE_DEBUG ! 1861: #include <stdio.h> ! 1862: ! 1863: /* this host function dumps a chain thunk: */ ! 1864: void ! 1865: tme_recode_chain_thunk_dump(const struct tme_recode_ic *ic, ! 1866: const struct tme_recode_chain_thunk *chain_thunk) ! 1867: { ! 1868: tme_uint32_t chain_info; ! 1869: const char *s; ! 1870: tme_recode_thunk_off_t insns_thunk; ! 1871: ! 1872: printf(" x86 chain prologue: x/10i %p\n", ! 1873: chain_thunk->tme_recode_x86_chain_thunk_prologue); ! 1874: for (chain_info = 0; ! 1875: chain_info <= (TME_RECODE_CHAIN_INFO_UNCONDITIONAL ! 1876: | TME_RECODE_CHAIN_INFO_CONDITIONAL ! 1877: | TME_RECODE_CHAIN_INFO_NEAR ! 1878: | TME_RECODE_CHAIN_INFO_FAR ! 1879: | TME_RECODE_CHAIN_INFO_JUMP ! 1880: | TME_RECODE_CHAIN_INFO_RETURN ! 1881: | TME_RECODE_CHAIN_INFO_CALL); ! 1882: chain_info++) { ! 1883: switch (chain_info ! 1884: & (TME_RECODE_CHAIN_INFO_JUMP ! 1885: | TME_RECODE_CHAIN_INFO_RETURN ! 1886: | TME_RECODE_CHAIN_INFO_CALL)) { ! 1887: case TME_RECODE_CHAIN_INFO_JUMP: s = "jump"; break; ! 1888: case TME_RECODE_CHAIN_INFO_RETURN: ! 1889: s = "return"; ! 1890: if ((chain_info & TME_RECODE_CHAIN_INFO_FAR) == 0) { ! 1891: continue; ! 1892: } ! 1893: break; ! 1894: case TME_RECODE_CHAIN_INFO_CALL: s = "call"; break; ! 1895: default: continue; ! 1896: } ! 1897: insns_thunk = TME_RECODE_X86_CHAIN_SUBS(chain_thunk, chain_info); ! 1898: printf(" x86 chain %s %s %s subs: x/10i %p\n", ! 1899: s, ! 1900: (chain_info & TME_RECODE_CHAIN_INFO_FAR ! 1901: ? "far" ! 1902: : "near"), ! 1903: (chain_info & TME_RECODE_CHAIN_INFO_CONDITIONAL ! 1904: ? "conditional" ! 1905: : "unconditional"), ! 1906: tme_recode_thunk_off_to_pointer(ic, ! 1907: insns_thunk, ! 1908: void (*)(void))); ! 1909: } ! 1910: insns_thunk ! 1911: = TME_RECODE_X86_CHAIN_FIXUP_TARGET_ALTERNATE(ic, ! 1912: (TME_RECODE_CHAIN_INFO_JUMP ! 1913: + TME_RECODE_CHAIN_INFO_ALTERNATE_NEAR)); ! 1914: printf(" x86 chain fixup chain jump alternate near: x/10i %p\n", ! 1915: tme_recode_thunk_off_to_pointer(ic, ! 1916: insns_thunk, ! 1917: void (*)(void))); ! 1918: insns_thunk ! 1919: = TME_RECODE_X86_CHAIN_FIXUP_TARGET(ic, ! 1920: (TME_RECODE_CHAIN_INFO_JUMP ! 1921: + TME_RECODE_CHAIN_INFO_NEAR)); ! 1922: printf(" x86 chain fixup chain jump near, chain return alternate near: x/10i %p\n", ! 1923: tme_recode_thunk_off_to_pointer(ic, ! 1924: insns_thunk, ! 1925: void (*)(void))); ! 1926: insns_thunk ! 1927: = TME_RECODE_X86_CHAIN_FIXUP_TARGET_ALTERNATE(ic, ! 1928: (TME_RECODE_CHAIN_INFO_JUMP ! 1929: + TME_RECODE_CHAIN_INFO_ALTERNATE_FAR)); ! 1930: printf(" x86 chain fixup chain jump alternate far: x/3i %p\n", ! 1931: tme_recode_thunk_off_to_pointer(ic, ! 1932: insns_thunk, ! 1933: void (*)(void))); ! 1934: insns_thunk ! 1935: = TME_RECODE_X86_CHAIN_FIXUP_TARGET(ic, ! 1936: (TME_RECODE_CHAIN_INFO_JUMP ! 1937: + TME_RECODE_CHAIN_INFO_FAR)); ! 1938: printf(" x86 chain fixup chain jump far: x/2i %p\n", ! 1939: tme_recode_thunk_off_to_pointer(ic, ! 1940: insns_thunk, ! 1941: void (*)(void))); ! 1942: insns_thunk ! 1943: = TME_RECODE_X86_CHAIN_FIXUP_TARGET(ic, ! 1944: (TME_RECODE_CHAIN_INFO_CALL ! 1945: + TME_RECODE_CHAIN_INFO_FAR)); ! 1946: printf(" x86 chain fixup chain call far: x/2i %p\n", ! 1947: tme_recode_thunk_off_to_pointer(ic, ! 1948: insns_thunk, ! 1949: void (*)(void))); ! 1950: insns_thunk ! 1951: = TME_RECODE_X86_CHAIN_FIXUP_TARGET(ic, ! 1952: (TME_RECODE_CHAIN_INFO_CALL ! 1953: + TME_RECODE_CHAIN_INFO_NEAR)); ! 1954: printf(" x86 chain fixup chain call near: x/2i %p\n", ! 1955: tme_recode_thunk_off_to_pointer(ic, ! 1956: insns_thunk, ! 1957: void (*)(void))); ! 1958: } ! 1959: ! 1960: #endif /* TME_RECODE_DEBUG */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.