283 lines
12 KiB
C++
283 lines
12 KiB
C++
#include "LibraryLoader.h"
|
|
#include "utils/StringUtils.h"
|
|
#include "utils/Exception.h"
|
|
|
|
#include "os/Mutex.h"
|
|
|
|
namespace il2cpp
|
|
{
|
|
namespace os
|
|
{
|
|
static Il2CppSetFindPlugInCallback s_FindPluginCallback = NULL;
|
|
|
|
typedef std::vector<std::pair<std::basic_string<Il2CppNativeChar>, Baselib_DynamicLibrary_Handle> > DllCacheContainer;
|
|
typedef DllCacheContainer::const_iterator DllCacheIterator;
|
|
static DllCacheContainer s_DllCache; // If a library does not need to be closed - do not add it to the cache.
|
|
static baselib::ReentrantLock s_DllCacheMutex;
|
|
|
|
static inline Il2CppNativeChar AsciiToLower(Il2CppNativeChar c)
|
|
{
|
|
if (c >= 'A' && c <= 'Z')
|
|
return c - 'A' + 'a';
|
|
|
|
return c;
|
|
}
|
|
|
|
static bool DoesNativeDynamicLibraryNameMatch(const il2cpp::utils::StringView<Il2CppNativeChar>& desiredLibraryName, const Il2CppNativeChar* hardcodedLibraryName)
|
|
{
|
|
size_t desiredLibraryNameLength = desiredLibraryName.Length();
|
|
for (size_t i = 0; i < desiredLibraryNameLength; i++)
|
|
{
|
|
Il2CppNativeChar desiredCharacter = AsciiToLower(desiredLibraryName[i]);
|
|
Il2CppNativeChar hardcodedCharacter = hardcodedLibraryName[i];
|
|
|
|
// Assume hardcodedLibraryName consists of only lower case ascii characters
|
|
IL2CPP_ASSERT(hardcodedCharacter < 128 && (hardcodedCharacter<'A' || hardcodedCharacter> 'Z'));
|
|
|
|
if (desiredCharacter != hardcodedCharacter)
|
|
{
|
|
// If we've reached end of our hardcoded dll name, it can still match if we've
|
|
// reached end of desiredLibraryName file name and only the extension is left
|
|
return hardcodedCharacter == 0 &&
|
|
i + 4 == desiredLibraryNameLength &&
|
|
desiredLibraryName[i] == '.' &&
|
|
AsciiToLower(desiredLibraryName[i + 1]) == 'd' &&
|
|
AsciiToLower(desiredLibraryName[i + 2]) == 'l' &&
|
|
AsciiToLower(desiredLibraryName[i + 3]) == 'l';
|
|
}
|
|
else if (hardcodedCharacter == 0)
|
|
{
|
|
// We've reached the end of hardcoded library name
|
|
// It's a match if we're at the end of desired library name too
|
|
return i + 1 == desiredLibraryNameLength;
|
|
}
|
|
else if (i == desiredLibraryNameLength - 1)
|
|
{
|
|
// We've reached the end of desired library name
|
|
// It's a match if we're at the end of hardcoded library name too
|
|
return hardcodedLibraryName[i + 1] == 0;
|
|
}
|
|
}
|
|
|
|
// We've reached the end of desired library name,
|
|
// but not the end of hardcoded library name.
|
|
// It is not a match.
|
|
return false;
|
|
}
|
|
|
|
Il2CppMethodPointer LibraryLoader::GetHardcodedPInvokeDependencyFunctionPointer(const il2cpp::utils::StringView<Il2CppNativeChar>& nativeDynamicLibrary, const il2cpp::utils::StringView<char>& entryPoint, Il2CppCharSet charSet)
|
|
{
|
|
// We don't support, nor do we need to Ansi functions. That would break forwarding method names to Unicode MoveFileEx -> MoveFileExW
|
|
if (HardcodedPInvokeDependencies == NULL || charSet == CHARSET_ANSI)
|
|
return NULL;
|
|
|
|
for (size_t i = 0; i < HardcodedPInvokeDependenciesCount; i++)
|
|
{
|
|
const HardcodedPInvokeDependencyLibrary& library = HardcodedPInvokeDependencies[i];
|
|
if (DoesNativeDynamicLibraryNameMatch(nativeDynamicLibrary, library.libraryName))
|
|
{
|
|
size_t functionCount = library.functionCount;
|
|
for (size_t j = 0; j < functionCount; j++)
|
|
{
|
|
const HardcodedPInvokeDependencyFunction function = library.functions[j];
|
|
if (EntryNameMatches(il2cpp::utils::StringView<char>(function.functionName, function.functionNameLen), entryPoint))
|
|
return function.functionPointer;
|
|
}
|
|
|
|
// We assume that kHardcodedPInvokeDependencies will not contain duplicates
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
Baselib_DynamicLibrary_Handle LibraryLoader::LoadDynamicLibrary(const utils::StringView<Il2CppNativeChar> nativeDynamicLibrary, std::string& detailedError)
|
|
{
|
|
StringViewAsNullTerminatedStringOf(Il2CppNativeChar, nativeDynamicLibrary, libraryName);
|
|
if (s_FindPluginCallback)
|
|
libraryName = s_FindPluginCallback(libraryName);
|
|
auto libraryNameLength = utils::StringUtils::StrLen(libraryName);
|
|
|
|
{
|
|
os::FastAutoLock lock(&s_DllCacheMutex);
|
|
for (DllCacheIterator it = s_DllCache.begin(); it != s_DllCache.end(); it++)
|
|
if (it->first.compare(0, std::string::npos, libraryName, libraryNameLength) == 0)
|
|
return it->second;
|
|
}
|
|
|
|
bool needsClosing = true;
|
|
|
|
auto handle = Baselib_DynamicLibrary_Handle_Invalid;
|
|
if (libraryName == nullptr || libraryNameLength == 0)
|
|
{
|
|
auto errorState = Baselib_ErrorState_Create();
|
|
handle = OpenProgramHandle(errorState, needsClosing);
|
|
// Disabling it for emscripten builds as they seem to be quite code sensitive
|
|
#if (!defined(__EMSCRIPTEN__))
|
|
if (Baselib_ErrorState_ErrorRaised(&errorState))
|
|
{
|
|
if (!detailedError.empty())
|
|
detailedError += " ";
|
|
detailedError += "Unable to open program handle because of '";
|
|
detailedError += utils::Exception::FormatBaselibErrorState(errorState);
|
|
detailedError += "'.";
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
handle = ProbeForLibrary(libraryName, libraryNameLength, detailedError);
|
|
|
|
if ((handle != Baselib_DynamicLibrary_Handle_Invalid) && needsClosing)
|
|
{
|
|
os::FastAutoLock lock(&s_DllCacheMutex);
|
|
s_DllCache.push_back(std::make_pair(libraryName, handle));
|
|
}
|
|
|
|
return handle;
|
|
}
|
|
|
|
Il2CppMethodPointer LibraryLoader::GetFunctionPointer(Baselib_DynamicLibrary_Handle handle, const PInvokeArguments& pinvokeArgs, std::string& detailedError)
|
|
{
|
|
if (handle == Baselib_DynamicLibrary_Handle_Invalid)
|
|
return NULL;
|
|
|
|
StringViewAsNullTerminatedStringOf(char, pinvokeArgs.entryPoint, entryPoint);
|
|
|
|
// If there's 'no mangle' flag set, just return directly what GetProcAddress returns
|
|
if (pinvokeArgs.isNoMangle)
|
|
return GetFunctionPointer(handle, entryPoint, detailedError);
|
|
|
|
const size_t kBufferOverhead = 10;
|
|
Il2CppMethodPointer func = nullptr;
|
|
size_t originalFuncNameLength = strlen(entryPoint) + 1;
|
|
std::string functionName;
|
|
|
|
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
|
|
memcpy(&functionName[1], entryPoint, originalFuncNameLength);
|
|
memset(&functionName[1] + originalFuncNameLength, 0, kBufferOverhead);
|
|
|
|
// If there's no 'dont mangle' flag set, 'W' function takes priority over original name, but 'A' function does not (yes, really)
|
|
if (pinvokeArgs.charSet == CHARSET_UNICODE)
|
|
{
|
|
functionName[originalFuncNameLength] = 'W';
|
|
if ((func = GetFunctionPointer(handle, functionName.c_str() + 1, detailedError)))
|
|
return func;
|
|
|
|
// If charset specific function lookup failed, try with original name
|
|
if ((func = GetFunctionPointer(handle, entryPoint, detailedError)))
|
|
return func;
|
|
}
|
|
else
|
|
{
|
|
if ((func = GetFunctionPointer(handle, entryPoint, detailedError)))
|
|
return func;
|
|
|
|
// If original name function lookup failed, try with mangled name
|
|
functionName[originalFuncNameLength] = 'A';
|
|
if ((func = GetFunctionPointer(handle, functionName.c_str() + 1, detailedError)))
|
|
return func;
|
|
}
|
|
|
|
// TODO is this Win only?
|
|
// If it's not cdecl, try mangling the name
|
|
// THIS ONLY APPLIES TO 32-bit x86!
|
|
#if defined(_X86_) && PLATFORM_ARCH_32
|
|
if (sizeof(void*) == 4 && pinvokeArgs.callingConvention != IL2CPP_CALL_C)
|
|
{
|
|
functionName[0] = '_';
|
|
sprintf(&functionName[0] + originalFuncNameLength, "@%i", pinvokeArgs.parameterSize);
|
|
if ((func = GetFunctionPointer(handle, functionName.c_str(), detailedError)))
|
|
return func;
|
|
}
|
|
#endif
|
|
|
|
return NULL;
|
|
}
|
|
|
|
Il2CppMethodPointer LibraryLoader::GetFunctionPointer(Baselib_DynamicLibrary_Handle handle, const char* functionName, std::string& detailedError)
|
|
{
|
|
auto errorState = Baselib_ErrorState_Create();
|
|
if (handle == Baselib_DynamicLibrary_Handle_Invalid)
|
|
return NULL;
|
|
auto func = reinterpret_cast<Il2CppMethodPointer>(Baselib_DynamicLibrary_GetFunction(handle, functionName, &errorState));
|
|
#if (!defined(__EMSCRIPTEN__))
|
|
if (Baselib_ErrorState_ErrorRaised(&errorState))
|
|
{
|
|
if (!detailedError.empty())
|
|
detailedError += " ";
|
|
detailedError += "Unable to get function '";
|
|
detailedError += functionName;
|
|
detailedError += "' because of '";
|
|
detailedError += utils::Exception::FormatBaselibErrorState(errorState);
|
|
detailedError += "'.";
|
|
}
|
|
#else
|
|
NO_UNUSED_WARNING(detailedError);
|
|
#endif
|
|
return func;
|
|
}
|
|
|
|
void LibraryLoader::CleanupLoadedLibraries()
|
|
{
|
|
// We assume that presence of the library in s_DllCache is a valid reason to be able to close it
|
|
for (DllCacheIterator it = s_DllCache.begin(); it != s_DllCache.end(); it++)
|
|
{
|
|
// If libc is a "loaded library", it is a special case, and closing it will cause dlclose
|
|
// on some Posix platforms to return an error (I'm looking at you, iOS 11). This really is
|
|
// not an error, but Baselib_DynamicLibrary_Close will correctly assert when dlclose
|
|
// returns an error. To avoid this assert, let's skip closing libc.
|
|
if (utils::StringUtils::NativeStringToUtf8(it->first.c_str()) != "libc")
|
|
Baselib_DynamicLibrary_Close(it->second);
|
|
}
|
|
s_DllCache.clear();
|
|
}
|
|
|
|
bool LibraryLoader::CloseLoadedLibrary(Baselib_DynamicLibrary_Handle handle)
|
|
{
|
|
if (handle == Baselib_DynamicLibrary_Handle_Invalid)
|
|
return false;
|
|
|
|
os::FastAutoLock lock(&s_DllCacheMutex);
|
|
// We assume that presence of the library in s_DllCache is a valid reason to be able to close it
|
|
for (DllCacheIterator it = s_DllCache.begin(); it != s_DllCache.end(); it++)
|
|
{
|
|
if (it->second == handle)
|
|
{
|
|
Baselib_DynamicLibrary_Close(it->second);
|
|
s_DllCache.erase(it);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void LibraryLoader::SetFindPluginCallback(Il2CppSetFindPlugInCallback method)
|
|
{
|
|
s_FindPluginCallback = method;
|
|
}
|
|
|
|
Baselib_DynamicLibrary_Handle LibraryLoader::TryOpeningLibrary(const Il2CppNativeChar* libraryName, std::string& detailedError)
|
|
{
|
|
auto errorState = Baselib_ErrorState_Create();
|
|
auto handle = Baselib_DynamicLibrary_Open(utils::StringUtils::NativeStringToBaselib(libraryName), &errorState);
|
|
|
|
#if (!defined(__EMSCRIPTEN__))
|
|
if (Baselib_ErrorState_ErrorRaised(&errorState))
|
|
{
|
|
if (!detailedError.empty())
|
|
detailedError += " ";
|
|
detailedError += "Unable to load dynamic library '";
|
|
detailedError += utils::StringUtils::NativeStringToUtf8(libraryName);
|
|
detailedError += "' because of '";
|
|
detailedError += utils::Exception::FormatBaselibErrorState(errorState);
|
|
detailedError += "'.";
|
|
}
|
|
#else
|
|
NO_UNUSED_WARNING(detailedError);
|
|
#endif
|
|
return handle;
|
|
}
|
|
} /* namespace vm */
|
|
} /* namespace il2cpp */
|