PlatformInvoke.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  1. #include "il2cpp-config.h"
  2. #include "il2cpp-object-internals.h"
  3. #include "il2cpp-class-internals.h"
  4. #include "PlatformInvoke.h"
  5. #include "Exception.h"
  6. #include "MetadataCache.h"
  7. #include "Method.h"
  8. #include "Object.h"
  9. #include "Runtime.h"
  10. #include "Type.h"
  11. #include "Reflection.h"
  12. #include "gc/WriteBarrier.h"
  13. #include "os/LibraryLoader.h"
  14. #include "os/MarshalStringAlloc.h"
  15. #include "utils/Memory.h"
  16. #include "utils/StringUtils.h"
  17. #include "vm-utils/VmStringUtils.h"
  18. #include "hybridclr/metadata/MetadataUtil.h"
  19. #include "hybridclr/metadata/MetadataModule.h"
  20. #include "hybridclr/interpreter/InterpreterModule.h"
  21. #include <stdint.h>
  22. #include <algorithm>
  23. namespace il2cpp
  24. {
  25. namespace vm
  26. {
  27. void PlatformInvoke::SetFindPluginCallback(Il2CppSetFindPlugInCallback method)
  28. {
  29. os::LibraryLoader::SetFindPluginCallback(method);
  30. }
  31. Il2CppMethodPointer PlatformInvoke::Resolve(const PInvokeArguments& pinvokeArgs)
  32. {
  33. // Before resolving a P/Invoke, check against a hardcoded list of "known P/Invokes" that is different for every platform.
  34. // This bit solves several different problems we have when P/Invoking into native system libraries from mscorlib.dll.
  35. // Some platforms, like UWP, just don't allow you to load to load system libraries at runtime dynamically.
  36. // On other platforms (THEY SHALL NOT BE NAMED :O), while the functions that mscorlib.dll wants to P/Invoke into exist,
  37. // They exist in different system libraries than it is said in the DllImport attribute.
  38. Il2CppMethodPointer function = os::LibraryLoader::GetHardcodedPInvokeDependencyFunctionPointer(pinvokeArgs.moduleName, pinvokeArgs.entryPoint, pinvokeArgs.charSet);
  39. if (function != NULL)
  40. return function;
  41. Baselib_DynamicLibrary_Handle dynamicLibrary = Baselib_DynamicLibrary_Handle_Invalid;
  42. std::string detailedLoadError;
  43. if (utils::VmStringUtils::CaseSensitiveEquals(il2cpp::utils::StringUtils::NativeStringToUtf8(pinvokeArgs.moduleName.Str()).c_str(), "__InternalDynamic"))
  44. dynamicLibrary = os::LibraryLoader::LoadDynamicLibrary(il2cpp::utils::StringView<Il2CppNativeChar>::Empty(), detailedLoadError);
  45. else
  46. dynamicLibrary = os::LibraryLoader::LoadDynamicLibrary(pinvokeArgs.moduleName, detailedLoadError);
  47. if (dynamicLibrary == Baselib_DynamicLibrary_Handle_Invalid)
  48. {
  49. std::string message;
  50. message += "Unable to load DLL '";
  51. message += il2cpp::utils::StringUtils::NativeStringToUtf8(pinvokeArgs.moduleName.Str());
  52. message += "'. Tried the load the following dynamic libraries: ";
  53. message += detailedLoadError;
  54. Exception::Raise(Exception::GetDllNotFoundException(message.c_str()));
  55. }
  56. std::string detailedGetFunctionError;
  57. function = os::LibraryLoader::GetFunctionPointer(dynamicLibrary, pinvokeArgs, detailedGetFunctionError);
  58. if (function == NULL)
  59. {
  60. std::string message;
  61. message += "Unable to find an entry point named '";
  62. message += pinvokeArgs.entryPoint.Str();
  63. message += "' in '";
  64. message += il2cpp::utils::StringUtils::NativeStringToUtf8(pinvokeArgs.moduleName.Str());
  65. message += "'. Tried the following entry points: ";
  66. message += detailedGetFunctionError;
  67. Exception::Raise(Exception::GetEntryPointNotFoundException(message.c_str()));
  68. }
  69. return function;
  70. }
  71. void PlatformInvoke::MarshalFree(void* ptr)
  72. {
  73. if (ptr != NULL)
  74. MarshalAlloc::Free(ptr);
  75. }
  76. char* PlatformInvoke::MarshalCSharpStringToCppString(Il2CppString* managedString)
  77. {
  78. if (managedString == NULL)
  79. return NULL;
  80. std::string utf8String = utils::StringUtils::Utf16ToUtf8(managedString->chars);
  81. char* nativeString = MarshalAllocateStringBuffer<char>(utf8String.size() + 1);
  82. strcpy(nativeString, utf8String.c_str());
  83. return nativeString;
  84. }
  85. void PlatformInvoke::MarshalCSharpStringToCppStringFixed(Il2CppString* managedString, char* buffer, int numberOfCharacters)
  86. {
  87. if (managedString == NULL)
  88. {
  89. *buffer = '\0';
  90. }
  91. else
  92. {
  93. std::string utf8String = utils::StringUtils::Utf16ToUtf8(managedString->chars, numberOfCharacters - 1);
  94. strcpy(buffer, utf8String.c_str());
  95. }
  96. }
  97. Il2CppChar* PlatformInvoke::MarshalCSharpStringToCppWString(Il2CppString* managedString)
  98. {
  99. if (managedString == NULL)
  100. return NULL;
  101. int32_t stringLength = utils::StringUtils::GetLength(managedString);
  102. Il2CppChar* nativeString = MarshalAllocateStringBuffer<Il2CppChar>(stringLength + 1);
  103. for (int32_t i = 0; i < managedString->length; ++i)
  104. nativeString[i] = managedString->chars[i];
  105. nativeString[managedString->length] = '\0';
  106. return nativeString;
  107. }
  108. void PlatformInvoke::MarshalCSharpStringToCppWStringFixed(Il2CppString* managedString, Il2CppChar* buffer, int numberOfCharacters)
  109. {
  110. if (managedString == NULL)
  111. {
  112. *buffer = '\0';
  113. }
  114. else
  115. {
  116. int32_t stringLength = std::min(utils::StringUtils::GetLength(managedString), numberOfCharacters - 1);
  117. for (int32_t i = 0; i < stringLength; ++i)
  118. buffer[i] = managedString->chars[i];
  119. buffer[stringLength] = '\0';
  120. }
  121. }
  122. il2cpp_hresult_t PlatformInvoke::MarshalCSharpStringToCppBStringNoThrow(Il2CppString* managedString, Il2CppChar** bstr)
  123. {
  124. IL2CPP_ASSERT(bstr);
  125. if (managedString == NULL)
  126. {
  127. *bstr = NULL;
  128. return IL2CPP_S_OK;
  129. }
  130. int32_t stringLength = utils::StringUtils::GetLength(managedString);
  131. Il2CppChar* stringChars = utils::StringUtils::GetChars(managedString);
  132. return os::MarshalStringAlloc::AllocateBStringLength(stringChars, stringLength, bstr);
  133. }
  134. Il2CppChar* PlatformInvoke::MarshalCSharpStringToCppBString(Il2CppString* managedString)
  135. {
  136. Il2CppChar* bstr;
  137. const il2cpp_hresult_t hr = MarshalCSharpStringToCppBStringNoThrow(managedString, &bstr);
  138. vm::Exception::RaiseIfFailed(hr, true);
  139. return bstr;
  140. }
  141. Il2CppString* PlatformInvoke::MarshalCppStringToCSharpStringResult(const char* value)
  142. {
  143. if (value == NULL)
  144. return NULL;
  145. return String::New(value);
  146. }
  147. Il2CppString* PlatformInvoke::MarshalCppWStringToCSharpStringResult(const Il2CppChar* value)
  148. {
  149. if (value == NULL)
  150. return NULL;
  151. return String::NewUtf16(value, (int32_t)utils::StringUtils::StrLen(value));
  152. }
  153. Il2CppString* PlatformInvoke::MarshalCppBStringToCSharpStringResult(const Il2CppChar* value)
  154. {
  155. if (value == NULL)
  156. return NULL;
  157. int32_t length;
  158. const il2cpp_hresult_t hr = os::MarshalStringAlloc::GetBStringLength(value, &length);
  159. vm::Exception::RaiseIfFailed(hr, true);
  160. return String::NewUtf16(value, length);
  161. }
  162. void PlatformInvoke::MarshalFreeBString(Il2CppChar* value)
  163. {
  164. const il2cpp_hresult_t hr = os::MarshalStringAlloc::FreeBString(value);
  165. vm::Exception::RaiseIfFailed(hr, true);
  166. }
  167. char* PlatformInvoke::MarshalEmptyStringBuilder(Il2CppStringBuilder* stringBuilder, size_t& stringLength, std::vector<std::string>& utf8Chunks, std::vector<Il2CppStringBuilder*>& builders)
  168. {
  169. if (stringBuilder == NULL)
  170. return NULL;
  171. stringLength = 0;
  172. Il2CppStringBuilder* currentBuilder = stringBuilder;
  173. while (true)
  174. {
  175. if (currentBuilder == NULL)
  176. break;
  177. const Il2CppChar *str = (Il2CppChar*)il2cpp::vm::Array::GetFirstElementAddress(currentBuilder->chunkChars);
  178. std::string utf8String = utils::StringUtils::Utf16ToUtf8(str, (int)currentBuilder->chunkChars->max_length);
  179. utf8Chunks.push_back(utf8String);
  180. builders.push_back(currentBuilder);
  181. size_t lenToCount = std::max((size_t)currentBuilder->chunkChars->max_length, utf8String.size());
  182. stringLength += lenToCount;
  183. currentBuilder = currentBuilder->chunkPrevious;
  184. }
  185. char* nativeString = MarshalAllocateStringBuffer<char>(stringLength + 1);
  186. // We need to zero out the memory because the chunkChar array lengh may have been larger than the chunkLength
  187. // and when this happens we'll have a utf8String that is smaller than the the nativeString we allocated. When we go to copy the
  188. // chunk utf8String into the nativeString it won't fill everything and we can end up with w/e junk value was in that memory before
  189. memset(nativeString, 0, sizeof(char) * (stringLength + 1));
  190. return nativeString;
  191. }
  192. char* PlatformInvoke::MarshalEmptyStringBuilder(Il2CppStringBuilder* stringBuilder)
  193. {
  194. size_t sizeLength;
  195. std::vector<std::string> utf8Chunks;
  196. std::vector<Il2CppStringBuilder*> builders;
  197. return MarshalEmptyStringBuilder(stringBuilder, sizeLength, utf8Chunks, builders);
  198. }
  199. char* PlatformInvoke::MarshalStringBuilder(Il2CppStringBuilder* stringBuilder)
  200. {
  201. if (stringBuilder == NULL)
  202. return NULL;
  203. size_t stringLength;
  204. std::vector<std::string> utf8Chunks;
  205. std::vector<Il2CppStringBuilder*> builders;
  206. char* nativeString = MarshalEmptyStringBuilder(stringBuilder, stringLength, utf8Chunks, builders);
  207. if (stringLength > 0)
  208. {
  209. int offsetAdjustment = 0;
  210. for (int i = (int)utf8Chunks.size() - 1; i >= 0; i--)
  211. {
  212. std::string utf8String = utf8Chunks[i];
  213. const char* utf8CString = utf8String.c_str();
  214. memcpy(nativeString + builders[i]->chunkOffset + offsetAdjustment, utf8CString, (int)utf8String.size());
  215. offsetAdjustment += (int)utf8String.size() - builders[i]->chunkLength;
  216. }
  217. }
  218. return nativeString;
  219. }
  220. Il2CppChar* PlatformInvoke::MarshalEmptyWStringBuilder(Il2CppStringBuilder* stringBuilder, size_t& stringLength)
  221. {
  222. if (stringBuilder == NULL)
  223. return NULL;
  224. stringLength = 0;
  225. Il2CppStringBuilder* currentBuilder = stringBuilder;
  226. while (true)
  227. {
  228. if (currentBuilder == NULL)
  229. break;
  230. stringLength += (size_t)currentBuilder->chunkChars->max_length;
  231. currentBuilder = currentBuilder->chunkPrevious;
  232. }
  233. return MarshalAllocateStringBuffer<Il2CppChar>(stringLength + 1);
  234. }
  235. Il2CppChar* PlatformInvoke::MarshalEmptyWStringBuilder(Il2CppStringBuilder* stringBuilder)
  236. {
  237. size_t stringLength;
  238. return MarshalEmptyWStringBuilder(stringBuilder, stringLength);
  239. }
  240. Il2CppChar* PlatformInvoke::MarshalWStringBuilder(Il2CppStringBuilder* stringBuilder)
  241. {
  242. if (stringBuilder == NULL)
  243. return NULL;
  244. size_t stringLength;
  245. Il2CppChar* nativeString = MarshalEmptyWStringBuilder(stringBuilder, stringLength);
  246. if (stringLength > 0)
  247. {
  248. Il2CppStringBuilder* currentBuilder = stringBuilder;
  249. while (true)
  250. {
  251. if (currentBuilder == NULL)
  252. break;
  253. const Il2CppChar *str = (Il2CppChar*)il2cpp::vm::Array::GetFirstElementAddress(currentBuilder->chunkChars);
  254. memcpy(nativeString + currentBuilder->chunkOffset, str, (int)currentBuilder->chunkChars->max_length * sizeof(Il2CppChar));
  255. currentBuilder = currentBuilder->chunkPrevious;
  256. }
  257. }
  258. nativeString[stringLength] = '\0';
  259. return nativeString;
  260. }
  261. void PlatformInvoke::MarshalStringBuilderResult(Il2CppStringBuilder* stringBuilder, char* buffer)
  262. {
  263. if (stringBuilder == NULL || buffer == NULL)
  264. return;
  265. UTF16String utf16String = utils::StringUtils::Utf8ToUtf16(buffer);
  266. IL2CPP_OBJECT_SETREF(stringBuilder, chunkChars, il2cpp::vm::Array::New(il2cpp_defaults.char_class, (int)utf16String.size() + 1));
  267. for (int i = 0; i < (int)utf16String.size(); i++)
  268. il2cpp_array_set(stringBuilder->chunkChars, Il2CppChar, i, utf16String[i]);
  269. il2cpp_array_set(stringBuilder->chunkChars, Il2CppChar, (int)utf16String.size(), '\0');
  270. stringBuilder->chunkLength = (int)utf16String.size();
  271. stringBuilder->chunkOffset = 0;
  272. IL2CPP_OBJECT_SETREF_NULL(stringBuilder, chunkPrevious);
  273. }
  274. void PlatformInvoke::MarshalWStringBuilderResult(Il2CppStringBuilder* stringBuilder, Il2CppChar* buffer)
  275. {
  276. if (stringBuilder == NULL || buffer == NULL)
  277. return;
  278. int len = (int)utils::StringUtils::StrLen(buffer);
  279. IL2CPP_OBJECT_SETREF(stringBuilder, chunkChars, il2cpp::vm::Array::New(il2cpp_defaults.char_class, len + 1));
  280. for (int i = 0; i < len; i++)
  281. il2cpp_array_set(stringBuilder->chunkChars, Il2CppChar, i, buffer[i]);
  282. il2cpp_array_set(stringBuilder->chunkChars, Il2CppChar, len, '\0');
  283. stringBuilder->chunkLength = len;
  284. stringBuilder->chunkOffset = 0;
  285. IL2CPP_OBJECT_SETREF_NULL(stringBuilder, chunkPrevious);
  286. }
  287. static bool IsGenericInstance(const Il2CppType* type)
  288. {
  289. if (type->type == IL2CPP_TYPE_GENERICINST)
  290. return true;
  291. while (type->type == IL2CPP_TYPE_SZARRAY)
  292. {
  293. if (type->data.type->type == IL2CPP_TYPE_GENERICINST)
  294. return true;
  295. type = type->data.type;
  296. }
  297. return false;
  298. }
  299. static std::string TypeNameListFor(const Il2CppGenericInst* genericInst)
  300. {
  301. std::string typeNameList;
  302. if (genericInst != NULL)
  303. {
  304. int numberOfTypeArguments = genericInst->type_argc;
  305. for (int i = 0; i < numberOfTypeArguments; i++)
  306. {
  307. typeNameList += Type::GetName(genericInst->type_argv[i], IL2CPP_TYPE_NAME_FORMAT_FULL_NAME);
  308. if (i != numberOfTypeArguments - 1)
  309. typeNameList += ", ";
  310. }
  311. }
  312. return typeNameList;
  313. }
  314. static Il2CppCallConvention GetDelegateCallConvention(Il2CppDelegate* d)
  315. {
  316. const Il2CppImage* image = d->object.klass->image;
  317. Il2CppMetadataCustomAttributeHandle customAttributeHandle = MetadataCache::GetCustomAttributeTypeToken(image, d->object.klass->token);
  318. if (!customAttributeHandle)
  319. {
  320. return IL2CPP_CALL_DEFAULT;
  321. }
  322. static Il2CppClass* umanagedFunctionPointerAttributeClass = nullptr;
  323. if (umanagedFunctionPointerAttributeClass == nullptr)
  324. {
  325. umanagedFunctionPointerAttributeClass = Class::FromName(il2cpp_defaults.corlib, "System.Runtime.InteropServices", "UnmanagedFunctionPointerAttribute");
  326. if (umanagedFunctionPointerAttributeClass == nullptr)
  327. {
  328. return IL2CPP_CALL_DEFAULT;
  329. }
  330. }
  331. Il2CppObject* customAttribute = il2cpp::vm::Reflection::GetCustomAttribute(customAttributeHandle, umanagedFunctionPointerAttributeClass);
  332. if (!customAttribute)
  333. {
  334. return IL2CPP_CALL_DEFAULT;
  335. }
  336. int32_t callConv = *(int32_t*)(customAttribute + 1);
  337. IL2CPP_ASSERT(callConv >= 1 && callConv <= 5);
  338. return (Il2CppCallConvention)(callConv - 1);
  339. }
  340. #if !IL2CPP_TINY
  341. intptr_t PlatformInvoke::MarshalDelegate(Il2CppDelegate* d)
  342. {
  343. if (d == NULL)
  344. return 0;
  345. if (IsFakeDelegateMethodMarshaledFromNativeCode(d))
  346. return reinterpret_cast<intptr_t>(d->delegate_trampoline);
  347. const MethodInfo* method = d->method;
  348. if (method && hybridclr::metadata::IsInterpreterImplement(method))
  349. {
  350. Il2CppCallConvention callConvention = GetDelegateCallConvention(d);
  351. return reinterpret_cast<intptr_t>(hybridclr::interpreter::InterpreterModule::GetReversePInvokeWrapper(d->method->klass->image, method, callConvention));
  352. }
  353. Il2CppMethodPointer reversePInvokeWrapper = MetadataCache::GetReversePInvokeWrapper(d->method->klass->image, d->method);
  354. if (reversePInvokeWrapper == NULL)
  355. {
  356. std::string methodName = il2cpp::vm::Method::GetFullName(d->method);
  357. // Okay, we cannot marshal it for some reason. Figure out why.
  358. if (Method::IsInstance(d->method))
  359. {
  360. std::string errorMessage = "IL2CPP does not support marshaling delegates that point to instance methods to native code. The method we're attempting to marshal is: " + methodName;
  361. vm::Exception::Raise(vm::Exception::GetNotSupportedException(errorMessage.c_str()));
  362. }
  363. if (il2cpp::vm::Method::HasFullGenericSharingSignature(d->method))
  364. {
  365. std::string errorMessage = "IL2CPP does not support marshaling generic delegates when full generic sharing is enabled. The method we're attempting to marshal is: " + methodName;
  366. errorMessage += "\nTo marshal this delegate, please add an attribute named 'MonoPInvokeCallback' to the method definition.";
  367. errorMessage += "\nThis attribute should have a type argument which is a generic delegate with all of the types required for this generic instantiation:";
  368. std::string genericTypeArguments = TypeNameListFor(d->method->genericMethod->context.class_inst);
  369. if (!genericTypeArguments.empty())
  370. errorMessage += "\nGeneric type arguments: " + genericTypeArguments;
  371. std::string genericMethodArguments = TypeNameListFor(d->method->genericMethod->context.method_inst);
  372. if (!genericMethodArguments.empty())
  373. errorMessage += "\nGeneric method arguments: " + genericMethodArguments;
  374. errorMessage += "\nThis C# code should work, for example:";
  375. std::string maybeComma = !genericTypeArguments.empty() ? ", " : "";
  376. errorMessage += "\n[MonoPInvokeCallback(typeof(System.Action<" + genericTypeArguments + maybeComma + genericMethodArguments + ">))]";
  377. vm::Exception::Raise(vm::Exception::GetNotSupportedException(errorMessage.c_str()));
  378. }
  379. if (d->method->parameters != NULL)
  380. {
  381. for (int i = 0; i < d->method->parameters_count; ++i)
  382. {
  383. if (IsGenericInstance(d->method->parameters[i]))
  384. {
  385. std::string errorMessage = "Cannot marshal method '" + methodName + "' parameter '" + Method::GetParamName(d->method, i) + "': Generic types cannot be marshaled.";
  386. vm::Exception::Raise(vm::Exception::GetMarshalDirectiveException(errorMessage.c_str()));
  387. }
  388. }
  389. }
  390. std::string errorMessage = "To marshal a managed method, please add an attribute named 'MonoPInvokeCallback' to the method definition. The method we're attempting to marshal is: " + methodName;
  391. vm::Exception::Raise(vm::Exception::GetNotSupportedException(errorMessage.c_str()));
  392. }
  393. return reinterpret_cast<intptr_t>(reversePInvokeWrapper);
  394. }
  395. Il2CppDelegate* PlatformInvoke::MarshalFunctionPointerToDelegate(void* functionPtr, Il2CppClass* delegateType)
  396. {
  397. if (functionPtr == NULL)
  398. return NULL;
  399. if (!Class::HasParent(delegateType, il2cpp_defaults.delegate_class))
  400. Exception::Raise(Exception::GetArgumentException("t", "Type must derive from Delegate."));
  401. const Il2CppInteropData* interopData = delegateType->interopData;
  402. Il2CppMethodPointer managedToNativeWrapperMethodPointer = interopData != NULL ? interopData->delegatePInvokeWrapperFunction : NULL;
  403. if (managedToNativeWrapperMethodPointer == NULL)
  404. Exception::Raise(Exception::GetMarshalDirectiveException(utils::StringUtils::Printf("Cannot marshal P/Invoke call through delegate of type '%s.%s'", Class::GetNamespace(delegateType), Class::GetName(delegateType)).c_str()));
  405. const MethodInfo* invokeMethod = il2cpp::vm::Runtime::GetDelegateInvoke(delegateType);
  406. Il2CppDelegate* delegate = (Il2CppDelegate*)il2cpp::vm::Object::New(delegateType);
  407. Type::ConstructClosedDelegate(delegate, (Il2CppObject*)delegate, managedToNativeWrapperMethodPointer, invokeMethod);
  408. delegate->delegate_trampoline = functionPtr;
  409. return delegate;
  410. }
  411. // When a delegate is marshalled from native code via Marshal.GetDelegateForFunctionPointer
  412. // It will store the native function pointer in delegate_trampoline
  413. bool PlatformInvoke::IsFakeDelegateMethodMarshaledFromNativeCode(const Il2CppDelegate* d)
  414. {
  415. return d->delegate_trampoline != NULL;
  416. }
  417. #endif // !IL2CPP_TINY
  418. } /* namespace vm */
  419. } /* namespace il2cpp */