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
29void 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
44void 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
62void 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
82extern "C" TADDR s_pStubHelperFrameVPtr;
83TADDR s_pStubHelperFrameVPtr = StubHelperFrame::GetMethodFrameVPtr();
84
85void 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
99void 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
117void 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
158void 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
237void 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
268TADDR ResumableFrame::GetReturnAddressPtr()
269{
270 LIMITED_METHOD_DAC_CONTRACT;
271 return dac_cast<TADDR>(m_Regs) + offsetof(CONTEXT, Rip);
272}
273
274void 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
314void 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
362BOOL 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//
380PCODE 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
397BOOL 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
414PCODE 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
433BOOL 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
461void 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
495void 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
530void 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
556void 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
590void 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
614UMEntryThunk* 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
623INT32 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
692INT32 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
728BOOL 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//
805DWORD 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//
835EXTERN_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
938PCODE 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
960void 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
985PCODE 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
994PCODE 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
1023PCODE 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
1052PCODE DynamicHelpers::CreateReturn(LoaderAllocator * pAllocator)
1053{
1054 BEGIN_DYNAMIC_HELPER_EMIT(1);
1055
1056 *p++ = 0xC3; // ret
1057
1058 END_DYNAMIC_HELPER_EMIT();
1059}
1060
1061PCODE 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
1075PCODE 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
1098PCODE 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
1118PCODE 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
1147PCODE 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