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// QCall.H
5
6
7
8#ifndef __QCall_h__
9#define __QCall_h__
10
11#include "clr_std/type_traits"
12
13//
14// QCALLS
15//
16
17// QCalls are internal calls from managed code in mscorlib.dll to unmanaged code in mscorwks.dll. QCalls are very much like
18// a normal P/Invoke from mscorlib.dll to mscorwks.dll.
19//
20// Unlike FCalls, QCalls will marshal all arguments as unmanaged types like a normal P/Invoke. QCall also switch to preemptive
21// GC mode like a normal P/Invoke. These two features should make QCalls easier to write reliably compared to FCalls.
22// QCalls are not prone to GC holes and GC starvation bugs that are common with FCalls.
23//
24// QCalls perform better compared to FCalls w/ HelperMethodFrame. The QCall overhead is about 1.4x less compared to
25// FCall w/ HelperMethodFrame overhead on x86. The performance is about the same on x64. However, the implementation
26// of P/Invoke marshaling on x64 is not tuned for performance yet. The QCalls should become significantly faster compared
27// to FCalls w/ HelperMethodFrame on x64 as we do performance tuning of P/Invoke marshaling on x64.
28//
29//
30// The preferred type of QCall arguments is primitive types that efficiently handled by the P/Invoke marshaler (INT32, LPCWSTR, BOOL).
31// (Notice that BOOL is the correct boolean flavor for QCall arguments. CLR_BOOL is the correct boolean flavor for FCall arguments.)
32//
33// The pointers to common unmanaged EE structures should be wrapped into helper handle types. This is to make the managed implementation
34// type safe and avoid falling into unsafe C# everywhere. See the AssemblyHandle below for a good example.
35//
36// There is a way to pass raw object references in and out of QCalls. It is done by wrapping a pointer to
37// a local variable in a handle. It is intentionally cumbersome and should be avoided if reasonably possible.
38// See the StringHandleOnStack in the example below. String arguments will get marshaled in as LPCWSTR.
39// Returning objects, especially strings, from QCalls is the only common pattern
40// where returning the raw objects (as an OUT argument) is widely acceptable.
41//
42//
43// QCall example - managed part (do not replicate the comments into your actual QCall implementation):
44// ---------------------------------------------------------------------------------------------------
45//
46// class Foo {
47//
48// // All QCalls should have the following DllImport and SuppressUnmanagedCodeSecurity attributes
49// [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
50// [SuppressUnmanagedCodeSecurity]
51// // QCalls should always be static extern.
52// private static extern bool Bar(int flags, string inString, StringHandleOnStack retString);
53//
54// // Many QCalls have a thin managed wrapper around them to expose them to the world in more meaningful way.
55// public string Bar(int flags)
56// {
57// string retString = null;
58//
59// // The strings are returned from QCalls by taking address
60// // of a local variable using JitHelpers.GetStringHandleOnStack method
61// if (!Bar(flags, this.Id, JitHelpers.GetStringHandleOnStack(ref retString)))
62// FatalError();
63//
64// return retString;
65// }
66//
67// Every QCall produces a couple of bogus FXCop warnings currently. Just add them to the FXCop exlusion list for now.
68//
69//
70// QCall example - unmanaged part (do not replicate the comments into your actual QCall implementation):
71// -----------------------------------------------------------------------------------------------------
72//
73// The entrypoints of all QCalls has to be registered in tables in vm\ecall.cpp using QCFuncEntry macro,
74// For example: QCFuncElement("Bar", FooNative::Bar)
75//
76// class FooNative {
77// public:
78// // All QCalls should be static and should be tagged with QCALLTYPE
79// static
80// BOOL QCALLTYPE Bar(int flags, LPCWSTR wszString, QCall::StringHandleOnStack retString);
81// };
82//
83// BOOL QCALLTYPE FooNative::Bar(int flags, LPCWSTR wszString, QCall::StringHandleOnStack retString)
84// {
85// // All QCalls should have QCALL_CONTRACT. It is alias for THROWS; GC_TRIGGERS; MODE_PREEMPTIVE; SO_TOLERANT.
86// QCALL_CONTRACT;
87//
88// // Optionally, use QCALL_CHECK instead and the expanded form of the contract if you want to specify preconditions:
89// // CONTRACTL {
90// // QCALL_CHECK;
91// // PRECONDITION(wszString != NULL);
92// // } CONTRACTL_END;
93//
94// // The only line between QCALL_CONTRACT and BEGIN_QCALL
95// // should be the return value declaration if there is one.
96// BOOL retVal = FALSE;
97//
98// // The body has to be enclosed in BEGIN_QCALL/END_QCALL macro. It is necessary to make the exception handling work.
99// BEGIN_QCALL;
100//
101// // Validate arguments if necessary and throw exceptions like anywhere else in the EE. There is no convention currently
102// // on whether the argument validation should be done in managed or unmanaged code.
103// if (flags != 0)
104// COMPlusThrow(kArgumentException, L"InvalidFlags");
105//
106// // No need to worry about GC moving strings passed into QCall. Marshaling pins them for us.
107// printf("%S", wszString);
108//
109// // This is the most efficient way to return strings back to managed code. No need to use StringBuilder.
110// retString.Set(L"Hello");
111//
112// // You can not return from inside of BEGIN_QCALL/END_QCALL. The return value has to be passed out in helper variable.
113// retVal = TRUE;
114//
115// END_QCALL;
116//
117// return retVal;
118// }
119
120
121#ifdef PLATFORM_UNIX
122#define QCALLTYPE __cdecl
123#else // PLATFORM_UNIX
124#define QCALLTYPE __stdcall
125#endif // !PLATFORM_UNIX
126
127#define BEGIN_QCALL \
128 INSTALL_MANAGED_EXCEPTION_DISPATCHER \
129 INSTALL_UNWIND_AND_CONTINUE_HANDLER
130
131#define END_QCALL \
132 UNINSTALL_UNWIND_AND_CONTINUE_HANDLER \
133 UNINSTALL_MANAGED_EXCEPTION_DISPATCHER
134
135#define BEGIN_QCALL_SO_TOLERANT \
136 INSTALL_MANAGED_EXCEPTION_DISPATCHER \
137 INSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE
138
139#define END_QCALL_SO_TOLERANT \
140 UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE \
141 UNINSTALL_MANAGED_EXCEPTION_DISPATCHER
142
143
144#define QCALL_CHECK \
145 THROWS; \
146 GC_TRIGGERS; \
147 MODE_PREEMPTIVE; \
148 SO_TOLERANT; \
149
150#define QCALL_CONTRACT CONTRACTL { QCALL_CHECK; } CONTRACTL_END;
151
152//
153// Scope class for QCall helper methods and types
154//
155class QCall
156{
157public:
158
159 //
160 // Helper types to aid marshaling of QCall arguments in type-safe manner
161 //
162 // The C/C++ compiler has to treat these types as POD (plain old data) to generate
163 // a calling convention compatible with P/Invoke marshaling. This means that:
164 // NONE OF THESE HELPER TYPES CAN HAVE A CONSTRUCTOR OR DESTRUCTOR!
165 // THESE HELPER TYPES CAN NOT BE IMPLEMENTED USING INHERITANCE OR TEMPLATES!
166 //
167
168 //
169 // StringHandleOnStack is used for managed strings
170 //
171 struct StringHandleOnStack
172 {
173 StringObject ** m_ppStringObject;
174
175#ifndef DACCESS_COMPILE
176 //
177 // Helpers for returning managed string from QCall
178 //
179
180 // Raw setter - note that you need to be in cooperative mode
181 void Set(STRINGREF s)
182 {
183 CONTRACTL
184 {
185 NOTHROW;
186 GC_NOTRIGGER;
187 MODE_COOPERATIVE;
188 SO_TOLERANT;
189 }
190 CONTRACTL_END;
191
192 // The space for the return value has to be on the stack
193 _ASSERTE(Thread::IsAddressInCurrentStack(m_ppStringObject));
194
195 *m_ppStringObject = STRINGREFToObject(s);
196 }
197
198 void Set(const SString& value);
199 void Set(LPCWSTR pwzValue);
200 void Set(LPCUTF8 pszValue);
201#endif // !DACCESS_COMPILE
202 };
203
204 //
205 // ObjectHandleOnStack type is used for managed objects
206 //
207 struct ObjectHandleOnStack
208 {
209 Object ** m_ppObject;
210
211#ifndef DACCESS_COMPILE
212 //
213 // Helpers for returning common managed types from QCall
214 //
215 void Set(OBJECTREF o)
216 {
217 LIMITED_METHOD_CONTRACT;
218
219 // The space for the return value has to be on the stack
220 _ASSERTE(Thread::IsAddressInCurrentStack(m_ppObject));
221
222 *m_ppObject = OBJECTREFToObject(o);
223 }
224
225 void SetByteArray(const BYTE * p, COUNT_T length);
226 void SetIntPtrArray(const PVOID * p, COUNT_T length);
227 void SetGuidArray(const GUID * p, COUNT_T length);
228
229 // Do not add operator overloads to convert this object into a stack reference to a specific object type
230 // such as OBJECTREF *. While such things are correct, our debug checking logic is unable to verify that
231 // the object reference is actually protected from access and therefore will assert.
232 // See bug 254159 for details.
233
234#endif // !DACCESS_COMPILE
235 };
236
237 //
238 // StackCrawlMarkHandle is used for passing StackCrawlMark into QCalls
239 //
240 struct StackCrawlMarkHandle
241 {
242 StackCrawlMark * m_pMark;
243
244 operator StackCrawlMark * ()
245 {
246 LIMITED_METHOD_CONTRACT;
247 return m_pMark;
248 }
249 };
250
251 // AppDomainHandle is used for passing AppDomains into QCalls via System.AppDomainHandle
252 struct AppDomainHandle
253 {
254 AppDomain *m_pAppDomain;
255
256 operator AppDomain *()
257 {
258 LIMITED_METHOD_CONTRACT;
259#ifdef _DEBUG
260 VerifyDomainHandle();
261#endif // _DEBUG
262 return m_pAppDomain;
263 }
264
265 AppDomain *operator->() const
266 {
267 LIMITED_METHOD_CONTRACT;
268#ifdef _DEBUG
269 VerifyDomainHandle();
270#endif // _DEBUG
271 return m_pAppDomain;
272 }
273
274 private:
275#ifdef _DEBUG
276 void VerifyDomainHandle() const;
277#endif // _DEBUG
278 };
279
280 struct AssemblyHandle
281 {
282 DomainAssembly * m_pAssembly;
283
284 operator DomainAssembly * ()
285 {
286 LIMITED_METHOD_CONTRACT;
287 return m_pAssembly;
288 }
289
290 DomainAssembly * operator->() const
291 {
292 LIMITED_METHOD_CONTRACT;
293 return m_pAssembly;
294 }
295 };
296
297 struct ModuleHandle
298 {
299 Module * m_pModule;
300
301 operator Module * ()
302 {
303 LIMITED_METHOD_CONTRACT;
304 return m_pModule;
305 }
306
307 Module * operator->() const
308 {
309 LIMITED_METHOD_CONTRACT;
310 return m_pModule;
311 }
312 };
313
314 struct LoaderAllocatorHandle
315 {
316 LoaderAllocator * m_pLoaderAllocator;
317
318 operator LoaderAllocator * ()
319 {
320 LIMITED_METHOD_CONTRACT;
321 return m_pLoaderAllocator;
322 }
323
324 LoaderAllocator * operator -> () const
325 {
326 LIMITED_METHOD_CONTRACT;
327 return m_pLoaderAllocator;
328 }
329
330 static LoaderAllocatorHandle From(LoaderAllocator * pLoaderAllocator)
331 {
332 LoaderAllocatorHandle h;
333 h.m_pLoaderAllocator = pLoaderAllocator;
334 return h;
335 }
336 };
337
338 // The lifetime management between managed and native Thread objects is broken. There is a resurrection
339 // race where one can get a dangling pointer to the unmanaged Thread object. Once this race is fixed
340 // we may need to revisit how the unmanaged thread handles are passed around.
341 struct ThreadHandle
342 {
343 Thread * m_pThread;
344
345 operator Thread * ()
346 {
347 LIMITED_METHOD_CONTRACT;
348 return m_pThread;
349 }
350
351 Thread * operator->() const
352 {
353 LIMITED_METHOD_CONTRACT;
354 return m_pThread;
355 }
356 };
357};
358
359typedef void* EnregisteredTypeHandle;
360
361#endif //__QCall_h__
362