diff --git a/src/libs/extensionsystem/patchedpluginloader.cpp b/src/libs/extensionsystem/patchedpluginloader.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2ff684e1f833d466f84cc2f8f88641258c69101c --- /dev/null +++ b/src/libs/extensionsystem/patchedpluginloader.cpp @@ -0,0 +1,1171 @@ + +#ifdef Q_WS_WIN +# include "QtCore/qt_windows.h" +#endif +#include "QtCore/qlibrary.h" +#include "QtCore/qpointer.h" +#include "QtCore/qstringlist.h" +#include "QtCore/qplugin.h" + + +#include "qplatformdefs.h" + +#include "qplugin.h" +#include "qpluginloader.h" +#include <qfileinfo.h> +#include "qdebug.h" + +#include "qplatformdefs.h" +#include "qlibrary.h" + +#include <qstringlist.h> +#include <qfile.h> +#include <qfileinfo.h> +#include <qmutex.h> +#include <qmap.h> +#include <qsettings.h> +#include <qdatetime.h> +#include <qcoreapplication.h> +#include <qvector.h> +#include <qdir.h> + + +#ifdef Q_OS_MAC +# include <private/qcore_mac_p.h> +#endif +#ifndef NO_ERRNO_H +#include <errno.h> +#endif // NO_ERROR_H + +#include "qplatformdefs.h" + +#ifdef Q_OS_MAC +# include <private/qcore_mac_p.h> +#endif + +#if defined(QT_AOUT_UNDERSCORE) +#include <string.h> +#endif + + +#if !defined(QT_HPUX_LD) +#include <dlfcn.h> +#endif + +QT_BEGIN_NAMESPACE + +bool qt_debug_component(); + +class PatchedLibraryPrivate +{ +public: + +#ifdef Q_WS_WIN + HINSTANCE +#else + void * +#endif + pHnd; + + QString fileName, qualifiedFileName; + QString fullVersion; + + bool load(); + bool loadPlugin(); // loads and resolves instance + bool unload(); + void release(); + void *resolve(const char *); + + static PatchedLibraryPrivate *findOrCreate(const QString &fileName, const QString &version = QString()); + + QtPluginInstanceFunction instance; + uint qt_version; + QString lastModified; + + QString errorString; + QLibrary::LoadHints loadHints; + + bool isPlugin(QSettings *settings = 0); + + +private: + explicit PatchedLibraryPrivate(const QString &canonicalFileName, const QString &version); + ~PatchedLibraryPrivate(); + + bool load_sys(); + bool unload_sys(); + void *resolve_sys(const char *); + + QAtomicInt libraryRefCount; + QAtomicInt libraryUnloadCount; + + enum {IsAPlugin, IsNotAPlugin, MightBeAPlugin } pluginState; + friend class PatchedLibraryPrivateHasFriends; +}; + +class PatchedPluginLoader +{ + Q_PROPERTY(QString fileName READ fileName WRITE setFileName) + Q_PROPERTY(QLibrary::LoadHints loadHints READ loadHints WRITE setLoadHints) +public: + explicit PatchedPluginLoader(QObject *parent = 0); + explicit PatchedPluginLoader(const QString &fileName, QObject *parent = 0); + ~PatchedPluginLoader(); + + QObject *instance(); + + static QObjectList staticInstances(); + + bool load(); + bool unload(); + bool isLoaded() const; + + void setFileName(const QString &fileName); + QString fileName() const; + + QString errorString() const; + + void setLoadHints(QLibrary::LoadHints loadHints); + QLibrary::LoadHints loadHints() const; + +private: + PatchedLibraryPrivate *d; + bool did_load; + Q_DISABLE_COPY(PatchedPluginLoader) +}; + +//#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, patched_qt_library_mutex) + + +#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(QT_NO_PLUGIN_CHECK) + +#if defined(Q_OS_FREEBSD) || defined(Q_OS_LINUX) +# define USE_MMAP +QT_BEGIN_INCLUDE_NAMESPACE +# include <sys/types.h> +# include <sys/mman.h> +QT_END_INCLUDE_NAMESPACE +#endif // Q_OS_FREEBSD || Q_OS_LINUX + +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, PatchedLibraryPrivate *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; + char *filedata = 0; + ulong fdlen = 0; + +#ifdef USE_MMAP + char *mapaddr = 0; + size_t maplen = file.size(); + mapaddr = (char *) mmap(mapaddr, maplen, PROT_READ, MAP_PRIVATE, file.handle(), 0); + if (mapaddr != MAP_FAILED) { + // mmap succeeded + filedata = mapaddr; + fdlen = maplen; + } else { + // mmap failed + if (qt_debug_component()) { + qWarning("mmap: %s", qPrintable(qt_error_string(errno))); + } + if (lib) + lib->errorString = QLibrary::tr("Could not mmap '%1': %2") + .arg(library) + .arg(qt_error_string()); +#endif // USE_MMAP + // try reading the data into memory instead + data = file.readAll(); + filedata = data.data(); + fdlen = data.size(); +#ifdef USE_MMAP + } +#endif // USE_MMAP + + // 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); +#ifdef USE_MMAP + if (mapaddr != MAP_FAILED && munmap(mapaddr, maplen) != 0) { + if (qt_debug_component()) + qWarning("munmap: %s", qPrintable(qt_error_string(errno))); + if (lib) + lib->errorString = QLibrary::tr("Could not unmap '%1': %2") + .arg(library) + .arg( qt_error_string() ); + } +#endif // USE_MMAP + + file.close(); + return ret; +} + +#endif // Q_OS_UNIX && !Q_OS_MAC && !defined(QT_NO_PLUGIN_CHECK) + +typedef QMap<QString, PatchedLibraryPrivate*> LibraryMap; +Q_GLOBAL_STATIC(LibraryMap, libraryMap) + +PatchedLibraryPrivate::PatchedLibraryPrivate(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); } + +PatchedLibraryPrivate *PatchedLibraryPrivate::findOrCreate(const QString &fileName, const QString &version) +{ + QMutexLocker locker(patched_qt_library_mutex()); + if (PatchedLibraryPrivate *lib = libraryMap()->value(fileName)) { + lib->libraryRefCount.ref(); + return lib; + } + + return new PatchedLibraryPrivate(fileName, version); +} + +PatchedLibraryPrivate::~PatchedLibraryPrivate() +{ + LibraryMap * const map = libraryMap(); + if (map) { + PatchedLibraryPrivate *that = map->take(fileName); + Q_ASSERT(this == that); + Q_UNUSED(that); + } +} + +void *PatchedLibraryPrivate::resolve(const char *symbol) +{ + if (!pHnd) + return 0; + return resolve_sys(symbol); +} + + +bool PatchedLibraryPrivate::load() +{ + libraryUnloadCount.ref(); + if (pHnd) + return true; + if (fileName.isEmpty()) + return false; + return load_sys(); +} + +bool PatchedLibraryPrivate::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 PatchedLibraryPrivate::release() +{ + QMutexLocker locker(patched_qt_library_mutex()); + if (!libraryRefCount.deref()) + delete this; +} + +bool PatchedLibraryPrivate::loadPlugin() +{ + if (instance) { + libraryUnloadCount.ref(); + return true; + } + if (load()) { + instance = (QtPluginInstanceFunction)resolve("qt_plugin_instance"); + 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 + \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")); +#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 PatchedLibraryPrivate::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; + + 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); + QStringList reg; +#ifndef QT_NO_SETTINGS + if (!settings) { + static QSettings *staticSettings = + new QSettings(QSettings::UserScope, QLatin1String("Trolltech")); + settings = staticSettings; + } + 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) + 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 + QT_WA({ + hTempModule = ::LoadLibraryExW((wchar_t*)QDir::toNativeSeparators(fileName).utf16(), 0, DONT_RESOLVE_DLL_REFERENCES); + } , { + temporary_load = load_sys(); + }); +#else + 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 + ::GetProcAddressW(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 = + (QtPluginQueryVerificationDataFunction) resolve("qt_plugin_query_verification_data"); +#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 + } + + QStringList queried; + queried << QString::number(qt_version,16) + << QString::number((int)debug) + << QLatin1String(key) + << lastModified; +#ifndef QT_NO_SETTINGS + 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 > QT_VERSION) || ((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 +#ifdef QT_BUILD_KEY_COMPAT + // be sure to load plugins using an older but compatible build key + && key != QT_BUILD_KEY_COMPAT +#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 +} + +/* 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 +} + +PatchedPluginLoader::PatchedPluginLoader(QObject *) + : d(0), did_load(false) +{ +} + +PatchedPluginLoader::PatchedPluginLoader(const QString &fileName, QObject *) + : d(0), did_load(false) +{ + setFileName(fileName); +} + +PatchedPluginLoader::~PatchedPluginLoader() +{ + if (d) + d->release(); +} + +QObject *PatchedPluginLoader::instance() +{ + if (!load()) + return 0; + if (d->instance) + return d->instance(); + return 0; +} + +bool PatchedPluginLoader::load() +{ + if (!d || d->fileName.isEmpty()) + return false; + if (did_load) + return d->pHnd && d->instance; + if (!d->isPlugin()) + return false; + did_load = true; + return d->loadPlugin(); +} + +bool PatchedPluginLoader::unload() +{ + if (did_load) { + did_load = false; + return d->unload(); + } + if (d) // Ouch + d->errorString = QLibrary::tr("The plugin was not loaded."); + return false; +} + +bool PatchedPluginLoader::isLoaded() const +{ + return d && d->pHnd && d->instance; +} + +void PatchedPluginLoader::setFileName(const QString &fileName) +{ +#if defined(QT_SHARED) + QLibrary::LoadHints lh; + if (d) { + lh = d->loadHints; + d->release(); + d = 0; + did_load = false; + } + QString fn = QFileInfo(fileName).canonicalFilePath(); + d = PatchedLibraryPrivate::findOrCreate(fn); + d->loadHints = lh; + if (fn.isEmpty()) + d->errorString = QLibrary::tr("The shared library was not found."); +#else + if (qt_debug_component()) { + qWarning("Cannot load %s into a statically linked Qt library.", + (const char*)QFile::encodeName(fileName)); + } + Q_UNUSED(fileName); +#endif +} + +QString PatchedPluginLoader::fileName() const +{ + if (d) + return d->fileName; + return QString(); +} + +QString PatchedPluginLoader::errorString() const +{ + return (!d || d->errorString.isEmpty()) ? QLibrary::tr("Unknown error") : d->errorString; +} + +typedef QList<QtPluginInstanceFunction> StaticInstanceFunctionList; +Q_GLOBAL_STATIC(StaticInstanceFunctionList, staticInstanceFunctionList) + +void PatchedPluginLoader::setLoadHints(QLibrary::LoadHints loadHints) +{ + if (!d) { + d = PatchedLibraryPrivate::findOrCreate(QString()); // ugly, but we need a d-ptr + d->errorString.clear(); + } + d->loadHints = loadHints; +} + +QLibrary::LoadHints PatchedPluginLoader::loadHints() const +{ + if (!d) { + PatchedPluginLoader *that = const_cast<PatchedPluginLoader *>(this); + that->d = PatchedLibraryPrivate::findOrCreate(QString()); // ugly, but we need a d-ptr + that->d->errorString.clear(); + } + return d->loadHints; +} + +void qRegisterStaticPluginInstanceFunction(QtPluginInstanceFunction function) +{ + staticInstanceFunctionList()->append(function); +} + +QObjectList PatchedPluginLoader::staticInstances() +{ + QObjectList instances; + StaticInstanceFunctionList *functions = staticInstanceFunctionList(); + if (functions) { + for (int i = 0; i < functions->count(); ++i) + instances.append((*functions)[i]()); + } + return instances; +} + + + + + + +#ifdef Q_OS_UNIX + + + +static QString qdlerror() +{ +#if !defined(QT_HPUX_LD) + const char *err = dlerror(); +#else + const char *err = strerror(errno); +#endif + return err ? QLatin1String("(")+QString::fromLocal8Bit(err) + QLatin1String(")"): QString(); +} + +bool PatchedLibraryPrivate::load_sys() +{ + QFileInfo fi(fileName); + QString path = fi.path(); + QString name = fi.fileName(); + if (path == QLatin1String(".") && !fileName.startsWith(path)) + path.clear(); + else + path += QLatin1Char('/'); + + // The first filename we want to attempt to load is the filename as the callee specified. + // Thus, the first attempt we do must be with an empty prefix and empty suffix. + QStringList suffixes(QLatin1String("")), prefixes(QLatin1String("")); + if (pluginState != IsAPlugin) { + prefixes << QLatin1String("lib"); +#if defined(Q_OS_HPUX) + // according to + // http://docs.hp.com/en/B2355-90968/linkerdifferencesiapa.htm + + // 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. + + // But since we don't know if we are built on HPUX or HPUXi, + // we support both .sl (and .<version>) and .so suffixes but + // .so is preferred. +# if defined(__ia64) + if (!fullVersion.isEmpty()) { + suffixes << QString::fromLatin1(".so.%1").arg(fullVersion); + } else { + suffixes << QLatin1String(".so"); + } +# endif + if (!fullVersion.isEmpty()) { + suffixes << QString::fromLatin1(".sl.%1").arg(fullVersion); + suffixes << QString::fromLatin1(".%1").arg(fullVersion); + } else { + suffixes << QLatin1String(".sl"); + } +#elif defined(Q_OS_AIX) + suffixes << ".a"; +#else + if (!fullVersion.isEmpty()) { + suffixes << QString::fromLatin1(".so.%1").arg(fullVersion); + } else { + suffixes << QLatin1String(".so"); + } +#endif +# ifdef Q_OS_MAC + if (!fullVersion.isEmpty()) { + suffixes << QString::fromLatin1(".%1.bundle").arg(fullVersion); + suffixes << QString::fromLatin1(".%1.dylib").arg(fullVersion); + } else { + suffixes << QLatin1String(".bundle") << QLatin1String(".dylib"); + } +#endif + } + int dlFlags = 0; +#if defined(QT_HPUX_LD) + dlFlags = DYNAMIC_PATH | BIND_NONFATAL; + if (loadHints & QLibrary::ResolveAllSymbolsHint) { + dlFlags |= BIND_IMMEDIATE; + } else { + dlFlags |= BIND_DEFERRED; + } +#else + if (loadHints & QLibrary::ResolveAllSymbolsHint) { + dlFlags |= RTLD_NOW; + } else { + dlFlags |= RTLD_LAZY; + } + if (loadHints & QLibrary::ExportExternalSymbolsHint) { + dlFlags |= RTLD_GLOBAL; + } +#if !defined(Q_OS_CYGWIN) + else { +#if defined(Q_OS_MAC) + if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) +#endif + dlFlags |= RTLD_LOCAL; + } +#endif +#if defined(Q_OS_AIX) // Not sure if any other platform actually support this thing. + if (loadHints & QLibrary::LoadArchiveMemberHint) { + dlFlags |= RTLD_MEMBER; + } +#endif +#endif // QT_HPUX_LD + QString attempt; + bool retry = true; + for(int prefix = 0; retry && !pHnd && prefix < prefixes.size(); prefix++) { + for(int suffix = 0; retry && !pHnd && suffix < suffixes.size(); suffix++) { + if (!prefixes.at(prefix).isEmpty() && name.startsWith(prefixes.at(prefix))) + continue; + if (!suffixes.at(suffix).isEmpty() && name.endsWith(suffixes.at(suffix))) + continue; + if (loadHints & QLibrary::LoadArchiveMemberHint) { + attempt = name; + int lparen = attempt.indexOf(QLatin1Char('(')); + if (lparen == -1) + lparen = attempt.count(); + attempt = path + prefixes.at(prefix) + attempt.insert(lparen, suffixes.at(suffix)); + } else { + attempt = path + prefixes.at(prefix) + name + suffixes.at(suffix); + } +#if defined(QT_HPUX_LD) + pHnd = (void*)shl_load(QFile::encodeName(attempt), dlFlags, 0); +#else + pHnd = dlopen(QFile::encodeName(attempt), dlFlags); +#endif + if (!pHnd && fileName.startsWith(QLatin1Char('/')) && QFile::exists(attempt)) { + // We only want to continue if dlopen failed due to that the shared library did not exist. + // However, we are only able to apply this check for absolute filenames (since they are + // not influenced by the content of LD_LIBRARY_PATH, /etc/ld.so.cache, DT_RPATH etc...) + // This is all because dlerror is flawed and cannot tell us the reason why it failed. + retry = false; + } + } + } + +#ifdef Q_OS_MAC + if (!pHnd) { + if (CFBundleRef bundle = CFBundleGetBundleWithIdentifier(QCFString(fileName))) { + QCFType<CFURLRef> url = CFBundleCopyExecutableURL(bundle); + QCFString str = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle); + pHnd = dlopen(QFile::encodeName(str), dlFlags); + attempt = str; + } + } +# endif + if (!pHnd) { + errorString = QLibrary::tr("Cannot load library %1: %2").arg(fileName).arg(qdlerror()); + } + if (pHnd) { + qualifiedFileName = attempt; + errorString.clear(); + } + return (pHnd != 0); +} + +bool PatchedLibraryPrivate::unload_sys() +{ +#if defined(QT_HPUX_LD) + if (shl_unload((shl_t)pHnd)) { +#else + if (dlclose(pHnd)) { +#endif + errorString = QLibrary::tr("Cannot unload library %1: %2").arg(fileName).arg(qdlerror()); + return false; + } + errorString.clear(); + return true; +} + +void* PatchedLibraryPrivate::resolve_sys(const char* symbol) +{ +#if defined(QT_AOUT_UNDERSCORE) + // older a.out systems add an underscore in front of symbols + char* undrscr_symbol = new char[strlen(symbol)+2]; + undrscr_symbol[0] = '_'; + strcpy(undrscr_symbol+1, symbol); + void* address = dlsym(pHnd, undrscr_symbol); + delete [] undrscr_symbol; +#elif defined(QT_HPUX_LD) + void* address = 0; + if (shl_findsym((shl_t*)&pHnd, symbol, TYPE_UNDEFINED, &address) < 0) + address = 0; +#else + void* address = dlsym(pHnd, symbol); +#endif + if (!address) { + errorString = QLibrary::tr("Cannot resolve symbol \"%1\" in %2: %3").arg( + QString::fromAscii(symbol)).arg(fileName).arg(qdlerror()); + } else { + errorString.clear(); + } + return address; +} + +#endif // Q_OS_UNIX + + + + + +#ifdef Q_OS_WIN + +extern QString qt_error_string(int code); + +bool PatchedLibraryPrivate::load_sys() +{ +#ifdef Q_OS_WINCE + QString attempt = QFileInfo(fileName).absoluteFilePath(); +#else + QString attempt = fileName; +#endif + + //avoid 'Bad Image' message box + UINT oldmode = SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX); + QT_WA({ + pHnd = LoadLibraryW((TCHAR*)QDir::toNativeSeparators(attempt).utf16()); + } , { + pHnd = LoadLibraryA(QFile::encodeName(QDir::toNativeSeparators(attempt)).data()); + }); + + if (pluginState != IsAPlugin) { +#if defined(Q_OS_WINCE) + if (!pHnd && ::GetLastError() == ERROR_MOD_NOT_FOUND) { + QString secondAttempt = fileName; + QT_WA({ + pHnd = LoadLibraryW((TCHAR*)QDir::toNativeSeparators(secondAttempt).utf16()); + } , { + pHnd = LoadLibraryA(QFile::encodeName(QDir::toNativeSeparators(secondAttempt)).data()); + }); + } +#endif + if (!pHnd && ::GetLastError() == ERROR_MOD_NOT_FOUND) { + attempt += QLatin1String(".dll"); + QT_WA({ + pHnd = LoadLibraryW((TCHAR*)QDir::toNativeSeparators(attempt).utf16()); + } , { + pHnd = LoadLibraryA(QFile::encodeName(QDir::toNativeSeparators(attempt)).data()); + }); + } + } + + SetErrorMode(oldmode); + if (!pHnd) { + errorString = QLibrary::tr("Cannot load library %1: %2").arg(fileName).arg(qt_error_string()); + } + if (pHnd) { + errorString.clear(); + QT_WA({ + TCHAR buffer[MAX_PATH + 1]; + ::GetModuleFileNameW(pHnd, buffer, MAX_PATH); + attempt = QString::fromUtf16(reinterpret_cast<const ushort *>(&buffer)); + }, { + char buffer[MAX_PATH + 1]; + ::GetModuleFileNameA(pHnd, buffer, MAX_PATH); + attempt = QString::fromLocal8Bit(buffer); + }); + const QDir dir = QFileInfo(fileName).dir(); + const QString realfilename = attempt.mid(attempt.lastIndexOf(QLatin1Char('\\')) + 1); + if (dir.path() == QLatin1String(".")) + qualifiedFileName = realfilename; + else + qualifiedFileName = dir.filePath(realfilename); + } + return (pHnd != 0); +} + +bool PatchedLibraryPrivate::unload_sys() +{ + if (!FreeLibrary(pHnd)) { + errorString = QLibrary::tr("Cannot unload library %1: %2").arg(fileName).arg(qt_error_string()); + return false; + } + errorString.clear(); + return true; +} + +void* PatchedLibraryPrivate::resolve_sys(const char* symbol) +{ +#ifdef Q_OS_WINCE + void* address = (void*)GetProcAddress(pHnd, (const wchar_t*)QString::fromLatin1(symbol).utf16()); +#else + void* address = (void*)GetProcAddress(pHnd, symbol); +#endif + if (!address) { + errorString = QLibrary::tr("Cannot resolve symbol \"%1\" in %2: %3").arg( + QString::fromAscii(symbol)).arg(fileName).arg(qt_error_string()); + } else { + errorString.clear(); + } + return address; +} + +#endif // Q_OS_WIN + +QT_END_NAMESPACE diff --git a/src/libs/extensionsystem/pluginmanager.cpp b/src/libs/extensionsystem/pluginmanager.cpp index 7f523ad277f2f4105221361e8c4ffa17f8222603..28616526785571974cf5e18e1c9ec53e8ef240ae 100644 --- a/src/libs/extensionsystem/pluginmanager.cpp +++ b/src/libs/extensionsystem/pluginmanager.cpp @@ -39,7 +39,6 @@ #include "iplugin.h" #include <QtCore/QMetaProperty> -#include <QtCore/QPluginLoader> #include <QtCore/QDir> #include <QtCore/QTextStream> #include <QtCore/QWriteLocker> diff --git a/src/libs/extensionsystem/pluginspec.cpp b/src/libs/extensionsystem/pluginspec.cpp index 48aa88950e9a16c027b3a5055607c8a5a05138e2..55c830ec6f42b751aa5f87a06447096313b3a791 100644 --- a/src/libs/extensionsystem/pluginspec.cpp +++ b/src/libs/extensionsystem/pluginspec.cpp @@ -42,10 +42,23 @@ #include <QtCore/QFileInfo> #include <QtCore/QXmlStreamReader> #include <QtCore/QRegExp> -#include <QtCore/QPluginLoader> #include <QtCore/QCoreApplication> #include <QtDebug> +#define USE_UNPATCHED_QPLUGINLOADER 1 + +#if USE_UNPATCHED_QPLUGINLOADER + +#include <QtCore/QPluginLoader> +typedef QT_PREPEND_NAMESPACE(QPluginLoader) PluginLoader; + +#else + +#include "patchedpluginloader.cpp" +typedef PatchedPluginLoader PluginLoader; + +#endif + /*! \class ExtensionSystem::PluginDependency \brief Struct that contains the name and required compatible version number of a plugin's dependency. @@ -770,7 +783,7 @@ bool PluginSpecPrivate::loadLibrary() #endif - QPluginLoader loader(libName); + PluginLoader loader(libName); if (!loader.load()) { hasError = true; errorString = loader.errorString();