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