Add a Mach-O decoder to the QPluginLoader

We already had an ELF decoder, which helped us greatly to find the
metadata and that catches most Unix systems (Solaris, QNX, HP-UXi, and
all of the free Unixes). On other Unix systems, aside from Mac OS X,
we simply scanned the entire file for the signature. On Windows, even
without a COFF-PE decoder, we use a LoadLibrary trick to load the
plugin without loading the dependent libraries. In most cases, that
works.

Unfortunately, on Mac OS X we didn't have a decoder and nor could we
do the file scan: because Mac OS X binaries could be fat binaries, we
wouldn't know which architecture's signature we had found.

No more. This adds a full Mach-O decoder to QtCore. It is also capable
of finding the boundaries of the architecture's binary, but that
functionality is disabled since all Qt 5 plugins have plugin metadata
sections.

Change-Id: I2d5c04c5ecf024864b8a43f31ab6b7e6c5eae9ce
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@digia.com>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Thiago Macieira 2013-02-28 13:00:19 -08:00 committed by The Qt Project
parent 3fdf69b599
commit 62d636a666
11 changed files with 613 additions and 8 deletions

View File

@ -74,6 +74,9 @@
#if defined (__ELF__)
# define Q_OF_ELF
#endif
#if defined (__MACH__) && defined (__APPLE__)
# define Q_OF_MACH_O
#endif
#ifdef __cplusplus

View File

