9#include <q20algorithm.h>
10#include <qbytearraymatcher.h>
15#include <qjsondocument.h>
17#include <qoperatingsystemversion.h>
18#include <qstringlist.h>
21# include <private/qcore_mac_p.h>
23#include <private/qcoreapplication_p.h>
24#include <private/qloggingregistry_p.h>
25#include <private/qsystemerror_p.h>
32#include <qtcore_tracepoints_p.h>
34#include <QtCore/q20map.h>
38using namespace Qt::StringLiterals;
48#if defined(Q_CC_MINGW)
49 && QT_CONFIG(debug_and_release)
54static constexpr bool QtBuildIsDebug =
false;
59Q_LOGGING_CATEGORY_WITH_ENV_OVERRIDE(qt_lcDebugPlugins,
"QT_DEBUG_PLUGINS",
"qt.core.plugin.loader")
60Q_STATIC_LOGGING_CATEGORY_WITH_ENV_OVERRIDE(lcDebugLibrary,
"QT_DEBUG_PLUGINS",
"qt.core.library")
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
167static QLibraryScanResult qt_find_pattern(
const char *s, qsizetype s_len, QString *errMsg)
170
171
172
173
174
175
176
177
179 return QElfParser::parse({s, s_len}, errMsg);
180#elif defined(Q_OF_MACH_O)
181 return QMachOParser::parse(s, s_len, errMsg);
182#elif defined(Q_OS_WIN) || defined(Q_OS_CYGWIN)
183 return QCoffPeParser::parse({s, s_len}, errMsg);
185# warning "Qt does not know how to efficiently parse your platform's binary format; using slow fall-back."
187 static constexpr auto matcher = [] {
191 constexpr auto &pattern =
"QTMETADATA !";
192 constexpr auto magic = std::string_view(QPluginMetaData::MagicString,
193 sizeof(QPluginMetaData::MagicString));
194 static_assert(pattern == magic);
195 return qMakeStaticByteArrayMatcher(pattern);
197 qsizetype i = matcher.indexIn({s, s_len});
199 *errMsg = QLibrary::tr(
"'%1' is not a Qt plugin").arg(*errMsg);
200 return QLibraryScanResult{};
202 i +=
sizeof(QPluginMetaData::MagicString);
203 return { i, s_len - i };
207
208
209
210
211
212
213
214
215
219 if (!file.open(QIODevice::ReadOnly)) {
221 lib->errorString = file.errorString();
222 qCWarning(qt_lcDebugPlugins,
"%ls: cannot open: %ls", qUtf16Printable(library),
223 qUtf16Printable(file.errorString()));
229 constexpr qint64 MaxMemoryMapSize =
230 Q_INT64_C(1) << (
sizeof(qsizetype) > 4 ? 40 : 29);
232 qsizetype fdlen = qMin(file.size(), MaxMemoryMapSize);
233 const char *filedata =
reinterpret_cast<
char *>(file.map(0, fdlen));
236 if (filedata ==
nullptr) {
239 qCWarning(qt_lcDebugPlugins,
"%ls: failed to map to memory: %ls",
240 qUtf16Printable(library), qUtf16Printable(file.errorString()));
245 if (filedata ==
nullptr) {
249 data = file.read(64 * 1024 * 1024);
250 filedata = data.constData();
255 QString errMsg = library;
258#if defined(Q_OF_MACH_O)
262 if (!lib->metaData.parse(QByteArrayView(filedata + r.pos, r.length))) {
263 errMsg = lib->metaData.errorString();
264 qCDebug(qt_lcDebugPlugins,
"Found invalid metadata in lib %ls: %ls",
265 qUtf16Printable(library), qUtf16Printable(errMsg));
267 qCDebug(qt_lcDebugPlugins,
"Found metadata in lib %ls, metadata=\n%s\n",
268 qUtf16Printable(library),
269 QJsonDocument(lib->metaData.toJson()).toJson().constData());
273 qCDebug(qt_lcDebugPlugins,
"Failed to find metadata in lib %ls: %ls",
274 qUtf16Printable(library), qUtf16Printable(errMsg));
277 lib->errorString = QLibrary::tr(
"Failed to extract plugin meta data from '%1': %2")
278 .arg(library, errMsg);
284#ifdef __COVERAGESCANNER__
286
287
288
289
290
291
292
293
294
295
296
298 int ret = __coveragescanner_register_library(libPrivate->fileName.toLocal8Bit());
301 qDebug(
"coverage data for %ls registered",
302 qUtf16Printable(libPrivate->fileName));
304 qWarning(
"could not register %ls: error %d; coverage data may be incomplete",
305 qUtf16Printable(libPrivate->fileName),
310 Q_UNUSED(libPrivate);
327 typedef std::map<QString, QLibraryPrivate *> LibraryMap;
328 LibraryMap libraryMap;
331Q_CONSTINIT
static QBasicMutex qt_library_mutex;
332Q_CONSTINIT
static QLibraryStore *qt_library_data =
nullptr;
337 qt_library_data =
nullptr;
347 for (
auto &[_, lib] : data->libraryMap) {
348 if (lib->libraryRefCount.loadRelaxed() == 1) {
349 if (lib->libraryUnloadCount.loadRelaxed() > 0) {
350 Q_ASSERT(lib->pHnd.loadRelaxed());
351 lib->libraryUnloadCount.storeRelaxed(1);
352#if defined(Q_OS_DARWIN)
356 lib->unload(QLibraryPrivate::NoUnloadSys);
361 delete std::exchange(lib,
nullptr);
366 if (lcDebugLibrary().isDebugEnabled()) {
367 for (
auto &[_, lib] : data->libraryMap) {
369 qCDebug(lcDebugLibrary)
370 <<
"On QtCore unload," << lib->fileName <<
"was leaked, with"
371 << lib->libraryRefCount.loadRelaxed() <<
"users";
382Q_DESTRUCTOR_FUNCTION(qlibraryCleanup)
387 if (Q_UNLIKELY(!qt_library_data_once && !qt_library_data)) {
389 qt_library_data =
new QLibraryStore;
392 return qt_library_data;
396 QLibrary::LoadHints loadHints)
398 auto lazyNewLib = [&] {
400 result->libraryRefCount.ref();
404 if (fileName.isEmpty())
410 if (Q_UNLIKELY(!data)) {
415 QString mapName = version.isEmpty() ? fileName : fileName + u'\0' + version;
420 lib->libraryRefCount.ref();
421 lib->mergeLoadHints(loadHints);
434 if (lib->libraryRefCount.deref()) {
440 Q_ASSERT(lib->libraryUnloadCount.loadRelaxed() == 0);
442 if (Q_LIKELY(data) && !lib->fileName.isEmpty()) {
444 const auto n = erase_if(data->libraryMap, [lib](
const auto &e) {
445 return e.second == lib;
447 Q_ASSERT_X(n,
"~QLibrary",
"Did not find this library in the library map");
453QLibraryPrivate::QLibraryPrivate(
const QString &canonicalFileName,
const QString &version, QLibrary::LoadHints loadHints)
454 : fileName(canonicalFileName), fullVersion(version), pluginState(MightBeAPlugin)
456 loadHintsInt.storeRelaxed(loadHints.toInt());
457 if (canonicalFileName.isEmpty())
458 errorString = QLibrary::tr(
"The shared library was not found.");
462 QLibrary::LoadHints loadHints)
464 return QLibraryStore::findOrCreate(fileName, version, loadHints);
474 if (pHnd.loadRelaxed())
477 loadHintsInt.fetchAndOrRelaxed(lh.toInt());
482 if (!pHnd.loadRelaxed())
484 return resolve_sys(symbol);
491 if (fileName.isEmpty()) {
492 loadHintsInt.storeRelaxed(lh.toInt());
504 QObject *obj = [&](){
QMutexLocker locker(&mutex);
return inst.data(); }();
510 QtPluginInstanceFunction factory = instanceFactory.loadAcquire();
512 factory = loadPlugin();
530 if (pHnd.loadRelaxed()) {
531 libraryUnloadCount.ref();
534 if (fileName.isEmpty())
537 Q_TRACE(QLibraryPrivate_load_entry, fileName);
539 bool ret = load_sys();
540 qCDebug(lcDebugLibrary)
542 << (ret ?
"loaded library" : qUtf8Printable(u"cannot load: " + errorString));
546 libraryUnloadCount.ref();
547 libraryRefCount.ref();
551 Q_TRACE(QLibraryPrivate_load_exit, ret);
558 if (!pHnd.loadRelaxed())
560 if (libraryUnloadCount.loadRelaxed() > 0 && !libraryUnloadCount.deref()) {
564 qCDebug(lcDebugLibrary) << fileName <<
"unloaded library"
565 << (flag == NoUnloadSys ?
"(faked)" :
"");
568 libraryRefCount.deref();
569 pHnd.storeRelaxed(
nullptr);
570 instanceFactory.storeRelaxed(
nullptr);
585 if (
auto ptr = instanceFactory.loadAcquire()) {
586 libraryUnloadCount.ref();
589 if (pluginState == IsNotAPlugin)
592 auto ptr =
reinterpret_cast<QtPluginInstanceFunction>(resolve(
"qt_plugin_instance"));
593 instanceFactory.storeRelease(ptr);
596 errorString = QLibrary::tr(
"Could not resolve 'qt_plugin_instance' function");
598 qCDebug(qt_lcDebugPlugins) <<
"QLibraryPrivate::loadPlugin failed on" << fileName <<
":" << errorString;
599 pluginState = IsNotAPlugin;
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618bool QLibrary::isLibrary(
const QString &fileName)
620#if defined(Q_OS_WIN) || defined(Q_OS_CYGWIN)
621 return fileName.endsWith(
".dll"_L1, Qt::CaseInsensitive);
623# if defined(Q_OS_DARWIN)
625 if (fileName.endsWith(
".dylib"_L1))
628 QString completeSuffix = QFileInfo(fileName).completeSuffix();
629 if (completeSuffix.isEmpty())
633 const QLatin1StringView candidates[] = {
634# if defined(Q_OS_HPUX)
636
637
638
639
644# elif defined(Q_OS_AIX)
647# elif defined(Q_OS_DARWIN)
650# elif defined(Q_OS_UNIX)
655 auto isValidSuffix = [&candidates](QStringView s) {
656 return std::find(std::begin(candidates), std::end(candidates), s) != std::end(candidates);
666 auto suffixes = qTokenize(completeSuffix, u'.');
667 auto it = suffixes.begin();
668 const auto end = suffixes.end();
670 auto isNumeric = [](QStringView s) {
bool ok; (
void)s.toInt(&ok);
return ok; };
673 if (isValidSuffix(*it++))
674 return q20::ranges::all_of(it, end, isNumeric);
682 auto error = [=](QString &&explanation) {
683 *errMsg = QLibrary::tr(
"'%1' is not a Qt plugin (%2)").arg(priv->fileName, std::move(explanation));
687 QPluginMetaData metaData;
688 QFunctionPointer pfn = priv->resolve(
"qt_plugin_query_metadata_v2");
690 metaData =
reinterpret_cast<QPluginMetaData (*)()>(pfn)();
691#if QT_VERSION <= QT_VERSION_CHECK(7
, 0
, 0
)
692 }
else if ((pfn = priv->resolve(
"qt_plugin_query_metadata"))) {
693 metaData =
reinterpret_cast<QPluginMetaData (*)()>(pfn)();
694 if (metaData.size <
sizeof(QPluginMetaData::MagicHeader))
695 return error(QLibrary::tr(
"metadata too small"));
698 auto data =
reinterpret_cast<
const char *>(metaData.data);
699 data +=
sizeof(QPluginMetaData::MagicString);
700 metaData.data = data;
701 metaData.size -=
sizeof(QPluginMetaData::MagicString);
704 return error(QLibrary::tr(
"entrypoint to query the plugin meta data not found"));
707 if (metaData.size <
sizeof(QPluginMetaData::Header))
708 return error(QLibrary::tr(
"metadata too small"));
710 if (priv->metaData.parse(metaData))
712 *errMsg = priv->metaData.errorString();
718 if (pluginState == MightBeAPlugin)
721 return pluginState == IsAPlugin;
728 if (pluginState != MightBeAPlugin)
731 bool success =
false;
733#if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN)
734 if (fileName.endsWith(
".debug"_L1)) {
741 errorString = QLibrary::tr(
"The shared library was not found.");
742 pluginState = IsNotAPlugin;
747 if (!pHnd.loadRelaxed()) {
750#if defined(Q_OF_MACH_O)
751 if (result.length && result.isEncrypted) {
755 qCDebug(qt_lcDebugPlugins,
"Library is encrypted. Doing prospective load before parsing metadata");
759 success = qt_get_metadata(
this, &errorString);
763 success = result.length != 0;
768 success = qt_get_metadata(
this, &errorString);
772 if (errorString.isEmpty()) {
773 if (fileName.isEmpty())
774 errorString = QLibrary::tr(
"The shared library was not found.");
776 errorString = QLibrary::tr(
"The file '%1' is not a valid Qt plugin.").arg(fileName);
778 pluginState = IsNotAPlugin;
782 pluginState = IsNotAPlugin;
784 uint qt_version = uint(metaData.value(QtPluginMetaDataKeys::QtVersion).toInteger());
785 bool debug = metaData.value(QtPluginMetaDataKeys::IsDebug).toBool();
786 if ((qt_version & 0x00ff00) > (QT_VERSION & 0x00ff00) || (qt_version & 0xff0000) != (QT_VERSION & 0xff0000)) {
787 qCDebug(qt_lcDebugPlugins,
"In %s:\n"
788 " Plugin uses incompatible Qt library (%d.%d.%d) [%s]",
789 QFile::encodeName(fileName).constData(),
790 (qt_version&0xff0000) >> 16, (qt_version&0xff00) >> 8, qt_version&0xff,
791 debug ?
"debug" :
"release");
792 errorString = QLibrary::tr(
"The plugin '%1' uses incompatible Qt library. (%2.%3.%4) [%5]")
794 QString::number((qt_version & 0xff0000) >> 16),
795 QString::number((qt_version & 0xff00) >> 8),
796 QString::number(qt_version & 0xff),
797 debug ?
"debug"_L1 :
"release"_L1);
800 errorString = QLibrary::tr(
"The plugin '%1' uses incompatible Qt library."
801 " (Cannot mix debug and release libraries.)").arg(fileName);
803 pluginState = IsAPlugin;
808
809
810
811
812
813
814
815
816
821 if (d.tag() == Loaded)
822 return d->pHnd.loadRelaxed();
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847bool QLibrary::unload()
849 if (d.tag() == Loaded) {
857
858
859
860
861
862
863
864
865bool QLibrary::isLoaded()
const
867 return d.tag() == Loaded;
872
873
874QLibrary::QLibrary(QObject *parent) : QObject(parent)
880
881
882
883
884
885
886
887
888QLibrary::QLibrary(
const QString &fileName, QObject *parent) : QObject(parent)
890 setFileName(fileName);
894
895
896
897
898
899
900
901
902
903QLibrary::QLibrary(
const QString &fileName,
int verNum, QObject *parent) : QObject(parent)
905 setFileNameAndVersion(fileName, verNum);
909
910
911
912
913
914
915
916
917
918QLibrary::QLibrary(
const QString &fileName,
const QString &version, QObject *parent)
921 setFileNameAndVersion(fileName, version);
925
926
927
928
929
930
931
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
959void QLibrary::setFileName(
const QString &fileName)
961 setFileNameAndVersion(fileName, QString());
964QString QLibrary::fileName()
const
967 QMutexLocker locker(&d->mutex);
968 return d->qualifiedFileName.isEmpty() ? d->fileName : d->qualifiedFileName;
974
975
976
977
978
979
980
981
982void QLibrary::setFileNameAndVersion(
const QString &fileName,
int verNum)
984 setFileNameAndVersion(fileName, verNum >= 0 ? QString::number(verNum) : QString());
988
989
990
991
992
993
994
995
996void QLibrary::setFileNameAndVersion(
const QString &fileName,
const QString &version)
998 QLibrary::LoadHints lh;
1000 lh = d->loadHints();
1003 QLibraryPrivate *dd = QLibraryPrivate::findOrCreate(fileName, version, lh);
1004 d = QTaggedPointer(dd, NotLoaded);
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027QFunctionPointer QLibrary::resolve(
const char *symbol)
1029 if (!isLoaded() && !load())
1031 return d->resolve(symbol);
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047QFunctionPointer QLibrary::resolve(
const QString &fileName,
const char *symbol)
1049 QLibrary library(fileName);
1050 return library.resolve(symbol);
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067QFunctionPointer QLibrary::resolve(
const QString &fileName,
int verNum,
const char *symbol)
1069 QLibrary library(fileName, verNum);
1070 return library.resolve(symbol);
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088QFunctionPointer QLibrary::resolve(
const QString &fileName,
const QString &version,
const char *symbol)
1090 QLibrary library(fileName, version);
1091 return library.resolve(symbol);
1095
1096
1097
1098
1099
1100QString QLibrary::errorString()
const
1104 QMutexLocker locker(&d->mutex);
1105 str = d->errorString;
1107 return str.isEmpty() ? tr(
"Unknown error") : str;
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149void QLibrary::setLoadHints(LoadHints hints)
1152 d = QLibraryPrivate::findOrCreate(QString());
1153 d->errorString.clear();
1155 d->setLoadHints(hints);
1158QLibrary::LoadHints QLibrary::loadHints()
const
1160 return d ? d->loadHints() : QLibrary::LoadHints();
1166 static int debug_env = QT_PREPEND_NAMESPACE(qEnvironmentVariableIntValue)(
"QT_DEBUG_PLUGINS");
1167 return debug_env != 0;
1172#include "moc_qlibrary.cpp"
bool unload(UnloadFlag flag=UnloadSys)
QtPluginInstanceFunction loadPlugin()
void setLoadHints(QLibrary::LoadHints lh)
QFunctionPointer resolve(const char *)
QObject * pluginInstance()
static void releaseLibrary(QLibraryPrivate *lib)
static QLibraryPrivate * findOrCreate(const QString &fileName, const QString &version, QLibrary::LoadHints loadHints)
Q_TRACE_POINT(qtcore, QLibraryPrivate_load_exit, bool success)
static void installCoverageTool(QLibraryPrivate *libPrivate)
static constexpr bool PluginMustMatchQtDebug
bool qt_debug_component()
Q_TRACE_POINT(qtcore, QLibraryPrivate_load_entry, const QString &fileName)
static bool qt_get_metadata(QLibraryPrivate *priv, QString *errMsg)
static Q_CONSTINIT bool qt_library_data_once
static QLibraryScanResult findPatternUnloaded(const QString &library, QLibraryPrivate *lib)
static constexpr bool QtBuildIsDebug
static void qlibraryCleanup()