Make sure that the reference count for plugins is kept correctly

For systems where the Unix signature checker isn't enabled (read: Mac
and Windows), QPluginLoader must actually load the plugin to query for
the metadata. On Mac it even tried to keep the library loaded to avoid
unloading and reloading again when the user calls load().

However, that plus the fact that it was calling load_sys() (on Mac)
meant that it would bypass the reference count checking. And on all
Unix, if a library-that-wasnt-a-plugin was already loaded by way of a
QLibrary, it would have an effect of unloading said library.

So remove the "caching" of the library. We should instead invest time to
write a proper Mach-O binary decoder.

Task-number: QTBUG-29776
Change-Id: Iebbddabe60047aafedeced21f26a170f59656757
Reviewed-by: Liang Qi <liang.qi@digia.com>
Reviewed-by: Shawn Rutledge <shawn.rutledge@digia.com>
This commit is contained in:
Thiago Macieira 2013-02-21 13:58:57 -08:00 committed by The Qt Project
parent 790fe22c52
commit cafb02911a
5 changed files with 66 additions and 6 deletions

View File

@ -711,7 +711,7 @@ void QLibraryPrivate::updatePluginState()
hTempModule = ::LoadLibraryEx((wchar_t*)QDir::toNativeSeparators(fileName).utf16(), 0, dwFlags);
SetErrorMode(oldmode);
#else
temporary_load = load_sys();
temporary_load = load();
#endif
}
QtPluginQueryVerificationDataFunction getMetaData = NULL;
@ -736,11 +736,10 @@ void QLibraryPrivate::updatePluginState()
if (getMetaData)
ret = qt_get_metadata(getMetaData, this, &exceptionThrown);
if (temporary_load)
unload();
if (!exceptionThrown) {
if (!ret) {
if (temporary_load)
unload_sys();
} else {
if (ret) {
success = true;
}
retryLoadLibrary = false;

View File

@ -127,7 +127,9 @@ private:
bool unload_sys();
QFunctionPointer resolve_sys(const char *);
/// counts how many QLibrary or QPluginLoader are attached to us, plus 1 if it's loaded
QAtomicInt libraryRefCount;
/// counts how many times load() or loadPlugin() were called
QAtomicInt libraryUnloadCount;
enum { IsAPlugin, IsNotAPlugin, MightBeAPlugin } pluginState;

View File

@ -53,6 +53,12 @@
# define BORLAND_STDCALL
#endif
static int pluginVariable = 0xc0ffee;
LIB_EXPORT int *pointerAddress()
{
return &pluginVariable;
}
LIB_EXPORT int BORLAND_STDCALL version()
{
return 1;

View File

@ -46,3 +46,9 @@ QString ThePlugin::pluginName() const
{
return QLatin1String("Plugin ok");
}
static int pluginVariable = 0xc0ffee;
extern "C" Q_DECL_EXPORT int *pointerAddress()
{
return &pluginVariable;
}

View File

@ -99,7 +99,11 @@
static QString sys_qualifiedLibraryName(const QString &fileName)
{
return QFINDTESTDATA(QString("bin/%1%2%3").arg(PREFIX).arg(fileName).arg(SUFFIX));
QString libname = QFINDTESTDATA(QString("bin/%1%2%3").arg(PREFIX).arg(fileName).arg(SUFFIX));
QFileInfo fi(libname);
if (fi.exists())
return fi.canonicalFilePath();
return libname;
}
QT_FORWARD_DECLARE_CLASS(QPluginLoader)
@ -119,6 +123,8 @@ private slots:
#endif
void relativePath();
void reloadPlugin();
void preloadedPlugin_data();
void preloadedPlugin();
};
void tst_QPluginLoader::cleanup()
@ -354,6 +360,47 @@ void tst_QPluginLoader::reloadPlugin()
QVERIFY(loader.unload());
}
void tst_QPluginLoader::preloadedPlugin_data()
{
QTest::addColumn<bool>("doLoad");
QTest::addColumn<QString>("libname");
QTest::newRow("create-plugin") << false << sys_qualifiedLibraryName("theplugin");
QTest::newRow("load-plugin") << true << sys_qualifiedLibraryName("theplugin");
QTest::newRow("create-non-plugin") << false << sys_qualifiedLibraryName("tst_qpluginloaderlib");
QTest::newRow("load-non-plugin") << true << sys_qualifiedLibraryName("tst_qpluginloaderlib");
}
void tst_QPluginLoader::preloadedPlugin()
{
// check that using QPluginLoader does not interfere with QLibrary
QFETCH(QString, libname);
QLibrary lib(libname);
QVERIFY(lib.load());
typedef int *(*pf_t)();
pf_t pf = (pf_t)lib.resolve("pointerAddress");
QVERIFY(pf);
int *pluginVariable = pf();
QVERIFY(pluginVariable);
QCOMPARE(*pluginVariable, 0xc0ffee);
{
// load the plugin
QPluginLoader loader(libname);
QFETCH(bool, doLoad);
if (doLoad && loader.load()) {
// unload() returns false because QLibrary has it loaded
QVERIFY(!loader.unload());
}
}
QVERIFY(lib.isLoaded());
// if the library was unloaded behind our backs, the following will crash:
QCOMPARE(*pluginVariable, 0xc0ffee);
QVERIFY(lib.unload());
}
QTEST_MAIN(tst_QPluginLoader)