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