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 <qlist.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(s + i, pattern, 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(QIODevice::ReadOnly)) {
241 if (lib)
242 lib->errorString = file.errorString();
243 if (qt_debug_component()) {
244 qWarning("%s: %ls", QFile::encodeName(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(file.size(), MaxMemoryMapSize);
257 const char *filedata = reinterpret_cast<char *>(file.map(0, fdlen));
258
259 if (filedata == nullptr) {
260 // Try reading the data into memory instead (up to 64 MB).
261 data = file.read(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 = ulong(qstrlen(pattern));
274#if defined (Q_OF_ELF) && defined(Q_CC_GNU)
275 int r = QElfParser().parse(filedata, fdlen, library, lib, &pos, &fdlen);
276 if (r == QElfParser::Corrupt || r == QElfParser::NotElf) {
277 if (lib && qt_debug_component()) {
278 qWarning("QElfParser: %ls", qUtf16Printable(lib->errorString));
279 }
280 return false;
281 } else if (r == QElfParser::QtMetaDataSection) {
282 qsizetype rel = qt_find_pattern(filedata + pos, fdlen, pattern, 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(data, fdlen, &errMsg);
321 if (doc.isNull()) {
322 qWarning("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("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("Failed to extract plugin meta data from '%1'").arg(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(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(QLibraryPrivate::NoUnloadSys);
417#else
418 lib->unload();
419#endif
420 }
421 delete lib;
422 it.value() = nullptr;
423 }
424 }
425
426 if (qt_debug_component()) {
427 // dump all objects that remain
428 for (QLibraryPrivate *lib : qAsConst(data->libraryMap)) {
429 if (lib)
430 qDebug() << "On QtCore unload," << lib->fileName << "was leaked, with"
431 << lib->libraryRefCount.loadRelaxed() << "users";
432 }
433 }
434
435 delete data;
436}
437
438static void qlibraryCleanup()
439{
440 QLibraryStore::cleanup();
441}
442Q_DESTRUCTOR_FUNCTION(qlibraryCleanup)
443
444// must be called with a locked mutex
445QLibraryStore *QLibraryStore::instance()
446{
447 if (Q_UNLIKELY(!qt_library_data_once && !qt_library_data)) {
448 // only create once per process lifetime
449 qt_library_data = new QLibraryStore;
450 qt_library_data_once = true;
451 }
452 return qt_library_data;
453}
454
455inline QLibraryPrivate *QLibraryStore::findOrCreate(const QString &fileName, const QString &version,
456 QLibrary::LoadHints loadHints)
457{
458 QMutexLocker locker(&qt_library_mutex);
459 QLibraryStore *data = instance();
460
461 // check if this library is already loaded
462 QLibraryPrivate *lib = nullptr;
463 if (Q_LIKELY(data)) {
464 lib = data->libraryMap.value(fileName);
465 if (lib)
466 lib->mergeLoadHints(loadHints);
467 }
468 if (!lib)
469 lib = new QLibraryPrivate(fileName, version, loadHints);
470
471 // track this library
472 if (Q_LIKELY(data) && !fileName.isEmpty())
473 data->libraryMap.insert(fileName, lib);
474
475 lib->libraryRefCount.ref();
476 return lib;
477}
478
479inline void QLibraryStore::releaseLibrary(QLibraryPrivate *lib)
480{
481 QMutexLocker locker(&qt_library_mutex);
482 QLibraryStore *data = instance();
483
484 if (lib->libraryRefCount.deref()) {
485 // still in use
486 return;
487 }
488
489 // no one else is using
490 Q_ASSERT(lib->libraryUnloadCount.loadRelaxed() == 0);
491
492 if (Q_LIKELY(data) && !lib->fileName.isEmpty()) {
493 QLibraryPrivate *that = data->libraryMap.take(lib->fileName);
494 Q_ASSERT(lib == that);
495 Q_UNUSED(that);
496 }
497 delete lib;
498}
499
500QLibraryPrivate::QLibraryPrivate(const QString &canonicalFileName, const QString &version, QLibrary::LoadHints loadHints)
501 : fileName(canonicalFileName), fullVersion(version), pluginState(MightBeAPlugin)
502{
503 loadHintsInt.storeRelaxed(loadHints);
504 if (canonicalFileName.isEmpty())
505 errorString = QLibrary::tr("The shared library was not found.");
506}
507
508QLibraryPrivate *QLibraryPrivate::findOrCreate(const QString &fileName, const QString &version,
509 QLibrary::LoadHints loadHints)
510{
511 return QLibraryStore::findOrCreate(fileName, version, loadHints);
512}
513
514QLibraryPrivate::~QLibraryPrivate()
515{
516}
517
518void QLibraryPrivate::mergeLoadHints(QLibrary::LoadHints lh)
519{
520 // if the library is already loaded, we can't change the load hints
521 if (pHnd.loadRelaxed())
522 return;
523
524 loadHintsInt.storeRelaxed(lh);
525}
526
527QFunctionPointer QLibraryPrivate::resolve(const char *symbol)
528{
529 if (!pHnd.loadRelaxed())
530 return nullptr;
531 return resolve_sys(symbol);
532}
533
534void QLibraryPrivate::setLoadHints(QLibrary::LoadHints lh)
535{
536 // this locks a global mutex
537 QMutexLocker lock(&qt_library_mutex);
538 mergeLoadHints(lh);
539}
540
541QObject *QLibraryPrivate::pluginInstance()
542{
543 // first, check if the instance is cached and hasn't been deleted
544 QObject *obj = (QMutexLocker(&mutex), inst.data());
545 if (obj)
546 return obj;
547
548 // We need to call the plugin's factory function. Is that cached?
549 // skip increasing the reference count (why? -Thiago)
550 QtPluginInstanceFunction factory = instanceFactory.loadAcquire();
551 if (!factory)
552 factory = loadPlugin();
553
554 if (!factory)
555 return nullptr;
556
557 obj = factory();
558
559 // cache again
560 QMutexLocker locker(&mutex);
561 if (inst)
562 obj = inst;
563 else
564 inst = obj;
565 return obj;
566}
567
568bool QLibraryPrivate::load()
569{
570 if (pHnd.loadRelaxed()) {
571 libraryUnloadCount.ref();
572 return true;
573 }
574 if (fileName.isEmpty())
575 return false;
576
577 Q_TRACE(QLibraryPrivate_load_entry, fileName);
578
579 bool ret = load_sys();
580 if (qt_debug_component()) {
581 if (ret) {
582 qDebug() << "loaded library" << fileName;
583 } else {
584 qDebug() << qUtf8Printable(errorString);
585 }
586 }
587 if (ret) {
588 //when loading a library we add a reference to it so that the QLibraryPrivate won't get deleted
589 //this allows to unload the library at a later time
590 libraryUnloadCount.ref();
591 libraryRefCount.ref();
592 installCoverageTool(this);
593 }
594
595 Q_TRACE(QLibraryPrivate_load_exit, ret);
596
597 return ret;
598}
599
600bool QLibraryPrivate::unload(UnloadFlag flag)
601{
602 if (!pHnd.loadRelaxed())
603 return false;
604 if (libraryUnloadCount.loadRelaxed() > 0 && !libraryUnloadCount.deref()) { // only unload if ALL QLibrary instance wanted to
605 QMutexLocker locker(&mutex);
606 delete inst.data();
607 if (flag == NoUnloadSys || unload_sys()) {
608 if (qt_debug_component())
609 qWarning() << "QLibraryPrivate::unload succeeded on" << fileName
610 << (flag == NoUnloadSys ? "(faked)" : "");
611 // when the library is unloaded, we release the reference on it so that 'this'
612 // can get deleted
613 libraryRefCount.deref();
614 pHnd.storeRelaxed(nullptr);
615 instanceFactory.storeRelaxed(nullptr);
616 return true;
617 }
618 }
619
620 return false;
621}
622
623void QLibraryPrivate::release()
624{
625 QLibraryStore::releaseLibrary(this);
626}
627
628QtPluginInstanceFunction QLibraryPrivate::loadPlugin()
629{
630 if (auto ptr = instanceFactory.loadAcquire()) {
631 libraryUnloadCount.ref();
632 return ptr;
633 }
634 if (pluginState == IsNotAPlugin)
635 return nullptr;
636 if (load()) {
637 auto ptr = reinterpret_cast<QtPluginInstanceFunction>(resolve("qt_plugin_instance"));
638 instanceFactory.storeRelease(ptr); // two threads may store the same value
639 return ptr;
640 }
641 if (qt_debug_component())
642 qWarning() << "QLibraryPrivate::loadPlugin failed on" << fileName << ":" << errorString;
643 pluginState = IsNotAPlugin;
644 return nullptr;
645}
646
647/*!
648 Returns \c true if \a fileName has a valid suffix for a loadable
649 library; otherwise returns \c false.
650
651 \table
652 \header \li Platform \li Valid suffixes
653 \row \li Windows \li \c .dll, \c .DLL
654 \row \li Unix/Linux \li \c .so
655 \row \li AIX \li \c .a
656 \row \li HP-UX \li \c .sl, \c .so (HP-UXi)
657 \row \li \macos and iOS \li \c .dylib, \c .bundle, \c .so
658 \endtable
659
660 Trailing versioning numbers on Unix are ignored.
661 */
662bool QLibrary::isLibrary(const QString &fileName)
663{
664#if defined(Q_OS_WIN)
665 return fileName.endsWith(QLatin1String(".dll"), Qt::CaseInsensitive);
666#else // Generic Unix
667 QString completeSuffix = QFileInfo(fileName).completeSuffix();
668 if (completeSuffix.isEmpty())
669 return false;
670 const auto suffixes = QStringView{completeSuffix}.split(QLatin1Char('.'));
671 QStringList validSuffixList;
672
673# if defined(Q_OS_HPUX)
674/*
675 See "HP-UX Linker and Libraries User's Guide", section "Link-time Differences between PA-RISC and IPF":
676 "In PA-RISC (PA-32 and PA-64) shared libraries are suffixed with .sl. In IPF (32-bit and 64-bit),
677 the shared libraries are suffixed with .so. For compatibility, the IPF linker also supports the .sl suffix."
678 */
679 validSuffixList << QLatin1String("sl");
680# if defined __ia64
681 validSuffixList << QLatin1String("so");
682# endif
683# elif defined(Q_OS_AIX)
684 validSuffixList << QLatin1String("a") << QLatin1String("so");
685# elif defined(Q_OS_DARWIN)
686 // On Apple platforms, dylib look like libmylib.1.0.0.dylib
687 if (suffixes.last() == QLatin1String("dylib"))
688 return true;
689
690 validSuffixList << QLatin1String("so") << QLatin1String("bundle");
691# elif defined(Q_OS_UNIX)
692 validSuffixList << QLatin1String("so");
693# endif
694
695 // Examples of valid library names:
696 // libfoo.so
697 // libfoo.so.0
698 // libfoo.so.0.3
699 // libfoo-0.3.so
700 // libfoo-0.3.so.0.3.0
701
702 int suffix;
703 int suffixPos = -1;
704 for (suffix = 0; suffix < validSuffixList.count() && suffixPos == -1; ++suffix)
705 suffixPos = suffixes.indexOf(validSuffixList.at(suffix));
706
707 bool valid = suffixPos != -1;
708 for (int i = suffixPos + 1; i < suffixes.count() && valid; ++i)
709 if (i != suffixPos)
710 (void)suffixes.at(i).toInt(&valid);
711 return valid;
712#endif
713}
714
715static bool qt_get_metadata(QLibraryPrivate *priv, QString *errMsg)
716{
717 auto getMetaData = [](QFunctionPointer fptr) {
718 auto f = reinterpret_cast<QPluginMetaData (*)()>(fptr);
719 return f();
720 };
721
722 QFunctionPointer pfn = priv->resolve("qt_plugin_query_metadata");
723 if (!pfn)
724 return false;
725
726 auto metaData = getMetaData(pfn);
727 QJsonDocument doc = qJsonFromRawLibraryMetaData(reinterpret_cast<const char *>(metaData.data), metaData.size, errMsg);
728 if (doc.isNull())
729 return false;
730 priv->metaData = doc.object();
731 return true;
732}
733
734bool QLibraryPrivate::isPlugin()
735{
736 if (pluginState == MightBeAPlugin)
737 updatePluginState();
738
739 return pluginState == IsAPlugin;
740}
741
742void QLibraryPrivate::updatePluginState()
743{
744 QMutexLocker locker(&mutex);
745 errorString.clear();
746 if (pluginState != MightBeAPlugin)
747 return;
748
749 bool success = false;
750
751#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
752 if (fileName.endsWith(QLatin1String(".debug"))) {
753 // refuse to load a file that ends in .debug
754 // these are the debug symbols from the libraries
755 // the problem is that they are valid shared library files
756 // and dlopen is known to crash while opening them
757
758 // pretend we didn't see the file
759 errorString = QLibrary::tr("The shared library was not found.");
760 pluginState = IsNotAPlugin;
761 return;
762 }
763#endif
764
765 if (!pHnd.loadRelaxed()) {
766 // scan for the plugin metadata without loading
767 success = findPatternUnloaded(fileName, this);
768 } else {
769 // library is already loaded (probably via QLibrary)
770 // simply get the target function and call it.
771 success = qt_get_metadata(this, &errorString);
772 }
773
774 if (!success) {
775 if (errorString.isEmpty()) {
776 if (fileName.isEmpty())
777 errorString = QLibrary::tr("The shared library was not found.");
778 else
779 errorString = QLibrary::tr("The file '%1' is not a valid Qt plugin.").arg(fileName);
780 }
781 pluginState = IsNotAPlugin;
782 return;
783 }
784
785 pluginState = IsNotAPlugin; // be pessimistic
786
787 uint qt_version = (uint)metaData.value(QLatin1String("version")).toDouble();
788 bool debug = metaData.value(QLatin1String("debug")).toBool();
789 if ((qt_version & 0x00ff00) > (QT_VERSION & 0x00ff00) || (qt_version & 0xff0000) != (QT_VERSION & 0xff0000)) {
790 if (qt_debug_component()) {
791 qWarning("In %s:\n"
792 " Plugin uses incompatible Qt library (%d.%d.%d) [%s]",
793 QFile::encodeName(fileName).constData(),
794 (qt_version&0xff0000) >> 16, (qt_version&0xff00) >> 8, qt_version&0xff,
795 debug ? "debug" : "release");
796 }
797 errorString = QLibrary::tr("The plugin '%1' uses incompatible Qt library. (%2.%3.%4) [%5]")
798 .arg(fileName)
799 .arg((qt_version&0xff0000) >> 16)
800 .arg((qt_version&0xff00) >> 8)
801 .arg(qt_version&0xff)
802 .arg(debug ? QLatin1String("debug") : QLatin1String("release"));
803#ifndef QT_NO_DEBUG_PLUGIN_CHECK
804 } else if(debug != QLIBRARY_AS_DEBUG) {
805 //don't issue a qWarning since we will hopefully find a non-debug? --Sam
806 errorString = QLibrary::tr("The plugin '%1' uses incompatible Qt library."
807 " (Cannot mix debug and release libraries.)").arg(fileName);
808#endif
809 } else {
810 pluginState = IsAPlugin;
811 }
812}
813
814/*!
815 Loads the library and returns \c true if the library was loaded
816 successfully; otherwise returns \c false. Since resolve() always
817 calls this function before resolving any symbols it is not
818 necessary to call it explicitly. In some situations you might want
819 the library loaded in advance, in which case you would use this
820 function.
821
822 \sa unload()
823*/
824bool QLibrary::load()
825{
826 if (!d)
827 return false;
828 if (d.tag() == Loaded)
829 return d->pHnd.loadRelaxed();
830 else
831 d.setTag(Loaded);
832 return d->load();
833}
834
835/*!
836 Unloads the library and returns \c true if the library could be
837 unloaded; otherwise returns \c false.
838
839 This happens automatically on application termination, so you
840 shouldn't normally need to call this function.
841
842 If other instances of QLibrary are using the same library, the
843 call will fail, and unloading will only happen when every instance
844 has called unload().
845
846 Note that on Mac OS X 10.3 (Panther), dynamic libraries cannot be unloaded.
847
848 \sa resolve(), load()
849*/
850bool QLibrary::unload()
851{
852 if (d.tag() == Loaded) {
853 d.setTag(NotLoaded);
854 return d->unload();
855 }
856 return false;
857}
858
859/*!
860 Returns \c true if the library is loaded; otherwise returns \c false.
861
862 \sa load()
863 */
864bool QLibrary::isLoaded() const
865{
866 return d && d->pHnd.loadRelaxed();
867}
868
869
870/*!
871 Constructs a library with the given \a parent.
872 */
873QLibrary::QLibrary(QObject *parent) : QObject(parent)
874{
875}
876
877
878/*!
879 Constructs a library object with the given \a parent that will
880 load the library specified by \a fileName.
881
882 We recommend omitting the file's suffix in \a fileName, since
883 QLibrary will automatically look for the file with the appropriate
884 suffix in accordance with the platform, e.g. ".so" on Unix,
885 ".dylib" on \macos and iOS, and ".dll" on Windows. (See \l{fileName}.)
886 */
887QLibrary::QLibrary(const QString &fileName, QObject *parent) : QObject(parent)
888{
889 setFileName(fileName);
890}
891
892/*!
893 Constructs a library object with the given \a parent that will
894 load the library specified by \a fileName and major version number \a verNum.
895 Currently, the version number is ignored on Windows.
896
897 We recommend omitting the file's suffix in \a fileName, since
898 QLibrary will automatically look for the file with the appropriate
899 suffix in accordance with the platform, e.g. ".so" on Unix,
900 ".dylib" on \macos and iOS, and ".dll" on Windows. (See \l{fileName}.)
901*/
902QLibrary::QLibrary(const QString &fileName, int verNum, QObject *parent) : QObject(parent)
903{
904 setFileNameAndVersion(fileName, verNum);
905}
906
907/*!
908 Constructs a library object with the given \a parent that will
909 load the library specified by \a fileName and full version number \a version.
910 Currently, the version number is ignored on Windows.
911
912 We recommend omitting the file's suffix in \a fileName, since
913 QLibrary will automatically look for the file with the appropriate
914 suffix in accordance with the platform, e.g. ".so" on Unix,
915 ".dylib" on \macos and iOS, and ".dll" on Windows. (See \l{fileName}.)
916 */
917QLibrary::QLibrary(const QString &fileName, const QString &version, QObject *parent)
918 : QObject(parent)
919{
920 setFileNameAndVersion(fileName, version);
921}
922
923/*!
924 Destroys the QLibrary object.
925
926 Unless unload() was called explicitly, the library stays in memory
927 until the application terminates.
928
929 \sa isLoaded(), unload()
930*/
931QLibrary::~QLibrary()
932{
933 if (d)
934 d->release();
935}
936
937/*!
938 \property QLibrary::fileName
939 \brief the file name of the library
940
941 We recommend omitting the file's suffix in the file name, since
942 QLibrary will automatically look for the file with the appropriate
943 suffix (see isLibrary()).
944
945 When loading the library, QLibrary searches in all system-specific
946 library locations (for example, \c LD_LIBRARY_PATH on Unix), unless the
947 file name has an absolute path. After loading the library
948 successfully, fileName() returns the fully-qualified file name of
949 the library, including the full path to the library if one was given
950 in the constructor or passed to setFileName().
951
952 For example, after successfully loading the "GL" library on Unix
953 platforms, fileName() will return "libGL.so". If the file name was
954 originally passed as "/usr/lib/libGL", fileName() will return
955 "/usr/lib/libGL.so".
956*/
957
958void QLibrary::setFileName(const QString &fileName)
959{
960 QLibrary::LoadHints lh;
961 if (d) {
962 lh = d->loadHints();
963 d->release();
964 d = {};
965 }
966 d = QLibraryPrivate::findOrCreate(fileName, QString(), lh);
967}
968
969QString QLibrary::fileName() const
970{
971 if (d) {
972 QMutexLocker locker(&d->mutex);
973 return d->qualifiedFileName.isEmpty() ? d->fileName : d->qualifiedFileName;
974 }
975 return QString();
976}
977
978/*!
979 \fn void QLibrary::setFileNameAndVersion(const QString &fileName, int versionNumber)
980
981 Sets the fileName property and major version number to \a fileName
982 and \a versionNumber respectively.
983 The \a versionNumber is ignored on Windows.
984
985 \sa setFileName()
986*/
987void QLibrary::setFileNameAndVersion(const QString &fileName, int verNum)
988{
989 QLibrary::LoadHints lh;
990 if (d) {
991 lh = d->loadHints();
992 d->release();
993 d = {};
994 }
995 d = QLibraryPrivate::findOrCreate(fileName, verNum >= 0 ? QString::number(verNum) : QString(), lh);
996}
997
998/*!
999 \since 4.4
1000
1001 Sets the fileName property and full version number to \a fileName
1002 and \a version respectively.
1003 The \a version parameter is ignored on Windows.
1004
1005 \sa setFileName()
1006*/
1007void QLibrary::setFileNameAndVersion(const QString &fileName, const QString &version)
1008{
1009 QLibrary::LoadHints lh;
1010 if (d) {
1011 lh = d->loadHints();
1012 d->release();
1013 d = {};
1014 }
1015 d = QLibraryPrivate::findOrCreate(fileName, version, lh);
1016}
1017
1018/*!
1019 Returns the address of the exported symbol \a symbol. The library is
1020 loaded if necessary. The function returns \nullptr if the symbol could
1021 not be resolved or if the library could not be loaded.
1022
1023 Example:
1024 \snippet code/src_corelib_plugin_qlibrary.cpp 2
1025
1026 The symbol must be exported as a C function from the library. This
1027 means that the function must be wrapped in an \c{extern "C"} if
1028 the library is compiled with a C++ compiler. On Windows you must
1029 also explicitly export the function from the DLL using the
1030 \c{__declspec(dllexport)} compiler directive, for example:
1031
1032 \snippet code/src_corelib_plugin_qlibrary.cpp 3
1033
1034 with \c MY_EXPORT defined as
1035
1036 \snippet code/src_corelib_plugin_qlibrary.cpp 4
1037*/
1038QFunctionPointer QLibrary::resolve(const char *symbol)
1039{
1040 if (!isLoaded() && !load())
1041 return nullptr;
1042 return d->resolve(symbol);
1043}
1044
1045/*!
1046 \overload
1047
1048 Loads the library \a fileName and returns the address of the
1049 exported symbol \a symbol. Note that \a fileName should not
1050 include the platform-specific file suffix; (see \l{fileName}). The
1051 library remains loaded until the application exits.
1052
1053 The function returns \nullptr if the symbol could not be resolved or if
1054 the library could not be loaded.
1055
1056 \sa resolve()
1057*/
1058QFunctionPointer QLibrary::resolve(const QString &fileName, const char *symbol)
1059{
1060 QLibrary library(fileName);
1061 return library.resolve(symbol);
1062}
1063
1064/*!
1065 \overload
1066
1067 Loads the library \a fileName with major version number \a verNum and
1068 returns the address of the exported symbol \a symbol.
1069 Note that \a fileName should not include the platform-specific file suffix;
1070 (see \l{fileName}). The library remains loaded until the application exits.
1071 \a verNum is ignored on Windows.
1072
1073 The function returns \nullptr if the symbol could not be resolved or if
1074 the library could not be loaded.
1075
1076 \sa resolve()
1077*/
1078QFunctionPointer QLibrary::resolve(const QString &fileName, int verNum, const char *symbol)
1079{
1080 QLibrary library(fileName, verNum);
1081 return library.resolve(symbol);
1082}
1083
1084/*!
1085 \overload
1086 \since 4.4
1087
1088 Loads the library \a fileName with full version number \a version and
1089 returns the address of the exported symbol \a symbol.
1090 Note that \a fileName should not include the platform-specific file suffix;
1091 (see \l{fileName}). The library remains loaded until the application exits.
1092 \a version is ignored on Windows.
1093
1094 The function returns \nullptr if the symbol could not be resolved or if
1095 the library could not be loaded.
1096
1097 \sa resolve()
1098*/
1099QFunctionPointer QLibrary::resolve(const QString &fileName, const QString &version, const char *symbol)
1100{
1101 QLibrary library(fileName, version);
1102 return library.resolve(symbol);
1103}
1104
1105/*!
1106 \since 4.2
1107
1108 Returns a text string with the description of the last error that occurred.
1109 Currently, errorString will only be set if load(), unload() or resolve() for some reason fails.
1110*/
1111QString QLibrary::errorString() const
1112{
1113 QString str;
1114 if (d) {
1115 QMutexLocker locker(&d->mutex);
1116 str = d->errorString;
1117 }
1118 return str.isEmpty() ? tr("Unknown error") : str;
1119}
1120
1121/*!
1122 \property QLibrary::loadHints
1123 \brief Give the load() function some hints on how it should behave.
1124
1125 You can give some hints on how the symbols are resolved. Usually,
1126 the symbols are not resolved at load time, but resolved lazily,
1127 (that is, when resolve() is called). If you set the loadHints to
1128 ResolveAllSymbolsHint, then all symbols will be resolved at load time
1129 if the platform supports it.
1130
1131 Setting ExportExternalSymbolsHint will make the external symbols in the
1132 library available for resolution in subsequent loaded libraries.
1133
1134 If LoadArchiveMemberHint is set, the file name
1135 is composed of two components: A path which is a reference to an
1136 archive file followed by the second component which is the reference to
1137 the archive member. For instance, the fileName \c libGL.a(shr_64.o) will refer
1138 to the library \c shr_64.o in the archive file named \c libGL.a. This
1139 is only supported on the AIX platform.
1140
1141 The interpretation of the load hints is platform dependent, and if
1142 you use it you are probably making some assumptions on which platform
1143 you are compiling for, so use them only if you understand the consequences
1144 of them.
1145
1146 By default, none of these flags are set, so libraries will be loaded with
1147 lazy symbol resolution, and will not export external symbols for resolution
1148 in other dynamically-loaded libraries.
1149
1150 \note Setting this property after the library has been loaded has no effect
1151 and loadHints() will not reflect those changes.
1152
1153 \note This property is shared among all QLibrary instances that refer to
1154 the same library.
1155*/
1156void QLibrary::setLoadHints(LoadHints hints)
1157{
1158 if (!d) {
1159 d = QLibraryPrivate::findOrCreate(QString()); // ugly, but we need a d-ptr
1160 d->errorString.clear();
1161 }
1162 d->setLoadHints(hints);
1163}
1164
1165QLibrary::LoadHints QLibrary::loadHints() const
1166{
1167 return d ? d->loadHints() : QLibrary::LoadHints();
1168}
1169
1170/* Internal, for debugging */
1171bool qt_debug_component()
1172{
1173 static int debug_env = QT_PREPEND_NAMESPACE(qEnvironmentVariableIntValue)("QT_DEBUG_PLUGINS");
1174 return debug_env != 0;
1175}
1176
1177QT_END_NAMESPACE
1178
1179#include "moc_qlibrary.cpp"
1180