You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
479 lines
13 KiB
479 lines
13 KiB
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
|
file Copyright.txt or https://cmake.org/licensing#kwsys for details. */
|
|
#if defined(_WIN32)
|
|
# define NOMINMAX // hide min,max to not conflict with <limits>
|
|
#endif
|
|
|
|
#include "kwsysPrivate.h"
|
|
#include KWSYS_HEADER(DynamicLoader.hxx)
|
|
|
|
#include KWSYS_HEADER(Configure.hxx)
|
|
#include KWSYS_HEADER(Encoding.hxx)
|
|
|
|
// Work-around CMake dependency scanning limitation. This must
|
|
// duplicate the above list of headers.
|
|
#if 0
|
|
# include "Configure.hxx.in"
|
|
# include "DynamicLoader.hxx.in"
|
|
#endif
|
|
|
|
// This file actually contains several different implementations:
|
|
// * NOOP for environments without dynamic libs
|
|
// * HP machines which uses shl_load
|
|
// * Mac OS X 10.2.x and earlier which uses NSLinkModule
|
|
// * Windows which uses LoadLibrary
|
|
// * BeOS / Haiku
|
|
// * FreeMiNT for Atari
|
|
// * Default implementation for *NIX systems (including Mac OS X 10.3 and
|
|
// later) which use dlopen
|
|
//
|
|
// Each part of the ifdef contains a complete implementation for
|
|
// the static methods of DynamicLoader.
|
|
|
|
#define CHECK_OPEN_FLAGS(var, supported, ret) \
|
|
do { \
|
|
/* Check for unknown flags. */ \
|
|
if ((var & AllOpenFlags) != var) { \
|
|
return ret; \
|
|
} \
|
|
\
|
|
/* Check for unsupported flags. */ \
|
|
if ((var & (supported)) != var) { \
|
|
return ret; \
|
|
} \
|
|
} while (0)
|
|
|
|
namespace KWSYS_NAMESPACE {
|
|
|
|
DynamicLoader::LibraryHandle DynamicLoader::OpenLibrary(
|
|
const std::string& libname)
|
|
{
|
|
return DynamicLoader::OpenLibrary(libname, 0);
|
|
}
|
|
}
|
|
|
|
#if !KWSYS_SUPPORTS_SHARED_LIBS
|
|
// Implementation for environments without dynamic libs
|
|
# include <string.h> // for strerror()
|
|
|
|
namespace KWSYS_NAMESPACE {
|
|
|
|
DynamicLoader::LibraryHandle DynamicLoader::OpenLibrary(
|
|
const std::string& libname, int flags)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int DynamicLoader::CloseLibrary(DynamicLoader::LibraryHandle lib)
|
|
{
|
|
if (!lib) {
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
DynamicLoader::SymbolPointer DynamicLoader::GetSymbolAddress(
|
|
DynamicLoader::LibraryHandle lib, const std::string& sym)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
const char* DynamicLoader::LastError()
|
|
{
|
|
return "General error";
|
|
}
|
|
|
|
} // namespace KWSYS_NAMESPACE
|
|
|
|
#elif defined(__hpux)
|
|
// Implementation for HPUX machines
|
|
# include <dl.h>
|
|
# include <errno.h>
|
|
|
|
namespace KWSYS_NAMESPACE {
|
|
|
|
DynamicLoader::LibraryHandle DynamicLoader::OpenLibrary(
|
|
const std::string& libname, int flags)
|
|
{
|
|
CHECK_OPEN_FLAGS(flags, 0, 0);
|
|
|
|
return shl_load(libname.c_str(), BIND_DEFERRED | DYNAMIC_PATH, 0L);
|
|
}
|
|
|
|
int DynamicLoader::CloseLibrary(DynamicLoader::LibraryHandle lib)
|
|
{
|
|
if (!lib) {
|
|
return 0;
|
|
}
|
|
return !shl_unload(lib);
|
|
}
|
|
|
|
DynamicLoader::SymbolPointer DynamicLoader::GetSymbolAddress(
|
|
DynamicLoader::LibraryHandle lib, const std::string& sym)
|
|
{
|
|
void* addr;
|
|
int status;
|
|
|
|
/* TYPE_PROCEDURE Look for a function or procedure. (This used to be default)
|
|
* TYPE_DATA Look for a symbol in the data segment (for example,
|
|
* variables).
|
|
* TYPE_UNDEFINED Look for any symbol.
|
|
*/
|
|
status = shl_findsym(&lib, sym.c_str(), TYPE_UNDEFINED, &addr);
|
|
void* result = (status < 0) ? (void*)0 : addr;
|
|
|
|
// Hack to cast pointer-to-data to pointer-to-function.
|
|
return *reinterpret_cast<DynamicLoader::SymbolPointer*>(&result);
|
|
}
|
|
|
|
const char* DynamicLoader::LastError()
|
|
{
|
|
// TODO: Need implementation with errno/strerror
|
|
/* If successful, shl_findsym returns an integer (int) value zero. If
|
|
* shl_findsym cannot find sym, it returns -1 and sets errno to zero.
|
|
* If any other errors occur, shl_findsym returns -1 and sets errno to one
|
|
* of these values (defined in <errno.h>):
|
|
* ENOEXEC
|
|
* A format error was detected in the specified library.
|
|
* ENOSYM
|
|
* A symbol on which sym depends could not be found.
|
|
* EINVAL
|
|
* The specified handle is invalid.
|
|
*/
|
|
|
|
if (errno == ENOEXEC || errno == ENOSYM || errno == EINVAL) {
|
|
return strerror(errno);
|
|
}
|
|
// else
|
|
return 0;
|
|
}
|
|
|
|
} // namespace KWSYS_NAMESPACE
|
|
|
|
#elif defined(__APPLE__) && (MAC_OS_X_VERSION_MAX_ALLOWED < 1030)
|
|
// Implementation for Mac OS X 10.2.x and earlier
|
|
# include <mach-o/dyld.h>
|
|
# include <string.h> // for strlen
|
|
|
|
namespace KWSYS_NAMESPACE {
|
|
|
|
DynamicLoader::LibraryHandle DynamicLoader::OpenLibrary(
|
|
const std::string& libname, int flags)
|
|
{
|
|
CHECK_OPEN_FLAGS(flags, 0, 0);
|
|
|
|
NSObjectFileImageReturnCode rc;
|
|
NSObjectFileImage image = 0;
|
|
|
|
rc = NSCreateObjectFileImageFromFile(libname.c_str(), &image);
|
|
// rc == NSObjectFileImageInappropriateFile when trying to load a dylib file
|
|
if (rc != NSObjectFileImageSuccess) {
|
|
return 0;
|
|
}
|
|
NSModule handle = NSLinkModule(image, libname.c_str(),
|
|
NSLINKMODULE_OPTION_BINDNOW |
|
|
NSLINKMODULE_OPTION_RETURN_ON_ERROR);
|
|
NSDestroyObjectFileImage(image);
|
|
return handle;
|
|
}
|
|
|
|
int DynamicLoader::CloseLibrary(DynamicLoader::LibraryHandle lib)
|
|
{
|
|
// NSUNLINKMODULE_OPTION_KEEP_MEMORY_MAPPED
|
|
// With this option the memory for the module is not deallocated
|
|
// allowing pointers into the module to still be valid.
|
|
// You should use this option instead if your code experience some problems
|
|
// reported against Panther 10.3.9 (fixed in Tiger 10.4.2 and up)
|
|
bool success = NSUnLinkModule(lib, NSUNLINKMODULE_OPTION_NONE);
|
|
return success;
|
|
}
|
|
|
|
DynamicLoader::SymbolPointer DynamicLoader::GetSymbolAddress(
|
|
DynamicLoader::LibraryHandle lib, const std::string& sym)
|
|
{
|
|
void* result = 0;
|
|
// Need to prepend symbols with '_' on Apple-gcc compilers
|
|
std::string rsym = '_' + sym;
|
|
|
|
NSSymbol symbol = NSLookupSymbolInModule(lib, rsym.c_str());
|
|
if (symbol) {
|
|
result = NSAddressOfSymbol(symbol);
|
|
}
|
|
|
|
// Hack to cast pointer-to-data to pointer-to-function.
|
|
return *reinterpret_cast<DynamicLoader::SymbolPointer*>(&result);
|
|
}
|
|
|
|
const char* DynamicLoader::LastError()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
} // namespace KWSYS_NAMESPACE
|
|
|
|
#elif defined(_WIN32) && !defined(__CYGWIN__)
|
|
// Implementation for Windows win32 code but not cygwin
|
|
# include <windows.h>
|
|
|
|
# include <stdio.h>
|
|
|
|
namespace KWSYS_NAMESPACE {
|
|
|
|
DynamicLoader::LibraryHandle DynamicLoader::OpenLibrary(
|
|
const std::string& libname, int flags)
|
|
{
|
|
CHECK_OPEN_FLAGS(flags, SearchBesideLibrary, nullptr);
|
|
|
|
DWORD llFlags = 0;
|
|
if (flags & SearchBesideLibrary) {
|
|
llFlags |= LOAD_WITH_ALTERED_SEARCH_PATH;
|
|
}
|
|
|
|
return LoadLibraryExW(Encoding::ToWindowsExtendedPath(libname).c_str(),
|
|
nullptr, llFlags);
|
|
}
|
|
|
|
int DynamicLoader::CloseLibrary(DynamicLoader::LibraryHandle lib)
|
|
{
|
|
return (int)FreeLibrary(lib);
|
|
}
|
|
|
|
DynamicLoader::SymbolPointer DynamicLoader::GetSymbolAddress(
|
|
DynamicLoader::LibraryHandle lib, const std::string& sym)
|
|
{
|
|
// TODO: The calling convention affects the name of the symbol. We
|
|
// should have a tool to help get the symbol with the desired
|
|
// calling convention. Currently we assume cdecl.
|
|
//
|
|
// MSVC:
|
|
// __cdecl = "func" (default)
|
|
// __fastcall = "@_func@X"
|
|
// __stdcall = "_func@X"
|
|
//
|
|
// Note that the "@X" part of the name above is the total size (in
|
|
// bytes) of the arguments on the stack.
|
|
void* result;
|
|
const char* rsym = sym.c_str();
|
|
result = (void*)GetProcAddress(lib, rsym);
|
|
return *reinterpret_cast<DynamicLoader::SymbolPointer*>(&result);
|
|
}
|
|
|
|
# define DYNLOAD_ERROR_BUFFER_SIZE 1024
|
|
|
|
const char* DynamicLoader::LastError()
|
|
{
|
|
wchar_t lpMsgBuf[DYNLOAD_ERROR_BUFFER_SIZE + 1];
|
|
|
|
DWORD error = GetLastError();
|
|
DWORD length = FormatMessageW(
|
|
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, error,
|
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
|
|
lpMsgBuf, DYNLOAD_ERROR_BUFFER_SIZE, nullptr);
|
|
|
|
static char str[DYNLOAD_ERROR_BUFFER_SIZE + 1];
|
|
|
|
if (length < 1) {
|
|
/* FormatMessage failed. Use a default message. */
|
|
snprintf(str, DYNLOAD_ERROR_BUFFER_SIZE,
|
|
"DynamicLoader encountered error 0x%lX. "
|
|
"FormatMessage failed with error 0x%lX",
|
|
error, GetLastError());
|
|
return str;
|
|
}
|
|
|
|
if (!WideCharToMultiByte(CP_UTF8, 0, lpMsgBuf, -1, str,
|
|
DYNLOAD_ERROR_BUFFER_SIZE, nullptr, nullptr)) {
|
|
/* WideCharToMultiByte failed. Use a default message. */
|
|
snprintf(str, DYNLOAD_ERROR_BUFFER_SIZE,
|
|
"DynamicLoader encountered error 0x%lX. "
|
|
"WideCharToMultiByte failed with error 0x%lX",
|
|
error, GetLastError());
|
|
}
|
|
|
|
return str;
|
|
}
|
|
|
|
} // namespace KWSYS_NAMESPACE
|
|
|
|
#elif defined(__BEOS__)
|
|
// Implementation for BeOS / Haiku
|
|
# include <string.h> // for strerror()
|
|
|
|
# include <be/kernel/image.h>
|
|
# include <be/support/Errors.h>
|
|
|
|
namespace KWSYS_NAMESPACE {
|
|
|
|
static image_id last_dynamic_err = B_OK;
|
|
|
|
DynamicLoader::LibraryHandle DynamicLoader::OpenLibrary(
|
|
const std::string& libname, int flags)
|
|
{
|
|
CHECK_OPEN_FLAGS(flags, 0, 0);
|
|
|
|
// image_id's are integers, errors are negative. Add one just in case we
|
|
// get a valid image_id of zero (is that even possible?).
|
|
image_id rc = load_add_on(libname.c_str());
|
|
if (rc < 0) {
|
|
last_dynamic_err = rc;
|
|
return 0;
|
|
}
|
|
|
|
return rc + 1;
|
|
}
|
|
|
|
int DynamicLoader::CloseLibrary(DynamicLoader::LibraryHandle lib)
|
|
{
|
|
if (!lib) {
|
|
last_dynamic_err = B_BAD_VALUE;
|
|
return 0;
|
|
} else {
|
|
// The function dlclose() returns 0 on success, and non-zero on error.
|
|
status_t rc = unload_add_on(lib - 1);
|
|
if (rc != B_OK) {
|
|
last_dynamic_err = rc;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
DynamicLoader::SymbolPointer DynamicLoader::GetSymbolAddress(
|
|
DynamicLoader::LibraryHandle lib, const std::string& sym)
|
|
{
|
|
// Hack to cast pointer-to-data to pointer-to-function.
|
|
union
|
|
{
|
|
void* pvoid;
|
|
DynamicLoader::SymbolPointer psym;
|
|
} result;
|
|
|
|
result.psym = nullptr;
|
|
|
|
if (!lib) {
|
|
last_dynamic_err = B_BAD_VALUE;
|
|
} else {
|
|
// !!! FIXME: BeOS can do function-only lookups...does this ever
|
|
// !!! FIXME: actually _want_ a data symbol lookup, or was this union
|
|
// !!! FIXME: a leftover of dlsym()? (s/ANY/TEXT for functions only).
|
|
status_t rc =
|
|
get_image_symbol(lib - 1, sym.c_str(), B_SYMBOL_TYPE_ANY, &result.pvoid);
|
|
if (rc != B_OK) {
|
|
last_dynamic_err = rc;
|
|
result.psym = nullptr;
|
|
}
|
|
}
|
|
return result.psym;
|
|
}
|
|
|
|
const char* DynamicLoader::LastError()
|
|
{
|
|
const char* retval = strerror(last_dynamic_err);
|
|
last_dynamic_err = B_OK;
|
|
return retval;
|
|
}
|
|
|
|
} // namespace KWSYS_NAMESPACE
|
|
|
|
#elif defined(__MINT__)
|
|
// Implementation for FreeMiNT on Atari
|
|
# define _GNU_SOURCE /* for program_invocation_name */
|
|
# include <dld.h>
|
|
# include <errno.h>
|
|
# include <malloc.h>
|
|
# include <string.h>
|
|
|
|
namespace KWSYS_NAMESPACE {
|
|
|
|
DynamicLoader::LibraryHandle DynamicLoader::OpenLibrary(
|
|
const std::string& libname, int flags)
|
|
{
|
|
CHECK_OPEN_FLAGS(flags, 0, nullptr);
|
|
|
|
char* name = (char*)calloc(1, libname.size() + 1);
|
|
dld_init(program_invocation_name);
|
|
strncpy(name, libname.c_str(), libname.size());
|
|
dld_link(libname.c_str());
|
|
return (void*)name;
|
|
}
|
|
|
|
int DynamicLoader::CloseLibrary(DynamicLoader::LibraryHandle lib)
|
|
{
|
|
dld_unlink_by_file((char*)lib, 0);
|
|
free(lib);
|
|
return 0;
|
|
}
|
|
|
|
DynamicLoader::SymbolPointer DynamicLoader::GetSymbolAddress(
|
|
DynamicLoader::LibraryHandle lib, const std::string& sym)
|
|
{
|
|
// Hack to cast pointer-to-data to pointer-to-function.
|
|
union
|
|
{
|
|
void* pvoid;
|
|
DynamicLoader::SymbolPointer psym;
|
|
} result;
|
|
result.pvoid = dld_get_symbol(sym.c_str());
|
|
return result.psym;
|
|
}
|
|
|
|
const char* DynamicLoader::LastError()
|
|
{
|
|
return dld_strerror(dld_errno);
|
|
}
|
|
|
|
} // namespace KWSYS_NAMESPACE
|
|
|
|
#else
|
|
// Default implementation for *NIX systems (including Mac OS X 10.3 and
|
|
// later) which use dlopen
|
|
# include <dlfcn.h>
|
|
|
|
namespace KWSYS_NAMESPACE {
|
|
|
|
DynamicLoader::LibraryHandle DynamicLoader::OpenLibrary(
|
|
const std::string& libname, int flags)
|
|
{
|
|
CHECK_OPEN_FLAGS(flags, RTLDGlobal, nullptr);
|
|
|
|
int llFlags = RTLD_LAZY;
|
|
if (flags & RTLDGlobal) {
|
|
llFlags |= RTLD_GLOBAL;
|
|
}
|
|
|
|
return dlopen(libname.c_str(), llFlags);
|
|
}
|
|
|
|
int DynamicLoader::CloseLibrary(DynamicLoader::LibraryHandle lib)
|
|
{
|
|
if (lib) {
|
|
// The function dlclose() returns 0 on success, and non-zero on error.
|
|
return !dlclose(lib);
|
|
}
|
|
// else
|
|
return 0;
|
|
}
|
|
|
|
DynamicLoader::SymbolPointer DynamicLoader::GetSymbolAddress(
|
|
DynamicLoader::LibraryHandle lib, const std::string& sym)
|
|
{
|
|
// Hack to cast pointer-to-data to pointer-to-function.
|
|
union
|
|
{
|
|
void* pvoid;
|
|
DynamicLoader::SymbolPointer psym;
|
|
} result;
|
|
result.pvoid = dlsym(lib, sym.c_str());
|
|
return result.psym;
|
|
}
|
|
|
|
const char* DynamicLoader::LastError()
|
|
{
|
|
return dlerror();
|
|
}
|
|
|
|
} // namespace KWSYS_NAMESPACE
|
|
#endif
|