Qt 6.x
The Qt SDK
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
25
26using namespace Qt::StringLiterals;
27
29{
30 const char *err = dlerror();
31 return err ? u'(' + QString::fromLocal8Bit(err) + u')' : QString();
32}
33
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_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
87{
88 return QStringList() << "lib"_L1;
89}
90
91bool QLibraryPrivate::load_sys()
92{
93#if defined(Q_OS_WASM) && defined(QT_STATIC)
94 // emscripten does not support dlopen when using static linking
95 return false;
96#endif
97
98 QMutexLocker locker(&mutex);
99 QString attempt;
100 QFileSystemEntry fsEntry(fileName);
101
102 QString path = fsEntry.path();
103 QString name = fsEntry.fileName();
104 if (path == "."_L1 && !fileName.startsWith(path))
105 path.clear();
106 else
107 path += u'/';
108
109 QStringList suffixes;
110 QStringList prefixes;
111 if (pluginState != IsAPlugin) {
112 prefixes = prefixes_sys();
113 suffixes = suffixes_sys(fullVersion);
114 }
115 int dlFlags = 0;
116 auto loadHints = this->loadHints();
118 dlFlags |= RTLD_NOW;
119 } else {
120 dlFlags |= RTLD_LAZY;
121 }
123 dlFlags |= RTLD_GLOBAL;
124 }
125#if !defined(Q_OS_CYGWIN)
126 else {
127 dlFlags |= RTLD_LOCAL;
128 }
129#endif
130#if defined(RTLD_DEEPBIND)
132 dlFlags |= RTLD_DEEPBIND;
133#endif
134
135 // Provide access to RTLD_NODELETE flag on Unix
136 // From GNU documentation on RTLD_NODELETE:
137 // Do not unload the library during dlclose(). Consequently, the
138 // library's specific static variables are not reinitialized if the
139 // library is reloaded with dlopen() at a later time.
140#if defined(RTLD_NODELETE)
142# ifdef Q_OS_ANDROID // RTLD_NODELETE flag is supported by Android 23+
144# endif
145 dlFlags |= RTLD_NODELETE;
146 }
147#endif
148
149#if defined(Q_OS_AIX) // Not sure if any other platform actually support this thing.
151 dlFlags |= RTLD_MEMBER;
152 }
153#endif
154
155 // If the filename is an absolute path then we want to try that first as it is most likely
156 // what the callee wants. If we have been given a non-absolute path then lets try the
157 // native library name first to avoid unnecessary calls to dlopen().
158 if (fsEntry.isAbsolute()) {
159 suffixes.prepend(QString());
160 prefixes.prepend(QString());
161 } else {
162 suffixes.append(QString());
163 prefixes.append(QString());
164 }
165
166#if defined(Q_PROCESSOR_X86) && !defined(Q_OS_DARWIN)
167 if (qCpuHasFeature(ArchHaswell)) {
168 auto transform = [](QStringList &list, void (*f)(QString *)) {
169 QStringList tmp;
170 qSwap(tmp, list);
171 list.reserve(tmp.size() * 2);
172 for (const QString &s : std::as_const(tmp)) {
173 QString modifiedPath = s;
174 f(&modifiedPath);
175 list.append(modifiedPath);
176 list.append(s);
177 }
178 };
179 if (pluginState == IsAPlugin) {
180 // add ".avx2" to each suffix in the list
181 transform(suffixes, [](QString *s) { s->append(".avx2"_L1); });
182 } else {
183 // prepend "haswell/" to each prefix in the list
184 transform(prefixes, [](QString *s) { s->prepend("haswell/"_L1); });
185 }
186 }
187#endif
188
189 locker.unlock();
190 bool retry = true;
191 Handle hnd = nullptr;
192 for (int prefix = 0; retry && !hnd && prefix < prefixes.size(); prefix++) {
193 for (int suffix = 0; retry && !hnd && suffix < suffixes.size(); suffix++) {
194 if (!prefixes.at(prefix).isEmpty() && name.startsWith(prefixes.at(prefix)))
195 continue;
196 if (path.isEmpty() && prefixes.at(prefix).contains(u'/'))
197 continue;
198 if (!suffixes.at(suffix).isEmpty() && name.endsWith(suffixes.at(suffix)))
199 continue;
201 attempt = name;
202 qsizetype lparen = attempt.indexOf(u'(');
203 if (lparen == -1)
204 lparen = attempt.size();
205 attempt = path + prefixes.at(prefix) + attempt.insert(lparen, suffixes.at(suffix));
206 } else {
207 attempt = path + prefixes.at(prefix) + name + suffixes.at(suffix);
208 }
209
210 hnd = dlopen(QFile::encodeName(attempt), dlFlags);
211#ifdef Q_OS_ANDROID
212 if (!hnd) {
213 auto attemptFromBundle = attempt;
214 hnd = dlopen(QFile::encodeName(attemptFromBundle.replace(u'/', u'_')), dlFlags);
215 }
216 if (hnd) {
217 using JniOnLoadPtr = jint (*)(JavaVM *vm, void *reserved);
218 JniOnLoadPtr jniOnLoad = reinterpret_cast<JniOnLoadPtr>(dlsym(hnd, "JNI_OnLoad"));
219 if (jniOnLoad && jniOnLoad(QJniEnvironment::javaVM(), nullptr) == JNI_ERR) {
220 dlclose(hnd);
221 hnd = nullptr;
222 }
223 }
224#endif
225
226 if (!hnd && fileName.startsWith(u'/') && QFile::exists(attempt)) {
227 // We only want to continue if dlopen failed due to that the shared library did not exist.
228 // However, we are only able to apply this check for absolute filenames (since they are
229 // not influenced by the content of LD_LIBRARY_PATH, /etc/ld.so.cache, DT_RPATH etc...)
230 // This is all because dlerror is flawed and cannot tell us the reason why it failed.
231 retry = false;
232 }
233 }
234 }
235
236#ifdef Q_OS_DARWIN
237 if (!hnd) {
238 QByteArray utf8Bundle = fileName.toUtf8();
239 QCFType<CFURLRef> bundleUrl = CFURLCreateFromFileSystemRepresentation(NULL, reinterpret_cast<const UInt8*>(utf8Bundle.data()), utf8Bundle.length(), true);
240 QCFType<CFBundleRef> bundle = CFBundleCreate(NULL, bundleUrl);
241 if (bundle) {
242 QCFType<CFURLRef> url = CFBundleCopyExecutableURL(bundle);
243 char executableFile[FILENAME_MAX];
244 CFURLGetFileSystemRepresentation(url, true, reinterpret_cast<UInt8*>(executableFile), FILENAME_MAX);
245 attempt = QString::fromUtf8(executableFile);
246 hnd = dlopen(QFile::encodeName(attempt), dlFlags);
247 }
248 }
249#endif
250
251 locker.relock();
252 if (!hnd) {
253 errorString = QLibrary::tr("Cannot load library %1: %2").arg(fileName, qdlerror());
254 }
255 if (hnd) {
256 qualifiedFileName = attempt;
258 }
259 pHnd.storeRelaxed(hnd);
260 return (hnd != nullptr);
261}
262
263bool QLibraryPrivate::unload_sys()
264{
265 if (dlclose(pHnd.loadAcquire())) {
266#if defined (Q_OS_QNX) // Workaround until fixed in QNX; fixes crash in
267 char *error = dlerror(); // QtDeclarative auto test "qqmlenginecleanup" for instance
268 if (!qstrcmp(error, "Shared objects still referenced")) // On QNX that's only "informative"
269 return true;
270 errorString = QLibrary::tr("Cannot unload library %1: %2").arg(fileName,
272#else
273 errorString = QLibrary::tr("Cannot unload library %1: %2").arg(fileName, qdlerror());
274#endif
275 return false;
276 }
278 return true;
279}
280
281#if defined(Q_OS_LINUX)
282Q_CORE_EXPORT QFunctionPointer qt_linux_find_symbol_sys(const char *symbol)
283{
284 return QFunctionPointer(dlsym(RTLD_DEFAULT, symbol));
285}
286#endif
287
288#ifdef Q_OS_DARWIN
289Q_CORE_EXPORT QFunctionPointer qt_mac_resolve_sys(void *handle, const char *symbol)
290{
291 return QFunctionPointer(dlsym(handle, symbol));
292}
293#endif
294
295QFunctionPointer QLibraryPrivate::resolve_sys(const char *symbol)
296{
297 QFunctionPointer address = QFunctionPointer(dlsym(pHnd.loadAcquire(), symbol));
298 return address;
299}
300
Type loadAcquire() const noexcept
void storeRelaxed(Type newValue) noexcept
\inmodule QtCore
Definition qbytearray.h:57
char * data()
\macro QT_NO_CAST_FROM_BYTEARRAY
Definition qbytearray.h:534
qsizetype length() const noexcept
Same as size().
Definition qbytearray.h:479
static QByteArray encodeName(const QString &fileName)
Converts fileName to an 8-bit encoding that you can use in native APIs.
Definition qfile.h:158
bool exists() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qfile.cpp:351
QString errorString
Definition qlibrary_p.h:83
QString qualifiedFileName
Definition qlibrary_p.h:84
QAtomicPointer< std::remove_pointer< Handle >::type > pHnd
Definition qlibrary_p.h:77
static QStringList suffixes_sys(const QString &fullVersion)
const QString fileName
Definition qlibrary_p.h:57
const QString fullVersion
Definition qlibrary_p.h:58
static QStringList prefixes_sys()
QLibrary::LoadHints loadHints() const
Definition qlibrary_p.h:66
@ LoadArchiveMemberHint
Definition qlibrary.h:25
@ PreventUnloadHint
Definition qlibrary.h:26
@ ExportExternalSymbolsHint
Definition qlibrary.h:24
@ ResolveAllSymbolsHint
Definition qlibrary.h:23
@ DeepBindHint
Definition qlibrary.h:27
void reserve(qsizetype size)
Definition qlist.h:746
void append(parameter_type t)
Definition qlist.h:441
\inmodule QtCore
Definition qmutex.h:317
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
bool startsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string starts with s; otherwise returns false.
Definition qstring.cpp:5299
static QString fromLocal8Bit(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5788
void clear()
Clears the contents of the string and makes it null.
Definition qstring.h:1107
qsizetype size() const
Returns the number of characters in this string.
Definition qstring.h:182
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5857
QString arg(qlonglong a, int fieldwidth=0, int base=10, QChar fillChar=u' ') const
Definition qstring.cpp:8606
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
QString & insert(qsizetype i, QChar c)
Definition qstring.cpp:3110
static QString static QString qsizetype indexOf(QChar c, qsizetype from=0, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition qstring.cpp:4420
QByteArray toUtf8() const &
Definition qstring.h:563
qSwap(pi, e)
Combined button and popup list for selecting options.
Q_CORE_EXPORT jint androidSdkVersion()
Q_CORE_EXPORT int qstrcmp(const char *str1, const char *str2)
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char return DBusMessage dbus_bool_t return DBusMessage dbus_uint32_t return DBusMessage void
DBusConnection const char DBusError * error
static QString qdlerror()
GLuint64 GLenum void * handle
GLfloat GLfloat f
GLuint name
GLuint GLenum GLenum transform
GLuint GLuint64EXT address
GLsizei const GLchar *const * path
GLdouble s
[6]
Definition qopenglext.h:235
#define qCpuHasFeature(feature)
Definition qsimd_p.h:378
#define QStringLiteral(str)
ptrdiff_t qsizetype
Definition qtypes.h:70
QList< int > list
[14]
QString bundle
QUrl url("example.com")
[constructor-url-reference]