| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Copyright (C) 2018 Intel Corporation. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the QtCore module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU Lesser General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Lesser |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or (at your option) the GNU General |
| ** Public license version 3 or any later version approved by the KDE Free |
| ** Qt Foundation. The licenses are as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| ** https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| #include "qplatformdefs.h" |
| #include "qlibrary.h" |
| |
| #include "qfactoryloader_p.h" |
| #include "qlibrary_p.h" |
| #include <qstringlist.h> |
| #include <qfile.h> |
| #include <qfileinfo.h> |
| #include <qmutex.h> |
| #include <qmap.h> |
| #include <private/qcoreapplication_p.h> |
| #include <private/qsystemerror_p.h> |
| #ifdef Q_OS_MAC |
| # include <private/qcore_mac_p.h> |
| #endif |
| #ifndef NO_ERRNO_H |
| #include <errno.h> |
| #endif // NO_ERROR_H |
| #include <qdebug.h> |
| #include <qvector.h> |
| #include <qdir.h> |
| #include <qendian.h> |
| #include <qjsondocument.h> |
| #include <qjsonvalue.h> |
| #include "qelfparser_p.h" |
| #include "qmachparser_p.h" |
| |
| #include <qtcore_tracepoints_p.h> |
| |
| QT_BEGIN_NAMESPACE |
| |
| #ifdef QT_NO_DEBUG |
| # define QLIBRARY_AS_DEBUG false |
| #else |
| # define QLIBRARY_AS_DEBUG true |
| #endif |
| |
| #if defined(Q_OS_UNIX) || (defined(Q_CC_MINGW) && !QT_CONFIG(debug_and_release)) |
| // We don't use separate debug and release libs on UNIX, so we want |
| // to allow loading plugins, regardless of how they were built. |
| # define QT_NO_DEBUG_PLUGIN_CHECK |
| #endif |
| |
| /*! |
| \class QLibrary |
| \inmodule QtCore |
| \reentrant |
| \brief The QLibrary class loads shared libraries at runtime. |
| |
| |
| \ingroup plugins |
| |
| An instance of a QLibrary object operates on a single shared |
| object file (which we call a "library", but is also known as a |
| "DLL"). A QLibrary provides access to the functionality in the |
| library in a platform independent way. You can either pass a file |
| name in the constructor, or set it explicitly with setFileName(). |
| When loading the library, QLibrary searches in all the |
| system-specific library locations (e.g. \c LD_LIBRARY_PATH on |
| Unix), unless the file name has an absolute path. |
| |
| If the file name is an absolute path then an attempt is made to |
| load this path first. If the file cannot be found, QLibrary tries |
| the name with different platform-specific file prefixes, like |
| "lib" on Unix and Mac, and suffixes, like ".so" on Unix, ".dylib" |
| on the Mac, or ".dll" on Windows. |
| |
| If the file path is not absolute then QLibrary modifies the search |
| order to try the system-specific prefixes and suffixes first, |
| followed by the file path specified. |
| |
| This makes it possible to specify shared libraries that are only |
| identified by their basename (i.e. without their suffix), so the |
| same code will work on different operating systems yet still |
| minimise the number of attempts to find the library. |
| |
| The most important functions are load() to dynamically load the |
| library file, isLoaded() to check whether loading was successful, |
| and resolve() to resolve a symbol in the library. The resolve() |
| function implicitly tries to load the library if it has not been |
| loaded yet. Multiple instances of QLibrary can be used to access |
| the same physical library. Once loaded, libraries remain in memory |
| until the application terminates. You can attempt to unload a |
| library using unload(), but if other instances of QLibrary are |
| using the same library, the call will fail, and unloading will |
| only happen when every instance has called unload(). |
| |
| A typical use of QLibrary is to resolve an exported symbol in a |
| library, and to call the C function that this symbol represents. |
| This is called "explicit linking" in contrast to "implicit |
| linking", which is done by the link step in the build process when |
| linking an executable against a library. |
| |
| The following code snippet loads a library, resolves the symbol |
| "mysymbol", and calls the function if everything succeeded. If |
| something goes wrong, e.g. the library file does not exist or the |
| symbol is not defined, the function pointer will be \nullptr and |
| won't be called. |
| |
| \snippet code/src_corelib_plugin_qlibrary.cpp 0 |
| |
| The symbol must be exported as a C function from the library for |
| resolve() to work. This means that the function must be wrapped in |
| an \c{extern "C"} block if the library is compiled with a C++ |
| compiler. On Windows, this also requires the use of a \c dllexport |
| macro; see resolve() for the details of how this is done. For |
| convenience, there is a static resolve() function which you can |
| use if you just want to call a function in a library without |
| explicitly loading the library first: |
| |
| \snippet code/src_corelib_plugin_qlibrary.cpp 1 |
| |
| \sa QPluginLoader |
| */ |
| |
| /*! |
| \enum QLibrary::LoadHint |
| |
| This enum describes the possible hints that can be used to change the way |
| libraries are handled when they are loaded. These values indicate how |
| symbols are resolved when libraries are loaded, and are specified using |
| the setLoadHints() function. |
| |
| \value ResolveAllSymbolsHint |
| Causes all symbols in a library to be resolved when it is loaded, not |
| simply when resolve() is called. |
| \value ExportExternalSymbolsHint |
| Exports unresolved and external symbols in the library so that they can be |
| resolved in other dynamically-loaded libraries loaded later. |
| \value LoadArchiveMemberHint |
| Allows the file name of the library to specify a particular object file |
| within an archive file. |
| If this hint is given, the filename of the library consists of |
| a path, which is a reference to an archive file, followed by |
| a reference to the archive member. |
| \value PreventUnloadHint |
| Prevents the library from being unloaded from the address space if close() |
| is called. The library's static variables are not reinitialized if open() |
| is called at a later time. |
| \value DeepBindHint |
| Instructs the linker to prefer definitions in the loaded library |
| over exported definitions in the loading application when resolving |
| external symbols in the loaded library. This option is only supported |
| on Linux. |
| |
| \sa loadHints |
| */ |
| |
| |
| static qsizetype qt_find_pattern(const char *s, qsizetype s_len, |
| const char *pattern, ulong p_len) |
| { |
| /* |
| we search from the end of the file because on the supported |
| systems, the read-only data/text segments are placed at the end |
| of the file. HOWEVER, when building with debugging enabled, all |
| the debug symbols are placed AFTER the data/text segments. |
| |
| what does this mean? when building in release mode, the search |
| is fast because the data we are looking for is at the end of the |
| file... when building in debug mode, the search is slower |
| because we have to skip over all the debugging symbols first |
| */ |
| |
| if (!s || !pattern || qsizetype(p_len) > s_len) |
| return -1; |
| |
| size_t i, hs = 0, hp = 0, delta = s_len - p_len; |
| |
| for (i = 0; i < p_len; ++i) { |
| hs += s[delta + i]; |
| hp += pattern[i]; |
| } |
| i = delta; |
| for (;;) { |
| if (hs == hp && qstrncmp(s + i, pattern, p_len) == 0) |
| return i; // can't overflow, by construction |
| if (i == 0) |
| break; |
| --i; |
| hs -= s[i + p_len]; |
| hs += s[i]; |
| } |
| |
| return -1; |
| } |
| |
| /* |
| This opens the specified library, mmaps it into memory, and searches |
| for the QT_PLUGIN_VERIFICATION_DATA. The advantage of this approach is that |
| we can get the verification data without have to actually load the library. |
| This lets us detect mismatches more safely. |
| |
| Returns \c false if version information is not present, or if the |
| information could not be read. |
| Returns true if version information is present and successfully read. |
| */ |
| static bool findPatternUnloaded(const QString &library, QLibraryPrivate *lib) |
| { |
| QFile file(library); |
| if (!file.open(QIODevice::ReadOnly)) { |
| if (lib) |
| lib->errorString = file.errorString(); |
| if (qt_debug_component()) { |
| qWarning("%s: %ls", QFile::encodeName(library).constData(), |
| qUtf16Printable(QSystemError::stdString())); |
| } |
| return false; |
| } |
| |
| // Files can be bigger than the virtual memory size on 32-bit systems, so |
| // we limit to 512 MB there. For 64-bit, we allow up to 2^40 bytes. |
| constexpr qint64 MaxMemoryMapSize = |
| Q_INT64_C(1) << (sizeof(qsizetype) > 4 ? 40 : 29); |
| |
| QByteArray data; |
| qsizetype fdlen = qMin(file.size(), MaxMemoryMapSize); |
| const char *filedata = reinterpret_cast<char *>(file.map(0, fdlen)); |
| |
| if (filedata == 0) { |
| // Try reading the data into memory instead (up to 64 MB). |
| data = file.read(64 * 1024 * 1024); |
| filedata = data.constData(); |
| fdlen = data.size(); |
| } |
| |
| /* |
| ELF and Mach-O binaries with GCC have .qplugin sections. |
| */ |
| bool hasMetaData = false; |
| qsizetype pos = 0; |
| char pattern[] = "qTMETADATA "; |
| pattern[0] = 'Q'; // Ensure the pattern "QTMETADATA" is not found in this library should QPluginLoader ever encounter it. |
| const ulong plen = qstrlen(pattern); |
| #if defined (Q_OF_ELF) && defined(Q_CC_GNU) |
| int r = QElfParser().parse(filedata, fdlen, library, lib, &pos, &fdlen); |
| if (r == QElfParser::Corrupt || r == QElfParser::NotElf) { |
| if (lib && qt_debug_component()) { |
| qWarning("QElfParser: %ls", qUtf16Printable(lib->errorString)); |
| } |
| return false; |
| } else if (r == QElfParser::QtMetaDataSection) { |
| qsizetype rel = qt_find_pattern(filedata + pos, fdlen, pattern, plen); |
| if (rel < 0) |
| pos = -1; |
| else |
| pos += rel; |
| hasMetaData = true; |
| } |
| #elif defined (Q_OF_MACH_O) |
| { |
| QString errorString; |
| int r = QMachOParser::parse(filedata, fdlen, library, &errorString, &pos, &fdlen); |
| if (r == QMachOParser::NotSuitable) { |
| if (qt_debug_component()) |
| qWarning("QMachOParser: %ls", qUtf16Printable(errorString)); |
| if (lib) |
| lib->errorString = errorString; |
| return false; |
| } |
| // even if the metadata section was not found, the Mach-O parser will |
| // at least return the boundaries of the right architecture |
| qsizetype rel = qt_find_pattern(filedata + pos, fdlen, pattern, plen); |
| if (rel < 0) |
| pos = -1; |
| else |
| pos += rel; |
| hasMetaData = true; |
| } |
| #else |
| pos = qt_find_pattern(filedata, fdlen, pattern, plen); |
| if (pos > 0) |
| hasMetaData = true; |
| #endif // defined(Q_OF_ELF) && defined(Q_CC_GNU) |
| |
| bool ret = false; |
| |
| if (pos >= 0 && hasMetaData) { |
| const char *data = filedata + pos; |
| QString errMsg; |
| QJsonDocument doc = qJsonFromRawLibraryMetaData(data, fdlen, &errMsg); |
| if (doc.isNull()) { |
| qWarning("Found invalid metadata in lib %ls: %ls", |
| qUtf16Printable(library), qUtf16Printable(errMsg)); |
| } else { |
| lib->metaData = doc.object(); |
| if (qt_debug_component()) |
| qWarning("Found metadata in lib %s, metadata=\n%s\n", |
| library.toLocal8Bit().constData(), doc.toJson().constData()); |
| ret = !doc.isNull(); |
| } |
| } |
| |
| if (!ret && lib) |
| lib->errorString = QLibrary::tr("Failed to extract plugin meta data from '%1'").arg(library); |
| file.close(); |
| return ret; |
| } |
| |
| static void installCoverageTool(QLibraryPrivate *libPrivate) |
| { |
| #ifdef __COVERAGESCANNER__ |
| /* |
| __COVERAGESCANNER__ is defined when Qt has been instrumented for code |
| coverage by TestCocoon. CoverageScanner is the name of the tool that |
| generates the code instrumentation. |
| This code is required here when code coverage analysis with TestCocoon |
| is enabled in order to allow the loading application to register the plugin |
| and then store its execution report. The execution report gathers information |
| about each part of the plugin's code that has been used when |
| the plugin was loaded by the launching application. |
| The execution report for the plugin will go to the same execution report |
| as the one defined for the application loading it. |
| */ |
| |
| int ret = __coveragescanner_register_library(libPrivate->fileName.toLocal8Bit()); |
| |
| if (qt_debug_component()) { |
| if (ret >= 0) { |
| qDebug("coverage data for %ls registered", |
| qUtf16Printable(libPrivate->fileName)); |
| } else { |
| qWarning("could not register %ls: error %d; coverage data may be incomplete", |
| qUtf16Printable(libPrivate->fileName), |
| ret); |
| } |
| } |
| #else |
| Q_UNUSED(libPrivate); |
| #endif |
| } |
| |
| class QLibraryStore |
| { |
| public: |
| inline ~QLibraryStore(); |
| static inline QLibraryPrivate *findOrCreate(const QString &fileName, const QString &version, QLibrary::LoadHints loadHints); |
| static inline void releaseLibrary(QLibraryPrivate *lib); |
| |
| static inline void cleanup(); |
| |
| private: |
| static inline QLibraryStore *instance(); |
| |
| // all members and instance() are protected by qt_library_mutex |
| typedef QMap<QString, QLibraryPrivate*> LibraryMap; |
| LibraryMap libraryMap; |
| }; |
| |
| static QBasicMutex qt_library_mutex; |
| static QLibraryStore *qt_library_data = 0; |
| static bool qt_library_data_once; |
| |
| QLibraryStore::~QLibraryStore() |
| { |
| qt_library_data = 0; |
| } |
| |
| inline void QLibraryStore::cleanup() |
| { |
| QLibraryStore *data = qt_library_data; |
| if (!data) |
| return; |
| |
| // find any libraries that are still loaded but have a no one attached to them |
| LibraryMap::Iterator it = data->libraryMap.begin(); |
| for (; it != data->libraryMap.end(); ++it) { |
| QLibraryPrivate *lib = it.value(); |
| if (lib->libraryRefCount.loadRelaxed() == 1) { |
| if (lib->libraryUnloadCount.loadRelaxed() > 0) { |
| Q_ASSERT(lib->pHnd); |
| lib->libraryUnloadCount.storeRelaxed(1); |
| #ifdef __GLIBC__ |
| // glibc has a bug in unloading from global destructors |
| // see https://bugzilla.novell.com/show_bug.cgi?id=622977 |
| // and http://sourceware.org/bugzilla/show_bug.cgi?id=11941 |
| lib->unload(QLibraryPrivate::NoUnloadSys); |
| #else |
| lib->unload(); |
| #endif |
| } |
| delete lib; |
| it.value() = 0; |
| } |
| } |
| |
| if (qt_debug_component()) { |
| // dump all objects that remain |
| for (QLibraryPrivate *lib : qAsConst(data->libraryMap)) { |
| if (lib) |
| qDebug() << "On QtCore unload," << lib->fileName << "was leaked, with" |
| << lib->libraryRefCount.loadRelaxed() << "users"; |
| } |
| } |
| |
| delete data; |
| } |
| |
| static void qlibraryCleanup() |
| { |
| QLibraryStore::cleanup(); |
| } |
| Q_DESTRUCTOR_FUNCTION(qlibraryCleanup) |
| |
| // must be called with a locked mutex |
| QLibraryStore *QLibraryStore::instance() |
| { |
| if (Q_UNLIKELY(!qt_library_data_once && !qt_library_data)) { |
| // only create once per process lifetime |
| qt_library_data = new QLibraryStore; |
| qt_library_data_once = true; |
| } |
| return qt_library_data; |
| } |
| |
| inline QLibraryPrivate *QLibraryStore::findOrCreate(const QString &fileName, const QString &version, |
| QLibrary::LoadHints loadHints) |
| { |
| QMutexLocker locker(&qt_library_mutex); |
| QLibraryStore *data = instance(); |
| |
| // check if this library is already loaded |
| QLibraryPrivate *lib = 0; |
| if (Q_LIKELY(data)) { |
| lib = data->libraryMap.value(fileName); |
| if (lib) |
| lib->mergeLoadHints(loadHints); |
| } |
| if (!lib) |
| lib = new QLibraryPrivate(fileName, version, loadHints); |
| |
| // track this library |
| if (Q_LIKELY(data) && !fileName.isEmpty()) |
| data->libraryMap.insert(fileName, lib); |
| |
| lib->libraryRefCount.ref(); |
| return lib; |
| } |
| |
| inline void QLibraryStore::releaseLibrary(QLibraryPrivate *lib) |
| { |
| QMutexLocker locker(&qt_library_mutex); |
| QLibraryStore *data = instance(); |
| |
| if (lib->libraryRefCount.deref()) { |
| // still in use |
| return; |
| } |
| |
| // no one else is using |
| Q_ASSERT(lib->libraryUnloadCount.loadRelaxed() == 0); |
| |
| if (Q_LIKELY(data) && !lib->fileName.isEmpty()) { |
| QLibraryPrivate *that = data->libraryMap.take(lib->fileName); |
| Q_ASSERT(lib == that); |
| Q_UNUSED(that); |
| } |
| delete lib; |
| } |
| |
| QLibraryPrivate::QLibraryPrivate(const QString &canonicalFileName, const QString &version, QLibrary::LoadHints loadHints) |
| : pHnd(0), fileName(canonicalFileName), fullVersion(version), instance(0), |
| libraryRefCount(0), libraryUnloadCount(0), pluginState(MightBeAPlugin) |
| { |
| loadHintsInt.storeRelaxed(loadHints); |
| if (canonicalFileName.isEmpty()) |
| errorString = QLibrary::tr("The shared library was not found."); |
| } |
| |
| QLibraryPrivate *QLibraryPrivate::findOrCreate(const QString &fileName, const QString &version, |
| QLibrary::LoadHints loadHints) |
| { |
| return QLibraryStore::findOrCreate(fileName, version, loadHints); |
| } |
| |
| QLibraryPrivate::~QLibraryPrivate() |
| { |
| } |
| |
| void QLibraryPrivate::mergeLoadHints(QLibrary::LoadHints lh) |
| { |
| // if the library is already loaded, we can't change the load hints |
| if (pHnd) |
| return; |
| |
| loadHintsInt.storeRelaxed(lh); |
| } |
| |
| QFunctionPointer QLibraryPrivate::resolve(const char *symbol) |
| { |
| if (!pHnd) |
| return 0; |
| return resolve_sys(symbol); |
| } |
| |
| void QLibraryPrivate::setLoadHints(QLibrary::LoadHints lh) |
| { |
| // this locks a global mutex |
| QMutexLocker lock(&qt_library_mutex); |
| mergeLoadHints(lh); |
| } |
| |
| bool QLibraryPrivate::load() |
| { |
| if (pHnd) { |
| libraryUnloadCount.ref(); |
| return true; |
| } |
| if (fileName.isEmpty()) |
| return false; |
| |
| Q_TRACE(QLibraryPrivate_load_entry, fileName); |
| |
| bool ret = load_sys(); |
| if (qt_debug_component()) { |
| if (ret) { |
| qDebug() << "loaded library" << fileName; |
| } else { |
| qDebug() << qUtf8Printable(errorString); |
| } |
| } |
| if (ret) { |
| //when loading a library we add a reference to it so that the QLibraryPrivate won't get deleted |
| //this allows to unload the library at a later time |
| libraryUnloadCount.ref(); |
| libraryRefCount.ref(); |
| installCoverageTool(this); |
| } |
| |
| Q_TRACE(QLibraryPrivate_load_exit, ret); |
| |
| return ret; |
| } |
| |
| bool QLibraryPrivate::unload(UnloadFlag flag) |
| { |
| if (!pHnd) |
| return false; |
| if (libraryUnloadCount.loadRelaxed() > 0 && !libraryUnloadCount.deref()) { // only unload if ALL QLibrary instance wanted to |
| delete inst.data(); |
| if (flag == NoUnloadSys || unload_sys()) { |
| if (qt_debug_component()) |
| qWarning() << "QLibraryPrivate::unload succeeded on" << fileName |
| << (flag == NoUnloadSys ? "(faked)" : ""); |
| //when the library is unloaded, we release the reference on it so that 'this' |
| //can get deleted |
| libraryRefCount.deref(); |
| pHnd = 0; |
| instance = 0; |
| } |
| } |
| |
| return (pHnd == 0); |
| } |
| |
| void QLibraryPrivate::release() |
| { |
| QLibraryStore::releaseLibrary(this); |
| } |
| |
| bool QLibraryPrivate::loadPlugin() |
| { |
| if (instance) { |
| libraryUnloadCount.ref(); |
| return true; |
| } |
| if (pluginState == IsNotAPlugin) |
| return false; |
| if (load()) { |
| instance = (QtPluginInstanceFunction)resolve("qt_plugin_instance"); |
| return instance; |
| } |
| if (qt_debug_component()) |
| qWarning() << "QLibraryPrivate::loadPlugin failed on" << fileName << ":" << errorString; |
| pluginState = IsNotAPlugin; |
| return false; |
| } |
| |
| /*! |
| Returns \c true if \a fileName has a valid suffix for a loadable |
| library; otherwise returns \c false. |
| |
| \table |
| \header \li Platform \li Valid suffixes |
| \row \li Windows \li \c .dll, \c .DLL |
| \row \li Unix/Linux \li \c .so |
| \row \li AIX \li \c .a |
| \row \li HP-UX \li \c .sl, \c .so (HP-UXi) |
| \row \li \macos and iOS \li \c .dylib, \c .bundle, \c .so |
| \endtable |
| |
| Trailing versioning numbers on Unix are ignored. |
| */ |
| bool QLibrary::isLibrary(const QString &fileName) |
| { |
| #if defined(Q_OS_WIN) |
| return fileName.endsWith(QLatin1String(".dll"), Qt::CaseInsensitive); |
| #else // Generic Unix |
| QString completeSuffix = QFileInfo(fileName).completeSuffix(); |
| if (completeSuffix.isEmpty()) |
| return false; |
| const QVector<QStringRef> suffixes = completeSuffix.splitRef(QLatin1Char('.')); |
| QStringList validSuffixList; |
| |
| # if defined(Q_OS_HPUX) |
| /* |
| See "HP-UX Linker and Libraries User's Guide", section "Link-time Differences between PA-RISC and IPF": |
| "In PA-RISC (PA-32 and PA-64) shared libraries are suffixed with .sl. In IPF (32-bit and 64-bit), |
| the shared libraries are suffixed with .so. For compatibility, the IPF linker also supports the .sl suffix." |
| */ |
| validSuffixList << QLatin1String("sl"); |
| # if defined __ia64 |
| validSuffixList << QLatin1String("so"); |
| # endif |
| # elif defined(Q_OS_AIX) |
| validSuffixList << QLatin1String("a") << QLatin1String("so"); |
| # elif defined(Q_OS_DARWIN) |
| // On Apple platforms, dylib look like libmylib.1.0.0.dylib |
| if (suffixes.last() == QLatin1String("dylib")) |
| return true; |
| |
| validSuffixList << QLatin1String("so") << QLatin1String("bundle"); |
| # elif defined(Q_OS_UNIX) |
| validSuffixList << QLatin1String("so"); |
| # endif |
| |
| // Examples of valid library names: |
| // libfoo.so |
| // libfoo.so.0 |
| // libfoo.so.0.3 |
| // libfoo-0.3.so |
| // libfoo-0.3.so.0.3.0 |
| |
| int suffix; |
| int suffixPos = -1; |
| for (suffix = 0; suffix < validSuffixList.count() && suffixPos == -1; ++suffix) |
| suffixPos = suffixes.indexOf(QStringRef(&validSuffixList.at(suffix))); |
| |
| bool valid = suffixPos != -1; |
| for (int i = suffixPos + 1; i < suffixes.count() && valid; ++i) |
| if (i != suffixPos) |
| suffixes.at(i).toInt(&valid); |
| return valid; |
| #endif |
| } |
| |
| static bool qt_get_metadata(QLibraryPrivate *priv, QString *errMsg) |
| { |
| #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) |
| auto getMetaData = [](QFunctionPointer fptr) { |
| auto f = reinterpret_cast<const char * (*)()>(fptr); |
| return qMakePair<const char *, size_t>(f(), INT_MAX); |
| }; |
| #else |
| auto getMetaData = [](QFunctionPointer fptr) { |
| auto f = reinterpret_cast<QPair<const char *, size_t> (*)()>(fptr); |
| return f(); |
| }; |
| #endif |
| |
| QFunctionPointer pfn = priv->resolve("qt_plugin_query_metadata"); |
| if (!pfn) |
| return false; |
| |
| auto metaData = getMetaData(pfn); |
| QJsonDocument doc = qJsonFromRawLibraryMetaData(metaData.first, metaData.second, errMsg); |
| if (doc.isNull()) |
| return false; |
| priv->metaData = doc.object(); |
| return true; |
| } |
| |
| bool QLibraryPrivate::isPlugin() |
| { |
| if (pluginState == MightBeAPlugin) |
| updatePluginState(); |
| |
| return pluginState == IsAPlugin; |
| } |
| |
| void QLibraryPrivate::updatePluginState() |
| { |
| errorString.clear(); |
| if (pluginState != MightBeAPlugin) |
| return; |
| |
| bool success = false; |
| |
| #if defined(Q_OS_UNIX) && !defined(Q_OS_MAC) |
| if (fileName.endsWith(QLatin1String(".debug"))) { |
| // refuse to load a file that ends in .debug |
| // these are the debug symbols from the libraries |
| // the problem is that they are valid shared library files |
| // and dlopen is known to crash while opening them |
| |
| // pretend we didn't see the file |
| errorString = QLibrary::tr("The shared library was not found."); |
| pluginState = IsNotAPlugin; |
| return; |
| } |
| #endif |
| |
| if (!pHnd) { |
| // scan for the plugin metadata without loading |
| success = findPatternUnloaded(fileName, this); |
| } else { |
| // library is already loaded (probably via QLibrary) |
| // simply get the target function and call it. |
| success = qt_get_metadata(this, &errorString); |
| } |
| |
| if (!success) { |
| if (errorString.isEmpty()){ |
| if (fileName.isEmpty()) |
| errorString = QLibrary::tr("The shared library was not found."); |
| else |
| errorString = QLibrary::tr("The file '%1' is not a valid Qt plugin.").arg(fileName); |
| } |
| pluginState = IsNotAPlugin; |
| return; |
| } |
| |
| pluginState = IsNotAPlugin; // be pessimistic |
| |
| uint qt_version = (uint)metaData.value(QLatin1String("version")).toDouble(); |
| bool debug = metaData.value(QLatin1String("debug")).toBool(); |
| if ((qt_version & 0x00ff00) > (QT_VERSION & 0x00ff00) || (qt_version & 0xff0000) != (QT_VERSION & 0xff0000)) { |
| if (qt_debug_component()) { |
| qWarning("In %s:\n" |
| " Plugin uses incompatible Qt library (%d.%d.%d) [%s]", |
| QFile::encodeName(fileName).constData(), |
| (qt_version&0xff0000) >> 16, (qt_version&0xff00) >> 8, qt_version&0xff, |
| debug ? "debug" : "release"); |
| } |
| errorString = QLibrary::tr("The plugin '%1' uses incompatible Qt library. (%2.%3.%4) [%5]") |
| .arg(fileName) |
| .arg((qt_version&0xff0000) >> 16) |
| .arg((qt_version&0xff00) >> 8) |
| .arg(qt_version&0xff) |
| .arg(debug ? QLatin1String("debug") : QLatin1String("release")); |
| #ifndef QT_NO_DEBUG_PLUGIN_CHECK |
| } else if(debug != QLIBRARY_AS_DEBUG) { |
| //don't issue a qWarning since we will hopefully find a non-debug? --Sam |
| errorString = QLibrary::tr("The plugin '%1' uses incompatible Qt library." |
| " (Cannot mix debug and release libraries.)").arg(fileName); |
| #endif |
| } else { |
| pluginState = IsAPlugin; |
| } |
| } |
| |
| /*! |
| Loads the library and returns \c true if the library was loaded |
| successfully; otherwise returns \c false. Since resolve() always |
| calls this function before resolving any symbols it is not |
| necessary to call it explicitly. In some situations you might want |
| the library loaded in advance, in which case you would use this |
| function. |
| |
| \sa unload() |
| */ |
| bool QLibrary::load() |
| { |
| if (!d) |
| return false; |
| if (did_load) |
| return d->pHnd; |
| did_load = true; |
| return d->load(); |
| } |
| |
| /*! |
| Unloads the library and returns \c true if the library could be |
| unloaded; otherwise returns \c false. |
| |
| This happens automatically on application termination, so you |
| shouldn't normally need to call this function. |
| |
| If other instances of QLibrary are using the same library, the |
| call will fail, and unloading will only happen when every instance |
| has called unload(). |
| |
| Note that on Mac OS X 10.3 (Panther), dynamic libraries cannot be unloaded. |
| |
| \sa resolve(), load() |
| */ |
| bool QLibrary::unload() |
| { |
| if (did_load) { |
| did_load = false; |
| return d->unload(); |
| } |
| return false; |
| } |
| |
| /*! |
| Returns \c true if the library is loaded; otherwise returns \c false. |
| |
| \sa load() |
| */ |
| bool QLibrary::isLoaded() const |
| { |
| return d && d->pHnd; |
| } |
| |
| |
| /*! |
| Constructs a library with the given \a parent. |
| */ |
| QLibrary::QLibrary(QObject *parent) |
| :QObject(parent), d(0), did_load(false) |
| { |
| } |
| |
| |
| /*! |
| Constructs a library object with the given \a parent that will |
| load the library specified by \a fileName. |
| |
| We recommend omitting the file's suffix in \a fileName, since |
| QLibrary will automatically look for the file with the appropriate |
| suffix in accordance with the platform, e.g. ".so" on Unix, |
| ".dylib" on \macos and iOS, and ".dll" on Windows. (See \l{fileName}.) |
| */ |
| QLibrary::QLibrary(const QString& fileName, QObject *parent) |
| :QObject(parent), d(0), did_load(false) |
| { |
| setFileName(fileName); |
| } |
| |
| |
| /*! |
| Constructs a library object with the given \a parent that will |
| load the library specified by \a fileName and major version number \a verNum. |
| Currently, the version number is ignored on Windows. |
| |
| We recommend omitting the file's suffix in \a fileName, since |
| QLibrary will automatically look for the file with the appropriate |
| suffix in accordance with the platform, e.g. ".so" on Unix, |
| ".dylib" on \macos and iOS, and ".dll" on Windows. (See \l{fileName}.) |
| */ |
| QLibrary::QLibrary(const QString& fileName, int verNum, QObject *parent) |
| :QObject(parent), d(0), did_load(false) |
| { |
| setFileNameAndVersion(fileName, verNum); |
| } |
| |
| /*! |
| Constructs a library object with the given \a parent that will |
| load the library specified by \a fileName and full version number \a version. |
| Currently, the version number is ignored on Windows. |
| |
| We recommend omitting the file's suffix in \a fileName, since |
| QLibrary will automatically look for the file with the appropriate |
| suffix in accordance with the platform, e.g. ".so" on Unix, |
| ".dylib" on \macos and iOS, and ".dll" on Windows. (See \l{fileName}.) |
| */ |
| QLibrary::QLibrary(const QString& fileName, const QString &version, QObject *parent) |
| :QObject(parent), d(0), did_load(false) |
| { |
| setFileNameAndVersion(fileName, version); |
| } |
| |
| /*! |
| Destroys the QLibrary object. |
| |
| Unless unload() was called explicitly, the library stays in memory |
| until the application terminates. |
| |
| \sa isLoaded(), unload() |
| */ |
| QLibrary::~QLibrary() |
| { |
| if (d) |
| d->release(); |
| } |
| |
| |
| /*! |
| \property QLibrary::fileName |
| \brief the file name of the library |
| |
| We recommend omitting the file's suffix in the file name, since |
| QLibrary will automatically look for the file with the appropriate |
| suffix (see isLibrary()). |
| |
| When loading the library, QLibrary searches in all system-specific |
| library locations (for example, \c LD_LIBRARY_PATH on Unix), unless the |
| file name has an absolute path. After loading the library |
| successfully, fileName() returns the fully-qualified file name of |
| the library, including the full path to the library if one was given |
| in the constructor or passed to setFileName(). |
| |
| For example, after successfully loading the "GL" library on Unix |
| platforms, fileName() will return "libGL.so". If the file name was |
| originally passed as "/usr/lib/libGL", fileName() will return |
| "/usr/lib/libGL.so". |
| */ |
| |
| void QLibrary::setFileName(const QString &fileName) |
| { |
| QLibrary::LoadHints lh; |
| if (d) { |
| lh = d->loadHints(); |
| d->release(); |
| d = 0; |
| did_load = false; |
| } |
| d = QLibraryPrivate::findOrCreate(fileName, QString(), lh); |
| } |
| |
| QString QLibrary::fileName() const |
| { |
| if (d) |
| return d->qualifiedFileName.isEmpty() ? d->fileName : d->qualifiedFileName; |
| return QString(); |
| } |
| |
| /*! |
| \fn void QLibrary::setFileNameAndVersion(const QString &fileName, int versionNumber) |
| |
| Sets the fileName property and major version number to \a fileName |
| and \a versionNumber respectively. |
| The \a versionNumber is ignored on Windows. |
| |
| \sa setFileName() |
| */ |
| void QLibrary::setFileNameAndVersion(const QString &fileName, int verNum) |
| { |
| QLibrary::LoadHints lh; |
| if (d) { |
| lh = d->loadHints(); |
| d->release(); |
| d = 0; |
| did_load = false; |
| } |
| d = QLibraryPrivate::findOrCreate(fileName, verNum >= 0 ? QString::number(verNum) : QString(), lh); |
| } |
| |
| /*! |
| \since 4.4 |
| |
| Sets the fileName property and full version number to \a fileName |
| and \a version respectively. |
| The \a version parameter is ignored on Windows. |
| |
| \sa setFileName() |
| */ |
| void QLibrary::setFileNameAndVersion(const QString &fileName, const QString &version) |
| { |
| QLibrary::LoadHints lh; |
| if (d) { |
| lh = d->loadHints(); |
| d->release(); |
| d = 0; |
| did_load = false; |
| } |
| d = QLibraryPrivate::findOrCreate(fileName, version, lh); |
| } |
| |
| /*! |
| Returns the address of the exported symbol \a symbol. The library is |
| loaded if necessary. The function returns \nullptr if the symbol could |
| not be resolved or if the library could not be loaded. |
| |
| Example: |
| \snippet code/src_corelib_plugin_qlibrary.cpp 2 |
| |
| The symbol must be exported as a C function from the library. This |
| means that the function must be wrapped in an \c{extern "C"} if |
| the library is compiled with a C++ compiler. On Windows you must |
| also explicitly export the function from the DLL using the |
| \c{__declspec(dllexport)} compiler directive, for example: |
| |
| \snippet code/src_corelib_plugin_qlibrary.cpp 3 |
| |
| with \c MY_EXPORT defined as |
| |
| \snippet code/src_corelib_plugin_qlibrary.cpp 4 |
| */ |
| QFunctionPointer QLibrary::resolve(const char *symbol) |
| { |
| if (!isLoaded() && !load()) |
| return 0; |
| return d->resolve(symbol); |
| } |
| |
| /*! |
| \overload |
| |
| Loads the library \a fileName and returns the address of the |
| exported symbol \a symbol. Note that \a fileName should not |
| include the platform-specific file suffix; (see \l{fileName}). The |
| library remains loaded until the application exits. |
| |
| The function returns \nullptr if the symbol could not be resolved or if |
| the library could not be loaded. |
| |
| \sa resolve() |
| */ |
| QFunctionPointer QLibrary::resolve(const QString &fileName, const char *symbol) |
| { |
| QLibrary library(fileName); |
| return library.resolve(symbol); |
| } |
| |
| /*! |
| \overload |
| |
| Loads the library \a fileName with major version number \a verNum and |
| returns the address of the exported symbol \a symbol. |
| Note that \a fileName should not include the platform-specific file suffix; |
| (see \l{fileName}). The library remains loaded until the application exits. |
| \a verNum is ignored on Windows. |
| |
| The function returns \nullptr if the symbol could not be resolved or if |
| the library could not be loaded. |
| |
| \sa resolve() |
| */ |
| QFunctionPointer QLibrary::resolve(const QString &fileName, int verNum, const char *symbol) |
| { |
| QLibrary library(fileName, verNum); |
| return library.resolve(symbol); |
| } |
| |
| /*! |
| \overload |
| \since 4.4 |
| |
| Loads the library \a fileName with full version number \a version and |
| returns the address of the exported symbol \a symbol. |
| Note that \a fileName should not include the platform-specific file suffix; |
| (see \l{fileName}). The library remains loaded until the application exits. |
| \a version is ignored on Windows. |
| |
| The function returns \nullptr if the symbol could not be resolved or if |
| the library could not be loaded. |
| |
| \sa resolve() |
| */ |
| QFunctionPointer QLibrary::resolve(const QString &fileName, const QString &version, const char *symbol) |
| { |
| QLibrary library(fileName, version); |
| return library.resolve(symbol); |
| } |
| |
| /*! |
| \since 4.2 |
| |
| Returns a text string with the description of the last error that occurred. |
| Currently, errorString will only be set if load(), unload() or resolve() for some reason fails. |
| */ |
| QString QLibrary::errorString() const |
| { |
| return (!d || d->errorString.isEmpty()) ? tr("Unknown error") : d->errorString; |
| } |
| |
| /*! |
| \property QLibrary::loadHints |
| \brief Give the load() function some hints on how it should behave. |
| |
| You can give some hints on how the symbols are resolved. Usually, |
| the symbols are not resolved at load time, but resolved lazily, |
| (that is, when resolve() is called). If you set the loadHints to |
| ResolveAllSymbolsHint, then all symbols will be resolved at load time |
| if the platform supports it. |
| |
| Setting ExportExternalSymbolsHint will make the external symbols in the |
| library available for resolution in subsequent loaded libraries. |
| |
| If LoadArchiveMemberHint is set, the file name |
| is composed of two components: A path which is a reference to an |
| archive file followed by the second component which is the reference to |
| the archive member. For instance, the fileName \c libGL.a(shr_64.o) will refer |
| to the library \c shr_64.o in the archive file named \c libGL.a. This |
| is only supported on the AIX platform. |
| |
| The interpretation of the load hints is platform dependent, and if |
| you use it you are probably making some assumptions on which platform |
| you are compiling for, so use them only if you understand the consequences |
| of them. |
| |
| By default, none of these flags are set, so libraries will be loaded with |
| lazy symbol resolution, and will not export external symbols for resolution |
| in other dynamically-loaded libraries. |
| |
| \note Setting this property after the library has been loaded has no effect |
| and loadHints() will not reflect those changes. |
| |
| \note This property is shared among all QLibrary instances that refer to |
| the same library. |
| */ |
| void QLibrary::setLoadHints(LoadHints hints) |
| { |
| if (!d) { |
| d = QLibraryPrivate::findOrCreate(QString()); // ugly, but we need a d-ptr |
| d->errorString.clear(); |
| } |
| d->setLoadHints(hints); |
| } |
| |
| QLibrary::LoadHints QLibrary::loadHints() const |
| { |
| return d ? d->loadHints() : QLibrary::LoadHints(); |
| } |
| |
| /* Internal, for debugging */ |
| bool qt_debug_component() |
| { |
| static int debug_env = QT_PREPEND_NAMESPACE(qEnvironmentVariableIntValue)("QT_DEBUG_PLUGINS"); |
| return debug_env != 0; |
| } |
| |
| QT_END_NAMESPACE |
| |
| #include "moc_qlibrary.cpp" |