Fix memory leaked caused by Marshal.GetFunctionPointerForDelegate (#28074)
[platform/upstream/coreclr.git] / src / vm / dllimportcallback.cpp
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 // File: DllImportCallback.cpp
6 //
7
8 //
9
10
11 #include "common.h"
12
13 #include "threads.h"
14 #include "excep.h"
15 #include "object.h"
16 #include "dllimportcallback.h"
17 #include "mlinfo.h"
18 #include "comdelegate.h"
19 #include "ceeload.h"
20 #include "eeconfig.h"
21 #include "dbginterface.h"
22 #include "stubgen.h"
23 #include "appdomain.inl"
24
25 #ifndef CROSSGEN_COMPILE
26
27 struct UM2MThunk_Args
28 {
29     UMEntryThunk *pEntryThunk;
30     void *pAddr;
31     void *pThunkArgs;
32     int argLen;
33 };
34
35 class UMEntryThunkFreeList
36 {
37 public:
38     UMEntryThunkFreeList(size_t threshold) :
39         m_threshold(threshold),
40         m_count(0),
41         m_pHead(NULL),
42         m_pTail(NULL)
43     {
44         WRAPPER_NO_CONTRACT;
45
46         m_crst.Init(CrstLeafLock, CRST_UNSAFE_ANYMODE);
47     }
48
49     UMEntryThunk *GetUMEntryThunk()
50     {
51         WRAPPER_NO_CONTRACT;
52
53         if (m_count < m_threshold)
54             return NULL;
55
56         CrstHolder ch(&m_crst);
57
58         UMEntryThunk *pThunk = m_pHead;
59
60         if (pThunk == NULL)
61             return NULL;
62
63         m_pHead = m_pHead->m_pNextFreeThunk;
64         --m_count;
65
66         return pThunk;
67     }
68
69     void AddToList(UMEntryThunk *pThunk)
70     {
71         CONTRACTL
72         {
73             NOTHROW;
74         }
75         CONTRACTL_END;
76
77         CrstHolder ch(&m_crst);
78
79         if (m_pHead == NULL)
80         {
81             m_pHead = pThunk;
82             m_pTail = pThunk;
83         }
84         else
85         {
86             m_pTail->m_pNextFreeThunk = pThunk;
87             m_pTail = pThunk;
88         }
89
90         pThunk->m_pNextFreeThunk = NULL;
91
92         ++m_count;
93     }
94
95 private:
96     // Used to delay reusing freed thunks
97     size_t m_threshold;
98     size_t m_count;
99     UMEntryThunk *m_pHead;
100     UMEntryThunk *m_pTail;
101     CrstStatic m_crst;
102 };
103
104 #define DEFAULT_THUNK_FREE_LIST_THRESHOLD 64
105
106 static UMEntryThunkFreeList s_thunkFreeList(DEFAULT_THUNK_FREE_LIST_THRESHOLD);
107
108 #if defined(_TARGET_X86_) && !defined(FEATURE_STUBS_AS_IL)
109
110 EXTERN_C VOID __cdecl UMThunkStubRareDisable();
111 EXTERN_C Thread* __stdcall CreateThreadBlockThrow();
112
113 // argument stack offsets are multiple of sizeof(SLOT) so we can tag them by OR'ing with 1
114 static_assert_no_msg((sizeof(SLOT) & 1) == 0);
115 #define MAKE_BYVAL_STACK_OFFSET(x) (x)
116 #define MAKE_BYREF_STACK_OFFSET(x) ((x) | 1)
117 #define IS_BYREF_STACK_OFFSET(x)   ((x) & 1)
118 #define GET_STACK_OFFSET(x)        ((x) & ~1)
119
120 // -1 means not used
121 #define UNUSED_STACK_OFFSET        (UINT)-1
122
123 // static
124 VOID UMEntryThunk::CompileUMThunkWorker(UMThunkStubInfo *pInfo,
125                                         CPUSTUBLINKER *pcpusl,
126                                         UINT *psrcofsregs, // NUM_ARGUMENT_REGISTERS elements
127                                         UINT *psrcofs,     // pInfo->m_cbDstStack/STACK_ELEM_SIZE elements
128                                         UINT retbufofs)    // the large structure return buffer ptr arg offset (if any)
129 {
130     STANDARD_VM_CONTRACT;
131
132     CodeLabel* pSetupThreadLabel    = pcpusl->NewCodeLabel();
133     CodeLabel* pRejoinThreadLabel   = pcpusl->NewCodeLabel();
134     CodeLabel* pDisableGCLabel      = pcpusl->NewCodeLabel();
135     CodeLabel* pRejoinGCLabel       = pcpusl->NewCodeLabel();
136
137     // We come into this code with UMEntryThunk in EAX
138     const X86Reg kEAXentryThunk = kEAX;
139
140     // For ThisCall, we make it look like a normal stdcall so that
141     // the rest of the code (like repushing the arguments) does not
142     // have to worry about it.
143
144     if (pInfo->m_wFlags & umtmlThisCall)
145     {
146         // pop off the return address into EDX
147         pcpusl->X86EmitPopReg(kEDX);
148
149         if (pInfo->m_wFlags & umtmlThisCallHiddenArg)
150         {
151             // exchange ecx ( "this") with the hidden structure return buffer
152             //  xchg ecx, [esp]
153             pcpusl->X86EmitOp(0x87, kECX, (X86Reg)4 /*ESP*/);
154         }
155
156         // jam ecx (the "this" param onto stack. Now it looks like a normal stdcall.)
157         pcpusl->X86EmitPushReg(kECX);
158
159         // push edx - repush the return address
160         pcpusl->X86EmitPushReg(kEDX);
161     }
162
163     // Setup the EBP frame
164     pcpusl->X86EmitPushEBPframe();
165
166     // Save EBX
167     pcpusl->X86EmitPushReg(kEBX);
168
169     // Make space for return value - instead of repeatedly doing push eax edx <trash regs> pop edx eax
170     // we will save the return value once and restore it just before returning.
171     pcpusl->X86EmitSubEsp(sizeof(PCONTEXT(NULL)->Eax) + sizeof(PCONTEXT(NULL)->Edx));
172     
173     // Load thread descriptor into ECX
174     const X86Reg kECXthread = kECX;
175
176     // save UMEntryThunk
177     pcpusl->X86EmitPushReg(kEAXentryThunk);
178
179     pcpusl->EmitSetup(pSetupThreadLabel);
180
181     pcpusl->X86EmitMovRegReg(kECX, kEBX);
182
183     pcpusl->EmitLabel(pRejoinThreadLabel);
184
185     // restore UMEntryThunk
186     pcpusl->X86EmitPopReg(kEAXentryThunk);
187
188 #ifdef _DEBUG
189     // Save incoming registers
190     pcpusl->X86EmitPushReg(kEAXentryThunk); // UMEntryThunk
191     pcpusl->X86EmitPushReg(kECXthread); // thread descriptor
192
193     pcpusl->X86EmitPushReg(kEAXentryThunk);
194     pcpusl->X86EmitCall(pcpusl->NewExternalCodeLabel((LPVOID) LogUMTransition), 4);
195
196     // Restore registers
197     pcpusl->X86EmitPopReg(kECXthread);
198     pcpusl->X86EmitPopReg(kEAXentryThunk);
199 #endif
200
201 #ifdef PROFILING_SUPPORTED
202     // Notify profiler of transition into runtime, before we disable preemptive GC
203     if (CORProfilerTrackTransitions())
204     {
205         // Load the methoddesc into EBX (UMEntryThunk->m_pMD)
206         pcpusl->X86EmitIndexRegLoad(kEBX, kEAXentryThunk, UMEntryThunk::GetOffsetOfMethodDesc());
207
208         // Save registers
209         pcpusl->X86EmitPushReg(kEAXentryThunk); // UMEntryThunk
210         pcpusl->X86EmitPushReg(kECXthread); // pCurThread
211
212         // Push arguments and notify profiler
213         pcpusl->X86EmitPushImm32(COR_PRF_TRANSITION_CALL);    // Reason
214         pcpusl->X86EmitPushReg(kEBX);          // MethodDesc*
215         pcpusl->X86EmitCall(pcpusl->NewExternalCodeLabel((LPVOID)ProfilerUnmanagedToManagedTransitionMD), 8);
216
217         // Restore registers
218         pcpusl->X86EmitPopReg(kECXthread);
219         pcpusl->X86EmitPopReg(kEAXentryThunk);
220
221         // Push the MethodDesc* (in EBX) for use by the transition on the way out.
222         pcpusl->X86EmitPushReg(kEBX);
223     }
224 #endif // PROFILING_SUPPORTED
225
226     pcpusl->EmitDisable(pDisableGCLabel, TRUE, kECXthread);
227
228     pcpusl->EmitLabel(pRejoinGCLabel);
229
230     // construct a FrameHandlerExRecord
231
232     // push [ECX]Thread.m_pFrame - corresponding to FrameHandlerExRecord::m_pEntryFrame
233     pcpusl->X86EmitIndexPush(kECXthread, offsetof(Thread, m_pFrame));
234
235     // push offset FastNExportExceptHandler
236     pcpusl->X86EmitPushImm32((INT32)(size_t)FastNExportExceptHandler);
237
238     // push fs:[0]
239     const static BYTE codeSEH1[] = { 0x64, 0xFF, 0x35, 0x0, 0x0, 0x0, 0x0};
240     pcpusl->EmitBytes(codeSEH1, sizeof(codeSEH1));
241
242     // link in the exception frame
243     // mov dword ptr fs:[0], esp
244     const static BYTE codeSEH2[] = { 0x64, 0x89, 0x25, 0x0, 0x0, 0x0, 0x0};
245     pcpusl->EmitBytes(codeSEH2, sizeof(codeSEH2));
246
247     // EBX will hold address of start of arguments. Calculate here so the AD switch case can access
248     // the arguments at their original location rather than re-copying them to the inner frame.
249     // lea ebx, [ebp + 8]
250     pcpusl->X86EmitIndexLea(kEBX, kEBP, 8);
251
252     //
253     // ----------------------------------------------------------------------------------------------
254     //
255     // From this point on (until noted) we might be executing as the result of calling into the
256     // runtime in order to switch AppDomain. In order for the following code to function in both
257     // scenarios it must be careful when making assumptions about the current stack layout (in the AD
258     // switch case a new inner frame has been pushed which is not identical to the original outer
259     // frame).
260     //
261     // Our guaranteed state at this point is as follows:
262     //   EAX: Pointer to UMEntryThunk
263     //   EBX: Pointer to start of caller's arguments
264     //   ECX: Pointer to current Thread
265     //   EBP: Equals EBX - 8 (no AD switch) or unspecified (AD switch)
266     //
267     // Stack:
268     //
269     //            +-------------------------+
270     //    ESP + 0 |                         |
271     //
272     //            |         Varies          |
273     //
274     //            |                         |
275     //            +-------------------------+
276     //   EBX - 20 | Saved Result: EDX/ST(0) |
277     //            +- - - - - - - - - - - - -+
278     //   EBX - 16 | Saved Result: EAX/ST(0) |
279     //            +-------------------------+
280     //   EBX - 12 |      Caller's EBX       |
281     //            +-------------------------+
282     //    EBX - 8 |      Caller's EBP       |
283     //            +-------------------------+
284     //    EBX - 4 |     Return address      |
285     //            +-------------------------+
286     //    EBX + 0 |                         |
287     //
288     //            |   Caller's arguments    |
289     //
290     //            |                         |
291     //            +-------------------------+
292     //
293
294     // save the thread pointer
295     pcpusl->X86EmitPushReg(kECXthread);
296
297     // reserve the space for call slot
298     pcpusl->X86EmitSubEsp(4);
299
300     // remember stack size for offset computations
301     INT iStackSizeAtCallSlot = pcpusl->GetStackSize();
302
303     if (!(pInfo->m_wFlags & umtmlSkipStub))
304     {
305         // save EDI (it's used by the IL stub invocation code)
306         pcpusl->X86EmitPushReg(kEDI);
307     }
308
309     // repush any stack arguments
310     int arg = pInfo->m_cbDstStack/STACK_ELEM_SIZE;
311
312     while (arg--)
313     {
314         if (IS_BYREF_STACK_OFFSET(psrcofs[arg]))
315         {
316             // lea ecx, [ebx + ofs]
317             pcpusl->X86EmitIndexLea(kECX, kEBX, GET_STACK_OFFSET(psrcofs[arg]));
318
319             // push ecx
320             pcpusl->X86EmitPushReg(kECX);
321         }
322         else
323         {
324             // push dword ptr [ebx + ofs]
325             pcpusl->X86EmitIndexPush(kEBX, GET_STACK_OFFSET(psrcofs[arg]));
326         }
327     }
328
329     // load register arguments
330     int regidx = 0;
331
332 #define ARGUMENT_REGISTER(regname)                                                                 \
333     if (psrcofsregs[regidx] != UNUSED_STACK_OFFSET)                                                \
334     {                                                                                              \
335         if (IS_BYREF_STACK_OFFSET(psrcofsregs[regidx]))                                            \
336         {                                                                                          \
337             /* lea reg, [ebx + ofs] */                                                             \
338             pcpusl->X86EmitIndexLea(k##regname, kEBX, GET_STACK_OFFSET(psrcofsregs[regidx]));      \
339         }                                                                                          \
340         else                                                                                       \
341         {                                                                                          \
342             /* mov reg, [ebx + ofs] */                                                             \
343             pcpusl->X86EmitIndexRegLoad(k##regname, kEBX, GET_STACK_OFFSET(psrcofsregs[regidx]));  \
344         }                                                                                          \
345     }                                                                                              \
346     regidx++;
347
348     ENUM_ARGUMENT_REGISTERS_BACKWARD();
349
350 #undef ARGUMENT_REGISTER
351
352     if (!(pInfo->m_wFlags & umtmlSkipStub))
353     {
354         //
355         // Call the IL stub which will:
356         // 1) marshal
357         // 2) call the managed method
358         // 3) unmarshal
359         //
360
361         // the delegate object is extracted by the stub from UMEntryThunk
362         _ASSERTE(pInfo->m_wFlags & umtmlIsStatic);
363
364         // mov EDI, [EAX + UMEntryThunk.m_pUMThunkMarshInfo]
365         pcpusl->X86EmitIndexRegLoad(kEDI, kEAXentryThunk, offsetof(UMEntryThunk, m_pUMThunkMarshInfo));
366
367         // mov EDI, [EDI + UMThunkMarshInfo.m_pILStub]
368         pcpusl->X86EmitIndexRegLoad(kEDI, kEDI, UMThunkMarshInfo::GetOffsetOfStub());
369
370         // EAX still contains the UMEntryThunk pointer, so we cannot really use SCRATCHREG
371         // we can use EDI, though
372
373         INT iCallSlotOffset = pcpusl->GetStackSize() - iStackSizeAtCallSlot;
374
375         // mov [ESP+iCallSlotOffset], EDI
376         pcpusl->X86EmitIndexRegStore((X86Reg)kESP_Unsafe, iCallSlotOffset, kEDI);
377
378         // call [ESP+iCallSlotOffset]
379         pcpusl->X86EmitOp(0xff, (X86Reg)2, (X86Reg)kESP_Unsafe, iCallSlotOffset);
380
381         // Emit a NOP so we know that we can call managed code
382         INDEBUG(pcpusl->Emit8(X86_INSTR_NOP)); 
383
384         // restore EDI
385         pcpusl->X86EmitPopReg(kEDI);
386     }
387     else if (!(pInfo->m_wFlags & umtmlIsStatic))
388     {
389         //
390         // This is call on delegate
391         //
392
393         // mov THIS, [EAX + UMEntryThunk.m_pObjectHandle]
394         pcpusl->X86EmitOp(0x8b, THIS_kREG, kEAXentryThunk, offsetof(UMEntryThunk, m_pObjectHandle));
395
396         // mov THIS, [THIS]
397         pcpusl->X86EmitOp(0x8b, THIS_kREG, THIS_kREG);
398
399         //
400         // Inline Delegate.Invoke for perf
401         //
402
403         // mov SCRATCHREG, [THISREG + Delegate.FP]  ; Save target stub in register
404         pcpusl->X86EmitIndexRegLoad(SCRATCH_REGISTER_X86REG, THIS_kREG, DelegateObject::GetOffsetOfMethodPtr());
405
406         // mov THISREG, [THISREG + Delegate.OR]  ; replace "this" pointer
407         pcpusl->X86EmitIndexRegLoad(THIS_kREG, THIS_kREG, DelegateObject::GetOffsetOfTarget());
408
409         INT iCallSlotOffset = pcpusl->GetStackSize() - iStackSizeAtCallSlot;
410
411         // mov [ESP+iCallSlotOffset], SCRATCHREG
412         pcpusl->X86EmitIndexRegStore((X86Reg)kESP_Unsafe,iCallSlotOffset,SCRATCH_REGISTER_X86REG);
413         
414         // call [ESP+iCallSlotOffset]
415         pcpusl->X86EmitOp(0xff, (X86Reg)2, (X86Reg)kESP_Unsafe, iCallSlotOffset);
416
417         INDEBUG(pcpusl->Emit8(X86_INSTR_NOP)); // Emit a NOP so we know that we can call managed code
418     }
419     else
420     {
421         //
422         // Call the managed method
423         //
424
425         INT iCallSlotOffset = pcpusl->GetStackSize() - iStackSizeAtCallSlot;
426
427         // mov SCRATCH, [SCRATCH + offsetof(UMEntryThunk.m_pManagedTarget)]
428         pcpusl->X86EmitIndexRegLoad(SCRATCH_REGISTER_X86REG, SCRATCH_REGISTER_X86REG, offsetof(UMEntryThunk, m_pManagedTarget));
429
430         // mov [ESP+iCallSlotOffset], SCRATCHREG
431         pcpusl->X86EmitIndexRegStore((X86Reg)kESP_Unsafe, iCallSlotOffset, SCRATCH_REGISTER_X86REG);
432
433         // call [ESP+iCallSlotOffset]
434         pcpusl->X86EmitOp(0xff, (X86Reg)2, (X86Reg)kESP_Unsafe, iCallSlotOffset);
435
436         INDEBUG(pcpusl->Emit8(X86_INSTR_NOP)); // Emit a NOP so we know that we can call managed code
437     }
438     
439     // skip the call slot
440     pcpusl->X86EmitAddEsp(4);
441
442     // Save the return value to the outer frame
443     if (pInfo->m_wFlags & umtmlFpu)
444     {
445         // save FP return value
446
447         // fstp qword ptr [ebx - 0x8 - 0xc]
448         pcpusl->X86EmitOffsetModRM(0xdd, (X86Reg)3, kEBX, -0x8 /* to outer EBP */ -0xc /* skip saved EBP, EBX */);
449     }
450     else
451     {
452         // save EDX:EAX
453         if (retbufofs == UNUSED_STACK_OFFSET)
454         {
455             pcpusl->X86EmitIndexRegStore(kEBX, -0x8 /* to outer EBP */ -0x8 /* skip saved EBP, EBX */, kEAX);
456             pcpusl->X86EmitIndexRegStore(kEBX, -0x8 /* to outer EBP */ -0xc /* skip saved EBP, EBX, EAX */, kEDX);
457         }
458         else
459         {
460             // pretend that the method returned the ret buf hidden argument
461             // (the structure ptr); C++ compiler seems to rely on this
462
463             // mov dword ptr eax, [ebx + retbufofs]
464             pcpusl->X86EmitIndexRegLoad(kEAX, kEBX, retbufofs);
465
466             // save it as the return value
467             pcpusl->X86EmitIndexRegStore(kEBX, -0x8 /* to outer EBP */ -0x8 /* skip saved EBP, EBX */, kEAX);
468         }
469     }
470
471     // restore the thread pointer
472     pcpusl->X86EmitPopReg(kECXthread);
473
474     //
475     // Once we reach this point in the code we're back to a single scenario: the outer frame of the
476     // reverse p/invoke.
477     //
478     // ----------------------------------------------------------------------------------------------
479     //
480
481     // move byte ptr [ecx + Thread.m_fPreemptiveGCDisabled],0
482     pcpusl->X86EmitOffsetModRM(0xc6, (X86Reg)0, kECXthread, Thread::GetOffsetOfGCFlag());
483     pcpusl->Emit8(0);
484
485     CodeLabel *pRareEnable, *pEnableRejoin;
486     pRareEnable    = pcpusl->NewCodeLabel();
487     pEnableRejoin    = pcpusl->NewCodeLabel();
488
489     // test byte ptr [ecx + Thread.m_State], TS_CatchAtSafePoint
490     pcpusl->X86EmitOffsetModRM(0xf6, (X86Reg)0, kECXthread, Thread::GetOffsetOfState());
491     pcpusl->Emit8(Thread::TS_CatchAtSafePoint);
492
493     pcpusl->X86EmitCondJump(pRareEnable,X86CondCode::kJNZ);
494
495     pcpusl->EmitLabel(pEnableRejoin);
496
497     // *** unhook SEH frame
498
499     // mov edx,[esp]  ;;pointer to the next exception record
500     pcpusl->X86EmitEspOffset(0x8B, kEDX, 0);
501
502     // mov dword ptr fs:[0], edx
503     static const BYTE codeSEH[] = { 0x64, 0x89, 0x15, 0x0, 0x0, 0x0, 0x0 };
504     pcpusl->EmitBytes(codeSEH, sizeof(codeSEH));
505
506     // deallocate SEH frame
507     pcpusl->X86EmitAddEsp(sizeof(FrameHandlerExRecord));
508
509 #ifdef PROFILING_SUPPORTED
510     if (CORProfilerTrackTransitions())
511     {
512         // Load the MethodDesc* we pushed on the entry transition into EBX.
513         pcpusl->X86EmitPopReg(kEBX);
514
515         // Save registers
516         pcpusl->X86EmitPushReg(kECX);
517
518         // Push arguments and notify profiler
519         pcpusl->X86EmitPushImm32(COR_PRF_TRANSITION_RETURN);    // Reason
520         pcpusl->X86EmitPushReg(kEBX); // MethodDesc*
521         pcpusl->X86EmitCall(pcpusl->NewExternalCodeLabel((LPVOID)ProfilerManagedToUnmanagedTransitionMD), 8);
522
523         // Restore registers
524         pcpusl->X86EmitPopReg(kECX);
525     }
526 #endif // PROFILING_SUPPORTED
527
528     // Load the saved return value
529     if (pInfo->m_wFlags & umtmlFpu)
530     {
531         // fld qword ptr [esp]
532         pcpusl->Emit8(0xdd);
533         pcpusl->Emit16(0x2404);
534
535         pcpusl->X86EmitAddEsp(8);
536     }
537     else
538     {
539         pcpusl->X86EmitPopReg(kEDX);
540         pcpusl->X86EmitPopReg(kEAX);
541     }
542
543     // Restore EBX, which was saved in prolog
544     pcpusl->X86EmitPopReg(kEBX);
545
546     pcpusl->X86EmitPopReg(kEBP);
547
548     //retn n
549     pcpusl->X86EmitReturn(pInfo->m_cbRetPop);
550
551     //-------------------------------------------------------------
552     // coming here if the thread is not set up yet
553     //
554
555     pcpusl->EmitLabel(pSetupThreadLabel);
556
557     // call CreateThreadBlock
558     pcpusl->X86EmitCall(pcpusl->NewExternalCodeLabel((LPVOID) CreateThreadBlockThrow), 0);
559
560     // mov ecx,eax
561     pcpusl->Emit16(0xc189);
562
563     // jump back into the main code path
564     pcpusl->X86EmitNearJump(pRejoinThreadLabel);
565
566     //-------------------------------------------------------------
567     // coming here if g_TrapReturningThreads was true
568     //
569
570     pcpusl->EmitLabel(pDisableGCLabel);
571
572     // call UMThunkStubRareDisable.  This may throw if we are not allowed
573     // to enter.  Note that we have not set up our SEH yet (deliberately).
574     // This is important to handle the case where we cannot enter the CLR
575     // during shutdown and cannot coordinate with the GC because of
576     // deadlocks.
577     pcpusl->X86EmitCall(pcpusl->NewExternalCodeLabel((LPVOID) UMThunkStubRareDisable), 0);
578
579     // jump back into the main code path
580     pcpusl->X86EmitNearJump(pRejoinGCLabel);
581
582     //-------------------------------------------------------------
583     // Coming here for rare case when enabling GC pre-emptive mode
584     //
585
586     pcpusl->EmitLabel(pRareEnable);
587
588     // Thread object is expected to be in EBX. So first save caller's EBX
589     pcpusl->X86EmitPushReg(kEBX);
590     // mov ebx, ecx
591     pcpusl->X86EmitMovRegReg(kEBX, kECXthread);
592
593     pcpusl->EmitRareEnable(NULL);
594
595     // restore ebx
596     pcpusl->X86EmitPopReg(kEBX);
597
598     // return to mainline of function
599     pcpusl->X86EmitNearJump(pEnableRejoin);
600 }
601
602 // Compiles an unmanaged to managed thunk for the given signature.
603 Stub *UMThunkMarshInfo::CompileNExportThunk(LoaderHeap *pLoaderHeap, PInvokeStaticSigInfo* pSigInfo, MetaSig *pMetaSig, BOOL fNoStub)
604 {
605     STANDARD_VM_CONTRACT;
606
607     // stub is always static
608     BOOL fIsStatic = (fNoStub ? pSigInfo->IsStatic() : TRUE);
609
610     ArgIterator argit(pMetaSig);
611
612     UINT nStackBytes = argit.SizeOfArgStack();
613     _ASSERTE((nStackBytes % STACK_ELEM_SIZE) == 0);
614
615     // size of stack passed to us from unmanaged, may be bigger that nStackBytes if there are
616     // parameters with copy constructors where we perform value-to-reference transformation
617     UINT nStackBytesIncoming = nStackBytes;
618
619     UINT *psrcofs = (UINT *)_alloca((nStackBytes / STACK_ELEM_SIZE) * sizeof(UINT));
620     UINT psrcofsregs[NUM_ARGUMENT_REGISTERS];
621     UINT retbufofs = UNUSED_STACK_OFFSET;
622
623     for (int i = 0; i < NUM_ARGUMENT_REGISTERS; i++)
624         psrcofsregs[i] = UNUSED_STACK_OFFSET;
625
626     UINT nNumArgs = pMetaSig->NumFixedArgs();
627
628     UINT nOffset = 0;
629     int numRegistersUsed = 0;
630     int numStackSlotsIndex = nStackBytes / STACK_ELEM_SIZE;
631
632     // process this
633     if (!fIsStatic)
634     {
635         // just reserve ECX, instance target is special-cased in the thunk compiler
636         numRegistersUsed++;
637     }
638
639     // process the return buffer parameter
640     if (argit.HasRetBuffArg())
641     {
642         numRegistersUsed++;
643         _ASSERTE(numRegistersUsed - 1 < NUM_ARGUMENT_REGISTERS);
644         psrcofsregs[NUM_ARGUMENT_REGISTERS - numRegistersUsed] = nOffset;
645         retbufofs = nOffset;
646
647         nOffset += StackElemSize(sizeof(LPVOID));
648     }
649
650     // process ordinary parameters
651     for (DWORD i = nNumArgs; i > 0; i--)
652     {
653         TypeHandle thValueType;
654         CorElementType type = pMetaSig->NextArgNormalized(&thValueType);
655
656         UINT cbSize = MetaSig::GetElemSize(type, thValueType);
657
658         BOOL fPassPointer = FALSE;
659         if (!fNoStub && type == ELEMENT_TYPE_PTR)
660         {
661             // this is a copy-constructed argument - get its size
662             TypeHandle thPtr = pMetaSig->GetLastTypeHandleThrowing();
663             
664             _ASSERTE(thPtr.IsPointer());
665             cbSize = thPtr.AsTypeDesc()->GetTypeParam().GetSize();
666
667             // the incoming stack may be bigger that the outgoing (IL stub) stack
668             nStackBytesIncoming += (StackElemSize(cbSize) - StackElemSize(sizeof(LPVOID)));
669             fPassPointer = TRUE;
670         }
671
672         if (ArgIterator::IsArgumentInRegister(&numRegistersUsed, type))
673         {
674             _ASSERTE(numRegistersUsed - 1 < NUM_ARGUMENT_REGISTERS);
675             psrcofsregs[NUM_ARGUMENT_REGISTERS - numRegistersUsed] =
676                 (fPassPointer ?
677                 MAKE_BYREF_STACK_OFFSET(nOffset) :  // the register will get pointer to the incoming stack slot
678                 MAKE_BYVAL_STACK_OFFSET(nOffset));  // the register will get the incoming stack slot
679         }
680         else if (fPassPointer)
681         {
682             // the stack slot will get pointer to the incoming stack slot
683             psrcofs[--numStackSlotsIndex] = MAKE_BYREF_STACK_OFFSET(nOffset);
684         }
685         else
686         {
687             // stack slots will get incoming stack slots (we may need more stack slots for larger parameters)
688             for (UINT nSlotOfs = StackElemSize(cbSize); nSlotOfs > 0; nSlotOfs -= STACK_ELEM_SIZE)
689             {
690                 // note the reverse order here which is necessary to maintain
691                 // the original layout of the structure (it'll be reversed once
692                 // more when repushing)
693                 psrcofs[--numStackSlotsIndex] = MAKE_BYVAL_STACK_OFFSET(nOffset + nSlotOfs - STACK_ELEM_SIZE);
694             }
695         }
696
697         nOffset += StackElemSize(cbSize);
698     }
699     _ASSERTE(numStackSlotsIndex == 0);
700
701     UINT cbActualArgSize = nStackBytesIncoming + (numRegistersUsed * STACK_ELEM_SIZE);
702
703     if (!fIsStatic)
704     {
705         // do not count THIS
706         cbActualArgSize -= StackElemSize(sizeof(LPVOID));
707     }
708
709     m_cbActualArgSize = cbActualArgSize;
710
711     m_callConv = static_cast<UINT16>(pSigInfo->GetCallConv());
712
713     UMThunkStubInfo stubInfo;
714     memset(&stubInfo, 0, sizeof(stubInfo));
715
716     if (!FitsInU2(m_cbActualArgSize))
717         COMPlusThrow(kMarshalDirectiveException, IDS_EE_SIGTOOCOMPLEX);
718
719     stubInfo.m_cbSrcStack = static_cast<UINT16>(m_cbActualArgSize);
720     stubInfo.m_cbDstStack = nStackBytes;
721
722     if (pSigInfo->GetCallConv() == pmCallConvCdecl)
723     {
724         // caller pop
725         m_cbRetPop = 0;
726     }
727     else
728     {
729         // callee pop
730         m_cbRetPop = static_cast<UINT16>(m_cbActualArgSize);
731
732         if (pSigInfo->GetCallConv() == pmCallConvThiscall)
733         {
734             stubInfo.m_wFlags |= umtmlThisCall;
735             if (argit.HasRetBuffArg())
736             {
737                 stubInfo.m_wFlags |= umtmlThisCallHiddenArg;
738             }
739         }
740     }
741     stubInfo.m_cbRetPop = m_cbRetPop;
742
743     if (fIsStatic) stubInfo.m_wFlags |= umtmlIsStatic;
744     if (fNoStub) stubInfo.m_wFlags |= umtmlSkipStub;
745
746     if (pMetaSig->HasFPReturn()) stubInfo.m_wFlags |= umtmlFpu;
747
748     CPUSTUBLINKER cpusl;
749     CPUSTUBLINKER *pcpusl = &cpusl;
750
751     // call the worker to emit the actual thunk
752     UMEntryThunk::CompileUMThunkWorker(&stubInfo, pcpusl, psrcofsregs, psrcofs, retbufofs);
753
754     return pcpusl->Link(pLoaderHeap);
755 }
756
757 #else // _TARGET_X86_ && !FEATURE_STUBS_AS_IL
758
759 PCODE UMThunkMarshInfo::GetExecStubEntryPoint()
760 {
761     LIMITED_METHOD_CONTRACT;
762
763     return GetEEFuncEntryPoint(UMThunkStub);
764 }
765
766 #endif // _TARGET_X86_ && !FEATURE_STUBS_AS_IL
767
768 UMEntryThunkCache::UMEntryThunkCache(AppDomain *pDomain) :
769     m_crst(CrstUMEntryThunkCache),
770     m_pDomain(pDomain)
771 {
772     WRAPPER_NO_CONTRACT;
773     _ASSERTE(pDomain != NULL);
774 }
775
776 UMEntryThunkCache::~UMEntryThunkCache()
777 {
778     WRAPPER_NO_CONTRACT;
779
780     for (SHash<ThunkSHashTraits>::Iterator i = m_hash.Begin(); i != m_hash.End(); i++)
781     {
782         // UMEntryThunks in this cache own UMThunkMarshInfo in 1-1 fashion
783         DestroyMarshInfo(i->m_pThunk->GetUMThunkMarshInfo());
784         UMEntryThunk::FreeUMEntryThunk(i->m_pThunk);
785     }
786 }
787
788 UMEntryThunk *UMEntryThunkCache::GetUMEntryThunk(MethodDesc *pMD)
789 {
790     CONTRACT (UMEntryThunk *)
791     {
792         THROWS;
793         GC_TRIGGERS;
794         MODE_ANY;
795         PRECONDITION(CheckPointer(pMD));
796         POSTCONDITION(CheckPointer(RETVAL));
797     }
798     CONTRACT_END;
799
800     UMEntryThunk *pThunk;
801
802     CrstHolder ch(&m_crst);
803
804     const CacheElement *pElement = m_hash.LookupPtr(pMD);
805     if (pElement != NULL)
806     {
807         pThunk = pElement->m_pThunk;
808     }
809     else
810     {
811         // cache miss -> create a new thunk
812         pThunk = UMEntryThunk::CreateUMEntryThunk();
813         Holder<UMEntryThunk *, DoNothing, UMEntryThunk::FreeUMEntryThunk> umHolder;
814         umHolder.Assign(pThunk);
815
816         UMThunkMarshInfo *pMarshInfo = (UMThunkMarshInfo *)(void *)(m_pDomain->GetStubHeap()->AllocMem(S_SIZE_T(sizeof(UMThunkMarshInfo))));
817         Holder<UMThunkMarshInfo *, DoNothing, UMEntryThunkCache::DestroyMarshInfo> miHolder;
818         miHolder.Assign(pMarshInfo);
819
820         pMarshInfo->LoadTimeInit(pMD);
821         pThunk->LoadTimeInit(NULL, NULL, pMarshInfo, pMD);
822
823         // add it to the cache
824         CacheElement element;
825         element.m_pMD = pMD;
826         element.m_pThunk = pThunk;
827         m_hash.Add(element);
828
829         miHolder.SuppressRelease();
830         umHolder.SuppressRelease();
831     }
832
833     RETURN pThunk;
834 }
835
836 // FailFast if a native callable method invoked directly from managed code.
837 // UMThunkStub.asm check the mode and call this function to failfast.
838 extern "C" VOID STDCALL ReversePInvokeBadTransition()
839 {
840     STATIC_CONTRACT_THROWS;
841     STATIC_CONTRACT_GC_TRIGGERS;
842     // Fail 
843     EEPOLICY_HANDLE_FATAL_ERROR_WITH_MESSAGE(
844                                              COR_E_EXECUTIONENGINE,
845                                              W("Invalid Program: attempted to call a NativeCallable method from runtime-typesafe code.")
846                                             );
847 }
848
849 // Disable from a place that is calling into managed code via a UMEntryThunk.
850 extern "C" VOID STDCALL UMThunkStubRareDisableWorker(Thread *pThread, UMEntryThunk *pUMEntryThunk)
851 {
852     STATIC_CONTRACT_THROWS;
853     STATIC_CONTRACT_GC_TRIGGERS;
854
855     // Do not add a CONTRACT here.  We haven't set up SEH.
856
857     // WARNING!!!!
858     // when we start executing here, we are actually in cooperative mode.  But we
859     // haven't synchronized with the barrier to reentry yet.  So we are in a highly
860     // dangerous mode.  If we call managed code, we will potentially be active in
861     // the GC heap, even as GC's are occuring!
862
863     // We must do the following in this order, because otherwise we would be constructing
864     // the exception for the abort without synchronizing with the GC.  Also, we have no
865     // CLR SEH set up, despite the fact that we may throw a ThreadAbortException.
866     pThread->RareDisablePreemptiveGC();
867     pThread->HandleThreadAbort();
868
869 #ifdef DEBUGGING_SUPPORTED
870     // If the debugger is attached, we use this opportunity to see if
871     // we're disabling preemptive GC on the way into the runtime from
872     // unmanaged code. We end up here because
873     // Increment/DecrementTraceCallCount() will bump
874     // g_TrapReturningThreads for us.
875     if (CORDebuggerTraceCall())
876         g_pDebugInterface->TraceCall((const BYTE *)pUMEntryThunk->GetManagedTarget());
877 #endif // DEBUGGING_SUPPORTED
878 }
879
880 PCODE TheUMEntryPrestubWorker(UMEntryThunk * pUMEntryThunk)
881 {
882     STATIC_CONTRACT_THROWS;
883     STATIC_CONTRACT_GC_TRIGGERS;
884     STATIC_CONTRACT_MODE_PREEMPTIVE;
885
886     Thread * pThread = GetThreadNULLOk();
887     if (pThread == NULL)
888         pThread = CreateThreadBlockThrow();
889
890     GCX_COOP_THREAD_EXISTS(pThread);
891
892     if (pThread->IsAbortRequested())
893         pThread->HandleThreadAbort();
894
895     UMEntryThunk::DoRunTimeInit(pUMEntryThunk);
896
897     return (PCODE)pUMEntryThunk->GetCode();
898 }
899
900 void RunTimeInit_Wrapper(LPVOID /* UMThunkMarshInfo * */ ptr)
901 {
902     WRAPPER_NO_CONTRACT;
903
904     UMEntryThunk::DoRunTimeInit((UMEntryThunk*)ptr);
905 }
906
907
908 // asm entrypoint
909 void STDCALL UMEntryThunk::DoRunTimeInit(UMEntryThunk* pUMEntryThunk)
910 {
911
912     CONTRACTL
913     {
914         THROWS;
915         GC_TRIGGERS;
916         MODE_COOPERATIVE;
917         ENTRY_POINT;
918         PRECONDITION(CheckPointer(pUMEntryThunk));
919     }
920     CONTRACTL_END;
921
922     INSTALL_MANAGED_EXCEPTION_DISPATCHER;
923     // this method is called by stubs which are called by managed code,
924     // so we need an unwind and continue handler so that our internal
925     // exceptions don't leak out into managed code.
926     INSTALL_UNWIND_AND_CONTINUE_HANDLER;
927
928     {
929         GCX_PREEMP();
930         pUMEntryThunk->RunTimeInit();
931     }
932
933     UNINSTALL_UNWIND_AND_CONTINUE_HANDLER;
934     UNINSTALL_MANAGED_EXCEPTION_DISPATCHER;
935 }
936
937 UMEntryThunk* UMEntryThunk::CreateUMEntryThunk()
938 {
939     CONTRACT (UMEntryThunk*)
940     {
941         THROWS;
942         GC_NOTRIGGER;
943         MODE_ANY;
944         INJECT_FAULT(COMPlusThrowOM());
945         POSTCONDITION(CheckPointer(RETVAL));
946     }
947     CONTRACT_END;
948
949     UMEntryThunk * p;
950
951     p = s_thunkFreeList.GetUMEntryThunk();
952
953     if (p == NULL)
954         p = (UMEntryThunk *)(void *)SystemDomain::GetGlobalLoaderAllocator()->GetExecutableHeap()->AllocMem(S_SIZE_T(sizeof(UMEntryThunk)));
955
956     RETURN p;
957 }
958
959 void UMEntryThunk::Terminate()
960 {
961     CONTRACTL
962     {
963         NOTHROW;
964         MODE_ANY;
965     }
966     CONTRACTL_END;
967
968     m_code.Poison();
969
970     if (GetObjectHandle())
971     {
972         DestroyLongWeakHandle(GetObjectHandle());
973         m_pObjectHandle = 0;
974     }
975
976     s_thunkFreeList.AddToList(this);
977 }
978
979 VOID UMEntryThunk::FreeUMEntryThunk(UMEntryThunk* p)
980 {
981     CONTRACTL
982     {
983         NOTHROW;
984         GC_TRIGGERS;
985         MODE_ANY;
986         PRECONDITION(CheckPointer(p));
987     }
988     CONTRACTL_END;
989
990     p->Terminate();
991 }
992
993 #endif // CROSSGEN_COMPILE
994
995 //-------------------------------------------------------------------------
996 // This function is used to report error when we call collected delegate.
997 // But memory that was allocated for thunk can be reused, due to it this
998 // function will not be called in all cases of the collected delegate call,
999 // also it may crash while trying to report the problem.
1000 //-------------------------------------------------------------------------
1001 VOID __fastcall UMEntryThunk::ReportViolation(UMEntryThunk* pEntryThunk)
1002 {
1003     CONTRACTL
1004     {
1005         THROWS;
1006         GC_TRIGGERS;
1007         MODE_COOPERATIVE;
1008         PRECONDITION(CheckPointer(pEntryThunk));
1009     }
1010     CONTRACTL_END;
1011
1012     MethodDesc* pMethodDesc = pEntryThunk->GetMethod();
1013
1014     SString namespaceOrClassName;
1015     SString methodName;
1016     SString moduleName;
1017
1018     pMethodDesc->GetMethodInfoNoSig(namespaceOrClassName, methodName);
1019     moduleName.SetUTF8(pMethodDesc->GetModule()->GetSimpleName());
1020
1021     SString message;
1022
1023     message.Printf(W("A callback was made on a garbage collected delegate of type '%s!%s::%s'."),
1024         moduleName.GetUnicode(),
1025         namespaceOrClassName.GetUnicode(),
1026         methodName.GetUnicode());
1027
1028     EEPOLICY_HANDLE_FATAL_ERROR_WITH_MESSAGE(COR_E_FAILFAST, message.GetUnicode());
1029 }
1030
1031 UMThunkMarshInfo::~UMThunkMarshInfo()
1032 {
1033     CONTRACTL
1034     {
1035         NOTHROW;
1036         GC_TRIGGERS;
1037         MODE_ANY;
1038     }
1039     CONTRACTL_END;
1040
1041 #if defined(_TARGET_X86_) && !defined(FEATURE_STUBS_AS_IL)
1042     if (m_pExecStub)
1043         m_pExecStub->DecRef();
1044 #endif
1045
1046 #ifdef _DEBUG
1047     FillMemory(this, sizeof(*this), 0xcc);
1048 #endif
1049 }
1050
1051 MethodDesc* UMThunkMarshInfo::GetILStubMethodDesc(MethodDesc* pInvokeMD, PInvokeStaticSigInfo* pSigInfo, DWORD dwStubFlags)
1052 {
1053     STANDARD_VM_CONTRACT;
1054
1055     MethodDesc* pStubMD = NULL;
1056     dwStubFlags |= NDIRECTSTUB_FL_REVERSE_INTEROP;  // could be either delegate interop or not--that info is passed in from the caller
1057
1058 #if defined(DEBUGGING_SUPPORTED)
1059     // Combining the next two lines, and eliminating jitDebuggerFlags, leads to bad codegen in x86 Release builds using Visual C++ 19.00.24215.1.
1060     CORJIT_FLAGS jitDebuggerFlags = GetDebuggerCompileFlags(pSigInfo->GetModule(), CORJIT_FLAGS());
1061     if (jitDebuggerFlags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_DEBUG_CODE))
1062     {
1063         dwStubFlags |= NDIRECTSTUB_FL_GENERATEDEBUGGABLEIL;
1064     }
1065 #endif // DEBUGGING_SUPPORTED
1066
1067     pStubMD = NDirect::CreateCLRToNativeILStub(
1068         pSigInfo,
1069         dwStubFlags,
1070         pInvokeMD // may be NULL
1071         );
1072
1073     return pStubMD;
1074 }
1075
1076 //----------------------------------------------------------
1077 // This initializer is called during load time.
1078 // It does not do any stub initialization or sigparsing.
1079 // The RunTimeInit() must be called subsequently to fully 
1080 // UMThunkMarshInfo.
1081 //----------------------------------------------------------
1082 VOID UMThunkMarshInfo::LoadTimeInit(MethodDesc* pMD)
1083 {
1084     LIMITED_METHOD_CONTRACT;
1085     PRECONDITION(pMD != NULL);
1086
1087     LoadTimeInit(pMD->GetSignature(), pMD->GetModule(), pMD);
1088 }
1089
1090 VOID UMThunkMarshInfo::LoadTimeInit(Signature sig, Module * pModule, MethodDesc * pMD)
1091 {
1092     LIMITED_METHOD_CONTRACT;
1093
1094     FillMemory(this, sizeof(UMThunkMarshInfo), 0); // Prevent problems with partial deletes
1095
1096     // This will be overwritten by the actual code pointer (or NULL) at the end of UMThunkMarshInfo::RunTimeInit()
1097     m_pILStub = (PCODE)1;
1098
1099     m_pMD = pMD;
1100     m_pModule = pModule;
1101     m_sig = sig;
1102
1103 #if defined(_TARGET_X86_) && !defined(FEATURE_STUBS_AS_IL)
1104     INDEBUG(m_cbRetPop = 0xcccc;)
1105 #endif
1106 }
1107
1108 #ifndef CROSSGEN_COMPILE
1109 //----------------------------------------------------------
1110 // This initializer finishes the init started by LoadTimeInit.
1111 // It does stub creation and can throw an exception.
1112 //
1113 // It can safely be called multiple times and by concurrent
1114 // threads.
1115 //----------------------------------------------------------
1116 VOID UMThunkMarshInfo::RunTimeInit()
1117 {
1118     STANDARD_VM_CONTRACT;
1119
1120     // Nothing to do if already inited
1121     if (IsCompletelyInited())
1122         return;
1123
1124     PCODE pFinalILStub = NULL;
1125     MethodDesc* pStubMD = NULL;
1126
1127     MethodDesc * pMD = GetMethod();
1128
1129     // Lookup NGened stub - currently we only support ngening of reverse delegate invoke interop stubs
1130     if (pMD != NULL && pMD->IsEEImpl())
1131     {
1132         DWORD dwStubFlags = NDIRECTSTUB_FL_NGENEDSTUB | NDIRECTSTUB_FL_REVERSE_INTEROP | NDIRECTSTUB_FL_DELEGATE;
1133
1134 #if defined(DEBUGGING_SUPPORTED)
1135         // Combining the next two lines, and eliminating jitDebuggerFlags, leads to bad codegen in x86 Release builds using Visual C++ 19.00.24215.1.
1136         CORJIT_FLAGS jitDebuggerFlags = GetDebuggerCompileFlags(GetModule(), CORJIT_FLAGS());
1137         if (jitDebuggerFlags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_DEBUG_CODE))
1138         {
1139             dwStubFlags |= NDIRECTSTUB_FL_GENERATEDEBUGGABLEIL;
1140         }
1141 #endif // DEBUGGING_SUPPORTED
1142
1143         pFinalILStub = GetStubForInteropMethod(pMD, dwStubFlags, &pStubMD);
1144     }
1145
1146 #if defined(_TARGET_X86_) && !defined(FEATURE_STUBS_AS_IL)
1147     PInvokeStaticSigInfo sigInfo;
1148
1149     if (pMD != NULL)
1150         new (&sigInfo) PInvokeStaticSigInfo(pMD);
1151     else
1152         new (&sigInfo) PInvokeStaticSigInfo(GetSignature(), GetModule());
1153
1154     Stub *pFinalExecStub = NULL;
1155
1156     // we will always emit the argument-shuffling thunk, m_cbActualArgSize is set inside
1157     LoaderHeap *pHeap = (pMD == NULL ? NULL : pMD->GetLoaderAllocator()->GetStubHeap());
1158
1159     if (pFinalILStub != NULL ||
1160         NDirect::MarshalingRequired(pMD, GetSignature().GetRawSig(), GetModule()))
1161     {
1162         if (pFinalILStub == NULL)
1163         {
1164             DWORD dwStubFlags = 0;
1165
1166             if (sigInfo.IsDelegateInterop())
1167                 dwStubFlags |= NDIRECTSTUB_FL_DELEGATE;
1168
1169             pStubMD = GetILStubMethodDesc(pMD, &sigInfo, dwStubFlags);
1170             pFinalILStub = JitILStub(pStubMD);
1171         }
1172
1173         MetaSig msig(pStubMD);
1174         pFinalExecStub = CompileNExportThunk(pHeap, &sigInfo, &msig, FALSE);
1175     }
1176     else
1177     {
1178         MetaSig msig(GetSignature(), GetModule(), NULL);
1179         pFinalExecStub = CompileNExportThunk(pHeap, &sigInfo, &msig, TRUE);
1180     }
1181
1182     if (FastInterlockCompareExchangePointer(&m_pExecStub,
1183                                             pFinalExecStub,
1184                                             NULL) != NULL)
1185     {
1186
1187         // Some thread swooped in and set us. Our stub is now a
1188         // duplicate, so throw it away.
1189         if (pFinalExecStub)
1190             pFinalExecStub->DecRef();
1191     }
1192
1193 #else // _TARGET_X86_ && !FEATURE_STUBS_AS_IL
1194
1195     if (pFinalILStub == NULL)
1196     {
1197         if (pMD != NULL && !pMD->IsEEImpl() &&
1198             !NDirect::MarshalingRequired(pMD, GetSignature().GetRawSig(), GetModule()))
1199         {
1200             // Call the method directly in no-delegate case if possible. This is important to avoid JITing
1201             // for stubs created via code:ICLRRuntimeHost2::CreateDelegate during coreclr startup.
1202             pFinalILStub = pMD->GetMultiCallableAddrOfCode();
1203         }
1204         else
1205         {
1206             // For perf, it is important to avoid expensive initialization of
1207             // PInvokeStaticSigInfo if we have NGened stub.
1208             PInvokeStaticSigInfo sigInfo;
1209
1210             if (pMD != NULL)
1211                 new (&sigInfo) PInvokeStaticSigInfo(pMD);
1212             else
1213                 new (&sigInfo) PInvokeStaticSigInfo(GetSignature(), GetModule());
1214
1215             DWORD dwStubFlags = 0;
1216
1217             if (sigInfo.IsDelegateInterop())
1218                 dwStubFlags |= NDIRECTSTUB_FL_DELEGATE;
1219
1220             pStubMD = GetILStubMethodDesc(pMD, &sigInfo, dwStubFlags);
1221             pFinalILStub = JitILStub(pStubMD);
1222
1223         }
1224     }
1225
1226 #if defined(_TARGET_X86_)
1227     MetaSig sig(pMD);
1228     int numRegistersUsed = 0;
1229     UINT16 cbRetPop = 0;
1230
1231     //
1232     // cbStackArgSize represents the number of arg bytes for the MANAGED signature
1233     //
1234     UINT32 cbStackArgSize = 0;
1235
1236     int offs = 0;
1237
1238 #ifdef UNIX_X86_ABI
1239     if (HasRetBuffArgUnmanagedFixup(&sig))
1240     {
1241         // callee should pop retbuf
1242         numRegistersUsed += 1;
1243         offs += STACK_ELEM_SIZE;
1244         cbRetPop += STACK_ELEM_SIZE;
1245     }
1246 #endif // UNIX_X86_ABI
1247
1248     for (UINT i = 0 ; i < sig.NumFixedArgs(); i++)
1249     {
1250         TypeHandle thValueType;
1251         CorElementType type = sig.NextArgNormalized(&thValueType);
1252         int cbSize = sig.GetElemSize(type, thValueType);
1253         if (ArgIterator::IsArgumentInRegister(&numRegistersUsed, type))
1254         {
1255             offs += STACK_ELEM_SIZE;
1256         }
1257         else
1258         {
1259             offs += StackElemSize(cbSize);
1260             cbStackArgSize += StackElemSize(cbSize);
1261         }
1262     }
1263     m_cbStackArgSize = cbStackArgSize;
1264     m_cbActualArgSize = (pStubMD != NULL) ? pStubMD->AsDynamicMethodDesc()->GetNativeStackArgSize() : offs;
1265
1266     PInvokeStaticSigInfo sigInfo;
1267     if (pMD != NULL)
1268         new (&sigInfo) PInvokeStaticSigInfo(pMD);
1269     else
1270         new (&sigInfo) PInvokeStaticSigInfo(GetSignature(), GetModule());
1271     if (sigInfo.GetCallConv() == pmCallConvCdecl)
1272     {
1273         m_cbRetPop = cbRetPop;
1274     }
1275     else
1276     {
1277         // For all the other calling convention except cdecl, callee pops the stack arguments
1278         m_cbRetPop = cbRetPop + static_cast<UINT16>(m_cbActualArgSize);
1279     }
1280 #else // _TARGET_X86_
1281     //
1282     // m_cbActualArgSize gets the number of arg bytes for the NATIVE signature
1283     //
1284     m_cbActualArgSize =
1285         (pStubMD != NULL) ? pStubMD->AsDynamicMethodDesc()->GetNativeStackArgSize() : pMD->SizeOfArgStack();
1286
1287 #endif // _TARGET_X86_
1288
1289 #endif // _TARGET_X86_ && !FEATURE_STUBS_AS_IL
1290
1291     // Must be the last thing we set!
1292     InterlockedCompareExchangeT<PCODE>(&m_pILStub, pFinalILStub, (PCODE)1);
1293 }
1294
1295 #if defined(_TARGET_X86_) && defined(FEATURE_STUBS_AS_IL)
1296 VOID UMThunkMarshInfo::SetupArguments(char *pSrc, ArgumentRegisters *pArgRegs, char *pDst)
1297 {
1298     MethodDesc *pMD = GetMethod();
1299
1300     _ASSERTE(pMD);
1301
1302     //
1303     // x86 native uses the following stack layout:
1304     // | saved eip |
1305     // | --------- | <- CFA
1306     // | stkarg 0  |
1307     // | stkarg 1  |
1308     // | ...       |
1309     // | stkarg N  |
1310     //
1311     // x86 managed, however, uses a bit different stack layout:
1312     // | saved eip |
1313     // | --------- | <- CFA
1314     // | stkarg M  | (NATIVE/MANAGE may have different number of stack arguments)
1315     // | ...       |
1316     // | stkarg 1  |
1317     // | stkarg 0  |
1318     //
1319     // This stub bridges the gap between them.
1320     //
1321     char *pCurSrc = pSrc;
1322     char *pCurDst = pDst + m_cbStackArgSize;
1323
1324     MetaSig sig(pMD);
1325
1326     int numRegistersUsed = 0;
1327
1328 #ifdef UNIX_X86_ABI
1329     if (HasRetBuffArgUnmanagedFixup(&sig))
1330     {
1331         // Pass retbuf via Ecx
1332         numRegistersUsed += 1;
1333         pArgRegs->Ecx = *((UINT32 *)pCurSrc);
1334         pCurSrc += STACK_ELEM_SIZE;
1335     }
1336 #endif // UNIX_X86_ABI
1337
1338     for (UINT i = 0 ; i < sig.NumFixedArgs(); i++)
1339     {
1340         TypeHandle thValueType;
1341         CorElementType type = sig.NextArgNormalized(&thValueType);
1342         int cbSize = sig.GetElemSize(type, thValueType);
1343         int elemSize = StackElemSize(cbSize);
1344
1345         if (ArgIterator::IsArgumentInRegister(&numRegistersUsed, type))
1346         {
1347             _ASSERTE(elemSize == STACK_ELEM_SIZE);
1348
1349             if (numRegistersUsed == 1)
1350                 pArgRegs->Ecx = *((UINT32 *)pCurSrc);
1351             else if (numRegistersUsed == 2)
1352                 pArgRegs->Edx = *((UINT32 *)pCurSrc);
1353         }
1354         else
1355         {
1356             pCurDst -= elemSize;
1357             memcpy(pCurDst, pCurSrc, elemSize);
1358         }
1359
1360         pCurSrc += elemSize;
1361     }
1362
1363     _ASSERTE(pDst == pCurDst);
1364 }
1365
1366 EXTERN_C VOID STDCALL UMThunkStubSetupArgumentsWorker(UMThunkMarshInfo *pMarshInfo,
1367                                                       char *pSrc,
1368                                                       UMThunkMarshInfo::ArgumentRegisters *pArgRegs,
1369                                                       char *pDst)
1370 {
1371     pMarshInfo->SetupArguments(pSrc, pArgRegs, pDst);
1372 }
1373 #endif // _TARGET_X86_ && FEATURE_STUBS_AS_IL
1374
1375 #ifdef _DEBUG
1376 void STDCALL LogUMTransition(UMEntryThunk* thunk)
1377 {
1378     CONTRACTL
1379     {
1380         NOTHROW;
1381         DEBUG_ONLY;
1382         GC_NOTRIGGER;
1383         ENTRY_POINT;
1384         if (GetThread()) MODE_PREEMPTIVE; else MODE_ANY;
1385         DEBUG_ONLY;
1386         PRECONDITION(CheckPointer(thunk));
1387         PRECONDITION((GetThread() != NULL) ? (!GetThread()->PreemptiveGCDisabled()) : TRUE);
1388     }
1389     CONTRACTL_END;
1390
1391     BEGIN_ENTRYPOINT_VOIDRET;
1392
1393     void** retESP = ((void**) &thunk) + 4;
1394
1395     MethodDesc* method = thunk->GetMethod();
1396     if (method)
1397     {
1398         LOG((LF_STUBS, LL_INFO1000000, "UNMANAGED -> MANAGED Stub To Method = %s::%s SIG %s Ret Address ESP = 0x%x ret = 0x%x\n",
1399             method->m_pszDebugClassName,
1400             method->m_pszDebugMethodName,
1401             method->m_pszDebugMethodSignature, retESP, *retESP));
1402     }
1403
1404     END_ENTRYPOINT_VOIDRET;
1405
1406     }
1407 #endif
1408
1409 #endif // CROSSGEN_COMPILE