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