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 | |
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 | |
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 | */ |
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 = 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 | |
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 | |
393 | QLibraryStore::~QLibraryStore() |
394 | { |
395 | qt_library_data = nullptr; |
396 | } |
397 | |
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() = 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 | |
438 | static void qlibraryCleanup() |
439 | { |
440 | QLibraryStore::cleanup(); |
441 | } |
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 | |
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 | |
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 | |
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 | |
508 | QLibraryPrivate *QLibraryPrivate::findOrCreate(const QString &fileName, const QString &version, |
509 | QLibrary::LoadHints loadHints) |
510 | { |
511 | return QLibraryStore::findOrCreate(fileName, version, loadHints); |
512 | } |
513 | |
514 | QLibraryPrivate::~QLibraryPrivate() |
515 | { |
516 | } |
517 | |
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 | |
527 | QFunctionPointer QLibraryPrivate::resolve(const char *symbol) |
528 | { |
529 | if (!pHnd.loadRelaxed()) |
530 | return nullptr; |
531 | return resolve_sys(symbol); |
532 | } |
533 | |
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 | |
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 | |
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 | |
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 | |
623 | void QLibraryPrivate::release() |
624 | { |
625 | QLibraryStore::releaseLibrary(this); |
626 | } |
627 | |
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 | */ |
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 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 | |
715 | static 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 | |
734 | bool QLibraryPrivate::isPlugin() |
735 | { |
736 | if (pluginState == MightBeAPlugin) |
737 | updatePluginState(); |
738 | |
739 | return pluginState == IsAPlugin; |
740 | } |
741 | |
742 | void 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 | */ |
824 | bool 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 | */ |
850 | bool 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 | */ |
864 | bool QLibrary::isLoaded() const |
865 | { |
866 | return d && d->pHnd.loadRelaxed(); |
867 | } |
868 | |
869 | |
870 | /*! |
871 | Constructs a library with the given \a parent. |
872 | */ |
873 | QLibrary::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 | */ |
887 | QLibrary::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 | */ |
902 | QLibrary::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 | */ |
917 | QLibrary::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 | */ |
931 | QLibrary::~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 | |
958 | void 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 | |
969 | QString 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 | */ |
987 | void 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 | */ |
1007 | void 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 | */ |
1038 | QFunctionPointer 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 | */ |
1058 | QFunctionPointer 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 | */ |
1078 | QFunctionPointer 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 | */ |
1099 | QFunctionPointer 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 | */ |
1111 | QString 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 | */ |
1156 | void 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 | |
1165 | QLibrary::LoadHints QLibrary::loadHints() const |
1166 | { |
1167 | return d ? d->loadHints() : QLibrary::LoadHints(); |
1168 | } |
1169 | |
1170 | /* Internal, for debugging */ |
1171 | bool qt_debug_component() |
1172 | { |
1173 | static int debug_env = QT_PREPEND_NAMESPACE(qEnvironmentVariableIntValue)("QT_DEBUG_PLUGINS" ); |
1174 | return debug_env != 0; |
1175 | } |
1176 | |
1177 | QT_END_NAMESPACE |
1178 | |
1179 | #include "moc_qlibrary.cpp" |
1180 | |