LibraryLoader.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. #include "LibraryLoader.h"
  2. #include "utils/StringUtils.h"
  3. #include "utils/Exception.h"
  4. #if !RUNTIME_TINY
  5. #include "os/Mutex.h"
  6. #endif
  7. namespace il2cpp
  8. {
  9. namespace os
  10. {
  11. static Il2CppSetFindPlugInCallback s_FindPluginCallback = NULL;
  12. #if !RUNTIME_TINY
  13. typedef std::vector<std::pair<std::basic_string<Il2CppNativeChar>, Baselib_DynamicLibrary_Handle> > DllCacheContainer;
  14. typedef DllCacheContainer::const_iterator DllCacheIterator;
  15. static DllCacheContainer s_DllCache; // If a library does not need to be closed - do not add it to the cache.
  16. static baselib::ReentrantLock s_DllCacheMutex;
  17. #endif
  18. static inline Il2CppNativeChar AsciiToLower(Il2CppNativeChar c)
  19. {
  20. if (c >= 'A' && c <= 'Z')
  21. return c - 'A' + 'a';
  22. return c;
  23. }
  24. static bool DoesNativeDynamicLibraryNameMatch(const il2cpp::utils::StringView<Il2CppNativeChar>& desiredLibraryName, const Il2CppNativeChar* hardcodedLibraryName)
  25. {
  26. size_t desiredLibraryNameLength = desiredLibraryName.Length();
  27. for (size_t i = 0; i < desiredLibraryNameLength; i++)
  28. {
  29. Il2CppNativeChar desiredCharacter = AsciiToLower(desiredLibraryName[i]);
  30. Il2CppNativeChar hardcodedCharacter = hardcodedLibraryName[i];
  31. // Assume hardcodedLibraryName consists of only lower case ascii characters
  32. IL2CPP_ASSERT(hardcodedCharacter < 128 && (hardcodedCharacter<'A' || hardcodedCharacter> 'Z'));
  33. if (desiredCharacter != hardcodedCharacter)
  34. {
  35. // If we've reached end of our hardcoded dll name, it can still match if we've
  36. // reached end of desiredLibraryName file name and only the extension is left
  37. return hardcodedCharacter == 0 &&
  38. i + 4 == desiredLibraryNameLength &&
  39. desiredLibraryName[i] == '.' &&
  40. AsciiToLower(desiredLibraryName[i + 1]) == 'd' &&
  41. AsciiToLower(desiredLibraryName[i + 2]) == 'l' &&
  42. AsciiToLower(desiredLibraryName[i + 3]) == 'l';
  43. }
  44. else if (hardcodedCharacter == 0)
  45. {
  46. // We've reached the end of hardcoded library name
  47. // It's a match if we're at the end of desired library name too
  48. return i + 1 == desiredLibraryNameLength;
  49. }
  50. else if (i == desiredLibraryNameLength - 1)
  51. {
  52. // We've reached the end of desired library name
  53. // It's a match if we're at the end of hardcoded library name too
  54. return hardcodedLibraryName[i + 1] == 0;
  55. }
  56. }
  57. // We've reached the end of desired library name,
  58. // but not the end of hardcoded library name.
  59. // It is not a match.
  60. return false;
  61. }
  62. Il2CppMethodPointer LibraryLoader::GetHardcodedPInvokeDependencyFunctionPointer(const il2cpp::utils::StringView<Il2CppNativeChar>& nativeDynamicLibrary, const il2cpp::utils::StringView<char>& entryPoint, Il2CppCharSet charSet)
  63. {
  64. // We don't support, nor do we need to Ansi functions. That would break forwarding method names to Unicode MoveFileEx -> MoveFileExW
  65. if (HardcodedPInvokeDependencies == NULL || charSet == CHARSET_ANSI)
  66. return NULL;
  67. for (size_t i = 0; i < HardcodedPInvokeDependenciesCount; i++)
  68. {
  69. const HardcodedPInvokeDependencyLibrary& library = HardcodedPInvokeDependencies[i];
  70. if (DoesNativeDynamicLibraryNameMatch(nativeDynamicLibrary, library.libraryName))
  71. {
  72. size_t functionCount = library.functionCount;
  73. for (size_t j = 0; j < functionCount; j++)
  74. {
  75. const HardcodedPInvokeDependencyFunction function = library.functions[j];
  76. if (EntryNameMatches(il2cpp::utils::StringView<char>(function.functionName, function.functionNameLen), entryPoint))
  77. return function.functionPointer;
  78. }
  79. // We assume that kHardcodedPInvokeDependencies will not contain duplicates
  80. return NULL;
  81. }
  82. }
  83. return NULL;
  84. }
  85. Baselib_DynamicLibrary_Handle LibraryLoader::LoadDynamicLibrary(const utils::StringView<Il2CppNativeChar> nativeDynamicLibrary, std::string& detailedError)
  86. {
  87. StringViewAsNullTerminatedStringOf(Il2CppNativeChar, nativeDynamicLibrary, libraryName);
  88. if (s_FindPluginCallback)
  89. libraryName = s_FindPluginCallback(libraryName);
  90. auto libraryNameLength = utils::StringUtils::StrLen(libraryName);
  91. #if !RUNTIME_TINY
  92. {
  93. os::FastAutoLock lock(&s_DllCacheMutex);
  94. for (DllCacheIterator it = s_DllCache.begin(); it != s_DllCache.end(); it++)
  95. if (it->first.compare(0, std::string::npos, libraryName, libraryNameLength) == 0)
  96. return it->second;
  97. }
  98. #endif
  99. bool needsClosing = true;
  100. auto handle = Baselib_DynamicLibrary_Handle_Invalid;
  101. if (libraryName == nullptr || libraryNameLength == 0)
  102. {
  103. auto errorState = Baselib_ErrorState_Create();
  104. handle = OpenProgramHandle(errorState, needsClosing);
  105. // Disabling it for emscripten and tiny builds as they seem to be quite code sensitive
  106. #if (!RUNTIME_TINY) && (!defined(__EMSCRIPTEN__))
  107. if (Baselib_ErrorState_ErrorRaised(&errorState))
  108. {
  109. if (!detailedError.empty())
  110. detailedError += " ";
  111. detailedError += "Unable to open program handle because of '";
  112. detailedError += utils::Exception::FormatBaselibErrorState(errorState);
  113. detailedError += "'.";
  114. }
  115. #endif
  116. }
  117. else
  118. handle = ProbeForLibrary(libraryName, libraryNameLength, detailedError);
  119. #if !RUNTIME_TINY
  120. if ((handle != Baselib_DynamicLibrary_Handle_Invalid) && needsClosing)
  121. {
  122. os::FastAutoLock lock(&s_DllCacheMutex);
  123. s_DllCache.push_back(std::make_pair(libraryName, handle));
  124. }
  125. #endif
  126. return handle;
  127. }
  128. Il2CppMethodPointer LibraryLoader::GetFunctionPointer(Baselib_DynamicLibrary_Handle handle, const PInvokeArguments& pinvokeArgs, std::string& detailedError)
  129. {
  130. if (handle == Baselib_DynamicLibrary_Handle_Invalid)
  131. return NULL;
  132. StringViewAsNullTerminatedStringOf(char, pinvokeArgs.entryPoint, entryPoint);
  133. // If there's 'no mangle' flag set, just return directly what GetProcAddress returns
  134. if (pinvokeArgs.isNoMangle)
  135. return GetFunctionPointer(handle, entryPoint, detailedError);
  136. const size_t kBufferOverhead = 10;
  137. Il2CppMethodPointer func = nullptr;
  138. size_t originalFuncNameLength = strlen(entryPoint) + 1;
  139. std::string functionName;
  140. functionName.resize(originalFuncNameLength + kBufferOverhead + 1); // Let's index the string from '1', because we might have to prepend an underscore in case of stdcall mangling
  141. memcpy(&functionName[1], entryPoint, originalFuncNameLength);
  142. memset(&functionName[1] + originalFuncNameLength, 0, kBufferOverhead);
  143. // If there's no 'dont mangle' flag set, 'W' function takes priority over original name, but 'A' function does not (yes, really)
  144. if (pinvokeArgs.charSet == CHARSET_UNICODE)
  145. {
  146. functionName[originalFuncNameLength] = 'W';
  147. if ((func = GetFunctionPointer(handle, functionName.c_str() + 1, detailedError)))
  148. return func;
  149. // If charset specific function lookup failed, try with original name
  150. if ((func = GetFunctionPointer(handle, entryPoint, detailedError)))
  151. return func;
  152. }
  153. else
  154. {
  155. if ((func = GetFunctionPointer(handle, entryPoint, detailedError)))
  156. return func;
  157. // If original name function lookup failed, try with mangled name
  158. functionName[originalFuncNameLength] = 'A';
  159. if ((func = GetFunctionPointer(handle, functionName.c_str() + 1, detailedError)))
  160. return func;
  161. }
  162. // TODO is this Win only?
  163. // If it's not cdecl, try mangling the name
  164. // THIS ONLY APPLIES TO 32-bit x86!
  165. #if defined(_X86_) && PLATFORM_ARCH_32
  166. if (sizeof(void*) == 4 && pinvokeArgs.callingConvention != IL2CPP_CALL_C)
  167. {
  168. functionName[0] = '_';
  169. sprintf(&functionName[0] + originalFuncNameLength, "@%i", pinvokeArgs.parameterSize);
  170. if ((func = GetFunctionPointer(handle, functionName.c_str(), detailedError)))
  171. return func;
  172. }
  173. #endif
  174. return NULL;
  175. }
  176. Il2CppMethodPointer LibraryLoader::GetFunctionPointer(Baselib_DynamicLibrary_Handle handle, const char* functionName, std::string& detailedError)
  177. {
  178. auto errorState = Baselib_ErrorState_Create();
  179. if (handle == Baselib_DynamicLibrary_Handle_Invalid)
  180. return NULL;
  181. auto func = reinterpret_cast<Il2CppMethodPointer>(Baselib_DynamicLibrary_GetFunction(handle, functionName, &errorState));
  182. #if (!RUNTIME_TINY) && (!defined(__EMSCRIPTEN__))
  183. if (Baselib_ErrorState_ErrorRaised(&errorState))
  184. {
  185. if (!detailedError.empty())
  186. detailedError += " ";
  187. detailedError += "Unable to get function '";
  188. detailedError += functionName;
  189. detailedError += "' because of '";
  190. detailedError += utils::Exception::FormatBaselibErrorState(errorState);
  191. detailedError += "'.";
  192. }
  193. #else
  194. NO_UNUSED_WARNING(detailedError);
  195. #endif
  196. return func;
  197. }
  198. void LibraryLoader::CleanupLoadedLibraries()
  199. {
  200. #if !RUNTIME_TINY
  201. // We assume that presence of the library in s_DllCache is a valid reason to be able to close it
  202. for (DllCacheIterator it = s_DllCache.begin(); it != s_DllCache.end(); it++)
  203. {
  204. // If libc is a "loaded library", it is a special case, and closing it will cause dlclose
  205. // on some Posix platforms to return an error (I'm looking at you, iOS 11). This really is
  206. // not an error, but Baselib_DynamicLibrary_Close will correctly assert when dlclose
  207. // returns an error. To avoid this assert, let's skip closing libc.
  208. if (utils::StringUtils::NativeStringToUtf8(it->first.c_str()) != "libc")
  209. Baselib_DynamicLibrary_Close(it->second);
  210. }
  211. s_DllCache.clear();
  212. #endif
  213. }
  214. bool LibraryLoader::CloseLoadedLibrary(Baselib_DynamicLibrary_Handle handle)
  215. {
  216. if (handle == Baselib_DynamicLibrary_Handle_Invalid)
  217. return false;
  218. #if !RUNTIME_TINY
  219. os::FastAutoLock lock(&s_DllCacheMutex);
  220. // We assume that presence of the library in s_DllCache is a valid reason to be able to close it
  221. for (DllCacheIterator it = s_DllCache.begin(); it != s_DllCache.end(); it++)
  222. {
  223. if (it->second == handle)
  224. {
  225. Baselib_DynamicLibrary_Close(it->second);
  226. s_DllCache.erase(it);
  227. return true;
  228. }
  229. }
  230. #endif
  231. return false;
  232. }
  233. void LibraryLoader::SetFindPluginCallback(Il2CppSetFindPlugInCallback method)
  234. {
  235. s_FindPluginCallback = method;
  236. }
  237. Baselib_DynamicLibrary_Handle LibraryLoader::TryOpeningLibrary(const Il2CppNativeChar* libraryName, std::string& detailedError)
  238. {
  239. auto errorState = Baselib_ErrorState_Create();
  240. auto handle = Baselib_DynamicLibrary_Open(utils::StringUtils::NativeStringToBaselib(libraryName), &errorState);
  241. #if (!RUNTIME_TINY) && (!defined(__EMSCRIPTEN__))
  242. if (Baselib_ErrorState_ErrorRaised(&errorState))
  243. {
  244. if (!detailedError.empty())
  245. detailedError += " ";
  246. detailedError += "Unable to load dynamic library '";
  247. detailedError += utils::StringUtils::NativeStringToUtf8(libraryName);
  248. detailedError += "' because of '";
  249. detailedError += utils::Exception::FormatBaselibErrorState(errorState);
  250. detailedError += "'.";
  251. }
  252. #else
  253. NO_UNUSED_WARNING(detailedError);
  254. #endif
  255. return handle;
  256. }
  257. } /* namespace vm */
  258. } /* namespace il2cpp */