SNode.C
Loading...
Searching...
No Matches
DynamicLoader.cpp
Go to the documentation of this file.
1/*
2 * SNode.C - A Slim Toolkit for Network Communication
3 * Copyright (C) Volker Christian <me@vchrist.at>
4 * 2020, 2021, 2022, 2023, 2024, 2025
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published
8 * by the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20/*
21 * MIT License
22 *
23 * Permission is hereby granted, free of charge, to any person obtaining a copy
24 * of this software and associated documentation files (the "Software"), to deal
25 * in the Software without restriction, including without limitation the rights
26 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
27 * copies of the Software, and to permit persons to whom the Software is
28 * furnished to do so, subject to the following conditions:
29 *
30 * The above copyright notice and this permission notice shall be included in
31 * all copies or substantial portions of the Software.
32 *
33 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
39 * THE SOFTWARE.
40 */
41
42#include "core/DynamicLoader.h"
43
44#ifndef DOXYGEN_SHOULD_SKIP_THIS
45
46#include "log/Logger.h"
47
48#include <filesystem>
49#include <utility>
50
51#endif /* DOXYGEN_SHOULD_SKIP_THIS */
52
53namespace core {
54
56 std::map<void*, std::string> DynamicLoader::dlOpenedLibrariesByHandle;
57 std::list<std::string> DynamicLoader::closeQueue;
58
59 std::string DynamicLoader::canonicalizePath(const std::string& libFile) {
60 std::string result = libFile;
61
62 try {
63 const std::filesystem::path p(libFile);
64 // Only try to resolve symlinks / normalize if the file actually exists.
65 // Otherwise (e.g. "libfoo.so" to be found via ld search path), keep as-is.
66 if (std::filesystem::exists(p)) {
67 result = std::filesystem::canonical(p).string();
68 }
69 } catch (...) {
70 // keep as-is
71 }
72
73 return result;
74 }
75
76 void* DynamicLoader::dlOpen(const std::string& libFile, int flags) {
77 void* handle = nullptr;
78
79 const std::string canonicalFile = canonicalizePath(libFile);
80
81 auto it = dlOpenedLibraries.find(canonicalFile);
82 if (it != dlOpenedLibraries.end()) {
83 Library& lib = it->second;
84 ++lib.refCount;
85 lib.closePending = false;
86
87 LOG(TRACE) << "DynLoader: dlOpen: " << lib.fileName << ": already open (refCount=" << lib.refCount << ")";
88 handle = lib.handle;
89 } else {
90 // Clear possible stale error
92
93 handle = core::system::dlopen(libFile.c_str(), flags);
94 if (handle != nullptr) {
95 Library lib;
96 lib.fileName = libFile;
97 lib.canonicalFileName = canonicalFile;
98 lib.handle = handle;
99 lib.refCount = 1;
100 lib.closePending = false;
101
102 dlOpenedLibraries.emplace(canonicalFile, lib);
103 dlOpenedLibrariesByHandle.emplace(handle, canonicalFile);
104
105 LOG(TRACE) << "DynLoader: dlOpen: " << libFile << ": success";
106 } else {
107 LOG(TRACE) << "DynLoader: dlOpen: " << libFile << ": " << DynamicLoader::dlError();
108 }
109 }
110
111 return handle;
112 }
113
114 void DynamicLoader::dlCloseDelayed(void* handle) {
115 if (handle == nullptr) {
116 LOG(TRACE) << "DynLoader: dlCloseDelayed: handle is nullptr";
117 } else {
118 auto itHandle = dlOpenedLibrariesByHandle.find(handle);
119 if (itHandle == dlOpenedLibrariesByHandle.end()) {
120 LOG(TRACE) << "DynLoader: dlCloseDelayed: " << handle << ": not opened using dlOpen";
121 } else {
122 auto itLib = dlOpenedLibraries.find(itHandle->second);
123 if (itLib == dlOpenedLibraries.end()) {
124 LOG(TRACE) << "DynLoader: dlCloseDelayed: internal error: handle known but library record missing";
125 } else {
126 Library& lib = itLib->second;
127
128 if (lib.refCount > 0) {
129 --lib.refCount;
130 }
131
132 if (lib.refCount == 0) {
133 lib.closePending = true;
134 closeQueue.push_back(lib.canonicalFileName);
135 LOG(TRACE) << "DynLoader: dlCloseDelayed: " << lib.fileName;
136 } else {
137 LOG(TRACE) << "DynLoader: dlCloseDelayed: " << lib.fileName << ": still referenced (refCount=" << lib.refCount
138 << ")";
139 }
140 }
141 }
142 }
143 }
144
145 int DynamicLoader::dlClose(void* handle) {
146 int ret = 0;
147
148 if (handle == nullptr) {
149 LOG(TRACE) << "DynLoader: dlClose: handle is nullptr";
150 } else {
151 auto itHandle = dlOpenedLibrariesByHandle.find(handle);
152 if (itHandle == dlOpenedLibrariesByHandle.end()) {
153 LOG(TRACE) << "DynLoader: dlClose: " << handle << ": not opened using dlOpen";
154 } else {
155 auto itLib = dlOpenedLibraries.find(itHandle->second);
156 if (itLib == dlOpenedLibraries.end()) {
157 LOG(TRACE) << "DynLoader: dlClose: internal error: handle known but library record missing";
158 } else {
159 Library& lib = itLib->second;
160
161 if (lib.refCount > 0) {
162 --lib.refCount;
163 }
164
165 if (lib.refCount != 0) {
166 LOG(TRACE) << "DynLoader: dlClose: " << lib.fileName << ": still referenced (refCount=" << lib.refCount << ")";
167 } else {
168 lib.closePending = false;
169 ret = dlClose(lib);
170
172 dlOpenedLibraries.erase(itLib);
173 }
174 }
175 }
176 }
177
178 return ret;
179 }
180
181 const void* DynamicLoader::dlSym(void* handle, const std::string& symbol) {
182 const void* sym = core::system::dlsym(handle, symbol.c_str());
183 return sym;
184 }
185
186 const char* DynamicLoader::dlError() {
187 const char* err = core::system::dlerror();
188 return err;
189 }
190
191 int DynamicLoader::realExecDlClose(const Library& library) {
192 const int ret = core::system::dlclose(library.handle);
193 return ret;
194 }
195
197 int ret = 0;
198
199 ret = realExecDlClose(library);
200
201 if (ret != 0) {
202 LOG(TRACE) << " dlClose: " << DynamicLoader::dlError();
203 } else {
204 LOG(TRACE) << " dlClose: " << library.fileName << ": success";
205 }
206
207 return ret;
208 }
209
211 if (!closeQueue.empty()) {
212 LOG(TRACE) << "DynLoader: execDlCloseDeleyed";
213
214 for (const std::string& canonicalFile : closeQueue) {
215 auto it = dlOpenedLibraries.find(canonicalFile);
216 if (it == dlOpenedLibraries.end()) {
217 continue;
218 }
219
220 Library& lib = it->second;
221 if (!lib.closePending || lib.refCount != 0) {
222 continue;
223 }
224
225 lib.closePending = false;
226 (void) dlClose(lib);
228 dlOpenedLibraries.erase(it);
229 }
230
231 closeQueue.clear();
232
233 LOG(TRACE) << "DynLoader: execDlCloseDeleyed done";
234 }
235 }
236
238 LOG(TRACE) << "DynLoader: execDlCloseAll";
239
240 for (auto& [canonical, library] : dlOpenedLibraries) {
241 (void) dlClose(library);
242 }
243
244 dlOpenedLibraries.clear();
246 closeQueue.clear();
247
248 LOG(TRACE) << "DynLoader: execDlCloseAll done";
249 }
250
251} // namespace core
static int realExecDlClose(const Library &library)
static int dlClose(void *handle)
static std::string canonicalizePath(const std::string &libFile)
static const char * dlError()
static void * dlOpen(const std::string &libFile, int flags=RTLD_LOCAL|RTLD_LAZY)
static void execDlCloseDeleyed()
static int dlClose(Library &library)
static const void * dlSym(void *handle, const std::string &symbol)
static std::map< void *, std::string > dlOpenedLibrariesByHandle
static std::map< std::string, Library > dlOpenedLibraries
static void execDlCloseAll()
static std::list< std::string > closeQueue
static void dlCloseDelayed(void *handle)
int dlclose(void *handle)
Definition dlfcn.cpp:57
void * dlsym(void *handle, const char *symbol)
Definition dlfcn.cpp:62
void * dlopen(const char *filename, int flags)
Definition dlfcn.cpp:52
char * dlerror()
Definition dlfcn.cpp:67