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

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