Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qelfparser_p.cpp
Go to the documentation of this file.
1// Copyright (C) 2017 The Qt Company Ltd.
2// Copyright (C) 2021 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 "qelfparser_p.h"
6
7#ifdef Q_OF_ELF
8
9#include "qlibrary_p.h"
10
11#include <qloggingcategory.h>
12#include <qnumeric.h>
13#include <qsysinfo.h>
14
15#if __has_include(<elf.h>)
16# include <elf.h>
17#elif __has_include(<sys/elf.h>)
18# include <sys/elf.h>
19#else
20# error "Need ELF header to parse plugins."
21#endif
22
24
25using namespace Qt::StringLiterals;
26
27// ### Qt7: propagate the constant and eliminate dead code
28static constexpr bool ElfNotesAreMandatory = QT_VERSION >= QT_VERSION_CHECK(7,0,0);
29
30// Whether we include some extra validity checks
31// (checks to ensure we don't read out-of-bounds are always included)
32static constexpr bool IncludeValidityChecks = true;
33
34#ifdef QT_BUILD_INTERNAL
35# define QELFPARSER_DEBUG
36#endif
37#if defined(QELFPARSER_DEBUG)
38static Q_LOGGING_CATEGORY(lcElfParser, "qt.core.plugin.elfparser")
39# define qEDebug qCDebug(lcElfParser) << reinterpret_cast<const char16_t *>(error.errMsg->constData()) << ':'
40#else
41# define qEDebug if (false) {} else QNoDebug()
42#endif
43
44#ifndef PT_GNU_EH_FRAME
45# define PT_GNU_EH_FRAME 0x6474e550
46#endif
47#ifndef PT_GNU_STACK
48# define PT_GNU_STACK 0x6474e551
49#endif
50#ifndef PT_GNU_RELRO
51# define PT_GNU_RELRO 0x6474e552
52#endif
53#ifndef PT_GNU_PROPERTY
54# define PT_GNU_PROPERTY 0x6474e553
55#endif
56
57#ifndef PN_XNUM
58# define PN_XNUM 0xffff
59#endif
60
62QT_WARNING_DISABLE_CLANG("-Wunused-const-variable")
63
64namespace {
65template <QSysInfo::Endian Order> struct ElfEndianTraits
66{
67 static constexpr unsigned char DataOrder = ELFDATA2LSB;
68 template <typename T> static T fromEndian(T value) { return qFromLittleEndian(value); }
69};
70template <> struct ElfEndianTraits<QSysInfo::BigEndian>
71{
72 static constexpr unsigned char DataOrder = ELFDATA2MSB;
73 template <typename T> static T fromEndian(T value) { return qFromBigEndian(value); }
74};
75
76template <typename EquivalentPointerType> struct ElfTypeTraits
77{
78 static constexpr unsigned char Class = ELFCLASS64;
79
80 // integer types
81 using Half = Elf64_Half;
82 using Word = Elf64_Word;
83 using Addr = Elf64_Addr;
84 using Off = Elf64_Off;
85
86 // structure types
87 using Ehdr = Elf64_Ehdr;
88 using Shdr = Elf64_Shdr;
89 using Phdr = Elf64_Phdr;
90 using Nhdr = Elf64_Nhdr;
91};
92template <> struct ElfTypeTraits<quint32>
93{
94 static constexpr unsigned char Class = ELFCLASS32;
95
96 // integer types
97 using Half = Elf32_Half;
98 using Word = Elf32_Word;
99 using Addr = Elf32_Addr;
100 using Off = Elf32_Off;
101
102 // structure types
103 using Ehdr = Elf32_Ehdr;
104 using Shdr = Elf32_Shdr;
105 using Phdr = Elf32_Phdr;
106 using Nhdr = Elf32_Nhdr;
107};
108
109struct ElfMachineCheck
110{
111 static const Elf32_Half ExpectedMachine =
112#if 0
113 // nothing
114#elif defined(Q_PROCESSOR_ALPHA)
115 EM_ALPHA
116#elif defined(Q_PROCESSOR_ARM_32)
117 EM_ARM
118#elif defined(Q_PROCESSOR_ARM_64)
119 EM_AARCH64
120#elif defined(Q_PROCESSOR_BLACKFIN)
121 EM_BLACKFIN
122#elif defined(Q_PROCESSOR_HPPA)
123 EM_PARISC
124#elif defined(Q_PROCESSOR_IA64)
125 EM_IA_64
126#elif defined(Q_PROCESSOR_LOONGARCH)
127 EM_LOONGARCH
128#elif defined(Q_PROCESSOR_M68K)
129 EM_68K
130#elif defined(Q_PROCESSOR_MIPS)
131 EM_MIPS
132#elif defined(Q_PROCESSOR_POWER_32)
133 EM_PPC
134#elif defined(Q_PROCESSOR_POWER_64)
135 EM_PPC64
136#elif defined(Q_PROCESSOR_RISCV)
137 EM_RISCV
138#elif defined(Q_PROCESSOR_S390)
139 EM_S390
140#elif defined(Q_PROCESSOR_SH)
141 EM_SH
142#elif defined(Q_PROCESSOR_SPARC_V9)
143 EM_SPARCV9
144#elif defined(Q_PROCESSOR_SPARC_64)
145 EM_SPARCV9
146#elif defined(Q_PROCESSOR_SPARC)
147 EM_SPARC
148#elif defined(Q_PROCESSOR_WASM)
149#elif defined(Q_PROCESSOR_X86_32)
150 EM_386
151#elif defined(Q_PROCESSOR_X86_64)
152 EM_X86_64
153#else
154# error "Unknown Q_PROCESSOR_xxx macro, please update."
155 EM_NONE
156#endif
157 ;
158};
159
160struct ElfHeaderCommonCheck
161{
162 static_assert(std::is_same_v<decltype(Elf32_Ehdr::e_ident), decltype(Elf64_Ehdr::e_ident)>,
163 "e_ident field is not the same in both Elf32_Ehdr and Elf64_Ehdr");
164
165 // bytes 0-3
166 static bool checkElfMagic(const uchar *ident)
167 {
168 return memcmp(ident, ELFMAG, SELFMAG) == 0;
169 }
170
171 // byte 6
172 static bool checkElfVersion(const uchar *ident)
173 {
174 uchar elfversion = ident[EI_VERSION];
175 return elfversion == EV_CURRENT;
176 }
177
178 struct CommonHeader {
179 Elf32_Half type;
180 Elf32_Half machine;
181 Elf32_Word version;
182 };
183};
184
185template <typename EquivalentPointerType = quintptr, QSysInfo::Endian Order = QSysInfo::ByteOrder>
186struct ElfHeaderCheck : public ElfHeaderCommonCheck
187{
188 using TypeTraits = ElfTypeTraits<EquivalentPointerType>;
189 using EndianTraits = ElfEndianTraits<Order>;
190 using Ehdr = typename TypeTraits::Ehdr;
191
192 // byte 4
193 static bool checkClass(const uchar *ident)
194 {
195 uchar klass = ident[EI_CLASS];
196 return klass == TypeTraits::Class;
197 }
198
199 // byte 5
200 static bool checkDataOrder(const uchar *ident)
201 {
202 uchar data = ident[EI_DATA];
203 return data == EndianTraits::DataOrder;
204 }
205
206 // byte 7
207 static bool checkOsAbi(const uchar *ident)
208 {
209 uchar osabi = ident[EI_OSABI];
210 // we don't check
211 Q_UNUSED(osabi);
212 return true;
213 }
214
215 // byte 8
216 static bool checkAbiVersion(const uchar *ident)
217 {
218 uchar abiversion = ident[EI_ABIVERSION];
219 // we don't check (and I don't know anyone who uses this)
220 Q_UNUSED(abiversion);
221 return true;
222 }
223
224 // bytes 9-16
225 static bool checkPadding(const uchar *ident)
226 {
227 // why would we check this?
229 return true;
230 }
231
232 static bool checkIdent(const Ehdr &header)
233 {
234 return checkElfMagic(header.e_ident)
235 && checkClass(header.e_ident)
236 && checkDataOrder(header.e_ident)
237 && checkElfVersion(header.e_ident)
238 && checkOsAbi(header.e_ident)
239 && checkAbiVersion(header.e_ident)
240 && checkPadding(header.e_ident);
241 }
242
243 static bool checkType(const Ehdr &header)
244 {
245 return header.e_type == ET_DYN;
246 }
247
248 static bool checkMachine(const Ehdr &header)
249 {
250 return header.e_machine == ElfMachineCheck::ExpectedMachine;
251 }
252
253 static bool checkFileVersion(const Ehdr &header)
254 {
255 return header.e_version == EV_CURRENT;
256 }
257
258 static bool checkHeader(const Ehdr &header)
259 {
260 if (!checkIdent(header))
261 return false;
263 return true;
264 return checkType(header)
265 && checkMachine(header)
266 && checkFileVersion(header);
267 }
268
269 Q_DECL_COLD_FUNCTION static QString explainCheckFailure(const Ehdr &header)
270 {
271 if (!checkElfMagic(header.e_ident))
272 return QLibrary::tr("invalid signature");
273 if (!checkClass(header.e_ident))
274 return QLibrary::tr("file is for a different word size");
275 if (!checkDataOrder(header.e_ident))
276 return QLibrary::tr("file is for the wrong endianness");
277 if (!checkElfVersion(header.e_ident) || !checkFileVersion(header))
278 return QLibrary::tr("file has an unknown ELF version");
279 if (!checkOsAbi(header.e_ident) || !checkAbiVersion(header.e_ident))
280 return QLibrary::tr("file has an unexpected ABI");
281 if (!checkType(header))
282 return QLibrary::tr("file is not a shared object");
283 if (!checkMachine(header))
284 return QLibrary::tr("file is for a different processor");
285 return QString();
286 }
287
288 static CommonHeader extractCommonHeader(const uchar *data)
289 {
290 auto header = reinterpret_cast<const Ehdr *>(data);
291 CommonHeader r;
292 r.type = EndianTraits::fromEndian(header->e_type);
293 r.machine = EndianTraits::fromEndian(header->e_machine);
294 r.version = EndianTraits::fromEndian(header->e_version);
295 return r;
296 }
297};
298
299struct ElfHeaderDebug { const uchar *e_ident; };
301{
302 const uchar *e_ident = h.e_ident;
303 if (!ElfHeaderCommonCheck::checkElfMagic(e_ident)) {
304 d << "Not an ELF file (invalid signature)";
305 return d;
306 }
307
308 QDebugStateSaver saver(d);
309 d.nospace();
310 quint8 elfclass = e_ident[EI_CLASS];
311 switch (elfclass) {
312 case ELFCLASSNONE:
313 default:
314 d << "Invalid ELF file (class " << e_ident[EI_CLASS] << "), ";
315 break;
316 case ELFCLASS32:
317 d << "ELF 32-bit ";
318 break;
319 case ELFCLASS64:
320 d << "ELF 64-bit ";
321 break;
322 }
323
324 quint8 dataorder = e_ident[EI_DATA];
325 switch (dataorder) {
326 case ELFDATANONE:
327 default:
328 d << "invalid endianness (" << e_ident[EI_DATA] << ')';
329 break;
330 case ELFDATA2LSB:
331 d << "LSB";
332 break;
333 case ELFDATA2MSB:
334 d << "MSB";
335 break;
336 }
337
338 switch (e_ident[EI_OSABI]) {
339 case ELFOSABI_SYSV: d << " (SYSV"; break;
340 case ELFOSABI_HPUX: d << " (HP-UX"; break;
341 case ELFOSABI_NETBSD: d << " (NetBSD"; break;
342 case ELFOSABI_LINUX: d << " (GNU/Linux"; break;
343 case ELFOSABI_SOLARIS: d << " (Solaris"; break;
344 case ELFOSABI_AIX: d << " (AIX"; break;
345 case ELFOSABI_IRIX: d << " (IRIX"; break;
346 case ELFOSABI_FREEBSD: d << " (FreeBSD"; break;
347 case ELFOSABI_OPENBSD: d << " (OpenBSD"; break;
348 default: d << " (OS ABI " << e_ident[EI_VERSION]; break;
349 }
350
351 if (e_ident[EI_ABIVERSION])
352 d << " v" << e_ident[EI_ABIVERSION];
353 d << ')';
354
355 if (e_ident[EI_VERSION] != 1) {
356 d << ", file version " << e_ident[EI_VERSION];
357 return d;
358 }
359
360 ElfHeaderCommonCheck::CommonHeader r;
361 if (elfclass == ELFCLASS64 && dataorder == ELFDATA2LSB)
362 r = ElfHeaderCheck<quint64, QSysInfo::LittleEndian>::extractCommonHeader(e_ident);
363 else if (elfclass == ELFCLASS32 && dataorder == ELFDATA2LSB)
364 r = ElfHeaderCheck<quint32, QSysInfo::LittleEndian>::extractCommonHeader(e_ident);
365 else if (elfclass == ELFCLASS64 && dataorder == ELFDATA2MSB)
366 r = ElfHeaderCheck<quint64, QSysInfo::BigEndian>::extractCommonHeader(e_ident);
367 else if (elfclass == ELFCLASS32 && dataorder == ELFDATA2MSB)
368 r = ElfHeaderCheck<quint32, QSysInfo::BigEndian>::extractCommonHeader(e_ident);
369 else
370 return d;
371
372 d << ", version " << r.version;
373
374 switch (r.type) {
375 case ET_NONE: d << ", no type"; break;
376 case ET_REL: d << ", relocatable"; break;
377 case ET_EXEC: d << ", executable"; break;
378 case ET_DYN: d << ", shared library or PIC executable"; break;
379 case ET_CORE: d << ", core dump"; break;
380 default: d << ", unknown type " << r.type; break;
381 }
382
383 switch (r.machine) {
384 // list definitely not exhaustive!
385 case EM_NONE: d << ", no machine"; break;
386 case EM_ALPHA: d << ", Alpha"; break;
387 case EM_68K: d << ", MC68000"; break;
388 case EM_ARM: d << ", ARM"; break;
389 case EM_AARCH64: d << ", AArch64"; break;
390#ifdef EM_BLACKFIN
391 case EM_BLACKFIN: d << ", Blackfin"; break;
392#endif
393 case EM_IA_64: d << ", IA-64"; break;
394#ifdef EM_LOONGARCH
395 case EM_LOONGARCH: d << ", LoongArch"; break;
396#endif
397 case EM_MIPS: d << ", MIPS"; break;
398 case EM_PARISC: d << ", HPPA"; break;
399 case EM_PPC: d << ", PowerPC"; break;
400 case EM_PPC64: d << ", PowerPC 64-bit"; break;
401#ifdef EM_RISCV
402 case EM_RISCV: d << ", RISC-V"; break;
403#endif
404#ifdef EM_S390
405 case EM_S390: d << ", S/390"; break;
406#endif
407 case EM_SH: d << ", SuperH"; break;
408 case EM_SPARC: d << ", SPARC"; break;
409 case EM_SPARCV9: d << ", SPARCv9"; break;
410 case EM_386: d << ", i386"; break;
411 case EM_X86_64: d << ", x86-64"; break;
412 default: d << ", other machine type " << r.machine; break;
413 }
414
415 return d;
416}
417
418struct ElfSectionDebug { const ElfHeaderCheck<>::TypeTraits::Shdr *shdr; };
419Q_DECL_UNUSED static QDebug &operator<<(QDebug &d, ElfSectionDebug s)
420{
421 // not exhaustive, just a few common things
422 QDebugStateSaver saver(d);
423 d << Qt::hex << Qt::showbase;
424 d << "type";
425 switch (s.shdr->sh_type) {
426 case SHT_NULL: d << "NULL"; break;
427 case SHT_PROGBITS: d << "PROGBITS"; break;
428 case SHT_SYMTAB: d << "SYMTAB"; break;
429 case SHT_STRTAB: d << "STRTAB"; break;
430 case SHT_RELA: d << "RELA"; break;
431 case SHT_HASH: d << "HASH"; break;
432 case SHT_DYNAMIC: d << "DYNAMIC"; break;
433 case SHT_NOTE: d << "NOTE"; break;
434 case SHT_NOBITS: d << "NOBITS"; break;
435 case SHT_DYNSYM: d << "DYNSYM"; break;
436 case SHT_INIT_ARRAY: d << "INIT_ARRAY"; break;
437 case SHT_FINI_ARRAY: d << "FINI_ARRAY"; break;
438 default: d << s.shdr->sh_type;
439 }
440
441 d << "flags";
442 d.nospace();
443 if (s.shdr->sh_flags & SHF_WRITE)
444 d << 'W';
445 if (s.shdr->sh_flags & SHF_ALLOC)
446 d << 'A';
447 if (s.shdr->sh_flags & SHF_EXECINSTR)
448 d << 'X';
449 if (s.shdr->sh_flags & SHF_STRINGS)
450 d << 'S';
451 if (s.shdr->sh_flags & SHF_TLS)
452 d << 'T';
453
454 d.space() << "offset" << s.shdr->sh_offset << "size" << s.shdr->sh_size;
455 return d;
456}
457
458struct ElfProgramDebug { const ElfHeaderCheck<>::TypeTraits::Phdr *phdr; };
459Q_DECL_UNUSED static QDebug &operator<<(QDebug &d, ElfProgramDebug p)
460{
461 QDebugStateSaver saved(d);
462 d << Qt::hex << Qt::showbase << "program";
463 switch (p.phdr->p_type) {
464 case PT_NULL: d << "NULL"; break;
465 case PT_LOAD: d << "LOAD"; break;
466 case PT_DYNAMIC: d << "DYNAMIC"; break;
467 case PT_INTERP: d << "INTERP"; break;
468 case PT_NOTE: d << "NOTE"; break;
469 case PT_PHDR: d << "PHDR"; break;
470 case PT_TLS: d << "TLS"; break;
471 case PT_GNU_EH_FRAME: d << "GNU_EH_FRAME"; break;
472 case PT_GNU_STACK: d << "GNU_STACK"; break;
473 case PT_GNU_RELRO: d << "GNU_RELRO"; break;
474 case PT_GNU_PROPERTY: d << "GNU_PROPERTY"; break;
475 default: d << "type" << p.phdr->p_type; break;
476 }
477
478 d << "offset" << p.phdr->p_offset
479 << "virtaddr" << p.phdr->p_vaddr
480 << "filesz" << p.phdr->p_filesz
481 << "memsz" << p.phdr->p_memsz
482 << "align" << p.phdr->p_align
483 << "flags";
484
485 d.nospace();
486 if (p.phdr->p_flags & PF_R)
487 d << 'R';
488 if (p.phdr->p_flags & PF_W)
489 d << 'W';
490 if (p.phdr->p_flags & PF_X)
491 d << 'X';
492
493 return d;
494}
495
496struct ErrorMaker
497{
499 constexpr ErrorMaker(QString *errMsg) : errMsg(errMsg) {}
500
501
503 {
504 *errMsg = QLibrary::tr("'%1' is not a valid ELF object (%2)").arg(*errMsg, std::move(text));
505 return {};
506 }
507
508 Q_DECL_COLD_FUNCTION QLibraryScanResult notplugin(QString &&explanation) const
509 {
510 *errMsg = QLibrary::tr("'%1' is not a Qt plugin (%2)").arg(*errMsg, explanation);
511 return {};
512 }
513
515 {
516 return notplugin(QLibrary::tr("metadata not found"));
517 }
518};
519} // unnamed namespace
520
522
523using T = ElfHeaderCheck<>::TypeTraits;
524
525template <typename F>
526static bool scanProgramHeaders(QByteArrayView data, const ErrorMaker &error, F f)
527{
528 auto header = reinterpret_cast<const T::Ehdr *>(data.data());
530
531 auto phdr = reinterpret_cast<const T::Phdr *>(data.data() + header->e_phoff);
532 auto phdr_end = phdr + header->e_phnum;
533 for ( ; phdr != phdr_end; ++phdr) {
534 if (!f(phdr))
535 return false;
536 }
537 return true;
538}
539
540static bool preScanProgramHeaders(QByteArrayView data, const ErrorMaker &error)
541{
542 auto header = reinterpret_cast<const T::Ehdr *>(data.data());
543
544 // first, validate the extent of the full program header table
545 T::Word e_phnum = header->e_phnum;
546 if (e_phnum == PN_XNUM)
547 return error(QLibrary::tr("unimplemented: PN_XNUM program headers")), false;
548 T::Off offset = e_phnum * sizeof(T::Phdr); // can't overflow due to size of T::Half
549 if (qAddOverflow(offset, header->e_phoff, &offset) || offset > size_t(data.size()))
550 return error(QLibrary::tr("program header table extends past the end of the file")), false;
551
552 // confirm validity
553 bool hasCode = false;
554 auto checker = [&](const T::Phdr *phdr) {
555 qEDebug << ElfProgramDebug{phdr};
556
557 if (T::Off end; qAddOverflow(phdr->p_offset, phdr->p_filesz, &end)
558 || end > size_t(data.size()))
559 return error(QLibrary::tr("a program header entry extends past the end of the file")), false;
560
561 // this is not a validity check, it's to exclude debug symbol files
562 if (phdr->p_type == PT_LOAD && phdr->p_filesz != 0 && (phdr->p_flags & PF_X))
563 hasCode = true;
564
565 // this probably applies to all segments, but we'll only apply it to notes
566 if (phdr->p_type == PT_NOTE && qPopulationCount(phdr->p_align) == 1
567 && phdr->p_offset & (phdr->p_align - 1)) {
568 return error(QLibrary::tr("a note segment start is not properly aligned "
569 "(offset 0x%1, alignment %2)")
570 .arg(phdr->p_offset, 6, 16, QChar(u'0'))
571 .arg(phdr->p_align)), false;
572 }
573
574 return true;
575 };
576 if (!scanProgramHeaders(data, error, checker))
577 return false;
578 if (!hasCode)
579 return error.notplugin(QLibrary::tr("file has no code")), false;
580 return true;
581}
582
583static QLibraryScanResult scanProgramHeadersForNotes(QByteArrayView data, const ErrorMaker &error)
584{
585 // minimum metadata payload is 2 bytes
586 constexpr size_t MinPayloadSize = sizeof(QPluginMetaData::Header) + 2;
587 constexpr qptrdiff MinNoteSize = sizeof(QPluginMetaData::ElfNoteHeader) + 2;
588 constexpr size_t NoteNameSize = sizeof(QPluginMetaData::ElfNoteHeader::name);
589 constexpr size_t NoteAlignment = alignof(QPluginMetaData::ElfNoteHeader);
590 constexpr qptrdiff PayloadStartDelta = offsetof(QPluginMetaData::ElfNoteHeader, header);
591 static_assert(MinNoteSize > PayloadStartDelta);
592 static_assert((PayloadStartDelta & (NoteAlignment - 1)) == 0);
593
595 auto noteFinder = [&](const T::Phdr *phdr) {
596 if (phdr->p_type != PT_NOTE || phdr->p_align != NoteAlignment)
597 return true;
598
599 // check for signed integer overflows, to avoid issues with the
600 // arithmetic below
601 if (qptrdiff(phdr->p_filesz) < 0) {
602 auto h = reinterpret_cast<const T::Ehdr *>(data.data());
603 auto segments = reinterpret_cast<const T::Phdr *>(data.data() + h->e_phoff);
604 qEDebug << "segment" << (phdr - segments) << "contains a note with size"
605 << Qt::hex << Qt::showbase << phdr->p_filesz
606 << "which is larger than half the virtual memory space";
607 return true;
608 }
609
610 // iterate over the notes in this segment
611 T::Off offset = phdr->p_offset;
612 const T::Off end_offset = offset + phdr->p_filesz;
613 while (qptrdiff(end_offset - offset) >= MinNoteSize) {
614 auto nhdr = reinterpret_cast<const T::Nhdr *>(data.data() + offset);
615 T::Word n_namesz = nhdr->n_namesz;
616 T::Word n_descsz = nhdr->n_descsz;
617 T::Word n_type = nhdr->n_type;
618
619 // overflow check: calculate where the next note will be, if it exists
620 T::Off next_offset = offset;
621 next_offset += sizeof(T::Nhdr); // can't overflow (we checked above)
622 next_offset += NoteAlignment - 3; // offset is aligned, this can't overflow
623 if (qAddOverflow<T::Off>(next_offset, n_namesz, &next_offset))
624 break;
625 next_offset &= -NoteAlignment;
626
627 next_offset += NoteAlignment - 3; // offset is aligned, this can't overflow
628 if (qAddOverflow<T::Off>(next_offset, n_descsz, &next_offset))
629 break;
630 next_offset &= -NoteAlignment;
631 if (next_offset > end_offset)
632 break;
633
634 if (n_namesz == NoteNameSize && n_descsz >= MinPayloadSize
636 && memcmp(nhdr + 1, QPluginMetaData::ElfNoteHeader::NoteName, NoteNameSize) == 0) {
637 // yes, it's our note
638 r.pos = offset + PayloadStartDelta;
639 r.length = nhdr->n_descsz;
640 return false;
641 }
642 offset = next_offset;
643 }
644 return true;
645 };
646 scanProgramHeaders(data, error, noteFinder);
647
648 if (!r.length)
649 return r;
650
651 qEDebug << "found Qt metadata in ELF note at"
652 << Qt::hex << Qt::showbase << r.pos << "size" << Qt::reset << r.length;
653 return r;
654}
655
656static QLibraryScanResult scanSections(QByteArrayView data, const ErrorMaker &error)
657{
658 auto header = reinterpret_cast<const T::Ehdr *>(data.data());
659
660 // in order to find the .qtmetadata section, we need to:
661 // a) find the section table
662 // it's located at offset header->e_shoff
663 // validate it
664 T::Word e_shnum = header->e_shnum;
665 T::Off offset = e_shnum * sizeof(T::Shdr); // can't overflow due to size of T::Half
666 if (qAddOverflow(offset, header->e_shoff, &offset) || offset > size_t(data.size()))
667 return error(QLibrary::tr("section table extends past the end of the file"));
668
669 // b) find the section entry for the section header string table (shstrab)
670 // it's a section whose entry is pointed by e_shstrndx
671 auto sections = reinterpret_cast<const T::Shdr *>(data.data() + header->e_shoff);
672 auto sections_end = sections + e_shnum;
673 auto shdr = sections + header->e_shstrndx;
674
675 // validate the shstrtab
676 offset = shdr->sh_offset;
677 T::Off shstrtab_size = shdr->sh_size;
678 qEDebug << "shstrtab section is located at offset" << offset << "size" << shstrtab_size;
679 if (T::Off end; qAddOverflow<T::Off>(offset, shstrtab_size, &end)
680 || end > size_t(data.size()))
681 return error(QLibrary::tr("section header string table extends past the end of the file"));
682
683 // c) iterate over the sections to find .qtmetadata
684 const char *shstrtab_start = data.data() + offset;
685 shdr = sections;
686 for (int section = 0; shdr != sections_end; ++section, ++shdr) {
688 if (shdr->sh_name < shstrtab_size) {
689 const char *namestart = shstrtab_start + shdr->sh_name;
690 size_t len = qstrnlen(namestart, shstrtab_size - shdr->sh_name);
691 name = QLatin1StringView(namestart, len);
692 }
693 qEDebug << "section" << section << "name" << name << ElfSectionDebug{shdr};
694
695 // sanity check the section
696 if (name.isNull())
697 return error(QLibrary::tr("a section name extends past the end of the file"));
698
699 // sections aren't allowed to extend past the end of the file, unless
700 // they are NOBITS sections
701 if (shdr->sh_type == SHT_NOBITS)
702 continue;
703 if (T::Off end; qAddOverflow(shdr->sh_offset, shdr->sh_size, &end)
704 || end > size_t(data.size())) {
705 return error(QLibrary::tr("section contents extend past the end of the file"));
706 }
707
708 if (name != ".qtmetadata"_L1)
709 continue;
710 qEDebug << "found .qtmetadata section";
711 if (shdr->sh_size < sizeof(QPluginMetaData::MagicHeader))
712 return error(QLibrary::tr(".qtmetadata section is too small"));
713
716 QByteArrayView actualMagic = data.sliced(shdr->sh_offset, expectedMagic.size());
717 if (expectedMagic != actualMagic)
718 return error(QLibrary::tr(".qtmetadata section has incorrect magic"));
719
720 if (shdr->sh_flags & SHF_WRITE)
721 return error(QLibrary::tr(".qtmetadata section is writable"));
722 if (shdr->sh_flags & SHF_EXECINSTR)
723 return error(QLibrary::tr(".qtmetadata section is executable"));
724 }
725
726 return { qsizetype(shdr->sh_offset + sizeof(QPluginMetaData::MagicString)),
727 qsizetype(shdr->sh_size - sizeof(QPluginMetaData::MagicString)) };
728 }
729
730 // section .qtmetadata not found
731 return error.notfound();
732}
733
735{
736 ErrorMaker error(errMsg);
737 if (size_t(data.size()) < sizeof(T::Ehdr)) {
738 qEDebug << "file too small:" << size_t(data.size());
739 return error(QLibrary::tr("file too small"));
740 }
741
742 qEDebug << ElfHeaderDebug{ reinterpret_cast<const uchar *>(data.data()) };
743
744 auto header = reinterpret_cast<const T::Ehdr *>(data.data());
745 if (!ElfHeaderCheck<>::checkHeader(*header))
746 return error(ElfHeaderCheck<>::explainCheckFailure(*header));
747
748 qEDebug << "contains" << header->e_phnum << "program headers of"
749 << header->e_phentsize << "bytes at offset" << header->e_phoff;
750 qEDebug << "contains" << header->e_shnum << "sections of" << header->e_shentsize
751 << "bytes at offset" << header->e_shoff
752 << "; section header string table (shstrtab) is entry" << header->e_shstrndx;
753
754 // some sanity checks
755 if constexpr (IncludeValidityChecks) {
756 if (header->e_phentsize != sizeof(T::Phdr))
757 return error(QLibrary::tr("unexpected program header entry size (%1)")
758 .arg(header->e_phentsize));
759 }
760
761 if (!preScanProgramHeaders(data, error))
762 return {};
763
764 if (QLibraryScanResult r = scanProgramHeadersForNotes(data, error); r.length)
765 return r;
766
767 if (!ElfNotesAreMandatory) {
768 if constexpr (IncludeValidityChecks) {
769 if (header->e_shentsize != sizeof(T::Shdr))
770 return error(QLibrary::tr("unexpected section entry size (%1)")
771 .arg(header->e_shentsize));
772 }
773 if (header->e_shoff == 0 || header->e_shnum == 0) {
774 // this is still a valid ELF file but we don't have a section table
775 qEDebug << "no section table present, not able to find Qt metadata";
776 return error.notfound();
777 }
778
779 if (header->e_shnum && header->e_shstrndx >= header->e_shnum)
780 return error(QLibrary::tr("e_shstrndx greater than the number of sections e_shnum (%1 >= %2)")
781 .arg(header->e_shstrndx).arg(header->e_shnum));
782 return scanSections(data, error);
783 }
784 return error.notfound();
785}
786
788
789#endif // Q_OF_ELF
constexpr QByteArrayView sliced(qsizetype pos) const
static constexpr QByteArrayView fromArray(const Byte(&data)[Size]) noexcept
\inmodule QtCore
\inmodule QtCore
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
QString arg(qlonglong a, int fieldwidth=0, int base=10, QChar fillChar=u' ') const
Definition qstring.cpp:8870
\inmodule QtCore
Definition qsysinfo.h:22
QString text
Token ident
Definition keywords.cpp:448
Combined button and popup list for selecting options.
QTextStream & hex(QTextStream &stream)
Calls QTextStream::setIntegerBase(16) on stream and returns stream.
QTextStream & showbase(QTextStream &stream)
Calls QTextStream::setNumberFlags(QTextStream::numberFlags() | QTextStream::ShowBase) on stream and r...
QTextStream & reset(QTextStream &stream)
Calls QTextStream::reset() on stream and returns stream.
Q_DECL_CONST_FUNCTION QT_POPCOUNT_CONSTEXPR uint qPopulationCount(quint32 v) noexcept
size_t qstrnlen(const char *str, size_t maxlen)
static QT_WARNING_PUSH const WORD ExpectedMachine
static constexpr bool IncludeValidityChecks
#define Q_DECL_UNUSED
#define QT_WARNING_POP
#define Q_DECL_COLD_FUNCTION
#define QT_WARNING_PUSH
#define QT_WARNING_DISABLE_CLANG(text)
DBusConnection const char DBusError * error
static QString header(const QString &name)
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
constexpr T qFromLittleEndian(T source)
Definition qendian.h:178
constexpr T qFromBigEndian(T source)
Definition qendian.h:174
qsizetype QString * errMsg
Definition qlibrary.cpp:166
#define Q_LOGGING_CATEGORY(name,...)
static Q_DECL_COLD_FUNCTION QLibraryScanResult notfound(const QString &reason, QString *errorString)
std::enable_if_t< std::is_unsigned_v< T >, bool > qAddOverflow(T v1, T v2, T *r)
Definition qnumeric.h:113
GLboolean r
[2]
GLuint GLuint end
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLfloat GLfloat f
GLenum type
GLenum GLuint GLintptr offset
GLuint name
GLfloat GLfloat GLfloat GLfloat h
GLdouble s
[6]
Definition qopenglext.h:235
GLuint segments
GLfloat GLfloat p
[1]
GLenum GLsizei len
SSL_CTX int void * arg
#define Q_UNUSED(x)
#define QT_VERSION_CHECK(major, minor, patch)
#define QT_VERSION
unsigned int quint32
Definition qtypes.h:50
unsigned char uchar
Definition qtypes.h:32
ptrdiff_t qptrdiff
Definition qtypes.h:164
ptrdiff_t qsizetype
Definition qtypes.h:165
unsigned char quint8
Definition qtypes.h:46
QDataStream & operator<<(QDataStream &out, const MyClass &myObj)
[4]
char name[sizeof(NoteName)]
Definition qplugin.h:92
static constexpr quint32 NoteType
Definition qplugin.h:85
static constexpr char NoteName[]
Definition qplugin.h:86
static constexpr char MagicString[]
Definition qplugin.h:40