8#include <qloggingcategory.h>
14#define WIN32_LEAN_AND_MEAN
16#include <qt_windows.h>
20using namespace Qt::StringLiterals;
30#ifdef QT_BUILD_INTERNAL
31# define QCOFFPEPARSER_DEBUG
33#if defined(QCOFFPEPARSER_DEBUG)
34Q_STATIC_LOGGING_CATEGORY(lcCoffPeParser,
"qt.core.plugin.coffpeparser")
35# define peDebug qCDebug(lcCoffPeParser) << reinterpret_cast<const char16_t *>(error.errMsg->constData()) << ':'
37# define peDebug if (false) {} else QNoDebug()
41QT_WARNING_DISABLE_CLANG(
"-Wunused-const-variable")
46#elif defined(Q_PROCESSOR_ARM_32)
47 IMAGE_FILE_MACHINE_ARMNT
48#elif defined(Q_PROCESSOR_ARM_64_EC)
49 IMAGE_FILE_MACHINE_AMD64
50#elif defined(Q_PROCESSOR_ARM_64)
51 IMAGE_FILE_MACHINE_ARM64
52#elif defined(Q_PROCESSOR_IA64)
53 IMAGE_FILE_MACHINE_IA64
54#elif defined(Q_PROCESSOR_RISCV_32)
56#elif defined(Q_PROCESSOR_RISCV_64)
58#elif defined(Q_PROCESSOR_X86_32)
59 IMAGE_FILE_MACHINE_I386
60#elif defined(Q_PROCESSOR_X86_64)
61 IMAGE_FILE_MACHINE_AMD64
63# error "Unknown Q_PROCESSOR_xxx macro, please update."
64 IMAGE_FILE_MACHINE_UNKNOWN
69 sizeof(
void*) ==
sizeof(quint64) ? IMAGE_NT_OPTIONAL_HDR64_MAGIC :
70 IMAGE_NT_OPTIONAL_HDR32_MAGIC;
76 constexpr ErrorMaker(QString *errMsg) : errMsg(errMsg) {}
78 Q_DECL_COLD_FUNCTION QLibraryScanResult operator()(QString &&text)
const
80 *errMsg = QLibrary::tr(
"'%1' is not a valid Windows DLL (%2)").arg(*errMsg, std::move(text));
84 Q_DECL_COLD_FUNCTION QLibraryScanResult toosmall()
const
86 *errMsg = QLibrary::tr(
"'%1' is too small").arg(*errMsg);
90 Q_DECL_COLD_FUNCTION QLibraryScanResult notplugin(QString &&explanation)
const
92 *errMsg = QLibrary::tr(
"'%1' is not a Qt plugin (%2)").arg(*errMsg, explanation);
96 Q_DECL_COLD_FUNCTION QLibraryScanResult notfound()
const
98 return notplugin(QLibrary::tr(
"metadata not found"));
102struct HeaderDebug {
const IMAGE_NT_HEADERS *h; };
103Q_DECL_UNUSED
static QDebug &operator<<(QDebug &d, HeaderDebug h)
105 switch (h.h->Signature & 0xffff) {
106 case IMAGE_OS2_SIGNATURE:
return d <<
"NE executable";
107 case IMAGE_VXD_SIGNATURE:
return d <<
"LE executable";
108 default:
return d <<
"Unknown file type";
109 case IMAGE_NT_SIGNATURE:
break;
114 switch (h.h->OptionalHeader.Magic) {
115 case IMAGE_NT_OPTIONAL_HDR32_MAGIC: d <<
"COFF PE";
break;
116 case IMAGE_NT_OPTIONAL_HDR64_MAGIC: d <<
"COFF PE+";
break;
117 default:
return d <<
"Unknown COFF PE type";
120 QDebugStateSaver saver(d);
121 d.nospace() << Qt::hex << Qt::showbase;
123 switch (h.h->FileHeader.Machine) {
124 case IMAGE_FILE_MACHINE_I386: d <<
"i386";
break;
125 case IMAGE_FILE_MACHINE_ARM: d <<
"ARM";
break;
126 case IMAGE_FILE_MACHINE_ARMNT: d <<
"ARM Thumb-2";
break;
127 case IMAGE_FILE_MACHINE_THUMB: d <<
"Thumb";
break;
128 case IMAGE_FILE_MACHINE_IA64: d <<
"IA-64";
break;
129 case IMAGE_FILE_MACHINE_MIPS16: d <<
"MIPS16";
break;
130 case IMAGE_FILE_MACHINE_MIPSFPU: d <<
"MIPS with FPU";
break;
131 case IMAGE_FILE_MACHINE_MIPSFPU16: d <<
"MIPS16 with FPU";
break;
132 case IMAGE_FILE_MACHINE_AMD64: d <<
"x86-64";
break;
133 case 0xaa64: d <<
"AArch64";
break;
134 default: d << h.h->FileHeader.Machine;
break;
138 if (h.h->FileHeader.Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE)
140 if (h.h->FileHeader.Characteristics & IMAGE_FILE_DLL)
142 if (h.h->FileHeader.Characteristics & IMAGE_FILE_LARGE_ADDRESS_AWARE)
143 d <<
" large-address aware";
145 d <<
", " << Qt::dec << h.h->FileHeader.NumberOfSections <<
" sections, ";
146 if (h.h->FileHeader.SizeOfOptionalHeader <
sizeof(IMAGE_OPTIONAL_HEADER32))
149 auto optDebug = [&d](
const auto *hdr) {
150 d <<
"(Windows " << hdr->MajorSubsystemVersion
151 <<
'.' << hdr->MinorSubsystemVersion;
152 switch (hdr->Subsystem) {
153 case IMAGE_SUBSYSTEM_NATIVE: d <<
" native)";
break;
154 case IMAGE_SUBSYSTEM_WINDOWS_GUI: d <<
" GUI)";
break;
155 case IMAGE_SUBSYSTEM_WINDOWS_CUI: d <<
" CUI)";
break;
156 default: d <<
" subsystem " << hdr->Subsystem <<
')';
break;
159 d.space() << Qt::hex << hdr->SizeOfHeaders <<
"header bytes,"
160 << Qt::dec << hdr->NumberOfRvaAndSizes <<
"RVA entries";
163 if (h.h->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC)
164 optDebug(
reinterpret_cast<
const IMAGE_OPTIONAL_HEADER64 *>(&h.h->OptionalHeader));
166 optDebug(
reinterpret_cast<
const IMAGE_OPTIONAL_HEADER32 *>(&h.h->OptionalHeader));
170struct SectionDebug {
const IMAGE_SECTION_HEADER *s; };
171Q_DECL_UNUSED
static QDebug &operator<<(QDebug &d, SectionDebug s)
173 QDebugStateSaver saver(d);
174 d << Qt::hex << Qt::showbase;
176 if (s.s->Characteristics & IMAGE_SCN_CNT_CODE)
178 if (s.s->Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA)
180 if (s.s->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA)
185 if (s.s->Characteristics & IMAGE_SCN_MEM_READ)
187 if (s.s->Characteristics & IMAGE_SCN_MEM_WRITE)
189 if (s.s->Characteristics & IMAGE_SCN_MEM_EXECUTE)
191 if (s.s->Characteristics & IMAGE_SCN_MEM_SHARED)
193 if (s.s->Characteristics & IMAGE_SCN_MEM_DISCARDABLE)
196 d.space() <<
"offset" << s.s->PointerToRawData <<
"size" << s.s->SizeOfRawData;
203const IMAGE_NT_HEADERS *checkNtHeaders(QByteArrayView data,
const ErrorMaker &error)
205 if (size_t(data.size()) < qMax(
sizeof(IMAGE_DOS_HEADER),
sizeof(IMAGE_NT_HEADERS))) {
206 peDebug <<
"file too small:" << size_t(data.size());
207 return error.toosmall(),
nullptr;
212 auto dosHeader =
reinterpret_cast<
const IMAGE_DOS_HEADER *>(data.data());
213 if (dosHeader->e_magic == IMAGE_DOS_SIGNATURE) {
214 off = dosHeader->e_lfanew;
216 if (size_t end; qAddOverflow<
sizeof(IMAGE_NT_HEADERS)>(off, &end)
217 || end > size_t(data.size())) {
218 peDebug <<
"file too small:" << size_t(data.size());
219 return error.toosmall(),
nullptr;
224 auto ntHeader =
reinterpret_cast<
const IMAGE_NT_HEADERS *>(data.data() + off);
225 peDebug << HeaderDebug{ntHeader};
226 if (ntHeader->Signature != IMAGE_NT_SIGNATURE)
227 return error(QLibrary::tr(
"invalid signature")),
nullptr;
228 if (ntHeader->FileHeader.Machine != ExpectedMachine)
229 return error(QLibrary::tr(
"file is for a different processor")),
nullptr;
230 if (ntHeader->FileHeader.NumberOfSections == 0)
231 return error(QLibrary::tr(
"file has no sections")),
nullptr;
233 WORD requiredCharacteristics =
234 IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_DLL;
235 if ((ntHeader->FileHeader.Characteristics & requiredCharacteristics) != requiredCharacteristics)
236 return error(QLibrary::tr(
"wrong characteristics")),
nullptr;
239 if (ntHeader->OptionalHeader.Magic != ExpectedOptionalHeaderSignature)
240 return error(QLibrary::tr(
"file is for a different word size")),
nullptr;
241 if (ntHeader->OptionalHeader.SizeOfCode == 0)
242 return error.notplugin(QLibrary::tr(
"file has no code")),
nullptr;
248findSectionTable(QByteArrayView data,
const IMAGE_NT_HEADERS *ntHeader,
const ErrorMaker &error)
253 static_assert(
sizeof(ntHeader->FileHeader.SizeOfOptionalHeader) <
sizeof(size_t));
254 static_assert(
sizeof(ntHeader->FileHeader.NumberOfSections) <
sizeof(size_t));
256 size_t off = offsetof(IMAGE_NT_HEADERS, OptionalHeader);
257 off += ntHeader->FileHeader.SizeOfOptionalHeader;
258 if (qAddOverflow<size_t>(off,
reinterpret_cast<
const char *>(ntHeader) - data.data(), &off))
259 return error.toosmall(),
nullptr;
261 size_t end = ntHeader->FileHeader.NumberOfSections *
sizeof(IMAGE_SECTION_HEADER);
265 if (qAddOverflow(end, off, &end) || end > size_t(data.size()))
266 return error.toosmall(),
nullptr;
268 peDebug <<
"contains" << ntHeader->FileHeader.NumberOfSections <<
"sections at offset" << off;
269 return reinterpret_cast<
const IMAGE_SECTION_HEADER *>(data.data() + off);
273findStringTable(QByteArrayView data,
const IMAGE_NT_HEADERS *ntHeader,
const ErrorMaker &error)
276 size_t off = ntHeader->FileHeader.PointerToSymbolTable;
278 return QByteArrayView();
281 constexpr size_t SymbolEntrySize = 18;
282 size_t size = ntHeader->FileHeader.NumberOfSymbols;
283 if (qMulOverflow<SymbolEntrySize>(size, &size)
284 || qAddOverflow(off, size, &off)
285 || qAddOverflow(off,
sizeof(DWORD), &off)
286 || off > size_t(data.size()))
287 return error.toosmall(),
std::nullopt;
289 off -=
sizeof(DWORD);
292 size = qFromUnaligned<DWORD>(data.data() + off);
293 if (size_t end; qAddOverflow(off, size, &end) || end > size_t(data.size()))
294 return error.toosmall(),
std::nullopt;
298 return data.sliced(off, size);
303 auto ptr =
reinterpret_cast<
const char *>(section->Name);
304 qsizetype n = qstrnlen(ptr,
sizeof(section->Name));
305 if (ptr[0] ==
'/' && !stringTable.isEmpty()) {
313 static_assert(
sizeof(section->Name) - 1 <
std::numeric_limits<uint>::digits10);
315 qsizetype offset = QByteArrayView(ptr + 1, n - 1).toUInt(&ok);
316 if (!ok || offset >= stringTable.size())
319 ptr = stringTable.data() + offset;
320 n = qstrnlen(ptr, stringTable.size() - offset);
326QLibraryScanResult QCoffPeParser::parse(QByteArrayView data, QString *errMsg)
328 ErrorMaker error(errMsg);
329 auto ntHeaders = checkNtHeaders(data, error);
333 auto section = findSectionTable(data, ntHeaders, error);
337 QByteArrayView stringTable;
338 if (
auto optional = findStringTable(data, ntHeaders, error))
339 stringTable = *optional;
344 const auto sectionTableEnd = section + ntHeaders->FileHeader.NumberOfSections;
345 for ( ; section < sectionTableEnd; ++section) {
346 QLatin1StringView sectionName = findSectionName(section, stringTable);
347 peDebug <<
"section" << sectionName << SectionDebug{section};
348 if (IncludeValidityChecks && sectionName.isEmpty())
349 return error(QLibrary::tr(
"a section name is empty or extends past the end of the file"));
351 size_t offset = section->PointerToRawData;
352 if (size_t end; qAddOverflow<size_t>(offset, section->SizeOfRawData, &end)
353 || end > size_t(data.size()))
354 return error(QLibrary::tr(
"section contents extend past the end of the file"));
356 DWORD type = section->Characteristics
357 & (IMAGE_SCN_CNT_CODE | IMAGE_SCN_CNT_INITIALIZED_DATA
358 | IMAGE_SCN_CNT_UNINITIALIZED_DATA);
359 if (type != IMAGE_SCN_CNT_INITIALIZED_DATA)
363 if (sectionName != truncatedSectionName && sectionName != metadataSectionName())
365 peDebug <<
"found .qtmetadata section";
367 size_t size = qMin(section->SizeOfRawData, section->Misc.VirtualSize);
368 if (size <
sizeof(QPluginMetaData::MagicHeader))
369 return error(QLibrary::tr(
".qtmetadata section is too small"));
370 if (IncludeValidityChecks) {
371 QByteArrayView expectedMagic = QByteArrayView::fromArray(QPluginMetaData::MagicString);
372 QByteArrayView actualMagic = data.sliced(offset, expectedMagic.size());
373 if (expectedMagic != actualMagic)
374 return error(QLibrary::tr(
".qtmetadata section has incorrect magic"));
376 if (section->Characteristics & IMAGE_SCN_MEM_WRITE)
377 return error(QLibrary::tr(
".qtmetadata section is writable"));
378 if (section->Characteristics & IMAGE_SCN_MEM_EXECUTE)
379 return error(QLibrary::tr(
".qtmetadata section is executable"));
382 return { qsizetype(offset +
sizeof(QPluginMetaData::MagicString)),
383 qsizetype(size -
sizeof(QPluginMetaData::MagicString)) };
386 return error.notfound();
static QT_WARNING_PUSH const WORD ExpectedMachine
static QLatin1StringView findSectionName(const IMAGE_SECTION_HEADER *section, QByteArrayView stringTable)
static constexpr auto metadataSectionName() noexcept
static constexpr bool IncludeValidityChecks
static std::optional< QByteArrayView > findStringTable(QByteArrayView data, const IMAGE_NT_HEADERS *ntHeader, const ErrorMaker &error)
static constexpr QLatin1StringView truncatedSectionName
static const WORD ExpectedOptionalHeaderSignature
static const IMAGE_SECTION_HEADER * findSectionTable(QByteArrayView data, const IMAGE_NT_HEADERS *ntHeader, const ErrorMaker &error)