summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThiago Macieira <thiago.macieira@intel.com>2013-02-28 13:00:19 -0800
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-07-20 02:09:26 +0200
commit62d636a666f38fb4483e2d528f1e175c90f5272a (patch)
tree20b11b55e659585973fba46cccec808544cb9e9d
parent3fdf69b599457bf32a6620d66faefb940fd21c93 (diff)
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>
-rw-r--r--src/corelib/global/qglobal.h3
-rw-r--r--src/corelib/plugin/plugin.pri6
-rw-r--r--src/corelib/plugin/qlibrary.cpp29
-rw-r--r--src/corelib/plugin/qmachparser.cpp213
-rw-r--r--src/corelib/plugin/qmachparser_p.h79
-rw-r--r--tests/auto/corelib/plugin/qpluginloader/fakeplugin.cpp49
-rw-r--r--tests/auto/corelib/plugin/qpluginloader/machtest/machtest.pro51
-rwxr-xr-xtests/auto/corelib/plugin/qpluginloader/machtest/ppcconverter.pl112
-rw-r--r--tests/auto/corelib/plugin/qpluginloader/qpluginloader.pro1
-rw-r--r--tests/auto/corelib/plugin/qpluginloader/tst/tst.pro5
-rw-r--r--tests/auto/corelib/plugin/qpluginloader/tst_qpluginloader.cpp73
11 files changed, 613 insertions, 8 deletions
diff --git a/src/corelib/global/qglobal.h b/src/corelib/global/qglobal.h
index 2132e555cd..f9629ff430 100644
--- a/src/corelib/global/qglobal.h
+++ b/src/corelib/global/qglobal.h
@@ -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
diff --git a/src/corelib/plugin/plugin.pri b/src/corelib/plugin/plugin.pri
index eb7a7f7fa8..338b3d0972 100644
--- a/src/corelib/plugin/plugin.pri
+++ b/src/corelib/plugin/plugin.pri
@@ -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 += \
diff --git a/src/corelib/plugin/qlibrary.cpp b/src/corelib/plugin/qlibrary.cpp
index 3432f9619d..f015c3c236 100644
--- a/src/corelib/plugin/qlibrary.cpp
+++ b/src/corelib/plugin/qlibrary.cpp
@@ -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);
diff --git a/src/corelib/plugin/qmachparser.cpp b/src/corelib/plugin/qmachparser.cpp
new file mode 100644
index 0000000000..b9a56a403c
--- /dev/null
+++ b/src/corelib/plugin/qmachparser.cpp
@@ -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
diff --git a/src/corelib/plugin/qmachparser_p.h b/src/corelib/plugin/qmachparser_p.h
new file mode 100644
index 0000000000..6b2774dab6
--- /dev/null
+++ b/src/corelib/plugin/qmachparser_p.h
@@ -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
diff --git a/tests/auto/corelib/plugin/qpluginloader/fakeplugin.cpp b/tests/auto/corelib/plugin/qpluginloader/fakeplugin.cpp
new file mode 100644
index 0000000000..d5c933e3af
--- /dev/null
+++ b/tests/auto/corelib/plugin/qpluginloader/fakeplugin.cpp
@@ -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";
diff --git a/tests/auto/corelib/plugin/qpluginloader/machtest/machtest.pro b/tests/auto/corelib/plugin/qpluginloader/machtest/machtest.pro
new file mode 100644
index 0000000000..d4fc9f2836
--- /dev/null
+++ b/tests/auto/corelib/plugin/qpluginloader/machtest/machtest.pro
@@ -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
+
+
diff --git a/tests/auto/corelib/plugin/qpluginloader/machtest/ppcconverter.pl b/tests/auto/corelib/plugin/qpluginloader/machtest/ppcconverter.pl
new file mode 100755
index 0000000000..86943161b7
--- /dev/null
+++ b/tests/auto/corelib/plugin/qpluginloader/machtest/ppcconverter.pl
@@ -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);
diff --git a/tests/auto/corelib/plugin/qpluginloader/qpluginloader.pro b/tests/auto/corelib/plugin/qpluginloader/qpluginloader.pro
index 0cba19887e..8d117793bf 100644
--- a/tests/auto/corelib/plugin/qpluginloader/qpluginloader.pro
+++ b/tests/auto/corelib/plugin/qpluginloader/qpluginloader.pro
@@ -5,6 +5,7 @@ SUBDIRS = lib \
theplugin \
tst
!win32: !mac: SUBDIRS += almostplugin
+macx-*: SUBDIRS += machtest
TARGET = tst_qpluginloader
# no special install rule for subdir
diff --git a/tests/auto/corelib/plugin/qpluginloader/tst/tst.pro b/tests/auto/corelib/plugin/qpluginloader/tst/tst.pro
index a7a9661a54..3894c90ae3 100644
--- a/tests/auto/corelib/plugin/qpluginloader/tst/tst.pro
+++ b/tests/auto/corelib/plugin/qpluginloader/tst/tst.pro
@@ -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
diff --git a/tests/auto/corelib/plugin/qpluginloader/tst_qpluginloader.cpp b/tests/auto/corelib/plugin/qpluginloader/tst_qpluginloader.cpp
index cef4f53101..989d311ebf 100644
--- a/tests/auto/corelib/plugin/qpluginloader/tst_qpluginloader.cpp
+++ b/tests/auto/corelib/plugin/qpluginloader/tst_qpluginloader.cpp
@@ -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()
{