199 lines
5.9 KiB
C++
199 lines
5.9 KiB
C++
// Copyright 2023 Intel Corporation
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
#include "module.h"
|
|
#include "context.h"
|
|
|
|
#if !defined(_WIN32)
|
|
#include <dlfcn.h>
|
|
#include <fstream>
|
|
#endif
|
|
|
|
OIDN_NAMESPACE_BEGIN
|
|
|
|
ModuleLoader::ModuleLoader()
|
|
{
|
|
// Get the path of the current module
|
|
const Path path = getModulePath();
|
|
|
|
// Remove the filename from the path
|
|
const size_t lastPathSep = path.find_last_of(pathSeps);
|
|
if (lastPathSep == Path::npos)
|
|
throw std::runtime_error("could not get absolute path of module directory");
|
|
modulePathPrefix = path.substr(0, lastPathSep + 1);
|
|
}
|
|
|
|
bool ModuleLoader::load(const std::string& name)
|
|
{
|
|
if (modules.find(name) != modules.end())
|
|
return true; // module already loaded
|
|
|
|
// Get the path of the module to load
|
|
std::string filename = OIDN_LIBRARY_NAME "_" + name;
|
|
#if defined(_WIN32)
|
|
filename += ".dll";
|
|
#else
|
|
#if defined(OIDN_LIBRARY_VERSIONED)
|
|
const std::string versionStr = "." OIDN_VERSION_STRING_SHORT;
|
|
#else
|
|
const std::string versionStr = "";
|
|
#endif
|
|
#if defined(__APPLE__)
|
|
filename = "lib" + filename + versionStr + ".dylib";
|
|
#else
|
|
filename = "lib" + filename + ".so" + versionStr;
|
|
#endif
|
|
#endif
|
|
|
|
const Path path = modulePathPrefix + Path(filename.begin(), filename.end());
|
|
|
|
// Load the module
|
|
#if defined(_WIN32)
|
|
// Prevent the system from displaying a message box when the module fails to load
|
|
UINT prevErrorMode = GetErrorMode();
|
|
SetErrorMode(prevErrorMode | SEM_FAILCRITICALERRORS);
|
|
void* module = LoadLibraryW(path.c_str());
|
|
SetErrorMode(prevErrorMode);
|
|
#else
|
|
void* module = dlopen(path.c_str(), RTLD_NOW | RTLD_LOCAL);
|
|
#endif
|
|
if (module == nullptr)
|
|
return false;
|
|
|
|
// Get the address of the module init function
|
|
const std::string initSymbol =
|
|
(OIDN_TO_STRING(OIDN_NAMESPACE_C) "_init_module_") + name + ("_v" OIDN_TO_STRING(OIDN_VERSION));
|
|
void* initAddress = getSymbolAddress(module, initSymbol);
|
|
if (initAddress == nullptr)
|
|
{
|
|
Context::get().printWarning("invalid module: '" + filename + "'");
|
|
closeModule(module);
|
|
return false;
|
|
}
|
|
|
|
// Call the module init function
|
|
auto initFunc = reinterpret_cast<void (*)()>(initAddress);
|
|
initFunc();
|
|
|
|
// The module has been loaded successfully.
|
|
// We won't unload the module manually to avoid issues due to the undefined module unloading
|
|
// and static object destruction order at process exit. This intentional "leak" is fine
|
|
// because the modules are owned by the context which is static, and the modules will be
|
|
// unloaded at process exit anyway. Thus we don't need the module handle anymore.
|
|
modules.insert(name);
|
|
|
|
Context::get().printDebug("Loaded module: '" + filename + "'");
|
|
return true;
|
|
}
|
|
|
|
void* ModuleLoader::getSymbolAddress(void* module, const std::string& name)
|
|
{
|
|
#if defined(_WIN32)
|
|
return reinterpret_cast<void*>(GetProcAddress(static_cast<HMODULE>(module), name.c_str()));
|
|
#else
|
|
return dlsym(module, name.c_str());
|
|
#endif
|
|
}
|
|
|
|
void ModuleLoader::closeModule(void* module)
|
|
{
|
|
#if defined(_WIN32)
|
|
FreeLibrary(static_cast<HMODULE>(module));
|
|
#else
|
|
dlclose(module);
|
|
#endif
|
|
}
|
|
|
|
ModuleLoader::Path ModuleLoader::getModulePath(void* address)
|
|
{
|
|
if (address == nullptr)
|
|
address = reinterpret_cast<void*>(&getModulePath); // any other function in this module would work
|
|
|
|
#if defined(_WIN32)
|
|
|
|
// Get the handle of the module which contains the address
|
|
HMODULE module;
|
|
const DWORD flags = GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
|
|
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT;
|
|
if (!GetModuleHandleExA(flags, reinterpret_cast<LPCSTR>(address), &module))
|
|
throw std::runtime_error("GetModuleHandleExA failed");
|
|
|
|
// Get the path of the module
|
|
// Since we don't know the length of the path, we use a buffer of increasing size
|
|
DWORD pathSize = MAX_PATH + 1;
|
|
for (; ;)
|
|
{
|
|
std::vector<wchar_t> path(pathSize);
|
|
DWORD result = GetModuleFileNameW(module, path.data(), pathSize);
|
|
if (result == 0)
|
|
throw std::runtime_error("GetModuleFileNameW failed");
|
|
else if (result < pathSize)
|
|
return path.data();
|
|
else
|
|
pathSize *= 2;
|
|
}
|
|
|
|
#else
|
|
|
|
// dladdr should return an absolute path on Linux except for the main executable
|
|
// On macOS it should always return an absolute path
|
|
Dl_info info;
|
|
if (dladdr(address, &info))
|
|
{
|
|
// Check whether the path is absolute
|
|
if (info.dli_fname && info.dli_fname[0] == '/')
|
|
return info.dli_fname;
|
|
}
|
|
|
|
#if defined(__APPLE__)
|
|
// This shouldn't happen
|
|
throw std::runtime_error("failed to get absolute path with dladdr");
|
|
#else
|
|
// We failed to get an absolute path, so we try to parse /proc/self/maps
|
|
std::ifstream file("/proc/self/maps");
|
|
if (!file)
|
|
throw std::runtime_error("could not open /proc/self/maps");
|
|
|
|
// Parse the lines
|
|
for (std::string lineStr; std::getline(file, lineStr); )
|
|
{
|
|
std::istringstream line(lineStr);
|
|
|
|
// Parse the address range
|
|
uintptr_t addressBegin, addressEnd;
|
|
line >> std::hex;
|
|
line >> addressBegin;
|
|
if (line.get() != '-')
|
|
continue; // parse error
|
|
line >> addressEnd;
|
|
if (!isspace(line.peek()) || !line)
|
|
continue; // parse error
|
|
|
|
// Check whether the address is in this range
|
|
if (reinterpret_cast<uintptr_t>(address) < addressBegin ||
|
|
reinterpret_cast<uintptr_t>(address) >= addressEnd)
|
|
continue;
|
|
|
|
// Skip the permissions, offset, device, inode
|
|
std::string str;
|
|
for (int i = 0; i < 4; ++i)
|
|
line >> str;
|
|
|
|
// Parse the path
|
|
line >> std::ws;
|
|
if (!std::getline(line, str))
|
|
continue; // no path or parse error
|
|
|
|
// Check whether the path is absolute
|
|
if (str[0] == '/')
|
|
return str;
|
|
}
|
|
|
|
throw std::runtime_error("could not find address in /proc/self/maps");
|
|
#endif
|
|
|
|
#endif
|
|
}
|
|
|
|
|
|
OIDN_NAMESPACE_END |