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

source code of qtbase/src/corelib/plugin/qlibrary.cpp