1 | // Licensed to the .NET Foundation under one or more agreements. |
2 | // The .NET Foundation licenses this file to you under the MIT license. |
3 | // See the LICENSE file in the project root for more information. |
4 | // |
5 | // Various helper routines for generating AMD64 assembly code. |
6 | // |
7 | |
8 | // Precompiled Header |
9 | |
10 | #include "common.h" |
11 | |
12 | #include "stublink.h" |
13 | #include "cgensys.h" |
14 | #include "siginfo.hpp" |
15 | #include "excep.h" |
16 | #include "ecall.h" |
17 | #include "dllimport.h" |
18 | #include "dllimportcallback.h" |
19 | #include "dbginterface.h" |
20 | #include "fcall.h" |
21 | #include "array.h" |
22 | #include "virtualcallstub.h" |
23 | #include "jitinterface.h" |
24 | |
25 | #ifdef FEATURE_COMINTEROP |
26 | #include "clrtocomcall.h" |
27 | #endif // FEATURE_COMINTEROP |
28 | |
29 | void UpdateRegDisplayFromCalleeSavedRegisters(REGDISPLAY * pRD, CalleeSavedRegisters * pRegs) |
30 | { |
31 | LIMITED_METHOD_CONTRACT; |
32 | |
33 | T_CONTEXT * pContext = pRD->pCurrentContext; |
34 | #define CALLEE_SAVED_REGISTER(regname) pContext->regname = pRegs->regname; |
35 | ENUM_CALLEE_SAVED_REGISTERS(); |
36 | #undef CALLEE_SAVED_REGISTER |
37 | |
38 | KNONVOLATILE_CONTEXT_POINTERS * pContextPointers = pRD->pCurrentContextPointers; |
39 | #define CALLEE_SAVED_REGISTER(regname) pContextPointers->regname = (PULONG64)&pRegs->regname; |
40 | ENUM_CALLEE_SAVED_REGISTERS(); |
41 | #undef CALLEE_SAVED_REGISTER |
42 | } |
43 | |
44 | void ClearRegDisplayArgumentAndScratchRegisters(REGDISPLAY * pRD) |
45 | { |
46 | LIMITED_METHOD_CONTRACT; |
47 | |
48 | KNONVOLATILE_CONTEXT_POINTERS * pContextPointers = pRD->pCurrentContextPointers; |
49 | pContextPointers->Rax = NULL; |
50 | #ifdef UNIX_AMD64_ABI |
51 | pContextPointers->Rsi = NULL; |
52 | pContextPointers->Rdi = NULL; |
53 | #endif |
54 | pContextPointers->Rcx = NULL; |
55 | pContextPointers->Rdx = NULL; |
56 | pContextPointers->R8 = NULL; |
57 | pContextPointers->R9 = NULL; |
58 | pContextPointers->R10 = NULL; |
59 | pContextPointers->R11 = NULL; |
60 | } |
61 | |
62 | void TransitionFrame::UpdateRegDisplay(const PREGDISPLAY pRD) |
63 | { |
64 | LIMITED_METHOD_CONTRACT; |
65 | |
66 | pRD->IsCallerContextValid = FALSE; |
67 | pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary. |
68 | |
69 | pRD->pCurrentContext->Rip = GetReturnAddress(); |
70 | pRD->pCurrentContext->Rsp = GetSP(); |
71 | |
72 | UpdateRegDisplayFromCalleeSavedRegisters(pRD, GetCalleeSavedRegisters()); |
73 | ClearRegDisplayArgumentAndScratchRegisters(pRD); |
74 | |
75 | SyncRegDisplayToCurrentContext(pRD); |
76 | |
77 | LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK TransitionFrame::UpdateRegDisplay(rip:%p, rsp:%p)\n" , pRD->ControlPC, pRD->SP)); |
78 | } |
79 | |
80 | #ifndef DACCESS_COMPILE |
81 | |
82 | extern "C" TADDR s_pStubHelperFrameVPtr; |
83 | TADDR s_pStubHelperFrameVPtr = StubHelperFrame::GetMethodFrameVPtr(); |
84 | |
85 | void TailCallFrame::InitFromContext(T_CONTEXT * pContext) |
86 | { |
87 | WRAPPER_NO_CONTRACT; |
88 | |
89 | #define CALLEE_SAVED_REGISTER(regname) m_calleeSavedRegisters.regname = pContext->regname; |
90 | ENUM_CALLEE_SAVED_REGISTERS(); |
91 | #undef CALLEE_SAVED_REGISTER |
92 | |
93 | m_pGCLayout = 0; |
94 | m_ReturnAddress = pContext->Rip; |
95 | } |
96 | |
97 | #endif // !DACCESS_COMPILE |
98 | |
99 | void TailCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD) |
100 | { |
101 | LIMITED_METHOD_CONTRACT; |
102 | |
103 | pRD->IsCallerContextValid = FALSE; |
104 | pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary. |
105 | |
106 | pRD->pCurrentContext->Rip = m_ReturnAddress; |
107 | pRD->pCurrentContext->Rsp = dac_cast<TADDR>(this) + sizeof(*this); |
108 | |
109 | UpdateRegDisplayFromCalleeSavedRegisters(pRD, &m_calleeSavedRegisters); |
110 | ClearRegDisplayArgumentAndScratchRegisters(pRD); |
111 | |
112 | SyncRegDisplayToCurrentContext(pRD); |
113 | |
114 | LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK TransitionFrame::UpdateRegDisplay(rip:%p, rsp:%p)\n" , pRD->ControlPC, pRD->SP)); |
115 | } |
116 | |
117 | void InlinedCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD) |
118 | { |
119 | CONTRACTL |
120 | { |
121 | NOTHROW; |
122 | GC_NOTRIGGER; |
123 | #ifdef PROFILING_SUPPORTED |
124 | PRECONDITION(CORProfilerStackSnapshotEnabled() || InlinedCallFrame::FrameHasActiveCall(this)); |
125 | #endif |
126 | HOST_NOCALLS; |
127 | MODE_ANY; |
128 | SUPPORTS_DAC; |
129 | } |
130 | CONTRACTL_END; |
131 | |
132 | if (!InlinedCallFrame::FrameHasActiveCall(this)) |
133 | { |
134 | LOG((LF_CORDB, LL_ERROR, "WARNING: InlinedCallFrame::UpdateRegDisplay called on inactive frame %p\n" , this)); |
135 | return; |
136 | } |
137 | |
138 | pRD->IsCallerContextValid = FALSE; |
139 | pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary. |
140 | |
141 | pRD->pCurrentContext->Rip = *(DWORD64 *)&m_pCallerReturnAddress; |
142 | pRD->pCurrentContext->Rsp = *(DWORD64 *)&m_pCallSiteSP; |
143 | pRD->pCurrentContext->Rbp = *(DWORD64 *)&m_pCalleeSavedFP; |
144 | |
145 | ClearRegDisplayArgumentAndScratchRegisters(pRD); |
146 | |
147 | #define CALLEE_SAVED_REGISTER(regname) pRD->pCurrentContextPointers->regname = NULL; |
148 | ENUM_CALLEE_SAVED_REGISTERS(); |
149 | #undef CALLEE_SAVED_REGISTER |
150 | |
151 | pRD->pCurrentContextPointers->Rbp = (DWORD64 *)&m_pCalleeSavedFP; |
152 | |
153 | SyncRegDisplayToCurrentContext(pRD); |
154 | |
155 | LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK InlinedCallFrame::UpdateRegDisplay(rip:%p, rsp:%p)\n" , pRD->ControlPC, pRD->SP)); |
156 | } |
157 | |
158 | void HelperMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD) |
159 | { |
160 | CONTRACTL |
161 | { |
162 | NOTHROW; |
163 | GC_NOTRIGGER; |
164 | MODE_ANY; |
165 | PRECONDITION(m_MachState._pRetAddr == PTR_TADDR(&m_MachState.m_Rip)); |
166 | SUPPORTS_DAC; |
167 | } |
168 | CONTRACTL_END; |
169 | |
170 | pRD->IsCallerContextValid = FALSE; |
171 | pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary. |
172 | |
173 | // |
174 | // Copy the saved state from the frame to the current context. |
175 | // |
176 | |
177 | LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK HelperMethodFrame::UpdateRegDisplay cached ip:%p, sp:%p\n" , m_MachState.m_Rip, m_MachState.m_Rsp)); |
178 | |
179 | #if defined(DACCESS_COMPILE) |
180 | // For DAC, we may get here when the HMF is still uninitialized. |
181 | // So we may need to unwind here. |
182 | if (!m_MachState.isValid()) |
183 | { |
184 | // This allocation throws on OOM. |
185 | MachState* pUnwoundState = (MachState*)DacAllocHostOnlyInstance(sizeof(*pUnwoundState), true); |
186 | |
187 | InsureInit(false, pUnwoundState); |
188 | |
189 | pRD->pCurrentContext->Rip = pRD->ControlPC = pUnwoundState->m_Rip; |
190 | pRD->pCurrentContext->Rsp = pRD->SP = pUnwoundState->m_Rsp; |
191 | |
192 | #define CALLEE_SAVED_REGISTER(regname) pRD->pCurrentContext->regname = pUnwoundState->m_Capture.regname; |
193 | ENUM_CALLEE_SAVED_REGISTERS(); |
194 | #undef CALLEE_SAVED_REGISTER |
195 | |
196 | #define CALLEE_SAVED_REGISTER(regname) pRD->pCurrentContextPointers->regname = pUnwoundState->m_Ptrs.p##regname; |
197 | ENUM_CALLEE_SAVED_REGISTERS(); |
198 | #undef CALLEE_SAVED_REGISTER |
199 | |
200 | ClearRegDisplayArgumentAndScratchRegisters(pRD); |
201 | |
202 | return; |
203 | } |
204 | #endif // DACCESS_COMPILE |
205 | |
206 | pRD->pCurrentContext->Rip = pRD->ControlPC = m_MachState.m_Rip; |
207 | pRD->pCurrentContext->Rsp = pRD->SP = m_MachState.m_Rsp; |
208 | |
209 | #ifdef FEATURE_PAL |
210 | |
211 | #define CALLEE_SAVED_REGISTER(regname) pRD->pCurrentContext->regname = (m_MachState.m_Ptrs.p##regname != NULL) ? \ |
212 | *m_MachState.m_Ptrs.p##regname : m_MachState.m_Unwound.regname; |
213 | ENUM_CALLEE_SAVED_REGISTERS(); |
214 | #undef CALLEE_SAVED_REGISTER |
215 | |
216 | #else // FEATURE_PAL |
217 | |
218 | #define CALLEE_SAVED_REGISTER(regname) pRD->pCurrentContext->regname = *m_MachState.m_Ptrs.p##regname; |
219 | ENUM_CALLEE_SAVED_REGISTERS(); |
220 | #undef CALLEE_SAVED_REGISTER |
221 | |
222 | #endif // FEATURE_PAL |
223 | |
224 | #define CALLEE_SAVED_REGISTER(regname) pRD->pCurrentContextPointers->regname = m_MachState.m_Ptrs.p##regname; |
225 | ENUM_CALLEE_SAVED_REGISTERS(); |
226 | #undef CALLEE_SAVED_REGISTER |
227 | |
228 | // |
229 | // Clear all knowledge of scratch registers. We're skipping to any |
230 | // arbitrary point on the stack, and frames aren't required to preserve or |
231 | // keep track of these anyways. |
232 | // |
233 | |
234 | ClearRegDisplayArgumentAndScratchRegisters(pRD); |
235 | } |
236 | |
237 | void FaultingExceptionFrame::UpdateRegDisplay(const PREGDISPLAY pRD) |
238 | { |
239 | LIMITED_METHOD_DAC_CONTRACT; |
240 | |
241 | memcpy(pRD->pCurrentContext, &m_ctx, sizeof(CONTEXT)); |
242 | |
243 | pRD->ControlPC = m_ctx.Rip; |
244 | |
245 | pRD->SP = m_ctx.Rsp; |
246 | |
247 | pRD->pCurrentContextPointers->Rax = &m_ctx.Rax; |
248 | pRD->pCurrentContextPointers->Rcx = &m_ctx.Rcx; |
249 | pRD->pCurrentContextPointers->Rdx = &m_ctx.Rdx; |
250 | pRD->pCurrentContextPointers->Rbx = &m_ctx.Rbx; |
251 | pRD->pCurrentContextPointers->Rbp = &m_ctx.Rbp; |
252 | pRD->pCurrentContextPointers->Rsi = &m_ctx.Rsi; |
253 | pRD->pCurrentContextPointers->Rdi = &m_ctx.Rdi; |
254 | pRD->pCurrentContextPointers->R8 = &m_ctx.R8; |
255 | pRD->pCurrentContextPointers->R9 = &m_ctx.R9; |
256 | pRD->pCurrentContextPointers->R10 = &m_ctx.R10; |
257 | pRD->pCurrentContextPointers->R11 = &m_ctx.R11; |
258 | pRD->pCurrentContextPointers->R12 = &m_ctx.R12; |
259 | pRD->pCurrentContextPointers->R13 = &m_ctx.R13; |
260 | pRD->pCurrentContextPointers->R14 = &m_ctx.R14; |
261 | pRD->pCurrentContextPointers->R15 = &m_ctx.R15; |
262 | |
263 | pRD->IsCallerContextValid = FALSE; |
264 | pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary. |
265 | } |
266 | |
267 | #ifdef FEATURE_HIJACK |
268 | TADDR ResumableFrame::GetReturnAddressPtr() |
269 | { |
270 | LIMITED_METHOD_DAC_CONTRACT; |
271 | return dac_cast<TADDR>(m_Regs) + offsetof(CONTEXT, Rip); |
272 | } |
273 | |
274 | void ResumableFrame::UpdateRegDisplay(const PREGDISPLAY pRD) |
275 | { |
276 | CONTRACT_VOID |
277 | { |
278 | NOTHROW; |
279 | GC_NOTRIGGER; |
280 | MODE_ANY; |
281 | SUPPORTS_DAC; |
282 | } |
283 | CONTRACT_END; |
284 | |
285 | CopyMemory(pRD->pCurrentContext, m_Regs, sizeof(CONTEXT)); |
286 | |
287 | pRD->ControlPC = m_Regs->Rip; |
288 | |
289 | pRD->SP = m_Regs->Rsp; |
290 | |
291 | pRD->pCurrentContextPointers->Rax = &m_Regs->Rax; |
292 | pRD->pCurrentContextPointers->Rcx = &m_Regs->Rcx; |
293 | pRD->pCurrentContextPointers->Rdx = &m_Regs->Rdx; |
294 | pRD->pCurrentContextPointers->Rbx = &m_Regs->Rbx; |
295 | pRD->pCurrentContextPointers->Rbp = &m_Regs->Rbp; |
296 | pRD->pCurrentContextPointers->Rsi = &m_Regs->Rsi; |
297 | pRD->pCurrentContextPointers->Rdi = &m_Regs->Rdi; |
298 | pRD->pCurrentContextPointers->R8 = &m_Regs->R8; |
299 | pRD->pCurrentContextPointers->R9 = &m_Regs->R9; |
300 | pRD->pCurrentContextPointers->R10 = &m_Regs->R10; |
301 | pRD->pCurrentContextPointers->R11 = &m_Regs->R11; |
302 | pRD->pCurrentContextPointers->R12 = &m_Regs->R12; |
303 | pRD->pCurrentContextPointers->R13 = &m_Regs->R13; |
304 | pRD->pCurrentContextPointers->R14 = &m_Regs->R14; |
305 | pRD->pCurrentContextPointers->R15 = &m_Regs->R15; |
306 | |
307 | pRD->IsCallerContextValid = FALSE; |
308 | pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary. |
309 | |
310 | RETURN; |
311 | } |
312 | |
313 | // The HijackFrame has to know the registers that are pushed by OnHijackTripThread |
314 | void HijackFrame::UpdateRegDisplay(const PREGDISPLAY pRD) |
315 | { |
316 | CONTRACTL { |
317 | NOTHROW; |
318 | GC_NOTRIGGER; |
319 | SUPPORTS_DAC; |
320 | } |
321 | CONTRACTL_END; |
322 | |
323 | pRD->IsCallerContextValid = FALSE; |
324 | pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary. |
325 | |
326 | pRD->pCurrentContext->Rip = m_ReturnAddress; |
327 | pRD->pCurrentContext->Rsp = PTR_TO_MEMBER_TADDR(HijackArgs, m_Args, Rip) + sizeof(void *); |
328 | |
329 | UpdateRegDisplayFromCalleeSavedRegisters(pRD, &(m_Args->Regs)); |
330 | |
331 | #ifdef UNIX_AMD64_ABI |
332 | pRD->pCurrentContextPointers->Rsi = NULL; |
333 | pRD->pCurrentContextPointers->Rdi = NULL; |
334 | #endif |
335 | pRD->pCurrentContextPointers->Rcx = NULL; |
336 | #ifdef UNIX_AMD64_ABI |
337 | pRD->pCurrentContextPointers->Rdx = (PULONG64)&m_Args->Rdx; |
338 | #else // UNIX_AMD64_ABI |
339 | pRD->pCurrentContextPointers->Rdx = NULL; |
340 | #endif // UNIX_AMD64_ABI |
341 | pRD->pCurrentContextPointers->R8 = NULL; |
342 | pRD->pCurrentContextPointers->R9 = NULL; |
343 | pRD->pCurrentContextPointers->R10 = NULL; |
344 | pRD->pCurrentContextPointers->R11 = NULL; |
345 | |
346 | pRD->pCurrentContextPointers->Rax = (PULONG64)&m_Args->Rax; |
347 | |
348 | SyncRegDisplayToCurrentContext(pRD); |
349 | |
350 | /* |
351 | // This only describes the top-most frame |
352 | pRD->pContext = NULL; |
353 | |
354 | |
355 | pRD->PCTAddr = dac_cast<TADDR>(m_Args) + offsetof(HijackArgs, Rip); |
356 | //pRD->pPC = PTR_SLOT(pRD->PCTAddr); |
357 | pRD->SP = (ULONG64)(pRD->PCTAddr + sizeof(TADDR)); |
358 | */ |
359 | } |
360 | #endif // FEATURE_HIJACK |
361 | |
362 | BOOL isJumpRel32(PCODE pCode) |
363 | { |
364 | CONTRACTL { |
365 | NOTHROW; |
366 | GC_NOTRIGGER; |
367 | SO_TOLERANT; |
368 | SUPPORTS_DAC; |
369 | } CONTRACTL_END; |
370 | |
371 | PTR_BYTE pbCode = PTR_BYTE(pCode); |
372 | |
373 | return 0xE9 == pbCode[0]; |
374 | } |
375 | |
376 | // |
377 | // Given the same pBuffer that was used by emitJump this |
378 | // method decodes the instructions and returns the jump target |
379 | // |
380 | PCODE decodeJump32(PCODE pBuffer) |
381 | { |
382 | CONTRACTL |
383 | { |
384 | NOTHROW; |
385 | GC_NOTRIGGER; |
386 | SO_TOLERANT; |
387 | SUPPORTS_DAC; |
388 | } |
389 | CONTRACTL_END; |
390 | |
391 | // jmp rel32 |
392 | _ASSERTE(isJumpRel32(pBuffer)); |
393 | |
394 | return rel32Decode(pBuffer+1); |
395 | } |
396 | |
397 | BOOL isJumpRel64(PCODE pCode) |
398 | { |
399 | CONTRACTL { |
400 | NOTHROW; |
401 | GC_NOTRIGGER; |
402 | SO_TOLERANT; |
403 | SUPPORTS_DAC; |
404 | } CONTRACTL_END; |
405 | |
406 | PTR_BYTE pbCode = PTR_BYTE(pCode); |
407 | |
408 | return 0x48 == pbCode[0] && |
409 | 0xB8 == pbCode[1] && |
410 | 0xFF == pbCode[10] && |
411 | 0xE0 == pbCode[11]; |
412 | } |
413 | |
414 | PCODE decodeJump64(PCODE pBuffer) |
415 | { |
416 | CONTRACTL |
417 | { |
418 | NOTHROW; |
419 | GC_NOTRIGGER; |
420 | SO_TOLERANT; |
421 | SUPPORTS_DAC; |
422 | } |
423 | CONTRACTL_END; |
424 | |
425 | // mov rax, xxx |
426 | // jmp rax |
427 | _ASSERTE(isJumpRel64(pBuffer)); |
428 | |
429 | return *PTR_UINT64(pBuffer+2); |
430 | } |
431 | |
432 | #ifdef DACCESS_COMPILE |
433 | BOOL GetAnyThunkTarget (CONTEXT *pctx, TADDR *pTarget, TADDR *pTargetMethodDesc) |
434 | { |
435 | TADDR pThunk = GetIP(pctx); |
436 | |
437 | *pTargetMethodDesc = NULL; |
438 | |
439 | // |
440 | // Check for something generated by emitJump. |
441 | // |
442 | if (isJumpRel64(pThunk)) |
443 | { |
444 | *pTarget = decodeJump64(pThunk); |
445 | return TRUE; |
446 | } |
447 | |
448 | return FALSE; |
449 | } |
450 | #endif // DACCESS_COMPILE |
451 | |
452 | |
453 | #ifndef DACCESS_COMPILE |
454 | |
455 | // Note: This is only used on server GC on Windows. |
456 | // |
457 | // This function returns the number of logical processors on a given physical chip. If it cannot |
458 | // determine the number of logical cpus, or the machine is not populated uniformly with the same |
459 | // type of processors, this function returns 1. |
460 | |
461 | void EncodeLoadAndJumpThunk (LPBYTE pBuffer, LPVOID pv, LPVOID pTarget) |
462 | { |
463 | CONTRACTL |
464 | { |
465 | THROWS; |
466 | GC_NOTRIGGER; |
467 | MODE_ANY; |
468 | |
469 | PRECONDITION(CheckPointer(pBuffer)); |
470 | } |
471 | CONTRACTL_END; |
472 | |
473 | // mov r10, pv 49 ba xx xx xx xx xx xx xx xx |
474 | |
475 | pBuffer[0] = 0x49; |
476 | pBuffer[1] = 0xBA; |
477 | |
478 | *((UINT64 UNALIGNED *)&pBuffer[2]) = (UINT64)pv; |
479 | |
480 | // mov rax, pTarget 48 b8 xx xx xx xx xx xx xx xx |
481 | |
482 | pBuffer[10] = 0x48; |
483 | pBuffer[11] = 0xB8; |
484 | |
485 | *((UINT64 UNALIGNED *)&pBuffer[12]) = (UINT64)pTarget; |
486 | |
487 | // jmp rax ff e0 |
488 | |
489 | pBuffer[20] = 0xFF; |
490 | pBuffer[21] = 0xE0; |
491 | |
492 | _ASSERTE(DbgIsExecutable(pBuffer, 22)); |
493 | } |
494 | |
495 | void emitCOMStubCall (ComCallMethodDesc *pCOMMethod, PCODE target) |
496 | { |
497 | CONTRACT_VOID |
498 | { |
499 | THROWS; |
500 | GC_NOTRIGGER; |
501 | MODE_ANY; |
502 | } |
503 | CONTRACT_END; |
504 | |
505 | BYTE *pBuffer = (BYTE*)pCOMMethod - COMMETHOD_CALL_PRESTUB_SIZE; |
506 | |
507 | // We need the target to be in a 64-bit aligned memory location and the call instruction |
508 | // to immediately precede the ComCallMethodDesc. We'll generate an indirect call to avoid |
509 | // consuming 3 qwords for this (mov rax, | target | nops & call rax). |
510 | |
511 | // dq 123456789abcdef0h |
512 | // nop 90 |
513 | // nop 90 |
514 | // call [$ - 10] ff 15 f0 ff ff ff |
515 | |
516 | *((UINT64 *)&pBuffer[COMMETHOD_CALL_PRESTUB_ADDRESS_OFFSET]) = (UINT64)target; |
517 | |
518 | pBuffer[-2] = 0x90; |
519 | pBuffer[-1] = 0x90; |
520 | |
521 | pBuffer[0] = 0xFF; |
522 | pBuffer[1] = 0x15; |
523 | *((UINT32 UNALIGNED *)&pBuffer[2]) = (UINT32)(COMMETHOD_CALL_PRESTUB_ADDRESS_OFFSET - COMMETHOD_CALL_PRESTUB_SIZE); |
524 | |
525 | _ASSERTE(DbgIsExecutable(pBuffer, COMMETHOD_CALL_PRESTUB_SIZE)); |
526 | |
527 | RETURN; |
528 | } |
529 | |
530 | void emitJump(LPBYTE pBuffer, LPVOID target) |
531 | { |
532 | CONTRACTL |
533 | { |
534 | THROWS; |
535 | GC_NOTRIGGER; |
536 | MODE_ANY; |
537 | |
538 | PRECONDITION(CheckPointer(pBuffer)); |
539 | } |
540 | CONTRACTL_END; |
541 | |
542 | // mov rax, 123456789abcdef0h 48 b8 xx xx xx xx xx xx xx xx |
543 | // jmp rax ff e0 |
544 | |
545 | pBuffer[0] = 0x48; |
546 | pBuffer[1] = 0xB8; |
547 | |
548 | *((UINT64 UNALIGNED *)&pBuffer[2]) = (UINT64)target; |
549 | |
550 | pBuffer[10] = 0xFF; |
551 | pBuffer[11] = 0xE0; |
552 | |
553 | _ASSERTE(DbgIsExecutable(pBuffer, 12)); |
554 | } |
555 | |
556 | void UMEntryThunkCode::Encode(BYTE* pTargetCode, void* pvSecretParam) |
557 | { |
558 | CONTRACTL |
559 | { |
560 | NOTHROW; |
561 | GC_NOTRIGGER; |
562 | MODE_ANY; |
563 | } |
564 | CONTRACTL_END; |
565 | |
566 | // padding // CC CC CC CC |
567 | // mov r10, pUMEntryThunk // 49 ba xx xx xx xx xx xx xx xx // METHODDESC_REGISTER |
568 | // mov rax, pJmpDest // 48 b8 xx xx xx xx xx xx xx xx // need to ensure this imm64 is qword aligned |
569 | // TAILJMP_RAX // 48 FF E0 |
570 | |
571 | #ifdef _DEBUG |
572 | m_padding[0] = X86_INSTR_INT3; |
573 | m_padding[1] = X86_INSTR_INT3; |
574 | m_padding[2] = X86_INSTR_INT3; |
575 | m_padding[3] = X86_INSTR_INT3; |
576 | #endif // _DEBUG |
577 | m_movR10[0] = REX_PREFIX_BASE | REX_OPERAND_SIZE_64BIT | REX_OPCODE_REG_EXT; |
578 | m_movR10[1] = 0xBA; |
579 | m_uet = pvSecretParam; |
580 | m_movRAX[0] = REX_PREFIX_BASE | REX_OPERAND_SIZE_64BIT; |
581 | m_movRAX[1] = 0xB8; |
582 | m_execstub = pTargetCode; |
583 | m_jmpRAX[0] = REX_PREFIX_BASE | REX_OPERAND_SIZE_64BIT; |
584 | m_jmpRAX[1] = 0xFF; |
585 | m_jmpRAX[2] = 0xE0; |
586 | |
587 | _ASSERTE(DbgIsExecutable(&m_movR10[0], &m_jmpRAX[3]-&m_movR10[0])); |
588 | } |
589 | |
590 | void UMEntryThunkCode::Poison() |
591 | { |
592 | CONTRACTL |
593 | { |
594 | NOTHROW; |
595 | GC_NOTRIGGER; |
596 | MODE_ANY; |
597 | } |
598 | CONTRACTL_END; |
599 | |
600 | m_execstub = (BYTE *)UMEntryThunk::ReportViolation; |
601 | |
602 | m_movR10[0] = REX_PREFIX_BASE | REX_OPERAND_SIZE_64BIT; |
603 | #ifdef _WIN32 |
604 | // mov rcx, pUMEntryThunk // 48 b9 xx xx xx xx xx xx xx xx |
605 | m_movR10[1] = 0xB9; |
606 | #else |
607 | // mov rdi, pUMEntryThunk // 48 bf xx xx xx xx xx xx xx xx |
608 | m_movR10[1] = 0xBF; |
609 | #endif |
610 | |
611 | ClrFlushInstructionCache(&m_movR10[0], &m_jmpRAX[3]-&m_movR10[0]); |
612 | } |
613 | |
614 | UMEntryThunk* UMEntryThunk::Decode(LPVOID pCallback) |
615 | { |
616 | LIMITED_METHOD_CONTRACT; |
617 | |
618 | UMEntryThunkCode *pThunkCode = (UMEntryThunkCode*)((BYTE*)pCallback - UMEntryThunkCode::GetEntryPointOffset()); |
619 | |
620 | return (UMEntryThunk*)pThunkCode->m_uet; |
621 | } |
622 | |
623 | INT32 rel32UsingJumpStub(INT32 UNALIGNED * pRel32, PCODE target, MethodDesc *pMethod, |
624 | LoaderAllocator *pLoaderAllocator /* = NULL */, bool throwOnOutOfMemoryWithinRange /*= true*/) |
625 | { |
626 | CONTRACTL |
627 | { |
628 | THROWS; // Creating a JumpStub could throw OutOfMemory |
629 | GC_NOTRIGGER; |
630 | |
631 | PRECONDITION(pMethod != NULL || pLoaderAllocator != NULL); |
632 | // If a loader allocator isn't explicitly provided, we must be able to get one via the MethodDesc. |
633 | PRECONDITION(pLoaderAllocator != NULL || pMethod->GetLoaderAllocator() != NULL); |
634 | // If a domain is provided, the MethodDesc mustn't yet be set up to have one, or it must match the MethodDesc's domain, |
635 | // unless we're in a compilation domain (NGen loads assemblies as domain-bound but compiles them as domain neutral). |
636 | PRECONDITION(!pLoaderAllocator || !pMethod || pMethod->GetMethodDescChunk()->GetMethodTablePtr()->IsNull() || |
637 | pLoaderAllocator == pMethod->GetMethodDescChunk()->GetFirstMethodDesc()->GetLoaderAllocatorForCode() || IsCompilationProcess()); |
638 | } |
639 | CONTRACTL_END; |
640 | |
641 | TADDR baseAddr = (TADDR)pRel32 + 4; |
642 | |
643 | INT_PTR offset = target - baseAddr; |
644 | |
645 | if (!FitsInI4(offset) INDEBUG(|| PEDecoder::GetForceRelocs())) |
646 | { |
647 | TADDR loAddr = baseAddr + INT32_MIN; |
648 | if (loAddr > baseAddr) loAddr = UINT64_MIN; // overflow |
649 | |
650 | TADDR hiAddr = baseAddr + INT32_MAX; |
651 | if (hiAddr < baseAddr) hiAddr = UINT64_MAX; // overflow |
652 | |
653 | // Always try to allocate with throwOnOutOfMemoryWithinRange:false first to conserve reserveForJumpStubs until when |
654 | // it is really needed. LoaderCodeHeap::CreateCodeHeap and EEJitManager::CanUseCodeHeap won't use the reserved |
655 | // space when throwOnOutOfMemoryWithinRange is false. |
656 | // |
657 | // The reserved space should be only used by jump stubs for precodes and other similar code fragments. It should |
658 | // not be used by JITed code. And since the accounting of the reserved space is not precise, we are conservative |
659 | // and try to save the reserved space until it is really needed to avoid throwing out of memory within range exception. |
660 | PCODE jumpStubAddr = ExecutionManager::jumpStub(pMethod, |
661 | target, |
662 | (BYTE *)loAddr, |
663 | (BYTE *)hiAddr, |
664 | pLoaderAllocator, |
665 | /* throwOnOutOfMemoryWithinRange */ false); |
666 | if (jumpStubAddr == NULL) |
667 | { |
668 | if (!throwOnOutOfMemoryWithinRange) |
669 | return 0; |
670 | |
671 | jumpStubAddr = ExecutionManager::jumpStub(pMethod, |
672 | target, |
673 | (BYTE *)loAddr, |
674 | (BYTE *)hiAddr, |
675 | pLoaderAllocator, |
676 | /* throwOnOutOfMemoryWithinRange */ true); |
677 | } |
678 | |
679 | offset = jumpStubAddr - baseAddr; |
680 | |
681 | if (!FitsInI4(offset)) |
682 | { |
683 | _ASSERTE(!"jump stub was not in expected range" ); |
684 | EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE); |
685 | } |
686 | } |
687 | |
688 | _ASSERTE(FitsInI4(offset)); |
689 | return static_cast<INT32>(offset); |
690 | } |
691 | |
692 | INT32 rel32UsingPreallocatedJumpStub(INT32 UNALIGNED * pRel32, PCODE target, PCODE jumpStubAddr, bool emitJump) |
693 | { |
694 | CONTRACTL |
695 | { |
696 | THROWS; // emitBackToBackJump may throw (see emitJump) |
697 | GC_NOTRIGGER; |
698 | } |
699 | CONTRACTL_END; |
700 | |
701 | TADDR baseAddr = (TADDR)pRel32 + 4; |
702 | _ASSERTE(FitsInI4(jumpStubAddr - baseAddr)); |
703 | |
704 | INT_PTR offset = target - baseAddr; |
705 | if (!FitsInI4(offset) INDEBUG(|| PEDecoder::GetForceRelocs())) |
706 | { |
707 | offset = jumpStubAddr - baseAddr; |
708 | if (!FitsInI4(offset)) |
709 | { |
710 | _ASSERTE(!"jump stub was not in expected range" ); |
711 | EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE); |
712 | } |
713 | |
714 | if (emitJump) |
715 | { |
716 | emitBackToBackJump((LPBYTE)jumpStubAddr, (LPVOID)target); |
717 | } |
718 | else |
719 | { |
720 | _ASSERTE(decodeBackToBackJump(jumpStubAddr) == target); |
721 | } |
722 | } |
723 | |
724 | _ASSERTE(FitsInI4(offset)); |
725 | return static_cast<INT32>(offset); |
726 | } |
727 | |
728 | BOOL DoesSlotCallPrestub(PCODE pCode) |
729 | { |
730 | CONTRACTL { |
731 | NOTHROW; |
732 | GC_NOTRIGGER; |
733 | SO_TOLERANT; |
734 | PRECONDITION(pCode != GetPreStubEntryPoint()); |
735 | } CONTRACTL_END; |
736 | |
737 | // AMD64 has the following possible sequences for prestub logic: |
738 | // 1. slot -> temporary entrypoint -> prestub |
739 | // 2. slot -> precode -> prestub |
740 | // 3. slot -> precode -> jumprel64 (jump stub) -> prestub |
741 | // 4. slot -> precode -> jumprel64 (NGEN case) -> prestub |
742 | |
743 | #ifdef HAS_COMPACT_ENTRYPOINTS |
744 | if (MethodDescChunk::GetMethodDescFromCompactEntryPoint(pCode, TRUE) != NULL) |
745 | { |
746 | return TRUE; |
747 | } |
748 | #endif |
749 | |
750 | if (!IS_ALIGNED(pCode, PRECODE_ALIGNMENT)) |
751 | { |
752 | return FALSE; |
753 | } |
754 | |
755 | #ifdef HAS_FIXUP_PRECODE |
756 | if (*PTR_BYTE(pCode) == X86_INSTR_CALL_REL32) |
757 | { |
758 | // Note that call could have been patched to jmp in the meantime |
759 | pCode = rel32Decode(pCode+1); |
760 | |
761 | #ifdef FEATURE_PREJIT |
762 | // NGEN helper |
763 | if (*PTR_BYTE(pCode) == X86_INSTR_JMP_REL32) { |
764 | pCode = (TADDR)rel32Decode(pCode+1); |
765 | } |
766 | #endif |
767 | |
768 | // JumpStub |
769 | if (isJumpRel64(pCode)) { |
770 | pCode = decodeJump64(pCode); |
771 | } |
772 | |
773 | return pCode == (TADDR)PrecodeFixupThunk; |
774 | } |
775 | #endif |
776 | |
777 | if (*PTR_USHORT(pCode) != X86_INSTR_MOV_R10_IMM64 || // mov rax,XXXX |
778 | *PTR_BYTE(pCode+10) != X86_INSTR_NOP || // nop |
779 | *PTR_BYTE(pCode+11) != X86_INSTR_JMP_REL32) // jmp rel32 |
780 | { |
781 | return FALSE; |
782 | } |
783 | pCode = rel32Decode(pCode+12); |
784 | |
785 | #ifdef FEATURE_PREJIT |
786 | // NGEN helper |
787 | if (*PTR_BYTE(pCode) == X86_INSTR_JMP_REL32) { |
788 | pCode = (TADDR)rel32Decode(pCode+1); |
789 | } |
790 | #endif |
791 | |
792 | // JumpStub |
793 | if (isJumpRel64(pCode)) { |
794 | pCode = decodeJump64(pCode); |
795 | } |
796 | |
797 | return pCode == GetPreStubEntryPoint(); |
798 | } |
799 | |
800 | // |
801 | // Some AMD64 assembly functions have one or more DWORDS at the end of the function |
802 | // that specify the offsets where significant instructions are |
803 | // we use this function to get at these offsets |
804 | // |
805 | DWORD GetOffsetAtEndOfFunction(ULONGLONG uImageBase, |
806 | PT_RUNTIME_FUNCTION pFunctionEntry, |
807 | int offsetNum /* = 1*/) |
808 | { |
809 | CONTRACTL |
810 | { |
811 | MODE_ANY; |
812 | NOTHROW; |
813 | GC_NOTRIGGER; |
814 | SO_TOLERANT; |
815 | PRECONDITION((offsetNum > 0) && (offsetNum < 20)); /* we only allow reasonable offsetNums 1..19 */ |
816 | } |
817 | CONTRACTL_END; |
818 | |
819 | DWORD functionSize = pFunctionEntry->EndAddress - pFunctionEntry->BeginAddress; |
820 | BYTE* pEndOfFunction = (BYTE*) (uImageBase + pFunctionEntry->EndAddress); |
821 | DWORD* pOffset = (DWORD*) (pEndOfFunction) - offsetNum; |
822 | DWORD offsetInFunc = *pOffset; |
823 | |
824 | _ASSERTE_ALL_BUILDS("clr/src/VM/AMD64/cGenAMD64.cpp" , (offsetInFunc >= 0) && (offsetInFunc < functionSize)); |
825 | |
826 | return offsetInFunc; |
827 | } |
828 | |
829 | //========================================================================================== |
830 | // In NGen image, virtual slots inherited from cross-module dependencies point to jump thunks. |
831 | // These jump thunk initially point to VirtualMethodFixupStub which transfers control here. |
832 | // This method 'VirtualMethodFixupWorker' will patch the jump thunk to point to the actual |
833 | // inherited method body after we have execute the precode and a stable entry point. |
834 | // |
835 | EXTERN_C PCODE VirtualMethodFixupWorker(TransitionBlock * pTransitionBlock, CORCOMPILE_VIRTUAL_IMPORT_THUNK * pThunk) |
836 | { |
837 | CONTRACTL |
838 | { |
839 | THROWS; |
840 | GC_TRIGGERS; // GC not allowed until we call pEMFrame->SetFunction(pMD); |
841 | |
842 | ENTRY_POINT; |
843 | } |
844 | CONTRACTL_END; |
845 | |
846 | MAKE_CURRENT_THREAD_AVAILABLE(); |
847 | |
848 | PCODE pCode = NULL; |
849 | MethodDesc * pMD = NULL; |
850 | |
851 | #ifdef _DEBUG |
852 | Thread::ObjectRefFlush(CURRENT_THREAD); |
853 | #endif |
854 | |
855 | BEGIN_SO_INTOLERANT_CODE(CURRENT_THREAD); |
856 | |
857 | _ASSERTE(IS_ALIGNED((size_t)pThunk, sizeof(INT64))); |
858 | |
859 | FrameWithCookie<ExternalMethodFrame> frame(pTransitionBlock); |
860 | ExternalMethodFrame * pEMFrame = &frame; |
861 | |
862 | OBJECTREF pThisPtr = pEMFrame->GetThis(); |
863 | _ASSERTE(pThisPtr != NULL); |
864 | VALIDATEOBJECT(pThisPtr); |
865 | |
866 | MethodTable * pMT = pThisPtr->GetMethodTable(); |
867 | |
868 | WORD slotNumber = pThunk->slotNum; |
869 | _ASSERTE(slotNumber != (WORD)-1); |
870 | |
871 | pCode = pMT->GetRestoredSlot(slotNumber); |
872 | |
873 | if (!DoesSlotCallPrestub(pCode)) |
874 | { |
875 | pMD = MethodTable::GetMethodDescForSlotAddress(pCode); |
876 | |
877 | pEMFrame->SetFunction(pMD); // We will use the pMD to enumerate the GC refs in the arguments |
878 | pEMFrame->Push(CURRENT_THREAD); |
879 | |
880 | INSTALL_MANAGED_EXCEPTION_DISPATCHER; |
881 | INSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE; |
882 | |
883 | // Skip fixup precode jump for better perf |
884 | PCODE pDirectTarget = Precode::TryToSkipFixupPrecode(pCode); |
885 | if (pDirectTarget != NULL) |
886 | pCode = pDirectTarget; |
887 | |
888 | INT64 oldValue = *(INT64*)pThunk; |
889 | BYTE* pOldValue = (BYTE*)&oldValue; |
890 | |
891 | if (pOldValue[0] == X86_INSTR_CALL_REL32) |
892 | { |
893 | INT64 newValue = oldValue; |
894 | BYTE* pNewValue = (BYTE*)&newValue; |
895 | pNewValue[0] = X86_INSTR_JMP_REL32; |
896 | |
897 | *(INT32 *)(pNewValue+1) = rel32UsingJumpStub((INT32*)(&pThunk->callJmp[1]), pCode, pMD, NULL); |
898 | |
899 | _ASSERTE(IS_ALIGNED(pThunk, sizeof(INT64))); |
900 | EnsureWritableExecutablePages(pThunk, sizeof(INT64)); |
901 | FastInterlockCompareExchangeLong((INT64*)pThunk, newValue, oldValue); |
902 | |
903 | FlushInstructionCache(GetCurrentProcess(), pThunk, 8); |
904 | } |
905 | |
906 | UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE; |
907 | UNINSTALL_MANAGED_EXCEPTION_DISPATCHER; |
908 | pEMFrame->Pop(CURRENT_THREAD); |
909 | } |
910 | |
911 | // Ready to return |
912 | |
913 | END_SO_INTOLERANT_CODE; |
914 | |
915 | return pCode; |
916 | } |
917 | |
918 | #ifdef FEATURE_READYTORUN |
919 | |
920 | // |
921 | // Allocation of dynamic helpers |
922 | // |
923 | |
924 | #define DYNAMIC_HELPER_ALIGNMENT sizeof(TADDR) |
925 | |
926 | #define BEGIN_DYNAMIC_HELPER_EMIT(size) \ |
927 | SIZE_T cb = size; \ |
928 | SIZE_T cbAligned = ALIGN_UP(cb, DYNAMIC_HELPER_ALIGNMENT); \ |
929 | BYTE * pStart = (BYTE *)(void *)pAllocator->GetDynamicHelpersHeap()->AllocAlignedMem(cbAligned, DYNAMIC_HELPER_ALIGNMENT); \ |
930 | BYTE * p = pStart; |
931 | |
932 | #define END_DYNAMIC_HELPER_EMIT() \ |
933 | _ASSERTE(pStart + cb == p); \ |
934 | while (p < pStart + cbAligned) *p++ = X86_INSTR_INT3; \ |
935 | ClrFlushInstructionCache(pStart, cbAligned); \ |
936 | return (PCODE)pStart |
937 | |
938 | PCODE DynamicHelpers::CreateHelper(LoaderAllocator * pAllocator, TADDR arg, PCODE target) |
939 | { |
940 | STANDARD_VM_CONTRACT; |
941 | |
942 | BEGIN_DYNAMIC_HELPER_EMIT(15); |
943 | |
944 | #ifdef UNIX_AMD64_ABI |
945 | *(UINT16 *)p = 0xBF48; // mov rdi, XXXXXX |
946 | #else |
947 | *(UINT16 *)p = 0xB948; // mov rcx, XXXXXX |
948 | #endif |
949 | p += 2; |
950 | *(TADDR *)p = arg; |
951 | p += 8; |
952 | |
953 | *p++ = X86_INSTR_JMP_REL32; // jmp rel32 |
954 | *(INT32 *)p = rel32UsingJumpStub((INT32 *)p, target, NULL, pAllocator); |
955 | p += 4; |
956 | |
957 | END_DYNAMIC_HELPER_EMIT(); |
958 | } |
959 | |
960 | void DynamicHelpers::EmitHelperWithArg(BYTE*& p, LoaderAllocator * pAllocator, TADDR arg, PCODE target) |
961 | { |
962 | CONTRACTL |
963 | { |
964 | GC_NOTRIGGER; |
965 | PRECONDITION(p != NULL && target != NULL); |
966 | } |
967 | CONTRACTL_END; |
968 | |
969 | // Move an an argument into the second argument register and jump to a target function. |
970 | |
971 | #ifdef UNIX_AMD64_ABI |
972 | *(UINT16 *)p = 0xBE48; // mov rsi, XXXXXX |
973 | #else |
974 | *(UINT16 *)p = 0xBA48; // mov rdx, XXXXXX |
975 | #endif |
976 | p += 2; |
977 | *(TADDR *)p = arg; |
978 | p += 8; |
979 | |
980 | *p++ = X86_INSTR_JMP_REL32; // jmp rel32 |
981 | *(INT32 *)p = rel32UsingJumpStub((INT32 *)p, target, NULL, pAllocator); |
982 | p += 4; |
983 | } |
984 | |
985 | PCODE DynamicHelpers::CreateHelperWithArg(LoaderAllocator * pAllocator, TADDR arg, PCODE target) |
986 | { |
987 | BEGIN_DYNAMIC_HELPER_EMIT(15); |
988 | |
989 | EmitHelperWithArg(p, pAllocator, arg, target); |
990 | |
991 | END_DYNAMIC_HELPER_EMIT(); |
992 | } |
993 | |
994 | PCODE DynamicHelpers::CreateHelper(LoaderAllocator * pAllocator, TADDR arg, TADDR arg2, PCODE target) |
995 | { |
996 | BEGIN_DYNAMIC_HELPER_EMIT(25); |
997 | |
998 | #ifdef UNIX_AMD64_ABI |
999 | *(UINT16 *)p = 0xBF48; // mov rdi, XXXXXX |
1000 | #else |
1001 | *(UINT16 *)p = 0xB948; // mov rcx, XXXXXX |
1002 | #endif |
1003 | p += 2; |
1004 | *(TADDR *)p = arg; |
1005 | p += 8; |
1006 | |
1007 | #ifdef UNIX_AMD64_ABI |
1008 | *(UINT16 *)p = 0xBE48; // mov rsi, XXXXXX |
1009 | #else |
1010 | *(UINT16 *)p = 0xBA48; // mov rdx, XXXXXX |
1011 | #endif |
1012 | p += 2; |
1013 | *(TADDR *)p = arg2; |
1014 | p += 8; |
1015 | |
1016 | *p++ = X86_INSTR_JMP_REL32; // jmp rel32 |
1017 | *(INT32 *)p = rel32UsingJumpStub((INT32 *)p, target, NULL, pAllocator); |
1018 | p += 4; |
1019 | |
1020 | END_DYNAMIC_HELPER_EMIT(); |
1021 | } |
1022 | |
1023 | PCODE DynamicHelpers::CreateHelperArgMove(LoaderAllocator * pAllocator, TADDR arg, PCODE target) |
1024 | { |
1025 | BEGIN_DYNAMIC_HELPER_EMIT(18); |
1026 | |
1027 | #ifdef UNIX_AMD64_ABI |
1028 | *p++ = 0x48; // mov rsi, rdi |
1029 | *(UINT16 *)p = 0xF78B; |
1030 | #else |
1031 | *p++ = 0x48; // mov rdx, rcx |
1032 | *(UINT16 *)p = 0xD18B; |
1033 | #endif |
1034 | p += 2; |
1035 | |
1036 | #ifdef UNIX_AMD64_ABI |
1037 | *(UINT16 *)p = 0xBF48; // mov rdi, XXXXXX |
1038 | #else |
1039 | *(UINT16 *)p = 0xB948; // mov rcx, XXXXXX |
1040 | #endif |
1041 | p += 2; |
1042 | *(TADDR *)p = arg; |
1043 | p += 8; |
1044 | |
1045 | *p++ = X86_INSTR_JMP_REL32; // jmp rel32 |
1046 | *(INT32 *)p = rel32UsingJumpStub((INT32 *)p, target, NULL, pAllocator); |
1047 | p += 4; |
1048 | |
1049 | END_DYNAMIC_HELPER_EMIT(); |
1050 | } |
1051 | |
1052 | PCODE DynamicHelpers::CreateReturn(LoaderAllocator * pAllocator) |
1053 | { |
1054 | BEGIN_DYNAMIC_HELPER_EMIT(1); |
1055 | |
1056 | *p++ = 0xC3; // ret |
1057 | |
1058 | END_DYNAMIC_HELPER_EMIT(); |
1059 | } |
1060 | |
1061 | PCODE DynamicHelpers::CreateReturnConst(LoaderAllocator * pAllocator, TADDR arg) |
1062 | { |
1063 | BEGIN_DYNAMIC_HELPER_EMIT(11); |
1064 | |
1065 | *(UINT16 *)p = 0xB848; // mov rax, XXXXXX |
1066 | p += 2; |
1067 | *(TADDR *)p = arg; |
1068 | p += 8; |
1069 | |
1070 | *p++ = 0xC3; // ret |
1071 | |
1072 | END_DYNAMIC_HELPER_EMIT(); |
1073 | } |
1074 | |
1075 | PCODE DynamicHelpers::CreateReturnIndirConst(LoaderAllocator * pAllocator, TADDR arg, INT8 offset) |
1076 | { |
1077 | BEGIN_DYNAMIC_HELPER_EMIT((offset != 0) ? 15 : 11); |
1078 | |
1079 | *(UINT16 *)p = 0xA148; // mov rax, [XXXXXX] |
1080 | p += 2; |
1081 | *(TADDR *)p = arg; |
1082 | p += 8; |
1083 | |
1084 | if (offset != 0) |
1085 | { |
1086 | // add rax, <offset> |
1087 | *p++ = 0x48; |
1088 | *p++ = 0x83; |
1089 | *p++ = 0xC0; |
1090 | *p++ = offset; |
1091 | } |
1092 | |
1093 | *p++ = 0xC3; // ret |
1094 | |
1095 | END_DYNAMIC_HELPER_EMIT(); |
1096 | } |
1097 | |
1098 | PCODE DynamicHelpers::CreateHelperWithTwoArgs(LoaderAllocator * pAllocator, TADDR arg, PCODE target) |
1099 | { |
1100 | BEGIN_DYNAMIC_HELPER_EMIT(15); |
1101 | |
1102 | #ifdef UNIX_AMD64_ABI |
1103 | *(UINT16 *)p = 0xBA48; // mov rdx, XXXXXX |
1104 | #else |
1105 | *(UINT16 *)p = 0xB849; // mov r8, XXXXXX |
1106 | #endif |
1107 | p += 2; |
1108 | *(TADDR *)p = arg; |
1109 | p += 8; |
1110 | |
1111 | *p++ = X86_INSTR_JMP_REL32; // jmp rel32 |
1112 | *(INT32 *)p = rel32UsingJumpStub((INT32 *)p, target, NULL, pAllocator); |
1113 | p += 4; |
1114 | |
1115 | END_DYNAMIC_HELPER_EMIT(); |
1116 | } |
1117 | |
1118 | PCODE DynamicHelpers::CreateHelperWithTwoArgs(LoaderAllocator * pAllocator, TADDR arg, TADDR arg2, PCODE target) |
1119 | { |
1120 | BEGIN_DYNAMIC_HELPER_EMIT(25); |
1121 | |
1122 | #ifdef UNIX_AMD64_ABI |
1123 | *(UINT16 *)p = 0xBA48; // mov rdx, XXXXXX |
1124 | #else |
1125 | *(UINT16 *)p = 0xB849; // mov r8, XXXXXX |
1126 | #endif |
1127 | p += 2; |
1128 | *(TADDR *)p = arg; |
1129 | p += 8; |
1130 | |
1131 | #ifdef UNIX_AMD64_ABI |
1132 | *(UINT16 *)p = 0xB948; // mov rcx, XXXXXX |
1133 | #else |
1134 | *(UINT16 *)p = 0xB949; // mov r9, XXXXXX |
1135 | #endif |
1136 | p += 2; |
1137 | *(TADDR *)p = arg2; |
1138 | p += 8; |
1139 | |
1140 | *p++ = X86_INSTR_JMP_REL32; // jmp rel32 |
1141 | *(INT32 *)p = rel32UsingJumpStub((INT32 *)p, target, NULL, pAllocator); |
1142 | p += 4; |
1143 | |
1144 | END_DYNAMIC_HELPER_EMIT(); |
1145 | } |
1146 | |
1147 | PCODE DynamicHelpers::CreateDictionaryLookupHelper(LoaderAllocator * pAllocator, CORINFO_RUNTIME_LOOKUP * pLookup, DWORD dictionaryIndexAndSlot, Module * pModule) |
1148 | { |
1149 | STANDARD_VM_CONTRACT; |
1150 | |
1151 | PCODE helperAddress = (pLookup->helper == CORINFO_HELP_RUNTIMEHANDLE_METHOD ? |
1152 | GetEEFuncEntryPoint(JIT_GenericHandleMethodWithSlotAndModule) : |
1153 | GetEEFuncEntryPoint(JIT_GenericHandleClassWithSlotAndModule)); |
1154 | |
1155 | GenericHandleArgs * pArgs = (GenericHandleArgs *)(void *)pAllocator->GetDynamicHelpersHeap()->AllocAlignedMem(sizeof(GenericHandleArgs), DYNAMIC_HELPER_ALIGNMENT); |
1156 | pArgs->dictionaryIndexAndSlot = dictionaryIndexAndSlot; |
1157 | pArgs->signature = pLookup->signature; |
1158 | pArgs->module = (CORINFO_MODULE_HANDLE)pModule; |
1159 | |
1160 | // It's available only via the run-time helper function |
1161 | if (pLookup->indirections == CORINFO_USEHELPER) |
1162 | { |
1163 | BEGIN_DYNAMIC_HELPER_EMIT(15); |
1164 | |
1165 | // rcx/rdi contains the generic context parameter |
1166 | // mov rdx/rsi,pArgs |
1167 | // jmp helperAddress |
1168 | EmitHelperWithArg(p, pAllocator, (TADDR)pArgs, helperAddress); |
1169 | |
1170 | END_DYNAMIC_HELPER_EMIT(); |
1171 | } |
1172 | else |
1173 | { |
1174 | int indirectionsSize = 0; |
1175 | for (WORD i = 0; i < pLookup->indirections; i++) |
1176 | indirectionsSize += (pLookup->offsets[i] >= 0x80 ? 7 : 4); |
1177 | |
1178 | int codeSize = indirectionsSize + (pLookup->testForNull ? 30 : 4); |
1179 | |
1180 | BEGIN_DYNAMIC_HELPER_EMIT(codeSize); |
1181 | |
1182 | if (pLookup->testForNull) |
1183 | { |
1184 | // rcx/rdi contains the generic context parameter. Save a copy of it in the rax register |
1185 | #ifdef UNIX_AMD64_ABI |
1186 | *(UINT32*)p = 0x00f88948; p += 3; // mov rax,rdi |
1187 | #else |
1188 | *(UINT32*)p = 0x00c88948; p += 3; // mov rax,rcx |
1189 | #endif |
1190 | } |
1191 | |
1192 | for (WORD i = 0; i < pLookup->indirections; i++) |
1193 | { |
1194 | #ifdef UNIX_AMD64_ABI |
1195 | // mov rdi,qword ptr [rdi+offset] |
1196 | if (pLookup->offsets[i] >= 0x80) |
1197 | { |
1198 | *(UINT32*)p = 0x00bf8b48; p += 3; |
1199 | *(UINT32*)p = (UINT32)pLookup->offsets[i]; p += 4; |
1200 | } |
1201 | else |
1202 | { |
1203 | *(UINT32*)p = 0x007f8b48; p += 3; |
1204 | *p++ = (BYTE)pLookup->offsets[i]; |
1205 | } |
1206 | #else |
1207 | // mov rcx,qword ptr [rcx+offset] |
1208 | if (pLookup->offsets[i] >= 0x80) |
1209 | { |
1210 | *(UINT32*)p = 0x00898b48; p += 3; |
1211 | *(UINT32*)p = (UINT32)pLookup->offsets[i]; p += 4; |
1212 | } |
1213 | else |
1214 | { |
1215 | *(UINT32*)p = 0x00498b48; p += 3; |
1216 | *p++ = (BYTE)pLookup->offsets[i]; |
1217 | } |
1218 | #endif |
1219 | } |
1220 | |
1221 | // No null test required |
1222 | if (!pLookup->testForNull) |
1223 | { |
1224 | // No fixups needed for R2R |
1225 | |
1226 | #ifdef UNIX_AMD64_ABI |
1227 | *(UINT32*)p = 0x00f88948; p += 3; // mov rax,rdi |
1228 | #else |
1229 | *(UINT32*)p = 0x00c88948; p += 3; // mov rax,rcx |
1230 | #endif |
1231 | *p++ = 0xC3; // ret |
1232 | } |
1233 | else |
1234 | { |
1235 | // rcx/rdi contains the value of the dictionary slot entry |
1236 | |
1237 | _ASSERTE(pLookup->indirections != 0); |
1238 | |
1239 | #ifdef UNIX_AMD64_ABI |
1240 | *(UINT32*)p = 0x00ff8548; p += 3; // test rdi,rdi |
1241 | #else |
1242 | *(UINT32*)p = 0x00c98548; p += 3; // test rcx,rcx |
1243 | #endif |
1244 | |
1245 | // je 'HELPER_CALL' (a jump of 4 bytes) |
1246 | *(UINT16*)p = 0x0474; p += 2; |
1247 | |
1248 | #ifdef UNIX_AMD64_ABI |
1249 | *(UINT32*)p = 0x00f88948; p += 3; // mov rax,rdi |
1250 | #else |
1251 | *(UINT32*)p = 0x00c88948; p += 3; // mov rax,rcx |
1252 | #endif |
1253 | *p++ = 0xC3; // ret |
1254 | |
1255 | // 'HELPER_CALL' |
1256 | { |
1257 | // Put the generic context back into rcx (was previously saved in rax) |
1258 | #ifdef UNIX_AMD64_ABI |
1259 | *(UINT32*)p = 0x00c78948; p += 3; // mov rdi,rax |
1260 | #else |
1261 | *(UINT32*)p = 0x00c18948; p += 3; // mov rcx,rax |
1262 | #endif |
1263 | |
1264 | // mov rdx,pArgs |
1265 | // jmp helperAddress |
1266 | EmitHelperWithArg(p, pAllocator, (TADDR)pArgs, helperAddress); |
1267 | } |
1268 | } |
1269 | |
1270 | END_DYNAMIC_HELPER_EMIT(); |
1271 | } |
1272 | } |
1273 | |
1274 | #endif // FEATURE_READYTORUN |
1275 | |
1276 | #endif // DACCESS_COMPILE |
1277 | |