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_AIX)
57 suffixes << ".a";
58
59#else
60 if (!fullVersion.isEmpty()) {
61 suffixes << ".so.%1"_L1.arg(args: 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
80QStringList QLibraryPrivate::prefixes_sys()
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;
94 QFileSystemEntry fsEntry(fileName);
95
96 QString path = fsEntry.path();
97 QString name = fsEntry.fileName();
98 if (path == "."_L1 && !fileName.startsWith(s: 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();
111 if (loadHints & QLibrary::ResolveAllSymbolsHint) {
112 dlFlags |= RTLD_NOW;
113 } else {
114 dlFlags |= RTLD_LAZY;
115 }
116 if (loadHints & QLibrary::ExportExternalSymbolsHint) {
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)
125 if (loadHints & QLibrary::DeepBindHint)
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)
135 if (loadHints & QLibrary::PreventUnloadHint) {
136# ifdef Q_OS_ANDROID // RTLD_NODELETE flag is supported by Android 23+
137 if (QtAndroidPrivate::androidSdkVersion() > 22)
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.
144 if (loadHints & QLibrary::LoadArchiveMemberHint) {
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(t: QString());
154 prefixes.prepend(t: QString());
155 } else {
156 suffixes.append(t: QString());
157 prefixes.append(t: 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(value1&: tmp, value2&: list);
165 list.reserve(asize: tmp.size() * 2);
166 for (const QString &s : std::as_const(t&: tmp)) {
167 QString modifiedPath = s;
168 f(&modifiedPath);
169 list.append(t: modifiedPath);
170 list.append(t: s);
171 }
172 };
173 if (pluginState == IsAPlugin) {
174 // add ".avx2" to each suffix in the list
175 transform(suffixes, [](QString *s) { s->append(s: ".avx2"_L1); });
176 } else {
177# ifdef __GLIBC__
178 // prepend "glibc-hwcaps/x86-64-v3/" to each prefix in the list
179 transform(prefixes, [](QString *s) { s->prepend(s: "glibc-hwcaps/x86-64-v3/"_L1); });
180# endif
181 }
182 }
183#endif
184
185 locker.unlock();
186 bool retry = true;
187 Handle hnd = nullptr;
188 for (int prefix = 0; retry && !hnd && prefix < prefixes.size(); prefix++) {
189 for (int suffix = 0; retry && !hnd && suffix < suffixes.size(); suffix++) {
190 if (path.isEmpty() && prefixes.at(i: prefix).contains(c: u'/'))
191 continue;
192 if (!suffixes.at(i: suffix).isEmpty() && name.endsWith(s: suffixes.at(i: suffix)))
193 continue;
194 if (loadHints & QLibrary::LoadArchiveMemberHint) {
195 attempt = name;
196 qsizetype lparen = attempt.indexOf(c: u'(');
197 if (lparen == -1)
198 lparen = attempt.size();
199 attempt = path + prefixes.at(i: prefix) + attempt.insert(i: lparen, s: suffixes.at(i: suffix));
200 } else {
201 attempt = path + prefixes.at(i: prefix) + name + suffixes.at(i: suffix);
202 }
203
204 hnd = dlopen(file: QFile::encodeName(fileName: attempt), mode: 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(c: u'/') && QFile::exists(fileName: 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(s: "Cannot load library %1: %2")
240 .arg(args: fileName, args: QString::fromLocal8Bit(ba: dlerror()));
241 }
242 if (hnd) {
243 qualifiedFileName = attempt;
244 errorString.clear();
245 }
246 pHnd.storeRelaxed(newValue: hnd);
247 return (hnd != nullptr);
248}
249
250bool QLibraryPrivate::unload_sys()
251{
252 bool doTryUnload = true;
253#ifndef RTLD_NODELETE
254 if (loadHints() & QLibrary::PreventUnloadHint)
255 doTryUnload = false;
256#endif
257 if (doTryUnload && dlclose(handle: 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(s: "Cannot unload library %1: %2")
266 .arg(args: fileName, args: QString::fromLocal8Bit(ba: error));
267 return false;
268 }
269 errorString.clear();
270 return true;
271}
272
273QFunctionPointer QLibraryPrivate::resolve_sys(const char *symbol)
274{
275 QFunctionPointer address = QFunctionPointer(dlsym(handle: pHnd.loadAcquire(), name: symbol));
276 return address;
277}
278
279QT_END_NAMESPACE
280

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

source code of qtbase/src/corelib/plugin/qlibrary_unix.cpp