src/corelib/plugin/qlibrary.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002 **
00003 ** Copyright (C) 1992-2006 Trolltech ASA. All rights reserved.
00004 **
00005 ** This file is part of the QtCore module of the Qt Toolkit.
00006 **
00007 ** This file may be used under the terms of the GNU General Public
00008 ** License version 2.0 as published by the Free Software Foundation
00009 ** and appearing in the file LICENSE.GPL included in the packaging of
00010 ** this file.  Please review the following information to ensure GNU
00011 ** General Public Licensing requirements will be met:
00012 ** http://www.trolltech.com/products/qt/opensource.html
00013 **
00014 ** If you are unsure which license is appropriate for your use, please
00015 ** review the following information:
00016 ** http://www.trolltech.com/products/qt/licensing.html or contact the
00017 ** sales department at sales@trolltech.com.
00018 **
00019 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00020 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00021 **
00022 ****************************************************************************/
00023 
00024 #include "qplatformdefs.h"
00025 #include "qlibrary.h"
00026 
00027 #ifndef QT_NO_LIBRARY
00028 
00029 #include "qlibrary_p.h"
00030 #include <qstringlist.h>
00031 #include <qfile.h>
00032 #include <qfileinfo.h>
00033 #include <qmutex.h>
00034 #include <qmap.h>
00035 #include <qsettings.h>
00036 #include <qdatetime.h>
00037 #ifdef Q_OS_MAC
00038 #  include <private/qcore_mac_p.h>
00039 #endif
00040 #ifndef NO_ERRNO_H
00041 #include <errno.h>
00042 #endif // NO_ERROR_H
00043 #include <qdebug.h>
00044 #include <qvector.h>
00045 
00046 //#define QT_DEBUG_COMPONENT
00047 
00048 #ifdef QT_NO_DEBUG
00049 #  define QLIBRARY_AS_DEBUG false
00050 #else
00051 #  define QLIBRARY_AS_DEBUG true
00052 #endif
00053 
00054 #if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
00055 // We don't use separate debug and release libs on UNIX, so we want
00056 // to allow loading plugins, regardless of how they were built.
00057 #  define QT_NO_DEBUG_PLUGIN_CHECK
00058 #endif
00059 
00060 Q_GLOBAL_STATIC(QMutex, qt_library_mutex)
00061 
00062 
00157 struct qt_token_info
00158 {
00159     qt_token_info(const char *f, const ulong fc)
00160         : fields(f), field_count(fc), results(fc), lengths(fc)
00161     {
00162         results.fill(0);
00163         lengths.fill(0);
00164     }
00165 
00166     const char *fields;
00167     const ulong field_count;
00168 
00169     QVector<const char *> results;
00170     QVector<ulong> lengths;
00171 };
00172 
00173 /*
00174   return values:
00175        1 parse ok
00176        0 eos
00177       -1 parse error
00178 */
00179 static int qt_tokenize(const char *s, ulong s_len, ulong *advance,
00180                         qt_token_info &token_info)
00181 {
00182     ulong pos = 0, field = 0, fieldlen = 0;
00183     char current;
00184     int ret = -1;
00185     *advance = 0;
00186     for (;;) {
00187         current = s[pos];
00188 
00189         // next char
00190         ++pos;
00191         ++fieldlen;
00192         ++*advance;
00193 
00194         if (! current || pos == s_len + 1) {
00195             // save result
00196             token_info.results[(int)field] = s;
00197             token_info.lengths[(int)field] = fieldlen - 1;
00198 
00199             // end of string
00200             ret = 0;
00201             break;
00202         }
00203 
00204         if (current == token_info.fields[field]) {
00205             // save result
00206             token_info.results[(int)field] = s;
00207             token_info.lengths[(int)field] = fieldlen - 1;
00208 
00209             // end of field
00210             fieldlen = 0;
00211             ++field;
00212             if (field == token_info.field_count - 1) {
00213                 // parse ok
00214                 ret = 1;
00215             }
00216             if (field == token_info.field_count) {
00217                 // done parsing
00218                 break;
00219             }
00220 
00221             // reset string and its length
00222             s = s + pos;
00223             s_len -= pos;
00224             pos = 0;
00225         }
00226     }
00227 
00228     return ret;
00229 }
00230 
00231 /*
00232   returns true if the string s was correctly parsed, false otherwise.
00233 */
00234 static bool qt_parse_pattern(const char *s, uint *version, bool *debug, QByteArray *key)
00235 {
00236     bool ret = true;
00237 
00238     qt_token_info pinfo("=\n", 2);
00239     int parse;
00240     ulong at = 0, advance, parselen = qstrlen(s);
00241     do {
00242         parse = qt_tokenize(s + at, parselen, &advance, pinfo);
00243         if (parse == -1) {
00244             ret = false;
00245             break;
00246         }
00247 
00248         at += advance;
00249         parselen -= advance;
00250 
00251         if (qstrncmp("version", pinfo.results[0], pinfo.lengths[0]) == 0) {
00252             // parse version string
00253             qt_token_info pinfo2("..-", 3);
00254             if (qt_tokenize(pinfo.results[1], pinfo.lengths[1],
00255                               &advance, pinfo2) != -1) {
00256                 QByteArray m(pinfo2.results[0], pinfo2.lengths[0]);
00257                 QByteArray n(pinfo2.results[1], pinfo2.lengths[1]);
00258                 QByteArray p(pinfo2.results[2], pinfo2.lengths[2]);
00259                 *version  = (m.toUInt() << 16) | (n.toUInt() << 8) | p.toUInt();
00260             } else {
00261                 ret = false;
00262                 break;
00263             }
00264         } else if (qstrncmp("debug", pinfo.results[0], pinfo.lengths[0]) == 0) {
00265             *debug = qstrncmp("true", pinfo.results[1], pinfo.lengths[1]) == 0;
00266         } else if (qstrncmp("buildkey", pinfo.results[0],
00267                               pinfo.lengths[0]) == 0){
00268             // save buildkey
00269             *key = QByteArray(pinfo.results[1], pinfo.lengths[1] + 1);
00270         }
00271     } while (parse == 1 && parselen > 0);
00272 
00273     return ret;
00274 }
00275 
00276 #if defined(Q_OS_UNIX)
00277 
00278 #if defined(Q_OS_FREEBSD) || defined(Q_OS_LINUX)
00279 #  define USE_MMAP
00280 #  include <sys/types.h>
00281 #  include <sys/mman.h>
00282 #endif // Q_OS_FREEBSD || Q_OS_LINUX
00283 
00284 static long qt_find_pattern(const char *s, ulong s_len,
00285                              const char *pattern, ulong p_len)
00286 {
00287     /*
00288       we search from the end of the file because on the supported
00289       systems, the read-only data/text segments are placed at the end
00290       of the file.  HOWEVER, when building with debugging enabled, all
00291       the debug symbols are placed AFTER the data/text segments.
00292 
00293       what does this mean?  when building in release mode, the search
00294       is fast because the data we are looking for is at the end of the
00295       file... when building in debug mode, the search is slower
00296       because we have to skip over all the debugging symbols first
00297     */
00298 
00299     if (! s || ! pattern || p_len > s_len) return -1;
00300     ulong i, hs = 0, hp = 0, delta = s_len - p_len;
00301 
00302     for (i = 0; i < p_len; ++i) {
00303         hs += s[delta + i];
00304         hp += pattern[i];
00305     }
00306     i = delta;
00307     for (;;) {
00308         if (hs == hp && qstrncmp(s + i, pattern, p_len) == 0)
00309             return i;
00310         if (i == 0)
00311             break;
00312         --i;
00313         hs -= s[i + p_len];
00314         hs += s[i];
00315     }
00316 
00317     return -1;
00318 }
00319 
00320 /*
00321   This opens the specified library, mmaps it into memory, and searches
00322   for the QT_PLUGIN_VERIFICATION_DATA.  The advantage of this approach is that
00323   we can get the verification data without have to actually load the library.
00324   This lets us detect mismatches more safely.
00325 
00326   Returns false if version/key information is not present, or if the
00327                 information could not be read.
00328   Returns  true if version/key information is present and successfully read.
00329 */
00330 static bool qt_unix_query(const QString &library, uint *version, bool *debug, QByteArray *key, QLibraryPrivate *lib = 0)
00331 {
00332     QFile file(library);
00333     if (!file.open(QIODevice::ReadOnly)) {
00334         if (lib)
00335             lib->errorString = file.errorString();
00336         if (qt_debug_component()) {
00337             qWarning("%s: %s", (const char*) QFile::encodeName(library),
00338                 qPrintable(qt_error_string(errno)));
00339         }
00340         return false;
00341     }
00342 
00343     QByteArray data;
00344     char *filedata = 0;
00345     ulong fdlen = 0;
00346 
00347 #ifdef USE_MMAP
00348     char *mapaddr = 0;
00349     size_t maplen = file.size();
00350     mapaddr = (char *) mmap(mapaddr, maplen, PROT_READ, MAP_PRIVATE, file.handle(), 0);
00351     if (mapaddr != MAP_FAILED) {
00352         // mmap succeeded
00353         filedata = mapaddr;
00354         fdlen = maplen;
00355     } else {
00356         // mmap failed
00357         if (qt_debug_component()) {
00358             qWarning("mmap: %s", qPrintable(qt_error_string(errno)));
00359         }
00360         if (lib)
00361             lib->errorString = QLibrary::tr("Could not mmap '%1': %2")
00362                 .arg(library)
00363                 .arg(qt_error_string());
00364 #endif // USE_MMAP
00365         // try reading the data into memory instead
00366         data = file.readAll();
00367         filedata = data.data();
00368         fdlen = data.size();
00369 #ifdef USE_MMAP
00370     }
00371 #endif // USE_MMAP
00372 
00373     // verify that the pattern is present in the plugin
00374     const char pattern[] = "pattern=QT_PLUGIN_VERIFICATION_DATA";
00375     const ulong plen = qstrlen(pattern);
00376     long pos = qt_find_pattern(filedata, fdlen, pattern, plen);
00377 
00378     bool ret = false;
00379     if (pos >= 0)
00380         ret = qt_parse_pattern(filedata + pos, version, debug, key);
00381 
00382     if (!ret && lib)
00383         lib->errorString = QLibrary::tr("Plugin verification data mismatch in '%1'").arg(library);
00384 #ifdef USE_MMAP
00385     if (mapaddr != MAP_FAILED && munmap(mapaddr, maplen) != 0) {
00386         if (qt_debug_component())
00387             qWarning("munmap: %s", qPrintable(qt_error_string(errno)));
00388         if (lib)
00389             lib->errorString = QLibrary::tr("Could not unmap '%1': %2")
00390                 .arg(library)
00391                 .arg( qt_error_string() );
00392     }
00393 #endif // USE_MMAP
00394 
00395     file.close();
00396     return ret;
00397 }
00398 
00399 #endif // Q_OS_UNIX
00400 
00401 typedef QMap<QString, QLibraryPrivate*> LibraryMap;
00402 Q_GLOBAL_STATIC(LibraryMap, libraryMap)
00403 
00404 QLibraryPrivate::QLibraryPrivate(const QString &canonicalFileName, int verNum)
00405     :pHnd(0), fileName(canonicalFileName), majorVerNum(verNum), instance(0), qt_version(0),
00406      libraryRefCount(1), libraryUnloadCount(1), pluginState(MightBeAPlugin)
00407 { libraryMap()->insert(canonicalFileName, this); }
00408 
00409 QLibraryPrivate *QLibraryPrivate::findOrCreate(const QString &fileName, int verNum)
00410 {
00411     QMutexLocker locker(qt_library_mutex());
00412     if (QLibraryPrivate *lib = libraryMap()->value(fileName)) {
00413         lib->libraryUnloadCount.ref();
00414         lib->libraryRefCount.ref();
00415         return lib;
00416     }
00417 
00418     return new QLibraryPrivate(fileName, verNum);
00419 }
00420 
00421 QLibraryPrivate::~QLibraryPrivate()
00422 {
00423     LibraryMap * const map = libraryMap();
00424     if (map) {
00425         QLibraryPrivate *that = map->take(fileName);
00426         Q_ASSERT(this == that);
00427   Q_UNUSED(that);
00428     }
00429 }
00430 
00431 void *QLibraryPrivate::resolve(const char *symbol)
00432 {
00433     if (!pHnd)
00434         return 0;
00435     return resolve_sys(symbol);
00436 }
00437 
00438 
00439 bool QLibraryPrivate::load()
00440 {
00441     if (pHnd)
00442         return true;
00443     if (fileName.isEmpty())
00444         return false;
00445     return load_sys();
00446 }
00447 
00448 bool QLibraryPrivate::unload()
00449 {
00450     if (!pHnd)
00451         return true;
00452     if (!libraryUnloadCount.deref()) // only unload if ALL QLibrary instance wanted to
00453         if  (unload_sys())
00454             pHnd = 0;
00455     return (pHnd == 0);
00456 }
00457 
00458 void QLibraryPrivate::release()
00459 {
00460     QMutexLocker locker(qt_library_mutex());
00461     if (!libraryRefCount.deref())
00462         delete this;
00463 }
00464 
00465 bool QLibraryPrivate::loadPlugin()
00466 {
00467     if (instance)
00468         return true;
00469     if (load()) {
00470         instance = (QtPluginInstanceFunction)resolve("qt_plugin_instance");
00471         return instance;
00472     }
00473     return false;
00474 }
00475 
00491 bool QLibrary::isLibrary(const QString &fileName)
00492 {
00493 #if defined(Q_OS_WIN32)
00494     return fileName.endsWith(QLatin1String(".dll"));
00495 #else
00496     QString completeSuffix = QFileInfo(fileName).completeSuffix();
00497     if (completeSuffix.isEmpty())
00498         return false;
00499     QStringList suffixes = completeSuffix.split(QLatin1Char('.'));
00500     QString suffix = suffixes.first();
00501 # if defined(Q_OS_DARWIN)
00502 
00503     // On Mac, libs look like libmylib.1.0.0.dylib
00504     const QString lastSuffix = suffixes.at(suffixes.count() - 1);
00505     const QString firstSuffix = suffixes.at(0);
00506 
00507     bool valid = (lastSuffix == QLatin1String("dylib")
00508             || firstSuffix == QLatin1String("so")
00509             || firstSuffix == QLatin1String("bundle"));
00510 
00511     return valid;
00512 # elif defined(Q_OS_HPUX)
00513 /*
00514     See "HP-UX Linker and Libraries User's Guide", section "Link-time Differences between PA-RISC and IPF":
00515     "In PA-RISC (PA-32 and PA-64) shared libraries are suffixed with .sl. In IPF (32-bit and 64-bit),
00516     the shared libraries are suffixed with .so. For compatibility, the IPF linker also supports the .sl suffix."
00517  */
00518     bool valid = (suffix == QLatin1String("sl"));
00519 #  if defined __ia64
00520     valid = valid || (suffix == QLatin1String("so"))
00521 #  endif
00522 # elif defined(Q_OS_AIX)
00523     bool valid = (suffix == QLatin1String("a") || suffix == QLatin1String("so"));
00524 # elif defined(Q_OS_UNIX)
00525     bool valid = (suffix == QLatin1String("so"));
00526 # else
00527     bool valid = false;
00528 # endif
00529 
00530     for (int i = 1; i < suffixes.count() && valid; ++i)
00531         suffixes.at(i).toInt(&valid);
00532     return valid;
00533 #endif
00534 
00535 }
00536 
00537 bool QLibraryPrivate::isPlugin()
00538 {
00539     if (pluginState != MightBeAPlugin)
00540         return pluginState == IsAPlugin;
00541 
00542 #ifndef QT_NO_PLUGIN_CHECK
00543     bool debug = !QLIBRARY_AS_DEBUG;
00544     QByteArray key;
00545     bool success = false;
00546 
00547     QFileInfo fileinfo(fileName);
00548 
00549 #ifndef QT_NO_DATESTRING
00550     lastModified  = fileinfo.lastModified().toString(Qt::ISODate);
00551 #endif
00552     QString regkey = QString::fromLatin1("Qt Plugin Cache %1.%2.%3/%4")
00553                      .arg((QT_VERSION & 0xff0000) >> 16)
00554                      .arg((QT_VERSION & 0xff00) >> 8)
00555                      .arg(QLIBRARY_AS_DEBUG ? QLatin1String("debug") : QLatin1String("false"))
00556                      .arg(fileName);
00557     QStringList reg;
00558 
00559     QSettings settings(QSettings::UserScope, QLatin1String("Trolltech"));
00560     reg = settings.value(regkey).toStringList();
00561     if (reg.count() == 4 && lastModified == reg.at(3)) {
00562         qt_version = reg.at(0).toUInt(0, 16);
00563         debug = bool(reg.at(1).toInt());
00564         key = reg.at(2).toLatin1();
00565         success = qt_version != 0;
00566     } else {
00567 #if defined(Q_OS_UNIX)
00568         if (!pHnd) {
00569             // use unix shortcut to avoid loading the library
00570             success = qt_unix_query(fileName, &qt_version, &debug, &key, this);
00571         } else
00572 #endif
00573         {
00574             bool temporary_load = false;
00575             if (!pHnd)
00576                 temporary_load =  load_sys();
00577 #  ifdef Q_CC_BOR
00578             typedef const char * __stdcall (*QtPluginQueryVerificationDataFunction)();
00579 #  else
00580             typedef const char * (*QtPluginQueryVerificationDataFunction)();
00581 #  endif
00582             QtPluginQueryVerificationDataFunction qtPluginQueryVerificationDataFunction =
00583                 (QtPluginQueryVerificationDataFunction) resolve("qt_plugin_query_verification_data");
00584 
00585             if (!qtPluginQueryVerificationDataFunction
00586                 || !qt_parse_pattern(qtPluginQueryVerificationDataFunction(), &qt_version, &debug, &key)) {
00587                 qt_version = 0;
00588                 key = "unknown";
00589                 if (temporary_load)
00590                     unload_sys();
00591             } else {
00592                 success = true;
00593             }
00594         }
00595 
00596         QStringList queried;
00597         queried << QString::number(qt_version,16)
00598                 << QString::number((int)debug)
00599                 << QLatin1String(key)
00600                 << lastModified;
00601         settings.setValue(regkey, queried);
00602     }
00603 
00604     if (!success)
00605         return false;
00606 
00607     pluginState = IsNotAPlugin; // be pessimistic
00608 
00609     if ((qt_version > QT_VERSION) || ((QT_VERSION & 0xff0000) > (qt_version & 0xff0000))) {
00610         if (qt_debug_component()) {
00611             qWarning("In %s:\n"
00612                  "  Plugin uses incompatible Qt library (%d.%d.%d) [%s]",
00613                  (const char*) QFile::encodeName(fileName),
00614                  (qt_version&0xff0000) >> 16, (qt_version&0xff00) >> 8, qt_version&0xff,
00615                  debug ? "debug" : "release");
00616         }
00617         errorString = QLibrary::tr("The plugin '%1' uses incompatible Qt library. (%2.%3.%4) [%5]")
00618             .arg(fileName)
00619             .arg((qt_version&0xff0000) >> 16)
00620             .arg((qt_version&0xff00) >> 8)
00621             .arg(qt_version&0xff)
00622             .arg(debug ? QLatin1String("debug") : QLatin1String("release"));
00623     } else if (key != QT_BUILD_KEY) {
00624         if (qt_debug_component()) {
00625             qWarning("In %s:\n"
00626                  "  Plugin uses incompatible Qt library\n"
00627                  "  expected build key \"%s\", got \"%s\"",
00628                  (const char*) QFile::encodeName(fileName),
00629                  QT_BUILD_KEY,
00630                  key.isEmpty() ? "<null>" : (const char *) key);
00631         }
00632         errorString = QLibrary::tr("The plugin '%1' uses incompatible Qt library."
00633                  " Expected build key \"%2\", got \"%3\"")
00634                  .arg(fileName)
00635                  .arg(QLatin1String(QT_BUILD_KEY))
00636                  .arg(key.isEmpty() ? QLatin1String("<null>") : QLatin1String((const char *) key));
00637 #ifndef QT_NO_DEBUG_PLUGIN_CHECK
00638     } else if(debug != QLIBRARY_AS_DEBUG) {
00639         //don't issue a qWarning since we will hopefully find a non-debug? --Sam
00640 #endif
00641     } else {
00642         pluginState = IsAPlugin;
00643     }
00644 
00645     return pluginState == IsAPlugin;
00646 #else
00647     return pluginState == MightBeAPlugin;
00648 #endif
00649 }
00650 
00661 bool QLibrary::load()
00662 {
00663     if (!d)
00664         return false;
00665     if (did_load)
00666         return d->pHnd;
00667     did_load = true;
00668     return d->load();
00669 }
00670 
00686 bool QLibrary::unload()
00687 {
00688     if (did_load) {
00689         did_load = false;
00690         return d->unload();
00691     }
00692     return false;
00693 }
00694 
00700 bool QLibrary::isLoaded() const
00701 {
00702     return d && d->pHnd;
00703 }
00704 
00705 
00709 QLibrary::QLibrary(QObject *parent)
00710     :QObject(parent), d(0), did_load(false)
00711 {
00712 }
00713 
00714 
00724 QLibrary::QLibrary(const QString& fileName, QObject *parent)
00725     :QObject(parent), d(0), did_load(false)
00726 {
00727     setFileName(fileName);
00728 }
00729 
00730 
00741 QLibrary::QLibrary(const QString& fileName, int verNum, QObject *parent)
00742     :QObject(parent), d(0), did_load(false)
00743 {
00744     setFileNameAndVersion(fileName, verNum);
00745 }
00746 
00755 QLibrary::~QLibrary()
00756 {
00757     if (d)
00758         d->release();
00759 }
00760 
00761 
00778 void QLibrary::setFileName(const QString &fileName)
00779 {
00780     if (d) {
00781         d->release();
00782         d = 0;
00783         did_load = false;
00784     }
00785     d = QLibraryPrivate::findOrCreate(fileName);
00786     if (d && d->pHnd)
00787         did_load = true;
00788 
00789 }
00790 
00791 QString QLibrary::fileName() const
00792 {
00793     if (d)
00794         return d->qualifiedFileName.isEmpty() ? d->fileName : d->qualifiedFileName;
00795     return QString();
00796 }
00797 
00806 void QLibrary::setFileNameAndVersion(const QString &fileName, int verNum)
00807 {
00808     if (d) {
00809         d->release();
00810         d = 0;
00811         did_load = false;
00812     }
00813     d = QLibraryPrivate::findOrCreate(fileName, verNum);
00814     if (d && d->pHnd)
00815         did_load = true;
00816 }
00817 
00858 void *QLibrary::resolve(const char *symbol)
00859 {
00860     if (!d)
00861         return 0;
00862     if (!d->pHnd)
00863         d->load();
00864     return d->resolve(symbol);
00865 }
00866 
00880 void *QLibrary::resolve(const QString &fileName, const char *symbol)
00881 {
00882     QLibrary library(fileName);
00883     return library.resolve(symbol);
00884 }
00885 
00900 void *QLibrary::resolve(const QString &fileName, int verNum, const char *symbol)
00901 {
00902     QLibrary library(fileName, verNum);
00903     return library.resolve(symbol);
00904 }
00905 
00924 QString QLibrary::errorString() const
00925 {
00926     return d->errorString.isEmpty() ? tr("Unknown error") : d->errorString;
00927 }
00928 
00974 void QLibrary::setLoadHints(LoadHints hints)
00975 {
00976     d->loadHints = hints;
00977 }
00978 
00979 QLibrary::LoadHints QLibrary::loadHints() const
00980 {
00981     return d->loadHints;
00982 }
00983 
00984 /* Internal, for debugging */
00985 bool qt_debug_component()
00986 {
00987 #if defined(QT_DEBUG_COMPONENT)
00988     return true;    //compatibility?
00989 #else
00990     static int debug_env = -1;
00991     if (debug_env == -1)
00992        debug_env = ::qgetenv("QT_DEBUG_PLUGINS").toInt();
00993 
00994     return debug_env != 0;
00995 #endif
00996 }
00997 
00998 #endif // QT_NO_LIBRARY

Generated on Thu Mar 15 11:53:35 2007 for Qt 4.2 User's Guide by  doxygen 1.5.1