summaryrefslogtreecommitdiffstats
path: root/tests
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 /tests
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>
Diffstat (limited to 'tests')
-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
6 files changed, 289 insertions, 2 deletions
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()
{