// Copyright (C) 2017 The Qt Company Ltd. // Copyright (C) 2021 Intel Corporation. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qelfparser_p.h" #ifdef Q_OF_ELF #include "qlibrary_p.h" #include #include #include #if __has_include() # include #elif __has_include() # include #else # error "Need ELF header to parse plugins." #endif QT_BEGIN_NAMESPACE using namespace Qt::StringLiterals; // ### Qt7: propagate the constant and eliminate dead code static constexpr bool ElfNotesAreMandatory = QT_VERSION >= QT_VERSION_CHECK(7,0,0); // Whether we include some extra validity checks // (checks to ensure we don't read out-of-bounds are always included) static constexpr bool IncludeValidityChecks = true; #ifdef QT_BUILD_INTERNAL # define QELFPARSER_DEBUG #endif #if defined(QELFPARSER_DEBUG) static Q_LOGGING_CATEGORY(lcElfParser, "qt.core.plugin.elfparser") # define qEDebug qCDebug(lcElfParser) << reinterpret_cast(error.errMsg->constData()) << ':' #else # define qEDebug if (false) {} else QNoDebug() #endif #ifndef PT_GNU_EH_FRAME # define PT_GNU_EH_FRAME 0x6474e550 #endif #ifndef PT_GNU_STACK # define PT_GNU_STACK 0x6474e551 #endif #ifndef PT_GNU_RELRO # define PT_GNU_RELRO 0x6474e552 #endif #ifndef PT_GNU_PROPERTY # define PT_GNU_PROPERTY 0x6474e553 #endif QT_WARNING_PUSH QT_WARNING_DISABLE_CLANG("-Wunused-const-variable") namespace { template struct ElfEndianTraits { static constexpr unsigned char DataOrder = ELFDATA2LSB; template static T fromEndian(T value) { return qFromLittleEndian(value); } }; template <> struct ElfEndianTraits { static constexpr unsigned char DataOrder = ELFDATA2MSB; template static T fromEndian(T value) { return qFromBigEndian(value); } }; template struct ElfTypeTraits { static constexpr unsigned char Class = ELFCLASS64; // integer types using Half = Elf64_Half; using Word = Elf64_Word; using Addr = Elf64_Addr; using Off = Elf64_Off; // structure types using Ehdr = Elf64_Ehdr; using Shdr = Elf64_Shdr; using Phdr = Elf64_Phdr; using Nhdr = Elf64_Nhdr; }; template <> struct ElfTypeTraits { static constexpr unsigned char Class = ELFCLASS32; // integer types using Half = Elf32_Half; using Word = Elf32_Word; using Addr = Elf32_Addr; using Off = Elf32_Off; // structure types using Ehdr = Elf32_Ehdr; using Shdr = Elf32_Shdr; using Phdr = Elf32_Phdr; using Nhdr = Elf32_Nhdr; }; struct ElfMachineCheck { static const Elf32_Half ExpectedMachine = #if 0 // nothing #elif defined(Q_PROCESSOR_ARM_32) EM_ARM #elif defined(Q_PROCESSOR_ARM_64) EM_AARCH64 #elif defined(Q_PROCESSOR_BLACKFIN) EM_BLACKFIN #elif defined(Q_PROCESSOR_IA64) EM_IA_64 #elif defined(Q_PROCESSOR_MIPS) EM_MIPS #elif defined(Q_PROCESSOR_POWER_32) EM_PPC #elif defined(Q_PROCESSOR_POWER_64) EM_PPC64 #elif defined(Q_PROCESSOR_RISCV) EM_RISCV #elif defined(Q_PROCESSOR_S390) EM_S390 #elif defined(Q_PROCESSOR_SH) EM_SH #elif defined(Q_PROCESSOR_SPARC_V9) # warning "Please confirm that this is correct for Linux and Solaris" EM_SPARCV9 #elif defined(Q_PROCESSOR_SPARC_64) # warning "Please confirm that this is correct for Linux and Solaris" EM_SPARCV9 #elif defined(Q_PROCESSOR_SPARC) EM_SPARC #elif defined(Q_PROCESSOR_WASM) #elif defined(Q_PROCESSOR_X86_32) EM_386 #elif defined(Q_PROCESSOR_X86_64) EM_X86_64 #else # error "Unknown Q_PROCESSOR_xxx macro, please update." EM_NONE #endif ; }; struct ElfHeaderCommonCheck { static_assert(std::is_same_v, "e_ident field is not the same in both Elf32_Ehdr and Elf64_Ehdr"); // bytes 0-3 static bool checkElfMagic(const uchar *ident) { return memcmp(ident, ELFMAG, SELFMAG) == 0; } // byte 6 static bool checkElfVersion(const uchar *ident) { uchar elfversion = ident[EI_VERSION]; return elfversion == EV_CURRENT; } struct CommonHeader { Elf32_Half type; Elf32_Half machine; Elf32_Word version; }; }; template struct ElfHeaderCheck : public ElfHeaderCommonCheck { using TypeTraits = ElfTypeTraits; using EndianTraits = ElfEndianTraits; using Ehdr = typename TypeTraits::Ehdr; // byte 4 static bool checkClass(const uchar *ident) { uchar klass = ident[EI_CLASS]; return klass == TypeTraits::Class; } // byte 5 static bool checkDataOrder(const uchar *ident) { uchar data = ident[EI_DATA]; return data == EndianTraits::DataOrder; } // byte 7 static bool checkOsAbi(const uchar *ident) { uchar osabi = ident[EI_OSABI]; // we don't check Q_UNUSED(osabi); return true; } // byte 8 static bool checkAbiVersion(const uchar *ident) { uchar abiversion = ident[EI_ABIVERSION]; // we don't check (and I don't know anyone who uses this) Q_UNUSED(abiversion); return true; } // bytes 9-16 static bool checkPadding(const uchar *ident) { // why would we check this? Q_UNUSED(ident); return true; } static bool checkIdent(const Ehdr &header) { return checkElfMagic(header.e_ident) && checkClass(header.e_ident) && checkDataOrder(header.e_ident) && checkElfVersion(header.e_ident) && checkOsAbi(header.e_ident) && checkAbiVersion(header.e_ident) && checkPadding(header.e_ident); } static bool checkType(const Ehdr &header) { return header.e_type == ET_DYN; } static bool checkMachine(const Ehdr &header) { return header.e_machine == ElfMachineCheck::ExpectedMachine; } static bool checkFileVersion(const Ehdr &header) { return header.e_version == EV_CURRENT; } static bool checkHeader(const Ehdr &header) { if (!checkIdent(header)) return false; if (!IncludeValidityChecks) return true; return checkType(header) && checkMachine(header) && checkFileVersion(header); } Q_DECL_COLD_FUNCTION static QString explainCheckFailure(const Ehdr &header) { if (!checkElfMagic(header.e_ident)) return QLibrary::tr("invalid signature"); if (!checkClass(header.e_ident)) return QLibrary::tr("file is for a different word size"); if (!checkDataOrder(header.e_ident)) return QLibrary::tr("file is for the wrong endianness"); if (!checkElfVersion(header.e_ident) || !checkFileVersion(header)) return QLibrary::tr("file has an unknown ELF version"); if (!checkOsAbi(header.e_ident) || !checkAbiVersion(header.e_ident)) return QLibrary::tr("file has an unexpected ABI"); if (!checkType(header)) return QLibrary::tr("file is not a shared object"); if (!checkMachine(header)) return QLibrary::tr("file is for a different processor"); return QString(); } static CommonHeader extractCommonHeader(const uchar *data) { auto header = reinterpret_cast(data); CommonHeader r; r.type = EndianTraits::fromEndian(header->e_type); r.machine = EndianTraits::fromEndian(header->e_machine); r.version = EndianTraits::fromEndian(header->e_version); return r; } }; struct ElfHeaderDebug { const uchar *e_ident; }; Q_DECL_UNUSED Q_DECL_COLD_FUNCTION static QDebug &operator<<(QDebug &d, ElfHeaderDebug h) { const uchar *e_ident = h.e_ident; if (!ElfHeaderCommonCheck::checkElfMagic(e_ident)) { d << "Not an ELF file (invalid signature)"; return d; } QDebugStateSaver saver(d); d.nospace(); quint8 elfclass = e_ident[EI_CLASS]; switch (elfclass) { case ELFCLASSNONE: default: d << "Invalid ELF file (class " << e_ident[EI_CLASS] << "), "; break; case ELFCLASS32: d << "ELF 32-bit "; break; case ELFCLASS64: d << "ELF 64-bit "; break; } quint8 dataorder = e_ident[EI_DATA]; switch (dataorder) { case ELFDATANONE: default: d << "invalid endianness (" << e_ident[EI_DATA] << ')'; break; case ELFDATA2LSB: d << "LSB"; break; case ELFDATA2MSB: d << "MSB"; break; } switch (e_ident[EI_OSABI]) { case ELFOSABI_SYSV: d << " (SYSV"; break; case ELFOSABI_HPUX: d << " (HP-UX"; break; case ELFOSABI_NETBSD: d << " (NetBSD"; break; case ELFOSABI_GNU: d << " (GNU/Linux"; break; case ELFOSABI_SOLARIS: d << " (Solaris"; break; case ELFOSABI_AIX: d << " (AIX"; break; case ELFOSABI_IRIX: d << " (IRIX"; break; case ELFOSABI_FREEBSD: d << " (FreeBSD"; break; case ELFOSABI_OPENBSD: d << " (OpenBSD"; break; default: d << " (OS ABI " << e_ident[EI_VERSION]; break; } if (e_ident[EI_ABIVERSION]) d << " v" << e_ident[EI_ABIVERSION]; d << ')'; if (e_ident[EI_VERSION] != 1) { d << ", file version " << e_ident[EI_VERSION]; return d; } ElfHeaderCommonCheck::CommonHeader r; if (elfclass == ELFCLASS64 && dataorder == ELFDATA2LSB) r = ElfHeaderCheck::extractCommonHeader(e_ident); else if (elfclass == ELFCLASS32 && dataorder == ELFDATA2LSB) r = ElfHeaderCheck::extractCommonHeader(e_ident); else if (elfclass == ELFCLASS64 && dataorder == ELFDATA2MSB) r = ElfHeaderCheck::extractCommonHeader(e_ident); else if (elfclass == ELFCLASS32 && dataorder == ELFDATA2MSB) r = ElfHeaderCheck::extractCommonHeader(e_ident); else return d; d << ", version " << r.version; switch (r.type) { case ET_NONE: d << ", no type"; break; case ET_REL: d << ", relocatable"; break; case ET_EXEC: d << ", executable"; break; case ET_DYN: d << ", shared library or PIC executable"; break; case ET_CORE: d << ", core dump"; break; default: d << ", unknown type " << r.type; break; } switch (r.machine) { // list definitely not exhaustive! case EM_NONE: d << ", no machine"; break; case EM_ARM: d << ", ARM"; break; case EM_AARCH64: d << ", AArch64"; break; #ifdef EM_BLACKFIN case EM_BLACKFIN: d << ", Blackfin"; break; #endif case EM_IA_64: d << ", IA-64"; break; case EM_MIPS: d << ", MIPS"; break; case EM_PPC: d << ", PowerPC"; break; case EM_PPC64: d << ", PowerPC 64-bit"; break; #ifdef EM_RISCV case EM_RISCV: d << ", RISC-V"; break; #endif case EM_S390: d << ", S/390"; break; case EM_SH: d << ", SuperH"; break; case EM_SPARC: d << ", SPARC"; break; case EM_SPARCV9: d << ", SPARCv9"; break; case EM_386: d << ", i386"; break; case EM_X86_64: d << ", x86-64"; break; default: d << ", other machine type " << r.machine; break; } return d; } struct ElfSectionDebug { const ElfHeaderCheck<>::TypeTraits::Shdr *shdr; }; Q_DECL_UNUSED static QDebug &operator<<(QDebug &d, ElfSectionDebug s) { // not exhaustive, just a few common things QDebugStateSaver saver(d); d << Qt::hex << Qt::showbase; d << "type"; switch (s.shdr->sh_type) { case SHT_NULL: d << "NULL"; break; case SHT_PROGBITS: d << "PROGBITS"; break; case SHT_SYMTAB: d << "SYMTAB"; break; case SHT_STRTAB: d << "STRTAB"; break; case SHT_RELA: d << "RELA"; break; case SHT_HASH: d << "HASH"; break; case SHT_DYNAMIC: d << "DYNAMIC"; break; case SHT_NOTE: d << "NOTE"; break; case SHT_NOBITS: d << "NOBITS"; break; case SHT_DYNSYM: d << "DYNSYM"; break; case SHT_INIT_ARRAY: d << "INIT_ARRAY"; break; case SHT_FINI_ARRAY: d << "FINI_ARRAY"; break; default: d << s.shdr->sh_type; } d << "flags"; d.nospace(); if (s.shdr->sh_flags & SHF_WRITE) d << 'W'; if (s.shdr->sh_flags & SHF_ALLOC) d << 'A'; if (s.shdr->sh_flags & SHF_EXECINSTR) d << 'X'; if (s.shdr->sh_flags & SHF_STRINGS) d << 'S'; if (s.shdr->sh_flags & SHF_TLS) d << 'T'; d.space() << "offset" << s.shdr->sh_offset << "size" << s.shdr->sh_size; return d; } struct ElfProgramDebug { const ElfHeaderCheck<>::TypeTraits::Phdr *phdr; }; Q_DECL_UNUSED static QDebug &operator<<(QDebug &d, ElfProgramDebug p) { QDebugStateSaver saved(d); d << Qt::hex << Qt::showbase << "program"; switch (p.phdr->p_type) { case PT_NULL: d << "NULL"; break; case PT_LOAD: d << "LOAD"; break; case PT_DYNAMIC: d << "DYNAMIC"; break; case PT_INTERP: d << "INTERP"; break; case PT_NOTE: d << "NOTE"; break; case PT_PHDR: d << "PHDR"; break; case PT_TLS: d << "TLS"; break; case PT_GNU_EH_FRAME: d << "GNU_EH_FRAME"; break; case PT_GNU_STACK: d << "GNU_STACK"; break; case PT_GNU_RELRO: d << "GNU_RELRO"; break; case PT_GNU_PROPERTY: d << "GNU_PROPERTY"; break; default: d << "type" << p.phdr->p_type; break; } d << "offset" << p.phdr->p_offset << "virtaddr" << p.phdr->p_vaddr << "filesz" << p.phdr->p_filesz << "memsz" << p.phdr->p_memsz << "align" << p.phdr->p_align << "flags"; d.nospace(); if (p.phdr->p_flags & PF_R) d << 'R'; if (p.phdr->p_flags & PF_W) d << 'W'; if (p.phdr->p_flags & PF_X) d << 'X'; return d; } struct ErrorMaker { QString *errMsg; constexpr ErrorMaker(QString *errMsg) : errMsg(errMsg) {} Q_DECL_COLD_FUNCTION QLibraryScanResult operator()(QString &&text) const { *errMsg = QLibrary::tr("'%1' is not a valid ELF object (%2)").arg(*errMsg, std::move(text)); return {}; } Q_DECL_COLD_FUNCTION QLibraryScanResult notplugin(QString &&explanation) const { *errMsg = QLibrary::tr("'%1' is not a Qt plugin (%2)").arg(*errMsg, explanation); return {}; } Q_DECL_COLD_FUNCTION QLibraryScanResult notfound() const { return notplugin(QLibrary::tr("metadata not found")); } }; } // unnamed namespace QT_WARNING_POP using T = ElfHeaderCheck<>::TypeTraits; template static bool scanProgramHeaders(QByteArrayView data, const ErrorMaker &error, F f) { auto header = reinterpret_cast(data.data()); Q_UNUSED(error); auto phdr = reinterpret_cast(data.data() + header->e_phoff); auto phdr_end = phdr + header->e_phnum; for ( ; phdr != phdr_end; ++phdr) { if (!f(phdr)) return false; } return true; } static bool preScanProgramHeaders(QByteArrayView data, const ErrorMaker &error) { auto header = reinterpret_cast(data.data()); // first, validate the extent of the full program header table T::Word e_phnum = header->e_phnum; T::Off offset = e_phnum * sizeof(T::Phdr); // can't overflow due to size of T::Half if (qAddOverflow(offset, header->e_phoff, &offset) || offset > size_t(data.size())) return error(QLibrary::tr("program header table extends past the end of the file")), false; // confirm validity bool hasCode = false; auto checker = [&](const T::Phdr *phdr) { qEDebug << ElfProgramDebug{phdr}; if (T::Off end; qAddOverflow(phdr->p_offset, phdr->p_filesz, &end) || end > size_t(data.size())) return error(QLibrary::tr("a program header entry extends past the end of the file")), false; // this is not a validity check, it's to exclude debug symbol files if (phdr->p_type == PT_LOAD && phdr->p_filesz != 0 && (phdr->p_flags & PF_X)) hasCode = true; // this probably applies to all segments, but we'll only apply it to notes if (phdr->p_type == PT_NOTE && qPopulationCount(phdr->p_align) == 1 && phdr->p_offset & (phdr->p_align - 1)) { return error(QLibrary::tr("a note segment start is not properly aligned " "(offset 0x%1, alignment %2)") .arg(phdr->p_offset, 6, 16, QChar(u'0')) .arg(phdr->p_align)), false; } return true; }; if (!scanProgramHeaders(data, error, checker)) return false; if (!hasCode) return error.notplugin(QLibrary::tr("file has no code")), false; return true; } static QLibraryScanResult scanProgramHeadersForNotes(QByteArrayView data, const ErrorMaker &error) { // minimum metadata payload is 2 bytes constexpr size_t MinPayloadSize = sizeof(QPluginMetaData::Header) + 2; constexpr qptrdiff MinNoteSize = sizeof(QPluginMetaData::ElfNoteHeader) + 2; constexpr size_t NoteNameSize = sizeof(QPluginMetaData::ElfNoteHeader::name); constexpr size_t NoteAlignment = alignof(QPluginMetaData::ElfNoteHeader); constexpr qptrdiff PayloadStartDelta = offsetof(QPluginMetaData::ElfNoteHeader, header); static_assert(MinNoteSize > PayloadStartDelta); static_assert((PayloadStartDelta & (NoteAlignment - 1)) == 0); QLibraryScanResult r = {}; auto noteFinder = [&](const T::Phdr *phdr) { if (phdr->p_type != PT_NOTE || phdr->p_align != NoteAlignment) return true; // check for signed integer overflows, to avoid issues with the // arithmetic below if (qptrdiff(phdr->p_filesz) < 0) { auto h = reinterpret_cast(data.data()); auto segments = reinterpret_cast(data.data() + h->e_phoff); qEDebug << "segment" << (phdr - segments) << "contains a note with size" << Qt::hex << Qt::showbase << phdr->p_filesz << "which is larger than half the virtual memory space"; return true; } // iterate over the notes in this segment T::Off offset = phdr->p_offset; const T::Off end_offset = offset + phdr->p_filesz; while (qptrdiff(end_offset - offset) >= MinNoteSize) { auto nhdr = reinterpret_cast(data.data() + offset); T::Word n_namesz = nhdr->n_namesz; T::Word n_descsz = nhdr->n_descsz; T::Word n_type = nhdr->n_type; // overflow check: calculate where the next note will be, if it exists T::Off next_offset = offset; next_offset += sizeof(T::Nhdr); // can't overflow (we checked above) next_offset += NoteAlignment - 3; // offset is aligned, this can't overflow if (qAddOverflow(next_offset, n_namesz, &next_offset)) break; next_offset &= -NoteAlignment; next_offset += NoteAlignment - 3; // offset is aligned, this can't overflow if (qAddOverflow(next_offset, n_descsz, &next_offset)) break; next_offset &= -NoteAlignment; if (next_offset > end_offset) break; if (n_namesz == NoteNameSize && n_descsz >= MinPayloadSize && n_type == QPluginMetaData::ElfNoteHeader::NoteType && memcmp(nhdr + 1, QPluginMetaData::ElfNoteHeader::NoteName, NoteNameSize) == 0) { // yes, it's our note r.pos = offset + PayloadStartDelta; r.length = nhdr->n_descsz; return false; } offset = next_offset; } return true; }; scanProgramHeaders(data, error, noteFinder); if (!r.length) return r; qEDebug << "found Qt metadata in ELF note at" << Qt::hex << Qt::showbase << r.pos << "size" << Qt::reset << r.length; return r; } static QLibraryScanResult scanSections(QByteArrayView data, const ErrorMaker &error) { auto header = reinterpret_cast(data.data()); // in order to find the .qtmetadata section, we need to: // a) find the section table // it's located at offset header->e_shoff // validate it T::Word e_shnum = header->e_shnum; T::Off offset = e_shnum * sizeof(T::Shdr); // can't overflow due to size of T::Half if (qAddOverflow(offset, header->e_shoff, &offset) || offset > size_t(data.size())) return error(QLibrary::tr("section table extends past the end of the file")); // b) find the section entry for the section header string table (shstrab) // it's a section whose entry is pointed by e_shstrndx auto sections = reinterpret_cast(data.data() + header->e_shoff); auto sections_end = sections + e_shnum; auto shdr = sections + header->e_shstrndx; // validate the shstrtab offset = shdr->sh_offset; T::Off shstrtab_size = shdr->sh_size; qEDebug << "shstrtab section is located at offset" << offset << "size" << shstrtab_size; if (T::Off end; qAddOverflow(offset, shstrtab_size, &end) || end > size_t(data.size())) return error(QLibrary::tr("section header string table extends past the end of the file")); // c) iterate over the sections to find .qtmetadata const char *shstrtab_start = data.data() + offset; shdr = sections; for (int section = 0; shdr != sections_end; ++section, ++shdr) { QLatin1StringView name; if (shdr->sh_name < shstrtab_size) { const char *namestart = shstrtab_start + shdr->sh_name; size_t len = qstrnlen(namestart, shstrtab_size - shdr->sh_name); name = QLatin1StringView(namestart, len); } qEDebug << "section" << section << "name" << name << ElfSectionDebug{shdr}; // sanity check the section if (name.isNull()) return error(QLibrary::tr("a section name extends past the end of the file")); // sections aren't allowed to extend past the end of the file, unless // they are NOBITS sections if (shdr->sh_type == SHT_NOBITS) continue;; if (T::Off end; qAddOverflow(shdr->sh_offset, shdr->sh_size, &end) || end > size_t(data.size())) { return error(QLibrary::tr("section contents extend past the end of the file")); } if (name != ".qtmetadata"_L1) continue; qEDebug << "found .qtmetadata section"; if (shdr->sh_size < sizeof(QPluginMetaData::MagicHeader)) return error(QLibrary::tr(".qtmetadata section is too small")); if (IncludeValidityChecks) { QByteArrayView expectedMagic = QByteArrayView::fromArray(QPluginMetaData::MagicString); QByteArrayView actualMagic = data.sliced(shdr->sh_offset, expectedMagic.size()); if (expectedMagic != actualMagic) return error(QLibrary::tr(".qtmetadata section has incorrect magic")); if (shdr->sh_flags & SHF_WRITE) return error(QLibrary::tr(".qtmetadata section is writable")); if (shdr->sh_flags & SHF_EXECINSTR) return error(QLibrary::tr(".qtmetadata section is executable")); } return { qsizetype(shdr->sh_offset + sizeof(QPluginMetaData::MagicString)), qsizetype(shdr->sh_size - sizeof(QPluginMetaData::MagicString)) }; } // section .qtmetadata not found return error.notfound(); } QLibraryScanResult QElfParser::parse(QByteArrayView data, QString *errMsg) { ErrorMaker error(errMsg); if (size_t(data.size()) < sizeof(T::Ehdr)) { qEDebug << "file too small:" << size_t(data.size()); return error(QLibrary::tr("file too small")); } qEDebug << ElfHeaderDebug{ reinterpret_cast(data.data()) }; auto header = reinterpret_cast(data.data()); if (!ElfHeaderCheck<>::checkHeader(*header)) return error(ElfHeaderCheck<>::explainCheckFailure(*header)); qEDebug << "contains" << header->e_phnum << "program headers of" << header->e_phentsize << "bytes at offset" << header->e_phoff; qEDebug << "contains" << header->e_shnum << "sections of" << header->e_shentsize << "bytes at offset" << header->e_shoff << "; section header string table (shstrtab) is entry" << header->e_shstrndx; // some sanity checks if constexpr (IncludeValidityChecks) { if (header->e_phentsize != sizeof(T::Phdr)) return error(QLibrary::tr("unexpected program header entry size (%1)") .arg(header->e_phentsize)); } if (!preScanProgramHeaders(data, error)) return {}; if (QLibraryScanResult r = scanProgramHeadersForNotes(data, error); r.length) return r; if (!ElfNotesAreMandatory) { if constexpr (IncludeValidityChecks) { if (header->e_shentsize != sizeof(T::Shdr)) return error(QLibrary::tr("unexpected section entry size (%1)") .arg(header->e_shentsize)); } if (header->e_shoff == 0 || header->e_shnum == 0) { // this is still a valid ELF file but we don't have a section table qEDebug << "no section table present, not able to find Qt metadata"; return error.notfound(); } if (header->e_shnum && header->e_shstrndx >= header->e_shnum) return error(QLibrary::tr("e_shstrndx greater than the number of sections e_shnum (%1 >= %2)") .arg(header->e_shstrndx).arg(header->e_shnum)); return scanSections(data, error); } return error.notfound(); } QT_END_NAMESPACE #endif // Q_OF_ELF