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.h
6//
7
8//
9
10
11#ifndef __dllimportcallback_h__
12#define __dllimportcallback_h__
13
14#include "object.h"
15#include "stublink.h"
16#include "ceeload.h"
17#include "class.h"
18#include "dllimport.h"
19#include "mdaassistants.h"
20
21enum UMThunkStubFlags
22{
23 umtmlIsStatic = 0x0001,
24 umtmlThisCall = 0x0002,
25 umtmlThisCallHiddenArg = 0x0004,
26 umtmlFpu = 0x0008,
27#ifdef _TARGET_X86_
28 // the signature is trivial so stub need not be generated and the target can be called directly
29 umtmlSkipStub = 0x0080,
30#endif // _TARGET_X86_
31};
32
33#include <pshpack1.h>
34//--------------------------------------------------------------------------
35// This structure captures basic info needed to build an UMThunk.
36//--------------------------------------------------------------------------
37struct UMThunkStubInfo
38{
39 UINT32 m_cbDstStack; //# of bytes of stack portion of managed args
40 UINT16 m_cbSrcStack; //# of bytes of stack portion of unmanaged args
41 UINT16 m_cbRetPop; //# of bytes to pop on return to unmanaged
42 UINT16 m_wFlags; // UMThunkStubFlags enum
43};
44#include <poppack.h>
45
46//----------------------------------------------------------------------
47// This structure collects all information needed to marshal an
48// unmanaged->managed thunk. The only information missing is the
49// managed target and the "this" object (if any.) Those two pieces
50// are broken out into a small UMEntryThunk.
51//
52// The idea is to share UMThunkMarshInfo's between multiple thunks
53// that have the same signature while the UMEntryThunk contains the
54// minimal info needed to distinguish between actual function pointers.
55//----------------------------------------------------------------------
56
57class UMThunkMarshInfo
58{
59 friend class CheckAsmOffsets;
60
61private:
62 enum
63 {
64 kLoadTimeInited = 0x4c55544d, //'LUTM'
65 kRunTimeInited = 0x5255544d, //'RUTM'
66 };
67
68public:
69 //----------------------------------------------------------
70 // This initializer can be called during load time.
71 // It does not do any ML stub initialization or sigparsing.
72 // The RunTimeInit() must be called subsequently before this
73 // can safely be used.
74 //----------------------------------------------------------
75 VOID LoadTimeInit(MethodDesc* pMD);
76 VOID LoadTimeInit(Signature sig, Module * pModule, MethodDesc * pMD = NULL);
77
78 //----------------------------------------------------------
79 // This initializer finishes the init started by LoadTimeInit.
80 // It does all the ML stub creation, and can throw a COM+
81 // exception.
82 //
83 // It can safely be called multiple times and by concurrent
84 // threads.
85 //----------------------------------------------------------
86 VOID RunTimeInit();
87
88 // Destructor.
89 //----------------------------------------------------------
90 ~UMThunkMarshInfo();
91
92 //----------------------------------------------------------
93 // Accessor functions
94 //----------------------------------------------------------
95 Signature GetSignature()
96 {
97 LIMITED_METHOD_CONTRACT;
98 return m_sig;
99 }
100
101 Module* GetModule()
102 {
103 LIMITED_METHOD_CONTRACT;
104 return m_pModule;
105 }
106
107 MethodDesc * GetMethod()
108 {
109 LIMITED_METHOD_CONTRACT;
110 return m_pMD;
111 }
112
113#if defined(_TARGET_X86_) && !defined(FEATURE_STUBS_AS_IL)
114 PCODE GetExecStubEntryPoint()
115 {
116 WRAPPER_NO_CONTRACT;
117 return GetExecStub()->GetEntryPoint();
118 }
119
120 Stub* GetExecStub()
121 {
122 CONTRACT (Stub*)
123 {
124 NOTHROW;
125 GC_NOTRIGGER;
126 MODE_ANY;
127 PRECONDITION(IsCompletelyInited());
128 POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
129 }
130 CONTRACT_END;
131
132 RETURN m_pExecStub;
133 }
134
135 UINT16 GetCbRetPop()
136 {
137 CONTRACTL
138 {
139 NOTHROW;
140 GC_NOTRIGGER;
141 MODE_ANY;
142 SUPPORTS_DAC;
143 PRECONDITION(IsCompletelyInited());
144 }
145 CONTRACTL_END;
146
147 return m_cbRetPop;
148 }
149
150 CorPinvokeMap GetCallingConvention()
151 {
152 CONTRACTL
153 {
154 NOTHROW;
155 GC_NOTRIGGER;
156 MODE_ANY;
157 SUPPORTS_DAC;
158 PRECONDITION(IsCompletelyInited());
159 }
160 CONTRACTL_END;
161
162 return (CorPinvokeMap)m_callConv;
163 }
164
165 VOID SetCallingConvention(const CorPinvokeMap callConv)
166 {
167 m_callConv = (UINT16)callConv;
168 }
169
170#else
171 PCODE GetExecStubEntryPoint();
172#endif
173
174 UINT32 GetCbActualArgSize()
175 {
176 CONTRACTL
177 {
178 NOTHROW;
179 GC_NOTRIGGER;
180 MODE_ANY;
181 PRECONDITION(IsCompletelyInited());
182 }
183 CONTRACTL_END;
184
185 return m_cbActualArgSize;
186 }
187
188 BOOL IsCompletelyInited()
189 {
190 LIMITED_METHOD_CONTRACT;
191 return (m_pILStub != (PCODE)1);
192 }
193
194 static MethodDesc* GetILStubMethodDesc(MethodDesc* pInvokeMD, PInvokeStaticSigInfo* pSigInfo, DWORD dwStubFlags);
195
196 static UINT32 GetOffsetOfStub()
197 {
198 LIMITED_METHOD_CONTRACT;
199 return (UINT32)offsetof(UMThunkMarshInfo, m_pILStub);
200 }
201
202#if defined(_TARGET_X86_) && !defined(FEATURE_STUBS_AS_IL)
203 // Compiles an unmanaged to managed thunk for the given signature. The thunk
204 // will call the stub or, if fNoStub == TRUE, directly the managed target.
205 Stub *CompileNExportThunk(LoaderHeap *pLoaderHeap, PInvokeStaticSigInfo* pSigInfo, MetaSig *pMetaSig, BOOL fNoStub);
206#endif // _TARGET_X86_ && !FEATURE_STUBS_AS_IL
207
208#if defined(_TARGET_X86_) && defined(FEATURE_STUBS_AS_IL)
209 struct ArgumentRegisters
210 {
211 UINT32 Ecx;
212 UINT32 Edx;
213 };
214
215 VOID SetupArguments(char *pSrc, ArgumentRegisters *pArgRegs, char *pDst);
216#endif // _TARGET_X86_ && FEATURE_STUBS_AS_IL
217
218private:
219 PCODE m_pILStub; // IL stub for marshaling
220 // On x86, NULL for no-marshal signatures
221 // On non-x86, the managed entrypoint for no-delegate no-marshal signatures
222 UINT32 m_cbActualArgSize; // caches m_pSig.SizeOfFrameArgumentArray()
223 // On x86/Linux we have to augment with numRegistersUsed * STACK_ELEM_SIZE
224#if defined(_TARGET_X86_)
225 UINT16 m_cbRetPop; // stack bytes popped by callee (for UpdateRegDisplay)
226#if defined(FEATURE_STUBS_AS_IL)
227 UINT32 m_cbStackArgSize; // stack bytes pushed for managed code
228#else
229 Stub* m_pExecStub; // UMEntryThunk jumps directly here
230 UINT16 m_callConv; // unmanaged calling convention and flags (CorPinvokeMap)
231#endif // FEATURE_STUBS_AS_IL
232#endif // _TARGET_X86_
233
234 MethodDesc * m_pMD; // maybe null
235 Module * m_pModule;
236 Signature m_sig;
237};
238
239
240//----------------------------------------------------------------------
241// This structure contains the minimal information required to
242// distinguish one function pointer from another, with the rest
243// being stored in a shared UMThunkMarshInfo.
244//
245// This structure also contains the actual code bytes that form the
246// front end of the thunk. A pointer to the m_code[] byte array is
247// what is actually handed to unmanaged client code.
248//----------------------------------------------------------------------
249class UMEntryThunk
250{
251 friend class CheckAsmOffsets;
252 friend class NDirectStubLinker;
253 friend class UMEntryThunkFreeList;
254
255private:
256#ifdef _DEBUG
257 enum
258 {
259 kLoadTimeInited = 0x4c554554, //'LUET'
260 kRunTimeInited = 0x52554554, //'RUET'
261 };
262#endif
263
264public:
265 static UMEntryThunk* CreateUMEntryThunk();
266 static VOID FreeUMEntryThunk(UMEntryThunk* p);
267
268#if defined(_TARGET_X86_) && !defined(FEATURE_STUBS_AS_IL)
269 // Compiles an unmanaged to managed thunk with the given calling convention adaptation.
270 // - psrcofsregs are stack offsets that should be loaded to argument registers (ECX, EDX)
271 // - psrcofs are stack offsets that should be repushed for the managed target
272 // - retbufofs is the offset of the hidden byref structure argument when returning large
273 // structures; -1 means there is none
274 // Special values recognized by psrcofsregs and psrcofs are -1 which means not present
275 // and 1 which means that this register/stack slot should get the UMEntryThunk pointer.
276 // This method is used for all reverse P/Invoke calls on x86 (the umtmlSkipStub
277 // flag determines whether the managed target is stub or the actual target method).
278 static VOID CompileUMThunkWorker(UMThunkStubInfo *pInfo,
279 CPUSTUBLINKER *pcpusl,
280 UINT *psrcofsregs,
281 UINT *psrcofs,
282 UINT retbufofs);
283#endif // _TARGET_X86_ && !FEATURE_STUBS_AS_IL
284
285#ifndef DACCESS_COMPILE
286 VOID LoadTimeInit(PCODE pManagedTarget,
287 OBJECTHANDLE pObjectHandle,
288 UMThunkMarshInfo *pUMThunkMarshInfo,
289 MethodDesc *pMD,
290 ADID dwDomainId)
291 {
292 CONTRACTL
293 {
294 NOTHROW;
295 GC_NOTRIGGER;
296 MODE_ANY;
297 PRECONDITION(CheckPointer(pUMThunkMarshInfo));
298 PRECONDITION(pMD != NULL);
299 }
300 CONTRACTL_END;
301
302 m_pManagedTarget = pManagedTarget;
303 m_pObjectHandle = pObjectHandle;
304 m_pUMThunkMarshInfo = pUMThunkMarshInfo;
305 m_dwDomainId = dwDomainId;
306
307 m_pMD = pMD; // For debugging and profiling, so they can identify the target
308
309 m_code.Encode((BYTE*)TheUMThunkPreStub(), this);
310
311#ifdef _DEBUG
312 m_state = kLoadTimeInited;
313#endif
314 }
315
316 ~UMEntryThunk()
317 {
318 CONTRACTL
319 {
320 NOTHROW;
321 GC_NOTRIGGER;
322 MODE_ANY;
323 }
324 CONTRACTL_END;
325
326 if (GetObjectHandle())
327 {
328 DestroyLongWeakHandle(GetObjectHandle());
329 }
330 }
331
332 void Terminate();
333
334 void OnADUnload();
335
336 VOID RunTimeInit()
337 {
338 STANDARD_VM_CONTRACT;
339
340 // Ensure method's module is activate in app domain
341 m_pMD->EnsureActive();
342
343 m_pUMThunkMarshInfo->RunTimeInit();
344
345 // Ensure that we have either the managed target or the delegate.
346 if (m_pObjectHandle == NULL && m_pManagedTarget == NULL)
347 m_pManagedTarget = m_pMD->GetMultiCallableAddrOfCode();
348
349 m_code.Encode((BYTE*)m_pUMThunkMarshInfo->GetExecStubEntryPoint(), this);
350
351#ifdef _DEBUG
352 m_state = kRunTimeInited;
353#endif
354 }
355
356 // asm entrypoint
357 static VOID STDCALL DoRunTimeInit(UMEntryThunk* pThis);
358
359 PCODE GetManagedTarget() const
360 {
361 CONTRACT (PCODE)
362 {
363 THROWS;
364 GC_TRIGGERS;
365 MODE_ANY;
366 PRECONDITION(m_state == kRunTimeInited || m_state == kLoadTimeInited);
367 POSTCONDITION(RETVAL != NULL);
368 }
369 CONTRACT_END;
370
371 OBJECTHANDLE hndDelegate = GetObjectHandle();
372 if (hndDelegate != NULL)
373 {
374 GCX_COOP();
375
376 DELEGATEREF orDelegate = (DELEGATEREF)ObjectFromHandle(hndDelegate);
377 _ASSERTE(orDelegate != NULL);
378 _ASSERTE(m_pMD->IsEEImpl());
379
380 // We have optimizations that skip the Invoke method and call directly the
381 // delegate's target method. We need to return the target in that case,
382 // otherwise debugger would fail to step in.
383 RETURN orDelegate->GetMethodPtr();
384 }
385 else
386 {
387 if (m_pManagedTarget != NULL)
388 {
389 RETURN m_pManagedTarget;
390 }
391 else
392 {
393 RETURN m_pMD->GetMultiCallableAddrOfCode();
394 }
395 }
396 }
397#endif // !DACCESS_COMPILE
398
399 OBJECTHANDLE GetObjectHandle() const
400 {
401 CONTRACT (OBJECTHANDLE)
402 {
403 NOTHROW;
404 GC_NOTRIGGER;
405 MODE_ANY;
406 // If we OOM after we create the holder but
407 // before we set the m_state we can have
408 // m_state == 0 and m_pObjectHandle == NULL
409 PRECONDITION(m_state == kRunTimeInited ||
410 m_state == kLoadTimeInited ||
411 m_pObjectHandle == NULL);
412 }
413 CONTRACT_END;
414
415 RETURN m_pObjectHandle;
416 }
417
418 UMThunkMarshInfo* GetUMThunkMarshInfo() const
419 {
420 CONTRACT (UMThunkMarshInfo*)
421 {
422 NOTHROW;
423 GC_NOTRIGGER;
424 MODE_ANY;
425 SUPPORTS_DAC;
426 PRECONDITION(m_state == kRunTimeInited || m_state == kLoadTimeInited);
427 POSTCONDITION(CheckPointer(RETVAL));
428 }
429 CONTRACT_END;
430
431 RETURN m_pUMThunkMarshInfo;
432 }
433
434
435 const BYTE* GetCode() const
436 {
437 CONTRACT (const BYTE*)
438 {
439 NOTHROW;
440 GC_NOTRIGGER;
441 MODE_ANY;
442 PRECONDITION(m_state == kRunTimeInited || m_state == kLoadTimeInited);
443 POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
444 }
445 CONTRACT_END;
446
447 RETURN m_code.GetEntryPoint();
448 }
449
450 static UMEntryThunk* RecoverUMEntryThunk(const VOID* pCode)
451 {
452 LIMITED_METHOD_CONTRACT;
453 return (UMEntryThunk*)( ((LPBYTE)pCode) - offsetof(UMEntryThunk, m_code) );
454 }
455
456
457 MethodDesc* GetMethod() const
458 {
459 CONTRACT (MethodDesc*)
460 {
461 NOTHROW;
462 GC_NOTRIGGER;
463 MODE_ANY;
464 PRECONDITION(m_state == kRunTimeInited || m_state == kLoadTimeInited);
465 POSTCONDITION(CheckPointer(RETVAL,NULL_OK));
466 }
467 CONTRACT_END;
468
469 RETURN m_pMD;
470 }
471
472 ADID GetDomainId() const
473 {
474 CONTRACT (ADID)
475 {
476 NOTHROW;
477 GC_NOTRIGGER;
478 MODE_ANY;
479 PRECONDITION(m_state == kRunTimeInited || m_state == kLoadTimeInited);
480 }
481 CONTRACT_END;
482
483 RETURN m_dwDomainId;
484 }
485
486 static DWORD GetOffsetOfMethodDesc()
487 {
488 LIMITED_METHOD_CONTRACT;
489 return offsetof(class UMEntryThunk, m_pMD);
490 }
491
492 static DWORD GetCodeOffset()
493 {
494 LIMITED_METHOD_CONTRACT;
495 return offsetof(UMEntryThunk, m_code);
496 }
497
498 static UMEntryThunk* Decode(LPVOID pCallback);
499
500 static VOID __fastcall ReportViolation(UMEntryThunk* p);
501
502private:
503 // The start of the managed code.
504 // if m_pObjectHandle is non-NULL, this field is still set to help with diagnostic of call on collected delegate crashes
505 // but it may not have the correct value.
506 PCODE m_pManagedTarget;
507
508 // This is used for profiling.
509 PTR_MethodDesc m_pMD;
510
511 // Object handle holding "this" reference. May be a strong or weak handle.
512 // Field is NULL for a static method.
513 OBJECTHANDLE m_pObjectHandle;
514
515 union
516 {
517 // Pointer to the shared structure containing everything else
518 PTR_UMThunkMarshInfo m_pUMThunkMarshInfo;
519 // Pointer to the next UMEntryThunk in the free list. Used when it is freed.
520 UMEntryThunk *m_pNextFreeThunk;
521 };
522
523 ADID m_dwDomainId; // appdomain of module (cached for fast access)
524#ifdef _DEBUG
525 DWORD m_state; // the initialization state
526#endif
527
528 UMEntryThunkCode m_code;
529};
530
531// Cache to hold UMEntryThunk/UMThunkMarshInfo instances associated with MethodDescs.
532// All UMEntryThunk/UMThunkMarshInfo instances are destroyed when the cache goes away.
533class UMEntryThunkCache
534{
535public:
536 UMEntryThunkCache(AppDomain *pDomain);
537 ~UMEntryThunkCache();
538
539 UMEntryThunk *GetUMEntryThunk(MethodDesc *pMD);
540
541private:
542 struct CacheElement
543 {
544 CacheElement()
545 {
546 LIMITED_METHOD_CONTRACT;
547 m_pMD = NULL;
548 m_pThunk = NULL;
549 }
550
551 MethodDesc *m_pMD;
552 UMEntryThunk *m_pThunk;
553 };
554
555 class ThunkSHashTraits : public NoRemoveSHashTraits< DefaultSHashTraits<CacheElement> >
556 {
557 public:
558 typedef MethodDesc *key_t;
559 static key_t GetKey(element_t e) { LIMITED_METHOD_CONTRACT; return e.m_pMD; }
560 static BOOL Equals(key_t k1, key_t k2) { LIMITED_METHOD_CONTRACT; return (k1 == k2); }
561 static count_t Hash(key_t k) { LIMITED_METHOD_CONTRACT; return (count_t)(size_t)k; }
562 static const element_t Null() { LIMITED_METHOD_CONTRACT; return CacheElement(); }
563 static bool IsNull(const element_t &e) { LIMITED_METHOD_CONTRACT; return (e.m_pMD == NULL); }
564 };
565
566 static void DestroyMarshInfo(UMThunkMarshInfo *pMarshInfo)
567 {
568 WRAPPER_NO_CONTRACT;
569 pMarshInfo->~UMThunkMarshInfo();
570 }
571
572 SHash<ThunkSHashTraits> m_hash;
573 Crst m_crst;
574 AppDomain *m_pDomain;
575};
576
577#if defined(_TARGET_X86_) && !defined(FEATURE_STUBS_AS_IL)
578//-------------------------------------------------------------------------
579// One-time creation of special prestub to initialize UMEntryThunks.
580//-------------------------------------------------------------------------
581Stub *GenerateUMThunkPrestub();
582#endif // _TARGET_X86_ && !FEATURE_STUBS_AS_IL
583
584//-------------------------------------------------------------------------
585// NExport stub
586//-------------------------------------------------------------------------
587#if !defined(_WIN64) && !defined(DACCESS_COMPILE) && !defined(CROSS_COMPILE)
588EXCEPTION_HANDLER_DECL(FastNExportExceptHandler);
589EXCEPTION_HANDLER_DECL(UMThunkPrestubHandler);
590#endif // _WIN64
591
592extern "C" void TheUMEntryPrestub(void);
593extern "C" PCODE TheUMEntryPrestubWorker(UMEntryThunk * pUMEntryThunk);
594
595EXTERN_C void UMThunkStub(void);
596
597#ifdef _DEBUG
598void STDCALL LogUMTransition(UMEntryThunk* thunk);
599#endif
600
601#endif //__dllimportcallback_h__
602