@ -9,14 +9,16 @@ HEADERS += \
plugin/quuid.h \
plugin/qfactoryloader_p.h \
plugin/qsystemlibrary_p.h \
plugin/qelfparser_p.h
plugin/qelfparser_p.h \
plugin/qmachparser_p.h
SOURCES += \
plugin/qpluginloader.cpp \
plugin/qfactoryloader.cpp \
plugin/quuid.cpp \
plugin/qlibrary.cpp \
plugin/qelfparser_p.cpp
plugin/qelfparser_p.cpp \
plugin/qmachparser.cpp
win32 {
SOURCES += \

View File

@ -1,7 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Copyright (C) 2013 Intel Corporation
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtCore module of the Qt Toolkit.
@ -64,6 +64,7 @@
#include <qjsondocument.h>
#include <qjsonvalue.h>
#include "qelfparser_p.h"
#include "qmachparser_p.h"
QT_BEGIN_NAMESPACE
@ -180,7 +181,7 @@ QT_BEGIN_NAMESPACE
*/
#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
#if defined(Q_OS_UNIX)
static long qt_find_pattern(const char *s, ulong s_len,
const char *pattern, ulong p_len)
@ -253,7 +254,7 @@ static bool qt_unix_query(const QString &library, QLibraryPrivate *lib)
}
/*
ELF binaries on GNU, have .qplugin sections.
ELF and Mach-O binaries with GCC have .qplugin sections.
*/
bool hasMetaData = false;
long pos = 0;
@ -274,6 +275,26 @@ static bool qt_unix_query(const QString &library, QLibraryPrivate *lib)
pos += rel;
hasMetaData = true;
}
#elif defined (Q_OF_MACH_O)
{
QString errorString;
int r = QMachOParser::parse(filedata, fdlen, library, &errorString, &pos, &fdlen);
if (r == QMachOParser::NotSuitable) {
if (qt_debug_component())
qWarning("QMachOParser: %s", qPrintable(errorString));
if (lib)
lib->errorString = errorString;
return false;
}
// even if the metadata section was not found, the Mach-O parser will
// at least return the boundaries of the right architecture
long rel = qt_find_pattern(filedata + pos, fdlen, pattern, plen);
if (rel < 0)
pos = -1;
else
pos += rel;
hasMetaData = true;
}
#else
pos = qt_find_pattern(filedata, fdlen, pattern, plen);
if (pos > 0)
@ -690,7 +711,7 @@ void QLibraryPrivate::updatePluginState()
}
#endif
#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
#if defined(Q_OS_UNIX)
if (!pHnd) {
// use unix shortcut to avoid loading the library
success = qt_unix_query(fileName, this);

View File

@ -0,0 +1,213 @@
/****************************************************************************
**
** Copyright (C) 2013 Intel Corporation
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtCore module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qmachparser_p.h"
#if defined(Q_OF_MACH_O) && !defined(QT_NO_LIBRARY)
#include <qendian.h>
#include "qlibrary_p.h"
#include <mach-o/loader.h>
#include <mach-o/fat.h>
QT_BEGIN_NAMESPACE
#if defined(Q_PROCESSOR_X86_64)
# define MACHO64
static const cpu_type_t my_cputype = CPU_TYPE_X86_64;
#elif defined(Q_PROCESSOR_X86_32)
static const cpu_type_t my_cputype = CPU_TYPE_X86;
#elif defined(Q_PROCESSOR_POWER_64)
# define MACHO64
static const cpu_type_t my_cputype = CPU_TYPE_POWERPC64;
#elif defined(Q_PROCESSOR_POWER_32)
static const cpu_type_t my_cputype = CPU_TYPE_POWERPC;
#elif defined(Q_PROCESSOR_ARM)
static const cpu_type_t my_cputype = CPU_TYPE_ARM;
#else
# error "Unknown CPU type"
#endif
#ifdef MACHO64
# undef MACHO64
typedef mach_header_64 my_mach_header;
typedef segment_command_64 my_segment_command;
typedef section_64 my_section;
static const uint32_t my_magic = MH_MAGIC_64;
#else
typedef mach_header my_mach_header;
typedef segment_command my_segment_command;
typedef section my_section;
static const uint32_t my_magic = MH_MAGIC;
#endif
static int ns(const QString &reason, const QString &library, QString *errorString)
{
if (errorString)
*errorString = QLibrary::tr("'%1' is not a valid Mach-O binary (%2)")
.arg(library, reason.isEmpty() ? QLibrary::tr("file is corrupt") : reason);
return QMachOParser::NotSuitable;
}
int QMachOParser::parse(const char *m_s, ulong fdlen, const QString &library, QString *errorString, long *pos, ulong *sectionlen)
{
// we test all possibilities so that we report whether a file is a binary or not
const fat_header *fat = reinterpret_cast<const fat_header *>(m_s);
const mach_header *mh = reinterpret_cast<const mach_header *>(m_s);
const mach_header_64 *mh64 = reinterpret_cast<const mach_header_64 *>(m_s);
if (fdlen < sizeof(uint32_t) && fat->magic != qToBigEndian(FAT_MAGIC)
&& mh->magic != MH_MAGIC && mh->magic != MH_CIGAM
&& mh64->magic != MH_MAGIC_64 && mh64->magic != MH_CIGAM_64) {
if (errorString)
*errorString = QLibrary::tr("'%1' is not a Mach-O binary (%2)").arg(library, QLibrary::tr("invalid magic"));
return NotSuitable;
}
// find out if this is a fat Mach-O binary first
const my_mach_header *header = 0;
if (fat->magic == qToBigEndian(FAT_MAGIC)) {
// find our architecture in the binary
const fat_arch *arch = reinterpret_cast<const fat_arch *>(m_s + sizeof(*fat));
if (Q_UNLIKELY(fdlen < sizeof(*fat) + 2 * sizeof(*arch))) {
// fat binaries must contain at least two architectures
return ns(QLibrary::tr("file too small"), library, errorString);
}
int count = qFromBigEndian(fat->nfat_arch);
if (Q_UNLIKELY(fdlen < sizeof(*fat) + sizeof(*arch) * count))
return ns(QString(), library, errorString);
for (int i = 0; i < count; ++i) {
if (arch[i].cputype == qToBigEndian(my_cputype)) {
// ### should we check the CPU subtype? Maybe on ARM?
uint32_t size = qFromBigEndian(arch[i].size);
uint32_t offset = qFromBigEndian(arch[i].offset);
if (Q_UNLIKELY(size + offset > fdlen || size < sizeof(my_mach_header)))
return ns(QString(), library, errorString);
header = reinterpret_cast<const my_mach_header *>(m_s + offset);
fdlen = size;
break;
}
}
if (!header)
return ns(QLibrary::tr("no suitable architecture in fat binary"), library, errorString);
// check the magic again
if (Q_UNLIKELY(header->magic != my_magic))
return ns(QString(), library, errorString);
} else {
header = reinterpret_cast<const my_mach_header *>(m_s);
fat = 0;
// check magic
if (header->magic != my_magic)
return ns(QLibrary::tr("invalid magic"), library, errorString);
if (Q_UNLIKELY(fdlen < sizeof(my_mach_header)))
return ns(QString(), library, errorString);
}
// from this point on, fdlen is specific to this architecture
// from this point on, everything is in host byte order
*pos = reinterpret_cast<const char *>(header) - m_s;
// (re-)check the CPU type
// ### should we check the CPU subtype? Maybe on ARM?
if (header->cputype != my_cputype) {
if (fat)
return ns(QString(), library, errorString);
return ns(QLibrary::tr("wrong architecture"), library, errorString);
}
// check the file type
if (Q_UNLIKELY(header->filetype != MH_BUNDLE && header->filetype != MH_DYLIB))
return ns(QLibrary::tr("not a dynamic library"), library, errorString);
// find the __TEXT segment, "qtmetadata" section
const my_segment_command *seg = reinterpret_cast<const my_segment_command *>(header + 1);
ulong minsize = sizeof(*header);
for (uint i = 0; i < header->ncmds; ++i,
seg = reinterpret_cast<const my_segment_command *>(reinterpret_cast<const char *>(seg) + seg->cmdsize)) {
if (Q_UNLIKELY(fdlen < minsize + sizeof(load_command)))
return ns(QString(), library, errorString);
minsize += seg->cmdsize;
if (Q_UNLIKELY(fdlen < minsize))
return ns(QString(), library, errorString);
const uint32_t MyLoadCommand = sizeof(void *) > 4 ? LC_SEGMENT_64 : LC_SEGMENT;
if (seg->cmd != MyLoadCommand)
continue;
// is this the __TEXT segment?
if (strcmp(seg->segname, "__TEXT") == 0) {
const my_section *sect = reinterpret_cast<const my_section *>(seg + 1);
for (uint j = 0; j < seg->nsects; ++j) {
// is this the "qtmetadata" section?
if (strcmp(sect[j].sectname, "qtmetadata") != 0)
continue;
// found it!
if (Q_UNLIKELY(fdlen < sect[j].offset + sect[j].size))
return ns(QString(), library, errorString);
*pos += sect[j].offset;
*sectionlen = sect[j].size;
return QtMetaDataSection;
}
}
// other type of segment
seg = reinterpret_cast<const my_segment_command *>(reinterpret_cast<const char *>(seg) + seg->cmdsize);
}
// // No Qt section was found, but at least we know that where the proper architecture's boundaries are
// return NoQtSection;
if (errorString)
*errorString = QLibrary::tr("'%1' is not a Qt plugin").arg(library);
return NotSuitable;
}
QT_END_NAMESPACE
#endif

View File

@ -0,0 +1,79 @@
/****************************************************************************
**
** Copyright (C) 2013 Intel Corporation
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtCore module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QMACHPARSER_P_H
#define QMACHPARSER_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include <qendian.h>
#include <qglobal.h>
#ifndef QT_NO_LIBRARY
#if defined(Q_OF_MACH_O)
QT_BEGIN_NAMESPACE
class QString;
class QLibraryPrivate;
class Q_AUTOTEST_EXPORT QMachOParser
{
public:
enum { QtMetaDataSection, NoQtSection, NotSuitable };
static int parse(const char *m_s, ulong fdlen, const QString &library, QString *errorString, long *pos, ulong *sectionlen);
};
QT_END_NAMESPACE
#endif // defined(Q_OF_ELF) && defined(Q_CC_GNU)
#endif // QT_NO_LIBRARY
#endif // QMACHPARSER_P_H

View File

@ -0,0 +1,49 @@
/****************************************************************************
**
** Copyright (C) 2013 Intel Corporation
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtCore module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtCore/qplugin.h>
#if QT_POINTER_SIZE == 8
QT_PLUGIN_METADATA_SECTION void *const pluginSection = (void*)(0xc0ffeec0ffeeL);
#else
QT_PLUGIN_METADATA_SECTION void *const pluginSection = (void*)0xc0ffee;
#endif
QT_PLUGIN_METADATA_SECTION const char message[] = "QTMETADATA";

View File

@ -0,0 +1,51 @@
TEMPLATE = aux
OTHER_FILES += \
ppcconverter.pl
i386.target = good.i386.dylib
i386.commands = $(CXX) -shared -arch i386 -o $@ -I$$[QT_INSTALL_HEADERS/get] $<
i386.depends += $$PWD/../fakeplugin.cpp
x86_64.target = good.x86_64.dylib
x86_64.commands = $(CXX) -shared -arch x86_64 -o $@ -I$$[QT_INSTALL_HEADERS/get] $<
x86_64.depends += $$PWD/../fakeplugin.cpp
# Current Mac OS X toolchains have no compiler for PPC anymore
# So we fake it by converting an x86-64 binary to (little-endian!) PPC64
ppc64.target = good.ppc64.dylib
ppc64.commands = $$PWD/ppcconverter.pl $< $@
ppc64.depends = x86_64 $$PWD/ppcconverter.pl
# Generate a fat binary with three architectures
fat_all.target = good.fat.all.dylib
fat_all.commands = lipo -create -output $@ \
-arch ppc64 $$ppc64.target \
-arch i386 $$i386.target \
-arch x86_64 $$x86_64.target
fat_all.depends += i386 x86_64 ppc64
fat_no_i386.target = good.fat.no-i386.dylib
fat_no_i386.commands = lipo -create -output $@ -arch x86_64 $$x86_64.target -arch ppc64 $$ppc64.target
fat_no_i386.depends += x86_64 ppc64
fat_no_x86_64.target = good.fat.no-x86_64.dylib
fat_no_x86_64.commands = lipo -create -output $@ -arch i386 $$i386.target -arch ppc64 $$ppc64.target
fat_no_x86_64.depends += i386 ppc64
fat_stub_i386.target = good.fat.stub-i386.dylib
fat_stub_i386.commands = lipo -create -output $@ -arch ppc64 $$ppc64.target -arch_blank i386
fat_stub_i386.depends += x86_64 ppc64
fat_stub_x86_64.target = good.fat.stub-x86_64.dylib
fat_stub_x86_64.commands = lipo -create -output $@ -arch ppc64 $$ppc64.target -arch_blank x86_64
fat_stub_x86_64.depends += i386 ppc64
MYTARGETS = $$fat_all.depends fat_all fat_no_x86_64 fat_no_i386 \
fat_stub_i386 fat_stub_x86_64
all.depends += $$MYTARGETS
QMAKE_EXTRA_TARGETS += $$MYTARGETS all
QMAKE_CLEAN += $$i386.target $$x86_64.target $$ppc64.target $$fat_all.target \
$$fat_no_i386.target $$fat_no_x86_64.target \
$$fat_stub_i386.target $$fat_stub_x86_64.target

View File

@ -0,0 +1,112 @@
#!/usr/bin/perl
#############################################################################
##
## Copyright (C) 2013 Intel Corporation.
## Contact: http://www.qt-project.org/legal
##
## This file is the build configuration utility of the Qt Toolkit.
##
## $QT_BEGIN_LICENSE:LGPL$
## Commercial License Usage
## Licensees holding valid commercial Qt licenses may use this file in
## accordance with the commercial license agreement provided with the
## Software or, alternatively, in accordance with the terms contained in
## a written agreement between you and Digia. For licensing terms and
## conditions see http://qt.digia.com/licensing. For further information
## use the contact form at http://qt.digia.com/contact-us.
##
## GNU Lesser General Public License Usage
## Alternatively, this file may be used under the terms of the GNU Lesser
## General Public License version 2.1 as published by the Free Software
## Foundation and appearing in the file LICENSE.LGPL included in the
## packaging of this file. Please review the following information to
## ensure the GNU Lesser General Public License version 2.1 requirements
## will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
##
## In addition, as a special exception, Digia gives you certain additional
## rights. These rights are described in the Digia Qt LGPL Exception
## version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
##
## GNU General Public License Usage
## Alternatively, this file may be used under the terms of the GNU
## General Public License version 3.0 as published by the Free Software
## Foundation and appearing in the file LICENSE.GPL included in the
## packaging of this file. Please review the following information to
## ensure the GNU General Public License version 3.0 requirements will be
## met: http://www.gnu.org/copyleft/gpl.html.
##
##
## $QT_END_LICENSE$
##
#############################################################################
# Changes the Mach-O file type header to PowerPC.
#
# The header is (from mach-o/loader.h):
# struct mach_header {
# uint32_t magic; /* mach magic number identifier */
# cpu_type_t cputype; /* cpu specifier */
# cpu_subtype_t cpusubtype; /* machine specifier */
# uint32_t filetype; /* type of file */
# uint32_t ncmds; /* number of load commands */
# uint32_t sizeofcmds; /* the size of all the load commands */
# uint32_t flags; /* flags */
# };
#
# The 64-bit header is identical in the first three fields, except for a different
# magic number. We will not touch the magic number, we'll just reset the cputype
# field to the PowerPC type and the subtype field to zero.
#
# We will not change the file's endianness. That means we might create a little-endian
# PowerPC binary, which could not be run in real life.
#
# We will also not change the 64-bit ABI flag, which is found in the cputype's high
# byte. That means we'll create a PPC64 binary if fed a 64-bit input.
#
use strict;
use constant MH_MAGIC => 0xfeedface;
use constant MH_CIGAM => 0xcefaedfe;
use constant MH_MAGIC_64 => 0xfeedfacf;
use constant MH_CIGAM_64 => 0xcffaedfe;
use constant CPU_TYPE_POWERPC => 18;
use constant CPU_SUBTYPE_POWERPC_ALL => 0;
my $infile = shift @ARGV or die("Missing input filename");
my $outfile = shift @ARGV or die("Missing output filename");
open IN, "<$infile" or die("Can't open $infile for reading: $!\n");
open OUT, ">$outfile" or die("Can't open $outfile for writing: $!\n");
binmode IN;
binmode OUT;
# Read the first 12 bytes, which includes the interesting fields of the header
my $buffer;
read(IN, $buffer, 12);
my $magic = vec($buffer, 0, 32);
if ($magic == MH_MAGIC || $magic == MH_MAGIC_64) {
# Big endian
# The low byte of cputype is at offset 7
vec($buffer, 7, 8) = CPU_TYPE_POWERPC;
} elsif ($magic == MH_CIGAM || $magic == MH_CIGAM_64) {
# Little endian
# The low byte of cpytype is at offset 4
vec($buffer, 4, 8) = CPU_TYPE_POWERPC;
} else {
$magic = '';
$magic .= sprintf("%02X ", $_) for unpack("CCCC", $buffer);
die("Invalid input. Unknown magic $magic\n");
}
vec($buffer, 2, 32) = CPU_SUBTYPE_POWERPC_ALL;
print OUT $buffer;
# Copy the rest
while (!eof(IN)) {
read(IN, $buffer, 4096) and
print OUT $buffer or
die("Problem copying: $!\n");
}
close(IN);
close(OUT);

View File

@ -5,6 +5,7 @@ SUBDIRS = lib \
theplugin \
tst
!win32: !mac: SUBDIRS += almostplugin
macx-*: SUBDIRS += machtest
TARGET = tst_qpluginloader
# no special install rule for subdir

View File

@ -2,7 +2,8 @@ CONFIG += testcase
CONFIG += parallel_test
TARGET = ../tst_qpluginloader
QT = core testlib
SOURCES = ../tst_qpluginloader.cpp
contains(QT_CONFIG, private_tests): QT += core-private
SOURCES = ../tst_qpluginloader.cpp ../fakeplugin.cpp
HEADERS = ../theplugin/plugininterface.h
CONFIG -= app_bundle
@ -14,5 +15,5 @@ win32 {
}
}
TESTDATA += ../elftest
TESTDATA += ../elftest ../machtest
DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0

