#include "il2cpp-config.h"
|
|
#if IL2CPP_TARGET_POSIX
|
|
//#define VERBOSE_OUTPUT
|
|
#ifdef VERBOSE_OUTPUT
|
#include <stdio.h>
|
#endif
|
|
#include <dlfcn.h>
|
#include <string>
|
#include <set>
|
#if IL2CPP_TARGET_LINUX
|
#include <unistd.h>
|
#include <gnu/lib-names.h>
|
#endif
|
|
#include "il2cpp-runtime-metadata.h"
|
#include "os/LibraryLoader.h"
|
#include "os/Mutex.h"
|
#include "utils/PathUtils.h"
|
#include "utils/StringUtils.h"
|
#include "utils/Environment.h"
|
|
namespace il2cpp
|
{
|
namespace os
|
{
|
#if !IL2CPP_TINY_WITHOUT_DEBUGGER
|
static std::set<void*> s_NativeHandlesOpen;
|
typedef std::set<void*>::const_iterator OpenHandleIterator;
|
os::FastMutex s_NativeHandlesOpenMutex;
|
#endif
|
|
struct LibraryNamePrefixAndSuffix
|
{
|
LibraryNamePrefixAndSuffix(const char* prefix_, const char* suffix_)
|
{
|
prefix = std::string(prefix_);
|
suffix = std::string(suffix_);
|
}
|
|
std::string prefix;
|
std::string suffix;
|
};
|
|
static LibraryNamePrefixAndSuffix LibraryNamePrefixAndSuffixVariations[8] =
|
{
|
LibraryNamePrefixAndSuffix("", ".so"),
|
LibraryNamePrefixAndSuffix("", ".dll"),
|
LibraryNamePrefixAndSuffix("", ".dylib"),
|
LibraryNamePrefixAndSuffix("", ".bundle"),
|
LibraryNamePrefixAndSuffix("lib", ".so"),
|
LibraryNamePrefixAndSuffix("lib", ".dll"),
|
LibraryNamePrefixAndSuffix("lib", ".dylib"),
|
LibraryNamePrefixAndSuffix("lib", ".bundle")
|
};
|
|
// Note that testing this code can be a bit difficult, since dlopen will cache
|
// the values it returns, and we don't call dlcose from the C# level. See the
|
// comments in the integration test code for more details.
|
|
static void* LoadLibraryWithName(const char* name, int flags)
|
{
|
#ifdef VERBOSE_OUTPUT
|
printf("Trying name: %s\n", name);
|
#endif
|
|
void* handle = NULL;
|
#if IL2CPP_TARGET_IOS
|
std::string dirName;
|
if (utils::Environment::GetNumMainArgs() > 0)
|
{
|
std::string main = utils::StringUtils::Utf16ToUtf8(utils::Environment::GetMainArgs()[0]);
|
dirName = utils::PathUtils::DirectoryName(main);
|
}
|
|
std::string libPath = utils::StringUtils::Printf("%s/%s", dirName.c_str(), name);
|
handle = dlopen(libPath.c_str(), flags);
|
|
// Fallback to just using the name. This might be a system dylib.
|
if (handle == NULL)
|
handle = dlopen(name, flags);
|
#else
|
handle = dlopen(name, flags);
|
#endif
|
|
if (handle != NULL)
|
return handle;
|
|
#ifdef VERBOSE_OUTPUT
|
printf("Error: %s\n", dlerror());
|
#endif
|
|
return NULL;
|
}
|
|
#if IL2CPP_TARGET_LINUX
|
static void* LoadLibraryRelativeToExecutableDirectory(const char* name, int flags)
|
{
|
if (name == NULL || name[0] == '/')
|
return NULL;
|
|
char exePath[PATH_MAX + 1];
|
int len;
|
|
if ((len = readlink("/proc/self/exe", exePath, sizeof(exePath))) == -1)
|
{
|
#ifdef VERBOSE_OUTPUT
|
printf("Error: %s\n", perror());
|
#endif
|
return NULL;
|
}
|
exePath[len] = '\0'; // readlink does not terminate buffer
|
while (len > 0 && exePath[len] != '/')
|
len--;
|
exePath[len] = '\0';
|
std::string libPath = utils::StringUtils::Printf("%s/%s", exePath, name);
|
return dlopen(libPath.c_str(), flags);
|
}
|
|
#endif
|
|
static void* CheckLibraryVariations(const char* name, int flags)
|
{
|
int numberOfVariations = sizeof(LibraryNamePrefixAndSuffixVariations) / sizeof(LibraryNamePrefixAndSuffixVariations[0]);
|
for (int i = 0; i < numberOfVariations; ++i)
|
{
|
std::string libraryName = LibraryNamePrefixAndSuffixVariations[i].prefix + name + LibraryNamePrefixAndSuffixVariations[i].suffix;
|
void* handle = LoadLibraryWithName(libraryName.c_str(), flags);
|
if (handle != NULL)
|
return handle;
|
#if IL2CPP_TARGET_LINUX
|
// Linux does not search current directory by default
|
handle = LoadLibraryRelativeToExecutableDirectory(libraryName.c_str(), flags);
|
if (handle != NULL)
|
return handle;
|
#endif
|
}
|
|
return NULL;
|
}
|
|
Il2CppMethodPointer LibraryLoader::GetHardcodedPInvokeDependencyFunctionPointer(const il2cpp::utils::StringView<Il2CppNativeChar>& nativeDynamicLibrary, const il2cpp::utils::StringView<char>& entryPoint)
|
{
|
return NULL;
|
}
|
|
void* LibraryLoader::LoadDynamicLibrary(const utils::StringView<Il2CppNativeChar>& nativeDynamicLibrary)
|
{
|
return LoadDynamicLibrary(nativeDynamicLibrary, RTLD_LAZY);
|
}
|
|
void* LibraryLoader::LoadDynamicLibrary(const utils::StringView<Il2CppNativeChar>& nativeDynamicLibrary, int flags)
|
{
|
#ifdef VERBOSE_OUTPUT
|
printf("Attempting to load dynamic library: %s\n", nativeDynamicLibrary.Str());
|
#endif
|
|
if (nativeDynamicLibrary.IsEmpty())
|
return LoadLibraryWithName(NULL, flags);
|
|
#if IL2CPP_TARGET_LINUX
|
// Workaround the fact that on Linux, libc is actually named libc.so.6 instead of libc.so.
|
// mscorlib P/Invokes into plain libc, so we need this for those P/Invokes to succeed
|
if (strncasecmp(nativeDynamicLibrary.Str(), "libc", nativeDynamicLibrary.Length()) == 0)
|
return LoadLibraryWithName(LIBC_SO, flags);
|
#endif
|
|
StringViewAsNullTerminatedStringOf(char, nativeDynamicLibrary, libraryName);
|
void* handle = LoadLibraryWithName(libraryName, flags);
|
|
if (handle == NULL)
|
handle = CheckLibraryVariations(libraryName, flags);
|
|
if (handle == NULL)
|
{
|
size_t lengthWithoutDotDll = nativeDynamicLibrary.Length() - 4;
|
if (strncmp(libraryName + lengthWithoutDotDll, ".dll", 4) == 0)
|
{
|
char* nativeDynamicLibraryWithoutExtension = static_cast<char*>(alloca((lengthWithoutDotDll + 1) * sizeof(char)));
|
memcpy(nativeDynamicLibraryWithoutExtension, libraryName, lengthWithoutDotDll);
|
nativeDynamicLibraryWithoutExtension[lengthWithoutDotDll] = 0;
|
|
handle = CheckLibraryVariations(nativeDynamicLibraryWithoutExtension, flags);
|
}
|
}
|
|
#if !IL2CPP_TINY_WITHOUT_DEBUGGER
|
os::FastAutoLock lock(&s_NativeHandlesOpenMutex);
|
if (handle != NULL)
|
s_NativeHandlesOpen.insert(handle);
|
#endif
|
|
return handle;
|
}
|
|
Il2CppMethodPointer LibraryLoader::GetFunctionPointer(void* dynamicLibrary, const PInvokeArguments& pinvokeArgs)
|
{
|
StringViewAsNullTerminatedStringOf(char, pinvokeArgs.entryPoint, entryPoint);
|
#if IL2CPP_TINY_WITHOUT_DEBUGGER
|
return reinterpret_cast<Il2CppMethodPointer>(dlsym(dynamicLibrary, entryPoint));
|
#else
|
|
if (pinvokeArgs.isNoMangle)
|
return reinterpret_cast<Il2CppMethodPointer>(dlsym(dynamicLibrary, entryPoint));
|
|
const size_t kBufferOverhead = 10;
|
void* functionPtr = NULL;
|
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';
|
functionPtr = dlsym(dynamicLibrary, functionName.c_str() + 1);
|
if (functionPtr != NULL)
|
return reinterpret_cast<Il2CppMethodPointer>(functionPtr);
|
|
// If charset specific function lookup failed, try with original name
|
functionPtr = dlsym(dynamicLibrary, entryPoint);
|
}
|
else
|
{
|
functionPtr = dlsym(dynamicLibrary, entryPoint);
|
if (functionPtr != NULL)
|
return reinterpret_cast<Il2CppMethodPointer>(functionPtr);
|
|
// If original name function lookup failed, try with mangled name
|
functionName[originalFuncNameLength] = 'A';
|
functionPtr = dlsym(dynamicLibrary, functionName.c_str() + 1);
|
}
|
|
return reinterpret_cast<Il2CppMethodPointer>(functionPtr);
|
#endif
|
}
|
|
Il2CppMethodPointer LibraryLoader::GetFunctionPointer(void* dynamicLibrary, const char* functionName)
|
{
|
#ifdef VERBOSE_OUTPUT
|
printf("Attempting to load method at entry point: %s\n", functionName);
|
#endif
|
|
Il2CppMethodPointer method = reinterpret_cast<Il2CppMethodPointer>(dlsym(dynamicLibrary, functionName));
|
|
#ifdef VERBOSE_OUTPUT
|
if (method == NULL)
|
printf("Error: %s\n", dlerror());
|
#endif
|
|
return method;
|
}
|
|
void LibraryLoader::CleanupLoadedLibraries()
|
{
|
#if !IL2CPP_TINY_WITHOUT_DEBUGGER
|
os::FastAutoLock lock(&s_NativeHandlesOpenMutex);
|
for (OpenHandleIterator it = s_NativeHandlesOpen.begin(); it != s_NativeHandlesOpen.end(); it++)
|
{
|
dlclose(*it);
|
}
|
#endif
|
}
|
|
bool LibraryLoader::CloseLoadedLibrary(void*& dynamicLibrary)
|
{
|
if (dynamicLibrary == NULL)
|
return false;
|
|
#if !IL2CPP_TINY_WITHOUT_DEBUGGER
|
os::FastAutoLock lock(&s_NativeHandlesOpenMutex);
|
OpenHandleIterator it = s_NativeHandlesOpen.find(dynamicLibrary);
|
if (it != s_NativeHandlesOpen.end())
|
{
|
dlclose(*it);
|
s_NativeHandlesOpen.erase(it);
|
return true;
|
}
|
return false;
|
#else
|
return true;
|
#endif
|
}
|
}
|
}
|
|
#endif
|