Coverage Report

Created: 2024-02-25 06:10

/src/qtbase/src/corelib/plugin/qlibrary.cpp
Line
Count
Source (jump to first uncovered line)
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
0
{
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
0
    if (!s || !pattern || qsizetype(p_len) > s_len)
205
0
        return -1;
206
207
0
    size_t i, hs = 0, hp = 0, delta = s_len - p_len;
208
209
0
    for (i = 0; i < p_len; ++i) {
210
0
        hs += s[delta + i];
211
0
        hp += pattern[i];
212
0
    }
213
0
    i = delta;
214
0
    for (;;) {
215
0
        if (hs == hp && qstrncmp(s + i, pattern, p_len) == 0)
216
0
            return i;   // can't overflow, by construction
217
0
        if (i == 0)
218
0
            break;
219
0
        --i;
220
0
        hs -= s[i + p_len];
221
0
        hs += s[i];
222
0
    }
223
224
0
    return -1;
225
0
}
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
0
{
239
0
    QFile file(library);
240
0
    if (!file.open(QIODevice::ReadOnly)) {
241
0
        if (lib)
242
0
            lib->errorString = file.errorString();
243
0
        if (qt_debug_component()) {
244
0
            qWarning("%s: %ls", QFile::encodeName(library).constData(),
245
0
                     qUtf16Printable(QSystemError::stdString()));
246
0
        }
247
0
        return false;
248
0
    }
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
0
    constexpr qint64 MaxMemoryMapSize =
253
0
            Q_INT64_C(1) << (sizeof(qsizetype) > 4 ? 40 : 29);
254
255
0
    QByteArray data;
256
0
    qsizetype fdlen = qMin(file.size(), MaxMemoryMapSize);
257
0
    const char *filedata = reinterpret_cast<char *>(file.map(0, fdlen));
258
259
0
    if (filedata == nullptr) {
260
        // Try reading the data into memory instead (up to 64 MB).
261
0
        data = file.read(64 * 1024 * 1024);
262
0
        filedata = data.constData();
263
0
        fdlen = data.size();
264
0
    }
265
266
    /*
267
       ELF and Mach-O binaries with GCC have .qplugin sections.
268
    */
269
0
    bool hasMetaData = false;
270
0
    qsizetype pos = 0;
271
0
    char pattern[] = "qTMETADATA ";
272
0
    pattern[0] = 'Q'; // Ensure the pattern "QTMETADATA" is not found in this library should QPluginLoader ever encounter it.
273
0
    const ulong plen = qstrlen(pattern);
274
0
#if defined (Q_OF_ELF) && defined(Q_CC_GNU)
275
0
    int r = QElfParser().parse(filedata, fdlen, library, lib, &pos, &fdlen);
276
0
    if (r == QElfParser::Corrupt || r == QElfParser::NotElf) {
277
0
            if (lib && qt_debug_component()) {
278
0
                qWarning("QElfParser: %ls", qUtf16Printable(lib->errorString));
279
0
            }
280
0
            return false;
281
0
    } else if (r == QElfParser::QtMetaDataSection) {
282
0
        qsizetype rel = qt_find_pattern(filedata + pos, fdlen, pattern, plen);
283
0
        if (rel < 0)
284
0
            pos = -1;
285
0
        else
286
0
            pos += rel;
287
0
        hasMetaData = true;
288
0
    }
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
0
    bool ret = false;
316
317
0
    if (pos >= 0 && hasMetaData) {
318
0
        const char *data = filedata + pos;
319
0
        QString errMsg;
320
0
        QJsonDocument doc = qJsonFromRawLibraryMetaData(data, fdlen, &errMsg);
321
0
        if (doc.isNull()) {
322
0
            qWarning("Found invalid metadata in lib %ls: %ls",
323
0
                     qUtf16Printable(library), qUtf16Printable(errMsg));
324
0
        } else {
325
0
            lib->metaData = doc.object();
326
0
            if (qt_debug_component())
327
0
                qWarning("Found metadata in lib %s, metadata=\n%s\n",
328
0
                         library.toLocal8Bit().constData(), doc.toJson().constData());
329
0
            ret = !doc.isNull();
330
0
        }
331
0
    }
332
333
0
    if (!ret && lib)
334
0
        lib->errorString = QLibrary::tr("Failed to extract plugin meta data from '%1'").arg(library);
335
0
    file.close();
336
0
    return ret;
337
0
}
338
339
static void installCoverageTool(QLibraryPrivate *libPrivate)
340
0
{
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
0
    Q_UNUSED(libPrivate);
369
0
#endif
370
0
}
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
0
{
395
0
    qt_library_data = nullptr;
396
0
}
397
398
inline void QLibraryStore::cleanup()
399
0
{
400
0
    QLibraryStore *data = qt_library_data;
401
0
    if (!data)
402
0
        return;
403
404
    // find any libraries that are still loaded but have a no one attached to them
405
0
    LibraryMap::Iterator it = data->libraryMap.begin();
406
0
    for (; it != data->libraryMap.end(); ++it) {
407
0
        QLibraryPrivate *lib = it.value();
408
0
        if (lib->libraryRefCount.loadRelaxed() == 1) {
409
0
            if (lib->libraryUnloadCount.loadRelaxed() > 0) {
410
0
                Q_ASSERT(lib->pHnd.loadRelaxed());
411
0
                lib->libraryUnloadCount.storeRelaxed(1);
412
0
#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
0
                lib->unload(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
0
            }
426
0
            delete lib;
427
0
            it.value() = 0;
428
0
        }
429
0
    }
430
431
0
    if (qt_debug_component()) {
432
        // dump all objects that remain
433
0
        for (QLibraryPrivate *lib : qAsConst(data->libraryMap)) {
434
0
            if (lib)
435
0
                qDebug() << "On QtCore unload," << lib->fileName << "was leaked, with"
436
0
                         << lib->libraryRefCount.loadRelaxed() << "users";
437
0
        }
438
0
    }
439
440
0
    delete data;
441
0
}
442
443
static void qlibraryCleanup()
444
0
{
445
0
    QLibraryStore::cleanup();
446
0
}
447
Q_DESTRUCTOR_FUNCTION(qlibraryCleanup)
448
449
// must be called with a locked mutex
450
QLibraryStore *QLibraryStore::instance()
451
0
{
452
0
    if (Q_UNLIKELY(!qt_library_data_once && !qt_library_data)) {
453
        // only create once per process lifetime
454
0
        qt_library_data = new QLibraryStore;
455
0
        qt_library_data_once = true;
456
0
    }
457
0
    return qt_library_data;
458
0
}
459
460
inline QLibraryPrivate *QLibraryStore::findOrCreate(const QString &fileName, const QString &version,
461
                                                    QLibrary::LoadHints loadHints)
462
0
{
463
0
    QMutexLocker locker(&qt_library_mutex);
464
0
    QLibraryStore *data = instance();
465
466
    // check if this library is already loaded
467
0
    QLibraryPrivate *lib = nullptr;
468
0
    if (Q_LIKELY(data)) {
469
0
        lib = data->libraryMap.value(fileName);
470
0
        if (lib)
471
0
            lib->mergeLoadHints(loadHints);
472
0
    }
473
0
    if (!lib)
474
0
        lib = new QLibraryPrivate(fileName, version, loadHints);
475
476
    // track this library
477
0
    if (Q_LIKELY(data) && !fileName.isEmpty())
478
0
        data->libraryMap.insert(fileName, lib);
479
480
0
    lib->libraryRefCount.ref();
481
0
    return lib;
482
0
}
483
484
inline void QLibraryStore::releaseLibrary(QLibraryPrivate *lib)
485
0
{
486
0
    QMutexLocker locker(&qt_library_mutex);
487
0
    QLibraryStore *data = instance();
488
489
0
    if (lib->libraryRefCount.deref()) {
490
        // still in use
491
0
        return;
492
0
    }
493
494
    // no one else is using
495
0
    Q_ASSERT(lib->libraryUnloadCount.loadRelaxed() == 0);
496
497
0
    if (Q_LIKELY(data) && !lib->fileName.isEmpty()) {
498
0
        QLibraryPrivate *that = data->libraryMap.take(lib->fileName);
499
0
        Q_ASSERT(lib == that);
500
0
        Q_UNUSED(that);
501
0
    }
502
0
    delete lib;
503
0
}
504
505
QLibraryPrivate::QLibraryPrivate(const QString &canonicalFileName, const QString &version, QLibrary::LoadHints loadHints)
506
    : fileName(canonicalFileName), fullVersion(version), pluginState(MightBeAPlugin)
507
0
{
508
0
    loadHintsInt.storeRelaxed(loadHints);
509
0
    if (canonicalFileName.isEmpty())
510
0
        errorString = QLibrary::tr("The shared library was not found.");
511
0
}
512
513
QLibraryPrivate *QLibraryPrivate::findOrCreate(const QString &fileName, const QString &version,
514
                                               QLibrary::LoadHints loadHints)
515
0
{
516
0
    return QLibraryStore::findOrCreate(fileName, version, loadHints);
517
0
}
518
519
QLibraryPrivate::~QLibraryPrivate()
520
0
{
521
0
}
522
523
void QLibraryPrivate::mergeLoadHints(QLibrary::LoadHints lh)
524
0
{
525
    // if the library is already loaded, we can't change the load hints
526
0
    if (pHnd.loadRelaxed())
527
0
        return;
528
529
0
    loadHintsInt.storeRelaxed(lh);
530
0
}
531
532
QFunctionPointer QLibraryPrivate::resolve(const char *symbol)
533
0
{
534
0
    if (!pHnd.loadRelaxed())
535
0
        return nullptr;
536
0
    return resolve_sys(symbol);
537
0
}
538
539
void QLibraryPrivate::setLoadHints(QLibrary::LoadHints lh)
540
0
{
541
    // this locks a global mutex
542
0
    QMutexLocker lock(&qt_library_mutex);
543
0
    mergeLoadHints(lh);
544
0
}
545
546
QObject *QLibraryPrivate::pluginInstance()
547
0
{
548
    // first, check if the instance is cached and hasn't been deleted
549
0
    QObject *obj = (QMutexLocker(&mutex), inst.data());
550
0
    if (obj)
551
0
        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
0
    QtPluginInstanceFunction factory = instanceFactory.loadAcquire();
556
0
    if (!factory)
557
0
        factory = loadPlugin();
558
559
0
    if (!factory)
560
0
        return nullptr;
561
562
0
    obj = factory();
563
564
    // cache again
565
0
    QMutexLocker locker(&mutex);
566
0
    if (inst)
567
0
        obj = inst;
568
0
    else
569
0
        inst = obj;
570
0
    return obj;
571
0
}
572
573
bool QLibraryPrivate::load()
574
0
{
575
0
    if (pHnd.loadRelaxed()) {
576
0
        libraryUnloadCount.ref();
577
0
        return true;
578
0
    }
579
0
    if (fileName.isEmpty())
580
0
        return false;
581
582
0
    Q_TRACE(QLibraryPrivate_load_entry, fileName);
583
584
0
    bool ret = load_sys();
585
0
    if (qt_debug_component()) {
586
0
        if (ret) {
587
0
            qDebug() << "loaded library" << fileName;
588
0
        } else {
589
0
            qDebug() << qUtf8Printable(errorString);
590
0
        }
591
0
    }
592
0
    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
0
        libraryUnloadCount.ref();
596
0
        libraryRefCount.ref();
597
0
        installCoverageTool(this);
598
0
    }
599
600
0
    Q_TRACE(QLibraryPrivate_load_exit, ret);
601
602
0
    return ret;
603
0
}
604
605
bool QLibraryPrivate::unload(UnloadFlag flag)
606
0
{
607
0
    if (!pHnd.loadRelaxed())
608
0
        return false;
609
0
    if (libraryUnloadCount.loadRelaxed() > 0 && !libraryUnloadCount.deref()) { // only unload if ALL QLibrary instance wanted to
610
0
        QMutexLocker locker(&mutex);
611
0
        delete inst.data();
612
0
        if (flag == NoUnloadSys || unload_sys()) {
613
0
            if (qt_debug_component())
614
0
                qWarning() << "QLibraryPrivate::unload succeeded on" << fileName
615
0
                           << (flag == NoUnloadSys ? "(faked)" : "");
616
            //when the library is unloaded, we release the reference on it so that 'this'
617
            //can get deleted
618
0
            libraryRefCount.deref();
619
0
            pHnd.storeRelaxed(nullptr);
620
0
            instanceFactory.storeRelaxed(nullptr);
621
0
            return true;
622
0
        }
623
0
    }
624
625
0
    return false;
626
0
}
627
628
void QLibraryPrivate::release()
629
0
{
630
0
    QLibraryStore::releaseLibrary(this);
631
0
}
632
633
QtPluginInstanceFunction QLibraryPrivate::loadPlugin()
634
0
{
635
0
    if (auto ptr = instanceFactory.loadAcquire()) {
636
0
        libraryUnloadCount.ref();
637
0
        return ptr;
638
0
    }
639
0
    if (pluginState == IsNotAPlugin)
640
0
        return nullptr;
641
0
    if (load()) {
642
0
        auto ptr = reinterpret_cast<QtPluginInstanceFunction>(resolve("qt_plugin_instance"));
643
0
        instanceFactory.storeRelease(ptr); // two threads may store the same value
644
0
        return ptr;
645
0
    }
646
0
    if (qt_debug_component())
647
0
        qWarning() << "QLibraryPrivate::loadPlugin failed on" << fileName << ":" << errorString;
648
0
    pluginState = IsNotAPlugin;
649
0
    return nullptr;
650
0
}
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
0
{
669
#if defined(Q_OS_WIN)
670
    return fileName.endsWith(QLatin1String(".dll"), Qt::CaseInsensitive);
671
#else // Generic Unix
672
0
    QString completeSuffix = QFileInfo(fileName).completeSuffix();
673
0
    if (completeSuffix.isEmpty())
674
0
        return false;
675
0
    const QVector<QStringRef> suffixes = completeSuffix.splitRef(QLatin1Char('.'));
676
0
    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
0
    validSuffixList << QLatin1String("so");
698
0
# 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
0
    int suffix;
708
0
    int suffixPos = -1;
709
0
    for (suffix = 0; suffix < validSuffixList.count() && suffixPos == -1; ++suffix)
710
0
        suffixPos = suffixes.indexOf(QStringRef(&validSuffixList.at(suffix)));
711
712
0
    bool valid = suffixPos != -1;
713
0
    for (int i = suffixPos + 1; i < suffixes.count() && valid; ++i)
714
0
        if (i != suffixPos)
715
0
            suffixes.at(i).toInt(&valid);
716
0
    return valid;
717
0
#endif
718
0
}
719
720
static bool qt_get_metadata(QLibraryPrivate *priv, QString *errMsg)
721
0
{
722
0
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
723
0
    auto getMetaData = [](QFunctionPointer fptr) {
724
0
        auto f = reinterpret_cast<const char * (*)()>(fptr);
725
0
        return qMakePair<const char *, size_t>(f(), INT_MAX);
726
0
    };
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
0
    QFunctionPointer pfn = priv->resolve("qt_plugin_query_metadata");
735
0
    if (!pfn)
736
0
        return false;
737
738
0
    auto metaData = getMetaData(pfn);
739
0
    QJsonDocument doc = qJsonFromRawLibraryMetaData(metaData.first, metaData.second, errMsg);
740
0
    if (doc.isNull())
741
0
        return false;
742
0
    priv->metaData = doc.object();
743
0
    return true;
744
0
}
745
746
bool QLibraryPrivate::isPlugin()
747
0
{
748
0
    if (pluginState == MightBeAPlugin)
749
0
        updatePluginState();
750
751
0
    return pluginState == IsAPlugin;
752
0
}
753
754
void QLibraryPrivate::updatePluginState()
755
0
{
756
0
    QMutexLocker locker(&mutex);
757
0
    errorString.clear();
758
0
    if (pluginState != MightBeAPlugin)
759
0
        return;
760
761
0
    bool success = false;
762
763
0
#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
764
0
    if (fileName.endsWith(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
0
        errorString = QLibrary::tr("The shared library was not found.");
772
0
        pluginState = IsNotAPlugin;
773
0
        return;
774
0
    }
775
0
#endif
776
777
0
    if (!pHnd.loadRelaxed()) {
778
        // scan for the plugin metadata without loading
779
0
        success = findPatternUnloaded(fileName, this);
780
0
    } else {
781
        // library is already loaded (probably via QLibrary)
782
        // simply get the target function and call it.
783
0
        success = qt_get_metadata(this, &errorString);
784
0
    }
785
786
0
    if (!success) {
787
0
        if (errorString.isEmpty()){
788
0
            if (fileName.isEmpty())
789
0
                errorString = QLibrary::tr("The shared library was not found.");
790
0
            else
791
0
                errorString = QLibrary::tr("The file '%1' is not a valid Qt plugin.").arg(fileName);
792
0
        }
793
0
        pluginState = IsNotAPlugin;
794
0
        return;
795
0
    }
796
797
0
    pluginState = IsNotAPlugin; // be pessimistic
798
799
0
    uint qt_version = (uint)metaData.value(QLatin1String("version")).toDouble();
800
0
    bool debug = metaData.value(QLatin1String("debug")).toBool();
801
0
    if ((qt_version & 0x00ff00) > (QT_VERSION & 0x00ff00) || (qt_version & 0xff0000) != (QT_VERSION & 0xff0000)) {
802
0
        if (qt_debug_component()) {
803
0
            qWarning("In %s:\n"
804
0
                 "  Plugin uses incompatible Qt library (%d.%d.%d) [%s]",
805
0
                 QFile::encodeName(fileName).constData(),
806
0
                 (qt_version&0xff0000) >> 16, (qt_version&0xff00) >> 8, qt_version&0xff,
807
0
                 debug ? "debug" : "release");
808
0
        }
809
0
        errorString = QLibrary::tr("The plugin '%1' uses incompatible Qt library. (%2.%3.%4) [%5]")
810
0
            .arg(fileName,
811
0
                 QString::number((qt_version & 0xff0000) >> 16),
812
0
                 QString::number((qt_version & 0xff00) >> 8),
813
0
                 QString::number(qt_version & 0xff),
814
0
                 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
0
    } else {
822
0
        pluginState = IsAPlugin;
823
0
    }
824
0
}
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
0
{
838
0
    if (!d)
839
0
        return false;
840
0
    if (did_load)
841
0
        return d->pHnd.loadRelaxed();
842
0
    did_load = true;
843
0
    return d->load();
844
0
}
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
0
{
863
0
    if (did_load) {
864
0
        did_load = false;
865
0
        return d->unload();
866
0
    }
867
0
    return false;
868
0
}
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
0
{
877
0
    return d && d->pHnd.loadRelaxed();
878
0
}
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
0
{
887
0
}
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
0
{
902
0
    setFileName(fileName);
903
0
}
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
0
{
919
0
    setFileNameAndVersion(fileName, verNum);
920
0
}
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
0
{
935
0
    setFileNameAndVersion(fileName, version);
936
0
}
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
0
{
948
0
    if (d)
949
0
        d->release();
950
0
}
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
0
{
976
0
    QLibrary::LoadHints lh;
977
0
    if (d) {
978
0
        lh = d->loadHints();
979
0
        d->release();
980
0
        d = nullptr;
981
0
        did_load = false;
982
0
    }
983
0
    d = QLibraryPrivate::findOrCreate(fileName, QString(), lh);
984
0
}
985
986
QString QLibrary::fileName() const
987
0
{
988
0
    if (d) {
989
0
        QMutexLocker locker(&d->mutex);
990
0
        return d->qualifiedFileName.isEmpty() ? d->fileName : d->qualifiedFileName;
991
0
    }
992
0
    return QString();
993
0
}
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
0
{
1006
0
    QLibrary::LoadHints lh;
1007
0
    if (d) {
1008
0
        lh = d->loadHints();
1009
0
        d->release();
1010
0
        d = nullptr;
1011
0
        did_load = false;
1012
0
    }
1013
0
    d = QLibraryPrivate::findOrCreate(fileName, verNum >= 0 ? QString::number(verNum) : QString(), lh);
1014
0
}
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
0
{
1027
0
    QLibrary::LoadHints lh;
1028
0
    if (d) {
1029
0
        lh = d->loadHints();
1030
0
        d->release();
1031
0
        d = nullptr;
1032
0
        did_load = false;
1033
0
    }
1034
0
    d = QLibraryPrivate::findOrCreate(fileName, version, lh);
1035
0
}
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
0
{
1059
0
    if (!isLoaded() && !load())
1060
0
        return nullptr;
1061
0
    return d->resolve(symbol);
1062
0
}
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
0
{
1079
0
    QLibrary library(fileName);
1080
0
    return library.resolve(symbol);
1081
0
}
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
0
{
1099
0
    QLibrary library(fileName, verNum);
1100
0
    return library.resolve(symbol);
1101
0
}
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
0
{
1120
0
    QLibrary library(fileName, version);
1121
0
    return library.resolve(symbol);
1122
0
}
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
0
{
1132
0
    QString str;
1133
0
    if (d) {
1134
0
        QMutexLocker locker(&d->mutex);
1135
0
        str = d->errorString;
1136
0
    }
1137
0
    return str.isEmpty() ? tr("Unknown error") : str;
1138
0
}
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
0
{
1177
0
    if (!d) {
1178
0
        d = QLibraryPrivate::findOrCreate(QString());   // ugly, but we need a d-ptr
1179
0
        d->errorString.clear();
1180
0
    }
1181
0
    d->setLoadHints(hints);
1182
0
}
1183
1184
QLibrary::LoadHints QLibrary::loadHints() const
1185
0
{
1186
0
    return d ? d->loadHints() : QLibrary::LoadHints();
1187
0
}
1188
1189
/* Internal, for debugging */
1190
bool qt_debug_component()
1191
0
{
1192
0
    static int debug_env = QT_PREPEND_NAMESPACE(qEnvironmentVariableIntValue)("QT_DEBUG_PLUGINS");
1193
0
    return debug_env != 0;
1194
0
}
1195
1196
QT_END_NAMESPACE
1197
1198
#include "moc_qlibrary.cpp"