1 // Copyright 2014 Renato Tegon Forti, Antony Polukhin.
2 // Copyright 2015-2019 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_RUNTIME_SYMBOL_INFO_HPP
9 #define BOOST_DLL_RUNTIME_SYMBOL_INFO_HPP
10 
11 #include <boost/dll/config.hpp>
12 #include <boost/predef/os.h>
13 #include <boost/predef/compiler/visualc.h>
14 #include <boost/dll/detail/aggressive_ptr_cast.hpp>
15 #if BOOST_OS_WINDOWS
16 #   include <boost/winapi/dll.hpp>
17 #   include <boost/dll/detail/windows/path_from_handle.hpp>
18 #else
19 #   include <dlfcn.h>
20 #   include <boost/dll/detail/posix/program_location_impl.hpp>
21 #endif
22 
23 #ifdef BOOST_HAS_PRAGMA_ONCE
24 # pragma once
25 #endif
26 
27 /// \file boost/dll/runtime_symbol_info.hpp
28 /// \brief Provides methods for getting acceptable by boost::dll::shared_library location of symbol, source line or program.
29 namespace boost { namespace dll {
30 
31 #if BOOST_OS_WINDOWS
32 namespace detail {
program_location_impl(boost::dll::fs::error_code & ec)33     inline boost::dll::fs::path program_location_impl(boost::dll::fs::error_code& ec) {
34         return boost::dll::detail::path_from_handle(NULL, ec);
35     }
36 } // namespace detail
37 #endif
38 
39     /*!
40     * On success returns full path and name to the binary object that holds symbol pointed by ptr_to_symbol.
41     *
42     * \param ptr_to_symbol Pointer to symbol which location is to be determined.
43     * \param ec Variable that will be set to the result of the operation.
44     * \return Path to the binary object that holds symbol or empty path in case error.
45     * \throws std::bad_alloc in case of insufficient memory. Overload that does not accept \forcedlinkfs{error_code} also throws \forcedlinkfs{system_error}.
46     *
47     * \b Examples:
48     * \code
49     * int main() {
50     *    dll::symbol_location_ptr(std::set_terminate(0));       // returns "/some/path/libmy_terminate_handler.so"
51     *    dll::symbol_location_ptr(::signal(SIGSEGV, SIG_DFL));  // returns "/some/path/libmy_symbol_handler.so"
52     * }
53     * \endcode
54     */
55     template <class T>
symbol_location_ptr(T ptr_to_symbol,boost::dll::fs::error_code & ec)56     inline boost::dll::fs::path symbol_location_ptr(T ptr_to_symbol, boost::dll::fs::error_code& ec) {
57         BOOST_STATIC_ASSERT_MSG(boost::is_pointer<T>::value, "boost::dll::symbol_location_ptr works only with pointers! `ptr_to_symbol` must be a pointer");
58         boost::dll::fs::path ret;
59         if (!ptr_to_symbol) {
60             ec = boost::dll::fs::make_error_code(
61                 boost::dll::fs::errc::bad_address
62             );
63 
64             return ret;
65         }
66         ec.clear();
67 
68         const void* ptr = boost::dll::detail::aggressive_ptr_cast<const void*>(ptr_to_symbol);
69 
70 #if BOOST_OS_WINDOWS
71         boost::winapi::MEMORY_BASIC_INFORMATION_ mbi;
72         if (!boost::winapi::VirtualQuery(ptr, &mbi, sizeof(mbi))) {
73             ec = boost::dll::detail::last_error_code();
74             return ret;
75         }
76 
77         return boost::dll::detail::path_from_handle(reinterpret_cast<boost::winapi::HMODULE_>(mbi.AllocationBase), ec);
78 #else
79         Dl_info info;
80 
81         // Some of the libc headers miss `const` in `dladdr(const void*, Dl_info*)`
82         const int res = dladdr(const_cast<void*>(ptr), &info);
83 
84         if (res) {
85             ret = info.dli_fname;
86         } else {
87             boost::dll::detail::reset_dlerror();
88             ec = boost::dll::fs::make_error_code(
89                 boost::dll::fs::errc::bad_address
90             );
91         }
92 
93         return ret;
94 #endif
95     }
96 
97     //! \overload symbol_location_ptr(const void* ptr_to_symbol, boost::dll::fs::error_code& ec)
98     template <class T>
symbol_location_ptr(T ptr_to_symbol)99     inline boost::dll::fs::path symbol_location_ptr(T ptr_to_symbol) {
100         boost::dll::fs::path ret;
101         boost::dll::fs::error_code ec;
102         ret = boost::dll::symbol_location_ptr(ptr_to_symbol, ec);
103 
104         if (ec) {
105             boost::dll::detail::report_error(ec, "boost::dll::symbol_location_ptr(T ptr_to_symbol) failed");
106         }
107 
108         return ret;
109     }
110 
111     /*!
112     * On success returns full path and name of the binary object that holds symbol.
113     *
114     * \tparam T Type of the symbol, must not be explicitly specified.
115     * \param symbol Symbol which location is to be determined.
116     * \param ec Variable that will be set to the result of the operation.
117     * \return Path to the binary object that holds symbol or empty path in case error.
118     * \throws std::bad_alloc in case of insufficient memory. Overload that does not accept \forcedlinkfs{error_code} also throws \forcedlinkfs{system_error}.
119     *
120     * \b Examples:
121     * \code
122     * int var;
123     * void foo() {}
124     *
125     * int main() {
126     *    dll::symbol_location(var);                     // returns program location
127     *    dll::symbol_location(foo);                     // returns program location
128     *    dll::symbol_location(std::cerr);               // returns location of libstdc++: "/usr/lib/x86_64-linux-gnu/libstdc++.so.6"
129     *    dll::symbol_location(std::placeholders::_1);   // returns location of libstdc++: "/usr/lib/x86_64-linux-gnu/libstdc++.so.6"
130     *    dll::symbol_location(std::puts);               // returns location of libc: "/lib/x86_64-linux-gnu/libc.so.6"
131     * }
132     * \endcode
133     */
134     template <class T>
symbol_location(const T & symbol,boost::dll::fs::error_code & ec)135     inline boost::dll::fs::path symbol_location(const T& symbol, boost::dll::fs::error_code& ec) {
136         ec.clear();
137         return boost::dll::symbol_location_ptr(
138             boost::dll::detail::aggressive_ptr_cast<const void*>(boost::addressof(symbol)),
139             ec
140         );
141     }
142 
143 #if BOOST_COMP_MSVC < BOOST_VERSION_NUMBER(14,0,0)
144     // Without this MSVC 7.1 fails with:
145     //  ..\boost\dll\runtime_symbol_info.hpp(133) : error C2780: 'filesystem::path dll::symbol_location(const T &)' : expects 1 arguments - 2 provided
146     template <class T>
symbol_location(const T & symbol,const char * =0)147     inline boost::dll::fs::path symbol_location(const T& symbol, const char* /*workaround*/ = 0)
148 #else
149     //! \overload symbol_location(const T& symbol, boost::dll::fs::error_code& ec)
150     template <class T>
151     inline boost::dll::fs::path symbol_location(const T& symbol)
152 #endif
153     {
154         boost::dll::fs::path ret;
155         boost::dll::fs::error_code ec;
156         ret = boost::dll::symbol_location_ptr(
157             boost::dll::detail::aggressive_ptr_cast<const void*>(boost::addressof(symbol)),
158             ec
159         );
160 
161         if (ec) {
162             boost::dll::detail::report_error(ec, "boost::dll::symbol_location(const T& symbol) failed");
163         }
164 
165         return ret;
166     }
167 
168     /// @cond
169     // We have anonymous namespace here to make sure that `this_line_location()` method is instantiated in
170     // current translation unit and is not shadowed by instantiations from other units.
171     namespace {
172     /// @endcond
173 
174     /*!
175     * On success returns full path and name of the binary object that holds the current line of code
176     * (the line in which the `this_line_location()` method was called).
177     *
178     * \param ec Variable that will be set to the result of the operation.
179     * \throws std::bad_alloc in case of insufficient memory. Overload that does not accept \forcedlinkfs{error_code} also throws \forcedlinkfs{system_error}.
180     */
this_line_location(boost::dll::fs::error_code & ec)181     static inline boost::dll::fs::path this_line_location(boost::dll::fs::error_code& ec) {
182         typedef boost::dll::fs::path(func_t)(boost::dll::fs::error_code& );
183         func_t& f = this_line_location;
184         return boost::dll::symbol_location(f, ec);
185     }
186 
187     //! \overload this_line_location(boost::dll::fs::error_code& ec)
this_line_location()188     static inline boost::dll::fs::path this_line_location() {
189         boost::dll::fs::path ret;
190         boost::dll::fs::error_code ec;
191         ret = this_line_location(ec);
192 
193         if (ec) {
194             boost::dll::detail::report_error(ec, "boost::dll::this_line_location() failed");
195         }
196 
197         return ret;
198     }
199 
200     /// @cond
201     } // anonymous namespace
202     /// @endcond
203 
204     /*!
205     * On success returns full path and name of the currently running program (the one which contains the `main()` function).
206     *
207     * Return value can be used as a parameter for shared_library. See Tutorial "Linking plugin into the executable"
208     * for usage example. Flag '-rdynamic' must be used when linking the plugin into the executable
209     * on Linux OS.
210     *
211     * \param ec Variable that will be set to the result of the operation.
212     * \throws std::bad_alloc in case of insufficient memory. Overload that does not accept \forcedlinkfs{error_code} also throws \forcedlinkfs{system_error}.
213     */
program_location(boost::dll::fs::error_code & ec)214     inline boost::dll::fs::path program_location(boost::dll::fs::error_code& ec) {
215         ec.clear();
216         return boost::dll::detail::program_location_impl(ec);
217     }
218 
219     //! \overload program_location(boost::dll::fs::error_code& ec) {
program_location()220     inline boost::dll::fs::path program_location() {
221         boost::dll::fs::path ret;
222         boost::dll::fs::error_code ec;
223         ret = boost::dll::detail::program_location_impl(ec);
224 
225         if (ec) {
226             boost::dll::detail::report_error(ec, "boost::dll::program_location() failed");
227         }
228 
229         return ret;
230     }
231 
232 }} // namespace boost::dll
233 
234 #endif // BOOST_DLL_RUNTIME_SYMBOL_INFO_HPP
235 
236