xref: /oneTBB/src/tbb/dynamic_link.cpp (revision 4bb2faa0)
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