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