00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
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
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
00056
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
00175
00176
00177
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
00190 ++pos;
00191 ++fieldlen;
00192 ++*advance;
00193
00194 if (! current || pos == s_len + 1) {
00195
00196 token_info.results[(int)field] = s;
00197 token_info.lengths[(int)field] = fieldlen - 1;
00198
00199
00200 ret = 0;
00201 break;
00202 }
00203
00204 if (current == token_info.fields[field]) {
00205
00206 token_info.results[(int)field] = s;
00207 token_info.lengths[(int)field] = fieldlen - 1;
00208
00209
00210 fieldlen = 0;
00211 ++field;
00212 if (field == token_info.field_count - 1) {
00213
00214 ret = 1;
00215 }
00216 if (field == token_info.field_count) {
00217
00218 break;
00219 }
00220
00221
00222 s = s + pos;
00223 s_len -= pos;
00224 pos = 0;
00225 }
00226 }
00227
00228 return ret;
00229 }
00230
00231
00232
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
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
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
00289
00290
00291
00292
00293
00294
00295
00296
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
00322
00323
00324
00325
00326
00327
00328
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
00353 filedata = mapaddr;
00354 fdlen = maplen;
00355 } else {
00356
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
00366 data = file.readAll();
00367 filedata = data.data();
00368 fdlen = data.size();
00369 #ifdef USE_MMAP
00370 }
00371 #endif // USE_MMAP
00372
00373
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())
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
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
00515
00516
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
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;
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
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
00985 bool qt_debug_component()
00986 {
00987 #if defined(QT_DEBUG_COMPONENT)
00988 return true;
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