1 /* 2 Copyright (c) 2005-2021 Intel Corporation 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 #include "dynamic_link.h" 18 19 #include "oneapi/tbb/detail/_template_helpers.h" 20 #include "oneapi/tbb/detail/_utils.h" 21 22 /* 23 This file is used by both TBB and OpenMP RTL. Do not use __TBB_ASSERT() macro 24 and runtime_warning() function because they are not available in OpenMP. Use 25 __TBB_ASSERT_EX and DYNAMIC_LINK_WARNING instead. 26 */ 27 28 #include <cstdarg> // va_list etc. 29 #include <cstring> // strrchr 30 #if _WIN32 31 #include <malloc.h> 32 33 // Unify system calls 34 #define dlopen( name, flags ) LoadLibrary( name ) 35 #define dlsym( handle, name ) GetProcAddress( handle, name ) 36 #define dlclose( handle ) ( ! FreeLibrary( handle ) ) 37 #define dlerror() GetLastError() 38 #ifndef PATH_MAX 39 #define PATH_MAX MAX_PATH 40 #endif 41 #else /* _WIN32 */ 42 #include <dlfcn.h> 43 #include <unistd.h> 44 45 #include <climits> 46 #include <cstdlib> 47 #endif /* _WIN32 */ 48 49 #if __TBB_WEAK_SYMBOLS_PRESENT && !__TBB_DYNAMIC_LOAD_ENABLED 50 //TODO: use function attribute for weak symbols instead of the pragma. 51 #pragma weak dlopen 52 #pragma weak dlsym 53 #pragma weak dlclose 54 #endif /* __TBB_WEAK_SYMBOLS_PRESENT && !__TBB_DYNAMIC_LOAD_ENABLED */ 55 56 57 #define __USE_STATIC_DL_INIT ( !__ANDROID__ ) 58 59 60 /* 61 dynamic_link is a common interface for searching for required symbols in an 62 executable and dynamic libraries. 63 64 dynamic_link provides certain guarantees: 65 1. Either all or none of the requested symbols are resolved. Moreover, if 66 symbols are not resolved, the dynamic_link_descriptor table is not modified; 67 2. All returned symbols have secured lifetime: this means that none of them 68 can be invalidated until dynamic_unlink is called; 69 3. Any loaded library is loaded only via the full path. The full path is that 70 from which the runtime itself was loaded. (This is done to avoid security 71 issues caused by loading libraries from insecure paths). 72 73 dynamic_link searches for the requested symbols in three stages, stopping as 74 soon as all of the symbols have been resolved. 75 76 1. Search the global scope: 77 a. On Windows: dynamic_link tries to obtain the handle of the requested 78 library and if it succeeds it resolves the symbols via that handle. 79 b. On Linux: dynamic_link tries to search for the symbols in the global 80 scope via the main program handle. If the symbols are present in the global 81 scope their lifetime is not guaranteed (since dynamic_link does not know 82 anything about the library from which they are exported). Therefore it 83 tries to "pin" the symbols by obtaining the library name and reopening it. 84 dlopen may fail to reopen the library in two cases: 85 i. The symbols are exported from the executable. Currently dynamic _link 86 cannot handle this situation, so it will not find these symbols in this 87 step. 88 ii. The necessary library has been unloaded and cannot be reloaded. It 89 seems there is nothing that can be done in this case. No symbols are 90 returned. 91 92 2. Dynamic load: an attempt is made to load the requested library via the 93 full path. 94 The full path used is that from which the runtime itself was loaded. If the 95 library can be loaded, then an attempt is made to resolve the requested 96 symbols in the newly loaded library. 97 If the symbols are not found the library is unloaded. 98 99 3. Weak symbols: if weak symbols are available they are returned. 100 */ 101 102 namespace tbb { 103 namespace detail { 104 namespace r1 { 105 106 #if __TBB_WEAK_SYMBOLS_PRESENT || __TBB_DYNAMIC_LOAD_ENABLED 107 108 #if !defined(DYNAMIC_LINK_WARNING) && !__TBB_WIN8UI_SUPPORT && __TBB_DYNAMIC_LOAD_ENABLED 109 // Report runtime errors and continue. 110 #define DYNAMIC_LINK_WARNING dynamic_link_warning dynamic_link_warning(dynamic_link_error_t code,...)111 static void dynamic_link_warning( dynamic_link_error_t code, ... ) { 112 suppress_unused_warning(code); 113 } // library_warning 114 #endif /* !defined(DYNAMIC_LINK_WARNING) && !__TBB_WIN8UI_SUPPORT && __TBB_DYNAMIC_LOAD_ENABLED */ 115 resolve_symbols(dynamic_link_handle module,const dynamic_link_descriptor descriptors[],std::size_t required)116 static bool resolve_symbols( dynamic_link_handle module, const dynamic_link_descriptor descriptors[], std::size_t required ) 117 { 118 if ( !module ) 119 return false; 120 121 #if !__TBB_DYNAMIC_LOAD_ENABLED /* only __TBB_WEAK_SYMBOLS_PRESENT is defined */ 122 if ( !dlsym ) return false; 123 #endif /* !__TBB_DYNAMIC_LOAD_ENABLED */ 124 125 const std::size_t n_desc=20; // Usually we don't have more than 20 descriptors per library 126 __TBB_ASSERT_EX( required <= n_desc, "Too many descriptors is required" ); 127 if ( required > n_desc ) return false; 128 pointer_to_handler h[n_desc]; 129 130 for ( std::size_t k = 0; k < required; ++k ) { 131 dynamic_link_descriptor const & desc = descriptors[k]; 132 pointer_to_handler addr = (pointer_to_handler)dlsym( module, desc.name ); 133 if ( !addr ) { 134 return false; 135 } 136 h[k] = addr; 137 } 138 139 // Commit the entry points. 140 // Cannot use memset here, because the writes must be atomic. 141 for( std::size_t k = 0; k < required; ++k ) 142 *descriptors[k].handler = h[k]; 143 return true; 144 } 145 146 #if __TBB_WIN8UI_SUPPORT dynamic_link(const char * library,const dynamic_link_descriptor descriptors[],std::size_t required,dynamic_link_handle *,int flags)147 bool dynamic_link( const char* library, const dynamic_link_descriptor descriptors[], std::size_t required, dynamic_link_handle*, int flags ) { 148 dynamic_link_handle tmp_handle = NULL; 149 TCHAR wlibrary[256]; 150 if ( MultiByteToWideChar(CP_UTF8, 0, library, -1, wlibrary, 255) == 0 ) return false; 151 if ( flags & DYNAMIC_LINK_LOAD ) 152 tmp_handle = LoadPackagedLibrary( wlibrary, 0 ); 153 if (tmp_handle != NULL){ 154 return resolve_symbols(tmp_handle, descriptors, required); 155 }else{ 156 return false; 157 } 158 } dynamic_unlink(dynamic_link_handle)159 void dynamic_unlink( dynamic_link_handle ) {} dynamic_unlink_all()160 void dynamic_unlink_all() {} 161 #else 162 #if __TBB_DYNAMIC_LOAD_ENABLED 163 /* 164 There is a security issue on Windows: LoadLibrary() may load and execute malicious code. 165 See http://www.microsoft.com/technet/security/advisory/2269637.mspx for details. 166 To avoid the issue, we have to pass full path (not just library name) to LoadLibrary. This 167 function constructs full path to the specified library (it is assumed the library located 168 side-by-side with the tbb.dll. 169 170 The function constructs absolute path for given relative path. Important: Base directory is not 171 current one, it is the directory tbb.dll loaded from. 172 173 Example: 174 Let us assume "tbb.dll" is located in "c:\program files\common\intel\" directory, e.g. 175 absolute path of the library is "c:\program files\common\intel\tbb.dll". Absolute path for 176 "tbbmalloc.dll" would be "c:\program files\common\intel\tbbmalloc.dll". Absolute path for 177 "malloc\tbbmalloc.dll" would be "c:\program files\common\intel\malloc\tbbmalloc.dll". 178 */ 179 180 // Struct handle_storage is used by dynamic_link routine to store handles of 181 // all loaded or pinned dynamic libraries. When TBB is shut down, it calls 182 // dynamic_unlink_all() that unloads modules referenced by handle_storage. 183 // This struct should not have any constructors since it may be used before 184 // the constructor is called. 185 #define MAX_LOADED_MODULES 8 // The number of maximum possible modules which can be loaded 186 187 using atomic_incrementer = std::atomic<std::size_t>; 188 189 static struct handles_t { 190 atomic_incrementer my_size; 191 dynamic_link_handle my_handles[MAX_LOADED_MODULES]; 192 addtbb::detail::r1::handles_t193 void add(const dynamic_link_handle &handle) { 194 const std::size_t ind = my_size++; 195 __TBB_ASSERT_EX( ind < MAX_LOADED_MODULES, "Too many modules are loaded" ); 196 my_handles[ind] = handle; 197 } 198 freetbb::detail::r1::handles_t199 void free() { 200 const std::size_t size = my_size; 201 for (std::size_t i=0; i<size; ++i) 202 dynamic_unlink( my_handles[i] ); 203 } 204 } handles; 205 206 static std::once_flag init_dl_data_state; 207 208 static struct ap_data_t { 209 char _path[PATH_MAX+1]; 210 std::size_t _len; 211 } ap_data; 212 init_ap_data()213 static void init_ap_data() { 214 #if _WIN32 215 // Get handle of our DLL first. 216 HMODULE handle; 217 BOOL brc = GetModuleHandleEx( 218 GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, 219 (LPCSTR)( & dynamic_link ), // any function inside the library can be used for the address 220 & handle 221 ); 222 if ( !brc ) { // Error occurred. 223 int err = GetLastError(); 224 DYNAMIC_LINK_WARNING( dl_sys_fail, "GetModuleHandleEx", err ); 225 return; 226 } 227 // Now get path to our DLL. 228 DWORD drc = GetModuleFileName( handle, ap_data._path, static_cast< DWORD >( PATH_MAX ) ); 229 if ( drc == 0 ) { // Error occurred. 230 int err = GetLastError(); 231 DYNAMIC_LINK_WARNING( dl_sys_fail, "GetModuleFileName", err ); 232 return; 233 } 234 if ( drc >= PATH_MAX ) { // Buffer too short. 235 DYNAMIC_LINK_WARNING( dl_buff_too_small ); 236 return; 237 } 238 // Find the position of the last backslash. 239 char *backslash = std::strrchr( ap_data._path, '\\' ); 240 241 if ( !backslash ) { // Backslash not found. 242 __TBB_ASSERT_EX( backslash!=NULL, "Unbelievable."); 243 return; 244 } 245 __TBB_ASSERT_EX( backslash >= ap_data._path, "Unbelievable."); 246 ap_data._len = (std::size_t)(backslash - ap_data._path) + 1; 247 *(backslash+1) = 0; 248 #else 249 // Get the library path 250 Dl_info dlinfo; 251 int res = dladdr( (void*)&dynamic_link, &dlinfo ); // any function inside the library can be used for the address 252 if ( !res ) { 253 char const * err = dlerror(); 254 DYNAMIC_LINK_WARNING( dl_sys_fail, "dladdr", err ); 255 return; 256 } else { 257 __TBB_ASSERT_EX( dlinfo.dli_fname!=NULL, "Unbelievable." ); 258 } 259 260 char const *slash = std::strrchr( dlinfo.dli_fname, '/' ); 261 std::size_t fname_len=0; 262 if ( slash ) { 263 __TBB_ASSERT_EX( slash >= dlinfo.dli_fname, "Unbelievable."); 264 fname_len = (std::size_t)(slash - dlinfo.dli_fname) + 1; 265 } 266 267 std::size_t rc; 268 if ( dlinfo.dli_fname[0]=='/' ) { 269 // The library path is absolute 270 rc = 0; 271 ap_data._len = 0; 272 } else { 273 // The library path is relative so get the current working directory 274 if ( !getcwd( ap_data._path, sizeof(ap_data._path)/sizeof(ap_data._path[0]) ) ) { 275 DYNAMIC_LINK_WARNING( dl_buff_too_small ); 276 return; 277 } 278 ap_data._len = std::strlen( ap_data._path ); 279 ap_data._path[ap_data._len++]='/'; 280 rc = ap_data._len; 281 } 282 283 if ( fname_len>0 ) { 284 if ( ap_data._len>PATH_MAX ) { 285 DYNAMIC_LINK_WARNING( dl_buff_too_small ); 286 ap_data._len=0; 287 return; 288 } 289 std::strncpy( ap_data._path+rc, dlinfo.dli_fname, fname_len ); 290 ap_data._len += fname_len; 291 ap_data._path[ap_data._len]=0; 292 } 293 #endif /* _WIN32 */ 294 } 295 init_dl_data()296 static void init_dl_data() { 297 init_ap_data(); 298 } 299 300 /* 301 The function constructs absolute path for given relative path. Important: Base directory is not 302 current one, it is the directory libtbb.so loaded from. 303 304 Arguments: 305 in name -- Name of a file (may be with relative path; it must not be an absolute one). 306 out path -- Buffer to save result (absolute path) to. 307 in len -- Size of buffer. 308 ret -- 0 -- Error occurred. 309 > len -- Buffer too short, required size returned. 310 otherwise -- Ok, number of characters (incl. terminating null) written to buffer. 311 */ abs_path(char const * name,char * path,std::size_t len)312 static std::size_t abs_path( char const * name, char * path, std::size_t len ) { 313 if ( ap_data._len == 0 ) 314 return 0; 315 316 std::size_t name_len = std::strlen( name ); 317 std::size_t full_len = name_len+ap_data._len; 318 if ( full_len < len ) { 319 __TBB_ASSERT( ap_data._path[ap_data._len] == 0, NULL); 320 __TBB_ASSERT( std::strlen(ap_data._path) == ap_data._len, NULL); 321 std::strncpy( path, ap_data._path, ap_data._len + 1 ); 322 __TBB_ASSERT( path[ap_data._len] == 0, NULL ); 323 std::strncat( path, name, len - ap_data._len ); 324 __TBB_ASSERT( std::strlen(path) == full_len, NULL ); 325 } 326 return full_len+1; // +1 for null character 327 } 328 #endif // __TBB_DYNAMIC_LOAD_ENABLED init_dynamic_link_data()329 void init_dynamic_link_data() { 330 #if __TBB_DYNAMIC_LOAD_ENABLED 331 std::call_once( init_dl_data_state, init_dl_data ); 332 #endif 333 } 334 335 #if __USE_STATIC_DL_INIT 336 // ap_data structure is initialized with current directory on Linux. 337 // So it should be initialized as soon as possible since the current directory may be changed. 338 // static_init_ap_data object provides this initialization during library loading. 339 static struct static_init_dl_data_t { static_init_dl_data_ttbb::detail::r1::static_init_dl_data_t340 static_init_dl_data_t() { 341 init_dynamic_link_data(); 342 } 343 } static_init_dl_data; 344 #endif 345 346 #if __TBB_WEAK_SYMBOLS_PRESENT weak_symbol_link(const dynamic_link_descriptor descriptors[],std::size_t required)347 static bool weak_symbol_link( const dynamic_link_descriptor descriptors[], std::size_t required ) 348 { 349 // Check if the required entries are present in what was loaded into our process. 350 for ( std::size_t k = 0; k < required; ++k ) 351 if ( !descriptors[k].ptr ) 352 return false; 353 // Commit the entry points. 354 for ( std::size_t k = 0; k < required; ++k ) 355 *descriptors[k].handler = (pointer_to_handler) descriptors[k].ptr; 356 return true; 357 } 358 #else weak_symbol_link(const dynamic_link_descriptor[],std::size_t)359 static bool weak_symbol_link( const dynamic_link_descriptor[], std::size_t ) { 360 return false; 361 } 362 #endif /* __TBB_WEAK_SYMBOLS_PRESENT */ 363 dynamic_unlink(dynamic_link_handle handle)364 void dynamic_unlink( dynamic_link_handle handle ) { 365 #if !__TBB_DYNAMIC_LOAD_ENABLED /* only __TBB_WEAK_SYMBOLS_PRESENT is defined */ 366 if ( !dlclose ) return; 367 #endif 368 if ( handle ) { 369 dlclose( handle ); 370 } 371 } 372 dynamic_unlink_all()373 void dynamic_unlink_all() { 374 #if __TBB_DYNAMIC_LOAD_ENABLED 375 handles.free(); 376 #endif 377 } 378 global_symbols_link(const char * library,const dynamic_link_descriptor descriptors[],std::size_t required)379 static dynamic_link_handle global_symbols_link( const char* library, const dynamic_link_descriptor descriptors[], std::size_t required ) { 380 dynamic_link_handle library_handle{}; 381 #if _WIN32 382 auto res = GetModuleHandleEx(0, library, &library_handle); 383 __TBB_ASSERT_EX((res && library_handle) || (!res && !library_handle), nullptr); 384 #else /* _WIN32 */ 385 #if !__TBB_DYNAMIC_LOAD_ENABLED /* only __TBB_WEAK_SYMBOLS_PRESENT is defined */ 386 if ( !dlopen ) return 0; 387 #endif /* !__TBB_DYNAMIC_LOAD_ENABLED */ 388 // RTLD_GLOBAL - to guarantee that old TBB will find the loaded library 389 // RTLD_NOLOAD - not to load the library without the full path 390 library_handle = dlopen(library, RTLD_LAZY | RTLD_GLOBAL | RTLD_NOLOAD); 391 #endif /* _WIN32 */ 392 if (library_handle) { 393 if (!resolve_symbols(library_handle, descriptors, required)) { 394 dynamic_unlink(library_handle); 395 library_handle = nullptr; 396 } 397 } 398 return library_handle; 399 } 400 save_library_handle(dynamic_link_handle src,dynamic_link_handle * dst)401 static void save_library_handle( dynamic_link_handle src, dynamic_link_handle *dst ) { 402 __TBB_ASSERT_EX( src, "The library handle to store must be non-zero" ); 403 if ( dst ) 404 *dst = src; 405 #if __TBB_DYNAMIC_LOAD_ENABLED 406 else 407 handles.add( src ); 408 #endif /* __TBB_DYNAMIC_LOAD_ENABLED */ 409 } 410 411 #if !_WIN32 loading_flags(bool local_binding)412 int loading_flags(bool local_binding) { 413 int flags = RTLD_NOW; 414 if (local_binding) { 415 flags = flags | RTLD_LOCAL; 416 #if __linux__ && !__ANDROID__ && !__TBB_USE_SANITIZERS 417 flags = flags | RTLD_DEEPBIND; 418 #endif 419 } else { 420 flags = flags | RTLD_GLOBAL; 421 } 422 return flags; 423 } 424 #endif 425 dynamic_load(const char * library,const dynamic_link_descriptor descriptors[],std::size_t required,bool local_binding)426 dynamic_link_handle dynamic_load( const char* library, const dynamic_link_descriptor descriptors[], std::size_t required, bool local_binding ) { 427 ::tbb::detail::suppress_unused_warning( library, descriptors, required, local_binding ); 428 #if __TBB_DYNAMIC_LOAD_ENABLED 429 std::size_t const len = PATH_MAX + 1; 430 char path[ len ]; 431 std::size_t rc = abs_path( library, path, len ); 432 if ( 0 < rc && rc <= len ) { 433 #if _WIN32 434 // Prevent Windows from displaying silly message boxes if it fails to load library 435 // (e.g. because of MS runtime problems - one of those crazy manifest related ones) 436 UINT prev_mode = SetErrorMode (SEM_FAILCRITICALERRORS); 437 #endif /* _WIN32 */ 438 // The second argument (loading_flags) is ignored on Windows 439 dynamic_link_handle library_handle = dlopen( path, loading_flags(local_binding) ); 440 #if _WIN32 441 SetErrorMode (prev_mode); 442 #endif /* _WIN32 */ 443 if( library_handle ) { 444 if( !resolve_symbols( library_handle, descriptors, required ) ) { 445 // The loaded library does not contain all the expected entry points 446 dynamic_unlink( library_handle ); 447 library_handle = NULL; 448 } 449 } else 450 DYNAMIC_LINK_WARNING( dl_lib_not_found, path, dlerror() ); 451 return library_handle; 452 } else if ( rc>len ) 453 DYNAMIC_LINK_WARNING( dl_buff_too_small ); 454 // rc == 0 means failing of init_ap_data so the warning has already been issued. 455 456 #endif /* __TBB_DYNAMIC_LOAD_ENABLED */ 457 return 0; 458 } 459 dynamic_link(const char * library,const dynamic_link_descriptor descriptors[],std::size_t required,dynamic_link_handle * handle,int flags)460 bool dynamic_link( const char* library, const dynamic_link_descriptor descriptors[], std::size_t required, dynamic_link_handle *handle, int flags ) { 461 init_dynamic_link_data(); 462 463 // TODO: May global_symbols_link find weak symbols? 464 dynamic_link_handle library_handle = ( flags & DYNAMIC_LINK_GLOBAL ) ? global_symbols_link( library, descriptors, required ) : 0; 465 466 #if defined(_MSC_VER) && _MSC_VER <= 1900 467 #pragma warning (push) 468 // MSVC 2015 warning: 'int': forcing value to bool 'true' or 'false' 469 #pragma warning (disable: 4800) 470 #endif 471 if ( !library_handle && ( flags & DYNAMIC_LINK_LOAD ) ) 472 library_handle = dynamic_load( library, descriptors, required, flags & DYNAMIC_LINK_LOCAL ); 473 474 #if defined(_MSC_VER) && _MSC_VER <= 1900 475 #pragma warning (pop) 476 #endif 477 if ( !library_handle && ( flags & DYNAMIC_LINK_WEAK ) ) 478 return weak_symbol_link( descriptors, required ); 479 480 if ( library_handle ) { 481 save_library_handle( library_handle, handle ); 482 return true; 483 } 484 return false; 485 } 486 487 #endif /*__TBB_WIN8UI_SUPPORT*/ 488 #else /* __TBB_WEAK_SYMBOLS_PRESENT || __TBB_DYNAMIC_LOAD_ENABLED */ 489 bool dynamic_link( const char*, const dynamic_link_descriptor*, std::size_t, dynamic_link_handle *handle, int ) { 490 if ( handle ) 491 *handle=0; 492 return false; 493 } 494 void dynamic_unlink( dynamic_link_handle ) {} 495 void dynamic_unlink_all() {} 496 #endif /* __TBB_WEAK_SYMBOLS_PRESENT || __TBB_DYNAMIC_LOAD_ENABLED */ 497 498 } // namespace r1 499 } // namespace detail 500 } // namespace tbb 501