View File

@ -44,6 +44,10 @@
#include <qpluginloader.h>
#include "theplugin/plugininterface.h"
#if defined(QT_BUILD_INTERNAL) && defined(Q_OF_MACH_O)
# include <QtCore/private/qmachparser_p.h>
#endif
// Helper macros to let us know if some suffixes are valid
#define bundle_VALID false
#define dylib_VALID false
@ -118,6 +122,8 @@ private slots:
void deleteinstanceOnUnload();
void loadDebugObj();
void loadCorruptElf();
void loadMachO_data();
void loadMachO();
#if defined (Q_OS_UNIX)
void loadGarbage();
#endif
@ -311,6 +317,73 @@ void tst_QPluginLoader::loadCorruptElf()
#endif
}
void tst_QPluginLoader::loadMachO_data()
{
#ifdef Q_OF_MACH_O
QTest::addColumn<int>("parseResult");
QTest::newRow("/dev/null") << int(QMachOParser::NotSuitable);
QTest::newRow("elftest/debugobj.so") << int(QMachOParser::NotSuitable);
QTest::newRow("tst_qpluginloader.cpp") << int(QMachOParser::NotSuitable);
QTest::newRow("tst_qpluginloader") << int(QMachOParser::NotSuitable);
# ifdef Q_PROCESSOR_X86_64
QTest::newRow("machtest/good.x86_64.dylib") << int(QMachOParser::QtMetaDataSection);
QTest::newRow("machtest/good.i386.dylib") << int(QMachOParser::NotSuitable);
QTest::newRow("machtest/good.fat.no-x86_64.dylib") << int(QMachOParser::NotSuitable);
QTest::newRow("machtest/good.fat.no-i386.dylib") << int(QMachOParser::QtMetaDataSection);
# elif defined(Q_PROCESSOR_X86_32)
QTest::newRow("machtest/good.i386.dylib") << int(QMachOParser::QtMetaDataSection);
QTest::newRow("machtest/good.x86_64.dylib") << int(QMachOParser::NotSuitable);
QTest::newRow("machtest/good.fat.no-i386.dylib") << int(QMachOParser::NotSuitable);
QTest::newRow("machtest/good.fat.no-x86_64.dylib") << int(QMachOParser::QtMetaDataSection);
# endif
# ifndef Q_PROCESSOR_POWER_64
QTest::newRow("machtest/good.ppc64.dylib") << int(QMachOParser::NotSuitable);
# endif
QTest::newRow("machtest/good.fat.all.dylib") << int(QMachOParser::QtMetaDataSection);
QTest::newRow("machtest/good.fat.stub-x86_64.dylib") << int(QMachOParser::NotSuitable);
QTest::newRow("machtest/good.fat.stub-i386.dylib") << int(QMachOParser::NotSuitable);
#endif
}
void tst_QPluginLoader::loadMachO()
{
#ifdef Q_OF_MACH_O
QFile f(QFINDTESTDATA(QTest::currentDataTag()));
QVERIFY(f.open(QIODevice::ReadOnly));
QByteArray data = f.readAll();
long pos;
ulong len;
QString errorString;
int r = QMachOParser::parse(data.constData(), data.size(), f.fileName(), &errorString, &pos, &len);
QFETCH(int, parseResult);
QCOMPARE(r, parseResult);
if (r == QMachOParser::NotSuitable)
return;
QVERIFY(pos > 0);
QVERIFY(len >= sizeof(void*));
QVERIFY(pos + long(len) < data.size());
QCOMPARE(pos & (sizeof(void*) - 1), 0UL);
void *value = *(void**)(data.constData() + pos);
QCOMPARE(value, sizeof(void*) > 4 ? (void*)(0xc0ffeec0ffeeL) : (void*)0xc0ffee);
// now that we know it's valid, let's try to make it invalid
ulong offeredlen = pos;
do {
--offeredlen;
r = QMachOParser::parse(data.constData(), offeredlen, f.fileName(), &errorString, &pos, &len);
QVERIFY2(r == QMachOParser::NotSuitable, qPrintable(QString("Failed at size 0x%1").arg(offeredlen, 0, 16)));
} while (offeredlen);
#endif
}
#if defined (Q_OS_UNIX)
void tst_QPluginLoader::loadGarbage()
{