diff options
author | Joerg Bornemann <joerg.bornemann@qt.io> | 2021-11-18 14:58:02 +0100 |
---|---|---|
committer | Joerg Bornemann <joerg.bornemann@qt.io> | 2021-11-26 18:10:44 +0100 |
commit | 4d8ca0d6c3c15ab0b92cbc4b8913a0f092fd8bdd (patch) | |
tree | edaee42b4dfdd13d345fa6b64c6a19bb726c2769 /src/shared/winutils/utils.cpp | |
parent | b6c369549425a660a51a3dc06c19f55f9a72076f (diff) |
Remove macdeployqt and windeployqt
These tools have been moved to qtbase.
Change-Id: Idaf799d44be399040f9058252675c0ee3eecc39b
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io>
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Iikka Eklund <iikka.eklund@qt.io>
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
Diffstat (limited to 'src/shared/winutils/utils.cpp')
-rw-r--r-- | src/shared/winutils/utils.cpp | 1006 |
1 files changed, 0 insertions, 1006 deletions
diff --git a/src/shared/winutils/utils.cpp b/src/shared/winutils/utils.cpp deleted file mode 100644 index bc52de924..000000000 --- a/src/shared/winutils/utils.cpp +++ /dev/null @@ -1,1006 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the tools applications of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "utils.h" -#include "elfreader.h" - -#include <QtCore/QString> -#include <QtCore/QDebug> -#include <QtCore/QDir> -#include <QtCore/QFile> -#include <QtCore/QFileInfo> -#include <QtCore/QTemporaryFile> -#include <QtCore/QScopedPointer> -#include <QtCore/QScopedArrayPointer> -#include <QtCore/QStandardPaths> -#if defined(Q_OS_WIN) -# include <QtCore/qt_windows.h> -# include <shlwapi.h> -# include <delayimp.h> -#else // Q_OS_WIN -# include <sys/wait.h> -# include <sys/types.h> -# include <sys/stat.h> -# include <unistd.h> -# include <stdlib.h> -# include <string.h> -# include <errno.h> -# include <fcntl.h> -#endif // !Q_OS_WIN - -QT_BEGIN_NAMESPACE - -int optVerboseLevel = 1; - -bool isBuildDirectory(Platform platform, const QString &dirName) -{ - return (platform.testFlag(Msvc) || platform.testFlag(ClangMsvc)) - && (dirName == QLatin1String("debug") || dirName == QLatin1String("release")); -} - -// Create a symbolic link by changing to the source directory to make sure the -// link uses relative paths only (QFile::link() otherwise uses the absolute path). -bool createSymbolicLink(const QFileInfo &source, const QString &target, QString *errorMessage) -{ - const QString oldDirectory = QDir::currentPath(); - if (!QDir::setCurrent(source.absolutePath())) { - *errorMessage = QStringLiteral("Unable to change to directory %1.").arg(QDir::toNativeSeparators(source.absolutePath())); - return false; - } - QFile file(source.fileName()); - const bool success = file.link(target); - QDir::setCurrent(oldDirectory); - if (!success) { - *errorMessage = QString::fromLatin1("Failed to create symbolic link %1 -> %2: %3") - .arg(QDir::toNativeSeparators(source.absoluteFilePath()), - QDir::toNativeSeparators(target), file.errorString()); - return false; - } - return true; -} - -bool createDirectory(const QString &directory, QString *errorMessage) -{ - const QFileInfo fi(directory); - if (fi.isDir()) - return true; - if (fi.exists()) { - *errorMessage = QString::fromLatin1("%1 already exists and is not a directory."). - arg(QDir::toNativeSeparators(directory)); - return false; - } - if (optVerboseLevel) - std::wcout << "Creating " << QDir::toNativeSeparators(directory) << "...\n"; - QDir dir; - if (!dir.mkpath(directory)) { - *errorMessage = QString::fromLatin1("Could not create directory %1."). - arg(QDir::toNativeSeparators(directory)); - return false; - } - return true; -} - -// Find shared libraries matching debug/Platform in a directory, return relative names. -QStringList findSharedLibraries(const QDir &directory, Platform platform, - DebugMatchMode debugMatchMode, - const QString &prefix) -{ - QString nameFilter = prefix; - if (nameFilter.isEmpty()) - nameFilter += QLatin1Char('*'); - if (debugMatchMode == MatchDebug && platformHasDebugSuffix(platform)) - nameFilter += QLatin1Char('d'); - nameFilter += sharedLibrarySuffix(platform); - QStringList result; - QString errorMessage; - const QFileInfoList &dlls = directory.entryInfoList(QStringList(nameFilter), QDir::Files); - for (const QFileInfo &dllFi : dlls) { - const QString dllPath = dllFi.absoluteFilePath(); - bool matches = true; - if (debugMatchMode != MatchDebugOrRelease && (platform & WindowsBased)) { - bool debugDll; - if (readPeExecutable(dllPath, &errorMessage, 0, 0, &debugDll, - (platform == WindowsDesktopMinGW))) { - matches = debugDll == (debugMatchMode == MatchDebug); - } else { - std::wcerr << "Warning: Unable to read " << QDir::toNativeSeparators(dllPath) - << ": " << errorMessage; - } - } // Windows - if (matches) - result += dllFi.fileName(); - } // for - return result; -} - -#ifdef Q_OS_WIN -QString winErrorMessage(unsigned long error) -{ - QString rc = QString::fromLatin1("#%1: ").arg(error); - char16_t *lpMsgBuf; - - const DWORD len = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, error, 0, reinterpret_cast<LPTSTR>(&lpMsgBuf), 0, NULL); - if (len) { - rc = QString::fromUtf16(lpMsgBuf, int(len)); - LocalFree(lpMsgBuf); - } else { - rc += QString::fromLatin1("<unknown error>"); - } - return rc; -} - -// Case-Normalize file name via GetShortPathNameW()/GetLongPathNameW() -QString normalizeFileName(const QString &name) -{ - wchar_t shortBuffer[MAX_PATH]; - const QString nativeFileName = QDir::toNativeSeparators(name); - if (!GetShortPathNameW(reinterpret_cast<LPCWSTR>(nativeFileName.utf16()), shortBuffer, MAX_PATH)) - return name; - wchar_t result[MAX_PATH]; - if (!GetLongPathNameW(shortBuffer, result, MAX_PATH)) - return name; - return QDir::fromNativeSeparators(QString::fromWCharArray(result)); -} - -// Find a tool binary in the Windows SDK 8 -QString findSdkTool(const QString &tool) -{ - QStringList paths = QString::fromLocal8Bit(qgetenv("PATH")).split(QLatin1Char(';')); - const QByteArray sdkDir = qgetenv("WindowsSdkDir"); - if (!sdkDir.isEmpty()) - paths.prepend(QDir::cleanPath(QString::fromLocal8Bit(sdkDir)) + QLatin1String("/Tools/x64")); - return QStandardPaths::findExecutable(tool, paths); -} - -// runProcess helper: Create a temporary file for stdout/stderr redirection. -static HANDLE createInheritableTemporaryFile() -{ - wchar_t path[MAX_PATH]; - if (!GetTempPath(MAX_PATH, path)) - return INVALID_HANDLE_VALUE; - wchar_t name[MAX_PATH]; - if (!GetTempFileName(path, L"temp", 0, name)) // Creates file. - return INVALID_HANDLE_VALUE; - SECURITY_ATTRIBUTES securityAttributes; - ZeroMemory(&securityAttributes, sizeof(securityAttributes)); - securityAttributes.nLength = sizeof(securityAttributes); - securityAttributes.bInheritHandle = TRUE; - return CreateFile(name, GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, &securityAttributes, - TRUNCATE_EXISTING, - FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL); -} - -// runProcess helper: Rewind and read out a temporary file for stdout/stderr. -static inline void readTemporaryProcessFile(HANDLE handle, QByteArray *result) -{ - if (SetFilePointer(handle, 0, 0, FILE_BEGIN) == 0xFFFFFFFF) - return; - char buf[1024]; - DWORD bytesRead; - while (ReadFile(handle, buf, sizeof(buf), &bytesRead, NULL) && bytesRead) - result->append(buf, int(bytesRead)); - CloseHandle(handle); -} - -static inline void appendToCommandLine(const QString &argument, QString *commandLine) -{ - const bool needsQuote = argument.contains(QLatin1Char(' ')); - if (!commandLine->isEmpty()) - commandLine->append(QLatin1Char(' ')); - if (needsQuote) - commandLine->append(QLatin1Char('"')); - commandLine->append(argument); - if (needsQuote) - commandLine->append(QLatin1Char('"')); -} - -// runProcess: Run a command line process (replacement for QProcess which -// does not exist in the bootstrap library). -bool runProcess(const QString &binary, const QStringList &args, - const QString &workingDirectory, - unsigned long *exitCode, QByteArray *stdOut, QByteArray *stdErr, - QString *errorMessage) -{ - if (exitCode) - *exitCode = 0; - - STARTUPINFO si; - ZeroMemory(&si, sizeof(si)); - si.cb = sizeof(si); - - STARTUPINFO myInfo; - GetStartupInfo(&myInfo); - si.hStdInput = myInfo.hStdInput; - si.hStdOutput = myInfo.hStdOutput; - si.hStdError = myInfo.hStdError; - - PROCESS_INFORMATION pi; - ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); - const QChar backSlash = QLatin1Char('\\'); - QString nativeWorkingDir = QDir::toNativeSeparators(workingDirectory.isEmpty() ? QDir::currentPath() : workingDirectory); - if (!nativeWorkingDir.endsWith(backSlash)) - nativeWorkingDir += backSlash; - - if (stdOut) { - si.hStdOutput = createInheritableTemporaryFile(); - if (si.hStdOutput == INVALID_HANDLE_VALUE) { - if (errorMessage) - *errorMessage = QStringLiteral("Error creating stdout temporary file"); - return false; - } - si.dwFlags |= STARTF_USESTDHANDLES; - } - - if (stdErr) { - si.hStdError = createInheritableTemporaryFile(); - if (si.hStdError == INVALID_HANDLE_VALUE) { - if (errorMessage) - *errorMessage = QStringLiteral("Error creating stderr temporary file"); - return false; - } - si.dwFlags |= STARTF_USESTDHANDLES; - } - - // Create a copy of the command line which CreateProcessW can modify. - QString commandLine; - appendToCommandLine(binary, &commandLine); - for (const QString &a : args) - appendToCommandLine(a, &commandLine); - if (optVerboseLevel > 1) - std::wcout << "Running: " << commandLine << '\n'; - - QScopedArrayPointer<wchar_t> commandLineW(new wchar_t[commandLine.size() + 1]); - commandLine.toWCharArray(commandLineW.data()); - commandLineW[commandLine.size()] = 0; - if (!CreateProcessW(0, commandLineW.data(), 0, 0, /* InheritHandles */ TRUE, 0, 0, - reinterpret_cast<LPCWSTR>(nativeWorkingDir.utf16()), &si, &pi)) { - if (stdOut) - CloseHandle(si.hStdOutput); - if (stdErr) - CloseHandle(si.hStdError); - if (errorMessage) - *errorMessage = QStringLiteral("CreateProcessW failed: ") + winErrorMessage(GetLastError()); - return false; - } - - WaitForSingleObject(pi.hProcess, INFINITE); - CloseHandle(pi.hThread); - if (exitCode) - GetExitCodeProcess(pi.hProcess, exitCode); - CloseHandle(pi.hProcess); - - if (stdOut) - readTemporaryProcessFile(si.hStdOutput, stdOut); - if (stdErr) - readTemporaryProcessFile(si.hStdError, stdErr); - return true; -} - -#else // Q_OS_WIN - -static inline char *encodeFileName(const QString &f) -{ - const QByteArray encoded = QFile::encodeName(f); - char *result = new char[encoded.size() + 1]; - strcpy(result, encoded.constData()); - return result; -} - -static inline char *tempFilePattern() -{ - QString path = QDir::tempPath(); - if (!path.endsWith(QLatin1Char('/'))) - path += QLatin1Char('/'); - path += QStringLiteral("tmpXXXXXX"); - return encodeFileName(path); -} - -static inline QByteArray readOutRedirectFile(int fd) -{ - enum { bufSize = 256 }; - - QByteArray result; - if (!lseek(fd, 0, 0)) { - char buf[bufSize]; - while (true) { - const ssize_t rs = read(fd, buf, bufSize); - if (rs <= 0) - break; - result.append(buf, int(rs)); - } - } - close(fd); - return result; -} - -// runProcess: Run a command line process (replacement for QProcess which -// does not exist in the bootstrap library). -bool runProcess(const QString &binary, const QStringList &args, - const QString &workingDirectory, - unsigned long *exitCode, QByteArray *stdOut, QByteArray *stdErr, - QString *errorMessage) -{ - QScopedArrayPointer<char> stdOutFileName; - QScopedArrayPointer<char> stdErrFileName; - - int stdOutFile = 0; - if (stdOut) { - stdOutFileName.reset(tempFilePattern()); - stdOutFile = mkstemp(stdOutFileName.data()); - if (stdOutFile < 0) { - *errorMessage = QStringLiteral("mkstemp() failed: ") + QString::fromLocal8Bit(strerror(errno)); - return false; - } - } - - int stdErrFile = 0; - if (stdErr) { - stdErrFileName.reset(tempFilePattern()); - stdErrFile = mkstemp(stdErrFileName.data()); - if (stdErrFile < 0) { - *errorMessage = QStringLiteral("mkstemp() failed: ") + QString::fromLocal8Bit(strerror(errno)); - return false; - } - } - - const pid_t pID = fork(); - - if (pID < 0) { - *errorMessage = QStringLiteral("Fork failed: ") + QString::fromLocal8Bit(strerror(errno)); - return false; - } - - if (!pID) { // Child - if (stdOut) { - dup2(stdOutFile, STDOUT_FILENO); - close(stdOutFile); - } - if (stdErr) { - dup2(stdErrFile, STDERR_FILENO); - close(stdErrFile); - } - - if (!workingDirectory.isEmpty() && !QDir::setCurrent(workingDirectory)) { - std::wcerr << "Failed to change working directory to " << workingDirectory << ".\n"; - ::_exit(-1); - } - - char **argv = new char *[args.size() + 2]; // Create argv. - char **ap = argv; - *ap++ = encodeFileName(binary); - for (const QString &a : qAsConst(args)) - *ap++ = encodeFileName(a); - *ap = 0; - - execvp(argv[0], argv); - ::_exit(-1); - } - - int status; - pid_t waitResult; - - do { - waitResult = waitpid(pID, &status, 0); - } while (waitResult == -1 && errno == EINTR); - - if (stdOut) { - *stdOut = readOutRedirectFile(stdOutFile); - unlink(stdOutFileName.data()); - } - if (stdErr) { - *stdErr = readOutRedirectFile(stdErrFile); - unlink(stdErrFileName.data()); - } - - if (waitResult < 0) { - *errorMessage = QStringLiteral("Wait failed: ") + QString::fromLocal8Bit(strerror(errno)); - return false; - } - if (!WIFEXITED(status)) { - *errorMessage = binary + QStringLiteral(" did not exit cleanly."); - return false; - } - if (exitCode) - *exitCode = WEXITSTATUS(status); - return true; -} - -#endif // !Q_OS_WIN - -// Find a file in the path using ShellAPI. This can be used to locate DLLs which -// QStandardPaths cannot do. -QString findInPath(const QString &file) -{ -#if defined(Q_OS_WIN) - if (file.size() < MAX_PATH - 1) { - wchar_t buffer[MAX_PATH]; - file.toWCharArray(buffer); - buffer[file.size()] = 0; - if (PathFindOnPath(buffer, NULL)) - return QDir::cleanPath(QString::fromWCharArray(buffer)); - } - return QString(); -#else // Q_OS_WIN - return QStandardPaths::findExecutable(file); -#endif // !Q_OS_WIN -} - -const char *qmakeInfixKey = "QT_INFIX"; - -QMap<QString, QString> queryQtPaths(const QString &qtpathsBinary, QString *errorMessage) -{ - const QString binary = !qtpathsBinary.isEmpty() ? qtpathsBinary : QStringLiteral("qtpaths"); - QByteArray stdOut; - QByteArray stdErr; - unsigned long exitCode = 0; - if (!runProcess(binary, QStringList(QStringLiteral("-query")), QString(), &exitCode, &stdOut, &stdErr, errorMessage)) - return QMap<QString, QString>(); - if (exitCode) { - *errorMessage = binary + QStringLiteral(" returns ") + QString::number(exitCode) - + QStringLiteral(": ") + QString::fromLocal8Bit(stdErr); - return QMap<QString, QString>(); - } - const QString output = QString::fromLocal8Bit(stdOut).trimmed().remove(QLatin1Char('\r')); - QMap<QString, QString> result; - const int size = output.size(); - for (int pos = 0; pos < size; ) { - const int colonPos = output.indexOf(QLatin1Char(':'), pos); - if (colonPos < 0) - break; - int endPos = output.indexOf(QLatin1Char('\n'), colonPos + 1); - if (endPos < 0) - endPos = size; - const QString key = output.mid(pos, colonPos - pos); - const QString value = output.mid(colonPos + 1, endPos - colonPos - 1); - result.insert(key, value); - pos = endPos + 1; - } - QFile qconfigPriFile(result.value(QStringLiteral("QT_HOST_DATA")) + QStringLiteral("/mkspecs/qconfig.pri")); - if (qconfigPriFile.open(QIODevice::ReadOnly | QIODevice::Text)) { - while (true) { - const QByteArray line = qconfigPriFile.readLine(); - if (line.isEmpty()) - break; - if (line.startsWith("QT_LIBINFIX")) { - const int pos = line.indexOf('='); - if (pos >= 0) { - const QString infix = QString::fromUtf8(line.right(line.size() - pos - 1).trimmed()); - if (!infix.isEmpty()) - result.insert(QLatin1String(qmakeInfixKey), infix); - } - break; - } - } - } else { - std::wcerr << "Warning: Unable to read " << QDir::toNativeSeparators(qconfigPriFile.fileName()) - << ": " << qconfigPriFile.errorString()<< '\n'; - } - return result; -} - -// Update a file or directory. -bool updateFile(const QString &sourceFileName, const QStringList &nameFilters, - const QString &targetDirectory, unsigned flags, JsonOutput *json, QString *errorMessage) -{ - const QFileInfo sourceFileInfo(sourceFileName); - const QString targetFileName = targetDirectory + QLatin1Char('/') + sourceFileInfo.fileName(); - if (optVerboseLevel > 1) - std::wcout << "Checking " << sourceFileName << ", " << targetFileName<< '\n'; - - if (!sourceFileInfo.exists()) { - *errorMessage = QString::fromLatin1("%1 does not exist.").arg(QDir::toNativeSeparators(sourceFileName)); - return false; - } - - if (sourceFileInfo.isSymLink()) { - *errorMessage = QString::fromLatin1("Symbolic links are not supported (%1).") - .arg(QDir::toNativeSeparators(sourceFileName)); - return false; - } - - const QFileInfo targetFileInfo(targetFileName); - - if (sourceFileInfo.isDir()) { - if (targetFileInfo.exists()) { - if (!targetFileInfo.isDir()) { - *errorMessage = QString::fromLatin1("%1 already exists and is not a directory.") - .arg(QDir::toNativeSeparators(targetFileName)); - return false; - } // Not a directory. - } else { // exists. - QDir d(targetDirectory); - if (optVerboseLevel) - std::wcout << "Creating " << QDir::toNativeSeparators(targetFileName) << ".\n"; - if (!(flags & SkipUpdateFile) && !d.mkdir(sourceFileInfo.fileName())) { - *errorMessage = QString::fromLatin1("Cannot create directory %1 under %2.") - .arg(sourceFileInfo.fileName(), QDir::toNativeSeparators(targetDirectory)); - return false; - } - } - // Recurse into directory - QDir dir(sourceFileName); - const QFileInfoList allEntries = dir.entryInfoList(nameFilters, QDir::Files) - + dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); - for (const QFileInfo &entryFi : allEntries) { - if (!updateFile(entryFi.absoluteFilePath(), nameFilters, targetFileName, flags, json, errorMessage)) - return false; - } - return true; - } // Source is directory. - - if (targetFileInfo.exists()) { - if (!(flags & ForceUpdateFile) - && targetFileInfo.lastModified() >= sourceFileInfo.lastModified()) { - if (optVerboseLevel) - std::wcout << sourceFileInfo.fileName() << " is up to date.\n"; - if (json) - json->addFile(sourceFileName, targetDirectory); - return true; - } - QFile targetFile(targetFileName); - if (!(flags & SkipUpdateFile) && !targetFile.remove()) { - *errorMessage = QString::fromLatin1("Cannot remove existing file %1: %2") - .arg(QDir::toNativeSeparators(targetFileName), targetFile.errorString()); - return false; - } - } // target exists - QFile file(sourceFileName); - if (optVerboseLevel) - std::wcout << "Updating " << sourceFileInfo.fileName() << ".\n"; - if (!(flags & SkipUpdateFile) && !file.copy(targetFileName)) { - *errorMessage = QString::fromLatin1("Cannot copy %1 to %2: %3") - .arg(QDir::toNativeSeparators(sourceFileName), - QDir::toNativeSeparators(targetFileName), - file.errorString()); - return false; - } - if (json) - json->addFile(sourceFileName, targetDirectory); - return true; -} - -bool readElfExecutable(const QString &elfExecutableFileName, QString *errorMessage, - QStringList *dependentLibraries, unsigned *wordSize, - bool *isDebug) -{ - ElfReader elfReader(elfExecutableFileName); - const ElfData data = elfReader.readHeaders(); - if (data.sectionHeaders.isEmpty()) { - *errorMessage = QStringLiteral("Unable to read ELF binary \"") - + QDir::toNativeSeparators(elfExecutableFileName) + QStringLiteral("\": ") - + elfReader.errorString(); - return false; - } - if (wordSize) - *wordSize = data.elfclass == Elf_ELFCLASS64 ? 64 : 32; - if (dependentLibraries) { - dependentLibraries->clear(); - const QList<QByteArray> libs = elfReader.dependencies(); - if (libs.isEmpty()) { - *errorMessage = QStringLiteral("Unable to read dependenices of ELF binary \"") - + QDir::toNativeSeparators(elfExecutableFileName) + QStringLiteral("\": ") - + elfReader.errorString(); - return false; - } - for (const QByteArray &l : libs) - dependentLibraries->push_back(QString::fromLocal8Bit(l)); - } - if (isDebug) - *isDebug = data.symbolsType != UnknownSymbols && data.symbolsType != NoSymbols; - return true; -} - -#ifdef Q_OS_WIN - -static inline QString stringFromRvaPtr(const void *rvaPtr) -{ - return QString::fromLocal8Bit(static_cast<const char *>(rvaPtr)); -} - -// Helper for reading out PE executable files: Find a section header for an RVA -// (IMAGE_NT_HEADERS64, IMAGE_NT_HEADERS32). -template <class ImageNtHeader> -const IMAGE_SECTION_HEADER *findSectionHeader(DWORD rva, const ImageNtHeader *nTHeader) -{ - const IMAGE_SECTION_HEADER *section = IMAGE_FIRST_SECTION(nTHeader); - const IMAGE_SECTION_HEADER *sectionEnd = section + nTHeader->FileHeader.NumberOfSections; - for ( ; section < sectionEnd; ++section) - if (rva >= section->VirtualAddress && rva < (section->VirtualAddress + section->Misc.VirtualSize)) - return section; - return 0; -} - -// Helper for reading out PE executable files: convert RVA to pointer (IMAGE_NT_HEADERS64, IMAGE_NT_HEADERS32). -template <class ImageNtHeader> -inline const void *rvaToPtr(DWORD rva, const ImageNtHeader *nTHeader, const void *imageBase) -{ - const IMAGE_SECTION_HEADER *sectionHdr = findSectionHeader(rva, nTHeader); - if (!sectionHdr) - return 0; - const DWORD delta = sectionHdr->VirtualAddress - sectionHdr->PointerToRawData; - return static_cast<const char *>(imageBase) + rva - delta; -} - -// Helper for reading out PE executable files: return word size of a IMAGE_NT_HEADERS64, IMAGE_NT_HEADERS32 -template <class ImageNtHeader> -inline unsigned ntHeaderWordSize(const ImageNtHeader *header) -{ - // defines IMAGE_NT_OPTIONAL_HDR32_MAGIC, IMAGE_NT_OPTIONAL_HDR64_MAGIC - enum { imageNtOptionlHeader32Magic = 0x10b, imageNtOptionlHeader64Magic = 0x20b }; - if (header->OptionalHeader.Magic == imageNtOptionlHeader32Magic) - return 32; - if (header->OptionalHeader.Magic == imageNtOptionlHeader64Magic) - return 64; - return 0; -} - -// Helper for reading out PE executable files: Retrieve the NT image header of an -// executable via the legacy DOS header. -static IMAGE_NT_HEADERS *getNtHeader(void *fileMemory, QString *errorMessage) -{ - IMAGE_DOS_HEADER *dosHeader = static_cast<PIMAGE_DOS_HEADER>(fileMemory); - // Check DOS header consistency - if (IsBadReadPtr(dosHeader, sizeof(IMAGE_DOS_HEADER)) - || dosHeader->e_magic != IMAGE_DOS_SIGNATURE) { - *errorMessage = QString::fromLatin1("DOS header check failed."); - return 0; - } - // Retrieve NT header - char *ntHeaderC = static_cast<char *>(fileMemory) + dosHeader->e_lfanew; - IMAGE_NT_HEADERS *ntHeaders = reinterpret_cast<IMAGE_NT_HEADERS *>(ntHeaderC); - // check NT header consistency - if (IsBadReadPtr(ntHeaders, sizeof(ntHeaders->Signature)) - || ntHeaders->Signature != IMAGE_NT_SIGNATURE - || IsBadReadPtr(&ntHeaders->FileHeader, sizeof(IMAGE_FILE_HEADER))) { - *errorMessage = QString::fromLatin1("NT header check failed."); - return 0; - } - // Check magic - if (!ntHeaderWordSize(ntHeaders)) { - *errorMessage = QString::fromLatin1("NT header check failed; magic %1 is invalid."). - arg(ntHeaders->OptionalHeader.Magic); - return 0; - } - // Check section headers - IMAGE_SECTION_HEADER *sectionHeaders = IMAGE_FIRST_SECTION(ntHeaders); - if (IsBadReadPtr(sectionHeaders, ntHeaders->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER))) { - *errorMessage = QString::fromLatin1("NT header section header check failed."); - return 0; - } - return ntHeaders; -} - -// Helper for reading out PE executable files: Read out import sections from -// IMAGE_NT_HEADERS64, IMAGE_NT_HEADERS32. -template <class ImageNtHeader> -inline QStringList readImportSections(const ImageNtHeader *ntHeaders, const void *base, QString *errorMessage) -{ - // Get import directory entry RVA and read out - const DWORD importsStartRVA = ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; - if (!importsStartRVA) { - *errorMessage = QString::fromLatin1("Failed to find IMAGE_DIRECTORY_ENTRY_IMPORT entry."); - return QStringList(); - } - const IMAGE_IMPORT_DESCRIPTOR *importDesc = static_cast<const IMAGE_IMPORT_DESCRIPTOR *>(rvaToPtr(importsStartRVA, ntHeaders, base)); - if (!importDesc) { - *errorMessage = QString::fromLatin1("Failed to find IMAGE_IMPORT_DESCRIPTOR entry."); - return QStringList(); - } - QStringList result; - for ( ; importDesc->Name; ++importDesc) - result.push_back(stringFromRvaPtr(rvaToPtr(importDesc->Name, ntHeaders, base))); - - // Read delay-loaded DLLs, see http://msdn.microsoft.com/en-us/magazine/cc301808.aspx . - // Check on grAttr bit 1 whether this is the format using RVA's > VS 6 - if (const DWORD delayedImportsStartRVA = ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT].VirtualAddress) { - const ImgDelayDescr *delayedImportDesc = static_cast<const ImgDelayDescr *>(rvaToPtr(delayedImportsStartRVA, ntHeaders, base)); - for ( ; delayedImportDesc->rvaDLLName && (delayedImportDesc->grAttrs & 1); ++delayedImportDesc) - result.push_back(stringFromRvaPtr(rvaToPtr(delayedImportDesc->rvaDLLName, ntHeaders, base))); - } - - return result; -} - -// Check for MSCV runtime (MSVCP90D.dll/MSVCP90.dll, MSVCP120D.dll/MSVCP120.dll, -// VCRUNTIME140D.DLL/VCRUNTIME140.DLL (VS2015) or msvcp120d_app.dll/msvcp120_app.dll). -enum MsvcDebugRuntimeResult { MsvcDebugRuntime, MsvcReleaseRuntime, NoMsvcRuntime }; - -static inline MsvcDebugRuntimeResult checkMsvcDebugRuntime(const QStringList &dependentLibraries) -{ - for (const QString &lib : dependentLibraries) { - int pos = 0; - if (lib.startsWith(QLatin1String("MSVCR"), Qt::CaseInsensitive) - || lib.startsWith(QLatin1String("MSVCP"), Qt::CaseInsensitive) - || lib.startsWith(QLatin1String("VCRUNTIME"), Qt::CaseInsensitive)) { - int lastDotPos = lib.lastIndexOf(QLatin1Char('.')); - pos = -1 == lastDotPos ? 0 : lastDotPos - 1; - } - - if (pos > 0 && lib.contains(QLatin1String("_app"), Qt::CaseInsensitive)) - pos -= 4; - - if (pos) { - return lib.at(pos).toLower() == QLatin1Char('d') - ? MsvcDebugRuntime : MsvcReleaseRuntime; - } - } - return NoMsvcRuntime; -} - -template <class ImageNtHeader> -inline void determineDebugAndDependentLibs(const ImageNtHeader *nth, const void *fileMemory, - bool isMinGW, - QStringList *dependentLibrariesIn, - bool *isDebugIn, QString *errorMessage) -{ - const bool hasDebugEntry = nth->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size; - QStringList dependentLibraries; - if (dependentLibrariesIn || (isDebugIn != nullptr && hasDebugEntry && !isMinGW)) - dependentLibraries = readImportSections(nth, fileMemory, errorMessage); - - if (dependentLibrariesIn) - *dependentLibrariesIn = dependentLibraries; - if (isDebugIn != nullptr) { - if (isMinGW) { - // Use logic that's used e.g. in objdump / pfd library - *isDebugIn = !(nth->FileHeader.Characteristics & IMAGE_FILE_DEBUG_STRIPPED); - } else { - // When an MSVC debug entry is present, check whether the debug runtime - // is actually used to detect -release / -force-debug-info builds. - *isDebugIn = hasDebugEntry && checkMsvcDebugRuntime(dependentLibraries) != MsvcReleaseRuntime; - } - } -} - -// Read a PE executable and determine dependent libraries, word size -// and debug flags. -bool readPeExecutable(const QString &peExecutableFileName, QString *errorMessage, - QStringList *dependentLibrariesIn, unsigned *wordSizeIn, - bool *isDebugIn, bool isMinGW, unsigned short *machineArchIn) -{ - bool result = false; - HANDLE hFile = NULL; - HANDLE hFileMap = NULL; - void *fileMemory = 0; - - if (dependentLibrariesIn) - dependentLibrariesIn->clear(); - if (wordSizeIn) - *wordSizeIn = 0; - if (isDebugIn) - *isDebugIn = false; - - do { - // Create a memory mapping of the file - hFile = CreateFile(reinterpret_cast<const WCHAR*>(peExecutableFileName.utf16()), GENERIC_READ, FILE_SHARE_READ, NULL, - OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (hFile == INVALID_HANDLE_VALUE || hFile == NULL) { - *errorMessage = QString::fromLatin1("Cannot open '%1': %2").arg(peExecutableFileName, winErrorMessage(GetLastError())); - break; - } - - hFileMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); - if (hFileMap == NULL) { - *errorMessage = QString::fromLatin1("Cannot create file mapping of '%1': %2").arg(peExecutableFileName, winErrorMessage(GetLastError())); - break; - } - - fileMemory = MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 0); - if (!fileMemory) { - *errorMessage = QString::fromLatin1("Cannot map '%1': %2").arg(peExecutableFileName, winErrorMessage(GetLastError())); - break; - } - - const IMAGE_NT_HEADERS *ntHeaders = getNtHeader(fileMemory, errorMessage); - if (!ntHeaders) - break; - - const unsigned wordSize = ntHeaderWordSize(ntHeaders); - if (wordSizeIn) - *wordSizeIn = wordSize; - if (wordSize == 32) { - determineDebugAndDependentLibs(reinterpret_cast<const IMAGE_NT_HEADERS32 *>(ntHeaders), - fileMemory, isMinGW, dependentLibrariesIn, isDebugIn, errorMessage); - } else { - determineDebugAndDependentLibs(reinterpret_cast<const IMAGE_NT_HEADERS64 *>(ntHeaders), - fileMemory, isMinGW, dependentLibrariesIn, isDebugIn, errorMessage); - } - - if (machineArchIn) - *machineArchIn = ntHeaders->FileHeader.Machine; - - result = true; - if (optVerboseLevel > 1) { - std::wcout << __FUNCTION__ << ": " << QDir::toNativeSeparators(peExecutableFileName) - << ' ' << wordSize << " bit"; - if (isMinGW) - std::wcout << ", MinGW"; - if (dependentLibrariesIn) { - std::wcout << ", dependent libraries: "; - if (optVerboseLevel > 2) - std::wcout << dependentLibrariesIn->join(QLatin1Char(' ')); - else - std::wcout << dependentLibrariesIn->size(); - } - if (isDebugIn) - std::wcout << (*isDebugIn ? ", debug" : ", release"); - std::wcout << '\n'; - } - } while (false); - - if (fileMemory) - UnmapViewOfFile(fileMemory); - - if (hFileMap != NULL) - CloseHandle(hFileMap); - - if (hFile != NULL && hFile != INVALID_HANDLE_VALUE) - CloseHandle(hFile); - - return result; -} - -QString findD3dCompiler(Platform platform, const QString &qtBinDir, unsigned wordSize) -{ - const QString prefix = QStringLiteral("D3Dcompiler_"); - const QString suffix = QLatin1String(windowsSharedLibrarySuffix); - // Get the DLL from Kit 8.0 onwards - const QString kitDir = QString::fromLocal8Bit(qgetenv("WindowsSdkDir")); - if (!kitDir.isEmpty()) { - QString redistDirPath = QDir::cleanPath(kitDir) + QStringLiteral("/Redist/D3D/"); - if (platform.testFlag(ArmBased)) { - redistDirPath += QStringLiteral("arm"); - } else { - redistDirPath += wordSize == 32 ? QStringLiteral("x86") : QStringLiteral("x64"); - } - QDir redistDir(redistDirPath); - if (redistDir.exists()) { - const QFileInfoList files = redistDir.entryInfoList(QStringList(prefix + QLatin1Char('*') + suffix), QDir::Files); - if (!files.isEmpty()) - return files.front().absoluteFilePath(); - } - } - QStringList candidateVersions; - for (int i = 47 ; i >= 40 ; --i) - candidateVersions.append(prefix + QString::number(i) + suffix); - // Check the bin directory of the Qt SDK (in case it is shadowed by the - // Windows system directory in PATH). - for (const QString &candidate : qAsConst(candidateVersions)) { - const QFileInfo fi(qtBinDir + QLatin1Char('/') + candidate); - if (fi.isFile()) - return fi.absoluteFilePath(); - } - // Find the latest D3D compiler DLL in path (Windows 8.1 has d3dcompiler_47). - if (platform.testFlag(IntelBased)) { - QString errorMessage; - unsigned detectedWordSize; - for (const QString &candidate : qAsConst(candidateVersions)) { - const QString dll = findInPath(candidate); - if (!dll.isEmpty() - && readPeExecutable(dll, &errorMessage, 0, &detectedWordSize, 0) - && detectedWordSize == wordSize) { - return dll; - } - } - } - return QString(); -} - -#else // Q_OS_WIN - -bool readPeExecutable(const QString &, QString *errorMessage, - QStringList *, unsigned *, bool *, bool, unsigned short *) -{ - *errorMessage = QStringLiteral("Not implemented."); - return false; -} - -QString findD3dCompiler(Platform, const QString &, unsigned) -{ - return QString(); -} - -#endif // !Q_OS_WIN - -// Search for "qt_prfxpath=xxxx" in \a path, and replace it with "qt_prfxpath=." -bool patchQtCore(const QString &path, QString *errorMessage) -{ - if (optVerboseLevel) - std::wcout << "Patching " << QFileInfo(path).fileName() << "...\n"; - - QFile file(path); - if (!file.open(QIODevice::ReadOnly)) { - *errorMessage = QString::fromLatin1("Unable to patch %1: %2").arg( - QDir::toNativeSeparators(path), file.errorString()); - return false; - } - const QByteArray oldContent = file.readAll(); - - if (oldContent.isEmpty()) { - *errorMessage = QString::fromLatin1("Unable to patch %1: Could not read file content").arg( - QDir::toNativeSeparators(path)); - return false; - } - file.close(); - - QByteArray content = oldContent; - - QByteArray prfxpath("qt_prfxpath="); - int startPos = content.indexOf(prfxpath); - if (startPos == -1) { - *errorMessage = QString::fromLatin1( - "Unable to patch %1: Could not locate pattern \"qt_prfxpath=\"").arg( - QDir::toNativeSeparators(path)); - return false; - } - startPos += prfxpath.length(); - int endPos = content.indexOf(char(0), startPos); - if (endPos == -1) { - *errorMessage = QString::fromLatin1("Unable to patch %1: Internal error").arg( - QDir::toNativeSeparators(path)); - return false; - } - - QByteArray replacement = QByteArray(endPos - startPos, char(0)); - replacement[0] = '.'; - content.replace(startPos, endPos - startPos, replacement); - if (content == oldContent) - return true; - - if (!file.open(QIODevice::WriteOnly) - || (file.write(content) != content.size())) { - *errorMessage = QString::fromLatin1("Unable to patch %1: Could not write to file: %2").arg( - QDir::toNativeSeparators(path), file.errorString()); - return false; - } - return true; -} - -#ifdef Q_OS_WIN -QString getArchString(unsigned short machineArch) -{ - switch (machineArch) { - case IMAGE_FILE_MACHINE_I386: - return QStringLiteral("x86"); - case IMAGE_FILE_MACHINE_ARM: - return QStringLiteral("arm"); - case IMAGE_FILE_MACHINE_AMD64: - return QStringLiteral("x64"); - case IMAGE_FILE_MACHINE_ARM64: - return QStringLiteral("arm64"); - default: - break; - } - return QString(); -} -#endif // Q_OS_WIN - -QT_END_NAMESPACE |