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

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

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