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 using System;
6 using System.Text;
7 using System.Runtime.InteropServices;
8 using System.Diagnostics;
9 using Interlocked = System.Threading.Interlocked;
10 
11 namespace Internal.Runtime.CompilerHelpers
12 {
13     /// <summary>
14     /// These methods are used to throw exceptions from generated code.
15     /// </summary>
16     internal static class InteropHelpers
17     {
StringToAnsiString(String str, bool bestFit, bool throwOnUnmappableChar)18         internal static unsafe byte* StringToAnsiString(String str, bool bestFit, bool throwOnUnmappableChar)
19         {
20             return PInvokeMarshal.StringToAnsiString(str, bestFit, throwOnUnmappableChar);
21         }
22 
AnsiStringToString(byte* buffer)23         public static unsafe string AnsiStringToString(byte* buffer)
24         {
25             return PInvokeMarshal.AnsiStringToString(buffer);
26         }
27 
28 
StringToByValAnsiString(string str, byte* pNative, int charCount, bool bestFit, bool throwOnUnmappableChar)29         internal static unsafe void StringToByValAnsiString(string str, byte* pNative, int charCount, bool bestFit, bool throwOnUnmappableChar)
30         {
31             // In CoreRT charCount = Min(SizeConst, str.Length). So we don't need to truncate again.
32             PInvokeMarshal.StringToByValAnsiString(str, pNative, charCount, bestFit, throwOnUnmappableChar, truncate: false);
33         }
34 
ByValAnsiStringToString(byte* buffer, int length)35         public static unsafe string ByValAnsiStringToString(byte* buffer, int length)
36         {
37             return PInvokeMarshal.ByValAnsiStringToString(buffer, length);
38         }
39 
StringToUnicodeFixedArray(String str, UInt16* buffer, int length)40         internal static unsafe void StringToUnicodeFixedArray(String str, UInt16* buffer, int length)
41         {
42             if (buffer == null)
43                 return;
44 
45             if (str == null)
46             {
47                 buffer[0] = '\0';
48                 return;
49             }
50 
51             Debug.Assert(str.Length >= length);
52 
53             fixed (char* pStr = str)
54             {
55                 int size = length * sizeof(char);
56                 Buffer.MemoryCopy(pStr, buffer, size, size);
57                 *(buffer + length) = 0;
58             }
59         }
60 
UnicodeToStringFixedArray(UInt16* buffer, int length)61         internal static unsafe string UnicodeToStringFixedArray(UInt16* buffer, int length)
62         {
63             if (buffer == null)
64                 return String.Empty;
65 
66             string result = String.Empty;
67 
68             if (length > 0)
69             {
70                 result = new String(' ', length);
71 
72                 fixed (char* pTemp = result)
73                 {
74                     int size = length * sizeof(char);
75                     Buffer.MemoryCopy(buffer, pTemp, size, size);
76                 }
77             }
78             return result;
79         }
80 
StringToUnicodeBuffer(String str)81         internal static unsafe char* StringToUnicodeBuffer(String str)
82         {
83             if (str == null)
84                 return null;
85 
86             int stringLength = str.Length;
87 
88             char* buffer = (char*)PInvokeMarshal.CoTaskMemAlloc((UIntPtr)(sizeof(char) * (stringLength + 1))).ToPointer();
89 
90             fixed (char* pStr = str)
91             {
92                 int size = stringLength * sizeof(char);
93                 Buffer.MemoryCopy(pStr, buffer, size, size);
94                 *(buffer + stringLength) = '\0';
95             }
96             return buffer;
97         }
98 
UnicodeBufferToString(char* buffer)99         public static unsafe string UnicodeBufferToString(char* buffer)
100         {
101             return new String(buffer);
102         }
103 
AllocMemoryForAnsiStringBuilder(StringBuilder sb)104         public static unsafe byte* AllocMemoryForAnsiStringBuilder(StringBuilder sb)
105         {
106             if (sb == null)
107             {
108                 return null;
109             }
110             return (byte *)CoTaskMemAllocAndZeroMemory(new IntPtr(checked((sb.Capacity + 2) * PInvokeMarshal.GetSystemMaxDBCSCharSize())));
111         }
112 
AllocMemoryForUnicodeStringBuilder(StringBuilder sb)113         public static unsafe char* AllocMemoryForUnicodeStringBuilder(StringBuilder sb)
114         {
115             if (sb == null)
116             {
117                 return null;
118             }
119             return (char *)CoTaskMemAllocAndZeroMemory(new IntPtr(checked((sb.Capacity + 2) * 2)));
120         }
121 
AllocMemoryForAnsiCharArray(char[] chArray)122         public static unsafe byte* AllocMemoryForAnsiCharArray(char[] chArray)
123         {
124             if (chArray == null)
125             {
126                 return null;
127             }
128             return (byte*)CoTaskMemAllocAndZeroMemory(new IntPtr(checked((chArray.Length + 2) * PInvokeMarshal.GetSystemMaxDBCSCharSize())));
129         }
130 
AnsiStringToStringBuilder(byte* newBuffer, System.Text.StringBuilder stringBuilder)131         public static unsafe void AnsiStringToStringBuilder(byte* newBuffer, System.Text.StringBuilder stringBuilder)
132         {
133             if (stringBuilder == null)
134                 return;
135 
136             PInvokeMarshal.AnsiStringToStringBuilder(newBuffer, stringBuilder);
137         }
138 
UnicodeStringToStringBuilder(ushort* newBuffer, System.Text.StringBuilder stringBuilder)139         public static unsafe void UnicodeStringToStringBuilder(ushort* newBuffer, System.Text.StringBuilder stringBuilder)
140         {
141             if (stringBuilder == null)
142                 return;
143 
144             PInvokeMarshal.UnicodeStringToStringBuilder(newBuffer, stringBuilder);
145         }
146 
StringBuilderToAnsiString(System.Text.StringBuilder stringBuilder, byte* pNative, bool bestFit, bool throwOnUnmappableChar)147         public static unsafe void StringBuilderToAnsiString(System.Text.StringBuilder stringBuilder, byte* pNative,
148             bool bestFit, bool throwOnUnmappableChar)
149         {
150             if (pNative == null)
151                 return;
152 
153             PInvokeMarshal.StringBuilderToAnsiString(stringBuilder, pNative, bestFit, throwOnUnmappableChar);
154         }
155 
StringBuilderToUnicodeString(System.Text.StringBuilder stringBuilder, ushort* destination)156         public static unsafe void StringBuilderToUnicodeString(System.Text.StringBuilder stringBuilder, ushort* destination)
157         {
158             if (destination == null)
159                 return;
160 
161             PInvokeMarshal.StringBuilderToUnicodeString(stringBuilder, destination);
162         }
163 
WideCharArrayToAnsiCharArray(char[] managedArray, byte* pNative, bool bestFit, bool throwOnUnmappableChar)164         public static unsafe void WideCharArrayToAnsiCharArray(char[] managedArray, byte* pNative, bool bestFit, bool throwOnUnmappableChar)
165         {
166             PInvokeMarshal.WideCharArrayToAnsiCharArray(managedArray, pNative, bestFit, throwOnUnmappableChar);
167         }
168 
169         /// <summary>
170         /// Convert ANSI ByVal byte array to UNICODE wide char array, best fit
171         /// </summary>
172         /// <remarks>
173         /// * This version works with array instead to string, it means that the len must be provided and there will be NO NULL to
174         /// terminate the array.
175         /// * The buffer to the UNICODE wide char array must be allocated by the caller.
176         /// </remarks>
177         /// <param name="pNative">Pointer to the ANSI byte array. Could NOT be null.</param>
178         /// <param name="lenInBytes">Maximum buffer size.</param>
179         /// <param name="managedArray">Wide char array that has already been allocated.</param>
AnsiCharArrayToWideCharArray(byte* pNative, char[] managedArray)180         public static unsafe void AnsiCharArrayToWideCharArray(byte* pNative, char[] managedArray)
181         {
182             PInvokeMarshal.AnsiCharArrayToWideCharArray(pNative, managedArray);
183         }
184 
185         /// <summary>
186         /// Convert a single UNICODE wide char to a single ANSI byte.
187         /// </summary>
188         /// <param name="managedArray">single UNICODE wide char value</param>
WideCharToAnsiChar(char managedValue, bool bestFit, bool throwOnUnmappableChar)189         public static unsafe byte WideCharToAnsiChar(char managedValue, bool bestFit, bool throwOnUnmappableChar)
190         {
191             return PInvokeMarshal.WideCharToAnsiChar(managedValue, bestFit, throwOnUnmappableChar);
192         }
193 
194         /// <summary>
195         /// Convert a single ANSI byte value to a single UNICODE wide char value, best fit.
196         /// </summary>
197         /// <param name="nativeValue">Single ANSI byte value.</param>
AnsiCharToWideChar(byte nativeValue)198         public static unsafe char AnsiCharToWideChar(byte nativeValue)
199         {
200             return PInvokeMarshal.AnsiCharToWideChar(nativeValue);
201         }
202 
ResolvePInvoke(MethodFixupCell* pCell)203         internal static unsafe IntPtr ResolvePInvoke(MethodFixupCell* pCell)
204         {
205             if (pCell->Target != IntPtr.Zero)
206                 return pCell->Target;
207 
208             return ResolvePInvokeSlow(pCell);
209         }
210 
ResolvePInvokeSlow(MethodFixupCell* pCell)211         internal static unsafe IntPtr ResolvePInvokeSlow(MethodFixupCell* pCell)
212         {
213             ModuleFixupCell* pModuleCell = pCell->Module;
214             IntPtr hModule = pModuleCell->Handle;
215             if (hModule == IntPtr.Zero)
216             {
217                 FixupModuleCell(pModuleCell);
218                 hModule = pModuleCell->Handle;
219             }
220 
221             FixupMethodCell(hModule, pCell);
222             return pCell->Target;
223         }
224 
TryResolveModule(string moduleName)225         internal static unsafe IntPtr TryResolveModule(string moduleName)
226         {
227             IntPtr hModule = IntPtr.Zero;
228 
229             // Try original name first
230             hModule = LoadLibrary(moduleName);
231             if (hModule != IntPtr.Zero) return hModule;
232 
233 #if PLATFORM_UNIX
234             const string PAL_SHLIB_PREFIX = "lib";
235 #if PLATFORM_OSX
236             const string PAL_SHLIB_SUFFIX = ".dylib";
237 #else
238             const string PAL_SHLIB_SUFFIX = ".so";
239 #endif
240 
241              // Try prefix+name+suffix
242             hModule = LoadLibrary(PAL_SHLIB_PREFIX + moduleName + PAL_SHLIB_SUFFIX);
243             if (hModule != IntPtr.Zero) return hModule;
244 
245             // Try name+suffix
246             hModule = LoadLibrary(moduleName + PAL_SHLIB_SUFFIX);
247             if (hModule != IntPtr.Zero) return hModule;
248 
249             // Try prefix+name
250             hModule = LoadLibrary(PAL_SHLIB_PREFIX + moduleName);
251             if (hModule != IntPtr.Zero) return hModule;
252 #endif
253             return IntPtr.Zero;
254         }
255 
LoadLibrary(string moduleName)256         internal static unsafe IntPtr LoadLibrary(string moduleName)
257         {
258             IntPtr hModule;
259 
260 #if !PLATFORM_UNIX
261             hModule = Interop.mincore.LoadLibraryEx(moduleName, IntPtr.Zero, 0);
262 #else
263             hModule = Interop.Sys.LoadLibrary(moduleName);
264 #endif
265 
266             return hModule;
267         }
268 
FreeLibrary(IntPtr hModule)269         internal static unsafe void FreeLibrary(IntPtr hModule)
270         {
271 #if !PLATFORM_UNIX
272             Interop.mincore.FreeLibrary(hModule);
273 #else
274             Interop.Sys.FreeLibrary(hModule);
275 #endif
276         }
277 
GetModuleName(ModuleFixupCell* pCell)278         private static unsafe string GetModuleName(ModuleFixupCell* pCell)
279         {
280             byte* pModuleName = (byte*)pCell->ModuleName;
281             return Encoding.UTF8.GetString(pModuleName, strlen(pModuleName));
282         }
283 
FixupModuleCell(ModuleFixupCell* pCell)284         internal static unsafe void FixupModuleCell(ModuleFixupCell* pCell)
285         {
286             string moduleName = GetModuleName(pCell);
287             IntPtr hModule = TryResolveModule(moduleName);
288             if (hModule != IntPtr.Zero)
289             {
290                 var oldValue = Interlocked.CompareExchange(ref pCell->Handle, hModule, IntPtr.Zero);
291                 if (oldValue != IntPtr.Zero)
292                 {
293                     // Some other thread won the race to fix it up.
294                     FreeLibrary(hModule);
295                 }
296             }
297             else
298             {
299                 throw new DllNotFoundException(SR.Format(SR.Arg_DllNotFoundExceptionParameterized, moduleName));
300             }
301         }
302 
FixupMethodCell(IntPtr hModule, MethodFixupCell* pCell)303         internal static unsafe void FixupMethodCell(IntPtr hModule, MethodFixupCell* pCell)
304         {
305             byte* methodName = (byte*)pCell->MethodName;
306 
307 #if !PLATFORM_UNIX
308             pCell->Target = Interop.mincore.GetProcAddress(hModule, methodName);
309 #else
310             pCell->Target = Interop.Sys.GetProcAddress(hModule, methodName);
311 #endif
312             if (pCell->Target == IntPtr.Zero)
313             {
314                 string entryPointName = Encoding.UTF8.GetString(methodName, strlen(methodName));
315                 throw new EntryPointNotFoundException(SR.Format(SR.Arg_EntryPointNotFoundExceptionParameterized, entryPointName, GetModuleName(pCell->Module)));
316             }
317         }
318 
strlen(byte* pString)319         internal static unsafe int strlen(byte* pString)
320         {
321             byte* p = pString;
322             while (*p != 0) p++;
323             return checked((int)(p - pString));
324         }
325 
CoTaskMemAllocAndZeroMemory(global::System.IntPtr size)326         internal unsafe static void* CoTaskMemAllocAndZeroMemory(global::System.IntPtr size)
327         {
328             void* ptr;
329             ptr = PInvokeMarshal.CoTaskMemAlloc((UIntPtr)(void*)size).ToPointer();
330 
331             // PInvokeMarshal.CoTaskMemAlloc will throw OOMException if out of memory
332             Debug.Assert(ptr != null);
333 
334             Buffer.ZeroMemory((byte*)ptr, size.ToInt64());
335             return ptr;
336         }
337 
CoTaskMemFree(void* p)338         internal unsafe static void CoTaskMemFree(void* p)
339         {
340             PInvokeMarshal.CoTaskMemFree((IntPtr)p);
341         }
342         /// <summary>
343         /// Returns the stub to the pinvoke marshalling stub
344         /// </summary>
GetStubForPInvokeDelegate(Delegate del)345         public static IntPtr GetStubForPInvokeDelegate(Delegate del)
346         {
347             return PInvokeMarshal.GetStubForPInvokeDelegate(del);
348         }
349 
350         /// <summary>
351         /// Retrieve the corresponding P/invoke instance from the stub
352         /// </summary>
GetPInvokeDelegateForStub(IntPtr pStub, RuntimeTypeHandle delegateType)353         public static Delegate GetPInvokeDelegateForStub(IntPtr pStub, RuntimeTypeHandle delegateType)
354         {
355             return PInvokeMarshal.GetPInvokeDelegateForStub(pStub, delegateType);
356         }
357 
358         /// <summary>
359         /// Retrieves the function pointer for the current open static delegate that is being called
360         /// </summary>
GetCurrentCalleeOpenStaticDelegateFunctionPointer()361         public static IntPtr GetCurrentCalleeOpenStaticDelegateFunctionPointer()
362         {
363             return PInvokeMarshal.GetCurrentCalleeOpenStaticDelegateFunctionPointer();
364         }
365 
366         /// <summary>
367         /// Retrieves the current delegate that is being called
368         /// </summary>
369         public static T GetCurrentCalleeDelegate<T>() where T : class // constraint can't be System.Delegate
370         {
371             return PInvokeMarshal.GetCurrentCalleeDelegate<T>();
372         }
373 
374         [StructLayout(LayoutKind.Sequential)]
375         internal unsafe struct ModuleFixupCell
376         {
377             public IntPtr Handle;
378             public IntPtr ModuleName;
379         }
380 
381         [StructLayout(LayoutKind.Sequential)]
382         internal unsafe struct MethodFixupCell
383         {
384             public IntPtr Target;
385             public IntPtr MethodName;
386             public ModuleFixupCell* Module;
387         }
388     }
389 }
390