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