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 
69 QT_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 
qt_find_pattern(const char * s,qsizetype s_len,const char * pattern,ulong p_len)189 static 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 */
findPatternUnloaded(const QString & library,QLibraryPrivate * lib)237 static 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 = 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 
installCoverageTool(QLibraryPrivate * libPrivate)339 static 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 
372 class QLibraryStore
373 {
374 public:
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 
381 private:
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 
389 static QBasicMutex qt_library_mutex;
390 static QLibraryStore *qt_library_data = nullptr;
391 static bool qt_library_data_once;
392 
~QLibraryStore()393 QLibraryStore::~QLibraryStore()
394 {
395     qt_library_data = nullptr;
396 }
397 
cleanup()398 inline 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() = 0;
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 
qlibraryCleanup()438 static void qlibraryCleanup()
439 {
440     QLibraryStore::cleanup();
441 }
Q_DESTRUCTOR_FUNCTION(qlibraryCleanup)442 Q_DESTRUCTOR_FUNCTION(qlibraryCleanup)
443 
444 // must be called with a locked mutex
445 QLibraryStore *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 
findOrCreate(const QString & fileName,const QString & version,QLibrary::LoadHints loadHints)455 inline 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 
releaseLibrary(QLibraryPrivate * lib)479 inline 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 
QLibraryPrivate(const QString & canonicalFileName,const QString & version,QLibrary::LoadHints loadHints)500 QLibraryPrivate::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 
findOrCreate(const QString & fileName,const QString & version,QLibrary::LoadHints loadHints)508 QLibraryPrivate *QLibraryPrivate::findOrCreate(const QString &fileName, const QString &version,
509                                                QLibrary::LoadHints loadHints)
510 {
511     return QLibraryStore::findOrCreate(fileName, version, loadHints);
512 }
513 
~QLibraryPrivate()514 QLibraryPrivate::~QLibraryPrivate()
515 {
516 }
517 
mergeLoadHints(QLibrary::LoadHints lh)518 void 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 
resolve(const char * symbol)527 QFunctionPointer QLibraryPrivate::resolve(const char *symbol)
528 {
529     if (!pHnd.loadRelaxed())
530         return nullptr;
531     return resolve_sys(symbol);
532 }
533 
setLoadHints(QLibrary::LoadHints lh)534 void QLibraryPrivate::setLoadHints(QLibrary::LoadHints lh)
535 {
536     // this locks a global mutex
537     QMutexLocker lock(&qt_library_mutex);
538     mergeLoadHints(lh);
539 }
540 
pluginInstance()541 QObject *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 
load()568 bool 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 
unload(UnloadFlag flag)600 bool 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 
release()623 void QLibraryPrivate::release()
624 {
625     QLibraryStore::releaseLibrary(this);
626 }
627 
loadPlugin()628 QtPluginInstanceFunction 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  */
isLibrary(const QString & fileName)662 bool 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 QVector<QStringRef> suffixes = completeSuffix.splitRef(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(QStringRef(&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             suffixes.at(i).toInt(&valid);
711     return valid;
712 #endif
713 }
714 
qt_get_metadata(QLibraryPrivate * priv,QString * errMsg)715 static bool qt_get_metadata(QLibraryPrivate *priv, QString *errMsg)
716 {
717 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
718     auto getMetaData = [](QFunctionPointer fptr) {
719         auto f = reinterpret_cast<const char * (*)()>(fptr);
720         return qMakePair<const char *, size_t>(f(), INT_MAX);
721     };
722 #else
723     auto getMetaData = [](QFunctionPointer fptr) {
724         auto f = reinterpret_cast<QPair<const char *, size_t> (*)()>(fptr);
725         return f();
726     };
727 #endif
728 
729     QFunctionPointer pfn = priv->resolve("qt_plugin_query_metadata");
730     if (!pfn)
731         return false;
732 
733     auto metaData = getMetaData(pfn);
734     QJsonDocument doc = qJsonFromRawLibraryMetaData(metaData.first, metaData.second, errMsg);
735     if (doc.isNull())
736         return false;
737     priv->metaData = doc.object();
738     return true;
739 }
740 
isPlugin()741 bool QLibraryPrivate::isPlugin()
742 {
743     if (pluginState == MightBeAPlugin)
744         updatePluginState();
745 
746     return pluginState == IsAPlugin;
747 }
748 
updatePluginState()749 void QLibraryPrivate::updatePluginState()
750 {
751     QMutexLocker locker(&mutex);
752     errorString.clear();
753     if (pluginState != MightBeAPlugin)
754         return;
755 
756     bool success = false;
757 
758 #if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
759     if (fileName.endsWith(QLatin1String(".debug"))) {
760         // refuse to load a file that ends in .debug
761         // these are the debug symbols from the libraries
762         // the problem is that they are valid shared library files
763         // and dlopen is known to crash while opening them
764 
765         // pretend we didn't see the file
766         errorString = QLibrary::tr("The shared library was not found.");
767         pluginState = IsNotAPlugin;
768         return;
769     }
770 #endif
771 
772     if (!pHnd.loadRelaxed()) {
773         // scan for the plugin metadata without loading
774         success = findPatternUnloaded(fileName, this);
775     } else {
776         // library is already loaded (probably via QLibrary)
777         // simply get the target function and call it.
778         success = qt_get_metadata(this, &errorString);
779     }
780 
781     if (!success) {
782         if (errorString.isEmpty()){
783             if (fileName.isEmpty())
784                 errorString = QLibrary::tr("The shared library was not found.");
785             else
786                 errorString = QLibrary::tr("The file '%1' is not a valid Qt plugin.").arg(fileName);
787         }
788         pluginState = IsNotAPlugin;
789         return;
790     }
791 
792     pluginState = IsNotAPlugin; // be pessimistic
793 
794     uint qt_version = (uint)metaData.value(QLatin1String("version")).toDouble();
795     bool debug = metaData.value(QLatin1String("debug")).toBool();
796     if ((qt_version & 0x00ff00) > (QT_VERSION & 0x00ff00) || (qt_version & 0xff0000) != (QT_VERSION & 0xff0000)) {
797         if (qt_debug_component()) {
798             qWarning("In %s:\n"
799                  "  Plugin uses incompatible Qt library (%d.%d.%d) [%s]",
800                  QFile::encodeName(fileName).constData(),
801                  (qt_version&0xff0000) >> 16, (qt_version&0xff00) >> 8, qt_version&0xff,
802                  debug ? "debug" : "release");
803         }
804         errorString = QLibrary::tr("The plugin '%1' uses incompatible Qt library. (%2.%3.%4) [%5]")
805             .arg(fileName)
806             .arg((qt_version&0xff0000) >> 16)
807             .arg((qt_version&0xff00) >> 8)
808             .arg(qt_version&0xff)
809             .arg(debug ? QLatin1String("debug") : QLatin1String("release"));
810 #ifndef QT_NO_DEBUG_PLUGIN_CHECK
811     } else if(debug != QLIBRARY_AS_DEBUG) {
812         //don't issue a qWarning since we will hopefully find a non-debug? --Sam
813         errorString = QLibrary::tr("The plugin '%1' uses incompatible Qt library."
814                  " (Cannot mix debug and release libraries.)").arg(fileName);
815 #endif
816     } else {
817         pluginState = IsAPlugin;
818     }
819 }
820 
821 /*!
822     Loads the library and returns \c true if the library was loaded
823     successfully; otherwise returns \c false. Since resolve() always
824     calls this function before resolving any symbols it is not
825     necessary to call it explicitly. In some situations you might want
826     the library loaded in advance, in which case you would use this
827     function.
828 
829     \sa unload()
830 */
load()831 bool QLibrary::load()
832 {
833     if (!d)
834         return false;
835     if (did_load)
836         return d->pHnd.loadRelaxed();
837     did_load = true;
838     return d->load();
839 }
840 
841 /*!
842     Unloads the library and returns \c true if the library could be
843     unloaded; otherwise returns \c false.
844 
845     This happens automatically on application termination, so you
846     shouldn't normally need to call this function.
847 
848     If other instances of QLibrary are using the same library, the
849     call will fail, and unloading will only happen when every instance
850     has called unload().
851 
852     Note that on Mac OS X 10.3 (Panther), dynamic libraries cannot be unloaded.
853 
854     \sa resolve(), load()
855 */
unload()856 bool QLibrary::unload()
857 {
858     if (did_load) {
859         did_load = false;
860         return d->unload();
861     }
862     return false;
863 }
864 
865 /*!
866     Returns \c true if the library is loaded; otherwise returns \c false.
867 
868     \sa load()
869  */
isLoaded() const870 bool QLibrary::isLoaded() const
871 {
872     return d && d->pHnd.loadRelaxed();
873 }
874 
875 
876 /*!
877     Constructs a library with the given \a parent.
878  */
QLibrary(QObject * parent)879 QLibrary::QLibrary(QObject *parent)
880     :QObject(parent), d(nullptr), did_load(false)
881 {
882 }
883 
884 
885 /*!
886     Constructs a library object with the given \a parent that will
887     load the library specified by \a fileName.
888 
889     We recommend omitting the file's suffix in \a fileName, since
890     QLibrary will automatically look for the file with the appropriate
891     suffix in accordance with the platform, e.g. ".so" on Unix,
892     ".dylib" on \macos and iOS, and ".dll" on Windows. (See \l{fileName}.)
893  */
QLibrary(const QString & fileName,QObject * parent)894 QLibrary::QLibrary(const QString& fileName, QObject *parent)
895     :QObject(parent), d(nullptr), did_load(false)
896 {
897     setFileName(fileName);
898 }
899 
900 
901 /*!
902     Constructs a library object with the given \a parent that will
903     load the library specified by \a fileName and major version number \a verNum.
904     Currently, the version number is ignored on Windows.
905 
906     We recommend omitting the file's suffix in \a fileName, since
907     QLibrary will automatically look for the file with the appropriate
908     suffix in accordance with the platform, e.g. ".so" on Unix,
909     ".dylib" on \macos and iOS, and ".dll" on Windows. (See \l{fileName}.)
910 */
QLibrary(const QString & fileName,int verNum,QObject * parent)911 QLibrary::QLibrary(const QString& fileName, int verNum, QObject *parent)
912     :QObject(parent), d(nullptr), did_load(false)
913 {
914     setFileNameAndVersion(fileName, verNum);
915 }
916 
917 /*!
918     Constructs a library object with the given \a parent that will
919     load the library specified by \a fileName and full version number \a version.
920     Currently, the version number is ignored on Windows.
921 
922     We recommend omitting the file's suffix in \a fileName, since
923     QLibrary will automatically look for the file with the appropriate
924     suffix in accordance with the platform, e.g. ".so" on Unix,
925     ".dylib" on \macos and iOS, and ".dll" on Windows. (See \l{fileName}.)
926  */
QLibrary(const QString & fileName,const QString & version,QObject * parent)927 QLibrary::QLibrary(const QString& fileName, const QString &version, QObject *parent)
928     :QObject(parent), d(nullptr), did_load(false)
929 {
930     setFileNameAndVersion(fileName, version);
931 }
932 
933 /*!
934     Destroys the QLibrary object.
935 
936     Unless unload() was called explicitly, the library stays in memory
937     until the application terminates.
938 
939     \sa isLoaded(), unload()
940 */
~QLibrary()941 QLibrary::~QLibrary()
942 {
943     if (d)
944         d->release();
945 }
946 
947 
948 /*!
949     \property QLibrary::fileName
950     \brief the file name of the library
951 
952     We recommend omitting the file's suffix in the file name, since
953     QLibrary will automatically look for the file with the appropriate
954     suffix (see isLibrary()).
955 
956     When loading the library, QLibrary searches in all system-specific
957     library locations (for example, \c LD_LIBRARY_PATH on Unix), unless the
958     file name has an absolute path. After loading the library
959     successfully, fileName() returns the fully-qualified file name of
960     the library, including the full path to the library if one was given
961     in the constructor or passed to setFileName().
962 
963     For example, after successfully loading the "GL" library on Unix
964     platforms, fileName() will return "libGL.so". If the file name was
965     originally passed as "/usr/lib/libGL", fileName() will return
966     "/usr/lib/libGL.so".
967 */
968 
setFileName(const QString & fileName)969 void QLibrary::setFileName(const QString &fileName)
970 {
971     QLibrary::LoadHints lh;
972     if (d) {
973         lh = d->loadHints();
974         d->release();
975         d = nullptr;
976         did_load = false;
977     }
978     d = QLibraryPrivate::findOrCreate(fileName, QString(), lh);
979 }
980 
fileName() const981 QString QLibrary::fileName() const
982 {
983     if (d) {
984         QMutexLocker locker(&d->mutex);
985         return d->qualifiedFileName.isEmpty() ? d->fileName : d->qualifiedFileName;
986     }
987     return QString();
988 }
989 
990 /*!
991     \fn void QLibrary::setFileNameAndVersion(const QString &fileName, int versionNumber)
992 
993     Sets the fileName property and major version number to \a fileName
994     and \a versionNumber respectively.
995     The \a versionNumber is ignored on Windows.
996 
997     \sa setFileName()
998 */
setFileNameAndVersion(const QString & fileName,int verNum)999 void QLibrary::setFileNameAndVersion(const QString &fileName, int verNum)
1000 {
1001     QLibrary::LoadHints lh;
1002     if (d) {
1003         lh = d->loadHints();
1004         d->release();
1005         d = nullptr;
1006         did_load = false;
1007     }
1008     d = QLibraryPrivate::findOrCreate(fileName, verNum >= 0 ? QString::number(verNum) : QString(), lh);
1009 }
1010 
1011 /*!
1012     \since 4.4
1013 
1014     Sets the fileName property and full version number to \a fileName
1015     and \a version respectively.
1016     The \a version parameter is ignored on Windows.
1017 
1018     \sa setFileName()
1019 */
setFileNameAndVersion(const QString & fileName,const QString & version)1020 void QLibrary::setFileNameAndVersion(const QString &fileName, const QString &version)
1021 {
1022     QLibrary::LoadHints lh;
1023     if (d) {
1024         lh = d->loadHints();
1025         d->release();
1026         d = nullptr;
1027         did_load = false;
1028     }
1029     d = QLibraryPrivate::findOrCreate(fileName, version, lh);
1030 }
1031 
1032 /*!
1033     Returns the address of the exported symbol \a symbol. The library is
1034     loaded if necessary. The function returns \nullptr if the symbol could
1035     not be resolved or if the library could not be loaded.
1036 
1037     Example:
1038     \snippet code/src_corelib_plugin_qlibrary.cpp 2
1039 
1040     The symbol must be exported as a C function from the library. This
1041     means that the function must be wrapped in an \c{extern "C"} if
1042     the library is compiled with a C++ compiler. On Windows you must
1043     also explicitly export the function from the DLL using the
1044     \c{__declspec(dllexport)} compiler directive, for example:
1045 
1046     \snippet code/src_corelib_plugin_qlibrary.cpp 3
1047 
1048     with \c MY_EXPORT defined as
1049 
1050     \snippet code/src_corelib_plugin_qlibrary.cpp 4
1051 */
resolve(const char * symbol)1052 QFunctionPointer QLibrary::resolve(const char *symbol)
1053 {
1054     if (!isLoaded() && !load())
1055         return nullptr;
1056     return d->resolve(symbol);
1057 }
1058 
1059 /*!
1060     \overload
1061 
1062     Loads the library \a fileName and returns the address of the
1063     exported symbol \a symbol. Note that \a fileName should not
1064     include the platform-specific file suffix; (see \l{fileName}). The
1065     library remains loaded until the application exits.
1066 
1067     The function returns \nullptr if the symbol could not be resolved or if
1068     the library could not be loaded.
1069 
1070     \sa resolve()
1071 */
resolve(const QString & fileName,const char * symbol)1072 QFunctionPointer QLibrary::resolve(const QString &fileName, const char *symbol)
1073 {
1074     QLibrary library(fileName);
1075     return library.resolve(symbol);
1076 }
1077 
1078 /*!
1079     \overload
1080 
1081     Loads the library \a fileName with major version number \a verNum and
1082     returns the address of the exported symbol \a symbol.
1083     Note that \a fileName should not include the platform-specific file suffix;
1084     (see \l{fileName}). The library remains loaded until the application exits.
1085     \a verNum is ignored on Windows.
1086 
1087     The function returns \nullptr if the symbol could not be resolved or if
1088     the library could not be loaded.
1089 
1090     \sa resolve()
1091 */
resolve(const QString & fileName,int verNum,const char * symbol)1092 QFunctionPointer QLibrary::resolve(const QString &fileName, int verNum, const char *symbol)
1093 {
1094     QLibrary library(fileName, verNum);
1095     return library.resolve(symbol);
1096 }
1097 
1098 /*!
1099     \overload
1100     \since 4.4
1101 
1102     Loads the library \a fileName with full version number \a version and
1103     returns the address of the exported symbol \a symbol.
1104     Note that \a fileName should not include the platform-specific file suffix;
1105     (see \l{fileName}). The library remains loaded until the application exits.
1106     \a version is ignored on Windows.
1107 
1108     The function returns \nullptr if the symbol could not be resolved or if
1109     the library could not be loaded.
1110 
1111     \sa resolve()
1112 */
resolve(const QString & fileName,const QString & version,const char * symbol)1113 QFunctionPointer QLibrary::resolve(const QString &fileName, const QString &version, const char *symbol)
1114 {
1115     QLibrary library(fileName, version);
1116     return library.resolve(symbol);
1117 }
1118 
1119 /*!
1120     \since 4.2
1121 
1122     Returns a text string with the description of the last error that occurred.
1123     Currently, errorString will only be set if load(), unload() or resolve() for some reason fails.
1124 */
errorString() const1125 QString QLibrary::errorString() const
1126 {
1127     QString str;
1128     if (d) {
1129         QMutexLocker locker(&d->mutex);
1130         str = d->errorString;
1131     }
1132     return str.isEmpty() ? tr("Unknown error") : str;
1133 }
1134 
1135 /*!
1136     \property QLibrary::loadHints
1137     \brief Give the load() function some hints on how it should behave.
1138 
1139     You can give some hints on how the symbols are resolved. Usually,
1140     the symbols are not resolved at load time, but resolved lazily,
1141     (that is, when resolve() is called). If you set the loadHints to
1142     ResolveAllSymbolsHint, then all symbols will be resolved at load time
1143     if the platform supports it.
1144 
1145     Setting ExportExternalSymbolsHint will make the external symbols in the
1146     library available for resolution in subsequent loaded libraries.
1147 
1148     If LoadArchiveMemberHint is set, the file name
1149     is composed of two components: A path which is a reference to an
1150     archive file followed by the second component which is the reference to
1151     the archive member. For instance, the fileName \c libGL.a(shr_64.o) will refer
1152     to the library \c shr_64.o in the archive file named \c libGL.a. This
1153     is only supported on the AIX platform.
1154 
1155     The interpretation of the load hints is platform dependent, and if
1156     you use it you are probably making some assumptions on which platform
1157     you are compiling for, so use them only if you understand the consequences
1158     of them.
1159 
1160     By default, none of these flags are set, so libraries will be loaded with
1161     lazy symbol resolution, and will not export external symbols for resolution
1162     in other dynamically-loaded libraries.
1163 
1164     \note Setting this property after the library has been loaded has no effect
1165     and loadHints() will not reflect those changes.
1166 
1167     \note This property is shared among all QLibrary instances that refer to
1168     the same library.
1169 */
setLoadHints(LoadHints hints)1170 void QLibrary::setLoadHints(LoadHints hints)
1171 {
1172     if (!d) {
1173         d = QLibraryPrivate::findOrCreate(QString());   // ugly, but we need a d-ptr
1174         d->errorString.clear();
1175     }
1176     d->setLoadHints(hints);
1177 }
1178 
loadHints() const1179 QLibrary::LoadHints QLibrary::loadHints() const
1180 {
1181     return d ? d->loadHints() : QLibrary::LoadHints();
1182 }
1183 
1184 /* Internal, for debugging */
qt_debug_component()1185 bool qt_debug_component()
1186 {
1187     static int debug_env = QT_PREPEND_NAMESPACE(qEnvironmentVariableIntValue)("QT_DEBUG_PLUGINS");
1188     return debug_env != 0;
1189 }
1190 
1191 QT_END_NAMESPACE
1192 
1193 #include "moc_qlibrary.cpp"
1194