暂无描述
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

LibraryLoader.cpp 12KB

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