/src/qtbase/src/corelib/plugin/qlibrary.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /**************************************************************************** |
2 | | ** |
3 | | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | | ** Copyright (C) 2018 Intel Corporation. |
5 | | ** Contact: https://www.qt.io/licensing/ |
6 | | ** |
7 | | ** This file is part of the QtCore module of the Qt Toolkit. |
8 | | ** |
9 | | ** $QT_BEGIN_LICENSE:LGPL$ |
10 | | ** Commercial License Usage |
11 | | ** Licensees holding valid commercial Qt licenses may use this file in |
12 | | ** accordance with the commercial license agreement provided with the |
13 | | ** Software or, alternatively, in accordance with the terms contained in |
14 | | ** a written agreement between you and The Qt Company. For licensing terms |
15 | | ** and conditions see https://www.qt.io/terms-conditions. For further |
16 | | ** information use the contact form at https://www.qt.io/contact-us. |
17 | | ** |
18 | | ** GNU Lesser General Public License Usage |
19 | | ** Alternatively, this file may be used under the terms of the GNU Lesser |
20 | | ** General Public License version 3 as published by the Free Software |
21 | | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
22 | | ** packaging of this file. Please review the following information to |
23 | | ** ensure the GNU Lesser General Public License version 3 requirements |
24 | | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
25 | | ** |
26 | | ** GNU General Public License Usage |
27 | | ** Alternatively, this file may be used under the terms of the GNU |
28 | | ** General Public License version 2.0 or (at your option) the GNU General |
29 | | ** Public license version 3 or any later version approved by the KDE Free |
30 | | ** Qt Foundation. The licenses are as published by the Free Software |
31 | | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
32 | | ** included in the packaging of this file. Please review the following |
33 | | ** information to ensure the GNU General Public License requirements will |
34 | | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
35 | | ** https://www.gnu.org/licenses/gpl-3.0.html. |
36 | | ** |
37 | | ** $QT_END_LICENSE$ |
38 | | ** |
39 | | ****************************************************************************/ |
40 | | #include "qplatformdefs.h" |
41 | | #include "qlibrary.h" |
42 | | |
43 | | #include "qfactoryloader_p.h" |
44 | | #include "qlibrary_p.h" |
45 | | #include <qstringlist.h> |
46 | | #include <qfile.h> |
47 | | #include <qfileinfo.h> |
48 | | #include <qmutex.h> |
49 | | #include <qmap.h> |
50 | | #include <private/qcoreapplication_p.h> |
51 | | #include <private/qsystemerror_p.h> |
52 | | #ifdef Q_OS_MAC |
53 | | # include <private/qcore_mac_p.h> |
54 | | #endif |
55 | | #ifndef NO_ERRNO_H |
56 | | #include <errno.h> |
57 | | #endif // NO_ERROR_H |
58 | | #include <qdebug.h> |
59 | | #include <qvector.h> |
60 | | #include <qdir.h> |
61 | | #include <qendian.h> |
62 | | #include <qjsondocument.h> |
63 | | #include <qjsonvalue.h> |
64 | | #include "qelfparser_p.h" |
65 | | #include "qmachparser_p.h" |
66 | | |
67 | | #include <qtcore_tracepoints_p.h> |
68 | | |
69 | | QT_BEGIN_NAMESPACE |
70 | | |
71 | | #ifdef QT_NO_DEBUG |
72 | | # define QLIBRARY_AS_DEBUG false |
73 | | #else |
74 | | # define QLIBRARY_AS_DEBUG true |
75 | | #endif |
76 | | |
77 | | #if defined(Q_OS_UNIX) || (defined(Q_CC_MINGW) && !QT_CONFIG(debug_and_release)) |
78 | | // We don't use separate debug and release libs on UNIX, so we want |
79 | | // to allow loading plugins, regardless of how they were built. |
80 | | # define QT_NO_DEBUG_PLUGIN_CHECK |
81 | | #endif |
82 | | |
83 | | /*! |
84 | | \class QLibrary |
85 | | \inmodule QtCore |
86 | | \reentrant |
87 | | \brief The QLibrary class loads shared libraries at runtime. |
88 | | |
89 | | |
90 | | \ingroup plugins |
91 | | |
92 | | An instance of a QLibrary object operates on a single shared |
93 | | object file (which we call a "library", but is also known as a |
94 | | "DLL"). A QLibrary provides access to the functionality in the |
95 | | library in a platform independent way. You can either pass a file |
96 | | name in the constructor, or set it explicitly with setFileName(). |
97 | | When loading the library, QLibrary searches in all the |
98 | | system-specific library locations (e.g. \c LD_LIBRARY_PATH on |
99 | | Unix), unless the file name has an absolute path. |
100 | | |
101 | | If the file name is an absolute path then an attempt is made to |
102 | | load this path first. If the file cannot be found, QLibrary tries |
103 | | the name with different platform-specific file prefixes, like |
104 | | "lib" on Unix and Mac, and suffixes, like ".so" on Unix, ".dylib" |
105 | | on the Mac, or ".dll" on Windows. |
106 | | |
107 | | If the file path is not absolute then QLibrary modifies the search |
108 | | order to try the system-specific prefixes and suffixes first, |
109 | | followed by the file path specified. |
110 | | |
111 | | This makes it possible to specify shared libraries that are only |
112 | | identified by their basename (i.e. without their suffix), so the |
113 | | same code will work on different operating systems yet still |
114 | | minimise the number of attempts to find the library. |
115 | | |
116 | | The most important functions are load() to dynamically load the |
117 | | library file, isLoaded() to check whether loading was successful, |
118 | | and resolve() to resolve a symbol in the library. The resolve() |
119 | | function implicitly tries to load the library if it has not been |
120 | | loaded yet. Multiple instances of QLibrary can be used to access |
121 | | the same physical library. Once loaded, libraries remain in memory |
122 | | until the application terminates. You can attempt to unload a |
123 | | library using unload(), but if other instances of QLibrary are |
124 | | using the same library, the call will fail, and unloading will |
125 | | only happen when every instance has called unload(). |
126 | | |
127 | | A typical use of QLibrary is to resolve an exported symbol in a |
128 | | library, and to call the C function that this symbol represents. |
129 | | This is called "explicit linking" in contrast to "implicit |
130 | | linking", which is done by the link step in the build process when |
131 | | linking an executable against a library. |
132 | | |
133 | | The following code snippet loads a library, resolves the symbol |
134 | | "mysymbol", and calls the function if everything succeeded. If |
135 | | something goes wrong, e.g. the library file does not exist or the |
136 | | symbol is not defined, the function pointer will be \nullptr and |
137 | | won't be called. |
138 | | |
139 | | \snippet code/src_corelib_plugin_qlibrary.cpp 0 |
140 | | |
141 | | The symbol must be exported as a C function from the library for |
142 | | resolve() to work. This means that the function must be wrapped in |
143 | | an \c{extern "C"} block if the library is compiled with a C++ |
144 | | compiler. On Windows, this also requires the use of a \c dllexport |
145 | | macro; see resolve() for the details of how this is done. For |
146 | | convenience, there is a static resolve() function which you can |
147 | | use if you just want to call a function in a library without |
148 | | explicitly loading the library first: |
149 | | |
150 | | \snippet code/src_corelib_plugin_qlibrary.cpp 1 |
151 | | |
152 | | \sa QPluginLoader |
153 | | */ |
154 | | |
155 | | /*! |
156 | | \enum QLibrary::LoadHint |
157 | | |
158 | | This enum describes the possible hints that can be used to change the way |
159 | | libraries are handled when they are loaded. These values indicate how |
160 | | symbols are resolved when libraries are loaded, and are specified using |
161 | | the setLoadHints() function. |
162 | | |
163 | | \value ResolveAllSymbolsHint |
164 | | Causes all symbols in a library to be resolved when it is loaded, not |
165 | | simply when resolve() is called. |
166 | | \value ExportExternalSymbolsHint |
167 | | Exports unresolved and external symbols in the library so that they can be |
168 | | resolved in other dynamically-loaded libraries loaded later. |
169 | | \value LoadArchiveMemberHint |
170 | | Allows the file name of the library to specify a particular object file |
171 | | within an archive file. |
172 | | If this hint is given, the filename of the library consists of |
173 | | a path, which is a reference to an archive file, followed by |
174 | | a reference to the archive member. |
175 | | \value PreventUnloadHint |
176 | | Prevents the library from being unloaded from the address space if close() |
177 | | is called. The library's static variables are not reinitialized if open() |
178 | | is called at a later time. |
179 | | \value DeepBindHint |
180 | | Instructs the linker to prefer definitions in the loaded library |
181 | | over exported definitions in the loading application when resolving |
182 | | external symbols in the loaded library. This option is only supported |
183 | | on Linux. |
184 | | |
185 | | \sa loadHints |
186 | | */ |
187 | | |
188 | | |
189 | | static qsizetype qt_find_pattern(const char *s, qsizetype s_len, |
190 | | const char *pattern, ulong p_len) |
191 | 0 | { |
192 | | /* |
193 | | we search from the end of the file because on the supported |
194 | | systems, the read-only data/text segments are placed at the end |
195 | | of the file. HOWEVER, when building with debugging enabled, all |
196 | | the debug symbols are placed AFTER the data/text segments. |
197 | | |
198 | | what does this mean? when building in release mode, the search |
199 | | is fast because the data we are looking for is at the end of the |
200 | | file... when building in debug mode, the search is slower |
201 | | because we have to skip over all the debugging symbols first |
202 | | */ |
203 | |
|
204 | 0 | if (!s || !pattern || qsizetype(p_len) > s_len) |
205 | 0 | return -1; |
206 | | |
207 | 0 | size_t i, hs = 0, hp = 0, delta = s_len - p_len; |
208 | |
|
209 | 0 | for (i = 0; i < p_len; ++i) { |
210 | 0 | hs += s[delta + i]; |
211 | 0 | hp += pattern[i]; |
212 | 0 | } |
213 | 0 | i = delta; |
214 | 0 | for (;;) { |
215 | 0 | if (hs == hp && qstrncmp(s + i, pattern, p_len) == 0) |
216 | 0 | return i; // can't overflow, by construction |
217 | 0 | if (i == 0) |
218 | 0 | break; |
219 | 0 | --i; |
220 | 0 | hs -= s[i + p_len]; |
221 | 0 | hs += s[i]; |
222 | 0 | } |
223 | | |
224 | 0 | return -1; |
225 | 0 | } |
226 | | |
227 | | /* |
228 | | This opens the specified library, mmaps it into memory, and searches |
229 | | for the QT_PLUGIN_VERIFICATION_DATA. The advantage of this approach is that |
230 | | we can get the verification data without have to actually load the library. |
231 | | This lets us detect mismatches more safely. |
232 | | |
233 | | Returns \c false if version information is not present, or if the |
234 | | information could not be read. |
235 | | Returns true if version information is present and successfully read. |
236 | | */ |
237 | | static bool findPatternUnloaded(const QString &library, QLibraryPrivate *lib) |
238 | 0 | { |
239 | 0 | QFile file(library); |
240 | 0 | if (!file.open(QIODevice::ReadOnly)) { |
241 | 0 | if (lib) |
242 | 0 | lib->errorString = file.errorString(); |
243 | 0 | if (qt_debug_component()) { |
244 | 0 | qWarning("%s: %ls", QFile::encodeName(library).constData(), |
245 | 0 | qUtf16Printable(QSystemError::stdString())); |
246 | 0 | } |
247 | 0 | return false; |
248 | 0 | } |
249 | | |
250 | | // Files can be bigger than the virtual memory size on 32-bit systems, so |
251 | | // we limit to 512 MB there. For 64-bit, we allow up to 2^40 bytes. |
252 | 0 | constexpr qint64 MaxMemoryMapSize = |
253 | 0 | Q_INT64_C(1) << (sizeof(qsizetype) > 4 ? 40 : 29); |
254 | |
|
255 | 0 | QByteArray data; |
256 | 0 | qsizetype fdlen = qMin(file.size(), MaxMemoryMapSize); |
257 | 0 | const char *filedata = reinterpret_cast<char *>(file.map(0, fdlen)); |
258 | |
|
259 | 0 | if (filedata == nullptr) { |
260 | | // Try reading the data into memory instead (up to 64 MB). |
261 | 0 | data = file.read(64 * 1024 * 1024); |
262 | 0 | filedata = data.constData(); |
263 | 0 | fdlen = data.size(); |
264 | 0 | } |
265 | | |
266 | | /* |
267 | | ELF and Mach-O binaries with GCC have .qplugin sections. |
268 | | */ |
269 | 0 | bool hasMetaData = false; |
270 | 0 | qsizetype pos = 0; |
271 | 0 | char pattern[] = "qTMETADATA "; |
272 | 0 | pattern[0] = 'Q'; // Ensure the pattern "QTMETADATA" is not found in this library should QPluginLoader ever encounter it. |
273 | 0 | const ulong plen = qstrlen(pattern); |
274 | 0 | #if defined (Q_OF_ELF) && defined(Q_CC_GNU) |
275 | 0 | int r = QElfParser().parse(filedata, fdlen, library, lib, &pos, &fdlen); |
276 | 0 | if (r == QElfParser::Corrupt || r == QElfParser::NotElf) { |
277 | 0 | if (lib && qt_debug_component()) { |
278 | 0 | qWarning("QElfParser: %ls", qUtf16Printable(lib->errorString)); |
279 | 0 | } |
280 | 0 | return false; |
281 | 0 | } else if (r == QElfParser::QtMetaDataSection) { |
282 | 0 | qsizetype rel = qt_find_pattern(filedata + pos, fdlen, pattern, plen); |
283 | 0 | if (rel < 0) |
284 | 0 | pos = -1; |
285 | 0 | else |
286 | 0 | pos += rel; |
287 | 0 | hasMetaData = true; |
288 | 0 | } |
289 | | #elif defined (Q_OF_MACH_O) |
290 | | { |
291 | | QString errorString; |
292 | | int r = QMachOParser::parse(filedata, fdlen, library, &errorString, &pos, &fdlen); |
293 | | if (r == QMachOParser::NotSuitable) { |
294 | | if (qt_debug_component()) |
295 | | qWarning("QMachOParser: %ls", qUtf16Printable(errorString)); |
296 | | if (lib) |
297 | | lib->errorString = errorString; |
298 | | return false; |
299 | | } |
300 | | // even if the metadata section was not found, the Mach-O parser will |
301 | | // at least return the boundaries of the right architecture |
302 | | qsizetype rel = qt_find_pattern(filedata + pos, fdlen, pattern, plen); |
303 | | if (rel < 0) |
304 | | pos = -1; |
305 | | else |
306 | | pos += rel; |
307 | | hasMetaData = true; |
308 | | } |
309 | | #else |
310 | | pos = qt_find_pattern(filedata, fdlen, pattern, plen); |
311 | | if (pos > 0) |
312 | | hasMetaData = true; |
313 | | #endif // defined(Q_OF_ELF) && defined(Q_CC_GNU) |
314 | | |
315 | 0 | bool ret = false; |
316 | |
|
317 | 0 | if (pos >= 0 && hasMetaData) { |
318 | 0 | const char *data = filedata + pos; |
319 | 0 | QString errMsg; |
320 | 0 | QJsonDocument doc = qJsonFromRawLibraryMetaData(data, fdlen, &errMsg); |
321 | 0 | if (doc.isNull()) { |
322 | 0 | qWarning("Found invalid metadata in lib %ls: %ls", |
323 | 0 | qUtf16Printable(library), qUtf16Printable(errMsg)); |
324 | 0 | } else { |
325 | 0 | lib->metaData = doc.object(); |
326 | 0 | if (qt_debug_component()) |
327 | 0 | qWarning("Found metadata in lib %s, metadata=\n%s\n", |
328 | 0 | library.toLocal8Bit().constData(), doc.toJson().constData()); |
329 | 0 | ret = !doc.isNull(); |
330 | 0 | } |
331 | 0 | } |
332 | |
|
333 | 0 | if (!ret && lib) |
334 | 0 | lib->errorString = QLibrary::tr("Failed to extract plugin meta data from '%1'").arg(library); |
335 | 0 | file.close(); |
336 | 0 | return ret; |
337 | 0 | } |
338 | | |
339 | | static void installCoverageTool(QLibraryPrivate *libPrivate) |
340 | 0 | { |
341 | | #ifdef __COVERAGESCANNER__ |
342 | | /* |
343 | | __COVERAGESCANNER__ is defined when Qt has been instrumented for code |
344 | | coverage by TestCocoon. CoverageScanner is the name of the tool that |
345 | | generates the code instrumentation. |
346 | | This code is required here when code coverage analysis with TestCocoon |
347 | | is enabled in order to allow the loading application to register the plugin |
348 | | and then store its execution report. The execution report gathers information |
349 | | about each part of the plugin's code that has been used when |
350 | | the plugin was loaded by the launching application. |
351 | | The execution report for the plugin will go to the same execution report |
352 | | as the one defined for the application loading it. |
353 | | */ |
354 | | |
355 | | int ret = __coveragescanner_register_library(libPrivate->fileName.toLocal8Bit()); |
356 | | |
357 | | if (qt_debug_component()) { |
358 | | if (ret >= 0) { |
359 | | qDebug("coverage data for %ls registered", |
360 | | qUtf16Printable(libPrivate->fileName)); |
361 | | } else { |
362 | | qWarning("could not register %ls: error %d; coverage data may be incomplete", |
363 | | qUtf16Printable(libPrivate->fileName), |
364 | | ret); |
365 | | } |
366 | | } |
367 | | #else |
368 | 0 | Q_UNUSED(libPrivate); |
369 | 0 | #endif |
370 | 0 | } |
371 | | |
372 | | class QLibraryStore |
373 | | { |
374 | | public: |
375 | | inline ~QLibraryStore(); |
376 | | static inline QLibraryPrivate *findOrCreate(const QString &fileName, const QString &version, QLibrary::LoadHints loadHints); |
377 | | static inline void releaseLibrary(QLibraryPrivate *lib); |
378 | | |
379 | | static inline void cleanup(); |
380 | | |
381 | | private: |
382 | | static inline QLibraryStore *instance(); |
383 | | |
384 | | // all members and instance() are protected by qt_library_mutex |
385 | | typedef QMap<QString, QLibraryPrivate*> LibraryMap; |
386 | | LibraryMap libraryMap; |
387 | | }; |
388 | | |
389 | | static QBasicMutex qt_library_mutex; |
390 | | static QLibraryStore *qt_library_data = nullptr; |
391 | | static bool qt_library_data_once; |
392 | | |
393 | | QLibraryStore::~QLibraryStore() |
394 | 0 | { |
395 | 0 | qt_library_data = nullptr; |
396 | 0 | } |
397 | | |
398 | | inline void QLibraryStore::cleanup() |
399 | 0 | { |
400 | 0 | QLibraryStore *data = qt_library_data; |
401 | 0 | if (!data) |
402 | 0 | return; |
403 | | |
404 | | // find any libraries that are still loaded but have a no one attached to them |
405 | 0 | LibraryMap::Iterator it = data->libraryMap.begin(); |
406 | 0 | for (; it != data->libraryMap.end(); ++it) { |
407 | 0 | QLibraryPrivate *lib = it.value(); |
408 | 0 | if (lib->libraryRefCount.loadRelaxed() == 1) { |
409 | 0 | if (lib->libraryUnloadCount.loadRelaxed() > 0) { |
410 | 0 | Q_ASSERT(lib->pHnd.loadRelaxed()); |
411 | 0 | lib->libraryUnloadCount.storeRelaxed(1); |
412 | 0 | #ifdef __GLIBC__ |
413 | | // glibc has a bug in unloading from global destructors |
414 | | // see https://bugzilla.novell.com/show_bug.cgi?id=622977 |
415 | | // and http://sourceware.org/bugzilla/show_bug.cgi?id=11941 |
416 | 0 | lib->unload(QLibraryPrivate::NoUnloadSys); |
417 | | #elif defined(Q_OS_DARWIN) |
418 | | // We cannot fully unload libraries, as we don't know if there are |
419 | | // lingering references (in system threads e.g.) to Objective-C classes |
420 | | // defined in the library. |
421 | | lib->unload(QLibraryPrivate::NoUnloadSys); |
422 | | #else |
423 | | lib->unload(); |
424 | | #endif |
425 | 0 | } |
426 | 0 | delete lib; |
427 | 0 | it.value() = 0; |
428 | 0 | } |
429 | 0 | } |
430 | |
|
431 | 0 | if (qt_debug_component()) { |
432 | | // dump all objects that remain |
433 | 0 | for (QLibraryPrivate *lib : qAsConst(data->libraryMap)) { |
434 | 0 | if (lib) |
435 | 0 | qDebug() << "On QtCore unload," << lib->fileName << "was leaked, with" |
436 | 0 | << lib->libraryRefCount.loadRelaxed() << "users"; |
437 | 0 | } |
438 | 0 | } |
439 | |
|
440 | 0 | delete data; |
441 | 0 | } |
442 | | |
443 | | static void qlibraryCleanup() |
444 | 0 | { |
445 | 0 | QLibraryStore::cleanup(); |
446 | 0 | } |
447 | | Q_DESTRUCTOR_FUNCTION(qlibraryCleanup) |
448 | | |
449 | | // must be called with a locked mutex |
450 | | QLibraryStore *QLibraryStore::instance() |
451 | 0 | { |
452 | 0 | if (Q_UNLIKELY(!qt_library_data_once && !qt_library_data)) { |
453 | | // only create once per process lifetime |
454 | 0 | qt_library_data = new QLibraryStore; |
455 | 0 | qt_library_data_once = true; |
456 | 0 | } |
457 | 0 | return qt_library_data; |
458 | 0 | } |
459 | | |
460 | | inline QLibraryPrivate *QLibraryStore::findOrCreate(const QString &fileName, const QString &version, |
461 | | QLibrary::LoadHints loadHints) |
462 | 0 | { |
463 | 0 | QMutexLocker locker(&qt_library_mutex); |
464 | 0 | QLibraryStore *data = instance(); |
465 | | |
466 | | // check if this library is already loaded |
467 | 0 | QLibraryPrivate *lib = nullptr; |
468 | 0 | if (Q_LIKELY(data)) { |
469 | 0 | lib = data->libraryMap.value(fileName); |
470 | 0 | if (lib) |
471 | 0 | lib->mergeLoadHints(loadHints); |
472 | 0 | } |
473 | 0 | if (!lib) |
474 | 0 | lib = new QLibraryPrivate(fileName, version, loadHints); |
475 | | |
476 | | // track this library |
477 | 0 | if (Q_LIKELY(data) && !fileName.isEmpty()) |
478 | 0 | data->libraryMap.insert(fileName, lib); |
479 | |
|
480 | 0 | lib->libraryRefCount.ref(); |
481 | 0 | return lib; |
482 | 0 | } |
483 | | |
484 | | inline void QLibraryStore::releaseLibrary(QLibraryPrivate *lib) |
485 | 0 | { |
486 | 0 | QMutexLocker locker(&qt_library_mutex); |
487 | 0 | QLibraryStore *data = instance(); |
488 | |
|
489 | 0 | if (lib->libraryRefCount.deref()) { |
490 | | // still in use |
491 | 0 | return; |
492 | 0 | } |
493 | | |
494 | | // no one else is using |
495 | 0 | Q_ASSERT(lib->libraryUnloadCount.loadRelaxed() == 0); |
496 | |
|
497 | 0 | if (Q_LIKELY(data) && !lib->fileName.isEmpty()) { |
498 | 0 | QLibraryPrivate *that = data->libraryMap.take(lib->fileName); |
499 | 0 | Q_ASSERT(lib == that); |
500 | 0 | Q_UNUSED(that); |
501 | 0 | } |
502 | 0 | delete lib; |
503 | 0 | } |
504 | | |
505 | | QLibraryPrivate::QLibraryPrivate(const QString &canonicalFileName, const QString &version, QLibrary::LoadHints loadHints) |
506 | | : fileName(canonicalFileName), fullVersion(version), pluginState(MightBeAPlugin) |
507 | 0 | { |
508 | 0 | loadHintsInt.storeRelaxed(loadHints); |
509 | 0 | if (canonicalFileName.isEmpty()) |
510 | 0 | errorString = QLibrary::tr("The shared library was not found."); |
511 | 0 | } |
512 | | |
513 | | QLibraryPrivate *QLibraryPrivate::findOrCreate(const QString &fileName, const QString &version, |
514 | | QLibrary::LoadHints loadHints) |
515 | 0 | { |
516 | 0 | return QLibraryStore::findOrCreate(fileName, version, loadHints); |
517 | 0 | } |
518 | | |
519 | | QLibraryPrivate::~QLibraryPrivate() |
520 | 0 | { |
521 | 0 | } |
522 | | |
523 | | void QLibraryPrivate::mergeLoadHints(QLibrary::LoadHints lh) |
524 | 0 | { |
525 | | // if the library is already loaded, we can't change the load hints |
526 | 0 | if (pHnd.loadRelaxed()) |
527 | 0 | return; |
528 | | |
529 | 0 | loadHintsInt.storeRelaxed(lh); |
530 | 0 | } |
531 | | |
532 | | QFunctionPointer QLibraryPrivate::resolve(const char *symbol) |
533 | 0 | { |
534 | 0 | if (!pHnd.loadRelaxed()) |
535 | 0 | return nullptr; |
536 | 0 | return resolve_sys(symbol); |
537 | 0 | } |
538 | | |
539 | | void QLibraryPrivate::setLoadHints(QLibrary::LoadHints lh) |
540 | 0 | { |
541 | | // this locks a global mutex |
542 | 0 | QMutexLocker lock(&qt_library_mutex); |
543 | 0 | mergeLoadHints(lh); |
544 | 0 | } |
545 | | |
546 | | QObject *QLibraryPrivate::pluginInstance() |
547 | 0 | { |
548 | | // first, check if the instance is cached and hasn't been deleted |
549 | 0 | QObject *obj = (QMutexLocker(&mutex), inst.data()); |
550 | 0 | if (obj) |
551 | 0 | return obj; |
552 | | |
553 | | // We need to call the plugin's factory function. Is that cached? |
554 | | // skip increasing the reference count (why? -Thiago) |
555 | 0 | QtPluginInstanceFunction factory = instanceFactory.loadAcquire(); |
556 | 0 | if (!factory) |
557 | 0 | factory = loadPlugin(); |
558 | |
|
559 | 0 | if (!factory) |
560 | 0 | return nullptr; |
561 | | |
562 | 0 | obj = factory(); |
563 | | |
564 | | // cache again |
565 | 0 | QMutexLocker locker(&mutex); |
566 | 0 | if (inst) |
567 | 0 | obj = inst; |
568 | 0 | else |
569 | 0 | inst = obj; |
570 | 0 | return obj; |
571 | 0 | } |
572 | | |
573 | | bool QLibraryPrivate::load() |
574 | 0 | { |
575 | 0 | if (pHnd.loadRelaxed()) { |
576 | 0 | libraryUnloadCount.ref(); |
577 | 0 | return true; |
578 | 0 | } |
579 | 0 | if (fileName.isEmpty()) |
580 | 0 | return false; |
581 | | |
582 | 0 | Q_TRACE(QLibraryPrivate_load_entry, fileName); |
583 | |
|
584 | 0 | bool ret = load_sys(); |
585 | 0 | if (qt_debug_component()) { |
586 | 0 | if (ret) { |
587 | 0 | qDebug() << "loaded library" << fileName; |
588 | 0 | } else { |
589 | 0 | qDebug() << qUtf8Printable(errorString); |
590 | 0 | } |
591 | 0 | } |
592 | 0 | if (ret) { |
593 | | //when loading a library we add a reference to it so that the QLibraryPrivate won't get deleted |
594 | | //this allows to unload the library at a later time |
595 | 0 | libraryUnloadCount.ref(); |
596 | 0 | libraryRefCount.ref(); |
597 | 0 | installCoverageTool(this); |
598 | 0 | } |
599 | |
|
600 | 0 | Q_TRACE(QLibraryPrivate_load_exit, ret); |
601 | |
|
602 | 0 | return ret; |
603 | 0 | } |
604 | | |
605 | | bool QLibraryPrivate::unload(UnloadFlag flag) |
606 | 0 | { |
607 | 0 | if (!pHnd.loadRelaxed()) |
608 | 0 | return false; |
609 | 0 | if (libraryUnloadCount.loadRelaxed() > 0 && !libraryUnloadCount.deref()) { // only unload if ALL QLibrary instance wanted to |
610 | 0 | QMutexLocker locker(&mutex); |
611 | 0 | delete inst.data(); |
612 | 0 | if (flag == NoUnloadSys || unload_sys()) { |
613 | 0 | if (qt_debug_component()) |
614 | 0 | qWarning() << "QLibraryPrivate::unload succeeded on" << fileName |
615 | 0 | << (flag == NoUnloadSys ? "(faked)" : ""); |
616 | | //when the library is unloaded, we release the reference on it so that 'this' |
617 | | //can get deleted |
618 | 0 | libraryRefCount.deref(); |
619 | 0 | pHnd.storeRelaxed(nullptr); |
620 | 0 | instanceFactory.storeRelaxed(nullptr); |
621 | 0 | return true; |
622 | 0 | } |
623 | 0 | } |
624 | | |
625 | 0 | return false; |
626 | 0 | } |
627 | | |
628 | | void QLibraryPrivate::release() |
629 | 0 | { |
630 | 0 | QLibraryStore::releaseLibrary(this); |
631 | 0 | } |
632 | | |
633 | | QtPluginInstanceFunction QLibraryPrivate::loadPlugin() |
634 | 0 | { |
635 | 0 | if (auto ptr = instanceFactory.loadAcquire()) { |
636 | 0 | libraryUnloadCount.ref(); |
637 | 0 | return ptr; |
638 | 0 | } |
639 | 0 | if (pluginState == IsNotAPlugin) |
640 | 0 | return nullptr; |
641 | 0 | if (load()) { |
642 | 0 | auto ptr = reinterpret_cast<QtPluginInstanceFunction>(resolve("qt_plugin_instance")); |
643 | 0 | instanceFactory.storeRelease(ptr); // two threads may store the same value |
644 | 0 | return ptr; |
645 | 0 | } |
646 | 0 | if (qt_debug_component()) |
647 | 0 | qWarning() << "QLibraryPrivate::loadPlugin failed on" << fileName << ":" << errorString; |
648 | 0 | pluginState = IsNotAPlugin; |
649 | 0 | return nullptr; |
650 | 0 | } |
651 | | |
652 | | /*! |
653 | | Returns \c true if \a fileName has a valid suffix for a loadable |
654 | | library; otherwise returns \c false. |
655 | | |
656 | | \table |
657 | | \header \li Platform \li Valid suffixes |
658 | | \row \li Windows \li \c .dll, \c .DLL |
659 | | \row \li Unix/Linux \li \c .so |
660 | | \row \li AIX \li \c .a |
661 | | \row \li HP-UX \li \c .sl, \c .so (HP-UXi) |
662 | | \row \li \macos and iOS \li \c .dylib, \c .bundle, \c .so |
663 | | \endtable |
664 | | |
665 | | Trailing versioning numbers on Unix are ignored. |
666 | | */ |
667 | | bool QLibrary::isLibrary(const QString &fileName) |
668 | 0 | { |
669 | | #if defined(Q_OS_WIN) |
670 | | return fileName.endsWith(QLatin1String(".dll"), Qt::CaseInsensitive); |
671 | | #else // Generic Unix |
672 | 0 | QString completeSuffix = QFileInfo(fileName).completeSuffix(); |
673 | 0 | if (completeSuffix.isEmpty()) |
674 | 0 | return false; |
675 | 0 | const QVector<QStringRef> suffixes = completeSuffix.splitRef(QLatin1Char('.')); |
676 | 0 | QStringList validSuffixList; |
677 | |
|
678 | | # if defined(Q_OS_HPUX) |
679 | | /* |
680 | | See "HP-UX Linker and Libraries User's Guide", section "Link-time Differences between PA-RISC and IPF": |
681 | | "In PA-RISC (PA-32 and PA-64) shared libraries are suffixed with .sl. In IPF (32-bit and 64-bit), |
682 | | the shared libraries are suffixed with .so. For compatibility, the IPF linker also supports the .sl suffix." |
683 | | */ |
684 | | validSuffixList << QLatin1String("sl"); |
685 | | # if defined __ia64 |
686 | | validSuffixList << QLatin1String("so"); |
687 | | # endif |
688 | | # elif defined(Q_OS_AIX) |
689 | | validSuffixList << QLatin1String("a") << QLatin1String("so"); |
690 | | # elif defined(Q_OS_DARWIN) |
691 | | // On Apple platforms, dylib look like libmylib.1.0.0.dylib |
692 | | if (suffixes.last() == QLatin1String("dylib")) |
693 | | return true; |
694 | | |
695 | | validSuffixList << QLatin1String("so") << QLatin1String("bundle"); |
696 | | # elif defined(Q_OS_UNIX) |
697 | 0 | validSuffixList << QLatin1String("so"); |
698 | 0 | # endif |
699 | | |
700 | | // Examples of valid library names: |
701 | | // libfoo.so |
702 | | // libfoo.so.0 |
703 | | // libfoo.so.0.3 |
704 | | // libfoo-0.3.so |
705 | | // libfoo-0.3.so.0.3.0 |
706 | |
|
707 | 0 | int suffix; |
708 | 0 | int suffixPos = -1; |
709 | 0 | for (suffix = 0; suffix < validSuffixList.count() && suffixPos == -1; ++suffix) |
710 | 0 | suffixPos = suffixes.indexOf(QStringRef(&validSuffixList.at(suffix))); |
711 | |
|
712 | 0 | bool valid = suffixPos != -1; |
713 | 0 | for (int i = suffixPos + 1; i < suffixes.count() && valid; ++i) |
714 | 0 | if (i != suffixPos) |
715 | 0 | suffixes.at(i).toInt(&valid); |
716 | 0 | return valid; |
717 | 0 | #endif |
718 | 0 | } |
719 | | |
720 | | static bool qt_get_metadata(QLibraryPrivate *priv, QString *errMsg) |
721 | 0 | { |
722 | 0 | #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) |
723 | 0 | auto getMetaData = [](QFunctionPointer fptr) { |
724 | 0 | auto f = reinterpret_cast<const char * (*)()>(fptr); |
725 | 0 | return qMakePair<const char *, size_t>(f(), INT_MAX); |
726 | 0 | }; |
727 | | #else |
728 | | auto getMetaData = [](QFunctionPointer fptr) { |
729 | | auto f = reinterpret_cast<QPair<const char *, size_t> (*)()>(fptr); |
730 | | return f(); |
731 | | }; |
732 | | #endif |
733 | |
|
734 | 0 | QFunctionPointer pfn = priv->resolve("qt_plugin_query_metadata"); |
735 | 0 | if (!pfn) |
736 | 0 | return false; |
737 | | |
738 | 0 | auto metaData = getMetaData(pfn); |
739 | 0 | QJsonDocument doc = qJsonFromRawLibraryMetaData(metaData.first, metaData.second, errMsg); |
740 | 0 | if (doc.isNull()) |
741 | 0 | return false; |
742 | 0 | priv->metaData = doc.object(); |
743 | 0 | return true; |
744 | 0 | } |
745 | | |
746 | | bool QLibraryPrivate::isPlugin() |
747 | 0 | { |
748 | 0 | if (pluginState == MightBeAPlugin) |
749 | 0 | updatePluginState(); |
750 | |
|
751 | 0 | return pluginState == IsAPlugin; |
752 | 0 | } |
753 | | |
754 | | void QLibraryPrivate::updatePluginState() |
755 | 0 | { |
756 | 0 | QMutexLocker locker(&mutex); |
757 | 0 | errorString.clear(); |
758 | 0 | if (pluginState != MightBeAPlugin) |
759 | 0 | return; |
760 | | |
761 | 0 | bool success = false; |
762 | |
|
763 | 0 | #if defined(Q_OS_UNIX) && !defined(Q_OS_MAC) |
764 | 0 | if (fileName.endsWith(QLatin1String(".debug"))) { |
765 | | // refuse to load a file that ends in .debug |
766 | | // these are the debug symbols from the libraries |
767 | | // the problem is that they are valid shared library files |
768 | | // and dlopen is known to crash while opening them |
769 | | |
770 | | // pretend we didn't see the file |
771 | 0 | errorString = QLibrary::tr("The shared library was not found."); |
772 | 0 | pluginState = IsNotAPlugin; |
773 | 0 | return; |
774 | 0 | } |
775 | 0 | #endif |
776 | | |
777 | 0 | if (!pHnd.loadRelaxed()) { |
778 | | // scan for the plugin metadata without loading |
779 | 0 | success = findPatternUnloaded(fileName, this); |
780 | 0 | } else { |
781 | | // library is already loaded (probably via QLibrary) |
782 | | // simply get the target function and call it. |
783 | 0 | success = qt_get_metadata(this, &errorString); |
784 | 0 | } |
785 | |
|
786 | 0 | if (!success) { |
787 | 0 | if (errorString.isEmpty()){ |
788 | 0 | if (fileName.isEmpty()) |
789 | 0 | errorString = QLibrary::tr("The shared library was not found."); |
790 | 0 | else |
791 | 0 | errorString = QLibrary::tr("The file '%1' is not a valid Qt plugin.").arg(fileName); |
792 | 0 | } |
793 | 0 | pluginState = IsNotAPlugin; |
794 | 0 | return; |
795 | 0 | } |
796 | | |
797 | 0 | pluginState = IsNotAPlugin; // be pessimistic |
798 | |
|
799 | 0 | uint qt_version = (uint)metaData.value(QLatin1String("version")).toDouble(); |
800 | 0 | bool debug = metaData.value(QLatin1String("debug")).toBool(); |
801 | 0 | if ((qt_version & 0x00ff00) > (QT_VERSION & 0x00ff00) || (qt_version & 0xff0000) != (QT_VERSION & 0xff0000)) { |
802 | 0 | if (qt_debug_component()) { |
803 | 0 | qWarning("In %s:\n" |
804 | 0 | " Plugin uses incompatible Qt library (%d.%d.%d) [%s]", |
805 | 0 | QFile::encodeName(fileName).constData(), |
806 | 0 | (qt_version&0xff0000) >> 16, (qt_version&0xff00) >> 8, qt_version&0xff, |
807 | 0 | debug ? "debug" : "release"); |
808 | 0 | } |
809 | 0 | errorString = QLibrary::tr("The plugin '%1' uses incompatible Qt library. (%2.%3.%4) [%5]") |
810 | 0 | .arg(fileName, |
811 | 0 | QString::number((qt_version & 0xff0000) >> 16), |
812 | 0 | QString::number((qt_version & 0xff00) >> 8), |
813 | 0 | QString::number(qt_version & 0xff), |
814 | 0 | debug ? QLatin1String("debug") : QLatin1String("release")); |
815 | | #ifndef QT_NO_DEBUG_PLUGIN_CHECK |
816 | | } else if(debug != QLIBRARY_AS_DEBUG) { |
817 | | //don't issue a qWarning since we will hopefully find a non-debug? --Sam |
818 | | errorString = QLibrary::tr("The plugin '%1' uses incompatible Qt library." |
819 | | " (Cannot mix debug and release libraries.)").arg(fileName); |
820 | | #endif |
821 | 0 | } else { |
822 | 0 | pluginState = IsAPlugin; |
823 | 0 | } |
824 | 0 | } |
825 | | |
826 | | /*! |
827 | | Loads the library and returns \c true if the library was loaded |
828 | | successfully; otherwise returns \c false. Since resolve() always |
829 | | calls this function before resolving any symbols it is not |
830 | | necessary to call it explicitly. In some situations you might want |
831 | | the library loaded in advance, in which case you would use this |
832 | | function. |
833 | | |
834 | | \sa unload() |
835 | | */ |
836 | | bool QLibrary::load() |
837 | 0 | { |
838 | 0 | if (!d) |
839 | 0 | return false; |
840 | 0 | if (did_load) |
841 | 0 | return d->pHnd.loadRelaxed(); |
842 | 0 | did_load = true; |
843 | 0 | return d->load(); |
844 | 0 | } |
845 | | |
846 | | /*! |
847 | | Unloads the library and returns \c true if the library could be |
848 | | unloaded; otherwise returns \c false. |
849 | | |
850 | | This happens automatically on application termination, so you |
851 | | shouldn't normally need to call this function. |
852 | | |
853 | | If other instances of QLibrary are using the same library, the |
854 | | call will fail, and unloading will only happen when every instance |
855 | | has called unload(). |
856 | | |
857 | | Note that on Mac OS X 10.3 (Panther), dynamic libraries cannot be unloaded. |
858 | | |
859 | | \sa resolve(), load() |
860 | | */ |
861 | | bool QLibrary::unload() |
862 | 0 | { |
863 | 0 | if (did_load) { |
864 | 0 | did_load = false; |
865 | 0 | return d->unload(); |
866 | 0 | } |
867 | 0 | return false; |
868 | 0 | } |
869 | | |
870 | | /*! |
871 | | Returns \c true if the library is loaded; otherwise returns \c false. |
872 | | |
873 | | \sa load() |
874 | | */ |
875 | | bool QLibrary::isLoaded() const |
876 | 0 | { |
877 | 0 | return d && d->pHnd.loadRelaxed(); |
878 | 0 | } |
879 | | |
880 | | |
881 | | /*! |
882 | | Constructs a library with the given \a parent. |
883 | | */ |
884 | | QLibrary::QLibrary(QObject *parent) |
885 | | :QObject(parent), d(nullptr), did_load(false) |
886 | 0 | { |
887 | 0 | } |
888 | | |
889 | | |
890 | | /*! |
891 | | Constructs a library object with the given \a parent that will |
892 | | load the library specified by \a fileName. |
893 | | |
894 | | We recommend omitting the file's suffix in \a fileName, since |
895 | | QLibrary will automatically look for the file with the appropriate |
896 | | suffix in accordance with the platform, e.g. ".so" on Unix, |
897 | | ".dylib" on \macos and iOS, and ".dll" on Windows. (See \l{fileName}.) |
898 | | */ |
899 | | QLibrary::QLibrary(const QString& fileName, QObject *parent) |
900 | | :QObject(parent), d(nullptr), did_load(false) |
901 | 0 | { |
902 | 0 | setFileName(fileName); |
903 | 0 | } |
904 | | |
905 | | |
906 | | /*! |
907 | | Constructs a library object with the given \a parent that will |
908 | | load the library specified by \a fileName and major version number \a verNum. |
909 | | Currently, the version number is ignored on Windows. |
910 | | |
911 | | We recommend omitting the file's suffix in \a fileName, since |
912 | | QLibrary will automatically look for the file with the appropriate |
913 | | suffix in accordance with the platform, e.g. ".so" on Unix, |
914 | | ".dylib" on \macos and iOS, and ".dll" on Windows. (See \l{fileName}.) |
915 | | */ |
916 | | QLibrary::QLibrary(const QString& fileName, int verNum, QObject *parent) |
917 | | :QObject(parent), d(nullptr), did_load(false) |
918 | 0 | { |
919 | 0 | setFileNameAndVersion(fileName, verNum); |
920 | 0 | } |
921 | | |
922 | | /*! |
923 | | Constructs a library object with the given \a parent that will |
924 | | load the library specified by \a fileName and full version number \a version. |
925 | | Currently, the version number is ignored on Windows. |
926 | | |
927 | | We recommend omitting the file's suffix in \a fileName, since |
928 | | QLibrary will automatically look for the file with the appropriate |
929 | | suffix in accordance with the platform, e.g. ".so" on Unix, |
930 | | ".dylib" on \macos and iOS, and ".dll" on Windows. (See \l{fileName}.) |
931 | | */ |
932 | | QLibrary::QLibrary(const QString& fileName, const QString &version, QObject *parent) |
933 | | :QObject(parent), d(nullptr), did_load(false) |
934 | 0 | { |
935 | 0 | setFileNameAndVersion(fileName, version); |
936 | 0 | } |
937 | | |
938 | | /*! |
939 | | Destroys the QLibrary object. |
940 | | |
941 | | Unless unload() was called explicitly, the library stays in memory |
942 | | until the application terminates. |
943 | | |
944 | | \sa isLoaded(), unload() |
945 | | */ |
946 | | QLibrary::~QLibrary() |
947 | 0 | { |
948 | 0 | if (d) |
949 | 0 | d->release(); |
950 | 0 | } |
951 | | |
952 | | |
953 | | /*! |
954 | | \property QLibrary::fileName |
955 | | \brief the file name of the library |
956 | | |
957 | | We recommend omitting the file's suffix in the file name, since |
958 | | QLibrary will automatically look for the file with the appropriate |
959 | | suffix (see isLibrary()). |
960 | | |
961 | | When loading the library, QLibrary searches in all system-specific |
962 | | library locations (for example, \c LD_LIBRARY_PATH on Unix), unless the |
963 | | file name has an absolute path. After loading the library |
964 | | successfully, fileName() returns the fully-qualified file name of |
965 | | the library, including the full path to the library if one was given |
966 | | in the constructor or passed to setFileName(). |
967 | | |
968 | | For example, after successfully loading the "GL" library on Unix |
969 | | platforms, fileName() will return "libGL.so". If the file name was |
970 | | originally passed as "/usr/lib/libGL", fileName() will return |
971 | | "/usr/lib/libGL.so". |
972 | | */ |
973 | | |
974 | | void QLibrary::setFileName(const QString &fileName) |
975 | 0 | { |
976 | 0 | QLibrary::LoadHints lh; |
977 | 0 | if (d) { |
978 | 0 | lh = d->loadHints(); |
979 | 0 | d->release(); |
980 | 0 | d = nullptr; |
981 | 0 | did_load = false; |
982 | 0 | } |
983 | 0 | d = QLibraryPrivate::findOrCreate(fileName, QString(), lh); |
984 | 0 | } |
985 | | |
986 | | QString QLibrary::fileName() const |
987 | 0 | { |
988 | 0 | if (d) { |
989 | 0 | QMutexLocker locker(&d->mutex); |
990 | 0 | return d->qualifiedFileName.isEmpty() ? d->fileName : d->qualifiedFileName; |
991 | 0 | } |
992 | 0 | return QString(); |
993 | 0 | } |
994 | | |
995 | | /*! |
996 | | \fn void QLibrary::setFileNameAndVersion(const QString &fileName, int versionNumber) |
997 | | |
998 | | Sets the fileName property and major version number to \a fileName |
999 | | and \a versionNumber respectively. |
1000 | | The \a versionNumber is ignored on Windows. |
1001 | | |
1002 | | \sa setFileName() |
1003 | | */ |
1004 | | void QLibrary::setFileNameAndVersion(const QString &fileName, int verNum) |
1005 | 0 | { |
1006 | 0 | QLibrary::LoadHints lh; |
1007 | 0 | if (d) { |
1008 | 0 | lh = d->loadHints(); |
1009 | 0 | d->release(); |
1010 | 0 | d = nullptr; |
1011 | 0 | did_load = false; |
1012 | 0 | } |
1013 | 0 | d = QLibraryPrivate::findOrCreate(fileName, verNum >= 0 ? QString::number(verNum) : QString(), lh); |
1014 | 0 | } |
1015 | | |
1016 | | /*! |
1017 | | \since 4.4 |
1018 | | |
1019 | | Sets the fileName property and full version number to \a fileName |
1020 | | and \a version respectively. |
1021 | | The \a version parameter is ignored on Windows. |
1022 | | |
1023 | | \sa setFileName() |
1024 | | */ |
1025 | | void QLibrary::setFileNameAndVersion(const QString &fileName, const QString &version) |
1026 | 0 | { |
1027 | 0 | QLibrary::LoadHints lh; |
1028 | 0 | if (d) { |
1029 | 0 | lh = d->loadHints(); |
1030 | 0 | d->release(); |
1031 | 0 | d = nullptr; |
1032 | 0 | did_load = false; |
1033 | 0 | } |
1034 | 0 | d = QLibraryPrivate::findOrCreate(fileName, version, lh); |
1035 | 0 | } |
1036 | | |
1037 | | /*! |
1038 | | Returns the address of the exported symbol \a symbol. The library is |
1039 | | loaded if necessary. The function returns \nullptr if the symbol could |
1040 | | not be resolved or if the library could not be loaded. |
1041 | | |
1042 | | Example: |
1043 | | \snippet code/src_corelib_plugin_qlibrary.cpp 2 |
1044 | | |
1045 | | The symbol must be exported as a C function from the library. This |
1046 | | means that the function must be wrapped in an \c{extern "C"} if |
1047 | | the library is compiled with a C++ compiler. On Windows you must |
1048 | | also explicitly export the function from the DLL using the |
1049 | | \c{__declspec(dllexport)} compiler directive, for example: |
1050 | | |
1051 | | \snippet code/src_corelib_plugin_qlibrary.cpp 3 |
1052 | | |
1053 | | with \c MY_EXPORT defined as |
1054 | | |
1055 | | \snippet code/src_corelib_plugin_qlibrary.cpp 4 |
1056 | | */ |
1057 | | QFunctionPointer QLibrary::resolve(const char *symbol) |
1058 | 0 | { |
1059 | 0 | if (!isLoaded() && !load()) |
1060 | 0 | return nullptr; |
1061 | 0 | return d->resolve(symbol); |
1062 | 0 | } |
1063 | | |
1064 | | /*! |
1065 | | \overload |
1066 | | |
1067 | | Loads the library \a fileName and returns the address of the |
1068 | | exported symbol \a symbol. Note that \a fileName should not |
1069 | | include the platform-specific file suffix; (see \l{fileName}). The |
1070 | | library remains loaded until the application exits. |
1071 | | |
1072 | | The function returns \nullptr if the symbol could not be resolved or if |
1073 | | the library could not be loaded. |
1074 | | |
1075 | | \sa resolve() |
1076 | | */ |
1077 | | QFunctionPointer QLibrary::resolve(const QString &fileName, const char *symbol) |
1078 | 0 | { |
1079 | 0 | QLibrary library(fileName); |
1080 | 0 | return library.resolve(symbol); |
1081 | 0 | } |
1082 | | |
1083 | | /*! |
1084 | | \overload |
1085 | | |
1086 | | Loads the library \a fileName with major version number \a verNum and |
1087 | | returns the address of the exported symbol \a symbol. |
1088 | | Note that \a fileName should not include the platform-specific file suffix; |
1089 | | (see \l{fileName}). The library remains loaded until the application exits. |
1090 | | \a verNum is ignored on Windows. |
1091 | | |
1092 | | The function returns \nullptr if the symbol could not be resolved or if |
1093 | | the library could not be loaded. |
1094 | | |
1095 | | \sa resolve() |
1096 | | */ |
1097 | | QFunctionPointer QLibrary::resolve(const QString &fileName, int verNum, const char *symbol) |
1098 | 0 | { |
1099 | 0 | QLibrary library(fileName, verNum); |
1100 | 0 | return library.resolve(symbol); |
1101 | 0 | } |
1102 | | |
1103 | | /*! |
1104 | | \overload |
1105 | | \since 4.4 |
1106 | | |
1107 | | Loads the library \a fileName with full version number \a version and |
1108 | | returns the address of the exported symbol \a symbol. |
1109 | | Note that \a fileName should not include the platform-specific file suffix; |
1110 | | (see \l{fileName}). The library remains loaded until the application exits. |
1111 | | \a version is ignored on Windows. |
1112 | | |
1113 | | The function returns \nullptr if the symbol could not be resolved or if |
1114 | | the library could not be loaded. |
1115 | | |
1116 | | \sa resolve() |
1117 | | */ |
1118 | | QFunctionPointer QLibrary::resolve(const QString &fileName, const QString &version, const char *symbol) |
1119 | 0 | { |
1120 | 0 | QLibrary library(fileName, version); |
1121 | 0 | return library.resolve(symbol); |
1122 | 0 | } |
1123 | | |
1124 | | /*! |
1125 | | \since 4.2 |
1126 | | |
1127 | | Returns a text string with the description of the last error that occurred. |
1128 | | Currently, errorString will only be set if load(), unload() or resolve() for some reason fails. |
1129 | | */ |
1130 | | QString QLibrary::errorString() const |
1131 | 0 | { |
1132 | 0 | QString str; |
1133 | 0 | if (d) { |
1134 | 0 | QMutexLocker locker(&d->mutex); |
1135 | 0 | str = d->errorString; |
1136 | 0 | } |
1137 | 0 | return str.isEmpty() ? tr("Unknown error") : str; |
1138 | 0 | } |
1139 | | |
1140 | | /*! |
1141 | | \property QLibrary::loadHints |
1142 | | \brief Give the load() function some hints on how it should behave. |
1143 | | |
1144 | | You can give some hints on how the symbols are resolved. Usually, |
1145 | | the symbols are not resolved at load time, but resolved lazily, |
1146 | | (that is, when resolve() is called). If you set the loadHints to |
1147 | | ResolveAllSymbolsHint, then all symbols will be resolved at load time |
1148 | | if the platform supports it. |
1149 | | |
1150 | | Setting ExportExternalSymbolsHint will make the external symbols in the |
1151 | | library available for resolution in subsequent loaded libraries. |
1152 | | |
1153 | | If LoadArchiveMemberHint is set, the file name |
1154 | | is composed of two components: A path which is a reference to an |
1155 | | archive file followed by the second component which is the reference to |
1156 | | the archive member. For instance, the fileName \c libGL.a(shr_64.o) will refer |
1157 | | to the library \c shr_64.o in the archive file named \c libGL.a. This |
1158 | | is only supported on the AIX platform. |
1159 | | |
1160 | | The interpretation of the load hints is platform dependent, and if |
1161 | | you use it you are probably making some assumptions on which platform |
1162 | | you are compiling for, so use them only if you understand the consequences |
1163 | | of them. |
1164 | | |
1165 | | By default, none of these flags are set, so libraries will be loaded with |
1166 | | lazy symbol resolution, and will not export external symbols for resolution |
1167 | | in other dynamically-loaded libraries. |
1168 | | |
1169 | | \note Setting this property after the library has been loaded has no effect |
1170 | | and loadHints() will not reflect those changes. |
1171 | | |
1172 | | \note This property is shared among all QLibrary instances that refer to |
1173 | | the same library. |
1174 | | */ |
1175 | | void QLibrary::setLoadHints(LoadHints hints) |
1176 | 0 | { |
1177 | 0 | if (!d) { |
1178 | 0 | d = QLibraryPrivate::findOrCreate(QString()); // ugly, but we need a d-ptr |
1179 | 0 | d->errorString.clear(); |
1180 | 0 | } |
1181 | 0 | d->setLoadHints(hints); |
1182 | 0 | } |
1183 | | |
1184 | | QLibrary::LoadHints QLibrary::loadHints() const |
1185 | 0 | { |
1186 | 0 | return d ? d->loadHints() : QLibrary::LoadHints(); |
1187 | 0 | } |
1188 | | |
1189 | | /* Internal, for debugging */ |
1190 | | bool qt_debug_component() |
1191 | 0 | { |
1192 | 0 | static int debug_env = QT_PREPEND_NAMESPACE(qEnvironmentVariableIntValue)("QT_DEBUG_PLUGINS"); |
1193 | 0 | return debug_env != 0; |
1194 | 0 | } |
1195 | | |
1196 | | QT_END_NAMESPACE |
1197 | | |
1198 | | #include "moc_qlibrary.cpp" |