Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qpluginloader.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2018 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4// Qt-Security score:critical reason:execute-external-code
5
7
9#include "qdebug.h"
10#include "qdir.h"
12#include "qfileinfo.h"
13#include "qjsondocument.h"
14
15#if QT_CONFIG(library)
16# include "qlibrary_p.h"
17#endif
18
20
21using namespace Qt::StringLiterals;
22
23#if QT_CONFIG(library)
24
25/*!
26 \class QPluginLoader
27 \inmodule QtCore
28 \reentrant
29 \brief The QPluginLoader class loads a plugin at run-time.
30
31
32 \ingroup plugins
33
34 QPluginLoader provides access to a \l{How to Create Qt
35 Plugins}{Qt plugin}. A Qt plugin is stored in a shared library (a
36 DLL) and offers these benefits over shared libraries accessed
37 using QLibrary:
38
39 \list
40 \li QPluginLoader checks that a plugin is linked against the same
41 version of Qt as the application.
42 \li QPluginLoader provides direct access to a root component object
43 (instance()), instead of forcing you to resolve a C function manually.
44 \endlist
45
46 An instance of a QPluginLoader object operates on a single shared
47 library file, which we call a plugin. It provides access to the
48 functionality in the plugin in a platform-independent way. To
49 specify which plugin to load, either pass a file name in
50 the constructor or set it with setFileName().
51
52 The most important functions are load() to dynamically load the
53 plugin file, isLoaded() to check whether loading was successful,
54 and instance() to access the root component in the plugin. The
55 instance() function implicitly tries to load the plugin if it has
56 not been loaded yet. Multiple instances of QPluginLoader can be
57 used to access the same physical plugin.
58
59 Once loaded, plugins remain in memory until all instances of
60 QPluginLoader has been unloaded, or until the application
61 terminates. You can attempt to unload a plugin using unload(),
62 but if other instances of QPluginLoader are using the same
63 library, the call will fail, and unloading will only happen when
64 every instance has called unload(). Right before the unloading
65 happens, the root component will also be deleted.
66
67 See \l{How to Create Qt Plugins} for more information about
68 how to make your application extensible through plugins.
69
70 Note that the QPluginLoader cannot be used if your application is
71 statically linked against Qt. In this case, you will also have to
72 link to plugins statically. You can use QLibrary if you need to
73 load dynamic libraries in a statically linked application.
74
75 \sa QLibrary
76*/
77
78static constexpr QLibrary::LoadHints defaultLoadHints = QLibrary::PreventUnloadHint;
79
80/*!
81 Constructs a plugin loader with the given \a parent.
82*/
83QPluginLoader::QPluginLoader(QObject *parent)
84 : QObject(parent), d(nullptr), did_load(false)
85{
86}
87
88/*!
89 Constructs a plugin loader with the given \a parent that will
90 load the plugin specified by \a fileName.
91
92 To be loadable, the file's suffix must be a valid suffix for a
93 loadable library in accordance with the platform, e.g. \c .so on
94 Unix, - \c .dylib on \macos and iOS, and \c .dll on Windows. The suffix
95 can be verified with QLibrary::isLibrary().
96
97 \sa setFileName()
98*/
99QPluginLoader::QPluginLoader(const QString &fileName, QObject *parent)
100 : QObject(parent), d(nullptr), did_load(false)
101{
102 setFileName(fileName);
103 setLoadHints(defaultLoadHints);
104}
105
106/*!
107 Destroys the QPluginLoader object.
108
109 Unless unload() was called explicitly, the plugin stays in memory
110 until the application terminates.
111
112 \sa isLoaded(), unload()
113*/
114QPluginLoader::~QPluginLoader()
115{
116 if (d)
117 d->release();
118}
119
120/*!
121 Returns the root component object of the plugin. The plugin is
122 loaded if necessary. The function returns \nullptr if the plugin could
123 not be loaded or if the root component object could not be
124 instantiated.
125
126 If the root component object was destroyed, calling this function
127 creates a new instance.
128
129 The root component, returned by this function, is not deleted when
130 the QPluginLoader is destroyed. If you want to ensure that the root
131 component is deleted, you should call unload() as soon you don't
132 need to access the core component anymore. When the library is
133 finally unloaded, the root component will automatically be deleted.
134
135 The component object is a QObject. Use qobject_cast() to access
136 interfaces you are interested in.
137
138 \sa load()
139*/
140QObject *QPluginLoader::instance()
141{
142 if (!isLoaded() && !load())
143 return nullptr;
144 return d->pluginInstance();
145}
146
147/*!
148 Returns the meta data for this plugin. The meta data is data specified
149 in a json format using the Q_PLUGIN_METADATA() macro when compiling
150 the plugin.
151
152 The meta data can be queried in a fast and inexpensive way without
153 actually loading the plugin. This makes it possible to e.g. store
154 capabilities of the plugin in there, and make the decision whether to
155 load the plugin dependent on this meta data.
156 */
157QJsonObject QPluginLoader::metaData() const
158{
159 if (!d)
160 return QJsonObject();
161 return d->metaData.toJson();
162}
163
164/*!
165 Loads the plugin and returns \c true if the plugin was loaded
166 successfully; otherwise returns \c false. Since instance() always
167 calls this function before resolving any symbols it is not
168 necessary to call it explicitly. In some situations you might want
169 the plugin loaded in advance, in which case you would use this
170 function.
171
172 \sa unload()
173*/
174bool QPluginLoader::load()
175{
176 if (!d || d->fileName.isEmpty())
177 return false;
178 if (did_load)
179 return d->pHnd && d->instanceFactory.loadAcquire();
180#if !defined(Q_OS_WASM)
181 // On wasm, plugins are fetched over HTTP by emscripten_dlopen() and may
182 // not exist on the file system. Skip the isPlugin() check which probes the
183 // filesystem; the metadata is read from the loaded module instead.
184 if (!d->isPlugin())
185 return false;
186#endif
187 did_load = true;
188 return d->loadPlugin();
189}
190
191/*!
192 Unloads the plugin and returns \c true if the plugin could be
193 unloaded; otherwise returns \c false.
194
195 This happens automatically on application termination, so you
196 shouldn't normally need to call this function.
197
198 If other instances of QPluginLoader are using the same plugin, the
199 call will fail, and unloading will only happen when every instance
200 has called unload().
201
202 Don't try to delete the root component. Instead rely on
203 that unload() will automatically delete it when needed.
204
205 \sa instance(), load()
206*/
207bool QPluginLoader::unload()
208{
209 if (did_load) {
210 did_load = false;
211 return d->unload();
212 }
213 if (d) // Ouch
214 d->errorString = tr("The plugin was not loaded.");
215 return false;
216}
217
218/*!
219 Returns \c true if the plugin is loaded; otherwise returns \c false.
220
221 \sa load()
222 */
223bool QPluginLoader::isLoaded() const
224{
225 return d && d->pHnd && d->instanceFactory.loadRelaxed();
226}
227
228#if defined(QT_SHARED)
229# if defined(Q_OS_WASM)
230// On wasm, plugins are fetched over HTTP by emscripten_dlopen() and may not exist
231// on the file system, so there is nothing to locate: pass the file name through
232// and let the loader resolve it.
233static QString locatePlugin(const QString &fileName)
234{
235 return fileName;
236}
237# else
238static QString locatePlugin(const QString& fileName)
239{
240 const bool isAbsolute = QDir::isAbsolutePath(fileName);
241 if (isAbsolute) {
242 QFileInfo fi(fileName);
243 if (fi.isFile()) {
244 return fi.canonicalFilePath();
245 }
246 }
247 std::array<QStringView, 2> prefixes = { QStringView(), QLibraryPrivate::prefix_sys() };
248 QStringList suffixes = QLibraryPrivate::suffixes_sys(QString());
249 suffixes.prepend(QString());
250
251 // Split up "subdir/filename"
252 const qsizetype slash = fileName.lastIndexOf(u'/');
253 const auto baseName = QStringView{fileName}.mid(slash + 1);
254 const auto basePath = isAbsolute ? QStringView() : QStringView{fileName}.left(slash + 1); // keep the '/'
255
256 QStringList paths;
257 if (isAbsolute) {
258 paths.append(fileName.left(slash)); // don't include the '/'
259 } else {
260 paths = QCoreApplication::libraryPaths();
261 }
262
263 for (const QString &path : std::as_const(paths)) {
264 for (QStringView prefix : prefixes) {
265 for (const QString &suffix : std::as_const(suffixes)) {
266#ifdef Q_OS_ANDROID
267 {
268 QString pluginPath = basePath + prefix + baseName + suffix;
269 const QString fn = path + "/lib"_L1 + pluginPath.replace(u'/', u'_');
270 qCDebug(qt_lcDebugPlugins) << "Trying..." << fn;
271 if (QFileInfo(fn).isFile())
272 return fn;
273 }
274#endif
275 const QString fn = path + u'/' + basePath + prefix + baseName + suffix;
276 qCDebug(qt_lcDebugPlugins) << "Trying..." << fn;
277 if (QFileInfo(fn).isFile())
278 return fn;
279 }
280 }
281 }
282 qCDebug(qt_lcDebugPlugins) << fileName << "not found";
283 return QString();
284}
285# endif // Q_OS_WASM
286#endif // QT_SHARED
287
288/*!
289 \property QPluginLoader::fileName
290 \brief the file name of the plugin
291
292 We recommend omitting the file's suffix in the file name, since
293 QPluginLoader will automatically look for the file with the appropriate
294 suffix (see QLibrary::isLibrary()).
295
296 When loading the plugin, QPluginLoader searches
297 in all plugin locations specified by QCoreApplication::libraryPaths(),
298 unless the file name has an absolute path. After loading the plugin
299 successfully, fileName() returns the fully-qualified file name of
300 the plugin, including the full path to the plugin if one was given
301 in the constructor or passed to setFileName().
302
303 If the file name does not exist, it will not be set. This property
304 will then contain an empty string.
305
306 By default, this property contains an empty string.
307
308 \sa load()
309*/
310void QPluginLoader::setFileName(const QString &fileName)
311{
312#if defined(QT_SHARED)
313 QLibrary::LoadHints lh = defaultLoadHints;
314 if (d) {
315 lh = d->loadHints();
316 d->release();
317 d = nullptr;
318 did_load = false;
319 }
320
321 const QString fn = locatePlugin(fileName);
322
323 d = QLibraryPrivate::findOrCreate(fn, QString(), lh);
324 if (!fn.isEmpty())
325 d->updatePluginState();
326
327#else
328 qCWarning(qt_lcDebugPlugins, "Cannot load '%ls' into a statically linked Qt library.",
329 qUtf16Printable(fileName));
330#endif
331}
332
333QString QPluginLoader::fileName() const
334{
335 if (d)
336 return d->fileName;
337 return QString();
338}
339
340/*!
341 \since 4.2
342
343 Returns a text string with the description of the last error that occurred.
344*/
345QString QPluginLoader::errorString() const
346{
347 return (!d || d->errorString.isEmpty()) ? tr("Unknown error") : d->errorString;
348}
349
350/*! \since 4.4
351
352 \property QPluginLoader::loadHints
353 \brief Give the load() function some hints on how it should behave.
354
355 You can give hints on how the symbols in the plugin are
356 resolved. By default since Qt 5.7, QLibrary::PreventUnloadHint is set.
357
358 See the documentation of QLibrary::loadHints for a complete
359 description of how this property works.
360
361 \sa QLibrary::loadHints
362*/
363
364void QPluginLoader::setLoadHints(QLibrary::LoadHints loadHints)
365{
366 if (!d) {
367 d = QLibraryPrivate::findOrCreate({}, {}, loadHints); // ugly, but we need a d-ptr
368 d->errorString.clear();
369 } else {
370 d->setLoadHints(loadHints);
371 }
372}
373
374QLibrary::LoadHints QPluginLoader::loadHints() const
375{
376 // Not having a d-pointer means that the user hasn't called
377 // setLoadHints() / setFileName() yet. In setFileName() we will
378 // then force defaultLoadHints on loading, so we must return them
379 // from here as well.
380
381 return d ? d->loadHints() : defaultLoadHints;
382}
383
384#endif // QT_CONFIG(library)
385
388
389/*!
390 \relates QPluginLoader
391 \since 5.0
392
393 Registers the \a plugin specified with the plugin loader, and is used
394 by Q_IMPORT_PLUGIN().
395*/
396void Q_CORE_EXPORT qRegisterStaticPluginFunction(QStaticPlugin plugin)
397{
398 // using operator* because we shouldn't be registering plugins while
399 // unloading the application!
400 StaticPluginList &plugins = *staticPluginList;
401
402 // insert the plugin in the list, sorted by address, so we can detect
403 // duplicate registrations
404 auto comparator = [=](const QStaticPlugin &p1, const QStaticPlugin &p2) {
405 using Less = std::less<decltype(plugin.instance)>;
406 return Less{}(p1.instance, p2.instance);
407 };
408 auto pos = std::lower_bound(plugins.constBegin(), plugins.constEnd(), plugin, comparator);
409 if (pos == plugins.constEnd() || pos->instance != plugin.instance)
410 plugins.insert(pos, plugin);
411}
412
413/*!
414 Returns a list of static plugin instances (root components) held
415 by the plugin loader.
416 \sa staticPlugins()
417*/
418QObjectList QPluginLoader::staticInstances()
419{
420 QObjectList instances;
421 if (staticPluginList.exists()) {
422 const StaticPluginList &plugins = *staticPluginList;
423 instances.reserve(plugins.size());
424 for (QStaticPlugin plugin : plugins)
425 instances += plugin.instance();
426 }
427 return instances;
428}
429
430/*!
431 Returns a list of QStaticPlugins held by the plugin
432 loader. The function is similar to \l staticInstances()
433 with the addition that a QStaticPlugin also contains
434 meta data information.
435 \sa staticInstances()
436*/
437QList<QStaticPlugin> QPluginLoader::staticPlugins()
438{
439 StaticPluginList *plugins = staticPluginList();
440 if (plugins)
441 return *plugins;
442 return QList<QStaticPlugin>();
443}
444
445/*!
446 \class QStaticPlugin
447 \inmodule QtCore
448 \since 5.2
449
450 \brief QStaticPlugin is a struct containing a reference to a
451 static plugin instance together with its meta data.
452
453 \sa QPluginLoader, {How to Create Qt Plugins}
454*/
455
456/*!
457 \fn QStaticPlugin::QStaticPlugin(QtPluginInstanceFunction i, QtPluginMetaDataFunction m)
458 \internal
459*/
460
461/*!
462 \variable QStaticPlugin::instance
463
464 Holds the plugin instance.
465
466 \sa QPluginLoader::staticInstances()
467*/
468
469/*!
470 Returns a the meta data for the plugin as a QJsonObject.
471
472 \sa Q_PLUGIN_METADATA()
473*/
474QJsonObject QStaticPlugin::metaData() const
475{
476 QByteArrayView data(static_cast<const char *>(rawMetaData), rawMetaDataSize);
477 QPluginParsedMetaData parsed(data);
478 Q_ASSERT(!parsed.isError());
479 return parsed.toJson();
480}
481
482QT_END_NAMESPACE
483
484#include "moc_qpluginloader.cpp"
Combined button and popup list for selecting options.
Q_GLOBAL_STATIC(QReadWriteLock, g_updateMutex)
QList< QStaticPlugin > StaticPluginList