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