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