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 #include "common.h"
5 
6 #include <windows.h>
7 
8 #include "CommonTypes.h"
9 #include "CommonMacros.h"
10 #include "daccess.h"
11 #include "PalRedhawkCommon.h"
12 #include "regdisplay.h"
13 #include "ICodeManager.h"
14 #include "CoffNativeCodeManager.h"
15 #include "varint.h"
16 #include "holder.h"
17 
18 #include "CommonMacros.inl"
19 
20 #define GCINFODECODER_NO_EE
21 #include "coreclr/gcinfodecoder.cpp"
22 
23 #define UBF_FUNC_KIND_MASK      0x03
24 #define UBF_FUNC_KIND_ROOT      0x00
25 #define UBF_FUNC_KIND_HANDLER   0x01
26 #define UBF_FUNC_KIND_FILTER    0x02
27 
28 #define UBF_FUNC_HAS_EHINFO             0x04
29 #define UBF_FUNC_REVERSE_PINVOKE        0x08
30 #define UBF_FUNC_HAS_ASSOCIATED_DATA    0x10
31 
32 #ifdef _TARGET_X86_
33 //
34 // x86 ABI does not define RUNTIME_FUNCTION. Define our own to allow unification between x86 and other platforms.
35 //
36 typedef struct _RUNTIME_FUNCTION {
37     DWORD BeginAddress;
38     DWORD EndAddress;
39     DWORD UnwindData;
40 } RUNTIME_FUNCTION, *PRUNTIME_FUNCTION;
41 
42 typedef struct _KNONVOLATILE_CONTEXT_POINTERS {
43 
44     // The ordering of these fields should be aligned with that
45     // of corresponding fields in CONTEXT
46     //
47     // (See REGDISPLAY in Runtime/regdisp.h for details)
48     PDWORD Edi;
49     PDWORD Esi;
50     PDWORD Ebx;
51     PDWORD Edx;
52     PDWORD Ecx;
53     PDWORD Eax;
54 
55     PDWORD Ebp;
56 
57 } KNONVOLATILE_CONTEXT_POINTERS, *PKNONVOLATILE_CONTEXT_POINTERS;
58 
59 typedef struct _UNWIND_INFO {
60     ULONG FunctionLength;
61 } UNWIND_INFO, *PUNWIND_INFO;
62 
63 #elif defined(_TARGET_AMD64_)
64 
65 #define UNW_FLAG_NHANDLER 0x0
66 #define UNW_FLAG_EHANDLER 0x1
67 #define UNW_FLAG_UHANDLER 0x2
68 #define UNW_FLAG_CHAININFO 0x4
69 
70 //
71 // The following structures are defined in Windows x64 unwind info specification
72 // http://www.bing.com/search?q=msdn+Exception+Handling+x64
73 //
74 typedef union _UNWIND_CODE {
75     struct {
76         uint8_t CodeOffset;
77         uint8_t UnwindOp : 4;
78         uint8_t OpInfo : 4;
79     };
80 
81     uint16_t FrameOffset;
82 } UNWIND_CODE, *PUNWIND_CODE;
83 
84 typedef struct _UNWIND_INFO {
85     uint8_t Version : 3;
86     uint8_t Flags : 5;
87     uint8_t SizeOfProlog;
88     uint8_t CountOfUnwindCodes;
89     uint8_t FrameRegister : 4;
90     uint8_t FrameOffset : 4;
91     UNWIND_CODE UnwindCode[1];
92 } UNWIND_INFO, *PUNWIND_INFO;
93 
94 #endif // _TARGET_X86_
95 
96 typedef DPTR(struct _UNWIND_INFO)      PTR_UNWIND_INFO;
97 typedef DPTR(union _UNWIND_CODE)       PTR_UNWIND_CODE;
98 
GetUnwindDataBlob(TADDR moduleBase,PTR_RUNTIME_FUNCTION pRuntimeFunction,size_t * pSize)99 static PTR_VOID GetUnwindDataBlob(TADDR moduleBase, PTR_RUNTIME_FUNCTION pRuntimeFunction, /* out */ size_t * pSize)
100 {
101 #if defined(_TARGET_AMD64_)
102     PTR_UNWIND_INFO pUnwindInfo(dac_cast<PTR_UNWIND_INFO>(moduleBase + pRuntimeFunction->UnwindInfoAddress));
103 
104     size_t size = offsetof(UNWIND_INFO, UnwindCode) + sizeof(UNWIND_CODE) * pUnwindInfo->CountOfUnwindCodes;
105 
106     // Chained unwind info is not supported at this time
107     ASSERT((pUnwindInfo->Flags & UNW_FLAG_CHAININFO) == 0);
108 
109     if (pUnwindInfo->Flags & (UNW_FLAG_EHANDLER | UNW_FLAG_UHANDLER))
110     {
111         // Personality routine
112         size = ALIGN_UP(size, sizeof(DWORD)) + sizeof(DWORD);
113     }
114 
115     *pSize = size;
116 
117     return pUnwindInfo;
118 
119 #elif defined(_TARGET_X86_)
120 
121     PTR_UNWIND_INFO pUnwindInfo(dac_cast<PTR_UNWIND_INFO>(moduleBase + pRuntimeFunction->UnwindInfoAddress));
122 
123     *pSize = sizeof(UNWIND_INFO);
124 
125     return pUnwindInfo;
126 
127 #elif defined(_TARGET_ARM_)
128 
129     // if this function uses packed unwind data then at least one of the two least significant bits
130     // will be non-zero.  if this is the case then there will be no xdata record to enumerate.
131     ASSERT((pRuntimeFunction->UnwindData & 0x3) == 0);
132 
133     // compute the size of the unwind info
134     PTR_TADDR xdata = dac_cast<PTR_TADDR>(pRuntimeFunction->UnwindData + moduleBase);
135 
136     ULONG epilogScopes = 0;
137     ULONG unwindWords = 0;
138     ULONG size = 0;
139 
140     if ((xdata[0] >> 23) != 0)
141     {
142         size = 4;
143         epilogScopes = (xdata[0] >> 23) & 0x1f;
144         unwindWords = (xdata[0] >> 28) & 0x0f;
145     }
146     else
147     {
148         size = 8;
149         epilogScopes = xdata[1] & 0xffff;
150         unwindWords = (xdata[1] >> 16) & 0xff;
151     }
152 
153     if (!(xdata[0] & (1 << 21)))
154         size += 4 * epilogScopes;
155 
156     size += 4 * unwindWords;
157 
158     if ((xdata[0] & (1 << 20)) != 0)
159     {
160         // Personality routine
161         size += 4;
162     }
163 
164     *pSize = size;
165     return xdata;
166 #else
167     PORTABILITY_ASSERT("GetUnwindDataBlob");
168     *pSize = 0;
169     return NULL;
170 #endif
171 }
172 
173 
CoffNativeCodeManager(TADDR moduleBase,PTR_RUNTIME_FUNCTION pRuntimeFunctionTable,UInt32 nRuntimeFunctionTable,PTR_PTR_VOID pClasslibFunctions,UInt32 nClasslibFunctions)174 CoffNativeCodeManager::CoffNativeCodeManager(TADDR moduleBase,
175                                              PTR_RUNTIME_FUNCTION pRuntimeFunctionTable, UInt32 nRuntimeFunctionTable,
176                                              PTR_PTR_VOID pClasslibFunctions, UInt32 nClasslibFunctions)
177     : m_moduleBase(moduleBase),
178       m_pRuntimeFunctionTable(pRuntimeFunctionTable), m_nRuntimeFunctionTable(nRuntimeFunctionTable),
179       m_pClasslibFunctions(pClasslibFunctions), m_nClasslibFunctions(nClasslibFunctions)
180 {
181 }
182 
~CoffNativeCodeManager()183 CoffNativeCodeManager::~CoffNativeCodeManager()
184 {
185 }
186 
LookupUnwindInfoForMethod(UInt32 relativePc,PTR_RUNTIME_FUNCTION pRuntimeFunctionTable,int low,int high)187 static int LookupUnwindInfoForMethod(UInt32 relativePc,
188                                      PTR_RUNTIME_FUNCTION pRuntimeFunctionTable,
189                                      int low,
190                                      int high)
191 {
192 #ifdef _TARGET_ARM_
193     relativePc |= THUMB_CODE;
194 #endif
195 
196     // Entries are sorted and terminated by sentinel value (DWORD)-1
197 
198     // Binary search the RUNTIME_FUNCTION table
199     // Use linear search once we get down to a small number of elements
200     // to avoid Binary search overhead.
201     while (high - low > 10)
202     {
203        int middle = low + (high - low) / 2;
204 
205        PTR_RUNTIME_FUNCTION pFunctionEntry = pRuntimeFunctionTable + middle;
206        if (relativePc < pFunctionEntry->BeginAddress)
207        {
208            high = middle - 1;
209        }
210        else
211        {
212            low = middle;
213        }
214     }
215 
216     for (int i = low; i <= high; ++i)
217     {
218         // This is safe because of entries are terminated by sentinel value (DWORD)-1
219         PTR_RUNTIME_FUNCTION pNextFunctionEntry = pRuntimeFunctionTable + (i + 1);
220 
221         if (relativePc < pNextFunctionEntry->BeginAddress)
222         {
223             PTR_RUNTIME_FUNCTION pFunctionEntry = pRuntimeFunctionTable + i;
224             if (relativePc >= pFunctionEntry->BeginAddress)
225             {
226                 return i;
227             }
228             break;
229         }
230     }
231 
232     return -1;
233 }
234 
235 struct CoffNativeMethodInfo
236 {
237     PTR_RUNTIME_FUNCTION mainRuntimeFunction;
238     PTR_RUNTIME_FUNCTION runtimeFunction;
239     bool executionAborted;
240 };
241 
242 // Ensure that CoffNativeMethodInfo fits into the space reserved by MethodInfo
243 static_assert(sizeof(CoffNativeMethodInfo) <= sizeof(MethodInfo), "CoffNativeMethodInfo too big");
244 
FindMethodInfo(PTR_VOID ControlPC,MethodInfo * pMethodInfoOut)245 bool CoffNativeCodeManager::FindMethodInfo(PTR_VOID        ControlPC,
246                                            MethodInfo *    pMethodInfoOut)
247 {
248     CoffNativeMethodInfo * pMethodInfo = (CoffNativeMethodInfo *)pMethodInfoOut;
249 
250     TADDR relativePC = dac_cast<TADDR>(ControlPC) - m_moduleBase;
251 
252     int MethodIndex = LookupUnwindInfoForMethod((UInt32)relativePC, m_pRuntimeFunctionTable,
253         0, m_nRuntimeFunctionTable - 1);
254     if (MethodIndex < 0)
255         return false;
256 
257     PTR_RUNTIME_FUNCTION pRuntimeFunction = m_pRuntimeFunctionTable + MethodIndex;
258 
259     pMethodInfo->runtimeFunction = pRuntimeFunction;
260 
261     // The runtime function could correspond to a funclet.  We need to get to the
262     // runtime function of the main method.
263     for (;;)
264     {
265         size_t unwindDataBlobSize;
266         PTR_VOID pUnwindDataBlob = GetUnwindDataBlob(m_moduleBase, pRuntimeFunction, &unwindDataBlobSize);
267 
268         uint8_t unwindBlockFlags = *(dac_cast<DPTR(uint8_t)>(pUnwindDataBlob) + unwindDataBlobSize);
269         if ((unwindBlockFlags & UBF_FUNC_KIND_MASK) == UBF_FUNC_KIND_ROOT)
270             break;
271 
272         pRuntimeFunction--;
273     }
274 
275     pMethodInfo->mainRuntimeFunction = pRuntimeFunction;
276 
277     pMethodInfo->executionAborted = false;
278 
279     return true;
280 }
281 
IsFunclet(MethodInfo * pMethInfo)282 bool CoffNativeCodeManager::IsFunclet(MethodInfo * pMethInfo)
283 {
284     CoffNativeMethodInfo * pMethodInfo = (CoffNativeMethodInfo *)pMethInfo;
285 
286     size_t unwindDataBlobSize;
287     PTR_VOID pUnwindDataBlob = GetUnwindDataBlob(m_moduleBase, pMethodInfo->runtimeFunction, &unwindDataBlobSize);
288 
289     uint8_t unwindBlockFlags = *(dac_cast<DPTR(uint8_t)>(pUnwindDataBlob) + unwindDataBlobSize);
290 
291     // A funclet will have an entry in funclet to main method map
292     return (unwindBlockFlags & UBF_FUNC_KIND_MASK) != UBF_FUNC_KIND_ROOT;
293 }
294 
IsFilter(MethodInfo * pMethInfo)295 bool CoffNativeCodeManager::IsFilter(MethodInfo * pMethInfo)
296 {
297     CoffNativeMethodInfo * pMethodInfo = (CoffNativeMethodInfo *)pMethInfo;
298 
299     size_t unwindDataBlobSize;
300     PTR_VOID pUnwindDataBlob = GetUnwindDataBlob(m_moduleBase, pMethodInfo->runtimeFunction, &unwindDataBlobSize);
301 
302     uint8_t unwindBlockFlags = *(dac_cast<DPTR(uint8_t)>(pUnwindDataBlob) + unwindDataBlobSize);
303 
304     return (unwindBlockFlags & UBF_FUNC_KIND_MASK) == UBF_FUNC_KIND_FILTER;
305 }
306 
GetFramePointer(MethodInfo * pMethInfo,REGDISPLAY * pRegisterSet)307 PTR_VOID CoffNativeCodeManager::GetFramePointer(MethodInfo *   pMethInfo,
308                                          REGDISPLAY *   pRegisterSet)
309 {
310     CoffNativeMethodInfo * pMethodInfo = (CoffNativeMethodInfo *)pMethInfo;
311 
312     size_t unwindDataBlobSize;
313     PTR_VOID pUnwindDataBlob = GetUnwindDataBlob(m_moduleBase, pMethodInfo->runtimeFunction, &unwindDataBlobSize);
314 
315     uint8_t unwindBlockFlags = *(dac_cast<DPTR(uint8_t)>(pUnwindDataBlob) + unwindDataBlobSize);
316 
317     // Return frame pointer for methods with EH and funclets
318     if ((unwindBlockFlags & UBF_FUNC_HAS_EHINFO) != 0 || (unwindBlockFlags & UBF_FUNC_KIND_MASK) != UBF_FUNC_KIND_ROOT)
319     {
320         return (PTR_VOID)pRegisterSet->GetFP();
321     }
322 
323     return NULL;
324 }
325 
EnumGcRefs(MethodInfo * pMethodInfo,PTR_VOID safePointAddress,REGDISPLAY * pRegisterSet,GCEnumContext * hCallback)326 void CoffNativeCodeManager::EnumGcRefs(MethodInfo *    pMethodInfo,
327                                        PTR_VOID        safePointAddress,
328                                        REGDISPLAY *    pRegisterSet,
329                                        GCEnumContext * hCallback)
330 {
331     CoffNativeMethodInfo * pNativeMethodInfo = (CoffNativeMethodInfo *)pMethodInfo;
332 
333     size_t unwindDataBlobSize;
334     PTR_VOID pUnwindDataBlob = GetUnwindDataBlob(m_moduleBase, pNativeMethodInfo->mainRuntimeFunction, &unwindDataBlobSize);
335 
336     PTR_UInt8 p = dac_cast<PTR_UInt8>(pUnwindDataBlob) + unwindDataBlobSize;
337 
338     uint8_t unwindBlockFlags = *p++;
339 
340     if ((unwindBlockFlags & UBF_FUNC_HAS_ASSOCIATED_DATA) != 0)
341         p += sizeof(int32_t);
342 
343     if ((unwindBlockFlags & UBF_FUNC_HAS_EHINFO) != 0)
344         p += sizeof(int32_t);
345 
346     TADDR methodStartAddress = m_moduleBase + pNativeMethodInfo->mainRuntimeFunction->BeginAddress;
347     UInt32 codeOffset = (UInt32)(dac_cast<TADDR>(safePointAddress) - methodStartAddress);
348 
349     GcInfoDecoder decoder(
350         GCInfoToken(p),
351         GcInfoDecoderFlags(DECODE_GC_LIFETIMES | DECODE_SECURITY_OBJECT | DECODE_VARARG),
352         codeOffset - 1 // TODO: Is this adjustment correct?
353         );
354 
355     ICodeManagerFlags flags = (ICodeManagerFlags)0;
356     if (pNativeMethodInfo->executionAborted)
357         flags = ICodeManagerFlags::ExecutionAborted;
358     if (IsFilter(pMethodInfo))
359         flags = (ICodeManagerFlags)(flags | ICodeManagerFlags::NoReportUntracked);
360 
361     if (!decoder.EnumerateLiveSlots(
362         pRegisterSet,
363         false /* reportScratchSlots */,
364         flags,
365         hCallback->pCallback,
366         hCallback
367         ))
368     {
369         assert(false);
370     }
371 }
372 
GetConservativeUpperBoundForOutgoingArgs(MethodInfo * pMethodInfo,REGDISPLAY * pRegisterSet)373 UIntNative CoffNativeCodeManager::GetConservativeUpperBoundForOutgoingArgs(MethodInfo * pMethodInfo, REGDISPLAY * pRegisterSet)
374 {
375     // @TODO: CORERT: GetConservativeUpperBoundForOutgoingArgs
376     assert(false);
377     return false;
378 }
379 
UnwindStackFrame(MethodInfo * pMethodInfo,REGDISPLAY * pRegisterSet,PTR_VOID * ppPreviousTransitionFrame)380 bool CoffNativeCodeManager::UnwindStackFrame(MethodInfo *    pMethodInfo,
381                                       REGDISPLAY *    pRegisterSet,                 // in/out
382                                       PTR_VOID *      ppPreviousTransitionFrame)    // out
383 {
384     CoffNativeMethodInfo * pNativeMethodInfo = (CoffNativeMethodInfo *)pMethodInfo;
385 
386     size_t unwindDataBlobSize;
387     PTR_VOID pUnwindDataBlob = GetUnwindDataBlob(m_moduleBase, pNativeMethodInfo->runtimeFunction, &unwindDataBlobSize);
388 
389     PTR_UInt8 p = dac_cast<PTR_UInt8>(pUnwindDataBlob) + unwindDataBlobSize;
390 
391     uint8_t unwindBlockFlags = *p++;
392 
393     if ((unwindBlockFlags & UBF_FUNC_HAS_ASSOCIATED_DATA) != 0)
394         p += sizeof(int32_t);
395 
396     if ((unwindBlockFlags & UBF_FUNC_REVERSE_PINVOKE) != 0)
397     {
398         // Reverse PInvoke transition should on the main function body only
399         assert(pNativeMethodInfo->mainRuntimeFunction == pNativeMethodInfo->runtimeFunction);
400 
401         if ((unwindBlockFlags & UBF_FUNC_HAS_EHINFO) != 0)
402             p += sizeof(int32_t);
403 
404         GcInfoDecoder decoder(GCInfoToken(p), DECODE_REVERSE_PINVOKE_VAR);
405         INT32 slot = decoder.GetReversePInvokeFrameStackSlot();
406         assert(slot != NO_REVERSE_PINVOKE_FRAME);
407 
408         TADDR basePointer = NULL;
409         UINT32 stackBasedRegister = decoder.GetStackBaseRegister();
410         if (stackBasedRegister == NO_STACK_BASE_REGISTER)
411         {
412             basePointer = dac_cast<TADDR>(pRegisterSet->GetSP());
413         }
414         else
415         {
416             basePointer = dac_cast<TADDR>(pRegisterSet->GetFP());
417         }
418         *ppPreviousTransitionFrame = *(void**)(basePointer + slot);
419         return true;
420     }
421 
422     *ppPreviousTransitionFrame = NULL;
423 
424     CONTEXT context;
425     KNONVOLATILE_CONTEXT_POINTERS contextPointers;
426 
427 #ifdef _DEBUG
428     memset(&context, 0xDD, sizeof(context));
429     memset(&contextPointers, 0xDD, sizeof(contextPointers));
430 #endif
431 
432 #ifdef _TARGET_X86_
433     #define FOR_EACH_NONVOLATILE_REGISTER(F) \
434         F(E, ax) F(E, cx) F(E, dx) F(E, bx) F(E, bp) F(E, si) F(E, di)
435     #define WORDPTR PDWORD
436 #else
437     #define FOR_EACH_NONVOLATILE_REGISTER(F) \
438         F(R, ax) F(R, cx) F(R, dx) F(R, bx) F(R, bp) F(R, si) F(R, di) \
439         F(R, 8) F(R, 9) F(R, 10) F(R, 11) F(R, 12) F(R, 13) F(R, 14) F(R, 15)
440     #define WORDPTR PDWORD64
441 #endif
442 
443 #define REGDISPLAY_TO_CONTEXT(prefix, reg) \
444     contextPointers.prefix####reg = (WORDPTR) pRegisterSet->pR##reg; \
445     if (pRegisterSet->pR##reg != NULL) context.prefix##reg = *(pRegisterSet->pR##reg);
446 
447 #define CONTEXT_TO_REGDISPLAY(prefix, reg) \
448     pRegisterSet->pR##reg = (PTR_UIntNative) contextPointers.prefix####reg;
449 
450     FOR_EACH_NONVOLATILE_REGISTER(REGDISPLAY_TO_CONTEXT);
451 
452 #ifdef _TARGET_X86_
453     PORTABILITY_ASSERT("CoffNativeCodeManager::UnwindStackFrame");
454 #else // _TARGET_X86_
455     memcpy(&context.Xmm6, pRegisterSet->Xmm, sizeof(pRegisterSet->Xmm));
456 
457     context.Rsp = pRegisterSet->SP;
458     context.Rip = pRegisterSet->IP;
459 
460     SIZE_T  EstablisherFrame;
461     PVOID   HandlerData;
462 
463     RtlVirtualUnwind(NULL,
464                     dac_cast<TADDR>(m_moduleBase),
465                     pRegisterSet->IP,
466                     (PRUNTIME_FUNCTION)pNativeMethodInfo->runtimeFunction,
467                     &context,
468                     &HandlerData,
469                     &EstablisherFrame,
470                     &contextPointers);
471 
472     pRegisterSet->SP = context.Rsp;
473     pRegisterSet->IP = context.Rip;
474 
475     pRegisterSet->pIP = PTR_PCODE(pRegisterSet->SP - sizeof(TADDR));
476 
477     memcpy(pRegisterSet->Xmm, &context.Xmm6, sizeof(pRegisterSet->Xmm));
478 #endif // _TARGET_X86_
479 
480     FOR_EACH_NONVOLATILE_REGISTER(CONTEXT_TO_REGDISPLAY);
481 
482 #undef FOR_EACH_NONVOLATILE_REGISTER
483 #undef REGDISPLAY_TO_CONTEXT
484 #undef CONTEXT_TO_REGDISPLAY
485 
486     return true;
487 }
488 
489 // Convert the return kind that was encoded by RyuJIT to the
490 // value that CoreRT runtime can understand and support.
GetGcRefKind(ReturnKind returnKind)491 GCRefKind GetGcRefKind(ReturnKind returnKind)
492 {
493     static_assert((GCRefKind)ReturnKind::RT_Scalar == GCRK_Scalar, "ReturnKind::RT_Scalar does not match GCRK_Scalar");
494     static_assert((GCRefKind)ReturnKind::RT_Object == GCRK_Object, "ReturnKind::RT_Object does not match GCRK_Object");
495     static_assert((GCRefKind)ReturnKind::RT_ByRef  == GCRK_Byref, "ReturnKind::RT_ByRef does not match GCRK_Byref");
496     ASSERT((returnKind == RT_Scalar) || (returnKind == GCRK_Object) || (returnKind == GCRK_Byref));
497 
498     return (GCRefKind)returnKind;
499 }
500 
GetReturnAddressHijackInfo(MethodInfo * pMethodInfo,REGDISPLAY * pRegisterSet,PTR_PTR_VOID * ppvRetAddrLocation,GCRefKind * pRetValueKind)501 bool CoffNativeCodeManager::GetReturnAddressHijackInfo(MethodInfo *    pMethodInfo,
502                                                 REGDISPLAY *    pRegisterSet,       // in
503                                                 PTR_PTR_VOID *  ppvRetAddrLocation, // out
504                                                 GCRefKind *     pRetValueKind)      // out
505 {
506 #if defined(_TARGET_AMD64_)
507     CoffNativeMethodInfo * pNativeMethodInfo = (CoffNativeMethodInfo *)pMethodInfo;
508 
509     size_t unwindDataBlobSize;
510     PTR_VOID pUnwindDataBlob = GetUnwindDataBlob(m_moduleBase, pNativeMethodInfo->runtimeFunction, &unwindDataBlobSize);
511 
512     PTR_UInt8 p = dac_cast<PTR_UInt8>(pUnwindDataBlob) + unwindDataBlobSize;
513 
514     uint8_t unwindBlockFlags = *p++;
515 
516     if ((unwindBlockFlags & UBF_FUNC_HAS_ASSOCIATED_DATA) != 0)
517         p += sizeof(int32_t);
518 
519     // Check whether this is a funclet
520     if ((unwindBlockFlags & UBF_FUNC_KIND_MASK) != UBF_FUNC_KIND_ROOT)
521         return false;
522 
523     // Skip hijacking a reverse-pinvoke method - it doesn't get us much because we already synchronize
524     // with the GC on the way back to native code.
525     if ((unwindBlockFlags & UBF_FUNC_REVERSE_PINVOKE) != 0)
526         return false;
527 
528     if ((unwindBlockFlags & UBF_FUNC_HAS_EHINFO) != 0)
529         p += sizeof(int32_t);
530 
531     // Decode the GC info for the current method to detemine its return type
532     GcInfoDecoder decoder(
533         GCInfoToken(p),
534         GcInfoDecoderFlags(DECODE_RETURN_KIND),
535         0
536         );
537 
538     GCRefKind gcRefKind = GetGcRefKind(decoder.GetReturnKind());
539 
540     // Unwind the current method context to the caller's context to get its stack pointer
541     // and obtain the location of the return address on the stack
542     SIZE_T  EstablisherFrame;
543     PVOID   HandlerData;
544     CONTEXT context;
545     context.Rsp = pRegisterSet->GetSP();
546     context.Rbp = pRegisterSet->GetFP();
547     context.Rip = pRegisterSet->GetIP();
548 
549     RtlVirtualUnwind(NULL,
550                     dac_cast<TADDR>(m_moduleBase),
551                     pRegisterSet->IP,
552                     (PRUNTIME_FUNCTION)pNativeMethodInfo->runtimeFunction,
553                     &context,
554                     &HandlerData,
555                     &EstablisherFrame,
556                     NULL);
557 
558     *ppvRetAddrLocation = (PTR_PTR_VOID)(context.Rsp - sizeof (PVOID));
559     *pRetValueKind = gcRefKind;
560     return true;
561 #else
562     return false;
563 #endif // defined(_TARGET_AMD64_)
564 }
565 
UnsynchronizedHijackMethodLoops(MethodInfo * pMethodInfo)566 void CoffNativeCodeManager::UnsynchronizedHijackMethodLoops(MethodInfo * pMethodInfo)
567 {
568     // @TODO: CORERT: UnsynchronizedHijackMethodLoops
569 }
570 
RemapHardwareFaultToGCSafePoint(MethodInfo * pMethodInfo,PTR_VOID controlPC)571 PTR_VOID CoffNativeCodeManager::RemapHardwareFaultToGCSafePoint(MethodInfo * pMethodInfo, PTR_VOID controlPC)
572 {
573     // GCInfo decoder needs to know whether execution of the method is aborted
574     // while querying for gc-info.  But ICodeManager::EnumGCRef() doesn't receive any
575     // flags from mrt. Call to this method is used as a cue to mark the method info
576     // as execution aborted. Note - if pMethodInfo was cached, this scheme would not work.
577     //
578     // If the method has EH, then JIT will make sure the method is fully interruptible
579     // and we will have GC-info available at the faulting address as well.
580 
581     CoffNativeMethodInfo * pNativeMethodInfo = (CoffNativeMethodInfo *)pMethodInfo;
582     pNativeMethodInfo->executionAborted = true;
583 
584     return controlPC;
585 }
586 
587 struct CoffEHEnumState
588 {
589     PTR_UInt8 pMethodStartAddress;
590     PTR_UInt8 pEHInfo;
591     UInt32 uClause;
592     UInt32 nClauses;
593 };
594 
595 // Ensure that CoffEHEnumState fits into the space reserved by EHEnumState
596 static_assert(sizeof(CoffEHEnumState) <= sizeof(EHEnumState), "CoffEHEnumState too big");
597 
EHEnumInit(MethodInfo * pMethodInfo,PTR_VOID * pMethodStartAddress,EHEnumState * pEHEnumStateOut)598 bool CoffNativeCodeManager::EHEnumInit(MethodInfo * pMethodInfo, PTR_VOID * pMethodStartAddress, EHEnumState * pEHEnumStateOut)
599 {
600     assert(pMethodInfo != NULL);
601     assert(pMethodStartAddress != NULL);
602     assert(pEHEnumStateOut != NULL);
603 
604     CoffNativeMethodInfo * pNativeMethodInfo = (CoffNativeMethodInfo *)pMethodInfo;
605     CoffEHEnumState * pEnumState = (CoffEHEnumState *)pEHEnumStateOut;
606 
607     size_t unwindDataBlobSize;
608     PTR_VOID pUnwindDataBlob = GetUnwindDataBlob(m_moduleBase, pNativeMethodInfo->mainRuntimeFunction, &unwindDataBlobSize);
609 
610     PTR_UInt8 p = dac_cast<PTR_UInt8>(pUnwindDataBlob) + unwindDataBlobSize;
611 
612     uint8_t unwindBlockFlags = *p++;
613 
614     if ((unwindBlockFlags & UBF_FUNC_HAS_ASSOCIATED_DATA) != 0)
615         p += sizeof(int32_t);
616 
617     // return if there is no EH info associated with this method
618     if ((unwindBlockFlags & UBF_FUNC_HAS_EHINFO) == 0)
619     {
620         return false;
621     }
622 
623     *pMethodStartAddress = dac_cast<PTR_VOID>(m_moduleBase + pNativeMethodInfo->mainRuntimeFunction->BeginAddress);
624 
625     pEnumState->pMethodStartAddress = dac_cast<PTR_UInt8>(*pMethodStartAddress);
626     pEnumState->pEHInfo = dac_cast<PTR_UInt8>(m_moduleBase + *dac_cast<PTR_Int32>(p));
627     pEnumState->uClause = 0;
628     pEnumState->nClauses = VarInt::ReadUnsigned(pEnumState->pEHInfo);
629 
630     return true;
631 }
632 
EHEnumNext(EHEnumState * pEHEnumState,EHClause * pEHClauseOut)633 bool CoffNativeCodeManager::EHEnumNext(EHEnumState * pEHEnumState, EHClause * pEHClauseOut)
634 {
635     assert(pEHEnumState != NULL);
636     assert(pEHClauseOut != NULL);
637 
638     CoffEHEnumState * pEnumState = (CoffEHEnumState *)pEHEnumState;
639     if (pEnumState->uClause >= pEnumState->nClauses)
640         return false;
641     pEnumState->uClause++;
642 
643     pEHClauseOut->m_tryStartOffset = VarInt::ReadUnsigned(pEnumState->pEHInfo);
644 
645     UInt32 tryEndDeltaAndClauseKind = VarInt::ReadUnsigned(pEnumState->pEHInfo);
646     pEHClauseOut->m_clauseKind = (EHClauseKind)(tryEndDeltaAndClauseKind & 0x3);
647     pEHClauseOut->m_tryEndOffset = pEHClauseOut->m_tryStartOffset + (tryEndDeltaAndClauseKind >> 2);
648 
649     // For each clause, we have up to 4 integers:
650     //      1)  try start offset
651     //      2)  (try length << 2) | clauseKind
652     //      3)  if (typed || fault || filter)    { handler start offset }
653     //      4a) if (typed)                       { type RVA }
654     //      4b) if (filter)                      { filter start offset }
655     //
656     // The first two integers have already been decoded
657 
658     switch (pEHClauseOut->m_clauseKind)
659     {
660     case EH_CLAUSE_TYPED:
661         pEHClauseOut->m_handlerAddress = pEnumState->pMethodStartAddress + VarInt::ReadUnsigned(pEnumState->pEHInfo);
662 
663         // Read target type
664         {
665             // @TODO: CORERT: Compress EHInfo using type table index scheme
666             // https://github.com/dotnet/corert/issues/972
667             UInt32 typeRVA = *((PTR_UInt32&)pEnumState->pEHInfo)++;
668             pEHClauseOut->m_pTargetType = dac_cast<PTR_VOID>(m_moduleBase + typeRVA);
669         }
670         break;
671     case EH_CLAUSE_FAULT:
672         pEHClauseOut->m_handlerAddress = pEnumState->pMethodStartAddress + VarInt::ReadUnsigned(pEnumState->pEHInfo);
673         break;
674     case EH_CLAUSE_FILTER:
675         pEHClauseOut->m_handlerAddress = pEnumState->pMethodStartAddress + VarInt::ReadUnsigned(pEnumState->pEHInfo);
676         pEHClauseOut->m_filterAddress = pEnumState->pMethodStartAddress + VarInt::ReadUnsigned(pEnumState->pEHInfo);
677         break;
678     default:
679         UNREACHABLE_MSG("unexpected EHClauseKind");
680     }
681 
682     return true;
683 }
684 
GetOsModuleHandle()685 PTR_VOID CoffNativeCodeManager::GetOsModuleHandle()
686 {
687     return dac_cast<PTR_VOID>(m_moduleBase);
688 }
689 
GetMethodStartAddress(MethodInfo * pMethodInfo)690 PTR_VOID CoffNativeCodeManager::GetMethodStartAddress(MethodInfo * pMethodInfo)
691 {
692     CoffNativeMethodInfo * pNativeMethodInfo = (CoffNativeMethodInfo *)pMethodInfo;
693     return dac_cast<PTR_VOID>(m_moduleBase + pNativeMethodInfo->mainRuntimeFunction->BeginAddress);
694 }
695 
GetClasslibFunction(ClasslibFunctionId functionId)696 void * CoffNativeCodeManager::GetClasslibFunction(ClasslibFunctionId functionId)
697 {
698     uint32_t id = (uint32_t)functionId;
699 
700     if (id >= m_nClasslibFunctions)
701         return nullptr;
702 
703     return m_pClasslibFunctions[id];
704 }
705 
GetAssociatedData(PTR_VOID ControlPC)706 PTR_VOID CoffNativeCodeManager::GetAssociatedData(PTR_VOID ControlPC)
707 {
708     TADDR relativePC = dac_cast<TADDR>(ControlPC) - m_moduleBase;
709 
710     int MethodIndex = LookupUnwindInfoForMethod((UInt32)relativePC, m_pRuntimeFunctionTable, 0, m_nRuntimeFunctionTable - 1);
711     if (MethodIndex < 0)
712         return NULL;
713 
714     PTR_RUNTIME_FUNCTION pRuntimeFunction = m_pRuntimeFunctionTable + MethodIndex;
715 
716     size_t unwindDataBlobSize;
717     PTR_VOID pUnwindDataBlob = GetUnwindDataBlob(m_moduleBase, pRuntimeFunction, &unwindDataBlobSize);
718 
719     PTR_UInt8 p = dac_cast<PTR_UInt8>(pUnwindDataBlob) + unwindDataBlobSize;
720 
721     uint8_t unwindBlockFlags = *p++;
722     if ((unwindBlockFlags & UBF_FUNC_HAS_ASSOCIATED_DATA) == 0)
723         return NULL;
724 
725     UInt32 dataRVA = *(UInt32*)p;
726     return dac_cast<PTR_VOID>(m_moduleBase + dataRVA);
727 }
728 
729 extern "C" bool __stdcall RegisterCodeManager(ICodeManager * pCodeManager, PTR_VOID pvStartRange, UInt32 cbRange);
730 extern "C" void __stdcall UnregisterCodeManager(ICodeManager * pCodeManager);
731 extern "C" bool __stdcall RegisterUnboxingStubs(PTR_VOID pvStartRange, UInt32 cbRange);
732 
733 extern "C"
RhRegisterOSModule(void * pModule,void * pvManagedCodeStartRange,UInt32 cbManagedCodeRange,void * pvUnboxingStubsStartRange,UInt32 cbUnboxingStubsRange,void ** pClasslibFunctions,UInt32 nClasslibFunctions)734 bool RhRegisterOSModule(void * pModule,
735                         void * pvManagedCodeStartRange, UInt32 cbManagedCodeRange,
736                         void * pvUnboxingStubsStartRange, UInt32 cbUnboxingStubsRange,
737                         void ** pClasslibFunctions, UInt32 nClasslibFunctions)
738 {
739     PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pModule;
740     PIMAGE_NT_HEADERS pNTHeaders = (PIMAGE_NT_HEADERS)((TADDR)pModule + pDosHeader->e_lfanew);
741 
742     IMAGE_DATA_DIRECTORY * pRuntimeFunctions = &(pNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION]);
743 
744     NewHolder<CoffNativeCodeManager> pCoffNativeCodeManager = new (nothrow) CoffNativeCodeManager((TADDR)pModule,
745         dac_cast<PTR_RUNTIME_FUNCTION>((TADDR)pModule + pRuntimeFunctions->VirtualAddress),
746         pRuntimeFunctions->Size / sizeof(RUNTIME_FUNCTION),
747         pClasslibFunctions, nClasslibFunctions);
748 
749     if (pCoffNativeCodeManager == nullptr)
750         return false;
751 
752     if (!RegisterCodeManager(pCoffNativeCodeManager, pvManagedCodeStartRange, cbManagedCodeRange))
753         return false;
754 
755     if (!RegisterUnboxingStubs(pvUnboxingStubsStartRange, cbUnboxingStubsRange))
756     {
757         UnregisterCodeManager(pCoffNativeCodeManager);
758         return false;
759     }
760 
761     pCoffNativeCodeManager.SuppressRelease();
762 
763     return true;
764 }
765