| /**************************************************************************** |
| ** |
| ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). |
| ** All rights reserved. |
| ** Contact: Nokia Corporation (qt-info@nokia.com) |
| ** |
| ** This file is part of the QtCore module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** GNU Lesser General Public License Usage |
| ** This file may be used under the terms of the GNU Lesser General Public |
| ** License version 2.1 as published by the Free Software Foundation and |
| ** appearing in the file LICENSE.LGPL included in the packaging of this |
| ** file. Please review the following information to ensure the GNU Lesser |
| ** General Public License version 2.1 requirements will be met: |
| ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
| ** |
| ** In addition, as a special exception, Nokia gives you certain additional |
| ** rights. These rights are described in the Nokia Qt LGPL Exception |
| ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU General |
| ** Public License version 3.0 as published by the Free Software Foundation |
| ** and appearing in the file LICENSE.GPL included in the packaging of this |
| ** file. Please review the following information to ensure the GNU General |
| ** Public License version 3.0 requirements will be met: |
| ** http://www.gnu.org/copyleft/gpl.html. |
| ** |
| ** Other Usage |
| ** Alternatively, this file may be used in accordance with the terms and |
| ** conditions contained in a signed written agreement between you and Nokia. |
| ** |
| ** |
| ** |
| ** |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "qplatformdefs.h" |
| #include "qlibrary.h" |
| |
| #ifndef QT_NO_LIBRARY |
| |
| #include "qlibrary_p.h" |
| #include <qstringlist.h> |
| #include <qfile.h> |
| #include <qfileinfo.h> |
| #include <qmutex.h> |
| #include <qmap.h> |
| #include <qsettings.h> |
| #include <qdatetime.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> |
| |
| QT_BEGIN_NAMESPACE |
| |
| //#define QT_DEBUG_COMPONENT |
| |
| #ifdef QT_NO_DEBUG |
| # define QLIBRARY_AS_DEBUG false |
| #else |
| # define QLIBRARY_AS_DEBUG true |
| #endif |
| |
| #if defined(Q_OS_UNIX) && !defined(Q_OS_MAC) |
| // 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 |
| |
| Q_GLOBAL_STATIC(QMutex, qt_library_mutex) |
| |
| /*! |
| \class QLibrary |
| \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 |
| cannot be found, QLibrary tries the name with different |
| platform-specific file suffixes, like ".so" on Unix, ".dylib" on |
| the Mac, or ".dll" on Windows and Symbian. 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. |
| |
| 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. |
| |
| Note: In Symbian resolving symbols using their names is supported |
| only if the library is built as STDDLL. Otherwise ordinals must |
| be used. Also, in Symbian the path of the library is ignored and |
| system default library location is always used. |
| |
| 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 0 and won't be |
| called. |
| |
| \snippet doc/src/snippets/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 doc/src/snippets/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. |
| |
| \sa loadHints |
| */ |
| |
| |
| #ifndef QT_NO_PLUGIN_CHECK |
| struct qt_token_info |
| { |
| qt_token_info(const char *f, const ulong fc) |
| : fields(f), field_count(fc), results(fc), lengths(fc) |
| { |
| results.fill(0); |
| lengths.fill(0); |
| } |
| |
| const char *fields; |
| const ulong field_count; |
| |
| QVector<const char *> results; |
| QVector<ulong> lengths; |
| }; |
| |
| /* |
| return values: |
| 1 parse ok |
| 0 eos |
| -1 parse error |
| */ |
| static int qt_tokenize(const char *s, ulong s_len, ulong *advance, |
| qt_token_info &token_info) |
| { |
| ulong pos = 0, field = 0, fieldlen = 0; |
| char current; |
| int ret = -1; |
| *advance = 0; |
| for (;;) { |
| current = s[pos]; |
| |
| // next char |
| ++pos; |
| ++fieldlen; |
| ++*advance; |
| |
| if (! current || pos == s_len + 1) { |
| // save result |
| token_info.results[(int)field] = s; |
| token_info.lengths[(int)field] = fieldlen - 1; |
| |
| // end of string |
| ret = 0; |
| break; |
| } |
| |
| if (current == token_info.fields[field]) { |
| // save result |
| token_info.results[(int)field] = s; |
| token_info.lengths[(int)field] = fieldlen - 1; |
| |
| // end of field |
| fieldlen = 0; |
| ++field; |
| if (field == token_info.field_count - 1) { |
| // parse ok |
| ret = 1; |
| } |
| if (field == token_info.field_count) { |
| // done parsing |
| break; |
| } |
| |
| // reset string and its length |
| s = s + pos; |
| s_len -= pos; |
| pos = 0; |
| } |
| } |
| |
| return ret; |
| } |
| |
| /* |
| returns true if the string s was correctly parsed, false otherwise. |
| */ |
| static bool qt_parse_pattern(const char *s, uint *version, bool *debug, QByteArray *key) |
| { |
| bool ret = true; |
| |
| qt_token_info pinfo("=\n", 2); |
| int parse; |
| ulong at = 0, advance, parselen = qstrlen(s); |
| do { |
| parse = qt_tokenize(s + at, parselen, &advance, pinfo); |
| if (parse == -1) { |
| ret = false; |
| break; |
| } |
| |
| at += advance; |
| parselen -= advance; |
| |
| if (qstrncmp("version", pinfo.results[0], pinfo.lengths[0]) == 0) { |
| // parse version string |
| qt_token_info pinfo2("..-", 3); |
| if (qt_tokenize(pinfo.results[1], pinfo.lengths[1], |
| &advance, pinfo2) != -1) { |
| QByteArray m(pinfo2.results[0], pinfo2.lengths[0]); |
| QByteArray n(pinfo2.results[1], pinfo2.lengths[1]); |
| QByteArray p(pinfo2.results[2], pinfo2.lengths[2]); |
| *version = (m.toUInt() << 16) | (n.toUInt() << 8) | p.toUInt(); |
| } else { |
| ret = false; |
| break; |
| } |
| } else if (qstrncmp("debug", pinfo.results[0], pinfo.lengths[0]) == 0) { |
| *debug = qstrncmp("true", pinfo.results[1], pinfo.lengths[1]) == 0; |
| } else if (qstrncmp("buildkey", pinfo.results[0], |
| pinfo.lengths[0]) == 0){ |
| // save buildkey |
| *key = QByteArray(pinfo.results[1], pinfo.lengths[1]); |
| } |
| } while (parse == 1 && parselen > 0); |
| |
| return ret; |
| } |
| #endif // QT_NO_PLUGIN_CHECK |
| |
| #if defined(Q_OS_UNIX) && !defined(Q_OS_MAC) && !defined(Q_OS_SYMBIAN) && !defined(QT_NO_PLUGIN_CHECK) |
| |
| static long qt_find_pattern(const char *s, ulong 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 || p_len > s_len) return -1; |
| ulong 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; |
| 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 false if version/key information is not present, or if the |
| information could not be read. |
| Returns true if version/key information is present and successfully read. |
| */ |
| static bool qt_unix_query(const QString &library, uint *version, bool *debug, QByteArray *key, QLibraryPrivate *lib = 0) |
| { |
| QFile file(library); |
| if (!file.open(QIODevice::ReadOnly)) { |
| if (lib) |
| lib->errorString = file.errorString(); |
| if (qt_debug_component()) { |
| qWarning("%s: %s", (const char*) QFile::encodeName(library), |
| qPrintable(qt_error_string(errno))); |
| } |
| return false; |
| } |
| |
| QByteArray data; |
| const char *filedata = 0; |
| ulong fdlen = file.size(); |
| filedata = (char *) file.map(0, fdlen); |
| if (filedata == 0) { |
| // try reading the data into memory instead |
| data = file.readAll(); |
| filedata = data.constData(); |
| fdlen = data.size(); |
| } |
| |
| // verify that the pattern is present in the plugin |
| const char pattern[] = "pattern=QT_PLUGIN_VERIFICATION_DATA"; |
| const ulong plen = qstrlen(pattern); |
| long pos = qt_find_pattern(filedata, fdlen, pattern, plen); |
| |
| bool ret = false; |
| if (pos >= 0) |
| ret = qt_parse_pattern(filedata + pos, version, debug, key); |
| |
| if (!ret && lib) |
| lib->errorString = QLibrary::tr("Plugin verification data mismatch in '%1'").arg(library); |
| file.close(); |
| return ret; |
| } |
| |
| #endif // Q_OS_UNIX && !Q_OS_MAC && !defined(Q_OS_SYMBIAN) && !defined(QT_NO_PLUGIN_CHECK) |
| |
| typedef QMap<QString, QLibraryPrivate*> LibraryMap; |
| |
| struct LibraryData { |
| LibraryData() : settings(0) { } |
| ~LibraryData() { |
| delete settings; |
| } |
| |
| QSettings *settings; |
| LibraryMap libraryMap; |
| }; |
| |
| Q_GLOBAL_STATIC(LibraryData, libraryData) |
| |
| static LibraryMap *libraryMap() |
| { |
| LibraryData *data = libraryData(); |
| return data ? &data->libraryMap : 0; |
| } |
| |
| QLibraryPrivate::QLibraryPrivate(const QString &canonicalFileName, const QString &version) |
| :pHnd(0), fileName(canonicalFileName), fullVersion(version), instance(0), qt_version(0), |
| libraryRefCount(1), libraryUnloadCount(0), pluginState(MightBeAPlugin) |
| { libraryMap()->insert(canonicalFileName, this); } |
| |
| QLibraryPrivate *QLibraryPrivate::findOrCreate(const QString &fileName, const QString &version) |
| { |
| QMutexLocker locker(qt_library_mutex()); |
| if (QLibraryPrivate *lib = libraryMap()->value(fileName)) { |
| lib->libraryRefCount.ref(); |
| return lib; |
| } |
| |
| return new QLibraryPrivate(fileName, version); |
| } |
| |
| QLibraryPrivate::~QLibraryPrivate() |
| { |
| LibraryMap * const map = libraryMap(); |
| if (map) { |
| QLibraryPrivate *that = map->take(fileName); |
| Q_ASSERT(this == that); |
| Q_UNUSED(that); |
| } |
| } |
| |
| void *QLibraryPrivate::resolve(const char *symbol) |
| { |
| if (!pHnd) |
| return 0; |
| return resolve_sys(symbol); |
| } |
| |
| |
| bool QLibraryPrivate::load() |
| { |
| libraryUnloadCount.ref(); |
| if (pHnd) |
| return true; |
| if (fileName.isEmpty()) |
| return false; |
| return load_sys(); |
| } |
| |
| bool QLibraryPrivate::unload() |
| { |
| if (!pHnd) |
| return false; |
| if (!libraryUnloadCount.deref()) { // only unload if ALL QLibrary instance wanted to |
| if (instance) |
| delete instance(); |
| if (unload_sys()) { |
| instance = 0; |
| pHnd = 0; |
| } |
| } |
| |
| return (pHnd == 0); |
| } |
| |
| void QLibraryPrivate::release() |
| { |
| QMutexLocker locker(qt_library_mutex()); |
| if (!libraryRefCount.deref()) |
| delete this; |
| } |
| |
| bool QLibraryPrivate::loadPlugin() |
| { |
| if (instance) { |
| libraryUnloadCount.ref(); |
| return true; |
| } |
| if (load()) { |
| instance = (QtPluginInstanceFunction)resolve("qt_plugin_instance"); |
| #if defined(Q_OS_SYMBIAN) |
| if (!instance) { |
| // If resolving with function name failed (i.e. not STDDLL), |
| // try resolving using known ordinal, which for |
| // qt_plugin_instance function is always "2". |
| instance = (QtPluginInstanceFunction)resolve("2"); |
| } |
| #endif |
| return instance; |
| } |
| return false; |
| } |
| |
| /*! |
| Returns true if \a fileName has a valid suffix for a loadable |
| library; otherwise returns false. |
| |
| \table |
| \header \i Platform \i Valid suffixes |
| \row \i Windows \i \c .dll |
| \row \i Unix/Linux \i \c .so |
| \row \i AIX \i \c .a |
| \row \i HP-UX \i \c .sl, \c .so (HP-UXi) |
| \row \i Mac OS X \i \c .dylib, \c .bundle, \c .so |
| \row \i Symbian \i \c .dll |
| \endtable |
| |
| Trailing versioning numbers on Unix are ignored. |
| */ |
| bool QLibrary::isLibrary(const QString &fileName) |
| { |
| #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) |
| return fileName.endsWith(QLatin1String(".dll")); |
| #elif defined(Q_OS_SYMBIAN) |
| // Plugin stubs are also considered libraries in Symbian. |
| return (fileName.endsWith(QLatin1String(".dll")) || |
| fileName.endsWith(QLatin1String(".qtplugin"))); |
| #else |
| QString completeSuffix = QFileInfo(fileName).completeSuffix(); |
| if (completeSuffix.isEmpty()) |
| return false; |
| QStringList suffixes = completeSuffix.split(QLatin1Char('.')); |
| # if defined(Q_OS_DARWIN) |
| |
| // On Mac, libs look like libmylib.1.0.0.dylib |
| const QString lastSuffix = suffixes.at(suffixes.count() - 1); |
| const QString firstSuffix = suffixes.at(0); |
| |
| bool valid = (lastSuffix == QLatin1String("dylib") |
| || firstSuffix == QLatin1String("so") |
| || firstSuffix == QLatin1String("bundle")); |
| |
| return valid; |
| # else // Generic Unix |
| 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_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(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 |
| #endif |
| |
| } |
| |
| bool QLibraryPrivate::isPlugin(QSettings *settings) |
| { |
| errorString.clear(); |
| if (pluginState != MightBeAPlugin) |
| return pluginState == IsAPlugin; |
| |
| #ifndef QT_NO_PLUGIN_CHECK |
| bool debug = !QLIBRARY_AS_DEBUG; |
| QByteArray key; |
| 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 false; |
| } |
| #endif |
| |
| QFileInfo fileinfo(fileName); |
| |
| #ifndef QT_NO_DATESTRING |
| lastModified = fileinfo.lastModified().toString(Qt::ISODate); |
| #endif |
| QString regkey = QString::fromLatin1("Qt Plugin Cache %1.%2.%3/%4") |
| .arg((QT_VERSION & 0xff0000) >> 16) |
| .arg((QT_VERSION & 0xff00) >> 8) |
| .arg(QLIBRARY_AS_DEBUG ? QLatin1String("debug") : QLatin1String("false")) |
| .arg(fileName); |
| #ifdef Q_WS_MAC |
| // On Mac, add the application arch to the reg key in order to |
| // cache plugin information separately for each arch. This prevents |
| // Qt from wrongly caching plugin load failures when the archs |
| // don't match. |
| #if defined(__x86_64__) |
| regkey += QLatin1String("-x86_64"); |
| #elif defined(__i386__) |
| regkey += QLatin1String("-i386"); |
| #elif defined(__ppc64__) |
| regkey += QLatin1String("-ppc64"); |
| #elif defined(__ppc__) |
| regkey += QLatin1String("-ppc"); |
| #endif |
| #endif // Q_WS_MAC |
| |
| QStringList reg; |
| #ifndef QT_NO_SETTINGS |
| if (!settings) { |
| settings = libraryData()->settings; |
| if (!settings) { |
| settings = new QSettings(QSettings::UserScope, QLatin1String("Trolltech")); |
| libraryData()->settings = settings; |
| } |
| } |
| reg = settings->value(regkey).toStringList(); |
| #endif |
| if (reg.count() == 4 && lastModified == reg.at(3)) { |
| qt_version = reg.at(0).toUInt(0, 16); |
| debug = bool(reg.at(1).toInt()); |
| key = reg.at(2).toLatin1(); |
| success = qt_version != 0; |
| } else { |
| #if defined(Q_OS_UNIX) && !defined(Q_OS_MAC) && !defined(Q_OS_SYMBIAN) |
| if (!pHnd) { |
| // use unix shortcut to avoid loading the library |
| success = qt_unix_query(fileName, &qt_version, &debug, &key, this); |
| } else |
| #endif |
| { |
| bool temporary_load = false; |
| #ifdef Q_OS_WIN |
| HMODULE hTempModule = 0; |
| #endif |
| if (!pHnd) { |
| #ifdef Q_OS_WIN |
| //avoid 'Bad Image' message box |
| UINT oldmode = SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX); |
| hTempModule = ::LoadLibraryEx((wchar_t*)QDir::toNativeSeparators(fileName).utf16(), 0, DONT_RESOLVE_DLL_REFERENCES); |
| SetErrorMode(oldmode); |
| #else |
| # if defined(Q_OS_SYMBIAN) |
| //Guard against accidentally trying to load non-plugin libraries by making sure the stub exists |
| if (fileinfo.exists()) |
| # endif |
| temporary_load = load_sys(); |
| #endif |
| } |
| # ifdef Q_CC_BOR |
| typedef const char * __stdcall (*QtPluginQueryVerificationDataFunction)(); |
| # else |
| typedef const char * (*QtPluginQueryVerificationDataFunction)(); |
| # endif |
| #ifdef Q_OS_WIN |
| QtPluginQueryVerificationDataFunction qtPluginQueryVerificationDataFunction = hTempModule |
| ? (QtPluginQueryVerificationDataFunction) |
| #ifdef Q_OS_WINCE |
| ::GetProcAddress(hTempModule, L"qt_plugin_query_verification_data") |
| #else |
| ::GetProcAddress(hTempModule, "qt_plugin_query_verification_data") |
| #endif |
| : (QtPluginQueryVerificationDataFunction) resolve("qt_plugin_query_verification_data"); |
| #else |
| QtPluginQueryVerificationDataFunction qtPluginQueryVerificationDataFunction = NULL; |
| # if defined(Q_OS_SYMBIAN) |
| if (temporary_load) { |
| qtPluginQueryVerificationDataFunction = (QtPluginQueryVerificationDataFunction) resolve("qt_plugin_query_verification_data"); |
| // If resolving with function name failed (i.e. not STDDLL), try resolving using known ordinal |
| if (!qtPluginQueryVerificationDataFunction) |
| qtPluginQueryVerificationDataFunction = (QtPluginQueryVerificationDataFunction) resolve("1"); |
| } |
| # else |
| qtPluginQueryVerificationDataFunction = (QtPluginQueryVerificationDataFunction) resolve("qt_plugin_query_verification_data"); |
| # endif |
| #endif |
| |
| if (!qtPluginQueryVerificationDataFunction |
| || !qt_parse_pattern(qtPluginQueryVerificationDataFunction(), &qt_version, &debug, &key)) { |
| qt_version = 0; |
| key = "unknown"; |
| if (temporary_load) |
| unload_sys(); |
| } else { |
| success = true; |
| } |
| #ifdef Q_OS_WIN |
| if (hTempModule) { |
| BOOL ok = ::FreeLibrary(hTempModule); |
| if (ok) { |
| hTempModule = 0; |
| } |
| |
| } |
| #endif |
| } |
| |
| // Qt 4.5 compatibility: stl doesn't affect binary compatibility |
| key.replace(" no-stl", ""); |
| |
| #ifndef QT_NO_SETTINGS |
| QStringList queried; |
| queried << QString::number(qt_version,16) |
| << QString::number((int)debug) |
| << QLatin1String(key) |
| << lastModified; |
| settings->setValue(regkey, queried); |
| #endif |
| } |
| |
| 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); |
| } |
| return false; |
| } |
| |
| pluginState = IsNotAPlugin; // be pessimistic |
| |
| 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]", |
| (const char*) QFile::encodeName(fileName), |
| (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")); |
| } else if (key != QT_BUILD_KEY |
| // we may have some compatibility keys, try them too: |
| #ifdef QT_BUILD_KEY_COMPAT |
| && key != QT_BUILD_KEY_COMPAT |
| #endif |
| #ifdef QT_BUILD_KEY_COMPAT2 |
| && key != QT_BUILD_KEY_COMPAT2 |
| #endif |
| ) { |
| if (qt_debug_component()) { |
| qWarning("In %s:\n" |
| " Plugin uses incompatible Qt library\n" |
| " expected build key \"%s\", got \"%s\"", |
| (const char*) QFile::encodeName(fileName), |
| QT_BUILD_KEY, |
| key.isEmpty() ? "<null>" : (const char *) key); |
| } |
| errorString = QLibrary::tr("The plugin '%1' uses incompatible Qt library." |
| " Expected build key \"%2\", got \"%3\"") |
| .arg(fileName) |
| .arg(QLatin1String(QT_BUILD_KEY)) |
| .arg(key.isEmpty() ? QLatin1String("<null>") : QLatin1String((const char *) key)); |
| #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; |
| } |
| |
| return pluginState == IsAPlugin; |
| #else |
| Q_UNUSED(settings); |
| return pluginState == MightBeAPlugin; |
| #endif |
| } |
| |
| /*! |
| Loads the library and returns true if the library was loaded |
| successfully; otherwise returns 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 true if the library could be |
| unloaded; otherwise returns 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 true if the library is loaded; otherwise returns 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 Mac OS X, and ".dll" on Windows. (See \l{fileName}.) |
| |
| Note: In Symbian the path portion of the \a fileName is ignored. |
| */ |
| 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 and Symbian. |
| |
| 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 Mac OS X, and ".dll" on Windows. (See \l{fileName}.) |
| |
| Note: In Symbian the path portion of the \a fileName is ignored. |
| */ |
| 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 and Symbian. |
| |
| 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 Mac OS X, and ".dll" on Windows. (See \l{fileName}.) |
| |
| Note: In Symbian the path portion of the \a fileName is ignored. |
| */ |
| 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 (e.g. \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". |
| |
| Note: In Symbian the path portion of the \a fileName is ignored. |
| */ |
| |
| 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); |
| d->loadHints = 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 and Symbian. |
| |
| Note: In Symbian the path portion of the \a fileName is ignored. |
| |
| \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()); |
| d->loadHints = 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 and Symbian. |
| |
| Note: In Symbian the path portion of the \a fileName is ignored. |
| |
| \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); |
| d->loadHints = lh; |
| } |
| |
| /*! |
| Returns the address of the exported symbol \a symbol. The library is |
| loaded if necessary. The function returns 0 if the symbol could |
| not be resolved or if the library could not be loaded. |
| |
| Example: |
| \snippet doc/src/snippets/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 doc/src/snippets/code/src_corelib_plugin_qlibrary.cpp 3 |
| |
| with \c MY_EXPORT defined as |
| |
| \snippet doc/src/snippets/code/src_corelib_plugin_qlibrary.cpp 4 |
| |
| Note: In Symbian resolving with symbol names works only if the loaded |
| library was built as STDDLL. Otherwise, the ordinals must be used. |
| */ |
| void *QLibrary::resolve(const char *symbol) |
| { |
| if (!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 0 if the symbol could not be resolved or if |
| the library could not be loaded. |
| |
| Note: In Symbian resolving with symbol names works only if the loaded |
| library was built as STDDLL. Otherwise, the ordinals must be used. |
| |
| \sa resolve() |
| */ |
| void *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 0 if the symbol could not be resolved or if |
| the library could not be loaded. |
| |
| Note: In Symbian resolving with symbol names works only if the loaded |
| library was built as STDDLL. Otherwise, the ordinals must be used. |
| |
| \sa resolve() |
| */ |
| void *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 0 if the symbol could not be resolved or if |
| the library could not be loaded. |
| |
| Note: In Symbian resolving with symbol names works only if the loaded |
| library was built as STDDLL. Otherwise, the ordinals must be used. |
| |
| \sa resolve() |
| */ |
| void *QLibrary::resolve(const QString &fileName, const QString &version, const char *symbol) |
| { |
| QLibrary library(fileName, version); |
| return library.resolve(symbol); |
| } |
| |
| /*! |
| \fn QString QLibrary::library() const |
| |
| Use fileName() instead. |
| */ |
| |
| /*! |
| \fn void QLibrary::setAutoUnload( bool b ) |
| |
| Use load(), isLoaded(), and unload() as necessary instead. |
| */ |
| |
| /*! |
| \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 loadHint 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. |
| */ |
| void QLibrary::setLoadHints(LoadHints hints) |
| { |
| if (!d) { |
| d = QLibraryPrivate::findOrCreate(QString()); // ugly, but we need a d-ptr |
| d->errorString.clear(); |
| } |
| d->loadHints = hints; |
| } |
| |
| QLibrary::LoadHints QLibrary::loadHints() const |
| { |
| return d ? d->loadHints : (QLibrary::LoadHints)0; |
| } |
| |
| /* Internal, for debugging */ |
| bool qt_debug_component() |
| { |
| #if defined(QT_DEBUG_COMPONENT) |
| return true; //compatibility? |
| #else |
| static int debug_env = -1; |
| if (debug_env == -1) |
| debug_env = QT_PREPEND_NAMESPACE(qgetenv)("QT_DEBUG_PLUGINS").toInt(); |
| |
| return debug_env != 0; |
| #endif |
| } |
| |
| QT_END_NAMESPACE |
| |
| #endif // QT_NO_LIBRARY |