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:
parent
3fdf69b599
commit
62d636a666
@ -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
|
||||
|
||||
|
@ -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 += \
|
||||
|
@ -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);
|
||||
|
213
src/corelib/plugin/qmachparser.cpp
Normal file
213
src/corelib/plugin/qmachparser.cpp
Normal 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
|
79
src/corelib/plugin/qmachparser_p.h
Normal file
79
src/corelib/plugin/qmachparser_p.h
Normal 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
|
49
tests/auto/corelib/plugin/qpluginloader/fakeplugin.cpp
Normal file
49
tests/auto/corelib/plugin/qpluginloader/fakeplugin.cpp
Normal 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";
|
@ -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
|
||||
|
||||
|
112
tests/auto/corelib/plugin/qpluginloader/machtest/ppcconverter.pl
Executable file
112
tests/auto/corelib/plugin/qpluginloader/machtest/ppcconverter.pl
Executable 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);
|
@ -5,6 +5,7 @@ SUBDIRS = lib \
|
||||
theplugin \
|
||||
tst
|
||||
!win32: !mac: SUBDIRS += almostplugin
|
||||
macx-*: SUBDIRS += machtest
|
||||
TARGET = tst_qpluginloader
|
||||
|
||||
# no special install rule for subdir
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user