1 // Copyright Vladimir Prus 2004.
2 // Copyright (c) 2005-2018 Hartmut Kaiser
3 // Distributed under the Boost Software License, Version 1.0.
4 // (See accompanying file LICENSE_1_0.txt
5 // or copy at http://www.boost.org/LICENSE_1_0.txt)
6 
7 // hpxinspect:nodeprecatedinclude:boost/shared_ptr.hpp
8 // hpxinspect:nodeprecatedname:boost::shared_ptr
9 
10 #ifndef HPX_DLL_DLOPEN_HPP_VP_2004_08_24
11 #define HPX_DLL_DLOPEN_HPP_VP_2004_08_24
12 
13 #include <hpx/config.hpp>
14 #include <hpx/compat/mutex.hpp>
15 #include <hpx/error_code.hpp>
16 #include <hpx/throw_exception.hpp>
17 #include <hpx/util/assert.hpp>
18 #include <hpx/util/plugin/config.hpp>
19 
20 #include <boost/filesystem/convenience.hpp>
21 #include <boost/filesystem/path.hpp>
22 #include <boost/shared_ptr.hpp>
23 
24 #include <iostream>
25 #include <memory>
26 #include <mutex>
27 #include <sstream>
28 #include <stdexcept>
29 #include <string>
30 #include <type_traits>
31 #include <utility>
32 
33 #if !defined(__ANDROID__) && !defined(ANDROID) && !defined(__APPLE__)
34 #include <link.h>
35 #endif
36 #if defined(__APPLE__)
37 #include <mach-o/dyld.h>
38 #endif
39 #include <dlfcn.h>
40 #include <limits.h>
41 
42 #if !defined(HPX_HAS_DLOPEN)
43 #error "This file shouldn't be included directly, use the file hpx/util/plugin/dll.hpp only."
44 #endif
45 
46 #if !defined(_WIN32)
47 typedef void* HMODULE;
48 #else
49 typedef struct HINSTANCE__* HMODULE;
50 #endif
51 
52 ///////////////////////////////////////////////////////////////////////////////
53 #if !defined(RTLD_LOCAL)
54 #define RTLD_LOCAL 0        // some systems do not have RTLD_LOCAL
55 #endif
56 #if !defined(RTLD_DEEPBIND)
57 #define RTLD_DEEPBIND 0     // some systems do not have RTLD_DEEPBIND
58 #endif
59 
60 ///////////////////////////////////////////////////////////////////////////////
61 #define MyFreeLibrary(x)      dlclose (x)
62 #define MyLoadLibrary(x) \
63       reinterpret_cast<HMODULE>(dlopen(x, RTLD_GLOBAL | RTLD_LAZY))
64 #define MyGetProcAddress(x,y) dlsym   (x, y)
65 
66 ///////////////////////////////////////////////////////////////////////////////
67 namespace hpx { namespace util { namespace plugin {
68 
69     namespace very_detail
70     {
71         template <typename TO, typename FROM>
72         TO nasty_cast(FROM f)
73         {
74             union {
75                 FROM f; TO t;
76             } u;
77             u.f = f;
78             return u.t;
79         }
80     }
81 
82     ///////////////////////////////////////////////////////////////////////////
83     class dll
84     {
85     protected:
86         ///////////////////////////////////////////////////////////////////////
init_library(HMODULE)87         static void init_library(HMODULE)
88         {
89 #if defined(__AIX__) && defined(__GNUC__)
90             dlerror();              // Clear the error state.
91             typedef void (*init_proc_type)();
92             init_proc_type init_proc =
93                 (init_proc_type)MyGetProcAddress(dll_hand, "_GLOBAL__DI");
94             if (init_proc)
95                 init_proc();
96 #endif
97         }
98 
deinit_library(HMODULE)99         static void deinit_library(HMODULE)
100         {
101 #if defined(__AIX__) && defined(__GNUC__)
102             dlerror();              // Clear the error state.
103             typedef void (*free_proc_type)();
104             free_proc_type free_proc =
105                 (free_proc_type)MyGetProcAddress(dll_hand, "_GLOBAL__DD");
106             if (free_proc)
107                 free_proc();
108 #endif
109         }
110 
111         ///////////////////////////////////////////////////////////////////////
112         template<typename T>
113         struct free_dll
114         {
free_dllhpx::util::plugin::dll::free_dll115             free_dll(HMODULE h, std::shared_ptr<compat::recursive_mutex> mtx)
116               : h_(h), mtx_(mtx)
117             {
118             }
119 
operator ()hpx::util::plugin::dll::free_dll120             void operator()(T)
121             {
122                 if (nullptr != h_)
123                 {
124                     std::lock_guard<compat::recursive_mutex> lock(*mtx_);
125 
126                     dll::deinit_library(h_);
127                     dlerror();
128                     MyFreeLibrary(h_);
129                 }
130             }
131 
132             HMODULE h_;
133             std::shared_ptr<compat::recursive_mutex> mtx_;
134         };
135         template <typename T> friend struct free_dll;
136 
137     public:
dll()138         dll()
139         :   dll_handle (nullptr)
140           , mtx_(mutex_instance())
141         {}
142 
dll(dll const & rhs)143         dll(dll const& rhs)
144           : dll_name(rhs.dll_name), map_name(rhs.map_name), dll_handle(nullptr)
145           , mtx_(rhs.mtx_)
146         {}
147 
dll(std::string const & name)148         dll(std::string const& name)
149           : dll_name(name), map_name(""), dll_handle(nullptr)
150           , mtx_(mutex_instance())
151         {
152             // map_name defaults to dll base name
153             namespace fs = boost::filesystem;
154 
155 #if BOOST_FILESYSTEM_VERSION == 2
156             fs::path dll_path(dll_name, fs::native);
157 #else
158             fs::path dll_path(dll_name);
159 #endif
160             map_name = fs::basename(dll_path);
161         }
162 
load_library(error_code & ec=throws)163         void load_library(error_code& ec = throws)
164         {
165             LoadLibrary(ec);
166         }
167 
dll(std::string const & libname,std::string const & mapname)168         dll(std::string const& libname, std::string const& mapname)
169         :   dll_name(libname), map_name(mapname), dll_handle(nullptr)
170           , mtx_(mutex_instance())
171         {}
172 
dll(dll && rhs)173         dll(dll && rhs)
174           : dll_name(std::move(rhs.dll_name))
175           , map_name(std::move(rhs.map_name))
176           , dll_handle(rhs.dll_handle)
177           , mtx_(std::move(rhs.mtx_))
178         {
179             rhs.dll_handle = nullptr;
180         }
181 
operator =(dll const & rhs)182         dll &operator=(dll const & rhs)
183         {
184             if (this != &rhs) {
185             //  free any existing dll_handle
186                 FreeLibrary();
187 
188             //  load the library for this instance of the dll class
189                 dll_name = rhs.dll_name;
190                 map_name = rhs.map_name;
191                 mtx_ = rhs.mtx_;
192                 LoadLibrary();
193             }
194             return *this;
195         }
196 
operator =(dll && rhs)197         dll &operator=(dll && rhs)
198         {
199             if (&rhs != this) {
200                 dll_name = std::move(rhs.dll_name);
201                 map_name = std::move(rhs.map_name);
202                 dll_handle = rhs.dll_handle;
203                 rhs.dll_handle = nullptr;
204                 mtx_ = std::move(rhs.mtx_);
205             }
206             return *this;
207         }
208 
~dll()209         ~dll()
210         {
211             FreeLibrary();
212         }
213 
get_name() const214         std::string get_name() const { return dll_name; }
get_mapname() const215         std::string get_mapname() const { return map_name; }
216 
217         template<typename SymbolType, typename Deleter>
218         std::pair<SymbolType, Deleter>
get(std::string const & symbol_name,error_code & ec=throws) const219         get(std::string const& symbol_name, error_code& ec = throws) const
220         {
221             const_cast<dll&>(*this).LoadLibrary(ec);
222             // make sure everything is initialized
223             if (ec) return std::pair<SymbolType, Deleter>();
224 
225             std::unique_lock<compat::recursive_mutex> lock(*mtx_);
226 
227             static_assert(
228                 std::is_pointer<SymbolType>::value,
229                 "std::is_pointer<SymbolType>::value");
230 
231             SymbolType address = very_detail::nasty_cast<SymbolType>(
232                 MyGetProcAddress(dll_handle, symbol_name.c_str()));
233             if (nullptr == address)
234             {
235                 std::ostringstream str;
236                 str << "Hpx.Plugin: Unable to locate the exported symbol name '"
237                     << symbol_name << "' in the shared library '"
238                     << dll_name << "' (dlerror: " << dlerror () << ")";
239 
240                 dlerror();
241 
242                 lock.unlock();
243 
244                 // report error
245                 HPX_THROWS_IF(ec, dynamic_link_failure,
246                     "plugin::get", str.str());
247                 return std::pair<SymbolType, Deleter>();
248             }
249 
250             // Open the library. Yes, we do it on every access to
251             // a symbol, the LoadLibrary function increases the refcnt of the dll
252             // so in the end the dll class holds one refcnt and so does every
253             // symbol.
254 
255             dlerror();              // Clear the error state.
256             HMODULE handle = MyLoadLibrary(
257                 (dll_name.empty() ? nullptr : dll_name.c_str()));
258             if (!handle) {
259                 std::ostringstream str;
260                 str << "Hpx.Plugin: Could not open shared library '"
261                     << dll_name << "' (dlerror: " << dlerror() << ")";
262 
263                 lock.unlock();
264 
265                 // report error
266                 HPX_THROWS_IF(ec, filesystem_error,
267                     "plugin::get", str.str());
268                 return std::pair<SymbolType, Deleter>();
269             }
270 
271 #if !defined(__AIX__)
272             // AIX seems to return different handle values for the second and
273             // any following call
274             HPX_ASSERT(handle == dll_handle);
275 #endif
276 
277             init_library(handle);   // initialize library
278 
279             // Cast to the right type.
280             dlerror();              // Clear the error state.
281 
282             return std::make_pair(address, free_dll<SymbolType>(handle, mtx_));
283         }
284 
keep_alive(error_code & ec=throws)285         void keep_alive(error_code& ec = throws)
286         {
287             LoadLibrary(ec, true);
288         }
289 
290     protected:
LoadLibrary(error_code & ec=throws,bool force=false)291         void LoadLibrary(error_code& ec = throws, bool force = false)
292         {
293             if (!dll_handle || force) {
294                 std::unique_lock<compat::recursive_mutex> lock(*mtx_);
295 
296                 ::dlerror();                // Clear the error state.
297                 dll_handle = MyLoadLibrary(
298                     (dll_name.empty() ? nullptr : dll_name.c_str()));
299                 // std::cout << "open\n";
300                 if (!dll_handle) {
301                     std::ostringstream str;
302                     str << "Hpx.Plugin: Could not open shared library '"
303                         << dll_name << "' (dlerror: " << dlerror() << ")";
304 
305                     lock.unlock();
306 
307                     HPX_THROWS_IF(ec, filesystem_error,
308                         "plugin::LoadLibrary", str.str());
309                     return;
310                 }
311 
312                 init_library(dll_handle);   // initialize library
313             }
314 
315             if (&ec != &throws)
316                 ec = make_success_code();
317         }
318 
319     public:
get_directory(error_code & ec=throws) const320         std::string get_directory(error_code& ec = throws) const
321         {
322             // now find the full path of the loaded library
323             using boost::filesystem::path;
324             std::string result;
325 
326 #if !defined(__ANDROID__) && !defined(ANDROID) && !defined(__APPLE__)
327             char directory[PATH_MAX] = { '\0' };
328             const_cast<dll&>(*this).LoadLibrary(ec);
329             if (!ec && ::dlinfo(dll_handle, RTLD_DI_ORIGIN, directory) < 0) {
330                 std::ostringstream str;
331                 str << "Hpx.Plugin: Could not extract path the shared "
332                        "library '" << dll_name << "' has been loaded from "
333                        "(dlerror: " << dlerror() << ")";
334 
335                 HPX_THROWS_IF(ec, filesystem_error,
336                     "plugin::get_directory",
337                     str.str());
338             }
339             result = directory;
340             ::dlerror();                // Clear the error state.
341 #elif defined(__APPLE__)
342             // SO staticfloat's solution
343             const_cast<dll&>(*this).LoadLibrary(ec);
344             if (ec)
345             {
346                 // iterate through all images currently in memory
347                 for (size_t i = 0; i < ::_dyld_image_count(); ++i)
348                 {
349                     if (const char* image_name = ::_dyld_get_image_name(i))
350                     {
351                         HMODULE probe_handle = ::dlopen(image_name, RTLD_NOW);
352                         ::dlclose(probe_handle);
353 
354                         // If the handle is the same as what was passed in
355                         // (modulo mode bits), return this image name
356                         if (((intptr_t)dll_handle & (-4)) ==
357                             ((intptr_t)probe_handle & (-4)))
358                         {
359                             result = path(image_name).parent_path().string();
360                             std::cout << "found directory: "
361                                       << result << std::endl;
362                             break;
363                         }
364                     }
365                 }
366             }
367             ::dlerror();                // Clear the error state.
368 #endif
369             if (&ec != &throws)
370                 ec = make_success_code();
371 
372             return result;
373         }
374 
375     protected:
FreeLibrary()376         void FreeLibrary()
377         {
378             if (nullptr != dll_handle)
379             {
380                 std::lock_guard<compat::recursive_mutex> lock(*mtx_);
381 
382                 deinit_library(dll_handle);
383                 dlerror();
384                 MyFreeLibrary(dll_handle);
385             }
386         }
387 
388         // protect access to dl... functions
mutex_instance()389         static std::shared_ptr<compat::recursive_mutex> mutex_instance()
390         {
391             static std::shared_ptr<compat::recursive_mutex> mutex =
392                 std::make_shared<compat::recursive_mutex>();
393             return mutex;
394         }
395 
396     private:
397         std::string dll_name;
398         std::string map_name;
399         HMODULE dll_handle;
400         std::shared_ptr<compat::recursive_mutex> mtx_;
401     };
402 
403 ///////////////////////////////////////////////////////////////////////////////
404 }}}
405 
406 #endif
407 
408