1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Copyright (C) 2018 Intel Corporation
5** Contact: https://www.qt.io/licensing/
6**
7** This file is part of the QtCore module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial License Usage
11** Licensees holding valid commercial Qt licenses may use this file in
12** accordance with the commercial license agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and The Qt Company. For licensing terms
15** and conditions see https://www.qt.io/terms-conditions. For further
16** information use the contact form at https://www.qt.io/contact-us.
17**
18** GNU Lesser General Public License Usage
19** Alternatively, this file may be used under the terms of the GNU Lesser
20** General Public License version 3 as published by the Free Software
21** Foundation and appearing in the file LICENSE.LGPL3 included in the
22** packaging of this file. Please review the following information to
23** ensure the GNU Lesser General Public License version 3 requirements
24** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
25**
26** GNU General Public License Usage
27** Alternatively, this file may be used under the terms of the GNU
28** General Public License version 2.0 or (at your option) the GNU General
29** Public license version 3 or any later version approved by the KDE Free
30** Qt Foundation. The licenses are as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
32** included in the packaging of this file. Please review the following
33** information to ensure the GNU General Public License requirements will
34** be met: https://www.gnu.org/licenses/gpl-2.0.html and
35** https://www.gnu.org/licenses/gpl-3.0.html.
36**
37** $QT_END_LICENSE$
38**
39****************************************************************************/
40
41#include "qplatformdefs.h"
42
43#include <qfile.h>
44#include "qlibrary_p.h"
45#include <qcoreapplication.h>
46#include <private/qfilesystementry_p.h>
47#include <private/qsimd_p.h>
48
49#include <dlfcn.h>
50
51#ifdef Q_OS_MAC
52# include <private/qcore_mac_p.h>
53#endif
54
55QT_BEGIN_NAMESPACE
56
57static QString qdlerror()
58{
59 const char *err = dlerror();
60 return err ? QLatin1Char('(') + QString::fromLocal8Bit(err) + QLatin1Char(')'): QString();
61}
62
63QStringList QLibraryPrivate::suffixes_sys(const QString& fullVersion)
64{
65 QStringList suffixes;
66#if defined(Q_OS_HPUX)
67 // according to
68 // http://docs.hp.com/en/B2355-90968/linkerdifferencesiapa.htm
69
70 // In PA-RISC (PA-32 and PA-64) shared libraries are suffixed
71 // with .sl. In IPF (32-bit and 64-bit), the shared libraries
72 // are suffixed with .so. For compatibility, the IPF linker
73 // also supports the .sl suffix.
74
75 // But since we don't know if we are built on HPUX or HPUXi,
76 // we support both .sl (and .<version>) and .so suffixes but
77 // .so is preferred.
78# if defined(__ia64)
79 if (!fullVersion.isEmpty()) {
80 suffixes << QLatin1String(".so.%1").arg(fullVersion);
81 } else {
82 suffixes << QLatin1String(".so");
83 }
84# endif
85 if (!fullVersion.isEmpty()) {
86 suffixes << QLatin1String(".sl.%1").arg(fullVersion);
87 suffixes << QLatin1String(".%1").arg(fullVersion);
88 } else {
89 suffixes << QLatin1String(".sl");
90 }
91#elif defined(Q_OS_AIX)
92 suffixes << ".a";
93
94#else
95 if (!fullVersion.isEmpty()) {
96 suffixes << QLatin1String(".so.%1").arg(fullVersion);
97 } else {
98 suffixes << QLatin1String(".so");
99 }
100#endif
101# ifdef Q_OS_MAC
102 if (!fullVersion.isEmpty()) {
103 suffixes << QLatin1String(".%1.bundle").arg(fullVersion);
104 suffixes << QLatin1String(".%1.dylib").arg(fullVersion);
105 } else {
106 suffixes << QLatin1String(".bundle") << QLatin1String(".dylib");
107 }
108#endif
109 return suffixes;
110}
111
112QStringList QLibraryPrivate::prefixes_sys()
113{
114 return QStringList() << QLatin1String("lib");
115}
116
117bool QLibraryPrivate::load_sys()
118{
119 QString attempt;
120 QFileSystemEntry fsEntry(fileName);
121
122 QString path = fsEntry.path();
123 QString name = fsEntry.fileName();
124 if (path == QLatin1String(".") && !fileName.startsWith(path))
125 path.clear();
126 else
127 path += QLatin1Char('/');
128
129 QStringList suffixes;
130 QStringList prefixes;
131 if (pluginState != IsAPlugin) {
132 prefixes = prefixes_sys();
133 suffixes = suffixes_sys(fullVersion);
134 }
135 int dlFlags = 0;
136 int loadHints = this->loadHints();
137 if (loadHints & QLibrary::ResolveAllSymbolsHint) {
138 dlFlags |= RTLD_NOW;
139 } else {
140 dlFlags |= RTLD_LAZY;
141 }
142 if (loadHints & QLibrary::ExportExternalSymbolsHint) {
143 dlFlags |= RTLD_GLOBAL;
144 }
145#if !defined(Q_OS_CYGWIN)
146 else {
147 dlFlags |= RTLD_LOCAL;
148 }
149#endif
150#if defined(RTLD_DEEPBIND)
151 if (loadHints & QLibrary::DeepBindHint)
152 dlFlags |= RTLD_DEEPBIND;
153#endif
154
155 // Provide access to RTLD_NODELETE flag on Unix
156 // From GNU documentation on RTLD_NODELETE:
157 // Do not unload the library during dlclose(). Consequently, the
158 // library's specific static variables are not reinitialized if the
159 // library is reloaded with dlopen() at a later time.
160#if defined(RTLD_NODELETE) && !defined(Q_OS_ANDROID)
161 if (loadHints & QLibrary::PreventUnloadHint) {
162 dlFlags |= RTLD_NODELETE;
163 }
164#endif
165
166#if defined(Q_OS_AIX) // Not sure if any other platform actually support this thing.
167 if (loadHints & QLibrary::LoadArchiveMemberHint) {
168 dlFlags |= RTLD_MEMBER;
169 }
170#endif
171
172 // If the filename is an absolute path then we want to try that first as it is most likely
173 // what the callee wants. If we have been given a non-absolute path then lets try the
174 // native library name first to avoid unnecessary calls to dlopen().
175 if (fsEntry.isAbsolute()) {
176 suffixes.prepend(QString());
177 prefixes.prepend(QString());
178 } else {
179 suffixes.append(QString());
180 prefixes.append(QString());
181 }
182
183#if defined(Q_PROCESSOR_X86) && !defined(Q_OS_DARWIN)
184 if (qCpuHasFeature(ArchHaswell)) {
185 auto transform = [](QStringList &list, void (*f)(QString *)) {
186 QStringList tmp;
187 qSwap(tmp, list);
188 list.reserve(tmp.size() * 2);
189 for (const QString &s : qAsConst(tmp)) {
190 QString modifiedPath = s;
191 f(&modifiedPath);
192 list.append(modifiedPath);
193 list.append(s);
194 }
195 };
196 if (pluginState == IsAPlugin) {
197 // add ".avx2" to each suffix in the list
198 transform(suffixes, [](QString *s) { s->append(QLatin1String(".avx2")); });
199 } else {
200 // prepend "haswell/" to each prefix in the list
201 transform(prefixes, [](QString *s) { s->prepend(QLatin1String("haswell/")); });
202 }
203 }
204#endif
205
206 bool retry = true;
207 for(int prefix = 0; retry && !pHnd && prefix < prefixes.size(); prefix++) {
208 for(int suffix = 0; retry && !pHnd && suffix < suffixes.size(); suffix++) {
209 if (!prefixes.at(prefix).isEmpty() && name.startsWith(prefixes.at(prefix)))
210 continue;
211 if (!suffixes.at(suffix).isEmpty() && name.endsWith(suffixes.at(suffix)))
212 continue;
213 if (loadHints & QLibrary::LoadArchiveMemberHint) {
214 attempt = name;
215 int lparen = attempt.indexOf(QLatin1Char('('));
216 if (lparen == -1)
217 lparen = attempt.count();
218 attempt = path + prefixes.at(prefix) + attempt.insert(lparen, suffixes.at(suffix));
219 } else {
220 attempt = path + prefixes.at(prefix) + name + suffixes.at(suffix);
221 }
222 pHnd = dlopen(QFile::encodeName(attempt), dlFlags);
223
224 if (!pHnd && fileName.startsWith(QLatin1Char('/')) && QFile::exists(attempt)) {
225 // We only want to continue if dlopen failed due to that the shared library did not exist.
226 // However, we are only able to apply this check for absolute filenames (since they are
227 // not influenced by the content of LD_LIBRARY_PATH, /etc/ld.so.cache, DT_RPATH etc...)
228 // This is all because dlerror is flawed and cannot tell us the reason why it failed.
229 retry = false;
230 }
231 }
232 }
233
234#ifdef Q_OS_MAC
235 if (!pHnd) {
236 QByteArray utf8Bundle = fileName.toUtf8();
237 QCFType<CFURLRef> bundleUrl = CFURLCreateFromFileSystemRepresentation(NULL, reinterpret_cast<const UInt8*>(utf8Bundle.data()), utf8Bundle.length(), true);
238 QCFType<CFBundleRef> bundle = CFBundleCreate(NULL, bundleUrl);
239 if(bundle) {
240 QCFType<CFURLRef> url = CFBundleCopyExecutableURL(bundle);
241 char executableFile[FILENAME_MAX];
242 CFURLGetFileSystemRepresentation(url, true, reinterpret_cast<UInt8*>(executableFile), FILENAME_MAX);
243 attempt = QString::fromUtf8(executableFile);
244 pHnd = dlopen(QFile::encodeName(attempt), dlFlags);
245 }
246 }
247#endif
248 if (!pHnd) {
249 errorString = QLibrary::tr("Cannot load library %1: %2").arg(fileName, qdlerror());
250 }
251 if (pHnd) {
252 qualifiedFileName = attempt;
253 errorString.clear();
254 }
255 return (pHnd != 0);
256}
257
258bool QLibraryPrivate::unload_sys()
259{
260 if (dlclose(pHnd)) {
261#if defined (Q_OS_QNX) // Workaround until fixed in QNX; fixes crash in
262 char *error = dlerror(); // QtDeclarative auto test "qqmlenginecleanup" for instance
263 if (!qstrcmp(error, "Shared objects still referenced")) // On QNX that's only "informative"
264 return true;
265 errorString = QLibrary::tr("Cannot unload library %1: %2").arg(fileName,
266 QLatin1String(error));
267#else
268 errorString = QLibrary::tr("Cannot unload library %1: %2").arg(fileName, qdlerror());
269#endif
270 return false;
271 }
272 errorString.clear();
273 return true;
274}
275
276#if defined(Q_OS_LINUX)
277Q_CORE_EXPORT QFunctionPointer qt_linux_find_symbol_sys(const char *symbol)
278{
279 return QFunctionPointer(dlsym(RTLD_DEFAULT, symbol));
280}
281#endif
282
283#ifdef Q_OS_MAC
284Q_CORE_EXPORT QFunctionPointer qt_mac_resolve_sys(void *handle, const char *symbol)
285{
286 return QFunctionPointer(dlsym(handle, symbol));
287}
288#endif
289
290QFunctionPointer QLibraryPrivate::resolve_sys(const char* symbol)
291{
292 QFunctionPointer address = QFunctionPointer(dlsym(pHnd, symbol));
293 if (!address) {
294 errorString = QLibrary::tr("Cannot resolve symbol \"%1\" in %2: %3").arg(
295 QString::fromLatin1(symbol), fileName, qdlerror());
296 } else {
297 errorString.clear();
298 }
299 return address;
300}
301
302QT_END_NAMESPACE
303