15#if __has_include(<elf.h>)
17#elif __has_include(<sys/elf.h>)
20# error "Need ELF header to parse plugins."
34#ifdef QT_BUILD_INTERNAL
35# define QELFPARSER_DEBUG
37#if defined(QELFPARSER_DEBUG)
39# define qEDebug qCDebug(lcElfParser) << reinterpret_cast<const char16_t *>(error.errMsg->constData()) << ':'
41# define qEDebug if (false) {} else QNoDebug()
44#ifndef PT_GNU_EH_FRAME
45# define PT_GNU_EH_FRAME 0x6474e550
48# define PT_GNU_STACK 0x6474e551
51# define PT_GNU_RELRO 0x6474e552
53#ifndef PT_GNU_PROPERTY
54# define PT_GNU_PROPERTY 0x6474e553
61template <QSysInfo::Endian Order>
struct ElfEndianTraits
63 static constexpr unsigned char DataOrder = ELFDATA2LSB;
66template <>
struct ElfEndianTraits<
QSysInfo::BigEndian>
68 static constexpr unsigned char DataOrder = ELFDATA2MSB;
72template <
typename EquivalentPo
interType>
struct ElfTypeTraits
74 static constexpr unsigned char Class = ELFCLASS64;
77 using Half = Elf64_Half;
78 using Word = Elf64_Word;
79 using Addr = Elf64_Addr;
80 using Off = Elf64_Off;
83 using Ehdr = Elf64_Ehdr;
84 using Shdr = Elf64_Shdr;
85 using Phdr = Elf64_Phdr;
86 using Nhdr = Elf64_Nhdr;
88template <>
struct ElfTypeTraits<
quint32>
90 static constexpr unsigned char Class = ELFCLASS32;
93 using Half = Elf32_Half;
94 using Word = Elf32_Word;
95 using Addr = Elf32_Addr;
96 using Off = Elf32_Off;
99 using Ehdr = Elf32_Ehdr;
100 using Shdr = Elf32_Shdr;
101 using Phdr = Elf32_Phdr;
102 using Nhdr = Elf32_Nhdr;
105struct ElfMachineCheck
110#elif defined(Q_PROCESSOR_ALPHA)
112#elif defined(Q_PROCESSOR_ARM_32)
114#elif defined(Q_PROCESSOR_ARM_64)
116#elif defined(Q_PROCESSOR_BLACKFIN)
118#elif defined(Q_PROCESSOR_HPPA)
120#elif defined(Q_PROCESSOR_IA64)
122#elif defined(Q_PROCESSOR_LOONGARCH)
124#elif defined(Q_PROCESSOR_M68K)
126#elif defined(Q_PROCESSOR_MIPS)
128#elif defined(Q_PROCESSOR_POWER_32)
130#elif defined(Q_PROCESSOR_POWER_64)
132#elif defined(Q_PROCESSOR_RISCV)
134#elif defined(Q_PROCESSOR_S390)
136#elif defined(Q_PROCESSOR_SH)
138#elif defined(Q_PROCESSOR_SPARC_V9)
140#elif defined(Q_PROCESSOR_SPARC_64)
142#elif defined(Q_PROCESSOR_SPARC)
144#elif defined(Q_PROCESSOR_WASM)
145#elif defined(Q_PROCESSOR_X86_32)
147#elif defined(Q_PROCESSOR_X86_64)
150# error "Unknown Q_PROCESSOR_xxx macro, please update."
156struct ElfHeaderCommonCheck
158 static_assert(std::is_same_v<
decltype(Elf32_Ehdr::e_ident),
decltype(Elf64_Ehdr::e_ident)>,
159 "e_ident field is not the same in both Elf32_Ehdr and Elf64_Ehdr");
164 return memcmp(
ident, ELFMAG, SELFMAG) == 0;
168 static bool checkElfVersion(
const uchar *
ident)
171 return elfversion == EV_CURRENT;
174 struct CommonHeader {
181template <
typename EquivalentPo
interType = qu
intptr, QSysInfo::Endian Order = QSysInfo::ByteOrder>
182struct ElfHeaderCheck :
public ElfHeaderCommonCheck
184 using TypeTraits = ElfTypeTraits<EquivalentPointerType>;
185 using EndianTraits = ElfEndianTraits<Order>;
186 using Ehdr =
typename TypeTraits::Ehdr;
192 return klass == TypeTraits::Class;
196 static bool checkDataOrder(
const uchar *
ident)
199 return data == EndianTraits::DataOrder;
212 static bool checkAbiVersion(
const uchar *
ident)
228 static bool checkIdent(
const Ehdr &
header)
230 return checkElfMagic(
header.e_ident)
231 && checkClass(
header.e_ident)
232 && checkDataOrder(
header.e_ident)
233 && checkElfVersion(
header.e_ident)
234 && checkOsAbi(
header.e_ident)
235 && checkAbiVersion(
header.e_ident)
236 && checkPadding(
header.e_ident);
239 static bool checkType(
const Ehdr &
header)
241 return header.e_type == ET_DYN;
244 static bool checkMachine(
const Ehdr &
header)
246 return header.e_machine == ElfMachineCheck::ExpectedMachine;
249 static bool checkFileVersion(
const Ehdr &
header)
251 return header.e_version == EV_CURRENT;
254 static bool checkHeader(
const Ehdr &
header)
262 && checkFileVersion(
header);
267 if (!checkElfMagic(
header.e_ident))
268 return QLibrary::tr(
"invalid signature");
269 if (!checkClass(
header.e_ident))
270 return QLibrary::tr(
"file is for a different word size");
271 if (!checkDataOrder(
header.e_ident))
272 return QLibrary::tr(
"file is for the wrong endianness");
273 if (!checkElfVersion(
header.e_ident) || !checkFileVersion(
header))
274 return QLibrary::tr(
"file has an unknown ELF version");
275 if (!checkOsAbi(
header.e_ident) || !checkAbiVersion(
header.e_ident))
276 return QLibrary::tr(
"file has an unexpected ABI");
278 return QLibrary::tr(
"file is not a shared object");
279 if (!checkMachine(
header))
280 return QLibrary::tr(
"file is for a different processor");
284 static CommonHeader extractCommonHeader(
const uchar *
data)
286 auto header =
reinterpret_cast<const Ehdr *
>(
data);
288 r.type = EndianTraits::fromEndian(
header->e_type);
289 r.machine = EndianTraits::fromEndian(
header->e_machine);
290 r.version = EndianTraits::fromEndian(
header->e_version);
295struct ElfHeaderDebug {
const uchar *e_ident; };
298 const uchar *e_ident =
h.e_ident;
299 if (!ElfHeaderCommonCheck::checkElfMagic(e_ident)) {
300 d <<
"Not an ELF file (invalid signature)";
306 quint8 elfclass = e_ident[EI_CLASS];
310 d <<
"Invalid ELF file (class " << e_ident[EI_CLASS] <<
"), ";
320 quint8 dataorder = e_ident[EI_DATA];
324 d <<
"invalid endianness (" << e_ident[EI_DATA] <<
')';
334 switch (e_ident[EI_OSABI]) {
335 case ELFOSABI_SYSV:
d <<
" (SYSV";
break;
336 case ELFOSABI_HPUX:
d <<
" (HP-UX";
break;
337 case ELFOSABI_NETBSD:
d <<
" (NetBSD";
break;
338 case ELFOSABI_LINUX:
d <<
" (GNU/Linux";
break;
339 case ELFOSABI_SOLARIS:
d <<
" (Solaris";
break;
340 case ELFOSABI_AIX:
d <<
" (AIX";
break;
341 case ELFOSABI_IRIX:
d <<
" (IRIX";
break;
342 case ELFOSABI_FREEBSD:
d <<
" (FreeBSD";
break;
343 case ELFOSABI_OPENBSD:
d <<
" (OpenBSD";
break;
344 default:
d <<
" (OS ABI " << e_ident[EI_VERSION];
break;
347 if (e_ident[EI_ABIVERSION])
348 d <<
" v" << e_ident[EI_ABIVERSION];
351 if (e_ident[EI_VERSION] != 1) {
352 d <<
", file version " << e_ident[EI_VERSION];
356 ElfHeaderCommonCheck::CommonHeader
r;
357 if (elfclass == ELFCLASS64 && dataorder == ELFDATA2LSB)
358 r = ElfHeaderCheck<quint64, QSysInfo::LittleEndian>::extractCommonHeader(e_ident);
359 else if (elfclass == ELFCLASS32 && dataorder == ELFDATA2LSB)
360 r = ElfHeaderCheck<quint32, QSysInfo::LittleEndian>::extractCommonHeader(e_ident);
361 else if (elfclass == ELFCLASS64 && dataorder == ELFDATA2MSB)
362 r = ElfHeaderCheck<quint64, QSysInfo::BigEndian>::extractCommonHeader(e_ident);
363 else if (elfclass == ELFCLASS32 && dataorder == ELFDATA2MSB)
364 r = ElfHeaderCheck<quint32, QSysInfo::BigEndian>::extractCommonHeader(e_ident);
368 d <<
", version " <<
r.version;
371 case ET_NONE:
d <<
", no type";
break;
372 case ET_REL:
d <<
", relocatable";
break;
373 case ET_EXEC:
d <<
", executable";
break;
374 case ET_DYN:
d <<
", shared library or PIC executable";
break;
375 case ET_CORE:
d <<
", core dump";
break;
376 default:
d <<
", unknown type " <<
r.type;
break;
381 case EM_NONE:
d <<
", no machine";
break;
382 case EM_ALPHA:
d <<
", Alpha";
break;
383 case EM_68K:
d <<
", MC68000";
break;
384 case EM_ARM:
d <<
", ARM";
break;
385 case EM_AARCH64:
d <<
", AArch64";
break;
387 case EM_BLACKFIN:
d <<
", Blackfin";
break;
389 case EM_IA_64:
d <<
", IA-64";
break;
391 case EM_LOONGARCH:
d <<
", LoongArch";
break;
393 case EM_MIPS:
d <<
", MIPS";
break;
394 case EM_PARISC:
d <<
", HPPA";
break;
395 case EM_PPC:
d <<
", PowerPC";
break;
396 case EM_PPC64:
d <<
", PowerPC 64-bit";
break;
398 case EM_RISCV:
d <<
", RISC-V";
break;
401 case EM_S390:
d <<
", S/390";
break;
403 case EM_SH:
d <<
", SuperH";
break;
404 case EM_SPARC:
d <<
", SPARC";
break;
405 case EM_SPARCV9:
d <<
", SPARCv9";
break;
406 case EM_386:
d <<
", i386";
break;
407 case EM_X86_64:
d <<
", x86-64";
break;
408 default:
d <<
", other machine type " <<
r.machine;
break;
414struct ElfSectionDebug {
const ElfHeaderCheck<>::TypeTraits::Shdr *shdr; };
421 switch (
s.shdr->sh_type) {
422 case SHT_NULL:
d <<
"NULL";
break;
423 case SHT_PROGBITS:
d <<
"PROGBITS";
break;
424 case SHT_SYMTAB:
d <<
"SYMTAB";
break;
425 case SHT_STRTAB:
d <<
"STRTAB";
break;
426 case SHT_RELA:
d <<
"RELA";
break;
427 case SHT_HASH:
d <<
"HASH";
break;
428 case SHT_DYNAMIC:
d <<
"DYNAMIC";
break;
429 case SHT_NOTE:
d <<
"NOTE";
break;
430 case SHT_NOBITS:
d <<
"NOBITS";
break;
431 case SHT_DYNSYM:
d <<
"DYNSYM";
break;
432 case SHT_INIT_ARRAY:
d <<
"INIT_ARRAY";
break;
433 case SHT_FINI_ARRAY:
d <<
"FINI_ARRAY";
break;
434 default:
d <<
s.shdr->sh_type;
439 if (
s.shdr->sh_flags & SHF_WRITE)
441 if (
s.shdr->sh_flags & SHF_ALLOC)
443 if (
s.shdr->sh_flags & SHF_EXECINSTR)
445 if (
s.shdr->sh_flags & SHF_STRINGS)
447 if (
s.shdr->sh_flags & SHF_TLS)
450 d.space() <<
"offset" <<
s.shdr->sh_offset <<
"size" <<
s.shdr->sh_size;
454struct ElfProgramDebug {
const ElfHeaderCheck<>::TypeTraits::Phdr *phdr; };
459 switch (
p.phdr->p_type) {
460 case PT_NULL:
d <<
"NULL";
break;
461 case PT_LOAD:
d <<
"LOAD";
break;
462 case PT_DYNAMIC:
d <<
"DYNAMIC";
break;
463 case PT_INTERP:
d <<
"INTERP";
break;
464 case PT_NOTE:
d <<
"NOTE";
break;
465 case PT_PHDR:
d <<
"PHDR";
break;
466 case PT_TLS:
d <<
"TLS";
break;
467 case PT_GNU_EH_FRAME:
d <<
"GNU_EH_FRAME";
break;
468 case PT_GNU_STACK:
d <<
"GNU_STACK";
break;
469 case PT_GNU_RELRO:
d <<
"GNU_RELRO";
break;
470 case PT_GNU_PROPERTY:
d <<
"GNU_PROPERTY";
break;
471 default:
d <<
"type" <<
p.phdr->p_type;
break;
474 d <<
"offset" <<
p.phdr->p_offset
475 <<
"virtaddr" <<
p.phdr->p_vaddr
476 <<
"filesz" <<
p.phdr->p_filesz
477 <<
"memsz" <<
p.phdr->p_memsz
478 <<
"align" <<
p.phdr->p_align
482 if (
p.phdr->p_flags & PF_R)
484 if (
p.phdr->p_flags & PF_W)
486 if (
p.phdr->p_flags & PF_X)
500 *
errMsg = QLibrary::tr(
"'%1' is not a valid ELF object (%2)").
arg(*errMsg, std::move(
text));
506 *
errMsg = QLibrary::tr(
"'%1' is not a Qt plugin (%2)").
arg(*errMsg, explanation);
512 return notplugin(QLibrary::tr(
"metadata not found"));
519using T = ElfHeaderCheck<>::TypeTraits;
524 auto header =
reinterpret_cast<const T::Ehdr *
>(
data.data());
527 auto phdr =
reinterpret_cast<const T::Phdr *
>(
data.data() +
header->e_phoff);
528 auto phdr_end = phdr +
header->e_phnum;
529 for ( ; phdr != phdr_end; ++phdr) {
538 auto header =
reinterpret_cast<const T::Ehdr *
>(
data.data());
541 T::Word e_phnum =
header->e_phnum;
542 T::Off
offset = e_phnum *
sizeof(T::Phdr);
544 return error(QLibrary::tr(
"program header table extends past the end of the file")),
false;
547 bool hasCode =
false;
548 auto checker = [&](
const T::Phdr *phdr) {
549 qEDebug << ElfProgramDebug{phdr};
553 return error(QLibrary::tr(
"a program header entry extends past the end of the file")),
false;
556 if (phdr->p_type == PT_LOAD && phdr->p_filesz != 0 && (phdr->p_flags & PF_X))
561 && phdr->p_offset & (phdr->p_align - 1)) {
562 return error(QLibrary::tr(
"a note segment start is not properly aligned "
563 "(offset 0x%1, alignment %2)")
564 .
arg(phdr->p_offset, 6, 16,
QChar(u
'0'))
565 .
arg(phdr->p_align)),
false;
570 if (!scanProgramHeaders(
data,
error, checker))
573 return error.notplugin(QLibrary::tr(
"file has no code")),
false;
585 static_assert(MinNoteSize > PayloadStartDelta);
586 static_assert((PayloadStartDelta & (NoteAlignment - 1)) == 0);
589 auto noteFinder = [&](
const T::Phdr *phdr) {
590 if (phdr->p_type != PT_NOTE || phdr->p_align != NoteAlignment)
596 auto h =
reinterpret_cast<const T::Ehdr *
>(
data.data());
597 auto segments =
reinterpret_cast<const T::Phdr *
>(
data.data() +
h->e_phoff);
598 qEDebug <<
"segment" << (phdr -
segments) <<
"contains a note with size"
600 <<
"which is larger than half the virtual memory space";
605 T::Off
offset = phdr->p_offset;
606 const T::Off end_offset =
offset + phdr->p_filesz;
608 auto nhdr =
reinterpret_cast<const T::Nhdr *
>(
data.data() +
offset);
609 T::Word n_namesz = nhdr->n_namesz;
610 T::Word n_descsz = nhdr->n_descsz;
611 T::Word n_type = nhdr->n_type;
614 T::Off next_offset =
offset;
615 next_offset +=
sizeof(T::Nhdr);
616 next_offset += NoteAlignment - 3;
617 if (qAddOverflow<T::Off>(next_offset, n_namesz, &next_offset))
619 next_offset &= -NoteAlignment;
621 next_offset += NoteAlignment - 3;
622 if (qAddOverflow<T::Off>(next_offset, n_descsz, &next_offset))
624 next_offset &= -NoteAlignment;
625 if (next_offset > end_offset)
628 if (n_namesz == NoteNameSize && n_descsz >= MinPayloadSize
632 r.pos =
offset + PayloadStartDelta;
633 r.length = nhdr->n_descsz;
640 scanProgramHeaders(
data,
error, noteFinder);
645 qEDebug <<
"found Qt metadata in ELF note at"
652 auto header =
reinterpret_cast<const T::Ehdr *
>(
data.data());
658 T::Word e_shnum =
header->e_shnum;
659 T::Off
offset = e_shnum *
sizeof(T::Shdr);
661 return error(QLibrary::tr(
"section table extends past the end of the file"));
665 auto sections =
reinterpret_cast<const T::Shdr *
>(
data.data() +
header->e_shoff);
666 auto sections_end = sections + e_shnum;
667 auto shdr = sections +
header->e_shstrndx;
671 T::Off shstrtab_size = shdr->sh_size;
672 qEDebug <<
"shstrtab section is located at offset" <<
offset <<
"size" << shstrtab_size;
673 if (T::Off
end; qAddOverflow<T::Off>(
offset, shstrtab_size, &
end)
675 return error(QLibrary::tr(
"section header string table extends past the end of the file"));
678 const char *shstrtab_start =
data.data() +
offset;
680 for (
int section = 0; shdr != sections_end; ++section, ++shdr) {
682 if (shdr->sh_name < shstrtab_size) {
683 const char *namestart = shstrtab_start + shdr->sh_name;
684 size_t len =
qstrnlen(namestart, shstrtab_size - shdr->sh_name);
687 qEDebug <<
"section" << section <<
"name" <<
name << ElfSectionDebug{shdr};
691 return error(QLibrary::tr(
"a section name extends past the end of the file"));
695 if (shdr->sh_type == SHT_NOBITS)
698 ||
end > size_t(
data.size())) {
699 return error(QLibrary::tr(
"section contents extend past the end of the file"));
702 if (
name !=
".qtmetadata"_L1)
704 qEDebug <<
"found .qtmetadata section";
706 return error(QLibrary::tr(
".qtmetadata section is too small"));
711 if (expectedMagic != actualMagic)
712 return error(QLibrary::tr(
".qtmetadata section has incorrect magic"));
714 if (shdr->sh_flags & SHF_WRITE)
715 return error(QLibrary::tr(
".qtmetadata section is writable"));
716 if (shdr->sh_flags & SHF_EXECINSTR)
717 return error(QLibrary::tr(
".qtmetadata section is executable"));
725 return error.notfound();
731 if (
size_t(
data.size()) <
sizeof(T::Ehdr)) {
732 qEDebug <<
"file too small:" << size_t(
data.size());
733 return error(QLibrary::tr(
"file too small"));
736 qEDebug << ElfHeaderDebug{
reinterpret_cast<const uchar *
>(
data.data()) };
738 auto header =
reinterpret_cast<const T::Ehdr *
>(
data.data());
739 if (!ElfHeaderCheck<>::checkHeader(*
header))
740 return error(ElfHeaderCheck<>::explainCheckFailure(*
header));
742 qEDebug <<
"contains" <<
header->e_phnum <<
"program headers of"
743 <<
header->e_phentsize <<
"bytes at offset" <<
header->e_phoff;
744 qEDebug <<
"contains" <<
header->e_shnum <<
"sections of" <<
header->e_shentsize
745 <<
"bytes at offset" <<
header->e_shoff
746 <<
"; section header string table (shstrtab) is entry" <<
header->e_shstrndx;
750 if (
header->e_phentsize !=
sizeof(T::Phdr))
751 return error(QLibrary::tr(
"unexpected program header entry size (%1)")
752 .arg(
header->e_phentsize));
761 if (!ElfNotesAreMandatory) {
763 if (
header->e_shentsize !=
sizeof(T::Shdr))
764 return error(QLibrary::tr(
"unexpected section entry size (%1)")
765 .arg(
header->e_shentsize));
769 qEDebug <<
"no section table present, not able to find Qt metadata";
770 return error.notfound();
774 return error(QLibrary::tr(
"e_shstrndx greater than the number of sections e_shnum (%1 >= %2)")
778 return error.notfound();
constexpr QByteArrayView sliced(qsizetype pos) const
constexpr qsizetype size() const noexcept
static constexpr QByteArrayView fromArray(const Byte(&data)[Size]) noexcept
\macro QT_RESTRICTED_CAST_FROM_ASCII
QString arg(qlonglong a, int fieldwidth=0, int base=10, QChar fillChar=u' ') const
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_COLD_FUNCTION
#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)
constexpr T qFromBigEndian(T source)
qsizetype QString * errMsg
#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)
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum GLuint GLintptr offset
GLfloat GLfloat GLfloat GLfloat h
#define QT_VERSION_CHECK(major, minor, patch)