#include "dynamic_link.h"
#ifndef LIBRARY_ASSERT
#include "tbb/tbb_stddef.h"
#define LIBRARY_ASSERT(x,y) __TBB_ASSERT(x,y)
#endif
#include <cstdarg>
#if _WIN32||_WIN64
#include <malloc.h>
#else
#include <dlfcn.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#if __FreeBSD__ || __NetBSD__
#include <stdlib.h>
#else
#include <alloca.h>
#endif
#endif
#ifdef __TBB_BUILD
#include "tbb/atomic.h"
#endif
OPEN_INTERNAL_NAMESPACE
#if !defined(DYNAMIC_LINK_WARNING) && __TBB_DYNAMIC_LOAD_ENABLED
#define DYNAMIC_LINK_WARNING dynamic_link_warning
static void dynamic_link_warning( dynamic_link_error_t code, ... ) {
(void) code;
}
#endif
#if __TBB_DYNAMIC_LOAD_ENABLED
#if _WIN32 || _WIN64
http://www.microsoft.com/technet/security/advisory/2269637.mspx
static size_t abs_path( char const * name, char * path, size_t len ) {
HMODULE handle;
BOOL brc =
GetModuleHandleEx(
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
(LPCSTR)( & abs_path ),
& handle
);
if ( ! brc ) {
int err = GetLastError();
DYNAMIC_LINK_WARNING( dl_sys_fail, "GetModuleHandleEx", err );
return 0;
}
DWORD drc = GetModuleFileName( handle, path, static_cast< DWORD >( len ) );
if ( drc == 0 ) {
int err = GetLastError();
DYNAMIC_LINK_WARNING( dl_sys_fail, "GetModuleFileName", err );
return drc;
}
if ( drc >= len ) {
DYNAMIC_LINK_WARNING( dl_buff_too_small );
return drc;
}
char * backslash = path + drc;
LIBRARY_ASSERT( * backslash == 0, NULL );
while ( backslash > path && * backslash != '\\' ) {
-- backslash;
}
if ( backslash <= path ) {
return 0;
}
LIBRARY_ASSERT( * backslash == '\\', NULL );
size_t rc = ( backslash + 1 - path ) + strlen( name );
if ( rc >= len ) {
DYNAMIC_LINK_WARNING( dl_buff_too_small );
return rc + 1;
}
strcpy( backslash + 1, name );
LIBRARY_ASSERT( rc == strlen( path ), NULL );
return rc;
}
#else
class _abs_path {
char _path[PATH_MAX+1];
size_t _len;
enum {
ap_invalid = 0,
ap_only_cwd,
ap_ready
} _state;
bool prepare_full_path() {
LIBRARY_ASSERT( _state==ap_only_cwd, NULL );
Dl_info dlinfo;
int res = dladdr( (void*)&dynamic_unlink, &dlinfo );
if ( !res ) {
char const * err = dlerror();
DYNAMIC_LINK_WARNING( dl_sys_fail, "dladdr", err );
return false;
}
size_t fname_len = strlen( dlinfo.dli_fname );
while ( fname_len>0 && dlinfo.dli_fname[fname_len-1]!='/' ) fname_len-=1;
size_t rc;
if ( dlinfo.dli_fname[0]=='/' ) {
rc = 0;
_len = fname_len;
} else {
rc = _len;
_len += fname_len;
}
if ( fname_len>0 ) {
if ( _len>PATH_MAX ) {
DYNAMIC_LINK_WARNING( dl_buff_too_small );
return false;
}
memcpy( _path+rc, dlinfo.dli_fname, fname_len*sizeof(char) );
_path[_len]=0;
}
return true;
}
public:
_abs_path() {
if ( getcwd( _path, PATH_MAX+1 ) ) {
_state = ap_only_cwd;
_len = strlen( _path );
_path[_len++]='/';
} else {
DYNAMIC_LINK_WARNING( dl_buff_too_small );
_state = ap_invalid;
}
}
size_t operator ()( char const * name, char * path, size_t len ) {
switch ( _state ) {
case ap_only_cwd:
if ( !prepare_full_path() ) {
_state = ap_invalid;
return 0;
}
_state = ap_ready;
case ap_ready: {
size_t name_len = strlen( name );
size_t full_len = name_len+_len;
if ( full_len+_len < len ) {
memcpy( path, _path, _len );
memcpy( path+_len, name, name_len );
path[full_len] = 0;
}
return full_len;
}
default:
return 0;
}
}
};
_abs_path abs_path;
#endif
#endif
#if __TBB_WEAK_SYMBOLS
bool dynamic_link( dynamic_link_handle, const dynamic_link_descriptor descriptors[], size_t n, size_t required )
{
if ( required == ~(size_t)0 )
required = n;
LIBRARY_ASSERT( required<=n, "Number of required entry points exceeds their total number" );
size_t k = 0;
while ( k < required && descriptors[k].ptr )
++k;
if ( k < required )
return false;
for ( k = 0; k < n; ++k )
*descriptors[k].handler = (pointer_to_handler) descriptors[k].ptr;
return true;
}
#else
#if __TBB_DYNAMIC_LOAD_ENABLED
bool dynamic_link( dynamic_link_handle module, const dynamic_link_descriptor descriptors[], size_t n, size_t required )
{
LIBRARY_ASSERT( module != NULL, "Module handle is NULL" );
if ( module == NULL ) {
return false;
}
pointer_to_handler *h = (pointer_to_handler*)alloca(n * sizeof(pointer_to_handler));
if ( required == ~(size_t)0 )
required = n;
LIBRARY_ASSERT( required<=n, "Number of required entry points exceeds their total number" );
size_t k = 0;
for ( ; k < n; ++k ) {
dynamic_link_descriptor const & desc = descriptors[k];
#if _WIN32||_WIN64
FARPROC addr = GetProcAddress( module, desc.name );
if ( addr == NULL ) {
int err = GetLastError();
DYNAMIC_LINK_WARNING( dl_sym_not_found, desc.name, err );
}
h[k] = pointer_to_handler( addr );
#else
void * addr = dlsym( module, desc.name );
if ( addr == NULL ) {
char const * err = dlerror();
DYNAMIC_LINK_WARNING( dl_sym_not_found, desc.name, err );
}
(void *&)h[k] = addr;
#endif
if ( !h[k] && k < required )
return false;
}
LIBRARY_ASSERT( k == n, "if required entries are initialized, all entries are expected to be walked");
for( k = 0; k < n; ++k )
*descriptors[k].handler = h[k];
return true;
}
#else
bool dynamic_link( dynamic_link_handle, const dynamic_link_descriptor [], size_t, size_t )
{
return false;
}
#endif
#endif
void dynamic_unlink( dynamic_link_handle handle ) {
if ( handle ) {
#if __TBB_DYNAMIC_LOAD_ENABLED
#if _WIN32||_WIN64
FreeLibrary( handle );
#else
dlclose( handle );
#endif
(void)handle;
#endif
}
}
#if __TBB_BUILD
#define MAX_LOADED_MODULES 8
class handle_storage {
tbb::atomic<size_t> my_size;
dynamic_link_handle my_handles[MAX_LOADED_MODULES];
public:
handle_storage() {
my_size = 0;
}
void add_handle(dynamic_link_handle &handle) {
const size_t ind = my_size++;
LIBRARY_ASSERT( ind < MAX_LOADED_MODULES, "Too many modules are loaded" );
my_handles[ind] = handle;
}
void free_handles() {
const size_t size = my_size.fetch_and_store( 0 );
for (size_t i=0; i<size; ++i)
dynamic_unlink( my_handles[i] );
}
} handles;
#endif
bool dynamic_link( const char* library, const dynamic_link_descriptor descriptors[], size_t n, size_t required, dynamic_link_handle *handle ) {
#if ! __TBB_DYNAMIC_LOAD_ENABLED
dynamic_link_handle library_handle = NULL;
__TBB_ASSERT_EX( library, "library name must be provided");
#elif _WIN32||_WIN64
dynamic_link_handle library_handle = GetModuleHandle( library );
#else
dynamic_link_handle library_handle = dlopen( NULL, RTLD_LAZY );
#endif
if ( library_handle && dynamic_link( library_handle, descriptors, n, required ) ) {
#if !__TBB_DYNAMIC_LOAD_ENABLED
return true;
#else
#if _WIN32||_WIN64
char library_full_name[ MAX_PATH+1 ];
if ( GetModuleFileName( library_handle, library_full_name, MAX_PATH+1 ) ) {
library_handle = LoadLibrary( library_full_name );
if ( library_handle == NULL ) {
int err = GetLastError();
DYNAMIC_LINK_WARNING( dl_lib_not_found, library_full_name, err );
}
}
#else
Dl_info info;
if ( dladdr( (void*)*descriptors[0].handler, &info ) ) {
library_handle = dlopen( info.dli_fname, RTLD_LAZY );
if ( library_handle == NULL ) {
char const * err = dlerror();
DYNAMIC_LINK_WARNING( dl_lib_not_found, info.dli_fname, err );
}
}
#endif
else {
library_handle = 0;
}
if ( library_handle ) {
if ( !dynamic_link( library_handle, descriptors, n, required ) ) {
dynamic_unlink(library_handle);
library_handle = 0;
}
}
if ( !library_handle ) {
for( size_t i=0; i<n; ++i )
*descriptors[i].handler = 0;
}
#endif
} else {
library_handle = 0;
}
#if __TBB_DYNAMIC_LOAD_ENABLED
if ( !library_handle ) {
#if _WIN32||_WIN64
#if _XBOX
library_handle = LoadLibrary (library);
#else
library_handle = NULL;
size_t const len = MAX_PATH + 1;
char path[ len ];
size_t rc = abs_path( library, path, len );
if ( 0 < rc && rc < len ) {
UINT prev_mode = SetErrorMode (SEM_FAILCRITICALERRORS);
library_handle = LoadLibrary (path);
SetErrorMode (prev_mode);
if ( library_handle == NULL ) {
int err = GetLastError();
DYNAMIC_LINK_WARNING( dl_lib_not_found, path, err );
}
}
#endif
#else
library_handle = NULL;
size_t const len = PATH_MAX + 1;
char path[ len ];
size_t rc = abs_path( library, path, len );
if ( 0 < rc && rc < len ) {
library_handle = dlopen( path, RTLD_LAZY );
if ( library_handle == NULL ) {
char const * err = dlerror();
DYNAMIC_LINK_WARNING( dl_lib_not_found, library, err );
}
}
#endif
if( library_handle ) {
if( !dynamic_link( library_handle, descriptors, n, required ) ) {
dynamic_unlink( library_handle );
library_handle = NULL;
}
}
}
#endif
if ( library_handle ) {
if ( handle )
*handle = library_handle;
#if __TBB_BUILD
else
handles.add_handle( library_handle );
#endif
return true;
}
return false;
}
#if __TBB_BUILD
void dynamic_unlink_all() {
handles.free_handles();
}
#endif
CLOSE_INTERNAL_NAMESPACE