Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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 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_AIX)
57 suffixes << ".a";
58
59#else
60 if (!fullVersion.isEmpty()) {
61 suffixes << ".so.%1"_L1.arg(fullVersion);
62 } else {
63 suffixes << ".so"_L1;
64# ifdef Q_OS_ANDROID
65 suffixes << QStringLiteral(LIBS_SUFFIX);
66# endif
67 }
68#endif
69# ifdef Q_OS_DARWIN
70 if (!fullVersion.isEmpty()) {
71 suffixes << ".%1.bundle"_L1.arg(fullVersion);
72 suffixes << ".%1.dylib"_L1.arg(fullVersion);
73 } else {
74 suffixes << ".bundle"_L1 << ".dylib"_L1;
75 }
76#endif
77 return suffixes;
78}
79
81{
82 return QStringList() << "lib"_L1;
83}
84
85bool QLibraryPrivate::load_sys()
86{
87#if defined(Q_OS_WASM) && defined(QT_STATIC)
88 // emscripten does not support dlopen when using static linking
89 return false;
90#endif
91
92 QMutexLocker locker(&mutex);
93 QString attempt;
95
96 QString path = fsEntry.path();
97 QString name = fsEntry.fileName();
98 if (path == "."_L1 && !fileName.startsWith(path))
99 path.clear();
100 else
101 path += u'/';
102
103 QStringList suffixes;
104 QStringList prefixes;
105 if (pluginState != IsAPlugin) {
106 prefixes = prefixes_sys();
107 suffixes = suffixes_sys(fullVersion);
108 }
109 int dlFlags = 0;
110 auto loadHints = this->loadHints();
112 dlFlags |= RTLD_NOW;
113 } else {
114 dlFlags |= RTLD_LAZY;
115 }
117 dlFlags |= RTLD_GLOBAL;
118 }
119#if !defined(Q_OS_CYGWIN)
120 else {
121 dlFlags |= RTLD_LOCAL;
122 }
123#endif
124#if defined(RTLD_DEEPBIND)
126 dlFlags |= RTLD_DEEPBIND;
127#endif
128
129 // Provide access to RTLD_NODELETE flag on Unix
130 // From GNU documentation on RTLD_NODELETE:
131 // Do not unload the library during dlclose(). Consequently, the
132 // library's specific static variables are not reinitialized if the
133 // library is reloaded with dlopen() at a later time.
134#if defined(RTLD_NODELETE)
136# ifdef Q_OS_ANDROID // RTLD_NODELETE flag is supported by Android 23+
138# endif
139 dlFlags |= RTLD_NODELETE;
140 }
141#endif
142
143#if defined(Q_OS_AIX) // Not sure if any other platform actually support this thing.
145 dlFlags |= RTLD_MEMBER;
146 }
147#endif
148
149 // If the filename is an absolute path then we want to try that first as it is most likely
150 // what the callee wants. If we have been given a non-absolute path then lets try the
151 // native library name first to avoid unnecessary calls to dlopen().
152 if (fsEntry.isAbsolute()) {
153 suffixes.prepend(QString());
154 prefixes.prepend(QString());
155 } else {
156 suffixes.append(QString());
157 prefixes.append(QString());
158 }
159
160#if defined(Q_PROCESSOR_X86) && !defined(Q_OS_DARWIN)
161 if (qCpuHasFeature(ArchHaswell)) {
162 auto transform = [](QStringList &list, void (*f)(QString *)) {
163 QStringList tmp;
164 qSwap(tmp, list);
165 list.reserve(tmp.size() * 2);
166 for (const QString &s : std::as_const(tmp)) {
167 QString modifiedPath = s;
168 f(&modifiedPath);
169 list.append(modifiedPath);
170 list.append(s);
171 }
172 };
173 if (pluginState == IsAPlugin) {
174 // add ".avx2" to each suffix in the list
175 transform(suffixes, [](QString *s) { s->append(".avx2"_L1); });
176 } else {
177 // prepend "haswell/" to each prefix in the list
178 transform(prefixes, [](QString *s) { s->prepend("haswell/"_L1); });
179 }
180 }
181#endif
182
183 locker.unlock();
184 bool retry = true;
185 Handle hnd = nullptr;
186 for (int prefix = 0; retry && !hnd && prefix < prefixes.size(); prefix++) {
187 for (int suffix = 0; retry && !hnd && suffix < suffixes.size(); suffix++) {
188 if (!prefixes.at(prefix).isEmpty() && name.startsWith(prefixes.at(prefix)))
189 continue;
190 if (path.isEmpty() && prefixes.at(prefix).contains(u'/'))
191 continue;
192 if (!suffixes.at(suffix).isEmpty() && name.endsWith(suffixes.at(suffix)))
193 continue;
195 attempt = name;
196 qsizetype lparen = attempt.indexOf(u'(');
197 if (lparen == -1)
198 lparen = attempt.size();
199 attempt = path + prefixes.at(prefix) + attempt.insert(lparen, suffixes.at(suffix));
200 } else {
201 attempt = path + prefixes.at(prefix) + name + suffixes.at(suffix);
202 }
203
204 hnd = dlopen(QFile::encodeName(attempt), dlFlags);
205#ifdef Q_OS_ANDROID
206 if (!hnd) {
207 auto attemptFromBundle = attempt;
208 hnd = dlopen(QFile::encodeName(attemptFromBundle.replace(u'/', u'_')), dlFlags);
209 }
210#endif
211
212 if (!hnd && fileName.startsWith(u'/') && QFile::exists(attempt)) {
213 // We only want to continue if dlopen failed due to that the shared library did not exist.
214 // However, we are only able to apply this check for absolute filenames (since they are
215 // not influenced by the content of LD_LIBRARY_PATH, /etc/ld.so.cache, DT_RPATH etc...)
216 // This is all because dlerror is flawed and cannot tell us the reason why it failed.
217 retry = false;
218 }
219 }
220 }
221
222#ifdef Q_OS_DARWIN
223 if (!hnd) {
224 QByteArray utf8Bundle = fileName.toUtf8();
225 QCFType<CFURLRef> bundleUrl = CFURLCreateFromFileSystemRepresentation(NULL, reinterpret_cast<const UInt8*>(utf8Bundle.data()), utf8Bundle.length(), true);
226 QCFType<CFBundleRef> bundle = CFBundleCreate(NULL, bundleUrl);
227 if (bundle) {
228 QCFType<CFURLRef> url = CFBundleCopyExecutableURL(bundle);
229 char executableFile[FILENAME_MAX];
230 CFURLGetFileSystemRepresentation(url, true, reinterpret_cast<UInt8*>(executableFile), FILENAME_MAX);
231 attempt = QString::fromUtf8(executableFile);
232 hnd = dlopen(QFile::encodeName(attempt), dlFlags);
233 }
234 }
235#endif
236
237 locker.relock();
238 if (!hnd) {
239 errorString = QLibrary::tr("Cannot load library %1: %2").arg(fileName,
240 QLatin1StringView(dlerror()));
241 }
242 if (hnd) {
243 qualifiedFileName = attempt;
245 }
246 pHnd.storeRelaxed(hnd);
247 return (hnd != nullptr);
248}
249
250bool QLibraryPrivate::unload_sys()
251{
252 bool doTryUnload = true;
253#ifndef RTLD_NODELETE
255 doTryUnload = false;
256#endif
257 if (doTryUnload && dlclose(pHnd.loadAcquire())) {
258 const char *error = dlerror();
259#if defined (Q_OS_QNX)
260 // Workaround until fixed in QNX; fixes crash in
261 // QtDeclarative auto test "qqmlenginecleanup" for instance
262 if (!qstrcmp(error, "Shared objects still referenced")) // On QNX that's only "informative"
263 return true;
264#endif
265 errorString = QLibrary::tr("Cannot unload library %1: %2").arg(fileName,
267 return false;
268 }
270 return true;
271}
272
273QFunctionPointer QLibraryPrivate::resolve_sys(const char *symbol)
274{
275 QFunctionPointer address = QFunctionPointer(dlsym(pHnd.loadAcquire(), symbol));
276 return address;
277}
278
Type loadAcquire() const noexcept
void storeRelaxed(Type newValue) noexcept
\inmodule QtCore
Definition qbytearray.h:57
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:91
QString qualifiedFileName
Definition qlibrary_p.h:92
QAtomicPointer< std::remove_pointer< Handle >::type > pHnd
Definition qlibrary_p.h:85
static QStringList suffixes_sys(const QString &fullVersion)
const QString fileName
Definition qlibrary_p.h:65
const QString fullVersion
Definition qlibrary_p.h:66
static QStringList prefixes_sys()
QLibrary::LoadHints loadHints() const
Definition qlibrary_p.h:74
@ 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:753
void append(parameter_type t)
Definition qlist.h:458
\inmodule QtCore
Definition qmutex.h:313
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
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:5455
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
void clear()
Clears the contents of the string and makes it null.
Definition qstring.h:1252
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:6018
QString arg(qlonglong a, int fieldwidth=0, int base=10, QChar fillChar=u' ') const
Definition qstring.cpp:8870
QByteArray toUtf8() const &
Definition qstring.h:634
Combined button and popup list for selecting options.
Q_CORE_EXPORT jint androidSdkVersion()
Q_CORE_EXPORT int qstrcmp(const char *str1, const char *str2)
QList< QString > QStringList
Constructs a string list that contains the given string, str.
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
GLfloat GLfloat f
GLuint name
GLuint GLenum GLenum transform
GLdouble s
[6]
Definition qopenglext.h:235
GLuint GLuint64EXT address
GLsizei const GLchar *const * path
#define qCpuHasFeature(feature)
Definition qsimd_p.h:387
#define QStringLiteral(str)
QT_BEGIN_NAMESPACE constexpr void qSwap(T &value1, T &value2) noexcept(std::is_nothrow_swappable_v< T >)
Definition qswap.h:20
ptrdiff_t qsizetype
Definition qtypes.h:165
QList< int > list
[14]
QUrl url("example.com")
[constructor-url-reference]