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
qlibrary_unix.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2020 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
6#include "qplatformdefs.h"
7
8#include <qcoreapplication.h>
9#include <qfile.h>
10#include "qlibrary_p.h"
11#include <private/qfilesystementry_p.h>
12#include <private/qsimd_p.h>
13
14#include <dlfcn.h>
15
16#ifdef Q_OS_DARWIN
17# include <private/qcore_mac_p.h>
18
19// Apple's dyld *does* support RTLD_NODELETE and the library remains loaded in
20// memory after the dlclose() call, but their Objective C crashes when running
21// code from unloaded-but-still-loaded plugins.
22# undef RTLD_NODELETE
23#endif
24
25#ifdef Q_OS_ANDROID
26#include <private/qjnihelpers_p.h>
27#include <QtCore/qjnienvironment.h>
28#endif
29
30QT_BEGIN_NAMESPACE
31
32using namespace Qt::StringLiterals;
33
34QStringList QLibraryPrivate::suffixes_sys(const QString &fullVersion)
35{
36 QStringList suffixes;
37#if defined(Q_OS_HPUX)
38 // according to
39 // http://docs.hp.com/en/B2355-90968/linkerdifferencesiapa.htm
40
41 // In PA-RISC (PA-32 and PA-64) shared libraries are suffixed
42 // with .sl. In IPF (32-bit and 64-bit), the shared libraries
43 // are suffixed with .so. For compatibility, the IPF linker
44 // also supports the .sl suffix.
45
46 // But since we don't know if we are built on HPUX or HPUXi,
47 // we support both .sl (and .<version>) and .so suffixes but
48 // .so is preferred.
49# if defined(__ia64)
50 if (!fullVersion.isEmpty()) {
51 suffixes << ".so.%1"_L1.arg(fullVersion);
52 } else {
53 suffixes << ".so"_L1;
54 }
55# endif
56 if (!fullVersion.isEmpty()) {
57 suffixes << ".sl.%1"_L1.arg(fullVersion);
58 suffixes << ".%1"_L1.arg(fullVersion);
59 } else {
60 suffixes << ".sl"_L1;
61 }
62#elif defined(Q_OS_CYGWIN)
63 if (!fullVersion.isEmpty()) {
64 suffixes << "-%1.dll"_L1.arg(fullVersion);
65 } else {
66 suffixes << QStringLiteral(".dll");
67 }
68#elif defined(Q_OS_AIX)
69 suffixes << ".a";
70
71#else
72 if (!fullVersion.isEmpty()) {
73 suffixes << ".so.%1"_L1.arg(fullVersion);
74 } else {
75 suffixes << ".so"_L1;
76# ifdef Q_OS_ANDROID
77 suffixes << QStringLiteral(LIBS_SUFFIX);
78# endif
79 }
80#endif
81# ifdef Q_OS_DARWIN
82 if (!fullVersion.isEmpty()) {
83 suffixes << ".%1.bundle"_L1.arg(fullVersion);
84 suffixes << ".%1.dylib"_L1.arg(fullVersion);
85 } else {
86 suffixes << ".bundle"_L1 << ".dylib"_L1;
87 }
88#endif
89 return suffixes;
90}
91
92bool QLibraryPrivate::load_sys()
93{
94#if defined(Q_OS_WASM) && defined(QT_STATIC)
95 // emscripten does not support dlopen when using static linking
96 return false;
97#endif
98
99 QMutexLocker locker(&mutex);
100 QString attempt;
101 QFileSystemEntry fsEntry(fileName);
102
103 QString path = fsEntry.path();
104 QString name = fsEntry.fileName();
105 if (path == "."_L1 && !fileName.startsWith(path))
106 path.clear();
107 else
108 path += u'/';
109
110 QStringList suffixes;
111 QStringList prefixes;
112 if (pluginState != IsAPlugin) {
113 prefixes << prefix_sys().toString();
114 suffixes = suffixes_sys(fullVersion);
115 }
116 int dlFlags = 0;
117 auto loadHints = this->loadHints();
118 if (loadHints & QLibrary::ResolveAllSymbolsHint) {
119 dlFlags |= RTLD_NOW;
120 } else {
121 dlFlags |= RTLD_LAZY;
122 }
123 if (loadHints & QLibrary::ExportExternalSymbolsHint) {
124 dlFlags |= RTLD_GLOBAL;
125 }
126#if !defined(Q_OS_CYGWIN)
127 else {
128 dlFlags |= RTLD_LOCAL;
129 }
130#endif
131#if defined(RTLD_DEEPBIND)
132 if (loadHints & QLibrary::DeepBindHint)
133 dlFlags |= RTLD_DEEPBIND;
134#endif
135
136 // Provide access to RTLD_NODELETE flag on Unix
137 // From GNU documentation on RTLD_NODELETE:
138 // Do not unload the library during dlclose(). Consequently, the
139 // library's specific static variables are not reinitialized if the
140 // library is reloaded with dlopen() at a later time.
141#if defined(RTLD_NODELETE)
142 if (loadHints & QLibrary::PreventUnloadHint) {
143# ifdef Q_OS_ANDROID // RTLD_NODELETE flag is supported by Android 23+
144 if (QtAndroidPrivate::androidSdkVersion() > 22)
145# endif
146 dlFlags |= RTLD_NODELETE;
147 }
148#endif
149
150#if defined(Q_OS_AIX) // Not sure if any other platform actually support this thing.
151 if (loadHints & QLibrary::LoadArchiveMemberHint) {
152 dlFlags |= RTLD_MEMBER;
153 }
154#endif
155
156 // If the filename is an absolute path then we want to try that first as it is most likely
157 // what the callee wants. If we have been given a non-absolute path then lets try the
158 // native library name first to avoid unnecessary calls to dlopen().
159 if (fsEntry.isAbsolute()) {
160 suffixes.prepend(QString());
161 prefixes.prepend(QString());
162 } else {
163 suffixes.append(QString());
164 prefixes.append(QString());
165 }
166
167#if defined(Q_PROCESSOR_X86) && !defined(Q_OS_DARWIN)
168 if (qCpuHasFeature(ArchHaswell)) {
169 auto transform = [](QStringList &list, void (*f)(QString *)) {
170 QStringList tmp;
171 qSwap(tmp, list);
172 list.reserve(tmp.size() * 2);
173 for (const QString &s : std::as_const(tmp)) {
174 QString modifiedPath = s;
175 f(&modifiedPath);
176 list.append(modifiedPath);
177 list.append(s);
178 }
179 };
180 if (pluginState == IsAPlugin) {
181 // add ".avx2" to each suffix in the list
182 transform(suffixes, [](QString *s) { s->append(".avx2"_L1); });
183 } else {
184# ifdef __GLIBC__
185 // prepend "glibc-hwcaps/x86-64-v3/" to each prefix in the list
186 transform(prefixes, [](QString *s) { s->prepend("glibc-hwcaps/x86-64-v3/"_L1); });
187# endif
188 }
189 }
190#endif
191
192 locker.unlock();
193 bool retry = true;
194 Handle hnd = nullptr;
195 for (int prefix = 0; retry && !hnd && prefix < prefixes.size(); prefix++) {
196 for (int suffix = 0; retry && !hnd && suffix < suffixes.size(); suffix++) {
197 if (path.isEmpty() && prefixes.at(prefix).contains(u'/'))
198 continue;
199 if (!suffixes.at(suffix).isEmpty() && name.endsWith(suffixes.at(suffix)))
200 continue;
201 if (loadHints & QLibrary::LoadArchiveMemberHint) {
202 attempt = name;
203 qsizetype lparen = attempt.indexOf(u'(');
204 if (lparen == -1)
205 lparen = attempt.size();
206 attempt = path + prefixes.at(prefix) + attempt.insert(lparen, suffixes.at(suffix));
207 } else {
208 attempt = path + prefixes.at(prefix) + name + suffixes.at(suffix);
209 }
210
211 hnd = dlopen(QFile::encodeName(attempt), dlFlags);
212#ifdef Q_OS_ANDROID
213 if (!hnd) {
214 auto attemptFromBundle = attempt;
215 hnd = dlopen(QFile::encodeName(attemptFromBundle.replace(u'/', u'_')), dlFlags);
216 }
217#endif
218
219 if (!hnd && fileName.startsWith(u'/') && QFile::exists(attempt)) {
220 // We only want to continue if dlopen failed due to that the shared library did not exist.
221 // However, we are only able to apply this check for absolute filenames (since they are
222 // not influenced by the content of LD_LIBRARY_PATH, /etc/ld.so.cache, DT_RPATH etc...)
223 // This is all because dlerror is flawed and cannot tell us the reason why it failed.
224 retry = false;
225 }
226 }
227 }
228
229#ifdef Q_OS_DARWIN
230 if (!hnd) {
231 QByteArray utf8Bundle = fileName.toUtf8();
232 QCFType<CFURLRef> bundleUrl = CFURLCreateFromFileSystemRepresentation(NULL, reinterpret_cast<const UInt8*>(utf8Bundle.data()), utf8Bundle.length(), true);
233 QCFType<CFBundleRef> bundle = CFBundleCreate(NULL, bundleUrl);
234 if (bundle) {
235 QCFType<CFURLRef> url = CFBundleCopyExecutableURL(bundle);
236 char executableFile[FILENAME_MAX];
237 CFURLGetFileSystemRepresentation(url, true, reinterpret_cast<UInt8*>(executableFile), FILENAME_MAX);
238 attempt = QString::fromUtf8(executableFile);
239 hnd = dlopen(QFile::encodeName(attempt), dlFlags);
240 }
241 }
242#endif
243
244 locker.relock();
245 if (!hnd) {
246 errorString = QLibrary::tr("Cannot load library %1: %2")
247 .arg(fileName, QString::fromLocal8Bit(dlerror()));
248 }
249 if (hnd) {
250 qualifiedFileName = attempt;
251 errorString.clear();
252 }
253 pHnd.storeRelaxed(hnd);
254 return (hnd != nullptr);
255}
256
257bool QLibraryPrivate::unload_sys()
258{
259 bool doTryUnload = true;
260#ifndef RTLD_NODELETE
261 if (loadHints() & QLibrary::PreventUnloadHint)
262 doTryUnload = false;
263#endif
264 if (doTryUnload && dlclose(pHnd.loadAcquire())) {
265 const char *error = dlerror();
266#if defined (Q_OS_QNX)
267 // Workaround until fixed in QNX; fixes crash in
268 // QtDeclarative auto test "qqmlenginecleanup" for instance
269 if (!qstrcmp(error, "Shared objects still referenced")) // On QNX that's only "informative"
270 return true;
271#endif
272 errorString = QLibrary::tr("Cannot unload library %1: %2")
273 .arg(fileName, QString::fromLocal8Bit(error));
274 return false;
275 }
276 errorString.clear();
277 return true;
278}
279
280QFunctionPointer QLibraryPrivate::resolve_sys(const char *symbol)
281{
282 QFunctionPointer address = QFunctionPointer(dlsym(pHnd.loadAcquire(), symbol));
283 return address;
284}
285
286QT_END_NAMESPACE