shared_library.hpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569
  1. // Copyright 2014 Renato Tegon Forti, Antony Polukhin.
  2. // Copyright Antony Polukhin, 2015-2024.
  3. //
  4. // Distributed under the Boost Software License, Version 1.0.
  5. // (See accompanying file LICENSE_1_0.txt
  6. // or copy at http://www.boost.org/LICENSE_1_0.txt)
  7. #ifndef BOOST_DLL_SHARED_LIBRARY_HPP
  8. #define BOOST_DLL_SHARED_LIBRARY_HPP
  9. /// \file boost/dll/shared_library.hpp
  10. /// \brief Contains the boost::dll::shared_library class, core class for all the
  11. /// DLL/DSO operations.
  12. #include <boost/dll/config.hpp>
  13. #include <boost/predef/os.h>
  14. #include <boost/core/enable_if.hpp>
  15. #include <boost/core/explicit_operator_bool.hpp>
  16. #include <boost/type_traits/is_member_pointer.hpp>
  17. #include <boost/dll/detail/system_error.hpp>
  18. #include <boost/dll/detail/aggressive_ptr_cast.hpp>
  19. #if BOOST_OS_WINDOWS
  20. # include <boost/dll/detail/windows/shared_library_impl.hpp>
  21. #else
  22. # include <boost/dll/detail/posix/shared_library_impl.hpp>
  23. #endif
  24. #ifdef BOOST_HAS_PRAGMA_ONCE
  25. # pragma once
  26. #endif
  27. namespace boost { namespace dll {
  28. /*!
  29. * \brief This class can be used to load a
  30. * Dynamic link libraries (DLL's) or Shared Libraries, also know
  31. * as dynamic shared objects (DSO's) and get their exported
  32. * symbols (functions and variables).
  33. *
  34. * shared_library instances share reference count to an actual loaded DLL/DSO, so it
  35. * is safe and memory efficient to have multiple instances of shared_library referencing the same DLL/DSO
  36. * even if those instances were loaded using different paths (relative + absolute) referencing the same object.
  37. *
  38. * On Linux/POSIX link with library "dl". "-fvisibility=hidden" flag is also recommended for use on Linux/POSIX.
  39. */
  40. class shared_library
  41. /// @cond
  42. : private boost::dll::detail::shared_library_impl
  43. /// @endcond
  44. {
  45. typedef boost::dll::detail::shared_library_impl base_t;
  46. BOOST_COPYABLE_AND_MOVABLE(shared_library)
  47. public:
  48. #ifdef BOOST_DLL_DOXYGEN
  49. typedef platform_specific native_handle_t;
  50. #else
  51. typedef shared_library_impl::native_handle_t native_handle_t;
  52. #endif
  53. /*!
  54. * Creates in anstance that does not reference any DLL/DSO.
  55. *
  56. * \post this->is_loaded() returns false.
  57. * \throw Nothing.
  58. */
  59. shared_library() BOOST_NOEXCEPT {}
  60. /*!
  61. * Copy constructor that increments the reference count of an underlying shared library.
  62. * Same as calling constructor with `lib.location()` parameter.
  63. *
  64. * \param lib A library to copy.
  65. * \post lib == *this
  66. * \throw \forcedlinkfs{system_error}, std::bad_alloc in case of insufficient memory.
  67. */
  68. shared_library(const shared_library& lib)
  69. : base_t()
  70. {
  71. assign(lib);
  72. }
  73. /*!
  74. * Copy constructor that increments the reference count of an underlying shared library.
  75. * Same as calling constructor with `lib.location(), ec` parameters.
  76. *
  77. * \param lib A shared library to copy.
  78. * \param ec Variable that will be set to the result of the operation.
  79. * \post lib == *this
  80. * \throw std::bad_alloc in case of insufficient memory.
  81. */
  82. shared_library(const shared_library& lib, boost::dll::fs::error_code& ec)
  83. : base_t()
  84. {
  85. assign(lib, ec);
  86. }
  87. /*!
  88. * Move constructor. Does not invalidate existing symbols and functions loaded from lib.
  89. *
  90. * \param lib A shared library to move from.
  91. * \post lib.is_loaded() returns false, this->is_loaded() return true.
  92. * \throw Nothing.
  93. */
  94. shared_library(BOOST_RV_REF(shared_library) lib) BOOST_NOEXCEPT
  95. : base_t(boost::move(static_cast<base_t&>(lib)))
  96. {}
  97. /*!
  98. * Loads a library by specified path with a specified mode.
  99. *
  100. * \param lib_path Library file name. Can handle std::string, const char*, std::wstring,
  101. * const wchar_t* or \forcedlinkfs{path}.
  102. * \param mode A mode that will be used on library load.
  103. * \throw \forcedlinkfs{system_error}, std::bad_alloc in case of insufficient memory.
  104. */
  105. explicit shared_library(const boost::dll::fs::path& lib_path, load_mode::type mode = load_mode::default_mode) {
  106. shared_library::load(lib_path, mode);
  107. }
  108. /*!
  109. * Loads a library by specified path with a specified mode.
  110. *
  111. * \param lib_path Library file name. Can handle std::string, const char*, std::wstring,
  112. * const wchar_t* or \forcedlinkfs{path}.
  113. * \param mode A mode that will be used on library load.
  114. * \param ec Variable that will be set to the result of the operation.
  115. * \throw std::bad_alloc in case of insufficient memory.
  116. */
  117. shared_library(const boost::dll::fs::path& lib_path, boost::dll::fs::error_code& ec, load_mode::type mode = load_mode::default_mode) {
  118. shared_library::load(lib_path, mode, ec);
  119. }
  120. //! \overload shared_library(const boost::dll::fs::path& lib_path, boost::dll::fs::error_code& ec, load_mode::type mode = load_mode::default_mode)
  121. shared_library(const boost::dll::fs::path& lib_path, load_mode::type mode, boost::dll::fs::error_code& ec) {
  122. shared_library::load(lib_path, mode, ec);
  123. }
  124. /*!
  125. * Assignment operator. If this->is_loaded() then calls this->unload(). Does not invalidate existing symbols and functions loaded from lib.
  126. *
  127. * \param lib A shared library to assign from.
  128. * \post lib == *this
  129. * \throw \forcedlinkfs{system_error}, std::bad_alloc in case of insufficient memory.
  130. */
  131. shared_library& operator=(BOOST_COPY_ASSIGN_REF(shared_library) lib) {
  132. boost::dll::fs::error_code ec;
  133. assign(lib, ec);
  134. if (ec) {
  135. boost::dll::detail::report_error(ec, "boost::dll::shared_library::operator= failed");
  136. }
  137. return *this;
  138. }
  139. /*!
  140. * Move assignment operator. If this->is_loaded() then calls this->unload(). Does not invalidate existing symbols and functions loaded from lib.
  141. *
  142. * \param lib A library to move from.
  143. * \post lib.is_loaded() returns false.
  144. * \throw Nothing.
  145. */
  146. shared_library& operator=(BOOST_RV_REF(shared_library) lib) BOOST_NOEXCEPT {
  147. if (lib.native() != native()) {
  148. swap(lib);
  149. }
  150. return *this;
  151. }
  152. /*!
  153. * Destroys the object by calling `unload()`. If library was loaded multiple times
  154. * by different instances, the actual DLL/DSO won't be unloaded until
  155. * there is at least one instance that references the DLL/DSO.
  156. *
  157. * \throw Nothing.
  158. */
  159. ~shared_library() BOOST_NOEXCEPT {}
  160. /*!
  161. * Makes *this share the same shared object as lib. If *this is loaded, then unloads it.
  162. *
  163. * \post lib.location() == this->location(), lib == *this
  164. * \param lib A library to copy.
  165. * \param ec Variable that will be set to the result of the operation.
  166. * \throw std::bad_alloc in case of insufficient memory.
  167. */
  168. shared_library& assign(const shared_library& lib, boost::dll::fs::error_code& ec) {
  169. ec.clear();
  170. if (native() == lib.native()) {
  171. return *this;
  172. }
  173. if (!lib) {
  174. unload();
  175. return *this;
  176. }
  177. boost::dll::fs::path loc = lib.location(ec);
  178. if (ec) {
  179. return *this;
  180. }
  181. shared_library copy(loc, ec);
  182. if (ec) {
  183. return *this;
  184. }
  185. swap(copy);
  186. return *this;
  187. }
  188. /*!
  189. * Makes *this share the same shared object as lib. If *this is loaded, then unloads it.
  190. *
  191. * \param lib A library instance to assign from.
  192. * \post lib.location() == this->location()
  193. * \throw \forcedlinkfs{system_error}, std::bad_alloc in case of insufficient memory.
  194. */
  195. shared_library& assign(const shared_library& lib) {
  196. boost::dll::fs::error_code ec;
  197. assign(lib, ec);
  198. if (ec) {
  199. boost::dll::detail::report_error(ec, "boost::dll::shared_library::assign() failed");
  200. }
  201. return *this;
  202. }
  203. /*!
  204. * Loads a library by specified path with a specified mode.
  205. *
  206. * Note that if some library is already loaded in this instance, load will
  207. * call unload() and then load the new provided library.
  208. *
  209. * \param lib_path Library file name. Can handle std::string, const char*, std::wstring,
  210. * const wchar_t* or \forcedlinkfs{path}.
  211. * \param mode A mode that will be used on library load.
  212. * \throw \forcedlinkfs{system_error}, std::bad_alloc in case of insufficient memory.
  213. *
  214. */
  215. void load(const boost::dll::fs::path& lib_path, load_mode::type mode = load_mode::default_mode) {
  216. boost::dll::fs::error_code ec;
  217. base_t::load(lib_path, mode, ec);
  218. if (ec) {
  219. boost::dll::detail::report_error(ec, "boost::dll::shared_library::load() failed");
  220. }
  221. }
  222. /*!
  223. * Loads a library by specified path with a specified mode.
  224. *
  225. * Note that if some library is already loaded in this instance, load will
  226. * call unload() and then load the new provided library.
  227. *
  228. * \param lib_path Library file name. Can handle std::string, const char*, std::wstring,
  229. * const wchar_t* or \forcedlinkfs{path}.
  230. * \param ec Variable that will be set to the result of the operation.
  231. * \param mode A mode that will be used on library load.
  232. * \throw std::bad_alloc in case of insufficient memory.
  233. */
  234. void load(const boost::dll::fs::path& lib_path, boost::dll::fs::error_code& ec, load_mode::type mode = load_mode::default_mode) {
  235. ec.clear();
  236. base_t::load(lib_path, mode, ec);
  237. }
  238. //! \overload void load(const boost::dll::fs::path& lib_path, boost::dll::fs::error_code& ec, load_mode::type mode = load_mode::default_mode)
  239. void load(const boost::dll::fs::path& lib_path, load_mode::type mode, boost::dll::fs::error_code& ec) {
  240. ec.clear();
  241. base_t::load(lib_path, mode, ec);
  242. }
  243. /*!
  244. * Unloads a shared library. If library was loaded multiple times
  245. * by different instances, the actual DLL/DSO won't be unloaded until
  246. * there is at least one instance that references the DLL/DSO.
  247. *
  248. * \post this->is_loaded() returns false.
  249. * \throw Nothing.
  250. */
  251. void unload() BOOST_NOEXCEPT {
  252. base_t::unload();
  253. }
  254. /*!
  255. * Check if an library is loaded.
  256. *
  257. * \return true if a library has been loaded.
  258. * \throw Nothing.
  259. */
  260. bool is_loaded() const BOOST_NOEXCEPT {
  261. return base_t::is_loaded();
  262. }
  263. /*!
  264. * Check if an library is not loaded.
  265. *
  266. * \return true if a library has not been loaded.
  267. * \throw Nothing.
  268. */
  269. bool operator!() const BOOST_NOEXCEPT {
  270. return !is_loaded();
  271. }
  272. /*!
  273. * Check if an library is loaded.
  274. *
  275. * \return true if a library has been loaded.
  276. * \throw Nothing.
  277. */
  278. BOOST_EXPLICIT_OPERATOR_BOOL()
  279. /*!
  280. * Search for a given symbol on loaded library. Works for all symbols, including alias names.
  281. *
  282. * \param symbol_name Null-terminated symbol name. Can handle std::string, char*, const char*.
  283. * \return `true` if the loaded library contains a symbol with a given name.
  284. * \throw Nothing.
  285. */
  286. bool has(const char* symbol_name) const BOOST_NOEXCEPT {
  287. boost::dll::fs::error_code ec;
  288. return is_loaded() && !!base_t::symbol_addr(symbol_name, ec) && !ec;
  289. }
  290. //! \overload bool has(const char* symbol_name) const
  291. bool has(const std::string& symbol_name) const BOOST_NOEXCEPT {
  292. return has(symbol_name.c_str());
  293. }
  294. /*!
  295. * Returns reference to the symbol (function or variable) with the given name from the loaded library.
  296. * This call will always succeed and throw nothing if call to `has(const char* )`
  297. * member function with the same symbol name returned `true`.
  298. *
  299. * \b Example:
  300. * \code
  301. * int& i0 = lib.get<int>("integer_name");
  302. * int& i1 = *lib.get<int*>("integer_alias_name");
  303. * \endcode
  304. *
  305. * \tparam T Type of the symbol that we are going to import. Must be explicitly specified.
  306. * \param symbol_name Null-terminated symbol name. Can handle std::string, char*, const char*.
  307. * \return Reference to the symbol.
  308. * \throw \forcedlinkfs{system_error} if symbol does not exist or if the DLL/DSO was not loaded.
  309. */
  310. template <typename T>
  311. inline typename boost::enable_if_c<boost::is_member_pointer<T>::value || boost::is_reference<T>::value, T>::type get(const std::string& symbol_name) const {
  312. return get<T>(symbol_name.c_str());
  313. }
  314. //! \overload T& get(const std::string& symbol_name) const
  315. template <typename T>
  316. inline typename boost::disable_if_c<boost::is_member_pointer<T>::value || boost::is_reference<T>::value, T&>::type get(const std::string& symbol_name) const {
  317. return get<T>(symbol_name.c_str());
  318. }
  319. //! \overload T& get(const std::string& symbol_name) const
  320. template <typename T>
  321. inline typename boost::enable_if_c<boost::is_member_pointer<T>::value || boost::is_reference<T>::value, T>::type get(const char* symbol_name) const {
  322. return boost::dll::detail::aggressive_ptr_cast<T>(
  323. get_void(symbol_name)
  324. );
  325. }
  326. //! \overload T& get(const std::string& symbol_name) const
  327. template <typename T>
  328. inline typename boost::disable_if_c<boost::is_member_pointer<T>::value || boost::is_reference<T>::value, T&>::type get(const char* symbol_name) const {
  329. return *boost::dll::detail::aggressive_ptr_cast<T*>(
  330. get_void(symbol_name)
  331. );
  332. }
  333. /*!
  334. * Returns a symbol (function or variable) from a shared library by alias name of the symbol.
  335. *
  336. * \b Example:
  337. * \code
  338. * int& i = lib.get_alias<int>("integer_alias_name");
  339. * \endcode
  340. *
  341. * \tparam T Type of the symbol that we are going to import. Must be explicitly specified..
  342. * \param alias_name Null-terminated alias symbol name. Can handle std::string, char*, const char*.
  343. * \throw \forcedlinkfs{system_error} if symbol does not exist or if the DLL/DSO was not loaded.
  344. */
  345. template <typename T>
  346. inline T& get_alias(const char* alias_name) const {
  347. return *get<T*>(alias_name);
  348. }
  349. //! \overload T& get_alias(const char* alias_name) const
  350. template <typename T>
  351. inline T& get_alias(const std::string& alias_name) const {
  352. return *get<T*>(alias_name.c_str());
  353. }
  354. private:
  355. /// @cond
  356. // get_void is required to reduce binary size: it does not depend on a template
  357. // parameter and will be instantiated only once.
  358. void* get_void(const char* sb) const {
  359. boost::dll::fs::error_code ec;
  360. if (!is_loaded()) {
  361. ec = boost::dll::fs::make_error_code(
  362. boost::dll::fs::errc::bad_file_descriptor
  363. );
  364. // report_error() calls dlsym, do not use it here!
  365. boost::throw_exception(
  366. boost::dll::fs::system_error(
  367. ec, "boost::dll::shared_library::get() failed: no library was loaded"
  368. )
  369. );
  370. }
  371. void* const ret = base_t::symbol_addr(sb, ec);
  372. if (ec || !ret) {
  373. boost::dll::detail::report_error(ec, "boost::dll::shared_library::get() failed");
  374. }
  375. return ret;
  376. }
  377. /// @endcond
  378. public:
  379. /*!
  380. * Returns the native handler of the loaded library.
  381. *
  382. * \return Platform-specific handle.
  383. */
  384. native_handle_t native() const BOOST_NOEXCEPT {
  385. return base_t::native();
  386. }
  387. /*!
  388. * Returns full path and name of this shared object.
  389. *
  390. * \b Example:
  391. * \code
  392. * shared_library lib("test_lib.dll");
  393. * filesystem::path full_path = lib.location(); // C:\Windows\System32\test_lib.dll
  394. * \endcode
  395. *
  396. * \return Full path to the shared library.
  397. * \throw \forcedlinkfs{system_error}, std::bad_alloc.
  398. */
  399. boost::dll::fs::path location() const {
  400. boost::dll::fs::error_code ec;
  401. if (!is_loaded()) {
  402. ec = boost::dll::fs::make_error_code(
  403. boost::dll::fs::errc::bad_file_descriptor
  404. );
  405. boost::throw_exception(
  406. boost::dll::fs::system_error(
  407. ec, "boost::dll::shared_library::location() failed (no library was loaded)"
  408. )
  409. );
  410. }
  411. boost::dll::fs::path full_path = base_t::full_module_path(ec);
  412. if (ec) {
  413. boost::dll::detail::report_error(ec, "boost::dll::shared_library::location() failed");
  414. }
  415. return full_path;
  416. }
  417. /*!
  418. * Returns full path and name of shared module.
  419. *
  420. * \b Example:
  421. * \code
  422. * shared_library lib("test_lib.dll");
  423. * filesystem::path full_path = lib.location(); // C:\Windows\System32\test_lib.dll
  424. * \endcode
  425. *
  426. * \param ec Variable that will be set to the result of the operation.
  427. * \return Full path to the shared library.
  428. * \throw std::bad_alloc.
  429. */
  430. boost::dll::fs::path location(boost::dll::fs::error_code& ec) const {
  431. if (!is_loaded()) {
  432. ec = boost::dll::fs::make_error_code(
  433. boost::dll::fs::errc::bad_file_descriptor
  434. );
  435. return boost::dll::fs::path();
  436. }
  437. ec.clear();
  438. return base_t::full_module_path(ec);
  439. }
  440. /*!
  441. * Returns suffix of shared module:
  442. * in a call to load() or the constructor/load.
  443. *
  444. * \return The suffix od shared module: ".dll" (Windows), ".so" (Unix/Linux/BSD), ".dylib" (MacOS/IOS)
  445. */
  446. static boost::dll::fs::path suffix() {
  447. return base_t::suffix();
  448. }
  449. /*!
  450. * Returns the decorated path to a shared module name, i.e. with needed prefix/suffix added.
  451. *
  452. * \b Recommendations: Use `load` with `load_mode::append_decorations` instead of constructing the decorated path via `decorate()` and loading by it.
  453. *
  454. * For instance, for a path like "path/to/boost" it returns :
  455. * - path/to/libboost.so on posix platforms
  456. * - path/to/libboost.dylib on OSX
  457. * - path/to/boost.dll on Windows
  458. *
  459. * Method handles both relative and absolute paths.
  460. *
  461. * - Windows note: `decorate()` does not prepend "lib" to the decorated path. Use `load` with `load_mode::append_decorations` for MinGW compatibility purpose.
  462. * - Posix note: if the initial module name is already prepended with lib, only the suffix() is appended to the path
  463. *
  464. * \param sl the module name and path to decorate - for instance : /usr/lib/boost
  465. *
  466. * \return The decorated unportable path that may not exists in the filesystem or could be wrong due to platform specifics.
  467. */
  468. static boost::dll::fs::path decorate(const boost::dll::fs::path& sl) {
  469. return base_t::decorate(sl);
  470. }
  471. /*!
  472. * Swaps two libraries. Does not invalidate existing symbols and functions loaded from libraries.
  473. *
  474. * \param rhs Library to swap with.
  475. * \throw Nothing.
  476. */
  477. void swap(shared_library& rhs) BOOST_NOEXCEPT {
  478. base_t::swap(rhs);
  479. }
  480. };
  481. /// Very fast equality check that compares the actual DLL/DSO objects. Throws nothing.
  482. inline bool operator==(const shared_library& lhs, const shared_library& rhs) BOOST_NOEXCEPT {
  483. return lhs.native() == rhs.native();
  484. }
  485. /// Very fast inequality check that compares the actual DLL/DSO objects. Throws nothing.
  486. inline bool operator!=(const shared_library& lhs, const shared_library& rhs) BOOST_NOEXCEPT {
  487. return lhs.native() != rhs.native();
  488. }
  489. /// Compare the actual DLL/DSO objects without any guarantee to be stable between runs. Throws nothing.
  490. inline bool operator<(const shared_library& lhs, const shared_library& rhs) BOOST_NOEXCEPT {
  491. return lhs.native() < rhs.native();
  492. }
  493. /// Swaps two shared libraries. Does not invalidate symbols and functions loaded from libraries. Throws nothing.
  494. inline void swap(shared_library& lhs, shared_library& rhs) BOOST_NOEXCEPT {
  495. lhs.swap(rhs);
  496. }
  497. }} // boost::dll
  498. #endif // BOOST_DLL_SHARED_LIBRARY_HPP