rts.S (7376B)
1 // 2 // Assembly-language runtime support for translated vx32 code. 3 // 4 5 #include "libvx32/asm.h" 6 #include "libvx32/os.h" 7 8 9 // The method we use to get back out to host code differs for 32/64-bit hosts. 10 #ifdef __i386 11 #ifdef __APPLE__ 12 #define RETURN jmp vxrun_return_stub 13 .section __IMPORT,__jump_table,symbol_stubs,self_modifying_code+pure_instructions,5 14 vxrun_return_stub: 15 .indirect_symbol EXT(vxrun_return) 16 hlt ; hlt ; hlt ; hlt ; hlt 17 #else // i386, not APPLE 18 #define RETURN jmp EXT(vxrun_return) 19 #endif // i386, not APPLE 20 #else // x86-64 21 #define RETURN ljmpl *VSEG:VXEMU_RETPTR 22 #endif 23 24 25 .text 26 27 // All the following runtime support code is 32-bit 28 // (even on 64-bit hosts). 29 .code32 30 31 .globl EXT(vx_rts_S_start) 32 EXT(vx_rts_S_start): 33 34 35 // Look up a vx32 EIP and jump to the corresponding translated code entrypoint, 36 // then back-patching the calling instruction to point to the translated code. 37 // 38 // Called from trampolines embedded out-of-line in translated code. 39 // The inline jmp/jcc instruction looks like this: 40 // jmp 2f // ALWAYS uses a rel32, not a rel8 41 // 1: 42 // 43 // A translated call instruction is the same, except prefixed by: 44 // pushl $<return_eip> 45 // 46 // Then the out-of-line trampoline code looks like this: 47 // 2: movl $3f,VSEG:vx_jmpinfo 48 // jmp vx_lookup_backpatch 49 // 3: .long dest_eip 50 // .long 3b-1b // offset of jmp inst to backpatch 51 // 52 .globl EXT(vxrun_lookup_backpatch) 53 EXT(vxrun_lookup_backpatch): 54 55 // Save registers we'll need, and load the jump information. 56 movl %edx,VSEG:VXEMU_EDX 57 movl VSEG:VXEMU_JMPINFO,%edx // edx: jmpinfo pointer 58 movl %ebx,VSEG:VXEMU_EBX 59 movl VSEG:(%edx),%ebx // ebx: target eip 60 movl %ecx,VSEG:VXEMU_ECX 61 movl %ebx,%ecx // ecx: target eip 62 movl %eax,VSEG:VXEMU_EAX 63 64 // Save condition codes (except for DF, which we don't modify here). 65 // We use LAHF and SAHF instead of pushf/popf because the latter 66 // would require us to reload the stack segment, which is slow. 67 // But unfortunately the OF is not in the low 8 bits of EFLAGS 68 // covered by LAHF/SAHF, so we have to save that flag separately. 69 lahf // Copy low 8 bits of EFLAGS into AH 70 seto %al // Set %al to 0x00 or 0xff according to OF 71 72 // Hash the vx32 target EIP into ecx. 73 shrl $10,%ecx 74 addl %ebx,%ecx 75 shrl $10,%ecx 76 subl %ebx,%ecx 77 78 1: // Look up the appropriate hash table entry 79 andl VSEG:VXEMU_ETABMASK,%ecx 80 cmpl VSEG:VXEMU_ETAB(,%ecx,8),%ebx 81 jne 2f 82 83 // Found the correct entry! 84 // Get the translated code entrypoint into ebx. 85 movl VSEG:VXEMU_ETAB+4(,%ecx,8),%ebx 86 87 // Backpatch the original jmp instruction with this translated target. 88 // %edx still points to the jmpinfo structure. 89 movl VSEG:4(%edx),%edx // find end of original jmp insn 90 lea 7(%ebx),%ecx // skip target's load-ebx prolog 91 subl %edx,%ecx // form 32-bit relative jmp target 92 subl VSEG:VXEMU_EMUPTR,%edx // form vxemu-relative jmp insn offset 93 movl %ecx,VSEG:-4(%edx) // patch jmp insn 94 95 // Restore condition codes. 96 shrb $1,%al // Restore overflow flag 97 sahf // Restore low 8 bits of EFLAGS 98 99 // Restore other registers 100 movl VSEG:VXEMU_EAX,%eax 101 movl VSEG:VXEMU_ECX,%ecx 102 movl VSEG:VXEMU_EDX,%edx 103 104 // Jump to appropriate translated code entrypoint. 105 // The translated code will restore ebx before doing anything else. 106 jmp *%ebx 107 108 2: // Not the correct entry - walk forward until we find 109 // the correct entry or the first empty one. 110 incl %ecx 111 cmpl $-1,VSEG:VXEMU_ETAB-8(,%ecx,8) // XX NULLSRCEIP 112 jne 1b 113 114 3: // No correct entry - drop back to C to translate more code. 115 116 // Save EIP that we were trying to jump to 117 movl %ebx,VSEG:VXEMU_EIP 118 119 // Restore condition codes 120 shrb $1,%al // Restore overflow flag 121 sahf // Restore low 8 bits of EFLAGS 122 123 // Indicate that we're returning due to a translation miss 124 movl $0,%eax // Careful not to trash condition codes 125 126 // Return to host code. 127 RETURN 128 129 130 131 // Look up a vx32 EIP and jump to the corresponding translated code entrypoint, 132 // without backpatching. Used to handle indirect jmps and calls. 133 // 134 // The generated code for an indirect jump looks basically like this: 135 // movl %ebx,VSEG:VXEMU_EBX 136 // movl <indirect_ea>,%ebx 137 // jmp vx_lookup_indirect 138 // 139 // The generated code for an indirect call is as follows: 140 // movl %ebx,VSEG:VXEMU_EBX 141 // movl <indirect_ea>,%ebx 142 // pushl $<return_eip> 143 // jmp vx_lookup_indirect 144 // 145 // Finally, the code generated for a RET instruction looks like this: 146 // movl %ebx,VSEG:VXEMU_EBX 147 // popl %ebx 148 // jmp vx_lookup_indirect 149 // 150 .p2align 4 151 .globl EXT(vxrun_lookup_indirect) 152 EXT(vxrun_lookup_indirect): 153 154 // Save more registers we'll need 155 movl %eax,VSEG:VXEMU_EAX 156 movl %ecx,VSEG:VXEMU_ECX 157 158 // Save condition codes (except for DF, which we don't modify here). 159 // We use LAHF and SAHF instead of pushf/popf because the latter 160 // would require us to reload the stack segment, which is slow. 161 // But unfortunately the OF is not in the low 8 bits of EFLAGS 162 // covered by LAHF/SAHF, so we have to save that flag separately. 163 lahf // Copy low 8 bits of EFLAGS into AH 164 seto %al // Set %al to 0x00 or 0xff according to OF 165 166 // Hash the vx32 EIP into ecx. 167 movl %ebx,%ecx 168 shrl $10,%ecx 169 addl %ebx,%ecx 170 shrl $10,%ecx 171 subl %ebx,%ecx 172 173 1: // Look up the appropriate hash table entry 174 andl VSEG:VXEMU_ETABMASK,%ecx 175 cmpl VSEG:VXEMU_ETAB(,%ecx,8),%ebx 176 jne 2f 177 178 // Found the correct entry! 179 // Get the translated code entrypoint into ebx. 180 movl VSEG:VXEMU_ETAB+4(,%ecx,8),%ebx 181 182 // Restore condition codes. 183 shrb $1,%al // Restore overflow flag 184 sahf // Restore low 8 bits of EFLAGS 185 186 // Restore other registers 187 movl VSEG:VXEMU_EAX,%eax 188 movl VSEG:VXEMU_ECX,%ecx 189 190 // Jump to appropriate translated code entrypoint. 191 // The translated code will restore ebx before doing anything else. 192 jmp *%ebx 193 194 2: // Not the correct entry - walk forward until we find 195 // the correct entry or the first empty one. 196 incl %ecx 197 cmpl $-1,VSEG:VXEMU_ETAB-8(,%ecx,8) // XX NULLSRCEIP 198 jne 1b 199 200 3: // No correct entry - drop back to C to translate more code. 201 movl %edx,VSEG:VXEMU_EDX 202 203 // Save EIP that we were trying to jump to 204 movl %ebx,VSEG:VXEMU_EIP 205 206 // Restore condition codes 207 shrb $1,%al // Restore overflow flag 208 sahf // Restore low 8 bits of EFLAGS 209 210 // Indicate that we're returning due to a translation miss 211 movl $0,%eax // Careful not to trash condition codes 212 213 // Jump to host code. 214 RETURN 215 216 217 // Return from running translated code, generating a VX trap. 218 // Assumes the environment's EAX has already been saved 219 // and EAX now contains the trap code to return. 220 .p2align 4 221 .globl EXT(vxrun_gentrap) 222 EXT(vxrun_gentrap): 223 gentrap: 224 movl %ebx,VSEG:VXEMU_EBX 225 movl %ecx,VSEG:VXEMU_ECX 226 movl %edx,VSEG:VXEMU_EDX 227 // vxrun_return will save the others 228 229 // Besides returning it, also record trap number in emu struct. 230 movl %eax,VSEG:VXEMU_TRAPNO 231 232 RETURN 233 234 235 // Special "pseudo-fragment" entrypoint used as the dstip 236 // for unused entries in the entrypoint hash table. 237 // The corresponding srcip in unused entries is -1, an invalid VX address. 238 // This way we can keep the unused entry check 239 // off the critical "cache hit" path for collision-free entrypoint lookups: 240 // if VX code does jump to address -1, it'll just wind up here. 241 .globl EXT(vxrun_nullfrag) 242 EXT(vxrun_nullfrag): 243 movl VSEG:VXEMU_EBX,%ebx // standard fragment prolog 244 movl %eax,VSEG:VXEMU_EAX // standard trap generation code 245 movl $0x106,%eax // VXTRAP_INVALID 246 jmp gentrap 247 248 249 .globl EXT(vx_rts_S_end) 250 EXT(vx_rts_S_end):