1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Copyright (C) 2020 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 
55 #ifdef Q_OS_ANDROID
56 #  include <private/qjnihelpers_p.h>
57 #endif
58 
59 QT_BEGIN_NAMESPACE
60 
qdlerror()61 static QString qdlerror()
62 {
63     const char *err = dlerror();
64     return err ? QLatin1Char('(') + QString::fromLocal8Bit(err) + QLatin1Char(')'): QString();
65 }
66 
suffixes_sys(const QString & fullVersion)67 QStringList QLibraryPrivate::suffixes_sys(const QString& fullVersion)
68 {
69     QStringList suffixes;
70 #if defined(Q_OS_HPUX)
71     // according to
72     // http://docs.hp.com/en/B2355-90968/linkerdifferencesiapa.htm
73 
74     // In PA-RISC (PA-32 and PA-64) shared libraries are suffixed
75     // with .sl. In IPF (32-bit and 64-bit), the shared libraries
76     // are suffixed with .so. For compatibility, the IPF linker
77     // also supports the .sl suffix.
78 
79     // But since we don't know if we are built on HPUX or HPUXi,
80     // we support both .sl (and .<version>) and .so suffixes but
81     // .so is preferred.
82 # if defined(__ia64)
83     if (!fullVersion.isEmpty()) {
84         suffixes << QLatin1String(".so.%1").arg(fullVersion);
85     } else {
86         suffixes << QLatin1String(".so");
87     }
88 # endif
89     if (!fullVersion.isEmpty()) {
90         suffixes << QLatin1String(".sl.%1").arg(fullVersion);
91         suffixes << QLatin1String(".%1").arg(fullVersion);
92     } else {
93         suffixes << QLatin1String(".sl");
94     }
95 #elif defined(Q_OS_AIX)
96     suffixes << ".a";
97 
98 #else
99     if (!fullVersion.isEmpty()) {
100         suffixes << QLatin1String(".so.%1").arg(fullVersion);
101     } else {
102         suffixes << QLatin1String(".so");
103 # ifdef Q_OS_ANDROID
104         suffixes << QStringLiteral(LIBS_SUFFIX);
105 # endif
106     }
107 #endif
108 # ifdef Q_OS_MAC
109     if (!fullVersion.isEmpty()) {
110         suffixes << QLatin1String(".%1.bundle").arg(fullVersion);
111         suffixes << QLatin1String(".%1.dylib").arg(fullVersion);
112     } else {
113         suffixes << QLatin1String(".bundle") << QLatin1String(".dylib");
114     }
115 #endif
116     return suffixes;
117 }
118 
prefixes_sys()119 QStringList QLibraryPrivate::prefixes_sys()
120 {
121     return QStringList() << QLatin1String("lib");
122 }
123 
load_sys()124 bool QLibraryPrivate::load_sys()
125 {
126     QMutexLocker locker(&mutex);
127     QString attempt;
128     QFileSystemEntry fsEntry(fileName);
129 
130     QString path = fsEntry.path();
131     QString name = fsEntry.fileName();
132     if (path == QLatin1String(".") && !fileName.startsWith(path))
133         path.clear();
134     else
135         path += QLatin1Char('/');
136 
137     QStringList suffixes;
138     QStringList prefixes;
139     if (pluginState != IsAPlugin) {
140         prefixes = prefixes_sys();
141         suffixes = suffixes_sys(fullVersion);
142     }
143     int dlFlags = 0;
144     int loadHints = this->loadHints();
145     if (loadHints & QLibrary::ResolveAllSymbolsHint) {
146         dlFlags |= RTLD_NOW;
147     } else {
148         dlFlags |= RTLD_LAZY;
149     }
150     if (loadHints & QLibrary::ExportExternalSymbolsHint) {
151         dlFlags |= RTLD_GLOBAL;
152     }
153 #if !defined(Q_OS_CYGWIN)
154     else {
155         dlFlags |= RTLD_LOCAL;
156     }
157 #endif
158 #if defined(RTLD_DEEPBIND)
159     if (loadHints & QLibrary::DeepBindHint)
160         dlFlags |= RTLD_DEEPBIND;
161 #endif
162 
163     // Provide access to RTLD_NODELETE flag on Unix
164     // From GNU documentation on RTLD_NODELETE:
165     // Do not unload the library during dlclose(). Consequently, the
166     // library's specific static variables are not reinitialized if the
167     // library is reloaded with dlopen() at a later time.
168 #if defined(RTLD_NODELETE)
169     if (loadHints & QLibrary::PreventUnloadHint) {
170 #   ifdef Q_OS_ANDROID // RTLD_NODELETE flag is supported by Android 23+
171         if (QtAndroidPrivate::androidSdkVersion() > 22)
172 #   endif
173             dlFlags |= RTLD_NODELETE;
174     }
175 #endif
176 
177 #if defined(Q_OS_AIX)   // Not sure if any other platform actually support this thing.
178     if (loadHints & QLibrary::LoadArchiveMemberHint) {
179         dlFlags |= RTLD_MEMBER;
180     }
181 #endif
182 
183     // If the filename is an absolute path then we want to try that first as it is most likely
184     // what the callee wants. If we have been given a non-absolute path then lets try the
185     // native library name first to avoid unnecessary calls to dlopen().
186     if (fsEntry.isAbsolute()) {
187         suffixes.prepend(QString());
188         prefixes.prepend(QString());
189     } else {
190         suffixes.append(QString());
191         prefixes.append(QString());
192     }
193 
194 #if defined(Q_PROCESSOR_X86) && !defined(Q_OS_DARWIN)
195     if (qCpuHasFeature(ArchHaswell)) {
196         auto transform = [](QStringList &list, void (*f)(QString *)) {
197             QStringList tmp;
198             qSwap(tmp, list);
199             list.reserve(tmp.size() * 2);
200             for (const QString &s : qAsConst(tmp)) {
201                 QString modifiedPath = s;
202                 f(&modifiedPath);
203                 list.append(modifiedPath);
204                 list.append(s);
205             }
206         };
207         if (pluginState == IsAPlugin) {
208             // add ".avx2" to each suffix in the list
209             transform(suffixes, [](QString *s) { s->append(QLatin1String(".avx2")); });
210         } else {
211             // prepend "haswell/" to each prefix in the list
212             transform(prefixes, [](QString *s) { s->prepend(QLatin1String("haswell/")); });
213         }
214     }
215 #endif
216 
217     locker.unlock();
218     bool retry = true;
219     Handle hnd = nullptr;
220     for (int prefix = 0; retry && !hnd && prefix < prefixes.size(); prefix++) {
221         for (int suffix = 0; retry && !hnd && suffix < suffixes.size(); suffix++) {
222             if (!prefixes.at(prefix).isEmpty() && name.startsWith(prefixes.at(prefix)))
223                 continue;
224             if (path.isEmpty() && prefixes.at(prefix).contains(QLatin1Char('/')))
225                 continue;
226             if (!suffixes.at(suffix).isEmpty() && name.endsWith(suffixes.at(suffix)))
227                 continue;
228             if (loadHints & QLibrary::LoadArchiveMemberHint) {
229                 attempt = name;
230                 int lparen = attempt.indexOf(QLatin1Char('('));
231                 if (lparen == -1)
232                     lparen = attempt.count();
233                 attempt = path + prefixes.at(prefix) + attempt.insert(lparen, suffixes.at(suffix));
234             } else {
235                 attempt = path + prefixes.at(prefix) + name + suffixes.at(suffix);
236             }
237 
238             hnd = dlopen(QFile::encodeName(attempt), dlFlags);
239 #ifdef Q_OS_ANDROID
240             if (!hnd) {
241                 auto attemptFromBundle = attempt;
242                 hnd = dlopen(QFile::encodeName(attemptFromBundle.replace(QLatin1Char('/'), QLatin1Char('_'))), dlFlags);
243             }
244             if (hnd) {
245                 using JniOnLoadPtr = jint (*)(JavaVM *vm, void *reserved);
246                 JniOnLoadPtr jniOnLoad = reinterpret_cast<JniOnLoadPtr>(dlsym(hnd, "JNI_OnLoad"));
247                 if (jniOnLoad && jniOnLoad(QtAndroidPrivate::javaVM(), nullptr) == JNI_ERR) {
248                     dlclose(hnd);
249                     hnd = nullptr;
250                 }
251             }
252 #endif
253 
254             if (!hnd && fileName.startsWith(QLatin1Char('/')) && QFile::exists(attempt)) {
255                 // We only want to continue if dlopen failed due to that the shared library did not exist.
256                 // However, we are only able to apply this check for absolute filenames (since they are
257                 // not influenced by the content of LD_LIBRARY_PATH, /etc/ld.so.cache, DT_RPATH etc...)
258                 // This is all because dlerror is flawed and cannot tell us the reason why it failed.
259                 retry = false;
260             }
261         }
262     }
263 
264 #ifdef Q_OS_MAC
265     if (!hnd) {
266         QByteArray utf8Bundle = fileName.toUtf8();
267         QCFType<CFURLRef> bundleUrl = CFURLCreateFromFileSystemRepresentation(NULL, reinterpret_cast<const UInt8*>(utf8Bundle.data()), utf8Bundle.length(), true);
268         QCFType<CFBundleRef> bundle = CFBundleCreate(NULL, bundleUrl);
269         if(bundle) {
270             QCFType<CFURLRef> url = CFBundleCopyExecutableURL(bundle);
271             char executableFile[FILENAME_MAX];
272             CFURLGetFileSystemRepresentation(url, true, reinterpret_cast<UInt8*>(executableFile), FILENAME_MAX);
273             attempt = QString::fromUtf8(executableFile);
274             hnd = dlopen(QFile::encodeName(attempt), dlFlags);
275         }
276     }
277 #endif
278 
279     locker.relock();
280     if (!hnd) {
281         errorString = QLibrary::tr("Cannot load library %1: %2").arg(fileName, qdlerror());
282     }
283     if (hnd) {
284         qualifiedFileName = attempt;
285         errorString.clear();
286     }
287     pHnd.storeRelaxed(hnd);
288     return (hnd != nullptr);
289 }
290 
unload_sys()291 bool QLibraryPrivate::unload_sys()
292 {
293     if (dlclose(pHnd.loadAcquire())) {
294 #if defined (Q_OS_QNX)                // Workaround until fixed in QNX; fixes crash in
295         char *error = dlerror();      // QtDeclarative auto test "qqmlenginecleanup" for instance
296         if (!qstrcmp(error, "Shared objects still referenced")) // On QNX that's only "informative"
297             return true;
298         errorString = QLibrary::tr("Cannot unload library %1: %2").arg(fileName,
299                                                                        QLatin1String(error));
300 #else
301         errorString = QLibrary::tr("Cannot unload library %1: %2").arg(fileName, qdlerror());
302 #endif
303         return false;
304     }
305     errorString.clear();
306     return true;
307 }
308 
309 #if defined(Q_OS_LINUX)
qt_linux_find_symbol_sys(const char * symbol)310 Q_CORE_EXPORT QFunctionPointer qt_linux_find_symbol_sys(const char *symbol)
311 {
312     return QFunctionPointer(dlsym(RTLD_DEFAULT, symbol));
313 }
314 #endif
315 
316 #ifdef Q_OS_MAC
qt_mac_resolve_sys(void * handle,const char * symbol)317 Q_CORE_EXPORT QFunctionPointer qt_mac_resolve_sys(void *handle, const char *symbol)
318 {
319     return QFunctionPointer(dlsym(handle, symbol));
320 }
321 #endif
322 
resolve_sys(const char * symbol)323 QFunctionPointer QLibraryPrivate::resolve_sys(const char* symbol)
324 {
325     QFunctionPointer address = QFunctionPointer(dlsym(pHnd.loadAcquire(), symbol));
326     return address;
327 }
328 
329 QT_END_NAMESPACE
330