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