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 | |
21 | enum 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 | //-------------------------------------------------------------------------- |
37 | struct 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 | |
57 | class UMThunkMarshInfo |
58 | { |
59 | friend class CheckAsmOffsets; |
60 | |
61 | private: |
62 | enum |
63 | { |
64 | kLoadTimeInited = 0x4c55544d, //'LUTM' |
65 | kRunTimeInited = 0x5255544d, //'RUTM' |
66 | }; |
67 | |
68 | public: |
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 | |
218 | private: |
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 | //---------------------------------------------------------------------- |
249 | class UMEntryThunk |
250 | { |
251 | friend class CheckAsmOffsets; |
252 | friend class NDirectStubLinker; |
253 | friend class UMEntryThunkFreeList; |
254 | |
255 | private: |
256 | #ifdef _DEBUG |
257 | enum |
258 | { |
259 | kLoadTimeInited = 0x4c554554, //'LUET' |
260 | kRunTimeInited = 0x52554554, //'RUET' |
261 | }; |
262 | #endif |
263 | |
264 | public: |
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 | |
502 | private: |
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. |
533 | class UMEntryThunkCache |
534 | { |
535 | public: |
536 | UMEntryThunkCache(AppDomain *pDomain); |
537 | ~UMEntryThunkCache(); |
538 | |
539 | UMEntryThunk *GetUMEntryThunk(MethodDesc *pMD); |
540 | |
541 | private: |
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 | //------------------------------------------------------------------------- |
581 | Stub *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) |
588 | EXCEPTION_HANDLER_DECL(FastNExportExceptHandler); |
589 | EXCEPTION_HANDLER_DECL(UMThunkPrestubHandler); |
590 | #endif // _WIN64 |
591 | |
592 | extern "C" void TheUMEntryPrestub(void); |
593 | extern "C" PCODE TheUMEntryPrestubWorker(UMEntryThunk * pUMEntryThunk); |
594 | |
595 | EXTERN_C void UMThunkStub(void); |
596 | |
597 | #ifdef _DEBUG |
598 | void STDCALL LogUMTransition(UMEntryThunk* thunk); |
599 | #endif |
600 | |
601 | #endif //__dllimportcallback_h__ |
602 | |