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 (!d->isPlugin())
181 return false;
182 did_load = true;
183 return d->loadPlugin();
184}
185
186/*!
187 Unloads the plugin and returns \c true if the plugin could be
188 unloaded; otherwise returns \c false.
189
190 This happens automatically on application termination, so you
191 shouldn't normally need to call this function.
192
193 If other instances of QPluginLoader are using the same plugin, the
194 call will fail, and unloading will only happen when every instance
195 has called unload().
196
197 Don't try to delete the root component. Instead rely on
198 that unload() will automatically delete it when needed.
199
200 \sa instance(), load()
201*/
202bool QPluginLoader::unload()
203{
204 if (did_load) {
205 did_load = false;
206 return d->unload();
207 }
208 if (d) // Ouch
209 d->errorString = tr("The plugin was not loaded.");
210 return false;
211}
212
213/*!
214 Returns \c true if the plugin is loaded; otherwise returns \c false.
215
216 \sa load()
217 */
218bool QPluginLoader::isLoaded() const
219{
220 return d && d->pHnd && d->instanceFactory.loadRelaxed();
221}
222
223#if defined(QT_SHARED)
224static QString locatePlugin(const QString& fileName)
225{
226 const bool isAbsolute = QDir::isAbsolutePath(fileName);
227 if (isAbsolute) {
228 QFileInfo fi(fileName);
229 if (fi.isFile()) {
230 return fi.canonicalFilePath();
231 }
232 }
233 std::array<QStringView, 2> prefixes = { QStringView(), QLibraryPrivate::prefix_sys() };
234 QStringList suffixes = QLibraryPrivate::suffixes_sys(QString());
235 suffixes.prepend(QString());
236
237 // Split up "subdir/filename"
238 const qsizetype slash = fileName.lastIndexOf(u'/');
239 const auto baseName = QStringView{fileName}.mid(slash + 1);
240 const auto basePath = isAbsolute ? QStringView() : QStringView{fileName}.left(slash + 1); // keep the '/'
241
242 QStringList paths;
243 if (isAbsolute) {
244 paths.append(fileName.left(slash)); // don't include the '/'
245 } else {
246 paths = QCoreApplication::libraryPaths();
247 }
248
249 for (const QString &path : std::as_const(paths)) {
250 for (QStringView prefix : prefixes) {
251 for (const QString &suffix : std::as_const(suffixes)) {
252#ifdef Q_OS_ANDROID
253 {
254 QString pluginPath = basePath + prefix + baseName + suffix;
255 const QString fn = path + "/lib"_L1 + pluginPath.replace(u'/', u'_');
256 qCDebug(qt_lcDebugPlugins) << "Trying..." << fn;
257 if (QFileInfo(fn).isFile())
258 return fn;
259 }
260#endif
261 const QString fn = path + u'/' + basePath + prefix + baseName + suffix;
262 qCDebug(qt_lcDebugPlugins) << "Trying..." << fn;
263 if (QFileInfo(fn).isFile())
264 return fn;
265 }
266 }
267 }
268 qCDebug(qt_lcDebugPlugins) << fileName << "not found";
269 return QString();
270}
271#endif
272
273/*!
274 \property QPluginLoader::fileName
275 \brief the file name of the plugin
276
277 We recommend omitting the file's suffix in the file name, since
278 QPluginLoader will automatically look for the file with the appropriate
279 suffix (see QLibrary::isLibrary()).
280
281 When loading the plugin, QPluginLoader searches
282 in all plugin locations specified by QCoreApplication::libraryPaths(),
283 unless the file name has an absolute path. After loading the plugin
284 successfully, fileName() returns the fully-qualified file name of
285 the plugin, including the full path to the plugin if one was given
286 in the constructor or passed to setFileName().
287
288 If the file name does not exist, it will not be set. This property
289 will then contain an empty string.
290
291 By default, this property contains an empty string.
292
293 \sa load()
294*/
295void QPluginLoader::setFileName(const QString &fileName)
296{
297#if defined(QT_SHARED)
298 QLibrary::LoadHints lh = defaultLoadHints;
299 if (d) {
300 lh = d->loadHints();
301 d->release();
302 d = nullptr;
303 did_load = false;
304 }
305
306 const QString fn = locatePlugin(fileName);
307
308 d = QLibraryPrivate::findOrCreate(fn, QString(), lh);
309 if (!fn.isEmpty())
310 d->updatePluginState();
311
312#else
313 qCWarning(qt_lcDebugPlugins, "Cannot load '%ls' into a statically linked Qt library.",
314 qUtf16Printable(fileName));
315#endif
316}
317
318QString QPluginLoader::fileName() const
319{
320 if (d)
321 return d->fileName;
322 return QString();
323}
324
325/*!
326 \since 4.2
327
328 Returns a text string with the description of the last error that occurred.
329*/
330QString QPluginLoader::errorString() const
331{
332 return (!d || d->errorString.isEmpty()) ? tr("Unknown error") : d->errorString;
333}
334
335/*! \since 4.4
336
337 \property QPluginLoader::loadHints
338 \brief Give the load() function some hints on how it should behave.
339
340 You can give hints on how the symbols in the plugin are
341 resolved. By default since Qt 5.7, QLibrary::PreventUnloadHint is set.
342
343 See the documentation of QLibrary::loadHints for a complete
344 description of how this property works.
345
346 \sa QLibrary::loadHints
347*/
348
349void QPluginLoader::setLoadHints(QLibrary::LoadHints loadHints)
350{
351 if (!d) {
352 d = QLibraryPrivate::findOrCreate({}, {}, loadHints); // ugly, but we need a d-ptr
353 d->errorString.clear();
354 } else {
355 d->setLoadHints(loadHints);
356 }
357}
358
359QLibrary::LoadHints QPluginLoader::loadHints() const
360{
361 // Not having a d-pointer means that the user hasn't called
362 // setLoadHints() / setFileName() yet. In setFileName() we will
363 // then force defaultLoadHints on loading, so we must return them
364 // from here as well.
365
366 return d ? d->loadHints() : defaultLoadHints;
367}
368
369#endif // QT_CONFIG(library)
370
373
374/*!
375 \relates QPluginLoader
376 \since 5.0
377
378 Registers the \a plugin specified with the plugin loader, and is used
379 by Q_IMPORT_PLUGIN().
380*/
381void Q_CORE_EXPORT qRegisterStaticPluginFunction(QStaticPlugin plugin)
382{
383 // using operator* because we shouldn't be registering plugins while
384 // unloading the application!
385 StaticPluginList &plugins = *staticPluginList;
386
387 // insert the plugin in the list, sorted by address, so we can detect
388 // duplicate registrations
389 auto comparator = [=](const QStaticPlugin &p1, const QStaticPlugin &p2) {
390 using Less = std::less<decltype(plugin.instance)>;
391 return Less{}(p1.instance, p2.instance);
392 };
393 auto pos = std::lower_bound(plugins.constBegin(), plugins.constEnd(), plugin, comparator);
394 if (pos == plugins.constEnd() || pos->instance != plugin.instance)
395 plugins.insert(pos, plugin);
396}
397
398/*!
399 Returns a list of static plugin instances (root components) held
400 by the plugin loader.
401 \sa staticPlugins()
402*/
403QObjectList QPluginLoader::staticInstances()
404{
405 QObjectList instances;
406 if (staticPluginList.exists()) {
407 const StaticPluginList &plugins = *staticPluginList;
408 instances.reserve(plugins.size());
409 for (QStaticPlugin plugin : plugins)
410 instances += plugin.instance();
411 }
412 return instances;
413}
414
415/*!
416 Returns a list of QStaticPlugins held by the plugin
417 loader. The function is similar to \l staticInstances()
418 with the addition that a QStaticPlugin also contains
419 meta data information.
420 \sa staticInstances()
421*/
422QList<QStaticPlugin> QPluginLoader::staticPlugins()
423{
424 StaticPluginList *plugins = staticPluginList();
425 if (plugins)
426 return *plugins;
427 return QList<QStaticPlugin>();
428}
429
430/*!
431 \class QStaticPlugin
432 \inmodule QtCore
433 \since 5.2
434
435 \brief QStaticPlugin is a struct containing a reference to a
436 static plugin instance together with its meta data.
437
438 \sa QPluginLoader, {How to Create Qt Plugins}
439*/
440
441/*!
442 \fn QStaticPlugin::QStaticPlugin(QtPluginInstanceFunction i, QtPluginMetaDataFunction m)
443 \internal
444*/
445
446/*!
447 \variable QStaticPlugin::instance
448
449 Holds the plugin instance.
450
451 \sa QPluginLoader::staticInstances()
452*/
453
454/*!
455 Returns a the meta data for the plugin as a QJsonObject.
456
457 \sa Q_PLUGIN_METADATA()
458*/
459QJsonObject QStaticPlugin::metaData() const
460{
461 QByteArrayView data(static_cast<const char *>(rawMetaData), rawMetaDataSize);
462 QPluginParsedMetaData parsed(data);
463 Q_ASSERT(!parsed.isError());
464 return parsed.toJson();
465}
466
467QT_END_NAMESPACE
468
469#include "moc_qpluginloader.cpp"
Combined button and popup list for selecting options.
Q_GLOBAL_STATIC(QReadWriteLock, g_updateMutex)
QList< QStaticPlugin > StaticPluginList