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