Gamp v0.0.8
Gamp: Graphics, Audio, Multimedia and Processing
Loading...
Searching...
No Matches
dyn_linker.hpp
Go to the documentation of this file.
1/**
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright (c) 2024 Gothel Software e.K.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sublicense, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24#pragma once
25
26#include <cstdint>
27#include <string>
28#include <memory>
29#include <mutex>
30#include <unordered_map>
31
32#include "jau/debug.hpp"
33#include "jau/string_util.hpp"
34#include "jau/environment.hpp"
35#include "jau/os/os_support.hpp"
36
37namespace jau::os {
38
39 /** \addtogroup OSSup
40 *
41 * @{
42 */
43
44 /** Low level secure dynamic linker access. */
46 public:
47 /** library handle */
48 typedef void* libhandle_t;
49 /** symbol handle within a library */
50 typedef void* symhandle_t;
51
52 protected:
53 constexpr static const bool DEBUG_LOOKUP = false;
54
55 //
56 // Implemented per platform as hidden detail
57 //
58
59 virtual libhandle_t openLibraryGlobalImpl(const std::string& pathname) noexcept = 0;
60 virtual libhandle_t openLibraryLocalImpl(const std::string& pathname) noexcept = 0;
61 virtual const char* lookupLibraryPathnameImpl(libhandle_t libraryHandle, const std::string& symbolName) noexcept = 0;
62 virtual symhandle_t lookupSymbolGlobalImpl(const std::string& symbolName) noexcept = 0;
63 virtual symhandle_t lookupSymbolLocalImpl(libhandle_t handle, const std::string& symbolName) noexcept = 0;
64 virtual void closeLibraryImpl(libhandle_t handle) noexcept = 0;
65 virtual std::string getLastErrorImpl() noexcept = 0;
66
67 private:
68 class LibRef {
69 private:
70 std::string m_name;
71 ssize_t m_count;
72 public:
73 LibRef(std::string name) noexcept
74 : m_name(std::move(name)), m_count(1) {}
75
76 constexpr_cxx20 LibRef(const LibRef& o) noexcept = default;
77 constexpr_cxx20 LibRef(LibRef&& o) noexcept = default;
78 LibRef& operator=(const LibRef&) noexcept = default;
79 LibRef& operator=(LibRef&&) noexcept = default;
80
81 ssize_t incrRefCount() { return ++m_count; }
82 ssize_t decrRefCount() { return --m_count; }
83 ssize_t count() { return m_count; }
84
85 const std::string& name() { return m_name; }
86
87 std::string toString() { return "LibRef["+m_name+", count "+std::to_string(m_count)+"]"; }
88 };
89 typedef std::shared_ptr<LibRef> LibRef_ref;
90
91 typedef std::unordered_map<libhandle_t, LibRef_ref> LibRefMap_t;
92 typedef typename LibRefMap_t::iterator LibRefIter_t;
93
94 std::mutex m_mtx_libref;
95 LibRefMap_t m_handleToNameMap;
96
97 static DynamicLinker* create();
98
99 protected:
100 DynamicLinker() noexcept = default;
101
102 LibRef_ref incrLibRefCount(const libhandle_t handle, const std::string& libName) noexcept {
103 std::unique_lock<std::mutex> lock(m_mtx_libref);
104 LibRef_ref libRef = nullptr;
105 LibRefIter_t iter = m_handleToNameMap.find(handle);
106 if( m_handleToNameMap.end() == iter ) {
107 std::pair<LibRefIter_t,bool> res = m_handleToNameMap.insert( { handle, std::make_shared<LibRef>(libName) } );
108 libRef = res.first->second;
109 if( !res.second ) {
110 libRef->incrRefCount();
111 }
112 } else {
113 libRef = iter->second;
114 libRef->incrRefCount();
115 }
116 jau_DBG_PRINT("DynamicLinkerImpl.incrLibRefCount %p -> %s, libs loaded %zu", handle, libRef->toString(), m_handleToNameMap.size());
117 return libRef;
118 }
119
120 LibRef_ref decrLibRefCount(const libhandle_t handle) noexcept {
121 std::unique_lock<std::mutex> lock(m_mtx_libref);
122 LibRef_ref libRef = nullptr;
123 LibRefIter_t iter = m_handleToNameMap.find(handle);
124 if( m_handleToNameMap.end() != iter ) {
125 libRef = iter->second;
126 if( 0 == libRef->decrRefCount() ) {
127 m_handleToNameMap.erase(iter);
128 }
129 jau_DBG_PRINT("DynamicLinkerImpl.decrLibRefCount %p -> %s, libs loaded %zu", handle, libRef->toString(), m_handleToNameMap.size());
130 } else {
131 jau_DBG_PRINT("DynamicLinkerImpl.decrLibRefCount %p -> null, libs loaded %zu", handle, m_handleToNameMap.size());
132 }
133 return libRef;
134 }
135
136 public:
137 virtual ~DynamicLinker() noexcept = default;
138
139 /** Returns the environment library path variable name, e.g. `LD_LIBRARY_PATH` */
140 constexpr_cxx20 static std::string getEnvLibPathVarName() noexcept {
141 if constexpr ( jau::os::is_darwin() ) {
142 return "DYLD_LIBRARY_PATH";
143 } else if constexpr ( jau::os::is_windows() ) {
144 return "PATH";
145 } else {
146 return "LD_LIBRARY_PATH";
147 }
148 }
149 /**
150 * Returns a list of system paths, from the {@link #getSystemEnvLibraryPathVarname()} variable.
151 */
155
156 /** Returns the native library prefix, e.g. `lib` */
157 constexpr_cxx20 static std::string getDefaultPrefix() noexcept {
158 if constexpr ( jau::os::is_windows() ) {
159 return "";
160 } else {
161 return "lib";
162 }
163 }
164 /** Returns the native library suffix including the dot, e.g. `.so` */
165 constexpr_cxx20 static std::string getDefaultSuffix() noexcept {
166 if constexpr ( jau::os::is_darwin() ) {
167 return ".dylib";
168 } else if constexpr ( jau::os::is_windows() ) {
169 return ".dll";
170 } else {
171 return ".so";
172 }
173 }
174 /**
175 * Returns canonical library name for this system from given library-basename, e.g. `tool' -> `libtool.so`
176 * if it is not yet canonical, see isCanonicalName().
177 * @param basename the library basename
178 * @param checkIsCanonical pass true to first check is basename is already canonical, defaults to true
179 * @see isCanonicalName()
180 * @see getBaseName()
181 * @see jau::fs::basename()
182 */
183 constexpr_cxx20 static std::string getCanonicalName(const std::string& basename, const bool checkIsCanonical=true) noexcept {
184 if( !checkIsCanonical || !isCanonicalName(basename, true) ) {
185 return getDefaultPrefix()+basename+getDefaultSuffix();
186 } else {
187 return basename;
188 }
189 }
190
191 /**
192 * Returns true if the given filename contains the canonical prefix and suffix,
193 * otherwise returns false.
194 *
195 * Validation is performed case insensitive if `caseInsensitive == true`
196 *
197 * @param filename the filename to test
198 * @param isBasename pass true if filename is a basename, otherwise false. Defaults to false. See jau::fs::basename().
199 * @param caseInsensitive perform prefix and suffix comparison case-insensitive, defaults to true on Windows otherwise false
200 * @see getCanonicalName()
201 * @see getBaseName()
202 * @see jau::fs::basename()
203 */
204 static bool isCanonicalName(const std::string& filename, const bool isBasename=false, const bool caseInsensitive=jau::os::is_windows()) noexcept;
205
206 /**
207 * Returns the library basename, i.e. the file basename without prefix nor suffix,
208 * performed case insensitive if `caseInsensitive == true`
209 *
210 * @param filename the filename to process
211 * @param isBasename pass true if filename is a basename, otherwise false. Defaults to false. See jau::fs::basename().
212 * @param caseInsensitive perform prefix and suffix comparison case-insensitive, defaults to true on Windows otherwise false
213 * @return basename of filename w/o path nor prefix or suffix, ie. /usr/lib/libDrinkBeer.so -> DrinkBeer on Unix systems
214 * @see getCanonicalName()
215 * @see isCanonicalName()
216 * @see jau::fs::basename()
217 */
218 static std::string getBaseName(const std::string& filename, const bool isBasename=false, const bool caseInsensitive=jau::os::is_windows()) noexcept;
219
220 /**
221 * Returns list of potential absolute library filenames
222 * @param libName library basename (implies file-basename) or canonical-library-name as file-basename or absolute-path
223 * @param searchSystemPath
224 * @param searchSystemPathFirst
225 */
226 static std::vector<std::string> enumerateLibraryPaths(const std::string& libName,
227 bool searchSystemPath=false,
228 bool searchSystemPathFirst=false) noexcept;
229
230
231 /** Returns static singleton instance of DynamicLinker */
232 static DynamicLinker& get() noexcept {
233 /**
234 * Thread safe starting with C++11 6.7:
235 *
236 * If control enters the declaration concurrently while the variable is being initialized,
237 * the concurrent execution shall wait for completion of the initialization.
238 *
239 * (Magic Statics)
240 *
241 * Avoiding non-working double checked locking.
242 */
243 static DynamicLinker* dl = create();
244 return *dl;
245 }
246
247 /**
248 * Opens the named library, allowing system wide access for other <i>users</i>.
249 *
250 * @param pathname the full pathname for the library to open
251 * @return the library handle, maybe 0 if not found.
252 */
253 libhandle_t openLibraryGlobal(const std::string& pathname) noexcept {
254 libhandle_t handle = openLibraryGlobalImpl(pathname);
255 if( nullptr != handle ) {
256 LibRef_ref libRef = incrLibRefCount(handle, pathname);
257 jau_DBG_PRINT("DynamicLinkerImpl.openLibraryGlobal \"%s\": %p -> %s", pathname, handle, libRef->toString());
258 } else {
259 jau_DBG_PRINT("DynamicLinkerImpl.openLibraryGlobal \"%s\" failed, error %s", pathname, getLastError());
260 }
261 return handle;
262 }
263
264 /**
265 * Opens the named library, restricting access to this process.
266 *
267 * @param pathname the full pathname for the library to open
268 * @return the library handle, maybe 0 if not found.
269 */
270 libhandle_t openLibraryLocal(const std::string& pathname) noexcept {
271 libhandle_t handle = openLibraryLocalImpl(pathname);
272 if( nullptr != handle ) {
273 LibRef_ref libRef = incrLibRefCount(handle, pathname);
274 jau_DBG_PRINT("DynamicLinkerImpl.openLibraryLocal \"%s\": %p -> %s", pathname, handle, libRef->toString());
275 } else {
276 jau_DBG_PRINT("DynamicLinkerImpl.openLibraryLocal \"%s\" failed, error %s", pathname, getLastError());
277 }
278 return handle;
279 }
280
281 /**
282 * @param libraryHandle a library handle previously retrieved via {@link #openLibraryLocal(const std::string_view&, boolean)} or {@link #openLibraryGlobal(const std::string_view&, boolean)}.
283 * @param symbolName optional symbol name for an OS which requires the symbol's address to retrieve the path of the containing library
284 * @return the library pathname if found and supported by OS or {@code null}.
285 */
286 const char* lookupLibraryPathname(libhandle_t handle, const std::string& symbolName) noexcept {
287 const char* fname = lookupLibraryPathnameImpl(handle, symbolName);
288 if(DEBUG_LOOKUP) {
289 jau_INFO_PRINT("DynamicLinkerImpl.lookupLibraryPathname(%p, %s) -> '%s'", handle, symbolName, fname);
290 }
291 return fname;
292 }
293
294 /**
295 * @param symbolName global symbol name to lookup up system wide.
296 * @return the symbol handle, maybe nullptr if not found.
297 */
298 symhandle_t lookupSymbolGlobal(const std::string& symbolName) noexcept {
299 symhandle_t addr = lookupSymbolGlobalImpl(symbolName);
300 if(DEBUG_LOOKUP) {
301 jau_INFO_PRINT("DynamicLinkerImpl.lookupSymbolGlobal(%s) -> %p", symbolName, addr);
302 }
303 return addr;
304 }
305
306 /**
307 * @param libraryHandle a library handle previously retrieved via {@link #openLibraryLocal(const std::string_view&, boolean)} or {@link #openLibraryGlobal(const std::string_view&, boolean)}.
308 * @param symbolName global symbol name to lookup up system wide.
309 * @return the symbol handle, maybe nullptr if not found.
310 */
311 symhandle_t lookupSymbol(libhandle_t handle, const std::string& symbolName) noexcept {
312 symhandle_t addr = lookupSymbolLocalImpl(handle, symbolName);
313 if(DEBUG_LOOKUP) {
314 jau_format_check("DynamicLinkerImpl.lookupSymbol(%p, %s) -> %p", handle, symbolName, addr);
315 jau_INFO_PRINT("DynamicLinkerImpl.lookupSymbol(%p, %s) -> %p", handle, symbolName, addr);
316 }
317 return addr;
318 }
319
320 /**
321 * Security checks are implicit by previous call of
322 * {@link #openLibraryLocal(const std::string_view&, boolean)} or {@link #openLibraryGlobal(const std::string_view&, boolean)}
323 * retrieving the <code>librarHandle</code>.
324 *
325 * @param libraryHandle a library handle previously retrieved via {@link #openLibraryLocal(const std::string_view&, boolean)} or {@link #openLibraryGlobal(const std::string_view&, boolean)}.
326 */
327 void closeLibrary(libhandle_t handle) noexcept {
328 LibRef_ref libRef = decrLibRefCount( handle ); // null libRef is OK for global lookup
329 if( nullptr != libRef ) {
330 jau_DBG_PRINT("DynamicLinkerImpl.closeLibrary(%p -> %s)", handle, libRef->toString());
331 } else {
332 jau_DBG_PRINT("DynamicLinkerImpl.closeLibrary(%p -> null)", handle);
333 }
334 if( nullptr != handle ) {
335 closeLibraryImpl(handle);
336 }
337 }
338 /**
339 * Returns a string containing the last error.
340 * Maybe called for debugging purposed if any method fails.
341 * @return error string, maybe null. A null or non-null value has no semantics.
342 */
343 std::string getLastError() noexcept { return getLastErrorImpl(); }
344 };
345
346 /**@}*/
347
348} // namespace jau::os
349
String toString()
static std::string getProperty(const std::string &name) noexcept
Returns the value of the environment's variable 'name'.
Low level secure dynamic linker access.
static std::string getBaseName(const std::string &filename, const bool isBasename=false, const bool caseInsensitive=jau::os::is_windows()) noexcept
Returns the library basename, i.e.
static constexpr const bool DEBUG_LOOKUP
virtual const char * lookupLibraryPathnameImpl(libhandle_t libraryHandle, const std::string &symbolName) noexcept=0
virtual libhandle_t openLibraryLocalImpl(const std::string &pathname) noexcept=0
virtual libhandle_t openLibraryGlobalImpl(const std::string &pathname) noexcept=0
static DynamicLinker & get() noexcept
Returns static singleton instance of DynamicLinker.
symhandle_t lookupSymbolGlobal(const std::string &symbolName) noexcept
virtual symhandle_t lookupSymbolLocalImpl(libhandle_t handle, const std::string &symbolName) noexcept=0
static std::vector< std::string > enumerateLibraryPaths(const std::string &libName, bool searchSystemPath=false, bool searchSystemPathFirst=false) noexcept
Returns list of potential absolute library filenames.
LibRef_ref decrLibRefCount(const libhandle_t handle) noexcept
symhandle_t lookupSymbol(libhandle_t handle, const std::string &symbolName) noexcept
virtual std::string getLastErrorImpl() noexcept=0
void * symhandle_t
symbol handle within a library
static constexpr_cxx20 std::string getDefaultPrefix() noexcept
Returns the native library prefix, e.g.
static constexpr_cxx20 std::string getDefaultSuffix() noexcept
Returns the native library suffix including the dot, e.g.
LibRef_ref incrLibRefCount(const libhandle_t handle, const std::string &libName) noexcept
libhandle_t openLibraryGlobal(const std::string &pathname) noexcept
Opens the named library, allowing system wide access for other users.
DynamicLinker() noexcept=default
static constexpr_cxx20 std::string getEnvLibPathVarName() noexcept
Returns the environment library path variable name, e.g.
void * libhandle_t
library handle
const char * lookupLibraryPathname(libhandle_t handle, const std::string &symbolName) noexcept
virtual ~DynamicLinker() noexcept=default
std::string getLastError() noexcept
Returns a string containing the last error.
static constexpr_cxx20 std::string getCanonicalName(const std::string &basename, const bool checkIsCanonical=true) noexcept
Returns canonical library name for this system from given library-basename, e.g.
libhandle_t openLibraryLocal(const std::string &pathname) noexcept
Opens the named library, restricting access to this process.
void closeLibrary(libhandle_t handle) noexcept
Security checks are implicit by previous call of openLibraryLocal(const std::string_view&,...
static bool isCanonicalName(const std::string &filename, const bool isBasename=false, const bool caseInsensitive=jau::os::is_windows()) noexcept
Returns true if the given filename contains the canonical prefix and suffix, otherwise returns false.
static std::vector< std::string > getSystemEnvLibraryPaths()
Returns a list of system paths, from the getSystemEnvLibraryPathVarname() variable.
virtual void closeLibraryImpl(libhandle_t handle) noexcept=0
virtual symhandle_t lookupSymbolGlobalImpl(const std::string &symbolName) noexcept=0
#define jau_INFO_PRINT(fmt,...)
Use for unconditional informal messages, prefix '[elapsed_time] Info: '.
Definition debug.hpp:167
#define jau_DBG_PRINT(fmt,...)
Use for environment-variable environment::DEBUG conditional debug messages, prefix '[elapsed_time] De...
Definition debug.hpp:102
consteval_cxx20 std::string_view name() noexcept
#define constexpr_cxx20
constexpr qualifier replacement for C++20 constexpr.
constexpr bool is_darwin() noexcept
Evaluates true if platform os_type::native contains os_type::Darwin.
constexpr_cxx20 std::string path_separator() noexcept
Returns the OS's path separator as a string, e.g.
constexpr bool is_windows() noexcept
Evaluates true if platform os_type::native contains os_type::Windows.
std::vector< std::string > split_string(const std::string &str, const std::string &separator)
Split given string str at separator into the resulting std::vector excluding the separator sequence .
#define jau_format_check(fmt,...)
Macro produces compile time validation using a static_assert against jau::cfmt::check2.
Author: Sven Gothel sgothel@jausoft.com Copyright (c) 2024 Gothel Software e.K.
__pack(...): Produces MSVC, clang and gcc compatible lead-in and -out macros.
Definition backtrace.hpp:32
STL namespace.