QElfParser: rewrite using elf.h
This rewrite uses the actual structures supplied by the system's C library, so it should be easier to read. It removes hardcoded constants with little evident meaning in favor of sizeof() and the macros from that header. It also removes advancing the data pointer in favor of having absolute offsets. The resulting implementation is stricter than the original, checking more fields in the header. Because the QPluginLoader and QFactoryLoader users may make decisions based on availability of plugins before attempting to load them, it's better to be stricter here than to fail later when trying to dlopen() them. Debugging and testing are much improved. Instead of stored artifacts, I added a routine to modify a valid plugin to make it invalid, given the conditions we've found so far. If you turn debugging on for this category, you'll see things like: not-elf.fcqdMq.so : Not an ELF file (invalid signature) wrong-word-size.QrnSAx.so : ELF 32-bit LSB (GNU/Linux), version 1, shared library or PIC executable, x86-64 invalid-word-size.bOkXvp.so : Invalid ELF file (class 0), LSB (GNU/Linux) unknown-word-size.ogYKeF.so : Invalid ELF file (class 66), LSB (GNU/Linux) wrong-endian.owiElX.so : ELF 64-bit MSB (GNU/Linux), version 1, shared library or PIC executable, x86-64 invalid-endian.FRxClR.so : ELF 64-bit invalid endianness (0) (GNU/Linux) unknown-endian.FfvRrP.so : ELF 64-bit invalid endianness (65) (GNU/Linux) elf-version-0.gPTdpQ.so : ELF 64-bit LSB (GNU/Linux), file version 0 elf-version-2.jlIUUg.so : ELF 64-bit LSB (GNU/Linux), file version 2 executable.LlXiFp.so : ELF 64-bit LSB (GNU/Linux), version 1, executable, x86-64 relocatable.UsOYuy.so : ELF 64-bit LSB (GNU/Linux), version 1, relocatable, x86-64 core-file.hqvNRz.so : ELF 64-bit LSB (GNU/Linux), version 1, core dump, x86-64 invalid-type.CIJgfS.so : ELF 64-bit LSB (GNU/Linux), version 1, unknown type 259, x86-64 wrong-arch.UcNmgz.so : ELF 64-bit LSB (GNU/Linux), version 1, shared library or PIC executable, AArch64 file-version-0.lZYuda.so : ELF 64-bit LSB (GNU/Linux), version 0, shared library or PIC executable, x86-64 file-version-2.ucfdwL.so : ELF 64-bit LSB (GNU/Linux), version 2, shared library or PIC executable, x86-64 no-sections.rSjsHh.so : ELF 64-bit LSB (GNU/Linux), version 1, shared library or PIC executable, x86-64 no-sections.rSjsHh.so : contains 0 sections of 64 bytes at offset 0 ; section header string table (shstrtab) is entry 0 no-sections.rSjsHh.so : no section table present, not able to find Qt metadata qtmetadata-executable.vrxcIf.so : ELF 64-bit LSB (GNU/Linux), version 1, shared library or PIC executable, x86-64 qtmetadata-executable.vrxcIf.so : contains 42 sections of 64 bytes at offset 997256 ; section header string table (shstrtab) is entry 41 qtmetadata-executable.vrxcIf.so : shstrtab section is located at offset 996831 size 423 qtmetadata-executable.vrxcIf.so : section 0 name "" type NULL flags X offset 0x0 size 0x0 qtmetadata-executable.vrxcIf.so : section 1 name ".note.gnu.property" type NOTE flags AX offset 0x2a8 size 0x30 qtmetadata-executable.vrxcIf.so : section 2 name ".note.gnu.build-id" type NOTE flags AX offset 0x2d8 size 0x24 qtmetadata-executable.vrxcIf.so : section 3 name ".hash" type HASH flags AX offset 0x300 size 0x44c qtmetadata-executable.vrxcIf.so : section 4 name ".gnu.hash" type 0x6ffffff6 flags AX offset 0x750 size 0x3b8 qtmetadata-executable.vrxcIf.so : section 5 name ".dynsym" type DYNSYM flags AX offset 0xb08 size 0xd50 qtmetadata-executable.vrxcIf.so : section 6 name ".dynstr" type STRTAB flags AX offset 0x1858 size 0x15d8 qtmetadata-executable.vrxcIf.so : section 7 name ".gnu.version" type 0x6fffffff flags AX offset 0x2e30 size 0x11c qtmetadata-executable.vrxcIf.so : section 8 name ".gnu.version_r" type 0x6ffffffe flags AX offset 0x2f50 size 0xb0 qtmetadata-executable.vrxcIf.so : section 9 name ".rela.dyn" type RELA flags AX offset 0x3000 size 0x480 qtmetadata-executable.vrxcIf.so : section 10 name ".rela.plt" type RELA flags AX offset 0x3480 size 0x7e0 qtmetadata-executable.vrxcIf.so : section 11 name ".init" type PROGBITS flags AX offset 0x4000 size 0x1b qtmetadata-executable.vrxcIf.so : section 12 name ".plt" type PROGBITS flags AX offset 0x4020 size 0x550 qtmetadata-executable.vrxcIf.so : section 13 name ".plt.got" type PROGBITS flags AX offset 0x4570 size 0x8 qtmetadata-executable.vrxcIf.so : section 14 name ".text" type PROGBITS flags AX offset 0x4580 size 0x110e qtmetadata-executable.vrxcIf.so : section 15 name ".fini" type PROGBITS flags AX offset 0x5690 size 0xd qtmetadata-executable.vrxcIf.so : section 16 name ".rodata" type PROGBITS flags AX offset 0x6000 size 0x473 qtmetadata-executable.vrxcIf.so : section 17 name ".qtversion" type PROGBITS flags AX offset 0x6478 size 0x10 qtmetadata-executable.vrxcIf.so : section 18 name ".qtmetadata" type PROGBITS flags AX offset 0x64a0 size 0x19b qtmetadata-executable.vrxcIf.so : found .qtmetadata section qtmetadata-writable.stzwrk.so : ELF 64-bit LSB (GNU/Linux), version 1, shared library or PIC executable, x86-64 Change-Id: I42eb903a916645db9900fffd16a4437af9728eea Reviewed-by: Lars Knoll <lars.knoll@qt.io>
This commit is contained in:
parent
3abcff49eb
commit
46fc01d7ca
@ -1,6 +1,7 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Copyright (C) 2017 The Qt Company Ltd.
|
||||
** Copyright (C) 2021 Intel Corporation.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtCore module of the Qt Toolkit.
|
||||
@ -39,189 +40,542 @@
|
||||
|
||||
#include "qelfparser_p.h"
|
||||
|
||||
#if defined (Q_OF_ELF) && defined(Q_CC_GNU)
|
||||
#if defined (Q_OF_ELF) && __has_include(<elf.h>)
|
||||
|
||||
#include "qlibrary_p.h"
|
||||
#include <qdebug.h>
|
||||
|
||||
#include <qloggingcategory.h>
|
||||
#include <qnumeric.h>
|
||||
#include <qsysinfo.h>
|
||||
|
||||
#include <elf.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
// #define QELFPARSER_DEBUG 1
|
||||
// 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;
|
||||
|
||||
const char *QElfParser::parseSectionHeader(const char *data, ElfSectionHeader *sh)
|
||||
#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<const char16_t *>(error.errMsg->constData()) << ':'
|
||||
#else
|
||||
# define qEDebug if (false) {} else QNoDebug()
|
||||
#endif
|
||||
|
||||
QT_WARNING_PUSH
|
||||
QT_WARNING_DISABLE_CLANG("-Wunused-const-variable")
|
||||
|
||||
namespace {
|
||||
template <QSysInfo::Endian Order> struct ElfEndianTraits
|
||||
{
|
||||
sh->name = qFromUnaligned<qelfword_t>(data);
|
||||
data += sizeof(qelfword_t); // sh_name
|
||||
sh->type = qFromUnaligned<qelfword_t>(data);
|
||||
data += sizeof(qelfword_t) // sh_type
|
||||
+ sizeof(qelfaddr_t) // sh_flags
|
||||
+ sizeof(qelfaddr_t); // sh_addr
|
||||
sh->offset = qFromUnaligned<qelfoff_t>(data);
|
||||
data += sizeof(qelfoff_t); // sh_offset
|
||||
sh->size = qFromUnaligned<qelfoff_t>(data);
|
||||
data += sizeof(qelfoff_t); // sh_size
|
||||
return data;
|
||||
static constexpr unsigned char DataOrder = ELFDATA2LSB;
|
||||
template <typename T> static T fromEndian(T value) { return qFromLittleEndian(value); }
|
||||
};
|
||||
template <> struct ElfEndianTraits<QSysInfo::BigEndian>
|
||||
{
|
||||
static constexpr unsigned char DataOrder = ELFDATA2MSB;
|
||||
template <typename T> static T fromEndian(T value) { return qFromBigEndian(value); }
|
||||
};
|
||||
|
||||
template <typename EquivalentPointerType> 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<quint32>
|
||||
{
|
||||
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<decltype(Elf32_Ehdr::e_ident), decltype(Elf64_Ehdr::e_ident)>,
|
||||
"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 <typename EquivalentPointerType = quintptr, QSysInfo::Endian Order = QSysInfo::ByteOrder>
|
||||
struct ElfHeaderCheck : public ElfHeaderCommonCheck
|
||||
{
|
||||
using TypeTraits = ElfTypeTraits<EquivalentPointerType>;
|
||||
using EndianTraits = ElfEndianTraits<Order>;
|
||||
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<const Ehdr *>(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<quint64, QSysInfo::LittleEndian>::extractCommonHeader(e_ident);
|
||||
else if (elfclass == ELFCLASS32 && dataorder == ELFDATA2LSB)
|
||||
r = ElfHeaderCheck<quint32, QSysInfo::LittleEndian>::extractCommonHeader(e_ident);
|
||||
else if (elfclass == ELFCLASS64 && dataorder == ELFDATA2MSB)
|
||||
r = ElfHeaderCheck<quint64, QSysInfo::BigEndian>::extractCommonHeader(e_ident);
|
||||
else if (elfclass == ELFCLASS32 && dataorder == ELFDATA2MSB)
|
||||
r = ElfHeaderCheck<quint32, QSysInfo::BigEndian>::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;
|
||||
case EM_BLACKFIN: d << ", Blackfin"; break;
|
||||
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;
|
||||
case EM_RISCV: d << ", RISC-V"; break;
|
||||
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;
|
||||
}
|
||||
|
||||
QLibraryScanResult QElfParser::parse(const char *dataStart, ulong fdlen, QString *errMsg)
|
||||
struct ElfSectionDebug { const ElfHeaderCheck<>::TypeTraits::Shdr *shdr; };
|
||||
Q_DECL_UNUSED static QDebug &operator<<(QDebug &d, ElfSectionDebug s)
|
||||
{
|
||||
#if defined(QELFPARSER_DEBUG)
|
||||
qDebug() << "QElfParser::parse " << library;
|
||||
#endif
|
||||
// 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;
|
||||
}
|
||||
|
||||
if (fdlen < 64) {
|
||||
*errMsg = QLibrary::tr("'%1' is not an ELF object (%2)").arg(*errMsg, QLibrary::tr("file too small"));
|
||||
return {};
|
||||
}
|
||||
const char *data = dataStart;
|
||||
if (qstrncmp(data, "\177ELF", 4) != 0) {
|
||||
*errMsg = QLibrary::tr("'%1' is not an ELF object").arg(*errMsg);
|
||||
return {};
|
||||
}
|
||||
// 32 or 64 bit
|
||||
if (data[4] != 1 && data[4] != 2) {
|
||||
*errMsg = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(*errMsg, QLibrary::tr("odd cpu architecture"));
|
||||
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 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 {};
|
||||
}
|
||||
|
||||
/* If you remove this check, to read ELF objects of a different arch, please make sure you modify the typedefs
|
||||
to match the _plugin_ architecture.
|
||||
*/
|
||||
constexpr int ExpectedClass = (sizeof(void *) == 4) ? 1 : 2;
|
||||
if (data[4] != ExpectedClass) {
|
||||
*errMsg = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(*errMsg, QLibrary::tr("wrong cpu architecture"));
|
||||
QLibraryScanResult notfound() const
|
||||
{
|
||||
*errMsg = QLibrary::tr("'%1' is not a Qt plugin (.qtmetadata section not found)")
|
||||
.arg(*errMsg);
|
||||
return {};
|
||||
}
|
||||
};
|
||||
} // unnamed namespace
|
||||
|
||||
// endian
|
||||
constexpr int ExpectedEndianness = (Q_BYTE_ORDER == Q_LITTLE_ENDIAN) ? 1 : 2;
|
||||
if (data[5] != ExpectedEndianness) {
|
||||
*errMsg = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(*errMsg, QLibrary::tr("odd endianness"));
|
||||
return {};
|
||||
}
|
||||
QT_WARNING_POP
|
||||
|
||||
data += 16 // e_ident
|
||||
+ sizeof(qelfhalf_t) // e_type
|
||||
+ sizeof(qelfhalf_t) // e_machine
|
||||
+ sizeof(qelfword_t) // e_version
|
||||
+ sizeof(qelfaddr_t) // e_entry
|
||||
+ sizeof(qelfoff_t); // e_phoff
|
||||
using T = ElfHeaderCheck<>::TypeTraits;
|
||||
|
||||
qelfoff_t e_shoff = qFromUnaligned<qelfoff_t> (data);
|
||||
data += sizeof(qelfoff_t) // e_shoff
|
||||
+ sizeof(qelfword_t); // e_flags
|
||||
static QLibraryScanResult scanSections(QByteArrayView data, const ErrorMaker &error)
|
||||
{
|
||||
auto header = reinterpret_cast<const T::Ehdr *>(data.data());
|
||||
|
||||
qelfhalf_t e_shsize = qFromUnaligned<qelfhalf_t> (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"));
|
||||
|
||||
if (e_shsize > fdlen) {
|
||||
*errMsg = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(*errMsg, QLibrary::tr("unexpected e_shsize"));
|
||||
return {};
|
||||
}
|
||||
// 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<const T::Shdr *>(data.data() + header->e_shoff);
|
||||
auto sections_end = sections + e_shnum;
|
||||
auto shdr = sections + header->e_shstrndx;
|
||||
|
||||
data += sizeof(qelfhalf_t) // e_ehsize
|
||||
+ sizeof(qelfhalf_t) // e_phentsize
|
||||
+ sizeof(qelfhalf_t); // e_phnum
|
||||
// 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<T::Off>(offset, shstrtab_size, &end)
|
||||
|| end > size_t(data.size()))
|
||||
return error(QLibrary::tr("section header string table extends past the end of the file"));
|
||||
|
||||
qelfhalf_t e_shentsize = qFromUnaligned<qelfhalf_t> (data);
|
||||
// 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) {
|
||||
QLatin1String 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 = QLatin1String(namestart, len);
|
||||
}
|
||||
qEDebug << "section" << section << "name" << name << ElfSectionDebug{shdr};
|
||||
|
||||
if (e_shentsize % 4) {
|
||||
*errMsg = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(*errMsg, QLibrary::tr("unexpected e_shentsize"));
|
||||
return {};
|
||||
}
|
||||
data += sizeof(qelfhalf_t); // e_shentsize
|
||||
qelfhalf_t e_shnum = qFromUnaligned<qelfhalf_t> (data);
|
||||
data += sizeof(qelfhalf_t); // e_shnum
|
||||
qelfhalf_t e_shtrndx = qFromUnaligned<qelfhalf_t> (data);
|
||||
data += sizeof(qelfhalf_t); // e_shtrndx
|
||||
// sanity check the section
|
||||
if (name.isNull())
|
||||
return error(QLibrary::tr("a section name extends past the end of the file"));
|
||||
|
||||
if ((quint32)(e_shnum * e_shentsize) > fdlen) {
|
||||
const QString message =
|
||||
QLibrary::tr("announced %n section(s), each %1 byte(s), exceed file size",
|
||||
nullptr, int(e_shnum)).arg(e_shentsize);
|
||||
*errMsg = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(*errMsg, message);
|
||||
return {};
|
||||
}
|
||||
// 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("a section data extends past the end of the file"));
|
||||
}
|
||||
|
||||
#if defined(QELFPARSER_DEBUG)
|
||||
qDebug() << e_shnum << "sections starting at " << ("0x" + QByteArray::number(e_shoff, 16)).data() << "each" << e_shentsize << "bytes";
|
||||
#endif
|
||||
|
||||
ElfSectionHeader strtab;
|
||||
qulonglong soff = e_shoff + qelfword_t(e_shentsize) * qelfword_t(e_shtrndx);
|
||||
|
||||
if ((soff + e_shentsize) > fdlen || soff % 4 || soff == 0) {
|
||||
*errMsg = QLibrary::tr("'%1' is an invalid ELF object (%2)")
|
||||
.arg(*errMsg, QLibrary::tr("shstrtab section header seems to be at %1")
|
||||
.arg(QString::number(soff, 16)));
|
||||
return {};
|
||||
}
|
||||
|
||||
parseSectionHeader(dataStart + soff, &strtab);
|
||||
m_stringTableFileOffset = strtab.offset;
|
||||
|
||||
if ((quint32)(strtab.offset + strtab.size) > fdlen || strtab.offset == 0) {
|
||||
*errMsg = QLibrary::tr("'%1' is an invalid ELF object (%2)")
|
||||
.arg(*errMsg, QLibrary::tr("string table seems to be at %1")
|
||||
.arg(QString::number(strtab.offset, 16)));
|
||||
return {};
|
||||
}
|
||||
|
||||
#if defined(QELFPARSER_DEBUG)
|
||||
qDebug(".shstrtab at 0x%s", QByteArray::number(m_stringTableFileOffset, 16).data());
|
||||
#endif
|
||||
|
||||
const char *s = dataStart + e_shoff;
|
||||
for (int i = 0; i < e_shnum; ++i) {
|
||||
ElfSectionHeader sh;
|
||||
parseSectionHeader(s, &sh);
|
||||
if (sh.name == 0) {
|
||||
s += e_shentsize;
|
||||
if (name != QLatin1String(".qtmetadata"))
|
||||
continue;
|
||||
qEDebug << "found .qtmetadata section";
|
||||
if (IncludeValidityChecks && shdr->sh_flags & (SHF_WRITE | SHF_EXECINSTR)) {
|
||||
if (shdr->sh_flags & SHF_WRITE)
|
||||
return error(QLibrary::tr(".qtmetadata section is writable"));
|
||||
return error(QLibrary::tr(".qtmetadata section is executable"));
|
||||
}
|
||||
const char *shnam = dataStart + m_stringTableFileOffset + sh.name;
|
||||
if (shdr->sh_size < sizeof(QPluginMetaData::MagicHeader))
|
||||
return error(QLibrary::tr("section .qtmetadata is too small"));
|
||||
|
||||
if (m_stringTableFileOffset + sh.name > fdlen) {
|
||||
*errMsg = QLibrary::tr("'%1' is an invalid ELF object (%2)")
|
||||
.arg(*errMsg, QLibrary::tr("section name %1 of %2 behind end of file")
|
||||
.arg(i).arg(e_shnum));
|
||||
return {};
|
||||
}
|
||||
|
||||
#if defined(QELFPARSER_DEBUG)
|
||||
qDebug() << "++++" << i << shnam;
|
||||
#endif
|
||||
|
||||
if (qstrcmp(shnam, ".qtmetadata") == 0 ) {
|
||||
if (!(sh.type & 0x1)) {
|
||||
if (shnam[1] == 'r') {
|
||||
*errMsg = QLibrary::tr("'%1' is an invalid ELF object (%2)")
|
||||
.arg(*errMsg, QLibrary::tr("empty .rodata. not a library."));
|
||||
return {};
|
||||
}
|
||||
#if defined(QELFPARSER_DEBUG)
|
||||
qDebug()<<"section is not program data. skipped.";
|
||||
#endif
|
||||
s += e_shentsize;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sh.offset == 0 || (sh.offset + sh.size) > fdlen || sh.size < 1) {
|
||||
*errMsg = QLibrary::tr("'%1' is an invalid ELF object (%2)")
|
||||
.arg(*errMsg, QLibrary::tr("missing section data. This is not a library."));
|
||||
return {};
|
||||
}
|
||||
if (sh.size < sizeof(QPluginMetaData::MagicHeader)) {
|
||||
*errMsg = QLibrary::tr("'%1' is an invalid ELF object (%2)")
|
||||
.arg(*errMsg, QLibrary::tr("section .qtmetadata is too small"));
|
||||
return {};
|
||||
}
|
||||
sh.offset += sizeof(QPluginMetaData::MagicString);
|
||||
sh.size -= sizeof(QPluginMetaData::MagicString);
|
||||
return { qsizetype(sh.offset), qsizetype(sh.size) };
|
||||
}
|
||||
s += e_shentsize;
|
||||
return { qsizetype(shdr->sh_offset + sizeof(QPluginMetaData::MagicString)),
|
||||
qsizetype(shdr->sh_size - sizeof(QPluginMetaData::MagicString)) };
|
||||
}
|
||||
return {};
|
||||
|
||||
// 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<const uchar *>(data.data()) };
|
||||
|
||||
auto header = reinterpret_cast<const T::Ehdr *>(data.data());
|
||||
if (!ElfHeaderCheck<>::checkHeader(*header))
|
||||
return error(ElfHeaderCheck<>::explainCheckFailure(*header));
|
||||
|
||||
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_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);
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -56,35 +56,13 @@
|
||||
|
||||
QT_REQUIRE_CONFIG(library);
|
||||
|
||||
#if defined(Q_OF_ELF) && defined(Q_CC_GNU)
|
||||
#if defined(Q_OF_ELF) && __has_include(<elf.h>)
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QString;
|
||||
class QLibraryPrivate;
|
||||
|
||||
typedef quint16 qelfhalf_t;
|
||||
typedef quint32 qelfword_t;
|
||||
typedef quintptr qelfoff_t;
|
||||
typedef quintptr qelfaddr_t;
|
||||
|
||||
class QElfParser
|
||||
struct QElfParser
|
||||
{
|
||||
public:
|
||||
enum { ElfLittleEndian = 0, ElfBigEndian = 1 };
|
||||
|
||||
struct ElfSectionHeader
|
||||
{
|
||||
qelfword_t name;
|
||||
qelfword_t type;
|
||||
qelfoff_t offset;
|
||||
qelfoff_t size;
|
||||
};
|
||||
|
||||
qelfoff_t m_stringTableFileOffset;
|
||||
|
||||
const char *parseSectionHeader(const char* s, ElfSectionHeader *sh);
|
||||
QLibraryScanResult parse(const char *m_s, ulong fdlen, QString *errMsg);
|
||||
static QLibraryScanResult parse(QByteArrayView data, QString *errMsg);
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -193,8 +193,8 @@ static QLibraryScanResult qt_find_pattern(const char *s, qsizetype s_len, QStrin
|
||||
More importantly, the pattern string may exist in the debug information due
|
||||
to it being used in the plugin in the first place.
|
||||
*/
|
||||
#if defined (Q_OF_ELF)
|
||||
return QElfParser().parse(s, s_len, errMsg);
|
||||
#if defined (Q_OF_ELF) && __has_include(<elf.h>)
|
||||
return QElfParser::parse({s, s_len}, errMsg);
|
||||
#elif defined(Q_OF_MACH_O)
|
||||
return QMachOParser::parse(s, s_len, errMsg);
|
||||
#endif
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,4 +0,0 @@
|
||||
p¶¤Ðã¨ø±ÕÛcdL+ôúæî&‘¿&÷ýe<C3BD>ü¥=კ²
|
||||
•o°Ã’ÊŽI›§Ù<C2A7>Ïmgƒ]!ÀZ
|
||||
L'Ž)t±
|
||||
ÙN»¸(e©×P)Y8öG ˆ6ß-yÈÏÀñŸ§÷—“"ô–ZÖÿ›kõ4â?Ë^<5E>náÁÇß5$ž’ôY=£ð#y
|
@ -1 +0,0 @@
|
||||
£Çv.³Y‹<59>¨tKëW3
|
@ -1 +0,0 @@
|
||||
£ÝÈÈ‚åžT-õ«´ÊôÚ¥ Àä¸ï¨ì¾œÀi<C380>8¼_ñxÓõª¾I±Ð×®ÝxÎ=úØ4@þñ[¨<>—úBàKS$ú
|
@ -1 +0,0 @@
|
||||
<EFBFBD>!<21>\~هUu<C2AD><75>:9<><39> <><1B>T<EFBFBD>+91<39>Q،E<>ما<D985>x<EFBFBD>ng5سك<D8B3>zh<13><><EFBFBD><EFBFBD>ئ^t<><1D><><EFBFBD><EFBFBD>'ئخmm*ث<>dXH;vw+<2B>G<EFBFBD><47>أـظ
<0A>9L<>0!
|
@ -1,2 +0,0 @@
|
||||
яЬє<EFBFBD>QВ
|
||||
уЕ-Ђ9в
|
@ -1,7 +1,7 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Copyright (C) 2018 Intel Corporation.
|
||||
** Copyright (C) 2020 The Qt Company Ltd.
|
||||
** Copyright (C) 2021 Intel Corporation.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the test suite of the Qt Toolkit.
|
||||
@ -31,7 +31,10 @@
|
||||
#include <QSignalSpy>
|
||||
#include <QJsonArray>
|
||||
#include <qdir.h>
|
||||
#include <qendian.h>
|
||||
#include <qpluginloader.h>
|
||||
#include <qprocess.h>
|
||||
#include <qtemporaryfile.h>
|
||||
#include "theplugin/plugininterface.h"
|
||||
|
||||
#if defined(QT_BUILD_INTERNAL) && defined(Q_OF_MACH_O)
|
||||
@ -91,6 +94,84 @@
|
||||
# define PREFIX "lib"
|
||||
#endif
|
||||
|
||||
#if defined(Q_OF_ELF)
|
||||
# include <elf.h>
|
||||
# include <memory>
|
||||
# include <functional>
|
||||
|
||||
# ifdef _LP64
|
||||
using ElfHeader = Elf64_Ehdr;
|
||||
using ElfShdr = Elf64_Shdr;
|
||||
# else
|
||||
using ElfHeader = Elf32_Ehdr;
|
||||
using ElfShdr = Elf32_Shdr;
|
||||
# endif
|
||||
|
||||
struct ElfPatcher
|
||||
{
|
||||
using FullPatcher = void(ElfHeader *, QFile *);
|
||||
FullPatcher *f;
|
||||
|
||||
ElfPatcher(FullPatcher *f = nullptr) : f(f) {}
|
||||
|
||||
template <typename T> using IsSingleArg = std::is_invocable<T, ElfHeader *>;
|
||||
template <typename T> static std::enable_if_t<IsSingleArg<T>::value, ElfPatcher> fromLambda(T &&t)
|
||||
{
|
||||
using WithoutQFile = void(*)(ElfHeader *);
|
||||
static const WithoutQFile f = t;
|
||||
return { [](ElfHeader *h, QFile *) { f(h);} };
|
||||
}
|
||||
template <typename T> static std::enable_if_t<!IsSingleArg<T>::value, ElfPatcher> fromLambda(T &&t)
|
||||
{
|
||||
return { t };
|
||||
}
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(ElfPatcher)
|
||||
|
||||
static std::unique_ptr<QTemporaryFile> patchElf(const QString &source, ElfPatcher patcher)
|
||||
{
|
||||
std::unique_ptr<QTemporaryFile> tmplib;
|
||||
|
||||
bool ok = false;
|
||||
[&]() {
|
||||
QFile srclib(source);
|
||||
QVERIFY2(srclib.open(QIODevice::ReadOnly), qPrintable(srclib.errorString()));
|
||||
qint64 srcsize = srclib.size();
|
||||
const uchar *srcdata = srclib.map(0, srcsize, QFile::MapPrivateOption);
|
||||
QVERIFY2(srcdata, qPrintable(srclib.errorString()));
|
||||
|
||||
// copy our source plugin so we can modify it
|
||||
tmplib.reset(new QTemporaryFile(QTest::currentDataTag() + QString(".XXXXXX" SUFFIX)));
|
||||
QVERIFY2(tmplib->open(), qPrintable(tmplib->errorString()));
|
||||
|
||||
// sanity-check
|
||||
QByteArray magic = QByteArray::fromRawData(reinterpret_cast<const char *>(srcdata), SELFMAG);
|
||||
QCOMPARE(magic, QByteArray(ELFMAG));
|
||||
|
||||
// copy everything via mmap()
|
||||
QVERIFY2(tmplib->resize(srcsize), qPrintable(tmplib->errorString()));
|
||||
uchar *dstdata = tmplib->map(0, srcsize);
|
||||
memcpy(dstdata, srcdata, srcsize);
|
||||
|
||||
// now patch the file
|
||||
patcher.f(reinterpret_cast<ElfHeader *>(dstdata), tmplib.get());
|
||||
|
||||
ok = true;
|
||||
}();
|
||||
if (!ok)
|
||||
tmplib.reset();
|
||||
return tmplib;
|
||||
}
|
||||
|
||||
// All ELF systems are expected to support GCC expression statements
|
||||
#define patchElf(source, patcher) __extension__({ \
|
||||
auto r = patchElf(source, patcher); \
|
||||
if (QTest::currentTestFailed()) return; \
|
||||
std::move(r); \
|
||||
})
|
||||
#endif
|
||||
|
||||
static QString sys_qualifiedLibraryName(const QString &fileName)
|
||||
{
|
||||
QString name = QLatin1String("bin/") + QLatin1String(PREFIX) + fileName + QLatin1String(SUFFIX);
|
||||
@ -111,13 +192,13 @@ private slots:
|
||||
void errorString();
|
||||
void loadHints();
|
||||
void deleteinstanceOnUnload();
|
||||
#if defined (__ELF__)
|
||||
void loadDebugObj();
|
||||
void loadCorruptElf_data();
|
||||
void loadCorruptElf();
|
||||
#endif
|
||||
void loadMachO_data();
|
||||
void loadMachO();
|
||||
#if defined (Q_OS_UNIX)
|
||||
void loadGarbage();
|
||||
#endif
|
||||
void relativePath();
|
||||
void absolutePath();
|
||||
void reloadPlugin();
|
||||
@ -289,48 +370,253 @@ void tst_QPluginLoader::deleteinstanceOnUnload()
|
||||
}
|
||||
}
|
||||
|
||||
#if defined (__ELF__)
|
||||
void tst_QPluginLoader::loadDebugObj()
|
||||
{
|
||||
#if !defined(QT_SHARED)
|
||||
QSKIP("This test requires a shared build of Qt, as QPluginLoader::setFileName is a no-op in static builds");
|
||||
#endif
|
||||
#if defined (__ELF__)
|
||||
QVERIFY(QFile::exists(QFINDTESTDATA("elftest/debugobj.so")));
|
||||
QPluginLoader lib1(QFINDTESTDATA("elftest/debugobj.so"));
|
||||
QCOMPARE(lib1.load(), false);
|
||||
#endif
|
||||
}
|
||||
|
||||
void tst_QPluginLoader::loadCorruptElf()
|
||||
void tst_QPluginLoader::loadCorruptElf_data()
|
||||
{
|
||||
#if !defined(QT_SHARED)
|
||||
QSKIP("This test requires a shared build of Qt, as QPluginLoader::setFileName is a no-op in static builds");
|
||||
#endif
|
||||
#if defined (__ELF__)
|
||||
if (sizeof(void*) == 8) {
|
||||
QVERIFY(QFile::exists(QFINDTESTDATA("elftest/corrupt1.elf64.so")));
|
||||
QTest::addColumn<QString>("snippet");
|
||||
QTest::addColumn<ElfPatcher>("patcher");
|
||||
auto newRow = [](const char *rowname, QString &&snippet, auto patcher) {
|
||||
QTest::newRow(rowname) << std::move(snippet) << ElfPatcher::fromLambda(patcher);
|
||||
};
|
||||
|
||||
QPluginLoader lib1(QFINDTESTDATA("elftest/corrupt1.elf64.so"));
|
||||
QCOMPARE(lib1.load(), false);
|
||||
QVERIFY2(lib1.errorString().contains("not an ELF object"), qPrintable(lib1.errorString()));
|
||||
using H = ElfHeader *; // because I'm lazy
|
||||
newRow("not-elf", "invalid signature", [](H h) {
|
||||
h->e_ident[EI_MAG0] = 'Q';
|
||||
h->e_ident[EI_MAG1] = 't';
|
||||
});
|
||||
|
||||
QPluginLoader lib2(QFINDTESTDATA("elftest/corrupt2.elf64.so"));
|
||||
QCOMPARE(lib2.load(), false);
|
||||
QVERIFY2(lib2.errorString().contains("invalid"), qPrintable(lib2.errorString()));
|
||||
newRow("wrong-word-size", "file is for a different word size", [](H h) {
|
||||
h->e_ident[EI_CLASS] = sizeof(void *) == 8 ? ELFCLASS32 : ELFCLASS64;
|
||||
|
||||
QPluginLoader lib3(QFINDTESTDATA("elftest/corrupt3.elf64.so"));
|
||||
QCOMPARE(lib3.load(), false);
|
||||
QVERIFY2(lib3.errorString().contains("invalid"), qPrintable(lib3.errorString()));
|
||||
} else if (sizeof(void*) == 4) {
|
||||
QPluginLoader libW(QFINDTESTDATA("elftest/corrupt3.elf64.so"));
|
||||
QCOMPARE(libW.load(), false);
|
||||
QVERIFY2(libW.errorString().contains("architecture"), qPrintable(libW.errorString()));
|
||||
} else {
|
||||
QFAIL("Please port QElfParser to this platform or blacklist this test.");
|
||||
}
|
||||
#endif
|
||||
// unnecessary, but we're doing it anyway
|
||||
# ifdef _LP64
|
||||
Elf32_Ehdr o;
|
||||
o.e_phentsize = sizeof(Elf32_Phdr);
|
||||
o.e_shentsize = sizeof(Elf32_Shdr);
|
||||
# else
|
||||
Elf64_Ehdr o;
|
||||
o.e_phentsize = sizeof(Elf64_Phdr);
|
||||
o.e_shentsize = sizeof(Elf64_Shdr);
|
||||
# endif
|
||||
memcpy(o.e_ident, h->e_ident, EI_NIDENT);
|
||||
o.e_type = h->e_type;
|
||||
o.e_machine = h->e_machine;
|
||||
o.e_version = h->e_version;
|
||||
o.e_entry = h->e_entry;
|
||||
o.e_phoff = h->e_phoff;
|
||||
o.e_shoff = h->e_shoff;
|
||||
o.e_flags = h->e_flags;
|
||||
o.e_ehsize = sizeof(o);
|
||||
o.e_phnum = h->e_phnum;
|
||||
o.e_shnum = h->e_shnum;
|
||||
o.e_shstrndx = h->e_shstrndx;
|
||||
memcpy(h, &o, sizeof(o));
|
||||
});
|
||||
newRow("invalid-word-size", "file is for a different word size", [](H h) {
|
||||
h->e_ident[EI_CLASS] = ELFCLASSNONE;;
|
||||
});
|
||||
newRow("unknown-word-size", "file is for a different word size", [](H h) {
|
||||
h->e_ident[EI_CLASS] |= 0x40;
|
||||
});
|
||||
|
||||
newRow("wrong-endian", "file is for the wrong endianness", [](H h) {
|
||||
h->e_ident[EI_DATA] = QSysInfo::ByteOrder == QSysInfo::LittleEndian ? ELFDATA2MSB : ELFDATA2LSB;
|
||||
|
||||
// unnecessary, but we're doing it anyway
|
||||
h->e_type = qbswap(h->e_type);
|
||||
h->e_machine = qbswap(h->e_machine);
|
||||
h->e_version = qbswap(h->e_version);
|
||||
h->e_entry = qbswap(h->e_entry);
|
||||
h->e_phoff = qbswap(h->e_phoff);
|
||||
h->e_shoff = qbswap(h->e_shoff);
|
||||
h->e_flags = qbswap(h->e_flags);
|
||||
h->e_ehsize = qbswap(h->e_ehsize);
|
||||
h->e_phnum = qbswap(h->e_phnum);
|
||||
h->e_phentsize = qbswap(h->e_phentsize);
|
||||
h->e_shnum = qbswap(h->e_shnum);
|
||||
h->e_shentsize = qbswap(h->e_shentsize);
|
||||
h->e_shstrndx = qbswap(h->e_shstrndx);
|
||||
});
|
||||
newRow("invalid-endian", "file is for the wrong endianness", [](H h) {
|
||||
h->e_ident[EI_DATA] = ELFDATANONE;
|
||||
});
|
||||
newRow("unknown-endian", "file is for the wrong endianness", [](H h) {
|
||||
h->e_ident[EI_DATA] |= 0x40;
|
||||
});
|
||||
|
||||
newRow("elf-version-0", "file has an unknown ELF version", [](H h) {
|
||||
--h->e_ident[EI_VERSION];
|
||||
});
|
||||
newRow("elf-version-2", "file has an unknown ELF version", [](H h) {
|
||||
++h->e_ident[EI_VERSION];
|
||||
});
|
||||
|
||||
newRow("executable", "file is not a shared object", [](H h) {
|
||||
h->e_type = ET_EXEC;
|
||||
});
|
||||
newRow("relocatable", "file is not a shared object", [](H h) {
|
||||
h->e_type = ET_REL;
|
||||
});
|
||||
newRow("core-file", "file is not a shared object", [](H h) {
|
||||
h->e_type = ET_CORE;
|
||||
});
|
||||
newRow("invalid-type", "file is not a shared object", [](H h) {
|
||||
h->e_type |= 0x100;
|
||||
});
|
||||
|
||||
newRow("wrong-arch", "file is for a different processor", [](H h) {
|
||||
// could just ++h->e_machine...
|
||||
# if defined(Q_PROCESSOR_X86_64)
|
||||
h->e_machine = EM_AARCH64;
|
||||
# elif defined(Q_PROCESSOR_ARM_64)
|
||||
h->e_machine = EM_X86_64;
|
||||
# elif defined(Q_PROCESSOR_X86_32)
|
||||
h->e_machine = EM_ARM;
|
||||
# elif defined(Q_PROCESSOR_ARM)
|
||||
h->e_machine = EM_386;
|
||||
# elif defined(Q_PROCESSOR_MIPS_64)
|
||||
h->e_machine = EM_PPC64;
|
||||
# elif defined(Q_PROCESSOR_MIPS_32)
|
||||
h->e_machine = EM_PPC;
|
||||
# elif defined(Q_PROCESSOR_POWER_64)
|
||||
h->e_machine = EM_S390;
|
||||
# elif defined(Q_PROCESSOR_POWER_32)
|
||||
h->e_machine = EM_MIPS;
|
||||
# endif
|
||||
});
|
||||
|
||||
newRow("file-version-0", "file has an unknown ELF version", [](H h) {
|
||||
--h->e_version;
|
||||
});
|
||||
newRow("file-version-2", "file has an unknown ELF version", [](H h) {
|
||||
++h->e_version;
|
||||
});
|
||||
|
||||
newRow("section-entry-size-zero", "unexpected section entry size", [](H h) {
|
||||
h->e_shentsize = 0;
|
||||
});
|
||||
newRow("section-entry-small", "unexpected section entry size", [](H h) {
|
||||
h->e_shentsize = alignof(ElfShdr);
|
||||
});
|
||||
newRow("section-entry-misaligned", "unexpected section entry size", [](H h) {
|
||||
++h->e_shentsize;
|
||||
});
|
||||
newRow("no-sections", "is not a Qt plugin (.qtmetadata section not found)", [](H h){
|
||||
h->e_shnum = h->e_shoff = h->e_shstrndx = 0;
|
||||
});
|
||||
|
||||
// section table tests
|
||||
newRow("section-table-starts-past-eof", "section table extends past the end of the file",
|
||||
[](H h, QFile *f) {
|
||||
h->e_shoff = f->size();
|
||||
});
|
||||
newRow("section-table-ends-past-eof", "section table extends past the end of the file",
|
||||
[](H h, QFile *f) {
|
||||
h->e_shoff = f->size() + 1 - h->e_shentsize * h->e_shnum;
|
||||
});
|
||||
|
||||
static auto getSection = +[](H h, int index) {
|
||||
auto sections = reinterpret_cast<ElfShdr *>(h->e_shoff + reinterpret_cast<uchar *>(h));
|
||||
return sections + index;
|
||||
};
|
||||
|
||||
// arbitrary section bounds checks
|
||||
// section index = 0 is usually a NULL section, so we try 1
|
||||
newRow("section1-starts-past-eof", "a section data extends past the end of the file",
|
||||
[](H h, QFile *f) {
|
||||
ElfShdr *s = getSection(h, 1);
|
||||
s->sh_offset = f->size();
|
||||
});
|
||||
newRow("section1-ends-past-eof", "a section data extends past the end of the file",
|
||||
[](H h, QFile *f) {
|
||||
ElfShdr *s = getSection(h, 1);
|
||||
s->sh_size = f->size() + 1 - s->sh_offset;
|
||||
});
|
||||
newRow("section1-bounds-overflow", "a section data extends past the end of the file", [](H h) {
|
||||
ElfShdr *s = getSection(h, 1);
|
||||
s->sh_size = -sizeof(*s);
|
||||
});
|
||||
|
||||
// section header string table tests
|
||||
newRow("shstrndx-invalid", "e_shstrndx greater than the number of sections", [](H h) {
|
||||
h->e_shstrndx = h->e_shnum;
|
||||
});
|
||||
newRow("shstrtab-starts-past-eof", "section header string table extends past the end of the file",
|
||||
[](H h, QFile *f) {
|
||||
ElfShdr *s = getSection(h, h->e_shstrndx);
|
||||
s->sh_offset = f->size();
|
||||
});
|
||||
newRow("shstrtab-ends-past-eof", "section header string table extends past the end of the file",
|
||||
[](H h, QFile *f) {
|
||||
ElfShdr *s = getSection(h, h->e_shstrndx);
|
||||
s->sh_size = f->size() + 1 - s->sh_offset;
|
||||
});
|
||||
newRow("shstrtab-bounds-overflow", "section header string table extends past the end of the file", [](H h) {
|
||||
ElfShdr *s = getSection(h, h->e_shstrndx);
|
||||
s->sh_size = -sizeof(*s);
|
||||
});
|
||||
newRow("section-name-past-eof", "section name extends past the end of the file", [](H h, QFile *f) {
|
||||
ElfShdr *section1 = getSection(h, 1);
|
||||
ElfShdr *shstrtab = getSection(h, h->e_shstrndx);
|
||||
section1->sh_name = f->size() - shstrtab->sh_offset;
|
||||
});
|
||||
newRow("section-name-past-end-of-shstrtab", "section name extends past the end of the file", [](H h) {
|
||||
ElfShdr *section1 = getSection(h, 1);
|
||||
ElfShdr *shstrtab = getSection(h, h->e_shstrndx);
|
||||
section1->sh_name = shstrtab->sh_size;
|
||||
});
|
||||
|
||||
newRow("debug-symbols", ".qtmetadata section not found", [](H h) {
|
||||
// attempt to make it look like extracted debug info
|
||||
for (int i = 1; i < h->e_shnum; ++i) {
|
||||
ElfShdr *s = getSection(h, i);
|
||||
if (s->sh_type == SHT_NOBITS)
|
||||
break;
|
||||
if (s->sh_type != SHT_NOTE && s->sh_flags & SHF_ALLOC)
|
||||
s->sh_type = SHT_NOBITS;
|
||||
}
|
||||
});
|
||||
|
||||
// we don't know which section is .qtmetadata, so we just apply to all of them
|
||||
static auto applyToAllSectionFlags = +[](H h, int flag) {
|
||||
for (int i = 0; i < h->e_shnum; ++i)
|
||||
getSection(h, i)->sh_flags |= flag;
|
||||
};
|
||||
newRow("qtmetadata-executable", ".qtmetadata section is executable", [](H h) {
|
||||
applyToAllSectionFlags(h, SHF_EXECINSTR);
|
||||
});
|
||||
newRow("qtmetadata-writable", ".qtmetadata section is writable", [](H h) {
|
||||
applyToAllSectionFlags(h, SHF_WRITE);
|
||||
});
|
||||
}
|
||||
|
||||
void tst_QPluginLoader::loadCorruptElf()
|
||||
{
|
||||
QFETCH(QString, snippet);
|
||||
QFETCH(ElfPatcher, patcher);
|
||||
|
||||
std::unique_ptr<QTemporaryFile> tmplib =
|
||||
patchElf(sys_qualifiedLibraryName("theplugin"), patcher);
|
||||
|
||||
QPluginLoader lib(tmplib->fileName());
|
||||
QVERIFY(!lib.load());
|
||||
QVERIFY2(lib.errorString().contains(snippet), qPrintable(lib.errorString()));
|
||||
}
|
||||
#endif // __ELF__
|
||||
|
||||
void tst_QPluginLoader::loadMachO_data()
|
||||
{
|
||||
#if defined(QT_BUILD_INTERNAL) && defined(Q_OF_MACH_O)
|
||||
@ -404,21 +690,6 @@ void tst_QPluginLoader::loadMachO()
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined (Q_OS_UNIX)
|
||||
void tst_QPluginLoader::loadGarbage()
|
||||
{
|
||||
#if !defined(QT_SHARED)
|
||||
QSKIP("This test requires a shared build of Qt, as QPluginLoader::setFileName is a no-op in static builds");
|
||||
#endif
|
||||
for (int i=0; i<5; i++) {
|
||||
const QString name = QLatin1String("elftest/garbage") + QString::number(i + 1) + QLatin1String(".so");
|
||||
QPluginLoader lib(QFINDTESTDATA(name));
|
||||
QCOMPARE(lib.load(), false);
|
||||
QVERIFY(lib.errorString() != QString("Unknown error"));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void tst_QPluginLoader::relativePath()
|
||||
{
|
||||
#if !defined(QT_SHARED)
|
||||
|
Loading…
x
Reference in New Issue
Block a user